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