pf_ioctl.c revision 127145
1/* $FreeBSD: head/sys/contrib/pf/net/pf_ioctl.c 127145 2004-03-17 21:11:02Z mlaier $ */ 2/* $OpenBSD: pf_ioctl.c,v 1.81 2003/08/22 21:50:34 david Exp $ */ 3 4/* 5 * Copyright (c) 2001 Daniel Hartmeier 6 * All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 12 * - Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * - Redistributions in binary form must reproduce the above 15 * copyright notice, this list of conditions and the following 16 * disclaimer in the documentation and/or other materials provided 17 * with the distribution. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 20 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 21 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 22 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 23 * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 24 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 25 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 26 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 29 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 30 * POSSIBILITY OF SUCH DAMAGE. 31 * 32 * Effort sponsored in part by the Defense Advanced Research Projects 33 * Agency (DARPA) and Air Force Research Laboratory, Air Force 34 * Materiel Command, USAF, under agreement number F30602-01-2-0537. 35 * 36 */ 37 38#ifdef __FreeBSD__ 39#include "opt_inet.h" 40#include "opt_inet6.h" 41#endif 42 43#include <sys/param.h> 44#include <sys/systm.h> 45#include <sys/mbuf.h> 46#include <sys/filio.h> 47#include <sys/fcntl.h> 48#include <sys/socket.h> 49#include <sys/socketvar.h> 50#include <sys/kernel.h> 51#include <sys/time.h> 52#include <sys/malloc.h> 53#ifdef __FreeBSD__ 54#include <sys/conf.h> 55#else 56#include <sys/timeout.h> 57#include <sys/pool.h> 58#endif 59 60#include <net/if.h> 61#include <net/if_types.h> 62#include <net/route.h> 63 64#include <netinet/in.h> 65#include <netinet/in_var.h> 66#include <netinet/in_systm.h> 67#include <netinet/ip.h> 68#include <netinet/ip_var.h> 69#include <netinet/ip_icmp.h> 70 71#include <net/pfvar.h> 72 73#ifdef INET6 74#include <netinet/ip6.h> 75#include <netinet/in_pcb.h> 76#if defined(__FreeBSD__) && (__FreeBSD_version < 501108) 77#include <netinet6/ip6protosw.h> 78#endif 79#endif /* INET6 */ 80 81#ifdef ALTQ 82#include <altq/altq.h> 83#endif 84 85#ifdef __FreeBSD__ 86#if (__FreeBSD_version >= 500112) 87#include <sys/limits.h> 88#else 89#include <machine/limits.h> 90#endif 91#include <sys/lock.h> 92#include <sys/mutex.h> 93#if __FreeBSD_version < 501108 94#include <sys/protosw.h> 95#endif 96#include <net/pfil.h> 97#endif /* __FreeBSD__ */ 98 99#ifdef __FreeBSD__ 100void init_zone_var(void); 101void cleanup_pf_zone(void); 102int pfattach(void); 103#else 104void pfattach(int); 105int pfopen(dev_t, int, int, struct proc *); 106int pfclose(dev_t, int, int, struct proc *); 107#endif 108struct pf_pool *pf_get_pool(char *, char *, u_int32_t, 109 u_int8_t, u_int8_t, u_int8_t, u_int8_t, u_int8_t); 110int pf_get_ruleset_number(u_int8_t); 111void pf_init_ruleset(struct pf_ruleset *); 112void pf_mv_pool(struct pf_palist *, struct pf_palist *); 113void pf_empty_pool(struct pf_palist *); 114#ifdef __FreeBSD__ 115int pfioctl(dev_t, u_long, caddr_t, int, struct thread *); 116#else 117int pfioctl(dev_t, u_long, caddr_t, int, struct proc *); 118#endif 119 120#ifdef __FreeBSD__ 121extern struct callout pf_expire_to; 122#if __FreeBSD_version < 501108 123extern struct protosw inetsw[]; 124#endif 125#else 126extern struct timeout pf_expire_to; 127#endif 128 129struct pf_rule pf_default_rule; 130 131#define TAGID_MAX 50000 132TAILQ_HEAD(pf_tags, pf_tagname) pf_tags = TAILQ_HEAD_INITIALIZER(pf_tags); 133 134#define DPFPRINTF(n, x) if (pf_status.debug >= (n)) printf x 135 136 137#ifdef __FreeBSD__ 138static dev_t pf_dev; 139 140/* 141 * XXX - These are new and need to be checked when moveing to a new version 142 */ 143static int pf_beginrules(void *addr); 144static int pf_commitrules(void *addr); 145#ifdef ALTQ 146static int pf_beginaltqs(void *addr); 147static int pf_commitaltqs(void *addr); 148static int pf_stopaltq(void); 149#endif 150static void pf_clearstates(void); 151static int pf_clear_tables(void *addr); 152/* 153 * XXX - These are new and need to be checked when moveing to a new version 154 */ 155 156#if (__FreeBSD_version < 501108) 157static int pf_check_in(void *ip, int hlen, struct ifnet *ifp, int dir, 158 struct mbuf **m); 159static int pf_check_out(void *ip, int hlen, struct ifnet *ifp, int dir, 160 struct mbuf **m); 161#ifdef INET6 162static int pf_check6_in(void *ip, int hlen, struct ifnet *ifp, int dir, 163 struct mbuf **m); 164static int pf_check6_out(void *ip, int hlen, struct ifnet *ifp, int dir, 165 struct mbuf **m); 166#endif 167#else /* (__FreeBSD_version >= 501108) */ 168static int pf_check_in(void *arg, struct mbuf **m, struct ifnet *ifp, 169 int dir); 170static int pf_check_out(void *arg, struct mbuf **m, struct ifnet *ifp, 171 int dir); 172#ifdef INET6 173static int pf_check6_in(void *arg, struct mbuf **m, struct ifnet *ifp, 174 int dir); 175static int pf_check6_out(void *arg, struct mbuf **m, struct ifnet *ifp, 176 int dir); 177#endif 178#endif /* (__FreeBSD_version >= 501108) */ 179static int hook_pf(void); 180static int dehook_pf(void); 181static int shutdown_pf(void); 182static int pf_load(void); 183static int pf_unload(void); 184 185 186 187static struct cdevsw pf_cdevsw = { 188#if (__FreeBSD_version < 500105) 189 /* open */ noopen, 190 /* close */ noclose, 191 /* read */ noread, 192 /* write */ nowrite, 193 /* ioctl */ pfioctl, 194 /* poll */ nopoll, 195 /* mmap */ nommap, 196 /* strategy */ nostrategy, 197 /* name */ PF_NAME, 198 /* maj */ PF_CDEV_MAJOR, 199 /* dump */ nodump, 200 /* psize */ nopsize, 201 /* flags */ 0, 202 /* kqfilter */ nokqfilter, 203#elif (__FreeBSD_version < 501110) 204 .d_open = noopen, 205 .d_close = noclose, 206 .d_read = noread, 207 .d_write = nowrite, 208 .d_ioctl = pfioctl, 209 .d_poll = nopoll, 210 .d_mmap = nommap, 211 .d_strategy = nostrategy, 212 .d_name = PF_NAME, 213 .d_maj = MAJOR_AUTO, /* PF_CDEV_MAJOR */ 214 .d_dump = nodump, 215 .d_flags = 0, 216 .d_kqfilter = nokqfilter, 217#else 218 .d_ioctl = pfioctl, 219 .d_name = PF_NAME, 220 .d_version = D_VERSION, 221#endif 222}; 223 224static volatile int pf_pfil_hooked = 0; 225struct mtx pf_task_mtx; 226 227void 228init_pf_mutex(void) 229{ 230 mtx_init(&pf_task_mtx, "pf task mtx", NULL, MTX_DEF); 231/* 232 * pf_altq_mtx is initialized at altq_subr.c. 233 * 234 * #if defined(ALTQ) && !defined(ALTQ3_COMPAT) 235 * mtx_init(&pf_altq_mtx, "pf altq mtx", NULL, MTX_DEF); 236 * #endif 237 */ 238} 239 240void 241destroy_pf_mutex(void) 242{ 243 mtx_destroy(&pf_task_mtx); 244/* 245 * pf_altq_mtx is initialized at altq_subr.c. 246 * 247 * #if defined(ALTQ) && !defined(ALTQ3_COMPAT) 248 * mtx_destroy(&pf_altq_mtx); 249 * #endif 250 */ 251} 252 253void 254init_zone_var(void) 255{ 256 pf_tree_pl = pf_rule_pl = pf_addr_pl = NULL; 257 pf_state_pl = pf_altq_pl = pf_pooladdr_pl = NULL; 258 pf_frent_pl = pf_frag_pl = pf_cache_pl = pf_cent_pl = NULL; 259 pf_state_scrub_pl = NULL; 260 pfr_ktable_pl = pfr_kentry_pl = NULL; 261} 262 263void 264cleanup_pf_zone(void) 265{ 266 UMA_DESTROY(pf_tree_pl); 267 UMA_DESTROY(pf_rule_pl); 268 UMA_DESTROY(pf_addr_pl); 269 UMA_DESTROY(pf_state_pl); 270 UMA_DESTROY(pf_altq_pl); 271 UMA_DESTROY(pf_pooladdr_pl); 272 UMA_DESTROY(pf_frent_pl); 273 UMA_DESTROY(pf_frag_pl); 274 UMA_DESTROY(pf_cache_pl); 275 UMA_DESTROY(pf_cent_pl); 276 UMA_DESTROY(pfr_ktable_pl); 277 UMA_DESTROY(pfr_kentry_pl); 278 UMA_DESTROY(pf_state_scrub_pl); 279} 280 281int 282pfattach(void) 283{ 284 u_int32_t *my_timeout = pf_default_rule.timeout; 285 int error = 1; 286 287 do { 288 UMA_CREATE(pf_tree_pl, struct pf_tree_node, "pftrpl"); 289 UMA_CREATE(pf_rule_pl, struct pf_rule, "pfrulepl"); 290 UMA_CREATE(pf_addr_pl, struct pf_addr_dyn, "pfaddrpl"); 291 UMA_CREATE(pf_state_pl, struct pf_state, "pfstatepl"); 292 UMA_CREATE(pf_altq_pl, struct pf_altq, "pfaltqpl"); 293 UMA_CREATE(pf_pooladdr_pl, struct pf_pooladdr, "pfpooladdrpl"); 294 UMA_CREATE(pfr_ktable_pl, struct pfr_ktable, "pfrktable"); 295 UMA_CREATE(pfr_kentry_pl, struct pfr_kentry, "pfrkentry"); 296 UMA_CREATE(pf_frent_pl, struct pf_frent, "pffrent"); 297 UMA_CREATE(pf_frag_pl, struct pf_fragment, "pffrag"); 298 UMA_CREATE(pf_cache_pl, struct pf_fragment, "pffrcache"); 299 UMA_CREATE(pf_cent_pl, struct pf_frcache, "pffrcent"); 300 UMA_CREATE(pf_state_scrub_pl, struct pf_state_scrub, 301 "pfstatescrub"); 302 error = 0; 303 } while(0); 304 if (error) { 305 cleanup_pf_zone(); 306 return (error); 307 } 308 pfr_initialize(); 309 if ( (error = pf_osfp_initialize()) ) { 310 cleanup_pf_zone(); 311 pf_osfp_cleanup(); 312 return (error); 313 } 314 315 pf_pool_limits[PF_LIMIT_STATES].pp = pf_state_pl; 316 pf_pool_limits[PF_LIMIT_STATES].limit = PFSTATE_HIWAT; 317 pf_pool_limits[PF_LIMIT_FRAGS].pp = pf_frent_pl; 318 pf_pool_limits[PF_LIMIT_FRAGS].limit = PFFRAG_FRENT_HIWAT; 319 uma_zone_set_max(pf_pool_limits[PF_LIMIT_STATES].pp, 320 pf_pool_limits[PF_LIMIT_STATES].limit); 321 322 RB_INIT(&tree_lan_ext); 323 RB_INIT(&tree_ext_gwy); 324 TAILQ_INIT(&pf_anchors); 325 pf_init_ruleset(&pf_main_ruleset); 326 TAILQ_INIT(&pf_altqs[0]); 327 TAILQ_INIT(&pf_altqs[1]); 328 TAILQ_INIT(&pf_pabuf); 329 pf_altqs_active = &pf_altqs[0]; 330 pf_altqs_inactive = &pf_altqs[1]; 331 332 /* default rule should never be garbage collected */ 333 pf_default_rule.entries.tqe_prev = &pf_default_rule.entries.tqe_next; 334 pf_default_rule.action = PF_PASS; 335 pf_default_rule.nr = -1; 336 337 /* initialize default timeouts */ 338 my_timeout[PFTM_TCP_FIRST_PACKET] = 120; /* First TCP packet */ 339 my_timeout[PFTM_TCP_OPENING] = 30; /* No response yet */ 340 my_timeout[PFTM_TCP_ESTABLISHED] = 24*60*60; /* Established */ 341 my_timeout[PFTM_TCP_CLOSING] = 15 * 60; /* Half closed */ 342 my_timeout[PFTM_TCP_FIN_WAIT] = 45; /* Got both FINs */ 343 my_timeout[PFTM_TCP_CLOSED] = 90; /* Got a RST */ 344 my_timeout[PFTM_UDP_FIRST_PACKET] = 60; /* First UDP packet */ 345 my_timeout[PFTM_UDP_SINGLE] = 30; /* Unidirectional */ 346 my_timeout[PFTM_UDP_MULTIPLE] = 60; /* Bidirectional */ 347 my_timeout[PFTM_ICMP_FIRST_PACKET] = 20; /* First ICMP packet */ 348 my_timeout[PFTM_ICMP_ERROR_REPLY] = 10; /* Got error response */ 349 my_timeout[PFTM_OTHER_FIRST_PACKET] = 60; /* First packet */ 350 my_timeout[PFTM_OTHER_SINGLE] = 30; /* Unidirectional */ 351 my_timeout[PFTM_OTHER_MULTIPLE] = 60; /* Bidirectional */ 352 my_timeout[PFTM_FRAG] = 30; /* Fragment expire */ 353 my_timeout[PFTM_INTERVAL] = 10; /* Expire interval */ 354 355 /* 356 * XXX 357 * The 2nd arg. 0 to callout_init(9) shoule be set to CALLOUT_MPSAFE 358 * if Gaint lock is removed from the network stack. 359 */ 360 callout_init(&pf_expire_to, 0); 361 callout_reset(&pf_expire_to, my_timeout[PFTM_INTERVAL] * hz, 362 pf_purge_timeout, &pf_expire_to); 363 364 pf_normalize_init(); 365 pf_status.debug = PF_DEBUG_URGENT; 366 pf_pfil_hooked = 0; 367 return (error); 368} 369#else /* !__FreeBSD__ */ 370void 371pfattach(int num) 372{ 373 u_int32_t *timeout = pf_default_rule.timeout; 374 375 pool_init(&pf_tree_pl, sizeof(struct pf_tree_node), 0, 0, 0, "pftrpl", 376 NULL); 377 pool_init(&pf_rule_pl, sizeof(struct pf_rule), 0, 0, 0, "pfrulepl", 378 &pool_allocator_nointr); 379 pool_init(&pf_addr_pl, sizeof(struct pf_addr_dyn), 0, 0, 0, "pfaddrpl", 380 &pool_allocator_nointr); 381 pool_init(&pf_state_pl, sizeof(struct pf_state), 0, 0, 0, "pfstatepl", 382 NULL); 383 pool_init(&pf_altq_pl, sizeof(struct pf_altq), 0, 0, 0, "pfaltqpl", 384 NULL); 385 pool_init(&pf_pooladdr_pl, sizeof(struct pf_pooladdr), 0, 0, 0, 386 "pfpooladdrpl", NULL); 387 pfr_initialize(); 388 pf_osfp_initialize(); 389 390 pool_sethardlimit(&pf_state_pl, pf_pool_limits[PF_LIMIT_STATES].limit, 391 NULL, 0); 392 393 RB_INIT(&tree_lan_ext); 394 RB_INIT(&tree_ext_gwy); 395 TAILQ_INIT(&pf_anchors); 396 pf_init_ruleset(&pf_main_ruleset); 397 TAILQ_INIT(&pf_altqs[0]); 398 TAILQ_INIT(&pf_altqs[1]); 399 TAILQ_INIT(&pf_pabuf); 400 pf_altqs_active = &pf_altqs[0]; 401 pf_altqs_inactive = &pf_altqs[1]; 402 403 /* default rule should never be garbage collected */ 404 pf_default_rule.entries.tqe_prev = &pf_default_rule.entries.tqe_next; 405 pf_default_rule.action = PF_PASS; 406 pf_default_rule.nr = -1; 407 408 /* initialize default timeouts */ 409 timeout[PFTM_TCP_FIRST_PACKET] = 120; /* First TCP packet */ 410 timeout[PFTM_TCP_OPENING] = 30; /* No response yet */ 411 timeout[PFTM_TCP_ESTABLISHED] = 24*60*60; /* Established */ 412 timeout[PFTM_TCP_CLOSING] = 15 * 60; /* Half closed */ 413 timeout[PFTM_TCP_FIN_WAIT] = 45; /* Got both FINs */ 414 timeout[PFTM_TCP_CLOSED] = 90; /* Got a RST */ 415 timeout[PFTM_UDP_FIRST_PACKET] = 60; /* First UDP packet */ 416 timeout[PFTM_UDP_SINGLE] = 30; /* Unidirectional */ 417 timeout[PFTM_UDP_MULTIPLE] = 60; /* Bidirectional */ 418 timeout[PFTM_ICMP_FIRST_PACKET] = 20; /* First ICMP packet */ 419 timeout[PFTM_ICMP_ERROR_REPLY] = 10; /* Got error response */ 420 timeout[PFTM_OTHER_FIRST_PACKET] = 60; /* First packet */ 421 timeout[PFTM_OTHER_SINGLE] = 30; /* Unidirectional */ 422 timeout[PFTM_OTHER_MULTIPLE] = 60; /* Bidirectional */ 423 timeout[PFTM_FRAG] = 30; /* Fragment expire */ 424 timeout[PFTM_INTERVAL] = 10; /* Expire interval */ 425 426 timeout_set(&pf_expire_to, pf_purge_timeout, &pf_expire_to); 427 timeout_add(&pf_expire_to, timeout[PFTM_INTERVAL] * hz); 428 429 pf_normalize_init(); 430 pf_status.debug = PF_DEBUG_URGENT; 431} 432 433int 434pfopen(dev_t dev, int flags, int fmt, struct proc *p) 435{ 436 if (minor(dev) >= 1) 437 return (ENXIO); 438 return (0); 439} 440 441int 442pfclose(dev_t dev, int flags, int fmt, struct proc *p) 443{ 444 if (minor(dev) >= 1) 445 return (ENXIO); 446 return (0); 447} 448#endif /* __FreeBSD__ */ 449 450struct pf_pool * 451pf_get_pool(char *anchorname, char *rulesetname, u_int32_t ticket, 452 u_int8_t rule_action, u_int8_t rule_number, u_int8_t r_last, 453 u_int8_t active, u_int8_t check_ticket) 454{ 455 struct pf_ruleset *ruleset; 456 struct pf_rule *rule; 457 int rs_num; 458 459 ruleset = pf_find_ruleset(anchorname, rulesetname); 460 if (ruleset == NULL) 461 return (NULL); 462 rs_num = pf_get_ruleset_number(rule_action); 463 if (rs_num >= PF_RULESET_MAX) 464 return (NULL); 465 if (active) { 466 if (check_ticket && ticket != 467 ruleset->rules[rs_num].active.ticket) 468 return (NULL); 469 if (r_last) 470 rule = TAILQ_LAST(ruleset->rules[rs_num].active.ptr, 471 pf_rulequeue); 472 else 473 rule = TAILQ_FIRST(ruleset->rules[rs_num].active.ptr); 474 } else { 475 if (check_ticket && ticket != 476 ruleset->rules[rs_num].inactive.ticket) 477 return (NULL); 478 if (r_last) 479 rule = TAILQ_LAST(ruleset->rules[rs_num].inactive.ptr, 480 pf_rulequeue); 481 else 482 rule = TAILQ_FIRST(ruleset->rules[rs_num].inactive.ptr); 483 } 484 if (!r_last) { 485 while ((rule != NULL) && (rule->nr != rule_number)) 486 rule = TAILQ_NEXT(rule, entries); 487 } 488 if (rule == NULL) 489 return (NULL); 490 491 return (&rule->rpool); 492} 493 494int 495pf_get_ruleset_number(u_int8_t action) 496{ 497 switch (action) { 498 case PF_SCRUB: 499 return (PF_RULESET_SCRUB); 500 break; 501 case PF_PASS: 502 case PF_DROP: 503 return (PF_RULESET_FILTER); 504 break; 505 case PF_NAT: 506 case PF_NONAT: 507 return (PF_RULESET_NAT); 508 break; 509 case PF_BINAT: 510 case PF_NOBINAT: 511 return (PF_RULESET_BINAT); 512 break; 513 case PF_RDR: 514 case PF_NORDR: 515 return (PF_RULESET_RDR); 516 break; 517 default: 518 return (PF_RULESET_MAX); 519 break; 520 } 521} 522 523void 524pf_init_ruleset(struct pf_ruleset *ruleset) 525{ 526 int i; 527 528 memset(ruleset, 0, sizeof(struct pf_ruleset)); 529 for (i = 0; i < PF_RULESET_MAX; i++) { 530 TAILQ_INIT(&ruleset->rules[i].queues[0]); 531 TAILQ_INIT(&ruleset->rules[i].queues[1]); 532 ruleset->rules[i].active.ptr = &ruleset->rules[i].queues[0]; 533 ruleset->rules[i].inactive.ptr = &ruleset->rules[i].queues[1]; 534 } 535} 536 537struct pf_anchor * 538pf_find_anchor(const char *anchorname) 539{ 540 struct pf_anchor *anchor; 541 int n = -1; 542 543 anchor = TAILQ_FIRST(&pf_anchors); 544 while (anchor != NULL && (n = strcmp(anchor->name, anchorname)) < 0) 545 anchor = TAILQ_NEXT(anchor, entries); 546 if (n == 0) 547 return (anchor); 548 else 549 return (NULL); 550} 551 552struct pf_ruleset * 553pf_find_ruleset(char *anchorname, char *rulesetname) 554{ 555 struct pf_anchor *anchor; 556 struct pf_ruleset *ruleset; 557 558 if (!anchorname[0] && !rulesetname[0]) 559 return (&pf_main_ruleset); 560 if (!anchorname[0] || !rulesetname[0]) 561 return (NULL); 562 anchorname[PF_ANCHOR_NAME_SIZE-1] = 0; 563 rulesetname[PF_RULESET_NAME_SIZE-1] = 0; 564 anchor = pf_find_anchor(anchorname); 565 if (anchor == NULL) 566 return (NULL); 567 ruleset = TAILQ_FIRST(&anchor->rulesets); 568 while (ruleset != NULL && strcmp(ruleset->name, rulesetname) < 0) 569 ruleset = TAILQ_NEXT(ruleset, entries); 570 if (ruleset != NULL && !strcmp(ruleset->name, rulesetname)) 571 return (ruleset); 572 else 573 return (NULL); 574} 575 576struct pf_ruleset * 577pf_find_or_create_ruleset(char *anchorname, char *rulesetname) 578{ 579 struct pf_anchor *anchor, *a; 580 struct pf_ruleset *ruleset, *r; 581 582 if (!anchorname[0] && !rulesetname[0]) 583 return (&pf_main_ruleset); 584 if (!anchorname[0] || !rulesetname[0]) 585 return (NULL); 586 anchorname[PF_ANCHOR_NAME_SIZE-1] = 0; 587 rulesetname[PF_RULESET_NAME_SIZE-1] = 0; 588 a = TAILQ_FIRST(&pf_anchors); 589 while (a != NULL && strcmp(a->name, anchorname) < 0) 590 a = TAILQ_NEXT(a, entries); 591 if (a != NULL && !strcmp(a->name, anchorname)) 592 anchor = a; 593 else { 594 anchor = (struct pf_anchor *)malloc(sizeof(struct pf_anchor), 595 M_TEMP, M_NOWAIT); 596 if (anchor == NULL) 597 return (NULL); 598 memset(anchor, 0, sizeof(struct pf_anchor)); 599 bcopy(anchorname, anchor->name, sizeof(anchor->name)); 600 TAILQ_INIT(&anchor->rulesets); 601 if (a != NULL) 602 TAILQ_INSERT_BEFORE(a, anchor, entries); 603 else 604 TAILQ_INSERT_TAIL(&pf_anchors, anchor, entries); 605 } 606 r = TAILQ_FIRST(&anchor->rulesets); 607 while (r != NULL && strcmp(r->name, rulesetname) < 0) 608 r = TAILQ_NEXT(r, entries); 609 if (r != NULL && !strcmp(r->name, rulesetname)) 610 return (r); 611 ruleset = (struct pf_ruleset *)malloc(sizeof(struct pf_ruleset), 612 M_TEMP, M_NOWAIT); 613 if (ruleset != NULL) { 614 pf_init_ruleset(ruleset); 615 bcopy(rulesetname, ruleset->name, sizeof(ruleset->name)); 616 ruleset->anchor = anchor; 617 if (r != NULL) 618 TAILQ_INSERT_BEFORE(r, ruleset, entries); 619 else 620 TAILQ_INSERT_TAIL(&anchor->rulesets, ruleset, entries); 621 } 622 return (ruleset); 623} 624 625void 626pf_remove_if_empty_ruleset(struct pf_ruleset *ruleset) 627{ 628 struct pf_anchor *anchor; 629 int i; 630 631 if (ruleset == NULL || ruleset->anchor == NULL || ruleset->tables > 0 || 632 ruleset->topen) 633 return; 634 for (i = 0; i < PF_RULESET_MAX; ++i) 635 if (!TAILQ_EMPTY(ruleset->rules[i].active.ptr) || 636 !TAILQ_EMPTY(ruleset->rules[i].inactive.ptr)) 637 return; 638 639 anchor = ruleset->anchor; 640 TAILQ_REMOVE(&anchor->rulesets, ruleset, entries); 641 free(ruleset, M_TEMP); 642 643 if (TAILQ_EMPTY(&anchor->rulesets)) { 644 TAILQ_REMOVE(&pf_anchors, anchor, entries); 645 free(anchor, M_TEMP); 646 } 647} 648 649void 650pf_mv_pool(struct pf_palist *poola, struct pf_palist *poolb) 651{ 652 struct pf_pooladdr *mv_pool_pa; 653 654 while ((mv_pool_pa = TAILQ_FIRST(poola)) != NULL) { 655 TAILQ_REMOVE(poola, mv_pool_pa, entries); 656 TAILQ_INSERT_TAIL(poolb, mv_pool_pa, entries); 657 } 658} 659 660void 661pf_empty_pool(struct pf_palist *poola) 662{ 663 struct pf_pooladdr *empty_pool_pa; 664 665 while ((empty_pool_pa = TAILQ_FIRST(poola)) != NULL) { 666 pf_dynaddr_remove(&empty_pool_pa->addr); 667 pf_tbladdr_remove(&empty_pool_pa->addr); 668 TAILQ_REMOVE(poola, empty_pool_pa, entries); 669 pool_put(&pf_pooladdr_pl, empty_pool_pa); 670 } 671} 672 673void 674pf_rm_rule(struct pf_rulequeue *rulequeue, struct pf_rule *rule) 675{ 676 if (rulequeue != NULL) { 677 if (rule->states <= 0) { 678 /* 679 * XXX - we need to remove the table *before* detaching 680 * the rule to make sure the table code does not delete 681 * the anchor under our feet. 682 */ 683 pf_tbladdr_remove(&rule->src.addr); 684 pf_tbladdr_remove(&rule->dst.addr); 685 } 686 TAILQ_REMOVE(rulequeue, rule, entries); 687 rule->entries.tqe_prev = NULL; 688 rule->nr = -1; 689 } 690 if (rule->states > 0 || rule->entries.tqe_prev != NULL) 691 return; 692 pf_tag_unref(rule->tag); 693 pf_tag_unref(rule->match_tag); 694 pf_dynaddr_remove(&rule->src.addr); 695 pf_dynaddr_remove(&rule->dst.addr); 696 if (rulequeue == NULL) { 697 pf_tbladdr_remove(&rule->src.addr); 698 pf_tbladdr_remove(&rule->dst.addr); 699 } 700 pf_empty_pool(&rule->rpool.list); 701 pool_put(&pf_rule_pl, rule); 702} 703 704u_int16_t 705pf_tagname2tag(char *tagname) 706{ 707 struct pf_tagname *tag, *p = NULL; 708 u_int16_t new_tagid = 1; 709 710 TAILQ_FOREACH(tag, &pf_tags, entries) 711 if (strcmp(tagname, tag->name) == 0) { 712 tag->ref++; 713 return (tag->tag); 714 } 715 716 /* 717 * to avoid fragmentation, we do a linear search from the beginning 718 * and take the first free slot we find. if there is none or the list 719 * is empty, append a new entry at the end. 720 */ 721 722 /* new entry */ 723 if (!TAILQ_EMPTY(&pf_tags)) 724 for (p = TAILQ_FIRST(&pf_tags); p != NULL && 725 p->tag == new_tagid; p = TAILQ_NEXT(p, entries)) 726 new_tagid = p->tag + 1; 727 728 if (new_tagid > TAGID_MAX) 729 return (0); 730 731 /* allocate and fill new struct pf_tagname */ 732 tag = (struct pf_tagname *)malloc(sizeof(struct pf_tagname), 733 M_TEMP, M_NOWAIT); 734 if (tag == NULL) 735 return (0); 736 bzero(tag, sizeof(struct pf_tagname)); 737 strlcpy(tag->name, tagname, sizeof(tag->name)); 738 tag->tag = new_tagid; 739 tag->ref++; 740 741 if (p != NULL) /* insert new entry before p */ 742 TAILQ_INSERT_BEFORE(p, tag, entries); 743 else /* either list empty or no free slot in between */ 744 TAILQ_INSERT_TAIL(&pf_tags, tag, entries); 745 746 return (tag->tag); 747} 748 749void 750pf_tag2tagname(u_int16_t tagid, char *p) 751{ 752 struct pf_tagname *tag; 753 754 TAILQ_FOREACH(tag, &pf_tags, entries) 755 if (tag->tag == tagid) { 756 strlcpy(p, tag->name, PF_TAG_NAME_SIZE); 757 return; 758 } 759} 760 761void 762pf_tag_unref(u_int16_t tag) 763{ 764 struct pf_tagname *p, *next; 765 766 if (tag == 0) 767 return; 768 769 for (p = TAILQ_FIRST(&pf_tags); p != NULL; p = next) { 770 next = TAILQ_NEXT(p, entries); 771 if (tag == p->tag) { 772 if (--p->ref == 0) { 773 TAILQ_REMOVE(&pf_tags, p, entries); 774 free(p, M_TEMP); 775 } 776 break; 777 } 778 } 779} 780 781#ifdef __FreeBSD__ 782int 783pfioctl(dev_t dev, u_long cmd, caddr_t addr, int flags, struct thread *td) 784#else 785int 786pfioctl(dev_t dev, u_long cmd, caddr_t addr, int flags, struct proc *p) 787#endif 788{ 789 struct pf_pooladdr *pa = NULL; 790 struct pf_pool *pool = NULL; 791 int s; 792 int error = 0; 793 794 /* XXX keep in sync with switch() below */ 795 if (securelevel > 1) 796 switch (cmd) { 797 case DIOCGETRULES: 798 case DIOCGETRULE: 799 case DIOCGETADDRS: 800 case DIOCGETADDR: 801 case DIOCGETSTATE: 802 case DIOCSETSTATUSIF: 803 case DIOCGETSTATUS: 804 case DIOCCLRSTATUS: 805 case DIOCNATLOOK: 806 case DIOCSETDEBUG: 807 case DIOCGETSTATES: 808 case DIOCGETTIMEOUT: 809 case DIOCCLRRULECTRS: 810 case DIOCGETLIMIT: 811 case DIOCGETALTQS: 812 case DIOCGETALTQ: 813 case DIOCGETQSTATS: 814 case DIOCGETANCHORS: 815 case DIOCGETANCHOR: 816 case DIOCGETRULESETS: 817 case DIOCGETRULESET: 818 case DIOCRGETTABLES: 819 case DIOCRGETTSTATS: 820 case DIOCRCLRTSTATS: 821 case DIOCRCLRADDRS: 822 case DIOCRADDADDRS: 823 case DIOCRDELADDRS: 824 case DIOCRSETADDRS: 825 case DIOCRGETADDRS: 826 case DIOCRGETASTATS: 827 case DIOCRCLRASTATS: 828 case DIOCRTSTADDRS: 829 case DIOCOSFPGET: 830#ifdef __FreeBSD__ 831 case DIOCGIFSPEED: 832#endif 833 break; 834 default: 835 return (EPERM); 836 } 837 838 if (!(flags & FWRITE)) 839 switch (cmd) { 840 case DIOCGETRULES: 841 case DIOCGETRULE: 842 case DIOCGETADDRS: 843 case DIOCGETADDR: 844 case DIOCGETSTATE: 845 case DIOCGETSTATUS: 846 case DIOCGETSTATES: 847 case DIOCGETTIMEOUT: 848 case DIOCGETLIMIT: 849 case DIOCGETALTQS: 850 case DIOCGETALTQ: 851 case DIOCGETQSTATS: 852 case DIOCGETANCHORS: 853 case DIOCGETANCHOR: 854 case DIOCGETRULESETS: 855 case DIOCGETRULESET: 856 case DIOCRGETTABLES: 857 case DIOCRGETTSTATS: 858 case DIOCRGETADDRS: 859 case DIOCRGETASTATS: 860 case DIOCRTSTADDRS: 861 case DIOCOSFPGET: 862#ifdef __FreeBSD__ 863 case DIOCGIFSPEED: 864#endif 865 break; 866 default: 867 return (EACCES); 868 } 869 870#ifdef __FreeBSD__ 871 PF_LOCK(); 872#endif 873 874 switch (cmd) { 875 876 case DIOCSTART: 877 if (pf_status.running) 878 error = EEXIST; 879 else { 880 u_int32_t states = pf_status.states; 881#ifdef __FreeBSD__ 882 PF_UNLOCK(); 883 error = hook_pf(); 884 PF_LOCK(); 885 if (error) { 886 DPFPRINTF(PF_DEBUG_MISC, 887 ("pf: pfil registeration fail\n")); 888 break; 889 } 890#endif 891 bzero(&pf_status, sizeof(struct pf_status)); 892 pf_status.running = 1; 893 pf_status.states = states; 894#ifdef __FreeBSD__ 895 pf_status.since = time_second; 896#else 897 pf_status.since = time.tv_sec; 898#endif 899 if (status_ifp != NULL) 900#if defined(__FreeBSD__) && (__FreeBSD_version < 501113) 901 snprintf(pf_status.ifname, IFNAMSIZ, "%s%d", 902 status_ifp->if_name, status_ifp->if_unit); 903#else 904 strlcpy(pf_status.ifname, 905 status_ifp->if_xname, IFNAMSIZ); 906#endif 907 DPFPRINTF(PF_DEBUG_MISC, ("pf: started\n")); 908 } 909 break; 910 911 case DIOCSTOP: 912 if (!pf_status.running) 913 error = ENOENT; 914 else { 915 pf_status.running = 0; 916#ifdef __FreeBSD__ 917 PF_UNLOCK(); 918 error = dehook_pf(); 919 PF_LOCK(); 920 if (error) { 921 pf_status.running = 1; 922 DPFPRINTF(PF_DEBUG_MISC, 923 ("pf: pfil unregisteration failed\n")); 924 } 925#endif 926 DPFPRINTF(PF_DEBUG_MISC, ("pf: stopped\n")); 927 } 928 break; 929 930 case DIOCBEGINRULES: { 931 struct pfioc_rule *pr = (struct pfioc_rule *)addr; 932 struct pf_ruleset *ruleset; 933 struct pf_rule *rule; 934 int rs_num; 935 936 ruleset = pf_find_or_create_ruleset(pr->anchor, pr->ruleset); 937 if (ruleset == NULL) { 938 error = EINVAL; 939 break; 940 } 941 rs_num = pf_get_ruleset_number(pr->rule.action); 942 if (rs_num >= PF_RULESET_MAX) { 943 error = EINVAL; 944 break; 945 } 946 while ((rule = 947 TAILQ_FIRST(ruleset->rules[rs_num].inactive.ptr)) != NULL) 948 pf_rm_rule(ruleset->rules[rs_num].inactive.ptr, rule); 949 pr->ticket = ++ruleset->rules[rs_num].inactive.ticket; 950 break; 951 } 952 953 case DIOCADDRULE: { 954 struct pfioc_rule *pr = (struct pfioc_rule *)addr; 955 struct pf_ruleset *ruleset; 956 struct pf_rule *rule, *tail; 957 struct pf_pooladdr *pa; 958 int rs_num; 959 960 ruleset = pf_find_ruleset(pr->anchor, pr->ruleset); 961 if (ruleset == NULL) { 962 error = EINVAL; 963 break; 964 } 965 rs_num = pf_get_ruleset_number(pr->rule.action); 966 if (rs_num >= PF_RULESET_MAX) { 967 error = EINVAL; 968 break; 969 } 970 if (pr->rule.anchorname[0] && ruleset != &pf_main_ruleset) { 971 error = EINVAL; 972 break; 973 } 974 if (pr->rule.return_icmp >> 8 > ICMP_MAXTYPE) { 975 error = EINVAL; 976 break; 977 } 978 if (pr->ticket != ruleset->rules[rs_num].inactive.ticket) { 979 error = EBUSY; 980 break; 981 } 982 if (pr->pool_ticket != ticket_pabuf) { 983 error = EBUSY; 984 break; 985 } 986 rule = pool_get(&pf_rule_pl, PR_NOWAIT); 987 if (rule == NULL) { 988 error = ENOMEM; 989 break; 990 } 991 bcopy(&pr->rule, rule, sizeof(struct pf_rule)); 992 rule->anchor = NULL; 993 rule->ifp = NULL; 994 TAILQ_INIT(&rule->rpool.list); 995 /* initialize refcounting */ 996 rule->states = 0; 997 rule->entries.tqe_prev = NULL; 998#ifndef INET 999 if (rule->af == AF_INET) { 1000 pool_put(&pf_rule_pl, rule); 1001 error = EAFNOSUPPORT; 1002 break; 1003 } 1004#endif /* INET */ 1005#ifndef INET6 1006 if (rule->af == AF_INET6) { 1007 pool_put(&pf_rule_pl, rule); 1008 error = EAFNOSUPPORT; 1009 break; 1010 } 1011#endif /* INET6 */ 1012 tail = TAILQ_LAST(ruleset->rules[rs_num].inactive.ptr, 1013 pf_rulequeue); 1014 if (tail) 1015 rule->nr = tail->nr + 1; 1016 else 1017 rule->nr = 0; 1018 if (rule->ifname[0]) { 1019 rule->ifp = ifunit(rule->ifname); 1020 if (rule->ifp == NULL) { 1021 pool_put(&pf_rule_pl, rule); 1022 error = EINVAL; 1023 break; 1024 } 1025 } 1026 1027 if (rule->tagname[0]) 1028 if ((rule->tag = pf_tagname2tag(rule->tagname)) == 0) 1029 error = EBUSY; 1030 if (rule->match_tagname[0]) 1031 if ((rule->match_tag = 1032 pf_tagname2tag(rule->match_tagname)) == 0) 1033 error = EBUSY; 1034 if (rule->rt && !rule->direction) 1035 error = EINVAL; 1036 if (pf_dynaddr_setup(&rule->src.addr, rule->af)) 1037 error = EINVAL; 1038 if (pf_dynaddr_setup(&rule->dst.addr, rule->af)) 1039 error = EINVAL; 1040 if (pf_tbladdr_setup(ruleset, &rule->src.addr)) 1041 error = EINVAL; 1042 if (pf_tbladdr_setup(ruleset, &rule->dst.addr)) 1043 error = EINVAL; 1044 TAILQ_FOREACH(pa, &pf_pabuf, entries) 1045 if (pf_tbladdr_setup(ruleset, &pa->addr)) 1046 error = EINVAL; 1047 1048 pf_mv_pool(&pf_pabuf, &rule->rpool.list); 1049 if (((((rule->action == PF_NAT) || (rule->action == PF_RDR) || 1050 (rule->action == PF_BINAT)) && !rule->anchorname[0]) || 1051 (rule->rt > PF_FASTROUTE)) && 1052 (TAILQ_FIRST(&rule->rpool.list) == NULL)) 1053 error = EINVAL; 1054 1055 if (error) { 1056 pf_rm_rule(NULL, rule); 1057 break; 1058 } 1059 rule->rpool.cur = TAILQ_FIRST(&rule->rpool.list); 1060 rule->evaluations = rule->packets = rule->bytes = 0; 1061 TAILQ_INSERT_TAIL(ruleset->rules[rs_num].inactive.ptr, 1062 rule, entries); 1063 break; 1064 } 1065 1066 case DIOCCOMMITRULES: { 1067 struct pfioc_rule *pr = (struct pfioc_rule *)addr; 1068 struct pf_ruleset *ruleset; 1069 struct pf_rulequeue *old_rules; 1070 struct pf_rule *rule; 1071 int rs_num; 1072 1073 ruleset = pf_find_ruleset(pr->anchor, pr->ruleset); 1074 if (ruleset == NULL) { 1075 error = EINVAL; 1076 break; 1077 } 1078 rs_num = pf_get_ruleset_number(pr->rule.action); 1079 if (rs_num >= PF_RULESET_MAX) { 1080 error = EINVAL; 1081 break; 1082 } 1083 if (pr->ticket != ruleset->rules[rs_num].inactive.ticket) { 1084 error = EBUSY; 1085 break; 1086 } 1087 1088#ifdef ALTQ 1089 /* set queue IDs */ 1090 if (rs_num == PF_RULESET_FILTER) 1091 pf_rule_set_qid(ruleset->rules[rs_num].inactive.ptr); 1092#endif 1093 1094 /* Swap rules, keep the old. */ 1095 s = splsoftnet(); 1096 old_rules = ruleset->rules[rs_num].active.ptr; 1097 ruleset->rules[rs_num].active.ptr = 1098 ruleset->rules[rs_num].inactive.ptr; 1099 ruleset->rules[rs_num].inactive.ptr = old_rules; 1100 ruleset->rules[rs_num].active.ticket = 1101 ruleset->rules[rs_num].inactive.ticket; 1102 pf_calc_skip_steps(ruleset->rules[rs_num].active.ptr); 1103 1104 /* Purge the old rule list. */ 1105 while ((rule = TAILQ_FIRST(old_rules)) != NULL) 1106 pf_rm_rule(old_rules, rule); 1107 pf_remove_if_empty_ruleset(ruleset); 1108 pf_update_anchor_rules(); 1109 splx(s); 1110 break; 1111 } 1112 1113 case DIOCGETRULES: { 1114 struct pfioc_rule *pr = (struct pfioc_rule *)addr; 1115 struct pf_ruleset *ruleset; 1116 struct pf_rule *tail; 1117 int rs_num; 1118 1119 ruleset = pf_find_ruleset(pr->anchor, pr->ruleset); 1120 if (ruleset == NULL) { 1121 error = EINVAL; 1122 break; 1123 } 1124 rs_num = pf_get_ruleset_number(pr->rule.action); 1125 if (rs_num >= PF_RULESET_MAX) { 1126 error = EINVAL; 1127 break; 1128 } 1129 s = splsoftnet(); 1130 tail = TAILQ_LAST(ruleset->rules[rs_num].active.ptr, 1131 pf_rulequeue); 1132 if (tail) 1133 pr->nr = tail->nr + 1; 1134 else 1135 pr->nr = 0; 1136 pr->ticket = ruleset->rules[rs_num].active.ticket; 1137 splx(s); 1138 break; 1139 } 1140 1141 case DIOCGETRULE: { 1142 struct pfioc_rule *pr = (struct pfioc_rule *)addr; 1143 struct pf_ruleset *ruleset; 1144 struct pf_rule *rule; 1145 int rs_num, i; 1146 1147 ruleset = pf_find_ruleset(pr->anchor, pr->ruleset); 1148 if (ruleset == NULL) { 1149 error = EINVAL; 1150 break; 1151 } 1152 rs_num = pf_get_ruleset_number(pr->rule.action); 1153 if (rs_num >= PF_RULESET_MAX) { 1154 error = EINVAL; 1155 break; 1156 } 1157 if (pr->ticket != ruleset->rules[rs_num].active.ticket) { 1158 error = EBUSY; 1159 break; 1160 } 1161 s = splsoftnet(); 1162 rule = TAILQ_FIRST(ruleset->rules[rs_num].active.ptr); 1163 while ((rule != NULL) && (rule->nr != pr->nr)) 1164 rule = TAILQ_NEXT(rule, entries); 1165 if (rule == NULL) { 1166 error = EBUSY; 1167 splx(s); 1168 break; 1169 } 1170 bcopy(rule, &pr->rule, sizeof(struct pf_rule)); 1171 pf_dynaddr_copyout(&pr->rule.src.addr); 1172 pf_dynaddr_copyout(&pr->rule.dst.addr); 1173 pf_tbladdr_copyout(&pr->rule.src.addr); 1174 pf_tbladdr_copyout(&pr->rule.dst.addr); 1175 for (i = 0; i < PF_SKIP_COUNT; ++i) 1176 if (rule->skip[i].ptr == NULL) 1177 pr->rule.skip[i].nr = -1; 1178 else 1179 pr->rule.skip[i].nr = 1180 rule->skip[i].ptr->nr; 1181 splx(s); 1182 break; 1183 } 1184 1185 case DIOCCHANGERULE: { 1186 struct pfioc_rule *pcr = (struct pfioc_rule *)addr; 1187 struct pf_ruleset *ruleset; 1188 struct pf_rule *oldrule = NULL, *newrule = NULL; 1189 u_int32_t nr = 0; 1190 int rs_num; 1191 1192 if (!(pcr->action == PF_CHANGE_REMOVE || 1193 pcr->action == PF_CHANGE_GET_TICKET) && 1194 pcr->pool_ticket != ticket_pabuf) { 1195 error = EBUSY; 1196 break; 1197 } 1198 1199 if (pcr->action < PF_CHANGE_ADD_HEAD || 1200 pcr->action > PF_CHANGE_GET_TICKET) { 1201 error = EINVAL; 1202 break; 1203 } 1204 ruleset = pf_find_ruleset(pcr->anchor, pcr->ruleset); 1205 if (ruleset == NULL) { 1206 error = EINVAL; 1207 break; 1208 } 1209 rs_num = pf_get_ruleset_number(pcr->rule.action); 1210 if (rs_num >= PF_RULESET_MAX) { 1211 error = EINVAL; 1212 break; 1213 } 1214 1215 if (pcr->action == PF_CHANGE_GET_TICKET) { 1216 pcr->ticket = ++ruleset->rules[rs_num].active.ticket; 1217 break; 1218 } else { 1219 if (pcr->ticket != 1220 ruleset->rules[rs_num].active.ticket) { 1221 error = EINVAL; 1222 break; 1223 } 1224 if (pcr->rule.return_icmp >> 8 > ICMP_MAXTYPE) { 1225 error = EINVAL; 1226 break; 1227 } 1228 } 1229 1230 if (pcr->action != PF_CHANGE_REMOVE) { 1231 newrule = pool_get(&pf_rule_pl, PR_NOWAIT); 1232 if (newrule == NULL) { 1233 error = ENOMEM; 1234 break; 1235 } 1236 bcopy(&pcr->rule, newrule, sizeof(struct pf_rule)); 1237 TAILQ_INIT(&newrule->rpool.list); 1238 /* initialize refcounting */ 1239 newrule->states = 0; 1240 newrule->entries.tqe_prev = NULL; 1241#ifndef INET 1242 if (newrule->af == AF_INET) { 1243 pool_put(&pf_rule_pl, newrule); 1244 error = EAFNOSUPPORT; 1245 break; 1246 } 1247#endif /* INET */ 1248#ifndef INET6 1249 if (newrule->af == AF_INET6) { 1250 pool_put(&pf_rule_pl, newrule); 1251 error = EAFNOSUPPORT; 1252 break; 1253 } 1254#endif /* INET6 */ 1255 if (newrule->ifname[0]) { 1256 newrule->ifp = ifunit(newrule->ifname); 1257 if (newrule->ifp == NULL) { 1258 pool_put(&pf_rule_pl, newrule); 1259 error = EINVAL; 1260 break; 1261 } 1262 } else 1263 newrule->ifp = NULL; 1264 1265#ifdef ALTQ 1266 /* set queue IDs */ 1267 if (newrule->qname[0] != 0) { 1268 newrule->qid = pf_qname_to_qid(newrule->qname); 1269 if (newrule->pqname[0] != 0) 1270 newrule->pqid = 1271 pf_qname_to_qid(newrule->pqname); 1272 else 1273 newrule->pqid = newrule->qid; 1274 } 1275#endif 1276 if (newrule->tagname[0]) 1277 if ((newrule->tag = 1278 pf_tagname2tag(newrule->tagname)) == 0) 1279 error = EBUSY; 1280 if (newrule->match_tagname[0]) 1281 if ((newrule->match_tag = pf_tagname2tag( 1282 newrule->match_tagname)) == 0) 1283 error = EBUSY; 1284 1285 if (newrule->rt && !newrule->direction) 1286 error = EINVAL; 1287 if (pf_dynaddr_setup(&newrule->src.addr, newrule->af)) 1288 error = EINVAL; 1289 if (pf_dynaddr_setup(&newrule->dst.addr, newrule->af)) 1290 error = EINVAL; 1291 if (pf_tbladdr_setup(ruleset, &newrule->src.addr)) 1292 error = EINVAL; 1293 if (pf_tbladdr_setup(ruleset, &newrule->dst.addr)) 1294 error = EINVAL; 1295 1296 pf_mv_pool(&pf_pabuf, &newrule->rpool.list); 1297 if (((((newrule->action == PF_NAT) || 1298 (newrule->action == PF_RDR) || 1299 (newrule->action == PF_BINAT) || 1300 (newrule->rt > PF_FASTROUTE)) && 1301 !newrule->anchorname[0])) && 1302 (TAILQ_FIRST(&newrule->rpool.list) == NULL)) 1303 error = EINVAL; 1304 1305 if (error) { 1306 pf_rm_rule(NULL, newrule); 1307 break; 1308 } 1309 newrule->rpool.cur = TAILQ_FIRST(&newrule->rpool.list); 1310 newrule->evaluations = newrule->packets = 0; 1311 newrule->bytes = 0; 1312 } 1313 pf_empty_pool(&pf_pabuf); 1314 1315 s = splsoftnet(); 1316 1317 if (pcr->action == PF_CHANGE_ADD_HEAD) 1318 oldrule = TAILQ_FIRST( 1319 ruleset->rules[rs_num].active.ptr); 1320 else if (pcr->action == PF_CHANGE_ADD_TAIL) 1321 oldrule = TAILQ_LAST( 1322 ruleset->rules[rs_num].active.ptr, pf_rulequeue); 1323 else { 1324 oldrule = TAILQ_FIRST( 1325 ruleset->rules[rs_num].active.ptr); 1326 while ((oldrule != NULL) && (oldrule->nr != pcr->nr)) 1327 oldrule = TAILQ_NEXT(oldrule, entries); 1328 if (oldrule == NULL) { 1329 pf_rm_rule(NULL, newrule); 1330 error = EINVAL; 1331 splx(s); 1332 break; 1333 } 1334 } 1335 1336 if (pcr->action == PF_CHANGE_REMOVE) 1337 pf_rm_rule(ruleset->rules[rs_num].active.ptr, oldrule); 1338 else { 1339 if (oldrule == NULL) 1340 TAILQ_INSERT_TAIL( 1341 ruleset->rules[rs_num].active.ptr, 1342 newrule, entries); 1343 else if (pcr->action == PF_CHANGE_ADD_HEAD || 1344 pcr->action == PF_CHANGE_ADD_BEFORE) 1345 TAILQ_INSERT_BEFORE(oldrule, newrule, entries); 1346 else 1347 TAILQ_INSERT_AFTER( 1348 ruleset->rules[rs_num].active.ptr, 1349 oldrule, newrule, entries); 1350 } 1351 1352 nr = 0; 1353 TAILQ_FOREACH(oldrule, 1354 ruleset->rules[rs_num].active.ptr, entries) 1355 oldrule->nr = nr++; 1356 1357 pf_calc_skip_steps(ruleset->rules[rs_num].active.ptr); 1358 pf_remove_if_empty_ruleset(ruleset); 1359 pf_update_anchor_rules(); 1360 1361 ruleset->rules[rs_num].active.ticket++; 1362 splx(s); 1363 break; 1364 } 1365 1366 case DIOCCLRSTATES: { 1367 struct pf_tree_node *n; 1368 1369 s = splsoftnet(); 1370 RB_FOREACH(n, pf_state_tree, &tree_ext_gwy) 1371 n->state->timeout = PFTM_PURGE; 1372 pf_purge_expired_states(); 1373 pf_status.states = 0; 1374 splx(s); 1375 break; 1376 } 1377 1378 case DIOCKILLSTATES: { 1379 struct pf_tree_node *n; 1380 struct pf_state *st; 1381 struct pfioc_state_kill *psk = (struct pfioc_state_kill *)addr; 1382 int killed = 0; 1383 1384 s = splsoftnet(); 1385 RB_FOREACH(n, pf_state_tree, &tree_ext_gwy) { 1386 st = n->state; 1387 if ((!psk->psk_af || st->af == psk->psk_af) && 1388 (!psk->psk_proto || psk->psk_proto == st->proto) && 1389 PF_MATCHA(psk->psk_src.not, 1390 &psk->psk_src.addr.v.a.addr, 1391 &psk->psk_src.addr.v.a.mask, &st->lan.addr, 1392 st->af) && 1393 PF_MATCHA(psk->psk_dst.not, 1394 &psk->psk_dst.addr.v.a.addr, 1395 &psk->psk_dst.addr.v.a.mask, &st->ext.addr, 1396 st->af) && 1397 (psk->psk_src.port_op == 0 || 1398 pf_match_port(psk->psk_src.port_op, 1399 psk->psk_src.port[0], psk->psk_src.port[1], 1400 st->lan.port)) && 1401 (psk->psk_dst.port_op == 0 || 1402 pf_match_port(psk->psk_dst.port_op, 1403 psk->psk_dst.port[0], psk->psk_dst.port[1], 1404 st->ext.port))) { 1405 st->timeout = PFTM_PURGE; 1406 killed++; 1407 } 1408 } 1409 pf_purge_expired_states(); 1410 splx(s); 1411 psk->psk_af = killed; 1412 break; 1413 } 1414 1415 case DIOCADDSTATE: { 1416 struct pfioc_state *ps = (struct pfioc_state *)addr; 1417 struct pf_state *state; 1418 1419 if (ps->state.timeout >= PFTM_MAX && 1420 ps->state.timeout != PFTM_UNTIL_PACKET) { 1421 error = EINVAL; 1422 break; 1423 } 1424 state = pool_get(&pf_state_pl, PR_NOWAIT); 1425 if (state == NULL) { 1426 error = ENOMEM; 1427 break; 1428 } 1429 s = splsoftnet(); 1430 bcopy(&ps->state, state, sizeof(struct pf_state)); 1431 state->rule.ptr = NULL; 1432 state->nat_rule.ptr = NULL; 1433 state->anchor.ptr = NULL; 1434 state->rt_ifp = NULL; 1435#ifdef __FreeBSD__ 1436 state->creation = time_second; 1437#else 1438 state->creation = time.tv_sec; 1439#endif 1440 state->packets[0] = state->packets[1] = 0; 1441 state->bytes[0] = state->bytes[1] = 0; 1442 if (pf_insert_state(state)) { 1443 pool_put(&pf_state_pl, state); 1444 error = ENOMEM; 1445 } 1446 splx(s); 1447 break; 1448 } 1449 1450 case DIOCGETSTATE: { 1451 struct pfioc_state *ps = (struct pfioc_state *)addr; 1452 struct pf_tree_node *n; 1453 u_int32_t nr; 1454 1455 nr = 0; 1456 s = splsoftnet(); 1457 RB_FOREACH(n, pf_state_tree, &tree_ext_gwy) { 1458 if (nr >= ps->nr) 1459 break; 1460 nr++; 1461 } 1462 if (n == NULL) { 1463 error = EBUSY; 1464 splx(s); 1465 break; 1466 } 1467 bcopy(n->state, &ps->state, sizeof(struct pf_state)); 1468 ps->state.rule.nr = n->state->rule.ptr->nr; 1469 ps->state.nat_rule.nr = (n->state->nat_rule.ptr == NULL) ? 1470 -1 : n->state->nat_rule.ptr->nr; 1471 ps->state.anchor.nr = (n->state->anchor.ptr == NULL) ? 1472 -1 : n->state->anchor.ptr->nr; 1473 splx(s); 1474 ps->state.expire = pf_state_expires(n->state); 1475#ifdef __FreeBSD__ 1476 if (ps->state.expire > time_second) 1477 ps->state.expire -= time_second; 1478#else 1479 if (ps->state.expire > time.tv_sec) 1480 ps->state.expire -= time.tv_sec; 1481#endif 1482 else 1483 ps->state.expire = 0; 1484 break; 1485 } 1486 1487 case DIOCGETSTATES: { 1488 struct pfioc_states *ps = (struct pfioc_states *)addr; 1489 struct pf_tree_node *n; 1490 struct pf_state *p, pstore; 1491 u_int32_t nr = 0; 1492 int space = ps->ps_len; 1493 1494 if (space == 0) { 1495 s = splsoftnet(); 1496 RB_FOREACH(n, pf_state_tree, &tree_ext_gwy) 1497 nr++; 1498 splx(s); 1499 ps->ps_len = sizeof(struct pf_state) * nr; 1500#ifdef __FreeBSD__ 1501 PF_UNLOCK(); 1502#endif 1503 return (0); 1504 } 1505 1506 s = splsoftnet(); 1507 p = ps->ps_states; 1508 RB_FOREACH(n, pf_state_tree, &tree_ext_gwy) { 1509#ifdef __FreeBSD__ 1510 int secs = time_second; 1511#else 1512 int secs = time.tv_sec; 1513#endif 1514 1515 if ((nr + 1) * sizeof(*p) > (unsigned)ps->ps_len) 1516 break; 1517 1518 bcopy(n->state, &pstore, sizeof(pstore)); 1519 pstore.rule.nr = n->state->rule.ptr->nr; 1520 pstore.nat_rule.nr = (n->state->nat_rule.ptr == NULL) ? 1521 -1 : n->state->nat_rule.ptr->nr; 1522 pstore.anchor.nr = (n->state->anchor.ptr == NULL) ? 1523 -1 : n->state->anchor.ptr->nr; 1524 pstore.creation = secs - pstore.creation; 1525 pstore.expire = pf_state_expires(n->state); 1526 if (pstore.expire > secs) 1527 pstore.expire -= secs; 1528 else 1529 pstore.expire = 0; 1530#ifdef __FreeBSD__ 1531 PF_COPYOUT(&pstore, p, sizeof(*p), error); 1532#else 1533 error = copyout(&pstore, p, sizeof(*p)); 1534#endif 1535 if (error) { 1536 splx(s); 1537 goto fail; 1538 } 1539 p++; 1540 nr++; 1541 } 1542 ps->ps_len = sizeof(struct pf_state) * nr; 1543 splx(s); 1544 break; 1545 } 1546 1547 case DIOCGETSTATUS: { 1548 struct pf_status *s = (struct pf_status *)addr; 1549 bcopy(&pf_status, s, sizeof(struct pf_status)); 1550 break; 1551 } 1552 1553 case DIOCSETSTATUSIF: { 1554 struct pfioc_if *pi = (struct pfioc_if *)addr; 1555 struct ifnet *ifp; 1556 1557 if (pi->ifname[0] == 0) { 1558 status_ifp = NULL; 1559 bzero(pf_status.ifname, IFNAMSIZ); 1560 break; 1561 } 1562 if ((ifp = ifunit(pi->ifname)) == NULL) { 1563 error = EINVAL; 1564 break; 1565 } else if (ifp == status_ifp) 1566 break; 1567 status_ifp = ifp; 1568 /* fallthrough into DIOCCLRSTATUS */ 1569 } 1570 1571 case DIOCCLRSTATUS: { 1572 u_int32_t running = pf_status.running; 1573 u_int32_t states = pf_status.states; 1574 u_int32_t since = pf_status.since; 1575 u_int32_t debug = pf_status.debug; 1576 1577 bzero(&pf_status, sizeof(struct pf_status)); 1578 pf_status.running = running; 1579 pf_status.states = states; 1580 pf_status.since = since; 1581 pf_status.debug = debug; 1582 if (status_ifp != NULL) 1583#if defined(__FreeBSD__) && (__FreeBSD_version < 501113) 1584 snprintf(pf_status.ifname, IFNAMSIZ, "%s%d", 1585 status_ifp->if_name, status_ifp->if_unit); 1586#else 1587 strlcpy(pf_status.ifname, 1588 status_ifp->if_xname, IFNAMSIZ); 1589#endif 1590 break; 1591 } 1592 1593 case DIOCNATLOOK: { 1594 struct pfioc_natlook *pnl = (struct pfioc_natlook *)addr; 1595 struct pf_state *st; 1596 struct pf_tree_node key; 1597 int direction = pnl->direction; 1598 1599 key.af = pnl->af; 1600 key.proto = pnl->proto; 1601 1602 /* 1603 * userland gives us source and dest of connection, reverse 1604 * the lookup so we ask for what happens with the return 1605 * traffic, enabling us to find it in the state tree. 1606 */ 1607 PF_ACPY(&key.addr[1], &pnl->saddr, pnl->af); 1608 key.port[1] = pnl->sport; 1609 PF_ACPY(&key.addr[0], &pnl->daddr, pnl->af); 1610 key.port[0] = pnl->dport; 1611 1612 if (!pnl->proto || 1613 PF_AZERO(&pnl->saddr, pnl->af) || 1614 PF_AZERO(&pnl->daddr, pnl->af) || 1615 !pnl->dport || !pnl->sport) 1616 error = EINVAL; 1617 else { 1618 s = splsoftnet(); 1619 if (direction == PF_IN) 1620 st = pf_find_state(&tree_ext_gwy, &key); 1621 else 1622 st = pf_find_state(&tree_lan_ext, &key); 1623 if (st != NULL) { 1624 if (direction == PF_IN) { 1625 PF_ACPY(&pnl->rsaddr, &st->lan.addr, 1626 st->af); 1627 pnl->rsport = st->lan.port; 1628 PF_ACPY(&pnl->rdaddr, &pnl->daddr, 1629 pnl->af); 1630 pnl->rdport = pnl->dport; 1631 } else { 1632 PF_ACPY(&pnl->rdaddr, &st->gwy.addr, 1633 st->af); 1634 pnl->rdport = st->gwy.port; 1635 PF_ACPY(&pnl->rsaddr, &pnl->saddr, 1636 pnl->af); 1637 pnl->rsport = pnl->sport; 1638 } 1639 } else 1640 error = ENOENT; 1641 splx(s); 1642 } 1643 break; 1644 } 1645 1646 case DIOCSETTIMEOUT: { 1647 struct pfioc_tm *pt = (struct pfioc_tm *)addr; 1648 int old; 1649 1650 if (pt->timeout < 0 || pt->timeout >= PFTM_MAX || 1651 pt->seconds < 0) { 1652 error = EINVAL; 1653 goto fail; 1654 } 1655 old = pf_default_rule.timeout[pt->timeout]; 1656 pf_default_rule.timeout[pt->timeout] = pt->seconds; 1657 pt->seconds = old; 1658 break; 1659 } 1660 1661 case DIOCGETTIMEOUT: { 1662 struct pfioc_tm *pt = (struct pfioc_tm *)addr; 1663 1664 if (pt->timeout < 0 || pt->timeout >= PFTM_MAX) { 1665 error = EINVAL; 1666 goto fail; 1667 } 1668 pt->seconds = pf_default_rule.timeout[pt->timeout]; 1669 break; 1670 } 1671 1672 case DIOCGETLIMIT: { 1673 struct pfioc_limit *pl = (struct pfioc_limit *)addr; 1674 1675 if (pl->index < 0 || pl->index >= PF_LIMIT_MAX) { 1676 error = EINVAL; 1677 goto fail; 1678 } 1679 pl->limit = pf_pool_limits[pl->index].limit; 1680 break; 1681 } 1682 1683 case DIOCSETLIMIT: { 1684 struct pfioc_limit *pl = (struct pfioc_limit *)addr; 1685 int old_limit; 1686 1687 if (pl->index < 0 || pl->index >= PF_LIMIT_MAX) { 1688 error = EINVAL; 1689 goto fail; 1690 } 1691#ifdef __FreeBSD__ 1692 uma_zone_set_max(pf_pool_limits[pl->index].pp, pl->limit); 1693#else 1694 if (pool_sethardlimit(pf_pool_limits[pl->index].pp, 1695 pl->limit, NULL, 0) != 0) { 1696 error = EBUSY; 1697 goto fail; 1698 } 1699#endif 1700 old_limit = pf_pool_limits[pl->index].limit; 1701 pf_pool_limits[pl->index].limit = pl->limit; 1702 pl->limit = old_limit; 1703 break; 1704 } 1705 1706 case DIOCSETDEBUG: { 1707 u_int32_t *level = (u_int32_t *)addr; 1708 1709 pf_status.debug = *level; 1710 break; 1711 } 1712 1713 case DIOCCLRRULECTRS: { 1714 struct pf_ruleset *ruleset = &pf_main_ruleset; 1715 struct pf_rule *rule; 1716 1717 s = splsoftnet(); 1718 TAILQ_FOREACH(rule, 1719 ruleset->rules[PF_RULESET_FILTER].active.ptr, entries) 1720 rule->evaluations = rule->packets = 1721 rule->bytes = 0; 1722 splx(s); 1723 break; 1724 } 1725 1726#ifdef __FreeBSD__ 1727 case DIOCGIFSPEED: { 1728 struct pf_ifspeed *psp = (struct pf_ifspeed *)addr; 1729 struct pf_ifspeed ps; 1730 struct ifnet *ifp; 1731 1732 if (psp->ifname[0] != 0) { 1733 /* Can we completely trust user-land? */ 1734 strlcpy(ps.ifname, psp->ifname, IFNAMSIZ); 1735 ifp = ifunit(ps.ifname); 1736 if (ifp ) 1737 psp->baudrate = ifp->if_baudrate; 1738 else 1739 error = EINVAL; 1740 } else 1741 error = EINVAL; 1742 break; 1743 } 1744#endif /* __FreeBSD__ */ 1745 1746#ifdef ALTQ 1747 case DIOCSTARTALTQ: { 1748 struct pf_altq *altq; 1749 struct ifnet *ifp; 1750 struct tb_profile tb; 1751 1752 /* enable all altq interfaces on active list */ 1753 s = splsoftnet(); 1754 TAILQ_FOREACH(altq, pf_altqs_active, entries) { 1755 if (altq->qname[0] == 0) { 1756 if ((ifp = ifunit(altq->ifname)) == NULL) { 1757 error = EINVAL; 1758 break; 1759 } 1760 if (ifp->if_snd.altq_type != ALTQT_NONE) 1761 error = altq_enable(&ifp->if_snd); 1762 if (error != 0) 1763 break; 1764 /* set tokenbucket regulator */ 1765 tb.rate = altq->ifbandwidth; 1766 tb.depth = altq->tbrsize; 1767 error = tbr_set(&ifp->if_snd, &tb); 1768 if (error != 0) 1769 break; 1770 } 1771 } 1772#ifdef __FreeBSD__ 1773 if (error == 0) { 1774 mtx_lock(&pf_altq_mtx); 1775 pfaltq_running = 1; 1776 mtx_unlock(&pf_altq_mtx); 1777 } 1778#else 1779 if (error == 0) 1780 pfaltq_running = 1; 1781#endif 1782 splx(s); 1783 DPFPRINTF(PF_DEBUG_MISC, ("altq: started\n")); 1784 break; 1785 } 1786 1787 case DIOCSTOPALTQ: { 1788 struct pf_altq *altq; 1789 struct ifnet *ifp; 1790 struct tb_profile tb; 1791 int err; 1792 1793 /* disable all altq interfaces on active list */ 1794 s = splsoftnet(); 1795 TAILQ_FOREACH(altq, pf_altqs_active, entries) { 1796 if (altq->qname[0] == 0) { 1797 if ((ifp = ifunit(altq->ifname)) == NULL) { 1798 error = EINVAL; 1799 break; 1800 } 1801 if (ifp->if_snd.altq_type != ALTQT_NONE) { 1802 err = altq_disable(&ifp->if_snd); 1803 if (err != 0 && error == 0) 1804 error = err; 1805 } 1806 /* clear tokenbucket regulator */ 1807 tb.rate = 0; 1808 err = tbr_set(&ifp->if_snd, &tb); 1809 if (err != 0 && error == 0) 1810 error = err; 1811 } 1812 } 1813#ifdef __FreeBSD__ 1814 if (error == 0) { 1815 mtx_lock(&pf_altq_mtx); 1816 pfaltq_running = 0; 1817 mtx_unlock(&pf_altq_mtx); 1818 } 1819#else 1820 if (error == 0) 1821 pfaltq_running = 0; 1822#endif 1823 splx(s); 1824 DPFPRINTF(PF_DEBUG_MISC, ("altq: stopped\n")); 1825 break; 1826 } 1827 1828 case DIOCBEGINALTQS: { 1829 u_int32_t *ticket = (u_int32_t *)addr; 1830 struct pf_altq *altq; 1831 1832 /* Purge the old altq list */ 1833 while ((altq = TAILQ_FIRST(pf_altqs_inactive)) != NULL) { 1834 TAILQ_REMOVE(pf_altqs_inactive, altq, entries); 1835 if (altq->qname[0] == 0) { 1836 /* detach and destroy the discipline */ 1837#ifdef __FreeBSD__ 1838 PF_UNLOCK(); 1839#endif 1840 error = altq_remove(altq); 1841#ifdef __FreeBSD__ 1842 PF_LOCK(); 1843#endif 1844 } 1845 pool_put(&pf_altq_pl, altq); 1846 } 1847 *ticket = ++ticket_altqs_inactive; 1848 break; 1849 } 1850 1851 case DIOCADDALTQ: { 1852 struct pfioc_altq *pa = (struct pfioc_altq *)addr; 1853 struct pf_altq *altq, *a; 1854 1855 if (pa->ticket != ticket_altqs_inactive) { 1856 error = EBUSY; 1857 break; 1858 } 1859 altq = pool_get(&pf_altq_pl, PR_NOWAIT); 1860 if (altq == NULL) { 1861 error = ENOMEM; 1862 break; 1863 } 1864 bcopy(&pa->altq, altq, sizeof(struct pf_altq)); 1865 1866 /* 1867 * if this is for a queue, find the discipline and 1868 * copy the necessary fields 1869 */ 1870 if (altq->qname[0] != 0) { 1871 TAILQ_FOREACH(a, pf_altqs_inactive, entries) { 1872 if (strncmp(a->ifname, altq->ifname, 1873 IFNAMSIZ) == 0 && a->qname[0] == 0) { 1874 altq->altq_disc = a->altq_disc; 1875 break; 1876 } 1877 } 1878 } 1879 1880#ifdef __FreeBSD__ 1881 PF_UNLOCK(); 1882#endif 1883 error = altq_add(altq); 1884#ifdef __FreeBSD__ 1885 PF_LOCK(); 1886#endif 1887 if (error) { 1888 pool_put(&pf_altq_pl, altq); 1889 break; 1890 } 1891 1892 TAILQ_INSERT_TAIL(pf_altqs_inactive, altq, entries); 1893 bcopy(altq, &pa->altq, sizeof(struct pf_altq)); 1894 break; 1895 } 1896 1897 case DIOCCOMMITALTQS: { 1898 u_int32_t *ticket = (u_int32_t *)addr; 1899 struct pf_altqqueue *old_altqs; 1900 struct pf_altq *altq; 1901 struct pf_anchor *anchor; 1902 struct pf_ruleset *ruleset; 1903 int err; 1904 1905 if (*ticket != ticket_altqs_inactive) { 1906 error = EBUSY; 1907 break; 1908 } 1909 1910 /* Swap altqs, keep the old. */ 1911 s = splsoftnet(); 1912 old_altqs = pf_altqs_active; 1913 pf_altqs_active = pf_altqs_inactive; 1914 pf_altqs_inactive = old_altqs; 1915 ticket_altqs_active = ticket_altqs_inactive; 1916 1917 /* Attach new disciplines */ 1918 TAILQ_FOREACH(altq, pf_altqs_active, entries) { 1919 if (altq->qname[0] == 0) { 1920 /* attach the discipline */ 1921#ifdef __FreeBSD__ 1922 PF_UNLOCK(); 1923#endif 1924 error = altq_pfattach(altq); 1925#ifdef __FreeBSD__ 1926 PF_LOCK(); 1927#endif 1928 if (error) { 1929 splx(s); 1930 goto fail; 1931 } 1932 } 1933 } 1934 1935 /* Purge the old altq list */ 1936 while ((altq = TAILQ_FIRST(pf_altqs_inactive)) != NULL) { 1937 TAILQ_REMOVE(pf_altqs_inactive, altq, entries); 1938 if (altq->qname[0] == 0) { 1939 /* detach and destroy the discipline */ 1940#ifdef __FreeBSD__ 1941 PF_UNLOCK(); 1942#endif 1943 err = altq_pfdetach(altq); 1944 if (err != 0 && error == 0) 1945 error = err; 1946 err = altq_remove(altq); 1947 if (err != 0 && error == 0) 1948 error = err; 1949#ifdef __FreeBSD__ 1950 PF_LOCK(); 1951#endif 1952 } 1953 pool_put(&pf_altq_pl, altq); 1954 } 1955 splx(s); 1956 1957 /* update queue IDs */ 1958 pf_rule_set_qid( 1959 pf_main_ruleset.rules[PF_RULESET_FILTER].active.ptr); 1960 TAILQ_FOREACH(anchor, &pf_anchors, entries) { 1961 TAILQ_FOREACH(ruleset, &anchor->rulesets, entries) { 1962 pf_rule_set_qid( 1963 ruleset->rules[PF_RULESET_FILTER].active.ptr 1964 ); 1965 } 1966 } 1967 break; 1968 } 1969 1970 case DIOCGETALTQS: { 1971 struct pfioc_altq *pa = (struct pfioc_altq *)addr; 1972 struct pf_altq *altq; 1973 1974 pa->nr = 0; 1975 s = splsoftnet(); 1976 TAILQ_FOREACH(altq, pf_altqs_active, entries) 1977 pa->nr++; 1978 pa->ticket = ticket_altqs_active; 1979 splx(s); 1980 break; 1981 } 1982 1983 case DIOCGETALTQ: { 1984 struct pfioc_altq *pa = (struct pfioc_altq *)addr; 1985 struct pf_altq *altq; 1986 u_int32_t nr; 1987 1988 if (pa->ticket != ticket_altqs_active) { 1989 error = EBUSY; 1990 break; 1991 } 1992 nr = 0; 1993 s = splsoftnet(); 1994 altq = TAILQ_FIRST(pf_altqs_active); 1995 while ((altq != NULL) && (nr < pa->nr)) { 1996 altq = TAILQ_NEXT(altq, entries); 1997 nr++; 1998 } 1999 if (altq == NULL) { 2000 error = EBUSY; 2001 splx(s); 2002 break; 2003 } 2004 bcopy(altq, &pa->altq, sizeof(struct pf_altq)); 2005 splx(s); 2006 break; 2007 } 2008 2009 case DIOCCHANGEALTQ: 2010 /* CHANGEALTQ not supported yet! */ 2011 error = ENODEV; 2012 break; 2013 2014 case DIOCGETQSTATS: { 2015 struct pfioc_qstats *pq = (struct pfioc_qstats *)addr; 2016 struct pf_altq *altq; 2017 u_int32_t nr; 2018 int nbytes; 2019 2020 if (pq->ticket != ticket_altqs_active) { 2021 error = EBUSY; 2022 break; 2023 } 2024 nbytes = pq->nbytes; 2025 nr = 0; 2026 s = splsoftnet(); 2027 altq = TAILQ_FIRST(pf_altqs_active); 2028 while ((altq != NULL) && (nr < pq->nr)) { 2029 altq = TAILQ_NEXT(altq, entries); 2030 nr++; 2031 } 2032 if (altq == NULL) { 2033 error = EBUSY; 2034 splx(s); 2035 break; 2036 } 2037#ifdef __FreeBSD__ 2038 PF_UNLOCK(); 2039#endif 2040 error = altq_getqstats(altq, pq->buf, &nbytes); 2041#ifdef __FreeBSD__ 2042 PF_LOCK(); 2043#endif 2044 splx(s); 2045 if (error == 0) { 2046 pq->scheduler = altq->scheduler; 2047 pq->nbytes = nbytes; 2048 } 2049 break; 2050 } 2051#endif /* ALTQ */ 2052 2053 case DIOCBEGINADDRS: { 2054 struct pfioc_pooladdr *pp = (struct pfioc_pooladdr *)addr; 2055 2056 pf_empty_pool(&pf_pabuf); 2057 pp->ticket = ++ticket_pabuf; 2058 break; 2059 } 2060 2061 case DIOCADDADDR: { 2062 struct pfioc_pooladdr *pp = (struct pfioc_pooladdr *)addr; 2063 2064#ifndef INET 2065 if (pp->af == AF_INET) { 2066 error = EAFNOSUPPORT; 2067 break; 2068 } 2069#endif /* INET */ 2070#ifndef INET6 2071 if (pp->af == AF_INET6) { 2072 error = EAFNOSUPPORT; 2073 break; 2074 } 2075#endif /* INET6 */ 2076 if (pp->addr.addr.type != PF_ADDR_ADDRMASK && 2077 pp->addr.addr.type != PF_ADDR_DYNIFTL && 2078 pp->addr.addr.type != PF_ADDR_TABLE) { 2079 error = EINVAL; 2080 break; 2081 } 2082 pa = pool_get(&pf_pooladdr_pl, PR_NOWAIT); 2083 if (pa == NULL) { 2084 error = ENOMEM; 2085 break; 2086 } 2087 bcopy(&pp->addr, pa, sizeof(struct pf_pooladdr)); 2088 if (pa->ifname[0]) { 2089 pa->ifp = ifunit(pa->ifname); 2090 if (pa->ifp == NULL) { 2091 pool_put(&pf_pooladdr_pl, pa); 2092 error = EINVAL; 2093 break; 2094 } 2095 } 2096 if (pf_dynaddr_setup(&pa->addr, pp->af)) { 2097 pf_dynaddr_remove(&pa->addr); 2098 pool_put(&pf_pooladdr_pl, pa); 2099 error = EINVAL; 2100 break; 2101 } 2102 TAILQ_INSERT_TAIL(&pf_pabuf, pa, entries); 2103 break; 2104 } 2105 2106 case DIOCGETADDRS: { 2107 struct pfioc_pooladdr *pp = (struct pfioc_pooladdr *)addr; 2108 2109 pp->nr = 0; 2110 s = splsoftnet(); 2111 pool = pf_get_pool(pp->anchor, pp->ruleset, pp->ticket, 2112 pp->r_action, pp->r_num, 0, 1, 0); 2113 if (pool == NULL) { 2114 error = EBUSY; 2115 splx(s); 2116 break; 2117 } 2118 TAILQ_FOREACH(pa, &pool->list, entries) 2119 pp->nr++; 2120 splx(s); 2121 break; 2122 } 2123 2124 case DIOCGETADDR: { 2125 struct pfioc_pooladdr *pp = (struct pfioc_pooladdr *)addr; 2126 u_int32_t nr = 0; 2127 2128 s = splsoftnet(); 2129 pool = pf_get_pool(pp->anchor, pp->ruleset, pp->ticket, 2130 pp->r_action, pp->r_num, 0, 1, 1); 2131 if (pool == NULL) { 2132 error = EBUSY; 2133 splx(s); 2134 break; 2135 } 2136 pa = TAILQ_FIRST(&pool->list); 2137 while ((pa != NULL) && (nr < pp->nr)) { 2138 pa = TAILQ_NEXT(pa, entries); 2139 nr++; 2140 } 2141 if (pa == NULL) { 2142 error = EBUSY; 2143 splx(s); 2144 break; 2145 } 2146 bcopy(pa, &pp->addr, sizeof(struct pf_pooladdr)); 2147 pf_dynaddr_copyout(&pp->addr.addr); 2148 pf_tbladdr_copyout(&pp->addr.addr); 2149 splx(s); 2150 break; 2151 } 2152 2153 case DIOCCHANGEADDR: { 2154 struct pfioc_pooladdr *pca = (struct pfioc_pooladdr *)addr; 2155 struct pf_pooladdr *oldpa = NULL, *newpa = NULL; 2156 struct pf_ruleset *ruleset; 2157 2158 if (pca->action < PF_CHANGE_ADD_HEAD || 2159 pca->action > PF_CHANGE_REMOVE) { 2160 error = EINVAL; 2161 break; 2162 } 2163 if (pca->addr.addr.type != PF_ADDR_ADDRMASK && 2164 pca->addr.addr.type != PF_ADDR_DYNIFTL && 2165 pca->addr.addr.type != PF_ADDR_TABLE) { 2166 error = EINVAL; 2167 break; 2168 } 2169 2170 ruleset = pf_find_ruleset(pca->anchor, pca->ruleset); 2171 if (ruleset == NULL) { 2172 error = EBUSY; 2173 break; 2174 } 2175 pool = pf_get_pool(pca->anchor, pca->ruleset, pca->ticket, 2176 pca->r_action, pca->r_num, pca->r_last, 1, 1); 2177 if (pool == NULL) { 2178 error = EBUSY; 2179 break; 2180 } 2181 if (pca->action != PF_CHANGE_REMOVE) { 2182 newpa = pool_get(&pf_pooladdr_pl, PR_NOWAIT); 2183 if (newpa == NULL) { 2184 error = ENOMEM; 2185 break; 2186 } 2187 bcopy(&pca->addr, newpa, sizeof(struct pf_pooladdr)); 2188#ifndef INET 2189 if (pca->af == AF_INET) { 2190 pool_put(&pf_pooladdr_pl, newpa); 2191 error = EAFNOSUPPORT; 2192 break; 2193 } 2194#endif /* INET */ 2195#ifndef INET6 2196 if (pca->af == AF_INET6) { 2197 pool_put(&pf_pooladdr_pl, newpa); 2198 error = EAFNOSUPPORT; 2199 break; 2200 } 2201#endif /* INET6 */ 2202 if (newpa->ifname[0]) { 2203 newpa->ifp = ifunit(newpa->ifname); 2204 if (newpa->ifp == NULL) { 2205 pool_put(&pf_pooladdr_pl, newpa); 2206 error = EINVAL; 2207 break; 2208 } 2209 } else 2210 newpa->ifp = NULL; 2211 if (pf_dynaddr_setup(&newpa->addr, pca->af) || 2212 pf_tbladdr_setup(ruleset, &newpa->addr)) { 2213 pf_dynaddr_remove(&newpa->addr); 2214 pool_put(&pf_pooladdr_pl, newpa); 2215 error = EINVAL; 2216 break; 2217 } 2218 } 2219 2220 s = splsoftnet(); 2221 2222 if (pca->action == PF_CHANGE_ADD_HEAD) 2223 oldpa = TAILQ_FIRST(&pool->list); 2224 else if (pca->action == PF_CHANGE_ADD_TAIL) 2225 oldpa = TAILQ_LAST(&pool->list, pf_palist); 2226 else { 2227 int i = 0; 2228 2229 oldpa = TAILQ_FIRST(&pool->list); 2230 while ((oldpa != NULL) && (i < pca->nr)) { 2231 oldpa = TAILQ_NEXT(oldpa, entries); 2232 i++; 2233 } 2234 if (oldpa == NULL) { 2235 error = EINVAL; 2236 splx(s); 2237 break; 2238 } 2239 } 2240 2241 if (pca->action == PF_CHANGE_REMOVE) { 2242 TAILQ_REMOVE(&pool->list, oldpa, entries); 2243 pf_dynaddr_remove(&oldpa->addr); 2244 pf_tbladdr_remove(&oldpa->addr); 2245 pool_put(&pf_pooladdr_pl, oldpa); 2246 } else { 2247 if (oldpa == NULL) 2248 TAILQ_INSERT_TAIL(&pool->list, newpa, entries); 2249 else if (pca->action == PF_CHANGE_ADD_HEAD || 2250 pca->action == PF_CHANGE_ADD_BEFORE) 2251 TAILQ_INSERT_BEFORE(oldpa, newpa, entries); 2252 else 2253 TAILQ_INSERT_AFTER(&pool->list, oldpa, 2254 newpa, entries); 2255 } 2256 2257 pool->cur = TAILQ_FIRST(&pool->list); 2258 PF_ACPY(&pool->counter, &pool->cur->addr.v.a.addr, 2259 pca->af); 2260 splx(s); 2261 break; 2262 } 2263 2264 case DIOCGETANCHORS: { 2265 struct pfioc_anchor *pa = (struct pfioc_anchor *)addr; 2266 struct pf_anchor *anchor; 2267 2268 pa->nr = 0; 2269 TAILQ_FOREACH(anchor, &pf_anchors, entries) 2270 pa->nr++; 2271 break; 2272 } 2273 2274 case DIOCGETANCHOR: { 2275 struct pfioc_anchor *pa = (struct pfioc_anchor *)addr; 2276 struct pf_anchor *anchor; 2277 u_int32_t nr = 0; 2278 2279 anchor = TAILQ_FIRST(&pf_anchors); 2280 while (anchor != NULL && nr < pa->nr) { 2281 anchor = TAILQ_NEXT(anchor, entries); 2282 nr++; 2283 } 2284 if (anchor == NULL) 2285 error = EBUSY; 2286 else 2287 bcopy(anchor->name, pa->name, sizeof(pa->name)); 2288 break; 2289 } 2290 2291 case DIOCGETRULESETS: { 2292 struct pfioc_ruleset *pr = (struct pfioc_ruleset *)addr; 2293 struct pf_anchor *anchor; 2294 struct pf_ruleset *ruleset; 2295 2296 pr->anchor[PF_ANCHOR_NAME_SIZE-1] = 0; 2297 if ((anchor = pf_find_anchor(pr->anchor)) == NULL) { 2298 error = EINVAL; 2299 break; 2300 } 2301 pr->nr = 0; 2302 TAILQ_FOREACH(ruleset, &anchor->rulesets, entries) 2303 pr->nr++; 2304 break; 2305 } 2306 2307 case DIOCGETRULESET: { 2308 struct pfioc_ruleset *pr = (struct pfioc_ruleset *)addr; 2309 struct pf_anchor *anchor; 2310 struct pf_ruleset *ruleset; 2311 u_int32_t nr = 0; 2312 2313 if ((anchor = pf_find_anchor(pr->anchor)) == NULL) { 2314 error = EINVAL; 2315 break; 2316 } 2317 ruleset = TAILQ_FIRST(&anchor->rulesets); 2318 while (ruleset != NULL && nr < pr->nr) { 2319 ruleset = TAILQ_NEXT(ruleset, entries); 2320 nr++; 2321 } 2322 if (ruleset == NULL) 2323 error = EBUSY; 2324 else 2325 bcopy(ruleset->name, pr->name, sizeof(pr->name)); 2326 break; 2327 } 2328 2329 case DIOCRCLRTABLES: { 2330 struct pfioc_table *io = (struct pfioc_table *)addr; 2331 2332 if (io->pfrio_esize != 0) { 2333 error = ENODEV; 2334 break; 2335 } 2336 error = pfr_clr_tables(&io->pfrio_table, &io->pfrio_ndel, 2337 io->pfrio_flags); 2338 break; 2339 } 2340 2341 case DIOCRADDTABLES: { 2342 struct pfioc_table *io = (struct pfioc_table *)addr; 2343 2344 if (io->pfrio_esize != sizeof(struct pfr_table)) { 2345 error = ENODEV; 2346 break; 2347 } 2348 error = pfr_add_tables(io->pfrio_buffer, io->pfrio_size, 2349 &io->pfrio_nadd, io->pfrio_flags); 2350 break; 2351 } 2352 2353 case DIOCRDELTABLES: { 2354 struct pfioc_table *io = (struct pfioc_table *)addr; 2355 2356 if (io->pfrio_esize != sizeof(struct pfr_table)) { 2357 error = ENODEV; 2358 break; 2359 } 2360 error = pfr_del_tables(io->pfrio_buffer, io->pfrio_size, 2361 &io->pfrio_ndel, io->pfrio_flags); 2362 break; 2363 } 2364 2365 case DIOCRGETTABLES: { 2366 struct pfioc_table *io = (struct pfioc_table *)addr; 2367 2368 if (io->pfrio_esize != sizeof(struct pfr_table)) { 2369 error = ENODEV; 2370 break; 2371 } 2372 error = pfr_get_tables(&io->pfrio_table, io->pfrio_buffer, 2373 &io->pfrio_size, io->pfrio_flags); 2374 break; 2375 } 2376 2377 case DIOCRGETTSTATS: { 2378 struct pfioc_table *io = (struct pfioc_table *)addr; 2379 2380 if (io->pfrio_esize != sizeof(struct pfr_tstats)) { 2381 error = ENODEV; 2382 break; 2383 } 2384 error = pfr_get_tstats(&io->pfrio_table, io->pfrio_buffer, 2385 &io->pfrio_size, io->pfrio_flags); 2386 break; 2387 } 2388 2389 case DIOCRCLRTSTATS: { 2390 struct pfioc_table *io = (struct pfioc_table *)addr; 2391 2392 if (io->pfrio_esize != sizeof(struct pfr_table)) { 2393 error = ENODEV; 2394 break; 2395 } 2396 error = pfr_clr_tstats(io->pfrio_buffer, io->pfrio_size, 2397 &io->pfrio_nzero, io->pfrio_flags); 2398 break; 2399 } 2400 2401 case DIOCRSETTFLAGS: { 2402 struct pfioc_table *io = (struct pfioc_table *)addr; 2403 2404 if (io->pfrio_esize != sizeof(struct pfr_table)) { 2405 error = ENODEV; 2406 break; 2407 } 2408 error = pfr_set_tflags(io->pfrio_buffer, io->pfrio_size, 2409 io->pfrio_setflag, io->pfrio_clrflag, &io->pfrio_nchange, 2410 &io->pfrio_ndel, io->pfrio_flags); 2411 break; 2412 } 2413 2414 case DIOCRCLRADDRS: { 2415 struct pfioc_table *io = (struct pfioc_table *)addr; 2416 2417 if (io->pfrio_esize != 0) { 2418 error = ENODEV; 2419 break; 2420 } 2421 error = pfr_clr_addrs(&io->pfrio_table, &io->pfrio_ndel, 2422 io->pfrio_flags); 2423 break; 2424 } 2425 2426 case DIOCRADDADDRS: { 2427 struct pfioc_table *io = (struct pfioc_table *)addr; 2428 2429 if (io->pfrio_esize != sizeof(struct pfr_addr)) { 2430 error = ENODEV; 2431 break; 2432 } 2433 error = pfr_add_addrs(&io->pfrio_table, io->pfrio_buffer, 2434 io->pfrio_size, &io->pfrio_nadd, io->pfrio_flags); 2435 break; 2436 } 2437 2438 case DIOCRDELADDRS: { 2439 struct pfioc_table *io = (struct pfioc_table *)addr; 2440 2441 if (io->pfrio_esize != sizeof(struct pfr_addr)) { 2442 error = ENODEV; 2443 break; 2444 } 2445 error = pfr_del_addrs(&io->pfrio_table, io->pfrio_buffer, 2446 io->pfrio_size, &io->pfrio_ndel, io->pfrio_flags); 2447 break; 2448 } 2449 2450 case DIOCRSETADDRS: { 2451 struct pfioc_table *io = (struct pfioc_table *)addr; 2452 2453 if (io->pfrio_esize != sizeof(struct pfr_addr)) { 2454 error = ENODEV; 2455 break; 2456 } 2457 error = pfr_set_addrs(&io->pfrio_table, io->pfrio_buffer, 2458 io->pfrio_size, &io->pfrio_size2, &io->pfrio_nadd, 2459 &io->pfrio_ndel, &io->pfrio_nchange, io->pfrio_flags); 2460 break; 2461 } 2462 2463 case DIOCRGETADDRS: { 2464 struct pfioc_table *io = (struct pfioc_table *)addr; 2465 2466 if (io->pfrio_esize != sizeof(struct pfr_addr)) { 2467 error = ENODEV; 2468 break; 2469 } 2470 error = pfr_get_addrs(&io->pfrio_table, io->pfrio_buffer, 2471 &io->pfrio_size, io->pfrio_flags); 2472 break; 2473 } 2474 2475 case DIOCRGETASTATS: { 2476 struct pfioc_table *io = (struct pfioc_table *)addr; 2477 2478 if (io->pfrio_esize != sizeof(struct pfr_astats)) { 2479 error = ENODEV; 2480 break; 2481 } 2482 error = pfr_get_astats(&io->pfrio_table, io->pfrio_buffer, 2483 &io->pfrio_size, io->pfrio_flags); 2484 break; 2485 } 2486 2487 case DIOCRCLRASTATS: { 2488 struct pfioc_table *io = (struct pfioc_table *)addr; 2489 2490 if (io->pfrio_esize != sizeof(struct pfr_addr)) { 2491 error = ENODEV; 2492 break; 2493 } 2494 error = pfr_clr_astats(&io->pfrio_table, io->pfrio_buffer, 2495 io->pfrio_size, &io->pfrio_nzero, io->pfrio_flags); 2496 break; 2497 } 2498 2499 case DIOCRTSTADDRS: { 2500 struct pfioc_table *io = (struct pfioc_table *)addr; 2501 2502 if (io->pfrio_esize != sizeof(struct pfr_addr)) { 2503 error = ENODEV; 2504 break; 2505 } 2506 error = pfr_tst_addrs(&io->pfrio_table, io->pfrio_buffer, 2507 io->pfrio_size, &io->pfrio_nmatch, io->pfrio_flags); 2508 break; 2509 } 2510 2511 case DIOCRINABEGIN: { 2512 struct pfioc_table *io = (struct pfioc_table *)addr; 2513 2514 if (io->pfrio_esize != 0) { 2515 error = ENODEV; 2516 break; 2517 } 2518 error = pfr_ina_begin(&io->pfrio_table, &io->pfrio_ticket, 2519 &io->pfrio_ndel, io->pfrio_flags); 2520 break; 2521 } 2522 2523 case DIOCRINACOMMIT: { 2524 struct pfioc_table *io = (struct pfioc_table *)addr; 2525 2526 if (io->pfrio_esize != 0) { 2527 error = ENODEV; 2528 break; 2529 } 2530 error = pfr_ina_commit(&io->pfrio_table, io->pfrio_ticket, 2531 &io->pfrio_nadd, &io->pfrio_nchange, io->pfrio_flags); 2532 break; 2533 } 2534 2535 case DIOCRINADEFINE: { 2536 struct pfioc_table *io = (struct pfioc_table *)addr; 2537 2538 if (io->pfrio_esize != sizeof(struct pfr_addr)) { 2539 error = ENODEV; 2540 break; 2541 } 2542 error = pfr_ina_define(&io->pfrio_table, io->pfrio_buffer, 2543 io->pfrio_size, &io->pfrio_nadd, &io->pfrio_naddr, 2544 io->pfrio_ticket, io->pfrio_flags); 2545 break; 2546 } 2547 2548 case DIOCOSFPFLUSH: 2549 s = splsoftnet(); 2550 pf_osfp_flush(); 2551 splx(s); 2552 break; 2553 2554 case DIOCOSFPADD: { 2555 struct pf_osfp_ioctl *io = (struct pf_osfp_ioctl *)addr; 2556 s = splsoftnet(); 2557 error = pf_osfp_add(io); 2558 splx(s); 2559 break; 2560 } 2561 2562 case DIOCOSFPGET: { 2563 struct pf_osfp_ioctl *io = (struct pf_osfp_ioctl *)addr; 2564 s = splsoftnet(); 2565 error = pf_osfp_get(io); 2566 splx(s); 2567 break; 2568 } 2569 2570 default: 2571 error = ENODEV; 2572 break; 2573 } 2574fail: 2575#ifdef __FreeBSD__ 2576 PF_UNLOCK(); 2577#endif 2578 return (error); 2579} 2580 2581#ifdef __FreeBSD__ 2582/* 2583 * XXX - Check for version missmatch!!! 2584 */ 2585static int 2586pf_beginrules(void *addr) 2587{ 2588 struct pfioc_rule *pr = (struct pfioc_rule *)addr; 2589 struct pf_ruleset *ruleset; 2590 struct pf_rule *rule; 2591 int rs_num; 2592 int error = 0; 2593 2594 do { 2595 ruleset = pf_find_or_create_ruleset(pr->anchor, pr->ruleset); 2596 if (ruleset == NULL) { 2597 error = EINVAL; 2598 break; 2599 } 2600 rs_num = pf_get_ruleset_number(pr->rule.action); 2601 if (rs_num >= PF_RULESET_MAX) { 2602 error = EINVAL; 2603 break; 2604 } 2605 while ((rule = 2606 TAILQ_FIRST(ruleset->rules[rs_num].inactive.ptr)) != NULL) 2607 pf_rm_rule(ruleset->rules[rs_num].inactive.ptr, rule); 2608 pr->ticket = ++ruleset->rules[rs_num].inactive.ticket; 2609 } while(0); 2610 2611 return (error); 2612} 2613 2614static int 2615pf_commitrules(void *addr) 2616{ 2617 struct pfioc_rule *pr = (struct pfioc_rule *)addr; 2618 struct pf_ruleset *ruleset; 2619 struct pf_rulequeue *old_rules; 2620 struct pf_rule *rule; 2621 int rs_num, s; 2622 int error = 0; 2623 2624 do { 2625 ruleset = pf_find_ruleset(pr->anchor, pr->ruleset); 2626 if (ruleset == NULL) { 2627 error = EINVAL; 2628 break; 2629 } 2630 rs_num = pf_get_ruleset_number(pr->rule.action); 2631 if (rs_num >= PF_RULESET_MAX) { 2632 error = EINVAL; 2633 break; 2634 } 2635 if (pr->ticket != ruleset->rules[rs_num].inactive.ticket) { 2636 error = EBUSY; 2637 break; 2638 } 2639 2640#ifdef ALTQ 2641 /* set queue IDs */ 2642 if (rs_num == PF_RULESET_FILTER) 2643 pf_rule_set_qid(ruleset->rules[rs_num].inactive.ptr); 2644#endif 2645 2646 /* Swap rules, keep the old. */ 2647 s = splsoftnet(); 2648 old_rules = ruleset->rules[rs_num].active.ptr; 2649 ruleset->rules[rs_num].active.ptr = 2650 ruleset->rules[rs_num].inactive.ptr; 2651 ruleset->rules[rs_num].inactive.ptr = old_rules; 2652 ruleset->rules[rs_num].active.ticket = 2653 ruleset->rules[rs_num].inactive.ticket; 2654 pf_calc_skip_steps(ruleset->rules[rs_num].active.ptr); 2655 2656 /* Purge the old rule list. */ 2657 while ((rule = TAILQ_FIRST(old_rules)) != NULL) 2658 pf_rm_rule(old_rules, rule); 2659 pf_remove_if_empty_ruleset(ruleset); 2660 pf_update_anchor_rules(); 2661 splx(s); 2662 } while (0); 2663 2664 return (error); 2665} 2666 2667#ifdef ALTQ 2668static int 2669pf_beginaltqs(void *addr) 2670{ 2671 u_int32_t *ticket = (u_int32_t *)addr; 2672 struct pf_altq *altq; 2673 int error = 0; 2674 2675 /* Purge the old altq list */ 2676 while ((altq = TAILQ_FIRST(pf_altqs_inactive)) != NULL) { 2677 TAILQ_REMOVE(pf_altqs_inactive, altq, entries); 2678 if (altq->qname[0] == 0) { 2679#ifdef __FreeBSD__ 2680 PF_UNLOCK(); 2681#endif 2682 /* detach and destroy the discipline */ 2683 error = altq_remove(altq); 2684#ifdef __FreeBSD__ 2685 PF_LOCK(); 2686#endif 2687 } 2688 uma_zfree(pf_altq_pl, altq); 2689 } 2690 *ticket = ++ticket_altqs_inactive; 2691 2692 return (error); 2693} 2694 2695static int 2696pf_commitaltqs(void *addr) 2697{ 2698 u_int32_t *ticket = (u_int32_t *)addr; 2699 struct pf_altqqueue *old_altqs; 2700 struct pf_altq *altq; 2701 struct pf_anchor *anchor; 2702 struct pf_ruleset *ruleset; 2703 int err; 2704 int s; 2705 int error = 0; 2706 2707 do { 2708 if (*ticket != ticket_altqs_inactive) { 2709 error = EBUSY; 2710 break; 2711 } 2712 2713 /* Swap altqs, keep the old. */ 2714 s = splsoftnet(); 2715 old_altqs = pf_altqs_active; 2716 pf_altqs_active = pf_altqs_inactive; 2717 pf_altqs_inactive = old_altqs; 2718 ticket_altqs_active = ticket_altqs_inactive; 2719 2720 /* Attach new disciplines */ 2721 TAILQ_FOREACH(altq, pf_altqs_active, entries) { 2722 if (altq->qname[0] == 0) { 2723 /* attach the discipline */ 2724#ifdef __FreeBSD__ 2725 PF_UNLOCK(); 2726#endif 2727 error = altq_pfattach(altq); 2728#ifdef __FreeBSD__ 2729 PF_LOCK(); 2730#endif 2731 if (error) { 2732 splx(s); 2733 goto altq_fail; 2734 } 2735 } 2736 } 2737 2738 /* Purge the old altq list */ 2739 while ((altq = TAILQ_FIRST(pf_altqs_inactive)) != NULL) { 2740 TAILQ_REMOVE(pf_altqs_inactive, altq, entries); 2741 if (altq->qname[0] == 0) { 2742 /* detach and destroy the discipline */ 2743#ifdef __FreeBSD__ 2744 PF_UNLOCK(); 2745#endif 2746 err = altq_pfdetach(altq); 2747 if (err != 0 && error == 0) 2748 error = err; 2749 err = altq_remove(altq); 2750 if (err != 0 && error == 0) 2751 error = err; 2752#ifdef __FreeBSD__ 2753 PF_LOCK(); 2754#endif 2755 } 2756 uma_zfree(pf_altq_pl, altq); 2757 } 2758 splx(s); 2759 2760 /* update queue IDs */ 2761 pf_rule_set_qid( 2762 pf_main_ruleset.rules[PF_RULESET_FILTER].active.ptr); 2763 TAILQ_FOREACH(anchor, &pf_anchors, entries) { 2764 TAILQ_FOREACH(ruleset, &anchor->rulesets, entries) { 2765 pf_rule_set_qid( 2766 ruleset->rules[PF_RULESET_FILTER].active.ptr 2767 ); 2768 } 2769 } 2770 } while (0); 2771 2772altq_fail: 2773 2774 return (error); 2775} 2776 2777static int 2778pf_stopaltq(void) 2779{ 2780 struct pf_altq *altq; 2781 struct ifnet *ifp; 2782 struct tb_profile tb; 2783 int err; 2784 int s; 2785 int error = 0; 2786 2787 do { 2788 /* disable all altq interfaces on active list */ 2789 s = splsoftnet(); 2790 TAILQ_FOREACH(altq, pf_altqs_active, entries) { 2791 if (altq->qname[0] == 0) { 2792 if ((ifp = ifunit(altq->ifname)) == NULL) { 2793 error = EINVAL; 2794 break; 2795 } 2796 if (ifp->if_snd.altq_type != ALTQT_NONE) { 2797 err = altq_disable(&ifp->if_snd); 2798 if (err != 0 && error == 0) 2799 error = err; 2800 } 2801 /* clear tokenbucket regulator */ 2802 tb.rate = 0; 2803 err = tbr_set(&ifp->if_snd, &tb); 2804 if (err != 0 && error == 0) 2805 error = err; 2806 } 2807 } 2808#ifdef __FreeBSD__ 2809 if (error == 0) { 2810 mtx_lock(&pf_altq_mtx); 2811 pfaltq_running = 0; 2812 mtx_unlock(&pf_altq_mtx); 2813 } 2814#else 2815 if (error == 0) 2816 pfaltq_running = 0; 2817#endif 2818 splx(s); 2819 } while (0); 2820 2821 return (error); 2822} 2823#endif 2824 2825static void 2826pf_clearstates(void) 2827{ 2828 struct pf_tree_node *n; 2829 int s; 2830 2831 s = splsoftnet(); 2832 RB_FOREACH(n, pf_state_tree, &tree_ext_gwy) 2833 n->state->timeout = PFTM_PURGE; 2834 pf_purge_expired_states(); 2835 pf_status.states = 0; 2836 splx(s); 2837} 2838 2839static int 2840pf_clear_tables(void *addr) 2841{ 2842 struct pfioc_table *io = (struct pfioc_table *)addr; 2843 int error; 2844 2845 error = pfr_clr_tables(&io->pfrio_table, &io->pfrio_ndel, 2846 io->pfrio_flags); 2847 2848 return (error); 2849} 2850 2851static int 2852shutdown_pf(void) 2853{ 2854 struct pfioc_rule pr; 2855#ifdef ALTQ 2856 struct pfioc_altq pa; 2857#endif 2858 struct pfioc_table io; 2859 int error = 0; 2860 2861 callout_stop(&pf_expire_to); 2862 2863 PF_LOCK(); 2864 pf_status.running = 0; 2865 do { 2866#ifdef ALTQ 2867 if ((error = pf_stopaltq())) { 2868 DPFPRINTF(PF_DEBUG_MISC, 2869 ("ALTQ: stop(%i)\n", error)); 2870 break; 2871 } 2872#endif 2873 bzero(&pr, sizeof(pr)); 2874 pr.rule.action = PF_SCRUB; 2875 if ((error = pf_beginrules(&pr))) { 2876 DPFPRINTF(PF_DEBUG_MISC, 2877 ("PF_SCRUB: begin(%i)\n", error)); 2878 break; 2879 } 2880 if ((error = pf_commitrules(&pr))) { 2881 DPFPRINTF(PF_DEBUG_MISC, 2882 ("PF_SCRUB: commit(%i)\n", error)); 2883 break; 2884 } 2885 2886 pr.rule.action = PF_PASS; 2887 if ((error = pf_beginrules(&pr))) { 2888 DPFPRINTF(PF_DEBUG_MISC, 2889 ("PF_PASS: begin(%i)\n", error)); 2890 break; 2891 } 2892 if ((error = pf_commitrules(&pr))) { 2893 DPFPRINTF(PF_DEBUG_MISC, 2894 ("PF_PASS: commit(%i)\n", error)); 2895 break; 2896 } 2897 2898/* 2899 * XXX not sure, but can't hurt: 2900 */ 2901 bzero(&pr, sizeof(pr)); 2902 pr.rule.action = PF_NAT; 2903 if ((error = pf_beginrules(&pr))) { 2904 DPFPRINTF(PF_DEBUG_MISC, 2905 ("PF_NAT: begin(%i)\n", error)); 2906 break; 2907 } 2908 if ((error = pf_commitrules(&pr))) { 2909 DPFPRINTF(PF_DEBUG_MISC, 2910 ("PF_NAT: commit(%i)\n", error)); 2911 break; 2912 } 2913 2914 pr.rule.action = PF_BINAT; 2915 if ((error = pf_beginrules(&pr))) { 2916 DPFPRINTF(PF_DEBUG_MISC, 2917 ("PF_BINAT: begin(%i)\n", error)); 2918 break; 2919 } 2920 if ((error = pf_commitrules(&pr))) { 2921 DPFPRINTF(PF_DEBUG_MISC, 2922 ("PF_BINAT: begin(%i)\n", error)); 2923 break; 2924 } 2925 2926 pr.rule.action = PF_RDR; 2927 if ((error = pf_beginrules(&pr))) { 2928 DPFPRINTF(PF_DEBUG_MISC, 2929 ("PF_RDR: begin(%i)\n", error)); 2930 break; 2931 } 2932 if ((error = pf_commitrules(&pr))) { 2933 DPFPRINTF(PF_DEBUG_MISC, 2934 ("PF_RDR: commit(%i)\n", error)); 2935 break; 2936 } 2937 2938#ifdef ALTQ 2939 bzero(&pa, sizeof(pa)); 2940 if ((error = pf_beginaltqs(&pa))) { 2941 DPFPRINTF(PF_DEBUG_MISC, 2942 ("ALTQ: begin(%i)\n", error)); 2943 break; 2944 } 2945 if ((error = pf_commitaltqs(&pa))) { 2946 DPFPRINTF(PF_DEBUG_MISC, 2947 ("ALTQ: commit(%i)\n", error)); 2948 break; 2949 } 2950#endif 2951 pf_clearstates(); 2952 2953 bzero(&io, sizeof(io)); 2954 if ((error = pf_clear_tables(&io))) { 2955 DPFPRINTF(PF_DEBUG_MISC, 2956 ("TABLES: clear(%i)\n", error)); 2957 break; 2958 } 2959 pf_osfp_flush(); 2960 } while(0); 2961 2962 PF_UNLOCK(); 2963 return (error); 2964} 2965 2966static int 2967#if (__FreeBSD_version < 501108) 2968pf_check_in(void *ip, int hlen, struct ifnet *ifp, int dir, struct mbuf **m) 2969#else 2970pf_check_in(void *arg, struct mbuf **m, struct ifnet *ifp, int dir) 2971#endif 2972{ 2973 /* 2974 * XXX Wed Jul 9 22:03:16 2003 UTC 2975 * OpenBSD has changed its byte ordering convention on ip_len/ip_off 2976 * in network stack. OpenBSD's network stack have converted 2977 * ip_len/ip_off to host byte order frist as FreeBSD. 2978 * Now this is not true anymore , so we should convert back to network 2979 * byte order. 2980 */ 2981 struct ip *h = NULL; 2982 int chk; 2983 2984 if ((*m)->m_pkthdr.len >= (int)sizeof(struct ip)) { 2985 /* if m_pkthdr.len is less than ip header, pf will handle. */ 2986 h = mtod(*m, struct ip *); 2987 HTONS(h->ip_len); 2988 HTONS(h->ip_off); 2989 } 2990 chk = pf_test(PF_IN, ifp, m); 2991 if (chk && *m) { 2992 m_freem(*m); 2993 *m = NULL; 2994 } 2995 if (*m != NULL) { 2996 /* pf_test can change ip header location */ 2997 h = mtod(*m, struct ip *); 2998 NTOHS(h->ip_len); 2999 NTOHS(h->ip_off); 3000 } 3001 return chk; 3002} 3003 3004static int 3005#if (__FreeBSD_version < 501108) 3006pf_check_out(void *ip, int hlen, struct ifnet *ifp, int dir, struct mbuf **m) 3007#else 3008pf_check_out(void *arg, struct mbuf **m, struct ifnet *ifp, int dir) 3009#endif 3010{ 3011 /* 3012 * XXX Wed Jul 9 22:03:16 2003 UTC 3013 * OpenBSD has changed its byte ordering convention on ip_len/ip_off 3014 * in network stack. OpenBSD's network stack have converted 3015 * ip_len/ip_off to host byte order frist as FreeBSD. 3016 * Now this is not true anymore , so we should convert back to network 3017 * byte order. 3018 */ 3019 struct ip *h = NULL; 3020 int chk; 3021 3022 /* We need a proper CSUM befor we start (s. OpenBSD ip_output) */ 3023 if ((*m)->m_pkthdr.csum_flags & CSUM_DELAY_DATA) { 3024 in_delayed_cksum(*m); 3025 (*m)->m_pkthdr.csum_flags &= ~CSUM_DELAY_DATA; 3026 } 3027 if ((*m)->m_pkthdr.len >= (int)sizeof(*h)) { 3028 /* if m_pkthdr.len is less than ip header, pf will handle. */ 3029 h = mtod(*m, struct ip *); 3030 HTONS(h->ip_len); 3031 HTONS(h->ip_off); 3032 } 3033 chk = pf_test(PF_OUT, ifp, m); 3034 if (chk && *m) { 3035 m_freem(*m); 3036 *m = NULL; 3037 } 3038 if (*m != NULL) { 3039 /* pf_test can change ip header location */ 3040 h = mtod(*m, struct ip *); 3041 NTOHS(h->ip_len); 3042 NTOHS(h->ip_off); 3043 } 3044 return chk; 3045} 3046 3047#ifdef INET6 3048static int 3049#if (__FreeBSD_version < 501108) 3050pf_check6_in(void *ip, int hlen, struct ifnet *ifp, int dir, struct mbuf **m) 3051#else 3052pf_check6_in(void *arg, struct mbuf **m, struct ifnet *ifp, int dir) 3053#endif 3054{ 3055 /* 3056 * IPv6 does not affected ip_len/ip_off byte order changes. 3057 */ 3058 int chk; 3059 3060 chk = pf_test6(PF_IN, ifp, m); 3061 if (chk && *m) { 3062 m_freem(*m); 3063 *m = NULL; 3064 } 3065 return chk; 3066} 3067 3068static int 3069#if (__FreeBSD_version < 501108) 3070pf_check6_out(void *ip, int hlen, struct ifnet *ifp, int dir, struct mbuf **m) 3071#else 3072pf_check6_out(void *arg, struct mbuf **m, struct ifnet *ifp, int dir) 3073#endif 3074{ 3075 /* 3076 * IPv6 does not affected ip_len/ip_off byte order changes. 3077 */ 3078 int chk; 3079 3080 /* We need a proper CSUM befor we start (s. OpenBSD ip_output) */ 3081 if ((*m)->m_pkthdr.csum_flags & CSUM_DELAY_DATA) { 3082 in_delayed_cksum(*m); 3083 (*m)->m_pkthdr.csum_flags &= ~CSUM_DELAY_DATA; 3084 } 3085 chk = pf_test6(PF_OUT, ifp, m); 3086 if (chk && *m) { 3087 m_freem(*m); 3088 *m = NULL; 3089 } 3090 return chk; 3091} 3092#endif /* INET6 */ 3093 3094static int 3095hook_pf(void) 3096{ 3097#if (__FreeBSD_version >= 501108) 3098 struct pfil_head *pfh_inet; 3099#ifdef INET6 3100 struct pfil_head *pfh_inet6; 3101#endif 3102#endif 3103 3104 PF_ASSERT(MA_NOTOWNED); 3105 3106 if (pf_pfil_hooked) 3107 return (0); 3108 3109#if (__FreeBSD_version < 501108) 3110 /* 3111 * XXX 3112 * There is no easy way to get pfil header pointer with address 3113 * family such as AF_INET, AF_INET6. 3114 * Needs direct variable reference. 3115 */ 3116 3117 pfil_add_hook(pf_check_in, PFIL_IN, 3118 &inetsw[ip_protox[IPPROTO_IP]].pr_pfh); 3119 pfil_add_hook(pf_check_out, PFIL_OUT, 3120 &inetsw[ip_protox[IPPROTO_IP]].pr_pfh); 3121#ifdef INET6 3122 pfil_add_hook(pf_check6_in, PFIL_IN, 3123 &inet6sw[ip6_protox[IPPROTO_IPV6]].pr_pfh); 3124 pfil_add_hook(pf_check6_out, PFIL_OUT, 3125 &inet6sw[ip6_protox[IPPROTO_IPV6]].pr_pfh); 3126#endif 3127#else /* __FreeBSD_version >= 501108 */ 3128 pfh_inet = pfil_head_get(PFIL_TYPE_AF, AF_INET); 3129 if (pfh_inet == NULL) 3130 return (ESRCH); /* XXX */ 3131 pfil_add_hook(pf_check_in, NULL, PFIL_IN | PFIL_WAITOK, pfh_inet); 3132 pfil_add_hook(pf_check_out, NULL, PFIL_OUT | PFIL_WAITOK, pfh_inet); 3133#ifdef INET6 3134 pfh_inet6 = pfil_head_get(PFIL_TYPE_AF, AF_INET6); 3135 if (pfh_inet6 == NULL) { 3136 pfil_remove_hook(pf_check_in, NULL, PFIL_IN | PFIL_WAITOK, 3137 pfh_inet); 3138 pfil_remove_hook(pf_check_out, NULL, PFIL_OUT | PFIL_WAITOK, 3139 pfh_inet); 3140 return (ESRCH); /* XXX */ 3141 } 3142 pfil_add_hook(pf_check6_in, NULL, PFIL_IN | PFIL_WAITOK, pfh_inet6); 3143 pfil_add_hook(pf_check6_out, NULL, PFIL_OUT | PFIL_WAITOK, pfh_inet6); 3144#endif 3145#endif /* __FreeBSD_version >= 501108 */ 3146 3147 pf_pfil_hooked = 1; 3148 return (0); 3149} 3150 3151static int 3152dehook_pf(void) 3153{ 3154#if (__FreeBSD_version >= 501108) 3155 struct pfil_head *pfh_inet; 3156#ifdef INET6 3157 struct pfil_head *pfh_inet6; 3158#endif 3159#endif 3160 3161 PF_ASSERT(MA_NOTOWNED); 3162 3163 if (pf_pfil_hooked == 0) 3164 return (0); 3165 3166#if (__FreeBSD_version < 501108) 3167 pfil_remove_hook(pf_check_in, PFIL_IN, 3168 &inetsw[ip_protox[IPPROTO_IP]].pr_pfh); 3169 pfil_remove_hook(pf_check_out, PFIL_OUT, 3170 &inetsw[ip_protox[IPPROTO_IP]].pr_pfh); 3171#ifdef INET6 3172 pfil_remove_hook(pf_check6_in, PFIL_IN, 3173 &inet6sw[ip6_protox[IPPROTO_IPV6]].pr_pfh); 3174 pfil_remove_hook(pf_check6_out, PFIL_OUT, 3175 &inet6sw[ip6_protox[IPPROTO_IPV6]].pr_pfh); 3176#endif 3177#else /* __FreeBSD_version >= 501108 */ 3178 pfh_inet = pfil_head_get(PFIL_TYPE_AF, AF_INET); 3179 if (pfh_inet == NULL) 3180 return (ESRCH); /* XXX */ 3181 pfil_remove_hook(pf_check_in, NULL, PFIL_IN | PFIL_WAITOK, 3182 pfh_inet); 3183 pfil_remove_hook(pf_check_out, NULL, PFIL_OUT | PFIL_WAITOK, 3184 pfh_inet); 3185#ifdef INET6 3186 pfh_inet6 = pfil_head_get(PFIL_TYPE_AF, AF_INET6); 3187 if (pfh_inet6 == NULL) 3188 return (ESRCH); /* XXX */ 3189 pfil_remove_hook(pf_check6_in, NULL, PFIL_IN | PFIL_WAITOK, 3190 pfh_inet6); 3191 pfil_remove_hook(pf_check6_out, NULL, PFIL_OUT | PFIL_WAITOK, 3192 pfh_inet6); 3193#endif 3194#endif /* __FreeBSD_version >= 501108 */ 3195 3196 pf_pfil_hooked = 0; 3197 return (0); 3198} 3199 3200static int 3201pf_load(void) 3202{ 3203 init_zone_var(); 3204 init_pf_mutex(); 3205 pf_dev = make_dev(&pf_cdevsw, 0, 0, 0, 0600, PF_NAME); 3206 if (pfattach() < 0) { 3207 destroy_dev(pf_dev); 3208 destroy_pf_mutex(); 3209 return (ENOMEM); 3210 } 3211#ifdef ALTQ 3212 mtx_lock(&pf_altq_mtx); 3213 ++pfaltq_ref; 3214 mtx_unlock(&pf_altq_mtx); 3215#endif 3216 return (0); 3217} 3218 3219static int 3220pf_unload(void) 3221{ 3222 int error = 0; 3223 3224 PF_LOCK(); 3225 pf_status.running = 0; 3226 PF_UNLOCK(); 3227 error = dehook_pf(); 3228 if (error) { 3229 /* 3230 * Should not happen! 3231 * XXX Due to error code ESRCH, kldunload will show 3232 * a message like 'No such process'. 3233 */ 3234 printf("%s : pfil unregisteration fail\n", __FUNCTION__); 3235 return error; 3236 } 3237 shutdown_pf(); 3238 cleanup_pf_zone(); 3239 pf_osfp_cleanup(); 3240 destroy_dev(pf_dev); 3241#ifdef ALTQ 3242 mtx_lock(&pf_altq_mtx); 3243 --pfaltq_ref; 3244 mtx_unlock(&pf_altq_mtx); 3245#endif 3246 destroy_pf_mutex(); 3247 return error; 3248} 3249 3250static int 3251pf_modevent(module_t mod, int type, void *data) 3252{ 3253 int error = 0; 3254 3255 switch(type) { 3256 case MOD_LOAD: 3257 error = pf_load(); 3258 break; 3259 3260 case MOD_UNLOAD: 3261 error = pf_unload(); 3262 break; 3263 default: 3264 error = EINVAL; 3265 break; 3266 } 3267 return error; 3268} 3269 3270static moduledata_t pf_mod = { 3271 "pf", 3272 pf_modevent, 3273 0 3274}; 3275 3276DECLARE_MODULE(pf, pf_mod, SI_SUB_PSEUDO, SI_ORDER_ANY); 3277MODULE_DEPEND(pf, pflog, PFLOG_MINVER, PFLOG_PREFVER, PFLOG_MAXVER); 3278MODULE_DEPEND(pf, pfsync, PFSYNC_MINVER, PFSYNC_PREFVER, PFSYNC_MAXVER); 3279#ifdef ALTQ 3280MODULE_DEPEND(pf, pfaltq, PFALTQ_MINVER, PFALTQ_PREFVER, PFALTQ_MAXVER); 3281#endif 3282MODULE_VERSION(pf, PF_MODVER); 3283#endif /* __FreeBSD__ */ 3284