diff --git a/lib/libpfctl/libpfctl.h b/lib/libpfctl/libpfctl.h --- a/lib/libpfctl/libpfctl.h +++ b/lib/libpfctl/libpfctl.h @@ -413,6 +413,7 @@ const char *anchor, const char *anchor_call, uint32_t ticket, uint32_t pool_ticket); int pfctl_set_keepcounters(int dev, bool keep); +int pfctl_get_creatorids(uint32_t *creators, size_t *len); typedef int (*pfctl_get_state_fn)(struct pfctl_state *, void *); int pfctl_get_states_iter(pfctl_get_state_fn f, void *arg); int pfctl_get_states(int dev, struct pfctl_states *states); diff --git a/lib/libpfctl/libpfctl.c b/lib/libpfctl/libpfctl.c --- a/lib/libpfctl/libpfctl.c +++ b/lib/libpfctl/libpfctl.c @@ -1106,6 +1106,71 @@ return (ret); } +struct pfctl_creator { + uint32_t id; +}; +#define _IN(_field) offsetof(struct genlmsghdr, _field) +#define _OUT(_field) offsetof(struct pfctl_creator, _field) +static struct snl_attr_parser ap_creators[] = { + { .type = PF_ST_CREATORID, .off = _OUT(id), .cb = snl_attr_get_uint32 }, +}; +static struct snl_field_parser fp_creators[] = { +}; +#undef _IN +#undef _OUT +SNL_DECLARE_PARSER(creator_parser, struct genlmsghdr, fp_creators, ap_creators); + +static int +pfctl_get_creators_nl(struct snl_state *ss, uint32_t *creators, size_t *len) +{ + + int family_id = snl_get_genl_family(ss, PFNL_FAMILY_NAME); + size_t i = 0; + + struct nlmsghdr *hdr; + struct snl_writer nw; + + snl_init_writer(ss, &nw); + hdr = snl_create_genl_msg_request(&nw, family_id, PFNL_CMD_GETCREATORS); + hdr->nlmsg_flags |= NLM_F_DUMP; + snl_finalize_msg(&nw); + uint32_t seq_id = hdr->nlmsg_seq; + + snl_send_message(ss, hdr); + + struct snl_errmsg_data e = {}; + while ((hdr = snl_read_reply_multi(ss, seq_id, &e)) != NULL) { + struct pfctl_creator c; + bzero(&c, sizeof(c)); + + if (!snl_parse_nlmsg(ss, hdr, &creator_parser, &c)) + continue; + + creators[i] = c.id; + i++; + if (i > *len) + return (E2BIG); + } + + *len = i; + + return (0); +} + +int +pfctl_get_creatorids(uint32_t *creators, size_t *len) +{ + struct snl_state ss = {}; + int error; + + snl_init(&ss, NETLINK_GENERIC); + error = pfctl_get_creators_nl(&ss, creators, len); + snl_free(&ss); + + return (error); + +} + static void pfctl_nv_add_state_cmp(nvlist_t *nvl, const char *name, const struct pfctl_state_cmp *cmp) @@ -1199,7 +1264,8 @@ SNL_DECLARE_PARSER(state_parser, struct genlmsghdr, fp_state, ap_state); static const struct snl_hdr_parser *all_parsers[] = { - &state_parser, &skey_parser, &speer_parser + &state_parser, &skey_parser, &speer_parser, + &creator_parser, }; static int diff --git a/sbin/pfctl/pfctl.h b/sbin/pfctl/pfctl.h --- a/sbin/pfctl/pfctl.h +++ b/sbin/pfctl/pfctl.h @@ -88,6 +88,7 @@ int pfctl_show_altq(int, const char *, int, int); void warn_namespace_collision(const char *); int pfctl_show_ifaces(const char *, int); +void pfctl_show_creators(int); FILE *pfctl_fopen(const char *, const char *); #ifdef __FreeBSD__ diff --git a/sbin/pfctl/pfctl.c b/sbin/pfctl/pfctl.c --- a/sbin/pfctl/pfctl.c +++ b/sbin/pfctl/pfctl.c @@ -233,7 +233,7 @@ static const char * const showopt_list[] = { "ether", "nat", "queue", "rules", "Anchors", "Sources", "states", "info", "Interfaces", "labels", "timeouts", "memory", "Tables", - "osfp", "Running", "all", NULL + "osfp", "Running", "all", "creatorids", NULL }; static const char * const tblcmdopt_list[] = { @@ -1639,6 +1639,22 @@ return (0); } +void +pfctl_show_creators(int opts) +{ + int ret; + uint32_t creators[16]; + size_t count = nitems(creators); + + ret = pfctl_get_creatorids(creators, &count); + if (ret != 0) + errx(ret, "Failed to retrieve creators"); + + printf("Creator IDs:\n"); + for (size_t i = 0; i < count; i++) + printf("%x\n", creators[i]); +} + /* callbacks for rule/nat/rdr/addr */ int pfctl_add_pool(struct pfctl *pf, struct pfctl_pool *p, sa_family_t af) @@ -3121,6 +3137,9 @@ case 'I': pfctl_show_ifaces(ifaceopt, opts); break; + case 'c': + pfctl_show_creators(opts); + break; } } diff --git a/sys/netpfil/pf/pf_nl.h b/sys/netpfil/pf/pf_nl.h --- a/sys/netpfil/pf/pf_nl.h +++ b/sys/netpfil/pf/pf_nl.h @@ -37,6 +37,7 @@ enum { PFNL_CMD_UNSPEC = 0, PFNL_CMD_GETSTATES = 1, + PFNL_CMD_GETCREATORS = 2, __PFNL_CMD_MAX, }; #define PFNL_CMD_MAX (__PFNL_CMD_MAX -1) diff --git a/sys/netpfil/pf/pf_nl.c b/sys/netpfil/pf/pf_nl.c --- a/sys/netpfil/pf/pf_nl.c +++ b/sys/netpfil/pf/pf_nl.c @@ -246,6 +246,30 @@ return (dump_state(nlp, hdr, s, npt)); } +static int +dump_creatorid(struct nlpcb *nlp, const struct nlmsghdr *hdr, uint32_t creator, + struct nl_pstate *npt) +{ + struct nl_writer *nw = npt->nw; + + if (!nlmsg_reply(nw, hdr, sizeof(struct genlmsghdr))) + goto enomem; + + struct genlmsghdr *ghdr_new = nlmsg_reserve_object(nw, struct genlmsghdr); + ghdr_new->cmd = PFNL_CMD_GETCREATORS; + ghdr_new->version = 0; + ghdr_new->reserved = 0; + + nlattr_add_u32(nw, PF_ST_CREATORID, htonl(creator)); + + if (nlmsg_end(nw)) + return (0); + +enomem: + nlmsg_abort(nw); + return (ENOMEM); +} + static int pf_handle_getstates(struct nlmsghdr *hdr, struct nl_pstate *npt) { @@ -264,6 +288,56 @@ return (error); } +static int +pf_handle_getcreators(struct nlmsghdr *hdr, struct nl_pstate *npt) +{ + uint32_t creators[16]; + int error = 0; + + bzero(creators, sizeof(creators)); + + for (int i = 0; i < pf_hashmask; i++) { + struct pf_idhash *ih = &V_pf_idhash[i]; + struct pf_kstate *s; + + if (LIST_EMPTY(&ih->states)) + continue; + + PF_HASHROW_LOCK(ih); + LIST_FOREACH(s, &ih->states, entry) { + int j; + if (s->timeout == PFTM_UNLINKED) + continue; + + for (j = 0; j < nitems(creators); j++) { + if (creators[j] == s->creatorid) + break; + if (creators[j] == 0) { + creators[j] = s->creatorid; + break; + } + } + if (j == nitems(creators)) + printf("Warning: too many creators!\n"); + } + PF_HASHROW_UNLOCK(ih); + } + + hdr->nlmsg_flags |= NLM_F_MULTI; + for (int i = 0; i < nitems(creators); i++) { + if (creators[i] == 0) + break; + error = dump_creatorid(npt->nlp, hdr, creators[i], npt); + } + + if (!nlmsg_end_dump(npt->nw, error, hdr)) { + NL_LOG(LOG_DEBUG, "Unable to finalize the dump"); + return (ENOMEM); + } + + return (error); +} + static const struct nlhdr_parser *all_parsers[] = { &state_parser }; static int family_id; @@ -275,6 +349,12 @@ .cmd_cb = pf_handle_getstates, .cmd_flags = GENL_CMD_CAP_DO | GENL_CMD_CAP_DUMP | GENL_CMD_CAP_HASPOL, }, + { + .cmd_num = PFNL_CMD_GETCREATORS, + .cmd_name = "GETCREATORS", + .cmd_cb = pf_handle_getcreators, + .cmd_flags = GENL_CMD_CAP_DO | GENL_CMD_CAP_DUMP | GENL_CMD_CAP_HASPOL, + }, }; void diff --git a/tests/sys/netpfil/pf/pfsync.sh b/tests/sys/netpfil/pf/pfsync.sh --- a/tests/sys/netpfil/pf/pfsync.sh +++ b/tests/sys/netpfil/pf/pfsync.sh @@ -78,6 +78,8 @@ "set skip on ${epair_sync}b" \ "pass out keep state" + hostid_one=$(jexec one pfctl -si -v | awk '/Hostid:/ { gsub(/0x/, "", $2); printf($2); }') + ifconfig ${epair_one}b 198.51.100.254/24 up ping -c 1 -S 198.51.100.254 198.51.100.1 @@ -89,6 +91,12 @@ grep 198.51.100.254 ; then atf_fail "state not found on synced host" fi + + if ! jexec two pfctl -sc | grep ""${hostid_one}""; + then + jexec two pfctl -sc + atf_fail "HostID for host one not found on two" + fi } basic_cleanup()