]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/netpfil/ipfw/ip_fw_iface.c
Merge ^/vendor/lld/dist up to its last change, and resolve conflicts.
[FreeBSD/FreeBSD.git] / sys / netpfil / ipfw / ip_fw_iface.c
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$");
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/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
65 static void handle_ifdetach(struct ip_fw_chain *ch, struct ipfw_iface *iif,
66     uint16_t ifindex);
67 static void handle_ifattach(struct ip_fw_chain *ch, struct ipfw_iface *iif,
68     uint16_t ifindex);
69 static int list_ifaces(struct ip_fw_chain *ch, ip_fw3_opheader *op3,
70     struct sockopt_data *sd);
71
72 static struct ipfw_sopt_handler scodes[] = {
73         { IP_FW_XIFLIST,        0,      HDIR_GET,       list_ifaces },
74 };
75
76 /*
77  * FreeBSD Kernel interface.
78  */
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);
83
84 static eventhandler_tag ipfw_ifdetach_event, ipfw_ifattach_event;
85 static int num_vnets = 0;
86 static 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  */
92 static void
93 ipfw_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  */
127 static void
128 iface_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  */
158 static void
159 iface_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  */
184 static int
185 ipfw_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  */
205 int
206 ipfw_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  */
218 void
219 ipfw_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  */
230 static void
231 vnet_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
252 static int
253 destroy_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  */
266 void
267 vnet_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  */
291 int
292 ipfw_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  */
369 void
370 ipfw_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  */
388 void
389 ipfw_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  */
403 void
404 ipfw_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  */
420 static void
421 handle_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  */
441 static void
442 handle_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
459 struct dump_iface_args {
460         struct ip_fw_chain *ch;
461         struct sockopt_data *sd;
462 };
463
464 static int
465 export_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  */
496 static int
497 list_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