pf: convert rule addition to netlink

The nvlist-based version will be removed in FreeBSD 16.

Sponsored by:	Rubicon Communications, LLC ("Netgate")
Differential Revision:	https://reviews.freebsd.org/D42279
This commit is contained in:
Kristof Provost 2023-10-14 15:10:03 +02:00
parent ae2ca32781
commit ffbf25951e
5 changed files with 630 additions and 181 deletions

View file

@ -438,40 +438,6 @@ pf_nvrule_addr_to_rule_addr(const nvlist_t *nvl, struct pf_rule_addr *addr)
addr->port_op = nvlist_get_number(nvl, "port_op");
}
static void
pfctl_nv_add_mape(nvlist_t *nvparent, const char *name,
const struct pf_mape_portset *mape)
{
nvlist_t *nvl = nvlist_create(0);
nvlist_add_number(nvl, "offset", mape->offset);
nvlist_add_number(nvl, "psidlen", mape->psidlen);
nvlist_add_number(nvl, "psid", mape->psid);
nvlist_add_nvlist(nvparent, name, nvl);
nvlist_destroy(nvl);
}
static void
pfctl_nv_add_pool(nvlist_t *nvparent, const char *name,
const struct pfctl_pool *pool)
{
uint64_t ports[2];
nvlist_t *nvl = nvlist_create(0);
nvlist_add_binary(nvl, "key", &pool->key, sizeof(pool->key));
pfctl_nv_add_addr(nvl, "counter", &pool->counter);
nvlist_add_number(nvl, "tblidx", pool->tblidx);
ports[0] = pool->proxy_port[0];
ports[1] = pool->proxy_port[1];
nvlist_add_number_array(nvl, "proxy_port", ports, 2);
nvlist_add_number(nvl, "opts", pool->opts);
pfctl_nv_add_mape(nvl, "mape", &pool->mape);
nvlist_add_nvlist(nvparent, name, nvl);
nvlist_destroy(nvl);
}
static void
pf_nvmape_to_mape(const nvlist_t *nvl, struct pf_mape_portset *mape)
{
@ -500,22 +466,6 @@ pf_nvpool_to_pool(const nvlist_t *nvl, struct pfctl_pool *pool)
pf_nvmape_to_mape(nvlist_get_nvlist(nvl, "mape"), &pool->mape);
}
static void
pfctl_nv_add_uid(nvlist_t *nvparent, const char *name,
const struct pf_rule_uid *uid)
{
uint64_t uids[2];
nvlist_t *nvl = nvlist_create(0);
uids[0] = uid->uid[0];
uids[1] = uid->uid[1];
nvlist_add_number_array(nvl, "uid", uids, 2);
nvlist_add_number(nvl, "op", uid->op);
nvlist_add_nvlist(nvparent, name, nvl);
nvlist_destroy(nvl);
}
static void
pf_nvrule_uid_to_rule_uid(const nvlist_t *nvl, struct pf_rule_uid *uid)
{
@ -523,19 +473,6 @@ pf_nvrule_uid_to_rule_uid(const nvlist_t *nvl, struct pf_rule_uid *uid)
uid->op = nvlist_get_number(nvl, "op");
}
static void
pfctl_nv_add_divert(nvlist_t *nvparent, const char *name,
const struct pfctl_rule *r)
{
nvlist_t *nvl = nvlist_create(0);
pfctl_nv_add_addr(nvl, "addr", &r->divert.addr);
nvlist_add_number(nvl, "port", r->divert.port);
nvlist_add_nvlist(nvparent, name, nvl);
nvlist_destroy(nvl);
}
static void
pf_nvdivert_to_divert(const nvlist_t *nvl, struct pfctl_rule *rule)
{
@ -926,127 +863,235 @@ pfctl_add_eth_rule(int dev, const struct pfctl_eth_rule *r, const char *anchor,
return (error);
}
static void
snl_add_msg_attr_addr_wrap(struct snl_writer *nw, uint32_t type, const struct pf_addr_wrap *addr)
{
int off;
off = snl_add_msg_attr_nested(nw, type);
snl_add_msg_attr_ip6(nw, PF_AT_ADDR, &addr->v.a.addr.v6);
snl_add_msg_attr_ip6(nw, PF_AT_MASK, &addr->v.a.mask.v6);
if (addr->type == PF_ADDR_DYNIFTL)
snl_add_msg_attr_string(nw, PF_AT_IFNAME, addr->v.ifname);
if (addr->type == PF_ADDR_TABLE)
snl_add_msg_attr_string(nw, PF_AT_TABLENAME, addr->v.tblname);
snl_add_msg_attr_u8(nw, PF_AT_TYPE, addr->type);
snl_add_msg_attr_u8(nw, PF_AT_IFLAGS, addr->iflags);
snl_end_attr_nested(nw, off);
}
static void
snl_add_msg_attr_rule_addr(struct snl_writer *nw, uint32_t type, const struct pf_rule_addr *addr)
{
int off;
off = snl_add_msg_attr_nested(nw, type);
snl_add_msg_attr_addr_wrap(nw, PF_RAT_ADDR, &addr->addr);
snl_add_msg_attr_u16(nw, PF_RAT_SRC_PORT, addr->port[0]);
snl_add_msg_attr_u16(nw, PF_RAT_DST_PORT, addr->port[1]);
snl_add_msg_attr_u8(nw, PF_RAT_NEG, addr->neg);
snl_add_msg_attr_u8(nw, PF_RAT_OP, addr->port_op);
snl_end_attr_nested(nw, off);
}
static void
snl_add_msg_attr_rule_labels(struct snl_writer *nw, uint32_t type, const char labels[PF_RULE_MAX_LABEL_COUNT][PF_RULE_LABEL_SIZE])
{
int off, i = 0;
off = snl_add_msg_attr_nested(nw, type);
while (labels[i][0] != 0 &&
i < PF_RULE_MAX_LABEL_COUNT) {
snl_add_msg_attr_string(nw, PF_LT_LABEL, labels[i]);
i++;
}
snl_end_attr_nested(nw, off);
}
static void
snl_add_msg_attr_mape(struct snl_writer *nw, uint32_t type, const struct pf_mape_portset *me)
{
int off;
off = snl_add_msg_attr_nested(nw, type);
snl_add_msg_attr_u8(nw, PF_MET_OFFSET, me->offset);
snl_add_msg_attr_u8(nw, PF_MET_PSID_LEN, me->psidlen);
snl_add_msg_attr_u16(nw, PF_MET_PSID, me->psid);
snl_end_attr_nested(nw, off);
}
static void
snl_add_msg_attr_rpool(struct snl_writer *nw, uint32_t type, const struct pfctl_pool *pool)
{
int off;
off = snl_add_msg_attr_nested(nw, type);
snl_add_msg_attr(nw, PF_PT_KEY, sizeof(pool->key), &pool->key);
snl_add_msg_attr_ip6(nw, PF_PT_COUNTER, &pool->counter.v6);
snl_add_msg_attr_u32(nw, PF_PT_TBLIDX, pool->tblidx);
snl_add_msg_attr_u16(nw, PF_PT_PROXY_SRC_PORT, pool->proxy_port[0]);
snl_add_msg_attr_u16(nw, PF_PT_PROXY_DST_PORT, pool->proxy_port[1]);
snl_add_msg_attr_u8(nw, PF_PT_OPTS, pool->opts);
snl_add_msg_attr_mape(nw, PF_PT_MAPE, &pool->mape);
snl_end_attr_nested(nw, off);
}
static void
snl_add_msg_attr_timeouts(struct snl_writer *nw, uint32_t type, const uint32_t *timeouts)
{
int off;
off = snl_add_msg_attr_nested(nw, type);
for (int i = 0; i < PFTM_MAX; i++)
snl_add_msg_attr_u32(nw, PF_TT_TIMEOUT, timeouts[i]);
snl_end_attr_nested(nw, off);
}
static void
snl_add_msg_attr_uid(struct snl_writer *nw, uint32_t type, const struct pf_rule_uid *uid)
{
int off;
off = snl_add_msg_attr_nested(nw, type);
snl_add_msg_attr_u32(nw, PF_RUT_UID_LOW, uid->uid[0]);
snl_add_msg_attr_u32(nw, PF_RUT_UID_HIGH, uid->uid[1]);
snl_add_msg_attr_u8(nw, PF_RUT_OP, uid->op);
snl_end_attr_nested(nw, off);
}
static void
snl_add_msg_attr_pf_rule(struct snl_writer *nw, uint32_t type, const struct pfctl_rule *r)
{
int off;
off = snl_add_msg_attr_nested(nw, type);
snl_add_msg_attr_rule_addr(nw, PF_RT_SRC, &r->src);
snl_add_msg_attr_rule_addr(nw, PF_RT_DST, &r->dst);
snl_add_msg_attr_rule_labels(nw, PF_RT_LABELS, r->label);
snl_add_msg_attr_u32(nw, PF_RT_RIDENTIFIER, r->ridentifier);
snl_add_msg_attr_string(nw, PF_RT_IFNAME, r->ifname);
snl_add_msg_attr_string(nw, PF_RT_QNAME, r->qname);
snl_add_msg_attr_string(nw, PF_RT_PQNAME, r->pqname);
snl_add_msg_attr_string(nw, PF_RT_TAGNAME, r->tagname);
snl_add_msg_attr_string(nw, PF_RT_MATCH_TAGNAME, r->match_tagname);
snl_add_msg_attr_string(nw, PF_RT_OVERLOAD_TBLNAME, r->overload_tblname);
snl_add_msg_attr_rpool(nw, PF_RT_RPOOL, &r->rpool);
snl_add_msg_attr_u32(nw, PF_RT_OS_FINGERPRINT, r->os_fingerprint);
snl_add_msg_attr_u32(nw, PF_RT_RTABLEID, r->rtableid);
snl_add_msg_attr_timeouts(nw, PF_RT_TIMEOUT, r->timeout);
snl_add_msg_attr_u32(nw, PF_RT_MAX_STATES, r->max_states);
snl_add_msg_attr_u32(nw, PF_RT_MAX_SRC_NODES, r->max_src_nodes);
snl_add_msg_attr_u32(nw, PF_RT_MAX_SRC_STATES, r->max_src_states);
snl_add_msg_attr_u32(nw, PF_RT_MAX_SRC_CONN_RATE_LIMIT, r->max_src_conn_rate.limit);
snl_add_msg_attr_u32(nw, PF_RT_MAX_SRC_CONN_RATE_SECS, r->max_src_conn_rate.seconds);
snl_add_msg_attr_u16(nw, PF_RT_DNPIPE, r->dnpipe);
snl_add_msg_attr_u16(nw, PF_RT_DNRPIPE, r->dnrpipe);
snl_add_msg_attr_u32(nw, PF_RT_DNFLAGS, r->free_flags);
snl_add_msg_attr_u32(nw, PF_RT_NR, r->nr);
snl_add_msg_attr_u32(nw, PF_RT_PROB, r->prob);
snl_add_msg_attr_u32(nw, PF_RT_CUID, r->cuid);
snl_add_msg_attr_u32(nw, PF_RT_CPID, r->cpid);
snl_add_msg_attr_u16(nw, PF_RT_RETURN_ICMP, r->return_icmp);
snl_add_msg_attr_u16(nw, PF_RT_RETURN_ICMP6, r->return_icmp6);
snl_add_msg_attr_u16(nw, PF_RT_MAX_MSS, r->max_mss);
snl_add_msg_attr_u16(nw, PF_RT_SCRUB_FLAGS, r->scrub_flags);
snl_add_msg_attr_uid(nw, PF_RT_UID, &r->uid);
snl_add_msg_attr_uid(nw, PF_RT_GID, (const struct pf_rule_uid *)&r->gid);
snl_add_msg_attr_u32(nw, PF_RT_RULE_FLAG, r->rule_flag);
snl_add_msg_attr_u8(nw, PF_RT_ACTION, r->action);
snl_add_msg_attr_u8(nw, PF_RT_DIRECTION, r->direction);
snl_add_msg_attr_u8(nw, PF_RT_LOG, r->log);
snl_add_msg_attr_u8(nw, PF_RT_LOGIF, r->logif);
snl_add_msg_attr_u8(nw, PF_RT_QUICK, r->quick);
snl_add_msg_attr_u8(nw, PF_RT_IF_NOT, r->ifnot);
snl_add_msg_attr_u8(nw, PF_RT_MATCH_TAG_NOT, r->match_tag_not);
snl_add_msg_attr_u8(nw, PF_RT_NATPASS, r->natpass);
snl_add_msg_attr_u8(nw, PF_RT_KEEP_STATE, r->keep_state);
snl_add_msg_attr_u8(nw, PF_RT_AF, r->af);
snl_add_msg_attr_u8(nw, PF_RT_PROTO, r->proto);
snl_add_msg_attr_u8(nw, PF_RT_TYPE, r->type);
snl_add_msg_attr_u8(nw, PF_RT_CODE, r->code);
snl_add_msg_attr_u8(nw, PF_RT_FLAGS, r->flags);
snl_add_msg_attr_u8(nw, PF_RT_FLAGSET, r->flagset);
snl_add_msg_attr_u8(nw, PF_RT_MIN_TTL, r->min_ttl);
snl_add_msg_attr_u8(nw, PF_RT_ALLOW_OPTS, r->allow_opts);
snl_add_msg_attr_u8(nw, PF_RT_RT, r->rt);
snl_add_msg_attr_u8(nw, PF_RT_RETURN_TTL, r->return_ttl);
snl_add_msg_attr_u8(nw, PF_RT_TOS, r->tos);
snl_add_msg_attr_u8(nw, PF_RT_SET_TOS, r->set_tos);
snl_add_msg_attr_u8(nw, PF_RT_ANCHOR_RELATIVE, r->anchor_relative);
snl_add_msg_attr_u8(nw, PF_RT_ANCHOR_WILDCARD, r->anchor_wildcard);
snl_add_msg_attr_u8(nw, PF_RT_FLUSH, r->flush);
snl_add_msg_attr_u8(nw, PF_RT_PRIO, r->prio);
snl_add_msg_attr_u8(nw, PF_RT_SET_PRIO, r->set_prio[0]);
snl_add_msg_attr_u8(nw, PF_RT_SET_PRIO_REPLY, r->set_prio[1]);
snl_add_msg_attr_ip6(nw, PF_RT_DIVERT_ADDRESS, &r->divert.addr.v6);
snl_add_msg_attr_u16(nw, PF_RT_DIVERT_PORT, r->divert.port);
snl_end_attr_nested(nw, off);
}
int
pfctl_add_rule(int dev, const struct pfctl_rule *r, const char *anchor,
pfctl_add_rule(int dev __unused, const struct pfctl_rule *r, const char *anchor,
const char *anchor_call, uint32_t ticket, uint32_t pool_ticket)
{
struct pfioc_nv nv;
uint64_t timeouts[PFTM_MAX];
uint64_t set_prio[2];
nvlist_t *nvl, *nvlr;
size_t labelcount;
int ret;
struct snl_writer nw;
struct snl_state ss = {};
struct snl_errmsg_data e = {};
struct nlmsghdr *hdr;
uint32_t seq_id;
int family_id;
nvl = nvlist_create(0);
nvlr = nvlist_create(0);
snl_init(&ss, NETLINK_GENERIC);
family_id = snl_get_genl_family(&ss, PFNL_FAMILY_NAME);
nvlist_add_number(nvl, "ticket", ticket);
nvlist_add_number(nvl, "pool_ticket", pool_ticket);
nvlist_add_string(nvl, "anchor", anchor);
nvlist_add_string(nvl, "anchor_call", anchor_call);
snl_init_writer(&ss, &nw);
hdr = snl_create_genl_msg_request(&nw, family_id, PFNL_CMD_ADDRULE);
hdr->nlmsg_flags |= NLM_F_DUMP;
snl_add_msg_attr_u32(&nw, PF_ART_TICKET, ticket);
snl_add_msg_attr_u32(&nw, PF_ART_POOL_TICKET, pool_ticket);
snl_add_msg_attr_string(&nw, PF_ART_ANCHOR, anchor);
snl_add_msg_attr_string(&nw, PF_ART_ANCHOR_CALL, anchor_call);
nvlist_add_number(nvlr, "nr", r->nr);
pfctl_nv_add_rule_addr(nvlr, "src", &r->src);
pfctl_nv_add_rule_addr(nvlr, "dst", &r->dst);
snl_add_msg_attr_pf_rule(&nw, PF_ART_RULE, r);
labelcount = 0;
while (r->label[labelcount][0] != 0 &&
labelcount < PF_RULE_MAX_LABEL_COUNT) {
nvlist_append_string_array(nvlr, "labels",
r->label[labelcount]);
labelcount++;
if ((hdr = snl_finalize_msg(&nw)) == NULL)
return (ENXIO);
seq_id = hdr->nlmsg_seq;
if (! snl_send_message(&ss, hdr)) {
printf("Send failed\n");
return (ENXIO);
}
nvlist_add_number(nvlr, "ridentifier", r->ridentifier);
nvlist_add_string(nvlr, "ifname", r->ifname);
nvlist_add_string(nvlr, "qname", r->qname);
nvlist_add_string(nvlr, "pqname", r->pqname);
nvlist_add_string(nvlr, "tagname", r->tagname);
nvlist_add_string(nvlr, "match_tagname", r->match_tagname);
nvlist_add_string(nvlr, "overload_tblname", r->overload_tblname);
while ((hdr = snl_read_reply_multi(&ss, seq_id, &e)) != NULL) {
}
pfctl_nv_add_pool(nvlr, "rpool", &r->rpool);
nvlist_add_number(nvlr, "os_fingerprint", r->os_fingerprint);
nvlist_add_number(nvlr, "rtableid", r->rtableid);
for (int i = 0; i < PFTM_MAX; i++)
timeouts[i] = r->timeout[i];
nvlist_add_number_array(nvlr, "timeout", timeouts, PFTM_MAX);
nvlist_add_number(nvlr, "max_states", r->max_states);
nvlist_add_number(nvlr, "max_src_nodes", r->max_src_nodes);
nvlist_add_number(nvlr, "max_src_states", r->max_src_states);
nvlist_add_number(nvlr, "max_src_conn", r->max_src_conn);
nvlist_add_number(nvlr, "max_src_conn_rate.limit",
r->max_src_conn_rate.limit);
nvlist_add_number(nvlr, "max_src_conn_rate.seconds",
r->max_src_conn_rate.seconds);
nvlist_add_number(nvlr, "dnpipe", r->dnpipe);
nvlist_add_number(nvlr, "dnrpipe", r->dnrpipe);
nvlist_add_number(nvlr, "dnflags", r->free_flags);
nvlist_add_number(nvlr, "prob", r->prob);
nvlist_add_number(nvlr, "cuid", r->cuid);
nvlist_add_number(nvlr, "cpid", r->cpid);
nvlist_add_number(nvlr, "return_icmp", r->return_icmp);
nvlist_add_number(nvlr, "return_icmp6", r->return_icmp6);
nvlist_add_number(nvlr, "max_mss", r->max_mss);
nvlist_add_number(nvlr, "scrub_flags", r->scrub_flags);
pfctl_nv_add_uid(nvlr, "uid", &r->uid);
pfctl_nv_add_uid(nvlr, "gid", (const struct pf_rule_uid *)&r->gid);
nvlist_add_number(nvlr, "rule_flag", r->rule_flag);
nvlist_add_number(nvlr, "action", r->action);
nvlist_add_number(nvlr, "direction", r->direction);
nvlist_add_number(nvlr, "log", r->log);
nvlist_add_number(nvlr, "logif", r->logif);
nvlist_add_number(nvlr, "quick", r->quick);
nvlist_add_number(nvlr, "ifnot", r->ifnot);
nvlist_add_number(nvlr, "match_tag_not", r->match_tag_not);
nvlist_add_number(nvlr, "natpass", r->natpass);
nvlist_add_number(nvlr, "keep_state", r->keep_state);
nvlist_add_number(nvlr, "af", r->af);
nvlist_add_number(nvlr, "proto", r->proto);
nvlist_add_number(nvlr, "type", r->type);
nvlist_add_number(nvlr, "code", r->code);
nvlist_add_number(nvlr, "flags", r->flags);
nvlist_add_number(nvlr, "flagset", r->flagset);
nvlist_add_number(nvlr, "min_ttl", r->min_ttl);
nvlist_add_number(nvlr, "allow_opts", r->allow_opts);
nvlist_add_number(nvlr, "rt", r->rt);
nvlist_add_number(nvlr, "return_ttl", r->return_ttl);
nvlist_add_number(nvlr, "tos", r->tos);
nvlist_add_number(nvlr, "set_tos", r->set_tos);
nvlist_add_number(nvlr, "anchor_relative", r->anchor_relative);
nvlist_add_number(nvlr, "anchor_wildcard", r->anchor_wildcard);
nvlist_add_number(nvlr, "flush", r->flush);
nvlist_add_number(nvlr, "prio", r->prio);
set_prio[0] = r->set_prio[0];
set_prio[1] = r->set_prio[1];
nvlist_add_number_array(nvlr, "set_prio", set_prio, 2);
pfctl_nv_add_divert(nvlr, "divert", r);
nvlist_add_nvlist(nvl, "rule", nvlr);
nvlist_destroy(nvlr);
/* Now do the call. */
nv.data = nvlist_pack(nvl, &nv.len);
nv.size = nv.len;
ret = ioctl(dev, DIOCADDRULENV, &nv);
if (ret == -1)
ret = errno;
free(nv.data);
nvlist_destroy(nvl);
return (ret);
return (e.error);
}
int

View file

@ -2465,6 +2465,10 @@ int pf_keth_anchor_nvcopyout(
struct pf_keth_ruleset *pf_find_or_create_keth_ruleset(const char *);
void pf_keth_anchor_remove(struct pf_keth_rule *);
int pf_ioctl_addrule(struct pf_krule *, uint32_t,
uint32_t, const char *, const char *, uid_t uid,
pid_t);
void pf_krule_free(struct pf_krule *);
#endif

View file

@ -2016,10 +2016,10 @@ pf_rule_to_krule(const struct pf_rule *rule, struct pf_krule *krule)
return (0);
}
static int
int
pf_ioctl_addrule(struct pf_krule *rule, uint32_t ticket,
uint32_t pool_ticket, const char *anchor, const char *anchor_call,
struct thread *td)
uid_t uid, pid_t pid)
{
struct pf_kruleset *ruleset;
struct pf_krule *tail;
@ -2045,8 +2045,8 @@ pf_ioctl_addrule(struct pf_krule *rule, uint32_t ticket,
rule->states_cur = counter_u64_alloc(M_WAITOK);
rule->states_tot = counter_u64_alloc(M_WAITOK);
rule->src_nodes = counter_u64_alloc(M_WAITOK);
rule->cuid = td->td_ucred->cr_ruid;
rule->cpid = td->td_proc ? td->td_proc->p_pid : 0;
rule->cuid = uid;
rule->cpid = pid;
TAILQ_INIT(&rule->rpool.list);
PF_CONFIG_LOCK();
@ -3076,7 +3076,8 @@ pfioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flags, struct thread *td
/* Frees rule on error */
error = pf_ioctl_addrule(rule, ticket, pool_ticket, anchor,
anchor_call, td);
anchor_call, td->td_ucred->cr_ruid,
td->td_proc ? td->td_proc->p_pid : 0);
nvlist_destroy(nvl);
free(nvlpacked, M_NVLIST);
@ -3104,7 +3105,8 @@ pfioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flags, struct thread *td
/* Frees rule on error */
error = pf_ioctl_addrule(rule, pr->ticket, pr->pool_ticket,
pr->anchor, pr->anchor_call, td);
pr->anchor, pr->anchor_call, td->td_ucred->cr_ruid,
td->td_proc ? td->td_proc->p_pid : 0);
break;
}

View file

@ -348,7 +348,265 @@ pf_handle_stop(struct nlmsghdr *hdr __unused, struct nl_pstate *npt __unused)
return (pf_stop());
}
static const struct nlhdr_parser *all_parsers[] = { &state_parser };
#define _OUT(_field) offsetof(struct pf_addr_wrap, _field)
static const struct nlattr_parser nla_p_addr_wrap[] = {
{ .type = PF_AT_ADDR, .off = _OUT(v.a.addr), .cb = nlattr_get_in6_addr },
{ .type = PF_AT_MASK, .off = _OUT(v.a.mask), .cb = nlattr_get_in6_addr },
{ .type = PF_AT_IFNAME, .off = _OUT(v.ifname), .arg = (void *)IFNAMSIZ,.cb = nlattr_get_chara },
{ .type = PF_AT_TABLENAME, .off = _OUT(v.tblname), .arg = (void *)PF_TABLE_NAME_SIZE, .cb = nlattr_get_chara },
{ .type = PF_AT_TYPE, .off = _OUT(type), .cb = nlattr_get_uint8 },
{ .type = PF_AT_IFLAGS, .off = _OUT(iflags), .cb = nlattr_get_uint8 },
};
NL_DECLARE_ATTR_PARSER(addr_wrap_parser, nla_p_addr_wrap);
#undef _OUT
#define _OUT(_field) offsetof(struct pf_rule_addr, _field)
static const struct nlattr_parser nla_p_ruleaddr[] = {
{ .type = PF_RAT_ADDR, .off = _OUT(addr), .arg = &addr_wrap_parser, .cb = nlattr_get_nested },
{ .type = PF_RAT_SRC_PORT, .off = _OUT(port[0]), .cb = nlattr_get_uint16 },
{ .type = PF_RAT_DST_PORT, .off = _OUT(port[1]), .cb = nlattr_get_uint16 },
{ .type = PF_RAT_NEG, .off = _OUT(neg), .cb = nlattr_get_uint8 },
{ .type = PF_RAT_OP, .off = _OUT(port_op), .cb = nlattr_get_uint8 },
};
NL_DECLARE_ATTR_PARSER(rule_addr_parser, nla_p_ruleaddr);
#undef _OUT
#define _OUT(_field) offsetof(struct pf_mape_portset, _field)
static const struct nlattr_parser nla_p_mape_portset[] = {
{ .type = PF_MET_OFFSET, .off = _OUT(offset), .cb = nlattr_get_uint8 },
{ .type = PF_MET_PSID_LEN, .off = _OUT(psidlen), .cb = nlattr_get_uint8 },
{. type = PF_MET_PSID, .off = _OUT(psid), .cb = nlattr_get_uint16 },
};
NL_DECLARE_ATTR_PARSER(mape_portset_parser, nla_p_mape_portset);
#undef _OUT
struct nl_parsed_labels
{
char labels[PF_RULE_MAX_LABEL_COUNT][PF_RULE_LABEL_SIZE];
uint32_t i;
};
static int
nlattr_get_pf_rule_labels(struct nlattr *nla, struct nl_pstate *npt,
const void *arg, void *target)
{
struct nl_parsed_labels *l = (struct nl_parsed_labels *)target;
int ret;
if (l->i >= PF_RULE_MAX_LABEL_COUNT)
return (E2BIG);
ret = nlattr_get_chara(nla, npt, (void *)PF_RULE_LABEL_SIZE,
l->labels[l->i]);
if (ret == 0)
l->i++;
return (ret);
}
#define _OUT(_field) offsetof(struct nl_parsed_labels, _field)
static const struct nlattr_parser nla_p_labels[] = {
{ .type = PF_LT_LABEL, .off = 0, .cb = nlattr_get_pf_rule_labels },
};
NL_DECLARE_ATTR_PARSER(rule_labels_parser, nla_p_labels);
#undef _OUT
static int
nlattr_get_nested_pf_rule_labels(struct nlattr *nla, struct nl_pstate *npt, const void *arg, void *target)
{
struct nl_parsed_labels parsed_labels = { };
int error;
/* Assumes target points to the beginning of the structure */
error = nl_parse_header(NLA_DATA(nla), NLA_DATA_LEN(nla), &rule_labels_parser, npt, &parsed_labels);
if (error != 0)
return (error);
memcpy(target, parsed_labels.labels, sizeof(parsed_labels));
return (0);
}
#define _OUT(_field) offsetof(struct pf_kpool, _field)
static const struct nlattr_parser nla_p_pool[] = {
{ .type = PF_PT_KEY, .off = _OUT(key), .arg = (void *)sizeof(struct pf_poolhashkey), .cb = nlattr_get_bytes },
{ .type = PF_PT_COUNTER, .off = _OUT(counter), .cb = nlattr_get_in6_addr },
{ .type = PF_PT_TBLIDX, .off = _OUT(tblidx), .cb = nlattr_get_uint32 },
{ .type = PF_PT_PROXY_SRC_PORT, .off = _OUT(proxy_port[0]), .cb = nlattr_get_uint16 },
{ .type = PF_PT_PROXY_DST_PORT, .off = _OUT(proxy_port[1]), .cb = nlattr_get_uint16 },
{ .type = PF_PT_OPTS, .off = _OUT(opts), .cb = nlattr_get_uint8 },
{ .type = PF_PT_MAPE, .off = _OUT(mape), .arg = &mape_portset_parser, .cb = nlattr_get_nested },
};
NL_DECLARE_ATTR_PARSER(pool_parser, nla_p_pool);
#undef _OUT
#define _OUT(_field) offsetof(struct pf_rule_uid, _field)
static const struct nlattr_parser nla_p_rule_uid[] = {
{ .type = PF_RUT_UID_LOW, .off = _OUT(uid[0]), .cb = nlattr_get_uint32 },
{ .type = PF_RUT_UID_HIGH, .off = _OUT(uid[1]), .cb = nlattr_get_uint32 },
{ .type = PF_RUT_OP, .off = _OUT(op), .cb = nlattr_get_uint8 },
};
NL_DECLARE_ATTR_PARSER(rule_uid_parser, nla_p_rule_uid);
#undef _OUT
struct nl_parsed_timeouts
{
uint32_t timeouts[PFTM_MAX];
uint32_t i;
};
static int
nlattr_get_pf_timeout(struct nlattr *nla, struct nl_pstate *npt,
const void *arg, void *target)
{
struct nl_parsed_timeouts *t = (struct nl_parsed_timeouts *)target;
int ret;
if (t->i >= PFTM_MAX)
return (E2BIG);
ret = nlattr_get_uint32(nla, npt, NULL, &t->timeouts[t->i]);
if (ret == 0)
t->i++;
return (ret);
}
#define _OUT(_field) offsetof(struct nl_parsed_timeout, _field)
static const struct nlattr_parser nla_p_timeouts[] = {
{ .type = PF_TT_TIMEOUT, .off = 0, .cb = nlattr_get_pf_timeout },
};
NL_DECLARE_ATTR_PARSER(timeout_parser, nla_p_timeouts);
#undef _OUT
static int
nlattr_get_nested_timeouts(struct nlattr *nla, struct nl_pstate *npt, const void *arg, void *target)
{
struct nl_parsed_timeouts parsed_timeouts = { };
int error;
/* Assumes target points to the beginning of the structure */
error = nl_parse_header(NLA_DATA(nla), NLA_DATA_LEN(nla), &timeout_parser, npt, &parsed_timeouts);
if (error != 0)
return (error);
memcpy(target, parsed_timeouts.timeouts, sizeof(parsed_timeouts.timeouts));
return (0);
}
#define _OUT(_field) offsetof(struct pf_krule, _field)
static const struct nlattr_parser nla_p_rule[] = {
{ .type = PF_RT_SRC, .off = _OUT(src), .arg = &rule_addr_parser,.cb = nlattr_get_nested },
{ .type = PF_RT_DST, .off = _OUT(dst), .arg = &rule_addr_parser,.cb = nlattr_get_nested },
{ .type = PF_RT_RIDENTIFIER, .off = _OUT(ridentifier), .cb = nlattr_get_uint32 },
{ .type = PF_RT_LABELS, .off = _OUT(label), .arg = &rule_labels_parser,.cb = nlattr_get_nested_pf_rule_labels },
{ .type = PF_RT_IFNAME, .off = _OUT(ifname), .arg = (void *)IFNAMSIZ, .cb = nlattr_get_chara },
{ .type = PF_RT_QNAME, .off = _OUT(qname), .arg = (void *)PF_QNAME_SIZE, .cb = nlattr_get_chara },
{ .type = PF_RT_PQNAME, .off = _OUT(pqname), .arg = (void *)PF_QNAME_SIZE, .cb = nlattr_get_chara },
{ .type = PF_RT_TAGNAME, .off = _OUT(tagname), .arg = (void *)PF_TAG_NAME_SIZE, .cb = nlattr_get_chara },
{ .type = PF_RT_MATCH_TAGNAME, .off = _OUT(match_tagname), .arg = (void *)PF_TAG_NAME_SIZE, .cb = nlattr_get_chara },
{ .type = PF_RT_OVERLOAD_TBLNAME, .off = _OUT(overload_tblname), .arg = (void *)PF_TABLE_NAME_SIZE, .cb = nlattr_get_chara },
{ .type = PF_RT_RPOOL, .off = _OUT(rpool), .arg = &pool_parser, .cb = nlattr_get_nested },
{ .type = PF_RT_OS_FINGERPRINT, .off = _OUT(os_fingerprint), .cb = nlattr_get_uint32 },
{ .type = PF_RT_RTABLEID, .off = _OUT(rtableid), .cb = nlattr_get_uint32 },
{ .type = PF_RT_TIMEOUT, .off = _OUT(timeout), .arg = &timeout_parser, .cb = nlattr_get_nested_timeouts },
{ .type = PF_RT_MAX_STATES, .off = _OUT(max_states), .cb = nlattr_get_uint32 },
{ .type = PF_RT_MAX_SRC_NODES, .off = _OUT(max_src_nodes), .cb = nlattr_get_uint32 },
{ .type = PF_RT_MAX_SRC_STATES, .off = _OUT(max_src_states), .cb = nlattr_get_uint32 },
{ .type = PF_RT_MAX_SRC_CONN_RATE_LIMIT, .off = _OUT(max_src_conn_rate.limit), .cb = nlattr_get_uint32 },
{ .type = PF_RT_MAX_SRC_CONN_RATE_SECS, .off = _OUT(max_src_conn_rate.seconds), .cb = nlattr_get_uint32 },
{ .type = PF_RT_DNPIPE, .off = _OUT(dnpipe), .cb = nlattr_get_uint16 },
{ .type = PF_RT_DNRPIPE, .off = _OUT(dnrpipe), .cb = nlattr_get_uint16 },
{ .type = PF_RT_DNFLAGS, .off = _OUT(free_flags), .cb = nlattr_get_uint32 },
{ .type = PF_RT_NR, .off = _OUT(nr), .cb = nlattr_get_uint32 },
{ .type = PF_RT_PROB, .off = _OUT(prob), .cb = nlattr_get_uint32 },
{ .type = PF_RT_CUID, .off = _OUT(cuid), .cb = nlattr_get_uint32 },
{. type = PF_RT_CPID, .off = _OUT(cpid), .cb = nlattr_get_uint32 },
{ .type = PF_RT_RETURN_ICMP, .off = _OUT(return_icmp), .cb = nlattr_get_uint16 },
{ .type = PF_RT_RETURN_ICMP6, .off = _OUT(return_icmp6), .cb = nlattr_get_uint16 },
{ .type = PF_RT_MAX_MSS, .off = _OUT(max_mss), .cb = nlattr_get_uint16 },
{ .type = PF_RT_SCRUB_FLAGS, .off = _OUT(scrub_flags), .cb = nlattr_get_uint16 },
{ .type = PF_RT_UID, .off = _OUT(uid), .arg = &rule_uid_parser, .cb = nlattr_get_nested },
{ .type = PF_RT_GID, .off = _OUT(gid), .arg = &rule_uid_parser, .cb = nlattr_get_nested },
{ .type = PF_RT_RULE_FLAG, .off = _OUT(rule_flag), .cb = nlattr_get_uint32 },
{ .type = PF_RT_ACTION, .off = _OUT(action), .cb = nlattr_get_uint8 },
{ .type = PF_RT_DIRECTION, .off = _OUT(direction), .cb = nlattr_get_uint8 },
{ .type = PF_RT_LOG, .off = _OUT(log), .cb = nlattr_get_uint8 },
{ .type = PF_RT_LOGIF, .off = _OUT(logif), .cb = nlattr_get_uint8 },
{ .type = PF_RT_QUICK, .off = _OUT(quick), .cb = nlattr_get_uint8 },
{ .type = PF_RT_IF_NOT, .off = _OUT(ifnot), .cb = nlattr_get_uint8 },
{ .type = PF_RT_MATCH_TAG_NOT, .off = _OUT(match_tag_not), .cb = nlattr_get_uint8 },
{ .type = PF_RT_NATPASS, .off = _OUT(natpass), .cb = nlattr_get_uint8 },
{ .type = PF_RT_KEEP_STATE, .off = _OUT(keep_state), .cb = nlattr_get_uint8 },
{ .type = PF_RT_AF, .off = _OUT(af), .cb = nlattr_get_uint8 },
{ .type = PF_RT_PROTO, .off = _OUT(proto), .cb = nlattr_get_uint8 },
{ .type = PF_RT_TYPE, .off = _OUT(type), .cb = nlattr_get_uint8 },
{ .type = PF_RT_CODE, .off = _OUT(code), .cb = nlattr_get_uint8 },
{ .type = PF_RT_FLAGS, .off = _OUT(flags), .cb = nlattr_get_uint8 },
{ .type = PF_RT_FLAGSET, .off = _OUT(flagset), .cb = nlattr_get_uint8 },
{ .type = PF_RT_MIN_TTL, .off = _OUT(min_ttl), .cb = nlattr_get_uint8 },
{ .type = PF_RT_ALLOW_OPTS, .off = _OUT(allow_opts), .cb = nlattr_get_uint8 },
{ .type = PF_RT_RT, .off = _OUT(rt), .cb = nlattr_get_uint8 },
{ .type = PF_RT_RETURN_TTL, .off = _OUT(return_ttl), .cb = nlattr_get_uint8 },
{ .type = PF_RT_TOS, .off = _OUT(tos), .cb = nlattr_get_uint8 },
{ .type = PF_RT_SET_TOS, .off = _OUT(set_tos), .cb = nlattr_get_uint8 },
{ .type = PF_RT_ANCHOR_RELATIVE, .off = _OUT(anchor_relative), .cb = nlattr_get_uint8 },
{ .type = PF_RT_ANCHOR_WILDCARD, .off = _OUT(anchor_wildcard), .cb = nlattr_get_uint8 },
{ .type = PF_RT_FLUSH, .off = _OUT(flush), .cb = nlattr_get_uint8 },
{ .type = PF_RT_PRIO, .off = _OUT(prio), .cb = nlattr_get_uint8 },
{ .type = PF_RT_SET_PRIO, .off = _OUT(set_prio[0]), .cb = nlattr_get_uint8 },
{ .type = PF_RT_SET_PRIO_REPLY, .off = _OUT(set_prio[1]), .cb = nlattr_get_uint8 },
{ .type = PF_RT_DIVERT_ADDRESS, .off = _OUT(divert.addr), .cb = nlattr_get_in6_addr },
{ .type = PF_RT_DIVERT_PORT, .off = _OUT(divert.port), .cb = nlattr_get_uint16 },
};
NL_DECLARE_ATTR_PARSER(rule_parser, nla_p_rule);
#undef _OUT
struct nl_parsed_addrule {
struct pf_krule *rule;
uint32_t ticket;
uint32_t pool_ticket;
char *anchor;
char *anchor_call;
};
#define _IN(_field) offsetof(struct genlmsghdr, _field)
#define _OUT(_field) offsetof(struct nl_parsed_addrule, _field)
static const struct nlattr_parser nla_p_addrule[] = {
{ .type = PF_ART_TICKET, .off = _OUT(ticket), .cb = nlattr_get_uint32 },
{ .type = PF_ART_POOL_TICKET, .off = _OUT(pool_ticket), .cb = nlattr_get_uint32 },
{ .type = PF_ART_ANCHOR, .off = _OUT(anchor), .cb = nlattr_get_string },
{ .type = PF_ART_ANCHOR_CALL, .off = _OUT(anchor_call), .cb = nlattr_get_string },
{ .type = PF_ART_RULE, .off = _OUT(rule), .arg = &rule_parser, .cb = nlattr_get_nested_ptr }
};
static const struct nlfield_parser nlf_p_addrule[] = {
};
#undef _IN
#undef _OUT
NL_DECLARE_PARSER(addrule_parser, struct genlmsghdr, nlf_p_addrule, nla_p_addrule);
static int
pf_handle_addrule(struct nlmsghdr *hdr, struct nl_pstate *npt)
{
int error;
struct nl_parsed_addrule attrs = {};
attrs.rule = pf_krule_alloc();
error = nl_parse_nlmsg(hdr, &addrule_parser, npt, &attrs);
if (error != 0)
return (error);
error = pf_ioctl_addrule(attrs.rule, attrs.ticket, attrs.pool_ticket,
attrs.anchor, attrs.anchor_call, nlp_get_cred(npt->nlp)->cr_uid,
hdr->nlmsg_pid);
if (error != 0)
pf_krule_free(attrs.rule);
return (error);
}
static const struct nlhdr_parser *all_parsers[] = { &state_parser, &addrule_parser };
static int family_id;
@ -377,12 +635,20 @@ static const struct genl_cmd pf_cmds[] = {
.cmd_cb = pf_handle_stop,
.cmd_flags = GENL_CMD_CAP_DO | GENL_CMD_CAP_HASPOL,
},
{
.cmd_num = PFNL_CMD_ADDRULE,
.cmd_name = "ADDRULE",
.cmd_cb = pf_handle_addrule,
.cmd_flags = GENL_CMD_CAP_DO | GENL_CMD_CAP_DUMP | GENL_CMD_CAP_HASPOL,
},
};
void
pf_nl_register(void)
{
NL_VERIFY_PARSERS(all_parsers);
family_id = genl_register_family(PFNL_FAMILY_NAME, 0, 2, PFNL_CMD_MAX);
genl_register_cmds(PFNL_FAMILY_NAME, pf_cmds, NL_ARRAY_LEN(pf_cmds));
}

