diff --git a/sbin/pfctl/Makefile b/sbin/pfctl/Makefile --- a/sbin/pfctl/Makefile +++ b/sbin/pfctl/Makefile @@ -9,7 +9,7 @@ SRCS = pfctl.c parse.y pfctl_parser.c pf_print_state.c pfctl_altq.c SRCS+= pfctl_osfp.c pfctl_radix.c pfctl_table.c pfctl_qstats.c -SRCS+= pfctl_optimize.c +SRCS+= pfctl_optimize.c pfctl_ioctl.c SRCS+= pf_ruleset.c WARNS?= 2 diff --git a/sbin/pfctl/pfctl.c b/sbin/pfctl/pfctl.c --- a/sbin/pfctl/pfctl.c +++ b/sbin/pfctl/pfctl.c @@ -63,6 +63,7 @@ #include #include +#include "pfctl_ioctl.h" #include "pfctl_parser.h" #include "pfctl.h" @@ -952,8 +953,9 @@ for (nr = 0; nr < mnr; ++nr) { pr.nr = nr; - if (ioctl(dev, DIOCGETRULE, &pr)) { - warn("DIOCGETRULE"); + if (pfctl_get_rule(dev, nr, pr.ticket, path, PF_SCRUB, + &pr.rule, pr.anchor_call)) { + warn("DIOCGETRULENV"); goto error; } @@ -984,7 +986,8 @@ mnr = pr.nr; for (nr = 0; nr < mnr; ++nr) { pr.nr = nr; - if (ioctl(dev, DIOCGETRULE, &pr)) { + if (pfctl_get_rule(dev, nr, pr.ticket, path, PF_PASS, + &pr.rule, pr.anchor_call)) { warn("DIOCGETRULE"); goto error; } @@ -1074,7 +1077,8 @@ mnr = pr.nr; for (nr = 0; nr < mnr; ++nr) { pr.nr = nr; - if (ioctl(dev, DIOCGETRULE, &pr)) { + if (pfctl_get_rule(dev, nr, pr.ticket, anchorname, + nattype[i], &pr.rule, pr.anchor_call)) { warn("DIOCGETRULE"); return (-1); } diff --git a/sbin/pfctl/pfctl_ioctl.h b/sbin/pfctl/pfctl_ioctl.h new file mode 100644 --- /dev/null +++ b/sbin/pfctl/pfctl_ioctl.h @@ -0,0 +1,43 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2021 Rubicon Communications, LLC (Netgate) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#ifndef _PFCTL_IOCTL_H_ +#define _PFCTL_IOCTL_H_ + +#include + +int pfctl_get_rule(int dev, u_int32_t nr, u_int32_t ticket, + const char *anchor, u_int32_t ruleset, struct pf_rule *rule, + char *anchor_call); + +#endif diff --git a/sbin/pfctl/pfctl_ioctl.c b/sbin/pfctl/pfctl_ioctl.c new file mode 100644 --- /dev/null +++ b/sbin/pfctl/pfctl_ioctl.c @@ -0,0 +1,339 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2021 Rubicon Communications, LLC (Netgate) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#include + +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "pfctl_ioctl.h" + +static void +pf_nvuint_8_array(const nvlist_t *nvl, const char *name, size_t maxelems, + u_int8_t *numbers, size_t *nelems) +{ + const uint64_t *tmp; + size_t elems; + + tmp = nvlist_get_number_array(nvl, name, &elems); + assert(elems <= maxelems); + + for (size_t i = 0; i < elems; i++) + numbers[i] = tmp[i]; + + if (nelems) + *nelems = elems; +} + +static void +pf_nvuint_16_array(const nvlist_t *nvl, const char *name, size_t maxelems, + u_int16_t *numbers, size_t *nelems) +{ + const uint64_t *tmp; + size_t elems; + + tmp = nvlist_get_number_array(nvl, name, &elems); + assert(elems <= maxelems); + + for (size_t i = 0; i < elems; i++) + numbers[i] = tmp[i]; + + if (nelems) + *nelems = elems; +} + +static void +pf_nvuint_32_array(const nvlist_t *nvl, const char *name, size_t maxelems, + u_int32_t *numbers, size_t *nelems) +{ + const uint64_t *tmp; + size_t elems; + + tmp = nvlist_get_number_array(nvl, name, &elems); + assert(elems <= maxelems); + + for (size_t i = 0; i < elems; i++) + numbers[i] = tmp[i]; + + if (nelems) + *nelems = elems; +} + +static void +pf_nvuint_64_array(const nvlist_t *nvl, const char *name, size_t maxelems, + u_int64_t *numbers, size_t *nelems) +{ + const uint64_t *tmp; + size_t elems; + + tmp = nvlist_get_number_array(nvl, name, &elems); + assert(elems <= maxelems); + + for (size_t i = 0; i < elems; i++) + numbers[i] = tmp[i]; + + if (nelems) + *nelems = elems; +} + +static void +pf_nvaddr_to_addr(const nvlist_t *nvl, struct pf_addr *addr) +{ + size_t len; + const void *data; + + data = nvlist_get_binary(nvl, "addr", &len); + assert(len == sizeof(struct pf_addr)); + memcpy(addr, data, len); +} + +static void +pf_nvaddr_wrap_to_addr_wrap(const nvlist_t *nvl, struct pf_addr_wrap *addr) +{ + addr->type = nvlist_get_number(nvl, "type"); + addr->iflags = nvlist_get_number(nvl, "iflags"); + strlcpy(addr->v.ifname, nvlist_get_string(nvl, "ifname"), IFNAMSIZ); + strlcpy(addr->v.tblname, nvlist_get_string(nvl, "tblname"), + PF_TABLE_NAME_SIZE); + + pf_nvaddr_to_addr(nvlist_get_nvlist(nvl, "addr"), &addr->v.a.addr); + pf_nvaddr_to_addr(nvlist_get_nvlist(nvl, "mask"), &addr->v.a.mask); +} + +static void +pf_nvrule_addr_to_rule_addr(const nvlist_t *nvl, struct pf_rule_addr *addr) +{ + pf_nvaddr_wrap_to_addr_wrap(nvlist_get_nvlist(nvl, "addr"), &addr->addr); + + pf_nvuint_16_array(nvl, "port", 2, addr->port, NULL); + addr->neg = nvlist_get_number(nvl, "neg"); + addr->port_op = nvlist_get_number(nvl, "port_op"); +} + +static void +pf_nvpool_to_pool(const nvlist_t *nvl, struct pf_pool *pool) +{ + size_t len; + const void *data; + + data = nvlist_get_binary(nvl, "key", &len); + assert(len == sizeof(pool->key)); + memcpy(&pool->key, data, len); + + pf_nvaddr_to_addr(nvlist_get_nvlist(nvl, "counter"), &pool->counter); + + pool->tblidx = nvlist_get_number(nvl, "tblidx"); + pf_nvuint_16_array(nvl, "proxy_port", 2, pool->proxy_port, NULL); + pool->opts = nvlist_get_number(nvl, "opts"); +} + +static void +pf_nvrule_uid_to_rule_uid(const nvlist_t *nvl, struct pf_rule_uid *uid) +{ + pf_nvuint_32_array(nvl, "uid", 2, uid->uid, NULL); + uid->op = nvlist_get_number(nvl, "op"); +} + +static void +pf_nvdivert_to_divert(const nvlist_t *nvl, struct pf_rule *rule) +{ + pf_nvaddr_to_addr(nvlist_get_nvlist(nvl, "addr"), &rule->divert.addr); + rule->divert.port = nvlist_get_number(nvl, "port"); +} + +static void +pf_nvrule_to_rule(const nvlist_t *nvl, struct pf_rule *rule) +{ + const uint64_t *skip; + size_t skipcount; + + rule->nr = nvlist_get_number(nvl, "nr"); + + pf_nvrule_addr_to_rule_addr(nvlist_get_nvlist(nvl, "src"), &rule->src); + pf_nvrule_addr_to_rule_addr(nvlist_get_nvlist(nvl, "dst"), &rule->dst); + + skip = nvlist_get_number_array(nvl, "skip", &skipcount); + assert(skip); + assert(skipcount == PF_SKIP_COUNT); + for (int i = 0; i < PF_SKIP_COUNT; i++) + rule->skip[i].nr = skip[i]; + + strlcpy(rule->label, nvlist_get_string(nvl, "label"), PF_RULE_LABEL_SIZE); + strlcpy(rule->ifname, nvlist_get_string(nvl, "ifname"), IFNAMSIZ); + strlcpy(rule->qname, nvlist_get_string(nvl, "qname"), PF_QNAME_SIZE); + strlcpy(rule->pqname, nvlist_get_string(nvl, "pqname"), PF_QNAME_SIZE); + strlcpy(rule->tagname, nvlist_get_string(nvl, "tagname"), + PF_TAG_NAME_SIZE); + strlcpy(rule->match_tagname, nvlist_get_string(nvl, "match_tagname"), + PF_TAG_NAME_SIZE); + + strlcpy(rule->overload_tblname, nvlist_get_string(nvl, "overload_tblname"), + PF_TABLE_NAME_SIZE); + + pf_nvpool_to_pool(nvlist_get_nvlist(nvl, "rpool"), &rule->rpool); + + rule->evaluations = nvlist_get_number(nvl, "evaluations"); + pf_nvuint_64_array(nvl, "packets", 2, rule->packets, NULL); + pf_nvuint_64_array(nvl, "bytes", 2, rule->bytes, NULL); + + rule->os_fingerprint = nvlist_get_number(nvl, "os_fingerprint"); + + rule->rtableid = nvlist_get_number(nvl, "rtableid"); + pf_nvuint_32_array(nvl, "timeout", PFTM_MAX, rule->timeout, NULL); + rule->max_states = nvlist_get_number(nvl, "max_states"); + rule->max_src_nodes = nvlist_get_number(nvl, "max_src_nodes"); + rule->max_src_states = nvlist_get_number(nvl, "max_src_states"); + rule->max_src_conn = nvlist_get_number(nvl, "max_src_conn"); + rule->max_src_conn_rate.limit = + nvlist_get_number(nvl, "max_src_conn_rate.limit"); + rule->max_src_conn_rate.seconds = + nvlist_get_number(nvl, "max_src_conn_rate.seconds"); + rule->qid = nvlist_get_number(nvl, "qid"); + rule->pqid = nvlist_get_number(nvl, "pqid"); + rule->prob = nvlist_get_number(nvl, "prob"); + rule->cuid = nvlist_get_number(nvl, "cuid"); + rule->cpid = nvlist_get_number(nvl, "cpid"); + + rule->return_icmp = nvlist_get_number(nvl, "return_icmp"); + rule->return_icmp6 = nvlist_get_number(nvl, "return_icmp6"); + rule->max_mss = nvlist_get_number(nvl, "max_mss"); + rule->scrub_flags = nvlist_get_number(nvl, "scrub_flags"); + + pf_nvrule_uid_to_rule_uid(nvlist_get_nvlist(nvl, "uid"), &rule->uid); + pf_nvrule_uid_to_rule_uid(nvlist_get_nvlist(nvl, "gid"), + (struct pf_rule_uid *)&rule->gid); + + rule->rule_flag = nvlist_get_number(nvl, "rule_flag"); + rule->action = nvlist_get_number(nvl, "action"); + rule->direction = nvlist_get_number(nvl, "direction"); + rule->log = nvlist_get_number(nvl, "log"); + rule->logif = nvlist_get_number(nvl, "logif"); + rule->quick = nvlist_get_number(nvl, "quick"); + rule->ifnot = nvlist_get_number(nvl, "ifnot"); + rule->match_tag_not = nvlist_get_number(nvl, "match_tag_not"); + rule->natpass = nvlist_get_number(nvl, "natpass"); + + rule->keep_state = nvlist_get_number(nvl, "keep_state"); + rule->af = nvlist_get_number(nvl, "af"); + rule->proto = nvlist_get_number(nvl, "proto"); + rule->type = nvlist_get_number(nvl, "type"); + rule->code = nvlist_get_number(nvl, "code"); + rule->flags = nvlist_get_number(nvl, "flags"); + rule->flagset = nvlist_get_number(nvl, "flagset"); + rule->min_ttl = nvlist_get_number(nvl, "min_ttl"); + rule->allow_opts = nvlist_get_number(nvl, "allow_opts"); + rule->rt = nvlist_get_number(nvl, "rt"); + rule->return_ttl = nvlist_get_number(nvl, "return_ttl"); + rule->tos = nvlist_get_number(nvl, "tos"); + rule->set_tos = nvlist_get_number(nvl, "set_tos"); + rule->anchor_relative = nvlist_get_number(nvl, "anchor_relative"); + rule->anchor_wildcard = nvlist_get_number(nvl, "anchor_wildcard"); + + rule->flush = nvlist_get_number(nvl, "flush"); + rule->prio = nvlist_get_number(nvl, "prio"); + pf_nvuint_8_array(nvl, "set_prio", 2, rule->set_prio, NULL); + + pf_nvdivert_to_divert(nvlist_get_nvlist(nvl, "divert"), rule); + + rule->u_states_cur = nvlist_get_number(nvl, "states_cur"); + rule->u_states_tot = nvlist_get_number(nvl, "states_tot"); + rule->u_src_nodes = nvlist_get_number(nvl, "src_nodes"); +} + + +int +pfctl_get_rule(int dev, u_int32_t nr, u_int32_t ticket, const char *anchor, + u_int32_t ruleset, struct pf_rule *rule, char *anchor_call) +{ + struct pfioc_nv nv; + nvlist_t *nvl; + void *nvlpacked; + int ret; + + nvl = nvlist_create(0); + if (nvl == 0) + return (ENOMEM); + + nvlist_add_number(nvl, "nr", nr); + nvlist_add_number(nvl, "ticket", ticket); + nvlist_add_string(nvl, "anchor", anchor); + nvlist_add_number(nvl, "ruleset", ruleset); + + nvlpacked = nvlist_pack(nvl, &nv.len); + if (nvlpacked == NULL) { + nvlist_destroy(nvl); + return (ENOMEM); + } + nv.data = malloc(8182); + nv.size = 8192; + assert(nv.len <= nv.size); + memcpy(nv.data, nvlpacked, nv.len); + nvlist_destroy(nvl); + nvl = NULL; + free(nvlpacked); + + ret = ioctl(dev, DIOCGETRULENV, &nv); + if (ret != 0) { + free(nv.data); + return (ret); + } + + nvl = nvlist_unpack(nv.data, nv.len, 0); + if (nvl == NULL) { + free(nv.data); + return (EIO); + } + + pf_nvrule_to_rule(nvlist_get_nvlist(nvl, "rule"), rule); + + if (anchor_call) + strlcpy(anchor_call, nvlist_get_string(nvl, "anchor_call"), + MAXPATHLEN); + + free(nv.data); + nvlist_destroy(nvl); + + return (0); +} diff --git a/sbin/pfctl/pfctl_optimize.c b/sbin/pfctl/pfctl_optimize.c --- a/sbin/pfctl/pfctl_optimize.c +++ b/sbin/pfctl/pfctl_optimize.c @@ -38,6 +38,7 @@ #include #include +#include "pfctl_ioctl.h" #include "pfctl_parser.h" #include "pfctl.h" @@ -909,8 +910,10 @@ return (1); } pr.nr = nr; - if (ioctl(pf->dev, DIOCGETRULE, &pr)) { - warn("DIOCGETRULES"); + + if (pfctl_get_rule(pf->dev, nr, pr.ticket, "", PF_PASS, + &pr.rule, pr.anchor_call)) { + warn("DIOCGETRULENV"); return (1); } memcpy(&por->por_rule, &pr.rule, sizeof(por->por_rule));