[IPV6]: Seperate sit driver to extra module (addrconf.c changes)
[safe/jmp/linux-2.6] / net / ipv6 / addrconf.c
index bb18b9c..b312a5f 100644 (file)
@@ -119,9 +119,6 @@ static int ipv6_count_addresses(struct inet6_dev *idev);
 static struct inet6_ifaddr             *inet6_addr_lst[IN6_ADDR_HSIZE];
 static DEFINE_RWLOCK(addrconf_hash_lock);
 
-/* Protects inet6 devices */
-DEFINE_RWLOCK(addrconf_lock);
-
 static void addrconf_verify(unsigned long);
 
 static DEFINE_TIMER(addr_chk_timer, addrconf_verify, 0, 0);
@@ -175,6 +172,7 @@ struct ipv6_devconf ipv6_devconf __read_mostly = {
        .accept_ra_rt_info_max_plen = 0,
 #endif
 #endif
+       .proxy_ndp              = 0,
 };
 
 static struct ipv6_devconf ipv6_devconf_dflt __read_mostly = {
@@ -205,6 +203,7 @@ static struct ipv6_devconf ipv6_devconf_dflt __read_mostly = {
        .accept_ra_rt_info_max_plen = 0,
 #endif
 #endif
+       .proxy_ndp              = 0,
 };
 
 /* IPv6 Wildcard Address and Loopback Address defined by RFC2553 */
@@ -316,6 +315,12 @@ static void addrconf_mod_timer(struct inet6_ifaddr *ifp,
 
 /* Nobody refers to this device, we may destroy it. */
 
+static void in6_dev_finish_destroy_rcu(struct rcu_head *head)
+{
+       struct inet6_dev *idev = container_of(head, struct inet6_dev, rcu);
+       kfree(idev);
+}
+
 void in6_dev_finish_destroy(struct inet6_dev *idev)
 {
        struct net_device *dev = idev->dev;
@@ -330,7 +335,7 @@ void in6_dev_finish_destroy(struct inet6_dev *idev)
                return;
        }
        snmp6_free_dev(idev);
-       kfree(idev);
+       call_rcu(&idev->rcu, in6_dev_finish_destroy_rcu);
 }
 
 static struct inet6_dev * ipv6_add_dev(struct net_device *dev)
@@ -391,8 +396,10 @@ static struct inet6_dev * ipv6_add_dev(struct net_device *dev)
        ndev->regen_timer.data = (unsigned long) ndev;
        if ((dev->flags&IFF_LOOPBACK) ||
            dev->type == ARPHRD_TUNNEL ||
-           dev->type == ARPHRD_NONE ||
-           dev->type == ARPHRD_SIT) {
+#if defined(CONFIG_IPV6_SIT) || defined(CONFIG_IPV6_SIT_MODULE)
+           dev->type == ARPHRD_SIT ||
+#endif
+           dev->type == ARPHRD_NONE) {
                printk(KERN_INFO
                       "%s: Disabled Privacy Extensions\n",
                       dev->name);
@@ -406,9 +413,8 @@ static struct inet6_dev * ipv6_add_dev(struct net_device *dev)
        if (netif_carrier_ok(dev))
                ndev->if_flags |= IF_READY;
 
-       write_lock_bh(&addrconf_lock);
-       dev->ip6_ptr = ndev;
-       write_unlock_bh(&addrconf_lock);
+       /* protected by rtnl_lock */
+       rcu_assign_pointer(dev->ip6_ptr, ndev);
 
        ipv6_mc_init_dev(ndev);
        ndev->tstamp = jiffies;
@@ -472,7 +478,7 @@ static void addrconf_forward_change(void)
 
        read_lock(&dev_base_lock);
        for (dev=dev_base; dev; dev=dev->next) {
-               read_lock(&addrconf_lock);
+               rcu_read_lock();
                idev = __in6_dev_get(dev);
                if (idev) {
                        int changed = (!idev->cnf.forwarding) ^ (!ipv6_devconf.forwarding);
@@ -480,7 +486,7 @@ static void addrconf_forward_change(void)
                        if (changed)
                                dev_forward_change(idev);
                }
-               read_unlock(&addrconf_lock);
+               rcu_read_unlock();
        }
        read_unlock(&dev_base_lock);
 }
@@ -541,7 +547,7 @@ ipv6_add_addr(struct inet6_dev *idev, const struct in6_addr *addr, int pfxlen,
        int hash;
        int err = 0;
 
-       read_lock_bh(&addrconf_lock);
+       rcu_read_lock_bh();
        if (idev->dead) {
                err = -ENODEV;                  /*XXX*/
                goto out2;
@@ -610,7 +616,7 @@ ipv6_add_addr(struct inet6_dev *idev, const struct in6_addr *addr, int pfxlen,
        in6_ifa_hold(ifa);
        write_unlock(&idev->lock);
 out2:
-       read_unlock_bh(&addrconf_lock);
+       rcu_read_unlock_bh();
 
        if (likely(err == 0))
                atomic_notifier_call_chain(&inet6addr_chain, NETDEV_UP, ifa);
@@ -913,7 +919,7 @@ int ipv6_dev_get_saddr(struct net_device *daddr_dev,
        memset(&hiscore, 0, sizeof(hiscore));
 
        read_lock(&dev_base_lock);
-       read_lock(&addrconf_lock);
+       rcu_read_lock();
 
        for (dev = dev_base; dev; dev=dev->next) {
                struct inet6_dev *idev;
@@ -1034,9 +1040,27 @@ int ipv6_dev_get_saddr(struct net_device *daddr_dev,
                                        continue;
                        }
 
-                       /* Rule 4: Prefer home address -- not implemented yet */
+                       /* Rule 4: Prefer home address */
+#ifdef CONFIG_IPV6_MIP6
+                       if (hiscore.rule < 4) {
+                               if (ifa_result->flags & IFA_F_HOMEADDRESS)
+                                       hiscore.attrs |= IPV6_SADDR_SCORE_HOA;
+                               hiscore.rule++;
+                       }
+                       if (ifa->flags & IFA_F_HOMEADDRESS) {
+                               score.attrs |= IPV6_SADDR_SCORE_HOA;
+                               if (!(ifa_result->flags & IFA_F_HOMEADDRESS)) {
+                                       score.rule = 4;
+                                       goto record_it;
+                               }
+                       } else {
+                               if (hiscore.attrs & IPV6_SADDR_SCORE_HOA)
+                                       continue;
+                       }
+#else
                        if (hiscore.rule < 4)
                                hiscore.rule++;
+#endif
 
                        /* Rule 5: Prefer outgoing interface */
                        if (hiscore.rule < 5) {
@@ -1125,7 +1149,7 @@ record_it:
                }
                read_unlock_bh(&idev->lock);
        }
-       read_unlock(&addrconf_lock);
+       rcu_read_unlock();
        read_unlock(&dev_base_lock);
 
        if (!ifa_result)
@@ -1149,7 +1173,7 @@ int ipv6_get_lladdr(struct net_device *dev, struct in6_addr *addr)
        struct inet6_dev *idev;
        int err = -EADDRNOTAVAIL;
 
-       read_lock(&addrconf_lock);
+       rcu_read_lock();
        if ((idev = __in6_dev_get(dev)) != NULL) {
                struct inet6_ifaddr *ifp;
 
@@ -1163,7 +1187,7 @@ int ipv6_get_lladdr(struct net_device *dev, struct in6_addr *addr)
                }
                read_unlock_bh(&idev->lock);
        }
-       read_unlock(&addrconf_lock);
+       rcu_read_unlock();
        return err;
 }
 
@@ -1236,8 +1260,8 @@ int ipv6_rcv_saddr_equal(const struct sock *sk, const struct sock *sk2)
 {
        const struct in6_addr *sk_rcv_saddr6 = &inet6_sk(sk)->rcv_saddr;
        const struct in6_addr *sk2_rcv_saddr6 = inet6_rcv_saddr(sk2);
-       u32 sk_rcv_saddr = inet_sk(sk)->rcv_saddr;
-       u32 sk2_rcv_saddr = inet_rcv_saddr(sk2);
+       __be32 sk_rcv_saddr = inet_sk(sk)->rcv_saddr;
+       __be32 sk2_rcv_saddr = inet_rcv_saddr(sk2);
        int sk_ipv6only = ipv6_only_sock(sk);
        int sk2_ipv6only = inet_v6_ipv6only(sk2);
        int addr_type = ipv6_addr_type(sk_rcv_saddr6);
@@ -1464,7 +1488,7 @@ static void ipv6_regen_rndid(unsigned long data)
        struct inet6_dev *idev = (struct inet6_dev *) data;
        unsigned long expires;
 
-       read_lock_bh(&addrconf_lock);
+       rcu_read_lock_bh();
        write_lock_bh(&idev->lock);
 
        if (idev->dead)
@@ -1488,7 +1512,7 @@ static void ipv6_regen_rndid(unsigned long data)
 
 out:
        write_unlock_bh(&idev->lock);
-       read_unlock_bh(&addrconf_lock);
+       rcu_read_unlock_bh();
        in6_dev_put(idev);
 }
 
@@ -1524,8 +1548,10 @@ addrconf_prefix_route(struct in6_addr *pfx, int plen, struct net_device *dev,
           This thing is done here expecting that the whole
           class of non-broadcast devices need not cloning.
         */
+#if defined(CONFIG_IPV6_SIT) || defined(CONFIG_IPV6_SIT_MODULE)
        if (dev->type == ARPHRD_SIT && (dev->flags & IFF_POINTOPOINT))
                cfg.fc_flags |= RTF_NONEXTHOP;
+#endif
 
        ip6_route_add(&cfg);
 }
@@ -1547,6 +1573,7 @@ static void addrconf_add_mroute(struct net_device *dev)
        ip6_route_add(&cfg);
 }
 
+#if defined(CONFIG_IPV6_SIT) || defined(CONFIG_IPV6_SIT_MODULE)
 static void sit_route_add(struct net_device *dev)
 {
        struct fib6_config cfg = {
@@ -1560,6 +1587,7 @@ static void sit_route_add(struct net_device *dev)
        /* prefix length - 96 bits "::d.d.d.d" */
        ip6_route_add(&cfg);
 }
+#endif
 
 static void addrconf_add_lroute(struct net_device *dev)
 {
@@ -1830,6 +1858,7 @@ int addrconf_set_dstaddr(void __user *arg)
        if (dev == NULL)
                goto err_exit;
 
+#if defined(CONFIG_IPV6_SIT) || defined(CONFIG_IPV6_SIT_MODULE)
        if (dev->type == ARPHRD_SIT) {
                struct ifreq ifr;
                mm_segment_t    oldfs;
@@ -1859,6 +1888,7 @@ int addrconf_set_dstaddr(void __user *arg)
                        err = dev_open(dev);
                }
        }
+#endif
 
 err_exit:
        rtnl_unlock();
@@ -1869,12 +1899,11 @@ err_exit:
  *     Manual configuration of address on an interface
  */
 static int inet6_addr_add(int ifindex, struct in6_addr *pfx, int plen,
-                         __u32 prefered_lft, __u32 valid_lft)
+                         __u8 ifa_flags, __u32 prefered_lft, __u32 valid_lft)
 {
        struct inet6_ifaddr *ifp;
        struct inet6_dev *idev;
        struct net_device *dev;
-       __u8 ifa_flags = 0;
        int scope;
 
        ASSERT_RTNL();
@@ -1967,7 +1996,7 @@ int addrconf_add_ifaddr(void __user *arg)
 
        rtnl_lock();
        err = inet6_addr_add(ireq.ifr6_ifindex, &ireq.ifr6_addr, ireq.ifr6_prefixlen,
-                            INFINITY_LIFE_TIME, INFINITY_LIFE_TIME);
+                            IFA_F_PERMANENT, INFINITY_LIFE_TIME, INFINITY_LIFE_TIME);
        rtnl_unlock();
        return err;
 }
@@ -1989,6 +2018,7 @@ int addrconf_del_ifaddr(void __user *arg)
        return err;
 }
 
