aboutsummaryrefslogtreecommitdiffstats
path: root/net/ipv6/ip6_fib.c
diff options
context:
space:
mode:
Diffstat (limited to 'net/ipv6/ip6_fib.c')
-rw-r--r--net/ipv6/ip6_fib.c124
1 files changed, 83 insertions, 41 deletions
diff --git a/net/ipv6/ip6_fib.c b/net/ipv6/ip6_fib.c
index 075602fc6b6a..87891f5f57b5 100644
--- a/net/ipv6/ip6_fib.c
+++ b/net/ipv6/ip6_fib.c
@@ -9,14 +9,12 @@
9 * modify it under the terms of the GNU General Public License 9 * modify it under the terms of the GNU General Public License
10 * as published by the Free Software Foundation; either version 10 * as published by the Free Software Foundation; either version
11 * 2 of the License, or (at your option) any later version. 11 * 2 of the License, or (at your option) any later version.
12 */ 12 *
13 13 * Changes:
14/* 14 * Yuji SEKIYA @USAGI: Support default route on router node;
15 * Changes: 15 * remove ip6_null_entry from the top of
16 * Yuji SEKIYA @USAGI: Support default route on router node; 16 * routing table.
17 * remove ip6_null_entry from the top of 17 * Ville Nuorvala: Fixed routing subtrees.
18 * routing table.
19 * Ville Nuorvala: Fixed routing subtrees.
20 */ 18 */
21 19
22#define pr_fmt(fmt) "IPv6: " fmt 20#define pr_fmt(fmt) "IPv6: " fmt
@@ -46,10 +44,9 @@
46#define RT6_TRACE(x...) do { ; } while (0) 44#define RT6_TRACE(x...) do { ; } while (0)
47#endif 45#endif
48 46
49static struct kmem_cache * fib6_node_kmem __read_mostly; 47static struct kmem_cache *fib6_node_kmem __read_mostly;
50 48
51enum fib_walk_state_t 49enum fib_walk_state_t {
52{
53#ifdef CONFIG_IPV6_SUBTREES 50#ifdef CONFIG_IPV6_SUBTREES
54 FWS_S, 51 FWS_S,
55#endif 52#endif
@@ -59,8 +56,7 @@ enum fib_walk_state_t
59 FWS_U 56 FWS_U
60}; 57};
61 58
62struct fib6_cleaner_t 59struct fib6_cleaner_t {
63{
64 struct fib6_walker_t w; 60 struct fib6_walker_t w;
65 struct net *net; 61 struct net *net;
66 int (*func)(struct rt6_info *, void *arg); 62 int (*func)(struct rt6_info *, void *arg);
@@ -138,7 +134,7 @@ static __inline__ __be32 addr_bit_set(const void *token, int fn_bit)
138 const __be32 *addr = token; 134 const __be32 *addr = token;
139 /* 135 /*
140 * Here, 136 * Here,
141 * 1 << ((~fn_bit ^ BITOP_BE32_SWIZZLE) & 0x1f) 137 * 1 << ((~fn_bit ^ BITOP_BE32_SWIZZLE) & 0x1f)
142 * is optimized version of 138 * is optimized version of
143 * htonl(1 << ((~fn_bit)&0x1F)) 139 * htonl(1 << ((~fn_bit)&0x1F))
144 * See include/asm-generic/bitops/le.h. 140 * See include/asm-generic/bitops/le.h.
@@ -147,7 +143,7 @@ static __inline__ __be32 addr_bit_set(const void *token, int fn_bit)
147 addr[fn_bit >> 5]; 143 addr[fn_bit >> 5];
148} 144}
149 145
150static __inline__ struct fib6_node * node_alloc(void) 146static __inline__ struct fib6_node *node_alloc(void)
151{ 147{
152 struct fib6_node *fn; 148 struct fib6_node *fn;
153 149
@@ -156,7 +152,7 @@ static __inline__ struct fib6_node * node_alloc(void)
156 return fn; 152 return fn;
157} 153}
158 154
159static __inline__ void node_free(struct fib6_node * fn) 155static __inline__ void node_free(struct fib6_node *fn)
160{ 156{
161 kmem_cache_free(fib6_node_kmem, fn); 157 kmem_cache_free(fib6_node_kmem, fn);
162} 158}
@@ -292,7 +288,7 @@ static int fib6_dump_node(struct fib6_walker_t *w)
292 288
293static void fib6_dump_end(struct netlink_callback *cb) 289static void fib6_dump_end(struct netlink_callback *cb)
294{ 290{
295 struct fib6_walker_t *w = (void*)cb->args[2]; 291 struct fib6_walker_t *w = (void *)cb->args[2];
296 292
297 if (w) { 293 if (w) {
298 if (cb->args[4]) { 294 if (cb->args[4]) {
@@ -302,7 +298,7 @@ static void fib6_dump_end(struct netlink_callback *cb)
302 cb->args[2] = 0; 298 cb->args[2] = 0;
303 kfree(w); 299 kfree(w);
304 } 300 }
305 cb->done = (void*)cb->args[3]; 301 cb->done = (void *)cb->args[3];
306 cb->args[1] = 3; 302 cb->args[1] = 3;
307} 303}
308 304
@@ -485,7 +481,7 @@ static struct fib6_node *fib6_add_1(struct fib6_node *root,
485 fn->fn_sernum = sernum; 481 fn->fn_sernum = sernum;
486 dir = addr_bit_set(addr, fn->fn_bit); 482 dir = addr_bit_set(addr, fn->fn_bit);
487 pn = fn; 483 pn = fn;
488 fn = dir ? fn->right: fn->left; 484 fn = dir ? fn->right : fn->left;
489 } while (fn); 485 } while (fn);
490 486
491 if (!allow_create) { 487 if (!allow_create) {
@@ -638,12 +634,41 @@ static inline bool rt6_qualify_for_ecmp(struct rt6_info *rt)
638 RTF_GATEWAY; 634 RTF_GATEWAY;
639} 635}
640 636
637static int fib6_commit_metrics(struct dst_entry *dst,
638 struct nlattr *mx, int mx_len)
639{
640 struct nlattr *nla;
641 int remaining;
642 u32 *mp;
643
644 if (dst->flags & DST_HOST) {
645 mp = dst_metrics_write_ptr(dst);
646 } else {
647 mp = kzalloc(sizeof(u32) * RTAX_MAX, GFP_KERNEL);
648 if (!mp)
649 return -ENOMEM;
650 dst_init_metrics(dst, mp, 0);
651 }
652
653 nla_for_each_attr(nla, mx, mx_len, remaining) {
654 int type = nla_type(nla);
655
656 if (type) {
657 if (type > RTAX_MAX)
658 return -EINVAL;
659
660 mp[type - 1] = nla_get_u32(nla);
661 }
662 }
663 return 0;
664}
665
641/* 666/*
642 * Insert routing information in a node. 667 * Insert routing information in a node.
643 */ 668 */
644 669
645static int fib6_add_rt2node(struct fib6_node *fn, struct rt6_info *rt, 670static int fib6_add_rt2node(struct fib6_node *fn, struct rt6_info *rt,
646 struct nl_info *info) 671 struct nl_info *info, struct nlattr *mx, int mx_len)
647{ 672{
648 struct rt6_info *iter = NULL; 673 struct rt6_info *iter = NULL;
649 struct rt6_info **ins; 674 struct rt6_info **ins;
@@ -653,6 +678,7 @@ static int fib6_add_rt2node(struct fib6_node *fn, struct rt6_info *rt,
653 (info->nlh->nlmsg_flags & NLM_F_CREATE)); 678 (info->nlh->nlmsg_flags & NLM_F_CREATE));
654 int found = 0; 679 int found = 0;
655 bool rt_can_ecmp = rt6_qualify_for_ecmp(rt); 680 bool rt_can_ecmp = rt6_qualify_for_ecmp(rt);
681 int err;
656 682
657 ins = &fn->leaf; 683 ins = &fn->leaf;
658 684
@@ -751,6 +777,11 @@ static int fib6_add_rt2node(struct fib6_node *fn, struct rt6_info *rt,
751 pr_warn("NLM_F_CREATE should be set when creating new route\n"); 777 pr_warn("NLM_F_CREATE should be set when creating new route\n");
752 778
753add: 779add:
780 if (mx) {
781 err = fib6_commit_metrics(&rt->dst, mx, mx_len);
782 if (err)
783 return err;
784 }
754 rt->dst.rt6_next = iter; 785 rt->dst.rt6_next = iter;
755 *ins = rt; 786 *ins = rt;
756 rt->rt6i_node = fn; 787 rt->rt6i_node = fn;
@@ -770,6 +801,11 @@ add:
770 pr_warn("NLM_F_REPLACE set, but no existing node found!\n"); 801 pr_warn("NLM_F_REPLACE set, but no existing node found!\n");
771 return -ENOENT; 802 return -ENOENT;
772 } 803 }
804 if (mx) {
805 err = fib6_commit_metrics(&rt->dst, mx, mx_len);
806 if (err)
807 return err;
808 }
773 *ins = rt; 809 *ins = rt;
774 rt->rt6i_node = fn; 810 rt->rt6i_node = fn;
775 rt->dst.rt6_next = iter->dst.rt6_next; 811 rt->dst.rt6_next = iter->dst.rt6_next;
@@ -806,7 +842,8 @@ void fib6_force_start_gc(struct net *net)
806 * with source addr info in sub-trees 842 * with source addr info in sub-trees
807 */ 843 */
808 844
809int fib6_add(struct fib6_node *root, struct rt6_info *rt, struct nl_info *info) 845int fib6_add(struct fib6_node *root, struct rt6_info *rt, struct nl_info *info,
846 struct nlattr *mx, int mx_len)
810{ 847{
811 struct fib6_node *fn, *pn = NULL; 848 struct fib6_node *fn, *pn = NULL;
812 int err = -ENOMEM; 849 int err = -ENOMEM;
@@ -900,7 +937,7 @@ int fib6_add(struct fib6_node *root, struct rt6_info *rt, struct nl_info *info)
900 } 937 }
901#endif 938#endif
902 939
903 err = fib6_add_rt2node(fn, rt, info); 940 err = fib6_add_rt2node(fn, rt, info, mx, mx_len);
904 if (!err) { 941 if (!err) {
905 fib6_start_gc(info->nl_net, rt); 942 fib6_start_gc(info->nl_net, rt);
906 if (!(rt->rt6i_flags & RTF_CACHE)) 943 if (!(rt->rt6i_flags & RTF_CACHE))
@@ -955,8 +992,8 @@ struct lookup_args {
955 const struct in6_addr *addr; /* search key */ 992 const struct in6_addr *addr; /* search key */
956}; 993};
957 994
958static struct fib6_node * fib6_lookup_1(struct fib6_node *root, 995static struct fib6_node *fib6_lookup_1(struct fib6_node *root,
959 struct lookup_args *args) 996 struct lookup_args *args)
960{ 997{
961 struct fib6_node *fn; 998 struct fib6_node *fn;
962 __be32 dir; 999 __be32 dir;
@@ -1018,8 +1055,8 @@ backtrack:
1018 return NULL; 1055 return NULL;
1019} 1056}
1020 1057
1021struct fib6_node * fib6_lookup(struct fib6_node *root, const struct in6_addr *daddr, 1058struct fib6_node *fib6_lookup(struct fib6_node *root, const struct in6_addr *daddr,
1022 const struct in6_addr *saddr) 1059 const struct in6_addr *saddr)
1023{ 1060{
1024 struct fib6_node *fn; 1061 struct fib6_node *fn;
1025 struct lookup_args args[] = { 1062 struct lookup_args args[] = {
@@ -1051,9 +1088,9 @@ struct fib6_node * fib6_lookup(struct fib6_node *root, const struct in6_addr *da
1051 */ 1088 */
1052 1089
1053 1090
1054static struct fib6_node * fib6_locate_1(struct fib6_node *root, 1091static struct fib6_node *fib6_locate_1(struct fib6_node *root,
1055 const struct in6_addr *addr, 1092 const struct in6_addr *addr,
1056 int plen, int offset) 1093 int plen, int offset)
1057{ 1094{
1058 struct fib6_node *fn; 1095 struct fib6_node *fn;
1059 1096
@@ -1081,9 +1118,9 @@ static struct fib6_node * fib6_locate_1(struct fib6_node *root,
1081 return NULL; 1118 return NULL;
1082} 1119}
1083 1120
1084struct fib6_node * fib6_locate(struct fib6_node *root, 1121struct fib6_node *fib6_locate(struct fib6_node *root,
1085 const struct in6_addr *daddr, int dst_len, 1122 const struct in6_addr *daddr, int dst_len,
1086 const struct in6_addr *saddr, int src_len) 1123 const struct in6_addr *saddr, int src_len)
1087{ 1124{
1088 struct fib6_node *fn; 1125 struct fib6_node *fn;
1089 1126
@@ -1151,8 +1188,10 @@ static struct fib6_node *fib6_repair_tree(struct net *net,
1151 1188
1152 children = 0; 1189 children = 0;
1153 child = NULL; 1190 child = NULL;
1154 if (fn->right) child = fn->right, children |= 1; 1191 if (fn->right)
1155 if (fn->left) child = fn->left, children |= 2; 1192 child = fn->right, children |= 1;
1193 if (fn->left)
1194 child = fn->left, children |= 2;
1156 1195
1157 if (children == 3 || FIB6_SUBTREE(fn) 1196 if (children == 3 || FIB6_SUBTREE(fn)
1158#ifdef CONFIG_IPV6_SUBTREES 1197#ifdef CONFIG_IPV6_SUBTREES
@@ -1180,8 +1219,10 @@ static struct fib6_node *fib6_repair_tree(struct net *net,
1180 } else { 1219 } else {
1181 WARN_ON(fn->fn_flags & RTN_ROOT); 1220 WARN_ON(fn->fn_flags & RTN_ROOT);
1182#endif 1221#endif
1183 if (pn->right == fn) pn->right = child; 1222 if (pn->right == fn)
1184 else if (pn->left == fn) pn->left = child; 1223 pn->right = child;
1224 else if (pn->left == fn)
1225 pn->left = child;
1185#if RT6_DEBUG >= 2 1226#if RT6_DEBUG >= 2
1186 else 1227 else
1187 WARN_ON(1); 1228 WARN_ON(1);
@@ -1213,10 +1254,10 @@ static struct fib6_node *fib6_repair_tree(struct net *net,
1213 w->node = child; 1254 w->node = child;
1214 if (children&2) { 1255 if (children&2) {
1215 RT6_TRACE("W %p adjusted by delnode 2, s=%d\n", w, w->state); 1256 RT6_TRACE("W %p adjusted by delnode 2, s=%d\n", w, w->state);
1216 w->state = w->state>=FWS_R ? FWS_U : FWS_INIT; 1257 w->state = w->state >= FWS_R ? FWS_U : FWS_INIT;
1217 } else { 1258 } else {
1218 RT6_TRACE("W %p adjusted by delnode 2, s=%d\n", w, w->state); 1259 RT6_TRACE("W %p adjusted by delnode 2, s=%d\n", w, w->state);
1219 w->state = w->state>=FWS_C ? FWS_U : FWS_INIT; 1260 w->state = w->state >= FWS_C ? FWS_U : FWS_INIT;
1220 } 1261 }
1221 } 1262 }
1222 } 1263 }
@@ -1314,7 +1355,7 @@ int fib6_del(struct rt6_info *rt, struct nl_info *info)
1314 struct rt6_info **rtp; 1355 struct rt6_info **rtp;
1315 1356
1316#if RT6_DEBUG >= 2 1357#if RT6_DEBUG >= 2
1317 if (rt->dst.obsolete>0) { 1358 if (rt->dst.obsolete > 0) {
1318 WARN_ON(fn != NULL); 1359 WARN_ON(fn != NULL);
1319 return -ENOENT; 1360 return -ENOENT;
1320 } 1361 }
@@ -1418,7 +1459,7 @@ static int fib6_walk_continue(struct fib6_walker_t *w)
1418 1459
1419 if (w->skip) { 1460 if (w->skip) {
1420 w->skip--; 1461 w->skip--;
1421 continue; 1462 goto skip;
1422 } 1463 }
1423 1464
1424 err = w->func(w); 1465 err = w->func(w);
@@ -1428,6 +1469,7 @@ static int fib6_walk_continue(struct fib6_walker_t *w)
1428 w->count++; 1469 w->count++;
1429 continue; 1470 continue;
1430 } 1471 }
1472skip:
1431 w->state = FWS_U; 1473 w->state = FWS_U;
1432 case FWS_U: 1474 case FWS_U:
1433 if (fn == w->root) 1475 if (fn == w->root)
@@ -1707,7 +1749,7 @@ out_rt6_stats:
1707 kfree(net->ipv6.rt6_stats); 1749 kfree(net->ipv6.rt6_stats);
1708out_timer: 1750out_timer:
1709 return -ENOMEM; 1751 return -ENOMEM;
1710 } 1752}
1711 1753
1712static void fib6_net_exit(struct net *net) 1754static void fib6_net_exit(struct net *net)
1713{ 1755{