udp: Wrong locking code in udp seq_file infrastructure
[safe/jmp/linux-2.6] / net / ipv4 / devinet.c
index 823c724..309997e 100644 (file)
@@ -1,8 +1,6 @@
 /*
  *     NET3    IP device support routines.
  *
- *     Version: $Id: devinet.c,v 1.44 2001/10/31 21:55:54 davem Exp $
- *
  *             This program is free software; you can redistribute it and/or
  *             modify it under the terms of the GNU General Public License
  *             as published by the Free Software Foundation; either version
@@ -90,7 +88,6 @@ static const struct nla_policy ifa_ipv4_policy[IFA_MAX+1] = {
        [IFA_LOCAL]             = { .type = NLA_U32 },
        [IFA_ADDRESS]           = { .type = NLA_U32 },
        [IFA_BROADCAST]         = { .type = NLA_U32 },
-       [IFA_ANYCAST]           = { .type = NLA_U32 },
        [IFA_LABEL]             = { .type = NLA_STRING, .len = IFNAMSIZ - 1 },
 };
 
@@ -115,13 +112,7 @@ static inline void devinet_sysctl_unregister(struct in_device *idev)
 
 static struct in_ifaddr *inet_alloc_ifa(void)
 {
-       struct in_ifaddr *ifa = kzalloc(sizeof(*ifa), GFP_KERNEL);
-
-       if (ifa) {
-               INIT_RCU_HEAD(&ifa->rcu_head);
-       }
-
-       return ifa;
+       return kzalloc(sizeof(struct in_ifaddr), GFP_KERNEL);
 }
 
 static void inet_rcu_free_ifa(struct rcu_head *head)
@@ -141,8 +132,8 @@ void in_dev_finish_destroy(struct in_device *idev)
 {
        struct net_device *dev = idev->dev;
 
-       BUG_TRAP(!idev->ifa_list);
-       BUG_TRAP(!idev->mc_list);
+       WARN_ON(idev->ifa_list);
+       WARN_ON(idev->mc_list);
 #ifdef NET_REFCNT_DEBUG
        printk(KERN_DEBUG "in_dev_finish_destroy: %p=%s\n",
               idev, dev ? dev->name : "NIL");
@@ -164,13 +155,14 @@ static struct in_device *inetdev_init(struct net_device *dev)
        in_dev = kzalloc(sizeof(*in_dev), GFP_KERNEL);
        if (!in_dev)
                goto out;
-       INIT_RCU_HEAD(&in_dev->rcu_head);
        memcpy(&in_dev->cnf, dev_net(dev)->ipv4.devconf_dflt,
                        sizeof(in_dev->cnf));
        in_dev->cnf.sysctl = NULL;
        in_dev->dev = dev;
        if ((in_dev->arp_parms = neigh_parms_alloc(dev, &arp_tbl)) == NULL)
                goto out_kfree;
+       if (IPV4_DEVCONF(in_dev->cnf, FORWARDING))
+               dev_disable_lro(dev);
        /* Reference in_dev->dev */
        dev_hold(dev);
        /* Account for reference dev->ip_ptr (below) */
@@ -400,7 +392,7 @@ static int inet_set_ifa(struct net_device *dev, struct in_ifaddr *ifa)
        }
        ipv4_devconf_setall(in_dev);
        if (ifa->ifa_dev != in_dev) {
-               BUG_TRAP(!ifa->ifa_dev);
+               WARN_ON(ifa->ifa_dev);
                in_dev_hold(in_dev);
                ifa->ifa_dev = in_dev;
        }
