xref: /freebsd/sys/netpfil/ipfw/ip_fw_compat.c (revision 4a77657cbc011ea657ccb079fff6b58b295eccb0)
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