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 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) 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 785static const char * 786basename(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 796struct option_desc { 797 const char *option; 798 const char *description; 799 int required; 800}; 801 802struct 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 820static void 821usage(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 855int 856main(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 983