xref: /freebsd/sys/netpfil/ipfw/ip_fw_iface.c (revision ccba94b8fc30d4fbb7eaaa5b0bf90dbbd1dc7c64)
168394ec8SAlexander V. Chernikov /*-
268394ec8SAlexander V. Chernikov  * Copyright (c) 2014 Yandex LLC.
368394ec8SAlexander V. Chernikov  *
468394ec8SAlexander V. Chernikov  * Redistribution and use in source and binary forms, with or without
568394ec8SAlexander V. Chernikov  * modification, are permitted provided that the following conditions
668394ec8SAlexander V. Chernikov  * are met:
768394ec8SAlexander V. Chernikov  * 1. Redistributions of source code must retain the above copyright
868394ec8SAlexander V. Chernikov  *    notice, this list of conditions and the following disclaimer.
968394ec8SAlexander V. Chernikov  * 2. Redistributions in binary form must reproduce the above copyright
1068394ec8SAlexander V. Chernikov  *    notice, this list of conditions and the following disclaimer in the
1168394ec8SAlexander V. Chernikov  *    documentation and/or other materials provided with the distribution.
1268394ec8SAlexander V. Chernikov  *
1368394ec8SAlexander V. Chernikov  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1468394ec8SAlexander V. Chernikov  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1568394ec8SAlexander V. Chernikov  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1668394ec8SAlexander V. Chernikov  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
1768394ec8SAlexander V. Chernikov  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
1868394ec8SAlexander V. Chernikov  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
1968394ec8SAlexander V. Chernikov  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2068394ec8SAlexander V. Chernikov  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2168394ec8SAlexander V. Chernikov  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2268394ec8SAlexander V. Chernikov  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2368394ec8SAlexander V. Chernikov  * SUCH DAMAGE.
2468394ec8SAlexander V. Chernikov  */
2568394ec8SAlexander V. Chernikov 
2668394ec8SAlexander V. Chernikov #include <sys/cdefs.h>
2768394ec8SAlexander V. Chernikov __FBSDID("$FreeBSD: projects/ipfw/sys/netpfil/ipfw/ip_fw_iface.c 267384 2014-06-12 09:59:11Z melifaro $");
2868394ec8SAlexander V. Chernikov 
2968394ec8SAlexander V. Chernikov /*
3068394ec8SAlexander V. Chernikov  * Kernel interface tracking API.
3168394ec8SAlexander V. Chernikov  *
3268394ec8SAlexander V. Chernikov  */
3368394ec8SAlexander V. Chernikov 
3468394ec8SAlexander V. Chernikov #include "opt_ipfw.h"
3568394ec8SAlexander V. Chernikov #include "opt_inet.h"
3668394ec8SAlexander V. Chernikov #ifndef INET
3768394ec8SAlexander V. Chernikov #error IPFIREWALL requires INET.
3868394ec8SAlexander V. Chernikov #endif /* INET */
3968394ec8SAlexander V. Chernikov #include "opt_inet6.h"
4068394ec8SAlexander V. Chernikov 
4168394ec8SAlexander V. Chernikov #include <sys/param.h>
4268394ec8SAlexander V. Chernikov #include <sys/systm.h>
4368394ec8SAlexander V. Chernikov #include <sys/malloc.h>
4468394ec8SAlexander V. Chernikov #include <sys/kernel.h>
4568394ec8SAlexander V. Chernikov #include <sys/lock.h>
4668394ec8SAlexander V. Chernikov #include <sys/rwlock.h>
47*ccba94b8SAlexander V. Chernikov #include <sys/rmlock.h>
4868394ec8SAlexander V. Chernikov #include <sys/socket.h>
4968394ec8SAlexander V. Chernikov #include <sys/queue.h>
5068394ec8SAlexander V. Chernikov #include <sys/eventhandler.h>
5168394ec8SAlexander V. Chernikov #include <net/if.h>
5268394ec8SAlexander V. Chernikov #include <net/if_var.h>
5368394ec8SAlexander V. Chernikov #include <net/vnet.h>
5468394ec8SAlexander V. Chernikov 
5568394ec8SAlexander V. Chernikov #include <netinet/in.h>
5668394ec8SAlexander V. Chernikov #include <netinet/ip_var.h>	/* struct ipfw_rule_ref */
5768394ec8SAlexander V. Chernikov #include <netinet/ip_fw.h>
5868394ec8SAlexander V. Chernikov 
5968394ec8SAlexander V. Chernikov #include <netpfil/ipfw/ip_fw_private.h>
6068394ec8SAlexander V. Chernikov 
6168394ec8SAlexander V. Chernikov #define	CHAIN_TO_II(ch)		((struct namedobj_instance *)ch->ifcfg)
6268394ec8SAlexander V. Chernikov 
6368394ec8SAlexander V. Chernikov #define	DEFAULT_IFACES	128
6468394ec8SAlexander V. Chernikov 
6568394ec8SAlexander V. Chernikov static void handle_ifdetach(struct ip_fw_chain *ch, struct ipfw_iface *iif,
6668394ec8SAlexander V. Chernikov     uint16_t ifindex);
6768394ec8SAlexander V. Chernikov static void handle_ifattach(struct ip_fw_chain *ch, struct ipfw_iface *iif,
6868394ec8SAlexander V. Chernikov     uint16_t ifindex);
696b988f3aSAlexander V. Chernikov static int list_ifaces(struct ip_fw_chain *ch, ip_fw3_opheader *op3,
706b988f3aSAlexander V. Chernikov     struct sockopt_data *sd);
716b988f3aSAlexander V. Chernikov 
726b988f3aSAlexander V. Chernikov static struct ipfw_sopt_handler	scodes[] = {
736b988f3aSAlexander V. Chernikov 	{ IP_FW_XIFLIST,	0,	HDIR_GET,	list_ifaces },
746b988f3aSAlexander V. Chernikov };
7568394ec8SAlexander V. Chernikov 
7668394ec8SAlexander V. Chernikov /*
7768394ec8SAlexander V. Chernikov  * FreeBSD Kernel interface.
7868394ec8SAlexander V. Chernikov  */
7968394ec8SAlexander V. Chernikov static void ipfw_kifhandler(void *arg, struct ifnet *ifp);
8068394ec8SAlexander V. Chernikov static int ipfw_kiflookup(char *name);
8168394ec8SAlexander V. Chernikov static void iface_khandler_register(void);
8268394ec8SAlexander V. Chernikov static void iface_khandler_deregister(void);
8368394ec8SAlexander V. Chernikov 
8468394ec8SAlexander V. Chernikov static eventhandler_tag ipfw_ifdetach_event, ipfw_ifattach_event;
8568394ec8SAlexander V. Chernikov static int num_vnets = 0;
8698eff10eSAlexander V. Chernikov static struct mtx vnet_mtx;
8768394ec8SAlexander V. Chernikov 
8868394ec8SAlexander V. Chernikov /*
8968394ec8SAlexander V. Chernikov  * Checks if kernel interface is contained in our tracked
9068394ec8SAlexander V. Chernikov  * interface list and calls attach/detach handler.
9168394ec8SAlexander V. Chernikov  */
9268394ec8SAlexander V. Chernikov static void
9368394ec8SAlexander V. Chernikov ipfw_kifhandler(void *arg, struct ifnet *ifp)
9468394ec8SAlexander V. Chernikov {
9568394ec8SAlexander V. Chernikov 	struct ip_fw_chain *ch;
9668394ec8SAlexander V. Chernikov 	struct ipfw_iface *iif;
9768394ec8SAlexander V. Chernikov 	struct namedobj_instance *ii;
9868394ec8SAlexander V. Chernikov 	uintptr_t htype;
9968394ec8SAlexander V. Chernikov 
10098eff10eSAlexander V. Chernikov 	if (V_ipfw_vnet_ready == 0)
10198eff10eSAlexander V. Chernikov 		return;
10298eff10eSAlexander V. Chernikov 
10368394ec8SAlexander V. Chernikov 	ch = &V_layer3_chain;
10468394ec8SAlexander V. Chernikov 	htype = (uintptr_t)arg;
10568394ec8SAlexander V. Chernikov 
10668394ec8SAlexander V. Chernikov 	IPFW_UH_WLOCK(ch);
10768394ec8SAlexander V. Chernikov 	ii = CHAIN_TO_II(ch);
10868394ec8SAlexander V. Chernikov 	if (ii == NULL) {
10968394ec8SAlexander V. Chernikov 		IPFW_UH_WUNLOCK(ch);
11068394ec8SAlexander V. Chernikov 		return;
11168394ec8SAlexander V. Chernikov 	}
11298eff10eSAlexander V. Chernikov 	iif = (struct ipfw_iface*)ipfw_objhash_lookup_name(ii, 0,
11398eff10eSAlexander V. Chernikov 	    if_name(ifp));
11468394ec8SAlexander V. Chernikov 	if (iif != NULL) {
11568394ec8SAlexander V. Chernikov 		if (htype == 1)
11668394ec8SAlexander V. Chernikov 			handle_ifattach(ch, iif, ifp->if_index);
11768394ec8SAlexander V. Chernikov 		else
11868394ec8SAlexander V. Chernikov 			handle_ifdetach(ch, iif, ifp->if_index);
11968394ec8SAlexander V. Chernikov 	}
12068394ec8SAlexander V. Chernikov 	IPFW_UH_WUNLOCK(ch);
12168394ec8SAlexander V. Chernikov }
12268394ec8SAlexander V. Chernikov 
12368394ec8SAlexander V. Chernikov /*
12468394ec8SAlexander V. Chernikov  * Reference current VNET as iface tracking API user.
12568394ec8SAlexander V. Chernikov  * Registers interface tracking handlers for first VNET.
12668394ec8SAlexander V. Chernikov  */
12768394ec8SAlexander V. Chernikov static void
12868394ec8SAlexander V. Chernikov iface_khandler_register()
12968394ec8SAlexander V. Chernikov {
13068394ec8SAlexander V. Chernikov 	int create;
13168394ec8SAlexander V. Chernikov 
13268394ec8SAlexander V. Chernikov 	create = 0;
13368394ec8SAlexander V. Chernikov 
13468394ec8SAlexander V. Chernikov 	mtx_lock(&vnet_mtx);
13568394ec8SAlexander V. Chernikov 	if (num_vnets == 0)
13668394ec8SAlexander V. Chernikov 		create = 1;
13768394ec8SAlexander V. Chernikov 	num_vnets++;
13868394ec8SAlexander V. Chernikov 	mtx_unlock(&vnet_mtx);
13968394ec8SAlexander V. Chernikov 
14068394ec8SAlexander V. Chernikov 	if (create == 0)
14168394ec8SAlexander V. Chernikov 		return;
14268394ec8SAlexander V. Chernikov 
14368394ec8SAlexander V. Chernikov 	printf("IPFW: starting up interface tracker\n");
14468394ec8SAlexander V. Chernikov 
14568394ec8SAlexander V. Chernikov 	ipfw_ifdetach_event = EVENTHANDLER_REGISTER(
14668394ec8SAlexander V. Chernikov 	    ifnet_departure_event, ipfw_kifhandler, NULL,
14768394ec8SAlexander V. Chernikov 	    EVENTHANDLER_PRI_ANY);
14868394ec8SAlexander V. Chernikov 	ipfw_ifattach_event = EVENTHANDLER_REGISTER(
14968394ec8SAlexander V. Chernikov 	    ifnet_arrival_event, ipfw_kifhandler, (void*)((uintptr_t)1),
15068394ec8SAlexander V. Chernikov 	    EVENTHANDLER_PRI_ANY);
15168394ec8SAlexander V. Chernikov }
15268394ec8SAlexander V. Chernikov 
15368394ec8SAlexander V. Chernikov /*
15468394ec8SAlexander V. Chernikov  *
15568394ec8SAlexander V. Chernikov  * Detach interface event handlers on last VNET instance
15668394ec8SAlexander V. Chernikov  * detach.
15768394ec8SAlexander V. Chernikov  */
15868394ec8SAlexander V. Chernikov static void
15968394ec8SAlexander V. Chernikov iface_khandler_deregister()
16068394ec8SAlexander V. Chernikov {
16168394ec8SAlexander V. Chernikov 	int destroy;
16268394ec8SAlexander V. Chernikov 
16368394ec8SAlexander V. Chernikov 	destroy = 0;
16468394ec8SAlexander V. Chernikov 	mtx_lock(&vnet_mtx);
16598eff10eSAlexander V. Chernikov 	if (num_vnets == 1)
16668394ec8SAlexander V. Chernikov 		destroy = 1;
16798eff10eSAlexander V. Chernikov 	num_vnets--;
16868394ec8SAlexander V. Chernikov 	mtx_unlock(&vnet_mtx);
16968394ec8SAlexander V. Chernikov 
17068394ec8SAlexander V. Chernikov 	if (destroy == 0)
17168394ec8SAlexander V. Chernikov 		return;
17268394ec8SAlexander V. Chernikov 
17368394ec8SAlexander V. Chernikov 	EVENTHANDLER_DEREGISTER(ifnet_arrival_event,
17468394ec8SAlexander V. Chernikov 	    ipfw_ifattach_event);
17568394ec8SAlexander V. Chernikov 	EVENTHANDLER_DEREGISTER(ifnet_departure_event,
17668394ec8SAlexander V. Chernikov 	    ipfw_ifdetach_event);
17768394ec8SAlexander V. Chernikov }
17868394ec8SAlexander V. Chernikov 
17968394ec8SAlexander V. Chernikov /*
18068394ec8SAlexander V. Chernikov  * Retrieves ifindex for given @name.
18168394ec8SAlexander V. Chernikov  *
18268394ec8SAlexander V. Chernikov  * Returns ifindex or 0.
18368394ec8SAlexander V. Chernikov  */
18468394ec8SAlexander V. Chernikov static int
18568394ec8SAlexander V. Chernikov ipfw_kiflookup(char *name)
18668394ec8SAlexander V. Chernikov {
18768394ec8SAlexander V. Chernikov 	struct ifnet *ifp;
18868394ec8SAlexander V. Chernikov 	int ifindex;
18968394ec8SAlexander V. Chernikov 
19068394ec8SAlexander V. Chernikov 	ifindex = 0;
19168394ec8SAlexander V. Chernikov 
19268394ec8SAlexander V. Chernikov 	if ((ifp = ifunit_ref(name)) != NULL) {
19368394ec8SAlexander V. Chernikov 		ifindex = ifp->if_index;
19468394ec8SAlexander V. Chernikov 		if_rele(ifp);
19568394ec8SAlexander V. Chernikov 	}
19668394ec8SAlexander V. Chernikov 
19768394ec8SAlexander V. Chernikov 	return (ifindex);
19868394ec8SAlexander V. Chernikov }
19968394ec8SAlexander V. Chernikov 
20068394ec8SAlexander V. Chernikov /*
20168394ec8SAlexander V. Chernikov  * Global ipfw startup hook.
20268394ec8SAlexander V. Chernikov  * Since we perform lazy initialization, do nothing except
20368394ec8SAlexander V. Chernikov  * mutex init.
20468394ec8SAlexander V. Chernikov  */
20568394ec8SAlexander V. Chernikov int
20668394ec8SAlexander V. Chernikov ipfw_iface_init()
20768394ec8SAlexander V. Chernikov {
20868394ec8SAlexander V. Chernikov 
20968394ec8SAlexander V. Chernikov 	mtx_init(&vnet_mtx, "IPFW ifhandler mtx", NULL, MTX_DEF);
2106b988f3aSAlexander V. Chernikov 	IPFW_ADD_SOPT_HANDLER(1, scodes);
21168394ec8SAlexander V. Chernikov 	return (0);
21268394ec8SAlexander V. Chernikov }
21368394ec8SAlexander V. Chernikov 
21468394ec8SAlexander V. Chernikov /*
21568394ec8SAlexander V. Chernikov  * Global ipfw destroy hook.
21668394ec8SAlexander V. Chernikov  * Unregister khandlers iff init has been done.
21768394ec8SAlexander V. Chernikov  */
21868394ec8SAlexander V. Chernikov void
21968394ec8SAlexander V. Chernikov ipfw_iface_destroy()
22068394ec8SAlexander V. Chernikov {
22168394ec8SAlexander V. Chernikov 
2226b988f3aSAlexander V. Chernikov 	IPFW_DEL_SOPT_HANDLER(1, scodes);
22368394ec8SAlexander V. Chernikov 	mtx_destroy(&vnet_mtx);
22468394ec8SAlexander V. Chernikov }
22568394ec8SAlexander V. Chernikov 
22668394ec8SAlexander V. Chernikov /*
22768394ec8SAlexander V. Chernikov  * Perform actual init on internal request.
22868394ec8SAlexander V. Chernikov  * Inits both namehash and global khandler.
22968394ec8SAlexander V. Chernikov  */
23068394ec8SAlexander V. Chernikov static void
23168394ec8SAlexander V. Chernikov vnet_ipfw_iface_init(struct ip_fw_chain *ch)
23268394ec8SAlexander V. Chernikov {
23368394ec8SAlexander V. Chernikov 	struct namedobj_instance *ii;
23468394ec8SAlexander V. Chernikov 
23568394ec8SAlexander V. Chernikov 	ii = ipfw_objhash_create(DEFAULT_IFACES);
23668394ec8SAlexander V. Chernikov 	IPFW_UH_WLOCK(ch);
23768394ec8SAlexander V. Chernikov 	if (ch->ifcfg == NULL) {
23868394ec8SAlexander V. Chernikov 		ch->ifcfg = ii;
23968394ec8SAlexander V. Chernikov 		ii = NULL;
24068394ec8SAlexander V. Chernikov 	}
24168394ec8SAlexander V. Chernikov 	IPFW_UH_WUNLOCK(ch);
24268394ec8SAlexander V. Chernikov 
24368394ec8SAlexander V. Chernikov 	if (ii != NULL) {
24468394ec8SAlexander V. Chernikov 		/* Already initialized. Free namehash. */
24568394ec8SAlexander V. Chernikov 		ipfw_objhash_destroy(ii);
24668394ec8SAlexander V. Chernikov 	} else {
24768394ec8SAlexander V. Chernikov 		/* We're the first ones. Init kernel hooks. */
24868394ec8SAlexander V. Chernikov 		iface_khandler_register();
24968394ec8SAlexander V. Chernikov 	}
25068394ec8SAlexander V. Chernikov }
25168394ec8SAlexander V. Chernikov 
25268394ec8SAlexander V. Chernikov static void
25368394ec8SAlexander V. Chernikov destroy_iface(struct namedobj_instance *ii, struct named_object *no,
25468394ec8SAlexander V. Chernikov     void *arg)
25568394ec8SAlexander V. Chernikov {
25668394ec8SAlexander V. Chernikov 
25768394ec8SAlexander V. Chernikov 	/* Assume all consumers have been already detached */
25898eff10eSAlexander V. Chernikov 	free(no, M_IPFW);
25968394ec8SAlexander V. Chernikov }
26068394ec8SAlexander V. Chernikov 
26168394ec8SAlexander V. Chernikov /*
26268394ec8SAlexander V. Chernikov  * Per-VNET ipfw detach hook.
26368394ec8SAlexander V. Chernikov  *
26468394ec8SAlexander V. Chernikov  */
26568394ec8SAlexander V. Chernikov void
26668394ec8SAlexander V. Chernikov vnet_ipfw_iface_destroy(struct ip_fw_chain *ch)
26768394ec8SAlexander V. Chernikov {
26868394ec8SAlexander V. Chernikov 	struct namedobj_instance *ii;
26968394ec8SAlexander V. Chernikov 
27068394ec8SAlexander V. Chernikov 	IPFW_UH_WLOCK(ch);
27168394ec8SAlexander V. Chernikov 	ii = CHAIN_TO_II(ch);
27268394ec8SAlexander V. Chernikov 	ch->ifcfg = NULL;
27368394ec8SAlexander V. Chernikov 	IPFW_UH_WUNLOCK(ch);
27468394ec8SAlexander V. Chernikov 
27568394ec8SAlexander V. Chernikov 	if (ii != NULL) {
27668394ec8SAlexander V. Chernikov 		ipfw_objhash_foreach(ii, destroy_iface, ch);
27768394ec8SAlexander V. Chernikov 		ipfw_objhash_destroy(ii);
27868394ec8SAlexander V. Chernikov 		iface_khandler_deregister();
27968394ec8SAlexander V. Chernikov 	}
28068394ec8SAlexander V. Chernikov }
28168394ec8SAlexander V. Chernikov 
28268394ec8SAlexander V. Chernikov /*
28368394ec8SAlexander V. Chernikov  * Notify the subsystem that we are interested in tracking
28468394ec8SAlexander V. Chernikov  * interface @name. This function has to be called without
28568394ec8SAlexander V. Chernikov  * holding any locks to permit allocating the necessary states
28668394ec8SAlexander V. Chernikov  * for proper interface tracking.
28768394ec8SAlexander V. Chernikov  *
28868394ec8SAlexander V. Chernikov  * Returns 0 on success.
28968394ec8SAlexander V. Chernikov  */
29068394ec8SAlexander V. Chernikov int
29168394ec8SAlexander V. Chernikov ipfw_iface_ref(struct ip_fw_chain *ch, char *name,
29268394ec8SAlexander V. Chernikov     struct ipfw_ifc *ic)
29368394ec8SAlexander V. Chernikov {
29468394ec8SAlexander V. Chernikov 	struct namedobj_instance *ii;
29568394ec8SAlexander V. Chernikov 	struct ipfw_iface *iif, *tmp;
29668394ec8SAlexander V. Chernikov 
29768394ec8SAlexander V. Chernikov 	if (strlen(name) >= sizeof(iif->ifname))
29868394ec8SAlexander V. Chernikov 		return (EINVAL);
29968394ec8SAlexander V. Chernikov 
30068394ec8SAlexander V. Chernikov 	IPFW_UH_WLOCK(ch);
30168394ec8SAlexander V. Chernikov 
30268394ec8SAlexander V. Chernikov 	ii = CHAIN_TO_II(ch);
30368394ec8SAlexander V. Chernikov 	if (ii == NULL) {
30468394ec8SAlexander V. Chernikov 
30568394ec8SAlexander V. Chernikov 		/*
30668394ec8SAlexander V. Chernikov 		 * First request to subsystem.
30768394ec8SAlexander V. Chernikov 		 * Let's perform init.
30868394ec8SAlexander V. Chernikov 		 */
30968394ec8SAlexander V. Chernikov 		IPFW_UH_WUNLOCK(ch);
31068394ec8SAlexander V. Chernikov 		vnet_ipfw_iface_init(ch);
31168394ec8SAlexander V. Chernikov 		IPFW_UH_WLOCK(ch);
31268394ec8SAlexander V. Chernikov 		ii = CHAIN_TO_II(ch);
31368394ec8SAlexander V. Chernikov 	}
31468394ec8SAlexander V. Chernikov 
31568394ec8SAlexander V. Chernikov 	iif = (struct ipfw_iface *)ipfw_objhash_lookup_name(ii, 0, name);
31668394ec8SAlexander V. Chernikov 
31768394ec8SAlexander V. Chernikov 	if (iif != NULL) {
31868394ec8SAlexander V. Chernikov 		iif->no.refcnt++;
31968394ec8SAlexander V. Chernikov 		ic->iface = iif;
32068394ec8SAlexander V. Chernikov 		IPFW_UH_WUNLOCK(ch);
32168394ec8SAlexander V. Chernikov 		return (0);
32268394ec8SAlexander V. Chernikov 	}
32368394ec8SAlexander V. Chernikov 
32468394ec8SAlexander V. Chernikov 	IPFW_UH_WUNLOCK(ch);
32568394ec8SAlexander V. Chernikov 
32668394ec8SAlexander V. Chernikov 	/* Not found. Let's create one */
32768394ec8SAlexander V. Chernikov 	iif = malloc(sizeof(struct ipfw_iface), M_IPFW, M_WAITOK | M_ZERO);
32868394ec8SAlexander V. Chernikov 	TAILQ_INIT(&iif->consumers);
32968394ec8SAlexander V. Chernikov 	iif->no.name = iif->ifname;
33068394ec8SAlexander V. Chernikov 	strlcpy(iif->ifname, name, sizeof(iif->ifname));
33168394ec8SAlexander V. Chernikov 
33268394ec8SAlexander V. Chernikov 	/*
33368394ec8SAlexander V. Chernikov 	 * Ref & link to the list.
33468394ec8SAlexander V. Chernikov 	 *
33568394ec8SAlexander V. Chernikov 	 * We assume  ifnet_arrival_event / ifnet_departure_event
33668394ec8SAlexander V. Chernikov 	 * are not holding any locks.
33768394ec8SAlexander V. Chernikov 	 */
33868394ec8SAlexander V. Chernikov 	iif->no.refcnt = 1;
33968394ec8SAlexander V. Chernikov 	IPFW_UH_WLOCK(ch);
34068394ec8SAlexander V. Chernikov 
34168394ec8SAlexander V. Chernikov 	tmp = (struct ipfw_iface *)ipfw_objhash_lookup_name(ii, 0, name);
34268394ec8SAlexander V. Chernikov 	if (tmp != NULL) {
34368394ec8SAlexander V. Chernikov 		/* Interface has been created since unlock. Ref and return */
34468394ec8SAlexander V. Chernikov 		tmp->no.refcnt++;
34568394ec8SAlexander V. Chernikov 		ic->iface = tmp;
34668394ec8SAlexander V. Chernikov 		IPFW_UH_WUNLOCK(ch);
34768394ec8SAlexander V. Chernikov 		free(iif, M_IPFW);
34868394ec8SAlexander V. Chernikov 		return (0);
34968394ec8SAlexander V. Chernikov 	}
35068394ec8SAlexander V. Chernikov 
35168394ec8SAlexander V. Chernikov 	iif->ifindex = ipfw_kiflookup(name);
35268394ec8SAlexander V. Chernikov 	if (iif->ifindex != 0)
35368394ec8SAlexander V. Chernikov 		iif->resolved = 1;
35468394ec8SAlexander V. Chernikov 
35568394ec8SAlexander V. Chernikov 	ipfw_objhash_add(ii, &iif->no);
35668394ec8SAlexander V. Chernikov 	ic->iface = iif;
35768394ec8SAlexander V. Chernikov 
35868394ec8SAlexander V. Chernikov 	IPFW_UH_WUNLOCK(ch);
35968394ec8SAlexander V. Chernikov 
36068394ec8SAlexander V. Chernikov 	return (0);
36168394ec8SAlexander V. Chernikov }
36268394ec8SAlexander V. Chernikov 
36368394ec8SAlexander V. Chernikov /*
36468394ec8SAlexander V. Chernikov  * Adds @ic to the list of iif interface consumers.
36568394ec8SAlexander V. Chernikov  * Must be called with holding both UH+WLOCK.
36668394ec8SAlexander V. Chernikov  * Callback may be immediately called (if interface exists).
36768394ec8SAlexander V. Chernikov  */
36868394ec8SAlexander V. Chernikov void
36968394ec8SAlexander V. Chernikov ipfw_iface_add_notify(struct ip_fw_chain *ch, struct ipfw_ifc *ic)
37068394ec8SAlexander V. Chernikov {
37168394ec8SAlexander V. Chernikov 	struct ipfw_iface *iif;
37268394ec8SAlexander V. Chernikov 
37368394ec8SAlexander V. Chernikov 	IPFW_UH_WLOCK_ASSERT(ch);
37468394ec8SAlexander V. Chernikov 	IPFW_WLOCK_ASSERT(ch);
37568394ec8SAlexander V. Chernikov 
37668394ec8SAlexander V. Chernikov 	iif = ic->iface;
37768394ec8SAlexander V. Chernikov 
37868394ec8SAlexander V. Chernikov 	TAILQ_INSERT_TAIL(&iif->consumers, ic, next);
37968394ec8SAlexander V. Chernikov 	if (iif->resolved != 0)
38068394ec8SAlexander V. Chernikov 		ic->cb(ch, ic->cbdata, iif->ifindex);
38168394ec8SAlexander V. Chernikov }
38268394ec8SAlexander V. Chernikov 
38368394ec8SAlexander V. Chernikov /*
38468394ec8SAlexander V. Chernikov  * Unlinks interface tracker object @ic from interface.
38598eff10eSAlexander V. Chernikov  * Must be called while holding UH lock.
38668394ec8SAlexander V. Chernikov  */
38768394ec8SAlexander V. Chernikov void
38868394ec8SAlexander V. Chernikov ipfw_iface_del_notify(struct ip_fw_chain *ch, struct ipfw_ifc *ic)
38968394ec8SAlexander V. Chernikov {
39068394ec8SAlexander V. Chernikov 	struct ipfw_iface *iif;
39168394ec8SAlexander V. Chernikov 
39268394ec8SAlexander V. Chernikov 	IPFW_UH_WLOCK_ASSERT(ch);
39368394ec8SAlexander V. Chernikov 
39468394ec8SAlexander V. Chernikov 	iif = ic->iface;
39568394ec8SAlexander V. Chernikov 	if (ic->linked != 0)
39668394ec8SAlexander V. Chernikov 		TAILQ_REMOVE(&iif->consumers, ic, next);
39768394ec8SAlexander V. Chernikov }
39868394ec8SAlexander V. Chernikov 
39968394ec8SAlexander V. Chernikov /*
40068394ec8SAlexander V. Chernikov  * Unreference interface specified by @ic.
40168394ec8SAlexander V. Chernikov  * Must be called without holding any locks.
40268394ec8SAlexander V. Chernikov  */
40368394ec8SAlexander V. Chernikov void
40468394ec8SAlexander V. Chernikov ipfw_iface_unref(struct ip_fw_chain *ch, struct ipfw_ifc *ic)
40568394ec8SAlexander V. Chernikov {
40668394ec8SAlexander V. Chernikov 	struct ipfw_iface *iif;
40768394ec8SAlexander V. Chernikov 
40868394ec8SAlexander V. Chernikov 	iif = ic->iface;
40968394ec8SAlexander V. Chernikov 	ic->iface = NULL;
41068394ec8SAlexander V. Chernikov 
41168394ec8SAlexander V. Chernikov 	IPFW_UH_WLOCK(ch);
41268394ec8SAlexander V. Chernikov 	iif->no.refcnt--;
41368394ec8SAlexander V. Chernikov 	/* TODO: check for references & delete */
41468394ec8SAlexander V. Chernikov 	IPFW_UH_WUNLOCK(ch);
41568394ec8SAlexander V. Chernikov }
41668394ec8SAlexander V. Chernikov 
41768394ec8SAlexander V. Chernikov /*
41868394ec8SAlexander V. Chernikov  * Interface arrival handler.
41968394ec8SAlexander V. Chernikov  */
42068394ec8SAlexander V. Chernikov static void
42168394ec8SAlexander V. Chernikov handle_ifattach(struct ip_fw_chain *ch, struct ipfw_iface *iif,
42268394ec8SAlexander V. Chernikov     uint16_t ifindex)
42368394ec8SAlexander V. Chernikov {
42468394ec8SAlexander V. Chernikov 	struct ipfw_ifc *ic;
42568394ec8SAlexander V. Chernikov 
42668394ec8SAlexander V. Chernikov 	IPFW_UH_WLOCK_ASSERT(ch);
42768394ec8SAlexander V. Chernikov 
42868394ec8SAlexander V. Chernikov 	iif->gencnt++;
42968394ec8SAlexander V. Chernikov 	iif->resolved = 1;
43068394ec8SAlexander V. Chernikov 	iif->ifindex = ifindex;
43168394ec8SAlexander V. Chernikov 
43268394ec8SAlexander V. Chernikov 	IPFW_WLOCK(ch);
43368394ec8SAlexander V. Chernikov 	TAILQ_FOREACH(ic, &iif->consumers, next)
43468394ec8SAlexander V. Chernikov 		ic->cb(ch, ic->cbdata, iif->ifindex);
43568394ec8SAlexander V. Chernikov 	IPFW_WUNLOCK(ch);
43668394ec8SAlexander V. Chernikov }
43768394ec8SAlexander V. Chernikov 
43868394ec8SAlexander V. Chernikov /*
43968394ec8SAlexander V. Chernikov  * Interface departure handler.
44068394ec8SAlexander V. Chernikov  */
44168394ec8SAlexander V. Chernikov static void
44268394ec8SAlexander V. Chernikov handle_ifdetach(struct ip_fw_chain *ch, struct ipfw_iface *iif,
44368394ec8SAlexander V. Chernikov     uint16_t ifindex)
44468394ec8SAlexander V. Chernikov {
44568394ec8SAlexander V. Chernikov 	struct ipfw_ifc *ic;
44668394ec8SAlexander V. Chernikov 
44768394ec8SAlexander V. Chernikov 	IPFW_UH_WLOCK_ASSERT(ch);
44868394ec8SAlexander V. Chernikov 
44968394ec8SAlexander V. Chernikov 	IPFW_WLOCK(ch);
45068394ec8SAlexander V. Chernikov 	TAILQ_FOREACH(ic, &iif->consumers, next)
45168394ec8SAlexander V. Chernikov 		ic->cb(ch, ic->cbdata, 0);
45268394ec8SAlexander V. Chernikov 	IPFW_WUNLOCK(ch);
45368394ec8SAlexander V. Chernikov 
45468394ec8SAlexander V. Chernikov 	iif->gencnt++;
45568394ec8SAlexander V. Chernikov 	iif->resolved = 0;
45668394ec8SAlexander V. Chernikov 	iif->ifindex = 0;
45768394ec8SAlexander V. Chernikov }
45868394ec8SAlexander V. Chernikov 
45968394ec8SAlexander V. Chernikov struct dump_iface_args {
46068394ec8SAlexander V. Chernikov 	struct ip_fw_chain *ch;
46168394ec8SAlexander V. Chernikov 	struct sockopt_data *sd;
46268394ec8SAlexander V. Chernikov };
46368394ec8SAlexander V. Chernikov 
46468394ec8SAlexander V. Chernikov static void
46568394ec8SAlexander V. Chernikov export_iface_internal(struct namedobj_instance *ii, struct named_object *no,
46668394ec8SAlexander V. Chernikov     void *arg)
46768394ec8SAlexander V. Chernikov {
46868394ec8SAlexander V. Chernikov 	ipfw_iface_info *i;
46968394ec8SAlexander V. Chernikov 	struct dump_iface_args *da;
47068394ec8SAlexander V. Chernikov 	struct ipfw_iface *iif;
47168394ec8SAlexander V. Chernikov 
47268394ec8SAlexander V. Chernikov 	da = (struct dump_iface_args *)arg;
47368394ec8SAlexander V. Chernikov 
47468394ec8SAlexander V. Chernikov 	i = (ipfw_iface_info *)ipfw_get_sopt_space(da->sd, sizeof(*i));
47568394ec8SAlexander V. Chernikov 	KASSERT(i != 0, ("previously checked buffer is not enough"));
47668394ec8SAlexander V. Chernikov 
47768394ec8SAlexander V. Chernikov 	iif = (struct ipfw_iface *)no;
47868394ec8SAlexander V. Chernikov 
47968394ec8SAlexander V. Chernikov 	strlcpy(i->ifname, iif->ifname, sizeof(i->ifname));
48068394ec8SAlexander V. Chernikov 	if (iif->resolved)
48168394ec8SAlexander V. Chernikov 		i->flags |= IPFW_IFFLAG_RESOLVED;
48268394ec8SAlexander V. Chernikov 	i->ifindex = iif->ifindex;
48368394ec8SAlexander V. Chernikov 	i->refcnt = iif->no.refcnt;
48468394ec8SAlexander V. Chernikov 	i->gencnt = iif->gencnt;
48568394ec8SAlexander V. Chernikov }
48668394ec8SAlexander V. Chernikov 
48768394ec8SAlexander V. Chernikov /*
48868394ec8SAlexander V. Chernikov  * Lists all interface currently tracked by ipfw.
48968394ec8SAlexander V. Chernikov  * Data layout (v0)(current):
49068394ec8SAlexander V. Chernikov  * Request: [ ipfw_obj_lheader ], size = ipfw_obj_lheader.size
49168394ec8SAlexander V. Chernikov  * Reply: [ ipfw_obj_lheader ipfw_iface_info x N ]
49268394ec8SAlexander V. Chernikov  *
49368394ec8SAlexander V. Chernikov  * Returns 0 on success
49468394ec8SAlexander V. Chernikov  */
4956b988f3aSAlexander V. Chernikov static int
4966b988f3aSAlexander V. Chernikov list_ifaces(struct ip_fw_chain *ch, ip_fw3_opheader *op3,
497e822d936SAlexander V. Chernikov     struct sockopt_data *sd)
49868394ec8SAlexander V. Chernikov {
49935d5a820SAlexander V. Chernikov 	struct namedobj_instance *ii;
50068394ec8SAlexander V. Chernikov 	struct _ipfw_obj_lheader *olh;
50168394ec8SAlexander V. Chernikov 	struct dump_iface_args da;
50268394ec8SAlexander V. Chernikov 	uint32_t count, size;
50368394ec8SAlexander V. Chernikov 
50468394ec8SAlexander V. Chernikov 	olh = (struct _ipfw_obj_lheader *)ipfw_get_sopt_header(sd,sizeof(*olh));
50568394ec8SAlexander V. Chernikov 	if (olh == NULL)
50668394ec8SAlexander V. Chernikov 		return (EINVAL);
50768394ec8SAlexander V. Chernikov 	if (sd->valsize < olh->size)
50868394ec8SAlexander V. Chernikov 		return (EINVAL);
50968394ec8SAlexander V. Chernikov 
51068394ec8SAlexander V. Chernikov 	IPFW_UH_RLOCK(ch);
51135d5a820SAlexander V. Chernikov 	ii = CHAIN_TO_II(ch);
51235d5a820SAlexander V. Chernikov 	if (ii != NULL)
51335d5a820SAlexander V. Chernikov 		count = ipfw_objhash_count(ii);
51435d5a820SAlexander V. Chernikov 	else
51535d5a820SAlexander V. Chernikov 		count = 0;
51668394ec8SAlexander V. Chernikov 	size = count * sizeof(ipfw_iface_info) + sizeof(ipfw_obj_lheader);
51768394ec8SAlexander V. Chernikov 
51868394ec8SAlexander V. Chernikov 	/* Fill in header regadless of buffer size */
51968394ec8SAlexander V. Chernikov 	olh->count = count;
52068394ec8SAlexander V. Chernikov 	olh->objsize = sizeof(ipfw_iface_info);
52168394ec8SAlexander V. Chernikov 
52268394ec8SAlexander V. Chernikov 	if (size > olh->size) {
52368394ec8SAlexander V. Chernikov 		olh->size = size;
52468394ec8SAlexander V. Chernikov 		IPFW_UH_RUNLOCK(ch);
52568394ec8SAlexander V. Chernikov 		return (ENOMEM);
52668394ec8SAlexander V. Chernikov 	}
52768394ec8SAlexander V. Chernikov 	olh->size = size;
52868394ec8SAlexander V. Chernikov 
52968394ec8SAlexander V. Chernikov 	da.ch = ch;
53068394ec8SAlexander V. Chernikov 	da.sd = sd;
53168394ec8SAlexander V. Chernikov 
53235d5a820SAlexander V. Chernikov 	if (ii != NULL)
53335d5a820SAlexander V. Chernikov 		ipfw_objhash_foreach(ii, export_iface_internal, &da);
53468394ec8SAlexander V. Chernikov 	IPFW_UH_RUNLOCK(ch);
53568394ec8SAlexander V. Chernikov 
53668394ec8SAlexander V. Chernikov 	return (0);
53768394ec8SAlexander V. Chernikov }
53868394ec8SAlexander V. Chernikov 
539