tech-kern archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

compat linux SIOCGIFCONF fix for 64bit archs



Hi,

Here follow a patch that fix SIOCGIFCONF ioctl under compat linux for
64bits archs.

The main problem, here, is that Linux `struct ifreq' is not MI, but
our is ... By example, on Linux/x86_64 use a 40 bytes structure but
our corresponding structure has only 32 bytes (this works on i386,
because the Linux structure size is 32 bytes). This difference will
lead to an alignment problem when reporting the interfaces list with
SIOCGIFCONF ioctl.

To fix it, i added a new `struct linux_ifreq' which has the expected
size, and a new linux_getifconf() function that use it (a cleaned up
version of compat_ifconf()).

While here, the new linux_getifconf() function will only report
interfaces of AF_INET family, like the Linux kernel does.

I tested it on -current NetBSD/amd64 and NetBSD/i386 that i have
access to.

Thanks.

NB: If this is ok, i'll plan to modify compat linux32 too; at least
for consistency with AF_INET restriction.

-- 
Nicolas Joly

Biological Software and Databanks.
Institut Pasteur, Paris.
Index: sys/compat/linux/common/linux_socket.c
===================================================================
RCS file: /cvsroot/src/sys/compat/linux/common/linux_socket.c,v
retrieving revision 1.96
diff -u -p -r1.96 linux_socket.c
--- sys/compat/linux/common/linux_socket.c      24 Jun 2008 11:18:15 -0000      
1.96
+++ sys/compat/linux/common/linux_socket.c      30 Jun 2008 16:44:08 -0000
@@ -116,6 +116,7 @@ int linux_to_bsd_so_sockopt(int);
 int linux_to_bsd_ip_sockopt(int);
 int linux_to_bsd_tcp_sockopt(int);
 int linux_to_bsd_udp_sockopt(int);
+int linux_getifconf(struct lwp *, register_t *, void *);
 int linux_getifhwaddr(struct lwp *, register_t *, u_int, void *);
 static int linux_get_sa(struct lwp *, int, struct mbuf **,
                const struct osockaddr *, unsigned int);
@@ -966,17 +967,63 @@ linux_sys_getsockopt(struct lwp *l, cons
        return sys_getsockopt(l, &bga, retval);
 }
 
-#define IF_NAME_LEN 16
+int
+linux_getifconf(struct lwp *l, register_t *retval, void *data)
+{
+       struct linux_ifreq ifr, *ifrp;
+       struct ifconf *ifc = data;
+       struct ifnet *ifp;
+       struct ifaddr *ifa;
+       struct sockaddr *sa;
+       struct osockaddr *osa;
+       int space, error = 0;
+       const int sz = (int)sizeof(ifr);
+
+       ifrp = (struct linux_ifreq *)ifc->ifc_req;
+       if (ifrp == NULL)
+               space = 0;
+       else
+               space = ifc->ifc_len;
+
+       IFNET_FOREACH(ifp) {
+               (void)strncpy(ifr.ifr_name, ifp->if_xname,
+                   sizeof(ifr.ifr_name));
+               if (ifr.ifr_name[sizeof(ifr.ifr_name) - 1] != '\0')
+                       return ENAMETOOLONG;
+               if (IFADDR_EMPTY(ifp))
+                       continue;
+               IFADDR_FOREACH(ifa, ifp) {
+                       sa = ifa->ifa_addr;
+                       if (sa->sa_family != AF_INET ||
+                           sa->sa_len > sizeof(*osa))
+                               continue;
+                       memcpy(&ifr.ifr_addr, sa, sa->sa_len);
+                       osa = (struct osockaddr *)&ifr.ifr_addr;
+                       osa->sa_family = sa->sa_family;
+                       if (space >= sz) {
+                               error = copyout(&ifr, ifrp, sz);
+                               if (error != 0)
+                                       return error;
+                               ifrp++;
+                       }
+                       space -= sz;
+               }
+       }
+
+       if (ifrp != NULL)
+               ifc->ifc_len -= space;
+       else
+               ifc->ifc_len = -space;
+
+       return 0;
+}
 
 int
 linux_getifhwaddr(struct lwp *l, register_t *retval, u_int fd,
     void *data)
 {
        /* Not the full structure, just enough to map what we do here */
-       struct linux_ifreq {
-               char if_name[IF_NAME_LEN];
-               struct osockaddr hwaddr;
-       } lreq;
+       struct linux_ifreq lreq;
        file_t *fp;
        struct ifaddr *ifa;
        struct ifnet *ifp;
@@ -1008,7 +1055,7 @@ linux_getifhwaddr(struct lwp *l, registe
        error = copyin(data, &lreq, sizeof(lreq));
        if (error)
                goto out;
-       lreq.if_name[IF_NAME_LEN-1] = '\0';             /* just in case */
+       lreq.ifr_name[LINUX_IFNAMSIZ-1] = '\0';         /* just in case */
 
        /*
         * Try real interface name first, then fake "ethX"
@@ -1017,7 +1064,7 @@ linux_getifhwaddr(struct lwp *l, registe
        IFNET_FOREACH(ifp) {
                if (found)
                        break;
-               if (strcmp(lreq.if_name, ifp->if_xname))
+               if (strcmp(lreq.ifr_name, ifp->if_xname))
                        /* not this interface */
                        continue;
                found=1;
@@ -1032,22 +1079,22 @@ linux_getifhwaddr(struct lwp *l, registe
                        if (sadl->sdl_family != AF_LINK ||
                            sadl->sdl_type != IFT_ETHER)
                                continue;
-                       memcpy(&lreq.hwaddr.sa_data, CLLADDR(sadl),
+                       memcpy(&lreq.ifr_hwaddr.sa_data, CLLADDR(sadl),
                               MIN(sadl->sdl_alen,
-                                  sizeof(lreq.hwaddr.sa_data)));
-                       lreq.hwaddr.sa_family =
+                                  sizeof(lreq.ifr_hwaddr.sa_data)));
+                       lreq.ifr_hwaddr.sa_family =
                                sadl->sdl_family;
                        error = copyout(&lreq, data, sizeof(lreq));
                        goto out;
                }
        }
 
