/*-
* Copyright (c) 2009-2025 The NetBSD Foundation, Inc.
* All rights reserved.
*
* This material is based upon work partially supported by The
* NetBSD Foundation under a contract with Mindaugas Rasiukevicius.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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.
*/
/*
* NPF device control.
*
* Implementation of (re)loading, construction of tables and rules.
* NPF nvlist(3) consumer.
*/
/*
* NAT rules are standard rules, plus the translation policy.
* We first construct the rule structure.
*/
error = npf_mk_singlerule(npf, nat, resp, NULL, &rl);
if (error) {
return error;
}
KASSERT(rl != NULL);
*rlp = rl;
/* If this rule is named, then it is a group with NAT policies. */
if (dnvlist_get_string(nat, "name", NULL)) {
return 0;
}
/* Check the table ID. */
if (nvlist_exists_number(nat, "nat-table-id")) {
unsigned tid = nvlist_get_number(nat, "nat-table-id");
/*
* npf_mk_connlist: import a list of connections and load them.
*/
static int __noinline
npf_mk_connlist(npf_t *npf, const nvlist_t *req, nvlist_t *resp,
npf_config_t *nc, npf_conndb_t **conndb)
{
const nvlist_t * const *conns;
npf_conndb_t *cd;
size_t nitems;
int error = 0;
if (!nvlist_exists_nvlist_array(req, "conn-list")) {
*conndb = NULL;
return 0;
}
cd = npf_conndb_create();
conns = nvlist_get_nvlist_array(req, "conn-list", &nitems);
for (unsigned i = 0; i < nitems; i++) {
const nvlist_t *conn = conns[i];
/* Construct and insert the connection. */
error = npf_conn_import(npf, cd, conn, nc->nat_ruleset);
if (error) {
NPF_ERR_DEBUG(resp);
break;
}
}
if (error) {
npf_conndb_gc(npf, cd, true, false);
npf_conndb_destroy(cd);
} else {
*conndb = cd;
}
return error;
}
/*
* npfctl_load: store passed data i.e. the update settings, create the
* passed rules, tables, etc and atomically activate them all.
*/
static int
npfctl_load(npf_t *npf, const nvlist_t *req, nvlist_t *resp)
{
npf_config_t *nc;
npf_conndb_t *conndb = NULL;
bool flush;
int error;
/*
* Finally - perform the load.
*/
npf_config_load(npf, nc, conndb, flush);
npf_mk_params(npf, req, resp, true /* set the params */);
return 0;
fail:
npf_config_destroy(nc);
return error;
}
/*
* npfctl_save: export the active configuration, including the current
* snapshot of the connections. Additionally, set the version and indicate
* whether the ruleset is currently active.
*/
static int
npfctl_save(npf_t *npf, const nvlist_t *req, nvlist_t *resp)
{
npf_config_t *nc;
int error;
if (rl) {
KASSERT(error);
npf_rule_free(rl);
}
return error;
}
/*
* npfctl_table: add, remove or query entries in the specified table.
*
* For maximum performance, the interface is using plain structures.
*/
int
npfctl_table(npf_t *npf, void *data)
{
const npf_ioctl_table_t *nct = data;
char tname[NPF_TABLE_MAXNAMELEN];
npf_config_t *nc;
npf_table_t *t;
int error;
/*
* npfctl_run_op: run a particular NPF operation with a given the request.
*
* => Checks the ABI version.
* => Sets the error number for the response.
*/
int
npfctl_run_op(npf_t *npf, unsigned op, const nvlist_t *req, nvlist_t *resp)
{
uint64_t ver;
int error;
ver = dnvlist_get_number(req, "version", UINT64_MAX);
if (__predict_false(ver != UINT64_MAX && ver != NPF_VERSION)) {
return EPROGMISMATCH;
}
switch (op) {
case IOC_NPF_LOAD:
error = npfctl_load(npf, req, resp);
break;
case IOC_NPF_SAVE:
error = npfctl_save(npf, req, resp);
break;
case IOC_NPF_RULE:
error = npfctl_rule(npf, req, resp);
break;
case IOC_NPF_CONN_LOOKUP:
error = npf_conn_find(npf, req, resp);
break;
case IOC_NPF_TABLE_REPLACE:
error = npfctl_table_replace(npf, req, resp);
break;
default:
error = ENOTTY;
break;
}
nvlist_add_number(resp, "errno", error);
return error;
}