1 /* $OpenBSD: netstat.c,v 1.35 2012/01/30 14:35:50 okan 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 void inetprint(struct in_addr *, int, char *, field_def *); 96 static void inet6print(struct in6_addr *, int, char *, field_def *); 97 static void shownetstat(struct netinfo *p); 98 99 void print_ns(void); 100 int read_ns(void); 101 int select_ns(void); 102 int ns_keyboard_callback(int); 103 104 #define streq(a,b) (strcmp(a,b)==0) 105 106 static int aflag = 0; 107 108 static struct nlist namelist[] = { 109 #define X_TCBTABLE 0 /* no sysctl */ 110 { "_tcbtable" }, 111 #define X_UDBTABLE 1 /* no sysctl */ 112 { "_udbtable" }, 113 { "" }, 114 }; 115 #define ADD_ALLOC 1000 116 117 118 int protos; 119 120 struct netinfo *netinfos = NULL; 121 size_t num_ns = 0; 122 static size_t num_alloc = 0; 123 124 125 field_def fields_ns[] = { 126 {"LOCAL ADDRESS", 20, 45, 1, FLD_ALIGN_LEFT, -1, 0, 0, 0}, 127 {"FOREIGN ADDRESS", 20, 45, 1, FLD_ALIGN_LEFT, -1, 0, 0, 0}, 128 {"PROTO", 4, 9, 1, FLD_ALIGN_LEFT, -1, 0, 0, 0}, 129 {"RECV-Q", 5, 8, 1, FLD_ALIGN_RIGHT, -1, 0, 0, 0}, 130 {"SEND-Q", 5, 8, 1, FLD_ALIGN_RIGHT, -1, 0, 0, 0}, 131 {"STATE", 5, 11, 6, FLD_ALIGN_LEFT, -1, 0, 0, 0}, 132 }; 133 134 #define FLD_NS_LOCAL FIELD_ADDR(fields_ns,0) 135 #define FLD_NS_FOREIGN FIELD_ADDR(fields_ns,1) 136 #define FLD_NS_PROTO FIELD_ADDR(fields_ns,2) 137 #define FLD_NS_RECV_Q FIELD_ADDR(fields_ns,3) 138 #define FLD_NS_SEND_Q FIELD_ADDR(fields_ns,4) 139 #define FLD_NS_STATE FIELD_ADDR(fields_ns,5) 140 141 /* Define views */ 142 field_def *view_ns_0[] = { 143 FLD_NS_LOCAL, FLD_NS_FOREIGN, FLD_NS_PROTO, 144 FLD_NS_RECV_Q, FLD_NS_SEND_Q, FLD_NS_STATE, NULL 145 }; 146 147 /* Define view managers */ 148 struct view_manager netstat_mgr = { 149 "Netstat", select_ns, read_ns, NULL, print_header, 150 print_ns, ns_keyboard_callback, NULL, NULL 151 }; 152 153 field_view views_ns[] = { 154 {view_ns_0, "netstat", '0', &netstat_mgr}, 155 {NULL, NULL, 0, NULL} 156 }; 157 158 159 160 161 struct netinfo * 162 next_ns(void) 163 { 164 if (num_alloc <= num_ns) { 165 struct netinfo *ni; 166 size_t a = num_alloc + ADD_ALLOC; 167 if (a < num_alloc) 168 return NULL; 169 ni = realloc(netinfos, a * sizeof(*ni)); 170 if (ni == NULL) 171 return NULL; 172 netinfos = ni; 173 num_alloc = a; 174 } 175 176 return &netinfos[num_ns++]; 177 } 178 179 static void 180 enter(struct inpcb *inp, struct socket *so, int state, char *proto) 181 { 182 struct netinfo *p; 183 184 p = next_ns(); 185 if (p == NULL) { 186 error("Out of Memory!"); 187 return; 188 } 189 190 p->nif_lport = inp->inp_lport; 191 p->nif_fport = inp->inp_fport; 192 p->nif_proto = proto; 193 194 if (inp->inp_flags & INP_IPV6) { 195 p->nif_laddr6 = inp->inp_laddr6; 196 p->nif_faddr6 = inp->inp_faddr6; 197 p->nif_family = AF_INET6; 198 } else { 199 p->nif_laddr = inp->inp_laddr; 200 p->nif_faddr = inp->inp_faddr; 201 p->nif_family = AF_INET; 202 } 203 204 p->nif_rcvcc = so->so_rcv.sb_cc; 205 p->nif_sndcc = so->so_snd.sb_cc; 206 p->nif_state = state; 207 } 208 209 210 /* netstat callback functions */ 211 212 int 213 select_ns(void) 214 { 215 static int init = 0; 216 if (kd == NULL) { 217 num_disp = 1; 218 return (0); 219 } 220 221 if (!init) { 222 sethostent(1); 223 setnetent(1); 224 init = 1; 225 } 226 227 num_disp = num_ns; 228 return (0); 229 } 230 231 int 232 read_ns(void) 233 { 234 struct inpcbtable pcbtable; 235 struct inpcb *head, *prev, *next; 236 struct inpcb inpcb; 237 struct socket sockb; 238 struct tcpcb tcpcb; 239 void *off; 240 int istcp; 241 242 if (kd == NULL) { 243 return (0); 244 } 245 246 num_ns = 0; 247 248 if (namelist[X_TCBTABLE].n_value == 0) 249 return 0; 250 251 if (protos & TCP) { 252 off = NPTR(X_TCBTABLE); 253 istcp = 1; 254 } else if (protos & UDP) { 255 off = NPTR(X_UDBTABLE); 256 istcp = 0; 257 } else { 258 error("No protocols to display"); 259 return 0; 260 } 261 262 again: 263 KREAD(off, &pcbtable, sizeof (struct inpcbtable)); 264 265 prev = head = (struct inpcb *)&((struct inpcbtable *)off)->inpt_queue; 266 next = CIRCLEQ_FIRST(&pcbtable.inpt_queue); 267 268 while (next != head) { 269 KREAD(next, &inpcb, sizeof (inpcb)); 270 if (CIRCLEQ_PREV(&inpcb, inp_queue) != prev) { 271 error("Kernel state in transition"); 272 return 0; 273 } 274 prev = next; 275 next = CIRCLEQ_NEXT(&inpcb, inp_queue); 276 277 if (!aflag) { 278 if (!(inpcb.inp_flags & INP_IPV6) && 279 inet_lnaof(inpcb.inp_faddr) == INADDR_ANY) 280 continue; 281 if ((inpcb.inp_flags & INP_IPV6) && 282 IN6_IS_ADDR_UNSPECIFIED(&inpcb.inp_faddr6)) 283 continue; 284 } 285 KREAD(inpcb.inp_socket, &sockb, sizeof (sockb)); 286 if (istcp) { 287 KREAD(inpcb.inp_ppcb, &tcpcb, sizeof (tcpcb)); 288 if (!aflag && tcpcb.t_state <= TCPS_LISTEN) 289 continue; 290 enter(&inpcb, &sockb, tcpcb.t_state, "tcp"); 291 } else 292 enter(&inpcb, &sockb, 0, "udp"); 293 } 294 if (istcp && (protos & UDP)) { 295 istcp = 0; 296 off = NPTR(X_UDBTABLE); 297 goto again; 298 } 299 300 num_disp = num_ns; 301 return 0; 302 } 303 304 void 305 print_ns(void) 306 { 307 int n, count = 0; 308 309 if (kd == NULL) { 310 print_fld_str(FLD_NS_LOCAL, "Failed to initialize KVM!"); 311 print_fld_str(FLD_NS_FOREIGN, "Failed to initialize KVM!"); 312 end_line(); 313 return; 314 } 315 316 for (n = dispstart; n < num_disp; n++) { 317 shownetstat(netinfos + n); 318 count++; 319 if (maxprint > 0 && count >= maxprint) 320 break; 321 } 322 } 323 324 325 int 326 initnetstat(void) 327 { 328 field_view *v; 329 int ret; 330 331 if (kd) { 332 if ((ret = kvm_nlist(kd, namelist)) == -1) 333 errx(1, "%s", kvm_geterr(kd)); 334 else if (ret) 335 nlisterr(namelist); 336 337 if (namelist[X_TCBTABLE].n_value == 0) { 338 error("No symbols in namelist"); 339 return(0); 340 } 341 } 342 protos = TCP|UDP; 343 344 for (v = views_ns; v->name != NULL; v++) 345 add_view(v); 346 347 return(1); 348 } 349 350 static void 351 shownetstat(struct netinfo *p) 352 { 353 switch (p->nif_family) { 354 case AF_INET: 355 inetprint(&p->nif_laddr, p->nif_lport, 356 p->nif_proto, FLD_NS_LOCAL); 357 inetprint(&p->nif_faddr, p->nif_fport, 358 p->nif_proto, FLD_NS_FOREIGN); 359 break; 360 case AF_INET6: 361 inet6print(&p->nif_laddr6, p->nif_lport, 362 p->nif_proto, FLD_NS_LOCAL); 363 inet6print(&p->nif_faddr6, p->nif_fport, 364 p->nif_proto, FLD_NS_FOREIGN); 365 break; 366 } 367 368 tb_start(); 369 tbprintf("%s", p->nif_proto); 370 if (p->nif_family == AF_INET6) 371 tbprintf("6"); 372 373 print_fld_tb(FLD_NS_PROTO); 374 375 print_fld_size(FLD_NS_RECV_Q, p->nif_rcvcc); 376 print_fld_size(FLD_NS_SEND_Q, p->nif_sndcc); 377 378 if (streq(p->nif_proto, "tcp")) { 379 if (p->nif_state < 0 || p->nif_state >= TCP_NSTATES) 380 print_fld_uint(FLD_NS_STATE, p->nif_state); 381 else 382 print_fld_str(FLD_NS_STATE, tcpstates[p->nif_state]); 383 } 384 end_line(); 385 } 386 387 /* 388 * Pretty print an Internet address (net address + port). 389 * If the nflag was specified, use numbers instead of names. 390 */ 391 static void 392 inetprint(struct in_addr *in, int port, char *proto, field_def *fld) 393 { 394 struct servent *sp = 0; 395 396 tb_start(); 397 tbprintf("%s", inetname(*in)); 398 399 if (!nflag && port) 400 sp = getservbyport(port, proto); 401 if (sp || port == 0) 402 tbprintf(":%s", sp ? sp->s_name : "*"); 403 else 404 tbprintf(":%d", ntohs((u_short)port)); 405 406 print_fld_tb(fld); 407 } 408 409 static void 410 inet6print(struct in6_addr *in6, int port, char *proto, field_def *fld) 411 { 412 struct servent *sp = 0; 413 414 tb_start(); 415 416 tbprintf("%s", inet6name(in6)); 417 if (!nflag && port) 418 sp = getservbyport(port, proto); 419 if (sp || port == 0) 420 tbprintf(":%s", sp ? sp->s_name : "*"); 421 else 422 tbprintf(":%d", ntohs((u_short)port)); 423 424 print_fld_tb(fld); 425 } 426 427 int 428 kvm_ckread(void *a, void *b, size_t l) 429 { 430 if (kvm_read(kd, (u_long)a, b, l) != l) { 431 if (verbose) 432 error("error reading kmem at %x\n", a); 433 return (0); 434 } else 435 return (1); 436 } 437 438 439 int 440 ns_keyboard_callback(int ch) 441 { 442 switch (ch) { 443 case 'a': 444 aflag = !aflag; 445 gotsig_alarm = 1; 446 break; 447 case 'n': 448 nflag = !nflag; 449 gotsig_alarm = 1; 450 break; 451 case 'r': 452 aflag = 0; 453 nflag = 1; 454 protos = TCP|UDP; 455 gotsig_alarm = 1; 456 break; 457 case 't': 458 protos ^= TCP; 459 gotsig_alarm = 1; 460 break; 461 case 'u': 462 protos ^= UDP; 463 gotsig_alarm = 1; 464 break; 465 default: 466 return keyboard_callback(ch); 467 }; 468 469 return 1; 470 } 471 472