From 5fd83771641d15c418f747bd343ba6738d3875f7 Mon Sep 17 00:00:00 2001 From: Cameron Katri Date: Sun, 9 May 2021 14:20:58 -0400 Subject: Import macOS userland adv_cmds-176 basic_cmds-55 bootstrap_cmds-116.100.1 developer_cmds-66 diskdev_cmds-667.40.1 doc_cmds-53.60.1 file_cmds-321.40.3 mail_cmds-35 misc_cmds-34 network_cmds-606.40.1 patch_cmds-17 remote_cmds-63 shell_cmds-216.60.1 system_cmds-880.60.2 text_cmds-106 --- network_cmds/cfilutil/cfilutil.c | 987 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 987 insertions(+) create mode 100644 network_cmds/cfilutil/cfilutil.c (limited to 'network_cmds/cfilutil/cfilutil.c') diff --git a/network_cmds/cfilutil/cfilutil.c b/network_cmds/cfilutil/cfilutil.c new file mode 100644 index 0000000..4aaa719 --- /dev/null +++ b/network_cmds/cfilutil/cfilutil.c @@ -0,0 +1,987 @@ +/* + * Copyright (c) 2013-2016 Apple Inc. All rights reserved. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. The rights granted to you under the License + * may not be used to create, or enable the creation or redistribution of, + * unlawful or unlicensed copies of an Apple operating system, or to + * circumvent, violate, or enable the circumvention or violation of, any + * terms of an Apple operating system software license agreement. + * + * Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +extern void print_filter_list(void); +extern void print_socket_list(void); +extern void print_cfil_stats(void); + +#define MAX_BUFFER (65536 + 1024) + +#define MAXHEXDUMPCOL 16 + + +enum { + MODE_NONE = 0, + MODE_INTERACTIVE = 0x01, + MODE_PEEK = 0x02, + MODE_PASS = 0x04, + MODE_DELAY = 0x08 +}; +int mode = MODE_NONE; + +unsigned long delay_ms = 0; +struct timeval delay_tv = { 0, 0 }; +long verbosity = 0; +uint32_t necp_control_unit = 0; +unsigned long auto_start = 0; +uint64_t peek_inc = 0; +uint64_t pass_offset = 0; +struct timeval now, deadline; +int sf = -1; +int pass_loopback = 0; +uint32_t random_drop = 0; +uint32_t event_total = 0; +uint32_t event_dropped = 0; + +uint64_t default_in_pass = 0; +uint64_t default_in_peek = 0; +uint64_t default_out_pass = 0; +uint64_t default_out_peek = 0; + +unsigned long max_dump_len = 32; + +TAILQ_HEAD(sock_info_head, sock_info) sock_info_head = TAILQ_HEAD_INITIALIZER(sock_info_head); + + +struct sock_info { + TAILQ_ENTRY(sock_info) si_link; + cfil_sock_id_t si_sock_id; + struct timeval si_deadline; + uint64_t si_in_pass; + uint64_t si_in_peek; + uint64_t si_out_pass; + uint64_t si_out_peek; +}; + +static void +HexDump(void *data, size_t len) +{ + size_t i, j, k; + unsigned char *ptr = (unsigned char *)data; + unsigned char buf[32 + 3 * MAXHEXDUMPCOL + 2 + MAXHEXDUMPCOL + 1]; + + for (i = 0; i < len; i += MAXHEXDUMPCOL) { + k = snprintf((char *)buf, sizeof(buf), "\t0x%04lx: ", i); + for (j = i; j < i + MAXHEXDUMPCOL; j++) { + if (j < len) { + unsigned char msnbl = ptr[j] >> 4; + unsigned char lsnbl = ptr[j] & 0x0f; + + buf[k++] = msnbl < 10 ? msnbl + '0' : msnbl + 'a' - 10; + buf[k++] = lsnbl < 10 ? lsnbl + '0' : lsnbl + 'a' - 10; + } else { + buf[k++] = ' '; + buf[k++] = ' '; + } + if ((j % 2) == 1) + buf[k++] = ' '; + if ((j % MAXHEXDUMPCOL) == MAXHEXDUMPCOL - 1) + buf[k++] = ' '; + } + + buf[k++] = ' '; + buf[k++] = ' '; + + for (j = i; j < i + MAXHEXDUMPCOL && j < len; j++) { + if (isprint(ptr[j])) + buf[k++] = ptr[j]; + else + buf[k++] = '.'; + } + buf[k] = 0; + printf("%s\n", buf); + } +} + +void +print_hdr(struct cfil_msg_hdr *hdr) +{ + const char *typestr = "unknown"; + const char *opstr = "unknown"; + + if (hdr->cfm_type == CFM_TYPE_EVENT) { + typestr = "event"; + switch (hdr->cfm_op) { + case CFM_OP_SOCKET_ATTACHED: + opstr = "attached"; + break; + case CFM_OP_SOCKET_CLOSED: + opstr = "closed"; + break; + case CFM_OP_DATA_OUT: + opstr = "dataout"; + break; + case CFM_OP_DATA_IN: + opstr = "datain"; + break; + case CFM_OP_DISCONNECT_OUT: + opstr = "disconnectout"; + break; + case CFM_OP_DISCONNECT_IN: + opstr = "disconnectin"; + break; + + default: + break; + } + } else if (hdr->cfm_type == CFM_TYPE_ACTION) { + typestr = "action"; + switch (hdr->cfm_op) { + case CFM_OP_DATA_UPDATE: + opstr = "update"; + break; + case CFM_OP_DROP: + opstr = "drop"; + break; + + default: + break; + } + + } + printf("%s %s len %u version %u type %u op %u sock_id 0x%llx\n", + typestr, opstr, + hdr->cfm_len, hdr->cfm_version, hdr->cfm_type, + hdr->cfm_op, hdr->cfm_sock_id); +} + +void +print_data_req(struct cfil_msg_data_event *data_req) +{ + size_t datalen; + void *databuf; + + if (verbosity <= 0) + return; + + print_hdr(&data_req->cfd_msghdr); + + printf(" start %llu end %llu\n", + data_req->cfd_start_offset, data_req->cfd_end_offset); + + datalen = (size_t)(data_req->cfd_end_offset - data_req->cfd_start_offset); + + databuf = (void *)(data_req + 1); + + if (verbosity > 1) + HexDump(databuf, MIN(datalen, max_dump_len)); +} + +void +print_action_msg(struct cfil_msg_action *action) +{ + if (verbosity <= 0) + return; + + print_hdr(&action->cfa_msghdr); + + if (action->cfa_msghdr.cfm_op == CFM_OP_DATA_UPDATE) + printf(" out pass %llu peek %llu in pass %llu peek %llu\n", + action->cfa_out_pass_offset, action->cfa_out_peek_offset, + action->cfa_in_pass_offset, action->cfa_in_peek_offset); +} + +struct sock_info * +find_sock_info(cfil_sock_id_t sockid) +{ + struct sock_info *sock_info; + + TAILQ_FOREACH(sock_info, &sock_info_head, si_link) { + if (sock_info->si_sock_id == sockid) + return (sock_info); + } + return (NULL); +} + +struct sock_info * +add_sock_info(cfil_sock_id_t sockid) +{ + struct sock_info *sock_info; + + if (find_sock_info(sockid) != NULL) + return (NULL); + + sock_info = calloc(1, sizeof(struct sock_info)); + if (sock_info == NULL) + err(EX_OSERR, "calloc()"); + sock_info->si_sock_id = sockid; + TAILQ_INSERT_TAIL(&sock_info_head, sock_info, si_link); + + return (sock_info); +} + +void +remove_sock_info(cfil_sock_id_t sockid) +{ + struct sock_info *sock_info = find_sock_info(sockid); + + if (sock_info != NULL) { + TAILQ_REMOVE(&sock_info_head, sock_info, si_link); + free(sock_info); + } +} + +/* return 0 if timer is already set */ +int +set_sock_info_deadline(struct sock_info *sock_info) +{ + if (timerisset(&sock_info->si_deadline)) + return (0); + + timeradd(&now, &sock_info->si_deadline, &sock_info->si_deadline); + + if (!timerisset(&deadline)) { + timeradd(&now, &delay_tv, &deadline); + } + + return (1); +} + +void +send_action_message(uint32_t op, struct sock_info *sock_info, int nodelay) +{ + struct cfil_msg_action action; + + if (!nodelay && delay_ms) { + set_sock_info_deadline(sock_info); + return; + } + bzero(&action, sizeof(struct cfil_msg_action)); + action.cfa_msghdr.cfm_len = sizeof(struct cfil_msg_action); + action.cfa_msghdr.cfm_version = CFM_VERSION_CURRENT; + action.cfa_msghdr.cfm_type = CFM_TYPE_ACTION; + action.cfa_msghdr.cfm_op = op; + action.cfa_msghdr.cfm_sock_id = sock_info->si_sock_id; + switch (op) { + case CFM_OP_DATA_UPDATE: + action.cfa_out_pass_offset = sock_info->si_out_pass; + action.cfa_out_peek_offset = sock_info->si_out_peek; + action.cfa_in_pass_offset = sock_info->si_in_pass; + action.cfa_in_peek_offset = sock_info->si_in_peek; + break; + + default: + break; + } + + if (verbosity > -1) + print_action_msg(&action); + + if (send(sf, &action, sizeof(struct cfil_msg_action), 0) == -1) + warn("send()"); + + timerclear(&sock_info->si_deadline); +} + +void +process_delayed_actions() +{ + struct sock_info *sock_info; + + TAILQ_FOREACH(sock_info, &sock_info_head, si_link) { + if (timerisset(&sock_info->si_deadline) && + timercmp(&sock_info->si_deadline, &now, >=)) + send_action_message(CFM_OP_DATA_UPDATE, sock_info, 1); + } +} + +int +set_non_blocking(int fd) +{ + int flags; + + flags = fcntl(fd, F_GETFL); + if (flags == -1) { + warn("fcntl(F_GETFL)"); + return (-1); + } + flags |= O_NONBLOCK; + if (fcntl(fd, F_SETFL, flags) == -1) { + warn("fcntl(F_SETFL)"); + return (-1); + } + return (0); +} + +int +offset_from_str(const char *str, uint64_t *ret_val) +{ + char *endptr; + uint64_t offset; + int success = 1; + + if (strcasecmp(str, "max") == 0 || strcasecmp(str, "all") == 0) + offset = CFM_MAX_OFFSET; + else { + offset = strtoull(str, &endptr, 0); + if (*str == '\0' || *endptr != '\0') + success = 0; + } + if (success) + *ret_val = offset; + return (success); +} + +#define IN6_IS_ADDR_V4MAPPED_LOOPBACK(a) \ + ((*(const __uint32_t *)(const void *)(&(a)->s6_addr[0]) == 0) && \ + (*(const __uint32_t *)(const void *)(&(a)->s6_addr[4]) == 0) && \ + (*(const __uint32_t *)(const void *)(&(a)->s6_addr[8]) == ntohl(0x0000ffff)) && \ + (*(const __uint32_t *)(const void *)(&(a)->s6_addr[12]) == ntohl(INADDR_LOOPBACK))) + + +int +is_loopback(struct cfil_msg_data_event *data_req) +{ + if (data_req->cfc_dst.sa.sa_family == AF_INET && + ntohl(data_req->cfc_dst.sin.sin_addr.s_addr) == INADDR_LOOPBACK) + return (1); + if (data_req->cfc_dst.sa.sa_family == AF_INET6 && + IN6_IS_ADDR_LOOPBACK(&data_req->cfc_dst.sin6.sin6_addr)) + return (1); + if (data_req->cfc_dst.sa.sa_family == AF_INET6 && + IN6_IS_ADDR_V4MAPPED_LOOPBACK(&data_req->cfc_dst.sin6.sin6_addr)) + return (1); + + if (data_req->cfc_src.sa.sa_family == AF_INET && + ntohl(data_req->cfc_src.sin.sin_addr.s_addr) == INADDR_LOOPBACK) + return (1); + if (data_req->cfc_src.sa.sa_family == AF_INET6 && + IN6_IS_ADDR_LOOPBACK(&data_req->cfc_src.sin6.sin6_addr)) + return (1); + if (data_req->cfc_src.sa.sa_family == AF_INET6 && + IN6_IS_ADDR_V4MAPPED_LOOPBACK(&data_req->cfc_src.sin6.sin6_addr)) + return (1); + + return (0); +} + +int +drop(struct sock_info *sock_info) +{ + event_total++; + if (random_drop > 0) { + uint32_t r = arc4random(); + if (r <= random_drop) { + event_dropped++; + printf("dropping 0x%llx dropped %u total %u rate %f\n", + sock_info->si_sock_id, + event_dropped, event_total, + (double)event_dropped/(double)event_total * 100); + send_action_message(CFM_OP_DROP, sock_info, 0); + return (1); + } + } + return (0); +} + +int +doit() +{ + struct sockaddr_ctl sac; + struct ctl_info ctl_info; + void *buffer = NULL; + struct cfil_msg_hdr *hdr; + int kq = -1; + struct kevent kv; + int fdin = fileno(stdin); + char *linep = NULL; + size_t linecap = 0; + char *cmdptr = NULL; + char *argptr = NULL; + size_t cmdlen = 0; + struct cfil_msg_action action; + cfil_sock_id_t last_sock_id = 0; + struct sock_info *sock_info = NULL; + struct timeval last_time, elapsed, delta; + struct timespec interval, *timeout = NULL; + + kq = kqueue(); + if (kq == -1) + err(1, "kqueue()"); + + sf = socket(PF_SYSTEM, SOCK_DGRAM, SYSPROTO_CONTROL); + if (sf == -1) + err(1, "socket()"); + + bzero(&ctl_info, sizeof(struct ctl_info)); + strlcpy(ctl_info.ctl_name, CONTENT_FILTER_CONTROL_NAME, sizeof(ctl_info.ctl_name)); + if (ioctl(sf, CTLIOCGINFO, &ctl_info) == -1) + err(1, "ioctl(CTLIOCGINFO)"); + + if (fcntl(sf, F_SETNOSIGPIPE, 1) == -1) + err(1, "fcntl(F_SETNOSIGPIPE)"); + + bzero(&sac, sizeof(struct sockaddr_ctl)); + sac.sc_len = sizeof(struct sockaddr_ctl); + sac.sc_family = AF_SYSTEM; + sac.ss_sysaddr = AF_SYS_CONTROL; + sac.sc_id = ctl_info.ctl_id; + + if (connect(sf, (struct sockaddr *)&sac, sizeof(struct sockaddr_ctl)) == -1) + err(1, "connect()"); + + if (set_non_blocking(sf) == -1) + err(1, "set_non_blocking(sf)"); + + if (setsockopt(sf, SYSPROTO_CONTROL, CFIL_OPT_NECP_CONTROL_UNIT, + &necp_control_unit, sizeof(uint32_t)) == -1) + err(1, "setsockopt(CFIL_OPT_NECP_CONTROL_UNIT, %u)", necp_control_unit); + + bzero(&kv, sizeof(struct kevent)); + kv.ident = sf; + kv.filter = EVFILT_READ; + kv.flags = EV_ADD; + if (kevent(kq, &kv, 1, NULL, 0, NULL) == -1) + err(1, "kevent(sf %d)", sf); + + /* + * We can only read from an interactive terminal + */ + if (isatty(fdin)) { + bzero(&kv, sizeof(struct kevent)); + kv.ident = fdin; + kv.filter = EVFILT_READ; + kv.flags = EV_ADD; + if (kevent(kq, &kv, 1, NULL, 0, NULL) == -1) + err(1, "kevent(fdin %d)", fdin); + } + + buffer = malloc(MAX_BUFFER); + if (buffer == NULL) + err(1, "malloc()"); + + gettimeofday(&now, NULL); + + while (1) { + last_time = now; + if (delay_ms && timerisset(&deadline)) { + timersub(&deadline, &now, &delta); + TIMEVAL_TO_TIMESPEC(&delta, &interval); + timeout = &interval; + } else { + timeout = NULL; + } + + if (kevent(kq, NULL, 0, &kv, 1, timeout) == -1) { + if (errno == EINTR) + continue; + err(1, "kevent()"); + } + gettimeofday(&now, NULL); + timersub(&now, &last_time, &elapsed); + if (delay_ms && timerisset(&deadline)) { + if (timercmp(&now, &deadline, >=)) { + process_delayed_actions(); + interval.tv_sec = 0; + interval.tv_nsec = 0; + } + } + + if (kv.ident == sf && kv.filter == EVFILT_READ) { + while (1) { + ssize_t nread; + + nread = recv(sf, buffer, MAX_BUFFER, 0); + if (nread == 0) { + warnx("recv(sf) returned 0, connection closed"); + break; + } + if (nread == -1) { + if (errno == EINTR) + continue; + if (errno == EWOULDBLOCK) + break; + err(1, "recv()"); + + } + if (nread < sizeof(struct cfil_msg_hdr)) + errx(1, "too small"); + hdr = (struct cfil_msg_hdr *)buffer; + + + if (hdr->cfm_type != CFM_TYPE_EVENT) { + warnx("not a content filter event type %u", hdr->cfm_type); + continue; + } + switch (hdr->cfm_op) { + case CFM_OP_SOCKET_ATTACHED: { + struct cfil_msg_sock_attached *msg_attached = (struct cfil_msg_sock_attached *)hdr; + + if (verbosity > -2) + print_hdr(hdr); + if (verbosity > -1) + printf(" fam %d type %d proto %d pid %u epid %u\n", + msg_attached->cfs_sock_family, + msg_attached->cfs_sock_type, + msg_attached->cfs_sock_protocol, + msg_attached->cfs_pid, + msg_attached->cfs_e_pid); + break; + } + case CFM_OP_SOCKET_CLOSED: + case CFM_OP_DISCONNECT_IN: + case CFM_OP_DISCONNECT_OUT: + if (verbosity > -2) + print_hdr(hdr); + break; + case CFM_OP_DATA_OUT: + case CFM_OP_DATA_IN: + if (verbosity > -3) + print_data_req((struct cfil_msg_data_event *)hdr); + break; + default: + warnx("unknown content filter event op %u", hdr->cfm_op); + continue; + } + switch (hdr->cfm_op) { + case CFM_OP_SOCKET_ATTACHED: + sock_info = add_sock_info(hdr->cfm_sock_id); + if (sock_info == NULL) { + warnx("sock_id %llx already exists", hdr->cfm_sock_id); + continue; + } + break; + case CFM_OP_DATA_OUT: + case CFM_OP_DATA_IN: + case CFM_OP_DISCONNECT_IN: + case CFM_OP_DISCONNECT_OUT: + case CFM_OP_SOCKET_CLOSED: + sock_info = find_sock_info(hdr->cfm_sock_id); + + if (sock_info == NULL) { + warnx("unexpected data message, sock_info is NULL"); + continue; + } + break; + default: + warnx("unknown content filter event op %u", hdr->cfm_op); + continue; + } + + + switch (hdr->cfm_op) { + case CFM_OP_SOCKET_ATTACHED: { + if ((mode & MODE_PASS) || (mode & MODE_PEEK) || auto_start) { + sock_info->si_out_pass = default_out_pass; + sock_info->si_out_peek = (mode & MODE_PEEK) ? peek_inc : (mode & MODE_PASS) ? CFM_MAX_OFFSET : default_out_peek; + sock_info->si_in_pass = default_in_pass; + sock_info->si_in_peek = (mode & MODE_PEEK) ? peek_inc : (mode & MODE_PASS) ? CFM_MAX_OFFSET : default_in_peek; + + send_action_message(CFM_OP_DATA_UPDATE, sock_info, 0); + } + break; + } + case CFM_OP_SOCKET_CLOSED: { + remove_sock_info(hdr->cfm_sock_id); + sock_info = NULL; + break; + } + case CFM_OP_DATA_OUT: + case CFM_OP_DATA_IN: { + struct cfil_msg_data_event *data_req = (struct cfil_msg_data_event *)hdr; + + if (pass_loopback && is_loopback(data_req)) { + sock_info->si_out_pass = CFM_MAX_OFFSET; + sock_info->si_in_pass = CFM_MAX_OFFSET; + } else { + if (drop(sock_info)) + continue; + + if ((mode & MODE_PASS)) { + if (data_req->cfd_msghdr.cfm_op == CFM_OP_DATA_OUT) { + if (pass_offset == 0 || pass_offset == CFM_MAX_OFFSET) + sock_info->si_out_pass = data_req->cfd_end_offset; + else if (data_req->cfd_end_offset > pass_offset) { + sock_info->si_out_pass = CFM_MAX_OFFSET; + sock_info->si_in_pass = CFM_MAX_OFFSET; + } + sock_info->si_out_peek = (mode & MODE_PEEK) ? + data_req->cfd_end_offset + peek_inc : 0; + } else { + if (pass_offset == 0 || pass_offset == CFM_MAX_OFFSET) + sock_info->si_in_pass = data_req->cfd_end_offset; + else if (data_req->cfd_end_offset > pass_offset) { + sock_info->si_out_pass = CFM_MAX_OFFSET; + sock_info->si_in_pass = CFM_MAX_OFFSET; + } + sock_info->si_in_peek = (mode & MODE_PEEK) ? + data_req->cfd_end_offset + peek_inc : 0; + } + } else { + break; + } + } + send_action_message(CFM_OP_DATA_UPDATE, sock_info, 0); + + break; + } + case CFM_OP_DISCONNECT_IN: + case CFM_OP_DISCONNECT_OUT: { + if (drop(sock_info)) + continue; + + if ((mode & MODE_PASS)) { + sock_info->si_out_pass = CFM_MAX_OFFSET; + sock_info->si_in_pass = CFM_MAX_OFFSET; + + send_action_message(CFM_OP_DATA_UPDATE, sock_info, 0); + } + break; + } + default: + warnx("unkown message op %u", hdr->cfm_op); + break; + } + if (sock_info) + last_sock_id = sock_info->si_sock_id; + } + } + if (kv.ident == fdin && kv.filter == EVFILT_READ) { + ssize_t nread; + uint64_t offset = 0; + int nitems; + int op = 0; + + nread = getline(&linep, &linecap, stdin); + if (nread == -1) + errx(1, "getline()"); + + if (verbosity > 2) + printf("linecap %lu nread %lu\n", linecap, nread); + if (nread > 0) + linep[nread - 1] = '\0'; + + if (verbosity > 2) + HexDump(linep, linecap); + + if (*linep == 0) + continue; + + if (cmdptr == NULL || argptr == NULL || linecap > cmdlen) { + cmdlen = linecap; + cmdptr = realloc(cmdptr, cmdlen); + argptr = realloc(argptr, cmdlen); + } + + /* + * Trick to support unisgned and hexadecimal arguments + * as I can't figure out sscanf() conversions + */ + nitems = sscanf(linep, "%s %s", cmdptr, argptr); + if (nitems == 0) { + warnx("I didn't get that..."); + continue; + } else if (nitems > 1) { + if (offset_from_str(argptr, &offset) == 0) { + warnx("I didn't get that either..."); + continue; + } + } + if (verbosity > 2) + printf("nitems %d %s %s\n", nitems, cmdptr, argptr); + + bzero(&action, sizeof(struct cfil_msg_action)); + action.cfa_msghdr.cfm_len = sizeof(struct cfil_msg_action); + action.cfa_msghdr.cfm_version = CFM_VERSION_CURRENT; + action.cfa_msghdr.cfm_type = CFM_TYPE_ACTION; + + if (strcasecmp(cmdptr, "passout") == 0 && nitems > 1) { + op = CFM_OP_DATA_UPDATE; + action.cfa_out_pass_offset = offset; + } else if (strcasecmp(cmdptr, "passin") == 0 && nitems > 1) { + op = CFM_OP_DATA_UPDATE; + action.cfa_in_pass_offset = offset; + } else if (strcasecmp(cmdptr, "pass") == 0 && nitems > 1) { + op = CFM_OP_DATA_UPDATE; + action.cfa_out_pass_offset = offset; + action.cfa_in_pass_offset = offset; + } else if (strcasecmp(cmdptr, "peekout") == 0 && nitems > 1) { + op = CFM_OP_DATA_UPDATE; + action.cfa_out_peek_offset = offset; + } else if (strcasecmp(cmdptr, "peekin") == 0 && nitems > 1) { + op = CFM_OP_DATA_UPDATE; + action.cfa_in_peek_offset = offset; + } else if (strcasecmp(cmdptr, "peek") == 0 && nitems > 1) { + op = CFM_OP_DATA_UPDATE; + action.cfa_out_peek_offset = offset; + action.cfa_in_peek_offset = offset; + } else if (strcasecmp(cmdptr, "start") == 0) { + op = CFM_OP_DATA_UPDATE; + action.cfa_out_pass_offset = 0; + action.cfa_out_peek_offset = CFM_MAX_OFFSET; + action.cfa_in_pass_offset = 0; + action.cfa_in_peek_offset = CFM_MAX_OFFSET; + } else if (strcasecmp(cmdptr, "peekall") == 0) { + op = CFM_OP_DATA_UPDATE; + action.cfa_out_peek_offset = CFM_MAX_OFFSET; + action.cfa_in_peek_offset = CFM_MAX_OFFSET; + } else if (strcasecmp(cmdptr, "passall") == 0) { + op = CFM_OP_DATA_UPDATE; + action.cfa_out_pass_offset = CFM_MAX_OFFSET; + action.cfa_out_peek_offset = CFM_MAX_OFFSET; + action.cfa_in_pass_offset = CFM_MAX_OFFSET; + action.cfa_in_peek_offset = CFM_MAX_OFFSET; + } else if (strcasecmp(cmdptr, "drop") == 0) + op = CFM_OP_DROP; + else if (strcasecmp(cmdptr, "sock") == 0) { + last_sock_id = offset; + printf("last_sock_id 0x%llx\n", last_sock_id); + } else + warnx("syntax error"); + + if (op == CFM_OP_DATA_UPDATE || op == CFM_OP_DROP) { + action.cfa_msghdr.cfm_op = op; + action.cfa_msghdr.cfm_sock_id = last_sock_id; + print_action_msg(&action); + + if (send(sf, &action, sizeof(struct cfil_msg_action), 0) == -1) + warn("send()"); + } + } + } + + return 0; +} + +static const char * +basename(const char * str) +{ + const char *last_slash = strrchr(str, '/'); + + if (last_slash == NULL) + return (str); + else + return (last_slash + 1); +} + +struct option_desc { + const char *option; + const char *description; + int required; +}; + +struct option_desc option_desc_list[] = { + { "-a offset", "auto start with offset", 0 }, + { "-d offset value", "default offset value for passin, peekin, passout, peekout, pass, peek", 0 }, + { "-h", "dsiplay this help", 0 }, + { "-i", "interactive mode", 0 }, + { "-k increment", "peek mode with increment", 0 }, + {"-l", "pass loopback", 0 }, + { "-m length", "max dump length", 0 }, + { "-p offset", "pass mode (all or after given offset if > 0)", 0 }, + { "-q", "decrease verbose level", 0 }, + { "-r random", "random drop rate", 0 }, + { "-s ", "display content filter statistics (all, sock, filt, cfil)", 0 }, + { "-t delay", "pass delay in microseconds", 0 }, + { "-u unit", "NECP filter control unit", 1 }, + { "-v", "increase verbose level", 0 }, + { NULL, NULL, 0 } /* Mark end of list */ +}; + +static void +usage(const char *cmd) +{ + struct option_desc *option_desc; + char *usage_str = malloc(LINE_MAX); + size_t usage_len; + + if (usage_str == NULL) + err(1, "%s: malloc(%d)", __func__, LINE_MAX); + + usage_len = snprintf(usage_str, LINE_MAX, "# usage: %s ", basename(cmd)); + + for (option_desc = option_desc_list; option_desc->option != NULL; option_desc++) { + int len; + + if (option_desc->required) + len = snprintf(usage_str + usage_len, LINE_MAX - usage_len, "%s ", option_desc->option); + else + len = snprintf(usage_str + usage_len, LINE_MAX - usage_len, "[%s] ", option_desc->option); + if (len < 0) + err(1, "%s: snprintf(", __func__); + + usage_len += len; + if (usage_len > LINE_MAX) + break; + } + printf("%s\n", usage_str); + printf("options:\n"); + + for (option_desc = option_desc_list; option_desc->option != NULL; option_desc++) { + printf(" %-20s # %s\n", option_desc->option, option_desc->description); + } + +} + +int +main(int argc, char * const argv[]) +{ + int ch; + double d; + int stats_sock_list = 0; + int stats_filt_list = 0; + int stats_cfil_stats = 0; + + while ((ch = getopt(argc, argv, "a:d:hik:lm:p:qr:s:t:u:v")) != -1) { + switch (ch) { + case 'a': + auto_start = strtoul(optarg, NULL, 0); + break; + case 'd': { + if (optind >= argc) + errx(1, "'-d' needs 2 parameters"); + if (strcasecmp(optarg, "passout") == 0) { + if (offset_from_str(argv[optind], &default_out_pass) == 0) + errx(1, "bad %s offset: %s", optarg, argv[optind + 1]); + } else if (strcasecmp(optarg, "passin") == 0) { + if (offset_from_str(argv[optind], &default_in_pass) == 0) + errx(1, "bad %s offset: %s", optarg, argv[optind + 1]); + } else if (strcasecmp(optarg, "pass") == 0) { + if (offset_from_str(argv[optind], &default_out_pass) == 0) + errx(1, "bad %s offset: %s", optarg, argv[optind + 1]); + default_in_pass = default_out_pass; + } else if (strcasecmp(optarg, "peekout") == 0) { + if (offset_from_str(argv[optind], &default_out_peek) == 0) + errx(1, "bad %s offset: %s", optarg, argv[optind + 1]); + } else if (strcasecmp(optarg, "peekin") == 0) { + if (offset_from_str(argv[optind], &default_in_peek) == 0) + errx(1, "bad %s offset: %s", optarg, argv[optind + 1]); + } else if (strcasecmp(optarg, "peek") == 0) { + if (offset_from_str(argv[optind], &default_out_peek) == 0) + errx(1, "bad %s offset: %s", optarg, argv[optind + 1]); + default_in_peek = default_out_peek; + } else + errx(1, "syntax error"); + break; + } + case 'h': + usage(argv[0]); + exit(0); + case 'i': + mode |= MODE_INTERACTIVE; + break; + case 'k': + mode |= MODE_PEEK; + if (offset_from_str(optarg, &peek_inc) == 0) + errx(1, "bad peek offset: %s", optarg); + break; + case 'l': + pass_loopback = 1; + break; + case 'm': + max_dump_len = strtoul(optarg, NULL, 0); + break; + case 'p': + mode |= MODE_PASS; + if (offset_from_str(optarg, &pass_offset) == 0) + errx(1, "bad pass offset: %s", optarg); + break; + case 'q': + verbosity--; + break; + case 'r': + d = strtod(optarg, NULL); + if (d < 0 || d > 1) + errx(1, "bad drop rate: %s -- it must be between 0 and 1", optarg); + random_drop = (uint32_t)(d * UINT32_MAX); + break; + case 's': + if (strcasecmp(optarg, "all") == 0) { + stats_sock_list = 1; + stats_filt_list = 1; + stats_cfil_stats = 1; + } else if (strcasecmp(optarg, "sock") == 0) { + stats_sock_list = 1; + } else if (strcasecmp(optarg, "filt") == 0) { + stats_filt_list = 1; + } else if (strcasecmp(optarg, "cfil") == 0) { + stats_cfil_stats = 1; + } else { + warnx("# Error: unknown type of statistic: %s", optarg); + usage(argv[0]); + exit(0); + } + break; + case 't': + mode |= MODE_DELAY; + delay_ms = strtoul(optarg, NULL, 0); + delay_tv.tv_sec = delay_ms / 1000; + delay_tv.tv_usec = (delay_ms % 1000) * 1000; + break; + case 'u': + necp_control_unit = (uint32_t)strtoul(optarg, NULL, 0); + break; + case 'v': + verbosity++; + break; + default: + errx(1, "# syntax error, unknow option '%d'", ch); + usage(argv[0]); + exit(0); + } + } + + if (stats_filt_list) + print_filter_list(); + if (stats_sock_list) + print_socket_list(); + if (stats_cfil_stats) + print_cfil_stats(); + if (necp_control_unit == 0 && (stats_filt_list || stats_sock_list || stats_cfil_stats)) + return (0); + + if (necp_control_unit == 0) { + warnx("necp filter control unit is 0"); + usage(argv[0]); + exit(EX_USAGE); + } + doit(); + + + return (0); +} + -- cgit v1.2.3-56-ge451