]> granicus.if.org Git - strace/blobdiff - sock.c
nlattr: add UID/GID netlink attribute decoders
[strace] / sock.c
diff --git a/sock.c b/sock.c
index 97d8afa6ce6514e6efe374ea39150097ca4f4b02..a520852e7673797337ac6a280f013403d8fe0d58 100644 (file)
--- a/sock.c
+++ b/sock.c
@@ -1,5 +1,6 @@
 /*
  * 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>
+#endif
 #include <linux/sockios.h>
 #include <arpa/inet.h>
-#if defined(ALPHA) || defined(SH) || defined(SH64)
-# if defined(HAVE_SYS_IOCTL_H)
-#  include <sys/ioctl.h>
-# elif defined(HAVE_IOCTLS_H)
-#  include <ioctls.h>
-# endif
-#endif
 #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"
 
-static void
-print_ifreq_addr(struct tcb *tcp, const struct ifreq *ifr, const long addr)
-{
-       tprintf("{");
-       printxval(addrfams, ifr->ifr_addr.sa_family, "AF_???");
-       tprints(", ");
-       if (ifr->ifr_addr.sa_family == AF_INET) {
-               const struct sockaddr_in *sinp =
-                       (struct sockaddr_in *) &ifr->ifr_addr;
-               tprintf("inet_addr(\"%s\")", inet_ntoa(sinp->sin_addr));
-       } else
-               printstr(tcp, addr + offsetof(struct ifreq, ifr_addr.sa_data),
-                        sizeof(ifr->ifr_addr.sa_data));
-       tprints("}");
-}
+#define XLAT_MACROS_ONLY
+# include "xlat/arp_hardware_types.h"
+#undef XLAT_MACROS_ONLY
 
 static void
 print_ifname(const char *ifname)
@@ -63,45 +58,117 @@ print_ifname(const char *ifname)
 }
 
 static void
-print_ifreq(struct tcb *tcp, const unsigned int code, const long arg,
-           const struct ifreq *ifr)
+print_ifreq(struct tcb *const tcp, const unsigned int code,
+           const kernel_ulong_t arg, const struct_ifreq *const ifr)
 {
        switch (code) {
        case SIOCSIFADDR:
        case SIOCGIFADDR:
-               tprints("ifr_addr=");
-               print_ifreq_addr(tcp, ifr, arg);
+               PRINT_FIELD_SOCKADDR("", *ifr, ifr_addr);
                break;
        case SIOCSIFDSTADDR:
        case SIOCGIFDSTADDR:
-               tprints("ifr_dstaddr=");
-               print_ifreq_addr(tcp, ifr, arg);
+               PRINT_FIELD_SOCKADDR("", *ifr, ifr_dstaddr);
                break;
        case SIOCSIFBRDADDR:
        case SIOCGIFBRDADDR:
-               tprints("ifr_broadaddr=");
-               print_ifreq_addr(tcp, ifr, arg);
+               PRINT_FIELD_SOCKADDR("", *ifr, ifr_broadaddr);
                break;
        case SIOCSIFNETMASK:
        case SIOCGIFNETMASK:
-               tprints("ifr_netmask=");
-               print_ifreq_addr(tcp, ifr, arg);
+               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:
        case SIOCGIFFLAGS:
                tprints("ifr_flags=");
-               printflags(iffflags, ifr->ifr_flags, "IFF_???");
+               printflags(iffflags, (unsigned short) ifr->ifr_flags, "IFF_???");
                break;
        case SIOCSIFMETRIC:
        case SIOCGIFMETRIC:
@@ -122,11 +189,11 @@ print_ifreq(struct tcb *tcp, const unsigned int code, const long arg,
                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,
@@ -138,9 +205,9 @@ print_ifreq(struct tcb *tcp, const unsigned int code, const long arg,
 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);
@@ -148,78 +215,116 @@ print_ifc_len(int len)
        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 *tcp, const long 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, (unsigned long) 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(", ");
-               if (verbose(tcp)) {
-                       tprints("ifr_addr=");
-                       print_ifreq_addr(tcp, &ifra[i],
-                                        addr + i * sizeof(ifra[0]));
-               } else
-                       tprints("...");
-               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 *tcp, const unsigned int code, const long 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:
@@ -229,7 +334,7 @@ sock_ioctl(struct tcb *tcp, const unsigned int code, const long arg)
        case SIOCBRADDBR:
        case SIOCBRDELBR:
                tprints(", ");
-               printstr(tcp, arg, -1);
+               printstr(tcp, arg);
                break;
 #endif
 
@@ -319,7 +424,7 @@ sock_ioctl(struct tcb *tcp, const unsigned int code, const long arg)
                                tprints("{ifr_name=");
                                print_ifname(ifr.ifr_name);
                        }
-                       return 1;
+                       return 0;
                } else {
                        if (syserror(tcp)) {
                                tprints("}");
@@ -346,10 +451,5 @@ sock_ioctl(struct tcb *tcp, const unsigned int code, const long arg)
                return RVAL_DECODED;
        }
 
-       return RVAL_DECODED | 1;
-}
-
-SYS_FUNC(socketcall)
-{
-       return printargs(tcp);
+       return RVAL_IOCTL_DECODED;
 }