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