staging: ks7010: bail out when registering netdevice fails
[cascardo/linux.git] / net / ipv4 / devinet.c
index f6303b1..e333bc8 100644 (file)
@@ -334,6 +334,9 @@ static void __inet_del_ifa(struct in_device *in_dev, struct in_ifaddr **ifap,
 
        ASSERT_RTNL();
 
+       if (in_dev->dead)
+               goto no_promotions;
+
        /* 1. Deleting primary ifaddr forces deletion all secondaries
         * unless alias promotion is set
         **/
@@ -380,6 +383,7 @@ static void __inet_del_ifa(struct in_device *in_dev, struct in_ifaddr **ifap,
                        fib_del_ifaddr(ifa, ifa1);
        }
 
+no_promotions:
        /* 2. Unlink it */
 
        *ifap = ifa1->ifa_next;
@@ -1194,6 +1198,7 @@ __be32 inet_select_addr(const struct net_device *dev, __be32 dst, int scope)
        __be32 addr = 0;
        struct in_device *in_dev;
        struct net *net = dev_net(dev);
+       int master_idx;
 
        rcu_read_lock();
        in_dev = __in_dev_get_rcu(dev);
@@ -1214,12 +1219,33 @@ __be32 inet_select_addr(const struct net_device *dev, __be32 dst, int scope)
        if (addr)
                goto out_unlock;
 no_in_dev:
+       master_idx = l3mdev_master_ifindex_rcu(dev);
+
+       /* For VRFs, the VRF device takes the place of the loopback device,
+        * with addresses on it being preferred.  Note in such cases the
+        * loopback device will be among the devices that fail the master_idx
+        * equality check in the loop below.
+        */
+       if (master_idx &&
+           (dev = dev_get_by_index_rcu(net, master_idx)) &&
+           (in_dev = __in_dev_get_rcu(dev))) {
+               for_primary_ifa(in_dev) {
+                       if (ifa->ifa_scope != RT_SCOPE_LINK &&
+                           ifa->ifa_scope <= scope) {
+                               addr = ifa->ifa_local;
+                               goto out_unlock;
+                       }
+               } endfor_ifa(in_dev);
+       }
 
        /* Not loopback addresses on loopback should be preferred
           in this case. It is important that lo is the first interface
           in dev_base list.
         */
        for_each_netdev_rcu(net, dev) {
+               if (l3mdev_master_ifindex_rcu(dev) != master_idx)
+                       continue;
+
                in_dev = __in_dev_get_rcu(dev);
                if (!in_dev)
                        continue;
@@ -1731,17 +1757,20 @@ static int inet_netconf_msgsize_devconf(int type)
 {
        int size = NLMSG_ALIGN(sizeof(struct netconfmsg))
                   + nla_total_size(4); /* NETCONFA_IFINDEX */
+       bool all = false;
+
+       if (type == NETCONFA_ALL)
+               all = true;
 
-       /* type -1 is used for ALL */
-       if (type == -1 || type == NETCONFA_FORWARDING)
+       if (all || type == NETCONFA_FORWARDING)
                size += nla_total_size(4);
-       if (type == -1 || type == NETCONFA_RP_FILTER)
+       if (all || type == NETCONFA_RP_FILTER)
                size += nla_total_size(4);
-       if (type == -1 || type == NETCONFA_MC_FORWARDING)
+       if (all || type == NETCONFA_MC_FORWARDING)
                size += nla_total_size(4);
-       if (type == -1 || type == NETCONFA_PROXY_NEIGH)
+       if (all || type == NETCONFA_PROXY_NEIGH)
                size += nla_total_size(4);
-       if (type == -1 || type == NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN)
+       if (all || type == NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN)
                size += nla_total_size(4);
 
        return size;
@@ -1754,36 +1783,39 @@ static int inet_netconf_fill_devconf(struct sk_buff *skb, int ifindex,
 {
        struct nlmsghdr  *nlh;
        struct netconfmsg *ncm;
+       bool all = false;
 
        nlh = nlmsg_put(skb, portid, seq, event, sizeof(struct netconfmsg),
                        flags);
        if (!nlh)
                return -EMSGSIZE;
 
+       if (type == NETCONFA_ALL)
+               all = true;
+
        ncm = nlmsg_data(nlh);
        ncm->ncm_family = AF_INET;
 
        if (nla_put_s32(skb, NETCONFA_IFINDEX, ifindex) < 0)
                goto nla_put_failure;
 
-       /* type -1 is used for ALL */
-       if ((type == -1 || type == NETCONFA_FORWARDING) &&
+       if ((all || type == NETCONFA_FORWARDING) &&
            nla_put_s32(skb, NETCONFA_FORWARDING,
                        IPV4_DEVCONF(*devconf, FORWARDING)) < 0)
                goto nla_put_failure;
-       if ((type == -1 || type == NETCONFA_RP_FILTER) &&
+       if ((all || type == NETCONFA_RP_FILTER) &&
            nla_put_s32(skb, NETCONFA_RP_FILTER,
                        IPV4_DEVCONF(*devconf, RP_FILTER)) < 0)
                goto nla_put_failure;
-       if ((type == -1 || type == NETCONFA_MC_FORWARDING) &&
+       if ((all || type == NETCONFA_MC_FORWARDING) &&
            nla_put_s32(skb, NETCONFA_MC_FORWARDING,
                        IPV4_DEVCONF(*devconf, MC_FORWARDING)) < 0)
                goto nla_put_failure;
-       if ((type == -1 || type == NETCONFA_PROXY_NEIGH) &&
+       if ((all || type == NETCONFA_PROXY_NEIGH) &&
            nla_put_s32(skb, NETCONFA_PROXY_NEIGH,
                        IPV4_DEVCONF(*devconf, PROXY_ARP)) < 0)
                goto nla_put_failure;
-       if ((type == -1 || type == NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN) &&
+       if ((all || type == NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN) &&
            nla_put_s32(skb, NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN,
                        IPV4_DEVCONF(*devconf, IGNORE_ROUTES_WITH_LINKDOWN)) < 0)
                goto nla_put_failure;
@@ -1871,14 +1903,14 @@ static int inet_netconf_get_devconf(struct sk_buff *in_skb,
        }
 
        err = -ENOBUFS;
-       skb = nlmsg_new(inet_netconf_msgsize_devconf(-1), GFP_ATOMIC);
+       skb = nlmsg_new(inet_netconf_msgsize_devconf(NETCONFA_ALL), GFP_ATOMIC);
        if (!skb)
                goto errout;
 
        err = inet_netconf_fill_devconf(skb, ifindex, devconf,
                                        NETLINK_CB(in_skb).portid,
                                        nlh->nlmsg_seq, RTM_NEWNETCONF, 0,
-                                       -1);
+                                       NETCONFA_ALL);
        if (err < 0) {
                /* -EMSGSIZE implies BUG in inet_netconf_msgsize_devconf() */
                WARN_ON(err == -EMSGSIZE);
@@ -1922,7 +1954,7 @@ static int inet_netconf_dump_devconf(struct sk_buff *skb,
                                                      cb->nlh->nlmsg_seq,
                                                      RTM_NEWNETCONF,
                                                      NLM_F_MULTI,
-                                                     -1) < 0) {
+                                                     NETCONFA_ALL) < 0) {
                                rcu_read_unlock();
                                goto done;
                        }
@@ -1938,7 +1970,7 @@ cont:
                                              NETLINK_CB(cb->skb).portid,
                                              cb->nlh->nlmsg_seq,
                                              RTM_NEWNETCONF, NLM_F_MULTI,
-                                             -1) < 0)
+                                             NETCONFA_ALL) < 0)
                        goto done;
                else
                        h++;
@@ -1949,7 +1981,7 @@ cont:
                                              NETLINK_CB(cb->skb).portid,
                                              cb->nlh->nlmsg_seq,
                                              RTM_NEWNETCONF, NLM_F_MULTI,
-                                             -1) < 0)
+                                             NETCONFA_ALL) < 0)
                        goto done;
                else
                        h++;
@@ -2185,6 +2217,8 @@ static struct devinet_sysctl_table {
                                        "igmpv3_unsolicited_report_interval"),
                DEVINET_SYSCTL_RW_ENTRY(IGNORE_ROUTES_WITH_LINKDOWN,
                                        "ignore_routes_with_linkdown"),
+               DEVINET_SYSCTL_RW_ENTRY(DROP_GRATUITOUS_ARP,
+                                       "drop_gratuitous_arp"),
 
                DEVINET_SYSCTL_FLUSHING_ENTRY(NOXFRM, "disable_xfrm"),
                DEVINET_SYSCTL_FLUSHING_ENTRY(NOPOLICY, "disable_policy"),
@@ -2192,6 +2226,8 @@ static struct devinet_sysctl_table {
                                              "promote_secondaries"),
                DEVINET_SYSCTL_FLUSHING_ENTRY(ROUTE_LOCALNET,
                                              "route_localnet"),
+               DEVINET_SYSCTL_FLUSHING_ENTRY(DROP_UNICAST_IN_L2_MULTICAST,
+                                             "drop_unicast_in_l2_multicast"),
        },
 };