[PATCH F20/F21 (3.13)] ipv6: introduce IFA_F_NOPREFIXROUTE and IFA_F_MANAGETEMPADDR flags
Jiri Pirko
jpirko at redhat.com
Wed Jan 29 13:46:45 UTC 2014
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 at redhat.com 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 at redhat.com>
>
>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:
More information about the kernel
mailing list