git: bc941291473d - main - pfctl: Use DIOCGETSTATESNV

Kristof Provost kp at FreeBSD.org
Thu May 20 11:54:52 UTC 2021


The branch main has been updated by kp:

URL: https://cgit.FreeBSD.org/src/commit/?id=bc941291473d8a2164e4ffc3f3e7e6a356cbe747

commit bc941291473d8a2164e4ffc3f3e7e6a356cbe747
Author:     Kristof Provost <kp at FreeBSD.org>
AuthorDate: 2021-05-10 14:51:38 +0000
Commit:     Kristof Provost <kp at FreeBSD.org>
CommitDate: 2021-05-20 10:49:27 +0000

    pfctl: Use DIOCGETSTATESNV
    
    Migrate to using the new nvlist-based DIOCGETSTATESNV call to obtain the
    states list.
    
    MFC after:      1 week
    Sponsored by:   Rubicon Communications, LLC ("Netgate")
    Differential Revision:  https://reviews.freebsd.org/D30244
---
 lib/libpfctl/libpfctl.c     | 173 ++++++++++++++++++++++++++++++++++++++++++++
 lib/libpfctl/libpfctl.h     |  60 +++++++++++++++
 sbin/pfctl/pf_print_state.c |  82 ++++++++++-----------
 sbin/pfctl/pfctl.c          |  49 ++++---------
 sbin/pfctl/pfctl.h          |   4 +-
 5 files changed, 289 insertions(+), 79 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,
diff --git a/sbin/pfctl/pf_print_state.c b/sbin/pfctl/pf_print_state.c
index e2f9d6efe609..7119308d195b 100644
--- a/sbin/pfctl/pf_print_state.c
+++ b/sbin/pfctl/pf_print_state.c
@@ -196,25 +196,27 @@ print_host(struct pf_addr *addr, u_int16_t port, sa_family_t af, int opts)
 }
 
 void
-print_seq(struct pfsync_state_peer *p)
+print_seq(struct pfctl_state_peer *p)
 {
 	if (p->seqdiff)
-		printf("[%u + %u](+%u)", ntohl(p->seqlo),
-		    ntohl(p->seqhi) - ntohl(p->seqlo), ntohl(p->seqdiff));
+		printf("[%u + %u](+%u)", p->seqlo,
+		    p->seqhi - p->seqlo, p->seqdiff);
 	else
-		printf("[%u + %u]", ntohl(p->seqlo),
-		    ntohl(p->seqhi) - ntohl(p->seqlo));
+		printf("[%u + %u]", p->seqlo,
+		    p->seqhi - p->seqlo);
 }
 
 void
