Now that the dust has settled a bit, I'd like to simplify in6_ioctl() a bit more. At the moment it still has two tangled switches that deal with SIOCAIFADDR_IN6 and SIOCDIFADDR_IN6. We can disentangle them at the cost of repeating a few lines, but I think it's worth it.
I also introduced another function to make in6_ioctl() appear a bit more symmetric. Index: sys/netinet6/in6.c =================================================================== RCS file: /var/cvs/src/sys/netinet6/in6.c,v retrieving revision 1.224 diff -u -p -r1.224 in6.c --- sys/netinet6/in6.c 4 May 2018 19:43:07 -0000 1.224 +++ sys/netinet6/in6.c 5 May 2018 12:21:41 -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_change_ifaddr(u_long, caddr_t, struct ifnet *); 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 *); @@ -206,12 +207,6 @@ in6_control(struct socket *so, u_long cm int in6_ioctl(u_long cmd, caddr_t data, struct ifnet *ifp, int privileged) { - struct in6_ifreq *ifr = (struct in6_ifreq *)data; - struct in6_ifaddr *ia6 = NULL; - struct in6_aliasreq *ifra = (struct in6_aliasreq *)data; - struct sockaddr_in6 *sa6; - int error = 0; - if (ifp == NULL) return (ENXIO); @@ -225,11 +220,10 @@ in6_ioctl(u_long cmd, caddr_t data, stru 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; + if (!privileged) + return (EPERM); + return (in6_ioctl_change_ifaddr(cmd, data, ifp)); case SIOCSIFADDR: case SIOCSIFDSTADDR: case SIOCSIFBRDADDR: @@ -242,6 +236,16 @@ in6_ioctl(u_long cmd, caddr_t data, stru default: return (EOPNOTSUPP); } +} + +int +in6_ioctl_change_ifaddr(u_long cmd, caddr_t data, struct ifnet *ifp) +{ + struct in6_ifreq *ifr = (struct in6_ifreq *)data; + struct in6_ifaddr *ia6 = NULL; + struct in6_aliasreq *ifra = (struct in6_aliasreq *)data; + struct sockaddr_in6 *sa6; + int error = 0, newifaddr = 0, plen; /* * Find address for this interface, if it exists. @@ -255,6 +259,16 @@ in6_ioctl(u_long cmd, caddr_t data, stru * 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 SIOCDIFADDR_IN6: + sa6 = &ifr->ifr_addr; + break; + default: + panic("unknown ioctl %lu", cmd); + } NET_LOCK(); @@ -279,8 +293,7 @@ in6_ioctl(u_long cmd, caddr_t data, stru } } ia6 = in6ifa_ifpwithaddr(ifp, &sa6->sin6_addr); - } else - ia6 = NULL; + } switch (cmd) { case SIOCDIFADDR_IN6: @@ -293,10 +306,8 @@ in6_ioctl(u_long cmd, caddr_t data, stru */ if (ia6 == NULL) { error = EADDRNOTAVAIL; - goto err; + break; } - /* FALLTHROUGH */ - case SIOCAIFADDR_IN6: /* * We always require users to specify a valid IPv6 address for * the corresponding operation. @@ -304,19 +315,22 @@ in6_ioctl(u_long cmd, caddr_t data, stru if (ifra->ifra_addr.sin6_family != AF_INET6 || ifra->ifra_addr.sin6_len != sizeof(struct sockaddr_in6)) { error = EAFNOSUPPORT; - goto err; - } - if (!privileged) { - error = EPERM; - goto err; + break; } + in6_purgeaddr(&ia6->ia_ifa); + dohooks(ifp->if_addrhooks, 0); break; - } - switch (cmd) { case SIOCAIFADDR_IN6: - { - int plen, newifaddr = 0; + /* + * We always require users to specify a valid IPv6 address for + * the corresponding operation. + */ + if (ifra->ifra_addr.sin6_family != AF_INET6 || + ifra->ifra_addr.sin6_len != sizeof(struct sockaddr_in6)) { + error = EAFNOSUPPORT; + break; + } /* reject read-only flags */ if ((ifra->ifra_flags & IN6_IFF_DUPLICATED) != 0 || @@ -379,12 +393,6 @@ in6_ioctl(u_long cmd, caddr_t data, stru in6_purgeaddr(&ia6->ia_ifa); break; } - dohooks(ifp->if_addrhooks, 0); - break; - } - - case SIOCDIFADDR_IN6: - in6_purgeaddr(&ia6->ia_ifa); dohooks(ifp->if_addrhooks, 0); break; }