diff --git a/share/man/man4/pf.4 b/share/man/man4/pf.4 index 24843535c924..133e4d300043 100644 --- a/share/man/man4/pf.4 +++ b/share/man/man4/pf.4 @@ -415,30 +415,65 @@ Set the debug level. enum { PF_DEBUG_NONE, PF_DEBUG_URGENT, PF_DEBUG_MISC, PF_DEBUG_NOISY }; .Ed -.It Dv DIOCGETSTATES Fa "struct pfioc_states *ps" +.It Dv DIOCGETSTATESNV Fa "struct pfioc_nv *nv" Get state table entries. .Bd -literal -struct pfioc_states { - int ps_len; - union { - caddr_t psu_buf; - struct pf_state *psu_states; - } ps_u; -#define ps_buf ps_u.psu_buf -#define ps_states ps_u.psu_states +nvlist pf_state_key { + nvlist pf_addr addr[2]; + number port[2]; + number af; + number proto; +}; + +nvlist pf_state_scrub { + bool timestamp; + number ttl; + number ts_mod; +}; + +nvlist pf_state_peer { + nvlist pf_state_scrub scrub; + number seqlo; + number seqhi; + number seqdiff; + number max_win; + number mss; + number state; + number wscale; +}; + +nvlist pf_state { + number id; + string ifname; + nvlist pf_state_key stack_key; + nvlist pf_state_key wire_key; + nvlist pf_state_peer src; + nvlist pf_state_peer dst; + nvlist pf_addr rt_addr; + number rule; + number anchor; + number nat_rule; + number expire; + number packets[2]; + number bytes[2]; + number creatorid; + number direction; + number log; + number state_flags; + number timeout; + number sync_flags; +}; + +nvlist pf_states { + number count; + nvlist pf_state states[]; }; .Ed .Pp If -.Va ps_len -is non-zero on entry, as many states as possible that can fit into this -size will be copied into the supplied buffer -.Va ps_states . -On exit, -.Va ps_len -is always set to the total size required to hold all state table entries -(i.e., it is set to -.Li sizeof(struct pf_state) * nr ) . +.Va pfioc_nv.size +is insufficiently large, as many states as possible that can fit into this +size will be copied into the supplied buffer. .It Dv DIOCCHANGERULE Fa "struct pfioc_rule *pcr" Add or remove the .Va rule diff --git a/sys/net/pfvar.h b/sys/net/pfvar.h index 1bd1ebd27449..d9e35dae753a 100644 --- a/sys/net/pfvar.h +++ b/sys/net/pfvar.h @@ -1264,6 +1264,7 @@ struct pfioc_iface { #define DIOCNATLOOK _IOWR('D', 23, struct pfioc_natlook) #define DIOCSETDEBUG _IOWR('D', 24, u_int32_t) #define DIOCGETSTATES _IOWR('D', 25, struct pfioc_states) +#define DIOCGETSTATESNV _IOWR('D', 25, struct pfioc_nv) #define DIOCCHANGERULE _IOWR('D', 26, struct pfioc_rule) /* XXX cut 26 - 28 */ #define DIOCSETTIMEOUT _IOWR('D', 29, struct pfioc_tm) diff --git a/sys/netpfil/pf/pf_ioctl.c b/sys/netpfil/pf/pf_ioctl.c index 1ea7310ebebb..8424e0ce5689 100644 --- a/sys/netpfil/pf/pf_ioctl.c +++ b/sys/netpfil/pf/pf_ioctl.c @@ -209,6 +209,7 @@ static int pf_killstates_row(struct pf_kstate_kill *, 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_getstates(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 *); @@ -2948,6 +2949,7 @@ pfioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flags, struct thread *td case DIOCNATLOOK: case DIOCSETDEBUG: case DIOCGETSTATES: + case DIOCGETSTATESNV: case DIOCGETTIMEOUT: case DIOCCLRRULECTRS: case DIOCGETLIMIT: @@ -3000,6 +3002,7 @@ pfioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flags, struct thread *td case DIOCGETSTATENV: case DIOCGETSTATUS: case DIOCGETSTATES: + case DIOCGETSTATESNV: case DIOCGETTIMEOUT: case DIOCGETLIMIT: case DIOCGETALTQSV0: @@ -3709,6 +3712,11 @@ pfioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flags, struct thread *td break; } + case DIOCGETSTATESNV: { + error = pf_getstates((struct pfioc_nv *)addr); + break; + } + case DIOCGETSTATUS: { struct pf_status *s = (struct pf_status *)addr; @@ -5916,6 +5924,74 @@ pf_getstate(struct pfioc_nv *nv) return (error); } +static int +pf_getstates(struct pfioc_nv *nv) +{ + nvlist_t *nvl = NULL, *nvls; + void *nvlpacked = NULL; + struct pf_state *s = NULL; + int error = 0; + uint64_t count = 0; + +#define ERROUT(x) ERROUT_FUNCTION(errout, x) + + nvl = nvlist_create(0); + if (nvl == NULL) + ERROUT(ENOMEM); + + nvlist_add_number(nvl, "count", uma_zone_get_cur(V_pf_state_z)); + + for (int i = 0; i < pf_hashmask; i++) { + struct pf_idhash *ih = &V_pf_idhash[i]; + + PF_HASHROW_LOCK(ih); + LIST_FOREACH(s, &ih->states, entry) { + if (s->timeout == PFTM_UNLINKED) + continue; + + nvls = pf_state_to_nvstate(s); + if (nvls == NULL) { + PF_HASHROW_UNLOCK(ih); + ERROUT(ENOMEM); + } + if ((nvlist_size(nvl) + nvlist_size(nvls)) > nv->size) { + /* We've run out of room for more states. */ + nvlist_destroy(nvls); + PF_HASHROW_UNLOCK(ih); + goto DIOCGETSTATESNV_full; + } + nvlist_append_nvlist_array(nvl, "states", nvls); + count++; + } + PF_HASHROW_UNLOCK(ih); + } + + /* We've managed to put them all the available space. Let's make sure + * 'count' matches our array (that's racy, because we don't hold a lock + * over all states, only over each row individually. */ + (void)nvlist_take_number(nvl, "count"); + nvlist_add_number(nvl, "count", count); + +DIOCGETSTATESNV_full: + + 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: + free(nvlpacked, M_TEMP); + nvlist_destroy(nvl); + return (error); +} + /* * XXX - Check for version missmatch!!! */