Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/sys/netpfil/ipfw/ip_fw_iface.c
39482 views
1
/*-
2
* SPDX-License-Identifier: BSD-2-Clause
3
*
4
* Copyright (c) 2014-2025 Yandex LLC.
5
*
6
* Redistribution and use in source and binary forms, with or without
7
* modification, are permitted provided that the following conditions
8
* are met:
9
* 1. Redistributions of source code must retain the above copyright
10
* notice, this list of conditions and the following disclaimer.
11
* 2. Redistributions in binary form must reproduce the above copyright
12
* notice, this list of conditions and the following disclaimer in the
13
* documentation and/or other materials provided with the distribution.
14
*
15
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25
* SUCH DAMAGE.
26
*/
27
28
#include <sys/cdefs.h>
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/if_private.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
66
static void handle_ifdetach(struct ip_fw_chain *ch, struct ipfw_iface *iif,
67
uint16_t ifindex);
68
static void handle_ifattach(struct ip_fw_chain *ch, struct ipfw_iface *iif,
69
uint16_t ifindex);
70
static int list_ifaces(struct ip_fw_chain *ch, ip_fw3_opheader *op3,
71
struct sockopt_data *sd);
72
73
static struct ipfw_sopt_handler scodes[] = {
74
{ IP_FW_XIFLIST, IP_FW3_OPVER, HDIR_GET, list_ifaces },
75
};
76
77
/*
78
* FreeBSD Kernel interface.
79
*/
80
static void ipfw_kifhandler(void *arg, struct ifnet *ifp);
81
static int ipfw_kiflookup(char *name);
82
static void iface_khandler_register(void);
83
static void iface_khandler_deregister(void);
84
85
static eventhandler_tag ipfw_ifdetach_event, ipfw_ifattach_event;
86
static int num_vnets = 0;
87
static 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
*/
93
static void
94
ipfw_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
*/
128
static void
129
iface_khandler_register(void)
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
*/
159
static void
160
iface_khandler_deregister(void)
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
*/
185
static int
186
ipfw_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
*/
206
int
207
ipfw_iface_init(void)
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
*/
219
void
220
ipfw_iface_destroy(void)
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
*/
231
static void
232
vnet_ipfw_iface_init(struct ip_fw_chain *ch)
233
{
234
struct namedobj_instance *ii;
235
236
ii = ipfw_objhash_create(DEFAULT_IFACES, DEFAULT_OBJHASH_SIZE);
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
253
static int
254
destroy_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
*/
267
void
268
vnet_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
*/
292
int
293
ipfw_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
* 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 (v1)(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
540