qemu/linux-user/fd-trans.c
Paul Brook 33f53ac52a linux-user: Fix inotify on aarch64
The inotify implementation originally called the raw host syscalls.
Commit 3b3f24add0 changed this to use the glibc wrappers. However ifdefs
in syscall.c still test for presence of the raw syscalls.

This causes a problem on e.g. aarch64 hosts which never had the
inotify_init syscall - it had been obsoleted by inotify_init1 before
aarch64 was invented! However it does have a perfectly good glibc
implementation of inotify_wait.

Fix this by removing all the raw __NR_inotify_* tests, and instead check
CONFIG_INOTIFY, which already tests for the glibc functionality we use.

Also remove the now-pointless sys_inotify* wrappers.

Tested using x86-64 inotifywatch on aarch64 host, and vice-versa

Signed-off-by: Paul Brook <paul@nowt.org>
Reviewed-by: Laurent Vivier <laurent@vivier.eu>
Message-Id: <20220126202636.655289-1-paul@nowt.org>
Signed-off-by: Laurent Vivier <laurent@vivier.eu>
2022-01-27 16:58:33 +01:00

1672 lines
50 KiB
C

/*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
#include "qemu/osdep.h"
#include <sys/signalfd.h>
#include <linux/unistd.h>
#include <linux/audit.h>
#ifdef CONFIG_INOTIFY
#include <sys/inotify.h>
#endif
#include <linux/netlink.h>
#ifdef CONFIG_RTNETLINK
#include <linux/rtnetlink.h>
#include <linux/if_bridge.h>
#endif
#include "qemu.h"
#include "user-internals.h"
#include "fd-trans.h"
#include "signal-common.h"
enum {
QEMU_IFLA_BR_UNSPEC,
QEMU_IFLA_BR_FORWARD_DELAY,
QEMU_IFLA_BR_HELLO_TIME,
QEMU_IFLA_BR_MAX_AGE,
QEMU_IFLA_BR_AGEING_TIME,
QEMU_IFLA_BR_STP_STATE,
QEMU_IFLA_BR_PRIORITY,
QEMU_IFLA_BR_VLAN_FILTERING,
QEMU_IFLA_BR_VLAN_PROTOCOL,
QEMU_IFLA_BR_GROUP_FWD_MASK,
QEMU_IFLA_BR_ROOT_ID,
QEMU_IFLA_BR_BRIDGE_ID,
QEMU_IFLA_BR_ROOT_PORT,
QEMU_IFLA_BR_ROOT_PATH_COST,
QEMU_IFLA_BR_TOPOLOGY_CHANGE,
QEMU_IFLA_BR_TOPOLOGY_CHANGE_DETECTED,
QEMU_IFLA_BR_HELLO_TIMER,
QEMU_IFLA_BR_TCN_TIMER,
QEMU_IFLA_BR_TOPOLOGY_CHANGE_TIMER,
QEMU_IFLA_BR_GC_TIMER,
QEMU_IFLA_BR_GROUP_ADDR,
QEMU_IFLA_BR_FDB_FLUSH,
QEMU_IFLA_BR_MCAST_ROUTER,
QEMU_IFLA_BR_MCAST_SNOOPING,
QEMU_IFLA_BR_MCAST_QUERY_USE_IFADDR,
QEMU_IFLA_BR_MCAST_QUERIER,
QEMU_IFLA_BR_MCAST_HASH_ELASTICITY,
QEMU_IFLA_BR_MCAST_HASH_MAX,
QEMU_IFLA_BR_MCAST_LAST_MEMBER_CNT,
QEMU_IFLA_BR_MCAST_STARTUP_QUERY_CNT,
QEMU_IFLA_BR_MCAST_LAST_MEMBER_INTVL,
QEMU_IFLA_BR_MCAST_MEMBERSHIP_INTVL,
QEMU_IFLA_BR_MCAST_QUERIER_INTVL,
QEMU_IFLA_BR_MCAST_QUERY_INTVL,
QEMU_IFLA_BR_MCAST_QUERY_RESPONSE_INTVL,
QEMU_IFLA_BR_MCAST_STARTUP_QUERY_INTVL,
QEMU_IFLA_BR_NF_CALL_IPTABLES,
QEMU_IFLA_BR_NF_CALL_IP6TABLES,
QEMU_IFLA_BR_NF_CALL_ARPTABLES,
QEMU_IFLA_BR_VLAN_DEFAULT_PVID,
QEMU_IFLA_BR_PAD,
QEMU_IFLA_BR_VLAN_STATS_ENABLED,
QEMU_IFLA_BR_MCAST_STATS_ENABLED,
QEMU_IFLA_BR_MCAST_IGMP_VERSION,
QEMU_IFLA_BR_MCAST_MLD_VERSION,
QEMU_IFLA_BR_VLAN_STATS_PER_PORT,
QEMU_IFLA_BR_MULTI_BOOLOPT,
QEMU___IFLA_BR_MAX,
};
enum {
QEMU_IFLA_UNSPEC,
QEMU_IFLA_ADDRESS,
QEMU_IFLA_BROADCAST,
QEMU_IFLA_IFNAME,
QEMU_IFLA_MTU,
QEMU_IFLA_LINK,
QEMU_IFLA_QDISC,
QEMU_IFLA_STATS,
QEMU_IFLA_COST,
QEMU_IFLA_PRIORITY,
QEMU_IFLA_MASTER,
QEMU_IFLA_WIRELESS,
QEMU_IFLA_PROTINFO,
QEMU_IFLA_TXQLEN,
QEMU_IFLA_MAP,
QEMU_IFLA_WEIGHT,
QEMU_IFLA_OPERSTATE,
QEMU_IFLA_LINKMODE,
QEMU_IFLA_LINKINFO,
QEMU_IFLA_NET_NS_PID,
QEMU_IFLA_IFALIAS,
QEMU_IFLA_NUM_VF,
QEMU_IFLA_VFINFO_LIST,
QEMU_IFLA_STATS64,
QEMU_IFLA_VF_PORTS,
QEMU_IFLA_PORT_SELF,
QEMU_IFLA_AF_SPEC,
QEMU_IFLA_GROUP,
QEMU_IFLA_NET_NS_FD,
QEMU_IFLA_EXT_MASK,
QEMU_IFLA_PROMISCUITY,
QEMU_IFLA_NUM_TX_QUEUES,
QEMU_IFLA_NUM_RX_QUEUES,
QEMU_IFLA_CARRIER,
QEMU_IFLA_PHYS_PORT_ID,
QEMU_IFLA_CARRIER_CHANGES,
QEMU_IFLA_PHYS_SWITCH_ID,
QEMU_IFLA_LINK_NETNSID,
QEMU_IFLA_PHYS_PORT_NAME,
QEMU_IFLA_PROTO_DOWN,
QEMU_IFLA_GSO_MAX_SEGS,
QEMU_IFLA_GSO_MAX_SIZE,
QEMU_IFLA_PAD,
QEMU_IFLA_XDP,
QEMU_IFLA_EVENT,
QEMU_IFLA_NEW_NETNSID,
QEMU_IFLA_IF_NETNSID,
QEMU_IFLA_CARRIER_UP_COUNT,
QEMU_IFLA_CARRIER_DOWN_COUNT,
QEMU_IFLA_NEW_IFINDEX,
QEMU_IFLA_MIN_MTU,
QEMU_IFLA_MAX_MTU,
QEMU_IFLA_PROP_LIST,
QEMU_IFLA_ALT_IFNAME,
QEMU_IFLA_PERM_ADDRESS,
QEMU_IFLA_PROTO_DOWN_REASON,
QEMU_IFLA_PARENT_DEV_NAME,
QEMU_IFLA_PARENT_DEV_BUS_NAME,
QEMU___IFLA_MAX
};
enum {
QEMU_IFLA_BRPORT_UNSPEC,
QEMU_IFLA_BRPORT_STATE,
QEMU_IFLA_BRPORT_PRIORITY,
QEMU_IFLA_BRPORT_COST,
QEMU_IFLA_BRPORT_MODE,
QEMU_IFLA_BRPORT_GUARD,
QEMU_IFLA_BRPORT_PROTECT,
QEMU_IFLA_BRPORT_FAST_LEAVE,
QEMU_IFLA_BRPORT_LEARNING,
QEMU_IFLA_BRPORT_UNICAST_FLOOD,
QEMU_IFLA_BRPORT_PROXYARP,
QEMU_IFLA_BRPORT_LEARNING_SYNC,
QEMU_IFLA_BRPORT_PROXYARP_WIFI,
QEMU_IFLA_BRPORT_ROOT_ID,
QEMU_IFLA_BRPORT_BRIDGE_ID,
QEMU_IFLA_BRPORT_DESIGNATED_PORT,
QEMU_IFLA_BRPORT_DESIGNATED_COST,
QEMU_IFLA_BRPORT_ID,
QEMU_IFLA_BRPORT_NO,
QEMU_IFLA_BRPORT_TOPOLOGY_CHANGE_ACK,
QEMU_IFLA_BRPORT_CONFIG_PENDING,
QEMU_IFLA_BRPORT_MESSAGE_AGE_TIMER,
QEMU_IFLA_BRPORT_FORWARD_DELAY_TIMER,
QEMU_IFLA_BRPORT_HOLD_TIMER,
QEMU_IFLA_BRPORT_FLUSH,
QEMU_IFLA_BRPORT_MULTICAST_ROUTER,
QEMU_IFLA_BRPORT_PAD,
QEMU_IFLA_BRPORT_MCAST_FLOOD,
QEMU_IFLA_BRPORT_MCAST_TO_UCAST,
QEMU_IFLA_BRPORT_VLAN_TUNNEL,
QEMU_IFLA_BRPORT_BCAST_FLOOD,
QEMU_IFLA_BRPORT_GROUP_FWD_MASK,
QEMU_IFLA_BRPORT_NEIGH_SUPPRESS,
QEMU_IFLA_BRPORT_ISOLATED,
QEMU_IFLA_BRPORT_BACKUP_PORT,
QEMU_IFLA_BRPORT_MRP_RING_OPEN,
QEMU_IFLA_BRPORT_MRP_IN_OPEN,
QEMU_IFLA_BRPORT_MCAST_EHT_HOSTS_LIMIT,
QEMU_IFLA_BRPORT_MCAST_EHT_HOSTS_CNT,
QEMU___IFLA_BRPORT_MAX
};
enum {
QEMU_IFLA_TUN_UNSPEC,
QEMU_IFLA_TUN_OWNER,
QEMU_IFLA_TUN_GROUP,
QEMU_IFLA_TUN_TYPE,
QEMU_IFLA_TUN_PI,
QEMU_IFLA_TUN_VNET_HDR,
QEMU_IFLA_TUN_PERSIST,
QEMU_IFLA_TUN_MULTI_QUEUE,
QEMU_IFLA_TUN_NUM_QUEUES,
QEMU_IFLA_TUN_NUM_DISABLED_QUEUES,
QEMU___IFLA_TUN_MAX,
};
enum {
QEMU_IFLA_INFO_UNSPEC,
QEMU_IFLA_INFO_KIND,
QEMU_IFLA_INFO_DATA,
QEMU_IFLA_INFO_XSTATS,
QEMU_IFLA_INFO_SLAVE_KIND,
QEMU_IFLA_INFO_SLAVE_DATA,
QEMU___IFLA_INFO_MAX,
};
enum {
QEMU_IFLA_INET_UNSPEC,
QEMU_IFLA_INET_CONF,
QEMU___IFLA_INET_MAX,
};
enum {
QEMU_IFLA_INET6_UNSPEC,
QEMU_IFLA_INET6_FLAGS,
QEMU_IFLA_INET6_CONF,
QEMU_IFLA_INET6_STATS,
QEMU_IFLA_INET6_MCAST,
QEMU_IFLA_INET6_CACHEINFO,
QEMU_IFLA_INET6_ICMP6STATS,
QEMU_IFLA_INET6_TOKEN,
QEMU_IFLA_INET6_ADDR_GEN_MODE,
QEMU___IFLA_INET6_MAX
};
enum {
QEMU_IFLA_XDP_UNSPEC,
QEMU_IFLA_XDP_FD,
QEMU_IFLA_XDP_ATTACHED,
QEMU_IFLA_XDP_FLAGS,
QEMU_IFLA_XDP_PROG_ID,
QEMU___IFLA_XDP_MAX,
};
enum {
QEMU_RTA_UNSPEC,
QEMU_RTA_DST,
QEMU_RTA_SRC,
QEMU_RTA_IIF,
QEMU_RTA_OIF,
QEMU_RTA_GATEWAY,
QEMU_RTA_PRIORITY,
QEMU_RTA_PREFSRC,
QEMU_RTA_METRICS,
QEMU_RTA_MULTIPATH,
QEMU_RTA_PROTOINFO, /* no longer used */
QEMU_RTA_FLOW,
QEMU_RTA_CACHEINFO,
QEMU_RTA_SESSION, /* no longer used */
QEMU_RTA_MP_ALGO, /* no longer used */
QEMU_RTA_TABLE,
QEMU_RTA_MARK,
QEMU_RTA_MFC_STATS,
QEMU_RTA_VIA,
QEMU_RTA_NEWDST,
QEMU_RTA_PREF,
QEMU_RTA_ENCAP_TYPE,
QEMU_RTA_ENCAP,
QEMU_RTA_EXPIRES,
QEMU_RTA_PAD,
QEMU_RTA_UID,
QEMU_RTA_TTL_PROPAGATE,
QEMU_RTA_IP_PROTO,
QEMU_RTA_SPORT,
QEMU_RTA_DPORT,
QEMU___RTA_MAX
};
enum {
QEMU_IFLA_VF_STATS_RX_PACKETS,
QEMU_IFLA_VF_STATS_TX_PACKETS,
QEMU_IFLA_VF_STATS_RX_BYTES,
QEMU_IFLA_VF_STATS_TX_BYTES,
QEMU_IFLA_VF_STATS_BROADCAST,
QEMU_IFLA_VF_STATS_MULTICAST,
QEMU_IFLA_VF_STATS_PAD,
QEMU_IFLA_VF_STATS_RX_DROPPED,
QEMU_IFLA_VF_STATS_TX_DROPPED,
QEMU__IFLA_VF_STATS_MAX,
};
enum {
QEMU_IFLA_VF_UNSPEC,
QEMU_IFLA_VF_MAC,
QEMU_IFLA_VF_VLAN,
QEMU_IFLA_VF_TX_RATE,
QEMU_IFLA_VF_SPOOFCHK,
QEMU_IFLA_VF_LINK_STATE,
QEMU_IFLA_VF_RATE,
QEMU_IFLA_VF_RSS_QUERY_EN,
QEMU_IFLA_VF_STATS,
QEMU_IFLA_VF_TRUST,
QEMU_IFLA_VF_IB_NODE_GUID,
QEMU_IFLA_VF_IB_PORT_GUID,
QEMU_IFLA_VF_VLAN_LIST,
QEMU_IFLA_VF_BROADCAST,
QEMU__IFLA_VF_MAX,
};
TargetFdTrans **target_fd_trans;
QemuMutex target_fd_trans_lock;
unsigned int target_fd_max;
static void tswap_nlmsghdr(struct nlmsghdr *nlh)
{
nlh->nlmsg_len = tswap32(nlh->nlmsg_len);
nlh->nlmsg_type = tswap16(nlh->nlmsg_type);
nlh->nlmsg_flags = tswap16(nlh->nlmsg_flags);
nlh->nlmsg_seq = tswap32(nlh->nlmsg_seq);
nlh->nlmsg_pid = tswap32(nlh->nlmsg_pid);
}
static abi_long host_to_target_for_each_nlmsg(struct nlmsghdr *nlh,
size_t len,
abi_long (*host_to_target_nlmsg)
(struct nlmsghdr *))
{
uint32_t nlmsg_len;
uint32_t aligned_nlmsg_len;
abi_long ret;
while (len > sizeof(struct nlmsghdr)) {
nlmsg_len = nlh->nlmsg_len;
if (nlmsg_len < sizeof(struct nlmsghdr) ||
nlmsg_len > len) {
break;
}
switch (nlh->nlmsg_type) {
case NLMSG_DONE:
tswap_nlmsghdr(nlh);
return 0;
case NLMSG_NOOP:
break;
case NLMSG_ERROR:
{
struct nlmsgerr *e = NLMSG_DATA(nlh);
e->error = tswap32(e->error);
tswap_nlmsghdr(&e->msg);
tswap_nlmsghdr(nlh);
return 0;
}
default:
ret = host_to_target_nlmsg(nlh);
if (ret < 0) {
tswap_nlmsghdr(nlh);
return ret;
}
break;
}
tswap_nlmsghdr(nlh);
aligned_nlmsg_len = NLMSG_ALIGN(nlmsg_len);
if (aligned_nlmsg_len >= len) {
break;
}
len -= aligned_nlmsg_len;
nlh = (struct nlmsghdr *)(((char*)nlh) + aligned_nlmsg_len);
}
return 0;
}
static abi_long target_to_host_for_each_nlmsg(struct nlmsghdr *nlh,
size_t len,
abi_long (*target_to_host_nlmsg)
(struct nlmsghdr *))
{
uint32_t aligned_nlmsg_len;
int ret;
while (len > sizeof(struct nlmsghdr)) {
if (tswap32(nlh->nlmsg_len) < sizeof(struct nlmsghdr) ||
tswap32(nlh->nlmsg_len) > len) {
break;
}
tswap_nlmsghdr(nlh);
switch (nlh->nlmsg_type) {
case NLMSG_DONE:
return 0;
case NLMSG_NOOP:
break;
case NLMSG_ERROR:
{
struct nlmsgerr *e = NLMSG_DATA(nlh);
e->error = tswap32(e->error);
tswap_nlmsghdr(&e->msg);
return 0;
}
default:
ret = target_to_host_nlmsg(nlh);
if (ret < 0) {
return ret;
}
}
aligned_nlmsg_len = NLMSG_ALIGN(nlh->nlmsg_len);
if (aligned_nlmsg_len >= len) {
break;
}
len -= aligned_nlmsg_len;
nlh = (struct nlmsghdr *)(((char *)nlh) + aligned_nlmsg_len);
}
return 0;
}
#ifdef CONFIG_RTNETLINK
static abi_long host_to_target_for_each_nlattr(struct nlattr *nlattr,
size_t len, void *context,
abi_long (*host_to_target_nlattr)
(struct nlattr *,
void *context))
{
unsigned short nla_len;
unsigned short aligned_nla_len;
abi_long ret;
while (len > sizeof(struct nlattr)) {
nla_len = nlattr->nla_len;
if (nla_len < sizeof(struct nlattr) ||
nla_len > len) {
break;
}
ret = host_to_target_nlattr(nlattr, context);
nlattr->nla_len = tswap16(nlattr->nla_len);
nlattr->nla_type = tswap16(nlattr->nla_type);
if (ret < 0) {
return ret;
}
aligned_nla_len = NLA_ALIGN(nla_len);
if (aligned_nla_len >= len) {
break;
}
len -= aligned_nla_len;
nlattr = (struct nlattr *)(((char *)nlattr) + aligned_nla_len);
}
return 0;
}
static abi_long host_to_target_for_each_rtattr(struct rtattr *rtattr,
size_t len,
abi_long (*host_to_target_rtattr)
(struct rtattr *))
{
unsigned short rta_len;
unsigned short aligned_rta_len;
abi_long ret;
while (len > sizeof(struct rtattr)) {
rta_len = rtattr->rta_len;
if (rta_len < sizeof(struct rtattr) ||
rta_len > len) {
break;
}
ret = host_to_target_rtattr(rtattr);
rtattr->rta_len = tswap16(rtattr->rta_len);
rtattr->rta_type = tswap16(rtattr->rta_type);
if (ret < 0) {
return ret;
}
aligned_rta_len = RTA_ALIGN(rta_len);
if (aligned_rta_len >= len) {
break;
}
len -= aligned_rta_len;
rtattr = (struct rtattr *)(((char *)rtattr) + aligned_rta_len);
}
return 0;
}
#define NLA_DATA(nla) ((void *)((char *)(nla)) + NLA_HDRLEN)
static abi_long host_to_target_data_bridge_nlattr(struct nlattr *nlattr,
void *context)
{
uint16_t *u16;
uint32_t *u32;
uint64_t *u64;
switch (nlattr->nla_type) {
/* no data */
case QEMU_IFLA_BR_FDB_FLUSH:
break;
/* binary */
case QEMU_IFLA_BR_GROUP_ADDR:
break;
/* uint8_t */
case QEMU_IFLA_BR_VLAN_FILTERING:
case QEMU_IFLA_BR_TOPOLOGY_CHANGE:
case QEMU_IFLA_BR_TOPOLOGY_CHANGE_DETECTED:
case QEMU_IFLA_BR_MCAST_ROUTER:
case QEMU_IFLA_BR_MCAST_SNOOPING:
case QEMU_IFLA_BR_MCAST_QUERY_USE_IFADDR:
case QEMU_IFLA_BR_MCAST_QUERIER:
case QEMU_IFLA_BR_NF_CALL_IPTABLES:
case QEMU_IFLA_BR_NF_CALL_IP6TABLES:
case QEMU_IFLA_BR_NF_CALL_ARPTABLES:
case QEMU_IFLA_BR_VLAN_STATS_ENABLED:
case QEMU_IFLA_BR_MCAST_STATS_ENABLED:
case QEMU_IFLA_BR_MCAST_IGMP_VERSION:
case QEMU_IFLA_BR_MCAST_MLD_VERSION:
case QEMU_IFLA_BR_VLAN_STATS_PER_PORT:
break;
/* uint16_t */
case QEMU_IFLA_BR_PRIORITY:
case QEMU_IFLA_BR_VLAN_PROTOCOL:
case QEMU_IFLA_BR_GROUP_FWD_MASK:
case QEMU_IFLA_BR_ROOT_PORT:
case QEMU_IFLA_BR_VLAN_DEFAULT_PVID:
u16 = NLA_DATA(nlattr);
*u16 = tswap16(*u16);
break;
/* uint32_t */
case QEMU_IFLA_BR_FORWARD_DELAY:
case QEMU_IFLA_BR_HELLO_TIME:
case QEMU_IFLA_BR_MAX_AGE:
case QEMU_IFLA_BR_AGEING_TIME:
case QEMU_IFLA_BR_STP_STATE:
case QEMU_IFLA_BR_ROOT_PATH_COST:
case QEMU_IFLA_BR_MCAST_HASH_ELASTICITY:
case QEMU_IFLA_BR_MCAST_HASH_MAX:
case QEMU_IFLA_BR_MCAST_LAST_MEMBER_CNT:
case QEMU_IFLA_BR_MCAST_STARTUP_QUERY_CNT:
u32 = NLA_DATA(nlattr);
*u32 = tswap32(*u32);
break;
/* uint64_t */
case QEMU_IFLA_BR_HELLO_TIMER:
case QEMU_IFLA_BR_TCN_TIMER:
case QEMU_IFLA_BR_GC_TIMER:
case QEMU_IFLA_BR_TOPOLOGY_CHANGE_TIMER:
case QEMU_IFLA_BR_MCAST_LAST_MEMBER_INTVL:
case QEMU_IFLA_BR_MCAST_MEMBERSHIP_INTVL:
case QEMU_IFLA_BR_MCAST_QUERIER_INTVL:
case QEMU_IFLA_BR_MCAST_QUERY_INTVL:
case QEMU_IFLA_BR_MCAST_QUERY_RESPONSE_INTVL:
case QEMU_IFLA_BR_MCAST_STARTUP_QUERY_INTVL:
u64 = NLA_DATA(nlattr);
*u64 = tswap64(*u64);
break;
/* ifla_bridge_id: uin8_t[] */
case QEMU_IFLA_BR_ROOT_ID:
case QEMU_IFLA_BR_BRIDGE_ID:
break;
/* br_boolopt_multi { uint32_t, uint32_t } */
case QEMU_IFLA_BR_MULTI_BOOLOPT:
u32 = NLA_DATA(nlattr);
u32[0] = tswap32(u32[0]); /* optval */
u32[1] = tswap32(u32[1]); /* optmask */
break;
default:
qemu_log_mask(LOG_UNIMP, "Unknown QEMU_IFLA_BR type %d\n",
nlattr->nla_type);
break;
}
return 0;
}
static abi_long host_to_target_slave_data_bridge_nlattr(struct nlattr *nlattr,
void *context)
{
uint16_t *u16;
uint32_t *u32;
uint64_t *u64;
switch (nlattr->nla_type) {
/* uint8_t */
case QEMU_IFLA_BRPORT_STATE:
case QEMU_IFLA_BRPORT_MODE:
case QEMU_IFLA_BRPORT_GUARD:
case QEMU_IFLA_BRPORT_PROTECT:
case QEMU_IFLA_BRPORT_FAST_LEAVE:
case QEMU_IFLA_BRPORT_LEARNING:
case QEMU_IFLA_BRPORT_UNICAST_FLOOD:
case QEMU_IFLA_BRPORT_PROXYARP:
case QEMU_IFLA_BRPORT_LEARNING_SYNC:
case QEMU_IFLA_BRPORT_PROXYARP_WIFI:
case QEMU_IFLA_BRPORT_TOPOLOGY_CHANGE_ACK:
case QEMU_IFLA_BRPORT_CONFIG_PENDING:
case QEMU_IFLA_BRPORT_MULTICAST_ROUTER:
case QEMU_IFLA_BRPORT_MCAST_FLOOD:
case QEMU_IFLA_BRPORT_MCAST_TO_UCAST:
case QEMU_IFLA_BRPORT_VLAN_TUNNEL:
case QEMU_IFLA_BRPORT_BCAST_FLOOD:
case QEMU_IFLA_BRPORT_NEIGH_SUPPRESS:
case QEMU_IFLA_BRPORT_ISOLATED:
case QEMU_IFLA_BRPORT_MRP_RING_OPEN:
case QEMU_IFLA_BRPORT_MRP_IN_OPEN:
break;
/* uint16_t */
case QEMU_IFLA_BRPORT_PRIORITY:
case QEMU_IFLA_BRPORT_DESIGNATED_PORT:
case QEMU_IFLA_BRPORT_DESIGNATED_COST:
case QEMU_IFLA_BRPORT_ID:
case QEMU_IFLA_BRPORT_NO:
case QEMU_IFLA_BRPORT_GROUP_FWD_MASK:
u16 = NLA_DATA(nlattr);
*u16 = tswap16(*u16);
break;
/* uin32_t */
case QEMU_IFLA_BRPORT_COST:
case QEMU_IFLA_BRPORT_BACKUP_PORT:
case QEMU_IFLA_BRPORT_MCAST_EHT_HOSTS_LIMIT:
case QEMU_IFLA_BRPORT_MCAST_EHT_HOSTS_CNT:
u32 = NLA_DATA(nlattr);
*u32 = tswap32(*u32);
break;
/* uint64_t */
case QEMU_IFLA_BRPORT_MESSAGE_AGE_TIMER:
case QEMU_IFLA_BRPORT_FORWARD_DELAY_TIMER:
case QEMU_IFLA_BRPORT_HOLD_TIMER:
u64 = NLA_DATA(nlattr);
*u64 = tswap64(*u64);
break;
/* ifla_bridge_id: uint8_t[] */
case QEMU_IFLA_BRPORT_ROOT_ID:
case QEMU_IFLA_BRPORT_BRIDGE_ID:
break;
default:
qemu_log_mask(LOG_UNIMP, "Unknown QEMU_IFLA_BRPORT type %d\n",
nlattr->nla_type);
break;
}
return 0;
}
static abi_long host_to_target_data_tun_nlattr(struct nlattr *nlattr,
void *context)
{
uint32_t *u32;
switch (nlattr->nla_type) {
/* uint8_t */
case QEMU_IFLA_TUN_TYPE:
case QEMU_IFLA_TUN_PI:
case QEMU_IFLA_TUN_VNET_HDR:
case QEMU_IFLA_TUN_PERSIST:
case QEMU_IFLA_TUN_MULTI_QUEUE:
break;
/* uint32_t */
case QEMU_IFLA_TUN_NUM_QUEUES:
case QEMU_IFLA_TUN_NUM_DISABLED_QUEUES:
case QEMU_IFLA_TUN_OWNER:
case QEMU_IFLA_TUN_GROUP:
u32 = NLA_DATA(nlattr);
*u32 = tswap32(*u32);
break;
default:
qemu_log_mask(LOG_UNIMP, "Unknown QEMU_IFLA_TUN type %d\n",
nlattr->nla_type);
break;
}
return 0;
}
struct linkinfo_context {
int len;
char *name;
int slave_len;
char *slave_name;
};
static abi_long host_to_target_data_linkinfo_nlattr(struct nlattr *nlattr,
void *context)
{
struct linkinfo_context *li_context = context;
switch (nlattr->nla_type) {
/* string */
case QEMU_IFLA_INFO_KIND:
li_context->name = NLA_DATA(nlattr);
li_context->len = nlattr->nla_len - NLA_HDRLEN;
break;
case QEMU_IFLA_INFO_SLAVE_KIND:
li_context->slave_name = NLA_DATA(nlattr);
li_context->slave_len = nlattr->nla_len - NLA_HDRLEN;
break;
/* stats */
case QEMU_IFLA_INFO_XSTATS:
/* FIXME: only used by CAN */
break;
/* nested */
case QEMU_IFLA_INFO_DATA:
if (strncmp(li_context->name, "bridge",
li_context->len) == 0) {
return host_to_target_for_each_nlattr(NLA_DATA(nlattr),
nlattr->nla_len,
NULL,
host_to_target_data_bridge_nlattr);
} else if (strncmp(li_context->name, "tun",
li_context->len) == 0) {
return host_to_target_for_each_nlattr(NLA_DATA(nlattr),
nlattr->nla_len,
NULL,
host_to_target_data_tun_nlattr);
} else {
qemu_log_mask(LOG_UNIMP, "Unknown QEMU_IFLA_INFO_KIND %s\n",
li_context->name);
}
break;
case QEMU_IFLA_INFO_SLAVE_DATA:
if (strncmp(li_context->slave_name, "bridge",
li_context->slave_len) == 0) {
return host_to_target_for_each_nlattr(NLA_DATA(nlattr),
nlattr->nla_len,
NULL,
host_to_target_slave_data_bridge_nlattr);
} else {
qemu_log_mask(LOG_UNIMP, "Unknown QEMU_IFLA_INFO_SLAVE_KIND %s\n",
li_context->slave_name);
}
break;
default:
qemu_log_mask(LOG_UNIMP, "Unknown host QEMU_IFLA_INFO type: %d\n",
nlattr->nla_type);
break;
}
return 0;
}
static abi_long host_to_target_data_inet_nlattr(struct nlattr *nlattr,
void *context)
{
uint32_t *u32;
int i;
switch (nlattr->nla_type) {
case QEMU_IFLA_INET_CONF:
u32 = NLA_DATA(nlattr);
for (i = 0; i < (nlattr->nla_len - NLA_HDRLEN) / sizeof(*u32);
i++) {
u32[i] = tswap32(u32[i]);
}
break;
default:
qemu_log_mask(LOG_UNIMP, "Unknown host AF_INET type: %d\n",
nlattr->nla_type);
}
return 0;
}
static abi_long host_to_target_data_inet6_nlattr(struct nlattr *nlattr,
void *context)
{
uint32_t *u32;
uint64_t *u64;
struct ifla_cacheinfo *ci;
int i;
switch (nlattr->nla_type) {
/* binaries */
case QEMU_IFLA_INET6_TOKEN:
break;
/* uint8_t */
case QEMU_IFLA_INET6_ADDR_GEN_MODE:
break;
/* uint32_t */
case QEMU_IFLA_INET6_FLAGS:
u32 = NLA_DATA(nlattr);
*u32 = tswap32(*u32);
break;
/* uint32_t[] */
case QEMU_IFLA_INET6_CONF:
u32 = NLA_DATA(nlattr);
for (i = 0; i < (nlattr->nla_len - NLA_HDRLEN) / sizeof(*u32);
i++) {
u32[i] = tswap32(u32[i]);
}
break;
/* ifla_cacheinfo */
case QEMU_IFLA_INET6_CACHEINFO:
ci = NLA_DATA(nlattr);
ci->max_reasm_len = tswap32(ci->max_reasm_len);
ci->tstamp = tswap32(ci->tstamp);
ci->reachable_time = tswap32(ci->reachable_time);
ci->retrans_time = tswap32(ci->retrans_time);
break;
/* uint64_t[] */
case QEMU_IFLA_INET6_STATS:
case QEMU_IFLA_INET6_ICMP6STATS:
u64 = NLA_DATA(nlattr);
for (i = 0; i < (nlattr->nla_len - NLA_HDRLEN) / sizeof(*u64);
i++) {
u64[i] = tswap64(u64[i]);
}
break;
default:
qemu_log_mask(LOG_UNIMP, "Unknown host AF_INET6 type: %d\n",
nlattr->nla_type);
}
return 0;
}
static abi_long host_to_target_data_spec_nlattr(struct nlattr *nlattr,
void *context)
{
switch (nlattr->nla_type) {
case AF_INET:
return host_to_target_for_each_nlattr(NLA_DATA(nlattr), nlattr->nla_len,
NULL,
host_to_target_data_inet_nlattr);
case AF_INET6:
return host_to_target_for_each_nlattr(NLA_DATA(nlattr), nlattr->nla_len,
NULL,
host_to_target_data_inet6_nlattr);
default:
qemu_log_mask(LOG_UNIMP, "Unknown host AF_SPEC type: %d\n",
nlattr->nla_type);
break;
}
return 0;
}
static abi_long host_to_target_data_xdp_nlattr(struct nlattr *nlattr,
void *context)
{
uint32_t *u32;
switch (nlattr->nla_type) {
/* uint8_t */
case QEMU_IFLA_XDP_ATTACHED:
break;
/* uint32_t */
case QEMU_IFLA_XDP_PROG_ID:
u32 = NLA_DATA(nlattr);
*u32 = tswap32(*u32);
break;
default:
qemu_log_mask(
LOG_UNIMP, "Unknown host XDP type: %d\n", nlattr->nla_type);
break;
}
return 0;
}
static abi_long host_to_target_data_vlan_list_nlattr(struct nlattr *nlattr,
void *context)
{
struct ifla_vf_vlan_info *vlan_info;
switch (nlattr->nla_type) {
/* struct ifla_vf_vlan_info */
case IFLA_VF_VLAN_INFO:
vlan_info = NLA_DATA(nlattr);
vlan_info->vf = tswap32(vlan_info->vf);
vlan_info->vlan = tswap32(vlan_info->vlan);
vlan_info->qos = tswap32(vlan_info->qos);
break;
default:
qemu_log_mask(LOG_UNIMP, "Unknown host VLAN LIST type: %d\n",
nlattr->nla_type);
break;
}
return 0;
}
static abi_long host_to_target_data_vf_stats_nlattr(struct nlattr *nlattr,
void *context)
{
uint64_t *u64;
switch (nlattr->nla_type) {
/* uint64_t */
case QEMU_IFLA_VF_STATS_RX_PACKETS:
case QEMU_IFLA_VF_STATS_TX_PACKETS:
case QEMU_IFLA_VF_STATS_RX_BYTES:
case QEMU_IFLA_VF_STATS_TX_BYTES:
case QEMU_IFLA_VF_STATS_BROADCAST:
case QEMU_IFLA_VF_STATS_MULTICAST:
case QEMU_IFLA_VF_STATS_PAD:
case QEMU_IFLA_VF_STATS_RX_DROPPED:
case QEMU_IFLA_VF_STATS_TX_DROPPED:
u64 = NLA_DATA(nlattr);
*u64 = tswap64(*u64);
break;
default:
qemu_log_mask(LOG_UNIMP, "Unknown host VF STATS type: %d\n",
nlattr->nla_type);
break;
}
return 0;
}
static abi_long host_to_target_data_vfinfo_nlattr(struct nlattr *nlattr,
void *context)
{
struct ifla_vf_mac *mac;
struct ifla_vf_vlan *vlan;
struct ifla_vf_vlan_info *vlan_info;
struct ifla_vf_spoofchk *spoofchk;
struct ifla_vf_rate *rate;
struct ifla_vf_link_state *link_state;
struct ifla_vf_rss_query_en *rss_query_en;
struct ifla_vf_trust *trust;
struct ifla_vf_guid *guid;
switch (nlattr->nla_type) {
/* struct ifla_vf_mac */
case QEMU_IFLA_VF_MAC:
mac = NLA_DATA(nlattr);
mac->vf = tswap32(mac->vf);
break;
/* struct ifla_vf_broadcast */
case QEMU_IFLA_VF_BROADCAST:
break;
/* struct struct ifla_vf_vlan */
case QEMU_IFLA_VF_VLAN:
vlan = NLA_DATA(nlattr);
vlan->vf = tswap32(vlan->vf);
vlan->vlan = tswap32(vlan->vlan);
vlan->qos = tswap32(vlan->qos);
break;
/* struct ifla_vf_vlan_info */
case QEMU_IFLA_VF_TX_RATE:
vlan_info = NLA_DATA(nlattr);
vlan_info->vf = tswap32(vlan_info->vf);
vlan_info->vlan = tswap32(vlan_info->vlan);
vlan_info->qos = tswap32(vlan_info->qos);
break;
/* struct ifla_vf_spoofchk */
case QEMU_IFLA_VF_SPOOFCHK:
spoofchk = NLA_DATA(nlattr);
spoofchk->vf = tswap32(spoofchk->vf);
spoofchk->setting = tswap32(spoofchk->setting);
break;
/* struct ifla_vf_rate */
case QEMU_IFLA_VF_RATE:
rate = NLA_DATA(nlattr);
rate->vf = tswap32(rate->vf);
rate->min_tx_rate = tswap32(rate->min_tx_rate);
rate->max_tx_rate = tswap32(rate->max_tx_rate);
break;
/* struct ifla_vf_link_state */
case QEMU_IFLA_VF_LINK_STATE:
link_state = NLA_DATA(nlattr);
link_state->vf = tswap32(link_state->vf);
link_state->link_state = tswap32(link_state->link_state);
break;
/* struct ifla_vf_rss_query_en */
case QEMU_IFLA_VF_RSS_QUERY_EN:
rss_query_en = NLA_DATA(nlattr);
rss_query_en->vf = tswap32(rss_query_en->vf);
rss_query_en->setting = tswap32(rss_query_en->setting);
break;
/* struct ifla_vf_trust */
case QEMU_IFLA_VF_TRUST:
trust = NLA_DATA(nlattr);
trust->vf = tswap32(trust->vf);
trust->setting = tswap32(trust->setting);
break;
/* struct ifla_vf_guid */
case QEMU_IFLA_VF_IB_NODE_GUID:
case QEMU_IFLA_VF_IB_PORT_GUID:
guid = NLA_DATA(nlattr);
guid->vf = tswap32(guid->vf);
guid->guid = tswap32(guid->guid);
break;
/* nested */
case QEMU_IFLA_VF_VLAN_LIST:
return host_to_target_for_each_nlattr(RTA_DATA(nlattr), nlattr->nla_len,
NULL,
host_to_target_data_vlan_list_nlattr);
case QEMU_IFLA_VF_STATS:
return host_to_target_for_each_nlattr(RTA_DATA(nlattr), nlattr->nla_len,
NULL,
host_to_target_data_vf_stats_nlattr);
default:
qemu_log_mask(LOG_UNIMP, "Unknown host VFINFO type: %d\n",
nlattr->nla_type);
break;
}
return 0;
}
static abi_long host_to_target_data_link_rtattr(struct rtattr *rtattr)
{
uint32_t *u32;
struct rtnl_link_stats *st;
struct rtnl_link_stats64 *st64;
struct rtnl_link_ifmap *map;
struct linkinfo_context li_context;
switch (rtattr->rta_type) {
/* binary stream */
case QEMU_IFLA_ADDRESS:
case QEMU_IFLA_BROADCAST:
case QEMU_IFLA_PERM_ADDRESS:
case QEMU_IFLA_PHYS_PORT_ID:
/* string */
case QEMU_IFLA_IFNAME:
case QEMU_IFLA_QDISC:
case QEMU_IFLA_PARENT_DEV_NAME:
case QEMU_IFLA_PARENT_DEV_BUS_NAME:
break;
/* uin8_t */
case QEMU_IFLA_OPERSTATE:
case QEMU_IFLA_LINKMODE:
case QEMU_IFLA_CARRIER:
case QEMU_IFLA_PROTO_DOWN:
break;
/* uint32_t */
case QEMU_IFLA_MTU:
case QEMU_IFLA_LINK:
case QEMU_IFLA_WEIGHT:
case QEMU_IFLA_TXQLEN:
case QEMU_IFLA_CARRIER_CHANGES:
case QEMU_IFLA_NUM_RX_QUEUES:
case QEMU_IFLA_NUM_TX_QUEUES:
case QEMU_IFLA_PROMISCUITY:
case QEMU_IFLA_EXT_MASK:
case QEMU_IFLA_LINK_NETNSID:
case QEMU_IFLA_GROUP:
case QEMU_IFLA_MASTER:
case QEMU_IFLA_NUM_VF:
case QEMU_IFLA_GSO_MAX_SEGS:
case QEMU_IFLA_GSO_MAX_SIZE:
case QEMU_IFLA_CARRIER_UP_COUNT:
case QEMU_IFLA_CARRIER_DOWN_COUNT:
case QEMU_IFLA_MIN_MTU:
case QEMU_IFLA_MAX_MTU:
u32 = RTA_DATA(rtattr);
*u32 = tswap32(*u32);
break;
/* struct rtnl_link_stats */
case QEMU_IFLA_STATS:
st = RTA_DATA(rtattr);
st->rx_packets = tswap32(st->rx_packets);
st->tx_packets = tswap32(st->tx_packets);
st->rx_bytes = tswap32(st->rx_bytes);
st->tx_bytes = tswap32(st->tx_bytes);
st->rx_errors = tswap32(st->rx_errors);
st->tx_errors = tswap32(st->tx_errors);
st->rx_dropped = tswap32(st->rx_dropped);
st->tx_dropped = tswap32(st->tx_dropped);
st->multicast = tswap32(st->multicast);
st->collisions = tswap32(st->collisions);
/* detailed rx_errors: */
st->rx_length_errors = tswap32(st->rx_length_errors);
st->rx_over_errors = tswap32(st->rx_over_errors);
st->rx_crc_errors = tswap32(st->rx_crc_errors);
st->rx_frame_errors = tswap32(st->rx_frame_errors);
st->rx_fifo_errors = tswap32(st->rx_fifo_errors);
st->rx_missed_errors = tswap32(st->rx_missed_errors);
/* detailed tx_errors */
st->tx_aborted_errors = tswap32(st->tx_aborted_errors);
st->tx_carrier_errors = tswap32(st->tx_carrier_errors);
st->tx_fifo_errors = tswap32(st->tx_fifo_errors);
st->tx_heartbeat_errors = tswap32(st->tx_heartbeat_errors);
st->tx_window_errors = tswap32(st->tx_window_errors);
/* for cslip etc */
st->rx_compressed = tswap32(st->rx_compressed);
st->tx_compressed = tswap32(st->tx_compressed);
break;
/* struct rtnl_link_stats64 */
case QEMU_IFLA_STATS64:
st64 = RTA_DATA(rtattr);
st64->rx_packets = tswap64(st64->rx_packets);
st64->tx_packets = tswap64(st64->tx_packets);
st64->rx_bytes = tswap64(st64->rx_bytes);
st64->tx_bytes = tswap64(st64->tx_bytes);
st64->rx_errors = tswap64(st64->rx_errors);
st64->tx_errors = tswap64(st64->tx_errors);
st64->rx_dropped = tswap64(st64->rx_dropped);
st64->tx_dropped = tswap64(st64->tx_dropped);
st64->multicast = tswap64(st64->multicast);
st64->collisions = tswap64(st64->collisions);
/* detailed rx_errors: */
st64->rx_length_errors = tswap64(st64->rx_length_errors);
st64->rx_over_errors = tswap64(st64->rx_over_errors);
st64->rx_crc_errors = tswap64(st64->rx_crc_errors);
st64->rx_frame_errors = tswap64(st64->rx_frame_errors);
st64->rx_fifo_errors = tswap64(st64->rx_fifo_errors);
st64->rx_missed_errors = tswap64(st64->rx_missed_errors);
/* detailed tx_errors */
st64->tx_aborted_errors = tswap64(st64->tx_aborted_errors);
st64->tx_carrier_errors = tswap64(st64->tx_carrier_errors);
st64->tx_fifo_errors = tswap64(st64->tx_fifo_errors);
st64->tx_heartbeat_errors = tswap64(st64->tx_heartbeat_errors);
st64->tx_window_errors = tswap64(st64->tx_window_errors);
/* for cslip etc */
st64->rx_compressed = tswap64(st64->rx_compressed);
st64->tx_compressed = tswap64(st64->tx_compressed);
break;
/* struct rtnl_link_ifmap */
case QEMU_IFLA_MAP:
map = RTA_DATA(rtattr);
map->mem_start = tswap64(map->mem_start);
map->mem_end = tswap64(map->mem_end);
map->base_addr = tswap64(map->base_addr);
map->irq = tswap16(map->irq);
break;
/* nested */
case QEMU_IFLA_LINKINFO:
memset(&li_context, 0, sizeof(li_context));
return host_to_target_for_each_nlattr(RTA_DATA(rtattr), rtattr->rta_len,
&li_context,
host_to_target_data_linkinfo_nlattr);
case QEMU_IFLA_AF_SPEC:
return host_to_target_for_each_nlattr(RTA_DATA(rtattr), rtattr->rta_len,
NULL,
host_to_target_data_spec_nlattr);
case QEMU_IFLA_XDP:
return host_to_target_for_each_nlattr(RTA_DATA(rtattr), rtattr->rta_len,
NULL,
host_to_target_data_xdp_nlattr);
case QEMU_IFLA_VFINFO_LIST:
return host_to_target_for_each_nlattr(RTA_DATA(rtattr), rtattr->rta_len,
NULL,
host_to_target_data_vfinfo_nlattr);
default:
qemu_log_mask(LOG_UNIMP, "Unknown host QEMU_IFLA type: %d\n",
rtattr->rta_type);
break;
}
return 0;
}
static abi_long host_to_target_data_addr_rtattr(struct rtattr *rtattr)
{
uint32_t *u32;
struct ifa_cacheinfo *ci;
switch (rtattr->rta_type) {
/* binary: depends on family type */
case IFA_ADDRESS:
case IFA_LOCAL:
break;
/* string */
case IFA_LABEL:
break;
/* u32 */
case IFA_FLAGS:
case IFA_BROADCAST:
u32 = RTA_DATA(rtattr);
*u32 = tswap32(*u32);
break;
/* struct ifa_cacheinfo */
case IFA_CACHEINFO:
ci = RTA_DATA(rtattr);
ci->ifa_prefered = tswap32(ci->ifa_prefered);
ci->ifa_valid = tswap32(ci->ifa_valid);
ci->cstamp = tswap32(ci->cstamp);
ci->tstamp = tswap32(ci->tstamp);
break;
default:
qemu_log_mask(
LOG_UNIMP, "Unknown host IFA type: %d\n", rtattr->rta_type);
break;
}
return 0;
}
static abi_long host_to_target_data_route_rtattr(struct rtattr *rtattr)
{
uint32_t *u32;
struct rta_cacheinfo *ci;
switch (rtattr->rta_type) {
/* binary: depends on family type */
case QEMU_RTA_GATEWAY:
case QEMU_RTA_DST:
case QEMU_RTA_PREFSRC:
break;
/* u8 */
case QEMU_RTA_PREF:
break;
/* u32 */
case QEMU_RTA_PRIORITY:
case QEMU_RTA_TABLE:
case QEMU_RTA_OIF:
u32 = RTA_DATA(rtattr);
*u32 = tswap32(*u32);
break;
/* struct rta_cacheinfo */
case QEMU_RTA_CACHEINFO:
ci = RTA_DATA(rtattr);
ci->rta_clntref = tswap32(ci->rta_clntref);
ci->rta_lastuse = tswap32(ci->rta_lastuse);
ci->rta_expires = tswap32(ci->rta_expires);
ci->rta_error = tswap32(ci->rta_error);
ci->rta_used = tswap32(ci->rta_used);
#if defined(RTNETLINK_HAVE_PEERINFO)
ci->rta_id = tswap32(ci->rta_id);
ci->rta_ts = tswap32(ci->rta_ts);
ci->rta_tsage = tswap32(ci->rta_tsage);
#endif
break;
default:
qemu_log_mask(
LOG_UNIMP, "Unknown host RTA type: %d\n", rtattr->rta_type);
break;
}
return 0;
}
static abi_long host_to_target_link_rtattr(struct rtattr *rtattr,
uint32_t rtattr_len)
{
return host_to_target_for_each_rtattr(rtattr, rtattr_len,
host_to_target_data_link_rtattr);
}
static abi_long host_to_target_addr_rtattr(struct rtattr *rtattr,
uint32_t rtattr_len)
{
return host_to_target_for_each_rtattr(rtattr, rtattr_len,
host_to_target_data_addr_rtattr);
}
static abi_long host_to_target_route_rtattr(struct rtattr *rtattr,
uint32_t rtattr_len)
{
return host_to_target_for_each_rtattr(rtattr, rtattr_len,
host_to_target_data_route_rtattr);
}
static abi_long host_to_target_data_route(struct nlmsghdr *nlh)
{
uint32_t nlmsg_len;
struct ifinfomsg *ifi;
struct ifaddrmsg *ifa;
struct rtmsg *rtm;
nlmsg_len = nlh->nlmsg_len;
switch (nlh->nlmsg_type) {
case RTM_NEWLINK:
case RTM_DELLINK:
case RTM_GETLINK:
if (nlh->nlmsg_len >= NLMSG_LENGTH(sizeof(*ifi))) {
ifi = NLMSG_DATA(nlh);
ifi->ifi_type = tswap16(ifi->ifi_type);
ifi->ifi_index = tswap32(ifi->ifi_index);
ifi->ifi_flags = tswap32(ifi->ifi_flags);
ifi->ifi_change = tswap32(ifi->ifi_change);
host_to_target_link_rtattr(IFLA_RTA(ifi),
nlmsg_len - NLMSG_LENGTH(sizeof(*ifi)));
}
break;
case RTM_NEWADDR:
case RTM_DELADDR:
case RTM_GETADDR:
if (nlh->nlmsg_len >= NLMSG_LENGTH(sizeof(*ifa))) {
ifa = NLMSG_DATA(nlh);
ifa->ifa_index = tswap32(ifa->ifa_index);
host_to_target_addr_rtattr(IFA_RTA(ifa),
nlmsg_len - NLMSG_LENGTH(sizeof(*ifa)));
}
break;
case RTM_NEWROUTE:
case RTM_DELROUTE:
case RTM_GETROUTE:
if (nlh->nlmsg_len >= NLMSG_LENGTH(sizeof(*rtm))) {
rtm = NLMSG_DATA(nlh);
rtm->rtm_flags = tswap32(rtm->rtm_flags);
host_to_target_route_rtattr(RTM_RTA(rtm),
nlmsg_len - NLMSG_LENGTH(sizeof(*rtm)));
}
break;
default:
return -TARGET_EINVAL;
}
return 0;
}
static inline abi_long host_to_target_nlmsg_route(struct nlmsghdr *nlh,
size_t len)
{
return host_to_target_for_each_nlmsg(nlh, len, host_to_target_data_route);
}
static abi_long target_to_host_for_each_rtattr(struct rtattr *rtattr,
size_t len,
abi_long (*target_to_host_rtattr)
(struct rtattr *))
{
unsigned short aligned_rta_len;
abi_long ret;
while (len >= sizeof(struct rtattr)) {
if (tswap16(rtattr->rta_len) < sizeof(struct rtattr) ||
tswap16(rtattr->rta_len) > len) {
break;
}
rtattr->rta_len = tswap16(rtattr->rta_len);
rtattr->rta_type = tswap16(rtattr->rta_type);
ret = target_to_host_rtattr(rtattr);
if (ret < 0) {
return ret;
}
aligned_rta_len = RTA_ALIGN(rtattr->rta_len);
if (aligned_rta_len >= len) {
break;
}
len -= aligned_rta_len;
rtattr = (struct rtattr *)(((char *)rtattr) + aligned_rta_len);
}
return 0;
}
static abi_long target_to_host_data_link_rtattr(struct rtattr *rtattr)
{
uint32_t *u32;
switch (rtattr->rta_type) {
/* uint32_t */
case QEMU_IFLA_EXT_MASK:
u32 = RTA_DATA(rtattr);
*u32 = tswap32(*u32);
break;
default:
qemu_log_mask(LOG_UNIMP, "Unknown target QEMU_IFLA type: %d\n",
rtattr->rta_type);
break;
}
return 0;
}
static abi_long target_to_host_data_addr_rtattr(struct rtattr *rtattr)
{
switch (rtattr->rta_type) {
/* binary: depends on family type */
case IFA_LOCAL:
case IFA_ADDRESS:
break;
default:
qemu_log_mask(LOG_UNIMP, "Unknown target IFA type: %d\n",
rtattr->rta_type);
break;
}
return 0;
}
static abi_long target_to_host_data_route_rtattr(struct rtattr *rtattr)
{
uint32_t *u32;
switch (rtattr->rta_type) {
/* binary: depends on family type */
case QEMU_RTA_DST:
case QEMU_RTA_SRC:
case QEMU_RTA_GATEWAY:
break;
/* u32 */
case QEMU_RTA_PRIORITY:
case QEMU_RTA_TABLE:
case QEMU_RTA_OIF:
u32 = RTA_DATA(rtattr);
*u32 = tswap32(*u32);
break;
default:
qemu_log_mask(LOG_UNIMP, "Unknown target RTA type: %d\n",
rtattr->rta_type);
break;
}
return 0;
}
static void target_to_host_link_rtattr(struct rtattr *rtattr,
uint32_t rtattr_len)
{
target_to_host_for_each_rtattr(rtattr, rtattr_len,
target_to_host_data_link_rtattr);
}
static void target_to_host_addr_rtattr(struct rtattr *rtattr,
uint32_t rtattr_len)
{
target_to_host_for_each_rtattr(rtattr, rtattr_len,
target_to_host_data_addr_rtattr);
}
static void target_to_host_route_rtattr(struct rtattr *rtattr,
uint32_t rtattr_len)
{
target_to_host_for_each_rtattr(rtattr, rtattr_len,
target_to_host_data_route_rtattr);
}
static abi_long target_to_host_data_route(struct nlmsghdr *nlh)
{
struct ifinfomsg *ifi;
struct ifaddrmsg *ifa;
struct rtmsg *rtm;
switch (nlh->nlmsg_type) {
case RTM_NEWLINK:
case RTM_DELLINK:
case RTM_SETLINK:
case RTM_GETLINK:
if (nlh->nlmsg_len >= NLMSG_LENGTH(sizeof(*ifi))) {
ifi = NLMSG_DATA(nlh);
ifi->ifi_type = tswap16(ifi->ifi_type);
ifi->ifi_index = tswap32(ifi->ifi_index);
ifi->ifi_flags = tswap32(ifi->ifi_flags);
ifi->ifi_change = tswap32(ifi->ifi_change);
target_to_host_link_rtattr(IFLA_RTA(ifi), nlh->nlmsg_len -
NLMSG_LENGTH(sizeof(*ifi)));
}
break;
case RTM_GETADDR:
case RTM_NEWADDR:
case RTM_DELADDR:
if (nlh->nlmsg_len >= NLMSG_LENGTH(sizeof(*ifa))) {
ifa = NLMSG_DATA(nlh);
ifa->ifa_index = tswap32(ifa->ifa_index);
target_to_host_addr_rtattr(IFA_RTA(ifa), nlh->nlmsg_len -
NLMSG_LENGTH(sizeof(*ifa)));
}
break;
case RTM_NEWROUTE:
case RTM_DELROUTE:
case RTM_GETROUTE:
if (nlh->nlmsg_len >= NLMSG_LENGTH(sizeof(*rtm))) {
rtm = NLMSG_DATA(nlh);
rtm->rtm_flags = tswap32(rtm->rtm_flags);
target_to_host_route_rtattr(RTM_RTA(rtm), nlh->nlmsg_len -
NLMSG_LENGTH(sizeof(*rtm)));
}
break;
default:
return -TARGET_EOPNOTSUPP;
}
return 0;
}
static abi_long target_to_host_nlmsg_route(struct nlmsghdr *nlh, size_t len)
{
return target_to_host_for_each_nlmsg(nlh, len, target_to_host_data_route);
}
#endif /* CONFIG_RTNETLINK */
static abi_long host_to_target_data_audit(struct nlmsghdr *nlh)
{
switch (nlh->nlmsg_type) {
default:
qemu_log_mask(LOG_UNIMP, "Unknown host audit message type %d\n",
nlh->nlmsg_type);
return -TARGET_EINVAL;
}
return 0;
}
static inline abi_long host_to_target_nlmsg_audit(struct nlmsghdr *nlh,
size_t len)
{
return host_to_target_for_each_nlmsg(nlh, len, host_to_target_data_audit);
}
static abi_long target_to_host_data_audit(struct nlmsghdr *nlh)
{
switch (nlh->nlmsg_type) {
case AUDIT_USER:
case AUDIT_FIRST_USER_MSG ... AUDIT_LAST_USER_MSG:
case AUDIT_FIRST_USER_MSG2 ... AUDIT_LAST_USER_MSG2:
break;
default:
qemu_log_mask(LOG_UNIMP, "Unknown target audit message type %d\n",
nlh->nlmsg_type);
return -TARGET_EINVAL;
}
return 0;
}
static abi_long target_to_host_nlmsg_audit(struct nlmsghdr *nlh, size_t len)
{
return target_to_host_for_each_nlmsg(nlh, len, target_to_host_data_audit);
}
static abi_long packet_target_to_host_sockaddr(void *host_addr,
abi_ulong target_addr,
socklen_t len)
{
struct sockaddr *addr = host_addr;
struct target_sockaddr *target_saddr;
target_saddr = lock_user(VERIFY_READ, target_addr, len, 1);
if (!target_saddr) {
return -TARGET_EFAULT;
}
memcpy(addr, target_saddr, len);
addr->sa_family = tswap16(target_saddr->sa_family);
/* spkt_protocol is big-endian */
unlock_user(target_saddr, target_addr, 0);
return 0;
}
TargetFdTrans target_packet_trans = {
.target_to_host_addr = packet_target_to_host_sockaddr,
};
#ifdef CONFIG_RTNETLINK
static abi_long netlink_route_target_to_host(void *buf, size_t len)
{
abi_long ret;
ret = target_to_host_nlmsg_route(buf, len);
if (ret < 0) {
return ret;
}
return len;
}
static abi_long netlink_route_host_to_target(void *buf, size_t len)
{
abi_long ret;
ret = host_to_target_nlmsg_route(buf, len);
if (ret < 0) {
return ret;
}
return len;
}
TargetFdTrans target_netlink_route_trans = {
.target_to_host_data = netlink_route_target_to_host,
.host_to_target_data = netlink_route_host_to_target,
};
#endif /* CONFIG_RTNETLINK */
static abi_long netlink_audit_target_to_host(void *buf, size_t len)
{
abi_long ret;
ret = target_to_host_nlmsg_audit(buf, len);
if (ret < 0) {
return ret;
}
return len;
}
static abi_long netlink_audit_host_to_target(void *buf, size_t len)
{
abi_long ret;
ret = host_to_target_nlmsg_audit(buf, len);
if (ret < 0) {
return ret;
}
return len;
}
TargetFdTrans target_netlink_audit_trans = {
.target_to_host_data = netlink_audit_target_to_host,
.host_to_target_data = netlink_audit_host_to_target,
};
/* signalfd siginfo conversion */
static void
host_to_target_signalfd_siginfo(struct signalfd_siginfo *tinfo,
const struct signalfd_siginfo *info)
{
int sig = host_to_target_signal(info->ssi_signo);
/* linux/signalfd.h defines a ssi_addr_lsb
* not defined in sys/signalfd.h but used by some kernels
*/
#ifdef BUS_MCEERR_AO
if (tinfo->ssi_signo == SIGBUS &&
(tinfo->ssi_code == BUS_MCEERR_AR ||
tinfo->ssi_code == BUS_MCEERR_AO)) {
uint16_t *ssi_addr_lsb = (uint16_t *)(&info->ssi_addr + 1);
uint16_t *tssi_addr_lsb = (uint16_t *)(&tinfo->ssi_addr + 1);
*tssi_addr_lsb = tswap16(*ssi_addr_lsb);
}
#endif
tinfo->ssi_signo = tswap32(sig);
tinfo->ssi_errno = tswap32(tinfo->ssi_errno);
tinfo->ssi_code = tswap32(info->ssi_code);
tinfo->ssi_pid = tswap32(info->ssi_pid);
tinfo->ssi_uid = tswap32(info->ssi_uid);
tinfo->ssi_fd = tswap32(info->ssi_fd);
tinfo->ssi_tid = tswap32(info->ssi_tid);
tinfo->ssi_band = tswap32(info->ssi_band);
tinfo->ssi_overrun = tswap32(info->ssi_overrun);
tinfo->ssi_trapno = tswap32(info->ssi_trapno);
tinfo->ssi_status = tswap32(info->ssi_status);
tinfo->ssi_int = tswap32(info->ssi_int);
tinfo->ssi_ptr = tswap64(info->ssi_ptr);
tinfo->ssi_utime = tswap64(info->ssi_utime);
tinfo->ssi_stime = tswap64(info->ssi_stime);
tinfo->ssi_addr = tswap64(info->ssi_addr);
}
static abi_long host_to_target_data_signalfd(void *buf, size_t len)
{
int i;
for (i = 0; i < len; i += sizeof(struct signalfd_siginfo)) {
host_to_target_signalfd_siginfo(buf + i, buf + i);
}
return len;
}
TargetFdTrans target_signalfd_trans = {
.host_to_target_data = host_to_target_data_signalfd,
};
static abi_long swap_data_eventfd(void *buf, size_t len)
{
uint64_t *counter = buf;
int i;
if (len < sizeof(uint64_t)) {
return -EINVAL;
}
for (i = 0; i < len; i += sizeof(uint64_t)) {
*counter = tswap64(*counter);
counter++;
}
return len;
}
TargetFdTrans target_eventfd_trans = {
.host_to_target_data = swap_data_eventfd,
.target_to_host_data = swap_data_eventfd,
};
#if defined(CONFIG_INOTIFY) && (defined(TARGET_NR_inotify_init) || \
defined(TARGET_NR_inotify_init1))
static abi_long host_to_target_data_inotify(void *buf, size_t len)
{
struct inotify_event *ev;
int i;
uint32_t name_len;
for (i = 0; i < len; i += sizeof(struct inotify_event) + name_len) {
ev = (struct inotify_event *)((char *)buf + i);
name_len = ev->len;
ev->wd = tswap32(ev->wd);
ev->mask = tswap32(ev->mask);
ev->cookie = tswap32(ev->cookie);
ev->len = tswap32(name_len);
}
return len;
}
TargetFdTrans target_inotify_trans = {
.host_to_target_data = host_to_target_data_inotify,
};
#endif