/*
* Copyright (c) 1993, 1994, 1995, 1996 Rick Sladkey <jrs@world.std.com>
+ * Copyright (c) 1996-2017 The strace developers.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
*/
#include "defs.h"
+#include "print_fields.h"
+
#include <sys/socket.h>
#if defined ALPHA || defined SH || defined SH64
# include <linux/ioctl.h>
#include <arpa/inet.h>
#include <net/if.h>
+#include DEF_MPERS_TYPE(struct_ifconf)
+#include DEF_MPERS_TYPE(struct_ifreq)
+
+typedef struct ifconf struct_ifconf;
+typedef struct ifreq struct_ifreq;
+
+#include MPERS_DEFS
+
#include "xlat/iffflags.h"
-#define PRINT_IFREQ_ADDR(tcp, ifr, sockaddr) \
- do { \
- tprints(#sockaddr "="); \
- print_sockaddr(tcp, &((ifr)->sockaddr), \
- sizeof((ifr)->sockaddr)); \
- } while (0)
+#define XLAT_MACROS_ONLY
+# include "xlat/arp_hardware_types.h"
+#undef XLAT_MACROS_ONLY
static void
print_ifname(const char *ifname)
static void
print_ifreq(struct tcb *const tcp, const unsigned int code,
- const kernel_ureg_t arg, const struct ifreq *const ifr)
+ const kernel_ulong_t arg, const struct_ifreq *const ifr)
{
switch (code) {
case SIOCSIFADDR:
case SIOCGIFADDR:
- PRINT_IFREQ_ADDR(tcp, ifr, ifr_addr);
+ PRINT_FIELD_SOCKADDR("", *ifr, ifr_addr);
break;
case SIOCSIFDSTADDR:
case SIOCGIFDSTADDR:
- PRINT_IFREQ_ADDR(tcp, ifr, ifr_dstaddr);
+ PRINT_FIELD_SOCKADDR("", *ifr, ifr_dstaddr);
break;
case SIOCSIFBRDADDR:
case SIOCGIFBRDADDR:
- PRINT_IFREQ_ADDR(tcp, ifr, ifr_broadaddr);
+ PRINT_FIELD_SOCKADDR("", *ifr, ifr_broadaddr);
break;
case SIOCSIFNETMASK:
case SIOCGIFNETMASK:
- PRINT_IFREQ_ADDR(tcp, ifr, ifr_netmask);
+ PRINT_FIELD_SOCKADDR("", *ifr, ifr_netmask);
break;
case SIOCSIFHWADDR:
case SIOCGIFHWADDR: {
- /* XXX Are there other hardware addresses
- than 6-byte MACs? */
- const unsigned char *bytes =
- (unsigned char *) &ifr->ifr_hwaddr.sa_data;
- tprintf("ifr_hwaddr=%02x:%02x:%02x:%02x:%02x:%02x",
- bytes[0], bytes[1], bytes[2],
- bytes[3], bytes[4], bytes[5]);
+ static uint8_t hwaddr_sizes[] = {
+ [0 ... ARPHRD_IEEE802_TR] = 255,
+
+ [ARPHRD_NETROM] = 7 /* AX25_ADDR_LEN */,
+ [ARPHRD_ETHER] = 6 /* ETH_ALEN */,
+ /* ARPHRD_EETHER - no actual devices in Linux */
+ [ARPHRD_AX25] = 7 /* AX25_ADDR_LEN */,
+ /* ARPHRD_PRONET - no actual devices in Linux */
+ /* ARPHRD_CHAOS - no actual devices in Linux */
+ [ARPHRD_IEEE802] = 6 /* FC_ALEN */,
+ [ARPHRD_ARCNET] = 1 /* ARCNET_ALEN */,
+ /* ARPHRD_APPLETLK - no actual devices in Linux */
+ [ARPHRD_DLCI] = sizeof(short),
+ /* ARPHRD_ATM - no explicit setting */
+ /* ARPHRD_METRICOM - no actual devices in Linux */
+ [ARPHRD_IEEE1394] = 16 /* FWNET_ALEN */,
+ [ARPHRD_EUI64] = 8 /* EUI64_ADDR_LEN */,
+ [ARPHRD_INFINIBAND] = 20 /* INFINIBAND_ALEN */,
+ [ARPHRD_SLIP] = 0,
+ /* ARPHRD_CSLIP - no actual devices in Linux */
+ /* ARPHRD_SLIP6 - no actual devices in Linux */
+ /* ARPHRD_CSLIP6 - no actual devices in Linux */
+ /* ARPHRD_RSRVD - no actual devices in Linux */
+ /* ARPHRD_ADAPT - no actual devices in Linux */
+ [ARPHRD_ROSE] = 5 /* ROSE_ADDR_LEN */,
+ [ARPHRD_X25] = 0,
+ /* ARPHRD_HWX25 - no actual devices in Linux */
+ [ARPHRD_CAN] = 0,
+ [ARPHRD_PPP] = 0,
+ /* ARPHRD_CISCO - no actual devices in Linux */
+ /* ARPHRD_LAPB - no actual devices in Linux */
+ /* ARPHRD_DDCMP - no actual devices in Linux */
+ [ARPHRD_RAWHDLC] = 0,
+ [ARPHRD_RAWIP] = 0,
+ [ARPHRD_TUNNEL] = 4 /* IPIP */,
+ [ARPHRD_TUNNEL6] = 16 /* sizeof(struct in6_addr) */,
+ /* ARPHRD_FRAD - no actual devices in Linux */
+ /* ARPHRD_SKIP - no actual devices in Linux */
+ [ARPHRD_LOOPBACK] = 6 /* ETH_ALEN */,
+ [ARPHRD_LOCALTLK] = 1 /* LTALK_ALEN */,
+ [ARPHRD_FDDI] = 6 /* FDDI_K_ALEN */,
+ /* ARPHRD_BIF - no actual devices in Linux */
+ [ARPHRD_SIT] = 4,
+ [ARPHRD_IPDDP] = 0,
+ [ARPHRD_IPGRE] = 4,
+ [ARPHRD_PIMREG] = 0,
+ [ARPHRD_HIPPI] = 6 /* HIPPI_ALEN */,
+ /* ARPHRD_ASH - no actual devices in Linux */
+ /* ARPHRD_ECONET - no actual devices in Linux */
+ [ARPHRD_IRDA] = 4 /* LAP_ALEN */,
+ /* ARPHRD_FCPP - no actual devices in Linux */
+ /* ARPHRD_FCAL - no actual devices in Linux */
+ /* ARPHRD_FCPL - no actual devices in Linux */
+ /* ARPHRD_FCFABRIC - no actual devices in Linux */
+ /* ARPHRD_IEEE802_TR - no actual devices in Linux */
+ [ARPHRD_IEEE80211] = 6 /* ETH_ALEN */,
+ [ARPHRD_IEEE80211_PRISM] = 6 /* ETH_ALEN */,
+ [ARPHRD_IEEE80211_RADIOTAP] = 6 /* ETH_ALEN */,
+ [ARPHRD_IEEE802154]
+ = 8 /* IEEE802154_EXTENDED_ADDR_LEN */,
+ [ARPHRD_IEEE802154_MONITOR]
+ = 8 /* IEEE802154_EXTENDED_ADDR_LEN */,
+ [ARPHRD_PHONET] = 1,
+ [ARPHRD_PHONET_PIPE] = 1,
+ [ARPHRD_CAIF] = 0,
+ [ARPHRD_IP6GRE] = 16 /* sizeof(struct in6_addr) */,
+ [ARPHRD_NETLINK] = 0,
+ [ARPHRD_6LOWPAN] = 8 /* EUI64_ADDR_LEN */
+ /* ^ or ETH_ALEN, depending on lltype */,
+ [ARPHRD_VSOCKMON] = 0,
+ };
+
+ uint16_t proto = ifr->ifr_hwaddr.sa_family;
+ uint8_t sz = (proto < ARRAY_SIZE(hwaddr_sizes))
+ ? hwaddr_sizes[proto] : 255;
+
+ PRINT_FIELD_XVAL_SORTED_SIZED("ifr_hwaddr={", ifr->ifr_hwaddr,
+ sa_family, arp_hardware_types,
+ arp_hardware_types_size,
+ "ARPHRD_???");
+ PRINT_FIELD_MAC_SZ(", ", ifr->ifr_hwaddr, sa_data,
+ MIN(sizeof(ifr->ifr_hwaddr.sa_data), sz));
+ tprints("}");
break;
}
case SIOCSIFFLAGS:
break;
case SIOCSIFMAP:
case SIOCGIFMAP:
- tprintf("ifr_map={mem_start=%#lx, "
- "mem_end=%#lx, base_addr=%#x, "
+ tprintf("ifr_map={mem_start=%#" PRI_klx ", "
+ "mem_end=%#" PRI_klx ", base_addr=%#x, "
"irq=%u, dma=%u, port=%u}",
- ifr->ifr_map.mem_start,
- ifr->ifr_map.mem_end,
+ (kernel_ulong_t) ifr->ifr_map.mem_start,
+ (kernel_ulong_t) ifr->ifr_map.mem_end,
(unsigned) ifr->ifr_map.base_addr,
(unsigned) ifr->ifr_map.irq,
(unsigned) ifr->ifr_map.dma,
static unsigned int
print_ifc_len(int len)
{
- const unsigned int n = (unsigned int) len / sizeof(struct ifreq);
+ const unsigned int n = (unsigned int) len / sizeof(struct_ifreq);
- if (len < 0 || n * sizeof(struct ifreq) != (unsigned int) len)
+ if (len < 0 || n * sizeof(struct_ifreq) != (unsigned int) len)
tprintf("%d", len);
else
tprintf("%u * sizeof(struct ifreq)", n);
return n;
}
+static bool
+print_ifconf_ifreq(struct tcb *tcp, void *elem_buf, size_t elem_size,
+ void *dummy)
+{
+ struct_ifreq *ifr = elem_buf;
+
+ tprints("{ifr_name=");
+ print_ifname(ifr->ifr_name);
+ PRINT_FIELD_SOCKADDR(", ", *ifr, ifr_addr);
+ tprints("}");
+
+ return true;
+}
+
+/*
+ * There are two different modes of operation:
+ *
+ * - Get buffer size. In this case, the callee sets ifc_buf to NULL,
+ * and the kernel returns the buffer size in ifc_len.
+ * - Get actual data. In this case, the callee specifies the buffer address
+ * in ifc_buf and its size in ifc_len. The kernel fills the buffer with
+ * the data, and its amount is returned in ifc_len.
+ *
+ * Note that, technically, the whole struct ifconf is overwritten,
+ * so ifc_buf could be different on exit, but current ioctl handler
+ * implementation does not touch it.
+ */
static int
-decode_ifconf(struct tcb *const tcp, const kernel_ureg_t addr)
+decode_ifconf(struct tcb *const tcp, const kernel_ulong_t addr)
{
- struct ifconf ifc;
+ struct_ifconf *entering_ifc = NULL;
+ struct_ifconf *ifc =
+ entering(tcp) ? malloc(sizeof(*ifc)) : alloca(sizeof(*ifc));
- if (entering(tcp)) {
- tprints(", ");
- if (umove_or_printaddr(tcp, addr, &ifc))
- return RVAL_DECODED | 1;
- if (ifc.ifc_buf) {
- tprints("{");
- print_ifc_len(ifc.ifc_len);
+ if (exiting(tcp)) {
+ entering_ifc = get_tcb_priv_data(tcp);
+
+ if (!entering_ifc) {
+ error_func_msg("where is my ifconf?");
+ return 0;
}
- return 1;
}
- if (syserror(tcp) || umove(tcp, addr, &ifc) < 0) {
- if (ifc.ifc_buf)
- tprints("}");
- else
+ if (!ifc || umove(tcp, addr, ifc) < 0) {
+ if (entering(tcp)) {
+ free(ifc);
+
+ tprints(", ");
printaddr(addr);
- return RVAL_DECODED | 1;
- }
+ } else {
+ /*
+ * We failed to fetch the structure on exiting syscall,
+ * print whatever was fetched on entering syscall.
+ */
+ if (!entering_ifc->ifc_buf)
+ print_ifc_len(entering_ifc->ifc_len);
- if (!ifc.ifc_buf) {
- tprints("{");
- print_ifc_len(ifc.ifc_len);
- tprints(", NULL}");
- return RVAL_DECODED | 1;
- }
+ tprints(", ifc_buf=");
+ printaddr(ptr_to_kulong(entering_ifc->ifc_buf));
- tprints(" => ");
- const unsigned int nifra = print_ifc_len(ifc.ifc_len);
- if (!nifra) {
- tprints("}");
- return RVAL_DECODED | 1;
+ tprints("}");
+ }
+
+ return RVAL_IOCTL_DECODED;
}
- struct ifreq ifra[nifra > max_strlen ? max_strlen : nifra];
- tprints(", ");
- if (umove_or_printaddr(tcp, (kernel_ureg_t) ifc.ifc_buf, &ifra)) {
- tprints("}");
- return RVAL_DECODED | 1;
+ if (entering(tcp)) {
+ tprints(", {ifc_len=");
+ if (ifc->ifc_buf)
+ print_ifc_len(ifc->ifc_len);
+
+ set_tcb_priv_data(tcp, ifc, free);
+
+ return 0;
}
- tprints("[");
- unsigned int i;
- for (i = 0; i < ARRAY_SIZE(ifra); ++i) {
- if (i > 0)
- tprints(", ");
- tprints("{ifr_name=");
- print_ifname(ifra[i].ifr_name);
- tprints(", ");
- PRINT_IFREQ_ADDR(tcp, &ifra[i], ifr_addr);
- tprints("}");
+ /* exiting */
+
+ if (entering_ifc->ifc_buf && (entering_ifc->ifc_len != ifc->ifc_len))
+ tprints(" => ");
+ if (!entering_ifc->ifc_buf || (entering_ifc->ifc_len != ifc->ifc_len))
+ print_ifc_len(ifc->ifc_len);
+
+ tprints(", ifc_buf=");
+
+ if (!entering_ifc->ifc_buf || syserror(tcp)) {
+ printaddr(ptr_to_kulong(entering_ifc->ifc_buf));
+ if (entering_ifc->ifc_buf != ifc->ifc_buf) {
+ tprints(" => ");
+ printaddr(ptr_to_kulong(ifc->ifc_buf));
+ }
+ } else {
+ struct_ifreq ifr;
+
+ print_array(tcp, ptr_to_kulong(ifc->ifc_buf),
+ ifc->ifc_len / sizeof(struct_ifreq),
+ &ifr, sizeof(ifr),
+ tfetch_mem, print_ifconf_ifreq, NULL);
}
- if (i < nifra)
- tprints(", ...");
- tprints("]}");
- return RVAL_DECODED | 1;
+ tprints("}");
+
+ return RVAL_IOCTL_DECODED;
}
-int
-sock_ioctl(struct tcb *const tcp, const unsigned int code, const kernel_ureg_t arg)
+MPERS_PRINTER_DECL(int, sock_ioctl,
+ struct tcb *tcp, const unsigned int code,
+ const kernel_ulong_t arg)
{
- struct ifreq ifr;
+ struct_ifreq ifr;
switch (code) {
case SIOCGIFCONF:
case SIOCBRADDBR:
case SIOCBRDELBR:
tprints(", ");
- printstr(tcp, arg, -1);
+ printstr(tcp, arg);
break;
#endif
tprints("{ifr_name=");
print_ifname(ifr.ifr_name);
}
- return 1;
+ return 0;
} else {
if (syserror(tcp)) {
tprints("}");
return RVAL_DECODED;
}
- return RVAL_DECODED | 1;
+ return RVAL_IOCTL_DECODED;
}