]> www.infradead.org Git - users/dwmw2/linux.git/commitdiff
ipv6: Move lifetime validation to inet6_rtm_newaddr().
authorKuniyuki Iwashima <kuniyu@amazon.com>
Wed, 15 Jan 2025 08:06:06 +0000 (17:06 +0900)
committerJakub Kicinski <kuba@kernel.org>
Mon, 20 Jan 2025 20:16:05 +0000 (12:16 -0800)
inet6_addr_add() and inet6_addr_modify() have the same code to validate
IPv6 lifetime that is done under RTNL.

Let's factorise it out to inet6_rtm_newaddr() so that we can validate
the lifetime without RTNL later.

Note that inet6_addr_add() is called from addrconf_add_ifaddr(), but the
lifetime is INFINITY_LIFE_TIME in the path, so expires and flags are 0.

Signed-off-by: Kuniyuki Iwashima <kuniyu@amazon.com>
Link: https://patch.msgid.link/20250115080608.28127-10-kuniyu@amazon.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
net/ipv6/addrconf.c

index 9720ff17f0a1e838a2b0b0bfe5b94e647ebb5050..9ae25a8d163291a8d7ae2ce49dc8bcf0296e4ddd 100644 (file)
@@ -3008,14 +3008,11 @@ static int ipv6_mc_config(struct sock *sk, bool join,
  *     Manual configuration of address on an interface
  */
 static int inet6_addr_add(struct net *net, struct net_device *dev,
-                         struct ifa6_config *cfg,
+                         struct ifa6_config *cfg, clock_t expires, u32 flags,
                          struct netlink_ext_ack *extack)
 {
        struct inet6_ifaddr *ifp;
        struct inet6_dev *idev;
-       unsigned long timeout;
-       clock_t expires;
-       u32 flags;
 
        ASSERT_RTNL();
 
@@ -3024,12 +3021,6 @@ static int inet6_addr_add(struct net *net, struct net_device *dev,
                return -EINVAL;
        }
 
-       /* check the lifetime */
-       if (!cfg->valid_lft || cfg->preferred_lft > cfg->valid_lft) {
-               NL_SET_ERR_MSG_MOD(extack, "address lifetime invalid");
-               return -EINVAL;
-       }
-
        if (cfg->ifa_flags & IFA_F_MANAGETEMPADDR && cfg->plen != 64) {
                NL_SET_ERR_MSG_MOD(extack, "address with \"mngtmpaddr\" flag must have a prefix length of 64");
                return -EINVAL;
@@ -3053,24 +3044,6 @@ static int inet6_addr_add(struct net *net, struct net_device *dev,
 
        cfg->scope = ipv6_addr_scope(cfg->pfx);
 
-       timeout = addrconf_timeout_fixup(cfg->valid_lft, HZ);
-       if (addrconf_finite_timeout(timeout)) {
-               expires = jiffies_to_clock_t(timeout * HZ);
-               cfg->valid_lft = timeout;
-               flags = RTF_EXPIRES;
-       } else {
-               expires = 0;
-               flags = 0;
-               cfg->ifa_flags |= IFA_F_PERMANENT;
-       }
-
-       timeout = addrconf_timeout_fixup(cfg->preferred_lft, HZ);
-       if (addrconf_finite_timeout(timeout)) {
-               if (timeout == 0)
-                       cfg->ifa_flags |= IFA_F_DEPRECATED;
-               cfg->preferred_lft = timeout;
-       }
-
        ifp = ipv6_add_addr(idev, cfg, true, extack);
        if (!IS_ERR(ifp)) {
                if (!(cfg->ifa_flags & IFA_F_NOPREFIXROUTE)) {
@@ -3180,7 +3153,7 @@ int addrconf_add_ifaddr(struct net *net, void __user *arg)
        rtnl_net_lock(net);
        dev = __dev_get_by_index(net, ireq.ifr6_ifindex);
        if (dev)
-               err = inet6_addr_add(net, dev, &cfg, NULL);
+               err = inet6_addr_add(net, dev, &cfg, 0, 0, NULL);
        else
                err = -ENODEV;
        rtnl_net_unlock(net);
@@ -4869,20 +4842,15 @@ static int modify_prefix_route(struct net *net, struct inet6_ifaddr *ifp,
 }
 
 static int inet6_addr_modify(struct net *net, struct inet6_ifaddr *ifp,
-                            struct ifa6_config *cfg)
+                            struct ifa6_config *cfg, clock_t expires,
+                            u32 flags)
 {
-       u32 flags;
-       clock_t expires;
-       unsigned long timeout;
        bool was_managetempaddr;
-       bool had_prefixroute;
        bool new_peer = false;
+       bool had_prefixroute;
 
        ASSERT_RTNL();
 
-       if (!cfg->valid_lft || cfg->preferred_lft > cfg->valid_lft)
-               return -EINVAL;
-
        if (cfg->ifa_flags & IFA_F_MANAGETEMPADDR &&
            (ifp->flags & IFA_F_TEMPORARY || ifp->prefix_len != 64))
                return -EINVAL;
@@ -4890,24 +4858,6 @@ static int inet6_addr_modify(struct net *net, struct inet6_ifaddr *ifp,
        if (!(ifp->flags & IFA_F_TENTATIVE) || ifp->flags & IFA_F_DADFAILED)
                cfg->ifa_flags &= ~IFA_F_OPTIMISTIC;
 
-       timeout = addrconf_timeout_fixup(cfg->valid_lft, HZ);
-       if (addrconf_finite_timeout(timeout)) {
-               expires = jiffies_to_clock_t(timeout * HZ);
-               cfg->valid_lft = timeout;
-               flags = RTF_EXPIRES;
-       } else {
-               expires = 0;
-               flags = 0;
-               cfg->ifa_flags |= IFA_F_PERMANENT;
-       }
-
-       timeout = addrconf_timeout_fixup(cfg->preferred_lft, HZ);
-       if (addrconf_finite_timeout(timeout)) {
-               if (timeout == 0)
-                       cfg->ifa_flags |= IFA_F_DEPRECATED;
-               cfg->preferred_lft = timeout;
-       }
-
        if (cfg->peer_pfx &&
            memcmp(&ifp->peer_addr, cfg->peer_pfx, sizeof(struct in6_addr))) {
                if (!ipv6_addr_any(&ifp->peer_addr))
@@ -4992,13 +4942,16 @@ inet6_rtm_newaddr(struct sk_buff *skb, struct nlmsghdr *nlh,
                  struct netlink_ext_ack *extack)
 {
        struct net *net = sock_net(skb->sk);
-       struct ifaddrmsg *ifm;
        struct nlattr *tb[IFA_MAX+1];
        struct in6_addr *peer_pfx;
        struct inet6_ifaddr *ifa;
        struct net_device *dev;
        struct inet6_dev *idev;
        struct ifa6_config cfg;
+       struct ifaddrmsg *ifm;
+       unsigned long timeout;
+       clock_t expires;
+       u32 flags;
        int err;
 
        err = nlmsg_parse_deprecated(nlh, sizeof(*ifm), tb, IFA_MAX,
@@ -5028,8 +4981,11 @@ inet6_rtm_newaddr(struct sk_buff *skb, struct nlmsghdr *nlh,
                         IFA_F_MANAGETEMPADDR | IFA_F_NOPREFIXROUTE |
                         IFA_F_MCAUTOJOIN | IFA_F_OPTIMISTIC;
 
+       cfg.ifa_flags |= IFA_F_PERMANENT;
        cfg.valid_lft = INFINITY_LIFE_TIME;
        cfg.preferred_lft = INFINITY_LIFE_TIME;
+       expires = 0;
+       flags = 0;
 
        if (tb[IFA_CACHEINFO]) {
                struct ifa_cacheinfo *ci;
@@ -5037,6 +4993,27 @@ inet6_rtm_newaddr(struct sk_buff *skb, struct nlmsghdr *nlh,
                ci = nla_data(tb[IFA_CACHEINFO]);
                cfg.valid_lft = ci->ifa_valid;
                cfg.preferred_lft = ci->ifa_prefered;
+
+               if (!cfg.valid_lft || cfg.preferred_lft > cfg.valid_lft) {
+                       NL_SET_ERR_MSG_MOD(extack, "address lifetime invalid");
+                       return -EINVAL;
+               }
+
+               timeout = addrconf_timeout_fixup(cfg.valid_lft, HZ);
+               if (addrconf_finite_timeout(timeout)) {
+                       cfg.ifa_flags &= ~IFA_F_PERMANENT;
+                       cfg.valid_lft = timeout;
+                       expires = jiffies_to_clock_t(timeout * HZ);
+                       flags = RTF_EXPIRES;
+               }
+
+               timeout = addrconf_timeout_fixup(cfg.preferred_lft, HZ);
+               if (addrconf_finite_timeout(timeout)) {
+                       if (timeout == 0)
+                               cfg.ifa_flags |= IFA_F_DEPRECATED;
+
+                       cfg.preferred_lft = timeout;
+               }
        }
 
        dev =  __dev_get_by_index(net, ifm->ifa_index);
@@ -5064,7 +5041,7 @@ inet6_rtm_newaddr(struct sk_buff *skb, struct nlmsghdr *nlh,
                 * It would be best to check for !NLM_F_CREATE here but
                 * userspace already relies on not having to provide this.
                 */
-               return inet6_addr_add(net, dev, &cfg, extack);
+               return inet6_addr_add(net, dev, &cfg, expires, flags, extack);
        }
 
        if (nlh->nlmsg_flags & NLM_F_EXCL ||
@@ -5072,7 +5049,7 @@ inet6_rtm_newaddr(struct sk_buff *skb, struct nlmsghdr *nlh,
                NL_SET_ERR_MSG_MOD(extack, "address already assigned");
                err = -EEXIST;
        } else {
-               err = inet6_addr_modify(net, ifa, &cfg);
+               err = inet6_addr_modify(net, ifa, &cfg, expires, flags);
        }
 
        in6_ifa_put(ifa);