]> asedeno.scripts.mit.edu Git - linux.git/blob - net/ipv4/netfilter/nft_fib_ipv4.c
PM / QoS: Remove global notifiers
[linux.git] / net / ipv4 / netfilter / nft_fib_ipv4.c
1 /*
2  * This program is free software; you can redistribute it and/or modify
3  * it under the terms of the GNU General Public License version 2 as
4  * published by the Free Software Foundation.
5  */
6
7 #include <linux/kernel.h>
8 #include <linux/init.h>
9 #include <linux/module.h>
10 #include <linux/netlink.h>
11 #include <linux/netfilter.h>
12 #include <linux/netfilter/nf_tables.h>
13 #include <net/netfilter/nf_tables_core.h>
14 #include <net/netfilter/nf_tables.h>
15 #include <net/netfilter/nft_fib.h>
16
17 #include <net/ip_fib.h>
18 #include <net/route.h>
19
20 /* don't try to find route from mcast/bcast/zeronet */
21 static __be32 get_saddr(__be32 addr)
22 {
23         if (ipv4_is_multicast(addr) || ipv4_is_lbcast(addr) ||
24             ipv4_is_zeronet(addr))
25                 return 0;
26         return addr;
27 }
28
29 static bool fib4_is_local(const struct sk_buff *skb)
30 {
31         const struct rtable *rt = skb_rtable(skb);
32
33         return rt && (rt->rt_flags & RTCF_LOCAL);
34 }
35
36 #define DSCP_BITS     0xfc
37
38 void nft_fib4_eval_type(const struct nft_expr *expr, struct nft_regs *regs,
39                         const struct nft_pktinfo *pkt)
40 {
41         const struct nft_fib *priv = nft_expr_priv(expr);
42         u32 *dst = &regs->data[priv->dreg];
43         const struct net_device *dev = NULL;
44         const struct iphdr *iph;
45         __be32 addr;
46
47         if (priv->flags & NFTA_FIB_F_IIF)
48                 dev = nft_in(pkt);
49         else if (priv->flags & NFTA_FIB_F_OIF)
50                 dev = nft_out(pkt);
51
52         iph = ip_hdr(pkt->skb);
53         if (priv->flags & NFTA_FIB_F_DADDR)
54                 addr = iph->daddr;
55         else
56                 addr = iph->saddr;
57
58         *dst = inet_dev_addr_type(nft_net(pkt), dev, addr);
59 }
60 EXPORT_SYMBOL_GPL(nft_fib4_eval_type);
61
62 static int get_ifindex(const struct net_device *dev)
63 {
64         return dev ? dev->ifindex : 0;
65 }
66
67 void nft_fib4_eval(const struct nft_expr *expr, struct nft_regs *regs,
68                    const struct nft_pktinfo *pkt)
69 {
70         const struct nft_fib *priv = nft_expr_priv(expr);
71         u32 *dest = &regs->data[priv->dreg];
72         const struct iphdr *iph;
73         struct fib_result res;
74         struct flowi4 fl4 = {
75                 .flowi4_scope = RT_SCOPE_UNIVERSE,
76                 .flowi4_iif = LOOPBACK_IFINDEX,
77         };
78         const struct net_device *oif;
79         struct net_device *found;
80 #ifdef CONFIG_IP_ROUTE_MULTIPATH
81         int i;
82 #endif
83
84         /*
85          * Do not set flowi4_oif, it restricts results (for example, asking
86          * for oif 3 will get RTN_UNICAST result even if the daddr exits
87          * on another interface.
88          *
89          * Search results for the desired outinterface instead.
90          */
91         if (priv->flags & NFTA_FIB_F_OIF)
92                 oif = nft_out(pkt);
93         else if (priv->flags & NFTA_FIB_F_IIF)
94                 oif = nft_in(pkt);
95         else
96                 oif = NULL;
97
98         if (nft_hook(pkt) == NF_INET_PRE_ROUTING && fib4_is_local(pkt->skb)) {
99                 nft_fib_store_result(dest, priv->result, pkt, LOOPBACK_IFINDEX);
100                 return;
101         }
102
103         iph = ip_hdr(pkt->skb);
104         if (ipv4_is_zeronet(iph->saddr)) {
105                 if (ipv4_is_lbcast(iph->daddr) ||
106                     ipv4_is_local_multicast(iph->daddr)) {
107                         nft_fib_store_result(dest, priv->result, pkt,
108                                              get_ifindex(pkt->skb->dev));
109                         return;
110                 }
111         }
112
113         if (priv->flags & NFTA_FIB_F_MARK)
114                 fl4.flowi4_mark = pkt->skb->mark;
115
116         fl4.flowi4_tos = iph->tos & DSCP_BITS;
117
118         if (priv->flags & NFTA_FIB_F_DADDR) {
119                 fl4.daddr = iph->daddr;
120                 fl4.saddr = get_saddr(iph->saddr);
121         } else {
122                 fl4.daddr = iph->saddr;
123                 fl4.saddr = get_saddr(iph->daddr);
124         }
125
126         *dest = 0;
127
128         if (fib_lookup(nft_net(pkt), &fl4, &res, FIB_LOOKUP_IGNORE_LINKSTATE))
129                 return;
130
131         switch (res.type) {
132         case RTN_UNICAST:
133                 break;
134         case RTN_LOCAL: /* should not appear here, see fib4_is_local() above */
135                 return;
136         default:
137                 break;
138         }
139
140        if (!oif) {
141                found = FIB_RES_DEV(res);
142                goto ok;
143        }
144
145 #ifdef CONFIG_IP_ROUTE_MULTIPATH
146         for (i = 0; i < res.fi->fib_nhs; i++) {
147                 struct fib_nh *nh = &res.fi->fib_nh[i];
148
149                 if (nh->nh_dev == oif) {
150                         found = nh->nh_dev;
151                         goto ok;
152                 }
153         }
154         return;
155 #else
156         found = FIB_RES_DEV(res);
157         if (found != oif)
158                 return;
159 #endif
160 ok:
161         switch (priv->result) {
162         case NFT_FIB_RESULT_OIF:
163                 *dest = found->ifindex;
164                 break;
165         case NFT_FIB_RESULT_OIFNAME:
166                 strncpy((char *)dest, found->name, IFNAMSIZ);
167                 break;
168         default:
169                 WARN_ON_ONCE(1);
170                 break;
171         }
172 }
173 EXPORT_SYMBOL_GPL(nft_fib4_eval);
174
175 static struct nft_expr_type nft_fib4_type;
176
177 static const struct nft_expr_ops nft_fib4_type_ops = {
178         .type           = &nft_fib4_type,
179         .size           = NFT_EXPR_SIZE(sizeof(struct nft_fib)),
180         .eval           = nft_fib4_eval_type,
181         .init           = nft_fib_init,
182         .dump           = nft_fib_dump,
183         .validate       = nft_fib_validate,
184 };
185
186 static const struct nft_expr_ops nft_fib4_ops = {
187         .type           = &nft_fib4_type,
188         .size           = NFT_EXPR_SIZE(sizeof(struct nft_fib)),
189         .eval           = nft_fib4_eval,
190         .init           = nft_fib_init,
191         .dump           = nft_fib_dump,
192         .validate       = nft_fib_validate,
193 };
194
195 static const struct nft_expr_ops *
196 nft_fib4_select_ops(const struct nft_ctx *ctx,
197                     const struct nlattr * const tb[])
198 {
199         enum nft_fib_result result;
200
201         if (!tb[NFTA_FIB_RESULT])
202                 return ERR_PTR(-EINVAL);
203
204         result = ntohl(nla_get_be32(tb[NFTA_FIB_RESULT]));
205
206         switch (result) {
207         case NFT_FIB_RESULT_OIF:
208                 return &nft_fib4_ops;
209         case NFT_FIB_RESULT_OIFNAME:
210                 return &nft_fib4_ops;
211         case NFT_FIB_RESULT_ADDRTYPE:
212                 return &nft_fib4_type_ops;
213         default:
214                 return ERR_PTR(-EOPNOTSUPP);
215         }
216 }
217
218 static struct nft_expr_type nft_fib4_type __read_mostly = {
219         .name           = "fib",
220         .select_ops     = &nft_fib4_select_ops,
221         .policy         = nft_fib_policy,
222         .maxattr        = NFTA_FIB_MAX,
223         .family         = NFPROTO_IPV4,
224         .owner          = THIS_MODULE,
225 };
226
227 static int __init nft_fib4_module_init(void)
228 {
229         return nft_register_expr(&nft_fib4_type);
230 }
231
232 static void __exit nft_fib4_module_exit(void)
233 {
234         nft_unregister_expr(&nft_fib4_type);
235 }
236
237 module_init(nft_fib4_module_init);
238 module_exit(nft_fib4_module_exit);
239 MODULE_LICENSE("GPL");
240 MODULE_AUTHOR("Florian Westphal <fw@strlen.de>");
241 MODULE_ALIAS_NFT_AF_EXPR(2, "fib");