Annotation of src/usr.bin/systat/netstat.c, Revision 1.31
1.31 ! canacar 1: /* $OpenBSD: netstat.c,v 1.30 2007/02/25 18:21:24 deraadt Exp $ */
1.1 deraadt 2: /* $NetBSD: netstat.c,v 1.3 1995/06/18 23:53:07 cgd Exp $ */
3:
4: /*-
5: * Copyright (c) 1980, 1992, 1993
6: * The Regents of the University of California. All rights reserved.
7: *
8: * Redistribution and use in source and binary forms, with or without
9: * modification, are permitted provided that the following conditions
10: * are met:
11: * 1. Redistributions of source code must retain the above copyright
12: * notice, this list of conditions and the following disclaimer.
13: * 2. Redistributions in binary form must reproduce the above copyright
14: * notice, this list of conditions and the following disclaimer in the
15: * documentation and/or other materials provided with the distribution.
1.24 millert 16: * 3. Neither the name of the University nor the names of its contributors
1.1 deraadt 17: * may be used to endorse or promote products derived from this software
18: * without specific prior written permission.
19: *
20: * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21: * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23: * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24: * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25: * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26: * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27: * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28: * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29: * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30: * SUCH DAMAGE.
31: */
32:
33: /*
34: * netstat
35: */
36: #include <sys/param.h>
37: #include <sys/socket.h>
38: #include <sys/socketvar.h>
39: #include <sys/mbuf.h>
40: #include <sys/protosw.h>
41:
42: #include <netinet/in.h>
43: #include <net/route.h>
44: #include <netinet/in_systm.h>
45: #include <netinet/ip.h>
46: #include <netinet/in_pcb.h>
47: #include <netinet/ip_icmp.h>
48: #include <netinet/icmp_var.h>
49: #include <netinet/ip_var.h>
50: #include <netinet/tcp.h>
51: #include <netinet/tcpip.h>
52: #include <netinet/tcp_seq.h>
53: #define TCPSTATES
54: #include <netinet/tcp_fsm.h>
55: #include <netinet/tcp_timer.h>
56: #include <netinet/tcp_var.h>
57: #include <netinet/tcp_debug.h>
58: #include <netinet/udp.h>
59: #include <netinet/udp_var.h>
1.7 millert 60: #include <arpa/inet.h>
1.1 deraadt 61:
62: #include <netdb.h>
63: #include <stdlib.h>
64: #include <string.h>
1.16 pvalchev 65: #include <err.h>
1.1 deraadt 66: #include <nlist.h>
67: #include <paths.h>
68: #include "systat.h"
1.31 ! canacar 69: #include "engine.h"
! 70:
! 71: struct netinfo {
! 72: union {
! 73: struct in_addr nif_laddr; /* local address */
! 74: struct in6_addr nif_laddr6; /* local address */
! 75: } l;
! 76: union {
! 77: struct in_addr nif_faddr; /* foreign address */
! 78: struct in6_addr nif_faddr6; /* foreign address */
! 79: } f;
! 80: char *nif_proto; /* protocol */
! 81: long nif_rcvcc; /* rcv buffer character count */
! 82: long nif_sndcc; /* snd buffer character count */
! 83: short nif_lport; /* local port */
! 84: short nif_fport; /* foreign port */
! 85: short nif_state; /* tcp state */
! 86: short nif_family;
! 87: };
! 88:
! 89: #define nif_laddr l.nif_laddr
! 90: #define nif_laddr6 l.nif_laddr6
! 91: #define nif_faddr f.nif_faddr
! 92: #define nif_faddr6 f.nif_faddr6
1.1 deraadt 93:
1.21 millert 94: static void enter(struct inpcb *, struct socket *, int, char *);
95: static const char *inetname(struct in_addr);
1.31 ! canacar 96: static void inetprint(struct in_addr *, int, char *, field_def *);
1.21 millert 97: static const char *inet6name(struct in6_addr *);
1.31 ! canacar 98: static void inet6print(struct in6_addr *, int, char *, field_def *);
! 99: static void shownetstat(struct netinfo *p);
! 100:
! 101: void print_ns(void);
! 102: int read_ns(void);
! 103: int select_ns(void);
! 104: int ns_keyboard_callback(int);
1.1 deraadt 105:
106: #define streq(a,b) (strcmp(a,b)==0)
107:
1.31 ! canacar 108: static int aflag = 0;
! 109:
! 110: static struct nlist namelist[] = {
! 111: #define X_TCBTABLE 0 /* no sysctl */
! 112: { "_tcbtable" },
! 113: #define X_UDBTABLE 1 /* no sysctl */
! 114: { "_udbtable" },
! 115: { "" },
! 116: };
! 117: #define ADD_ALLOC 1000
! 118:
! 119:
! 120: int protos;
! 121:
! 122: struct netinfo *netinfos = NULL;
! 123: size_t num_ns = 0;
! 124: static size_t num_alloc = 0;
! 125:
! 126:
! 127: field_def fields_ns[] = {
! 128: {"LOCAL ADDRESS", 20, 45, 1, FLD_ALIGN_LEFT, -1, 0, 0, 0},
! 129: {"FOREIGN ADDRESS", 20, 45, 1, FLD_ALIGN_LEFT, -1, 0, 0, 0},
! 130: {"PROTO", 4, 9, 1, FLD_ALIGN_LEFT, -1, 0, 0, 0},
! 131: {"RECV-Q", 5, 8, 1, FLD_ALIGN_RIGHT, -1, 0, 0, 0},
! 132: {"SEND-Q", 5, 8, 1, FLD_ALIGN_RIGHT, -1, 0, 0, 0},
! 133: {"STATE", 5, 11, 6, FLD_ALIGN_LEFT, -1, 0, 0, 0},
! 134: };
! 135:
! 136: #define FIELD_ADDR(x) (&fields_ns[x])
! 137:
! 138: #define FLD_NS_LOCAL FIELD_ADDR(0)
! 139: #define FLD_NS_FOREIGN FIELD_ADDR(1)
! 140: #define FLD_NS_PROTO FIELD_ADDR(2)
! 141: #define FLD_NS_RECV_Q FIELD_ADDR(3)
! 142: #define FLD_NS_SEND_Q FIELD_ADDR(4)
! 143: #define FLD_NS_STATE FIELD_ADDR(5)
! 144:
! 145: /* Define views */
! 146: field_def *view_ns_0[] = {
! 147: FLD_NS_LOCAL, FLD_NS_FOREIGN, FLD_NS_PROTO,
! 148: FLD_NS_RECV_Q, FLD_NS_SEND_Q, FLD_NS_STATE, NULL
! 149: };
! 150:
! 151: /* Define view managers */
! 152: struct view_manager netstat_mgr = {
! 153: "Netstat", select_ns, read_ns, NULL, print_header,
! 154: print_ns, ns_keyboard_callback, NULL, NULL
! 155: };
1.1 deraadt 156:
1.31 ! canacar 157: field_view views_ns[] = {
! 158: {view_ns_0, "netstat", '0', &netstat_mgr},
! 159: {NULL, NULL, 0, NULL}
1.1 deraadt 160: };
161:
162:
163:
1.31 ! canacar 164:
! 165: struct netinfo *
! 166: next_ns(void)
! 167: {
! 168: if (num_alloc <= num_ns) {
! 169: struct netinfo *ni;
! 170: size_t a = num_alloc + ADD_ALLOC;
! 171: if (a < num_alloc)
! 172: return NULL;
! 173: ni = realloc(netinfos, a * sizeof(*ni));
! 174: if (ni == NULL)
! 175: return NULL;
! 176: netinfos = ni;
! 177: num_alloc = a;
! 178: }
! 179:
! 180: return &netinfos[num_ns++];
! 181: }
! 182:
! 183: static void
! 184: enter(struct inpcb *inp, struct socket *so, int state, char *proto)
1.1 deraadt 185: {
1.17 mpech 186: struct netinfo *p;
1.1 deraadt 187:
1.31 ! canacar 188: p = next_ns();
! 189: if (p == NULL) {
! 190: error("Out of Memory!");
! 191: return;
1.1 deraadt 192: }
1.31 ! canacar 193:
! 194: p->nif_lport = inp->inp_lport;
! 195: p->nif_fport = inp->inp_fport;
! 196: p->nif_proto = proto;
! 197:
! 198: if (inp->inp_flags & INP_IPV6) {
! 199: p->nif_laddr6 = inp->inp_laddr6;
! 200: p->nif_faddr6 = inp->inp_faddr6;
! 201: p->nif_family = AF_INET6;
! 202: } else {
! 203: p->nif_laddr = inp->inp_laddr;
! 204: p->nif_faddr = inp->inp_faddr;
! 205: p->nif_family = AF_INET;
! 206: }
! 207:
! 208: p->nif_rcvcc = so->so_rcv.sb_cc;
! 209: p->nif_sndcc = so->so_snd.sb_cc;
! 210: p->nif_state = state;
1.1 deraadt 211: }
212:
1.31 ! canacar 213:
! 214: /* netstat callback functions */
1.1 deraadt 215:
216: int
1.31 ! canacar 217: select_ns(void)
1.1 deraadt 218: {
1.31 ! canacar 219: static int init = 0;
! 220: if (kd == NULL) {
! 221: num_disp = 1;
! 222: return (0);
! 223: }
1.14 ericj 224:
1.31 ! canacar 225: if (!init) {
! 226: sethostent(1);
! 227: setnetent(1);
! 228: init = 1;
1.1 deraadt 229: }
1.31 ! canacar 230:
! 231: num_disp = num_ns;
! 232: return (0);
1.1 deraadt 233: }
234:
1.31 ! canacar 235: int
! 236: read_ns(void)
1.1 deraadt 237: {
238: struct inpcbtable pcbtable;
1.17 mpech 239: struct inpcb *head, *prev, *next;
1.1 deraadt 240: struct inpcb inpcb;
241: struct socket sockb;
242: struct tcpcb tcpcb;
243: void *off;
244: int istcp;
245:
1.31 ! canacar 246: if (kd == NULL) {
! 247: return (0);
! 248: }
! 249:
! 250: num_ns = 0;
! 251:
1.1 deraadt 252: if (namelist[X_TCBTABLE].n_value == 0)
1.31 ! canacar 253: return 0;
! 254:
! 255: if (protos & TCP) {
1.20 deraadt 256: off = NPTR(X_TCBTABLE);
1.1 deraadt 257: istcp = 1;
1.31 ! canacar 258: } else if (protos & UDP) {
1.20 deraadt 259: off = NPTR(X_UDBTABLE);
1.1 deraadt 260: istcp = 0;
1.20 deraadt 261: } else {
1.1 deraadt 262: error("No protocols to display");
1.31 ! canacar 263: return 0;
1.1 deraadt 264: }
1.31 ! canacar 265:
1.1 deraadt 266: again:
267: KREAD(off, &pcbtable, sizeof (struct inpcbtable));
1.31 ! canacar 268:
1.1 deraadt 269: prev = head = (struct inpcb *)&((struct inpcbtable *)off)->inpt_queue;
1.28 otto 270: next = CIRCLEQ_FIRST(&pcbtable.inpt_queue);
1.31 ! canacar 271:
1.1 deraadt 272: while (next != head) {
273: KREAD(next, &inpcb, sizeof (inpcb));
1.28 otto 274: if (CIRCLEQ_PREV(&inpcb, inp_queue) != prev) {
1.1 deraadt 275: error("Kernel state in transition");
1.31 ! canacar 276: return 0;
1.1 deraadt 277: }
278: prev = next;
1.28 otto 279: next = CIRCLEQ_NEXT(&inpcb, inp_queue);
1.12 itojun 280:
281: if (!aflag) {
1.20 deraadt 282: if (!(inpcb.inp_flags & INP_IPV6) &&
283: inet_lnaof(inpcb.inp_laddr) == INADDR_ANY)
1.12 itojun 284: continue;
1.20 deraadt 285: if ((inpcb.inp_flags & INP_IPV6) &&
286: IN6_IS_ADDR_UNSPECIFIED(&inpcb.inp_laddr6))
1.12 itojun 287: continue;
288: }
1.1 deraadt 289: KREAD(inpcb.inp_socket, &sockb, sizeof (sockb));
290: if (istcp) {
291: KREAD(inpcb.inp_ppcb, &tcpcb, sizeof (tcpcb));
292: enter(&inpcb, &sockb, tcpcb.t_state, "tcp");
293: } else
294: enter(&inpcb, &sockb, 0, "udp");
295: }
1.31 ! canacar 296: if (istcp && (protos & UDP)) {
1.1 deraadt 297: istcp = 0;
298: off = NPTR(X_UDBTABLE);
299: goto again;
300: }
1.31 ! canacar 301:
! 302: num_disp = num_ns;
! 303: return 0;
1.1 deraadt 304: }
305:
1.31 ! canacar 306: void
! 307: print_ns(void)
1.1 deraadt 308: {
1.31 ! canacar 309: int n, count = 0;
1.1 deraadt 310:
1.31 ! canacar 311: if (kd == NULL) {
! 312: print_fld_str(FLD_NS_LOCAL, "Failed to initialize KVM!");
! 313: print_fld_str(FLD_NS_FOREIGN, "Failed to initialize KVM!");
! 314: end_line();
! 315: return;
1.1 deraadt 316: }
1.31 ! canacar 317:
! 318: for (n = dispstart; n < num_disp; n++) {
! 319: shownetstat(netinfos + n);
! 320: count++;
! 321: if (maxprint > 0 && count >= maxprint)
! 322: break;
1.11 angelos 323: }
1.1 deraadt 324: }
325:
326:
1.31 ! canacar 327: int
! 328: initnetstat(void)
! 329: {
! 330: field_view *v;
! 331: int ret;
1.1 deraadt 332:
1.31 ! canacar 333: if (kd) {
! 334: if ((ret = kvm_nlist(kd, namelist)) == -1)
! 335: errx(1, "%s", kvm_geterr(kd));
! 336: else if (ret)
! 337: nlisterr(namelist);
! 338:
! 339: if (namelist[X_TCBTABLE].n_value == 0) {
! 340: error("No symbols in namelist");
! 341: return(0);
! 342: }
! 343: }
! 344: protos = TCP|UDP;
! 345:
! 346: for (v = views_ns; v->name != NULL; v++)
! 347: add_view(v);
! 348:
! 349: return(1);
1.1 deraadt 350: }
351:
1.31 ! canacar 352: static void
! 353: shownetstat(struct netinfo *p)
1.1 deraadt 354: {
1.31 ! canacar 355: switch (p->nif_family) {
! 356: case AF_INET:
! 357: inetprint(&p->nif_laddr, p->nif_lport,
! 358: p->nif_proto, FLD_NS_LOCAL);
! 359: inetprint(&p->nif_faddr, p->nif_fport,
! 360: p->nif_proto, FLD_NS_FOREIGN);
! 361: break;
! 362: case AF_INET6:
! 363: inet6print(&p->nif_laddr6, p->nif_lport,
! 364: p->nif_proto, FLD_NS_LOCAL);
! 365: inet6print(&p->nif_faddr6, p->nif_fport,
! 366: p->nif_proto, FLD_NS_FOREIGN);
! 367: break;
! 368: }
! 369:
! 370: tb_start();
! 371: tbprintf("%s", p->nif_proto);
! 372: if (p->nif_family == AF_INET6)
! 373: tbprintf("6");
! 374:
! 375: print_fld_tb(FLD_NS_PROTO);
! 376:
! 377: print_fld_size(FLD_NS_RECV_Q, p->nif_rcvcc);
! 378: print_fld_size(FLD_NS_SEND_Q, p->nif_sndcc);
! 379:
! 380: if (streq(p->nif_proto, "tcp")) {
! 381: if (p->nif_state < 0 || p->nif_state >= TCP_NSTATES)
! 382: print_fld_uint(FLD_NS_STATE, p->nif_state);
! 383: else
! 384: print_fld_str(FLD_NS_STATE, tcpstates[p->nif_state]);
1.1 deraadt 385: }
1.31 ! canacar 386: end_line();
1.1 deraadt 387: }
388:
389: /*
390: * Pretty print an Internet address (net address + port).
391: * If the nflag was specified, use numbers instead of names.
392: */
393: static void
1.31 ! canacar 394: inetprint(struct in_addr *in, int port, char *proto, field_def *fld)
1.1 deraadt 395: {
396: struct servent *sp = 0;
397:
1.31 ! canacar 398: tb_start();
! 399: tbprintf("%s", inetname(*in));
! 400:
1.1 deraadt 401: if (!nflag && port)
402: sp = getservbyport(port, proto);
403: if (sp || port == 0)
1.31 ! canacar 404: tbprintf(":%s", sp ? sp->s_name : "*");
1.1 deraadt 405: else
1.31 ! canacar 406: tbprintf(":%d", ntohs((u_short)port));
! 407:
! 408: print_fld_tb(fld);
1.1 deraadt 409: }
410:
1.12 itojun 411: static void
1.31 ! canacar 412: inet6print(struct in6_addr *in6, int port, char *proto, field_def *fld)
1.12 itojun 413: {
414: struct servent *sp = 0;
415:
1.31 ! canacar 416: tb_start();
! 417:
! 418: tbprintf("%s", inet6name(in6));
1.12 itojun 419: if (!nflag && port)
420: sp = getservbyport(port, proto);
421: if (sp || port == 0)
1.31 ! canacar 422: tbprintf(":%s", sp ? sp->s_name : "*");
1.12 itojun 423: else
1.31 ! canacar 424: tbprintf(":%d", ntohs((u_short)port));
1.12 itojun 425:
1.31 ! canacar 426: print_fld_tb(fld);
1.1 deraadt 427: }
1.12 itojun 428:
429: static const char *
1.22 deraadt 430: inet6name(struct in6_addr *in6)
1.12 itojun 431: {
432: static char line[NI_MAXHOST];
433: struct sockaddr_in6 sin6;
434: int flags;
435:
1.25 itojun 436: flags = nflag ? NI_NUMERICHOST : 0;
1.12 itojun 437: if (IN6_IS_ADDR_UNSPECIFIED(in6))
438: return "*";
439: memset(&sin6, 0, sizeof(sin6));
440: sin6.sin6_family = AF_INET6;
441: sin6.sin6_len = sizeof(struct sockaddr_in6);
442: sin6.sin6_addr = *in6;
443: if (getnameinfo((struct sockaddr *)&sin6, sin6.sin6_len,
1.20 deraadt 444: line, sizeof(line), NULL, 0, flags) == 0)
1.12 itojun 445: return line;
446: return "?";
447: }
1.1 deraadt 448:
1.31 ! canacar 449: static const char *
! 450: inetname(struct in_addr in)
! 451: {
! 452: static char line[NI_MAXHOST];
! 453: struct sockaddr_in sin;
! 454: int flags, e;
! 455:
! 456: flags = nflag ? NI_NUMERICHOST : 0;
! 457: if (in.s_addr == INADDR_ANY)
! 458: return "*";
! 459:
! 460: memset(&sin, 0, sizeof(sin));
! 461: sin.sin_family = AF_INET;
! 462: sin.sin_len = sizeof(struct sockaddr_in);
! 463: sin.sin_addr = in;
! 464:
! 465: e = getnameinfo((struct sockaddr *)&sin, sin.sin_len,
! 466: line, sizeof(line), NULL, 0, flags);
! 467:
! 468: if (e == 0)
! 469: return line;
! 470:
! 471: error("Lookup: %s", gai_strerror(e));
! 472:
! 473: return "?";
! 474: }
! 475:
! 476: int
! 477: kvm_ckread(void *a, void *b, size_t l)
! 478: {
! 479: if (kvm_read(kd, (u_long)a, b, l) != l) {
! 480: if (verbose)
! 481: error("error reading kmem at %x\n", a);
! 482: return (0);
! 483: } else
! 484: return (1);
! 485: }
! 486:
! 487:
1.1 deraadt 488: int
1.31 ! canacar 489: ns_keyboard_callback(int ch)
1.1 deraadt 490: {
1.31 ! canacar 491: switch (ch) {
! 492: case 'n':
! 493: nflag = !nflag;
! 494: gotsig_alarm = 1;
! 495: break;
! 496: case 't':
! 497: protos ^= TCP;
! 498: gotsig_alarm = 1;
! 499: break;
! 500: case 'u':
! 501: protos ^= UDP;
! 502: gotsig_alarm = 1;
! 503: break;
! 504: default:
! 505: return keyboard_callback(ch);
! 506: };
1.1 deraadt 507:
1.31 ! canacar 508: return 1;
1.1 deraadt 509: }
1.31 ! canacar 510: