1 /* $OpenBSD: netstat.c,v 1.47 2024/09/20 02:00:46 jsg Exp $ */
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.
16 * 3. Neither the name of the University nor the names of its contributors
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
37 #include <kvm.h>
38 #include <sys/types.h>
39 #include <sys/sysctl.h>
40 #include <sys/socket.h>
41 #define _KERNEL
42 #include <sys/file.h>
43 #undef _KERNEL
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>
50 #include <arpa/inet.h>
51
52 #include <netdb.h>
53 #include <signal.h>
54 #include <stdlib.h>
55 #include <string.h>
56 #include <err.h>
57 #include <nlist.h>
58 #include <paths.h>
59 #include "systat.h"
60 #include "engine.h"
61
62 #define TCP 0x1
63 #define UDP 0x2
64 #define OTHER 0x4
65
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;
81 short nif_proto; /* protocol */
82 short nif_ipproto;
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
89
90 static void enter(struct kinfo_file *);
91 static int kf_comp(const void *, const void *);
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);
100
101 #define streq(a,b) (strcmp(a,b)==0)
102
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
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)
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 };
141
142 field_view views_ns[] = {
143 {view_ns_0, "netstat", '0', &netstat_mgr},
144 {NULL, NULL, 0, NULL}
145 };
146
147
148
149
150 struct netinfo *
next_ns(void)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;
158 ni = reallocarray(netinfos, a, sizeof(*ni));
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
enter(struct kinfo_file * kf)169 enter(struct kinfo_file *kf)
170 {
171 #define s6_addr32 __u6_addr.__u6_addr32
172 struct netinfo *p;
173
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 */
213 p = next_ns();
214 if (p == NULL) {
215 error("Out of Memory!");
216 return;
217 }
218
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:
231 p->nif_family = AF_INET6;
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;
241 }
242
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
247 }
248
249
250 /* netstat callback functions */
251
252 int
select_ns(void)253 select_ns(void)
254 {
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
kf_comp(const void * a,const void * b)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 }
298 }
299 return (0);
300 }
301
302
303 int
read_ns(void)304 read_ns(void)
305 {
306 struct kinfo_file *kf;
307 int i, fcnt;
308
309 if (kd == NULL) {
310 error("Failed to initialize KVM!");
311 return (0);
312 }
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);
322
323 num_ns = 0;
324
325 for (i = 0; i < fcnt; i++)
326 enter(&kf[i]);
327
328 num_disp = num_ns;
329 return 0;
330 }
331
332 void
print_ns(void)333 print_ns(void)
334 {
335 int n, count = 0;
336
337 for (n = dispstart; n < num_disp; n++) {
338 shownetstat(netinfos + n);
339 count++;
340 if (maxprint > 0 && count >= maxprint)
341 break;
342 }
343 }
344
345
346 int
initnetstat(void)347 initnetstat(void)
348 {
349 field_view *v;
350
351 protos = TCP|UDP|OTHER;
352 for (v = views_ns; v->name != NULL; v++)
353 add_view(v);
354
355 return(1);
356 }
357
358 static void
shownetstat(struct netinfo * p)359 shownetstat(struct netinfo *p)
360 {
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
372 switch (p->nif_family) {
373 case AF_INET:
374 inetprint(&p->nif_laddr, p->nif_lport,
375 proto, FLD_NS_LOCAL);
376 inetprint(&p->nif_faddr, p->nif_fport,
377 proto, FLD_NS_FOREIGN);
378 break;
379 case AF_INET6:
380 inet6print(&p->nif_laddr6, p->nif_lport,
381 proto, FLD_NS_LOCAL);
382 inet6print(&p->nif_faddr6, p->nif_fport,
383 proto, FLD_NS_FOREIGN);
384 break;
385 }
386
387 tb_start();
388 switch (p->nif_proto) {
389 case IPPROTO_TCP:
390 case IPPROTO_UDP:
391 tbprintf("%s", 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 }
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
410 if (p->nif_proto == IPPROTO_TCP) {
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]);
415 }
416 end_line();
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
inetprint(struct in_addr * in,int port,char * proto,field_def * fld)424 inetprint(struct in_addr *in, int port, char *proto, field_def *fld)
425 {
426 struct servent *sp = 0;
427
428 tb_start();
429 tbprintf("%s", inetname(*in));
430
431 if (!nflag && port)
432 sp = getservbyport(port, proto);
433 if (sp || port == 0)
434 tbprintf(":%s", sp ? sp->s_name : "*");
435 else
436 tbprintf(":%d", ntohs((u_short)port));
437
438 print_fld_tb(fld);
439 }
440
441 static void
inet6print(struct in6_addr * in6,int port,char * proto,field_def * fld)442 inet6print(struct in6_addr *in6, int port, char *proto, field_def *fld)
443 {
444 struct servent *sp = 0;
445
446 tb_start();
447
448 tbprintf("%s", inet6name(in6));
449 if (!nflag && port)
450 sp = getservbyport(port, proto);
451 if (sp || port == 0)
452 tbprintf(":%s", sp ? sp->s_name : "*");
453 else
454 tbprintf(":%d", ntohs((u_short)port));
455
456 print_fld_tb(fld);
457 }
458
459 int
ns_keyboard_callback(int ch)460 ns_keyboard_callback(int ch)
461 {
462 switch (ch) {
463 case 'a':
464 aflag = !aflag;
465 gotsig_alarm = 1;
466 break;
467 case 'n':
468 nflag = !nflag;
469 gotsig_alarm = 1;
470 break;
471 case 'o':
472 protos ^= OTHER;
473 gotsig_alarm = 1;
474 break;
475 case 'r':
476 aflag = 0;
477 nflag = 1;
478 protos = TCP|UDP;
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 }
492
493 return 1;
494 }
495
496