-       if (strncmp(lreq.if_name, "eth", 3) == 0) {
+       if (strncmp(lreq.ifr_name, "eth", 3) == 0) {
                for (ifnum = 0, index = 3;
-                    lreq.if_name[index] != '\0' && index < IF_NAME_LEN;
+                    lreq.ifr_name[index] != '\0' && index < LINUX_IFNAMSIZ;
                     index++) {
                        ifnum *= 10;
-                       ifnum += lreq.if_name[index] - '0';
+                       ifnum += lreq.ifr_name[index] - '0';
                }
 
                error = EINVAL;                 /* in case we don't find one */
@@ -1055,8 +1102,8 @@ linux_getifhwaddr(struct lwp *l, registe
                IFNET_FOREACH(ifp) {
                        if (found)
                                break;
-                       memcpy(lreq.if_name, ifp->if_xname,
-                              MIN(IF_NAME_LEN, IFNAMSIZ));
+                       memcpy(lreq.ifr_name, ifp->if_xname,
+                              MIN(LINUX_IFNAMSIZ, IFNAMSIZ));
                        IFADDR_FOREACH(ifa, ifp) {
                                sadl = satosdl(ifa->ifa_addr);
                                /* only return ethernet addresses */
@@ -1067,11 +1114,11 @@ linux_getifhwaddr(struct lwp *l, registe
                                if (ifnum--)
                                        /* not the reqested iface */
                                        continue;
-                               memcpy(&lreq.hwaddr.sa_data,
+                               memcpy(&lreq.ifr_hwaddr.sa_data,
                                       CLLADDR(sadl),
                                       MIN(sadl->sdl_alen,
-                                          sizeof(lreq.hwaddr.sa_data)));
-                               lreq.hwaddr.sa_family =
+                                          sizeof(lreq.ifr_hwaddr.sa_data)));
+                               lreq.ifr_hwaddr.sa_family =
                                        sadl->sdl_family;
                                error = copyout(&lreq, data, sizeof(lreq));
                                found = 1;
@@ -1088,7 +1135,6 @@ out:
        fd_putfile(fd);
        return error;
 }
-#undef IF_NAME_LEN
 
 int
 linux_ioctl_socket(struct lwp *l, const struct linux_sys_ioctl_args *uap, 
register_t *retval)
@@ -1143,7 +1189,8 @@ linux_ioctl_socket(struct lwp *l, const 
 
        switch (com) {
        case LINUX_SIOCGIFCONF:
-               SCARG(&ia, com) = OOSIOCGIFCONF;
+               error = linux_getifconf(l, retval, SCARG(uap, data));
+               dosys = 0;
                break;
        case LINUX_SIOCGIFFLAGS:
                SCARG(&ia, com) = OSIOCGIFFLAGS;
Index: sys/compat/linux/common/linux_socketcall.c
===================================================================
RCS file: /cvsroot/src/sys/compat/linux/common/linux_socketcall.c,v
retrieving revision 1.38
diff -u -p -r1.38 linux_socketcall.c
--- sys/compat/linux/common/linux_socketcall.c  28 Apr 2008 20:23:44 -0000      
1.38
+++ sys/compat/linux/common/linux_socketcall.c  30 Jun 2008 16:44:08 -0000
@@ -55,6 +55,8 @@ __KERNEL_RCSID(0, "$NetBSD: linux_socket
 
 #include <sys/syscallargs.h>
 
+#include <compat/sys/socket.h>
+
 #include <compat/linux/common/linux_types.h>
 #include <compat/linux/common/linux_util.h>
 #include <compat/linux/common/linux_signal.h>
Index: sys/compat/linux/common/linux_sockio.h
===================================================================
RCS file: /cvsroot/src/sys/compat/linux/common/linux_sockio.h,v
retrieving revision 1.15
diff -u -p -r1.15 linux_sockio.h
--- sys/compat/linux/common/linux_sockio.h      28 Apr 2008 20:23:44 -0000      
1.15
+++ sys/compat/linux/common/linux_sockio.h      30 Jun 2008 16:44:08 -0000
@@ -47,4 +47,30 @@
 #define LINUX_SIOCGIFBR                _LINUX_IO(0x89, 0x40)
 #define LINUX_SIOCSIFBR                _LINUX_IO(0x89, 0x41)
 
+#define LINUX_IFNAMSIZ 16
+
+struct linux_ifmap {
+       unsigned long mem_start;
+       unsigned long mem_end;
+       unsigned short base_addr; 
+       unsigned char irq;
+       unsigned char dma;
+       unsigned char port;
+};
+
+struct linux_ifreq {
+       union {
+               char ifrn_name[LINUX_IFNAMSIZ]; /* if name, e.g. "en0" */
+       } ifr_ifrn;
+       union {
+               struct osockaddr ifru_addr;
+               struct osockaddr ifru_hwaddr;
+               struct linux_ifmap ifru_map;
+       } ifr_ifru;
+#define ifr_name       ifr_ifrn.ifrn_name      /* interface name       */
+#define ifr_addr       ifr_ifru.ifru_addr      /* address              */
+#define ifr_hwaddr     ifr_ifru.ifru_hwaddr    /* MAC address          */
+#define ifr_map                ifr_ifru.ifru_map       /* device map           
*/
+};
+
 #endif /* !_LINUX_SOCKIO_H */


Home | Main Index | Thread Index | Old Index