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