]> git.saurik.com Git - apple/network_cmds.git/blame - cfilutil/cfilutil.c
network_cmds-543.tar.gz
[apple/network_cmds.git] / cfilutil / cfilutil.c
CommitLineData
342c141e 1/*
755a8d69 2 * Copyright (c) 2013-2016 Apple Inc. All rights reserved.
342c141e
A
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
48extern void print_filter_list(void);
49extern void print_socket_list(void);
50extern void print_cfil_stats(void);
51
52#define MAX_BUFFER (65536 + 1024)
53
54#define MAXHEXDUMPCOL 16
55
56
57enum {
58 MODE_NONE = 0,
59 MODE_INTERACTIVE = 0x01,
60 MODE_PEEK = 0x02,
61 MODE_PASS = 0x04,
62 MODE_DELAY = 0x08
63};
64int mode = MODE_NONE;
65
66unsigned long delay_ms = 0;
67struct timeval delay_tv = { 0, 0 };
68long verbosity = 0;
69uint32_t necp_control_unit = 0;
70unsigned long auto_start = 0;
71uint64_t peek_inc = 0;
72uint64_t pass_offset = 0;
73struct timeval now, deadline;
74int sf = -1;
75int pass_loopback = 0;
76uint32_t random_drop = 0;
77uint32_t event_total = 0;
78uint32_t event_dropped = 0;
79
80uint64_t default_in_pass = 0;
81uint64_t default_in_peek = 0;
82uint64_t default_out_pass = 0;
83uint64_t default_out_peek = 0;
84
85unsigned long max_dump_len = 32;
86
87TAILQ_HEAD(sock_info_head, sock_info) sock_info_head = TAILQ_HEAD_INITIALIZER(sock_info_head);
88
89
90struct 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
100static void
101HexDump(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
140void
141print_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
192void
193print_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
214void
215print_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
228struct sock_info *
229find_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
240struct sock_info *
241add_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
257void
258remove_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 */
269int
270set_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
284void
285send_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
320void
321process_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
332int
333set_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
350int
351offset_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
376int
377is_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
402int
403drop(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
421int
422doit()
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)
755a8d69
A
479 err(1, "kevent(sf %d)", sf);
480
481 /*
482 * We can only read from an interactive terminal
483 */
484 if (isatty(fdin)) {
485 bzero(&kv, sizeof(struct kevent));
486 kv.ident = fdin;
487 kv.filter = EVFILT_READ;
488 kv.flags = EV_ADD;
489 if (kevent(kq, &kv, 1, NULL, 0, NULL) == -1)
490 err(1, "kevent(fdin %d)", fdin);
491 }
342c141e
A
492
493 buffer = malloc(MAX_BUFFER);
494 if (buffer == NULL)
495 err(1, "malloc()");
496
497 gettimeofday(&now, NULL);
498
499 while (1) {
500 last_time = now;
501 if (delay_ms && timerisset(&deadline)) {
502 timersub(&deadline, &now, &delta);
503 TIMEVAL_TO_TIMESPEC(&delta, &interval);
504 timeout = &interval;
505 } else {
506 timeout = NULL;
507 }
508
509 if (kevent(kq, NULL, 0, &kv, 1, timeout) == -1) {
510 if (errno == EINTR)
511 continue;
512 err(1, "kevent()");
513 }
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();
519 interval.tv_sec = 0;
520 interval.tv_nsec = 0;
521 }
522 }
523
524 if (kv.ident == sf && kv.filter == EVFILT_READ) {
525 while (1) {
526 ssize_t nread;
527
528 nread = recv(sf, buffer, MAX_BUFFER, 0);
529 if (nread == 0) {
530 warnx("recv(sf) returned 0, connection closed");
531 break;
532 }
533 if (nread == -1) {
534 if (errno == EINTR)
535 continue;
536 if (errno == EWOULDBLOCK)
537 break;
538 err(1, "recv()");
539
540 }
541 if (nread < sizeof(struct cfil_msg_hdr))
542 errx(1, "too small");
543 hdr = (struct cfil_msg_hdr *)buffer;
544
545
546 if (hdr->cfm_type != CFM_TYPE_EVENT) {
547 warnx("not a content filter event type %u", hdr->cfm_type);
548 continue;
549 }
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;
553
554 if (verbosity > -2)
555 print_hdr(hdr);
556 if (verbosity > -1)
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);
563 break;
564 }
565 case CFM_OP_SOCKET_CLOSED:
566 case CFM_OP_DISCONNECT_IN:
567 case CFM_OP_DISCONNECT_OUT:
568 if (verbosity > -2)
569 print_hdr(hdr);
570 break;
571 case CFM_OP_DATA_OUT:
572 case CFM_OP_DATA_IN:
573 if (verbosity > -3)
574 print_data_req((struct cfil_msg_data_event *)hdr);
575 break;
576 default:
577 warnx("unknown content filter event op %u", hdr->cfm_op);
578 continue;
579 }
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);
585 continue;
586 }
587 break;
588 case CFM_OP_DATA_OUT:
589 case CFM_OP_DATA_IN:
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);
594
595 if (sock_info == NULL) {
596 warnx("unexpected data message, sock_info is NULL");
597 continue;
598 }
599 break;
600 default:
601 warnx("unknown content filter event op %u", hdr->cfm_op);
602 continue;
603 }
604
605
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;
613
614 send_action_message(CFM_OP_DATA_UPDATE, sock_info, 0);
615 }
616 break;
617 }
618 case CFM_OP_SOCKET_CLOSED: {
619 remove_sock_info(hdr->cfm_sock_id);
620 sock_info = NULL;
621 break;
622 }
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;
626
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;
630 } else {
631 if (drop(sock_info))
632 continue;
633
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;
641 }
642 sock_info->si_out_peek = (mode & MODE_PEEK) ?
643 data_req->cfd_end_offset + peek_inc : 0;
644 } else {
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;
650 }
651 sock_info->si_in_peek = (mode & MODE_PEEK) ?
652 data_req->cfd_end_offset + peek_inc : 0;
653 }
654 } else {
655 break;
656 }
657 }
658 send_action_message(CFM_OP_DATA_UPDATE, sock_info, 0);
659
660 break;
661 }
662 case CFM_OP_DISCONNECT_IN:
663 case CFM_OP_DISCONNECT_OUT: {
664 if (drop(sock_info))
665 continue;
666
667 if ((mode & MODE_PASS)) {
668 sock_info->si_out_pass = CFM_MAX_OFFSET;
669 sock_info->si_in_pass = CFM_MAX_OFFSET;
670
671 send_action_message(CFM_OP_DATA_UPDATE, sock_info, 0);
672 }
673 break;
674 }
675 default:
676 warnx("unkown message op %u", hdr->cfm_op);
677 break;
678 }
679 if (sock_info)
680 last_sock_id = sock_info->si_sock_id;
681 }
682 }
683 if (kv.ident == fdin && kv.filter == EVFILT_READ) {
684 ssize_t nread;
685 uint64_t offset = 0;
686 int nitems;
687 int op = 0;
688
689 nread = getline(&linep, &linecap, stdin);
690 if (nread == -1)
691 errx(1, "getline()");
692
693 if (verbosity > 2)
694 printf("linecap %lu nread %lu\n", linecap, nread);
695 if (nread > 0)
696 linep[nread - 1] = '\0';
697
698 if (verbosity > 2)
699 HexDump(linep, linecap);
700
701 if (*linep == 0)
702 continue;
703
704 if (cmdptr == NULL || argptr == NULL || linecap > cmdlen) {
705 cmdlen = linecap;
706 cmdptr = realloc(cmdptr, cmdlen);
707 argptr = realloc(argptr, cmdlen);
708 }
709
710 /*
711 * Trick to support unisgned and hexadecimal arguments
712 * as I can't figure out sscanf() conversions
713 */
714 nitems = sscanf(linep, "%s %s", cmdptr, argptr);
715 if (nitems == 0) {
716 warnx("I didn't get that...");
717 continue;
718 } else if (nitems > 1) {
719 if (offset_from_str(argptr, &offset) == 0) {
720 warnx("I didn't get that either...");
721 continue;
722 }
723 }
724 if (verbosity > 2)
725 printf("nitems %d %s %s\n", nitems, cmdptr, argptr);
726
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;
731
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)
769 op = CFM_OP_DROP;
770 else if (strcasecmp(cmdptr, "sock") == 0) {
771 last_sock_id = offset;
772 printf("last_sock_id 0x%llx\n", last_sock_id);
773 } else
774 warnx("syntax error");
775
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);
780
781 if (send(sf, &action, sizeof(struct cfil_msg_action), 0) == -1)
782 warn("send()");
783 }
784 }
785 }
786
787 return 0;
788}
789
790static const char *
791basename(const char * str)
792{
793 const char *last_slash = strrchr(str, '/');
794
795 if (last_slash == NULL)
796 return (str);
797 else
798 return (last_slash + 1);
799}
800
801struct option_desc {
802 const char *option;
803 const char *description;
804 int required;
805};
806
807struct 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 */
823};
824
825static void
826usage(const char *cmd)
827{
828 struct option_desc *option_desc;
829 char *usage_str = malloc(LINE_MAX);
830 size_t usage_len;
831
832 if (usage_str == NULL)
833 err(1, "%s: malloc(%d)", __func__, LINE_MAX);
834
835 usage_len = snprintf(usage_str, LINE_MAX, "# usage: %s ", basename(cmd));
836
837 for (option_desc = option_desc_list; option_desc->option != NULL; option_desc++) {
838 int len;
839
840 if (option_desc->required)
841 len = snprintf(usage_str + usage_len, LINE_MAX - usage_len, "%s ", option_desc->option);
842 else
843 len = snprintf(usage_str + usage_len, LINE_MAX - usage_len, "[%s] ", option_desc->option);
844 if (len < 0)
845 err(1, "%s: snprintf(", __func__);
846
847 usage_len += len;
848 if (usage_len > LINE_MAX)
849 break;
850 }
851 printf("%s\n", usage_str);
852 printf("options:\n");
853
854 for (option_desc = option_desc_list; option_desc->option != NULL; option_desc++) {
855 printf(" %-20s # %s\n", option_desc->option, option_desc->description);
856 }
857
858}
859
860int
861main(int argc, char * const argv[])
862{
863 int ch;
864 double d;
865 int stats_sock_list = 0;
866 int stats_filt_list = 0;
867 int stats_cfil_stats = 0;
868
869 while ((ch = getopt(argc, argv, "a:d:hik:lm:p:qr:s:t:u:v")) != -1) {
870 switch (ch) {
871 case 'a':
872 auto_start = strtoul(optarg, NULL, 0);
873 break;
874 case 'd': {
875 if (optind >= argc)
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;
897 } else
898 errx(1, "syntax error");
899 break;
900 }
901 case 'h':
902 usage(argv[0]);
903 exit(0);
904 case 'i':
905 mode |= MODE_INTERACTIVE;
906 break;
907 case 'k':
908 mode |= MODE_PEEK;
909 if (offset_from_str(optarg, &peek_inc) == 0)
910 errx(1, "bad peek offset: %s", optarg);
911 break;
912 case 'l':
913 pass_loopback = 1;
914 break;
915 case 'm':
916 max_dump_len = strtoul(optarg, NULL, 0);
917 break;
918 case 'p':
919 mode |= MODE_PASS;
920 if (offset_from_str(optarg, &pass_offset) == 0)
921 errx(1, "bad pass offset: %s", optarg);
922 break;
923 case 'q':
924 verbosity--;
925 break;
926 case 'r':
927 d = strtod(optarg, NULL);
928 if (d < 0 || d > 1)
929 errx(1, "bad drop rate: %s -- it must be between 0 and 1", optarg);
930 random_drop = (uint32_t)(d * UINT32_MAX);
931 break;
932 case 's':
933 if (strcasecmp(optarg, "all") == 0) {
934 stats_sock_list = 1;
935 stats_filt_list = 1;
936 stats_cfil_stats = 1;
937 } else if (strcasecmp(optarg, "sock") == 0) {
938 stats_sock_list = 1;
939 } else if (strcasecmp(optarg, "filt") == 0) {
940 stats_filt_list = 1;
941 } else if (strcasecmp(optarg, "cfil") == 0) {
942 stats_cfil_stats = 1;
943 } else {
944 warnx("# Error: unknown type of statistic: %s", optarg);
945 usage(argv[0]);
946 exit(0);
947 }
948 break;
949 case 't':
950 mode |= MODE_DELAY;
951 delay_ms = strtoul(optarg, NULL, 0);
952 delay_tv.tv_sec = delay_ms / 1000;
953 delay_tv.tv_usec = (delay_ms % 1000) * 1000;
954 break;
955 case 'u':
956 necp_control_unit = (uint32_t)strtoul(optarg, NULL, 0);
957 break;
958 case 'v':
959 verbosity++;
960 break;
961 default:
962 errx(1, "# syntax error, unknow option '%d'", ch);
963 usage(argv[0]);
964 exit(0);
965 }
966 }
967
968 if (stats_filt_list)
969 print_filter_list();
970 if (stats_sock_list)
971 print_socket_list();
972 if (stats_cfil_stats)
973 print_cfil_stats();
974 if (necp_control_unit == 0 && (stats_filt_list || stats_sock_list || stats_cfil_stats))
975 return (0);
976
977 if (necp_control_unit == 0) {
978 warnx("necp filter control unit is 0");
979 usage(argv[0]);
980 exit(EX_USAGE);
981 }
982 doit();
983
984
985 return (0);
986}
987