xref: /freebsd/sys/netpfil/ipfw/ip_fw_nat.c (revision 9768746b)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3  *
4  * Copyright (c) 2008 Paolo Pisati
5  * All rights reserved.
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 __FBSDID("$FreeBSD$");
31 
32 #include <sys/param.h>
33 #include <sys/systm.h>
34 #include <sys/eventhandler.h>
35 #include <sys/malloc.h>
36 #include <sys/mbuf.h>
37 #include <sys/kernel.h>
38 #include <sys/lock.h>
39 #include <sys/module.h>
40 #include <sys/rwlock.h>
41 #include <sys/rmlock.h>
42 
43 #include <netinet/libalias/alias.h>
44 #include <netinet/libalias/alias_local.h>
45 
46 #include <net/if.h>
47 #include <net/if_var.h>
48 #include <net/if_private.h>
49 #include <netinet/in.h>
50 #include <netinet/ip.h>
51 #include <netinet/ip_var.h>
52 #include <netinet/ip_fw.h>
53 #include <netinet/tcp.h>
54 #include <netinet/udp.h>
55 
56 #include <netpfil/ipfw/ip_fw_private.h>
57 
58 #include <machine/in_cksum.h>	/* XXX for in_cksum */
59 
60 struct cfg_spool {
61 	LIST_ENTRY(cfg_spool)   _next;          /* chain of spool instances */
62 	struct in_addr          addr;
63 	uint16_t		port;
64 };
65 
66 /* Nat redirect configuration. */
67 struct cfg_redir {
68 	LIST_ENTRY(cfg_redir)	_next;	/* chain of redir instances */
69 	uint16_t		mode;	/* type of redirect mode */
70 	uint16_t		proto;	/* protocol: tcp/udp */
71 	struct in_addr		laddr;	/* local ip address */
72 	struct in_addr		paddr;	/* public ip address */
73 	struct in_addr		raddr;	/* remote ip address */
74 	uint16_t		lport;	/* local port */
75 	uint16_t		pport;	/* public port */
76 	uint16_t		rport;	/* remote port	*/
77 	uint16_t		pport_cnt;	/* number of public ports */
78 	uint16_t		rport_cnt;	/* number of remote ports */
79 	struct alias_link	**alink;
80 	u_int16_t		spool_cnt; /* num of entry in spool chain */
81 	/* chain of spool instances */
82 	LIST_HEAD(spool_chain, cfg_spool) spool_chain;
83 };
84 
85 /* Nat configuration data struct. */
86 struct cfg_nat {
87 	/* chain of nat instances */
88 	LIST_ENTRY(cfg_nat)	_next;
89 	int			id;		/* nat id  */
90 	struct in_addr		ip;		/* nat ip address */
91 	struct libalias		*lib;		/* libalias instance */
92 	int			mode;		/* aliasing mode */
93 	int			redir_cnt; /* number of entry in spool chain */
94 	/* chain of redir instances */
95 	LIST_HEAD(redir_chain, cfg_redir) redir_chain;
96 	char			if_name[IF_NAMESIZE];	/* interface name */
97 	u_short			alias_port_lo;	/* low range for port aliasing */
98 	u_short			alias_port_hi;	/* high range for port aliasing */
99 };
100 
101 static eventhandler_tag ifaddr_event_tag;
102 
103 static void
104 ifaddr_change(void *arg __unused, struct ifnet *ifp)
105 {
106 	struct cfg_nat *ptr;
107 	struct ifaddr *ifa;
108 	struct ip_fw_chain *chain;
109 
110 	KASSERT(curvnet == ifp->if_vnet,
111 	    ("curvnet(%p) differs from iface vnet(%p)", curvnet, ifp->if_vnet));
112 
113 	if (V_ipfw_vnet_ready == 0 || V_ipfw_nat_ready == 0)
114 		return;
115 
116 	chain = &V_layer3_chain;
117 	IPFW_UH_WLOCK(chain);
118 	/* Check every nat entry... */
119 	LIST_FOREACH(ptr, &chain->nat, _next) {
120 		struct epoch_tracker et;
121 
122 		/* ...using nic 'ifp->if_xname' as dynamic alias address. */
123 		if (strncmp(ptr->if_name, ifp->if_xname, IF_NAMESIZE) != 0)
124 			continue;
125 		NET_EPOCH_ENTER(et);
126 		CK_STAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) {
127 			if (ifa->ifa_addr == NULL)
128 				continue;
129 			if (ifa->ifa_addr->sa_family != AF_INET)
130 				continue;
131 			IPFW_WLOCK(chain);
132 			ptr->ip = ((struct sockaddr_in *)
133 			    (ifa->ifa_addr))->sin_addr;
134 			LibAliasSetAddress(ptr->lib, ptr->ip);
135 			IPFW_WUNLOCK(chain);
136 		}
137 		NET_EPOCH_EXIT(et);
138 	}
139 	IPFW_UH_WUNLOCK(chain);
140 }
141 
142 /*
143  * delete the pointers for nat entry ix, or all of them if ix < 0
144  */
145 static void
146 flush_nat_ptrs(struct ip_fw_chain *chain, const int ix)
147 {
148 	ipfw_insn_nat *cmd;
149 	int i;
150 
151 	IPFW_WLOCK_ASSERT(chain);
152 	for (i = 0; i < chain->n_rules; i++) {
153 		cmd = (ipfw_insn_nat *)ipfw_get_action(chain->map[i]);
154 		if (cmd->o.opcode == O_NAT && cmd->nat != NULL &&
155 			    (ix < 0 || cmd->nat->id == ix))
156 			cmd->nat = NULL;
157 	}
158 }
159 
160 static void
161 del_redir_spool_cfg(struct cfg_nat *n, struct redir_chain *head)
162 {
163 	struct cfg_redir *r, *tmp_r;
164 	struct cfg_spool *s, *tmp_s;
165 	int i, num;
166 
167 	LIST_FOREACH_SAFE(r, head, _next, tmp_r) {
168 		num = 1; /* Number of alias_link to delete. */
169 		switch (r->mode) {
170 		case NAT44_REDIR_PORT:
171 			num = r->pport_cnt;
172 			/* FALLTHROUGH */
173 		case NAT44_REDIR_ADDR:
174 		case NAT44_REDIR_PROTO:
175 			/* Delete all libalias redirect entry. */
176 			for (i = 0; i < num; i++)
177 				LibAliasRedirectDelete(n->lib, r->alink[i]);
178 			/* Del spool cfg if any. */
179 			LIST_FOREACH_SAFE(s, &r->spool_chain, _next, tmp_s) {
180 				LIST_REMOVE(s, _next);
181 				free(s, M_IPFW);
182 			}
183 			free(r->alink, M_IPFW);
184 			LIST_REMOVE(r, _next);
185 			free(r, M_IPFW);
186 			break;
187 		default:
188 			printf("unknown redirect mode: %u\n", r->mode);
189 			/* XXX - panic?!?!? */
190 			break;
191 		}
192 	}
193 }
194 
195 static int
196 add_redir_spool_cfg(char *buf, struct cfg_nat *ptr)
197 {
198 	struct cfg_redir *r;
199 	struct cfg_spool *s;
200 	struct nat44_cfg_redir *ser_r;
201 	struct nat44_cfg_spool *ser_s;
202 
203 	int cnt, off, i;
204 
205 	for (cnt = 0, off = 0; cnt < ptr->redir_cnt; cnt++) {
206 		ser_r = (struct nat44_cfg_redir *)&buf[off];
207 		r = malloc(sizeof(*r), M_IPFW, M_WAITOK | M_ZERO);
208 		r->mode = ser_r->mode;
209 		r->laddr = ser_r->laddr;
210 		r->paddr = ser_r->paddr;
211 		r->raddr = ser_r->raddr;
212 		r->lport = ser_r->lport;
213 		r->pport = ser_r->pport;
214 		r->rport = ser_r->rport;
215 		r->pport_cnt = ser_r->pport_cnt;
216 		r->rport_cnt = ser_r->rport_cnt;
217 		r->proto = ser_r->proto;
218 		r->spool_cnt = ser_r->spool_cnt;
219 		//memcpy(r, ser_r, SOF_REDIR);
220 		LIST_INIT(&r->spool_chain);
221 		off += sizeof(struct nat44_cfg_redir);
222 		r->alink = malloc(sizeof(struct alias_link *) * r->pport_cnt,
223 		    M_IPFW, M_WAITOK | M_ZERO);
224 		switch (r->mode) {
225 		case NAT44_REDIR_ADDR:
226 			r->alink[0] = LibAliasRedirectAddr(ptr->lib, r->laddr,
227 			    r->paddr);
228 			break;
229 		case NAT44_REDIR_PORT:
230 			for (i = 0 ; i < r->pport_cnt; i++) {
231 				/* If remotePort is all ports, set it to 0. */
232 				u_short remotePortCopy = r->rport + i;
233 				if (r->rport_cnt == 1 && r->rport == 0)
234 					remotePortCopy = 0;
235 				r->alink[i] = LibAliasRedirectPort(ptr->lib,
236 				    r->laddr, htons(r->lport + i), r->raddr,
237 				    htons(remotePortCopy), r->paddr,
238 				    htons(r->pport + i), r->proto);
239 				if (r->alink[i] == NULL) {
240 					r->alink[0] = NULL;
241 					break;
242 				}
243 			}
244 			break;
245 		case NAT44_REDIR_PROTO:
246 			r->alink[0] = LibAliasRedirectProto(ptr->lib ,r->laddr,
247 			    r->raddr, r->paddr, r->proto);
248 			break;
249 		default:
250 			printf("unknown redirect mode: %u\n", r->mode);
251 			break;
252 		}
253 		if (r->alink[0] == NULL) {
254 			printf("LibAliasRedirect* returned NULL\n");
255 			free(r->alink, M_IPFW);
256 			free(r, M_IPFW);
257 			return (EINVAL);
258 		}
259 		/* LSNAT handling. */
260 		for (i = 0; i < r->spool_cnt; i++) {
261 			ser_s = (struct nat44_cfg_spool *)&buf[off];
262 			s = malloc(sizeof(*s), M_IPFW, M_WAITOK | M_ZERO);
263 			s->addr = ser_s->addr;
264 			s->port = ser_s->port;
265 			LibAliasAddServer(ptr->lib, r->alink[0],
266 			    s->addr, htons(s->port));
267 			off += sizeof(struct nat44_cfg_spool);
268 			/* Hook spool entry. */
269 			LIST_INSERT_HEAD(&r->spool_chain, s, _next);
270 		}
271 		/* And finally hook this redir entry. */
272 		LIST_INSERT_HEAD(&ptr->redir_chain, r, _next);
273 	}
274 
275 	return (0);
276 }
277 
278 static void
279 free_nat_instance(struct cfg_nat *ptr)
280 {
281 
282 	del_redir_spool_cfg(ptr, &ptr->redir_chain);
283 	LibAliasUninit(ptr->lib);
284 	free(ptr, M_IPFW);
285 }
286 
287 /*
288  * ipfw_nat - perform mbuf header translation.
289  *
290  * Note V_layer3_chain has to be locked while calling ipfw_nat() in
291  * 'global' operation mode (t == NULL).
292  *
293  */
294 static int
295 ipfw_nat(struct ip_fw_args *args, struct cfg_nat *t, struct mbuf *m)
296 {
297 	struct mbuf *mcl;
298 	struct ip *ip;
299 	/* XXX - libalias duct tape */
300 	int ldt, retval, found;
301 	struct ip_fw_chain *chain;
302 	char *c;
303 
304 	ldt = 0;
305 	retval = 0;
306 	mcl = m_megapullup(m, m->m_pkthdr.len);
307 	if (mcl == NULL) {
308 		args->m = NULL;
309 		return (IP_FW_DENY);
310 	}
311 	M_ASSERTMAPPED(mcl);
312 	ip = mtod(mcl, struct ip *);
313 
314 	/*
315 	 * XXX - Libalias checksum offload 'duct tape':
316 	 *
317 	 * locally generated packets have only pseudo-header checksum
318 	 * calculated and libalias will break it[1], so mark them for
319 	 * later fix.  Moreover there are cases when libalias modifies
320 	 * tcp packet data[2], mark them for later fix too.
321 	 *
322 	 * [1] libalias was never meant to run in kernel, so it does
323 	 * not have any knowledge about checksum offloading, and
324 	 * expects a packet with a full internet checksum.
325 	 * Unfortunately, packets generated locally will have just the
326 	 * pseudo header calculated, and when libalias tries to adjust
327 	 * the checksum it will actually compute a wrong value.
328 	 *
329 	 * [2] when libalias modifies tcp's data content, full TCP
330 	 * checksum has to be recomputed: the problem is that
331 	 * libalias does not have any idea about checksum offloading.
332 	 * To work around this, we do not do checksumming in LibAlias,
333 	 * but only mark the packets in th_x2 field. If we receive a
334 	 * marked packet, we calculate correct checksum for it
335 	 * aware of offloading.  Why such a terrible hack instead of
336 	 * recalculating checksum for each packet?
337 	 * Because the previous checksum was not checked!
338 	 * Recalculating checksums for EVERY packet will hide ALL
339 	 * transmission errors. Yes, marked packets still suffer from
340 	 * this problem. But, sigh, natd(8) has this problem, too.
341 	 *
342 	 * TODO: -make libalias mbuf aware (so
343 	 * it can handle delayed checksum and tso)
344 	 */
345 
346 	if (mcl->m_pkthdr.rcvif == NULL &&
347 	    mcl->m_pkthdr.csum_flags & CSUM_DELAY_DATA)
348 		ldt = 1;
349 
350 	c = mtod(mcl, char *);
351 
352 	/* Check if this is 'global' instance */
353 	if (t == NULL) {
354 		if (args->flags & IPFW_ARGS_IN) {
355 			/* Wrong direction, skip processing */
356 			args->m = mcl;
357 			return (IP_FW_NAT);
358 		}
359 
360 		found = 0;
361 		chain = &V_layer3_chain;
362 		IPFW_RLOCK_ASSERT(chain);
363 		/* Check every nat entry... */
364 		LIST_FOREACH(t, &chain->nat, _next) {
365 			if ((t->mode & PKT_ALIAS_SKIP_GLOBAL) != 0)
366 				continue;
367 			retval = LibAliasOutTry(t->lib, c,
368 			    mcl->m_len + M_TRAILINGSPACE(mcl), 0);
369 			if (retval == PKT_ALIAS_OK) {
370 				/* Nat instance recognises state */
371 				found = 1;
372 				break;
373 			}
374 		}
375 		if (found != 1) {
376 			/* No instance found, return ignore */
377 			args->m = mcl;
378 			return (IP_FW_NAT);
379 		}
380 	} else {
381 		if (args->flags & IPFW_ARGS_IN)
382 			retval = LibAliasIn(t->lib, c,
383 				mcl->m_len + M_TRAILINGSPACE(mcl));
384 		else
385 			retval = LibAliasOut(t->lib, c,
386 				mcl->m_len + M_TRAILINGSPACE(mcl));
387 	}
388 
389 	/*
390 	 * We drop packet when:
391 	 * 1. libalias returns PKT_ALIAS_ERROR;
392 	 * 2. For incoming packets:
393 	 *	a) for unresolved fragments;
394 	 *	b) libalias returns PKT_ALIAS_IGNORED and
395 	 *		PKT_ALIAS_DENY_INCOMING flag is set.
396 	 */
397 	if (retval == PKT_ALIAS_ERROR ||
398 	    ((args->flags & IPFW_ARGS_IN) &&
399 	    (retval == PKT_ALIAS_UNRESOLVED_FRAGMENT ||
400 	    (retval == PKT_ALIAS_IGNORED &&
401 	    (t->mode & PKT_ALIAS_DENY_INCOMING) != 0)))) {
402 		/* XXX - should i add some logging? */
403 		m_free(mcl);
404 		args->m = NULL;
405 		return (IP_FW_DENY);
406 	}
407 
408 	if (retval == PKT_ALIAS_RESPOND)
409 		mcl->m_flags |= M_SKIP_FIREWALL;
410 	mcl->m_pkthdr.len = mcl->m_len = ntohs(ip->ip_len);
411 
412 	/*
413 	 * XXX - libalias checksum offload
414 	 * 'duct tape' (see above)
415 	 */
416 
417 	if ((ip->ip_off & htons(IP_OFFMASK)) == 0 &&
418 	    ip->ip_p == IPPROTO_TCP) {
419 		struct tcphdr 	*th;
420 
421 		th = (struct tcphdr *)(ip + 1);
422 		if (th->th_x2 & (TH_RES1 >> 8))
423 			ldt = 1;
424 	}
425 
426 	if (ldt) {
427 		struct tcphdr 	*th;
428 		struct udphdr 	*uh;
429 		uint16_t ip_len, cksum;
430 
431 		ip_len = ntohs(ip->ip_len);
432 		cksum = in_pseudo(ip->ip_src.s_addr, ip->ip_dst.s_addr,
433 		    htons(ip->ip_p + ip_len - (ip->ip_hl << 2)));
434 
435 		switch (ip->ip_p) {
436 		case IPPROTO_TCP:
437 			th = (struct tcphdr *)(ip + 1);
438 			/*
439 			 * Maybe it was set in
440 			 * libalias...
441 			 */
442 			th->th_x2 &= ~(TH_RES1 >> 8);
443 			th->th_sum = cksum;
444 			mcl->m_pkthdr.csum_data =
445 			    offsetof(struct tcphdr, th_sum);
446 			break;
447 		case IPPROTO_UDP:
448 			uh = (struct udphdr *)(ip + 1);
449 			uh->uh_sum = cksum;
450 			mcl->m_pkthdr.csum_data =
451 			    offsetof(struct udphdr, uh_sum);
452 			break;
453 		}
454 		/* No hw checksum offloading: do it ourselves */
455 		if ((mcl->m_pkthdr.csum_flags & CSUM_DELAY_DATA) == 0) {
456 			in_delayed_cksum(mcl);
457 			mcl->m_pkthdr.csum_flags &= ~CSUM_DELAY_DATA;
458 		}
459 	}
460 	args->m = mcl;
461 	return (IP_FW_NAT);
462 }
463 
464 static struct cfg_nat *
465 lookup_nat(struct nat_list *l, int nat_id)
466 {
467 	struct cfg_nat *res;
468 
469 	LIST_FOREACH(res, l, _next) {
470 		if (res->id == nat_id)
471 			break;
472 	}
473 	return res;
474 }
475 
476 static struct cfg_nat *
477 lookup_nat_name(struct nat_list *l, char *name)
478 {
479 	struct cfg_nat *res;
480 	int id;
481 	char *errptr;
482 
483 	id = strtol(name, &errptr, 10);
484 	if (id == 0 || *errptr != '\0')
485 		return (NULL);
486 
487 	LIST_FOREACH(res, l, _next) {
488 		if (res->id == id)
489 			break;
490 	}
491 	return (res);
492 }
493 
494 /* IP_FW3 configuration routines */
495 
496 static void
497 nat44_config(struct ip_fw_chain *chain, struct nat44_cfg_nat *ucfg)
498 {
499 	struct cfg_nat *ptr, *tcfg;
500 	int gencnt;
501 
502 	/*
503 	 * Find/create nat rule.
504 	 */
505 	IPFW_UH_WLOCK(chain);
506 	gencnt = chain->gencnt;
507 	ptr = lookup_nat_name(&chain->nat, ucfg->name);
508 	if (ptr == NULL) {
509 		IPFW_UH_WUNLOCK(chain);
510 		/* New rule: allocate and init new instance. */
511 		ptr = malloc(sizeof(struct cfg_nat), M_IPFW, M_WAITOK | M_ZERO);
512 		ptr->lib = LibAliasInit(NULL);
513 		LIST_INIT(&ptr->redir_chain);
514 	} else {
515 		/* Entry already present: temporarily unhook it. */
516 		IPFW_WLOCK(chain);
517 		LIST_REMOVE(ptr, _next);
518 		flush_nat_ptrs(chain, ptr->id);
519 		IPFW_WUNLOCK(chain);
520 		IPFW_UH_WUNLOCK(chain);
521 	}
522 
523 	/*
524 	 * Basic nat (re)configuration.
525 	 */
526 	ptr->id = strtol(ucfg->name, NULL, 10);
527 	/*
528 	 * XXX - what if this rule doesn't nat any ip and just
529 	 * redirect?
530 	 * do we set aliasaddress to 0.0.0.0?
531 	 */
532 	ptr->ip = ucfg->ip;
533 	ptr->redir_cnt = ucfg->redir_cnt;
534 	ptr->mode = ucfg->mode;
535 	ptr->alias_port_lo = ucfg->alias_port_lo;
536 	ptr->alias_port_hi = ucfg->alias_port_hi;
537 	strlcpy(ptr->if_name, ucfg->if_name, sizeof(ptr->if_name));
538 	LibAliasSetMode(ptr->lib, ptr->mode, ~0);
539 	LibAliasSetAddress(ptr->lib, ptr->ip);
540 	LibAliasSetAliasPortRange(ptr->lib, ptr->alias_port_lo, ptr->alias_port_hi);
541 
542 	/*
543 	 * Redir and LSNAT configuration.
544 	 */
545 	/* Delete old cfgs. */
546 	del_redir_spool_cfg(ptr, &ptr->redir_chain);
547 	/* Add new entries. */
548 	add_redir_spool_cfg((char *)(ucfg + 1), ptr);
549 	IPFW_UH_WLOCK(chain);
550 
551 	/* Extra check to avoid race with another ipfw_nat_cfg() */
552 	tcfg = NULL;
553 	if (gencnt != chain->gencnt)
554 	    tcfg = lookup_nat_name(&chain->nat, ucfg->name);
555 	IPFW_WLOCK(chain);
556 	if (tcfg != NULL)
557 		LIST_REMOVE(tcfg, _next);
558 	LIST_INSERT_HEAD(&chain->nat, ptr, _next);
559 	IPFW_WUNLOCK(chain);
560 	chain->gencnt++;
561 
562 	IPFW_UH_WUNLOCK(chain);
563 
564 	if (tcfg != NULL)
565 		free_nat_instance(ptr);
566 }
567 
568 /*
569  * Creates/configure nat44 instance
570  * Data layout (v0)(current):
571  * Request: [ ipfw_obj_header nat44_cfg_nat .. ]
572  *
573  * Returns 0 on success
574  */
575 static int
576 nat44_cfg(struct ip_fw_chain *chain, ip_fw3_opheader *op3,
577     struct sockopt_data *sd)
578 {
579 	ipfw_obj_header *oh;
580 	struct nat44_cfg_nat *ucfg;
581 	int id;
582 	size_t read;
583 	char *errptr;
584 
585 	/* Check minimum header size */
586 	if (sd->valsize < (sizeof(*oh) + sizeof(*ucfg)))
587 		return (EINVAL);
588 
589 	oh = (ipfw_obj_header *)sd->kbuf;
590 
591 	/* Basic length checks for TLVs */
592 	if (oh->ntlv.head.length != sizeof(oh->ntlv))
593 		return (EINVAL);
594 
595 	ucfg = (struct nat44_cfg_nat *)(oh + 1);
596 
597 	/* Check if name is properly terminated and looks like number */
598 	if (strnlen(ucfg->name, sizeof(ucfg->name)) == sizeof(ucfg->name))
599 		return (EINVAL);
600 	id = strtol(ucfg->name, &errptr, 10);
601 	if (id == 0 || *errptr != '\0')
602 		return (EINVAL);
603 
604 	read = sizeof(*oh) + sizeof(*ucfg);
605 	/* Check number of redirs */
606 	if (sd->valsize < read + ucfg->redir_cnt*sizeof(struct nat44_cfg_redir))
607 		return (EINVAL);
608 
609 	nat44_config(chain, ucfg);
610 	return (0);
611 }
612 
613 /*
614  * Destroys given nat instances.
615  * Data layout (v0)(current):
616  * Request: [ ipfw_obj_header ]
617  *
618  * Returns 0 on success
619  */
620 static int
621 nat44_destroy(struct ip_fw_chain *chain, ip_fw3_opheader *op3,
622     struct sockopt_data *sd)
623 {
624 	ipfw_obj_header *oh;
625 	struct cfg_nat *ptr;
626 	ipfw_obj_ntlv *ntlv;
627 
628 	/* Check minimum header size */
629 	if (sd->valsize < sizeof(*oh))
630 		return (EINVAL);
631 
632 	oh = (ipfw_obj_header *)sd->kbuf;
633 
634 	/* Basic length checks for TLVs */
635 	if (oh->ntlv.head.length != sizeof(oh->ntlv))
636 		return (EINVAL);
637 
638 	ntlv = &oh->ntlv;
639 	/* Check if name is properly terminated */
640 	if (strnlen(ntlv->name, sizeof(ntlv->name)) == sizeof(ntlv->name))
641 		return (EINVAL);
642 
643 	IPFW_UH_WLOCK(chain);
644 	ptr = lookup_nat_name(&chain->nat, ntlv->name);
645 	if (ptr == NULL) {
646 		IPFW_UH_WUNLOCK(chain);
647 		return (ESRCH);
648 	}
649 	IPFW_WLOCK(chain);
650 	LIST_REMOVE(ptr, _next);
651 	flush_nat_ptrs(chain, ptr->id);
652 	IPFW_WUNLOCK(chain);
653 	IPFW_UH_WUNLOCK(chain);
654 
655 	free_nat_instance(ptr);
656 
657 	return (0);
658 }
659 
660 static void
661 export_nat_cfg(struct cfg_nat *ptr, struct nat44_cfg_nat *ucfg)
662 {
663 
664 	snprintf(ucfg->name, sizeof(ucfg->name), "%d", ptr->id);
665 	ucfg->ip = ptr->ip;
666 	ucfg->redir_cnt = ptr->redir_cnt;
667 	ucfg->mode = ptr->mode;
668 	ucfg->alias_port_lo = ptr->alias_port_lo;
669 	ucfg->alias_port_hi = ptr->alias_port_hi;
670 	strlcpy(ucfg->if_name, ptr->if_name, sizeof(ucfg->if_name));
671 }
672 
673 /*
674  * Gets config for given nat instance
675  * Data layout (v0)(current):
676  * Request: [ ipfw_obj_header nat44_cfg_nat .. ]
677  *
678  * Returns 0 on success
679  */
680 static int
681 nat44_get_cfg(struct ip_fw_chain *chain, ip_fw3_opheader *op3,
682     struct sockopt_data *sd)
683 {
684 	ipfw_obj_header *oh;
685 	struct nat44_cfg_nat *ucfg;
686 	struct cfg_nat *ptr;
687 	struct cfg_redir *r;
688 	struct cfg_spool *s;
689 	struct nat44_cfg_redir *ser_r;
690 	struct nat44_cfg_spool *ser_s;
691 	size_t sz;
692 
693 	sz = sizeof(*oh) + sizeof(*ucfg);
694 	/* Check minimum header size */
695 	if (sd->valsize < sz)
696 		return (EINVAL);
697 
698 	oh = (struct _ipfw_obj_header *)ipfw_get_sopt_header(sd, sz);
699 
700 	/* Basic length checks for TLVs */
701 	if (oh->ntlv.head.length != sizeof(oh->ntlv))
702 		return (EINVAL);
703 
704 	ucfg = (struct nat44_cfg_nat *)(oh + 1);
705 
706 	/* Check if name is properly terminated */
707 	if (strnlen(ucfg->name, sizeof(ucfg->name)) == sizeof(ucfg->name))
708 		return (EINVAL);
709 
710 	IPFW_UH_RLOCK(chain);
711 	ptr = lookup_nat_name(&chain->nat, ucfg->name);
712 	if (ptr == NULL) {
713 		IPFW_UH_RUNLOCK(chain);
714 		return (ESRCH);
715 	}
716 
717 	export_nat_cfg(ptr, ucfg);
718 
719 	/* Estimate memory amount */
720 	sz = sizeof(ipfw_obj_header) + sizeof(struct nat44_cfg_nat);
721 	LIST_FOREACH(r, &ptr->redir_chain, _next) {
722 		sz += sizeof(struct nat44_cfg_redir);
723 		LIST_FOREACH(s, &r->spool_chain, _next)
724 			sz += sizeof(struct nat44_cfg_spool);
725 	}
726 
727 	ucfg->size = sz;
728 	if (sd->valsize < sz) {
729 		/*
730 		 * Submitted buffer size is not enough.
731 		 * WE've already filled in @ucfg structure with
732 		 * relevant info including size, so we
733 		 * can return. Buffer will be flushed automatically.
734 		 */
735 		IPFW_UH_RUNLOCK(chain);
736 		return (ENOMEM);
737 	}
738 
739 	/* Size OK, let's copy data */
740 	LIST_FOREACH(r, &ptr->redir_chain, _next) {
741 		ser_r = (struct nat44_cfg_redir *)ipfw_get_sopt_space(sd,
742 		    sizeof(*ser_r));
743 		ser_r->mode = r->mode;
744 		ser_r->laddr = r->laddr;
745 		ser_r->paddr = r->paddr;
746 		ser_r->raddr = r->raddr;
747 		ser_r->lport = r->lport;
748 		ser_r->pport = r->pport;
749 		ser_r->rport = r->rport;
750 		ser_r->pport_cnt = r->pport_cnt;
751 		ser_r->rport_cnt = r->rport_cnt;
752 		ser_r->proto = r->proto;
753 		ser_r->spool_cnt = r->spool_cnt;
754 
755 		LIST_FOREACH(s, &r->spool_chain, _next) {
756 			ser_s = (struct nat44_cfg_spool *)ipfw_get_sopt_space(
757 			    sd, sizeof(*ser_s));
758 
759 			ser_s->addr = s->addr;
760 			ser_s->port = s->port;
761 		}
762 	}
763 
764 	IPFW_UH_RUNLOCK(chain);
765 
766 	return (0);
767 }
768 
769 /*
770  * Lists all nat44 instances currently available in kernel.
771  * Data layout (v0)(current):
772  * Request: [ ipfw_obj_lheader ]
773  * Reply: [ ipfw_obj_lheader nat44_cfg_nat x N ]
774  *
775  * Returns 0 on success
776  */
777 static int
778 nat44_list_nat(struct ip_fw_chain *chain, ip_fw3_opheader *op3,
779     struct sockopt_data *sd)
780 {
781 	ipfw_obj_lheader *olh;
782 	struct nat44_cfg_nat *ucfg;
783 	struct cfg_nat *ptr;
784 	int nat_count;
785 
786 	/* Check minimum header size */
787 	if (sd->valsize < sizeof(ipfw_obj_lheader))
788 		return (EINVAL);
789 
790 	olh = (ipfw_obj_lheader *)ipfw_get_sopt_header(sd, sizeof(*olh));
791 	IPFW_UH_RLOCK(chain);
792 	nat_count = 0;
793 	LIST_FOREACH(ptr, &chain->nat, _next)
794 		nat_count++;
795 
796 	olh->count = nat_count;
797 	olh->objsize = sizeof(struct nat44_cfg_nat);
798 	olh->size = sizeof(*olh) + olh->count * olh->objsize;
799 
800 	if (sd->valsize < olh->size) {
801 		IPFW_UH_RUNLOCK(chain);
802 		return (ENOMEM);
803 	}
804 
805 	LIST_FOREACH(ptr, &chain->nat, _next) {
806 		ucfg = (struct nat44_cfg_nat *)ipfw_get_sopt_space(sd,
807 		    sizeof(*ucfg));
808 		export_nat_cfg(ptr, ucfg);
809 	}
810 
811 	IPFW_UH_RUNLOCK(chain);
812 
813 	return (0);
814 }
815 
816 /*
817  * Gets log for given nat instance
818  * Data layout (v0)(current):
819  * Request: [ ipfw_obj_header nat44_cfg_nat ]
820  * Reply: [ ipfw_obj_header nat44_cfg_nat LOGBUFFER ]
821  *
822  * Returns 0 on success
823  */
824 static int
825 nat44_get_log(struct ip_fw_chain *chain, ip_fw3_opheader *op3,
826     struct sockopt_data *sd)
827 {
828 	ipfw_obj_header *oh;
829 	struct nat44_cfg_nat *ucfg;
830 	struct cfg_nat *ptr;
831 	void *pbuf;
832 	size_t sz;
833 
834 	sz = sizeof(*oh) + sizeof(*ucfg);
835 	/* Check minimum header size */
836 	if (sd->valsize < sz)
837 		return (EINVAL);
838 
839 	oh = (struct _ipfw_obj_header *)ipfw_get_sopt_header(sd, sz);
840 
841 	/* Basic length checks for TLVs */
842 	if (oh->ntlv.head.length != sizeof(oh->ntlv))
843 		return (EINVAL);
844 
845 	ucfg = (struct nat44_cfg_nat *)(oh + 1);
846 
847 	/* Check if name is properly terminated */
848 	if (strnlen(ucfg->name, sizeof(ucfg->name)) == sizeof(ucfg->name))
849 		return (EINVAL);
850 
851 	IPFW_UH_RLOCK(chain);
852 	ptr = lookup_nat_name(&chain->nat, ucfg->name);
853 	if (ptr == NULL) {
854 		IPFW_UH_RUNLOCK(chain);
855 		return (ESRCH);
856 	}
857 
858 	if (ptr->lib->logDesc == NULL) {
859 		IPFW_UH_RUNLOCK(chain);
860 		return (ENOENT);
861 	}
862 
863 	export_nat_cfg(ptr, ucfg);
864 
865 	/* Estimate memory amount */
866 	ucfg->size = sizeof(struct nat44_cfg_nat) + LIBALIAS_BUF_SIZE;
867 	if (sd->valsize < sz + sizeof(*oh)) {
868 		/*
869 		 * Submitted buffer size is not enough.
870 		 * WE've already filled in @ucfg structure with
871 		 * relevant info including size, so we
872 		 * can return. Buffer will be flushed automatically.
873 		 */
874 		IPFW_UH_RUNLOCK(chain);
875 		return (ENOMEM);
876 	}
877 
878 	pbuf = (void *)ipfw_get_sopt_space(sd, LIBALIAS_BUF_SIZE);
879 	memcpy(pbuf, ptr->lib->logDesc, LIBALIAS_BUF_SIZE);
880 
881 	IPFW_UH_RUNLOCK(chain);
882 
883 	return (0);
884 }
885 
886 static struct ipfw_sopt_handler	scodes[] = {
887 	{ IP_FW_NAT44_XCONFIG,	0,	HDIR_SET,	nat44_cfg },
888 	{ IP_FW_NAT44_DESTROY,	0,	HDIR_SET,	nat44_destroy },
889 	{ IP_FW_NAT44_XGETCONFIG,	0,	HDIR_GET,	nat44_get_cfg },
890 	{ IP_FW_NAT44_LIST_NAT,	0,	HDIR_GET,	nat44_list_nat },
891 	{ IP_FW_NAT44_XGETLOG,	0,	HDIR_GET,	nat44_get_log },
892 };
893 
894 /*
895  * Legacy configuration routines
896  */
897 
898 struct cfg_spool_legacy {
899 	LIST_ENTRY(cfg_spool_legacy)	_next;
900 	struct in_addr			addr;
901 	u_short				port;
902 };
903 
904 struct cfg_redir_legacy {
905 	LIST_ENTRY(cfg_redir)   _next;
906 	u_int16_t               mode;
907 	struct in_addr	        laddr;
908 	struct in_addr	        paddr;
909 	struct in_addr	        raddr;
910 	u_short                 lport;
911 	u_short                 pport;
912 	u_short                 rport;
913 	u_short                 pport_cnt;
914 	u_short                 rport_cnt;
915 	int                     proto;
916 	struct alias_link       **alink;
917 	u_int16_t               spool_cnt;
918 	LIST_HEAD(, cfg_spool_legacy) spool_chain;
919 };
920 
921 struct cfg_nat_legacy {
922 	LIST_ENTRY(cfg_nat_legacy)	_next;
923 	int				id;
924 	struct in_addr			ip;
925 	char				if_name[IF_NAMESIZE];
926 	int				mode;
927 	struct libalias			*lib;
928 	int				redir_cnt;
929 	LIST_HEAD(, cfg_redir_legacy)	redir_chain;
930 };
931 
932 static int
933 ipfw_nat_cfg(struct sockopt *sopt)
934 {
935 	struct cfg_nat_legacy *cfg;
936 	struct nat44_cfg_nat *ucfg;
937 	struct cfg_redir_legacy *rdir;
938 	struct nat44_cfg_redir *urdir;
939 	char *buf;
940 	size_t len, len2;
941 	int error, i;
942 
943 	len = sopt->sopt_valsize;
944 	len2 = len + 128;
945 
946 	/*
947 	 * Allocate 2x buffer to store converted structures.
948 	 * new redir_cfg has shrunk, so we're sure that
949 	 * new buffer size is enough.
950 	 */
951 	buf = malloc(roundup2(len, 8) + len2, M_TEMP, M_WAITOK | M_ZERO);
952 	error = sooptcopyin(sopt, buf, len, sizeof(struct cfg_nat_legacy));
953 	if (error != 0)
954 		goto out;
955 
956 	cfg = (struct cfg_nat_legacy *)buf;
957 	if (cfg->id < 0) {
958 		error = EINVAL;
959 		goto out;
960 	}
961 
962 	ucfg = (struct nat44_cfg_nat *)&buf[roundup2(len, 8)];
963 	snprintf(ucfg->name, sizeof(ucfg->name), "%d", cfg->id);
964 	strlcpy(ucfg->if_name, cfg->if_name, sizeof(ucfg->if_name));
965 	ucfg->ip = cfg->ip;
966 	ucfg->mode = cfg->mode;
967 	ucfg->redir_cnt = cfg->redir_cnt;
968 
969 	if (len < sizeof(*cfg) + cfg->redir_cnt * sizeof(*rdir)) {
970 		error = EINVAL;
971 		goto out;
972 	}
973 
974 	urdir = (struct nat44_cfg_redir *)(ucfg + 1);
975 	rdir = (struct cfg_redir_legacy *)(cfg + 1);
976 	for (i = 0; i < cfg->redir_cnt; i++) {
977 		urdir->mode = rdir->mode;
978 		urdir->laddr = rdir->laddr;
979 		urdir->paddr = rdir->paddr;
980 		urdir->raddr = rdir->raddr;
981 		urdir->lport = rdir->lport;
982 		urdir->pport = rdir->pport;
983 		urdir->rport = rdir->rport;
984 		urdir->pport_cnt = rdir->pport_cnt;
985 		urdir->rport_cnt = rdir->rport_cnt;
986 		urdir->proto = rdir->proto;
987 		urdir->spool_cnt = rdir->spool_cnt;
988 
989 		urdir++;
990 		rdir++;
991 	}
992 
993 	nat44_config(&V_layer3_chain, ucfg);
994 
995 out:
996 	free(buf, M_TEMP);
997 	return (error);
998 }
999 
1000 static int
1001 ipfw_nat_del(struct sockopt *sopt)
1002 {
1003 	struct cfg_nat *ptr;
1004 	struct ip_fw_chain *chain = &V_layer3_chain;
1005 	int i;
1006 
1007 	sooptcopyin(sopt, &i, sizeof i, sizeof i);
1008 	/* XXX validate i */
1009 	IPFW_UH_WLOCK(chain);
1010 	ptr = lookup_nat(&chain->nat, i);
1011 	if (ptr == NULL) {
1012 		IPFW_UH_WUNLOCK(chain);
1013 		return (EINVAL);
1014 	}
1015 	IPFW_WLOCK(chain);
1016 	LIST_REMOVE(ptr, _next);
1017 	flush_nat_ptrs(chain, i);
1018 	IPFW_WUNLOCK(chain);
1019 	IPFW_UH_WUNLOCK(chain);
1020 	free_nat_instance(ptr);
1021 	return (0);
1022 }
1023 
1024 static int
1025 ipfw_nat_get_cfg(struct sockopt *sopt)
1026 {
1027 	struct ip_fw_chain *chain = &V_layer3_chain;
1028 	struct cfg_nat *n;
1029 	struct cfg_nat_legacy *ucfg;
1030 	struct cfg_redir *r;
1031 	struct cfg_spool *s;
1032 	struct cfg_redir_legacy *ser_r;
1033 	struct cfg_spool_legacy *ser_s;
1034 	char *data;
1035 	int gencnt, nat_cnt, len, error;
1036 
1037 	nat_cnt = 0;
1038 	len = sizeof(nat_cnt);
1039 
1040 	IPFW_UH_RLOCK(chain);
1041 retry:
1042 	gencnt = chain->gencnt;
1043 	/* Estimate memory amount */
1044 	LIST_FOREACH(n, &chain->nat, _next) {
1045 		nat_cnt++;
1046 		len += sizeof(struct cfg_nat_legacy);
1047 		LIST_FOREACH(r, &n->redir_chain, _next) {
1048 			len += sizeof(struct cfg_redir_legacy);
1049 			LIST_FOREACH(s, &r->spool_chain, _next)
1050 				len += sizeof(struct cfg_spool_legacy);
1051 		}
1052 	}
1053 	IPFW_UH_RUNLOCK(chain);
1054 
1055 	data = malloc(len, M_TEMP, M_WAITOK | M_ZERO);
1056 	bcopy(&nat_cnt, data, sizeof(nat_cnt));
1057 
1058 	nat_cnt = 0;
1059 	len = sizeof(nat_cnt);
1060 
1061 	IPFW_UH_RLOCK(chain);
1062 	if (gencnt != chain->gencnt) {
1063 		free(data, M_TEMP);
1064 		goto retry;
1065 	}
1066 	/* Serialize all the data. */
1067 	LIST_FOREACH(n, &chain->nat, _next) {
1068 		ucfg = (struct cfg_nat_legacy *)&data[len];
1069 		ucfg->id = n->id;
1070 		ucfg->ip = n->ip;
1071 		ucfg->redir_cnt = n->redir_cnt;
1072 		ucfg->mode = n->mode;
1073 		strlcpy(ucfg->if_name, n->if_name, sizeof(ucfg->if_name));
1074 		len += sizeof(struct cfg_nat_legacy);
1075 		LIST_FOREACH(r, &n->redir_chain, _next) {
1076 			ser_r = (struct cfg_redir_legacy *)&data[len];
1077 			ser_r->mode = r->mode;
1078 			ser_r->laddr = r->laddr;
1079 			ser_r->paddr = r->paddr;
1080 			ser_r->raddr = r->raddr;
1081 			ser_r->lport = r->lport;
1082 			ser_r->pport = r->pport;
1083 			ser_r->rport = r->rport;
1084 			ser_r->pport_cnt = r->pport_cnt;
1085 			ser_r->rport_cnt = r->rport_cnt;
1086 			ser_r->proto = r->proto;
1087 			ser_r->spool_cnt = r->spool_cnt;
1088 			len += sizeof(struct cfg_redir_legacy);
1089 			LIST_FOREACH(s, &r->spool_chain, _next) {
1090 				ser_s = (struct cfg_spool_legacy *)&data[len];
1091 				ser_s->addr = s->addr;
1092 				ser_s->port = s->port;
1093 				len += sizeof(struct cfg_spool_legacy);
1094 			}
1095 		}
1096 	}
1097 	IPFW_UH_RUNLOCK(chain);
1098 
1099 	error = sooptcopyout(sopt, data, len);
1100 	free(data, M_TEMP);
1101 
1102 	return (error);
1103 }
1104 
1105 static int
1106 ipfw_nat_get_log(struct sockopt *sopt)
1107 {
1108 	uint8_t *data;
1109 	struct cfg_nat *ptr;
1110 	int i, size;
1111 	struct ip_fw_chain *chain;
1112 	IPFW_RLOCK_TRACKER;
1113 
1114 	chain = &V_layer3_chain;
1115 
1116 	IPFW_RLOCK(chain);
1117 	/* one pass to count, one to copy the data */
1118 	i = 0;
1119 	LIST_FOREACH(ptr, &chain->nat, _next) {
1120 		if (ptr->lib->logDesc == NULL)
1121 			continue;
1122 		i++;
1123 	}
1124 	size = i * (LIBALIAS_BUF_SIZE + sizeof(int));
1125 	data = malloc(size, M_IPFW, M_NOWAIT | M_ZERO);
1126 	if (data == NULL) {
1127 		IPFW_RUNLOCK(chain);
1128 		return (ENOSPC);
1129 	}
1130 	i = 0;
1131 	LIST_FOREACH(ptr, &chain->nat, _next) {
1132 		if (ptr->lib->logDesc == NULL)
1133 			continue;
1134 		bcopy(&ptr->id, &data[i], sizeof(int));
1135 		i += sizeof(int);
1136 		bcopy(ptr->lib->logDesc, &data[i], LIBALIAS_BUF_SIZE);
1137 		i += LIBALIAS_BUF_SIZE;
1138 	}
1139 	IPFW_RUNLOCK(chain);
1140 	sooptcopyout(sopt, data, size);
1141 	free(data, M_IPFW);
1142 	return(0);
1143 }
1144 
1145 static int
1146 vnet_ipfw_nat_init(const void *arg __unused)
1147 {
1148 
1149 	V_ipfw_nat_ready = 1;
1150 	return (0);
1151 }
1152 
1153 static int
1154 vnet_ipfw_nat_uninit(const void *arg __unused)
1155 {
1156 	struct cfg_nat *ptr, *ptr_temp;
1157 	struct ip_fw_chain *chain;
1158 
1159 	chain = &V_layer3_chain;
1160 	IPFW_WLOCK(chain);
1161 	V_ipfw_nat_ready = 0;
1162 	LIST_FOREACH_SAFE(ptr, &chain->nat, _next, ptr_temp) {
1163 		LIST_REMOVE(ptr, _next);
1164 		free_nat_instance(ptr);
1165 	}
1166 	flush_nat_ptrs(chain, -1 /* flush all */);
1167 	IPFW_WUNLOCK(chain);
1168 	return (0);
1169 }
1170 
1171 static void
1172 ipfw_nat_init(void)
1173 {
1174 
1175 	/* init ipfw hooks */
1176 	ipfw_nat_ptr = ipfw_nat;
1177 	lookup_nat_ptr = lookup_nat;
1178 	ipfw_nat_cfg_ptr = ipfw_nat_cfg;
1179 	ipfw_nat_del_ptr = ipfw_nat_del;
1180 	ipfw_nat_get_cfg_ptr = ipfw_nat_get_cfg;
1181 	ipfw_nat_get_log_ptr = ipfw_nat_get_log;
1182 	IPFW_ADD_SOPT_HANDLER(1, scodes);
1183 
1184 	ifaddr_event_tag = EVENTHANDLER_REGISTER(ifaddr_event, ifaddr_change,
1185 	    NULL, EVENTHANDLER_PRI_ANY);
1186 }
1187 
1188 static void
1189 ipfw_nat_destroy(void)
1190 {
1191 
1192 	EVENTHANDLER_DEREGISTER(ifaddr_event, ifaddr_event_tag);
1193 	/* deregister ipfw_nat */
1194 	IPFW_DEL_SOPT_HANDLER(1, scodes);
1195 	ipfw_nat_ptr = NULL;
1196 	lookup_nat_ptr = NULL;
1197 	ipfw_nat_cfg_ptr = NULL;
1198 	ipfw_nat_del_ptr = NULL;
1199 	ipfw_nat_get_cfg_ptr = NULL;
1200 	ipfw_nat_get_log_ptr = NULL;
1201 }
1202 
1203 static int
1204 ipfw_nat_modevent(module_t mod, int type, void *unused)
1205 {
1206 	int err = 0;
1207 
1208 	switch (type) {
1209 	case MOD_LOAD:
1210 		break;
1211 
1212 	case MOD_UNLOAD:
1213 		break;
1214 
1215 	default:
1216 		return EOPNOTSUPP;
1217 		break;
1218 	}
1219 	return err;
1220 }
1221 
1222 static moduledata_t ipfw_nat_mod = {
1223 	"ipfw_nat",
1224 	ipfw_nat_modevent,
1225 	0
1226 };
1227 
1228 /* Define startup order. */
1229 #define	IPFW_NAT_SI_SUB_FIREWALL	SI_SUB_PROTO_FIREWALL
1230 #define	IPFW_NAT_MODEVENT_ORDER		(SI_ORDER_ANY - 128) /* after ipfw */
1231 #define	IPFW_NAT_MODULE_ORDER		(IPFW_NAT_MODEVENT_ORDER + 1)
1232 #define	IPFW_NAT_VNET_ORDER		(IPFW_NAT_MODEVENT_ORDER + 2)
1233 
1234 DECLARE_MODULE(ipfw_nat, ipfw_nat_mod, IPFW_NAT_SI_SUB_FIREWALL, SI_ORDER_ANY);
1235 MODULE_DEPEND(ipfw_nat, libalias, 1, 1, 1);
1236 MODULE_DEPEND(ipfw_nat, ipfw, 3, 3, 3);
1237 MODULE_VERSION(ipfw_nat, 1);
1238 
1239 SYSINIT(ipfw_nat_init, IPFW_NAT_SI_SUB_FIREWALL, IPFW_NAT_MODULE_ORDER,
1240     ipfw_nat_init, NULL);
1241 VNET_SYSINIT(vnet_ipfw_nat_init, IPFW_NAT_SI_SUB_FIREWALL, IPFW_NAT_VNET_ORDER,
1242     vnet_ipfw_nat_init, NULL);
1243 
1244 SYSUNINIT(ipfw_nat_destroy, IPFW_NAT_SI_SUB_FIREWALL, IPFW_NAT_MODULE_ORDER,
1245     ipfw_nat_destroy, NULL);
1246 VNET_SYSUNINIT(vnet_ipfw_nat_uninit, IPFW_NAT_SI_SUB_FIREWALL,
1247     IPFW_NAT_VNET_ORDER, vnet_ipfw_nat_uninit, NULL);
1248 
1249 /* end of file */
1250