Josh, should I repost this with the fix or will you apply the fix onto this? Thanks, Jiri Fri, Jan 24, 2014 at 04:44:34PM CET, jpirko@xxxxxxxxxx wrote: >This patch is made against 3.13 kernel. So it's suitable for F21 and for F20 >later on when F20 is rebased to 3.13. > >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/f21_backport_of_IFA_F_NOPREFIXROUTE_and_IFA_F_MANAGETEMPADDR/ > >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/net/addrconf.h | 4 +- > include/net/if_inet6.h | 2 +- > include/uapi/linux/if_addr.h | 6 + > net/ipv6/addrconf.c | 409 +++++++++++++++++++++++++------------------ > 4 files changed, 250 insertions(+), 171 deletions(-) > >Signed-off-by: Jiri Pirko <jpirko@xxxxxxxxxx> > >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 65bb130..9650a3f 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; > >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/addrconf.c b/net/ipv6/addrconf.c >index 4b6b720..3c4e25d 100644 >--- a/net/ipv6/addrconf.c >+++ b/net/ipv6/addrconf.c >@@ -891,15 +891,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; >@@ -913,7 +993,7 @@ 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); >+ write_lock_bh(&ifp->idev->lock); > > if (ifp->flags&IFA_F_TEMPORARY) { > list_del(&ifp->tmp_list); >@@ -924,45 +1004,13 @@ static void ipv6_del_addr(struct inet6_ifaddr *ifp) > __in6_ifa_put(ifp); > } > >- 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 +1018,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 */ >@@ -1024,7 +1040,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); >@@ -1036,7 +1052,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; >@@ -1046,7 +1062,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); >@@ -1072,7 +1088,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, >@@ -1099,7 +1115,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; > } > >@@ -1200,7 +1216,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 >@@ -1407,7 +1423,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; >@@ -1424,7 +1440,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; >@@ -2016,6 +2032,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; >@@ -2170,6 +2253,7 @@ ok: > return; > } > >+ ifp->flags |= IFA_F_MANAGETEMPADDR; > update_lft = 0; > create = 1; > ifp->cstamp = jiffies; >@@ -2178,9 +2262,8 @@ ok: > } > > if (ifp) { >- int flags; >+ u32 flags; > unsigned long now; >- struct inet6_ifaddr *ift; > u32 stored_lft; > > /* update lifetime (RFC2462 5.5.3 e) */ >@@ -2221,70 +2304,8 @@ ok: > } else > spin_unlock(&ifp->lock); > >- 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); >- } >- >- 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); >- } >+ manage_tempaddrs(in6_dev, ifp, valid_lft, prefered_lft, >+ create, now); > > in6_ifa_put(ifp); > addrconf_verify(0); >@@ -2363,10 +2384,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; >@@ -2385,6 +2407,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; >@@ -2417,14 +2442,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; >@@ -2857,7 +2888,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. > */ > >@@ -3366,7 +3397,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, >@@ -3593,6 +3624,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 >@@ -3616,16 +3648,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); >@@ -3645,7 +3683,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; >@@ -3654,8 +3698,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; >@@ -3671,7 +3737,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); >@@ -3698,14 +3764,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, >@@ -3723,7 +3792,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; >@@ -3766,7 +3835,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, >@@ -3815,6 +3885,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: _______________________________________________ kernel mailing list kernel@xxxxxxxxxxxxxxxxxxxxxxx https://admin.fedoraproject.org/mailman/listinfo/kernel