net: Fix up inet_addr_type checks
authorDavid Ahern <dsa@cumulusnetworks.com>
Thu, 13 Aug 2015 20:59:05 +0000 (14:59 -0600)
committerDavid S. Miller <davem@davemloft.net>
Fri, 14 Aug 2015 05:43:21 +0000 (22:43 -0700)
Currently inet_addr_type and inet_dev_addr_type expect local addresses
to be in the local table. With the VRF device local routes for devices
associated with a VRF will be in the table associated with the VRF.
Provide an alternate inet_addr lookup to use a specific table rather
than defaulting to the local table.

inet_addr_type_dev_table keeps the same semantics as inet_addr_type but
if the passed in device is enslaved to a VRF then the table for that VRF
is used for the lookup.

Signed-off-by: David Ahern <dsa@cumulusnetworks.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
include/net/route.h
net/ipv4/af_inet.c
net/ipv4/arp.c
net/ipv4/fib_frontend.c
net/ipv4/fib_semantics.c
net/ipv4/icmp.c

index 6ba681f0b98d23074fdf14272f538016e8b9c6ab..6dda2c1bf8c6d12951b7e3d81273ca77d16a1720 100644 (file)
@@ -192,6 +192,9 @@ unsigned int inet_addr_type(struct net *net, __be32 addr);
 unsigned int inet_addr_type_table(struct net *net, __be32 addr, int tb_id);
 unsigned int inet_dev_addr_type(struct net *net, const struct net_device *dev,
                                __be32 addr);
+unsigned int inet_addr_type_dev_table(struct net *net,
+                                     const struct net_device *dev,
+                                     __be32 addr);
 void ip_rt_multicast_event(struct in_device *);
 int ip_rt_ioctl(struct net *, unsigned int cmd, void __user *arg);
 void ip_rt_get_source(u8 *src, struct sk_buff *skb, struct rtable *rt);
index cc4e498a0ccf390115c2f6f0306248650f4a2c35..c8b855882fa522011405cf43209a7671bd3272c7 100644 (file)
 #ifdef CONFIG_IP_MROUTE
 #include <linux/mroute.h>
 #endif
+#include <net/vrf.h>
 
 
 /* The inetsw table contains everything that inet_create needs to
@@ -427,6 +428,7 @@ int inet_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
        struct net *net = sock_net(sk);
        unsigned short snum;
        int chk_addr_ret;
+       int tb_id = RT_TABLE_LOCAL;
        int err;
 
        /* If the socket has its own bind function then use it. (RAW) */
@@ -448,7 +450,16 @@ int inet_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
                        goto out;
        }
 
-       chk_addr_ret = inet_addr_type(net, addr->sin_addr.s_addr);
+       if (sk->sk_bound_dev_if) {
+               struct net_device *dev;
+
+               rcu_read_lock();
+               dev = dev_get_by_index_rcu(net, sk->sk_bound_dev_if);
+               if (dev)
+                       tb_id = vrf_dev_table_rcu(dev) ? : tb_id;
+               rcu_read_unlock();
+       }
+       chk_addr_ret = inet_addr_type_table(net, addr->sin_addr.s_addr, tb_id);
 
        /* Not specified by any standard per-se, however it breaks too
         * many applications when removed.  It is unfortunate since
index 34a308573f4b0e7bcae0ad9ffa5e45b19c72c1d1..30409b75e92503cca0daacc48010a63936c6aa20 100644 (file)
@@ -233,7 +233,7 @@ static int arp_constructor(struct neighbour *neigh)
                return -EINVAL;
        }
 
-       neigh->type = inet_addr_type(dev_net(dev), addr);
+       neigh->type = inet_addr_type_dev_table(dev_net(dev), dev, addr);
 
        parms = in_dev->arp_parms;
        __neigh_parms_put(neigh->parms);
@@ -343,7 +343,7 @@ static void arp_solicit(struct neighbour *neigh, struct sk_buff *skb)
        switch (IN_DEV_ARP_ANNOUNCE(in_dev)) {
        default:
        case 0:         /* By default announce any local IP */
