Skip to content

Commit

Permalink
linux-user: convert ioctl(SIOCGIFCONF, ...) result.
Browse files Browse the repository at this point in the history
The result needs to be converted as it is stored in an array of struct
ifreq and sizeof(struct ifreq) differs according to target and host
alignment rules.

This patch allows to execute correctly the following program on arm
and m68k:

 #include <stdio.h>
 #include <sys/ioctl.h>
 #include <net/if.h>
 #include <alloca.h>
 #include <string.h>
 #include <sys/socket.h>
 #include <netinet/in.h>
 #include <arpa/inet.h>

int main(void)
{
    int s, ret;
    struct ifconf ifc;
    int i;

    memset( &ifc, 0, sizeof( struct ifconf ) );
    ifc.ifc_len = 8 * sizeof(struct ifreq);
    ifc.ifc_buf = alloca(ifc.ifc_len);

    s = socket( AF_INET, SOCK_DGRAM, 0 );
    if (s < 0) {
        perror("Cannot open socket");
        return 1;
    }
    ret = ioctl( s, SIOCGIFCONF, &ifc );
    if (s < 0) {
        perror("ioctl() failed");
        return 1;
    }

    for (i = 0; i < ifc.ifc_len / sizeof(struct ifreq) ; i ++) {
        struct sockaddr_in *s;
        s = (struct sockaddr_in*)&ifc.ifc_req[i].ifr_addr;
        printf("%s\n", ifc.ifc_req[i].ifr_name);
        printf("%s\n", inet_ntoa(s->sin_addr));
    }
}

Signed-off-by: Laurent Vivier <laurent@vivier.eu>
Signed-off-by: Riku Voipio <riku.voipio@iki.fi>
  • Loading branch information
