diff --git a/examples/STM32-LWIP/README.md b/examples/STM32-LWIP/README.md index 430cc6e..f2c24b6 100644 --- a/examples/STM32-LWIP/README.md +++ b/examples/STM32-LWIP/README.md @@ -68,7 +68,10 @@ In `StartDefaultTask()`: ```c /* USER CODE BEGIN 5 */ printf("Sentry init\n"); - sentry_init(); + if (sentry_init() != 0) { + printf("Sentry init failed, all traffic will be denied\n"); + Error_Handler(); + } printf("Echo init\n"); echo_init(); printf("Ping init\n"); diff --git a/examples/STM32-LWIP/Src/sentry.c b/examples/STM32-LWIP/Src/sentry.c index 69b7f6a..035f60a 100644 --- a/examples/STM32-LWIP/Src/sentry.c +++ b/examples/STM32-LWIP/Src/sentry.c @@ -135,25 +135,28 @@ static wolfsentry_errcode_t test_action( int sentry_init(void) { wolfsentry_errcode_t ret; + wolfsentry_errcode_t fini_ret; wolfsentry_ent_id_t id; + struct wolfsentry_context *ctx = NULL; struct wolfsentry_eventconfig config = { 0 }; char err_buf[512]; - struct wolfsentry_json_process_state *jps; + struct wolfsentry_json_process_state *jps = NULL; config.route_private_data_size = 32; config.route_private_data_alignment = 16; ret = wolfsentry_init(wolfsentry_build_settings, WOLFSENTRY_CONTEXT_ARGS_OUT_EX4(NULL /* hpi */, NULL /* thread */), &config, - &wolfsentry); + &ctx); if (ret < 0) { printf("unable to initialize wolfSentry, " WOLFSENTRY_ERROR_FMT "\n", WOLFSENTRY_ERROR_FMT_ARGS(ret)); + return ret; } /* Insert the possible actions into wolfSentry */ - wolfsentry_action_insert( WOLFSENTRY_CONTEXT_ARGS_OUT_EX4(wolfsentry, NULL), + ret = wolfsentry_action_insert(WOLFSENTRY_CONTEXT_ARGS_OUT_EX4(ctx, NULL), "handle-insert", WOLFSENTRY_LENGTH_NULL_TERMINATED, WOLFSENTRY_ACTION_FLAG_NONE, @@ -161,83 +164,115 @@ int sentry_init(void) NULL, &id); - wolfsentry_action_insert( - WOLFSENTRY_CONTEXT_ARGS_OUT_EX4(wolfsentry, NULL), - "handle-delete", - WOLFSENTRY_LENGTH_NULL_TERMINATED, - WOLFSENTRY_ACTION_FLAG_NONE, - test_action, - NULL, - &id); - - wolfsentry_action_insert( - WOLFSENTRY_CONTEXT_ARGS_OUT_EX4(wolfsentry, NULL), - "handle-match", - WOLFSENTRY_LENGTH_NULL_TERMINATED, - WOLFSENTRY_ACTION_FLAG_NONE, - test_action, - NULL, - &id); - - wolfsentry_action_insert( - WOLFSENTRY_CONTEXT_ARGS_OUT_EX4(wolfsentry, NULL), - "notify-on-match", - WOLFSENTRY_LENGTH_NULL_TERMINATED, - WOLFSENTRY_ACTION_FLAG_NONE, - test_action, - NULL, - &id); - - wolfsentry_action_insert( - WOLFSENTRY_CONTEXT_ARGS_OUT_EX4(wolfsentry, NULL), - "handle-connect", - WOLFSENTRY_LENGTH_NULL_TERMINATED, - WOLFSENTRY_ACTION_FLAG_NONE, - test_action, - NULL, - &id); - - wolfsentry_action_insert( - WOLFSENTRY_CONTEXT_ARGS_OUT_EX4(wolfsentry, NULL), - "handle-connect2", - WOLFSENTRY_LENGTH_NULL_TERMINATED, - WOLFSENTRY_ACTION_FLAG_NONE, - test_action, - NULL, - &id); + if (ret >= 0) + ret = wolfsentry_action_insert( + WOLFSENTRY_CONTEXT_ARGS_OUT_EX4(ctx, NULL), + "handle-delete", + WOLFSENTRY_LENGTH_NULL_TERMINATED, + WOLFSENTRY_ACTION_FLAG_NONE, + test_action, + NULL, + &id); + + if (ret >= 0) + ret = wolfsentry_action_insert( + WOLFSENTRY_CONTEXT_ARGS_OUT_EX4(ctx, NULL), + "handle-match", + WOLFSENTRY_LENGTH_NULL_TERMINATED, + WOLFSENTRY_ACTION_FLAG_NONE, + test_action, + NULL, + &id); + + if (ret >= 0) + ret = wolfsentry_action_insert( + WOLFSENTRY_CONTEXT_ARGS_OUT_EX4(ctx, NULL), + "notify-on-match", + WOLFSENTRY_LENGTH_NULL_TERMINATED, + WOLFSENTRY_ACTION_FLAG_NONE, + test_action, + NULL, + &id); + + if (ret >= 0) + ret = wolfsentry_action_insert( + WOLFSENTRY_CONTEXT_ARGS_OUT_EX4(ctx, NULL), + "handle-connect", + WOLFSENTRY_LENGTH_NULL_TERMINATED, + WOLFSENTRY_ACTION_FLAG_NONE, + test_action, + NULL, + &id); + + if (ret >= 0) + ret = wolfsentry_action_insert( + WOLFSENTRY_CONTEXT_ARGS_OUT_EX4(ctx, NULL), + "handle-connect2", + WOLFSENTRY_LENGTH_NULL_TERMINATED, + WOLFSENTRY_ACTION_FLAG_NONE, + test_action, + NULL, + &id); + if (ret < 0) + { + printf("unable to insert wolfSentry actions, " WOLFSENTRY_ERROR_FMT "\n", + WOLFSENTRY_ERROR_FMT_ARGS(ret)); + } /* Initalize the wolfSentry JSON parser */ - if ((ret = wolfsentry_config_json_init( - WOLFSENTRY_CONTEXT_ARGS_OUT_EX4(wolfsentry, NULL), - WOLFSENTRY_CONFIG_LOAD_FLAG_NONE, - &jps)) < 0) + if (ret >= 0) { - printf("error while initializing wolfSentry config parser, " - WOLFSENTRY_ERROR_FMT "\n", WOLFSENTRY_ERROR_FMT_ARGS(ret)); + ret = wolfsentry_config_json_init( + WOLFSENTRY_CONTEXT_ARGS_OUT_EX4(ctx, NULL), + WOLFSENTRY_CONFIG_LOAD_FLAG_NONE, + &jps); + if (ret < 0) + { + printf("error while initializing wolfSentry config parser, " + WOLFSENTRY_ERROR_FMT "\n", WOLFSENTRY_ERROR_FMT_ARGS(ret)); + } } /* wolfSentry uses a streaming reader/parser for the config file */ /* Send the read data into the JSON parser */ - ret = wolfsentry_config_json_feed(jps, - (const unsigned char *)wolfsentry_config_data, - strlen(wolfsentry_config_data), err_buf, sizeof err_buf); - - if (ret < 0) - { - printf("error while loading wolfSentry config data, " - WOLFSENTRY_ERROR_FMT ", msg: %s\n", - WOLFSENTRY_ERROR_FMT_ARGS(ret), err_buf); - } - - /* Clean up the JSON parser */ - if ((ret = wolfsentry_config_json_fini(&jps, err_buf, sizeof err_buf)) < 0) + if (ret >= 0) { - printf("error while loading wolfSentry config data, " - WOLFSENTRY_ERROR_FMT ", msg: %s\n", - WOLFSENTRY_ERROR_FMT_ARGS(ret), err_buf); + ret = wolfsentry_config_json_feed(jps, + (const unsigned char *)wolfsentry_config_data, + strlen(wolfsentry_config_data), err_buf, sizeof err_buf); + if (ret < 0) + { + printf("error while loading wolfSentry config data, " + WOLFSENTRY_ERROR_FMT ", msg: %s\n", + WOLFSENTRY_ERROR_FMT_ARGS(ret), err_buf); + } } + /* Clean up the JSON parser. This commits the parsed config. */ + if (jps != NULL) + { + fini_ret = wolfsentry_config_json_fini(&jps, err_buf, sizeof err_buf); + if (fini_ret < 0) + { + printf("error while loading wolfSentry config data, " + WOLFSENTRY_ERROR_FMT ", msg: %s\n", + WOLFSENTRY_ERROR_FMT_ARGS(fini_ret), err_buf); + if (ret >= 0) + ret = fini_ret; + } + } + + if (ret < 0) + { + (void)wolfsentry_shutdown(WOLFSENTRY_CONTEXT_ARGS_OUT_EX4(&ctx, NULL)); + return ret; + } + + /* Publish the context only once the policy is committed, so a concurrent + * packet filter never dispatches against an empty route table. */ + WOLFSENTRY_ATOMIC_STORE(wolfsentry, ctx); + return 0; } @@ -247,7 +282,7 @@ int sentry_init(void) int sentry_action(ip_addr_t *local_ip, ip_addr_t *remote_ip, uint16_t local_port, uint16_t remote_port, sentry_action_type action) { wolfsentry_errcode_t ret; - wolfsentry_action_res_t action_results; + wolfsentry_action_res_t action_results = WOLFSENTRY_ACTION_RES_NONE; /* Note that sa.addr is 0 bytes, addr_buf essentially enlarges this to the correct size */ struct { struct wolfsentry_sockaddr sa; @@ -255,6 +290,11 @@ int sentry_action(ip_addr_t *local_ip, ip_addr_t *remote_ip, uint16_t local_port } remote, local; u32_t remoteip = remote_ip->addr; u32_t localip = local_ip->addr; + struct wolfsentry_context *ctx = WOLFSENTRY_ATOMIC_LOAD(wolfsentry); + + /* Fail closed until initialization has published a configured context */ + if (ctx == NULL) + return -1; /* Connect will increment the connection count in wolfSentry, disconnect * will decrement it */ @@ -289,7 +329,7 @@ int sentry_action(ip_addr_t *local_ip, ip_addr_t *remote_ip, uint16_t local_port /* Send the details of this to wolfSentry and get the result */ ret = wolfsentry_route_event_dispatch_with_inited_result( - WOLFSENTRY_CONTEXT_ARGS_OUT_EX4(wolfsentry, NULL), + WOLFSENTRY_CONTEXT_ARGS_OUT_EX4(ctx, NULL), &remote.sa, &local.sa, WOLFSENTRY_ROUTE_FLAG_DIRECTION_IN, @@ -304,11 +344,14 @@ int sentry_action(ip_addr_t *local_ip, ip_addr_t *remote_ip, uint16_t local_port WOLFSENTRY_ERROR_FMT_ARGS(ret)); fflush(stdout); + /* Fail closed: a dispatch error denies the connection */ + if (ret < 0) { + return -1; + } + /* Check the result, if it contains "reject" then notify the caller */ - if (WOLFSENTRY_ERROR_DECODE_ERROR_CODE(ret) >= 0) { - if (WOLFSENTRY_MASKIN_BITS(action_results, WOLFSENTRY_ACTION_RES_REJECT)) { - return -1; - } + if (WOLFSENTRY_MASKIN_BITS(action_results, WOLFSENTRY_ACTION_RES_REJECT)) { + return -1; } return 0; @@ -324,6 +367,11 @@ int sentry_action_ping(const ip_addr_t *addr, u8_t type) struct wolfsentry_sockaddr sa; byte addr_buf[4]; } remote; + struct wolfsentry_context *ctx = WOLFSENTRY_ATOMIC_LOAD(wolfsentry); + + /* Fail closed until initialization has published a configured context */ + if (ctx == NULL) + return -1; /* ICMP protocol check. The ICMP packet type is the port number for this * check */ @@ -335,7 +383,7 @@ int sentry_action_ping(const ip_addr_t *addr, u8_t type) memcpy(remote.sa.addr, &addr->addr, 4); ret = wolfsentry_route_event_dispatch( - WOLFSENTRY_CONTEXT_ARGS_OUT_EX4(wolfsentry, NULL), + WOLFSENTRY_CONTEXT_ARGS_OUT_EX4(ctx, NULL), &remote.sa, &remote.sa, /* Reuse for now */ WOLFSENTRY_ROUTE_FLAG_DIRECTION_IN, @@ -348,10 +396,14 @@ int sentry_action_ping(const ip_addr_t *addr, u8_t type) printf("PING Sentry action returned " WOLFSENTRY_ERROR_FMT "\n", WOLFSENTRY_ERROR_FMT_ARGS(ret)); fflush(stdout); - if (WOLFSENTRY_ERROR_DECODE_ERROR_CODE(ret) >= 0) { - if (WOLFSENTRY_MASKIN_BITS(action_results, WOLFSENTRY_ACTION_RES_REJECT)) { - return -1; - } + + /* Fail closed: a dispatch error denies the packet */ + if (ret < 0) { + return -1; + } + + if (WOLFSENTRY_MASKIN_BITS(action_results, WOLFSENTRY_ACTION_RES_REJECT)) { + return -1; } return 0; @@ -367,6 +419,11 @@ int sentry_action_mac(struct eth_addr *addr) struct wolfsentry_sockaddr sa; byte addr_buf[6]; } remote; + struct wolfsentry_context *ctx = WOLFSENTRY_ATOMIC_LOAD(wolfsentry); + + /* Fail closed until initialization has published a configured context */ + if (ctx == NULL) + return -1; /* We only really care about the data and length, the family is AF_LINK */ remote.sa.sa_family = WOLFSENTRY_AF_LINK; @@ -376,7 +433,7 @@ int sentry_action_mac(struct eth_addr *addr) memcpy(remote.sa.addr, &addr->addr, 6); ret = wolfsentry_route_event_dispatch( - WOLFSENTRY_CONTEXT_ARGS_OUT_EX4(wolfsentry, NULL), + WOLFSENTRY_CONTEXT_ARGS_OUT_EX4(ctx, NULL), &remote.sa, &remote.sa, /* Reuse for now */ WOLFSENTRY_ROUTE_FLAG_DIRECTION_IN, @@ -390,10 +447,14 @@ int sentry_action_mac(struct eth_addr *addr) printf("MAC Sentry action returned " WOLFSENTRY_ERROR_FMT "\n", WOLFSENTRY_ERROR_FMT_ARGS(ret)); fflush(stdout); - if (WOLFSENTRY_ERROR_DECODE_ERROR_CODE(ret) >= 0) { - if (WOLFSENTRY_MASKIN_BITS(action_results, WOLFSENTRY_ACTION_RES_REJECT)) { - return -1; - } + + /* Fail closed: a dispatch error denies the frame */ + if (ret < 0) { + return -1; + } + + if (WOLFSENTRY_MASKIN_BITS(action_results, WOLFSENTRY_ACTION_RES_REJECT)) { + return -1; } return 0; diff --git a/src/lwip/packet_filter_glue.c b/src/lwip/packet_filter_glue.c index f8d3b60..a7cca6d 100644 --- a/src/lwip/packet_filter_glue.c +++ b/src/lwip/packet_filter_glue.c @@ -105,7 +105,7 @@ static err_t ethernet_filter_with_wolfsentry( #endif if (wolfsentry == NULL) - WOLFSENTRY_RETURN_VALUE(ERR_OK); + WOLFSENTRY_RETURN_VALUE(ERR_ABRT); switch(event->reason) { case FILT_RECEIVING: @@ -186,7 +186,10 @@ static err_t ethernet_filter_with_wolfsentry( WOLFSENTRY_WARN_ON_FAILURE(ws_ret); - if (WOLFSENTRY_MASKIN_BITS(action_results, WOLFSENTRY_ACTION_RES_REJECT)) + /* fail closed: a failed dispatch leaves no trustworthy verdict. */ + if (WOLFSENTRY_IS_FAILURE(ws_ret)) + ret = ERR_ABRT; + else if (WOLFSENTRY_MASKIN_BITS(action_results, WOLFSENTRY_ACTION_RES_REJECT)) ret = ERR_ABRT; else ret = ERR_OK; @@ -242,7 +245,7 @@ static err_t ip4_filter_with_wolfsentry( #endif if (wolfsentry == NULL) - WOLFSENTRY_RETURN_VALUE(ERR_OK); + WOLFSENTRY_RETURN_VALUE(ERR_ABRT); switch(event->reason) { case FILT_RECEIVING: @@ -326,7 +329,10 @@ static err_t ip4_filter_with_wolfsentry( WOLFSENTRY_WARN_ON_FAILURE(ws_ret); - if (WOLFSENTRY_MASKIN_BITS(action_results, WOLFSENTRY_ACTION_RES_REJECT)) + /* fail closed: a failed dispatch leaves no trustworthy verdict. */ + if (WOLFSENTRY_IS_FAILURE(ws_ret)) + ret = ERR_ABRT; + else if (WOLFSENTRY_MASKIN_BITS(action_results, WOLFSENTRY_ACTION_RES_REJECT)) ret = ERR_ABRT; else ret = ERR_OK; @@ -377,7 +383,7 @@ static err_t ip6_filter_with_wolfsentry( #endif if (wolfsentry == NULL) - WOLFSENTRY_RETURN_VALUE(ERR_OK); + WOLFSENTRY_RETURN_VALUE(ERR_ABRT); switch(event->reason) { case FILT_RECEIVING: @@ -461,7 +467,10 @@ static err_t ip6_filter_with_wolfsentry( WOLFSENTRY_WARN_ON_FAILURE(ws_ret); - if (WOLFSENTRY_MASKIN_BITS(action_results, WOLFSENTRY_ACTION_RES_REJECT)) + /* fail closed: a failed dispatch leaves no trustworthy verdict. */ + if (WOLFSENTRY_IS_FAILURE(ws_ret)) + ret = ERR_ABRT; + else if (WOLFSENTRY_MASKIN_BITS(action_results, WOLFSENTRY_ACTION_RES_REJECT)) ret = ERR_ABRT; else ret = ERR_OK; @@ -515,7 +524,7 @@ static err_t tcp_filter_with_wolfsentry( #endif if (wolfsentry == NULL) - WOLFSENTRY_RETURN_VALUE(ERR_OK); + WOLFSENTRY_RETURN_VALUE(ERR_ABRT); switch(event->reason) { case FILT_ACCEPTING: @@ -540,6 +549,7 @@ static err_t tcp_filter_with_wolfsentry( route_flags |= WOLFSENTRY_ROUTE_FLAG_DIRECTION_IN; else route_flags |= WOLFSENTRY_ROUTE_FLAG_DIRECTION_OUT; + action_results = WOLFSENTRY_ACTION_RES_CLOSE_WAIT; break; case FILT_PORT_UNREACHABLE: route_flags |= WOLFSENTRY_ROUTE_FLAG_DIRECTION_IN; @@ -662,7 +672,10 @@ static err_t tcp_filter_with_wolfsentry( WOLFSENTRY_WARN_ON_FAILURE(ws_ret); - if (WOLFSENTRY_MASKIN_BITS(action_results, WOLFSENTRY_ACTION_RES_PORT_RESET)) + /* fail closed: a failed dispatch leaves no trustworthy verdict. */ + if (WOLFSENTRY_IS_FAILURE(ws_ret)) + ret = ERR_ABRT; + else if (WOLFSENTRY_MASKIN_BITS(action_results, WOLFSENTRY_ACTION_RES_PORT_RESET)) ret = ERR_RST; else if (WOLFSENTRY_MASKIN_BITS(action_results, WOLFSENTRY_ACTION_RES_REJECT)) ret = ERR_ABRT; @@ -727,7 +740,7 @@ static err_t udp_filter_with_wolfsentry( #endif if (wolfsentry == NULL) - WOLFSENTRY_RETURN_VALUE(ERR_OK); + WOLFSENTRY_RETURN_VALUE(ERR_ABRT); switch(event->reason) { case FILT_BINDING: @@ -851,7 +864,10 @@ static err_t udp_filter_with_wolfsentry( WOLFSENTRY_WARN_ON_FAILURE(ws_ret); - if (WOLFSENTRY_MASKIN_BITS(action_results, WOLFSENTRY_ACTION_RES_PORT_RESET)) + /* fail closed: a failed dispatch leaves no trustworthy verdict. */ + if (WOLFSENTRY_IS_FAILURE(ws_ret)) + ret = ERR_ABRT; + else if (WOLFSENTRY_MASKIN_BITS(action_results, WOLFSENTRY_ACTION_RES_PORT_RESET)) ret = ERR_RST; else if (WOLFSENTRY_MASKIN_BITS(action_results, WOLFSENTRY_ACTION_RES_REJECT)) ret = ERR_ABRT; @@ -914,7 +930,7 @@ static err_t icmp4_filter_with_wolfsentry( #endif if (wolfsentry == NULL) - WOLFSENTRY_RETURN_VALUE(ERR_OK); + WOLFSENTRY_RETURN_VALUE(ERR_ABRT); switch(event->reason) { case FILT_RECEIVING: @@ -999,7 +1015,10 @@ static err_t icmp4_filter_with_wolfsentry( WOLFSENTRY_WARN_ON_FAILURE(ws_ret); - if (WOLFSENTRY_MASKIN_BITS(action_results, WOLFSENTRY_ACTION_RES_REJECT)) + /* fail closed: a failed dispatch leaves no trustworthy verdict. */ + if (WOLFSENTRY_IS_FAILURE(ws_ret)) + ret = ERR_ABRT; + else if (WOLFSENTRY_MASKIN_BITS(action_results, WOLFSENTRY_ACTION_RES_REJECT)) ret = ERR_ABRT; else ret = ERR_OK; @@ -1050,7 +1069,7 @@ static err_t icmp6_filter_with_wolfsentry( #endif if (wolfsentry == NULL) - WOLFSENTRY_RETURN_VALUE(ERR_OK); + WOLFSENTRY_RETURN_VALUE(ERR_ABRT); switch(event->reason) { case FILT_RECEIVING: @@ -1135,7 +1154,10 @@ static err_t icmp6_filter_with_wolfsentry( WOLFSENTRY_WARN_ON_FAILURE(ws_ret); - if (WOLFSENTRY_MASKIN_BITS(action_results, WOLFSENTRY_ACTION_RES_REJECT)) + /* fail closed: a failed dispatch leaves no trustworthy verdict. */ + if (WOLFSENTRY_IS_FAILURE(ws_ret)) + ret = ERR_ABRT; + else if (WOLFSENTRY_MASKIN_BITS(action_results, WOLFSENTRY_ACTION_RES_REJECT)) ret = ERR_ABRT; else ret = ERR_OK; @@ -1148,8 +1170,118 @@ static err_t icmp6_filter_with_wolfsentry( #endif /* LWIP_ICMP6 */ +#define LWIP_FILTER_SLOT_ETHERNET (1U << 0) +#define LWIP_FILTER_SLOT_IP (1U << 1) +#define LWIP_FILTER_SLOT_ICMP (1U << 2) +#define LWIP_FILTER_SLOT_TCP (1U << 3) +#define LWIP_FILTER_SLOT_UDP (1U << 4) + +/* lwIP has no getters for its filter slots, so the owning context is tracked + * here. Contexts hold only their own mutex, so ownership changes atomically + * and only the owner touches the slots and the lwIP setters. */ +static struct wolfsentry_context *lwip_filter_owner = NULL; +static unsigned int lwip_filter_slots = 0; + +static wolfsentry_errcode_t lwip_filter_claim_slot(WOLFSENTRY_CONTEXT_ARGS_IN, unsigned int slot) +{ + wolfsentry_errcode_t ret; +#ifdef WOLFSENTRY_THREADSAFE + struct wolfsentry_context *expected = NULL; + + if ((! WOLFSENTRY_ATOMIC_CMPXCHG(&lwip_filter_owner, &expected, wolfsentry, 0, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST)) && + (expected != wolfsentry)) + { + WOLFSENTRY_ERROR_RETURN(INCOMPATIBLE_STATE); + } +#else + if ((lwip_filter_owner != NULL) && (lwip_filter_owner != wolfsentry)) + WOLFSENTRY_ERROR_RETURN(INCOMPATIBLE_STATE); + lwip_filter_owner = wolfsentry; +#endif + ret = wolfsentry_cleanup_push(WOLFSENTRY_CONTEXT_ARGS_OUT, wolfsentry_cleanup_lwip_filter_callbacks, NULL); + if (ret < 0) { + if (lwip_filter_slots == 0) + WOLFSENTRY_ATOMIC_STORE(lwip_filter_owner, NULL); + WOLFSENTRY_ERROR_RERETURN(ret); + } + lwip_filter_slots |= slot; + WOLFSENTRY_RETURN_OK; +} + +static void lwip_filter_clear_slot(unsigned int slot) +{ + switch (slot) { + case LWIP_FILTER_SLOT_ETHERNET: +#if LWIP_ARP || LWIP_ETHERNET + ethernet_filter(NULL); + ethernet_filter_mask(0); + ethernet_filter_arg(NULL); +#endif + break; + case LWIP_FILTER_SLOT_IP: +#if LWIP_IPV4 + ip4_filter(NULL); + ip4_filter_mask(0); + ip4_filter_arg(NULL); +#endif +#if LWIP_IPV6 + ip6_filter(NULL); + ip6_filter_mask(0); + ip6_filter_arg(NULL); +#endif + break; + case LWIP_FILTER_SLOT_ICMP: +#if LWIP_ICMP + icmp_filter(NULL); + icmp_filter_mask(0); + icmp_filter_arg(NULL); +#endif +#if LWIP_ICMP6 + icmp6_filter(NULL); + icmp6_filter_mask(0); + icmp6_filter_arg(NULL); +#endif + break; + case LWIP_FILTER_SLOT_TCP: +#if LWIP_TCP + tcp_filter(NULL); + tcp_filter_mask(0); + tcp_filter_arg(NULL); +#endif + break; + case LWIP_FILTER_SLOT_UDP: +#if LWIP_UDP + udp_filter(NULL); + udp_filter_mask(0); + udp_filter_arg(NULL); +#endif + break; + } +} + +static wolfsentry_errcode_t lwip_filter_release_slot(WOLFSENTRY_CONTEXT_ARGS_IN, unsigned int slot) +{ + struct wolfsentry_context *owner = WOLFSENTRY_ATOMIC_LOAD(lwip_filter_owner); + + WOLFSENTRY_CONTEXT_ARGS_THREAD_NOT_USED; + /* with no owner, no wolfSentry callbacks are installed, so there is nothing to clear. */ + if (owner == NULL) + WOLFSENTRY_RETURN_OK; + if (owner != wolfsentry) + WOLFSENTRY_ERROR_RETURN(INCOMPATIBLE_STATE); + lwip_filter_clear_slot(slot); + lwip_filter_slots &= ~slot; + /* ownership is released only after the last slot is fully cleared. */ + if (lwip_filter_slots == 0) + WOLFSENTRY_ATOMIC_STORE(lwip_filter_owner, NULL); + WOLFSENTRY_RETURN_OK; +} + WOLFSENTRY_API_VOID wolfsentry_cleanup_lwip_filter_callbacks(WOLFSENTRY_CONTEXT_ARGS_IN, void *cleanup_arg) { (void)cleanup_arg; + /* a destroyed non-owner must not disable the surviving owner's filters. */ + if (WOLFSENTRY_ATOMIC_LOAD(lwip_filter_owner) != wolfsentry) + return; WOLFSENTRY_WARN_ON_FAILURE(wolfsentry_install_lwip_filter_callbacks(WOLFSENTRY_CONTEXT_ARGS_OUT, 0, 0, 0, 0, 0)); } @@ -1158,17 +1290,18 @@ WOLFSENTRY_API wolfsentry_errcode_t wolfsentry_install_lwip_filter_ethernet_call packet_filter_event_mask_t ethernet_mask) { #if LWIP_ARP || LWIP_ETHERNET + wolfsentry_errcode_t ret; + WOLFSENTRY_MUTEX_OR_RETURN(); if (ethernet_mask) { - wolfsentry_errcode_t ret = wolfsentry_cleanup_push(WOLFSENTRY_CONTEXT_ARGS_OUT, wolfsentry_cleanup_lwip_filter_callbacks, NULL); + ret = lwip_filter_claim_slot(WOLFSENTRY_CONTEXT_ARGS_OUT, LWIP_FILTER_SLOT_ETHERNET); WOLFSENTRY_UNLOCK_AND_RERETURN_IF_ERROR(ret); - ethernet_filter(ethernet_filter_with_wolfsentry); - ethernet_filter_mask(ethernet_mask); ethernet_filter_arg((void *)wolfsentry); + ethernet_filter_mask(ethernet_mask); + ethernet_filter(ethernet_filter_with_wolfsentry); } else { - ethernet_filter(NULL); - ethernet_filter_mask(0); - ethernet_filter_arg(NULL); + ret = lwip_filter_release_slot(WOLFSENTRY_CONTEXT_ARGS_OUT, LWIP_FILTER_SLOT_ETHERNET); + WOLFSENTRY_UNLOCK_AND_RERETURN_IF_ERROR(ret); } WOLFSENTRY_UNLOCK_AND_RETURN_OK; #else @@ -1184,33 +1317,28 @@ WOLFSENTRY_API wolfsentry_errcode_t wolfsentry_install_lwip_filter_ip_callbacks( packet_filter_event_mask_t ip_mask) { #if LWIP_IPV4 || LWIP_IPV6 + wolfsentry_errcode_t ret; + WOLFSENTRY_MUTEX_OR_RETURN(); - if (ip_mask) { - wolfsentry_errcode_t ret = wolfsentry_cleanup_push(WOLFSENTRY_CONTEXT_ARGS_OUT, wolfsentry_cleanup_lwip_filter_callbacks, NULL); - WOLFSENTRY_UNLOCK_AND_RERETURN_IF_ERROR(ret); - } + if (ip_mask) + ret = lwip_filter_claim_slot(WOLFSENTRY_CONTEXT_ARGS_OUT, LWIP_FILTER_SLOT_IP); + else + ret = lwip_filter_release_slot(WOLFSENTRY_CONTEXT_ARGS_OUT, LWIP_FILTER_SLOT_IP); + WOLFSENTRY_UNLOCK_AND_RERETURN_IF_ERROR(ret); #endif #if LWIP_IPV4 if (ip_mask) { - ip4_filter(ip4_filter_with_wolfsentry); - ip4_filter_mask(ip_mask); ip4_filter_arg((void *)wolfsentry); - } else { - ip4_filter(NULL); - ip4_filter_mask(0); - ip4_filter_arg(NULL); + ip4_filter_mask(ip_mask); + ip4_filter(ip4_filter_with_wolfsentry); } #endif /* LWIP_IPV4 */ #if LWIP_IPV6 if (ip_mask) { - ip6_filter(ip6_filter_with_wolfsentry); - ip6_filter_mask(ip_mask); ip6_filter_arg((void *)wolfsentry); - } else { - ip6_filter(NULL); - ip6_filter_mask(0); - ip6_filter_arg(NULL); + ip6_filter_mask(ip_mask); + ip6_filter(ip6_filter_with_wolfsentry); } #endif /* LWIP_IPV6 */ @@ -1229,30 +1357,28 @@ WOLFSENTRY_API wolfsentry_errcode_t wolfsentry_install_lwip_filter_icmp_callback packet_filter_event_mask_t icmp_mask) { #if LWIP_ICMP || LWIP_ICMP6 + wolfsentry_errcode_t ret; + WOLFSENTRY_MUTEX_OR_RETURN(); - if (icmp_mask) { - wolfsentry_errcode_t ret = wolfsentry_cleanup_push(WOLFSENTRY_CONTEXT_ARGS_OUT, wolfsentry_cleanup_lwip_filter_callbacks, NULL); - WOLFSENTRY_UNLOCK_AND_RERETURN_IF_ERROR(ret); - } + if (icmp_mask) + ret = lwip_filter_claim_slot(WOLFSENTRY_CONTEXT_ARGS_OUT, LWIP_FILTER_SLOT_ICMP); + else + ret = lwip_filter_release_slot(WOLFSENTRY_CONTEXT_ARGS_OUT, LWIP_FILTER_SLOT_ICMP); + WOLFSENTRY_UNLOCK_AND_RERETURN_IF_ERROR(ret); #endif #if LWIP_ICMP if (icmp_mask) { - icmp_filter(icmp4_filter_with_wolfsentry); - icmp_filter_mask(icmp_mask); icmp_filter_arg((void *)wolfsentry); - } else - icmp_filter(NULL); + icmp_filter_mask(icmp_mask); + icmp_filter(icmp4_filter_with_wolfsentry); + } #endif /* LWIP_ICMP */ #if LWIP_ICMP6 if (icmp_mask) { - icmp6_filter(icmp6_filter_with_wolfsentry); - icmp6_filter_mask(icmp_mask); icmp6_filter_arg((void *)wolfsentry); - } else { - icmp6_filter(NULL); - icmp6_filter_mask(0); - icmp6_filter_arg(NULL); + icmp6_filter_mask(icmp_mask); + icmp6_filter(icmp6_filter_with_wolfsentry); } #endif /* LWIP_ICMP6 */ @@ -1271,22 +1397,23 @@ WOLFSENTRY_API wolfsentry_errcode_t wolfsentry_install_lwip_filter_tcp_callback( packet_filter_event_mask_t tcp_mask) { #if LWIP_TCP + wolfsentry_errcode_t ret; + WOLFSENTRY_MUTEX_OR_RETURN(); if (tcp_mask) { - wolfsentry_errcode_t ret = wolfsentry_cleanup_push(WOLFSENTRY_CONTEXT_ARGS_OUT, wolfsentry_cleanup_lwip_filter_callbacks, NULL); + ret = lwip_filter_claim_slot(WOLFSENTRY_CONTEXT_ARGS_OUT, LWIP_FILTER_SLOT_TCP); WOLFSENTRY_UNLOCK_AND_RERETURN_IF_ERROR(ret); - tcp_filter(tcp_filter_with_wolfsentry); /* make sure wolfSentry sees the close/reset events that balance earlier * accepts, for concurrent-connection accounting purposes. */ if (tcp_mask & (FILT_MASK(ACCEPTING) | FILT_MASK(CLOSED) | FILT_MASK(REMOTE_RESET))) tcp_mask |= FILT_MASK(ACCEPTING) | FILT_MASK(CLOSED) | FILT_MASK(REMOTE_RESET); - tcp_filter_mask(tcp_mask); tcp_filter_arg((void *)wolfsentry); + tcp_filter_mask(tcp_mask); + tcp_filter(tcp_filter_with_wolfsentry); } else { - tcp_filter(NULL); - tcp_filter_mask(0); - tcp_filter_arg(NULL); + ret = lwip_filter_release_slot(WOLFSENTRY_CONTEXT_ARGS_OUT, LWIP_FILTER_SLOT_TCP); + WOLFSENTRY_UNLOCK_AND_RERETURN_IF_ERROR(ret); } WOLFSENTRY_UNLOCK_AND_RETURN_OK; #else @@ -1302,17 +1429,18 @@ WOLFSENTRY_API wolfsentry_errcode_t wolfsentry_install_lwip_filter_udp_callback( packet_filter_event_mask_t udp_mask) { #if LWIP_UDP + wolfsentry_errcode_t ret; + WOLFSENTRY_MUTEX_OR_RETURN(); if (udp_mask) { - wolfsentry_errcode_t ret = wolfsentry_cleanup_push(WOLFSENTRY_CONTEXT_ARGS_OUT, wolfsentry_cleanup_lwip_filter_callbacks, NULL); + ret = lwip_filter_claim_slot(WOLFSENTRY_CONTEXT_ARGS_OUT, LWIP_FILTER_SLOT_UDP); WOLFSENTRY_UNLOCK_AND_RERETURN_IF_ERROR(ret); - udp_filter(udp_filter_with_wolfsentry); - udp_filter_mask(udp_mask); udp_filter_arg((void *)wolfsentry); + udp_filter_mask(udp_mask); + udp_filter(udp_filter_with_wolfsentry); } else { - udp_filter(NULL); - udp_filter_mask(0); - udp_filter_arg(NULL); + ret = lwip_filter_release_slot(WOLFSENTRY_CONTEXT_ARGS_OUT, LWIP_FILTER_SLOT_UDP); + WOLFSENTRY_UNLOCK_AND_RERETURN_IF_ERROR(ret); } WOLFSENTRY_UNLOCK_AND_RETURN_OK; #else diff --git a/wolfsentry/wolfsentry_lwip.h b/wolfsentry/wolfsentry_lwip.h index 2a459f9..202d7c4 100644 --- a/wolfsentry/wolfsentry_lwip.h +++ b/wolfsentry/wolfsentry_lwip.h @@ -39,6 +39,8 @@ * `FILT_PORT_UNREACHABLE` -- Call into wolfSentry when inbound traffic attempts to reach an unlistened/unbound port
* `FILT_INBOUND_ERR` -- Call into wolfSentry when inbound traffic results in detection of an error by lwIP
* `FILT_OUTBOUND_ERR` -- Call into wolfSentry when outbound traffic results in detection of an error by lwIP
+ * + * lwIP has a single process-wide set of filter slots, so at most one wolfSentry context can own them at a time. The first context to install a callback with a nonzero mask becomes the owner of all slots. While it owns them, any installation or disabling call from a different context fails with `WOLFSENTRY_ERROR_ID_INCOMPATIBLE_STATE` and leaves the owner's callbacks in place. Ownership is released when the owner disables every slot it installed (mask zero), or when the owner is shut down, which runs `wolfsentry_cleanup_lwip_filter_callbacks()`; a replacement context can install only after that. A filter callback denies the packet whenever the wolfSentry dispatch fails or no context is attached. */ #ifndef WOLFSENTRY_LWIP_H