xref: /freebsd/sys/netpfil/pf/pf_if.c (revision 58571f3ea37c3e5019d39591aa09c1723c00face)
1d8aa10ccSGleb Smirnoff /*-
2fe267a55SPedro F. Giffuni  * SPDX-License-Identifier: BSD-2-Clause
3fe267a55SPedro F. Giffuni  *
43b3a8eb9SGleb Smirnoff  * Copyright (c) 2001 Daniel Hartmeier
53b3a8eb9SGleb Smirnoff  * Copyright (c) 2003 Cedric Berger
6d8aa10ccSGleb Smirnoff  * Copyright (c) 2005 Henning Brauer <henning@openbsd.org>
7d8aa10ccSGleb Smirnoff  * Copyright (c) 2005 Ryan McBride <mcbride@openbsd.org>
8d8aa10ccSGleb Smirnoff  * Copyright (c) 2012 Gleb Smirnoff <glebius@FreeBSD.org>
93b3a8eb9SGleb Smirnoff  * All rights reserved.
103b3a8eb9SGleb Smirnoff  *
113b3a8eb9SGleb Smirnoff  * Redistribution and use in source and binary forms, with or without
123b3a8eb9SGleb Smirnoff  * modification, are permitted provided that the following conditions
133b3a8eb9SGleb Smirnoff  * are met:
143b3a8eb9SGleb Smirnoff  *
153b3a8eb9SGleb Smirnoff  *    - Redistributions of source code must retain the above copyright
163b3a8eb9SGleb Smirnoff  *      notice, this list of conditions and the following disclaimer.
173b3a8eb9SGleb Smirnoff  *    - Redistributions in binary form must reproduce the above
183b3a8eb9SGleb Smirnoff  *      copyright notice, this list of conditions and the following
193b3a8eb9SGleb Smirnoff  *      disclaimer in the documentation and/or other materials provided
203b3a8eb9SGleb Smirnoff  *      with the distribution.
213b3a8eb9SGleb Smirnoff  *
223b3a8eb9SGleb Smirnoff  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
233b3a8eb9SGleb Smirnoff  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
243b3a8eb9SGleb Smirnoff  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
253b3a8eb9SGleb Smirnoff  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
263b3a8eb9SGleb Smirnoff  * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
273b3a8eb9SGleb Smirnoff  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
283b3a8eb9SGleb Smirnoff  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
293b3a8eb9SGleb Smirnoff  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
303b3a8eb9SGleb Smirnoff  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
313b3a8eb9SGleb Smirnoff  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
323b3a8eb9SGleb Smirnoff  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
333b3a8eb9SGleb Smirnoff  * POSSIBILITY OF SUCH DAMAGE.
34d8aa10ccSGleb Smirnoff  *
35d8aa10ccSGleb Smirnoff  *	$OpenBSD: pf_if.c,v 1.54 2008/06/14 16:55:28 mk Exp $
363b3a8eb9SGleb Smirnoff  */
373b3a8eb9SGleb Smirnoff 
383b3a8eb9SGleb Smirnoff #include <sys/cdefs.h>
393b3a8eb9SGleb Smirnoff #include "opt_inet.h"
403b3a8eb9SGleb Smirnoff #include "opt_inet6.h"
413b3a8eb9SGleb Smirnoff 
423b3a8eb9SGleb Smirnoff #include <sys/param.h>
433b3a8eb9SGleb Smirnoff #include <sys/kernel.h>
44ea8d1492SAlexander V. Chernikov #include <sys/eventhandler.h>
4576039bc8SGleb Smirnoff #include <sys/lock.h>
4676039bc8SGleb Smirnoff #include <sys/mbuf.h>
473b3a8eb9SGleb Smirnoff #include <sys/socket.h>
483b3a8eb9SGleb Smirnoff 
493b3a8eb9SGleb Smirnoff #include <net/if.h>
5076039bc8SGleb Smirnoff #include <net/if_var.h>
513d0d5b21SJustin Hibbits #include <net/if_private.h>
5276039bc8SGleb Smirnoff #include <net/vnet.h>
533b3a8eb9SGleb Smirnoff #include <net/pfvar.h>
543b3a8eb9SGleb Smirnoff #include <net/route.h>
553b3a8eb9SGleb Smirnoff 
56320c1116SKristof Provost VNET_DEFINE(struct pfi_kkif *,	 pfi_all);
575f901c92SAndrew Turner VNET_DEFINE_STATIC(long, pfi_update);
583b3a8eb9SGleb Smirnoff #define	V_pfi_update	VNET(pfi_update)
593b3a8eb9SGleb Smirnoff #define PFI_BUFFER_MAX	0x10000
603b3a8eb9SGleb Smirnoff 
61a0429b54SBjoern A. Zeeb VNET_DECLARE(int, pf_vnet_active);
62a0429b54SBjoern A. Zeeb #define V_pf_vnet_active	VNET(pf_vnet_active)
63a0429b54SBjoern A. Zeeb 
645f901c92SAndrew Turner VNET_DEFINE_STATIC(struct pfr_addr *, pfi_buffer);
655f901c92SAndrew Turner VNET_DEFINE_STATIC(int, pfi_buffer_cnt);
665f901c92SAndrew Turner VNET_DEFINE_STATIC(int,	pfi_buffer_max);
673b3a8eb9SGleb Smirnoff #define	V_pfi_buffer		 VNET(pfi_buffer)
683b3a8eb9SGleb Smirnoff #define	V_pfi_buffer_cnt	 VNET(pfi_buffer_cnt)
693b3a8eb9SGleb Smirnoff #define	V_pfi_buffer_max	 VNET(pfi_buffer_max)
703b3a8eb9SGleb Smirnoff 
71d40d4b3eSMateusz Guzik #ifdef PF_WANT_32_TO_64_COUNTER
72d40d4b3eSMateusz Guzik VNET_DEFINE(struct allkiflist_head, pf_allkiflist);
73d40d4b3eSMateusz Guzik VNET_DEFINE(size_t, pf_allkifcount);
74d40d4b3eSMateusz Guzik VNET_DEFINE(struct pfi_kkif *, pf_kifmarker);
75d40d4b3eSMateusz Guzik #endif
76d40d4b3eSMateusz Guzik 
773b3a8eb9SGleb Smirnoff eventhandler_tag	 pfi_attach_cookie;
783b3a8eb9SGleb Smirnoff eventhandler_tag	 pfi_detach_cookie;
793b3a8eb9SGleb Smirnoff eventhandler_tag	 pfi_attach_group_cookie;
803b3a8eb9SGleb Smirnoff eventhandler_tag	 pfi_change_group_cookie;
813b3a8eb9SGleb Smirnoff eventhandler_tag	 pfi_detach_group_cookie;
823b3a8eb9SGleb Smirnoff eventhandler_tag	 pfi_ifaddr_event_cookie;
833b3a8eb9SGleb Smirnoff 
84320c1116SKristof Provost static void	 pfi_attach_ifnet(struct ifnet *, struct pfi_kkif *);
85320c1116SKristof Provost static void	 pfi_attach_ifgroup(struct ifg_group *, struct pfi_kkif *);
863b3a8eb9SGleb Smirnoff 
87320c1116SKristof Provost static void	 pfi_kkif_update(struct pfi_kkif *);
883b3a8eb9SGleb Smirnoff static void	 pfi_dynaddr_update(struct pfi_dynaddr *dyn);
898e7d333fSKristof Provost static void	 pfi_table_update(struct pfr_ktable *, struct pfi_kkif *, uint8_t,
903b3a8eb9SGleb Smirnoff 		    int);
918e7d333fSKristof Provost static void	 pfi_instance_add(struct ifnet *, uint8_t, int);
928e7d333fSKristof Provost static void	 pfi_address_add(struct sockaddr *, sa_family_t, uint8_t);
93320c1116SKristof Provost static int	 pfi_kkif_compare(struct pfi_kkif *, struct pfi_kkif *);
94320c1116SKristof Provost static int	 pfi_skip_if(const char *, struct pfi_kkif *);
953b3a8eb9SGleb Smirnoff static int	 pfi_unmask(void *);
963b3a8eb9SGleb Smirnoff static void	 pfi_attach_ifnet_event(void * __unused, struct ifnet *);
973b3a8eb9SGleb Smirnoff static void	 pfi_detach_ifnet_event(void * __unused, struct ifnet *);
983a36ee40SMarko Zec static void	 pfi_attach_group_event(void * __unused, struct ifg_group *);
993a36ee40SMarko Zec static void	 pfi_change_group_event(void * __unused, char *);
1003a36ee40SMarko Zec static void	 pfi_detach_group_event(void * __unused, struct ifg_group *);
1013b3a8eb9SGleb Smirnoff static void	 pfi_ifaddr_event(void * __unused, struct ifnet *);
1023b3a8eb9SGleb Smirnoff 
103320c1116SKristof Provost RB_HEAD(pfi_ifhead, pfi_kkif);
104320c1116SKristof Provost static RB_PROTOTYPE(pfi_ifhead, pfi_kkif, pfik_tree, pfi_kkif_compare);
105320c1116SKristof Provost static RB_GENERATE(pfi_ifhead, pfi_kkif, pfik_tree, pfi_kkif_compare);
1065f901c92SAndrew Turner VNET_DEFINE_STATIC(struct pfi_ifhead, pfi_ifs);
1073b3a8eb9SGleb Smirnoff #define	V_pfi_ifs	VNET(pfi_ifs)
1083b3a8eb9SGleb Smirnoff 
1093b3a8eb9SGleb Smirnoff #define	PFI_BUFFER_MAX		0x10000
1103b3a8eb9SGleb Smirnoff MALLOC_DEFINE(PFI_MTYPE, "pf_ifnet", "pf(4) interface database");
1113b3a8eb9SGleb Smirnoff 
112320c1116SKristof Provost LIST_HEAD(pfi_list, pfi_kkif);
1135f901c92SAndrew Turner VNET_DEFINE_STATIC(struct pfi_list, pfi_unlinked_kifs);
1143b3a8eb9SGleb Smirnoff #define	V_pfi_unlinked_kifs	VNET(pfi_unlinked_kifs)
1153b3a8eb9SGleb Smirnoff static struct mtx pfi_unlnkdkifs_mtx;
1163dd01a88SGleb Smirnoff MTX_SYSINIT(pfi_unlnkdkifs_mtx, &pfi_unlnkdkifs_mtx, "pf unlinked interfaces",
1173dd01a88SGleb Smirnoff     MTX_DEF);
1183b3a8eb9SGleb Smirnoff 
1193b3a8eb9SGleb Smirnoff void
pfi_initialize_vnet(void)120a0429b54SBjoern A. Zeeb pfi_initialize_vnet(void)
1213b3a8eb9SGleb Smirnoff {
122b8a6e03fSGleb Smirnoff 	struct pfi_list kifs = LIST_HEAD_INITIALIZER();
123b8a6e03fSGleb Smirnoff 	struct epoch_tracker et;
124320c1116SKristof Provost 	struct pfi_kkif *kif;
1253b3a8eb9SGleb Smirnoff 	struct ifg_group *ifg;
1263b3a8eb9SGleb Smirnoff 	struct ifnet *ifp;
127b8a6e03fSGleb Smirnoff 	int nkifs;
1283b3a8eb9SGleb Smirnoff 
1293b3a8eb9SGleb Smirnoff 	V_pfi_buffer_max = 64;
1303b3a8eb9SGleb Smirnoff 	V_pfi_buffer = malloc(V_pfi_buffer_max * sizeof(*V_pfi_buffer),
1313b3a8eb9SGleb Smirnoff 	    PFI_MTYPE, M_WAITOK);
132efc6c51fSGleb Smirnoff 
133b8a6e03fSGleb Smirnoff 	nkifs = 1;	/* one for V_pfi_all */
1343b3a8eb9SGleb Smirnoff 	IFNET_RLOCK();
1354f6c66ccSMatt Macy 	CK_STAILQ_FOREACH(ifg, &V_ifg_head, ifg_next)
136b8a6e03fSGleb Smirnoff 		nkifs++;
1374f6c66ccSMatt Macy 	CK_STAILQ_FOREACH(ifp, &V_ifnet, if_link)
138b8a6e03fSGleb Smirnoff 		nkifs++;
139b8a6e03fSGleb Smirnoff 
140b8a6e03fSGleb Smirnoff 	for (int n = 0; n < nkifs; n++) {
14126c841e2SKristof Provost 		kif = pf_kkif_create(M_WAITOK);
142b8a6e03fSGleb Smirnoff 		LIST_INSERT_HEAD(&kifs, kif, pfik_list);
143b8a6e03fSGleb Smirnoff 	}
144b8a6e03fSGleb Smirnoff 
145b8a6e03fSGleb Smirnoff 	NET_EPOCH_ENTER(et);
146b8a6e03fSGleb Smirnoff 	PF_RULES_WLOCK();
147b8a6e03fSGleb Smirnoff 	kif = LIST_FIRST(&kifs);
148b8a6e03fSGleb Smirnoff 	LIST_REMOVE(kif, pfik_list);
149320c1116SKristof Provost 	V_pfi_all = pfi_kkif_attach(kif, IFG_ALL);
150b8a6e03fSGleb Smirnoff 	CK_STAILQ_FOREACH(ifg, &V_ifg_head, ifg_next) {
151b8a6e03fSGleb Smirnoff 		kif = LIST_FIRST(&kifs);
152b8a6e03fSGleb Smirnoff 		LIST_REMOVE(kif, pfik_list);
153b8a6e03fSGleb Smirnoff 		pfi_attach_ifgroup(ifg, kif);
154b8a6e03fSGleb Smirnoff 	}
155b8a6e03fSGleb Smirnoff 	CK_STAILQ_FOREACH(ifp, &V_ifnet, if_link) {
156b8a6e03fSGleb Smirnoff 		kif = LIST_FIRST(&kifs);
157b8a6e03fSGleb Smirnoff 		LIST_REMOVE(kif, pfik_list);
158b8a6e03fSGleb Smirnoff 		pfi_attach_ifnet(ifp, kif);
159b8a6e03fSGleb Smirnoff 	}
160b8a6e03fSGleb Smirnoff 	PF_RULES_WUNLOCK();
161b8a6e03fSGleb Smirnoff 	NET_EPOCH_EXIT(et);
1623b3a8eb9SGleb Smirnoff 	IFNET_RUNLOCK();
163b8a6e03fSGleb Smirnoff 
164b8a6e03fSGleb Smirnoff 	MPASS(LIST_EMPTY(&kifs));
165a0429b54SBjoern A. Zeeb }
166a0429b54SBjoern A. Zeeb 
167a0429b54SBjoern A. Zeeb void
pfi_initialize(void)168a0429b54SBjoern A. Zeeb pfi_initialize(void)
169a0429b54SBjoern A. Zeeb {
1703b3a8eb9SGleb Smirnoff 
1713b3a8eb9SGleb Smirnoff 	pfi_attach_cookie = EVENTHANDLER_REGISTER(ifnet_arrival_event,
1723b3a8eb9SGleb Smirnoff 	    pfi_attach_ifnet_event, NULL, EVENTHANDLER_PRI_ANY);
1733b3a8eb9SGleb Smirnoff 	pfi_detach_cookie = EVENTHANDLER_REGISTER(ifnet_departure_event,
1743b3a8eb9SGleb Smirnoff 	    pfi_detach_ifnet_event, NULL, EVENTHANDLER_PRI_ANY);
1753b3a8eb9SGleb Smirnoff 	pfi_attach_group_cookie = EVENTHANDLER_REGISTER(group_attach_event,
1763a36ee40SMarko Zec 	    pfi_attach_group_event, NULL, EVENTHANDLER_PRI_ANY);
1773b3a8eb9SGleb Smirnoff 	pfi_change_group_cookie = EVENTHANDLER_REGISTER(group_change_event,
1783a36ee40SMarko Zec 	    pfi_change_group_event, NULL, EVENTHANDLER_PRI_ANY);
1793b3a8eb9SGleb Smirnoff 	pfi_detach_group_cookie = EVENTHANDLER_REGISTER(group_detach_event,
1803a36ee40SMarko Zec 	    pfi_detach_group_event, NULL, EVENTHANDLER_PRI_ANY);
1813b3a8eb9SGleb Smirnoff 	pfi_ifaddr_event_cookie = EVENTHANDLER_REGISTER(ifaddr_event,
1823b3a8eb9SGleb Smirnoff 	    pfi_ifaddr_event, NULL, EVENTHANDLER_PRI_ANY);
1833b3a8eb9SGleb Smirnoff }
1843b3a8eb9SGleb Smirnoff 
1853b3a8eb9SGleb Smirnoff void
pfi_cleanup_vnet(void)186a0429b54SBjoern A. Zeeb pfi_cleanup_vnet(void)
187a0429b54SBjoern A. Zeeb {
188320c1116SKristof Provost 	struct pfi_kkif *kif;
189a0429b54SBjoern A. Zeeb 
190a0429b54SBjoern A. Zeeb 	PF_RULES_WASSERT();
191a0429b54SBjoern A. Zeeb 
192a0429b54SBjoern A. Zeeb 	V_pfi_all = NULL;
193a0429b54SBjoern A. Zeeb 	while ((kif = RB_MIN(pfi_ifhead, &V_pfi_ifs))) {
194a0429b54SBjoern A. Zeeb 		RB_REMOVE(pfi_ifhead, &V_pfi_ifs, kif);
195a0429b54SBjoern A. Zeeb 		if (kif->pfik_group)
196a0429b54SBjoern A. Zeeb 			kif->pfik_group->ifg_pf_kif = NULL;
19714624ab5SKristof Provost 		if (kif->pfik_ifp) {
19814624ab5SKristof Provost 			if_rele(kif->pfik_ifp);
199a0429b54SBjoern A. Zeeb 			kif->pfik_ifp->if_pf_kif = NULL;
20014624ab5SKristof Provost 		}
20126c841e2SKristof Provost 		pf_kkif_free(kif);
202a0429b54SBjoern A. Zeeb 	}
203a0429b54SBjoern A. Zeeb 
204a0429b54SBjoern A. Zeeb 	mtx_lock(&pfi_unlnkdkifs_mtx);
205a0429b54SBjoern A. Zeeb 	while ((kif = LIST_FIRST(&V_pfi_unlinked_kifs))) {
206a0429b54SBjoern A. Zeeb 		LIST_REMOVE(kif, pfik_list);
20726c841e2SKristof Provost 		pf_kkif_free(kif);
208a0429b54SBjoern A. Zeeb 	}
209a0429b54SBjoern A. Zeeb 	mtx_unlock(&pfi_unlnkdkifs_mtx);
210a0429b54SBjoern A. Zeeb 
211a0429b54SBjoern A. Zeeb 	free(V_pfi_buffer, PFI_MTYPE);
212a0429b54SBjoern A. Zeeb }
213a0429b54SBjoern A. Zeeb 
214a0429b54SBjoern A. Zeeb void
pfi_cleanup(void)2153b3a8eb9SGleb Smirnoff pfi_cleanup(void)
2163b3a8eb9SGleb Smirnoff {
2173b3a8eb9SGleb Smirnoff 
2183b3a8eb9SGleb Smirnoff 	EVENTHANDLER_DEREGISTER(ifnet_arrival_event, pfi_attach_cookie);
2193b3a8eb9SGleb Smirnoff 	EVENTHANDLER_DEREGISTER(ifnet_departure_event, pfi_detach_cookie);
2203b3a8eb9SGleb Smirnoff 	EVENTHANDLER_DEREGISTER(group_attach_event, pfi_attach_group_cookie);
2213b3a8eb9SGleb Smirnoff 	EVENTHANDLER_DEREGISTER(group_change_event, pfi_change_group_cookie);
2223b3a8eb9SGleb Smirnoff 	EVENTHANDLER_DEREGISTER(group_detach_event, pfi_detach_group_cookie);
2233b3a8eb9SGleb Smirnoff 	EVENTHANDLER_DEREGISTER(ifaddr_event, pfi_ifaddr_event_cookie);
2243b3a8eb9SGleb Smirnoff }
2253b3a8eb9SGleb Smirnoff 
226320c1116SKristof Provost struct pfi_kkif*
pf_kkif_create(int flags)22726c841e2SKristof Provost pf_kkif_create(int flags)
22826c841e2SKristof Provost {
22926c841e2SKristof Provost 	struct pfi_kkif *kif;
230d40d4b3eSMateusz Guzik #ifdef PF_WANT_32_TO_64_COUNTER
231d40d4b3eSMateusz Guzik 	bool wowned;
232d40d4b3eSMateusz Guzik #endif
23326c841e2SKristof Provost 
2345a3b9507SKristof Provost 	kif = malloc(sizeof(*kif), PFI_MTYPE, flags | M_ZERO);
2355a3b9507SKristof Provost 	if (! kif)
2365a3b9507SKristof Provost 		return (kif);
2375a3b9507SKristof Provost 
2385a3b9507SKristof Provost 	for (int i = 0; i < 2; i++) {
2395a3b9507SKristof Provost 		for (int j = 0; j < 2; j++) {
2405a3b9507SKristof Provost 			for (int k = 0; k < 2; k++) {
241d40d4b3eSMateusz Guzik 				if (pf_counter_u64_init(&kif->pfik_packets[i][j][k], flags) != 0) {
242d40d4b3eSMateusz Guzik 					pf_kkif_free(kif);
243d40d4b3eSMateusz Guzik 					return (NULL);
244d40d4b3eSMateusz Guzik 				}
2455a3b9507SKristof Provost 
246d40d4b3eSMateusz Guzik 				if (pf_counter_u64_init(&kif->pfik_bytes[i][j][k], flags) != 0) {
2475a3b9507SKristof Provost 					pf_kkif_free(kif);
2485a3b9507SKristof Provost 					return (NULL);
2495a3b9507SKristof Provost 				}
2505a3b9507SKristof Provost 			}
2515a3b9507SKristof Provost 		}
2525a3b9507SKristof Provost 	}
25326c841e2SKristof Provost 
254d40d4b3eSMateusz Guzik #ifdef PF_WANT_32_TO_64_COUNTER
255d40d4b3eSMateusz Guzik 	wowned = PF_RULES_WOWNED();
256d40d4b3eSMateusz Guzik 	if (!wowned)
257d40d4b3eSMateusz Guzik 		PF_RULES_WLOCK();
258d40d4b3eSMateusz Guzik 	LIST_INSERT_HEAD(&V_pf_allkiflist, kif, pfik_allkiflist);
259d40d4b3eSMateusz Guzik 	V_pf_allkifcount++;
260d40d4b3eSMateusz Guzik 	if (!wowned)
261d40d4b3eSMateusz Guzik 		PF_RULES_WUNLOCK();
262d40d4b3eSMateusz Guzik #endif
263d40d4b3eSMateusz Guzik 
26426c841e2SKristof Provost 	return (kif);
26526c841e2SKristof Provost }
26626c841e2SKristof Provost 
26726c841e2SKristof Provost void
pf_kkif_free(struct pfi_kkif * kif)26826c841e2SKristof Provost pf_kkif_free(struct pfi_kkif *kif)
26926c841e2SKristof Provost {
270d40d4b3eSMateusz Guzik #ifdef PF_WANT_32_TO_64_COUNTER
271d40d4b3eSMateusz Guzik 	bool wowned;
272d40d4b3eSMateusz Guzik #endif
273d40d4b3eSMateusz Guzik 
27426c841e2SKristof Provost 	if (! kif)
27526c841e2SKristof Provost 		return;
27626c841e2SKristof Provost 
277358c5f5cSKristof Provost #ifdef INVARIANTS
278358c5f5cSKristof Provost 	if (kif->pfik_ifp) {
279358c5f5cSKristof Provost 		struct ifnet *ifp = kif->pfik_ifp;
280358c5f5cSKristof Provost 		MPASS(ifp->if_pf_kif == NULL || ifp->if_pf_kif == kif);
281358c5f5cSKristof Provost 	}
282358c5f5cSKristof Provost #endif
283358c5f5cSKristof Provost 
284d40d4b3eSMateusz Guzik #ifdef PF_WANT_32_TO_64_COUNTER
285d40d4b3eSMateusz Guzik 	wowned = PF_RULES_WOWNED();
286d40d4b3eSMateusz Guzik 	if (!wowned)
287d40d4b3eSMateusz Guzik 		PF_RULES_WLOCK();
288d40d4b3eSMateusz Guzik 	LIST_REMOVE(kif, pfik_allkiflist);
289d40d4b3eSMateusz Guzik 	V_pf_allkifcount--;
290d40d4b3eSMateusz Guzik 	if (!wowned)
291d40d4b3eSMateusz Guzik 		PF_RULES_WUNLOCK();
292d40d4b3eSMateusz Guzik #endif
293d40d4b3eSMateusz Guzik 
2945a3b9507SKristof Provost 	for (int i = 0; i < 2; i++) {
2955a3b9507SKristof Provost 		for (int j = 0; j < 2; j++) {
2965a3b9507SKristof Provost 			for (int k = 0; k < 2; k++) {
297d40d4b3eSMateusz Guzik 				pf_counter_u64_deinit(&kif->pfik_packets[i][j][k]);
298d40d4b3eSMateusz Guzik 				pf_counter_u64_deinit(&kif->pfik_bytes[i][j][k]);
2995a3b9507SKristof Provost 			}
3005a3b9507SKristof Provost 		}
3015a3b9507SKristof Provost 	}
3025a3b9507SKristof Provost 
30326c841e2SKristof Provost 	free(kif, PFI_MTYPE);
30426c841e2SKristof Provost }
30526c841e2SKristof Provost 
3065a3b9507SKristof Provost void
pf_kkif_zero(struct pfi_kkif * kif)3075a3b9507SKristof Provost pf_kkif_zero(struct pfi_kkif *kif)
3085a3b9507SKristof Provost {
3095a3b9507SKristof Provost 
3105a3b9507SKristof Provost 	for (int i = 0; i < 2; i++) {
3115a3b9507SKristof Provost 		for (int j = 0; j < 2; j++) {
3125a3b9507SKristof Provost 			for (int k = 0; k < 2; k++) {
313d40d4b3eSMateusz Guzik 				pf_counter_u64_zero(&kif->pfik_packets[i][j][k]);
314d40d4b3eSMateusz Guzik 				pf_counter_u64_zero(&kif->pfik_bytes[i][j][k]);
3155a3b9507SKristof Provost 			}
3165a3b9507SKristof Provost 		}
3175a3b9507SKristof Provost 	}
3185a3b9507SKristof Provost 	kif->pfik_tzero = time_second;
3195a3b9507SKristof Provost }
3205a3b9507SKristof Provost 
32126c841e2SKristof Provost struct pfi_kkif *
pfi_kkif_find(const char * kif_name)322320c1116SKristof Provost pfi_kkif_find(const char *kif_name)
3233b3a8eb9SGleb Smirnoff {
3243b3a8eb9SGleb Smirnoff 	struct pfi_kif_cmp s;
3253b3a8eb9SGleb Smirnoff 
3263b3a8eb9SGleb Smirnoff 	PF_RULES_ASSERT();
3273b3a8eb9SGleb Smirnoff 
3286b598e26SEd Maste 	memset(&s, 0, sizeof(s));
3293b3a8eb9SGleb Smirnoff 	strlcpy(s.pfik_name, kif_name, sizeof(s.pfik_name));
3303b3a8eb9SGleb Smirnoff 
331320c1116SKristof Provost 	return (RB_FIND(pfi_ifhead, &V_pfi_ifs, (struct pfi_kkif *)&s));
3323b3a8eb9SGleb Smirnoff }
3333b3a8eb9SGleb Smirnoff 
334320c1116SKristof Provost struct pfi_kkif *
pfi_kkif_attach(struct pfi_kkif * kif,const char * kif_name)335320c1116SKristof Provost pfi_kkif_attach(struct pfi_kkif *kif, const char *kif_name)
3363b3a8eb9SGleb Smirnoff {
337320c1116SKristof Provost 	struct pfi_kkif *kif1;
3383b3a8eb9SGleb Smirnoff 
3393b3a8eb9SGleb Smirnoff 	PF_RULES_WASSERT();
3403b3a8eb9SGleb Smirnoff 	KASSERT(kif != NULL, ("%s: null kif", __func__));
3413b3a8eb9SGleb Smirnoff 
342320c1116SKristof Provost 	kif1 = pfi_kkif_find(kif_name);
3433b3a8eb9SGleb Smirnoff 	if (kif1 != NULL) {
34426c841e2SKristof Provost 		pf_kkif_free(kif);
3453b3a8eb9SGleb Smirnoff 		return (kif1);
3463b3a8eb9SGleb Smirnoff 	}
3473b3a8eb9SGleb Smirnoff 
3485a3b9507SKristof Provost 	pf_kkif_zero(kif);
3493b3a8eb9SGleb Smirnoff 	strlcpy(kif->pfik_name, kif_name, sizeof(kif->pfik_name));
3503b3a8eb9SGleb Smirnoff 	/*
3513b3a8eb9SGleb Smirnoff 	 * It seems that the value of time_second is in unintialzied state
3523b3a8eb9SGleb Smirnoff 	 * when pf sets interface statistics clear time in boot phase if pf
3533b3a8eb9SGleb Smirnoff 	 * was statically linked to kernel. Instead of setting the bogus
3543b3a8eb9SGleb Smirnoff 	 * time value have pfi_get_ifaces handle this case. In
3553b3a8eb9SGleb Smirnoff 	 * pfi_get_ifaces it uses time_second if it sees the time is 0.
3563b3a8eb9SGleb Smirnoff 	 */
3573b3a8eb9SGleb Smirnoff 	kif->pfik_tzero = time_second > 1 ? time_second : 0;
3583b3a8eb9SGleb Smirnoff 	TAILQ_INIT(&kif->pfik_dynaddrs);
3593b3a8eb9SGleb Smirnoff 
360d2bb1988SKristof Provost 	if (!strcmp(kif->pfik_name, "any")) {
361d2bb1988SKristof Provost 		/* both so it works in the ioctl and the regular case */
362d2bb1988SKristof Provost 		kif->pfik_flags |= PFI_IFLAG_ANY;
363d2bb1988SKristof Provost 	}
364d2bb1988SKristof Provost 
3653b3a8eb9SGleb Smirnoff 	RB_INSERT(pfi_ifhead, &V_pfi_ifs, kif);
3663b3a8eb9SGleb Smirnoff 
3673b3a8eb9SGleb Smirnoff 	return (kif);
3683b3a8eb9SGleb Smirnoff }
3693b3a8eb9SGleb Smirnoff 
3703b3a8eb9SGleb Smirnoff void
pfi_kkif_ref(struct pfi_kkif * kif)371320c1116SKristof Provost pfi_kkif_ref(struct pfi_kkif *kif)
3723b3a8eb9SGleb Smirnoff {
3733b3a8eb9SGleb Smirnoff 
3743b3a8eb9SGleb Smirnoff 	PF_RULES_WASSERT();
3753b3a8eb9SGleb Smirnoff 	kif->pfik_rulerefs++;
3763b3a8eb9SGleb Smirnoff }
3773b3a8eb9SGleb Smirnoff 
378bfeef0d3SNick Reilly static void
pfi_kkif_remove_if_unref(struct pfi_kkif * kif)379bfeef0d3SNick Reilly pfi_kkif_remove_if_unref(struct pfi_kkif *kif)
3803b3a8eb9SGleb Smirnoff {
3813b3a8eb9SGleb Smirnoff 
3823b3a8eb9SGleb Smirnoff 	PF_RULES_WASSERT();
3833b3a8eb9SGleb Smirnoff 
3843b3a8eb9SGleb Smirnoff 	if (kif->pfik_rulerefs > 0)
3853b3a8eb9SGleb Smirnoff 		return;
3863b3a8eb9SGleb Smirnoff 
38752b83a06SKristof Provost 	/* kif referencing an existing ifnet or group or holding flags should
38852b83a06SKristof Provost 	 * exist. */
38952b83a06SKristof Provost 	if (kif->pfik_ifp != NULL || kif->pfik_group != NULL ||
39052b83a06SKristof Provost 	    kif == V_pfi_all || kif->pfik_flags != 0)
3913b3a8eb9SGleb Smirnoff 		return;
3923b3a8eb9SGleb Smirnoff 
393358c5f5cSKristof Provost 	/*
394358c5f5cSKristof Provost 	 * We can get here in at least two distinct paths:
395358c5f5cSKristof Provost 	 * - when the struct ifnet is removed, via pfi_detach_ifnet_event()
396358c5f5cSKristof Provost 	 * - when a rule referencing us is removed, via pfi_kkif_unref().
397358c5f5cSKristof Provost 	 * These two events can race against each other, leading us to free this kif
398358c5f5cSKristof Provost 	 * twice. That leads to a loop in V_pfi_unlinked_kifs, and an eventual
399358c5f5cSKristof Provost 	 * deadlock.
400358c5f5cSKristof Provost 	 *
401358c5f5cSKristof Provost 	 * Avoid this by making sure we only ever insert the kif into
402358c5f5cSKristof Provost 	 * V_pfi_unlinked_kifs once.
403358c5f5cSKristof Provost 	 * If we don't find it in V_pfi_ifs it's already been removed. Check that it
404358c5f5cSKristof Provost 	 * exists in V_pfi_unlinked_kifs.
405358c5f5cSKristof Provost 	 */
406358c5f5cSKristof Provost 	if (! RB_FIND(pfi_ifhead, &V_pfi_ifs, kif)) {
407358c5f5cSKristof Provost #ifdef INVARIANTS
408358c5f5cSKristof Provost 		struct pfi_kkif *tmp;
409358c5f5cSKristof Provost 		bool found = false;
410358c5f5cSKristof Provost 		mtx_lock(&pfi_unlnkdkifs_mtx);
411358c5f5cSKristof Provost 		LIST_FOREACH(tmp, &V_pfi_unlinked_kifs, pfik_list) {
412358c5f5cSKristof Provost 			if (tmp == kif) {
413358c5f5cSKristof Provost 				found = true;
414358c5f5cSKristof Provost 				break;
415358c5f5cSKristof Provost 			}
416358c5f5cSKristof Provost 		}
417358c5f5cSKristof Provost 		mtx_unlock(&pfi_unlnkdkifs_mtx);
418358c5f5cSKristof Provost 		MPASS(found);
419358c5f5cSKristof Provost #endif
420358c5f5cSKristof Provost 		return;
421358c5f5cSKristof Provost 	}
4223b3a8eb9SGleb Smirnoff 	RB_REMOVE(pfi_ifhead, &V_pfi_ifs, kif);
4233b3a8eb9SGleb Smirnoff 
4243b3a8eb9SGleb Smirnoff 	kif->pfik_flags |= PFI_IFLAG_REFS;
4253b3a8eb9SGleb Smirnoff 
4263b3a8eb9SGleb Smirnoff 	mtx_lock(&pfi_unlnkdkifs_mtx);
4273b3a8eb9SGleb Smirnoff 	LIST_INSERT_HEAD(&V_pfi_unlinked_kifs, kif, pfik_list);
4283b3a8eb9SGleb Smirnoff 	mtx_unlock(&pfi_unlnkdkifs_mtx);
4293b3a8eb9SGleb Smirnoff }
4303b3a8eb9SGleb Smirnoff 
4313b3a8eb9SGleb Smirnoff void
pfi_kkif_unref(struct pfi_kkif * kif)432bfeef0d3SNick Reilly pfi_kkif_unref(struct pfi_kkif *kif)
433bfeef0d3SNick Reilly {
434bfeef0d3SNick Reilly 
435bfeef0d3SNick Reilly 	PF_RULES_WASSERT();
436bfeef0d3SNick Reilly 	KASSERT(kif->pfik_rulerefs > 0, ("%s: %p has zero refs", __func__, kif));
437bfeef0d3SNick Reilly 
438bfeef0d3SNick Reilly 	kif->pfik_rulerefs--;
439bfeef0d3SNick Reilly 
440bfeef0d3SNick Reilly 	pfi_kkif_remove_if_unref(kif);
441bfeef0d3SNick Reilly }
442bfeef0d3SNick Reilly 
443bfeef0d3SNick Reilly void
pfi_kkif_purge(void)444320c1116SKristof Provost pfi_kkif_purge(void)
4453b3a8eb9SGleb Smirnoff {
446320c1116SKristof Provost 	struct pfi_kkif *kif, *kif1;
4473b3a8eb9SGleb Smirnoff 
4483b3a8eb9SGleb Smirnoff 	/*
4493b3a8eb9SGleb Smirnoff 	 * Do naive mark-and-sweep garbage collecting of old kifs.
4503b3a8eb9SGleb Smirnoff 	 * Reference flag is raised by pf_purge_expired_states().
4513b3a8eb9SGleb Smirnoff 	 */
4523b3a8eb9SGleb Smirnoff 	mtx_lock(&pfi_unlnkdkifs_mtx);
4533b3a8eb9SGleb Smirnoff 	LIST_FOREACH_SAFE(kif, &V_pfi_unlinked_kifs, pfik_list, kif1) {
4543b3a8eb9SGleb Smirnoff 		if (!(kif->pfik_flags & PFI_IFLAG_REFS)) {
4553b3a8eb9SGleb Smirnoff 			LIST_REMOVE(kif, pfik_list);
45626c841e2SKristof Provost 			pf_kkif_free(kif);
4573b3a8eb9SGleb Smirnoff 		} else
4583b3a8eb9SGleb Smirnoff 			kif->pfik_flags &= ~PFI_IFLAG_REFS;
4593b3a8eb9SGleb Smirnoff 	}
4603b3a8eb9SGleb Smirnoff 	mtx_unlock(&pfi_unlnkdkifs_mtx);
4613b3a8eb9SGleb Smirnoff }
4623b3a8eb9SGleb Smirnoff 
4633b3a8eb9SGleb Smirnoff int
pfi_kkif_match(struct pfi_kkif * rule_kif,struct pfi_kkif * packet_kif)464320c1116SKristof Provost pfi_kkif_match(struct pfi_kkif *rule_kif, struct pfi_kkif *packet_kif)
4653b3a8eb9SGleb Smirnoff {
4663b3a8eb9SGleb Smirnoff 	struct ifg_list	*p;
4673b3a8eb9SGleb Smirnoff 
468b8a6e03fSGleb Smirnoff 	NET_EPOCH_ASSERT();
469b8a6e03fSGleb Smirnoff 
470c22c9879SKristof Provost 	MPASS(packet_kif != NULL);
471c22c9879SKristof Provost 	MPASS(packet_kif->pfik_ifp != NULL);
472c22c9879SKristof Provost 
4733b3a8eb9SGleb Smirnoff 	if (rule_kif == NULL || rule_kif == packet_kif)
4743b3a8eb9SGleb Smirnoff 		return (1);
4753b3a8eb9SGleb Smirnoff 
476e9ddca4aSKristof Provost 	if (rule_kif->pfik_group != NULL) {
4774f6c66ccSMatt Macy 		CK_STAILQ_FOREACH(p, &packet_kif->pfik_ifp->if_groups, ifgl_next)
478b8a6e03fSGleb Smirnoff 			if (p->ifgl_group == rule_kif->pfik_group)
4793b3a8eb9SGleb Smirnoff 				return (1);
480e9ddca4aSKristof Provost 	}
4813b3a8eb9SGleb Smirnoff 
482d2bb1988SKristof Provost 	if (rule_kif->pfik_flags & PFI_IFLAG_ANY && packet_kif->pfik_ifp &&
483d2bb1988SKristof Provost 	    !(packet_kif->pfik_ifp->if_flags & IFF_LOOPBACK))
484d2bb1988SKristof Provost 			return (1);
485d2bb1988SKristof Provost 
4863b3a8eb9SGleb Smirnoff 	return (0);
4873b3a8eb9SGleb Smirnoff }
4883b3a8eb9SGleb Smirnoff 
4893b3a8eb9SGleb Smirnoff static void
pfi_attach_ifnet(struct ifnet * ifp,struct pfi_kkif * kif)490320c1116SKristof Provost pfi_attach_ifnet(struct ifnet *ifp, struct pfi_kkif *kif)
4913b3a8eb9SGleb Smirnoff {
4923b3a8eb9SGleb Smirnoff 
493b8a6e03fSGleb Smirnoff 	PF_RULES_WASSERT();
4943b3a8eb9SGleb Smirnoff 
4953b3a8eb9SGleb Smirnoff 	V_pfi_update++;
496320c1116SKristof Provost 	kif = pfi_kkif_attach(kif, ifp->if_xname);
49714624ab5SKristof Provost 	if_ref(ifp);
4983b3a8eb9SGleb Smirnoff 	kif->pfik_ifp = ifp;
4993b3a8eb9SGleb Smirnoff 	ifp->if_pf_kif = kif;
500320c1116SKristof Provost 	pfi_kkif_update(kif);
5013b3a8eb9SGleb Smirnoff }
5023b3a8eb9SGleb Smirnoff 
5033b3a8eb9SGleb Smirnoff static void
pfi_attach_ifgroup(struct ifg_group * ifg,struct pfi_kkif * kif)504320c1116SKristof Provost pfi_attach_ifgroup(struct ifg_group *ifg, struct pfi_kkif *kif)
5053b3a8eb9SGleb Smirnoff {
5063b3a8eb9SGleb Smirnoff 
507b8a6e03fSGleb Smirnoff 	PF_RULES_WASSERT();
5083b3a8eb9SGleb Smirnoff 
5093b3a8eb9SGleb Smirnoff 	V_pfi_update++;
510320c1116SKristof Provost 	kif = pfi_kkif_attach(kif, ifg->ifg_group);
5113b3a8eb9SGleb Smirnoff 	kif->pfik_group = ifg;
5123b3a8eb9SGleb Smirnoff 	ifg->ifg_pf_kif = kif;
5133b3a8eb9SGleb Smirnoff }
5143b3a8eb9SGleb Smirnoff 
5153b3a8eb9SGleb Smirnoff int
pfi_match_addr(struct pfi_dynaddr * dyn,struct pf_addr * a,sa_family_t af)5163b3a8eb9SGleb Smirnoff pfi_match_addr(struct pfi_dynaddr *dyn, struct pf_addr *a, sa_family_t af)
5173b3a8eb9SGleb Smirnoff {
5183b3a8eb9SGleb Smirnoff 	switch (af) {
5193b3a8eb9SGleb Smirnoff #ifdef INET
5203b3a8eb9SGleb Smirnoff 	case AF_INET:
5213b3a8eb9SGleb Smirnoff 		switch (dyn->pfid_acnt4) {
5223b3a8eb9SGleb Smirnoff 		case 0:
5233b3a8eb9SGleb Smirnoff 			return (0);
5243b3a8eb9SGleb Smirnoff 		case 1:
525*58571f3eSKristof Provost 			return (pf_match_addr(0, &dyn->pfid_addr4,
5263b3a8eb9SGleb Smirnoff 			    &dyn->pfid_mask4, a, AF_INET));
5273b3a8eb9SGleb Smirnoff 		default:
5283b3a8eb9SGleb Smirnoff 			return (pfr_match_addr(dyn->pfid_kt, a, AF_INET));
5293b3a8eb9SGleb Smirnoff 		}
5303b3a8eb9SGleb Smirnoff 		break;
5313b3a8eb9SGleb Smirnoff #endif /* INET */
5323b3a8eb9SGleb Smirnoff #ifdef INET6
5333b3a8eb9SGleb Smirnoff 	case AF_INET6:
5343b3a8eb9SGleb Smirnoff 		switch (dyn->pfid_acnt6) {
5353b3a8eb9SGleb Smirnoff 		case 0:
5363b3a8eb9SGleb Smirnoff 			return (0);
5373b3a8eb9SGleb Smirnoff 		case 1:
538*58571f3eSKristof Provost 			return (pf_match_addr(0, &dyn->pfid_addr6,
5393b3a8eb9SGleb Smirnoff 			    &dyn->pfid_mask6, a, AF_INET6));
5403b3a8eb9SGleb Smirnoff 		default:
5413b3a8eb9SGleb Smirnoff 			return (pfr_match_addr(dyn->pfid_kt, a, AF_INET6));
5423b3a8eb9SGleb Smirnoff 		}
5433b3a8eb9SGleb Smirnoff 		break;
5443b3a8eb9SGleb Smirnoff #endif /* INET6 */
5453b3a8eb9SGleb Smirnoff 	default:
5463b3a8eb9SGleb Smirnoff 		return (0);
5473b3a8eb9SGleb Smirnoff 	}
5483b3a8eb9SGleb Smirnoff }
5493b3a8eb9SGleb Smirnoff 
5503b3a8eb9SGleb Smirnoff int
pfi_dynaddr_setup(struct pf_addr_wrap * aw,sa_family_t af)5513b3a8eb9SGleb Smirnoff pfi_dynaddr_setup(struct pf_addr_wrap *aw, sa_family_t af)
5523b3a8eb9SGleb Smirnoff {
553b8a6e03fSGleb Smirnoff 	struct epoch_tracker	 et;
5543b3a8eb9SGleb Smirnoff 	struct pfi_dynaddr	*dyn;
5553b3a8eb9SGleb Smirnoff 	char			 tblname[PF_TABLE_NAME_SIZE];
556e86bddeaSKristof Provost 	struct pf_kruleset	*ruleset = NULL;
557320c1116SKristof Provost 	struct pfi_kkif		*kif;
5583b3a8eb9SGleb Smirnoff 	int			 rv = 0;
5593b3a8eb9SGleb Smirnoff 
5603b3a8eb9SGleb Smirnoff 	PF_RULES_WASSERT();
5613b3a8eb9SGleb Smirnoff 	KASSERT(aw->type == PF_ADDR_DYNIFTL, ("%s: type %u",
5623b3a8eb9SGleb Smirnoff 	    __func__, aw->type));
5633b3a8eb9SGleb Smirnoff 	KASSERT(aw->p.dyn == NULL, ("%s: dyn is %p", __func__, aw->p.dyn));
5643b3a8eb9SGleb Smirnoff 
5653b3a8eb9SGleb Smirnoff 	if ((dyn = malloc(sizeof(*dyn), PFI_MTYPE, M_NOWAIT | M_ZERO)) == NULL)
5663b3a8eb9SGleb Smirnoff 		return (ENOMEM);
5673b3a8eb9SGleb Smirnoff 
56826c841e2SKristof Provost 	if ((kif = pf_kkif_create(M_NOWAIT)) == NULL) {
5693b3a8eb9SGleb Smirnoff 		free(dyn, PFI_MTYPE);
5703b3a8eb9SGleb Smirnoff 		return (ENOMEM);
5713b3a8eb9SGleb Smirnoff 	}
5723b3a8eb9SGleb Smirnoff 
5733b3a8eb9SGleb Smirnoff 	if (!strcmp(aw->v.ifname, "self"))
574320c1116SKristof Provost 		dyn->pfid_kif = pfi_kkif_attach(kif, IFG_ALL);
5753b3a8eb9SGleb Smirnoff 	else
576320c1116SKristof Provost 		dyn->pfid_kif = pfi_kkif_attach(kif, aw->v.ifname);
5775e98cae6SKristof Provost 	kif = NULL;
578320c1116SKristof Provost 	pfi_kkif_ref(dyn->pfid_kif);
5793b3a8eb9SGleb Smirnoff 
5803b3a8eb9SGleb Smirnoff 	dyn->pfid_net = pfi_unmask(&aw->v.a.mask);
5813b3a8eb9SGleb Smirnoff 	if (af == AF_INET && dyn->pfid_net == 32)
5823b3a8eb9SGleb Smirnoff 		dyn->pfid_net = 128;
5833b3a8eb9SGleb Smirnoff 	strlcpy(tblname, aw->v.ifname, sizeof(tblname));
5843b3a8eb9SGleb Smirnoff 	if (aw->iflags & PFI_AFLAG_NETWORK)
5853b3a8eb9SGleb Smirnoff 		strlcat(tblname, ":network", sizeof(tblname));
5863b3a8eb9SGleb Smirnoff 	if (aw->iflags & PFI_AFLAG_BROADCAST)
5873b3a8eb9SGleb Smirnoff 		strlcat(tblname, ":broadcast", sizeof(tblname));
5883b3a8eb9SGleb Smirnoff 	if (aw->iflags & PFI_AFLAG_PEER)
5893b3a8eb9SGleb Smirnoff 		strlcat(tblname, ":peer", sizeof(tblname));
5903b3a8eb9SGleb Smirnoff 	if (aw->iflags & PFI_AFLAG_NOALIAS)
5913b3a8eb9SGleb Smirnoff 		strlcat(tblname, ":0", sizeof(tblname));
5923b3a8eb9SGleb Smirnoff 	if (dyn->pfid_net != 128)
5933b3a8eb9SGleb Smirnoff 		snprintf(tblname + strlen(tblname),
5943b3a8eb9SGleb Smirnoff 		    sizeof(tblname) - strlen(tblname), "/%d", dyn->pfid_net);
595e86bddeaSKristof Provost 	if ((ruleset = pf_find_or_create_kruleset(PF_RESERVED_ANCHOR)) == NULL) {
5963b3a8eb9SGleb Smirnoff 		rv = ENOMEM;
5973b3a8eb9SGleb Smirnoff 		goto _bad;
5983b3a8eb9SGleb Smirnoff 	}
5993b3a8eb9SGleb Smirnoff 
6003b3a8eb9SGleb Smirnoff 	if ((dyn->pfid_kt = pfr_attach_table(ruleset, tblname)) == NULL) {
6013b3a8eb9SGleb Smirnoff 		rv = ENOMEM;
6023b3a8eb9SGleb Smirnoff 		goto _bad;
6033b3a8eb9SGleb Smirnoff 	}
6043b3a8eb9SGleb Smirnoff 
6053b3a8eb9SGleb Smirnoff 	dyn->pfid_kt->pfrkt_flags |= PFR_TFLAG_ACTIVE;
6063b3a8eb9SGleb Smirnoff 	dyn->pfid_iflags = aw->iflags;
6073b3a8eb9SGleb Smirnoff 	dyn->pfid_af = af;
6083b3a8eb9SGleb Smirnoff 
6093b3a8eb9SGleb Smirnoff 	TAILQ_INSERT_TAIL(&dyn->pfid_kif->pfik_dynaddrs, dyn, entry);
6103b3a8eb9SGleb Smirnoff 	aw->p.dyn = dyn;
611b8a6e03fSGleb Smirnoff 	NET_EPOCH_ENTER(et);
612320c1116SKristof Provost 	pfi_kkif_update(dyn->pfid_kif);
613b8a6e03fSGleb Smirnoff 	NET_EPOCH_EXIT(et);
6143b3a8eb9SGleb Smirnoff 
6153b3a8eb9SGleb Smirnoff 	return (0);
6163b3a8eb9SGleb Smirnoff 
6173b3a8eb9SGleb Smirnoff _bad:
6183b3a8eb9SGleb Smirnoff 	if (dyn->pfid_kt != NULL)
6193b3a8eb9SGleb Smirnoff 		pfr_detach_table(dyn->pfid_kt);
6203b3a8eb9SGleb Smirnoff 	if (ruleset != NULL)
621e86bddeaSKristof Provost 		pf_remove_if_empty_kruleset(ruleset);
622320c1116SKristof Provost 	pfi_kkif_unref(dyn->pfid_kif);
6233b3a8eb9SGleb Smirnoff 	free(dyn, PFI_MTYPE);
6243b3a8eb9SGleb Smirnoff 
6253b3a8eb9SGleb Smirnoff 	return (rv);
6263b3a8eb9SGleb Smirnoff }
6273b3a8eb9SGleb Smirnoff 
6283b3a8eb9SGleb Smirnoff static void
pfi_kkif_update(struct pfi_kkif * kif)629320c1116SKristof Provost pfi_kkif_update(struct pfi_kkif *kif)
6303b3a8eb9SGleb Smirnoff {
6313b3a8eb9SGleb Smirnoff 	struct ifg_list		*ifgl;
632e3e03bc1SKristof Provost 	struct ifg_member	*ifgm;
6333b3a8eb9SGleb Smirnoff 	struct pfi_dynaddr	*p;
634320c1116SKristof Provost 	struct pfi_kkif		*tmpkif;
6353b3a8eb9SGleb Smirnoff 
636b8a6e03fSGleb Smirnoff 	NET_EPOCH_ASSERT();
6373b3a8eb9SGleb Smirnoff 	PF_RULES_WASSERT();
6383b3a8eb9SGleb Smirnoff 
6393b3a8eb9SGleb Smirnoff 	/* update all dynaddr */
6403b3a8eb9SGleb Smirnoff 	TAILQ_FOREACH(p, &kif->pfik_dynaddrs, entry)
6413b3a8eb9SGleb Smirnoff 		pfi_dynaddr_update(p);
6423b3a8eb9SGleb Smirnoff 
643e3e03bc1SKristof Provost 	/* Apply group flags to new members. */
644e3e03bc1SKristof Provost 	if (kif->pfik_group != NULL) {
645e3e03bc1SKristof Provost 		CK_STAILQ_FOREACH(ifgm, &kif->pfik_group->ifg_members,
646e3e03bc1SKristof Provost 		    ifgm_next) {
647320c1116SKristof Provost 			tmpkif = (struct pfi_kkif *)ifgm->ifgm_ifp->if_pf_kif;
648e3e03bc1SKristof Provost 			if (tmpkif == NULL)
649e3e03bc1SKristof Provost 				continue;
650e3e03bc1SKristof Provost 
651e3e03bc1SKristof Provost 			tmpkif->pfik_flags |= kif->pfik_flags;
652e3e03bc1SKristof Provost 		}
653e3e03bc1SKristof Provost 	}
654e3e03bc1SKristof Provost 
6553b3a8eb9SGleb Smirnoff 	/* again for all groups kif is member of */
6563b3a8eb9SGleb Smirnoff 	if (kif->pfik_ifp != NULL) {
6574f6c66ccSMatt Macy 		CK_STAILQ_FOREACH(ifgl, &kif->pfik_ifp->if_groups, ifgl_next)
658320c1116SKristof Provost 			pfi_kkif_update((struct pfi_kkif *)
6593b3a8eb9SGleb Smirnoff 			    ifgl->ifgl_group->ifg_pf_kif);
6603b3a8eb9SGleb Smirnoff 	}
6613b3a8eb9SGleb Smirnoff }
6623b3a8eb9SGleb Smirnoff 
6633b3a8eb9SGleb Smirnoff static void
pfi_dynaddr_update(struct pfi_dynaddr * dyn)6643b3a8eb9SGleb Smirnoff pfi_dynaddr_update(struct pfi_dynaddr *dyn)
6653b3a8eb9SGleb Smirnoff {
666320c1116SKristof Provost 	struct pfi_kkif		*kif;
6673b3a8eb9SGleb Smirnoff 	struct pfr_ktable	*kt;
6683b3a8eb9SGleb Smirnoff 
6693b3a8eb9SGleb Smirnoff 	PF_RULES_WASSERT();
6703b3a8eb9SGleb Smirnoff 	KASSERT(dyn && dyn->pfid_kif && dyn->pfid_kt,
6713b3a8eb9SGleb Smirnoff 	    ("%s: bad argument", __func__));
6723b3a8eb9SGleb Smirnoff 
6733b3a8eb9SGleb Smirnoff 	kif = dyn->pfid_kif;
6743b3a8eb9SGleb Smirnoff 	kt = dyn->pfid_kt;
6753b3a8eb9SGleb Smirnoff 
6763b3a8eb9SGleb Smirnoff 	if (kt->pfrkt_larg != V_pfi_update) {
6773b3a8eb9SGleb Smirnoff 		/* this table needs to be brought up-to-date */
6783b3a8eb9SGleb Smirnoff 		pfi_table_update(kt, kif, dyn->pfid_net, dyn->pfid_iflags);
6793b3a8eb9SGleb Smirnoff 		kt->pfrkt_larg = V_pfi_update;
6803b3a8eb9SGleb Smirnoff 	}
6813b3a8eb9SGleb Smirnoff 	pfr_dynaddr_update(kt, dyn);
6823b3a8eb9SGleb Smirnoff }
6833b3a8eb9SGleb Smirnoff 
6843b3a8eb9SGleb Smirnoff static void
pfi_table_update(struct pfr_ktable * kt,struct pfi_kkif * kif,uint8_t net,int flags)6858e7d333fSKristof Provost pfi_table_update(struct pfr_ktable *kt, struct pfi_kkif *kif, uint8_t net,
6868e7d333fSKristof Provost     int flags)
6873b3a8eb9SGleb Smirnoff {
6883b3a8eb9SGleb Smirnoff 	int			 e, size2 = 0;
6893b3a8eb9SGleb Smirnoff 	struct ifg_member	*ifgm;
6903b3a8eb9SGleb Smirnoff 
691b8a6e03fSGleb Smirnoff 	NET_EPOCH_ASSERT();
692b8a6e03fSGleb Smirnoff 
6933b3a8eb9SGleb Smirnoff 	V_pfi_buffer_cnt = 0;
6943b3a8eb9SGleb Smirnoff 
6953b3a8eb9SGleb Smirnoff 	if (kif->pfik_ifp != NULL)
6963b3a8eb9SGleb Smirnoff 		pfi_instance_add(kif->pfik_ifp, net, flags);
6973b3a8eb9SGleb Smirnoff 	else if (kif->pfik_group != NULL) {
6984f6c66ccSMatt Macy 		CK_STAILQ_FOREACH(ifgm, &kif->pfik_group->ifg_members, ifgm_next)
6993b3a8eb9SGleb Smirnoff 			pfi_instance_add(ifgm->ifgm_ifp, net, flags);
7003b3a8eb9SGleb Smirnoff 	}
7013b3a8eb9SGleb Smirnoff 
7023b3a8eb9SGleb Smirnoff 	if ((e = pfr_set_addrs(&kt->pfrkt_t, V_pfi_buffer, V_pfi_buffer_cnt, &size2,
7033b3a8eb9SGleb Smirnoff 	    NULL, NULL, NULL, 0, PFR_TFLAG_ALLMASK)))
7043b3a8eb9SGleb Smirnoff 		printf("%s: cannot set %d new addresses into table %s: %d\n",
7053b3a8eb9SGleb Smirnoff 		    __func__, V_pfi_buffer_cnt, kt->pfrkt_name, e);
7063b3a8eb9SGleb Smirnoff }
7073b3a8eb9SGleb Smirnoff 
7083b3a8eb9SGleb Smirnoff static void
pfi_instance_add(struct ifnet * ifp,uint8_t net,int flags)7098e7d333fSKristof Provost pfi_instance_add(struct ifnet *ifp, uint8_t net, int flags)
7103b3a8eb9SGleb Smirnoff {
7113b3a8eb9SGleb Smirnoff 	struct ifaddr	*ia;
7123b3a8eb9SGleb Smirnoff 	int		 got4 = 0, got6 = 0;
7138e7d333fSKristof Provost 	sa_family_t	 af;
7148e7d333fSKristof Provost 	uint8_t		 net2;
7153b3a8eb9SGleb Smirnoff 
716b8a6e03fSGleb Smirnoff 	NET_EPOCH_ASSERT();
717b8a6e03fSGleb Smirnoff 
718d7c5a620SMatt Macy 	CK_STAILQ_FOREACH(ia, &ifp->if_addrhead, ifa_link) {
7193b3a8eb9SGleb Smirnoff 		if (ia->ifa_addr == NULL)
7203b3a8eb9SGleb Smirnoff 			continue;
7213b3a8eb9SGleb Smirnoff 		af = ia->ifa_addr->sa_family;
7223b3a8eb9SGleb Smirnoff 		if (af != AF_INET && af != AF_INET6)
7233b3a8eb9SGleb Smirnoff 			continue;
7243b3a8eb9SGleb Smirnoff 		/*
7253b3a8eb9SGleb Smirnoff 		 * XXX: For point-to-point interfaces, (ifname:0) and IPv4,
7263b3a8eb9SGleb Smirnoff 		 *      jump over addresses without a proper route to work
7273b3a8eb9SGleb Smirnoff 		 *      around a problem with ppp not fully removing the
7283b3a8eb9SGleb Smirnoff 		 *      address used during IPCP.
7293b3a8eb9SGleb Smirnoff 		 */
7303b3a8eb9SGleb Smirnoff 		if ((ifp->if_flags & IFF_POINTOPOINT) &&
7313b3a8eb9SGleb Smirnoff 		    !(ia->ifa_flags & IFA_ROUTE) &&
7323b3a8eb9SGleb Smirnoff 		    (flags & PFI_AFLAG_NOALIAS) && (af == AF_INET))
7333b3a8eb9SGleb Smirnoff 			continue;
7343b3a8eb9SGleb Smirnoff 		if ((flags & PFI_AFLAG_BROADCAST) && af == AF_INET6)
7353b3a8eb9SGleb Smirnoff 			continue;
7363b3a8eb9SGleb Smirnoff 		if ((flags & PFI_AFLAG_BROADCAST) &&
7373b3a8eb9SGleb Smirnoff 		    !(ifp->if_flags & IFF_BROADCAST))
7383b3a8eb9SGleb Smirnoff 			continue;
7393b3a8eb9SGleb Smirnoff 		if ((flags & PFI_AFLAG_PEER) &&
7403b3a8eb9SGleb Smirnoff 		    !(ifp->if_flags & IFF_POINTOPOINT))
7413b3a8eb9SGleb Smirnoff 			continue;
74299eb0055SKristof Provost 		if ((flags & (PFI_AFLAG_NETWORK | PFI_AFLAG_NOALIAS)) &&
74399eb0055SKristof Provost 		    af == AF_INET6 &&
7443b3a8eb9SGleb Smirnoff 		    IN6_IS_ADDR_LINKLOCAL(
7453b3a8eb9SGleb Smirnoff 		    &((struct sockaddr_in6 *)ia->ifa_addr)->sin6_addr))
7463b3a8eb9SGleb Smirnoff 			continue;
7473b3a8eb9SGleb Smirnoff 		if (flags & PFI_AFLAG_NOALIAS) {
7483b3a8eb9SGleb Smirnoff 			if (af == AF_INET && got4)
7493b3a8eb9SGleb Smirnoff 				continue;
7503b3a8eb9SGleb Smirnoff 			if (af == AF_INET6 && got6)
7513b3a8eb9SGleb Smirnoff 				continue;
7523b3a8eb9SGleb Smirnoff 		}
7533b3a8eb9SGleb Smirnoff 		if (af == AF_INET)
7543b3a8eb9SGleb Smirnoff 			got4 = 1;
7553b3a8eb9SGleb Smirnoff 		else if (af == AF_INET6)
7563b3a8eb9SGleb Smirnoff 			got6 = 1;
7573b3a8eb9SGleb Smirnoff 		net2 = net;
7583b3a8eb9SGleb Smirnoff 		if (net2 == 128 && (flags & PFI_AFLAG_NETWORK)) {
7593b3a8eb9SGleb Smirnoff 			if (af == AF_INET)
7603b3a8eb9SGleb Smirnoff 				net2 = pfi_unmask(&((struct sockaddr_in *)
7613b3a8eb9SGleb Smirnoff 				    ia->ifa_netmask)->sin_addr);
7623b3a8eb9SGleb Smirnoff 			else if (af == AF_INET6)
7633b3a8eb9SGleb Smirnoff 				net2 = pfi_unmask(&((struct sockaddr_in6 *)
7643b3a8eb9SGleb Smirnoff 				    ia->ifa_netmask)->sin6_addr);
7653b3a8eb9SGleb Smirnoff 		}
7663b3a8eb9SGleb Smirnoff 		if (af == AF_INET && net2 > 32)
7673b3a8eb9SGleb Smirnoff 			net2 = 32;
7683b3a8eb9SGleb Smirnoff 		if (flags & PFI_AFLAG_BROADCAST)
7693b3a8eb9SGleb Smirnoff 			pfi_address_add(ia->ifa_broadaddr, af, net2);
7703b3a8eb9SGleb Smirnoff 		else if (flags & PFI_AFLAG_PEER)
7713b3a8eb9SGleb Smirnoff 			pfi_address_add(ia->ifa_dstaddr, af, net2);
7723b3a8eb9SGleb Smirnoff 		else
7733b3a8eb9SGleb Smirnoff 			pfi_address_add(ia->ifa_addr, af, net2);
7743b3a8eb9SGleb Smirnoff 	}
7753b3a8eb9SGleb Smirnoff }
7763b3a8eb9SGleb Smirnoff 
7773b3a8eb9SGleb Smirnoff static void
pfi_address_add(struct sockaddr * sa,sa_family_t af,uint8_t net)7788e7d333fSKristof Provost pfi_address_add(struct sockaddr *sa, sa_family_t af, uint8_t net)
7793b3a8eb9SGleb Smirnoff {
7803b3a8eb9SGleb Smirnoff 	struct pfr_addr	*p;
7813b3a8eb9SGleb Smirnoff 	int		 i;
7823b3a8eb9SGleb Smirnoff 
7833b3a8eb9SGleb Smirnoff 	if (V_pfi_buffer_cnt >= V_pfi_buffer_max) {
7843b3a8eb9SGleb Smirnoff 		int		 new_max = V_pfi_buffer_max * 2;
7853b3a8eb9SGleb Smirnoff 
7863b3a8eb9SGleb Smirnoff 		if (new_max > PFI_BUFFER_MAX) {
7873b3a8eb9SGleb Smirnoff 			printf("%s: address buffer full (%d/%d)\n", __func__,
7883b3a8eb9SGleb Smirnoff 			    V_pfi_buffer_cnt, PFI_BUFFER_MAX);
7893b3a8eb9SGleb Smirnoff 			return;
7903b3a8eb9SGleb Smirnoff 		}
7913b3a8eb9SGleb Smirnoff 		p = malloc(new_max * sizeof(*V_pfi_buffer), PFI_MTYPE,
7923b3a8eb9SGleb Smirnoff 		    M_NOWAIT);
7933b3a8eb9SGleb Smirnoff 		if (p == NULL) {
7943b3a8eb9SGleb Smirnoff 			printf("%s: no memory to grow buffer (%d/%d)\n",
7953b3a8eb9SGleb Smirnoff 			    __func__, V_pfi_buffer_cnt, PFI_BUFFER_MAX);
7963b3a8eb9SGleb Smirnoff 			return;
7973b3a8eb9SGleb Smirnoff 		}
79822932fc9SLuiz Otavio O Souza 		memcpy(p, V_pfi_buffer, V_pfi_buffer_max * sizeof(*V_pfi_buffer));
7993b3a8eb9SGleb Smirnoff 		/* no need to zero buffer */
8003b3a8eb9SGleb Smirnoff 		free(V_pfi_buffer, PFI_MTYPE);
8013b3a8eb9SGleb Smirnoff 		V_pfi_buffer = p;
8023b3a8eb9SGleb Smirnoff 		V_pfi_buffer_max = new_max;
8033b3a8eb9SGleb Smirnoff 	}
8043b3a8eb9SGleb Smirnoff 	if (af == AF_INET && net > 32)
8053b3a8eb9SGleb Smirnoff 		net = 128;
8063b3a8eb9SGleb Smirnoff 	p = V_pfi_buffer + V_pfi_buffer_cnt++;
8076b598e26SEd Maste 	memset(p, 0, sizeof(*p));
8083b3a8eb9SGleb Smirnoff 	p->pfra_af = af;
8093b3a8eb9SGleb Smirnoff 	p->pfra_net = net;
8103b3a8eb9SGleb Smirnoff 	if (af == AF_INET)
8113b3a8eb9SGleb Smirnoff 		p->pfra_ip4addr = ((struct sockaddr_in *)sa)->sin_addr;
8123b3a8eb9SGleb Smirnoff 	else if (af == AF_INET6) {
8133b3a8eb9SGleb Smirnoff 		p->pfra_ip6addr = ((struct sockaddr_in6 *)sa)->sin6_addr;
8143b3a8eb9SGleb Smirnoff 		if (IN6_IS_SCOPE_EMBED(&p->pfra_ip6addr))
8153b3a8eb9SGleb Smirnoff 			p->pfra_ip6addr.s6_addr16[1] = 0;
8163b3a8eb9SGleb Smirnoff 	}
8173b3a8eb9SGleb Smirnoff 	/* mask network address bits */
8183b3a8eb9SGleb Smirnoff 	if (net < 128)
8193b3a8eb9SGleb Smirnoff 		((caddr_t)p)[p->pfra_net/8] &= ~(0xFF >> (p->pfra_net%8));
8203b3a8eb9SGleb Smirnoff 	for (i = (p->pfra_net+7)/8; i < sizeof(p->pfra_u); i++)
8213b3a8eb9SGleb Smirnoff 		((caddr_t)p)[i] = 0;
8223b3a8eb9SGleb Smirnoff }
8233b3a8eb9SGleb Smirnoff 
8243b3a8eb9SGleb Smirnoff void
pfi_dynaddr_remove(struct pfi_dynaddr * dyn)8253b3a8eb9SGleb Smirnoff pfi_dynaddr_remove(struct pfi_dynaddr *dyn)
8263b3a8eb9SGleb Smirnoff {
8273b3a8eb9SGleb Smirnoff 
8283b3a8eb9SGleb Smirnoff 	KASSERT(dyn->pfid_kif != NULL, ("%s: null pfid_kif", __func__));
8293b3a8eb9SGleb Smirnoff 	KASSERT(dyn->pfid_kt != NULL, ("%s: null pfid_kt", __func__));
8303b3a8eb9SGleb Smirnoff 
8313b3a8eb9SGleb Smirnoff 	TAILQ_REMOVE(&dyn->pfid_kif->pfik_dynaddrs, dyn, entry);
832320c1116SKristof Provost 	pfi_kkif_unref(dyn->pfid_kif);
8333b3a8eb9SGleb Smirnoff 	pfr_detach_table(dyn->pfid_kt);
8343b3a8eb9SGleb Smirnoff 	free(dyn, PFI_MTYPE);
8353b3a8eb9SGleb Smirnoff }
8363b3a8eb9SGleb Smirnoff 
8373b3a8eb9SGleb Smirnoff void
pfi_dynaddr_copyout(struct pf_addr_wrap * aw)8383b3a8eb9SGleb Smirnoff pfi_dynaddr_copyout(struct pf_addr_wrap *aw)
8393b3a8eb9SGleb Smirnoff {
8403b3a8eb9SGleb Smirnoff 
8413b3a8eb9SGleb Smirnoff 	KASSERT(aw->type == PF_ADDR_DYNIFTL,
8423b3a8eb9SGleb Smirnoff 	    ("%s: type %u", __func__, aw->type));
8433b3a8eb9SGleb Smirnoff 
8443b3a8eb9SGleb Smirnoff 	if (aw->p.dyn == NULL || aw->p.dyn->pfid_kif == NULL)
8453b3a8eb9SGleb Smirnoff 		return;
8463b3a8eb9SGleb Smirnoff 	aw->p.dyncnt = aw->p.dyn->pfid_acnt4 + aw->p.dyn->pfid_acnt6;
8473b3a8eb9SGleb Smirnoff }
8483b3a8eb9SGleb Smirnoff 
8493b3a8eb9SGleb Smirnoff static int
pfi_kkif_compare(struct pfi_kkif * p,struct pfi_kkif * q)850320c1116SKristof Provost pfi_kkif_compare(struct pfi_kkif *p, struct pfi_kkif *q)
8513b3a8eb9SGleb Smirnoff {
8523b3a8eb9SGleb Smirnoff 	return (strncmp(p->pfik_name, q->pfik_name, IFNAMSIZ));
8533b3a8eb9SGleb Smirnoff }
8543b3a8eb9SGleb Smirnoff 
8553b3a8eb9SGleb Smirnoff void
pfi_update_status(const char * name,struct pf_status * pfs)8563b3a8eb9SGleb Smirnoff pfi_update_status(const char *name, struct pf_status *pfs)
8573b3a8eb9SGleb Smirnoff {
858320c1116SKristof Provost 	struct pfi_kkif		*p;
8593b3a8eb9SGleb Smirnoff 	struct pfi_kif_cmp	 key;
8603b3a8eb9SGleb Smirnoff 	struct ifg_member	 p_member, *ifgm;
8614f6c66ccSMatt Macy 	CK_STAILQ_HEAD(, ifg_member) ifg_members;
8623b3a8eb9SGleb Smirnoff 	int			 i, j, k;
8633b3a8eb9SGleb Smirnoff 
86433367037SMark Johnston 	if (pfs) {
8656b598e26SEd Maste 		memset(pfs->pcounters, 0, sizeof(pfs->pcounters));
8666b598e26SEd Maste 		memset(pfs->bcounters, 0, sizeof(pfs->bcounters));
86733367037SMark Johnston 	}
86833367037SMark Johnston 
8693b3a8eb9SGleb Smirnoff 	strlcpy(key.pfik_name, name, sizeof(key.pfik_name));
870320c1116SKristof Provost 	p = RB_FIND(pfi_ifhead, &V_pfi_ifs, (struct pfi_kkif *)&key);
8716b598e26SEd Maste 	if (p == NULL) {
8723b3a8eb9SGleb Smirnoff 		return;
8736b598e26SEd Maste 	}
8743b3a8eb9SGleb Smirnoff 
8753b3a8eb9SGleb Smirnoff 	if (p->pfik_group != NULL) {
8766b598e26SEd Maste 		memcpy(&ifg_members, &p->pfik_group->ifg_members,
8773b3a8eb9SGleb Smirnoff 		    sizeof(ifg_members));
8783b3a8eb9SGleb Smirnoff 	} else {
8793b3a8eb9SGleb Smirnoff 		/* build a temporary list for p only */
8806b598e26SEd Maste 		memset(&p_member, 0, sizeof(p_member));
8813b3a8eb9SGleb Smirnoff 		p_member.ifgm_ifp = p->pfik_ifp;
8824f6c66ccSMatt Macy 		CK_STAILQ_INIT(&ifg_members);
8834f6c66ccSMatt Macy 		CK_STAILQ_INSERT_TAIL(&ifg_members, &p_member, ifgm_next);
8843b3a8eb9SGleb Smirnoff 	}
8854f6c66ccSMatt Macy 	CK_STAILQ_FOREACH(ifgm, &ifg_members, ifgm_next) {
886a0429b54SBjoern A. Zeeb 		if (ifgm->ifgm_ifp == NULL || ifgm->ifgm_ifp->if_pf_kif == NULL)
8873b3a8eb9SGleb Smirnoff 			continue;
888320c1116SKristof Provost 		p = (struct pfi_kkif *)ifgm->ifgm_ifp->if_pf_kif;
8893b3a8eb9SGleb Smirnoff 
8903b3a8eb9SGleb Smirnoff 		/* just clear statistics */
8913b3a8eb9SGleb Smirnoff 		if (pfs == NULL) {
8925a3b9507SKristof Provost 			pf_kkif_zero(p);
8933b3a8eb9SGleb Smirnoff 			continue;
8943b3a8eb9SGleb Smirnoff 		}
8953b3a8eb9SGleb Smirnoff 		for (i = 0; i < 2; i++)
8963b3a8eb9SGleb Smirnoff 			for (j = 0; j < 2; j++)
8973b3a8eb9SGleb Smirnoff 				for (k = 0; k < 2; k++) {
8983b3a8eb9SGleb Smirnoff 					pfs->pcounters[i][j][k] +=
899d40d4b3eSMateusz Guzik 					    pf_counter_u64_fetch(&p->pfik_packets[i][j][k]);
9003b3a8eb9SGleb Smirnoff 					pfs->bcounters[i][j] +=
901d40d4b3eSMateusz Guzik 					    pf_counter_u64_fetch(&p->pfik_bytes[i][j][k]);
9023b3a8eb9SGleb Smirnoff 				}
9033b3a8eb9SGleb Smirnoff 	}
9043b3a8eb9SGleb Smirnoff }
9053b3a8eb9SGleb Smirnoff 
906320c1116SKristof Provost static void
pf_kkif_to_kif(struct pfi_kkif * kkif,struct pfi_kif * kif)907d40d4b3eSMateusz Guzik pf_kkif_to_kif(struct pfi_kkif *kkif, struct pfi_kif *kif)
908320c1116SKristof Provost {
909320c1116SKristof Provost 
9106b598e26SEd Maste 	memset(kif, 0, sizeof(*kif));
911320c1116SKristof Provost 	strlcpy(kif->pfik_name, kkif->pfik_name, sizeof(kif->pfik_name));
912320c1116SKristof Provost 	for (int i = 0; i < 2; i++) {
913320c1116SKristof Provost 		for (int j = 0; j < 2; j++) {
914320c1116SKristof Provost 			for (int k = 0; k < 2; k++) {
915320c1116SKristof Provost 				kif->pfik_packets[i][j][k] =
916d40d4b3eSMateusz Guzik 				    pf_counter_u64_fetch(&kkif->pfik_packets[i][j][k]);
917320c1116SKristof Provost 				kif->pfik_bytes[i][j][k] =
918d40d4b3eSMateusz Guzik 				    pf_counter_u64_fetch(&kkif->pfik_bytes[i][j][k]);
919320c1116SKristof Provost 			}
920320c1116SKristof Provost 		}
921320c1116SKristof Provost 	}
9220fcb03fbSKristof Provost 	kif->pfik_flags = kkif->pfik_flags;
923320c1116SKristof Provost 	kif->pfik_tzero = kkif->pfik_tzero;
924320c1116SKristof Provost 	kif->pfik_rulerefs = kkif->pfik_rulerefs;
925d69cc040SKristof Provost 	/*
926d69cc040SKristof Provost 	 * Userspace relies on this pointer to decide if this is a group or
927d69cc040SKristof Provost 	 * not. We don't want to share the actual pointer, because it's
928d69cc040SKristof Provost 	 * useless to userspace and leaks kernel memory layout information.
929d69cc040SKristof Provost 	 * So instead we provide 0xfeedcode as 'true' and NULL as 'false'.
930d69cc040SKristof Provost 	 */
931d69cc040SKristof Provost 	kif->pfik_group =
932d69cc040SKristof Provost 	    kkif->pfik_group ? (struct ifg_group *)0xfeedc0de : NULL;
933320c1116SKristof Provost }
934320c1116SKristof Provost 
9353b3a8eb9SGleb Smirnoff void
pfi_get_ifaces(const char * name,struct pfi_kif * buf,int * size)9363b3a8eb9SGleb Smirnoff pfi_get_ifaces(const char *name, struct pfi_kif *buf, int *size)
9373b3a8eb9SGleb Smirnoff {
938b8a6e03fSGleb Smirnoff 	struct epoch_tracker et;
939320c1116SKristof Provost 	struct pfi_kkif	*p, *nextp;
9403b3a8eb9SGleb Smirnoff 	int		 n = 0;
9413b3a8eb9SGleb Smirnoff 
942b8a6e03fSGleb Smirnoff 	NET_EPOCH_ENTER(et);
9433b3a8eb9SGleb Smirnoff 	for (p = RB_MIN(pfi_ifhead, &V_pfi_ifs); p; p = nextp) {
9443b3a8eb9SGleb Smirnoff 		nextp = RB_NEXT(pfi_ifhead, &V_pfi_ifs, p);
9453b3a8eb9SGleb Smirnoff 		if (pfi_skip_if(name, p))
9463b3a8eb9SGleb Smirnoff 			continue;
9473b3a8eb9SGleb Smirnoff 		if (*size <= n++)
9483b3a8eb9SGleb Smirnoff 			break;
9493b3a8eb9SGleb Smirnoff 		if (!p->pfik_tzero)
9503b3a8eb9SGleb Smirnoff 			p->pfik_tzero = time_second;
951320c1116SKristof Provost 		pf_kkif_to_kif(p, buf++);
9523b3a8eb9SGleb Smirnoff 		nextp = RB_NEXT(pfi_ifhead, &V_pfi_ifs, p);
9533b3a8eb9SGleb Smirnoff 	}
9543b3a8eb9SGleb Smirnoff 	*size = n;
955b8a6e03fSGleb Smirnoff 	NET_EPOCH_EXIT(et);
9563b3a8eb9SGleb Smirnoff }
9573b3a8eb9SGleb Smirnoff 
9583b3a8eb9SGleb Smirnoff static int
pfi_skip_if(const char * filter,struct pfi_kkif * p)959320c1116SKristof Provost pfi_skip_if(const char *filter, struct pfi_kkif *p)
9603b3a8eb9SGleb Smirnoff {
96133b242b5SKristof Provost 	struct ifg_list *i;
9623b3a8eb9SGleb Smirnoff 	int	n;
9633b3a8eb9SGleb Smirnoff 
964b8a6e03fSGleb Smirnoff 	NET_EPOCH_ASSERT();
965b8a6e03fSGleb Smirnoff 
9663b3a8eb9SGleb Smirnoff 	if (filter == NULL || !*filter)
9673b3a8eb9SGleb Smirnoff 		return (0);
9683b3a8eb9SGleb Smirnoff 	if (!strcmp(p->pfik_name, filter))
9693b3a8eb9SGleb Smirnoff 		return (0);	/* exact match */
9703b3a8eb9SGleb Smirnoff 	n = strlen(filter);
9713b3a8eb9SGleb Smirnoff 	if (n < 1 || n >= IFNAMSIZ)
9723b3a8eb9SGleb Smirnoff 		return (1);	/* sanity check */
9733b3a8eb9SGleb Smirnoff 	if (filter[n-1] >= '0' && filter[n-1] <= '9')
97433b242b5SKristof Provost 		return (1);	/* group names may not end in a digit */
975b8a6e03fSGleb Smirnoff 	if (p->pfik_ifp == NULL)
976b8a6e03fSGleb Smirnoff 		return (1);
977b8a6e03fSGleb Smirnoff 	CK_STAILQ_FOREACH(i, &p->pfik_ifp->if_groups, ifgl_next)
978b8a6e03fSGleb Smirnoff 		if (!strncmp(i->ifgl_group->ifg_group, filter, IFNAMSIZ))
97933b242b5SKristof Provost 			return (0); /* iface is in group "filter" */
98033b242b5SKristof Provost 	return (1);
9813b3a8eb9SGleb Smirnoff }
9823b3a8eb9SGleb Smirnoff 
9833b3a8eb9SGleb Smirnoff int
pfi_set_flags(const char * name,int flags)9843b3a8eb9SGleb Smirnoff pfi_set_flags(const char *name, int flags)
9853b3a8eb9SGleb Smirnoff {
986b8a6e03fSGleb Smirnoff 	struct epoch_tracker et;
987320c1116SKristof Provost 	struct pfi_kkif	*p, *kif;
988c9449e4fSKristof Provost 
98926c841e2SKristof Provost 	kif = pf_kkif_create(M_NOWAIT);
990c9449e4fSKristof Provost 	if (kif == NULL)
991c9449e4fSKristof Provost 		return (ENOMEM);
9923b3a8eb9SGleb Smirnoff 
993b8a6e03fSGleb Smirnoff 	NET_EPOCH_ENTER(et);
994c9449e4fSKristof Provost 
995320c1116SKristof Provost 	kif = pfi_kkif_attach(kif, name);
996c9449e4fSKristof Provost 
9973b3a8eb9SGleb Smirnoff 	RB_FOREACH(p, pfi_ifhead, &V_pfi_ifs) {
9983b3a8eb9SGleb Smirnoff 		if (pfi_skip_if(name, p))
9993b3a8eb9SGleb Smirnoff 			continue;
10003b3a8eb9SGleb Smirnoff 		p->pfik_flags |= flags;
10013b3a8eb9SGleb Smirnoff 	}
1002b8a6e03fSGleb Smirnoff 	NET_EPOCH_EXIT(et);
10033b3a8eb9SGleb Smirnoff 	return (0);
10043b3a8eb9SGleb Smirnoff }
10053b3a8eb9SGleb Smirnoff 
10063b3a8eb9SGleb Smirnoff int
pfi_clear_flags(const char * name,int flags)10073b3a8eb9SGleb Smirnoff pfi_clear_flags(const char *name, int flags)
10083b3a8eb9SGleb Smirnoff {
1009b8a6e03fSGleb Smirnoff 	struct epoch_tracker et;
1010320c1116SKristof Provost 	struct pfi_kkif *p, *tmp;
10113b3a8eb9SGleb Smirnoff 
1012b8a6e03fSGleb Smirnoff 	NET_EPOCH_ENTER(et);
1013c9449e4fSKristof Provost 	RB_FOREACH_SAFE(p, pfi_ifhead, &V_pfi_ifs, tmp) {
10143b3a8eb9SGleb Smirnoff 		if (pfi_skip_if(name, p))
10153b3a8eb9SGleb Smirnoff 			continue;
10163b3a8eb9SGleb Smirnoff 		p->pfik_flags &= ~flags;
1017c9449e4fSKristof Provost 
1018c9449e4fSKristof Provost 		if (p->pfik_ifp == NULL && p->pfik_group == NULL &&
101952b83a06SKristof Provost 		    p->pfik_flags == 0 && p->pfik_rulerefs == 0) {
1020c9449e4fSKristof Provost 			/* Delete this kif. */
1021c9449e4fSKristof Provost 			RB_REMOVE(pfi_ifhead, &V_pfi_ifs, p);
102226c841e2SKristof Provost 			pf_kkif_free(p);
1023c9449e4fSKristof Provost 		}
10243b3a8eb9SGleb Smirnoff 	}
1025b8a6e03fSGleb Smirnoff 	NET_EPOCH_EXIT(et);
10263b3a8eb9SGleb Smirnoff 	return (0);
10273b3a8eb9SGleb Smirnoff }
10283b3a8eb9SGleb Smirnoff 
10293b3a8eb9SGleb Smirnoff /* from pf_print_state.c */
10303b3a8eb9SGleb Smirnoff static int
pfi_unmask(void * addr)10313b3a8eb9SGleb Smirnoff pfi_unmask(void *addr)
10323b3a8eb9SGleb Smirnoff {
10333b3a8eb9SGleb Smirnoff 	struct pf_addr *m = addr;
10343b3a8eb9SGleb Smirnoff 	int i = 31, j = 0, b = 0;
10353b3a8eb9SGleb Smirnoff 	u_int32_t tmp;
10363b3a8eb9SGleb Smirnoff 
10373b3a8eb9SGleb Smirnoff 	while (j < 4 && m->addr32[j] == 0xffffffff) {
10383b3a8eb9SGleb Smirnoff 		b += 32;
10393b3a8eb9SGleb Smirnoff 		j++;
10403b3a8eb9SGleb Smirnoff 	}
10413b3a8eb9SGleb Smirnoff 	if (j < 4) {
10423b3a8eb9SGleb Smirnoff 		tmp = ntohl(m->addr32[j]);
10433b3a8eb9SGleb Smirnoff 		for (i = 31; tmp & (1 << i); --i)
10443b3a8eb9SGleb Smirnoff 			b++;
10453b3a8eb9SGleb Smirnoff 	}
10463b3a8eb9SGleb Smirnoff 	return (b);
10473b3a8eb9SGleb Smirnoff }
10483b3a8eb9SGleb Smirnoff 
10493b3a8eb9SGleb Smirnoff static void
pfi_attach_ifnet_event(void * arg __unused,struct ifnet * ifp)10503b3a8eb9SGleb Smirnoff pfi_attach_ifnet_event(void *arg __unused, struct ifnet *ifp)
10513b3a8eb9SGleb Smirnoff {
1052b8a6e03fSGleb Smirnoff 	struct epoch_tracker et;
1053320c1116SKristof Provost 	struct pfi_kkif *kif;
10543b3a8eb9SGleb Smirnoff 
1055a0429b54SBjoern A. Zeeb 	if (V_pf_vnet_active == 0) {
1056a0429b54SBjoern A. Zeeb 		/* Avoid teardown race in the least expensive way. */
1057a0429b54SBjoern A. Zeeb 		return;
1058a0429b54SBjoern A. Zeeb 	}
105926c841e2SKristof Provost 	kif = pf_kkif_create(M_NOWAIT);
1060b8a6e03fSGleb Smirnoff 	NET_EPOCH_ENTER(et);
10613b3a8eb9SGleb Smirnoff 	PF_RULES_WLOCK();
1062b8a6e03fSGleb Smirnoff 	pfi_attach_ifnet(ifp, kif);
1063b8a6e03fSGleb Smirnoff #ifdef ALTQ
10643b3a8eb9SGleb Smirnoff 	pf_altq_ifnet_event(ifp, 0);
10653b3a8eb9SGleb Smirnoff #endif
1066b8a6e03fSGleb Smirnoff 	PF_RULES_WUNLOCK();
1067b8a6e03fSGleb Smirnoff 	NET_EPOCH_EXIT(et);
10683b3a8eb9SGleb Smirnoff }
10693b3a8eb9SGleb Smirnoff 
10703b3a8eb9SGleb Smirnoff static void
pfi_detach_ifnet_event(void * arg __unused,struct ifnet * ifp)10713b3a8eb9SGleb Smirnoff pfi_detach_ifnet_event(void *arg __unused, struct ifnet *ifp)
10723b3a8eb9SGleb Smirnoff {
1073b8a6e03fSGleb Smirnoff 	struct epoch_tracker et;
1074320c1116SKristof Provost 	struct pfi_kkif *kif = (struct pfi_kkif *)ifp->if_pf_kif;
10753b3a8eb9SGleb Smirnoff 
1076fbbf436dSKristof Provost 	if (pfsync_detach_ifnet_ptr)
1077fbbf436dSKristof Provost 		pfsync_detach_ifnet_ptr(ifp);
1078fbbf436dSKristof Provost 
1079a0429b54SBjoern A. Zeeb 	if (kif == NULL)
1080a0429b54SBjoern A. Zeeb 		return;
1081a0429b54SBjoern A. Zeeb 
1082a0429b54SBjoern A. Zeeb 	if (V_pf_vnet_active == 0) {
1083a0429b54SBjoern A. Zeeb 		/* Avoid teardown race in the least expensive way. */
1084a0429b54SBjoern A. Zeeb 		return;
1085a0429b54SBjoern A. Zeeb 	}
1086fbbf436dSKristof Provost 
1087b8a6e03fSGleb Smirnoff 	NET_EPOCH_ENTER(et);
10883b3a8eb9SGleb Smirnoff 	PF_RULES_WLOCK();
10893b3a8eb9SGleb Smirnoff 	V_pfi_update++;
1090320c1116SKristof Provost 	pfi_kkif_update(kif);
10913b3a8eb9SGleb Smirnoff 
1092b2e0b24fSKristof Provost 	if (kif->pfik_ifp)
109314624ab5SKristof Provost 		if_rele(kif->pfik_ifp);
109414624ab5SKristof Provost 
10953b3a8eb9SGleb Smirnoff 	kif->pfik_ifp = NULL;
10963b3a8eb9SGleb Smirnoff 	ifp->if_pf_kif = NULL;
10973b3a8eb9SGleb Smirnoff #ifdef ALTQ
10983b3a8eb9SGleb Smirnoff 	pf_altq_ifnet_event(ifp, 1);
10993b3a8eb9SGleb Smirnoff #endif
1100bfeef0d3SNick Reilly 	pfi_kkif_remove_if_unref(kif);
1101bfeef0d3SNick Reilly 
11023b3a8eb9SGleb Smirnoff 	PF_RULES_WUNLOCK();
1103b8a6e03fSGleb Smirnoff 	NET_EPOCH_EXIT(et);
11043b3a8eb9SGleb Smirnoff }
11053b3a8eb9SGleb Smirnoff 
11063b3a8eb9SGleb Smirnoff static void
pfi_attach_group_event(void * arg __unused,struct ifg_group * ifg)11073a36ee40SMarko Zec pfi_attach_group_event(void *arg __unused, struct ifg_group *ifg)
11083b3a8eb9SGleb Smirnoff {
1109b8a6e03fSGleb Smirnoff 	struct epoch_tracker et;
1110320c1116SKristof Provost 	struct pfi_kkif *kif;
11113b3a8eb9SGleb Smirnoff 
1112a0429b54SBjoern A. Zeeb 	if (V_pf_vnet_active == 0) {
1113a0429b54SBjoern A. Zeeb 		/* Avoid teardown race in the least expensive way. */
1114a0429b54SBjoern A. Zeeb 		return;
1115a0429b54SBjoern A. Zeeb 	}
111626c841e2SKristof Provost 	kif = pf_kkif_create(M_WAITOK);
1117b8a6e03fSGleb Smirnoff 	NET_EPOCH_ENTER(et);
1118b8a6e03fSGleb Smirnoff 	PF_RULES_WLOCK();
1119b8a6e03fSGleb Smirnoff 	pfi_attach_ifgroup(ifg, kif);
1120b8a6e03fSGleb Smirnoff 	PF_RULES_WUNLOCK();
1121b8a6e03fSGleb Smirnoff 	NET_EPOCH_EXIT(et);
11223b3a8eb9SGleb Smirnoff }
11233b3a8eb9SGleb Smirnoff 
11243b3a8eb9SGleb Smirnoff static void
pfi_change_group_event(void * arg __unused,char * gname)11253a36ee40SMarko Zec pfi_change_group_event(void *arg __unused, char *gname)
11263b3a8eb9SGleb Smirnoff {
1127b8a6e03fSGleb Smirnoff 	struct epoch_tracker et;
1128320c1116SKristof Provost 	struct pfi_kkif *kif;
11293b3a8eb9SGleb Smirnoff 
1130a0429b54SBjoern A. Zeeb 	if (V_pf_vnet_active == 0) {
1131a0429b54SBjoern A. Zeeb 		/* Avoid teardown race in the least expensive way. */
1132a0429b54SBjoern A. Zeeb 		return;
1133a0429b54SBjoern A. Zeeb 	}
1134a0429b54SBjoern A. Zeeb 
113526c841e2SKristof Provost 	kif = pf_kkif_create(M_WAITOK);
1136b8a6e03fSGleb Smirnoff 	NET_EPOCH_ENTER(et);
11373b3a8eb9SGleb Smirnoff 	PF_RULES_WLOCK();
11383b3a8eb9SGleb Smirnoff 	V_pfi_update++;
1139320c1116SKristof Provost 	kif = pfi_kkif_attach(kif, gname);
1140320c1116SKristof Provost 	pfi_kkif_update(kif);
11413b3a8eb9SGleb Smirnoff 	PF_RULES_WUNLOCK();
1142b8a6e03fSGleb Smirnoff 	NET_EPOCH_EXIT(et);
11433b3a8eb9SGleb Smirnoff }
11443b3a8eb9SGleb Smirnoff 
11453b3a8eb9SGleb Smirnoff static void
pfi_detach_group_event(void * arg __unused,struct ifg_group * ifg)11463a36ee40SMarko Zec pfi_detach_group_event(void *arg __unused, struct ifg_group *ifg)
11473b3a8eb9SGleb Smirnoff {
1148320c1116SKristof Provost 	struct pfi_kkif *kif = (struct pfi_kkif *)ifg->ifg_pf_kif;
11493b3a8eb9SGleb Smirnoff 
1150a0429b54SBjoern A. Zeeb 	if (kif == NULL)
1151a0429b54SBjoern A. Zeeb 		return;
1152a0429b54SBjoern A. Zeeb 
1153a0429b54SBjoern A. Zeeb 	if (V_pf_vnet_active == 0) {
1154a0429b54SBjoern A. Zeeb 		/* Avoid teardown race in the least expensive way. */
1155a0429b54SBjoern A. Zeeb 		return;
1156a0429b54SBjoern A. Zeeb 	}
11573b3a8eb9SGleb Smirnoff 	PF_RULES_WLOCK();
11583b3a8eb9SGleb Smirnoff 	V_pfi_update++;
11593b3a8eb9SGleb Smirnoff 
11603b3a8eb9SGleb Smirnoff 	kif->pfik_group = NULL;
11613b3a8eb9SGleb Smirnoff 	ifg->ifg_pf_kif = NULL;
1162bfeef0d3SNick Reilly 
1163bfeef0d3SNick Reilly 	pfi_kkif_remove_if_unref(kif);
11643b3a8eb9SGleb Smirnoff 	PF_RULES_WUNLOCK();
11653b3a8eb9SGleb Smirnoff }
11663b3a8eb9SGleb Smirnoff 
11673b3a8eb9SGleb Smirnoff static void
pfi_ifaddr_event(void * arg __unused,struct ifnet * ifp)11683b3a8eb9SGleb Smirnoff pfi_ifaddr_event(void *arg __unused, struct ifnet *ifp)
11693b3a8eb9SGleb Smirnoff {
117073c90145SKristof Provost 
117173c90145SKristof Provost 	KASSERT(ifp, ("ifp == NULL"));
117273c90145SKristof Provost 
1173a0429b54SBjoern A. Zeeb 	if (ifp->if_pf_kif == NULL)
1174a0429b54SBjoern A. Zeeb 		return;
11753b3a8eb9SGleb Smirnoff 
1176a0429b54SBjoern A. Zeeb 	if (V_pf_vnet_active == 0) {
1177a0429b54SBjoern A. Zeeb 		/* Avoid teardown race in the least expensive way. */
1178a0429b54SBjoern A. Zeeb 		return;
1179a0429b54SBjoern A. Zeeb 	}
11803b3a8eb9SGleb Smirnoff 	PF_RULES_WLOCK();
118173c90145SKristof Provost 	if (ifp->if_pf_kif) {
1182b8a6e03fSGleb Smirnoff 		struct epoch_tracker et;
1183b8a6e03fSGleb Smirnoff 
11843b3a8eb9SGleb Smirnoff 		V_pfi_update++;
1185b8a6e03fSGleb Smirnoff 		NET_EPOCH_ENTER(et);
1186320c1116SKristof Provost 		pfi_kkif_update(ifp->if_pf_kif);
1187b8a6e03fSGleb Smirnoff 		NET_EPOCH_EXIT(et);
11883b3a8eb9SGleb Smirnoff 	}
11893b3a8eb9SGleb Smirnoff 	PF_RULES_WUNLOCK();
11903b3a8eb9SGleb Smirnoff }
1191