-print_state(struct pfsync_state *s, int opts)
+print_state(struct pfctl_state *s, int opts)
 {
-	struct pfsync_state_peer *src, *dst;
-	struct pfsync_state_key *key, *sk, *nk;
+	struct pfctl_state_peer *src, *dst;
+	struct pfctl_state_key *key, *sk, *nk;
 	struct protoent *p;
 	int min, sec;
+	sa_family_t af;
+	uint8_t proto;
 #ifndef __NO_STRICT_ALIGNMENT
-	struct pfsync_state_key aligned_key[2];
+	struct pfctl_state_key aligned_key[2];
 
 	bcopy(&s->key, aligned_key, sizeof(aligned_key));
 	key = aligned_key;
@@ -222,48 +224,51 @@ print_state(struct pfsync_state *s, int opts)
 	key = s->key;
 #endif
 
+	af = s->key[PF_SK_WIRE].af;
+	proto = s->key[PF_SK_WIRE].proto;
+
 	if (s->direction == PF_OUT) {
 		src = &s->src;
 		dst = &s->dst;
 		sk = &key[PF_SK_STACK];
 		nk = &key[PF_SK_WIRE];
-		if (s->proto == IPPROTO_ICMP || s->proto == IPPROTO_ICMPV6) 
+		if (proto == IPPROTO_ICMP || proto == IPPROTO_ICMPV6)
 			sk->port[0] = nk->port[0];
 	} else {
 		src = &s->dst;
 		dst = &s->src;
 		sk = &key[PF_SK_WIRE];
 		nk = &key[PF_SK_STACK];
-		if (s->proto == IPPROTO_ICMP || s->proto == IPPROTO_ICMPV6) 
+		if (proto == IPPROTO_ICMP || proto == IPPROTO_ICMPV6)
 			sk->port[1] = nk->port[1];
 	}
 	printf("%s ", s->ifname);
-	if ((p = getprotobynumber(s->proto)) != NULL)
+	if ((p = getprotobynumber(proto)) != NULL)
 		printf("%s ", p->p_name);
 	else
-		printf("%u ", s->proto);
+		printf("%u ", proto);
 
-	print_host(&nk->addr[1], nk->port[1], s->af, opts);
-	if (PF_ANEQ(&nk->addr[1], &sk->addr[1], s->af) ||
+	print_host(&nk->addr[1], nk->port[1], af, opts);
+	if (PF_ANEQ(&nk->addr[1], &sk->addr[1], af) ||
 	    nk->port[1] != sk->port[1]) {
 		printf(" (");
-		print_host(&sk->addr[1], sk->port[1], s->af, opts);
+		print_host(&sk->addr[1], sk->port[1], af, opts);
 		printf(")");
 	}
 	if (s->direction == PF_OUT)
 		printf(" -> ");
 	else
 		printf(" <- ");
-	print_host(&nk->addr[0], nk->port[0], s->af, opts);
-	if (PF_ANEQ(&nk->addr[0], &sk->addr[0], s->af) ||
+	print_host(&nk->addr[0], nk->port[0], af, opts);
+	if (PF_ANEQ(&nk->addr[0], &sk->addr[0], af) ||
 	    nk->port[0] != sk->port[0]) {
 		printf(" (");
-		print_host(&sk->addr[0], sk->port[0], s->af, opts);
+		print_host(&sk->addr[0], sk->port[0], af, opts);
 		printf(")");
 	}
 
 	printf("    ");
-	if (s->proto == IPPROTO_TCP) {
+	if (proto == IPPROTO_TCP) {
 		if (src->state <= TCPS_TIME_WAIT &&
 		    dst->state <= TCPS_TIME_WAIT)
 			printf("   %s:%s\n", tcpstates[src->state],
@@ -290,16 +295,16 @@ print_state(struct pfsync_state *s, int opts)
 				    dst->wscale & PF_WSCALE_MASK);
 			printf("\n");
 		}
-	} else if (s->proto == IPPROTO_UDP && src->state < PFUDPS_NSTATES &&
+	} else if (proto == IPPROTO_UDP && src->state < PFUDPS_NSTATES &&
 	    dst->state < PFUDPS_NSTATES) {
 		const char *states[] = PFUDPS_NAMES;
 
 		printf("   %s:%s\n", states[src->state], states[dst->state]);
 #ifndef INET6
-	} else if (s->proto != IPPROTO_ICMP && src->state < PFOTHERS_NSTATES &&
+	} else if (proto != IPPROTO_ICMP && src->state < PFOTHERS_NSTATES &&
 	    dst->state < PFOTHERS_NSTATES) {
 #else
-	} else if (s->proto != IPPROTO_ICMP && s->proto != IPPROTO_ICMPV6 &&
+	} else if (proto != IPPROTO_ICMP && proto != IPPROTO_ICMPV6 &&
 	    src->state < PFOTHERS_NSTATES && dst->state < PFOTHERS_NSTATES) {
 #endif
 		/* XXX ICMP doesn't really have state levels */
@@ -311,10 +316,8 @@ print_state(struct pfsync_state *s, int opts)
 	}
 
 	if (opts & PF_OPT_VERBOSE) {
-		u_int64_t packets[2];
-		u_int64_t bytes[2];
-		u_int32_t creation = ntohl(s->creation);
-		u_int32_t expire = ntohl(s->expire);
+		u_int32_t creation = s->creation;
+		u_int32_t expire = s->expire;
 
 		sec = creation % 60;
 		creation /= 60;
@@ -327,19 +330,15 @@ print_state(struct pfsync_state *s, int opts)
 		expire /= 60;
 		printf(", expires in %.2u:%.2u:%.2u", expire, min, sec);
 
-		bcopy(s->packets[0], &packets[0], sizeof(u_int64_t));
-		bcopy(s->packets[1], &packets[1], sizeof(u_int64_t));
-		bcopy(s->bytes[0], &bytes[0], sizeof(u_int64_t));
-		bcopy(s->bytes[1], &bytes[1], sizeof(u_int64_t));
 		printf(", %ju:%ju pkts, %ju:%ju bytes",
-		    (uintmax_t )be64toh(packets[0]),
-		    (uintmax_t )be64toh(packets[1]),
-		    (uintmax_t )be64toh(bytes[0]),
-		    (uintmax_t )be64toh(bytes[1]));
-		if (ntohl(s->anchor) != -1)
-			printf(", anchor %u", ntohl(s->anchor));
-		if (ntohl(s->rule) != -1)
-			printf(", rule %u", ntohl(s->rule));
+		    s->packets[0],
+		    s->packets[1],
+		    s->bytes[0],
+		    s->bytes[1]);
+		if (s->anchor != -1)
+			printf(", anchor %u", s->anchor);
+		if (s->rule != -1)
+			printf(", rule %u", s->rule);
 		if (s->state_flags & PFSTATE_SLOPPY)
 			printf(", sloppy");
 		if (s->sync_flags & PFSYNC_FLAG_SRCNODE)
@@ -352,10 +351,9 @@ print_state(struct pfsync_state *s, int opts)
 		u_int64_t id;
 
 		bcopy(&s->id, &id, sizeof(u_int64_t));
-		printf("   id: %016jx creatorid: %08x",
-		    (uintmax_t )be64toh(id), ntohl(s->creatorid));
+		printf("   id: %016jx creatorid: %08x", id, s->creatorid);
 		printf("   gateway: ");
-		print_host(&s->rt_addr, 0, s->af, opts);
+		print_host(&s->rt_addr, 0, af, opts);
 		printf("\n");
 	}
 }
diff --git a/sbin/pfctl/pfctl.c b/sbin/pfctl/pfctl.c
index fd937cac9f63..f82d75198d61 100644
--- a/sbin/pfctl/pfctl.c
+++ b/sbin/pfctl/pfctl.c
@@ -1237,48 +1237,27 @@ done:
 int
 pfctl_show_states(int dev, const char *iface, int opts)
 {
-	struct pfioc_states ps;
-	struct pfsync_state *p;
-	char *inbuf = NULL, *newinbuf = NULL;
-	unsigned int len = 0;
-	int i, dotitle = (opts & PF_OPT_SHOWALL);
+	struct pfctl_states states;
+	struct pfctl_state *s;
+	int dotitle = (opts & PF_OPT_SHOWALL);
 
-	memset(&ps, 0, sizeof(ps));
-	for (;;) {
-		ps.ps_len = len;
-		if (len) {
-			newinbuf = realloc(inbuf, len);
-			if (newinbuf == NULL)
-				err(1, "realloc");
-			ps.ps_buf = inbuf = newinbuf;
-		}
-		if (ioctl(dev, DIOCGETSTATES, &ps) < 0) {
-			warn("DIOCGETSTATES");
-			free(inbuf);
-			return (-1);
-		}
-		if (ps.ps_len + sizeof(struct pfioc_states) < len)
-			break;
-		if (len == 0 && ps.ps_len == 0)
-			goto done;
-		if (len == 0 && ps.ps_len != 0)
-			len = ps.ps_len;
-		if (ps.ps_len == 0)
-			goto done;	/* no states */
-		len *= 2;
-	}
-	p = ps.ps_states;
-	for (i = 0; i < ps.ps_len; i += sizeof(*p), p++) {
-		if (iface != NULL && strcmp(p->ifname, iface))
+	memset(&states, 0, sizeof(states));
+
+	if (pfctl_get_states(dev, &states))
+		return (-1);
+
+	TAILQ_FOREACH(s, &states.states, entry) {
+		if (iface != NULL && strcmp(s->ifname, iface))
 			continue;
 		if (dotitle) {
 			pfctl_print_title("STATES:");
 			dotitle = 0;
 		}
-		print_state(p, opts);
+		print_state(s, opts);
 	}
-done:
-	free(inbuf);
+
+	pfctl_free_states(&states);
+
 	return (0);
 }
 
diff --git a/sbin/pfctl/pfctl.h b/sbin/pfctl/pfctl.h
index 93b3af083b5f..f8ff5012e01b 100644
--- a/sbin/pfctl/pfctl.h
+++ b/sbin/pfctl/pfctl.h
@@ -120,8 +120,8 @@ char		*rate2str(double);
 
 void	 print_addr(struct pf_addr_wrap *, sa_family_t, int);
 void	 print_host(struct pf_addr *, u_int16_t p, sa_family_t, int);
-void	 print_seq(struct pfsync_state_peer *);
-void	 print_state(struct pfsync_state *, int);
+void	 print_seq(struct pfctl_state_peer *);
+void	 print_state(struct pfctl_state *, int);
 int	 unmask(struct pf_addr *, sa_family_t);
 
 int	 pfctl_cmdline_symset(char *);


More information about the dev-commits-src-all mailing list