vivier authored and suihkulokki committed Apr 26, 2011
1 parent 608e559 commit 059c2f2
Show file tree
Hide file tree
Showing 2 changed files with 97 additions and 2 deletions.
3 changes: 2 additions & 1 deletion linux-user/ioctls.h
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,8 @@
IOCTL(SIOCADDMULTI, IOC_W, MK_PTR(MK_STRUCT(STRUCT_sockaddr_ifreq)))
IOCTL(SIOCDELMULTI, IOC_W, MK_PTR(MK_STRUCT(STRUCT_sockaddr_ifreq)))
IOCTL(SIOCSIFLINK, 0, TYPE_NULL)
IOCTL(SIOCGIFCONF, IOC_W | IOC_R, MK_PTR(MK_STRUCT(STRUCT_ifconf)))
IOCTL_SPECIAL(SIOCGIFCONF, IOC_W | IOC_R, do_ioctl_ifconf,
MK_PTR(MK_STRUCT(STRUCT_ifconf)))
IOCTL(SIOCGIFENCAP, IOC_RW, MK_PTR(TYPE_INT))
IOCTL(SIOCSIFENCAP, IOC_W, MK_PTR(TYPE_INT))
IOCTL(SIOCDARP, IOC_W, MK_PTR(MK_STRUCT(STRUCT_arpreq)))
Expand Down
96 changes: 95 additions & 1 deletion linux-user/syscall.c
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ int __clone2(int (*fn)(void *), void *child_stack_base,
//#include <sys/user.h>
#include <netinet/ip.h>
#include <netinet/tcp.h>
#include <net/if.h>
#include <qemu-common.h>
#ifdef TARGET_GPROF
#include <sys/gmon.h>
Expand Down Expand Up @@ -2970,7 +2971,6 @@ static abi_long do_ipc(unsigned int call, int first,
#endif

/* kernel structure types definitions */
#define IFNAMSIZ 16

#define STRUCT(name, ...) STRUCT_ ## name,
#define STRUCT_SPECIAL(name) STRUCT_ ## name,
Expand Down Expand Up @@ -3095,6 +3095,100 @@ static abi_long do_ioctl_fs_ioc_fiemap(const IOCTLEntry *ie, uint8_t *buf_temp,
}
#endif

static abi_long do_ioctl_ifconf(const IOCTLEntry *ie, uint8_t *buf_temp,
int fd, abi_long cmd, abi_long arg)
{
const argtype *arg_type = ie->arg_type;
int target_size;
void *argptr;
int ret;
struct ifconf *host_ifconf;
uint32_t outbufsz;
const argtype ifreq_arg_type[] = { MK_STRUCT(STRUCT_sockaddr_ifreq) };
int target_ifreq_size;
int nb_ifreq;
int free_buf = 0;
int i;
int target_ifc_len;
abi_long target_ifc_buf;
int host_ifc_len;
char *host_ifc_buf;

assert(arg_type[0] == TYPE_PTR);
assert(ie->access == IOC_RW);

arg_type++;
target_size = thunk_type_size(arg_type, 0);

argptr = lock_user(VERIFY_READ, arg, target_size, 1);
if (!argptr)
return -TARGET_EFAULT;
thunk_convert(buf_temp, argptr, arg_type, THUNK_HOST);
unlock_user(argptr, arg, 0);

host_ifconf = (struct ifconf *)(unsigned long)buf_temp;
target_ifc_len = host_ifconf->ifc_len;
target_ifc_buf = (abi_long)(unsigned long)host_ifconf->ifc_buf;

target_ifreq_size = thunk_type_size(ifreq_arg_type, 0);
nb_ifreq = target_ifc_len / target_ifreq_size;
host_ifc_len = nb_ifreq * sizeof(struct ifreq);

outbufsz = sizeof(*host_ifconf) + host_ifc_len;
if (outbufsz > MAX_STRUCT_SIZE) {
/* We can't fit all the extents into the fixed size buffer.
* Allocate one that is large enough and use it instead.
*/
host_ifconf = malloc(outbufsz);
if (!host_ifconf) {
return -TARGET_ENOMEM;
}
memcpy(host_ifconf, buf_temp, sizeof(*host_ifconf));
free_buf = 1;
}
host_ifc_buf = (char*)host_ifconf + sizeof(*host_ifconf);

host_ifconf->ifc_len = host_ifc_len;
host_ifconf->ifc_buf = host_ifc_buf;

ret = get_errno(ioctl(fd, ie->host_cmd, host_ifconf));
if (!is_error(ret)) {
/* convert host ifc_len to target ifc_len */

nb_ifreq = host_ifconf->ifc_len / sizeof(struct ifreq);
target_ifc_len = nb_ifreq * target_ifreq_size;
host_ifconf->ifc_len = target_ifc_len;

/* restore target ifc_buf */

host_ifconf->ifc_buf = (char *)(unsigned long)target_ifc_buf;

/* copy struct ifconf to target user */

argptr = lock_user(VERIFY_WRITE, arg, target_size, 0);
if (!argptr)
return -TARGET_EFAULT;
thunk_convert(argptr, host_ifconf, arg_type, THUNK_TARGET);
unlock_user(argptr, arg, target_size);

/* copy ifreq[] to target user */

argptr = lock_user(VERIFY_WRITE, target_ifc_buf, target_ifc_len, 0);
for (i = 0; i < nb_ifreq ; i++) {
thunk_convert(argptr + i * target_ifreq_size,
host_ifc_buf + i * sizeof(struct ifreq),
ifreq_arg_type, THUNK_TARGET);
}
unlock_user(argptr, target_ifc_buf, target_ifc_len);
}

if (free_buf) {
free(host_ifconf);
}

return ret;
}

static IOCTLEntry ioctl_entries[] = {
#define IOCTL(cmd, access, ...) \
{ TARGET_ ## cmd, cmd, #cmd, access, 0, { __VA_ARGS__ } },
Expand Down

0 comments on commit 059c2f2

Please sign in to comment.