1 /* $OpenBSD: netstat.c,v 1.39 2013/12/25 01:46:00 tedu 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 *next, *prev; 236 struct inpcb inpcb, prevpcb; 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 = NULL; 266 next = TAILQ_FIRST(&pcbtable.inpt_queue); 267 268 while (next != NULL) { 269 KREAD(next, &inpcb, sizeof (inpcb)); 270 if (prev != NULL) { 271 KREAD(prev, &prevpcb, sizeof (prevpcb)); 272 if (TAILQ_NEXT(&prevpcb, inp_queue) != next) { 273 error("Kernel state in transition"); 274 return 0; 275 } 276 } 277 prev = next; 278 next = TAILQ_NEXT(&inpcb, inp_queue); 279 280 if (!aflag) { 281 if (!(inpcb.inp_flags & INP_IPV6) && 282 inet_lnaof(inpcb.inp_faddr) == INADDR_ANY) 283 continue; 284 if ((inpcb.inp_flags & INP_IPV6) && 285 IN6_IS_ADDR_UNSPECIFIED(&inpcb.inp_faddr6)) 286 continue; 287 } 288 KREAD(inpcb.inp_socket, &sockb, sizeof (sockb)); 289 if (istcp) { 290 KREAD(inpcb.inp_ppcb, &tcpcb, sizeof (tcpcb)); 291 if (!aflag && tcpcb.t_state <= TCPS_LISTEN) 292 continue; 293 enter(&inpcb, &sockb, tcpcb.t_state, "tcp"); 294 } else 295 enter(&inpcb, &sockb, 0, "udp"); 296 } 297 if (istcp && (protos & UDP)) { 298 istcp = 0; 299 off = NPTR(X_UDBTABLE); 300 goto again; 301 } 302 303 num_disp = num_ns; 304 return 0; 305 } 306 307 void 308 print_ns(void) 309 { 310 int n, count = 0; 311 312 if (kd == NULL) { 313 print_fld_str(FLD_NS_LOCAL, "Failed to initialize KVM!"); 314 print_fld_str(FLD_NS_FOREIGN, "Failed to initialize KVM!"); 315 end_line(); 316 return; 317 } 318 319 for (n = dispstart; n < num_disp; n++) { 320 shownetstat(netinfos + n); 321 count++; 322 if (maxprint > 0 && count >= maxprint) 323 break; 324 } 325 } 326 327 328 int 329 initnetstat(void) 330 { 331 field_view *v; 332 int ret; 333 334 if (kd) { 335 if ((ret = kvm_nlist(kd, namelist)) == -1) 336 errx(1, "%s", kvm_geterr(kd)); 337 else if (ret) 338 nlisterr(namelist); 339 340 if (namelist[X_TCBTABLE].n_value == 0) { 341 error("No symbols in namelist"); 342 return(0); 343 } 344 } 345 protos = TCP|UDP; 346 347 for (v = views_ns; v->name != NULL; v++) 348 add_view(v); 349 350 return(1); 351 } 352 353 static void 354 shownetstat(struct netinfo *p) 355 { 356 switch (p->nif_family) { 357 case AF_INET: 358 inetprint(&p->nif_laddr, p->nif_lport, 359 p->nif_proto, FLD_NS_LOCAL); 360 inetprint(&p->nif_faddr, p->nif_fport, 361 p->nif_proto, FLD_NS_FOREIGN); 362 break; 363 case AF_INET6: 364 inet6print(&p->nif_laddr6, p->nif_lport, 365 p->nif_proto, FLD_NS_LOCAL); 366 inet6print(&p->nif_faddr6, p->nif_fport, 367 p->nif_proto, FLD_NS_FOREIGN); 368 break; 369 } 370 371 tb_start(); 372 tbprintf("%s", p->nif_proto); 373 if (p->nif_family == AF_INET6) 374 tbprintf("6"); 375 376 print_fld_tb(FLD_NS_PROTO); 377 378 print_fld_size(FLD_NS_RECV_Q, p->nif_rcvcc); 379 print_fld_size(FLD_NS_SEND_Q, p->nif_sndcc); 380 381 if (streq(p->nif_proto, "tcp")) { 382 if (p->nif_state < 0 || p->nif_state >= TCP_NSTATES) 383 print_fld_uint(FLD_NS_STATE, p->nif_state); 384 else 385 print_fld_str(FLD_NS_STATE, tcpstates[p->nif_state]); 386 } 387 end_line(); 388 } 389 390 /* 391 * Pretty print an Internet address (net address + port). 392 * If the nflag was specified, use numbers instead of names. 393 */ 394 static void 395 inetprint(struct in_addr *in, int port, char *proto, field_def *fld) 396 { 397 struct servent *sp = 0; 398 399 tb_start(); 400 tbprintf("%s", inetname(*in)); 401 402 if (!nflag && port) 403 sp = getservbyport(port, proto); 404 if (sp || port == 0) 405 tbprintf(":%s", sp ? sp->s_name : "*"); 406 else 407 tbprintf(":%d", ntohs((u_short)port)); 408 409 print_fld_tb(fld); 410 } 411 412 static void 413 inet6print(struct in6_addr *in6, int port, char *proto, field_def *fld) 414 { 415 struct servent *sp = 0; 416 417 tb_start(); 418 419 tbprintf("%s", inet6name(in6)); 420 if (!nflag && port) 421 sp = getservbyport(port, proto); 422 if (sp || port == 0) 423 tbprintf(":%s", sp ? sp->s_name : "*"); 424 else 425 tbprintf(":%d", ntohs((u_short)port)); 426 427 print_fld_tb(fld); 428 } 429 430 int 431 kvm_ckread(void *a, void *b, size_t l) 432 { 433 if (kvm_read(kd, (u_long)a, b, l) != l) { 434 if (verbose) 435 error("error reading kmem\n"); 436 return (0); 437 } else 438 return (1); 439 } 440 441 442 int 443 ns_keyboard_callback(int ch) 444 { 445 switch (ch) { 446 case 'a': 447 aflag = !aflag; 448 gotsig_alarm = 1; 449 break; 450 case 'n': 451 nflag = !nflag; 452 gotsig_alarm = 1; 453 break; 454 case 'r': 455 aflag = 0; 456 nflag = 1; 457 protos = TCP|UDP; 458 gotsig_alarm = 1; 459 break; 460 case 't': 461 protos ^= TCP; 462 gotsig_alarm = 1; 463 break; 464 case 'u': 465 protos ^= UDP; 466 gotsig_alarm = 1; 467 break; 468 default: 469 return keyboard_callback(ch); 470 }; 471 472 return 1; 473 } 474 475