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.
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.
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]
22 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
24 * Copyright 2012 Milan Jurik. All rights reserved.
34 #include <sys/types.h>
35 #include <sys/signal.h>
37 #include <sys/socket.h>
38 #include <sys/sockio.h>
39 #include <netinet/in.h>
40 #include <netinet/ip.h>
41 #include <sys/pfmod.h>
44 #include <sys/bufmod.h>
57 * Actually two concatenated structs: nit_bufhdr + nit_head
64 struct timeval o_time
;
69 static void scan(char *, int, int, int, int, void (*)(), int, int, int);
70 void convert_to_network();
71 void convert_from_network();
72 static void convert_old(struct ohdr
*);
73 extern sigjmp_buf jmp_env
, ojmp_env
;
74 static char *bufp
; /* pointer to read buffer */
76 static int strioctl(int, int, int, int, void *);
78 enum { DWA_NONE
, DWA_EXISTS
, DWA_PLUMBED
};
80 typedef struct dlpi_walk_arg
{
81 char dwa_linkname
[MAXLINKNAMELEN
];
82 int dwa_type
; /* preference type above */
83 int dwa_s4
; /* IPv4 socket */
84 int dwa_s6
; /* IPv6 socket */
88 select_datalink(const char *linkname
, void *arg
)
91 dlpi_walk_arg_t
*dwap
= arg
;
92 int s4
= dwap
->dwa_s4
;
93 int s6
= dwap
->dwa_s6
;
95 (void) strlcpy(dwap
->dwa_linkname
, linkname
, MAXLINKNAMELEN
);
96 dwap
->dwa_type
= DWA_EXISTS
;
99 * See if it's plumbed by IP. We prefer such links because they're
100 * more likely to have interesting traffic.
102 bzero(&lifr
, sizeof (lifr
));
103 (void) strlcpy(lifr
.lifr_name
, linkname
, LIFNAMSIZ
);
104 if ((s4
!= -1 && ioctl(s4
, SIOCGLIFFLAGS
, &lifr
) != -1) ||
105 (s6
!= -1 && ioctl(s6
, SIOCGLIFFLAGS
, &lifr
) != -1)) {
106 dwap
->dwa_type
= DWA_PLUMBED
;
113 * Open `linkname' in raw/passive mode (see dlpi_open(3DLPI)). If `linkname'
114 * is NULL, pick a datalink as per snoop(1M). Also gather some information
115 * about the datalink useful for building the proper packet filters.
118 open_datalink(dlpi_handle_t
*dhp
, const char *linkname
)
121 int flags
= DLPI_PASSIVE
| DLPI_RAW
;
125 if (linkname
== NULL
) {
127 * Select a datalink to use by default. Prefer datalinks that
130 bzero(&dwa
, sizeof (dwa
));
131 dwa
.dwa_s4
= socket(AF_INET
, SOCK_DGRAM
, 0);
132 dwa
.dwa_s6
= socket(AF_INET6
, SOCK_DGRAM
, 0);
133 dlpi_walk(select_datalink
, &dwa
, 0);
134 (void) close(dwa
.dwa_s4
);
135 (void) close(dwa
.dwa_s6
);
137 if (dwa
.dwa_type
== DWA_NONE
)
138 pr_err("no datalinks found");
139 if (dwa
.dwa_type
== DWA_EXISTS
) {
140 (void) fprintf(stderr
, "snoop: WARNING: "
141 "no datalinks plumbed for IP traffic\n");
143 linkname
= dwa
.dwa_linkname
;
146 flags
|= DLPI_DEVIPNET
;
147 if (Iflg
|| strcmp(linkname
, "lo0") == 0)
148 flags
|= DLPI_IPNETINFO
;
149 if ((retval
= dlpi_open(linkname
, dhp
, flags
)) != DLPI_SUCCESS
) {
150 pr_err("cannot open \"%s\": %s", linkname
,
151 dlpi_strerror(retval
));
154 if ((retval
= dlpi_info(*dhp
, &dlinfo
, 0)) != DLPI_SUCCESS
)
155 pr_errdlpi(*dhp
, "dlpi_info failed", retval
);
157 for (interface
= &INTERFACES
[0]; interface
->mac_type
!= -1; interface
++)
158 if (interface
->mac_type
== dlinfo
.di_mactype
)
161 /* allow limited functionality even if interface isn't known */
162 if (interface
->mac_type
== -1) {
163 (void) fprintf(stderr
, "snoop: WARNING: Mac Type = %x "
164 "not supported\n", dlinfo
.di_mactype
);
167 return (interface
->try_kernel_filter
);
171 * Initialize `dh' for packet capture using the provided arguments.
174 init_datalink(dlpi_handle_t dh
, ulong_t snaplen
, ulong_t chunksize
,
175 struct timeval
*timeout
, struct Pf_ext_packetfilt
*fp
)
180 retv
= dlpi_bind(dh
, DLPI_ANY_SAP
, NULL
);
181 if (retv
!= DLPI_SUCCESS
)
182 pr_errdlpi(dh
, "cannot bind on", retv
);
185 (void) fprintf(stderr
, "Using device ipnet/%s ",
188 (void) fprintf(stderr
, "Using device %s ", dlpi_linkname(dh
));
192 * If Pflg not set - use physical level
193 * promiscuous mode. Otherwise - just SAP level.
196 (void) fprintf(stderr
, "(promiscuous mode)\n");
197 retv
= dlpi_promiscon(dh
, DL_PROMISC_PHYS
);
198 if (retv
!= DLPI_SUCCESS
) {
199 pr_errdlpi(dh
, "promiscuous mode(physical) failed",
203 (void) fprintf(stderr
, "(non promiscuous)\n");
204 retv
= dlpi_promiscon(dh
, DL_PROMISC_MULTI
);
205 if (retv
!= DLPI_SUCCESS
) {
206 pr_errdlpi(dh
, "promiscuous mode(multicast) failed",
211 retv
= dlpi_promiscon(dh
, DL_PROMISC_SAP
);
212 if (retv
!= DLPI_SUCCESS
)
213 pr_errdlpi(dh
, "promiscuous mode(SAP) failed", retv
);
219 * push and configure the packet filtering module
221 if (ioctl(netfd
, I_PUSH
, "pfmod") < 0)
222 pr_errdlpi(dh
, "cannot push \"pfmod\"", DL_SYSERR
);
224 if (strioctl(netfd
, PFIOCSETF
, -1, sizeof (*fp
),
226 pr_errdlpi(dh
, "PFIOCSETF", DL_SYSERR
);
229 if (ioctl(netfd
, I_PUSH
, "bufmod") < 0)
230 pr_errdlpi(dh
, "cannot push \"bufmod\"", DL_SYSERR
);
232 if (strioctl(netfd
, SBIOCSTIME
, -1, sizeof (struct timeval
),
233 (char *)timeout
) < 0)
234 pr_errdlpi(dh
, "SBIOCSTIME", DL_SYSERR
);
236 if (strioctl(netfd
, SBIOCSCHUNK
, -1, sizeof (uint_t
),
237 (char *)&chunksize
) < 0)
238 pr_errdlpi(dh
, "SBIOCGCHUNK", DL_SYSERR
);
240 if (strioctl(netfd
, SBIOCSSNAP
, -1, sizeof (uint_t
),
241 (char *)&snaplen
) < 0)
242 pr_errdlpi(dh
, "SBIOCSSNAP", DL_SYSERR
);
245 * Flush the read queue, to get rid of anything that
246 * accumulated before the device reached its final configuration.
248 if (ioctl(netfd
, I_FLUSH
, FLUSHR
) < 0)
249 pr_errdlpi(dh
, "cannot flush \"I_FLUSH\"", DL_SYSERR
);
253 * Read packets from the network. init_datalink() is called in
254 * here to set up the network interface for reading of
255 * raw ethernet packets in promiscuous mode into a buffer.
256 * Packets are read and either written directly to a file
257 * or interpreted for display on the fly.
260 net_read(dlpi_handle_t dh
, size_t chunksize
, int filter
, void (*proc
)(),
269 /* allocate a read buffer */
270 bufp
= malloc(chunksize
);
272 pr_err("no memory for %d buffer", chunksize
);
279 retval
= dlpi_recv(dh
, NULL
, NULL
, bufp
, &msglen
, -1, NULL
);
281 if (retval
!= DLPI_SUCCESS
|| quitting
)
285 scan(bufp
, msglen
, filter
, 0, 0, proc
, 0, 0, flags
);
291 pr_errdlpi(dh
, "network read failed", retval
);
296 * corrupt: simulate packet corruption for debugging interpreters
299 corrupt(volatile char *pktp
, volatile char *pstop
, char *buf
,
300 volatile char *bufstop
)
305 int li
= rand() % (pstop
- pktp
- 1) + 1;
306 volatile char *pp
= pktp
;
307 volatile char *pe
= bufstop
< pstop
? bufstop
: pstop
;
309 if (pktp
< buf
|| pktp
> bufstop
)
312 for (pp
= pktp
; pp
< pe
; pp
+= li
) {
313 c
= ((pe
- pp
) < li
? pe
- pp
: li
);
317 pp
[p
] = (unsigned char)(rand() & 0xFF);
324 scan(char *buf
, int len
, int filter
, int cap
, int old
, void (*proc
)(),
325 int first
, int last
, int flags
)
327 volatile char *bp
, *bufstop
;
328 volatile struct sb_hdr
*hdrp
;
329 volatile struct sb_hdr nhdr
, *nhdrp
;
331 volatile struct timeval last_timestamp
;
332 volatile int header_okay
;
333 extern int count
, maxcount
;
334 extern int snoop_nrecover
;
344 * Loop through each packet in the buffer
346 last_timestamp
.tv_sec
= 0;
347 (void) memcpy((char *)ojmp_env
, (char *)jmp_env
, sizeof (jmp_env
));
348 for (bp
= buf
; bp
< bufstop
; bp
+= nhdrp
->sbh_totlen
) {
350 * Gracefully exit if user terminates
355 * Global error recocery: Prepare to continue when a corrupt
356 * packet or header is encountered.
358 if (sigsetjmp(jmp_env
, 1)) {
363 hdrp
= (struct sb_hdr
*)bp
;
365 pktp
= (char *)hdrp
+ sizeof (*hdrp
);
368 * If reading a capture file
369 * convert the headers from network
370 * byte order (for little-endians like X86)
374 * If the packets come from an old
375 * capture file, convert the header.
378 convert_old((struct ohdr
*)hdrp
);
383 nhdrp
->sbh_origlen
= ntohl(hdrp
->sbh_origlen
);
384 nhdrp
->sbh_msglen
= ntohl(hdrp
->sbh_msglen
);
385 nhdrp
->sbh_totlen
= ntohl(hdrp
->sbh_totlen
);
386 nhdrp
->sbh_drops
= ntohl(hdrp
->sbh_drops
);
387 nhdrp
->sbh_timestamp
.tv_sec
=
388 ntohl(hdrp
->sbh_timestamp
.tv_sec
);
389 nhdrp
->sbh_timestamp
.tv_usec
=
390 ntohl(hdrp
->sbh_timestamp
.tv_usec
);
393 /* Enhanced check for valid header */
395 if ((nhdrp
->sbh_totlen
== 0) ||
396 (bp
+ nhdrp
->sbh_totlen
) < bp
||
397 (bp
+ nhdrp
->sbh_totlen
) > bufstop
||
398 (nhdrp
->sbh_origlen
== 0) ||
399 (bp
+ nhdrp
->sbh_origlen
) < bp
||
400 (nhdrp
->sbh_msglen
== 0) ||
401 (bp
+ nhdrp
->sbh_msglen
) < bp
||
402 (bp
+ nhdrp
->sbh_msglen
) > bufstop
||
403 (nhdrp
->sbh_msglen
> nhdrp
->sbh_origlen
) ||
404 (nhdrp
->sbh_totlen
< nhdrp
->sbh_msglen
) ||
405 (nhdrp
->sbh_timestamp
.tv_sec
== 0)) {
407 (void) fprintf(stderr
, "(warning) bad packet "
408 "header in capture file");
410 (void) fprintf(stderr
, "(warning) bad packet "
413 (void) fprintf(stderr
, " offset %d: length=%d\n",
414 bp
- buf
, nhdrp
->sbh_totlen
);
419 * Check for incomplete packet. We are conservative here,
420 * since we don't know how good the checking is in other
421 * parts of the code. We pass a partial packet, with
424 if (pktp
+ nhdrp
->sbh_msglen
> bufstop
) {
425 (void) fprintf(stderr
, "truncated packet buffer\n");
426 nhdrp
->sbh_msglen
= bufstop
- pktp
;
431 corrupt(pktp
, pktp
+ nhdrp
->sbh_msglen
, buf
, bufstop
);
436 want_packet((uchar_t
*)pktp
,
438 nhdrp
->sbh_origlen
)) {
442 * Start deadman timer for interpreter processing
444 (void) snoop_alarm(SNOOP_ALARM_GRAN
*SNOOP_MAXRECOVER
,
448 if (!cap
|| count
>= first
)
449 proc(nhdrp
, pktp
, count
, flags
);
451 if (cap
&& count
>= last
) {
452 (void) snoop_alarm(0, NULL
);
456 if (maxcount
&& count
>= maxcount
) {
457 (void) fprintf(stderr
, "%d packets captured\n",
462 snoop_nrecover
= 0; /* success */
463 (void) snoop_alarm(0, NULL
);
464 last_timestamp
= hdrp
->sbh_timestamp
; /* save stamp */
469 * Corruption has been detected. Reset errors.
474 * packet header was apparently okay. Continue.
480 * Otherwise try to scan forward to the next packet, using
481 * the last known timestamp if it is available.
484 nhdrp
->sbh_totlen
= 0;
485 if (last_timestamp
.tv_sec
== 0) {
488 for (bp
+= sizeof (int); bp
<= bufstop
;
489 bp
+= sizeof (int)) {
490 hdrp
= (struct sb_hdr
*)bp
;
491 /* An approximate timestamp located */
492 if ((hdrp
->sbh_timestamp
.tv_sec
>> 8) ==
493 (last_timestamp
.tv_sec
>> 8))
498 /* reset jmp_env for program exit */
499 (void) memcpy((char *)jmp_env
, (char *)ojmp_env
, sizeof (jmp_env
));
504 * Called if nwrite() encounters write problems.
507 cap_write_error(const char *msgtype
)
509 (void) fprintf(stderr
,
510 "snoop: cannot write %s to capture file: %s\n",
511 msgtype
, strerror(errno
));
516 * Writes target buffer to the open file descriptor. Upon detection of a short
517 * write, an attempt to process the remaining bytes occurs until all anticipated
518 * bytes are written. An error status is returned to indicate any serious write
522 nwrite(int fd
, const void *buffer
, size_t buflen
)
526 const char *buf
= buffer
;
528 for (nwritten
= 0; nwritten
< buflen
; nwritten
+= nbytes
) {
529 nbytes
= write(fd
, &buf
[nwritten
], buflen
- nwritten
);
541 * Routines for opening, closing, reading and writing
542 * a capture file of packets saved with the -o option.
544 static int capfile_out
;
547 * The snoop capture file has a header to identify
548 * it as a capture file and record its version.
549 * A file without this header is assumed to be an
550 * old format snoop file.
552 * A version 1 header looks like this:
554 * 0 1 2 3 4 5 6 7 8 9 10 11
555 * +---+---+---+---+---+---+---+---+---+---+---+---+---+
556 * | s | n | o | o | p | \0| \0| \0| version | data
557 * +---+---+---+---+---+---+---+---+---+---+---+---+---+
558 * | word 0 | word 1 | word 2 |
561 * A version 2 header adds a word that identifies the MAC type.
562 * This allows for capture files from FDDI etc.
564 * 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
565 * +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
566 * | s | n | o | o | p | \0| \0| \0| version | MAC type | data
567 * +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
568 * | word 0 | word 1 | word 2 | word 3
571 static const char *snoop_id
= "snoop\0\0\0";
572 static const int snoop_idlen
= 8;
573 static const int snoop_version
= 2;
576 cap_open_write(const char *name
)
580 capfile_out
= open(name
, O_CREAT
| O_TRUNC
| O_RDWR
, 0666);
582 pr_err("%s: %m", name
);
584 vers
= htonl(snoop_version
);
585 if (nwrite(capfile_out
, snoop_id
, snoop_idlen
) == -1)
586 cap_write_error("snoop_id");
588 if (nwrite(capfile_out
, &vers
, sizeof (int)) == -1)
589 cap_write_error("version");
596 (void) close(capfile_out
);
599 static char *cap_buffp
= NULL
;
600 static int cap_len
= 0;
604 cap_open_read(const char *name
)
609 int device_mac_type
= -1;
612 capfile_in
= open(name
, O_RDONLY
);
614 pr_err("couldn't open %s: %m", name
);
616 if (fstat(capfile_in
, &st
) < 0)
617 pr_err("couldn't stat %s: %m", name
);
618 if (st
.st_size
> INT_MAX
)
619 pr_err("input file size (%llu bytes) exceeds maximum "
620 "supported size (%d bytes)",
621 (unsigned long long)st
.st_size
, INT_MAX
);
622 cap_len
= st
.st_size
;
624 cap_buffp
= mmap(NULL
, cap_len
, PROT_READ
, MAP_PRIVATE
, capfile_in
, 0);
625 (void) close(capfile_in
);
626 if ((int)cap_buffp
== -1)
627 pr_err("couldn't mmap %s: %m", name
);
629 /* Check if new snoop capture file format */
631 cap_new
= bcmp(cap_buffp
, snoop_id
, snoop_idlen
) == 0;
634 * If new file - check version and
635 * set buffer pointer to point at first packet
638 cap_vers
= ntohl(*(int *)(cap_buffp
+ snoop_idlen
));
639 cap_buffp
+= snoop_idlen
+ sizeof (int);
640 cap_len
-= snoop_idlen
+ sizeof (int);
644 device_mac_type
= DL_ETHER
;
648 device_mac_type
= ntohl(*((int *)cap_buffp
));
649 cap_buffp
+= sizeof (int);
650 cap_len
-= sizeof (int);
654 pr_err("capture file: %s: Version %d unrecognized\n",
658 for (interface
= &INTERFACES
[0]; interface
->mac_type
!= -1;
660 if (interface
->mac_type
== device_mac_type
)
663 if (interface
->mac_type
== -1)
664 pr_err("Mac Type = %x is not supported\n",
667 /* Use heuristic to check if it's an old-style file */
669 device_mac_type
= DL_ETHER
;
670 word
= (int *)cap_buffp
;
672 if (!((word
[0] < 1600 && word
[1] < 1600) &&
673 (word
[0] < word
[1]) &&
674 (word
[2] > 610000000 && word
[2] < 770000000)))
675 pr_err("not a capture file: %s", name
);
677 /* Change protection so's we can fix the headers */
679 if (mprotect(cap_buffp
, cap_len
, PROT_READ
| PROT_WRITE
) < 0)
680 pr_err("mprotect: %s: %m", name
);
685 cap_read(int first
, int last
, int filter
, void (*proc
)(), int flags
)
691 scan(cap_buffp
, cap_len
, filter
, 1, !cap_new
, proc
, first
, last
, flags
);
693 (void) munmap(cap_buffp
, cap_len
);
698 cap_write(struct sb_hdr
*hdrp
, char *pktp
, int num
, int flags
)
701 static int first
= 1;
703 extern boolean_t qflg
;
710 mac
= htonl(interface
->mac_type
);
711 if (nwrite(capfile_out
, &mac
, sizeof (int)) == -1)
712 cap_write_error("mac_type");
715 pktlen
= hdrp
->sbh_totlen
- sizeof (*hdrp
);
718 * Convert sb_hdr to network byte order
720 nhdr
.sbh_origlen
= htonl(hdrp
->sbh_origlen
);
721 nhdr
.sbh_msglen
= htonl(hdrp
->sbh_msglen
);
722 nhdr
.sbh_totlen
= htonl(hdrp
->sbh_totlen
);
723 nhdr
.sbh_drops
= htonl(hdrp
->sbh_drops
);
724 nhdr
.sbh_timestamp
.tv_sec
= htonl(hdrp
->sbh_timestamp
.tv_sec
);
725 nhdr
.sbh_timestamp
.tv_usec
= htonl(hdrp
->sbh_timestamp
.tv_usec
);
727 if (nwrite(capfile_out
, &nhdr
, sizeof (nhdr
)) == -1)
728 cap_write_error("packet header");
730 if (nwrite(capfile_out
, pktp
, pktlen
) == -1)
731 cap_write_error("packet");
738 * Convert a packet header from
742 convert_old(struct ohdr
*ohdrp
)
746 nhdr
.sbh_origlen
= ohdrp
->o_len
;
747 nhdr
.sbh_msglen
= ohdrp
->o_msglen
;
748 nhdr
.sbh_totlen
= ohdrp
->o_totlen
;
749 nhdr
.sbh_drops
= ohdrp
->o_drops
;
750 nhdr
.sbh_timestamp
= ohdrp
->o_time
;
752 *(struct sb_hdr
*)ohdrp
= nhdr
;
756 strioctl(int fd
, int cmd
, int timout
, int len
, void *dp
)
758 struct strioctl sioc
;
762 sioc
.ic_timout
= timout
;
765 rc
= ioctl(fd
, I_STR
, &sioc
);
770 return (sioc
.ic_len
);