David van Moolenbroek 521fa314e2 Add trace(1): the MINIX3 system call tracer
Change-Id: Ib970c8647409196902ed53d6e9631a1673a4ab2e
2014-11-04 21:46:31 +00:00

566 lines
13 KiB
C

#include "inc.h"
#include <sys/ioctl.h>
#include <sys/ucred.h>
#include <net/gen/in.h>
#include <net/gen/ether.h>
#include <net/gen/eth_io.h>
#include <net/gen/arp_io.h>
#include <net/gen/ip_io.h>
#include <net/gen/route.h>
#include <net/gen/tcp.h>
#include <net/gen/tcp_io.h>
#include <net/gen/udp.h>
#include <net/gen/udp_io.h>
#include <net/gen/udp_io_hdr.h>
#include <net/gen/psip_io.h>
#include <arpa/inet.h>
const char *
net_ioctl_name(unsigned long req)
{
switch (req) {
NAME(FIONREAD);
NAME(NWIOSETHOPT); /* TODO: print argument */
NAME(NWIOGETHOPT); /* TODO: print argument */
NAME(NWIOGETHSTAT); /* TODO: print argument */
NAME(NWIOARPGIP); /* TODO: print argument */
NAME(NWIOARPGNEXT); /* TODO: print argument */
NAME(NWIOARPSIP); /* TODO: print argument */
NAME(NWIOARPDIP); /* TODO: print argument */
NAME(NWIOSIPCONF2); /* TODO: print argument */
NAME(NWIOSIPCONF); /* TODO: print argument */
NAME(NWIOGIPCONF2); /* TODO: print argument */
NAME(NWIOGIPCONF); /* TODO: print argument */
NAME(NWIOSIPOPT);
NAME(NWIOGIPOPT);
NAME(NWIOGIPOROUTE); /* TODO: print argument */
NAME(NWIOSIPOROUTE); /* TODO: print argument */
NAME(NWIODIPOROUTE); /* TODO: print argument */
NAME(NWIOGIPIROUTE); /* TODO: print argument */
NAME(NWIOSIPIROUTE); /* TODO: print argument */
NAME(NWIODIPIROUTE); /* TODO: print argument */
NAME(NWIOSTCPCONF);
NAME(NWIOGTCPCONF);
NAME(NWIOTCPCONN);
NAME(NWIOTCPLISTEN);
NAME(NWIOTCPATTACH); /* TODO: print argument */
NAME(NWIOTCPSHUTDOWN); /* no argument */
NAME(NWIOSTCPOPT);
NAME(NWIOGTCPOPT);
NAME(NWIOTCPPUSH); /* no argument */
NAME(NWIOTCPLISTENQ);
NAME(NWIOGTCPCOOKIE);
NAME(NWIOTCPACCEPTTO);
NAME(NWIOTCPGERROR);
NAME(NWIOSUDPOPT);
NAME(NWIOGUDPOPT);
NAME(NWIOUDPPEEK); /* TODO: print argument */
NAME(NWIOSPSIPOPT); /* TODO: print argument */
NAME(NWIOGPSIPOPT); /* TODO: print argument */
NAME(NWIOGUDSFADDR);
NAME(NWIOSUDSTADDR);
NAME(NWIOSUDSADDR);
NAME(NWIOGUDSADDR);
NAME(NWIOGUDSPADDR);
NAME(NWIOSUDSTYPE);
NAME(NWIOSUDSBLOG);
NAME(NWIOSUDSCONN);
NAME(NWIOSUDSSHUT);
NAME(NWIOSUDSPAIR);
NAME(NWIOSUDSACCEPT);
NAME(NWIOSUDSCTRL);
NAME(NWIOGUDSCTRL);
NAME(NWIOGUDSSOTYPE);
NAME(NWIOGUDSPEERCRED);
NAME(NWIOGUDSSNDBUF);
NAME(NWIOSUDSSNDBUF);
NAME(NWIOGUDSRCVBUF);
NAME(NWIOSUDSRCVBUF);
}
return NULL;
}
static const struct flags ipopt_flags[] = {
FLAG_ZERO(NWIO_NOFLAGS),
FLAG_MASK(NWIO_ACC_MASK, NWIO_EXCL),
FLAG_MASK(NWIO_ACC_MASK, NWIO_SHARED),
FLAG_MASK(NWIO_ACC_MASK, NWIO_COPY),
FLAG(NWIO_EN_LOC),
FLAG(NWIO_DI_LOC),
FLAG(NWIO_EN_BROAD),
FLAG(NWIO_DI_BROAD),
FLAG(NWIO_REMSPEC),
FLAG(NWIO_REMANY),
FLAG(NWIO_PROTOSPEC),
FLAG(NWIO_PROTOANY),
FLAG(NWIO_HDR_O_SPEC),
FLAG(NWIO_HDR_O_ANY),
FLAG(NWIO_RWDATONLY),
FLAG(NWIO_RWDATALL),
};
static void
put_ipaddr(struct trace_proc * proc, const char * name, ipaddr_t ipaddr)
{
struct in_addr in;
if (!valuesonly) {
in.s_addr = ipaddr;
/* Is this an acceptable encapsulation? */
put_value(proc, name, "[%s]", inet_ntoa(in));
} else
put_value(proc, name, "0x%08x", ntohl(ipaddr));
}
static void
put_ipproto(struct trace_proc * proc, const char * name, ipproto_t proto)
{
const char *text = NULL;
if (!valuesonly) {
switch (proto) {
TEXT(IPPROTO_ICMP);
TEXT(IPPROTO_TCP);
TEXT(IPPROTO_UDP);
}
}
if (text != NULL)
put_field(proc, name, text);
else
put_value(proc, name, "%u", proto);
}
static const struct flags tcpconf_flags[] = {
FLAG_ZERO(NWTC_NOFLAGS),
FLAG_MASK(NWTC_ACC_MASK, NWTC_EXCL),
FLAG_MASK(NWTC_ACC_MASK, NWTC_SHARED),
FLAG_MASK(NWTC_ACC_MASK, NWTC_COPY),
FLAG_MASK(NWTC_LOCPORT_MASK, NWTC_LP_UNSET),
FLAG_MASK(NWTC_LOCPORT_MASK, NWTC_LP_SET),
FLAG_MASK(NWTC_LOCPORT_MASK, NWTC_LP_SEL),
FLAG(NWTC_SET_RA),
FLAG(NWTC_UNSET_RA),
FLAG(NWTC_SET_RP),
FLAG(NWTC_UNSET_RP),
};
#define put_port(proc, name, port) \
put_value(proc, name, "%u", ntohs(port))
static const struct flags tcpcl_flags[] = {
FLAG_ZERO(TCF_DEFAULT),
FLAG(TCF_ASYNCH),
};
static const struct flags tcpopt_flags[] = {
FLAG_ZERO(NWTO_NOFLAG),
FLAG(NWTO_SND_URG),
FLAG(NWTO_SND_NOTURG),
FLAG(NWTO_RCV_URG),
FLAG(NWTO_RCV_NOTURG),
FLAG(NWTO_BSD_URG),
FLAG(NWTO_NOTBSD_URG),
FLAG(NWTO_DEL_RST),
FLAG(NWTO_BULK),
FLAG(NWTO_NOBULK),
};
static const struct flags udpopt_flags[] = {
FLAG_ZERO(NWUO_NOFLAGS),
FLAG_MASK(NWUO_ACC_MASK, NWUO_EXCL),
FLAG_MASK(NWUO_ACC_MASK, NWUO_SHARED),
FLAG_MASK(NWUO_ACC_MASK, NWUO_COPY),
FLAG_MASK(NWUO_LOCPORT_MASK, NWUO_LP_SET),
FLAG_MASK(NWUO_LOCPORT_MASK, NWUO_LP_SEL),
FLAG_MASK(NWUO_LOCPORT_MASK, NWUO_LP_ANY),
FLAG(NWUO_EN_LOC),
FLAG(NWUO_DI_LOC),
FLAG(NWUO_EN_BROAD),
FLAG(NWUO_DI_BROAD),
FLAG(NWUO_RP_SET),
FLAG(NWUO_RP_ANY),
FLAG(NWUO_RA_SET),
FLAG(NWUO_RA_ANY),
FLAG(NWUO_RWDATONLY),
FLAG(NWUO_RWDATALL),
FLAG(NWUO_EN_IPOPT),
FLAG(NWUO_DI_IPOPT),
};
static void
put_family(struct trace_proc * proc, const char * name, int family)
{
const char *text = NULL;
if (!valuesonly) {
/* TODO: add all the other protocols */
switch (family) {
TEXT(AF_UNSPEC);
TEXT(AF_LOCAL);
TEXT(AF_INET);
TEXT(AF_INET6);
}
}
if (text != NULL)
put_field(proc, name, text);
else
put_value(proc, name, "%d", family);
}
static const struct flags sock_type[] = {
FLAG_MASK(~SOCK_FLAGS_MASK, SOCK_STREAM),
FLAG_MASK(~SOCK_FLAGS_MASK, SOCK_DGRAM),
FLAG_MASK(~SOCK_FLAGS_MASK, SOCK_RAW),
FLAG_MASK(~SOCK_FLAGS_MASK, SOCK_RDM),
FLAG_MASK(~SOCK_FLAGS_MASK, SOCK_SEQPACKET),
FLAG(SOCK_CLOEXEC),
FLAG(SOCK_NONBLOCK),
FLAG(SOCK_NOSIGPIPE),
};
static void
put_shutdown_how(struct trace_proc * proc, const char * name, int how)
{
const char *text = NULL;
if (!valuesonly) {
switch (how) {
TEXT(SHUT_RD);
TEXT(SHUT_WR);
TEXT(SHUT_RDWR);
}
}
if (text != NULL)
put_field(proc, name, text);
else
put_value(proc, name, "%d", how);
}
static void
put_struct_uucred(struct trace_proc * proc, const char * name, int flags,
vir_bytes addr)
{
struct uucred cred;
if (!put_open_struct(proc, name, flags, addr, &cred, sizeof(cred)))
return;
put_value(proc, "cr_uid", "%u", cred.cr_uid);
if (verbose > 0) {
put_value(proc, "cr_gid", "%u", cred.cr_gid);
if (verbose > 1)
put_value(proc, "cr_ngroups", "%d", cred.cr_ngroups);
put_groups(proc, "cr_groups", PF_LOCADDR,
(vir_bytes)&cred.cr_groups, cred.cr_ngroups);
}
put_close_struct(proc, verbose > 0);
}
static void
put_cmsg_type(struct trace_proc * proc, const char * name, int type)
{
const char *text = NULL;
if (!valuesonly) {
switch (type) {
TEXT(SCM_RIGHTS);
TEXT(SCM_CREDS);
TEXT(SCM_TIMESTAMP);
}
}
if (text != NULL)
put_field(proc, name, text);
else
put_value(proc, name, "%d", type);
}
static void
put_msg_control(struct trace_proc * proc, struct msg_control * ptr)
{
struct msghdr msg;
struct cmsghdr *cmsg;
size_t len;
int i;
if (ptr->msg_controllen > sizeof(ptr->msg_control)) {
put_field(proc, NULL, "..");
return;
}
put_open(proc, NULL, PF_NONAME, "[", ", ");
memset(&msg, 0, sizeof(msg));
msg.msg_control = ptr->msg_control;
msg.msg_controllen = ptr->msg_controllen;
/*
* TODO: decide if we need a verbosity-based limit here. The argument
* in favor of printing everything is that upon receipt, SCM_RIGHTS
* actually creates new file descriptors, which is pretty essential in
* terms of figuring out what is happening in a process. In addition,
* these calls should be sufficiently rare that the lengthy output is
* not really disruptive for the general output flow.
*/
for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL;
cmsg = CMSG_NXTHDR(&msg, cmsg)) {
put_open(proc, NULL, 0, "{", ", ");
if (verbose > 0)
put_value(proc, "cmsg_len", "%u", cmsg->cmsg_len);
if (!valuesonly && cmsg->cmsg_level == SOL_SOCKET)
put_field(proc, "cmsg_level", "SOL_SOCKET");
else
put_value(proc, "cmsg_level", "%d", cmsg->cmsg_level);
if (cmsg->cmsg_level == SOL_SOCKET)
put_cmsg_type(proc, "cmsg_type", cmsg->cmsg_type);
len = cmsg->cmsg_len - CMSG_LEN(0);
/* Print the contents of the messages that we know. */
if (cmsg->cmsg_level == SOL_SOCKET &&
cmsg->cmsg_type == SCM_RIGHTS) {
put_open(proc, NULL, PF_NONAME, "[", ", ");
for (i = 0; i < len / sizeof(int); i++)
put_fd(proc, NULL,
((int *)CMSG_DATA(cmsg))[i]);
put_close(proc, "]");
} else if (cmsg->cmsg_level == SOL_SOCKET &&
cmsg->cmsg_type == SCM_CREDS) {
put_struct_uucred(proc, NULL, PF_LOCADDR,
(vir_bytes)CMSG_DATA(cmsg));
} else if (len > 0)
put_field(proc, NULL, "..");
put_close(proc, "}");
}
put_close(proc, "]");
}
int
net_ioctl_arg(struct trace_proc * proc, unsigned long req, void * ptr, int dir)
{
const char *text;
nwio_ipopt_t *ipopt;
nwio_tcpconf_t *nwtc;
nwio_tcpcl_t *nwtcl;
nwio_tcpopt_t *nwto;
tcp_cookie_t *cookie;
nwio_udpopt_t *nwuo;
struct sockaddr_un *sun;
int i;
switch (req) {
case FIONREAD:
/*
* Arguably this does not belong here, but as of writing, the
* network services are the only ones actually implementing
* support for this IOCTL, and we don't have a more suitable
* place to put it either.
*/
if (ptr == NULL)
return IF_IN;
put_value(proc, NULL, "%d", *(int *)ptr);
return IF_ALL;
case NWIOSIPOPT:
case NWIOGIPOPT:
if ((ipopt = (nwio_ipopt_t *)ptr) == NULL)
return dir;
put_flags(proc, "nwio_flags", ipopt_flags, COUNT(ipopt_flags),
"0x%x", ipopt->nwio_flags);
if (ipopt->nwio_flags & NWIO_REMSPEC)
put_ipaddr(proc, "nwio_rem", ipopt->nwio_rem);
if (ipopt->nwio_flags & NWIO_PROTOSPEC)
put_ipproto(proc, "nwio_proto", ipopt->nwio_proto);
return 0; /* TODO: the remaining fields */
case NWIOSTCPCONF:
case NWIOGTCPCONF:
if ((nwtc = (nwio_tcpconf_t *)ptr) == NULL)
return dir;
put_flags(proc, "nwtc_flags", tcpconf_flags,
COUNT(tcpconf_flags), "0x%x", nwtc->nwtc_flags);
/* The local address cannot be set, just retrieved. */
if (req == NWIOGTCPCONF)
put_ipaddr(proc, "nwtc_locaddr", nwtc->nwtc_locaddr);
if ((nwtc->nwtc_flags & NWTC_LOCPORT_MASK) == NWTC_LP_SET)
put_port(proc, "nwtc_locport", nwtc->nwtc_locport);
if (nwtc->nwtc_flags & NWTC_SET_RA)
put_ipaddr(proc, "nwtc_remaddr", nwtc->nwtc_remaddr);
if (nwtc->nwtc_flags & NWTC_SET_RP)
put_port(proc, "nwtc_remport", nwtc->nwtc_remport);
return IF_ALL;
case NWIOTCPCONN:
case NWIOTCPLISTEN:
if ((nwtcl = (nwio_tcpcl_t *)ptr) == NULL)
return dir;
put_flags(proc, "nwtcl_flags", tcpcl_flags,
COUNT(tcpcl_flags), "0x%x", nwtcl->nwtcl_flags);
/* We pretend the unused nwtcl_ttl field does not exist. */
return IF_ALL;
case NWIOSTCPOPT:
case NWIOGTCPOPT:
if ((nwto = (nwio_tcpopt_t *)ptr) == NULL)
return dir;
put_flags(proc, "nwto_flags", tcpopt_flags,
COUNT(tcpopt_flags), "0x%x", nwto->nwto_flags);
return IF_ALL;
case NWIOTCPLISTENQ:
case NWIOSUDSBLOG:
if (ptr == NULL)
return IF_OUT;
put_value(proc, NULL, "%d", *(int *)ptr);
return IF_ALL;
case NWIOGTCPCOOKIE:
case NWIOTCPACCEPTTO:
if ((cookie = (tcp_cookie_t *)ptr) == NULL)
return dir;
put_value(proc, "tc_ref", "%"PRIu32, cookie->tc_ref);
if (verbose > 0)
put_buf(proc, "tc_secret", PF_LOCADDR,
(vir_bytes)&cookie->tc_secret,
sizeof(cookie->tc_secret));
return (verbose > 0) ? IF_ALL : 0;
case NWIOTCPGERROR:
if (ptr == NULL)
return IF_IN;
i = *(int *)ptr;
if (!valuesonly && (text = get_error_name(i)) != NULL)
put_field(proc, NULL, text);
else
put_value(proc, NULL, "%d", i);
return IF_ALL;
case NWIOSUDPOPT:
case NWIOGUDPOPT:
if ((nwuo = (nwio_udpopt_t *)ptr) == NULL)
return dir;
put_flags(proc, "nwuo_flags", udpopt_flags,
COUNT(udpopt_flags), "0x%x", nwuo->nwuo_flags);
/* The local address cannot be set, just retrieved. */
if (req == NWIOGUDPOPT)
put_ipaddr(proc, "nwuo_locaddr", nwuo->nwuo_locaddr);
if ((nwuo->nwuo_flags & NWUO_LOCPORT_MASK) == NWUO_LP_SET)
put_port(proc, "nwuo_locport", nwuo->nwuo_locport);
if (nwuo->nwuo_flags & NWUO_RA_SET)
put_ipaddr(proc, "nwuo_remaddr", nwuo->nwuo_remaddr);
if (nwuo->nwuo_flags & NWUO_RP_SET)
put_port(proc, "nwuo_remport", nwuo->nwuo_remport);
return IF_ALL;
case NWIOGUDSFADDR:
case NWIOSUDSTADDR:
case NWIOSUDSADDR:
case NWIOGUDSADDR:
case NWIOGUDSPADDR:
case NWIOSUDSCONN:
case NWIOSUDSACCEPT:
if ((sun = (struct sockaddr_un *)ptr) == NULL)
return dir;
put_family(proc, "sun_family", sun->sun_family);
/* This could be extended to a generic sockaddr printer.. */
if (sun->sun_family == AF_LOCAL) {
put_buf(proc, "sun_path", PF_LOCADDR | PF_PATH,
(vir_bytes)&sun->sun_path, sizeof(sun->sun_path));
return IF_ALL; /* skipping sun_len, it's unused */
} else
return 0;
case NWIOSUDSTYPE:
case NWIOGUDSSOTYPE:
if (ptr == NULL)
return dir;
put_flags(proc, NULL, sock_type, COUNT(sock_type), "0x%x",
*(int *)ptr);
return IF_ALL;
case NWIOSUDSSHUT:
if (ptr == NULL)
return IF_OUT;
put_shutdown_how(proc, NULL, *(int *)ptr);
return IF_ALL;
case NWIOSUDSPAIR:
if (ptr == NULL)
return IF_OUT;
put_dev(proc, NULL, *(dev_t *)ptr);
return IF_ALL;
case NWIOSUDSCTRL:
if (ptr == NULL)
return IF_OUT;
/* FALLTHROUGH */
case NWIOGUDSCTRL:
if (ptr == NULL)
return IF_IN;
put_msg_control(proc, (struct msg_control *)ptr);
return IF_ALL;
case NWIOGUDSPEERCRED:
if (ptr == NULL)
return IF_IN;
put_struct_uucred(proc, NULL, PF_LOCADDR, (vir_bytes)ptr);
return IF_ALL;
case NWIOGUDSSNDBUF:
case NWIOSUDSSNDBUF:
case NWIOGUDSRCVBUF:
case NWIOSUDSRCVBUF:
if (ptr == NULL)
return dir;
put_value(proc, NULL, "%zu", *(size_t *)ptr);
return IF_ALL;
default:
return 0;
}
}