qemu/linux-user/fd-trans.c
Mathis Marion 2fe8ed6fe9 linux-user: handle netlink flag NLA_F_NESTED
Newer kernel versions require this flag to be present contrary to older
ones. Depending on the libnl version it is added or not.

Typically when using rtnl_link_inet6_set_addr_gen_mode, the netlink
packet generated may contain the following attribute:

with libnl 3.4

  {nla_len=16, nla_type=IFLA_AF_SPEC},
  [
    {nla_len=12, nla_type=AF_INET6},
    [{nla_len=5, nla_type=IFLA_INET6_ADDR_GEN_MODE}, IN6_ADDR_GEN_MODE_NONE]
  ]

with libnl 3.7

  {nla_len=16, nla_type=NLA_F_NESTED|IFLA_AF_SPEC},
  [
    {nla_len=12, nla_type=NLA_F_NESTED|AF_INET6},
    [{nla_len=5, nla_type=IFLA_INET6_ADDR_GEN_MODE}, IN6_ADDR_GEN_MODE_NONE]]
  ]

Masking the type is likely needed in other places. Only the above cases
are implemented in this patch.

Signed-off-by: Mathis Marion <mathis.marion@silabs.com>
Reviewed-by: Laurent Vivier <laurent@vivier.eu>
Message-Id: <20230307154256.101528-3-Mathis.Marion@silabs.com>
Signed-off-by: Laurent Vivier <laurent@vivier.eu>
2023-03-10 20:45:47 +01:00

1737 lines
52 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_nlattr(struct nlattr *nlattr,
size_t len,
abi_long (*target_to_host_nlattr)
(struct nlattr *))
{
unsigned short aligned_nla_len;
abi_long ret;
while (len > sizeof(struct nlattr)) {
if (tswap16(nlattr->nla_len) < sizeof(struct rtattr) ||
tswap16(nlattr->nla_len) > len) {
break;
}
nlattr->nla_len = tswap16(nlattr->nla_len);
nlattr->nla_type = tswap16(nlattr->nla_type);
ret = target_to_host_nlattr(nlattr);
if (ret < 0) {
return ret;
}
aligned_nla_len = NLA_ALIGN(nlattr->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 target_to_host_data_inet6_nlattr(struct nlattr *nlattr)
{
switch (nlattr->nla_type) {
/* uint8_t */
case QEMU_IFLA_INET6_ADDR_GEN_MODE:
break;
default:
qemu_log_mask(LOG_UNIMP, "Unknown target AF_INET6 type: %d\n",
nlattr->nla_type);
}
return 0;
}
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_spec_nlattr(struct nlattr *nlattr)
{
switch (nlattr->nla_type & NLA_TYPE_MASK) {
case AF_INET6:
return target_to_host_for_each_nlattr(NLA_DATA(nlattr), nlattr->nla_len,
target_to_host_data_inet6_nlattr);
default:
qemu_log_mask(LOG_UNIMP, "Unknown target AF_SPEC type: %d\n",
nlattr->nla_type);
break;
}
return 0;
}
static abi_long target_to_host_data_link_rtattr(struct rtattr *rtattr)
{
uint32_t *u32;
switch (rtattr->rta_type & NLA_TYPE_MASK) {
/* uint32_t */
case QEMU_IFLA_MTU:
case QEMU_IFLA_TXQLEN:
case QEMU_IFLA_EXT_MASK:
u32 = RTA_DATA(rtattr);
*u32 = tswap32(*u32);
break;
case QEMU_IFLA_AF_SPEC:
return target_to_host_for_each_nlattr(RTA_DATA(rtattr), rtattr->rta_len,
target_to_host_data_spec_nlattr);
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_u64(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_u64,
.target_to_host_data = swap_data_u64,
};
TargetFdTrans target_timerfd_trans = {
.host_to_target_data = swap_data_u64,
};
#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