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