1 /* $OpenBSD: netstat.c,v 1.45 2015/03/12 01:03:00 claudio 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 37 #include <kvm.h> 38 #include <sys/types.h> 39 #include <sys/sysctl.h> 40 #include <sys/socket.h> 41 #define _KERNEL 42 #include <sys/file.h> 43 #undef _KERNEL 44 45 #include <netinet/in.h> 46 #include <netinet/tcp.h> 47 #include <netinet/tcp_seq.h> 48 #define TCPSTATES 49 #include <netinet/tcp_fsm.h> 50 #include <arpa/inet.h> 51 52 #include <netdb.h> 53 #include <signal.h> 54 #include <stdlib.h> 55 #include <string.h> 56 #include <err.h> 57 #include <nlist.h> 58 #include <paths.h> 59 #include "systat.h" 60 #include "engine.h" 61 62 #define TCP 0x1 63 #define UDP 0x2 64 #define OTHER 0x4 65 66 struct netinfo { 67 union { 68 struct in_addr nif_laddr; /* local address */ 69 struct in6_addr nif_laddr6; /* local address */ 70 } l; 71 union { 72 struct in_addr nif_faddr; /* foreign address */ 73 struct in6_addr nif_faddr6; /* foreign address */ 74 } f; 75 long nif_rcvcc; /* rcv buffer character count */ 76 long nif_sndcc; /* snd buffer character count */ 77 short nif_lport; /* local port */ 78 short nif_fport; /* foreign port */ 79 short nif_state; /* tcp state */ 80 short nif_family; 81 short nif_proto; /* protocol */ 82 short nif_ipproto; 83 }; 84 85 #define nif_laddr l.nif_laddr 86 #define nif_laddr6 l.nif_laddr6 87 #define nif_faddr f.nif_faddr 88 #define nif_faddr6 f.nif_faddr6 89 90 static void enter(struct kinfo_file *); 91 static int kf_comp(const void *, const void *); 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 #define ADD_ALLOC 1000 106 107 int protos; 108 109 struct netinfo *netinfos = NULL; 110 size_t num_ns = 0; 111 static size_t num_alloc = 0; 112 113 114 field_def fields_ns[] = { 115 {"LOCAL ADDRESS", 20, 45, 1, FLD_ALIGN_LEFT, -1, 0, 0, 0}, 116 {"FOREIGN ADDRESS", 20, 45, 1, FLD_ALIGN_LEFT, -1, 0, 0, 0}, 117 {"PROTO", 4, 9, 1, FLD_ALIGN_LEFT, -1, 0, 0, 0}, 118 {"RECV-Q", 5, 8, 1, FLD_ALIGN_RIGHT, -1, 0, 0, 0}, 119 {"SEND-Q", 5, 8, 1, FLD_ALIGN_RIGHT, -1, 0, 0, 0}, 120 {"STATE", 5, 11, 6, FLD_ALIGN_LEFT, -1, 0, 0, 0}, 121 }; 122 123 #define FLD_NS_LOCAL FIELD_ADDR(fields_ns,0) 124 #define FLD_NS_FOREIGN FIELD_ADDR(fields_ns,1) 125 #define FLD_NS_PROTO FIELD_ADDR(fields_ns,2) 126 #define FLD_NS_RECV_Q FIELD_ADDR(fields_ns,3) 127 #define FLD_NS_SEND_Q FIELD_ADDR(fields_ns,4) 128 #define FLD_NS_STATE FIELD_ADDR(fields_ns,5) 129 130 /* Define views */ 131 field_def *view_ns_0[] = { 132 FLD_NS_LOCAL, FLD_NS_FOREIGN, FLD_NS_PROTO, 133 FLD_NS_RECV_Q, FLD_NS_SEND_Q, FLD_NS_STATE, NULL 134 }; 135 136 /* Define view managers */ 137 struct view_manager netstat_mgr = { 138 "Netstat", select_ns, read_ns, NULL, print_header, 139 print_ns, ns_keyboard_callback, NULL, NULL 140 }; 141 142 field_view views_ns[] = { 143 {view_ns_0, "netstat", '0', &netstat_mgr}, 144 {NULL, NULL, 0, NULL} 145 }; 146 147 148 149 150 struct netinfo * 151 next_ns(void) 152 { 153 if (num_alloc <= num_ns) { 154 struct netinfo *ni; 155 size_t a = num_alloc + ADD_ALLOC; 156 if (a < num_alloc) 157 return NULL; 158 ni = reallocarray(netinfos, a, sizeof(*ni)); 159 if (ni == NULL) 160 return NULL; 161 netinfos = ni; 162 num_alloc = a; 163 } 164 165 return &netinfos[num_ns++]; 166 } 167 168 static void 169 enter(struct kinfo_file *kf) 170 { 171 #define s6_addr32 __u6_addr.__u6_addr32 172 struct netinfo *p; 173 174 /* first filter out unwanted sockets */ 175 if (kf->so_family != AF_INET && kf->so_family != AF_INET6) 176 return; 177 178 switch (kf->so_protocol) { 179 case IPPROTO_TCP: 180 if ((protos & TCP) == 0) 181 return; 182 break; 183 case IPPROTO_UDP: 184 if ((protos & UDP) == 0) 185 return; 186 break; 187 default: 188 if ((protos & OTHER) == 0) 189 return; 190 break; 191 } 192 193 if (!aflag) { 194 struct in6_addr faddr6; 195 196 switch (kf->so_family) { 197 case AF_INET: 198 if (kf->inp_faddru[0] == INADDR_ANY) 199 return; 200 break; 201 case AF_INET6: 202 faddr6.s6_addr32[0] = kf->inp_faddru[0]; 203 faddr6.s6_addr32[1] = kf->inp_faddru[1]; 204 faddr6.s6_addr32[2] = kf->inp_faddru[2]; 205 faddr6.s6_addr32[3] = kf->inp_faddru[3]; 206 if (IN6_IS_ADDR_UNSPECIFIED(&faddr6)) 207 return; 208 break; 209 } 210 } 211 212 /* finally enter the socket to the table */ 213 p = next_ns(); 214 if (p == NULL) { 215 error("Out of Memory!"); 216 return; 217 } 218 219 p->nif_lport = kf->inp_lport; 220 p->nif_fport = kf->inp_fport; 221 p->nif_proto = kf->so_protocol; 222 p->nif_ipproto = kf->inp_proto; 223 224 switch (kf->so_family) { 225 case AF_INET: 226 p->nif_family = AF_INET; 227 p->nif_laddr.s_addr = kf->inp_laddru[0]; 228 p->nif_faddr.s_addr = kf->inp_faddru[0]; 229 break; 230 case AF_INET6: 231 p->nif_family = AF_INET6; 232 p->nif_laddr6.s6_addr32[0] = kf->inp_laddru[0]; 233 p->nif_laddr6.s6_addr32[1] = kf->inp_laddru[1]; 234 p->nif_laddr6.s6_addr32[2] = kf->inp_laddru[2]; 235 p->nif_laddr6.s6_addr32[3] = kf->inp_laddru[3]; 236 p->nif_faddr6.s6_addr32[0] = kf->inp_faddru[0]; 237 p->nif_faddr6.s6_addr32[1] = kf->inp_faddru[1]; 238 p->nif_faddr6.s6_addr32[2] = kf->inp_faddru[2]; 239 p->nif_faddr6.s6_addr32[3] = kf->inp_faddru[3]; 240 break; 241 } 242 243 p->nif_rcvcc = kf->so_rcv_cc; 244 p->nif_sndcc = kf->so_snd_cc; 245 p->nif_state = kf->t_state; 246 #undef s6_addr32 247 } 248 249 250 /* netstat callback functions */ 251 252 int 253 select_ns(void) 254 { 255 num_disp = num_ns; 256 return (0); 257 } 258 259 static int type_map[] = { -1, 2, 3, 1, 4, 5 }; 260 261 static int 262 kf_comp(const void *a, const void *b) 263 { 264 const struct kinfo_file *ka = a, *kb = b; 265 266 if (ka->so_family != kb->so_family) { 267 /* AF_INET < AF_INET6 < AF_LOCAL */ 268 if (ka->so_family == AF_INET) 269 return (-1); 270 if (ka->so_family == AF_LOCAL) 271 return (1); 272 if (kb->so_family == AF_LOCAL) 273 return (-1); 274 return (1); 275 } 276 if (ka->so_family == AF_LOCAL) { 277 if (type_map[ka->so_type] < type_map[kb->so_type]) 278 return (-1); 279 if (type_map[ka->so_type] > type_map[kb->so_type]) 280 return (1); 281 } else if (ka->so_family == AF_INET || ka->so_family == AF_INET6) { 282 if (ka->so_protocol < kb->so_protocol) 283 return (-1); 284 if (ka->so_protocol > kb->so_protocol) 285 return (1); 286 if (ka->so_type == SOCK_DGRAM || ka->so_type == SOCK_STREAM) { 287 /* order sockets by remote port desc */ 288 if (ka->inp_fport > kb->inp_fport) 289 return (-1); 290 if (ka->inp_fport < kb->inp_fport) 291 return (1); 292 } else if (ka->so_type == SOCK_RAW) { 293 if (ka->inp_proto > kb->inp_proto) 294 return (-1); 295 if (ka->inp_proto < kb->inp_proto) 296 return (1); 297 } 298 } 299 return (0); 300 } 301 302 303 int 304 read_ns(void) 305 { 306 struct kinfo_file *kf; 307 int i, fcnt; 308 309 if (kd == NULL) { 310 error("Failed to initialize KVM!"); 311 return (0); 312 } 313 kf = kvm_getfiles(kd, KERN_FILE_BYFILE, DTYPE_SOCKET, 314 sizeof(*kf), &fcnt); 315 if (kf == NULL) { 316 error("Out of Memory!"); 317 return (0); 318 } 319 320 /* sort sockets by AF, proto and type */ 321 qsort(kf, fcnt, sizeof(*kf), kf_comp); 322 323 num_ns = 0; 324 325 for (i = 0; i < fcnt; i++) 326 enter(&kf[i]); 327 328 num_disp = num_ns; 329 return 0; 330 } 331 332 void 333 print_ns(void) 334 { 335 int n, count = 0; 336 337 for (n = dispstart; n < num_disp; n++) { 338 shownetstat(netinfos + n); 339 count++; 340 if (maxprint > 0 && count >= maxprint) 341 break; 342 } 343 } 344 345 346 int 347 initnetstat(void) 348 { 349 field_view *v; 350 351 protos = TCP|UDP|OTHER; 352 for (v = views_ns; v->name != NULL; v++) 353 add_view(v); 354 355 return(1); 356 } 357 358 static void 359 shownetstat(struct netinfo *p) 360 { 361 char *proto = NULL; 362 363 switch (p->nif_proto) { 364 case IPPROTO_TCP: 365 proto = "tcp"; 366 break; 367 case IPPROTO_UDP: 368 proto = "udp"; 369 break; 370 } 371 372 switch (p->nif_family) { 373 case AF_INET: 374 inetprint(&p->nif_laddr, p->nif_lport, 375 proto, FLD_NS_LOCAL); 376 inetprint(&p->nif_faddr, p->nif_fport, 377 proto, FLD_NS_FOREIGN); 378 break; 379 case AF_INET6: 380 inet6print(&p->nif_laddr6, p->nif_lport, 381 proto, FLD_NS_LOCAL); 382 inet6print(&p->nif_faddr6, p->nif_fport, 383 proto, FLD_NS_FOREIGN); 384 break; 385 } 386 387 tb_start(); 388 switch (p->nif_proto) { 389 case IPPROTO_TCP: 390 case IPPROTO_UDP: 391 tbprintf(proto); 392 if (p->nif_family == AF_INET6) 393 tbprintf("6"); 394 break; 395 case IPPROTO_DIVERT: 396 tbprintf("divert"); 397 if (p->nif_family == AF_INET6) 398 tbprintf("6"); 399 break; 400 default: 401 tbprintf("%d", p->nif_ipproto); 402 break; 403 } 404 405 print_fld_tb(FLD_NS_PROTO); 406 407 print_fld_size(FLD_NS_RECV_Q, p->nif_rcvcc); 408 print_fld_size(FLD_NS_SEND_Q, p->nif_sndcc); 409 410 if (p->nif_proto == IPPROTO_TCP) { 411 if (p->nif_state < 0 || p->nif_state >= TCP_NSTATES) 412 print_fld_uint(FLD_NS_STATE, p->nif_state); 413 else 414 print_fld_str(FLD_NS_STATE, tcpstates[p->nif_state]); 415 } 416 end_line(); 417 } 418 419 /* 420 * Pretty print an Internet address (net address + port). 421 * If the nflag was specified, use numbers instead of names. 422 */ 423 static void 424 inetprint(struct in_addr *in, int port, char *proto, field_def *fld) 425 { 426 struct servent *sp = 0; 427 428 tb_start(); 429 tbprintf("%s", inetname(*in)); 430 431 if (!nflag && port) 432 sp = getservbyport(port, proto); 433 if (sp || port == 0) 434 tbprintf(":%s", sp ? sp->s_name : "*"); 435 else 436 tbprintf(":%d", ntohs((u_short)port)); 437 438 print_fld_tb(fld); 439 } 440 441 static void 442 inet6print(struct in6_addr *in6, int port, char *proto, field_def *fld) 443 { 444 struct servent *sp = 0; 445 446 tb_start(); 447 448 tbprintf("%s", inet6name(in6)); 449 if (!nflag && port) 450 sp = getservbyport(port, proto); 451 if (sp || port == 0) 452 tbprintf(":%s", sp ? sp->s_name : "*"); 453 else 454 tbprintf(":%d", ntohs((u_short)port)); 455 456 print_fld_tb(fld); 457 } 458 459 int 460 ns_keyboard_callback(int ch) 461 { 462 switch (ch) { 463 case 'a': 464 aflag = !aflag; 465 gotsig_alarm = 1; 466 break; 467 case 'n': 468 nflag = !nflag; 469 gotsig_alarm = 1; 470 break; 471 case 'o': 472 protos ^= OTHER; 473 gotsig_alarm = 1; 474 break; 475 case 'r': 476 aflag = 0; 477 nflag = 1; 478 protos = TCP|UDP; 479 gotsig_alarm = 1; 480 break; 481 case 't': 482 protos ^= TCP; 483 gotsig_alarm = 1; 484 break; 485 case 'u': 486 protos ^= UDP; 487 gotsig_alarm = 1; 488 break; 489 default: 490 return keyboard_callback(ch); 491 }; 492 493 return 1; 494 } 495 496