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