ipfw3_nat: new sysctl node 'cleanup_interval'
[dragonfly.git] / sys / net / ipfw3_nat / ip_fw3_nat.c
blobf9bb93d076e4497483f3de799cf934ec0deb9a11
1 /*
2 * Copyright (c) 2014 - 2016 The DragonFly Project. All rights reserved.
4 * This code is derived from software contributed to The DragonFly Project
5 * by Bill Yuan <bycn82@dragonflybsd.org>
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in
15 * the documentation and/or other materials provided with the
16 * distribution.
17 * 3. Neither the name of The DragonFly Project nor the names of its
18 * contributors may be used to endorse or promote products derived
19 * from this software without specific, prior written permission.
21 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
25 * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26 * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
27 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
29 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
30 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
31 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32 * SUCH DAMAGE.
35 #include <sys/param.h>
36 #include <sys/kernel.h>
37 #include <sys/malloc.h>
38 #include <sys/mbuf.h>
39 #include <sys/socketvar.h>
40 #include <sys/sysctl.h>
41 #include <sys/systimer.h>
42 #include <sys/thread2.h>
43 #include <sys/in_cksum.h>
44 #include <sys/systm.h>
45 #include <sys/proc.h>
46 #include <sys/socket.h>
47 #include <sys/syslog.h>
48 #include <sys/ucred.h>
49 #include <sys/lock.h>
50 #include <sys/mplock2.h>
52 #include <net/ethernet.h>
53 #include <net/netmsg2.h>
54 #include <net/netisr2.h>
55 #include <net/route.h>
56 #include <net/if.h>
58 #include <netinet/in.h>
59 #include <netinet/ip.h>
60 #include <netinet/ip_icmp.h>
61 #include <netinet/tcp.h>
62 #include <netinet/tcp_timer.h>
63 #include <netinet/tcp_var.h>
64 #include <netinet/tcpip.h>
65 #include <netinet/udp.h>
66 #include <netinet/udp_var.h>
67 #include <netinet/in_systm.h>
68 #include <netinet/in_var.h>
69 #include <netinet/in_pcb.h>
70 #include <netinet/ip_var.h>
71 #include <netinet/ip_divert.h>
73 #include <net/libalias/alias.h>
74 #include <net/libalias/alias_local.h>
76 #include <net/ipfw3/ip_fw.h>
78 #include "ip_fw3_nat.h"
81 struct ipfw_nat_context *ipfw_nat_ctx[MAXCPU];
82 extern struct ipfw_context *ipfw_ctx[MAXCPU];
83 extern ip_fw_ctl_t *ipfw_ctl_nat_ptr;
85 static int fw3_nat_cleanup_interval = 5;
87 SYSCTL_NODE(_net_inet_ip, OID_AUTO, fw3_nat, CTLFLAG_RW, 0, "ipfw3 NAT");
88 SYSCTL_INT(_net_inet_ip_fw3_nat, OID_AUTO, cleanup_interval, CTLFLAG_RW,
89 &fw3_nat_cleanup_interval, 0, "default life time");
91 void
92 check_nat(int *cmd_ctl, int *cmd_val, struct ip_fw_args **args,
93 struct ip_fw **f, ipfw_insn *cmd, uint16_t ip_len)
95 if ((*args)->eh != NULL) {
96 *cmd_ctl = IP_FW_CTL_NO;
97 *cmd_val = IP_FW_NOT_MATCH;
98 return;
101 struct ipfw_nat_context *nat_ctx;
102 struct cfg_nat *t;
103 int nat_id;
105 nat_ctx = ipfw_nat_ctx[mycpuid];
106 (*args)->rule = *f;
107 t = ((ipfw_insn_nat *)cmd)->nat;
108 if (t == NULL) {
109 nat_id = cmd->arg1;
110 LOOKUP_NAT((*nat_ctx), nat_id, t);
111 if (t == NULL) {
112 *cmd_val = IP_FW_DENY;
113 *cmd_ctl = IP_FW_CTL_DONE;
114 return;
116 ((ipfw_insn_nat *)cmd)->nat = t;
118 *cmd_val = ipfw_nat(*args, t, (*args)->m);
119 *cmd_ctl = IP_FW_CTL_NAT;
122 /* Local prototypes */
123 u_int StartPointIn(struct in_addr, u_short, int);
125 u_int StartPointOut(struct in_addr, struct in_addr,
126 u_short, u_short, int);
128 u_int
129 StartPointIn(struct in_addr alias_addr,
130 u_short alias_port,
131 int link_type)
133 u_int n;
135 n = alias_addr.s_addr;
136 if (link_type != LINK_PPTP)
137 n += alias_port;
138 n += link_type;
139 return (n % LINK_TABLE_IN_SIZE);
143 u_int
144 StartPointOut(struct in_addr src_addr, struct in_addr dst_addr,
145 u_short src_port, u_short dst_port, int link_type)
147 u_int n;
149 n = src_addr.s_addr;
150 n += dst_addr.s_addr;
151 if (link_type != LINK_PPTP) {
152 n += src_port;
153 n += dst_port;
155 n += link_type;
157 return (n % LINK_TABLE_OUT_SIZE);
161 void
162 add_alias_link_dispatch(netmsg_t alias_link_add)
164 struct ipfw_nat_context *nat_ctx;
165 struct netmsg_alias_link_add *msg;
166 struct libalias *la;
167 struct alias_link *lnk;
168 struct cfg_nat *t;
169 struct tcp_dat *aux_tcp;
170 u_int start_point;
172 msg = (struct netmsg_alias_link_add *)alias_link_add;
173 nat_ctx = ipfw_nat_ctx[mycpuid];
174 LOOKUP_NAT((*nat_ctx), msg->id, t);
175 la = t->lib;
176 lnk = kmalloc(sizeof(struct alias_link), M_ALIAS, M_WAITOK | M_ZERO);
177 memcpy(lnk, msg->lnk, sizeof(struct alias_link));
178 lnk->la = la;
179 if (msg->is_tcp) {
180 aux_tcp = kmalloc(sizeof(struct tcp_dat),
181 M_ALIAS, M_WAITOK | M_ZERO);
182 memcpy(aux_tcp, msg->lnk->data.tcp, sizeof(struct tcp_dat));
183 lnk->data.tcp = aux_tcp;
186 /* Set up pointers for output lookup table */
187 start_point = StartPointOut(lnk->src_addr, lnk->dst_addr,
188 lnk->src_port, lnk->dst_port, lnk->link_type);
189 LIST_INSERT_HEAD(&la->linkTableOut[start_point], lnk, list_out);
191 /* Set up pointers for input lookup table */
192 start_point = StartPointIn(lnk->alias_addr,
193 lnk->alias_port, lnk->link_type);
194 LIST_INSERT_HEAD(&la->linkTableIn[start_point], lnk, list_in);
195 kfree(alias_link_add, M_LWKTMSG);
199 ipfw_nat(struct ip_fw_args *args, struct cfg_nat *t, struct mbuf *m)
201 struct alias_link *new = NULL;
202 struct mbuf *mcl;
203 struct ip *ip;
204 int ldt, retval, nextcpu;
205 char *c;
207 ldt = 0;
208 retval = 0;
209 if ((mcl = m_megapullup(m, m->m_pkthdr.len)) ==NULL)
210 goto badnat;
212 ip = mtod(mcl, struct ip *);
213 if (args->eh == NULL) {
214 ip->ip_len = htons(ip->ip_len);
215 ip->ip_off = htons(ip->ip_off);
218 if (mcl->m_pkthdr.rcvif == NULL &&
219 mcl->m_pkthdr.csum_flags & CSUM_DELAY_DATA) {
220 ldt = 1;
223 c = mtod(mcl, char *);
224 if (args->oif == NULL) {
225 retval = LibAliasIn(t->lib, c,
226 mcl->m_len + M_TRAILINGSPACE(mcl), &new);
227 } else {
228 retval = LibAliasOut(t->lib, c,
229 mcl->m_len + M_TRAILINGSPACE(mcl), &new);
231 if (retval != PKT_ALIAS_OK &&
232 retval != PKT_ALIAS_FOUND_HEADER_FRAGMENT) {
233 /* XXX - should i add some logging? */
234 m_free(mcl);
235 badnat:
236 args->m = NULL;
237 return IP_FW_DENY;
239 mcl->m_pkthdr.len = mcl->m_len = ntohs(ip->ip_len);
241 if ((ip->ip_off & htons(IP_OFFMASK)) == 0 &&
242 ip->ip_p == IPPROTO_TCP) {
243 struct tcphdr *th;
245 th = (struct tcphdr *)(ip + 1);
246 if (th->th_x2){
247 ldt = 1;
250 if (new != NULL &&
251 (ip->ip_p == IPPROTO_TCP || ip->ip_p == IPPROTO_UDP)) {
252 ip_hashfn(&mcl, 0);
253 nextcpu = netisr_hashcpu(m->m_pkthdr.hash);
254 if (nextcpu != mycpuid) {
255 struct netmsg_alias_link_add *msg;
256 msg = kmalloc(sizeof(struct netmsg_alias_link_add),
257 M_LWKTMSG, M_NOWAIT | M_ZERO);
259 netmsg_init(&msg->base, NULL,
260 &curthread->td_msgport, 0,
261 add_alias_link_dispatch);
262 msg->lnk = new;
263 msg->id = t->id;
264 if (ip->ip_p == IPPROTO_TCP) {
265 msg->is_tcp = 1;
267 if (args->oif == NULL) {
268 msg->is_outgoing = 0;
269 } else {
270 msg->is_outgoing = 1;
272 netisr_sendmsg(&msg->base, nextcpu);
275 if (ldt) {
276 struct tcphdr *th;
277 struct udphdr *uh;
278 u_short cksum;
280 ip->ip_len = ntohs(ip->ip_len);
281 cksum = in_pseudo(
282 ip->ip_src.s_addr,
283 ip->ip_dst.s_addr,
284 htons(ip->ip_p + ip->ip_len - (ip->ip_hl << 2))
287 switch (ip->ip_p) {
288 case IPPROTO_TCP:
289 th = (struct tcphdr *)(ip + 1);
290 th->th_x2 = 0;
291 th->th_sum = cksum;
292 mcl->m_pkthdr.csum_data =
293 offsetof(struct tcphdr, th_sum);
294 break;
295 case IPPROTO_UDP:
296 uh = (struct udphdr *)(ip + 1);
297 uh->uh_sum = cksum;
298 mcl->m_pkthdr.csum_data =
299 offsetof(struct udphdr, uh_sum);
300 break;
303 * No hw checksum offloading: do it
304 * by ourself.
306 if ((mcl->m_pkthdr.csum_flags & CSUM_DELAY_DATA) == 0) {
307 in_delayed_cksum(mcl);
308 mcl->m_pkthdr.csum_flags &= ~CSUM_DELAY_DATA;
310 ip->ip_len = htons(ip->ip_len);
313 if (args->eh == NULL) {
314 ip->ip_len = ntohs(ip->ip_len);
315 ip->ip_off = ntohs(ip->ip_off);
318 args->m = mcl;
319 return IP_FW_NAT;
322 void
323 del_redir_spool_cfg(struct cfg_nat *n, struct redir_chain *head)
325 struct cfg_redir *r, *tmp_r;
326 struct cfg_spool *s, *tmp_s;
327 int i, num;
329 LIST_FOREACH_MUTABLE(r, head, _next, tmp_r) {
330 num = 1; /* Number of alias_link to delete. */
331 switch (r->mode) {
332 case REDIR_PORT:
333 num = r->pport_cnt;
334 /* FALLTHROUGH */
335 case REDIR_ADDR:
336 case REDIR_PROTO:
337 /* Delete all libalias redirect entry. */
338 for (i = 0; i < num; i++)
339 LibAliasRedirectDelete(n->lib,
340 r->alink[i]);
342 /* Del spool cfg if any. */
343 LIST_FOREACH_MUTABLE(s, &r->spool_chain,
344 _next, tmp_s) {
345 LIST_REMOVE(s, _next);
346 kfree(s, M_IPFW_NAT);
348 kfree(r->alink, M_IPFW_NAT);
349 LIST_REMOVE(r, _next);
350 kfree(r, M_IPFW_NAT);
351 break;
352 default:
353 kprintf("unknown redirect mode: %u\n", r->mode);
354 /* XXX - panic?!?!? */
355 break;
361 add_redir_spool_cfg(char *buf, struct cfg_nat *ptr)
363 struct cfg_redir *r, *ser_r;
364 struct cfg_spool *s, *ser_s;
365 int cnt, off, i;
366 char *panic_err;
368 for (cnt = 0, off = 0; cnt < ptr->redir_cnt; cnt++) {
369 ser_r = (struct cfg_redir *)&buf[off];
370 r = kmalloc(SOF_REDIR, M_IPFW_NAT, M_WAITOK | M_ZERO);
371 memcpy(r, ser_r, SOF_REDIR);
372 LIST_INIT(&r->spool_chain);
373 off += SOF_REDIR;
374 r->alink = kmalloc(sizeof(struct alias_link *) * r->pport_cnt,
375 M_IPFW_NAT, M_WAITOK | M_ZERO);
376 switch (r->mode) {
377 case REDIR_ADDR:
378 r->alink[0] = LibAliasRedirectAddr(ptr->lib,
379 r->laddr, r->paddr);
380 break;
381 case REDIR_PORT:
382 for (i = 0 ; i < r->pport_cnt; i++) {
384 * If remotePort is all ports
385 * set it to 0.
387 u_short remotePortCopy = r->rport + i;
388 if (r->rport_cnt == 1 && r->rport == 0)
389 remotePortCopy = 0;
390 r->alink[i] =
392 LibAliasRedirectPort(ptr->lib,
393 r->laddr,htons(r->lport + i),
394 r->raddr,htons(remotePortCopy),
395 r->paddr,htons(r->pport + i),
396 r->proto);
398 if (r->alink[i] == NULL) {
399 r->alink[0] = NULL;
400 break;
403 break;
404 case REDIR_PROTO:
405 r->alink[0] = LibAliasRedirectProto(ptr->lib,
406 r->laddr, r->raddr, r->paddr, r->proto);
407 break;
408 default:
409 kprintf("unknown redirect mode: %u\n", r->mode);
410 break;
412 if (r->alink[0] == NULL) {
413 panic_err = "LibAliasRedirect* returned NULL";
414 goto bad;
415 } else /* LSNAT handling. */
416 for (i = 0; i < r->spool_cnt; i++) {
417 ser_s = (struct cfg_spool *)&buf[off];
418 s = kmalloc(SOF_REDIR, M_IPFW_NAT,
419 M_WAITOK | M_ZERO);
420 memcpy(s, ser_s, SOF_SPOOL);
421 LibAliasAddServer(ptr->lib, r->alink[0],
422 s->addr, htons(s->port));
423 off += SOF_SPOOL;
424 /* Hook spool entry. */
425 HOOK_SPOOL(&r->spool_chain, s);
427 /* And finally hook this redir entry. */
428 HOOK_REDIR(&ptr->redir_chain, r);
430 return 1;
431 bad:
432 /* something really bad happened: panic! */
433 panic("%s\n", panic_err);
437 ipfw_ctl_nat_get_cfg(struct sockopt *sopt)
439 struct ipfw_nat_context *nat_ctx;
440 struct cfg_nat *n;
441 struct cfg_redir *r;
442 struct cfg_spool *s;
443 int nat_cnt, off, nat_cfg_size;
444 size_t size;
445 uint8_t *data;
447 nat_cnt = 0;
448 nat_cfg_size = 0;
449 off = sizeof(nat_cnt);
451 nat_ctx = ipfw_nat_ctx[mycpuid];
452 size = sopt->sopt_valsize;
454 data = sopt->sopt_val;
455 /* count the size of nat cfg */
456 LIST_FOREACH(n, &((*nat_ctx).nat), _next) {
457 nat_cfg_size += SOF_NAT;
460 LIST_FOREACH(n, &((*nat_ctx).nat), _next) {
461 nat_cnt++;
462 if (off + SOF_NAT < size) {
463 bcopy(n, &data[off], SOF_NAT);
464 off += SOF_NAT;
465 LIST_FOREACH(r, &n->redir_chain, _next) {
466 if (off + SOF_REDIR < size) {
467 bcopy(r, &data[off], SOF_REDIR);
468 off += SOF_REDIR;
469 LIST_FOREACH(s, &r->spool_chain,
470 _next) {
471 if (off + SOF_SPOOL < size) {
472 bcopy(s, &data[off],
473 SOF_SPOOL);
474 off += SOF_SPOOL;
475 } else
476 goto nospace;
478 } else
479 goto nospace;
481 } else
482 goto nospace;
484 bcopy(&nat_cnt, data, sizeof(nat_cnt));
485 sopt->sopt_valsize = nat_cfg_size;
486 return 0;
487 nospace:
488 bzero(sopt->sopt_val, sopt->sopt_valsize);
489 sopt->sopt_valsize = nat_cfg_size;
490 return 0;
494 ipfw_ctl_nat_get_record(struct sockopt *sopt)
496 struct ipfw_nat_context *nat_ctx;
497 struct cfg_nat *t;
498 struct alias_link *lnk;
499 struct libalias *la;
500 size_t sopt_size, all_lnk_size = 0;
501 int i, *nat_id, id, n;
502 struct ipfw_ioc_nat_state *nat_state;
504 int cpu;
506 nat_id = (int *)(sopt->sopt_val);
507 n = *nat_id;
508 sopt_size = sopt->sopt_valsize;
509 nat_state = (struct ipfw_ioc_nat_state *)sopt->sopt_val;
510 for (cpu = 0; cpu < ncpus; cpu++) {
511 nat_ctx = ipfw_nat_ctx[cpu];
512 id = n;
513 LOOKUP_NAT((*nat_ctx), id, t);
514 if (t != NULL) {
515 la = t->lib;
516 LIBALIAS_LOCK_ASSERT(la);
517 for (i = 0; i < LINK_TABLE_OUT_SIZE; i++) {
518 LIST_FOREACH(lnk, &la->linkTableOut[i],
519 list_out) {
520 all_lnk_size += sizeof(*nat_state);
521 if (all_lnk_size > sopt_size)
522 goto nospace;
523 nat_state->src_addr = lnk->src_addr;
524 nat_state->dst_addr = lnk->dst_addr;
525 nat_state->alias_addr = lnk->alias_addr;
526 nat_state->src_port = lnk->src_port;
527 nat_state->dst_port = lnk->dst_port;
528 nat_state->alias_port = lnk->alias_port;
529 nat_state->link_type = lnk->link_type;
530 nat_state->timestamp = lnk->timestamp;
531 nat_state->cpuid = cpu;
532 nat_state->is_outgoing = 1;
533 nat_state++;
535 LIST_FOREACH(lnk, &la->linkTableIn[i],
536 list_out) {
537 all_lnk_size += sizeof(*nat_state);
538 if (all_lnk_size > sopt_size)
539 goto nospace;
540 nat_state->src_addr = lnk->src_addr;
541 nat_state->dst_addr = lnk->dst_addr;
542 nat_state->alias_addr = lnk->alias_addr;
543 nat_state->src_port = lnk->src_port;
544 nat_state->dst_port = lnk->dst_port;
545 nat_state->alias_port = lnk->alias_port;
546 nat_state->link_type = lnk->link_type;
547 nat_state->timestamp = lnk->timestamp;
548 nat_state->cpuid = cpu;
549 nat_state->is_outgoing = 0;
550 nat_state++;
555 sopt->sopt_valsize = all_lnk_size;
556 return 0;
557 nospace:
558 return 0;
561 void
562 nat_add_dispatch(netmsg_t nat_add_msg)
564 struct ipfw_nat_context *nat_ctx;
565 struct cfg_nat *ptr, *ser_n;
566 struct netmsg_nat_add *msg;
568 msg = (struct netmsg_nat_add *)nat_add_msg;
570 ser_n = (struct cfg_nat *)(msg->buf);
572 /* New rule: allocate and init new instance. */
573 ptr = kmalloc(sizeof(struct cfg_nat), M_IPFW_NAT, M_WAITOK | M_ZERO);
575 ptr->lib = LibAliasInit(NULL);
576 if (ptr->lib == NULL) {
577 kfree(ptr, M_IPFW_NAT);
578 kfree(msg->buf, M_IPFW_NAT);
581 LIST_INIT(&ptr->redir_chain);
583 * Basic nat configuration.
585 ptr->id = ser_n->id;
587 * XXX - what if this rule doesn't nat any ip and just
588 * redirect?
589 * do we set aliasaddress to 0.0.0.0?
591 ptr->ip = ser_n->ip;
592 ptr->redir_cnt = ser_n->redir_cnt;
593 ptr->mode = ser_n->mode;
595 LibAliasSetMode(ptr->lib, ser_n->mode, ser_n->mode);
596 LibAliasSetAddress(ptr->lib, ptr->ip);
597 memcpy(ptr->if_name, ser_n->if_name, IF_NAMESIZE);
599 /* Add new entries. */
600 add_redir_spool_cfg(&msg->buf[(sizeof(struct cfg_nat))], ptr);
602 nat_ctx = ipfw_nat_ctx[mycpuid];
603 HOOK_NAT(&(nat_ctx->nat), ptr);
604 netisr_forwardmsg(&msg->base, mycpuid + 1);
608 ipfw_ctl_nat_add(struct sockopt *sopt)
610 struct ipfw_nat_context *nat_ctx;
611 struct cfg_nat *ptr, *ser_n;
612 ser_n = (struct cfg_nat *)(sopt->sopt_val);
614 nat_ctx = ipfw_nat_ctx[mycpuid];
616 * Find/create nat rule.
618 LOOKUP_NAT((*nat_ctx), ser_n->id, ptr);
620 if (ptr == NULL) {
621 struct netmsg_nat_add nat_add_msg;
622 struct netmsg_nat_add *msg;
624 msg = &nat_add_msg;
625 msg->buf = kmalloc(sopt->sopt_valsize,
626 M_IPFW_NAT, M_WAITOK | M_ZERO);
628 sooptcopyin(sopt, msg->buf, sopt->sopt_valsize,
629 sizeof(struct cfg_nat));
631 netmsg_init(&msg->base, NULL, &curthread->td_msgport,
632 0, nat_add_dispatch);
635 netisr_domsg(&msg->base, 0);
636 kfree(msg->buf, M_IPFW_NAT);
637 } else {
638 goto done;
640 done:
641 return 0;
644 void
645 nat_del_dispatch(netmsg_t nat_del_msg)
647 struct ipfw_nat_context *nat_ctx;
648 struct ipfw_context *ctx;
649 struct cfg_nat *n, *tmp;
650 struct netmsg_nat_del *msg;
651 struct ip_fw *f;
652 ipfw_insn *cmd;
653 int id;
655 msg = (struct netmsg_nat_del *)nat_del_msg;
656 id = msg->id;
658 nat_ctx = ipfw_nat_ctx[mycpuid];
659 LOOKUP_NAT((*nat_ctx), id, n);
660 if (n == NULL) {
664 * stop deleting when this cfg_nat was in use in ipfw_ctx
666 ctx = ipfw_ctx[mycpuid];
667 for (f = ctx->ipfw_rule_chain; f; f = f->next) {
668 cmd = ACTION_PTR(f);
669 if ((int)cmd->module == MODULE_NAT_ID &&
670 (int)cmd->opcode == O_NAT_NAT) {
671 tmp = ((ipfw_insn_nat *)cmd)->nat;
672 if (tmp != NULL && tmp->id == n->id) {
677 UNHOOK_NAT(n);
678 del_redir_spool_cfg(n, &n->redir_chain);
679 LibAliasUninit(n->lib);
680 kfree(n, M_IPFW_NAT);
684 ipfw_ctl_nat_del(struct sockopt *sopt)
686 struct netmsg_nat_del nat_del_msg;
687 struct netmsg_nat_del *msg;
688 int *id;
690 msg = &nat_del_msg;
691 id = sopt->sopt_val;
692 msg->id = *id;
694 netmsg_init(&msg->base, NULL, &curthread->td_msgport,
695 0, nat_del_dispatch);
697 netisr_domsg(&msg->base, 0);
698 return 0;
702 ipfw_ctl_nat_flush(struct sockopt *sopt)
704 struct ipfw_nat_context *nat_ctx;
705 struct ipfw_context *ctx;
706 struct cfg_nat *ptr, *tmp;
707 struct ip_fw *f;
708 ipfw_insn *cmd;
709 int cpu;
712 * stop flushing when any cfg_nat was in use in ipfw_ctx
714 for (cpu = 0; cpu < ncpus; cpu++) {
715 ctx = ipfw_ctx[cpu];
716 for (f = ctx->ipfw_rule_chain; f; f = f->next) {
717 cmd = ACTION_PTR(f);
718 if ((int)cmd->module == MODULE_NAT_ID &&
719 (int)cmd->opcode == O_NAT_NAT) {
720 return EINVAL;
725 nat_ctx = ipfw_nat_ctx[mycpuid];
727 LIST_FOREACH_MUTABLE(ptr, &(nat_ctx->nat), _next, tmp) {
728 LIST_REMOVE(ptr, _next);
729 del_redir_spool_cfg(ptr, &ptr->redir_chain);
730 LibAliasUninit(ptr->lib);
731 kfree(ptr, M_IPFW_NAT);
733 return 0;
737 ipfw_ctl_nat_sockopt(struct sockopt *sopt)
739 int error = 0;
740 switch (sopt->sopt_name) {
741 case IP_FW_NAT_ADD:
742 error = ipfw_ctl_nat_add(sopt);
743 break;
744 case IP_FW_NAT_DEL:
745 error = ipfw_ctl_nat_del(sopt);
746 break;
747 case IP_FW_NAT_FLUSH:
748 error = ipfw_ctl_nat_flush(sopt);
749 break;
750 case IP_FW_NAT_GET:
751 error = ipfw_ctl_nat_get_cfg(sopt);
752 break;
753 case IP_FW_NAT_GET_RECORD:
754 error = ipfw_ctl_nat_get_record(sopt);
755 break;
756 default:
757 kprintf("ipfw3 nat invalid socket option %d\n",
758 sopt->sopt_name);
760 return error;
763 void
764 nat_init_ctx_dispatch(netmsg_t msg)
766 struct ipfw_nat_context *tmp;
767 tmp = kmalloc(sizeof(struct ipfw_nat_context),
768 M_IPFW_NAT, M_WAITOK | M_ZERO);
769 ipfw_nat_ctx[mycpuid] = tmp;
770 netisr_forwardmsg(&msg->base, mycpuid + 1);
773 static
774 int ipfw_nat_init(void)
776 struct netmsg_base msg;
777 register_ipfw_module(MODULE_NAT_ID, MODULE_NAT_NAME);
778 register_ipfw_filter_funcs(MODULE_NAT_ID, O_NAT_NAT,
779 (filter_func)check_nat);
780 ipfw_ctl_nat_ptr = ipfw_ctl_nat_sockopt;
781 netmsg_init(&msg, NULL, &curthread->td_msgport,
782 0, nat_init_ctx_dispatch);
783 netisr_domsg(&msg, 0);
784 return 0;
787 static int
788 ipfw_nat_fini(void)
790 struct cfg_nat *ptr, *tmp;
791 struct ipfw_nat_context *ctx;
792 int cpu;
794 for (cpu = 0; cpu < ncpus; cpu++) {
795 ctx = ipfw_nat_ctx[cpu];
796 if(ctx != NULL) {
797 LIST_FOREACH_MUTABLE(ptr, &(ctx->nat), _next, tmp) {
798 LIST_REMOVE(ptr, _next);
799 del_redir_spool_cfg(ptr, &ptr->redir_chain);
800 LibAliasUninit(ptr->lib);
801 kfree(ptr, M_IPFW_NAT);
804 kfree(ctx, M_IPFW_NAT);
805 ipfw_nat_ctx[cpu] = NULL;
808 ipfw_ctl_nat_ptr = NULL;
810 return unregister_ipfw_module(MODULE_NAT_ID);
813 static int
814 ipfw_nat_modevent(module_t mod, int type, void *data)
816 switch (type) {
817 case MOD_LOAD:
818 return ipfw_nat_init();
819 case MOD_UNLOAD:
820 return ipfw_nat_fini();
821 default:
822 break;
824 return 0;
827 moduledata_t ipfw_nat_mod = {
828 "ipfw3_nat",
829 ipfw_nat_modevent,
830 NULL
833 DECLARE_MODULE(ipfw3_nat, ipfw_nat_mod,
834 SI_SUB_PROTO_IFATTACHDOMAIN, SI_ORDER_ANY);
835 MODULE_DEPEND(ipfw3_nat, libalias, 1, 1, 1);
836 MODULE_DEPEND(ipfw3_nat, ipfw3_basic, 1, 1, 1);
837 MODULE_VERSION(ipfw3_nat, 1);