]> git.saurik.com Git - apple/network_cmds.git/blob - cfilutil/cfilutil.c
network_cmds-511.tar.gz
[apple/network_cmds.git] / cfilutil / cfilutil.c
1 /*
2 * Copyright (c) 2013-2014 Apple Inc. All rights reserved.
3 *
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
5 *
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.
14 *
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
17 *
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.
25 *
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27 */
28
29 #include <sys/socket.h>
30 #include <sys/errno.h>
31 #include <sys/event.h>
32 #include <sys/time.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>
39 #include <stdio.h>
40 #include <err.h>
41 #include <string.h>
42 #include <stdlib.h>
43 #include <fcntl.h>
44 #include <unistd.h>
45 #include <ctype.h>
46 #include <sysexits.h>
47
48 extern void print_filter_list(void);
49 extern void print_socket_list(void);
50 extern void print_cfil_stats(void);
51
52 #define MAX_BUFFER (65536 + 1024)
53
54 #define MAXHEXDUMPCOL 16
55
56
57 enum {
58 MODE_NONE = 0,
59 MODE_INTERACTIVE = 0x01,
60 MODE_PEEK = 0x02,
61 MODE_PASS = 0x04,
62 MODE_DELAY = 0x08
63 };
64 int mode = MODE_NONE;
65
66 unsigned long delay_ms = 0;
67 struct timeval delay_tv = { 0, 0 };
68 long verbosity = 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;
74 int sf = -1;
75 int pass_loopback = 0;
76 uint32_t random_drop = 0;
77 uint32_t event_total = 0;
78 uint32_t event_dropped = 0;
79
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;
84
85 unsigned long max_dump_len = 32;
86
87 TAILQ_HEAD(sock_info_head, sock_info) sock_info_head = TAILQ_HEAD_INITIALIZER(sock_info_head);
88
89
90 struct sock_info {
91 TAILQ_ENTRY(sock_info) si_link;
92 cfil_sock_id_t si_sock_id;
93 struct timeval si_deadline;
94 uint64_t si_in_pass;
95 uint64_t si_in_peek;
96 uint64_t si_out_pass;
97 uint64_t si_out_peek;
98 };
99
100 static void
101 HexDump(void *data, size_t len)
102 {
103 size_t i, j, k;
104 unsigned char *ptr = (unsigned char *)data;
105 unsigned char buf[32 + 3 * MAXHEXDUMPCOL + 2 + MAXHEXDUMPCOL + 1];
106
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++) {
110 if (j < len) {
111 unsigned char msnbl = ptr[j] >> 4;
112 unsigned char lsnbl = ptr[j] & 0x0f;
113
114 buf[k++] = msnbl < 10 ? msnbl + '0' : msnbl + 'a' - 10;
115 buf[k++] = lsnbl < 10 ? lsnbl + '0' : lsnbl + 'a' - 10;
116 } else {
117 buf[k++] = ' ';
118 buf[k++] = ' ';
119 }
120 if ((j % 2) == 1)
121 buf[k++] = ' ';
122 if ((j % MAXHEXDUMPCOL) == MAXHEXDUMPCOL - 1)
123 buf[k++] = ' ';
124 }
125
126 buf[k++] = ' ';
127 buf[k++] = ' ';
128
129 for (j = i; j < i + MAXHEXDUMPCOL && j < len; j++) {
130 if (isprint(ptr[j]))
131 buf[k++] = ptr[j];
132 else
133 buf[k++] = '.';
134 }
135 buf[k] = 0;
136 printf("%s\n", buf);
137 }
138 }
139
140 void
141 print_hdr(struct cfil_msg_hdr *hdr)
142 {
143 const char *typestr = "unknown";
144 const char *opstr = "unknown";
145
146 if (hdr->cfm_type == CFM_TYPE_EVENT) {
147 typestr = "event";
148 switch (hdr->cfm_op) {
149 case CFM_OP_SOCKET_ATTACHED:
150 opstr = "attached";
151 break;
152 case CFM_OP_SOCKET_CLOSED:
153 opstr = "closed";
154 break;
155 case CFM_OP_DATA_OUT:
156 opstr = "dataout";
157 break;
158 case CFM_OP_DATA_IN:
159 opstr = "datain";
160 break;
161 case CFM_OP_DISCONNECT_OUT:
162 opstr = "disconnectout";
163 break;
164 case CFM_OP_DISCONNECT_IN:
165 opstr = "disconnectin";
166 break;
167
168 default:
169 break;
170 }
171 } else if (hdr->cfm_type == CFM_TYPE_ACTION) {
172 typestr = "action";
173 switch (hdr->cfm_op) {
174 case CFM_OP_DATA_UPDATE:
175 opstr = "update";
176 break;
177 case CFM_OP_DROP:
178 opstr = "drop";
179 break;
180
181 default:
182 break;
183 }
184
185 }
186 printf("%s %s len %u version %u type %u op %u sock_id 0x%llx\n",
187 typestr, opstr,
188 hdr->cfm_len, hdr->cfm_version, hdr->cfm_type,
189 hdr->cfm_op, hdr->cfm_sock_id);
190 }
191
192 void
193 print_data_req(struct cfil_msg_data_event *data_req)
194 {
195 size_t datalen;
196 void *databuf;
197
198 if (verbosity <= 0)
199 return;
200
201 print_hdr(&data_req->cfd_msghdr);
202
203 printf(" start %llu end %llu\n",
204 data_req->cfd_start_offset, data_req->cfd_end_offset);
205
206 datalen = (size_t)(data_req->cfd_end_offset - data_req->cfd_start_offset);
207
208 databuf = (void *)(data_req + 1);
209
210 if (verbosity > 1)
211 HexDump(databuf, MIN(datalen, max_dump_len));
212 }
213
214 void
215 print_action_msg(struct cfil_msg_action *action)
216 {
217 if (verbosity <= 0)
218 return;
219
220 print_hdr(&action->cfa_msghdr);
221
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);
226 }
227
228 struct sock_info *
229 find_sock_info(cfil_sock_id_t sockid)
230 {
231 struct sock_info *sock_info;
232
233 TAILQ_FOREACH(sock_info, &sock_info_head, si_link) {
234 if (sock_info->si_sock_id == sockid)
235 return (sock_info);
236 }
237 return (NULL);
238 }
239
240 struct sock_info *
241 add_sock_info(cfil_sock_id_t sockid)
242 {
243 struct sock_info *sock_info;
244
245 if (find_sock_info(sockid) != NULL)
246 return (NULL);
247
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);
253
254 return (sock_info);
255 }
256
257 void
258 remove_sock_info(cfil_sock_id_t sockid)
259 {
260 struct sock_info *sock_info = find_sock_info(sockid);
261
262 if (sock_info != NULL) {
263 TAILQ_REMOVE(&sock_info_head, sock_info, si_link);
264 free(sock_info);
265 }
266 }
267
268 /* return 0 if timer is already set */
269 int
270 set_sock_info_deadline(struct sock_info *sock_info)
271 {
272 if (timerisset(&sock_info->si_deadline))
273 return (0);
274
275 timeradd(&now, &sock_info->si_deadline, &sock_info->si_deadline);
276
277 if (!timerisset(&deadline)) {
278 timeradd(&now, &delay_tv, &deadline);
279 }
280
281 return (1);
282 }
283
284 void
285 send_action_message(uint32_t op, struct sock_info *sock_info, int nodelay)
286 {
287 struct cfil_msg_action action;
288
289 if (!nodelay && delay_ms) {
290 set_sock_info_deadline(sock_info);
291 return;
292 }
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;
299 switch (op) {
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;
305 break;
306
307 default:
308 break;
309 }
310
311 if (verbosity > -1)
312 print_action_msg(&action);
313
314 if (send(sf, &action, sizeof(struct cfil_msg_action), 0) == -1)
315 warn("send()");
316
317 timerclear(&sock_info->si_deadline);
318 }
319
320 void
321 process_delayed_actions()
322 {
323 struct sock_info *sock_info;
324
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);
329 }
330 }
331
332 int
333 set_non_blocking(int fd)
334 {
335 int flags;
336
337 flags = fcntl(fd, F_GETFL);
338 if (flags == -1) {
339 warn("fcntl(F_GETFL)");
340 return (-1);
341 }
342 flags |= O_NONBLOCK;
343 if (fcntl(fd, F_SETFL, flags) == -1) {
344 warn("fcntl(F_SETFL)");
345 return (-1);
346 }
347 return (0);
348 }
349
350 int
351 offset_from_str(const char *str, uint64_t *ret_val)
352 {
353 char *endptr;
354 uint64_t offset;
355 int success = 1;
356
357 if (strcasecmp(str, "max") == 0 || strcasecmp(str, "all") == 0)
358 offset = CFM_MAX_OFFSET;
359 else {
360 offset = strtoull(str, &endptr, 0);
361 if (*str == '\0' || *endptr != '\0')
362 success = 0;
363 }
364 if (success)
365 *ret_val = offset;
366 return (success);
367 }
368
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)))
374
375
376 int
377 is_loopback(struct cfil_msg_data_event *data_req)
378 {
379 if (data_req->cfc_dst.sa.sa_family == AF_INET &&
380 ntohl(data_req->cfc_dst.sin.sin_addr.s_addr) == INADDR_LOOPBACK)
381 return (1);
382 if (data_req->cfc_dst.sa.sa_family == AF_INET6 &&
383 IN6_IS_ADDR_LOOPBACK(&data_req->cfc_dst.sin6.sin6_addr))
384 return (1);
385 if (data_req->cfc_dst.sa.sa_family == AF_INET6 &&
386 IN6_IS_ADDR_V4MAPPED_LOOPBACK(&data_req->cfc_dst.sin6.sin6_addr))
387 return (1);
388
389 if (data_req->cfc_src.sa.sa_family == AF_INET &&
390 ntohl(data_req->cfc_src.sin.sin_addr.s_addr) == INADDR_LOOPBACK)
391 return (1);
392 if (data_req->cfc_src.sa.sa_family == AF_INET6 &&
393 IN6_IS_ADDR_LOOPBACK(&data_req->cfc_src.sin6.sin6_addr))
394 return (1);
395 if (data_req->cfc_src.sa.sa_family == AF_INET6 &&
396 IN6_IS_ADDR_V4MAPPED_LOOPBACK(&data_req->cfc_src.sin6.sin6_addr))
397 return (1);
398
399 return (0);
400 }
401
402 int
403 drop(struct sock_info *sock_info)
404 {
405 event_total++;
406 if (random_drop > 0) {
407 uint32_t r = arc4random();
408 if (r <= random_drop) {
409 event_dropped++;
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);
415 return (1);
416 }
417 }
418 return (0);
419 }
420
421 int
422 doit()
423 {
424 struct sockaddr_ctl sac;
425 struct ctl_info ctl_info;
426 void *buffer = NULL;
427 struct cfil_msg_hdr *hdr;
428 int kq = -1;
429 struct kevent kv;
430 int fdin = fileno(stdin);
431 char *linep = NULL;
432 size_t linecap = 0;
433 char *cmdptr = NULL;
434 char *argptr = NULL;
435 size_t cmdlen = 0;
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;
441
442 kq = kqueue();
443 if (kq == -1)
444 err(1, "kqueue()");
445
446 sf = socket(PF_SYSTEM, SOCK_DGRAM, SYSPROTO_CONTROL);
447 if (sf == -1)
448 err(1, "socket()");
449
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)");
454
455 if (fcntl(sf, F_SETNOSIGPIPE, 1) == -1)
456 err(1, "fcntl(F_SETNOSIGPIPE)");
457
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;
463
464 if (connect(sf, (struct sockaddr *)&sac, sizeof(struct sockaddr_ctl)) == -1)
465 err(1, "connect()");
466
467 if (set_non_blocking(sf) == -1)
468 err(1, "set_non_blocking(sf)");
469
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);
473
474 bzero(&kv, sizeof(struct kevent));
475 kv.ident = sf;
476 kv.filter = EVFILT_READ;
477 kv.flags = EV_ADD;
478 if (kevent(kq, &kv, 1, NULL, 0, NULL) == -1)
479 err(1, "kevent(sf)");
480
481 bzero(&kv, sizeof(struct kevent));
482 kv.ident = fdin;
483 kv.filter = EVFILT_READ;
484 kv.flags = EV_ADD;
485 if (kevent(kq, &kv, 1, NULL, 0, NULL) == -1)
486 err(1, "kevent(sf)");
487
488 buffer = malloc(MAX_BUFFER);
489 if (buffer == NULL)
490 err(1, "malloc()");
491
492 gettimeofday(&now, NULL);
493
494 while (1) {
495 last_time = now;
496 if (delay_ms && timerisset(&deadline)) {
497 timersub(&deadline, &now, &delta);
498 TIMEVAL_TO_TIMESPEC(&delta, &interval);
499 timeout = &interval;
500 } else {
501 timeout = NULL;
502 }
503
504 if (kevent(kq, NULL, 0, &kv, 1, timeout) == -1) {
505 if (errno == EINTR)
506 continue;
507 err(1, "kevent()");
508 }
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();
514 interval.tv_sec = 0;
515 interval.tv_nsec = 0;
516 }
517 }
518
519 if (kv.ident == sf && kv.filter == EVFILT_READ) {
520 while (1) {
521 ssize_t nread;
522
523 nread = recv(sf, buffer, MAX_BUFFER, 0);
524 if (nread == 0) {
525 warnx("recv(sf) returned 0, connection closed");
526 break;
527 }
528 if (nread == -1) {
529 if (errno == EINTR)
530 continue;
531 if (errno == EWOULDBLOCK)
532 break;
533 err(1, "recv()");
534
535 }
536 if (nread < sizeof(struct cfil_msg_hdr))
537 errx(1, "too small");
538 hdr = (struct cfil_msg_hdr *)buffer;
539
540
541 if (hdr->cfm_type != CFM_TYPE_EVENT) {
542 warnx("not a content filter event type %u", hdr->cfm_type);
543 continue;
544 }
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;
548
549 if (verbosity > -2)
550 print_hdr(hdr);
551 if (verbosity > -1)
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);
558 break;
559 }
560 case CFM_OP_SOCKET_CLOSED:
561 case CFM_OP_DISCONNECT_IN:
562 case CFM_OP_DISCONNECT_OUT:
563 if (verbosity > -2)
564 print_hdr(hdr);
565 break;
566 case CFM_OP_DATA_OUT:
567 case CFM_OP_DATA_IN:
568 if (verbosity > -3)
569 print_data_req((struct cfil_msg_data_event *)hdr);
570 break;
571 default:
572 warnx("unknown content filter event op %u", hdr->cfm_op);
573 continue;
574 }
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);
580 continue;
581 }
582 break;
583 case CFM_OP_DATA_OUT:
584 case CFM_OP_DATA_IN:
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);
589
590 if (sock_info == NULL) {
591 warnx("unexpected data message, sock_info is NULL");
592 continue;
593 }
594 break;
595 default:
596 warnx("unknown content filter event op %u", hdr->cfm_op);
597 continue;
598 }
599
600
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;
608
609 send_action_message(CFM_OP_DATA_UPDATE, sock_info, 0);
610 }
611 break;
612 }
613 case CFM_OP_SOCKET_CLOSED: {
614 remove_sock_info(hdr->cfm_sock_id);
615 sock_info = NULL;
616 break;
617 }
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;
621
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;
625 } else {
626 if (drop(sock_info))
627 continue;
628
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;
636 }
637 sock_info->si_out_peek = (mode & MODE_PEEK) ?
638 data_req->cfd_end_offset + peek_inc : 0;
639 } else {
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;
645 }
646 sock_info->si_in_peek = (mode & MODE_PEEK) ?
647 data_req->cfd_end_offset + peek_inc : 0;
648 }
649 } else {
650 break;
651 }
652 }
653 send_action_message(CFM_OP_DATA_UPDATE, sock_info, 0);
654
655 break;
656 }
657 case CFM_OP_DISCONNECT_IN:
658 case CFM_OP_DISCONNECT_OUT: {
659 if (drop(sock_info))
660 continue;
661
662 if ((mode & MODE_PASS)) {
663 sock_info->si_out_pass = CFM_MAX_OFFSET;
664 sock_info->si_in_pass = CFM_MAX_OFFSET;
665
666 send_action_message(CFM_OP_DATA_UPDATE, sock_info, 0);
667 }
668 break;
669 }
670 default:
671 warnx("unkown message op %u", hdr->cfm_op);
672 break;
673 }
674 if (sock_info)
675 last_sock_id = sock_info->si_sock_id;
676 }
677 }
678 if (kv.ident == fdin && kv.filter == EVFILT_READ) {
679 ssize_t nread;
680 uint64_t offset = 0;
681 int nitems;
682 int op = 0;
683
684 nread = getline(&linep, &linecap, stdin);
685 if (nread == -1)
686 errx(1, "getline()");
687
688 if (verbosity > 2)
689 printf("linecap %lu nread %lu\n", linecap, nread);
690 if (nread > 0)
691 linep[nread - 1] = '\0';
692
693 if (verbosity > 2)
694 HexDump(linep, linecap);
695
696 if (*linep == 0)
697 continue;
698
699 if (cmdptr == NULL || argptr == NULL || linecap > cmdlen) {
700 cmdlen = linecap;
701 cmdptr = realloc(cmdptr, cmdlen);
702 argptr = realloc(argptr, cmdlen);
703 }
704
705 /*
706 * Trick to support unisgned and hexadecimal arguments
707 * as I can't figure out sscanf() conversions
708 */
709 nitems = sscanf(linep, "%s %s", cmdptr, argptr);
710 if (nitems == 0) {
711 warnx("I didn't get that...");
712 continue;
713 } else if (nitems > 1) {
714 if (offset_from_str(argptr, &offset) == 0) {
715 warnx("I didn't get that either...");
716 continue;
717 }
718 }
719 if (verbosity > 2)
720 printf("nitems %d %s %s\n", nitems, cmdptr, argptr);
721
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;
726
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)
764 op = CFM_OP_DROP;
765 else if (strcasecmp(cmdptr, "sock") == 0) {
766 last_sock_id = offset;
767 printf("last_sock_id 0x%llx\n", last_sock_id);
768 } else
769 warnx("syntax error");
770
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);
775
776 if (send(sf, &action, sizeof(struct cfil_msg_action), 0) == -1)
777 warn("send()");
778 }
779 }
780 }
781
782 return 0;
783 }
784
785 static const char *
786 basename(const char * str)
787 {
788 const char *last_slash = strrchr(str, '/');
789
790 if (last_slash == NULL)
791 return (str);
792 else
793 return (last_slash + 1);
794 }
795
796 struct option_desc {
797 const char *option;
798 const char *description;
799 int required;
800 };
801
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 */
818 };
819
820 static void
821 usage(const char *cmd)
822 {
823 struct option_desc *option_desc;
824 char *usage_str = malloc(LINE_MAX);
825 size_t usage_len;
826
827 if (usage_str == NULL)
828 err(1, "%s: malloc(%d)", __func__, LINE_MAX);
829
830 usage_len = snprintf(usage_str, LINE_MAX, "# usage: %s ", basename(cmd));
831
832 for (option_desc = option_desc_list; option_desc->option != NULL; option_desc++) {
833 int len;
834
835 if (option_desc->required)
836 len = snprintf(usage_str + usage_len, LINE_MAX - usage_len, "%s ", option_desc->option);
837 else
838 len = snprintf(usage_str + usage_len, LINE_MAX - usage_len, "[%s] ", option_desc->option);
839 if (len < 0)
840 err(1, "%s: snprintf(", __func__);
841
842 usage_len += len;
843 if (usage_len > LINE_MAX)
844 break;
845 }
846 printf("%s\n", usage_str);
847 printf("options:\n");
848
849 for (option_desc = option_desc_list; option_desc->option != NULL; option_desc++) {
850 printf(" %-20s # %s\n", option_desc->option, option_desc->description);
851 }
852
853 }
854
855 int
856 main(int argc, char * const argv[])
857 {
858 int ch;
859 double d;
860 int stats_sock_list = 0;
861 int stats_filt_list = 0;
862 int stats_cfil_stats = 0;
863
864 while ((ch = getopt(argc, argv, "a:d:hik:lm:p:qr:s:t:u:v")) != -1) {
865 switch (ch) {
866 case 'a':
867 auto_start = strtoul(optarg, NULL, 0);
868 break;
869 case 'd': {
870 if (optind >= argc)
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;
892 } else
893 errx(1, "syntax error");
894 break;
895 }
896 case 'h':
897 usage(argv[0]);
898 exit(0);
899 case 'i':
900 mode |= MODE_INTERACTIVE;
901 break;
902 case 'k':
903 mode |= MODE_PEEK;
904 if (offset_from_str(optarg, &peek_inc) == 0)
905 errx(1, "bad peek offset: %s", optarg);
906 break;
907 case 'l':
908 pass_loopback = 1;
909 break;
910 case 'm':
911 max_dump_len = strtoul(optarg, NULL, 0);
912 break;
913 case 'p':
914 mode |= MODE_PASS;
915 if (offset_from_str(optarg, &pass_offset) == 0)
916 errx(1, "bad pass offset: %s", optarg);
917 break;
918 case 'q':
919 verbosity--;
920 break;
921 case 'r':
922 d = strtod(optarg, NULL);
923 if (d < 0 || d > 1)
924 errx(1, "bad drop rate: %s -- it must be between 0 and 1", optarg);
925 random_drop = (uint32_t)(d * UINT32_MAX);
926 break;
927 case 's':
928 if (strcasecmp(optarg, "all") == 0) {
929 stats_sock_list = 1;
930 stats_filt_list = 1;
931 stats_cfil_stats = 1;
932 } else if (strcasecmp(optarg, "sock") == 0) {
933 stats_sock_list = 1;
934 } else if (strcasecmp(optarg, "filt") == 0) {
935 stats_filt_list = 1;
936 } else if (strcasecmp(optarg, "cfil") == 0) {
937 stats_cfil_stats = 1;
938 } else {
939 warnx("# Error: unknown type of statistic: %s", optarg);
940 usage(argv[0]);
941 exit(0);
942 }
943 break;
944 case 't':
945 mode |= MODE_DELAY;
946 delay_ms = strtoul(optarg, NULL, 0);
947 delay_tv.tv_sec = delay_ms / 1000;
948 delay_tv.tv_usec = (delay_ms % 1000) * 1000;
949 break;
950 case 'u':
951 necp_control_unit = (uint32_t)strtoul(optarg, NULL, 0);
952 break;
953 case 'v':
954 verbosity++;
955 break;
956 default:
957 errx(1, "# syntax error, unknow option '%d'", ch);
958 usage(argv[0]);
959 exit(0);
960 }
961 }
962
963 if (stats_filt_list)
964 print_filter_list();
965 if (stats_sock_list)
966 print_socket_list();
967 if (stats_cfil_stats)
968 print_cfil_stats();
969 if (necp_control_unit == 0 && (stats_filt_list || stats_sock_list || stats_cfil_stats))
970 return (0);
971
972 if (necp_control_unit == 0) {
973 warnx("necp filter control unit is 0");
974 usage(argv[0]);
975 exit(EX_USAGE);
976 }
977 doit();
978
979
980 return (0);
981 }
982