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