sockstat(1): print out full connection graph for unix(4) sockets
Kernel provides us with enough information to display all possible connections between UNIX sockets. o Store unp_conn, xu_firstref and xu_nextref in the faddr of a UNIX sock. o Build tree of file descriptors, indexed by the socket pointer. o In displaysock() print out all possible information: 1) if socket is bound, print name of this socket 2) if socket has connected to a peer with a name, print peers name 3) if socket has connected to a peer without a name, print [pid fd] 4) if a bound socket has received connections, print list of them as [pid fd] Previously, only 1) either 2) were printed. Reviewed by: tuexen Differential revision: https://reviews.freebsd.org/D35726
This commit is contained in:
parent
c5bdcd1f10
commit
2c436d4890
@ -27,7 +27,7 @@
|
|||||||
.\"
|
.\"
|
||||||
.\" $FreeBSD$
|
.\" $FreeBSD$
|
||||||
.\"
|
.\"
|
||||||
.Dd February 2, 2022
|
.Dd June 6, 2022
|
||||||
.Dt SOCKSTAT 1
|
.Dt SOCKSTAT 1
|
||||||
.Os
|
.Os
|
||||||
.Sh NAME
|
.Sh NAME
|
||||||
@ -151,18 +151,27 @@ sockets.
|
|||||||
For Internet sockets, this is the address the local end of the socket
|
For Internet sockets, this is the address the local end of the socket
|
||||||
is bound to (see
|
is bound to (see
|
||||||
.Xr getsockname 2 ) .
|
.Xr getsockname 2 ) .
|
||||||
|
.Pp
|
||||||
For bound
|
For bound
|
||||||
.Ux
|
.Ux
|
||||||
sockets, it is the socket's filename.
|
sockets, socket's filename is printed.
|
||||||
For other
|
For not bound
|
||||||
.Ux
|
.Ux
|
||||||
sockets, it is a right arrow followed by the endpoint's filename, or
|
sockets, the field is empty.
|
||||||
.Dq Li ??
|
|
||||||
if the endpoint could not be determined.
|
|
||||||
.It Li FOREIGN ADDRESS
|
.It Li FOREIGN ADDRESS
|
||||||
(Internet sockets only)
|
For Internet sockets, this is the address the foreign end of the socket
|
||||||
The address the foreign end of the socket is bound to (see
|
is bound to (see
|
||||||
.Xr getpeername 2 ) .
|
.Xr getpeername 2 ) .
|
||||||
|
.Pp
|
||||||
|
For bound
|
||||||
|
.Ux
|
||||||
|
sockets a left arrow followed by the peer list is printed.
|
||||||
|
For
|
||||||
|
.Ux
|
||||||
|
sockets that went through
|
||||||
|
.Xr connect 2
|
||||||
|
system call a right arrow followed by the peer is printed.
|
||||||
|
Peers are printed in square brackets as [PID FD].
|
||||||
.It Li ID
|
.It Li ID
|
||||||
The inp_gencnt if
|
The inp_gencnt if
|
||||||
.Fl i
|
.Fl i
|
||||||
|
@ -114,7 +114,14 @@ static int *ports;
|
|||||||
#define CHK_PORT(p) (ports[p / INT_BIT] & (1 << (p % INT_BIT)))
|
#define CHK_PORT(p) (ports[p / INT_BIT] & (1 << (p % INT_BIT)))
|
||||||
|
|
||||||
struct addr {
|
struct addr {
|
||||||
struct sockaddr_storage address;
|
union {
|
||||||
|
struct sockaddr_storage address;
|
||||||
|
struct { /* unix(4) faddr */
|
||||||
|
kvaddr_t conn;
|
||||||
|
kvaddr_t firstref;
|
||||||
|
kvaddr_t nextref;
|
||||||
|
};
|
||||||
|
};
|
||||||
unsigned int encaps_port;
|
unsigned int encaps_port;
|
||||||
int state;
|
int state;
|
||||||
struct addr *next;
|
struct addr *next;
|
||||||
@ -159,8 +166,24 @@ RB_GENERATE_STATIC(pcbs_t, sock, pcb_tree, pcb_compare);
|
|||||||
|
|
||||||
static SLIST_HEAD(, sock) nosocks = SLIST_HEAD_INITIALIZER(&nosocks);
|
static SLIST_HEAD(, sock) nosocks = SLIST_HEAD_INITIALIZER(&nosocks);
|
||||||
|
|
||||||
static struct xfile *xfiles;
|
struct file {
|
||||||
static int nxfiles;
|
RB_ENTRY(file) file_tree;
|
||||||
|
kvaddr_t xf_data;
|
||||||
|
pid_t xf_pid;
|
||||||
|
uid_t xf_uid;
|
||||||
|
int xf_fd;
|
||||||
|
};
|
||||||
|
|
||||||
|
static RB_HEAD(files_t, file) ftree = RB_INITIALIZER(&ftree);
|
||||||
|
static int64_t
|
||||||
|
file_compare(const struct file *a, const struct file *b)
|
||||||
|
{
|
||||||
|
return ((int64_t)(a->xf_data/2 - b->xf_data/2));
|
||||||
|
}
|
||||||
|
RB_GENERATE_STATIC(files_t, file, file_tree, file_compare);
|
||||||
|
|
||||||
|
static struct file *files;
|
||||||
|
static int nfiles;
|
||||||
|
|
||||||
static cap_channel_t *capnet;
|
static cap_channel_t *capnet;
|
||||||
static cap_channel_t *capnetdb;
|
static cap_channel_t *capnetdb;
|
||||||
@ -862,8 +885,9 @@ gather_unix(int proto)
|
|||||||
if (xup->xu_addr.sun_family == AF_UNIX)
|
if (xup->xu_addr.sun_family == AF_UNIX)
|
||||||
laddr->address =
|
laddr->address =
|
||||||
*(struct sockaddr_storage *)(void *)&xup->xu_addr;
|
*(struct sockaddr_storage *)(void *)&xup->xu_addr;
|
||||||
else if (xup->unp_conn != 0)
|
faddr->conn = xup->unp_conn;
|
||||||
*(kvaddr_t*)&(faddr->address) = xup->unp_conn;
|
faddr->firstref = xup->xu_firstref;
|
||||||
|
faddr->nextref = xup->xu_nextref;
|
||||||
laddr->next = NULL;
|
laddr->next = NULL;
|
||||||
faddr->next = NULL;
|
faddr->next = NULL;
|
||||||
sock->laddr = laddr;
|
sock->laddr = laddr;
|
||||||
@ -878,6 +902,7 @@ gather_unix(int proto)
|
|||||||
static void
|
static void
|
||||||
getfiles(void)
|
getfiles(void)
|
||||||
{
|
{
|
||||||
|
struct xfile *xfiles;
|
||||||
size_t len, olen;
|
size_t len, olen;
|
||||||
|
|
||||||
olen = len = sizeof(*xfiles);
|
olen = len = sizeof(*xfiles);
|
||||||
@ -893,7 +918,20 @@ getfiles(void)
|
|||||||
}
|
}
|
||||||
if (len > 0)
|
if (len > 0)
|
||||||
enforce_ksize(xfiles->xf_size, struct xfile);
|
enforce_ksize(xfiles->xf_size, struct xfile);
|
||||||
nxfiles = len / sizeof(*xfiles);
|
nfiles = len / sizeof(*xfiles);
|
||||||
|
|
||||||
|
if ((files = malloc(nfiles * sizeof(struct file))) == NULL)
|
||||||
|
err(1, "malloc()");
|
||||||
|
|
||||||
|
for (int i = 0; i < nfiles; i++) {
|
||||||
|
files[i].xf_data = xfiles[i].xf_data;
|
||||||
|
files[i].xf_pid = xfiles[i].xf_pid;
|
||||||
|
files[i].xf_uid = xfiles[i].xf_uid;
|
||||||
|
files[i].xf_fd = xfiles[i].xf_fd;
|
||||||
|
RB_INSERT(files_t, &ftree, &files[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
free(xfiles);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
@ -1066,10 +1104,8 @@ sctp_path_state(int state)
|
|||||||
static void
|
static void
|
||||||
displaysock(struct sock *s, int pos)
|
displaysock(struct sock *s, int pos)
|
||||||
{
|
{
|
||||||
kvaddr_t p;
|
|
||||||
int first, offset;
|
int first, offset;
|
||||||
struct addr *laddr, *faddr;
|
struct addr *laddr, *faddr;
|
||||||
struct sock *s_tmp;
|
|
||||||
|
|
||||||
while (pos < 30)
|
while (pos < 30)
|
||||||
pos += xprintf(" ");
|
pos += xprintf(" ");
|
||||||
@ -1106,26 +1142,57 @@ displaysock(struct sock *s, int pos)
|
|||||||
if ((laddr == NULL) || (faddr == NULL))
|
if ((laddr == NULL) || (faddr == NULL))
|
||||||
errx(1, "laddr = %p or faddr = %p is NULL",
|
errx(1, "laddr = %p or faddr = %p is NULL",
|
||||||
(void *)laddr, (void *)faddr);
|
(void *)laddr, (void *)faddr);
|
||||||
/* server */
|
if (laddr->address.ss_len == 0 && faddr->conn == 0) {
|
||||||
if (laddr->address.ss_len > 0) {
|
|
||||||
pos += printaddr(&laddr->address);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
/* client */
|
|
||||||
p = *(kvaddr_t*)&(faddr->address);
|
|
||||||
if (p == 0) {
|
|
||||||
pos += xprintf("(not connected)");
|
pos += xprintf("(not connected)");
|
||||||
offset += opt_w ? 92 : 44;
|
offset += opt_w ? 92 : 44;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
pos += xprintf("-> ");
|
/* Local bind(2) address, if any. */
|
||||||
s_tmp = RB_FIND(pcbs_t, &pcbs,
|
if (laddr->address.ss_len > 0)
|
||||||
&(struct sock){ .pcb = p });
|
pos += printaddr(&laddr->address);
|
||||||
if (s_tmp == NULL || s_tmp->laddr == NULL ||
|
/* Remote peer we connect(2) to, if any. */
|
||||||
s_tmp->laddr->address.ss_len == 0)
|
if (faddr->conn != 0) {
|
||||||
pos += xprintf("??");
|
struct sock *p;
|
||||||
else
|
|
||||||
pos += printaddr(&s_tmp->laddr->address);
|
pos += xprintf("%s-> ",
|
||||||
|
laddr->address.ss_len > 0 ? " " : "");
|
||||||
|
p = RB_FIND(pcbs_t, &pcbs,
|
||||||
|
&(struct sock){ .pcb = faddr->conn });
|
||||||
|
if (__predict_false(p == NULL)) {
|
||||||
|
/* XXGL: can this happen at all? */
|
||||||
|
pos += xprintf("??");
|
||||||
|
} else if (p->laddr->address.ss_len == 0) {
|
||||||
|
struct file *f;
|
||||||
|
|
||||||
|
f = RB_FIND(files_t, &ftree,
|
||||||
|
&(struct file){ .xf_data =
|
||||||
|
p->socket });
|
||||||
|
pos += xprintf("[%lu %d]",
|
||||||
|
(u_long)f->xf_pid, f->xf_fd);
|
||||||
|
} else
|
||||||
|
pos += printaddr(&p->laddr->address);
|
||||||
|
}
|
||||||
|
/* Remote peer(s) connect(2)ed to us, if any. */
|
||||||
|
if (faddr->firstref != 0) {
|
||||||
|
struct sock *p;
|
||||||
|
struct file *f;
|
||||||
|
kvaddr_t ref = faddr->firstref;
|
||||||
|
bool fref = true;
|
||||||
|
|
||||||
|
pos += xprintf(" <- ");
|
||||||
|
|
||||||
|
while ((p = RB_FIND(pcbs_t, &pcbs,
|
||||||
|
&(struct sock){ .pcb = ref })) != 0) {
|
||||||
|
f = RB_FIND(files_t, &ftree,
|
||||||
|
&(struct file){ .xf_data =
|
||||||
|
p->socket });
|
||||||
|
pos += xprintf("%s[%lu %d]",
|
||||||
|
fref ? "" : ",",
|
||||||
|
(u_long)f->xf_pid, f->xf_fd);
|
||||||
|
ref = p->faddr->nextref;
|
||||||
|
fref = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
offset += opt_w ? 92 : 44;
|
offset += opt_w ? 92 : 44;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
@ -1228,7 +1295,7 @@ static void
|
|||||||
display(void)
|
display(void)
|
||||||
{
|
{
|
||||||
struct passwd *pwd;
|
struct passwd *pwd;
|
||||||
struct xfile *xf;
|
struct file *xf;
|
||||||
struct sock *s;
|
struct sock *s;
|
||||||
int n, pos;
|
int n, pos;
|
||||||
|
|
||||||
@ -1253,7 +1320,7 @@ display(void)
|
|||||||
printf("\n");
|
printf("\n");
|
||||||
}
|
}
|
||||||
cap_setpassent(cappwd, 1);
|
cap_setpassent(cappwd, 1);
|
||||||
for (xf = xfiles, n = 0; n < nxfiles; ++n, ++xf) {
|
for (xf = files, n = 0; n < nfiles; ++n, ++xf) {
|
||||||
if (xf->xf_data == 0)
|
if (xf->xf_data == 0)
|
||||||
continue;
|
continue;
|
||||||
if (opt_j >= 0 && opt_j != getprocjid(xf->xf_pid))
|
if (opt_j >= 0 && opt_j != getprocjid(xf->xf_pid))
|
||||||
|
Loading…
Reference in New Issue
Block a user