1 0 stevel /* 2 0 stevel * CDDL HEADER START 3 0 stevel * 4 0 stevel * The contents of this file are subject to the terms of the 5 1676 jpk * Common Development and Distribution License (the "License"). 6 1676 jpk * You may not use this file except in compliance with the License. 7 0 stevel * 8 0 stevel * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 0 stevel * or http://www.opensolaris.org/os/licensing. 10 0 stevel * See the License for the specific language governing permissions 11 0 stevel * and limitations under the License. 12 0 stevel * 13 0 stevel * When distributing Covered Code, include this CDDL HEADER in each 14 0 stevel * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 0 stevel * If applicable, add the following below this CDDL HEADER, with the 16 0 stevel * fields enclosed by brackets "[]" replaced with your own identifying 17 0 stevel * information: Portions Copyright [yyyy] [name of copyright owner] 18 0 stevel * 19 0 stevel * CDDL HEADER END 20 0 stevel */ 21 0 stevel /* 22 8485 Peter * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 23 0 stevel * Use is subject to license terms. 24 0 stevel */ 25 0 stevel 26 0 stevel #include <stdio.h> 27 0 stevel #include <string.h> 28 8868 Peter #include <strings.h> 29 0 stevel #include <errno.h> 30 0 stevel #include <fcntl.h> 31 0 stevel #include <setjmp.h> 32 0 stevel #include <sys/types.h> 33 0 stevel #include <sys/signal.h> 34 0 stevel #include <sys/time.h> 35 0 stevel #include <sys/socket.h> 36 0 stevel #include <sys/sockio.h> 37 0 stevel #include <netinet/in.h> 38 0 stevel #include <netinet/ip.h> 39 0 stevel #include <sys/pfmod.h> 40 0 stevel #include <sys/mman.h> 41 0 stevel #include <sys/stat.h> 42 0 stevel #include <sys/bufmod.h> 43 0 stevel 44 0 stevel #include <unistd.h> 45 0 stevel #include <stropts.h> 46 0 stevel #include <stdlib.h> 47 0 stevel #include <ctype.h> 48 0 stevel #include <values.h> 49 0 stevel #include <libdlpi.h> 50 0 stevel 51 0 stevel #include "snoop.h" 52 0 stevel 53 1676 jpk /* 54 1676 jpk * Old header format. 55 1676 jpk * Actually two concatenated structs: nit_bufhdr + nit_head 56 1676 jpk */ 57 1676 jpk struct ohdr { 58 1676 jpk /* nit_bufhdr */ 59 1676 jpk int o_msglen; 60 1676 jpk int o_totlen; 61 1676 jpk /* nit_head */ 62 1676 jpk struct timeval o_time; 63 1676 jpk int o_drops; 64 1676 jpk int o_len; 65 1676 jpk }; 66 1676 jpk 67 1676 jpk static void scan(char *, int, int, int, int, void (*)(), int, int, int); 68 0 stevel void convert_to_network(); 69 0 stevel void convert_from_network(); 70 1676 jpk static void convert_old(struct ohdr *); 71 0 stevel extern sigjmp_buf jmp_env, ojmp_env; 72 1676 jpk static char *bufp; /* pointer to read buffer */ 73 0 stevel 74 1676 jpk static int strioctl(int, int, int, int, void *); 75 410 kcpoon 76 8868 Peter enum { DWA_NONE, DWA_EXISTS, DWA_PLUMBED }; 77 8868 Peter 78 8868 Peter typedef struct dlpi_walk_arg { 79 8868 Peter char dwa_linkname[MAXLINKNAMELEN]; 80 8868 Peter int dwa_type; /* preference type above */ 81 8868 Peter int dwa_s4; /* IPv4 socket */ 82 8868 Peter int dwa_s6; /* IPv6 socket */ 83 8868 Peter } dlpi_walk_arg_t; 84 8868 Peter 85 8868 Peter static boolean_t 86 8868 Peter select_datalink(const char *linkname, void *arg) 87 8868 Peter { 88 8868 Peter struct lifreq lifr; 89 8868 Peter dlpi_walk_arg_t *dwap = arg; 90 8868 Peter int s4 = dwap->dwa_s4; 91 8868 Peter int s6 = dwap->dwa_s6; 92 8868 Peter 93 8868 Peter (void) strlcpy(dwap->dwa_linkname, linkname, MAXLINKNAMELEN); 94 8868 Peter dwap->dwa_type = DWA_EXISTS; 95 8868 Peter 96 8868 Peter /* 97 8868 Peter * See if it's plumbed by IP. We prefer such links because they're 98 8868 Peter * more likely to have interesting traffic. 99 8868 Peter */ 100 8868 Peter bzero(&lifr, sizeof (lifr)); 101 8868 Peter (void) strlcpy(lifr.lifr_name, linkname, LIFNAMSIZ); 102 8868 Peter if ((s4 != -1 && ioctl(s4, SIOCGLIFFLAGS, &lifr) != -1) || 103 8868 Peter (s6 != -1 && ioctl(s6, SIOCGLIFFLAGS, &lifr) != -1)) { 104 8868 Peter dwap->dwa_type = DWA_PLUMBED; 105 8868 Peter return (B_TRUE); 106 8868 Peter } 107 8868 Peter return (B_FALSE); 108 8868 Peter } 109 8868 Peter 110 0 stevel /* 111 8868 Peter * Open `linkname' in raw/passive mode (see dlpi_open(3DLPI)). If `linkname' 112 8868 Peter * is NULL, pick a datalink as per snoop(1M). Also gather some information 113 8868 Peter * about the datalink useful for building the proper packet filters. 114 0 stevel */ 115 410 kcpoon boolean_t 116 8868 Peter open_datalink(dlpi_handle_t *dhp, const char *linkname) 117 0 stevel { 118 8868 Peter int retval; 119 8868 Peter int flags = DLPI_PASSIVE | DLPI_RAW; 120 8868 Peter dlpi_walk_arg_t dwa; 121 10689 Roamer dlpi_info_t dlinfo; 122 3628 ss150715 123 8868 Peter if (linkname == NULL) { 124 8868 Peter /* 125 8868 Peter * Select a datalink to use by default. Prefer datalinks that 126 8868 Peter * are plumbed by IP. 127 8868 Peter */ 128 8868 Peter bzero(&dwa, sizeof (dwa)); 129 8868 Peter dwa.dwa_s4 = socket(AF_INET, SOCK_DGRAM, 0); 130 8868 Peter dwa.dwa_s6 = socket(AF_INET6, SOCK_DGRAM, 0); 131 8868 Peter dlpi_walk(select_datalink, &dwa, 0); 132 8868 Peter (void) close(dwa.dwa_s4); 133 8868 Peter (void) close(dwa.dwa_s6); 134 0 stevel 135 8868 Peter if (dwa.dwa_type == DWA_NONE) 136 8868 Peter pr_err("no datalinks found"); 137 8868 Peter if (dwa.dwa_type == DWA_EXISTS) { 138 8868 Peter (void) fprintf(stderr, "snoop: WARNING: " 139 8868 Peter "no datalinks plumbed for IP traffic\n"); 140 0 stevel } 141 8868 Peter linkname = dwa.dwa_linkname; 142 0 stevel } 143 8023 Phil if (Iflg) 144 8023 Phil flags |= DLPI_DEVIPNET; 145 8868 Peter if (Iflg || strcmp(linkname, "lo0") == 0) 146 8023 Phil flags |= DLPI_IPNETINFO; 147 8868 Peter if ((retval = dlpi_open(linkname, dhp, flags)) != DLPI_SUCCESS) { 148 8868 Peter pr_err("cannot open \"%s\": %s", linkname, 149 3628 ss150715 dlpi_strerror(retval)); 150 0 stevel } 151 0 stevel 152 3628 ss150715 if ((retval = dlpi_info(*dhp, &dlinfo, 0)) != DLPI_SUCCESS) 153 3628 ss150715 pr_errdlpi(*dhp, "dlpi_info failed", retval); 154 0 stevel 155 0 stevel for (interface = &INTERFACES[0]; interface->mac_type != -1; interface++) 156 3628 ss150715 if (interface->mac_type == dlinfo.di_mactype) 157 0 stevel break; 158 0 stevel 159 3628 ss150715 /* allow limited functionality even if interface isn't known */ 160 0 stevel if (interface->mac_type == -1) { 161 3628 ss150715 (void) fprintf(stderr, "snoop: WARNING: Mac Type = %x " 162 3628 ss150715 "not supported\n", dlinfo.di_mactype); 163 0 stevel } 164 0 stevel 165 2760 dg199075 return (interface->try_kernel_filter); 166 0 stevel } 167 0 stevel 168 0 stevel /* 169 8868 Peter * Initialize `dh' for packet capture using the provided arguments. 170 0 stevel */ 171 0 stevel void 172 8868 Peter init_datalink(dlpi_handle_t dh, ulong_t snaplen, ulong_t chunksize, 173 3628 ss150715 struct timeval *timeout, struct Pf_ext_packetfilt *fp) 174 0 stevel { 175 3628 ss150715 int retv; 176 3628 ss150715 int netfd; 177 0 stevel 178 3628 ss150715 retv = dlpi_bind(dh, DLPI_ANY_SAP, NULL); 179 3628 ss150715 if (retv != DLPI_SUCCESS) 180 3628 ss150715 pr_errdlpi(dh, "cannot bind on", retv); 181 0 stevel 182 8023 Phil if (Iflg) { 183 8023 Phil (void) fprintf(stderr, "Using device ipnet/%s ", 184 8023 Phil dlpi_linkname(dh)); 185 8023 Phil } else { 186 8023 Phil (void) fprintf(stderr, "Using device %s ", dlpi_linkname(dh)); 187 8023 Phil } 188 0 stevel 189 0 stevel /* 190 0 stevel * If Pflg not set - use physical level 191 0 stevel * promiscuous mode. Otherwise - just SAP level. 192 0 stevel */ 193 0 stevel if (!Pflg) { 194 0 stevel (void) fprintf(stderr, "(promiscuous mode)\n"); 195 3628 ss150715 retv = dlpi_promiscon(dh, DL_PROMISC_PHYS); 196 3628 ss150715 if (retv != DLPI_SUCCESS) { 197 3628 ss150715 pr_errdlpi(dh, "promiscuous mode(physical) failed", 198 3628 ss150715 retv); 199 3628 ss150715 } 200 0 stevel } else { 201 0 stevel (void) fprintf(stderr, "(non promiscuous)\n"); 202 3628 ss150715 retv = dlpi_promiscon(dh, DL_PROMISC_MULTI); 203 3628 ss150715 if (retv != DLPI_SUCCESS) { 204 3628 ss150715 pr_errdlpi(dh, "promiscuous mode(multicast) failed", 205 3628 ss150715 retv); 206 3628 ss150715 } 207 0 stevel } 208 0 stevel 209 3628 ss150715 retv = dlpi_promiscon(dh, DL_PROMISC_SAP); 210 3628 ss150715 if (retv != DLPI_SUCCESS) 211 3628 ss150715 pr_errdlpi(dh, "promiscuous mode(SAP) failed", retv); 212 0 stevel 213 3628 ss150715 netfd = dlpi_fd(dh); 214 0 stevel 215 0 stevel if (fp) { 216 0 stevel /* 217 0 stevel * push and configure the packet filtering module 218 0 stevel */ 219 3628 ss150715 if (ioctl(netfd, I_PUSH, "pfmod") < 0) 220 3628 ss150715 pr_errdlpi(dh, "cannot push \"pfmod\"", DL_SYSERR); 221 0 stevel 222 410 kcpoon if (strioctl(netfd, PFIOCSETF, -1, sizeof (*fp), 223 3628 ss150715 (char *)fp) < 0) 224 3628 ss150715 pr_errdlpi(dh, "PFIOCSETF", DL_SYSERR); 225 0 stevel } 226 0 stevel 227 3628 ss150715 if (ioctl(netfd, I_PUSH, "bufmod") < 0) 228 3628 ss150715 pr_errdlpi(dh, "cannot push \"bufmod\"", DL_SYSERR); 229 0 stevel 230 0 stevel if (strioctl(netfd, SBIOCSTIME, -1, sizeof (struct timeval), 231 3628 ss150715 (char *)timeout) < 0) 232 3628 ss150715 pr_errdlpi(dh, "SBIOCSTIME", DL_SYSERR); 233 0 stevel 234 0 stevel if (strioctl(netfd, SBIOCSCHUNK, -1, sizeof (uint_t), 235 3628 ss150715 (char *)&chunksize) < 0) 236 3628 ss150715 pr_errdlpi(dh, "SBIOCGCHUNK", DL_SYSERR); 237 0 stevel 238 0 stevel if (strioctl(netfd, SBIOCSSNAP, -1, sizeof (uint_t), 239 3628 ss150715 (char *)&snaplen) < 0) 240 3628 ss150715 pr_errdlpi(dh, "SBIOCSSNAP", DL_SYSERR); 241 0 stevel 242 0 stevel /* 243 0 stevel * Flush the read queue, to get rid of anything that 244 0 stevel * accumulated before the device reached its final configuration. 245 0 stevel */ 246 3628 ss150715 if (ioctl(netfd, I_FLUSH, FLUSHR) < 0) 247 3628 ss150715 pr_errdlpi(dh, "cannot flush \"I_FLUSH\"", DL_SYSERR); 248 0 stevel } 249 0 stevel 250 0 stevel /* 251 8868 Peter * Read packets from the network. init_datalink() is called in 252 0 stevel * here to set up the network interface for reading of 253 0 stevel * raw ethernet packets in promiscuous mode into a buffer. 254 0 stevel * Packets are read and either written directly to a file 255 0 stevel * or interpreted for display on the fly. 256 0 stevel */ 257 0 stevel void 258 3628 ss150715 net_read(dlpi_handle_t dh, size_t chunksize, int filter, void (*proc)(), 259 3628 ss150715 int flags) 260 0 stevel { 261 3628 ss150715 int retval; 262 0 stevel extern int count; 263 3628 ss150715 size_t msglen; 264 0 stevel 265 0 stevel count = 0; 266 0 stevel 267 0 stevel /* allocate a read buffer */ 268 0 stevel bufp = malloc(chunksize); 269 0 stevel if (bufp == NULL) 270 3628 ss150715 pr_err("no memory for %d buffer", chunksize); 271 0 stevel 272 0 stevel /* 273 3628 ss150715 * read frames 274 0 stevel */ 275 0 stevel for (;;) { 276 3628 ss150715 msglen = chunksize; 277 3628 ss150715 retval = dlpi_recv(dh, NULL, NULL, bufp, &msglen, -1, NULL); 278 0 stevel 279 3628 ss150715 if (retval != DLPI_SUCCESS || quitting) 280 0 stevel break; 281 0 stevel 282 3628 ss150715 if (msglen != 0) 283 3628 ss150715 scan(bufp, msglen, filter, 0, 0, proc, 0, 0, flags); 284 0 stevel } 285 0 stevel 286 0 stevel free(bufp); 287 0 stevel 288 3628 ss150715 if (!quitting) 289 3628 ss150715 pr_errdlpi(dh, "network read failed", retval); 290 0 stevel } 291 0 stevel 292 0 stevel #ifdef DEBUG 293 0 stevel /* 294 0 stevel * corrupt: simulate packet corruption for debugging interpreters 295 0 stevel */ 296 0 stevel void 297 0 stevel corrupt(volatile char *pktp, volatile char *pstop, char *buf, 298 0 stevel volatile char *bufstop) 299 0 stevel { 300 0 stevel int c; 301 0 stevel int i; 302 0 stevel int p; 303 0 stevel int li = rand() % (pstop - pktp - 1) + 1; 304 0 stevel volatile char *pp = pktp; 305 0 stevel volatile char *pe = bufstop < pstop ? bufstop : pstop; 306 0 stevel 307 0 stevel if (pktp < buf || pktp > bufstop) 308 0 stevel return; 309 0 stevel 310 0 stevel for (pp = pktp; pp < pe; pp += li) { 311 0 stevel c = ((pe - pp) < li ? pe - pp : li); 312 0 stevel i = (rand() % c)>>1; 313 0 stevel while (--i > 0) { 314 0 stevel p = (rand() % c); 315 0 stevel pp[p] = (unsigned char)(rand() & 0xFF); 316 0 stevel } 317 0 stevel } 318 0 stevel } 319 0 stevel #endif /* DEBUG */ 320 0 stevel 321 1676 jpk static void 322 1676 jpk scan(char *buf, int len, int filter, int cap, int old, void (*proc)(), 323 1676 jpk int first, int last, int flags) 324 0 stevel { 325 0 stevel volatile char *bp, *bufstop; 326 0 stevel volatile struct sb_hdr *hdrp; 327 0 stevel volatile struct sb_hdr nhdr, *nhdrp; 328 0 stevel volatile char *pktp; 329 0 stevel volatile struct timeval last_timestamp; 330 0 stevel volatile int header_okay; 331 0 stevel extern int count, maxcount; 332 0 stevel extern int snoop_nrecover; 333 0 stevel #ifdef DEBUG 334 0 stevel extern int zflg; 335 0 stevel #endif /* DEBUG */ 336 0 stevel 337 0 stevel proc(0, 0, 0); 338 0 stevel bufstop = buf + len; 339 0 stevel 340 0 stevel /* 341 0 stevel * 342 0 stevel * Loop through each packet in the buffer 343 0 stevel */ 344 0 stevel last_timestamp.tv_sec = 0; 345 0 stevel (void) memcpy((char *)ojmp_env, (char *)jmp_env, sizeof (jmp_env)); 346 0 stevel for (bp = buf; bp < bufstop; bp += nhdrp->sbh_totlen) { 347 0 stevel /* 348 0 stevel * Gracefully exit if user terminates 349 0 stevel */ 350 0 stevel if (quitting) 351 0 stevel break; 352 0 stevel /* 353 0 stevel * Global error recocery: Prepare to continue when a corrupt 354 0 stevel * packet or header is encountered. 355 0 stevel */ 356 0 stevel if (sigsetjmp(jmp_env, 1)) { 357 0 stevel goto err; 358 0 stevel } 359 0 stevel 360 0 stevel header_okay = 0; 361 0 stevel hdrp = (struct sb_hdr *)bp; 362 0 stevel nhdrp = hdrp; 363 0 stevel pktp = (char *)hdrp + sizeof (*hdrp); 364 0 stevel 365 0 stevel /* 366 0 stevel * If reading a capture file 367 0 stevel * convert the headers from network 368 0 stevel * byte order (for little-endians like X86) 369 0 stevel */ 370 0 stevel if (cap) { 371 0 stevel /* 372 0 stevel * If the packets come from an old 373 0 stevel * capture file, convert the header. 374 0 stevel */ 375 0 stevel if (old) { 376 1676 jpk convert_old((struct ohdr *)hdrp); 377 0 stevel } 378 0 stevel 379 0 stevel nhdrp = &nhdr; 380 0 stevel 381 0 stevel nhdrp->sbh_origlen = ntohl(hdrp->sbh_origlen); 382 0 stevel nhdrp->sbh_msglen = ntohl(hdrp->sbh_msglen); 383 0 stevel nhdrp->sbh_totlen = ntohl(hdrp->sbh_totlen); 384 0 stevel nhdrp->sbh_drops = ntohl(hdrp->sbh_drops); 385 0 stevel nhdrp->sbh_timestamp.tv_sec = 386 8023 Phil ntohl(hdrp->sbh_timestamp.tv_sec); 387 0 stevel nhdrp->sbh_timestamp.tv_usec = 388 8023 Phil ntohl(hdrp->sbh_timestamp.tv_usec); 389 0 stevel } 390 0 stevel 391 0 stevel /* Enhanced check for valid header */ 392 0 stevel 393 0 stevel if ((nhdrp->sbh_totlen == 0) || 394 0 stevel (bp + nhdrp->sbh_totlen) < bp || 395 0 stevel (bp + nhdrp->sbh_totlen) > bufstop || 396 0 stevel (nhdrp->sbh_origlen == 0) || 397 0 stevel (bp + nhdrp->sbh_origlen) < bp || 398 0 stevel (nhdrp->sbh_msglen == 0) || 399 0 stevel (bp + nhdrp->sbh_msglen) < bp || 400 0 stevel (bp + nhdrp->sbh_msglen) > bufstop || 401 0 stevel (nhdrp->sbh_msglen > nhdrp->sbh_origlen) || 402 0 stevel (nhdrp->sbh_totlen < nhdrp->sbh_msglen) || 403 0 stevel (nhdrp->sbh_timestamp.tv_sec == 0)) { 404 8023 Phil if (cap) { 405 3628 ss150715 (void) fprintf(stderr, "(warning) bad packet " 406 3628 ss150715 "header in capture file"); 407 8023 Phil } else { 408 3628 ss150715 (void) fprintf(stderr, "(warning) bad packet " 409 3628 ss150715 "header in buffer"); 410 8023 Phil } 411 0 stevel (void) fprintf(stderr, " offset %d: length=%d\n", 412 8023 Phil bp - buf, nhdrp->sbh_totlen); 413 0 stevel goto err; 414 0 stevel } 415 0 stevel 416 0 stevel /* 417 0 stevel * Check for incomplete packet. We are conservative here, 418 0 stevel * since we don't know how good the checking is in other 419 0 stevel * parts of the code. We pass a partial packet, with 420 0 stevel * a warning. 421 0 stevel */ 422 0 stevel if (pktp + nhdrp->sbh_msglen > bufstop) { 423 3628 ss150715 (void) fprintf(stderr, "truncated packet buffer\n"); 424 0 stevel nhdrp->sbh_msglen = bufstop - pktp; 425 0 stevel } 426 0 stevel 427 0 stevel #ifdef DEBUG 428 0 stevel if (zflg) 429 0 stevel corrupt(pktp, pktp + nhdrp->sbh_msglen, buf, bufstop); 430 0 stevel #endif /* DEBUG */ 431 0 stevel 432 0 stevel header_okay = 1; 433 0 stevel if (!filter || 434 10616 Sebastien want_packet((uchar_t *)pktp, 435 8023 Phil nhdrp->sbh_msglen, 436 8023 Phil nhdrp->sbh_origlen)) { 437 0 stevel count++; 438 0 stevel 439 0 stevel /* 440 0 stevel * Start deadman timer for interpreter processing 441 0 stevel */ 442 0 stevel (void) snoop_alarm(SNOOP_ALARM_GRAN*SNOOP_MAXRECOVER, 443 8023 Phil NULL); 444 0 stevel 445 0 stevel encap_levels = 0; 446 0 stevel if (!cap || count >= first) 447 0 stevel proc(nhdrp, pktp, count, flags); 448 0 stevel 449 0 stevel if (cap && count >= last) { 450 0 stevel (void) snoop_alarm(0, NULL); 451 0 stevel break; 452 0 stevel } 453 0 stevel 454 0 stevel if (maxcount && count >= maxcount) { 455 3628 ss150715 (void) fprintf(stderr, "%d packets captured\n", 456 3628 ss150715 count); 457 0 stevel exit(0); 458 0 stevel } 459 0 stevel 460 0 stevel snoop_nrecover = 0; /* success */ 461 0 stevel (void) snoop_alarm(0, NULL); 462 0 stevel last_timestamp = hdrp->sbh_timestamp; /* save stamp */ 463 0 stevel } 464 0 stevel continue; 465 0 stevel err: 466 0 stevel /* 467 0 stevel * Corruption has been detected. Reset errors. 468 0 stevel */ 469 0 stevel snoop_recover(); 470 0 stevel 471 0 stevel /* 472 0 stevel * packet header was apparently okay. Continue. 473 0 stevel */ 474 0 stevel if (header_okay) 475 0 stevel continue; 476 0 stevel 477 0 stevel /* 478 0 stevel * Otherwise try to scan forward to the next packet, using 479 0 stevel * the last known timestamp if it is available. 480 0 stevel */ 481 0 stevel nhdrp = &nhdr; 482 0 stevel nhdrp->sbh_totlen = 0; 483 0 stevel if (last_timestamp.tv_sec == 0) { 484 0 stevel bp += sizeof (int); 485 0 stevel } else { 486 0 stevel for (bp += sizeof (int); bp <= bufstop; 487 8023 Phil bp += sizeof (int)) { 488 0 stevel hdrp = (struct sb_hdr *)bp; 489 0 stevel /* An approximate timestamp located */ 490 0 stevel if ((hdrp->sbh_timestamp.tv_sec >> 8) == 491 0 stevel (last_timestamp.tv_sec >> 8)) 492 0 stevel break; 493 0 stevel } 494 0 stevel } 495 0 stevel } 496 0 stevel /* reset jmp_env for program exit */ 497 0 stevel (void) memcpy((char *)jmp_env, (char *)ojmp_env, sizeof (jmp_env)); 498 0 stevel proc(0, -1, 0); 499 0 stevel } 500 0 stevel 501 0 stevel /* 502 0 stevel * Called if nwrite() encounters write problems. 503 0 stevel */ 504 0 stevel static void 505 0 stevel cap_write_error(const char *msgtype) 506 0 stevel { 507 0 stevel (void) fprintf(stderr, 508 8023 Phil "snoop: cannot write %s to capture file: %s\n", 509 8023 Phil msgtype, strerror(errno)); 510 0 stevel exit(1); 511 0 stevel } 512 0 stevel 513 0 stevel /* 514 0 stevel * Writes target buffer to the open file descriptor. Upon detection of a short 515 0 stevel * write, an attempt to process the remaining bytes occurs until all anticipated 516 0 stevel * bytes are written. An error status is returned to indicate any serious write 517 0 stevel * failures. 518 0 stevel */ 519 0 stevel static int 520 0 stevel nwrite(int fd, const void *buffer, size_t buflen) 521 0 stevel { 522 0 stevel size_t nwritten; 523 0 stevel ssize_t nbytes = 0; 524 0 stevel const char *buf = buffer; 525 0 stevel 526 0 stevel for (nwritten = 0; nwritten < buflen; nwritten += nbytes) { 527 0 stevel nbytes = write(fd, &buf[nwritten], buflen - nwritten); 528 0 stevel if (nbytes == -1) 529 0 stevel return (-1); 530 0 stevel if (nbytes == 0) { 531 0 stevel errno = EIO; 532 0 stevel return (-1); 533 0 stevel } 534 0 stevel } 535 0 stevel return (0); 536 0 stevel } 537 0 stevel 538 0 stevel /* 539 0 stevel * Routines for opening, closing, reading and writing 540 0 stevel * a capture file of packets saved with the -o option. 541 0 stevel */ 542 0 stevel static int capfile_out; 543 0 stevel 544 0 stevel /* 545 0 stevel * The snoop capture file has a header to identify 546 0 stevel * it as a capture file and record its version. 547 0 stevel * A file without this header is assumed to be an 548 0 stevel * old format snoop file. 549 0 stevel * 550 0 stevel * A version 1 header looks like this: 551 0 stevel * 552 0 stevel * 0 1 2 3 4 5 6 7 8 9 10 11 553 0 stevel * +---+---+---+---+---+---+---+---+---+---+---+---+---+ 554 0 stevel * | s | n | o | o | p | \0| \0| \0| version | data 555 0 stevel * +---+---+---+---+---+---+---+---+---+---+---+---+---+ 556 0 stevel * | word 0 | word 1 | word 2 | 557 0 stevel * 558 0 stevel * 559 0 stevel * A version 2 header adds a word that identifies the MAC type. 560 0 stevel * This allows for capture files from FDDI etc. 561 0 stevel * 562 0 stevel * 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 563 0 stevel * +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 564 0 stevel * | s | n | o | o | p | \0| \0| \0| version | MAC type | data 565 0 stevel * +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 566 0 stevel * | word 0 | word 1 | word 2 | word 3 567 0 stevel * 568 0 stevel */ 569 1676 jpk static const char *snoop_id = "snoop\0\0\0"; 570 1676 jpk static const int snoop_idlen = 8; 571 1676 jpk static const int snoop_version = 2; 572 0 stevel 573 0 stevel void 574 3628 ss150715 cap_open_write(const char *name) 575 0 stevel { 576 0 stevel int vers; 577 0 stevel 578 0 stevel capfile_out = open(name, O_CREAT | O_TRUNC | O_RDWR, 0666); 579 0 stevel if (capfile_out < 0) 580 0 stevel pr_err("%s: %m", name); 581 0 stevel 582 0 stevel vers = htonl(snoop_version); 583 1676 jpk if (nwrite(capfile_out, snoop_id, snoop_idlen) == -1) 584 0 stevel cap_write_error("snoop_id"); 585 0 stevel 586 1676 jpk if (nwrite(capfile_out, &vers, sizeof (int)) == -1) 587 0 stevel cap_write_error("version"); 588 0 stevel } 589 0 stevel 590 0 stevel 591 0 stevel void 592 1676 jpk cap_close(void) 593 0 stevel { 594 1676 jpk (void) close(capfile_out); 595 0 stevel } 596 0 stevel 597 0 stevel static char *cap_buffp = NULL; 598 0 stevel static int cap_len = 0; 599 0 stevel static int cap_new; 600 0 stevel 601 0 stevel void 602 3628 ss150715 cap_open_read(const char *name) 603 0 stevel { 604 0 stevel struct stat st; 605 0 stevel int cap_vers; 606 0 stevel int *word, device_mac_type; 607 0 stevel int capfile_in; 608 0 stevel 609 0 stevel capfile_in = open(name, O_RDONLY); 610 0 stevel if (capfile_in < 0) 611 0 stevel pr_err("couldn't open %s: %m", name); 612 0 stevel 613 0 stevel if (fstat(capfile_in, &st) < 0) 614 0 stevel pr_err("couldn't stat %s: %m", name); 615 0 stevel cap_len = st.st_size; 616 0 stevel 617 0 stevel cap_buffp = mmap(0, cap_len, PROT_READ, MAP_PRIVATE, capfile_in, 0); 618 1676 jpk (void) close(capfile_in); 619 0 stevel if ((int)cap_buffp == -1) 620 0 stevel pr_err("couldn't mmap %s: %m", name); 621 0 stevel 622 0 stevel /* Check if new snoop capture file format */ 623 0 stevel 624 0 stevel cap_new = bcmp(cap_buffp, snoop_id, snoop_idlen) == 0; 625 0 stevel 626 0 stevel /* 627 0 stevel * If new file - check version and 628 0 stevel * set buffer pointer to point at first packet 629 0 stevel */ 630 0 stevel if (cap_new) { 631 0 stevel cap_vers = ntohl(*(int *)(cap_buffp + snoop_idlen)); 632 0 stevel cap_buffp += snoop_idlen + sizeof (int); 633 0 stevel cap_len -= snoop_idlen + sizeof (int); 634 0 stevel 635 0 stevel switch (cap_vers) { 636 0 stevel case 1: 637 0 stevel device_mac_type = DL_ETHER; 638 0 stevel break; 639 0 stevel 640 0 stevel case 2: 641 0 stevel device_mac_type = ntohl(*((int *)cap_buffp)); 642 0 stevel cap_buffp += sizeof (int); 643 0 stevel cap_len -= sizeof (int); 644 0 stevel break; 645 0 stevel 646 0 stevel default: 647 0 stevel pr_err("capture file: %s: Version %d unrecognized\n", 648 8023 Phil name, cap_vers); 649 0 stevel } 650 0 stevel 651 0 stevel for (interface = &INTERFACES[0]; interface->mac_type != -1; 652 8023 Phil interface++) 653 0 stevel if (interface->mac_type == device_mac_type) 654 0 stevel break; 655 0 stevel 656 0 stevel if (interface->mac_type == -1) 657 0 stevel pr_err("Mac Type = %x is not supported\n", 658 8023 Phil device_mac_type); 659 0 stevel } else { 660 0 stevel /* Use heuristic to check if it's an old-style file */ 661 0 stevel 662 0 stevel device_mac_type = DL_ETHER; 663 0 stevel word = (int *)cap_buffp; 664 0 stevel 665 0 stevel if (!((word[0] < 1600 && word[1] < 1600) && 666 0 stevel (word[0] < word[1]) && 667 0 stevel (word[2] > 610000000 && word[2] < 770000000))) 668 0 stevel pr_err("not a capture file: %s", name); 669 0 stevel 670 0 stevel /* Change protection so's we can fix the headers */ 671 0 stevel 672 0 stevel if (mprotect(cap_buffp, cap_len, PROT_READ | PROT_WRITE) < 0) 673 0 stevel pr_err("mprotect: %s: %m", name); 674 0 stevel } 675 0 stevel } 676 0 stevel 677 0 stevel void 678 3628 ss150715 cap_read(int first, int last, int filter, void (*proc)(), int flags) 679 0 stevel { 680 0 stevel extern int count; 681 0 stevel 682 0 stevel count = 0; 683 0 stevel 684 0 stevel scan(cap_buffp, cap_len, filter, 1, !cap_new, proc, first, last, flags); 685 0 stevel 686 1676 jpk (void) munmap(cap_buffp, cap_len); 687 0 stevel } 688 0 stevel 689 1676 jpk /* ARGSUSED */ 690 0 stevel void 691 3628 ss150715 cap_write(struct sb_hdr *hdrp, char *pktp, int num, int flags) 692 0 stevel { 693 0 stevel int pktlen, mac; 694 0 stevel static int first = 1; 695 0 stevel struct sb_hdr nhdr; 696 0 stevel extern boolean_t qflg; 697 0 stevel 698 0 stevel if (hdrp == NULL) 699 0 stevel return; 700 0 stevel 701 0 stevel if (first) { 702 0 stevel first = 0; 703 0 stevel mac = htonl(interface->mac_type); 704 1676 jpk if (nwrite(capfile_out, &mac, sizeof (int)) == -1) 705 0 stevel cap_write_error("mac_type"); 706 0 stevel } 707 0 stevel 708 0 stevel pktlen = hdrp->sbh_totlen - sizeof (*hdrp); 709 0 stevel 710 0 stevel /* 711 0 stevel * Convert sb_hdr to network byte order 712 0 stevel */ 713 0 stevel nhdr.sbh_origlen = htonl(hdrp->sbh_origlen); 714 0 stevel nhdr.sbh_msglen = htonl(hdrp->sbh_msglen); 715 0 stevel nhdr.sbh_totlen = htonl(hdrp->sbh_totlen); 716 0 stevel nhdr.sbh_drops = htonl(hdrp->sbh_drops); 717 0 stevel nhdr.sbh_timestamp.tv_sec = htonl(hdrp->sbh_timestamp.tv_sec); 718 0 stevel nhdr.sbh_timestamp.tv_usec = htonl(hdrp->sbh_timestamp.tv_usec); 719 0 stevel 720 1676 jpk if (nwrite(capfile_out, &nhdr, sizeof (nhdr)) == -1) 721 0 stevel cap_write_error("packet header"); 722 0 stevel 723 1676 jpk if (nwrite(capfile_out, pktp, pktlen) == -1) 724 0 stevel cap_write_error("packet"); 725 0 stevel 726 0 stevel if (! qflg) 727 0 stevel show_count(); 728 0 stevel } 729 0 stevel 730 0 stevel /* 731 0 stevel * Convert a packet header from 732 0 stevel * old to new format. 733 0 stevel */ 734 1676 jpk static void 735 1676 jpk convert_old(struct ohdr *ohdrp) 736 0 stevel { 737 0 stevel struct sb_hdr nhdr; 738 0 stevel 739 0 stevel nhdr.sbh_origlen = ohdrp->o_len; 740 0 stevel nhdr.sbh_msglen = ohdrp->o_msglen; 741 0 stevel nhdr.sbh_totlen = ohdrp->o_totlen; 742 0 stevel nhdr.sbh_drops = ohdrp->o_drops; 743 0 stevel nhdr.sbh_timestamp = ohdrp->o_time; 744 0 stevel 745 0 stevel *(struct sb_hdr *)ohdrp = nhdr; 746 0 stevel } 747 0 stevel 748 410 kcpoon static int 749 1676 jpk strioctl(int fd, int cmd, int timout, int len, void *dp) 750 0 stevel { 751 0 stevel struct strioctl sioc; 752 0 stevel int rc; 753 0 stevel 754 0 stevel sioc.ic_cmd = cmd; 755 0 stevel sioc.ic_timout = timout; 756 0 stevel sioc.ic_len = len; 757 0 stevel sioc.ic_dp = dp; 758 0 stevel rc = ioctl(fd, I_STR, &sioc); 759 0 stevel 760 0 stevel if (rc < 0) 761 0 stevel return (rc); 762 0 stevel else 763 0 stevel return (sioc.ic_len); 764 0 stevel } 765