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