inet6: emit RTM_NEWADDR messages on address flag changes.
authorRoy Marples <roy@marples.name>
Fri, 16 Aug 2019 16:54:35 +0000 (17:54 +0100)
committerRoy Marples <roy@marples.name>
Fri, 16 Aug 2019 16:54:35 +0000 (17:54 +0100)
We no longer emit RTM_NEWADDR right away when adding a new address
and starting DAD. Instead the result of the DAD will be emitted.

Some minor fixes from NetBSD are also brought in, such as
starting DAD when addresses are no longer detached and the
tentative state not being added when address lifetime is
extended.

Taken-from: NetBSD
Reviewed-by: sephe
sys/net/route.c
sys/netinet6/in6.c
sys/netinet6/in6_var.h
sys/netinet6/nd6.c
sys/netinet6/nd6_nbr.c
sys/netinet6/nd6_rtr.c

index a276988..fbce93f 100644 (file)
@@ -1002,6 +1002,19 @@ makeroute:
                        *ret_nrt = rt;
                }
                break;
+       case RTM_GET:
+               /* Get the item from the tree. */
+               rn = rnh->rnh_lookup((char *)rtinfo->rti_info[RTAX_DST],
+                                    (char *)rtinfo->rti_info[RTAX_NETMASK],
+                                     rnh);
+               if (rn == NULL)
+                       gotoerr(ESRCH);
+               if (ret_nrt != NULL) {
+                       rt = (struct rtentry *)rn;
+                       rt->rt_refcnt++;
+                       *ret_nrt = rt;
+               }
+               break;
        default:
                error = EOPNOTSUPP;
        }
index 08c1f16..54da554 100644 (file)
@@ -146,7 +146,8 @@ struct in6_multihead in6_multihead; /* XXX BSS initialization */
  * This routine does actual work.
  */
 static void
