Merge branch 'we21-fix' of git://git.kernel.org/pub/scm/linux/kernel/git/linville...
[GitHub/mt8127/android_kernel_alcatel_ttab.git] / net / ipv6 / addrconf.c
index c09ebb7bb98a3e20a8855e1f4136b7234284d614..b312a5f7a759caa298d42d4ce6763baac0b9ba04 100644 (file)
@@ -396,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);
@@ -1038,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) {
@@ -1240,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);
@@ -1528,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);
 }
@@ -1551,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 = {
@@ -1564,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)
 {
@@ -1834,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;
@@ -1863,6 +1888,7 @@ int addrconf_set_dstaddr(void __user *arg)
                        err = dev_open(dev);
                }
        }
+#endif
 
 err_exit:
        rtnl_unlock();
@@ -1873,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();
@@ -1971,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;
 }
@@ -1993,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;
@@ -2061,6 +2087,7 @@ static void sit_add_v4_addrs(struct inet6_dev *idev)
                }
         }
 }
+#endif
 
 static void init_loopback(struct net_device *dev)
 {
@@ -2124,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;
@@ -2149,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)
@@ -2243,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;
@@ -2514,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);
@@ -2759,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
  */
@@ -2912,28 +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(struct inet6_ifaddr *ifp, u32 prefered_lft,
-                            u32 valid_lft)
+static int inet6_addr_modify(struct inet6_ifaddr *ifp, u8 ifa_flags,
+                            u32 prefered_lft, u32 valid_lft)
 {
-       int ifa_flags = 0;
-
        if (!valid_lft || (prefered_lft > valid_lft))
                return -EINVAL;
 
        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;
@@ -2955,7 +3004,8 @@ inet6_rtm_newaddr(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
        struct in6_addr *pfx;
        struct inet6_ifaddr *ifa;
        struct net_device *dev;
-       u32 valid_lft, preferred_lft;
+       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);
@@ -2982,6 +3032,9 @@ inet6_rtm_newaddr(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
        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) {
                /*
@@ -2989,14 +3042,14 @@ inet6_rtm_newaddr(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
                 * userspace alreay relies on not having to provide this.
                 */
                return inet6_addr_add(ifm->ifa_index, pfx, ifm->ifa_prefixlen,
-                                     preferred_lft, valid_lft);
+                                     ifa_flags, 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, preferred_lft, valid_lft);
+               err = inet6_addr_modify(ifa, ifa_flags, preferred_lft, valid_lft);
 
        in6_ifa_put(ifa);