-               if (skb && inet_addr_type(dev_net(dev),
+               if (skb && inet_addr_type_dev_table(dev_net(dev), dev,
                                          ip_hdr(skb)->saddr) == RTN_LOCAL)
                        saddr = ip_hdr(skb)->saddr;
                break;
@@ -351,7 +351,8 @@ static void arp_solicit(struct neighbour *neigh, struct sk_buff *skb)
                if (!skb)
                        break;
                saddr = ip_hdr(skb)->saddr;
-               if (inet_addr_type(dev_net(dev), saddr) == RTN_LOCAL) {
+               if (inet_addr_type_dev_table(dev_net(dev), dev,
+                                            saddr) == RTN_LOCAL) {
                        /* saddr should be known to target */
                        if (inet_addr_onlink(in_dev, target, saddr))
                                break;
@@ -751,7 +752,7 @@ static int arp_process(struct sock *sk, struct sk_buff *skb)
        /* Special case: IPv4 duplicate address detection packet (RFC2131) */
        if (sip == 0) {
                if (arp->ar_op == htons(ARPOP_REQUEST) &&
-                   inet_addr_type(net, tip) == RTN_LOCAL &&
+                   inet_addr_type_dev_table(net, dev, tip) == RTN_LOCAL &&
                    !arp_ignore(in_dev, sip, tip))
                        arp_send(ARPOP_REPLY, ETH_P_ARP, sip, dev, tip, sha,
                                 dev->dev_addr, sha);
@@ -811,16 +812,18 @@ static int arp_process(struct sock *sk, struct sk_buff *skb)
        n = __neigh_lookup(&arp_tbl, &sip, dev, 0);
 
        if (IN_DEV_ARP_ACCEPT(in_dev)) {
+               unsigned int addr_type = inet_addr_type_dev_table(net, dev, sip);
+
                /* Unsolicited ARP is not accepted by default.
                   It is possible, that this option should be enabled for some
                   devices (strip is candidate)
                 */
                is_garp = arp->ar_op == htons(ARPOP_REQUEST) && tip == sip &&
-                         inet_addr_type(net, sip) == RTN_UNICAST;
+                         addr_type == RTN_UNICAST;
 
                if (!n &&
                    ((arp->ar_op == htons(ARPOP_REPLY)  &&
-                     inet_addr_type(net, sip) == RTN_UNICAST) || is_garp))
+                               addr_type == RTN_UNICAST) || is_garp))
                        n = __neigh_lookup(&arp_tbl, &sip, dev, 1);
        }
 
index b11321a8e58d8b63004ba761a1e91fb06dfea66f..c55723ec4c3e5cc3f02a71ba5523fbed88ba30d0 100644 (file)
@@ -260,6 +260,19 @@ unsigned int inet_dev_addr_type(struct net *net, const struct net_device *dev,
 }
 EXPORT_SYMBOL(inet_dev_addr_type);
 
+/* inet_addr_type with dev == NULL but using the table from a dev
+ * if one is associated
+ */
+unsigned int inet_addr_type_dev_table(struct net *net,
+                                     const struct net_device *dev,
+                                     __be32 addr)
+{
+       int rt_table = vrf_dev_table(dev) ? : RT_TABLE_LOCAL;
+
+       return __inet_dev_addr_type(net, NULL, addr, rt_table);
+}
+EXPORT_SYMBOL(inet_addr_type_dev_table);
+
 __be32 fib_compute_spec_dst(struct sk_buff *skb)
 {
        struct net_device *dev = skb->dev;
@@ -510,9 +523,12 @@ static int rtentry_to_fib_config(struct net *net, int cmd, struct rtentry *rt,
 
        addr = sk_extract_addr(&rt->rt_gateway);
        if (rt->rt_gateway.sa_family == AF_INET && addr) {
+               unsigned int addr_type;
+
                cfg->fc_gw = addr;
+               addr_type = inet_addr_type_table(net, addr, cfg->fc_table);
                if (rt->rt_flags & RTF_GATEWAY &&
-                   inet_addr_type(net, addr) == RTN_UNICAST)
+                   addr_type == RTN_UNICAST)
                        cfg->fc_scope = RT_SCOPE_UNIVERSE;
        }
 
@@ -984,11 +1000,14 @@ void fib_del_ifaddr(struct in_ifaddr *ifa, struct in_ifaddr *iprim)
                        fib_magic(RTM_DELROUTE, RTN_BROADCAST, any, 32, prim);
        }
        if (!(ok & LOCAL_OK)) {
+               unsigned int addr_type;
+
                fib_magic(RTM_DELROUTE, RTN_LOCAL, ifa->ifa_local, 32, prim);
 
                /* Check, that this local address finally disappeared. */
-               if (gone &&
-                   inet_addr_type(dev_net(dev), ifa->ifa_local) != RTN_LOCAL) {
+               addr_type = inet_addr_type_dev_table(dev_net(dev), dev,
+                                                    ifa->ifa_local);
+               if (gone && addr_type != RTN_LOCAL) {
                        /* And the last, but not the least thing.
                         * We must flush stray FIB entries.
                         *
index 558e196bae0f5a10a6bc81102c657db00b726ad1..410ddb67221e5bec39b0b1caf03cd8c224aa25c5 100644 (file)
@@ -670,16 +670,18 @@ static int fib_check_nh(struct fib_config *cfg, struct fib_info *fi,
                struct fib_result res;
 
                if (nh->nh_flags & RTNH_F_ONLINK) {
+                       unsigned int addr_type;
 
                        if (cfg->fc_scope >= RT_SCOPE_LINK)
                                return -EINVAL;
-                       if (inet_addr_type(net, nh->nh_gw) != RTN_UNICAST)
-                               return -EINVAL;
                        dev = __dev_get_by_index(net, nh->nh_oif);
                        if (!dev)
                                return -ENODEV;
                        if (!(dev->flags & IFF_UP))
                                return -ENETDOWN;
+                       addr_type = inet_addr_type_dev_table(net, dev, nh->nh_gw);
+                       if (addr_type != RTN_UNICAST)
+                               return -EINVAL;
                        if (!netif_carrier_ok(dev))
                                nh->nh_flags |= RTNH_F_LINKDOWN;
                        nh->nh_dev = dev;
index 1164fc4ce3bc3aef5c721201ed7d2cc0a5180762..c6f1ce149ffb868af6c31cfc86207f13dad16238 100644 (file)
@@ -484,7 +484,8 @@ static struct rtable *icmp_route_lookup(struct net *net,
        if (err)
                goto relookup_failed;
 
-       if (inet_addr_type(net, fl4_dec.saddr) == RTN_LOCAL) {
+       if (inet_addr_type_dev_table(net, skb_in->dev,
+                                    fl4_dec.saddr) == RTN_LOCAL) {
                rt2 = __ip_route_output_key(net, &fl4_dec);
                if (IS_ERR(rt2))
                        err = PTR_ERR(rt2);
@@ -833,7 +834,7 @@ static bool icmp_unreach(struct sk_buff *skb)
         */
 
        if (!net->ipv4.sysctl_icmp_ignore_bogus_error_responses &&
-           inet_addr_type(net, iph->daddr) == RTN_BROADCAST) {
+           inet_addr_type_dev_table(net, skb->dev, iph->daddr) == RTN_BROADCAST) {
                net_warn_ratelimited("%pI4 sent an invalid ICMP type %u, code %u error to a broadcast: %pI4 on %s\n",
                                     &ip_hdr(skb)->saddr,
                                     icmph->type, icmph->code,