1 /* $OpenBSD: netstat.c,v 1.32 2008/12/07 02:56:06 canacar 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 #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> 60 #include <arpa/inet.h> 61 62 #include <netdb.h> 63 #include <stdlib.h> 64 #include <string.h> 65 #include <err.h> 66 #include <nlist.h> 67 #include <paths.h> 68 #include "systat.h" 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 93 94 static void enter(struct inpcb *, struct socket *, int, char *); 95 static const char *inetname(struct in_addr); 96 static void inetprint(struct in_addr *, int, char *, field_def *); 97 static const char *inet6name(struct in6_addr *); 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); 105 106 #define streq(a,b) (strcmp(a,b)==0) 107 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 }; 156 157 field_view views_ns[] = { 158 {view_ns_0, "netstat", '0', &netstat_mgr}, 159 {NULL, NULL, 0, NULL} 160 }; 161 162 163 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) 185 { 186 struct netinfo *p; 187 188 p = next_ns(); 189 if (p == NULL) { 190 error("Out of Memory!"); 191 return; 192 } 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; 211 } 212 213 214 /* netstat callback functions */ 215 216 int 217 select_ns(void) 218 { 219 static int init = 0; 220 if (kd == NULL) { 221 num_disp = 1; 222 return (0); 223 } 224 225 if (!init) { 226 sethostent(1); 227 setnetent(1); 228 init = 1; 229 } 230 231 num_disp = num_ns; 232 return (0); 233 } 234 235 int 236 read_ns(void) 237 { 238 struct inpcbtable pcbtable; 239 struct inpcb *head, *prev, *next; 240 struct inpcb inpcb; 241 struct socket sockb; 242 struct tcpcb tcpcb; 243 void *off; 244 int istcp; 245 246 if (kd == NULL) { 247 return (0); 248 } 249 250 num_ns = 0; 251 252 if (namelist[X_TCBTABLE].n_value == 0) 253 return 0; 254 255 if (protos & TCP) { 256 off = NPTR(X_TCBTABLE); 257 istcp = 1; 258 } else if (protos & UDP) { 259 off = NPTR(X_UDBTABLE); 260 istcp = 0; 261 } else { 262 error("No protocols to display"); 263 return 0; 264 } 265 266 again: 267 KREAD(off, &pcbtable, sizeof (struct inpcbtable)); 268 269 prev = head = (struct inpcb *)&((struct inpcbtable *)off)->inpt_queue; 270 next = CIRCLEQ_FIRST(&pcbtable.inpt_queue); 271 272 while (next != head) { 273 KREAD(next, &inpcb, sizeof (inpcb)); 274 if (CIRCLEQ_PREV(&inpcb, inp_queue) != prev) { 275 error("Kernel state in transition"); 276 return 0; 277 } 278 prev = next; 279 next = CIRCLEQ_NEXT(&inpcb, inp_queue); 280 281 if (!aflag) { 282 if (!(inpcb.inp_flags & INP_IPV6) && 283 inet_lnaof(inpcb.inp_laddr) == INADDR_ANY) 284 continue; 285 if ((inpcb.inp_flags & INP_IPV6) && 286 IN6_IS_ADDR_UNSPECIFIED(&inpcb.inp_laddr6)) 287 continue; 288 } 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 } 296 if (istcp && (protos & UDP)) { 297 istcp = 0; 298 off = NPTR(X_UDBTABLE); 299 goto again; 300 } 301 302 num_disp = num_ns; 303 return 0; 304 } 305 306 void 307 print_ns(void) 308 { 309 int n, count = 0; 310 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; 316 } 317 318 for (n = dispstart; n < num_disp; n++) { 319 shownetstat(netinfos + n); 320 count++; 321 if (maxprint > 0 && count >= maxprint) 322 break; 323 } 324 } 325 326 327 int 328 initnetstat(void) 329 { 330 field_view *v; 331 int ret; 332 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); 350 } 351 352 static void 353 shownetstat(struct netinfo *p) 354 { 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]); 385 } 386 end_line(); 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 394 inetprint(struct in_addr *in, int port, char *proto, field_def *fld) 395 { 396 struct servent *sp = 0; 397 398 tb_start(); 399 tbprintf("%s", inetname(*in)); 400 401 if (!nflag && port) 402 sp = getservbyport(port, proto); 403 if (sp || port == 0) 404 tbprintf(":%s", sp ? sp->s_name : "*"); 405 else 406 tbprintf(":%d", ntohs((u_short)port)); 407 408 print_fld_tb(fld); 409 } 410 411 static void 412 inet6print(struct in6_addr *in6, int port, char *proto, field_def *fld) 413 { 414 struct servent *sp = 0; 415 416 tb_start(); 417 418 tbprintf("%s", inet6name(in6)); 419 if (!nflag && port) 420 sp = getservbyport(port, proto); 421 if (sp || port == 0) 422 tbprintf(":%s", sp ? sp->s_name : "*"); 423 else 424 tbprintf(":%d", ntohs((u_short)port)); 425 426 print_fld_tb(fld); 427 } 428 429 static const char * 430 inet6name(struct in6_addr *in6) 431 { 432 static char line[NI_MAXHOST]; 433 struct sockaddr_in6 sin6; 434 int flags; 435 436 flags = nflag ? NI_NUMERICHOST : 0; 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, 444 line, sizeof(line), NULL, 0, flags) == 0) 445 return line; 446 return "?"; 447 } 448 449 static const char * 450 inetname(struct in_addr in) 451 { 452 static char line[NI_MAXHOST]; 453 struct sockaddr_in si; 454 int flags, e; 455 456 flags = nflag ? NI_NUMERICHOST : 0; 457 if (in.s_addr == INADDR_ANY) 458 return "*"; 459 460 memset(&si, 0, sizeof(si)); 461 si.sin_family = AF_INET; 462 si.sin_len = sizeof(struct sockaddr_in); 463 si.sin_addr = in; 464 465 e = getnameinfo((struct sockaddr *)&si, si.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 488 int 489 ns_keyboard_callback(int ch) 490 { 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 }; 507 508 return 1; 509 } 510 511