|  | /* | 
|  | *  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 |