Annotation of src/usr.bin/systat/netstat.c, Revision 1.45
1.45 ! claudio 1: /* $OpenBSD: netstat.c,v 1.44 2015/01/20 18:26:57 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: */
1.43 deraadt 36:
1.45 ! claudio 37: #include <kvm.h>
! 38: #include <sys/types.h>
! 39: #include <sys/sysctl.h>
1.1 deraadt 40: #include <sys/socket.h>
1.45 ! claudio 41: #define _KERNEL
! 42: #include <sys/file.h>
! 43: #undef _KERNEL
1.1 deraadt 44:
45: #include <netinet/in.h>
46: #include <netinet/tcp.h>
47: #include <netinet/tcp_seq.h>
48: #define TCPSTATES
49: #include <netinet/tcp_fsm.h>
1.7 millert 50: #include <arpa/inet.h>
1.1 deraadt 51:
52: #include <netdb.h>
1.45 ! claudio 53: #include <signal.h>
1.1 deraadt 54: #include <stdlib.h>
55: #include <string.h>
1.16 pvalchev 56: #include <err.h>
1.1 deraadt 57: #include <nlist.h>
58: #include <paths.h>
59: #include "systat.h"
1.31 canacar 60: #include "engine.h"
61:
1.45 ! claudio 62: #define TCP 0x1
! 63: #define UDP 0x2
! 64: #define OTHER 0x4
! 65:
1.31 canacar 66: struct netinfo {
67: union {
68: struct in_addr nif_laddr; /* local address */
69: struct in6_addr nif_laddr6; /* local address */
70: } l;
71: union {
72: struct in_addr nif_faddr; /* foreign address */
73: struct in6_addr nif_faddr6; /* foreign address */
74: } f;
75: long nif_rcvcc; /* rcv buffer character count */
76: long nif_sndcc; /* snd buffer character count */
77: short nif_lport; /* local port */
78: short nif_fport; /* foreign port */
79: short nif_state; /* tcp state */
80: short nif_family;
1.45 ! claudio 81: short nif_proto; /* protocol */
! 82: short nif_ipproto;
1.31 canacar 83: };
84:
85: #define nif_laddr l.nif_laddr
86: #define nif_laddr6 l.nif_laddr6
87: #define nif_faddr f.nif_faddr
88: #define nif_faddr6 f.nif_faddr6
1.1 deraadt 89:
1.45 ! claudio 90: static void enter(struct kinfo_file *);
! 91: static int kf_comp(const void *, const void *);
1.31 canacar 92: static void inetprint(struct in_addr *, int, char *, field_def *);
93: static void inet6print(struct in6_addr *, int, char *, field_def *);
94: static void shownetstat(struct netinfo *p);
95:
96: void print_ns(void);
97: int read_ns(void);
98: int select_ns(void);
99: int ns_keyboard_callback(int);
1.1 deraadt 100:
101: #define streq(a,b) (strcmp(a,b)==0)
102:
1.31 canacar 103: static int aflag = 0;
104:
105: #define ADD_ALLOC 1000
106:
107: int protos;
108:
109: struct netinfo *netinfos = NULL;
110: size_t num_ns = 0;
111: static size_t num_alloc = 0;
112:
113:
114: field_def fields_ns[] = {
115: {"LOCAL ADDRESS", 20, 45, 1, FLD_ALIGN_LEFT, -1, 0, 0, 0},
116: {"FOREIGN ADDRESS", 20, 45, 1, FLD_ALIGN_LEFT, -1, 0, 0, 0},
117: {"PROTO", 4, 9, 1, FLD_ALIGN_LEFT, -1, 0, 0, 0},
118: {"RECV-Q", 5, 8, 1, FLD_ALIGN_RIGHT, -1, 0, 0, 0},
119: {"SEND-Q", 5, 8, 1, FLD_ALIGN_RIGHT, -1, 0, 0, 0},
120: {"STATE", 5, 11, 6, FLD_ALIGN_LEFT, -1, 0, 0, 0},
121: };
122:
1.34 jasper 123: #define FLD_NS_LOCAL FIELD_ADDR(fields_ns,0)
124: #define FLD_NS_FOREIGN FIELD_ADDR(fields_ns,1)
125: #define FLD_NS_PROTO FIELD_ADDR(fields_ns,2)
126: #define FLD_NS_RECV_Q FIELD_ADDR(fields_ns,3)
127: #define FLD_NS_SEND_Q FIELD_ADDR(fields_ns,4)
128: #define FLD_NS_STATE FIELD_ADDR(fields_ns,5)
1.31 canacar 129:
130: /* Define views */
131: field_def *view_ns_0[] = {
132: FLD_NS_LOCAL, FLD_NS_FOREIGN, FLD_NS_PROTO,
133: FLD_NS_RECV_Q, FLD_NS_SEND_Q, FLD_NS_STATE, NULL
134: };
135:
136: /* Define view managers */
137: struct view_manager netstat_mgr = {
138: "Netstat", select_ns, read_ns, NULL, print_header,
139: print_ns, ns_keyboard_callback, NULL, NULL
140: };
1.1 deraadt 141:
1.31 canacar 142: field_view views_ns[] = {
143: {view_ns_0, "netstat", '0', &netstat_mgr},
144: {NULL, NULL, 0, NULL}
1.1 deraadt 145: };
146:
147:
148:
1.31 canacar 149:
150: struct netinfo *
151: next_ns(void)
152: {
153: if (num_alloc <= num_ns) {
154: struct netinfo *ni;
155: size_t a = num_alloc + ADD_ALLOC;
156: if (a < num_alloc)
157: return NULL;
1.41 doug 158: ni = reallocarray(netinfos, a, sizeof(*ni));
1.31 canacar 159: if (ni == NULL)
160: return NULL;
161: netinfos = ni;
162: num_alloc = a;
163: }
164:
165: return &netinfos[num_ns++];
166: }
167:
168: static void
1.45 ! claudio 169: enter(struct kinfo_file *kf)
1.1 deraadt 170: {
1.45 ! claudio 171: #define s6_addr32 __u6_addr.__u6_addr32
1.17 mpech 172: struct netinfo *p;
1.1 deraadt 173:
1.45 ! claudio 174: /* first filter out unwanted sockets */
! 175: if (kf->so_family != AF_INET && kf->so_family != AF_INET6)
! 176: return;
! 177:
! 178: switch (kf->so_protocol) {
! 179: case IPPROTO_TCP:
! 180: if ((protos & TCP) == 0)
! 181: return;
! 182: break;
! 183: case IPPROTO_UDP:
! 184: if ((protos & UDP) == 0)
! 185: return;
! 186: break;
! 187: default:
! 188: if ((protos & OTHER) == 0)
! 189: return;
! 190: break;
! 191: }
! 192:
! 193: if (!aflag) {
! 194: struct in6_addr faddr6;
! 195:
! 196: switch (kf->so_family) {
! 197: case AF_INET:
! 198: if (kf->inp_faddru[0] == INADDR_ANY)
! 199: return;
! 200: break;
! 201: case AF_INET6:
! 202: faddr6.s6_addr32[0] = kf->inp_faddru[0];
! 203: faddr6.s6_addr32[1] = kf->inp_faddru[1];
! 204: faddr6.s6_addr32[2] = kf->inp_faddru[2];
! 205: faddr6.s6_addr32[3] = kf->inp_faddru[3];
! 206: if (IN6_IS_ADDR_UNSPECIFIED(&faddr6))
! 207: return;
! 208: break;
! 209: }
! 210: }
! 211:
! 212: /* finally enter the socket to the table */
1.31 canacar 213: p = next_ns();
214: if (p == NULL) {
215: error("Out of Memory!");
216: return;
1.1 deraadt 217: }
1.31 canacar 218:
1.45 ! claudio 219: p->nif_lport = kf->inp_lport;
! 220: p->nif_fport = kf->inp_fport;
! 221: p->nif_proto = kf->so_protocol;
! 222: p->nif_ipproto = kf->inp_proto;
! 223:
! 224: switch (kf->so_family) {
! 225: case AF_INET:
! 226: p->nif_family = AF_INET;
! 227: p->nif_laddr.s_addr = kf->inp_laddru[0];
! 228: p->nif_faddr.s_addr = kf->inp_faddru[0];
! 229: break;
! 230: case AF_INET6:
1.31 canacar 231: p->nif_family = AF_INET6;
1.45 ! claudio 232: p->nif_laddr6.s6_addr32[0] = kf->inp_laddru[0];
! 233: p->nif_laddr6.s6_addr32[1] = kf->inp_laddru[1];
! 234: p->nif_laddr6.s6_addr32[2] = kf->inp_laddru[2];
! 235: p->nif_laddr6.s6_addr32[3] = kf->inp_laddru[3];
! 236: p->nif_faddr6.s6_addr32[0] = kf->inp_faddru[0];
! 237: p->nif_faddr6.s6_addr32[1] = kf->inp_faddru[1];
! 238: p->nif_faddr6.s6_addr32[2] = kf->inp_faddru[2];
! 239: p->nif_faddr6.s6_addr32[3] = kf->inp_faddru[3];
! 240: break;
1.31 canacar 241: }
242:
1.45 ! claudio 243: p->nif_rcvcc = kf->so_rcv_cc;
! 244: p->nif_sndcc = kf->so_snd_cc;
! 245: p->nif_state = kf->t_state;
! 246: #undef s6_addr32
1.1 deraadt 247: }
248:
1.31 canacar 249:
250: /* netstat callback functions */
1.1 deraadt 251:
252: int
1.31 canacar 253: select_ns(void)
1.1 deraadt 254: {
1.45 ! claudio 255: num_disp = num_ns;
! 256: return (0);
! 257: }
! 258:
! 259: static int type_map[] = { -1, 2, 3, 1, 4, 5 };
! 260:
! 261: static int
! 262: kf_comp(const void *a, const void *b)
! 263: {
! 264: const struct kinfo_file *ka = a, *kb = b;
! 265:
! 266: if (ka->so_family != kb->so_family) {
! 267: /* AF_INET < AF_INET6 < AF_LOCAL */
! 268: if (ka->so_family == AF_INET)
! 269: return (-1);
! 270: if (ka->so_family == AF_LOCAL)
! 271: return (1);
! 272: if (kb->so_family == AF_LOCAL)
! 273: return (-1);
! 274: return (1);
! 275: }
! 276: if (ka->so_family == AF_LOCAL) {
! 277: if (type_map[ka->so_type] < type_map[kb->so_type])
! 278: return (-1);
! 279: if (type_map[ka->so_type] > type_map[kb->so_type])
! 280: return (1);
! 281: } else if (ka->so_family == AF_INET || ka->so_family == AF_INET6) {
! 282: if (ka->so_protocol < kb->so_protocol)
! 283: return (-1);
! 284: if (ka->so_protocol > kb->so_protocol)
! 285: return (1);
! 286: if (ka->so_type == SOCK_DGRAM || ka->so_type == SOCK_STREAM) {
! 287: /* order sockets by remote port desc */
! 288: if (ka->inp_fport > kb->inp_fport)
! 289: return (-1);
! 290: if (ka->inp_fport < kb->inp_fport)
! 291: return (1);
! 292: } else if (ka->so_type == SOCK_RAW) {
! 293: if (ka->inp_proto > kb->inp_proto)
! 294: return (-1);
! 295: if (ka->inp_proto < kb->inp_proto)
! 296: return (1);
! 297: }
1.31 canacar 298: }
299: return (0);
1.1 deraadt 300: }
301:
1.45 ! claudio 302:
1.31 canacar 303: int
304: read_ns(void)
1.1 deraadt 305: {
1.45 ! claudio 306: struct kinfo_file *kf;
! 307: int i, fcnt;
1.1 deraadt 308:
1.31 canacar 309: if (kd == NULL) {
1.45 ! claudio 310: error("Failed to initialize KVM!");
1.31 canacar 311: return (0);
312: }
1.45 ! claudio 313: kf = kvm_getfiles(kd, KERN_FILE_BYFILE, DTYPE_SOCKET,
! 314: sizeof(*kf), &fcnt);
! 315: if (kf == NULL) {
! 316: error("Out of Memory!");
! 317: return (0);
! 318: }
! 319:
! 320: /* sort sockets by AF, proto and type */
! 321: qsort(kf, fcnt, sizeof(*kf), kf_comp);
1.31 canacar 322:
323: num_ns = 0;
324:
1.45 ! claudio 325: for (i = 0; i < fcnt; i++)
! 326: enter(&kf[i]);
1.31 canacar 327:
328: num_disp = num_ns;
329: return 0;
1.1 deraadt 330: }
331:
1.31 canacar 332: void
333: print_ns(void)
1.1 deraadt 334: {
1.31 canacar 335: int n, count = 0;
1.1 deraadt 336:
1.31 canacar 337: for (n = dispstart; n < num_disp; n++) {
338: shownetstat(netinfos + n);
339: count++;
340: if (maxprint > 0 && count >= maxprint)
341: break;
1.11 angelos 342: }
1.1 deraadt 343: }
344:
345:
1.31 canacar 346: int
347: initnetstat(void)
348: {
349: field_view *v;
350:
1.45 ! claudio 351: protos = TCP|UDP|OTHER;
1.31 canacar 352: for (v = views_ns; v->name != NULL; v++)
353: add_view(v);
354:
355: return(1);
1.1 deraadt 356: }
357:
1.31 canacar 358: static void
359: shownetstat(struct netinfo *p)
1.1 deraadt 360: {
1.45 ! claudio 361: char *proto = NULL;
! 362:
! 363: switch (p->nif_proto) {
! 364: case IPPROTO_TCP:
! 365: proto = "tcp";
! 366: break;
! 367: case IPPROTO_UDP:
! 368: proto = "udp";
! 369: break;
! 370: }
! 371:
1.31 canacar 372: switch (p->nif_family) {
373: case AF_INET:
374: inetprint(&p->nif_laddr, p->nif_lport,
1.45 ! claudio 375: proto, FLD_NS_LOCAL);
1.31 canacar 376: inetprint(&p->nif_faddr, p->nif_fport,
1.45 ! claudio 377: proto, FLD_NS_FOREIGN);
1.31 canacar 378: break;
379: case AF_INET6:
380: inet6print(&p->nif_laddr6, p->nif_lport,
1.45 ! claudio 381: proto, FLD_NS_LOCAL);
1.31 canacar 382: inet6print(&p->nif_faddr6, p->nif_fport,
1.45 ! claudio 383: proto, FLD_NS_FOREIGN);
1.31 canacar 384: break;
385: }
386:
387: tb_start();
1.45 ! claudio 388: switch (p->nif_proto) {
! 389: case IPPROTO_TCP:
! 390: case IPPROTO_UDP:
! 391: tbprintf(proto);
! 392: if (p->nif_family == AF_INET6)
! 393: tbprintf("6");
! 394: break;
! 395: case IPPROTO_DIVERT:
! 396: tbprintf("divert");
! 397: if (p->nif_family == AF_INET6)
! 398: tbprintf("6");
! 399: break;
! 400: default:
! 401: tbprintf("%d", p->nif_ipproto);
! 402: break;
! 403: }
1.31 canacar 404:
405: print_fld_tb(FLD_NS_PROTO);
406:
407: print_fld_size(FLD_NS_RECV_Q, p->nif_rcvcc);
408: print_fld_size(FLD_NS_SEND_Q, p->nif_sndcc);
409:
1.45 ! claudio 410: if (p->nif_proto == IPPROTO_TCP) {
1.31 canacar 411: if (p->nif_state < 0 || p->nif_state >= TCP_NSTATES)
412: print_fld_uint(FLD_NS_STATE, p->nif_state);
413: else
414: print_fld_str(FLD_NS_STATE, tcpstates[p->nif_state]);
1.1 deraadt 415: }
1.31 canacar 416: end_line();
1.1 deraadt 417: }
418:
419: /*
420: * Pretty print an Internet address (net address + port).
421: * If the nflag was specified, use numbers instead of names.
422: */
423: static void
1.31 canacar 424: inetprint(struct in_addr *in, int port, char *proto, field_def *fld)
1.1 deraadt 425: {
426: struct servent *sp = 0;
427:
1.31 canacar 428: tb_start();
429: tbprintf("%s", inetname(*in));
430:
1.1 deraadt 431: if (!nflag && port)
432: sp = getservbyport(port, proto);
433: if (sp || port == 0)
1.31 canacar 434: tbprintf(":%s", sp ? sp->s_name : "*");
1.1 deraadt 435: else
1.31 canacar 436: tbprintf(":%d", ntohs((u_short)port));
437:
438: print_fld_tb(fld);
1.1 deraadt 439: }
440:
1.12 itojun 441: static void
1.31 canacar 442: inet6print(struct in6_addr *in6, int port, char *proto, field_def *fld)
1.12 itojun 443: {
444: struct servent *sp = 0;
445:
1.31 canacar 446: tb_start();
447:
448: tbprintf("%s", inet6name(in6));
1.12 itojun 449: if (!nflag && port)
450: sp = getservbyport(port, proto);
451: if (sp || port == 0)
1.31 canacar 452: tbprintf(":%s", sp ? sp->s_name : "*");
1.12 itojun 453: else
1.31 canacar 454: tbprintf(":%d", ntohs((u_short)port));
1.12 itojun 455:
1.31 canacar 456: print_fld_tb(fld);
457: }
458:
459: int
460: ns_keyboard_callback(int ch)
1.1 deraadt 461: {
1.31 canacar 462: switch (ch) {
1.35 okan 463: case 'a':
464: aflag = !aflag;
465: gotsig_alarm = 1;
466: break;
1.31 canacar 467: case 'n':
468: nflag = !nflag;
1.45 ! claudio 469: gotsig_alarm = 1;
! 470: break;
! 471: case 'o':
! 472: protos ^= OTHER;
1.35 okan 473: gotsig_alarm = 1;
474: break;
475: case 'r':
476: aflag = 0;
477: nflag = 1;
478: protos = TCP|UDP;
1.31 canacar 479: gotsig_alarm = 1;
480: break;
481: case 't':
482: protos ^= TCP;
483: gotsig_alarm = 1;
484: break;
485: case 'u':
486: protos ^= UDP;
487: gotsig_alarm = 1;
488: break;
489: default:
490: return keyboard_callback(ch);
491: };
1.1 deraadt 492:
1.31 canacar 493: return 1;
1.1 deraadt 494: }
1.31 canacar 495: