diff options
Diffstat (limited to 'net/ipv4/fib_frontend.c')
-rw-r--r-- | net/ipv4/fib_frontend.c | 117 |
1 files changed, 89 insertions, 28 deletions
diff --git a/net/ipv4/fib_frontend.c b/net/ipv4/fib_frontend.c index 3854411fa37..81f85716a89 100644 --- a/net/ipv4/fib_frontend.c +++ b/net/ipv4/fib_frontend.c | |||
@@ -31,6 +31,7 @@ | |||
31 | #include <linux/if_addr.h> | 31 | #include <linux/if_addr.h> |
32 | #include <linux/if_arp.h> | 32 | #include <linux/if_arp.h> |
33 | #include <linux/skbuff.h> | 33 | #include <linux/skbuff.h> |
34 | #include <linux/cache.h> | ||
34 | #include <linux/init.h> | 35 | #include <linux/init.h> |
35 | #include <linux/list.h> | 36 | #include <linux/list.h> |
36 | #include <linux/slab.h> | 37 | #include <linux/slab.h> |
@@ -85,6 +86,24 @@ struct fib_table *fib_new_table(struct net *net, u32 id) | |||
85 | tb = fib_trie_table(id); | 86 | tb = fib_trie_table(id); |
86 | if (!tb) | 87 | if (!tb) |
87 | return NULL; | 88 | return NULL; |
89 | |||
90 | switch (id) { | ||
91 | case RT_TABLE_LOCAL: | ||
92 | net->ipv4.fib_local = tb; | ||
93 | break; | ||
94 | |||
95 | case RT_TABLE_MAIN: | ||
96 | net->ipv4.fib_main = tb; | ||
97 | break; | ||
98 | |||
99 | case RT_TABLE_DEFAULT: | ||
100 | net->ipv4.fib_default = tb; | ||
101 | break; | ||
102 | |||
103 | default: | ||
104 | break; | ||
105 | } | ||
106 | |||
88 | h = id & (FIB_TABLE_HASHSZ - 1); | 107 | h = id & (FIB_TABLE_HASHSZ - 1); |
89 | hlist_add_head_rcu(&tb->tb_hlist, &net->ipv4.fib_table_hash[h]); | 108 | hlist_add_head_rcu(&tb->tb_hlist, &net->ipv4.fib_table_hash[h]); |
90 | return tb; | 109 | return tb; |
@@ -180,6 +199,43 @@ unsigned int inet_dev_addr_type(struct net *net, const struct net_device *dev, | |||
180 | } | 199 | } |
181 | EXPORT_SYMBOL(inet_dev_addr_type); | 200 | EXPORT_SYMBOL(inet_dev_addr_type); |
182 | 201 | ||
202 | __be32 fib_compute_spec_dst(struct sk_buff *skb) | ||
203 | { | ||
204 | struct net_device *dev = skb->dev; | ||
205 | struct in_device *in_dev; | ||
206 | struct fib_result res; | ||
207 | struct rtable *rt; | ||
208 | struct flowi4 fl4; | ||
209 | struct net *net; | ||
210 | int scope; | ||
211 | |||
212 | rt = skb_rtable(skb); | ||
213 | if (!(rt->rt_flags & (RTCF_BROADCAST | RTCF_MULTICAST))) | ||
214 | return ip_hdr(skb)->daddr; | ||
215 | |||
216 | in_dev = __in_dev_get_rcu(dev); | ||
217 | BUG_ON(!in_dev); | ||
218 | |||
219 | net = dev_net(dev); | ||
220 | |||
221 | scope = RT_SCOPE_UNIVERSE; | ||
222 | if (!ipv4_is_zeronet(ip_hdr(skb)->saddr)) { | ||
223 | fl4.flowi4_oif = 0; | ||
224 | fl4.flowi4_iif = net->loopback_dev->ifindex; | ||
225 | fl4.daddr = ip_hdr(skb)->saddr; | ||
226 | fl4.saddr = 0; | ||
227 | fl4.flowi4_tos = RT_TOS(ip_hdr(skb)->tos); | ||
228 | fl4.flowi4_scope = scope; | ||
229 | fl4.flowi4_mark = IN_DEV_SRC_VMARK(in_dev) ? skb->mark : 0; | ||
230 | if (!fib_lookup(net, &fl4, &res)) | ||
231 | return FIB_RES_PREFSRC(net, res); | ||
232 | } else { | ||
233 | scope = RT_SCOPE_LINK; | ||
234 | } | ||
235 | |||
236 | return inet_select_addr(dev, ip_hdr(skb)->saddr, scope); | ||
237 | } | ||
238 | |||
183 | /* Given (packet source, input interface) and optional (dst, oif, tos): | 239 | /* Given (packet source, input interface) and optional (dst, oif, tos): |
184 | * - (main) check, that source is valid i.e. not broadcast or our local | 240 | * - (main) check, that source is valid i.e. not broadcast or our local |
185 | * address. | 241 | * address. |
@@ -188,17 +244,15 @@ EXPORT_SYMBOL(inet_dev_addr_type); | |||
188 | * - check, that packet arrived from expected physical interface. | 244 | * - check, that packet arrived from expected physical interface. |
189 | * called with rcu_read_lock() | 245 | * called with rcu_read_lock() |
190 | */ | 246 | */ |
191 | int fib_validate_source(struct sk_buff *skb, __be32 src, __be32 dst, u8 tos, | 247 | static int __fib_validate_source(struct sk_buff *skb, __be32 src, __be32 dst, |
192 | int oif, struct net_device *dev, __be32 *spec_dst, | 248 | u8 tos, int oif, struct net_device *dev, |
193 | u32 *itag) | 249 | int rpf, struct in_device *idev, u32 *itag) |
194 | { | 250 | { |
195 | struct in_device *in_dev; | 251 | int ret, no_addr, accept_local; |
196 | struct flowi4 fl4; | ||
197 | struct fib_result res; | 252 | struct fib_result res; |
198 | int no_addr, rpf, accept_local; | 253 | struct flowi4 fl4; |
199 | bool dev_match; | ||
200 | int ret; | ||
201 | struct net *net; | 254 | struct net *net; |
255 | bool dev_match; | ||
202 | 256 | ||
203 | fl4.flowi4_oif = 0; | 257 | fl4.flowi4_oif = 0; |
204 | fl4.flowi4_iif = oif; | 258 | fl4.flowi4_iif = oif; |
@@ -207,20 +261,11 @@ int fib_validate_source(struct sk_buff *skb, __be32 src, __be32 dst, u8 tos, | |||
207 | fl4.flowi4_tos = tos; | 261 | fl4.flowi4_tos = tos; |
208 | fl4.flowi4_scope = RT_SCOPE_UNIVERSE; | 262 | fl4.flowi4_scope = RT_SCOPE_UNIVERSE; |
209 | 263 | ||
210 | no_addr = rpf = accept_local = 0; | 264 | no_addr = accept_local = 0; |
211 | in_dev = __in_dev_get_rcu(dev); | 265 | no_addr = idev->ifa_list == NULL; |
212 | if (in_dev) { | ||
213 | no_addr = in_dev->ifa_list == NULL; | ||
214 | |||
215 | /* Ignore rp_filter for packets protected by IPsec. */ | ||
216 | rpf = secpath_exists(skb) ? 0 : IN_DEV_RPFILTER(in_dev); | ||
217 | 266 | ||
218 | accept_local = IN_DEV_ACCEPT_LOCAL(in_dev); | 267 | accept_local = IN_DEV_ACCEPT_LOCAL(idev); |
219 | fl4.flowi4_mark = IN_DEV_SRC_VMARK(in_dev) ? skb->mark : 0; | 268 | fl4.flowi4_mark = IN_DEV_SRC_VMARK(idev) ? skb->mark : 0; |
220 | } | ||
221 | |||
222 | if (in_dev == NULL) | ||
223 | goto e_inval; | ||
224 | 269 | ||
225 | net = dev_net(dev); | 270 | net = dev_net(dev); |
226 | if (fib_lookup(net, &fl4, &res)) | 271 | if (fib_lookup(net, &fl4, &res)) |
@@ -229,7 +274,6 @@ int fib_validate_source(struct sk_buff *skb, __be32 src, __be32 dst, u8 tos, | |||
229 | if (res.type != RTN_LOCAL || !accept_local) | 274 | if (res.type != RTN_LOCAL || !accept_local) |
230 | goto e_inval; | 275 | goto e_inval; |
231 | } | 276 | } |
232 | *spec_dst = FIB_RES_PREFSRC(net, res); | ||
233 | fib_combine_itag(itag, &res); | 277 | fib_combine_itag(itag, &res); |
234 | dev_match = false; | 278 | dev_match = false; |
235 | 279 | ||
@@ -258,17 +302,14 @@ int fib_validate_source(struct sk_buff *skb, __be32 src, __be32 dst, u8 tos, | |||
258 | 302 | ||
259 | ret = 0; | 303 | ret = 0; |
260 | if (fib_lookup(net, &fl4, &res) == 0) { | 304 | if (fib_lookup(net, &fl4, &res) == 0) { |
261 | if (res.type == RTN_UNICAST) { | 305 | if (res.type == RTN_UNICAST) |
262 | *spec_dst = FIB_RES_PREFSRC(net, res); | ||
263 | ret = FIB_RES_NH(res).nh_scope >= RT_SCOPE_HOST; | 306 | ret = FIB_RES_NH(res).nh_scope >= RT_SCOPE_HOST; |
264 | } | ||
265 | } | 307 | } |
266 | return ret; | 308 | return ret; |
267 | 309 | ||
268 | last_resort: | 310 | last_resort: |
269 | if (rpf) | 311 | if (rpf) |
270 | goto e_rpf; | 312 | goto e_rpf; |
271 | *spec_dst = inet_select_addr(dev, 0, RT_SCOPE_UNIVERSE); | ||
272 | *itag = 0; | 313 | *itag = 0; |
273 | return 0; | 314 | return 0; |
274 | 315 | ||
@@ -278,6 +319,20 @@ e_rpf: | |||
278 | return -EXDEV; | 319 | return -EXDEV; |
279 | } | 320 | } |
280 | 321 | ||
322 | /* Ignore rp_filter for packets protected by IPsec. */ | ||
323 | int fib_validate_source(struct sk_buff *skb, __be32 src, __be32 dst, | ||
324 | u8 tos, int oif, struct net_device *dev, | ||
325 | struct in_device *idev, u32 *itag) | ||
326 | { | ||
327 | int r = secpath_exists(skb) ? 0 : IN_DEV_RPFILTER(idev); | ||
328 | |||
329 | if (!r && !fib_num_tclassid_users(dev_net(dev))) { | ||
330 | *itag = 0; | ||
331 | return 0; | ||
332 | } | ||
333 | return __fib_validate_source(skb, src, dst, tos, oif, dev, r, idev, itag); | ||
334 | } | ||
335 | |||
281 | static inline __be32 sk_extract_addr(struct sockaddr *addr) | 336 | static inline __be32 sk_extract_addr(struct sockaddr *addr) |
282 | { | 337 | { |
283 | return ((struct sockaddr_in *) addr)->sin_addr.s_addr; | 338 | return ((struct sockaddr_in *) addr)->sin_addr.s_addr; |
@@ -935,8 +990,11 @@ static void nl_fib_input(struct sk_buff *skb) | |||
935 | static int __net_init nl_fib_lookup_init(struct net *net) | 990 | static int __net_init nl_fib_lookup_init(struct net *net) |
936 | { | 991 | { |
937 | struct sock *sk; | 992 | struct sock *sk; |
938 | sk = netlink_kernel_create(net, NETLINK_FIB_LOOKUP, 0, | 993 | struct netlink_kernel_cfg cfg = { |
939 | nl_fib_input, NULL, THIS_MODULE); | 994 | .input = nl_fib_input, |
995 | }; | ||
996 | |||
997 | sk = netlink_kernel_create(net, NETLINK_FIB_LOOKUP, THIS_MODULE, &cfg); | ||
940 | if (sk == NULL) | 998 | if (sk == NULL) |
941 | return -EAFNOSUPPORT; | 999 | return -EAFNOSUPPORT; |
942 | net->ipv4.fibnl = sk; | 1000 | net->ipv4.fibnl = sk; |
@@ -1090,6 +1148,9 @@ static int __net_init fib_net_init(struct net *net) | |||
1090 | { | 1148 | { |
1091 | int error; | 1149 | int error; |
1092 | 1150 | ||
1151 | #ifdef CONFIG_IP_ROUTE_CLASSID | ||
1152 | net->ipv4.fib_num_tclassid_users = 0; | ||
1153 | #endif | ||
1093 | error = ip_fib_net_init(net); | 1154 | error = ip_fib_net_init(net); |
1094 | if (error < 0) | 1155 | if (error < 0) |
1095 | goto out; | 1156 | goto out; |