Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/sys/netpfil/ipfw/ip_fw_sockopt.c
108654 views
1
/*-
2
* SPDX-License-Identifier: BSD-2-Clause
3
*
4
* Copyright (c) 2002-2009 Luigi Rizzo, Universita` di Pisa
5
* Copyright (c) 2014-2025 Yandex LLC
6
* Copyright (c) 2014 Alexander V. Chernikov
7
*
8
* Supported by: Valeria Paoli
9
*
10
* Redistribution and use in source and binary forms, with or without
11
* modification, are permitted provided that the following conditions
12
* are met:
13
* 1. Redistributions of source code must retain the above copyright
14
* notice, this list of conditions and the following disclaimer.
15
* 2. Redistributions in binary form must reproduce the above copyright
16
* notice, this list of conditions and the following disclaimer in the
17
* documentation and/or other materials provided with the distribution.
18
*
19
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
20
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
23
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29
* SUCH DAMAGE.
30
*/
31
32
#include <sys/cdefs.h>
33
/*
34
* Control socket and rule management routines for ipfw.
35
* Control is currently implemented via IP_FW3 setsockopt() code.
36
*/
37
38
#include "opt_ipfw.h"
39
#include "opt_inet.h"
40
#ifndef INET
41
#error IPFIREWALL requires INET.
42
#endif /* INET */
43
#include "opt_inet6.h"
44
45
#include <sys/param.h>
46
#include <sys/systm.h>
47
#include <sys/malloc.h>
48
#include <sys/mbuf.h> /* struct m_tag used by nested headers */
49
#include <sys/kernel.h>
50
#include <sys/lock.h>
51
#include <sys/priv.h>
52
#include <sys/proc.h>
53
#include <sys/rwlock.h>
54
#include <sys/rmlock.h>
55
#include <sys/socket.h>
56
#include <sys/socketvar.h>
57
#include <sys/sysctl.h>
58
#include <sys/syslog.h>
59
#include <sys/fnv_hash.h>
60
#include <net/if.h>
61
#include <net/route.h>
62
#include <net/vnet.h>
63
#include <vm/vm.h>
64
#include <vm/vm_extern.h>
65
66
#include <netinet/in.h>
67
#include <netinet/ip_var.h> /* hooks */
68
#include <netinet/ip_fw.h>
69
70
#include <netpfil/ipfw/ip_fw_private.h>
71
#include <netpfil/ipfw/ip_fw_table.h>
72
73
#ifdef MAC
74
#include <security/mac/mac_framework.h>
75
#endif
76
77
static enum ipfw_opcheck_result
78
check_opcode_compat_nop(ipfw_insn **pcmd, int *plen,
79
struct rule_check_info *ci)
80
{
81
/* Compatibility code is not registered */
82
return (FAILED);
83
}
84
85
static ipfw_check_opcode_t check_opcode_f = check_opcode_compat_nop;
86
87
static int check_ipfw_rule_body(ipfw_insn *cmd, int cmd_len,
88
struct rule_check_info *ci);
89
static int rewrite_rule_uidx(struct ip_fw_chain *chain,
90
struct rule_check_info *ci);
91
92
struct namedobj_instance {
93
struct namedobjects_head *names;
94
struct namedobjects_head *values;
95
uint32_t nn_size; /* names hash size */
96
uint32_t nv_size; /* number hash size */
97
u_long *idx_mask; /* used items bitmask */
98
uint32_t max_blocks; /* number of "long" blocks in bitmask */
99
uint32_t count; /* number of items */
100
uint16_t free_off[IPFW_MAX_SETS]; /* first possible free offset */
101
objhash_hash_f *hash_f;
102
objhash_cmp_f *cmp_f;
103
};
104
#define BLOCK_ITEMS (8 * sizeof(u_long)) /* Number of items for ffsl() */
105
106
static uint32_t objhash_hash_name(struct namedobj_instance *ni,
107
const void *key, uint32_t kopt);
108
static uint32_t objhash_hash_idx(struct namedobj_instance *ni, uint32_t val);
109
static int objhash_cmp_name(struct named_object *no, const void *name,
110
uint32_t set);
111
112
MALLOC_DEFINE(M_IPFW, "IpFw/IpAcct", "IpFw/IpAcct chain's");
113
114
/* ctl3 handler data */
115
static struct mtx ctl3_lock;
116
#define CTL3_LOCK_INIT() mtx_init(&ctl3_lock, "ctl3_lock", NULL, MTX_DEF)
117
#define CTL3_LOCK_DESTROY() mtx_destroy(&ctl3_lock)
118
#define CTL3_LOCK() mtx_lock(&ctl3_lock)
119
#define CTL3_UNLOCK() mtx_unlock(&ctl3_lock)
120
121
static struct ipfw_sopt_handler *ctl3_handlers;
122
static size_t ctl3_hsize;
123
static uint64_t ctl3_refct, ctl3_gencnt;
124
#define CTL3_SMALLBUF 4096 /* small page-size write buffer */
125
#define CTL3_LARGEBUF (16 * 1024 * 1024) /* handle large rulesets */
126
127
static int ipfw_flush_sopt_data(struct sockopt_data *sd);
128
129
static sopt_handler_f dump_config, add_rules, del_rules, clear_rules,
130
move_rules, manage_sets, dump_soptcodes, dump_srvobjects,
131
manage_skiptocache;
132
133
static struct ipfw_sopt_handler scodes[] = {
134
{ IP_FW_XGET, IP_FW3_OPVER, HDIR_GET, dump_config },
135
{ IP_FW_XADD, IP_FW3_OPVER, HDIR_BOTH, add_rules },
136
{ IP_FW_XDEL, IP_FW3_OPVER, HDIR_BOTH, del_rules },
137
{ IP_FW_XZERO, IP_FW3_OPVER, HDIR_SET, clear_rules },
138
{ IP_FW_XRESETLOG, IP_FW3_OPVER, HDIR_SET, clear_rules },
139
{ IP_FW_XMOVE, IP_FW3_OPVER, HDIR_SET, move_rules },
140
{ IP_FW_SET_SWAP, IP_FW3_OPVER, HDIR_SET, manage_sets },
141
{ IP_FW_SET_MOVE, IP_FW3_OPVER, HDIR_SET, manage_sets },
142
{ IP_FW_SET_ENABLE, IP_FW3_OPVER, HDIR_SET, manage_sets },
143
{ IP_FW_DUMP_SOPTCODES, IP_FW3_OPVER, HDIR_GET, dump_soptcodes },
144
{ IP_FW_DUMP_SRVOBJECTS, IP_FW3_OPVER, HDIR_GET, dump_srvobjects },
145
{ IP_FW_SKIPTO_CACHE, IP_FW3_OPVER, HDIR_BOTH, manage_skiptocache },
146
};
147
148
static struct opcode_obj_rewrite *find_op_rw(ipfw_insn *cmd,
149
uint32_t *puidx, uint8_t *ptype);
150
static int ref_rule_objects(struct ip_fw_chain *ch, struct ip_fw *rule,
151
struct rule_check_info *ci, struct obj_idx *oib, struct tid_info *ti);
152
static int ref_opcode_object(struct ip_fw_chain *ch, ipfw_insn *cmd,
153
struct tid_info *ti, struct obj_idx *pidx, int *unresolved);
154
static void unref_rule_objects(struct ip_fw_chain *chain, struct ip_fw *rule);
155
static void unref_oib_objects(struct ip_fw_chain *ch, ipfw_insn *cmd,
156
struct obj_idx *oib, struct obj_idx *end);
157
static int export_objhash_ntlv(struct namedobj_instance *ni, uint32_t kidx,
158
struct sockopt_data *sd);
159
160
/*
161
* Opcode object rewriter variables
162
*/
163
struct opcode_obj_rewrite *ctl3_rewriters;
164
static size_t ctl3_rsize;
165
166
/*
167
* static variables followed by global ones
168
*/
169
170
VNET_DEFINE_STATIC(uma_zone_t, ipfw_cntr_zone);
171
#define V_ipfw_cntr_zone VNET(ipfw_cntr_zone)
172
173
void
174
ipfw_init_counters(void)
175
{
176
177
V_ipfw_cntr_zone = uma_zcreate("IPFW counters",
178
IPFW_RULE_CNTR_SIZE, NULL, NULL, NULL, NULL,
179
UMA_ALIGN_PTR, UMA_ZONE_PCPU);
180
}
181
182
void
183
ipfw_destroy_counters(void)
184
{
185
186
uma_zdestroy(V_ipfw_cntr_zone);
187
}
188
189
struct ip_fw *
190
ipfw_alloc_rule(struct ip_fw_chain *chain, size_t rulesize)
191
{
192
struct ip_fw *rule;
193
194
rule = malloc(rulesize, M_IPFW, M_WAITOK | M_ZERO);
195
rule->cntr = uma_zalloc_pcpu(V_ipfw_cntr_zone, M_WAITOK | M_ZERO);
196
rule->refcnt = 1;
197
198
return (rule);
199
}
200
201
void
202
ipfw_free_rule(struct ip_fw *rule)
203
{
204
205
/*
206
* We don't release refcnt here, since this function
207
* can be called without any locks held. The caller
208
* must release reference under IPFW_UH_WLOCK, and then
209
* call this function if refcount becomes 1.
210
*/
211
if (rule->refcnt > 1)
212
return;
213
uma_zfree_pcpu(V_ipfw_cntr_zone, rule->cntr);
214
free(rule, M_IPFW);
215
}
216
217
/*
218
* Find the smallest rule >= key, id.
219
* We could use bsearch but it is so simple that we code it directly
220
*/
221
int
222
ipfw_find_rule(struct ip_fw_chain *chain, uint32_t key, uint32_t id)
223
{
224
int i, lo, hi;
225
struct ip_fw *r;
226
227
for (lo = 0, hi = chain->n_rules - 1; lo < hi;) {
228
i = (lo + hi) / 2;
229
r = chain->map[i];
230
if (r->rulenum < key)
231
lo = i + 1; /* continue from the next one */
232
else if (r->rulenum > key)
233
hi = i; /* this might be good */
234
else if (r->id < id)
235
lo = i + 1; /* continue from the next one */
236
else /* r->id >= id */
237
hi = i; /* this might be good */
238
}
239
return hi;
240
}
241
242
/*
243
* Builds skipto cache on rule set @map.
244
*/
245
static void
246
update_skipto_cache(struct ip_fw_chain *chain, struct ip_fw **map)
247
{
248
uint32_t *smap, rulenum;
249
int i, mi;
250
251
IPFW_UH_WLOCK_ASSERT(chain);
252
253
mi = 0;
254
rulenum = map[mi]->rulenum;
255
smap = chain->idxmap_back;
256
257
if (smap == NULL)
258
return;
259
260
for (i = 0; i <= IPFW_DEFAULT_RULE; i++) {
261
smap[i] = mi;
262
/* Use the same rule index until i < rulenum */
263
if (i != rulenum || i == IPFW_DEFAULT_RULE)
264
continue;
265
/* Find next rule with num > i */
266
rulenum = map[++mi]->rulenum;
267
while (rulenum == i)
268
rulenum = map[++mi]->rulenum;
269
}
270
}
271
272
/*
273
* Swaps prepared (backup) index with current one.
274
*/
275
static void
276
swap_skipto_cache(struct ip_fw_chain *chain)
277
{
278
uint32_t *map;
279
280
IPFW_UH_WLOCK_ASSERT(chain);
281
IPFW_WLOCK_ASSERT(chain);
282
283
map = chain->idxmap;
284
chain->idxmap = chain->idxmap_back;
285
chain->idxmap_back = map;
286
}
287
288
/*
289
* Allocate and initialize skipto cache.
290
*/
291
void
292
ipfw_init_skipto_cache(struct ip_fw_chain *chain)
293
{
294
uint32_t *idxmap, *idxmap_back;
295
296
idxmap = malloc((IPFW_DEFAULT_RULE + 1) * sizeof(uint32_t),
297
M_IPFW, M_WAITOK | M_ZERO);
298
idxmap_back = malloc((IPFW_DEFAULT_RULE + 1) * sizeof(uint32_t),
299
M_IPFW, M_WAITOK | M_ZERO);
300
301
/*
302
* Note we may be called at any time after initialization,
303
* for example, on first skipto rule, so we need to
304
* provide valid chain->idxmap on return
305
*/
306
307
IPFW_UH_WLOCK(chain);
308
if (chain->idxmap != NULL) {
309
IPFW_UH_WUNLOCK(chain);
310
free(idxmap, M_IPFW);
311
free(idxmap_back, M_IPFW);
312
return;
313
}
314
315
/* Set backup pointer first to permit building cache */
316
chain->idxmap_back = idxmap_back;
317
if (V_skipto_cache != 0)
318
update_skipto_cache(chain, chain->map);
319
IPFW_WLOCK(chain);
320
/* It is now safe to set chain->idxmap ptr */
321
chain->idxmap = idxmap;
322
swap_skipto_cache(chain);
323
IPFW_WUNLOCK(chain);
324
IPFW_UH_WUNLOCK(chain);
325
}
326
327
/*
328
* Destroys skipto cache.
329
*/
330
void
331
ipfw_destroy_skipto_cache(struct ip_fw_chain *chain)
332
{
333
free(chain->idxmap, M_IPFW);
334
free(chain->idxmap_back, M_IPFW);
335
}
336
337
/*
338
* swap the maps.
339
*/
340
static struct ip_fw **
341
swap_map(struct ip_fw_chain *chain, struct ip_fw **new_map, int new_len)
342
{
343
struct ip_fw **old_map;
344
345
IPFW_UH_WLOCK_ASSERT(chain);
346
347
IPFW_WLOCK(chain);
348
chain->id++;
349
chain->n_rules = new_len;
350
old_map = chain->map;
351
chain->map = new_map;
352
swap_skipto_cache(chain);
353
IPFW_WUNLOCK(chain);
354
return old_map;
355
}
356
357
static void
358
export_cntr1_base(struct ip_fw *krule, struct ip_fw_bcounter *cntr)
359
{
360
struct timeval boottime;
361
362
cntr->size = sizeof(*cntr);
363
364
if (krule->cntr != NULL) {
365
cntr->pcnt = counter_u64_fetch(krule->cntr);
366
cntr->bcnt = counter_u64_fetch(krule->cntr + 1);
367
cntr->timestamp = krule->timestamp;
368
}
369
if (cntr->timestamp > 0) {
370
getboottime(&boottime);
371
cntr->timestamp += boottime.tv_sec;
372
}
373
}
374
375
/*
376
* Export rule into v1 format (Current).
377
* Layout:
378
* [ ipfw_obj_tlv(IPFW_TLV_RULE_ENT)
379
* [ ip_fw_rule ] OR
380
* [ ip_fw_bcounter ip_fw_rule] (depends on rcntrs).
381
* ]
382
* Assume @data is zeroed.
383
*/
384
static void
385
export_rule1(struct ip_fw *krule, caddr_t data, int len, int rcntrs)
386
{
387
struct ip_fw_bcounter *cntr;
388
struct ip_fw_rule *urule;
389
ipfw_obj_tlv *tlv;
390
391
/* Fill in TLV header */
392
tlv = (ipfw_obj_tlv *)data;
393
tlv->type = IPFW_TLV_RULE_ENT;
394
tlv->length = len;
395
396
if (rcntrs != 0) {
397
/* Copy counters */
398
cntr = (struct ip_fw_bcounter *)(tlv + 1);
399
urule = (struct ip_fw_rule *)(cntr + 1);
400
export_cntr1_base(krule, cntr);
401
} else
402
urule = (struct ip_fw_rule *)(tlv + 1);
403
404
/* copy header */
405
urule->act_ofs = krule->act_ofs;
406
urule->cmd_len = krule->cmd_len;
407
urule->rulenum = krule->rulenum;
408
urule->set = krule->set;
409
urule->flags = krule->flags;
410
urule->id = krule->id;
411
412
/* Copy opcodes */
413
memcpy(urule->cmd, krule->cmd, krule->cmd_len * sizeof(uint32_t));
414
}
415
416
/*
417
* Add new rule(s) to the list possibly creating rule number for each.
418
* Update the rule_number in the input struct so the caller knows it as well.
419
* Must be called without IPFW_UH held
420
*/
421
int
422
ipfw_commit_rules(struct ip_fw_chain *chain, struct rule_check_info *rci,
423
int count)
424
{
425
int error, i, insert_before, tcount, rule_idx, last_rule_idx;
426
uint32_t rulenum;
427
struct rule_check_info *ci;
428
struct ip_fw *krule;
429
struct ip_fw **map; /* the new array of pointers */
430
431
IPFW_UH_WLOCK(chain);
432
/* Check if we need to do table/obj index remap */
433
tcount = 0;
434
for (ci = rci, i = 0; i < count; ci++, i++) {
435
if (ci->object_opcodes == 0)
436
continue;
437
438
/*
439
* Rule has some object opcodes.
440
* We need to find (and create non-existing)
441
* kernel objects, and reference existing ones.
442
*/
443
error = rewrite_rule_uidx(chain, ci);
444
if (error != 0) {
445
446
/*
447
* rewrite failed, state for current rule
448
* has been reverted. Check if we need to
449
* revert more.
450
*/
451
if (tcount > 0) {
452
453
/*
454
* We have some more table rules
455
* we need to rollback.
456
*/
457
while (ci != rci) {
458
ci--;
459
if (ci->object_opcodes == 0)
460
continue;
461
unref_rule_objects(chain,ci->krule);
462
463
}
464
}
465
IPFW_UH_WUNLOCK(chain);
466
return (error);
467
}
468
469
tcount++;
470
}
471
472
map = malloc((chain->n_rules + count) * sizeof(struct ip_fw *),
473
M_IPFW, M_ZERO | M_WAITOK);
474
475
if (V_autoinc_step < 1)
476
V_autoinc_step = 1;
477
else if (V_autoinc_step > 1000)
478
V_autoinc_step = 1000;
479
480
last_rule_idx = 0;
481
for (ci = rci, i = 0; i < count; ci++, i++) {
482
krule = ci->krule;
483
rulenum = krule->rulenum;
484
485
krule->id = chain->id + 1;
486
487
/* find the insertion point, we will insert before */
488
insert_before = rulenum ? rulenum + 1 : IPFW_DEFAULT_RULE;
489
rule_idx = ipfw_find_rule(chain, insert_before, 0);
490
/* duplicate the previous part */
491
if (last_rule_idx < rule_idx)
492
bcopy(chain->map + last_rule_idx, map + last_rule_idx + i,
493
(rule_idx - last_rule_idx) * sizeof(struct ip_fw *));
494
last_rule_idx = rule_idx;
495
map[rule_idx + i] = krule;
496
if (rulenum == 0) {
497
/* Compute rule number and write it back */
498
rulenum = rule_idx + i > 0 ? map[rule_idx + i - 1]->rulenum : 0;
499
if (rulenum < IPFW_DEFAULT_RULE - V_autoinc_step)
500
rulenum += V_autoinc_step;
501
krule->rulenum = rulenum;
502
/* Save number to userland rule */
503
memcpy((char *)ci->urule + ci->urule_numoff, &rulenum,
504
sizeof(rulenum));
505
}
506
if (ACTION_PTR(krule)->opcode == O_LOG)
507
ipfw_tap_alloc(chain, krule->rulenum);
508
}
509
510
/* duplicate the remaining part, we always have the default rule */
511
bcopy(chain->map + last_rule_idx, map + last_rule_idx + count,
512
(chain->n_rules - last_rule_idx) * sizeof(struct ip_fw *));
513
514
if (V_skipto_cache != 0)
515
update_skipto_cache(chain, map);
516
map = swap_map(chain, map, chain->n_rules + count);
517
IPFW_UH_WUNLOCK(chain);
518
if (map)
519
free(map, M_IPFW);
520
return (0);
521
}
522
523
int
524
ipfw_add_protected_rule(struct ip_fw_chain *chain, struct ip_fw *rule)
525
{
526
struct ip_fw **map;
527
528
IPFW_UH_WLOCK(chain);
529
map = malloc((chain->n_rules + 1) * sizeof(struct ip_fw *),
530
M_IPFW, M_ZERO | M_WAITOK);
531
if (chain->n_rules > 0)
532
bcopy(chain->map, map,
533
chain->n_rules * sizeof(struct ip_fw *));
534
map[chain->n_rules] = rule;
535
rule->rulenum = IPFW_DEFAULT_RULE;
536
rule->set = RESVD_SET;
537
rule->id = chain->id + 1;
538
/* We add rule in the end of chain, no need to update skipto cache */
539
map = swap_map(chain, map, chain->n_rules + 1);
540
IPFW_UH_WUNLOCK(chain);
541
free(map, M_IPFW);
542
return (0);
543
}
544
545
/*
546
* Adds @rule to the list of rules to reap
547
*/
548
void
549
ipfw_reap_add(struct ip_fw_chain *chain, struct ip_fw **head,
550
struct ip_fw *rule)
551
{
552
553
IPFW_UH_WLOCK_ASSERT(chain);
554
555
/* Unlink rule from everywhere */
556
unref_rule_objects(chain, rule);
557
558
rule->next = *head;
559
*head = rule;
560
}
561
562
/*
563
* Reclaim storage associated with a list of rules. This is
564
* typically the list created using remove_rule.
565
* A NULL pointer on input is handled correctly.
566
*/
567
void
568
ipfw_reap_rules(struct ip_fw *head)
569
{
570
struct ip_fw *rule;
571
572
while ((rule = head) != NULL) {
573
head = head->next;
574
ipfw_free_rule(rule);
575
}
576
}
577
578
/*
579
* Rules to keep are
580
* (default || reserved || !match_set || !match_number)
581
* where
582
* default ::= (rule->rulenum == IPFW_DEFAULT_RULE)
583
* // the default rule is always protected
584
*
585
* reserved ::= (cmd == 0 && n == 0 && rule->set == RESVD_SET)
586
* // RESVD_SET is protected only if cmd == 0 and n == 0 ("ipfw flush")
587
*
588
* match_set ::= (cmd == 0 || rule->set == set)
589
* // set number is ignored for cmd == 0
590
*
591
* match_number ::= (cmd == 1 || n == 0 || n == rule->rulenum)
592
* // number is ignored for cmd == 1 or n == 0
593
*
594
*/
595
int
596
ipfw_match_range(struct ip_fw *rule, ipfw_range_tlv *rt)
597
{
598
599
/* Don't match default rule for modification queries */
600
if (rule->rulenum == IPFW_DEFAULT_RULE &&
601
(rt->flags & IPFW_RCFLAG_DEFAULT) == 0)
602
return (0);
603
604
/* Don't match rules in reserved set for flush requests */
605
if ((rt->flags & IPFW_RCFLAG_ALL) != 0 && rule->set == RESVD_SET)
606
return (0);
607
608
/* If we're filtering by set, don't match other sets */
609
if ((rt->flags & IPFW_RCFLAG_SET) != 0 && rule->set != rt->set)
610
return (0);
611
612
if ((rt->flags & IPFW_RCFLAG_RANGE) != 0 &&
613
(rule->rulenum < rt->start_rule || rule->rulenum > rt->end_rule))
614
return (0);
615
616
return (1);
617
}
618
619
struct manage_sets_args {
620
uint32_t set;
621
uint8_t new_set;
622
};
623
624
static int
625
swap_sets_cb(struct namedobj_instance *ni, struct named_object *no,
626
void *arg)
627
{
628
struct manage_sets_args *args;
629
630
args = (struct manage_sets_args *)arg;
631
if (no->set == (uint8_t)args->set)
632
no->set = args->new_set;
633
else if (no->set == args->new_set)
634
no->set = (uint8_t)args->set;
635
return (0);
636
}
637
638
static int
639
move_sets_cb(struct namedobj_instance *ni, struct named_object *no,
640
void *arg)
641
{
642
struct manage_sets_args *args;
643
644
args = (struct manage_sets_args *)arg;
645
if (no->set == (uint8_t)args->set)
646
no->set = args->new_set;
647
return (0);
648
}
649
650
static int
651
test_sets_cb(struct namedobj_instance *ni, struct named_object *no,
652
void *arg)
653
{
654
struct manage_sets_args *args;
655
656
args = (struct manage_sets_args *)arg;
657
if (no->set != (uint8_t)args->set)
658
return (0);
659
if (ipfw_objhash_lookup_name_type(ni, args->new_set,
660
no->etlv, no->name) != NULL)
661
return (EEXIST);
662
return (0);
663
}
664
665
/*
666
* Generic function to handler moving and swapping sets.
667
*/
668
int
669
ipfw_obj_manage_sets(struct namedobj_instance *ni, uint16_t type,
670
uint32_t set, uint8_t new_set, enum ipfw_sets_cmd cmd)
671
{
672
struct manage_sets_args args;
673
struct named_object *no;
674
675
args.set = set;
676
args.new_set = new_set;
677
switch (cmd) {
678
case SWAP_ALL:
679
return (ipfw_objhash_foreach_type(ni, swap_sets_cb,
680
&args, type));
681
case TEST_ALL:
682
return (ipfw_objhash_foreach_type(ni, test_sets_cb,
683
&args, type));
684
case MOVE_ALL:
685
return (ipfw_objhash_foreach_type(ni, move_sets_cb,
686
&args, type));
687
case COUNT_ONE:
688
/*
689
* @set used to pass kidx.
690
* When @new_set is zero - reset object counter,
691
* otherwise increment it.
692
*/
693
no = ipfw_objhash_lookup_kidx(ni, set);
694
if (new_set != 0)
695
no->ocnt++;
696
else
697
no->ocnt = 0;
698
return (0);
699
case TEST_ONE:
700
/* @set used to pass kidx */
701
no = ipfw_objhash_lookup_kidx(ni, set);
702
/*
703
* First check number of references:
704
* when it differs, this mean other rules are holding
705
* reference to given object, so it is not possible to
706
* change its set. Note that refcnt may account references
707
* to some going-to-be-added rules. Since we don't know
708
* their numbers (and even if they will be added) it is
709
* perfectly OK to return error here.
710
*/
711
if (no->ocnt != no->refcnt)
712
return (EBUSY);
713
if (ipfw_objhash_lookup_name_type(ni, new_set, type,
714
no->name) != NULL)
715
return (EEXIST);
716
return (0);
717
case MOVE_ONE:
718
/* @set used to pass kidx */
719
no = ipfw_objhash_lookup_kidx(ni, set);
720
no->set = new_set;
721
return (0);
722
}
723
return (EINVAL);
724
}
725
726
/*
727
* Delete rules matching range @rt.
728
* Saves number of deleted rules in @ndel.
729
*
730
* Returns 0 on success.
731
*/
732
int
733
delete_range(struct ip_fw_chain *chain, ipfw_range_tlv *rt, int *ndel)
734
{
735
struct ip_fw *reap, *rule, **map;
736
uint32_t end, start;
737
int i, n, ndyn, ofs;
738
739
reap = NULL;
740
IPFW_UH_WLOCK(chain); /* arbitrate writers */
741
742
/*
743
* Stage 1: Determine range to inspect.
744
* Range is half-inclusive, e.g [start, end).
745
*/
746
start = 0;
747
end = chain->n_rules - 1;
748
749
if ((rt->flags & IPFW_RCFLAG_RANGE) != 0) {
750
start = ipfw_find_rule(chain, rt->start_rule, 0);
751
752
if (rt->end_rule >= IPFW_DEFAULT_RULE)
753
rt->end_rule = IPFW_DEFAULT_RULE - 1;
754
end = ipfw_find_rule(chain, rt->end_rule, UINT32_MAX);
755
}
756
757
if (rt->flags & IPFW_RCFLAG_DYNAMIC) {
758
/*
759
* Requested deleting only for dynamic states.
760
*/
761
*ndel = 0;
762
ipfw_expire_dyn_states(chain, rt);
763
IPFW_UH_WUNLOCK(chain);
764
return (0);
765
}
766
767
/* Allocate new map of the same size */
768
map = malloc(chain->n_rules * sizeof(struct ip_fw *),
769
M_IPFW, M_ZERO | M_WAITOK);
770
n = 0;
771
ndyn = 0;
772
ofs = start;
773
/* 1. bcopy the initial part of the map */
774
if (start > 0)
775
bcopy(chain->map, map, start * sizeof(struct ip_fw *));
776
/* 2. copy active rules between start and end */
777
for (i = start; i < end; i++) {
778
rule = chain->map[i];
779
if (ipfw_match_range(rule, rt) == 0) {
780
map[ofs++] = rule;
781
continue;
782
}
783
784
n++;
785
if (ipfw_is_dyn_rule(rule) != 0)
786
ndyn++;
787
}
788
/* 3. copy the final part of the map */
789
bcopy(chain->map + end, map + ofs,
790
(chain->n_rules - end) * sizeof(struct ip_fw *));
791
/* 4. recalculate skipto cache */
792
update_skipto_cache(chain, map);
793
/* 5. swap the maps (under UH_WLOCK + WHLOCK) */
794
map = swap_map(chain, map, chain->n_rules - n);
795
/* 6. Remove all dynamic states originated by deleted rules */
796
if (ndyn > 0)
797
ipfw_expire_dyn_states(chain, rt);
798
/* 7. now remove the rules deleted from the old map */
799
for (i = start; i < end; i++) {
800
rule = map[i];
801
if (ipfw_match_range(rule, rt) == 0)
802
continue;
803
ipfw_reap_add(chain, &reap, rule);
804
}
805
IPFW_UH_WUNLOCK(chain);
806
807
ipfw_reap_rules(reap);
808
if (map != NULL)
809
free(map, M_IPFW);
810
*ndel = n;
811
return (0);
812
}
813
814
static int
815
move_objects(struct ip_fw_chain *ch, ipfw_range_tlv *rt)
816
{
817
struct opcode_obj_rewrite *rw;
818
struct ip_fw *rule;
819
ipfw_insn *cmd;
820
uint32_t kidx;
821
int cmdlen, i, l, c;
822
823
IPFW_UH_WLOCK_ASSERT(ch);
824
825
/* Stage 1: count number of references by given rules */
826
for (c = 0, i = 0; i < ch->n_rules - 1; i++) {
827
rule = ch->map[i];
828
if (ipfw_match_range(rule, rt) == 0)
829
continue;
830
if (rule->set == rt->new_set) /* nothing to do */
831
continue;
832
/* Search opcodes with named objects */
833
for (l = rule->cmd_len, cmdlen = 0, cmd = rule->cmd;
834
l > 0; l -= cmdlen, cmd += cmdlen) {
835
cmdlen = F_LEN(cmd);
836
rw = find_op_rw(cmd, &kidx, NULL);
837
if (rw == NULL || rw->manage_sets == NULL)
838
continue;
839
/*
840
* When manage_sets() returns non-zero value to
841
* COUNT_ONE command, consider this as an object
842
* doesn't support sets (e.g. disabled with sysctl).
843
* So, skip checks for this object.
844
*/
845
if (rw->manage_sets(ch, kidx, 1, COUNT_ONE) != 0)
846
continue;
847
c++;
848
}
849
}
850
if (c == 0) /* No objects found */
851
return (0);
852
/* Stage 2: verify "ownership" */
853
for (c = 0, i = 0; (i < ch->n_rules - 1) && c == 0; i++) {
854
rule = ch->map[i];
855
if (ipfw_match_range(rule, rt) == 0)
856
continue;
857
if (rule->set == rt->new_set) /* nothing to do */
858
continue;
859
/* Search opcodes with named objects */
860
for (l = rule->cmd_len, cmdlen = 0, cmd = rule->cmd;
861
l > 0 && c == 0; l -= cmdlen, cmd += cmdlen) {
862
cmdlen = F_LEN(cmd);
863
rw = find_op_rw(cmd, &kidx, NULL);
864
if (rw == NULL || rw->manage_sets == NULL)
865
continue;
866
/* Test for ownership and conflicting names */
867
c = rw->manage_sets(ch, kidx,
868
(uint8_t)rt->new_set, TEST_ONE);
869
}
870
}
871
/* Stage 3: change set and cleanup */
872
for (i = 0; i < ch->n_rules - 1; i++) {
873
rule = ch->map[i];
874
if (ipfw_match_range(rule, rt) == 0)
875
continue;
876
if (rule->set == rt->new_set) /* nothing to do */
877
continue;
878
/* Search opcodes with named objects */
879
for (l = rule->cmd_len, cmdlen = 0, cmd = rule->cmd;
880
l > 0; l -= cmdlen, cmd += cmdlen) {
881
cmdlen = F_LEN(cmd);
882
rw = find_op_rw(cmd, &kidx, NULL);
883
if (rw == NULL || rw->manage_sets == NULL)
884
continue;
885
/* cleanup object counter */
886
rw->manage_sets(ch, kidx,
887
0 /* reset counter */, COUNT_ONE);
888
if (c != 0)
889
continue;
890
/* change set */
891
rw->manage_sets(ch, kidx,
892
(uint8_t)rt->new_set, MOVE_ONE);
893
}
894
}
895
return (c);
896
}
897
898
/*
899
* Changes set of given rule rannge @rt
900
* with each other.
901
*
902
* Returns 0 on success.
903
*/
904
static int
905
move_range(struct ip_fw_chain *chain, ipfw_range_tlv *rt)
906
{
907
struct ip_fw *rule;
908
int i;
909
910
IPFW_UH_WLOCK(chain);
911
912
/*
913
* Move rules with matching paramenerts to a new set.
914
* This one is much more complex. We have to ensure
915
* that all referenced tables (if any) are referenced
916
* by given rule subset only. Otherwise, we can't move
917
* them to new set and have to return error.
918
*/
919
if ((i = move_objects(chain, rt)) != 0) {
920
IPFW_UH_WUNLOCK(chain);
921
return (i);
922
}
923
924
/* XXX: We have to do swap holding WLOCK */
925
for (i = 0; i < chain->n_rules; i++) {
926
rule = chain->map[i];
927
if (ipfw_match_range(rule, rt) == 0)
928
continue;
929
rule->set = rt->new_set;
930
}
931
932
IPFW_UH_WUNLOCK(chain);
933
934
return (0);
935
}
936
937
/*
938
* Returns pointer to action instruction, skips all possible rule
939
* modifiers like O_LOG, O_TAG, O_ALTQ.
940
*/
941
ipfw_insn *
942
ipfw_get_action(struct ip_fw *rule)
943
{
944
ipfw_insn *cmd;
945
int l, cmdlen;
946
947
cmd = ACTION_PTR(rule);
948
l = rule->cmd_len - rule->act_ofs;
949
while (l > 0) {
950
switch (cmd->opcode) {
951
case O_ALTQ:
952
case O_LOG:
953
case O_TAG:
954
break;
955
default:
956
return (cmd);
957
}
958
cmdlen = F_LEN(cmd);
959
l -= cmdlen;
960
cmd += cmdlen;
961
}
962
panic("%s: rule (%p) has not action opcode", __func__, rule);
963
return (NULL);
964
}
965
966
/*
967
* Clear counters for a specific rule.
968
* Normally run under IPFW_UH_RLOCK, but these are idempotent ops
969
* so we only care that rules do not disappear.
970
*/
971
static void
972
clear_counters(struct ip_fw *rule, int log_only)
973
{
974
ipfw_insn_log *l = (ipfw_insn_log *)ACTION_PTR(rule);
975
976
if (log_only == 0)
977
IPFW_ZERO_RULE_COUNTER(rule);
978
if (l->o.opcode == O_LOG)
979
l->log_left = l->max_log;
980
}
981
982
/*
983
* Flushes rules counters and/or log values on matching range.
984
*
985
* Returns number of items cleared.
986
*/
987
static int
988
clear_range(struct ip_fw_chain *chain, ipfw_range_tlv *rt, int log_only)
989
{
990
struct ip_fw *rule;
991
int num;
992
int i;
993
994
num = 0;
995
rt->flags |= IPFW_RCFLAG_DEFAULT;
996
997
IPFW_UH_WLOCK(chain); /* arbitrate writers */
998
for (i = 0; i < chain->n_rules; i++) {
999
rule = chain->map[i];
1000
if (ipfw_match_range(rule, rt) == 0)
1001
continue;
1002
clear_counters(rule, log_only);
1003
num++;
1004
}
1005
IPFW_UH_WUNLOCK(chain);
1006
1007
return (num);
1008
}
1009
1010
static int
1011
check_range_tlv(ipfw_range_tlv *rt)
1012
{
1013
1014
if (rt->head.length != sizeof(*rt))
1015
return (1);
1016
if (rt->start_rule > rt->end_rule)
1017
return (1);
1018
if (rt->set >= IPFW_MAX_SETS || rt->new_set >= IPFW_MAX_SETS)
1019
return (1);
1020
1021
if ((rt->flags & IPFW_RCFLAG_USER) != rt->flags)
1022
return (1);
1023
1024
return (0);
1025
}
1026
1027
/*
1028
* Delete rules matching specified parameters
1029
* Data layout (v0)(current):
1030
* Request: [ ipfw_obj_header ipfw_range_tlv ]
1031
* Reply: [ ipfw_obj_header ipfw_range_tlv ]
1032
*
1033
* Saves number of deleted rules in ipfw_range_tlv->new_set.
1034
*
1035
* Returns 0 on success.
1036
*/
1037
static int
1038
del_rules(struct ip_fw_chain *chain, ip_fw3_opheader *op3,
1039
struct sockopt_data *sd)
1040
{
1041
ipfw_range_header *rh;
1042
int error, ndel;
1043
1044
if (sd->valsize != sizeof(*rh))
1045
return (EINVAL);
1046
1047
rh = (ipfw_range_header *)ipfw_get_sopt_space(sd, sd->valsize);
1048
1049
if (check_range_tlv(&rh->range) != 0)
1050
return (EINVAL);
1051
1052
ndel = 0;
1053
if ((error = delete_range(chain, &rh->range, &ndel)) != 0)
1054
return (error);
1055
1056
/* Save number of rules deleted */
1057
rh->range.new_set = ndel;
1058
return (0);
1059
}
1060
1061
/*
1062
* Move rules/sets matching specified parameters
1063
* Data layout (v0)(current):
1064
* Request: [ ipfw_obj_header ipfw_range_tlv ]
1065
*
1066
* Returns 0 on success.
1067
*/
1068
static int
1069
move_rules(struct ip_fw_chain *chain, ip_fw3_opheader *op3,
1070
struct sockopt_data *sd)
1071
{
1072
ipfw_range_header *rh;
1073
1074
if (sd->valsize != sizeof(*rh))
1075
return (EINVAL);
1076
1077
rh = (ipfw_range_header *)ipfw_get_sopt_space(sd, sd->valsize);
1078
1079
if (check_range_tlv(&rh->range) != 0)
1080
return (EINVAL);
1081
1082
return (move_range(chain, &rh->range));
1083
}
1084
1085
/*
1086
* Clear rule accounting data matching specified parameters
1087
* Data layout (v0)(current):
1088
* Request: [ ipfw_obj_header ipfw_range_tlv ]
1089
* Reply: [ ipfw_obj_header ipfw_range_tlv ]
1090
*
1091
* Saves number of cleared rules in ipfw_range_tlv->new_set.
1092
*
1093
* Returns 0 on success.
1094
*/
1095
static int
1096
clear_rules(struct ip_fw_chain *chain, ip_fw3_opheader *op3,
1097
struct sockopt_data *sd)
1098
{
1099
ipfw_range_header *rh;
1100
int log_only, num;
1101
char *msg;
1102
1103
if (sd->valsize != sizeof(*rh))
1104
return (EINVAL);
1105
1106
rh = (ipfw_range_header *)ipfw_get_sopt_space(sd, sd->valsize);
1107
1108
if (check_range_tlv(&rh->range) != 0)
1109
return (EINVAL);
1110
1111
log_only = (op3->opcode == IP_FW_XRESETLOG);
1112
1113
num = clear_range(chain, &rh->range, log_only);
1114
1115
if (rh->range.flags & IPFW_RCFLAG_ALL)
1116
msg = log_only ? "All logging counts reset" :
1117
"Accounting cleared";
1118
else
1119
msg = log_only ? "logging count reset" : "cleared";
1120
1121
if (V_fw_verbose) {
1122
int lev = LOG_SECURITY | LOG_NOTICE;
1123
log(lev, "ipfw: %s.\n", msg);
1124
}
1125
1126
/* Save number of rules cleared */
1127
rh->range.new_set = num;
1128
return (0);
1129
}
1130
1131
static void
1132
enable_sets(struct ip_fw_chain *chain, ipfw_range_tlv *rt)
1133
{
1134
uint32_t v_set;
1135
1136
IPFW_UH_WLOCK_ASSERT(chain);
1137
1138
/* Change enabled/disabled sets mask */
1139
v_set = (V_set_disable | rt->set) & ~rt->new_set;
1140
v_set &= ~(1 << RESVD_SET); /* set RESVD_SET always enabled */
1141
IPFW_WLOCK(chain);
1142
V_set_disable = v_set;
1143
IPFW_WUNLOCK(chain);
1144
}
1145
1146
static int
1147
swap_sets(struct ip_fw_chain *chain, ipfw_range_tlv *rt, int mv)
1148
{
1149
struct opcode_obj_rewrite *rw;
1150
struct ip_fw *rule;
1151
int i;
1152
1153
IPFW_UH_WLOCK_ASSERT(chain);
1154
1155
if (rt->set == rt->new_set) /* nothing to do */
1156
return (0);
1157
1158
if (mv != 0) {
1159
/*
1160
* Berfore moving the rules we need to check that
1161
* there aren't any conflicting named objects.
1162
*/
1163
for (rw = ctl3_rewriters;
1164
rw < ctl3_rewriters + ctl3_rsize; rw++) {
1165
if (rw->manage_sets == NULL)
1166
continue;
1167
i = rw->manage_sets(chain, (uint8_t)rt->set,
1168
(uint8_t)rt->new_set, TEST_ALL);
1169
if (i != 0)
1170
return (EEXIST);
1171
}
1172
}
1173
/* Swap or move two sets */
1174
for (i = 0; i < chain->n_rules - 1; i++) {
1175
rule = chain->map[i];
1176
if (rule->set == (uint8_t)rt->set)
1177
rule->set = (uint8_t)rt->new_set;
1178
else if (rule->set == (uint8_t)rt->new_set && mv == 0)
1179
rule->set = (uint8_t)rt->set;
1180
}
1181
for (rw = ctl3_rewriters; rw < ctl3_rewriters + ctl3_rsize; rw++) {
1182
if (rw->manage_sets == NULL)
1183
continue;
1184
rw->manage_sets(chain, (uint8_t)rt->set,
1185
(uint8_t)rt->new_set, mv != 0 ? MOVE_ALL: SWAP_ALL);
1186
}
1187
return (0);
1188
}
1189
1190
/*
1191
* Swaps or moves set
1192
* Data layout (v0)(current):
1193
* Request: [ ipfw_obj_header ipfw_range_tlv ]
1194
*
1195
* Returns 0 on success.
1196
*/
1197
static int
1198
manage_sets(struct ip_fw_chain *chain, ip_fw3_opheader *op3,
1199
struct sockopt_data *sd)
1200
{
1201
ipfw_range_header *rh;
1202
int ret;
1203
1204
if (sd->valsize != sizeof(*rh))
1205
return (EINVAL);
1206
1207
rh = (ipfw_range_header *)ipfw_get_sopt_space(sd, sd->valsize);
1208
1209
if (rh->range.head.length != sizeof(ipfw_range_tlv))
1210
return (1);
1211
/* enable_sets() expects bitmasks. */
1212
if (op3->opcode != IP_FW_SET_ENABLE &&
1213
(rh->range.set >= IPFW_MAX_SETS ||
1214
rh->range.new_set >= IPFW_MAX_SETS))
1215
return (EINVAL);
1216
1217
ret = 0;
1218
IPFW_UH_WLOCK(chain);
1219
switch (op3->opcode) {
1220
case IP_FW_SET_SWAP:
1221
case IP_FW_SET_MOVE:
1222
ret = swap_sets(chain, &rh->range,
1223
op3->opcode == IP_FW_SET_MOVE);
1224
break;
1225
case IP_FW_SET_ENABLE:
1226
enable_sets(chain, &rh->range);
1227
break;
1228
}
1229
IPFW_UH_WUNLOCK(chain);
1230
1231
return (ret);
1232
}
1233
1234
/* Check rule format */
1235
int
1236
ipfw_check_rule(struct ip_fw_rule *rule, size_t size,
1237
struct rule_check_info *ci)
1238
{
1239
int l;
1240
1241
if (size < sizeof(*rule)) {
1242
printf("ipfw: rule too short\n");
1243
return (EINVAL);
1244
}
1245
1246
/* Check for valid cmd_len */
1247
l = roundup2(RULESIZE(rule), sizeof(uint64_t));
1248
if (l != size) {
1249
printf("ipfw: size mismatch (have %zu want %d)\n", size, l);
1250
return (EINVAL);
1251
}
1252
if (rule->act_ofs >= rule->cmd_len) {
1253
printf("ipfw: bogus action offset (%u > %u)\n",
1254
rule->act_ofs, rule->cmd_len - 1);
1255
return (EINVAL);
1256
}
1257
1258
if (rule->rulenum > IPFW_DEFAULT_RULE - 1)
1259
return (EINVAL);
1260
1261
return (check_ipfw_rule_body(rule->cmd, rule->cmd_len, ci));
1262
}
1263
1264
#define CHECK_TARG(a, c) \
1265
((a) == IP_FW_TARG && ((c)->flags & IPFW_RCIFLAG_HAS_STATE))
1266
1267
enum ipfw_opcheck_result
1268
ipfw_check_opcode(ipfw_insn **pcmd, int *plen, struct rule_check_info *ci)
1269
{
1270
ipfw_insn *cmd;
1271
size_t cmdlen;
1272
1273
cmd = *pcmd;
1274
cmdlen = F_LEN(cmd);
1275
1276
switch (cmd->opcode) {
1277
case O_PROBE_STATE:
1278
case O_KEEP_STATE:
1279
if (cmdlen != F_INSN_SIZE(ipfw_insn_kidx))
1280
return (BAD_SIZE);
1281
ci->object_opcodes++;
1282
ci->flags |= IPFW_RCIFLAG_HAS_STATE;
1283
break;
1284
case O_PROTO:
1285
case O_IP_SRC_ME:
1286
case O_IP_DST_ME:
1287
case O_LAYER2:
1288
case O_IN:
1289
case O_FRAG:
1290
case O_DIVERTED:
1291
case O_IPOPT:
1292
case O_IPTOS:
1293
case O_IPPRECEDENCE:
1294
case O_IPVER:
1295
case O_SOCKARG:
1296
case O_TCPFLAGS:
1297
case O_TCPOPTS:
1298
case O_ESTAB:
1299
case O_VERREVPATH:
1300
case O_VERSRCREACH:
1301
case O_ANTISPOOF:
1302
case O_IPSEC:
1303
#ifdef INET6
1304
case O_IP6_SRC_ME:
1305
case O_IP6_DST_ME:
1306
case O_EXT_HDR:
1307
case O_IP6:
1308
#endif
1309
case O_IP4:
1310
case O_TAG:
1311
case O_SKIP_ACTION:
1312
if (cmdlen != F_INSN_SIZE(ipfw_insn))
1313
return (BAD_SIZE);
1314
break;
1315
1316
case O_EXTERNAL_ACTION:
1317
if (cmdlen != F_INSN_SIZE(ipfw_insn_kidx))
1318
return (BAD_SIZE);
1319
1320
if (insntod(cmd, kidx)->kidx == 0)
1321
return (FAILED);
1322
ci->object_opcodes++;
1323
/*
1324
* Do we have O_EXTERNAL_INSTANCE or O_EXTERNAL_DATA
1325
* opcode?
1326
*/
1327
if (*plen != cmdlen) {
1328
*plen -= cmdlen;
1329
cmd += cmdlen;
1330
*pcmd = cmd;
1331
cmdlen = F_LEN(cmd);
1332
if (cmd->opcode == O_EXTERNAL_DATA)
1333
return (CHECK_ACTION);
1334
if (cmd->opcode != O_EXTERNAL_INSTANCE) {
1335
printf("ipfw: invalid opcode "
1336
"next to external action %u\n",
1337
cmd->opcode);
1338
return (FAILED);
1339
}
1340
if (cmdlen != F_INSN_SIZE(ipfw_insn_kidx))
1341
return (BAD_SIZE);
1342
if (insntod(cmd, kidx)->kidx == 0)
1343
return (FAILED);
1344
ci->object_opcodes++;
1345
}
1346
return (CHECK_ACTION);
1347
1348
case O_FIB:
1349
if (cmdlen != F_INSN_SIZE(ipfw_insn))
1350
return (BAD_SIZE);
1351
if (cmd->arg1 >= rt_numfibs) {
1352
printf("ipfw: invalid fib number %d\n",
1353
cmd->arg1);
1354
return (FAILED);
1355
}
1356
break;
1357
1358
case O_SETFIB:
1359
if (cmdlen != F_INSN_SIZE(ipfw_insn))
1360
return (BAD_SIZE);
1361
if ((cmd->arg1 != IP_FW_TARG) &&
1362
((cmd->arg1 & 0x7FFF) >= rt_numfibs)) {
1363
printf("ipfw: invalid fib number %d\n",
1364
cmd->arg1 & 0x7FFF);
1365
return (FAILED);
1366
}
1367
if (CHECK_TARG(cmd->arg1, ci))
1368
goto bad_targ;
1369
return (CHECK_ACTION);
1370
1371
case O_UID:
1372
case O_GID:
1373
case O_JAIL:
1374
case O_IP_SRC:
1375
case O_IP_DST:
1376
case O_TCPSEQ:
1377
case O_TCPACK:
1378
case O_PROB:
1379
case O_ICMPTYPE:
1380
if (cmdlen != F_INSN_SIZE(ipfw_insn_u32))
1381
return (BAD_SIZE);
1382
break;
1383
1384
case O_LIMIT:
1385
if (cmdlen != F_INSN_SIZE(ipfw_insn_limit))
1386
return (BAD_SIZE);
1387
ci->object_opcodes++;
1388
break;
1389
1390
case O_LOG:
1391
if (cmdlen != F_INSN_SIZE(ipfw_insn_log))
1392
return (BAD_SIZE);
1393
insntod(cmd, log)->log_left = insntod(cmd, log)->max_log;
1394
break;
1395
1396
case O_IP_SRC_MASK:
1397
case O_IP_DST_MASK:
1398
/* only odd command lengths */
1399
if ((cmdlen & 1) == 0)
1400
return (BAD_SIZE);
1401
break;
1402
1403
case O_IP_SRC_SET:
1404
case O_IP_DST_SET:
1405
if (cmd->arg1 == 0 || cmd->arg1 > 256) {
1406
printf("ipfw: invalid set size %d\n",
1407
cmd->arg1);
1408
return (FAILED);
1409
}
1410
if (cmdlen != F_INSN_SIZE(ipfw_insn_u32) +
1411
(cmd->arg1+31)/32 )
1412
return (BAD_SIZE);
1413
break;
1414
1415
case O_IP_SRC_LOOKUP:
1416
case O_IP_DST_LOOKUP:
1417
case O_IP_FLOW_LOOKUP:
1418
case O_MAC_SRC_LOOKUP:
1419
case O_MAC_DST_LOOKUP:
1420
if (cmdlen != F_INSN_SIZE(ipfw_insn_kidx) &&
1421
cmdlen != F_INSN_SIZE(ipfw_insn_table))
1422
return (BAD_SIZE);
1423
if (insntod(cmd, kidx)->kidx >= V_fw_tables_max) {
1424
printf("ipfw: invalid table index %u\n",
1425
insntod(cmd, kidx)->kidx);
1426
return (FAILED);
1427
}
1428
ci->object_opcodes++;
1429
break;
1430
case O_MACADDR2:
1431
if (cmdlen != F_INSN_SIZE(ipfw_insn_mac))
1432
return (BAD_SIZE);
1433
break;
1434
1435
case O_NOP:
1436
case O_IPID:
1437
case O_IPTTL:
1438
case O_IPLEN:
1439
case O_TCPDATALEN:
1440
case O_TCPMSS:
1441
case O_TCPWIN:
1442
case O_TAGGED:
1443
if (cmdlen < 1 || cmdlen > 31)
1444
return (BAD_SIZE);
1445
break;
1446
1447
case O_DSCP:
1448
case O_MARK:
1449
if (cmdlen != F_INSN_SIZE(ipfw_insn_u32) + 1)
1450
return (BAD_SIZE);
1451
break;
1452
1453
case O_MAC_TYPE:
1454
case O_IP_SRCPORT:
1455
case O_IP_DSTPORT: /* XXX artificial limit, 30 port pairs */
1456
if (cmdlen < 2 || cmdlen > 31)
1457
return (BAD_SIZE);
1458
break;
1459
1460
case O_RECV:
1461
case O_XMIT:
1462
case O_VIA:
1463
if (cmdlen != F_INSN_SIZE(ipfw_insn_if))
1464
return (BAD_SIZE);
1465
ci->object_opcodes++;
1466
break;
1467
1468
case O_ALTQ:
1469
if (cmdlen != F_INSN_SIZE(ipfw_insn_altq))
1470
return (BAD_SIZE);
1471
break;
1472
1473
case O_PIPE:
1474
case O_QUEUE:
1475
if (cmdlen != F_INSN_SIZE(ipfw_insn))
1476
return (BAD_SIZE);
1477
if (CHECK_TARG(cmd->arg1, ci))
1478
goto bad_targ;
1479
return (CHECK_ACTION);
1480
1481
case O_FORWARD_IP:
1482
if (cmdlen != F_INSN_SIZE(ipfw_insn_sa))
1483
return (BAD_SIZE);
1484
if (insntoc(cmd, sa)->sa.sin_addr.s_addr == INADDR_ANY &&
1485
(ci->flags & IPFW_RCIFLAG_HAS_STATE))
1486
goto bad_targ;
1487
return (CHECK_ACTION);
1488
#ifdef INET6
1489
case O_FORWARD_IP6:
1490
if (cmdlen != F_INSN_SIZE(ipfw_insn_sa6))
1491
return (BAD_SIZE);
1492
return (CHECK_ACTION);
1493
#endif /* INET6 */
1494
1495
case O_DIVERT:
1496
case O_TEE:
1497
if (ip_divert_ptr == NULL)
1498
return (FAILED);
1499
if (cmdlen != F_INSN_SIZE(ipfw_insn))
1500
return (BAD_SIZE);
1501
if (CHECK_TARG(cmd->arg1, ci))
1502
goto bad_targ;
1503
return (CHECK_ACTION);
1504
case O_NETGRAPH:
1505
case O_NGTEE:
1506
if (ng_ipfw_input_p == NULL)
1507
return (FAILED);
1508
if (cmdlen != F_INSN_SIZE(ipfw_insn))
1509
return (BAD_SIZE);
1510
if (CHECK_TARG(cmd->arg1, ci))
1511
goto bad_targ;
1512
return (CHECK_ACTION);
1513
case O_NAT:
1514
if (!IPFW_NAT_LOADED)
1515
return (FAILED);
1516
if (cmdlen != F_INSN_SIZE(ipfw_insn_nat))
1517
return (BAD_SIZE);
1518
if (CHECK_TARG(cmd->arg1, ci))
1519
goto bad_targ;
1520
return (CHECK_ACTION);
1521
1522
case O_SKIPTO:
1523
case O_CALLRETURN:
1524
case O_SETMARK:
1525
if (cmdlen != F_INSN_SIZE(ipfw_insn_u32))
1526
return (BAD_SIZE);
1527
/* O_CALLRETURN + F_NOT means 'return' opcode. */
1528
if (cmd->opcode != O_CALLRETURN || (cmd->len & F_NOT) == 0) {
1529
if (CHECK_TARG(insntoc(cmd, u32)->d[0], ci))
1530
goto bad_targ;
1531
}
1532
return (CHECK_ACTION);
1533
1534
case O_CHECK_STATE:
1535
if (cmdlen != F_INSN_SIZE(ipfw_insn_kidx))
1536
return (BAD_SIZE);
1537
ci->object_opcodes++;
1538
return (CHECK_ACTION);
1539
1540
case O_FORWARD_MAC: /* XXX not implemented yet */
1541
case O_COUNT:
1542
case O_ACCEPT:
1543
case O_DENY:
1544
case O_REJECT:
1545
case O_SETDSCP:
1546
#ifdef INET6
1547
case O_UNREACH6:
1548
#endif
1549
case O_REASS:
1550
if (cmdlen != F_INSN_SIZE(ipfw_insn))
1551
return (BAD_SIZE);
1552
if (cmd->opcode == O_SETDSCP && CHECK_TARG(cmd->arg1, ci))
1553
goto bad_targ;
1554
return (CHECK_ACTION);
1555
#ifdef INET6
1556
case O_IP6_SRC:
1557
case O_IP6_DST:
1558
if (cmdlen != F_INSN_SIZE(struct in6_addr) +
1559
F_INSN_SIZE(ipfw_insn))
1560
return (BAD_SIZE);
1561
break;
1562
1563
case O_FLOW6ID:
1564
if (cmdlen != F_INSN_SIZE(ipfw_insn_u32) +
1565
((ipfw_insn_u32 *)cmd)->o.arg1)
1566
return (BAD_SIZE);
1567
break;
1568
1569
case O_IP6_SRC_MASK:
1570
case O_IP6_DST_MASK:
1571
if ( !(cmdlen & 1) || cmdlen > 127)
1572
return (BAD_SIZE);
1573
break;
1574
case O_ICMP6TYPE:
1575
if( cmdlen != F_INSN_SIZE( ipfw_insn_icmp6 ) )
1576
return (BAD_SIZE);
1577
break;
1578
#endif
1579
1580
default:
1581
switch (cmd->opcode) {
1582
#ifndef INET6
1583
case O_IP6_SRC_ME:
1584
case O_IP6_DST_ME:
1585
case O_EXT_HDR:
1586
case O_IP6:
1587
case O_UNREACH6:
1588
case O_IP6_SRC:
1589
case O_IP6_DST:
1590
case O_FLOW6ID:
1591
case O_IP6_SRC_MASK:
1592
case O_IP6_DST_MASK:
1593
case O_ICMP6TYPE:
1594
printf("ipfw: no IPv6 support in kernel\n");
1595
return (FAILED);
1596
#endif
1597
default:
1598
printf("ipfw: opcode %d: unknown opcode\n",
1599
cmd->opcode);
1600
return (FAILED);
1601
}
1602
}
1603
return (SUCCESS);
1604
bad_targ:
1605
/*
1606
* For dynamic states we can not correctly initialize tablearg value,
1607
* because we don't go through rule's opcodes except rule action.
1608
*/
1609
printf("ipfw: tablearg is not allowed with dynamic states\n");
1610
return (FAILED);
1611
}
1612
1613
static __noinline int
1614
check_ipfw_rule_body(ipfw_insn *cmd, int cmd_len, struct rule_check_info *ci)
1615
{
1616
int cmdlen, l;
1617
int have_action, ret;
1618
1619
/*
1620
* Now go for the individual checks. Very simple ones, basically only
1621
* instruction sizes.
1622
*/
1623
have_action = 0;
1624
for (l = cmd_len; l > 0 ; l -= cmdlen, cmd += cmdlen) {
1625
cmdlen = F_LEN(cmd);
1626
if (cmdlen > l) {
1627
printf("ipfw: opcode %d: size truncated\n",
1628
cmd->opcode);
1629
return (EINVAL);
1630
}
1631
if (ci->version != IP_FW3_OPVER)
1632
ret = (*check_opcode_f)(&cmd, &l, ci);
1633
else
1634
ret = ipfw_check_opcode(&cmd, &l, ci);
1635
1636
if (ret == CHECK_ACTION) {
1637
if (have_action != 0) {
1638
printf("ipfw: opcode %d: multiple actions"
1639
" not allowed\n", cmd->opcode);
1640
ret = FAILED;
1641
} else
1642
have_action = 1;
1643
1644
if (l != F_LEN(cmd)) {
1645
printf("ipfw: opcode %d: action must be"
1646
" last opcode\n", cmd->opcode);
1647
ret = FAILED;
1648
}
1649
}
1650
switch (ret) {
1651
case SUCCESS:
1652
continue;
1653
case BAD_SIZE:
1654
printf("ipfw: opcode %d: wrong size %d\n",
1655
cmd->opcode, cmdlen);
1656
/* FALLTHROUGH */
1657
case FAILED:
1658
return (EINVAL);
1659
}
1660
}
1661
if (have_action == 0) {
1662
printf("ipfw: missing action\n");
1663
return (EINVAL);
1664
}
1665
return (0);
1666
}
1667
1668
struct dump_args {
1669
uint32_t b; /* start rule */
1670
uint32_t e; /* end rule */
1671
uint32_t rcount; /* number of rules */
1672
uint32_t rsize; /* rules size */
1673
uint32_t tcount; /* number of tables */
1674
int rcounters; /* counters */
1675
uint32_t *bmask; /* index bitmask of used named objects */
1676
};
1677
1678
void
1679
ipfw_export_obj_ntlv(struct named_object *no, ipfw_obj_ntlv *ntlv)
1680
{
1681
1682
ntlv->head.type = no->etlv;
1683
ntlv->head.length = sizeof(*ntlv);
1684
ntlv->idx = no->kidx;
1685
strlcpy(ntlv->name, no->name, sizeof(ntlv->name));
1686
}
1687
1688
/*
1689
* Export named object info in instance @ni, identified by @kidx
1690
* to ipfw_obj_ntlv. TLV is allocated from @sd space.
1691
*
1692
* Returns 0 on success.
1693
*/
1694
static int
1695
export_objhash_ntlv(struct namedobj_instance *ni, uint32_t kidx,
1696
struct sockopt_data *sd)
1697
{
1698
struct named_object *no;
1699
ipfw_obj_ntlv *ntlv;
1700
1701
no = ipfw_objhash_lookup_kidx(ni, kidx);
1702
KASSERT(no != NULL, ("invalid object kernel index passed"));
1703
1704
ntlv = (ipfw_obj_ntlv *)ipfw_get_sopt_space(sd, sizeof(*ntlv));
1705
if (ntlv == NULL)
1706
return (ENOMEM);
1707
1708
ipfw_export_obj_ntlv(no, ntlv);
1709
return (0);
1710
}
1711
1712
static int
1713
export_named_objects(struct namedobj_instance *ni, struct dump_args *da,
1714
struct sockopt_data *sd)
1715
{
1716
uint32_t i;
1717
int error;
1718
1719
for (i = 0; i < IPFW_TABLES_MAX && da->tcount > 0; i++) {
1720
if ((da->bmask[i / 32] & (1 << (i % 32))) == 0)
1721
continue;
1722
if ((error = export_objhash_ntlv(ni, i, sd)) != 0)
1723
return (error);
1724
da->tcount--;
1725
}
1726
return (0);
1727
}
1728
1729
static int
1730
dump_named_objects(struct ip_fw_chain *ch, struct dump_args *da,
1731
struct sockopt_data *sd)
1732
{
1733
ipfw_obj_ctlv *ctlv;
1734
int error;
1735
1736
MPASS(da->tcount > 0);
1737
/* Header first */
1738
ctlv = (ipfw_obj_ctlv *)ipfw_get_sopt_space(sd, sizeof(*ctlv));
1739
if (ctlv == NULL)
1740
return (ENOMEM);
1741
ctlv->head.type = IPFW_TLV_TBLNAME_LIST;
1742
ctlv->head.length = da->tcount * sizeof(ipfw_obj_ntlv) +
1743
sizeof(*ctlv);
1744
ctlv->count = da->tcount;
1745
ctlv->objsize = sizeof(ipfw_obj_ntlv);
1746
1747
/* Dump table names first (if any) */
1748
error = export_named_objects(ipfw_get_table_objhash(ch), da, sd);
1749
if (error != 0)
1750
return (error);
1751
/* Then dump another named objects */
1752
da->bmask += IPFW_TABLES_MAX / 32;
1753
return (export_named_objects(CHAIN_TO_SRV(ch), da, sd));
1754
}
1755
1756
/*
1757
* Dumps static rules with table TLVs in buffer @sd.
1758
*
1759
* Returns 0 on success.
1760
*/
1761
static int
1762
dump_static_rules(struct ip_fw_chain *chain, struct dump_args *da,
1763
struct sockopt_data *sd)
1764
{
1765
ipfw_obj_ctlv *ctlv;
1766
struct ip_fw *krule;
1767
caddr_t dst;
1768
int i, l;
1769
1770
/* Dump rules */
1771
ctlv = (ipfw_obj_ctlv *)ipfw_get_sopt_space(sd, sizeof(*ctlv));
1772
if (ctlv == NULL)
1773
return (ENOMEM);
1774
ctlv->head.type = IPFW_TLV_RULE_LIST;
1775
ctlv->head.length = da->rsize + sizeof(*ctlv);
1776
ctlv->count = da->rcount;
1777
1778
for (i = da->b; i < da->e; i++) {
1779
krule = chain->map[i];
1780
1781
l = RULEUSIZE1(krule) + sizeof(ipfw_obj_tlv);
1782
if (da->rcounters != 0)
1783
l += sizeof(struct ip_fw_bcounter);
1784
dst = (caddr_t)ipfw_get_sopt_space(sd, l);
1785
if (dst == NULL)
1786
return (ENOMEM);
1787
1788
export_rule1(krule, dst, l, da->rcounters);
1789
}
1790
1791
return (0);
1792
}
1793
1794
int
1795
ipfw_mark_object_kidx(uint32_t *bmask, uint16_t etlv, uint32_t kidx)
1796
{
1797
uint32_t bidx;
1798
1799
/*
1800
* Maintain separate bitmasks for table and non-table objects.
1801
*/
1802
bidx = (etlv == IPFW_TLV_TBL_NAME) ? 0: IPFW_TABLES_MAX / 32;
1803
bidx += kidx / 32;
1804
if ((bmask[bidx] & (1 << (kidx % 32))) != 0)
1805
return (0);
1806
1807
bmask[bidx] |= 1 << (kidx % 32);
1808
return (1);
1809
}
1810
1811
/*
1812
* Marks every object index used in @rule with bit in @bmask.
1813
* Used to generate bitmask of referenced tables/objects for given ruleset
1814
* or its part.
1815
*/
1816
static void
1817
mark_rule_objects(struct ip_fw_chain *ch, struct ip_fw *rule,
1818
struct dump_args *da)
1819
{
1820
struct opcode_obj_rewrite *rw;
1821
ipfw_insn *cmd;
1822
uint32_t kidx;
1823
int cmdlen, l;
1824
uint8_t subtype;
1825
1826
l = rule->cmd_len;
1827
cmd = rule->cmd;
1828
cmdlen = 0;
1829
for ( ; l > 0 ; l -= cmdlen, cmd += cmdlen) {
1830
cmdlen = F_LEN(cmd);
1831
1832
rw = find_op_rw(cmd, &kidx, &subtype);
1833
if (rw == NULL)
1834
continue;
1835
1836
if (ipfw_mark_object_kidx(da->bmask, rw->etlv, kidx))
1837
da->tcount++;
1838
}
1839
}
1840
1841
/*
1842
* Dumps requested objects data
1843
* Data layout (version 0)(current):
1844
* Request: [ ipfw_cfg_lheader ] + IPFW_CFG_GET_* flags
1845
* size = ipfw_cfg_lheader.size
1846
* Reply: [ ipfw_cfg_lheader
1847
* [ ipfw_obj_ctlv(IPFW_TLV_TBL_LIST) ipfw_obj_ntlv x N ] (optional)
1848
* [ ipfw_obj_ctlv(IPFW_TLV_RULE_LIST)
1849
* ipfw_obj_tlv(IPFW_TLV_RULE_ENT) [ ip_fw_bcounter (optional) ip_fw_rule ]
1850
* ] (optional)
1851
* [ ipfw_obj_ctlv(IPFW_TLV_STATE_LIST) ipfw_obj_dyntlv x N ] (optional)
1852
* ]
1853
* * NOTE IPFW_TLV_STATE_LIST has the single valid field: objsize.
1854
* The rest (size, count) are set to zero and needs to be ignored.
1855
*
1856
* Returns 0 on success.
1857
*/
1858
static int
1859
dump_config(struct ip_fw_chain *chain, ip_fw3_opheader *op3,
1860
struct sockopt_data *sd)
1861
{
1862
struct dump_args da;
1863
ipfw_cfg_lheader *hdr;
1864
struct ip_fw *rule;
1865
size_t sz, rnum;
1866
uint32_t hdr_flags, *bmask;
1867
int error, i;
1868
1869
hdr = (ipfw_cfg_lheader *)ipfw_get_sopt_header(sd, sizeof(*hdr));
1870
if (hdr == NULL)
1871
return (EINVAL);
1872
1873
error = 0;
1874
bmask = NULL;
1875
memset(&da, 0, sizeof(da));
1876
/*
1877
* Allocate needed state.
1878
* Note we allocate 2xspace mask, for table & srv
1879
*/
1880
if (hdr->flags & (IPFW_CFG_GET_STATIC | IPFW_CFG_GET_STATES))
1881
da.bmask = bmask = malloc(
1882
sizeof(uint32_t) * IPFW_TABLES_MAX * 2 / 32, M_TEMP,
1883
M_WAITOK | M_ZERO);
1884
IPFW_UH_RLOCK(chain);
1885
1886
/*
1887
* STAGE 1: Determine size/count for objects in range.
1888
* Prepare used tables bitmask.
1889
*/
1890
sz = sizeof(ipfw_cfg_lheader);
1891
da.e = chain->n_rules;
1892
1893
if (hdr->end_rule != 0) {
1894
/* Handle custom range */
1895
if ((rnum = hdr->start_rule) > IPFW_DEFAULT_RULE)
1896
rnum = IPFW_DEFAULT_RULE;
1897
da.b = ipfw_find_rule(chain, rnum, 0);
1898
rnum = (hdr->end_rule < IPFW_DEFAULT_RULE) ?
1899
hdr->end_rule + 1: IPFW_DEFAULT_RULE;
1900
da.e = ipfw_find_rule(chain, rnum, UINT32_MAX) + 1;
1901
}
1902
1903
if (hdr->flags & IPFW_CFG_GET_STATIC) {
1904
for (i = da.b; i < da.e; i++) {
1905
rule = chain->map[i];
1906
da.rsize += RULEUSIZE1(rule) + sizeof(ipfw_obj_tlv);
1907
da.rcount++;
1908
/* Update bitmask of used objects for given range */
1909
mark_rule_objects(chain, rule, &da);
1910
}
1911
/* Add counters if requested */
1912
if (hdr->flags & IPFW_CFG_GET_COUNTERS) {
1913
da.rsize += sizeof(struct ip_fw_bcounter) * da.rcount;
1914
da.rcounters = 1;
1915
}
1916
sz += da.rsize + sizeof(ipfw_obj_ctlv);
1917
}
1918
1919
if (hdr->flags & IPFW_CFG_GET_STATES) {
1920
sz += sizeof(ipfw_obj_ctlv) +
1921
ipfw_dyn_get_count(bmask, &i) * sizeof(ipfw_obj_dyntlv);
1922
da.tcount += i;
1923
}
1924
1925
if (da.tcount > 0)
1926
sz += da.tcount * sizeof(ipfw_obj_ntlv) +
1927
sizeof(ipfw_obj_ctlv);
1928
1929
/*
1930
* Fill header anyway.
1931
* Note we have to save header fields to stable storage
1932
* buffer inside @sd can be flushed after dumping rules
1933
*/
1934
hdr->size = sz;
1935
hdr->set_mask = ~V_set_disable;
1936
hdr_flags = hdr->flags;
1937
hdr = NULL;
1938
1939
if (sd->valsize < sz) {
1940
error = ENOMEM;
1941
goto cleanup;
1942
}
1943
1944
/* STAGE2: Store actual data */
1945
if (da.tcount > 0) {
1946
error = dump_named_objects(chain, &da, sd);
1947
if (error != 0)
1948
goto cleanup;
1949
}
1950
1951
if (hdr_flags & IPFW_CFG_GET_STATIC) {
1952
error = dump_static_rules(chain, &da, sd);
1953
if (error != 0)
1954
goto cleanup;
1955
}
1956
1957
if (hdr_flags & IPFW_CFG_GET_STATES)
1958
error = ipfw_dump_states(chain, sd);
1959
1960
cleanup:
1961
IPFW_UH_RUNLOCK(chain);
1962
1963
if (bmask != NULL)
1964
free(bmask, M_TEMP);
1965
1966
return (error);
1967
}
1968
1969
int
1970
ipfw_check_object_name_generic(const char *name)
1971
{
1972
int nsize;
1973
1974
nsize = sizeof(((ipfw_obj_ntlv *)0)->name);
1975
if (strnlen(name, nsize) == nsize)
1976
return (EINVAL);
1977
if (name[0] == '\0')
1978
return (EINVAL);
1979
return (0);
1980
}
1981
1982
/*
1983
* Creates non-existent objects referenced by rule.
1984
*
1985
* Return 0 on success.
1986
*/
1987
static int
1988
create_objects_compat(struct ip_fw_chain *ch, ipfw_insn *cmd,
1989
struct obj_idx *oib, struct obj_idx *pidx, struct tid_info *ti)
1990
{
1991
struct opcode_obj_rewrite *rw;
1992
struct obj_idx *p;
1993
uint32_t kidx;
1994
int error;
1995
1996
/*
1997
* Compatibility stuff: do actual creation for non-existing,
1998
* but referenced objects.
1999
*/
2000
for (p = oib; p < pidx; p++) {
2001
if (p->kidx != 0)
2002
continue;
2003
2004
ti->uidx = p->uidx;
2005
ti->type = p->type;
2006
ti->atype = 0;
2007
2008
rw = find_op_rw(cmd + p->off, NULL, NULL);
2009
KASSERT(rw != NULL, ("Unable to find handler for op %d",
2010
(cmd + p->off)->opcode));
2011
2012
if (rw->create_object == NULL)
2013
error = EOPNOTSUPP;
2014
else
2015
error = rw->create_object(ch, ti, &kidx);
2016
if (error == 0) {
2017
p->kidx = kidx;
2018
continue;
2019
}
2020
2021
/*
2022
* Error happened. We have to rollback everything.
2023
* Drop all already acquired references.
2024
*/
2025
IPFW_UH_WLOCK(ch);
2026
unref_oib_objects(ch, cmd, oib, pidx);
2027
IPFW_UH_WUNLOCK(ch);
2028
2029
return (error);
2030
}
2031
2032
return (0);
2033
}
2034
2035
/*
2036
* Unreferences all already-referenced objects in given @cmd rule,
2037
* using information in @oib.
2038
*
2039
* Used to rollback partially converted rule on error.
2040
*/
2041
static void
2042
unref_oib_objects(struct ip_fw_chain *ch, ipfw_insn *cmd, struct obj_idx *oib,
2043
struct obj_idx *end)
2044
{
2045
struct opcode_obj_rewrite *rw;
2046
struct named_object *no;
2047
struct obj_idx *p;
2048
2049
IPFW_UH_WLOCK_ASSERT(ch);
2050
2051
for (p = oib; p < end; p++) {
2052
if (p->kidx == 0)
2053
continue;
2054
2055
rw = find_op_rw(cmd + p->off, NULL, NULL);
2056
KASSERT(rw != NULL, ("Unable to find handler for op %d",
2057
(cmd + p->off)->opcode));
2058
2059
/* Find & unref by existing idx */
2060
no = rw->find_bykidx(ch, p->kidx);
2061
KASSERT(no != NULL, ("Ref'd object %d disappeared", p->kidx));
2062
no->refcnt--;
2063
}
2064
}
2065
2066
/*
2067
* Remove references from every object used in @rule.
2068
* Used at rule removal code.
2069
*/
2070
static void
2071
unref_rule_objects(struct ip_fw_chain *ch, struct ip_fw *rule)
2072
{
2073
struct opcode_obj_rewrite *rw;
2074
struct named_object *no;
2075
ipfw_insn *cmd;
2076
uint32_t kidx;
2077
int cmdlen, l;
2078
uint8_t subtype;
2079
2080
IPFW_UH_WLOCK_ASSERT(ch);
2081
2082
l = rule->cmd_len;
2083
cmd = rule->cmd;
2084
cmdlen = 0;
2085
for ( ; l > 0 ; l -= cmdlen, cmd += cmdlen) {
2086
cmdlen = F_LEN(cmd);
2087
2088
rw = find_op_rw(cmd, &kidx, &subtype);
2089
if (rw == NULL)
2090
continue;
2091
no = rw->find_bykidx(ch, kidx);
2092
2093
KASSERT(no != NULL, ("object id %d not found", kidx));
2094
KASSERT(no->subtype == subtype,
2095
("wrong type %d (%d) for object id %d",
2096
no->subtype, subtype, kidx));
2097
KASSERT(no->refcnt > 0, ("refcount for object %d is %d",
2098
kidx, no->refcnt));
2099
2100
if (no->refcnt == 1 && rw->destroy_object != NULL)
2101
rw->destroy_object(ch, no);
2102
else
2103
no->refcnt--;
2104
}
2105
if (ACTION_PTR(rule)->opcode == O_LOG)
2106
ipfw_tap_free(ch, rule->rulenum);
2107
}
2108
2109
/*
2110
* Find and reference object (if any) stored in instruction @cmd.
2111
*
2112
* Saves object info in @pidx, sets
2113
* - @unresolved to 1 if object should exists but not found
2114
*
2115
* Returns non-zero value in case of error.
2116
*/
2117
static int
2118
ref_opcode_object(struct ip_fw_chain *ch, ipfw_insn *cmd, struct tid_info *ti,
2119
struct obj_idx *pidx, int *unresolved)
2120
{
2121
struct named_object *no;
2122
struct opcode_obj_rewrite *rw;
2123
int error;
2124
2125
/* Check if this opcode is candidate for rewrite */
2126
rw = find_op_rw(cmd, &ti->uidx, &ti->type);
2127
if (rw == NULL)
2128
return (0);
2129
2130
/* Need to rewrite. Save necessary fields */
2131
pidx->uidx = ti->uidx;
2132
pidx->type = ti->type;
2133
2134
/* Try to find referenced kernel object */
2135
error = rw->find_byname(ch, ti, &no);
2136
if (error != 0)
2137
return (error);
2138
if (no == NULL) {
2139
/*
2140
* Report about unresolved object for automaic
2141
* creation.
2142
*/
2143
*unresolved = 1;
2144
return (0);
2145
}
2146
2147
/*
2148
* Object is already exist.
2149
* Its subtype should match with expected value.
2150
*/
2151
if (ti->type != no->subtype)
2152
return (EINVAL);
2153
2154
/* Bump refcount and update kidx. */
2155
no->refcnt++;
2156
rw->update(cmd, no->kidx);
2157
return (0);
2158
}
2159
2160
/*
2161
* Finds and bumps refcount for objects referenced by given @rule.
2162
* Auto-creates non-existing tables.
2163
* Fills in @oib array with userland/kernel indexes.
2164
*
2165
* Returns 0 on success.
2166
*/
2167
static int
2168
ref_rule_objects(struct ip_fw_chain *ch, struct ip_fw *rule,
2169
struct rule_check_info *ci, struct obj_idx *oib, struct tid_info *ti)
2170
{
2171
struct obj_idx *pidx;
2172
ipfw_insn *cmd;
2173
int cmdlen, error, l, unresolved;
2174
2175
pidx = oib;
2176
l = rule->cmd_len;
2177
cmd = rule->cmd;
2178
cmdlen = 0;
2179
error = 0;
2180
2181
IPFW_UH_WLOCK_ASSERT(ch);
2182
2183
/* Increase refcount on each existing referenced table. */
2184
for ( ; l > 0 ; l -= cmdlen, cmd += cmdlen) {
2185
cmdlen = F_LEN(cmd);
2186
unresolved = 0;
2187
2188
error = ref_opcode_object(ch, cmd, ti, pidx, &unresolved);
2189
if (error != 0)
2190
break;
2191
/*
2192
* Compatibility stuff for old clients:
2193
* prepare to automaitcally create non-existing objects.
2194
*/
2195
if (unresolved != 0) {
2196
pidx->off = rule->cmd_len - l;
2197
pidx++;
2198
}
2199
}
2200
2201
if (error != 0) {
2202
/* Unref everything we have already done */
2203
unref_oib_objects(ch, rule->cmd, oib, pidx);
2204
return (error);
2205
}
2206
2207
/* Perform auto-creation for non-existing objects */
2208
if (pidx != oib)
2209
error = create_objects_compat(ch, rule->cmd, oib, pidx, ti);
2210
2211
/* Calculate real number of dynamic objects */
2212
ci->object_opcodes = (uint16_t)(pidx - oib);
2213
2214
return (error);
2215
}
2216
2217
/*
2218
* Checks is opcode is referencing table of appropriate type.
2219
* Adds reference count for found table if true.
2220
* Rewrites user-supplied opcode values with kernel ones.
2221
*
2222
* Returns 0 on success and appropriate error code otherwise.
2223
*/
2224
static int
2225
rewrite_rule_uidx(struct ip_fw_chain *chain, struct rule_check_info *ci)
2226
{
2227
int error;
2228
ipfw_insn *cmd;
2229
struct obj_idx *p, *pidx_first, *pidx_last;
2230
struct tid_info ti;
2231
2232
/*
2233
* Prepare an array for storing opcode indices.
2234
* Use stack allocation by default.
2235
*/
2236
if (ci->object_opcodes <= (sizeof(ci->obuf)/sizeof(ci->obuf[0]))) {
2237
/* Stack */
2238
pidx_first = ci->obuf;
2239
} else
2240
pidx_first = malloc(
2241
ci->object_opcodes * sizeof(struct obj_idx),
2242
M_IPFW, M_WAITOK | M_ZERO);
2243
2244
error = 0;
2245
memset(&ti, 0, sizeof(ti));
2246
2247
/* Use set rule is assigned to. */
2248
ti.set = ci->krule->set;
2249
if (ci->ctlv != NULL) {
2250
ti.tlvs = (void *)(ci->ctlv + 1);
2251
ti.tlen = ci->ctlv->head.length - sizeof(ipfw_obj_ctlv);
2252
}
2253
2254
/* Reference all used tables and other objects */
2255
error = ref_rule_objects(chain, ci->krule, ci, pidx_first, &ti);
2256
if (error != 0)
2257
goto free;
2258
/*
2259
* Note that ref_rule_objects() might have updated ci->object_opcodes
2260
* to reflect actual number of object opcodes.
2261
*/
2262
2263
/* Perform rewrite of remaining opcodes */
2264
p = pidx_first;
2265
pidx_last = pidx_first + ci->object_opcodes;
2266
for (p = pidx_first; p < pidx_last; p++) {
2267
cmd = ci->krule->cmd + p->off;
2268
update_opcode_kidx(cmd, p->kidx);
2269
}
2270
2271
free:
2272
if (pidx_first != ci->obuf)
2273
free(pidx_first, M_IPFW);
2274
2275
return (error);
2276
}
2277
2278
/*
2279
* Parses one or more rules from userland.
2280
* Data layout (version 1)(current):
2281
* Request:
2282
* [
2283
* ip_fw3_opheader
2284
* [ ipfw_obj_ctlv(IPFW_TLV_TBL_LIST) ipfw_obj_ntlv x N ] (optional *1)
2285
* [ ipfw_obj_ctlv(IPFW_TLV_RULE_LIST) ip_fw x N ] (*2) (*3)
2286
* ]
2287
* Reply:
2288
* [
2289
* ip_fw3_opheader
2290
* [ ipfw_obj_ctlv(IPFW_TLV_TBL_LIST) ipfw_obj_ntlv x N ] (optional)
2291
* [ ipfw_obj_ctlv(IPFW_TLV_RULE_LIST) ip_fw x N ]
2292
* ]
2293
*
2294
* Rules in reply are modified to store their actual ruleset number.
2295
*
2296
* (*1) TLVs inside IPFW_TLV_TBL_LIST needs to be sorted ascending
2297
* according to their idx field and there has to be no duplicates.
2298
* (*2) Numbered rules inside IPFW_TLV_RULE_LIST needs to be sorted ascending.
2299
* (*3) Each ip_fw structure needs to be aligned to u64 boundary.
2300
*
2301
* Returns 0 on success.
2302
*/
2303
static __noinline int
2304
parse_rules_v1(struct ip_fw_chain *chain, ip_fw3_opheader *op3,
2305
struct sockopt_data *sd, ipfw_obj_ctlv **prtlv,
2306
struct rule_check_info **pci)
2307
{
2308
ipfw_obj_ctlv *ctlv, *rtlv, *tstate;
2309
ipfw_obj_ntlv *ntlv;
2310
struct rule_check_info *ci, *cbuf;
2311
struct ip_fw_rule *r;
2312
size_t count, clen, read, rsize;
2313
uint32_t idx, rulenum;
2314
int error;
2315
2316
op3 = (ip_fw3_opheader *)ipfw_get_sopt_space(sd, sd->valsize);
2317
ctlv = (ipfw_obj_ctlv *)(op3 + 1);
2318
read = sizeof(ip_fw3_opheader);
2319
if (read + sizeof(*ctlv) > sd->valsize)
2320
return (EINVAL);
2321
2322
rtlv = NULL;
2323
tstate = NULL;
2324
cbuf = NULL;
2325
/* Table names or other named objects. */
2326
if (ctlv->head.type == IPFW_TLV_TBLNAME_LIST) {
2327
/* Check size and alignment. */
2328
clen = ctlv->head.length;
2329
if (read + clen > sd->valsize || clen < sizeof(*ctlv) ||
2330
(clen % sizeof(uint64_t)) != 0)
2331
return (EINVAL);
2332
/* Check for validness. */
2333
count = (ctlv->head.length - sizeof(*ctlv)) / sizeof(*ntlv);
2334
if (ctlv->count != count || ctlv->objsize != sizeof(*ntlv))
2335
return (EINVAL);
2336
/*
2337
* Check each TLV.
2338
* Ensure TLVs are sorted ascending and
2339
* there are no duplicates.
2340
*/
2341
idx = 0;
2342
ntlv = (ipfw_obj_ntlv *)(ctlv + 1);
2343
while (count > 0) {
2344
if (ntlv->head.length != sizeof(ipfw_obj_ntlv))
2345
return (EINVAL);
2346
2347
error = ipfw_check_object_name_generic(ntlv->name);
2348
if (error != 0)
2349
return (error);
2350
2351
if (ntlv->idx <= idx)
2352
return (EINVAL);
2353
2354
idx = ntlv->idx;
2355
count--;
2356
ntlv++;
2357
}
2358
2359
tstate = ctlv;
2360
read += ctlv->head.length;
2361
ctlv = (ipfw_obj_ctlv *)((caddr_t)ctlv + ctlv->head.length);
2362
2363
if (read + sizeof(*ctlv) > sd->valsize)
2364
return (EINVAL);
2365
}
2366
2367
/* List of rules. */
2368
if (ctlv->head.type == IPFW_TLV_RULE_LIST) {
2369
clen = ctlv->head.length;
2370
if (read + clen > sd->valsize || clen < sizeof(*ctlv) ||
2371
(clen % sizeof(uint64_t)) != 0)
2372
return (EINVAL);
2373
2374
clen -= sizeof(*ctlv);
2375
if (ctlv->count == 0 ||
2376
ctlv->count > clen / sizeof(struct ip_fw_rule))
2377
return (EINVAL);
2378
2379
/* Allocate state for each rule */
2380
cbuf = malloc(ctlv->count * sizeof(struct rule_check_info),
2381
M_TEMP, M_WAITOK | M_ZERO);
2382
2383
/*
2384
* Check each rule for validness.
2385
* Ensure numbered rules are sorted ascending
2386
* and properly aligned
2387
*/
2388
rulenum = 0;
2389
count = 0;
2390
error = 0;
2391
ci = cbuf;
2392
r = (struct ip_fw_rule *)(ctlv + 1);
2393
while (clen > 0) {
2394
rsize = RULEUSIZE1(r);
2395
if (rsize > clen || count > ctlv->count) {
2396
error = EINVAL;
2397
break;
2398
}
2399
ci->ctlv = tstate;
2400
ci->version = IP_FW3_OPVER;
2401
error = ipfw_check_rule(r, rsize, ci);
2402
if (error != 0)
2403
break;
2404
2405
/* Check sorting */
2406
if (count != 0 && ((rulenum == 0) != (r->rulenum == 0) ||
2407
r->rulenum < rulenum)) {
2408
printf("ipfw: wrong order: rulenum %u"
2409
" vs %u\n", r->rulenum, rulenum);
2410
error = EINVAL;
2411
break;
2412
}
2413
rulenum = r->rulenum;
2414
ci->urule = (caddr_t)r;
2415
clen -= rsize;
2416
r = (struct ip_fw_rule *)((caddr_t)r + rsize);
2417
count++;
2418
ci++;
2419
}
2420
2421
if (ctlv->count != count || error != 0) {
2422
free(cbuf, M_TEMP);
2423
return (EINVAL);
2424
}
2425
2426
rtlv = ctlv;
2427
read += ctlv->head.length;
2428
ctlv = (ipfw_obj_ctlv *)((caddr_t)ctlv + ctlv->head.length);
2429
}
2430
2431
if (read != sd->valsize || rtlv == NULL) {
2432
free(cbuf, M_TEMP);
2433
return (EINVAL);
2434
}
2435
2436
*prtlv = rtlv;
2437
*pci = cbuf;
2438
return (0);
2439
}
2440
2441
/*
2442
* Copy rule @urule from v1 userland format (current) to kernel @krule.
2443
*/
2444
static void
2445
import_rule_v1(struct ip_fw_chain *chain, struct rule_check_info *ci)
2446
{
2447
struct ip_fw_rule *urule;
2448
struct ip_fw *krule;
2449
2450
urule = (struct ip_fw_rule *)ci->urule;
2451
krule = ci->krule = ipfw_alloc_rule(chain, RULEKSIZE1(urule));
2452
2453
krule->act_ofs = urule->act_ofs;
2454
krule->cmd_len = urule->cmd_len;
2455
krule->rulenum = urule->rulenum;
2456
krule->set = urule->set;
2457
krule->flags = urule->flags;
2458
2459
/* Save rulenum offset */
2460
ci->urule_numoff = offsetof(struct ip_fw_rule, rulenum);
2461
2462
/* Copy opcodes */
2463
memcpy(krule->cmd, urule->cmd, krule->cmd_len * sizeof(uint32_t));
2464
}
2465
2466
/*
2467
* Adds one or more rules to ipfw @chain.
2468
*/
2469
static int
2470
add_rules(struct ip_fw_chain *chain, ip_fw3_opheader *op3,
2471
struct sockopt_data *sd)
2472
{
2473
ipfw_obj_ctlv *rtlv;
2474
struct rule_check_info *ci, *nci;
2475
int i, ret;
2476
2477
/*
2478
* Check rules buffer for validness.
2479
*/
2480
ret = parse_rules_v1(chain, op3, sd, &rtlv, &nci);
2481
if (ret != 0)
2482
return (ret);
2483
/*
2484
* Allocate storage for the kernel representation of rules.
2485
*/
2486
for (i = 0, ci = nci; i < rtlv->count; i++, ci++)
2487
import_rule_v1(chain, ci);
2488
/*
2489
* Try to add new rules to the chain.
2490
*/
2491
if ((ret = ipfw_commit_rules(chain, nci, rtlv->count)) != 0) {
2492
for (i = 0, ci = nci; i < rtlv->count; i++, ci++)
2493
ipfw_free_rule(ci->krule);
2494
}
2495
/* Cleanup after parse_rules() */
2496
free(nci, M_TEMP);
2497
return (ret);
2498
}
2499
2500
/*
2501
* Lists all sopts currently registered.
2502
* Data layout (v1)(current):
2503
* Request: [ ipfw_obj_lheader ], size = ipfw_obj_lheader.size
2504
* Reply: [ ipfw_obj_lheader ipfw_sopt_info x N ]
2505
*
2506
* Returns 0 on success
2507
*/
2508
static int
2509
dump_soptcodes(struct ip_fw_chain *chain, ip_fw3_opheader *op3,
2510
struct sockopt_data *sd)
2511
{
2512
struct _ipfw_obj_lheader *olh;
2513
ipfw_sopt_info *i;
2514
struct ipfw_sopt_handler *sh;
2515
uint32_t count, n, size;
2516
2517
olh = (struct _ipfw_obj_lheader *)ipfw_get_sopt_header(sd,
2518
sizeof(*olh));
2519
if (olh == NULL)
2520
return (EINVAL);
2521
if (sd->valsize < olh->size)
2522
return (EINVAL);
2523
2524
CTL3_LOCK();
2525
count = ctl3_hsize;
2526
size = count * sizeof(ipfw_sopt_info) + sizeof(ipfw_obj_lheader);
2527
2528
/* Fill in header regadless of buffer size */
2529
olh->count = count;
2530
olh->objsize = sizeof(ipfw_sopt_info);
2531
2532
if (size > olh->size) {
2533
olh->size = size;
2534
CTL3_UNLOCK();
2535
return (ENOMEM);
2536
}
2537
olh->size = size;
2538
2539
for (n = 0; n < count; n++) {
2540
i = (ipfw_sopt_info *)ipfw_get_sopt_space(sd, sizeof(*i));
2541
KASSERT(i != NULL, ("previously checked buffer is not enough"));
2542
sh = &ctl3_handlers[n];
2543
i->opcode = sh->opcode;
2544
i->version = sh->version;
2545
i->refcnt = sh->refcnt;
2546
}
2547
CTL3_UNLOCK();
2548
2549
return (0);
2550
}
2551
2552
/*
2553
* Compares two opcodes.
2554
* Used both in qsort() and bsearch().
2555
*
2556
* Returns 0 if match is found.
2557
*/
2558
static int
2559
compare_opcodes(const void *_a, const void *_b)
2560
{
2561
const struct opcode_obj_rewrite *a, *b;
2562
2563
a = (const struct opcode_obj_rewrite *)_a;
2564
b = (const struct opcode_obj_rewrite *)_b;
2565
2566
if (a->opcode < b->opcode)
2567
return (-1);
2568
else if (a->opcode > b->opcode)
2569
return (1);
2570
2571
return (0);
2572
}
2573
2574
/*
2575
* XXX: Rewrite bsearch()
2576
*/
2577
static int
2578
find_op_rw_range(uint16_t op, struct opcode_obj_rewrite **plo,
2579
struct opcode_obj_rewrite **phi)
2580
{
2581
struct opcode_obj_rewrite *ctl3_max, *lo, *hi, h, *rw;
2582
2583
memset(&h, 0, sizeof(h));
2584
h.opcode = op;
2585
2586
rw = (struct opcode_obj_rewrite *)bsearch(&h, ctl3_rewriters,
2587
ctl3_rsize, sizeof(h), compare_opcodes);
2588
if (rw == NULL)
2589
return (1);
2590
2591
/* Find the first element matching the same opcode */
2592
lo = rw;
2593
for ( ; lo > ctl3_rewriters && (lo - 1)->opcode == op; lo--)
2594
;
2595
2596
/* Find the last element matching the same opcode */
2597
hi = rw;
2598
ctl3_max = ctl3_rewriters + ctl3_rsize;
2599
for ( ; (hi + 1) < ctl3_max && (hi + 1)->opcode == op; hi++)
2600
;
2601
2602
*plo = lo;
2603
*phi = hi;
2604
2605
return (0);
2606
}
2607
2608
/*
2609
* Finds opcode object rewriter based on @code.
2610
*
2611
* Returns pointer to handler or NULL.
2612
*/
2613
static struct opcode_obj_rewrite *
2614
find_op_rw(ipfw_insn *cmd, uint32_t *puidx, uint8_t *ptype)
2615
{
2616
struct opcode_obj_rewrite *rw, *lo, *hi;
2617
uint32_t uidx;
2618
uint8_t subtype;
2619
2620
if (find_op_rw_range(cmd->opcode, &lo, &hi) != 0)
2621
return (NULL);
2622
2623
for (rw = lo; rw <= hi; rw++) {
2624
if (rw->classifier(cmd, &uidx, &subtype) == 0) {
2625
if (puidx != NULL)
2626
*puidx = uidx;
2627
if (ptype != NULL)
2628
*ptype = subtype;
2629
return (rw);
2630
}
2631
}
2632
2633
return (NULL);
2634
}
2635
int
2636
classify_opcode_kidx(ipfw_insn *cmd, uint32_t *puidx)
2637
{
2638
2639
if (find_op_rw(cmd, puidx, NULL) == NULL)
2640
return (1);
2641
return (0);
2642
}
2643
2644
void
2645
update_opcode_kidx(ipfw_insn *cmd, uint32_t idx)
2646
{
2647
struct opcode_obj_rewrite *rw;
2648
2649
rw = find_op_rw(cmd, NULL, NULL);
2650
KASSERT(rw != NULL, ("No handler to update opcode %d", cmd->opcode));
2651
rw->update(cmd, idx);
2652
}
2653
2654
void
2655
ipfw_init_obj_rewriter(void)
2656
{
2657
ctl3_rewriters = NULL;
2658
ctl3_rsize = 0;
2659
}
2660
2661
void
2662
ipfw_destroy_obj_rewriter(void)
2663
{
2664
if (ctl3_rewriters != NULL)
2665
free(ctl3_rewriters, M_IPFW);
2666
ctl3_rewriters = NULL;
2667
ctl3_rsize = 0;
2668
}
2669
2670
/*
2671
* Adds one or more opcode object rewrite handlers to the global array.
2672
* Function may sleep.
2673
*/
2674
void
2675
ipfw_add_obj_rewriter(struct opcode_obj_rewrite *rw, size_t count)
2676
{
2677
size_t sz;
2678
struct opcode_obj_rewrite *tmp;
2679
2680
CTL3_LOCK();
2681
2682
for (;;) {
2683
sz = ctl3_rsize + count;
2684
CTL3_UNLOCK();
2685
tmp = malloc(sizeof(*rw) * sz, M_IPFW, M_WAITOK | M_ZERO);
2686
CTL3_LOCK();
2687
if (ctl3_rsize + count <= sz)
2688
break;
2689
2690
/* Retry */
2691
free(tmp, M_IPFW);
2692
}
2693
2694
/* Merge old & new arrays */
2695
sz = ctl3_rsize + count;
2696
memcpy(tmp, ctl3_rewriters, ctl3_rsize * sizeof(*rw));
2697
memcpy(&tmp[ctl3_rsize], rw, count * sizeof(*rw));
2698
qsort(tmp, sz, sizeof(*rw), compare_opcodes);
2699
/* Switch new and free old */
2700
if (ctl3_rewriters != NULL)
2701
free(ctl3_rewriters, M_IPFW);
2702
ctl3_rewriters = tmp;
2703
ctl3_rsize = sz;
2704
2705
CTL3_UNLOCK();
2706
}
2707
2708
/*
2709
* Removes one or more object rewrite handlers from the global array.
2710
*/
2711
int
2712
ipfw_del_obj_rewriter(struct opcode_obj_rewrite *rw, size_t count)
2713
{
2714
size_t sz;
2715
struct opcode_obj_rewrite *ctl3_max, *ktmp, *lo, *hi;
2716
int i;
2717
2718
CTL3_LOCK();
2719
2720
for (i = 0; i < count; i++) {
2721
if (find_op_rw_range(rw[i].opcode, &lo, &hi) != 0)
2722
continue;
2723
2724
for (ktmp = lo; ktmp <= hi; ktmp++) {
2725
if (ktmp->classifier != rw[i].classifier)
2726
continue;
2727
2728
ctl3_max = ctl3_rewriters + ctl3_rsize;
2729
sz = (ctl3_max - (ktmp + 1)) * sizeof(*ktmp);
2730
memmove(ktmp, ktmp + 1, sz);
2731
ctl3_rsize--;
2732
break;
2733
}
2734
}
2735
2736
if (ctl3_rsize == 0) {
2737
if (ctl3_rewriters != NULL)
2738
free(ctl3_rewriters, M_IPFW);
2739
ctl3_rewriters = NULL;
2740
}
2741
2742
CTL3_UNLOCK();
2743
2744
return (0);
2745
}
2746
2747
static int
2748
export_objhash_ntlv_internal(struct namedobj_instance *ni,
2749
struct named_object *no, void *arg)
2750
{
2751
struct sockopt_data *sd;
2752
ipfw_obj_ntlv *ntlv;
2753
2754
sd = (struct sockopt_data *)arg;
2755
ntlv = (ipfw_obj_ntlv *)ipfw_get_sopt_space(sd, sizeof(*ntlv));
2756
if (ntlv == NULL)
2757
return (ENOMEM);
2758
ipfw_export_obj_ntlv(no, ntlv);
2759
return (0);
2760
}
2761
2762
/*
2763
* Lists all service objects.
2764
* Data layout (v0)(current):
2765
* Request: [ ipfw_obj_lheader ] size = ipfw_obj_lheader.size
2766
* Reply: [ ipfw_obj_lheader [ ipfw_obj_ntlv x N ] (optional) ]
2767
* Returns 0 on success
2768
*/
2769
static int
2770
dump_srvobjects(struct ip_fw_chain *chain, ip_fw3_opheader *op3,
2771
struct sockopt_data *sd)
2772
{
2773
ipfw_obj_lheader *hdr;
2774
int count;
2775
2776
hdr = (ipfw_obj_lheader *)ipfw_get_sopt_header(sd, sizeof(*hdr));
2777
if (hdr == NULL)
2778
return (EINVAL);
2779
2780
IPFW_UH_RLOCK(chain);
2781
count = ipfw_objhash_count(CHAIN_TO_SRV(chain));
2782
hdr->size = sizeof(ipfw_obj_lheader) + count * sizeof(ipfw_obj_ntlv);
2783
if (sd->valsize < hdr->size) {
2784
IPFW_UH_RUNLOCK(chain);
2785
return (ENOMEM);
2786
}
2787
hdr->count = count;
2788
hdr->objsize = sizeof(ipfw_obj_ntlv);
2789
if (count > 0)
2790
ipfw_objhash_foreach(CHAIN_TO_SRV(chain),
2791
export_objhash_ntlv_internal, sd);
2792
IPFW_UH_RUNLOCK(chain);
2793
return (0);
2794
}
2795
2796
void
2797
ipfw_enable_skipto_cache(struct ip_fw_chain *chain)
2798
{
2799
2800
IPFW_UH_WLOCK_ASSERT(chain);
2801
update_skipto_cache(chain, chain->map);
2802
2803
IPFW_WLOCK(chain);
2804
swap_skipto_cache(chain);
2805
V_skipto_cache = 1;
2806
IPFW_WUNLOCK(chain);
2807
}
2808
2809
/*
2810
* Enables or disable skipto cache.
2811
* Request: [ ipfw_cmd_header ] size = ipfw_cmd_header.size
2812
* Reply: [ ipfw_cmd_header ]
2813
* Returns 0 on success
2814
*/
2815
static int
2816
manage_skiptocache(struct ip_fw_chain *chain, ip_fw3_opheader *op3,
2817
struct sockopt_data *sd)
2818
{
2819
ipfw_cmd_header *hdr;
2820
2821
if (sd->valsize != sizeof(*hdr))
2822
return (EINVAL);
2823
2824
hdr = (ipfw_cmd_header *)ipfw_get_sopt_space(sd, sd->valsize);
2825
if (hdr->cmd != SKIPTO_CACHE_DISABLE &&
2826
hdr->cmd != SKIPTO_CACHE_ENABLE)
2827
return (EOPNOTSUPP);
2828
2829
IPFW_UH_WLOCK(chain);
2830
if (hdr->cmd != V_skipto_cache) {
2831
if (hdr->cmd == SKIPTO_CACHE_ENABLE)
2832
ipfw_enable_skipto_cache(chain);
2833
V_skipto_cache = hdr->cmd;
2834
}
2835
IPFW_UH_WUNLOCK(chain);
2836
return (0);
2837
}
2838
2839
/*
2840
* Compares two sopt handlers (code, version and handler ptr).
2841
* Used both as qsort() and bsearch().
2842
* Does not compare handler for latter case.
2843
*
2844
* Returns 0 if match is found.
2845
*/
2846
static int
2847
compare_sh(const void *_a, const void *_b)
2848
{
2849
const struct ipfw_sopt_handler *a, *b;
2850
2851
a = (const struct ipfw_sopt_handler *)_a;
2852
b = (const struct ipfw_sopt_handler *)_b;
2853
2854
if (a->opcode < b->opcode)
2855
return (-1);
2856
else if (a->opcode > b->opcode)
2857
return (1);
2858
2859
if (a->version < b->version)
2860
return (-1);
2861
else if (a->version > b->version)
2862
return (1);
2863
2864
/* bsearch helper */
2865
if (a->handler == NULL)
2866
return (0);
2867
2868
if ((uintptr_t)a->handler < (uintptr_t)b->handler)
2869
return (-1);
2870
else if ((uintptr_t)a->handler > (uintptr_t)b->handler)
2871
return (1);
2872
2873
return (0);
2874
}
2875
2876
/*
2877
* Finds sopt handler based on @code and @version.
2878
*
2879
* Returns pointer to handler or NULL.
2880
*/
2881
static struct ipfw_sopt_handler *
2882
find_sh(uint16_t code, uint8_t version, sopt_handler_f *handler)
2883
{
2884
struct ipfw_sopt_handler *sh, h;
2885
2886
memset(&h, 0, sizeof(h));
2887
h.opcode = code;
2888
h.version = version;
2889
h.handler = handler;
2890
2891
sh = (struct ipfw_sopt_handler *)bsearch(&h, ctl3_handlers,
2892
ctl3_hsize, sizeof(h), compare_sh);
2893
2894
return (sh);
2895
}
2896
2897
static int
2898
find_ref_sh(uint16_t opcode, uint8_t version, struct ipfw_sopt_handler *psh)
2899
{
2900
struct ipfw_sopt_handler *sh;
2901
2902
CTL3_LOCK();
2903
if ((sh = find_sh(opcode, version, NULL)) == NULL) {
2904
CTL3_UNLOCK();
2905
printf("ipfw: ipfw_ctl3 invalid option %d""v""%d\n",
2906
opcode, version);
2907
return (EINVAL);
2908
}
2909
sh->refcnt++;
2910
ctl3_refct++;
2911
/* Copy handler data to requested buffer */
2912
*psh = *sh;
2913
CTL3_UNLOCK();
2914
2915
return (0);
2916
}
2917
2918
static void
2919
find_unref_sh(struct ipfw_sopt_handler *psh)
2920
{
2921
struct ipfw_sopt_handler *sh;
2922
2923
CTL3_LOCK();
2924
sh = find_sh(psh->opcode, psh->version, NULL);
2925
KASSERT(sh != NULL, ("ctl3 handler disappeared"));
2926
sh->refcnt--;
2927
ctl3_refct--;
2928
CTL3_UNLOCK();
2929
}
2930
2931
void
2932
ipfw_init_sopt_handler(void)
2933
{
2934
CTL3_LOCK_INIT();
2935
IPFW_ADD_SOPT_HANDLER(1, scodes);
2936
}
2937
2938
void
2939
ipfw_destroy_sopt_handler(void)
2940
{
2941
IPFW_DEL_SOPT_HANDLER(1, scodes);
2942
CTL3_LOCK_DESTROY();
2943
}
2944
2945
void
2946
ipfw_register_compat(ipfw_check_opcode_t f)
2947
{
2948
check_opcode_f = f;
2949
}
2950
2951
void
2952
ipfw_unregister_compat(void)
2953
{
2954
check_opcode_f = check_opcode_compat_nop;
2955
}
2956
2957
/*
2958
* Adds one or more sockopt handlers to the global array.
2959
* Function may sleep.
2960
*/
2961
void
2962
ipfw_add_sopt_handler(struct ipfw_sopt_handler *sh, size_t count)
2963
{
2964
size_t sz;
2965
struct ipfw_sopt_handler *tmp;
2966
2967
CTL3_LOCK();
2968
2969
for (;;) {
2970
sz = ctl3_hsize + count;
2971
CTL3_UNLOCK();
2972
tmp = malloc(sizeof(*sh) * sz, M_IPFW, M_WAITOK | M_ZERO);
2973
CTL3_LOCK();
2974
if (ctl3_hsize + count <= sz)
2975
break;
2976
2977
/* Retry */
2978
free(tmp, M_IPFW);
2979
}
2980
2981
/* Merge old & new arrays */
2982
sz = ctl3_hsize + count;
2983
memcpy(tmp, ctl3_handlers, ctl3_hsize * sizeof(*sh));
2984
memcpy(&tmp[ctl3_hsize], sh, count * sizeof(*sh));
2985
qsort(tmp, sz, sizeof(*sh), compare_sh);
2986
/* Switch new and free old */
2987
if (ctl3_handlers != NULL)
2988
free(ctl3_handlers, M_IPFW);
2989
ctl3_handlers = tmp;
2990
ctl3_hsize = sz;
2991
ctl3_gencnt++;
2992
2993
CTL3_UNLOCK();
2994
}
2995
2996
/*
2997
* Removes one or more sockopt handlers from the global array.
2998
*/
2999
int
3000
ipfw_del_sopt_handler(struct ipfw_sopt_handler *sh, size_t count)
3001
{
3002
size_t sz;
3003
struct ipfw_sopt_handler *tmp, *h;
3004
int i;
3005
3006
CTL3_LOCK();
3007
3008
for (i = 0; i < count; i++) {
3009
tmp = &sh[i];
3010
h = find_sh(tmp->opcode, tmp->version, tmp->handler);
3011
if (h == NULL)
3012
continue;
3013
3014
sz = (ctl3_handlers + ctl3_hsize - (h + 1)) * sizeof(*h);
3015
memmove(h, h + 1, sz);
3016
ctl3_hsize--;
3017
}
3018
3019
if (ctl3_hsize == 0) {
3020
if (ctl3_handlers != NULL)
3021
free(ctl3_handlers, M_IPFW);
3022
ctl3_handlers = NULL;
3023
}
3024
3025
ctl3_gencnt++;
3026
3027
CTL3_UNLOCK();
3028
3029
return (0);
3030
}
3031
3032
/*
3033
* Writes data accumulated in @sd to sockopt buffer.
3034
* Zeroes internal @sd buffer.
3035
*/
3036
static int
3037
ipfw_flush_sopt_data(struct sockopt_data *sd)
3038
{
3039
struct sockopt *sopt;
3040
int error;
3041
size_t sz;
3042
3043
sz = sd->koff;
3044
if (sz == 0)
3045
return (0);
3046
3047
sopt = sd->sopt;
3048
3049
if (sopt->sopt_dir == SOPT_GET) {
3050
error = copyout(sd->kbuf, sopt->sopt_val, sz);
3051
if (error != 0)
3052
return (error);
3053
}
3054
3055
memset(sd->kbuf, 0, sd->ksize);
3056
sd->ktotal += sz;
3057
sd->koff = 0;
3058
if (sd->ktotal + sd->ksize < sd->valsize)
3059
sd->kavail = sd->ksize;
3060
else
3061
sd->kavail = sd->valsize - sd->ktotal;
3062
3063
/* Update sopt buffer data */
3064
sopt->sopt_valsize = sd->ktotal;
3065
sopt->sopt_val = sd->sopt_val + sd->ktotal;
3066
3067
return (0);
3068
}
3069
3070
/*
3071
* Ensures that @sd buffer has contiguous @neeeded number of
3072
* bytes.
3073
*
3074
* Returns pointer to requested space or NULL.
3075
*/
3076
caddr_t
3077
ipfw_get_sopt_space(struct sockopt_data *sd, size_t needed)
3078
{
3079
int error;
3080
caddr_t addr;
3081
3082
if (sd->kavail < needed) {
3083
/*
3084
* Flush data and try another time.
3085
*/
3086
error = ipfw_flush_sopt_data(sd);
3087
3088
if (sd->kavail < needed || error != 0)
3089
return (NULL);
3090
}
3091
3092
addr = sd->kbuf + sd->koff;
3093
sd->koff += needed;
3094
sd->kavail -= needed;
3095
return (addr);
3096
}
3097
3098
/*
3099
* Requests @needed contiguous bytes from @sd buffer.
3100
* Function is used to notify subsystem that we are
3101
* interesed in first @needed bytes (request header)
3102
* and the rest buffer can be safely zeroed.
3103
*
3104
* Returns pointer to requested space or NULL.
3105
*/
3106
caddr_t
3107
ipfw_get_sopt_header(struct sockopt_data *sd, size_t needed)
3108
{
3109
caddr_t addr;
3110
3111
if ((addr = ipfw_get_sopt_space(sd, needed)) == NULL)
3112
return (NULL);
3113
3114
if (sd->kavail > 0)
3115
memset(sd->kbuf + sd->koff, 0, sd->kavail);
3116
3117
return (addr);
3118
}
3119
3120
/*
3121
* New sockopt handler.
3122
*/
3123
int
3124
ipfw_ctl3(struct sockopt *sopt)
3125
{
3126
int error, locked;
3127
size_t size, valsize;
3128
struct ip_fw_chain *chain;
3129
char xbuf[256];
3130
struct sockopt_data sdata;
3131
struct ipfw_sopt_handler h;
3132
ip_fw3_opheader *op3 = NULL;
3133
3134
error = priv_check(sopt->sopt_td, PRIV_NETINET_IPFW);
3135
if (error != 0)
3136
return (error);
3137
3138
if (sopt->sopt_name != IP_FW3)
3139
return (EOPNOTSUPP);
3140
3141
chain = &V_layer3_chain;
3142
error = 0;
3143
3144
/* Save original valsize before it is altered via sooptcopyin() */
3145
valsize = sopt->sopt_valsize;
3146
memset(&sdata, 0, sizeof(sdata));
3147
/* Read op3 header first to determine actual operation */
3148
op3 = (ip_fw3_opheader *)xbuf;
3149
error = sooptcopyin(sopt, op3, sizeof(*op3), sizeof(*op3));
3150
if (error != 0)
3151
return (error);
3152
sopt->sopt_valsize = valsize;
3153
3154
/*
3155
* Find and reference command.
3156
*/
3157
error = find_ref_sh(op3->opcode, op3->version, &h);
3158
if (error != 0)
3159
return (error);
3160
3161
/*
3162
* Disallow modifications in really-really secure mode, but still allow
3163
* the logging counters to be reset.
3164
*/
3165
if ((h.dir & HDIR_SET) != 0 && h.opcode != IP_FW_XRESETLOG) {
3166
error = securelevel_ge(sopt->sopt_td->td_ucred, 3);
3167
if (error != 0) {
3168
find_unref_sh(&h);
3169
return (error);
3170
}
3171
}
3172
3173
/*
3174
* Fill in sockopt_data structure that may be useful for
3175
* IP_FW3 get requests.
3176
*/
3177
locked = 0;
3178
if (valsize <= sizeof(xbuf)) {
3179
/* use on-stack buffer */
3180
sdata.kbuf = xbuf;
3181
sdata.ksize = sizeof(xbuf);
3182
sdata.kavail = valsize;
3183
} else {
3184
/*
3185
* Determine opcode type/buffer size:
3186
* allocate sliding-window buf for data export or
3187
* contiguous buffer for special ops.
3188
*/
3189
if ((h.dir & HDIR_SET) != 0) {
3190
/* Set request. Allocate contigous buffer. */
3191
if (valsize > CTL3_LARGEBUF) {
3192
find_unref_sh(&h);
3193
return (EFBIG);
3194
}
3195
3196
size = valsize;
3197
} else {
3198
/* Get request. Allocate sliding window buffer */
3199
size = (valsize<CTL3_SMALLBUF) ? valsize:CTL3_SMALLBUF;
3200
3201
if (size < valsize) {
3202
/* We have to wire user buffer */
3203
error = vslock(sopt->sopt_val, valsize);
3204
if (error != 0)
3205
return (error);
3206
locked = 1;
3207
}
3208
}
3209
3210
sdata.kbuf = malloc(size, M_TEMP, M_WAITOK | M_ZERO);
3211
sdata.ksize = size;
3212
sdata.kavail = size;
3213
}
3214
3215
sdata.sopt = sopt;
3216
sdata.sopt_val = sopt->sopt_val;
3217
sdata.valsize = valsize;
3218
3219
/*
3220
* Copy either all request (if valsize < bsize_max)
3221
* or first bsize_max bytes to guarantee most consumers
3222
* that all necessary data has been copied).
3223
* Anyway, copy not less than sizeof(ip_fw3_opheader).
3224
*/
3225
if ((error = sooptcopyin(sopt, sdata.kbuf, sdata.ksize,
3226
sizeof(ip_fw3_opheader))) != 0)
3227
return (error);
3228
op3 = (ip_fw3_opheader *)sdata.kbuf;
3229
3230
/* Finally, run handler */
3231
error = h.handler(chain, op3, &sdata);
3232
find_unref_sh(&h);
3233
3234
/* Flush state and free buffers */
3235
if (error == 0)
3236
error = ipfw_flush_sopt_data(&sdata);
3237
else
3238
ipfw_flush_sopt_data(&sdata);
3239
3240
if (locked != 0)
3241
vsunlock(sdata.sopt_val, valsize);
3242
3243
/* Restore original pointer and set number of bytes written */
3244
sopt->sopt_val = sdata.sopt_val;
3245
sopt->sopt_valsize = sdata.ktotal;
3246
if (sdata.kbuf != xbuf)
3247
free(sdata.kbuf, M_TEMP);
3248
3249
return (error);
3250
}
3251
3252
/*
3253
* Named object api
3254
*
3255
*/
3256
3257
void
3258
ipfw_init_srv(struct ip_fw_chain *ch)
3259
{
3260
ch->srvmap = ipfw_objhash_create(IPFW_OBJECTS_DEFAULT,
3261
DEFAULT_OBJHASH_SIZE);
3262
ch->srvstate = malloc(sizeof(void *) * IPFW_OBJECTS_DEFAULT,
3263
M_IPFW, M_WAITOK | M_ZERO);
3264
}
3265
3266
void
3267
ipfw_destroy_srv(struct ip_fw_chain *ch)
3268
{
3269
free(ch->srvstate, M_IPFW);
3270
ipfw_objhash_destroy(ch->srvmap);
3271
}
3272
3273
/*
3274
* Allocate new bitmask which can be used to enlarge/shrink
3275
* named instance index.
3276
*/
3277
void
3278
ipfw_objhash_bitmap_alloc(uint32_t items, void **idx, int *pblocks)
3279
{
3280
size_t size;
3281
int max_blocks;
3282
u_long *idx_mask;
3283
3284
KASSERT((items % BLOCK_ITEMS) == 0,
3285
("bitmask size needs to power of 2 and greater or equal to %zu",
3286
BLOCK_ITEMS));
3287
3288
max_blocks = items / BLOCK_ITEMS;
3289
size = items / 8;
3290
idx_mask = malloc(size * IPFW_MAX_SETS, M_IPFW, M_WAITOK);
3291
/* Mark all as free */
3292
memset(idx_mask, 0xFF, size * IPFW_MAX_SETS);
3293
*idx_mask &= ~(u_long)1; /* Skip index 0 */
3294
3295
*idx = idx_mask;
3296
*pblocks = max_blocks;
3297
}
3298
3299
/*
3300
* Copy current bitmask index to new one.
3301
*/
3302
void
3303
ipfw_objhash_bitmap_merge(struct namedobj_instance *ni, void **idx, int *blocks)
3304
{
3305
int old_blocks, new_blocks;
3306
u_long *old_idx, *new_idx;
3307
int i;
3308
3309
old_idx = ni->idx_mask;
3310
old_blocks = ni->max_blocks;
3311
new_idx = *idx;
3312
new_blocks = *blocks;
3313
3314
for (i = 0; i < IPFW_MAX_SETS; i++) {
3315
memcpy(&new_idx[new_blocks * i], &old_idx[old_blocks * i],
3316
old_blocks * sizeof(u_long));
3317
}
3318
}
3319
3320
/*
3321
* Swaps current @ni index with new one.
3322
*/
3323
void
3324
ipfw_objhash_bitmap_swap(struct namedobj_instance *ni, void **idx, int *blocks)
3325
{
3326
int old_blocks;
3327
u_long *old_idx;
3328
3329
old_idx = ni->idx_mask;
3330
old_blocks = ni->max_blocks;
3331
3332
ni->idx_mask = *idx;
3333
ni->max_blocks = *blocks;
3334
3335
/* Save old values */
3336
*idx = old_idx;
3337
*blocks = old_blocks;
3338
}
3339
3340
void
3341
ipfw_objhash_bitmap_free(void *idx, int blocks)
3342
{
3343
free(idx, M_IPFW);
3344
}
3345
3346
/*
3347
* Creates named hash instance.
3348
* Must be called without holding any locks.
3349
* Return pointer to new instance.
3350
*/
3351
struct namedobj_instance *
3352
ipfw_objhash_create(uint32_t items, size_t hash_size)
3353
{
3354
struct namedobj_instance *ni;
3355
int i;
3356
size_t size;
3357
3358
size = sizeof(struct namedobj_instance) +
3359
sizeof(struct namedobjects_head) * hash_size +
3360
sizeof(struct namedobjects_head) * hash_size;
3361
3362
ni = malloc(size, M_IPFW, M_WAITOK | M_ZERO);
3363
ni->nn_size = hash_size;
3364
ni->nv_size = hash_size;
3365
3366
ni->names = (struct namedobjects_head *)(ni +1);
3367
ni->values = &ni->names[ni->nn_size];
3368
3369
for (i = 0; i < ni->nn_size; i++)
3370
TAILQ_INIT(&ni->names[i]);
3371
3372
for (i = 0; i < ni->nv_size; i++)
3373
TAILQ_INIT(&ni->values[i]);
3374
3375
/* Set default hashing/comparison functions */
3376
ni->hash_f = objhash_hash_name;
3377
ni->cmp_f = objhash_cmp_name;
3378
3379
/* Allocate bitmask separately due to possible resize */
3380
ipfw_objhash_bitmap_alloc(items, (void*)&ni->idx_mask, &ni->max_blocks);
3381
3382
return (ni);
3383
}
3384
3385
void
3386
ipfw_objhash_destroy(struct namedobj_instance *ni)
3387
{
3388
free(ni->idx_mask, M_IPFW);
3389
free(ni, M_IPFW);
3390
}
3391
3392
void
3393
ipfw_objhash_set_funcs(struct namedobj_instance *ni, objhash_hash_f *hash_f,
3394
objhash_cmp_f *cmp_f)
3395
{
3396
3397
ni->hash_f = hash_f;
3398
ni->cmp_f = cmp_f;
3399
}
3400
3401
static uint32_t
3402
objhash_hash_name(struct namedobj_instance *ni, const void *name, uint32_t set)
3403
{
3404
3405
return (fnv_32_str((const char *)name, FNV1_32_INIT));
3406
}
3407
3408
static int
3409
objhash_cmp_name(struct named_object *no, const void *name, uint32_t set)
3410
{
3411
3412
if ((strcmp(no->name, (const char *)name) == 0) && (no->set == set))
3413
return (0);
3414
3415
return (1);
3416
}
3417
3418
static uint32_t
3419
objhash_hash_idx(struct namedobj_instance *ni, uint32_t val)
3420
{
3421
uint32_t v;
3422
3423
v = val % (ni->nv_size - 1);
3424
3425
return (v);
3426
}
3427
3428
struct named_object *
3429
ipfw_objhash_lookup_name(struct namedobj_instance *ni, uint32_t set,
3430
const char *name)
3431
{
3432
struct named_object *no;
3433
uint32_t hash;
3434
3435
hash = ni->hash_f(ni, name, set) % ni->nn_size;
3436
3437
TAILQ_FOREACH(no, &ni->names[hash], nn_next) {
3438
if (ni->cmp_f(no, name, set) == 0)
3439
return (no);
3440
}
3441
3442
return (NULL);
3443
}
3444
3445
/*
3446
* Find named object by @uid.
3447
* Check @tlvs for valid data inside.
3448
*
3449
* Returns pointer to found TLV or NULL.
3450
*/
3451
ipfw_obj_ntlv *
3452
ipfw_find_name_tlv_type(void *tlvs, int len, uint32_t uidx, uint32_t etlv)
3453
{
3454
ipfw_obj_ntlv *ntlv;
3455
uintptr_t pa, pe;
3456
int l;
3457
3458
pa = (uintptr_t)tlvs;
3459
pe = pa + len;
3460
l = 0;
3461
for (; pa < pe; pa += l) {
3462
ntlv = (ipfw_obj_ntlv *)pa;
3463
l = ntlv->head.length;
3464
3465
if (l != sizeof(*ntlv))
3466
return (NULL);
3467
3468
if (ntlv->idx != uidx)
3469
continue;
3470
/*
3471
* When userland has specified zero TLV type, do
3472
* not compare it with eltv. In some cases userland
3473
* doesn't know what type should it have. Use only
3474
* uidx and name for search named_object.
3475
*/
3476
if (ntlv->head.type != 0 &&
3477
ntlv->head.type != (uint16_t)etlv)
3478
continue;
3479
3480
if (ipfw_check_object_name_generic(ntlv->name) != 0)
3481
return (NULL);
3482
3483
return (ntlv);
3484
}
3485
3486
return (NULL);
3487
}
3488
3489
/*
3490
* Finds object config based on either legacy index
3491
* or name in ntlv.
3492
* Note @ti structure contains unchecked data from userland.
3493
*
3494
* Returns 0 in success and fills in @pno with found config
3495
*/
3496
int
3497
ipfw_objhash_find_type(struct namedobj_instance *ni, struct tid_info *ti,
3498
uint32_t etlv, struct named_object **pno)
3499
{
3500
char *name;
3501
ipfw_obj_ntlv *ntlv;
3502
uint32_t set;
3503
3504
if (ti->tlvs == NULL)
3505
return (EINVAL);
3506
3507
ntlv = ipfw_find_name_tlv_type(ti->tlvs, ti->tlen, ti->uidx, etlv);
3508
if (ntlv == NULL)
3509
return (EINVAL);
3510
name = ntlv->name;
3511
3512
/*
3513
* Use set provided by @ti instead of @ntlv one.
3514
* This is needed due to different sets behavior
3515
* controlled by V_fw_tables_sets.
3516
*/
3517
set = ti->set;
3518
*pno = ipfw_objhash_lookup_name(ni, set, name);
3519
if (*pno == NULL)
3520
return (ESRCH);
3521
return (0);
3522
}
3523
3524
/*
3525
* Find named object by name, considering also its TLV type.
3526
*/
3527
struct named_object *
3528
ipfw_objhash_lookup_name_type(struct namedobj_instance *ni, uint32_t set,
3529
uint32_t type, const char *name)
3530
{
3531
struct named_object *no;
3532
uint32_t hash;
3533
3534
hash = ni->hash_f(ni, name, set) % ni->nn_size;
3535
3536
TAILQ_FOREACH(no, &ni->names[hash], nn_next) {
3537
if (ni->cmp_f(no, name, set) == 0 &&
3538
no->etlv == (uint16_t)type)
3539
return (no);
3540
}
3541
3542
return (NULL);
3543
}
3544
3545
struct named_object *
3546
ipfw_objhash_lookup_kidx(struct namedobj_instance *ni, uint32_t kidx)
3547
{
3548
struct named_object *no;
3549
uint32_t hash;
3550
3551
hash = objhash_hash_idx(ni, kidx);
3552
3553
TAILQ_FOREACH(no, &ni->values[hash], nv_next) {
3554
if (no->kidx == kidx)
3555
return (no);
3556
}
3557
3558
return (NULL);
3559
}
3560
3561
int
3562
ipfw_objhash_same_name(struct namedobj_instance *ni, struct named_object *a,
3563
struct named_object *b)
3564
{
3565
3566
if ((strcmp(a->name, b->name) == 0) && a->set == b->set)
3567
return (1);
3568
3569
return (0);
3570
}
3571
3572
void
3573
ipfw_objhash_add(struct namedobj_instance *ni, struct named_object *no)
3574
{
3575
uint32_t hash;
3576
3577
hash = ni->hash_f(ni, no->name, no->set) % ni->nn_size;
3578
TAILQ_INSERT_HEAD(&ni->names[hash], no, nn_next);
3579
3580
hash = objhash_hash_idx(ni, no->kidx);
3581
TAILQ_INSERT_HEAD(&ni->values[hash], no, nv_next);
3582
3583
ni->count++;
3584
}
3585
3586
void
3587
ipfw_objhash_del(struct namedobj_instance *ni, struct named_object *no)
3588
{
3589
uint32_t hash;
3590
3591
hash = ni->hash_f(ni, no->name, no->set) % ni->nn_size;
3592
TAILQ_REMOVE(&ni->names[hash], no, nn_next);
3593
3594
hash = objhash_hash_idx(ni, no->kidx);
3595
TAILQ_REMOVE(&ni->values[hash], no, nv_next);
3596
3597
ni->count--;
3598
}
3599
3600
uint32_t
3601
ipfw_objhash_count(struct namedobj_instance *ni)
3602
{
3603
3604
return (ni->count);
3605
}
3606
3607
uint32_t
3608
ipfw_objhash_count_type(struct namedobj_instance *ni, uint16_t type)
3609
{
3610
struct named_object *no;
3611
uint32_t count;
3612
int i;
3613
3614
count = 0;
3615
for (i = 0; i < ni->nn_size; i++) {
3616
TAILQ_FOREACH(no, &ni->names[i], nn_next) {
3617
if (no->etlv == type)
3618
count++;
3619
}
3620
}
3621
return (count);
3622
}
3623
3624
/*
3625
* Runs @func for each found named object.
3626
* It is safe to delete objects from callback
3627
*/
3628
int
3629
ipfw_objhash_foreach(struct namedobj_instance *ni, objhash_cb_t *f, void *arg)
3630
{
3631
struct named_object *no, *no_tmp;
3632
int i, ret;
3633
3634
for (i = 0; i < ni->nn_size; i++) {
3635
TAILQ_FOREACH_SAFE(no, &ni->names[i], nn_next, no_tmp) {
3636
ret = f(ni, no, arg);
3637
if (ret != 0)
3638
return (ret);
3639
}
3640
}
3641
return (0);
3642
}
3643
3644
/*
3645
* Runs @f for each found named object with type @type.
3646
* It is safe to delete objects from callback
3647
*/
3648
int
3649
ipfw_objhash_foreach_type(struct namedobj_instance *ni, objhash_cb_t *f,
3650
void *arg, uint16_t type)
3651
{
3652
struct named_object *no, *no_tmp;
3653
int i, ret;
3654
3655
for (i = 0; i < ni->nn_size; i++) {
3656
TAILQ_FOREACH_SAFE(no, &ni->names[i], nn_next, no_tmp) {
3657
if (no->etlv != type)
3658
continue;
3659
ret = f(ni, no, arg);
3660
if (ret != 0)
3661
return (ret);
3662
}
3663
}
3664
return (0);
3665
}
3666
3667
/*
3668
* Removes index from given set.
3669
* Returns 0 on success.
3670
*/
3671
int
3672
ipfw_objhash_free_idx(struct namedobj_instance *ni, uint32_t idx)
3673
{
3674
u_long *mask;
3675
int i, v;
3676
3677
i = idx / BLOCK_ITEMS;
3678
v = idx % BLOCK_ITEMS;
3679
3680
if (i >= ni->max_blocks)
3681
return (1);
3682
3683
mask = &ni->idx_mask[i];
3684
3685
if ((*mask & ((u_long)1 << v)) != 0)
3686
return (1);
3687
3688
/* Mark as free */
3689
*mask |= (u_long)1 << v;
3690
3691
/* Update free offset */
3692
if (ni->free_off[0] > i)
3693
ni->free_off[0] = i;
3694
3695
return (0);
3696
}
3697
3698
/*
3699
* Allocate new index in given instance and stores in in @pidx.
3700
* Returns 0 on success.
3701
*/
3702
int
3703
ipfw_objhash_alloc_idx(void *n, uint32_t *pidx)
3704
{
3705
struct namedobj_instance *ni;
3706
u_long *mask;
3707
int i, off, v;
3708
3709
ni = (struct namedobj_instance *)n;
3710
3711
off = ni->free_off[0];
3712
mask = &ni->idx_mask[off];
3713
3714
for (i = off; i < ni->max_blocks; i++, mask++) {
3715
if ((v = ffsl(*mask)) == 0)
3716
continue;
3717
3718
/* Mark as busy */
3719
*mask &= ~ ((u_long)1 << (v - 1));
3720
3721
ni->free_off[0] = i;
3722
3723
v = BLOCK_ITEMS * i + v - 1;
3724
3725
*pidx = v;
3726
return (0);
3727
}
3728
3729
return (1);
3730
}
3731
3732
/* end of file */
3733
3734