@@ -437,7 +429,7 @@ struct in_ifaddr *inet_ifa_byprefix(struct in_device *in_dev, __be32 prefix,
 
 static int inet_rtm_deladdr(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
 {
-       struct net *net = skb->sk->sk_net;
+       struct net *net = sock_net(skb->sk);
        struct nlattr *tb[IFA_MAX+1];
        struct in_device *in_dev;
        struct ifaddrmsg *ifm;
@@ -536,9 +528,6 @@ static struct in_ifaddr *rtm_to_ifaddr(struct net *net, struct nlmsghdr *nlh)
        if (tb[IFA_BROADCAST])
                ifa->ifa_broadcast = nla_get_be32(tb[IFA_BROADCAST]);
 
-       if (tb[IFA_ANYCAST])
-               ifa->ifa_anycast = nla_get_be32(tb[IFA_ANYCAST]);
-
        if (tb[IFA_LABEL])
                nla_strlcpy(ifa->ifa_label, tb[IFA_LABEL], IFNAMSIZ);
        else
@@ -552,7 +541,7 @@ errout:
 
 static int inet_rtm_newaddr(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
 {
-       struct net *net = skb->sk->sk_net;
+       struct net *net = sock_net(skb->sk);
        struct in_ifaddr *ifa;
 
        ASSERT_RTNL();
@@ -617,9 +606,7 @@ int devinet_ioctl(struct net *net, unsigned int cmd, void __user *arg)
        if (colon)
                *colon = 0;
 
-#ifdef CONFIG_KMOD
        dev_load(net, ifr.ifr_name);
-#endif
 
        switch (cmd) {
        case SIOCGIFADDR:       /* Get interface address */
@@ -745,7 +732,6 @@ int devinet_ioctl(struct net *net, unsigned int cmd, void __user *arg)
                                break;
                        inet_del_ifa(in_dev, ifap, 0);
                        ifa->ifa_broadcast = 0;
-                       ifa->ifa_anycast = 0;
                        ifa->ifa_scope = 0;
                }
 
@@ -1018,7 +1004,7 @@ static void inetdev_changename(struct net_device *dev, struct in_device *in_dev)
                memcpy(old, ifa->ifa_label, IFNAMSIZ);
                memcpy(ifa->ifa_label, dev->name, IFNAMSIZ);
                if (named++ == 0)
-                       continue;
+                       goto skip;
                dot = strchr(old, ':');
                if (dot == NULL) {
                        sprintf(old, ":%d", named);
@@ -1029,9 +1015,16 @@ static void inetdev_changename(struct net_device *dev, struct in_device *in_dev)
                } else {
                        strcpy(ifa->ifa_label + (IFNAMSIZ - strlen(dot) - 1), dot);
                }
+skip:
+               rtmsg_ifa(RTM_NEWADDR, ifa, NULL, 0);
        }
 }
 
+static inline bool inetdev_valid_mtu(unsigned mtu)
+{
+       return mtu >= 68;
+}
+
 /* Called only under RTNL semaphore */
 
 static int inetdev_event(struct notifier_block *this, unsigned long event,
@@ -1051,6 +1044,10 @@ static int inetdev_event(struct notifier_block *this, unsigned long event,
                                IN_DEV_CONF_SET(in_dev, NOXFRM, 1);
                                IN_DEV_CONF_SET(in_dev, NOPOLICY, 1);
                        }
+               } else if (event == NETDEV_CHANGEMTU) {
+                       /* Re-enabling IP */
+                       if (inetdev_valid_mtu(dev->mtu))
+                               in_dev = inetdev_init(dev);
                }
                goto out;
        }
@@ -1061,7 +1058,7 @@ static int inetdev_event(struct notifier_block *this, unsigned long event,
                dev->ip_ptr = NULL;
                break;
        case NETDEV_UP:
-               if (dev->mtu < 68)
+               if (!inetdev_valid_mtu(dev->mtu))
                        break;
                if (dev->flags & IFF_LOOPBACK) {
                        struct in_ifaddr *ifa;
@@ -1083,9 +1080,9 @@ static int inetdev_event(struct notifier_block *this, unsigned long event,
                ip_mc_down(in_dev);
                break;
        case NETDEV_CHANGEMTU:
-               if (dev->mtu >= 68)
+               if (inetdev_valid_mtu(dev->mtu))
                        break;
-               /* MTU falled under 68, disable IP */
+               /* disable IP when MTU is not enough */
        case NETDEV_UNREGISTER:
                inetdev_destroy(in_dev);
                break;
@@ -1104,7 +1101,7 @@ out:
 }
 
 static struct notifier_block ip_netdev_notifier = {
-       .notifier_call =inetdev_event,
+       .notifier_call = inetdev_event,
 };
 
 static inline size_t inet_nlmsg_size(void)
@@ -1113,7 +1110,6 @@ static inline size_t inet_nlmsg_size(void)
               + nla_total_size(4) /* IFA_ADDRESS */
               + nla_total_size(4) /* IFA_LOCAL */
               + nla_total_size(4) /* IFA_BROADCAST */
-              + nla_total_size(4) /* IFA_ANYCAST */
               + nla_total_size(IFNAMSIZ); /* IFA_LABEL */
 }
 
@@ -1143,9 +1139,6 @@ static int inet_fill_ifaddr(struct sk_buff *skb, struct in_ifaddr *ifa,
        if (ifa->ifa_broadcast)
                NLA_PUT_BE32(skb, IFA_BROADCAST, ifa->ifa_broadcast);
 
-       if (ifa->ifa_anycast)
-               NLA_PUT_BE32(skb, IFA_ANYCAST, ifa->ifa_anycast);
-
        if (ifa->ifa_label[0])
                NLA_PUT_STRING(skb, IFA_LABEL, ifa->ifa_label);
 
@@ -1158,7 +1151,7 @@ nla_put_failure:
 
 static int inet_dump_ifaddr(struct sk_buff *skb, struct netlink_callback *cb)
 {
-       struct net *net = skb->sk->sk_net;
+       struct net *net = sock_net(skb->sk);
        int idx, ip_idx;
        struct net_device *dev;
        struct in_device *in_dev;
@@ -1195,7 +1188,7 @@ done:
        return skb->len;
 }
 
-static void rtmsg_ifa(int event, struct in_ifaddrifa, struct nlmsghdr *nlh,
+static void rtmsg_ifa(int event, struct in_ifaddr *ifa, struct nlmsghdr *nlh,
                      u32 pid)
 {
        struct sk_buff *skb;
@@ -1250,6 +1243,8 @@ static void inet_forward_change(struct net *net)
        read_lock(&dev_base_lock);
        for_each_netdev(net, dev) {
                struct in_device *in_dev;
+               if (on)
+                       dev_disable_lro(dev);
                rcu_read_lock();
                in_dev = __in_dev_get_rcu(dev);
                if (in_dev)
@@ -1257,12 +1252,10 @@ static void inet_forward_change(struct net *net)
                rcu_read_unlock();
        }
        read_unlock(&dev_base_lock);
-
-       rt_cache_flush(0);
 }
 
 static int devinet_conf_proc(ctl_table *ctl, int write,
-                            struct filefilp, void __user *buffer,
+                            struct file *filp, void __user *buffer,
                             size_t *lenp, loff_t *ppos)
 {
        int ret = proc_dointvec(ctl, write, filp, buffer, lenp, ppos);
@@ -1281,7 +1274,7 @@ static int devinet_conf_proc(ctl_table *ctl, int write,
        return ret;
 }
 
-static int devinet_conf_sysctl(ctl_table *table, int __user *name, int nlen,
+static int devinet_conf_sysctl(ctl_table *table,
                               void __user *oldval, size_t __user *oldlenp,
                               void __user *newval, size_t newlen)
 {
@@ -1334,7 +1327,7 @@ static int devinet_conf_sysctl(ctl_table *table, int __user *name, int nlen,
 }
 
 static int devinet_sysctl_forward(ctl_table *ctl, int write,
-                                 struct filefilp, void __user *buffer,
+                                 struct file *filp, void __user *buffer,
                                  size_t *lenp, loff_t *ppos)
 {
        int *valp = ctl->data;
@@ -1344,38 +1337,48 @@ static int devinet_sysctl_forward(ctl_table *ctl, int write,
        if (write && *valp != val) {
                struct net *net = ctl->extra2;
 
-               if (valp == &IPV4_DEVCONF_ALL(net, FORWARDING))
-                       inet_forward_change(net);
-               else if (valp != &IPV4_DEVCONF_DFLT(net, FORWARDING))
-                       rt_cache_flush(0);
+               if (valp != &IPV4_DEVCONF_DFLT(net, FORWARDING)) {
+                       rtnl_lock();
+                       if (valp == &IPV4_DEVCONF_ALL(net, FORWARDING)) {
+                               inet_forward_change(net);
+                       } else if (*valp) {
+                               struct ipv4_devconf *cnf = ctl->extra1;
+                               struct in_device *idev =
+                                       container_of(cnf, struct in_device, cnf);
+                               dev_disable_lro(idev->dev);
+                       }
+                       rtnl_unlock();
+                       rt_cache_flush(net, 0);
+               }
        }
 
        return ret;
 }
 
 int ipv4_doint_and_flush(ctl_table *ctl, int write,
-                        struct filefilp, void __user *buffer,
+                        struct file *filp, void __user *buffer,
                         size_t *lenp, loff_t *ppos)
 {
        int *valp = ctl->data;
        int val = *valp;
        int ret = proc_dointvec(ctl, write, filp, buffer, lenp, ppos);
+       struct net *net = ctl->extra2;
 
        if (write && *valp != val)
-               rt_cache_flush(0);
+               rt_cache_flush(net, 0);
 
        return ret;
 }
 
-int ipv4_doint_and_flush_strategy(ctl_table *table, int __user *name, int nlen,
+int ipv4_doint_and_flush_strategy(ctl_table *table,
                                  void __user *oldval, size_t __user *oldlenp,
                                  void __user *newval, size_t newlen)
 {
-       int ret = devinet_conf_sysctl(table, name, nlen, oldval, oldlenp,
-                                     newval, newlen);
+       int ret = devinet_conf_sysctl(table, oldval, oldlenp, newval, newlen);
+       struct net *net = table->extra2;
 
        if (ret == 1)
-               rt_cache_flush(0);
+               rt_cache_flush(net, 0);
 
        return ret;
 }