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