-in6_ifloop_request(int cmd, struct ifaddr *ifa)
+in6_ifloop_request(int cmd, struct ifaddr *ifa,
+    void (*callback)(int, int, struct rt_addrinfo *, struct rtentry *, void *))
 {
        struct sockaddr_in6 all1_sa;
         struct rt_addrinfo rtinfo;
@@ -171,12 +172,11 @@ in6_ifloop_request(int cmd, struct ifaddr *ifa)
        rtinfo.rti_info[RTAX_NETMASK] = (struct sockaddr *)&all1_sa;
        rtinfo.rti_flags = RTF_UP|RTF_HOST|RTF_LLINFO;
 
-       error = rtrequest1_global(cmd, &rtinfo,
-           in6_ifloop_request_callback, ifa, RTREQ_PRIO_NORM);
+       error = rtrequest1_global(cmd, &rtinfo, callback, ifa, RTREQ_PRIO_NORM);
        if (error != 0) {
                log(LOG_ERR, "in6_ifloop_request: "
                    "%s operation failed for %s (errno=%d)\n",
-                   cmd == RTM_ADD ? "ADD" : "DELETE",
+                   cmd == RTM_ADD ? "ADD" : cmd == RTM_DELETE ? "DELETE" : "GET",
                    ip6_sprintf(&((struct in6_ifaddr *)ifa)->ia_addr.sin6_addr),
                    error);
        }
@@ -207,14 +207,21 @@ in6_ifloop_request_callback(int cmd, int error, struct rt_addrinfo *rtinfo,
        }
 
        /*
-        * Report the addition/removal of the address to the routing socket.
+        * Report the addition/removal of the address to the routing socket,
+        * unless the address is marked as tentative, where it will be reported
+        * once DAD completes.
         * XXX: since we called rtinit for a p2p interface with a destination,
         *      we end up reporting twice in such a case.  Should we rather
         *      omit the second report?
         */
        if (rt) {
-               if (mycpuid == 0)
-                       rt_newaddrmsg(cmd, ifa, error, rt);
+               if (mycpuid == 0) {
+                       struct in6_ifaddr *ia6 = (struct in6_ifaddr *)ifa;
+
+                       if (cmd != RTM_ADD ||
+                           !(ia6->ia6_flags & IN6_IFF_TENTATIVE))
+                               rt_newaddrmsg(cmd, ifa, error, rt);
+               }
                if (cmd == RTM_DELETE) {
                        if (rt->rt_refcnt == 0) {
                                ++rt->rt_refcnt;
@@ -227,6 +234,22 @@ done:
        ;
 }
 
+static void
+in6_newaddrmsg_callback(int cmd, int error, struct rt_addrinfo *rtinfo,
+                       struct rtentry *rt, void *arg)
+{
+       struct ifaddr *ifa = arg;
+
+       if (error == 0 && rt != NULL && mycpuid == 0)
+               rt_newaddrmsg(RTM_ADD, ifa, error, rt);
+}
+
+void
+in6_newaddrmsg(struct ifaddr *ifa)
+{
+       in6_ifloop_request(RTM_GET, ifa, in6_newaddrmsg_callback);
+}
+
 /*
  * Add ownaddr as loopback rtentry.  We previously add the route only if
  * necessary (ex. on a p2p link).  However, since we now manage addresses
@@ -243,7 +266,7 @@ in6_ifaddloop(struct ifaddr *ifa)
        rt = rtpurelookup(ifa->ifa_addr);
        if (rt == NULL || !(rt->rt_flags & RTF_HOST) ||
            !(rt->rt_ifp->if_flags & IFF_LOOPBACK))
-               in6_ifloop_request(RTM_ADD, ifa);
+               in6_ifloop_request(RTM_ADD, ifa, in6_ifloop_request_callback);
        if (rt != NULL)
                rt->rt_refcnt--;
 }
@@ -296,7 +319,8 @@ in6_ifremloop(struct ifaddr *ifa)
                if (rt != NULL && (rt->rt_flags & RTF_HOST) &&
                    (rt->rt_ifp->if_flags & IFF_LOOPBACK)) {
                        rt->rt_refcnt--;
-                       in6_ifloop_request(RTM_DELETE, ifa);
+                       in6_ifloop_request(RTM_DELETE, ifa,
+                                          in6_ifloop_request_callback);
                }
        }
 }
@@ -851,7 +875,7 @@ int
 in6_update_ifa(struct ifnet *ifp, struct in6_aliasreq *ifra,
               struct in6_ifaddr *ia)
 {
-       int error = 0, hostIsNew = 0, plen = -1;
+       int error = 0, hostIsNew = 0, was_tentative, plen = -1;
        struct in6_ifaddr *oia;
        struct sockaddr_in6 dst6;
        struct in6_addrlifetime *lt;
@@ -1041,6 +1065,28 @@ in6_update_ifa(struct ifnet *ifp, struct in6_aliasreq *ifra,
                ia->ia_dstaddr = dst6;
        }
 
+       was_tentative = ia->ia6_flags & (IN6_IFF_TENTATIVE|IN6_IFF_DUPLICATED);
+       ia->ia6_flags = ifra->ifra_flags;
+       ia->ia6_flags &= ~IN6_IFF_DUPLICATED;   /*safety*/
+       ia->ia6_flags &= ~IN6_IFF_NODAD;        /* Mobile IPv6 */
+       if ((hostIsNew || was_tentative) &&
+           in6if_do_dad(ifp) &&
+           !(ifra->ifra_flags & IN6_IFF_NODAD))
+               ia->ia6_flags |= IN6_IFF_TENTATIVE;
+
+       ia->ia6_lifetime = ifra->ifra_lifetime;
+       /* for sanity */
+       if (ia->ia6_lifetime.ia6t_vltime != ND6_INFINITE_LIFETIME) {
+               ia->ia6_lifetime.ia6t_expire =
+                       time_uptime + ia->ia6_lifetime.ia6t_vltime;
+       } else
+               ia->ia6_lifetime.ia6t_expire = 0;
+       if (ia->ia6_lifetime.ia6t_pltime != ND6_INFINITE_LIFETIME) {
+               ia->ia6_lifetime.ia6t_preferred =
+                       time_uptime + ia->ia6_lifetime.ia6t_pltime;
+       } else
+               ia->ia6_lifetime.ia6t_preferred = 0;
+
        /* reset the interface and routing table appropriately. */
        if ((error = in6_ifinit(ifp, ia, &ifra->ifra_addr, hostIsNew)) != 0)
                goto unlink;
@@ -1163,32 +1209,15 @@ in6_update_ifa(struct ifnet *ifp, struct in6_aliasreq *ifra,
                }
        }
 
-       ia->ia6_flags = ifra->ifra_flags;
-       ia->ia6_flags &= ~IN6_IFF_DUPLICATED;   /*safety*/
-       ia->ia6_flags &= ~IN6_IFF_NODAD;        /* Mobile IPv6 */
-
-       ia->ia6_lifetime = ifra->ifra_lifetime;
-       /* for sanity */
-       if (ia->ia6_lifetime.ia6t_vltime != ND6_INFINITE_LIFETIME) {
-               ia->ia6_lifetime.ia6t_expire =
-                       time_uptime + ia->ia6_lifetime.ia6t_vltime;
-       } else
-               ia->ia6_lifetime.ia6t_expire = 0;
-       if (ia->ia6_lifetime.ia6t_pltime != ND6_INFINITE_LIFETIME) {
-               ia->ia6_lifetime.ia6t_preferred =
-                       time_uptime + ia->ia6_lifetime.ia6t_pltime;
-       } else
-               ia->ia6_lifetime.ia6t_preferred = 0;
-
        /*
         * Perform DAD, if needed.
         * XXX It may be of use, if we can administratively
         * disable DAD.
         */
-       if (in6if_do_dad(ifp) && !(ifra->ifra_flags & IN6_IFF_NODAD)) {
-               ia->ia6_flags |= IN6_IFF_TENTATIVE;
+       if (in6if_do_dad(ifp) &&
+           !(ifra->ifra_flags & IN6_IFF_NODAD) &&
+           ia->ia6_flags & IN6_IFF_TENTATIVE)
                nd6_dad_start((struct ifaddr *)ia, NULL);
-       }
 
        return (error);
 
index fbaf656..3971d13 100644 (file)
@@ -624,6 +624,7 @@ int in6_addr2zoneid(struct ifnet *, struct in6_addr *, u_int32_t *);
 int    in6_matchlen (struct in6_addr *, struct in6_addr *);
 int    in6_are_prefix_equal(struct in6_addr *p1, struct in6_addr *p2, int len);
 void   in6_prefixlen2mask(struct in6_addr *maskp, int len);
+void   in6_newaddrmsg(struct ifaddr *);
 void   in6_ifremloop(struct ifaddr *);
 void   in6_ifaddloop(struct ifaddr *);
 
index 7143793..fbf2d52 100644 (file)
@@ -595,7 +595,10 @@ addrloop:
                if (IFA6_IS_DEPRECATED(ia6)) {
                        int oldflags = ia6->ia6_flags;
 
-                       ia6->ia6_flags |= IN6_IFF_DEPRECATED;
+                       if ((oldflags & IN6_IFF_DEPRECATED) == 0) {
+                               ia6->ia6_flags |= IN6_IFF_DEPRECATED;
+                               in6_newaddrmsg((struct ifaddr *)ia6);
+                       }
 
                        /*
                         * If a temporary address has just become deprecated,
@@ -626,7 +629,10 @@ addrloop:
                         * A new RA might have made a deprecated address
                         * preferred.
                         */
-                       ia6->ia6_flags &= ~IN6_IFF_DEPRECATED;
+                       if (ia6->ia6_flags & IN6_IFF_DEPRECATED) {
+                               ia6->ia6_flags &= ~IN6_IFF_DEPRECATED;
+                               in6_newaddrmsg((struct ifaddr *)ia6);
+                       }
                }
        }
 
index 16fcb0f..f3d3030 100644 (file)
@@ -1107,12 +1107,9 @@ nd6_dad_start(struct ifaddr *ifa,
                        ifa->ifa_ifp ? if_name(ifa->ifa_ifp) : "???");
                return;
        }
