changeset 18404:dbf319f845d5

OS-4138 lxbrand socket ioctls should be flexible about address family Reviewed by: Jerry Jelinek <jerry.jelinek@joyent.com>
author Patrick Mooney <patrick.f.mooney@gmail.com>
date Tue, 07 Apr 2015 00:47:53 +0000
parents e8188476e197
children 7ac3b7113c98
files usr/src/uts/common/brand/lx/os/lx_brand.c usr/src/uts/common/brand/lx/sys/lx_brand.h usr/src/uts/common/brand/lx/syscall/lx_ioctl.c
diffstat 3 files changed, 76 insertions(+), 30 deletions(-) [+]
line wrap: on
line diff
--- a/usr/src/uts/common/brand/lx/os/lx_brand.c	Wed Apr 08 16:01:25 2015 +0000
+++ b/usr/src/uts/common/brand/lx/os/lx_brand.c	Tue Apr 07 00:47:53 2015 +0000
@@ -169,6 +169,7 @@
 #include <sys/core.h>
 #include <sys/stack.h>
 #include <sys/stat.h>
+#include <sys/socket.h>
 #include <lx_signum.h>
 
 int	lx_debug = 0;
@@ -678,6 +679,15 @@
 	 * This can be changed by a call to setattr() during zone boot.
 	 */
 	(void) strlcpy(data->lxzd_kernel_version, "2.4.21", LX_VERS_MAX);
+
+	/*
+	 * Linux is not at all picky about address family when it comes to
+	 * supporting interface-related ioctls.  To mimic this behavior, we'll
+	 * attempt those ioctls against a ksocket configured for that purpose.
+	 */
+	(void) ksocket_socket(&data->lxzd_ioctl_sock, AF_INET, SOCK_DGRAM, 0,
+	    0, zone->zone_kcred);
+
 	zone->zone_brand_data = data;
 
 	/*
@@ -690,7 +700,14 @@
 void
 lx_free_brand_data(zone_t *zone)
 {
-	kmem_free(zone->zone_brand_data, sizeof (lx_zone_data_t));
+	lx_zone_data_t *data = ztolxzd(zone);
+	ASSERT(data != NULL);
+	if (data->lxzd_ioctl_sock != NULL) {
+		ksocket_close(data->lxzd_ioctl_sock, zone->zone_kcred);
+		data->lxzd_ioctl_sock = NULL;
+	}
+	zone->zone_brand_data = NULL;
+	kmem_free(data, sizeof (*data));
 }
 
 void
--- a/usr/src/uts/common/brand/lx/sys/lx_brand.h	Wed Apr 08 16:01:25 2015 +0000
+++ b/usr/src/uts/common/brand/lx/sys/lx_brand.h	Tue Apr 07 00:47:53 2015 +0000
@@ -34,6 +34,7 @@
 #include <sys/types.h>
 #include <sys/cpuvar.h>
 #include <sys/zone.h>
+#include <sys/ksocket.h>
 #endif
 
 #ifdef	__cplusplus
@@ -587,6 +588,7 @@
 /* brand specific data */
 typedef struct lx_zone_data {
 	char lxzd_kernel_version[LX_VERS_MAX];
+	ksocket_t lxzd_ioctl_sock;
 } lx_zone_data_t;
 
 #define	BR_CPU_BOUND	0x0001
@@ -599,6 +601,9 @@
 #define	ptolxproc(p)	\
 	(((p)->p_brand == &lx_brand) ? \
 	(struct lx_proc_data *)(p)->p_brand_data : NULL)
+#define	ztolxzd(z)		\
+	(((z)->zone_brand == &lx_brand) ?  \
+	(lx_zone_data_t *)(z)->zone_brand_data : NULL)
 
 /* Macro for converting to system call arguments. */
 #define	LX_ARGS(scall) ((struct lx_##scall##_args *)\
--- a/usr/src/uts/common/brand/lx/syscall/lx_ioctl.c	Wed Apr 08 16:01:25 2015 +0000
+++ b/usr/src/uts/common/brand/lx/syscall/lx_ioctl.c	Tue Apr 07 00:47:53 2015 +0000
@@ -806,11 +806,35 @@
 }
 
 static int
