From 1732afaa0dae9d844e341f2c1d6ed4b79c403bfb Mon Sep 17 00:00:00 2001 From: Kristof Provost Date: Wed, 5 May 2021 14:33:55 +0200 Subject: [PATCH] pf: Add DIOCGETSTATENV Add DIOCGETSTATENV, an nvlist-based alternative to DIOCGETSTATE. MFC after: 1 week Sponsored by: Rubicon Communications, LLC ("Netgate") Differential Revision: https://reviews.freebsd.org/D30242 --- share/man/man4/pf.4 | 4 +- sys/net/pfvar.h | 1 + sys/netpfil/pf/pf_ioctl.c | 226 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 229 insertions(+), 2 deletions(-) diff --git a/share/man/man4/pf.4 b/share/man/man4/pf.4 index 2fb132203908..24843535c924 100644 --- a/share/man/man4/pf.4 +++ b/share/man/man4/pf.4 @@ -326,14 +326,14 @@ struct pfioc_state { struct pfsync_state state; }; .Ed -.It Dv DIOCGETSTATE Fa "struct pfioc_state *ps" +.It Dv DIOCGETSTATENV Fa "struct pfioc_nv *nv" Extract the entry identified by the .Va id and .Va creatorid fields of the .Va state -structure from the state table. +nvlist from the state table. .It Dv DIOCKILLSTATES Fa "struct pfioc_state_kill *psk" Remove matching entries from the state table. This ioctl returns the number of killed states in diff --git a/sys/net/pfvar.h b/sys/net/pfvar.h index 51c51a82b36d..1bd1ebd27449 100644 --- a/sys/net/pfvar.h +++ b/sys/net/pfvar.h @@ -1257,6 +1257,7 @@ struct pfioc_iface { #define DIOCCLRSTATES _IOWR('D', 18, struct pfioc_state_kill) #define DIOCCLRSTATESNV _IOWR('D', 18, struct pfioc_nv) #define DIOCGETSTATE _IOWR('D', 19, struct pfioc_state) +#define DIOCGETSTATENV _IOWR('D', 19, struct pfioc_nv) #define DIOCSETSTATUSIF _IOWR('D', 20, struct pfioc_if) #define DIOCGETSTATUS _IOWR('D', 21, struct pf_status) #define DIOCCLRSTATUS _IO ('D', 22) diff --git a/sys/netpfil/pf/pf_ioctl.c b/sys/netpfil/pf/pf_ioctl.c index 2d13ddf1ac61..1ea7310ebebb 100644 --- a/sys/netpfil/pf/pf_ioctl.c +++ b/sys/netpfil/pf/pf_ioctl.c @@ -208,6 +208,7 @@ static int pf_killstates_row(struct pf_kstate_kill *, struct pf_idhash *); static int pf_killstates_nv(struct pfioc_nv *); static int pf_clearstates_nv(struct pfioc_nv *); +static int pf_getstate(struct pfioc_nv *); static int pf_clear_tables(void); static void pf_clear_srcnodes(struct pf_ksrc_node *); static void pf_kill_srcnodes(struct pfioc_src_node_kill *); @@ -2474,6 +2475,157 @@ pf_nvstate_kill_to_kstate_kill(const nvlist_t *nvl, return (error); } +static nvlist_t * +pf_state_key_to_nvstate_key(const struct pf_state_key *key) +{ + nvlist_t *nvl, *tmp; + + nvl = nvlist_create(0); + if (nvl == NULL) + return (NULL); + + for (int i = 0; i < 2; i++) { + tmp = pf_addr_to_nvaddr(&key->addr[i]); + if (tmp == NULL) + goto errout; + nvlist_append_nvlist_array(nvl, "addr", tmp); + nvlist_append_number_array(nvl, "port", key->port[i]); + } + nvlist_add_number(nvl, "af", key->af); + nvlist_add_number(nvl, "proto", key->proto); + + return (nvl); + +errout: + nvlist_destroy(nvl); + return (NULL); +} + +static nvlist_t * +pf_state_scrub_to_nvstate_scrub(const struct pf_state_scrub *scrub) +{ + nvlist_t *nvl; + + nvl = nvlist_create(0); + if (nvl == NULL) + return (NULL); + + nvlist_add_bool(nvl, "timestamp", scrub->pfss_flags & PFSS_TIMESTAMP); + nvlist_add_number(nvl, "ttl", scrub->pfss_ttl); + nvlist_add_number(nvl, "ts_mod", scrub->pfss_ts_mod); + + return (nvl); +} + +static nvlist_t * +pf_state_peer_to_nvstate_peer(const struct pf_state_peer *peer) +{ + nvlist_t *nvl, *tmp; + + nvl = nvlist_create(0); + if (nvl == NULL) + return (NULL); + + if (peer->scrub) { + tmp = pf_state_scrub_to_nvstate_scrub(peer->scrub); + if (tmp == NULL) + goto errout; + nvlist_add_nvlist(nvl, "scrub", tmp); + } + + nvlist_add_number(nvl, "seqlo", peer->seqlo); + nvlist_add_number(nvl, "seqhi", peer->seqhi); + nvlist_add_number(nvl, "seqdiff", peer->seqdiff); + nvlist_add_number(nvl, "max_win", peer->max_win); + nvlist_add_number(nvl, "mss", peer->mss); + nvlist_add_number(nvl, "state", peer->state); + nvlist_add_number(nvl, "wscale", peer->wscale); + + return (nvl); + +errout: + nvlist_destroy(nvl); + return (NULL); +} + + +static nvlist_t * +pf_state_to_nvstate(const struct pf_state *s) +{ + nvlist_t *nvl, *tmp; + uint32_t expire, flags = 0; + + nvl = nvlist_create(0); + if (nvl == NULL) + return (NULL); + + nvlist_add_number(nvl, "id", s->id); + nvlist_add_string(nvl, "ifname", s->kif->pfik_name); + + tmp = pf_state_key_to_nvstate_key(s->key[PF_SK_STACK]); + if (tmp == NULL) + goto errout; + nvlist_add_nvlist(nvl, "stack_key", tmp); + + tmp = pf_state_key_to_nvstate_key(s->key[PF_SK_WIRE]); + if (tmp == NULL) + goto errout; + nvlist_add_nvlist(nvl, "wire_key", tmp); + + tmp = pf_state_peer_to_nvstate_peer(&s->src); + if (tmp == NULL) + goto errout; + nvlist_add_nvlist(nvl, "src", tmp); + + tmp = pf_state_peer_to_nvstate_peer(&s->dst); + if (tmp == NULL) + goto errout; + nvlist_add_nvlist(nvl, "dst", tmp); + + tmp = pf_addr_to_nvaddr(&s->rt_addr); + if (tmp == NULL) + goto errout; + nvlist_add_nvlist(nvl, "rt_addr", tmp); + + nvlist_add_number(nvl, "rule", s->rule.ptr ? s->rule.ptr->nr : -1); + nvlist_add_number(nvl, "anchor", + s->anchor.ptr ? s->anchor.ptr->nr : -1); + nvlist_add_number(nvl, "nat_rule", + s->nat_rule.ptr ? s->nat_rule.ptr->nr : -1); + nvlist_add_number(nvl, "creation", s->creation); + + expire = pf_state_expires(s); + if (expire <= time_uptime) + expire = 0; + else + expire = expire - time_uptime; + nvlist_add_number(nvl, "expire", expire); + + for (int i = 0; i < 2; i++) { + nvlist_append_number_array(nvl, "packets", + counter_u64_fetch(s->packets[i])); + nvlist_append_number_array(nvl, "bytes", + counter_u64_fetch(s->bytes[i])); + } + + nvlist_add_number(nvl, "creatorid", s->creatorid); + nvlist_add_number(nvl, "direction", s->direction); + nvlist_add_number(nvl, "log", s->log); + nvlist_add_number(nvl, "state_flags", s->state_flags); + nvlist_add_number(nvl, "timeout", s->timeout); + if (s->src_node) + flags |= PFSYNC_FLAG_SRCNODE; + if (s->nat_src_node) + flags |= PFSYNC_FLAG_NATSRCNODE; + nvlist_add_number(nvl, "sync_flags", flags); + + return (nvl); + +errout: + nvlist_destroy(nvl); + return (NULL); +} + static int pf_ioctl_addrule(struct pf_krule *rule, uint32_t ticket, uint32_t pool_ticket, const char *anchor, const char *anchor_call, @@ -2789,6 +2941,7 @@ pfioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flags, struct thread *td case DIOCGETADDRS: case DIOCGETADDR: case DIOCGETSTATE: + case DIOCGETSTATENV: case DIOCSETSTATUSIF: case DIOCGETSTATUS: case DIOCCLRSTATUS: @@ -2844,6 +2997,7 @@ pfioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flags, struct thread *td case DIOCGETADDRS: case DIOCGETADDR: case DIOCGETSTATE: + case DIOCGETSTATENV: case DIOCGETSTATUS: case DIOCGETSTATES: case DIOCGETTIMEOUT: @@ -3504,6 +3658,11 @@ pfioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flags, struct thread *td break; } + case DIOCGETSTATENV: { + error = pf_getstate((struct pfioc_nv *)addr); + break; + } + case DIOCGETSTATES: { struct pfioc_states *ps = (struct pfioc_states *)addr; struct pf_state *s; @@ -5684,12 +5843,79 @@ pf_clearstates_nv(struct pfioc_nv *nv) error = copyout(nvlpacked, nv->data, nv->len); +#undef ERROUT on_error: nvlist_destroy(nvl); free(nvlpacked, M_TEMP); return (error); } +static int +pf_getstate(struct pfioc_nv *nv) +{ + nvlist_t *nvl = NULL, *nvls; + void *nvlpacked = NULL; + struct pf_state *s = NULL; + int error = 0; + uint64_t id, creatorid; + +#define ERROUT(x) ERROUT_FUNCTION(errout, x) + + if (nv->len > pf_ioctl_maxcount) + ERROUT(ENOMEM); + + nvlpacked = malloc(nv->len, M_TEMP, M_WAITOK); + if (nvlpacked == NULL) + ERROUT(ENOMEM); + + error = copyin(nv->data, nvlpacked, nv->len); + if (error) + ERROUT(error); + + nvl = nvlist_unpack(nvlpacked, nv->len, 0); + if (nvl == NULL) + ERROUT(EBADMSG); + + PFNV_CHK(pf_nvuint64(nvl, "id", &id)); + PFNV_CHK(pf_nvuint64(nvl, "creatorid", &creatorid)); + + s = pf_find_state_byid(id, creatorid); + if (s == NULL) + ERROUT(ENOENT); + + free(nvlpacked, M_TEMP); + nvlpacked = NULL; + nvlist_destroy(nvl); + nvl = nvlist_create(0); + if (nvl == NULL) + ERROUT(ENOMEM); + + nvls = pf_state_to_nvstate(s); + if (nvls == NULL) + ERROUT(ENOMEM); + + nvlist_add_nvlist(nvl, "state", nvls); + + nvlpacked = nvlist_pack(nvl, &nv->len); + if (nvlpacked == NULL) + ERROUT(ENOMEM); + + if (nv->size == 0) + ERROUT(0); + else if (nv->size < nv->len) + ERROUT(ENOSPC); + + error = copyout(nvlpacked, nv->data, nv->len); + +#undef ERROUT +errout: + if (s != NULL) + PF_STATE_UNLOCK(s); + free(nvlpacked, M_TEMP); + nvlist_destroy(nvl); + return (error); +} + /* * XXX - Check for version missmatch!!! */