pf: convert state limiter interface to netlink

This is a new feature with new ioctl calls, so we can safely remove them
right now.

Sponsored by:	Rubicon Communications, LLC ("Netgate")
This commit is contained in:
Kristof Provost
2026-01-06 22:33:31 +01:00
parent 4616481212
commit c72fb110e4
9 changed files with 1035 additions and 336 deletions
+43 -65
View File
@@ -1205,6 +1205,11 @@ struct pf_kstate {
* State limiter
*/
struct pf_limiter_rate {
unsigned int limit;
unsigned int seconds;
};
struct pf_statelim {
RB_ENTRY(pf_statelim) pfstlim_id_tree;
RB_ENTRY(pf_statelim) pfstlim_nm_tree;
@@ -1217,10 +1222,7 @@ struct pf_statelim {
/* config */
unsigned int pfstlim_limit;
struct {
unsigned int limit;
unsigned int seconds;
} pfstlim_rate;
struct pf_limiter_rate pfstlim_rate;
/* run state */
struct mtx pfstlim_lock;
@@ -1340,10 +1342,7 @@ struct pf_sourcelim {
unsigned int pfsrlim_ipv4_prefix;
unsigned int pfsrlim_ipv6_prefix;
struct {
unsigned int limit;
unsigned int seconds;
} pfsrlim_rate;
struct pf_limiter_rate pfsrlim_rate;
struct {
char name[PF_TABLE_NAME_SIZE];
@@ -2074,25 +2073,29 @@ enum pf_syncookies_mode {
#define PF_SYNCOOKIES_HIWATPCT 25
#define PF_SYNCOOKIES_LOWATPCT (PF_SYNCOOKIES_HIWATPCT / 2)
#define PF_STATELIM_ID_NONE 0
#define PF_STATELIM_ID_MIN 1
#define PF_STATELIM_ID_MAX 255 /* fits in pf_state uint8_t */
#define PF_STATELIM_LIMIT_MIN 1
#define PF_STATELIM_LIMIT_MAX (1 << 24) /* pf is pretty scalable */
#define PF_SOURCELIM_ID_NONE 0
#define PF_SOURCELIM_ID_MIN 1
#define PF_SOURCELIM_ID_MAX 255 /* fits in pf_state uint8_t */
#ifdef _KERNEL
struct pfioc_statelim {
uint32_t ticket;
char name[PF_STATELIM_NAME_LEN];
uint32_t id;
#define PF_STATELIM_ID_NONE 0
#define PF_STATELIM_ID_MIN 1
#define PF_STATELIM_ID_MAX 255 /* fits in pf_state uint8_t */
/* limit on the total number of states */
unsigned int limit;
#define PF_STATELIM_LIMIT_MIN 1
#define PF_STATELIM_LIMIT_MAX (1 << 24) /* pf is pretty scalable */
/* rate limit on the creation of states */
struct {
unsigned int limit;
unsigned int seconds;
} rate;
struct pf_limiter_rate rate;
char description[PF_STATELIM_DESCR_LEN];
@@ -2108,9 +2111,6 @@ struct pfioc_sourcelim {
char name[PF_SOURCELIM_NAME_LEN];
uint32_t id;
#define PF_SOURCELIM_ID_NONE 0
#define PF_SOURCELIM_ID_MIN 1
#define PF_SOURCELIM_ID_MAX 255 /* fits in pf_state uint8_t */
/* limit on the total number of address entries */
unsigned int entries;
@@ -2119,10 +2119,7 @@ struct pfioc_sourcelim {
unsigned int limit;
/* rate limit on the creation of states by an address entry */
struct {
unsigned int limit;
unsigned int seconds;
} rate;
struct pf_limiter_rate rate;
/*
* when the number of states on an entry exceeds hwm, add
@@ -2154,37 +2151,6 @@ struct pfioc_sourcelim {
uint64_t ratelimited; /* counter */
};
struct pfioc_source_entry {
sa_family_t af;
unsigned int rdomain;
struct pf_addr addr;
/* stats */
unsigned int inuse; /* gauge */
uint64_t admitted; /* counter */
uint64_t hardlimited; /* counter */
uint64_t ratelimited; /* counter */
};
struct pfioc_source {
char name[PF_SOURCELIM_NAME_LEN];
uint32_t id;
/* copied from the parent source limiter */
unsigned int inet_prefix;
unsigned int inet6_prefix;
unsigned int limit;
/* source entries */
size_t entry_size; /* sizeof(struct pfioc_source_entry) */
struct pfioc_source_entry *key;
struct pfioc_source_entry *entries;
size_t entrieslen; /* bytes */
};
struct pfioc_source_kill {
char name[PF_SOURCELIM_NAME_LEN];
uint32_t id;
@@ -2195,7 +2161,28 @@ struct pfioc_source_kill {
unsigned int rmstates; /* kill the states too? */
};
#ifdef _KERNEL
int pf_statelim_add(const struct pfioc_statelim *);
struct pf_statelim *pf_statelim_rb_find(struct pf_statelim_id_tree *,
struct pf_statelim *);
struct pf_statelim *pf_statelim_rb_nfind(struct pf_statelim_id_tree *,
struct pf_statelim *);
int pf_statelim_get(struct pfioc_statelim *,
struct pf_statelim *(*rbt_op)(struct pf_statelim_id_tree *,
struct pf_statelim *));
int pf_sourcelim_add(const struct pfioc_sourcelim *);
struct pf_sourcelim *pf_sourcelim_rb_find(struct pf_sourcelim_id_tree *,
struct pf_sourcelim *);
struct pf_sourcelim *pf_sourcelim_rb_nfind(struct pf_sourcelim_id_tree *,
struct pf_sourcelim *);
int pf_sourcelim_get(struct pfioc_sourcelim *,
struct pf_sourcelim *(*rbt_op)(struct pf_sourcelim_id_tree *,
struct pf_sourcelim *));
struct pf_source *pf_source_rb_find(struct pf_source_ioc_tree *,
struct pf_source *);
struct pf_source *pf_source_rb_nfind(struct pf_source_ioc_tree *,
struct pf_source *);
int pf_source_clr(struct pfioc_source_kill *);
struct pf_kstatus {
counter_u64_t counters[PFRES_MAX]; /* reason for passing/dropping */
counter_u64_t lcounters[KLCNT_MAX]; /* limit counters */
@@ -2569,15 +2556,6 @@ struct pfioc_iface {
#define DIOCGETETHRULESETS _IOWR('D', 100, struct pfioc_nv)
#define DIOCGETETHRULESET _IOWR('D', 101, struct pfioc_nv)
#define DIOCSETREASS _IOWR('D', 102, u_int32_t)
#define DIOCADDSTATELIM _IOW('D', 103, struct pfioc_statelim)
#define DIOCADDSOURCELIM _IOW('D', 104, struct pfioc_sourcelim)
#define DIOCGETSTATELIM _IOWR('D', 105, struct pfioc_statelim)
#define DIOCGETSOURCELIM _IOWR('D', 106, struct pfioc_sourcelim)
#define DIOCGETSOURCE _IOWR('D', 107, struct pfioc_source)
#define DIOCGETNSTATELIM _IOWR('D', 108, struct pfioc_statelim)
#define DIOCGETNSOURCELIM _IOWR('D', 109, struct pfioc_sourcelim)
#define DIOCGETNSOURCE _IOWR('D', 110, struct pfioc_source)
#define DIOCCLRSOURCE _IOWR('D', 111, struct pfioc_source_kill)
struct pf_ifspeed_v0 {
char ifname[IFNAMSIZ];
+11 -152
View File
@@ -1613,7 +1613,7 @@ pf_addr_copyout(struct pf_addr_wrap *addr)
}
}
static int
int
pf_statelim_add(const struct pfioc_statelim *ioc)
{
struct pf_statelim *pfstlim;
@@ -1976,7 +1976,7 @@ pf_statelim_rollback(void)
RB_INIT(&V_pf_statelim_nm_tree_inactive);
}
static struct pf_statelim *
struct pf_statelim *
pf_statelim_rb_find(struct pf_statelim_id_tree *tree, struct pf_statelim *key)
{
PF_RULES_ASSERT();
@@ -1984,7 +1984,7 @@ pf_statelim_rb_find(struct pf_statelim_id_tree *tree, struct pf_statelim *key)
return (RB_FIND(pf_statelim_id_tree, tree, key));
}
static struct pf_statelim *
struct pf_statelim *
pf_statelim_rb_nfind(struct pf_statelim_id_tree *tree, struct pf_statelim *key)
{
PF_RULES_ASSERT();
@@ -1992,7 +1992,7 @@ pf_statelim_rb_nfind(struct pf_statelim_id_tree *tree, struct pf_statelim *key)
return (RB_NFIND(pf_statelim_id_tree, tree, key));
}
static int
int
pf_statelim_get(struct pfioc_statelim *ioc,
struct pf_statelim *(*rbt_op)(struct pf_statelim_id_tree *,
struct pf_statelim *))
@@ -2028,7 +2028,7 @@ pf_statelim_get(struct pfioc_statelim *ioc,
return (error);
}
static int
int
pf_sourcelim_add(const struct pfioc_sourcelim *ioc)
{
struct pf_sourcelim *pfsrlim;
@@ -2187,7 +2187,7 @@ pf_sourcelim_rollback(void)
RB_INIT(&V_pf_sourcelim_nm_tree_inactive);
}
static struct pf_sourcelim *
struct pf_sourcelim *
pf_sourcelim_rb_find(struct pf_sourcelim_id_tree *tree,
struct pf_sourcelim *key)
{
@@ -2195,7 +2195,7 @@ pf_sourcelim_rb_find(struct pf_sourcelim_id_tree *tree,
return (RB_FIND(pf_sourcelim_id_tree, tree, key));
}
static struct pf_sourcelim *
struct pf_sourcelim *
pf_sourcelim_rb_nfind(struct pf_sourcelim_id_tree *tree,
struct pf_sourcelim *key)
{
@@ -2203,7 +2203,7 @@ pf_sourcelim_rb_nfind(struct pf_sourcelim_id_tree *tree,
return (RB_NFIND(pf_sourcelim_id_tree, tree, key));
}
static int
int
pf_sourcelim_get(struct pfioc_sourcelim *ioc,
struct pf_sourcelim *(*rbt_op)(struct pf_sourcelim_id_tree *,
struct pf_sourcelim *))
@@ -2262,7 +2262,7 @@ pf_sourcelim_get(struct pfioc_sourcelim *ioc,
return (error);
}
static struct pf_source *
struct pf_source *
pf_source_rb_find(struct pf_source_ioc_tree *tree,
struct pf_source *key)
{
@@ -2271,7 +2271,7 @@ pf_source_rb_find(struct pf_source_ioc_tree *tree,
return (RB_FIND(pf_source_ioc_tree, tree, key));
}
static struct pf_source *
struct pf_source *
pf_source_rb_nfind(struct pf_source_ioc_tree *tree,
struct pf_source *key)
{
@@ -2280,100 +2280,7 @@ pf_source_rb_nfind(struct pf_source_ioc_tree *tree,
return (RB_NFIND(pf_source_ioc_tree, tree, key));
}
static int
pf_source_get(struct pfioc_source *ioc,
struct pf_source *(*rbt_op)(struct pf_source_ioc_tree *,
struct pf_source *))
{
struct pf_sourcelim plkey = { .pfsrlim_id = ioc->id };
struct pfioc_source_entry e, *uentry;
struct pf_source key;
struct pf_sourcelim *pfsrlim;
struct pf_source *pfsr;
size_t used = 0, len = ioc->entrieslen;
int error = 0;
PF_RULES_RLOCK_TRACKER;
if (ioc->entry_size != sizeof(e))
return (EINVAL);
if (len < sizeof(e))
return (EMSGSIZE);
error = copyin(ioc->key, &e, sizeof(e));
if (error != 0)
return (error);
PF_RULES_RLOCK();
#if 0
if (ioc->ticket != pf_main_ruleset.rules.active.ticket) {
error = EBUSY;
goto unlock;
}
#endif
pfsrlim = pf_sourcelim_rb_find(&V_pf_sourcelim_id_tree_active, &plkey);
if (pfsrlim == NULL) {
error = ESRCH;
goto unlock;
}
key.pfsr_af = e.af;
key.pfsr_rdomain = e.rdomain;
key.pfsr_addr = e.addr;
pfsr = (*rbt_op)(&pfsrlim->pfsrlim_ioc_sources, &key);
if (pfsr == NULL) {
error = ENOENT;
goto unlock;
}
memset(&e, 0, sizeof(e));
uentry = ioc->entries;
for (;;) {
e.af = pfsr->pfsr_af;
e.rdomain = pfsr->pfsr_rdomain;
e.addr = pfsr->pfsr_addr;
e.inuse = pfsr->pfsr_inuse;
e.admitted = pfsr->pfsr_counters.admitted;
e.hardlimited = pfsr->pfsr_counters.hardlimited;
e.ratelimited = pfsr->pfsr_counters.ratelimited;
error = copyout(&e, uentry, sizeof(e));
if (error != 0)
goto unlock;
used += sizeof(e);
if (used == len)
break;
pfsr = RB_NEXT(pf_source_ioc_tree, srlim->pfsrlim_ioc_sources, pfsr);
if (pfsr == NULL)
break;
if ((len - used) < sizeof(e)) {
error = EMSGSIZE;
goto unlock;
}
uentry++;
}
MPASS(error == 0);
ioc->inet_prefix = pfsrlim->pfsrlim_ipv4_prefix;
ioc->inet6_prefix = pfsrlim->pfsrlim_ipv6_prefix;
ioc->limit = pfsrlim->pfsrlim_limit;
ioc->entrieslen = used;
unlock:
PF_RULES_RUNLOCK();
return (error);
}
static int
int
pf_source_clr(struct pfioc_source_kill *ioc)
{
extern struct pf_source_list pf_source_gc;
@@ -3876,12 +3783,6 @@ pfioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flags, struct thread *td
case DIOCGETTIMEOUT:
case DIOCCLRRULECTRS:
case DIOCGETLIMIT:
case DIOCGETSTATELIM:
case DIOCGETNSTATELIM:
case DIOCGETSOURCELIM:
case DIOCGETNSOURCELIM:
case DIOCGETSOURCE:
case DIOCGETNSOURCE:
case DIOCGETALTQSV0:
case DIOCGETALTQSV1:
case DIOCGETALTQV0:
@@ -3941,12 +3842,6 @@ pfioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flags, struct thread *td
#endif
case DIOCGETTIMEOUT:
case DIOCGETLIMIT:
case DIOCGETSTATELIM:
case DIOCGETNSTATELIM:
case DIOCGETSOURCELIM:
case DIOCGETNSOURCELIM:
case DIOCGETSOURCE:
case DIOCGETNSOURCE:
case DIOCGETALTQSV0:
case DIOCGETALTQSV1:
case DIOCGETALTQV0:
@@ -5235,42 +5130,6 @@ pfioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flags, struct thread *td
break;
}
case DIOCADDSTATELIM:
error = pf_statelim_add((struct pfioc_statelim *)addr);
break;
case DIOCGETSTATELIM:
error = pf_statelim_get((struct pfioc_statelim *)addr,
pf_statelim_rb_find);
break;
case DIOCGETNSTATELIM:
error = pf_statelim_get((struct pfioc_statelim *)addr,
pf_statelim_rb_nfind);
break;
case DIOCADDSOURCELIM:
error = pf_sourcelim_add((struct pfioc_sourcelim *)addr);
break;
case DIOCGETSOURCELIM:
error = pf_sourcelim_get((struct pfioc_sourcelim *)addr,
pf_sourcelim_rb_find);
break;
case DIOCGETNSOURCELIM:
error = pf_sourcelim_get((struct pfioc_sourcelim *)addr,
pf_sourcelim_rb_nfind);
break;
case DIOCGETSOURCE:
error = pf_source_get((struct pfioc_source *)addr,
pf_source_rb_find);
break;
case DIOCGETNSOURCE:
error = pf_source_get((struct pfioc_source *)addr,
pf_source_rb_nfind);
break;
case DIOCCLRSOURCE:
error = pf_source_clr((struct pfioc_source_kill *)addr);
break;
case DIOCCLRRULECTRS: {
/* obsoleted by DIOCGETRULE with action=PF_GET_CLR_CNTR */
struct pf_kruleset *ruleset = &pf_main_ruleset;
+407
View File
@@ -2463,6 +2463,346 @@ pf_handle_table_clear_astats(struct nlmsghdr *hdr, struct nl_pstate *npt)
return (error);
}
static const struct nlattr_parser nla_p_rate[] = {
{ .type = PF_LR_LIMIT, .off = 0, .cb = nlattr_get_uint32 },
{ .type = PF_LR_SECONDS, .off = sizeof(unsigned int), .cb = nlattr_get_uint32 },
};
NL_DECLARE_ATTR_PARSER(rate_parser, nla_p_rate);
#define _OUT(_field) offsetof(struct pfioc_statelim, _field)
static const struct nlattr_parser nla_p_state_limiter[] = {
{ .type = PF_SL_TICKET, .off = _OUT(ticket), .cb = nlattr_get_uint32 },
{ .type = PF_SL_NAME, .off = _OUT(name), .arg = (void *)PF_STATELIM_NAME_LEN, .cb = nlattr_get_chara },
{ .type = PF_SL_ID, .off = _OUT(id), .cb = nlattr_get_uint32 },
{ .type = PF_SL_LIMIT, .off = _OUT(limit), .cb = nlattr_get_uint32 },
{ .type = PF_SL_RATE, .off = _OUT(rate), .arg = &rate_parser, .cb = nlattr_get_nested },
{ .type = PF_SL_DESCR, .off = _OUT(description), .arg = (void *)PF_STATELIM_DESCR_LEN, .cb = nlattr_get_chara },
};
NL_DECLARE_PARSER(state_limiter_parser, struct genlmsghdr, nlf_p_empty, nla_p_state_limiter);
#undef _OUT
static int
pf_handle_state_limiter_add(struct nlmsghdr *hdr, struct nl_pstate *npt)
{
struct pfioc_statelim attrs = { 0 };
struct nl_writer *nw = npt->nw;
struct genlmsghdr *ghdr_new;
int error;
error = nl_parse_nlmsg(hdr, &state_limiter_parser, npt, &attrs);
if (error != 0)
return (error);
error = pf_statelim_add(&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_STATE_LIMITER_ADD;
nlattr_add_u32(nw, PF_SL_ID, attrs.id);
if (!nlmsg_end(nw))
return (ENOMEM);
return (error);
}
static bool
nlattr_add_limiter_rate(struct nl_writer *nw, int attrtype,
const struct pf_limiter_rate *rate)
{
int off = nlattr_add_nested(nw, attrtype);
if (off == 0)
return (false);
nlattr_add_u32(nw, PF_LR_LIMIT, rate->limit);
nlattr_add_u32(nw, PF_LR_SECONDS, rate->seconds);
nlattr_set_len(nw, off);
return (true);
}
static int
pf_handle_state_limiter_get(struct nlmsghdr *hdr, struct nl_pstate *npt)
{
struct pfioc_statelim attrs = { 0 };
struct nl_writer *nw = npt->nw;
struct genlmsghdr *ghdr = (struct genlmsghdr *)(hdr + 1);
struct genlmsghdr *ghdr_new;
int error;
error = nl_parse_nlmsg(hdr, &state_limiter_parser, npt, &attrs);
if (error != 0)
return (error);
error = pf_statelim_get(&attrs,
ghdr->cmd == PFNL_CMD_STATE_LIMITER_GET ? pf_statelim_rb_find :
pf_statelim_rb_nfind);
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_STATE_LIMITER_GET;
nlattr_add_string(nw, PF_SL_NAME, attrs.name);
nlattr_add_u32(nw, PF_SL_ID, attrs.id);
nlattr_add_u32(nw, PF_SL_LIMIT, attrs.limit);
nlattr_add_limiter_rate(nw, PF_SL_RATE, &attrs.rate);
nlattr_add_string(nw, PF_SL_DESCR, attrs.description);
nlattr_add_u32(nw, PF_SL_INUSE, attrs.inuse);
nlattr_add_u64(nw, PF_SL_ADMITTED, attrs.admitted);
nlattr_add_u64(nw, PF_SL_HARDLIMITED, attrs.hardlimited);
nlattr_add_u64(nw, PF_SL_RATELIMITED, attrs.ratelimited);
if (!nlmsg_end(nw))
return (ENOMEM);
return (error);
}
#define _OUT(_field) offsetof(struct pfioc_sourcelim, _field)
static const struct nlattr_parser nla_p_source_limiter[] = {
{ .type = PF_SCL_TICKET, .off = _OUT(ticket), .cb = nlattr_get_uint32 },
{ .type = PF_SCL_NAME, .off = _OUT(name), .arg = (void *)PF_STATELIM_NAME_LEN, .cb = nlattr_get_chara },
{ .type = PF_SCL_ID, .off = _OUT(id), .cb = nlattr_get_uint32 },
{ .type = PF_SCL_ENTRIES, .off = _OUT(entries), .cb = nlattr_get_uint32 },
{ .type = PF_SCL_LIMIT, .off = _OUT(limit), .cb = nlattr_get_uint32 },
{ .type = PF_SCL_RATE, .off = _OUT(rate), .arg = &rate_parser, .cb = nlattr_get_nested },
{ .type = PF_SCL_OVERLOAD_TBL_NAME, .off = _OUT(overload_tblname), .arg = (void *)PF_TABLE_NAME_SIZE, .cb = nlattr_get_chara },
{ .type = PF_SCL_OVERLOAD_HIGH_WM, .off = _OUT(overload_hwm), .cb = nlattr_get_uint32 },
{ .type = PF_SCL_OVERLOAD_LOW_WM, .off = _OUT(overload_lwm), .cb = nlattr_get_uint32 },
{ .type = PF_SCL_INET_PREFIX, .off = _OUT(inet_prefix), .cb = nlattr_get_uint32 },
{ .type = PF_SCL_INET6_PREFIX, .off = _OUT(inet6_prefix), .cb = nlattr_get_uint32 },
{ .type = PF_SCL_DESCR, .off = _OUT(description), .arg = (void *)PF_STATELIM_DESCR_LEN, .cb = nlattr_get_chara },
};
#undef _OUT
NL_DECLARE_PARSER(source_limiter_parser, struct genlmsghdr, nlf_p_empty, nla_p_source_limiter);
static int
pf_handle_source_limiter_add(struct nlmsghdr *hdr, struct nl_pstate *npt)
{
struct pfioc_sourcelim attrs = { 0 };
struct nl_writer *nw = npt->nw;
struct genlmsghdr *ghdr_new;
int error;
error = nl_parse_nlmsg(hdr, &source_limiter_parser, npt, &attrs);
if (error != 0)
return (error);
error = pf_sourcelim_add(&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_SOURCE_LIMITER_ADD;
nlattr_add_u32(nw, PF_SCL_ID, attrs.id);
if (!nlmsg_end(nw))
return (ENOMEM);
return (error);
}
static int
pf_handle_source_limiter_get(struct nlmsghdr *hdr, struct nl_pstate *npt)
{
struct pfioc_sourcelim attrs = { 0 };
struct nl_writer *nw = npt->nw;
struct genlmsghdr *ghdr = (struct genlmsghdr *)(hdr + 1);
struct genlmsghdr *ghdr_new;
int error;
error = nl_parse_nlmsg(hdr, &source_limiter_parser, npt, &attrs);
if (error != 0)
return (error);
error = pf_sourcelim_get(&attrs,
ghdr->cmd == PFNL_CMD_SOURCE_LIMITER_GET ? pf_sourcelim_rb_find :
pf_sourcelim_rb_nfind);
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 = ghdr->cmd;
nlattr_add_string(nw, PF_SCL_NAME, attrs.name);
nlattr_add_u32(nw, PF_SCL_ID, attrs.id);
nlattr_add_u32(nw, PF_SCL_ENTRIES, attrs.entries);
nlattr_add_u32(nw, PF_SCL_LIMIT, attrs.limit);
nlattr_add_limiter_rate(nw, PF_SCL_RATE, &attrs.rate);
nlattr_add_string(nw, PF_SCL_OVERLOAD_TBL_NAME, attrs.overload_tblname);
nlattr_add_u32(nw, PF_SCL_OVERLOAD_HIGH_WM, attrs.overload_hwm);
nlattr_add_u32(nw, PF_SCL_OVERLOAD_LOW_WM, attrs.overload_lwm);
nlattr_add_u32(nw, PF_SCL_INET_PREFIX, attrs.inet_prefix);
nlattr_add_u32(nw, PF_SCL_INET6_PREFIX, attrs.inet6_prefix);
nlattr_add_string(nw, PF_SCL_DESCR, attrs.description);
nlattr_add_u32(nw, PF_SCL_NENTRIES, attrs.nentries);
nlattr_add_u32(nw, PF_SCL_INUSE, attrs.inuse);
nlattr_add_u64(nw, PF_SCL_ADDR_ALLOCS, attrs.addrallocs);
nlattr_add_u64(nw, PF_SCL_ADDR_NOMEM, attrs.addrnomem);
nlattr_add_u64(nw, PF_SCL_ADMITTED, attrs.admitted);
nlattr_add_u64(nw, PF_SCL_ADDRLIMITED, attrs.addrlimited);
nlattr_add_u64(nw, PF_SCL_HARDLIMITED, attrs.hardlimited);
nlattr_add_u64(nw, PF_SCL_RATELIMITED, attrs.ratelimited);
if (!nlmsg_end(nw))
return (ENOMEM);
return (error);
}
struct nlattr_source {
char name[PF_SOURCELIM_NAME_LEN];
uint32_t id;
sa_family_t af;
unsigned int rdomain;
struct pf_addr addr;
};
#define _OUT(_field) offsetof(struct nlattr_source, _field)
static const struct nlattr_parser nla_p_source[] = {
{ .type = PF_SRC_NAME, .off = _OUT(name), .arg = (void *)PF_SOURCELIM_NAME_LEN, .cb = nlattr_get_chara },
{ .type = PF_SRC_ID, .off = _OUT(id), .cb = nlattr_get_uint32 },
{ .type = PF_SRC_AF, .off = _OUT(af), .cb = nlattr_get_uint8 },
{ .type = PF_SRC_RDOMAIN, .off = _OUT(rdomain), .cb = nlattr_get_uint32 },
{ .type = PF_SRC_ADDR, .off = _OUT(addr), .cb = nlattr_get_in6_addr },
};
#undef _OUT
NL_DECLARE_PARSER(source_parser, struct genlmsghdr, nlf_p_empty, nla_p_source);
static int
pf_handle_source_get(struct nlmsghdr *hdr, struct nl_pstate *npt)
{
struct nlattr_source attrs = { 0 };
struct pf_source key;
struct pf_sourcelim plkey;
struct nl_writer *nw = npt->nw;
struct genlmsghdr *ghdr = (struct genlmsghdr *)(hdr + 1);
struct genlmsghdr *ghdr_new;
struct pf_sourcelim *pfsrlim;
struct pf_source *pfsr;
int error;
PF_RULES_RLOCK_TRACKER;
error = nl_parse_nlmsg(hdr, &source_parser, npt, &attrs);
if (error != 0)
return (error);
PF_RULES_RLOCK();
plkey.pfsrlim_id = attrs.id;
pfsrlim = pf_sourcelim_rb_find(&V_pf_sourcelim_id_tree_active, &plkey);
if (pfsrlim == NULL) {
error = ESRCH;
goto out;
}
key.pfsr_af = attrs.af;
key.pfsr_rdomain = attrs.rdomain;
key.pfsr_addr = attrs.addr;
pfsr = (ghdr->cmd == PFNL_CMD_SOURCE_GET ? pf_source_rb_find :
pf_source_rb_nfind)(&pfsrlim->pfsrlim_ioc_sources, &key);
if (pfsr == NULL) {
error = ENOENT;
goto out;
}
for (;;) {
if (!nlmsg_reply(nw, hdr, sizeof(struct genlmsghdr))) {
error = ENOMEM;
goto out;
}
ghdr_new = nlmsg_reserve_object(nw, struct genlmsghdr);
ghdr_new->cmd = ghdr->cmd;
nlattr_add_u8(nw, PF_SRC_AF, pfsr->pfsr_af);
nlattr_add_u32(nw, PF_SRC_RDOMAIN, pfsr->pfsr_rdomain);
nlattr_add_in6_addr(nw, PF_SRC_ADDR, &pfsr->pfsr_addr.v6);
nlattr_add_u32(nw, PF_SRC_INUSE, pfsr->pfsr_inuse);
nlattr_add_u64(nw, PF_SRC_ADMITTED, pfsr->pfsr_counters.admitted);
nlattr_add_u64(nw, PF_SRC_HARDLIMITED, pfsr->pfsr_counters.hardlimited);
nlattr_add_u64(nw, PF_SRC_RATELIMITED, pfsr->pfsr_counters.ratelimited);
nlattr_add_u32(nw, PF_SRC_LIMIT, pfsrlim->pfsrlim_limit);
nlattr_add_u32(nw, PF_SRC_INET_PREFIX, pfsrlim->pfsrlim_ipv4_prefix);
nlattr_add_u32(nw, PF_SRC_INET6_PREFIX, pfsrlim->pfsrlim_ipv6_prefix);
if (!nlmsg_end(nw)) {
nlmsg_abort(nw);
error = ENOMEM;
goto out;
}
pfsr = RB_NEXT(pf_source_ioc_tree, srlim->pfsrlim_ioc_sources, pfsr);
if (pfsr == NULL)
break;
}
out:
PF_RULES_RUNLOCK();
return (error);
}
#define _OUT(_field) offsetof(struct pfioc_source_kill, _field)
static const struct nlattr_parser nla_p_source_clear[] = {
{ .type = PF_SC_NAME, .off = _OUT(name), .arg = (void *)PF_SOURCELIM_NAME_LEN, .cb = nlattr_get_chara },
{ .type = PF_SC_ID, .off = _OUT(id), .cb = nlattr_get_uint32 },
{ .type = PF_SC_RDOMAIN, .off = _OUT(rdomain), .cb = nlattr_get_uint32 },
{ .type = PF_SC_AF, .off = _OUT(af), .cb = nlattr_get_uint8 },
{ .type = PF_SC_ADDR, .off = _OUT(addr), .cb = nlattr_get_in6_addr },
};
#undef _OUT
NL_DECLARE_PARSER(source_clear_parser, struct genlmsghdr, nlf_p_empty, nla_p_source_clear);
static int
pf_handle_source_clear(struct nlmsghdr *hdr, struct nl_pstate *npt)
{
struct pfioc_source_kill attrs = { 0 };
struct nl_writer *nw = npt->nw;
struct genlmsghdr *ghdr = (struct genlmsghdr *)(hdr + 1);
struct genlmsghdr *ghdr_new;
int error;
error = nl_parse_nlmsg(hdr, &source_clear_parser, npt, &attrs);
if (error != 0)
return (error);
error = pf_source_clr(&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 = ghdr->cmd;
nlattr_add_string(nw, PF_SCL_NAME, attrs.name);
if (!nlmsg_end(nw))
return (ENOMEM);
return (error);
}
static const struct nlhdr_parser *all_parsers[] = {
&state_parser,
&addrule_parser,
@@ -2479,6 +2819,10 @@ static const struct nlhdr_parser *all_parsers[] = {
&table_parser,
&table_addr_parser,
&table_astats_parser,
&state_limiter_parser,
&source_limiter_parser,
&source_parser,
&source_clear_parser,
};
static uint16_t family_id;
@@ -2743,6 +3087,69 @@ 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_STATE_LIMITER_ADD,
.cmd_name = "STATE_LIMITER_ADD",
.cmd_cb = pf_handle_state_limiter_add,
.cmd_flags = GENL_CMD_CAP_DO | GENL_CMD_CAP_HASPOL,
.cmd_priv = PRIV_NETINET_PF,
},
{
.cmd_num = PFNL_CMD_STATE_LIMITER_GET,
.cmd_name = "STATE_LIMITER_GET",
.cmd_cb = pf_handle_state_limiter_get,
.cmd_flags = GENL_CMD_CAP_DUMP | GENL_CMD_CAP_HASPOL,
.cmd_priv = PRIV_NETINET_PF,
},
{
.cmd_num = PFNL_CMD_STATE_LIMITER_NGET,
.cmd_name = "STATE_LIMITER_NGET",
.cmd_cb = pf_handle_state_limiter_get,
.cmd_flags = GENL_CMD_CAP_DUMP | GENL_CMD_CAP_HASPOL,
.cmd_priv = PRIV_NETINET_PF,
},
{
.cmd_num = PFNL_CMD_SOURCE_LIMITER_ADD,
.cmd_name = "SOURCE_LIMITER_ADD",
.cmd_cb = pf_handle_source_limiter_add,
.cmd_flags = GENL_CMD_CAP_DO | GENL_CMD_CAP_HASPOL,
.cmd_priv = PRIV_NETINET_PF,
},
{
.cmd_num = PFNL_CMD_SOURCE_LIMITER_GET,
.cmd_name = "SOURCE_LIMITER_GET",
.cmd_cb = pf_handle_source_limiter_get,
.cmd_flags = GENL_CMD_CAP_DUMP | GENL_CMD_CAP_HASPOL,
.cmd_priv = PRIV_NETINET_PF,
},
{
.cmd_num = PFNL_CMD_SOURCE_LIMITER_NGET,
.cmd_name = "SOURCE_LIMITER_NGET",
.cmd_cb = pf_handle_source_limiter_get,
.cmd_flags = GENL_CMD_CAP_DUMP | GENL_CMD_CAP_HASPOL,
.cmd_priv = PRIV_NETINET_PF,
},
{
.cmd_num = PFNL_CMD_SOURCE_GET,
.cmd_name = "SOURCE_GET",
.cmd_cb = pf_handle_source_get,
.cmd_flags = GENL_CMD_CAP_DUMP | GENL_CMD_CAP_HASPOL,
.cmd_priv = PRIV_NETINET_PF,
},
{
.cmd_num = PFNL_CMD_SOURCE_NGET,
.cmd_name = "SOURCE_NGET",
.cmd_cb = pf_handle_source_get,
.cmd_flags = GENL_CMD_CAP_DUMP | GENL_CMD_CAP_HASPOL,
.cmd_priv = PRIV_NETINET_PF,
},
{
.cmd_num = PFNL_CMD_SOURCE_CLEAR,
.cmd_name = "SOURCE_CLEAR",
.cmd_cb = pf_handle_source_clear,
.cmd_flags = GENL_CMD_CAP_DO | GENL_CMD_CAP_HASPOL,
.cmd_priv = PRIV_NETINET_PF,
},
};
void
+79
View File
@@ -73,6 +73,15 @@ enum {
PFNL_CMD_TABLE_GET_ADDR = 35,
PFNL_CMD_TABLE_GET_ASTATS = 36,
PFNL_CMD_TABLE_CLEAR_ASTATS = 37,
PFNL_CMD_STATE_LIMITER_ADD = 38,
PFNL_CMD_STATE_LIMITER_GET = 39,
PFNL_CMD_STATE_LIMITER_NGET = 40,
PFNL_CMD_SOURCE_LIMITER_ADD = 41,
PFNL_CMD_SOURCE_LIMITER_GET = 42,
PFNL_CMD_SOURCE_LIMITER_NGET = 43,
PFNL_CMD_SOURCE_GET = 44,
PFNL_CMD_SOURCE_NGET = 45,
PFNL_CMD_SOURCE_CLEAR = 46,
__PFNL_CMD_MAX,
};
#define PFNL_CMD_MAX (__PFNL_CMD_MAX -1)
@@ -509,6 +518,76 @@ enum pf_table_astats_t {
PF_TAS_ASTATS_COUNT = 4, /* u32 */
PF_TAS_ASTATS_ZEROED = 5, /* u32 */
};
enum pf_limit_rate_t {
PF_LR_UNSPEC,
PF_LR_LIMIT = 1, /* u32 */
PF_LR_SECONDS = 2, /* u32 */
};
enum pf_state_limit_t {
PF_SL_UNSPEC,
PF_SL_TICKET = 1, /* u32 */
PF_SL_NAME = 2, /* string */
PF_SL_ID = 3, /* u32 */
PF_SL_LIMIT = 4, /* u32 */
PF_SL_RATE = 5, /* nested, pf_limit_rate_t */
PF_SL_DESCR = 6, /* string */
PF_SL_INUSE = 7, /* u32 */
PF_SL_ADMITTED = 8, /* u64 */
PF_SL_HARDLIMITED = 9, /* u64 */
PF_SL_RATELIMITED = 10, /* u64 */
};
enum pf_source_limit_t {
PF_SCL_UNSPEC,
PF_SCL_TICKET = 1, /* u32 */
PF_SCL_NAME = 2, /* string */
PF_SCL_ID = 3, /* u32 */
PF_SCL_ENTRIES = 4, /* u32 */
PF_SCL_LIMIT = 5, /* u32 */
PF_SCL_RATE = 6, /* nested, pf_limit_rate_t */
PF_SCL_OVERLOAD_TBL_NAME = 7, /* string*/
PF_SCL_OVERLOAD_HIGH_WM = 8, /* u32 */
PF_SCL_OVERLOAD_LOW_WM = 9, /* u32 */
PF_SCL_INET_PREFIX = 10, /* u32 */
PF_SCL_INET6_PREFIX = 11, /* u32 */
PF_SCL_DESCR = 12, /* string */
PF_SCL_NENTRIES = 13, /* u32 */
PF_SCL_INUSE = 14, /* u32 */
PF_SCL_ADDR_ALLOCS = 15, /* u64 */
PF_SCL_ADDR_NOMEM = 16, /* u64 */
PF_SCL_ADMITTED = 17, /* u64 */
PF_SCL_ADDRLIMITED = 18, /* u64 */
PF_SCL_HARDLIMITED = 19, /* u64 */
PF_SCL_RATELIMITED = 20, /* u64 */
};
enum pf_source_t {
PF_SRC_UNSPEC,
PF_SRC_NAME = 1, /* string */
PF_SRC_ID = 2, /* u32 */
PF_SRC_AF = 3, /* u8 */
PF_SRC_RDOMAIN = 4, /* u32 */
PF_SRC_ADDR = 5, /* in6_addr */
PF_SRC_INUSE = 6, /* u32 */
PF_SRC_ADMITTED = 7, /* u64 */
PF_SRC_HARDLIMITED = 8, /* u64 */
PF_SRC_RATELIMITED = 9, /* u64 */
PF_SRC_LIMIT = 10, /* u32 */
PF_SRC_INET_PREFIX = 11, /* u32 */
PF_SRC_INET6_PREFIX = 12, /* u32 */
};
enum pf_source_clear_t {
PF_SC_UNSPEC,
PF_SC_NAME = 1, /* string */
PF_SC_ID = 2, /* u32*/
PF_SC_RDOMAIN = 3, /* u32 */
PF_SC_AF = 4, /* u8 */
PF_SC_ADDR = 5, /* in6_addr */
};
#ifdef _KERNEL
void pf_nl_register(void);