View file

@ -40,6 +40,7 @@ enum {
PFNL_CMD_GETCREATORS = 2,
PFNL_CMD_START = 3,
PFNL_CMD_STOP = 4,
PFNL_CMD_ADDRULE = 5,
__PFNL_CMD_MAX,
};
#define PFNL_CMD_MAX (__PFNL_CMD_MAX -1)
@ -98,6 +99,137 @@ enum pfstate_type_t {
PF_ST_VERSION = 28, /* u64 */
};
enum pf_addr_type_t {
PF_AT_UNSPEC,
PF_AT_ADDR = 1, /* in6_addr */
PF_AT_MASK = 2, /* in6_addr */
PF_AT_IFNAME = 3, /* string */
PF_AT_TABLENAME = 4, /* string */
PF_AT_TYPE = 5, /* u8 */
PF_AT_IFLAGS = 6, /* u8 */
};
enum pfrule_addr_type_t {
PF_RAT_UNSPEC,
PF_RAT_ADDR = 1, /* nested, pf_addr_type_t */
PF_RAT_SRC_PORT = 2, /* u16 */
PF_RAT_DST_PORT = 3, /* u16 */
PF_RAT_NEG = 4, /* u8 */
PF_RAT_OP = 5, /* u8 */
};
enum pf_labels_type_t {
PF_LT_UNSPEC,
PF_LT_LABEL = 1, /* string */
};
enum pf_mape_portset_type_t
{
PF_MET_UNSPEC,
PF_MET_OFFSET = 1, /* u8 */
PF_MET_PSID_LEN = 2, /* u8 */
PF_MET_PSID = 3, /* u16 */
};
enum pf_rpool_type_t
{
PF_PT_UNSPEC,
PF_PT_KEY = 1, /* bytes, sizeof(struct pf_poolhashkey) */
PF_PT_COUNTER = 2, /* in6_addr */
PF_PT_TBLIDX = 3, /* u32 */
PF_PT_PROXY_SRC_PORT = 4, /* u16 */
PF_PT_PROXY_DST_PORT = 5, /* u16 */
PF_PT_OPTS = 6, /* u8 */
PF_PT_MAPE = 7, /* nested, pf_mape_portset_type_t */
};
enum pf_timeout_type_t {
PF_TT_UNSPEC,
PF_TT_TIMEOUT = 1, /* u32 */
};
enum pf_rule_uid_type_t {
PF_RUT_UNSPEC,
PF_RUT_UID_LOW = 1, /* u32 */
PF_RUT_UID_HIGH = 2, /* u32 */
PF_RUT_OP = 3, /* u8 */
};
enum pf_rule_type_t {
PF_RT_UNSPEC,
PF_RT_SRC = 1, /* nested, pf_rule_addr_type_t */
PF_RT_DST = 2, /* nested, pf_rule_addr_type_t */
PF_RT_RIDENTIFIER = 3, /* u32 */
PF_RT_LABELS = 4, /* nested, pf_labels_type_t */
PF_RT_IFNAME = 5, /* string */
PF_RT_QNAME = 6, /* string */
PF_RT_PQNAME = 7, /* string */
PF_RT_TAGNAME = 8, /* string */
PF_RT_MATCH_TAGNAME = 9, /* string */
PF_RT_OVERLOAD_TBLNAME = 10, /* string */
PF_RT_RPOOL = 11, /* nested, pf_rpool_type_t */
PF_RT_OS_FINGERPRINT = 12, /* u32 */
PF_RT_RTABLEID = 13, /* u32 */
PF_RT_TIMEOUT = 14, /* nested, pf_timeout_type_t */
PF_RT_MAX_STATES = 15, /* u32 */
PF_RT_MAX_SRC_NODES = 16, /* u32 */
PF_RT_MAX_SRC_STATES = 17, /* u32 */
PF_RT_MAX_SRC_CONN_RATE_LIMIT = 18, /* u32 */
PF_RT_MAX_SRC_CONN_RATE_SECS = 19, /* u32 */
PF_RT_DNPIPE = 20, /* u16 */
PF_RT_DNRPIPE = 21, /* u16 */
PF_RT_DNFLAGS = 22, /* u32 */
PF_RT_NR = 23, /* u32 */
PF_RT_PROB = 24, /* u32 */
PF_RT_CUID = 25, /* u32 */
PF_RT_CPID = 26, /* u32 */
PF_RT_RETURN_ICMP = 27, /* u16 */
PF_RT_RETURN_ICMP6 = 28, /* u16 */
PF_RT_MAX_MSS = 29, /* u16 */
PF_RT_SCRUB_FLAGS = 30, /* u16 */
PF_RT_UID = 31, /* nested, pf_rule_uid_type_t */
PF_RT_GID = 32, /* nested, pf_rule_uid_type_t */
PF_RT_RULE_FLAG = 33, /* u32 */
PF_RT_ACTION = 34, /* u8 */
PF_RT_DIRECTION = 35, /* u8 */
PF_RT_LOG = 36, /* u8 */
PF_RT_LOGIF = 37, /* u8 */
PF_RT_QUICK = 38, /* u8 */
PF_RT_IF_NOT = 39, /* u8 */
PF_RT_MATCH_TAG_NOT = 40, /* u8 */
PF_RT_NATPASS = 41, /* u8 */
PF_RT_KEEP_STATE = 42, /* u8 */
PF_RT_AF = 43, /* u8 */
PF_RT_PROTO = 44, /* u8 */
PF_RT_TYPE = 45, /* u8 */
PF_RT_CODE = 46, /* u8 */
PF_RT_FLAGS = 47, /* u8 */
PF_RT_FLAGSET = 48, /* u8 */
PF_RT_MIN_TTL = 49, /* u8 */
PF_RT_ALLOW_OPTS = 50, /* u8 */
PF_RT_RT = 51, /* u8 */
PF_RT_RETURN_TTL = 52, /* u8 */
PF_RT_TOS = 53, /* u8 */
PF_RT_SET_TOS = 54, /* u8 */
PF_RT_ANCHOR_RELATIVE = 55, /* u8 */
PF_RT_ANCHOR_WILDCARD = 56, /* u8 */
PF_RT_FLUSH = 57, /* u8 */
PF_RT_PRIO = 58, /* u8 */
PF_RT_SET_PRIO = 59, /* u8 */
PF_RT_SET_PRIO_REPLY = 60, /* u8 */
PF_RT_DIVERT_ADDRESS = 61, /* in6_addr */
PF_RT_DIVERT_PORT = 62, /* u16 */
};
enum pf_addrule_type_t {
PF_ART_UNSPEC,
PF_ART_TICKET = 1, /* u32 */
PF_ART_POOL_TICKET = 2, /* u32 */
PF_ART_ANCHOR = 3, /* string */
PF_ART_ANCHOR_CALL = 4, /* string */
PF_ART_RULE = 5, /* nested, pfrule_type_t */
};
#ifdef _KERNEL
void pf_nl_register(void);