1f9ab90drgrimes/*-
2872b698pfg * SPDX-License-Identifier: BSD-3-Clause
3872b698pfg *
4f9ab90drgrimes * Copyright (c) 1983, 1988, 1993
5f9ab90drgrimes *	The Regents of the University of California.  All rights reserved.
6f9ab90drgrimes *
7f9ab90drgrimes * Redistribution and use in source and binary forms, with or without
8f9ab90drgrimes * modification, are permitted provided that the following conditions
9f9ab90drgrimes * are met:
10f9ab90drgrimes * 1. Redistributions of source code must retain the above copyright
11f9ab90drgrimes *    notice, this list of conditions and the following disclaimer.
12f9ab90drgrimes * 2. Redistributions in binary form must reproduce the above copyright
13f9ab90drgrimes *    notice, this list of conditions and the following disclaimer in the
14f9ab90drgrimes *    documentation and/or other materials provided with the distribution.
157e6cabdimp * 3. Neither the name of the University nor the names of its contributors
16f9ab90drgrimes *    may be used to endorse or promote products derived from this software
17f9ab90drgrimes *    without specific prior written permission.
18f9ab90drgrimes *
19f9ab90drgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20f9ab90drgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21f9ab90drgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22f9ab90drgrimes * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23f9ab90drgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24f9ab90drgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25f9ab90drgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26f9ab90drgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27f9ab90drgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28f9ab90drgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29f9ab90drgrimes * SUCH DAMAGE.
30f9ab90drgrimes */
31f9ab90drgrimes
32e97a8f1charnier#if 0
33cd48a1dcharnier#ifndef lint
34f9ab90drgrimesstatic char sccsid[] = "@(#)unix.c	8.1 (Berkeley) 6/6/93";
35f9ab90drgrimes#endif /* not lint */
36cd48a1dcharnier#endif
37cd48a1dcharnier
38cd48a1dcharnier#include <sys/cdefs.h>
39cd48a1dcharnier__FBSDID("$FreeBSD$");
40f9ab90drgrimes
41f9ab90drgrimes/*
42f9ab90drgrimes * Display protocol blocks in the unix domain.
43f9ab90drgrimes */
44f9ab90drgrimes#include <sys/param.h>
454239068dg#include <sys/queue.h>
46f9ab90drgrimes#include <sys/protosw.h>
47f9ab90drgrimes#include <sys/socket.h>
487168facglebius#define	_WANT_SOCKET
49f9ab90drgrimes#include <sys/socketvar.h>
50f9ab90drgrimes#include <sys/mbuf.h>
51f9ab90drgrimes#include <sys/sysctl.h>
52f9ab90drgrimes#include <sys/un.h>
537168facglebius#define	_WANT_UNPCB
54f9ab90drgrimes#include <sys/unpcb.h>
55f9ab90drgrimes
56f9ab90drgrimes#include <netinet/in.h>
57f9ab90drgrimes
58e37570dwollman#include <errno.h>
598cd84a6phk#include <err.h>
60949dcf3wollman#include <stddef.h>
61e1db503yar#include <stdint.h>
62f9ab90drgrimes#include <stdio.h>
63f9ab90drgrimes#include <stdlib.h>
640ea1b83marcel#include <stdbool.h>
6527187e7jhb#include <strings.h>
66e37570dwollman#include <kvm.h>
670ea1b83marcel#include <libxo/xo.h>
68f9ab90drgrimes#include "netstat.h"
69f9ab90drgrimes
707407056obrienstatic	void unixdomainpr(struct xunpcb *, struct xsocket *);
71f9ab90drgrimes
72e37570dwollmanstatic	const char *const socktype[] =
73e37570dwollman    { "#0", "stream", "dgram", "raw", "rdm", "seqpacket" };
74f9ab90drgrimes
7527187e7jhbstatic int
7627187e7jhbpcblist_sysctl(int type, char **bufp)
77f9ab90drgrimes{
78e37570dwollman	char 	*buf;
79e37570dwollman	size_t	len;
80e37570dwollman	char mibvar[sizeof "net.local.seqpacket.pcblist"];
81f9ab90drgrimes
821b12c4fdelphij	snprintf(mibvar, sizeof(mibvar), "net.local.%s.pcblist", socktype[type]);
8327187e7jhb
8427187e7jhb	len = 0;
8527187e7jhb	if (sysctlbyname(mibvar, 0, &len, 0, 0) < 0) {
8627187e7jhb		if (errno != ENOENT)
870ea1b83marcel			xo_warn("sysctl: %s", mibvar);
8827187e7jhb		return (-1);
8927187e7jhb	}
90d2d25bbaraujo	if ((buf = malloc(len)) == NULL) {
910ea1b83marcel		xo_warnx("malloc %lu bytes", (u_long)len);
9227187e7jhb		return (-2);
9327187e7jhb	}
9427187e7jhb	if (sysctlbyname(mibvar, buf, &len, 0, 0) < 0) {
950ea1b83marcel		xo_warn("sysctl: %s", mibvar);
9627187e7jhb		free(buf);
9727187e7jhb		return (-2);
9827187e7jhb	}
9927187e7jhb	*bufp = buf;
10027187e7jhb	return (0);
10127187e7jhb}
10227187e7jhb
10327187e7jhbstatic int
10427187e7jhbpcblist_kvm(u_long count_off, u_long gencnt_off, u_long head_off, char **bufp)
10527187e7jhb{
10627187e7jhb	struct unp_head head;
1077168facglebius	struct unpcb *unp, unp0, unp_conn;
10827187e7jhb	u_char sun_len;
10927187e7jhb	struct socket so;
11027187e7jhb	struct xunpgen xug;
11127187e7jhb	struct xunpcb xu;
11227187e7jhb	unp_gen_t unp_gencnt;
11327187e7jhb	u_int	unp_count;
11427187e7jhb	char 	*buf, *p;
11527187e7jhb	size_t	len;
11627187e7jhb
11727187e7jhb	if (count_off == 0 || gencnt_off == 0)
11827187e7jhb		return (-2);
11927187e7jhb	if (head_off == 0)
12027187e7jhb		return (-1);
12127187e7jhb	kread(count_off, &unp_count, sizeof(unp_count));
12227187e7jhb	len = 2 * sizeof(xug) + (unp_count + unp_count / 8) * sizeof(xu);
123d2d25bbaraujo	if ((buf = malloc(len)) == NULL) {
1240ea1b83marcel		xo_warnx("malloc %lu bytes", (u_long)len);
12527187e7jhb		return (-2);
12627187e7jhb	}
12727187e7jhb	p = buf;
12827187e7jhb
1297407056obrien#define	COPYOUT(obj, size) do {						\
13027187e7jhb	if (len < (size)) {						\
1310ea1b83marcel		xo_warnx("buffer size exceeded");			\
13227187e7jhb		goto fail;						\
13327187e7jhb	}								\
13427187e7jhb	bcopy((obj), p, (size));					\
13527187e7jhb	len -= (size);							\
13627187e7jhb	p += (size);							\
13727187e7jhb} while (0)
138e37570dwollman
1397407056obrien#define	KREAD(off, buf, len) do {					\
14027187e7jhb	if (kread((uintptr_t)(off), (buf), (len)) != 0)			\
14127187e7jhb		goto fail;						\
14227187e7jhb} while (0)
14327187e7jhb
14427187e7jhb	/* Write out header. */
14527187e7jhb	kread(gencnt_off, &unp_gencnt, sizeof(unp_gencnt));
14627187e7jhb	xug.xug_len = sizeof xug;
14727187e7jhb	xug.xug_count = unp_count;
14827187e7jhb	xug.xug_gen = unp_gencnt;
14927187e7jhb	xug.xug_sogen = 0;
15027187e7jhb	COPYOUT(&xug, sizeof xug);
15127187e7jhb
15227187e7jhb	/* Walk the PCB list. */
15327187e7jhb	xu.xu_len = sizeof xu;
15427187e7jhb	KREAD(head_off, &head, sizeof(head));
15527187e7jhb	LIST_FOREACH(unp, &head, unp_link) {
15639f527ebrooks		xu.xu_unpp = (uintptr_t)unp;
1577168facglebius		KREAD(unp, &unp0, sizeof (*unp));
1587168facglebius		unp = &unp0;
15927187e7jhb
16027187e7jhb		if (unp->unp_gencnt > unp_gencnt)
161f9ab90drgrimes			continue;
16227187e7jhb		if (unp->unp_addr != NULL) {
16327187e7jhb			KREAD(unp->unp_addr, &sun_len, sizeof(sun_len));
16427187e7jhb			KREAD(unp->unp_addr, &xu.xu_addr, sun_len);
165e37570dwollman		}
16627187e7jhb		if (unp->unp_conn != NULL) {
16727187e7jhb			KREAD(unp->unp_conn, &unp_conn, sizeof(unp_conn));
16827187e7jhb			if (unp_conn.unp_addr != NULL) {
16927187e7jhb				KREAD(unp_conn.unp_addr, &sun_len,
17027187e7jhb				    sizeof(sun_len));
17127187e7jhb				KREAD(unp_conn.unp_addr, &xu.xu_caddr, sun_len);
17227187e7jhb			}
173e37570dwollman		}
17427187e7jhb		KREAD(unp->unp_socket, &so, sizeof(so));
17527187e7jhb		if (sotoxsocket(&so, &xu.xu_socket) != 0)
17627187e7jhb			goto fail;
17727187e7jhb		COPYOUT(&xu, sizeof(xu));
17827187e7jhb	}
17927187e7jhb
18027187e7jhb	/* Reread the counts and write the footer. */
18127187e7jhb	kread(count_off, &unp_count, sizeof(unp_count));
18227187e7jhb	kread(gencnt_off, &unp_gencnt, sizeof(unp_gencnt));
18327187e7jhb	xug.xug_count = unp_count;
18427187e7jhb	xug.xug_gen = unp_gencnt;
18527187e7jhb	COPYOUT(&xug, sizeof xug);
18627187e7jhb
18727187e7jhb	*bufp = buf;
18827187e7jhb	return (0);
18927187e7jhb
19027187e7jhbfail:
19127187e7jhb	free(buf);
19227187e7jhb	return (-1);
19327187e7jhb#undef COPYOUT
19427187e7jhb#undef KREAD
19527187e7jhb}
19627187e7jhb
19727187e7jhbvoid
19839779f4rwatsonunixpr(u_long count_off, u_long gencnt_off, u_long dhead_off, u_long shead_off,
1990ea1b83marcel    u_long sphead_off, bool *first)
20027187e7jhb{
20127187e7jhb	char 	*buf;
20227187e7jhb	int	ret, type;
20327187e7jhb	struct	xsocket *so;
20427187e7jhb	struct	xunpgen *xug, *oxug;
20527187e7jhb	struct	xunpcb *xunp;
20639779f4rwatson	u_long	head_off;
20727187e7jhb
2088f92a0echarnier	buf = NULL;
20927187e7jhb	for (type = SOCK_STREAM; type <= SOCK_SEQPACKET; type++) {
21027187e7jhb		if (live)
21127187e7jhb			ret = pcblist_sysctl(type, &buf);
21239779f4rwatson		else {
21339779f4rwatson			head_off = 0;
21439779f4rwatson			switch (type) {
21539779f4rwatson			case SOCK_STREAM:
21639779f4rwatson				head_off = shead_off;
21739779f4rwatson				break;
21839779f4rwatson
21939779f4rwatson			case SOCK_DGRAM:
22039779f4rwatson				head_off = dhead_off;
22139779f4rwatson				break;
22239779f4rwatson
22339779f4rwatson			case SOCK_SEQPACKET:
22439779f4rwatson				head_off = sphead_off;
22539779f4rwatson				break;
22639779f4rwatson			}
22739779f4rwatson			ret = pcblist_kvm(count_off, gencnt_off, head_off,
22839779f4rwatson			    &buf);
22939779f4rwatson		}
23027187e7jhb		if (ret == -1)
23127187e7jhb			continue;
23227187e7jhb		if (ret < 0)
233e37570dwollman			return;
234e37570dwollman
235e37570dwollman		oxug = xug = (struct xunpgen *)buf;
236e37570dwollman		for (xug = (struct xunpgen *)((char *)xug + xug->xug_len);
2370ea1b83marcel		    xug->xug_len > sizeof(struct xunpgen);
2380ea1b83marcel		    xug = (struct xunpgen *)((char *)xug + xug->xug_len)) {
239e37570dwollman			xunp = (struct xunpcb *)xug;
240e37570dwollman			so = &xunp->xu_socket;
241e37570dwollman
242e37570dwollman			/* Ignore PCBs which were freed during copyout. */
2437168facglebius			if (xunp->unp_gencnt > oxug->xug_gen)
244e37570dwollman				continue;
2450ea1b83marcel			if (*first) {
2460ea1b83marcel				xo_open_list("socket");
2470ea1b83marcel				*first = false;
2480ea1b83marcel			}
2490ea1b83marcel			xo_open_instance("socket");
250e37570dwollman			unixdomainpr(xunp, so);
2510ea1b83marcel			xo_close_instance("socket");
252e37570dwollman		}
253e37570dwollman		if (xug != oxug && xug->xug_gen != oxug->xug_gen) {
254e37570dwollman			if (oxug->xug_count > xug->xug_count) {
2550ea1b83marcel				xo_emit("Some {:type/%s} sockets may have "
2560ea1b83marcel				    "been {:action/deleted}.\n",
2570ea1b83marcel				    socktype[type]);
258e37570dwollman			} else if (oxug->xug_count < xug->xug_count) {
2590ea1b83marcel				xo_emit("Some {:type/%s} sockets may have "
2600ea1b83marcel				    "been {:action/created}.\n",
2610ea1b83marcel				    socktype[type]);
262e37570dwollman			} else {
2630ea1b83marcel				xo_emit("Some {:type/%s} sockets may have "
2640ea1b83marcel				    "been {:action/created or deleted}",
2650ea1b83marcel				    socktype[type]);
266e37570dwollman			}
267e37570dwollman		}
268e37570dwollman		free(buf);
269f9ab90drgrimes	}
270f9ab90drgrimes}
271f9ab90drgrimes
272f9ab90drgrimesstatic void
273ee746c9assarunixdomainpr(struct xunpcb *xunp, struct xsocket *so)
274f9ab90drgrimes{
275e37570dwollman	struct sockaddr_un *sa;
276f9ab90drgrimes	static int first = 1;
277b04e8a5alfred	char buf1[33];
2780ea1b83marcel	static const char *titles[2] = {
2790ea1b83marcel	    "{T:/%-8.8s} {T:/%-6.6s} {T:/%-6.6s} {T:/%-6.6s} {T:/%8.8s} "
2800ea1b83marcel	    "{T:/%8.8s} {T:/%8.8s} {T:/%8.8s} {T:Addr}\n",
2810ea1b83marcel	    "{T:/%-16.16s} {T:/%-6.6s} {T:/%-6.6s} {T:/%-6.6s} {T:/%16.16s} "
2820ea1b83marcel	    "{T:/%16.16s} {T:/%16.16s} {T:/%16.16s} {T:Addr}\n"
2830ea1b83marcel	};
2840ea1b83marcel	static const char *format[2] = {
2850ea1b83marcel	    "{q:address/%8lx} {t:type/%-6.6s} "
2860ea1b83marcel	    "{:receive-bytes-waiting/%6u} "
2870ea1b83marcel	    "{:send-bytes-waiting/%6u} "
2880ea1b83marcel	    "{q:vnode/%8lx} {q:connection/%8lx} "
2890ea1b83marcel	    "{q:first-reference/%8lx} {q:next-reference/%8lx}",
2900ea1b83marcel	    "{q:address/%16lx} {t:type/%-6.6s} "
2910ea1b83marcel	    "{:receive-bytes-waiting/%6u} "
2920ea1b83marcel	    "{:send-bytes-waiting/%6u} "
2930ea1b83marcel	    "{q:vnode/%16lx} {q:connection/%16lx} "
2940ea1b83marcel	    "{q:first-reference/%16lx} {q:next-reference/%16lx}"
2950ea1b83marcel	};
2960ea1b83marcel	int fmt = (sizeof(void *) == 8) ? 1 : 0;
297f9ab90drgrimes
2987168facglebius	sa = (xunp->xu_addr.sun_family == AF_UNIX) ? &xunp->xu_addr : NULL;
299e37570dwollman
3002175093maxim	if (first && !Lflag) {
3010ea1b83marcel		xo_emit("{T:Active UNIX domain sockets}\n");
3020ea1b83marcel		xo_emit(titles[fmt],
303f9ab90drgrimes		    "Address", "Type", "Recv-Q", "Send-Q",
304f9ab90drgrimes		    "Inode", "Conn", "Refs", "Nextref");
305f9ab90drgrimes		first = 0;
306f9ab90drgrimes	}
3072175093maxim
3082175093maxim	if (Lflag && so->so_qlimit == 0)
3092175093maxim		return;
3102175093maxim
3112175093maxim	if (Lflag) {
312b04e8a5alfred		snprintf(buf1, sizeof buf1, "%u/%u/%u", so->so_qlen,
3132175093maxim		    so->so_incqlen, so->so_qlimit);
314b04e8a5alfred		xo_emit("unix  {d:socket/%-32.32s}{e:queue-length/%u}"
315b04e8a5alfred		    "{e:incomplete-queue-length/%u}{e:queue-limit/%u}",
3160ea1b83marcel		    buf1, so->so_qlen, so->so_incqlen, so->so_qlimit);
3172175093maxim	} else {
3180ea1b83marcel		xo_emit(format[fmt],
3192175093maxim		    (long)so->so_pcb, socktype[so->so_type], so->so_rcv.sb_cc,
3207168facglebius		    so->so_snd.sb_cc, (long)xunp->unp_vnode,
3217168facglebius		    (long)xunp->unp_conn, (long)xunp->xu_firstref,
3227168facglebius		    (long)xunp->xu_nextref);
3232175093maxim	}
324143b997wollman	if (sa)
3250ea1b83marcel		xo_emit(" {:path/%.*s}",
3260609531bde		    (int)(sa->sun_len - offsetof(struct sockaddr_un, sun_path)),
3271ec8848ache		    sa->sun_path);
3280ea1b83marcel	xo_emit("\n");
329f9ab90drgrimes}
330