21#include "freertos/FreeRTOS.h"
22#include "freertos/queue.h"
26#include "lwip/ip_addr.h"
27#include "lwip/priv/tcpip_priv.h"
37extern err_t
lowlevel_recv_cb(
void *arg,
struct tcp_pcb *tpcb,
struct pbuf *p, err_t err);
136 int slot = (empty >= 0) ? empty : (expired >= 0) ? expired : lru;
181 if (bits < 0 || prefix_len > (uint8_t)bits)
199 const char *slash =
nullptr;
201 for (
const char *p = cidr; *p; p++)
208 if (n + 1 >=
sizeof(addr))
220 uint8_t prefix = width;
225 const char *p = slash + 1;
230 if (*p <
'0' || *p >
'9')
232 v = v * 10 + (uint32_t)(*p -
'0');
244 if (s_allow.
count == 0)
246 for (uint8_t i = 0; i < s_allow.
count; i++)
262#if DETWS_WORKER_COUNT > 1
268struct ListenerQueueCtx
274static ListenerQueueCtx s_lq;
276void listener_worker_queues_init(
void)
280 s_lq.wq[i] = xQueueCreateStatic(
EVT_QUEUE_DEPTH,
sizeof(
TcpEvt), s_lq.wq_storage[i], &s_lq.wq_struct[i]);
283QueueHandle_t listener_worker_queue(
int worker_id)
287 return s_lq.wq[worker_id];
293#if DETWS_WORKER_COUNT > 1
299 if (xQueueSend(s_lq.wq[owner], evt, 0) != pdTRUE)
310 if (xQueueSend(lst->
queue, evt, 0) != pdTRUE)
328static err_t listener_accept_cb(
void *arg,
struct tcp_pcb *newpcb, err_t err)
330 if (err != ERR_OK || newpcb ==
nullptr)
333 uint8_t idx = (uint8_t)(uintptr_t)arg;
338#if DETWS_ENABLE_ACCEPT_THROTTLE
348#if DETWS_ENABLE_PER_IP_THROTTLE || DETWS_ENABLE_IP_ALLOWLIST
360#if DETWS_ENABLE_PER_IP_THROTTLE
371#if DETWS_ENABLE_IP_ALLOWLIST
399#if DETWS_WORKER_COUNT > 1
404 static uint8_t s_next_owner = 0;
405 slot->
owner = s_next_owner;
424 uint32_t lip = ip4_addr_get_u32(ip_2_ip4(&newpcb->local_ip));
431 tcp_arg(newpcb, slot);
437 tcp_nagle_disable(newpcb);
442 slot->
tls = lst->
tls ? 1 : 0;
444 det_tls_conn_begin(free_slot);
453 DetConnReason::DET_CONN_R_ACCEPT);
463static err_t listener_lwip_marshal(uint8_t idx, uint16_t port,
bool create);
473#if DETWS_WORKER_COUNT > 1
474 listener_worker_queues_init();
492 if (listener_lwip_marshal(idx, port,
true) != ERR_OK)
494 vQueueDelete(lst->
queue);
495 lst->
queue =
nullptr;
499 struct tcp_pcb *pcb = tcp_new_ip_type(IPADDR_TYPE_ANY);
503 err_t bind_err = tcp_bind(pcb, IP_ANY_TYPE, port);
504 if (bind_err != ERR_OK)
517 tcp_arg(lst->
listen_pcb, (
void *)(uintptr_t)idx);
518 tcp_accept(lst->
listen_pcb, listener_accept_cb);
534 listener_lwip_marshal(idx, 0,
false);
540 vQueueDelete(lst->
queue);
541 lst->
queue =
nullptr;
562 struct tcpip_api_call_data
base;
570static err_t listener_lwip_do(
struct tcpip_api_call_data *c)
577 struct tcp_pcb *pcb = tcp_new_ip_type(IPADDR_TYPE_ANY);
583 if (tcp_bind(pcb, IP_ANY_TYPE, k->
port) != ERR_OK)
589 struct tcp_pcb *lp = tcp_listen_with_backlog(pcb,
MAX_CONNS);
596 tcp_arg(lp, (
void *)(uintptr_t)k->
idx);
597 tcp_accept(lp, listener_accept_cb);
608static err_t listener_lwip_marshal(uint8_t idx, uint16_t port,
bool create)
614 tcpip_api_call(listener_lwip_do, &k.
base);
625#if DETWS_WORKER_COUNT > 1
626 listener_worker_queues_init();
641 if (listener_lwip_marshal(idx, port,
true) != ERR_OK)
643 vQueueDelete(lst->
queue);
644 lst->
queue =
nullptr;
664 listener_lwip_marshal(idx, 0,
false);
670 vQueueDelete(lst->
queue);
671 lst->
queue =
nullptr;
#define DETWS_ACCEPT_THROTTLE_MAX
Max accepted connections per throttle window (see DETWS_ENABLE_ACCEPT_THROTTLE).
#define DETWS_PER_IP_THROTTLE_WINDOW_MS
Per-IP throttle window length in milliseconds (see DETWS_ENABLE_PER_IP_THROTTLE).
DetIface
Network interface a connection arrived on (for per-route filtering).
@ DETIFACE_STA
Station interface (joined to an AP / your LAN).
@ DETIFACE_ANY
Unknown / no filter (matches any interface).
@ DETIFACE_AP
softAP interface (clients joined to the device).
#define DETWS_ACCEPT_THROTTLE_WINDOW_MS
Throttle window length in milliseconds (see DETWS_ENABLE_ACCEPT_THROTTLE).
#define DETWS_PER_IP_THROTTLE_MAX
Max accepted connections per window from one source IP (see DETWS_ENABLE_PER_IP_THROTTLE).
#define DETWS_PER_IP_THROTTLE_SLOTS
Number of source IPv4 addresses tracked by the per-IP throttle (BSS bucket table).
#define MAX_LISTENERS
Maximum number of simultaneously active listener ports.
ConnProto
Application protocol spoken on a listener port or connection slot.
#define DETWS_WORKER_COUNT
Number of server worker tasks (slots partitioned i % N). Default 1.
#define EVT_QUEUE_DEPTH
Depth of the FreeRTOS event queue shared between lwIP callbacks and the main-loop task.
#define MAX_CONNS
Maximum simultaneous TCP connections (fixed static pool; ~3.95 KB of internal RAM per slot).
#define DETWS_IP_ALLOWLIST_SLOTS
Number of CIDR rules the source-IP allowlist can hold (BSS table).
Pluggable monotonic clock for all library timing.
uint32_t detws_millis(void)
The library's monotonic time at 1000 Hz (milliseconds).
bool det_ip_prefix_match(const DetIp *addr, const DetIp *net, uint8_t prefix_len)
CIDR containment: is addr inside the net / prefix_len block?
bool det_ip_is_unspecified(const DetIp *ip)
True if ip is empty (DetIpFamily::DET_IP_NONE) or the all-zero unspecified address (0....
bool det_ip_equal(const DetIp *a, const DetIp *b)
True if a and b are the same family and address.
bool det_ip_parse(const char *s, DetIp *out)
Parse an IPv4 or IPv6 textual address (RFC 4291 §2.2) into out.
#define DET_IP_STR_MAX
Longest text an det_ip_format can produce, including the NUL (RFC 5952 v4-mapped).
@ DET_IP_V4
IPv4 (bytes[0..3])
@ DET_IP_V6
IPv6 (bytes[0..15])
@ DET_IP_NONE
empty / unparsed
bool listener_enqueue(uint8_t listener_id, const TcpEvt *evt)
Post evt to the queue owned by listener listener_id.
bool listener_ip_allow_add(const DetIp *network, uint8_t prefix_len)
Add a CIDR rule to the source-IP allowlist.
int32_t listener_add(uint8_t idx, uint16_t port, ConnProto proto, bool tls)
Create a listening socket on port and register it at idx.
void listener_stop_all()
Stop all active listeners.
err_t lowlevel_recv_cb(void *arg, struct tcp_pcb *tpcb, struct pbuf *p, err_t err)
lwIP receive callback - fires when data arrives on a connection.
bool listener_accept_allowed_ip(const DetIp *ip, uint32_t now_ms)
Fixed-window per-IP accept-rate gate (connection-flood defense, keyed by source address).
void listener_per_ip_throttle_reset(void)
Reset the per-IP throttle bucket table.
err_t lowlevel_sent_cb(void *arg, struct tcp_pcb *tpcb, u16_t len)
lwIP sent callback - fires after the stack acknowledges sent bytes.
void lowlevel_err_cb(void *arg, err_t err)
lwIP error callback - fires when the stack detects a fatal error.
void listener_stop(uint8_t idx)
Stop listening on the port at idx and release its resources.
bool listener_ip_allowed(const DetIp *ip)
Test a source address against the allowlist (accept-time firewall).
int32_t listener_add_dynamic(uint8_t idx, uint16_t port, ConnProto proto)
Add / stop a listener from a running task (thread-safe variant).
bool listener_accept_allowed(uint32_t now_ms)
Fixed-window global accept-rate gate (connection-flood defense).
bool listener_ip_allow_add_cidr(const char *cidr)
Add an allowlist rule from CIDR text (the ergonomic public entry point).
void listener_accept_throttle_reset(void)
Reset the accept-throttle window counters.
Listener listener_pool[MAX_LISTENERS]
Static pool of listener contexts. Defined in listener.cpp.
void listener_stop_dynamic(uint8_t idx)
void listener_ip_allowlist_reset(void)
Clear all allowlist rules (the allowlist becomes empty = allow all).
Layer 4 (Listener) - per-port TCP listener abstraction.
A v4 or v6 address in network (big-endian) byte order.
DetIpFamily family
address family tag
struct tcpip_api_call_data base
IpAllowRule rules[DETWS_IP_ALLOWLIST_SLOTS]
uint8_t prefix_len
CIDR prefix length: 0..32 for v4, 0..128 for v6.
DetIp network
network address (family DetIpFamily::DET_IP_V4 / V6; DetIpFamily::DET_IP_NONE marks unused).
DetIp addr
source address (family DetIpFamily::DET_IP_NONE marks an empty bucket).
uint16_t count
connections counted from this address in the window.
uint32_t window_start
millis() at the start of this bucket's current window.
IpThrottleBucket buckets[DETWS_PER_IP_THROTTLE_SLOTS]
State for one TCP listening port.
QueueHandle_t queue
Handle returned by xQueueCreateStatic().
ConnProto proto
Application protocol for all connections accepted here.
bool tls
True when connections accepted here begin a TLS handshake.
uint8_t _queue_storage[EVT_QUEUE_DEPTH *sizeof(TcpEvt)]
Queue backing store.
uint16_t port
TCP port this listener binds.
bool active
True after listener_add(), false after listener_stop().
StaticQueue_t _queue_struct
FreeRTOS static queue descriptor.
struct tcp_pcb * listen_pcb
lwIP listen PCB; nullptr when inactive.
A single TCP connection context.
DetIface iface
Interface this connection arrived on; set at accept time.
struct tcp_pcb * pcb
lwIP PCB; null when slot is free.
uint32_t last_activity_ms
millis() timestamp of last TX/RX event.
uint8_t owner
Worker that owns this slot (round-robin at accept). Always 0 at N=1.
uint8_t listener_id
Index into listener_pool[]; set at accept time.
DetAtomic< size_t > rx_tail
Consumer read index (worker context).
DetAtomic< size_t > rx_head
Producer write index (lwIP/tcpip context).
DetAtomic< ConnState > state
Lifecycle state; acquire/release for inter-task visibility.
uint8_t tls
Non-zero when this connection is TLS (set at accept time).
ConnProto proto
Application protocol for this connection.
Event record posted from lwIP callbacks to the session layer.
uint8_t slot_id
Which connection slot is affected.
uint32_t detws_ap_ip
softAP IPv4 address (network byte order) for STA/AP interface tagging.
TcpConn conn_pool[CONN_POOL_SLOTS]
Static pool of connection contexts. Defined in tcp.cpp. Sized CONN_POOL_SLOTS: MAX_CONNS TCP slots pl...
void det_lwip_to_detip(const ip_addr_t *ra, DetIp *out)
A stable per-peer 32-bit identity key for slot (the v4 address, or an FNV-1a hash of a v6 address)....
#define DETWS_OBS_NOTICE(slot, st, reason)
@ EVT_CONNECT
New connection accepted.
#define DETWS_OBS_TRANSITION(slot, olds, news, reason)
@ CONN_ACTIVE
Live connection; PCB is valid.
@ CONN_FREE
Slot is available; no PCB is attached.
Deterministic TLS engine: mbedTLS over a static memory pool (DETWS_ENABLE_TLS).
void detws_worker_wake(int worker_id)
Wake worker worker_id so it services a freshly-queued event now.
Layer 5 (Session) - server worker identity.