16#if DETWS_ENABLE_FORWARD
37 uint32_t window_start;
42 det_fwd_action action;
53 det_fwd_action action;
60 uint32_t window_start;
80 det_fwd_action acl_default = det_fwd_action::DET_FWD_ALLOW;
82 det_fwd_inspect_fn inspector =
nullptr;
83 void *inspect_ctx =
nullptr;
85 det_forward_stats stats;
104const iface *find_if(
const ForwardCtx &f, uint8_t
id)
107 if (f.if_[i].used && f.if_[i].id ==
id)
114enum class resolve_result : uint8_t
120resolve_result resolve(
const ForwardCtx &f, uint8_t src, uint8_t dst,
int *allow_idx)
126 if (!f.rules[i].used || f.rules[i].src != src || f.rules[i].dst != dst)
128 if (f.rules[i].action == det_fwd_action::DET_FWD_DENY)
134 return resolve_result::R_DENY;
138 return resolve_result::R_ALLOW;
140 return resolve_result::R_NOROUTE;
145bool rate_gate(uint32_t &window_start, uint16_t &count, uint16_t rate_cap)
149 uint32_t now = fwd_now();
150 if ((now - window_start) >= 1000)
155 if (count >= rate_cap)
161bool rate_exceeded(rule *r)
163 return rate_gate(r->window_start, r->count, r->rate_cap);
168bool pat_match(uint16_t offset,
const uint8_t *pattern,
const uint8_t *mask, uint8_t patlen,
const uint8_t *data,
173 if ((uint32_t)offset + patlen > len)
175 for (uint8_t i = 0; i < patlen; i++)
176 if ((data[offset + i] & mask[i]) != pattern[i])
182bool acl_match(
const acl_entry *a, uint8_t src,
const uint8_t *data, uint16_t len)
184 if (a->src != DET_FWD_IF_ANY && a->src != src)
186 return pat_match(a->offset, a->pattern, a->mask, a->patlen, data, len);
190bool acl_permits(
const ForwardCtx &f, uint8_t src,
const uint8_t *data, uint16_t len)
193 if (f.acl[i].used && acl_match(&f.acl[i], src, data, len))
194 return f.acl[i].action == det_fwd_action::DET_FWD_ALLOW;
195 return f.acl_default == det_fwd_action::DET_FWD_ALLOW;
199void det_forward_reset(
void)
201 memset(s_fwd.if_, 0,
sizeof(s_fwd.if_));
202 memset(s_fwd.rules, 0,
sizeof(s_fwd.rules));
203 memset(s_fwd.acl, 0,
sizeof(s_fwd.acl));
204 memset(s_fwd.routes, 0,
sizeof(s_fwd.routes));
205 s_fwd.acl_default = det_fwd_action::DET_FWD_ALLOW;
207 s_fwd.inspector =
nullptr;
208 s_fwd.inspect_ctx =
nullptr;
210 memset(&s_fwd.stats, 0,
sizeof(s_fwd.stats));
213void det_forward_acl_set_default(det_fwd_action action)
215 s_fwd.acl_default = action;
218bool det_forward_acl_add(uint8_t src_if, uint16_t offset,
const uint8_t *pattern,
const uint8_t *mask, uint8_t patlen,
219 det_fwd_action action)
225 if (s_fwd.acl[i].used)
227 memset(s_fwd.acl[i].pattern, 0,
sizeof(s_fwd.acl[i].pattern));
228 memset(s_fwd.acl[i].mask, 0,
sizeof(s_fwd.acl[i].mask));
229 for (uint8_t k = 0; k < patlen; k++)
231 s_fwd.acl[i].pattern[k] = (uint8_t)(pattern[k] & mask[k]);
232 s_fwd.acl[i].mask[k] = mask[k];
234 s_fwd.acl[i].offset = offset;
235 s_fwd.acl[i].src = src_if;
236 s_fwd.acl[i].patlen = patlen;
237 s_fwd.acl[i].action = action;
238 s_fwd.acl[i].used =
true;
244bool det_forward_route_add(uint8_t src_if, uint16_t offset,
const uint8_t *pattern,
const uint8_t *mask, uint8_t patlen,
245 uint8_t egress_if, uint16_t rate_cap_per_sec)
251 if (s_fwd.routes[i].used)
253 memset(s_fwd.routes[i].pattern, 0,
sizeof(s_fwd.routes[i].pattern));
254 memset(s_fwd.routes[i].mask, 0,
sizeof(s_fwd.routes[i].mask));
255 for (uint8_t k = 0; k < patlen; k++)
257 s_fwd.routes[i].pattern[k] = (uint8_t)(pattern[k] & mask[k]);
258 s_fwd.routes[i].mask[k] = mask[k];
260 s_fwd.routes[i].window_start = 0;
261 s_fwd.routes[i].offset = offset;
262 s_fwd.routes[i].rate_cap = rate_cap_per_sec;
263 s_fwd.routes[i].count = 0;
264 s_fwd.routes[i].src = src_if;
265 s_fwd.routes[i].patlen = patlen;
266 s_fwd.routes[i].egress = egress_if;
267 s_fwd.routes[i].used =
true;
273bool det_forward_add_if(uint8_t if_id, det_if_kind kind, det_if_send_fn send,
void *ctx)
275 if (!send || find_if(s_fwd, if_id))
279 if (s_fwd.if_[i].used)
281 s_fwd.if_[i].send = send;
282 s_fwd.if_[i].ctx = ctx;
283 s_fwd.if_[i].id = if_id;
284 s_fwd.if_[i].kind = kind;
285 s_fwd.if_[i].used =
true;
291bool det_forward_add_rule(uint8_t src_if, uint8_t dst_if, det_fwd_action action, uint16_t rate_cap_per_sec)
295 if (s_fwd.rules[i].used)
297 s_fwd.rules[i].window_start = 0;
298 s_fwd.rules[i].rate_cap = rate_cap_per_sec;
299 s_fwd.rules[i].count = 0;
300 s_fwd.rules[i].src = src_if;
301 s_fwd.rules[i].dst = dst_if;
302 s_fwd.rules[i].action = action;
303 s_fwd.rules[i].used =
true;
311static uint8_t forward_policy_route(uint8_t src_if,
const uint8_t *data, uint16_t len,
bool *handled)
316 route &rt = s_fwd.routes[i];
317 if (!rt.used || (rt.src != DET_FWD_IF_ANY && rt.src != src_if))
319 if (!pat_match(rt.offset, rt.pattern, rt.mask, rt.patlen, data, len))
321 s_fwd.stats.policy_routed++;
322 if (rt.egress == src_if)
324 const iface *out = find_if(s_fwd, rt.egress);
327 s_fwd.stats.send_fail++;
330 if (rate_gate(rt.window_start, rt.count, rt.rate_cap))
332 s_fwd.stats.rate_dropped++;
335 if (out->send(out->id, data, len, out->ctx))
337 s_fwd.stats.forwarded++;
340 s_fwd.stats.send_fail++;
347uint8_t det_forward_ingress(uint8_t src_if,
const uint8_t *data, uint16_t len)
349 s_fwd.stats.frames_in++;
350 if (!acl_permits(s_fwd, src_if, data, len))
352 s_fwd.stats.acl_denied++;
357 if (s_fwd.inspector &&
358 s_fwd.inspector(src_if, data, len, s_fwd.inspect_ctx) == det_fwd_verdict::DET_FWD_INSPECT_DROP)
360 s_fwd.stats.inspect_dropped++;
367 uint8_t verdict = forward_policy_route(src_if, data, len, &routed);
373 if (!s_fwd.if_[i].used || s_fwd.if_[i].id == src_if)
376 resolve_result r = resolve(s_fwd, src_if, s_fwd.if_[i].id, &idx);
377 if (r == resolve_result::R_NOROUTE)
379 if (r == resolve_result::R_DENY)
381 s_fwd.stats.blocked++;
384 if (rate_exceeded(&s_fwd.rules[idx]))
386 s_fwd.stats.rate_dropped++;
389 if (s_fwd.if_[i].send(s_fwd.if_[i].id, data, len, s_fwd.if_[i].ctx))
391 s_fwd.stats.forwarded++;
396 s_fwd.stats.send_fail++;
402void det_forward_get_stats(det_forward_stats *out)
409void det_forward_set_inspector(det_fwd_inspect_fn fn,
void *ctx)
411 s_fwd.inspector = fn;
412 s_fwd.inspect_ctx = ctx;
417void det_forward_test_set_now(uint32_t ms)
#define DETWS_FWD_MAX_ACL
Max ingress access-control entries (byte-pattern permit/deny; static).
#define DETWS_FWD_MAX_RULES
Max forwarding rules (src -> dst allow/deny + rate cap; static-allocated).
#define DETWS_FWD_MAX_ROUTES
Max policy routes (byte-pattern -> egress interface; static). Policy routes take precedence over the ...
#define DETWS_FWD_MAX_IFACES
Max interfaces the forwarding plane tracks (static-allocated).
#define DETWS_FWD_ACL_PATLEN
Bytes an ACL entry can match (its pattern / mask length).
Pluggable monotonic clock for all library timing.
uint32_t detws_millis(void)
The library's monotonic time at 1000 Hz (milliseconds).
Interface forwarding plane (DETWS_ENABLE_FORWARD) - the v5 bridge / router.