pf: implement addrule via netlink

Sponsored by:	Rubicon Communications, LLC ("Netgate")
This commit is contained in:
Kristof Provost 2024-01-12 11:54:18 +01:00
parent b8ef285f6c
commit 777a4702c5
9 changed files with 659 additions and 63 deletions

View file

@ -1229,7 +1229,287 @@ pfctl_get_rule(int dev, uint32_t nr, uint32_t ticket, const char *anchor,
anchor_call, false));
}
int pfctl_get_clear_rule(int dev, uint32_t nr, uint32_t ticket,
#define _OUT(_field) offsetof(struct pf_addr_wrap, _field)
static const struct snl_attr_parser ap_addr_wrap[] = {
{ .type = PF_AT_ADDR, .off = _OUT(v.a.addr), .cb = snl_attr_get_in6_addr },
{ .type = PF_AT_MASK, .off = _OUT(v.a.mask), .cb = snl_attr_get_in6_addr },
{ .type = PF_AT_IFNAME, .off = _OUT(v.ifname), .arg = (void *)IFNAMSIZ,.cb = snl_attr_copy_string },
{ .type = PF_AT_TABLENAME, .off = _OUT(v.tblname), .arg = (void *)PF_TABLE_NAME_SIZE, .cb = snl_attr_copy_string },
{ .type = PF_AT_TYPE, .off = _OUT(type), .cb = snl_attr_get_uint8 },
{ .type = PF_AT_IFLAGS, .off = _OUT(iflags), .cb = snl_attr_get_uint8 },
{ .type = PF_AT_TBLCNT, .off = _OUT(p.tblcnt), .cb = snl_attr_get_uint32 },
{ .type = PF_AT_DYNCNT, .off = _OUT(p.dyncnt), .cb = snl_attr_get_uint32 },
};
SNL_DECLARE_ATTR_PARSER(addr_wrap_parser, ap_addr_wrap);
#undef _OUT
#define _OUT(_field) offsetof(struct pf_rule_addr, _field)
static struct snl_attr_parser ap_rule_addr[] = {
{ .type = PF_RAT_ADDR, .off = _OUT(addr), .arg = &addr_wrap_parser, .cb = snl_attr_get_nested },
{ .type = PF_RAT_SRC_PORT, .off = _OUT(port[0]), .cb = snl_attr_get_uint16 },
{ .type = PF_RAT_DST_PORT, .off = _OUT(port[1]), .cb = snl_attr_get_uint16 },
{ .type = PF_RAT_NEG, .off = _OUT(neg), .cb = snl_attr_get_uint8 },
{ .type = PF_RAT_OP, .off = _OUT(port_op), .cb = snl_attr_get_uint8 },
};
#undef _OUT
SNL_DECLARE_ATTR_PARSER(rule_addr_parser, ap_rule_addr);
struct snl_parsed_labels
{
char labels[PF_RULE_MAX_LABEL_COUNT][PF_RULE_LABEL_SIZE];
uint32_t i;
};
static bool
snl_attr_get_pf_rule_labels(struct snl_state *ss, struct nlattr *nla,
const void *arg __unused, void *target)
{
struct snl_parsed_labels *l = (struct snl_parsed_labels *)target;
bool ret;
if (l->i >= PF_RULE_MAX_LABEL_COUNT)
return (E2BIG);
ret = snl_attr_copy_string(ss, nla, (void *)PF_RULE_LABEL_SIZE,
l->labels[l->i]);
if (ret)
l->i++;
return (ret);
}
#define _OUT(_field) offsetof(struct nl_parsed_labels, _field)
static const struct snl_attr_parser ap_labels[] = {
{ .type = PF_LT_LABEL, .off = 0, .cb = snl_attr_get_pf_rule_labels },
};
SNL_DECLARE_ATTR_PARSER(rule_labels_parser, ap_labels);
#undef _OUT
static bool
snl_attr_get_nested_pf_rule_labels(struct snl_state *ss, struct nlattr *nla,
const void *arg __unused, void *target)
{
struct snl_parsed_labels parsed_labels = { };
bool error;
/* Assumes target points to the beginning of the structure */
error = snl_parse_header(ss, NLA_DATA(nla), NLA_DATA_LEN(nla), &rule_labels_parser, &parsed_labels);
if (! error)
return (error);
memcpy(target, parsed_labels.labels, sizeof(parsed_labels));
return (true);
}
#define _OUT(_field) offsetof(struct pf_mape_portset, _field)
static const struct snl_attr_parser ap_mape_portset[] = {
{ .type = PF_MET_OFFSET, .off = _OUT(offset), .cb = snl_attr_get_uint8 },
{ .type = PF_MET_PSID_LEN, .off = _OUT(psidlen), .cb = snl_attr_get_uint8 },
{. type = PF_MET_PSID, .off = _OUT(psid), .cb = snl_attr_get_uint16 },
};
SNL_DECLARE_ATTR_PARSER(mape_portset_parser, ap_mape_portset);
#undef _OUT
#define _OUT(_field) offsetof(struct pfctl_pool, _field)
static const struct snl_attr_parser ap_pool[] = {
{ .type = PF_PT_KEY, .off = _OUT(key), .arg = (void *)sizeof(struct pf_poolhashkey), .cb = snl_attr_get_bytes },
{ .type = PF_PT_COUNTER, .off = _OUT(counter), .cb = snl_attr_get_in6_addr },
{ .type = PF_PT_TBLIDX, .off = _OUT(tblidx), .cb = snl_attr_get_uint32 },
{ .type = PF_PT_PROXY_SRC_PORT, .off = _OUT(proxy_port[0]), .cb = snl_attr_get_uint16 },
{ .type = PF_PT_PROXY_DST_PORT, .off = _OUT(proxy_port[1]), .cb = snl_attr_get_uint16 },
{ .type = PF_PT_OPTS, .off = _OUT(opts), .cb = snl_attr_get_uint8 },
{ .type = PF_PT_MAPE, .off = _OUT(mape), .arg = &mape_portset_parser, .cb = snl_attr_get_nested },
};
SNL_DECLARE_ATTR_PARSER(pool_parser, ap_pool);
#undef _OUT
struct nl_parsed_timeouts
{
uint32_t timeouts[PFTM_MAX];
uint32_t i;
};
static bool
snl_attr_get_pf_timeout(struct snl_state *ss, struct nlattr *nla,
const void *arg __unused, void *target)
{
struct nl_parsed_timeouts *t = (struct nl_parsed_timeouts *)target;
bool ret;
if (t->i >= PFTM_MAX)
return (E2BIG);
ret = snl_attr_get_uint32(ss, nla, NULL, &t->timeouts[t->i]);
if (ret)
t->i++;
return (ret);
}
#define _OUT(_field) offsetof(struct nl_parsed_timeout, _field)
static const struct snl_attr_parser ap_timeouts[] = {
{ .type = PF_TT_TIMEOUT, .off = 0, .cb = snl_attr_get_pf_timeout },
};
SNL_DECLARE_ATTR_PARSER(timeout_parser, ap_timeouts);
#undef _OUT
static bool
snl_attr_get_nested_timeouts(struct snl_state *ss, struct nlattr *nla,
const void *arg __unused, void *target)
{
struct nl_parsed_timeouts parsed_timeouts = { };
bool error;
/* Assumes target points to the beginning of the structure */
error = snl_parse_header(ss, NLA_DATA(nla), NLA_DATA_LEN(nla), &timeout_parser, &parsed_timeouts);
if (! error)
return (error);
memcpy(target, parsed_timeouts.timeouts, sizeof(parsed_timeouts.timeouts));
return (true);
}
#define _OUT(_field) offsetof(struct pf_rule_uid, _field)
static const struct snl_attr_parser ap_rule_uid[] = {
{ .type = PF_RUT_UID_LOW, .off = _OUT(uid[0]), .cb = snl_attr_get_uint32 },
{ .type = PF_RUT_UID_HIGH, .off = _OUT(uid[1]), .cb = snl_attr_get_uint32 },
{ .type = PF_RUT_OP, .off = _OUT(op), .cb = snl_attr_get_uint8 },
};
SNL_DECLARE_ATTR_PARSER(rule_uid_parser, ap_rule_uid);
#undef _OUT
struct pfctl_nl_get_rule {
struct pfctl_rule r;
char anchor_call[MAXPATHLEN];
};
#define _OUT(_field) offsetof(struct pfctl_nl_get_rule, _field)
static struct snl_attr_parser ap_getrule[] = {
{ .type = PF_RT_SRC, .off = _OUT(r.src), .arg = &rule_addr_parser,.cb = snl_attr_get_nested },
{ .type = PF_RT_DST, .off = _OUT(r.dst), .arg = &rule_addr_parser,.cb = snl_attr_get_nested },
{ .type = PF_RT_RIDENTIFIER, .off = _OUT(r.ridentifier), .cb = snl_attr_get_uint32 },
{ .type = PF_RT_LABELS, .off = _OUT(r.label), .arg = &rule_labels_parser,.cb = snl_attr_get_nested_pf_rule_labels },
{ .type = PF_RT_IFNAME, .off = _OUT(r.ifname), .arg = (void *)IFNAMSIZ, .cb = snl_attr_copy_string },
{ .type = PF_RT_QNAME, .off = _OUT(r.qname), .arg = (void *)PF_QNAME_SIZE, .cb = snl_attr_copy_string },
{ .type = PF_RT_PQNAME, .off = _OUT(r.pqname), .arg = (void *)PF_QNAME_SIZE, .cb = snl_attr_copy_string },
{ .type = PF_RT_TAGNAME, .off = _OUT(r.tagname), .arg = (void *)PF_TAG_NAME_SIZE, .cb = snl_attr_copy_string },
{ .type = PF_RT_MATCH_TAGNAME, .off = _OUT(r.match_tagname), .arg = (void *)PF_TAG_NAME_SIZE, .cb = snl_attr_copy_string },
{ .type = PF_RT_OVERLOAD_TBLNAME, .off = _OUT(r.overload_tblname), .arg = (void *)PF_TABLE_NAME_SIZE, .cb = snl_attr_copy_string },
{ .type = PF_RT_RPOOL, .off = _OUT(r.rpool), .arg = &pool_parser, .cb = snl_attr_get_nested },
{ .type = PF_RT_OS_FINGERPRINT, .off = _OUT(r.os_fingerprint), .cb = snl_attr_get_uint32 },
{ .type = PF_RT_RTABLEID, .off = _OUT(r.rtableid), .cb = snl_attr_get_uint32 },
{ .type = PF_RT_TIMEOUT, .off = _OUT(r.timeout), .arg = &timeout_parser, .cb = snl_attr_get_nested_timeouts },
{ .type = PF_RT_MAX_STATES, .off = _OUT(r.max_states), .cb = snl_attr_get_uint32 },
{ .type = PF_RT_MAX_SRC_NODES, .off = _OUT(r.max_src_nodes), .cb = snl_attr_get_uint32 },
{ .type = PF_RT_MAX_SRC_STATES, .off = _OUT(r.max_src_states), .cb = snl_attr_get_uint32 },
{ .type = PF_RT_MAX_SRC_CONN_RATE_LIMIT, .off = _OUT(r.max_src_conn_rate.limit), .cb = snl_attr_get_uint32 },
{ .type = PF_RT_MAX_SRC_CONN_RATE_SECS, .off = _OUT(r.max_src_conn_rate.seconds), .cb = snl_attr_get_uint32 },
{ .type = PF_RT_DNPIPE, .off = _OUT(r.dnpipe), .cb = snl_attr_get_uint16 },
{ .type = PF_RT_DNRPIPE, .off = _OUT(r.dnrpipe), .cb = snl_attr_get_uint16 },
{ .type = PF_RT_DNFLAGS, .off = _OUT(r.free_flags), .cb = snl_attr_get_uint32 },
{ .type = PF_RT_NR, .off = _OUT(r.nr), .cb = snl_attr_get_uint32 },
{ .type = PF_RT_PROB, .off = _OUT(r.prob), .cb = snl_attr_get_uint32 },
{ .type = PF_RT_CUID, .off = _OUT(r.cuid), .cb = snl_attr_get_uint32 },
{. type = PF_RT_CPID, .off = _OUT(r.cpid), .cb = snl_attr_get_uint32 },
{ .type = PF_RT_RETURN_ICMP, .off = _OUT(r.return_icmp), .cb = snl_attr_get_uint16 },
{ .type = PF_RT_RETURN_ICMP6, .off = _OUT(r.return_icmp6), .cb = snl_attr_get_uint16 },
{ .type = PF_RT_MAX_MSS, .off = _OUT(r.max_mss), .cb = snl_attr_get_uint16 },
{ .type = PF_RT_SCRUB_FLAGS, .off = _OUT(r.scrub_flags), .cb = snl_attr_get_uint16 },
{ .type = PF_RT_UID, .off = _OUT(r.uid), .arg = &rule_uid_parser, .cb = snl_attr_get_nested },
{ .type = PF_RT_GID, .off = _OUT(r.gid), .arg = &rule_uid_parser, .cb = snl_attr_get_nested },
{ .type = PF_RT_RULE_FLAG, .off = _OUT(r.rule_flag), .cb = snl_attr_get_uint32 },
{ .type = PF_RT_ACTION, .off = _OUT(r.action), .cb = snl_attr_get_uint8 },
{ .type = PF_RT_DIRECTION, .off = _OUT(r.direction), .cb = snl_attr_get_uint8 },
{ .type = PF_RT_LOG, .off = _OUT(r.log), .cb = snl_attr_get_uint8 },
{ .type = PF_RT_LOGIF, .off = _OUT(r.logif), .cb = snl_attr_get_uint8 },
{ .type = PF_RT_QUICK, .off = _OUT(r.quick), .cb = snl_attr_get_uint8 },
{ .type = PF_RT_IF_NOT, .off = _OUT(r.ifnot), .cb = snl_attr_get_uint8 },
{ .type = PF_RT_MATCH_TAG_NOT, .off = _OUT(r.match_tag_not), .cb = snl_attr_get_uint8 },
{ .type = PF_RT_NATPASS, .off = _OUT(r.natpass), .cb = snl_attr_get_uint8 },
{ .type = PF_RT_KEEP_STATE, .off = _OUT(r.keep_state), .cb = snl_attr_get_uint8 },
{ .type = PF_RT_AF, .off = _OUT(r.af), .cb = snl_attr_get_uint8 },
{ .type = PF_RT_PROTO, .off = _OUT(r.proto), .cb = snl_attr_get_uint8 },
{ .type = PF_RT_TYPE, .off = _OUT(r.type), .cb = snl_attr_get_uint8 },
{ .type = PF_RT_CODE, .off = _OUT(r.code), .cb = snl_attr_get_uint8 },
{ .type = PF_RT_FLAGS, .off = _OUT(r.flags), .cb = snl_attr_get_uint8 },
{ .type = PF_RT_FLAGSET, .off = _OUT(r.flagset), .cb = snl_attr_get_uint8 },
{ .type = PF_RT_MIN_TTL, .off = _OUT(r.min_ttl), .cb = snl_attr_get_uint8 },
{ .type = PF_RT_ALLOW_OPTS, .off = _OUT(r.allow_opts), .cb = snl_attr_get_uint8 },
{ .type = PF_RT_RT, .off = _OUT(r.rt), .cb = snl_attr_get_uint8 },
{ .type = PF_RT_RETURN_TTL, .off = _OUT(r.return_ttl), .cb = snl_attr_get_uint8 },
{ .type = PF_RT_TOS, .off = _OUT(r.tos), .cb = snl_attr_get_uint8 },
{ .type = PF_RT_SET_TOS, .off = _OUT(r.set_tos), .cb = snl_attr_get_uint8 },
{ .type = PF_RT_ANCHOR_RELATIVE, .off = _OUT(r.anchor_relative), .cb = snl_attr_get_uint8 },
{ .type = PF_RT_ANCHOR_WILDCARD, .off = _OUT(r.anchor_wildcard), .cb = snl_attr_get_uint8 },
{ .type = PF_RT_FLUSH, .off = _OUT(r.flush), .cb = snl_attr_get_uint8 },
{ .type = PF_RT_PRIO, .off = _OUT(r.prio), .cb = snl_attr_get_uint8 },
{ .type = PF_RT_SET_PRIO, .off = _OUT(r.set_prio[0]), .cb = snl_attr_get_uint8 },
{ .type = PF_RT_SET_PRIO_REPLY, .off = _OUT(r.set_prio[1]), .cb = snl_attr_get_uint8 },
{ .type = PF_RT_DIVERT_ADDRESS, .off = _OUT(r.divert.addr), .cb = snl_attr_get_in6_addr },
{ .type = PF_RT_DIVERT_PORT, .off = _OUT(r.divert.port), .cb = snl_attr_get_uint16 },
{ .type = PF_RT_PACKETS_IN, .off = _OUT(r.packets[0]), .cb = snl_attr_get_uint64 },
{ .type = PF_RT_PACKETS_OUT, .off = _OUT(r.packets[1]), .cb = snl_attr_get_uint64 },
{ .type = PF_RT_BYTES_IN, .off = _OUT(r.bytes[0]), .cb = snl_attr_get_uint64 },
{ .type = PF_RT_BYTES_OUT, .off = _OUT(r.bytes[1]), .cb = snl_attr_get_uint64 },
{ .type = PF_RT_EVALUATIONS, .off = _OUT(r.evaluations), .cb = snl_attr_get_uint64 },
{ .type = PF_RT_TIMESTAMP, .off = _OUT(r.last_active_timestamp), .cb = snl_attr_get_uint64 },
{ .type = PF_RT_STATES_CUR, .off = _OUT(r.states_cur), .cb = snl_attr_get_uint64 },
{ .type = PF_RT_STATES_TOTAL, .off = _OUT(r.states_tot), .cb = snl_attr_get_uint64 },
{ .type = PF_RT_SRC_NODES, .off = _OUT(r.src_nodes), .cb = snl_attr_get_uint64 },
{ .type = PF_RT_ANCHOR_CALL, .off = _OUT(anchor_call), .arg = (void*)MAXPATHLEN, .cb = snl_attr_copy_string },
};
static struct snl_field_parser fp_getrule[] = {};
#undef _OUT
SNL_DECLARE_PARSER(getrule_parser, struct genlmsghdr, fp_getrule, ap_getrule);
int
pfctl_get_clear_rule_h(struct pfctl_handle *h, uint32_t nr, uint32_t ticket,
const char *anchor, uint32_t ruleset, struct pfctl_rule *rule,
char *anchor_call, bool clear)
{
struct pfctl_nl_get_rule attrs = {};
struct snl_errmsg_data e = {};
struct nlmsghdr *hdr;
struct snl_writer nw;
uint32_t seq_id;
int family_id;
family_id = snl_get_genl_family(&h->ss, PFNL_FAMILY_NAME);
if (family_id == 0)
return (ENOTSUP);
snl_init_writer(&h->ss, &nw);
hdr = snl_create_genl_msg_request(&nw, family_id, PFNL_CMD_GETRULE);
hdr->nlmsg_flags |= NLM_F_DUMP;
snl_add_msg_attr_string(&nw, PF_GR_ANCHOR, anchor);
snl_add_msg_attr_u8(&nw, PF_GR_ACTION, ruleset);
snl_add_msg_attr_u32(&nw, PF_GR_NR, nr);
snl_add_msg_attr_u32(&nw, PF_GR_TICKET, ticket);
snl_add_msg_attr_u8(&nw, PF_GR_CLEAR, clear);
hdr = snl_finalize_msg(&nw);
if (hdr == NULL)
return (ENOMEM);
seq_id = hdr->nlmsg_seq;
if (! snl_send_message(&h->ss, hdr))
return (ENXIO);
while ((hdr = snl_read_reply_multi(&h->ss, seq_id, &e)) != NULL) {
if (! snl_parse_nlmsg(&h->ss, hdr, &getrule_parser, &attrs))
continue;
}
memcpy(rule, &attrs.r, sizeof(attrs.r));
strlcpy(anchor_call, attrs.anchor_call, MAXPATHLEN);
return (e.error);
}
int
pfctl_get_clear_rule(int dev, uint32_t nr, uint32_t ticket,
const char *anchor, uint32_t ruleset, struct pfctl_rule *rule,
char *anchor_call, bool clear)
{

View file

@ -418,6 +418,9 @@ int pfctl_get_rule(int dev, uint32_t nr, uint32_t ticket,
int pfctl_get_clear_rule(int dev, uint32_t nr, uint32_t ticket,
const char *anchor, uint32_t ruleset, struct pfctl_rule *rule,
char *anchor_call, bool clear);
int pfctl_get_clear_rule_h(struct pfctl_handle *h, uint32_t nr, uint32_t ticket,
const char *anchor, uint32_t ruleset, struct pfctl_rule *rule,
char *anchor_call, bool clear);
int pfctl_add_rule(int dev, const struct pfctl_rule *r,
const char *anchor, const char *anchor_call, uint32_t ticket,
uint32_t pool_ticket);

View file

@ -1303,7 +1303,7 @@ pfctl_show_rules(int dev, char *path, int opts, enum pfctl_show format,
}
for (nr = 0; nr < ri.nr; ++nr) {
if (pfctl_get_clear_rule(dev, nr, ri.ticket, path, PF_SCRUB,
if (pfctl_get_clear_rule_h(pfh, nr, ri.ticket, path, PF_SCRUB,
&rule, anchor_call, opts & PF_OPT_CLRRULECTRS)) {
warn("DIOCGETRULENV");
goto error;
@ -1334,7 +1334,7 @@ pfctl_show_rules(int dev, char *path, int opts, enum pfctl_show format,
goto error;
}
for (nr = 0; nr < ri.nr; ++nr) {
if (pfctl_get_clear_rule(dev, nr, ri.ticket, path, PF_PASS,
if (pfctl_get_clear_rule_h(pfh, nr, ri.ticket, path, PF_PASS,
&rule, anchor_call, opts & PF_OPT_CLRRULECTRS)) {
warn("DIOCGETRULE");
goto error;

View file

@ -2470,10 +2470,10 @@ void pf_init_kruleset(struct pf_kruleset *);
void pf_init_keth(struct pf_keth_ruleset *);
int pf_kanchor_setup(struct pf_krule *,
const struct pf_kruleset *, const char *);
int pf_kanchor_copyout(const struct pf_kruleset *,
const struct pf_krule *, char *);
int pf_kanchor_nvcopyout(const struct pf_kruleset *,
const struct pf_krule *, nvlist_t *);
int pf_kanchor_copyout(const struct pf_kruleset *,
const struct pf_krule *, struct pfioc_rule *);
void pf_kanchor_remove(struct pf_krule *);
void pf_remove_if_empty_kruleset(struct pf_kruleset *);
struct pf_kruleset *pf_find_kruleset(const char *);
@ -2501,6 +2501,7 @@ int pf_ioctl_addrule(struct pf_krule *, uint32_t,
pid_t);
void pf_krule_free(struct pf_krule *);
void pf_krule_clear_counters(struct pf_krule *);
#endif
/* The fingerprint functions can be linked into userland programs (tcpdump) */

View file

@ -513,6 +513,18 @@ snl_attr_get_flag(struct snl_state *ss __unused, struct nlattr *nla, const void
return (false);
}
static inline bool
snl_attr_get_bytes(struct snl_state *ss __unused, struct nlattr *nla, const void *arg,
void *target)
{
if ((size_t)NLA_DATA_LEN(nla) != (size_t)arg)
return (false);
memcpy(target, NLA_DATA_CONST(nla), (size_t)arg);
return (true);
}
static inline bool
snl_attr_get_uint8(struct snl_state *ss __unused, struct nlattr *nla,
const void *arg __unused, void *target)

View file

@ -1867,6 +1867,17 @@ pf_krule_free(struct pf_krule *rule)
free(rule, M_PFRULE);
}
void
pf_krule_clear_counters(struct pf_krule *rule)
{
pf_counter_u64_zero(&rule->evaluations);
for (int i = 0; i < 2; i++) {
pf_counter_u64_zero(&rule->packets[i]);
pf_counter_u64_zero(&rule->bytes[i]);
}
counter_u64_zero(rule->states_tot);
}
static void
pf_kpooladdr_to_pooladdr(const struct pf_kpooladdr *kpool,
struct pf_pooladdr *pool)
@ -3266,14 +3277,9 @@ pfioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flags, struct thread *td
ERROUT(ENOSPC);
}
if (clear_counter) {
pf_counter_u64_zero(&rule->evaluations);
for (int i = 0; i < 2; i++) {
pf_counter_u64_zero(&rule->packets[i]);
pf_counter_u64_zero(&rule->bytes[i]);
}
counter_u64_zero(rule->states_tot);
}
if (clear_counter)
pf_krule_clear_counters(rule);
PF_RULES_WUNLOCK();
error = copyout(nvlpacked, nv->data, nv->len);

View file

@ -401,6 +401,42 @@ static const struct nlattr_parser nla_p_addr_wrap[] = {
NL_DECLARE_ATTR_PARSER(addr_wrap_parser, nla_p_addr_wrap);
#undef _OUT
static bool
nlattr_add_addr_wrap(struct nl_writer *nw, int attrtype, struct pf_addr_wrap *a)
{
int off = nlattr_add_nested(nw, attrtype);
int num;
nlattr_add_in6_addr(nw, PF_AT_ADDR, &a->v.a.addr.v6);
nlattr_add_in6_addr(nw, PF_AT_MASK, &a->v.a.mask.v6);
nlattr_add_u8(nw, PF_AT_TYPE, a->type);
nlattr_add_u8(nw, PF_AT_IFLAGS, a->iflags);
if (a->type == PF_ADDR_DYNIFTL) {
nlattr_add_string(nw, PF_AT_IFNAME, a->v.ifname);
num = 0;
if (a->p.dyn != NULL)
num = a->p.dyn->pfid_acnt4 + a->p.dyn->pfid_acnt6;
nlattr_add_u32(nw, PF_AT_DYNCNT, num);
} else if (a->type == PF_ADDR_TABLE) {
struct pfr_ktable *kt;
nlattr_add_string(nw, PF_AT_TABLENAME, a->v.tblname);
num = -1;
kt = a->p.tbl;
if ((kt->pfrkt_flags & PFR_TFLAG_ACTIVE) &&
kt->pfrkt_root != NULL)
kt = kt->pfrkt_root;
if (kt->pfrkt_flags & PFR_TFLAG_ACTIVE)
num = kt->pfrkt_cnt;
nlattr_add_u32(nw, PF_AT_TBLCNT, num);
}
nlattr_set_len(nw, off);
return (true);
}
#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 },
@ -412,6 +448,22 @@ static const struct nlattr_parser nla_p_ruleaddr[] = {
NL_DECLARE_ATTR_PARSER(rule_addr_parser, nla_p_ruleaddr);
#undef _OUT
static bool
nlattr_add_rule_addr(struct nl_writer *nw, int attrtype, struct pf_rule_addr *r)
{
int off = nlattr_add_nested(nw, attrtype);
nlattr_add_addr_wrap(nw, PF_RAT_ADDR, &r->addr);
nlattr_add_u16(nw, PF_RAT_SRC_PORT, r->port[0]);
nlattr_add_u16(nw, PF_RAT_DST_PORT, r->port[1]);
nlattr_add_u8(nw, PF_RAT_NEG, r->neg);
nlattr_add_u8(nw, PF_RAT_OP, r->port_op);
nlattr_set_len(nw, off);
return (true);
}
#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 },
@ -421,6 +473,20 @@ static const struct nlattr_parser nla_p_mape_portset[] = {
NL_DECLARE_ATTR_PARSER(mape_portset_parser, nla_p_mape_portset);
#undef _OUT
static bool
nlattr_add_mape_portset(struct nl_writer *nw, int attrtype, const struct pf_mape_portset *m)
{
int off = nlattr_add_nested(nw, attrtype);
nlattr_add_u8(nw, PF_MET_OFFSET, m->offset);
nlattr_add_u8(nw, PF_MET_PSID_LEN, m->psidlen);
nlattr_add_u16(nw, PF_MET_PSID, m->psid);
nlattr_set_len(nw, off);
return (true);
}
struct nl_parsed_labels
{
char labels[PF_RULE_MAX_LABEL_COUNT][PF_RULE_LABEL_SIZE];
@ -468,6 +534,23 @@ nlattr_get_nested_pf_rule_labels(struct nlattr *nla, struct nl_pstate *npt, cons
return (0);
}
static bool
nlattr_add_labels(struct nl_writer *nw, int attrtype, const struct pf_krule *r)
{
int off = nlattr_add_nested(nw, attrtype);
int i = 0;
while (r->label[i][0] != 0
&& i < PF_RULE_MAX_LABEL_COUNT) {
nlattr_add_string(nw, PF_LT_LABEL, r->label[i]);
i++;
}
nlattr_set_len(nw, off);
return (true);
}
#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 },
@ -481,6 +564,24 @@ static const struct nlattr_parser nla_p_pool[] = {
NL_DECLARE_ATTR_PARSER(pool_parser, nla_p_pool);
#undef _OUT
static bool
nlattr_add_pool(struct nl_writer *nw, int attrtype, const struct pf_kpool *pool)
{
int off = nlattr_add_nested(nw, attrtype);
nlattr_add(nw, PF_PT_KEY, sizeof(struct pf_poolhashkey), &pool->key);
nlattr_add_in6_addr(nw, PF_PT_COUNTER, (const struct in6_addr *)&pool->counter);
nlattr_add_u32(nw, PF_PT_TBLIDX, pool->tblidx);
nlattr_add_u16(nw, PF_PT_PROXY_SRC_PORT, pool->proxy_port[0]);
nlattr_add_u16(nw, PF_PT_PROXY_DST_PORT, pool->proxy_port[1]);
nlattr_add_u8(nw, PF_PT_OPTS, pool->opts);
nlattr_add_mape_portset(nw, PF_PT_MAPE, &pool->mape);
nlattr_set_len(nw, off);
return (true);
}
#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 },
@ -490,6 +591,20 @@ static const struct nlattr_parser nla_p_rule_uid[] = {
NL_DECLARE_ATTR_PARSER(rule_uid_parser, nla_p_rule_uid);
#undef _OUT
static bool
nlattr_add_rule_uid(struct nl_writer *nw, int attrtype, const struct pf_rule_uid *u)
{
int off = nlattr_add_nested(nw, attrtype);
nlattr_add_u32(nw, PF_RUT_UID_LOW, u->uid[0]);
nlattr_add_u32(nw, PF_RUT_UID_HIGH, u->uid[1]);
nlattr_add_u8(nw, PF_RUT_OP, u->op);
nlattr_set_len(nw, off);
return (true);
}
struct nl_parsed_timeouts
{
uint32_t timeouts[PFTM_MAX];
@ -536,6 +651,19 @@ nlattr_get_nested_timeouts(struct nlattr *nla, struct nl_pstate *npt, const void
return (0);
}
static bool
nlattr_add_timeout(struct nl_writer *nw, int attrtype, uint32_t *timeout)
{
int off = nlattr_add_nested(nw, attrtype);
for (int i = 0; i < PFTM_MAX; i++)
nlattr_add_u32(nw, PF_RT_TIMEOUT, timeout[i]);
nlattr_set_len(nw, off);
return (true);
}
#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 },
@ -654,6 +782,7 @@ static const struct nlattr_parser nla_p_getrules[] = {
};
static const struct nlfield_parser nlf_p_getrules[] = {
};
#undef _OUT
NL_DECLARE_PARSER(getrules_parser, struct genlmsghdr, nlf_p_getrules, nla_p_getrules);
static int
@ -695,6 +824,182 @@ pf_handle_getrules(struct nlmsghdr *hdr, struct nl_pstate *npt)
return (error);
}
struct nl_parsed_get_rule {
char anchor[MAXPATHLEN];
uint8_t action;
uint32_t nr;
uint32_t ticket;
uint8_t clear;
};
#define _IN(_field) offsetof(struct genlmsghdr, _field)
#define _OUT(_field) offsetof(struct nl_parsed_get_rule, _field)
static const struct nlattr_parser nla_p_getrule[] = {
{ .type = PF_GR_ANCHOR, .off = _OUT(anchor), .arg = (void *)MAXPATHLEN, .cb = nlattr_get_chara },
{ .type = PF_GR_ACTION, .off = _OUT(action), .cb = nlattr_get_uint8 },
{ .type = PF_GR_NR, .off = _OUT(nr), .cb = nlattr_get_uint32 },
{ .type = PF_GR_TICKET, .off = _OUT(ticket), .cb = nlattr_get_uint32 },
{ .type = PF_GR_CLEAR, .off = _OUT(clear), .cb = nlattr_get_uint8 },
};
static const struct nlfield_parser nlf_p_getrule[] = {
};
NL_DECLARE_PARSER(getrule_parser, struct genlmsghdr, nlf_p_getrule, nla_p_getrule);
static int
pf_handle_getrule(struct nlmsghdr *hdr, struct nl_pstate *npt)
{
char anchor_call[MAXPATHLEN];
struct nl_parsed_get_rule attrs = {};
struct nl_writer *nw = npt->nw;
struct genlmsghdr *ghdr_new;
struct pf_kruleset *ruleset;
struct pf_krule *rule;
int rs_num;
int error;
error = nl_parse_nlmsg(hdr, &getrule_parser, npt, &attrs);
if (error != 0)
return (error);
if (!nlmsg_reply(nw, hdr, sizeof(struct genlmsghdr)))
return (ENOMEM);
ghdr_new = nlmsg_reserve_object(nw, struct genlmsghdr);
ghdr_new->cmd = PFNL_CMD_GETRULE;
ghdr_new->version = 0;
ghdr_new->reserved = 0;
PF_RULES_WLOCK();
ruleset = pf_find_kruleset(attrs.anchor);
if (ruleset == NULL) {
PF_RULES_WUNLOCK();
error = ENOENT;
goto out;
}
rs_num = pf_get_ruleset_number(attrs.action);
if (rs_num >= PF_RULESET_MAX) {
PF_RULES_WUNLOCK();
error = EINVAL;
goto out;
}
if (attrs.ticket != ruleset->rules[rs_num].active.ticket) {
PF_RULES_WUNLOCK();
error = EBUSY;
goto out;
}
rule = TAILQ_FIRST(ruleset->rules[rs_num].active.ptr);
while ((rule != NULL) && (rule->nr != attrs.nr))
rule = TAILQ_NEXT(rule, entries);
if (rule == NULL) {
PF_RULES_WUNLOCK();
error = EBUSY;
goto out;
}
nlattr_add_rule_addr(nw, PF_RT_SRC, &rule->src);
nlattr_add_rule_addr(nw, PF_RT_DST, &rule->dst);
nlattr_add_u32(nw, PF_RT_RIDENTIFIER, rule->ridentifier);
nlattr_add_labels(nw, PF_RT_LABELS, rule);
nlattr_add_string(nw, PF_RT_IFNAME, rule->ifname);
nlattr_add_string(nw, PF_RT_QNAME, rule->qname);
nlattr_add_string(nw, PF_RT_PQNAME, rule->pqname);
nlattr_add_string(nw, PF_RT_TAGNAME, rule->tagname);
nlattr_add_string(nw, PF_RT_MATCH_TAGNAME, rule->match_tagname);
nlattr_add_string(nw, PF_RT_OVERLOAD_TBLNAME, rule->overload_tblname);
nlattr_add_pool(nw, PF_RT_RPOOL, &rule->rpool);
nlattr_add_u32(nw, PF_RT_OS_FINGERPRINT, rule->os_fingerprint);
nlattr_add_u32(nw, PF_RT_RTABLEID, rule->rtableid);
nlattr_add_timeout(nw, PF_RT_TIMEOUT, rule->timeout);
nlattr_add_u32(nw, PF_RT_MAX_STATES, rule->max_states);
nlattr_add_u32(nw, PF_RT_MAX_SRC_NODES, rule->max_src_nodes);
nlattr_add_u32(nw, PF_RT_MAX_SRC_STATES, rule->max_src_states);
nlattr_add_u32(nw, PF_RT_MAX_SRC_CONN_RATE_LIMIT, rule->max_src_conn_rate.limit);
nlattr_add_u32(nw, PF_RT_MAX_SRC_CONN_RATE_SECS, rule->max_src_conn_rate.seconds);
nlattr_add_u16(nw, PF_RT_DNPIPE, rule->dnpipe);
nlattr_add_u16(nw, PF_RT_DNRPIPE, rule->dnrpipe);
nlattr_add_u32(nw, PF_RT_DNFLAGS, rule->free_flags);
nlattr_add_u32(nw, PF_RT_NR, rule->nr);
nlattr_add_u32(nw, PF_RT_PROB, rule->prob);
nlattr_add_u32(nw, PF_RT_CUID, rule->cuid);
nlattr_add_u32(nw, PF_RT_CPID, rule->cpid);
nlattr_add_u16(nw, PF_RT_RETURN_ICMP, rule->return_icmp);
nlattr_add_u16(nw, PF_RT_RETURN_ICMP6, rule->return_icmp6);
nlattr_add_u16(nw, PF_RT_RETURN_ICMP6, rule->return_icmp6);
nlattr_add_u16(nw, PF_RT_MAX_MSS, rule->max_mss);
nlattr_add_u16(nw, PF_RT_SCRUB_FLAGS, rule->scrub_flags);
nlattr_add_rule_uid(nw, PF_RT_UID, &rule->uid);
nlattr_add_rule_uid(nw, PF_RT_GID, (const struct pf_rule_uid *)&rule->gid);
nlattr_add_u32(nw, PF_RT_RULE_FLAG, rule->rule_flag);
nlattr_add_u8(nw, PF_RT_ACTION, rule->action);
nlattr_add_u8(nw, PF_RT_DIRECTION, rule->direction);
nlattr_add_u8(nw, PF_RT_LOG, rule->log);
nlattr_add_u8(nw, PF_RT_LOGIF, rule->logif);
nlattr_add_u8(nw, PF_RT_QUICK, rule->quick);
nlattr_add_u8(nw, PF_RT_IF_NOT, rule->ifnot);
nlattr_add_u8(nw, PF_RT_MATCH_TAG_NOT, rule->match_tag_not);
nlattr_add_u8(nw, PF_RT_NATPASS, rule->natpass);
nlattr_add_u8(nw, PF_RT_KEEP_STATE, rule->keep_state);
nlattr_add_u8(nw, PF_RT_AF, rule->af);
nlattr_add_u8(nw, PF_RT_PROTO, rule->proto);
nlattr_add_u8(nw, PF_RT_TYPE, rule->type);
nlattr_add_u8(nw, PF_RT_CODE, rule->code);
nlattr_add_u8(nw, PF_RT_FLAGS, rule->flags);
nlattr_add_u8(nw, PF_RT_FLAGSET, rule->flagset);
nlattr_add_u8(nw, PF_RT_MIN_TTL, rule->min_ttl);
nlattr_add_u8(nw, PF_RT_ALLOW_OPTS, rule->allow_opts);
nlattr_add_u8(nw, PF_RT_RT, rule->rt);
nlattr_add_u8(nw, PF_RT_RETURN_TTL, rule->return_ttl);
nlattr_add_u8(nw, PF_RT_TOS, rule->tos);
nlattr_add_u8(nw, PF_RT_SET_TOS, rule->set_tos);
nlattr_add_u8(nw, PF_RT_ANCHOR_RELATIVE, rule->anchor_relative);
nlattr_add_u8(nw, PF_RT_ANCHOR_WILDCARD, rule->anchor_wildcard);
nlattr_add_u8(nw, PF_RT_FLUSH, rule->flush);
nlattr_add_u8(nw, PF_RT_PRIO, rule->prio);
nlattr_add_u8(nw, PF_RT_SET_PRIO, rule->set_prio[0]);
nlattr_add_u8(nw, PF_RT_SET_PRIO_REPLY, rule->set_prio[1]);
nlattr_add_in6_addr(nw, PF_RT_DIVERT_ADDRESS, &rule->divert.addr.v6);
nlattr_add_u16(nw, PF_RT_DIVERT_PORT, rule->divert.port);
nlattr_add_u64(nw, PF_RT_PACKETS_IN, pf_counter_u64_fetch(&rule->packets[0]));
nlattr_add_u64(nw, PF_RT_PACKETS_OUT, pf_counter_u64_fetch(&rule->packets[1]));
nlattr_add_u64(nw, PF_RT_BYTES_IN, pf_counter_u64_fetch(&rule->bytes[0]));
nlattr_add_u64(nw, PF_RT_BYTES_OUT, pf_counter_u64_fetch(&rule->bytes[1]));
nlattr_add_u64(nw, PF_RT_EVALUATIONS, pf_counter_u64_fetch(&rule->evaluations));
nlattr_add_u64(nw, PF_RT_TIMESTAMP, pf_get_timestamp(rule));
nlattr_add_u64(nw, PF_RT_STATES_CUR, counter_u64_fetch(rule->states_cur));
nlattr_add_u64(nw, PF_RT_STATES_TOTAL, counter_u64_fetch(rule->states_tot));
nlattr_add_u64(nw, PF_RT_SRC_NODES, counter_u64_fetch(rule->src_nodes));
error = pf_kanchor_copyout(ruleset, rule, anchor_call);
MPASS(error == 0);
nlattr_add_string(nw, PF_RT_ANCHOR_CALL, anchor_call);
if (attrs.clear)
pf_krule_clear_counters(rule);
PF_RULES_WUNLOCK();
if (!nlmsg_end(nw)) {
error = ENOMEM;
goto out;
}
return (0);
out:
nlmsg_abort(nw);
return (error);
}
static const struct nlhdr_parser *all_parsers[] = {
&state_parser,
&addrule_parser,
@ -746,6 +1051,13 @@ static const struct genl_cmd pf_cmds[] = {
.cmd_flags = GENL_CMD_CAP_DUMP | GENL_CMD_CAP_HASPOL,
.cmd_priv = PRIV_NETINET_PF,
},
{
.cmd_num = PFNL_CMD_GETRULE,
.cmd_name = "GETRULE",
.cmd_cb = pf_handle_getrule,
.cmd_flags = GENL_CMD_CAP_DUMP | GENL_CMD_CAP_HASPOL,
.cmd_priv = PRIV_NETINET_PF,
},
};
void

View file

@ -42,6 +42,7 @@ enum {
PFNL_CMD_STOP = 4,
PFNL_CMD_ADDRULE = 5,
PFNL_CMD_GETRULES = 6,
PFNL_CMD_GETRULE = 7,
__PFNL_CMD_MAX,
};
#define PFNL_CMD_MAX (__PFNL_CMD_MAX -1)
@ -117,6 +118,8 @@ enum pf_addr_type_t {
PF_AT_TABLENAME = 4, /* string */
PF_AT_TYPE = 5, /* u8 */
PF_AT_IFLAGS = 6, /* u8 */
PF_AT_TBLCNT = 7, /* u32 */
PF_AT_DYNCNT = 8, /* u32 */
};
enum pfrule_addr_type_t {
@ -229,6 +232,16 @@ enum pf_rule_type_t {
PF_RT_SET_PRIO_REPLY = 60, /* u8 */
PF_RT_DIVERT_ADDRESS = 61, /* in6_addr */
PF_RT_DIVERT_PORT = 62, /* u16 */
PF_RT_PACKETS_IN = 63, /* u64 */
PF_RT_PACKETS_OUT = 64, /* u64 */
PF_RT_BYTES_IN = 65, /* u64 */
PF_RT_BYTES_OUT = 66, /* u64 */
PF_RT_EVALUATIONS = 67, /* u64 */
PF_RT_TIMESTAMP = 68, /* u64 */
PF_RT_STATES_CUR = 69, /* u64 */
PF_RT_STATES_TOTAL = 70, /* u64 */
PF_RT_SRC_NODES = 71, /* u64 */
PF_RT_ANCHOR_CALL = 72, /* string */
};
enum pf_addrule_type_t {
@ -246,6 +259,7 @@ enum pf_getrules_type_t {
PF_GR_ACTION = 2, /* u8 */
PF_GR_NR = 3, /* u32 */
PF_GR_TICKET = 4, /* u32 */
PF_GR_CLEAR = 5, /* u8 */
};
#ifdef _KERNEL

View file

@ -367,10 +367,10 @@ pf_kanchor_setup(struct pf_krule *r, const struct pf_kruleset *s,
}
int
pf_kanchor_nvcopyout(const struct pf_kruleset *rs, const struct pf_krule *r,
nvlist_t *nvl)
pf_kanchor_copyout(const struct pf_kruleset *rs, const struct pf_krule *r,
char *anchor_call)
{
char anchor_call[MAXPATHLEN] = { 0 };
anchor_call[0] = 0;
if (r->anchor == NULL)
goto done;
@ -408,11 +408,25 @@ pf_kanchor_nvcopyout(const struct pf_kruleset *rs, const struct pf_krule *r,
sizeof(anchor_call));
done:
nvlist_add_string(nvl, "anchor_call", anchor_call);
return (0);
}
int
pf_kanchor_nvcopyout(const struct pf_kruleset *rs, const struct pf_krule *r,
nvlist_t *nvl)
{
char anchor_call[MAXPATHLEN] = { 0 };
int ret;
ret = pf_kanchor_copyout(rs, r, anchor_call);
MPASS(ret == 0);
nvlist_add_string(nvl, "anchor_call", anchor_call);
return (ret);
}
int
pf_keth_anchor_nvcopyout(const struct pf_keth_ruleset *rs,
const struct pf_keth_rule *r, nvlist_t *nvl)
@ -460,52 +474,6 @@ pf_keth_anchor_nvcopyout(const struct pf_keth_ruleset *rs,
return (0);
}
int
pf_kanchor_copyout(const struct pf_kruleset *rs, const struct pf_krule *r,
struct pfioc_rule *pr)
{
pr->anchor_call[0] = 0;
if (r->anchor == NULL)
return (0);
if (!r->anchor_relative) {
strlcpy(pr->anchor_call, "/", sizeof(pr->anchor_call));
strlcat(pr->anchor_call, r->anchor->path,
sizeof(pr->anchor_call));
} else {
char *a, *p;
int i;
a = (char *)rs_malloc(MAXPATHLEN);
if (a == NULL)
return (1);
if (rs->anchor == NULL)
a[0] = 0;
else
strlcpy(a, rs->anchor->path, MAXPATHLEN);
for (i = 1; i < r->anchor_relative; ++i) {
if ((p = strrchr(a, '/')) == NULL)
p = a;
*p = 0;
strlcat(pr->anchor_call, "../",
sizeof(pr->anchor_call));
}
if (strncmp(a, r->anchor->path, strlen(a))) {
printf("pf_anchor_copyout: '%s' '%s'\n", a,
r->anchor->path);
rs_free(a);
return (1);
}
if (strlen(r->anchor->path) > strlen(a))
strlcat(pr->anchor_call, r->anchor->path + (a[0] ?
strlen(a) + 1 : 0), sizeof(pr->anchor_call));
rs_free(a);
}
if (r->anchor_wildcard)
strlcat(pr->anchor_call, pr->anchor_call[0] ? "/*" : "*",
sizeof(pr->anchor_call));
return (0);
}
void
pf_kanchor_remove(struct pf_krule *r)
{