ip_fw_iface.c revision 317042
1/*-
2 * Copyright (c) 2014 Yandex LLC.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
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.
12 *
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
23 * SUCH DAMAGE.
24 */
25
26#include <sys/cdefs.h>
27__FBSDID("$FreeBSD: stable/11/sys/netpfil/ipfw/ip_fw_iface.c 317042 2017-04-17 09:34:09Z ae $");
28
29/*
30 * Kernel interface tracking API.
31 *
32 */
33
34#include "opt_ipfw.h"
35#include "opt_inet.h"
36#ifndef INET
37#error IPFIREWALL requires INET.
38#endif /* INET */
39#include "opt_inet6.h"
40
41#include <sys/param.h>
42#include <sys/systm.h>
43#include <sys/malloc.h>
44#include <sys/kernel.h>
45#include <sys/lock.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>
51#include <net/if.h>
52#include <net/if_var.h>
53#include <net/pfil.h>
54#include <net/vnet.h>
55
56#include <netinet/in.h>
57#include <netinet/ip_var.h>	/* struct ipfw_rule_ref */
58#include <netinet/ip_fw.h>
59
60#include <netpfil/ipfw/ip_fw_private.h>
61
62#define	CHAIN_TO_II(ch)		((struct namedobj_instance *)ch->ifcfg)
63
64#define	DEFAULT_IFACES	128
65
66static void handle_ifdetach(struct ip_fw_chain *ch, struct ipfw_iface *iif,
67    uint16_t ifindex);
68static void handle_ifattach(struct ip_fw_chain *ch, struct ipfw_iface *iif,
69    uint16_t ifindex);
70static int list_ifaces(struct ip_fw_chain *ch, ip_fw3_opheader *op3,
71    struct sockopt_data *sd);
72
73static struct ipfw_sopt_handler	scodes[] = {
74	{ IP_FW_XIFLIST,	0,	HDIR_GET,	list_ifaces },
75};
76
77/*
78 * FreeBSD Kernel interface.
79 */
80static void ipfw_kifhandler(void *arg, struct ifnet *ifp);
81static int ipfw_kiflookup(char *name);
82static void iface_khandler_register(void);
83static void iface_khandler_deregister(void);
84
85static eventhandler_tag ipfw_ifdetach_event, ipfw_ifattach_event;
86static int num_vnets = 0;
87static struct mtx vnet_mtx;
88
89/*
90 * Checks if kernel interface is contained in our tracked
91 * interface list and calls attach/detach handler.
92 */
93static void
94ipfw_kifhandler(void *arg, struct ifnet *ifp)
95{
96	struct ip_fw_chain *ch;
97	struct ipfw_iface *iif;
98	struct namedobj_instance *ii;
99	uintptr_t htype;
100
101	if (V_ipfw_vnet_ready == 0)
102		return;
103
104	ch = &V_layer3_chain;
105	htype = (uintptr_t)arg;
106
107	IPFW_UH_WLOCK(ch);
108	ii = CHAIN_TO_II(ch);
109	if (ii == NULL) {
110		IPFW_UH_WUNLOCK(ch);
111		return;
112	}
113	iif = (struct ipfw_iface*)ipfw_objhash_lookup_name(ii, 0,
114	    if_name(ifp));
115	if (iif != NULL) {
116		if (htype == 1)
117			handle_ifattach(ch, iif, ifp->if_index);
118		else
119			handle_ifdetach(ch, iif, ifp->if_index);
120	}
121	IPFW_UH_WUNLOCK(ch);
122}
123
124/*
125 * Reference current VNET as iface tracking API user.
126 * Registers interface tracking handlers for first VNET.
127 */
128static void
129iface_khandler_register()
130{
131	int create;
132
133	create = 0;
134
135	mtx_lock(&vnet_mtx);
136	if (num_vnets == 0)
137		create = 1;
138	num_vnets++;
139	mtx_unlock(&vnet_mtx);
140
141	if (create == 0)
142		return;
143
144	printf("IPFW: starting up interface tracker\n");
145
146	ipfw_ifdetach_event = EVENTHANDLER_REGISTER(
147	    ifnet_departure_event, ipfw_kifhandler, NULL,
148	    EVENTHANDLER_PRI_ANY);
149	ipfw_ifattach_event = EVENTHANDLER_REGISTER(
150	    ifnet_arrival_event, ipfw_kifhandler, (void*)((uintptr_t)1),
151	    EVENTHANDLER_PRI_ANY);
152}
153
154/*
155 *
156 * Detach interface event handlers on last VNET instance
157 * detach.
158 */
159static void
160iface_khandler_deregister()
161{
162	int destroy;
163
164	destroy = 0;
165	mtx_lock(&vnet_mtx);
166	if (num_vnets == 1)
167		destroy = 1;
168	num_vnets--;
169	mtx_unlock(&vnet_mtx);
170
171	if (destroy == 0)
172		return;
173
174	EVENTHANDLER_DEREGISTER(ifnet_arrival_event,
175	    ipfw_ifattach_event);
176	EVENTHANDLER_DEREGISTER(ifnet_departure_event,
177	    ipfw_ifdetach_event);
178}
179
180/*
181 * Retrieves ifindex for given @name.
182 *
183 * Returns ifindex or 0.
184 */
185static int
186ipfw_kiflookup(char *name)
187{
188	struct ifnet *ifp;
189	int ifindex;
190
191	ifindex = 0;
192
193	if ((ifp = ifunit_ref(name)) != NULL) {
194		ifindex = ifp->if_index;
195		if_rele(ifp);
196	}
197
198	return (ifindex);
199}
200
201/*
202 * Global ipfw startup hook.
203 * Since we perform lazy initialization, do nothing except
204 * mutex init.
205 */
206int
207ipfw_iface_init()
208{
209
210	mtx_init(&vnet_mtx, "IPFW ifhandler mtx", NULL, MTX_DEF);
211	IPFW_ADD_SOPT_HANDLER(1, scodes);
212	return (0);
213}
214
215/*
216 * Global ipfw destroy hook.
217 * Unregister khandlers iff init has been done.
218 */
219void
220ipfw_iface_destroy()
221{
222
223	IPFW_DEL_SOPT_HANDLER(1, scodes);
224	mtx_destroy(&vnet_mtx);
225}
226
227/*
228 * Perform actual init on internal request.
229 * Inits both namehash and global khandler.
230 */
231static void
232vnet_ipfw_iface_init(struct ip_fw_chain *ch)
233{
234	struct namedobj_instance *ii;
235
236	ii = ipfw_objhash_create(DEFAULT_IFACES);
237	IPFW_UH_WLOCK(ch);
238	if (ch->ifcfg == NULL) {
239		ch->ifcfg = ii;
240		ii = NULL;
241	}
242	IPFW_UH_WUNLOCK(ch);
243
244	if (ii != NULL) {
245		/* Already initialized. Free namehash. */
246		ipfw_objhash_destroy(ii);
247	} else {
248		/* We're the first ones. Init kernel hooks. */
249		iface_khandler_register();
250	}
251}
252
253static int
254destroy_iface(struct namedobj_instance *ii, struct named_object *no,
255    void *arg)
256{
257
258	/* Assume all consumers have been already detached */
259	free(no, M_IPFW);
260	return (0);
261}
262
263/*
264 * Per-VNET ipfw detach hook.
265 *
266 */
267void
268vnet_ipfw_iface_destroy(struct ip_fw_chain *ch)
269{
270	struct namedobj_instance *ii;
271
272	IPFW_UH_WLOCK(ch);
273	ii = CHAIN_TO_II(ch);
274	ch->ifcfg = NULL;
275	IPFW_UH_WUNLOCK(ch);
276
277	if (ii != NULL) {
278		ipfw_objhash_foreach(ii, destroy_iface, ch);
279		ipfw_objhash_destroy(ii);
280		iface_khandler_deregister();
281	}
282}
283
284/*
285 * Notify the subsystem that we are interested in tracking
286 * interface @name. This function has to be called without
287 * holding any locks to permit allocating the necessary states
288 * for proper interface tracking.
289 *
290 * Returns 0 on success.
291 */
292int
293ipfw_iface_ref(struct ip_fw_chain *ch, char *name,
294    struct ipfw_ifc *ic)
295{
296	struct namedobj_instance *ii;
297	struct ipfw_iface *iif, *tmp;
298
299	if (strlen(name) >= sizeof(iif->ifname))
300		return (EINVAL);
301
302	IPFW_UH_WLOCK(ch);
303
304	ii = CHAIN_TO_II(ch);
305	if (ii == NULL) {
306
307		/*
308		 * First request to subsystem.
309		 * Let's perform init.
310		 */
311		IPFW_UH_WUNLOCK(ch);
312		vnet_ipfw_iface_init(ch);
313		IPFW_UH_WLOCK(ch);
314		ii = CHAIN_TO_II(ch);
315	}
316
317	iif = (struct ipfw_iface *)ipfw_objhash_lookup_name(ii, 0, name);
318
319	if (iif != NULL) {
320		iif->no.refcnt++;
321		ic->iface = iif;
322		IPFW_UH_WUNLOCK(ch);
323		return (0);
324	}
325
326	IPFW_UH_WUNLOCK(ch);
327
328	/* Not found. Let's create one */
329	iif = malloc(sizeof(struct ipfw_iface), M_IPFW, M_WAITOK | M_ZERO);
330	TAILQ_INIT(&iif->consumers);
331	iif->no.name = iif->ifname;
332	strlcpy(iif->ifname, name, sizeof(iif->ifname));
333
334	/*
335	 * Ref & link to the list.
336	 *
337	 * We assume  ifnet_arrival_event / ifnet_departure_event
338	 * are not holding any locks.
339	 */
340	iif->no.refcnt = 1;
341	IPFW_UH_WLOCK(ch);
342
343	tmp = (struct ipfw_iface *)ipfw_objhash_lookup_name(ii, 0, name);
344	if (tmp != NULL) {
345		/* Interface has been created since unlock. Ref and return */
346		tmp->no.refcnt++;
347		ic->iface = tmp;
348		IPFW_UH_WUNLOCK(ch);
349		free(iif, M_IPFW);
350		return (0);
351	}
352
353	iif->ifindex = ipfw_kiflookup(name);
354	if (iif->ifindex != 0)
355		iif->resolved = 1;
356
357	ipfw_objhash_add(ii, &iif->no);
358	ic->iface = iif;
359
360	IPFW_UH_WUNLOCK(ch);
361
362	return (0);
363}
364
365/*
366 * Adds @ic to the list of iif interface consumers.
367 * Must be called with holding both UH+WLOCK.
368 * Callback may be immediately called (if interface exists).
369 */
370void
371ipfw_iface_add_notify(struct ip_fw_chain *ch, struct ipfw_ifc *ic)
372{
373	struct ipfw_iface *iif;
374
375	IPFW_UH_WLOCK_ASSERT(ch);
376	IPFW_WLOCK_ASSERT(ch);
377
378	iif = ic->iface;
379
380	TAILQ_INSERT_TAIL(&iif->consumers, ic, next);
381	if (iif->resolved != 0)
382		ic->cb(ch, ic->cbdata, iif->ifindex);
383}
384
385/*
386 * Unlinks interface tracker object @ic from interface.
387 * Must be called while holding UH lock.
388 */
389void
390ipfw_iface_del_notify(struct ip_fw_chain *ch, struct ipfw_ifc *ic)
391{
392	struct ipfw_iface *iif;
393
394	IPFW_UH_WLOCK_ASSERT(ch);
395
396	iif = ic->iface;
397	TAILQ_REMOVE(&iif->consumers, ic, next);
398}
399
400/*
401 * Unreference interface specified by @ic.
402 * Must be called while holding UH lock.
403 */
404void
405ipfw_iface_unref(struct ip_fw_chain *ch, struct ipfw_ifc *ic)
406{
407	struct ipfw_iface *iif;
408
409	IPFW_UH_WLOCK_ASSERT(ch);
410
411	iif = ic->iface;
412	ic->iface = NULL;
413
414	iif->no.refcnt--;
415	/* TODO: check for references & delete */
416}
417
418/*
419 * Interface arrival handler.
420 */
421static void
422handle_ifattach(struct ip_fw_chain *ch, struct ipfw_iface *iif,
423    uint16_t ifindex)
424{
425	struct ipfw_ifc *ic;
426
427	IPFW_UH_WLOCK_ASSERT(ch);
428
429	iif->gencnt++;
430	iif->resolved = 1;
431	iif->ifindex = ifindex;
432
433	IPFW_WLOCK(ch);
434	TAILQ_FOREACH(ic, &iif->consumers, next)
435		ic->cb(ch, ic->cbdata, iif->ifindex);
436	IPFW_WUNLOCK(ch);
437}
438
439/*
440 * Interface departure handler.
441 */
442static void
443handle_ifdetach(struct ip_fw_chain *ch, struct ipfw_iface *iif,
444    uint16_t ifindex)
445{
446	struct ipfw_ifc *ic;
447
448	IPFW_UH_WLOCK_ASSERT(ch);
449
450	IPFW_WLOCK(ch);
451	TAILQ_FOREACH(ic, &iif->consumers, next)
452		ic->cb(ch, ic->cbdata, 0);
453	IPFW_WUNLOCK(ch);
454
455	iif->gencnt++;
456	iif->resolved = 0;
457	iif->ifindex = 0;
458}
459
460struct dump_iface_args {
461	struct ip_fw_chain *ch;
462	struct sockopt_data *sd;
463};
464
465static int
466export_iface_internal(struct namedobj_instance *ii, struct named_object *no,
467    void *arg)
468{
469	ipfw_iface_info *i;
470	struct dump_iface_args *da;
471	struct ipfw_iface *iif;
472
473	da = (struct dump_iface_args *)arg;
474
475	i = (ipfw_iface_info *)ipfw_get_sopt_space(da->sd, sizeof(*i));
476	KASSERT(i != NULL, ("previously checked buffer is not enough"));
477
478	iif = (struct ipfw_iface *)no;
479
480	strlcpy(i->ifname, iif->ifname, sizeof(i->ifname));
481	if (iif->resolved)
482		i->flags |= IPFW_IFFLAG_RESOLVED;
483	i->ifindex = iif->ifindex;
484	i->refcnt = iif->no.refcnt;
485	i->gencnt = iif->gencnt;
486	return (0);
487}
488
489/*
490 * Lists all interface currently tracked by ipfw.
491 * Data layout (v0)(current):
492 * Request: [ ipfw_obj_lheader ], size = ipfw_obj_lheader.size
493 * Reply: [ ipfw_obj_lheader ipfw_iface_info x N ]
494 *
495 * Returns 0 on success
496 */
497static int
498list_ifaces(struct ip_fw_chain *ch, ip_fw3_opheader *op3,
499    struct sockopt_data *sd)
500{
501	struct namedobj_instance *ii;
502	struct _ipfw_obj_lheader *olh;
503	struct dump_iface_args da;
504	uint32_t count, size;
505
506	olh = (struct _ipfw_obj_lheader *)ipfw_get_sopt_header(sd,sizeof(*olh));
507	if (olh == NULL)
508		return (EINVAL);
509	if (sd->valsize < olh->size)
510		return (EINVAL);
511
512	IPFW_UH_RLOCK(ch);
513	ii = CHAIN_TO_II(ch);
514	if (ii != NULL)
515		count = ipfw_objhash_count(ii);
516	else
517		count = 0;
518	size = count * sizeof(ipfw_iface_info) + sizeof(ipfw_obj_lheader);
519
520	/* Fill in header regadless of buffer size */
521	olh->count = count;
522	olh->objsize = sizeof(ipfw_iface_info);
523
524	if (size > olh->size) {
525		olh->size = size;
526		IPFW_UH_RUNLOCK(ch);
527		return (ENOMEM);
528	}
529	olh->size = size;
530
531	da.ch = ch;
532	da.sd = sd;
533
534	if (ii != NULL)
535		ipfw_objhash_foreach(ii, export_iface_internal, &da);
536	IPFW_UH_RUNLOCK(ch);
537
538	return (0);
539}
540
541