diff --git a/sys/netpfil/pf/pf_ioctl.c b/sys/netpfil/pf/pf_ioctl.c --- a/sys/netpfil/pf/pf_ioctl.c +++ b/sys/netpfil/pf/pf_ioctl.c @@ -276,14 +276,6 @@ extern u_long pf_ioctl_maxcount; -#define ERROUT_FUNCTION(target, x) \ - do { \ - error = (x); \ - SDT_PROBE3(pf, ioctl, function, error, __func__, error, \ - __LINE__); \ - goto target; \ - } while (0) - static void pfattach_vnet(void) { @@ -1666,648 +1658,6 @@ rule->u_src_nodes = counter_u64_fetch(krule->src_nodes); } -static int -pf_check_rule_addr(const struct pf_rule_addr *addr) -{ - - switch (addr->addr.type) { - case PF_ADDR_ADDRMASK: - case PF_ADDR_NOROUTE: - case PF_ADDR_DYNIFTL: - case PF_ADDR_TABLE: - case PF_ADDR_URPFFAILED: - case PF_ADDR_RANGE: - break; - default: - return (EINVAL); - } - - if (addr->addr.p.dyn != NULL) { - return (EINVAL); - } - - return (0); -} - -static int -pf_nvaddr_to_addr(const nvlist_t *nvl, struct pf_addr *paddr) -{ - return (pf_nvbinary(nvl, "addr", paddr, sizeof(*paddr))); -} - -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(*paddr)); - - return (nvl); -} - -static int -pf_nvmape_to_mape(const nvlist_t *nvl, struct pf_mape_portset *mape) -{ - int error = 0; - - bzero(mape, sizeof(*mape)); - PFNV_CHK(pf_nvuint8(nvl, "offset", &mape->offset)); - PFNV_CHK(pf_nvuint8(nvl, "psidlen", &mape->psidlen)); - PFNV_CHK(pf_nvuint16(nvl, "psid", &mape->psid)); - -errout: - return (error); -} - -static nvlist_t * -pf_mape_to_nvmape(const struct pf_mape_portset *mape) -{ - nvlist_t *nvl; - - nvl = nvlist_create(0); - if (nvl == NULL) - return (NULL); - - nvlist_add_number(nvl, "offset", mape->offset); - nvlist_add_number(nvl, "psidlen", mape->psidlen); - nvlist_add_number(nvl, "psid", mape->psid); - - return (nvl); -} - -static int -pf_nvpool_to_pool(const nvlist_t *nvl, struct pf_kpool *kpool) -{ - int error = 0; - - bzero(kpool, sizeof(*kpool)); - - PFNV_CHK(pf_nvbinary(nvl, "key", &kpool->key, sizeof(kpool->key))); - - if (nvlist_exists_nvlist(nvl, "counter")) { - PFNV_CHK(pf_nvaddr_to_addr(nvlist_get_nvlist(nvl, "counter"), - &kpool->counter)); - } - - PFNV_CHK(pf_nvint(nvl, "tblidx", &kpool->tblidx)); - PFNV_CHK(pf_nvuint16_array(nvl, "proxy_port", kpool->proxy_port, 2, - NULL)); - PFNV_CHK(pf_nvuint8(nvl, "opts", &kpool->opts)); - - if (nvlist_exists_nvlist(nvl, "mape")) { - PFNV_CHK(pf_nvmape_to_mape(nvlist_get_nvlist(nvl, "mape"), - &kpool->mape)); - } - -errout: - 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_destroy(tmp); - - nvlist_add_number(nvl, "tblidx", pool->tblidx); - pf_uint16_array_nv(nvl, "proxy_port", pool->proxy_port, 2); - nvlist_add_number(nvl, "opts", pool->opts); - - tmp = pf_mape_to_nvmape(&pool->mape); - if (tmp == NULL) - goto error; - nvlist_add_nvlist(nvl, "mape", tmp); - nvlist_destroy(tmp); - - 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) -{ - int error = 0; - - bzero(addr, sizeof(*addr)); - - PFNV_CHK(pf_nvuint8(nvl, "type", &addr->type)); - PFNV_CHK(pf_nvuint8(nvl, "iflags", &addr->iflags)); - if (addr->type == PF_ADDR_DYNIFTL) - PFNV_CHK(pf_nvstring(nvl, "ifname", addr->v.ifname, - sizeof(addr->v.ifname))); - if (addr->type == PF_ADDR_TABLE) - PFNV_CHK(pf_nvstring(nvl, "tblname", addr->v.tblname, - sizeof(addr->v.tblname))); - - if (! nvlist_exists_nvlist(nvl, "addr")) - return (EINVAL); - PFNV_CHK(pf_nvaddr_to_addr(nvlist_get_nvlist(nvl, "addr"), - &addr->v.a.addr)); - - if (! nvlist_exists_nvlist(nvl, "mask")) - return (EINVAL); - PFNV_CHK(pf_nvaddr_to_addr(nvlist_get_nvlist(nvl, "mask"), - &addr->v.a.mask)); - - switch (addr->type) { - case PF_ADDR_DYNIFTL: - case PF_ADDR_TABLE: - case PF_ADDR_RANGE: - case PF_ADDR_ADDRMASK: - case PF_ADDR_NOROUTE: - case PF_ADDR_URPFFAILED: - break; - default: - return (EINVAL); - } - -errout: - 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); - if (addr->type == PF_ADDR_DYNIFTL) - nvlist_add_string(nvl, "ifname", addr->v.ifname); - if (addr->type == PF_ADDR_TABLE) - 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); - nvlist_destroy(tmp); - tmp = pf_addr_to_nvaddr(&addr->v.a.mask); - if (tmp == NULL) - goto error; - nvlist_add_nvlist(nvl, "mask", tmp); - nvlist_destroy(tmp); - - return (nvl); - -error: - nvlist_destroy(nvl); - return (NULL); -} - -static int -pf_validate_op(uint8_t op) -{ - switch (op) { - case PF_OP_NONE: - case PF_OP_IRG: - case PF_OP_EQ: - case PF_OP_NE: - case PF_OP_LT: - case PF_OP_LE: - case PF_OP_GT: - case PF_OP_GE: - case PF_OP_XRG: - case PF_OP_RRG: - break; - default: - return (EINVAL); - } - - return (0); -} - -static int -pf_nvrule_addr_to_rule_addr(const nvlist_t *nvl, struct pf_rule_addr *addr) -{ - int error = 0; - - if (! nvlist_exists_nvlist(nvl, "addr")) - return (EINVAL); - - PFNV_CHK(pf_nvaddr_wrap_to_addr_wrap(nvlist_get_nvlist(nvl, "addr"), - &addr->addr)); - PFNV_CHK(pf_nvuint16_array(nvl, "port", addr->port, 2, NULL)); - PFNV_CHK(pf_nvuint8(nvl, "neg", &addr->neg)); - PFNV_CHK(pf_nvuint8(nvl, "port_op", &addr->port_op)); - - PFNV_CHK(pf_validate_op(addr->port_op)); - -errout: - 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); - nvlist_destroy(tmp); - pf_uint16_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) -{ - int error = 0; - - bzero(uid, sizeof(*uid)); - - PFNV_CHK(pf_nvuint32_array(nvl, "uid", uid->uid, 2, NULL)); - PFNV_CHK(pf_nvuint8(nvl, "op", &uid->op)); - - PFNV_CHK(pf_validate_op(uid->op)); - -errout: - 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_uint32_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) -{ - /* Cheat a little. These stucts are the same, other than the name of - * the first field. */ - return (pf_nvrule_uid_to_rule_uid(nvl, (struct pf_rule_uid *)gid)); -} - -static int -pf_nvrule_to_krule(const nvlist_t *nvl, struct pf_krule **prule) -{ - struct pf_krule *rule; - int error = 0; - -#define ERROUT(x) ERROUT_FUNCTION(errout, x) - - rule = malloc(sizeof(*rule), M_PFRULE, M_WAITOK | M_ZERO); - - PFNV_CHK(pf_nvuint32(nvl, "nr", &rule->nr)); - - if (! nvlist_exists_nvlist(nvl, "src")) - ERROUT(EINVAL); - - error = pf_nvrule_addr_to_rule_addr(nvlist_get_nvlist(nvl, "src"), - &rule->src); - if (error != 0) - ERROUT(error); - - if (! nvlist_exists_nvlist(nvl, "dst")) - ERROUT(EINVAL); - - PFNV_CHK(pf_nvrule_addr_to_rule_addr(nvlist_get_nvlist(nvl, "dst"), - &rule->dst)); - - if (nvlist_exists_string(nvl, "label")) { - PFNV_CHK(pf_nvstring(nvl, "label", rule->label[0], - sizeof(rule->label[0]))); - } else if (nvlist_exists_string_array(nvl, "labels")) { - const char *const *strs; - size_t items; - int ret; - - strs = nvlist_get_string_array(nvl, "labels", &items); - if (items > PF_RULE_MAX_LABEL_COUNT) - ERROUT(E2BIG); - - for (size_t i = 0; i < items; i++) { - ret = strlcpy(rule->label[i], strs[i], - sizeof(rule->label[0])); - if (ret >= sizeof(rule->label[0])) - ERROUT(E2BIG); - } - } - - PFNV_CHK(pf_nvstring(nvl, "ifname", rule->ifname, - sizeof(rule->ifname))); - PFNV_CHK(pf_nvstring(nvl, "qname", rule->qname, sizeof(rule->qname))); - PFNV_CHK(pf_nvstring(nvl, "pqname", rule->pqname, - sizeof(rule->pqname))); - PFNV_CHK(pf_nvstring(nvl, "tagname", rule->tagname, - sizeof(rule->tagname))); - PFNV_CHK(pf_nvstring(nvl, "match_tagname", rule->match_tagname, - sizeof(rule->match_tagname))); - PFNV_CHK(pf_nvstring(nvl, "overload_tblname", rule->overload_tblname, - sizeof(rule->overload_tblname))); - - if (! nvlist_exists_nvlist(nvl, "rpool")) - ERROUT(EINVAL); - PFNV_CHK(pf_nvpool_to_pool(nvlist_get_nvlist(nvl, "rpool"), - &rule->rpool)); - - PFNV_CHK(pf_nvuint32(nvl, "os_fingerprint", &rule->os_fingerprint)); - - PFNV_CHK(pf_nvint(nvl, "rtableid", &rule->rtableid)); - PFNV_CHK(pf_nvuint32_array(nvl, "timeout", rule->timeout, PFTM_MAX, NULL)); - PFNV_CHK(pf_nvuint32(nvl, "max_states", &rule->max_states)); - PFNV_CHK(pf_nvuint32(nvl, "max_src_nodes", &rule->max_src_nodes)); - PFNV_CHK(pf_nvuint32(nvl, "max_src_states", &rule->max_src_states)); - PFNV_CHK(pf_nvuint32(nvl, "max_src_conn", &rule->max_src_conn)); - PFNV_CHK(pf_nvuint32(nvl, "max_src_conn_rate.limit", - &rule->max_src_conn_rate.limit)); - PFNV_CHK(pf_nvuint32(nvl, "max_src_conn_rate.seconds", - &rule->max_src_conn_rate.seconds)); - PFNV_CHK(pf_nvuint32(nvl, "prob", &rule->prob)); - PFNV_CHK(pf_nvuint32(nvl, "cuid", &rule->cuid)); - PFNV_CHK(pf_nvuint32(nvl, "cpid", &rule->cpid)); - - PFNV_CHK(pf_nvuint16(nvl, "return_icmp", &rule->return_icmp)); - PFNV_CHK(pf_nvuint16(nvl, "return_icmp6", &rule->return_icmp6)); - - PFNV_CHK(pf_nvuint16(nvl, "max_mss", &rule->max_mss)); - PFNV_CHK(pf_nvuint16(nvl, "scrub_flags", &rule->scrub_flags)); - - if (! nvlist_exists_nvlist(nvl, "uid")) - ERROUT(EINVAL); - PFNV_CHK(pf_nvrule_uid_to_rule_uid(nvlist_get_nvlist(nvl, "uid"), - &rule->uid)); - - if (! nvlist_exists_nvlist(nvl, "gid")) - ERROUT(EINVAL); - PFNV_CHK(pf_nvrule_gid_to_rule_gid(nvlist_get_nvlist(nvl, "gid"), - &rule->gid)); - - PFNV_CHK(pf_nvuint32(nvl, "rule_flag", &rule->rule_flag)); - PFNV_CHK(pf_nvuint8(nvl, "action", &rule->action)); - PFNV_CHK(pf_nvuint8(nvl, "direction", &rule->direction)); - PFNV_CHK(pf_nvuint8(nvl, "log", &rule->log)); - PFNV_CHK(pf_nvuint8(nvl, "logif", &rule->logif)); - PFNV_CHK(pf_nvuint8(nvl, "quick", &rule->quick)); - PFNV_CHK(pf_nvuint8(nvl, "ifnot", &rule->ifnot)); - PFNV_CHK(pf_nvuint8(nvl, "match_tag_not", &rule->match_tag_not)); - PFNV_CHK(pf_nvuint8(nvl, "natpass", &rule->natpass)); - - PFNV_CHK(pf_nvuint8(nvl, "keep_state", &rule->keep_state)); - PFNV_CHK(pf_nvuint8(nvl, "af", &rule->af)); - PFNV_CHK(pf_nvuint8(nvl, "proto", &rule->proto)); - PFNV_CHK(pf_nvuint8(nvl, "type", &rule->type)); - PFNV_CHK(pf_nvuint8(nvl, "code", &rule->code)); - PFNV_CHK(pf_nvuint8(nvl, "flags", &rule->flags)); - PFNV_CHK(pf_nvuint8(nvl, "flagset", &rule->flagset)); - PFNV_CHK(pf_nvuint8(nvl, "min_ttl", &rule->min_ttl)); - PFNV_CHK(pf_nvuint8(nvl, "allow_opts", &rule->allow_opts)); - PFNV_CHK(pf_nvuint8(nvl, "rt", &rule->rt)); - PFNV_CHK(pf_nvuint8(nvl, "return_ttl", &rule->return_ttl)); - PFNV_CHK(pf_nvuint8(nvl, "tos", &rule->tos)); - PFNV_CHK(pf_nvuint8(nvl, "set_tos", &rule->set_tos)); - PFNV_CHK(pf_nvuint8(nvl, "anchor_relative", &rule->anchor_relative)); - PFNV_CHK(pf_nvuint8(nvl, "anchor_wildcard", &rule->anchor_wildcard)); - - PFNV_CHK(pf_nvuint8(nvl, "flush", &rule->flush)); - PFNV_CHK(pf_nvuint8(nvl, "prio", &rule->prio)); - - PFNV_CHK(pf_nvuint8_array(nvl, "set_prio", &rule->prio, 2, NULL)); - - if (nvlist_exists_nvlist(nvl, "divert")) { - const nvlist_t *nvldivert = nvlist_get_nvlist(nvl, "divert"); - - if (! nvlist_exists_nvlist(nvldivert, "addr")) - ERROUT(EINVAL); - PFNV_CHK(pf_nvaddr_to_addr(nvlist_get_nvlist(nvldivert, "addr"), - &rule->divert.addr)); - PFNV_CHK(pf_nvuint16(nvldivert, "port", &rule->divert.port)); - } - - /* Validation */ -#ifndef INET - if (rule->af == AF_INET) - ERROUT(EAFNOSUPPORT); -#endif /* INET */ -#ifndef INET6 - if (rule->af == AF_INET6) - ERROUT(EAFNOSUPPORT); -#endif /* INET6 */ - - PFNV_CHK(pf_check_rule_addr(&rule->src)); - PFNV_CHK(pf_check_rule_addr(&rule->dst)); - - *prule = rule; - - return (0); - -#undef ERROUT -errout: - pf_krule_free(rule); - *prule = NULL; - - 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_destroy(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); - nvlist_destroy(tmp); - tmp = pf_rule_addr_to_nvrule_addr(&rule->dst); - if (tmp == NULL) - goto error; - nvlist_add_nvlist(nvl, "dst", tmp); - nvlist_destroy(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); - } - - for (int i = 0; i < PF_RULE_MAX_LABEL_COUNT; i++) { - nvlist_append_string_array(nvl, "labels", rule->label[i]); - } - nvlist_add_string(nvl, "label", rule->label[0]); - 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_destroy(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_uint32_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, "qid", rule->qid); - nvlist_add_number(nvl, "pqid", rule->pqid); - 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); - nvlist_destroy(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_destroy(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_uint8_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); - nvlist_destroy(tmp); - - return (nvl); - -error: - nvlist_destroy(nvl); - return (NULL); -} - static int pf_rule_to_krule(const struct pf_rule *rule, struct pf_krule *krule) { @@ -2433,219 +1783,6 @@ return (0); } -static int -pf_nvstate_cmp_to_state_cmp(const nvlist_t *nvl, struct pf_state_cmp *cmp) -{ - int error = 0; - - bzero(cmp, sizeof(*cmp)); - - PFNV_CHK(pf_nvuint64(nvl, "id", &cmp->id)); - PFNV_CHK(pf_nvuint32(nvl, "creatorid", &cmp->creatorid)); - PFNV_CHK(pf_nvuint8(nvl, "direction", &cmp->direction)); - -errout: - return (error); -} - -static int -pf_nvstate_kill_to_kstate_kill(const nvlist_t *nvl, - struct pf_kstate_kill *kill) -{ - int error = 0; - - bzero(kill, sizeof(*kill)); - - if (! nvlist_exists_nvlist(nvl, "cmp")) - return (EINVAL); - - PFNV_CHK(pf_nvstate_cmp_to_state_cmp(nvlist_get_nvlist(nvl, "cmp"), - &kill->psk_pfcmp)); - PFNV_CHK(pf_nvuint8(nvl, "af", &kill->psk_af)); - PFNV_CHK(pf_nvint(nvl, "proto", &kill->psk_proto)); - - if (! nvlist_exists_nvlist(nvl, "src")) - return (EINVAL); - PFNV_CHK(pf_nvrule_addr_to_rule_addr(nvlist_get_nvlist(nvl, "src"), - &kill->psk_src)); - if (! nvlist_exists_nvlist(nvl, "dst")) - return (EINVAL); - PFNV_CHK(pf_nvrule_addr_to_rule_addr(nvlist_get_nvlist(nvl, "dst"), - &kill->psk_dst)); - if (nvlist_exists_nvlist(nvl, "rt_addr")) { - PFNV_CHK(pf_nvrule_addr_to_rule_addr( - nvlist_get_nvlist(nvl, "rt_addr"), &kill->psk_rt_addr)); - } - - PFNV_CHK(pf_nvstring(nvl, "ifname", kill->psk_ifname, - sizeof(kill->psk_ifname))); - PFNV_CHK(pf_nvstring(nvl, "label", kill->psk_label, - sizeof(kill->psk_label))); - if (nvlist_exists_bool(nvl, "kill_match")) - kill->psk_kill_match = nvlist_get_bool(nvl, "kill_match"); - -errout: - 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_destroy(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); - nvlist_add_string(nvl, "orig_ifname", s->orig_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); - nvlist_destroy(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); - nvlist_destroy(tmp); - - tmp = pf_state_peer_to_nvstate_peer(&s->src); - if (tmp == NULL) - goto errout; - nvlist_add_nvlist(nvl, "src", tmp); - nvlist_destroy(tmp); - - tmp = pf_state_peer_to_nvstate_peer(&s->dst); - if (tmp == NULL) - goto errout; - nvlist_add_nvlist(nvl, "dst", tmp); - nvlist_destroy(tmp); - - tmp = pf_addr_to_nvaddr(&s->rt_addr); - if (tmp == NULL) - goto errout; - nvlist_add_nvlist(nvl, "rt_addr", tmp); - nvlist_destroy(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, @@ -3139,8 +2276,9 @@ if (! nvlist_exists_nvlist(nvl, "rule")) ERROUT(EINVAL); + rule = malloc(sizeof(*rule), M_PFRULE, M_WAITOK | M_ZERO); error = pf_nvrule_to_krule(nvlist_get_nvlist(nvl, "rule"), - &rule); + rule); if (error) ERROUT(error); diff --git a/sys/netpfil/pf/pf_nv.h b/sys/netpfil/pf/pf_nv.h --- a/sys/netpfil/pf/pf_nv.h +++ b/sys/netpfil/pf/pf_nv.h @@ -28,8 +28,33 @@ #ifndef _PF_NV_H_ #define _PF_NV_H_ -#include #include +#include +#include +#include + +#include +#include +#include + +SDT_PROVIDER_DECLARE(pf); +SDT_PROBE_DECLARE(pf, ioctl, function, error); +SDT_PROBE_DECLARE(pf, ioctl, nvchk, error); + +#define ERROUT_FUNCTION(target, x) \ + do { \ + error = (x); \ + SDT_PROBE3(pf, ioctl, function, error, __func__, error, \ + __LINE__); \ + goto target; \ + } while (0) + +#define PFNV_CHK(x) do { \ + error = (x); \ + SDT_PROBE2(pf, ioctl, nvchk, error, error, __LINE__); \ + if (error != 0) \ + goto errout; \ + } while (0) int pf_nvbinary(const nvlist_t *, const char *, void *, size_t); int pf_nvint(const nvlist_t *, const char *, int *); @@ -56,11 +81,14 @@ int pf_nvstring(const nvlist_t *, const char *, char *, size_t); -#define PFNV_CHK(x) do { \ - error = (x); \ - SDT_PROBE2(pf, ioctl, nvchk, error, error, __LINE__); \ - if (error != 0) \ - goto errout; \ - } while (0) +/* Translation functions */ + +int pf_check_rule_addr(const struct pf_rule_addr *); + +nvlist_t *pf_krule_to_nvrule(const struct pf_krule *); +int pf_nvrule_to_krule(const nvlist_t *, struct pf_krule *); +int pf_nvstate_kill_to_kstate_kill(const nvlist_t *, + struct pf_kstate_kill *); +nvlist_t *pf_state_to_nvstate(const struct pf_state *); #endif diff --git a/sys/netpfil/pf/pf_nv.c b/sys/netpfil/pf/pf_nv.c --- a/sys/netpfil/pf/pf_nv.c +++ b/sys/netpfil/pf/pf_nv.c @@ -28,9 +28,13 @@ #include __FBSDID("$FreeBSD$"); +#include "opt_inet.h" +#include "opt_inet6.h" + #include #include #include +#include #include #include @@ -137,3 +141,850 @@ return (0); } + +static int +pf_nvaddr_to_addr(const nvlist_t *nvl, struct pf_addr *paddr) +{ + return (pf_nvbinary(nvl, "addr", paddr, sizeof(*paddr))); +} + +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(*paddr)); + + return (nvl); +} + +static int +pf_nvmape_to_mape(const nvlist_t *nvl, struct pf_mape_portset *mape) +{ + int error = 0; + + bzero(mape, sizeof(*mape)); + PFNV_CHK(pf_nvuint8(nvl, "offset", &mape->offset)); + PFNV_CHK(pf_nvuint8(nvl, "psidlen", &mape->psidlen)); + PFNV_CHK(pf_nvuint16(nvl, "psid", &mape->psid)); + +errout: + return (error); +} + +static nvlist_t * +pf_mape_to_nvmape(const struct pf_mape_portset *mape) +{ + nvlist_t *nvl; + + nvl = nvlist_create(0); + if (nvl == NULL) + return (NULL); + + nvlist_add_number(nvl, "offset", mape->offset); + nvlist_add_number(nvl, "psidlen", mape->psidlen); + nvlist_add_number(nvl, "psid", mape->psid); + + return (nvl); +} + +static int +pf_nvpool_to_pool(const nvlist_t *nvl, struct pf_kpool *kpool) +{ + int error = 0; + + bzero(kpool, sizeof(*kpool)); + + PFNV_CHK(pf_nvbinary(nvl, "key", &kpool->key, sizeof(kpool->key))); + + if (nvlist_exists_nvlist(nvl, "counter")) { + PFNV_CHK(pf_nvaddr_to_addr(nvlist_get_nvlist(nvl, "counter"), + &kpool->counter)); + } + + PFNV_CHK(pf_nvint(nvl, "tblidx", &kpool->tblidx)); + PFNV_CHK(pf_nvuint16_array(nvl, "proxy_port", kpool->proxy_port, 2, + NULL)); + PFNV_CHK(pf_nvuint8(nvl, "opts", &kpool->opts)); + + if (nvlist_exists_nvlist(nvl, "mape")) { + PFNV_CHK(pf_nvmape_to_mape(nvlist_get_nvlist(nvl, "mape"), + &kpool->mape)); + } + +errout: + 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_destroy(tmp); + + nvlist_add_number(nvl, "tblidx", pool->tblidx); + pf_uint16_array_nv(nvl, "proxy_port", pool->proxy_port, 2); + nvlist_add_number(nvl, "opts", pool->opts); + + tmp = pf_mape_to_nvmape(&pool->mape); + if (tmp == NULL) + goto error; + nvlist_add_nvlist(nvl, "mape", tmp); + nvlist_destroy(tmp); + + 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) +{ + int error = 0; + + bzero(addr, sizeof(*addr)); + + PFNV_CHK(pf_nvuint8(nvl, "type", &addr->type)); + PFNV_CHK(pf_nvuint8(nvl, "iflags", &addr->iflags)); + if (addr->type == PF_ADDR_DYNIFTL) + PFNV_CHK(pf_nvstring(nvl, "ifname", addr->v.ifname, + sizeof(addr->v.ifname))); + if (addr->type == PF_ADDR_TABLE) + PFNV_CHK(pf_nvstring(nvl, "tblname", addr->v.tblname, + sizeof(addr->v.tblname))); + + if (! nvlist_exists_nvlist(nvl, "addr")) + return (EINVAL); + PFNV_CHK(pf_nvaddr_to_addr(nvlist_get_nvlist(nvl, "addr"), + &addr->v.a.addr)); + + if (! nvlist_exists_nvlist(nvl, "mask")) + return (EINVAL); + PFNV_CHK(pf_nvaddr_to_addr(nvlist_get_nvlist(nvl, "mask"), + &addr->v.a.mask)); + + switch (addr->type) { + case PF_ADDR_DYNIFTL: + case PF_ADDR_TABLE: + case PF_ADDR_RANGE: + case PF_ADDR_ADDRMASK: + case PF_ADDR_NOROUTE: + case PF_ADDR_URPFFAILED: + break; + default: + return (EINVAL); + } + +errout: + 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); + if (addr->type == PF_ADDR_DYNIFTL) + nvlist_add_string(nvl, "ifname", addr->v.ifname); + if (addr->type == PF_ADDR_TABLE) + 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); + nvlist_destroy(tmp); + tmp = pf_addr_to_nvaddr(&addr->v.a.mask); + if (tmp == NULL) + goto error; + nvlist_add_nvlist(nvl, "mask", tmp); + nvlist_destroy(tmp); + + return (nvl); + +error: + nvlist_destroy(nvl); + return (NULL); +} + +static int +pf_validate_op(uint8_t op) +{ + switch (op) { + case PF_OP_NONE: + case PF_OP_IRG: + case PF_OP_EQ: + case PF_OP_NE: + case PF_OP_LT: + case PF_OP_LE: + case PF_OP_GT: + case PF_OP_GE: + case PF_OP_XRG: + case PF_OP_RRG: + break; + default: + return (EINVAL); + } + + return (0); +} + +static int +pf_nvrule_addr_to_rule_addr(const nvlist_t *nvl, struct pf_rule_addr *addr) +{ + int error = 0; + + if (! nvlist_exists_nvlist(nvl, "addr")) + return (EINVAL); + + PFNV_CHK(pf_nvaddr_wrap_to_addr_wrap(nvlist_get_nvlist(nvl, "addr"), + &addr->addr)); + PFNV_CHK(pf_nvuint16_array(nvl, "port", addr->port, 2, NULL)); + PFNV_CHK(pf_nvuint8(nvl, "neg", &addr->neg)); + PFNV_CHK(pf_nvuint8(nvl, "port_op", &addr->port_op)); + + PFNV_CHK(pf_validate_op(addr->port_op)); + +errout: + 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); + nvlist_destroy(tmp); + pf_uint16_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) +{ + int error = 0; + + bzero(uid, sizeof(*uid)); + + PFNV_CHK(pf_nvuint32_array(nvl, "uid", uid->uid, 2, NULL)); + PFNV_CHK(pf_nvuint8(nvl, "op", &uid->op)); + + PFNV_CHK(pf_validate_op(uid->op)); + +errout: + 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_uint32_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) +{ + /* Cheat a little. These stucts are the same, other than the name of + * the first field. */ + return (pf_nvrule_uid_to_rule_uid(nvl, (struct pf_rule_uid *)gid)); +} + +int +pf_check_rule_addr(const struct pf_rule_addr *addr) +{ + + switch (addr->addr.type) { + case PF_ADDR_ADDRMASK: + case PF_ADDR_NOROUTE: + case PF_ADDR_DYNIFTL: + case PF_ADDR_TABLE: + case PF_ADDR_URPFFAILED: + case PF_ADDR_RANGE: + break; + default: + return (EINVAL); + } + + if (addr->addr.p.dyn != NULL) { + return (EINVAL); + } + + return (0); +} + + +int +pf_nvrule_to_krule(const nvlist_t *nvl, struct pf_krule *rule) +{ + int error = 0; + +#define ERROUT(x) ERROUT_FUNCTION(errout, x) + + PFNV_CHK(pf_nvuint32(nvl, "nr", &rule->nr)); + + if (! nvlist_exists_nvlist(nvl, "src")) + ERROUT(EINVAL); + + error = pf_nvrule_addr_to_rule_addr(nvlist_get_nvlist(nvl, "src"), + &rule->src); + if (error != 0) + ERROUT(error); + + if (! nvlist_exists_nvlist(nvl, "dst")) + ERROUT(EINVAL); + + PFNV_CHK(pf_nvrule_addr_to_rule_addr(nvlist_get_nvlist(nvl, "dst"), + &rule->dst)); + + if (nvlist_exists_string(nvl, "label")) { + PFNV_CHK(pf_nvstring(nvl, "label", rule->label[0], + sizeof(rule->label[0]))); + } else if (nvlist_exists_string_array(nvl, "labels")) { + const char *const *strs; + size_t items; + int ret; + + strs = nvlist_get_string_array(nvl, "labels", &items); + if (items > PF_RULE_MAX_LABEL_COUNT) + ERROUT(E2BIG); + + for (size_t i = 0; i < items; i++) { + ret = strlcpy(rule->label[i], strs[i], + sizeof(rule->label[0])); + if (ret >= sizeof(rule->label[0])) + ERROUT(E2BIG); + } + } + + PFNV_CHK(pf_nvstring(nvl, "ifname", rule->ifname, + sizeof(rule->ifname))); + PFNV_CHK(pf_nvstring(nvl, "qname", rule->qname, sizeof(rule->qname))); + PFNV_CHK(pf_nvstring(nvl, "pqname", rule->pqname, + sizeof(rule->pqname))); + PFNV_CHK(pf_nvstring(nvl, "tagname", rule->tagname, + sizeof(rule->tagname))); + PFNV_CHK(pf_nvstring(nvl, "match_tagname", rule->match_tagname, + sizeof(rule->match_tagname))); + PFNV_CHK(pf_nvstring(nvl, "overload_tblname", rule->overload_tblname, + sizeof(rule->overload_tblname))); + + if (! nvlist_exists_nvlist(nvl, "rpool")) + ERROUT(EINVAL); + PFNV_CHK(pf_nvpool_to_pool(nvlist_get_nvlist(nvl, "rpool"), + &rule->rpool)); + + PFNV_CHK(pf_nvuint32(nvl, "os_fingerprint", &rule->os_fingerprint)); + + PFNV_CHK(pf_nvint(nvl, "rtableid", &rule->rtableid)); + PFNV_CHK(pf_nvuint32_array(nvl, "timeout", rule->timeout, PFTM_MAX, NULL)); + PFNV_CHK(pf_nvuint32(nvl, "max_states", &rule->max_states)); + PFNV_CHK(pf_nvuint32(nvl, "max_src_nodes", &rule->max_src_nodes)); + PFNV_CHK(pf_nvuint32(nvl, "max_src_states", &rule->max_src_states)); + PFNV_CHK(pf_nvuint32(nvl, "max_src_conn", &rule->max_src_conn)); + PFNV_CHK(pf_nvuint32(nvl, "max_src_conn_rate.limit", + &rule->max_src_conn_rate.limit)); + PFNV_CHK(pf_nvuint32(nvl, "max_src_conn_rate.seconds", + &rule->max_src_conn_rate.seconds)); + PFNV_CHK(pf_nvuint32(nvl, "prob", &rule->prob)); + PFNV_CHK(pf_nvuint32(nvl, "cuid", &rule->cuid)); + PFNV_CHK(pf_nvuint32(nvl, "cpid", &rule->cpid)); + + PFNV_CHK(pf_nvuint16(nvl, "return_icmp", &rule->return_icmp)); + PFNV_CHK(pf_nvuint16(nvl, "return_icmp6", &rule->return_icmp6)); + + PFNV_CHK(pf_nvuint16(nvl, "max_mss", &rule->max_mss)); + PFNV_CHK(pf_nvuint16(nvl, "scrub_flags", &rule->scrub_flags)); + + if (! nvlist_exists_nvlist(nvl, "uid")) + ERROUT(EINVAL); + PFNV_CHK(pf_nvrule_uid_to_rule_uid(nvlist_get_nvlist(nvl, "uid"), + &rule->uid)); + + if (! nvlist_exists_nvlist(nvl, "gid")) + ERROUT(EINVAL); + PFNV_CHK(pf_nvrule_gid_to_rule_gid(nvlist_get_nvlist(nvl, "gid"), + &rule->gid)); + + PFNV_CHK(pf_nvuint32(nvl, "rule_flag", &rule->rule_flag)); + PFNV_CHK(pf_nvuint8(nvl, "action", &rule->action)); + PFNV_CHK(pf_nvuint8(nvl, "direction", &rule->direction)); + PFNV_CHK(pf_nvuint8(nvl, "log", &rule->log)); + PFNV_CHK(pf_nvuint8(nvl, "logif", &rule->logif)); + PFNV_CHK(pf_nvuint8(nvl, "quick", &rule->quick)); + PFNV_CHK(pf_nvuint8(nvl, "ifnot", &rule->ifnot)); + PFNV_CHK(pf_nvuint8(nvl, "match_tag_not", &rule->match_tag_not)); + PFNV_CHK(pf_nvuint8(nvl, "natpass", &rule->natpass)); + + PFNV_CHK(pf_nvuint8(nvl, "keep_state", &rule->keep_state)); + PFNV_CHK(pf_nvuint8(nvl, "af", &rule->af)); + PFNV_CHK(pf_nvuint8(nvl, "proto", &rule->proto)); + PFNV_CHK(pf_nvuint8(nvl, "type", &rule->type)); + PFNV_CHK(pf_nvuint8(nvl, "code", &rule->code)); + PFNV_CHK(pf_nvuint8(nvl, "flags", &rule->flags)); + PFNV_CHK(pf_nvuint8(nvl, "flagset", &rule->flagset)); + PFNV_CHK(pf_nvuint8(nvl, "min_ttl", &rule->min_ttl)); + PFNV_CHK(pf_nvuint8(nvl, "allow_opts", &rule->allow_opts)); + PFNV_CHK(pf_nvuint8(nvl, "rt", &rule->rt)); + PFNV_CHK(pf_nvuint8(nvl, "return_ttl", &rule->return_ttl)); + PFNV_CHK(pf_nvuint8(nvl, "tos", &rule->tos)); + PFNV_CHK(pf_nvuint8(nvl, "set_tos", &rule->set_tos)); + PFNV_CHK(pf_nvuint8(nvl, "anchor_relative", &rule->anchor_relative)); + PFNV_CHK(pf_nvuint8(nvl, "anchor_wildcard", &rule->anchor_wildcard)); + + PFNV_CHK(pf_nvuint8(nvl, "flush", &rule->flush)); + PFNV_CHK(pf_nvuint8(nvl, "prio", &rule->prio)); + + PFNV_CHK(pf_nvuint8_array(nvl, "set_prio", &rule->prio, 2, NULL)); + + if (nvlist_exists_nvlist(nvl, "divert")) { + const nvlist_t *nvldivert = nvlist_get_nvlist(nvl, "divert"); + + if (! nvlist_exists_nvlist(nvldivert, "addr")) + ERROUT(EINVAL); + PFNV_CHK(pf_nvaddr_to_addr(nvlist_get_nvlist(nvldivert, "addr"), + &rule->divert.addr)); + PFNV_CHK(pf_nvuint16(nvldivert, "port", &rule->divert.port)); + } + + /* Validation */ +#ifndef INET + if (rule->af == AF_INET) + ERROUT(EAFNOSUPPORT); +#endif /* INET */ +#ifndef INET6 + if (rule->af == AF_INET6) + ERROUT(EAFNOSUPPORT); +#endif /* INET6 */ + + PFNV_CHK(pf_check_rule_addr(&rule->src)); + PFNV_CHK(pf_check_rule_addr(&rule->dst)); + + return (0); + +#undef ERROUT +errout: + 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_destroy(tmp); + nvlist_add_number(nvl, "port", rule->divert.port); + + return (nvl); + +error: + nvlist_destroy(nvl); + return (NULL); +} + +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); + nvlist_destroy(tmp); + tmp = pf_rule_addr_to_nvrule_addr(&rule->dst); + if (tmp == NULL) + goto error; + nvlist_add_nvlist(nvl, "dst", tmp); + nvlist_destroy(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); + } + + for (int i = 0; i < PF_RULE_MAX_LABEL_COUNT; i++) { + nvlist_append_string_array(nvl, "labels", rule->label[i]); + } + nvlist_add_string(nvl, "label", rule->label[0]); + 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_destroy(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_uint32_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, "qid", rule->qid); + nvlist_add_number(nvl, "pqid", rule->pqid); + 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); + nvlist_destroy(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_destroy(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_uint8_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); + nvlist_destroy(tmp); + + return (nvl); + +error: + nvlist_destroy(nvl); + return (NULL); +} + +static int +pf_nvstate_cmp_to_state_cmp(const nvlist_t *nvl, struct pf_state_cmp *cmp) +{ + int error = 0; + + bzero(cmp, sizeof(*cmp)); + + PFNV_CHK(pf_nvuint64(nvl, "id", &cmp->id)); + PFNV_CHK(pf_nvuint32(nvl, "creatorid", &cmp->creatorid)); + PFNV_CHK(pf_nvuint8(nvl, "direction", &cmp->direction)); + +errout: + return (error); +} + +int +pf_nvstate_kill_to_kstate_kill(const nvlist_t *nvl, + struct pf_kstate_kill *kill) +{ + int error = 0; + + bzero(kill, sizeof(*kill)); + + if (! nvlist_exists_nvlist(nvl, "cmp")) + return (EINVAL); + + PFNV_CHK(pf_nvstate_cmp_to_state_cmp(nvlist_get_nvlist(nvl, "cmp"), + &kill->psk_pfcmp)); + PFNV_CHK(pf_nvuint8(nvl, "af", &kill->psk_af)); + PFNV_CHK(pf_nvint(nvl, "proto", &kill->psk_proto)); + + if (! nvlist_exists_nvlist(nvl, "src")) + return (EINVAL); + PFNV_CHK(pf_nvrule_addr_to_rule_addr(nvlist_get_nvlist(nvl, "src"), + &kill->psk_src)); + if (! nvlist_exists_nvlist(nvl, "dst")) + return (EINVAL); + PFNV_CHK(pf_nvrule_addr_to_rule_addr(nvlist_get_nvlist(nvl, "dst"), + &kill->psk_dst)); + if (nvlist_exists_nvlist(nvl, "rt_addr")) { + PFNV_CHK(pf_nvrule_addr_to_rule_addr( + nvlist_get_nvlist(nvl, "rt_addr"), &kill->psk_rt_addr)); + } + + PFNV_CHK(pf_nvstring(nvl, "ifname", kill->psk_ifname, + sizeof(kill->psk_ifname))); + PFNV_CHK(pf_nvstring(nvl, "label", kill->psk_label, + sizeof(kill->psk_label))); + if (nvlist_exists_bool(nvl, "kill_match")) + kill->psk_kill_match = nvlist_get_bool(nvl, "kill_match"); + +errout: + 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_destroy(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); +} + +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); + nvlist_add_string(nvl, "orig_ifname", s->orig_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); + nvlist_destroy(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); + nvlist_destroy(tmp); + + tmp = pf_state_peer_to_nvstate_peer(&s->src); + if (tmp == NULL) + goto errout; + nvlist_add_nvlist(nvl, "src", tmp); + nvlist_destroy(tmp); + + tmp = pf_state_peer_to_nvstate_peer(&s->dst); + if (tmp == NULL) + goto errout; + nvlist_add_nvlist(nvl, "dst", tmp); + nvlist_destroy(tmp); + + tmp = pf_addr_to_nvaddr(&s->rt_addr); + if (tmp == NULL) + goto errout; + nvlist_add_nvlist(nvl, "rt_addr", tmp); + nvlist_destroy(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); +}