37 uint32_t window_start;
60 uint32_t window_start;
80 pc_fwd_action acl_default = pc_fwd_action::PC_FWD_ALLOW;
82 pc_fwd_inspect_fn inspector =
nullptr;
83 void *inspect_ctx =
nullptr;
85 pc_forward_stats stats;
104const iface *find_if(
const ForwardCtx &f, uint8_t
id)
108 if (f.if_[i].used && f.if_[i].id ==
id)
118enum class resolve_result : uint8_t
124resolve_result resolve(
const ForwardCtx &f, uint8_t src, uint8_t dst,
int *allow_idx)
130 if (!f.rules[i].used || f.rules[i].src != src || f.rules[i].dst != dst)
134 if (f.rules[i].action == pc_fwd_action::PC_FWD_DENY)
145 return resolve_result::R_DENY;
150 return resolve_result::R_ALLOW;
152 return resolve_result::R_NOROUTE;
157bool rate_gate(uint32_t &window_start, uint16_t &count, uint16_t rate_cap)
163 uint32_t now = fwd_now();
164 if ((now - window_start) >= 1000)
169 if (count >= rate_cap)
177bool rate_exceeded(rule *r)
179 return rate_gate(r->window_start, r->count, r->rate_cap);
184bool pat_match(uint16_t offset,
const uint8_t *pattern,
const uint8_t *mask, uint8_t patlen,
const uint8_t *data,
191 if ((uint32_t)offset + patlen > len)
195 for (uint8_t i = 0; i < patlen; i++)
197 if ((data[offset + i] & mask[i]) != pattern[i])
206bool acl_match(
const acl_entry *a, uint8_t src,
const uint8_t *data, uint16_t len)
208 if (a->src != PC_FWD_IF_ANY && a->src != src)
212 return pat_match(a->offset, a->pattern, a->mask, a->patlen, data, len);
216bool acl_permits(
const ForwardCtx &f, uint8_t src,
const uint8_t *data, uint16_t len)
220 if (f.acl[i].used && acl_match(&f.acl[i], src, data, len))
222 return f.acl[i].action == pc_fwd_action::PC_FWD_ALLOW;
225 return f.acl_default == pc_fwd_action::PC_FWD_ALLOW;
229void pc_forward_reset(
void)
231 memset(s_fwd.if_, 0,
sizeof(s_fwd.if_));
232 memset(s_fwd.rules, 0,
sizeof(s_fwd.rules));
233 memset(s_fwd.acl, 0,
sizeof(s_fwd.acl));
234 memset(s_fwd.routes, 0,
sizeof(s_fwd.routes));
235 s_fwd.acl_default = pc_fwd_action::PC_FWD_ALLOW;
237 s_fwd.inspector =
nullptr;
238 s_fwd.inspect_ctx =
nullptr;
240 memset(&s_fwd.stats, 0,
sizeof(s_fwd.stats));
243void pc_forward_acl_set_default(pc_fwd_action action)
245 s_fwd.acl_default = action;
248bool pc_forward_acl_add(uint8_t src_if, uint16_t offset,
const uint8_t *pattern,
const uint8_t *mask, uint8_t patlen,
249 pc_fwd_action action)
257 if (s_fwd.acl[i].used)
261 memset(s_fwd.acl[i].pattern, 0,
sizeof(s_fwd.acl[i].pattern));
262 memset(s_fwd.acl[i].mask, 0,
sizeof(s_fwd.acl[i].mask));
263 for (uint8_t k = 0; k < patlen; k++)
265 s_fwd.acl[i].pattern[k] = (uint8_t)(pattern[k] & mask[k]);
266 s_fwd.acl[i].mask[k] =
mask[k];
268 s_fwd.acl[i].offset = offset;
269 s_fwd.acl[i].src = src_if;
270 s_fwd.acl[i].patlen = patlen;
271 s_fwd.acl[i].action = action;
272 s_fwd.acl[i].used =
true;
278bool pc_forward_route_add(uint8_t src_if, uint16_t offset,
const uint8_t *pattern,
const uint8_t *mask, uint8_t patlen,
279 uint8_t egress_if, uint16_t rate_cap_per_sec)
287 if (s_fwd.routes[i].used)
291 memset(s_fwd.routes[i].pattern, 0,
sizeof(s_fwd.routes[i].pattern));
292 memset(s_fwd.routes[i].mask, 0,
sizeof(s_fwd.routes[i].mask));
293 for (uint8_t k = 0; k < patlen; k++)
295 s_fwd.routes[i].pattern[k] = (uint8_t)(pattern[k] & mask[k]);
296 s_fwd.routes[i].mask[k] =
mask[k];
298 s_fwd.routes[i].window_start = 0;
299 s_fwd.routes[i].offset = offset;
300 s_fwd.routes[i].rate_cap = rate_cap_per_sec;
301 s_fwd.routes[i].count = 0;
302 s_fwd.routes[i].src = src_if;
303 s_fwd.routes[i].patlen = patlen;
304 s_fwd.routes[i].egress = egress_if;
305 s_fwd.routes[i].used =
true;
311bool pc_forward_add_if(uint8_t if_id, pc_if_kind kind, pc_if_send_fn send,
void *ctx)
313 if (!send || find_if(s_fwd, if_id))
319 if (s_fwd.if_[i].used)
323 s_fwd.if_[i].send = send;
324 s_fwd.if_[i].ctx = ctx;
325 s_fwd.if_[i].id = if_id;
326 s_fwd.if_[i].kind = kind;
327 s_fwd.if_[i].used =
true;
333bool pc_forward_add_rule(uint8_t src_if, uint8_t dst_if, pc_fwd_action action, uint16_t rate_cap_per_sec)
337 if (s_fwd.rules[i].used)
341 s_fwd.rules[i].window_start = 0;
342 s_fwd.rules[i].rate_cap = rate_cap_per_sec;
343 s_fwd.rules[i].count = 0;
344 s_fwd.rules[i].src = src_if;
345 s_fwd.rules[i].dst = dst_if;
346 s_fwd.rules[i].action = action;
347 s_fwd.rules[i].used =
true;
355static uint8_t forward_policy_route(uint8_t src_if,
const uint8_t *data, uint16_t len,
bool *handled)
360 route &rt = s_fwd.routes[i];
361 if (!rt.used || (rt.src != PC_FWD_IF_ANY && rt.src != src_if))
365 if (!pat_match(rt.offset, rt.pattern, rt.mask, rt.patlen, data, len))
369 s_fwd.stats.policy_routed++;
370 if (rt.egress == src_if)
374 const iface *out = find_if(s_fwd, rt.egress);
377 s_fwd.stats.send_fail++;
380 if (rate_gate(rt.window_start, rt.count, rt.rate_cap))
382 s_fwd.stats.rate_dropped++;
385 if (out->send(out->id, data, len, out->ctx))
387 s_fwd.stats.forwarded++;
390 s_fwd.stats.send_fail++;
397uint8_t pc_forward_ingress(uint8_t src_if,
const uint8_t *data, uint16_t len)
399 s_fwd.stats.frames_in++;
400 if (!acl_permits(s_fwd, src_if, data, len))
402 s_fwd.stats.acl_denied++;
407 if (s_fwd.inspector && s_fwd.inspector(src_if, data, len, s_fwd.inspect_ctx) == pc_fwd_verdict::PC_FWD_INSPECT_DROP)
409 s_fwd.stats.inspect_dropped++;
416 uint8_t verdict = forward_policy_route(src_if, data, len, &routed);
424 if (!s_fwd.if_[i].used || s_fwd.if_[i].id == src_if)
429 resolve_result r = resolve(s_fwd, src_if, s_fwd.if_[i].id, &idx);
430 if (r == resolve_result::R_NOROUTE)
434 if (r == resolve_result::R_DENY)
436 s_fwd.stats.blocked++;
439 if (rate_exceeded(&s_fwd.rules[idx]))
441 s_fwd.stats.rate_dropped++;
444 if (s_fwd.if_[i].send(s_fwd.if_[i].id, data, len, s_fwd.if_[i].ctx))
446 s_fwd.stats.forwarded++;
451 s_fwd.stats.send_fail++;
457void pc_forward_get_stats(pc_forward_stats *out)
466void pc_forward_set_inspector(pc_fwd_inspect_fn fn,
void *ctx)
468 s_fwd.inspector = fn;
469 s_fwd.inspect_ctx = ctx;
474void pc_forward_test_set_now(uint32_t ms)
Pluggable monotonic clock for all library timing.
uint32_t pc_millis(void)
The library's monotonic time at 1000 Hz (milliseconds).
Interface forwarding plane (PC_ENABLE_FORWARD) - the v5 bridge / router.
uint32_t mask(uint8_t width)
Mask of width low bits (width 32 handled without a 32-bit shift, which is UB).
#define PC_FWD_MAX_ACL
Max ingress access-control entries (byte-pattern permit/deny; static).
#define PC_FWD_MAX_ROUTES
Max policy routes (byte-pattern -> egress interface; static). Policy routes take precedence over the ...
#define PC_FWD_MAX_RULES
Max forwarding rules (src -> dst allow/deny + rate cap; static-allocated).
#define PC_FWD_MAX_IFACES
Max interfaces the forwarding plane tracks (static-allocated).
#define PC_FWD_ACL_PATLEN
Bytes an ACL entry can match (its pattern / mask length).