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