1 /*-
2 * SPDX-License-Identifier: BSD-2-Clause
3 *
4 * Copyright (c) 2025 Yandex LLC
5 * Copyright (c) 2025 Andrey V. Elsukov <ae@FreeBSD.org>
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * SUCH DAMAGE.
27 */
28
29 #include <sys/cdefs.h>
30 /*
31 * Example of compatibility layer for ipfw's rule management routines.
32 */
33
34 #include "opt_inet.h"
35 #include "opt_inet6.h"
36 #include "opt_ipfw.h"
37
38 #include <sys/param.h>
39 #include <sys/systm.h>
40 #include <sys/malloc.h>
41 #include <sys/mbuf.h>
42 #include <sys/module.h>
43 #include <sys/kernel.h>
44 #include <sys/lock.h>
45 #include <sys/priv.h>
46 #include <sys/proc.h>
47 #include <sys/rwlock.h>
48 #include <sys/rmlock.h>
49 #include <sys/socket.h>
50 #include <sys/socketvar.h>
51 #include <sys/sysctl.h>
52 #include <sys/syslog.h>
53 #include <sys/fnv_hash.h>
54 #include <net/if.h>
55 #include <net/pfil.h>
56 #include <net/route.h>
57 #include <net/vnet.h>
58 #include <vm/vm.h>
59 #include <vm/vm_extern.h>
60
61 #include <netinet/in.h>
62 #include <netinet/ip_var.h> /* hooks */
63 #include <netinet/ip_fw.h>
64
65 #include <netpfil/ipfw/ip_fw_private.h>
66 #include <netpfil/ipfw/ip_fw_table.h>
67
68 #ifdef MAC
69 #include <security/mac/mac_framework.h>
70 #endif
71
72 /*
73 * These structures were used by IP_FW3 socket option with version 0.
74 */
75 typedef struct _ipfw_dyn_rule_v0 {
76 ipfw_dyn_rule *next; /* linked list of rules. */
77 struct ip_fw *rule; /* pointer to rule */
78 /* 'rule' is used to pass up the rule number (from the parent) */
79
80 ipfw_dyn_rule *parent; /* pointer to parent rule */
81 u_int64_t pcnt; /* packet match counter */
82 u_int64_t bcnt; /* byte match counter */
83 struct ipfw_flow_id id; /* (masked) flow id */
84 u_int32_t expire; /* expire time */
85 u_int32_t bucket; /* which bucket in hash table */
86 u_int32_t state; /* state of this rule (typically a
87 * combination of TCP flags)
88 */
89 u_int32_t ack_fwd; /* most recent ACKs in forward */
90 u_int32_t ack_rev; /* and reverse directions (used */
91 /* to generate keepalives) */
92 u_int16_t dyn_type; /* rule type */
93 u_int16_t count; /* refcount */
94 u_int16_t kidx; /* index of named object */
95 } __packed __aligned(8) ipfw_dyn_rule_v0;
96
97 typedef struct _ipfw_obj_dyntlv_v0 {
98 ipfw_obj_tlv head;
99 ipfw_dyn_rule_v0 state;
100 } ipfw_obj_dyntlv_v0;
101
102 typedef struct _ipfw_obj_ntlv_v0 {
103 ipfw_obj_tlv head; /* TLV header */
104 uint16_t idx; /* Name index */
105 uint8_t set; /* set, if applicable */
106 uint8_t type; /* object type, if applicable */
107 uint32_t spare; /* unused */
108 char name[64]; /* Null-terminated name */
109 } ipfw_obj_ntlv_v0;
110
111 typedef struct _ipfw_range_tlv_v0 {
112 ipfw_obj_tlv head; /* TLV header */
113 uint32_t flags; /* Range flags */
114 uint16_t start_rule; /* Range start */
115 uint16_t end_rule; /* Range end */
116 uint32_t set; /* Range set to match */
117 uint32_t new_set; /* New set to move/swap to */
118 } ipfw_range_tlv_v0;
119
120 typedef struct _ipfw_range_header_v0 {
121 ip_fw3_opheader opheader; /* IP_FW3 opcode */
122 ipfw_range_tlv_v0 range;
123 } ipfw_range_header_v0;
124
125 typedef struct _ipfw_insn_limit_v0 {
126 ipfw_insn o;
127 uint8_t _pad;
128 uint8_t limit_mask;
129 uint16_t conn_limit;
130 } ipfw_insn_limit_v0;
131
132 typedef struct _ipfw_obj_tentry_v0 {
133 ipfw_obj_tlv head; /* TLV header */
134 uint8_t subtype; /* subtype (IPv4,IPv6) */
135 uint8_t masklen; /* mask length */
136 uint8_t result; /* request result */
137 uint8_t spare0;
138 uint16_t idx; /* Table name index */
139 uint16_t spare1;
140 union {
141 /* Longest field needs to be aligned by 8-byte boundary */
142 struct in_addr addr; /* IPv4 address */
143 uint32_t key; /* uid/gid/port */
144 struct in6_addr addr6; /* IPv6 address */
145 char iface[IF_NAMESIZE]; /* interface name */
146 struct tflow_entry flow;
147 } k;
148 union {
149 ipfw_table_value value; /* value data */
150 uint32_t kidx; /* value kernel index */
151 } v;
152 } ipfw_obj_tentry_v0;
153
154 static sopt_handler_f dump_config_v0, add_rules_v0, del_rules_v0,
155 clear_rules_v0, move_rules_v0, manage_sets_v0, dump_soptcodes_v0,
156 dump_srvobjects_v0;
157
158 static struct ipfw_sopt_handler scodes[] = {
159 { IP_FW_XGET, IP_FW3_OPVER_0, HDIR_GET, dump_config_v0 },
160 { IP_FW_XADD, IP_FW3_OPVER_0, HDIR_BOTH, add_rules_v0 },
161 { IP_FW_XDEL, IP_FW3_OPVER_0, HDIR_BOTH, del_rules_v0 },
162 { IP_FW_XZERO, IP_FW3_OPVER_0, HDIR_SET, clear_rules_v0 },
163 { IP_FW_XRESETLOG, IP_FW3_OPVER_0, HDIR_SET, clear_rules_v0 },
164 { IP_FW_XMOVE, IP_FW3_OPVER_0, HDIR_SET, move_rules_v0 },
165 { IP_FW_SET_SWAP, IP_FW3_OPVER_0, HDIR_SET, manage_sets_v0 },
166 { IP_FW_SET_MOVE, IP_FW3_OPVER_0, HDIR_SET, manage_sets_v0 },
167 { IP_FW_SET_ENABLE, IP_FW3_OPVER_0, HDIR_SET, manage_sets_v0 },
168 { IP_FW_DUMP_SOPTCODES, IP_FW3_OPVER_0, HDIR_GET, dump_soptcodes_v0 },
169 { IP_FW_DUMP_SRVOBJECTS, IP_FW3_OPVER_0, HDIR_GET, dump_srvobjects_v0 },
170 };
171
172 static int
dump_config_v0(struct ip_fw_chain * chain,ip_fw3_opheader * op3,struct sockopt_data * sd)173 dump_config_v0(struct ip_fw_chain *chain, ip_fw3_opheader *op3,
174 struct sockopt_data *sd)
175 {
176 return (EOPNOTSUPP);
177 }
178
179 /*
180 * Calculate the size adjust needed to store opcodes converted from v0
181 * to v1.
182 */
183 static int
adjust_size_v0(ipfw_insn * cmd)184 adjust_size_v0(ipfw_insn *cmd)
185 {
186 int cmdlen, adjust;
187
188 cmdlen = F_LEN(cmd);
189 switch (cmd->opcode) {
190 case O_CHECK_STATE:
191 case O_KEEP_STATE:
192 case O_PROBE_STATE:
193 case O_EXTERNAL_ACTION:
194 case O_EXTERNAL_INSTANCE:
195 adjust = F_INSN_SIZE(ipfw_insn_kidx) - cmdlen;
196 break;
197 case O_LIMIT:
198 adjust = F_INSN_SIZE(ipfw_insn_limit) - cmdlen;
199 break;
200 case O_IP_SRC_LOOKUP:
201 case O_IP_DST_LOOKUP:
202 case O_IP_FLOW_LOOKUP:
203 case O_MAC_SRC_LOOKUP:
204 case O_MAC_DST_LOOKUP:
205 if (cmdlen == F_INSN_SIZE(ipfw_insn))
206 adjust = F_INSN_SIZE(ipfw_insn_kidx) - cmdlen;
207 else
208 adjust = F_INSN_SIZE(ipfw_insn_table) - cmdlen;
209 break;
210 case O_SKIPTO:
211 case O_CALLRETURN:
212 adjust = F_INSN_SIZE(ipfw_insn_u32) - cmdlen;
213 break;
214 default:
215 adjust = 0;
216 }
217 return (adjust);
218 }
219
220 static int
parse_rules_v0(struct ip_fw_chain * chain,ip_fw3_opheader * op3,struct sockopt_data * sd,ipfw_obj_ctlv ** prtlv,struct rule_check_info ** pci)221 parse_rules_v0(struct ip_fw_chain *chain, ip_fw3_opheader *op3,
222 struct sockopt_data *sd, ipfw_obj_ctlv **prtlv,
223 struct rule_check_info **pci)
224 {
225 ipfw_obj_ctlv *ctlv, *rtlv, *tstate;
226 ipfw_obj_ntlv_v0 *ntlv;
227 struct rule_check_info *ci, *cbuf;
228 struct ip_fw_rule *r;
229 size_t count, clen, read, rsize;
230 uint32_t rulenum;
231 int idx, error;
232
233 op3 = (ip_fw3_opheader *)ipfw_get_sopt_space(sd, sd->valsize);
234 ctlv = (ipfw_obj_ctlv *)(op3 + 1);
235 read = sizeof(ip_fw3_opheader);
236 if (read + sizeof(*ctlv) > sd->valsize)
237 return (EINVAL);
238
239 rtlv = NULL;
240 tstate = NULL;
241 cbuf = NULL;
242 /* Table names or other named objects. */
243 if (ctlv->head.type == IPFW_TLV_TBLNAME_LIST) {
244 /* Check size and alignment. */
245 clen = ctlv->head.length;
246 if (read + clen > sd->valsize || clen < sizeof(*ctlv) ||
247 (clen % sizeof(uint64_t)) != 0)
248 return (EINVAL);
249 /* Check for validness. */
250 count = (ctlv->head.length - sizeof(*ctlv)) / sizeof(*ntlv);
251 if (ctlv->count != count || ctlv->objsize != sizeof(*ntlv))
252 return (EINVAL);
253 /*
254 * Check each TLV.
255 * Ensure TLVs are sorted ascending and
256 * there are no duplicates.
257 */
258 idx = -1;
259 ntlv = (ipfw_obj_ntlv_v0 *)(ctlv + 1);
260 while (count > 0) {
261 if (ntlv->head.length != sizeof(ipfw_obj_ntlv_v0))
262 return (EINVAL);
263
264 error = ipfw_check_object_name_generic(ntlv->name);
265 if (error != 0)
266 return (error);
267
268 if (ntlv->idx <= idx)
269 return (EINVAL);
270
271 idx = ntlv->idx;
272 count--;
273 ntlv++;
274 }
275
276 tstate = ctlv;
277 read += ctlv->head.length;
278 ctlv = (ipfw_obj_ctlv *)((caddr_t)ctlv + ctlv->head.length);
279
280 if (read + sizeof(*ctlv) > sd->valsize)
281 return (EINVAL);
282 }
283
284 /* List of rules. */
285 if (ctlv->head.type == IPFW_TLV_RULE_LIST) {
286 clen = ctlv->head.length;
287 if (read + clen > sd->valsize || clen < sizeof(*ctlv) ||
288 (clen % sizeof(uint64_t)) != 0)
289 return (EINVAL);
290
291 clen -= sizeof(*ctlv);
292 if (ctlv->count == 0 ||
293 ctlv->count > clen / sizeof(struct ip_fw_rule))
294 return (EINVAL);
295
296 /* Allocate state for each rule */
297 cbuf = malloc(ctlv->count * sizeof(struct rule_check_info),
298 M_TEMP, M_WAITOK | M_ZERO);
299
300 /*
301 * Check each rule for validness.
302 * Ensure numbered rules are sorted ascending
303 * and properly aligned
304 */
305 rulenum = 0;
306 count = 0;
307 error = 0;
308 ci = cbuf;
309 r = (struct ip_fw_rule *)(ctlv + 1);
310 while (clen > 0) {
311 rsize = RULEUSIZE1(r);
312 if (rsize > clen || count > ctlv->count) {
313 error = EINVAL;
314 break;
315 }
316 ci->ctlv = tstate;
317 ci->version = IP_FW3_OPVER_0;
318 error = ipfw_check_rule(r, rsize, ci);
319 if (error != 0)
320 break;
321
322 /* Check sorting */
323 if (r->rulenum != 0 && r->rulenum < rulenum) {
324 printf("ipfw: wrong order: rulenum %u"
325 " vs %u\n", r->rulenum, rulenum);
326 error = EINVAL;
327 break;
328 }
329 rulenum = r->rulenum;
330 ci->urule = (caddr_t)r;
331 clen -= rsize;
332 r = (struct ip_fw_rule *)((caddr_t)r + rsize);
333 count++;
334 ci++;
335 }
336
337 if (ctlv->count != count || error != 0) {
338 free(cbuf, M_TEMP);
339 return (EINVAL);
340 }
341
342 rtlv = ctlv;
343 read += ctlv->head.length;
344 ctlv = (ipfw_obj_ctlv *)((caddr_t)ctlv + ctlv->head.length);
345 }
346
347 if (read != sd->valsize || rtlv == NULL) {
348 free(cbuf, M_TEMP);
349 return (EINVAL);
350 }
351
352 *prtlv = rtlv;
353 *pci = cbuf;
354 return (0);
355 }
356
357 static void
convert_v0_to_v1(struct rule_check_info * ci,int rule_len)358 convert_v0_to_v1(struct rule_check_info *ci, int rule_len)
359 {
360 struct ip_fw_rule *urule;
361 struct ip_fw *krule;
362 ipfw_insn *src, *dst;
363 int l, cmdlen, newlen;
364
365 urule = (struct ip_fw_rule *)ci->urule;
366 krule = ci->krule;
367 for (l = urule->cmd_len, src = urule->cmd, dst = krule->cmd;
368 l > 0 && rule_len > 0;
369 l -= cmdlen, src += cmdlen,
370 rule_len -= newlen, dst += newlen) {
371 cmdlen = F_LEN(src);
372 switch (src->opcode) {
373 case O_CHECK_STATE:
374 case O_KEEP_STATE:
375 case O_PROBE_STATE:
376 case O_EXTERNAL_ACTION:
377 case O_EXTERNAL_INSTANCE:
378 newlen = F_INSN_SIZE(ipfw_insn_kidx);
379 insntod(dst, kidx)->kidx = src->arg1;
380 break;
381 case O_LIMIT:
382 newlen = F_INSN_SIZE(ipfw_insn_limit);
383 insntod(dst, limit)->kidx = src->arg1;
384 insntod(dst, limit)->limit_mask =
385 insntoc(src, limit)->limit_mask;
386 insntod(dst, limit)->conn_limit =
387 insntoc(src, limit)->conn_limit;
388 break;
389 case O_IP_DST_LOOKUP:
390 if (cmdlen == F_INSN_SIZE(ipfw_insn) + 2) {
391 /* lookup type stored in d[1] */
392 dst->arg1 = insntoc(src, table)->value;
393 }
394 case O_IP_SRC_LOOKUP:
395 case O_IP_FLOW_LOOKUP:
396 case O_MAC_SRC_LOOKUP:
397 case O_MAC_DST_LOOKUP:
398 if (cmdlen == F_INSN_SIZE(ipfw_insn)) {
399 newlen = F_INSN_SIZE(ipfw_insn_kidx);
400 insntod(dst, kidx)->kidx = src->arg1;
401 } else {
402 newlen = F_INSN_SIZE(ipfw_insn_table);
403 insntod(dst, table)->kidx = src->arg1;
404 insntod(dst, table)->value =
405 insntoc(src, u32)->d[0];
406 }
407 break;
408 case O_CALLRETURN:
409 case O_SKIPTO:
410 newlen = F_INSN_SIZE(ipfw_insn_u32);
411 insntod(dst, u32)->d[0] = src->arg1;
412 break;
413 default:
414 newlen = cmdlen;
415 memcpy(dst, src, sizeof(uint32_t) * newlen);
416 continue;
417 }
418 dst->opcode = src->opcode;
419 dst->len = (src->len & (F_NOT | F_OR)) | newlen;
420 }
421 }
422
423 /*
424 * Copy rule @urule from v0 userland format to kernel @krule.
425 */
426 static void
import_rule_v0(struct ip_fw_chain * chain,struct rule_check_info * ci)427 import_rule_v0(struct ip_fw_chain *chain, struct rule_check_info *ci)
428 {
429 struct ip_fw_rule *urule;
430 struct ip_fw *krule;
431 ipfw_insn *cmd;
432 int l, cmdlen, adjust, aadjust;
433
434 urule = (struct ip_fw_rule *)ci->urule;
435 l = urule->cmd_len;
436 cmd = urule->cmd;
437 adjust = aadjust = 0;
438
439 /* Scan all opcodes and determine the needed size */
440 while (l > 0) {
441 adjust += adjust_size_v0(cmd);
442 if (ACTION_PTR(urule) < cmd)
443 aadjust = adjust;
444 cmdlen = F_LEN(cmd);
445 l -= cmdlen;
446 cmd += cmdlen;
447 }
448
449 cmdlen = urule->cmd_len + adjust;
450 krule = ci->krule = ipfw_alloc_rule(chain, /* RULEKSIZE1(cmdlen) */
451 roundup2(sizeof(struct ip_fw) + cmdlen * 4 - 4, 8));
452
453 krule->act_ofs = urule->act_ofs + aadjust;
454 krule->cmd_len = urule->cmd_len + adjust;
455
456 if (adjust != 0)
457 printf("%s: converted rule %u: cmd_len %u -> %u, "
458 "act_ofs %u -> %u\n", __func__, urule->rulenum,
459 urule->cmd_len, krule->cmd_len, urule->act_ofs,
460 krule->act_ofs);
461
462 krule->rulenum = urule->rulenum;
463 krule->set = urule->set;
464 krule->flags = urule->flags;
465
466 /* Save rulenum offset */
467 ci->urule_numoff = offsetof(struct ip_fw_rule, rulenum);
468 convert_v0_to_v1(ci, cmdlen);
469 }
470
471 static int
add_rules_v0(struct ip_fw_chain * chain,ip_fw3_opheader * op3,struct sockopt_data * sd)472 add_rules_v0(struct ip_fw_chain *chain, ip_fw3_opheader *op3,
473 struct sockopt_data *sd)
474 {
475 ipfw_obj_ctlv *rtlv;
476 struct rule_check_info *ci, *nci;
477 int i, ret;
478
479 /*
480 * Check rules buffer for validness.
481 */
482 ret = parse_rules_v0(chain, op3, sd, &rtlv, &nci);
483 if (ret != 0)
484 return (ret);
485 /*
486 * Allocate storage for the kernel representation of rules.
487 */
488 for (i = 0, ci = nci; i < rtlv->count; i++, ci++)
489 import_rule_v0(chain, ci);
490 /*
491 * Try to add new rules to the chain.
492 */
493 if ((ret = ipfw_commit_rules(chain, nci, rtlv->count)) != 0) {
494 for (i = 0, ci = nci; i < rtlv->count; i++, ci++)
495 ipfw_free_rule(ci->krule);
496 }
497 /* Cleanup after ipfw_parse_rules() */
498 free(nci, M_TEMP);
499 return (ret);
500 }
501
502 static int
check_range_tlv_v0(const ipfw_range_tlv_v0 * rt,ipfw_range_tlv * crt)503 check_range_tlv_v0(const ipfw_range_tlv_v0 *rt, ipfw_range_tlv *crt)
504 {
505 if (rt->head.length != sizeof(*rt))
506 return (1);
507 if (rt->start_rule > rt->end_rule)
508 return (1);
509 if (rt->set >= IPFW_MAX_SETS || rt->new_set >= IPFW_MAX_SETS)
510 return (1);
511 if ((rt->flags & IPFW_RCFLAG_USER) != rt->flags)
512 return (1);
513
514 crt->head = rt->head;
515 crt->head.length = sizeof(*crt);
516 crt->flags = rt->flags;
517 crt->start_rule = rt->start_rule;
518 crt->end_rule = rt->end_rule;
519 crt->set = rt->set;
520 crt->new_set = rt->new_set;
521 return (0);
522 }
523
524 static int
del_rules_v0(struct ip_fw_chain * chain,ip_fw3_opheader * op3,struct sockopt_data * sd)525 del_rules_v0(struct ip_fw_chain *chain, ip_fw3_opheader *op3,
526 struct sockopt_data *sd)
527 {
528 ipfw_range_tlv rv;
529 ipfw_range_header_v0 *rh;
530 int error, ndel;
531
532 if (sd->valsize != sizeof(*rh))
533 return (EINVAL);
534
535 rh = (ipfw_range_header_v0 *)ipfw_get_sopt_space(sd, sd->valsize);
536 if (check_range_tlv_v0(&rh->range, &rv) != 0)
537 return (EINVAL);
538
539 ndel = 0;
540 if ((error = delete_range(chain, &rv, &ndel)) != 0)
541 return (error);
542
543 /* Save number of rules deleted */
544 rh->range.new_set = ndel;
545 return (0);
546 }
547
548 static int
clear_rules_v0(struct ip_fw_chain * chain,ip_fw3_opheader * op3,struct sockopt_data * sd)549 clear_rules_v0(struct ip_fw_chain *chain, ip_fw3_opheader *op3,
550 struct sockopt_data *sd)
551 {
552 return (EOPNOTSUPP);
553 }
554
555 static int
move_rules_v0(struct ip_fw_chain * chain,ip_fw3_opheader * op3,struct sockopt_data * sd)556 move_rules_v0(struct ip_fw_chain *chain, ip_fw3_opheader *op3,
557 struct sockopt_data *sd)
558 {
559 return (EOPNOTSUPP);
560 }
561
562 static int
manage_sets_v0(struct ip_fw_chain * chain,ip_fw3_opheader * op3,struct sockopt_data * sd)563 manage_sets_v0(struct ip_fw_chain *chain, ip_fw3_opheader *op3,
564 struct sockopt_data *sd)
565 {
566 return (EOPNOTSUPP);
567 }
568
569 static int
dump_soptcodes_v0(struct ip_fw_chain * chain,ip_fw3_opheader * op3,struct sockopt_data * sd)570 dump_soptcodes_v0(struct ip_fw_chain *chain, ip_fw3_opheader *op3,
571 struct sockopt_data *sd)
572 {
573 return (EOPNOTSUPP);
574 }
575
576 static int
dump_srvobjects_v0(struct ip_fw_chain * chain,ip_fw3_opheader * op3,struct sockopt_data * sd)577 dump_srvobjects_v0(struct ip_fw_chain *chain, ip_fw3_opheader *op3,
578 struct sockopt_data *sd)
579 {
580 return (EOPNOTSUPP);
581 }
582
583 static enum ipfw_opcheck_result
check_opcode_compat(ipfw_insn ** pcmd,int * plen,struct rule_check_info * ci)584 check_opcode_compat(ipfw_insn **pcmd, int *plen, struct rule_check_info *ci)
585 {
586 ipfw_insn *cmd;
587 size_t cmdlen;
588
589 if (ci->version != IP_FW3_OPVER_0)
590 return (FAILED);
591
592 cmd = *pcmd;
593 cmdlen = F_LEN(cmd);
594 switch (cmd->opcode) {
595 case O_PROBE_STATE:
596 case O_KEEP_STATE:
597 if (cmdlen != F_INSN_SIZE(ipfw_insn))
598 return (BAD_SIZE);
599 ci->object_opcodes++;
600 break;
601 case O_LIMIT:
602 if (cmdlen != F_INSN_SIZE(ipfw_insn_limit_v0))
603 return (BAD_SIZE);
604 ci->object_opcodes++;
605 break;
606 case O_IP_SRC_LOOKUP:
607 if (cmdlen > F_INSN_SIZE(ipfw_insn_u32))
608 return (BAD_SIZE);
609 /* FALLTHROUGH */
610 case O_IP_DST_LOOKUP:
611 if (cmdlen != F_INSN_SIZE(ipfw_insn) &&
612 cmdlen != F_INSN_SIZE(ipfw_insn_u32) + 1 &&
613 cmdlen != F_INSN_SIZE(ipfw_insn_u32))
614 return (BAD_SIZE);
615 if (cmd->arg1 >= V_fw_tables_max) {
616 printf("ipfw: invalid table number %u\n",
617 cmd->arg1);
618 return (FAILED);
619 }
620 ci->object_opcodes++;
621 break;
622 case O_IP_FLOW_LOOKUP:
623 if (cmdlen != F_INSN_SIZE(ipfw_insn) &&
624 cmdlen != F_INSN_SIZE(ipfw_insn_u32))
625 return (BAD_SIZE);
626 if (cmd->arg1 >= V_fw_tables_max) {
627 printf("ipfw: invalid table number %u\n",
628 cmd->arg1);
629 return (FAILED);
630 }
631 ci->object_opcodes++;
632 break;
633 case O_CHECK_STATE:
634 ci->object_opcodes++;
635 /* FALLTHROUGH */
636 case O_SKIPTO:
637 case O_CALLRETURN:
638 if (cmdlen != F_INSN_SIZE(ipfw_insn))
639 return (BAD_SIZE);
640 return (CHECK_ACTION);
641
642 case O_EXTERNAL_ACTION:
643 if (cmd->arg1 == 0 ||
644 cmdlen != F_INSN_SIZE(ipfw_insn)) {
645 printf("ipfw: invalid external "
646 "action opcode\n");
647 return (FAILED);
648 }
649 ci->object_opcodes++;
650 /*
651 * Do we have O_EXTERNAL_INSTANCE or O_EXTERNAL_DATA
652 * opcode?
653 */
654 if (*plen != cmdlen) {
655 *plen -= cmdlen;
656 *pcmd = cmd += cmdlen;
657 cmdlen = F_LEN(cmd);
658 if (cmd->opcode == O_EXTERNAL_DATA)
659 return (CHECK_ACTION);
660 if (cmd->opcode != O_EXTERNAL_INSTANCE) {
661 printf("ipfw: invalid opcode "
662 "next to external action %u\n",
663 cmd->opcode);
664 return (FAILED);
665 }
666 if (cmd->arg1 == 0 ||
667 cmdlen != F_INSN_SIZE(ipfw_insn)) {
668 printf("ipfw: invalid external "
669 "action instance opcode\n");
670 return (FAILED);
671 }
672 ci->object_opcodes++;
673 }
674 return (CHECK_ACTION);
675
676 default:
677 return (ipfw_check_opcode(pcmd, plen, ci));
678 }
679 return (SUCCESS);
680 }
681
682 static int
ipfw_compat_modevent(module_t mod,int type,void * unused)683 ipfw_compat_modevent(module_t mod, int type, void *unused)
684 {
685 switch (type) {
686 case MOD_LOAD:
687 IPFW_ADD_SOPT_HANDLER(1, scodes);
688 ipfw_register_compat(check_opcode_compat);
689 break;
690 case MOD_UNLOAD:
691 ipfw_unregister_compat();
692 IPFW_DEL_SOPT_HANDLER(1, scodes);
693 break;
694 default:
695 return (EOPNOTSUPP);
696 }
697 return (0);
698 }
699
700 static moduledata_t ipfw_compat_mod = {
701 "ipfw_compat",
702 ipfw_compat_modevent,
703 0
704 };
705
706 /* Define startup order. */
707 #define IPFW_COMPAT_SI_SUB_FIREWALL SI_SUB_PROTO_FIREWALL
708 #define IPFW_COMPAT_MODEVENT_ORDER (SI_ORDER_ANY - 128) /* after ipfw */
709 #define IPFW_COMPAT_MODULE_ORDER (IPFW_COMPAT_MODEVENT_ORDER + 1)
710
711 DECLARE_MODULE(ipfw_compat, ipfw_compat_mod, IPFW_COMPAT_SI_SUB_FIREWALL,
712 IPFW_COMPAT_MODULE_ORDER);
713 MODULE_DEPEND(ipfw_compat, ipfw, 3, 3, 3);
714 MODULE_VERSION(ipfw_compat, 1);
715