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);
}