diff --git a/sbin/pfctl/Makefile b/sbin/pfctl/Makefile --- a/sbin/pfctl/Makefile +++ b/sbin/pfctl/Makefile @@ -27,7 +27,7 @@ YFLAGS= -LIBADD= m md +LIBADD= m md nv HAS_TESTS= SUBDIR.${MK_TESTS}+= tests diff --git a/sbin/pfctl/pfctl.c b/sbin/pfctl/pfctl.c --- a/sbin/pfctl/pfctl.c +++ b/sbin/pfctl/pfctl.c @@ -40,6 +40,7 @@ #include #include +#include #include #include #include @@ -1422,19 +1423,217 @@ } +static void +pfctl_nv_add_addr(nvlist_t *nvparent, const char *name, + const struct pf_addr *addr) +{ + nvlist_t *nvl = nvlist_create(0); + + nvlist_add_binary(nvl, "addr", addr, sizeof(*addr)); + + nvlist_add_nvlist(nvparent, name, nvl); +} + +static void +pfctl_nv_add_addr_wrap(nvlist_t *nvparent, const char *name, + const struct pf_addr_wrap *addr) +{ + nvlist_t *nvl = nvlist_create(0); + + 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); + pfctl_nv_add_addr(nvl, "addr", &addr->v.a.addr); + pfctl_nv_add_addr(nvl, "mask", &addr->v.a.mask); + + nvlist_add_nvlist(nvparent, name, nvl); +} + +static void +pfctl_nv_add_rule_addr(nvlist_t *nvparent, const char *name, + const struct pf_rule_addr *addr) +{ + u_int64_t ports[2]; + nvlist_t *nvl = nvlist_create(0); + + pfctl_nv_add_addr_wrap(nvl, "addr", &addr->addr); + ports[0] = addr->port[0]; + ports[1] = addr->port[1]; + nvlist_add_number_array(nvl, "port", ports, 2); + nvlist_add_number(nvl, "neg", addr->neg); + nvlist_add_number(nvl, "port_op", addr->port_op); + + nvlist_add_nvlist(nvparent, name, nvl); +} + +static void +pfctl_nv_add_pool(nvlist_t *nvparent, const char *name, + const struct pf_pool *pool) +{ + u_int64_t ports[2]; + nvlist_t *nvl = nvlist_create(0); + + nvlist_add_binary(nvl, "key", &pool->key, sizeof(pool->key)); + pfctl_nv_add_addr(nvl, "counter", &pool->counter); + nvlist_add_number(nvl, "tblidx", pool->tblidx); + + ports[0] = pool->proxy_port[0]; + ports[1] = pool->proxy_port[1]; + nvlist_add_number_array(nvl, "proxy_port", ports, 2); + nvlist_add_number(nvl, "opts", pool->opts); + + nvlist_add_nvlist(nvparent, name, nvl); +} + +static void +pfctl_nv_add_uid(nvlist_t *nvparent, const char *name, + const struct pf_rule_uid *uid) +{ + u_int64_t uids[2]; + nvlist_t *nvl = nvlist_create(0); + + uids[0] = uid->uid[0]; + uids[1] = uid->uid[1]; + nvlist_add_number_array(nvl, "uid", uids, 2); + nvlist_add_number(nvl, "op", uid->op); + + nvlist_add_nvlist(nvparent, name, nvl); +} + +static void +pfctl_nv_add_divert(nvlist_t *nvparent, const char *name, + const struct pf_rule *r) +{ + nvlist_t *nvl = nvlist_create(0); + + pfctl_nv_add_addr(nvl, "addr", &r->divert.addr); + nvlist_add_number(nvl, "port", r->divert.port); + + nvlist_add_nvlist(nvparent, name, nvl); +} + +static int +pfctl_addrule(struct pfctl *pf, const struct pf_rule *r, const char *anchor, + const char *anchor_call, u_int32_t ticket, u_int32_t pool_ticket) +{ + struct pfioc_nv nv; + u_int64_t timeouts[PFTM_MAX]; + u_int64_t set_prio[2]; + nvlist_t *nvl, *nvlr; + int ret; + + nvl = nvlist_create(0); + nvlr = nvlist_create(0); + + nvlist_add_number(nvl, "ticket", ticket); + nvlist_add_number(nvl, "pool_ticket", pool_ticket); + nvlist_add_string(nvl, "anchor", anchor); + nvlist_add_string(nvl, "anchor_call", anchor_call); + + nvlist_add_number(nvlr, "nr", r->nr); + pfctl_nv_add_rule_addr(nvlr, "src", &r->src); + pfctl_nv_add_rule_addr(nvlr, "dst", &r->dst); + + nvlist_add_string(nvlr, "label", r->label); + nvlist_add_string(nvlr, "ifname", r->ifname); + nvlist_add_string(nvlr, "qname", r->qname); + nvlist_add_string(nvlr, "pqname", r->pqname); + nvlist_add_string(nvlr, "tagname", r->tagname); + nvlist_add_string(nvlr, "match_tagname", r->match_tagname); + nvlist_add_string(nvlr, "overload_tblname", r->overload_tblname); + + pfctl_nv_add_pool(nvlr, "rpool", &r->rpool); + + nvlist_add_number(nvlr, "os_fingerprint", r->os_fingerprint); + + nvlist_add_number(nvlr, "rtableid", r->rtableid); + for (int i = 0; i < PFTM_MAX; i++) + timeouts[i] = r->timeout[i]; + nvlist_add_number_array(nvlr, "timeout", timeouts, PFTM_MAX); + nvlist_add_number(nvlr, "max_states", r->max_states); + nvlist_add_number(nvlr, "max_src_nodes", r->max_src_nodes); + nvlist_add_number(nvlr, "max_src_states", r->max_src_states); + nvlist_add_number(nvlr, "max_src_conn", r->max_src_conn); + nvlist_add_number(nvlr, "max_src_conn_rate.limit", + r->max_src_conn_rate.limit); + nvlist_add_number(nvlr, "max_src_conn_rate.seconds", + r->max_src_conn_rate.seconds); + nvlist_add_number(nvlr, "prob", r->prob); + nvlist_add_number(nvlr, "cuid", r->cuid); + nvlist_add_number(nvlr, "cpid", r->cpid); + + nvlist_add_number(nvlr, "return_icmp", r->return_icmp); + nvlist_add_number(nvlr, "return_icmp6", r->return_icmp6); + + nvlist_add_number(nvlr, "max_mss", r->max_mss); + nvlist_add_number(nvlr, "scrub_flags", r->scrub_flags); + + pfctl_nv_add_uid(nvlr, "uid", &r->uid); + pfctl_nv_add_uid(nvlr, "gid", (struct pf_rule_uid *)&r->gid); + + nvlist_add_number(nvlr, "rule_flag", r->rule_flag); + nvlist_add_number(nvlr, "action", r->action); + nvlist_add_number(nvlr, "direction", r->direction); + nvlist_add_number(nvlr, "log", r->log); + nvlist_add_number(nvlr, "logif", r->logif); + nvlist_add_number(nvlr, "quick", r->quick); + nvlist_add_number(nvlr, "ifnot", r->ifnot); + nvlist_add_number(nvlr, "match_tag_not", r->match_tag_not); + nvlist_add_number(nvlr, "natpass", r->natpass); + + nvlist_add_number(nvlr, "keep_state", r->keep_state); + nvlist_add_number(nvlr, "af", r->af); + nvlist_add_number(nvlr, "proto", r->proto); + nvlist_add_number(nvlr, "type", r->type); + nvlist_add_number(nvlr, "code", r->code); + nvlist_add_number(nvlr, "flags", r->flags); + nvlist_add_number(nvlr, "flagset", r->flagset); + nvlist_add_number(nvlr, "min_ttl", r->min_ttl); + nvlist_add_number(nvlr, "allow_opts", r->allow_opts); + nvlist_add_number(nvlr, "rt", r->rt); + nvlist_add_number(nvlr, "return_ttl", r->return_ttl); + nvlist_add_number(nvlr, "tos", r->tos); + nvlist_add_number(nvlr, "set_tos", r->set_tos); + nvlist_add_number(nvlr, "anchor_relative", r->anchor_relative); + nvlist_add_number(nvlr, "anchor_wildcard", r->anchor_wildcard); + + nvlist_add_number(nvlr, "flush", r->flush); + + nvlist_add_number(nvlr, "prio", r->prio); + set_prio[0] = r->set_prio[0]; + set_prio[1] = r->set_prio[1]; + nvlist_add_number_array(nvlr, "set_prio", set_prio, 2); + + pfctl_nv_add_divert(nvlr, "divert", r); + + nvlist_add_nvlist(nvl, "rule", nvlr); + + /* Now do the call. */ + nv.data = nvlist_pack(nvl, &nv.len); + nv.size = nv.len; + + ret = ioctl(pf->dev, DIOCADDRULENV, &nv); + + free(nv.data); + nvlist_destroy(nvl); + + return (ret); +} + int pfctl_load_rule(struct pfctl *pf, char *path, struct pf_rule *r, int depth) { u_int8_t rs_num = pf_get_ruleset_number(r->action); char *name; - struct pfioc_rule pr; + u_int32_t ticket; + char anchor[PF_ANCHOR_NAME_SIZE]; int len = strlen(path); - bzero(&pr, sizeof(pr)); /* set up anchor before adding to path for anchor_call */ if ((pf->opts & PF_OPT_NOACTION) == 0) - pr.ticket = pfctl_get_ticket(pf->trans, rs_num, path); - if (strlcpy(pr.anchor, path, sizeof(pr.anchor)) >= sizeof(pr.anchor)) + ticket = pfctl_get_ticket(pf->trans, rs_num, path); + if (strlcpy(anchor, path, sizeof(anchor)) >= sizeof(anchor)) errx(1, "pfctl_load_rule: strlcpy"); if (r->anchor) { @@ -1454,13 +1653,9 @@ if ((pf->opts & PF_OPT_NOACTION) == 0) { if (pfctl_add_pool(pf, &r->rpool, r->af)) return (1); - pr.pool_ticket = pf->paddr.ticket; - memcpy(&pr.rule, r, sizeof(pr.rule)); - if (r->anchor && strlcpy(pr.anchor_call, name, - sizeof(pr.anchor_call)) >= sizeof(pr.anchor_call)) - errx(1, "pfctl_load_rule: strlcpy"); - if (ioctl(pf->dev, DIOCADDRULE, &pr)) - err(1, "DIOCADDRULE"); + if (pfctl_addrule(pf, r, anchor, name, ticket, + pf->paddr.ticket)) + err(1, "DIOCADDRULENV"); } if (pf->opts & PF_OPT_VERBOSE) {