1 /*-
2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3 *
4 * Copyright (c) 2002-2009 Luigi Rizzo, Universita` di Pisa
5 * Copyright (c) 2014 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 __FBSDID("$FreeBSD$");
34
35 /*
36 * Control socket and rule management routines for ipfw.
37 * Control is currently implemented via IP_FW3 setsockopt() code.
38 */
39
40 #include "opt_ipfw.h"
41 #include "opt_inet.h"
42 #ifndef INET
43 #error IPFIREWALL requires INET.
44 #endif /* INET */
45 #include "opt_inet6.h"
46
47 #include <sys/param.h>
48 #include <sys/systm.h>
49 #include <sys/malloc.h>
50 #include <sys/mbuf.h> /* struct m_tag used by nested headers */
51 #include <sys/kernel.h>
52 #include <sys/lock.h>
53 #include <sys/priv.h>
54 #include <sys/proc.h>
55 #include <sys/rwlock.h>
56 #include <sys/rmlock.h>
57 #include <sys/socket.h>
58 #include <sys/socketvar.h>
59 #include <sys/sysctl.h>
60 #include <sys/syslog.h>
61 #include <sys/fnv_hash.h>
62 #include <net/if.h>
63 #include <net/pfil.h>
64 #include <net/route.h>
65 #include <net/vnet.h>
66 #include <vm/vm.h>
67 #include <vm/vm_extern.h>
68
69 #include <netinet/in.h>
70 #include <netinet/ip_var.h> /* hooks */
71 #include <netinet/ip_fw.h>
72
73 #include <netpfil/ipfw/ip_fw_private.h>
74 #include <netpfil/ipfw/ip_fw_table.h>
75
76 #ifdef MAC
77 #include <security/mac/mac_framework.h>
78 #endif
79
80 static int ipfw_ctl(struct sockopt *sopt);
81 static int check_ipfw_rule_body(ipfw_insn *cmd, int cmd_len,
82 struct rule_check_info *ci);
83 static int check_ipfw_rule1(struct ip_fw_rule *rule, int size,
84 struct rule_check_info *ci);
85 static int check_ipfw_rule0(struct ip_fw_rule0 *rule, int size,
86 struct rule_check_info *ci);
87 static int rewrite_rule_uidx(struct ip_fw_chain *chain,
88 struct rule_check_info *ci);
89
90 #define NAMEDOBJ_HASH_SIZE 32
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 static int dump_config(struct ip_fw_chain *chain, ip_fw3_opheader *op3,
115 struct sockopt_data *sd);
116 static int add_rules(struct ip_fw_chain *chain, ip_fw3_opheader *op3,
117 struct sockopt_data *sd);
118 static int del_rules(struct ip_fw_chain *chain, ip_fw3_opheader *op3,
119 struct sockopt_data *sd);
120 static int clear_rules(struct ip_fw_chain *chain, ip_fw3_opheader *op3,
121 struct sockopt_data *sd);
122 static int move_rules(struct ip_fw_chain *chain, ip_fw3_opheader *op3,
123 struct sockopt_data *sd);
124 static int manage_sets(struct ip_fw_chain *chain, ip_fw3_opheader *op3,
125 struct sockopt_data *sd);
126 static int dump_soptcodes(struct ip_fw_chain *chain, ip_fw3_opheader *op3,
127 struct sockopt_data *sd);
128 static int dump_srvobjects(struct ip_fw_chain *chain, ip_fw3_opheader *op3,
129 struct sockopt_data *sd);
130
131 /* ctl3 handler data */
132 struct mtx ctl3_lock;
133 #define CTL3_LOCK_INIT() mtx_init(&ctl3_lock, "ctl3_lock", NULL, MTX_DEF)
134 #define CTL3_LOCK_DESTROY() mtx_destroy(&ctl3_lock)
135 #define CTL3_LOCK() mtx_lock(&ctl3_lock)
136 #define CTL3_UNLOCK() mtx_unlock(&ctl3_lock)
137
138 static struct ipfw_sopt_handler *ctl3_handlers;
139 static size_t ctl3_hsize;
140 static uint64_t ctl3_refct, ctl3_gencnt;
141 #define CTL3_SMALLBUF 4096 /* small page-size write buffer */
142 #define CTL3_LARGEBUF 16 * 1024 * 1024 /* handle large rulesets */
143
144 static int ipfw_flush_sopt_data(struct sockopt_data *sd);
145
146 static struct ipfw_sopt_handler scodes[] = {
147 { IP_FW_XGET, 0, HDIR_GET, dump_config },
148 { IP_FW_XADD, 0, HDIR_BOTH, add_rules },
149 { IP_FW_XDEL, 0, HDIR_BOTH, del_rules },
150 { IP_FW_XZERO, 0, HDIR_SET, clear_rules },
151 { IP_FW_XRESETLOG, 0, HDIR_SET, clear_rules },
152 { IP_FW_XMOVE, 0, HDIR_SET, move_rules },
153 { IP_FW_SET_SWAP, 0, HDIR_SET, manage_sets },
154 { IP_FW_SET_MOVE, 0, HDIR_SET, manage_sets },
155 { IP_FW_SET_ENABLE, 0, HDIR_SET, manage_sets },
156 { IP_FW_DUMP_SOPTCODES, 0, HDIR_GET, dump_soptcodes },
157 { IP_FW_DUMP_SRVOBJECTS,0, HDIR_GET, dump_srvobjects },
158 };
159
160 static int
161 set_legacy_obj_kidx(struct ip_fw_chain *ch, struct ip_fw_rule0 *rule);
162 static struct opcode_obj_rewrite *find_op_rw(ipfw_insn *cmd,
163 uint16_t *puidx, uint8_t *ptype);
164 static int ref_rule_objects(struct ip_fw_chain *ch, struct ip_fw *rule,
165 struct rule_check_info *ci, struct obj_idx *oib, struct tid_info *ti);
166 static int ref_opcode_object(struct ip_fw_chain *ch, ipfw_insn *cmd,
167 struct tid_info *ti, struct obj_idx *pidx, int *unresolved);
168 static void unref_rule_objects(struct ip_fw_chain *chain, struct ip_fw *rule);
169 static void unref_oib_objects(struct ip_fw_chain *ch, ipfw_insn *cmd,
170 struct obj_idx *oib, struct obj_idx *end);
171 static int export_objhash_ntlv(struct namedobj_instance *ni, uint16_t kidx,
172 struct sockopt_data *sd);
173
174 /*
175 * Opcode object rewriter variables
176 */
177 struct opcode_obj_rewrite *ctl3_rewriters;
178 static size_t ctl3_rsize;
179
180 /*
181 * static variables followed by global ones
182 */
183
184 VNET_DEFINE_STATIC(uma_zone_t, ipfw_cntr_zone);
185 #define V_ipfw_cntr_zone VNET(ipfw_cntr_zone)
186
187 void
ipfw_init_counters()188 ipfw_init_counters()
189 {
190
191 V_ipfw_cntr_zone = uma_zcreate("IPFW counters",
192 IPFW_RULE_CNTR_SIZE, NULL, NULL, NULL, NULL,
193 UMA_ALIGN_PTR, UMA_ZONE_PCPU);
194 }
195
196 void
ipfw_destroy_counters()197 ipfw_destroy_counters()
198 {
199
200 uma_zdestroy(V_ipfw_cntr_zone);
201 }
202
203 struct ip_fw *
ipfw_alloc_rule(struct ip_fw_chain * chain,size_t rulesize)204 ipfw_alloc_rule(struct ip_fw_chain *chain, size_t rulesize)
205 {
206 struct ip_fw *rule;
207
208 rule = malloc(rulesize, M_IPFW, M_WAITOK | M_ZERO);
209 rule->cntr = uma_zalloc_pcpu(V_ipfw_cntr_zone, M_WAITOK | M_ZERO);
210 rule->refcnt = 1;
211
212 return (rule);
213 }
214
215 void
ipfw_free_rule(struct ip_fw * rule)216 ipfw_free_rule(struct ip_fw *rule)
217 {
218
219 /*
220 * We don't release refcnt here, since this function
221 * can be called without any locks held. The caller
222 * must release reference under IPFW_UH_WLOCK, and then
223 * call this function if refcount becomes 1.
224 */
225 if (rule->refcnt > 1)
226 return;
227 uma_zfree_pcpu(V_ipfw_cntr_zone, rule->cntr);
228 free(rule, M_IPFW);
229 }
230
231
232 /*
233 * Find the smallest rule >= key, id.
234 * We could use bsearch but it is so simple that we code it directly
235 */
236 int
ipfw_find_rule(struct ip_fw_chain * chain,uint32_t key,uint32_t id)237 ipfw_find_rule(struct ip_fw_chain *chain, uint32_t key, uint32_t id)
238 {
239 int i, lo, hi;
240 struct ip_fw *r;
241
242 for (lo = 0, hi = chain->n_rules - 1; lo < hi;) {
243 i = (lo + hi) / 2;
244 r = chain->map[i];
245 if (r->rulenum < key)
246 lo = i + 1; /* continue from the next one */
247 else if (r->rulenum > key)
248 hi = i; /* this might be good */
249 else if (r->id < id)
250 lo = i + 1; /* continue from the next one */
251 else /* r->id >= id */
252 hi = i; /* this might be good */
253 }
254 return hi;
255 }
256
257 /*
258 * Builds skipto cache on rule set @map.
259 */
260 static void
update_skipto_cache(struct ip_fw_chain * chain,struct ip_fw ** map)261 update_skipto_cache(struct ip_fw_chain *chain, struct ip_fw **map)
262 {
263 int *smap, rulenum;
264 int i, mi;
265
266 IPFW_UH_WLOCK_ASSERT(chain);
267
268 mi = 0;
269 rulenum = map[mi]->rulenum;
270 smap = chain->idxmap_back;
271
272 if (smap == NULL)
273 return;
274
275 for (i = 0; i < 65536; i++) {
276 smap[i] = mi;
277 /* Use the same rule index until i < rulenum */
278 if (i != rulenum || i == 65535)
279 continue;
280 /* Find next rule with num > i */
281 rulenum = map[++mi]->rulenum;
282 while (rulenum == i)
283 rulenum = map[++mi]->rulenum;
284 }
285 }
286
287 /*
288 * Swaps prepared (backup) index with current one.
289 */
290 static void
swap_skipto_cache(struct ip_fw_chain * chain)291 swap_skipto_cache(struct ip_fw_chain *chain)
292 {
293 int *map;
294
295 IPFW_UH_WLOCK_ASSERT(chain);
296 IPFW_WLOCK_ASSERT(chain);
297
298 map = chain->idxmap;
299 chain->idxmap = chain->idxmap_back;
300 chain->idxmap_back = map;
301 }
302
303 /*
304 * Allocate and initialize skipto cache.
305 */
306 void
ipfw_init_skipto_cache(struct ip_fw_chain * chain)307 ipfw_init_skipto_cache(struct ip_fw_chain *chain)
308 {
309 int *idxmap, *idxmap_back;
310
311 idxmap = malloc(65536 * sizeof(int), M_IPFW, M_WAITOK | M_ZERO);
312 idxmap_back = malloc(65536 * sizeof(int), M_IPFW, M_WAITOK);
313
314 /*
315 * Note we may be called at any time after initialization,
316 * for example, on first skipto rule, so we need to
317 * provide valid chain->idxmap on return
318 */
319
320 IPFW_UH_WLOCK(chain);
321 if (chain->idxmap != NULL) {
322 IPFW_UH_WUNLOCK(chain);
323 free(idxmap, M_IPFW);
324 free(idxmap_back, M_IPFW);
325 return;
326 }
327
328 /* Set backup pointer first to permit building cache */
329 chain->idxmap_back = idxmap_back;
330 update_skipto_cache(chain, chain->map);
331 IPFW_WLOCK(chain);
332 /* It is now safe to set chain->idxmap ptr */
333 chain->idxmap = idxmap;
334 swap_skipto_cache(chain);
335 IPFW_WUNLOCK(chain);
336 IPFW_UH_WUNLOCK(chain);
337 }
338
339 /*
340 * Destroys skipto cache.
341 */
342 void
ipfw_destroy_skipto_cache(struct ip_fw_chain * chain)343 ipfw_destroy_skipto_cache(struct ip_fw_chain *chain)
344 {
345
346 if (chain->idxmap != NULL)
347 free(chain->idxmap, M_IPFW);
348 if (chain->idxmap != NULL)
349 free(chain->idxmap_back, M_IPFW);
350 }
351
352
353 /*
354 * allocate a new map, returns the chain locked. extra is the number
355 * of entries to add or delete.
356 */
357 static struct ip_fw **
get_map(struct ip_fw_chain * chain,int extra,int locked)358 get_map(struct ip_fw_chain *chain, int extra, int locked)
359 {
360
361 for (;;) {
362 struct ip_fw **map;
363 u_int i, mflags;
364
365 mflags = M_ZERO | ((locked != 0) ? M_NOWAIT : M_WAITOK);
366
367 i = chain->n_rules + extra;
368 map = malloc(i * sizeof(struct ip_fw *), M_IPFW, mflags);
369 if (map == NULL) {
370 printf("%s: cannot allocate map\n", __FUNCTION__);
371 return NULL;
372 }
373 if (!locked)
374 IPFW_UH_WLOCK(chain);
375 if (i >= chain->n_rules + extra) /* good */
376 return map;
377 /* otherwise we lost the race, free and retry */
378 if (!locked)
379 IPFW_UH_WUNLOCK(chain);
380 free(map, M_IPFW);
381 }
382 }
383
384 /*
385 * swap the maps. It is supposed to be called with IPFW_UH_WLOCK
386 */
387 static struct ip_fw **
swap_map(struct ip_fw_chain * chain,struct ip_fw ** new_map,int new_len)388 swap_map(struct ip_fw_chain *chain, struct ip_fw **new_map, int new_len)
389 {
390 struct ip_fw **old_map;
391
392 IPFW_WLOCK(chain);
393 chain->id++;
394 chain->n_rules = new_len;
395 old_map = chain->map;
396 chain->map = new_map;
397 swap_skipto_cache(chain);
398 IPFW_WUNLOCK(chain);
399 return old_map;
400 }
401
402
403 static void
export_cntr1_base(struct ip_fw * krule,struct ip_fw_bcounter * cntr)404 export_cntr1_base(struct ip_fw *krule, struct ip_fw_bcounter *cntr)
405 {
406 struct timeval boottime;
407
408 cntr->size = sizeof(*cntr);
409
410 if (krule->cntr != NULL) {
411 cntr->pcnt = counter_u64_fetch(krule->cntr);
412 cntr->bcnt = counter_u64_fetch(krule->cntr + 1);
413 cntr->timestamp = krule->timestamp;
414 }
415 if (cntr->timestamp > 0) {
416 getboottime(&boottime);
417 cntr->timestamp += boottime.tv_sec;
418 }
419 }
420
421 static void
export_cntr0_base(struct ip_fw * krule,struct ip_fw_bcounter0 * cntr)422 export_cntr0_base(struct ip_fw *krule, struct ip_fw_bcounter0 *cntr)
423 {
424 struct timeval boottime;
425
426 if (krule->cntr != NULL) {
427 cntr->pcnt = counter_u64_fetch(krule->cntr);
428 cntr->bcnt = counter_u64_fetch(krule->cntr + 1);
429 cntr->timestamp = krule->timestamp;
430 }
431 if (cntr->timestamp > 0) {
432 getboottime(&boottime);
433 cntr->timestamp += boottime.tv_sec;
434 }
435 }
436
437 /*
438 * Copies rule @urule from v1 userland format (current).
439 * to kernel @krule.
440 * Assume @krule is zeroed.
441 */
442 static void
import_rule1(struct rule_check_info * ci)443 import_rule1(struct rule_check_info *ci)
444 {
445 struct ip_fw_rule *urule;
446 struct ip_fw *krule;
447
448 urule = (struct ip_fw_rule *)ci->urule;
449 krule = (struct ip_fw *)ci->krule;
450
451 /* copy header */
452 krule->act_ofs = urule->act_ofs;
453 krule->cmd_len = urule->cmd_len;
454 krule->rulenum = urule->rulenum;
455 krule->set = urule->set;
456 krule->flags = urule->flags;
457
458 /* Save rulenum offset */
459 ci->urule_numoff = offsetof(struct ip_fw_rule, rulenum);
460
461 /* Copy opcodes */
462 memcpy(krule->cmd, urule->cmd, krule->cmd_len * sizeof(uint32_t));
463 }
464
465 /*
466 * Export rule into v1 format (Current).
467 * Layout:
468 * [ ipfw_obj_tlv(IPFW_TLV_RULE_ENT)
469 * [ ip_fw_rule ] OR
470 * [ ip_fw_bcounter ip_fw_rule] (depends on rcntrs).
471 * ]
472 * Assume @data is zeroed.
473 */
474 static void
export_rule1(struct ip_fw * krule,caddr_t data,int len,int rcntrs)475 export_rule1(struct ip_fw *krule, caddr_t data, int len, int rcntrs)
476 {
477 struct ip_fw_bcounter *cntr;
478 struct ip_fw_rule *urule;
479 ipfw_obj_tlv *tlv;
480
481 /* Fill in TLV header */
482 tlv = (ipfw_obj_tlv *)data;
483 tlv->type = IPFW_TLV_RULE_ENT;
484 tlv->length = len;
485
486 if (rcntrs != 0) {
487 /* Copy counters */
488 cntr = (struct ip_fw_bcounter *)(tlv + 1);
489 urule = (struct ip_fw_rule *)(cntr + 1);
490 export_cntr1_base(krule, cntr);
491 } else
492 urule = (struct ip_fw_rule *)(tlv + 1);
493
494 /* copy header */
495 urule->act_ofs = krule->act_ofs;
496 urule->cmd_len = krule->cmd_len;
497 urule->rulenum = krule->rulenum;
498 urule->set = krule->set;
499 urule->flags = krule->flags;
500 urule->id = krule->id;
501
502 /* Copy opcodes */
503 memcpy(urule->cmd, krule->cmd, krule->cmd_len * sizeof(uint32_t));
504 }
505
506
507 /*
508 * Copies rule @urule from FreeBSD8 userland format (v0)
509 * to kernel @krule.
510 * Assume @krule is zeroed.
511 */
512 static void
import_rule0(struct rule_check_info * ci)513 import_rule0(struct rule_check_info *ci)
514 {
515 struct ip_fw_rule0 *urule;
516 struct ip_fw *krule;
517 int cmdlen, l;
518 ipfw_insn *cmd;
519 ipfw_insn_limit *lcmd;
520 ipfw_insn_if *cmdif;
521
522 urule = (struct ip_fw_rule0 *)ci->urule;
523 krule = (struct ip_fw *)ci->krule;
524
525 /* copy header */
526 krule->act_ofs = urule->act_ofs;
527 krule->cmd_len = urule->cmd_len;
528 krule->rulenum = urule->rulenum;
529 krule->set = urule->set;
530 if ((urule->_pad & 1) != 0)
531 krule->flags |= IPFW_RULE_NOOPT;
532
533 /* Save rulenum offset */
534 ci->urule_numoff = offsetof(struct ip_fw_rule0, rulenum);
535
536 /* Copy opcodes */
537 memcpy(krule->cmd, urule->cmd, krule->cmd_len * sizeof(uint32_t));
538
539 /*
540 * Alter opcodes:
541 * 1) convert tablearg value from 65535 to 0
542 * 2) Add high bit to O_SETFIB/O_SETDSCP values (to make room
543 * for targ).
544 * 3) convert table number in iface opcodes to u16
545 * 4) convert old `nat global` into new 65535
546 */
547 l = krule->cmd_len;
548 cmd = krule->cmd;
549 cmdlen = 0;
550
551 for ( ; l > 0 ; l -= cmdlen, cmd += cmdlen) {
552 cmdlen = F_LEN(cmd);
553
554 switch (cmd->opcode) {
555 /* Opcodes supporting tablearg */
556 case O_TAG:
557 case O_TAGGED:
558 case O_PIPE:
559 case O_QUEUE:
560 case O_DIVERT:
561 case O_TEE:
562 case O_SKIPTO:
563 case O_CALLRETURN:
564 case O_NETGRAPH:
565 case O_NGTEE:
566 case O_NAT:
567 if (cmd->arg1 == IP_FW_TABLEARG)
568 cmd->arg1 = IP_FW_TARG;
569 else if (cmd->arg1 == 0)
570 cmd->arg1 = IP_FW_NAT44_GLOBAL;
571 break;
572 case O_SETFIB:
573 case O_SETDSCP:
574 if (cmd->arg1 == IP_FW_TABLEARG)
575 cmd->arg1 = IP_FW_TARG;
576 else
577 cmd->arg1 |= 0x8000;
578 break;
579 case O_LIMIT:
580 lcmd = (ipfw_insn_limit *)cmd;
581 if (lcmd->conn_limit == IP_FW_TABLEARG)
582 lcmd->conn_limit = IP_FW_TARG;
583 break;
584 /* Interface tables */
585 case O_XMIT:
586 case O_RECV:
587 case O_VIA:
588 /* Interface table, possibly */
589 cmdif = (ipfw_insn_if *)cmd;
590 if (cmdif->name[0] != '\1')
591 break;
592
593 cmdif->p.kidx = (uint16_t)cmdif->p.glob;
594 break;
595 }
596 }
597 }
598
599 /*
600 * Copies rule @krule from kernel to FreeBSD8 userland format (v0)
601 */
602 static void
export_rule0(struct ip_fw * krule,struct ip_fw_rule0 * urule,int len)603 export_rule0(struct ip_fw *krule, struct ip_fw_rule0 *urule, int len)
604 {
605 int cmdlen, l;
606 ipfw_insn *cmd;
607 ipfw_insn_limit *lcmd;
608 ipfw_insn_if *cmdif;
609
610 /* copy header */
611 memset(urule, 0, len);
612 urule->act_ofs = krule->act_ofs;
613 urule->cmd_len = krule->cmd_len;
614 urule->rulenum = krule->rulenum;
615 urule->set = krule->set;
616 if ((krule->flags & IPFW_RULE_NOOPT) != 0)
617 urule->_pad |= 1;
618
619 /* Copy opcodes */
620 memcpy(urule->cmd, krule->cmd, krule->cmd_len * sizeof(uint32_t));
621
622 /* Export counters */
623 export_cntr0_base(krule, (struct ip_fw_bcounter0 *)&urule->pcnt);
624
625 /*
626 * Alter opcodes:
627 * 1) convert tablearg value from 0 to 65535
628 * 2) Remove highest bit from O_SETFIB/O_SETDSCP values.
629 * 3) convert table number in iface opcodes to int
630 */
631 l = urule->cmd_len;
632 cmd = urule->cmd;
633 cmdlen = 0;
634
635 for ( ; l > 0 ; l -= cmdlen, cmd += cmdlen) {
636 cmdlen = F_LEN(cmd);
637
638 switch (cmd->opcode) {
639 /* Opcodes supporting tablearg */
640 case O_TAG:
641 case O_TAGGED:
642 case O_PIPE:
643 case O_QUEUE:
644 case O_DIVERT:
645 case O_TEE:
646 case O_SKIPTO:
647 case O_CALLRETURN:
648 case O_NETGRAPH:
649 case O_NGTEE:
650 case O_NAT:
651 if (cmd->arg1 == IP_FW_TARG)
652 cmd->arg1 = IP_FW_TABLEARG;
653 else if (cmd->arg1 == IP_FW_NAT44_GLOBAL)
654 cmd->arg1 = 0;
655 break;
656 case O_SETFIB:
657 case O_SETDSCP:
658 if (cmd->arg1 == IP_FW_TARG)
659 cmd->arg1 = IP_FW_TABLEARG;
660 else
661 cmd->arg1 &= ~0x8000;
662 break;
663 case O_LIMIT:
664 lcmd = (ipfw_insn_limit *)cmd;
665 if (lcmd->conn_limit == IP_FW_TARG)
666 lcmd->conn_limit = IP_FW_TABLEARG;
667 break;
668 /* Interface tables */
669 case O_XMIT:
670 case O_RECV:
671 case O_VIA:
672 /* Interface table, possibly */
673 cmdif = (ipfw_insn_if *)cmd;
674 if (cmdif->name[0] != '\1')
675 break;
676
677 cmdif->p.glob = cmdif->p.kidx;
678 break;
679 }
680 }
681 }
682
683 /*
684 * Add new rule(s) to the list possibly creating rule number for each.
685 * Update the rule_number in the input struct so the caller knows it as well.
686 * Must be called without IPFW_UH held
687 */
688 static int
commit_rules(struct ip_fw_chain * chain,struct rule_check_info * rci,int count)689 commit_rules(struct ip_fw_chain *chain, struct rule_check_info *rci, int count)
690 {
691 int error, i, insert_before, tcount;
692 uint16_t rulenum, *pnum;
693 struct rule_check_info *ci;
694 struct ip_fw *krule;
695 struct ip_fw **map; /* the new array of pointers */
696
697 /* Check if we need to do table/obj index remap */
698 tcount = 0;
699 for (ci = rci, i = 0; i < count; ci++, i++) {
700 if (ci->object_opcodes == 0)
701 continue;
702
703 /*
704 * Rule has some object opcodes.
705 * We need to find (and create non-existing)
706 * kernel objects, and reference existing ones.
707 */
708 error = rewrite_rule_uidx(chain, ci);
709 if (error != 0) {
710
711 /*
712 * rewrite failed, state for current rule
713 * has been reverted. Check if we need to
714 * revert more.
715 */
716 if (tcount > 0) {
717
718 /*
719 * We have some more table rules
720 * we need to rollback.
721 */
722
723 IPFW_UH_WLOCK(chain);
724 while (ci != rci) {
725 ci--;
726 if (ci->object_opcodes == 0)
727 continue;
728 unref_rule_objects(chain,ci->krule);
729
730 }
731 IPFW_UH_WUNLOCK(chain);
732
733 }
734
735 return (error);
736 }
737
738 tcount++;
739 }
740
741 /* get_map returns with IPFW_UH_WLOCK if successful */
742 map = get_map(chain, count, 0 /* not locked */);
743 if (map == NULL) {
744 if (tcount > 0) {
745 /* Unbind tables */
746 IPFW_UH_WLOCK(chain);
747 for (ci = rci, i = 0; i < count; ci++, i++) {
748 if (ci->object_opcodes == 0)
749 continue;
750
751 unref_rule_objects(chain, ci->krule);
752 }
753 IPFW_UH_WUNLOCK(chain);
754 }
755
756 return (ENOSPC);
757 }
758
759 if (V_autoinc_step < 1)
760 V_autoinc_step = 1;
761 else if (V_autoinc_step > 1000)
762 V_autoinc_step = 1000;
763
764 /* FIXME: Handle count > 1 */
765 ci = rci;
766 krule = ci->krule;
767 rulenum = krule->rulenum;
768
769 /* find the insertion point, we will insert before */
770 insert_before = rulenum ? rulenum + 1 : IPFW_DEFAULT_RULE;
771 i = ipfw_find_rule(chain, insert_before, 0);
772 /* duplicate first part */
773 if (i > 0)
774 bcopy(chain->map, map, i * sizeof(struct ip_fw *));
775 map[i] = krule;
776 /* duplicate remaining part, we always have the default rule */
777 bcopy(chain->map + i, map + i + 1,
778 sizeof(struct ip_fw *) *(chain->n_rules - i));
779 if (rulenum == 0) {
780 /* Compute rule number and write it back */
781 rulenum = i > 0 ? map[i-1]->rulenum : 0;
782 if (rulenum < IPFW_DEFAULT_RULE - V_autoinc_step)
783 rulenum += V_autoinc_step;
784 krule->rulenum = rulenum;
785 /* Save number to userland rule */
786 pnum = (uint16_t *)((caddr_t)ci->urule + ci->urule_numoff);
787 *pnum = rulenum;
788 }
789
790 krule->id = chain->id + 1;
791 update_skipto_cache(chain, map);
792 map = swap_map(chain, map, chain->n_rules + 1);
793 chain->static_len += RULEUSIZE0(krule);
794 IPFW_UH_WUNLOCK(chain);
795 if (map)
796 free(map, M_IPFW);
797 return (0);
798 }
799
800 int
ipfw_add_protected_rule(struct ip_fw_chain * chain,struct ip_fw * rule,int locked)801 ipfw_add_protected_rule(struct ip_fw_chain *chain, struct ip_fw *rule,
802 int locked)
803 {
804 struct ip_fw **map;
805
806 map = get_map(chain, 1, locked);
807 if (map == NULL)
808 return (ENOMEM);
809 if (chain->n_rules > 0)
810 bcopy(chain->map, map,
811 chain->n_rules * sizeof(struct ip_fw *));
812 map[chain->n_rules] = rule;
813 rule->rulenum = IPFW_DEFAULT_RULE;
814 rule->set = RESVD_SET;
815 rule->id = chain->id + 1;
816 /* We add rule in the end of chain, no need to update skipto cache */
817 map = swap_map(chain, map, chain->n_rules + 1);
818 chain->static_len += RULEUSIZE0(rule);
819 IPFW_UH_WUNLOCK(chain);
820 free(map, M_IPFW);
821 return (0);
822 }
823
824 /*
825 * Adds @rule to the list of rules to reap
826 */
827 void
ipfw_reap_add(struct ip_fw_chain * chain,struct ip_fw ** head,struct ip_fw * rule)828 ipfw_reap_add(struct ip_fw_chain *chain, struct ip_fw **head,
829 struct ip_fw *rule)
830 {
831
832 IPFW_UH_WLOCK_ASSERT(chain);
833
834 /* Unlink rule from everywhere */
835 unref_rule_objects(chain, rule);
836
837 rule->next = *head;
838 *head = rule;
839 }
840
841 /*
842 * Reclaim storage associated with a list of rules. This is
843 * typically the list created using remove_rule.
844 * A NULL pointer on input is handled correctly.
845 */
846 void
ipfw_reap_rules(struct ip_fw * head)847 ipfw_reap_rules(struct ip_fw *head)
848 {
849 struct ip_fw *rule;
850
851 while ((rule = head) != NULL) {
852 head = head->next;
853 ipfw_free_rule(rule);
854 }
855 }
856
857 /*
858 * Rules to keep are
859 * (default || reserved || !match_set || !match_number)
860 * where
861 * default ::= (rule->rulenum == IPFW_DEFAULT_RULE)
862 * // the default rule is always protected
863 *
864 * reserved ::= (cmd == 0 && n == 0 && rule->set == RESVD_SET)
865 * // RESVD_SET is protected only if cmd == 0 and n == 0 ("ipfw flush")
866 *
867 * match_set ::= (cmd == 0 || rule->set == set)
868 * // set number is ignored for cmd == 0
869 *
870 * match_number ::= (cmd == 1 || n == 0 || n == rule->rulenum)
871 * // number is ignored for cmd == 1 or n == 0
872 *
873 */
874 int
ipfw_match_range(struct ip_fw * rule,ipfw_range_tlv * rt)875 ipfw_match_range(struct ip_fw *rule, ipfw_range_tlv *rt)
876 {
877
878 /* Don't match default rule for modification queries */
879 if (rule->rulenum == IPFW_DEFAULT_RULE &&
880 (rt->flags & IPFW_RCFLAG_DEFAULT) == 0)
881 return (0);
882
883 /* Don't match rules in reserved set for flush requests */
884 if ((rt->flags & IPFW_RCFLAG_ALL) != 0 && rule->set == RESVD_SET)
885 return (0);
886
887 /* If we're filtering by set, don't match other sets */
888 if ((rt->flags & IPFW_RCFLAG_SET) != 0 && rule->set != rt->set)
889 return (0);
890
891 if ((rt->flags & IPFW_RCFLAG_RANGE) != 0 &&
892 (rule->rulenum < rt->start_rule || rule->rulenum > rt->end_rule))
893 return (0);
894
895 return (1);
896 }
897
898 struct manage_sets_args {
899 uint16_t set;
900 uint8_t new_set;
901 };
902
903 static int
swap_sets_cb(struct namedobj_instance * ni,struct named_object * no,void * arg)904 swap_sets_cb(struct namedobj_instance *ni, struct named_object *no,
905 void *arg)
906 {
907 struct manage_sets_args *args;
908
909 args = (struct manage_sets_args *)arg;
910 if (no->set == (uint8_t)args->set)
911 no->set = args->new_set;
912 else if (no->set == args->new_set)
913 no->set = (uint8_t)args->set;
914 return (0);
915 }
916
917 static int
move_sets_cb(struct namedobj_instance * ni,struct named_object * no,void * arg)918 move_sets_cb(struct namedobj_instance *ni, struct named_object *no,
919 void *arg)
920 {
921 struct manage_sets_args *args;
922
923 args = (struct manage_sets_args *)arg;
924 if (no->set == (uint8_t)args->set)
925 no->set = args->new_set;
926 return (0);
927 }
928
929 static int
test_sets_cb(struct namedobj_instance * ni,struct named_object * no,void * arg)930 test_sets_cb(struct namedobj_instance *ni, struct named_object *no,
931 void *arg)
932 {
933 struct manage_sets_args *args;
934
935 args = (struct manage_sets_args *)arg;
936 if (no->set != (uint8_t)args->set)
937 return (0);
938 if (ipfw_objhash_lookup_name_type(ni, args->new_set,
939 no->etlv, no->name) != NULL)
940 return (EEXIST);
941 return (0);
942 }
943
944 /*
945 * Generic function to handler moving and swapping sets.
946 */
947 int
ipfw_obj_manage_sets(struct namedobj_instance * ni,uint16_t type,uint16_t set,uint8_t new_set,enum ipfw_sets_cmd cmd)948 ipfw_obj_manage_sets(struct namedobj_instance *ni, uint16_t type,
949 uint16_t set, uint8_t new_set, enum ipfw_sets_cmd cmd)
950 {
951 struct manage_sets_args args;
952 struct named_object *no;
953
954 args.set = set;
955 args.new_set = new_set;
956 switch (cmd) {
957 case SWAP_ALL:
958 return (ipfw_objhash_foreach_type(ni, swap_sets_cb,
959 &args, type));
960 case TEST_ALL:
961 return (ipfw_objhash_foreach_type(ni, test_sets_cb,
962 &args, type));
963 case MOVE_ALL:
964 return (ipfw_objhash_foreach_type(ni, move_sets_cb,
965 &args, type));
966 case COUNT_ONE:
967 /*
968 * @set used to pass kidx.
969 * When @new_set is zero - reset object counter,
970 * otherwise increment it.
971 */
972 no = ipfw_objhash_lookup_kidx(ni, set);
973 if (new_set != 0)
974 no->ocnt++;
975 else
976 no->ocnt = 0;
977 return (0);
978 case TEST_ONE:
979 /* @set used to pass kidx */
980 no = ipfw_objhash_lookup_kidx(ni, set);
981 /*
982 * First check number of references:
983 * when it differs, this mean other rules are holding
984 * reference to given object, so it is not possible to
985 * change its set. Note that refcnt may account references
986 * to some going-to-be-added rules. Since we don't know
987 * their numbers (and even if they will be added) it is
988 * perfectly OK to return error here.
989 */
990 if (no->ocnt != no->refcnt)
991 return (EBUSY);
992 if (ipfw_objhash_lookup_name_type(ni, new_set, type,
993 no->name) != NULL)
994 return (EEXIST);
995 return (0);
996 case MOVE_ONE:
997 /* @set used to pass kidx */
998 no = ipfw_objhash_lookup_kidx(ni, set);
999 no->set = new_set;
1000 return (0);
1001 }
1002 return (EINVAL);
1003 }
1004
1005 /*
1006 * Delete rules matching range @rt.
1007 * Saves number of deleted rules in @ndel.
1008 *
1009 * Returns 0 on success.
1010 */
1011 static int
delete_range(struct ip_fw_chain * chain,ipfw_range_tlv * rt,int * ndel)1012 delete_range(struct ip_fw_chain *chain, ipfw_range_tlv *rt, int *ndel)
1013 {
1014 struct ip_fw *reap, *rule, **map;
1015 int end, start;
1016 int i, n, ndyn, ofs;
1017
1018 reap = NULL;
1019 IPFW_UH_WLOCK(chain); /* arbitrate writers */
1020
1021 /*
1022 * Stage 1: Determine range to inspect.
1023 * Range is half-inclusive, e.g [start, end).
1024 */
1025 start = 0;
1026 end = chain->n_rules - 1;
1027
1028 if ((rt->flags & IPFW_RCFLAG_RANGE) != 0) {
1029 start = ipfw_find_rule(chain, rt->start_rule, 0);
1030
1031 if (rt->end_rule >= IPFW_DEFAULT_RULE)
1032 rt->end_rule = IPFW_DEFAULT_RULE - 1;
1033 end = ipfw_find_rule(chain, rt->end_rule, UINT32_MAX);
1034 }
1035
1036 if (rt->flags & IPFW_RCFLAG_DYNAMIC) {
1037 /*
1038 * Requested deleting only for dynamic states.
1039 */
1040 *ndel = 0;
1041 ipfw_expire_dyn_states(chain, rt);
1042 IPFW_UH_WUNLOCK(chain);
1043 return (0);
1044 }
1045
1046 /* Allocate new map of the same size */
1047 map = get_map(chain, 0, 1 /* locked */);
1048 if (map == NULL) {
1049 IPFW_UH_WUNLOCK(chain);
1050 return (ENOMEM);
1051 }
1052
1053 n = 0;
1054 ndyn = 0;
1055 ofs = start;
1056 /* 1. bcopy the initial part of the map */
1057 if (start > 0)
1058 bcopy(chain->map, map, start * sizeof(struct ip_fw *));
1059 /* 2. copy active rules between start and end */
1060 for (i = start; i < end; i++) {
1061 rule = chain->map[i];
1062 if (ipfw_match_range(rule, rt) == 0) {
1063 map[ofs++] = rule;
1064 continue;
1065 }
1066
1067 n++;
1068 if (ipfw_is_dyn_rule(rule) != 0)
1069 ndyn++;
1070 }
1071 /* 3. copy the final part of the map */
1072 bcopy(chain->map + end, map + ofs,
1073 (chain->n_rules - end) * sizeof(struct ip_fw *));
1074 /* 4. recalculate skipto cache */
1075 update_skipto_cache(chain, map);
1076 /* 5. swap the maps (under UH_WLOCK + WHLOCK) */
1077 map = swap_map(chain, map, chain->n_rules - n);
1078 /* 6. Remove all dynamic states originated by deleted rules */
1079 if (ndyn > 0)
1080 ipfw_expire_dyn_states(chain, rt);
1081 /* 7. now remove the rules deleted from the old map */
1082 for (i = start; i < end; i++) {
1083 rule = map[i];
1084 if (ipfw_match_range(rule, rt) == 0)
1085 continue;
1086 chain->static_len -= RULEUSIZE0(rule);
1087 ipfw_reap_add(chain, &reap, rule);
1088 }
1089 IPFW_UH_WUNLOCK(chain);
1090
1091 ipfw_reap_rules(reap);
1092 if (map != NULL)
1093 free(map, M_IPFW);
1094 *ndel = n;
1095 return (0);
1096 }
1097
1098 static int
move_objects(struct ip_fw_chain * ch,ipfw_range_tlv * rt)1099 move_objects(struct ip_fw_chain *ch, ipfw_range_tlv *rt)
1100 {
1101 struct opcode_obj_rewrite *rw;
1102 struct ip_fw *rule;
1103 ipfw_insn *cmd;
1104 int cmdlen, i, l, c;
1105 uint16_t kidx;
1106
1107 IPFW_UH_WLOCK_ASSERT(ch);
1108
1109 /* Stage 1: count number of references by given rules */
1110 for (c = 0, i = 0; i < ch->n_rules - 1; i++) {
1111 rule = ch->map[i];
1112 if (ipfw_match_range(rule, rt) == 0)
1113 continue;
1114 if (rule->set == rt->new_set) /* nothing to do */
1115 continue;
1116 /* Search opcodes with named objects */
1117 for (l = rule->cmd_len, cmdlen = 0, cmd = rule->cmd;
1118 l > 0; l -= cmdlen, cmd += cmdlen) {
1119 cmdlen = F_LEN(cmd);
1120 rw = find_op_rw(cmd, &kidx, NULL);
1121 if (rw == NULL || rw->manage_sets == NULL)
1122 continue;
1123 /*
1124 * When manage_sets() returns non-zero value to
1125 * COUNT_ONE command, consider this as an object
1126 * doesn't support sets (e.g. disabled with sysctl).
1127 * So, skip checks for this object.
1128 */
1129 if (rw->manage_sets(ch, kidx, 1, COUNT_ONE) != 0)
1130 continue;
1131 c++;
1132 }
1133 }
1134 if (c == 0) /* No objects found */
1135 return (0);
1136 /* Stage 2: verify "ownership" */
1137 for (c = 0, i = 0; (i < ch->n_rules - 1) && c == 0; i++) {
1138 rule = ch->map[i];
1139 if (ipfw_match_range(rule, rt) == 0)
1140 continue;
1141 if (rule->set == rt->new_set) /* nothing to do */
1142 continue;
1143 /* Search opcodes with named objects */
1144 for (l = rule->cmd_len, cmdlen = 0, cmd = rule->cmd;
1145 l > 0 && c == 0; l -= cmdlen, cmd += cmdlen) {
1146 cmdlen = F_LEN(cmd);
1147 rw = find_op_rw(cmd, &kidx, NULL);
1148 if (rw == NULL || rw->manage_sets == NULL)
1149 continue;
1150 /* Test for ownership and conflicting names */
1151 c = rw->manage_sets(ch, kidx,
1152 (uint8_t)rt->new_set, TEST_ONE);
1153 }
1154 }
1155 /* Stage 3: change set and cleanup */
1156 for (i = 0; i < ch->n_rules - 1; i++) {
1157 rule = ch->map[i];
1158 if (ipfw_match_range(rule, rt) == 0)
1159 continue;
1160 if (rule->set == rt->new_set) /* nothing to do */
1161 continue;
1162 /* Search opcodes with named objects */
1163 for (l = rule->cmd_len, cmdlen = 0, cmd = rule->cmd;
1164 l > 0; l -= cmdlen, cmd += cmdlen) {
1165 cmdlen = F_LEN(cmd);
1166 rw = find_op_rw(cmd, &kidx, NULL);
1167 if (rw == NULL || rw->manage_sets == NULL)
1168 continue;
1169 /* cleanup object counter */
1170 rw->manage_sets(ch, kidx,
1171 0 /* reset counter */, COUNT_ONE);
1172 if (c != 0)
1173 continue;
1174 /* change set */
1175 rw->manage_sets(ch, kidx,
1176 (uint8_t)rt->new_set, MOVE_ONE);
1177 }
1178 }
1179 return (c);
1180 }
1181
1182 /*
1183 * Changes set of given rule rannge @rt
1184 * with each other.
1185 *
1186 * Returns 0 on success.
1187 */
1188 static int
move_range(struct ip_fw_chain * chain,ipfw_range_tlv * rt)1189 move_range(struct ip_fw_chain *chain, ipfw_range_tlv *rt)
1190 {
1191 struct ip_fw *rule;
1192 int i;
1193
1194 IPFW_UH_WLOCK(chain);
1195
1196 /*
1197 * Move rules with matching paramenerts to a new set.
1198 * This one is much more complex. We have to ensure
1199 * that all referenced tables (if any) are referenced
1200 * by given rule subset only. Otherwise, we can't move
1201 * them to new set and have to return error.
1202 */
1203 if ((i = move_objects(chain, rt)) != 0) {
1204 IPFW_UH_WUNLOCK(chain);
1205 return (i);
1206 }
1207
1208 /* XXX: We have to do swap holding WLOCK */
1209 for (i = 0; i < chain->n_rules; i++) {
1210 rule = chain->map[i];
1211 if (ipfw_match_range(rule, rt) == 0)
1212 continue;
1213 rule->set = rt->new_set;
1214 }
1215
1216 IPFW_UH_WUNLOCK(chain);
1217
1218 return (0);
1219 }
1220
1221 /*
1222 * Returns pointer to action instruction, skips all possible rule
1223 * modifiers like O_LOG, O_TAG, O_ALTQ.
1224 */
1225 ipfw_insn *
ipfw_get_action(struct ip_fw * rule)1226 ipfw_get_action(struct ip_fw *rule)
1227 {
1228 ipfw_insn *cmd;
1229 int l, cmdlen;
1230
1231 cmd = ACTION_PTR(rule);
1232 l = rule->cmd_len - rule->act_ofs;
1233 while (l > 0) {
1234 switch (cmd->opcode) {
1235 case O_ALTQ:
1236 case O_LOG:
1237 case O_TAG:
1238 break;
1239 default:
1240 return (cmd);
1241 }
1242 cmdlen = F_LEN(cmd);
1243 l -= cmdlen;
1244 cmd += cmdlen;
1245 }
1246 panic("%s: rule (%p) has not action opcode", __func__, rule);
1247 return (NULL);
1248 }
1249
1250 /*
1251 * Clear counters for a specific rule.
1252 * Normally run under IPFW_UH_RLOCK, but these are idempotent ops
1253 * so we only care that rules do not disappear.
1254 */
1255 static void
clear_counters(struct ip_fw * rule,int log_only)1256 clear_counters(struct ip_fw *rule, int log_only)
1257 {
1258 ipfw_insn_log *l = (ipfw_insn_log *)ACTION_PTR(rule);
1259
1260 if (log_only == 0)
1261 IPFW_ZERO_RULE_COUNTER(rule);
1262 if (l->o.opcode == O_LOG)
1263 l->log_left = l->max_log;
1264 }
1265
1266 /*
1267 * Flushes rules counters and/or log values on matching range.
1268 *
1269 * Returns number of items cleared.
1270 */
1271 static int
clear_range(struct ip_fw_chain * chain,ipfw_range_tlv * rt,int log_only)1272 clear_range(struct ip_fw_chain *chain, ipfw_range_tlv *rt, int log_only)
1273 {
1274 struct ip_fw *rule;
1275 int num;
1276 int i;
1277
1278 num = 0;
1279 rt->flags |= IPFW_RCFLAG_DEFAULT;
1280
1281 IPFW_UH_WLOCK(chain); /* arbitrate writers */
1282 for (i = 0; i < chain->n_rules; i++) {
1283 rule = chain->map[i];
1284 if (ipfw_match_range(rule, rt) == 0)
1285 continue;
1286 clear_counters(rule, log_only);
1287 num++;
1288 }
1289 IPFW_UH_WUNLOCK(chain);
1290
1291 return (num);
1292 }
1293
1294 static int
check_range_tlv(ipfw_range_tlv * rt)1295 check_range_tlv(ipfw_range_tlv *rt)
1296 {
1297
1298 if (rt->head.length != sizeof(*rt))
1299 return (1);
1300 if (rt->start_rule > rt->end_rule)
1301 return (1);
1302 if (rt->set >= IPFW_MAX_SETS || rt->new_set >= IPFW_MAX_SETS)
1303 return (1);
1304
1305 if ((rt->flags & IPFW_RCFLAG_USER) != rt->flags)
1306 return (1);
1307
1308 return (0);
1309 }
1310
1311 /*
1312 * Delete rules matching specified parameters
1313 * Data layout (v0)(current):
1314 * Request: [ ipfw_obj_header ipfw_range_tlv ]
1315 * Reply: [ ipfw_obj_header ipfw_range_tlv ]
1316 *
1317 * Saves number of deleted rules in ipfw_range_tlv->new_set.
1318 *
1319 * Returns 0 on success.
1320 */
1321 static int
del_rules(struct ip_fw_chain * chain,ip_fw3_opheader * op3,struct sockopt_data * sd)1322 del_rules(struct ip_fw_chain *chain, ip_fw3_opheader *op3,
1323 struct sockopt_data *sd)
1324 {
1325 ipfw_range_header *rh;
1326 int error, ndel;
1327
1328 if (sd->valsize != sizeof(*rh))
1329 return (EINVAL);
1330
1331 rh = (ipfw_range_header *)ipfw_get_sopt_space(sd, sd->valsize);
1332
1333 if (check_range_tlv(&rh->range) != 0)
1334 return (EINVAL);
1335
1336 ndel = 0;
1337 if ((error = delete_range(chain, &rh->range, &ndel)) != 0)
1338 return (error);
1339
1340 /* Save number of rules deleted */
1341 rh->range.new_set = ndel;
1342 return (0);
1343 }
1344
1345 /*
1346 * Move rules/sets matching specified parameters
1347 * Data layout (v0)(current):
1348 * Request: [ ipfw_obj_header ipfw_range_tlv ]
1349 *
1350 * Returns 0 on success.
1351 */
1352 static int
move_rules(struct ip_fw_chain * chain,ip_fw3_opheader * op3,struct sockopt_data * sd)1353 move_rules(struct ip_fw_chain *chain, ip_fw3_opheader *op3,
1354 struct sockopt_data *sd)
1355 {
1356 ipfw_range_header *rh;
1357
1358 if (sd->valsize != sizeof(*rh))
1359 return (EINVAL);
1360
1361 rh = (ipfw_range_header *)ipfw_get_sopt_space(sd, sd->valsize);
1362
1363 if (check_range_tlv(&rh->range) != 0)
1364 return (EINVAL);
1365
1366 return (move_range(chain, &rh->range));
1367 }
1368
1369 /*
1370 * Clear rule accounting data matching specified parameters
1371 * Data layout (v0)(current):
1372 * Request: [ ipfw_obj_header ipfw_range_tlv ]
1373 * Reply: [ ipfw_obj_header ipfw_range_tlv ]
1374 *
1375 * Saves number of cleared rules in ipfw_range_tlv->new_set.
1376 *
1377 * Returns 0 on success.
1378 */
1379 static int
clear_rules(struct ip_fw_chain * chain,ip_fw3_opheader * op3,struct sockopt_data * sd)1380 clear_rules(struct ip_fw_chain *chain, ip_fw3_opheader *op3,
1381 struct sockopt_data *sd)
1382 {
1383 ipfw_range_header *rh;
1384 int log_only, num;
1385 char *msg;
1386
1387 if (sd->valsize != sizeof(*rh))
1388 return (EINVAL);
1389
1390 rh = (ipfw_range_header *)ipfw_get_sopt_space(sd, sd->valsize);
1391
1392 if (check_range_tlv(&rh->range) != 0)
1393 return (EINVAL);
1394
1395 log_only = (op3->opcode == IP_FW_XRESETLOG);
1396
1397 num = clear_range(chain, &rh->range, log_only);
1398
1399 if (rh->range.flags & IPFW_RCFLAG_ALL)
1400 msg = log_only ? "All logging counts reset" :
1401 "Accounting cleared";
1402 else
1403 msg = log_only ? "logging count reset" : "cleared";
1404
1405 if (V_fw_verbose) {
1406 int lev = LOG_SECURITY | LOG_NOTICE;
1407 log(lev, "ipfw: %s.\n", msg);
1408 }
1409
1410 /* Save number of rules cleared */
1411 rh->range.new_set = num;
1412 return (0);
1413 }
1414
1415 static void
enable_sets(struct ip_fw_chain * chain,ipfw_range_tlv * rt)1416 enable_sets(struct ip_fw_chain *chain, ipfw_range_tlv *rt)
1417 {
1418 uint32_t v_set;
1419
1420 IPFW_UH_WLOCK_ASSERT(chain);
1421
1422 /* Change enabled/disabled sets mask */
1423 v_set = (V_set_disable | rt->set) & ~rt->new_set;
1424 v_set &= ~(1 << RESVD_SET); /* set RESVD_SET always enabled */
1425 IPFW_WLOCK(chain);
1426 V_set_disable = v_set;
1427 IPFW_WUNLOCK(chain);
1428 }
1429
1430 static int
swap_sets(struct ip_fw_chain * chain,ipfw_range_tlv * rt,int mv)1431 swap_sets(struct ip_fw_chain *chain, ipfw_range_tlv *rt, int mv)
1432 {
1433 struct opcode_obj_rewrite *rw;
1434 struct ip_fw *rule;
1435 int i;
1436
1437 IPFW_UH_WLOCK_ASSERT(chain);
1438
1439 if (rt->set == rt->new_set) /* nothing to do */
1440 return (0);
1441
1442 if (mv != 0) {
1443 /*
1444 * Berfore moving the rules we need to check that
1445 * there aren't any conflicting named objects.
1446 */
1447 for (rw = ctl3_rewriters;
1448 rw < ctl3_rewriters + ctl3_rsize; rw++) {
1449 if (rw->manage_sets == NULL)
1450 continue;
1451 i = rw->manage_sets(chain, (uint8_t)rt->set,
1452 (uint8_t)rt->new_set, TEST_ALL);
1453 if (i != 0)
1454 return (EEXIST);
1455 }
1456 }
1457 /* Swap or move two sets */
1458 for (i = 0; i < chain->n_rules - 1; i++) {
1459 rule = chain->map[i];
1460 if (rule->set == (uint8_t)rt->set)
1461 rule->set = (uint8_t)rt->new_set;
1462 else if (rule->set == (uint8_t)rt->new_set && mv == 0)
1463 rule->set = (uint8_t)rt->set;
1464 }
1465 for (rw = ctl3_rewriters; rw < ctl3_rewriters + ctl3_rsize; rw++) {
1466 if (rw->manage_sets == NULL)
1467 continue;
1468 rw->manage_sets(chain, (uint8_t)rt->set,
1469 (uint8_t)rt->new_set, mv != 0 ? MOVE_ALL: SWAP_ALL);
1470 }
1471 return (0);
1472 }
1473
1474 /*
1475 * Swaps or moves set
1476 * Data layout (v0)(current):
1477 * Request: [ ipfw_obj_header ipfw_range_tlv ]
1478 *
1479 * Returns 0 on success.
1480 */
1481 static int
manage_sets(struct ip_fw_chain * chain,ip_fw3_opheader * op3,struct sockopt_data * sd)1482 manage_sets(struct ip_fw_chain *chain, ip_fw3_opheader *op3,
1483 struct sockopt_data *sd)
1484 {
1485 ipfw_range_header *rh;
1486 int ret;
1487
1488 if (sd->valsize != sizeof(*rh))
1489 return (EINVAL);
1490
1491 rh = (ipfw_range_header *)ipfw_get_sopt_space(sd, sd->valsize);
1492
1493 if (rh->range.head.length != sizeof(ipfw_range_tlv))
1494 return (1);
1495 /* enable_sets() expects bitmasks. */
1496 if (op3->opcode != IP_FW_SET_ENABLE &&
1497 (rh->range.set >= IPFW_MAX_SETS ||
1498 rh->range.new_set >= IPFW_MAX_SETS))
1499 return (EINVAL);
1500
1501 ret = 0;
1502 IPFW_UH_WLOCK(chain);
1503 switch (op3->opcode) {
1504 case IP_FW_SET_SWAP:
1505 case IP_FW_SET_MOVE:
1506 ret = swap_sets(chain, &rh->range,
1507 op3->opcode == IP_FW_SET_MOVE);
1508 break;
1509 case IP_FW_SET_ENABLE:
1510 enable_sets(chain, &rh->range);
1511 break;
1512 }
1513 IPFW_UH_WUNLOCK(chain);
1514
1515 return (ret);
1516 }
1517
1518 /**
1519 * Remove all rules with given number, or do set manipulation.
1520 * Assumes chain != NULL && *chain != NULL.
1521 *
1522 * The argument is an uint32_t. The low 16 bit are the rule or set number;
1523 * the next 8 bits are the new set; the top 8 bits indicate the command:
1524 *
1525 * 0 delete rules numbered "rulenum"
1526 * 1 delete rules in set "rulenum"
1527 * 2 move rules "rulenum" to set "new_set"
1528 * 3 move rules from set "rulenum" to set "new_set"
1529 * 4 swap sets "rulenum" and "new_set"
1530 * 5 delete rules "rulenum" and set "new_set"
1531 */
1532 static int
del_entry(struct ip_fw_chain * chain,uint32_t arg)1533 del_entry(struct ip_fw_chain *chain, uint32_t arg)
1534 {
1535 uint32_t num; /* rule number or old_set */
1536 uint8_t cmd, new_set;
1537 int do_del, ndel;
1538 int error = 0;
1539 ipfw_range_tlv rt;
1540
1541 num = arg & 0xffff;
1542 cmd = (arg >> 24) & 0xff;
1543 new_set = (arg >> 16) & 0xff;
1544
1545 if (cmd > 5 || new_set > RESVD_SET)
1546 return EINVAL;
1547 if (cmd == 0 || cmd == 2 || cmd == 5) {
1548 if (num >= IPFW_DEFAULT_RULE)
1549 return EINVAL;
1550 } else {
1551 if (num > RESVD_SET) /* old_set */
1552 return EINVAL;
1553 }
1554
1555 /* Convert old requests into new representation */
1556 memset(&rt, 0, sizeof(rt));
1557 rt.start_rule = num;
1558 rt.end_rule = num;
1559 rt.set = num;
1560 rt.new_set = new_set;
1561 do_del = 0;
1562
1563 switch (cmd) {
1564 case 0: /* delete rules numbered "rulenum" */
1565 if (num == 0)
1566 rt.flags |= IPFW_RCFLAG_ALL;
1567 else
1568 rt.flags |= IPFW_RCFLAG_RANGE;
1569 do_del = 1;
1570 break;
1571 case 1: /* delete rules in set "rulenum" */
1572 rt.flags |= IPFW_RCFLAG_SET;
1573 do_del = 1;
1574 break;
1575 case 5: /* delete rules "rulenum" and set "new_set" */
1576 rt.flags |= IPFW_RCFLAG_RANGE | IPFW_RCFLAG_SET;
1577 rt.set = new_set;
1578 rt.new_set = 0;
1579 do_del = 1;
1580 break;
1581 case 2: /* move rules "rulenum" to set "new_set" */
1582 rt.flags |= IPFW_RCFLAG_RANGE;
1583 break;
1584 case 3: /* move rules from set "rulenum" to set "new_set" */
1585 IPFW_UH_WLOCK(chain);
1586 error = swap_sets(chain, &rt, 1);
1587 IPFW_UH_WUNLOCK(chain);
1588 return (error);
1589 case 4: /* swap sets "rulenum" and "new_set" */
1590 IPFW_UH_WLOCK(chain);
1591 error = swap_sets(chain, &rt, 0);
1592 IPFW_UH_WUNLOCK(chain);
1593 return (error);
1594 default:
1595 return (ENOTSUP);
1596 }
1597
1598 if (do_del != 0) {
1599 if ((error = delete_range(chain, &rt, &ndel)) != 0)
1600 return (error);
1601
1602 if (ndel == 0 && (cmd != 1 && num != 0))
1603 return (EINVAL);
1604
1605 return (0);
1606 }
1607
1608 return (move_range(chain, &rt));
1609 }
1610
1611 /**
1612 * Reset some or all counters on firewall rules.
1613 * The argument `arg' is an u_int32_t. The low 16 bit are the rule number,
1614 * the next 8 bits are the set number, the top 8 bits are the command:
1615 * 0 work with rules from all set's;
1616 * 1 work with rules only from specified set.
1617 * Specified rule number is zero if we want to clear all entries.
1618 * log_only is 1 if we only want to reset logs, zero otherwise.
1619 */
1620 static int
zero_entry(struct ip_fw_chain * chain,u_int32_t arg,int log_only)1621 zero_entry(struct ip_fw_chain *chain, u_int32_t arg, int log_only)
1622 {
1623 struct ip_fw *rule;
1624 char *msg;
1625 int i;
1626
1627 uint16_t rulenum = arg & 0xffff;
1628 uint8_t set = (arg >> 16) & 0xff;
1629 uint8_t cmd = (arg >> 24) & 0xff;
1630
1631 if (cmd > 1)
1632 return (EINVAL);
1633 if (cmd == 1 && set > RESVD_SET)
1634 return (EINVAL);
1635
1636 IPFW_UH_RLOCK(chain);
1637 if (rulenum == 0) {
1638 V_norule_counter = 0;
1639 for (i = 0; i < chain->n_rules; i++) {
1640 rule = chain->map[i];
1641 /* Skip rules not in our set. */
1642 if (cmd == 1 && rule->set != set)
1643 continue;
1644 clear_counters(rule, log_only);
1645 }
1646 msg = log_only ? "All logging counts reset" :
1647 "Accounting cleared";
1648 } else {
1649 int cleared = 0;
1650 for (i = 0; i < chain->n_rules; i++) {
1651 rule = chain->map[i];
1652 if (rule->rulenum == rulenum) {
1653 if (cmd == 0 || rule->set == set)
1654 clear_counters(rule, log_only);
1655 cleared = 1;
1656 }
1657 if (rule->rulenum > rulenum)
1658 break;
1659 }
1660 if (!cleared) { /* we did not find any matching rules */
1661 IPFW_UH_RUNLOCK(chain);
1662 return (EINVAL);
1663 }
1664 msg = log_only ? "logging count reset" : "cleared";
1665 }
1666 IPFW_UH_RUNLOCK(chain);
1667
1668 if (V_fw_verbose) {
1669 int lev = LOG_SECURITY | LOG_NOTICE;
1670
1671 if (rulenum)
1672 log(lev, "ipfw: Entry %d %s.\n", rulenum, msg);
1673 else
1674 log(lev, "ipfw: %s.\n", msg);
1675 }
1676 return (0);
1677 }
1678
1679
1680 /*
1681 * Check rule head in FreeBSD11 format
1682 *
1683 */
1684 static int
check_ipfw_rule1(struct ip_fw_rule * rule,int size,struct rule_check_info * ci)1685 check_ipfw_rule1(struct ip_fw_rule *rule, int size,
1686 struct rule_check_info *ci)
1687 {
1688 int l;
1689
1690 if (size < sizeof(*rule)) {
1691 printf("ipfw: rule too short\n");
1692 return (EINVAL);
1693 }
1694
1695 /* Check for valid cmd_len */
1696 l = roundup2(RULESIZE(rule), sizeof(uint64_t));
1697 if (l != size) {
1698 printf("ipfw: size mismatch (have %d want %d)\n", size, l);
1699 return (EINVAL);
1700 }
1701 if (rule->act_ofs >= rule->cmd_len) {
1702 printf("ipfw: bogus action offset (%u > %u)\n",
1703 rule->act_ofs, rule->cmd_len - 1);
1704 return (EINVAL);
1705 }
1706
1707 if (rule->rulenum > IPFW_DEFAULT_RULE - 1)
1708 return (EINVAL);
1709
1710 return (check_ipfw_rule_body(rule->cmd, rule->cmd_len, ci));
1711 }
1712
1713 /*
1714 * Check rule head in FreeBSD8 format
1715 *
1716 */
1717 static int
check_ipfw_rule0(struct ip_fw_rule0 * rule,int size,struct rule_check_info * ci)1718 check_ipfw_rule0(struct ip_fw_rule0 *rule, int size,
1719 struct rule_check_info *ci)
1720 {
1721 int l;
1722
1723 if (size < sizeof(*rule)) {
1724 printf("ipfw: rule too short\n");
1725 return (EINVAL);
1726 }
1727
1728 /* Check for valid cmd_len */
1729 l = sizeof(*rule) + rule->cmd_len * 4 - 4;
1730 if (l != size) {
1731 printf("ipfw: size mismatch (have %d want %d)\n", size, l);
1732 return (EINVAL);
1733 }
1734 if (rule->act_ofs >= rule->cmd_len) {
1735 printf("ipfw: bogus action offset (%u > %u)\n",
1736 rule->act_ofs, rule->cmd_len - 1);
1737 return (EINVAL);
1738 }
1739
1740 if (rule->rulenum > IPFW_DEFAULT_RULE - 1)
1741 return (EINVAL);
1742
1743 return (check_ipfw_rule_body(rule->cmd, rule->cmd_len, ci));
1744 }
1745
1746 static int
check_ipfw_rule_body(ipfw_insn * cmd,int cmd_len,struct rule_check_info * ci)1747 check_ipfw_rule_body(ipfw_insn *cmd, int cmd_len, struct rule_check_info *ci)
1748 {
1749 int cmdlen, l;
1750 int have_action;
1751
1752 have_action = 0;
1753
1754 /*
1755 * Now go for the individual checks. Very simple ones, basically only
1756 * instruction sizes.
1757 */
1758 for (l = cmd_len; l > 0 ; l -= cmdlen, cmd += cmdlen) {
1759 cmdlen = F_LEN(cmd);
1760 if (cmdlen > l) {
1761 printf("ipfw: opcode %d size truncated\n",
1762 cmd->opcode);
1763 return EINVAL;
1764 }
1765 switch (cmd->opcode) {
1766 case O_PROBE_STATE:
1767 case O_KEEP_STATE:
1768 if (cmdlen != F_INSN_SIZE(ipfw_insn))
1769 goto bad_size;
1770 ci->object_opcodes++;
1771 break;
1772 case O_PROTO:
1773 case O_IP_SRC_ME:
1774 case O_IP_DST_ME:
1775 case O_LAYER2:
1776 case O_IN:
1777 case O_FRAG:
1778 case O_DIVERTED:
1779 case O_IPOPT:
1780 case O_IPTOS:
1781 case O_IPPRECEDENCE:
1782 case O_IPVER:
1783 case O_SOCKARG:
1784 case O_TCPFLAGS:
1785 case O_TCPOPTS:
1786 case O_ESTAB:
1787 case O_VERREVPATH:
1788 case O_VERSRCREACH:
1789 case O_ANTISPOOF:
1790 case O_IPSEC:
1791 #ifdef INET6
1792 case O_IP6_SRC_ME:
1793 case O_IP6_DST_ME:
1794 case O_EXT_HDR:
1795 case O_IP6:
1796 #endif
1797 case O_IP4:
1798 case O_TAG:
1799 case O_SKIP_ACTION:
1800 if (cmdlen != F_INSN_SIZE(ipfw_insn))
1801 goto bad_size;
1802 break;
1803
1804 case O_EXTERNAL_ACTION:
1805 if (cmd->arg1 == 0 ||
1806 cmdlen != F_INSN_SIZE(ipfw_insn)) {
1807 printf("ipfw: invalid external "
1808 "action opcode\n");
1809 return (EINVAL);
1810 }
1811 ci->object_opcodes++;
1812 /*
1813 * Do we have O_EXTERNAL_INSTANCE or O_EXTERNAL_DATA
1814 * opcode?
1815 */
1816 if (l != cmdlen) {
1817 l -= cmdlen;
1818 cmd += cmdlen;
1819 cmdlen = F_LEN(cmd);
1820 if (cmd->opcode == O_EXTERNAL_DATA)
1821 goto check_action;
1822 if (cmd->opcode != O_EXTERNAL_INSTANCE) {
1823 printf("ipfw: invalid opcode "
1824 "next to external action %u\n",
1825 cmd->opcode);
1826 return (EINVAL);
1827 }
1828 if (cmd->arg1 == 0 ||
1829 cmdlen != F_INSN_SIZE(ipfw_insn)) {
1830 printf("ipfw: invalid external "
1831 "action instance opcode\n");
1832 return (EINVAL);
1833 }
1834 ci->object_opcodes++;
1835 }
1836 goto check_action;
1837
1838 case O_FIB:
1839 if (cmdlen != F_INSN_SIZE(ipfw_insn))
1840 goto bad_size;
1841 if (cmd->arg1 >= rt_numfibs) {
1842 printf("ipfw: invalid fib number %d\n",
1843 cmd->arg1);
1844 return EINVAL;
1845 }
1846 break;
1847
1848 case O_SETFIB:
1849 if (cmdlen != F_INSN_SIZE(ipfw_insn))
1850 goto bad_size;
1851 if ((cmd->arg1 != IP_FW_TARG) &&
1852 ((cmd->arg1 & 0x7FFF) >= rt_numfibs)) {
1853 printf("ipfw: invalid fib number %d\n",
1854 cmd->arg1 & 0x7FFF);
1855 return EINVAL;
1856 }
1857 goto check_action;
1858
1859 case O_UID:
1860 case O_GID:
1861 case O_JAIL:
1862 case O_IP_SRC:
1863 case O_IP_DST:
1864 case O_TCPSEQ:
1865 case O_TCPACK:
1866 case O_PROB:
1867 case O_ICMPTYPE:
1868 if (cmdlen != F_INSN_SIZE(ipfw_insn_u32))
1869 goto bad_size;
1870 break;
1871
1872 case O_LIMIT:
1873 if (cmdlen != F_INSN_SIZE(ipfw_insn_limit))
1874 goto bad_size;
1875 ci->object_opcodes++;
1876 break;
1877
1878 case O_LOG:
1879 if (cmdlen != F_INSN_SIZE(ipfw_insn_log))
1880 goto bad_size;
1881
1882 ((ipfw_insn_log *)cmd)->log_left =
1883 ((ipfw_insn_log *)cmd)->max_log;
1884
1885 break;
1886
1887 case O_IP_SRC_MASK:
1888 case O_IP_DST_MASK:
1889 /* only odd command lengths */
1890 if ((cmdlen & 1) == 0)
1891 goto bad_size;
1892 break;
1893
1894 case O_IP_SRC_SET:
1895 case O_IP_DST_SET:
1896 if (cmd->arg1 == 0 || cmd->arg1 > 256) {
1897 printf("ipfw: invalid set size %d\n",
1898 cmd->arg1);
1899 return EINVAL;
1900 }
1901 if (cmdlen != F_INSN_SIZE(ipfw_insn_u32) +
1902 (cmd->arg1+31)/32 )
1903 goto bad_size;
1904 break;
1905
1906 case O_IP_SRC_LOOKUP:
1907 if (cmdlen > F_INSN_SIZE(ipfw_insn_u32))
1908 goto bad_size;
1909 case O_IP_DST_LOOKUP:
1910 if (cmd->arg1 >= V_fw_tables_max) {
1911 printf("ipfw: invalid table number %d\n",
1912 cmd->arg1);
1913 return (EINVAL);
1914 }
1915 if (cmdlen != F_INSN_SIZE(ipfw_insn) &&
1916 cmdlen != F_INSN_SIZE(ipfw_insn_u32) + 1 &&
1917 cmdlen != F_INSN_SIZE(ipfw_insn_u32))
1918 goto bad_size;
1919 ci->object_opcodes++;
1920 break;
1921 case O_IP_FLOW_LOOKUP:
1922 if (cmd->arg1 >= V_fw_tables_max) {
1923 printf("ipfw: invalid table number %d\n",
1924 cmd->arg1);
1925 return (EINVAL);
1926 }
1927 if (cmdlen != F_INSN_SIZE(ipfw_insn) &&
1928 cmdlen != F_INSN_SIZE(ipfw_insn_u32))
1929 goto bad_size;
1930 ci->object_opcodes++;
1931 break;
1932 case O_MACADDR2:
1933 if (cmdlen != F_INSN_SIZE(ipfw_insn_mac))
1934 goto bad_size;
1935 break;
1936
1937 case O_NOP:
1938 case O_IPID:
1939 case O_IPTTL:
1940 case O_IPLEN:
1941 case O_TCPDATALEN:
1942 case O_TCPMSS:
1943 case O_TCPWIN:
1944 case O_TAGGED:
1945 if (cmdlen < 1 || cmdlen > 31)
1946 goto bad_size;
1947 break;
1948
1949 case O_DSCP:
1950 if (cmdlen != F_INSN_SIZE(ipfw_insn_u32) + 1)
1951 goto bad_size;
1952 break;
1953
1954 case O_MAC_TYPE:
1955 case O_IP_SRCPORT:
1956 case O_IP_DSTPORT: /* XXX artificial limit, 30 port pairs */
1957 if (cmdlen < 2 || cmdlen > 31)
1958 goto bad_size;
1959 break;
1960
1961 case O_RECV:
1962 case O_XMIT:
1963 case O_VIA:
1964 if (cmdlen != F_INSN_SIZE(ipfw_insn_if))
1965 goto bad_size;
1966 ci->object_opcodes++;
1967 break;
1968
1969 case O_ALTQ:
1970 if (cmdlen != F_INSN_SIZE(ipfw_insn_altq))
1971 goto bad_size;
1972 break;
1973
1974 case O_PIPE:
1975 case O_QUEUE:
1976 if (cmdlen != F_INSN_SIZE(ipfw_insn))
1977 goto bad_size;
1978 goto check_action;
1979
1980 case O_FORWARD_IP:
1981 if (cmdlen != F_INSN_SIZE(ipfw_insn_sa))
1982 goto bad_size;
1983 goto check_action;
1984 #ifdef INET6
1985 case O_FORWARD_IP6:
1986 if (cmdlen != F_INSN_SIZE(ipfw_insn_sa6))
1987 goto bad_size;
1988 goto check_action;
1989 #endif /* INET6 */
1990
1991 case O_DIVERT:
1992 case O_TEE:
1993 if (ip_divert_ptr == NULL)
1994 return EINVAL;
1995 else
1996 goto check_size;
1997 case O_NETGRAPH:
1998 case O_NGTEE:
1999 if (ng_ipfw_input_p == NULL)
2000 return EINVAL;
2001 else
2002 goto check_size;
2003 case O_NAT:
2004 if (!IPFW_NAT_LOADED)
2005 return EINVAL;
2006 if (cmdlen != F_INSN_SIZE(ipfw_insn_nat))
2007 goto bad_size;
2008 goto check_action;
2009 case O_CHECK_STATE:
2010 ci->object_opcodes++;
2011 /* FALLTHROUGH */
2012 case O_FORWARD_MAC: /* XXX not implemented yet */
2013 case O_COUNT:
2014 case O_ACCEPT:
2015 case O_DENY:
2016 case O_REJECT:
2017 case O_SETDSCP:
2018 #ifdef INET6
2019 case O_UNREACH6:
2020 #endif
2021 case O_SKIPTO:
2022 case O_REASS:
2023 case O_CALLRETURN:
2024 check_size:
2025 if (cmdlen != F_INSN_SIZE(ipfw_insn))
2026 goto bad_size;
2027 check_action:
2028 if (have_action) {
2029 printf("ipfw: opcode %d, multiple actions"
2030 " not allowed\n",
2031 cmd->opcode);
2032 return (EINVAL);
2033 }
2034 have_action = 1;
2035 if (l != cmdlen) {
2036 printf("ipfw: opcode %d, action must be"
2037 " last opcode\n",
2038 cmd->opcode);
2039 return (EINVAL);
2040 }
2041 break;
2042 #ifdef INET6
2043 case O_IP6_SRC:
2044 case O_IP6_DST:
2045 if (cmdlen != F_INSN_SIZE(struct in6_addr) +
2046 F_INSN_SIZE(ipfw_insn))
2047 goto bad_size;
2048 break;
2049
2050 case O_FLOW6ID:
2051 if (cmdlen != F_INSN_SIZE(ipfw_insn_u32) +
2052 ((ipfw_insn_u32 *)cmd)->o.arg1)
2053 goto bad_size;
2054 break;
2055
2056 case O_IP6_SRC_MASK:
2057 case O_IP6_DST_MASK:
2058 if ( !(cmdlen & 1) || cmdlen > 127)
2059 goto bad_size;
2060 break;
2061 case O_ICMP6TYPE:
2062 if( cmdlen != F_INSN_SIZE( ipfw_insn_icmp6 ) )
2063 goto bad_size;
2064 break;
2065 #endif
2066
2067 default:
2068 switch (cmd->opcode) {
2069 #ifndef INET6
2070 case O_IP6_SRC_ME:
2071 case O_IP6_DST_ME:
2072 case O_EXT_HDR:
2073 case O_IP6:
2074 case O_UNREACH6:
2075 case O_IP6_SRC:
2076 case O_IP6_DST:
2077 case O_FLOW6ID:
2078 case O_IP6_SRC_MASK:
2079 case O_IP6_DST_MASK:
2080 case O_ICMP6TYPE:
2081 printf("ipfw: no IPv6 support in kernel\n");
2082 return (EPROTONOSUPPORT);
2083 #endif
2084 default:
2085 printf("ipfw: opcode %d, unknown opcode\n",
2086 cmd->opcode);
2087 return (EINVAL);
2088 }
2089 }
2090 }
2091 if (have_action == 0) {
2092 printf("ipfw: missing action\n");
2093 return (EINVAL);
2094 }
2095 return 0;
2096
2097 bad_size:
2098 printf("ipfw: opcode %d size %d wrong\n",
2099 cmd->opcode, cmdlen);
2100 return (EINVAL);
2101 }
2102
2103
2104 /*
2105 * Translation of requests for compatibility with FreeBSD 7.2/8.
2106 * a static variable tells us if we have an old client from userland,
2107 * and if necessary we translate requests and responses between the
2108 * two formats.
2109 */
2110 static int is7 = 0;
2111
2112 struct ip_fw7 {
2113 struct ip_fw7 *next; /* linked list of rules */
2114 struct ip_fw7 *next_rule; /* ptr to next [skipto] rule */
2115 /* 'next_rule' is used to pass up 'set_disable' status */
2116
2117 uint16_t act_ofs; /* offset of action in 32-bit units */
2118 uint16_t cmd_len; /* # of 32-bit words in cmd */
2119 uint16_t rulenum; /* rule number */
2120 uint8_t set; /* rule set (0..31) */
2121 // #define RESVD_SET 31 /* set for default and persistent rules */
2122 uint8_t _pad; /* padding */
2123 // uint32_t id; /* rule id, only in v.8 */
2124 /* These fields are present in all rules. */
2125 uint64_t pcnt; /* Packet counter */
2126 uint64_t bcnt; /* Byte counter */
2127 uint32_t timestamp; /* tv_sec of last match */
2128
2129 ipfw_insn cmd[1]; /* storage for commands */
2130 };
2131
2132 static int convert_rule_to_7(struct ip_fw_rule0 *rule);
2133 static int convert_rule_to_8(struct ip_fw_rule0 *rule);
2134
2135 #ifndef RULESIZE7
2136 #define RULESIZE7(rule) (sizeof(struct ip_fw7) + \
2137 ((struct ip_fw7 *)(rule))->cmd_len * 4 - 4)
2138 #endif
2139
2140
2141 /*
2142 * Copy the static and dynamic rules to the supplied buffer
2143 * and return the amount of space actually used.
2144 * Must be run under IPFW_UH_RLOCK
2145 */
2146 static size_t
ipfw_getrules(struct ip_fw_chain * chain,void * buf,size_t space)2147 ipfw_getrules(struct ip_fw_chain *chain, void *buf, size_t space)
2148 {
2149 char *bp = buf;
2150 char *ep = bp + space;
2151 struct ip_fw *rule;
2152 struct ip_fw_rule0 *dst;
2153 struct timeval boottime;
2154 int error, i, l, warnflag;
2155 time_t boot_seconds;
2156
2157 warnflag = 0;
2158
2159 getboottime(&boottime);
2160 boot_seconds = boottime.tv_sec;
2161 for (i = 0; i < chain->n_rules; i++) {
2162 rule = chain->map[i];
2163
2164 if (is7) {
2165 /* Convert rule to FreeBSd 7.2 format */
2166 l = RULESIZE7(rule);
2167 if (bp + l + sizeof(uint32_t) <= ep) {
2168 bcopy(rule, bp, l + sizeof(uint32_t));
2169 error = set_legacy_obj_kidx(chain,
2170 (struct ip_fw_rule0 *)bp);
2171 if (error != 0)
2172 return (0);
2173 error = convert_rule_to_7((struct ip_fw_rule0 *) bp);
2174 if (error)
2175 return 0; /*XXX correct? */
2176 /*
2177 * XXX HACK. Store the disable mask in the "next"
2178 * pointer in a wild attempt to keep the ABI the same.
2179 * Why do we do this on EVERY rule?
2180 */
2181 bcopy(&V_set_disable,
2182 &(((struct ip_fw7 *)bp)->next_rule),
2183 sizeof(V_set_disable));
2184 if (((struct ip_fw7 *)bp)->timestamp)
2185 ((struct ip_fw7 *)bp)->timestamp += boot_seconds;
2186 bp += l;
2187 }
2188 continue; /* go to next rule */
2189 }
2190
2191 l = RULEUSIZE0(rule);
2192 if (bp + l > ep) { /* should not happen */
2193 printf("overflow dumping static rules\n");
2194 break;
2195 }
2196 dst = (struct ip_fw_rule0 *)bp;
2197 export_rule0(rule, dst, l);
2198 error = set_legacy_obj_kidx(chain, dst);
2199
2200 /*
2201 * XXX HACK. Store the disable mask in the "next"
2202 * pointer in a wild attempt to keep the ABI the same.
2203 * Why do we do this on EVERY rule?
2204 *
2205 * XXX: "ipfw set show" (ab)uses IP_FW_GET to read disabled mask
2206 * so we need to fail _after_ saving at least one mask.
2207 */
2208 bcopy(&V_set_disable, &dst->next_rule, sizeof(V_set_disable));
2209 if (dst->timestamp)
2210 dst->timestamp += boot_seconds;
2211 bp += l;
2212
2213 if (error != 0) {
2214 if (error == 2) {
2215 /* Non-fatal table rewrite error. */
2216 warnflag = 1;
2217 continue;
2218 }
2219 printf("Stop on rule %d. Fail to convert table\n",
2220 rule->rulenum);
2221 break;
2222 }
2223 }
2224 if (warnflag != 0)
2225 printf("ipfw: process %s is using legacy interfaces,"
2226 " consider rebuilding\n", "");
2227 ipfw_get_dynamic(chain, &bp, ep); /* protected by the dynamic lock */
2228 return (bp - (char *)buf);
2229 }
2230
2231
2232 struct dump_args {
2233 uint32_t b; /* start rule */
2234 uint32_t e; /* end rule */
2235 uint32_t rcount; /* number of rules */
2236 uint32_t rsize; /* rules size */
2237 uint32_t tcount; /* number of tables */
2238 int rcounters; /* counters */
2239 uint32_t *bmask; /* index bitmask of used named objects */
2240 };
2241
2242 void
ipfw_export_obj_ntlv(struct named_object * no,ipfw_obj_ntlv * ntlv)2243 ipfw_export_obj_ntlv(struct named_object *no, ipfw_obj_ntlv *ntlv)
2244 {
2245
2246 ntlv->head.type = no->etlv;
2247 ntlv->head.length = sizeof(*ntlv);
2248 ntlv->idx = no->kidx;
2249 strlcpy(ntlv->name, no->name, sizeof(ntlv->name));
2250 }
2251
2252 /*
2253 * Export named object info in instance @ni, identified by @kidx
2254 * to ipfw_obj_ntlv. TLV is allocated from @sd space.
2255 *
2256 * Returns 0 on success.
2257 */
2258 static int
export_objhash_ntlv(struct namedobj_instance * ni,uint16_t kidx,struct sockopt_data * sd)2259 export_objhash_ntlv(struct namedobj_instance *ni, uint16_t kidx,
2260 struct sockopt_data *sd)
2261 {
2262 struct named_object *no;
2263 ipfw_obj_ntlv *ntlv;
2264
2265 no = ipfw_objhash_lookup_kidx(ni, kidx);
2266 KASSERT(no != NULL, ("invalid object kernel index passed"));
2267
2268 ntlv = (ipfw_obj_ntlv *)ipfw_get_sopt_space(sd, sizeof(*ntlv));
2269 if (ntlv == NULL)
2270 return (ENOMEM);
2271
2272 ipfw_export_obj_ntlv(no, ntlv);
2273 return (0);
2274 }
2275
2276 static int
export_named_objects(struct namedobj_instance * ni,struct dump_args * da,struct sockopt_data * sd)2277 export_named_objects(struct namedobj_instance *ni, struct dump_args *da,
2278 struct sockopt_data *sd)
2279 {
2280 int error, i;
2281
2282 for (i = 0; i < IPFW_TABLES_MAX && da->tcount > 0; i++) {
2283 if ((da->bmask[i / 32] & (1 << (i % 32))) == 0)
2284 continue;
2285 if ((error = export_objhash_ntlv(ni, i, sd)) != 0)
2286 return (error);
2287 da->tcount--;
2288 }
2289 return (0);
2290 }
2291
2292 static int
dump_named_objects(struct ip_fw_chain * ch,struct dump_args * da,struct sockopt_data * sd)2293 dump_named_objects(struct ip_fw_chain *ch, struct dump_args *da,
2294 struct sockopt_data *sd)
2295 {
2296 ipfw_obj_ctlv *ctlv;
2297 int error;
2298
2299 MPASS(da->tcount > 0);
2300 /* Header first */
2301 ctlv = (ipfw_obj_ctlv *)ipfw_get_sopt_space(sd, sizeof(*ctlv));
2302 if (ctlv == NULL)
2303 return (ENOMEM);
2304 ctlv->head.type = IPFW_TLV_TBLNAME_LIST;
2305 ctlv->head.length = da->tcount * sizeof(ipfw_obj_ntlv) +
2306 sizeof(*ctlv);
2307 ctlv->count = da->tcount;
2308 ctlv->objsize = sizeof(ipfw_obj_ntlv);
2309
2310 /* Dump table names first (if any) */
2311 error = export_named_objects(ipfw_get_table_objhash(ch), da, sd);
2312 if (error != 0)
2313 return (error);
2314 /* Then dump another named objects */
2315 da->bmask += IPFW_TABLES_MAX / 32;
2316 return (export_named_objects(CHAIN_TO_SRV(ch), da, sd));
2317 }
2318
2319 /*
2320 * Dumps static rules with table TLVs in buffer @sd.
2321 *
2322 * Returns 0 on success.
2323 */
2324 static int
dump_static_rules(struct ip_fw_chain * chain,struct dump_args * da,struct sockopt_data * sd)2325 dump_static_rules(struct ip_fw_chain *chain, struct dump_args *da,
2326 struct sockopt_data *sd)
2327 {
2328 ipfw_obj_ctlv *ctlv;
2329 struct ip_fw *krule;
2330 caddr_t dst;
2331 int i, l;
2332
2333 /* Dump rules */
2334 ctlv = (ipfw_obj_ctlv *)ipfw_get_sopt_space(sd, sizeof(*ctlv));
2335 if (ctlv == NULL)
2336 return (ENOMEM);
2337 ctlv->head.type = IPFW_TLV_RULE_LIST;
2338 ctlv->head.length = da->rsize + sizeof(*ctlv);
2339 ctlv->count = da->rcount;
2340
2341 for (i = da->b; i < da->e; i++) {
2342 krule = chain->map[i];
2343
2344 l = RULEUSIZE1(krule) + sizeof(ipfw_obj_tlv);
2345 if (da->rcounters != 0)
2346 l += sizeof(struct ip_fw_bcounter);
2347 dst = (caddr_t)ipfw_get_sopt_space(sd, l);
2348 if (dst == NULL)
2349 return (ENOMEM);
2350
2351 export_rule1(krule, dst, l, da->rcounters);
2352 }
2353
2354 return (0);
2355 }
2356
2357 int
ipfw_mark_object_kidx(uint32_t * bmask,uint16_t etlv,uint16_t kidx)2358 ipfw_mark_object_kidx(uint32_t *bmask, uint16_t etlv, uint16_t kidx)
2359 {
2360 uint32_t bidx;
2361
2362 /*
2363 * Maintain separate bitmasks for table and non-table objects.
2364 */
2365 bidx = (etlv == IPFW_TLV_TBL_NAME) ? 0: IPFW_TABLES_MAX / 32;
2366 bidx += kidx / 32;
2367 if ((bmask[bidx] & (1 << (kidx % 32))) != 0)
2368 return (0);
2369
2370 bmask[bidx] |= 1 << (kidx % 32);
2371 return (1);
2372 }
2373
2374 /*
2375 * Marks every object index used in @rule with bit in @bmask.
2376 * Used to generate bitmask of referenced tables/objects for given ruleset
2377 * or its part.
2378 */
2379 static void
mark_rule_objects(struct ip_fw_chain * ch,struct ip_fw * rule,struct dump_args * da)2380 mark_rule_objects(struct ip_fw_chain *ch, struct ip_fw *rule,
2381 struct dump_args *da)
2382 {
2383 struct opcode_obj_rewrite *rw;
2384 ipfw_insn *cmd;
2385 int cmdlen, l;
2386 uint16_t kidx;
2387 uint8_t subtype;
2388
2389 l = rule->cmd_len;
2390 cmd = rule->cmd;
2391 cmdlen = 0;
2392 for ( ; l > 0 ; l -= cmdlen, cmd += cmdlen) {
2393 cmdlen = F_LEN(cmd);
2394
2395 rw = find_op_rw(cmd, &kidx, &subtype);
2396 if (rw == NULL)
2397 continue;
2398
2399 if (ipfw_mark_object_kidx(da->bmask, rw->etlv, kidx))
2400 da->tcount++;
2401 }
2402 }
2403
2404 /*
2405 * Dumps requested objects data
2406 * Data layout (version 0)(current):
2407 * Request: [ ipfw_cfg_lheader ] + IPFW_CFG_GET_* flags
2408 * size = ipfw_cfg_lheader.size
2409 * Reply: [ ipfw_cfg_lheader
2410 * [ ipfw_obj_ctlv(IPFW_TLV_TBL_LIST) ipfw_obj_ntlv x N ] (optional)
2411 * [ ipfw_obj_ctlv(IPFW_TLV_RULE_LIST)
2412 * ipfw_obj_tlv(IPFW_TLV_RULE_ENT) [ ip_fw_bcounter (optional) ip_fw_rule ]
2413 * ] (optional)
2414 * [ ipfw_obj_ctlv(IPFW_TLV_STATE_LIST) ipfw_obj_dyntlv x N ] (optional)
2415 * ]
2416 * * NOTE IPFW_TLV_STATE_LIST has the single valid field: objsize.
2417 * The rest (size, count) are set to zero and needs to be ignored.
2418 *
2419 * Returns 0 on success.
2420 */
2421 static int
dump_config(struct ip_fw_chain * chain,ip_fw3_opheader * op3,struct sockopt_data * sd)2422 dump_config(struct ip_fw_chain *chain, ip_fw3_opheader *op3,
2423 struct sockopt_data *sd)
2424 {
2425 struct dump_args da;
2426 ipfw_cfg_lheader *hdr;
2427 struct ip_fw *rule;
2428 size_t sz, rnum;
2429 uint32_t hdr_flags, *bmask;
2430 int error, i;
2431
2432 hdr = (ipfw_cfg_lheader *)ipfw_get_sopt_header(sd, sizeof(*hdr));
2433 if (hdr == NULL)
2434 return (EINVAL);
2435
2436 error = 0;
2437 bmask = NULL;
2438 memset(&da, 0, sizeof(da));
2439 /*
2440 * Allocate needed state.
2441 * Note we allocate 2xspace mask, for table & srv
2442 */
2443 if (hdr->flags & (IPFW_CFG_GET_STATIC | IPFW_CFG_GET_STATES))
2444 da.bmask = bmask = malloc(
2445 sizeof(uint32_t) * IPFW_TABLES_MAX * 2 / 32, M_TEMP,
2446 M_WAITOK | M_ZERO);
2447 IPFW_UH_RLOCK(chain);
2448
2449 /*
2450 * STAGE 1: Determine size/count for objects in range.
2451 * Prepare used tables bitmask.
2452 */
2453 sz = sizeof(ipfw_cfg_lheader);
2454 da.e = chain->n_rules;
2455
2456 if (hdr->end_rule != 0) {
2457 /* Handle custom range */
2458 if ((rnum = hdr->start_rule) > IPFW_DEFAULT_RULE)
2459 rnum = IPFW_DEFAULT_RULE;
2460 da.b = ipfw_find_rule(chain, rnum, 0);
2461 rnum = (hdr->end_rule < IPFW_DEFAULT_RULE) ?
2462 hdr->end_rule + 1: IPFW_DEFAULT_RULE;
2463 da.e = ipfw_find_rule(chain, rnum, UINT32_MAX) + 1;
2464 }
2465
2466 if (hdr->flags & IPFW_CFG_GET_STATIC) {
2467 for (i = da.b; i < da.e; i++) {
2468 rule = chain->map[i];
2469 da.rsize += RULEUSIZE1(rule) + sizeof(ipfw_obj_tlv);
2470 da.rcount++;
2471 /* Update bitmask of used objects for given range */
2472 mark_rule_objects(chain, rule, &da);
2473 }
2474 /* Add counters if requested */
2475 if (hdr->flags & IPFW_CFG_GET_COUNTERS) {
2476 da.rsize += sizeof(struct ip_fw_bcounter) * da.rcount;
2477 da.rcounters = 1;
2478 }
2479 sz += da.rsize + sizeof(ipfw_obj_ctlv);
2480 }
2481
2482 if (hdr->flags & IPFW_CFG_GET_STATES) {
2483 sz += sizeof(ipfw_obj_ctlv) +
2484 ipfw_dyn_get_count(bmask, &i) * sizeof(ipfw_obj_dyntlv);
2485 da.tcount += i;
2486 }
2487
2488 if (da.tcount > 0)
2489 sz += da.tcount * sizeof(ipfw_obj_ntlv) +
2490 sizeof(ipfw_obj_ctlv);
2491
2492 /*
2493 * Fill header anyway.
2494 * Note we have to save header fields to stable storage
2495 * buffer inside @sd can be flushed after dumping rules
2496 */
2497 hdr->size = sz;
2498 hdr->set_mask = ~V_set_disable;
2499 hdr_flags = hdr->flags;
2500 hdr = NULL;
2501
2502 if (sd->valsize < sz) {
2503 error = ENOMEM;
2504 goto cleanup;
2505 }
2506
2507 /* STAGE2: Store actual data */
2508 if (da.tcount > 0) {
2509 error = dump_named_objects(chain, &da, sd);
2510 if (error != 0)
2511 goto cleanup;
2512 }
2513
2514 if (hdr_flags & IPFW_CFG_GET_STATIC) {
2515 error = dump_static_rules(chain, &da, sd);
2516 if (error != 0)
2517 goto cleanup;
2518 }
2519
2520 if (hdr_flags & IPFW_CFG_GET_STATES)
2521 error = ipfw_dump_states(chain, sd);
2522
2523 cleanup:
2524 IPFW_UH_RUNLOCK(chain);
2525
2526 if (bmask != NULL)
2527 free(bmask, M_TEMP);
2528
2529 return (error);
2530 }
2531
2532 int
ipfw_check_object_name_generic(const char * name)2533 ipfw_check_object_name_generic(const char *name)
2534 {
2535 int nsize;
2536
2537 nsize = sizeof(((ipfw_obj_ntlv *)0)->name);
2538 if (strnlen(name, nsize) == nsize)
2539 return (EINVAL);
2540 if (name[0] == '\0')
2541 return (EINVAL);
2542 return (0);
2543 }
2544
2545 /*
2546 * Creates non-existent objects referenced by rule.
2547 *
2548 * Return 0 on success.
2549 */
2550 int
create_objects_compat(struct ip_fw_chain * ch,ipfw_insn * cmd,struct obj_idx * oib,struct obj_idx * pidx,struct tid_info * ti)2551 create_objects_compat(struct ip_fw_chain *ch, ipfw_insn *cmd,
2552 struct obj_idx *oib, struct obj_idx *pidx, struct tid_info *ti)
2553 {
2554 struct opcode_obj_rewrite *rw;
2555 struct obj_idx *p;
2556 uint16_t kidx;
2557 int error;
2558
2559 /*
2560 * Compatibility stuff: do actual creation for non-existing,
2561 * but referenced objects.
2562 */
2563 for (p = oib; p < pidx; p++) {
2564 if (p->kidx != 0)
2565 continue;
2566
2567 ti->uidx = p->uidx;
2568 ti->type = p->type;
2569 ti->atype = 0;
2570
2571 rw = find_op_rw(cmd + p->off, NULL, NULL);
2572 KASSERT(rw != NULL, ("Unable to find handler for op %d",
2573 (cmd + p->off)->opcode));
2574
2575 if (rw->create_object == NULL)
2576 error = EOPNOTSUPP;
2577 else
2578 error = rw->create_object(ch, ti, &kidx);
2579 if (error == 0) {
2580 p->kidx = kidx;
2581 continue;
2582 }
2583
2584 /*
2585 * Error happened. We have to rollback everything.
2586 * Drop all already acquired references.
2587 */
2588 IPFW_UH_WLOCK(ch);
2589 unref_oib_objects(ch, cmd, oib, pidx);
2590 IPFW_UH_WUNLOCK(ch);
2591
2592 return (error);
2593 }
2594
2595 return (0);
2596 }
2597
2598 /*
2599 * Compatibility function for old ipfw(8) binaries.
2600 * Rewrites table/nat kernel indices with userland ones.
2601 * Convert tables matching '/^\d+$/' to their atoi() value.
2602 * Use number 65535 for other tables.
2603 *
2604 * Returns 0 on success.
2605 */
2606 static int
set_legacy_obj_kidx(struct ip_fw_chain * ch,struct ip_fw_rule0 * rule)2607 set_legacy_obj_kidx(struct ip_fw_chain *ch, struct ip_fw_rule0 *rule)
2608 {
2609 struct opcode_obj_rewrite *rw;
2610 struct named_object *no;
2611 ipfw_insn *cmd;
2612 char *end;
2613 long val;
2614 int cmdlen, error, l;
2615 uint16_t kidx, uidx;
2616 uint8_t subtype;
2617
2618 error = 0;
2619
2620 l = rule->cmd_len;
2621 cmd = rule->cmd;
2622 cmdlen = 0;
2623 for ( ; l > 0 ; l -= cmdlen, cmd += cmdlen) {
2624 cmdlen = F_LEN(cmd);
2625
2626 /* Check if is index in given opcode */
2627 rw = find_op_rw(cmd, &kidx, &subtype);
2628 if (rw == NULL)
2629 continue;
2630
2631 /* Try to find referenced kernel object */
2632 no = rw->find_bykidx(ch, kidx);
2633 if (no == NULL)
2634 continue;
2635
2636 val = strtol(no->name, &end, 10);
2637 if (*end == '\0' && val < 65535) {
2638 uidx = val;
2639 } else {
2640
2641 /*
2642 * We are called via legacy opcode.
2643 * Save error and show table as fake number
2644 * not to make ipfw(8) hang.
2645 */
2646 uidx = 65535;
2647 error = 2;
2648 }
2649
2650 rw->update(cmd, uidx);
2651 }
2652
2653 return (error);
2654 }
2655
2656
2657 /*
2658 * Unreferences all already-referenced objects in given @cmd rule,
2659 * using information in @oib.
2660 *
2661 * Used to rollback partially converted rule on error.
2662 */
2663 static void
unref_oib_objects(struct ip_fw_chain * ch,ipfw_insn * cmd,struct obj_idx * oib,struct obj_idx * end)2664 unref_oib_objects(struct ip_fw_chain *ch, ipfw_insn *cmd, struct obj_idx *oib,
2665 struct obj_idx *end)
2666 {
2667 struct opcode_obj_rewrite *rw;
2668 struct named_object *no;
2669 struct obj_idx *p;
2670
2671 IPFW_UH_WLOCK_ASSERT(ch);
2672
2673 for (p = oib; p < end; p++) {
2674 if (p->kidx == 0)
2675 continue;
2676
2677 rw = find_op_rw(cmd + p->off, NULL, NULL);
2678 KASSERT(rw != NULL, ("Unable to find handler for op %d",
2679 (cmd + p->off)->opcode));
2680
2681 /* Find & unref by existing idx */
2682 no = rw->find_bykidx(ch, p->kidx);
2683 KASSERT(no != NULL, ("Ref'd object %d disappeared", p->kidx));
2684 no->refcnt--;
2685 }
2686 }
2687
2688 /*
2689 * Remove references from every object used in @rule.
2690 * Used at rule removal code.
2691 */
2692 static void
unref_rule_objects(struct ip_fw_chain * ch,struct ip_fw * rule)2693 unref_rule_objects(struct ip_fw_chain *ch, struct ip_fw *rule)
2694 {
2695 struct opcode_obj_rewrite *rw;
2696 struct named_object *no;
2697 ipfw_insn *cmd;
2698 int cmdlen, l;
2699 uint16_t kidx;
2700 uint8_t subtype;
2701
2702 IPFW_UH_WLOCK_ASSERT(ch);
2703
2704 l = rule->cmd_len;
2705 cmd = rule->cmd;
2706 cmdlen = 0;
2707 for ( ; l > 0 ; l -= cmdlen, cmd += cmdlen) {
2708 cmdlen = F_LEN(cmd);
2709
2710 rw = find_op_rw(cmd, &kidx, &subtype);
2711 if (rw == NULL)
2712 continue;
2713 no = rw->find_bykidx(ch, kidx);
2714
2715 KASSERT(no != NULL, ("object id %d not found", kidx));
2716 KASSERT(no->subtype == subtype,
2717 ("wrong type %d (%d) for object id %d",
2718 no->subtype, subtype, kidx));
2719 KASSERT(no->refcnt > 0, ("refcount for object %d is %d",
2720 kidx, no->refcnt));
2721
2722 if (no->refcnt == 1 && rw->destroy_object != NULL)
2723 rw->destroy_object(ch, no);
2724 else
2725 no->refcnt--;
2726 }
2727 }
2728
2729
2730 /*
2731 * Find and reference object (if any) stored in instruction @cmd.
2732 *
2733 * Saves object info in @pidx, sets
2734 * - @unresolved to 1 if object should exists but not found
2735 *
2736 * Returns non-zero value in case of error.
2737 */
2738 static int
ref_opcode_object(struct ip_fw_chain * ch,ipfw_insn * cmd,struct tid_info * ti,struct obj_idx * pidx,int * unresolved)2739 ref_opcode_object(struct ip_fw_chain *ch, ipfw_insn *cmd, struct tid_info *ti,
2740 struct obj_idx *pidx, int *unresolved)
2741 {
2742 struct named_object *no;
2743 struct opcode_obj_rewrite *rw;
2744 int error;
2745
2746 /* Check if this opcode is candidate for rewrite */
2747 rw = find_op_rw(cmd, &ti->uidx, &ti->type);
2748 if (rw == NULL)
2749 return (0);
2750
2751 /* Need to rewrite. Save necessary fields */
2752 pidx->uidx = ti->uidx;
2753 pidx->type = ti->type;
2754
2755 /* Try to find referenced kernel object */
2756 error = rw->find_byname(ch, ti, &no);
2757 if (error != 0)
2758 return (error);
2759 if (no == NULL) {
2760 /*
2761 * Report about unresolved object for automaic
2762 * creation.
2763 */
2764 *unresolved = 1;
2765 return (0);
2766 }
2767
2768 /*
2769 * Object is already exist.
2770 * Its subtype should match with expected value.
2771 */
2772 if (ti->type != no->subtype)
2773 return (EINVAL);
2774
2775 /* Bump refcount and update kidx. */
2776 no->refcnt++;
2777 rw->update(cmd, no->kidx);
2778 return (0);
2779 }
2780
2781 /*
2782 * Finds and bumps refcount for objects referenced by given @rule.
2783 * Auto-creates non-existing tables.
2784 * Fills in @oib array with userland/kernel indexes.
2785 *
2786 * Returns 0 on success.
2787 */
2788 static int
ref_rule_objects(struct ip_fw_chain * ch,struct ip_fw * rule,struct rule_check_info * ci,struct obj_idx * oib,struct tid_info * ti)2789 ref_rule_objects(struct ip_fw_chain *ch, struct ip_fw *rule,
2790 struct rule_check_info *ci, struct obj_idx *oib, struct tid_info *ti)
2791 {
2792 struct obj_idx *pidx;
2793 ipfw_insn *cmd;
2794 int cmdlen, error, l, unresolved;
2795
2796 pidx = oib;
2797 l = rule->cmd_len;
2798 cmd = rule->cmd;
2799 cmdlen = 0;
2800 error = 0;
2801
2802 IPFW_UH_WLOCK(ch);
2803
2804 /* Increase refcount on each existing referenced table. */
2805 for ( ; l > 0 ; l -= cmdlen, cmd += cmdlen) {
2806 cmdlen = F_LEN(cmd);
2807 unresolved = 0;
2808
2809 error = ref_opcode_object(ch, cmd, ti, pidx, &unresolved);
2810 if (error != 0)
2811 break;
2812 /*
2813 * Compatibility stuff for old clients:
2814 * prepare to automaitcally create non-existing objects.
2815 */
2816 if (unresolved != 0) {
2817 pidx->off = rule->cmd_len - l;
2818 pidx++;
2819 }
2820 }
2821
2822 if (error != 0) {
2823 /* Unref everything we have already done */
2824 unref_oib_objects(ch, rule->cmd, oib, pidx);
2825 IPFW_UH_WUNLOCK(ch);
2826 return (error);
2827 }
2828 IPFW_UH_WUNLOCK(ch);
2829
2830 /* Perform auto-creation for non-existing objects */
2831 if (pidx != oib)
2832 error = create_objects_compat(ch, rule->cmd, oib, pidx, ti);
2833
2834 /* Calculate real number of dynamic objects */
2835 ci->object_opcodes = (uint16_t)(pidx - oib);
2836
2837 return (error);
2838 }
2839
2840 /*
2841 * Checks is opcode is referencing table of appropriate type.
2842 * Adds reference count for found table if true.
2843 * Rewrites user-supplied opcode values with kernel ones.
2844 *
2845 * Returns 0 on success and appropriate error code otherwise.
2846 */
2847 static int
rewrite_rule_uidx(struct ip_fw_chain * chain,struct rule_check_info * ci)2848 rewrite_rule_uidx(struct ip_fw_chain *chain, struct rule_check_info *ci)
2849 {
2850 int error;
2851 ipfw_insn *cmd;
2852 uint8_t type;
2853 struct obj_idx *p, *pidx_first, *pidx_last;
2854 struct tid_info ti;
2855
2856 /*
2857 * Prepare an array for storing opcode indices.
2858 * Use stack allocation by default.
2859 */
2860 if (ci->object_opcodes <= (sizeof(ci->obuf)/sizeof(ci->obuf[0]))) {
2861 /* Stack */
2862 pidx_first = ci->obuf;
2863 } else
2864 pidx_first = malloc(
2865 ci->object_opcodes * sizeof(struct obj_idx),
2866 M_IPFW, M_WAITOK | M_ZERO);
2867
2868 error = 0;
2869 type = 0;
2870 memset(&ti, 0, sizeof(ti));
2871
2872 /* Use set rule is assigned to. */
2873 ti.set = ci->krule->set;
2874 if (ci->ctlv != NULL) {
2875 ti.tlvs = (void *)(ci->ctlv + 1);
2876 ti.tlen = ci->ctlv->head.length - sizeof(ipfw_obj_ctlv);
2877 }
2878
2879 /* Reference all used tables and other objects */
2880 error = ref_rule_objects(chain, ci->krule, ci, pidx_first, &ti);
2881 if (error != 0)
2882 goto free;
2883 /*
2884 * Note that ref_rule_objects() might have updated ci->object_opcodes
2885 * to reflect actual number of object opcodes.
2886 */
2887
2888 /* Perform rewrite of remaining opcodes */
2889 p = pidx_first;
2890 pidx_last = pidx_first + ci->object_opcodes;
2891 for (p = pidx_first; p < pidx_last; p++) {
2892 cmd = ci->krule->cmd + p->off;
2893 update_opcode_kidx(cmd, p->kidx);
2894 }
2895
2896 free:
2897 if (pidx_first != ci->obuf)
2898 free(pidx_first, M_IPFW);
2899
2900 return (error);
2901 }
2902
2903 /*
2904 * Adds one or more rules to ipfw @chain.
2905 * Data layout (version 0)(current):
2906 * Request:
2907 * [
2908 * ip_fw3_opheader
2909 * [ ipfw_obj_ctlv(IPFW_TLV_TBL_LIST) ipfw_obj_ntlv x N ] (optional *1)
2910 * [ ipfw_obj_ctlv(IPFW_TLV_RULE_LIST) ip_fw x N ] (*2) (*3)
2911 * ]
2912 * Reply:
2913 * [
2914 * ip_fw3_opheader
2915 * [ ipfw_obj_ctlv(IPFW_TLV_TBL_LIST) ipfw_obj_ntlv x N ] (optional)
2916 * [ ipfw_obj_ctlv(IPFW_TLV_RULE_LIST) ip_fw x N ]
2917 * ]
2918 *
2919 * Rules in reply are modified to store their actual ruleset number.
2920 *
2921 * (*1) TLVs inside IPFW_TLV_TBL_LIST needs to be sorted ascending
2922 * according to their idx field and there has to be no duplicates.
2923 * (*2) Numbered rules inside IPFW_TLV_RULE_LIST needs to be sorted ascending.
2924 * (*3) Each ip_fw structure needs to be aligned to u64 boundary.
2925 *
2926 * Returns 0 on success.
2927 */
2928 static int
add_rules(struct ip_fw_chain * chain,ip_fw3_opheader * op3,struct sockopt_data * sd)2929 add_rules(struct ip_fw_chain *chain, ip_fw3_opheader *op3,
2930 struct sockopt_data *sd)
2931 {
2932 ipfw_obj_ctlv *ctlv, *rtlv, *tstate;
2933 ipfw_obj_ntlv *ntlv;
2934 int clen, error, idx;
2935 uint32_t count, read;
2936 struct ip_fw_rule *r;
2937 struct rule_check_info rci, *ci, *cbuf;
2938 int i, rsize;
2939
2940 op3 = (ip_fw3_opheader *)ipfw_get_sopt_space(sd, sd->valsize);
2941 ctlv = (ipfw_obj_ctlv *)(op3 + 1);
2942
2943 read = sizeof(ip_fw3_opheader);
2944 rtlv = NULL;
2945 tstate = NULL;
2946 cbuf = NULL;
2947 memset(&rci, 0, sizeof(struct rule_check_info));
2948
2949 if (read + sizeof(*ctlv) > sd->valsize)
2950 return (EINVAL);
2951
2952 if (ctlv->head.type == IPFW_TLV_TBLNAME_LIST) {
2953 clen = ctlv->head.length;
2954 /* Check size and alignment */
2955 if (clen > sd->valsize || clen < sizeof(*ctlv))
2956 return (EINVAL);
2957 if ((clen % sizeof(uint64_t)) != 0)
2958 return (EINVAL);
2959
2960 /*
2961 * Some table names or other named objects.
2962 * Check for validness.
2963 */
2964 count = (ctlv->head.length - sizeof(*ctlv)) / sizeof(*ntlv);
2965 if (ctlv->count != count || ctlv->objsize != sizeof(*ntlv))
2966 return (EINVAL);
2967
2968 /*
2969 * Check each TLV.
2970 * Ensure TLVs are sorted ascending and
2971 * there are no duplicates.
2972 */
2973 idx = -1;
2974 ntlv = (ipfw_obj_ntlv *)(ctlv + 1);
2975 while (count > 0) {
2976 if (ntlv->head.length != sizeof(ipfw_obj_ntlv))
2977 return (EINVAL);
2978
2979 error = ipfw_check_object_name_generic(ntlv->name);
2980 if (error != 0)
2981 return (error);
2982
2983 if (ntlv->idx <= idx)
2984 return (EINVAL);
2985
2986 idx = ntlv->idx;
2987 count--;
2988 ntlv++;
2989 }
2990
2991 tstate = ctlv;
2992 read += ctlv->head.length;
2993 ctlv = (ipfw_obj_ctlv *)((caddr_t)ctlv + ctlv->head.length);
2994 }
2995
2996 if (read + sizeof(*ctlv) > sd->valsize)
2997 return (EINVAL);
2998
2999 if (ctlv->head.type == IPFW_TLV_RULE_LIST) {
3000 clen = ctlv->head.length;
3001 if (clen + read > sd->valsize || clen < sizeof(*ctlv))
3002 return (EINVAL);
3003 if ((clen % sizeof(uint64_t)) != 0)
3004 return (EINVAL);
3005
3006 /*
3007 * TODO: Permit adding multiple rules at once
3008 */
3009 if (ctlv->count != 1)
3010 return (ENOTSUP);
3011
3012 clen -= sizeof(*ctlv);
3013
3014 if (ctlv->count > clen / sizeof(struct ip_fw_rule))
3015 return (EINVAL);
3016
3017 /* Allocate state for each rule or use stack */
3018 if (ctlv->count == 1) {
3019 memset(&rci, 0, sizeof(struct rule_check_info));
3020 cbuf = &rci;
3021 } else
3022 cbuf = malloc(ctlv->count * sizeof(*ci), M_TEMP,
3023 M_WAITOK | M_ZERO);
3024 ci = cbuf;
3025
3026 /*
3027 * Check each rule for validness.
3028 * Ensure numbered rules are sorted ascending
3029 * and properly aligned
3030 */
3031 idx = 0;
3032 r = (struct ip_fw_rule *)(ctlv + 1);
3033 count = 0;
3034 error = 0;
3035 while (clen > 0) {
3036 rsize = roundup2(RULESIZE(r), sizeof(uint64_t));
3037 if (rsize > clen || ctlv->count <= count) {
3038 error = EINVAL;
3039 break;
3040 }
3041
3042 ci->ctlv = tstate;
3043 error = check_ipfw_rule1(r, rsize, ci);
3044 if (error != 0)
3045 break;
3046
3047 /* Check sorting */
3048 if (r->rulenum != 0 && r->rulenum < idx) {
3049 printf("rulenum %d idx %d\n", r->rulenum, idx);
3050 error = EINVAL;
3051 break;
3052 }
3053 idx = r->rulenum;
3054
3055 ci->urule = (caddr_t)r;
3056
3057 rsize = roundup2(rsize, sizeof(uint64_t));
3058 clen -= rsize;
3059 r = (struct ip_fw_rule *)((caddr_t)r + rsize);
3060 count++;
3061 ci++;
3062 }
3063
3064 if (ctlv->count != count || error != 0) {
3065 if (cbuf != &rci)
3066 free(cbuf, M_TEMP);
3067 return (EINVAL);
3068 }
3069
3070 rtlv = ctlv;
3071 read += ctlv->head.length;
3072 ctlv = (ipfw_obj_ctlv *)((caddr_t)ctlv + ctlv->head.length);
3073 }
3074
3075 if (read != sd->valsize || rtlv == NULL || rtlv->count == 0) {
3076 if (cbuf != NULL && cbuf != &rci)
3077 free(cbuf, M_TEMP);
3078 return (EINVAL);
3079 }
3080
3081 /*
3082 * Passed rules seems to be valid.
3083 * Allocate storage and try to add them to chain.
3084 */
3085 for (i = 0, ci = cbuf; i < rtlv->count; i++, ci++) {
3086 clen = RULEKSIZE1((struct ip_fw_rule *)ci->urule);
3087 ci->krule = ipfw_alloc_rule(chain, clen);
3088 import_rule1(ci);
3089 }
3090
3091 if ((error = commit_rules(chain, cbuf, rtlv->count)) != 0) {
3092 /* Free allocate krules */
3093 for (i = 0, ci = cbuf; i < rtlv->count; i++, ci++)
3094 ipfw_free_rule(ci->krule);
3095 }
3096
3097 if (cbuf != NULL && cbuf != &rci)
3098 free(cbuf, M_TEMP);
3099
3100 return (error);
3101 }
3102
3103 /*
3104 * Lists all sopts currently registered.
3105 * Data layout (v0)(current):
3106 * Request: [ ipfw_obj_lheader ], size = ipfw_obj_lheader.size
3107 * Reply: [ ipfw_obj_lheader ipfw_sopt_info x N ]
3108 *
3109 * Returns 0 on success
3110 */
3111 static int
dump_soptcodes(struct ip_fw_chain * chain,ip_fw3_opheader * op3,struct sockopt_data * sd)3112 dump_soptcodes(struct ip_fw_chain *chain, ip_fw3_opheader *op3,
3113 struct sockopt_data *sd)
3114 {
3115 struct _ipfw_obj_lheader *olh;
3116 ipfw_sopt_info *i;
3117 struct ipfw_sopt_handler *sh;
3118 uint32_t count, n, size;
3119
3120 olh = (struct _ipfw_obj_lheader *)ipfw_get_sopt_header(sd,sizeof(*olh));
3121 if (olh == NULL)
3122 return (EINVAL);
3123 if (sd->valsize < olh->size)
3124 return (EINVAL);
3125
3126 CTL3_LOCK();
3127 count = ctl3_hsize;
3128 size = count * sizeof(ipfw_sopt_info) + sizeof(ipfw_obj_lheader);
3129
3130 /* Fill in header regadless of buffer size */
3131 olh->count = count;
3132 olh->objsize = sizeof(ipfw_sopt_info);
3133
3134 if (size > olh->size) {
3135 olh->size = size;
3136 CTL3_UNLOCK();
3137 return (ENOMEM);
3138 }
3139 olh->size = size;
3140
3141 for (n = 1; n <= count; n++) {
3142 i = (ipfw_sopt_info *)ipfw_get_sopt_space(sd, sizeof(*i));
3143 KASSERT(i != NULL, ("previously checked buffer is not enough"));
3144 sh = &ctl3_handlers[n];
3145 i->opcode = sh->opcode;
3146 i->version = sh->version;
3147 i->refcnt = sh->refcnt;
3148 }
3149 CTL3_UNLOCK();
3150
3151 return (0);
3152 }
3153
3154 /*
3155 * Compares two opcodes.
3156 * Used both in qsort() and bsearch().
3157 *
3158 * Returns 0 if match is found.
3159 */
3160 static int
compare_opcodes(const void * _a,const void * _b)3161 compare_opcodes(const void *_a, const void *_b)
3162 {
3163 const struct opcode_obj_rewrite *a, *b;
3164
3165 a = (const struct opcode_obj_rewrite *)_a;
3166 b = (const struct opcode_obj_rewrite *)_b;
3167
3168 if (a->opcode < b->opcode)
3169 return (-1);
3170 else if (a->opcode > b->opcode)
3171 return (1);
3172
3173 return (0);
3174 }
3175
3176 /*
3177 * XXX: Rewrite bsearch()
3178 */
3179 static int
find_op_rw_range(uint16_t op,struct opcode_obj_rewrite ** plo,struct opcode_obj_rewrite ** phi)3180 find_op_rw_range(uint16_t op, struct opcode_obj_rewrite **plo,
3181 struct opcode_obj_rewrite **phi)
3182 {
3183 struct opcode_obj_rewrite *ctl3_max, *lo, *hi, h, *rw;
3184
3185 memset(&h, 0, sizeof(h));
3186 h.opcode = op;
3187
3188 rw = (struct opcode_obj_rewrite *)bsearch(&h, ctl3_rewriters,
3189 ctl3_rsize, sizeof(h), compare_opcodes);
3190 if (rw == NULL)
3191 return (1);
3192
3193 /* Find the first element matching the same opcode */
3194 lo = rw;
3195 for ( ; lo > ctl3_rewriters && (lo - 1)->opcode == op; lo--)
3196 ;
3197
3198 /* Find the last element matching the same opcode */
3199 hi = rw;
3200 ctl3_max = ctl3_rewriters + ctl3_rsize;
3201 for ( ; (hi + 1) < ctl3_max && (hi + 1)->opcode == op; hi++)
3202 ;
3203
3204 *plo = lo;
3205 *phi = hi;
3206
3207 return (0);
3208 }
3209
3210 /*
3211 * Finds opcode object rewriter based on @code.
3212 *
3213 * Returns pointer to handler or NULL.
3214 */
3215 static struct opcode_obj_rewrite *
find_op_rw(ipfw_insn * cmd,uint16_t * puidx,uint8_t * ptype)3216 find_op_rw(ipfw_insn *cmd, uint16_t *puidx, uint8_t *ptype)
3217 {
3218 struct opcode_obj_rewrite *rw, *lo, *hi;
3219 uint16_t uidx;
3220 uint8_t subtype;
3221
3222 if (find_op_rw_range(cmd->opcode, &lo, &hi) != 0)
3223 return (NULL);
3224
3225 for (rw = lo; rw <= hi; rw++) {
3226 if (rw->classifier(cmd, &uidx, &subtype) == 0) {
3227 if (puidx != NULL)
3228 *puidx = uidx;
3229 if (ptype != NULL)
3230 *ptype = subtype;
3231 return (rw);
3232 }
3233 }
3234
3235 return (NULL);
3236 }
3237 int
classify_opcode_kidx(ipfw_insn * cmd,uint16_t * puidx)3238 classify_opcode_kidx(ipfw_insn *cmd, uint16_t *puidx)
3239 {
3240
3241 if (find_op_rw(cmd, puidx, NULL) == NULL)
3242 return (1);
3243 return (0);
3244 }
3245
3246 void
update_opcode_kidx(ipfw_insn * cmd,uint16_t idx)3247 update_opcode_kidx(ipfw_insn *cmd, uint16_t idx)
3248 {
3249 struct opcode_obj_rewrite *rw;
3250
3251 rw = find_op_rw(cmd, NULL, NULL);
3252 KASSERT(rw != NULL, ("No handler to update opcode %d", cmd->opcode));
3253 rw->update(cmd, idx);
3254 }
3255
3256 void
ipfw_init_obj_rewriter()3257 ipfw_init_obj_rewriter()
3258 {
3259
3260 ctl3_rewriters = NULL;
3261 ctl3_rsize = 0;
3262 }
3263
3264 void
ipfw_destroy_obj_rewriter()3265 ipfw_destroy_obj_rewriter()
3266 {
3267
3268 if (ctl3_rewriters != NULL)
3269 free(ctl3_rewriters, M_IPFW);
3270 ctl3_rewriters = NULL;
3271 ctl3_rsize = 0;
3272 }
3273
3274 /*
3275 * Adds one or more opcode object rewrite handlers to the global array.
3276 * Function may sleep.
3277 */
3278 void
ipfw_add_obj_rewriter(struct opcode_obj_rewrite * rw,size_t count)3279 ipfw_add_obj_rewriter(struct opcode_obj_rewrite *rw, size_t count)
3280 {
3281 size_t sz;
3282 struct opcode_obj_rewrite *tmp;
3283
3284 CTL3_LOCK();
3285
3286 for (;;) {
3287 sz = ctl3_rsize + count;
3288 CTL3_UNLOCK();
3289 tmp = malloc(sizeof(*rw) * sz, M_IPFW, M_WAITOK | M_ZERO);
3290 CTL3_LOCK();
3291 if (ctl3_rsize + count <= sz)
3292 break;
3293
3294 /* Retry */
3295 free(tmp, M_IPFW);
3296 }
3297
3298 /* Merge old & new arrays */
3299 sz = ctl3_rsize + count;
3300 memcpy(tmp, ctl3_rewriters, ctl3_rsize * sizeof(*rw));
3301 memcpy(&tmp[ctl3_rsize], rw, count * sizeof(*rw));
3302 qsort(tmp, sz, sizeof(*rw), compare_opcodes);
3303 /* Switch new and free old */
3304 if (ctl3_rewriters != NULL)
3305 free(ctl3_rewriters, M_IPFW);
3306 ctl3_rewriters = tmp;
3307 ctl3_rsize = sz;
3308
3309 CTL3_UNLOCK();
3310 }
3311
3312 /*
3313 * Removes one or more object rewrite handlers from the global array.
3314 */
3315 int
ipfw_del_obj_rewriter(struct opcode_obj_rewrite * rw,size_t count)3316 ipfw_del_obj_rewriter(struct opcode_obj_rewrite *rw, size_t count)
3317 {
3318 size_t sz;
3319 struct opcode_obj_rewrite *ctl3_max, *ktmp, *lo, *hi;
3320 int i;
3321
3322 CTL3_LOCK();
3323
3324 for (i = 0; i < count; i++) {
3325 if (find_op_rw_range(rw[i].opcode, &lo, &hi) != 0)
3326 continue;
3327
3328 for (ktmp = lo; ktmp <= hi; ktmp++) {
3329 if (ktmp->classifier != rw[i].classifier)
3330 continue;
3331
3332 ctl3_max = ctl3_rewriters + ctl3_rsize;
3333 sz = (ctl3_max - (ktmp + 1)) * sizeof(*ktmp);
3334 memmove(ktmp, ktmp + 1, sz);
3335 ctl3_rsize--;
3336 break;
3337 }
3338
3339 }
3340
3341 if (ctl3_rsize == 0) {
3342 if (ctl3_rewriters != NULL)
3343 free(ctl3_rewriters, M_IPFW);
3344 ctl3_rewriters = NULL;
3345 }
3346
3347 CTL3_UNLOCK();
3348
3349 return (0);
3350 }
3351
3352 static int
export_objhash_ntlv_internal(struct namedobj_instance * ni,struct named_object * no,void * arg)3353 export_objhash_ntlv_internal(struct namedobj_instance *ni,
3354 struct named_object *no, void *arg)
3355 {
3356 struct sockopt_data *sd;
3357 ipfw_obj_ntlv *ntlv;
3358
3359 sd = (struct sockopt_data *)arg;
3360 ntlv = (ipfw_obj_ntlv *)ipfw_get_sopt_space(sd, sizeof(*ntlv));
3361 if (ntlv == NULL)
3362 return (ENOMEM);
3363 ipfw_export_obj_ntlv(no, ntlv);
3364 return (0);
3365 }
3366
3367 /*
3368 * Lists all service objects.
3369 * Data layout (v0)(current):
3370 * Request: [ ipfw_obj_lheader ] size = ipfw_obj_lheader.size
3371 * Reply: [ ipfw_obj_lheader [ ipfw_obj_ntlv x N ] (optional) ]
3372 * Returns 0 on success
3373 */
3374 static int
dump_srvobjects(struct ip_fw_chain * chain,ip_fw3_opheader * op3,struct sockopt_data * sd)3375 dump_srvobjects(struct ip_fw_chain *chain, ip_fw3_opheader *op3,
3376 struct sockopt_data *sd)
3377 {
3378 ipfw_obj_lheader *hdr;
3379 int count;
3380
3381 hdr = (ipfw_obj_lheader *)ipfw_get_sopt_header(sd, sizeof(*hdr));
3382 if (hdr == NULL)
3383 return (EINVAL);
3384
3385 IPFW_UH_RLOCK(chain);
3386 count = ipfw_objhash_count(CHAIN_TO_SRV(chain));
3387 hdr->size = sizeof(ipfw_obj_lheader) + count * sizeof(ipfw_obj_ntlv);
3388 if (sd->valsize < hdr->size) {
3389 IPFW_UH_RUNLOCK(chain);
3390 return (ENOMEM);
3391 }
3392 hdr->count = count;
3393 hdr->objsize = sizeof(ipfw_obj_ntlv);
3394 if (count > 0)
3395 ipfw_objhash_foreach(CHAIN_TO_SRV(chain),
3396 export_objhash_ntlv_internal, sd);
3397 IPFW_UH_RUNLOCK(chain);
3398 return (0);
3399 }
3400
3401 /*
3402 * Compares two sopt handlers (code, version and handler ptr).
3403 * Used both as qsort() and bsearch().
3404 * Does not compare handler for latter case.
3405 *
3406 * Returns 0 if match is found.
3407 */
3408 static int
compare_sh(const void * _a,const void * _b)3409 compare_sh(const void *_a, const void *_b)
3410 {
3411 const struct ipfw_sopt_handler *a, *b;
3412
3413 a = (const struct ipfw_sopt_handler *)_a;
3414 b = (const struct ipfw_sopt_handler *)_b;
3415
3416 if (a->opcode < b->opcode)
3417 return (-1);
3418 else if (a->opcode > b->opcode)
3419 return (1);
3420
3421 if (a->version < b->version)
3422 return (-1);
3423 else if (a->version > b->version)
3424 return (1);
3425
3426 /* bsearch helper */
3427 if (a->handler == NULL)
3428 return (0);
3429
3430 if ((uintptr_t)a->handler < (uintptr_t)b->handler)
3431 return (-1);
3432 else if ((uintptr_t)a->handler > (uintptr_t)b->handler)
3433 return (1);
3434
3435 return (0);
3436 }
3437
3438 /*
3439 * Finds sopt handler based on @code and @version.
3440 *
3441 * Returns pointer to handler or NULL.
3442 */
3443 static struct ipfw_sopt_handler *
find_sh(uint16_t code,uint8_t version,sopt_handler_f * handler)3444 find_sh(uint16_t code, uint8_t version, sopt_handler_f *handler)
3445 {
3446 struct ipfw_sopt_handler *sh, h;
3447
3448 memset(&h, 0, sizeof(h));
3449 h.opcode = code;
3450 h.version = version;
3451 h.handler = handler;
3452
3453 sh = (struct ipfw_sopt_handler *)bsearch(&h, ctl3_handlers,
3454 ctl3_hsize, sizeof(h), compare_sh);
3455
3456 return (sh);
3457 }
3458
3459 static int
find_ref_sh(uint16_t opcode,uint8_t version,struct ipfw_sopt_handler * psh)3460 find_ref_sh(uint16_t opcode, uint8_t version, struct ipfw_sopt_handler *psh)
3461 {
3462 struct ipfw_sopt_handler *sh;
3463
3464 CTL3_LOCK();
3465 if ((sh = find_sh(opcode, version, NULL)) == NULL) {
3466 CTL3_UNLOCK();
3467 printf("ipfw: ipfw_ctl3 invalid option %d""v""%d\n",
3468 opcode, version);
3469 return (EINVAL);
3470 }
3471 sh->refcnt++;
3472 ctl3_refct++;
3473 /* Copy handler data to requested buffer */
3474 *psh = *sh;
3475 CTL3_UNLOCK();
3476
3477 return (0);
3478 }
3479
3480 static void
find_unref_sh(struct ipfw_sopt_handler * psh)3481 find_unref_sh(struct ipfw_sopt_handler *psh)
3482 {
3483 struct ipfw_sopt_handler *sh;
3484
3485 CTL3_LOCK();
3486 sh = find_sh(psh->opcode, psh->version, NULL);
3487 KASSERT(sh != NULL, ("ctl3 handler disappeared"));
3488 sh->refcnt--;
3489 ctl3_refct--;
3490 CTL3_UNLOCK();
3491 }
3492
3493 void
ipfw_init_sopt_handler()3494 ipfw_init_sopt_handler()
3495 {
3496
3497 CTL3_LOCK_INIT();
3498 IPFW_ADD_SOPT_HANDLER(1, scodes);
3499 }
3500
3501 void
ipfw_destroy_sopt_handler()3502 ipfw_destroy_sopt_handler()
3503 {
3504
3505 IPFW_DEL_SOPT_HANDLER(1, scodes);
3506 CTL3_LOCK_DESTROY();
3507 }
3508
3509 /*
3510 * Adds one or more sockopt handlers to the global array.
3511 * Function may sleep.
3512 */
3513 void
ipfw_add_sopt_handler(struct ipfw_sopt_handler * sh,size_t count)3514 ipfw_add_sopt_handler(struct ipfw_sopt_handler *sh, size_t count)
3515 {
3516 size_t sz;
3517 struct ipfw_sopt_handler *tmp;
3518
3519 CTL3_LOCK();
3520
3521 for (;;) {
3522 sz = ctl3_hsize + count;
3523 CTL3_UNLOCK();
3524 tmp = malloc(sizeof(*sh) * sz, M_IPFW, M_WAITOK | M_ZERO);
3525 CTL3_LOCK();
3526 if (ctl3_hsize + count <= sz)
3527 break;
3528
3529 /* Retry */
3530 free(tmp, M_IPFW);
3531 }
3532
3533 /* Merge old & new arrays */
3534 sz = ctl3_hsize + count;
3535 memcpy(tmp, ctl3_handlers, ctl3_hsize * sizeof(*sh));
3536 memcpy(&tmp[ctl3_hsize], sh, count * sizeof(*sh));
3537 qsort(tmp, sz, sizeof(*sh), compare_sh);
3538 /* Switch new and free old */
3539 if (ctl3_handlers != NULL)
3540 free(ctl3_handlers, M_IPFW);
3541 ctl3_handlers = tmp;
3542 ctl3_hsize = sz;
3543 ctl3_gencnt++;
3544
3545 CTL3_UNLOCK();
3546 }
3547
3548 /*
3549 * Removes one or more sockopt handlers from the global array.
3550 */
3551 int
ipfw_del_sopt_handler(struct ipfw_sopt_handler * sh,size_t count)3552 ipfw_del_sopt_handler(struct ipfw_sopt_handler *sh, size_t count)
3553 {
3554 size_t sz;
3555 struct ipfw_sopt_handler *tmp, *h;
3556 int i;
3557
3558 CTL3_LOCK();
3559
3560 for (i = 0; i < count; i++) {
3561 tmp = &sh[i];
3562 h = find_sh(tmp->opcode, tmp->version, tmp->handler);
3563 if (h == NULL)
3564 continue;
3565
3566 sz = (ctl3_handlers + ctl3_hsize - (h + 1)) * sizeof(*h);
3567 memmove(h, h + 1, sz);
3568 ctl3_hsize--;
3569 }
3570
3571 if (ctl3_hsize == 0) {
3572 if (ctl3_handlers != NULL)
3573 free(ctl3_handlers, M_IPFW);
3574 ctl3_handlers = NULL;
3575 }
3576
3577 ctl3_gencnt++;
3578
3579 CTL3_UNLOCK();
3580
3581 return (0);
3582 }
3583
3584 /*
3585 * Writes data accumulated in @sd to sockopt buffer.
3586 * Zeroes internal @sd buffer.
3587 */
3588 static int
ipfw_flush_sopt_data(struct sockopt_data * sd)3589 ipfw_flush_sopt_data(struct sockopt_data *sd)
3590 {
3591 struct sockopt *sopt;
3592 int error;
3593 size_t sz;
3594
3595 sz = sd->koff;
3596 if (sz == 0)
3597 return (0);
3598
3599 sopt = sd->sopt;
3600
3601 if (sopt->sopt_dir == SOPT_GET) {
3602 error = copyout(sd->kbuf, sopt->sopt_val, sz);
3603 if (error != 0)
3604 return (error);
3605 }
3606
3607 memset(sd->kbuf, 0, sd->ksize);
3608 sd->ktotal += sz;
3609 sd->koff = 0;
3610 if (sd->ktotal + sd->ksize < sd->valsize)
3611 sd->kavail = sd->ksize;
3612 else
3613 sd->kavail = sd->valsize - sd->ktotal;
3614
3615 /* Update sopt buffer data */
3616 sopt->sopt_valsize = sd->ktotal;
3617 sopt->sopt_val = sd->sopt_val + sd->ktotal;
3618
3619 return (0);
3620 }
3621
3622 /*
3623 * Ensures that @sd buffer has contiguous @neeeded number of
3624 * bytes.
3625 *
3626 * Returns pointer to requested space or NULL.
3627 */
3628 caddr_t
ipfw_get_sopt_space(struct sockopt_data * sd,size_t needed)3629 ipfw_get_sopt_space(struct sockopt_data *sd, size_t needed)
3630 {
3631 int error;
3632 caddr_t addr;
3633
3634 if (sd->kavail < needed) {
3635 /*
3636 * Flush data and try another time.
3637 */
3638 error = ipfw_flush_sopt_data(sd);
3639
3640 if (sd->kavail < needed || error != 0)
3641 return (NULL);
3642 }
3643
3644 addr = sd->kbuf + sd->koff;
3645 sd->koff += needed;
3646 sd->kavail -= needed;
3647 return (addr);
3648 }
3649
3650 /*
3651 * Requests @needed contiguous bytes from @sd buffer.
3652 * Function is used to notify subsystem that we are
3653 * interesed in first @needed bytes (request header)
3654 * and the rest buffer can be safely zeroed.
3655 *
3656 * Returns pointer to requested space or NULL.
3657 */
3658 caddr_t
ipfw_get_sopt_header(struct sockopt_data * sd,size_t needed)3659 ipfw_get_sopt_header(struct sockopt_data *sd, size_t needed)
3660 {
3661 caddr_t addr;
3662
3663 if ((addr = ipfw_get_sopt_space(sd, needed)) == NULL)
3664 return (NULL);
3665
3666 if (sd->kavail > 0)
3667 memset(sd->kbuf + sd->koff, 0, sd->kavail);
3668
3669 return (addr);
3670 }
3671
3672 /*
3673 * New sockopt handler.
3674 */
3675 int
ipfw_ctl3(struct sockopt * sopt)3676 ipfw_ctl3(struct sockopt *sopt)
3677 {
3678 int error, locked;
3679 size_t size, valsize;
3680 struct ip_fw_chain *chain;
3681 char xbuf[256];
3682 struct sockopt_data sdata;
3683 struct ipfw_sopt_handler h;
3684 ip_fw3_opheader *op3 = NULL;
3685
3686 error = priv_check(sopt->sopt_td, PRIV_NETINET_IPFW);
3687 if (error != 0)
3688 return (error);
3689
3690 if (sopt->sopt_name != IP_FW3)
3691 return (ipfw_ctl(sopt));
3692
3693 chain = &V_layer3_chain;
3694 error = 0;
3695
3696 /* Save original valsize before it is altered via sooptcopyin() */
3697 valsize = sopt->sopt_valsize;
3698 memset(&sdata, 0, sizeof(sdata));
3699 /* Read op3 header first to determine actual operation */
3700 op3 = (ip_fw3_opheader *)xbuf;
3701 error = sooptcopyin(sopt, op3, sizeof(*op3), sizeof(*op3));
3702 if (error != 0)
3703 return (error);
3704 sopt->sopt_valsize = valsize;
3705
3706 /*
3707 * Find and reference command.
3708 */
3709 error = find_ref_sh(op3->opcode, op3->version, &h);
3710 if (error != 0)
3711 return (error);
3712
3713 /*
3714 * Disallow modifications in really-really secure mode, but still allow
3715 * the logging counters to be reset.
3716 */
3717 if ((h.dir & HDIR_SET) != 0 && h.opcode != IP_FW_XRESETLOG) {
3718 error = securelevel_ge(sopt->sopt_td->td_ucred, 3);
3719 if (error != 0) {
3720 find_unref_sh(&h);
3721 return (error);
3722 }
3723 }
3724
3725 /*
3726 * Fill in sockopt_data structure that may be useful for
3727 * IP_FW3 get requests.
3728 */
3729 locked = 0;
3730 if (valsize <= sizeof(xbuf)) {
3731 /* use on-stack buffer */
3732 sdata.kbuf = xbuf;
3733 sdata.ksize = sizeof(xbuf);
3734 sdata.kavail = valsize;
3735 } else {
3736
3737 /*
3738 * Determine opcode type/buffer size:
3739 * allocate sliding-window buf for data export or
3740 * contiguous buffer for special ops.
3741 */
3742 if ((h.dir & HDIR_SET) != 0) {
3743 /* Set request. Allocate contigous buffer. */
3744 if (valsize > CTL3_LARGEBUF) {
3745 find_unref_sh(&h);
3746 return (EFBIG);
3747 }
3748
3749 size = valsize;
3750 } else {
3751 /* Get request. Allocate sliding window buffer */
3752 size = (valsize<CTL3_SMALLBUF) ? valsize:CTL3_SMALLBUF;
3753
3754 if (size < valsize) {
3755 /* We have to wire user buffer */
3756 error = vslock(sopt->sopt_val, valsize);
3757 if (error != 0)
3758 return (error);
3759 locked = 1;
3760 }
3761 }
3762
3763 sdata.kbuf = malloc(size, M_TEMP, M_WAITOK | M_ZERO);
3764 sdata.ksize = size;
3765 sdata.kavail = size;
3766 }
3767
3768 sdata.sopt = sopt;
3769 sdata.sopt_val = sopt->sopt_val;
3770 sdata.valsize = valsize;
3771
3772 /*
3773 * Copy either all request (if valsize < bsize_max)
3774 * or first bsize_max bytes to guarantee most consumers
3775 * that all necessary data has been copied).
3776 * Anyway, copy not less than sizeof(ip_fw3_opheader).
3777 */
3778 if ((error = sooptcopyin(sopt, sdata.kbuf, sdata.ksize,
3779 sizeof(ip_fw3_opheader))) != 0)
3780 return (error);
3781 op3 = (ip_fw3_opheader *)sdata.kbuf;
3782
3783 /* Finally, run handler */
3784 error = h.handler(chain, op3, &sdata);
3785 find_unref_sh(&h);
3786
3787 /* Flush state and free buffers */
3788 if (error == 0)
3789 error = ipfw_flush_sopt_data(&sdata);
3790 else
3791 ipfw_flush_sopt_data(&sdata);
3792
3793 if (locked != 0)
3794 vsunlock(sdata.sopt_val, valsize);
3795
3796 /* Restore original pointer and set number of bytes written */
3797 sopt->sopt_val = sdata.sopt_val;
3798 sopt->sopt_valsize = sdata.ktotal;
3799 if (sdata.kbuf != xbuf)
3800 free(sdata.kbuf, M_TEMP);
3801
3802 return (error);
3803 }
3804
3805 /**
3806 * {set|get}sockopt parser.
3807 */
3808 int
ipfw_ctl(struct sockopt * sopt)3809 ipfw_ctl(struct sockopt *sopt)
3810 {
3811 #define RULE_MAXSIZE (512*sizeof(u_int32_t))
3812 int error;
3813 size_t size, valsize;
3814 struct ip_fw *buf;
3815 struct ip_fw_rule0 *rule;
3816 struct ip_fw_chain *chain;
3817 u_int32_t rulenum[2];
3818 uint32_t opt;
3819 struct rule_check_info ci;
3820 IPFW_RLOCK_TRACKER;
3821
3822 chain = &V_layer3_chain;
3823 error = 0;
3824
3825 /* Save original valsize before it is altered via sooptcopyin() */
3826 valsize = sopt->sopt_valsize;
3827 opt = sopt->sopt_name;
3828
3829 /*
3830 * Disallow modifications in really-really secure mode, but still allow
3831 * the logging counters to be reset.
3832 */
3833 if (opt == IP_FW_ADD ||
3834 (sopt->sopt_dir == SOPT_SET && opt != IP_FW_RESETLOG)) {
3835 error = securelevel_ge(sopt->sopt_td->td_ucred, 3);
3836 if (error != 0)
3837 return (error);
3838 }
3839
3840 switch (opt) {
3841 case IP_FW_GET:
3842 /*
3843 * pass up a copy of the current rules. Static rules
3844 * come first (the last of which has number IPFW_DEFAULT_RULE),
3845 * followed by a possibly empty list of dynamic rule.
3846 * The last dynamic rule has NULL in the "next" field.
3847 *
3848 * Note that the calculated size is used to bound the
3849 * amount of data returned to the user. The rule set may
3850 * change between calculating the size and returning the
3851 * data in which case we'll just return what fits.
3852 */
3853 for (;;) {
3854 int len = 0, want;
3855
3856 size = chain->static_len;
3857 size += ipfw_dyn_len();
3858 if (size >= sopt->sopt_valsize)
3859 break;
3860 buf = malloc(size, M_TEMP, M_WAITOK | M_ZERO);
3861 IPFW_UH_RLOCK(chain);
3862 /* check again how much space we need */
3863 want = chain->static_len + ipfw_dyn_len();
3864 if (size >= want)
3865 len = ipfw_getrules(chain, buf, size);
3866 IPFW_UH_RUNLOCK(chain);
3867 if (size >= want)
3868 error = sooptcopyout(sopt, buf, len);
3869 free(buf, M_TEMP);
3870 if (size >= want)
3871 break;
3872 }
3873 break;
3874
3875 case IP_FW_FLUSH:
3876 /* locking is done within del_entry() */
3877 error = del_entry(chain, 0); /* special case, rule=0, cmd=0 means all */
3878 break;
3879
3880 case IP_FW_ADD:
3881 rule = malloc(RULE_MAXSIZE, M_TEMP, M_WAITOK);
3882 error = sooptcopyin(sopt, rule, RULE_MAXSIZE,
3883 sizeof(struct ip_fw7) );
3884
3885 memset(&ci, 0, sizeof(struct rule_check_info));
3886
3887 /*
3888 * If the size of commands equals RULESIZE7 then we assume
3889 * a FreeBSD7.2 binary is talking to us (set is7=1).
3890 * is7 is persistent so the next 'ipfw list' command
3891 * will use this format.
3892 * NOTE: If wrong version is guessed (this can happen if
3893 * the first ipfw command is 'ipfw [pipe] list')
3894 * the ipfw binary may crash or loop infinitly...
3895 */
3896 size = sopt->sopt_valsize;
3897 if (size == RULESIZE7(rule)) {
3898 is7 = 1;
3899 error = convert_rule_to_8(rule);
3900 if (error) {
3901 free(rule, M_TEMP);
3902 return error;
3903 }
3904 size = RULESIZE(rule);
3905 } else
3906 is7 = 0;
3907 if (error == 0)
3908 error = check_ipfw_rule0(rule, size, &ci);
3909 if (error == 0) {
3910 /* locking is done within add_rule() */
3911 struct ip_fw *krule;
3912 krule = ipfw_alloc_rule(chain, RULEKSIZE0(rule));
3913 ci.urule = (caddr_t)rule;
3914 ci.krule = krule;
3915 import_rule0(&ci);
3916 error = commit_rules(chain, &ci, 1);
3917 if (error != 0)
3918 ipfw_free_rule(ci.krule);
3919 else if (sopt->sopt_dir == SOPT_GET) {
3920 if (is7) {
3921 error = convert_rule_to_7(rule);
3922 size = RULESIZE7(rule);
3923 if (error) {
3924 free(rule, M_TEMP);
3925 return error;
3926 }
3927 }
3928 error = sooptcopyout(sopt, rule, size);
3929 }
3930 }
3931 free(rule, M_TEMP);
3932 break;
3933
3934 case IP_FW_DEL:
3935 /*
3936 * IP_FW_DEL is used for deleting single rules or sets,
3937 * and (ab)used to atomically manipulate sets. Argument size
3938 * is used to distinguish between the two:
3939 * sizeof(u_int32_t)
3940 * delete single rule or set of rules,
3941 * or reassign rules (or sets) to a different set.
3942 * 2*sizeof(u_int32_t)
3943 * atomic disable/enable sets.
3944 * first u_int32_t contains sets to be disabled,
3945 * second u_int32_t contains sets to be enabled.
3946 */
3947 error = sooptcopyin(sopt, rulenum,
3948 2*sizeof(u_int32_t), sizeof(u_int32_t));
3949 if (error)
3950 break;
3951 size = sopt->sopt_valsize;
3952 if (size == sizeof(u_int32_t) && rulenum[0] != 0) {
3953 /* delete or reassign, locking done in del_entry() */
3954 error = del_entry(chain, rulenum[0]);
3955 } else if (size == 2*sizeof(u_int32_t)) { /* set enable/disable */
3956 IPFW_UH_WLOCK(chain);
3957 V_set_disable =
3958 (V_set_disable | rulenum[0]) & ~rulenum[1] &
3959 ~(1<<RESVD_SET); /* set RESVD_SET always enabled */
3960 IPFW_UH_WUNLOCK(chain);
3961 } else
3962 error = EINVAL;
3963 break;
3964
3965 case IP_FW_ZERO:
3966 case IP_FW_RESETLOG: /* argument is an u_int_32, the rule number */
3967 rulenum[0] = 0;
3968 if (sopt->sopt_val != 0) {
3969 error = sooptcopyin(sopt, rulenum,
3970 sizeof(u_int32_t), sizeof(u_int32_t));
3971 if (error)
3972 break;
3973 }
3974 error = zero_entry(chain, rulenum[0],
3975 sopt->sopt_name == IP_FW_RESETLOG);
3976 break;
3977
3978 /*--- TABLE opcodes ---*/
3979 case IP_FW_TABLE_ADD:
3980 case IP_FW_TABLE_DEL:
3981 {
3982 ipfw_table_entry ent;
3983 struct tentry_info tei;
3984 struct tid_info ti;
3985 struct table_value v;
3986
3987 error = sooptcopyin(sopt, &ent,
3988 sizeof(ent), sizeof(ent));
3989 if (error)
3990 break;
3991
3992 memset(&tei, 0, sizeof(tei));
3993 tei.paddr = &ent.addr;
3994 tei.subtype = AF_INET;
3995 tei.masklen = ent.masklen;
3996 ipfw_import_table_value_legacy(ent.value, &v);
3997 tei.pvalue = &v;
3998 memset(&ti, 0, sizeof(ti));
3999 ti.uidx = ent.tbl;
4000 ti.type = IPFW_TABLE_CIDR;
4001
4002 error = (opt == IP_FW_TABLE_ADD) ?
4003 add_table_entry(chain, &ti, &tei, 0, 1) :
4004 del_table_entry(chain, &ti, &tei, 0, 1);
4005 }
4006 break;
4007
4008
4009 case IP_FW_TABLE_FLUSH:
4010 {
4011 u_int16_t tbl;
4012 struct tid_info ti;
4013
4014 error = sooptcopyin(sopt, &tbl,
4015 sizeof(tbl), sizeof(tbl));
4016 if (error)
4017 break;
4018 memset(&ti, 0, sizeof(ti));
4019 ti.uidx = tbl;
4020 error = flush_table(chain, &ti);
4021 }
4022 break;
4023
4024 case IP_FW_TABLE_GETSIZE:
4025 {
4026 u_int32_t tbl, cnt;
4027 struct tid_info ti;
4028
4029 if ((error = sooptcopyin(sopt, &tbl, sizeof(tbl),
4030 sizeof(tbl))))
4031 break;
4032 memset(&ti, 0, sizeof(ti));
4033 ti.uidx = tbl;
4034 IPFW_RLOCK(chain);
4035 error = ipfw_count_table(chain, &ti, &cnt);
4036 IPFW_RUNLOCK(chain);
4037 if (error)
4038 break;
4039 error = sooptcopyout(sopt, &cnt, sizeof(cnt));
4040 }
4041 break;
4042
4043 case IP_FW_TABLE_LIST:
4044 {
4045 ipfw_table *tbl;
4046 struct tid_info ti;
4047
4048 if (sopt->sopt_valsize < sizeof(*tbl)) {
4049 error = EINVAL;
4050 break;
4051 }
4052 size = sopt->sopt_valsize;
4053 tbl = malloc(size, M_TEMP, M_WAITOK);
4054 error = sooptcopyin(sopt, tbl, size, sizeof(*tbl));
4055 if (error) {
4056 free(tbl, M_TEMP);
4057 break;
4058 }
4059 tbl->size = (size - sizeof(*tbl)) /
4060 sizeof(ipfw_table_entry);
4061 memset(&ti, 0, sizeof(ti));
4062 ti.uidx = tbl->tbl;
4063 IPFW_RLOCK(chain);
4064 error = ipfw_dump_table_legacy(chain, &ti, tbl);
4065 IPFW_RUNLOCK(chain);
4066 if (error) {
4067 free(tbl, M_TEMP);
4068 break;
4069 }
4070 error = sooptcopyout(sopt, tbl, size);
4071 free(tbl, M_TEMP);
4072 }
4073 break;
4074
4075 /*--- NAT operations are protected by the IPFW_LOCK ---*/
4076 case IP_FW_NAT_CFG:
4077 if (IPFW_NAT_LOADED)
4078 error = ipfw_nat_cfg_ptr(sopt);
4079 else {
4080 printf("IP_FW_NAT_CFG: %s\n",
4081 "ipfw_nat not present, please load it");
4082 error = EINVAL;
4083 }
4084 break;
4085
4086 case IP_FW_NAT_DEL:
4087 if (IPFW_NAT_LOADED)
4088 error = ipfw_nat_del_ptr(sopt);
4089 else {
4090 printf("IP_FW_NAT_DEL: %s\n",
4091 "ipfw_nat not present, please load it");
4092 error = EINVAL;
4093 }
4094 break;
4095
4096 case IP_FW_NAT_GET_CONFIG:
4097 if (IPFW_NAT_LOADED)
4098 error = ipfw_nat_get_cfg_ptr(sopt);
4099 else {
4100 printf("IP_FW_NAT_GET_CFG: %s\n",
4101 "ipfw_nat not present, please load it");
4102 error = EINVAL;
4103 }
4104 break;
4105
4106 case IP_FW_NAT_GET_LOG:
4107 if (IPFW_NAT_LOADED)
4108 error = ipfw_nat_get_log_ptr(sopt);
4109 else {
4110 printf("IP_FW_NAT_GET_LOG: %s\n",
4111 "ipfw_nat not present, please load it");
4112 error = EINVAL;
4113 }
4114 break;
4115
4116 default:
4117 printf("ipfw: ipfw_ctl invalid option %d\n", sopt->sopt_name);
4118 error = EINVAL;
4119 }
4120
4121 return (error);
4122 #undef RULE_MAXSIZE
4123 }
4124 #define RULE_MAXSIZE (256*sizeof(u_int32_t))
4125
4126 /* Functions to convert rules 7.2 <==> 8.0 */
4127 static int
convert_rule_to_7(struct ip_fw_rule0 * rule)4128 convert_rule_to_7(struct ip_fw_rule0 *rule)
4129 {
4130 /* Used to modify original rule */
4131 struct ip_fw7 *rule7 = (struct ip_fw7 *)rule;
4132 /* copy of original rule, version 8 */
4133 struct ip_fw_rule0 *tmp;
4134
4135 /* Used to copy commands */
4136 ipfw_insn *ccmd, *dst;
4137 int ll = 0, ccmdlen = 0;
4138
4139 tmp = malloc(RULE_MAXSIZE, M_TEMP, M_NOWAIT | M_ZERO);
4140 if (tmp == NULL) {
4141 return 1; //XXX error
4142 }
4143 bcopy(rule, tmp, RULE_MAXSIZE);
4144
4145 /* Copy fields */
4146 //rule7->_pad = tmp->_pad;
4147 rule7->set = tmp->set;
4148 rule7->rulenum = tmp->rulenum;
4149 rule7->cmd_len = tmp->cmd_len;
4150 rule7->act_ofs = tmp->act_ofs;
4151 rule7->next_rule = (struct ip_fw7 *)tmp->next_rule;
4152 rule7->cmd_len = tmp->cmd_len;
4153 rule7->pcnt = tmp->pcnt;
4154 rule7->bcnt = tmp->bcnt;
4155 rule7->timestamp = tmp->timestamp;
4156
4157 /* Copy commands */
4158 for (ll = tmp->cmd_len, ccmd = tmp->cmd, dst = rule7->cmd ;
4159 ll > 0 ; ll -= ccmdlen, ccmd += ccmdlen, dst += ccmdlen) {
4160 ccmdlen = F_LEN(ccmd);
4161
4162 bcopy(ccmd, dst, F_LEN(ccmd)*sizeof(uint32_t));
4163
4164 if (dst->opcode > O_NAT)
4165 /* O_REASS doesn't exists in 7.2 version, so
4166 * decrement opcode if it is after O_REASS
4167 */
4168 dst->opcode--;
4169
4170 if (ccmdlen > ll) {
4171 printf("ipfw: opcode %d size truncated\n",
4172 ccmd->opcode);
4173 return EINVAL;
4174 }
4175 }
4176 free(tmp, M_TEMP);
4177
4178 return 0;
4179 }
4180
4181 static int
convert_rule_to_8(struct ip_fw_rule0 * rule)4182 convert_rule_to_8(struct ip_fw_rule0 *rule)
4183 {
4184 /* Used to modify original rule */
4185 struct ip_fw7 *rule7 = (struct ip_fw7 *) rule;
4186
4187 /* Used to copy commands */
4188 ipfw_insn *ccmd, *dst;
4189 int ll = 0, ccmdlen = 0;
4190
4191 /* Copy of original rule */
4192 struct ip_fw7 *tmp = malloc(RULE_MAXSIZE, M_TEMP, M_NOWAIT | M_ZERO);
4193 if (tmp == NULL) {
4194 return 1; //XXX error
4195 }
4196
4197 bcopy(rule7, tmp, RULE_MAXSIZE);
4198
4199 for (ll = tmp->cmd_len, ccmd = tmp->cmd, dst = rule->cmd ;
4200 ll > 0 ; ll -= ccmdlen, ccmd += ccmdlen, dst += ccmdlen) {
4201 ccmdlen = F_LEN(ccmd);
4202
4203 bcopy(ccmd, dst, F_LEN(ccmd)*sizeof(uint32_t));
4204
4205 if (dst->opcode > O_NAT)
4206 /* O_REASS doesn't exists in 7.2 version, so
4207 * increment opcode if it is after O_REASS
4208 */
4209 dst->opcode++;
4210
4211 if (ccmdlen > ll) {
4212 printf("ipfw: opcode %d size truncated\n",
4213 ccmd->opcode);
4214 return EINVAL;
4215 }
4216 }
4217
4218 rule->_pad = tmp->_pad;
4219 rule->set = tmp->set;
4220 rule->rulenum = tmp->rulenum;
4221 rule->cmd_len = tmp->cmd_len;
4222 rule->act_ofs = tmp->act_ofs;
4223 rule->next_rule = (struct ip_fw *)tmp->next_rule;
4224 rule->cmd_len = tmp->cmd_len;
4225 rule->id = 0; /* XXX see if is ok = 0 */
4226 rule->pcnt = tmp->pcnt;
4227 rule->bcnt = tmp->bcnt;
4228 rule->timestamp = tmp->timestamp;
4229
4230 free (tmp, M_TEMP);
4231 return 0;
4232 }
4233
4234 /*
4235 * Named object api
4236 *
4237 */
4238
4239 void
ipfw_init_srv(struct ip_fw_chain * ch)4240 ipfw_init_srv(struct ip_fw_chain *ch)
4241 {
4242
4243 ch->srvmap = ipfw_objhash_create(IPFW_OBJECTS_DEFAULT);
4244 ch->srvstate = malloc(sizeof(void *) * IPFW_OBJECTS_DEFAULT,
4245 M_IPFW, M_WAITOK | M_ZERO);
4246 }
4247
4248 void
ipfw_destroy_srv(struct ip_fw_chain * ch)4249 ipfw_destroy_srv(struct ip_fw_chain *ch)
4250 {
4251
4252 free(ch->srvstate, M_IPFW);
4253 ipfw_objhash_destroy(ch->srvmap);
4254 }
4255
4256 /*
4257 * Allocate new bitmask which can be used to enlarge/shrink
4258 * named instance index.
4259 */
4260 void
ipfw_objhash_bitmap_alloc(uint32_t items,void ** idx,int * pblocks)4261 ipfw_objhash_bitmap_alloc(uint32_t items, void **idx, int *pblocks)
4262 {
4263 size_t size;
4264 int max_blocks;
4265 u_long *idx_mask;
4266
4267 KASSERT((items % BLOCK_ITEMS) == 0,
4268 ("bitmask size needs to power of 2 and greater or equal to %zu",
4269 BLOCK_ITEMS));
4270
4271 max_blocks = items / BLOCK_ITEMS;
4272 size = items / 8;
4273 idx_mask = malloc(size * IPFW_MAX_SETS, M_IPFW, M_WAITOK);
4274 /* Mark all as free */
4275 memset(idx_mask, 0xFF, size * IPFW_MAX_SETS);
4276 *idx_mask &= ~(u_long)1; /* Skip index 0 */
4277
4278 *idx = idx_mask;
4279 *pblocks = max_blocks;
4280 }
4281
4282 /*
4283 * Copy current bitmask index to new one.
4284 */
4285 void
ipfw_objhash_bitmap_merge(struct namedobj_instance * ni,void ** idx,int * blocks)4286 ipfw_objhash_bitmap_merge(struct namedobj_instance *ni, void **idx, int *blocks)
4287 {
4288 int old_blocks, new_blocks;
4289 u_long *old_idx, *new_idx;
4290 int i;
4291
4292 old_idx = ni->idx_mask;
4293 old_blocks = ni->max_blocks;
4294 new_idx = *idx;
4295 new_blocks = *blocks;
4296
4297 for (i = 0; i < IPFW_MAX_SETS; i++) {
4298 memcpy(&new_idx[new_blocks * i], &old_idx[old_blocks * i],
4299 old_blocks * sizeof(u_long));
4300 }
4301 }
4302
4303 /*
4304 * Swaps current @ni index with new one.
4305 */
4306 void
ipfw_objhash_bitmap_swap(struct namedobj_instance * ni,void ** idx,int * blocks)4307 ipfw_objhash_bitmap_swap(struct namedobj_instance *ni, void **idx, int *blocks)
4308 {
4309 int old_blocks;
4310 u_long *old_idx;
4311
4312 old_idx = ni->idx_mask;
4313 old_blocks = ni->max_blocks;
4314
4315 ni->idx_mask = *idx;
4316 ni->max_blocks = *blocks;
4317
4318 /* Save old values */
4319 *idx = old_idx;
4320 *blocks = old_blocks;
4321 }
4322
4323 void
ipfw_objhash_bitmap_free(void * idx,int blocks)4324 ipfw_objhash_bitmap_free(void *idx, int blocks)
4325 {
4326
4327 free(idx, M_IPFW);
4328 }
4329
4330 /*
4331 * Creates named hash instance.
4332 * Must be called without holding any locks.
4333 * Return pointer to new instance.
4334 */
4335 struct namedobj_instance *
ipfw_objhash_create(uint32_t items)4336 ipfw_objhash_create(uint32_t items)
4337 {
4338 struct namedobj_instance *ni;
4339 int i;
4340 size_t size;
4341
4342 size = sizeof(struct namedobj_instance) +
4343 sizeof(struct namedobjects_head) * NAMEDOBJ_HASH_SIZE +
4344 sizeof(struct namedobjects_head) * NAMEDOBJ_HASH_SIZE;
4345
4346 ni = malloc(size, M_IPFW, M_WAITOK | M_ZERO);
4347 ni->nn_size = NAMEDOBJ_HASH_SIZE;
4348 ni->nv_size = NAMEDOBJ_HASH_SIZE;
4349
4350 ni->names = (struct namedobjects_head *)(ni +1);
4351 ni->values = &ni->names[ni->nn_size];
4352
4353 for (i = 0; i < ni->nn_size; i++)
4354 TAILQ_INIT(&ni->names[i]);
4355
4356 for (i = 0; i < ni->nv_size; i++)
4357 TAILQ_INIT(&ni->values[i]);
4358
4359 /* Set default hashing/comparison functions */
4360 ni->hash_f = objhash_hash_name;
4361 ni->cmp_f = objhash_cmp_name;
4362
4363 /* Allocate bitmask separately due to possible resize */
4364 ipfw_objhash_bitmap_alloc(items, (void*)&ni->idx_mask, &ni->max_blocks);
4365
4366 return (ni);
4367 }
4368
4369 void
ipfw_objhash_destroy(struct namedobj_instance * ni)4370 ipfw_objhash_destroy(struct namedobj_instance *ni)
4371 {
4372
4373 free(ni->idx_mask, M_IPFW);
4374 free(ni, M_IPFW);
4375 }
4376
4377 void
ipfw_objhash_set_funcs(struct namedobj_instance * ni,objhash_hash_f * hash_f,objhash_cmp_f * cmp_f)4378 ipfw_objhash_set_funcs(struct namedobj_instance *ni, objhash_hash_f *hash_f,
4379 objhash_cmp_f *cmp_f)
4380 {
4381
4382 ni->hash_f = hash_f;
4383 ni->cmp_f = cmp_f;
4384 }
4385
4386 static uint32_t
objhash_hash_name(struct namedobj_instance * ni,const void * name,uint32_t set)4387 objhash_hash_name(struct namedobj_instance *ni, const void *name, uint32_t set)
4388 {
4389
4390 return (fnv_32_str((const char *)name, FNV1_32_INIT));
4391 }
4392
4393 static int
objhash_cmp_name(struct named_object * no,const void * name,uint32_t set)4394 objhash_cmp_name(struct named_object *no, const void *name, uint32_t set)
4395 {
4396
4397 if ((strcmp(no->name, (const char *)name) == 0) && (no->set == set))
4398 return (0);
4399
4400 return (1);
4401 }
4402
4403 static uint32_t
objhash_hash_idx(struct namedobj_instance * ni,uint32_t val)4404 objhash_hash_idx(struct namedobj_instance *ni, uint32_t val)
4405 {
4406 uint32_t v;
4407
4408 v = val % (ni->nv_size - 1);
4409
4410 return (v);
4411 }
4412
4413 struct named_object *
ipfw_objhash_lookup_name(struct namedobj_instance * ni,uint32_t set,char * name)4414 ipfw_objhash_lookup_name(struct namedobj_instance *ni, uint32_t set, char *name)
4415 {
4416 struct named_object *no;
4417 uint32_t hash;
4418
4419 hash = ni->hash_f(ni, name, set) % ni->nn_size;
4420
4421 TAILQ_FOREACH(no, &ni->names[hash], nn_next) {
4422 if (ni->cmp_f(no, name, set) == 0)
4423 return (no);
4424 }
4425
4426 return (NULL);
4427 }
4428
4429 /*
4430 * Find named object by @uid.
4431 * Check @tlvs for valid data inside.
4432 *
4433 * Returns pointer to found TLV or NULL.
4434 */
4435 ipfw_obj_ntlv *
ipfw_find_name_tlv_type(void * tlvs,int len,uint16_t uidx,uint32_t etlv)4436 ipfw_find_name_tlv_type(void *tlvs, int len, uint16_t uidx, uint32_t etlv)
4437 {
4438 ipfw_obj_ntlv *ntlv;
4439 uintptr_t pa, pe;
4440 int l;
4441
4442 pa = (uintptr_t)tlvs;
4443 pe = pa + len;
4444 l = 0;
4445 for (; pa < pe; pa += l) {
4446 ntlv = (ipfw_obj_ntlv *)pa;
4447 l = ntlv->head.length;
4448
4449 if (l != sizeof(*ntlv))
4450 return (NULL);
4451
4452 if (ntlv->idx != uidx)
4453 continue;
4454 /*
4455 * When userland has specified zero TLV type, do
4456 * not compare it with eltv. In some cases userland
4457 * doesn't know what type should it have. Use only
4458 * uidx and name for search named_object.
4459 */
4460 if (ntlv->head.type != 0 &&
4461 ntlv->head.type != (uint16_t)etlv)
4462 continue;
4463
4464 if (ipfw_check_object_name_generic(ntlv->name) != 0)
4465 return (NULL);
4466
4467 return (ntlv);
4468 }
4469
4470 return (NULL);
4471 }
4472
4473 /*
4474 * Finds object config based on either legacy index
4475 * or name in ntlv.
4476 * Note @ti structure contains unchecked data from userland.
4477 *
4478 * Returns 0 in success and fills in @pno with found config
4479 */
4480 int
ipfw_objhash_find_type(struct namedobj_instance * ni,struct tid_info * ti,uint32_t etlv,struct named_object ** pno)4481 ipfw_objhash_find_type(struct namedobj_instance *ni, struct tid_info *ti,
4482 uint32_t etlv, struct named_object **pno)
4483 {
4484 char *name;
4485 ipfw_obj_ntlv *ntlv;
4486 uint32_t set;
4487
4488 if (ti->tlvs == NULL)
4489 return (EINVAL);
4490
4491 ntlv = ipfw_find_name_tlv_type(ti->tlvs, ti->tlen, ti->uidx, etlv);
4492 if (ntlv == NULL)
4493 return (EINVAL);
4494 name = ntlv->name;
4495
4496 /*
4497 * Use set provided by @ti instead of @ntlv one.
4498 * This is needed due to different sets behavior
4499 * controlled by V_fw_tables_sets.
4500 */
4501 set = ti->set;
4502 *pno = ipfw_objhash_lookup_name(ni, set, name);
4503 if (*pno == NULL)
4504 return (ESRCH);
4505 return (0);
4506 }
4507
4508 /*
4509 * Find named object by name, considering also its TLV type.
4510 */
4511 struct named_object *
ipfw_objhash_lookup_name_type(struct namedobj_instance * ni,uint32_t set,uint32_t type,const char * name)4512 ipfw_objhash_lookup_name_type(struct namedobj_instance *ni, uint32_t set,
4513 uint32_t type, const char *name)
4514 {
4515 struct named_object *no;
4516 uint32_t hash;
4517
4518 hash = ni->hash_f(ni, name, set) % ni->nn_size;
4519
4520 TAILQ_FOREACH(no, &ni->names[hash], nn_next) {
4521 if (ni->cmp_f(no, name, set) == 0 &&
4522 no->etlv == (uint16_t)type)
4523 return (no);
4524 }
4525
4526 return (NULL);
4527 }
4528
4529 struct named_object *
ipfw_objhash_lookup_kidx(struct namedobj_instance * ni,uint16_t kidx)4530 ipfw_objhash_lookup_kidx(struct namedobj_instance *ni, uint16_t kidx)
4531 {
4532 struct named_object *no;
4533 uint32_t hash;
4534
4535 hash = objhash_hash_idx(ni, kidx);
4536
4537 TAILQ_FOREACH(no, &ni->values[hash], nv_next) {
4538 if (no->kidx == kidx)
4539 return (no);
4540 }
4541
4542 return (NULL);
4543 }
4544
4545 int
ipfw_objhash_same_name(struct namedobj_instance * ni,struct named_object * a,struct named_object * b)4546 ipfw_objhash_same_name(struct namedobj_instance *ni, struct named_object *a,
4547 struct named_object *b)
4548 {
4549
4550 if ((strcmp(a->name, b->name) == 0) && a->set == b->set)
4551 return (1);
4552
4553 return (0);
4554 }
4555
4556 void
ipfw_objhash_add(struct namedobj_instance * ni,struct named_object * no)4557 ipfw_objhash_add(struct namedobj_instance *ni, struct named_object *no)
4558 {
4559 uint32_t hash;
4560
4561 hash = ni->hash_f(ni, no->name, no->set) % ni->nn_size;
4562 TAILQ_INSERT_HEAD(&ni->names[hash], no, nn_next);
4563
4564 hash = objhash_hash_idx(ni, no->kidx);
4565 TAILQ_INSERT_HEAD(&ni->values[hash], no, nv_next);
4566
4567 ni->count++;
4568 }
4569
4570 void
ipfw_objhash_del(struct namedobj_instance * ni,struct named_object * no)4571 ipfw_objhash_del(struct namedobj_instance *ni, struct named_object *no)
4572 {
4573 uint32_t hash;
4574
4575 hash = ni->hash_f(ni, no->name, no->set) % ni->nn_size;
4576 TAILQ_REMOVE(&ni->names[hash], no, nn_next);
4577
4578 hash = objhash_hash_idx(ni, no->kidx);
4579 TAILQ_REMOVE(&ni->values[hash], no, nv_next);
4580
4581 ni->count--;
4582 }
4583
4584 uint32_t
ipfw_objhash_count(struct namedobj_instance * ni)4585 ipfw_objhash_count(struct namedobj_instance *ni)
4586 {
4587
4588 return (ni->count);
4589 }
4590
4591 uint32_t
ipfw_objhash_count_type(struct namedobj_instance * ni,uint16_t type)4592 ipfw_objhash_count_type(struct namedobj_instance *ni, uint16_t type)
4593 {
4594 struct named_object *no;
4595 uint32_t count;
4596 int i;
4597
4598 count = 0;
4599 for (i = 0; i < ni->nn_size; i++) {
4600 TAILQ_FOREACH(no, &ni->names[i], nn_next) {
4601 if (no->etlv == type)
4602 count++;
4603 }
4604 }
4605 return (count);
4606 }
4607
4608 /*
4609 * Runs @func for each found named object.
4610 * It is safe to delete objects from callback
4611 */
4612 int
ipfw_objhash_foreach(struct namedobj_instance * ni,objhash_cb_t * f,void * arg)4613 ipfw_objhash_foreach(struct namedobj_instance *ni, objhash_cb_t *f, void *arg)
4614 {
4615 struct named_object *no, *no_tmp;
4616 int i, ret;
4617
4618 for (i = 0; i < ni->nn_size; i++) {
4619 TAILQ_FOREACH_SAFE(no, &ni->names[i], nn_next, no_tmp) {
4620 ret = f(ni, no, arg);
4621 if (ret != 0)
4622 return (ret);
4623 }
4624 }
4625 return (0);
4626 }
4627
4628 /*
4629 * Runs @f for each found named object with type @type.
4630 * It is safe to delete objects from callback
4631 */
4632 int
ipfw_objhash_foreach_type(struct namedobj_instance * ni,objhash_cb_t * f,void * arg,uint16_t type)4633 ipfw_objhash_foreach_type(struct namedobj_instance *ni, objhash_cb_t *f,
4634 void *arg, uint16_t type)
4635 {
4636 struct named_object *no, *no_tmp;
4637 int i, ret;
4638
4639 for (i = 0; i < ni->nn_size; i++) {
4640 TAILQ_FOREACH_SAFE(no, &ni->names[i], nn_next, no_tmp) {
4641 if (no->etlv != type)
4642 continue;
4643 ret = f(ni, no, arg);
4644 if (ret != 0)
4645 return (ret);
4646 }
4647 }
4648 return (0);
4649 }
4650
4651 /*
4652 * Removes index from given set.
4653 * Returns 0 on success.
4654 */
4655 int
ipfw_objhash_free_idx(struct namedobj_instance * ni,uint16_t idx)4656 ipfw_objhash_free_idx(struct namedobj_instance *ni, uint16_t idx)
4657 {
4658 u_long *mask;
4659 int i, v;
4660
4661 i = idx / BLOCK_ITEMS;
4662 v = idx % BLOCK_ITEMS;
4663
4664 if (i >= ni->max_blocks)
4665 return (1);
4666
4667 mask = &ni->idx_mask[i];
4668
4669 if ((*mask & ((u_long)1 << v)) != 0)
4670 return (1);
4671
4672 /* Mark as free */
4673 *mask |= (u_long)1 << v;
4674
4675 /* Update free offset */
4676 if (ni->free_off[0] > i)
4677 ni->free_off[0] = i;
4678
4679 return (0);
4680 }
4681
4682 /*
4683 * Allocate new index in given instance and stores in in @pidx.
4684 * Returns 0 on success.
4685 */
4686 int
ipfw_objhash_alloc_idx(void * n,uint16_t * pidx)4687 ipfw_objhash_alloc_idx(void *n, uint16_t *pidx)
4688 {
4689 struct namedobj_instance *ni;
4690 u_long *mask;
4691 int i, off, v;
4692
4693 ni = (struct namedobj_instance *)n;
4694
4695 off = ni->free_off[0];
4696 mask = &ni->idx_mask[off];
4697
4698 for (i = off; i < ni->max_blocks; i++, mask++) {
4699 if ((v = ffsl(*mask)) == 0)
4700 continue;
4701
4702 /* Mark as busy */
4703 *mask &= ~ ((u_long)1 << (v - 1));
4704
4705 ni->free_off[0] = i;
4706
4707 v = BLOCK_ITEMS * i + v - 1;
4708
4709 *pidx = v;
4710 return (0);
4711 }
4712
4713 return (1);
4714 }
4715
4716 /* end of file */
4717