Blame view
net/ipv4/fib_semantics.c
29 KB
1da177e4c Linux-2.6.12-rc2 |
1 2 3 4 5 6 7 |
/* * INET An implementation of the TCP/IP protocol suite for the LINUX * operating system. INET is implemented using the BSD Socket * interface as the means of communication with the user level. * * IPv4 Forwarding Information Base: semantics. * |
1da177e4c Linux-2.6.12-rc2 |
8 9 10 11 12 13 14 |
* Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru> * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. */ |
1da177e4c Linux-2.6.12-rc2 |
15 16 17 18 19 20 21 22 23 24 25 26 27 |
#include <asm/uaccess.h> #include <asm/system.h> #include <linux/bitops.h> #include <linux/types.h> #include <linux/kernel.h> #include <linux/jiffies.h> #include <linux/mm.h> #include <linux/string.h> #include <linux/socket.h> #include <linux/sockios.h> #include <linux/errno.h> #include <linux/in.h> #include <linux/inet.h> |
14c850212 [INET_SOCK]: Move... |
28 |
#include <linux/inetdevice.h> |
1da177e4c Linux-2.6.12-rc2 |
29 30 31 32 |
#include <linux/netdevice.h> #include <linux/if_arp.h> #include <linux/proc_fs.h> #include <linux/skbuff.h> |
1da177e4c Linux-2.6.12-rc2 |
33 |
#include <linux/init.h> |
5a0e3ad6a include cleanup: ... |
34 |
#include <linux/slab.h> |
1da177e4c Linux-2.6.12-rc2 |
35 |
|
14c850212 [INET_SOCK]: Move... |
36 |
#include <net/arp.h> |
1da177e4c Linux-2.6.12-rc2 |
37 38 39 40 41 42 |
#include <net/ip.h> #include <net/protocol.h> #include <net/route.h> #include <net/tcp.h> #include <net/sock.h> #include <net/ip_fib.h> |
f21c7bc5f [IPv4] route: Con... |
43 |
#include <net/netlink.h> |
4e902c574 [IPv4]: FIB confi... |
44 |
#include <net/nexthop.h> |
1da177e4c Linux-2.6.12-rc2 |
45 46 |
#include "fib_lookup.h" |
832b4c5e1 [IPV4] fib: conve... |
47 |
static DEFINE_SPINLOCK(fib_info_lock); |
1da177e4c Linux-2.6.12-rc2 |
48 49 |
static struct hlist_head *fib_info_hash; static struct hlist_head *fib_info_laddrhash; |
123b9731b ipv4: Rename fib_... |
50 |
static unsigned int fib_info_hash_size; |
1da177e4c Linux-2.6.12-rc2 |
51 52 53 54 55 56 57 58 59 |
static unsigned int fib_info_cnt; #define DEVINDEX_HASHBITS 8 #define DEVINDEX_HASHSIZE (1U << DEVINDEX_HASHBITS) static struct hlist_head fib_info_devhash[DEVINDEX_HASHSIZE]; #ifdef CONFIG_IP_ROUTE_MULTIPATH static DEFINE_SPINLOCK(fib_multipath_lock); |
6a31d2a97 fib: cleanups |
60 61 62 63 64 65 66 67 68 69 70 |
#define for_nexthops(fi) { \ int nhsel; const struct fib_nh *nh; \ for (nhsel = 0, nh = (fi)->fib_nh; \ nhsel < (fi)->fib_nhs; \ nh++, nhsel++) #define change_nexthops(fi) { \ int nhsel; struct fib_nh *nexthop_nh; \ for (nhsel = 0, nexthop_nh = (struct fib_nh *)((fi)->fib_nh); \ nhsel < (fi)->fib_nhs; \ nexthop_nh++, nhsel++) |
1da177e4c Linux-2.6.12-rc2 |
71 72 73 74 |
#else /* CONFIG_IP_ROUTE_MULTIPATH */ /* Hope, that gcc will optimize it to get rid of dummy loop */ |
6a31d2a97 fib: cleanups |
75 76 77 |
#define for_nexthops(fi) { \ int nhsel; const struct fib_nh *nh = (fi)->fib_nh; \ for (nhsel = 0; nhsel < 1; nhsel++) |
1da177e4c Linux-2.6.12-rc2 |
78 |
|
6a31d2a97 fib: cleanups |
79 80 81 82 |
#define change_nexthops(fi) { \ int nhsel; \ struct fib_nh *nexthop_nh = (struct fib_nh *)((fi)->fib_nh); \ for (nhsel = 0; nhsel < 1; nhsel++) |
1da177e4c Linux-2.6.12-rc2 |
83 84 85 86 |
#endif /* CONFIG_IP_ROUTE_MULTIPATH */ #define endfor_nexthops(fi) } |
3be0686b6 ipv4: Inline fib_... |
87 |
const struct fib_prop fib_props[RTN_MAX + 1] = { |
6a31d2a97 fib: cleanups |
88 |
[RTN_UNSPEC] = { |
1da177e4c Linux-2.6.12-rc2 |
89 90 |
.error = 0, .scope = RT_SCOPE_NOWHERE, |
6a31d2a97 fib: cleanups |
91 92 |
}, [RTN_UNICAST] = { |
1da177e4c Linux-2.6.12-rc2 |
93 94 |
.error = 0, .scope = RT_SCOPE_UNIVERSE, |
6a31d2a97 fib: cleanups |
95 96 |
}, [RTN_LOCAL] = { |
1da177e4c Linux-2.6.12-rc2 |
97 98 |
.error = 0, .scope = RT_SCOPE_HOST, |
6a31d2a97 fib: cleanups |
99 100 |
}, [RTN_BROADCAST] = { |
1da177e4c Linux-2.6.12-rc2 |
101 102 |
.error = 0, .scope = RT_SCOPE_LINK, |
6a31d2a97 fib: cleanups |
103 104 |
}, [RTN_ANYCAST] = { |
1da177e4c Linux-2.6.12-rc2 |
105 106 |
.error = 0, .scope = RT_SCOPE_LINK, |
6a31d2a97 fib: cleanups |
107 108 |
}, [RTN_MULTICAST] = { |
1da177e4c Linux-2.6.12-rc2 |
109 110 |
.error = 0, .scope = RT_SCOPE_UNIVERSE, |
6a31d2a97 fib: cleanups |
111 112 |
}, [RTN_BLACKHOLE] = { |
1da177e4c Linux-2.6.12-rc2 |
113 114 |
.error = -EINVAL, .scope = RT_SCOPE_UNIVERSE, |
6a31d2a97 fib: cleanups |
115 116 |
}, [RTN_UNREACHABLE] = { |
1da177e4c Linux-2.6.12-rc2 |
117 118 |
.error = -EHOSTUNREACH, .scope = RT_SCOPE_UNIVERSE, |
6a31d2a97 fib: cleanups |
119 120 |
}, [RTN_PROHIBIT] = { |
1da177e4c Linux-2.6.12-rc2 |
121 122 |
.error = -EACCES, .scope = RT_SCOPE_UNIVERSE, |
6a31d2a97 fib: cleanups |
123 124 |
}, [RTN_THROW] = { |
1da177e4c Linux-2.6.12-rc2 |
125 126 |
.error = -EAGAIN, .scope = RT_SCOPE_UNIVERSE, |
6a31d2a97 fib: cleanups |
127 128 |
}, [RTN_NAT] = { |
1da177e4c Linux-2.6.12-rc2 |
129 130 |
.error = -EINVAL, .scope = RT_SCOPE_NOWHERE, |
6a31d2a97 fib: cleanups |
131 132 |
}, [RTN_XRESOLVE] = { |
1da177e4c Linux-2.6.12-rc2 |
133 134 |
.error = -EINVAL, .scope = RT_SCOPE_NOWHERE, |
6a31d2a97 fib: cleanups |
135 |
}, |
1da177e4c Linux-2.6.12-rc2 |
136 |
}; |
1da177e4c Linux-2.6.12-rc2 |
137 |
/* Release a nexthop info record */ |
19c1ea14c ipv4: Fix fib_inf... |
138 139 140 141 142 143 144 145 |
static void free_fib_info_rcu(struct rcu_head *head) { struct fib_info *fi = container_of(head, struct fib_info, rcu); if (fi->fib_metrics != (u32 *) dst_default_metrics) kfree(fi->fib_metrics); kfree(fi); } |
1da177e4c Linux-2.6.12-rc2 |
146 147 148 149 |
void free_fib_info(struct fib_info *fi) { if (fi->fib_dead == 0) { |
6a31d2a97 fib: cleanups |
150 151 |
pr_warning("Freeing alive fib_info %p ", fi); |
1da177e4c Linux-2.6.12-rc2 |
152 153 154 |
return; } change_nexthops(fi) { |
71fceff0e ipv4: Use less co... |
155 156 157 |
if (nexthop_nh->nh_dev) dev_put(nexthop_nh->nh_dev); nexthop_nh->nh_dev = NULL; |
1da177e4c Linux-2.6.12-rc2 |
158 159 |
} endfor_nexthops(fi); fib_info_cnt--; |
57d7a6009 [NETNS]: Add netn... |
160 |
release_net(fi->fib_net); |
19c1ea14c ipv4: Fix fib_inf... |
161 |
call_rcu(&fi->rcu, free_fib_info_rcu); |
1da177e4c Linux-2.6.12-rc2 |
162 163 164 165 |
} void fib_release_info(struct fib_info *fi) { |
832b4c5e1 [IPV4] fib: conve... |
166 |
spin_lock_bh(&fib_info_lock); |
1da177e4c Linux-2.6.12-rc2 |
167 168 169 170 171 |
if (fi && --fi->fib_treeref == 0) { hlist_del(&fi->fib_hash); if (fi->fib_prefsrc) hlist_del(&fi->fib_lhash); change_nexthops(fi) { |
71fceff0e ipv4: Use less co... |
172 |
if (!nexthop_nh->nh_dev) |
1da177e4c Linux-2.6.12-rc2 |
173 |
continue; |
71fceff0e ipv4: Use less co... |
174 |
hlist_del(&nexthop_nh->nh_hash); |
1da177e4c Linux-2.6.12-rc2 |
175 176 177 178 |
} endfor_nexthops(fi) fi->fib_dead = 1; fib_info_put(fi); } |
832b4c5e1 [IPV4] fib: conve... |
179 |
spin_unlock_bh(&fib_info_lock); |
1da177e4c Linux-2.6.12-rc2 |
180 |
} |
6a31d2a97 fib: cleanups |
181 |
static inline int nh_comp(const struct fib_info *fi, const struct fib_info *ofi) |
1da177e4c Linux-2.6.12-rc2 |
182 183 184 185 186 187 188 189 190 191 |
{ const struct fib_nh *onh = ofi->fib_nh; for_nexthops(fi) { if (nh->nh_oif != onh->nh_oif || nh->nh_gw != onh->nh_gw || nh->nh_scope != onh->nh_scope || #ifdef CONFIG_IP_ROUTE_MULTIPATH nh->nh_weight != onh->nh_weight || #endif |
c7066f70d netfilter: fix Kc... |
192 |
#ifdef CONFIG_IP_ROUTE_CLASSID |
1da177e4c Linux-2.6.12-rc2 |
193 194 |
nh->nh_tclassid != onh->nh_tclassid || #endif |
6a31d2a97 fib: cleanups |
195 |
((nh->nh_flags ^ onh->nh_flags) & ~RTNH_F_DEAD)) |
1da177e4c Linux-2.6.12-rc2 |
196 197 198 199 200 |
return -1; onh++; } endfor_nexthops(fi); return 0; } |
88ebc72f6 [IPV4] FIB: Inclu... |
201 202 203 204 205 206 207 208 |
static inline unsigned int fib_devindex_hashfn(unsigned int val) { unsigned int mask = DEVINDEX_HASHSIZE - 1; return (val ^ (val >> DEVINDEX_HASHBITS) ^ (val >> (DEVINDEX_HASHBITS * 2))) & mask; } |
1da177e4c Linux-2.6.12-rc2 |
209 210 |
static inline unsigned int fib_info_hashfn(const struct fib_info *fi) { |
123b9731b ipv4: Rename fib_... |
211 |
unsigned int mask = (fib_info_hash_size - 1); |
1da177e4c Linux-2.6.12-rc2 |
212 |
unsigned int val = fi->fib_nhs; |
37e826c51 ipv4: Fix nexthop... |
213 |
val ^= (fi->fib_protocol << 8) | fi->fib_scope; |
81f7bf6cb [IPV4]: net/ipv4/... |
214 |
val ^= (__force u32)fi->fib_prefsrc; |
1da177e4c Linux-2.6.12-rc2 |
215 |
val ^= fi->fib_priority; |
88ebc72f6 [IPV4] FIB: Inclu... |
216 217 218 |
for_nexthops(fi) { val ^= fib_devindex_hashfn(nh->nh_oif); } endfor_nexthops(fi) |
1da177e4c Linux-2.6.12-rc2 |
219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 |
return (val ^ (val >> 7) ^ (val >> 12)) & mask; } static struct fib_info *fib_find_info(const struct fib_info *nfi) { struct hlist_head *head; struct hlist_node *node; struct fib_info *fi; unsigned int hash; hash = fib_info_hashfn(nfi); head = &fib_info_hash[hash]; hlist_for_each_entry(fi, node, head, fib_hash) { |
09ad9bc75 net: use net_eq t... |
234 |
if (!net_eq(fi->fib_net, nfi->fib_net)) |
4814bdbd5 [NETNS]: Lookup i... |
235 |
continue; |
1da177e4c Linux-2.6.12-rc2 |
236 237 238 |
if (fi->fib_nhs != nfi->fib_nhs) continue; if (nfi->fib_protocol == fi->fib_protocol && |
37e826c51 ipv4: Fix nexthop... |
239 |
nfi->fib_scope == fi->fib_scope && |
1da177e4c Linux-2.6.12-rc2 |
240 241 242 |
nfi->fib_prefsrc == fi->fib_prefsrc && nfi->fib_priority == fi->fib_priority && memcmp(nfi->fib_metrics, fi->fib_metrics, |
fcd13f42c ipv4: fix fib met... |
243 |
sizeof(u32) * RTAX_MAX) == 0 && |
6a31d2a97 fib: cleanups |
244 |
((nfi->fib_flags ^ fi->fib_flags) & ~RTNH_F_DEAD) == 0 && |
1da177e4c Linux-2.6.12-rc2 |
245 246 247 248 249 250 |
(nfi->fib_nhs == 0 || nh_comp(fi, nfi) == 0)) return fi; } return NULL; } |
1da177e4c Linux-2.6.12-rc2 |
251 |
/* Check, that the gateway is already configured. |
6a31d2a97 fib: cleanups |
252 |
* Used only by redirect accept routine. |
1da177e4c Linux-2.6.12-rc2 |
253 |
*/ |
d878e72e4 [IPV4]: ip_fib_ch... |
254 |
int ip_fib_check_default(__be32 gw, struct net_device *dev) |
1da177e4c Linux-2.6.12-rc2 |
255 256 257 258 259 |
{ struct hlist_head *head; struct hlist_node *node; struct fib_nh *nh; unsigned int hash; |
832b4c5e1 [IPV4] fib: conve... |
260 |
spin_lock(&fib_info_lock); |
1da177e4c Linux-2.6.12-rc2 |
261 262 263 264 265 266 |
hash = fib_devindex_hashfn(dev->ifindex); head = &fib_info_devhash[hash]; hlist_for_each_entry(nh, node, head, nh_hash) { if (nh->nh_dev == dev && nh->nh_gw == gw && |
6a31d2a97 fib: cleanups |
267 |
!(nh->nh_flags & RTNH_F_DEAD)) { |
832b4c5e1 [IPV4] fib: conve... |
268 |
spin_unlock(&fib_info_lock); |
1da177e4c Linux-2.6.12-rc2 |
269 270 271 |
return 0; } } |
832b4c5e1 [IPV4] fib: conve... |
272 |
spin_unlock(&fib_info_lock); |
1da177e4c Linux-2.6.12-rc2 |
273 274 275 |
return -1; } |
339bf98ff [NETLINK]: Do pre... |
276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 |
static inline size_t fib_nlmsg_size(struct fib_info *fi) { size_t payload = NLMSG_ALIGN(sizeof(struct rtmsg)) + nla_total_size(4) /* RTA_TABLE */ + nla_total_size(4) /* RTA_DST */ + nla_total_size(4) /* RTA_PRIORITY */ + nla_total_size(4); /* RTA_PREFSRC */ /* space for nested metrics */ payload += nla_total_size((RTAX_MAX * nla_total_size(4))); if (fi->fib_nhs) { /* Also handles the special case fib_nhs == 1 */ /* each nexthop is packed in an attribute */ size_t nhsize = nla_total_size(sizeof(struct rtnexthop)); /* may contain flow and gateway attribute */ nhsize += 2 * nla_total_size(4); /* all nexthops are packed in a nested attribute */ payload += nla_total_size(fi->fib_nhs * nhsize); } return payload; } |
81f7bf6cb [IPV4]: net/ipv4/... |
302 |
void rtmsg_fib(int event, __be32 key, struct fib_alias *fa, |
b8f558313 [RTNETLINK]: Fix ... |
303 304 |
int dst_len, u32 tb_id, struct nl_info *info, unsigned int nlm_flags) |
1da177e4c Linux-2.6.12-rc2 |
305 306 |
{ struct sk_buff *skb; |
4e902c574 [IPv4]: FIB confi... |
307 |
u32 seq = info->nlh ? info->nlh->nlmsg_seq : 0; |
f21c7bc5f [IPv4] route: Con... |
308 |
int err = -ENOBUFS; |
1da177e4c Linux-2.6.12-rc2 |
309 |
|
339bf98ff [NETLINK]: Do pre... |
310 |
skb = nlmsg_new(fib_nlmsg_size(fa->fa_info), GFP_KERNEL); |
f21c7bc5f [IPv4] route: Con... |
311 312 |
if (skb == NULL) goto errout; |
1da177e4c Linux-2.6.12-rc2 |
313 |
|
4e902c574 [IPv4]: FIB confi... |
314 |
err = fib_dump_info(skb, info->pid, seq, event, tb_id, |
37e826c51 ipv4: Fix nexthop... |
315 |
fa->fa_type, key, dst_len, |
b8f558313 [RTNETLINK]: Fix ... |
316 |
fa->fa_tos, fa->fa_info, nlm_flags); |
26932566a [NETLINK]: Don't ... |
317 318 319 320 321 322 |
if (err < 0) { /* -EMSGSIZE implies BUG in fib_nlmsg_size() */ WARN_ON(err == -EMSGSIZE); kfree_skb(skb); goto errout; } |
1ce85fe40 netlink: change n... |
323 324 325 |
rtnl_notify(skb, info->nl_net, info->pid, RTNLGRP_IPV4_ROUTE, info->nlh, GFP_KERNEL); return; |
f21c7bc5f [IPv4] route: Con... |
326 327 |
errout: if (err < 0) |
4d1169c1e [NETNS]: Add netn... |
328 |
rtnl_set_sk_err(info->nl_net, RTNLGRP_IPV4_ROUTE, err); |
1da177e4c Linux-2.6.12-rc2 |
329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 |
} /* Return the first fib alias matching TOS with * priority less than or equal to PRIO. */ struct fib_alias *fib_find_alias(struct list_head *fah, u8 tos, u32 prio) { if (fah) { struct fib_alias *fa; list_for_each_entry(fa, fah, fa_list) { if (fa->fa_tos > tos) continue; if (fa->fa_info->fib_priority >= prio || fa->fa_tos < tos) return fa; } } return NULL; } int fib_detect_death(struct fib_info *fi, int order, |
c17860a03 [IPV4]: no need p... |
350 |
struct fib_info **last_resort, int *last_idx, int dflt) |
1da177e4c Linux-2.6.12-rc2 |
351 352 353 354 355 356 357 358 359 |
{ struct neighbour *n; int state = NUD_NONE; n = neigh_lookup(&arp_tbl, &fi->fib_nh[0].nh_gw, fi->fib_dev); if (n) { state = n->nud_state; neigh_release(n); } |
d9319100c net: clean up net... |
360 |
if (state == NUD_REACHABLE) |
1da177e4c Linux-2.6.12-rc2 |
361 |
return 0; |
6a31d2a97 fib: cleanups |
362 |
if ((state & NUD_VALID) && order != dflt) |
1da177e4c Linux-2.6.12-rc2 |
363 |
return 0; |
6a31d2a97 fib: cleanups |
364 365 |
if ((state & NUD_VALID) || (*last_idx < 0 && order > dflt)) { |
1da177e4c Linux-2.6.12-rc2 |
366 367 368 369 370 371 372 |
*last_resort = fi; *last_idx = order; } return 1; } #ifdef CONFIG_IP_ROUTE_MULTIPATH |
4e902c574 [IPv4]: FIB confi... |
373 |
static int fib_count_nexthops(struct rtnexthop *rtnh, int remaining) |
1da177e4c Linux-2.6.12-rc2 |
374 375 |
{ int nhs = 0; |
1da177e4c Linux-2.6.12-rc2 |
376 |
|
4e902c574 [IPv4]: FIB confi... |
377 |
while (rtnh_ok(rtnh, remaining)) { |
1da177e4c Linux-2.6.12-rc2 |
378 |
nhs++; |
4e902c574 [IPv4]: FIB confi... |
379 380 381 382 383 |
rtnh = rtnh_next(rtnh, &remaining); } /* leftover implies invalid nexthop configuration, discard it */ return remaining > 0 ? 0 : nhs; |
1da177e4c Linux-2.6.12-rc2 |
384 |
} |
4e902c574 [IPv4]: FIB confi... |
385 386 |
static int fib_get_nhs(struct fib_info *fi, struct rtnexthop *rtnh, int remaining, struct fib_config *cfg) |
1da177e4c Linux-2.6.12-rc2 |
387 |
{ |
1da177e4c Linux-2.6.12-rc2 |
388 |
change_nexthops(fi) { |
4e902c574 [IPv4]: FIB confi... |
389 390 391 |
int attrlen; if (!rtnh_ok(rtnh, remaining)) |
1da177e4c Linux-2.6.12-rc2 |
392 |
return -EINVAL; |
4e902c574 [IPv4]: FIB confi... |
393 |
|
71fceff0e ipv4: Use less co... |
394 395 396 397 |
nexthop_nh->nh_flags = (cfg->fc_flags & ~0xFF) | rtnh->rtnh_flags; nexthop_nh->nh_oif = rtnh->rtnh_ifindex; nexthop_nh->nh_weight = rtnh->rtnh_hops + 1; |
4e902c574 [IPv4]: FIB confi... |
398 399 400 401 402 403 |
attrlen = rtnh_attrlen(rtnh); if (attrlen > 0) { struct nlattr *nla, *attrs = rtnh_attrs(rtnh); nla = nla_find(attrs, attrlen, RTA_GATEWAY); |
71fceff0e ipv4: Use less co... |
404 |
nexthop_nh->nh_gw = nla ? nla_get_be32(nla) : 0; |
c7066f70d netfilter: fix Kc... |
405 |
#ifdef CONFIG_IP_ROUTE_CLASSID |
4e902c574 [IPv4]: FIB confi... |
406 |
nla = nla_find(attrs, attrlen, RTA_FLOW); |
71fceff0e ipv4: Use less co... |
407 |
nexthop_nh->nh_tclassid = nla ? nla_get_u32(nla) : 0; |
1da177e4c Linux-2.6.12-rc2 |
408 409 |
#endif } |
4e902c574 [IPv4]: FIB confi... |
410 411 |
rtnh = rtnh_next(rtnh, &remaining); |
1da177e4c Linux-2.6.12-rc2 |
412 |
} endfor_nexthops(fi); |
4e902c574 [IPv4]: FIB confi... |
413 |
|
1da177e4c Linux-2.6.12-rc2 |
414 415 416 417 |
return 0; } #endif |
4e902c574 [IPv4]: FIB confi... |
418 |
int fib_nh_match(struct fib_config *cfg, struct fib_info *fi) |
1da177e4c Linux-2.6.12-rc2 |
419 420 |
{ #ifdef CONFIG_IP_ROUTE_MULTIPATH |
4e902c574 [IPv4]: FIB confi... |
421 422 |
struct rtnexthop *rtnh; int remaining; |
1da177e4c Linux-2.6.12-rc2 |
423 |
#endif |
4e902c574 [IPv4]: FIB confi... |
424 |
if (cfg->fc_priority && cfg->fc_priority != fi->fib_priority) |
1da177e4c Linux-2.6.12-rc2 |
425 |
return 1; |
4e902c574 [IPv4]: FIB confi... |
426 427 428 |
if (cfg->fc_oif || cfg->fc_gw) { if ((!cfg->fc_oif || cfg->fc_oif == fi->fib_nh->nh_oif) && (!cfg->fc_gw || cfg->fc_gw == fi->fib_nh->nh_gw)) |
1da177e4c Linux-2.6.12-rc2 |
429 430 431 432 433 |
return 0; return 1; } #ifdef CONFIG_IP_ROUTE_MULTIPATH |
4e902c574 [IPv4]: FIB confi... |
434 |
if (cfg->fc_mp == NULL) |
1da177e4c Linux-2.6.12-rc2 |
435 |
return 0; |
4e902c574 [IPv4]: FIB confi... |
436 437 438 |
rtnh = cfg->fc_mp; remaining = cfg->fc_mp_len; |
e905a9eda [NET] IPV4: Fix w... |
439 |
|
1da177e4c Linux-2.6.12-rc2 |
440 |
for_nexthops(fi) { |
4e902c574 [IPv4]: FIB confi... |
441 |
int attrlen; |
1da177e4c Linux-2.6.12-rc2 |
442 |
|
4e902c574 [IPv4]: FIB confi... |
443 |
if (!rtnh_ok(rtnh, remaining)) |
1da177e4c Linux-2.6.12-rc2 |
444 |
return -EINVAL; |
4e902c574 [IPv4]: FIB confi... |
445 446 |
if (rtnh->rtnh_ifindex && rtnh->rtnh_ifindex != nh->nh_oif) |
1da177e4c Linux-2.6.12-rc2 |
447 |
return 1; |
4e902c574 [IPv4]: FIB confi... |
448 449 450 451 452 453 |
attrlen = rtnh_attrlen(rtnh); if (attrlen < 0) { struct nlattr *nla, *attrs = rtnh_attrs(rtnh); nla = nla_find(attrs, attrlen, RTA_GATEWAY); |
17fb2c643 [IPV4]: RTA_{DST,... |
454 |
if (nla && nla_get_be32(nla) != nh->nh_gw) |
1da177e4c Linux-2.6.12-rc2 |
455 |
return 1; |
c7066f70d netfilter: fix Kc... |
456 |
#ifdef CONFIG_IP_ROUTE_CLASSID |
4e902c574 [IPv4]: FIB confi... |
457 458 |
nla = nla_find(attrs, attrlen, RTA_FLOW); if (nla && nla_get_u32(nla) != nh->nh_tclassid) |
1da177e4c Linux-2.6.12-rc2 |
459 460 461 |
return 1; #endif } |
4e902c574 [IPv4]: FIB confi... |
462 463 |
rtnh = rtnh_next(rtnh, &remaining); |
1da177e4c Linux-2.6.12-rc2 |
464 465 466 467 468 469 470 |
} endfor_nexthops(fi); #endif return 0; } /* |
6a31d2a97 fib: cleanups |
471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 |
* Picture * ------- * * Semantics of nexthop is very messy by historical reasons. * We have to take into account, that: * a) gateway can be actually local interface address, * so that gatewayed route is direct. * b) gateway must be on-link address, possibly * described not by an ifaddr, but also by a direct route. * c) If both gateway and interface are specified, they should not * contradict. * d) If we use tunnel routes, gateway could be not on-link. * * Attempt to reconcile all of these (alas, self-contradictory) conditions * results in pretty ugly and hairy code with obscure logic. * * I chose to generalized it instead, so that the size * of code does not increase practically, but it becomes * much more general. * Every prefix is assigned a "scope" value: "host" is local address, * "link" is direct route, * [ ... "site" ... "interior" ... ] * and "universe" is true gateway route with global meaning. * * Every prefix refers to a set of "nexthop"s (gw, oif), * where gw must have narrower scope. This recursion stops * when gw has LOCAL scope or if "nexthop" is declared ONLINK, * which means that gw is forced to be on link. * * Code is still hairy, but now it is apparently logically * consistent and very flexible. F.e. as by-product it allows * to co-exists in peace independent exterior and interior * routing processes. * * Normally it looks as following. * * {universe prefix} -> (gw, oif) [scope link] * | * |-> {link prefix} -> (gw, oif) [scope local] * | * |-> {local prefix} (terminal node) |
1da177e4c Linux-2.6.12-rc2 |
512 |
*/ |
4e902c574 [IPv4]: FIB confi... |
513 514 |
static int fib_check_nh(struct fib_config *cfg, struct fib_info *fi, struct fib_nh *nh) |
1da177e4c Linux-2.6.12-rc2 |
515 516 |
{ int err; |
86167a377 [NETNS]: Pass cor... |
517 |
struct net *net; |
6a31d2a97 fib: cleanups |
518 |
struct net_device *dev; |
1da177e4c Linux-2.6.12-rc2 |
519 |
|
86167a377 [NETNS]: Pass cor... |
520 |
net = cfg->fc_nlinfo.nl_net; |
1da177e4c Linux-2.6.12-rc2 |
521 522 |
if (nh->nh_gw) { struct fib_result res; |
6a31d2a97 fib: cleanups |
523 |
if (nh->nh_flags & RTNH_F_ONLINK) { |
1da177e4c Linux-2.6.12-rc2 |
524 |
|
4e902c574 [IPv4]: FIB confi... |
525 |
if (cfg->fc_scope >= RT_SCOPE_LINK) |
1da177e4c Linux-2.6.12-rc2 |
526 |
return -EINVAL; |
86167a377 [NETNS]: Pass cor... |
527 |
if (inet_addr_type(net, nh->nh_gw) != RTN_UNICAST) |
1da177e4c Linux-2.6.12-rc2 |
528 |
return -EINVAL; |
6a31d2a97 fib: cleanups |
529 530 |
dev = __dev_get_by_index(net, nh->nh_oif); if (!dev) |
1da177e4c Linux-2.6.12-rc2 |
531 |
return -ENODEV; |
6a31d2a97 fib: cleanups |
532 |
if (!(dev->flags & IFF_UP)) |
1da177e4c Linux-2.6.12-rc2 |
533 534 535 536 537 538 |
return -ENETDOWN; nh->nh_dev = dev; dev_hold(dev); nh->nh_scope = RT_SCOPE_LINK; return 0; } |
ebc0ffae5 fib: RCU conversi... |
539 |
rcu_read_lock(); |
1da177e4c Linux-2.6.12-rc2 |
540 |
{ |
9ade22861 ipv4: Use flowi4 ... |
541 542 543 544 |
struct flowi4 fl4 = { .daddr = nh->nh_gw, .flowi4_scope = cfg->fc_scope + 1, .flowi4_oif = nh->nh_oif, |
4e902c574 [IPv4]: FIB confi... |
545 |
}; |
1da177e4c Linux-2.6.12-rc2 |
546 547 |
/* It is not necessary, but requires a bit of thinking */ |
9ade22861 ipv4: Use flowi4 ... |
548 549 550 |
if (fl4.flowi4_scope < RT_SCOPE_LINK) fl4.flowi4_scope = RT_SCOPE_LINK; err = fib_lookup(net, &fl4, &res); |
ebc0ffae5 fib: RCU conversi... |
551 552 |
if (err) { rcu_read_unlock(); |
1da177e4c Linux-2.6.12-rc2 |
553 |
return err; |
ebc0ffae5 fib: RCU conversi... |
554 |
} |
1da177e4c Linux-2.6.12-rc2 |
555 556 557 558 559 560 |
} err = -EINVAL; if (res.type != RTN_UNICAST && res.type != RTN_LOCAL) goto out; nh->nh_scope = res.scope; nh->nh_oif = FIB_RES_OIF(res); |
6a31d2a97 fib: cleanups |
561 562 |
nh->nh_dev = dev = FIB_RES_DEV(res); if (!dev) |
1da177e4c Linux-2.6.12-rc2 |
563 |
goto out; |
6a31d2a97 fib: cleanups |
564 |
dev_hold(dev); |
8723e1b4a inet: RCU changes... |
565 |
err = (dev->flags & IFF_UP) ? 0 : -ENETDOWN; |
1da177e4c Linux-2.6.12-rc2 |
566 567 |
} else { struct in_device *in_dev; |
6a31d2a97 fib: cleanups |
568 |
if (nh->nh_flags & (RTNH_F_PERVASIVE | RTNH_F_ONLINK)) |
1da177e4c Linux-2.6.12-rc2 |
569 |
return -EINVAL; |
8723e1b4a inet: RCU changes... |
570 571 |
rcu_read_lock(); err = -ENODEV; |
86167a377 [NETNS]: Pass cor... |
572 |
in_dev = inetdev_by_index(net, nh->nh_oif); |
1da177e4c Linux-2.6.12-rc2 |
573 |
if (in_dev == NULL) |
8723e1b4a inet: RCU changes... |
574 575 576 577 |
goto out; err = -ENETDOWN; if (!(in_dev->dev->flags & IFF_UP)) goto out; |
1da177e4c Linux-2.6.12-rc2 |
578 579 580 |
nh->nh_dev = in_dev->dev; dev_hold(nh->nh_dev); nh->nh_scope = RT_SCOPE_HOST; |
8723e1b4a inet: RCU changes... |
581 |
err = 0; |
1da177e4c Linux-2.6.12-rc2 |
582 |
} |
8723e1b4a inet: RCU changes... |
583 584 585 |
out: rcu_read_unlock(); return err; |
1da177e4c Linux-2.6.12-rc2 |
586 |
} |
81f7bf6cb [IPV4]: net/ipv4/... |
587 |
static inline unsigned int fib_laddr_hashfn(__be32 val) |
1da177e4c Linux-2.6.12-rc2 |
588 |
{ |
123b9731b ipv4: Rename fib_... |
589 |
unsigned int mask = (fib_info_hash_size - 1); |
1da177e4c Linux-2.6.12-rc2 |
590 |
|
6a31d2a97 fib: cleanups |
591 592 593 |
return ((__force u32)val ^ ((__force u32)val >> 7) ^ ((__force u32)val >> 14)) & mask; |
1da177e4c Linux-2.6.12-rc2 |
594 |
} |
123b9731b ipv4: Rename fib_... |
595 |
static struct hlist_head *fib_info_hash_alloc(int bytes) |
1da177e4c Linux-2.6.12-rc2 |
596 597 |
{ if (bytes <= PAGE_SIZE) |
88f834916 [IPV4] fib_semant... |
598 |
return kzalloc(bytes, GFP_KERNEL); |
1da177e4c Linux-2.6.12-rc2 |
599 600 |
else return (struct hlist_head *) |
6a31d2a97 fib: cleanups |
601 602 |
__get_free_pages(GFP_KERNEL | __GFP_ZERO, get_order(bytes)); |
1da177e4c Linux-2.6.12-rc2 |
603 |
} |
123b9731b ipv4: Rename fib_... |
604 |
static void fib_info_hash_free(struct hlist_head *hash, int bytes) |
1da177e4c Linux-2.6.12-rc2 |
605 606 607 608 609 610 611 612 613 |
{ if (!hash) return; if (bytes <= PAGE_SIZE) kfree(hash); else free_pages((unsigned long) hash, get_order(bytes)); } |
123b9731b ipv4: Rename fib_... |
614 615 616 |
static void fib_info_hash_move(struct hlist_head *new_info_hash, struct hlist_head *new_laddrhash, unsigned int new_size) |
1da177e4c Linux-2.6.12-rc2 |
617 |
{ |
b7656e7f2 [IPV4]: Fix memor... |
618 |
struct hlist_head *old_info_hash, *old_laddrhash; |
123b9731b ipv4: Rename fib_... |
619 |
unsigned int old_size = fib_info_hash_size; |
b7656e7f2 [IPV4]: Fix memor... |
620 |
unsigned int i, bytes; |
1da177e4c Linux-2.6.12-rc2 |
621 |
|
832b4c5e1 [IPV4] fib: conve... |
622 |
spin_lock_bh(&fib_info_lock); |
b7656e7f2 [IPV4]: Fix memor... |
623 624 |
old_info_hash = fib_info_hash; old_laddrhash = fib_info_laddrhash; |
123b9731b ipv4: Rename fib_... |
625 |
fib_info_hash_size = new_size; |
1da177e4c Linux-2.6.12-rc2 |
626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 |
for (i = 0; i < old_size; i++) { struct hlist_head *head = &fib_info_hash[i]; struct hlist_node *node, *n; struct fib_info *fi; hlist_for_each_entry_safe(fi, node, n, head, fib_hash) { struct hlist_head *dest; unsigned int new_hash; hlist_del(&fi->fib_hash); new_hash = fib_info_hashfn(fi); dest = &new_info_hash[new_hash]; hlist_add_head(&fi->fib_hash, dest); } } fib_info_hash = new_info_hash; for (i = 0; i < old_size; i++) { struct hlist_head *lhead = &fib_info_laddrhash[i]; struct hlist_node *node, *n; struct fib_info *fi; hlist_for_each_entry_safe(fi, node, n, lhead, fib_lhash) { struct hlist_head *ldest; unsigned int new_hash; hlist_del(&fi->fib_lhash); new_hash = fib_laddr_hashfn(fi->fib_prefsrc); ldest = &new_laddrhash[new_hash]; hlist_add_head(&fi->fib_lhash, ldest); } } fib_info_laddrhash = new_laddrhash; |
832b4c5e1 [IPV4] fib: conve... |
662 |
spin_unlock_bh(&fib_info_lock); |
b7656e7f2 [IPV4]: Fix memor... |
663 664 |
bytes = old_size * sizeof(struct hlist_head *); |
123b9731b ipv4: Rename fib_... |
665 666 |
fib_info_hash_free(old_info_hash, bytes); fib_info_hash_free(old_laddrhash, bytes); |
1da177e4c Linux-2.6.12-rc2 |
667 |
} |
436c3b66e ipv4: Invalidate ... |
668 669 670 671 |
__be32 fib_info_update_nh_saddr(struct net *net, struct fib_nh *nh) { nh->nh_saddr = inet_select_addr(nh->nh_dev, nh->nh_gw, |
37e826c51 ipv4: Fix nexthop... |
672 |
nh->nh_parent->fib_scope); |
436c3b66e ipv4: Invalidate ... |
673 674 675 676 |
nh->nh_saddr_genid = atomic_read(&net->ipv4.dev_addr_genid); return nh->nh_saddr; } |
4e902c574 [IPv4]: FIB confi... |
677 |
struct fib_info *fib_create_info(struct fib_config *cfg) |
1da177e4c Linux-2.6.12-rc2 |
678 679 680 681 |
{ int err; struct fib_info *fi = NULL; struct fib_info *ofi; |
1da177e4c Linux-2.6.12-rc2 |
682 |
int nhs = 1; |
7462bd744 [NETNS]: Add a na... |
683 |
struct net *net = cfg->fc_nlinfo.nl_net; |
1da177e4c Linux-2.6.12-rc2 |
684 |
|
4c8237cd7 ipv4: Validate ro... |
685 686 |
if (cfg->fc_type > RTN_MAX) goto err_inval; |
1da177e4c Linux-2.6.12-rc2 |
687 |
/* Fast check to catch the most weird cases */ |
4e902c574 [IPv4]: FIB confi... |
688 |
if (fib_props[cfg->fc_type].scope > cfg->fc_scope) |
1da177e4c Linux-2.6.12-rc2 |
689 690 691 |
goto err_inval; #ifdef CONFIG_IP_ROUTE_MULTIPATH |
4e902c574 [IPv4]: FIB confi... |
692 693 |
if (cfg->fc_mp) { nhs = fib_count_nexthops(cfg->fc_mp, cfg->fc_mp_len); |
1da177e4c Linux-2.6.12-rc2 |
694 695 696 697 |
if (nhs == 0) goto err_inval; } #endif |
1da177e4c Linux-2.6.12-rc2 |
698 699 |
err = -ENOBUFS; |
123b9731b ipv4: Rename fib_... |
700 701 |
if (fib_info_cnt >= fib_info_hash_size) { unsigned int new_size = fib_info_hash_size << 1; |
1da177e4c Linux-2.6.12-rc2 |
702 703 704 705 706 707 708 |
struct hlist_head *new_info_hash; struct hlist_head *new_laddrhash; unsigned int bytes; if (!new_size) new_size = 1; bytes = new_size * sizeof(struct hlist_head *); |
123b9731b ipv4: Rename fib_... |
709 710 |
new_info_hash = fib_info_hash_alloc(bytes); new_laddrhash = fib_info_hash_alloc(bytes); |
1da177e4c Linux-2.6.12-rc2 |
711 |
if (!new_info_hash || !new_laddrhash) { |
123b9731b ipv4: Rename fib_... |
712 713 |
fib_info_hash_free(new_info_hash, bytes); fib_info_hash_free(new_laddrhash, bytes); |
88f834916 [IPV4] fib_semant... |
714 |
} else |
123b9731b ipv4: Rename fib_... |
715 |
fib_info_hash_move(new_info_hash, new_laddrhash, new_size); |
1da177e4c Linux-2.6.12-rc2 |
716 |
|
123b9731b ipv4: Rename fib_... |
717 |
if (!fib_info_hash_size) |
1da177e4c Linux-2.6.12-rc2 |
718 719 |
goto failure; } |
0da974f4f [NET]: Conversion... |
720 |
fi = kzalloc(sizeof(*fi)+nhs*sizeof(struct fib_nh), GFP_KERNEL); |
1da177e4c Linux-2.6.12-rc2 |
721 722 |
if (fi == NULL) goto failure; |
725d1e1b4 ipv4: Attach FIB ... |
723 724 725 726 727 728 |
if (cfg->fc_mx) { fi->fib_metrics = kzalloc(sizeof(u32) * RTAX_MAX, GFP_KERNEL); if (!fi->fib_metrics) goto failure; } else fi->fib_metrics = (u32 *) dst_default_metrics; |
1da177e4c Linux-2.6.12-rc2 |
729 |
fib_info_cnt++; |
1da177e4c Linux-2.6.12-rc2 |
730 |
|
57d7a6009 [NETNS]: Add netn... |
731 |
fi->fib_net = hold_net(net); |
4e902c574 [IPv4]: FIB confi... |
732 |
fi->fib_protocol = cfg->fc_protocol; |
37e826c51 ipv4: Fix nexthop... |
733 |
fi->fib_scope = cfg->fc_scope; |
4e902c574 [IPv4]: FIB confi... |
734 735 736 |
fi->fib_flags = cfg->fc_flags; fi->fib_priority = cfg->fc_priority; fi->fib_prefsrc = cfg->fc_prefsrc; |
1da177e4c Linux-2.6.12-rc2 |
737 738 739 |
fi->fib_nhs = nhs; change_nexthops(fi) { |
71fceff0e ipv4: Use less co... |
740 |
nexthop_nh->nh_parent = fi; |
1da177e4c Linux-2.6.12-rc2 |
741 |
} endfor_nexthops(fi) |
4e902c574 [IPv4]: FIB confi... |
742 743 744 745 746 |
if (cfg->fc_mx) { struct nlattr *nla; int remaining; nla_for_each_attr(nla, cfg->fc_mx, cfg->fc_mx_len, remaining) { |
8f4c1f9b0 [NETLINK]: Introd... |
747 |
int type = nla_type(nla); |
4e902c574 [IPv4]: FIB confi... |
748 749 750 |
if (type) { if (type > RTAX_MAX) |
1da177e4c Linux-2.6.12-rc2 |
751 |
goto err_inval; |
4e902c574 [IPv4]: FIB confi... |
752 |
fi->fib_metrics[type - 1] = nla_get_u32(nla); |
1da177e4c Linux-2.6.12-rc2 |
753 |
} |
1da177e4c Linux-2.6.12-rc2 |
754 755 |
} } |
1da177e4c Linux-2.6.12-rc2 |
756 |
|
4e902c574 [IPv4]: FIB confi... |
757 |
if (cfg->fc_mp) { |
1da177e4c Linux-2.6.12-rc2 |
758 |
#ifdef CONFIG_IP_ROUTE_MULTIPATH |
4e902c574 [IPv4]: FIB confi... |
759 760 |
err = fib_get_nhs(fi, cfg->fc_mp, cfg->fc_mp_len, cfg); if (err != 0) |
1da177e4c Linux-2.6.12-rc2 |
761 |
goto failure; |
4e902c574 [IPv4]: FIB confi... |
762 |
if (cfg->fc_oif && fi->fib_nh->nh_oif != cfg->fc_oif) |
1da177e4c Linux-2.6.12-rc2 |
763 |
goto err_inval; |
4e902c574 [IPv4]: FIB confi... |
764 |
if (cfg->fc_gw && fi->fib_nh->nh_gw != cfg->fc_gw) |
1da177e4c Linux-2.6.12-rc2 |
765 |
goto err_inval; |
c7066f70d netfilter: fix Kc... |
766 |
#ifdef CONFIG_IP_ROUTE_CLASSID |
4e902c574 [IPv4]: FIB confi... |
767 |
if (cfg->fc_flow && fi->fib_nh->nh_tclassid != cfg->fc_flow) |
1da177e4c Linux-2.6.12-rc2 |
768 769 770 771 772 773 774 |
goto err_inval; #endif #else goto err_inval; #endif } else { struct fib_nh *nh = fi->fib_nh; |
4e902c574 [IPv4]: FIB confi... |
775 776 777 778 |
nh->nh_oif = cfg->fc_oif; nh->nh_gw = cfg->fc_gw; nh->nh_flags = cfg->fc_flags; |
c7066f70d netfilter: fix Kc... |
779 |
#ifdef CONFIG_IP_ROUTE_CLASSID |
4e902c574 [IPv4]: FIB confi... |
780 |
nh->nh_tclassid = cfg->fc_flow; |
1da177e4c Linux-2.6.12-rc2 |
781 |
#endif |
1da177e4c Linux-2.6.12-rc2 |
782 783 784 785 |
#ifdef CONFIG_IP_ROUTE_MULTIPATH nh->nh_weight = 1; #endif } |
4e902c574 [IPv4]: FIB confi... |
786 787 |
if (fib_props[cfg->fc_type].error) { if (cfg->fc_gw || cfg->fc_oif || cfg->fc_mp) |
1da177e4c Linux-2.6.12-rc2 |
788 789 |
goto err_inval; goto link_it; |
4c8237cd7 ipv4: Validate ro... |
790 791 792 793 794 795 796 797 798 799 800 |
} else { switch (cfg->fc_type) { case RTN_UNICAST: case RTN_LOCAL: case RTN_BROADCAST: case RTN_ANYCAST: case RTN_MULTICAST: break; default: goto err_inval; } |
1da177e4c Linux-2.6.12-rc2 |
801 |
} |
4e902c574 [IPv4]: FIB confi... |
802 |
if (cfg->fc_scope > RT_SCOPE_HOST) |
1da177e4c Linux-2.6.12-rc2 |
803 |
goto err_inval; |
4e902c574 [IPv4]: FIB confi... |
804 |
if (cfg->fc_scope == RT_SCOPE_HOST) { |
1da177e4c Linux-2.6.12-rc2 |
805 806 807 808 809 810 |
struct fib_nh *nh = fi->fib_nh; /* Local address is added. */ if (nhs != 1 || nh->nh_gw) goto err_inval; nh->nh_scope = RT_SCOPE_NOWHERE; |
7462bd744 [NETNS]: Add a na... |
811 |
nh->nh_dev = dev_get_by_index(net, fi->fib_nh->nh_oif); |
1da177e4c Linux-2.6.12-rc2 |
812 813 814 815 816 |
err = -ENODEV; if (nh->nh_dev == NULL) goto failure; } else { change_nexthops(fi) { |
6a31d2a97 fib: cleanups |
817 818 |
err = fib_check_nh(cfg, fi, nexthop_nh); if (err != 0) |
1da177e4c Linux-2.6.12-rc2 |
819 820 821 822 823 |
goto failure; } endfor_nexthops(fi) } if (fi->fib_prefsrc) { |
4e902c574 [IPv4]: FIB confi... |
824 825 |
if (cfg->fc_type != RTN_LOCAL || !cfg->fc_dst || fi->fib_prefsrc != cfg->fc_dst) |
7462bd744 [NETNS]: Add a na... |
826 |
if (inet_addr_type(net, fi->fib_prefsrc) != RTN_LOCAL) |
1da177e4c Linux-2.6.12-rc2 |
827 828 |
goto err_inval; } |
1fc050a13 ipv4: Cache sourc... |
829 |
change_nexthops(fi) { |
436c3b66e ipv4: Invalidate ... |
830 |
fib_info_update_nh_saddr(net, nexthop_nh); |
1fc050a13 ipv4: Cache sourc... |
831 |
} endfor_nexthops(fi) |
1da177e4c Linux-2.6.12-rc2 |
832 |
link_it: |
6a31d2a97 fib: cleanups |
833 834 |
ofi = fib_find_info(fi); if (ofi) { |
1da177e4c Linux-2.6.12-rc2 |
835 836 837 838 839 840 841 842 |
fi->fib_dead = 1; free_fib_info(fi); ofi->fib_treeref++; return ofi; } fi->fib_treeref++; atomic_inc(&fi->fib_clntref); |
832b4c5e1 [IPV4] fib: conve... |
843 |
spin_lock_bh(&fib_info_lock); |
1da177e4c Linux-2.6.12-rc2 |
844 845 846 847 848 849 850 851 852 853 854 |
hlist_add_head(&fi->fib_hash, &fib_info_hash[fib_info_hashfn(fi)]); if (fi->fib_prefsrc) { struct hlist_head *head; head = &fib_info_laddrhash[fib_laddr_hashfn(fi->fib_prefsrc)]; hlist_add_head(&fi->fib_lhash, head); } change_nexthops(fi) { struct hlist_head *head; unsigned int hash; |
71fceff0e ipv4: Use less co... |
855 |
if (!nexthop_nh->nh_dev) |
1da177e4c Linux-2.6.12-rc2 |
856 |
continue; |
71fceff0e ipv4: Use less co... |
857 |
hash = fib_devindex_hashfn(nexthop_nh->nh_dev->ifindex); |
1da177e4c Linux-2.6.12-rc2 |
858 |
head = &fib_info_devhash[hash]; |
71fceff0e ipv4: Use less co... |
859 |
hlist_add_head(&nexthop_nh->nh_hash, head); |
1da177e4c Linux-2.6.12-rc2 |
860 |
} endfor_nexthops(fi) |
832b4c5e1 [IPV4] fib: conve... |
861 |
spin_unlock_bh(&fib_info_lock); |
1da177e4c Linux-2.6.12-rc2 |
862 863 864 865 866 867 |
return fi; err_inval: err = -EINVAL; failure: |
e905a9eda [NET] IPV4: Fix w... |
868 |
if (fi) { |
1da177e4c Linux-2.6.12-rc2 |
869 870 871 |
fi->fib_dead = 1; free_fib_info(fi); } |
4e902c574 [IPv4]: FIB confi... |
872 873 |
return ERR_PTR(err); |
1da177e4c Linux-2.6.12-rc2 |
874 |
} |
be403ea18 [IPv4]: Convert F... |
875 |
int fib_dump_info(struct sk_buff *skb, u32 pid, u32 seq, int event, |
37e826c51 ipv4: Fix nexthop... |
876 |
u32 tb_id, u8 type, __be32 dst, int dst_len, u8 tos, |
be403ea18 [IPv4]: Convert F... |
877 |
struct fib_info *fi, unsigned int flags) |
1da177e4c Linux-2.6.12-rc2 |
878 |
{ |
be403ea18 [IPv4]: Convert F... |
879 |
struct nlmsghdr *nlh; |
1da177e4c Linux-2.6.12-rc2 |
880 |
struct rtmsg *rtm; |
1da177e4c Linux-2.6.12-rc2 |
881 |
|
be403ea18 [IPv4]: Convert F... |
882 883 |
nlh = nlmsg_put(skb, pid, seq, event, sizeof(*rtm), flags); if (nlh == NULL) |
26932566a [NETLINK]: Don't ... |
884 |
return -EMSGSIZE; |
be403ea18 [IPv4]: Convert F... |
885 886 |
rtm = nlmsg_data(nlh); |
1da177e4c Linux-2.6.12-rc2 |
887 888 889 890 |
rtm->rtm_family = AF_INET; rtm->rtm_dst_len = dst_len; rtm->rtm_src_len = 0; rtm->rtm_tos = tos; |
709772e6e net: Fix routing ... |
891 892 893 894 |
if (tb_id < 256) rtm->rtm_table = tb_id; else rtm->rtm_table = RT_TABLE_COMPAT; |
be403ea18 [IPv4]: Convert F... |
895 |
NLA_PUT_U32(skb, RTA_TABLE, tb_id); |
1da177e4c Linux-2.6.12-rc2 |
896 897 |
rtm->rtm_type = type; rtm->rtm_flags = fi->fib_flags; |
37e826c51 ipv4: Fix nexthop... |
898 |
rtm->rtm_scope = fi->fib_scope; |
1da177e4c Linux-2.6.12-rc2 |
899 |
rtm->rtm_protocol = fi->fib_protocol; |
be403ea18 [IPv4]: Convert F... |
900 901 |
if (rtm->rtm_dst_len) |
17fb2c643 [IPV4]: RTA_{DST,... |
902 |
NLA_PUT_BE32(skb, RTA_DST, dst); |
be403ea18 [IPv4]: Convert F... |
903 |
|
1da177e4c Linux-2.6.12-rc2 |
904 |
if (fi->fib_priority) |
be403ea18 [IPv4]: Convert F... |
905 |
NLA_PUT_U32(skb, RTA_PRIORITY, fi->fib_priority); |
1da177e4c Linux-2.6.12-rc2 |
906 |
if (rtnetlink_put_metrics(skb, fi->fib_metrics) < 0) |
be403ea18 [IPv4]: Convert F... |
907 |
goto nla_put_failure; |
1da177e4c Linux-2.6.12-rc2 |
908 |
if (fi->fib_prefsrc) |
17fb2c643 [IPV4]: RTA_{DST,... |
909 |
NLA_PUT_BE32(skb, RTA_PREFSRC, fi->fib_prefsrc); |
be403ea18 [IPv4]: Convert F... |
910 |
|
1da177e4c Linux-2.6.12-rc2 |
911 912 |
if (fi->fib_nhs == 1) { if (fi->fib_nh->nh_gw) |
17fb2c643 [IPV4]: RTA_{DST,... |
913 |
NLA_PUT_BE32(skb, RTA_GATEWAY, fi->fib_nh->nh_gw); |
be403ea18 [IPv4]: Convert F... |
914 |
|
1da177e4c Linux-2.6.12-rc2 |
915 |
if (fi->fib_nh->nh_oif) |
be403ea18 [IPv4]: Convert F... |
916 |
NLA_PUT_U32(skb, RTA_OIF, fi->fib_nh->nh_oif); |
c7066f70d netfilter: fix Kc... |
917 |
#ifdef CONFIG_IP_ROUTE_CLASSID |
8265abc08 [IPV4]: Fix nexth... |
918 |
if (fi->fib_nh[0].nh_tclassid) |
be403ea18 [IPv4]: Convert F... |
919 |
NLA_PUT_U32(skb, RTA_FLOW, fi->fib_nh[0].nh_tclassid); |
8265abc08 [IPV4]: Fix nexth... |
920 |
#endif |
1da177e4c Linux-2.6.12-rc2 |
921 922 923 |
} #ifdef CONFIG_IP_ROUTE_MULTIPATH if (fi->fib_nhs > 1) { |
be403ea18 [IPv4]: Convert F... |
924 925 926 927 928 929 |
struct rtnexthop *rtnh; struct nlattr *mp; mp = nla_nest_start(skb, RTA_MULTIPATH); if (mp == NULL) goto nla_put_failure; |
1da177e4c Linux-2.6.12-rc2 |
930 931 |
for_nexthops(fi) { |
be403ea18 [IPv4]: Convert F... |
932 933 934 935 936 937 938 |
rtnh = nla_reserve_nohdr(skb, sizeof(*rtnh)); if (rtnh == NULL) goto nla_put_failure; rtnh->rtnh_flags = nh->nh_flags & 0xFF; rtnh->rtnh_hops = nh->nh_weight - 1; rtnh->rtnh_ifindex = nh->nh_oif; |
1da177e4c Linux-2.6.12-rc2 |
939 |
if (nh->nh_gw) |
17fb2c643 [IPV4]: RTA_{DST,... |
940 |
NLA_PUT_BE32(skb, RTA_GATEWAY, nh->nh_gw); |
c7066f70d netfilter: fix Kc... |
941 |
#ifdef CONFIG_IP_ROUTE_CLASSID |
8265abc08 [IPV4]: Fix nexth... |
942 |
if (nh->nh_tclassid) |
be403ea18 [IPv4]: Convert F... |
943 |
NLA_PUT_U32(skb, RTA_FLOW, nh->nh_tclassid); |
8265abc08 [IPV4]: Fix nexth... |
944 |
#endif |
be403ea18 [IPv4]: Convert F... |
945 946 |
/* length of rtnetlink header + attributes */ rtnh->rtnh_len = nlmsg_get_pos(skb) - (void *) rtnh; |
1da177e4c Linux-2.6.12-rc2 |
947 |
} endfor_nexthops(fi); |
be403ea18 [IPv4]: Convert F... |
948 949 |
nla_nest_end(skb, mp); |
1da177e4c Linux-2.6.12-rc2 |
950 951 |
} #endif |
be403ea18 [IPv4]: Convert F... |
952 |
return nlmsg_end(skb, nlh); |
1da177e4c Linux-2.6.12-rc2 |
953 |
|
be403ea18 [IPv4]: Convert F... |
954 |
nla_put_failure: |
26932566a [NETLINK]: Don't ... |
955 956 |
nlmsg_cancel(skb, nlh); return -EMSGSIZE; |
1da177e4c Linux-2.6.12-rc2 |
957 |
} |
1da177e4c Linux-2.6.12-rc2 |
958 |
/* |
6a31d2a97 fib: cleanups |
959 960 961 962 |
* Update FIB if: * - local address disappeared -> we must delete all the entries * referring to it. * - device went down -> we must shutdown all nexthops going via it. |
1da177e4c Linux-2.6.12-rc2 |
963 |
*/ |
4814bdbd5 [NETNS]: Lookup i... |
964 |
int fib_sync_down_addr(struct net *net, __be32 local) |
1da177e4c Linux-2.6.12-rc2 |
965 966 |
{ int ret = 0; |
85326fa54 [IPV4]: fib_sync_... |
967 968 969 970 |
unsigned int hash = fib_laddr_hashfn(local); struct hlist_head *head = &fib_info_laddrhash[hash]; struct hlist_node *node; struct fib_info *fi; |
1da177e4c Linux-2.6.12-rc2 |
971 |
|
85326fa54 [IPV4]: fib_sync_... |
972 973 |
if (fib_info_laddrhash == NULL || local == 0) return 0; |
1da177e4c Linux-2.6.12-rc2 |
974 |
|
85326fa54 [IPV4]: fib_sync_... |
975 |
hlist_for_each_entry(fi, node, head, fib_lhash) { |
09ad9bc75 net: use net_eq t... |
976 |
if (!net_eq(fi->fib_net, net)) |
4814bdbd5 [NETNS]: Lookup i... |
977 |
continue; |
85326fa54 [IPV4]: fib_sync_... |
978 979 980 |
if (fi->fib_prefsrc == local) { fi->fib_flags |= RTNH_F_DEAD; ret++; |
1da177e4c Linux-2.6.12-rc2 |
981 982 |
} } |
85326fa54 [IPV4]: fib_sync_... |
983 984 985 986 987 988 989 990 991 992 993 994 |
return ret; } int fib_sync_down_dev(struct net_device *dev, int force) { int ret = 0; int scope = RT_SCOPE_NOWHERE; struct fib_info *prev_fi = NULL; unsigned int hash = fib_devindex_hashfn(dev->ifindex); struct hlist_head *head = &fib_info_devhash[hash]; struct hlist_node *node; struct fib_nh *nh; |
1da177e4c Linux-2.6.12-rc2 |
995 |
|
85326fa54 [IPV4]: fib_sync_... |
996 997 |
if (force) scope = -1; |
1da177e4c Linux-2.6.12-rc2 |
998 |
|
85326fa54 [IPV4]: fib_sync_... |
999 1000 1001 |
hlist_for_each_entry(nh, node, head, nh_hash) { struct fib_info *fi = nh->nh_parent; int dead; |
1da177e4c Linux-2.6.12-rc2 |
1002 |
|
85326fa54 [IPV4]: fib_sync_... |
1003 1004 1005 1006 1007 1008 |
BUG_ON(!fi->fib_nhs); if (nh->nh_dev != dev || fi == prev_fi) continue; prev_fi = fi; dead = 0; change_nexthops(fi) { |
6a31d2a97 fib: cleanups |
1009 |
if (nexthop_nh->nh_flags & RTNH_F_DEAD) |
85326fa54 [IPV4]: fib_sync_... |
1010 |
dead++; |
71fceff0e ipv4: Use less co... |
1011 1012 1013 |
else if (nexthop_nh->nh_dev == dev && nexthop_nh->nh_scope != scope) { nexthop_nh->nh_flags |= RTNH_F_DEAD; |
1da177e4c Linux-2.6.12-rc2 |
1014 |
#ifdef CONFIG_IP_ROUTE_MULTIPATH |
85326fa54 [IPV4]: fib_sync_... |
1015 |
spin_lock_bh(&fib_multipath_lock); |
71fceff0e ipv4: Use less co... |
1016 1017 |
fi->fib_power -= nexthop_nh->nh_power; nexthop_nh->nh_power = 0; |
85326fa54 [IPV4]: fib_sync_... |
1018 |
spin_unlock_bh(&fib_multipath_lock); |
1da177e4c Linux-2.6.12-rc2 |
1019 |
#endif |
85326fa54 [IPV4]: fib_sync_... |
1020 1021 |
dead++; } |
1da177e4c Linux-2.6.12-rc2 |
1022 |
#ifdef CONFIG_IP_ROUTE_MULTIPATH |
71fceff0e ipv4: Use less co... |
1023 |
if (force > 1 && nexthop_nh->nh_dev == dev) { |
85326fa54 [IPV4]: fib_sync_... |
1024 1025 |
dead = fi->fib_nhs; break; |
1da177e4c Linux-2.6.12-rc2 |
1026 |
} |
85326fa54 [IPV4]: fib_sync_... |
1027 1028 1029 1030 1031 |
#endif } endfor_nexthops(fi) if (dead == fi->fib_nhs) { fi->fib_flags |= RTNH_F_DEAD; ret++; |
1da177e4c Linux-2.6.12-rc2 |
1032 1033 1034 1035 1036 |
} } return ret; } |
0c838ff1a ipv4: Consolidate... |
1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 |
/* Must be invoked inside of an RCU protected region. */ void fib_select_default(struct fib_result *res) { struct fib_info *fi = NULL, *last_resort = NULL; struct list_head *fa_head = res->fa_head; struct fib_table *tb = res->table; int order = -1, last_idx = -1; struct fib_alias *fa; list_for_each_entry_rcu(fa, fa_head, fa_list) { struct fib_info *next_fi = fa->fa_info; |
37e826c51 ipv4: Fix nexthop... |
1048 |
if (next_fi->fib_scope != res->scope || |
0c838ff1a ipv4: Consolidate... |
1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 |
fa->fa_type != RTN_UNICAST) continue; if (next_fi->fib_priority > res->fi->fib_priority) break; if (!next_fi->fib_nh[0].nh_gw || next_fi->fib_nh[0].nh_scope != RT_SCOPE_LINK) continue; fib_alias_accessed(fa); if (fi == NULL) { if (next_fi != res->fi) break; } else if (!fib_detect_death(fi, order, &last_resort, &last_idx, tb->tb_default)) { fib_result_assign(res, fi); tb->tb_default = order; goto out; } fi = next_fi; order++; } if (order <= 0 || fi == NULL) { tb->tb_default = -1; goto out; } if (!fib_detect_death(fi, order, &last_resort, &last_idx, tb->tb_default)) { fib_result_assign(res, fi); tb->tb_default = order; goto out; } if (last_idx >= 0) fib_result_assign(res, last_resort); tb->tb_default = last_idx; out: |
31d409373 ipv4: fix rcu loc... |
1089 |
return; |
0c838ff1a ipv4: Consolidate... |
1090 |
} |
1da177e4c Linux-2.6.12-rc2 |
1091 1092 1093 |
#ifdef CONFIG_IP_ROUTE_MULTIPATH /* |
6a31d2a97 fib: cleanups |
1094 1095 |
* Dead device goes up. We wake up dead nexthops. * It takes sense only on multipath routes. |
1da177e4c Linux-2.6.12-rc2 |
1096 |
*/ |
1da177e4c Linux-2.6.12-rc2 |
1097 1098 1099 1100 1101 1102 1103 1104 |
int fib_sync_up(struct net_device *dev) { struct fib_info *prev_fi; unsigned int hash; struct hlist_head *head; struct hlist_node *node; struct fib_nh *nh; int ret; |
6a31d2a97 fib: cleanups |
1105 |
if (!(dev->flags & IFF_UP)) |
1da177e4c Linux-2.6.12-rc2 |
1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 |
return 0; prev_fi = NULL; hash = fib_devindex_hashfn(dev->ifindex); head = &fib_info_devhash[hash]; ret = 0; hlist_for_each_entry(nh, node, head, nh_hash) { struct fib_info *fi = nh->nh_parent; int alive; BUG_ON(!fi->fib_nhs); if (nh->nh_dev != dev || fi == prev_fi) continue; prev_fi = fi; alive = 0; change_nexthops(fi) { |
6a31d2a97 fib: cleanups |
1124 |
if (!(nexthop_nh->nh_flags & RTNH_F_DEAD)) { |
1da177e4c Linux-2.6.12-rc2 |
1125 1126 1127 |
alive++; continue; } |
71fceff0e ipv4: Use less co... |
1128 |
if (nexthop_nh->nh_dev == NULL || |
6a31d2a97 fib: cleanups |
1129 |
!(nexthop_nh->nh_dev->flags & IFF_UP)) |
1da177e4c Linux-2.6.12-rc2 |
1130 |
continue; |
71fceff0e ipv4: Use less co... |
1131 1132 |
if (nexthop_nh->nh_dev != dev || !__in_dev_get_rtnl(dev)) |
1da177e4c Linux-2.6.12-rc2 |
1133 1134 1135 |
continue; alive++; spin_lock_bh(&fib_multipath_lock); |
71fceff0e ipv4: Use less co... |
1136 1137 |
nexthop_nh->nh_power = 0; nexthop_nh->nh_flags &= ~RTNH_F_DEAD; |
1da177e4c Linux-2.6.12-rc2 |
1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 |
spin_unlock_bh(&fib_multipath_lock); } endfor_nexthops(fi) if (alive > 0) { fi->fib_flags &= ~RTNH_F_DEAD; ret++; } } return ret; } /* |
6a31d2a97 fib: cleanups |
1151 1152 |
* The algorithm is suboptimal, but it provides really * fair weighted route distribution. |
1da177e4c Linux-2.6.12-rc2 |
1153 |
*/ |
1b7fe5932 ipv4: Kill flowi ... |
1154 |
void fib_select_multipath(struct fib_result *res) |
1da177e4c Linux-2.6.12-rc2 |
1155 1156 1157 1158 1159 1160 1161 1162 |
{ struct fib_info *fi = res->fi; int w; spin_lock_bh(&fib_multipath_lock); if (fi->fib_power <= 0) { int power = 0; change_nexthops(fi) { |
6a31d2a97 fib: cleanups |
1163 |
if (!(nexthop_nh->nh_flags & RTNH_F_DEAD)) { |
71fceff0e ipv4: Use less co... |
1164 1165 |
power += nexthop_nh->nh_weight; nexthop_nh->nh_power = nexthop_nh->nh_weight; |
1da177e4c Linux-2.6.12-rc2 |
1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 |
} } endfor_nexthops(fi); fi->fib_power = power; if (power <= 0) { spin_unlock_bh(&fib_multipath_lock); /* Race condition: route has just become dead. */ res->nh_sel = 0; return; } } /* w should be random number [0..fi->fib_power-1], |
6a31d2a97 fib: cleanups |
1179 |
* it is pretty bad approximation. |
1da177e4c Linux-2.6.12-rc2 |
1180 1181 1182 1183 1184 |
*/ w = jiffies % fi->fib_power; change_nexthops(fi) { |
6a31d2a97 fib: cleanups |
1185 |
if (!(nexthop_nh->nh_flags & RTNH_F_DEAD) && |
71fceff0e ipv4: Use less co... |
1186 |
nexthop_nh->nh_power) { |
6a31d2a97 fib: cleanups |
1187 1188 |
w -= nexthop_nh->nh_power; if (w <= 0) { |
71fceff0e ipv4: Use less co... |
1189 |
nexthop_nh->nh_power--; |
1da177e4c Linux-2.6.12-rc2 |
1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 |
fi->fib_power--; res->nh_sel = nhsel; spin_unlock_bh(&fib_multipath_lock); return; } } } endfor_nexthops(fi); /* Race condition: route has just become dead. */ res->nh_sel = 0; spin_unlock_bh(&fib_multipath_lock); } #endif |