summaryrefslogtreecommitdiff
path: root/lib/libpfctl
diff options
context:
space:
mode:
authorKristof Provost <kp@FreeBSD.org>2021-05-10 14:51:38 +0000
committerKristof Provost <kp@FreeBSD.org>2021-05-27 07:06:00 +0000
commit59f8fc3fbf70d866ee20f0e37dcae0efcdace554 (patch)
tree27171cb3d5ff98e659d18dd28a7a95db1719dc5c /lib/libpfctl
parentdef59341c9384923f49ee05312733195e6476f9b (diff)
Diffstat (limited to 'lib/libpfctl')
-rw-r--r--lib/libpfctl/libpfctl.c173
-rw-r--r--lib/libpfctl/libpfctl.h60
2 files changed, 233 insertions, 0 deletions
diff --git a/lib/libpfctl/libpfctl.c b/lib/libpfctl/libpfctl.c
index 8271d9bab3df..6a6ecd8fb136 100644
--- a/lib/libpfctl/libpfctl.c
+++ b/lib/libpfctl/libpfctl.c
@@ -627,6 +627,179 @@ pfctl_nv_add_state_cmp(nvlist_t *nvl, const char *name,
nvlist_add_nvlist(nvl, name, nv);
}
+static void
+pf_nvstate_scrub_to_state_scrub(const nvlist_t *nvl,
+ struct pfctl_state_scrub *scrub)
+{
+ bzero(scrub, sizeof(*scrub));
+
+ scrub->timestamp = nvlist_get_bool(nvl, "timestamp");
+ scrub->ttl = nvlist_get_number(nvl, "ttl");
+ scrub->ts_mod = nvlist_get_number(nvl, "ts_mod");
+}
+
+static void
+pf_nvstate_peer_to_state_peer(const nvlist_t *nvl,
+ struct pfctl_state_peer *peer)
+{
+ bzero(peer, sizeof(*peer));
+
+ if (nvlist_exists_nvlist(nvl, "scrub")) {
+ peer->scrub = malloc(sizeof(*peer->scrub));
+ pf_nvstate_scrub_to_state_scrub(
+ nvlist_get_nvlist(nvl, "scrub"),
+ peer->scrub);
+ }
+
+ peer->seqlo = nvlist_get_number(nvl, "seqlo");
+ peer->seqhi = nvlist_get_number(nvl, "seqhi");
+ peer->seqdiff = nvlist_get_number(nvl, "seqdiff");
+ peer->max_win = nvlist_get_number(nvl, "max_win");
+ peer->mss = nvlist_get_number(nvl, "mss");
+ peer->state = nvlist_get_number(nvl, "state");
+ peer->wscale = nvlist_get_number(nvl, "wscale");
+}
+
+static void
+pf_nvstate_key_to_state_key(const nvlist_t *nvl, struct pfctl_state_key *key)
+{
+ const nvlist_t * const *tmp;
+ size_t count;
+
+ bzero(key, sizeof(*key));
+
+ tmp = nvlist_get_nvlist_array(nvl, "addr", &count);
+ assert(count == 2);
+
+ for (int i = 0; i < 2; i++)
+ pf_nvaddr_to_addr(tmp[i], &key->addr[i]);
+
+ pf_nvuint_16_array(nvl, "port", 2, key->port, NULL);
+
+ key->af = nvlist_get_number(nvl, "af");
+ key->proto = nvlist_get_number(nvl, "proto");
+}
+
+static void
+pf_nvstate_to_state(const nvlist_t *nvl, struct pfctl_state *s)
+{
+ bzero(s, sizeof(*s));
+
+ s->id = nvlist_get_number(nvl, "id");
+ s->creatorid = nvlist_get_number(nvl, "creatorid");
+ s->direction = nvlist_get_number(nvl, "direction");
+
+ pf_nvstate_peer_to_state_peer(nvlist_get_nvlist(nvl, "src"), &s->src);
+ pf_nvstate_peer_to_state_peer(nvlist_get_nvlist(nvl, "dst"), &s->dst);
+
+ pf_nvstate_key_to_state_key(nvlist_get_nvlist(nvl, "stack_key"),
+ &s->key[0]);
+ pf_nvstate_key_to_state_key(nvlist_get_nvlist(nvl, "wire_key"),
+ &s->key[1]);
+
+ strlcpy(s->ifname, nvlist_get_string(nvl, "ifname"),
+ sizeof(s->ifname));
+
+ pf_nvaddr_to_addr(nvlist_get_nvlist(nvl, "rt_addr"), &s->rt_addr);
+ s->rule = nvlist_get_number(nvl, "rule");
+ s->anchor = nvlist_get_number(nvl, "anchor");
+ s->nat_rule = nvlist_get_number(nvl, "nat_rule");
+ s->creation = nvlist_get_number(nvl, "creation");
+ s->expire = nvlist_get_number(nvl, "expire");
+
+ pf_nvuint_64_array(nvl, "packets", 2, s->packets, NULL);
+ pf_nvuint_64_array(nvl, "bytes", 2, s->bytes, NULL);
+
+ s->log = nvlist_get_number(nvl, "log");
+ s->state_flags = nvlist_get_number(nvl, "state_flags");
+ s->timeout = nvlist_get_number(nvl, "timeout");
+ s->sync_flags = nvlist_get_number(nvl, "sync_flags");
+}
+
+int
+pfctl_get_states(int dev, struct pfctl_states *states)
+{
+ struct pfioc_nv nv;
+ nvlist_t *nvl;
+ const nvlist_t * const *slist;
+ size_t found_count;
+
+ bzero(states, sizeof(*states));
+ TAILQ_INIT(&states->states);
+
+ /* Just enough to get a number, and we'll grow from there. */
+ nv.data = malloc(64);
+ nv.len = nv.size = 64;
+
+ for (;;) {
+ if (ioctl(dev, DIOCGETSTATESNV, &nv)) {
+ free(nv.data);
+ return (errno);
+ }
+
+ nvl = nvlist_unpack(nv.data, nv.len, 0);
+ if (nvl == NULL) {
+ free(nv.data);
+ return (EIO);
+ }
+
+ states->count = nvlist_get_number(nvl, "count");
+
+ /* Are there any states? */
+ if (states->count == 0)
+ break;
+
+ if (nvlist_exists_nvlist_array(nvl, "states"))
+ slist = nvlist_get_nvlist_array(nvl, "states", &found_count);
+ else
+ found_count = 0;
+
+ if (found_count < states->count) {
+ size_t new_size = nv.size +
+ (nv.size * states->count / (found_count + 1) * 2);
+
+ /* Our buffer is too small. Estimate what we need based
+ * on how many states fit in the previous allocation
+ * and how many states there are. Doubled for margin.
+ * */
+ nv.data = realloc(nv.data, new_size);
+ nv.size = new_size;
+
+ if (nv.data == NULL)
+ return (ENOMEM);
+ continue;
+ }
+
+ for (size_t i = 0; i < found_count; i++) {
+ struct pfctl_state *s = malloc(sizeof(*s));
+ if (s == NULL) {
+ pfctl_free_states(states);
+ nvlist_destroy(nvl);
+ free(nv.data);
+ return (ENOMEM);
+ }
+
+ pf_nvstate_to_state(slist[i], s);
+ TAILQ_INSERT_TAIL(&states->states, s, entry);
+ }
+ break;
+ }
+
+ return (0);
+}
+
+void
+pfctl_free_states(struct pfctl_states *states)
+{
+ struct pfctl_state *s, *tmp;
+
+ TAILQ_FOREACH_SAFE(s, &states->states, entry, tmp) {
+ free(s);
+ }
+
+ bzero(states, sizeof(*states));
+}
+
static int
_pfctl_clear_states(int dev, const struct pfctl_kill *kill,
unsigned int *killed, uint64_t ioctlval)
diff --git a/lib/libpfctl/libpfctl.h b/lib/libpfctl/libpfctl.h
index 7a1e02f3d01b..05447b5d8673 100644
--- a/lib/libpfctl/libpfctl.h
+++ b/lib/libpfctl/libpfctl.h
@@ -197,6 +197,64 @@ struct pfctl_kill {
bool kill_match;
};
+struct pfctl_state_scrub {
+ bool timestamp;
+ uint8_t ttl;
+ uint32_t ts_mod;
+};
+
+struct pfctl_state_peer {
+ struct pfctl_state_scrub *scrub;
+ uint32_t seqlo;
+ uint32_t seqhi;
+ uint32_t seqdiff;
+ uint16_t max_win;
+ uint16_t mss;
+ uint8_t state;
+ uint8_t wscale;
+};
+
+struct pfctl_state_key {
+ struct pf_addr addr[2];
+ uint16_t port[2];
+ sa_family_t af;
+ uint8_t proto;
+};
+
+struct pfctl_state {
+ TAILQ_ENTRY(pfctl_state) entry;
+
+ uint64_t id;
+ uint32_t creatorid;
+ uint8_t direction;
+
+ struct pfctl_state_peer src;
+ struct pfctl_state_peer dst;
+
+ uint32_t rule;
+ uint32_t anchor;
+ uint32_t nat_rule;
+ struct pf_addr rt_addr;
+ struct pfctl_state_key key[2]; /* addresses stack and wire */
+ char ifname[IFNAMSIZ];
+ uint64_t packets[2];
+ uint64_t bytes[2];
+ uint32_t creation;
+ uint32_t expire;
+ uint32_t pfsync_time;
+ uint16_t tag;
+ uint8_t log;
+ uint8_t state_flags;
+ uint8_t timeout;
+ uint32_t sync_flags;
+};
+
+TAILQ_HEAD(pfctl_statelist, pfctl_state);
+struct pfctl_states {
+ struct pfctl_statelist states;
+ size_t count;
+};
+
int pfctl_get_rule(int dev, u_int32_t nr, u_int32_t ticket,
const char *anchor, u_int32_t ruleset, struct pfctl_rule *rule,
char *anchor_call);
@@ -207,6 +265,8 @@ int pfctl_add_rule(int dev, const struct pfctl_rule *r,
const char *anchor, const char *anchor_call, u_int32_t ticket,
u_int32_t pool_ticket);
int pfctl_set_keepcounters(int dev, bool keep);
+int pfctl_get_states(int dev, struct pfctl_states *states);
+void pfctl_free_states(struct pfctl_states *states);
int pfctl_clear_states(int dev, const struct pfctl_kill *kill,
unsigned int *killed);
int pfctl_kill_states(int dev, const struct pfctl_kill *kill,