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