mirror of
https://github.com/opnsense/src.git
synced 2026-06-11 09:41:03 -04:00
Use netlink to export pf's state table. The primary motivation is to improve how we deal with very large state stables. With the previous implementation we had to build the entire list (both in the kernel and in userspace) before we could start processing. With netlink we start to get data in userspace while the kernel is still generating more. This reduces peak memory consumption (which can get to the GB range once we hit millions of states). Netlink also makes future extension easier, in that we can easily add fields to the state export without breaking userspace. In that regard it's similar to an nvlist-based approach, except that it also deals with transport to userspace and that it performs significantly better than nvlists. Testing has failed to measure a performance difference between the previous struct-copy based ioctl and the netlink approach. Differential Revision: https://reviews.freebsd.org/D38888
1589 lines
43 KiB
C
1589 lines
43 KiB
C
/*-
|
|
* 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.
|
|
*/
|
|
|
|
#include <sys/cdefs.h>
|
|
|
|
#include <sys/ioctl.h>
|
|
#include <sys/nv.h>
|
|
#include <sys/queue.h>
|
|
#include <sys/types.h>
|
|
|
|
#include <net/if.h>
|
|
#include <net/pfvar.h>
|
|
#include <netinet/in.h>
|
|
|
|
#include <netpfil/pf/pf_nl.h>
|
|
#include <netlink/netlink.h>
|
|
#include <netlink/netlink_generic.h>
|
|
#include <netlink/netlink_snl.h>
|
|
#include <netlink/netlink_snl_generic.h>
|
|
#include <netlink/netlink_snl_route.h>
|
|
|
|
#include <assert.h>
|
|
#include <err.h>
|
|
#include <errno.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#include "libpfctl.h"
|
|
|
|
const char* PFCTL_SYNCOOKIES_MODE_NAMES[] = {
|
|
"never",
|
|
"always",
|
|
"adaptive"
|
|
};
|
|
|
|
static int _pfctl_clear_states(int , const struct pfctl_kill *,
|
|
unsigned int *, uint64_t);
|
|
|
|
static int
|
|
pfctl_do_ioctl(int dev, uint cmd, size_t size, nvlist_t **nvl)
|
|
{
|
|
struct pfioc_nv nv;
|
|
void *data;
|
|
size_t nvlen;
|
|
int ret;
|
|
|
|
data = nvlist_pack(*nvl, &nvlen);
|
|
if (nvlen > size)
|
|
size = nvlen;
|
|
|
|
retry:
|
|
nv.data = malloc(size);
|
|
memcpy(nv.data, data, nvlen);
|
|
free(data);
|
|
|
|
nv.len = nvlen;
|
|
nv.size = size;
|
|
|
|
ret = ioctl(dev, cmd, &nv);
|
|
if (ret == -1 && errno == ENOSPC) {
|
|
size *= 2;
|
|
free(nv.data);
|
|
goto retry;
|
|
}
|
|
|
|
nvlist_destroy(*nvl);
|
|
*nvl = NULL;
|
|
|
|
if (ret == 0) {
|
|
*nvl = nvlist_unpack(nv.data, nv.len, 0);
|
|
if (*nvl == NULL) {
|
|
free(nv.data);
|
|
return (EIO);
|
|
}
|
|
} else {
|
|
ret = errno;
|
|
}
|
|
|
|
free(nv.data);
|
|
|
|
return (ret);
|
|
}
|
|
|
|
static void
|
|
pf_nvuint_8_array(const nvlist_t *nvl, const char *name, size_t maxelems,
|
|
uint8_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,
|
|
uint16_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,
|
|
uint32_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,
|
|
uint64_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
|
|
_pfctl_get_status_counters(const nvlist_t *nvl,
|
|
struct pfctl_status_counters *counters)
|
|
{
|
|
const uint64_t *ids, *counts;
|
|
const char *const *names;
|
|
size_t id_len, counter_len, names_len;
|
|
|
|
ids = nvlist_get_number_array(nvl, "ids", &id_len);
|
|
counts = nvlist_get_number_array(nvl, "counters", &counter_len);
|
|
names = nvlist_get_string_array(nvl, "names", &names_len);
|
|
assert(id_len == counter_len);
|
|
assert(counter_len == names_len);
|
|
|
|
TAILQ_INIT(counters);
|
|
|
|
for (size_t i = 0; i < id_len; i++) {
|
|
struct pfctl_status_counter *c;
|
|
|
|
c = malloc(sizeof(*c));
|
|
|
|
c->id = ids[i];
|
|
c->counter = counts[i];
|
|
c->name = strdup(names[i]);
|
|
|
|
TAILQ_INSERT_TAIL(counters, c, entry);
|
|
}
|
|
}
|
|
|
|
struct pfctl_status *
|
|
pfctl_get_status(int dev)
|
|
{
|
|
struct pfctl_status *status;
|
|
nvlist_t *nvl;
|
|
size_t len;
|
|
const void *chksum;
|
|
|
|
status = calloc(1, sizeof(*status));
|
|
if (status == NULL)
|
|
return (NULL);
|
|
|
|
nvl = nvlist_create(0);
|
|
|
|
if (pfctl_do_ioctl(dev, DIOCGETSTATUSNV, 4096, &nvl)) {
|
|
free(status);
|
|
return (NULL);
|
|
}
|
|
|
|
status->running = nvlist_get_bool(nvl, "running");
|
|
status->since = nvlist_get_number(nvl, "since");
|
|
status->debug = nvlist_get_number(nvl, "debug");
|
|
status->hostid = ntohl(nvlist_get_number(nvl, "hostid"));
|
|
status->states = nvlist_get_number(nvl, "states");
|
|
status->src_nodes = nvlist_get_number(nvl, "src_nodes");
|
|
status->syncookies_active = nvlist_get_bool(nvl, "syncookies_active");
|
|
status->reass = nvlist_get_number(nvl, "reass");
|
|
|
|
strlcpy(status->ifname, nvlist_get_string(nvl, "ifname"),
|
|
IFNAMSIZ);
|
|
chksum = nvlist_get_binary(nvl, "chksum", &len);
|
|
assert(len == PF_MD5_DIGEST_LENGTH);
|
|
memcpy(status->pf_chksum, chksum, len);
|
|
|
|
_pfctl_get_status_counters(nvlist_get_nvlist(nvl, "counters"),
|
|
&status->counters);
|
|
_pfctl_get_status_counters(nvlist_get_nvlist(nvl, "lcounters"),
|
|
&status->lcounters);
|
|
_pfctl_get_status_counters(nvlist_get_nvlist(nvl, "fcounters"),
|
|
&status->fcounters);
|
|
_pfctl_get_status_counters(nvlist_get_nvlist(nvl, "scounters"),
|
|
&status->scounters);
|
|
|
|
pf_nvuint_64_array(nvl, "pcounters", 2 * 2 * 3,
|
|
(uint64_t *)status->pcounters, NULL);
|
|
pf_nvuint_64_array(nvl, "bcounters", 2 * 2,
|
|
(uint64_t *)status->bcounters, NULL);
|
|
|
|
nvlist_destroy(nvl);
|
|
|
|
return (status);
|
|
}
|
|
|
|
static uint64_t
|
|
_pfctl_status_counter(struct pfctl_status_counters *counters, uint64_t id)
|
|
{
|
|
struct pfctl_status_counter *c;
|
|
|
|
TAILQ_FOREACH(c, counters, entry) {
|
|
if (c->id == id)
|
|
return (c->counter);
|
|
}
|
|
|
|
return (0);
|
|
}
|
|
|
|
uint64_t
|
|
pfctl_status_counter(struct pfctl_status *status, int id)
|
|
{
|
|
return (_pfctl_status_counter(&status->counters, id));
|
|
}
|
|
|
|
uint64_t
|
|
pfctl_status_fcounter(struct pfctl_status *status, int id)
|
|
{
|
|
return (_pfctl_status_counter(&status->fcounters, id));
|
|
}
|
|
|
|
uint64_t
|
|
pfctl_status_scounter(struct pfctl_status *status, int id)
|
|
{
|
|
return (_pfctl_status_counter(&status->scounters, id));
|
|
}
|
|
|
|
void
|
|
pfctl_free_status(struct pfctl_status *status)
|
|
{
|
|
struct pfctl_status_counter *c, *tmp;
|
|
|
|
if (status == NULL)
|
|
return;
|
|
|
|
TAILQ_FOREACH_SAFE(c, &status->counters, entry, tmp) {
|
|
free(c->name);
|
|
free(c);
|
|
}
|
|
TAILQ_FOREACH_SAFE(c, &status->lcounters, entry, tmp) {
|
|
free(c->name);
|
|
free(c);
|
|
}
|
|
TAILQ_FOREACH_SAFE(c, &status->fcounters, entry, tmp) {
|
|
free(c->name);
|
|
free(c);
|
|
}
|
|
TAILQ_FOREACH_SAFE(c, &status->scounters, entry, tmp) {
|
|
free(c->name);
|
|
free(c);
|
|
}
|
|
|
|
free(status);
|
|
}
|
|
|
|
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);
|
|
nvlist_destroy(nvl);
|
|
}
|
|
|
|
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
|
|
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);
|
|
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);
|
|
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);
|
|
nvlist_destroy(nvl);
|
|
}
|
|
|
|
static void
|
|
pf_nvaddr_wrap_to_addr_wrap(const nvlist_t *nvl, struct pf_addr_wrap *addr)
|
|
{
|
|
bzero(addr, sizeof(*addr));
|
|
|
|
addr->type = nvlist_get_number(nvl, "type");
|
|
addr->iflags = nvlist_get_number(nvl, "iflags");
|
|
if (addr->type == PF_ADDR_DYNIFTL) {
|
|
strlcpy(addr->v.ifname, nvlist_get_string(nvl, "ifname"),
|
|
IFNAMSIZ);
|
|
addr->p.dyncnt = nvlist_get_number(nvl, "dyncnt");
|
|
}
|
|
if (addr->type == PF_ADDR_TABLE) {
|
|
strlcpy(addr->v.tblname, nvlist_get_string(nvl, "tblname"),
|
|
PF_TABLE_NAME_SIZE);
|
|
addr->p.tblcnt = nvlist_get_number(nvl, "tblcnt");
|
|
}
|
|
|
|
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
|
|
pfctl_nv_add_rule_addr(nvlist_t *nvparent, const char *name,
|
|
const struct pf_rule_addr *addr)
|
|
{
|
|
uint64_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);
|
|
nvlist_destroy(nvl);
|
|
}
|
|
|
|
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
|
|
pfctl_nv_add_mape(nvlist_t *nvparent, const char *name,
|
|
const struct pf_mape_portset *mape)
|
|
{
|
|
nvlist_t *nvl = nvlist_create(0);
|
|
|
|
nvlist_add_number(nvl, "offset", mape->offset);
|
|
nvlist_add_number(nvl, "psidlen", mape->psidlen);
|
|
nvlist_add_number(nvl, "psid", mape->psid);
|
|
nvlist_add_nvlist(nvparent, name, nvl);
|
|
nvlist_destroy(nvl);
|
|
}
|
|
|
|
static void
|
|
pfctl_nv_add_pool(nvlist_t *nvparent, const char *name,
|
|
const struct pfctl_pool *pool)
|
|
{
|
|
uint64_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);
|
|
pfctl_nv_add_mape(nvl, "mape", &pool->mape);
|
|
|
|
nvlist_add_nvlist(nvparent, name, nvl);
|
|
nvlist_destroy(nvl);
|
|
}
|
|
|
|
static void
|
|
pf_nvmape_to_mape(const nvlist_t *nvl, struct pf_mape_portset *mape)
|
|
{
|
|
mape->offset = nvlist_get_number(nvl, "offset");
|
|
mape->psidlen = nvlist_get_number(nvl, "psidlen");
|
|
mape->psid = nvlist_get_number(nvl, "psid");
|
|
}
|
|
|
|
static void
|
|
pf_nvpool_to_pool(const nvlist_t *nvl, struct pfctl_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");
|
|
|
|
if (nvlist_exists_nvlist(nvl, "mape"))
|
|
pf_nvmape_to_mape(nvlist_get_nvlist(nvl, "mape"), &pool->mape);
|
|
}
|
|
|
|
static void
|
|
pfctl_nv_add_uid(nvlist_t *nvparent, const char *name,
|
|
const struct pf_rule_uid *uid)
|
|
{
|
|
uint64_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);
|
|
nvlist_destroy(nvl);
|
|
}
|
|
|
|
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
|
|
pfctl_nv_add_divert(nvlist_t *nvparent, const char *name,
|
|
const struct pfctl_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);
|
|
nvlist_destroy(nvl);
|
|
}
|
|
|
|
static void
|
|
pf_nvdivert_to_divert(const nvlist_t *nvl, struct pfctl_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 pfctl_rule *rule)
|
|
{
|
|
const uint64_t *skip;
|
|
const char *const *labels;
|
|
size_t skipcount, labelcount;
|
|
|
|
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];
|
|
|
|
labels = nvlist_get_string_array(nvl, "labels", &labelcount);
|
|
assert(labelcount <= PF_RULE_MAX_LABEL_COUNT);
|
|
for (size_t i = 0; i < labelcount; i++)
|
|
strlcpy(rule->label[i], labels[i], PF_RULE_LABEL_SIZE);
|
|
rule->ridentifier = nvlist_get_number(nvl, "ridentifier");
|
|
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);
|
|
|
|
if (nvlist_exists_number(nvl, "timestamp")) {
|
|
rule->last_active_timestamp = nvlist_get_number(nvl, "timestamp");
|
|
}
|
|
|
|
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->dnpipe = nvlist_get_number(nvl, "dnpipe");
|
|
rule->dnrpipe = nvlist_get_number(nvl, "dnrpipe");
|
|
rule->free_flags = nvlist_get_number(nvl, "dnflags");
|
|
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->states_cur = nvlist_get_number(nvl, "states_cur");
|
|
rule->states_tot = nvlist_get_number(nvl, "states_tot");
|
|
rule->src_nodes = nvlist_get_number(nvl, "src_nodes");
|
|
}
|
|
|
|
static void
|
|
pfctl_nveth_addr_to_eth_addr(const nvlist_t *nvl, struct pfctl_eth_addr *addr)
|
|
{
|
|
static const u_int8_t EMPTY_MAC[ETHER_ADDR_LEN] = { 0 };
|
|
size_t len;
|
|
const void *data;
|
|
|
|
data = nvlist_get_binary(nvl, "addr", &len);
|
|
assert(len == sizeof(addr->addr));
|
|
memcpy(addr->addr, data, sizeof(addr->addr));
|
|
|
|
data = nvlist_get_binary(nvl, "mask", &len);
|
|
assert(len == sizeof(addr->mask));
|
|
memcpy(addr->mask, data, sizeof(addr->mask));
|
|
|
|
addr->neg = nvlist_get_bool(nvl, "neg");
|
|
|
|
/* To make checks for 'is this address set?' easier. */
|
|
addr->isset = memcmp(addr->addr, EMPTY_MAC, ETHER_ADDR_LEN) != 0;
|
|
}
|
|
|
|
static nvlist_t *
|
|
pfctl_eth_addr_to_nveth_addr(const struct pfctl_eth_addr *addr)
|
|
{
|
|
nvlist_t *nvl;
|
|
|
|
nvl = nvlist_create(0);
|
|
if (nvl == NULL)
|
|
return (NULL);
|
|
|
|
nvlist_add_bool(nvl, "neg", addr->neg);
|
|
nvlist_add_binary(nvl, "addr", &addr->addr, ETHER_ADDR_LEN);
|
|
nvlist_add_binary(nvl, "mask", &addr->mask, ETHER_ADDR_LEN);
|
|
|
|
return (nvl);
|
|
}
|
|
|
|
static void
|
|
pfctl_nveth_rule_to_eth_rule(const nvlist_t *nvl, struct pfctl_eth_rule *rule)
|
|
{
|
|
const char *const *labels;
|
|
size_t labelcount, i;
|
|
|
|
rule->nr = nvlist_get_number(nvl, "nr");
|
|
rule->quick = nvlist_get_bool(nvl, "quick");
|
|
strlcpy(rule->ifname, nvlist_get_string(nvl, "ifname"), IFNAMSIZ);
|
|
rule->ifnot = nvlist_get_bool(nvl, "ifnot");
|
|
rule->direction = nvlist_get_number(nvl, "direction");
|
|
rule->proto = nvlist_get_number(nvl, "proto");
|
|
strlcpy(rule->match_tagname, nvlist_get_string(nvl, "match_tagname"),
|
|
PF_TAG_NAME_SIZE);
|
|
rule->match_tag = nvlist_get_number(nvl, "match_tag");
|
|
rule->match_tag_not = nvlist_get_bool(nvl, "match_tag_not");
|
|
|
|
labels = nvlist_get_string_array(nvl, "labels", &labelcount);
|
|
assert(labelcount <= PF_RULE_MAX_LABEL_COUNT);
|
|
for (i = 0; i < labelcount; i++)
|
|
strlcpy(rule->label[i], labels[i], PF_RULE_LABEL_SIZE);
|
|
rule->ridentifier = nvlist_get_number(nvl, "ridentifier");
|
|
|
|
pfctl_nveth_addr_to_eth_addr(nvlist_get_nvlist(nvl, "src"),
|
|
&rule->src);
|
|
pfctl_nveth_addr_to_eth_addr(nvlist_get_nvlist(nvl, "dst"),
|
|
&rule->dst);
|
|
|
|
pf_nvrule_addr_to_rule_addr(nvlist_get_nvlist(nvl, "ipsrc"),
|
|
&rule->ipsrc);
|
|
pf_nvrule_addr_to_rule_addr(nvlist_get_nvlist(nvl, "ipdst"),
|
|
&rule->ipdst);
|
|
|
|
rule->evaluations = nvlist_get_number(nvl, "evaluations");
|
|
rule->packets[0] = nvlist_get_number(nvl, "packets-in");
|
|
rule->packets[1] = nvlist_get_number(nvl, "packets-out");
|
|
rule->bytes[0] = nvlist_get_number(nvl, "bytes-in");
|
|
rule->bytes[1] = nvlist_get_number(nvl, "bytes-out");
|
|
|
|
if (nvlist_exists_number(nvl, "timestamp")) {
|
|
rule->last_active_timestamp = nvlist_get_number(nvl, "timestamp");
|
|
}
|
|
|
|
strlcpy(rule->qname, nvlist_get_string(nvl, "qname"), PF_QNAME_SIZE);
|
|
strlcpy(rule->tagname, nvlist_get_string(nvl, "tagname"),
|
|
PF_TAG_NAME_SIZE);
|
|
|
|
rule->dnpipe = nvlist_get_number(nvl, "dnpipe");
|
|
rule->dnflags = nvlist_get_number(nvl, "dnflags");
|
|
|
|
rule->anchor_relative = nvlist_get_number(nvl, "anchor_relative");
|
|
rule->anchor_wildcard = nvlist_get_number(nvl, "anchor_wildcard");
|
|
|
|
strlcpy(rule->bridge_to, nvlist_get_string(nvl, "bridge_to"),
|
|
IFNAMSIZ);
|
|
|
|
rule->action = nvlist_get_number(nvl, "action");
|
|
}
|
|
|
|
int
|
|
pfctl_get_eth_rulesets_info(int dev, struct pfctl_eth_rulesets_info *ri,
|
|
const char *path)
|
|
{
|
|
nvlist_t *nvl;
|
|
int ret;
|
|
|
|
bzero(ri, sizeof(*ri));
|
|
|
|
nvl = nvlist_create(0);
|
|
nvlist_add_string(nvl, "path", path);
|
|
|
|
if ((ret = pfctl_do_ioctl(dev, DIOCGETETHRULESETS, 256, &nvl)) != 0)
|
|
return (ret);
|
|
|
|
ri->nr = nvlist_get_number(nvl, "nr");
|
|
|
|
nvlist_destroy(nvl);
|
|
return (0);
|
|
}
|
|
|
|
int
|
|
pfctl_get_eth_ruleset(int dev, const char *path, int nr,
|
|
struct pfctl_eth_ruleset_info *ri)
|
|
{
|
|
nvlist_t *nvl;
|
|
int ret;
|
|
|
|
bzero(ri, sizeof(*ri));
|
|
|
|
nvl = nvlist_create(0);
|
|
nvlist_add_string(nvl, "path", path);
|
|
nvlist_add_number(nvl, "nr", nr);
|
|
|
|
if ((ret = pfctl_do_ioctl(dev, DIOCGETETHRULESET, 1024, &nvl)) != 0)
|
|
return (ret);
|
|
|
|
ri->nr = nvlist_get_number(nvl, "nr");
|
|
strlcpy(ri->path, nvlist_get_string(nvl, "path"), MAXPATHLEN);
|
|
strlcpy(ri->name, nvlist_get_string(nvl, "name"),
|
|
PF_ANCHOR_NAME_SIZE);
|
|
|
|
return (0);
|
|
}
|
|
|
|
int
|
|
pfctl_get_eth_rules_info(int dev, struct pfctl_eth_rules_info *rules,
|
|
const char *path)
|
|
{
|
|
nvlist_t *nvl;
|
|
int ret;
|
|
|
|
bzero(rules, sizeof(*rules));
|
|
|
|
nvl = nvlist_create(0);
|
|
nvlist_add_string(nvl, "anchor", path);
|
|
|
|
if ((ret = pfctl_do_ioctl(dev, DIOCGETETHRULES, 1024, &nvl)) != 0)
|
|
return (ret);
|
|
|
|
rules->nr = nvlist_get_number(nvl, "nr");
|
|
rules->ticket = nvlist_get_number(nvl, "ticket");
|
|
|
|
nvlist_destroy(nvl);
|
|
return (0);
|
|
}
|
|
|
|
int
|
|
pfctl_get_eth_rule(int dev, uint32_t nr, uint32_t ticket,
|
|
const char *path, struct pfctl_eth_rule *rule, bool clear,
|
|
char *anchor_call)
|
|
{
|
|
nvlist_t *nvl;
|
|
int ret;
|
|
|
|
nvl = nvlist_create(0);
|
|
|
|
nvlist_add_string(nvl, "anchor", path);
|
|
nvlist_add_number(nvl, "ticket", ticket);
|
|
nvlist_add_number(nvl, "nr", nr);
|
|
nvlist_add_bool(nvl, "clear", clear);
|
|
|
|
if ((ret = pfctl_do_ioctl(dev, DIOCGETETHRULE, 4096, &nvl)) != 0)
|
|
return (ret);
|
|
|
|
pfctl_nveth_rule_to_eth_rule(nvl, rule);
|
|
|
|
if (anchor_call)
|
|
strlcpy(anchor_call, nvlist_get_string(nvl, "anchor_call"),
|
|
MAXPATHLEN);
|
|
|
|
nvlist_destroy(nvl);
|
|
return (0);
|
|
}
|
|
|
|
int
|
|
pfctl_add_eth_rule(int dev, const struct pfctl_eth_rule *r, const char *anchor,
|
|
const char *anchor_call, uint32_t ticket)
|
|
{
|
|
struct pfioc_nv nv;
|
|
nvlist_t *nvl, *addr;
|
|
void *packed;
|
|
int error = 0;
|
|
size_t labelcount, size;
|
|
|
|
nvl = nvlist_create(0);
|
|
|
|
nvlist_add_number(nvl, "ticket", ticket);
|
|
nvlist_add_string(nvl, "anchor", anchor);
|
|
nvlist_add_string(nvl, "anchor_call", anchor_call);
|
|
|
|
nvlist_add_number(nvl, "nr", r->nr);
|
|
nvlist_add_bool(nvl, "quick", r->quick);
|
|
nvlist_add_string(nvl, "ifname", r->ifname);
|
|
nvlist_add_bool(nvl, "ifnot", r->ifnot);
|
|
nvlist_add_number(nvl, "direction", r->direction);
|
|
nvlist_add_number(nvl, "proto", r->proto);
|
|
nvlist_add_string(nvl, "match_tagname", r->match_tagname);
|
|
nvlist_add_bool(nvl, "match_tag_not", r->match_tag_not);
|
|
|
|
addr = pfctl_eth_addr_to_nveth_addr(&r->src);
|
|
if (addr == NULL) {
|
|
nvlist_destroy(nvl);
|
|
return (ENOMEM);
|
|
}
|
|
nvlist_add_nvlist(nvl, "src", addr);
|
|
nvlist_destroy(addr);
|
|
|
|
addr = pfctl_eth_addr_to_nveth_addr(&r->dst);
|
|
if (addr == NULL) {
|
|
nvlist_destroy(nvl);
|
|
return (ENOMEM);
|
|
}
|
|
nvlist_add_nvlist(nvl, "dst", addr);
|
|
nvlist_destroy(addr);
|
|
|
|
pfctl_nv_add_rule_addr(nvl, "ipsrc", &r->ipsrc);
|
|
pfctl_nv_add_rule_addr(nvl, "ipdst", &r->ipdst);
|
|
|
|
labelcount = 0;
|
|
while (r->label[labelcount][0] != 0 &&
|
|
labelcount < PF_RULE_MAX_LABEL_COUNT) {
|
|
nvlist_append_string_array(nvl, "labels",
|
|
r->label[labelcount]);
|
|
labelcount++;
|
|
}
|
|
nvlist_add_number(nvl, "ridentifier", r->ridentifier);
|
|
|
|
nvlist_add_string(nvl, "qname", r->qname);
|
|
nvlist_add_string(nvl, "tagname", r->tagname);
|
|
nvlist_add_number(nvl, "dnpipe", r->dnpipe);
|
|
nvlist_add_number(nvl, "dnflags", r->dnflags);
|
|
|
|
nvlist_add_string(nvl, "bridge_to", r->bridge_to);
|
|
|
|
nvlist_add_number(nvl, "action", r->action);
|
|
|
|
packed = nvlist_pack(nvl, &size);
|
|
if (packed == NULL) {
|
|
nvlist_destroy(nvl);
|
|
return (ENOMEM);
|
|
}
|
|
|
|
nv.len = size;
|
|
nv.size = size;
|
|
nv.data = packed;
|
|
|
|
if (ioctl(dev, DIOCADDETHRULE, &nv) != 0)
|
|
error = errno;
|
|
|
|
free(packed);
|
|
nvlist_destroy(nvl);
|
|
|
|
return (error);
|
|
}
|
|
|
|
int
|
|
pfctl_add_rule(int dev, const struct pfctl_rule *r, const char *anchor,
|
|
const char *anchor_call, uint32_t ticket, uint32_t pool_ticket)
|
|
{
|
|
struct pfioc_nv nv;
|
|
uint64_t timeouts[PFTM_MAX];
|
|
uint64_t set_prio[2];
|
|
nvlist_t *nvl, *nvlr;
|
|
size_t labelcount;
|
|
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);
|
|
|
|
labelcount = 0;
|
|
while (r->label[labelcount][0] != 0 &&
|
|
labelcount < PF_RULE_MAX_LABEL_COUNT) {
|
|
nvlist_append_string_array(nvlr, "labels",
|
|
r->label[labelcount]);
|
|
labelcount++;
|
|
}
|
|
nvlist_add_number(nvlr, "ridentifier", r->ridentifier);
|
|
|
|
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, "dnpipe", r->dnpipe);
|
|
nvlist_add_number(nvlr, "dnrpipe", r->dnrpipe);
|
|
nvlist_add_number(nvlr, "dnflags", r->free_flags);
|
|
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", (const 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);
|
|
nvlist_destroy(nvlr);
|
|
|
|
/* Now do the call. */
|
|
nv.data = nvlist_pack(nvl, &nv.len);
|
|
nv.size = nv.len;
|
|
|
|
ret = ioctl(dev, DIOCADDRULENV, &nv);
|
|
if (ret == -1)
|
|
ret = errno;
|
|
|
|
free(nv.data);
|
|
nvlist_destroy(nvl);
|
|
|
|
return (ret);
|
|
}
|
|
|
|
int
|
|
pfctl_get_rules_info(int dev, struct pfctl_rules_info *rules, uint32_t ruleset,
|
|
const char *path)
|
|
{
|
|
struct pfioc_rule pr;
|
|
int ret;
|
|
|
|
bzero(&pr, sizeof(pr));
|
|
if (strlcpy(pr.anchor, path, sizeof(pr.anchor)) >= sizeof(pr.anchor))
|
|
return (E2BIG);
|
|
|
|
pr.rule.action = ruleset;
|
|
ret = ioctl(dev, DIOCGETRULES, &pr);
|
|
if (ret != 0)
|
|
return (ret);
|
|
|
|
rules->nr = pr.nr;
|
|
rules->ticket = pr.ticket;
|
|
|
|
return (0);
|
|
}
|
|
|
|
int
|
|
pfctl_get_rule(int dev, uint32_t nr, uint32_t ticket, const char *anchor,
|
|
uint32_t ruleset, struct pfctl_rule *rule, char *anchor_call)
|
|
{
|
|
return (pfctl_get_clear_rule(dev, nr, ticket, anchor, ruleset, rule,
|
|
anchor_call, false));
|
|
}
|
|
|
|
int pfctl_get_clear_rule(int dev, uint32_t nr, uint32_t ticket,
|
|
const char *anchor, uint32_t ruleset, struct pfctl_rule *rule,
|
|
char *anchor_call, bool clear)
|
|
{
|
|
nvlist_t *nvl;
|
|
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);
|
|
|
|
if (clear)
|
|
nvlist_add_bool(nvl, "clear_counter", true);
|
|
|
|
if ((ret = pfctl_do_ioctl(dev, DIOCGETRULENV, 8192, &nvl)) != 0)
|
|
return (ret);
|
|
|
|
pf_nvrule_to_rule(nvlist_get_nvlist(nvl, "rule"), rule);
|
|
|
|
if (anchor_call)
|
|
strlcpy(anchor_call, nvlist_get_string(nvl, "anchor_call"),
|
|
MAXPATHLEN);
|
|
|
|
nvlist_destroy(nvl);
|
|
|
|
return (0);
|
|
}
|
|
|
|
int
|
|
pfctl_set_keepcounters(int dev, bool keep)
|
|
{
|
|
struct pfioc_nv nv;
|
|
nvlist_t *nvl;
|
|
int ret;
|
|
|
|
nvl = nvlist_create(0);
|
|
|
|
nvlist_add_bool(nvl, "keep_counters", keep);
|
|
|
|
nv.data = nvlist_pack(nvl, &nv.len);
|
|
nv.size = nv.len;
|
|
|
|
nvlist_destroy(nvl);
|
|
|
|
ret = ioctl(dev, DIOCKEEPCOUNTERS, &nv);
|
|
|
|
free(nv.data);
|
|
return (ret);
|
|
}
|
|
|
|
static void
|
|
pfctl_nv_add_state_cmp(nvlist_t *nvl, const char *name,
|
|
const struct pfctl_state_cmp *cmp)
|
|
{
|
|
nvlist_t *nv;
|
|
|
|
nv = nvlist_create(0);
|
|
|
|
nvlist_add_number(nv, "id", cmp->id);
|
|
nvlist_add_number(nv, "creatorid", htonl(cmp->creatorid));
|
|
nvlist_add_number(nv, "direction", cmp->direction);
|
|
|
|
nvlist_add_nvlist(nvl, name, nv);
|
|
nvlist_destroy(nv);
|
|
}
|
|
|
|
static inline bool
|
|
snl_attr_get_pfaddr(struct snl_state *ss __unused, struct nlattr *nla,
|
|
const void *arg __unused, void *target)
|
|
{
|
|
memcpy(target, NLA_DATA(nla), NLA_DATA_LEN(nla));
|
|
return (true);
|
|
}
|
|
|
|
static inline bool
|
|
snl_attr_store_ifname(struct snl_state *ss __unused, struct nlattr *nla,
|
|
const void *arg __unused, void *target)
|
|
{
|
|
size_t maxlen = NLA_DATA_LEN(nla);
|
|
|
|
if (strnlen((char *)NLA_DATA(nla), maxlen) < maxlen) {
|
|
strlcpy(target, (char *)NLA_DATA(nla), maxlen);
|
|
return (true);
|
|
}
|
|
return (false);
|
|
}
|
|
|
|
#define _OUT(_field) offsetof(struct pfctl_state_peer, _field)
|
|
static const struct snl_attr_parser nla_p_speer[] = {
|
|
{ .type = PF_STP_SEQLO, .off = _OUT(seqlo), .cb = snl_attr_get_uint32 },
|
|
{ .type = PF_STP_SEQHI, .off = _OUT(seqhi), .cb = snl_attr_get_uint32 },
|
|
{ .type = PF_STP_SEQDIFF, .off = _OUT(seqdiff), .cb = snl_attr_get_uint32 },
|
|
{ .type = PF_STP_STATE, .off = _OUT(state), .cb = snl_attr_get_uint8 },
|
|
{ .type = PF_STP_WSCALE, .off = _OUT(wscale), .cb = snl_attr_get_uint8 },
|
|
};
|
|
SNL_DECLARE_ATTR_PARSER(speer_parser, nla_p_speer);
|
|
#undef _OUT
|
|
|
|
#define _OUT(_field) offsetof(struct pf_state_key_export, _field)
|
|
static const struct snl_attr_parser nla_p_skey[] = {
|
|
{ .type = PF_STK_ADDR0, .off = _OUT(addr[0]), .cb = snl_attr_get_pfaddr },
|
|
{ .type = PF_STK_ADDR1, .off = _OUT(addr[1]), .cb = snl_attr_get_pfaddr },
|
|
{ .type = PF_STK_PORT0, .off = _OUT(port[0]), .cb = snl_attr_get_uint16 },
|
|
{ .type = PF_STK_PORT1, .off = _OUT(port[1]), .cb = snl_attr_get_uint16 },
|
|
};
|
|
SNL_DECLARE_ATTR_PARSER(skey_parser, nla_p_skey);
|
|
#undef _OUT
|
|
|
|
#define _IN(_field) offsetof(struct genlmsghdr, _field)
|
|
#define _OUT(_field) offsetof(struct pfctl_state, _field)
|
|
static struct snl_attr_parser ap_state[] = {
|
|
{ .type = PF_ST_ID, .off = _OUT(id), .cb = snl_attr_get_uint64 },
|
|
{ .type = PF_ST_CREATORID, .off = _OUT(creatorid), .cb = snl_attr_get_uint32 },
|
|
{ .type = PF_ST_IFNAME, .off = _OUT(ifname), .cb = snl_attr_store_ifname },
|
|
{ .type = PF_ST_ORIG_IFNAME, .off = _OUT(orig_ifname), .cb = snl_attr_store_ifname },
|
|
{ .type = PF_ST_KEY_WIRE, .off = _OUT(key[0]), .arg = &skey_parser, .cb = snl_attr_get_nested },
|
|
{ .type = PF_ST_KEY_STACK, .off = _OUT(key[1]), .arg = &skey_parser, .cb = snl_attr_get_nested },
|
|
{ .type = PF_ST_PEER_SRC, .off = _OUT(src), .arg = &speer_parser, .cb = snl_attr_get_nested },
|
|
{ .type = PF_ST_PEER_DST, .off = _OUT(dst), .arg = &speer_parser, .cb = snl_attr_get_nested },
|
|
{ .type = PF_ST_RT_ADDR, .off = _OUT(rt_addr), .cb = snl_attr_get_pfaddr },
|
|
{ .type = PF_ST_RULE, .off = _OUT(rule), .cb = snl_attr_get_uint32 },
|
|
{ .type = PF_ST_ANCHOR, .off = _OUT(anchor), .cb = snl_attr_get_uint32 },
|
|
{ .type = PF_ST_NAT_RULE, .off = _OUT(nat_rule), .cb = snl_attr_get_uint32 },
|
|
{ .type = PF_ST_CREATION, .off = _OUT(creation), .cb = snl_attr_get_uint32 },
|
|
{ .type = PF_ST_EXPIRE, .off = _OUT(expire), .cb = snl_attr_get_uint32 },
|
|
{ .type = PF_ST_PACKETS0, .off = _OUT(packets[0]), .cb = snl_attr_get_uint64 },
|
|
{ .type = PF_ST_PACKETS1, .off = _OUT(packets[1]), .cb = snl_attr_get_uint64 },
|
|
{ .type = PF_ST_BYTES0, .off = _OUT(bytes[0]), .cb = snl_attr_get_uint64 },
|
|
{ .type = PF_ST_BYTES1, .off = _OUT(bytes[1]), .cb = snl_attr_get_uint64 },
|
|
{ .type = PF_ST_AF, .off = _OUT(key[0].af), .cb = snl_attr_get_uint8 },
|
|
{ .type = PF_ST_PROTO, .off = _OUT(key[0].proto), .cb = snl_attr_get_uint8 },
|
|
{ .type = PF_ST_DIRECTION, .off = _OUT(direction), .cb = snl_attr_get_uint8 },
|
|
{ .type = PF_ST_LOG, .off = _OUT(log), .cb = snl_attr_get_uint8 },
|
|
{ .type = PF_ST_STATE_FLAGS, .off = _OUT(state_flags), .cb = snl_attr_get_uint16 },
|
|
{ .type = PF_ST_SYNC_FLAGS, .off = _OUT(sync_flags), .cb = snl_attr_get_uint8 },
|
|
};
|
|
static struct snl_field_parser fp_state[] = {
|
|
};
|
|
#undef _IN
|
|
#undef _OUT
|
|
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
|
|
};
|
|
|
|
static int
|
|
pfctl_get_states_nl(struct snl_state *ss, struct pfctl_states *states)
|
|
{
|
|
SNL_VERIFY_PARSERS(all_parsers);
|
|
int family_id = snl_get_genl_family(ss, PFNL_FAMILY_NAME);
|
|
|
|
struct nlmsghdr *hdr;
|
|
struct snl_writer nw;
|
|
|
|
snl_init_writer(ss, &nw);
|
|
hdr = snl_create_genl_msg_request(&nw, family_id, PFNL_CMD_GETSTATES);
|
|
hdr->nlmsg_flags |= NLM_F_DUMP;
|
|
snl_finalize_msg(&nw);
|
|
uint32_t seq_id = hdr->nlmsg_seq;
|
|
|
|
snl_send_message(ss, hdr);
|
|
|
|
bzero(states, sizeof(*states));
|
|
TAILQ_INIT(&states->states);
|
|
|
|
struct snl_errmsg_data e = {};
|
|
while ((hdr = snl_read_reply_multi(ss, seq_id, &e)) != NULL) {
|
|
struct pfctl_state *s = malloc(sizeof(*s));
|
|
bzero(s, sizeof(*s));
|
|
if (s == NULL) {
|
|
pfctl_free_states(states);
|
|
return (ENOMEM);
|
|
}
|
|
if (!snl_parse_nlmsg(ss, hdr, &state_parser, s))
|
|
continue;
|
|
|
|
s->key[1].af = s->key[0].af;
|
|
s->key[1].proto = s->key[0].proto;
|
|
|
|
TAILQ_INSERT_TAIL(&states->states, s, entry);
|
|
}
|
|
|
|
return (0);
|
|
}
|
|
|
|
int
|
|
pfctl_get_states(int dev __unused, struct pfctl_states *states)
|
|
{
|
|
struct snl_state ss = {};
|
|
int error;
|
|
|
|
snl_init(&ss, NETLINK_GENERIC);
|
|
error = pfctl_get_states_nl(&ss, states);
|
|
snl_free(&ss);
|
|
|
|
return (error);
|
|
}
|
|
|
|
void
|
|
pfctl_free_states(struct pfctl_states *states)
|
|
{
|
|
struct pfctl_state *s, *tmp;
|
|
|
|
TAILQ_FOREACH_SAFE(s, &states->states, entry, tmp) {
|
|
free(s);
|
|
}
|
|
|
|
bzero(states, sizeof(*states));
|
|
}
|
|
|
|
static int
|
|
_pfctl_clear_states(int dev, const struct pfctl_kill *kill,
|
|
unsigned int *killed, uint64_t ioctlval)
|
|
{
|
|
nvlist_t *nvl;
|
|
int ret;
|
|
|
|
nvl = nvlist_create(0);
|
|
|
|
pfctl_nv_add_state_cmp(nvl, "cmp", &kill->cmp);
|
|
nvlist_add_number(nvl, "af", kill->af);
|
|
nvlist_add_number(nvl, "proto", kill->proto);
|
|
pfctl_nv_add_rule_addr(nvl, "src", &kill->src);
|
|
pfctl_nv_add_rule_addr(nvl, "dst", &kill->dst);
|
|
pfctl_nv_add_rule_addr(nvl, "rt_addr", &kill->rt_addr);
|
|
nvlist_add_string(nvl, "ifname", kill->ifname);
|
|
nvlist_add_string(nvl, "label", kill->label);
|
|
nvlist_add_bool(nvl, "kill_match", kill->kill_match);
|
|
|
|
if ((ret = pfctl_do_ioctl(dev, ioctlval, 1024, &nvl)) != 0)
|
|
return (ret);
|
|
|
|
if (killed)
|
|
*killed = nvlist_get_number(nvl, "killed");
|
|
|
|
nvlist_destroy(nvl);
|
|
|
|
return (ret);
|
|
}
|
|
|
|
int
|
|
pfctl_clear_states(int dev, const struct pfctl_kill *kill,
|
|
unsigned int *killed)
|
|
{
|
|
return (_pfctl_clear_states(dev, kill, killed, DIOCCLRSTATESNV));
|
|
}
|
|
|
|
int
|
|
pfctl_kill_states(int dev, const struct pfctl_kill *kill, unsigned int *killed)
|
|
{
|
|
return (_pfctl_clear_states(dev, kill, killed, DIOCKILLSTATESNV));
|
|
}
|
|
|
|
int
|
|
pfctl_clear_rules(int dev, const char *anchorname)
|
|
{
|
|
struct pfioc_trans trans;
|
|
struct pfioc_trans_e transe[2];
|
|
int ret;
|
|
|
|
bzero(&trans, sizeof(trans));
|
|
bzero(&transe, sizeof(transe));
|
|
|
|
transe[0].rs_num = PF_RULESET_SCRUB;
|
|
if (strlcpy(transe[0].anchor, anchorname, sizeof(transe[0].anchor))
|
|
>= sizeof(transe[0].anchor))
|
|
return (E2BIG);
|
|
|
|
transe[1].rs_num = PF_RULESET_FILTER;
|
|
if (strlcpy(transe[1].anchor, anchorname, sizeof(transe[1].anchor))
|
|
>= sizeof(transe[1].anchor))
|
|
return (E2BIG);
|
|
|
|
trans.size = 2;
|
|
trans.esize = sizeof(transe[0]);
|
|
trans.array = transe;
|
|
|
|
ret = ioctl(dev, DIOCXBEGIN, &trans);
|
|
if (ret != 0)
|
|
return (ret);
|
|
return ioctl(dev, DIOCXCOMMIT, &trans);
|
|
}
|
|
|
|
int
|
|
pfctl_clear_nat(int dev, const char *anchorname)
|
|
{
|
|
struct pfioc_trans trans;
|
|
struct pfioc_trans_e transe[3];
|
|
int ret;
|
|
|
|
bzero(&trans, sizeof(trans));
|
|
bzero(&transe, sizeof(transe));
|
|
|
|
transe[0].rs_num = PF_RULESET_NAT;
|
|
if (strlcpy(transe[0].anchor, anchorname, sizeof(transe[0].anchor))
|
|
>= sizeof(transe[0].anchor))
|
|
return (E2BIG);
|
|
|
|
transe[1].rs_num = PF_RULESET_BINAT;
|
|
if (strlcpy(transe[1].anchor, anchorname, sizeof(transe[1].anchor))
|
|
>= sizeof(transe[0].anchor))
|
|
return (E2BIG);
|
|
|
|
transe[2].rs_num = PF_RULESET_RDR;
|
|
if (strlcpy(transe[2].anchor, anchorname, sizeof(transe[2].anchor))
|
|
>= sizeof(transe[2].anchor))
|
|
return (E2BIG);
|
|
|
|
trans.size = 3;
|
|
trans.esize = sizeof(transe[0]);
|
|
trans.array = transe;
|
|
|
|
ret = ioctl(dev, DIOCXBEGIN, &trans);
|
|
if (ret != 0)
|
|
return (ret);
|
|
return ioctl(dev, DIOCXCOMMIT, &trans);
|
|
}
|
|
int
|
|
pfctl_clear_eth_rules(int dev, const char *anchorname)
|
|
{
|
|
struct pfioc_trans trans;
|
|
struct pfioc_trans_e transe;
|
|
int ret;
|
|
|
|
bzero(&trans, sizeof(trans));
|
|
bzero(&transe, sizeof(transe));
|
|
|
|
transe.rs_num = PF_RULESET_ETH;
|
|
if (strlcpy(transe.anchor, anchorname, sizeof(transe.anchor))
|
|
>= sizeof(transe.anchor))
|
|
return (E2BIG);
|
|
|
|
trans.size = 1;
|
|
trans.esize = sizeof(transe);
|
|
trans.array = &transe;
|
|
|
|
ret = ioctl(dev, DIOCXBEGIN, &trans);
|
|
if (ret != 0)
|
|
return (ret);
|
|
return ioctl(dev, DIOCXCOMMIT, &trans);
|
|
}
|
|
|
|
static int
|
|
pfctl_get_limit(int dev, const int index, uint *limit)
|
|
{
|
|
struct pfioc_limit pl;
|
|
|
|
bzero(&pl, sizeof(pl));
|
|
pl.index = index;
|
|
|
|
if (ioctl(dev, DIOCGETLIMIT, &pl) == -1)
|
|
return (errno);
|
|
|
|
*limit = pl.limit;
|
|
|
|
return (0);
|
|
}
|
|
|
|
int
|
|
pfctl_set_syncookies(int dev, const struct pfctl_syncookies *s)
|
|
{
|
|
struct pfioc_nv nv;
|
|
nvlist_t *nvl;
|
|
int ret;
|
|
uint state_limit;
|
|
uint64_t lim, hi, lo;
|
|
|
|
ret = pfctl_get_limit(dev, PF_LIMIT_STATES, &state_limit);
|
|
if (ret != 0)
|
|
return (ret);
|
|
|
|
lim = state_limit;
|
|
hi = lim * s->highwater / 100;
|
|
lo = lim * s->lowwater / 100;
|
|
|
|
if (lo == hi)
|
|
hi++;
|
|
|
|
nvl = nvlist_create(0);
|
|
|
|
nvlist_add_bool(nvl, "enabled", s->mode != PFCTL_SYNCOOKIES_NEVER);
|
|
nvlist_add_bool(nvl, "adaptive", s->mode == PFCTL_SYNCOOKIES_ADAPTIVE);
|
|
nvlist_add_number(nvl, "highwater", hi);
|
|
nvlist_add_number(nvl, "lowwater", lo);
|
|
|
|
nv.data = nvlist_pack(nvl, &nv.len);
|
|
nv.size = nv.len;
|
|
nvlist_destroy(nvl);
|
|
nvl = NULL;
|
|
|
|
ret = ioctl(dev, DIOCSETSYNCOOKIES, &nv);
|
|
|
|
free(nv.data);
|
|
return (ret);
|
|
}
|
|
|
|
int
|
|
pfctl_get_syncookies(int dev, struct pfctl_syncookies *s)
|
|
{
|
|
nvlist_t *nvl;
|
|
int ret;
|
|
uint state_limit;
|
|
bool enabled, adaptive;
|
|
|
|
ret = pfctl_get_limit(dev, PF_LIMIT_STATES, &state_limit);
|
|
if (ret != 0)
|
|
return (ret);
|
|
|
|
bzero(s, sizeof(*s));
|
|
|
|
nvl = nvlist_create(0);
|
|
|
|
if ((ret = pfctl_do_ioctl(dev, DIOCGETSYNCOOKIES, 256, &nvl)) != 0)
|
|
return (errno);
|
|
|
|
enabled = nvlist_get_bool(nvl, "enabled");
|
|
adaptive = nvlist_get_bool(nvl, "adaptive");
|
|
|
|
if (enabled) {
|
|
if (adaptive)
|
|
s->mode = PFCTL_SYNCOOKIES_ADAPTIVE;
|
|
else
|
|
s->mode = PFCTL_SYNCOOKIES_ALWAYS;
|
|
} else {
|
|
s->mode = PFCTL_SYNCOOKIES_NEVER;
|
|
}
|
|
|
|
s->highwater = nvlist_get_number(nvl, "highwater") * 100 / state_limit;
|
|
s->lowwater = nvlist_get_number(nvl, "lowwater") * 100 / state_limit;
|
|
|
|
nvlist_destroy(nvl);
|
|
|
|
return (0);
|
|
}
|
|
|
|
int
|
|
pfctl_table_add_addrs(int dev, struct pfr_table *tbl, struct pfr_addr
|
|
*addr, int size, int *nadd, int flags)
|
|
{
|
|
struct pfioc_table io;
|
|
|
|
if (tbl == NULL || size < 0 || (size && addr == NULL)) {
|
|
return (EINVAL);
|
|
}
|
|
bzero(&io, sizeof io);
|
|
io.pfrio_flags = flags;
|
|
io.pfrio_table = *tbl;
|
|
io.pfrio_buffer = addr;
|
|
io.pfrio_esize = sizeof(*addr);
|
|
io.pfrio_size = size;
|
|
|
|
if (ioctl(dev, DIOCRADDADDRS, &io))
|
|
return (errno);
|
|
if (nadd != NULL)
|
|
*nadd = io.pfrio_nadd;
|
|
return (0);
|
|
}
|
|
|
|
int
|
|
pfctl_table_del_addrs(int dev, struct pfr_table *tbl, struct pfr_addr
|
|
*addr, int size, int *ndel, int flags)
|
|
{
|
|
struct pfioc_table io;
|
|
|
|
if (tbl == NULL || size < 0 || (size && addr == NULL)) {
|
|
return (EINVAL);
|
|
}
|
|
bzero(&io, sizeof io);
|
|
io.pfrio_flags = flags;
|
|
io.pfrio_table = *tbl;
|
|
io.pfrio_buffer = addr;
|
|
io.pfrio_esize = sizeof(*addr);
|
|
io.pfrio_size = size;
|
|
|
|
if (ioctl(dev, DIOCRDELADDRS, &io))
|
|
return (errno);
|
|
if (ndel != NULL)
|
|
*ndel = io.pfrio_ndel;
|
|
return (0);
|
|
}
|
|
|
|
int
|
|
pfctl_table_set_addrs(int dev, struct pfr_table *tbl, struct pfr_addr
|
|
*addr, int size, int *size2, int *nadd, int *ndel, int *nchange, int flags)
|
|
{
|
|
struct pfioc_table io;
|
|
|
|
if (tbl == NULL || size < 0 || (size && addr == NULL)) {
|
|
return (EINVAL);
|
|
}
|
|
bzero(&io, sizeof io);
|
|
io.pfrio_flags = flags;
|
|
io.pfrio_table = *tbl;
|
|
io.pfrio_buffer = addr;
|
|
io.pfrio_esize = sizeof(*addr);
|
|
io.pfrio_size = size;
|
|
io.pfrio_size2 = (size2 != NULL) ? *size2 : 0;
|
|
if (ioctl(dev, DIOCRSETADDRS, &io))
|
|
return (-1);
|
|
if (nadd != NULL)
|
|
*nadd = io.pfrio_nadd;
|
|
if (ndel != NULL)
|
|
*ndel = io.pfrio_ndel;
|
|
if (nchange != NULL)
|
|
*nchange = io.pfrio_nchange;
|
|
if (size2 != NULL)
|
|
*size2 = io.pfrio_size2;
|
|
return (0);
|
|
}
|
|
|
|
int pfctl_table_get_addrs(int dev, struct pfr_table *tbl, struct pfr_addr *addr,
|
|
int *size, int flags)
|
|
{
|
|
struct pfioc_table io;
|
|
|
|
if (tbl == NULL || size == NULL || *size < 0 ||
|
|
(*size && addr == NULL)) {
|
|
return (EINVAL);
|
|
}
|
|
bzero(&io, sizeof io);
|
|
io.pfrio_flags = flags;
|
|
io.pfrio_table = *tbl;
|
|
io.pfrio_buffer = addr;
|
|
io.pfrio_esize = sizeof(*addr);
|
|
io.pfrio_size = *size;
|
|
if (ioctl(dev, DIOCRGETADDRS, &io))
|
|
return (-1);
|
|
*size = io.pfrio_size;
|
|
return (0);
|
|
}
|