diff options
author | David S. Miller <davem@davemloft.net> | 2012-06-28 18:33:24 -0700 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2012-06-28 18:33:58 -0700 |
commit | a207a4b2e8067cbc7f33924e7f2c0fa4ef43b459 (patch) | |
tree | 78e6b5e66b8dd0f1a3609828461d8743397f40b2 /net | |
parent | 58050fce3530939372e6c2f4b4beb76fcb4caa65 (diff) | |
download | op-kernel-dev-a207a4b2e8067cbc7f33924e7f2c0fa4ef43b459.zip op-kernel-dev-a207a4b2e8067cbc7f33924e7f2c0fa4ef43b459.tar.gz |
ipv4: Fix bugs in fib_compute_spec_dst().
Based upon feedback from Julian Anastasov.
1) Use route flags to determine multicast/broadcast, not the
packet flags.
2) Leave saddr unspecified in flow key.
3) Adjust how we invoke inet_select_addr(). Pass ip_hdr(skb)->saddr as
second arg, and if it was zeronet use link scope.
4) Use loopback as input interface in flow key.
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net')
-rw-r--r-- | net/ipv4/fib_frontend.c | 34 |
1 files changed, 21 insertions, 13 deletions
diff --git a/net/ipv4/fib_frontend.c b/net/ipv4/fib_frontend.c index 63b11ca..1d13217 100644 --- a/net/ipv4/fib_frontend.c +++ b/net/ipv4/fib_frontend.c @@ -185,28 +185,36 @@ __be32 fib_compute_spec_dst(struct sk_buff *skb) struct net_device *dev = skb->dev; struct in_device *in_dev; struct fib_result res; + struct rtable *rt; struct flowi4 fl4; struct net *net; + int scope; - if (skb->pkt_type != PACKET_BROADCAST && - skb->pkt_type != PACKET_MULTICAST) + rt = skb_rtable(skb); + if (!(rt->rt_flags & (RTCF_BROADCAST | RTCF_MULTICAST))) return ip_hdr(skb)->daddr; in_dev = __in_dev_get_rcu(dev); BUG_ON(!in_dev); - fl4.flowi4_oif = 0; - fl4.flowi4_iif = 0; - fl4.daddr = ip_hdr(skb)->saddr; - fl4.saddr = ip_hdr(skb)->daddr; - fl4.flowi4_tos = RT_TOS(ip_hdr(skb)->tos); - fl4.flowi4_scope = RT_SCOPE_UNIVERSE; - fl4.flowi4_mark = IN_DEV_SRC_VMARK(in_dev) ? skb->mark : 0; net = dev_net(dev); - if (!fib_lookup(net, &fl4, &res)) - return FIB_RES_PREFSRC(net, res); - else - return inet_select_addr(dev, 0, RT_SCOPE_UNIVERSE); + + scope = RT_SCOPE_UNIVERSE; + if (!ipv4_is_zeronet(ip_hdr(skb)->saddr)) { + fl4.flowi4_oif = 0; + fl4.flowi4_iif = net->loopback_dev->ifindex; + fl4.daddr = ip_hdr(skb)->saddr; + fl4.saddr = 0; + fl4.flowi4_tos = RT_TOS(ip_hdr(skb)->tos); + fl4.flowi4_scope = scope; + fl4.flowi4_mark = IN_DEV_SRC_VMARK(in_dev) ? skb->mark : 0; + if (!fib_lookup(net, &fl4, &res)) + return FIB_RES_PREFSRC(net, res); + } else { + scope = RT_SCOPE_LINK; + } + + return inet_select_addr(dev, ip_hdr(skb)->saddr, scope); } /* Given (packet source, input interface) and optional (dst, oif, tos): |