I forgot do mention that the patches are cherry-picked from davem's
net-next tree.

Jiri

Thu, Jan 23, 2014 at 06:23:42PM CET, [email protected] wrote:
>https://bugzilla.redhat.com/show_bug.cgi?id=1056711
>
>NetworkManager depends on IFA_F_NOPREFIXROUTE IFA_F_MANAGETEMPADDR
>in order to do SLAAC in userspace correctly.
>
>Split patches available here:
>
>http://people.redhat.com/jpirko/f20_backport_of_IFA_F_NOPREFIXROUTE_and_IFA_F_MANAGETEMPADDR/
>
>David S. Miller (1):
>  ipv6: Remove privacy config option.
>
>Jiri Pirko (2):
>  ipv6 addrconf: extend ifa_flags to u32
>  ipv6 addrconf: introduce IFA_F_MANAGETEMPADDR to tell kernel to manage
>    temporary addresses
>
>Li RongQing (1):
>  ipv6: unneccessary to get address prefix in addrconf_get_prefix_route
>
>Thomas Haller (2):
>  ipv6 addrconf: add IFA_F_NOPREFIXROUTE flag to suppress creation of
>    IP6 routes
>  ipv6 addrconf: don't cleanup prefix route for IFA_F_NOPREFIXROUTE
>
>stephen hemminger (1):
>  ipv6: addrconf spelling fixes
>
> include/linux/ipv6.h         |   2 -
> include/net/addrconf.h       |   4 +-
> include/net/if_inet6.h       |   7 +-
> include/uapi/linux/if_addr.h |   6 +
> net/ipv6/Kconfig             |  18 --
> net/ipv6/addrconf.c          | 448 +++++++++++++++++++++++--------------------
> 6 files changed, 253 insertions(+), 232 deletions(-)
>
>Signed-off-by: Jiri Pirko <[email protected]>
>
>diff --git a/include/linux/ipv6.h b/include/linux/ipv6.h
>index 28ea384..69cbcac 100644
>--- a/include/linux/ipv6.h
>+++ b/include/linux/ipv6.h
>@@ -21,13 +21,11 @@ struct ipv6_devconf {
>       __s32           force_mld_version;
>       __s32           mldv1_unsolicited_report_interval;
>       __s32           mldv2_unsolicited_report_interval;
>-#ifdef CONFIG_IPV6_PRIVACY
>       __s32           use_tempaddr;
>       __s32           temp_valid_lft;
>       __s32           temp_prefered_lft;
>       __s32           regen_max_retry;
>       __s32           max_desync_factor;
>-#endif
>       __s32           max_addresses;
>       __s32           accept_ra_defrtr;
>       __s32           accept_ra_pinfo;
>diff --git a/include/net/addrconf.h b/include/net/addrconf.h
>index 86505bf..e70278e 100644
>--- a/include/net/addrconf.h
>+++ b/include/net/addrconf.h
>@@ -81,9 +81,9 @@ int ipv6_dev_get_saddr(struct net *net, const struct 
>net_device *dev,
>                      const struct in6_addr *daddr, unsigned int srcprefs,
>                      struct in6_addr *saddr);
> int __ipv6_get_lladdr(struct inet6_dev *idev, struct in6_addr *addr,
>-                    unsigned char banned_flags);
>+                    u32 banned_flags);
> int ipv6_get_lladdr(struct net_device *dev, struct in6_addr *addr,
>-                  unsigned char banned_flags);
>+                  u32 banned_flags);
> int ipv6_rcv_saddr_equal(const struct sock *sk, const struct sock *sk2);
> void addrconf_join_solict(struct net_device *dev, const struct in6_addr 
> *addr);
> void addrconf_leave_solict(struct inet6_dev *idev, const struct in6_addr 
> *addr);
>diff --git a/include/net/if_inet6.h b/include/net/if_inet6.h
>index 02ef772..b58c36c 100644
>--- a/include/net/if_inet6.h
>+++ b/include/net/if_inet6.h
>@@ -50,8 +50,8 @@ struct inet6_ifaddr {
> 
>       int                     state;
> 
>+      __u32                   flags;
>       __u8                    dad_probes;
>-      __u8                    flags;
> 
>       __u16                   scope;
> 
>@@ -66,11 +66,10 @@ struct inet6_ifaddr {
>       struct hlist_node       addr_lst;
>       struct list_head        if_list;
> 
>-#ifdef CONFIG_IPV6_PRIVACY
>       struct list_head        tmp_list;
>       struct inet6_ifaddr     *ifpub;
>       int                     regen_count;
>-#endif
>+
>       bool                    tokenized;
> 
>       struct rcu_head         rcu;
>@@ -192,11 +191,9 @@ struct inet6_dev {
>       __u32                   if_flags;
>       int                     dead;
> 
>-#ifdef CONFIG_IPV6_PRIVACY
>       u8                      rndid[8];
>       struct timer_list       regen_timer;
>       struct list_head        tempaddr_list;
>-#endif
> 
>       struct in6_addr         token;
> 
>diff --git a/include/uapi/linux/if_addr.h b/include/uapi/linux/if_addr.h
>index 23357ab..dea10a8 100644
>--- a/include/uapi/linux/if_addr.h
>+++ b/include/uapi/linux/if_addr.h
>@@ -18,6 +18,9 @@ struct ifaddrmsg {
>  * It makes no difference for normally configured broadcast interfaces,
>  * but for point-to-point IFA_ADDRESS is DESTINATION address,
>  * local address is supplied in IFA_LOCAL attribute.
>+ *
>+ * IFA_FLAGS is a u32 attribute that extends the u8 field ifa_flags.
>+ * If present, the value from struct ifaddrmsg will be ignored.
>  */
> enum {
>       IFA_UNSPEC,
>@@ -28,6 +31,7 @@ enum {
>       IFA_ANYCAST,
>       IFA_CACHEINFO,
>       IFA_MULTICAST,
>+      IFA_FLAGS,
>       __IFA_MAX,
> };
> 
>@@ -44,6 +48,8 @@ enum {
> #define IFA_F_DEPRECATED      0x20
> #define IFA_F_TENTATIVE               0x40
> #define IFA_F_PERMANENT               0x80
>+#define IFA_F_MANAGETEMPADDR  0x100
>+#define IFA_F_NOPREFIXROUTE   0x200
> 
> struct ifa_cacheinfo {
>       __u32   ifa_prefered;
>diff --git a/net/ipv6/Kconfig b/net/ipv6/Kconfig
>index 11b13ea..aac8434 100644
>--- a/net/ipv6/Kconfig
>+++ b/net/ipv6/Kconfig
>@@ -21,24 +21,6 @@ menuconfig IPV6
> 
> if IPV6
> 
>-config IPV6_PRIVACY
>-      bool "IPv6: Privacy Extensions (RFC 3041) support"
>-      ---help---
>-        Privacy Extensions for Stateless Address Autoconfiguration in IPv6
>-        support.  With this option, additional periodically-altered
>-        pseudo-random global-scope unicast address(es) will be assigned to
>-        your interface(s).
>-      
>-        We use our standard pseudo-random algorithm to generate the
>-          randomized interface identifier, instead of one described in RFC 
>3041.
>-
>-        By default the kernel does not generate temporary addresses.
>-        To use temporary addresses, do
>-      
>-              echo 2 >/proc/sys/net/ipv6/conf/all/use_tempaddr 
>-
>-        See <file:Documentation/networking/ip-sysctl.txt> for details.
>-
> config IPV6_ROUTER_PREF
>       bool "IPv6: Router Preference (RFC 4191) support"
>       ---help---
>diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c
>index cd3fb30..5ebd42e 100644
>--- a/net/ipv6/addrconf.c
>+++ b/net/ipv6/addrconf.c
>@@ -83,11 +83,7 @@
> #include <linux/if_tunnel.h>
> #include <linux/rtnetlink.h>
> #include <linux/netconf.h>
>-
>-#ifdef CONFIG_IPV6_PRIVACY
> #include <linux/random.h>
>-#endif
>-
> #include <linux/uaccess.h>
> #include <asm/unaligned.h>
> 
>@@ -124,11 +120,9 @@ static inline void addrconf_sysctl_unregister(struct 
>inet6_dev *idev)
> }
> #endif
> 
>-#ifdef CONFIG_IPV6_PRIVACY
> static void __ipv6_regen_rndid(struct inet6_dev *idev);
> static void __ipv6_try_regen_rndid(struct inet6_dev *idev, struct in6_addr 
> *tmpaddr);
> static void ipv6_regen_rndid(unsigned long data);
>-#endif
> 
> static int ipv6_generate_eui64(u8 *eui, struct net_device *dev);
> static int ipv6_count_addresses(struct inet6_dev *idev);
>@@ -183,13 +177,11 @@ static struct ipv6_devconf ipv6_devconf __read_mostly = {
>       .rtr_solicits           = MAX_RTR_SOLICITATIONS,
>       .rtr_solicit_interval   = RTR_SOLICITATION_INTERVAL,
>       .rtr_solicit_delay      = MAX_RTR_SOLICITATION_DELAY,
>-#ifdef CONFIG_IPV6_PRIVACY
>       .use_tempaddr           = 0,
>       .temp_valid_lft         = TEMP_VALID_LIFETIME,
>       .temp_prefered_lft      = TEMP_PREFERRED_LIFETIME,
>       .regen_max_retry        = REGEN_MAX_RETRY,
>       .max_desync_factor      = MAX_DESYNC_FACTOR,
>-#endif
>       .max_addresses          = IPV6_MAX_ADDRESSES,
>       .accept_ra_defrtr       = 1,
>       .accept_ra_pinfo        = 1,
>@@ -221,13 +213,11 @@ static struct ipv6_devconf ipv6_devconf_dflt 
>__read_mostly = {
>       .rtr_solicits           = MAX_RTR_SOLICITATIONS,
>       .rtr_solicit_interval   = RTR_SOLICITATION_INTERVAL,
>       .rtr_solicit_delay      = MAX_RTR_SOLICITATION_DELAY,
>-#ifdef CONFIG_IPV6_PRIVACY
>       .use_tempaddr           = 0,
>       .temp_valid_lft         = TEMP_VALID_LIFETIME,
>       .temp_prefered_lft      = TEMP_PREFERRED_LIFETIME,
>       .regen_max_retry        = REGEN_MAX_RETRY,
>       .max_desync_factor      = MAX_DESYNC_FACTOR,
>-#endif
>       .max_addresses          = IPV6_MAX_ADDRESSES,
>       .accept_ra_defrtr       = 1,
>       .accept_ra_pinfo        = 1,
>@@ -371,7 +361,6 @@ static struct inet6_dev *ipv6_add_dev(struct net_device 
>*dev)
>       }
> #endif
> 
>-#ifdef CONFIG_IPV6_PRIVACY
>       INIT_LIST_HEAD(&ndev->tempaddr_list);
>       setup_timer(&ndev->regen_timer, ipv6_regen_rndid, (unsigned long)ndev);
>       if ((dev->flags&IFF_LOOPBACK) ||
>@@ -384,7 +373,7 @@ static struct inet6_dev *ipv6_add_dev(struct net_device 
>*dev)
>               in6_dev_hold(ndev);
>               ipv6_regen_rndid((unsigned long) ndev);
>       }
>-#endif
>+
>       ndev->token = in6addr_any;
> 
>       if (netif_running(dev) && addrconf_qdisc_ok(dev))
>@@ -865,12 +854,10 @@ ipv6_add_addr(struct inet6_dev *idev, const struct 
>in6_addr *addr,
>       /* Add to inet6_dev unicast addr list. */
>       ipv6_link_dev_addr(idev, ifa);
> 
>-#ifdef CONFIG_IPV6_PRIVACY
>       if (ifa->flags&IFA_F_TEMPORARY) {
>               list_add(&ifa->tmp_list, &idev->tempaddr_list);
>               in6_ifa_hold(ifa);
>       }
>-#endif
> 
>       in6_ifa_hold(ifa);
>       write_unlock(&idev->lock);
>@@ -890,15 +877,95 @@ out:
>       goto out2;
> }
> 
>+enum cleanup_prefix_rt_t {
>+      CLEANUP_PREFIX_RT_NOP,    /* no cleanup action for prefix route */
>+      CLEANUP_PREFIX_RT_DEL,    /* delete the prefix route */
>+      CLEANUP_PREFIX_RT_EXPIRE, /* update the lifetime of the prefix route */
>+};
>+
>+/*
>+ * Check, whether the prefix for ifp would still need a prefix route
>+ * after deleting ifp. The function returns one of the CLEANUP_PREFIX_RT_*
>+ * constants.
>+ *
>+ * 1) we don't purge prefix if address was not permanent.
>+ *    prefix is managed by its own lifetime.
>+ * 2) we also don't purge, if the address was IFA_F_NOPREFIXROUTE.
>+ * 3) if there are no addresses, delete prefix.
>+ * 4) if there are still other permanent address(es),
>+ *    corresponding prefix is still permanent.
>+ * 5) if there are still other addresses with IFA_F_NOPREFIXROUTE,
>+ *    don't purge the prefix, assume user space is managing it.
>+ * 6) otherwise, update prefix lifetime to the
>+ *    longest valid lifetime among the corresponding
>+ *    addresses on the device.
>+ *    Note: subsequent RA will update lifetime.
>+ **/
>+static enum cleanup_prefix_rt_t
>+check_cleanup_prefix_route(struct inet6_ifaddr *ifp, unsigned long *expires)
>+{
>+      struct inet6_ifaddr *ifa;
>+      struct inet6_dev *idev = ifp->idev;
>+      unsigned long lifetime;
>+      enum cleanup_prefix_rt_t action = CLEANUP_PREFIX_RT_DEL;
>+
>+      *expires = jiffies;
>+
>+      list_for_each_entry(ifa, &idev->addr_list, if_list) {
>+              if (ifa == ifp)
>+                      continue;
>+              if (!ipv6_prefix_equal(&ifa->addr, &ifp->addr,
>+                                     ifp->prefix_len))
>+                      continue;
>+              if (ifa->flags & (IFA_F_PERMANENT | IFA_F_NOPREFIXROUTE))
>+                      return CLEANUP_PREFIX_RT_NOP;
>+
>+              action = CLEANUP_PREFIX_RT_EXPIRE;
>+
>+              spin_lock(&ifa->lock);
>+
>+              lifetime = addrconf_timeout_fixup(ifa->valid_lft, HZ);
>+              /*
>+               * Note: Because this address is
>+               * not permanent, lifetime <
>+               * LONG_MAX / HZ here.
>+               */
>+              if (time_before(*expires, ifa->tstamp + lifetime * HZ))
>+                      *expires = ifa->tstamp + lifetime * HZ;
>+              spin_unlock(&ifa->lock);
>+      }
>+
>+      return action;
>+}
>+
>+static void
>+cleanup_prefix_route(struct inet6_ifaddr *ifp, unsigned long expires, bool 
>del_rt)
>+{
>+      struct rt6_info *rt;
>+
>+      rt = addrconf_get_prefix_route(&ifp->addr,
>+                                     ifp->prefix_len,
>+                                     ifp->idev->dev,
>+                                     0, RTF_GATEWAY | RTF_DEFAULT);
>+      if (rt) {
>+              if (del_rt)
>+                      ip6_del_rt(rt);
>+              else {
>+                      if (!(rt->rt6i_flags & RTF_EXPIRES))
>+                              rt6_set_expires(rt, expires);
>+                      ip6_rt_put(rt);
>+              }
>+      }
>+}
>+
>+
> /* This function wants to get referenced ifp and releases it before return */
> 
> static void ipv6_del_addr(struct inet6_ifaddr *ifp)
> {
>-      struct inet6_ifaddr *ifa, *ifn;
>-      struct inet6_dev *idev = ifp->idev;
>       int state;
>-      int deleted = 0, onlink = 0;
>-      unsigned long expires = jiffies;
>+      enum cleanup_prefix_rt_t action = CLEANUP_PREFIX_RT_NOP;
>+      unsigned long expires;
> 
>       spin_lock_bh(&ifp->state_lock);
>       state = ifp->state;
>@@ -912,8 +979,8 @@ static void ipv6_del_addr(struct inet6_ifaddr *ifp)
>       hlist_del_init_rcu(&ifp->addr_lst);
>       spin_unlock_bh(&addrconf_hash_lock);
> 
>-      write_lock_bh(&idev->lock);
>-#ifdef CONFIG_IPV6_PRIVACY
>+      write_lock_bh(&ifp->idev->lock);
>+
>       if (ifp->flags&IFA_F_TEMPORARY) {
>               list_del(&ifp->tmp_list);
>               if (ifp->ifpub) {
>@@ -922,47 +989,14 @@ static void ipv6_del_addr(struct inet6_ifaddr *ifp)
>               }
>               __in6_ifa_put(ifp);
>       }
>-#endif
> 
>-      list_for_each_entry_safe(ifa, ifn, &idev->addr_list, if_list) {
>-              if (ifa == ifp) {
>-                      list_del_init(&ifp->if_list);
>-                      __in6_ifa_put(ifp);
>+      if (ifp->flags & IFA_F_PERMANENT && !(ifp->flags & IFA_F_NOPREFIXROUTE))
>+              action = check_cleanup_prefix_route(ifp, &expires);
> 
>-                      if (!(ifp->flags & IFA_F_PERMANENT) || onlink > 0)
>-                              break;
>-                      deleted = 1;
>-                      continue;
>-              } else if (ifp->flags & IFA_F_PERMANENT) {
>-                      if (ipv6_prefix_equal(&ifa->addr, &ifp->addr,
>-                                            ifp->prefix_len)) {
>-                              if (ifa->flags & IFA_F_PERMANENT) {
>-                                      onlink = 1;
>-                                      if (deleted)
>-                                              break;
>-                              } else {
>-                                      unsigned long lifetime;
>-
>-                                      if (!onlink)
>-                                              onlink = -1;
>-
>-                                      spin_lock(&ifa->lock);
>-
>-                                      lifetime = 
>addrconf_timeout_fixup(ifa->valid_lft, HZ);
>-                                      /*
>-                                       * Note: Because this address is
>-                                       * not permanent, lifetime <
>-                                       * LONG_MAX / HZ here.
>-                                       */
>-                                      if (time_before(expires,
>-                                                      ifa->tstamp + lifetime 
>* HZ))
>-                                              expires = ifa->tstamp + 
>lifetime * HZ;
>-                                      spin_unlock(&ifa->lock);
>-                              }
>-                      }
>-              }
>-      }
>-      write_unlock_bh(&idev->lock);
>+      list_del_init(&ifp->if_list);
>+      __in6_ifa_put(ifp);
>+
>+      write_unlock_bh(&ifp->idev->lock);
> 
>       addrconf_del_dad_timer(ifp);
> 
>@@ -970,41 +1004,9 @@ static void ipv6_del_addr(struct inet6_ifaddr *ifp)
> 
>       inet6addr_notifier_call_chain(NETDEV_DOWN, ifp);
> 
>-      /*
>-       * Purge or update corresponding prefix
>-       *
>-       * 1) we don't purge prefix here if address was not permanent.
>-       *    prefix is managed by its own lifetime.
>-       * 2) if there're no addresses, delete prefix.
>-       * 3) if there're still other permanent address(es),
>-       *    corresponding prefix is still permanent.
>-       * 4) otherwise, update prefix lifetime to the
>-       *    longest valid lifetime among the corresponding
>-       *    addresses on the device.
>-       *    Note: subsequent RA will update lifetime.
>-       *
>-       * --yoshfuji
>-       */
>-      if ((ifp->flags & IFA_F_PERMANENT) && onlink < 1) {
>-              struct in6_addr prefix;
>-              struct rt6_info *rt;
>-
>-              ipv6_addr_prefix(&prefix, &ifp->addr, ifp->prefix_len);
>-
>-              rt = addrconf_get_prefix_route(&prefix,
>-                                             ifp->prefix_len,
>-                                             ifp->idev->dev,
>-                                             0, RTF_GATEWAY | RTF_DEFAULT);
>-
>-              if (rt) {
>-                      if (onlink == 0) {
>-                              ip6_del_rt(rt);
>-                              rt = NULL;
>-                      } else if (!(rt->rt6i_flags & RTF_EXPIRES)) {
>-                              rt6_set_expires(rt, expires);
>-                      }
>-              }
>-              ip6_rt_put(rt);
>+      if (action != CLEANUP_PREFIX_RT_NOP) {
>+              cleanup_prefix_route(ifp, expires,
>+                      action == CLEANUP_PREFIX_RT_DEL);
>       }
> 
>       /* clean up prefsrc entries */
>@@ -1013,7 +1015,6 @@ out:
>       in6_ifa_put(ifp);
> }
> 
>-#ifdef CONFIG_IPV6_PRIVACY
> static int ipv6_create_tempaddr(struct inet6_ifaddr *ifp, struct inet6_ifaddr 
> *ift)
> {
>       struct inet6_dev *idev = ifp->idev;
>@@ -1025,7 +1026,7 @@ static int ipv6_create_tempaddr(struct inet6_ifaddr 
>*ifp, struct inet6_ifaddr *i
>       u32 addr_flags;
>       unsigned long now = jiffies;
> 
>-      write_lock(&idev->lock);
>+      write_lock_bh(&idev->lock);
>       if (ift) {
>               spin_lock_bh(&ift->lock);
>               memcpy(&addr.s6_addr[8], &ift->addr.s6_addr[8], 8);
>@@ -1037,7 +1038,7 @@ static int ipv6_create_tempaddr(struct inet6_ifaddr 
>*ifp, struct inet6_ifaddr *i
> retry:
>       in6_dev_hold(idev);
>       if (idev->cnf.use_tempaddr <= 0) {
>-              write_unlock(&idev->lock);
>+              write_unlock_bh(&idev->lock);
>               pr_info("%s: use_tempaddr is disabled\n", __func__);
>               in6_dev_put(idev);
>               ret = -1;
>@@ -1047,7 +1048,7 @@ retry:
>       if (ifp->regen_count++ >= idev->cnf.regen_max_retry) {
>               idev->cnf.use_tempaddr = -1;    /*XXX*/
>               spin_unlock_bh(&ifp->lock);
>-              write_unlock(&idev->lock);
>+              write_unlock_bh(&idev->lock);
>               pr_warn("%s: regeneration time exceeded - disabled temporary 
> address support\n",
>                       __func__);
>               in6_dev_put(idev);
>@@ -1073,7 +1074,7 @@ retry:
>       regen_advance = idev->cnf.regen_max_retry *
>                       idev->cnf.dad_transmits *
>                       idev->nd_parms->retrans_time / HZ;
>-      write_unlock(&idev->lock);
>+      write_unlock_bh(&idev->lock);
> 
>       /* A temporary address is created only if this calculated Preferred
>        * Lifetime is greater than REGEN_ADVANCE time units.  In particular,
>@@ -1100,7 +1101,7 @@ retry:
>               in6_dev_put(idev);
>               pr_info("%s: retry temporary address regeneration\n", __func__);
>               tmpaddr = &addr;
>-              write_lock(&idev->lock);
>+              write_lock_bh(&idev->lock);
>               goto retry;
>       }
> 
>@@ -1116,7 +1117,6 @@ retry:
> out:
>       return ret;
> }
>-#endif
> 
> /*
>  *    Choose an appropriate source address (RFC3484)
>@@ -1131,9 +1131,7 @@ enum {
> #endif
>       IPV6_SADDR_RULE_OIF,
>       IPV6_SADDR_RULE_LABEL,
>-#ifdef CONFIG_IPV6_PRIVACY
>       IPV6_SADDR_RULE_PRIVACY,
>-#endif
>       IPV6_SADDR_RULE_ORCHID,
>       IPV6_SADDR_RULE_PREFIX,
>       IPV6_SADDR_RULE_MAX
>@@ -1204,7 +1202,7 @@ static int ipv6_get_saddr_eval(struct net *net,
>                *       |             d is scope of the destination.
>                *  B-d  |  \
>                *       |   \      <- smaller scope is better if
>-               *  B-15 |    \        if scope is enough for destinaion.
>+               *  B-15 |    \        if scope is enough for destination.
>                *       |             ret = B - scope (-1 <= scope >= d <= 15).
>                * d-C-1 | /
>                *       |/         <- greater is better
>@@ -1247,7 +1245,6 @@ static int ipv6_get_saddr_eval(struct net *net,
>                                     &score->ifa->addr, score->addr_type,
>                                     score->ifa->idev->dev->ifindex) == 
> dst->label;
>               break;
>-#ifdef CONFIG_IPV6_PRIVACY
>       case IPV6_SADDR_RULE_PRIVACY:
>           {
>               /* Rule 7: Prefer public address
>@@ -1259,7 +1256,6 @@ static int ipv6_get_saddr_eval(struct net *net,
>               ret = (!(score->ifa->flags & IFA_F_TEMPORARY)) ^ preftmp;
>               break;
>           }
>-#endif
>       case IPV6_SADDR_RULE_ORCHID:
>               /* Rule 8-: Prefer ORCHID vs ORCHID or
>                *          non-ORCHID vs non-ORCHID
>@@ -1413,7 +1409,7 @@ try_nextdev:
> EXPORT_SYMBOL(ipv6_dev_get_saddr);
> 
> int __ipv6_get_lladdr(struct inet6_dev *idev, struct in6_addr *addr,
>-                    unsigned char banned_flags)
>+                    u32 banned_flags)
> {
>       struct inet6_ifaddr *ifp;
>       int err = -EADDRNOTAVAIL;
>@@ -1430,7 +1426,7 @@ int __ipv6_get_lladdr(struct inet6_dev *idev, struct 
>in6_addr *addr,
> }
> 
> int ipv6_get_lladdr(struct net_device *dev, struct in6_addr *addr,
>-                  unsigned char banned_flags)
>+                  u32 banned_flags)
> {
>       struct inet6_dev *idev;
>       int err = -EADDRNOTAVAIL;
>@@ -1588,7 +1584,6 @@ static void addrconf_dad_stop(struct inet6_ifaddr *ifp, 
>int dad_failed)
>               if (dad_failed)
>                       ipv6_ifa_notify(0, ifp);
>               in6_ifa_put(ifp);
>-#ifdef CONFIG_IPV6_PRIVACY
>       } else if (ifp->flags&IFA_F_TEMPORARY) {
>               struct inet6_ifaddr *ifpub;
>               spin_lock_bh(&ifp->lock);
>@@ -1602,7 +1597,6 @@ static void addrconf_dad_stop(struct inet6_ifaddr *ifp, 
>int dad_failed)
>                       spin_unlock_bh(&ifp->lock);
>               }
>               ipv6_del_addr(ifp);
>-#endif
>       } else
>               ipv6_del_addr(ifp);
> }
>@@ -1851,7 +1845,6 @@ static int ipv6_inherit_eui64(u8 *eui, struct inet6_dev 
>*idev)
>       return err;
> }
> 
>-#ifdef CONFIG_IPV6_PRIVACY
> /* (re)generation of randomized interface identifier (RFC 3041 3.2, 3.5) */
> static void __ipv6_regen_rndid(struct inet6_dev *idev)
> {
>@@ -1919,7 +1912,6 @@ static void  __ipv6_try_regen_rndid(struct inet6_dev 
>*idev, struct in6_addr *tmp
>       if (tmpaddr && memcmp(idev->rndid, &tmpaddr->s6_addr[8], 8) == 0)
>               __ipv6_regen_rndid(idev);
> }
>-#endif
> 
> /*
>  *    Add prefix route.
>@@ -2043,6 +2035,73 @@ static struct inet6_dev *addrconf_add_dev(struct 
>net_device *dev)
>       return idev;
> }
> 
>+static void manage_tempaddrs(struct inet6_dev *idev,
>+                           struct inet6_ifaddr *ifp,
>+                           __u32 valid_lft, __u32 prefered_lft,
>+                           bool create, unsigned long now)
>+{
>+      u32 flags;
>+      struct inet6_ifaddr *ift;
>+
>+      read_lock_bh(&idev->lock);
>+      /* update all temporary addresses in the list */
>+      list_for_each_entry(ift, &idev->tempaddr_list, tmp_list) {
>+              int age, max_valid, max_prefered;
>+
>+              if (ifp != ift->ifpub)
>+                      continue;
>+
>+              /* RFC 4941 section 3.3:
>+               * If a received option will extend the lifetime of a public
>+               * address, the lifetimes of temporary addresses should
>+               * be extended, subject to the overall constraint that no
>+               * temporary addresses should ever remain "valid" or "preferred"
>+               * for a time longer than (TEMP_VALID_LIFETIME) or
>+               * (TEMP_PREFERRED_LIFETIME - DESYNC_FACTOR), respectively.
>+               */
>+              age = (now - ift->cstamp) / HZ;
>+              max_valid = idev->cnf.temp_valid_lft - age;
>+              if (max_valid < 0)
>+                      max_valid = 0;
>+
>+              max_prefered = idev->cnf.temp_prefered_lft -
>+                             idev->cnf.max_desync_factor - age;
>+              if (max_prefered < 0)
>+                      max_prefered = 0;
>+
>+              if (valid_lft > max_valid)
>+                      valid_lft = max_valid;
>+
>+              if (prefered_lft > max_prefered)
>+                      prefered_lft = max_prefered;
>+
>+              spin_lock(&ift->lock);
>+              flags = ift->flags;
>+              ift->valid_lft = valid_lft;
>+              ift->prefered_lft = prefered_lft;
>+              ift->tstamp = now;
>+              if (prefered_lft > 0)
>+                      ift->flags &= ~IFA_F_DEPRECATED;
>+
>+              spin_unlock(&ift->lock);
>+              if (!(flags&IFA_F_TENTATIVE))
>+                      ipv6_ifa_notify(0, ift);
>+      }
>+
>+      if ((create || list_empty(&idev->tempaddr_list)) &&
>+          idev->cnf.use_tempaddr > 0) {
>+              /* When a new public address is created as described
>+               * in [ADDRCONF], also create a new temporary address.
>+               * Also create a temporary address if it's enabled but
>+               * no temporary address currently exists.
>+               */
>+              read_unlock_bh(&idev->lock);
>+              ipv6_create_tempaddr(ifp, NULL);
>+      } else {
>+              read_unlock_bh(&idev->lock);
>+      }
>+}
>+
> void addrconf_prefix_rcv(struct net_device *dev, u8 *opt, int len, bool sllao)
> {
>       struct prefix_info *pinfo;
>@@ -2197,6 +2256,7 @@ ok:
>                               return;
>                       }
> 
>+                      ifp->flags |= IFA_F_MANAGETEMPADDR;
>                       update_lft = 0;
>                       create = 1;
>                       ifp->cstamp = jiffies;
>@@ -2205,11 +2265,8 @@ ok:
>               }
> 
>               if (ifp) {
>-                      int flags;
>+                      u32 flags;
>                       unsigned long now;
>-#ifdef CONFIG_IPV6_PRIVACY
>-                      struct inet6_ifaddr *ift;
>-#endif
>                       u32 stored_lft;
> 
>                       /* update lifetime (RFC2462 5.5.3 e) */
>@@ -2250,72 +2307,9 @@ ok:
>                       } else
>                               spin_unlock(&ifp->lock);
> 
>-#ifdef CONFIG_IPV6_PRIVACY
>-                      read_lock_bh(&in6_dev->lock);
>-                      /* update all temporary addresses in the list */
>-                      list_for_each_entry(ift, &in6_dev->tempaddr_list,
>-                                          tmp_list) {
>-                              int age, max_valid, max_prefered;
>-
>-                              if (ifp != ift->ifpub)
>-                                      continue;
>-
>-                              /*
>-                               * RFC 4941 section 3.3:
>-                               * If a received option will extend the lifetime
>-                               * of a public address, the lifetimes of
>-                               * temporary addresses should be extended,
>-                               * subject to the overall constraint that no
>-                               * temporary addresses should ever remain
>-                               * "valid" or "preferred" for a time longer than
>-                               * (TEMP_VALID_LIFETIME) or
>-                               * (TEMP_PREFERRED_LIFETIME - DESYNC_FACTOR),
>-                               * respectively.
>-                               */
>-                              age = (now - ift->cstamp) / HZ;
>-                              max_valid = in6_dev->cnf.temp_valid_lft - age;
>-                              if (max_valid < 0)
>-                                      max_valid = 0;
>-
>-                              max_prefered = in6_dev->cnf.temp_prefered_lft -
>-                                             in6_dev->cnf.max_desync_factor -
>-                                             age;
>-                              if (max_prefered < 0)
>-                                      max_prefered = 0;
>-
>-                              if (valid_lft > max_valid)
>-                                      valid_lft = max_valid;
>-
>-                              if (prefered_lft > max_prefered)
>-                                      prefered_lft = max_prefered;
>-
>-                              spin_lock(&ift->lock);
>-                              flags = ift->flags;
>-                              ift->valid_lft = valid_lft;
>-                              ift->prefered_lft = prefered_lft;
>-                              ift->tstamp = now;
>-                              if (prefered_lft > 0)
>-                                      ift->flags &= ~IFA_F_DEPRECATED;
>-
>-                              spin_unlock(&ift->lock);
>-                              if (!(flags&IFA_F_TENTATIVE))
>-                                      ipv6_ifa_notify(0, ift);
>-                      }
>+                      manage_tempaddrs(in6_dev, ifp, valid_lft, prefered_lft,
>+                                       create, now);
> 
>-                      if ((create || list_empty(&in6_dev->tempaddr_list)) && 
>in6_dev->cnf.use_tempaddr > 0) {
>-                              /*
>-                               * When a new public address is created as
>-                               * described in [ADDRCONF], also create a new
>-                               * temporary address. Also create a temporary
>-                               * address if it's enabled but no temporary
>-                               * address currently exists.
>-                               */
>-                              read_unlock_bh(&in6_dev->lock);
>-                              ipv6_create_tempaddr(ifp, NULL);
>-                      } else {
>-                              read_unlock_bh(&in6_dev->lock);
>-                      }
>-#endif
>                       in6_ifa_put(ifp);
>                       addrconf_verify(0);
>               }
>@@ -2393,10 +2387,11 @@ err_exit:
> /*
>  *    Manual configuration of address on an interface
>  */
>-static int inet6_addr_add(struct net *net, int ifindex, const struct in6_addr 
>*pfx,
>+static int inet6_addr_add(struct net *net, int ifindex,
>+                        const struct in6_addr *pfx,
>                         const struct in6_addr *peer_pfx,
>-                        unsigned int plen, __u8 ifa_flags, __u32 prefered_lft,
>-                        __u32 valid_lft)
>+                        unsigned int plen, __u32 ifa_flags,
>+                        __u32 prefered_lft, __u32 valid_lft)
> {
>       struct inet6_ifaddr *ifp;
>       struct inet6_dev *idev;
>@@ -2415,6 +2410,9 @@ static int inet6_addr_add(struct net *net, int ifindex, 
>const struct in6_addr *p
>       if (!valid_lft || prefered_lft > valid_lft)
>               return -EINVAL;
> 
>+      if (ifa_flags & IFA_F_MANAGETEMPADDR && plen != 64)
>+              return -EINVAL;
>+
>       dev = __dev_get_by_index(net, ifindex);
>       if (!dev)
>               return -ENODEV;
>@@ -2447,14 +2445,20 @@ static int inet6_addr_add(struct net *net, int 
>ifindex, const struct in6_addr *p
>                           valid_lft, prefered_lft);
> 
>       if (!IS_ERR(ifp)) {
>-              addrconf_prefix_route(&ifp->addr, ifp->prefix_len, dev,
>-                                    expires, flags);
>+              if (!(ifa_flags & IFA_F_NOPREFIXROUTE)) {
>+                      addrconf_prefix_route(&ifp->addr, ifp->prefix_len, dev,
>+                                            expires, flags);
>+              }
>+
>               /*
>                * Note that section 3.1 of RFC 4429 indicates
>                * that the Optimistic flag should not be set for
>                * manually configured addresses
>                */
>               addrconf_dad_start(ifp);
>+              if (ifa_flags & IFA_F_MANAGETEMPADDR)
>+                      manage_tempaddrs(idev, ifp, valid_lft, prefered_lft,
>+                                       true, jiffies);
>               in6_ifa_put(ifp);
>               addrconf_verify(0);
>               return 0;
>@@ -2888,7 +2892,7 @@ static int addrconf_notify(struct notifier_block *this, 
>unsigned long event,
>               }
> 
>               /*
>-               * MTU falled under IPV6_MIN_MTU.
>+               * if MTU under IPV6_MIN_MTU.
>                * Stop IPv6 on this interface.
>                */
> 
>@@ -2995,7 +2999,6 @@ static int addrconf_ifdown(struct net_device *dev, int 
>how)
>       if (!how)
>               idev->if_flags &= ~(IF_RS_SENT|IF_RA_RCVD|IF_READY);
> 
>-#ifdef CONFIG_IPV6_PRIVACY
>       if (how && del_timer(&idev->regen_timer))
>               in6_dev_put(idev);
> 
>@@ -3015,7 +3018,6 @@ static int addrconf_ifdown(struct net_device *dev, int 
>how)
>               in6_ifa_put(ifa);
>               write_lock_bh(&idev->lock);
>       }
>-#endif
> 
>       while (!list_empty(&idev->addr_list)) {
>               ifa = list_first_entry(&idev->addr_list,
>@@ -3386,7 +3388,7 @@ static void if6_seq_stop(struct seq_file *seq, void *v)
> static int if6_seq_show(struct seq_file *seq, void *v)
> {
>       struct inet6_ifaddr *ifp = (struct inet6_ifaddr *)v;
>-      seq_printf(seq, "%pi6 %02x %02x %02x %02x %8s\n",
>+      seq_printf(seq, "%pi6 %02x %02x %02x %03x %8s\n",
>                  &ifp->addr,
>                  ifp->idev->dev->ifindex,
>                  ifp->prefix_len,
>@@ -3528,7 +3530,6 @@ restart:
>                                       in6_ifa_put(ifp);
>                                       goto restart;
>                               }
>-#ifdef CONFIG_IPV6_PRIVACY
>                       } else if ((ifp->flags&IFA_F_TEMPORARY) &&
>                                  !(ifp->flags&IFA_F_TENTATIVE)) {
>                               unsigned long regen_advance = 
> ifp->idev->cnf.regen_max_retry *
>@@ -3556,7 +3557,6 @@ restart:
>                               } else if (time_before(ifp->tstamp + 
> ifp->prefered_lft * HZ - regen_advance * HZ, next))
>                                       next = ifp->tstamp + ifp->prefered_lft 
> * HZ - regen_advance * HZ;
>                               spin_unlock(&ifp->lock);
>-#endif
>                       } else {
>                               /* ifp->prefered_lft <= ifp->valid_lft */
>                               if (time_before(ifp->tstamp + ifp->prefered_lft 
> * HZ, next))
>@@ -3609,6 +3609,7 @@ static const struct nla_policy 
>ifa_ipv6_policy[IFA_MAX+1] = {
>       [IFA_ADDRESS]           = { .len = sizeof(struct in6_addr) },
>       [IFA_LOCAL]             = { .len = sizeof(struct in6_addr) },
>       [IFA_CACHEINFO]         = { .len = sizeof(struct ifa_cacheinfo) },
>+      [IFA_FLAGS]             = { .len = sizeof(u32) },
> };
> 
> static int
>@@ -3632,16 +3633,22 @@ inet6_rtm_deladdr(struct sk_buff *skb, struct nlmsghdr 
>*nlh)
>       return inet6_addr_del(net, ifm->ifa_index, pfx, ifm->ifa_prefixlen);
> }
> 
>-static int inet6_addr_modify(struct inet6_ifaddr *ifp, u8 ifa_flags,
>+static int inet6_addr_modify(struct inet6_ifaddr *ifp, u32 ifa_flags,
>                            u32 prefered_lft, u32 valid_lft)
> {
>       u32 flags;
>       clock_t expires;
>       unsigned long timeout;
>+      bool was_managetempaddr;
>+      bool had_prefixroute;
> 
>       if (!valid_lft || (prefered_lft > valid_lft))
>               return -EINVAL;
> 
>+      if (ifa_flags & IFA_F_MANAGETEMPADDR &&
>+          (ifp->flags & IFA_F_TEMPORARY || ifp->prefix_len != 64))
>+              return -EINVAL;
>+
>       timeout = addrconf_timeout_fixup(valid_lft, HZ);
>       if (addrconf_finite_timeout(timeout)) {
>               expires = jiffies_to_clock_t(timeout * HZ);
>@@ -3661,7 +3668,13 @@ static int inet6_addr_modify(struct inet6_ifaddr *ifp, 
>u8 ifa_flags,
>       }
> 
>       spin_lock_bh(&ifp->lock);
>-      ifp->flags = (ifp->flags & ~(IFA_F_DEPRECATED | IFA_F_PERMANENT | 
>IFA_F_NODAD | IFA_F_HOMEADDRESS)) | ifa_flags;
>+      was_managetempaddr = ifp->flags & IFA_F_MANAGETEMPADDR;
>+      had_prefixroute = ifp->flags & IFA_F_PERMANENT &&
>+                        !(ifp->flags & IFA_F_NOPREFIXROUTE);
>+      ifp->flags &= ~(IFA_F_DEPRECATED | IFA_F_PERMANENT | IFA_F_NODAD |
>+                      IFA_F_HOMEADDRESS | IFA_F_MANAGETEMPADDR |
>+                      IFA_F_NOPREFIXROUTE);
>+      ifp->flags |= ifa_flags;
>       ifp->tstamp = jiffies;
>       ifp->valid_lft = valid_lft;
>       ifp->prefered_lft = prefered_lft;
>@@ -3670,8 +3683,30 @@ static int inet6_addr_modify(struct inet6_ifaddr *ifp, 
>u8 ifa_flags,
>       if (!(ifp->flags&IFA_F_TENTATIVE))
>               ipv6_ifa_notify(0, ifp);
> 
>-      addrconf_prefix_route(&ifp->addr, ifp->prefix_len, ifp->idev->dev,
>-                            expires, flags);
>+      if (!(ifa_flags & IFA_F_NOPREFIXROUTE)) {
>+              addrconf_prefix_route(&ifp->addr, ifp->prefix_len, 
>ifp->idev->dev,
>+                                    expires, flags);
>+      } else if (had_prefixroute) {
>+              enum cleanup_prefix_rt_t action;
>+              unsigned long rt_expires;
>+
>+              write_lock_bh(&ifp->idev->lock);
>+              action = check_cleanup_prefix_route(ifp, &rt_expires);
>+              write_unlock_bh(&ifp->idev->lock);
>+
>+              if (action != CLEANUP_PREFIX_RT_NOP) {
>+                      cleanup_prefix_route(ifp, rt_expires,
>+                              action == CLEANUP_PREFIX_RT_DEL);
>+              }
>+      }
>+
>+      if (was_managetempaddr || ifp->flags & IFA_F_MANAGETEMPADDR) {
>+              if (was_managetempaddr && !(ifp->flags & IFA_F_MANAGETEMPADDR))
>+                      valid_lft = prefered_lft = 0;
>+              manage_tempaddrs(ifp->idev, ifp, valid_lft, prefered_lft,
>+                               !was_managetempaddr, jiffies);
>+      }
>+
>       addrconf_verify(0);
> 
>       return 0;
>@@ -3687,7 +3722,7 @@ inet6_rtm_newaddr(struct sk_buff *skb, struct nlmsghdr 
>*nlh)
>       struct inet6_ifaddr *ifa;
>       struct net_device *dev;
>       u32 valid_lft = INFINITY_LIFE_TIME, preferred_lft = INFINITY_LIFE_TIME;
>-      u8 ifa_flags;
>+      u32 ifa_flags;
>       int err;
> 
>       err = nlmsg_parse(nlh, sizeof(*ifm), tb, IFA_MAX, ifa_ipv6_policy);
>@@ -3714,14 +3749,17 @@ inet6_rtm_newaddr(struct sk_buff *skb, struct nlmsghdr 
>*nlh)
>       if (dev == NULL)
>               return -ENODEV;
> 
>+      ifa_flags = tb[IFA_FLAGS] ? nla_get_u32(tb[IFA_FLAGS]) : ifm->ifa_flags;
>+
>       /* We ignore other flags so far. */
>-      ifa_flags = ifm->ifa_flags & (IFA_F_NODAD | IFA_F_HOMEADDRESS);
>+      ifa_flags &= IFA_F_NODAD | IFA_F_HOMEADDRESS | IFA_F_MANAGETEMPADDR |
>+                   IFA_F_NOPREFIXROUTE;
> 
>       ifa = ipv6_get_ifaddr(net, 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.
>+               * userspace already relies on not having to provide this.
>                */
>               return inet6_addr_add(net, ifm->ifa_index, pfx, peer_pfx,
>                                     ifm->ifa_prefixlen, ifa_flags,
>@@ -3739,7 +3777,7 @@ inet6_rtm_newaddr(struct sk_buff *skb, struct nlmsghdr 
>*nlh)
>       return err;
> }
> 
>-static void put_ifaddrmsg(struct nlmsghdr *nlh, u8 prefixlen, u8 flags,
>+static void put_ifaddrmsg(struct nlmsghdr *nlh, u8 prefixlen, u32 flags,
>                         u8 scope, int ifindex)
> {
>       struct ifaddrmsg *ifm;
>@@ -3782,7 +3820,8 @@ static inline int inet6_ifaddr_msgsize(void)
>       return NLMSG_ALIGN(sizeof(struct ifaddrmsg))
>              + nla_total_size(16) /* IFA_LOCAL */
>              + nla_total_size(16) /* IFA_ADDRESS */
>-             + nla_total_size(sizeof(struct ifa_cacheinfo));
>+             + nla_total_size(sizeof(struct ifa_cacheinfo))
>+             + nla_total_size(4)  /* IFA_FLAGS */;
> }
> 
> static int inet6_fill_ifaddr(struct sk_buff *skb, struct inet6_ifaddr *ifa,
>@@ -3830,6 +3869,9 @@ static int inet6_fill_ifaddr(struct sk_buff *skb, struct 
>inet6_ifaddr *ifa,
>       if (put_cacheinfo(skb, ifa->cstamp, ifa->tstamp, preferred, valid) < 0)
>               goto error;
> 
>+      if (nla_put_u32(skb, IFA_FLAGS, ifa->flags) < 0)
>+              goto error;
>+
>       return nlmsg_end(skb, nlh);
> 
> error:
>@@ -4128,13 +4170,11 @@ static inline void ipv6_store_devconf(struct 
>ipv6_devconf *cnf,
>               jiffies_to_msecs(cnf->mldv1_unsolicited_report_interval);
>       array[DEVCONF_MLDV2_UNSOLICITED_REPORT_INTERVAL] =
>               jiffies_to_msecs(cnf->mldv2_unsolicited_report_interval);
>-#ifdef CONFIG_IPV6_PRIVACY
>       array[DEVCONF_USE_TEMPADDR] = cnf->use_tempaddr;
>       array[DEVCONF_TEMP_VALID_LFT] = cnf->temp_valid_lft;
>       array[DEVCONF_TEMP_PREFERED_LFT] = cnf->temp_prefered_lft;
>       array[DEVCONF_REGEN_MAX_RETRY] = cnf->regen_max_retry;
>       array[DEVCONF_MAX_DESYNC_FACTOR] = cnf->max_desync_factor;
>-#endif
>       array[DEVCONF_MAX_ADDRESSES] = cnf->max_addresses;
>       array[DEVCONF_ACCEPT_RA_DEFRTR] = cnf->accept_ra_defrtr;
>       array[DEVCONF_ACCEPT_RA_PINFO] = cnf->accept_ra_pinfo;
>@@ -4828,7 +4868,6 @@ static struct addrconf_sysctl_table
>                       .mode           = 0644,
>                       .proc_handler   = proc_dointvec_ms_jiffies,
>               },
>-#ifdef CONFIG_IPV6_PRIVACY
>               {
>                       .procname       = "use_tempaddr",
>                       .data           = &ipv6_devconf.use_tempaddr,
>@@ -4864,7 +4903,6 @@ static struct addrconf_sysctl_table
>                       .mode           = 0644,
>                       .proc_handler   = proc_dointvec,
>               },
>-#endif
>               {
>                       .procname       = "max_addresses",
>                       .data           = &ipv6_devconf.max_addresses,
_______________________________________________
kernel mailing list
[email protected]
https://admin.fedoraproject.org/mailman/listinfo/kernel

Reply via email to