diff options
author | Duan Jiong <duanj.fnst@cn.fujitsu.com> | 2013-09-04 07:44:21 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2013-09-05 12:44:31 -0400 |
commit | b55b76b22144ab97cefcb3862bab61f088adf411 (patch) | |
tree | 653c9c8e67466faf48321d35ced7c9dc95f651fd /net/ipv6 | |
parent | 60cad4e67bd6ff400e7ea61fe762b3042b12ae9d (diff) |
ipv6:introduce function to find route for redirect
RFC 4861 says that the IP source address of the Redirect is the
same as the current first-hop router for the specified ICMP
Destination Address, so the gateway should be taken into
consideration when we find the route for redirect.
There was once a check in commit
a6279458c534d01ccc39498aba61c93083ee0372 ("NDISC: Search over
all possible rules on receipt of redirect.") and the check
went away in commit b94f1c0904da9b8bf031667afc48080ba7c3e8c9
("ipv6: Use icmpv6_notify() to propagate redirect, instead of
rt6_redirect()").
The bug is only "exploitable" on layer-2 because the source
address of the redirect is checked to be a valid link-local
address but it makes spoofing a lot easier in the same L2
domain nonetheless.
Thanks very much for Hannes's help.
Signed-off-by: Duan Jiong <duanj.fnst@cn.fujitsu.com>
Acked-by: Hannes Frederic Sowa <hannes@stressinduktion.org>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/ipv6')
-rw-r--r-- | net/ipv6/ah6.c | 2 | ||||
-rw-r--r-- | net/ipv6/esp6.c | 2 | ||||
-rw-r--r-- | net/ipv6/icmp.c | 2 | ||||
-rw-r--r-- | net/ipv6/ipcomp6.c | 2 | ||||
-rw-r--r-- | net/ipv6/ndisc.c | 3 | ||||
-rw-r--r-- | net/ipv6/route.c | 81 |
6 files changed, 81 insertions, 11 deletions
diff --git a/net/ipv6/ah6.c b/net/ipv6/ah6.c index bb02e176cb70..73784c3d4642 100644 --- a/net/ipv6/ah6.c +++ b/net/ipv6/ah6.c | |||
@@ -628,7 +628,7 @@ static void ah6_err(struct sk_buff *skb, struct inet6_skb_parm *opt, | |||
628 | return; | 628 | return; |
629 | 629 | ||
630 | if (type == NDISC_REDIRECT) | 630 | if (type == NDISC_REDIRECT) |
631 | ip6_redirect(skb, net, 0, 0); | 631 | ip6_redirect(skb, net, skb->dev->ifindex, 0); |
632 | else | 632 | else |
633 | ip6_update_pmtu(skb, net, info, 0, 0); | 633 | ip6_update_pmtu(skb, net, info, 0, 0); |
634 | xfrm_state_put(x); | 634 | xfrm_state_put(x); |
diff --git a/net/ipv6/esp6.c b/net/ipv6/esp6.c index aeac0dc3635d..d3618a78fcac 100644 --- a/net/ipv6/esp6.c +++ b/net/ipv6/esp6.c | |||
@@ -447,7 +447,7 @@ static void esp6_err(struct sk_buff *skb, struct inet6_skb_parm *opt, | |||
447 | return; | 447 | return; |
448 | 448 | ||
449 | if (type == NDISC_REDIRECT) | 449 | if (type == NDISC_REDIRECT) |
450 | ip6_redirect(skb, net, 0, 0); | 450 | ip6_redirect(skb, net, skb->dev->ifindex, 0); |
451 | else | 451 | else |
452 | ip6_update_pmtu(skb, net, info, 0, 0); | 452 | ip6_update_pmtu(skb, net, info, 0, 0); |
453 | xfrm_state_put(x); | 453 | xfrm_state_put(x); |
diff --git a/net/ipv6/icmp.c b/net/ipv6/icmp.c index 7cfc8d284870..73681c227453 100644 --- a/net/ipv6/icmp.c +++ b/net/ipv6/icmp.c | |||
@@ -92,7 +92,7 @@ static void icmpv6_err(struct sk_buff *skb, struct inet6_skb_parm *opt, | |||
92 | if (type == ICMPV6_PKT_TOOBIG) | 92 | if (type == ICMPV6_PKT_TOOBIG) |
93 | ip6_update_pmtu(skb, net, info, 0, 0); | 93 | ip6_update_pmtu(skb, net, info, 0, 0); |
94 | else if (type == NDISC_REDIRECT) | 94 | else if (type == NDISC_REDIRECT) |
95 | ip6_redirect(skb, net, 0, 0); | 95 | ip6_redirect(skb, net, skb->dev->ifindex, 0); |
96 | 96 | ||
97 | if (!(type & ICMPV6_INFOMSG_MASK)) | 97 | if (!(type & ICMPV6_INFOMSG_MASK)) |
98 | if (icmp6->icmp6_type == ICMPV6_ECHO_REQUEST) | 98 | if (icmp6->icmp6_type == ICMPV6_ECHO_REQUEST) |
diff --git a/net/ipv6/ipcomp6.c b/net/ipv6/ipcomp6.c index 7af5aee75d98..5636a912074a 100644 --- a/net/ipv6/ipcomp6.c +++ b/net/ipv6/ipcomp6.c | |||
@@ -76,7 +76,7 @@ static void ipcomp6_err(struct sk_buff *skb, struct inet6_skb_parm *opt, | |||
76 | return; | 76 | return; |
77 | 77 | ||
78 | if (type == NDISC_REDIRECT) | 78 | if (type == NDISC_REDIRECT) |
79 | ip6_redirect(skb, net, 0, 0); | 79 | ip6_redirect(skb, net, skb->dev->ifindex, 0); |
80 | else | 80 | else |
81 | ip6_update_pmtu(skb, net, info, 0, 0); | 81 | ip6_update_pmtu(skb, net, info, 0, 0); |
82 | xfrm_state_put(x); | 82 | xfrm_state_put(x); |
diff --git a/net/ipv6/ndisc.c b/net/ipv6/ndisc.c index 22210650596f..c4bc7a35cd56 100644 --- a/net/ipv6/ndisc.c +++ b/net/ipv6/ndisc.c | |||
@@ -1367,7 +1367,8 @@ static void ndisc_redirect_rcv(struct sk_buff *skb) | |||
1367 | return; | 1367 | return; |
1368 | 1368 | ||
1369 | if (!ndopts.nd_opts_rh) { | 1369 | if (!ndopts.nd_opts_rh) { |
1370 | ip6_redirect_no_header(skb, dev_net(skb->dev), 0, 0); | 1370 | ip6_redirect_no_header(skb, dev_net(skb->dev), |
1371 | skb->dev->ifindex, 0); | ||
1371 | return; | 1372 | return; |
1372 | } | 1373 | } |
1373 | 1374 | ||
diff --git a/net/ipv6/route.c b/net/ipv6/route.c index b770085ae36d..c979dd96d82a 100644 --- a/net/ipv6/route.c +++ b/net/ipv6/route.c | |||
@@ -1156,6 +1156,77 @@ void ip6_sk_update_pmtu(struct sk_buff *skb, struct sock *sk, __be32 mtu) | |||
1156 | } | 1156 | } |
1157 | EXPORT_SYMBOL_GPL(ip6_sk_update_pmtu); | 1157 | EXPORT_SYMBOL_GPL(ip6_sk_update_pmtu); |
1158 | 1158 | ||
1159 | /* Handle redirects */ | ||
1160 | struct ip6rd_flowi { | ||
1161 | struct flowi6 fl6; | ||
1162 | struct in6_addr gateway; | ||
1163 | }; | ||
1164 | |||
1165 | static struct rt6_info *__ip6_route_redirect(struct net *net, | ||
1166 | struct fib6_table *table, | ||
1167 | struct flowi6 *fl6, | ||
1168 | int flags) | ||
1169 | { | ||
1170 | struct ip6rd_flowi *rdfl = (struct ip6rd_flowi *)fl6; | ||
1171 | struct rt6_info *rt; | ||
1172 | struct fib6_node *fn; | ||
1173 | |||
1174 | /* Get the "current" route for this destination and | ||
1175 | * check if the redirect has come from approriate router. | ||
1176 | * | ||
1177 | * RFC 4861 specifies that redirects should only be | ||
1178 | * accepted if they come from the nexthop to the target. | ||
1179 | * Due to the way the routes are chosen, this notion | ||
1180 | * is a bit fuzzy and one might need to check all possible | ||
1181 | * routes. | ||
1182 | */ | ||
1183 | |||
1184 | read_lock_bh(&table->tb6_lock); | ||
1185 | fn = fib6_lookup(&table->tb6_root, &fl6->daddr, &fl6->saddr); | ||
1186 | restart: | ||
1187 | for (rt = fn->leaf; rt; rt = rt->dst.rt6_next) { | ||
1188 | if (rt6_check_expired(rt)) | ||
1189 | continue; | ||
1190 | if (rt->dst.error) | ||
1191 | break; | ||
1192 | if (!(rt->rt6i_flags & RTF_GATEWAY)) | ||
1193 | continue; | ||
1194 | if (fl6->flowi6_oif != rt->dst.dev->ifindex) | ||
1195 | continue; | ||
1196 | if (!ipv6_addr_equal(&rdfl->gateway, &rt->rt6i_gateway)) | ||
1197 | continue; | ||
1198 | break; | ||
1199 | } | ||
1200 | |||
1201 | if (!rt) | ||
1202 | rt = net->ipv6.ip6_null_entry; | ||
1203 | else if (rt->dst.error) { | ||
1204 | rt = net->ipv6.ip6_null_entry; | ||
1205 | goto out; | ||
1206 | } | ||
1207 | BACKTRACK(net, &fl6->saddr); | ||
1208 | out: | ||
1209 | dst_hold(&rt->dst); | ||
1210 | |||
1211 | read_unlock_bh(&table->tb6_lock); | ||
1212 | |||
1213 | return rt; | ||
1214 | }; | ||
1215 | |||
1216 | static struct dst_entry *ip6_route_redirect(struct net *net, | ||
1217 | const struct flowi6 *fl6, | ||
1218 | const struct in6_addr *gateway) | ||
1219 | { | ||
1220 | int flags = RT6_LOOKUP_F_HAS_SADDR; | ||
1221 | struct ip6rd_flowi rdfl; | ||
1222 | |||
1223 | rdfl.fl6 = *fl6; | ||
1224 | rdfl.gateway = *gateway; | ||
1225 | |||
1226 | return fib6_rule_lookup(net, &rdfl.fl6, | ||
1227 | flags, __ip6_route_redirect); | ||
1228 | } | ||
1229 | |||
1159 | void ip6_redirect(struct sk_buff *skb, struct net *net, int oif, u32 mark) | 1230 | void ip6_redirect(struct sk_buff *skb, struct net *net, int oif, u32 mark) |
1160 | { | 1231 | { |
1161 | const struct ipv6hdr *iph = (struct ipv6hdr *) skb->data; | 1232 | const struct ipv6hdr *iph = (struct ipv6hdr *) skb->data; |
@@ -1170,9 +1241,8 @@ void ip6_redirect(struct sk_buff *skb, struct net *net, int oif, u32 mark) | |||
1170 | fl6.saddr = iph->saddr; | 1241 | fl6.saddr = iph->saddr; |
1171 | fl6.flowlabel = ip6_flowinfo(iph); | 1242 | fl6.flowlabel = ip6_flowinfo(iph); |
1172 | 1243 | ||
1173 | dst = ip6_route_output(net, NULL, &fl6); | 1244 | dst = ip6_route_redirect(net, &fl6, &ipv6_hdr(skb)->saddr); |
1174 | if (!dst->error) | 1245 | rt6_do_redirect(dst, NULL, skb); |
1175 | rt6_do_redirect(dst, NULL, skb); | ||
1176 | dst_release(dst); | 1246 | dst_release(dst); |
1177 | } | 1247 | } |
1178 | EXPORT_SYMBOL_GPL(ip6_redirect); | 1248 | EXPORT_SYMBOL_GPL(ip6_redirect); |
@@ -1192,9 +1262,8 @@ void ip6_redirect_no_header(struct sk_buff *skb, struct net *net, int oif, | |||
1192 | fl6.daddr = msg->dest; | 1262 | fl6.daddr = msg->dest; |
1193 | fl6.saddr = iph->daddr; | 1263 | fl6.saddr = iph->daddr; |
1194 | 1264 | ||
1195 | dst = ip6_route_output(net, NULL, &fl6); | 1265 | dst = ip6_route_redirect(net, &fl6, &iph->saddr); |
1196 | if (!dst->error) | 1266 | rt6_do_redirect(dst, NULL, skb); |
1197 | rt6_do_redirect(dst, NULL, skb); | ||
1198 | dst_release(dst); | 1267 | dst_release(dst); |
1199 | } | 1268 | } |
1200 | 1269 | ||