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