-       if (ia->ia6_flags & IN6_IFF_ANYCAST) {
-               ia->ia6_flags &= ~IN6_IFF_TENTATIVE;
-               return;
-       }
-       if (!ip6_dad_count) {
+       if (ia->ia6_flags & IN6_IFF_ANYCAST || !ip6_dad_count) {
                ia->ia6_flags &= ~IN6_IFF_TENTATIVE;
+               in6_newaddrmsg(ifa);
                return;
        }
        if (!ifa->ifa_ifp)
@@ -1331,6 +1328,7 @@ nd6_dad_timer_handler(netmsg_t msg)
                         * duplicated address found.
                         */
                        ia->ia6_flags &= ~IN6_IFF_TENTATIVE;
+                       in6_newaddrmsg(ifa);
                        nd6log((LOG_DEBUG,
                            "%s: DAD complete for %s - no duplicates found\n",
                            if_name(ifa->ifa_ifp),
@@ -1367,6 +1365,7 @@ nd6_dad_duplicated(struct ifaddr *ifa)
 
        ia->ia6_flags &= ~IN6_IFF_TENTATIVE;
        ia->ia6_flags |= IN6_IFF_DUPLICATED;
+       in6_newaddrmsg(ifa);
 
        log(LOG_ERR, "%s: DAD complete for %s - duplicate found\n",
            if_name(ifa->ifa_ifp), ip6_sprintf(&ia->ia_addr.sin6_addr));
index e24b01d..138b7ef 100644 (file)
@@ -1296,10 +1296,18 @@ pfxlist_onlink_check(void)
                        if (ifa->ia6_ndpr == NULL) /* XXX: see above. */
                                continue;
 
-                       if (find_pfxlist_reachable_router(ifa->ia6_ndpr))
-                               ifa->ia6_flags &= ~IN6_IFF_DETACHED;
-                       else
-                               ifa->ia6_flags |= IN6_IFF_DETACHED;
+                       if (find_pfxlist_reachable_router(ifa->ia6_ndpr)) {
+                               if (ifa->ia6_flags & IN6_IFF_DETACHED) {
+                                       ifa->ia6_flags &= ~IN6_IFF_DETACHED;
+                                       ifa->ia6_flags |= IN6_IFF_TENTATIVE;
+                                       nd6_dad_start((struct ifaddr *)ifa, 0);
+                               }
+                       } else {
+                               if ((ifa->ia6_flags & IN6_IFF_DETACHED) == 0) {
+                                       ifa->ia6_flags |= IN6_IFF_DETACHED;
+                                       in6_newaddrmsg((struct ifaddr *)ifa);
+                               }
+                       }
                }
        }
        else {
@@ -1308,6 +1316,8 @@ pfxlist_onlink_check(void)
                                continue;
 
                        ifa->ia6_flags &= ~IN6_IFF_DETACHED;
+                       ifa->ia6_flags |= IN6_IFF_TENTATIVE;
+                       nd6_dad_start((struct ifaddr *)ifa, 0);
                }
        }
 }