1 /*-
2 * SPDX-License-Identifier: BSD-3-Clause
3 *
4 * Copyright (c) 1980, 1992, 1993
5 * The Regents of the University of California. All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * 3. Neither the name of the University nor the names of its contributors
16 * may be used to endorse or promote products derived from this software
17 * without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 * SUCH DAMAGE.
30 */
31
32
33
34 /*
35 * netstat
36 */
37 #include <sys/param.h>
38 #include <sys/queue.h>
39 #include <sys/socket.h>
40 #include <sys/callout.h>
41 #define _WANT_SOCKET
42 #include <sys/socketvar.h>
43 #include <sys/protosw.h>
44
45 #include <netinet/in.h>
46 #include <arpa/inet.h>
47 #include <net/route.h>
48 #include <netinet/in_systm.h>
49 #include <netinet/ip.h>
50 #ifdef INET6
51 #include <netinet/ip6.h>
52 #endif
53 #define _WANT_INPCB
54 #include <netinet/in_pcb.h>
55 #include <netinet/ip_icmp.h>
56 #include <netinet/icmp_var.h>
57 #include <netinet/ip_var.h>
58 #include <netinet/tcp.h>
59 #include <netinet/tcpip.h>
60 #include <netinet/tcp_seq.h>
61 #define TCPSTATES
62 #include <netinet/tcp_fsm.h>
63 #include <netinet/tcp_timer.h>
64 #define _WANT_TCPCB
65 #include <netinet/tcp_var.h>
66 #include <netinet/udp.h>
67 #include <netinet/udp_var.h>
68
69 #include <netdb.h>
70 #include <nlist.h>
71 #include <paths.h>
72 #include <stdlib.h>
73 #include <string.h>
74
75 #include "systat.h"
76 #include "extern.h"
77
78 static struct netinfo *enter(struct in_conninfo *, uint8_t, int, const char *);
79 static void enter_kvm(struct inpcb *, struct socket *, int, const char *);
80 static void enter_sysctl(struct xinpcb *, struct xsocket *, int, const char *);
81 static void fetchnetstat_kvm(void);
82 static void fetchnetstat_sysctl(void);
83 static char *inetname(struct sockaddr *);
84 static void inetprint(struct sockaddr *, const char *);
85
86 #define streq(a,b) (strcmp(a,b)==0)
87 #define YMAX(w) (getmaxy(w)-2)
88
89 WINDOW *
opennetstat(void)90 opennetstat(void)
91 {
92 sethostent(1);
93 setnetent(1);
94 return (subwin(stdscr, LINES-3-1, 0, MAINWIN_ROW, 0));
95 }
96
97 struct netinfo {
98 TAILQ_ENTRY(netinfo) chain;
99 short ni_line; /* line on screen */
100 short ni_seen; /* 0 when not present in list */
101 short ni_flags;
102 #define NIF_LACHG 0x1 /* local address changed */
103 #define NIF_FACHG 0x2 /* foreign address changed */
104 short ni_state; /* tcp state */
105 const char *ni_proto; /* protocol */
106 struct sockaddr_storage ni_lsa; /* local address */
107 struct sockaddr_storage ni_fsa; /* foreign address */
108 u_int ni_rcvcc; /* rcv buffer character count */
109 u_int ni_sndcc; /* snd buffer character count */
110 };
111
112 static TAILQ_HEAD(netinfohead, netinfo) netcb = TAILQ_HEAD_INITIALIZER(netcb);
113
114 static int aflag = 0;
115 static int nflag = 0;
116 static int lastrow = 1;
117
118 void
closenetstat(WINDOW * w)119 closenetstat(WINDOW *w)
120 {
121 struct netinfo *p;
122
123 endhostent();
124 endnetent();
125 TAILQ_FOREACH(p, &netcb, chain) {
126 if (p->ni_line != -1)
127 lastrow--;
128 p->ni_line = -1;
129 }
130 if (w != NULL) {
131 wclear(w);
132 wrefresh(w);
133 delwin(w);
134 }
135 }
136
137 static const char *miblist[] = {
138 "net.inet.tcp.pcblist",
139 "net.inet.udp.pcblist"
140 };
141
142 static char tcb[] = "tcb", udb[] = "udb";
143
144 struct nlist namelist[] = {
145 #define X_TCB 0
146 { .n_name = tcb },
147 #define X_UDB 1
148 { .n_name = udb },
149 { .n_name = NULL },
150 };
151
152 int
initnetstat(void)153 initnetstat(void)
154 {
155 protos = TCP|UDP;
156 return(1);
157 }
158
159 void
fetchnetstat(void)160 fetchnetstat(void)
161 {
162 if (use_kvm)
163 fetchnetstat_kvm();
164 else
165 fetchnetstat_sysctl();
166 }
167
168 static void
fetchnetstat_kvm(void)169 fetchnetstat_kvm(void)
170 {
171 struct netinfo *p;
172 struct inpcbhead head;
173 struct socket sockb;
174 struct tcpcb tcpcb;
175 struct inpcb *inpcb;
176 void *off;
177 int istcp;
178
179 if (namelist[X_TCB].n_value == 0)
180 return;
181 TAILQ_FOREACH(p, &netcb, chain)
182 p->ni_seen = 0;
183 if (protos&TCP) {
184 off = NPTR(X_TCB);
185 istcp = 1;
186 }
187 else if (protos&UDP) {
188 off = NPTR(X_UDB);
189 istcp = 0;
190 }
191 else {
192 error("No protocols to display");
193 return;
194 }
195 again:
196 KREAD(off, &head, sizeof (struct inpcbhead));
197 LIST_FOREACH(inpcb, &head, inp_list) {
198 KREAD(inpcb, &tcpcb, istcp ? sizeof(tcpcb) : sizeof(inpcb));
199 inpcb = (struct inpcb *)&tcpcb;
200 if (!aflag) {
201 if (inpcb->inp_vflag & INP_IPV4) {
202 if (inpcb->inp_laddr.s_addr == INADDR_ANY)
203 continue;
204 }
205 #ifdef INET6
206 else if (inpcb->inp_vflag & INP_IPV6) {
207 if (memcmp(&inpcb->in6p_laddr,
208 &in6addr_any, sizeof(in6addr_any)) == 0)
209 continue;
210 }
211 #endif
212 }
213 if (nhosts && !checkhost(&inpcb->inp_inc))
214 continue;
215 if (nports && !checkport(&inpcb->inp_inc))
216 continue;
217 if (istcp) {
218 KREAD(inpcb->inp_socket, &sockb, sizeof (sockb));
219 enter_kvm(inpcb, &sockb, tcpcb.t_state, "tcp");
220 } else
221 enter_kvm(inpcb, &sockb, 0, "udp");
222 }
223 if (istcp && (protos&UDP)) {
224 istcp = 0;
225 off = NPTR(X_UDB);
226 goto again;
227 }
228 }
229
230 static void
fetchnetstat_sysctl(void)231 fetchnetstat_sysctl(void)
232 {
233 struct netinfo *p;
234 int idx;
235 struct xinpgen *inpg;
236 char *cur, *end;
237 struct xinpcb *xip = NULL;
238 struct xtcpcb *xtp = NULL;
239 int plen;
240 size_t lsz;
241
242 TAILQ_FOREACH(p, &netcb, chain)
243 p->ni_seen = 0;
244 if (protos&TCP) {
245 idx = 0;
246 } else if (protos&UDP) {
247 idx = 1;
248 } else {
249 error("No protocols to display");
250 return;
251 }
252
253 for (;idx < 2; idx++) {
254 if (idx == 1 && !(protos&UDP))
255 break;
256 inpg = (struct xinpgen *)sysctl_dynread(miblist[idx], &lsz);
257 if (inpg == NULL) {
258 error("sysctl(%s...) failed", miblist[idx]);
259 continue;
260 }
261 /*
262 * We currently do no require a consistent pcb list.
263 * Try to be robust in case of struct size changes
264 */
265 cur = ((char *)inpg) + inpg->xig_len;
266 /* There is also a trailing struct xinpgen */
267 end = ((char *)inpg) + lsz - inpg->xig_len;
268 if (end <= cur) {
269 free(inpg);
270 continue;
271 }
272 if (idx == 0) { /* TCP */
273 xtp = (struct xtcpcb *)cur;
274 plen = xtp->xt_len;
275 } else {
276 xip = (struct xinpcb *)cur;
277 plen = xip->xi_len;
278 }
279 while (cur + plen <= end) {
280 if (idx == 0) { /* TCP */
281 xtp = (struct xtcpcb *)cur;
282 xip = &xtp->xt_inp;
283 } else {
284 xip = (struct xinpcb *)cur;
285 }
286 cur += plen;
287
288 if (!aflag) {
289 if (xip->inp_vflag & INP_IPV4) {
290 if (xip->inp_laddr.s_addr == INADDR_ANY)
291 continue;
292 }
293 #ifdef INET6
294 else if (xip->inp_vflag & INP_IPV6) {
295 if (memcmp(&xip->in6p_laddr,
296 &in6addr_any, sizeof(in6addr_any))
297 == 0)
298 continue;
299 }
300 #endif
301 }
302 if (nhosts && !checkhost(&xip->inp_inc))
303 continue;
304 if (nports && !checkport(&xip->inp_inc))
305 continue;
306 if (idx == 0)
307 enter_sysctl(xip, &xip->xi_socket,
308 xtp->t_state, "tcp");
309 else
310 enter_sysctl(xip, &xip->xi_socket, 0, "udp");
311 }
312 free(inpg);
313 }
314 }
315
316 static void
enter_kvm(struct inpcb * inp,struct socket * so,int state,const char * proto)317 enter_kvm(struct inpcb *inp, struct socket *so, int state, const char *proto)
318 {
319 struct netinfo *p;
320
321 if ((p = enter(&inp->inp_inc, inp->inp_vflag, state, proto)) != NULL) {
322 p->ni_rcvcc = so->so_rcv.sb_ccc;
323 p->ni_sndcc = so->so_snd.sb_ccc;
324 }
325 }
326
327 static void
enter_sysctl(struct xinpcb * xip,struct xsocket * so,int state,const char * proto)328 enter_sysctl(struct xinpcb *xip, struct xsocket *so, int state,
329 const char *proto)
330 {
331 struct netinfo *p;
332
333 if ((p = enter(&xip->inp_inc, xip->inp_vflag, state, proto)) != NULL) {
334 p->ni_rcvcc = so->so_rcv.sb_cc;
335 p->ni_sndcc = so->so_snd.sb_cc;
336 }
337 }
338
339 static struct netinfo *
enter(struct in_conninfo * inc,uint8_t vflag,int state,const char * proto)340 enter(struct in_conninfo *inc, uint8_t vflag, int state, const char *proto)
341 {
342 struct netinfo *p;
343 struct sockaddr_storage lsa, fsa;
344 struct sockaddr_in *sa4;
345 #ifdef INET6
346 struct sockaddr_in6 *sa6;
347 #endif
348
349 memset(&lsa, 0, sizeof(lsa));
350 memset(&fsa, 0, sizeof(fsa));
351 if (vflag & INP_IPV4) {
352 sa4 = (struct sockaddr_in *)&lsa;
353 sa4->sin_addr = inc->inc_laddr;
354 sa4->sin_port = inc->inc_lport;
355 sa4->sin_family = AF_INET;
356 sa4->sin_len = sizeof(struct sockaddr_in);
357
358 sa4 = (struct sockaddr_in *)&fsa;
359 sa4->sin_addr = inc->inc_faddr;
360 sa4->sin_port = inc->inc_fport;
361 sa4->sin_family = AF_INET;
362 sa4->sin_len = sizeof(struct sockaddr_in);
363 }
364 #ifdef INET6
365 else if (vflag & INP_IPV6) {
366 sa6 = (struct sockaddr_in6 *)&lsa;
367 memcpy(&sa6->sin6_addr, &inc->inc6_laddr,
368 sizeof(struct in6_addr));
369 sa6->sin6_port = inc->inc_lport;
370 sa6->sin6_family = AF_INET6;
371 sa6->sin6_len = sizeof(struct sockaddr_in6);
372
373 sa6 = (struct sockaddr_in6 *)&fsa;
374 memcpy(&sa6->sin6_addr, &inc->inc6_faddr,
375 sizeof(struct in6_addr));
376 sa6->sin6_port = inc->inc_fport;
377 sa6->sin6_family = AF_INET6;
378 sa6->sin6_len = sizeof(struct sockaddr_in6);
379 }
380 #endif
381 else
382 return NULL;
383
384 /*
385 * Only take exact matches, any sockets with
386 * previously unbound addresses will be deleted
387 * below in the display routine because they
388 * will appear as ``not seen'' in the kernel
389 * data structures.
390 */
391 TAILQ_FOREACH(p, &netcb, chain) {
392 if (!streq(proto, p->ni_proto))
393 continue;
394 if (p->ni_lsa.ss_family != lsa.ss_family ||
395 memcmp(&p->ni_lsa, &lsa, lsa.ss_len) != 0)
396 continue;
397 if (p->ni_fsa.ss_family == fsa.ss_family &&
398 memcmp(&p->ni_fsa, &fsa, fsa.ss_len) == 0)
399 break;
400 }
401 if (p == NULL) {
402 if ((p = malloc(sizeof(*p))) == NULL) {
403 error("Out of memory");
404 return NULL;
405 }
406 TAILQ_INSERT_HEAD(&netcb, p, chain);
407 p->ni_line = -1;
408 memcpy(&p->ni_lsa, &lsa, lsa.ss_len);
409 memcpy(&p->ni_fsa, &fsa, fsa.ss_len);
410 p->ni_proto = strdup(proto);
411 p->ni_flags = NIF_LACHG|NIF_FACHG;
412 }
413 p->ni_state = state;
414 p->ni_seen = 1;
415 return p;
416 }
417
418 /* column locations */
419 #define LADDR 0
420 #define FADDR LADDR+23
421 #define PROTO FADDR+23
422 #define RCVCC PROTO+6
423 #define SNDCC RCVCC+7
424 #define STATE SNDCC+7
425
426 void
labelnetstat(void)427 labelnetstat(void)
428 {
429 if (use_kvm && namelist[X_TCB].n_type == 0)
430 return;
431 wmove(wnd, 0, 0); wclrtobot(wnd);
432 mvwaddstr(wnd, 0, LADDR, "Local Address");
433 mvwaddstr(wnd, 0, FADDR, "Foreign Address");
434 mvwaddstr(wnd, 0, PROTO, "Proto");
435 mvwaddstr(wnd, 0, RCVCC, "Recv-Q");
436 mvwaddstr(wnd, 0, SNDCC, "Send-Q");
437 mvwaddstr(wnd, 0, STATE, "(state)");
438 }
439
440 void
shownetstat(void)441 shownetstat(void)
442 {
443 struct netinfo *p, *q;
444 char proto[6];
445 const char *family = "";
446
447 /*
448 * First, delete any connections that have gone
449 * away and adjust the position of connections
450 * below to reflect the deleted line.
451 */
452 p = TAILQ_FIRST(&netcb);
453 while (p != NULL) {
454 if (p->ni_line == -1 || p->ni_seen) {
455 p = TAILQ_NEXT(p, chain);
456 continue;
457 }
458 wmove(wnd, p->ni_line, 0); wdeleteln(wnd);
459 TAILQ_FOREACH(q, &netcb, chain)
460 if (q != p && q->ni_line > p->ni_line) {
461 q->ni_line--;
462 /* this shouldn't be necessary */
463 q->ni_flags |= NIF_LACHG|NIF_FACHG;
464 }
465 lastrow--;
466 q = TAILQ_NEXT(p, chain);
467 TAILQ_REMOVE(&netcb, p, chain);
468 free(p);
469 p = q;
470 }
471 /*
472 * Update existing connections and add new ones.
473 */
474 TAILQ_FOREACH(p, &netcb, chain) {
475 if (p->ni_line == -1) {
476 /*
477 * Add a new entry if possible.
478 */
479 if (lastrow > YMAX(wnd))
480 continue;
481 p->ni_line = lastrow++;
482 p->ni_flags |= NIF_LACHG|NIF_FACHG;
483 }
484 if (p->ni_flags & NIF_LACHG) {
485 wmove(wnd, p->ni_line, LADDR);
486 inetprint((struct sockaddr *)&p->ni_lsa, p->ni_proto);
487 p->ni_flags &= ~NIF_LACHG;
488 }
489 if (p->ni_flags & NIF_FACHG) {
490 wmove(wnd, p->ni_line, FADDR);
491 inetprint((struct sockaddr *)&p->ni_fsa, p->ni_proto);
492 p->ni_flags &= ~NIF_FACHG;
493 }
494 #ifdef INET6
495 family = (p->ni_lsa.ss_family == AF_INET) ? "4" : "6";
496 #endif
497 snprintf(proto, sizeof(proto), "%s%s", p->ni_proto, family);
498 mvwaddstr(wnd, p->ni_line, PROTO, proto);
499 mvwprintw(wnd, p->ni_line, RCVCC, "%6u", p->ni_rcvcc);
500 mvwprintw(wnd, p->ni_line, SNDCC, "%6u", p->ni_sndcc);
501 if (streq(p->ni_proto, "tcp")) {
502 if (p->ni_state < 0 || p->ni_state >= TCP_NSTATES)
503 mvwprintw(wnd, p->ni_line, STATE, "%d",
504 p->ni_state);
505 else
506 mvwaddstr(wnd, p->ni_line, STATE,
507 tcpstates[p->ni_state]);
508 }
509 wclrtoeol(wnd);
510 }
511 if (lastrow < YMAX(wnd)) {
512 wmove(wnd, lastrow, 0); wclrtobot(wnd);
513 wmove(wnd, YMAX(wnd), 0); wdeleteln(wnd); /* XXX */
514 }
515 }
516
517 /*
518 * Pretty print an Internet address (net address + port).
519 * If the nflag was specified, use numbers instead of names.
520 */
521 static void
inetprint(struct sockaddr * sa,const char * proto)522 inetprint(struct sockaddr *sa, const char *proto)
523 {
524 struct servent *sp = 0;
525 char line[80], *cp;
526 int port;
527
528 switch (sa->sa_family) {
529 case AF_INET:
530 port = ((struct sockaddr_in *)sa)->sin_port;
531 break;
532 #ifdef INET6
533 case AF_INET6:
534 port = ((struct sockaddr_in6 *)sa)->sin6_port;
535 break;
536 #endif
537 default:
538 port = 0;
539 break;
540 }
541 snprintf(line, sizeof(line), "%.*s.", 16, inetname(sa));
542 cp = strchr(line, '\0');
543 if (!nflag && port)
544 sp = getservbyport(port, proto);
545 if (sp || port == 0)
546 snprintf(cp, sizeof(line) - (cp - line), "%.8s",
547 sp ? sp->s_name : "*");
548 else
549 snprintf(cp, sizeof(line) - (cp - line), "%d",
550 ntohs((u_short)port));
551 /* pad to full column to clear any garbage */
552 cp = strchr(line, '\0');
553 while (cp - line < 22)
554 *cp++ = ' ';
555 line[22] = '\0';
556 waddstr(wnd, line);
557 }
558
559 /*
560 * Construct an Internet address representation.
561 * If the nflag has been supplied, give
562 * numeric value, otherwise try for symbolic name.
563 */
564 static char *
inetname(struct sockaddr * sa)565 inetname(struct sockaddr *sa)
566 {
567 char *cp = 0;
568 static char line[NI_MAXHOST];
569 struct hostent *hp;
570 struct in_addr in;
571
572 #ifdef INET6
573 if (sa->sa_family == AF_INET6) {
574 if (memcmp(&((struct sockaddr_in6 *)sa)->sin6_addr,
575 &in6addr_any, sizeof(in6addr_any)) == 0)
576 strcpy(line, "*");
577 else
578 getnameinfo(sa, sa->sa_len, line, sizeof(line), NULL, 0,
579 nflag ? NI_NUMERICHOST : 0);
580 return (line);
581 }
582 #endif
583
584 in = ((struct sockaddr_in *)sa)->sin_addr;
585 if (!nflag && in.s_addr != INADDR_ANY) {
586 hp = gethostbyaddr((char *)&in, sizeof (in), AF_INET);
587 if (hp)
588 cp = hp->h_name;
589 }
590 if (in.s_addr == INADDR_ANY)
591 strcpy(line, "*");
592 else if (cp)
593 snprintf(line, sizeof(line), "%s", cp);
594 else {
595 in.s_addr = ntohl(in.s_addr);
596 #define C(x) ((x) & 0xff)
597 snprintf(line, sizeof(line), "%u.%u.%u.%u", C(in.s_addr >> 24),
598 C(in.s_addr >> 16), C(in.s_addr >> 8), C(in.s_addr));
599 }
600 return (line);
601 }
602
603 int
cmdnetstat(const char * cmd,const char * args)604 cmdnetstat(const char *cmd, const char *args)
605 {
606 if (prefix(cmd, "all")) {
607 aflag = !aflag;
608 goto fixup;
609 }
610 if (prefix(cmd, "numbers") || prefix(cmd, "names")) {
611 struct netinfo *p;
612 int new;
613
614 new = prefix(cmd, "numbers");
615 if (new == nflag)
616 return (1);
617 TAILQ_FOREACH(p, &netcb, chain) {
618 if (p->ni_line == -1)
619 continue;
620 p->ni_flags |= NIF_LACHG|NIF_FACHG;
621 }
622 nflag = new;
623 goto redisplay;
624 }
625 if (!netcmd(cmd, args))
626 return (0);
627 fixup:
628 fetchnetstat();
629 redisplay:
630 shownetstat();
631 refresh();
632 return (1);
633 }
634