]> git.saurik.com Git - apple/xnu.git/blame - bsd/net/pf_ioctl.c
xnu-6153.121.1.tar.gz
[apple/xnu.git] / bsd / net / pf_ioctl.c
CommitLineData
b0d623f7 1/*
cb323159 2 * Copyright (c) 2007-2019 Apple Inc. All rights reserved.
b0d623f7
A
3 *
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. The rights granted to you under the License
10 * may not be used to create, or enable the creation or redistribution of,
11 * unlawful or unlicensed copies of an Apple operating system, or to
12 * circumvent, violate, or enable the circumvention or violation of, any
13 * terms of an Apple operating system software license agreement.
14 *
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
17 *
18 * The Original Code and all software distributed under the License are
19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23 * Please see the License for the specific language governing rights and
24 * limitations under the License.
25 *
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27 */
28
d1ecb069 29/* $apfw: git commit b6bf13f8321283cd7ee82b1795e86506084b1b95 $ */
b0d623f7
A
30/* $OpenBSD: pf_ioctl.c,v 1.175 2007/02/26 22:47:43 deraadt Exp $ */
31
32/*
33 * Copyright (c) 2001 Daniel Hartmeier
34 * Copyright (c) 2002,2003 Henning Brauer
35 * All rights reserved.
36 *
37 * Redistribution and use in source and binary forms, with or without
38 * modification, are permitted provided that the following conditions
39 * are met:
40 *
41 * - Redistributions of source code must retain the above copyright
42 * notice, this list of conditions and the following disclaimer.
43 * - Redistributions in binary form must reproduce the above
44 * copyright notice, this list of conditions and the following
45 * disclaimer in the documentation and/or other materials provided
46 * with the distribution.
47 *
48 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
49 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
50 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
51 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
52 * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
53 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
54 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
55 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
56 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
57 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
58 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
59 * POSSIBILITY OF SUCH DAMAGE.
60 *
61 * Effort sponsored in part by the Defense Advanced Research Projects
62 * Agency (DARPA) and Air Force Research Laboratory, Air Force
63 * Materiel Command, USAF, under agreement number F30602-01-2-0537.
64 *
65 */
66
67#include <machine/endian.h>
68#include <sys/param.h>
69#include <sys/systm.h>
70#include <sys/mbuf.h>
71#include <sys/filio.h>
72#include <sys/fcntl.h>
73#include <sys/socket.h>
74#include <sys/socketvar.h>
75#include <sys/kernel.h>
76#include <sys/time.h>
77#include <sys/proc_internal.h>
78#include <sys/malloc.h>
79#include <sys/kauth.h>
80#include <sys/conf.h>
81#include <sys/mcache.h>
6d2010ae 82#include <sys/queue.h>
cb323159 83#include <os/log.h>
b0d623f7
A
84
85#include <mach/vm_param.h>
86
316670eb 87#include <net/dlil.h>
b0d623f7
A
88#include <net/if.h>
89#include <net/if_types.h>
5ba3f43e 90#include <net/net_api_stats.h>
b0d623f7
A
91#include <net/route.h>
92
93#include <netinet/in.h>
94#include <netinet/in_var.h>
95#include <netinet/in_systm.h>
96#include <netinet/ip.h>
97#include <netinet/ip_var.h>
98#include <netinet/ip_icmp.h>
99#include <netinet/if_ether.h>
100
316670eb
A
101#if DUMMYNET
102#include <netinet/ip_dummynet.h>
103#else
104struct ip_fw_args;
105#endif /* DUMMYNET */
106
b0d623f7
A
107#include <libkern/crypto/md5.h>
108
316670eb
A
109#include <machine/machine_routines.h>
110
b0d623f7
A
111#include <miscfs/devfs/devfs.h>
112
113#include <net/pfvar.h>
114
115#if NPFSYNC
116#include <net/if_pfsync.h>
117#endif /* NPFSYNC */
118
119#if PFLOG
120#include <net/if_pflog.h>
121#endif /* PFLOG */
122
123#if INET6
124#include <netinet/ip6.h>
125#include <netinet/in_pcb.h>
126#endif /* INET6 */
127
39236c6e
A
128#include <dev/random/randomdev.h>
129
b0d623f7
A
130#if 0
131static void pfdetach(void);
132#endif
133static int pfopen(dev_t, int, int, struct proc *);
134static int pfclose(dev_t, int, int, struct proc *);
135static int pfioctl(dev_t, u_long, caddr_t, int, struct proc *);
316670eb
A
136static int pfioctl_ioc_table(u_long, struct pfioc_table_32 *,
137 struct pfioc_table_64 *, struct proc *);
138static int pfioctl_ioc_tokens(u_long, struct pfioc_tokens_32 *,
139 struct pfioc_tokens_64 *, struct proc *);
140static int pfioctl_ioc_rule(u_long, int, struct pfioc_rule *, struct proc *);
141static int pfioctl_ioc_state_kill(u_long, struct pfioc_state_kill *,
142 struct proc *);
143static int pfioctl_ioc_state(u_long, struct pfioc_state *, struct proc *);
144static int pfioctl_ioc_states(u_long, struct pfioc_states_32 *,
145 struct pfioc_states_64 *, struct proc *);
146static int pfioctl_ioc_natlook(u_long, struct pfioc_natlook *, struct proc *);
147static int pfioctl_ioc_tm(u_long, struct pfioc_tm *, struct proc *);
148static int pfioctl_ioc_limit(u_long, struct pfioc_limit *, struct proc *);
149static int pfioctl_ioc_pooladdr(u_long, struct pfioc_pooladdr *, struct proc *);
150static int pfioctl_ioc_ruleset(u_long, struct pfioc_ruleset *, struct proc *);
151static int pfioctl_ioc_trans(u_long, struct pfioc_trans_32 *,
152 struct pfioc_trans_64 *, struct proc *);
153static int pfioctl_ioc_src_nodes(u_long, struct pfioc_src_nodes_32 *,
154 struct pfioc_src_nodes_64 *, struct proc *);
155static int pfioctl_ioc_src_node_kill(u_long, struct pfioc_src_node_kill *,
156 struct proc *);
157static int pfioctl_ioc_iface(u_long, struct pfioc_iface_32 *,
158 struct pfioc_iface_64 *, struct proc *);
b0d623f7
A
159static struct pf_pool *pf_get_pool(char *, u_int32_t, u_int8_t, u_int32_t,
160 u_int8_t, u_int8_t, u_int8_t);
b0d623f7
A
161static void pf_mv_pool(struct pf_palist *, struct pf_palist *);
162static void pf_empty_pool(struct pf_palist *);
b0d623f7
A
163static int pf_begin_rules(u_int32_t *, int, const char *);
164static int pf_rollback_rules(u_int32_t, int, char *);
165static int pf_setup_pfsync_matching(struct pf_ruleset *);
166static void pf_hash_rule(MD5_CTX *, struct pf_rule *);
b0d623f7 167static void pf_hash_rule_addr(MD5_CTX *, struct pf_rule_addr *, u_int8_t);
b0d623f7 168static int pf_commit_rules(u_int32_t, int, char *);
316670eb
A
169static void pf_rule_copyin(struct pf_rule *, struct pf_rule *, struct proc *,
170 int);
6d2010ae 171static void pf_rule_copyout(struct pf_rule *, struct pf_rule *);
b0d623f7
A
172static void pf_state_export(struct pfsync_state *, struct pf_state_key *,
173 struct pf_state *);
174static void pf_state_import(struct pfsync_state *, struct pf_state_key *,
175 struct pf_state *);
6d2010ae
A
176static void pf_pooladdr_copyin(struct pf_pooladdr *, struct pf_pooladdr *);
177static void pf_pooladdr_copyout(struct pf_pooladdr *, struct pf_pooladdr *);
316670eb
A
178static void pf_expire_states_and_src_nodes(struct pf_rule *);
179static void pf_delete_rule_from_ruleset(struct pf_ruleset *,
180 int, struct pf_rule *);
39236c6e 181static void pf_addrwrap_setup(struct pf_addr_wrap *);
316670eb
A
182static int pf_rule_setup(struct pfioc_rule *, struct pf_rule *,
183 struct pf_ruleset *);
39236c6e
A
184static void pf_delete_rule_by_owner(char *, u_int32_t);
185static int pf_delete_rule_by_ticket(struct pfioc_rule *, u_int32_t);
316670eb
A
186static void pf_ruleset_cleanup(struct pf_ruleset *, int);
187static void pf_deleterule_anchor_step_out(struct pf_ruleset **,
188 int, struct pf_rule **);
b0d623f7 189
0a7de745 190#define PF_CDEV_MAJOR (-1)
b0d623f7
A
191
192static struct cdevsw pf_cdevsw = {
cb323159
A
193 .d_open = pfopen,
194 .d_close = pfclose,
195 .d_read = eno_rdwrt,
196 .d_write = eno_rdwrt,
197 .d_ioctl = pfioctl,
198 .d_stop = eno_stop,
199 .d_reset = eno_reset,
200 .d_ttys = NULL,
201 .d_select = eno_select,
202 .d_mmap = eno_mmap,
203 .d_strategy = eno_strat,
204 .d_reserved_1 = eno_getc,
205 .d_reserved_2 = eno_putc,
206 .d_type = 0
b0d623f7
A
207};
208
209static void pf_attach_hooks(void);
d1ecb069
A
210#if 0
211/* currently unused along with pfdetach() */
b0d623f7 212static void pf_detach_hooks(void);
d1ecb069
A
213#endif
214
215/*
216 * This is set during DIOCSTART/DIOCSTOP with pf_perim_lock held as writer,
217 * and used in pf_af_hook() for performance optimization, such that packets
218 * will enter pf_test() or pf_test6() only when PF is running.
219 */
6d2010ae
A
220int pf_is_enabled = 0;
221
316670eb 222u_int32_t pf_hash_seed;
3e170ce0 223int16_t pf_nat64_configured = 0;
316670eb 224
6d2010ae
A
225/*
226 * These are the pf enabled reference counting variables
227 */
cb323159
A
228#define NR_TOKENS_LIMIT (INT_MAX / sizeof(struct pfioc_token))
229
6d2010ae
A
230static u_int64_t pf_enabled_ref_count;
231static u_int32_t nr_tokens = 0;
316670eb
A
232static u_int64_t pffwrules;
233static u_int32_t pfdevcnt;
6d2010ae
A
234
235SLIST_HEAD(list_head, pfioc_kernel_token);
236static struct list_head token_list_head;
b0d623f7 237
0a7de745 238struct pf_rule pf_default_rule;
b0d623f7 239
0a7de745
A
240#define TAGID_MAX 50000
241static TAILQ_HEAD(pf_tags, pf_tagname) pf_tags =
b0d623f7 242 TAILQ_HEAD_INITIALIZER(pf_tags);
b0d623f7
A
243
244#if (PF_QNAME_SIZE != PF_TAG_NAME_SIZE)
245#error PF_QNAME_SIZE must be equal to PF_TAG_NAME_SIZE
246#endif
0a7de745
A
247static u_int16_t tagname2tag(struct pf_tags *, char *);
248static void tag2tagname(struct pf_tags *, u_int16_t, char *);
249static void tag_unref(struct pf_tags *, u_int16_t);
250static int pf_rtlabel_add(struct pf_addr_wrap *);
251static void pf_rtlabel_remove(struct pf_addr_wrap *);
252static void pf_rtlabel_copyout(struct pf_addr_wrap *);
b0d623f7
A
253
254#if INET
316670eb
A
255static int pf_inet_hook(struct ifnet *, struct mbuf **, int,
256 struct ip_fw_args *);
b0d623f7
A
257#endif /* INET */
258#if INET6
316670eb
A
259static int pf_inet6_hook(struct ifnet *, struct mbuf **, int,
260 struct ip_fw_args *);
b0d623f7
A
261#endif /* INET6 */
262
0a7de745 263#define DPFPRINTF(n, x) if (pf_status.debug >= (n)) printf x
316670eb
A
264
265/*
266 * Helper macros for ioctl structures which vary in size (32-bit vs. 64-bit)
267 */
0a7de745
A
268#define PFIOCX_STRUCT_DECL(s) \
269struct { \
270 union { \
271 struct s##_32 _s##_32; \
272 struct s##_64 _s##_64; \
273 } _u; \
274} *s##_un = NULL \
275
276#define PFIOCX_STRUCT_BEGIN(a, s, _action) { \
277 VERIFY(s##_un == NULL); \
278 s##_un = _MALLOC(sizeof (*s##_un), M_TEMP, M_WAITOK|M_ZERO); \
279 if (s##_un == NULL) { \
280 _action \
281 } else { \
282 if (p64) \
283 bcopy(a, &s##_un->_u._s##_64, \
284 sizeof (struct s##_64)); \
285 else \
286 bcopy(a, &s##_un->_u._s##_32, \
287 sizeof (struct s##_32)); \
288 } \
316670eb
A
289}
290
0a7de745
A
291#define PFIOCX_STRUCT_END(s, a) { \
292 VERIFY(s##_un != NULL); \
293 if (p64) \
294 bcopy(&s##_un->_u._s##_64, a, sizeof (struct s##_64)); \
295 else \
296 bcopy(&s##_un->_u._s##_32, a, sizeof (struct s##_32)); \
297 _FREE(s##_un, M_TEMP); \
298 s##_un = NULL; \
316670eb
A
299}
300
0a7de745
A
301#define PFIOCX_STRUCT_ADDR32(s) (&s##_un->_u._s##_32)
302#define PFIOCX_STRUCT_ADDR64(s) (&s##_un->_u._s##_64)
316670eb
A
303
304/*
305 * Helper macros for regular ioctl structures.
306 */
0a7de745
A
307#define PFIOC_STRUCT_BEGIN(a, v, _action) { \
308 VERIFY((v) == NULL); \
309 (v) = _MALLOC(sizeof (*(v)), M_TEMP, M_WAITOK|M_ZERO); \
310 if ((v) == NULL) { \
311 _action \
312 } else { \
313 bcopy(a, v, sizeof (*(v))); \
314 } \
316670eb
A
315}
316
0a7de745
A
317#define PFIOC_STRUCT_END(v, a) { \
318 VERIFY((v) != NULL); \
319 bcopy(v, a, sizeof (*(v))); \
320 _FREE(v, M_TEMP); \
321 (v) = NULL; \
316670eb 322}
b0d623f7 323
0a7de745
A
324#define PFIOC_STRUCT_ADDR32(s) (&s##_un->_u._s##_32)
325#define PFIOC_STRUCT_ADDR64(s) (&s##_un->_u._s##_64)
d1ecb069 326
b0d623f7
A
327static lck_attr_t *pf_perim_lock_attr;
328static lck_grp_t *pf_perim_lock_grp;
329static lck_grp_attr_t *pf_perim_lock_grp_attr;
330
331static lck_attr_t *pf_lock_attr;
332static lck_grp_t *pf_lock_grp;
333static lck_grp_attr_t *pf_lock_grp_attr;
334
335struct thread *pf_purge_thread;
336
337extern void pfi_kifaddr_update(void *);
338
6d2010ae 339/* pf enable ref-counting helper functions */
0a7de745
A
340static u_int64_t generate_token(struct proc *);
341static int remove_token(struct pfioc_remove_token *);
342static void invalidate_all_tokens(void);
6d2010ae
A
343
344static u_int64_t
316670eb 345generate_token(struct proc *p)
6d2010ae
A
346{
347 u_int64_t token_value;
348 struct pfioc_kernel_token *new_token;
349
cb323159
A
350 if (nr_tokens + 1 > NR_TOKENS_LIMIT) {
351 os_log_error(OS_LOG_DEFAULT, "%s: NR_TOKENS_LIMIT reached", __func__);
352 return 0;
353 }
354
0a7de745
A
355 new_token = _MALLOC(sizeof(struct pfioc_kernel_token), M_TEMP,
356 M_WAITOK | M_ZERO);
6d2010ae 357
5ba3f43e 358 LCK_MTX_ASSERT(pf_lock, LCK_MTX_ASSERT_OWNED);
6d2010ae
A
359
360 if (new_token == NULL) {
361 /* malloc failed! bail! */
cb323159 362 os_log_error(OS_LOG_DEFAULT, "%s: unable to allocate pf token structure!", __func__);
0a7de745 363 return 0;
6d2010ae
A
364 }
365
39236c6e 366 token_value = VM_KERNEL_ADDRPERM((u_int64_t)(uintptr_t)new_token);
6d2010ae
A
367
368 new_token->token.token_value = token_value;
316670eb 369 new_token->token.pid = proc_pid(p);
6d2010ae 370 proc_name(new_token->token.pid, new_token->token.proc_name,
0a7de745 371 sizeof(new_token->token.proc_name));
6d2010ae
A
372 new_token->token.timestamp = pf_calendar_time_second();
373
374 SLIST_INSERT_HEAD(&token_list_head, new_token, next);
375 nr_tokens++;
376
0a7de745 377 return token_value;
6d2010ae
A
378}
379
380static int
381remove_token(struct pfioc_remove_token *tok)
382{
383 struct pfioc_kernel_token *entry, *tmp;
384
5ba3f43e 385 LCK_MTX_ASSERT(pf_lock, LCK_MTX_ASSERT_OWNED);
6d2010ae
A
386
387 SLIST_FOREACH_SAFE(entry, &token_list_head, next, tmp) {
388 if (tok->token_value == entry->token.token_value) {
316670eb
A
389 SLIST_REMOVE(&token_list_head, entry,
390 pfioc_kernel_token, next);
6d2010ae
A
391 _FREE(entry, M_TEMP);
392 nr_tokens--;
0a7de745 393 return 0; /* success */
6d2010ae
A
394 }
395 }
396
397 printf("pf : remove failure\n");
0a7de745 398 return ESRCH; /* failure */
6d2010ae
A
399}
400
401static void
402invalidate_all_tokens(void)
403{
404 struct pfioc_kernel_token *entry, *tmp;
405
5ba3f43e 406 LCK_MTX_ASSERT(pf_lock, LCK_MTX_ASSERT_OWNED);
6d2010ae
A
407
408 SLIST_FOREACH_SAFE(entry, &token_list_head, next, tmp) {
409 SLIST_REMOVE(&token_list_head, entry, pfioc_kernel_token, next);
410 _FREE(entry, M_TEMP);
411 }
412
413 nr_tokens = 0;
6d2010ae
A
414}
415
b0d623f7
A
416void
417pfinit(void)
418{
419 u_int32_t *t = pf_default_rule.timeout;
420 int maj;
421
422 pf_perim_lock_grp_attr = lck_grp_attr_alloc_init();
423 pf_perim_lock_grp = lck_grp_alloc_init("pf_perim",
424 pf_perim_lock_grp_attr);
425 pf_perim_lock_attr = lck_attr_alloc_init();
316670eb 426 lck_rw_init(pf_perim_lock, pf_perim_lock_grp, pf_perim_lock_attr);
b0d623f7
A
427
428 pf_lock_grp_attr = lck_grp_attr_alloc_init();
429 pf_lock_grp = lck_grp_alloc_init("pf", pf_lock_grp_attr);
430 pf_lock_attr = lck_attr_alloc_init();
316670eb 431 lck_mtx_init(pf_lock, pf_lock_grp, pf_lock_attr);
b0d623f7 432
0a7de745 433 pool_init(&pf_rule_pl, sizeof(struct pf_rule), 0, 0, 0, "pfrulepl",
b0d623f7 434 NULL);
0a7de745 435 pool_init(&pf_src_tree_pl, sizeof(struct pf_src_node), 0, 0, 0,
b0d623f7 436 "pfsrctrpl", NULL);
0a7de745 437 pool_init(&pf_state_pl, sizeof(struct pf_state), 0, 0, 0, "pfstatepl",
b0d623f7 438 NULL);
0a7de745 439 pool_init(&pf_state_key_pl, sizeof(struct pf_state_key), 0, 0, 0,
b0d623f7 440 "pfstatekeypl", NULL);
0a7de745 441 pool_init(&pf_app_state_pl, sizeof(struct pf_app_state), 0, 0, 0,
b0d623f7 442 "pfappstatepl", NULL);
0a7de745 443 pool_init(&pf_pooladdr_pl, sizeof(struct pf_pooladdr), 0, 0, 0,
b0d623f7
A
444 "pfpooladdrpl", NULL);
445 pfr_initialize();
446 pfi_initialize();
447 pf_osfp_initialize();
448
449 pool_sethardlimit(pf_pool_limits[PF_LIMIT_STATES].pp,
450 pf_pool_limits[PF_LIMIT_STATES].limit, NULL, 0);
451
0a7de745 452 if (max_mem <= 256 * 1024 * 1024) {
b0d623f7
A
453 pf_pool_limits[PF_LIMIT_TABLE_ENTRIES].limit =
454 PFR_KENTRY_HIWAT_SMALL;
0a7de745 455 }
b0d623f7
A
456
457 RB_INIT(&tree_src_tracking);
458 RB_INIT(&pf_anchors);
459 pf_init_ruleset(&pf_main_ruleset);
460 TAILQ_INIT(&pf_pabuf);
461 TAILQ_INIT(&state_list);
316670eb
A
462
463 _CASSERT((SC_BE & SCIDX_MASK) == SCIDX_BE);
464 _CASSERT((SC_BK_SYS & SCIDX_MASK) == SCIDX_BK_SYS);
465 _CASSERT((SC_BK & SCIDX_MASK) == SCIDX_BK);
466 _CASSERT((SC_RD & SCIDX_MASK) == SCIDX_RD);
467 _CASSERT((SC_OAM & SCIDX_MASK) == SCIDX_OAM);
468 _CASSERT((SC_AV & SCIDX_MASK) == SCIDX_AV);
469 _CASSERT((SC_RV & SCIDX_MASK) == SCIDX_RV);
470 _CASSERT((SC_VI & SCIDX_MASK) == SCIDX_VI);
d9a64523 471 _CASSERT((SC_SIG & SCIDX_MASK) == SCIDX_SIG);
316670eb
A
472 _CASSERT((SC_VO & SCIDX_MASK) == SCIDX_VO);
473 _CASSERT((SC_CTL & SCIDX_MASK) == SCIDX_CTL);
b0d623f7
A
474
475 /* default rule should never be garbage collected */
476 pf_default_rule.entries.tqe_prev = &pf_default_rule.entries.tqe_next;
477 pf_default_rule.action = PF_PASS;
478 pf_default_rule.nr = -1;
479 pf_default_rule.rtableid = IFSCOPE_NONE;
480
481 /* initialize default timeouts */
482 t[PFTM_TCP_FIRST_PACKET] = PFTM_TCP_FIRST_PACKET_VAL;
483 t[PFTM_TCP_OPENING] = PFTM_TCP_OPENING_VAL;
484 t[PFTM_TCP_ESTABLISHED] = PFTM_TCP_ESTABLISHED_VAL;
485 t[PFTM_TCP_CLOSING] = PFTM_TCP_CLOSING_VAL;
486 t[PFTM_TCP_FIN_WAIT] = PFTM_TCP_FIN_WAIT_VAL;
487 t[PFTM_TCP_CLOSED] = PFTM_TCP_CLOSED_VAL;
488 t[PFTM_UDP_FIRST_PACKET] = PFTM_UDP_FIRST_PACKET_VAL;
489 t[PFTM_UDP_SINGLE] = PFTM_UDP_SINGLE_VAL;
490 t[PFTM_UDP_MULTIPLE] = PFTM_UDP_MULTIPLE_VAL;
491 t[PFTM_ICMP_FIRST_PACKET] = PFTM_ICMP_FIRST_PACKET_VAL;
492 t[PFTM_ICMP_ERROR_REPLY] = PFTM_ICMP_ERROR_REPLY_VAL;
b0d623f7
A
493 t[PFTM_GREv1_FIRST_PACKET] = PFTM_GREv1_FIRST_PACKET_VAL;
494 t[PFTM_GREv1_INITIATING] = PFTM_GREv1_INITIATING_VAL;
495 t[PFTM_GREv1_ESTABLISHED] = PFTM_GREv1_ESTABLISHED_VAL;
496 t[PFTM_ESP_FIRST_PACKET] = PFTM_ESP_FIRST_PACKET_VAL;
497 t[PFTM_ESP_INITIATING] = PFTM_ESP_INITIATING_VAL;
498 t[PFTM_ESP_ESTABLISHED] = PFTM_ESP_ESTABLISHED_VAL;
b0d623f7
A
499 t[PFTM_OTHER_FIRST_PACKET] = PFTM_OTHER_FIRST_PACKET_VAL;
500 t[PFTM_OTHER_SINGLE] = PFTM_OTHER_SINGLE_VAL;
501 t[PFTM_OTHER_MULTIPLE] = PFTM_OTHER_MULTIPLE_VAL;
502 t[PFTM_FRAG] = PFTM_FRAG_VAL;
503 t[PFTM_INTERVAL] = PFTM_INTERVAL_VAL;
504 t[PFTM_SRC_NODE] = PFTM_SRC_NODE_VAL;
505 t[PFTM_TS_DIFF] = PFTM_TS_DIFF_VAL;
506 t[PFTM_ADAPTIVE_START] = PFSTATE_ADAPT_START;
507 t[PFTM_ADAPTIVE_END] = PFSTATE_ADAPT_END;
508
509 pf_normalize_init();
0a7de745 510 bzero(&pf_status, sizeof(pf_status));
b0d623f7 511 pf_status.debug = PF_DEBUG_URGENT;
39236c6e 512 pf_hash_seed = RandomULong();
b0d623f7
A
513
514 /* XXX do our best to avoid a conflict */
515 pf_status.hostid = random();
516
517 if (kernel_thread_start(pf_purge_thread_fn, NULL,
518 &pf_purge_thread) != 0) {
519 printf("%s: unable to start purge thread!", __func__);
520 return;
521 }
522
523 maj = cdevsw_add(PF_CDEV_MAJOR, &pf_cdevsw);
524 if (maj == -1) {
525 printf("%s: failed to allocate major number!\n", __func__);
526 return;
527 }
316670eb 528 (void) devfs_make_node(makedev(maj, PFDEV_PF), DEVFS_CHAR,
b0d623f7 529 UID_ROOT, GID_WHEEL, 0600, "pf", 0);
d1ecb069 530
316670eb
A
531 (void) devfs_make_node(makedev(maj, PFDEV_PFM), DEVFS_CHAR,
532 UID_ROOT, GID_WHEEL, 0600, "pfm", 0);
533
d1ecb069 534 pf_attach_hooks();
5ba3f43e
A
535#if DUMMYNET
536 dummynet_init();
537#endif
b0d623f7
A
538}
539
540#if 0
541static void
542pfdetach(void)
543{
0a7de745
A
544 struct pf_anchor *anchor;
545 struct pf_state *state;
546 struct pf_src_node *node;
547 struct pfioc_table pt;
548 u_int32_t ticket;
549 int i;
550 char r = '\0';
b0d623f7 551
d1ecb069
A
552 pf_detach_hooks();
553
b0d623f7
A
554 pf_status.running = 0;
555 wakeup(pf_purge_thread_fn);
556
557 /* clear the rulesets */
0a7de745
A
558 for (i = 0; i < PF_RULESET_MAX; i++) {
559 if (pf_begin_rules(&ticket, i, &r) == 0) {
560 pf_commit_rules(ticket, i, &r);
561 }
562 }
b0d623f7
A
563
564 /* clear states */
565 RB_FOREACH(state, pf_state_tree_id, &tree_id) {
566 state->timeout = PFTM_PURGE;
567#if NPFSYNC
568 state->sync_flags = PFSTATE_NOSYNC;
569#endif
570 }
571 pf_purge_expired_states(pf_status.states);
572
573#if NPFSYNC
574 pfsync_clear_states(pf_status.hostid, NULL);
575#endif
576
577 /* clear source nodes */
578 RB_FOREACH(state, pf_state_tree_id, &tree_id) {
579 state->src_node = NULL;
580 state->nat_src_node = NULL;
581 }
582 RB_FOREACH(node, pf_src_tree, &tree_src_tracking) {
583 node->expire = 1;
584 node->states = 0;
585 }
586 pf_purge_expired_src_nodes();
587
588 /* clear tables */
0a7de745 589 memset(&pt, '\0', sizeof(pt));
b0d623f7
A
590 pfr_clr_tables(&pt.pfrio_table, &pt.pfrio_ndel, pt.pfrio_flags);
591
592 /* destroy anchors */
593 while ((anchor = RB_MIN(pf_anchor_global, &pf_anchors)) != NULL) {
0a7de745
A
594 for (i = 0; i < PF_RULESET_MAX; i++) {
595 if (pf_begin_rules(&ticket, i, anchor->name) == 0) {
b0d623f7 596 pf_commit_rules(ticket, i, anchor->name);
0a7de745
A
597 }
598 }
b0d623f7
A
599 }
600
601 /* destroy main ruleset */
602 pf_remove_if_empty_ruleset(&pf_main_ruleset);
603
604 /* destroy the pools */
605 pool_destroy(&pf_pooladdr_pl);
b0d623f7
A
606 pool_destroy(&pf_state_pl);
607 pool_destroy(&pf_rule_pl);
608 pool_destroy(&pf_src_tree_pl);
609
610 /* destroy subsystems */
611 pf_normalize_destroy();
612 pf_osfp_destroy();
613 pfr_destroy();
614 pfi_destroy();
615}
616#endif
617
618static int
619pfopen(dev_t dev, int flags, int fmt, struct proc *p)
620{
621#pragma unused(flags, fmt, p)
0a7de745
A
622 if (minor(dev) >= PFDEV_MAX) {
623 return ENXIO;
624 }
316670eb
A
625
626 if (minor(dev) == PFDEV_PFM) {
627 lck_mtx_lock(pf_lock);
628 if (pfdevcnt != 0) {
629 lck_mtx_unlock(pf_lock);
0a7de745 630 return EBUSY;
316670eb
A
631 }
632 pfdevcnt++;
633 lck_mtx_unlock(pf_lock);
634 }
0a7de745 635 return 0;
b0d623f7
A
636}
637
638static int
639pfclose(dev_t dev, int flags, int fmt, struct proc *p)
640{
641#pragma unused(flags, fmt, p)
0a7de745
A
642 if (minor(dev) >= PFDEV_MAX) {
643 return ENXIO;
644 }
316670eb
A
645
646 if (minor(dev) == PFDEV_PFM) {
647 lck_mtx_lock(pf_lock);
648 VERIFY(pfdevcnt > 0);
649 pfdevcnt--;
650 lck_mtx_unlock(pf_lock);
651 }
0a7de745 652 return 0;
b0d623f7
A
653}
654
655static struct pf_pool *
656pf_get_pool(char *anchor, u_int32_t ticket, u_int8_t rule_action,
657 u_int32_t rule_number, u_int8_t r_last, u_int8_t active,
658 u_int8_t check_ticket)
659{
0a7de745
A
660 struct pf_ruleset *ruleset;
661 struct pf_rule *rule;
662 int rs_num;
b0d623f7
A
663
664 ruleset = pf_find_ruleset(anchor);
0a7de745
A
665 if (ruleset == NULL) {
666 return NULL;
667 }
b0d623f7 668 rs_num = pf_get_ruleset_number(rule_action);
0a7de745
A
669 if (rs_num >= PF_RULESET_MAX) {
670 return NULL;
671 }
b0d623f7
A
672 if (active) {
673 if (check_ticket && ticket !=
0a7de745
A
674 ruleset->rules[rs_num].active.ticket) {
675 return NULL;
676 }
677 if (r_last) {
b0d623f7
A
678 rule = TAILQ_LAST(ruleset->rules[rs_num].active.ptr,
679 pf_rulequeue);
0a7de745 680 } else {
b0d623f7 681 rule = TAILQ_FIRST(ruleset->rules[rs_num].active.ptr);
0a7de745 682 }
b0d623f7
A
683 } else {
684 if (check_ticket && ticket !=
0a7de745
A
685 ruleset->rules[rs_num].inactive.ticket) {
686 return NULL;
687 }
688 if (r_last) {
b0d623f7
A
689 rule = TAILQ_LAST(ruleset->rules[rs_num].inactive.ptr,
690 pf_rulequeue);
0a7de745 691 } else {
b0d623f7 692 rule = TAILQ_FIRST(ruleset->rules[rs_num].inactive.ptr);
0a7de745 693 }
b0d623f7
A
694 }
695 if (!r_last) {
0a7de745 696 while ((rule != NULL) && (rule->nr != rule_number)) {
b0d623f7 697 rule = TAILQ_NEXT(rule, entries);
0a7de745
A
698 }
699 }
700 if (rule == NULL) {
701 return NULL;
b0d623f7 702 }
b0d623f7 703
0a7de745 704 return &rule->rpool;
b0d623f7
A
705}
706
707static void
708pf_mv_pool(struct pf_palist *poola, struct pf_palist *poolb)
709{
0a7de745 710 struct pf_pooladdr *mv_pool_pa;
b0d623f7
A
711
712 while ((mv_pool_pa = TAILQ_FIRST(poola)) != NULL) {
713 TAILQ_REMOVE(poola, mv_pool_pa, entries);
714 TAILQ_INSERT_TAIL(poolb, mv_pool_pa, entries);
715 }
716}
717
718static void
719pf_empty_pool(struct pf_palist *poola)
720{
0a7de745 721 struct pf_pooladdr *empty_pool_pa;
b0d623f7
A
722
723 while ((empty_pool_pa = TAILQ_FIRST(poola)) != NULL) {
724 pfi_dynaddr_remove(&empty_pool_pa->addr);
725 pf_tbladdr_remove(&empty_pool_pa->addr);
726 pfi_kif_unref(empty_pool_pa->kif, PFI_KIF_REF_RULE);
727 TAILQ_REMOVE(poola, empty_pool_pa, entries);
728 pool_put(&pf_pooladdr_pl, empty_pool_pa);
729 }
730}
731
732void
733pf_rm_rule(struct pf_rulequeue *rulequeue, struct pf_rule *rule)
734{
735 if (rulequeue != NULL) {
736 if (rule->states <= 0) {
737 /*
738 * XXX - we need to remove the table *before* detaching
739 * the rule to make sure the table code does not delete
740 * the anchor under our feet.
741 */
742 pf_tbladdr_remove(&rule->src.addr);
743 pf_tbladdr_remove(&rule->dst.addr);
0a7de745 744 if (rule->overload_tbl) {
b0d623f7 745 pfr_detach_table(rule->overload_tbl);
0a7de745 746 }
b0d623f7
A
747 }
748 TAILQ_REMOVE(rulequeue, rule, entries);
749 rule->entries.tqe_prev = NULL;
750 rule->nr = -1;
751 }
752
753 if (rule->states > 0 || rule->src_nodes > 0 ||
0a7de745 754 rule->entries.tqe_prev != NULL) {
b0d623f7 755 return;
0a7de745 756 }
b0d623f7
A
757 pf_tag_unref(rule->tag);
758 pf_tag_unref(rule->match_tag);
b0d623f7
A
759 pf_rtlabel_remove(&rule->src.addr);
760 pf_rtlabel_remove(&rule->dst.addr);
761 pfi_dynaddr_remove(&rule->src.addr);
762 pfi_dynaddr_remove(&rule->dst.addr);
763 if (rulequeue == NULL) {
764 pf_tbladdr_remove(&rule->src.addr);
765 pf_tbladdr_remove(&rule->dst.addr);
0a7de745 766 if (rule->overload_tbl) {
b0d623f7 767 pfr_detach_table(rule->overload_tbl);
0a7de745 768 }
b0d623f7
A
769 }
770 pfi_kif_unref(rule->kif, PFI_KIF_REF_RULE);
771 pf_anchor_remove(rule);
772 pf_empty_pool(&rule->rpool.list);
773 pool_put(&pf_rule_pl, rule);
774}
775
776static u_int16_t
777tagname2tag(struct pf_tags *head, char *tagname)
778{
0a7de745
A
779 struct pf_tagname *tag, *p = NULL;
780 u_int16_t new_tagid = 1;
b0d623f7
A
781
782 TAILQ_FOREACH(tag, head, entries)
0a7de745
A
783 if (strcmp(tagname, tag->name) == 0) {
784 tag->ref++;
785 return tag->tag;
786 }
b0d623f7
A
787
788 /*
789 * to avoid fragmentation, we do a linear search from the beginning
790 * and take the first free slot we find. if there is none or the list
791 * is empty, append a new entry at the end.
792 */
793
794 /* new entry */
0a7de745 795 if (!TAILQ_EMPTY(head)) {
b0d623f7 796 for (p = TAILQ_FIRST(head); p != NULL &&
0a7de745 797 p->tag == new_tagid; p = TAILQ_NEXT(p, entries)) {
b0d623f7 798 new_tagid = p->tag + 1;
0a7de745
A
799 }
800 }
b0d623f7 801
0a7de745
A
802 if (new_tagid > TAGID_MAX) {
803 return 0;
804 }
b0d623f7
A
805
806 /* allocate and fill new struct pf_tagname */
0a7de745
A
807 tag = _MALLOC(sizeof(*tag), M_TEMP, M_WAITOK | M_ZERO);
808 if (tag == NULL) {
809 return 0;
810 }
811 strlcpy(tag->name, tagname, sizeof(tag->name));
b0d623f7
A
812 tag->tag = new_tagid;
813 tag->ref++;
814
0a7de745 815 if (p != NULL) { /* insert new entry before p */
b0d623f7 816 TAILQ_INSERT_BEFORE(p, tag, entries);
0a7de745 817 } else { /* either list empty or no free slot in between */
b0d623f7 818 TAILQ_INSERT_TAIL(head, tag, entries);
0a7de745 819 }
b0d623f7 820
0a7de745 821 return tag->tag;
b0d623f7
A
822}
823
824static void
825tag2tagname(struct pf_tags *head, u_int16_t tagid, char *p)
826{
0a7de745 827 struct pf_tagname *tag;
b0d623f7
A
828
829 TAILQ_FOREACH(tag, head, entries)
0a7de745
A
830 if (tag->tag == tagid) {
831 strlcpy(p, tag->name, PF_TAG_NAME_SIZE);
832 return;
833 }
b0d623f7
A
834}
835
836static void
837tag_unref(struct pf_tags *head, u_int16_t tag)
838{
0a7de745 839 struct pf_tagname *p, *next;
b0d623f7 840
0a7de745 841 if (tag == 0) {
b0d623f7 842 return;
0a7de745 843 }
b0d623f7
A
844
845 for (p = TAILQ_FIRST(head); p != NULL; p = next) {
846 next = TAILQ_NEXT(p, entries);
847 if (tag == p->tag) {
848 if (--p->ref == 0) {
849 TAILQ_REMOVE(head, p, entries);
850 _FREE(p, M_TEMP);
851 }
852 break;
853 }
854 }
855}
856
857u_int16_t
858pf_tagname2tag(char *tagname)
859{
0a7de745 860 return tagname2tag(&pf_tags, tagname);
b0d623f7
A
861}
862
863void
864pf_tag2tagname(u_int16_t tagid, char *p)
865{
866 tag2tagname(&pf_tags, tagid, p);
867}
868
869void
870pf_tag_ref(u_int16_t tag)
871{
872 struct pf_tagname *t;
873
874 TAILQ_FOREACH(t, &pf_tags, entries)
0a7de745
A
875 if (t->tag == tag) {
876 break;
877 }
878 if (t != NULL) {
b0d623f7 879 t->ref++;
0a7de745 880 }
b0d623f7
A
881}
882
883void
884pf_tag_unref(u_int16_t tag)
885{
886 tag_unref(&pf_tags, tag);
887}
888
889static int
890pf_rtlabel_add(struct pf_addr_wrap *a)
891{
892#pragma unused(a)
0a7de745 893 return 0;
b0d623f7
A
894}
895
896static void
897pf_rtlabel_remove(struct pf_addr_wrap *a)
898{
899#pragma unused(a)
900}
901
902static void
903pf_rtlabel_copyout(struct pf_addr_wrap *a)
904{
905#pragma unused(a)
906}
907
b0d623f7
A
908static int
909pf_begin_rules(u_int32_t *ticket, int rs_num, const char *anchor)
910{
0a7de745
A
911 struct pf_ruleset *rs;
912 struct pf_rule *rule;
b0d623f7 913
0a7de745
A
914 if (rs_num < 0 || rs_num >= PF_RULESET_MAX) {
915 return EINVAL;
916 }
b0d623f7 917 rs = pf_find_or_create_ruleset(anchor);
0a7de745
A
918 if (rs == NULL) {
919 return EINVAL;
920 }
b0d623f7
A
921 while ((rule = TAILQ_FIRST(rs->rules[rs_num].inactive.ptr)) != NULL) {
922 pf_rm_rule(rs->rules[rs_num].inactive.ptr, rule);
923 rs->rules[rs_num].inactive.rcount--;
924 }
925 *ticket = ++rs->rules[rs_num].inactive.ticket;
926 rs->rules[rs_num].inactive.open = 1;
0a7de745 927 return 0;
b0d623f7
A
928}
929
930static int
931pf_rollback_rules(u_int32_t ticket, int rs_num, char *anchor)
932{
0a7de745
A
933 struct pf_ruleset *rs;
934 struct pf_rule *rule;
b0d623f7 935
0a7de745
A
936 if (rs_num < 0 || rs_num >= PF_RULESET_MAX) {
937 return EINVAL;
938 }
b0d623f7
A
939 rs = pf_find_ruleset(anchor);
940 if (rs == NULL || !rs->rules[rs_num].inactive.open ||
0a7de745
A
941 rs->rules[rs_num].inactive.ticket != ticket) {
942 return 0;
943 }
b0d623f7
A
944 while ((rule = TAILQ_FIRST(rs->rules[rs_num].inactive.ptr)) != NULL) {
945 pf_rm_rule(rs->rules[rs_num].inactive.ptr, rule);
946 rs->rules[rs_num].inactive.rcount--;
947 }
948 rs->rules[rs_num].inactive.open = 0;
0a7de745 949 return 0;
b0d623f7
A
950}
951
0a7de745 952#define PF_MD5_UPD(st, elm) \
b0d623f7
A
953 MD5Update(ctx, (u_int8_t *)&(st)->elm, sizeof ((st)->elm))
954
0a7de745 955#define PF_MD5_UPD_STR(st, elm) \
b0d623f7
A
956 MD5Update(ctx, (u_int8_t *)(st)->elm, strlen((st)->elm))
957
0a7de745
A
958#define PF_MD5_UPD_HTONL(st, elm, stor) do { \
959 (stor) = htonl((st)->elm); \
960 MD5Update(ctx, (u_int8_t *)&(stor), sizeof (u_int32_t)); \
b0d623f7
A
961} while (0)
962
0a7de745
A
963#define PF_MD5_UPD_HTONS(st, elm, stor) do { \
964 (stor) = htons((st)->elm); \
965 MD5Update(ctx, (u_int8_t *)&(stor), sizeof (u_int16_t)); \
b0d623f7
A
966} while (0)
967
b0d623f7
A
968static void
969pf_hash_rule_addr(MD5_CTX *ctx, struct pf_rule_addr *pfr, u_int8_t proto)
b0d623f7
A
970{
971 PF_MD5_UPD(pfr, addr.type);
972 switch (pfr->addr.type) {
973 case PF_ADDR_DYNIFTL:
974 PF_MD5_UPD(pfr, addr.v.ifname);
975 PF_MD5_UPD(pfr, addr.iflags);
976 break;
977 case PF_ADDR_TABLE:
978 PF_MD5_UPD(pfr, addr.v.tblname);
979 break;
980 case PF_ADDR_ADDRMASK:
981 /* XXX ignore af? */
982 PF_MD5_UPD(pfr, addr.v.a.addr.addr32);
983 PF_MD5_UPD(pfr, addr.v.a.mask.addr32);
984 break;
985 case PF_ADDR_RTLABEL:
986 PF_MD5_UPD(pfr, addr.v.rtlabelname);
987 break;
988 }
989
b0d623f7
A
990 switch (proto) {
991 case IPPROTO_TCP:
992 case IPPROTO_UDP:
993 PF_MD5_UPD(pfr, xport.range.port[0]);
994 PF_MD5_UPD(pfr, xport.range.port[1]);
995 PF_MD5_UPD(pfr, xport.range.op);
6d2010ae 996 break;
b0d623f7
A
997
998 default:
999 break;
1000 }
1001
1002 PF_MD5_UPD(pfr, neg);
b0d623f7
A
1003}
1004
1005static void
1006pf_hash_rule(MD5_CTX *ctx, struct pf_rule *rule)
1007{
1008 u_int16_t x;
1009 u_int32_t y;
1010
b0d623f7
A
1011 pf_hash_rule_addr(ctx, &rule->src, rule->proto);
1012 pf_hash_rule_addr(ctx, &rule->dst, rule->proto);
b0d623f7
A
1013 PF_MD5_UPD_STR(rule, label);
1014 PF_MD5_UPD_STR(rule, ifname);
1015 PF_MD5_UPD_STR(rule, match_tagname);
1016 PF_MD5_UPD_HTONS(rule, match_tag, x); /* dup? */
1017 PF_MD5_UPD_HTONL(rule, os_fingerprint, y);
1018 PF_MD5_UPD_HTONL(rule, prob, y);
1019 PF_MD5_UPD_HTONL(rule, uid.uid[0], y);
1020 PF_MD5_UPD_HTONL(rule, uid.uid[1], y);
1021 PF_MD5_UPD(rule, uid.op);
1022 PF_MD5_UPD_HTONL(rule, gid.gid[0], y);
1023 PF_MD5_UPD_HTONL(rule, gid.gid[1], y);
1024 PF_MD5_UPD(rule, gid.op);
1025 PF_MD5_UPD_HTONL(rule, rule_flag, y);
1026 PF_MD5_UPD(rule, action);
1027 PF_MD5_UPD(rule, direction);
1028 PF_MD5_UPD(rule, af);
1029 PF_MD5_UPD(rule, quick);
1030 PF_MD5_UPD(rule, ifnot);
1031 PF_MD5_UPD(rule, match_tag_not);
1032 PF_MD5_UPD(rule, natpass);
1033 PF_MD5_UPD(rule, keep_state);
1034 PF_MD5_UPD(rule, proto);
1035 PF_MD5_UPD(rule, type);
1036 PF_MD5_UPD(rule, code);
1037 PF_MD5_UPD(rule, flags);
1038 PF_MD5_UPD(rule, flagset);
1039 PF_MD5_UPD(rule, allow_opts);
1040 PF_MD5_UPD(rule, rt);
1041 PF_MD5_UPD(rule, tos);
1042}
1043
1044static int
1045pf_commit_rules(u_int32_t ticket, int rs_num, char *anchor)
1046{
0a7de745
A
1047 struct pf_ruleset *rs;
1048 struct pf_rule *rule, **old_array, *r;
1049 struct pf_rulequeue *old_rules;
1050 int error;
1051 u_int32_t old_rcount;
b0d623f7 1052
5ba3f43e 1053 LCK_MTX_ASSERT(pf_lock, LCK_MTX_ASSERT_OWNED);
b0d623f7 1054
0a7de745
A
1055 if (rs_num < 0 || rs_num >= PF_RULESET_MAX) {
1056 return EINVAL;
1057 }
b0d623f7
A
1058 rs = pf_find_ruleset(anchor);
1059 if (rs == NULL || !rs->rules[rs_num].inactive.open ||
0a7de745
A
1060 ticket != rs->rules[rs_num].inactive.ticket) {
1061 return EBUSY;
1062 }
b0d623f7
A
1063
1064 /* Calculate checksum for the main ruleset */
1065 if (rs == &pf_main_ruleset) {
1066 error = pf_setup_pfsync_matching(rs);
0a7de745
A
1067 if (error != 0) {
1068 return error;
1069 }
b0d623f7
A
1070 }
1071
1072 /* Swap rules, keep the old. */
1073 old_rules = rs->rules[rs_num].active.ptr;
1074 old_rcount = rs->rules[rs_num].active.rcount;
1075 old_array = rs->rules[rs_num].active.ptr_array;
1076
0a7de745 1077 if (old_rcount != 0) {
39236c6e
A
1078 r = TAILQ_FIRST(rs->rules[rs_num].active.ptr);
1079 while (r) {
0a7de745 1080 if (r->rule_flag & PFRULE_PFM) {
39236c6e 1081 pffwrules--;
0a7de745 1082 }
39236c6e
A
1083 r = TAILQ_NEXT(r, entries);
1084 }
1085 }
1086
1087
b0d623f7
A
1088 rs->rules[rs_num].active.ptr =
1089 rs->rules[rs_num].inactive.ptr;
1090 rs->rules[rs_num].active.ptr_array =
1091 rs->rules[rs_num].inactive.ptr_array;
1092 rs->rules[rs_num].active.rcount =
1093 rs->rules[rs_num].inactive.rcount;
1094 rs->rules[rs_num].inactive.ptr = old_rules;
1095 rs->rules[rs_num].inactive.ptr_array = old_array;
1096 rs->rules[rs_num].inactive.rcount = old_rcount;
1097
1098 rs->rules[rs_num].active.ticket =
1099 rs->rules[rs_num].inactive.ticket;
1100 pf_calc_skip_steps(rs->rules[rs_num].active.ptr);
1101
1102
1103 /* Purge the old rule list. */
0a7de745 1104 while ((rule = TAILQ_FIRST(old_rules)) != NULL) {
b0d623f7 1105 pf_rm_rule(old_rules, rule);
0a7de745
A
1106 }
1107 if (rs->rules[rs_num].inactive.ptr_array) {
b0d623f7 1108 _FREE(rs->rules[rs_num].inactive.ptr_array, M_TEMP);
0a7de745 1109 }
b0d623f7
A
1110 rs->rules[rs_num].inactive.ptr_array = NULL;
1111 rs->rules[rs_num].inactive.rcount = 0;
1112 rs->rules[rs_num].inactive.open = 0;
1113 pf_remove_if_empty_ruleset(rs);
0a7de745 1114 return 0;
b0d623f7
A
1115}
1116
6d2010ae 1117static void
316670eb
A
1118pf_rule_copyin(struct pf_rule *src, struct pf_rule *dst, struct proc *p,
1119 int minordev)
6d2010ae 1120{
0a7de745 1121 bcopy(src, dst, sizeof(struct pf_rule));
6d2010ae 1122
0a7de745
A
1123 dst->label[sizeof(dst->label) - 1] = '\0';
1124 dst->ifname[sizeof(dst->ifname) - 1] = '\0';
1125 dst->qname[sizeof(dst->qname) - 1] = '\0';
1126 dst->pqname[sizeof(dst->pqname) - 1] = '\0';
1127 dst->tagname[sizeof(dst->tagname) - 1] = '\0';
1128 dst->match_tagname[sizeof(dst->match_tagname) - 1] = '\0';
1129 dst->overload_tblname[sizeof(dst->overload_tblname) - 1] = '\0';
bca245ac 1130 dst->owner[sizeof(dst->owner) - 1] = '\0';
6d2010ae
A
1131
1132 dst->cuid = kauth_cred_getuid(p->p_ucred);
1133 dst->cpid = p->p_pid;
1134
1135 dst->anchor = NULL;
1136 dst->kif = NULL;
1137 dst->overload_tbl = NULL;
1138
1139 TAILQ_INIT(&dst->rpool.list);
1140 dst->rpool.cur = NULL;
1141
1142 /* initialize refcounting */
1143 dst->states = 0;
1144 dst->src_nodes = 0;
1145
1146 dst->entries.tqe_prev = NULL;
1147 dst->entries.tqe_next = NULL;
0a7de745 1148 if ((uint8_t)minordev == PFDEV_PFM) {
316670eb 1149 dst->rule_flag |= PFRULE_PFM;
0a7de745 1150 }
6d2010ae
A
1151}
1152
1153static void
1154pf_rule_copyout(struct pf_rule *src, struct pf_rule *dst)
1155{
0a7de745 1156 bcopy(src, dst, sizeof(struct pf_rule));
6d2010ae
A
1157
1158 dst->anchor = NULL;
1159 dst->kif = NULL;
1160 dst->overload_tbl = NULL;
1161
bca245ac
A
1162 dst->rpool.list.tqh_first = NULL;
1163 dst->rpool.list.tqh_last = NULL;
6d2010ae
A
1164 dst->rpool.cur = NULL;
1165
1166 dst->entries.tqe_prev = NULL;
1167 dst->entries.tqe_next = NULL;
1168}
1169
b0d623f7
A
1170static void
1171pf_state_export(struct pfsync_state *sp, struct pf_state_key *sk,
1172 struct pf_state *s)
1173{
1174 uint64_t secs = pf_time_second();
0a7de745 1175 bzero(sp, sizeof(struct pfsync_state));
b0d623f7
A
1176
1177 /* copy from state key */
b0d623f7
A
1178 sp->lan.addr = sk->lan.addr;
1179 sp->lan.xport = sk->lan.xport;
1180 sp->gwy.addr = sk->gwy.addr;
1181 sp->gwy.xport = sk->gwy.xport;
3e170ce0
A
1182 sp->ext_lan.addr = sk->ext_lan.addr;
1183 sp->ext_lan.xport = sk->ext_lan.xport;
1184 sp->ext_gwy.addr = sk->ext_gwy.addr;
1185 sp->ext_gwy.xport = sk->ext_gwy.xport;
b0d623f7
A
1186 sp->proto_variant = sk->proto_variant;
1187 sp->tag = s->tag;
b0d623f7 1188 sp->proto = sk->proto;
3e170ce0
A
1189 sp->af_lan = sk->af_lan;
1190 sp->af_gwy = sk->af_gwy;
b0d623f7 1191 sp->direction = sk->direction;
316670eb 1192 sp->flowhash = sk->flowhash;
b0d623f7
A
1193
1194 /* copy from state */
0a7de745 1195 memcpy(&sp->id, &s->id, sizeof(sp->id));
b0d623f7 1196 sp->creatorid = s->creatorid;
0a7de745 1197 strlcpy(sp->ifname, s->kif->pfik_name, sizeof(sp->ifname));
b0d623f7
A
1198 pf_state_peer_to_pfsync(&s->src, &sp->src);
1199 pf_state_peer_to_pfsync(&s->dst, &sp->dst);
1200
1201 sp->rule = s->rule.ptr->nr;
1202 sp->nat_rule = (s->nat_rule.ptr == NULL) ?
1203 (unsigned)-1 : s->nat_rule.ptr->nr;
1204 sp->anchor = (s->anchor.ptr == NULL) ?
1205 (unsigned)-1 : s->anchor.ptr->nr;
1206
1207 pf_state_counter_to_pfsync(s->bytes[0], sp->bytes[0]);
1208 pf_state_counter_to_pfsync(s->bytes[1], sp->bytes[1]);
1209 pf_state_counter_to_pfsync(s->packets[0], sp->packets[0]);
1210 pf_state_counter_to_pfsync(s->packets[1], sp->packets[1]);
1211 sp->creation = secs - s->creation;
1212 sp->expire = pf_state_expires(s);
1213 sp->log = s->log;
1214 sp->allow_opts = s->allow_opts;
1215 sp->timeout = s->timeout;
1216
0a7de745 1217 if (s->src_node) {
b0d623f7 1218 sp->sync_flags |= PFSYNC_FLAG_SRCNODE;
0a7de745
A
1219 }
1220 if (s->nat_src_node) {
b0d623f7 1221 sp->sync_flags |= PFSYNC_FLAG_NATSRCNODE;
0a7de745 1222 }
b0d623f7 1223
0a7de745 1224 if (sp->expire > secs) {
b0d623f7 1225 sp->expire -= secs;
0a7de745 1226 } else {
b0d623f7 1227 sp->expire = 0;
0a7de745 1228 }
b0d623f7
A
1229}
1230
1231static void
1232pf_state_import(struct pfsync_state *sp, struct pf_state_key *sk,
1233 struct pf_state *s)
1234{
1235 /* copy to state key */
b0d623f7
A
1236 sk->lan.addr = sp->lan.addr;
1237 sk->lan.xport = sp->lan.xport;
1238 sk->gwy.addr = sp->gwy.addr;
1239 sk->gwy.xport = sp->gwy.xport;
3e170ce0
A
1240 sk->ext_lan.addr = sp->ext_lan.addr;
1241 sk->ext_lan.xport = sp->ext_lan.xport;
1242 sk->ext_gwy.addr = sp->ext_gwy.addr;
1243 sk->ext_gwy.xport = sp->ext_gwy.xport;
b0d623f7
A
1244 sk->proto_variant = sp->proto_variant;
1245 s->tag = sp->tag;
b0d623f7 1246 sk->proto = sp->proto;
3e170ce0
A
1247 sk->af_lan = sp->af_lan;
1248 sk->af_gwy = sp->af_gwy;
b0d623f7 1249 sk->direction = sp->direction;
316670eb 1250 sk->flowhash = pf_calc_state_key_flowhash(sk);
b0d623f7
A
1251
1252 /* copy to state */
0a7de745 1253 memcpy(&s->id, &sp->id, sizeof(sp->id));
b0d623f7
A
1254 s->creatorid = sp->creatorid;
1255 pf_state_peer_from_pfsync(&sp->src, &s->src);
1256 pf_state_peer_from_pfsync(&sp->dst, &s->dst);
1257
1258 s->rule.ptr = &pf_default_rule;
1259 s->nat_rule.ptr = NULL;
1260 s->anchor.ptr = NULL;
1261 s->rt_kif = NULL;
1262 s->creation = pf_time_second();
1263 s->expire = pf_time_second();
0a7de745 1264 if (sp->expire > 0) {
b0d623f7 1265 s->expire -= pf_default_rule.timeout[sp->timeout] - sp->expire;
0a7de745 1266 }
b0d623f7
A
1267 s->pfsync_time = 0;
1268 s->packets[0] = s->packets[1] = 0;
1269 s->bytes[0] = s->bytes[1] = 0;
1270}
1271
6d2010ae
A
1272static void
1273pf_pooladdr_copyin(struct pf_pooladdr *src, struct pf_pooladdr *dst)
1274{
0a7de745 1275 bcopy(src, dst, sizeof(struct pf_pooladdr));
6d2010ae
A
1276
1277 dst->entries.tqe_prev = NULL;
1278 dst->entries.tqe_next = NULL;
0a7de745 1279 dst->ifname[sizeof(dst->ifname) - 1] = '\0';
6d2010ae
A
1280 dst->kif = NULL;
1281}
1282
1283static void
1284pf_pooladdr_copyout(struct pf_pooladdr *src, struct pf_pooladdr *dst)
1285{
0a7de745 1286 bcopy(src, dst, sizeof(struct pf_pooladdr));
6d2010ae
A
1287
1288 dst->entries.tqe_prev = NULL;
1289 dst->entries.tqe_next = NULL;
1290 dst->kif = NULL;
1291}
1292
b0d623f7
A
1293static int
1294pf_setup_pfsync_matching(struct pf_ruleset *rs)
1295{
0a7de745
A
1296 MD5_CTX ctx;
1297 struct pf_rule *rule;
1298 int rs_cnt;
1299 u_int8_t digest[PF_MD5_DIGEST_LENGTH];
b0d623f7
A
1300
1301 MD5Init(&ctx);
1302 for (rs_cnt = 0; rs_cnt < PF_RULESET_MAX; rs_cnt++) {
1303 /* XXX PF_RULESET_SCRUB as well? */
0a7de745 1304 if (rs_cnt == PF_RULESET_SCRUB) {
b0d623f7 1305 continue;
0a7de745 1306 }
b0d623f7 1307
0a7de745 1308 if (rs->rules[rs_cnt].inactive.ptr_array) {
b0d623f7 1309 _FREE(rs->rules[rs_cnt].inactive.ptr_array, M_TEMP);
0a7de745 1310 }
b0d623f7
A
1311 rs->rules[rs_cnt].inactive.ptr_array = NULL;
1312
1313 if (rs->rules[rs_cnt].inactive.rcount) {
1314 rs->rules[rs_cnt].inactive.ptr_array =
0a7de745 1315 _MALLOC(sizeof(caddr_t) *
b0d623f7
A
1316 rs->rules[rs_cnt].inactive.rcount,
1317 M_TEMP, M_WAITOK);
1318
0a7de745
A
1319 if (!rs->rules[rs_cnt].inactive.ptr_array) {
1320 return ENOMEM;
1321 }
b0d623f7
A
1322 }
1323
1324 TAILQ_FOREACH(rule, rs->rules[rs_cnt].inactive.ptr,
1325 entries) {
1326 pf_hash_rule(&ctx, rule);
1327 (rs->rules[rs_cnt].inactive.ptr_array)[rule->nr] = rule;
1328 }
1329 }
1330
1331 MD5Final(digest, &ctx);
0a7de745
A
1332 memcpy(pf_status.pf_chksum, digest, sizeof(pf_status.pf_chksum));
1333 return 0;
b0d623f7
A
1334}
1335
6d2010ae
A
1336static void
1337pf_start(void)
1338{
5ba3f43e 1339 LCK_MTX_ASSERT(pf_lock, LCK_MTX_ASSERT_OWNED);
6d2010ae
A
1340
1341 VERIFY(pf_is_enabled == 0);
1342
1343 pf_is_enabled = 1;
1344 pf_status.running = 1;
1345 pf_status.since = pf_calendar_time_second();
1346 if (pf_status.stateid == 0) {
1347 pf_status.stateid = pf_time_second();
1348 pf_status.stateid = pf_status.stateid << 32;
1349 }
1350 wakeup(pf_purge_thread_fn);
1351 DPFPRINTF(PF_DEBUG_MISC, ("pf: started\n"));
1352}
1353
1354static void
1355pf_stop(void)
1356{
5ba3f43e 1357 LCK_MTX_ASSERT(pf_lock, LCK_MTX_ASSERT_OWNED);
6d2010ae
A
1358
1359 VERIFY(pf_is_enabled);
1360
1361 pf_status.running = 0;
1362 pf_is_enabled = 0;
1363 pf_status.since = pf_calendar_time_second();
1364 wakeup(pf_purge_thread_fn);
1365 DPFPRINTF(PF_DEBUG_MISC, ("pf: stopped\n"));
1366}
1367
b0d623f7
A
1368static int
1369pfioctl(dev_t dev, u_long cmd, caddr_t addr, int flags, struct proc *p)
1370{
1371#pragma unused(dev)
316670eb
A
1372 int p64 = proc_is64bit(p);
1373 int error = 0;
1374 int minordev = minor(dev);
b0d623f7 1375
0a7de745
A
1376 if (kauth_cred_issuser(kauth_cred_get()) == 0) {
1377 return EPERM;
1378 }
b0d623f7
A
1379
1380 /* XXX keep in sync with switch() below */
0a7de745 1381 if (securelevel > 1) {
b0d623f7
A
1382 switch (cmd) {
1383 case DIOCGETRULES:
1384 case DIOCGETRULE:
1385 case DIOCGETADDRS:
1386 case DIOCGETADDR:
1387 case DIOCGETSTATE:
1388 case DIOCSETSTATUSIF:
1389 case DIOCGETSTATUS:
1390 case DIOCCLRSTATUS:
1391 case DIOCNATLOOK:
1392 case DIOCSETDEBUG:
1393 case DIOCGETSTATES:
316670eb
A
1394 case DIOCINSERTRULE:
1395 case DIOCDELETERULE:
b0d623f7
A
1396 case DIOCGETTIMEOUT:
1397 case DIOCCLRRULECTRS:
1398 case DIOCGETLIMIT:
1399 case DIOCGETALTQS:
1400 case DIOCGETALTQ:
1401 case DIOCGETQSTATS:
1402 case DIOCGETRULESETS:
1403 case DIOCGETRULESET:
1404 case DIOCRGETTABLES:
1405 case DIOCRGETTSTATS:
1406 case DIOCRCLRTSTATS:
1407 case DIOCRCLRADDRS:
1408 case DIOCRADDADDRS:
1409 case DIOCRDELADDRS:
1410 case DIOCRSETADDRS:
1411 case DIOCRGETADDRS:
1412 case DIOCRGETASTATS:
1413 case DIOCRCLRASTATS:
1414 case DIOCRTSTADDRS:
1415 case DIOCOSFPGET:
1416 case DIOCGETSRCNODES:
1417 case DIOCCLRSRCNODES:
1418 case DIOCIGETIFACES:
316670eb 1419 case DIOCGIFSPEED:
b0d623f7
A
1420 case DIOCSETIFFLAG:
1421 case DIOCCLRIFFLAG:
1422 break;
1423 case DIOCRCLRTABLES:
1424 case DIOCRADDTABLES:
1425 case DIOCRDELTABLES:
316670eb
A
1426 case DIOCRSETTFLAGS: {
1427 int pfrio_flags;
1428
1429 bcopy(&((struct pfioc_table *)(void *)addr)->
0a7de745 1430 pfrio_flags, &pfrio_flags, sizeof(pfrio_flags));
316670eb 1431
0a7de745 1432 if (pfrio_flags & PFR_FLAG_DUMMY) {
b0d623f7 1433 break; /* dummy operation ok */
0a7de745
A
1434 }
1435 return EPERM;
316670eb 1436 }
b0d623f7 1437 default:
0a7de745 1438 return EPERM;
b0d623f7 1439 }
0a7de745 1440 }
b0d623f7 1441
0a7de745 1442 if (!(flags & FWRITE)) {
b0d623f7
A
1443 switch (cmd) {
1444 case DIOCSTART:
6d2010ae 1445 case DIOCSTARTREF:
b0d623f7 1446 case DIOCSTOP:
6d2010ae
A
1447 case DIOCSTOPREF:
1448 case DIOCGETSTARTERS:
b0d623f7
A
1449 case DIOCGETRULES:
1450 case DIOCGETADDRS:
1451 case DIOCGETADDR:
1452 case DIOCGETSTATE:
1453 case DIOCGETSTATUS:
1454 case DIOCGETSTATES:
316670eb
A
1455 case DIOCINSERTRULE:
1456 case DIOCDELETERULE:
b0d623f7
A
1457 case DIOCGETTIMEOUT:
1458 case DIOCGETLIMIT:
1459 case DIOCGETALTQS:
1460 case DIOCGETALTQ:
1461 case DIOCGETQSTATS:
1462 case DIOCGETRULESETS:
1463 case DIOCGETRULESET:
1464 case DIOCNATLOOK:
1465 case DIOCRGETTABLES:
1466 case DIOCRGETTSTATS:
1467 case DIOCRGETADDRS:
1468 case DIOCRGETASTATS:
1469 case DIOCRTSTADDRS:
1470 case DIOCOSFPGET:
1471 case DIOCGETSRCNODES:
1472 case DIOCIGETIFACES:
316670eb 1473 case DIOCGIFSPEED:
b0d623f7
A
1474 break;
1475 case DIOCRCLRTABLES:
1476 case DIOCRADDTABLES:
1477 case DIOCRDELTABLES:
1478 case DIOCRCLRTSTATS:
1479 case DIOCRCLRADDRS:
1480 case DIOCRADDADDRS:
1481 case DIOCRDELADDRS:
1482 case DIOCRSETADDRS:
316670eb
A
1483 case DIOCRSETTFLAGS: {
1484 int pfrio_flags;
1485
1486 bcopy(&((struct pfioc_table *)(void *)addr)->
0a7de745 1487 pfrio_flags, &pfrio_flags, sizeof(pfrio_flags));
316670eb
A
1488
1489 if (pfrio_flags & PFR_FLAG_DUMMY) {
b0d623f7
A
1490 flags |= FWRITE; /* need write lock for dummy */
1491 break; /* dummy operation ok */
1492 }
0a7de745 1493 return EACCES;
316670eb
A
1494 }
1495 case DIOCGETRULE: {
1496 u_int32_t action;
1497
1498 bcopy(&((struct pfioc_rule *)(void *)addr)->action,
0a7de745 1499 &action, sizeof(action));
316670eb 1500
0a7de745
A
1501 if (action == PF_GET_CLR_CNTR) {
1502 return EACCES;
1503 }
b0d623f7 1504 break;
316670eb 1505 }
b0d623f7 1506 default:
0a7de745 1507 return EACCES;
b0d623f7 1508 }
0a7de745 1509 }
b0d623f7 1510
0a7de745 1511 if (flags & FWRITE) {
b0d623f7 1512 lck_rw_lock_exclusive(pf_perim_lock);
0a7de745 1513 } else {
b0d623f7 1514 lck_rw_lock_shared(pf_perim_lock);
0a7de745 1515 }
b0d623f7
A
1516
1517 lck_mtx_lock(pf_lock);
1518
1519 switch (cmd) {
b0d623f7
A
1520 case DIOCSTART:
1521 if (pf_status.running) {
6d2010ae
A
1522 /*
1523 * Increment the reference for a simple -e enable, so
1524 * that even if other processes drop their references,
1525 * pf will still be available to processes that turned
1526 * it on without taking a reference
1527 */
1528 if (nr_tokens == pf_enabled_ref_count) {
1529 pf_enabled_ref_count++;
1530 VERIFY(pf_enabled_ref_count != 0);
1531 }
b0d623f7
A
1532 error = EEXIST;
1533 } else if (pf_purge_thread == NULL) {
1534 error = ENOMEM;
1535 } else {
6d2010ae
A
1536 pf_start();
1537 pf_enabled_ref_count++;
1538 VERIFY(pf_enabled_ref_count != 0);
1539 }
1540 break;
1541
0a7de745 1542 case DIOCSTARTREF: /* u_int64_t */
6d2010ae
A
1543 if (pf_purge_thread == NULL) {
1544 error = ENOMEM;
1545 } else {
316670eb
A
1546 u_int64_t token;
1547
1548 /* small enough to be on stack */
1549 if ((token = generate_token(p)) != 0) {
6d2010ae
A
1550 if (pf_is_enabled == 0) {
1551 pf_start();
1552 }
1553 pf_enabled_ref_count++;
1554 VERIFY(pf_enabled_ref_count != 0);
1555 } else {
1556 error = ENOMEM;
1557 DPFPRINTF(PF_DEBUG_URGENT,
316670eb 1558 ("pf: unable to generate token\n"));
b0d623f7 1559 }
0a7de745 1560 bcopy(&token, addr, sizeof(token));
b0d623f7
A
1561 }
1562 break;
1563
1564 case DIOCSTOP:
1565 if (!pf_status.running) {
1566 error = ENOENT;
1567 } else {
6d2010ae
A
1568 pf_stop();
1569 pf_enabled_ref_count = 0;
1570 invalidate_all_tokens();
b0d623f7
A
1571 }
1572 break;
1573
0a7de745 1574 case DIOCSTOPREF: /* struct pfioc_remove_token */
6d2010ae
A
1575 if (!pf_status.running) {
1576 error = ENOENT;
1577 } else {
316670eb
A
1578 struct pfioc_remove_token pfrt;
1579
1580 /* small enough to be on stack */
0a7de745 1581 bcopy(addr, &pfrt, sizeof(pfrt));
316670eb 1582 if ((error = remove_token(&pfrt)) == 0) {
6d2010ae
A
1583 VERIFY(pf_enabled_ref_count != 0);
1584 pf_enabled_ref_count--;
316670eb
A
1585 /* return currently held references */
1586 pfrt.refcount = pf_enabled_ref_count;
6d2010ae 1587 DPFPRINTF(PF_DEBUG_MISC,
316670eb 1588 ("pf: enabled refcount decremented\n"));
6d2010ae
A
1589 } else {
1590 error = EINVAL;
1591 DPFPRINTF(PF_DEBUG_URGENT,
316670eb 1592 ("pf: token mismatch\n"));
6d2010ae 1593 }
0a7de745 1594 bcopy(&pfrt, addr, sizeof(pfrt));
6d2010ae 1595
0a7de745 1596 if (error == 0 && pf_enabled_ref_count == 0) {
6d2010ae 1597 pf_stop();
0a7de745 1598 }
6d2010ae
A
1599 }
1600 break;
1601
0a7de745 1602 case DIOCGETSTARTERS: { /* struct pfioc_tokens */
316670eb 1603 PFIOCX_STRUCT_DECL(pfioc_tokens);
6d2010ae 1604
0a7de745 1605 PFIOCX_STRUCT_BEGIN(addr, pfioc_tokens, error = ENOMEM; break; );
316670eb
A
1606 error = pfioctl_ioc_tokens(cmd,
1607 PFIOCX_STRUCT_ADDR32(pfioc_tokens),
1608 PFIOCX_STRUCT_ADDR64(pfioc_tokens), p);
1609 PFIOCX_STRUCT_END(pfioc_tokens, addr);
1610 break;
1611 }
1612
0a7de745
A
1613 case DIOCADDRULE: /* struct pfioc_rule */
1614 case DIOCGETRULES: /* struct pfioc_rule */
1615 case DIOCGETRULE: /* struct pfioc_rule */
1616 case DIOCCHANGERULE: /* struct pfioc_rule */
1617 case DIOCINSERTRULE: /* struct pfioc_rule */
1618 case DIOCDELETERULE: { /* struct pfioc_rule */
316670eb
A
1619 struct pfioc_rule *pr = NULL;
1620
0a7de745 1621 PFIOC_STRUCT_BEGIN(addr, pr, error = ENOMEM; break; );
316670eb
A
1622 error = pfioctl_ioc_rule(cmd, minordev, pr, p);
1623 PFIOC_STRUCT_END(pr, addr);
1624 break;
1625 }
6d2010ae 1626
0a7de745
A
1627 case DIOCCLRSTATES: /* struct pfioc_state_kill */
1628 case DIOCKILLSTATES: { /* struct pfioc_state_kill */
316670eb 1629 struct pfioc_state_kill *psk = NULL;
6d2010ae 1630
0a7de745 1631 PFIOC_STRUCT_BEGIN(addr, psk, error = ENOMEM; break; );
316670eb
A
1632 error = pfioctl_ioc_state_kill(cmd, psk, p);
1633 PFIOC_STRUCT_END(psk, addr);
1634 break;
1635 }
6d2010ae 1636
0a7de745
A
1637 case DIOCADDSTATE: /* struct pfioc_state */
1638 case DIOCGETSTATE: { /* struct pfioc_state */
316670eb 1639 struct pfioc_state *ps = NULL;
6d2010ae 1640
0a7de745 1641 PFIOC_STRUCT_BEGIN(addr, ps, error = ENOMEM; break; );
316670eb
A
1642 error = pfioctl_ioc_state(cmd, ps, p);
1643 PFIOC_STRUCT_END(ps, addr);
1644 break;
1645 }
1646
0a7de745 1647 case DIOCGETSTATES: { /* struct pfioc_states */
316670eb
A
1648 PFIOCX_STRUCT_DECL(pfioc_states);
1649
0a7de745 1650 PFIOCX_STRUCT_BEGIN(addr, pfioc_states, error = ENOMEM; break; );
316670eb
A
1651 error = pfioctl_ioc_states(cmd,
1652 PFIOCX_STRUCT_ADDR32(pfioc_states),
1653 PFIOCX_STRUCT_ADDR64(pfioc_states), p);
1654 PFIOCX_STRUCT_END(pfioc_states, addr);
1655 break;
1656 }
1657
0a7de745 1658 case DIOCGETSTATUS: { /* struct pf_status */
316670eb
A
1659 struct pf_status *s = NULL;
1660
0a7de745 1661 PFIOC_STRUCT_BEGIN(&pf_status, s, error = ENOMEM; break; );
316670eb
A
1662 pfi_update_status(s->ifname, s);
1663 PFIOC_STRUCT_END(s, addr);
1664 break;
1665 }
1666
0a7de745
A
1667 case DIOCSETSTATUSIF: { /* struct pfioc_if */
1668 struct pfioc_if *pi = (struct pfioc_if *)(void *)addr;
316670eb
A
1669
1670 /* OK for unaligned accesses */
1671 if (pi->ifname[0] == 0) {
1672 bzero(pf_status.ifname, IFNAMSIZ);
6d2010ae
A
1673 break;
1674 }
316670eb
A
1675 strlcpy(pf_status.ifname, pi->ifname, IFNAMSIZ);
1676 break;
1677 }
6d2010ae 1678
316670eb 1679 case DIOCCLRSTATUS: {
0a7de745
A
1680 bzero(pf_status.counters, sizeof(pf_status.counters));
1681 bzero(pf_status.fcounters, sizeof(pf_status.fcounters));
1682 bzero(pf_status.scounters, sizeof(pf_status.scounters));
316670eb 1683 pf_status.since = pf_calendar_time_second();
0a7de745 1684 if (*pf_status.ifname) {
316670eb 1685 pfi_update_status(pf_status.ifname, NULL);
0a7de745 1686 }
316670eb
A
1687 break;
1688 }
6d2010ae 1689
0a7de745 1690 case DIOCNATLOOK: { /* struct pfioc_natlook */
316670eb 1691 struct pfioc_natlook *pnl = NULL;
6d2010ae 1692
0a7de745 1693 PFIOC_STRUCT_BEGIN(addr, pnl, error = ENOMEM; break; );
316670eb
A
1694 error = pfioctl_ioc_natlook(cmd, pnl, p);
1695 PFIOC_STRUCT_END(pnl, addr);
1696 break;
1697 }
6d2010ae 1698
0a7de745
A
1699 case DIOCSETTIMEOUT: /* struct pfioc_tm */
1700 case DIOCGETTIMEOUT: { /* struct pfioc_tm */
1701 struct pfioc_tm pt;
6d2010ae 1702
316670eb 1703 /* small enough to be on stack */
0a7de745 1704 bcopy(addr, &pt, sizeof(pt));
316670eb 1705 error = pfioctl_ioc_tm(cmd, &pt, p);
0a7de745 1706 bcopy(&pt, addr, sizeof(pt));
316670eb
A
1707 break;
1708 }
1709
0a7de745
A
1710 case DIOCGETLIMIT: /* struct pfioc_limit */
1711 case DIOCSETLIMIT: { /* struct pfioc_limit */
316670eb 1712 struct pfioc_limit pl;
6d2010ae 1713
316670eb 1714 /* small enough to be on stack */
0a7de745 1715 bcopy(addr, &pl, sizeof(pl));
316670eb 1716 error = pfioctl_ioc_limit(cmd, &pl, p);
0a7de745 1717 bcopy(&pl, addr, sizeof(pl));
6d2010ae 1718 break;
316670eb 1719 }
6d2010ae 1720
0a7de745
A
1721 case DIOCSETDEBUG: { /* u_int32_t */
1722 bcopy(addr, &pf_status.debug, sizeof(u_int32_t));
316670eb
A
1723 break;
1724 }
b0d623f7 1725
316670eb
A
1726 case DIOCCLRRULECTRS: {
1727 /* obsoleted by DIOCGETRULE with action=PF_GET_CLR_CNTR */
0a7de745
A
1728 struct pf_ruleset *ruleset = &pf_main_ruleset;
1729 struct pf_rule *rule;
316670eb
A
1730
1731 TAILQ_FOREACH(rule,
1732 ruleset->rules[PF_RULESET_FILTER].active.ptr, entries) {
1733 rule->evaluations = 0;
1734 rule->packets[0] = rule->packets[1] = 0;
1735 rule->bytes[0] = rule->bytes[1] = 0;
b0d623f7 1736 }
316670eb
A
1737 break;
1738 }
1739
1740 case DIOCGIFSPEED: {
1741 struct pf_ifspeed *psp = (struct pf_ifspeed *)(void *)addr;
1742 struct pf_ifspeed ps;
1743 struct ifnet *ifp;
1744 u_int64_t baudrate;
1745
1746 if (psp->ifname[0] != '\0') {
1747 /* Can we completely trust user-land? */
1748 strlcpy(ps.ifname, psp->ifname, IFNAMSIZ);
1749 ps.ifname[IFNAMSIZ - 1] = '\0';
1750 ifp = ifunit(ps.ifname);
1751 if (ifp != NULL) {
1752 baudrate = ifp->if_output_bw.max_bw;
1753 bcopy(&baudrate, &psp->baudrate,
0a7de745 1754 sizeof(baudrate));
316670eb
A
1755 } else {
1756 error = EINVAL;
1757 }
1758 } else {
b0d623f7 1759 error = EINVAL;
b0d623f7 1760 }
316670eb
A
1761 break;
1762 }
1763
0a7de745
A
1764 case DIOCBEGINADDRS: /* struct pfioc_pooladdr */
1765 case DIOCADDADDR: /* struct pfioc_pooladdr */
1766 case DIOCGETADDRS: /* struct pfioc_pooladdr */
1767 case DIOCGETADDR: /* struct pfioc_pooladdr */
1768 case DIOCCHANGEADDR: { /* struct pfioc_pooladdr */
316670eb
A
1769 struct pfioc_pooladdr *pp = NULL;
1770
0a7de745 1771 PFIOC_STRUCT_BEGIN(addr, pp, error = ENOMEM; break; )
316670eb
A
1772 error = pfioctl_ioc_pooladdr(cmd, pp, p);
1773 PFIOC_STRUCT_END(pp, addr);
1774 break;
1775 }
1776
0a7de745
A
1777 case DIOCGETRULESETS: /* struct pfioc_ruleset */
1778 case DIOCGETRULESET: { /* struct pfioc_ruleset */
316670eb
A
1779 struct pfioc_ruleset *pr = NULL;
1780
0a7de745 1781 PFIOC_STRUCT_BEGIN(addr, pr, error = ENOMEM; break; );
316670eb
A
1782 error = pfioctl_ioc_ruleset(cmd, pr, p);
1783 PFIOC_STRUCT_END(pr, addr);
1784 break;
1785 }
1786
0a7de745
A
1787 case DIOCRCLRTABLES: /* struct pfioc_table */
1788 case DIOCRADDTABLES: /* struct pfioc_table */
1789 case DIOCRDELTABLES: /* struct pfioc_table */
1790 case DIOCRGETTABLES: /* struct pfioc_table */
1791 case DIOCRGETTSTATS: /* struct pfioc_table */
1792 case DIOCRCLRTSTATS: /* struct pfioc_table */
1793 case DIOCRSETTFLAGS: /* struct pfioc_table */
1794 case DIOCRCLRADDRS: /* struct pfioc_table */
1795 case DIOCRADDADDRS: /* struct pfioc_table */
1796 case DIOCRDELADDRS: /* struct pfioc_table */
1797 case DIOCRSETADDRS: /* struct pfioc_table */
1798 case DIOCRGETADDRS: /* struct pfioc_table */
1799 case DIOCRGETASTATS: /* struct pfioc_table */
1800 case DIOCRCLRASTATS: /* struct pfioc_table */
1801 case DIOCRTSTADDRS: /* struct pfioc_table */
1802 case DIOCRINADEFINE: { /* struct pfioc_table */
316670eb
A
1803 PFIOCX_STRUCT_DECL(pfioc_table);
1804
0a7de745 1805 PFIOCX_STRUCT_BEGIN(addr, pfioc_table, error = ENOMEM; break; );
316670eb
A
1806 error = pfioctl_ioc_table(cmd,
1807 PFIOCX_STRUCT_ADDR32(pfioc_table),
1808 PFIOCX_STRUCT_ADDR64(pfioc_table), p);
1809 PFIOCX_STRUCT_END(pfioc_table, addr);
1810 break;
1811 }
1812
0a7de745
A
1813 case DIOCOSFPADD: /* struct pf_osfp_ioctl */
1814 case DIOCOSFPGET: { /* struct pf_osfp_ioctl */
316670eb
A
1815 struct pf_osfp_ioctl *io = NULL;
1816
0a7de745 1817 PFIOC_STRUCT_BEGIN(addr, io, error = ENOMEM; break; );
316670eb
A
1818 if (cmd == DIOCOSFPADD) {
1819 error = pf_osfp_add(io);
1820 } else {
1821 VERIFY(cmd == DIOCOSFPGET);
1822 error = pf_osfp_get(io);
1823 }
1824 PFIOC_STRUCT_END(io, addr);
1825 break;
1826 }
1827
0a7de745
A
1828 case DIOCXBEGIN: /* struct pfioc_trans */
1829 case DIOCXROLLBACK: /* struct pfioc_trans */
1830 case DIOCXCOMMIT: { /* struct pfioc_trans */
316670eb
A
1831 PFIOCX_STRUCT_DECL(pfioc_trans);
1832
0a7de745 1833 PFIOCX_STRUCT_BEGIN(addr, pfioc_trans, error = ENOMEM; break; );
316670eb
A
1834 error = pfioctl_ioc_trans(cmd,
1835 PFIOCX_STRUCT_ADDR32(pfioc_trans),
1836 PFIOCX_STRUCT_ADDR64(pfioc_trans), p);
1837 PFIOCX_STRUCT_END(pfioc_trans, addr);
1838 break;
1839 }
1840
0a7de745 1841 case DIOCGETSRCNODES: { /* struct pfioc_src_nodes */
316670eb
A
1842 PFIOCX_STRUCT_DECL(pfioc_src_nodes);
1843
1844 PFIOCX_STRUCT_BEGIN(addr, pfioc_src_nodes,
0a7de745 1845 error = ENOMEM; break; );
316670eb
A
1846 error = pfioctl_ioc_src_nodes(cmd,
1847 PFIOCX_STRUCT_ADDR32(pfioc_src_nodes),
1848 PFIOCX_STRUCT_ADDR64(pfioc_src_nodes), p);
1849 PFIOCX_STRUCT_END(pfioc_src_nodes, addr);
1850 break;
1851 }
1852
1853 case DIOCCLRSRCNODES: {
0a7de745
A
1854 struct pf_src_node *n;
1855 struct pf_state *state;
316670eb
A
1856
1857 RB_FOREACH(state, pf_state_tree_id, &tree_id) {
1858 state->src_node = NULL;
1859 state->nat_src_node = NULL;
1860 }
1861 RB_FOREACH(n, pf_src_tree, &tree_src_tracking) {
1862 n->expire = 1;
1863 n->states = 0;
1864 }
1865 pf_purge_expired_src_nodes();
1866 pf_status.src_nodes = 0;
1867 break;
1868 }
1869
0a7de745 1870 case DIOCKILLSRCNODES: { /* struct pfioc_src_node_kill */
316670eb
A
1871 struct pfioc_src_node_kill *psnk = NULL;
1872
0a7de745 1873 PFIOC_STRUCT_BEGIN(addr, psnk, error = ENOMEM; break; );
316670eb
A
1874 error = pfioctl_ioc_src_node_kill(cmd, psnk, p);
1875 PFIOC_STRUCT_END(psnk, addr);
1876 break;
1877 }
1878
0a7de745 1879 case DIOCSETHOSTID: { /* u_int32_t */
316670eb
A
1880 u_int32_t hid;
1881
1882 /* small enough to be on stack */
0a7de745
A
1883 bcopy(addr, &hid, sizeof(hid));
1884 if (hid == 0) {
316670eb 1885 pf_status.hostid = random();
0a7de745 1886 } else {
316670eb 1887 pf_status.hostid = hid;
0a7de745 1888 }
316670eb
A
1889 break;
1890 }
1891
1892 case DIOCOSFPFLUSH:
1893 pf_osfp_flush();
1894 break;
1895
0a7de745
A
1896 case DIOCIGETIFACES: /* struct pfioc_iface */
1897 case DIOCSETIFFLAG: /* struct pfioc_iface */
1898 case DIOCCLRIFFLAG: { /* struct pfioc_iface */
316670eb
A
1899 PFIOCX_STRUCT_DECL(pfioc_iface);
1900
0a7de745 1901 PFIOCX_STRUCT_BEGIN(addr, pfioc_iface, error = ENOMEM; break; );
316670eb
A
1902 error = pfioctl_ioc_iface(cmd,
1903 PFIOCX_STRUCT_ADDR32(pfioc_iface),
1904 PFIOCX_STRUCT_ADDR64(pfioc_iface), p);
1905 PFIOCX_STRUCT_END(pfioc_iface, addr);
1906 break;
1907 }
1908
1909 default:
1910 error = ENODEV;
1911 break;
1912 }
1913
1914 lck_mtx_unlock(pf_lock);
1915 lck_rw_done(pf_perim_lock);
1916
0a7de745 1917 return error;
316670eb
A
1918}
1919
1920static int
1921pfioctl_ioc_table(u_long cmd, struct pfioc_table_32 *io32,
1922 struct pfioc_table_64 *io64, struct proc *p)
1923{
1924 int p64 = proc_is64bit(p);
1925 int error = 0;
1926
0a7de745 1927 if (!p64) {
316670eb 1928 goto struct32;
0a7de745 1929 }
316670eb
A
1930
1931 /*
1932 * 64-bit structure processing
1933 */
1934 switch (cmd) {
1935 case DIOCRCLRTABLES:
1936 if (io64->pfrio_esize != 0) {
1937 error = ENODEV;
1938 break;
1939 }
1940 pfr_table_copyin_cleanup(&io64->pfrio_table);
1941 error = pfr_clr_tables(&io64->pfrio_table, &io64->pfrio_ndel,
1942 io64->pfrio_flags | PFR_FLAG_USERIOCTL);
1943 break;
1944
1945 case DIOCRADDTABLES:
0a7de745 1946 if (io64->pfrio_esize != sizeof(struct pfr_table)) {
316670eb
A
1947 error = ENODEV;
1948 break;
1949 }
1950 error = pfr_add_tables(io64->pfrio_buffer, io64->pfrio_size,
1951 &io64->pfrio_nadd, io64->pfrio_flags | PFR_FLAG_USERIOCTL);
1952 break;
1953
1954 case DIOCRDELTABLES:
0a7de745 1955 if (io64->pfrio_esize != sizeof(struct pfr_table)) {
316670eb
A
1956 error = ENODEV;
1957 break;
1958 }
1959 error = pfr_del_tables(io64->pfrio_buffer, io64->pfrio_size,
1960 &io64->pfrio_ndel, io64->pfrio_flags | PFR_FLAG_USERIOCTL);
1961 break;
1962
1963 case DIOCRGETTABLES:
0a7de745 1964 if (io64->pfrio_esize != sizeof(struct pfr_table)) {
316670eb
A
1965 error = ENODEV;
1966 break;
1967 }
1968 pfr_table_copyin_cleanup(&io64->pfrio_table);
1969 error = pfr_get_tables(&io64->pfrio_table, io64->pfrio_buffer,
1970 &io64->pfrio_size, io64->pfrio_flags | PFR_FLAG_USERIOCTL);
1971 break;
1972
1973 case DIOCRGETTSTATS:
0a7de745 1974 if (io64->pfrio_esize != sizeof(struct pfr_tstats)) {
316670eb
A
1975 error = ENODEV;
1976 break;
1977 }
1978 pfr_table_copyin_cleanup(&io64->pfrio_table);
1979 error = pfr_get_tstats(&io64->pfrio_table, io64->pfrio_buffer,
1980 &io64->pfrio_size, io64->pfrio_flags | PFR_FLAG_USERIOCTL);
1981 break;
1982
1983 case DIOCRCLRTSTATS:
0a7de745 1984 if (io64->pfrio_esize != sizeof(struct pfr_table)) {
316670eb
A
1985 error = ENODEV;
1986 break;
1987 }
1988 error = pfr_clr_tstats(io64->pfrio_buffer, io64->pfrio_size,
1989 &io64->pfrio_nzero, io64->pfrio_flags | PFR_FLAG_USERIOCTL);
1990 break;
1991
1992 case DIOCRSETTFLAGS:
0a7de745 1993 if (io64->pfrio_esize != sizeof(struct pfr_table)) {
316670eb
A
1994 error = ENODEV;
1995 break;
1996 }
1997 error = pfr_set_tflags(io64->pfrio_buffer, io64->pfrio_size,
1998 io64->pfrio_setflag, io64->pfrio_clrflag,
1999 &io64->pfrio_nchange, &io64->pfrio_ndel,
2000 io64->pfrio_flags | PFR_FLAG_USERIOCTL);
2001 break;
2002
2003 case DIOCRCLRADDRS:
2004 if (io64->pfrio_esize != 0) {
2005 error = ENODEV;
2006 break;
2007 }
2008 pfr_table_copyin_cleanup(&io64->pfrio_table);
2009 error = pfr_clr_addrs(&io64->pfrio_table, &io64->pfrio_ndel,
2010 io64->pfrio_flags | PFR_FLAG_USERIOCTL);
2011 break;
2012
2013 case DIOCRADDADDRS:
0a7de745 2014 if (io64->pfrio_esize != sizeof(struct pfr_addr)) {
316670eb
A
2015 error = ENODEV;
2016 break;
2017 }
2018 pfr_table_copyin_cleanup(&io64->pfrio_table);
2019 error = pfr_add_addrs(&io64->pfrio_table, io64->pfrio_buffer,
2020 io64->pfrio_size, &io64->pfrio_nadd, io64->pfrio_flags |
2021 PFR_FLAG_USERIOCTL);
2022 break;
2023
2024 case DIOCRDELADDRS:
0a7de745 2025 if (io64->pfrio_esize != sizeof(struct pfr_addr)) {
316670eb
A
2026 error = ENODEV;
2027 break;
2028 }
2029 pfr_table_copyin_cleanup(&io64->pfrio_table);
2030 error = pfr_del_addrs(&io64->pfrio_table, io64->pfrio_buffer,
2031 io64->pfrio_size, &io64->pfrio_ndel, io64->pfrio_flags |
2032 PFR_FLAG_USERIOCTL);
2033 break;
2034
2035 case DIOCRSETADDRS:
0a7de745 2036 if (io64->pfrio_esize != sizeof(struct pfr_addr)) {
316670eb
A
2037 error = ENODEV;
2038 break;
2039 }
2040 pfr_table_copyin_cleanup(&io64->pfrio_table);
2041 error = pfr_set_addrs(&io64->pfrio_table, io64->pfrio_buffer,
2042 io64->pfrio_size, &io64->pfrio_size2, &io64->pfrio_nadd,
2043 &io64->pfrio_ndel, &io64->pfrio_nchange, io64->pfrio_flags |
2044 PFR_FLAG_USERIOCTL, 0);
2045 break;
2046
2047 case DIOCRGETADDRS:
0a7de745 2048 if (io64->pfrio_esize != sizeof(struct pfr_addr)) {
316670eb
A
2049 error = ENODEV;
2050 break;
2051 }
2052 pfr_table_copyin_cleanup(&io64->pfrio_table);
2053 error = pfr_get_addrs(&io64->pfrio_table, io64->pfrio_buffer,
2054 &io64->pfrio_size, io64->pfrio_flags | PFR_FLAG_USERIOCTL);
2055 break;
2056
2057 case DIOCRGETASTATS:
0a7de745 2058 if (io64->pfrio_esize != sizeof(struct pfr_astats)) {
316670eb
A
2059 error = ENODEV;
2060 break;
2061 }
2062 pfr_table_copyin_cleanup(&io64->pfrio_table);
2063 error = pfr_get_astats(&io64->pfrio_table, io64->pfrio_buffer,
2064 &io64->pfrio_size, io64->pfrio_flags | PFR_FLAG_USERIOCTL);
2065 break;
2066
2067 case DIOCRCLRASTATS:
0a7de745 2068 if (io64->pfrio_esize != sizeof(struct pfr_addr)) {
316670eb
A
2069 error = ENODEV;
2070 break;
2071 }
2072 pfr_table_copyin_cleanup(&io64->pfrio_table);
2073 error = pfr_clr_astats(&io64->pfrio_table, io64->pfrio_buffer,
2074 io64->pfrio_size, &io64->pfrio_nzero, io64->pfrio_flags |
2075 PFR_FLAG_USERIOCTL);
2076 break;
2077
2078 case DIOCRTSTADDRS:
0a7de745 2079 if (io64->pfrio_esize != sizeof(struct pfr_addr)) {
316670eb
A
2080 error = ENODEV;
2081 break;
2082 }
2083 pfr_table_copyin_cleanup(&io64->pfrio_table);
2084 error = pfr_tst_addrs(&io64->pfrio_table, io64->pfrio_buffer,
2085 io64->pfrio_size, &io64->pfrio_nmatch, io64->pfrio_flags |
2086 PFR_FLAG_USERIOCTL);
2087 break;
2088
2089 case DIOCRINADEFINE:
0a7de745 2090 if (io64->pfrio_esize != sizeof(struct pfr_addr)) {
316670eb
A
2091 error = ENODEV;
2092 break;
2093 }
2094 pfr_table_copyin_cleanup(&io64->pfrio_table);
2095 error = pfr_ina_define(&io64->pfrio_table, io64->pfrio_buffer,
2096 io64->pfrio_size, &io64->pfrio_nadd, &io64->pfrio_naddr,
2097 io64->pfrio_ticket, io64->pfrio_flags | PFR_FLAG_USERIOCTL);
2098 break;
2099
2100 default:
2101 VERIFY(0);
2102 /* NOTREACHED */
2103 }
2104 goto done;
2105
2106struct32:
2107 /*
2108 * 32-bit structure processing
2109 */
2110 switch (cmd) {
2111 case DIOCRCLRTABLES:
2112 if (io32->pfrio_esize != 0) {
2113 error = ENODEV;
2114 break;
2115 }
2116 pfr_table_copyin_cleanup(&io32->pfrio_table);
2117 error = pfr_clr_tables(&io32->pfrio_table, &io32->pfrio_ndel,
2118 io32->pfrio_flags | PFR_FLAG_USERIOCTL);
2119 break;
2120
2121 case DIOCRADDTABLES:
0a7de745 2122 if (io32->pfrio_esize != sizeof(struct pfr_table)) {
316670eb
A
2123 error = ENODEV;
2124 break;
2125 }
2126 error = pfr_add_tables(io32->pfrio_buffer, io32->pfrio_size,
2127 &io32->pfrio_nadd, io32->pfrio_flags | PFR_FLAG_USERIOCTL);
2128 break;
2129
2130 case DIOCRDELTABLES:
0a7de745 2131 if (io32->pfrio_esize != sizeof(struct pfr_table)) {
316670eb
A
2132 error = ENODEV;
2133 break;
2134 }
2135 error = pfr_del_tables(io32->pfrio_buffer, io32->pfrio_size,
2136 &io32->pfrio_ndel, io32->pfrio_flags | PFR_FLAG_USERIOCTL);
2137 break;
2138
2139 case DIOCRGETTABLES:
0a7de745 2140 if (io32->pfrio_esize != sizeof(struct pfr_table)) {
316670eb
A
2141 error = ENODEV;
2142 break;
2143 }
2144 pfr_table_copyin_cleanup(&io32->pfrio_table);
2145 error = pfr_get_tables(&io32->pfrio_table, io32->pfrio_buffer,
2146 &io32->pfrio_size, io32->pfrio_flags | PFR_FLAG_USERIOCTL);
2147 break;
2148
2149 case DIOCRGETTSTATS:
0a7de745 2150 if (io32->pfrio_esize != sizeof(struct pfr_tstats)) {
316670eb
A
2151 error = ENODEV;
2152 break;
2153 }
2154 pfr_table_copyin_cleanup(&io32->pfrio_table);
2155 error = pfr_get_tstats(&io32->pfrio_table, io32->pfrio_buffer,
2156 &io32->pfrio_size, io32->pfrio_flags | PFR_FLAG_USERIOCTL);
2157 break;
2158
2159 case DIOCRCLRTSTATS:
0a7de745 2160 if (io32->pfrio_esize != sizeof(struct pfr_table)) {
316670eb
A
2161 error = ENODEV;
2162 break;
2163 }
2164 error = pfr_clr_tstats(io32->pfrio_buffer, io32->pfrio_size,
2165 &io32->pfrio_nzero, io32->pfrio_flags | PFR_FLAG_USERIOCTL);
2166 break;
2167
2168 case DIOCRSETTFLAGS:
0a7de745 2169 if (io32->pfrio_esize != sizeof(struct pfr_table)) {
316670eb
A
2170 error = ENODEV;
2171 break;
2172 }
2173 error = pfr_set_tflags(io32->pfrio_buffer, io32->pfrio_size,
2174 io32->pfrio_setflag, io32->pfrio_clrflag,
2175 &io32->pfrio_nchange, &io32->pfrio_ndel,
2176 io32->pfrio_flags | PFR_FLAG_USERIOCTL);
2177 break;
2178
2179 case DIOCRCLRADDRS:
2180 if (io32->pfrio_esize != 0) {
2181 error = ENODEV;
2182 break;
2183 }
2184 pfr_table_copyin_cleanup(&io32->pfrio_table);
2185 error = pfr_clr_addrs(&io32->pfrio_table, &io32->pfrio_ndel,
2186 io32->pfrio_flags | PFR_FLAG_USERIOCTL);
2187 break;
2188
2189 case DIOCRADDADDRS:
0a7de745 2190 if (io32->pfrio_esize != sizeof(struct pfr_addr)) {
316670eb
A
2191 error = ENODEV;
2192 break;
2193 }
2194 pfr_table_copyin_cleanup(&io32->pfrio_table);
2195 error = pfr_add_addrs(&io32->pfrio_table, io32->pfrio_buffer,
2196 io32->pfrio_size, &io32->pfrio_nadd, io32->pfrio_flags |
2197 PFR_FLAG_USERIOCTL);
2198 break;
2199
2200 case DIOCRDELADDRS:
0a7de745 2201 if (io32->pfrio_esize != sizeof(struct pfr_addr)) {
316670eb
A
2202 error = ENODEV;
2203 break;
2204 }
2205 pfr_table_copyin_cleanup(&io32->pfrio_table);
2206 error = pfr_del_addrs(&io32->pfrio_table, io32->pfrio_buffer,
2207 io32->pfrio_size, &io32->pfrio_ndel, io32->pfrio_flags |
2208 PFR_FLAG_USERIOCTL);
2209 break;
2210
2211 case DIOCRSETADDRS:
0a7de745 2212 if (io32->pfrio_esize != sizeof(struct pfr_addr)) {
316670eb
A
2213 error = ENODEV;
2214 break;
2215 }
2216 pfr_table_copyin_cleanup(&io32->pfrio_table);
2217 error = pfr_set_addrs(&io32->pfrio_table, io32->pfrio_buffer,
2218 io32->pfrio_size, &io32->pfrio_size2, &io32->pfrio_nadd,
2219 &io32->pfrio_ndel, &io32->pfrio_nchange, io32->pfrio_flags |
2220 PFR_FLAG_USERIOCTL, 0);
2221 break;
2222
2223 case DIOCRGETADDRS:
0a7de745 2224 if (io32->pfrio_esize != sizeof(struct pfr_addr)) {
316670eb
A
2225 error = ENODEV;
2226 break;
2227 }
2228 pfr_table_copyin_cleanup(&io32->pfrio_table);
2229 error = pfr_get_addrs(&io32->pfrio_table, io32->pfrio_buffer,
2230 &io32->pfrio_size, io32->pfrio_flags | PFR_FLAG_USERIOCTL);
2231 break;
2232
2233 case DIOCRGETASTATS:
0a7de745 2234 if (io32->pfrio_esize != sizeof(struct pfr_astats)) {
316670eb
A
2235 error = ENODEV;
2236 break;
2237 }
2238 pfr_table_copyin_cleanup(&io32->pfrio_table);
2239 error = pfr_get_astats(&io32->pfrio_table, io32->pfrio_buffer,
2240 &io32->pfrio_size, io32->pfrio_flags | PFR_FLAG_USERIOCTL);
2241 break;
2242
2243 case DIOCRCLRASTATS:
0a7de745 2244 if (io32->pfrio_esize != sizeof(struct pfr_addr)) {
316670eb
A
2245 error = ENODEV;
2246 break;
2247 }
2248 pfr_table_copyin_cleanup(&io32->pfrio_table);
2249 error = pfr_clr_astats(&io32->pfrio_table, io32->pfrio_buffer,
2250 io32->pfrio_size, &io32->pfrio_nzero, io32->pfrio_flags |
2251 PFR_FLAG_USERIOCTL);
2252 break;
2253
2254 case DIOCRTSTADDRS:
0a7de745 2255 if (io32->pfrio_esize != sizeof(struct pfr_addr)) {
316670eb
A
2256 error = ENODEV;
2257 break;
2258 }
2259 pfr_table_copyin_cleanup(&io32->pfrio_table);
2260 error = pfr_tst_addrs(&io32->pfrio_table, io32->pfrio_buffer,
2261 io32->pfrio_size, &io32->pfrio_nmatch, io32->pfrio_flags |
2262 PFR_FLAG_USERIOCTL);
2263 break;
2264
2265 case DIOCRINADEFINE:
0a7de745 2266 if (io32->pfrio_esize != sizeof(struct pfr_addr)) {
316670eb
A
2267 error = ENODEV;
2268 break;
2269 }
2270 pfr_table_copyin_cleanup(&io32->pfrio_table);
2271 error = pfr_ina_define(&io32->pfrio_table, io32->pfrio_buffer,
2272 io32->pfrio_size, &io32->pfrio_nadd, &io32->pfrio_naddr,
2273 io32->pfrio_ticket, io32->pfrio_flags | PFR_FLAG_USERIOCTL);
2274 break;
2275
2276 default:
2277 VERIFY(0);
2278 /* NOTREACHED */
2279 }
2280
2281done:
0a7de745 2282 return error;
316670eb
A
2283}
2284
2285static int
2286pfioctl_ioc_tokens(u_long cmd, struct pfioc_tokens_32 *tok32,
2287 struct pfioc_tokens_64 *tok64, struct proc *p)
2288{
2289 struct pfioc_token *tokens;
2290 struct pfioc_kernel_token *entry, *tmp;
2291 user_addr_t token_buf;
2292 int ocnt, cnt, error = 0, p64 = proc_is64bit(p);
2293 char *ptr;
2294
2295 switch (cmd) {
2296 case DIOCGETSTARTERS: {
2297 int size;
2298
2299 if (nr_tokens == 0) {
2300 error = ENOENT;
2301 break;
2302 }
2303
0a7de745 2304 size = sizeof(struct pfioc_token) * nr_tokens;
cb323159
A
2305 if (size / nr_tokens != sizeof(struct pfioc_token)) {
2306 os_log_error(OS_LOG_DEFAULT, "%s: size overflows", __func__);
2307 error = ERANGE;
2308 break;
2309 }
316670eb
A
2310 ocnt = cnt = (p64 ? tok64->size : tok32->size);
2311 if (cnt == 0) {
0a7de745 2312 if (p64) {
316670eb 2313 tok64->size = size;
0a7de745 2314 } else {
316670eb 2315 tok32->size = size;
0a7de745 2316 }
316670eb
A
2317 break;
2318 }
2319
2320 token_buf = (p64 ? tok64->pgt_buf : tok32->pgt_buf);
0a7de745 2321 tokens = _MALLOC(size, M_TEMP, M_WAITOK | M_ZERO);
316670eb
A
2322 if (tokens == NULL) {
2323 error = ENOMEM;
2324 break;
2325 }
2326
2327 ptr = (void *)tokens;
2328 SLIST_FOREACH_SAFE(entry, &token_list_head, next, tmp) {
2329 struct pfioc_token *t;
2330
0a7de745 2331 if ((unsigned)cnt < sizeof(*tokens)) {
316670eb 2332 break; /* no more buffer space left */
0a7de745 2333 }
316670eb 2334 t = (struct pfioc_token *)(void *)ptr;
0a7de745
A
2335 t->token_value = entry->token.token_value;
2336 t->timestamp = entry->token.timestamp;
2337 t->pid = entry->token.pid;
316670eb
A
2338 bcopy(entry->token.proc_name, t->proc_name,
2339 PFTOK_PROCNAME_LEN);
0a7de745 2340 ptr += sizeof(struct pfioc_token);
316670eb 2341
0a7de745 2342 cnt -= sizeof(struct pfioc_token);
316670eb
A
2343 }
2344
0a7de745 2345 if (cnt < ocnt) {
316670eb 2346 error = copyout(tokens, token_buf, ocnt - cnt);
0a7de745 2347 }
316670eb 2348
0a7de745 2349 if (p64) {
316670eb 2350 tok64->size = ocnt - cnt;
0a7de745 2351 } else {
316670eb 2352 tok32->size = ocnt - cnt;
0a7de745 2353 }
316670eb
A
2354
2355 _FREE(tokens, M_TEMP);
2356 break;
2357 }
2358
2359 default:
2360 VERIFY(0);
2361 /* NOTREACHED */
2362 }
2363
0a7de745 2364 return error;
316670eb
A
2365}
2366
2367static void
2368pf_expire_states_and_src_nodes(struct pf_rule *rule)
2369{
0a7de745
A
2370 struct pf_state *state;
2371 struct pf_src_node *sn;
2372 int killed = 0;
316670eb
A
2373
2374 /* expire the states */
2375 state = TAILQ_FIRST(&state_list);
2376 while (state) {
0a7de745 2377 if (state->rule.ptr == rule) {
316670eb 2378 state->timeout = PFTM_PURGE;
0a7de745 2379 }
316670eb
A
2380 state = TAILQ_NEXT(state, entry_list);
2381 }
2382 pf_purge_expired_states(pf_status.states);
2383
2384 /* expire the src_nodes */
2385 RB_FOREACH(sn, pf_src_tree, &tree_src_tracking) {
0a7de745 2386 if (sn->rule.ptr != rule) {
316670eb 2387 continue;
0a7de745 2388 }
316670eb
A
2389 if (sn->states != 0) {
2390 RB_FOREACH(state, pf_state_tree_id,
2391 &tree_id) {
0a7de745 2392 if (state->src_node == sn) {
316670eb 2393 state->src_node = NULL;
0a7de745
A
2394 }
2395 if (state->nat_src_node == sn) {
316670eb 2396 state->nat_src_node = NULL;
0a7de745 2397 }
316670eb
A
2398 }
2399 sn->states = 0;
b0d623f7 2400 }
316670eb
A
2401 sn->expire = 1;
2402 killed++;
2403 }
0a7de745 2404 if (killed) {
316670eb 2405 pf_purge_expired_src_nodes();
0a7de745 2406 }
316670eb 2407}
b0d623f7 2408
316670eb
A
2409static void
2410pf_delete_rule_from_ruleset(struct pf_ruleset *ruleset, int rs_num,
2411 struct pf_rule *rule)
2412{
2413 struct pf_rule *r;
2414 int nr = 0;
2415
2416 pf_expire_states_and_src_nodes(rule);
2417
2418 pf_rm_rule(ruleset->rules[rs_num].active.ptr, rule);
0a7de745 2419 if (ruleset->rules[rs_num].active.rcount-- == 0) {
316670eb 2420 panic("%s: rcount value broken!", __func__);
0a7de745 2421 }
316670eb
A
2422 r = TAILQ_FIRST(ruleset->rules[rs_num].active.ptr);
2423
2424 while (r) {
2425 r->nr = nr++;
2426 r = TAILQ_NEXT(r, entries);
2427 }
2428}
2429
2430
2431static void
2432pf_ruleset_cleanup(struct pf_ruleset *ruleset, int rs)
2433{
2434 pf_calc_skip_steps(ruleset->rules[rs].active.ptr);
2435 ruleset->rules[rs].active.ticket =
2436 ++ruleset->rules[rs].inactive.ticket;
2437}
2438
39236c6e
A
2439/*
2440 * req_dev encodes the PF interface. Currently, possible values are
2441 * 0 or PFRULE_PFM
2442 */
316670eb 2443static int
39236c6e 2444pf_delete_rule_by_ticket(struct pfioc_rule *pr, u_int32_t req_dev)
316670eb 2445{
0a7de745
A
2446 struct pf_ruleset *ruleset;
2447 struct pf_rule *rule = NULL;
2448 int is_anchor;
2449 int error;
2450 int i;
316670eb
A
2451
2452 is_anchor = (pr->anchor_call[0] != '\0');
2453 if ((ruleset = pf_find_ruleset_with_owner(pr->anchor,
0a7de745
A
2454 pr->rule.owner, is_anchor, &error)) == NULL) {
2455 return error;
2456 }
316670eb 2457
39236c6e
A
2458 for (i = 0; i < PF_RULESET_MAX && rule == NULL; i++) {
2459 rule = TAILQ_FIRST(ruleset->rules[i].active.ptr);
0a7de745 2460 while (rule && (rule->ticket != pr->rule.ticket)) {
316670eb 2461 rule = TAILQ_NEXT(rule, entries);
0a7de745 2462 }
39236c6e 2463 }
0a7de745
A
2464 if (rule == NULL) {
2465 return ENOENT;
2466 } else {
39236c6e 2467 i--;
0a7de745 2468 }
316670eb 2469
0a7de745
A
2470 if (strcmp(rule->owner, pr->rule.owner)) {
2471 return EACCES;
2472 }
316670eb
A
2473
2474delete_rule:
39236c6e
A
2475 if (rule->anchor && (ruleset != &pf_main_ruleset) &&
2476 ((strcmp(ruleset->anchor->owner, "")) == 0) &&
2477 ((ruleset->rules[i].active.rcount - 1) == 0)) {
2478 /* set rule & ruleset to parent and repeat */
2479 struct pf_rule *delete_rule = rule;
2480 struct pf_ruleset *delete_ruleset = ruleset;
316670eb 2481
0a7de745
A
2482#define parent_ruleset ruleset->anchor->parent->ruleset
2483 if (ruleset->anchor->parent == NULL) {
39236c6e 2484 ruleset = &pf_main_ruleset;
0a7de745 2485 } else {
39236c6e 2486 ruleset = &parent_ruleset;
0a7de745 2487 }
316670eb 2488
39236c6e
A
2489 rule = TAILQ_FIRST(ruleset->rules[i].active.ptr);
2490 while (rule &&
0a7de745 2491 (rule->anchor != delete_ruleset->anchor)) {
39236c6e 2492 rule = TAILQ_NEXT(rule, entries);
0a7de745
A
2493 }
2494 if (rule == NULL) {
39236c6e 2495 panic("%s: rule not found!", __func__);
0a7de745 2496 }
316670eb 2497
743345f9 2498 /*
39236c6e
A
2499 * if reqest device != rule's device, bail :
2500 * with error if ticket matches;
2501 * without error if ticket doesn't match (i.e. its just cleanup)
2502 */
2503 if ((rule->rule_flag & PFRULE_PFM) ^ req_dev) {
2504 if (rule->ticket != pr->rule.ticket) {
0a7de745 2505 return 0;
39236c6e
A
2506 } else {
2507 return EACCES;
2508 }
2509 }
316670eb 2510
39236c6e
A
2511 if (delete_rule->rule_flag & PFRULE_PFM) {
2512 pffwrules--;
b0d623f7 2513 }
39236c6e
A
2514
2515 pf_delete_rule_from_ruleset(delete_ruleset,
2516 i, delete_rule);
2517 delete_ruleset->rules[i].active.ticket =
2518 ++delete_ruleset->rules[i].inactive.ticket;
2519 goto delete_rule;
2520 } else {
743345f9 2521 /*
39236c6e
A
2522 * process deleting rule only if device that added the
2523 * rule matches device that issued the request
2524 */
0a7de745 2525 if ((rule->rule_flag & PFRULE_PFM) ^ req_dev) {
39236c6e 2526 return EACCES;
0a7de745
A
2527 }
2528 if (rule->rule_flag & PFRULE_PFM) {
39236c6e 2529 pffwrules--;
0a7de745 2530 }
39236c6e
A
2531 pf_delete_rule_from_ruleset(ruleset, i,
2532 rule);
2533 pf_ruleset_cleanup(ruleset, i);
316670eb
A
2534 }
2535
0a7de745 2536 return 0;
316670eb
A
2537}
2538
39236c6e
A
2539/*
2540 * req_dev encodes the PF interface. Currently, possible values are
2541 * 0 or PFRULE_PFM
2542 */
316670eb 2543static void
39236c6e 2544pf_delete_rule_by_owner(char *owner, u_int32_t req_dev)
316670eb 2545{
0a7de745
A
2546 struct pf_ruleset *ruleset;
2547 struct pf_rule *rule, *next;
2548 int deleted = 0;
316670eb
A
2549
2550 for (int rs = 0; rs < PF_RULESET_MAX; rs++) {
2551 rule = TAILQ_FIRST(pf_main_ruleset.rules[rs].active.ptr);
2552 ruleset = &pf_main_ruleset;
2553 while (rule) {
2554 next = TAILQ_NEXT(rule, entries);
743345f9 2555 /*
39236c6e
A
2556 * process deleting rule only if device that added the
2557 * rule matches device that issued the request
2558 */
2559 if ((rule->rule_flag & PFRULE_PFM) ^ req_dev) {
2560 rule = next;
2561 continue;
2562 }
316670eb
A
2563 if (rule->anchor) {
2564 if (((strcmp(rule->owner, owner)) == 0) ||
2565 ((strcmp(rule->owner, "")) == 0)) {
2566 if (rule->anchor->ruleset.rules[rs].active.rcount > 0) {
2567 if (deleted) {
2568 pf_ruleset_cleanup(ruleset, rs);
2569 deleted = 0;
2570 }
2571 /* step into anchor */
2572 ruleset =
2573 &rule->anchor->ruleset;
2574 rule = TAILQ_FIRST(ruleset->rules[rs].active.ptr);
2575 continue;
2576 } else {
2577 if (rule->rule_flag &
0a7de745 2578 PFRULE_PFM) {
316670eb 2579 pffwrules--;
0a7de745 2580 }
316670eb
A
2581 pf_delete_rule_from_ruleset(ruleset, rs, rule);
2582 deleted = 1;
2583 rule = next;
2584 }
0a7de745 2585 } else {
316670eb 2586 rule = next;
0a7de745 2587 }
316670eb
A
2588 } else {
2589 if (((strcmp(rule->owner, owner)) == 0)) {
2590 /* delete rule */
0a7de745 2591 if (rule->rule_flag & PFRULE_PFM) {
316670eb 2592 pffwrules--;
0a7de745 2593 }
316670eb
A
2594 pf_delete_rule_from_ruleset(ruleset,
2595 rs, rule);
2596 deleted = 1;
2597 }
2598 rule = next;
2599 }
2600 if (rule == NULL) {
2601 if (deleted) {
2602 pf_ruleset_cleanup(ruleset, rs);
2603 deleted = 0;
2604 }
0a7de745 2605 if (ruleset != &pf_main_ruleset) {
316670eb
A
2606 pf_deleterule_anchor_step_out(&ruleset,
2607 rs, &rule);
0a7de745 2608 }
316670eb
A
2609 }
2610 }
2611 }
2612}
2613
2614static void
2615pf_deleterule_anchor_step_out(struct pf_ruleset **ruleset_ptr,
2616 int rs, struct pf_rule **rule_ptr)
2617{
2618 struct pf_ruleset *ruleset = *ruleset_ptr;
2619 struct pf_rule *rule = *rule_ptr;
2620
2621 /* step out of anchor */
2622 struct pf_ruleset *rs_copy = ruleset;
2623 ruleset = ruleset->anchor->parent?
2624 &ruleset->anchor->parent->ruleset:&pf_main_ruleset;
2625
2626 rule = TAILQ_FIRST(ruleset->rules[rs].active.ptr);
0a7de745 2627 while (rule && (rule->anchor != rs_copy->anchor)) {
316670eb 2628 rule = TAILQ_NEXT(rule, entries);
0a7de745
A
2629 }
2630 if (rule == NULL) {
316670eb 2631 panic("%s: parent rule of anchor not found!", __func__);
0a7de745
A
2632 }
2633 if (rule->anchor->ruleset.rules[rs].active.rcount > 0) {
316670eb 2634 rule = TAILQ_NEXT(rule, entries);
0a7de745 2635 }
316670eb
A
2636
2637 *ruleset_ptr = ruleset;
2638 *rule_ptr = rule;
2639}
2640
39236c6e
A
2641static void
2642pf_addrwrap_setup(struct pf_addr_wrap *aw)
2643{
2644 VERIFY(aw);
2645 bzero(&aw->p, sizeof aw->p);
2646}
2647
316670eb
A
2648static int
2649pf_rule_setup(struct pfioc_rule *pr, struct pf_rule *rule,
0a7de745
A
2650 struct pf_ruleset *ruleset)
2651{
2652 struct pf_pooladdr *apa;
2653 int error = 0;
316670eb
A
2654
2655 if (rule->ifname[0]) {
2656 rule->kif = pfi_kif_get(rule->ifname);
2657 if (rule->kif == NULL) {
2658 pool_put(&pf_rule_pl, rule);
0a7de745 2659 return EINVAL;
316670eb
A
2660 }
2661 pfi_kif_ref(rule->kif, PFI_KIF_REF_RULE);
2662 }
0a7de745
A
2663 if (rule->tagname[0]) {
2664 if ((rule->tag = pf_tagname2tag(rule->tagname)) == 0) {
316670eb 2665 error = EBUSY;
0a7de745
A
2666 }
2667 }
2668 if (rule->match_tagname[0]) {
316670eb 2669 if ((rule->match_tag =
0a7de745 2670 pf_tagname2tag(rule->match_tagname)) == 0) {
316670eb 2671 error = EBUSY;
0a7de745
A
2672 }
2673 }
2674 if (rule->rt && !rule->direction) {
316670eb 2675 error = EINVAL;
0a7de745 2676 }
b0d623f7 2677#if PFLOG
0a7de745 2678 if (!rule->log) {
316670eb 2679 rule->logif = 0;
0a7de745
A
2680 }
2681 if (rule->logif >= PFLOGIFS_MAX) {
316670eb 2682 error = EINVAL;
0a7de745 2683 }
b0d623f7 2684#endif /* PFLOG */
39236c6e
A
2685 pf_addrwrap_setup(&rule->src.addr);
2686 pf_addrwrap_setup(&rule->dst.addr);
316670eb 2687 if (pf_rtlabel_add(&rule->src.addr) ||
0a7de745 2688 pf_rtlabel_add(&rule->dst.addr)) {
316670eb 2689 error = EBUSY;
0a7de745
A
2690 }
2691 if (pfi_dynaddr_setup(&rule->src.addr, rule->af)) {
316670eb 2692 error = EINVAL;
0a7de745
A
2693 }
2694 if (pfi_dynaddr_setup(&rule->dst.addr, rule->af)) {
316670eb 2695 error = EINVAL;
0a7de745
A
2696 }
2697 if (pf_tbladdr_setup(ruleset, &rule->src.addr)) {
316670eb 2698 error = EINVAL;
0a7de745
A
2699 }
2700 if (pf_tbladdr_setup(ruleset, &rule->dst.addr)) {
316670eb 2701 error = EINVAL;
0a7de745
A
2702 }
2703 if (pf_anchor_setup(rule, ruleset, pr->anchor_call)) {
316670eb 2704 error = EINVAL;
0a7de745 2705 }
316670eb 2706 TAILQ_FOREACH(apa, &pf_pabuf, entries)
0a7de745
A
2707 if (pf_tbladdr_setup(ruleset, &apa->addr)) {
2708 error = EINVAL;
2709 }
316670eb
A
2710
2711 if (rule->overload_tblname[0]) {
2712 if ((rule->overload_tbl = pfr_attach_table(ruleset,
0a7de745 2713 rule->overload_tblname)) == NULL) {
b0d623f7 2714 error = EINVAL;
0a7de745 2715 } else {
316670eb
A
2716 rule->overload_tbl->pfrkt_flags |=
2717 PFR_TFLAG_ACTIVE;
0a7de745 2718 }
316670eb
A
2719 }
2720
2721 pf_mv_pool(&pf_pabuf, &rule->rpool.list);
3e170ce0 2722
316670eb 2723 if (((((rule->action == PF_NAT) || (rule->action == PF_RDR) ||
3e170ce0
A
2724 (rule->action == PF_BINAT) || (rule->action == PF_NAT64)) &&
2725 rule->anchor == NULL) ||
316670eb 2726 (rule->rt > PF_FASTROUTE)) &&
0a7de745 2727 (TAILQ_FIRST(&rule->rpool.list) == NULL)) {
316670eb 2728 error = EINVAL;
0a7de745 2729 }
316670eb
A
2730
2731 if (error) {
2732 pf_rm_rule(NULL, rule);
0a7de745 2733 return error;
316670eb 2734 }
3e170ce0
A
2735 /* For a NAT64 rule the rule's address family is AF_INET6 whereas
2736 * the address pool's family will be AF_INET
2737 */
2738 rule->rpool.af = (rule->action == PF_NAT64) ? AF_INET: rule->af;
316670eb
A
2739 rule->rpool.cur = TAILQ_FIRST(&rule->rpool.list);
2740 rule->evaluations = rule->packets[0] = rule->packets[1] =
2741 rule->bytes[0] = rule->bytes[1] = 0;
2742
0a7de745 2743 return 0;
316670eb
A
2744}
2745
2746static int
2747pfioctl_ioc_rule(u_long cmd, int minordev, struct pfioc_rule *pr, struct proc *p)
2748{
2749 int error = 0;
39236c6e 2750 u_int32_t req_dev = 0;
316670eb
A
2751
2752 switch (cmd) {
2753 case DIOCADDRULE: {
0a7de745
A
2754 struct pf_ruleset *ruleset;
2755 struct pf_rule *rule, *tail;
2756 int rs_num;
316670eb 2757
0a7de745
A
2758 pr->anchor[sizeof(pr->anchor) - 1] = '\0';
2759 pr->anchor_call[sizeof(pr->anchor_call) - 1] = '\0';
316670eb
A
2760 ruleset = pf_find_ruleset(pr->anchor);
2761 if (ruleset == NULL) {
b0d623f7 2762 error = EINVAL;
316670eb
A
2763 break;
2764 }
2765 rs_num = pf_get_ruleset_number(pr->rule.action);
2766 if (rs_num >= PF_RULESET_MAX) {
b0d623f7 2767 error = EINVAL;
316670eb 2768 break;
b0d623f7 2769 }
316670eb 2770 if (pr->rule.return_icmp >> 8 > ICMP_MAXTYPE) {
b0d623f7 2771 error = EINVAL;
b0d623f7
A
2772 break;
2773 }
316670eb
A
2774 if (pr->ticket != ruleset->rules[rs_num].inactive.ticket) {
2775 error = EBUSY;
2776 break;
2777 }
2778 if (pr->pool_ticket != ticket_pabuf) {
2779 error = EBUSY;
2780 break;
2781 }
2782 rule = pool_get(&pf_rule_pl, PR_WAITOK);
2783 if (rule == NULL) {
2784 error = ENOMEM;
2785 break;
2786 }
2787 pf_rule_copyin(&pr->rule, rule, p, minordev);
2788#if !INET
2789 if (rule->af == AF_INET) {
2790 pool_put(&pf_rule_pl, rule);
2791 error = EAFNOSUPPORT;
2792 break;
2793 }
2794#endif /* INET */
2795#if !INET6
2796 if (rule->af == AF_INET6) {
2797 pool_put(&pf_rule_pl, rule);
2798 error = EAFNOSUPPORT;
2799 break;
2800 }
2801#endif /* INET6 */
2802 tail = TAILQ_LAST(ruleset->rules[rs_num].inactive.ptr,
2803 pf_rulequeue);
0a7de745 2804 if (tail) {
316670eb 2805 rule->nr = tail->nr + 1;
0a7de745 2806 } else {
316670eb 2807 rule->nr = 0;
0a7de745 2808 }
316670eb 2809
0a7de745 2810 if ((error = pf_rule_setup(pr, rule, ruleset))) {
316670eb 2811 break;
0a7de745 2812 }
316670eb 2813
b0d623f7
A
2814 TAILQ_INSERT_TAIL(ruleset->rules[rs_num].inactive.ptr,
2815 rule, entries);
2816 ruleset->rules[rs_num].inactive.rcount++;
0a7de745 2817 if (rule->rule_flag & PFRULE_PFM) {
316670eb 2818 pffwrules++;
0a7de745 2819 }
3e170ce0 2820
0a7de745 2821 if (rule->action == PF_NAT64) {
3e170ce0 2822 atomic_add_16(&pf_nat64_configured, 1);
0a7de745 2823 }
5ba3f43e
A
2824
2825 if (pr->anchor_call[0] == '\0') {
2826 INC_ATOMIC_INT64_LIM(net_api_stats.nas_pf_addrule_total);
2827 if (rule->rule_flag & PFRULE_PFM) {
2828 INC_ATOMIC_INT64_LIM(net_api_stats.nas_pf_addrule_os);
2829 }
2830 }
2831
2832#if DUMMYNET
2833 if (rule->action == PF_DUMMYNET) {
2834 struct dummynet_event dn_event;
2835 uint32_t direction = DN_INOUT;;
2836 bzero(&dn_event, sizeof(dn_event));
2837
2838 dn_event.dn_event_code = DUMMYNET_RULE_CONFIG;
2839
0a7de745 2840 if (rule->direction == PF_IN) {
5ba3f43e 2841 direction = DN_IN;
0a7de745 2842 } else if (rule->direction == PF_OUT) {
5ba3f43e 2843 direction = DN_OUT;
0a7de745 2844 }
5ba3f43e
A
2845
2846 dn_event.dn_event_rule_config.dir = direction;
2847 dn_event.dn_event_rule_config.af = rule->af;
2848 dn_event.dn_event_rule_config.proto = rule->proto;
2849 dn_event.dn_event_rule_config.src_port = rule->src.xport.range.port[0];
2850 dn_event.dn_event_rule_config.dst_port = rule->dst.xport.range.port[0];
2851 strlcpy(dn_event.dn_event_rule_config.ifname, rule->ifname,
2852 sizeof(dn_event.dn_event_rule_config.ifname));
2853
2854 dummynet_event_enqueue_nwk_wq_entry(&dn_event);
2855 }
2856#endif
b0d623f7
A
2857 break;
2858 }
2859
2860 case DIOCGETRULES: {
0a7de745
A
2861 struct pf_ruleset *ruleset;
2862 struct pf_rule *tail;
2863 int rs_num;
b0d623f7 2864
0a7de745
A
2865 pr->anchor[sizeof(pr->anchor) - 1] = '\0';
2866 pr->anchor_call[sizeof(pr->anchor_call) - 1] = '\0';
b0d623f7
A
2867 ruleset = pf_find_ruleset(pr->anchor);
2868 if (ruleset == NULL) {
2869 error = EINVAL;
2870 break;
2871 }
2872 rs_num = pf_get_ruleset_number(pr->rule.action);
2873 if (rs_num >= PF_RULESET_MAX) {
2874 error = EINVAL;
2875 break;
2876 }
2877 tail = TAILQ_LAST(ruleset->rules[rs_num].active.ptr,
2878 pf_rulequeue);
0a7de745 2879 if (tail) {
b0d623f7 2880 pr->nr = tail->nr + 1;
0a7de745 2881 } else {
b0d623f7 2882 pr->nr = 0;
0a7de745 2883 }
b0d623f7
A
2884 pr->ticket = ruleset->rules[rs_num].active.ticket;
2885 break;
2886 }
2887
2888 case DIOCGETRULE: {
0a7de745
A
2889 struct pf_ruleset *ruleset;
2890 struct pf_rule *rule;
2891 int rs_num, i;
b0d623f7 2892
0a7de745
A
2893 pr->anchor[sizeof(pr->anchor) - 1] = '\0';
2894 pr->anchor_call[sizeof(pr->anchor_call) - 1] = '\0';
b0d623f7
A
2895 ruleset = pf_find_ruleset(pr->anchor);
2896 if (ruleset == NULL) {
2897 error = EINVAL;
2898 break;
2899 }
2900 rs_num = pf_get_ruleset_number(pr->rule.action);
2901 if (rs_num >= PF_RULESET_MAX) {
2902 error = EINVAL;
2903 break;
2904 }
2905 if (pr->ticket != ruleset->rules[rs_num].active.ticket) {
2906 error = EBUSY;
2907 break;
2908 }
2909 rule = TAILQ_FIRST(ruleset->rules[rs_num].active.ptr);
0a7de745 2910 while ((rule != NULL) && (rule->nr != pr->nr)) {
b0d623f7 2911 rule = TAILQ_NEXT(rule, entries);
0a7de745 2912 }
b0d623f7
A
2913 if (rule == NULL) {
2914 error = EBUSY;
2915 break;
2916 }
6d2010ae 2917 pf_rule_copyout(rule, &pr->rule);
b0d623f7
A
2918 if (pf_anchor_copyout(ruleset, rule, pr)) {
2919 error = EBUSY;
2920 break;
2921 }
2922 pfi_dynaddr_copyout(&pr->rule.src.addr);
2923 pfi_dynaddr_copyout(&pr->rule.dst.addr);
2924 pf_tbladdr_copyout(&pr->rule.src.addr);
2925 pf_tbladdr_copyout(&pr->rule.dst.addr);
2926 pf_rtlabel_copyout(&pr->rule.src.addr);
2927 pf_rtlabel_copyout(&pr->rule.dst.addr);
0a7de745
A
2928 for (i = 0; i < PF_SKIP_COUNT; ++i) {
2929 if (rule->skip[i].ptr == NULL) {
b0d623f7 2930 pr->rule.skip[i].nr = -1;
0a7de745 2931 } else {
b0d623f7
A
2932 pr->rule.skip[i].nr =
2933 rule->skip[i].ptr->nr;
0a7de745
A
2934 }
2935 }
b0d623f7
A
2936
2937 if (pr->action == PF_GET_CLR_CNTR) {
2938 rule->evaluations = 0;
2939 rule->packets[0] = rule->packets[1] = 0;
2940 rule->bytes[0] = rule->bytes[1] = 0;
2941 }
2942 break;
2943 }
2944
2945 case DIOCCHANGERULE: {
0a7de745
A
2946 struct pfioc_rule *pcr = pr;
2947 struct pf_ruleset *ruleset;
2948 struct pf_rule *oldrule = NULL, *newrule = NULL;
2949 struct pf_pooladdr *pa;
2950 u_int32_t nr = 0;
2951 int rs_num;
b0d623f7
A
2952
2953 if (!(pcr->action == PF_CHANGE_REMOVE ||
2954 pcr->action == PF_CHANGE_GET_TICKET) &&
2955 pcr->pool_ticket != ticket_pabuf) {
2956 error = EBUSY;
2957 break;
2958 }
2959
2960 if (pcr->action < PF_CHANGE_ADD_HEAD ||
2961 pcr->action > PF_CHANGE_GET_TICKET) {
2962 error = EINVAL;
2963 break;
2964 }
0a7de745
A
2965 pcr->anchor[sizeof(pcr->anchor) - 1] = '\0';
2966 pcr->anchor_call[sizeof(pcr->anchor_call) - 1] = '\0';
b0d623f7
A
2967 ruleset = pf_find_ruleset(pcr->anchor);
2968 if (ruleset == NULL) {
2969 error = EINVAL;
2970 break;
2971 }
2972 rs_num = pf_get_ruleset_number(pcr->rule.action);
2973 if (rs_num >= PF_RULESET_MAX) {
2974 error = EINVAL;
2975 break;
2976 }
2977
2978 if (pcr->action == PF_CHANGE_GET_TICKET) {
2979 pcr->ticket = ++ruleset->rules[rs_num].active.ticket;
2980 break;
2981 } else {
2982 if (pcr->ticket !=
2983 ruleset->rules[rs_num].active.ticket) {
2984 error = EINVAL;
2985 break;
2986 }
2987 if (pcr->rule.return_icmp >> 8 > ICMP_MAXTYPE) {
2988 error = EINVAL;
2989 break;
2990 }
2991 }
2992
2993 if (pcr->action != PF_CHANGE_REMOVE) {
2994 newrule = pool_get(&pf_rule_pl, PR_WAITOK);
2995 if (newrule == NULL) {
2996 error = ENOMEM;
2997 break;
2998 }
316670eb 2999 pf_rule_copyin(&pcr->rule, newrule, p, minordev);
b0d623f7
A
3000#if !INET
3001 if (newrule->af == AF_INET) {
3002 pool_put(&pf_rule_pl, newrule);
3003 error = EAFNOSUPPORT;
3004 break;
3005 }
3006#endif /* INET */
3007#if !INET6
3008 if (newrule->af == AF_INET6) {
3009 pool_put(&pf_rule_pl, newrule);
3010 error = EAFNOSUPPORT;
3011 break;
3012 }
3013#endif /* INET6 */
3014 if (newrule->ifname[0]) {
3015 newrule->kif = pfi_kif_get(newrule->ifname);
3016 if (newrule->kif == NULL) {
3017 pool_put(&pf_rule_pl, newrule);
3018 error = EINVAL;
3019 break;
3020 }
3021 pfi_kif_ref(newrule->kif, PFI_KIF_REF_RULE);
0a7de745 3022 } else {
b0d623f7 3023 newrule->kif = NULL;
0a7de745 3024 }
b0d623f7 3025
0a7de745 3026 if (newrule->tagname[0]) {
b0d623f7 3027 if ((newrule->tag =
0a7de745 3028 pf_tagname2tag(newrule->tagname)) == 0) {
b0d623f7 3029 error = EBUSY;
0a7de745
A
3030 }
3031 }
3032 if (newrule->match_tagname[0]) {
b0d623f7 3033 if ((newrule->match_tag = pf_tagname2tag(
0a7de745 3034 newrule->match_tagname)) == 0) {
b0d623f7 3035 error = EBUSY;
0a7de745
A
3036 }
3037 }
3038 if (newrule->rt && !newrule->direction) {
b0d623f7 3039 error = EINVAL;
0a7de745 3040 }
b0d623f7 3041#if PFLOG
0a7de745 3042 if (!newrule->log) {
b0d623f7 3043 newrule->logif = 0;
0a7de745
A
3044 }
3045 if (newrule->logif >= PFLOGIFS_MAX) {
b0d623f7 3046 error = EINVAL;
0a7de745 3047 }
b0d623f7 3048#endif /* PFLOG */
39236c6e
A
3049 pf_addrwrap_setup(&newrule->src.addr);
3050 pf_addrwrap_setup(&newrule->dst.addr);
b0d623f7 3051 if (pf_rtlabel_add(&newrule->src.addr) ||
0a7de745 3052 pf_rtlabel_add(&newrule->dst.addr)) {
b0d623f7 3053 error = EBUSY;
0a7de745
A
3054 }
3055 if (pfi_dynaddr_setup(&newrule->src.addr, newrule->af)) {
b0d623f7 3056 error = EINVAL;
0a7de745
A
3057 }
3058 if (pfi_dynaddr_setup(&newrule->dst.addr, newrule->af)) {
b0d623f7 3059 error = EINVAL;
0a7de745
A
3060 }
3061 if (pf_tbladdr_setup(ruleset, &newrule->src.addr)) {
b0d623f7 3062 error = EINVAL;
0a7de745
A
3063 }
3064 if (pf_tbladdr_setup(ruleset, &newrule->dst.addr)) {
b0d623f7 3065 error = EINVAL;
0a7de745
A
3066 }
3067 if (pf_anchor_setup(newrule, ruleset, pcr->anchor_call)) {
b0d623f7 3068 error = EINVAL;
0a7de745 3069 }
b0d623f7 3070 TAILQ_FOREACH(pa, &pf_pabuf, entries)
0a7de745
A
3071 if (pf_tbladdr_setup(ruleset, &pa->addr)) {
3072 error = EINVAL;
3073 }
b0d623f7
A
3074
3075 if (newrule->overload_tblname[0]) {
3076 if ((newrule->overload_tbl = pfr_attach_table(
0a7de745
A
3077 ruleset, newrule->overload_tblname)) ==
3078 NULL) {
b0d623f7 3079 error = EINVAL;
0a7de745 3080 } else {
b0d623f7
A
3081 newrule->overload_tbl->pfrkt_flags |=
3082 PFR_TFLAG_ACTIVE;
0a7de745 3083 }
b0d623f7
A
3084 }
3085
3086 pf_mv_pool(&pf_pabuf, &newrule->rpool.list);
3087 if (((((newrule->action == PF_NAT) ||
3088 (newrule->action == PF_RDR) ||
3089 (newrule->action == PF_BINAT) ||
3090 (newrule->rt > PF_FASTROUTE)) &&
3091 !newrule->anchor)) &&
0a7de745 3092 (TAILQ_FIRST(&newrule->rpool.list) == NULL)) {
b0d623f7 3093 error = EINVAL;
0a7de745 3094 }
b0d623f7
A
3095
3096 if (error) {
3097 pf_rm_rule(NULL, newrule);
3098 break;
3099 }
3100 newrule->rpool.cur = TAILQ_FIRST(&newrule->rpool.list);
3101 newrule->evaluations = 0;
3102 newrule->packets[0] = newrule->packets[1] = 0;
3103 newrule->bytes[0] = newrule->bytes[1] = 0;
3104 }
3105 pf_empty_pool(&pf_pabuf);
3106
0a7de745 3107 if (pcr->action == PF_CHANGE_ADD_HEAD) {
b0d623f7 3108 oldrule = TAILQ_FIRST(
0a7de745
A
3109 ruleset->rules[rs_num].active.ptr);
3110 } else if (pcr->action == PF_CHANGE_ADD_TAIL) {
b0d623f7 3111 oldrule = TAILQ_LAST(
0a7de745
A
3112 ruleset->rules[rs_num].active.ptr, pf_rulequeue);
3113 } else {
b0d623f7 3114 oldrule = TAILQ_FIRST(
0a7de745
A
3115 ruleset->rules[rs_num].active.ptr);
3116 while ((oldrule != NULL) && (oldrule->nr != pcr->nr)) {
b0d623f7 3117 oldrule = TAILQ_NEXT(oldrule, entries);
0a7de745 3118 }
b0d623f7 3119 if (oldrule == NULL) {
0a7de745 3120 if (newrule != NULL) {
b0d623f7 3121 pf_rm_rule(NULL, newrule);
0a7de745 3122 }
b0d623f7
A
3123 error = EINVAL;
3124 break;
3125 }
3126 }
3127
3128 if (pcr->action == PF_CHANGE_REMOVE) {
3129 pf_rm_rule(ruleset->rules[rs_num].active.ptr, oldrule);
3130 ruleset->rules[rs_num].active.rcount--;
3131 } else {
0a7de745 3132 if (oldrule == NULL) {
b0d623f7 3133 TAILQ_INSERT_TAIL(
0a7de745
A
3134 ruleset->rules[rs_num].active.ptr,
3135 newrule, entries);
3136 } else if (pcr->action == PF_CHANGE_ADD_HEAD ||
3137 pcr->action == PF_CHANGE_ADD_BEFORE) {
b0d623f7 3138 TAILQ_INSERT_BEFORE(oldrule, newrule, entries);
0a7de745 3139 } else {
b0d623f7 3140 TAILQ_INSERT_AFTER(
0a7de745
A
3141 ruleset->rules[rs_num].active.ptr,
3142 oldrule, newrule, entries);
3143 }
b0d623f7
A
3144 ruleset->rules[rs_num].active.rcount++;
3145 }
3146
3147 nr = 0;
3148 TAILQ_FOREACH(oldrule,
3149 ruleset->rules[rs_num].active.ptr, entries)
0a7de745 3150 oldrule->nr = nr++;
b0d623f7
A
3151
3152 ruleset->rules[rs_num].active.ticket++;
3153
3154 pf_calc_skip_steps(ruleset->rules[rs_num].active.ptr);
3155 pf_remove_if_empty_ruleset(ruleset);
3156
3157 break;
3158 }
3159
316670eb 3160 case DIOCINSERTRULE: {
0a7de745
A
3161 struct pf_ruleset *ruleset;
3162 struct pf_rule *rule, *tail, *r;
3163 int rs_num;
3164 int is_anchor;
316670eb 3165
0a7de745
A
3166 pr->anchor[sizeof(pr->anchor) - 1] = '\0';
3167 pr->anchor_call[sizeof(pr->anchor_call) - 1] = '\0';
316670eb
A
3168 is_anchor = (pr->anchor_call[0] != '\0');
3169
3170 if ((ruleset = pf_find_ruleset_with_owner(pr->anchor,
0a7de745 3171 pr->rule.owner, is_anchor, &error)) == NULL) {
316670eb 3172 break;
0a7de745 3173 }
316670eb
A
3174
3175 rs_num = pf_get_ruleset_number(pr->rule.action);
3176 if (rs_num >= PF_RULESET_MAX) {
3177 error = EINVAL;
3178 break;
3179 }
3180 if (pr->rule.return_icmp >> 8 > ICMP_MAXTYPE) {
3181 error = EINVAL;
3182 break;
3183 }
3184
3185 /* make sure this anchor rule doesn't exist already */
3186 if (is_anchor) {
3187 r = TAILQ_FIRST(ruleset->rules[rs_num].active.ptr);
3188 while (r) {
3189 if (r->anchor &&
3190 ((strcmp(r->anchor->name,
3191 pr->anchor_call)) == 0)) {
3192 if (((strcmp(pr->rule.owner,
3193 r->owner)) == 0) ||
0a7de745 3194 ((strcmp(r->owner, "")) == 0)) {
316670eb 3195 error = EEXIST;
0a7de745 3196 } else {
316670eb 3197 error = EPERM;
0a7de745 3198 }
316670eb
A
3199 break;
3200 }
3201 r = TAILQ_NEXT(r, entries);
3202 }
0a7de745
A
3203 if (error != 0) {
3204 return error;
3205 }
316670eb
A
3206 }
3207
3208 rule = pool_get(&pf_rule_pl, PR_WAITOK);
3209 if (rule == NULL) {
3210 error = ENOMEM;
3211 break;
3212 }
3213 pf_rule_copyin(&pr->rule, rule, p, minordev);
3214#if !INET
3215 if (rule->af == AF_INET) {
3216 pool_put(&pf_rule_pl, rule);
3217 error = EAFNOSUPPORT;
3218 break;
3219 }
3220#endif /* INET */
3221#if !INET6
3222 if (rule->af == AF_INET6) {
3223 pool_put(&pf_rule_pl, rule);
3224 error = EAFNOSUPPORT;
3225 break;
3226 }
3227
3228#endif /* INET6 */
3229 r = TAILQ_FIRST(ruleset->rules[rs_num].active.ptr);
0a7de745 3230 while ((r != NULL) && (rule->priority >= (unsigned)r->priority)) {
316670eb 3231 r = TAILQ_NEXT(r, entries);
0a7de745 3232 }
316670eb
A
3233 if (r == NULL) {
3234 if ((tail =
3235 TAILQ_LAST(ruleset->rules[rs_num].active.ptr,
0a7de745 3236 pf_rulequeue)) != NULL) {
316670eb 3237 rule->nr = tail->nr + 1;
0a7de745 3238 } else {
316670eb 3239 rule->nr = 0;
0a7de745 3240 }
316670eb
A
3241 } else {
3242 rule->nr = r->nr;
3243 }
3244
0a7de745 3245 if ((error = pf_rule_setup(pr, rule, ruleset))) {
316670eb 3246 break;
0a7de745 3247 }
316670eb 3248
0a7de745 3249 if (rule->anchor != NULL) {
fe8ab488 3250 strlcpy(rule->anchor->owner, rule->owner,
316670eb 3251 PF_OWNER_NAME_SIZE);
0a7de745 3252 }
316670eb
A
3253
3254 if (r) {
3255 TAILQ_INSERT_BEFORE(r, rule, entries);
0a7de745 3256 while (r && ++r->nr) {
316670eb 3257 r = TAILQ_NEXT(r, entries);
0a7de745
A
3258 }
3259 } else {
316670eb
A
3260 TAILQ_INSERT_TAIL(ruleset->rules[rs_num].active.ptr,
3261 rule, entries);
0a7de745 3262 }
316670eb
A
3263 ruleset->rules[rs_num].active.rcount++;
3264
3265 /* Calculate checksum for the main ruleset */
0a7de745 3266 if (ruleset == &pf_main_ruleset) {
316670eb 3267 error = pf_setup_pfsync_matching(ruleset);
0a7de745 3268 }
316670eb
A
3269
3270 pf_ruleset_cleanup(ruleset, rs_num);
39236c6e 3271 rule->ticket = VM_KERNEL_ADDRPERM((u_int64_t)(uintptr_t)rule);
316670eb
A
3272
3273 pr->rule.ticket = rule->ticket;
3274 pf_rule_copyout(rule, &pr->rule);
0a7de745 3275 if (rule->rule_flag & PFRULE_PFM) {
316670eb 3276 pffwrules++;
0a7de745
A
3277 }
3278 if (rule->action == PF_NAT64) {
3e170ce0 3279 atomic_add_16(&pf_nat64_configured, 1);
0a7de745 3280 }
5ba3f43e
A
3281
3282 if (pr->anchor_call[0] == '\0') {
3283 INC_ATOMIC_INT64_LIM(net_api_stats.nas_pf_addrule_total);
3284 if (rule->rule_flag & PFRULE_PFM) {
3285 INC_ATOMIC_INT64_LIM(net_api_stats.nas_pf_addrule_os);
3286 }
3287 }
316670eb
A
3288 break;
3289 }
3290
3291 case DIOCDELETERULE: {
0a7de745
A
3292 pr->anchor[sizeof(pr->anchor) - 1] = '\0';
3293 pr->anchor_call[sizeof(pr->anchor_call) - 1] = '\0';
316670eb
A
3294
3295 if (pr->rule.return_icmp >> 8 > ICMP_MAXTYPE) {
3296 error = EINVAL;
3297 break;
3298 }
3299
39236c6e 3300 /* get device through which request is made */
0a7de745 3301 if ((uint8_t)minordev == PFDEV_PFM) {
39236c6e 3302 req_dev |= PFRULE_PFM;
0a7de745 3303 }
39236c6e 3304
316670eb 3305 if (pr->rule.ticket) {
0a7de745 3306 if ((error = pf_delete_rule_by_ticket(pr, req_dev))) {
316670eb 3307 break;
0a7de745
A
3308 }
3309 } else {
39236c6e 3310 pf_delete_rule_by_owner(pr->rule.owner, req_dev);
0a7de745 3311 }
316670eb 3312 pr->nr = pffwrules;
0a7de745 3313 if (pr->rule.action == PF_NAT64) {
3e170ce0 3314 atomic_add_16(&pf_nat64_configured, -1);
0a7de745 3315 }
316670eb
A
3316 break;
3317 }
3318
3319 default:
3320 VERIFY(0);
3321 /* NOTREACHED */
3322 }
3323
0a7de745 3324 return error;
316670eb
A
3325}
3326
3327static int
3328pfioctl_ioc_state_kill(u_long cmd, struct pfioc_state_kill *psk, struct proc *p)
3329{
3330#pragma unused(p)
3331 int error = 0;
3332
0a7de745 3333 psk->psk_ifname[sizeof(psk->psk_ifname) - 1] = '\0';
fe8ab488
A
3334 psk->psk_ownername[sizeof(psk->psk_ownername) - 1] = '\0';
3335
3336 bool ifname_matched = true;
3337 bool owner_matched = true;
3338
316670eb 3339 switch (cmd) {
b0d623f7 3340 case DIOCCLRSTATES: {
0a7de745
A
3341 struct pf_state *s, *nexts;
3342 int killed = 0;
b0d623f7
A
3343
3344 for (s = RB_MIN(pf_state_tree_id, &tree_id); s; s = nexts) {
3345 nexts = RB_NEXT(pf_state_tree_id, &tree_id, s);
fe8ab488
A
3346 /*
3347 * Purge all states only when neither ifname
3348 * or owner is provided. If any of these are provided
3349 * we purge only the states with meta data that match
3350 */
3351 bool unlink_state = false;
3352 ifname_matched = true;
3353 owner_matched = true;
3354
3355 if (psk->psk_ifname[0] &&
3356 strcmp(psk->psk_ifname, s->kif->pfik_name)) {
3357 ifname_matched = false;
3358 }
3359
3360 if (psk->psk_ownername[0] &&
3361 ((NULL == s->rule.ptr) ||
0a7de745 3362 strcmp(psk->psk_ownername, s->rule.ptr->owner))) {
fe8ab488
A
3363 owner_matched = false;
3364 }
b0d623f7 3365
fe8ab488
A
3366 unlink_state = ifname_matched && owner_matched;
3367
3368 if (unlink_state) {
b0d623f7
A
3369#if NPFSYNC
3370 /* don't send out individual delete messages */
3371 s->sync_flags = PFSTATE_NOSYNC;
3372#endif
3373 pf_unlink_state(s);
3374 killed++;
3375 }
3376 }
3377 psk->psk_af = killed;
3378#if NPFSYNC
3379 pfsync_clear_states(pf_status.hostid, psk->psk_ifname);
3380#endif
3381 break;
3382 }
3383
3384 case DIOCKILLSTATES: {
0a7de745
A
3385 struct pf_state *s, *nexts;
3386 struct pf_state_key *sk;
3387 struct pf_state_host *src, *dst;
3388 int killed = 0;
b0d623f7
A
3389
3390 for (s = RB_MIN(pf_state_tree_id, &tree_id); s;
3391 s = nexts) {
3392 nexts = RB_NEXT(pf_state_tree_id, &tree_id, s);
3393 sk = s->state_key;
fe8ab488
A
3394 ifname_matched = true;
3395 owner_matched = true;
3396
3397 if (psk->psk_ifname[0] &&
3398 strcmp(psk->psk_ifname, s->kif->pfik_name)) {
3399 ifname_matched = false;
3400 }
3401
3402 if (psk->psk_ownername[0] &&
3403 ((NULL == s->rule.ptr) ||
0a7de745 3404 strcmp(psk->psk_ownername, s->rule.ptr->owner))) {
fe8ab488
A
3405 owner_matched = false;
3406 }
b0d623f7
A
3407
3408 if (sk->direction == PF_OUT) {
3409 src = &sk->lan;
3e170ce0 3410 dst = &sk->ext_lan;
b0d623f7 3411 } else {
3e170ce0 3412 src = &sk->ext_lan;
b0d623f7
A
3413 dst = &sk->lan;
3414 }
3e170ce0 3415 if ((!psk->psk_af || sk->af_lan == psk->psk_af) &&
b0d623f7
A
3416 (!psk->psk_proto || psk->psk_proto == sk->proto) &&
3417 PF_MATCHA(psk->psk_src.neg,
3418 &psk->psk_src.addr.v.a.addr,
3419 &psk->psk_src.addr.v.a.mask,
3e170ce0 3420 &src->addr, sk->af_lan) &&
b0d623f7
A
3421 PF_MATCHA(psk->psk_dst.neg,
3422 &psk->psk_dst.addr.v.a.addr,
3423 &psk->psk_dst.addr.v.a.mask,
3e170ce0 3424 &dst->addr, sk->af_lan) &&
b0d623f7
A
3425 (pf_match_xport(psk->psk_proto,
3426 psk->psk_proto_variant, &psk->psk_src.xport,
3427 &src->xport)) &&
3428 (pf_match_xport(psk->psk_proto,
3429 psk->psk_proto_variant, &psk->psk_dst.xport,
3430 &dst->xport)) &&
fe8ab488
A
3431 ifname_matched &&
3432 owner_matched) {
b0d623f7
A
3433#if NPFSYNC
3434 /* send immediate delete of state */
3435 pfsync_delete_state(s);
3436 s->sync_flags |= PFSTATE_NOSYNC;
3437#endif
3438 pf_unlink_state(s);
3439 killed++;
3440 }
3441 }
3442 psk->psk_af = killed;
3443 break;
3444 }
3445
316670eb
A
3446 default:
3447 VERIFY(0);
3448 /* NOTREACHED */
3449 }
3450
0a7de745 3451 return error;
316670eb
A
3452}
3453
3454static int
3455pfioctl_ioc_state(u_long cmd, struct pfioc_state *ps, struct proc *p)
3456{
3457#pragma unused(p)
3458 int error = 0;
3459
3460 switch (cmd) {
b0d623f7 3461 case DIOCADDSTATE: {
0a7de745
A
3462 struct pfsync_state *sp = &ps->state;
3463 struct pf_state *s;
3464 struct pf_state_key *sk;
3465 struct pfi_kif *kif;
b0d623f7 3466
fe8ab488 3467 if (sp->timeout >= PFTM_MAX) {
b0d623f7
A
3468 error = EINVAL;
3469 break;
3470 }
3471 s = pool_get(&pf_state_pl, PR_WAITOK);
3472 if (s == NULL) {
3473 error = ENOMEM;
3474 break;
3475 }
0a7de745 3476 bzero(s, sizeof(struct pf_state));
316670eb 3477 if ((sk = pf_alloc_state_key(s, NULL)) == NULL) {
b0d623f7
A
3478 pool_put(&pf_state_pl, s);
3479 error = ENOMEM;
3480 break;
3481 }
3482 pf_state_import(sp, sk, s);
3483 kif = pfi_kif_get(sp->ifname);
3484 if (kif == NULL) {
3485 pool_put(&pf_state_pl, s);
3486 pool_put(&pf_state_key_pl, sk);
3487 error = ENOENT;
3488 break;
3489 }
b0d623f7
A
3490 TAILQ_INIT(&s->unlink_hooks);
3491 s->state_key->app_state = 0;
b0d623f7
A
3492 if (pf_insert_state(kif, s)) {
3493 pfi_kif_unref(kif, PFI_KIF_REF_NONE);
3494 pool_put(&pf_state_pl, s);
3495 error = EEXIST;
3496 break;
3497 }
3498 pf_default_rule.states++;
b7266188 3499 VERIFY(pf_default_rule.states != 0);
b0d623f7
A
3500 break;
3501 }
3502
3503 case DIOCGETSTATE: {
0a7de745
A
3504 struct pf_state *s;
3505 struct pf_state_cmp id_key;
b0d623f7 3506
0a7de745 3507 bcopy(ps->state.id, &id_key.id, sizeof(id_key.id));
b0d623f7
A
3508 id_key.creatorid = ps->state.creatorid;
3509
3510 s = pf_find_state_byid(&id_key);
3511 if (s == NULL) {
3512 error = ENOENT;
3513 break;
3514 }
3515
3516 pf_state_export(&ps->state, s->state_key, s);
3517 break;
3518 }
3519
316670eb
A
3520 default:
3521 VERIFY(0);
3522 /* NOTREACHED */
3523 }
3524
0a7de745 3525 return error;
316670eb
A
3526}
3527
3528static int
3529pfioctl_ioc_states(u_long cmd, struct pfioc_states_32 *ps32,
3530 struct pfioc_states_64 *ps64, struct proc *p)
3531{
3532 int p64 = proc_is64bit(p);
3533 int error = 0;
3534
3535 switch (cmd) {
0a7de745
A
3536 case DIOCGETSTATES: { /* struct pfioc_states */
3537 struct pf_state *state;
3538 struct pfsync_state *pstore;
3539 user_addr_t buf;
3540 u_int32_t nr = 0;
3541 int len, size;
b0d623f7 3542
316670eb
A
3543 len = (p64 ? ps64->ps_len : ps32->ps_len);
3544 if (len == 0) {
0a7de745
A
3545 size = sizeof(struct pfsync_state) * pf_status.states;
3546 if (p64) {
316670eb 3547 ps64->ps_len = size;
0a7de745 3548 } else {
316670eb 3549 ps32->ps_len = size;
0a7de745 3550 }
b0d623f7
A
3551 break;
3552 }
3553
0a7de745 3554 pstore = _MALLOC(sizeof(*pstore), M_TEMP, M_WAITOK | M_ZERO);
316670eb
A
3555 if (pstore == NULL) {
3556 error = ENOMEM;
3557 break;
3558 }
3559 buf = (p64 ? ps64->ps_buf : ps32->ps_buf);
b0d623f7
A
3560
3561 state = TAILQ_FIRST(&state_list);
3562 while (state) {
3563 if (state->timeout != PFTM_UNLINKED) {
0a7de745 3564 if ((nr + 1) * sizeof(*pstore) > (unsigned)len) {
b0d623f7 3565 break;
0a7de745 3566 }
b0d623f7
A
3567
3568 pf_state_export(pstore,
3569 state->state_key, state);
0a7de745 3570 error = copyout(pstore, buf, sizeof(*pstore));
b0d623f7
A
3571 if (error) {
3572 _FREE(pstore, M_TEMP);
3573 goto fail;
3574 }
0a7de745 3575 buf += sizeof(*pstore);
b0d623f7
A
3576 nr++;
3577 }
3578 state = TAILQ_NEXT(state, entry_list);
3579 }
3580
0a7de745
A
3581 size = sizeof(struct pfsync_state) * nr;
3582 if (p64) {
316670eb 3583 ps64->ps_len = size;
0a7de745 3584 } else {
316670eb 3585 ps32->ps_len = size;
0a7de745 3586 }
b0d623f7
A
3587
3588 _FREE(pstore, M_TEMP);
3589 break;
3590 }
3591
316670eb
A
3592 default:
3593 VERIFY(0);
3594 /* NOTREACHED */
b0d623f7 3595 }
316670eb 3596fail:
0a7de745 3597 return error;
316670eb 3598}
b0d623f7 3599
316670eb
A
3600static int
3601pfioctl_ioc_natlook(u_long cmd, struct pfioc_natlook *pnl, struct proc *p)
3602{
3603#pragma unused(p)
3604 int error = 0;
b0d623f7 3605
316670eb 3606 switch (cmd) {
b0d623f7 3607 case DIOCNATLOOK: {
0a7de745
A
3608 struct pf_state_key *sk;
3609 struct pf_state *state;
3610 struct pf_state_key_cmp key;
3611 int m = 0, direction = pnl->direction;
b0d623f7 3612
b0d623f7 3613 key.proto = pnl->proto;
b0d623f7 3614 key.proto_variant = pnl->proto_variant;
b0d623f7
A
3615
3616 if (!pnl->proto ||
3617 PF_AZERO(&pnl->saddr, pnl->af) ||
3618 PF_AZERO(&pnl->daddr, pnl->af) ||
3619 ((pnl->proto == IPPROTO_TCP ||
3620 pnl->proto == IPPROTO_UDP) &&
0a7de745 3621 (!pnl->dxport.port || !pnl->sxport.port))) {
b0d623f7 3622 error = EINVAL;
0a7de745 3623 } else {
b0d623f7
A
3624 /*
3625 * userland gives us source and dest of connection,
3626 * reverse the lookup so we ask for what happens with
3627 * the return traffic, enabling us to find it in the
3628 * state tree.
3629 */
3630 if (direction == PF_IN) {
3e170ce0
A
3631 key.af_gwy = pnl->af;
3632 PF_ACPY(&key.ext_gwy.addr, &pnl->daddr,
0a7de745 3633 pnl->af);
3e170ce0 3634 memcpy(&key.ext_gwy.xport, &pnl->dxport,
0a7de745 3635 sizeof(key.ext_gwy.xport));
b0d623f7 3636 PF_ACPY(&key.gwy.addr, &pnl->saddr, pnl->af);
b0d623f7 3637 memcpy(&key.gwy.xport, &pnl->sxport,
0a7de745 3638 sizeof(key.gwy.xport));
b0d623f7
A
3639 state = pf_find_state_all(&key, PF_IN, &m);
3640 } else {
3e170ce0 3641 key.af_lan = pnl->af;
b0d623f7 3642 PF_ACPY(&key.lan.addr, &pnl->daddr, pnl->af);
b0d623f7 3643 memcpy(&key.lan.xport, &pnl->dxport,
0a7de745 3644 sizeof(key.lan.xport));
3e170ce0 3645 PF_ACPY(&key.ext_lan.addr, &pnl->saddr,
0a7de745 3646 pnl->af);
3e170ce0 3647 memcpy(&key.ext_lan.xport, &pnl->sxport,
0a7de745 3648 sizeof(key.ext_lan.xport));
b0d623f7
A
3649 state = pf_find_state_all(&key, PF_OUT, &m);
3650 }
0a7de745
A
3651 if (m > 1) {
3652 error = E2BIG; /* more than one state */
3653 } else if (state != NULL) {
b0d623f7
A
3654 sk = state->state_key;
3655 if (direction == PF_IN) {
3656 PF_ACPY(&pnl->rsaddr, &sk->lan.addr,
3e170ce0 3657 sk->af_lan);
b0d623f7 3658 memcpy(&pnl->rsxport, &sk->lan.xport,
0a7de745 3659 sizeof(pnl->rsxport));
b0d623f7
A
3660 PF_ACPY(&pnl->rdaddr, &pnl->daddr,
3661 pnl->af);
b0d623f7 3662 memcpy(&pnl->rdxport, &pnl->dxport,
0a7de745 3663 sizeof(pnl->rdxport));
b0d623f7
A
3664 } else {
3665 PF_ACPY(&pnl->rdaddr, &sk->gwy.addr,
3e170ce0 3666 sk->af_gwy);
b0d623f7 3667 memcpy(&pnl->rdxport, &sk->gwy.xport,
0a7de745 3668 sizeof(pnl->rdxport));
b0d623f7
A
3669 PF_ACPY(&pnl->rsaddr, &pnl->saddr,
3670 pnl->af);
b0d623f7 3671 memcpy(&pnl->rsxport, &pnl->sxport,
0a7de745 3672 sizeof(pnl->rsxport));
b0d623f7 3673 }
0a7de745 3674 } else {
b0d623f7 3675 error = ENOENT;
0a7de745 3676 }
b0d623f7
A
3677 }
3678 break;
3679 }
3680
316670eb
A
3681 default:
3682 VERIFY(0);
3683 /* NOTREACHED */
3684 }
3685
0a7de745 3686 return error;
316670eb
A
3687}
3688
3689static int
3690pfioctl_ioc_tm(u_long cmd, struct pfioc_tm *pt, struct proc *p)
3691{
3692#pragma unused(p)
3693 int error = 0;
3694
3695 switch (cmd) {
b0d623f7 3696 case DIOCSETTIMEOUT: {
316670eb 3697 int old;
b0d623f7
A
3698
3699 if (pt->timeout < 0 || pt->timeout >= PFTM_MAX ||
3700 pt->seconds < 0) {
3701 error = EINVAL;
3702 goto fail;
3703 }
3704 old = pf_default_rule.timeout[pt->timeout];
0a7de745 3705 if (pt->timeout == PFTM_INTERVAL && pt->seconds == 0) {
b0d623f7 3706 pt->seconds = 1;
0a7de745 3707 }
b0d623f7 3708 pf_default_rule.timeout[pt->timeout] = pt->seconds;
0a7de745 3709 if (pt->timeout == PFTM_INTERVAL && pt->seconds < old) {
b0d623f7 3710 wakeup(pf_purge_thread_fn);
0a7de745 3711 }
b0d623f7
A
3712 pt->seconds = old;
3713 break;
3714 }
3715
3716 case DIOCGETTIMEOUT: {
b0d623f7
A
3717 if (pt->timeout < 0 || pt->timeout >= PFTM_MAX) {
3718 error = EINVAL;
3719 goto fail;
3720 }
3721 pt->seconds = pf_default_rule.timeout[pt->timeout];
3722 break;
3723 }
3724
316670eb
A
3725 default:
3726 VERIFY(0);
3727 /* NOTREACHED */
3728 }
3729fail:
0a7de745 3730 return error;
316670eb
A
3731}
3732
3733static int
3734pfioctl_ioc_limit(u_long cmd, struct pfioc_limit *pl, struct proc *p)
3735{
3736#pragma unused(p)
3737 int error = 0;
3738
3739 switch (cmd) {
b0d623f7 3740 case DIOCGETLIMIT: {
b0d623f7
A
3741 if (pl->index < 0 || pl->index >= PF_LIMIT_MAX) {
3742 error = EINVAL;
3743 goto fail;
3744 }
3745 pl->limit = pf_pool_limits[pl->index].limit;
3746 break;
3747 }
3748
3749 case DIOCSETLIMIT: {
316670eb 3750 int old_limit;
b0d623f7
A
3751
3752 if (pl->index < 0 || pl->index >= PF_LIMIT_MAX ||
3753 pf_pool_limits[pl->index].pp == NULL) {
3754 error = EINVAL;
3755 goto fail;
3756 }
3757 pool_sethardlimit(pf_pool_limits[pl->index].pp,
3758 pl->limit, NULL, 0);
3759 old_limit = pf_pool_limits[pl->index].limit;
3760 pf_pool_limits[pl->index].limit = pl->limit;
3761 pl->limit = old_limit;
3762 break;
3763 }
3764
316670eb
A
3765 default:
3766 VERIFY(0);
3767 /* NOTREACHED */
b0d623f7 3768 }
316670eb 3769fail:
0a7de745 3770 return error;
316670eb 3771}
b0d623f7 3772
316670eb
A
3773static int
3774pfioctl_ioc_pooladdr(u_long cmd, struct pfioc_pooladdr *pp, struct proc *p)
3775{
3776#pragma unused(p)
3777 struct pf_pooladdr *pa = NULL;
3778 struct pf_pool *pool = NULL;
3779 int error = 0;
b0d623f7 3780
316670eb 3781 switch (cmd) {
b0d623f7 3782 case DIOCBEGINADDRS: {
b0d623f7
A
3783 pf_empty_pool(&pf_pabuf);
3784 pp->ticket = ++ticket_pabuf;
3785 break;
3786 }
3787
3788 case DIOCADDADDR: {
0a7de745 3789 pp->anchor[sizeof(pp->anchor) - 1] = '\0';
b0d623f7
A
3790 if (pp->ticket != ticket_pabuf) {
3791 error = EBUSY;
3792 break;
3793 }
3794#if !INET
3795 if (pp->af == AF_INET) {
3796 error = EAFNOSUPPORT;
3797 break;
3798 }
3799#endif /* INET */
3800#if !INET6
3801 if (pp->af == AF_INET6) {
3802 error = EAFNOSUPPORT;
3803 break;
3804 }
3805#endif /* INET6 */
3806 if (pp->addr.addr.type != PF_ADDR_ADDRMASK &&
3807 pp->addr.addr.type != PF_ADDR_DYNIFTL &&
3808 pp->addr.addr.type != PF_ADDR_TABLE) {
3809 error = EINVAL;
3810 break;
3811 }
3812 pa = pool_get(&pf_pooladdr_pl, PR_WAITOK);
3813 if (pa == NULL) {
3814 error = ENOMEM;
3815 break;
3816 }
6d2010ae 3817 pf_pooladdr_copyin(&pp->addr, pa);
b0d623f7
A
3818 if (pa->ifname[0]) {
3819 pa->kif = pfi_kif_get(pa->ifname);
3820 if (pa->kif == NULL) {
3821 pool_put(&pf_pooladdr_pl, pa);
3822 error = EINVAL;
3823 break;
3824 }
3825 pfi_kif_ref(pa->kif, PFI_KIF_REF_RULE);
3826 }
39236c6e 3827 pf_addrwrap_setup(&pa->addr);
b0d623f7
A
3828 if (pfi_dynaddr_setup(&pa->addr, pp->af)) {
3829 pfi_dynaddr_remove(&pa->addr);
3830 pfi_kif_unref(pa->kif, PFI_KIF_REF_RULE);
3831 pool_put(&pf_pooladdr_pl, pa);
3832 error = EINVAL;
3833 break;
3834 }
3835 TAILQ_INSERT_TAIL(&pf_pabuf, pa, entries);
3836 break;
3837 }
3838
3839 case DIOCGETADDRS: {
b0d623f7 3840 pp->nr = 0;
0a7de745 3841 pp->anchor[sizeof(pp->anchor) - 1] = '\0';
b0d623f7
A
3842 pool = pf_get_pool(pp->anchor, pp->ticket, pp->r_action,
3843 pp->r_num, 0, 1, 0);
3844 if (pool == NULL) {
3845 error = EBUSY;
3846 break;
3847 }
3848 TAILQ_FOREACH(pa, &pool->list, entries)
0a7de745 3849 pp->nr++;
b0d623f7
A
3850 break;
3851 }
3852
3853 case DIOCGETADDR: {
0a7de745 3854 u_int32_t nr = 0;
b0d623f7 3855
0a7de745 3856 pp->anchor[sizeof(pp->anchor) - 1] = '\0';
b0d623f7
A
3857 pool = pf_get_pool(pp->anchor, pp->ticket, pp->r_action,
3858 pp->r_num, 0, 1, 1);
3859 if (pool == NULL) {
3860 error = EBUSY;
3861 break;
3862 }
3863 pa = TAILQ_FIRST(&pool->list);
3864 while ((pa != NULL) && (nr < pp->nr)) {
3865 pa = TAILQ_NEXT(pa, entries);
3866 nr++;
3867 }
3868 if (pa == NULL) {
3869 error = EBUSY;
3870 break;
3871 }
6d2010ae 3872 pf_pooladdr_copyout(pa, &pp->addr);
b0d623f7
A
3873 pfi_dynaddr_copyout(&pp->addr.addr);
3874 pf_tbladdr_copyout(&pp->addr.addr);
3875 pf_rtlabel_copyout(&pp->addr.addr);
3876 break;
3877 }
3878
3879 case DIOCCHANGEADDR: {
0a7de745
A
3880 struct pfioc_pooladdr *pca = pp;
3881 struct pf_pooladdr *oldpa = NULL, *newpa = NULL;
3882 struct pf_ruleset *ruleset;
b0d623f7
A
3883
3884 if (pca->action < PF_CHANGE_ADD_HEAD ||
3885 pca->action > PF_CHANGE_REMOVE) {
3886 error = EINVAL;
3887 break;
3888 }
3889 if (pca->addr.addr.type != PF_ADDR_ADDRMASK &&
3890 pca->addr.addr.type != PF_ADDR_DYNIFTL &&
3891 pca->addr.addr.type != PF_ADDR_TABLE) {
3892 error = EINVAL;
3893 break;
3894 }
3895
0a7de745 3896 pca->anchor[sizeof(pca->anchor) - 1] = '\0';
b0d623f7
A
3897 ruleset = pf_find_ruleset(pca->anchor);
3898 if (ruleset == NULL) {
3899 error = EBUSY;
3900 break;
3901 }
3902 pool = pf_get_pool(pca->anchor, pca->ticket, pca->r_action,
3903 pca->r_num, pca->r_last, 1, 1);
3904 if (pool == NULL) {
3905 error = EBUSY;
3906 break;
3907 }
3908 if (pca->action != PF_CHANGE_REMOVE) {
3909 newpa = pool_get(&pf_pooladdr_pl, PR_WAITOK);
3910 if (newpa == NULL) {
3911 error = ENOMEM;
3912 break;
3913 }
6d2010ae 3914 pf_pooladdr_copyin(&pca->addr, newpa);
b0d623f7
A
3915#if !INET
3916 if (pca->af == AF_INET) {
3917 pool_put(&pf_pooladdr_pl, newpa);
3918 error = EAFNOSUPPORT;
3919 break;
3920 }
3921#endif /* INET */
3922#if !INET6
3923 if (pca->af == AF_INET6) {
3924 pool_put(&pf_pooladdr_pl, newpa);
3925 error = EAFNOSUPPORT;
3926 break;
3927 }
3928#endif /* INET6 */
3929 if (newpa->ifname[0]) {
3930 newpa->kif = pfi_kif_get(newpa->ifname);
3931 if (newpa->kif == NULL) {
3932 pool_put(&pf_pooladdr_pl, newpa);
3933 error = EINVAL;
3934 break;
3935 }
3936 pfi_kif_ref(newpa->kif, PFI_KIF_REF_RULE);
0a7de745 3937 } else {
b0d623f7 3938 newpa->kif = NULL;
0a7de745 3939 }
39236c6e 3940 pf_addrwrap_setup(&newpa->addr);
b0d623f7
A
3941 if (pfi_dynaddr_setup(&newpa->addr, pca->af) ||
3942 pf_tbladdr_setup(ruleset, &newpa->addr)) {
3943 pfi_dynaddr_remove(&newpa->addr);
3944 pfi_kif_unref(newpa->kif, PFI_KIF_REF_RULE);
3945 pool_put(&pf_pooladdr_pl, newpa);
3946 error = EINVAL;
3947 break;
3948 }
3949 }
3950
0a7de745 3951 if (pca->action == PF_CHANGE_ADD_HEAD) {
b0d623f7 3952 oldpa = TAILQ_FIRST(&pool->list);
0a7de745 3953 } else if (pca->action == PF_CHANGE_ADD_TAIL) {
b0d623f7 3954 oldpa = TAILQ_LAST(&pool->list, pf_palist);
0a7de745
A
3955 } else {
3956 int i = 0;
b0d623f7
A
3957
3958 oldpa = TAILQ_FIRST(&pool->list);
3959 while ((oldpa != NULL) && (i < (int)pca->nr)) {
3960 oldpa = TAILQ_NEXT(oldpa, entries);
3961 i++;
3962 }
3963 if (oldpa == NULL) {
3964 error = EINVAL;
3965 break;
3966 }
3967 }
3968
3969 if (pca->action == PF_CHANGE_REMOVE) {
3970 TAILQ_REMOVE(&pool->list, oldpa, entries);
3971 pfi_dynaddr_remove(&oldpa->addr);
3972 pf_tbladdr_remove(&oldpa->addr);
3973 pfi_kif_unref(oldpa->kif, PFI_KIF_REF_RULE);
3974 pool_put(&pf_pooladdr_pl, oldpa);
3975 } else {
0a7de745 3976 if (oldpa == NULL) {
b0d623f7 3977 TAILQ_INSERT_TAIL(&pool->list, newpa, entries);
0a7de745
A
3978 } else if (pca->action == PF_CHANGE_ADD_HEAD ||
3979 pca->action == PF_CHANGE_ADD_BEFORE) {
b0d623f7 3980 TAILQ_INSERT_BEFORE(oldpa, newpa, entries);
0a7de745 3981 } else {
b0d623f7
A
3982 TAILQ_INSERT_AFTER(&pool->list, oldpa,
3983 newpa, entries);
0a7de745 3984 }
b0d623f7
A
3985 }
3986
3987 pool->cur = TAILQ_FIRST(&pool->list);
3988 PF_ACPY(&pool->counter, &pool->cur->addr.v.a.addr,
3989 pca->af);
3990 break;
3991 }
3992
316670eb
A
3993 default:
3994 VERIFY(0);
3995 /* NOTREACHED */
3996 }
3997
0a7de745 3998 return error;
316670eb
A
3999}
4000
4001static int
4002pfioctl_ioc_ruleset(u_long cmd, struct pfioc_ruleset *pr, struct proc *p)
4003{
4004#pragma unused(p)
4005 int error = 0;
4006
4007 switch (cmd) {
b0d623f7 4008 case DIOCGETRULESETS: {
0a7de745
A
4009 struct pf_ruleset *ruleset;
4010 struct pf_anchor *anchor;
b0d623f7 4011
0a7de745
A
4012 pr->path[sizeof(pr->path) - 1] = '\0';
4013 pr->name[sizeof(pr->name) - 1] = '\0';
b0d623f7
A
4014 if ((ruleset = pf_find_ruleset(pr->path)) == NULL) {
4015 error = EINVAL;
4016 break;
4017 }
4018 pr->nr = 0;
4019 if (ruleset->anchor == NULL) {
4020 /* XXX kludge for pf_main_ruleset */
4021 RB_FOREACH(anchor, pf_anchor_global, &pf_anchors)
0a7de745
A
4022 if (anchor->parent == NULL) {
4023 pr->nr++;
4024 }
b0d623f7
A
4025 } else {
4026 RB_FOREACH(anchor, pf_anchor_node,
4027 &ruleset->anchor->children)
0a7de745 4028 pr->nr++;
b0d623f7
A
4029 }
4030 break;
4031 }
4032
4033 case DIOCGETRULESET: {
0a7de745
A
4034 struct pf_ruleset *ruleset;
4035 struct pf_anchor *anchor;
4036 u_int32_t nr = 0;
b0d623f7 4037
0a7de745 4038 pr->path[sizeof(pr->path) - 1] = '\0';
b0d623f7
A
4039 if ((ruleset = pf_find_ruleset(pr->path)) == NULL) {
4040 error = EINVAL;
4041 break;
4042 }
4043 pr->name[0] = 0;
4044 if (ruleset->anchor == NULL) {
4045 /* XXX kludge for pf_main_ruleset */
4046 RB_FOREACH(anchor, pf_anchor_global, &pf_anchors)
0a7de745
A
4047 if (anchor->parent == NULL && nr++ == pr->nr) {
4048 strlcpy(pr->name, anchor->name,
4049 sizeof(pr->name));
4050 break;
4051 }
b0d623f7
A
4052 } else {
4053 RB_FOREACH(anchor, pf_anchor_node,
4054 &ruleset->anchor->children)
0a7de745
A
4055 if (nr++ == pr->nr) {
4056 strlcpy(pr->name, anchor->name,
4057 sizeof(pr->name));
4058 break;
4059 }
b0d623f7 4060 }
0a7de745 4061 if (!pr->name[0]) {
b0d623f7 4062 error = EBUSY;
0a7de745 4063 }
b0d623f7
A
4064 break;
4065 }
4066
316670eb
A
4067 default:
4068 VERIFY(0);
4069 /* NOTREACHED */
b0d623f7
A
4070 }
4071
0a7de745 4072 return error;
316670eb 4073}
b0d623f7 4074
316670eb
A
4075static int
4076pfioctl_ioc_trans(u_long cmd, struct pfioc_trans_32 *io32,
4077 struct pfioc_trans_64 *io64, struct proc *p)
4078{
4079 int p64 = proc_is64bit(p);
4080 int error = 0, esize, size;
4081 user_addr_t buf;
b0d623f7 4082
316670eb
A
4083 esize = (p64 ? io64->esize : io32->esize);
4084 size = (p64 ? io64->size : io32->size);
4085 buf = (p64 ? io64->array : io32->array);
b0d623f7 4086
316670eb 4087 switch (cmd) {
b0d623f7 4088 case DIOCXBEGIN: {
0a7de745
A
4089 struct pfioc_trans_e *ioe;
4090 struct pfr_table *table;
4091 int i;
b0d623f7 4092
0a7de745 4093 if (esize != sizeof(*ioe)) {
b0d623f7
A
4094 error = ENODEV;
4095 goto fail;
4096 }
0a7de745
A
4097 ioe = _MALLOC(sizeof(*ioe), M_TEMP, M_WAITOK);
4098 table = _MALLOC(sizeof(*table), M_TEMP, M_WAITOK);
4099 for (i = 0; i < size; i++, buf += sizeof(*ioe)) {
4100 if (copyin(buf, ioe, sizeof(*ioe))) {
b0d623f7
A
4101 _FREE(table, M_TEMP);
4102 _FREE(ioe, M_TEMP);
4103 error = EFAULT;
4104 goto fail;
4105 }
0a7de745 4106 ioe->anchor[sizeof(ioe->anchor) - 1] = '\0';
b0d623f7
A
4107 switch (ioe->rs_num) {
4108 case PF_RULESET_ALTQ:
b0d623f7
A
4109 break;
4110 case PF_RULESET_TABLE:
0a7de745 4111 bzero(table, sizeof(*table));
b0d623f7 4112 strlcpy(table->pfrt_anchor, ioe->anchor,
0a7de745 4113 sizeof(table->pfrt_anchor));
b0d623f7
A
4114 if ((error = pfr_ina_begin(table,
4115 &ioe->ticket, NULL, 0))) {
4116 _FREE(table, M_TEMP);
4117 _FREE(ioe, M_TEMP);
4118 goto fail;
4119 }
4120 break;
4121 default:
4122 if ((error = pf_begin_rules(&ioe->ticket,
4123 ioe->rs_num, ioe->anchor))) {
4124 _FREE(table, M_TEMP);
4125 _FREE(ioe, M_TEMP);
4126 goto fail;
4127 }
4128 break;
4129 }
0a7de745 4130 if (copyout(ioe, buf, sizeof(*ioe))) {
b0d623f7
A
4131 _FREE(table, M_TEMP);
4132 _FREE(ioe, M_TEMP);
4133 error = EFAULT;
4134 goto fail;
4135 }
4136 }
4137 _FREE(table, M_TEMP);
4138 _FREE(ioe, M_TEMP);
4139 break;
4140 }
4141
4142 case DIOCXROLLBACK: {
0a7de745
A
4143 struct pfioc_trans_e *ioe;
4144 struct pfr_table *table;
4145 int i;
b0d623f7 4146
0a7de745 4147 if (esize != sizeof(*ioe)) {
b0d623f7
A
4148 error = ENODEV;
4149 goto fail;
4150 }
0a7de745
A
4151 ioe = _MALLOC(sizeof(*ioe), M_TEMP, M_WAITOK);
4152 table = _MALLOC(sizeof(*table), M_TEMP, M_WAITOK);
4153 for (i = 0; i < size; i++, buf += sizeof(*ioe)) {
4154 if (copyin(buf, ioe, sizeof(*ioe))) {
b0d623f7
A
4155 _FREE(table, M_TEMP);
4156 _FREE(ioe, M_TEMP);
4157 error = EFAULT;
4158 goto fail;
4159 }
0a7de745 4160 ioe->anchor[sizeof(ioe->anchor) - 1] = '\0';
b0d623f7
A
4161 switch (ioe->rs_num) {
4162 case PF_RULESET_ALTQ:
b0d623f7
A
4163 break;
4164 case PF_RULESET_TABLE:
0a7de745 4165 bzero(table, sizeof(*table));
b0d623f7 4166 strlcpy(table->pfrt_anchor, ioe->anchor,
0a7de745 4167 sizeof(table->pfrt_anchor));
b0d623f7
A
4168 if ((error = pfr_ina_rollback(table,
4169 ioe->ticket, NULL, 0))) {
4170 _FREE(table, M_TEMP);
4171 _FREE(ioe, M_TEMP);
4172 goto fail; /* really bad */
4173 }
4174 break;
4175 default:
4176 if ((error = pf_rollback_rules(ioe->ticket,
4177 ioe->rs_num, ioe->anchor))) {
4178 _FREE(table, M_TEMP);
4179 _FREE(ioe, M_TEMP);
4180 goto fail; /* really bad */
4181 }
4182 break;
4183 }
4184 }
4185 _FREE(table, M_TEMP);
4186 _FREE(ioe, M_TEMP);
4187 break;
4188 }
4189
4190 case DIOCXCOMMIT: {
0a7de745
A
4191 struct pfioc_trans_e *ioe;
4192 struct pfr_table *table;
4193 struct pf_ruleset *rs;
4194 user_addr_t _buf = buf;
4195 int i;
b0d623f7 4196
0a7de745 4197 if (esize != sizeof(*ioe)) {
b0d623f7
A
4198 error = ENODEV;
4199 goto fail;
4200 }
0a7de745
A
4201 ioe = _MALLOC(sizeof(*ioe), M_TEMP, M_WAITOK);
4202 table = _MALLOC(sizeof(*table), M_TEMP, M_WAITOK);
b0d623f7 4203 /* first makes sure everything will succeed */
0a7de745
A
4204 for (i = 0; i < size; i++, buf += sizeof(*ioe)) {
4205 if (copyin(buf, ioe, sizeof(*ioe))) {
b0d623f7
A
4206 _FREE(table, M_TEMP);
4207 _FREE(ioe, M_TEMP);
4208 error = EFAULT;
4209 goto fail;
4210 }
0a7de745 4211 ioe->anchor[sizeof(ioe->anchor) - 1] = '\0';
b0d623f7
A
4212 switch (ioe->rs_num) {
4213 case PF_RULESET_ALTQ:
b0d623f7
A
4214 break;
4215 case PF_RULESET_TABLE:
4216 rs = pf_find_ruleset(ioe->anchor);
4217 if (rs == NULL || !rs->topen || ioe->ticket !=
4218 rs->tticket) {
4219 _FREE(table, M_TEMP);
4220 _FREE(ioe, M_TEMP);
4221 error = EBUSY;
4222 goto fail;
4223 }
4224 break;
4225 default:
4226 if (ioe->rs_num < 0 || ioe->rs_num >=
4227 PF_RULESET_MAX) {
4228 _FREE(table, M_TEMP);
4229 _FREE(ioe, M_TEMP);
4230 error = EINVAL;
4231 goto fail;
4232 }
4233 rs = pf_find_ruleset(ioe->anchor);
4234 if (rs == NULL ||
4235 !rs->rules[ioe->rs_num].inactive.open ||
4236 rs->rules[ioe->rs_num].inactive.ticket !=
4237 ioe->ticket) {
4238 _FREE(table, M_TEMP);
4239 _FREE(ioe, M_TEMP);
4240 error = EBUSY;
4241 goto fail;
4242 }
4243 break;
4244 }
4245 }
d1ecb069 4246 buf = _buf;
b0d623f7 4247 /* now do the commit - no errors should happen here */
0a7de745
A
4248 for (i = 0; i < size; i++, buf += sizeof(*ioe)) {
4249 if (copyin(buf, ioe, sizeof(*ioe))) {
b0d623f7
A
4250 _FREE(table, M_TEMP);
4251 _FREE(ioe, M_TEMP);
4252 error = EFAULT;
4253 goto fail;
4254 }
0a7de745 4255 ioe->anchor[sizeof(ioe->anchor) - 1] = '\0';
b0d623f7
A
4256 switch (ioe->rs_num) {
4257 case PF_RULESET_ALTQ:
b0d623f7
A
4258 break;
4259 case PF_RULESET_TABLE:
0a7de745 4260 bzero(table, sizeof(*table));
b0d623f7 4261 strlcpy(table->pfrt_anchor, ioe->anchor,
0a7de745 4262 sizeof(table->pfrt_anchor));
b0d623f7
A
4263 if ((error = pfr_ina_commit(table, ioe->ticket,
4264 NULL, NULL, 0))) {
4265 _FREE(table, M_TEMP);
4266 _FREE(ioe, M_TEMP);
4267 goto fail; /* really bad */
4268 }
4269 break;
4270 default:
4271 if ((error = pf_commit_rules(ioe->ticket,
4272 ioe->rs_num, ioe->anchor))) {
4273 _FREE(table, M_TEMP);
4274 _FREE(ioe, M_TEMP);
4275 goto fail; /* really bad */
4276 }
4277 break;
4278 }
4279 }
4280 _FREE(table, M_TEMP);
4281 _FREE(ioe, M_TEMP);
4282 break;
4283 }
4284
316670eb
A
4285 default:
4286 VERIFY(0);
4287 /* NOTREACHED */
4288 }
4289fail:
0a7de745 4290 return error;
316670eb
A
4291}
4292
4293static int
4294pfioctl_ioc_src_nodes(u_long cmd, struct pfioc_src_nodes_32 *psn32,
4295 struct pfioc_src_nodes_64 *psn64, struct proc *p)
4296{
4297 int p64 = proc_is64bit(p);
4298 int error = 0;
4299
4300 switch (cmd) {
b0d623f7 4301 case DIOCGETSRCNODES: {
0a7de745
A
4302 struct pf_src_node *n, *pstore;
4303 user_addr_t buf;
4304 u_int32_t nr = 0;
4305 int space, size;
b0d623f7 4306
316670eb 4307 space = (p64 ? psn64->psn_len : psn32->psn_len);
b0d623f7
A
4308 if (space == 0) {
4309 RB_FOREACH(n, pf_src_tree, &tree_src_tracking)
0a7de745 4310 nr++;
316670eb 4311
0a7de745
A
4312 size = sizeof(struct pf_src_node) * nr;
4313 if (p64) {
316670eb 4314 psn64->psn_len = size;
0a7de745 4315 } else {
316670eb 4316 psn32->psn_len = size;
0a7de745 4317 }
b0d623f7
A
4318 break;
4319 }
4320
0a7de745 4321 pstore = _MALLOC(sizeof(*pstore), M_TEMP, M_WAITOK);
316670eb
A
4322 if (pstore == NULL) {
4323 error = ENOMEM;
4324 break;
4325 }
4326 buf = (p64 ? psn64->psn_buf : psn32->psn_buf);
b0d623f7 4327
b0d623f7
A
4328 RB_FOREACH(n, pf_src_tree, &tree_src_tracking) {
4329 uint64_t secs = pf_time_second(), diff;
4330
0a7de745 4331 if ((nr + 1) * sizeof(*pstore) > (unsigned)space) {
b0d623f7 4332 break;
0a7de745 4333 }
b0d623f7 4334
0a7de745
A
4335 bcopy(n, pstore, sizeof(*pstore));
4336 if (n->rule.ptr != NULL) {
b0d623f7 4337 pstore->rule.nr = n->rule.ptr->nr;
0a7de745 4338 }
b0d623f7 4339 pstore->creation = secs - pstore->creation;
0a7de745 4340 if (pstore->expire > secs) {
b0d623f7 4341 pstore->expire -= secs;
0a7de745 4342 } else {
b0d623f7 4343 pstore->expire = 0;
0a7de745 4344 }
b0d623f7
A
4345
4346 /* adjust the connection rate estimate */
4347 diff = secs - n->conn_rate.last;
0a7de745 4348 if (diff >= n->conn_rate.seconds) {
b0d623f7 4349 pstore->conn_rate.count = 0;
0a7de745 4350 } else {
b0d623f7
A
4351 pstore->conn_rate.count -=
4352 n->conn_rate.count * diff /
4353 n->conn_rate.seconds;
0a7de745 4354 }
b0d623f7 4355
6d2010ae
A
4356 _RB_PARENT(pstore, entry) = NULL;
4357 RB_LEFT(pstore, entry) = RB_RIGHT(pstore, entry) = NULL;
4358 pstore->kif = NULL;
4359
0a7de745 4360 error = copyout(pstore, buf, sizeof(*pstore));
b0d623f7
A
4361 if (error) {
4362 _FREE(pstore, M_TEMP);
4363 goto fail;
4364 }
0a7de745 4365 buf += sizeof(*pstore);
b0d623f7
A
4366 nr++;
4367 }
316670eb 4368
0a7de745
A
4369 size = sizeof(struct pf_src_node) * nr;
4370 if (p64) {
316670eb 4371 psn64->psn_len = size;
0a7de745 4372 } else {
316670eb 4373 psn32->psn_len = size;
0a7de745 4374 }
b0d623f7
A
4375
4376 _FREE(pstore, M_TEMP);
4377 break;
4378 }
4379
316670eb
A
4380 default:
4381 VERIFY(0);
4382 /* NOTREACHED */
b0d623f7 4383 }
316670eb 4384fail:
0a7de745 4385 return error;
316670eb
A
4386}
4387
4388static int
4389pfioctl_ioc_src_node_kill(u_long cmd, struct pfioc_src_node_kill *psnk,
4390 struct proc *p)
4391{
4392#pragma unused(p)
4393 int error = 0;
4394
4395 switch (cmd) {
b0d623f7 4396 case DIOCKILLSRCNODES: {
0a7de745
A
4397 struct pf_src_node *sn;
4398 struct pf_state *s;
4399 int killed = 0;
b0d623f7
A
4400
4401 RB_FOREACH(sn, pf_src_tree, &tree_src_tracking) {
4402 if (PF_MATCHA(psnk->psnk_src.neg,
4403 &psnk->psnk_src.addr.v.a.addr,
4404 &psnk->psnk_src.addr.v.a.mask,
4405 &sn->addr, sn->af) &&
4406 PF_MATCHA(psnk->psnk_dst.neg,
4407 &psnk->psnk_dst.addr.v.a.addr,
4408 &psnk->psnk_dst.addr.v.a.mask,
4409 &sn->raddr, sn->af)) {
4410 /* Handle state to src_node linkage */
4411 if (sn->states != 0) {
4412 RB_FOREACH(s, pf_state_tree_id,
4413 &tree_id) {
0a7de745 4414 if (s->src_node == sn) {
b0d623f7 4415 s->src_node = NULL;
0a7de745
A
4416 }
4417 if (s->nat_src_node == sn) {
b0d623f7 4418 s->nat_src_node = NULL;
0a7de745 4419 }
b0d623f7
A
4420 }
4421 sn->states = 0;
4422 }
4423 sn->expire = 1;
4424 killed++;
4425 }
4426 }
4427
0a7de745 4428 if (killed > 0) {
b0d623f7 4429 pf_purge_expired_src_nodes();
0a7de745 4430 }
b0d623f7
A
4431
4432 psnk->psnk_af = killed;
4433 break;
4434 }
4435
316670eb
A
4436 default:
4437 VERIFY(0);
4438 /* NOTREACHED */
b0d623f7
A
4439 }
4440
0a7de745 4441 return error;
316670eb
A
4442}
4443
4444static int
4445pfioctl_ioc_iface(u_long cmd, struct pfioc_iface_32 *io32,
4446 struct pfioc_iface_64 *io64, struct proc *p)
4447{
4448 int p64 = proc_is64bit(p);
4449 int error = 0;
b0d623f7 4450
316670eb 4451 switch (cmd) {
b0d623f7 4452 case DIOCIGETIFACES: {
316670eb
A
4453 user_addr_t buf;
4454 int esize;
4455
4456 buf = (p64 ? io64->pfiio_buffer : io32->pfiio_buffer);
4457 esize = (p64 ? io64->pfiio_esize : io32->pfiio_esize);
b0d623f7 4458
d1ecb069 4459 /* esize must be that of the user space version of pfi_kif */
0a7de745 4460 if (esize != sizeof(struct pfi_uif)) {
b0d623f7
A
4461 error = ENODEV;
4462 break;
4463 }
0a7de745
A
4464 if (p64) {
4465 io64->pfiio_name[sizeof(io64->pfiio_name) - 1] = '\0';
4466 } else {
4467 io32->pfiio_name[sizeof(io32->pfiio_name) - 1] = '\0';
4468 }
316670eb 4469 error = pfi_get_ifaces(
0a7de745
A
4470 p64 ? io64->pfiio_name : io32->pfiio_name, buf,
4471 p64 ? &io64->pfiio_size : &io32->pfiio_size);
b0d623f7
A
4472 break;
4473 }
4474
4475 case DIOCSETIFFLAG: {
0a7de745
A
4476 if (p64) {
4477 io64->pfiio_name[sizeof(io64->pfiio_name) - 1] = '\0';
4478 } else {
4479 io32->pfiio_name[sizeof(io32->pfiio_name) - 1] = '\0';
4480 }
b0d623f7 4481
316670eb 4482 error = pfi_set_flags(
0a7de745
A
4483 p64 ? io64->pfiio_name : io32->pfiio_name,
4484 p64 ? io64->pfiio_flags : io32->pfiio_flags);
b0d623f7
A
4485 break;
4486 }
4487
4488 case DIOCCLRIFFLAG: {
0a7de745
A
4489 if (p64) {
4490 io64->pfiio_name[sizeof(io64->pfiio_name) - 1] = '\0';
4491 } else {
4492 io32->pfiio_name[sizeof(io32->pfiio_name) - 1] = '\0';
4493 }
b0d623f7 4494
316670eb 4495 error = pfi_clear_flags(
0a7de745
A
4496 p64 ? io64->pfiio_name : io32->pfiio_name,
4497 p64 ? io64->pfiio_flags : io32->pfiio_flags);
b0d623f7
A
4498 break;
4499 }
4500
4501 default:
316670eb
A
4502 VERIFY(0);
4503 /* NOTREACHED */
b0d623f7 4504 }
b0d623f7 4505
0a7de745 4506 return error;
b0d623f7
A
4507}
4508
4509int
4510pf_af_hook(struct ifnet *ifp, struct mbuf **mppn, struct mbuf **mp,
316670eb 4511 unsigned int af, int input, struct ip_fw_args *fwa)
b0d623f7 4512{
39236c6e 4513 int error = 0;
b0d623f7 4514 struct mbuf *nextpkt;
39236c6e 4515 net_thread_marks_t marks;
fe8ab488 4516 struct ifnet * pf_ifp = ifp;
39236c6e 4517
743345f9 4518 /* Always allow traffic on co-processor interfaces. */
0a7de745
A
4519 if (!intcoproc_unrestricted && ifp && IFNET_IS_INTCOPROC(ifp)) {
4520 return 0;
4521 }
743345f9 4522
39236c6e 4523 marks = net_thread_marks_push(NET_THREAD_HELD_PF);
b0d623f7 4524
39236c6e 4525 if (marks != net_thread_marks_none) {
b0d623f7 4526 lck_rw_lock_shared(pf_perim_lock);
0a7de745 4527 if (!pf_is_enabled) {
b0d623f7 4528 goto done;
0a7de745 4529 }
b0d623f7 4530 lck_mtx_lock(pf_lock);
b0d623f7
A
4531 }
4532
0a7de745 4533 if (mppn != NULL && *mppn != NULL) {
b0d623f7 4534 VERIFY(*mppn == *mp);
0a7de745
A
4535 }
4536 if ((nextpkt = (*mp)->m_nextpkt) != NULL) {
b0d623f7 4537 (*mp)->m_nextpkt = NULL;
0a7de745 4538 }
b0d623f7 4539
0a7de745
A
4540 /*
4541 * For packets destined to locally hosted IP address
4542 * ip_output_list sets Mbuf's pkt header's rcvif to
4543 * the interface hosting the IP address.
4544 * While on the output path ifp passed to pf_af_hook
4545 * to such local communication is the loopback interface,
4546 * the input path derives ifp from mbuf packet header's
4547 * rcvif.
4548 * This asymmetry caues issues with PF.
4549 * To handle that case, we have a limited change here to
4550 * pass interface as loopback if packets are looped in.
4551 */
fe8ab488
A
4552 if (input && ((*mp)->m_pkthdr.pkt_flags & PKTF_LOOP)) {
4553 pf_ifp = lo_ifp;
4554 }
4555
b0d623f7
A
4556 switch (af) {
4557#if INET
4558 case AF_INET: {
fe8ab488 4559 error = pf_inet_hook(pf_ifp, mp, input, fwa);
b0d623f7
A
4560 break;
4561 }
4562#endif /* INET */
4563#if INET6
4564 case AF_INET6:
fe8ab488 4565 error = pf_inet6_hook(pf_ifp, mp, input, fwa);
b0d623f7
A
4566 break;
4567#endif /* INET6 */
4568 default:
4569 break;
4570 }
4571
316670eb
A
4572 /* When packet valid, link to the next packet */
4573 if (*mp != NULL && nextpkt != NULL) {
4574 struct mbuf *m = *mp;
0a7de745 4575 while (m->m_nextpkt != NULL) {
316670eb 4576 m = m->m_nextpkt;
0a7de745 4577 }
316670eb
A
4578 m->m_nextpkt = nextpkt;
4579 }
4580 /* Fix up linkage of previous packet in the chain */
4581 if (mppn != NULL) {
0a7de745 4582 if (*mp != NULL) {
316670eb 4583 *mppn = *mp;
0a7de745 4584 } else {
316670eb 4585 *mppn = nextpkt;
0a7de745 4586 }
b0d623f7 4587 }
39236c6e 4588
0a7de745 4589 if (marks != net_thread_marks_none) {
b0d623f7 4590 lck_mtx_unlock(pf_lock);
0a7de745 4591 }
39236c6e 4592
b0d623f7 4593done:
0a7de745 4594 if (marks != net_thread_marks_none) {
b0d623f7 4595 lck_rw_done(pf_perim_lock);
0a7de745 4596 }
b0d623f7 4597
39236c6e 4598 net_thread_marks_pop(marks);
0a7de745 4599 return error;
b0d623f7
A
4600}
4601
4602
4603#if INET
4604static int
316670eb
A
4605pf_inet_hook(struct ifnet *ifp, struct mbuf **mp, int input,
4606 struct ip_fw_args *fwa)
b0d623f7
A
4607{
4608 struct mbuf *m = *mp;
4609#if BYTE_ORDER != BIG_ENDIAN
4610 struct ip *ip = mtod(m, struct ip *);
4611#endif
4612 int error = 0;
4613
4614 /*
4615 * If the packet is outbound, is originated locally, is flagged for
4616 * delayed UDP/TCP checksum calculation, and is about to be processed
4617 * for an interface that doesn't support the appropriate checksum
4618 * offloading, then calculated the checksum here so that PF can adjust
4619 * it properly.
4620 */
4621 if (!input && m->m_pkthdr.rcvif == NULL) {
4622 static const int mask = CSUM_DELAY_DATA;
4623 const int flags = m->m_pkthdr.csum_flags &
4624 ~IF_HWASSIST_CSUM_FLAGS(ifp->if_hwassist);
4625
4626 if (flags & mask) {
4627 in_delayed_cksum(m);
4628 m->m_pkthdr.csum_flags &= ~mask;
4629 }
4630 }
4631
4632#if BYTE_ORDER != BIG_ENDIAN
4633 HTONS(ip->ip_len);
4634 HTONS(ip->ip_off);
4635#endif
5ba3f43e 4636 if (pf_test_mbuf(input ? PF_IN : PF_OUT, ifp, mp, NULL, fwa) != PF_PASS) {
b0d623f7
A
4637 if (*mp != NULL) {
4638 m_freem(*mp);
4639 *mp = NULL;
4640 error = EHOSTUNREACH;
4641 } else {
4642 error = ENOBUFS;
4643 }
4644 }
4645#if BYTE_ORDER != BIG_ENDIAN
4646 else {
6d2010ae
A
4647 if (*mp != NULL) {
4648 ip = mtod(*mp, struct ip *);
4649 NTOHS(ip->ip_len);
4650 NTOHS(ip->ip_off);
4651 }
b0d623f7
A
4652 }
4653#endif
0a7de745 4654 return error;
b0d623f7
A
4655}
4656#endif /* INET */
4657
4658#if INET6
4659int
316670eb
A
4660pf_inet6_hook(struct ifnet *ifp, struct mbuf **mp, int input,
4661 struct ip_fw_args *fwa)
b0d623f7
A
4662{
4663 int error = 0;
4664
b0d623f7
A
4665 /*
4666 * If the packet is outbound, is originated locally, is flagged for
4667 * delayed UDP/TCP checksum calculation, and is about to be processed
4668 * for an interface that doesn't support the appropriate checksum
4669 * offloading, then calculated the checksum here so that PF can adjust
4670 * it properly.
4671 */
4672 if (!input && (*mp)->m_pkthdr.rcvif == NULL) {
6d2010ae 4673 static const int mask = CSUM_DELAY_IPV6_DATA;
b0d623f7
A
4674 const int flags = (*mp)->m_pkthdr.csum_flags &
4675 ~IF_HWASSIST_CSUM_FLAGS(ifp->if_hwassist);
4676
4677 if (flags & mask) {
39236c6e
A
4678 /*
4679 * Checksum offload should not have been enabled
4680 * when extension headers exist, thus 0 for optlen.
4681 */
4682 in6_delayed_cksum(*mp);
b0d623f7
A
4683 (*mp)->m_pkthdr.csum_flags &= ~mask;
4684 }
4685 }
b0d623f7 4686
5ba3f43e 4687 if (pf_test6_mbuf(input ? PF_IN : PF_OUT, ifp, mp, NULL, fwa) != PF_PASS) {
b0d623f7
A
4688 if (*mp != NULL) {
4689 m_freem(*mp);
4690 *mp = NULL;
4691 error = EHOSTUNREACH;
4692 } else {
4693 error = ENOBUFS;
4694 }
4695 }
0a7de745 4696 return error;
b0d623f7
A
4697}
4698#endif /* INET6 */
4699
4700int
39236c6e 4701pf_ifaddr_hook(struct ifnet *ifp)
b0d623f7 4702{
39236c6e 4703 struct pfi_kif *kif = ifp->if_pf_kif;
b0d623f7 4704
39236c6e
A
4705 if (kif != NULL) {
4706 lck_rw_lock_shared(pf_perim_lock);
4707 lck_mtx_lock(pf_lock);
b0d623f7 4708
39236c6e
A
4709 pfi_kifaddr_update(kif);
4710
4711 lck_mtx_unlock(pf_lock);
4712 lck_rw_done(pf_perim_lock);
4713 }
0a7de745 4714 return 0;
b0d623f7
A
4715}
4716
4717/*
4718 * Caller acquires dlil lock as writer (exclusive)
4719 */
4720void
4721pf_ifnet_hook(struct ifnet *ifp, int attach)
4722{
4723 lck_rw_lock_shared(pf_perim_lock);
b0d623f7 4724 lck_mtx_lock(pf_lock);
0a7de745 4725 if (attach) {
b0d623f7 4726 pfi_attach_ifnet(ifp);
0a7de745 4727 } else {
b0d623f7 4728 pfi_detach_ifnet(ifp);
0a7de745 4729 }
b0d623f7 4730 lck_mtx_unlock(pf_lock);
b0d623f7
A
4731 lck_rw_done(pf_perim_lock);
4732}
4733
4734static void
4735pf_attach_hooks(void)
4736{
b0d623f7 4737 ifnet_head_lock_shared();
d1ecb069
A
4738 /*
4739 * Check against ifnet_addrs[] before proceeding, in case this
4740 * is called very early on, e.g. during dlil_init() before any
4741 * network interface is attached.
4742 */
4743 if (ifnet_addrs != NULL) {
4744 int i;
4745
4746 for (i = 0; i <= if_index; i++) {
4747 struct ifnet *ifp = ifindex2ifnet[i];
4748 if (ifp != NULL) {
4749 pfi_attach_ifnet(ifp);
4750 }
b0d623f7
A
4751 }
4752 }
4753 ifnet_head_done();
b0d623f7
A
4754}
4755
d1ecb069
A
4756#if 0
4757/* currently unused along with pfdetach() */
b0d623f7
A
4758static void
4759pf_detach_hooks(void)
4760{
b0d623f7 4761 ifnet_head_lock_shared();
d1ecb069
A
4762 if (ifnet_addrs != NULL) {
4763 for (i = 0; i <= if_index; i++) {
4764 int i;
4765
4766 struct ifnet *ifp = ifindex2ifnet[i];
4767 if (ifp != NULL && ifp->if_pf_kif != NULL) {
4768 pfi_detach_ifnet(ifp);
4769 }
b0d623f7
A
4770 }
4771 }
4772 ifnet_head_done();
b0d623f7 4773}
d1ecb069 4774#endif
39236c6e
A
4775
4776/*
4777 * 'D' group ioctls.
4778 *
4779 * The switch statement below does nothing at runtime, as it serves as a
4780 * compile time check to ensure that all of the socket 'D' ioctls (those
4781 * in the 'D' group going thru soo_ioctl) that are made available by the
4782 * networking stack is unique. This works as long as this routine gets
4783 * updated each time a new interface ioctl gets added.
4784 *
4785 * Any failures at compile time indicates duplicated ioctl values.
4786 */
4787static __attribute__((unused)) void
4788pfioctl_cassert(void)
4789{
4790 /*
4791 * This is equivalent to _CASSERT() and the compiler wouldn't
4792 * generate any instructions, thus for compile time only.
4793 */
4794 switch ((u_long)0) {
4795 case 0:
4796
4797 /* bsd/net/pfvar.h */
4798 case DIOCSTART:
4799 case DIOCSTOP:
4800 case DIOCADDRULE:
4801 case DIOCGETSTARTERS:
4802 case DIOCGETRULES:
4803 case DIOCGETRULE:
4804 case DIOCSTARTREF:
4805 case DIOCSTOPREF:
4806 case DIOCCLRSTATES:
4807 case DIOCGETSTATE:
4808 case DIOCSETSTATUSIF:
4809 case DIOCGETSTATUS:
4810 case DIOCCLRSTATUS:
4811 case DIOCNATLOOK:
4812 case DIOCSETDEBUG:
4813 case DIOCGETSTATES:
4814 case DIOCCHANGERULE:
4815 case DIOCINSERTRULE:
4816 case DIOCDELETERULE:
4817 case DIOCSETTIMEOUT:
4818 case DIOCGETTIMEOUT:
4819 case DIOCADDSTATE:
4820 case DIOCCLRRULECTRS:
4821 case DIOCGETLIMIT:
4822 case DIOCSETLIMIT:
4823 case DIOCKILLSTATES:
4824 case DIOCSTARTALTQ:
4825 case DIOCSTOPALTQ:
4826 case DIOCADDALTQ:
4827 case DIOCGETALTQS:
4828 case DIOCGETALTQ:
4829 case DIOCCHANGEALTQ:
4830 case DIOCGETQSTATS:
4831 case DIOCBEGINADDRS:
4832 case DIOCADDADDR:
4833 case DIOCGETADDRS:
4834 case DIOCGETADDR:
4835 case DIOCCHANGEADDR:
4836 case DIOCGETRULESETS:
4837 case DIOCGETRULESET:
4838 case DIOCRCLRTABLES:
4839 case DIOCRADDTABLES:
4840 case DIOCRDELTABLES:
4841 case DIOCRGETTABLES:
4842 case DIOCRGETTSTATS:
4843 case DIOCRCLRTSTATS:
4844 case DIOCRCLRADDRS:
4845 case DIOCRADDADDRS:
4846 case DIOCRDELADDRS:
4847 case DIOCRSETADDRS:
4848 case DIOCRGETADDRS:
4849 case DIOCRGETASTATS:
4850 case DIOCRCLRASTATS:
4851 case DIOCRTSTADDRS:
4852 case DIOCRSETTFLAGS:
4853 case DIOCRINADEFINE:
4854 case DIOCOSFPFLUSH:
4855 case DIOCOSFPADD:
4856 case DIOCOSFPGET:
4857 case DIOCXBEGIN:
4858 case DIOCXCOMMIT:
4859 case DIOCXROLLBACK:
4860 case DIOCGETSRCNODES:
4861 case DIOCCLRSRCNODES:
4862 case DIOCSETHOSTID:
4863 case DIOCIGETIFACES:
4864 case DIOCSETIFFLAG:
4865 case DIOCCLRIFFLAG:
4866 case DIOCKILLSRCNODES:
4867 case DIOCGIFSPEED:
4868 ;
4869 }
4870}