1 1.1 rmind /*- 2 1.62 joe * Copyright (c) 2009-2025 The NetBSD Foundation, Inc. 3 1.1 rmind * All rights reserved. 4 1.1 rmind * 5 1.1 rmind * This material is based upon work partially supported by The 6 1.1 rmind * NetBSD Foundation under a contract with Mindaugas Rasiukevicius. 7 1.1 rmind * 8 1.1 rmind * Redistribution and use in source and binary forms, with or without 9 1.1 rmind * modification, are permitted provided that the following conditions 10 1.1 rmind * are met: 11 1.1 rmind * 1. Redistributions of source code must retain the above copyright 12 1.1 rmind * notice, this list of conditions and the following disclaimer. 13 1.1 rmind * 2. Redistributions in binary form must reproduce the above copyright 14 1.1 rmind * notice, this list of conditions and the following disclaimer in the 15 1.1 rmind * documentation and/or other materials provided with the distribution. 16 1.1 rmind * 17 1.1 rmind * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 18 1.1 rmind * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 19 1.1 rmind * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 20 1.1 rmind * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 21 1.1 rmind * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 1.1 rmind * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 1.1 rmind * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 1.1 rmind * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 1.1 rmind * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 1.1 rmind * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 27 1.1 rmind * POSSIBILITY OF SUCH DAMAGE. 28 1.1 rmind */ 29 1.1 rmind 30 1.1 rmind /* 31 1.1 rmind * NPF device control. 32 1.1 rmind * 33 1.1 rmind * Implementation of (re)loading, construction of tables and rules. 34 1.51 rmind * NPF nvlist(3) consumer. 35 1.1 rmind */ 36 1.1 rmind 37 1.45 christos #ifdef _KERNEL 38 1.1 rmind #include <sys/cdefs.h> 39 1.62 joe __KERNEL_RCSID(0, "$NetBSD: npf_ctl.c,v 1.62 2025/06/01 00:54:36 joe Exp $"); 40 1.1 rmind 41 1.1 rmind #include <sys/param.h> 42 1.1 rmind #include <sys/conf.h> 43 1.21 rmind #include <sys/kmem.h> 44 1.21 rmind #include <net/bpf.h> 45 1.45 christos #endif 46 1.1 rmind 47 1.1 rmind #include "npf_impl.h" 48 1.34 rmind #include "npf_conn.h" 49 1.1 rmind 50 1.12 rmind #define NPF_ERR_DEBUG(e) \ 51 1.51 rmind nvlist_add_string((e), "source-file", __FILE__); \ 52 1.51 rmind nvlist_add_number((e), "source-line", __LINE__); 53 1.12 rmind 54 1.6 rmind static int __noinline 55 1.60 rmind npf_mk_params(npf_t *npf, const nvlist_t *req, nvlist_t *resp, bool set) 56 1.54 rmind { 57 1.54 rmind const nvlist_t *params; 58 1.54 rmind int type, error, val; 59 1.54 rmind const char *name; 60 1.54 rmind void *cookie; 61 1.54 rmind 62 1.60 rmind params = dnvlist_get_nvlist(req, "params", NULL); 63 1.54 rmind if (params == NULL) { 64 1.54 rmind return 0; 65 1.54 rmind } 66 1.54 rmind cookie = NULL; 67 1.54 rmind while ((name = nvlist_next(params, &type, &cookie)) != NULL) { 68 1.54 rmind if (type != NV_TYPE_NUMBER) { 69 1.60 rmind NPF_ERR_DEBUG(resp); 70 1.54 rmind return EINVAL; 71 1.54 rmind } 72 1.54 rmind val = (int)nvlist_get_number(params, name); 73 1.54 rmind if (set) { 74 1.54 rmind /* Actually set the parameter. */ 75 1.55 rmind error = npfk_param_set(npf, name, val); 76 1.54 rmind KASSERT(error == 0); 77 1.54 rmind continue; 78 1.54 rmind } 79 1.54 rmind 80 1.54 rmind /* Validate the parameter and its value. */ 81 1.54 rmind error = npf_param_check(npf, name, val); 82 1.54 rmind if (__predict_true(error == 0)) { 83 1.54 rmind continue; 84 1.54 rmind } 85 1.54 rmind if (error == ENOENT) { 86 1.60 rmind nvlist_add_stringf(resp, "error-msg", 87 1.54 rmind "invalid parameter `%s`", name); 88 1.54 rmind } 89 1.54 rmind if (error == EINVAL) { 90 1.60 rmind nvlist_add_stringf(resp, "error-msg", 91 1.54 rmind "invalid parameter `%s` value %d", name, val); 92 1.54 rmind } 93 1.54 rmind return error; 94 1.54 rmind } 95 1.54 rmind return 0; 96 1.54 rmind } 97 1.54 rmind 98 1.54 rmind static int __noinline 99 1.60 rmind npf_mk_table_entries(npf_table_t *t, const nvlist_t *req, nvlist_t *resp) 100 1.33 rmind { 101 1.51 rmind const nvlist_t * const *entries; 102 1.51 rmind size_t nitems; 103 1.33 rmind int error = 0; 104 1.33 rmind 105 1.60 rmind if (!nvlist_exists_nvlist_array(req, "entries")) { 106 1.51 rmind return 0; 107 1.39 rmind } 108 1.60 rmind entries = nvlist_get_nvlist_array(req, "entries", &nitems); 109 1.51 rmind for (unsigned i = 0; i < nitems; i++) { 110 1.51 rmind const nvlist_t *entry = entries[i]; 111 1.33 rmind const npf_addr_t *addr; 112 1.33 rmind npf_netmask_t mask; 113 1.51 rmind size_t alen; 114 1.33 rmind 115 1.60 rmind /* Get address and mask; add a table entry. */ 116 1.51 rmind addr = dnvlist_get_binary(entry, "addr", &alen, NULL, 0); 117 1.51 rmind mask = dnvlist_get_number(entry, "mask", NPF_NO_NETMASK); 118 1.51 rmind if (addr == NULL || alen == 0) { 119 1.60 rmind NPF_ERR_DEBUG(resp); 120 1.51 rmind error = EINVAL; 121 1.51 rmind break; 122 1.51 rmind } 123 1.33 rmind error = npf_table_insert(t, alen, addr, mask); 124 1.60 rmind if (__predict_false(error)) { 125 1.60 rmind if (error == EEXIST) { 126 1.60 rmind nvlist_add_stringf(resp, "error-msg", 127 1.60 rmind "table `%s' has a duplicate entry", 128 1.60 rmind nvlist_get_string(req, "name")); 129 1.60 rmind } else { 130 1.60 rmind NPF_ERR_DEBUG(resp); 131 1.60 rmind } 132 1.33 rmind break; 133 1.51 rmind } 134 1.33 rmind } 135 1.33 rmind return error; 136 1.33 rmind } 137 1.33 rmind 138 1.56 rmind /* 139 1.56 rmind * npf_mk_table: create a table from provided nvlist. 140 1.56 rmind */ 141 1.56 rmind static int __noinline 142 1.60 rmind npf_mk_table(npf_t *npf, const nvlist_t *req, nvlist_t *resp, 143 1.56 rmind npf_tableset_t *tblset, npf_table_t **tblp, bool replacing) 144 1.56 rmind { 145 1.56 rmind npf_table_t *t; 146 1.56 rmind const char *name; 147 1.56 rmind const void *blob; 148 1.56 rmind uint64_t tid; 149 1.56 rmind size_t size; 150 1.56 rmind int type; 151 1.56 rmind int error = 0; 152 1.56 rmind 153 1.56 rmind KASSERT(tblp != NULL); 154 1.56 rmind 155 1.56 rmind /* Table name, ID and type. Validate them. */ 156 1.60 rmind name = dnvlist_get_string(req, "name", NULL); 157 1.56 rmind if (!name) { 158 1.60 rmind NPF_ERR_DEBUG(resp); 159 1.56 rmind error = EINVAL; 160 1.56 rmind goto out; 161 1.56 rmind } 162 1.60 rmind tid = dnvlist_get_number(req, "id", UINT64_MAX); 163 1.60 rmind type = dnvlist_get_number(req, "type", UINT64_MAX); 164 1.56 rmind error = npf_table_check(tblset, name, tid, type, replacing); 165 1.56 rmind if (error) { 166 1.60 rmind NPF_ERR_DEBUG(resp); 167 1.56 rmind goto out; 168 1.56 rmind } 169 1.56 rmind 170 1.56 rmind /* Get the entries or binary data. */ 171 1.60 rmind blob = dnvlist_get_binary(req, "data", &size, NULL, 0); 172 1.56 rmind if (type == NPF_TABLE_CONST && (blob == NULL || size == 0)) { 173 1.60 rmind NPF_ERR_DEBUG(resp); 174 1.56 rmind error = EINVAL; 175 1.56 rmind goto out; 176 1.56 rmind } 177 1.56 rmind 178 1.59 rmind t = npf_table_create(name, (unsigned)tid, type, blob, size); 179 1.56 rmind if (t == NULL) { 180 1.60 rmind NPF_ERR_DEBUG(resp); 181 1.56 rmind error = ENOMEM; 182 1.56 rmind goto out; 183 1.56 rmind } 184 1.56 rmind 185 1.60 rmind if ((error = npf_mk_table_entries(t, req, resp)) != 0) { 186 1.56 rmind npf_table_destroy(t); 187 1.56 rmind goto out; 188 1.56 rmind } 189 1.56 rmind 190 1.56 rmind *tblp = t; 191 1.56 rmind out: 192 1.56 rmind return error; 193 1.56 rmind } 194 1.56 rmind 195 1.33 rmind static int __noinline 196 1.60 rmind npf_mk_tables(npf_t *npf, const nvlist_t *req, nvlist_t *resp, npf_config_t *nc) 197 1.1 rmind { 198 1.51 rmind const nvlist_t * const *tables; 199 1.51 rmind npf_tableset_t *tblset; 200 1.51 rmind size_t nitems; 201 1.1 rmind int error = 0; 202 1.1 rmind 203 1.60 rmind if (nvlist_exists_nvlist_array(req, "tables")) { 204 1.60 rmind tables = nvlist_get_nvlist_array(req, "tables", &nitems); 205 1.51 rmind if (nitems > NPF_MAX_TABLES) { 206 1.60 rmind NPF_ERR_DEBUG(resp); 207 1.51 rmind return E2BIG; 208 1.51 rmind } 209 1.51 rmind } else { 210 1.51 rmind tables = NULL; 211 1.51 rmind nitems = 0; 212 1.12 rmind } 213 1.51 rmind tblset = npf_tableset_create(nitems); 214 1.51 rmind for (unsigned i = 0; i < nitems; i++) { 215 1.51 rmind const nvlist_t *table = tables[i]; 216 1.1 rmind npf_table_t *t; 217 1.1 rmind 218 1.60 rmind error = npf_mk_table(npf, table, resp, tblset, &t, 0); 219 1.32 rmind if (error) { 220 1.33 rmind break; 221 1.33 rmind } 222 1.33 rmind 223 1.1 rmind error = npf_tableset_insert(tblset, t); 224 1.1 rmind KASSERT(error == 0); 225 1.1 rmind } 226 1.57 rmind nc->tableset = tblset; 227 1.1 rmind return error; 228 1.1 rmind } 229 1.1 rmind 230 1.5 rmind static npf_rproc_t * 231 1.60 rmind npf_mk_singlerproc(npf_t *npf, const nvlist_t *rproc, nvlist_t *resp) 232 1.5 rmind { 233 1.51 rmind const nvlist_t * const *extcalls; 234 1.51 rmind size_t nitems; 235 1.5 rmind npf_rproc_t *rp; 236 1.18 rmind 237 1.60 rmind if ((rp = npf_rproc_create(rproc)) == NULL) { 238 1.60 rmind NPF_ERR_DEBUG(resp); 239 1.18 rmind return NULL; 240 1.18 rmind } 241 1.60 rmind if (!nvlist_exists_nvlist_array(rproc, "extcalls")) { 242 1.60 rmind return rp; 243 1.18 rmind } 244 1.51 rmind extcalls = nvlist_get_nvlist_array(rproc, "extcalls", &nitems); 245 1.51 rmind for (unsigned i = 0; i < nitems; i++) { 246 1.51 rmind const nvlist_t *extcall = extcalls[i]; 247 1.21 rmind const char *name; 248 1.21 rmind 249 1.51 rmind name = dnvlist_get_string(extcall, "name", NULL); 250 1.51 rmind if (!name || npf_ext_construct(npf, name, rp, extcall)) { 251 1.60 rmind NPF_ERR_DEBUG(resp); 252 1.18 rmind npf_rproc_release(rp); 253 1.18 rmind rp = NULL; 254 1.18 rmind break; 255 1.18 rmind } 256 1.18 rmind } 257 1.21 rmind return rp; 258 1.21 rmind } 259 1.21 rmind 260 1.21 rmind static int __noinline 261 1.60 rmind npf_mk_rprocs(npf_t *npf, const nvlist_t *req, nvlist_t *resp, npf_config_t *nc) 262 1.21 rmind { 263 1.51 rmind const nvlist_t * const *rprocs; 264 1.51 rmind npf_rprocset_t *rpset; 265 1.51 rmind size_t nitems; 266 1.21 rmind int error = 0; 267 1.21 rmind 268 1.60 rmind if (nvlist_exists_nvlist_array(req, "rprocs")) { 269 1.60 rmind rprocs = nvlist_get_nvlist_array(req, "rprocs", &nitems); 270 1.51 rmind if (nitems > NPF_MAX_RPROCS) { 271 1.60 rmind NPF_ERR_DEBUG(resp); 272 1.51 rmind return E2BIG; 273 1.51 rmind } 274 1.51 rmind } else { 275 1.51 rmind rprocs = NULL; 276 1.51 rmind nitems = 0; 277 1.51 rmind } 278 1.51 rmind rpset = npf_rprocset_create(); 279 1.51 rmind for (unsigned i = 0; i < nitems; i++) { 280 1.51 rmind const nvlist_t *rproc = rprocs[i]; 281 1.21 rmind npf_rproc_t *rp; 282 1.18 rmind 283 1.60 rmind if ((rp = npf_mk_singlerproc(npf, rproc, resp)) == NULL) { 284 1.21 rmind error = EINVAL; 285 1.21 rmind break; 286 1.21 rmind } 287 1.21 rmind npf_rprocset_insert(rpset, rp); 288 1.5 rmind } 289 1.57 rmind nc->rule_procs = rpset; 290 1.21 rmind return error; 291 1.5 rmind } 292 1.5 rmind 293 1.51 rmind static int __noinline 294 1.60 rmind npf_mk_algs(npf_t *npf, const nvlist_t *req, nvlist_t *resp) 295 1.24 christos { 296 1.51 rmind const nvlist_t * const *algs; 297 1.51 rmind size_t nitems; 298 1.24 christos 299 1.60 rmind if (nvlist_exists_nvlist_array(req, "algs")) { 300 1.60 rmind algs = nvlist_get_nvlist_array(req, "algs", &nitems); 301 1.51 rmind } else { 302 1.51 rmind algs = NULL; 303 1.51 rmind nitems = 0; 304 1.51 rmind } 305 1.51 rmind for (unsigned i = 0; i < nitems; i++) { 306 1.51 rmind const nvlist_t *alg = algs[i]; 307 1.51 rmind const char *name; 308 1.24 christos 309 1.51 rmind name = dnvlist_get_string(alg, "name", NULL); 310 1.51 rmind if (!name) { 311 1.60 rmind NPF_ERR_DEBUG(resp); 312 1.51 rmind return EINVAL; 313 1.51 rmind } 314 1.51 rmind if (!npf_alg_construct(npf, name)) { 315 1.60 rmind NPF_ERR_DEBUG(resp); 316 1.51 rmind return EINVAL; 317 1.24 christos } 318 1.24 christos } 319 1.12 rmind return 0; 320 1.12 rmind } 321 1.12 rmind 322 1.12 rmind static int __noinline 323 1.60 rmind npf_mk_singlerule(npf_t *npf, const nvlist_t *req, nvlist_t *resp, 324 1.60 rmind npf_rprocset_t *rpset, npf_rule_t **rlret) 325 1.1 rmind { 326 1.21 rmind npf_rule_t *rl; 327 1.21 rmind const char *rname; 328 1.51 rmind const void *code; 329 1.51 rmind size_t clen; 330 1.51 rmind int error = 0; 331 1.1 rmind 332 1.60 rmind if ((rl = npf_rule_alloc(npf, req)) == NULL) { 333 1.60 rmind NPF_ERR_DEBUG(resp); 334 1.21 rmind return EINVAL; 335 1.21 rmind } 336 1.1 rmind 337 1.51 rmind /* Assign the rule procedure, if any. */ 338 1.60 rmind if ((rname = dnvlist_get_string(req, "rproc", NULL)) != NULL) { 339 1.21 rmind npf_rproc_t *rp; 340 1.21 rmind 341 1.21 rmind if (rpset == NULL) { 342 1.60 rmind NPF_ERR_DEBUG(resp); 343 1.21 rmind error = EINVAL; 344 1.21 rmind goto err; 345 1.21 rmind } 346 1.21 rmind if ((rp = npf_rprocset_lookup(rpset, rname)) == NULL) { 347 1.60 rmind NPF_ERR_DEBUG(resp); 348 1.18 rmind error = EINVAL; 349 1.18 rmind goto err; 350 1.18 rmind } 351 1.21 rmind npf_rule_setrproc(rl, rp); 352 1.18 rmind } 353 1.18 rmind 354 1.51 rmind /* Filter byte-code (binary data). */ 355 1.60 rmind code = dnvlist_get_binary(req, "code", &clen, NULL, 0); 356 1.51 rmind if (code) { 357 1.51 rmind void *bc; 358 1.21 rmind int type; 359 1.21 rmind 360 1.60 rmind type = dnvlist_get_number(req, "code-type", UINT64_MAX); 361 1.51 rmind if (type != NPF_CODE_BPF) { 362 1.60 rmind NPF_ERR_DEBUG(resp); 363 1.51 rmind error = ENOTSUP; 364 1.51 rmind goto err; 365 1.51 rmind } 366 1.51 rmind if (clen == 0) { 367 1.60 rmind NPF_ERR_DEBUG(resp); 368 1.51 rmind error = EINVAL; 369 1.51 rmind goto err; 370 1.51 rmind } 371 1.51 rmind if (!npf_bpf_validate(code, clen)) { 372 1.60 rmind NPF_ERR_DEBUG(resp); 373 1.51 rmind error = EINVAL; 374 1.12 rmind goto err; 375 1.4 rmind } 376 1.51 rmind bc = kmem_alloc(clen, KM_SLEEP); 377 1.51 rmind memcpy(bc, code, clen); // XXX: use nvlist_take 378 1.51 rmind npf_rule_setcode(rl, type, bc, clen); 379 1.1 rmind } 380 1.1 rmind 381 1.61 joe /* user and group ids filt option if set */ 382 1.61 joe if (nvlist_exists_number_array(req, "r_user")) { 383 1.61 joe npf_rule_setrid(req, rl, "r_user"); 384 1.61 joe } 385 1.61 joe 386 1.61 joe if (nvlist_exists_number_array(req, "r_group")) { 387 1.61 joe npf_rule_setrid(req, rl, "r_group"); 388 1.61 joe } 389 1.61 joe 390 1.21 rmind *rlret = rl; 391 1.6 rmind return 0; 392 1.12 rmind err: 393 1.60 rmind nvlist_add_number(resp, "id", dnvlist_get_number(req, "prio", 0)); 394 1.21 rmind npf_rule_free(rl); 395 1.12 rmind return error; 396 1.6 rmind } 397 1.6 rmind 398 1.6 rmind static int __noinline 399 1.60 rmind npf_mk_rules(npf_t *npf, const nvlist_t *req, nvlist_t *resp, npf_config_t *nc) 400 1.6 rmind { 401 1.51 rmind const nvlist_t * const *rules; 402 1.51 rmind npf_ruleset_t *rlset; 403 1.51 rmind size_t nitems; 404 1.51 rmind int error = 0; 405 1.6 rmind 406 1.60 rmind if (nvlist_exists_nvlist_array(req, "rules")) { 407 1.60 rmind rules = nvlist_get_nvlist_array(req, "rules", &nitems); 408 1.51 rmind if (nitems > NPF_MAX_RULES) { 409 1.60 rmind NPF_ERR_DEBUG(resp); 410 1.51 rmind return E2BIG; 411 1.51 rmind } 412 1.51 rmind } else { 413 1.51 rmind rules = NULL; 414 1.51 rmind nitems = 0; 415 1.6 rmind } 416 1.51 rmind rlset = npf_ruleset_create(nitems); 417 1.51 rmind for (unsigned i = 0; i < nitems; i++) { 418 1.51 rmind const nvlist_t *rule = rules[i]; 419 1.21 rmind npf_rule_t *rl = NULL; 420 1.50 rmind const char *name; 421 1.1 rmind 422 1.60 rmind error = npf_mk_singlerule(npf, rule, resp, nc->rule_procs, &rl); 423 1.6 rmind if (error) { 424 1.1 rmind break; 425 1.6 rmind } 426 1.51 rmind name = dnvlist_get_string(rule, "name", NULL); 427 1.51 rmind if (name && npf_ruleset_lookup(rlset, name)) { 428 1.60 rmind NPF_ERR_DEBUG(resp); 429 1.50 rmind npf_rule_free(rl); 430 1.51 rmind error = EEXIST; 431 1.51 rmind break; 432 1.50 rmind } 433 1.6 rmind npf_ruleset_insert(rlset, rl); 434 1.1 rmind } 435 1.57 rmind nc->ruleset = rlset; 436 1.1 rmind return error; 437 1.1 rmind } 438 1.1 rmind 439 1.6 rmind static int __noinline 440 1.60 rmind npf_mk_singlenat(npf_t *npf, const nvlist_t *nat, nvlist_t *resp, 441 1.60 rmind npf_ruleset_t *ntset, npf_tableset_t *tblset, npf_rule_t **rlp) 442 1.54 rmind { 443 1.54 rmind npf_rule_t *rl = NULL; 444 1.54 rmind npf_natpolicy_t *np; 445 1.54 rmind int error; 446 1.54 rmind 447 1.54 rmind /* 448 1.54 rmind * NAT rules are standard rules, plus the translation policy. 449 1.54 rmind * We first construct the rule structure. 450 1.54 rmind */ 451 1.60 rmind error = npf_mk_singlerule(npf, nat, resp, NULL, &rl); 452 1.54 rmind if (error) { 453 1.54 rmind return error; 454 1.54 rmind } 455 1.54 rmind KASSERT(rl != NULL); 456 1.54 rmind *rlp = rl; 457 1.54 rmind 458 1.59 rmind /* If this rule is named, then it is a group with NAT policies. */ 459 1.54 rmind if (dnvlist_get_string(nat, "name", NULL)) { 460 1.54 rmind return 0; 461 1.54 rmind } 462 1.54 rmind 463 1.54 rmind /* Check the table ID. */ 464 1.54 rmind if (nvlist_exists_number(nat, "nat-table-id")) { 465 1.54 rmind unsigned tid = nvlist_get_number(nat, "nat-table-id"); 466 1.54 rmind 467 1.54 rmind if (!npf_tableset_getbyid(tblset, tid)) { 468 1.60 rmind NPF_ERR_DEBUG(resp); 469 1.54 rmind error = EINVAL; 470 1.54 rmind goto out; 471 1.54 rmind } 472 1.54 rmind } 473 1.54 rmind 474 1.54 rmind /* Allocate a new NAT policy and assign it to the rule. */ 475 1.60 rmind np = npf_natpolicy_create(npf, nat, ntset); 476 1.54 rmind if (np == NULL) { 477 1.60 rmind NPF_ERR_DEBUG(resp); 478 1.54 rmind error = ENOMEM; 479 1.54 rmind goto out; 480 1.54 rmind } 481 1.54 rmind npf_rule_setnat(rl, np); 482 1.54 rmind out: 483 1.54 rmind if (error) { 484 1.54 rmind npf_rule_free(rl); 485 1.54 rmind } 486 1.54 rmind return error; 487 1.54 rmind } 488 1.54 rmind 489 1.54 rmind static int __noinline 490 1.60 rmind npf_mk_natlist(npf_t *npf, const nvlist_t *req, nvlist_t *resp, npf_config_t *nc) 491 1.1 rmind { 492 1.51 rmind const nvlist_t * const *nat_rules; 493 1.51 rmind npf_ruleset_t *ntset; 494 1.51 rmind size_t nitems; 495 1.51 rmind int error = 0; 496 1.1 rmind 497 1.51 rmind /* 498 1.51 rmind * NAT policies must be an array, but enforce a limit. 499 1.51 rmind */ 500 1.60 rmind if (nvlist_exists_nvlist_array(req, "nat")) { 501 1.60 rmind nat_rules = nvlist_get_nvlist_array(req, "nat", &nitems); 502 1.51 rmind if (nitems > NPF_MAX_RULES) { 503 1.60 rmind NPF_ERR_DEBUG(resp); 504 1.51 rmind return E2BIG; 505 1.51 rmind } 506 1.51 rmind } else { 507 1.51 rmind nat_rules = NULL; 508 1.51 rmind nitems = 0; 509 1.12 rmind } 510 1.51 rmind ntset = npf_ruleset_create(nitems); 511 1.51 rmind for (unsigned i = 0; i < nitems; i++) { 512 1.51 rmind const nvlist_t *nat = nat_rules[i]; 513 1.21 rmind npf_rule_t *rl = NULL; 514 1.1 rmind 515 1.60 rmind error = npf_mk_singlenat(npf, nat, resp, ntset, 516 1.60 rmind nc->tableset, &rl); 517 1.6 rmind if (error) { 518 1.1 rmind break; 519 1.6 rmind } 520 1.51 rmind npf_ruleset_insert(ntset, rl); 521 1.1 rmind } 522 1.57 rmind nc->nat_ruleset = ntset; 523 1.1 rmind return error; 524 1.1 rmind } 525 1.1 rmind 526 1.1 rmind /* 527 1.35 rmind * npf_mk_connlist: import a list of connections and load them. 528 1.35 rmind */ 529 1.35 rmind static int __noinline 530 1.60 rmind npf_mk_connlist(npf_t *npf, const nvlist_t *req, nvlist_t *resp, 531 1.57 rmind npf_config_t *nc, npf_conndb_t **conndb) 532 1.35 rmind { 533 1.51 rmind const nvlist_t * const *conns; 534 1.35 rmind npf_conndb_t *cd; 535 1.51 rmind size_t nitems; 536 1.40 rmind int error = 0; 537 1.35 rmind 538 1.60 rmind if (!nvlist_exists_nvlist_array(req, "conn-list")) { 539 1.51 rmind *conndb = NULL; 540 1.51 rmind return 0; 541 1.35 rmind } 542 1.51 rmind cd = npf_conndb_create(); 543 1.60 rmind conns = nvlist_get_nvlist_array(req, "conn-list", &nitems); 544 1.51 rmind for (unsigned i = 0; i < nitems; i++) { 545 1.51 rmind const nvlist_t *conn = conns[i]; 546 1.35 rmind 547 1.40 rmind /* Construct and insert the connection. */ 548 1.57 rmind error = npf_conn_import(npf, cd, conn, nc->nat_ruleset); 549 1.35 rmind if (error) { 550 1.60 rmind NPF_ERR_DEBUG(resp); 551 1.35 rmind break; 552 1.35 rmind } 553 1.35 rmind } 554 1.35 rmind if (error) { 555 1.53 rmind npf_conndb_gc(npf, cd, true, false); 556 1.35 rmind npf_conndb_destroy(cd); 557 1.35 rmind } else { 558 1.35 rmind *conndb = cd; 559 1.35 rmind } 560 1.35 rmind return error; 561 1.35 rmind } 562 1.35 rmind 563 1.35 rmind /* 564 1.60 rmind * npfctl_load: store passed data i.e. the update settings, create the 565 1.60 rmind * passed rules, tables, etc and atomically activate them all. 566 1.1 rmind */ 567 1.51 rmind static int 568 1.60 rmind npfctl_load(npf_t *npf, const nvlist_t *req, nvlist_t *resp) 569 1.1 rmind { 570 1.57 rmind npf_config_t *nc; 571 1.35 rmind npf_conndb_t *conndb = NULL; 572 1.11 rmind bool flush; 573 1.1 rmind int error; 574 1.1 rmind 575 1.57 rmind nc = npf_config_create(); 576 1.60 rmind error = npf_mk_params(npf, req, resp, false /* validate */); 577 1.54 rmind if (error) { 578 1.54 rmind goto fail; 579 1.54 rmind } 580 1.60 rmind error = npf_mk_algs(npf, req, resp); 581 1.24 christos if (error) { 582 1.24 christos goto fail; 583 1.24 christos } 584 1.60 rmind error = npf_mk_tables(npf, req, resp, nc); 585 1.51 rmind if (error) { 586 1.30 rmind goto fail; 587 1.30 rmind } 588 1.60 rmind error = npf_mk_rprocs(npf, req, resp, nc); 589 1.10 rmind if (error) { 590 1.1 rmind goto fail; 591 1.10 rmind } 592 1.60 rmind error = npf_mk_natlist(npf, req, resp, nc); 593 1.10 rmind if (error) { 594 1.1 rmind goto fail; 595 1.10 rmind } 596 1.60 rmind error = npf_mk_rules(npf, req, resp, nc); 597 1.21 rmind if (error) { 598 1.21 rmind goto fail; 599 1.21 rmind } 600 1.60 rmind error = npf_mk_connlist(npf, req, resp, nc, &conndb); 601 1.10 rmind if (error) { 602 1.1 rmind goto fail; 603 1.10 rmind } 604 1.1 rmind 605 1.60 rmind flush = dnvlist_get_bool(req, "flush", false); 606 1.57 rmind nc->default_pass = flush; 607 1.57 rmind 608 1.1 rmind /* 609 1.35 rmind * Finally - perform the load. 610 1.1 rmind */ 611 1.57 rmind npf_config_load(npf, nc, conndb, flush); 612 1.60 rmind npf_mk_params(npf, req, resp, true /* set the params */); 613 1.60 rmind return 0; 614 1.11 rmind 615 1.1 rmind fail: 616 1.60 rmind npf_config_destroy(nc); 617 1.51 rmind return error; 618 1.51 rmind } 619 1.51 rmind 620 1.21 rmind /* 621 1.60 rmind * npfctl_save: export the active configuration, including the current 622 1.60 rmind * snapshot of the connections. Additionally, set the version and indicate 623 1.60 rmind * whether the ruleset is currently active. 624 1.35 rmind */ 625 1.60 rmind static int 626 1.60 rmind npfctl_save(npf_t *npf, const nvlist_t *req, nvlist_t *resp) 627 1.35 rmind { 628 1.57 rmind npf_config_t *nc; 629 1.35 rmind int error; 630 1.35 rmind 631 1.37 rmind /* 632 1.60 rmind * Serialize the whole NPF configuration, including connections. 633 1.37 rmind */ 634 1.60 rmind nvlist_add_number(resp, "version", NPF_VERSION); 635 1.57 rmind nc = npf_config_enter(npf); 636 1.60 rmind error = npf_params_export(npf, resp); 637 1.60 rmind if (error) { 638 1.60 rmind goto out; 639 1.60 rmind } 640 1.60 rmind error = npf_conndb_export(npf, resp); 641 1.37 rmind if (error) { 642 1.37 rmind goto out; 643 1.37 rmind } 644 1.60 rmind error = npf_ruleset_export(npf, nc->ruleset, "rules", resp); 645 1.38 rmind if (error) { 646 1.38 rmind goto out; 647 1.38 rmind } 648 1.60 rmind error = npf_ruleset_export(npf, nc->nat_ruleset, "nat", resp); 649 1.35 rmind if (error) { 650 1.35 rmind goto out; 651 1.35 rmind } 652 1.60 rmind error = npf_tableset_export(npf, nc->tableset, resp); 653 1.38 rmind if (error) { 654 1.38 rmind goto out; 655 1.38 rmind } 656 1.60 rmind error = npf_rprocset_export(nc->rule_procs, resp); 657 1.38 rmind if (error) { 658 1.38 rmind goto out; 659 1.38 rmind } 660 1.60 rmind error = npf_alg_export(npf, resp); 661 1.51 rmind if (error) { 662 1.51 rmind goto out; 663 1.51 rmind } 664 1.60 rmind nvlist_add_bool(resp, "active", npf_active_p()); 665 1.35 rmind out: 666 1.45 christos npf_config_exit(npf); 667 1.35 rmind return error; 668 1.35 rmind } 669 1.35 rmind 670 1.35 rmind /* 671 1.60 rmind * npfctl_table_replace: atomically replace a table's contents with 672 1.60 rmind * the passed table data. 673 1.56 rmind */ 674 1.56 rmind static int __noinline 675 1.60 rmind npfctl_table_replace(npf_t *npf, const nvlist_t *req, nvlist_t *resp) 676 1.56 rmind { 677 1.56 rmind npf_table_t *tbl, *gc_tbl = NULL; 678 1.57 rmind npf_config_t *nc; 679 1.56 rmind int error = 0; 680 1.56 rmind 681 1.57 rmind nc = npf_config_enter(npf); 682 1.60 rmind error = npf_mk_table(npf, req, resp, nc->tableset, &tbl, true); 683 1.56 rmind if (error) { 684 1.56 rmind goto err; 685 1.56 rmind } 686 1.57 rmind gc_tbl = npf_tableset_swap(nc->tableset, tbl); 687 1.56 rmind if (gc_tbl == NULL) { 688 1.56 rmind error = EINVAL; 689 1.56 rmind gc_tbl = tbl; 690 1.56 rmind goto err; 691 1.56 rmind } 692 1.56 rmind npf_config_sync(npf); 693 1.56 rmind err: 694 1.56 rmind npf_config_exit(npf); 695 1.56 rmind if (gc_tbl) { 696 1.56 rmind npf_table_destroy(gc_tbl); 697 1.56 rmind } 698 1.56 rmind return error; 699 1.56 rmind } 700 1.56 rmind 701 1.44 christos /* 702 1.21 rmind * npfctl_rule: add or remove dynamic rules in the specified ruleset. 703 1.21 rmind */ 704 1.60 rmind static int 705 1.60 rmind npfctl_rule(npf_t *npf, const nvlist_t *req, nvlist_t *resp) 706 1.14 rmind { 707 1.21 rmind npf_ruleset_t *rlset; 708 1.21 rmind npf_rule_t *rl = NULL; 709 1.21 rmind const char *ruleset_name; 710 1.57 rmind npf_config_t *nc; 711 1.51 rmind uint32_t rcmd; 712 1.45 christos int error = 0; 713 1.54 rmind bool natset; 714 1.14 rmind 715 1.60 rmind rcmd = dnvlist_get_number(req, "command", 0); 716 1.60 rmind natset = dnvlist_get_bool(req, "nat-ruleset", false); 717 1.60 rmind ruleset_name = dnvlist_get_string(req, "ruleset-name", NULL); 718 1.51 rmind if (!ruleset_name) { 719 1.27 rmind error = EINVAL; 720 1.27 rmind goto out; 721 1.21 rmind } 722 1.21 rmind 723 1.57 rmind nc = npf_config_enter(npf); 724 1.57 rmind rlset = natset ? nc->nat_ruleset : nc->ruleset; 725 1.54 rmind switch (rcmd) { 726 1.54 rmind case NPF_CMD_RULE_ADD: { 727 1.54 rmind if (natset) { 728 1.54 rmind /* 729 1.54 rmind * Translation rule. 730 1.54 rmind */ 731 1.60 rmind error = npf_mk_singlenat(npf, req, resp, rlset, 732 1.60 rmind nc->tableset, &rl); 733 1.54 rmind } else { 734 1.54 rmind /* 735 1.54 rmind * Standard rule. 736 1.54 rmind */ 737 1.60 rmind error = npf_mk_singlerule(npf, req, resp, NULL, &rl); 738 1.54 rmind } 739 1.51 rmind if (error) { 740 1.27 rmind goto out; 741 1.21 rmind } 742 1.21 rmind if ((error = npf_ruleset_add(rlset, ruleset_name, rl)) == 0) { 743 1.21 rmind /* Success. */ 744 1.23 rmind uint64_t id = npf_rule_getid(rl); 745 1.60 rmind nvlist_add_number(resp, "id", id); 746 1.21 rmind rl = NULL; 747 1.21 rmind } 748 1.21 rmind break; 749 1.21 rmind } 750 1.21 rmind case NPF_CMD_RULE_REMOVE: { 751 1.60 rmind uint64_t id = dnvlist_get_number(req, "id", UINT64_MAX); 752 1.23 rmind error = npf_ruleset_remove(rlset, ruleset_name, id); 753 1.21 rmind break; 754 1.21 rmind } 755 1.21 rmind case NPF_CMD_RULE_REMKEY: { 756 1.51 rmind const void *key; 757 1.51 rmind size_t len; 758 1.21 rmind 759 1.60 rmind key = dnvlist_get_binary(req, "key", &len, NULL, 0); 760 1.21 rmind if (len == 0 || len > NPF_RULE_MAXKEYLEN) { 761 1.21 rmind error = EINVAL; 762 1.21 rmind break; 763 1.21 rmind } 764 1.22 rmind error = npf_ruleset_remkey(rlset, ruleset_name, key, len); 765 1.22 rmind break; 766 1.22 rmind } 767 1.22 rmind case NPF_CMD_RULE_LIST: { 768 1.60 rmind error = npf_ruleset_list(npf, rlset, ruleset_name, resp); 769 1.22 rmind break; 770 1.22 rmind } 771 1.22 rmind case NPF_CMD_RULE_FLUSH: { 772 1.22 rmind error = npf_ruleset_flush(rlset, ruleset_name); 773 1.21 rmind break; 774 1.21 rmind } 775 1.21 rmind default: 776 1.21 rmind error = EINVAL; 777 1.21 rmind break; 778 1.21 rmind } 779 1.22 rmind 780 1.22 rmind /* Destroy any removed rules. */ 781 1.22 rmind if (!error && rcmd != NPF_CMD_RULE_ADD && rcmd != NPF_CMD_RULE_LIST) { 782 1.45 christos npf_config_sync(npf); 783 1.22 rmind npf_ruleset_gc(rlset); 784 1.22 rmind } 785 1.60 rmind out: 786 1.45 christos npf_config_exit(npf); 787 1.14 rmind 788 1.21 rmind if (rl) { 789 1.41 rmind KASSERT(error); 790 1.21 rmind npf_rule_free(rl); 791 1.21 rmind } 792 1.14 rmind return error; 793 1.14 rmind } 794 1.14 rmind 795 1.6 rmind /* 796 1.2 rmind * npfctl_table: add, remove or query entries in the specified table. 797 1.1 rmind * 798 1.51 rmind * For maximum performance, the interface is using plain structures. 799 1.1 rmind */ 800 1.1 rmind int 801 1.45 christos npfctl_table(npf_t *npf, void *data) 802 1.1 rmind { 803 1.19 rmind const npf_ioctl_table_t *nct = data; 804 1.32 rmind char tname[NPF_TABLE_MAXNAMELEN]; 805 1.57 rmind npf_config_t *nc; 806 1.32 rmind npf_table_t *t; 807 1.53 rmind int error; 808 1.32 rmind 809 1.32 rmind error = copyinstr(nct->nct_name, tname, sizeof(tname), NULL); 810 1.32 rmind if (error) { 811 1.32 rmind return error; 812 1.32 rmind } 813 1.1 rmind 814 1.57 rmind nc = npf_config_enter(npf); 815 1.57 rmind if ((t = npf_tableset_getbyname(nc->tableset, tname)) == NULL) { 816 1.53 rmind npf_config_exit(npf); 817 1.32 rmind return EINVAL; 818 1.32 rmind } 819 1.21 rmind 820 1.21 rmind switch (nct->nct_cmd) { 821 1.21 rmind case NPF_CMD_TABLE_LOOKUP: 822 1.32 rmind error = npf_table_lookup(t, nct->nct_data.ent.alen, 823 1.32 rmind &nct->nct_data.ent.addr); 824 1.20 rmind break; 825 1.21 rmind case NPF_CMD_TABLE_ADD: 826 1.32 rmind error = npf_table_insert(t, nct->nct_data.ent.alen, 827 1.32 rmind &nct->nct_data.ent.addr, nct->nct_data.ent.mask); 828 1.1 rmind break; 829 1.21 rmind case NPF_CMD_TABLE_REMOVE: 830 1.32 rmind error = npf_table_remove(t, nct->nct_data.ent.alen, 831 1.32 rmind &nct->nct_data.ent.addr, nct->nct_data.ent.mask); 832 1.19 rmind break; 833 1.21 rmind case NPF_CMD_TABLE_LIST: 834 1.32 rmind error = npf_table_list(t, nct->nct_data.buf.buf, 835 1.32 rmind nct->nct_data.buf.len); 836 1.1 rmind break; 837 1.25 rmind case NPF_CMD_TABLE_FLUSH: 838 1.32 rmind error = npf_table_flush(t); 839 1.25 rmind break; 840 1.1 rmind default: 841 1.19 rmind error = EINVAL; 842 1.19 rmind break; 843 1.1 rmind } 844 1.53 rmind npf_table_gc(npf, t); 845 1.53 rmind npf_config_exit(npf); 846 1.21 rmind 847 1.1 rmind return error; 848 1.1 rmind } 849 1.60 rmind 850 1.60 rmind /* 851 1.60 rmind * npfctl_run_op: run a particular NPF operation with a given the request. 852 1.60 rmind * 853 1.60 rmind * => Checks the ABI version. 854 1.60 rmind * => Sets the error number for the response. 855 1.60 rmind */ 856 1.60 rmind int 857 1.60 rmind npfctl_run_op(npf_t *npf, unsigned op, const nvlist_t *req, nvlist_t *resp) 858 1.60 rmind { 859 1.60 rmind uint64_t ver; 860 1.60 rmind int error; 861 1.60 rmind 862 1.60 rmind ver = dnvlist_get_number(req, "version", UINT64_MAX); 863 1.60 rmind if (__predict_false(ver != UINT64_MAX && ver != NPF_VERSION)) { 864 1.60 rmind return EPROGMISMATCH; 865 1.60 rmind } 866 1.60 rmind switch (op) { 867 1.60 rmind case IOC_NPF_LOAD: 868 1.60 rmind error = npfctl_load(npf, req, resp); 869 1.60 rmind break; 870 1.60 rmind case IOC_NPF_SAVE: 871 1.60 rmind error = npfctl_save(npf, req, resp); 872 1.60 rmind break; 873 1.60 rmind case IOC_NPF_RULE: 874 1.60 rmind error = npfctl_rule(npf, req, resp); 875 1.60 rmind break; 876 1.60 rmind case IOC_NPF_CONN_LOOKUP: 877 1.60 rmind error = npf_conn_find(npf, req, resp); 878 1.60 rmind break; 879 1.60 rmind case IOC_NPF_TABLE_REPLACE: 880 1.60 rmind error = npfctl_table_replace(npf, req, resp); 881 1.60 rmind break; 882 1.60 rmind default: 883 1.60 rmind error = ENOTTY; 884 1.60 rmind break; 885 1.60 rmind } 886 1.60 rmind nvlist_add_number(resp, "errno", error); 887 1.60 rmind return error; 888 1.60 rmind } 889