linux-user: convert ioctl(SIOCGIFCONF, ...) result.

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>
This commit is contained in:
Laurent Vivier 2011-03-30 00:12:12 +02:00 committed by Riku Voipio
parent 608e559217
commit 059c2f2cd7
2 changed files with 97 additions and 2 deletions

View File

@ -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)))

View File

@ -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>
@ -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,
@ -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__ } },