Index: sys/net/pfvar.h =================================================================== --- sys/net/pfvar.h +++ sys/net/pfvar.h @@ -40,6 +40,7 @@ #include #include #include +#include #include #include #include @@ -1234,6 +1235,7 @@ #define DIOCADDRULENV _IOWR('D', 4, struct pfioc_nv) #define DIOCGETRULES _IOWR('D', 6, struct pfioc_rule) #define DIOCGETRULE _IOWR('D', 7, struct pfioc_rule) +#define DIOCGETRULENV _IOWR('D', 7, struct pfioc_nv) /* XXX cut 8 - 17 */ #define DIOCCLRSTATES _IOWR('D', 18, struct pfioc_state_kill) #define DIOCGETSTATE _IOWR('D', 19, struct pfioc_state) @@ -1637,6 +1639,8 @@ void pf_init_kruleset(struct pf_kruleset *); int pf_kanchor_setup(struct pf_krule *, const struct pf_kruleset *, const char *); +int pf_kanchor_nvcopyout(const struct pf_kruleset *, + const struct pf_krule *, nvlist_t *); int pf_kanchor_copyout(const struct pf_kruleset *, const struct pf_krule *, struct pfioc_rule *); void pf_kanchor_remove(struct pf_krule *); Index: sys/netpfil/pf/pf_ioctl.c =================================================================== --- sys/netpfil/pf/pf_ioctl.c +++ sys/netpfil/pf/pf_ioctl.c @@ -1632,6 +1632,20 @@ return (pf_nvbinary(nvl, "addr", paddr, sizeof(struct in6_addr))); } +static nvlist_t * +pf_addr_to_nvaddr(const struct pf_addr *paddr) +{ + nvlist_t *nvl; + + nvl = nvlist_create(0); + if (nvl == NULL) + return (NULL); + + nvlist_add_binary(nvl, "addr", paddr, sizeof(struct in6_addr)); + + return (nvl); +} + static int pf_nvpool_to_pool(const nvlist_t *nvl, struct pf_kpool *kpool) { @@ -1655,6 +1669,33 @@ return (error); } +static nvlist_t * +pf_pool_to_nvpool(const struct pf_kpool *pool) +{ + nvlist_t *nvl; + nvlist_t *tmp; + + nvl = nvlist_create(0); + if (nvl == NULL) + return (NULL); + + nvlist_add_binary(nvl, "key", &pool->key, sizeof(pool->key)); + tmp = pf_addr_to_nvaddr(&pool->counter); + if (tmp == NULL) + goto error; + nvlist_add_nvlist(nvl, "counter", tmp); + + nvlist_add_number(nvl, "tblidx", pool->tblidx); + pf_uint_16_array_nv(nvl, "proxy_port", pool->proxy_port, 2); + nvlist_add_number(nvl, "opts", pool->opts); + + return (nvl); + +error: + nvlist_destroy(nvl); + return (NULL); +} + static int pf_nvaddr_wrap_to_addr_wrap(const nvlist_t *nvl, struct pf_addr_wrap *addr) { @@ -1695,6 +1736,37 @@ return (error); } +static nvlist_t * +pf_addr_wrap_to_nvaddr_wrap(const struct pf_addr_wrap *addr) +{ + nvlist_t *nvl; + nvlist_t *tmp; + + nvl = nvlist_create(0); + if (nvl == NULL) + return (NULL); + + nvlist_add_number(nvl, "type", addr->type); + nvlist_add_number(nvl, "iflags", addr->iflags); + nvlist_add_string(nvl, "ifname", addr->v.ifname); + nvlist_add_string(nvl, "tblname", addr->v.tblname); + + tmp = pf_addr_to_nvaddr(&addr->v.a.addr); + if (tmp == NULL) + goto error; + nvlist_add_nvlist(nvl, "addr", tmp); + tmp = pf_addr_to_nvaddr(&addr->v.a.mask); + if (tmp == NULL) + goto error; + nvlist_add_nvlist(nvl, "mask", tmp); + + return (nvl); + +error: + nvlist_destroy(nvl); + return (NULL); +} + static int pf_validate_op(u_int8_t op) { @@ -1737,6 +1809,33 @@ return (error); } +static nvlist_t * +pf_rule_addr_to_nvrule_addr(const struct pf_rule_addr *addr) +{ + nvlist_t *nvl; + nvlist_t *tmp; + + nvl = nvlist_create(0); + if (nvl == NULL) + return (NULL); + + tmp = pf_addr_wrap_to_nvaddr_wrap(&addr->addr); + if (tmp == NULL) + goto error; + nvlist_add_nvlist(nvl, "addr", tmp); + pf_uint_16_array_nv(nvl, "port", addr->port, 2); + nvlist_add_number(nvl, "neg", addr->neg); + nvlist_add_number(nvl, "port_op", addr->port_op); + + return (nvl); + +error: + nvlist_destroy(nvl); + return (NULL); +} + + + static int pf_nvrule_uid_to_rule_uid(const nvlist_t *nvl, struct pf_rule_uid *uid) { @@ -1753,6 +1852,21 @@ return (error); } +static nvlist_t * +pf_rule_uid_to_nvrule_uid(const struct pf_rule_uid *uid) +{ + nvlist_t *nvl; + + nvl = nvlist_create(0); + if (nvl == NULL) + return (NULL); + + pf_uint_32_array_nv(nvl, "uid", uid->uid, 2); + nvlist_add_number(nvl, "op", uid->op); + + return (nvl); +} + static int pf_nvrule_gid_to_rule_gid(const nvlist_t *nvl, struct pf_rule_gid *gid) { @@ -1913,6 +2027,157 @@ return (error); } +static nvlist_t * +pf_divert_to_nvdivert(const struct pf_krule *rule) +{ + nvlist_t *nvl; + nvlist_t *tmp; + + nvl = nvlist_create(0); + if (nvl == NULL) + return (NULL); + + tmp = pf_addr_to_nvaddr(&rule->divert.addr); + if (tmp == NULL) + goto error; + nvlist_add_nvlist(nvl, "addr", tmp); + nvlist_add_number(nvl, "port", rule->divert.port); + + return (nvl); + +error: + nvlist_destroy(nvl); + return (NULL); +} + +static nvlist_t * +pf_krule_to_nvrule(const struct pf_krule *rule) +{ + nvlist_t *nvl, *tmp; + + nvl = nvlist_create(0); + if (nvl == NULL) + return (nvl); + + nvlist_add_number(nvl, "nr", rule->nr); + tmp = pf_rule_addr_to_nvrule_addr(&rule->src); + if (tmp == NULL) + goto error; + nvlist_add_nvlist(nvl, "src", tmp); + tmp = pf_rule_addr_to_nvrule_addr(&rule->dst); + if (tmp == NULL) + goto error; + nvlist_add_nvlist(nvl, "dst", tmp); + + for (int i = 0; i < PF_SKIP_COUNT; i++) { + nvlist_append_number_array(nvl, "skip", + rule->skip[i].ptr ? rule->skip[i].ptr->nr : -1); + } + + nvlist_add_string(nvl, "label", rule->label); + nvlist_add_string(nvl, "ifname", rule->ifname); + nvlist_add_string(nvl, "qname", rule->qname); + nvlist_add_string(nvl, "pqname", rule->pqname); + nvlist_add_string(nvl, "tagname", rule->tagname); + nvlist_add_string(nvl, "match_tagname", rule->match_tagname); + nvlist_add_string(nvl, "overload_tblname", rule->overload_tblname); + + tmp = pf_pool_to_nvpool(&rule->rpool); + if (tmp == NULL) + goto error; + nvlist_add_nvlist(nvl, "rpool", tmp); + + nvlist_add_number(nvl, "evaluations", + counter_u64_fetch(rule->evaluations)); + for (int i = 0; i < 2; i++) { + nvlist_append_number_array(nvl, "packets", + counter_u64_fetch(rule->packets[i])); + nvlist_append_number_array(nvl, "bytes", + counter_u64_fetch(rule->bytes[i])); + } + + nvlist_add_number(nvl, "os_fingerprint", rule->os_fingerprint); + + nvlist_add_number(nvl, "rtableid", rule->rtableid); + pf_uint_32_array_nv(nvl, "timeout", rule->timeout, PFTM_MAX); + nvlist_add_number(nvl, "max_states", rule->max_states); + nvlist_add_number(nvl, "max_src_nodes", rule->max_src_nodes); + nvlist_add_number(nvl, "max_src_states", rule->max_src_states); + nvlist_add_number(nvl, "max_src_conn", rule->max_src_conn); + nvlist_add_number(nvl, "max_src_conn_rate.limit", + rule->max_src_conn_rate.limit); + nvlist_add_number(nvl, "max_src_conn_rate.seconds", + rule->max_src_conn_rate.seconds); + nvlist_add_number(nvl, "rt_listid", rule->rt_listid); + nvlist_add_number(nvl, "prob", rule->prob); + nvlist_add_number(nvl, "cuid", rule->cuid); + nvlist_add_number(nvl, "cpid", rule->cpid); + + nvlist_add_number(nvl, "states_cur", + counter_u64_fetch(rule->states_cur)); + nvlist_add_number(nvl, "states_tot", + counter_u64_fetch(rule->states_tot)); + nvlist_add_number(nvl, "src_nodes", + counter_u64_fetch(rule->src_nodes)); + + nvlist_add_number(nvl, "return_icmp", rule->return_icmp); + nvlist_add_number(nvl, "return_icmp6", rule->return_icmp6); + + nvlist_add_number(nvl, "max_mss", rule->max_mss); + nvlist_add_number(nvl, "scrub_flags", rule->scrub_flags); + + tmp = pf_rule_uid_to_nvrule_uid(&rule->uid); + if (tmp == NULL) + goto error; + nvlist_add_nvlist(nvl, "uid", tmp); + tmp = pf_rule_uid_to_nvrule_uid((const struct pf_rule_uid *)&rule->gid); + if (tmp == NULL) + goto error; + nvlist_add_nvlist(nvl, "gid", tmp); + + nvlist_add_number(nvl, "rule_flag", rule->rule_flag); + nvlist_add_number(nvl, "action", rule->action); + nvlist_add_number(nvl, "direction", rule->direction); + nvlist_add_number(nvl, "log", rule->log); + nvlist_add_number(nvl, "logif", rule->logif); + nvlist_add_number(nvl, "quick", rule->quick); + nvlist_add_number(nvl, "ifnot", rule->ifnot); + nvlist_add_number(nvl, "match_tag_not", rule->match_tag_not); + nvlist_add_number(nvl, "natpass", rule->natpass); + + nvlist_add_number(nvl, "keep_state", rule->keep_state); + nvlist_add_number(nvl, "af", rule->af); + nvlist_add_number(nvl, "proto", rule->proto); + nvlist_add_number(nvl, "type", rule->type); + nvlist_add_number(nvl, "code", rule->code); + nvlist_add_number(nvl, "flags", rule->flags); + nvlist_add_number(nvl, "flagset", rule->flagset); + nvlist_add_number(nvl, "min_ttl", rule->min_ttl); + nvlist_add_number(nvl, "allow_opts", rule->allow_opts); + nvlist_add_number(nvl, "rt", rule->rt); + nvlist_add_number(nvl, "return_ttl", rule->return_ttl); + nvlist_add_number(nvl, "tos", rule->tos); + nvlist_add_number(nvl, "set_tos", rule->set_tos); + nvlist_add_number(nvl, "anchor_relative", rule->anchor_relative); + nvlist_add_number(nvl, "anchor_wildcard", rule->anchor_wildcard); + + nvlist_add_number(nvl, "flush", rule->flush); + nvlist_add_number(nvl, "prio", rule->prio); + + pf_uint_8_array_nv(nvl, "set_prio", &rule->prio, 2); + + tmp = pf_divert_to_nvdivert(rule); + if (tmp == NULL) + goto error; + nvlist_add_nvlist(nvl, "divert", tmp); + + return (nvl); + +error: + nvlist_destroy(nvl); + return (NULL); +} + static int pf_rule_to_krule(const struct pf_rule *rule, struct pf_krule *krule) { @@ -2190,6 +2455,7 @@ switch (cmd) { case DIOCGETRULES: case DIOCGETRULE: + case DIOCGETRULENV: case DIOCGETADDRS: case DIOCGETADDR: case DIOCGETSTATE: @@ -2271,6 +2537,7 @@ case DIOCIGETIFACES: case DIOCGIFSPEEDV1: case DIOCGIFSPEEDV0: + case DIOCGETRULENV: break; case DIOCRCLRTABLES: case DIOCRADDTABLES: @@ -2491,6 +2758,129 @@ break; } + case DIOCGETRULENV: { + struct pfioc_nv *nv = (struct pfioc_nv *)addr; + nvlist_t *nvrule = NULL; + nvlist_t *nvl = NULL; + struct pf_kruleset *ruleset; + struct pf_krule *rule; + void *nvlpacked = NULL; + int rs_num, nr; + bool clear_counter = false; + +#define ERROUT(x) do { error = (x); goto DIOCGETRULENV_error; } while (0) + + /* Copy the request in */ + nvlpacked = malloc(nv->len, M_TEMP, M_WAITOK); + if (nvlpacked == NULL) + ERROUT(ENOMEM); + + error = copyin(nv->data, nvlpacked, nv->len); + if (error) + ERROUT(ENOMEM); + + nvl = nvlist_unpack(nvlpacked, nv->len, 0); + if (nvl == NULL) + ERROUT(EBADMSG); + + if (! nvlist_exists_string(nvl, "anchor")) + ERROUT(EBADMSG); + if (! nvlist_exists_number(nvl, "ruleset")) + ERROUT(EBADMSG); + if (! nvlist_exists_number(nvl, "ticket")) + ERROUT(EBADMSG); + if (! nvlist_exists_number(nvl, "nr")) + ERROUT(EBADMSG); + + if (nvlist_exists_bool(nvl, "clear_counter")) + clear_counter = nvlist_get_bool(nvl, "clear_counter"); + + if (clear_counter && !(flags & FWRITE)) + ERROUT(EACCES); + + nr = nvlist_get_number(nvl, "nr"); + + PF_RULES_WLOCK(); + ruleset = pf_find_kruleset(nvlist_get_string(nvl, "anchor")); + if (ruleset == NULL) { + PF_RULES_WUNLOCK(); + ERROUT(ENOENT); + } + + rs_num = pf_get_ruleset_number(nvlist_get_number(nvl, "ruleset")); + if (rs_num >= PF_RULESET_MAX) { + PF_RULES_WUNLOCK(); + ERROUT(EINVAL); + } + + if (nvlist_get_number(nvl, "ticket") != + ruleset->rules[rs_num].active.ticket) { + PF_RULES_WUNLOCK(); + ERROUT(EBUSY); + break; + } + rule = TAILQ_FIRST(ruleset->rules[rs_num].active.ptr); + while ((rule != NULL) && (rule->nr != nr)) + rule = TAILQ_NEXT(rule, entries); + if (rule == NULL) { + PF_RULES_WUNLOCK(); + ERROUT(EBUSY); + break; + } + + nvrule = pf_krule_to_nvrule(rule); + + nvlist_destroy(nvl); + nvl = nvlist_create(0); + if (nvl == NULL) { + PF_RULES_WUNLOCK(); + ERROUT(ENOMEM); + } + nvlist_add_number(nvl, "nr", nr); + nvlist_add_nvlist(nvl, "rule", nvrule); + nvrule = NULL; + if (pf_kanchor_nvcopyout(ruleset, rule, nvl)) { + PF_RULES_WUNLOCK(); + ERROUT(EBUSY); + } + + free(nvlpacked, M_TEMP); + nvlpacked = nvlist_pack(nvl, &nv->len); + if (nvlpacked == NULL) { + PF_RULES_WUNLOCK(); + ERROUT(ENOMEM); + } + + if (nv->size == 0) { + PF_RULES_WUNLOCK(); + ERROUT(0); + } + else if (nv->size < nv->len) { + PF_RULES_WUNLOCK(); + ERROUT(ENOSPC); + } + + error = copyout(nvlpacked, nv->data, nv->len); + + if (clear_counter) { + counter_u64_zero(rule->evaluations); + for (int i = 0; i < 2; i++) { + counter_u64_zero(rule->packets[i]); + counter_u64_zero(rule->bytes[i]); + } + counter_u64_zero(rule->states_tot); + } + PF_RULES_WUNLOCK(); + +#undef ERROUT +DIOCGETRULENV_error: + free(nvlpacked, M_TEMP); + nvlist_destroy(nvrule); + nvlist_destroy(nvl); + + break; + } + case DIOCCHANGERULE: { struct pfioc_rule *pcr = (struct pfioc_rule *)addr; struct pf_kruleset *ruleset; Index: sys/netpfil/pf/pf_nv.h =================================================================== --- sys/netpfil/pf/pf_nv.h +++ sys/netpfil/pf/pf_nv.h @@ -36,12 +36,19 @@ int pf_nvuint_8(const nvlist_t *, const char *, u_int8_t *); int pf_nvuint_8_array(const nvlist_t *, const char *, u_int8_t *, size_t, size_t *); +void pf_uint_8_array_nv(nvlist_t *, const char *, const u_int8_t *, + size_t); int pf_nvuint_16(const nvlist_t *, const char *, u_int16_t *); int pf_nvuint_16_array(const nvlist_t *, const char *, u_int16_t *, size_t, size_t *); +void pf_uint_16_array_nv(nvlist_t *, const char *, const u_int16_t *, + size_t); int pf_nvuint_32(const nvlist_t *, const char *, u_int32_t *); int pf_nvuint_32_array(const nvlist_t *, const char *, u_int32_t *, size_t, size_t *); +void pf_uint_32_array_nv(nvlist_t *, const char *, const u_int32_t *, + size_t); + int pf_nvstring(const nvlist_t *, const char *, char *, size_t); #define PFNV_CHK(x) do { \ Index: sys/netpfil/pf/pf_nv.c =================================================================== --- sys/netpfil/pf/pf_nv.c +++ sys/netpfil/pf/pf_nv.c @@ -37,7 +37,7 @@ #define PV_NV_IMPL_UINT(fnname, type, max) \ int \ - fnname(const nvlist_t *nvl, const char *name, type *val) \ + pf_nv ## fnname(const nvlist_t *nvl, const char *name, type *val) \ { \ uint64_t raw; \ if (! nvlist_exists_number(nvl, name)) \ @@ -49,8 +49,8 @@ return (0); \ } \ int \ - fnname ## _array(const nvlist_t *nvl, const char *name, type *array, \ - size_t maxelems, size_t *nelems) \ + pf_nv ## fnname ## _array(const nvlist_t *nvl, const char *name, \ + type *array, size_t maxelems, size_t *nelems) \ { \ const u_int64_t *n; \ size_t nitems; \ @@ -68,7 +68,18 @@ array[i] = (type)n[i]; \ } \ return (0); \ + } \ + void \ + pf_ ## fnname ## _array_nv(nvlist_t *nvl, const char *name, \ + const type *numbers, size_t count) \ + { \ + uint64_t tmp; \ + for (size_t i = 0; i < count; i++) { \ + tmp = numbers[i]; \ + nvlist_append_number_array(nvl, name, tmp); \ + } \ } + int pf_nvbinary(const nvlist_t *nvl, const char *name, void *data, size_t expected_size) @@ -90,9 +101,9 @@ return (0); } -PV_NV_IMPL_UINT(pf_nvuint_8, u_int8_t, UINT8_MAX) -PV_NV_IMPL_UINT(pf_nvuint_16, u_int16_t, UINT16_MAX); -PV_NV_IMPL_UINT(pf_nvuint_32, u_int32_t, UINT32_MAX) +PV_NV_IMPL_UINT(uint_8, u_int8_t, UINT8_MAX) +PV_NV_IMPL_UINT(uint_16, u_int16_t, UINT16_MAX); +PV_NV_IMPL_UINT(uint_32, u_int32_t, UINT32_MAX) int pf_nvint(const nvlist_t *nvl, const char *name, int *val) Index: sys/netpfil/pf/pf_ruleset.c =================================================================== --- sys/netpfil/pf/pf_ruleset.c +++ sys/netpfil/pf/pf_ruleset.c @@ -336,6 +336,53 @@ return (0); } +int +pf_kanchor_nvcopyout(const struct pf_kruleset *rs, const struct pf_krule *r, + nvlist_t *nvl) +{ + char anchor_call[MAXPATHLEN] = { 0 }; + + if (r->anchor == NULL) + goto done; + if (!r->anchor_relative) { + strlcpy(anchor_call, "/", sizeof(anchor_call)); + strlcat(anchor_call, r->anchor->path, + sizeof(anchor_call)); + } else { + char a[MAXPATHLEN]; + char *p; + int i; + if (rs->anchor == NULL) + a[0] = 0; + else + strlcpy(a, rs->anchor->path, MAXPATHLEN); + for (i = 1; i < r->anchor_relative; ++i) { + if ((p = strrchr(a, '/')) == NULL) + p = a; + *p = 0; + strlcat(anchor_call, "../", + sizeof(anchor_call)); + } + if (strncmp(a, r->anchor->path, strlen(a))) { + printf("pf_anchor_copyout: '%s' '%s'\n", a, + r->anchor->path); + return (1); + } + if (strlen(r->anchor->path) > strlen(a)) + strlcat(anchor_call, r->anchor->path + (a[0] ? + strlen(a) + 1 : 0), sizeof(anchor_call)); + + } + if (r->anchor_wildcard) + strlcat(anchor_call, anchor_call[0] ? "/*" : "*", + sizeof(anchor_call)); + +done: + nvlist_add_string(nvl, "anchor_call", anchor_call); + + return (0); +} + int pf_kanchor_copyout(const struct pf_kruleset *rs, const struct pf_krule *r, struct pfioc_rule *pr)