+ict_if_ioctl(vnode_t *vn, int cmd, intptr_t arg, int flags, cred_t *cred)
+{
+	int error, rv;
+	lx_zone_data_t *lxzd = ztolxzd(curproc->p_zone);
+	ksocket_t ks;
+
+	ASSERT(lxzd != NULL);
+	ks = lxzd->lxzd_ioctl_sock;
+
+	/*
+	 * For ioctls of this type, Illumos is strict about address family
+	 * whereas Linux is lenient.  This strictness can be avoided by using
+	 * an internal AF_INET ksocket.
+	 */
+	if (ks != NULL) {
+		error = ksocket_ioctl(ks, cmd, arg, &rv, cred);
+	} else {
+		error = VOP_IOCTL(vn, cmd, arg, flags, cred, &rv, NULL);
+	}
+
+	return (error);
+}
+
+static int
 ict_siolifreq(file_t *fp, int cmd, intptr_t arg, int lxcmd)
 {
 	struct ifreq	req;
 	struct lifreq	lreq;
-	int		error, rv, len;
+	int		error, len;
 
 	/* Convert from Linux ifreq to illumos lifreq */
 	if (curproc->p_model == DATAMODEL_LP64)
@@ -844,31 +868,31 @@
 		 */
 		cmd = ((cmd & IOC_INOUT) |
 		    _IOW('i', ((cmd & 0xff) + 100), struct lifreq));
-		error = VOP_IOCTL(fp->f_vnode, cmd, (intptr_t)&lreq,
-		    FLFAKE(fp), fp->f_cred, &rv, NULL);
+		error = ict_if_ioctl(fp->f_vnode, cmd, (intptr_t)&lreq,
+		    FLFAKE(fp), fp->f_cred);
 		break;
 	case SIOCGIFINDEX:
 		cmd = SIOCGLIFINDEX;
-		error = VOP_IOCTL(fp->f_vnode, cmd, (intptr_t)&lreq,
-		    FLFAKE(fp), fp->f_cred, &rv, NULL);
+		error = ict_if_ioctl(fp->f_vnode, cmd, (intptr_t)&lreq,
+		    FLFAKE(fp), fp->f_cred);
 		break;
 	case SIOCGIFFLAGS:
 		cmd = SIOCGLIFFLAGS;
-		error = VOP_IOCTL(fp->f_vnode, cmd, (intptr_t)&lreq,
-		    FLFAKE(fp), fp->f_cred, &rv, NULL);
+		error = ict_if_ioctl(fp->f_vnode, cmd, (intptr_t)&lreq,
+		    FLFAKE(fp), fp->f_cred);
 		if (error == 0)
 			ict_convert_ifflags(&lreq.lifr_flags, B_FALSE);
 		break;
 	case SIOCSIFFLAGS:
 		cmd = SIOCSLIFFLAGS;
 		ict_convert_ifflags(&lreq.lifr_flags, B_TRUE);
-		error = VOP_IOCTL(fp->f_vnode, cmd, (intptr_t)&lreq,
-		    FLFAKE(fp), fp->f_cred, &rv, NULL);
+		error = ict_if_ioctl(fp->f_vnode, cmd, (intptr_t)&lreq,
+		    FLFAKE(fp), fp->f_cred);
 		break;
 	case SIOCGIFHWADDR:
 		cmd = SIOCGLIFHWADDR;
-		error = VOP_IOCTL(fp->f_vnode, cmd, (intptr_t)&lreq,
-		    FLFAKE(fp), fp->f_cred, &rv, NULL);
+		error = ict_if_ioctl(fp->f_vnode, cmd, (intptr_t)&lreq,
+		    FLFAKE(fp), fp->f_cred);
 		/*
 		 * SIOCGIFHWADDR on Linux sets sa_family to ARPHRD_ETHER (1) on
 		 * ethernet and ARPHRD_LOOPBACK (772) on loopback.
@@ -901,8 +925,8 @@
 			break;
 		}
 		cmd = SIOCGLIFINDEX;
-		error = VOP_IOCTL(fp->f_vnode, cmd, (intptr_t)&lreq,
-		    FLFAKE(fp), fp->f_cred, &rv, NULL);
+		error = ict_if_ioctl(fp->f_vnode, cmd, (intptr_t)&lreq,
+		    FLFAKE(fp), fp->f_cred);
 		if (error == 0) {
 			/* lifr_index aliases to the qlen field */
 			lreq.lifr_index = 1;
@@ -939,15 +963,15 @@
 	lx_ifconf32_t	conf;
 	lx_ifreq32_t	*oreq;
 	struct ifconf	sconf;
-	int		ifcount, error, rv, i, buf_len;
+	int		ifcount, error, i, buf_len;
 
 	if (copyin((lx_ifconf32_t *)arg, &conf, sizeof (conf)) != 0)
 		return (set_errno(EFAULT));
 
 	/* They want to know how many interfaces there are. */
 	if (conf.if_len <= 0 || conf.if_buf == NULL) {
-		error = VOP_IOCTL(fp->f_vnode, SIOCGIFNUM, (intptr_t)&ifcount,
-		    FLFAKE(fp), fp->f_cred, &rv, NULL);
+		error = ict_if_ioctl(fp->f_vnode, SIOCGIFNUM,
+		    (intptr_t)&ifcount, FLFAKE(fp), fp->f_cred);
 		if (error != 0)
 			return (set_errno(error));
 
@@ -964,8 +988,8 @@
 	sconf.ifc_len = ifcount * sizeof (struct ifreq);
 	sconf.ifc_req = (struct ifreq *)kmem_alloc(sconf.ifc_len, KM_SLEEP);
 
-	error = VOP_IOCTL(fp->f_vnode, cmd, (intptr_t)&sconf,
-	    FLFAKE(fp), fp->f_cred, &rv, NULL);
+	error = ict_if_ioctl(fp->f_vnode, cmd, (intptr_t)&sconf, FLFAKE(fp),
+	    fp->f_cred);
 	if (error != 0) {
 		kmem_free(sconf.ifc_req, ifcount * sizeof (struct ifreq));
 		return (set_errno(error));
@@ -981,13 +1005,13 @@
 	conf.if_len = i * sizeof (*oreq);
 	kmem_free(sconf.ifc_req, ifcount * sizeof (struct ifreq));
 
-	rv = 0;
+	error = 0;
 	if (copyout(oreq, (caddr_t)(uintptr_t)conf.if_buf, conf.if_len) != 0 ||
 	    copyout(&conf, (lx_ifconf32_t *)arg, sizeof (conf)) != 0)
-		rv = set_errno(EFAULT);
+		error = set_errno(EFAULT);
 
 	kmem_free(oreq, buf_len);
-	return (rv);
+	return (error);
 }
 
 static int
@@ -996,15 +1020,15 @@
 	lx_ifconf64_t	conf;
 	lx_ifreq64_t	*oreq;
 	struct ifconf	sconf;
-	int		ifcount, error, rv, i, buf_len;
+	int		ifcount, error, i, buf_len;
 
 	if (copyin((lx_ifconf64_t *)arg, &conf, sizeof (conf)) != 0)
 		return (set_errno(EFAULT));
 
 	/* They want to know how many interfaces there are. */
 	if (conf.if_len <= 0 || conf.if_buf == NULL) {
-		error = VOP_IOCTL(fp->f_vnode, SIOCGIFNUM, (intptr_t)&ifcount,
-		    FLFAKE(fp), fp->f_cred, &rv, NULL);
+		error = ict_if_ioctl(fp->f_vnode, SIOCGIFNUM,
+		    (intptr_t)&ifcount, FLFAKE(fp), fp->f_cred);
 		if (error != 0)
 			return (set_errno(error));
 
@@ -1021,8 +1045,8 @@
 	sconf.ifc_len = ifcount * sizeof (struct ifreq);
 	sconf.ifc_req = (struct ifreq *)kmem_alloc(sconf.ifc_len, KM_SLEEP);
 
-	error = VOP_IOCTL(fp->f_vnode, cmd, (intptr_t)&sconf,
-	    FLFAKE(fp), fp->f_cred, &rv, NULL);
+	error = ict_if_ioctl(fp->f_vnode, cmd, (intptr_t)&sconf, FLFAKE(fp),
+	    fp->f_cred);
 	if (error != 0) {
 		kmem_free(sconf.ifc_req, ifcount * sizeof (struct ifreq));
 		return (set_errno(error));
@@ -1038,13 +1062,13 @@
 	conf.if_len = i * sizeof (*oreq);
 	kmem_free(sconf.ifc_req, ifcount * sizeof (struct ifreq));
 
-	rv = 0;
+	error = 0;
 	if (copyout(oreq, (caddr_t)(uintptr_t)conf.if_buf, conf.if_len) != 0 ||
 	    copyout(&conf, (lx_ifconf64_t *)arg, sizeof (conf)) != 0)
-		rv = set_errno(EFAULT);
+		error = set_errno(EFAULT);
 
 	kmem_free(oreq, buf_len);
-	return (rv);
+	return (error);
 }
 
 static int