netstat.c revision 1.22 1 /* $NetBSD: netstat.c,v 1.22 2003/05/17 21:03:21 itojun Exp $ */
2
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. All advertising materials mentioning features or use of this software
16 * must display the following acknowledgement:
17 * This product includes software developed by the University of
18 * California, Berkeley and its contributors.
19 * 4. Neither the name of the University nor the names of its contributors
20 * may be used to endorse or promote products derived from this software
21 * without specific prior written permission.
22 *
23 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33 * SUCH DAMAGE.
34 */
35
36 #include <sys/cdefs.h>
37 #ifndef lint
38 #if 0
39 static char sccsid[] = "@(#)netstat.c 8.1 (Berkeley) 6/6/93";
40 #endif
41 __RCSID("$NetBSD: netstat.c,v 1.22 2003/05/17 21:03:21 itojun Exp $");
42 #endif /* not lint */
43
44 /*
45 * netstat
46 */
47 #include <sys/param.h>
48 #include <sys/socketvar.h>
49 #include <sys/mbuf.h>
50 #include <sys/protosw.h>
51
52 #include <netinet/in.h>
53
54 #include <arpa/inet.h>
55 #include <net/route.h>
56
57 #include <netinet/in_systm.h>
58 #include <netinet/ip.h>
59 #include <netinet/in_pcb.h>
60 #include <netinet/ip_icmp.h>
61 #include <netinet/icmp_var.h>
62 #include <netinet/ip_var.h>
63 #ifdef INET6
64 #include <netinet/ip6.h>
65 #include <netinet6/in6_pcb.h>
66 #endif
67 #include <netinet/tcp.h>
68 #include <netinet/tcpip.h>
69 #include <netinet/tcp_seq.h>
70 #define TCPSTATES
71 #include <netinet/tcp_fsm.h>
72 #include <netinet/tcp_timer.h>
73 #include <netinet/tcp_var.h>
74 #include <netinet/tcp_debug.h>
75 #include <netinet/udp.h>
76 #include <netinet/udp_var.h>
77
78 #include <netdb.h>
79 #include <stdlib.h>
80 #include <string.h>
81
82 #include "systat.h"
83 #include "extern.h"
84
85 static void fetchnetstat4(void *, int);
86 static void enter(struct inpcb *, struct socket *, int, char *);
87 static const char *inetname(struct in_addr);
88 static void inetprint(struct in_addr *, int, char *);
89 #ifdef INET6
90 static void fetchnetstat6(void *, int);
91 static void enter6(struct in6pcb *, struct socket *, int, char *);
92 static const char *inet6name(struct in6_addr *);
93 static void inet6print(struct in6_addr *, int, char *);
94 #endif
95
96 #define streq(a,b) (strcmp(a,b)==0)
97
98 struct netinfo {
99 struct netinfo *ni_forw, *ni_prev;
100 int ni_family;
101 short ni_line; /* line on screen */
102 short ni_seen; /* 0 when not present in list */
103 short ni_flags;
104 #define NIF_LACHG 0x1 /* local address changed */
105 #define NIF_FACHG 0x2 /* foreign address changed */
106 short ni_state; /* tcp state */
107 char *ni_proto; /* protocol */
108 struct in_addr ni_laddr; /* local address */
109 #ifdef INET6
110 struct in6_addr ni_laddr6; /* local address */
111 #endif
112 long ni_lport; /* local port */
113 struct in_addr ni_faddr; /* foreign address */
114 #ifdef INET6
115 struct in6_addr ni_faddr6; /* foreign address */
116 #endif
117 long ni_fport; /* foreign port */
118 long ni_rcvcc; /* rcv buffer character count */
119 long ni_sndcc; /* snd buffer character count */
120 };
121
122 static struct {
123 struct netinfo *ni_forw, *ni_prev;
124 } netcb;
125
126 static int aflag = 0;
127 int nflag = 0;
128 static int lastrow = 1;
129
130 WINDOW *
131 opennetstat(void)
132 {
133
134 sethostent(1);
135 setnetent(1);
136 return (subwin(stdscr, -1, 0, 5, 0));
137 }
138
139 void
140 closenetstat(WINDOW *w)
141 {
142 struct netinfo *p;
143
144 endhostent();
145 endnetent();
146 p = (struct netinfo *)netcb.ni_forw;
147 while (p != (struct netinfo *)&netcb) {
148 if (p->ni_line != -1)
149 lastrow--;
150 p->ni_line = -1;
151 p = p->ni_forw;
152 }
153 if (w != NULL) {
154 wclear(w);
155 wrefresh(w);
156 delwin(w);
157 }
158 }
159
160 static struct nlist namelist[] = {
161 #define X_TCBTABLE 0
162 { "_tcbtable" },
163 #define X_UDBTABLE 1
164 { "_udbtable" },
165 #ifdef INET6
166 #define X_TCB6 2
167 { "_tcb6" },
168 #define X_UDB6 3
169 { "_udb6" },
170 #endif
171 { "" },
172 };
173
174 int
175 initnetstat(void)
176 {
177 int n;
178
179 n = kvm_nlist(kd, namelist);
180 if (n < 0) {
181 nlisterr(namelist);
182 return(0);
183 } else if (n == sizeof(namelist) / sizeof(namelist[0]) - 1) {
184 error("No symbols in namelist");
185 return(0);
186 }
187
188 netcb.ni_forw = netcb.ni_prev = (struct netinfo *)&netcb;
189 protos = TCP|UDP;
190 return(1);
191 }
192
193 void
194 fetchnetstat(void)
195 {
196 struct netinfo *p;
197
198 if (namelist[X_TCBTABLE].n_value == 0)
199 return;
200 for (p = netcb.ni_forw; p != (struct netinfo *)&netcb; p = p->ni_forw)
201 p->ni_seen = 0;
202
203 if ((protos & (TCP | UDP)) == 0) {
204 error("No protocols to display");
205 return;
206 }
207 if ((protos & TCP) && namelist[X_TCBTABLE].n_type)
208 fetchnetstat4(NPTR(X_TCBTABLE), 1);
209 if ((protos & UDP) && namelist[X_UDBTABLE].n_type)
210 fetchnetstat4(NPTR(X_UDBTABLE), 0);
211 #ifdef INET6
212 if ((protos & TCP) && namelist[X_TCB6].n_type)
213 fetchnetstat6(NPTR(X_TCB6), 1);
214 if ((protos & UDP) && namelist[X_UDB6].n_type)
215 fetchnetstat6(NPTR(X_UDB6), 0);
216 #endif
217 }
218
219 static void
220 fetchnetstat4(void *off, int istcp)
221 {
222 struct inpcbtable pcbtable;
223 struct inpcb *head, *prev, *next;
224 struct netinfo *p;
225 struct inpcb inpcb;
226 struct socket sockb;
227 struct tcpcb tcpcb;
228
229 KREAD(off, &pcbtable, sizeof pcbtable);
230 prev = head = (struct inpcb *)&((struct inpcbtable *)off)->inpt_queue;
231 next = pcbtable.inpt_queue.cqh_first;
232 while (next != head) {
233 KREAD(next, &inpcb, sizeof (inpcb));
234 if (inpcb.inp_queue.cqe_prev != prev) {
235 printf("prev = %p, head = %p, next = %p, inpcb...prev = %p\n", prev, head, next, inpcb.inp_queue.cqe_prev);
236 p = netcb.ni_forw;
237 for (; p != (struct netinfo *)&netcb; p = p->ni_forw)
238 p->ni_seen = 1;
239 error("Kernel state in transition");
240 return;
241 }
242 prev = next;
243 next = inpcb.inp_queue.cqe_next;
244
245 if (!aflag && inet_lnaof(inpcb.inp_laddr) == INADDR_ANY)
246 continue;
247 if (nhosts && !checkhost(&inpcb))
248 continue;
249 if (nports && !checkport(&inpcb))
250 continue;
251 KREAD(inpcb.inp_socket, &sockb, sizeof (sockb));
252 if (istcp) {
253 KREAD(inpcb.inp_ppcb, &tcpcb, sizeof (tcpcb));
254 enter(&inpcb, &sockb, tcpcb.t_state, "tcp");
255 } else
256 enter(&inpcb, &sockb, 0, "udp");
257 }
258 }
259
260 #ifdef INET6
261 static void
262 fetchnetstat6(void *off, int istcp)
263 {
264 struct netinfo *p;
265 struct socket sockb;
266 struct tcpcb tcpcb;
267 struct in6pcb in6pcb;
268 struct in6pcb *head6, *prev6, *next6;
269
270 KREAD(off, &in6pcb, sizeof (struct in6pcb));
271 prev6 = head6 = (struct in6pcb *)off;
272 next6 = in6pcb.in6p_next;
273 while (next6 != head6) {
274 KREAD(next6, &in6pcb, sizeof (in6pcb));
275 if (in6pcb.in6p_prev != prev6) {
276 printf("prev = %p, head = %p, next = %p, in6pcb...prev = %p\n", prev6, head6, next6, in6pcb.in6p_prev);
277 p = netcb.ni_forw;
278 for (; p != (struct netinfo *)&netcb; p = p->ni_forw)
279 p->ni_seen = 1;
280 error("Kernel state in transition");
281 return;
282 }
283 prev6 = next6;
284 next6 = in6pcb.in6p_next;
285
286 if (!aflag && IN6_IS_ADDR_UNSPECIFIED(&in6pcb.in6p_laddr))
287 continue;
288 if (nhosts && !checkhost6(&in6pcb))
289 continue;
290 if (nports && !checkport6(&in6pcb))
291 continue;
292 KREAD(in6pcb.in6p_socket, &sockb, sizeof (sockb));
293 if (istcp) {
294 KREAD(in6pcb.in6p_ppcb, &tcpcb, sizeof (tcpcb));
295 enter6(&in6pcb, &sockb, tcpcb.t_state, "tcp");
296 } else
297 enter6(&in6pcb, &sockb, 0, "udp");
298 }
299 }
300 #endif /*INET6*/
301
302 static void
303 enter(struct inpcb *inp, struct socket *so, int state, char *proto)
304 {
305 struct netinfo *p;
306
307 /*
308 * Only take exact matches, any sockets with
309 * previously unbound addresses will be deleted
310 * below in the display routine because they
311 * will appear as ``not seen'' in the kernel
312 * data structures.
313 */
314 for (p = netcb.ni_forw; p != (struct netinfo *)&netcb; p = p->ni_forw) {
315 if (p->ni_family != AF_INET)
316 continue;
317 if (!streq(proto, p->ni_proto))
318 continue;
319 if (p->ni_lport != inp->inp_lport ||
320 p->ni_laddr.s_addr != inp->inp_laddr.s_addr)
321 continue;
322 if (p->ni_faddr.s_addr == inp->inp_faddr.s_addr &&
323 p->ni_fport == inp->inp_fport)
324 break;
325 }
326 if (p == (struct netinfo *)&netcb) {
327 if ((p = malloc(sizeof(*p))) == NULL) {
328 error("Out of memory");
329 return;
330 }
331 p->ni_prev = (struct netinfo *)&netcb;
332 p->ni_forw = netcb.ni_forw;
333 netcb.ni_forw->ni_prev = p;
334 netcb.ni_forw = p;
335 p->ni_line = -1;
336 p->ni_laddr = inp->inp_laddr;
337 p->ni_lport = inp->inp_lport;
338 p->ni_faddr = inp->inp_faddr;
339 p->ni_fport = inp->inp_fport;
340 p->ni_proto = proto;
341 p->ni_flags = NIF_LACHG|NIF_FACHG;
342 p->ni_family = AF_INET;
343 }
344 p->ni_rcvcc = so->so_rcv.sb_cc;
345 p->ni_sndcc = so->so_snd.sb_cc;
346 p->ni_state = state;
347 p->ni_seen = 1;
348 }
349
350 #ifdef INET6
351 static void
352 enter6(struct in6pcb *in6p, struct socket *so, int state, char *proto)
353 {
354 struct netinfo *p;
355
356 /*
357 * Only take exact matches, any sockets with
358 * previously unbound addresses will be deleted
359 * below in the display routine because they
360 * will appear as ``not seen'' in the kernel
361 * data structures.
362 */
363 for (p = netcb.ni_forw; p != (struct netinfo *)&netcb; p = p->ni_forw) {
364 if (p->ni_family != AF_INET6)
365 continue;
366 if (!streq(proto, p->ni_proto))
367 continue;
368 if (p->ni_lport != in6p->in6p_lport ||
369 !IN6_ARE_ADDR_EQUAL(&p->ni_laddr6, &in6p->in6p_laddr))
370 continue;
371 if (IN6_ARE_ADDR_EQUAL(&p->ni_faddr6, &in6p->in6p_faddr) &&
372 p->ni_fport == in6p->in6p_fport)
373 break;
374 }
375 if (p == (struct netinfo *)&netcb) {
376 if ((p = malloc(sizeof(*p))) == NULL) {
377 error("Out of memory");
378 return;
379 }
380 p->ni_prev = (struct netinfo *)&netcb;
381 p->ni_forw = netcb.ni_forw;
382 netcb.ni_forw->ni_prev = p;
383 netcb.ni_forw = p;
384 p->ni_line = -1;
385 p->ni_laddr6 = in6p->in6p_laddr;
386 p->ni_lport = in6p->in6p_lport;
387 p->ni_faddr6 = in6p->in6p_faddr;
388 p->ni_fport = in6p->in6p_fport;
389 p->ni_proto = proto;
390 p->ni_flags = NIF_LACHG|NIF_FACHG;
391 p->ni_family = AF_INET6;
392 }
393 p->ni_rcvcc = so->so_rcv.sb_cc;
394 p->ni_sndcc = so->so_snd.sb_cc;
395 p->ni_state = state;
396 p->ni_seen = 1;
397 }
398 #endif
399
400 /* column locations */
401 #define LADDR 0
402 #define FADDR LADDR+23
403 #define PROTO FADDR+23
404 #define RCVCC PROTO+6
405 #define SNDCC RCVCC+7
406 #define STATE SNDCC+7
407
408 void
409 labelnetstat(void)
410 {
411
412 if (namelist[X_TCBTABLE].n_type == 0)
413 return;
414 wmove(wnd, 0, 0); wclrtobot(wnd);
415 mvwaddstr(wnd, 0, LADDR, "Local Address");
416 mvwaddstr(wnd, 0, FADDR, "Foreign Address");
417 mvwaddstr(wnd, 0, PROTO, "Proto");
418 mvwaddstr(wnd, 0, RCVCC, "Recv-Q");
419 mvwaddstr(wnd, 0, SNDCC, "Send-Q");
420 mvwaddstr(wnd, 0, STATE, "(state)");
421 }
422
423 void
424 shownetstat(void)
425 {
426 struct netinfo *p, *q;
427
428 /*
429 * First, delete any connections that have gone
430 * away and adjust the position of connections
431 * below to reflect the deleted line.
432 */
433 p = netcb.ni_forw;
434 while (p != (struct netinfo *)&netcb) {
435 if (p->ni_line == -1 || p->ni_seen) {
436 p = p->ni_forw;
437 continue;
438 }
439 wmove(wnd, p->ni_line, 0); wdeleteln(wnd);
440 q = netcb.ni_forw;
441 for (; q != (struct netinfo *)&netcb; q = q->ni_forw)
442 if (q != p && q->ni_line > p->ni_line) {
443 q->ni_line--;
444 /* this shouldn't be necessary */
445 q->ni_flags |= NIF_LACHG|NIF_FACHG;
446 }
447 lastrow--;
448 q = p->ni_forw;
449 p->ni_prev->ni_forw = p->ni_forw;
450 p->ni_forw->ni_prev = p->ni_prev;
451 free(p);
452 p = q;
453 }
454 /*
455 * Update existing connections and add new ones.
456 */
457 for (p = netcb.ni_forw; p != (struct netinfo *)&netcb; p = p->ni_forw) {
458 if (p->ni_line == -1) {
459 /*
460 * Add a new entry if possible.
461 */
462 if (lastrow > getmaxy(wnd))
463 continue;
464 p->ni_line = lastrow++;
465 p->ni_flags |= NIF_LACHG|NIF_FACHG;
466 }
467 if (p->ni_flags & NIF_LACHG) {
468 wmove(wnd, p->ni_line, LADDR);
469 switch (p->ni_family) {
470 case AF_INET:
471 inetprint(&p->ni_laddr, p->ni_lport,
472 p->ni_proto);
473 break;
474 #ifdef INET6
475 case AF_INET6:
476 inet6print(&p->ni_laddr6, p->ni_lport,
477 p->ni_proto);
478 break;
479 #endif
480 }
481 p->ni_flags &= ~NIF_LACHG;
482 }
483 if (p->ni_flags & NIF_FACHG) {
484 wmove(wnd, p->ni_line, FADDR);
485 switch (p->ni_family) {
486 case AF_INET:
487 inetprint(&p->ni_faddr, p->ni_fport,
488 p->ni_proto);
489 break;
490 #ifdef INET6
491 case AF_INET6:
492 inet6print(&p->ni_faddr6, p->ni_fport,
493 p->ni_proto);
494 break;
495 #endif
496 }
497 p->ni_flags &= ~NIF_FACHG;
498 }
499 mvwaddstr(wnd, p->ni_line, PROTO, p->ni_proto);
500 #ifdef INET6
501 if (p->ni_family == AF_INET6)
502 waddstr(wnd, "6");
503 #endif
504 mvwprintw(wnd, p->ni_line, RCVCC, "%6ld", p->ni_rcvcc);
505 mvwprintw(wnd, p->ni_line, SNDCC, "%6ld", p->ni_sndcc);
506 if (streq(p->ni_proto, "tcp")) {
507 if (p->ni_state < 0 || p->ni_state >= TCP_NSTATES)
508 mvwprintw(wnd, p->ni_line, STATE, "%d",
509 p->ni_state);
510 else
511 mvwaddstr(wnd, p->ni_line, STATE,
512 tcpstates[p->ni_state]);
513 }
514 wclrtoeol(wnd);
515 }
516 if (lastrow < getmaxy(wnd)) {
517 wmove(wnd, lastrow, 0); wclrtobot(wnd);
518 wmove(wnd, getmaxy(wnd), 0); wdeleteln(wnd); /* XXX */
519 }
520 }
521
522 /*
523 * Pretty print an Internet address (net address + port).
524 * If the nflag was specified, use numbers instead of names.
525 */
526 static void
527 inetprint(struct in_addr *in, int port, char *proto)
528 {
529 struct servent *sp = 0;
530 char line[80], *cp;
531
532 (void)snprintf(line, sizeof line, "%.*s.", 16, inetname(*in));
533 cp = strchr(line, '\0');
534 if (!nflag && port)
535 sp = getservbyport(port, proto);
536 if (sp || port == 0)
537 (void)snprintf(cp, line + sizeof line - cp, "%.8s",
538 sp ? sp->s_name : "*");
539 else
540 (void)snprintf(cp, line + sizeof line - cp, "%d",
541 ntohs((u_short)port));
542 /* pad to full column to clear any garbage */
543 cp = strchr(line, '\0');
544 while (cp - line < 22)
545 *cp++ = ' ';
546 *cp = '\0';
547 waddstr(wnd, line);
548 }
549
550 #ifdef INET6
551 static void
552 inet6print(struct in6_addr *in6, int port, char *proto)
553 {
554 struct servent *sp = 0;
555 char line[80], *cp;
556
557 (void)snprintf(line, sizeof line, "%.*s.", 16, inet6name(in6));
558 cp = strchr(line, '\0');
559 if (!nflag && port)
560 sp = getservbyport(port, proto);
561 if (sp || port == 0)
562 (void)snprintf(cp, line + sizeof line - cp, "%.8s",
563 sp ? sp->s_name : "*");
564 else
565 (void)snprintf(cp, line + sizeof line - cp, "%d",
566 ntohs((u_short)port));
567 /* pad to full column to clear any garbage */
568 cp = strchr(line, '\0');
569 while (cp - line < 22)
570 *cp++ = ' ';
571 *cp = '\0';
572 waddstr(wnd, line);
573 }
574 #endif
575
576 /*
577 * Construct an Internet address representation.
578 * If the nflag has been supplied, give
579 * numeric value, otherwise try for symbolic name.
580 */
581 static const char *
582 inetname(struct in_addr in)
583 {
584 char *cp = 0;
585 static char line[50];
586 struct hostent *hp;
587 struct netent *np;
588
589 if (!nflag && in.s_addr != INADDR_ANY) {
590 int net = inet_netof(in);
591 int lna = inet_lnaof(in);
592
593 if (lna == INADDR_ANY) {
594 np = getnetbyaddr(net, AF_INET);
595 if (np)
596 cp = np->n_name;
597 }
598 if (cp == 0) {
599 hp = gethostbyaddr((char *)&in, sizeof (in), AF_INET);
600 if (hp)
601 cp = hp->h_name;
602 }
603 }
604 if (in.s_addr == INADDR_ANY)
605 strlcpy(line, "*", sizeof(line));
606 else if (cp)
607 strlcpy(line, cp, sizeof(line));
608 else {
609 in.s_addr = ntohl(in.s_addr);
610 #define C(x) ((x) & 0xff)
611 (void)snprintf(line, sizeof line, "%u.%u.%u.%u",
612 C(in.s_addr >> 24), C(in.s_addr >> 16),
613 C(in.s_addr >> 8), C(in.s_addr));
614 #undef C
615 }
616 return (line);
617 }
618
619 #ifdef INET6
620 static const char *
621 inet6name(struct in6_addr *in6)
622 {
623 static char line[NI_MAXHOST];
624 struct sockaddr_in6 sin6;
625 int flags;
626
627 if (nflag)
628 flags = NI_NUMERICHOST;
629 else
630 flags = 0;
631 if (IN6_IS_ADDR_UNSPECIFIED(in6))
632 return "*";
633 memset(&sin6, 0, sizeof(sin6));
634 sin6.sin6_family = AF_INET6;
635 sin6.sin6_len = sizeof(struct sockaddr_in6);
636 sin6.sin6_addr = *in6;
637 if (getnameinfo((struct sockaddr *)&sin6, sin6.sin6_len,
638 line, sizeof(line), NULL, 0, flags) == 0)
639 return line;
640 return "?";
641 }
642 #endif
643
644 /* please note: there are also some netstat commands in netcmds.c */
645
646 void
647 netstat_all(char *args)
648 {
649 aflag = !aflag;
650 fetchnetstat();
651 shownetstat();
652 refresh();
653 }
654
655 void
656 netstat_names(char *args)
657 {
658 struct netinfo *p;
659
660 if (nflag == 0)
661 return;
662
663 p = netcb.ni_forw;
664 for (; p != (struct netinfo *)&netcb; p = p->ni_forw) {
665 if (p->ni_line == -1)
666 continue;
667 p->ni_flags |= NIF_LACHG|NIF_FACHG;
668 }
669 nflag = 0;
670 wclear(wnd);
671 labelnetstat();
672 shownetstat();
673 refresh();
674 }
675
676 void
677 netstat_numbers(char *args)
678 {
679 struct netinfo *p;
680
681 if (nflag != 0)
682 return;
683
684 p = netcb.ni_forw;
685 for (; p != (struct netinfo *)&netcb; p = p->ni_forw) {
686 if (p->ni_line == -1)
687 continue;
688 p->ni_flags |= NIF_LACHG|NIF_FACHG;
689 }
690 nflag = 1;
691 wclear(wnd);
692 labelnetstat();
693 shownetstat();
694 refresh();
695 }
696