]> git.karo-electronics.de Git - mv-sheeva.git/blobdiff - net/ipv4/devinet.c
Merge tag 'v2.6.38' of git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6
[mv-sheeva.git] / net / ipv4 / devinet.c
index dc94b0316b783fd1c1985e407fb0339782c66df5..036652c8166d7cd541d5c3bb3b2af46e84779c48 100644 (file)
@@ -670,7 +670,7 @@ int devinet_ioctl(struct net *net, unsigned int cmd, void __user *arg)
                             ifap = &ifa->ifa_next) {
                                if (!strcmp(ifr.ifr_name, ifa->ifa_label) &&
                                    sin_orig.sin_addr.s_addr ==
-                                                       ifa->ifa_address) {
+                                                       ifa->ifa_local) {
                                        break; /* found */
                                }
                        }
@@ -1030,6 +1030,21 @@ static inline bool inetdev_valid_mtu(unsigned mtu)
        return mtu >= 68;
 }
 
+static void inetdev_send_gratuitous_arp(struct net_device *dev,
+                                       struct in_device *in_dev)
+
+{
+       struct in_ifaddr *ifa = in_dev->ifa_list;
+
+       if (!ifa)
+               return;
+
+       arp_send(ARPOP_REQUEST, ETH_P_ARP,
+                ifa->ifa_local, dev,
+                ifa->ifa_local, NULL,
+                dev->dev_addr, NULL);
+}
+
 /* Called only under RTNL semaphore */
 
 static int inetdev_event(struct notifier_block *this, unsigned long event,
@@ -1082,18 +1097,13 @@ static int inetdev_event(struct notifier_block *this, unsigned long event,
                }
                ip_mc_up(in_dev);
                /* fall through */
-       case NETDEV_NOTIFY_PEERS:
        case NETDEV_CHANGEADDR:
+               if (!IN_DEV_ARP_NOTIFY(in_dev))
+                       break;
+               /* fall through */
+       case NETDEV_NOTIFY_PEERS:
                /* Send gratuitous ARP to notify of link change */
-               if (IN_DEV_ARP_NOTIFY(in_dev)) {
-                       struct in_ifaddr *ifa = in_dev->ifa_list;
-
-                       if (ifa)
-                               arp_send(ARPOP_REQUEST, ETH_P_ARP,
-                                        ifa->ifa_address, dev,
-                                        ifa->ifa_address, NULL,
-                                        dev->dev_addr, NULL);
-               }
+               inetdev_send_gratuitous_arp(dev, in_dev);
                break;
        case NETDEV_DOWN:
                ip_mc_down(in_dev);
@@ -1256,6 +1266,87 @@ errout:
                rtnl_set_sk_err(net, RTNLGRP_IPV4_IFADDR, err);
 }
 
+static size_t inet_get_link_af_size(const struct net_device *dev)
+{
+       struct in_device *in_dev = __in_dev_get_rtnl(dev);
+
+       if (!in_dev)
+               return 0;
+
+       return nla_total_size(IPV4_DEVCONF_MAX * 4); /* IFLA_INET_CONF */
+}
+
+static int inet_fill_link_af(struct sk_buff *skb, const struct net_device *dev)
+{
+       struct in_device *in_dev = __in_dev_get_rtnl(dev);
+       struct nlattr *nla;
+       int i;
+
+       if (!in_dev)
+               return -ENODATA;
+
+       nla = nla_reserve(skb, IFLA_INET_CONF, IPV4_DEVCONF_MAX * 4);
+       if (nla == NULL)
+               return -EMSGSIZE;
+
+       for (i = 0; i < IPV4_DEVCONF_MAX; i++)
+               ((u32 *) nla_data(nla))[i] = in_dev->cnf.data[i];
+
+       return 0;
+}
+
+static const struct nla_policy inet_af_policy[IFLA_INET_MAX+1] = {
+       [IFLA_INET_CONF]        = { .type = NLA_NESTED },
+};
+
+static int inet_validate_link_af(const struct net_device *dev,
+                                const struct nlattr *nla)
+{
+       struct nlattr *a, *tb[IFLA_INET_MAX+1];
+       int err, rem;
+
+       if (dev && !__in_dev_get_rtnl(dev))
+               return -EAFNOSUPPORT;
+
+       err = nla_parse_nested(tb, IFLA_INET_MAX, nla, inet_af_policy);
+       if (err < 0)
+               return err;
+
+       if (tb[IFLA_INET_CONF]) {
+               nla_for_each_nested(a, tb[IFLA_INET_CONF], rem) {
+                       int cfgid = nla_type(a);
+
+                       if (nla_len(a) < 4)
+                               return -EINVAL;
+
+                       if (cfgid <= 0 || cfgid > IPV4_DEVCONF_MAX)
+                               return -EINVAL;
+               }
+       }
+
+       return 0;
+}
+
+static int inet_set_link_af(struct net_device *dev, const struct nlattr *nla)
+{
+       struct in_device *in_dev = __in_dev_get_rtnl(dev);
+       struct nlattr *a, *tb[IFLA_INET_MAX+1];
+       int rem;
+
+       if (!in_dev)
+               return -EAFNOSUPPORT;
+
+       if (nla_parse_nested(tb, IFLA_INET_MAX, nla, NULL) < 0)
+               BUG();
+
+       if (tb[IFLA_INET_CONF]) {
+               nla_for_each_nested(a, tb[IFLA_INET_CONF], rem)
+                       ipv4_devconf_set(in_dev, nla_type(a), nla_get_u32(a));
+       }
+
+       return 0;
+}
+
 #ifdef CONFIG_SYSCTL
 
 static void devinet_copy_dflt_conf(struct net *net, int i)
@@ -1349,9 +1440,9 @@ static int devinet_sysctl_forward(ctl_table *ctl, int write,
        return ret;
 }
 
-int ipv4_doint_and_flush(ctl_table *ctl, int write,
-                        void __user *buffer,
-                        size_t *lenp, loff_t *ppos)
+static int ipv4_doint_and_flush(ctl_table *ctl, int write,
+                               void __user *buffer,
+                               size_t *lenp, loff_t *ppos)
 {
        int *valp = ctl->data;
        int val = *valp;
@@ -1619,6 +1710,14 @@ static __net_initdata struct pernet_operations devinet_ops = {
        .exit = devinet_exit_net,
 };
 
+static struct rtnl_af_ops inet_af_ops = {
+       .family           = AF_INET,
+       .fill_link_af     = inet_fill_link_af,
+       .get_link_af_size = inet_get_link_af_size,
+       .validate_link_af = inet_validate_link_af,
+       .set_link_af      = inet_set_link_af,
+};
+
 void __init devinet_init(void)
 {
        register_pernet_subsys(&devinet_ops);
@@ -1626,6 +1725,8 @@ void __init devinet_init(void)
        register_gifconf(PF_INET, inet_gifconf);
        register_netdevice_notifier(&ip_netdev_notifier);
 
+       rtnl_af_register(&inet_af_ops);
+
        rtnl_register(PF_INET, RTM_NEWADDR, inet_rtm_newaddr, NULL);
        rtnl_register(PF_INET, RTM_DELADDR, inet_rtm_deladdr, NULL);
        rtnl_register(PF_INET, RTM_GETADDR, NULL, inet_dump_ifaddr);