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