+#if defined(CONFIG_IPV6_SIT) || defined(CONFIG_IPV6_SIT_MODULE)
 static void sit_add_v4_addrs(struct inet6_dev *idev)
 {
        struct inet6_ifaddr * ifp;
@@ -2057,6 +2087,7 @@ static void sit_add_v4_addrs(struct inet6_dev *idev)
                }
         }
 }
+#endif
 
 static void init_loopback(struct net_device *dev)
 {
@@ -2120,6 +2151,7 @@ static void addrconf_dev_config(struct net_device *dev)
                addrconf_add_linklocal(idev, &addr);
 }
 
+#if defined(CONFIG_IPV6_SIT) || defined(CONFIG_IPV6_SIT_MODULE)
 static void addrconf_sit_config(struct net_device *dev)
 {
        struct inet6_dev *idev;
@@ -2145,6 +2177,7 @@ static void addrconf_sit_config(struct net_device *dev)
        } else
                sit_route_add(dev);
 }
+#endif
 
 static inline int
 ipv6_inherit_linklocal(struct inet6_dev *idev, struct net_device *link_dev)
@@ -2239,9 +2272,11 @@ static int addrconf_notify(struct notifier_block *this, unsigned long event,
                }
 
                switch(dev->type) {
+#if defined(CONFIG_IPV6_SIT) || defined(CONFIG_IPV6_SIT_MODULE)
                case ARPHRD_SIT:
                        addrconf_sit_config(dev);
                        break;
+#endif
                case ARPHRD_TUNNEL6:
                        addrconf_ip6_tnl_config(dev);
                        break;
@@ -2340,10 +2375,10 @@ static int addrconf_ifdown(struct net_device *dev, int how)
                   Do not dev_put!
         */
        if (how == 1) {
-               write_lock_bh(&addrconf_lock);
-               dev->ip6_ptr = NULL;
                idev->dead = 1;
-               write_unlock_bh(&addrconf_lock);
+
+               /* protected by rtnl_lock */
+               rcu_assign_pointer(dev->ip6_ptr, NULL);
 
                /* Step 1.5: remove snmp6 entry */
                snmp6_unregister_dev(idev);
@@ -2510,7 +2545,8 @@ static void addrconf_dad_start(struct inet6_ifaddr *ifp, u32 flags)
        spin_lock_bh(&ifp->lock);
 
        if (dev->flags&(IFF_NOARP|IFF_LOOPBACK) ||
-           !(ifp->flags&IFA_F_TENTATIVE)) {
+           !(ifp->flags&IFA_F_TENTATIVE) ||
+           ifp->flags & IFA_F_NODAD) {
                ifp->flags &= ~IFA_F_TENTATIVE;
                spin_unlock_bh(&ifp->lock);
                read_unlock_bh(&idev->lock);
@@ -2755,6 +2791,26 @@ void if6_proc_exit(void)
 }
 #endif /* CONFIG_PROC_FS */
 
+#ifdef CONFIG_IPV6_MIP6
+/* Check if address is a home address configured on any interface. */
+int ipv6_chk_home_addr(struct in6_addr *addr)
+{
+       int ret = 0;
+       struct inet6_ifaddr * ifp;
+       u8 hash = ipv6_addr_hash(addr);
+       read_lock_bh(&addrconf_hash_lock);
+       for (ifp = inet6_addr_lst[hash]; ifp; ifp = ifp->lst_next) {
+               if (ipv6_addr_cmp(&ifp->addr, addr) == 0 &&
+                   (ifp->flags & IFA_F_HOMEADDRESS)) {
+                       ret = 1;
+                       break;
+               }
+       }
+       read_unlock_bh(&addrconf_hash_lock);
+       return ret;
+}
+#endif
+
 /*
  *     Periodic address status verification
  */
@@ -2908,38 +2964,25 @@ inet6_rtm_deladdr(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
        return inet6_addr_del(ifm->ifa_index, pfx, ifm->ifa_prefixlen);
 }
 
-static int
-inet6_addr_modify(int ifindex, struct in6_addr *pfx,
-                 __u32 prefered_lft, __u32 valid_lft)
+static int inet6_addr_modify(struct inet6_ifaddr *ifp, u8 ifa_flags,
+                            u32 prefered_lft, u32 valid_lft)
 {
-       struct inet6_ifaddr *ifp = NULL;
-       struct net_device *dev;
-       int ifa_flags = 0;
-
-       if ((dev = __dev_get_by_index(ifindex)) == NULL)
-               return -ENODEV;
-
        if (!valid_lft || (prefered_lft > valid_lft))
                return -EINVAL;
 
-       ifp = ipv6_get_ifaddr(pfx, dev, 1);
-       if (ifp == NULL)
-               return -ENOENT;
-
        if (valid_lft == INFINITY_LIFE_TIME)
-               ifa_flags = IFA_F_PERMANENT;
+               ifa_flags |= IFA_F_PERMANENT;
        else if (valid_lft >= 0x7FFFFFFF/HZ)
                valid_lft = 0x7FFFFFFF/HZ;
 
        if (prefered_lft == 0)
-               ifa_flags = IFA_F_DEPRECATED;
+               ifa_flags |= IFA_F_DEPRECATED;
        else if ((prefered_lft >= 0x7FFFFFFF/HZ) &&
                 (prefered_lft != INFINITY_LIFE_TIME))
                prefered_lft = 0x7FFFFFFF/HZ;
 
        spin_lock_bh(&ifp->lock);
-       ifp->flags = (ifp->flags & ~(IFA_F_DEPRECATED|IFA_F_PERMANENT)) | ifa_flags;
-
+       ifp->flags = (ifp->flags & ~(IFA_F_DEPRECATED | IFA_F_PERMANENT | IFA_F_NODAD | IFA_F_HOMEADDRESS)) | ifa_flags;
        ifp->tstamp = jiffies;
        ifp->valid_lft = valid_lft;
        ifp->prefered_lft = prefered_lft;
@@ -2947,7 +2990,6 @@ inet6_addr_modify(int ifindex, struct in6_addr *pfx,
        spin_unlock_bh(&ifp->lock);
        if (!(ifp->flags&IFA_F_TENTATIVE))
                ipv6_ifa_notify(0, ifp);
-       in6_ifa_put(ifp);
 
        addrconf_verify(0);
 
@@ -2960,7 +3002,10 @@ inet6_rtm_newaddr(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
        struct ifaddrmsg *ifm;
        struct nlattr *tb[IFA_MAX+1];
        struct in6_addr *pfx;
-       u32 valid_lft, preferred_lft;
+       struct inet6_ifaddr *ifa;
+       struct net_device *dev;
+       u32 valid_lft = INFINITY_LIFE_TIME, preferred_lft = INFINITY_LIFE_TIME;
+       u8 ifa_flags;
        int err;
 
        err = nlmsg_parse(nlh, sizeof(*ifm), tb, IFA_MAX, ifa_ipv6_policy);
@@ -2983,15 +3028,32 @@ inet6_rtm_newaddr(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
                valid_lft = INFINITY_LIFE_TIME;
        }
 
-       if (nlh->nlmsg_flags & NLM_F_REPLACE) {
-               err = inet6_addr_modify(ifm->ifa_index, pfx,
-                                       preferred_lft, valid_lft);
-               if (err == 0 || !(nlh->nlmsg_flags & NLM_F_CREATE))
-                       return err;
+       dev =  __dev_get_by_index(ifm->ifa_index);
+       if (dev == NULL)
+               return -ENODEV;
+
+       /* We ignore other flags so far. */
+       ifa_flags = ifm->ifa_flags & (IFA_F_NODAD | IFA_F_HOMEADDRESS);
+
+       ifa = ipv6_get_ifaddr(pfx, dev, 1);
+       if (ifa == NULL) {
+               /*
+                * It would be best to check for !NLM_F_CREATE here but
+                * userspace alreay relies on not having to provide this.
+                */
+               return inet6_addr_add(ifm->ifa_index, pfx, ifm->ifa_prefixlen,
+                                     ifa_flags, preferred_lft, valid_lft);
        }
 
-       return inet6_addr_add(ifm->ifa_index, pfx, ifm->ifa_prefixlen,
-                             preferred_lft, valid_lft);
+       if (nlh->nlmsg_flags & NLM_F_EXCL ||
+           !(nlh->nlmsg_flags & NLM_F_REPLACE))
+               err = -EEXIST;
+       else
+               err = inet6_addr_modify(ifa, ifa_flags, preferred_lft, valid_lft);
+
+       in6_ifa_put(ifa);
+
+       return err;
 }
 
 static void put_ifaddrmsg(struct nlmsghdr *nlh, u8 prefixlen, u8 flags,
@@ -3332,6 +3394,7 @@ static void inline ipv6_store_devconf(struct ipv6_devconf *cnf,
        array[DEVCONF_ACCEPT_RA_RT_INFO_MAX_PLEN] = cnf->accept_ra_rt_info_max_plen;
 #endif
 #endif
+       array[DEVCONF_PROXY_NDP] = cnf->proxy_ndp;
 }
 
 /* Maximum length of ifinfomsg attributes */
@@ -3565,10 +3628,10 @@ static void __ipv6_ifa_notify(int event, struct inet6_ifaddr *ifp)
 
 static void ipv6_ifa_notify(int event, struct inet6_ifaddr *ifp)
 {
-       read_lock_bh(&addrconf_lock);
+       rcu_read_lock_bh();
        if (likely(ifp->idev->dead == 0))
                __ipv6_ifa_notify(event, ifp);
-       read_unlock_bh(&addrconf_lock);
+       rcu_read_unlock_bh();
 }
 
 #ifdef CONFIG_SYSCTL
@@ -3855,6 +3918,14 @@ static struct addrconf_sysctl_table
 #endif
 #endif
                {
+                       .ctl_name       =       NET_IPV6_PROXY_NDP,
+                       .procname       =       "proxy_ndp",
+                       .data           =       &ipv6_devconf.proxy_ndp,
+                       .maxlen         =       sizeof(int),
+                       .mode           =       0644,
+                       .proc_handler   =       &proc_dointvec,
+               },
+               {
                        .ctl_name       =       0,      /* sentinel */
                }
        },