2 * Copyright (c) 2014 Yandex LLC.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
13 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
14 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
17 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
19 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
20 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
21 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
22 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 #include <sys/cdefs.h>
27 __FBSDID("$FreeBSD$");
30 * Kernel interface tracking API.
37 #error IPFIREWALL requires INET.
39 #include "opt_inet6.h"
41 #include <sys/param.h>
42 #include <sys/systm.h>
43 #include <sys/malloc.h>
44 #include <sys/kernel.h>
46 #include <sys/rwlock.h>
47 #include <sys/rmlock.h>
48 #include <sys/socket.h>
49 #include <sys/queue.h>
50 #include <sys/eventhandler.h>
52 #include <net/if_var.h>
55 #include <netinet/in.h>
56 #include <netinet/ip_var.h> /* struct ipfw_rule_ref */
57 #include <netinet/ip_fw.h>
59 #include <netpfil/ipfw/ip_fw_private.h>
61 #define CHAIN_TO_II(ch) ((struct namedobj_instance *)ch->ifcfg)
63 #define DEFAULT_IFACES 128
65 static void handle_ifdetach(struct ip_fw_chain *ch, struct ipfw_iface *iif,
67 static void handle_ifattach(struct ip_fw_chain *ch, struct ipfw_iface *iif,
69 static int list_ifaces(struct ip_fw_chain *ch, ip_fw3_opheader *op3,
70 struct sockopt_data *sd);
72 static struct ipfw_sopt_handler scodes[] = {
73 { IP_FW_XIFLIST, 0, HDIR_GET, list_ifaces },
77 * FreeBSD Kernel interface.
79 static void ipfw_kifhandler(void *arg, struct ifnet *ifp);
80 static int ipfw_kiflookup(char *name);
81 static void iface_khandler_register(void);
82 static void iface_khandler_deregister(void);
84 static eventhandler_tag ipfw_ifdetach_event, ipfw_ifattach_event;
85 static int num_vnets = 0;
86 static struct mtx vnet_mtx;
89 * Checks if kernel interface is contained in our tracked
90 * interface list and calls attach/detach handler.
93 ipfw_kifhandler(void *arg, struct ifnet *ifp)
95 struct ip_fw_chain *ch;
96 struct ipfw_iface *iif;
97 struct namedobj_instance *ii;
100 if (V_ipfw_vnet_ready == 0)
103 ch = &V_layer3_chain;
104 htype = (uintptr_t)arg;
107 ii = CHAIN_TO_II(ch);
112 iif = (struct ipfw_iface*)ipfw_objhash_lookup_name(ii, 0,
116 handle_ifattach(ch, iif, ifp->if_index);
118 handle_ifdetach(ch, iif, ifp->if_index);
124 * Reference current VNET as iface tracking API user.
125 * Registers interface tracking handlers for first VNET.
128 iface_khandler_register()
138 mtx_unlock(&vnet_mtx);
143 printf("IPFW: starting up interface tracker\n");
145 ipfw_ifdetach_event = EVENTHANDLER_REGISTER(
146 ifnet_departure_event, ipfw_kifhandler, NULL,
147 EVENTHANDLER_PRI_ANY);
148 ipfw_ifattach_event = EVENTHANDLER_REGISTER(
149 ifnet_arrival_event, ipfw_kifhandler, (void*)((uintptr_t)1),
150 EVENTHANDLER_PRI_ANY);
155 * Detach interface event handlers on last VNET instance
159 iface_khandler_deregister()
168 mtx_unlock(&vnet_mtx);
173 EVENTHANDLER_DEREGISTER(ifnet_arrival_event,
174 ipfw_ifattach_event);
175 EVENTHANDLER_DEREGISTER(ifnet_departure_event,
176 ipfw_ifdetach_event);
180 * Retrieves ifindex for given @name.
182 * Returns ifindex or 0.
185 ipfw_kiflookup(char *name)
192 if ((ifp = ifunit_ref(name)) != NULL) {
193 ifindex = ifp->if_index;
201 * Global ipfw startup hook.
202 * Since we perform lazy initialization, do nothing except
209 mtx_init(&vnet_mtx, "IPFW ifhandler mtx", NULL, MTX_DEF);
210 IPFW_ADD_SOPT_HANDLER(1, scodes);
215 * Global ipfw destroy hook.
216 * Unregister khandlers iff init has been done.
222 IPFW_DEL_SOPT_HANDLER(1, scodes);
223 mtx_destroy(&vnet_mtx);
227 * Perform actual init on internal request.
228 * Inits both namehash and global khandler.
231 vnet_ipfw_iface_init(struct ip_fw_chain *ch)
233 struct namedobj_instance *ii;
235 ii = ipfw_objhash_create(DEFAULT_IFACES);
237 if (ch->ifcfg == NULL) {
244 /* Already initialized. Free namehash. */
245 ipfw_objhash_destroy(ii);
247 /* We're the first ones. Init kernel hooks. */
248 iface_khandler_register();
253 destroy_iface(struct namedobj_instance *ii, struct named_object *no,
257 /* Assume all consumers have been already detached */
263 * Per-VNET ipfw detach hook.
267 vnet_ipfw_iface_destroy(struct ip_fw_chain *ch)
269 struct namedobj_instance *ii;
272 ii = CHAIN_TO_II(ch);
277 ipfw_objhash_foreach(ii, destroy_iface, ch);
278 ipfw_objhash_destroy(ii);
279 iface_khandler_deregister();
284 * Notify the subsystem that we are interested in tracking
285 * interface @name. This function has to be called without
286 * holding any locks to permit allocating the necessary states
287 * for proper interface tracking.
289 * Returns 0 on success.
292 ipfw_iface_ref(struct ip_fw_chain *ch, char *name,
295 struct namedobj_instance *ii;
296 struct ipfw_iface *iif, *tmp;
298 if (strlen(name) >= sizeof(iif->ifname))
303 ii = CHAIN_TO_II(ch);
307 * First request to subsystem.
308 * Let's perform init.
311 vnet_ipfw_iface_init(ch);
313 ii = CHAIN_TO_II(ch);
316 iif = (struct ipfw_iface *)ipfw_objhash_lookup_name(ii, 0, name);
327 /* Not found. Let's create one */
328 iif = malloc(sizeof(struct ipfw_iface), M_IPFW, M_WAITOK | M_ZERO);
329 TAILQ_INIT(&iif->consumers);
330 iif->no.name = iif->ifname;
331 strlcpy(iif->ifname, name, sizeof(iif->ifname));
334 * Ref & link to the list.
336 * We assume ifnet_arrival_event / ifnet_departure_event
337 * are not holding any locks.
342 tmp = (struct ipfw_iface *)ipfw_objhash_lookup_name(ii, 0, name);
344 /* Interface has been created since unlock. Ref and return */
352 iif->ifindex = ipfw_kiflookup(name);
353 if (iif->ifindex != 0)
356 ipfw_objhash_add(ii, &iif->no);
365 * Adds @ic to the list of iif interface consumers.
366 * Must be called with holding both UH+WLOCK.
367 * Callback may be immediately called (if interface exists).
370 ipfw_iface_add_notify(struct ip_fw_chain *ch, struct ipfw_ifc *ic)
372 struct ipfw_iface *iif;
374 IPFW_UH_WLOCK_ASSERT(ch);
375 IPFW_WLOCK_ASSERT(ch);
379 TAILQ_INSERT_TAIL(&iif->consumers, ic, next);
380 if (iif->resolved != 0)
381 ic->cb(ch, ic->cbdata, iif->ifindex);
385 * Unlinks interface tracker object @ic from interface.
386 * Must be called while holding UH lock.
389 ipfw_iface_del_notify(struct ip_fw_chain *ch, struct ipfw_ifc *ic)
391 struct ipfw_iface *iif;
393 IPFW_UH_WLOCK_ASSERT(ch);
396 TAILQ_REMOVE(&iif->consumers, ic, next);
400 * Unreference interface specified by @ic.
401 * Must be called while holding UH lock.
404 ipfw_iface_unref(struct ip_fw_chain *ch, struct ipfw_ifc *ic)
406 struct ipfw_iface *iif;
408 IPFW_UH_WLOCK_ASSERT(ch);
414 /* TODO: check for references & delete */
418 * Interface arrival handler.
421 handle_ifattach(struct ip_fw_chain *ch, struct ipfw_iface *iif,
426 IPFW_UH_WLOCK_ASSERT(ch);
430 iif->ifindex = ifindex;
433 TAILQ_FOREACH(ic, &iif->consumers, next)
434 ic->cb(ch, ic->cbdata, iif->ifindex);
439 * Interface departure handler.
442 handle_ifdetach(struct ip_fw_chain *ch, struct ipfw_iface *iif,
447 IPFW_UH_WLOCK_ASSERT(ch);
450 TAILQ_FOREACH(ic, &iif->consumers, next)
451 ic->cb(ch, ic->cbdata, 0);
459 struct dump_iface_args {
460 struct ip_fw_chain *ch;
461 struct sockopt_data *sd;
465 export_iface_internal(struct namedobj_instance *ii, struct named_object *no,
469 struct dump_iface_args *da;
470 struct ipfw_iface *iif;
472 da = (struct dump_iface_args *)arg;
474 i = (ipfw_iface_info *)ipfw_get_sopt_space(da->sd, sizeof(*i));
475 KASSERT(i != NULL, ("previously checked buffer is not enough"));
477 iif = (struct ipfw_iface *)no;
479 strlcpy(i->ifname, iif->ifname, sizeof(i->ifname));
481 i->flags |= IPFW_IFFLAG_RESOLVED;
482 i->ifindex = iif->ifindex;
483 i->refcnt = iif->no.refcnt;
484 i->gencnt = iif->gencnt;
489 * Lists all interface currently tracked by ipfw.
490 * Data layout (v0)(current):
491 * Request: [ ipfw_obj_lheader ], size = ipfw_obj_lheader.size
492 * Reply: [ ipfw_obj_lheader ipfw_iface_info x N ]
494 * Returns 0 on success
497 list_ifaces(struct ip_fw_chain *ch, ip_fw3_opheader *op3,
498 struct sockopt_data *sd)
500 struct namedobj_instance *ii;
501 struct _ipfw_obj_lheader *olh;
502 struct dump_iface_args da;
503 uint32_t count, size;
505 olh = (struct _ipfw_obj_lheader *)ipfw_get_sopt_header(sd,sizeof(*olh));
508 if (sd->valsize < olh->size)
512 ii = CHAIN_TO_II(ch);
514 count = ipfw_objhash_count(ii);
517 size = count * sizeof(ipfw_iface_info) + sizeof(ipfw_obj_lheader);
519 /* Fill in header regadless of buffer size */
521 olh->objsize = sizeof(ipfw_iface_info);
523 if (size > olh->size) {
534 ipfw_objhash_foreach(ii, export_iface_internal, &da);