--- /dev/null
+/*
+ * Copyright (c) 2014 Apple Inc. All rights reserved.
+ *
+ * @APPLE_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. 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_LICENSE_HEADER_END@
+ */
+
+/*
+ * THEORY OF OPERATION
+ *
+ * The packet mangler subsystem provides a limited way for user space
+ * applications to apply certain actions on certain flows.
+ *
+ * A user space applications opens a kernel control socket with the name
+ * PACKET_MANGLER_CONTROL_NAME to attach to the packet mangler subsystem.
+ * When connected, a "struct packet_mangler" is created and set as the
+ * "unitinfo" of the corresponding kernel control socket instance.
+ * Connect call for packet mangler's kernel control socket also registers
+ * ip filers with cookie set to the packet_mangler instance.
+ * The ip filters are removed when control socket is disconnected.
+ */
+#include <sys/types.h>
+#include <sys/kern_control.h>
+#include <sys/domain.h>
+#include <sys/protosw.h>
+#include <sys/syslog.h>
+
+#include <kern/locks.h>
+#include <kern/zalloc.h>
+#include <kern/debug.h>
+
+#include <net/packet_mangler.h>
+
+#include <netinet/tcp.h>
+#include <netinet/tcp_var.h>
+#include <netinet/ip.h>
+#include <netinet/kpi_ipfilter.h>
+#include <string.h>
+#include <libkern/libkern.h>
+
+#define MAX_PACKET_MANGLER 1
+
+#define PKT_MNGLR_FLG_IPFILTER_ATTACHED 0x00000001
+
+SYSCTL_NODE(_net, OID_AUTO, pktmnglr, CTLFLAG_RW|CTLFLAG_LOCKED, 0, "pktmnglr");
+SYSCTL_INT(_net_pktmnglr, OID_AUTO, log, CTLFLAG_RW|CTLFLAG_LOCKED,
+ &pkt_mnglr_log_level, 0, "");
+/*
+ * The structure packet_mangler represents a user space packet filter
+ * It's created and associated with a kernel control socket instance
+ */
+struct packet_mangler {
+ kern_ctl_ref pkt_mnglr_kcref;
+ uint32_t pkt_mnglr_kcunit;
+ uint32_t pkt_mnglr_flags;
+ /* IP filter related params */
+ ipfilter_t pkt_mnglr_ipfref;
+ ipfilter_t pkt_mnglr_ipfrefv6;
+ struct ipf_filter pkt_mnglr_ipfilter;
+
+ /* Options */
+ uint8_t activate;
+ Pkt_Mnglr_Flow dir;
+ struct sockaddr_storage lsaddr;
+ struct sockaddr_storage rsaddr;
+ struct sockaddr_storage swap_lsaddr;
+ struct sockaddr_storage swap_rsaddr;
+ uint32_t ip_action_mask;
+ uint16_t lport;
+ uint16_t rport;
+ uint32_t proto;
+ uint32_t proto_action_mask;
+};
+
+/* Array of all the packet mangler instancesi */
+struct packet_mangler **packet_manglers = NULL;
+
+uint32_t pkt_mnglr_active_count = 0; /* Number of active packet filters */
+uint32_t pkt_mnglr_close_wait_timeout = 1000; /* in milliseconds */
+
+static kern_ctl_ref pkt_mnglr_kctlref = NULL;
+
+static lck_grp_attr_t *pkt_mnglr_lck_grp_attr = NULL;
+static lck_attr_t *pkt_mnglr_lck_attr = NULL;
+static lck_grp_t *pkt_mnglr_lck_grp = NULL;
+
+/* The lock below protects packet_manglers DS, packet_mangler DS */
+decl_lck_rw_data(static, pkt_mnglr_lck_rw);
+
+#define PKT_MNGLR_RW_LCK_MAX 8
+
+int pkt_mnglr_rw_nxt_lck = 0;
+void* pkt_mnglr_rw_lock_history[PKT_MNGLR_RW_LCK_MAX];
+
+int pkt_mnglr_rw_nxt_unlck = 0;
+void* pkt_mnglr_rw_unlock_history[PKT_MNGLR_RW_LCK_MAX];
+
+
+#define PACKET_MANGLER_ZONE_NAME "packet_mangler"
+#define PACKET_MANGLER_ZONE_MAX 10
+static struct zone *packet_mangler_zone = NULL; /* zone for packet_mangler */
+
+/*
+ * For troubleshooting
+ */
+int pkt_mnglr_log_level = LOG_ERR;
+int pkt_mnglr_debug = 1;
+
+/*
+ * Forward declaration to appease the compiler
+ */
+static void pkt_mnglr_rw_lock_exclusive(lck_rw_t *);
+static void pkt_mnglr_rw_unlock_exclusive(lck_rw_t *);
+static void pkt_mnglr_rw_lock_shared(lck_rw_t *);
+static void pkt_mnglr_rw_unlock_shared(lck_rw_t *);
+
+static errno_t pktmnglr_ipfilter_output(void *cookie, mbuf_t *data,
+ ipf_pktopts_t options);
+static errno_t pktmnglr_ipfilter_input(void *cookie, mbuf_t *data,
+ int offset, u_int8_t protocol);
+static void pktmnglr_ipfilter_detach(void *cookie);
+
+static void chksm_update(mbuf_t data);
+
+/*
+ * packet filter global read write lock
+ */
+
+static void
+pkt_mnglr_rw_lock_exclusive(lck_rw_t *lck)
+{
+ void *lr_saved;
+
+ lr_saved = __builtin_return_address(0);
+
+ lck_rw_lock_exclusive(lck);
+
+ pkt_mnglr_rw_lock_history[pkt_mnglr_rw_nxt_lck] = lr_saved;
+ pkt_mnglr_rw_nxt_lck =
+ (pkt_mnglr_rw_nxt_lck + 1) % PKT_MNGLR_RW_LCK_MAX;
+}
+
+static void
+pkt_mnglr_rw_unlock_exclusive(lck_rw_t *lck)
+{
+ void *lr_saved;
+
+ lr_saved = __builtin_return_address(0);
+
+ lck_rw_unlock_exclusive(lck);
+
+ pkt_mnglr_rw_unlock_history[pkt_mnglr_rw_nxt_unlck] =
+ lr_saved;
+ pkt_mnglr_rw_nxt_unlck = (pkt_mnglr_rw_nxt_unlck + 1) % PKT_MNGLR_RW_LCK_MAX;
+}
+
+static void
+pkt_mnglr_rw_lock_shared(lck_rw_t *lck)
+{
+ void *lr_saved;
+
+ lr_saved = __builtin_return_address(0);
+
+ lck_rw_lock_shared(lck);
+
+ pkt_mnglr_rw_lock_history[pkt_mnglr_rw_nxt_lck] = lr_saved;
+ pkt_mnglr_rw_nxt_lck = (pkt_mnglr_rw_nxt_lck + 1) % PKT_MNGLR_RW_LCK_MAX;
+}
+
+static void
+pkt_mnglr_rw_unlock_shared(lck_rw_t *lck)
+{
+ void *lr_saved;
+
+ lr_saved = __builtin_return_address(0);
+
+ lck_rw_unlock_shared(lck);
+
+ pkt_mnglr_rw_unlock_history[pkt_mnglr_rw_nxt_unlck] = lr_saved;
+ pkt_mnglr_rw_nxt_unlck = (pkt_mnglr_rw_nxt_unlck + 1) % PKT_MNGLR_RW_LCK_MAX;
+}
+
+/*
+ * Packet Mangler's Kernel control socket callbacks
+ */
+static errno_t
+pkt_mnglr_ctl_connect(kern_ctl_ref kctlref, struct sockaddr_ctl *sac,
+ void **unitinfo)
+{
+ errno_t error = 0;
+ struct packet_mangler *p_pkt_mnglr = NULL;
+
+ PKT_MNGLR_LOG(LOG_NOTICE, "Connecting packet mangler filter.");
+
+ p_pkt_mnglr = zalloc(packet_mangler_zone);
+ if (p_pkt_mnglr == NULL) {
+ PKT_MNGLR_LOG(LOG_ERR, "zalloc failed");
+ error = ENOMEM;
+ goto done;
+ }
+
+ bzero(p_pkt_mnglr, sizeof(struct packet_mangler));
+
+ pkt_mnglr_rw_lock_exclusive(&pkt_mnglr_lck_rw);
+ if (packet_manglers == NULL) {
+ struct packet_mangler **tmp;
+
+ pkt_mnglr_rw_unlock_exclusive(&pkt_mnglr_lck_rw);
+
+ MALLOC(tmp,
+ struct packet_mangler **,
+ MAX_PACKET_MANGLER * sizeof(struct packet_mangler *),
+ M_TEMP,
+ M_WAITOK | M_ZERO);
+
+ pkt_mnglr_rw_lock_exclusive(&pkt_mnglr_lck_rw);
+
+ if (tmp == NULL && packet_manglers == NULL) {
+ error = ENOMEM;
+ pkt_mnglr_rw_unlock_exclusive(&pkt_mnglr_lck_rw);
+ goto done;
+ }
+ /* Another thread may have won the race */
+ if (packet_manglers != NULL)
+ FREE(tmp, M_TEMP);
+ else
+ packet_manglers = tmp;
+ }
+
+ if (sac->sc_unit == 0 || sac->sc_unit > MAX_PACKET_MANGLER) {
+ PKT_MNGLR_LOG(LOG_ERR, "bad sc_unit %u", sac->sc_unit);
+ error = EINVAL;
+ } else if (packet_manglers[sac->sc_unit - 1] != NULL) {
+ PKT_MNGLR_LOG(LOG_ERR, "sc_unit %u in use", sac->sc_unit);
+ error = EADDRINUSE;
+ } else {
+ /*
+ * kernel control socket kcunit numbers start at 1
+ */
+ packet_manglers[sac->sc_unit - 1] = p_pkt_mnglr;
+
+ p_pkt_mnglr->pkt_mnglr_kcref = kctlref;
+ p_pkt_mnglr->pkt_mnglr_kcunit = sac->sc_unit;
+
+ *unitinfo = p_pkt_mnglr;
+ pkt_mnglr_active_count++;
+ }
+
+ p_pkt_mnglr->pkt_mnglr_ipfilter.cookie = p_pkt_mnglr;
+ p_pkt_mnglr->pkt_mnglr_ipfilter.name = "com.apple.pktmnglripfilter";
+ p_pkt_mnglr->pkt_mnglr_ipfilter.ipf_input = pktmnglr_ipfilter_input;
+ p_pkt_mnglr->pkt_mnglr_ipfilter.ipf_output = pktmnglr_ipfilter_output;
+ p_pkt_mnglr->pkt_mnglr_ipfilter.ipf_detach = pktmnglr_ipfilter_detach;
+ error = ipf_addv4(&(p_pkt_mnglr->pkt_mnglr_ipfilter), &(p_pkt_mnglr->pkt_mnglr_ipfref));
+ if (error) {
+ PKT_MNGLR_LOG(LOG_ERR, "Could not register packet mangler's IPv4 Filter");
+ goto done;
+ }
+ error = ipf_addv6(&(p_pkt_mnglr->pkt_mnglr_ipfilter), &(p_pkt_mnglr->pkt_mnglr_ipfrefv6));
+ if (error) {
+ ipf_remove(p_pkt_mnglr->pkt_mnglr_ipfref);
+ PKT_MNGLR_LOG(LOG_ERR, "Could not register packet mangler's IPv6 Filter");
+ goto done;
+ }
+
+ PKT_MNGLR_LOG(LOG_INFO, "Registered packet mangler's IP Filters");
+ p_pkt_mnglr->pkt_mnglr_flags |= PKT_MNGLR_FLG_IPFILTER_ATTACHED;
+ pkt_mnglr_rw_unlock_exclusive(&pkt_mnglr_lck_rw);
+
+done:
+ if (error != 0 && p_pkt_mnglr != NULL)
+ zfree(packet_mangler_zone, p_pkt_mnglr);
+
+ PKT_MNGLR_LOG(LOG_INFO, "return %d pkt_mnglr_active_count %u kcunit %u",
+ error, pkt_mnglr_active_count, sac->sc_unit);
+
+ return (error);
+}
+
+static errno_t
+pkt_mnglr_ctl_disconnect(kern_ctl_ref kctlref, u_int32_t kcunit, void *unitinfo)
+{
+#pragma unused(kctlref)
+ errno_t error = 0;
+ struct packet_mangler *p_pkt_mnglr;
+
+ PKT_MNGLR_LOG(LOG_INFO, "Disconnecting packet mangler kernel control");
+
+ if (packet_manglers == NULL) {
+ PKT_MNGLR_LOG(LOG_ERR, "no packet filter");
+ error = EINVAL;
+ goto done;
+ }
+ if (kcunit > MAX_PACKET_MANGLER) {
+ PKT_MNGLR_LOG(LOG_ERR, "kcunit %u > MAX_PACKET_MANGLER (%d)",
+ kcunit, MAX_PACKET_MANGLER);
+ error = EINVAL;
+ goto done;
+ }
+
+ p_pkt_mnglr = (struct packet_mangler *)unitinfo;
+ if (p_pkt_mnglr == NULL) {
+ PKT_MNGLR_LOG(LOG_ERR, "Unit info is NULL");
+ goto done;
+ }
+
+ pkt_mnglr_rw_lock_exclusive(&pkt_mnglr_lck_rw);
+ if (packet_manglers[kcunit - 1] != p_pkt_mnglr || p_pkt_mnglr->pkt_mnglr_kcunit != kcunit) {
+ PKT_MNGLR_LOG(LOG_ERR, "bad unit info %u)",
+ kcunit);
+ pkt_mnglr_rw_unlock_exclusive(&pkt_mnglr_lck_rw);
+ goto done;
+ }
+
+ /*
+ * Make filter inactive
+ */
+ packet_manglers[kcunit - 1] = NULL;
+ pkt_mnglr_active_count--;
+ if (p_pkt_mnglr->pkt_mnglr_flags & PKT_MNGLR_FLG_IPFILTER_ATTACHED) {
+ (void) ipf_remove(p_pkt_mnglr->pkt_mnglr_ipfref);
+ (void) ipf_remove(p_pkt_mnglr->pkt_mnglr_ipfrefv6);
+ }
+ pkt_mnglr_rw_unlock_exclusive(&pkt_mnglr_lck_rw);
+ zfree(packet_mangler_zone, p_pkt_mnglr);
+done:
+ PKT_MNGLR_LOG(LOG_INFO, "return %d pkt_mnglr_active_count %u kcunit %u",
+ error, pkt_mnglr_active_count, kcunit);
+
+ return (error);
+}
+
+static errno_t
+pkt_mnglr_ctl_getopt(kern_ctl_ref kctlref, u_int32_t kcunit, void *unitinfo,
+ int opt, void *data, size_t *len)
+{
+#pragma unused(kctlref, opt)
+ errno_t error = 0;
+ struct packet_mangler *p_pkt_mnglr = (struct packet_mangler *)unitinfo;
+
+ PKT_MNGLR_LOG(LOG_NOTICE, "");
+
+ pkt_mnglr_rw_lock_shared(&pkt_mnglr_lck_rw);
+
+ if (packet_manglers == NULL) {
+ PKT_MNGLR_LOG(LOG_ERR, "no packet filter");
+ error = EINVAL;
+ goto done;
+ }
+ if (kcunit > MAX_PACKET_MANGLER) {
+ PKT_MNGLR_LOG(LOG_ERR, "kcunit %u > MAX_PACKET_MANGLER (%d)",
+ kcunit, MAX_PACKET_MANGLER);
+ error = EINVAL;
+ goto done;
+ }
+ if (p_pkt_mnglr != (void *)packet_manglers[kcunit - 1]) {
+ PKT_MNGLR_LOG(LOG_ERR, "unitinfo does not match for kcunit %u",
+ kcunit);
+ error = EINVAL;
+ goto done;
+ }
+ switch (opt) {
+ case PKT_MNGLR_OPT_PROTO_ACT_MASK:
+ if (*len < sizeof(uint32_t)) {
+ PKT_MNGLR_LOG(LOG_ERR, "PKT_MNGLR_OPT_PROTO_ACT_MASK "
+ "len too small %lu", *len);
+ error = EINVAL;
+ goto done;
+ }
+
+ if (data != NULL) {
+ *(uint32_t *)data = p_pkt_mnglr->proto_action_mask;
+ }
+ break;
+ case PKT_MNGLR_OPT_IP_ACT_MASK:
+ if (*len < sizeof(uint32_t)) {
+ PKT_MNGLR_LOG(LOG_ERR, "PKT_MNGLR_OPT_IP_ACT_MASK "
+ "len too small %lu", *len);
+ error = EINVAL;
+ goto done;
+ }
+
+ if (data != NULL) {
+ *(uint32_t *)data = p_pkt_mnglr->ip_action_mask;
+ }
+ break;
+ case PKT_MNGLR_OPT_LOCAL_IP:
+ if (*len < sizeof(struct sockaddr_storage)) {
+ PKT_MNGLR_LOG(LOG_ERR, "PKT_MNGLR_OPT_LOCAL_IP "
+ "len too small %lu", *len);
+ error = EINVAL;
+ goto done;
+ }
+
+ if (data != NULL) {
+ *(struct sockaddr_storage *)data = p_pkt_mnglr->lsaddr;
+ }
+ break;
+ case PKT_MNGLR_OPT_REMOTE_IP:
+ if (*len < sizeof(struct sockaddr_storage)) {
+ PKT_MNGLR_LOG(LOG_ERR, "PKT_MNGLR_OPT_REMOTE_IP "
+ "len too small %lu", *len);
+ error = EINVAL;
+ goto done;
+ }
+
+ if (data != NULL) {
+ *(struct sockaddr_storage *)data = p_pkt_mnglr->rsaddr;
+ }
+ break;
+ case PKT_MNGLR_OPT_LOCAL_PORT:
+ if (*len < sizeof(uint16_t)) {
+ PKT_MNGLR_LOG(LOG_ERR, "PKT_MNGLR_OPT_LOCAL_PORT "
+ "len too small %lu", *len);
+ error = EINVAL;
+ goto done;
+ }
+
+ if (data != NULL) {
+ *(uint16_t *)data = p_pkt_mnglr->lport;
+ }
+ break;
+ case PKT_MNGLR_OPT_REMOTE_PORT:
+ if (*len < sizeof(uint16_t)) {
+ PKT_MNGLR_LOG(LOG_ERR, "PKT_MNGLR_OPT_REMOTE_PORT "
+ "len too small %lu", *len);
+ error = EINVAL;
+ goto done;
+ }
+
+ if (data != NULL) {
+ *(uint16_t *)data = p_pkt_mnglr->rport;
+ }
+ break;
+ case PKT_MNGLR_OPT_DIRECTION:
+ if (*len < sizeof(uint32_t)) {
+ PKT_MNGLR_LOG(LOG_ERR, "PKT_MNGLR_OPT_DIRECTION "
+ "len too small %lu", *len);
+ error = EINVAL;
+ goto done;
+ }
+ if (data != NULL) {
+ *(uint32_t *)data = p_pkt_mnglr->dir;
+ }
+ break;
+ case PKT_MNGLR_OPT_PROTOCOL:
+ if (*len < sizeof(uint32_t)) {
+ PKT_MNGLR_LOG(LOG_ERR, "PKT_MNGLR_OPT_PROTOCOL "
+ "len too small %lu", *len);
+ error = EINVAL;
+ goto done;
+ }
+ if (data != NULL) {
+ *(uint32_t *)data = p_pkt_mnglr->proto;
+ }
+ break;
+ case PKT_MNGLR_OPT_ACTIVATE:
+ if (*len < sizeof(uint8_t)) {
+ PKT_MNGLR_LOG(LOG_ERR, "PKT_MNGLR_OPT_ACTIVATE "
+ "len too small %lu", *len);
+ error = EINVAL;
+ goto done;
+ }
+
+ if (data != NULL) {
+ *(uint8_t *)data = p_pkt_mnglr->activate;
+ }
+ break;
+ default:
+ error = ENOPROTOOPT;
+ break;
+ }
+done:
+ pkt_mnglr_rw_unlock_shared(&pkt_mnglr_lck_rw);
+
+ return (error);
+}
+
+static errno_t
+pkt_mnglr_ctl_setopt(kern_ctl_ref kctlref, u_int32_t kcunit, void *unitinfo,
+ int opt, void *data, size_t len)
+{
+#pragma unused(kctlref, opt)
+ errno_t error = 0;
+ struct packet_mangler *p_pkt_mnglr = (struct packet_mangler *)unitinfo;
+
+ PKT_MNGLR_LOG(LOG_NOTICE, "");
+
+ pkt_mnglr_rw_lock_exclusive(&pkt_mnglr_lck_rw);
+
+ if (packet_manglers == NULL) {
+ PKT_MNGLR_LOG(LOG_ERR, "no packet filter");
+ error = EINVAL;
+ goto done;
+ }
+ if (kcunit > MAX_PACKET_MANGLER) {
+ PKT_MNGLR_LOG(LOG_ERR, "kcunit %u > MAX_PACKET_MANGLER (%d)",
+ kcunit, MAX_PACKET_MANGLER);
+ error = EINVAL;
+ goto done;
+ }
+ if (p_pkt_mnglr != (void *)packet_manglers[kcunit - 1]) {
+ PKT_MNGLR_LOG(LOG_ERR, "unitinfo does not match for kcunit %u",
+ kcunit);
+ error = EINVAL;
+ goto done;
+ }
+ switch (opt) {
+ case PKT_MNGLR_OPT_PROTO_ACT_MASK:
+ if (len < sizeof(uint32_t)) {
+ PKT_MNGLR_LOG(LOG_ERR, "PKT_MNGLR_OPT_PROTO_ACT_MASK "
+ "len too small %lu", len);
+ error = EINVAL;
+ goto done;
+ }
+ if (p_pkt_mnglr->proto_action_mask != 0) {
+ PKT_MNGLR_LOG(LOG_ERR, "PKT_MNGLR_OPT_PROTO_ACT_MASK "
+ "already set %u",
+ p_pkt_mnglr->proto_action_mask);
+ error = EINVAL;
+ goto done;
+ }
+ p_pkt_mnglr->proto_action_mask = *(uint32_t *)data;
+ PKT_MNGLR_LOG(LOG_INFO, "p_pkt_mnglr->proto_action_mask set to :%d", p_pkt_mnglr->proto_action_mask);
+ break;
+ case PKT_MNGLR_OPT_IP_ACT_MASK:
+ if (len < sizeof(uint32_t)) {
+ PKT_MNGLR_LOG(LOG_ERR, "PKT_MNGLR_OPT_IP_ACT_MASK "
+ "len too small %lu", len);
+ error = EINVAL;
+ goto done;
+ }
+ if (p_pkt_mnglr->ip_action_mask != 0) {
+ PKT_MNGLR_LOG(LOG_ERR, "PKT_MNGLR_OPT_IP_ACT_MASK "
+ "already set %u",
+ p_pkt_mnglr->ip_action_mask);
+ error = EINVAL;
+ goto done;
+ }
+ p_pkt_mnglr->ip_action_mask = *(uint32_t *)data;
+ break;
+ case PKT_MNGLR_OPT_LOCAL_IP:
+ if (len < sizeof(struct sockaddr_storage)) {
+ PKT_MNGLR_LOG(LOG_ERR, "PKT_MNGLR_OPT_LOCAL_IP "
+ "len too small %lu", len);
+ error = EINVAL;
+ goto done;
+ }
+ if (p_pkt_mnglr->lsaddr.ss_family) {
+ PKT_MNGLR_LOG(LOG_ERR, "PKT_MNGLR_OPT_LOCAL_IP "
+ "already set");
+ error = EINVAL;
+ goto done;
+ }
+ p_pkt_mnglr->lsaddr = *(struct sockaddr_storage *)data;
+ break;
+ case PKT_MNGLR_OPT_REMOTE_IP:
+ if (len < sizeof(struct sockaddr_storage)) {
+ PKT_MNGLR_LOG(LOG_ERR, "PKT_MNGLR_OPT_REMOTE_IP "
+ "len too small %lu", len);
+ error = EINVAL;
+ goto done;
+ }
+ if (p_pkt_mnglr->rsaddr.ss_family) {
+ PKT_MNGLR_LOG(LOG_ERR, "PKT_MNGLR_OPT_REMOTE_IP "
+ "already set");
+ error = EINVAL;
+ goto done;
+ }
+
+ p_pkt_mnglr->rsaddr = *(struct sockaddr_storage *)data;
+ PKT_MNGLR_LOG(LOG_INFO,
+ "Remote IP registered for address family: %d",
+ p_pkt_mnglr->rsaddr.ss_family);
+ break;
+ case PKT_MNGLR_OPT_LOCAL_PORT:
+ if (len < sizeof(uint16_t)) {
+ PKT_MNGLR_LOG(LOG_ERR, "PKT_MNGLR_OPT_LOCAL_PORT "
+ "len too small %lu", len);
+ error = EINVAL;
+ goto done;
+ }
+ if (p_pkt_mnglr->lport != 0) {
+ PKT_MNGLR_LOG(LOG_ERR, "PKT_MNGLR_OPT_LOCAL_PORT "
+ "already set %d",
+ p_pkt_mnglr->lport);
+ error = EINVAL;
+ goto done;
+ }
+ p_pkt_mnglr->lport = *(uint16_t *)data;
+ break;
+ case PKT_MNGLR_OPT_REMOTE_PORT:
+ if (len < sizeof(uint16_t)) {
+ PKT_MNGLR_LOG(LOG_ERR, "PKT_MNGLR_OPT_REMOTE_PORT "
+ "len too small %lu", len);
+ error = EINVAL;
+ goto done;
+ }
+ if (p_pkt_mnglr->rport != 0) {
+ PKT_MNGLR_LOG(LOG_ERR, "PKT_MNGLR_OPT_REMOTE_PORT "
+ "already set %d",
+ p_pkt_mnglr->rport);
+ error = EINVAL;
+ goto done;
+ }
+ p_pkt_mnglr->rport = *(uint16_t *)data;
+ break;
+ case PKT_MNGLR_OPT_DIRECTION:
+ if (len < sizeof(uint32_t)) {
+ PKT_MNGLR_LOG(LOG_ERR, "PKT_MNGLR_OPT_DIRECTION "
+ "len too small %lu", len);
+ error = EINVAL;
+ goto done;
+ }
+ if (p_pkt_mnglr->dir != 0) {
+ PKT_MNGLR_LOG(LOG_ERR, "PKT_MNGLR_OPT_DIRECTION "
+ "already set %u",
+ p_pkt_mnglr->dir);
+ error = EINVAL;
+ goto done;
+ }
+ p_pkt_mnglr->dir = *(uint32_t *)data;
+ break;
+ case PKT_MNGLR_OPT_PROTOCOL:
+ if (len < sizeof(uint32_t)) {
+ PKT_MNGLR_LOG(LOG_ERR, "PKT_MNGLR_OPT_PROTOCOL "
+ "len too small %lu", len);
+ error = EINVAL;
+ goto done;
+ }
+ if (p_pkt_mnglr->proto != 0) {
+ PKT_MNGLR_LOG(LOG_ERR, "PKT_MNGLR_OPT_PROTOCOL "
+ "already set %u",
+ p_pkt_mnglr->proto);
+ error = EINVAL;
+ goto done;
+ }
+ p_pkt_mnglr->proto = *(uint32_t *)data;
+ break;
+ case PKT_MNGLR_OPT_ACTIVATE:
+ if (len < sizeof(uint8_t)) {
+ PKT_MNGLR_LOG(LOG_ERR, "PKT_MNGLR_OPT_ACTIVATE "
+ "len too small %lu", len);
+ error = EINVAL;
+ goto done;
+ }
+ if (p_pkt_mnglr->activate != 0) {
+ PKT_MNGLR_LOG(LOG_ERR, "PKT_MNGLR_OPT_ACTIVATE "
+ "already set %u",
+ p_pkt_mnglr->activate);
+ error = EINVAL;
+ goto done;
+ }
+ p_pkt_mnglr->activate = *(uint8_t *)data;
+ PKT_MNGLR_LOG(LOG_ERR, "p_pkt_mnglr->activate set to :%d",
+ p_pkt_mnglr->activate);
+ break;
+ default:
+ error = ENOPROTOOPT;
+ break;
+ }
+done:
+ pkt_mnglr_rw_unlock_exclusive(&pkt_mnglr_lck_rw);
+
+ return (error);
+}
+
+void
+pkt_mnglr_init(void)
+{
+ struct kern_ctl_reg kern_ctl;
+ errno_t error = 0;
+ vm_size_t pkt_mnglr_size = 0;
+
+ PKT_MNGLR_LOG(LOG_NOTICE, "");
+
+ /*
+ * Compile time verifications
+ */
+ _CASSERT(PKT_MNGLR_MAX_FILTER_COUNT == MAX_PACKET_MANGLER);
+
+ /*
+ * Zone for packet mangler kernel control sockets
+ */
+ pkt_mnglr_size = sizeof(struct packet_mangler);
+ packet_mangler_zone = zinit(pkt_mnglr_size,
+ PACKET_MANGLER_ZONE_MAX * pkt_mnglr_size,
+ 0,
+ PACKET_MANGLER_ZONE_NAME);
+
+ if (packet_mangler_zone == NULL) {
+ panic("%s: zinit(%s) failed", __func__,
+ PACKET_MANGLER_ZONE_NAME);
+ /* NOTREACHED */
+ }
+ zone_change(packet_mangler_zone, Z_CALLERACCT, FALSE);
+ zone_change(packet_mangler_zone, Z_EXPAND, TRUE);
+
+ /*
+ * Allocate locks
+ */
+ pkt_mnglr_lck_grp_attr = lck_grp_attr_alloc_init();
+ if (pkt_mnglr_lck_grp_attr == NULL) {
+ panic("%s: lck_grp_attr_alloc_init failed", __func__);
+ /* NOTREACHED */
+ }
+ pkt_mnglr_lck_grp = lck_grp_alloc_init("packet manglerr",
+ pkt_mnglr_lck_grp_attr);
+ if (pkt_mnglr_lck_grp == NULL) {
+ panic("%s: lck_grp_alloc_init failed", __func__);
+ /* NOTREACHED */
+ }
+ pkt_mnglr_lck_attr = lck_attr_alloc_init();
+ if (pkt_mnglr_lck_attr == NULL) {
+ panic("%s: lck_attr_alloc_init failed", __func__);
+ /* NOTREACHED */
+ }
+ lck_rw_init(&pkt_mnglr_lck_rw, pkt_mnglr_lck_grp, pkt_mnglr_lck_attr);
+
+ /*
+ * Register kernel control
+ */
+ bzero(&kern_ctl, sizeof(kern_ctl));
+ strlcpy(kern_ctl.ctl_name, PACKET_MANGLER_CONTROL_NAME,
+ sizeof(kern_ctl.ctl_name));
+ kern_ctl.ctl_flags = CTL_FLAG_PRIVILEGED | CTL_FLAG_REG_EXTENDED;
+ kern_ctl.ctl_connect = pkt_mnglr_ctl_connect;
+ kern_ctl.ctl_disconnect = pkt_mnglr_ctl_disconnect;
+ kern_ctl.ctl_getopt = pkt_mnglr_ctl_getopt;
+ kern_ctl.ctl_setopt = pkt_mnglr_ctl_setopt;
+ error = ctl_register(&kern_ctl, &pkt_mnglr_kctlref);
+ if (error != 0) {
+ PKT_MNGLR_LOG(LOG_ERR, "ctl_register failed: %d", error);
+ } else {
+ PKT_MNGLR_LOG(LOG_INFO, "Registered packet mangler kernel control.");
+ }
+}
+
+static errno_t pktmnglr_ipfilter_output(void *cookie, mbuf_t *data, ipf_pktopts_t options)
+{
+ struct packet_mangler *p_pkt_mnglr = (struct packet_mangler *)cookie;
+ unsigned char *ptr = (unsigned char *)mbuf_data(*data);
+ struct ip *ip = (struct ip *)(void *)ptr;
+ struct tcphdr *tcp;
+ int optlen = 0;
+
+#pragma unused(tcp, optlen, options)
+
+ if (p_pkt_mnglr == NULL) {
+ return 0;
+ }
+
+ if (!p_pkt_mnglr->activate) {
+ return 0;
+ }
+
+ if (data == NULL) {
+ PKT_MNGLR_LOG(LOG_INFO, "%s:%d Data pointer is NULL\n", __FILE__, __LINE__);
+ return 0;
+ }
+
+ if (p_pkt_mnglr->dir == IN) {
+ return 0;
+ }
+
+ if ((p_pkt_mnglr->lsaddr.ss_family == AF_INET6) && (ip->ip_v == 4)) {
+ return 0;
+ }
+
+ if ((p_pkt_mnglr->lsaddr.ss_family == AF_INET) && (ip->ip_v == 6)) {
+ return 0;
+ }
+
+ if (p_pkt_mnglr->lsaddr.ss_family == AF_INET) {
+ struct sockaddr_in laddr = *(struct sockaddr_in *)(&(p_pkt_mnglr->lsaddr));
+ if (ip->ip_src.s_addr != laddr.sin_addr.s_addr) {
+ return 0;
+ }
+ }
+
+ if (p_pkt_mnglr->rsaddr.ss_family == AF_INET) {
+ struct sockaddr_in raddr = *(struct sockaddr_in *)(&(p_pkt_mnglr->rsaddr));
+ if (ip->ip_dst.s_addr != raddr.sin_addr.s_addr) {
+ return 0;
+ }
+ }
+
+ if (ip->ip_v != 4) {
+ PKT_MNGLR_LOG(LOG_INFO, "%s:%d Not handling IP version %d\n", __FILE__, __LINE__, ip->ip_v);
+ return 0;
+ }
+
+ /* Not handling output flow */
+ return 0;
+}
+
+#define TCP_MAX_OPTLEN 40
+
+static errno_t pktmnglr_ipfilter_input(void *cookie, mbuf_t *data, int offset, u_int8_t protocol)
+{
+ struct packet_mangler *p_pkt_mnglr = (struct packet_mangler *)cookie;
+ struct ip ip;
+ struct tcphdr tcp;
+ char tcp_opt_buf[TCP_MAX_OPTLEN] = {0};
+ int orig_tcp_optlen;
+ int tcp_optlen = 0;
+ errno_t error = 0;
+
+ if (p_pkt_mnglr == NULL) {
+ PKT_MNGLR_LOG(LOG_ERR, "p_pkt_mnglr is NULL");
+ goto input_done;
+ }
+
+ if (p_pkt_mnglr->activate == 0) {
+ PKT_MNGLR_LOG(LOG_INFO, "p_pkt_mnglr not yet activated");
+ goto input_done;
+ }
+
+ if (data == NULL) {
+ PKT_MNGLR_LOG(LOG_ERR, "Data pointer is NULL");
+ goto input_done;
+ }
+
+ if (p_pkt_mnglr->dir == OUT) {
+ goto input_done;
+ }
+
+ /* Check for IP filter options */
+ error = mbuf_copydata(*data, 0, sizeof(ip), &ip);
+ if (error) {
+ PKT_MNGLR_LOG(LOG_ERR, "Could not make local IP header copy");
+ goto input_done;
+ }
+
+ if ((p_pkt_mnglr->lsaddr.ss_family == AF_INET6) && (ip.ip_v == 4)) {
+ PKT_MNGLR_LOG(LOG_INFO, "Skipping filtering as address family of packet is IPv4 but local "
+ "address is set to IPv6");
+ goto input_done;
+ }
+
+ if ((p_pkt_mnglr->lsaddr.ss_family == AF_INET) && (ip.ip_v == 6)) {
+ PKT_MNGLR_LOG(LOG_INFO, "Skipping filtering as address family "
+ "of packet is IPv6 but local address is set to IPv4");
+ goto input_done;
+ }
+
+ if (p_pkt_mnglr->lsaddr.ss_family == AF_INET) {
+ struct sockaddr_in laddr = *(struct sockaddr_in *)(&(p_pkt_mnglr->lsaddr));
+ if (ip.ip_dst.s_addr != laddr.sin_addr.s_addr) {
+ goto input_done;
+ }
+ }
+
+ if (p_pkt_mnglr->rsaddr.ss_family == AF_INET) {
+ struct sockaddr_in raddr = *(struct sockaddr_in *)(&(p_pkt_mnglr->rsaddr));
+ if (ip.ip_src.s_addr != raddr.sin_addr.s_addr) {
+ goto input_done;
+ }
+ PKT_MNGLR_LOG(LOG_INFO, "Remote IP: %x Source IP: %x in input path",
+ raddr.sin_addr.s_addr,
+ ip.ip_src.s_addr);
+ }
+
+ if (ip.ip_v != 4) {
+ goto input_done;
+ }
+
+ if (protocol != p_pkt_mnglr->proto) {
+ PKT_MNGLR_LOG(LOG_INFO, "Skip: Protocol mismatch");
+ goto input_done;
+ }
+
+ switch (protocol) {
+ case IPPROTO_TCP:
+ error = mbuf_copydata(*data, offset, sizeof(tcp), &tcp);
+ if (error) {
+ PKT_MNGLR_LOG(LOG_ERR, "Could not make local TCP header copy");
+ goto input_done;
+ }
+
+ if (p_pkt_mnglr->lport && (p_pkt_mnglr->lport != tcp.th_dport)) {
+ PKT_MNGLR_LOG(LOG_INFO, "Local port and IP des port do not match");
+ goto input_done;
+ }
+
+ if (p_pkt_mnglr->rport && (p_pkt_mnglr->rport != tcp.th_sport)) {
+ PKT_MNGLR_LOG(LOG_INFO, "Remote port and IP src port do not match");
+ goto input_done;
+ }
+ break;
+ case IPPROTO_UDP:
+ goto input_done;
+ break;
+ case IPPROTO_ICMP:
+ goto input_done;
+ break;
+ case IPPROTO_ICMPV6:
+ goto input_done;
+ break;
+ default:
+ goto input_done;
+ break;
+ }
+
+ /* XXX Do IP actions here */
+ PKT_MNGLR_LOG(LOG_INFO, "Proceeding with packet mangler actions on the packet");
+
+ /* Protocol actions */
+ switch (protocol) {
+ case IPPROTO_TCP:
+ if (p_pkt_mnglr->proto_action_mask & PKT_MNGLR_TCP_ACT_NOP_MPTCP) {
+ int i = 0;
+ tcp_optlen = (tcp.th_off << 2)-sizeof(struct tcphdr);
+ PKT_MNGLR_LOG(LOG_INFO, "Packet from F5 is TCP\n");
+ PKT_MNGLR_LOG(LOG_INFO, "Optlen: %d\n", tcp_optlen);
+ orig_tcp_optlen = tcp_optlen;
+ if (orig_tcp_optlen) {
+ error = mbuf_copydata(*data, offset+sizeof(struct tcphdr), orig_tcp_optlen, tcp_opt_buf);
+ if (error) {
+ PKT_MNGLR_LOG(LOG_ERR, "Failed to copy tcp options");
+ goto input_done;
+ }
+ }
+
+ while (tcp_optlen) {
+ if (tcp_opt_buf[i] == 0x1) {
+ PKT_MNGLR_LOG(LOG_INFO, "Skipping NOP\n");
+ tcp_optlen--;
+ i++;
+ continue;
+ } else if ((tcp_opt_buf[i] != 0) && (tcp_opt_buf[i] != 0x1e)) {
+ PKT_MNGLR_LOG(LOG_INFO, "Skipping option %x\n", tcp_opt_buf[i]);
+ tcp_optlen -= tcp_opt_buf[i+1];
+ i += tcp_opt_buf[i+1];
+ continue;
+ } else if (tcp_opt_buf[i] == 0x1e) {
+ int j = 0;
+ int mptcpoptlen = tcp_opt_buf[i+1];
+ PKT_MNGLR_LOG(LOG_INFO, "Got MPTCP option %x\n", tcp_opt_buf[i]);
+ PKT_MNGLR_LOG(LOG_INFO, "Overwriting with NOP\n");
+ for (; j < mptcpoptlen; j++) {
+ tcp_opt_buf[i+j] = 0x1;
+ }
+ tcp_optlen -= mptcpoptlen;
+ i += mptcpoptlen;
+ } else {
+ tcp_optlen--;
+ i++;
+ }
+ }
+ error = mbuf_copyback(*data,
+ offset+sizeof(struct tcphdr),
+ orig_tcp_optlen, tcp_opt_buf, MBUF_WAITOK);
+
+ if (error) {
+ PKT_MNGLR_LOG(LOG_ERR,
+ "Failed to copy tcp options");
+ goto input_done;
+ }
+ }
+ break;
+ case IPPROTO_UDP:
+ /* Don't handle UDP */
+ break;
+ case IPPROTO_ICMP:
+ break;
+ case IPPROTO_ICMPV6:
+ break;
+ default:
+ break;
+ }
+ chksm_update(*data);
+input_done:
+ return 0;
+}
+
+static void pktmnglr_ipfilter_detach(void *cookie)
+{
+#pragma unused(cookie)
+ return;
+}
+
+/* XXX Still need to modify this to use mbuf_copy* macros */
+static void chksm_update(mbuf_t data)
+{
+ u_int16_t ip_sum;
+ u_int16_t tsum;
+ struct tcphdr *tcp;
+
+ unsigned char *ptr = (unsigned char *)mbuf_data(data);
+ struct ip *ip = (struct ip *)(void *)ptr;
+ if (ip->ip_v != 4) {
+ return;
+ }
+
+ ip->ip_sum = 0;
+ mbuf_inet_cksum(data, 0, 0, ip->ip_hl << 2, &ip_sum); // ip sum
+
+ ip->ip_sum = ip_sum;
+ switch (ip->ip_p) {
+ case IPPROTO_TCP:
+ tcp = (struct tcphdr *)(void *)(ptr + (ip->ip_hl << 2));
+ tcp->th_sum = 0;
+ mbuf_inet_cksum(data, IPPROTO_TCP, ip->ip_hl << 2,
+ ntohs(ip->ip_len) - (ip->ip_hl << 2), &tsum);
+ tcp->th_sum = tsum;
+ break;
+ case IPPROTO_UDP:
+ /* Don't handle UDP */
+ break;
+ case IPPROTO_ICMP:
+ break;
+ case IPPROTO_ICMPV6:
+ break;
+ default:
+ break;
+ }
+
+ mbuf_clear_csum_performed(data);
+ return;
+}