086d7f02678a51a4ef6ca48bebc258c155570da5
[ipfw.git] / dummynet2 / ip_fw_sockopt.c
1 /*-
2  * Copyright (c) 2002-2009 Luigi Rizzo, Universita` di Pisa
3  *
4  * Supported by: Valeria Paoli
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25  * SUCH DAMAGE.
26  */
27
28 #include <sys/cdefs.h>
29 __FBSDID("$FreeBSD: head/sys/netinet/ipfw/ip_fw_sockopt.c 200601 2009-12-16 10:48:40Z luigi $");
30
31 /*
32  * Sockopt support for ipfw. The routines here implement
33  * the upper half of the ipfw code.
34  */
35
36 #if !defined(KLD_MODULE)
37 #include "opt_ipfw.h"
38 #include "opt_ipdivert.h"
39 #include "opt_ipdn.h"
40 #include "opt_inet.h"
41 #ifndef INET
42 #error IPFIREWALL requires INET.
43 #endif /* INET */
44 #endif
45 #include "opt_inet6.h"
46 #include "opt_ipsec.h"
47
48 #include <sys/param.h>
49 #include <sys/systm.h>
50 #include <sys/malloc.h>
51 #include <sys/mbuf.h>   /* struct m_tag used by nested headers */
52 #include <sys/kernel.h>
53 #include <sys/lock.h>
54 #include <sys/priv.h>
55 #include <sys/proc.h>
56 #include <sys/rwlock.h>
57 #include <sys/socket.h>
58 #include <sys/socketvar.h>
59 #include <sys/sysctl.h>
60 #include <sys/syslog.h>
61 #include <net/if.h>
62 #include <net/route.h>
63 #include <net/vnet.h>
64
65 #include <netinet/in.h>
66 #include <netinet/ip_fw.h>
67 #include <netinet/ipfw/ip_fw_private.h>
68
69 #ifdef MAC
70 #include <security/mac/mac_framework.h>
71 #endif
72
73 MALLOC_DEFINE(M_IPFW, "IpFw/IpAcct", "IpFw/IpAcct chain's");
74
75 /*
76  * static variables followed by global ones (none in this file)
77  */
78
79 /*
80  * Find the smallest rule >= key, id.
81  * We could use bsearch but it is so simple that we code it directly
82  */
83 int
84 ipfw_find_rule(struct ip_fw_chain *chain, uint32_t key, uint32_t id)
85 {
86         int i, lo, hi;
87         struct ip_fw *r;
88
89         for (lo = 0, hi = chain->n_rules - 1; lo < hi;) {
90                 i = (lo + hi) / 2;
91                 r = chain->map[i];
92                 if (r->rulenum < key)
93                         lo = i + 1;     /* continue from the next one */
94                 else if (r->rulenum > key)
95                         hi = i;         /* this might be good */
96                 else if (r->id < id)
97                         lo = i + 1;     /* continue from the next one */
98                 else /* r->id >= id */
99                         hi = i;         /* this might be good */
100         };
101         return hi;
102 }
103
104 /*
105  * allocate a new map, returns the chain locked. extra is the number
106  * of entries to add or delete.
107  */
108 static struct ip_fw **
109 get_map(struct ip_fw_chain *chain, int extra, int locked)
110 {
111
112         for (;;) {
113                 struct ip_fw **map;
114                 int i;
115
116                 i = chain->n_rules + extra;
117                 map = malloc(i * sizeof(struct ip_fw *), M_IPFW, M_WAITOK);
118                 if (map == NULL) {
119                         printf("%s: cannot allocate map\n", __FUNCTION__);
120                         return NULL;
121                 }
122                 if (!locked)
123                         IPFW_UH_WLOCK(chain);
124                 if (i >= chain->n_rules + extra) /* good */
125                         return map;
126                 /* otherwise we lost the race, free and retry */
127                 if (!locked)
128                         IPFW_UH_WUNLOCK(chain);
129                 free(map, M_IPFW);
130         }
131 }
132
133 /*
134  * swap the maps. It is supposed to be called with IPFW_UH_WLOCK
135  */
136 static struct ip_fw **
137 swap_map(struct ip_fw_chain *chain, struct ip_fw **new_map, int new_len)
138 {
139         struct ip_fw **old_map;
140
141         IPFW_WLOCK(chain);
142         chain->id++;
143         chain->n_rules = new_len;
144         old_map = chain->map;
145         chain->map = new_map;
146         IPFW_WUNLOCK(chain);
147         return old_map;
148 }
149
150 /*
151  * Add a new rule to the list. Copy the rule into a malloc'ed area, then
152  * possibly create a rule number and add the rule to the list.
153  * Update the rule_number in the input struct so the caller knows it as well.
154  * XXX DO NOT USE FOR THE DEFAULT RULE.
155  * Must be called without IPFW_UH held
156  */
157 int
158 ipfw_add_rule(struct ip_fw_chain *chain, struct ip_fw *input_rule)
159 {
160         struct ip_fw *rule;
161         int i, l, insert_before;
162         struct ip_fw **map;     /* the new array of pointers */
163
164         if (chain->rules == NULL || input_rule->rulenum > IPFW_DEFAULT_RULE-1)
165                 return (EINVAL);
166
167         l = RULESIZE(input_rule);
168         rule = malloc(l, M_IPFW, M_WAITOK | M_ZERO);
169         if (rule == NULL)
170                 return (ENOSPC);
171         /* get_map returns with IPFW_UH_WLOCK if successful */
172         map = get_map(chain, 1, 0 /* not locked */);
173         if (map == NULL) {
174                 free(rule, M_IPFW);
175                 return ENOSPC;
176         }
177
178         bcopy(input_rule, rule, l);
179         /* clear fields not settable from userland */
180         rule->x_next = NULL;
181         rule->next_rule = NULL;
182         rule->pcnt = 0;
183         rule->bcnt = 0;
184         rule->timestamp = 0;
185
186         if (V_autoinc_step < 1)
187                 V_autoinc_step = 1;
188         else if (V_autoinc_step > 1000)
189                 V_autoinc_step = 1000;
190         /* find the insertion point, we will insert before */
191         insert_before = rule->rulenum ? rule->rulenum + 1 : IPFW_DEFAULT_RULE;
192         i = ipfw_find_rule(chain, insert_before, 0);
193         /* duplicate first part */
194         if (i > 0)
195                 bcopy(chain->map, map, i * sizeof(struct ip_fw *));
196         map[i] = rule;
197         /* duplicate remaining part, we always have the default rule */
198         bcopy(chain->map + i, map + i + 1,
199                 sizeof(struct ip_fw *) *(chain->n_rules - i));
200         if (rule->rulenum == 0) {
201                 /* write back the number */
202                 rule->rulenum = i > 0 ? map[i-1]->rulenum : 0;
203                 if (rule->rulenum < IPFW_DEFAULT_RULE - V_autoinc_step)
204                         rule->rulenum += V_autoinc_step;
205                 input_rule->rulenum = rule->rulenum;
206         }
207
208         rule->id = chain->id + 1;
209         map = swap_map(chain, map, chain->n_rules + 1);
210         chain->static_len += l;
211         IPFW_UH_WUNLOCK(chain);
212         if (map)
213                 free(map, M_IPFW);
214         return (0);
215 }
216
217 /*
218  * Reclaim storage associated with a list of rules.  This is
219  * typically the list created using remove_rule.
220  * A NULL pointer on input is handled correctly.
221  */
222 void
223 ipfw_reap_rules(struct ip_fw *head)
224 {
225         struct ip_fw *rule;
226
227         while ((rule = head) != NULL) {
228                 head = head->x_next;
229                 free(rule, M_IPFW);
230         }
231 }
232
233 /**
234  * Remove all rules with given number, and also do set manipulation.
235  * Assumes chain != NULL && *chain != NULL.
236  *
237  * The argument is an u_int32_t. The low 16 bit are the rule or set number,
238  * the next 8 bits are the new set, the top 8 bits are the command:
239  *
240  *      0       delete rules with given number
241  *      1       delete rules with given set number
242  *      2       move rules with given number to new set
243  *      3       move rules with given set number to new set
244  *      4       swap sets with given numbers
245  *      5       delete rules with given number and with given set number
246  */
247 static int
248 del_entry(struct ip_fw_chain *chain, u_int32_t arg)
249 {
250         struct ip_fw *rule;
251         uint32_t rulenum;       /* rule or old_set */
252         uint8_t cmd, new_set;
253         int start, end = 0, i, ofs, n;
254         struct ip_fw **map = NULL;
255         int error = 0;
256
257         rulenum = arg & 0xffff;
258         cmd = (arg >> 24) & 0xff;
259         new_set = (arg >> 16) & 0xff;
260
261         if (cmd > 5 || new_set > RESVD_SET)
262                 return EINVAL;
263         if (cmd == 0 || cmd == 2 || cmd == 5) {
264                 if (rulenum >= IPFW_DEFAULT_RULE)
265                         return EINVAL;
266         } else {
267                 if (rulenum > RESVD_SET)        /* old_set */
268                         return EINVAL;
269         }
270
271         IPFW_UH_WLOCK(chain); /* prevent conflicts among the writers */
272         chain->reap = NULL;     /* prepare for deletions */
273
274         switch (cmd) {
275         case 0: /* delete rules with given number (0 is special means all) */
276         case 1: /* delete all rules with given set number, rule->set == rulenum */
277         case 5: /* delete rules with given number and with given set number.
278                  * rulenum - given rule number;
279                  * new_set - given set number.
280                  */
281                 /* locate first rule to delete (start), the one after the
282                  * last one (end), and count how many rules to delete (n)
283                  */
284                 n = 0;
285                 if (cmd == 1) { /* look for a specific set, must scan all */
286                         for (start = -1, i = 0; i < chain->n_rules; i++) {
287                                 if (chain->map[start]->set != rulenum)
288                                         continue;
289                                 if (start < 0)
290                                         start = i;
291                                 end = i;
292                                 n++;
293                         }
294                         end++;  /* first non-matching */
295                 } else {
296                         start = ipfw_find_rule(chain, rulenum, 0);
297                         for (end = start; end < chain->n_rules; end++) {
298                                 rule = chain->map[end];
299                                 if (rulenum > 0 && rule->rulenum != rulenum)
300                                         break;
301                                 if (rule->set != RESVD_SET &&
302                                     (cmd == 0 || rule->set == new_set) )
303                                         n++;
304                         }
305                 }
306                 if (n == 0 && arg == 0)
307                         break; /* special case, flush on empty ruleset */
308                 /* allocate the map, if needed */
309                 if (n > 0)
310                         map = get_map(chain, -n, 1 /* locked */);
311                 if (n == 0 || map == NULL) {
312                         error = EINVAL;
313                 break;
314                 }
315                 /* copy the initial part of the map */
316                 if (start > 0)
317                         bcopy(chain->map, map, start * sizeof(struct ip_fw *));
318                 /* copy active rules between start and end */
319                 for (i = ofs = start; i < end; i++) {
320                         rule = chain->map[i];
321                         if (!(rule->set != RESVD_SET &&
322                             (cmd == 0 || rule->set == new_set) ))
323                                 map[ofs++] = chain->map[i];
324                 }
325                 /* finally the tail */
326                 bcopy(chain->map + end, map + ofs,
327                         (chain->n_rules - end) * sizeof(struct ip_fw *));
328                 map = swap_map(chain, map, chain->n_rules - n);
329                 /* now remove the rules deleted */
330                 for (i = start; i < end; i++) {
331                         rule = map[i];
332                         if (rule->set != RESVD_SET &&
333                             (cmd == 0 || rule->set == new_set) ) {
334                                 int l = RULESIZE(rule);
335
336                                 chain->static_len -= l;
337                                 ipfw_remove_dyn_children(rule);
338                                 rule->x_next = chain->reap;
339                                 chain->reap = rule;
340                         }
341                 }
342                 break;
343
344         case 2: /* move rules with given number to new set */
345                 IPFW_UH_WLOCK(chain);
346                 for (i = 0; i < chain->n_rules; i++) {
347                         rule = chain->map[i];
348                         if (rule->rulenum == rulenum)
349                                 rule->set = new_set;
350                 }
351                 IPFW_UH_WUNLOCK(chain);
352                 break;
353
354         case 3: /* move rules with given set number to new set */
355                 IPFW_UH_WLOCK(chain);
356                 for (i = 0; i < chain->n_rules; i++) {
357                         rule = chain->map[i];
358                         if (rule->set == rulenum)
359                                 rule->set = new_set;
360                 }
361                 IPFW_UH_WUNLOCK(chain);
362                 break;
363
364         case 4: /* swap two sets */
365                 IPFW_UH_WLOCK(chain);
366                 for (i = 0; i < chain->n_rules; i++) {
367                         rule = chain->map[i];
368                         if (rule->set == rulenum)
369                                 rule->set = new_set;
370                         else if (rule->set == new_set)
371                                 rule->set = rulenum;
372                 }
373                 IPFW_UH_WUNLOCK(chain);
374                 break;
375         }
376         rule = chain->reap;
377         chain->reap = NULL;
378         IPFW_UH_WUNLOCK(chain);
379         ipfw_reap_rules(rule);
380         if (map)
381                 free(map, M_IPFW);
382         return error;
383 }
384
385 /*
386  * Clear counters for a specific rule.
387  * Normally run under IPFW_UH_RLOCK, but these are idempotent ops
388  * so we only care that rules do not disappear.
389  */
390 static void
391 clear_counters(struct ip_fw *rule, int log_only)
392 {
393         ipfw_insn_log *l = (ipfw_insn_log *)ACTION_PTR(rule);
394
395         if (log_only == 0) {
396                 rule->bcnt = rule->pcnt = 0;
397                 rule->timestamp = 0;
398         }
399         if (l->o.opcode == O_LOG)
400                 l->log_left = l->max_log;
401 }
402
403 /**
404  * Reset some or all counters on firewall rules.
405  * The argument `arg' is an u_int32_t. The low 16 bit are the rule number,
406  * the next 8 bits are the set number, the top 8 bits are the command:
407  *      0       work with rules from all set's;
408  *      1       work with rules only from specified set.
409  * Specified rule number is zero if we want to clear all entries.
410  * log_only is 1 if we only want to reset logs, zero otherwise.
411  */
412 static int
413 zero_entry(struct ip_fw_chain *chain, u_int32_t arg, int log_only)
414 {
415         struct ip_fw *rule;
416         char *msg;
417         int i;
418
419         uint16_t rulenum = arg & 0xffff;
420         uint8_t set = (arg >> 16) & 0xff;
421         uint8_t cmd = (arg >> 24) & 0xff;
422
423         if (cmd > 1)
424                 return (EINVAL);
425         if (cmd == 1 && set > RESVD_SET)
426                 return (EINVAL);
427
428         IPFW_UH_RLOCK(chain);
429         if (rulenum == 0) {
430                 V_norule_counter = 0;
431                 for (i = 0; i < chain->n_rules; i++) {
432                         rule = chain->map[i];
433                         /* Skip rules not in our set. */
434                         if (cmd == 1 && rule->set != set)
435                                 continue;
436                         clear_counters(rule, log_only);
437                 }
438                 msg = log_only ? "All logging counts reset" :
439                     "Accounting cleared";
440         } else {
441                 int cleared = 0;
442                 for (i = 0; i < chain->n_rules; i++) {
443                         rule = chain->map[i];
444                         if (rule->rulenum == rulenum) {
445                                         if (cmd == 0 || rule->set == set)
446                                                 clear_counters(rule, log_only);
447                                 cleared = 1;
448                         }
449                         if (rule->rulenum > rulenum)
450                                 break;
451                         }
452                 if (!cleared) { /* we did not find any matching rules */
453                         IPFW_WUNLOCK(chain);
454                         return (EINVAL);
455                 }
456                 msg = log_only ? "logging count reset" : "cleared";
457         }
458         IPFW_UH_RUNLOCK(chain);
459
460         if (V_fw_verbose) {
461                 int lev = LOG_SECURITY | LOG_NOTICE;
462
463                 if (rulenum)
464                         log(lev, "ipfw: Entry %d %s.\n", rulenum, msg);
465                 else
466                         log(lev, "ipfw: %s.\n", msg);
467         }
468         return (0);
469 }
470
471 /*
472  * Check validity of the structure before insert.
473  * Rules are simple, so this mostly need to check rule sizes.
474  */
475 static int
476 check_ipfw_struct(struct ip_fw *rule, int size)
477 {
478         int l, cmdlen = 0;
479         int have_action=0;
480         ipfw_insn *cmd;
481
482         if (size < sizeof(*rule)) {
483                 printf("ipfw: rule too short\n");
484                 return (EINVAL);
485         }
486         /* first, check for valid size */
487         l = RULESIZE(rule);
488         if (l != size) {
489                 printf("ipfw: size mismatch (have %d want %d)\n", size, l);
490                 return (EINVAL);
491         }
492         if (rule->act_ofs >= rule->cmd_len) {
493                 printf("ipfw: bogus action offset (%u > %u)\n",
494                     rule->act_ofs, rule->cmd_len - 1);
495                 return (EINVAL);
496         }
497         /*
498          * Now go for the individual checks. Very simple ones, basically only
499          * instruction sizes.
500          */
501         for (l = rule->cmd_len, cmd = rule->cmd ;
502                         l > 0 ; l -= cmdlen, cmd += cmdlen) {
503                 cmdlen = F_LEN(cmd);
504                 if (cmdlen > l) {
505                         printf("ipfw: opcode %d size truncated\n",
506                             cmd->opcode);
507                         return EINVAL;
508                 }
509                 switch (cmd->opcode) {
510                 case O_PROBE_STATE:
511                 case O_KEEP_STATE:
512                 case O_PROTO:
513                 case O_IP_SRC_ME:
514                 case O_IP_DST_ME:
515                 case O_LAYER2:
516                 case O_IN:
517                 case O_FRAG:
518                 case O_DIVERTED:
519                 case O_IPOPT:
520                 case O_IPTOS:
521                 case O_IPPRECEDENCE:
522                 case O_IPVER:
523                 case O_TCPWIN:
524                 case O_TCPFLAGS:
525                 case O_TCPOPTS:
526                 case O_ESTAB:
527                 case O_VERREVPATH:
528                 case O_VERSRCREACH:
529                 case O_ANTISPOOF:
530                 case O_IPSEC:
531 #ifdef INET6
532                 case O_IP6_SRC_ME:
533                 case O_IP6_DST_ME:
534                 case O_EXT_HDR:
535                 case O_IP6:
536 #endif
537                 case O_IP4:
538                 case O_TAG:
539                         if (cmdlen != F_INSN_SIZE(ipfw_insn))
540                                 goto bad_size;
541                         break;
542
543                 case O_FIB:
544                         if (cmdlen != F_INSN_SIZE(ipfw_insn))
545                                 goto bad_size;
546                         if (cmd->arg1 >= rt_numfibs) {
547                                 printf("ipfw: invalid fib number %d\n",
548                                         cmd->arg1);
549                                 return EINVAL;
550                         }
551                         break;
552
553                 case O_SETFIB:
554                         if (cmdlen != F_INSN_SIZE(ipfw_insn))
555                                 goto bad_size;
556                         if (cmd->arg1 >= rt_numfibs) {
557                                 printf("ipfw: invalid fib number %d\n",
558                                         cmd->arg1);
559                                 return EINVAL;
560                         }
561                         goto check_action;
562
563                 case O_UID:
564                 case O_GID:
565                 case O_JAIL:
566                 case O_IP_SRC:
567                 case O_IP_DST:
568                 case O_TCPSEQ:
569                 case O_TCPACK:
570                 case O_PROB:
571                 case O_ICMPTYPE:
572                         if (cmdlen != F_INSN_SIZE(ipfw_insn_u32))
573                                 goto bad_size;
574                         break;
575
576                 case O_LIMIT:
577                         if (cmdlen != F_INSN_SIZE(ipfw_insn_limit))
578                                 goto bad_size;
579                         break;
580
581                 case O_LOG:
582                         if (cmdlen != F_INSN_SIZE(ipfw_insn_log))
583                                 goto bad_size;
584
585                         ((ipfw_insn_log *)cmd)->log_left =
586                             ((ipfw_insn_log *)cmd)->max_log;
587
588                         break;
589
590                 case O_IP_SRC_MASK:
591                 case O_IP_DST_MASK:
592                         /* only odd command lengths */
593                         if ( !(cmdlen & 1) || cmdlen > 31)
594                                 goto bad_size;
595                         break;
596
597                 case O_IP_SRC_SET:
598                 case O_IP_DST_SET:
599                         if (cmd->arg1 == 0 || cmd->arg1 > 256) {
600                                 printf("ipfw: invalid set size %d\n",
601                                         cmd->arg1);
602                                 return EINVAL;
603                         }
604                         if (cmdlen != F_INSN_SIZE(ipfw_insn_u32) +
605                             (cmd->arg1+31)/32 )
606                                 goto bad_size;
607                         break;
608
609                 case O_IP_SRC_LOOKUP:
610                 case O_IP_DST_LOOKUP:
611                         if (cmd->arg1 >= IPFW_TABLES_MAX) {
612                                 printf("ipfw: invalid table number %d\n",
613                                     cmd->arg1);
614                                 return (EINVAL);
615                         }
616                         if (cmdlen != F_INSN_SIZE(ipfw_insn) &&
617                             cmdlen != F_INSN_SIZE(ipfw_insn_u32) + 1 &&
618                             cmdlen != F_INSN_SIZE(ipfw_insn_u32))
619                                 goto bad_size;
620                         break;
621
622                 case O_MACADDR2:
623                         if (cmdlen != F_INSN_SIZE(ipfw_insn_mac))
624                                 goto bad_size;
625                         break;
626
627                 case O_NOP:
628                 case O_IPID:
629                 case O_IPTTL:
630                 case O_IPLEN:
631                 case O_TCPDATALEN:
632                 case O_TAGGED:
633                         if (cmdlen < 1 || cmdlen > 31)
634                                 goto bad_size;
635                         break;
636
637                 case O_MAC_TYPE:
638                 case O_IP_SRCPORT:
639                 case O_IP_DSTPORT: /* XXX artificial limit, 30 port pairs */
640                         if (cmdlen < 2 || cmdlen > 31)
641                                 goto bad_size;
642                         break;
643
644                 case O_RECV:
645                 case O_XMIT:
646                 case O_VIA:
647                         if (cmdlen != F_INSN_SIZE(ipfw_insn_if))
648                                 goto bad_size;
649                         break;
650
651                 case O_ALTQ:
652                         if (cmdlen != F_INSN_SIZE(ipfw_insn_altq))
653                                 goto bad_size;
654                         break;
655
656                 case O_PIPE:
657                 case O_QUEUE:
658                         if (cmdlen != F_INSN_SIZE(ipfw_insn))
659                                 goto bad_size;
660                         goto check_action;
661
662                 case O_FORWARD_IP:
663 #ifdef  IPFIREWALL_FORWARD
664                         if (cmdlen != F_INSN_SIZE(ipfw_insn_sa))
665                                 goto bad_size;
666                         goto check_action;
667 #else
668                         return EINVAL;
669 #endif
670
671                 case O_DIVERT:
672                 case O_TEE:
673                         if (ip_divert_ptr == NULL)
674                                 return EINVAL;
675                         else
676                                 goto check_size;
677                 case O_NETGRAPH:
678                 case O_NGTEE:
679                         if (!NG_IPFW_LOADED)
680                                 return EINVAL;
681                         else
682                                 goto check_size;
683                 case O_NAT:
684                         if (!IPFW_NAT_LOADED)
685                                 return EINVAL;
686                         if (cmdlen != F_INSN_SIZE(ipfw_insn_nat))
687                                 goto bad_size;          
688                         goto check_action;
689                 case O_FORWARD_MAC: /* XXX not implemented yet */
690                 case O_CHECK_STATE:
691                 case O_COUNT:
692                 case O_ACCEPT:
693                 case O_DENY:
694                 case O_REJECT:
695 #ifdef INET6
696                 case O_UNREACH6:
697 #endif
698                 case O_SKIPTO:
699                 case O_REASS:
700 check_size:
701                         if (cmdlen != F_INSN_SIZE(ipfw_insn))
702                                 goto bad_size;
703 check_action:
704                         if (have_action) {
705                                 printf("ipfw: opcode %d, multiple actions"
706                                         " not allowed\n",
707                                         cmd->opcode);
708                                 return EINVAL;
709                         }
710                         have_action = 1;
711                         if (l != cmdlen) {
712                                 printf("ipfw: opcode %d, action must be"
713                                         " last opcode\n",
714                                         cmd->opcode);
715                                 return EINVAL;
716                         }
717                         break;
718 #ifdef INET6
719                 case O_IP6_SRC:
720                 case O_IP6_DST:
721                         if (cmdlen != F_INSN_SIZE(struct in6_addr) +
722                             F_INSN_SIZE(ipfw_insn))
723                                 goto bad_size;
724                         break;
725
726                 case O_FLOW6ID:
727                         if (cmdlen != F_INSN_SIZE(ipfw_insn_u32) +
728                             ((ipfw_insn_u32 *)cmd)->o.arg1)
729                                 goto bad_size;
730                         break;
731
732                 case O_IP6_SRC_MASK:
733                 case O_IP6_DST_MASK:
734                         if ( !(cmdlen & 1) || cmdlen > 127)
735                                 goto bad_size;
736                         break;
737                 case O_ICMP6TYPE:
738                         if( cmdlen != F_INSN_SIZE( ipfw_insn_icmp6 ) )
739                                 goto bad_size;
740                         break;
741 #endif
742
743                 default:
744                         switch (cmd->opcode) {
745 #ifndef INET6
746                         case O_IP6_SRC_ME:
747                         case O_IP6_DST_ME:
748                         case O_EXT_HDR:
749                         case O_IP6:
750                         case O_UNREACH6:
751                         case O_IP6_SRC:
752                         case O_IP6_DST:
753                         case O_FLOW6ID:
754                         case O_IP6_SRC_MASK:
755                         case O_IP6_DST_MASK:
756                         case O_ICMP6TYPE:
757                                 printf("ipfw: no IPv6 support in kernel\n");
758                                 return EPROTONOSUPPORT;
759 #endif
760                         default:
761                                 printf("ipfw: opcode %d, unknown opcode\n",
762                                         cmd->opcode);
763                                 return EINVAL;
764                         }
765                 }
766         }
767         if (have_action == 0) {
768                 printf("ipfw: missing action\n");
769                 return EINVAL;
770         }
771         return 0;
772
773 bad_size:
774         printf("ipfw: opcode %d size %d wrong\n",
775                 cmd->opcode, cmdlen);
776         return EINVAL;
777 }
778
779 /*
780  * Copy the static and dynamic rules to the supplied buffer
781  * and return the amount of space actually used.
782  * Must be run under IPFW_UH_RLOCK
783  */
784 static size_t
785 ipfw_getrules(struct ip_fw_chain *chain, void *buf, size_t space)
786 {
787         char *bp = buf;
788         char *ep = bp + space;
789         struct ip_fw *rule, *dst;
790         int l, i;
791         time_t  boot_seconds;
792
793         boot_seconds = boottime.tv_sec;
794         for (i = 0; i < chain->n_rules; i++) {
795                 rule = chain->map[i];
796                 l = RULESIZE(rule);
797                 if (bp + l > ep) { /* should not happen */
798                         printf("overflow dumping static rules\n");
799                         break;
800                 }
801                 dst = (struct ip_fw *)bp;
802                 bcopy(rule, dst, l);
803                         /*
804                          * XXX HACK. Store the disable mask in the "next"
805                          * pointer in a wild attempt to keep the ABI the same.
806                          * Why do we do this on EVERY rule?
807                          */
808                 bcopy(&V_set_disable, &dst->next_rule, sizeof(V_set_disable));
809                 if (dst->timestamp)
810                         dst->timestamp += boot_seconds;
811                 bp += l;
812         }
813         ipfw_get_dynamic(&bp, ep); /* protected by the dynamic lock */
814         return (bp - (char *)buf);
815 }
816
817
818 /**
819  * {set|get}sockopt parser.
820  */
821 int
822 ipfw_ctl(struct sockopt *sopt)
823 {
824 #define RULE_MAXSIZE    (256*sizeof(u_int32_t))
825         int error;
826         size_t size;
827         struct ip_fw *buf, *rule;
828         struct ip_fw_chain *chain;
829         u_int32_t rulenum[2];
830
831         error = priv_check(sopt->sopt_td, PRIV_NETINET_IPFW);
832         if (error)
833                 return (error);
834
835         /*
836          * Disallow modifications in really-really secure mode, but still allow
837          * the logging counters to be reset.
838          */
839         if (sopt->sopt_name == IP_FW_ADD ||
840             (sopt->sopt_dir == SOPT_SET && sopt->sopt_name != IP_FW_RESETLOG)) {
841                 error = securelevel_ge(sopt->sopt_td->td_ucred, 3);
842                 if (error)
843                         return (error);
844         }
845
846         chain = &V_layer3_chain;
847         error = 0;
848
849         switch (sopt->sopt_name) {
850         case IP_FW_GET:
851                 /*
852                  * pass up a copy of the current rules. Static rules
853                  * come first (the last of which has number IPFW_DEFAULT_RULE),
854                  * followed by a possibly empty list of dynamic rule.
855                  * The last dynamic rule has NULL in the "next" field.
856                  *
857                  * Note that the calculated size is used to bound the
858                  * amount of data returned to the user.  The rule set may
859                  * change between calculating the size and returning the
860                  * data in which case we'll just return what fits.
861                  */
862                 for (;;) {
863                         int len = 0, want;
864
865                         size = chain->static_len;
866                         size += ipfw_dyn_len();
867                 if (size >= sopt->sopt_valsize)
868                         break;
869                 buf = malloc(size, M_TEMP, M_WAITOK);
870                         if (buf == NULL)
871                                 break;
872                         IPFW_UH_RLOCK(chain);
873                         /* check again how much space we need */
874                         want = chain->static_len + ipfw_dyn_len();
875                         if (size >= want)
876                                 len = ipfw_getrules(chain, buf, size);
877                         IPFW_UH_RUNLOCK(chain);
878                         if (size >= want)
879                                 error = sooptcopyout(sopt, buf, len);
880                 free(buf, M_TEMP);
881                         if (size >= want)
882                                 break;
883                 }
884                 break;
885
886         case IP_FW_FLUSH:
887                 /* locking is done within del_entry() */
888                 error = del_entry(chain, 0); /* special case, rule=0, cmd=0 means all */
889                 break;
890
891         case IP_FW_ADD:
892                 rule = malloc(RULE_MAXSIZE, M_TEMP, M_WAITOK);
893                 error = sooptcopyin(sopt, rule, RULE_MAXSIZE,
894                         sizeof(struct ip_fw) );
895                 if (error == 0)
896                         error = check_ipfw_struct(rule, sopt->sopt_valsize);
897                 if (error == 0) {
898                         /* locking is done within ipfw_add_rule() */
899                         error = ipfw_add_rule(chain, rule);
900                         size = RULESIZE(rule);
901                         if (!error && sopt->sopt_dir == SOPT_GET)
902                                 error = sooptcopyout(sopt, rule, size);
903                 }
904                 free(rule, M_TEMP);
905                 break;
906
907         case IP_FW_DEL:
908                 /*
909                  * IP_FW_DEL is used for deleting single rules or sets,
910                  * and (ab)used to atomically manipulate sets. Argument size
911                  * is used to distinguish between the two:
912                  *    sizeof(u_int32_t)
913                  *      delete single rule or set of rules,
914                  *      or reassign rules (or sets) to a different set.
915                  *    2*sizeof(u_int32_t)
916                  *      atomic disable/enable sets.
917                  *      first u_int32_t contains sets to be disabled,
918                  *      second u_int32_t contains sets to be enabled.
919                  */
920                 error = sooptcopyin(sopt, rulenum,
921                         2*sizeof(u_int32_t), sizeof(u_int32_t));
922                 if (error)
923                         break;
924                 size = sopt->sopt_valsize;
925                 if (size == sizeof(u_int32_t) && rulenum[0] != 0) {
926                         /* delete or reassign, locking done in del_entry() */
927                         error = del_entry(chain, rulenum[0]);
928                 } else if (size == 2*sizeof(u_int32_t)) { /* set enable/disable */
929                         IPFW_UH_WLOCK(chain);
930                         V_set_disable =
931                             (V_set_disable | rulenum[0]) & ~rulenum[1] &
932                             ~(1<<RESVD_SET); /* set RESVD_SET always enabled */
933                         IPFW_UH_WUNLOCK(chain);
934                 } else
935                         error = EINVAL;
936                 break;
937
938         case IP_FW_ZERO:
939         case IP_FW_RESETLOG: /* argument is an u_int_32, the rule number */
940                 rulenum[0] = 0;
941                 if (sopt->sopt_val != 0) {
942                     error = sooptcopyin(sopt, rulenum,
943                             sizeof(u_int32_t), sizeof(u_int32_t));
944                     if (error)
945                         break;
946                 }
947                 error = zero_entry(chain, rulenum[0],
948                         sopt->sopt_name == IP_FW_RESETLOG);
949                 break;
950
951         /*--- TABLE manipulations are protected by the IPFW_LOCK ---*/
952         case IP_FW_TABLE_ADD:
953                 {
954                         ipfw_table_entry ent;
955
956                         error = sooptcopyin(sopt, &ent,
957                             sizeof(ent), sizeof(ent));
958                         if (error)
959                                 break;
960                         error = ipfw_add_table_entry(chain, ent.tbl,
961                             ent.addr, ent.masklen, ent.value);
962                 }
963                 break;
964
965         case IP_FW_TABLE_DEL:
966                 {
967                         ipfw_table_entry ent;
968
969                         error = sooptcopyin(sopt, &ent,
970                             sizeof(ent), sizeof(ent));
971                         if (error)
972                                 break;
973                         error = ipfw_del_table_entry(chain, ent.tbl,
974                             ent.addr, ent.masklen);
975                 }
976                 break;
977
978         case IP_FW_TABLE_FLUSH:
979                 {
980                         u_int16_t tbl;
981
982                         error = sooptcopyin(sopt, &tbl,
983                             sizeof(tbl), sizeof(tbl));
984                         if (error)
985                                 break;
986                         IPFW_WLOCK(chain);
987                         error = ipfw_flush_table(chain, tbl);
988                         IPFW_WUNLOCK(chain);
989                 }
990                 break;
991
992         case IP_FW_TABLE_GETSIZE:
993                 {
994                         u_int32_t tbl, cnt;
995
996                         if ((error = sooptcopyin(sopt, &tbl, sizeof(tbl),
997                             sizeof(tbl))))
998                                 break;
999                         IPFW_RLOCK(chain);
1000                         error = ipfw_count_table(chain, tbl, &cnt);
1001                         IPFW_RUNLOCK(chain);
1002                         if (error)
1003                                 break;
1004                         error = sooptcopyout(sopt, &cnt, sizeof(cnt));
1005                 }
1006                 break;
1007
1008         case IP_FW_TABLE_LIST:
1009                 {
1010                         ipfw_table *tbl;
1011
1012                         if (sopt->sopt_valsize < sizeof(*tbl)) {
1013                                 error = EINVAL;
1014                                 break;
1015                         }
1016                         size = sopt->sopt_valsize;
1017                         tbl = malloc(size, M_TEMP, M_WAITOK);
1018                         error = sooptcopyin(sopt, tbl, size, sizeof(*tbl));
1019                         if (error) {
1020                                 free(tbl, M_TEMP);
1021                                 break;
1022                         }
1023                         tbl->size = (size - sizeof(*tbl)) /
1024                             sizeof(ipfw_table_entry);
1025                         IPFW_RLOCK(chain);
1026                         error = ipfw_dump_table(chain, tbl);
1027                         IPFW_RUNLOCK(chain);
1028                         if (error) {
1029                                 free(tbl, M_TEMP);
1030                                 break;
1031                         }
1032                         error = sooptcopyout(sopt, tbl, size);
1033                         free(tbl, M_TEMP);
1034                 }
1035                 break;
1036
1037         /*--- NAT operations are protected by the IPFW_LOCK ---*/
1038         case IP_FW_NAT_CFG:
1039                 if (IPFW_NAT_LOADED)
1040                         error = ipfw_nat_cfg_ptr(sopt);
1041                 else {
1042                         printf("IP_FW_NAT_CFG: %s\n",
1043                             "ipfw_nat not present, please load it");
1044                         error = EINVAL;
1045                 }
1046                 break;
1047
1048         case IP_FW_NAT_DEL:
1049                 if (IPFW_NAT_LOADED)
1050                         error = ipfw_nat_del_ptr(sopt);
1051                 else {
1052                         printf("IP_FW_NAT_DEL: %s\n",
1053                             "ipfw_nat not present, please load it");
1054                         error = EINVAL;
1055                 }
1056                 break;
1057
1058         case IP_FW_NAT_GET_CONFIG:
1059                 if (IPFW_NAT_LOADED)
1060                         error = ipfw_nat_get_cfg_ptr(sopt);
1061                 else {
1062                         printf("IP_FW_NAT_GET_CFG: %s\n",
1063                             "ipfw_nat not present, please load it");
1064                         error = EINVAL;
1065                 }
1066                 break;
1067
1068         case IP_FW_NAT_GET_LOG:
1069                 if (IPFW_NAT_LOADED)
1070                         error = ipfw_nat_get_log_ptr(sopt);
1071                 else {
1072                         printf("IP_FW_NAT_GET_LOG: %s\n",
1073                             "ipfw_nat not present, please load it");
1074                         error = EINVAL;
1075                 }
1076                 break;
1077
1078         default:
1079                 printf("ipfw: ipfw_ctl invalid option %d\n", sopt->sopt_name);
1080                 error = EINVAL;
1081         }
1082
1083         return (error);
1084 #undef RULE_MAXSIZE
1085 }
1086 /* end of file */