2 * Copyright (c) 2013-2016 Apple Inc. All rights reserved.
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. The rights granted to you under the License
10 * may not be used to create, or enable the creation or redistribution of,
11 * unlawful or unlicensed copies of an Apple operating system, or to
12 * circumvent, violate, or enable the circumvention or violation of, any
13 * terms of an Apple operating system software license agreement.
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
18 * The Original Code and all software distributed under the License are
19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23 * Please see the License for the specific language governing rights and
24 * limitations under the License.
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
29 #include <sys/socket.h>
30 #include <sys/errno.h>
31 #include <sys/event.h>
33 #include <sys/sys_domain.h>
34 #include <sys/ioctl.h>
35 #include <sys/kern_control.h>
36 #include <sys/queue.h>
37 #include <net/content_filter.h>
38 #include <netinet/in.h>
48 extern void print_filter_list(void);
49 extern void print_socket_list(void);
50 extern void print_cfil_stats(void);
52 #define MAX_BUFFER (65536 + 1024)
54 #define MAXHEXDUMPCOL 16
59 MODE_INTERACTIVE
= 0x01,
66 unsigned long delay_ms
= 0;
67 struct timeval delay_tv
= { 0, 0 };
69 uint32_t necp_control_unit
= 0;
70 unsigned long auto_start
= 0;
71 uint64_t peek_inc
= 0;
72 uint64_t pass_offset
= 0;
73 struct timeval now
, deadline
;
75 int pass_loopback
= 0;
76 uint32_t random_drop
= 0;
77 uint32_t event_total
= 0;
78 uint32_t event_dropped
= 0;
80 uint64_t default_in_pass
= 0;
81 uint64_t default_in_peek
= 0;
82 uint64_t default_out_pass
= 0;
83 uint64_t default_out_peek
= 0;
85 unsigned long max_dump_len
= 32;
87 TAILQ_HEAD(sock_info_head
, sock_info
) sock_info_head
= TAILQ_HEAD_INITIALIZER(sock_info_head
);
91 TAILQ_ENTRY(sock_info
) si_link
;
92 cfil_sock_id_t si_sock_id
;
93 struct timeval si_deadline
;
101 HexDump(void *data
, size_t len
)
104 unsigned char *ptr
= (unsigned char *)data
;
105 unsigned char buf
[32 + 3 * MAXHEXDUMPCOL
+ 2 + MAXHEXDUMPCOL
+ 1];
107 for (i
= 0; i
< len
; i
+= MAXHEXDUMPCOL
) {
108 k
= snprintf((char *)buf
, sizeof(buf
), "\t0x%04lx: ", i
);
109 for (j
= i
; j
< i
+ MAXHEXDUMPCOL
; j
++) {
111 unsigned char msnbl
= ptr
[j
] >> 4;
112 unsigned char lsnbl
= ptr
[j
] & 0x0f;
114 buf
[k
++] = msnbl
< 10 ? msnbl
+ '0' : msnbl
+ 'a' - 10;
115 buf
[k
++] = lsnbl
< 10 ? lsnbl
+ '0' : lsnbl
+ 'a' - 10;
122 if ((j
% MAXHEXDUMPCOL
) == MAXHEXDUMPCOL
- 1)
129 for (j
= i
; j
< i
+ MAXHEXDUMPCOL
&& j
< len
; j
++) {
141 print_hdr(struct cfil_msg_hdr
*hdr
)
143 const char *typestr
= "unknown";
144 const char *opstr
= "unknown";
146 if (hdr
->cfm_type
== CFM_TYPE_EVENT
) {
148 switch (hdr
->cfm_op
) {
149 case CFM_OP_SOCKET_ATTACHED
:
152 case CFM_OP_SOCKET_CLOSED
:
155 case CFM_OP_DATA_OUT
:
161 case CFM_OP_DISCONNECT_OUT
:
162 opstr
= "disconnectout";
164 case CFM_OP_DISCONNECT_IN
:
165 opstr
= "disconnectin";
171 } else if (hdr
->cfm_type
== CFM_TYPE_ACTION
) {
173 switch (hdr
->cfm_op
) {
174 case CFM_OP_DATA_UPDATE
:
186 printf("%s %s len %u version %u type %u op %u sock_id 0x%llx\n",
188 hdr
->cfm_len
, hdr
->cfm_version
, hdr
->cfm_type
,
189 hdr
->cfm_op
, hdr
->cfm_sock_id
);
193 print_data_req(struct cfil_msg_data_event
*data_req
)
201 print_hdr(&data_req
->cfd_msghdr
);
203 printf(" start %llu end %llu\n",
204 data_req
->cfd_start_offset
, data_req
->cfd_end_offset
);
206 datalen
= (size_t)(data_req
->cfd_end_offset
- data_req
->cfd_start_offset
);
208 databuf
= (void *)(data_req
+ 1);
211 HexDump(databuf
, MIN(datalen
, max_dump_len
));
215 print_action_msg(struct cfil_msg_action
*action
)
220 print_hdr(&action
->cfa_msghdr
);
222 if (action
->cfa_msghdr
.cfm_op
== CFM_OP_DATA_UPDATE
)
223 printf(" out pass %llu peek %llu in pass %llu peek %llu\n",
224 action
->cfa_out_pass_offset
, action
->cfa_out_peek_offset
,
225 action
->cfa_in_pass_offset
, action
->cfa_in_peek_offset
);
229 find_sock_info(cfil_sock_id_t sockid
)
231 struct sock_info
*sock_info
;
233 TAILQ_FOREACH(sock_info
, &sock_info_head
, si_link
) {
234 if (sock_info
->si_sock_id
== sockid
)
241 add_sock_info(cfil_sock_id_t sockid
)
243 struct sock_info
*sock_info
;
245 if (find_sock_info(sockid
) != NULL
)
248 sock_info
= calloc(1, sizeof(struct sock_info
));
249 if (sock_info
== NULL
)
250 err(EX_OSERR
, "calloc()");
251 sock_info
->si_sock_id
= sockid
;
252 TAILQ_INSERT_TAIL(&sock_info_head
, sock_info
, si_link
);
258 remove_sock_info(cfil_sock_id_t sockid
)
260 struct sock_info
*sock_info
= find_sock_info(sockid
);
262 if (sock_info
!= NULL
) {
263 TAILQ_REMOVE(&sock_info_head
, sock_info
, si_link
);
268 /* return 0 if timer is already set */
270 set_sock_info_deadline(struct sock_info
*sock_info
)
272 if (timerisset(&sock_info
->si_deadline
))
275 timeradd(&now
, &sock_info
->si_deadline
, &sock_info
->si_deadline
);
277 if (!timerisset(&deadline
)) {
278 timeradd(&now
, &delay_tv
, &deadline
);
285 send_action_message(uint32_t op
, struct sock_info
*sock_info
, int nodelay
)
287 struct cfil_msg_action action
;
289 if (!nodelay
&& delay_ms
) {
290 set_sock_info_deadline(sock_info
);
293 bzero(&action
, sizeof(struct cfil_msg_action
));
294 action
.cfa_msghdr
.cfm_len
= sizeof(struct cfil_msg_action
);
295 action
.cfa_msghdr
.cfm_version
= CFM_VERSION_CURRENT
;
296 action
.cfa_msghdr
.cfm_type
= CFM_TYPE_ACTION
;
297 action
.cfa_msghdr
.cfm_op
= op
;
298 action
.cfa_msghdr
.cfm_sock_id
= sock_info
->si_sock_id
;
300 case CFM_OP_DATA_UPDATE
:
301 action
.cfa_out_pass_offset
= sock_info
->si_out_pass
;
302 action
.cfa_out_peek_offset
= sock_info
->si_out_peek
;
303 action
.cfa_in_pass_offset
= sock_info
->si_in_pass
;
304 action
.cfa_in_peek_offset
= sock_info
->si_in_peek
;
312 print_action_msg(&action
);
314 if (send(sf
, &action
, sizeof(struct cfil_msg_action
), 0) == -1)
317 timerclear(&sock_info
->si_deadline
);
321 process_delayed_actions()
323 struct sock_info
*sock_info
;
325 TAILQ_FOREACH(sock_info
, &sock_info_head
, si_link
) {
326 if (timerisset(&sock_info
->si_deadline
) &&
327 timercmp(&sock_info
->si_deadline
, &now
, >=))
328 send_action_message(CFM_OP_DATA_UPDATE
, sock_info
, 1);
333 set_non_blocking(int fd
)
337 flags
= fcntl(fd
, F_GETFL
);
339 warn("fcntl(F_GETFL)");
343 if (fcntl(fd
, F_SETFL
, flags
) == -1) {
344 warn("fcntl(F_SETFL)");
351 offset_from_str(const char *str
, uint64_t *ret_val
)
357 if (strcasecmp(str
, "max") == 0 || strcasecmp(str
, "all") == 0)
358 offset
= CFM_MAX_OFFSET
;
360 offset
= strtoull(str
, &endptr
, 0);
361 if (*str
== '\0' || *endptr
!= '\0')
369 #define IN6_IS_ADDR_V4MAPPED_LOOPBACK(a) \
370 ((*(const __uint32_t *)(const void *)(&(a)->s6_addr[0]) == 0) && \
371 (*(const __uint32_t *)(const void *)(&(a)->s6_addr[4]) == 0) && \
372 (*(const __uint32_t *)(const void *)(&(a)->s6_addr[8]) == ntohl(0x0000ffff)) && \
373 (*(const __uint32_t *)(const void *)(&(a)->s6_addr[12]) == ntohl(INADDR_LOOPBACK)))
377 is_loopback(struct cfil_msg_data_event
*data_req
)
379 if (data_req
->cfc_dst
.sa
.sa_family
== AF_INET
&&
380 ntohl(data_req
->cfc_dst
.sin
.sin_addr
.s_addr
) == INADDR_LOOPBACK
)
382 if (data_req
->cfc_dst
.sa
.sa_family
== AF_INET6
&&
383 IN6_IS_ADDR_LOOPBACK(&data_req
->cfc_dst
.sin6
.sin6_addr
))
385 if (data_req
->cfc_dst
.sa
.sa_family
== AF_INET6
&&
386 IN6_IS_ADDR_V4MAPPED_LOOPBACK(&data_req
->cfc_dst
.sin6
.sin6_addr
))
389 if (data_req
->cfc_src
.sa
.sa_family
== AF_INET
&&
390 ntohl(data_req
->cfc_src
.sin
.sin_addr
.s_addr
) == INADDR_LOOPBACK
)
392 if (data_req
->cfc_src
.sa
.sa_family
== AF_INET6
&&
393 IN6_IS_ADDR_LOOPBACK(&data_req
->cfc_src
.sin6
.sin6_addr
))
395 if (data_req
->cfc_src
.sa
.sa_family
== AF_INET6
&&
396 IN6_IS_ADDR_V4MAPPED_LOOPBACK(&data_req
->cfc_src
.sin6
.sin6_addr
))
403 drop(struct sock_info
*sock_info
)
406 if (random_drop
> 0) {
407 uint32_t r
= arc4random();
408 if (r
<= random_drop
) {
410 printf("dropping 0x%llx dropped %u total %u rate %f\n",
411 sock_info
->si_sock_id
,
412 event_dropped
, event_total
,
413 (double)event_dropped
/(double)event_total
* 100);
414 send_action_message(CFM_OP_DROP
, sock_info
, 0);
424 struct sockaddr_ctl sac
;
425 struct ctl_info ctl_info
;
427 struct cfil_msg_hdr
*hdr
;
430 int fdin
= fileno(stdin
);
436 struct cfil_msg_action action
;
437 cfil_sock_id_t last_sock_id
= 0;
438 struct sock_info
*sock_info
= NULL
;
439 struct timeval last_time
, elapsed
, delta
;
440 struct timespec interval
, *timeout
= NULL
;
446 sf
= socket(PF_SYSTEM
, SOCK_DGRAM
, SYSPROTO_CONTROL
);
450 bzero(&ctl_info
, sizeof(struct ctl_info
));
451 strlcpy(ctl_info
.ctl_name
, CONTENT_FILTER_CONTROL_NAME
, sizeof(ctl_info
.ctl_name
));
452 if (ioctl(sf
, CTLIOCGINFO
, &ctl_info
) == -1)
453 err(1, "ioctl(CTLIOCGINFO)");
455 if (fcntl(sf
, F_SETNOSIGPIPE
, 1) == -1)
456 err(1, "fcntl(F_SETNOSIGPIPE)");
458 bzero(&sac
, sizeof(struct sockaddr_ctl
));
459 sac
.sc_len
= sizeof(struct sockaddr_ctl
);
460 sac
.sc_family
= AF_SYSTEM
;
461 sac
.ss_sysaddr
= AF_SYS_CONTROL
;
462 sac
.sc_id
= ctl_info
.ctl_id
;
464 if (connect(sf
, (struct sockaddr
*)&sac
, sizeof(struct sockaddr_ctl
)) == -1)
467 if (set_non_blocking(sf
) == -1)
468 err(1, "set_non_blocking(sf)");
470 if (setsockopt(sf
, SYSPROTO_CONTROL
, CFIL_OPT_NECP_CONTROL_UNIT
,
471 &necp_control_unit
, sizeof(uint32_t)) == -1)
472 err(1, "setsockopt(CFIL_OPT_NECP_CONTROL_UNIT, %u)", necp_control_unit
);
474 bzero(&kv
, sizeof(struct kevent
));
476 kv
.filter
= EVFILT_READ
;
478 if (kevent(kq
, &kv
, 1, NULL
, 0, NULL
) == -1)
479 err(1, "kevent(sf %d)", sf
);
482 * We can only read from an interactive terminal
485 bzero(&kv
, sizeof(struct kevent
));
487 kv
.filter
= EVFILT_READ
;
489 if (kevent(kq
, &kv
, 1, NULL
, 0, NULL
) == -1)
490 err(1, "kevent(fdin %d)", fdin
);
493 buffer
= malloc(MAX_BUFFER
);
497 gettimeofday(&now
, NULL
);
501 if (delay_ms
&& timerisset(&deadline
)) {
502 timersub(&deadline
, &now
, &delta
);
503 TIMEVAL_TO_TIMESPEC(&delta
, &interval
);
509 if (kevent(kq
, NULL
, 0, &kv
, 1, timeout
) == -1) {
514 gettimeofday(&now
, NULL
);
515 timersub(&now
, &last_time
, &elapsed
);
516 if (delay_ms
&& timerisset(&deadline
)) {
517 if (timercmp(&now
, &deadline
, >=)) {
518 process_delayed_actions();
520 interval
.tv_nsec
= 0;
524 if (kv
.ident
== sf
&& kv
.filter
== EVFILT_READ
) {
528 nread
= recv(sf
, buffer
, MAX_BUFFER
, 0);
530 warnx("recv(sf) returned 0, connection closed");
536 if (errno
== EWOULDBLOCK
)
541 if (nread
< sizeof(struct cfil_msg_hdr
))
542 errx(1, "too small");
543 hdr
= (struct cfil_msg_hdr
*)buffer
;
546 if (hdr
->cfm_type
!= CFM_TYPE_EVENT
) {
547 warnx("not a content filter event type %u", hdr
->cfm_type
);
550 switch (hdr
->cfm_op
) {
551 case CFM_OP_SOCKET_ATTACHED
: {
552 struct cfil_msg_sock_attached
*msg_attached
= (struct cfil_msg_sock_attached
*)hdr
;
557 printf(" fam %d type %d proto %d pid %u epid %u\n",
558 msg_attached
->cfs_sock_family
,
559 msg_attached
->cfs_sock_type
,
560 msg_attached
->cfs_sock_protocol
,
561 msg_attached
->cfs_pid
,
562 msg_attached
->cfs_e_pid
);
565 case CFM_OP_SOCKET_CLOSED
:
566 case CFM_OP_DISCONNECT_IN
:
567 case CFM_OP_DISCONNECT_OUT
:
571 case CFM_OP_DATA_OUT
:
574 print_data_req((struct cfil_msg_data_event
*)hdr
);
577 warnx("unknown content filter event op %u", hdr
->cfm_op
);
580 switch (hdr
->cfm_op
) {
581 case CFM_OP_SOCKET_ATTACHED
:
582 sock_info
= add_sock_info(hdr
->cfm_sock_id
);
583 if (sock_info
== NULL
) {
584 warnx("sock_id %llx already exists", hdr
->cfm_sock_id
);
588 case CFM_OP_DATA_OUT
:
590 case CFM_OP_DISCONNECT_IN
:
591 case CFM_OP_DISCONNECT_OUT
:
592 case CFM_OP_SOCKET_CLOSED
:
593 sock_info
= find_sock_info(hdr
->cfm_sock_id
);
595 if (sock_info
== NULL
) {
596 warnx("unexpected data message, sock_info is NULL");
601 warnx("unknown content filter event op %u", hdr
->cfm_op
);
606 switch (hdr
->cfm_op
) {
607 case CFM_OP_SOCKET_ATTACHED
: {
608 if ((mode
& MODE_PASS
) || (mode
& MODE_PEEK
) || auto_start
) {
609 sock_info
->si_out_pass
= default_out_pass
;
610 sock_info
->si_out_peek
= (mode
& MODE_PEEK
) ? peek_inc
: (mode
& MODE_PASS
) ? CFM_MAX_OFFSET
: default_out_peek
;
611 sock_info
->si_in_pass
= default_in_pass
;
612 sock_info
->si_in_peek
= (mode
& MODE_PEEK
) ? peek_inc
: (mode
& MODE_PASS
) ? CFM_MAX_OFFSET
: default_in_peek
;
614 send_action_message(CFM_OP_DATA_UPDATE
, sock_info
, 0);
618 case CFM_OP_SOCKET_CLOSED
: {
619 remove_sock_info(hdr
->cfm_sock_id
);
623 case CFM_OP_DATA_OUT
:
624 case CFM_OP_DATA_IN
: {
625 struct cfil_msg_data_event
*data_req
= (struct cfil_msg_data_event
*)hdr
;
627 if (pass_loopback
&& is_loopback(data_req
)) {
628 sock_info
->si_out_pass
= CFM_MAX_OFFSET
;
629 sock_info
->si_in_pass
= CFM_MAX_OFFSET
;
634 if ((mode
& MODE_PASS
)) {
635 if (data_req
->cfd_msghdr
.cfm_op
== CFM_OP_DATA_OUT
) {
636 if (pass_offset
== 0 || pass_offset
== CFM_MAX_OFFSET
)
637 sock_info
->si_out_pass
= data_req
->cfd_end_offset
;
638 else if (data_req
->cfd_end_offset
> pass_offset
) {
639 sock_info
->si_out_pass
= CFM_MAX_OFFSET
;
640 sock_info
->si_in_pass
= CFM_MAX_OFFSET
;
642 sock_info
->si_out_peek
= (mode
& MODE_PEEK
) ?
643 data_req
->cfd_end_offset
+ peek_inc
: 0;
645 if (pass_offset
== 0 || pass_offset
== CFM_MAX_OFFSET
)
646 sock_info
->si_in_pass
= data_req
->cfd_end_offset
;
647 else if (data_req
->cfd_end_offset
> pass_offset
) {
648 sock_info
->si_out_pass
= CFM_MAX_OFFSET
;
649 sock_info
->si_in_pass
= CFM_MAX_OFFSET
;
651 sock_info
->si_in_peek
= (mode
& MODE_PEEK
) ?
652 data_req
->cfd_end_offset
+ peek_inc
: 0;
658 send_action_message(CFM_OP_DATA_UPDATE
, sock_info
, 0);
662 case CFM_OP_DISCONNECT_IN
:
663 case CFM_OP_DISCONNECT_OUT
: {
667 if ((mode
& MODE_PASS
)) {
668 sock_info
->si_out_pass
= CFM_MAX_OFFSET
;
669 sock_info
->si_in_pass
= CFM_MAX_OFFSET
;
671 send_action_message(CFM_OP_DATA_UPDATE
, sock_info
, 0);
676 warnx("unkown message op %u", hdr
->cfm_op
);
680 last_sock_id
= sock_info
->si_sock_id
;
683 if (kv
.ident
== fdin
&& kv
.filter
== EVFILT_READ
) {
689 nread
= getline(&linep
, &linecap
, stdin
);
691 errx(1, "getline()");
694 printf("linecap %lu nread %lu\n", linecap
, nread
);
696 linep
[nread
- 1] = '\0';
699 HexDump(linep
, linecap
);
704 if (cmdptr
== NULL
|| argptr
== NULL
|| linecap
> cmdlen
) {
706 cmdptr
= realloc(cmdptr
, cmdlen
);
707 argptr
= realloc(argptr
, cmdlen
);
711 * Trick to support unisgned and hexadecimal arguments
712 * as I can't figure out sscanf() conversions
714 nitems
= sscanf(linep
, "%s %s", cmdptr
, argptr
);
716 warnx("I didn't get that...");
718 } else if (nitems
> 1) {
719 if (offset_from_str(argptr
, &offset
) == 0) {
720 warnx("I didn't get that either...");
725 printf("nitems %d %s %s\n", nitems
, cmdptr
, argptr
);
727 bzero(&action
, sizeof(struct cfil_msg_action
));
728 action
.cfa_msghdr
.cfm_len
= sizeof(struct cfil_msg_action
);
729 action
.cfa_msghdr
.cfm_version
= CFM_VERSION_CURRENT
;
730 action
.cfa_msghdr
.cfm_type
= CFM_TYPE_ACTION
;
732 if (strcasecmp(cmdptr
, "passout") == 0 && nitems
> 1) {
733 op
= CFM_OP_DATA_UPDATE
;
734 action
.cfa_out_pass_offset
= offset
;
735 } else if (strcasecmp(cmdptr
, "passin") == 0 && nitems
> 1) {
736 op
= CFM_OP_DATA_UPDATE
;
737 action
.cfa_in_pass_offset
= offset
;
738 } else if (strcasecmp(cmdptr
, "pass") == 0 && nitems
> 1) {
739 op
= CFM_OP_DATA_UPDATE
;
740 action
.cfa_out_pass_offset
= offset
;
741 action
.cfa_in_pass_offset
= offset
;
742 } else if (strcasecmp(cmdptr
, "peekout") == 0 && nitems
> 1) {
743 op
= CFM_OP_DATA_UPDATE
;
744 action
.cfa_out_peek_offset
= offset
;
745 } else if (strcasecmp(cmdptr
, "peekin") == 0 && nitems
> 1) {
746 op
= CFM_OP_DATA_UPDATE
;
747 action
.cfa_in_peek_offset
= offset
;
748 } else if (strcasecmp(cmdptr
, "peek") == 0 && nitems
> 1) {
749 op
= CFM_OP_DATA_UPDATE
;
750 action
.cfa_out_peek_offset
= offset
;
751 action
.cfa_in_peek_offset
= offset
;
752 } else if (strcasecmp(cmdptr
, "start") == 0) {
753 op
= CFM_OP_DATA_UPDATE
;
754 action
.cfa_out_pass_offset
= 0;
755 action
.cfa_out_peek_offset
= CFM_MAX_OFFSET
;
756 action
.cfa_in_pass_offset
= 0;
757 action
.cfa_in_peek_offset
= CFM_MAX_OFFSET
;
758 } else if (strcasecmp(cmdptr
, "peekall") == 0) {
759 op
= CFM_OP_DATA_UPDATE
;
760 action
.cfa_out_peek_offset
= CFM_MAX_OFFSET
;
761 action
.cfa_in_peek_offset
= CFM_MAX_OFFSET
;
762 } else if (strcasecmp(cmdptr
, "passall") == 0) {
763 op
= CFM_OP_DATA_UPDATE
;
764 action
.cfa_out_pass_offset
= CFM_MAX_OFFSET
;
765 action
.cfa_out_peek_offset
= CFM_MAX_OFFSET
;
766 action
.cfa_in_pass_offset
= CFM_MAX_OFFSET
;
767 action
.cfa_in_peek_offset
= CFM_MAX_OFFSET
;
768 } else if (strcasecmp(cmdptr
, "drop") == 0)
770 else if (strcasecmp(cmdptr
, "sock") == 0) {
771 last_sock_id
= offset
;
772 printf("last_sock_id 0x%llx\n", last_sock_id
);
774 warnx("syntax error");
776 if (op
== CFM_OP_DATA_UPDATE
|| op
== CFM_OP_DROP
) {
777 action
.cfa_msghdr
.cfm_op
= op
;
778 action
.cfa_msghdr
.cfm_sock_id
= last_sock_id
;
779 print_action_msg(&action
);
781 if (send(sf
, &action
, sizeof(struct cfil_msg_action
), 0) == -1)
791 basename(const char * str
)
793 const char *last_slash
= strrchr(str
, '/');
795 if (last_slash
== NULL
)
798 return (last_slash
+ 1);
803 const char *description
;
807 struct option_desc option_desc_list
[] = {
808 { "-a offset", "auto start with offset", 0 },
809 { "-d offset value", "default offset value for passin, peekin, passout, peekout, pass, peek", 0 },
810 { "-h", "dsiplay this help", 0 },
811 { "-i", "interactive mode", 0 },
812 { "-k increment", "peek mode with increment", 0 },
813 {"-l", "pass loopback", 0 },
814 { "-m length", "max dump length", 0 },
815 { "-p offset", "pass mode (all or after given offset if > 0)", 0 },
816 { "-q", "decrease verbose level", 0 },
817 { "-r random", "random drop rate", 0 },
818 { "-s ", "display content filter statistics (all, sock, filt, cfil)", 0 },
819 { "-t delay", "pass delay in microseconds", 0 },
820 { "-u unit", "NECP filter control unit", 1 },
821 { "-v", "increase verbose level", 0 },
822 { NULL
, NULL
, 0 } /* Mark end of list */
826 usage(const char *cmd
)
828 struct option_desc
*option_desc
;
829 char *usage_str
= malloc(LINE_MAX
);
832 if (usage_str
== NULL
)
833 err(1, "%s: malloc(%d)", __func__
, LINE_MAX
);
835 usage_len
= snprintf(usage_str
, LINE_MAX
, "# usage: %s ", basename(cmd
));
837 for (option_desc
= option_desc_list
; option_desc
->option
!= NULL
; option_desc
++) {
840 if (option_desc
->required
)
841 len
= snprintf(usage_str
+ usage_len
, LINE_MAX
- usage_len
, "%s ", option_desc
->option
);
843 len
= snprintf(usage_str
+ usage_len
, LINE_MAX
- usage_len
, "[%s] ", option_desc
->option
);
845 err(1, "%s: snprintf(", __func__
);
848 if (usage_len
> LINE_MAX
)
851 printf("%s\n", usage_str
);
852 printf("options:\n");
854 for (option_desc
= option_desc_list
; option_desc
->option
!= NULL
; option_desc
++) {
855 printf(" %-20s # %s\n", option_desc
->option
, option_desc
->description
);
861 main(int argc
, char * const argv
[])
865 int stats_sock_list
= 0;
866 int stats_filt_list
= 0;
867 int stats_cfil_stats
= 0;
869 while ((ch
= getopt(argc
, argv
, "a:d:hik:lm:p:qr:s:t:u:v")) != -1) {
872 auto_start
= strtoul(optarg
, NULL
, 0);
876 errx(1, "'-d' needs 2 parameters");
877 if (strcasecmp(optarg
, "passout") == 0) {
878 if (offset_from_str(argv
[optind
], &default_out_pass
) == 0)
879 errx(1, "bad %s offset: %s", optarg
, argv
[optind
+ 1]);
880 } else if (strcasecmp(optarg
, "passin") == 0) {
881 if (offset_from_str(argv
[optind
], &default_in_pass
) == 0)
882 errx(1, "bad %s offset: %s", optarg
, argv
[optind
+ 1]);
883 } else if (strcasecmp(optarg
, "pass") == 0) {
884 if (offset_from_str(argv
[optind
], &default_out_pass
) == 0)
885 errx(1, "bad %s offset: %s", optarg
, argv
[optind
+ 1]);
886 default_in_pass
= default_out_pass
;
887 } else if (strcasecmp(optarg
, "peekout") == 0) {
888 if (offset_from_str(argv
[optind
], &default_out_peek
) == 0)
889 errx(1, "bad %s offset: %s", optarg
, argv
[optind
+ 1]);
890 } else if (strcasecmp(optarg
, "peekin") == 0) {
891 if (offset_from_str(argv
[optind
], &default_in_peek
) == 0)
892 errx(1, "bad %s offset: %s", optarg
, argv
[optind
+ 1]);
893 } else if (strcasecmp(optarg
, "peek") == 0) {
894 if (offset_from_str(argv
[optind
], &default_out_peek
) == 0)
895 errx(1, "bad %s offset: %s", optarg
, argv
[optind
+ 1]);
896 default_in_peek
= default_out_peek
;
898 errx(1, "syntax error");
905 mode
|= MODE_INTERACTIVE
;
909 if (offset_from_str(optarg
, &peek_inc
) == 0)
910 errx(1, "bad peek offset: %s", optarg
);
916 max_dump_len
= strtoul(optarg
, NULL
, 0);
920 if (offset_from_str(optarg
, &pass_offset
) == 0)
921 errx(1, "bad pass offset: %s", optarg
);
927 d
= strtod(optarg
, NULL
);
929 errx(1, "bad drop rate: %s -- it must be between 0 and 1", optarg
);
930 random_drop
= (uint32_t)(d
* UINT32_MAX
);
933 if (strcasecmp(optarg
, "all") == 0) {
936 stats_cfil_stats
= 1;
937 } else if (strcasecmp(optarg
, "sock") == 0) {
939 } else if (strcasecmp(optarg
, "filt") == 0) {
941 } else if (strcasecmp(optarg
, "cfil") == 0) {
942 stats_cfil_stats
= 1;
944 warnx("# Error: unknown type of statistic: %s", optarg
);
951 delay_ms
= strtoul(optarg
, NULL
, 0);
952 delay_tv
.tv_sec
= delay_ms
/ 1000;
953 delay_tv
.tv_usec
= (delay_ms
% 1000) * 1000;
956 necp_control_unit
= (uint32_t)strtoul(optarg
, NULL
, 0);
962 errx(1, "# syntax error, unknow option '%d'", ch
);
972 if (stats_cfil_stats
)
974 if (necp_control_unit
== 0 && (stats_filt_list
|| stats_sock_list
|| stats_cfil_stats
))
977 if (necp_control_unit
== 0) {
978 warnx("necp filter control unit is 0");