/* * 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 . */ #include "qemu/osdep.h" #include #include #include #ifdef CONFIG_INOTIFY #include #endif #include #ifdef CONFIG_RTNETLINK #include #include #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) { 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) { /* 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