|
|
|
@ -57,6 +57,8 @@ int __clone2(int (*fn)(void *), void *child_stack_base, |
|
|
|
#include <netinet/tcp.h> |
|
|
|
#include <linux/wireless.h> |
|
|
|
#include <linux/icmp.h> |
|
|
|
#include <linux/icmpv6.h> |
|
|
|
#include <linux/errqueue.h> |
|
|
|
#include "qemu-common.h" |
|
|
|
#ifdef CONFIG_TIMERFD |
|
|
|
#include <sys/timerfd.h> |
|
|
|
@ -1634,6 +1636,11 @@ static inline abi_long host_to_target_sockaddr(abi_ulong target_addr, |
|
|
|
struct sockaddr_ll *target_ll = (struct sockaddr_ll *)target_saddr; |
|
|
|
target_ll->sll_ifindex = tswap32(target_ll->sll_ifindex); |
|
|
|
target_ll->sll_hatype = tswap16(target_ll->sll_hatype); |
|
|
|
} else if (addr->sa_family == AF_INET6 && |
|
|
|
len >= sizeof(struct target_sockaddr_in6)) { |
|
|
|
struct target_sockaddr_in6 *target_in6 = |
|
|
|
(struct target_sockaddr_in6 *)target_saddr; |
|
|
|
target_in6->sin6_scope_id = tswap16(target_in6->sin6_scope_id); |
|
|
|
} |
|
|
|
unlock_user(target_saddr, target_addr, len); |
|
|
|
|
|
|
|
@ -1839,6 +1846,78 @@ static inline abi_long host_to_target_cmsg(struct target_msghdr *target_msgh, |
|
|
|
} |
|
|
|
break; |
|
|
|
|
|
|
|
case SOL_IP: |
|
|
|
switch (cmsg->cmsg_type) { |
|
|
|
case IP_TTL: |
|
|
|
{ |
|
|
|
uint32_t *v = (uint32_t *)data; |
|
|
|
uint32_t *t_int = (uint32_t *)target_data; |
|
|
|
|
|
|
|
__put_user(*v, t_int); |
|
|
|
break; |
|
|
|
} |
|
|
|
case IP_RECVERR: |
|
|
|
{ |
|
|
|
struct errhdr_t { |
|
|
|
struct sock_extended_err ee; |
|
|
|
struct sockaddr_in offender; |
|
|
|
}; |
|
|
|
struct errhdr_t *errh = (struct errhdr_t *)data; |
|
|
|
struct errhdr_t *target_errh = |
|
|
|
(struct errhdr_t *)target_data; |
|
|
|
|
|
|
|
__put_user(errh->ee.ee_errno, &target_errh->ee.ee_errno); |
|
|
|
__put_user(errh->ee.ee_origin, &target_errh->ee.ee_origin); |
|
|
|
__put_user(errh->ee.ee_type, &target_errh->ee.ee_type); |
|
|
|
__put_user(errh->ee.ee_code, &target_errh->ee.ee_code); |
|
|
|
__put_user(errh->ee.ee_pad, &target_errh->ee.ee_pad); |
|
|
|
__put_user(errh->ee.ee_info, &target_errh->ee.ee_info); |
|
|
|
__put_user(errh->ee.ee_data, &target_errh->ee.ee_data); |
|
|
|
host_to_target_sockaddr((unsigned long) &target_errh->offender, |
|
|
|
(void *) &errh->offender, sizeof(errh->offender)); |
|
|
|
break; |
|
|
|
} |
|
|
|
default: |
|
|
|
goto unimplemented; |
|
|
|
} |
|
|
|
break; |
|
|
|
|
|
|
|
case SOL_IPV6: |
|
|
|
switch (cmsg->cmsg_type) { |
|
|
|
case IPV6_HOPLIMIT: |
|
|
|
{ |
|
|
|
uint32_t *v = (uint32_t *)data; |
|
|
|
uint32_t *t_int = (uint32_t *)target_data; |
|
|
|
|
|
|
|
__put_user(*v, t_int); |
|
|
|
break; |
|
|
|
} |
|
|
|
case IPV6_RECVERR: |
|
|
|
{ |
|
|
|
struct errhdr6_t { |
|
|
|
struct sock_extended_err ee; |
|
|
|
struct sockaddr_in6 offender; |
|
|
|
}; |
|
|
|
struct errhdr6_t *errh = (struct errhdr6_t *)data; |
|
|
|
struct errhdr6_t *target_errh = |
|
|
|
(struct errhdr6_t *)target_data; |
|
|
|
|
|
|
|
__put_user(errh->ee.ee_errno, &target_errh->ee.ee_errno); |
|
|
|
__put_user(errh->ee.ee_origin, &target_errh->ee.ee_origin); |
|
|
|
__put_user(errh->ee.ee_type, &target_errh->ee.ee_type); |
|
|
|
__put_user(errh->ee.ee_code, &target_errh->ee.ee_code); |
|
|
|
__put_user(errh->ee.ee_pad, &target_errh->ee.ee_pad); |
|
|
|
__put_user(errh->ee.ee_info, &target_errh->ee.ee_info); |
|
|
|
__put_user(errh->ee.ee_data, &target_errh->ee.ee_data); |
|
|
|
host_to_target_sockaddr((unsigned long) &target_errh->offender, |
|
|
|
(void *) &errh->offender, sizeof(errh->offender)); |
|
|
|
break; |
|
|
|
} |
|
|
|
default: |
|
|
|
goto unimplemented; |
|
|
|
} |
|
|
|
break; |
|
|
|
|
|
|
|
default: |
|
|
|
unimplemented: |
|
|
|
gemu_log("Unsupported ancillary data: %d/%d\n", |
|
|
|
@ -2768,6 +2847,7 @@ static abi_long do_setsockopt(int sockfd, int level, int optname, |
|
|
|
case IP_PKTINFO: |
|
|
|
case IP_MTU_DISCOVER: |
|
|
|
case IP_RECVERR: |
|
|
|
case IP_RECVTTL: |
|
|
|
case IP_RECVTOS: |
|
|
|
#ifdef IP_FREEBIND |
|
|
|
case IP_FREEBIND: |
|
|
|
@ -2817,6 +2897,11 @@ static abi_long do_setsockopt(int sockfd, int level, int optname, |
|
|
|
case IPV6_MTU: |
|
|
|
case IPV6_V6ONLY: |
|
|
|
case IPV6_RECVPKTINFO: |
|
|
|
case IPV6_UNICAST_HOPS: |
|
|
|
case IPV6_RECVERR: |
|
|
|
case IPV6_RECVHOPLIMIT: |
|
|
|
case IPV6_2292HOPLIMIT: |
|
|
|
case IPV6_CHECKSUM: |
|
|
|
val = 0; |
|
|
|
if (optlen < sizeof(uint32_t)) { |
|
|
|
return -TARGET_EINVAL; |
|
|
|
@ -2827,6 +2912,50 @@ static abi_long do_setsockopt(int sockfd, int level, int optname, |
|
|
|
ret = get_errno(setsockopt(sockfd, level, optname, |
|
|
|
&val, sizeof(val))); |
|
|
|
break; |
|
|
|
case IPV6_PKTINFO: |
|
|
|
{ |
|
|
|
struct in6_pktinfo pki; |
|
|
|
|
|
|
|
if (optlen < sizeof(pki)) { |
|
|
|
return -TARGET_EINVAL; |
|
|
|
} |
|
|
|
|
|
|
|
if (copy_from_user(&pki, optval_addr, sizeof(pki))) { |
|
|
|
return -TARGET_EFAULT; |
|
|
|
} |
|
|
|
|
|
|
|
pki.ipi6_ifindex = tswap32(pki.ipi6_ifindex); |
|
|
|
|
|
|
|
ret = get_errno(setsockopt(sockfd, level, optname, |
|
|
|
&pki, sizeof(pki))); |
|
|
|
break; |
|
|
|
} |
|
|
|
default: |
|
|
|
goto unimplemented; |
|
|
|
} |
|
|
|
break; |
|
|
|
case SOL_ICMPV6: |
|
|
|
switch (optname) { |
|
|
|
case ICMPV6_FILTER: |
|
|
|
{ |
|
|
|
struct icmp6_filter icmp6f; |
|
|
|
|
|
|
|
if (optlen > sizeof(icmp6f)) { |
|
|
|
optlen = sizeof(icmp6f); |
|
|
|
} |
|
|
|
|
|
|
|
if (copy_from_user(&icmp6f, optval_addr, optlen)) { |
|
|
|
return -TARGET_EFAULT; |
|
|
|
} |
|
|
|
|
|
|
|
for (val = 0; val < 8; val++) { |
|
|
|
icmp6f.data[val] = tswap32(icmp6f.data[val]); |
|
|
|
} |
|
|
|
|
|
|
|
ret = get_errno(setsockopt(sockfd, level, optname, |
|
|
|
&icmp6f, optlen)); |
|
|
|
break; |
|
|
|
} |
|
|
|
default: |
|
|
|
goto unimplemented; |
|
|
|
} |
|
|
|
@ -2834,7 +2963,8 @@ static abi_long do_setsockopt(int sockfd, int level, int optname, |
|
|
|
case SOL_RAW: |
|
|
|
switch (optname) { |
|
|
|
case ICMP_FILTER: |
|
|
|
/* struct icmp_filter takes an u32 value */ |
|
|
|
case IPV6_CHECKSUM: |
|
|
|
/* those take an u32 value */ |
|
|
|
if (optlen < sizeof(uint32_t)) { |
|
|
|
return -TARGET_EINVAL; |
|
|
|
} |
|
|
|
|