#ifdef _KERNEL
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: npf_ruleset.c,v 1.57 2025/10/09 15:30:18 joe Exp $");
#include <sys/param.h>
#include <sys/types.h>
#include <sys/atomic.h>
#include <sys/kmem.h>
#include <sys/queue.h>
#include <sys/mbuf.h>
#include <sys/types.h>
#include <sys/kauth.h>
#include <net/bpf.h>
#include <net/bpfjit.h>
#include <net/pfil.h>
#include <net/if.h>
#endif
#include "npf_impl.h"
struct npf_ruleset {
LIST_HEAD(, npf_rule) rs_all;
LIST_HEAD(, npf_rule) rs_dynamic;
LIST_HEAD(, npf_rule) rs_gc;
uint64_t rs_idcnt;
unsigned rs_slots;
unsigned rs_nitems;
npf_rule_t * rs_rules[];
};
struct npf_rule {
uint32_t r_attr;
unsigned r_ifid;
unsigned r_skip_to;
int r_type;
bpfjit_func_t r_jcode;
void * r_code;
unsigned r_clen;
npf_natpolicy_t * r_natp;
npf_rproc_t * r_rproc;
union {
struct {
npf_rule_t * r_subset;
LIST_ENTRY(npf_rule) r_dentry;
};
struct {
int r_priority;
npf_rule_t * r_parent;
npf_rule_t * r_next;
};
};
uint64_t r_id;
char r_name[NPF_RULE_MAXNAMELEN];
uint8_t r_key[NPF_RULE_MAXKEYLEN];
LIST_ENTRY(npf_rule) r_aentry;
nvlist_t * r_info;
size_t r_info_len;
rid_t uid;
rid_t gid;
};
#define SKIPTO_ADJ_FLAG (1U << 31)
#define SKIPTO_MASK (SKIPTO_ADJ_FLAG - 1)
static nvlist_t * npf_rule_export(npf_t *, const npf_rule_t *);
#define NPF_RULE_KEEPNAT (0x01000000 & NPF_RULE_PRIVMASK)
#define NPF_DYNAMIC_GROUP_P(attr) \
(((attr) & NPF_DYNAMIC_GROUP) == NPF_DYNAMIC_GROUP)
#define NPF_DYNAMIC_RULE_P(attr) \
(((attr) & NPF_DYNAMIC_GROUP) == NPF_RULE_DYNAMIC)
npf_ruleset_t *
npf_ruleset_create(size_t slots)
{
size_t len = offsetof(npf_ruleset_t, rs_rules[slots]);
npf_ruleset_t *rlset;
rlset = kmem_zalloc(len, KM_SLEEP);
LIST_INIT(&rlset->rs_dynamic);
LIST_INIT(&rlset->rs_all);
LIST_INIT(&rlset->rs_gc);
rlset->rs_slots = slots;
return rlset;
}
void
npf_ruleset_destroy(npf_ruleset_t *rlset)
{
size_t len = offsetof(npf_ruleset_t, rs_rules[rlset->rs_slots]);
npf_rule_t *rl;
while ((rl = LIST_FIRST(&rlset->rs_all)) != NULL) {
if (NPF_DYNAMIC_GROUP_P(rl->r_attr)) {
rl->r_subset = NULL;
LIST_REMOVE(rl, r_dentry);
}
if (NPF_DYNAMIC_RULE_P(rl->r_attr)) {
KASSERT(rl->r_parent != NULL);
}
LIST_REMOVE(rl, r_aentry);
npf_rule_free(rl);
}
KASSERT(LIST_EMPTY(&rlset->rs_dynamic));
npf_ruleset_gc(rlset);
KASSERT(LIST_EMPTY(&rlset->rs_gc));
kmem_free(rlset, len);
}
void
npf_ruleset_insert(npf_ruleset_t *rlset, npf_rule_t *rl)
{
unsigned n = rlset->rs_nitems;
KASSERT(n < rlset->rs_slots);
LIST_INSERT_HEAD(&rlset->rs_all, rl, r_aentry);
if (NPF_DYNAMIC_GROUP_P(rl->r_attr)) {
LIST_INSERT_HEAD(&rlset->rs_dynamic, rl, r_dentry);
} else {
KASSERTMSG(rl->r_parent == NULL, "cannot be dynamic rule");
rl->r_attr &= ~NPF_RULE_DYNAMIC;
}
rlset->rs_rules[n] = rl;
rlset->rs_nitems++;
rl->r_id = ++rlset->rs_idcnt;
if (rl->r_skip_to < ++n) {
rl->r_skip_to = SKIPTO_ADJ_FLAG | n;
}
}
npf_rule_t *
npf_ruleset_lookup(npf_ruleset_t *rlset, const char *name)
{
npf_rule_t *rl;
LIST_FOREACH(rl, &rlset->rs_dynamic, r_dentry) {
KASSERT(NPF_DYNAMIC_GROUP_P(rl->r_attr));
if (strncmp(rl->r_name, name, NPF_RULE_MAXNAMELEN) == 0)
break;
}
return rl;
}
int
npf_ruleset_add(npf_ruleset_t *rlset, const char *rname, npf_rule_t *rl)
{
npf_rule_t *rg, *it, *target;
int priocmd;
if (!NPF_DYNAMIC_RULE_P(rl->r_attr)) {
return EINVAL;
}
rg = npf_ruleset_lookup(rlset, rname);
if (rg == NULL) {
return ESRCH;
}
rl->r_id = ++rlset->rs_idcnt;
rl->r_parent = rg;
if ((priocmd = rl->r_priority) < 0) {
rl->r_priority = 0;
}
switch (priocmd) {
case NPF_PRI_LAST:
default:
target = NULL;
it = rg->r_subset;
while (it && it->r_priority <= rl->r_priority) {
target = it;
it = it->r_next;
}
if (target) {
atomic_store_relaxed(&rl->r_next, target->r_next);
membar_producer();
atomic_store_relaxed(&target->r_next, rl);
break;
}
case NPF_PRI_FIRST:
atomic_store_relaxed(&rl->r_next, rg->r_subset);
membar_producer();
atomic_store_relaxed(&rg->r_subset, rl);
break;
}
LIST_INSERT_HEAD(&rlset->rs_all, rl, r_aentry);
return 0;
}
static void
npf_ruleset_unlink(npf_rule_t *rl, npf_rule_t *prev)
{
KASSERT(NPF_DYNAMIC_RULE_P(rl->r_attr));
if (prev) {
prev->r_next = rl->r_next;
} else {
npf_rule_t *rg = rl->r_parent;
rg->r_subset = rl->r_next;
}
LIST_REMOVE(rl, r_aentry);
}
int
npf_ruleset_remove(npf_ruleset_t *rlset, const char *rname, uint64_t id)
{
npf_rule_t *rg, *prev = NULL;
if ((rg = npf_ruleset_lookup(rlset, rname)) == NULL) {
return ESRCH;
}
for (npf_rule_t *rl = rg->r_subset; rl; rl = rl->r_next) {
KASSERT(rl->r_parent == rg);
KASSERT(NPF_DYNAMIC_RULE_P(rl->r_attr));
if (rl->r_id == id) {
npf_ruleset_unlink(rl, prev);
LIST_INSERT_HEAD(&rlset->rs_gc, rl, r_aentry);
return 0;
}
prev = rl;
}
return ENOENT;
}
int
npf_ruleset_remkey(npf_ruleset_t *rlset, const char *rname,
const void *key, size_t len)
{
npf_rule_t *rg, *rlast = NULL, *prev = NULL, *lastprev = NULL;
KASSERT(len && len <= NPF_RULE_MAXKEYLEN);
if ((rg = npf_ruleset_lookup(rlset, rname)) == NULL) {
return ESRCH;
}
for (npf_rule_t *rl = rg->r_subset; rl; rl = rl->r_next) {
KASSERT(rl->r_parent == rg);
KASSERT(NPF_DYNAMIC_RULE_P(rl->r_attr));
if (memcmp(rl->r_key, key, len) == 0) {
lastprev = prev;
rlast = rl;
}
prev = rl;
}
if (!rlast) {
return ENOENT;
}
npf_ruleset_unlink(rlast, lastprev);
LIST_INSERT_HEAD(&rlset->rs_gc, rlast, r_aentry);
return 0;
}
int
npf_ruleset_list(npf_t *npf, npf_ruleset_t *rlset, const char *rname,
nvlist_t *rlset_nvl)
{
const npf_rule_t *rg;
KASSERT(npf_config_locked_p(npf));
if ((rg = npf_ruleset_lookup(rlset, rname)) == NULL) {
return ESRCH;
}
for (const npf_rule_t *rl = rg->r_subset; rl; rl = rl->r_next) {
nvlist_t *rule;
KASSERT(rl->r_parent == rg);
KASSERT(NPF_DYNAMIC_RULE_P(rl->r_attr));
if ((rule = npf_rule_export(npf, rl)) == NULL) {
return ENOMEM;
}
nvlist_append_nvlist_array(rlset_nvl, "rules", rule);
nvlist_destroy(rule);
}
return 0;
}
int
npf_ruleset_flush(npf_ruleset_t *rlset, const char *rname)
{
npf_rule_t *rg, *rl;
if ((rg = npf_ruleset_lookup(rlset, rname)) == NULL) {
return ESRCH;
}
rl = atomic_swap_ptr(&rg->r_subset, NULL);
membar_producer();
while (rl) {
KASSERT(NPF_DYNAMIC_RULE_P(rl->r_attr));
KASSERT(rl->r_parent == rg);
LIST_REMOVE(rl, r_aentry);
LIST_INSERT_HEAD(&rlset->rs_gc, rl, r_aentry);
rl = rl->r_next;
}
rlset->rs_idcnt = 0;
return 0;
}
void
npf_ruleset_gc(npf_ruleset_t *rlset)
{
npf_rule_t *rl;
while ((rl = LIST_FIRST(&rlset->rs_gc)) != NULL) {
LIST_REMOVE(rl, r_aentry);
npf_rule_free(rl);
}
}
int
npf_ruleset_export(npf_t *npf, const npf_ruleset_t *rlset,
const char *key, nvlist_t *npf_nv)
{
const unsigned nitems = rlset->rs_nitems;
unsigned n = 0;
int error = 0;
KASSERT(npf_config_locked_p(npf));
while (n < nitems) {
const npf_rule_t *rl = rlset->rs_rules[n];
const npf_natpolicy_t *natp = rl->r_natp;
nvlist_t *rule;
rule = npf_rule_export(npf, rl);
if (!rule) {
error = ENOMEM;
break;
}
if (natp && (error = npf_natpolicy_export(natp, rule)) != 0) {
nvlist_destroy(rule);
break;
}
nvlist_append_nvlist_array(npf_nv, key, rule);
nvlist_destroy(rule);
n++;
}
return error;
}
void
npf_ruleset_reload(npf_t *npf, npf_ruleset_t *newset,
npf_ruleset_t *oldset, bool load)
{
npf_rule_t *rg, *rl;
uint64_t nid = 0;
KASSERT(npf_config_locked_p(npf));
LIST_FOREACH(rg, &newset->rs_dynamic, r_dentry) {
npf_rule_t *active_rgroup;
active_rgroup = npf_ruleset_lookup(oldset, rg->r_name);
if (active_rgroup == NULL) {
continue;
}
rg->r_subset = active_rgroup->r_subset;
for (rl = rg->r_subset; rl; rl = rl->r_next) {
KASSERT(NPF_DYNAMIC_RULE_P(rl->r_attr));
LIST_REMOVE(rl, r_aentry);
LIST_INSERT_HEAD(&newset->rs_all, rl, r_aentry);
KASSERT(rl->r_parent == active_rgroup);
rl->r_parent = rg;
}
}
if (load)
return;
LIST_FOREACH(rl, &newset->rs_all, r_aentry) {
npf_natpolicy_t *np;
npf_rule_t *actrl;
if ((np = rl->r_natp) == NULL) {
continue;
}
LIST_FOREACH(actrl, &oldset->rs_all, r_aentry) {
if (!actrl->r_natp)
continue;
if ((actrl->r_attr & NPF_RULE_KEEPNAT) != 0)
continue;
if (npf_natpolicy_cmp(actrl->r_natp, np))
break;
}
if (!actrl) {
npf_nat_setid(np, ++nid);
continue;
}
rl->r_natp = actrl->r_natp;
npf_nat_setid(rl->r_natp, ++nid);
actrl->r_attr |= NPF_RULE_KEEPNAT;
npf_natpolicy_destroy(np);
}
newset->rs_idcnt = oldset->rs_idcnt;
}
npf_natpolicy_t *
npf_ruleset_findnat(npf_ruleset_t *rlset, uint64_t id)
{
npf_rule_t *rl;
LIST_FOREACH(rl, &rlset->rs_all, r_aentry) {
npf_natpolicy_t *np = rl->r_natp;
if (np && npf_nat_getid(np) == id) {
return np;
}
}
return NULL;
}
void
npf_ruleset_freealg(npf_ruleset_t *rlset, npf_alg_t *alg)
{
npf_rule_t *rl;
npf_natpolicy_t *np;
LIST_FOREACH(rl, &rlset->rs_all, r_aentry) {
if ((np = rl->r_natp) != NULL) {
npf_nat_freealg(np, alg);
}
}
}
npf_rule_t *
npf_rule_alloc(npf_t *npf, const nvlist_t *rule)
{
npf_rule_t *rl;
const char *rname;
const void *key, *info;
size_t len;
rl = kmem_zalloc(sizeof(npf_rule_t), KM_SLEEP);
info = dnvlist_get_binary(rule, "info", &rl->r_info_len, NULL, 0);
if (info) {
rl->r_info = kmem_alloc(rl->r_info_len, KM_SLEEP);
memcpy(rl->r_info, info, rl->r_info_len);
}
rl->r_natp = NULL;
if ((rname = dnvlist_get_string(rule, "name", NULL)) != NULL) {
strlcpy(rl->r_name, rname, NPF_RULE_MAXNAMELEN);
} else {
rl->r_name[0] = '\0';
}
rl->r_attr = dnvlist_get_number(rule, "attr", 0);
rl->r_attr &= ~NPF_RULE_PRIVMASK;
if (NPF_DYNAMIC_RULE_P(rl->r_attr)) {
rl->r_priority = (int)dnvlist_get_number(rule, "prio", 0);
} else {
rl->r_skip_to = dnvlist_get_number(rule, "skip-to", 0);
}
if ((rname = dnvlist_get_string(rule, "ifname", NULL)) != NULL) {
if ((rl->r_ifid = npf_ifmap_register(npf, rname)) == 0) {
kmem_free(rl, sizeof(npf_rule_t));
return NULL;
}
} else {
rl->r_ifid = 0;
}
if ((key = dnvlist_get_binary(rule, "key", &len, NULL, 0)) != NULL) {
if (len > NPF_RULE_MAXKEYLEN) {
kmem_free(rl, sizeof(npf_rule_t));
return NULL;
}
memcpy(rl->r_key, key, len);
}
rl->gid.op = rl->uid.op = NPF_OP_NONE;
return rl;
}
static void
npf_rid_export(nvlist_t *rl, struct r_id rid, const char *name)
{
uint64_t uid_element[3] = { rid.id[0], rid.id[1], rid.op };
nvlist_add_number_array(rl, name, uid_element, 3);
}
static nvlist_t *
npf_rule_export(npf_t *npf, const npf_rule_t *rl)
{
nvlist_t *rule = nvlist_create(0);
unsigned skip_to = 0;
npf_rproc_t *rp;
nvlist_add_number(rule, "attr", rl->r_attr);
nvlist_add_number(rule, "prio", rl->r_priority);
if ((rl->r_skip_to & SKIPTO_ADJ_FLAG) == 0) {
skip_to = rl->r_skip_to & SKIPTO_MASK;
}
nvlist_add_number(rule, "skip-to", skip_to);
nvlist_add_number(rule, "code-type", rl->r_type);
if (rl->r_code) {
nvlist_add_binary(rule, "code", rl->r_code, rl->r_clen);
}
if (rl->r_ifid) {
char ifname[IFNAMSIZ];
npf_ifmap_copyname(npf, rl->r_ifid, ifname, sizeof(ifname));
nvlist_add_string(rule, "ifname", ifname);
}
nvlist_add_number(rule, "id", rl->r_id);
if (rl->r_name[0]) {
nvlist_add_string(rule, "name", rl->r_name);
}
if (NPF_DYNAMIC_RULE_P(rl->r_attr)) {
nvlist_add_binary(rule, "key", rl->r_key, NPF_RULE_MAXKEYLEN);
}
if (rl->r_info) {
nvlist_add_binary(rule, "info", rl->r_info, rl->r_info_len);
}
if (rl->uid.op != NPF_OP_NONE) {
npf_rid_export(rule, rl->uid, "r_user");
}
if (rl->gid.op != NPF_OP_NONE) {
npf_rid_export(rule, rl->gid, "r_group");
}
if ((rp = npf_rule_getrproc(rl)) != NULL) {
const char *rname = npf_rproc_getname(rp);
nvlist_add_string(rule, "rproc", rname);
npf_rproc_release(rp);
}
return rule;
}
void
npf_rule_setcode(npf_rule_t *rl, const int type, void *code, size_t size)
{
KASSERT(type == NPF_CODE_BPF);
rl->r_type = type;
rl->r_code = code;
rl->r_clen = size;
rl->r_jcode = npf_bpf_compile(code, size);
}
void
npf_rule_setrid(const nvlist_t *req, npf_rule_t *rl, const char *name)
{
size_t nitems;
rid_t id;
const uint64_t *rid = nvlist_get_number_array(req, name, &nitems);
KASSERT(nitems == 3);
id.id[0] = (uint32_t)rid[0];
id.id[1] = (uint32_t)rid[1];
id.op = (uint8_t)rid[2];
if (!strcmp(name, "r_user"))
rl->uid = id;
else if (!strcmp(name, "r_group"))
rl->gid = id;
}
void
npf_rule_setrproc(npf_rule_t *rl, npf_rproc_t *rp)
{
npf_rproc_acquire(rp);
rl->r_rproc = rp;
}
void
npf_rule_free(npf_rule_t *rl)
{
npf_natpolicy_t *np = rl->r_natp;
npf_rproc_t *rp = rl->r_rproc;
if (np && (rl->r_attr & NPF_RULE_KEEPNAT) == 0) {
npf_natpolicy_destroy(np);
}
if (rp) {
npf_rproc_release(rp);
}
if (rl->r_code) {
kmem_free(rl->r_code, rl->r_clen);
}
if (rl->r_jcode) {
bpf_jit_freecode(rl->r_jcode);
}
if (rl->r_info) {
kmem_free(rl->r_info, rl->r_info_len);
}
kmem_free(rl, sizeof(npf_rule_t));
}
uint64_t
npf_rule_getid(const npf_rule_t *rl)
{
KASSERT(NPF_DYNAMIC_RULE_P(rl->r_attr));
return rl->r_id;
}
npf_rproc_t *
npf_rule_getrproc(const npf_rule_t *rl)
{
npf_rproc_t *rp = rl->r_rproc;
if (rp) {
npf_rproc_acquire(rp);
}
return rp;
}
npf_natpolicy_t *
npf_rule_getnat(const npf_rule_t *rl)
{
return rl->r_natp;
}
void
npf_rule_setnat(npf_rule_t *rl, npf_natpolicy_t *np)
{
KASSERT(rl->r_natp == NULL);
rl->r_natp = np;
}
static inline bool
npf_rule_inspect(const npf_rule_t *rl, bpf_args_t *bc_args,
const int di_mask, const unsigned ifid)
{
if (rl->r_ifid && rl->r_ifid != ifid) {
return false;
}
if ((rl->r_attr & NPF_RULE_DIMASK) != NPF_RULE_DIMASK) {
if ((rl->r_attr & di_mask) == 0)
return false;
}
if (!rl->r_code) {
KASSERT(rl->r_jcode == NULL);
return true;
}
KASSERT(rl->r_type == NPF_CODE_BPF);
return npf_bpf_filter(bc_args, rl->r_code, rl->r_jcode) != 0;
}
static inline npf_rule_t *
npf_rule_reinspect(const npf_rule_t *rg, bpf_args_t *bc_args,
const int di_mask, const unsigned ifid)
{
npf_rule_t *final_rl = NULL, *rl;
KASSERT(NPF_DYNAMIC_GROUP_P(rg->r_attr));
rl = atomic_load_relaxed(&rg->r_subset);
for (; rl; rl = atomic_load_relaxed(&rl->r_next)) {
KASSERT(!final_rl || rl->r_priority >= final_rl->r_priority);
if (!npf_rule_inspect(rl, bc_args, di_mask, ifid)) {
continue;
}
if (rl->r_attr & NPF_RULE_FINAL) {
return rl;
}
final_rl = rl;
}
return final_rl;
}
npf_rule_t *
npf_ruleset_inspect(npf_cache_t *npc, const npf_ruleset_t *rlset,
const int di, const int layer)
{
nbuf_t *nbuf = npc->npc_nbuf;
const int di_mask = (di & PFIL_IN) ? NPF_RULE_IN : NPF_RULE_OUT;
const unsigned nitems = rlset->rs_nitems;
const unsigned ifid = nbuf->nb_ifid;
npf_rule_t *final_rl = NULL;
bpf_args_t bc_args;
unsigned n = 0;
KASSERT(((di & PFIL_IN) != 0) ^ ((di & PFIL_OUT) != 0));
uint32_t bc_words[NPF_BPF_NWORDS];
nbuf_reset(nbuf);
npf_bpf_prepare(npc, &bc_args, bc_words);
while (n < nitems) {
npf_rule_t *rl = rlset->rs_rules[n];
const unsigned skip_to = rl->r_skip_to & SKIPTO_MASK;
const uint32_t attr = rl->r_attr;
if (!(((layer == NPF_RULE_LAYER_3 && ((attr & (NPF_RULE_LAYER_2 | NPF_RULE_LAYER_3)) == 0)) ||
(attr & layer)))) {
n = skip_to;
continue;
}
KASSERT(!nbuf_flag_p(nbuf, NBUF_DATAREF_RESET));
KASSERT(n < skip_to);
if ((attr & NPF_DYNAMIC_GROUP) == NPF_RULE_GROUP && final_rl) {
break;
}
if (!npf_rule_inspect(rl, &bc_args, di_mask, ifid)) {
n = skip_to;
continue;
}
if (NPF_DYNAMIC_GROUP_P(attr)) {
rl = npf_rule_reinspect(rl, &bc_args, di_mask, ifid);
if (rl != NULL) {
final_rl = rl;
break;
}
} else if ((attr & NPF_RULE_GROUP) == 0) {
final_rl = rl;
}
if (attr & NPF_RULE_FINAL) {
break;
}
n++;
}
KASSERT(!nbuf_flag_p(nbuf, NBUF_DATAREF_RESET));
return final_rl;
}
int
npf_rule_reverse(npf_cache_t *npc, npf_match_info_t *mi, int ret)
{
KASSERT(npf_iscached(npc, NPC_LAYER4));
switch(npc->npc_proto) {
case IPPROTO_TCP:
if (ret == 0) {
mi->mi_retfl &= !(NPF_RULE_PASS | NPF_RULE_STATEFUL |
NPF_RULE_GSTATEFUL);
mi->mi_retfl |= NPF_RULE_RETRST;
}
else {
mi->mi_retfl &= !(NPF_RULE_RETRST);
mi->mi_retfl |= (NPF_RULE_PASS | NPF_RULE_STATEFUL |
NPF_RULE_GSTATEFUL);
}
break;
case IPPROTO_UDP:
if (ret == 0) {
mi->mi_retfl &= !(NPF_RULE_PASS);
mi->mi_retfl |= NPF_RULE_RETICMP;
}
else {
mi->mi_retfl &= !(NPF_RULE_RETICMP);
mi->mi_retfl |= NPF_RULE_PASS;
}
break;
}
return (ret == 0) ? ENETUNREACH : 0;
}
int
npf_rule_match_rid(npf_rule_t *rl, npf_cache_t *npc, int dir)
{
uint32_t sock_gid, sock_uid;
bool uid_matched = false, gid_matched = false;
if (rl->gid.op == NPF_OP_NONE && rl->uid.op == NPF_OP_NONE)
return -1;
KASSERT(npf_iscached(npc, NPC_IP46));
KASSERT(npf_iscached(npc, NPC_LAYER4));
if (rl->gid.op != NPF_OP_NONE) {
if (npf_socket_lookup_rid(npc, kauth_cred_getegid, &sock_gid, dir) == -1)
return ENOTCONN;
gid_matched |= npf_match_rid(&rl->gid, sock_gid);
}
if (rl->uid.op != NPF_OP_NONE) {
if (npf_socket_lookup_rid(npc, kauth_cred_geteuid, &sock_uid, dir) == -1)
return ENOTCONN;
uid_matched |= npf_match_rid(&rl->uid, sock_uid);
}
if (rl->gid.op && rl->uid.op)
return gid_matched && uid_matched;
else
return gid_matched || uid_matched;
}
int
npf_rule_conclude(const npf_rule_t *rl, npf_match_info_t *mi)
{
mi->mi_retfl = rl->r_attr;
mi->mi_rid = rl->r_id;
return (rl->r_attr & NPF_RULE_PASS) ? 0 : ENETUNREACH;
}
#if defined(DDB) || defined(_NPF_TESTING)
void
npf_ruleset_dump(npf_t *npf, const char *name)
{
npf_ruleset_t *rlset = npf_config_ruleset(npf);
npf_rule_t *rg, *rl;
LIST_FOREACH(rg, &rlset->rs_dynamic, r_dentry) {
printf("ruleset '%s':\n", rg->r_name);
for (rl = rg->r_subset; rl; rl = rl->r_next) {
printf("\tid %"PRIu64", key: ", rl->r_id);
for (unsigned i = 0; i < NPF_RULE_MAXKEYLEN; i++)
printf("%x", rl->r_key[i]);
printf("\n");
}
}
}
#endif