2 * Copyright (c) 2013-2014 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)");
481 bzero(&kv
, sizeof(struct kevent
));
483 kv
.filter
= EVFILT_READ
;
485 if (kevent(kq
, &kv
, 1, NULL
, 0, NULL
) == -1)
486 err(1, "kevent(sf)");
488 buffer
= malloc(MAX_BUFFER
);
492 gettimeofday(&now
, NULL
);
496 if (delay_ms
&& timerisset(&deadline
)) {
497 timersub(&deadline
, &now
, &delta
);
498 TIMEVAL_TO_TIMESPEC(&delta
, &interval
);
504 if (kevent(kq
, NULL
, 0, &kv
, 1, timeout
) == -1) {
509 gettimeofday(&now
, NULL
);
510 timersub(&now
, &last_time
, &elapsed
);
511 if (delay_ms
&& timerisset(&deadline
)) {
512 if (timercmp(&now
, &deadline
, >=)) {
513 process_delayed_actions();
515 interval
.tv_nsec
= 0;
519 if (kv
.ident
== sf
&& kv
.filter
== EVFILT_READ
) {
523 nread
= recv(sf
, buffer
, MAX_BUFFER
, 0);
525 warnx("recv(sf) returned 0, connection closed");
531 if (errno
== EWOULDBLOCK
)
536 if (nread
< sizeof(struct cfil_msg_hdr
))
537 errx(1, "too small");
538 hdr
= (struct cfil_msg_hdr
*)buffer
;
541 if (hdr
->cfm_type
!= CFM_TYPE_EVENT
) {
542 warnx("not a content filter event type %u", hdr
->cfm_type
);
545 switch (hdr
->cfm_op
) {
546 case CFM_OP_SOCKET_ATTACHED
: {
547 struct cfil_msg_sock_attached
*msg_attached
= (struct cfil_msg_sock_attached
*)hdr
;
552 printf(" fam %d type %d proto %d pid %u epid %u\n",
553 msg_attached
->cfs_sock_family
,
554 msg_attached
->cfs_sock_type
,
555 msg_attached
->cfs_sock_protocol
,
556 msg_attached
->cfs_pid
,
557 msg_attached
->cfs_e_pid
);
560 case CFM_OP_SOCKET_CLOSED
:
561 case CFM_OP_DISCONNECT_IN
:
562 case CFM_OP_DISCONNECT_OUT
:
566 case CFM_OP_DATA_OUT
:
569 print_data_req((struct cfil_msg_data_event
*)hdr
);
572 warnx("unknown content filter event op %u", hdr
->cfm_op
);
575 switch (hdr
->cfm_op
) {
576 case CFM_OP_SOCKET_ATTACHED
:
577 sock_info
= add_sock_info(hdr
->cfm_sock_id
);
578 if (sock_info
== NULL
) {
579 warnx("sock_id %llx already exists", hdr
->cfm_sock_id
);
583 case CFM_OP_DATA_OUT
:
585 case CFM_OP_DISCONNECT_IN
:
586 case CFM_OP_DISCONNECT_OUT
:
587 case CFM_OP_SOCKET_CLOSED
:
588 sock_info
= find_sock_info(hdr
->cfm_sock_id
);
590 if (sock_info
== NULL
) {
591 warnx("unexpected data message, sock_info is NULL");
596 warnx("unknown content filter event op %u", hdr
->cfm_op
);
601 switch (hdr
->cfm_op
) {
602 case CFM_OP_SOCKET_ATTACHED
: {
603 if ((mode
& MODE_PASS
) || (mode
& MODE_PEEK
) || auto_start
) {
604 sock_info
->si_out_pass
= default_out_pass
;
605 sock_info
->si_out_peek
= (mode
& MODE_PEEK
) ? peek_inc
: (mode
& MODE_PASS
) ? CFM_MAX_OFFSET
: default_out_peek
;
606 sock_info
->si_in_pass
= default_in_pass
;
607 sock_info
->si_in_peek
= (mode
& MODE_PEEK
) ? peek_inc
: (mode
& MODE_PASS
) ? CFM_MAX_OFFSET
: default_in_peek
;
609 send_action_message(CFM_OP_DATA_UPDATE
, sock_info
, 0);
613 case CFM_OP_SOCKET_CLOSED
: {
614 remove_sock_info(hdr
->cfm_sock_id
);
618 case CFM_OP_DATA_OUT
:
619 case CFM_OP_DATA_IN
: {
620 struct cfil_msg_data_event
*data_req
= (struct cfil_msg_data_event
*)hdr
;
622 if (pass_loopback
&& is_loopback(data_req
)) {
623 sock_info
->si_out_pass
= CFM_MAX_OFFSET
;
624 sock_info
->si_in_pass
= CFM_MAX_OFFSET
;
629 if ((mode
& MODE_PASS
)) {
630 if (data_req
->cfd_msghdr
.cfm_op
== CFM_OP_DATA_OUT
) {
631 if (pass_offset
== 0 || pass_offset
== CFM_MAX_OFFSET
)
632 sock_info
->si_out_pass
= data_req
->cfd_end_offset
;
633 else if (data_req
->cfd_end_offset
> pass_offset
) {
634 sock_info
->si_out_pass
= CFM_MAX_OFFSET
;
635 sock_info
->si_in_pass
= CFM_MAX_OFFSET
;
637 sock_info
->si_out_peek
= (mode
& MODE_PEEK
) ?
638 data_req
->cfd_end_offset
+ peek_inc
: 0;
640 if (pass_offset
== 0 || pass_offset
== CFM_MAX_OFFSET
)
641 sock_info
->si_in_pass
= data_req
->cfd_end_offset
;
642 else if (data_req
->cfd_end_offset
> pass_offset
) {
643 sock_info
->si_out_pass
= CFM_MAX_OFFSET
;
644 sock_info
->si_in_pass
= CFM_MAX_OFFSET
;
646 sock_info
->si_in_peek
= (mode
& MODE_PEEK
) ?
647 data_req
->cfd_end_offset
+ peek_inc
: 0;
653 send_action_message(CFM_OP_DATA_UPDATE
, sock_info
, 0);
657 case CFM_OP_DISCONNECT_IN
:
658 case CFM_OP_DISCONNECT_OUT
: {
662 if ((mode
& MODE_PASS
)) {
663 sock_info
->si_out_pass
= CFM_MAX_OFFSET
;
664 sock_info
->si_in_pass
= CFM_MAX_OFFSET
;
666 send_action_message(CFM_OP_DATA_UPDATE
, sock_info
, 0);
671 warnx("unkown message op %u", hdr
->cfm_op
);
675 last_sock_id
= sock_info
->si_sock_id
;
678 if (kv
.ident
== fdin
&& kv
.filter
== EVFILT_READ
) {
684 nread
= getline(&linep
, &linecap
, stdin
);
686 errx(1, "getline()");
689 printf("linecap %lu nread %lu\n", linecap
, nread
);
691 linep
[nread
- 1] = '\0';
694 HexDump(linep
, linecap
);
699 if (cmdptr
== NULL
|| argptr
== NULL
|| linecap
> cmdlen
) {
701 cmdptr
= realloc(cmdptr
, cmdlen
);
702 argptr
= realloc(argptr
, cmdlen
);
706 * Trick to support unisgned and hexadecimal arguments
707 * as I can't figure out sscanf() conversions
709 nitems
= sscanf(linep
, "%s %s", cmdptr
, argptr
);
711 warnx("I didn't get that...");
713 } else if (nitems
> 1) {
714 if (offset_from_str(argptr
, &offset
) == 0) {
715 warnx("I didn't get that either...");
720 printf("nitems %d %s %s\n", nitems
, cmdptr
, argptr
);
722 bzero(&action
, sizeof(struct cfil_msg_action
));
723 action
.cfa_msghdr
.cfm_len
= sizeof(struct cfil_msg_action
);
724 action
.cfa_msghdr
.cfm_version
= CFM_VERSION_CURRENT
;
725 action
.cfa_msghdr
.cfm_type
= CFM_TYPE_ACTION
;
727 if (strcasecmp(cmdptr
, "passout") == 0 && nitems
> 1) {
728 op
= CFM_OP_DATA_UPDATE
;
729 action
.cfa_out_pass_offset
= offset
;
730 } else if (strcasecmp(cmdptr
, "passin") == 0 && nitems
> 1) {
731 op
= CFM_OP_DATA_UPDATE
;
732 action
.cfa_in_pass_offset
= offset
;
733 } else if (strcasecmp(cmdptr
, "pass") == 0 && nitems
> 1) {
734 op
= CFM_OP_DATA_UPDATE
;
735 action
.cfa_out_pass_offset
= offset
;
736 action
.cfa_in_pass_offset
= offset
;
737 } else if (strcasecmp(cmdptr
, "peekout") == 0 && nitems
> 1) {
738 op
= CFM_OP_DATA_UPDATE
;
739 action
.cfa_out_peek_offset
= offset
;
740 } else if (strcasecmp(cmdptr
, "peekin") == 0 && nitems
> 1) {
741 op
= CFM_OP_DATA_UPDATE
;
742 action
.cfa_in_peek_offset
= offset
;
743 } else if (strcasecmp(cmdptr
, "peek") == 0 && nitems
> 1) {
744 op
= CFM_OP_DATA_UPDATE
;
745 action
.cfa_out_peek_offset
= offset
;
746 action
.cfa_in_peek_offset
= offset
;
747 } else if (strcasecmp(cmdptr
, "start") == 0) {
748 op
= CFM_OP_DATA_UPDATE
;
749 action
.cfa_out_pass_offset
= 0;
750 action
.cfa_out_peek_offset
= CFM_MAX_OFFSET
;
751 action
.cfa_in_pass_offset
= 0;
752 action
.cfa_in_peek_offset
= CFM_MAX_OFFSET
;
753 } else if (strcasecmp(cmdptr
, "peekall") == 0) {
754 op
= CFM_OP_DATA_UPDATE
;
755 action
.cfa_out_peek_offset
= CFM_MAX_OFFSET
;
756 action
.cfa_in_peek_offset
= CFM_MAX_OFFSET
;
757 } else if (strcasecmp(cmdptr
, "passall") == 0) {
758 op
= CFM_OP_DATA_UPDATE
;
759 action
.cfa_out_pass_offset
= CFM_MAX_OFFSET
;
760 action
.cfa_out_peek_offset
= CFM_MAX_OFFSET
;
761 action
.cfa_in_pass_offset
= CFM_MAX_OFFSET
;
762 action
.cfa_in_peek_offset
= CFM_MAX_OFFSET
;
763 } else if (strcasecmp(cmdptr
, "drop") == 0)
765 else if (strcasecmp(cmdptr
, "sock") == 0) {
766 last_sock_id
= offset
;
767 printf("last_sock_id 0x%llx\n", last_sock_id
);
769 warnx("syntax error");
771 if (op
== CFM_OP_DATA_UPDATE
|| op
== CFM_OP_DROP
) {
772 action
.cfa_msghdr
.cfm_op
= op
;
773 action
.cfa_msghdr
.cfm_sock_id
= last_sock_id
;
774 print_action_msg(&action
);
776 if (send(sf
, &action
, sizeof(struct cfil_msg_action
), 0) == -1)
786 basename(const char * str
)
788 const char *last_slash
= strrchr(str
, '/');
790 if (last_slash
== NULL
)
793 return (last_slash
+ 1);
798 const char *description
;
802 struct option_desc option_desc_list
[] = {
803 { "-a offset", "auto start with offset", 0 },
804 { "-d offset value", "default offset value for passin, peekin, passout, peekout, pass, peek", 0 },
805 { "-h", "dsiplay this help", 0 },
806 { "-i", "interactive mode", 0 },
807 { "-k increment", "peek mode with increment", 0 },
808 {"-l", "pass loopback", 0 },
809 { "-m length", "max dump length", 0 },
810 { "-p offset", "pass mode (all or after given offset if > 0)", 0 },
811 { "-q", "decrease verbose level", 0 },
812 { "-r random", "random drop rate", 0 },
813 { "-s ", "display content filter statistics (all, sock, filt, cfil)", 0 },
814 { "-t delay", "pass delay in microseconds", 0 },
815 { "-u unit", "NECP filter control unit", 1 },
816 { "-v", "increase verbose level", 0 },
817 { NULL
, NULL
, 0 } /* Mark end of list */
821 usage(const char *cmd
)
823 struct option_desc
*option_desc
;
824 char *usage_str
= malloc(LINE_MAX
);
827 if (usage_str
== NULL
)
828 err(1, "%s: malloc(%d)", __func__
, LINE_MAX
);
830 usage_len
= snprintf(usage_str
, LINE_MAX
, "# usage: %s ", basename(cmd
));
832 for (option_desc
= option_desc_list
; option_desc
->option
!= NULL
; option_desc
++) {
835 if (option_desc
->required
)
836 len
= snprintf(usage_str
+ usage_len
, LINE_MAX
- usage_len
, "%s ", option_desc
->option
);
838 len
= snprintf(usage_str
+ usage_len
, LINE_MAX
- usage_len
, "[%s] ", option_desc
->option
);
840 err(1, "%s: snprintf(", __func__
);
843 if (usage_len
> LINE_MAX
)
846 printf("%s\n", usage_str
);
847 printf("options:\n");
849 for (option_desc
= option_desc_list
; option_desc
->option
!= NULL
; option_desc
++) {
850 printf(" %-20s # %s\n", option_desc
->option
, option_desc
->description
);
856 main(int argc
, char * const argv
[])
860 int stats_sock_list
= 0;
861 int stats_filt_list
= 0;
862 int stats_cfil_stats
= 0;
864 while ((ch
= getopt(argc
, argv
, "a:d:hik:lm:p:qr:s:t:u:v")) != -1) {
867 auto_start
= strtoul(optarg
, NULL
, 0);
871 errx(1, "'-d' needs 2 parameters");
872 if (strcasecmp(optarg
, "passout") == 0) {
873 if (offset_from_str(argv
[optind
], &default_out_pass
) == 0)
874 errx(1, "bad %s offset: %s", optarg
, argv
[optind
+ 1]);
875 } else if (strcasecmp(optarg
, "passin") == 0) {
876 if (offset_from_str(argv
[optind
], &default_in_pass
) == 0)
877 errx(1, "bad %s offset: %s", optarg
, argv
[optind
+ 1]);
878 } else if (strcasecmp(optarg
, "pass") == 0) {
879 if (offset_from_str(argv
[optind
], &default_out_pass
) == 0)
880 errx(1, "bad %s offset: %s", optarg
, argv
[optind
+ 1]);
881 default_in_pass
= default_out_pass
;
882 } else if (strcasecmp(optarg
, "peekout") == 0) {
883 if (offset_from_str(argv
[optind
], &default_out_peek
) == 0)
884 errx(1, "bad %s offset: %s", optarg
, argv
[optind
+ 1]);
885 } else if (strcasecmp(optarg
, "peekin") == 0) {
886 if (offset_from_str(argv
[optind
], &default_in_peek
) == 0)
887 errx(1, "bad %s offset: %s", optarg
, argv
[optind
+ 1]);
888 } else if (strcasecmp(optarg
, "peek") == 0) {
889 if (offset_from_str(argv
[optind
], &default_out_peek
) == 0)
890 errx(1, "bad %s offset: %s", optarg
, argv
[optind
+ 1]);
891 default_in_peek
= default_out_peek
;
893 errx(1, "syntax error");
900 mode
|= MODE_INTERACTIVE
;
904 if (offset_from_str(optarg
, &peek_inc
) == 0)
905 errx(1, "bad peek offset: %s", optarg
);
911 max_dump_len
= strtoul(optarg
, NULL
, 0);
915 if (offset_from_str(optarg
, &pass_offset
) == 0)
916 errx(1, "bad pass offset: %s", optarg
);
922 d
= strtod(optarg
, NULL
);
924 errx(1, "bad drop rate: %s -- it must be between 0 and 1", optarg
);
925 random_drop
= (uint32_t)(d
* UINT32_MAX
);
928 if (strcasecmp(optarg
, "all") == 0) {
931 stats_cfil_stats
= 1;
932 } else if (strcasecmp(optarg
, "sock") == 0) {
934 } else if (strcasecmp(optarg
, "filt") == 0) {
936 } else if (strcasecmp(optarg
, "cfil") == 0) {
937 stats_cfil_stats
= 1;
939 warnx("# Error: unknown type of statistic: %s", optarg
);
946 delay_ms
= strtoul(optarg
, NULL
, 0);
947 delay_tv
.tv_sec
= delay_ms
/ 1000;
948 delay_tv
.tv_usec
= (delay_ms
% 1000) * 1000;
951 necp_control_unit
= (uint32_t)strtoul(optarg
, NULL
, 0);
957 errx(1, "# syntax error, unknow option '%d'", ch
);
967 if (stats_cfil_stats
)
969 if (necp_control_unit
== 0 && (stats_filt_list
|| stats_sock_list
|| stats_cfil_stats
))
972 if (necp_control_unit
== 0) {
973 warnx("necp filter control unit is 0");