diff options
Diffstat (limited to 'net/ipv6/ip6_fib.c')
-rw-r--r-- | net/ipv6/ip6_fib.c | 124 |
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 | ||
49 | static struct kmem_cache * fib6_node_kmem __read_mostly; | 47 | static struct kmem_cache *fib6_node_kmem __read_mostly; |
50 | 48 | ||
51 | enum fib_walk_state_t | 49 | enum 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 | ||
62 | struct fib6_cleaner_t | 59 | struct 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 | ||
150 | static __inline__ struct fib6_node * node_alloc(void) | 146 | static __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 | ||
159 | static __inline__ void node_free(struct fib6_node * fn) | 155 | static __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 | ||
293 | static void fib6_dump_end(struct netlink_callback *cb) | 289 | static 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 | ||
637 | static 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 | ||
645 | static int fib6_add_rt2node(struct fib6_node *fn, struct rt6_info *rt, | 670 | static 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 | ||
753 | add: | 779 | add: |
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 | ||
809 | int fib6_add(struct fib6_node *root, struct rt6_info *rt, struct nl_info *info) | 845 | int 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 | ||
958 | static struct fib6_node * fib6_lookup_1(struct fib6_node *root, | 995 | static 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 | ||
1021 | struct fib6_node * fib6_lookup(struct fib6_node *root, const struct in6_addr *daddr, | 1058 | struct 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 | ||
1054 | static struct fib6_node * fib6_locate_1(struct fib6_node *root, | 1091 | static 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 | ||
1084 | struct fib6_node * fib6_locate(struct fib6_node *root, | 1121 | struct 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 | } |
1472 | skip: | ||
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); |
1708 | out_timer: | 1750 | out_timer: |
1709 | return -ENOMEM; | 1751 | return -ENOMEM; |
1710 | } | 1752 | } |
1711 | 1753 | ||
1712 | static void fib6_net_exit(struct net *net) | 1754 | static void fib6_net_exit(struct net *net) |
1713 | { | 1755 | { |