On Wed, May 02, 2018 at 04:25:20PM +0200, Hrvoje Popovski wrote:
> On 2.5.2018. 12:16, Theo Buehler wrote:
> > Here's a further refactoring of in6_ioctl() that splits out the
> > SIOCGIF*_IN6 cases into the new function in6_ioctl_get(), where we only
> > need a read lock, similar to ifioctl() and the pending patch for
> > in_ioctl(). We get rid of one of the four switches in the function body
> > and error out early on on unknown ioctls before grabbing a lock instead
> > of doing nothing until the end of the function.
> > 
> > After grabbing the lock in the body of in6_ioctl(), we now only deal
> > with SIOCAIFADDR_IN6 and SIOCDIFADDR_IN6. This will need more cleanup
> > and streamlining in a later step.
> > 
> > I didn't really know what to do with the big comment. I left it
> > essentially where it was. Suggestions welcome.
> 
> Hi,
> 
> i'm testing this diff on top on -current tree fetched hour ago. i'm
> forwarding ip6 traffic over vlan on trunk and doing ip6 client server
> with iperf3 while destroying and recreating pseudo interfaces
> 
> for now everything seems stable
> 

Thanks, that's good to know.  However, I overlooked a shadowing issue.
There was a local re-declaration of error, making the function succeed
more often than it should. This additional piece is needed:

                switch(cmd) {
                case SIOCAIFADDR_IN6:
                {
        -               int plen, error = 0, newifaddr = 0;
        +               int plen, newifaddr = 0;

This is the only change to the previous submission.

Index: sys/netinet6/in6.c
===================================================================
RCS file: /var/cvs/src/sys/netinet6/in6.c,v
retrieving revision 1.223
diff -u -p -r1.223 in6.c
--- sys/netinet6/in6.c  2 May 2018 07:19:45 -0000       1.223
+++ sys/netinet6/in6.c  2 May 2018 23:10:05 -0000
@@ -116,6 +116,7 @@ const struct in6_addr in6mask96 = IN6MAS
 const struct in6_addr in6mask128 = IN6MASK128;
 
 int in6_ioctl(u_long, caddr_t, struct ifnet *, int);
+int in6_ioctl_get(u_long, caddr_t, struct ifnet *);
 int in6_ifinit(struct ifnet *, struct in6_ifaddr *, int);
 void in6_unlink_ifa(struct in6_ifaddr *, struct ifnet *);
 
@@ -218,29 +219,15 @@ in6_ioctl(u_long cmd, caddr_t data, stru
        case SIOCGIFINFO_IN6:
        case SIOCGNBRINFO_IN6:
                return (nd6_ioctl(cmd, data, ifp));
-       }
-
-       /*
-        * Find address for this interface, if it exists.
-        *
-        * In netinet code, we have checked ifra_addr in SIOCSIF*ADDR operation
-        * only, and used the first interface address as the target of other
-        * operations (without checking ifra_addr).  This was because netinet
-        * code/API assumed at most 1 interface address per interface.
-        * Since IPv6 allows a node to assign multiple addresses
-        * on a single interface, we almost always look and check the
-        * presence of ifra_addr, and reject invalid ones here.
-        * It also decreases duplicated code among SIOC*_IN6 operations.
-        */
-       switch (cmd) {
-       case SIOCAIFADDR_IN6:
-               sa6 = &ifra->ifra_addr;
-               break;
        case SIOCGIFDSTADDR_IN6:
        case SIOCGIFNETMASK_IN6:
-       case SIOCDIFADDR_IN6:
        case SIOCGIFAFLAG_IN6:
        case SIOCGIFALIFETIME_IN6:
+               return (in6_ioctl_get(cmd, data, ifp));
+       case SIOCAIFADDR_IN6:
+               sa6 = &ifra->ifra_addr;
+               break;
+       case SIOCDIFADDR_IN6:
                sa6 = &ifr->ifr_addr;
                break;
        case SIOCSIFADDR:
@@ -253,10 +240,22 @@ in6_ioctl(u_long cmd, caddr_t data, stru
                 */
                return (EINVAL);
        default:
-               sa6 = NULL;
-               break;
+               return (EOPNOTSUPP);
        }
 
+       /*
+        * Find address for this interface, if it exists.
+        *
+        * In netinet code, we have checked ifra_addr in SIOCSIF*ADDR operation
+        * only, and used the first interface address as the target of other
+        * operations (without checking ifra_addr).  This was because netinet
+        * code/API assumed at most 1 interface address per interface.
+        * Since IPv6 allows a node to assign multiple addresses
+        * on a single interface, we almost always look and check the
+        * presence of ifra_addr, and reject invalid ones here.
+        * It also decreases duplicated code among SIOC*_IN6 operations.
+        */
+
        NET_LOCK();
 
        if (sa6 && sa6->sin6_family == AF_INET6) {
@@ -312,93 +311,12 @@ in6_ioctl(u_long cmd, caddr_t data, stru
                        goto err;
                }
                break;
-
-       case SIOCGIFAFLAG_IN6:
-       case SIOCGIFNETMASK_IN6:
-       case SIOCGIFDSTADDR_IN6:
-       case SIOCGIFALIFETIME_IN6:
-               /* must think again about its semantics */
-               if (ia6 == NULL) {
-                       error = EADDRNOTAVAIL;
-                       goto err;
-               }
-               break;
        }
 
        switch (cmd) {
-       case SIOCGIFDSTADDR_IN6:
-               if ((ifp->if_flags & IFF_POINTOPOINT) == 0) {
-                       error = EINVAL;
-                       break;
-               }
-               /*
-                * XXX: should we check if ifa_dstaddr is NULL and return
-                * an error?
-                */
-               ifr->ifr_dstaddr = ia6->ia_dstaddr;
-               break;
-
-       case SIOCGIFNETMASK_IN6:
-               ifr->ifr_addr = ia6->ia_prefixmask;
-               break;
-
-       case SIOCGIFAFLAG_IN6:
-               ifr->ifr_ifru.ifru_flags6 = ia6->ia6_flags;
-               break;
-
-       case SIOCGIFALIFETIME_IN6:
-               ifr->ifr_ifru.ifru_lifetime = ia6->ia6_lifetime;
-               if (ia6->ia6_lifetime.ia6t_vltime != ND6_INFINITE_LIFETIME) {
-                       time_t expire, maxexpire;
-                       struct in6_addrlifetime *retlt =
-                           &ifr->ifr_ifru.ifru_lifetime;
-
-                       /*
-                        * XXX: adjust expiration time assuming time_t is
-                        * signed.
-                        */
-                       maxexpire =
-                           (time_t)~(1ULL << ((sizeof(maxexpire) * 8) - 1));
-                       if (ia6->ia6_lifetime.ia6t_vltime <
-                           maxexpire - ia6->ia6_updatetime) {
-                               expire = ia6->ia6_updatetime +
-                                   ia6->ia6_lifetime.ia6t_vltime;
-                               if (expire != 0) {
-                                       expire -= time_uptime;
-                                       expire += time_second;
-                               }
-                               retlt->ia6t_expire = expire;
-                       } else
-                               retlt->ia6t_expire = maxexpire;
-               }
-               if (ia6->ia6_lifetime.ia6t_pltime != ND6_INFINITE_LIFETIME) {
-                       time_t expire, maxexpire;
-                       struct in6_addrlifetime *retlt =
-                           &ifr->ifr_ifru.ifru_lifetime;
-
-                       /*
-                        * XXX: adjust expiration time assuming time_t is
-                        * signed.
-                        */
-                       maxexpire =
-                           (time_t)~(1ULL << ((sizeof(maxexpire) * 8) - 1));
-                       if (ia6->ia6_lifetime.ia6t_pltime <
-                           maxexpire - ia6->ia6_updatetime) {
-                               expire = ia6->ia6_updatetime +
-                                   ia6->ia6_lifetime.ia6t_pltime;
-                               if (expire != 0) {
-                                       expire -= time_uptime;
-                                       expire += time_second;
-                               }
-                               retlt->ia6t_preferred = expire;
-                       } else
-                               retlt->ia6t_preferred = maxexpire;
-               }
-               break;
-
        case SIOCAIFADDR_IN6:
        {
-               int plen, error = 0, newifaddr = 0;
+               int plen, newifaddr = 0;
 
                /* reject read-only flags */
                if ((ifra->ifra_flags & IN6_IFF_DUPLICATED) != 0 ||
@@ -469,14 +387,131 @@ in6_ioctl(u_long cmd, caddr_t data, stru
                in6_purgeaddr(&ia6->ia_ifa);
                dohooks(ifp->if_addrhooks, 0);
                break;
+       }
 
-       default:
-               error = EOPNOTSUPP;
+err:
+       NET_UNLOCK();
+       return (error);
+}
+
+int
+in6_ioctl_get(u_long cmd, caddr_t data, struct ifnet *ifp)
+{
+       struct  in6_ifreq *ifr = (struct in6_ifreq *)data;
+       struct  in6_ifaddr *ia6 = NULL;
+       struct sockaddr_in6 *sa6;
+       int     error = 0;
+
+       sa6 = &ifr->ifr_addr;
+
+       NET_RLOCK();
+
+       if (sa6 && sa6->sin6_family == AF_INET6) {
+               if (IN6_IS_ADDR_LINKLOCAL(&sa6->sin6_addr)) {
+                       if (sa6->sin6_addr.s6_addr16[1] == 0) {
+                               /* link ID is not embedded by the user */
+                               sa6->sin6_addr.s6_addr16[1] =
+                                   htons(ifp->if_index);
+                       } else if (sa6->sin6_addr.s6_addr16[1] !=
+                           htons(ifp->if_index)) {
+                               error = EINVAL; /* link ID contradicts */
+                               goto err;
+                       }
+                       if (sa6->sin6_scope_id) {
+                               if (sa6->sin6_scope_id !=
+                                   (u_int32_t)ifp->if_index) {
+                                       error = EINVAL;
+                                       goto err;
+                               }
+                               sa6->sin6_scope_id = 0; /* XXX: good way? */
+                       }
+               }
+               ia6 = in6ifa_ifpwithaddr(ifp, &sa6->sin6_addr);
+       }
+
+       /* must think again about its semantics */
+       if (ia6 == NULL) {
+               error = EADDRNOTAVAIL;
+               goto err;
+       }
+
+       switch(cmd) {
+       case SIOCGIFDSTADDR_IN6:
+               if ((ifp->if_flags & IFF_POINTOPOINT) == 0) {
+                       error = EINVAL;
+                       break;
+               }
+               /*
+                * XXX: should we check if ifa_dstaddr is NULL and return
+                * an error?
+                */
+               ifr->ifr_dstaddr = ia6->ia_dstaddr;
+               break;
+
+       case SIOCGIFNETMASK_IN6:
+               ifr->ifr_addr = ia6->ia_prefixmask;
                break;
+
+       case SIOCGIFAFLAG_IN6:
+               ifr->ifr_ifru.ifru_flags6 = ia6->ia6_flags;
+               break;
+
+       case SIOCGIFALIFETIME_IN6:
+               ifr->ifr_ifru.ifru_lifetime = ia6->ia6_lifetime;
+               if (ia6->ia6_lifetime.ia6t_vltime != ND6_INFINITE_LIFETIME) {
+                       time_t expire, maxexpire;
+                       struct in6_addrlifetime *retlt =
+                           &ifr->ifr_ifru.ifru_lifetime;
+
+                       /*
+                        * XXX: adjust expiration time assuming time_t is
+                        * signed.
+                        */
+                       maxexpire =
+                           (time_t)~(1ULL << ((sizeof(maxexpire) * 8) - 1));
+                       if (ia6->ia6_lifetime.ia6t_vltime <
+                           maxexpire - ia6->ia6_updatetime) {
+                               expire = ia6->ia6_updatetime +
+                                   ia6->ia6_lifetime.ia6t_vltime;
+                               if (expire != 0) {
+                                       expire -= time_uptime;
+                                       expire += time_second;
+                               }
+                               retlt->ia6t_expire = expire;
+                       } else
+                               retlt->ia6t_expire = maxexpire;
+               }
+               if (ia6->ia6_lifetime.ia6t_pltime != ND6_INFINITE_LIFETIME) {
+                       time_t expire, maxexpire;
+                       struct in6_addrlifetime *retlt =
+                           &ifr->ifr_ifru.ifru_lifetime;
+
+                       /*
+                        * XXX: adjust expiration time assuming time_t is
+                        * signed.
+                        */
+                       maxexpire =
+                           (time_t)~(1ULL << ((sizeof(maxexpire) * 8) - 1));
+                       if (ia6->ia6_lifetime.ia6t_pltime <
+                           maxexpire - ia6->ia6_updatetime) {
+                               expire = ia6->ia6_updatetime +
+                                   ia6->ia6_lifetime.ia6t_pltime;
+                               if (expire != 0) {
+                                       expire -= time_uptime;
+                                       expire += time_second;
+                               }
+                               retlt->ia6t_preferred = expire;
+                       } else
+                               retlt->ia6t_preferred = maxexpire;
+               }
+               break;
+
+       default:
+               panic("invalid ioctl %lu", cmd);
        }
 
 err:
-       NET_UNLOCK();
+       NET_RUNLOCK();
        return (error);
 }
 

Reply via email to