diff options
author | Shmulik Ladkani <shmulik.ladkani@gmail.com> | 2012-04-01 00:03:45 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2012-04-01 17:29:40 -0400 |
commit | 72331bc0cd072c3f4b670cd1256e47681fc53b80 (patch) | |
tree | 46665ad2830342d94b4d784c5aa397e3a6f0045b | |
parent | cdaf0b835df04177397b90214f8b457fd23b67e0 (diff) |
ipv6: Fix RTM_GETROUTE's interpretation of RTA_IIF to be consistent with ipv4
In IPv4, if an RTA_IIF attribute is specified within an RTM_GETROUTE
message, then a route is searched as if a packet was received on the
specified 'iif' interface.
However in IPv6, RTA_IIF is not interpreted in the same way:
'inet6_rtm_getroute()' always calls 'ip6_route_output()', regardless the
RTA_IIF attribute.
As a result, in IPv6 there's no way to use RTM_GETROUTE in order to look
for a route as if a packet was received on a specific interface.
Fix 'inet6_rtm_getroute()' so that RTA_IIF is interpreted as "lookup a
route as if a packet was received on the specified interface", similar
to IPv4's 'inet_rtm_getroute()' interpretation.
Reported-by: Ami Koren <amikoren@yahoo.com>
Signed-off-by: Shmulik Ladkani <shmulik.ladkani@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r-- | net/ipv6/route.c | 34 |
1 files changed, 27 insertions, 7 deletions
diff --git a/net/ipv6/route.c b/net/ipv6/route.c index 496b62712fe8..3992e26a6039 100644 --- a/net/ipv6/route.c +++ b/net/ipv6/route.c | |||
@@ -881,6 +881,16 @@ static struct rt6_info *ip6_pol_route_input(struct net *net, struct fib6_table * | |||
881 | return ip6_pol_route(net, table, fl6->flowi6_iif, fl6, flags); | 881 | return ip6_pol_route(net, table, fl6->flowi6_iif, fl6, flags); |
882 | } | 882 | } |
883 | 883 | ||
884 | static struct dst_entry *ip6_route_input_lookup(struct net *net, | ||
885 | struct net_device *dev, | ||
886 | struct flowi6 *fl6, int flags) | ||
887 | { | ||
888 | if (rt6_need_strict(&fl6->daddr) && dev->type != ARPHRD_PIMREG) | ||
889 | flags |= RT6_LOOKUP_F_IFACE; | ||
890 | |||
891 | return fib6_rule_lookup(net, fl6, flags, ip6_pol_route_input); | ||
892 | } | ||
893 | |||
884 | void ip6_route_input(struct sk_buff *skb) | 894 | void ip6_route_input(struct sk_buff *skb) |
885 | { | 895 | { |
886 | const struct ipv6hdr *iph = ipv6_hdr(skb); | 896 | const struct ipv6hdr *iph = ipv6_hdr(skb); |
@@ -895,10 +905,7 @@ void ip6_route_input(struct sk_buff *skb) | |||
895 | .flowi6_proto = iph->nexthdr, | 905 | .flowi6_proto = iph->nexthdr, |
896 | }; | 906 | }; |
897 | 907 | ||
898 | if (rt6_need_strict(&iph->daddr) && skb->dev->type != ARPHRD_PIMREG) | 908 | skb_dst_set(skb, ip6_route_input_lookup(net, skb->dev, &fl6, flags)); |
899 | flags |= RT6_LOOKUP_F_IFACE; | ||
900 | |||
901 | skb_dst_set(skb, fib6_rule_lookup(net, &fl6, flags, ip6_pol_route_input)); | ||
902 | } | 909 | } |
903 | 910 | ||
904 | static struct rt6_info *ip6_pol_route_output(struct net *net, struct fib6_table *table, | 911 | static struct rt6_info *ip6_pol_route_output(struct net *net, struct fib6_table *table, |
@@ -2537,7 +2544,7 @@ static int inet6_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr* nlh, void | |||
2537 | struct sk_buff *skb; | 2544 | struct sk_buff *skb; |
2538 | struct rtmsg *rtm; | 2545 | struct rtmsg *rtm; |
2539 | struct flowi6 fl6; | 2546 | struct flowi6 fl6; |
2540 | int err, iif = 0; | 2547 | int err, iif = 0, oif = 0; |
2541 | 2548 | ||
2542 | err = nlmsg_parse(nlh, sizeof(*rtm), tb, RTA_MAX, rtm_ipv6_policy); | 2549 | err = nlmsg_parse(nlh, sizeof(*rtm), tb, RTA_MAX, rtm_ipv6_policy); |
2543 | if (err < 0) | 2550 | if (err < 0) |
@@ -2564,15 +2571,29 @@ static int inet6_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr* nlh, void | |||
2564 | iif = nla_get_u32(tb[RTA_IIF]); | 2571 | iif = nla_get_u32(tb[RTA_IIF]); |
2565 | 2572 | ||
2566 | if (tb[RTA_OIF]) | 2573 | if (tb[RTA_OIF]) |
2567 | fl6.flowi6_oif = nla_get_u32(tb[RTA_OIF]); | 2574 | oif = nla_get_u32(tb[RTA_OIF]); |
2568 | 2575 | ||
2569 | if (iif) { | 2576 | if (iif) { |
2570 | struct net_device *dev; | 2577 | struct net_device *dev; |
2578 | int flags = 0; | ||
2579 | |||
2571 | dev = __dev_get_by_index(net, iif); | 2580 | dev = __dev_get_by_index(net, iif); |
2572 | if (!dev) { | 2581 | if (!dev) { |
2573 | err = -ENODEV; | 2582 | err = -ENODEV; |
2574 | goto errout; | 2583 | goto errout; |
2575 | } | 2584 | } |
2585 | |||
2586 | fl6.flowi6_iif = iif; | ||
2587 | |||
2588 | if (!ipv6_addr_any(&fl6.saddr)) | ||
2589 | flags |= RT6_LOOKUP_F_HAS_SADDR; | ||
2590 | |||
2591 | rt = (struct rt6_info *)ip6_route_input_lookup(net, dev, &fl6, | ||
2592 | flags); | ||
2593 | } else { | ||
2594 | fl6.flowi6_oif = oif; | ||
2595 | |||
2596 | rt = (struct rt6_info *)ip6_route_output(net, NULL, &fl6); | ||
2576 | } | 2597 | } |
2577 | 2598 | ||
2578 | skb = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL); | 2599 | skb = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL); |
@@ -2587,7 +2608,6 @@ static int inet6_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr* nlh, void | |||
2587 | skb_reset_mac_header(skb); | 2608 | skb_reset_mac_header(skb); |
2588 | skb_reserve(skb, MAX_HEADER + sizeof(struct ipv6hdr)); | 2609 | skb_reserve(skb, MAX_HEADER + sizeof(struct ipv6hdr)); |
2589 | 2610 | ||
2590 | rt = (struct rt6_info*) ip6_route_output(net, NULL, &fl6); | ||
2591 | skb_dst_set(skb, &rt->dst); | 2611 | skb_dst_set(skb, &rt->dst); |
2592 | 2612 | ||
2593 | err = rt6_fill_node(net, skb, rt, &fl6.daddr, &fl6.saddr, iif, | 2613 | err = rt6_fill_node(net, skb, rt, &fl6.daddr, &fl6.saddr, iif, |