Dmitry V. Levin
b93d52fe3d
strace is now provided under the terms of the GNU Lesser General Public License version 2.1 or later, see COPYING for more details. strace test suite is now provided under the terms of the GNU General Public License version 2 or later, see tests/COPYING for more details.
440 lines
11 KiB
C
440 lines
11 KiB
C
/*
|
|
* Copyright (c) 1993, 1994, 1995, 1996 Rick Sladkey <jrs@world.std.com>
|
|
* Copyright (c) 1996-2018 The strace developers.
|
|
* All rights reserved.
|
|
*
|
|
* SPDX-License-Identifier: LGPL-2.1-or-later
|
|
*/
|
|
|
|
#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>
|
|
#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 XLAT_MACROS_ONLY
|
|
# include "xlat/arp_hardware_types.h"
|
|
#undef XLAT_MACROS_ONLY
|
|
|
|
static void
|
|
print_ifname(const char *ifname)
|
|
{
|
|
print_quoted_string(ifname, IFNAMSIZ + 1, QUOTE_0_TERMINATED);
|
|
}
|
|
|
|
DIAG_PUSH_IGNORE_OVERRIDE_INIT
|
|
|
|
static void
|
|
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:
|
|
PRINT_FIELD_SOCKADDR("", *ifr, ifr_addr);
|
|
break;
|
|
case SIOCSIFDSTADDR:
|
|
case SIOCGIFDSTADDR:
|
|
PRINT_FIELD_SOCKADDR("", *ifr, ifr_dstaddr);
|
|
break;
|
|
case SIOCSIFBRDADDR:
|
|
case SIOCGIFBRDADDR:
|
|
PRINT_FIELD_SOCKADDR("", *ifr, ifr_broadaddr);
|
|
break;
|
|
case SIOCSIFNETMASK:
|
|
case SIOCGIFNETMASK:
|
|
PRINT_FIELD_SOCKADDR("", *ifr, ifr_netmask);
|
|
break;
|
|
case SIOCSIFHWADDR:
|
|
case SIOCGIFHWADDR: {
|
|
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, (unsigned short) ifr->ifr_flags, "IFF_???");
|
|
break;
|
|
case SIOCSIFMETRIC:
|
|
case SIOCGIFMETRIC:
|
|
tprintf("ifr_metric=%d", ifr->ifr_metric);
|
|
break;
|
|
case SIOCSIFMTU:
|
|
case SIOCGIFMTU:
|
|
tprintf("ifr_mtu=%d", ifr->ifr_mtu);
|
|
break;
|
|
case SIOCSIFSLAVE:
|
|
case SIOCGIFSLAVE:
|
|
tprints("ifr_slave=");
|
|
print_ifname(ifr->ifr_slave);
|
|
break;
|
|
case SIOCSIFTXQLEN:
|
|
case SIOCGIFTXQLEN:
|
|
tprintf("ifr_qlen=%d", ifr->ifr_qlen);
|
|
break;
|
|
case SIOCSIFMAP:
|
|
case SIOCGIFMAP:
|
|
tprintf("ifr_map={mem_start=%#" PRI_klx ", "
|
|
"mem_end=%#" PRI_klx ", base_addr=%#x, "
|
|
"irq=%u, dma=%u, port=%u}",
|
|
(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,
|
|
(unsigned) ifr->ifr_map.port);
|
|
break;
|
|
}
|
|
}
|
|
|
|
DIAG_POP_IGNORE_OVERRIDE_INIT
|
|
|
|
static unsigned int
|
|
print_ifc_len(int len)
|
|
{
|
|
const unsigned int n = (unsigned int) len / sizeof(struct_ifreq);
|
|
|
|
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_ulong_t addr)
|
|
{
|
|
struct_ifconf *entering_ifc = NULL;
|
|
struct_ifconf *ifc =
|
|
entering(tcp) ? malloc(sizeof(*ifc)) : alloca(sizeof(*ifc));
|
|
|
|
if (exiting(tcp)) {
|
|
entering_ifc = get_tcb_priv_data(tcp);
|
|
|
|
if (!entering_ifc) {
|
|
error_func_msg("where is my ifconf?");
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
if (!ifc || umove(tcp, addr, ifc) < 0) {
|
|
if (entering(tcp)) {
|
|
free(ifc);
|
|
|
|
tprints(", ");
|
|
printaddr(addr);
|
|
} 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);
|
|
|
|
tprints(", ifc_buf=");
|
|
printaddr(ptr_to_kulong(entering_ifc->ifc_buf));
|
|
|
|
tprints("}");
|
|
}
|
|
|
|
return RVAL_IOCTL_DECODED;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
/* 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);
|
|
}
|
|
|
|
tprints("}");
|
|
|
|
return RVAL_IOCTL_DECODED;
|
|
}
|
|
|
|
MPERS_PRINTER_DECL(int, sock_ioctl,
|
|
struct tcb *tcp, const unsigned int code,
|
|
const kernel_ulong_t arg)
|
|
{
|
|
struct_ifreq ifr;
|
|
|
|
switch (code) {
|
|
case SIOCGIFCONF:
|
|
return decode_ifconf(tcp, arg);
|
|
|
|
#ifdef SIOCBRADDBR
|
|
case SIOCBRADDBR:
|
|
case SIOCBRDELBR:
|
|
tprints(", ");
|
|
printstr(tcp, arg);
|
|
break;
|
|
#endif
|
|
|
|
#ifdef FIOSETOWN
|
|
case FIOSETOWN:
|
|
#endif
|
|
#ifdef SIOCSPGRP
|
|
case SIOCSPGRP:
|
|
#endif
|
|
tprints(", ");
|
|
printnum_int(tcp, arg, "%d");
|
|
break;
|
|
|
|
#ifdef FIOGETOWN
|
|
case FIOGETOWN:
|
|
#endif
|
|
#ifdef SIOCGPGRP
|
|
case SIOCGPGRP:
|
|
#endif
|
|
#ifdef SIOCATMARK
|
|
case SIOCATMARK:
|
|
#endif
|
|
if (entering(tcp))
|
|
return 0;
|
|
tprints(", ");
|
|
printnum_int(tcp, arg, "%d");
|
|
break;
|
|
|
|
#ifdef SIOCBRADDIF
|
|
case SIOCBRADDIF:
|
|
#endif
|
|
#ifdef SIOCBRDELIF
|
|
case SIOCBRDELIF:
|
|
#endif
|
|
/* no arguments */
|
|
break;
|
|
|
|
case SIOCSIFNAME:
|
|
case SIOCSIFADDR:
|
|
case SIOCSIFDSTADDR:
|
|
case SIOCSIFBRDADDR:
|
|
case SIOCSIFNETMASK:
|
|
case SIOCSIFFLAGS:
|
|
case SIOCSIFMETRIC:
|
|
case SIOCSIFMTU:
|
|
case SIOCSIFSLAVE:
|
|
case SIOCSIFHWADDR:
|
|
case SIOCSIFTXQLEN:
|
|
case SIOCSIFMAP:
|
|
tprints(", ");
|
|
if (umove_or_printaddr(tcp, arg, &ifr))
|
|
break;
|
|
|
|
tprints("{ifr_name=");
|
|
print_ifname(ifr.ifr_name);
|
|
tprints(", ");
|
|
if (code == SIOCSIFNAME) {
|
|
tprints("ifr_newname=");
|
|
print_ifname(ifr.ifr_newname);
|
|
} else {
|
|
print_ifreq(tcp, code, arg, &ifr);
|
|
}
|
|
tprints("}");
|
|
break;
|
|
|
|
case SIOCGIFNAME:
|
|
case SIOCGIFINDEX:
|
|
case SIOCGIFADDR:
|
|
case SIOCGIFDSTADDR:
|
|
case SIOCGIFBRDADDR:
|
|
case SIOCGIFNETMASK:
|
|
case SIOCGIFFLAGS:
|
|
case SIOCGIFMETRIC:
|
|
case SIOCGIFMTU:
|
|
case SIOCGIFSLAVE:
|
|
case SIOCGIFHWADDR:
|
|
case SIOCGIFTXQLEN:
|
|
case SIOCGIFMAP:
|
|
if (entering(tcp)) {
|
|
tprints(", ");
|
|
if (umove_or_printaddr(tcp, arg, &ifr))
|
|
break;
|
|
|
|
if (SIOCGIFNAME == code) {
|
|
tprintf("{ifr_index=%d", ifr.ifr_ifindex);
|
|
} else {
|
|
tprints("{ifr_name=");
|
|
print_ifname(ifr.ifr_name);
|
|
}
|
|
return 0;
|
|
} else {
|
|
if (syserror(tcp)) {
|
|
tprints("}");
|
|
break;
|
|
}
|
|
|
|
tprints(", ");
|
|
if (umove(tcp, arg, &ifr) < 0) {
|
|
tprints("???}");
|
|
break;
|
|
}
|
|
|
|
if (SIOCGIFNAME == code) {
|
|
tprints("ifr_name=");
|
|
print_ifname(ifr.ifr_name);
|
|
} else {
|
|
print_ifreq(tcp, code, arg, &ifr);
|
|
}
|
|
tprints("}");
|
|
break;
|
|
}
|
|
|
|
default:
|
|
return RVAL_DECODED;
|
|
}
|
|
|
|
return RVAL_IOCTL_DECODED;
|
|
}
|