kernel - Fix callout_stop/callout_reset rearm race
[dragonfly.git] / sys / net / ipfw3_nat / ip_fw3_nat.c
blob50cba05aae4969a9c1dfa4cd64bfb23832eb8728
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 static struct callout ipfw3_nat_cleanup_callout;
83 extern struct ipfw_context *ipfw_ctx[MAXCPU];
84 extern ip_fw_ctl_t *ipfw_ctl_nat_ptr;
85 extern libalias_housekeeping_t *libalias_housekeeping_prt;
87 static int fw3_nat_cleanup_interval = 5;
89 SYSCTL_NODE(_net_inet_ip, OID_AUTO, fw3_nat, CTLFLAG_RW, 0, "ipfw3 NAT");
90 SYSCTL_INT(_net_inet_ip_fw3_nat, OID_AUTO, cleanup_interval, CTLFLAG_RW,
91 &fw3_nat_cleanup_interval, 0, "default life time");
93 void
94 check_nat(int *cmd_ctl, int *cmd_val, struct ip_fw_args **args,
95 struct ip_fw **f, ipfw_insn *cmd, uint16_t ip_len)
97 if ((*args)->eh != NULL) {
98 *cmd_ctl = IP_FW_CTL_NO;
99 *cmd_val = IP_FW_NOT_MATCH;
100 return;
103 struct ipfw_nat_context *nat_ctx;
104 struct cfg_nat *t;
105 int nat_id;
107 nat_ctx = ipfw_nat_ctx[mycpuid];
108 (*args)->rule = *f;
109 t = ((ipfw_insn_nat *)cmd)->nat;
110 if (t == NULL) {
111 nat_id = cmd->arg1;
112 LOOKUP_NAT((*nat_ctx), nat_id, t);
113 if (t == NULL) {
114 *cmd_val = IP_FW_DENY;
115 *cmd_ctl = IP_FW_CTL_DONE;
116 return;
118 ((ipfw_insn_nat *)cmd)->nat = t;
120 *cmd_val = ipfw_nat(*args, t, (*args)->m);
121 *cmd_ctl = IP_FW_CTL_NAT;
124 /* Local prototypes */
125 u_int StartPointIn(struct in_addr, u_short, int);
127 u_int StartPointOut(struct in_addr, struct in_addr,
128 u_short, u_short, int);
130 u_int
131 StartPointIn(struct in_addr alias_addr,
132 u_short alias_port,
133 int link_type)
135 u_int n;
137 n = alias_addr.s_addr;
138 if (link_type != LINK_PPTP)
139 n += alias_port;
140 n += link_type;
141 return (n % LINK_TABLE_IN_SIZE);
145 u_int
146 StartPointOut(struct in_addr src_addr, struct in_addr dst_addr,
147 u_short src_port, u_short dst_port, int link_type)
149 u_int n;
151 n = src_addr.s_addr;
152 n += dst_addr.s_addr;
153 if (link_type != LINK_PPTP) {
154 n += src_port;
155 n += dst_port;
157 n += link_type;
159 return (n % LINK_TABLE_OUT_SIZE);
163 void
164 add_alias_link_dispatch(netmsg_t alias_link_add)
166 struct ipfw_nat_context *nat_ctx;
167 struct netmsg_alias_link_add *msg;
168 struct libalias *la;
169 struct alias_link *lnk;
170 struct cfg_nat *t;
171 struct tcp_dat *aux_tcp;
172 u_int start_point;
174 msg = (struct netmsg_alias_link_add *)alias_link_add;
175 nat_ctx = ipfw_nat_ctx[mycpuid];
176 LOOKUP_NAT((*nat_ctx), msg->id, t);
177 la = t->lib;
178 lnk = kmalloc(sizeof(struct alias_link), M_ALIAS, M_WAITOK | M_ZERO);
179 memcpy(lnk, msg->lnk, sizeof(struct alias_link));
180 lnk->la = la;
181 if (msg->is_tcp) {
182 aux_tcp = kmalloc(sizeof(struct tcp_dat),
183 M_ALIAS, M_WAITOK | M_ZERO);
184 memcpy(aux_tcp, msg->lnk->data.tcp, sizeof(struct tcp_dat));
185 lnk->data.tcp = aux_tcp;
188 /* Set up pointers for output lookup table */
189 start_point = StartPointOut(lnk->src_addr, lnk->dst_addr,
190 lnk->src_port, lnk->dst_port, lnk->link_type);
191 LIST_INSERT_HEAD(&la->linkTableOut[start_point], lnk, list_out);
193 /* Set up pointers for input lookup table */
194 start_point = StartPointIn(lnk->alias_addr,
195 lnk->alias_port, lnk->link_type);
196 LIST_INSERT_HEAD(&la->linkTableIn[start_point], lnk, list_in);
197 kfree(alias_link_add, M_LWKTMSG);
201 ipfw_nat(struct ip_fw_args *args, struct cfg_nat *t, struct mbuf *m)
203 struct alias_link *new = NULL;
204 struct mbuf *mcl;
205 struct ip *ip;
206 int ldt, retval, nextcpu;
207 char *c;
209 ldt = 0;
210 retval = 0;
211 if ((mcl = m_megapullup(m, m->m_pkthdr.len)) ==NULL)
212 goto badnat;
214 ip = mtod(mcl, struct ip *);
215 if (args->eh == NULL) {
216 ip->ip_len = htons(ip->ip_len);
217 ip->ip_off = htons(ip->ip_off);
220 if (mcl->m_pkthdr.rcvif == NULL &&
221 mcl->m_pkthdr.csum_flags & CSUM_DELAY_DATA) {
222 ldt = 1;
225 c = mtod(mcl, char *);
226 if (args->oif == NULL) {
227 retval = LibAliasIn(t->lib, c,
228 mcl->m_len + M_TRAILINGSPACE(mcl), &new);
229 } else {
230 retval = LibAliasOut(t->lib, c,
231 mcl->m_len + M_TRAILINGSPACE(mcl), &new);
233 if (retval != PKT_ALIAS_OK &&
234 retval != PKT_ALIAS_FOUND_HEADER_FRAGMENT) {
235 /* XXX - should i add some logging? */
236 m_free(mcl);
237 badnat:
238 args->m = NULL;
239 return IP_FW_DENY;
241 mcl->m_pkthdr.len = mcl->m_len = ntohs(ip->ip_len);
243 if ((ip->ip_off & htons(IP_OFFMASK)) == 0 &&
244 ip->ip_p == IPPROTO_TCP) {
245 struct tcphdr *th;
247 th = (struct tcphdr *)(ip + 1);
248 if (th->th_x2){
249 ldt = 1;
252 if (new != NULL &&
253 (ip->ip_p == IPPROTO_TCP || ip->ip_p == IPPROTO_UDP)) {
254 ip_hashfn(&mcl, 0);
255 nextcpu = netisr_hashcpu(m->m_pkthdr.hash);
256 if (nextcpu != mycpuid) {
257 struct netmsg_alias_link_add *msg;
258 msg = kmalloc(sizeof(struct netmsg_alias_link_add),
259 M_LWKTMSG, M_NOWAIT | M_ZERO);
261 netmsg_init(&msg->base, NULL,
262 &curthread->td_msgport, 0,
263 add_alias_link_dispatch);
264 msg->lnk = new;
265 msg->id = t->id;
266 if (ip->ip_p == IPPROTO_TCP) {
267 msg->is_tcp = 1;
269 if (args->oif == NULL) {
270 msg->is_outgoing = 0;
271 } else {
272 msg->is_outgoing = 1;
274 netisr_sendmsg(&msg->base, nextcpu);
277 if (ldt) {
278 struct tcphdr *th;
279 struct udphdr *uh;
280 u_short cksum;
282 ip->ip_len = ntohs(ip->ip_len);
283 cksum = in_pseudo(
284 ip->ip_src.s_addr,
285 ip->ip_dst.s_addr,
286 htons(ip->ip_p + ip->ip_len - (ip->ip_hl << 2))
289 switch (ip->ip_p) {
290 case IPPROTO_TCP:
291 th = (struct tcphdr *)(ip + 1);
292 th->th_x2 = 0;
293 th->th_sum = cksum;
294 mcl->m_pkthdr.csum_data =
295 offsetof(struct tcphdr, th_sum);
296 break;
297 case IPPROTO_UDP:
298 uh = (struct udphdr *)(ip + 1);
299 uh->uh_sum = cksum;
300 mcl->m_pkthdr.csum_data =
301 offsetof(struct udphdr, uh_sum);
302 break;
305 * No hw checksum offloading: do it
306 * by ourself.
308 if ((mcl->m_pkthdr.csum_flags & CSUM_DELAY_DATA) == 0) {
309 in_delayed_cksum(mcl);
310 mcl->m_pkthdr.csum_flags &= ~CSUM_DELAY_DATA;
312 ip->ip_len = htons(ip->ip_len);
315 if (args->eh == NULL) {
316 ip->ip_len = ntohs(ip->ip_len);
317 ip->ip_off = ntohs(ip->ip_off);
320 args->m = mcl;
321 return IP_FW_NAT;
324 void
325 del_redir_spool_cfg(struct cfg_nat *n, struct redir_chain *head)
327 struct cfg_redir *r, *tmp_r;
328 struct cfg_spool *s, *tmp_s;
329 int i, num;
331 LIST_FOREACH_MUTABLE(r, head, _next, tmp_r) {
332 num = 1; /* Number of alias_link to delete. */
333 switch (r->mode) {
334 case REDIR_PORT:
335 num = r->pport_cnt;
336 /* FALLTHROUGH */
337 case REDIR_ADDR:
338 case REDIR_PROTO:
339 /* Delete all libalias redirect entry. */
340 for (i = 0; i < num; i++)
341 LibAliasRedirectDelete(n->lib,
342 r->alink[i]);
344 /* Del spool cfg if any. */
345 LIST_FOREACH_MUTABLE(s, &r->spool_chain,
346 _next, tmp_s) {
347 LIST_REMOVE(s, _next);
348 kfree(s, M_IPFW_NAT);
350 kfree(r->alink, M_IPFW_NAT);
351 LIST_REMOVE(r, _next);
352 kfree(r, M_IPFW_NAT);
353 break;
354 default:
355 kprintf("unknown redirect mode: %u\n", r->mode);
356 /* XXX - panic?!?!? */
357 break;
363 add_redir_spool_cfg(char *buf, struct cfg_nat *ptr)
365 struct cfg_redir *r, *ser_r;
366 struct cfg_spool *s, *ser_s;
367 int cnt, off, i;
368 char *panic_err;
370 for (cnt = 0, off = 0; cnt < ptr->redir_cnt; cnt++) {
371 ser_r = (struct cfg_redir *)&buf[off];
372 r = kmalloc(SOF_REDIR, M_IPFW_NAT, M_WAITOK | M_ZERO);
373 memcpy(r, ser_r, SOF_REDIR);
374 LIST_INIT(&r->spool_chain);
375 off += SOF_REDIR;
376 r->alink = kmalloc(sizeof(struct alias_link *) * r->pport_cnt,
377 M_IPFW_NAT, M_WAITOK | M_ZERO);
378 switch (r->mode) {
379 case REDIR_ADDR:
380 r->alink[0] = LibAliasRedirectAddr(ptr->lib,
381 r->laddr, r->paddr);
382 break;
383 case REDIR_PORT:
384 for (i = 0 ; i < r->pport_cnt; i++) {
386 * If remotePort is all ports
387 * set it to 0.
389 u_short remotePortCopy = r->rport + i;
390 if (r->rport_cnt == 1 && r->rport == 0)
391 remotePortCopy = 0;
392 r->alink[i] =
394 LibAliasRedirectPort(ptr->lib,
395 r->laddr,htons(r->lport + i),
396 r->raddr,htons(remotePortCopy),
397 r->paddr,htons(r->pport + i),
398 r->proto);
400 if (r->alink[i] == NULL) {
401 r->alink[0] = NULL;
402 break;
405 break;
406 case REDIR_PROTO:
407 r->alink[0] = LibAliasRedirectProto(ptr->lib,
408 r->laddr, r->raddr, r->paddr, r->proto);
409 break;
410 default:
411 kprintf("unknown redirect mode: %u\n", r->mode);
412 break;
414 if (r->alink[0] == NULL) {
415 panic_err = "LibAliasRedirect* returned NULL";
416 goto bad;
417 } else /* LSNAT handling. */
418 for (i = 0; i < r->spool_cnt; i++) {
419 ser_s = (struct cfg_spool *)&buf[off];
420 s = kmalloc(SOF_REDIR, M_IPFW_NAT,
421 M_WAITOK | M_ZERO);
422 memcpy(s, ser_s, SOF_SPOOL);
423 LibAliasAddServer(ptr->lib, r->alink[0],
424 s->addr, htons(s->port));
425 off += SOF_SPOOL;
426 /* Hook spool entry. */
427 HOOK_SPOOL(&r->spool_chain, s);
429 /* And finally hook this redir entry. */
430 HOOK_REDIR(&ptr->redir_chain, r);
432 return 1;
433 bad:
434 /* something really bad happened: panic! */
435 panic("%s\n", panic_err);
439 ipfw_ctl_nat_get_cfg(struct sockopt *sopt)
441 struct ipfw_nat_context *nat_ctx;
442 struct cfg_nat *n;
443 struct cfg_redir *r;
444 struct cfg_spool *s;
445 int nat_cnt, off, nat_cfg_size;
446 size_t size;
447 uint8_t *data;
449 nat_cnt = 0;
450 nat_cfg_size = 0;
451 off = sizeof(nat_cnt);
453 nat_ctx = ipfw_nat_ctx[mycpuid];
454 size = sopt->sopt_valsize;
456 data = sopt->sopt_val;
457 /* count the size of nat cfg */
458 LIST_FOREACH(n, &((*nat_ctx).nat), _next) {
459 nat_cfg_size += SOF_NAT;
462 LIST_FOREACH(n, &((*nat_ctx).nat), _next) {
463 nat_cnt++;
464 if (off + SOF_NAT < size) {
465 bcopy(n, &data[off], SOF_NAT);
466 off += SOF_NAT;
467 LIST_FOREACH(r, &n->redir_chain, _next) {
468 if (off + SOF_REDIR < size) {
469 bcopy(r, &data[off], SOF_REDIR);
470 off += SOF_REDIR;
471 LIST_FOREACH(s, &r->spool_chain,
472 _next) {
473 if (off + SOF_SPOOL < size) {
474 bcopy(s, &data[off],
475 SOF_SPOOL);
476 off += SOF_SPOOL;
477 } else
478 goto nospace;
480 } else
481 goto nospace;
483 } else
484 goto nospace;
486 bcopy(&nat_cnt, data, sizeof(nat_cnt));
487 sopt->sopt_valsize = nat_cfg_size;
488 return 0;
489 nospace:
490 bzero(sopt->sopt_val, sopt->sopt_valsize);
491 sopt->sopt_valsize = nat_cfg_size;
492 return 0;
496 ipfw_ctl_nat_get_record(struct sockopt *sopt)
498 struct ipfw_nat_context *nat_ctx;
499 struct cfg_nat *t;
500 struct alias_link *lnk;
501 struct libalias *la;
502 size_t sopt_size, all_lnk_size = 0;
503 int i, *nat_id, id, n;
504 struct ipfw_ioc_nat_state *nat_state;
506 int cpu;
508 nat_id = (int *)(sopt->sopt_val);
509 n = *nat_id;
510 sopt_size = sopt->sopt_valsize;
511 nat_state = (struct ipfw_ioc_nat_state *)sopt->sopt_val;
512 for (cpu = 0; cpu < ncpus; cpu++) {
513 nat_ctx = ipfw_nat_ctx[cpu];
514 id = n;
515 LOOKUP_NAT((*nat_ctx), id, t);
516 if (t != NULL) {
517 la = t->lib;
518 LIBALIAS_LOCK_ASSERT(la);
519 for (i = 0; i < LINK_TABLE_OUT_SIZE; i++) {
520 LIST_FOREACH(lnk, &la->linkTableOut[i],
521 list_out) {
522 all_lnk_size += sizeof(*nat_state);
523 if (all_lnk_size > sopt_size)
524 goto nospace;
525 nat_state->src_addr = lnk->src_addr;
526 nat_state->dst_addr = lnk->dst_addr;
527 nat_state->alias_addr = lnk->alias_addr;
528 nat_state->src_port = lnk->src_port;
529 nat_state->dst_port = lnk->dst_port;
530 nat_state->alias_port = lnk->alias_port;
531 nat_state->link_type = lnk->link_type;
532 nat_state->timestamp = lnk->timestamp;
533 nat_state->cpuid = cpu;
534 nat_state->is_outgoing = 1;
535 nat_state++;
537 LIST_FOREACH(lnk, &la->linkTableIn[i],
538 list_out) {
539 all_lnk_size += sizeof(*nat_state);
540 if (all_lnk_size > sopt_size)
541 goto nospace;
542 nat_state->src_addr = lnk->src_addr;
543 nat_state->dst_addr = lnk->dst_addr;
544 nat_state->alias_addr = lnk->alias_addr;
545 nat_state->src_port = lnk->src_port;
546 nat_state->dst_port = lnk->dst_port;
547 nat_state->alias_port = lnk->alias_port;
548 nat_state->link_type = lnk->link_type;
549 nat_state->timestamp = lnk->timestamp;
550 nat_state->cpuid = cpu;
551 nat_state->is_outgoing = 0;
552 nat_state++;
557 sopt->sopt_valsize = all_lnk_size;
558 return 0;
559 nospace:
560 return 0;
563 void
564 nat_add_dispatch(netmsg_t nat_add_msg)
566 struct ipfw_nat_context *nat_ctx;
567 struct cfg_nat *ptr, *ser_n;
568 struct netmsg_nat_add *msg;
570 msg = (struct netmsg_nat_add *)nat_add_msg;
572 ser_n = (struct cfg_nat *)(msg->buf);
574 /* New rule: allocate and init new instance. */
575 ptr = kmalloc(sizeof(struct cfg_nat), M_IPFW_NAT, M_WAITOK | M_ZERO);
577 ptr->lib = LibAliasInit(NULL);
578 if (ptr->lib == NULL) {
579 kfree(ptr, M_IPFW_NAT);
580 kfree(msg->buf, M_IPFW_NAT);
583 LIST_INIT(&ptr->redir_chain);
585 * Basic nat configuration.
587 ptr->id = ser_n->id;
589 * XXX - what if this rule doesn't nat any ip and just
590 * redirect?
591 * do we set aliasaddress to 0.0.0.0?
593 ptr->ip = ser_n->ip;
594 ptr->redir_cnt = ser_n->redir_cnt;
595 ptr->mode = ser_n->mode;
597 LibAliasSetMode(ptr->lib, ser_n->mode, ser_n->mode);
598 LibAliasSetAddress(ptr->lib, ptr->ip);
599 memcpy(ptr->if_name, ser_n->if_name, IF_NAMESIZE);
601 /* Add new entries. */
602 add_redir_spool_cfg(&msg->buf[(sizeof(struct cfg_nat))], ptr);
604 nat_ctx = ipfw_nat_ctx[mycpuid];
605 HOOK_NAT(&(nat_ctx->nat), ptr);
606 netisr_forwardmsg_all(&msg->base, mycpuid + 1);
610 ipfw_ctl_nat_add(struct sockopt *sopt)
612 struct ipfw_nat_context *nat_ctx;
613 struct cfg_nat *ptr, *ser_n;
614 ser_n = (struct cfg_nat *)(sopt->sopt_val);
616 nat_ctx = ipfw_nat_ctx[mycpuid];
618 * Find/create nat rule.
620 LOOKUP_NAT((*nat_ctx), ser_n->id, ptr);
622 if (ptr == NULL) {
623 struct netmsg_nat_add nat_add_msg;
624 struct netmsg_nat_add *msg;
626 msg = &nat_add_msg;
627 msg->buf = kmalloc(sopt->sopt_valsize,
628 M_IPFW_NAT, M_WAITOK | M_ZERO);
630 sooptcopyin(sopt, msg->buf, sopt->sopt_valsize,
631 sizeof(struct cfg_nat));
633 netmsg_init(&msg->base, NULL, &curthread->td_msgport,
634 0, nat_add_dispatch);
637 netisr_domsg(&msg->base, 0);
638 kfree(msg->buf, M_IPFW_NAT);
639 } else {
640 goto done;
642 done:
643 return 0;
646 void
647 nat_del_dispatch(netmsg_t nat_del_msg)
649 struct ipfw_nat_context *nat_ctx;
650 struct ipfw_context *ctx;
651 struct cfg_nat *n, *tmp;
652 struct netmsg_nat_del *msg;
653 struct ip_fw *f;
654 ipfw_insn *cmd;
655 int id;
657 msg = (struct netmsg_nat_del *)nat_del_msg;
658 id = msg->id;
660 nat_ctx = ipfw_nat_ctx[mycpuid];
661 LOOKUP_NAT((*nat_ctx), id, n);
662 if (n == NULL) {
666 * stop deleting when this cfg_nat was in use in ipfw_ctx
668 ctx = ipfw_ctx[mycpuid];
669 for (f = ctx->ipfw_rule_chain; f; f = f->next) {
670 cmd = ACTION_PTR(f);
671 if ((int)cmd->module == MODULE_NAT_ID &&
672 (int)cmd->opcode == O_NAT_NAT) {
673 tmp = ((ipfw_insn_nat *)cmd)->nat;
674 if (tmp != NULL && tmp->id == n->id) {
679 UNHOOK_NAT(n);
680 del_redir_spool_cfg(n, &n->redir_chain);
681 LibAliasUninit(n->lib);
682 kfree(n, M_IPFW_NAT);
686 ipfw_ctl_nat_del(struct sockopt *sopt)
688 struct netmsg_nat_del nat_del_msg;
689 struct netmsg_nat_del *msg;
690 int *id;
692 msg = &nat_del_msg;
693 id = sopt->sopt_val;
694 msg->id = *id;
696 netmsg_init(&msg->base, NULL, &curthread->td_msgport,
697 0, nat_del_dispatch);
699 netisr_domsg(&msg->base, 0);
700 return 0;
704 ipfw_ctl_nat_flush(struct sockopt *sopt)
706 struct ipfw_nat_context *nat_ctx;
707 struct ipfw_context *ctx;
708 struct cfg_nat *ptr, *tmp;
709 struct ip_fw *f;
710 ipfw_insn *cmd;
711 int cpu;
714 * stop flushing when any cfg_nat was in use in ipfw_ctx
716 for (cpu = 0; cpu < ncpus; cpu++) {
717 ctx = ipfw_ctx[cpu];
718 for (f = ctx->ipfw_rule_chain; f; f = f->next) {
719 cmd = ACTION_PTR(f);
720 if ((int)cmd->module == MODULE_NAT_ID &&
721 (int)cmd->opcode == O_NAT_NAT) {
722 return EINVAL;
727 nat_ctx = ipfw_nat_ctx[mycpuid];
729 LIST_FOREACH_MUTABLE(ptr, &(nat_ctx->nat), _next, tmp) {
730 LIST_REMOVE(ptr, _next);
731 del_redir_spool_cfg(ptr, &ptr->redir_chain);
732 LibAliasUninit(ptr->lib);
733 kfree(ptr, M_IPFW_NAT);
735 return 0;
739 ipfw_ctl_nat_sockopt(struct sockopt *sopt)
741 int error = 0;
742 switch (sopt->sopt_name) {
743 case IP_FW_NAT_ADD:
744 error = ipfw_ctl_nat_add(sopt);
745 break;
746 case IP_FW_NAT_DEL:
747 error = ipfw_ctl_nat_del(sopt);
748 break;
749 case IP_FW_NAT_FLUSH:
750 error = ipfw_ctl_nat_flush(sopt);
751 break;
752 case IP_FW_NAT_GET:
753 error = ipfw_ctl_nat_get_cfg(sopt);
754 break;
755 case IP_FW_NAT_GET_RECORD:
756 error = ipfw_ctl_nat_get_record(sopt);
757 break;
758 default:
759 kprintf("ipfw3 nat invalid socket option %d\n",
760 sopt->sopt_name);
762 return error;
765 void
766 nat_init_ctx_dispatch(netmsg_t msg)
768 struct ipfw_nat_context *tmp;
769 tmp = kmalloc(sizeof(struct ipfw_nat_context),
770 M_IPFW_NAT, M_WAITOK | M_ZERO);
771 ipfw_nat_ctx[mycpuid] = tmp;
772 netisr_forwardmsg_all(&msg->base, mycpuid + 1);
775 static void
776 ipfw3_nat_cleanup_func_dispatch(netmsg_t nmsg)
778 struct ipfw_nat_context *nctx;
779 struct cfg_nat *ptr, *tmp;
781 nctx = ipfw_nat_ctx[mycpuid];
782 LIST_FOREACH_MUTABLE(ptr, &(nctx->nat), _next, tmp) {
783 if (libalias_housekeeping_prt != NULL) {
784 (*libalias_housekeeping_prt)(ptr->lib);
787 netisr_forwardmsg_all(&nmsg->base, mycpuid + 1);
790 static void
791 ipfw3_nat_cleanup_func(void *dummy __unused)
793 struct netmsg_base msg;
794 netmsg_init(&msg, NULL, &curthread->td_msgport, 0,
795 ipfw3_nat_cleanup_func_dispatch);
796 netisr_domsg(&msg, 0);
798 callout_reset(&ipfw3_nat_cleanup_callout,
799 fw3_nat_cleanup_interval * hz,
800 ipfw3_nat_cleanup_func,
801 NULL);
804 static
805 int ipfw_nat_init(void)
807 struct netmsg_base msg;
808 register_ipfw_module(MODULE_NAT_ID, MODULE_NAT_NAME);
809 register_ipfw_filter_funcs(MODULE_NAT_ID, O_NAT_NAT,
810 (filter_func)check_nat);
811 ipfw_ctl_nat_ptr = ipfw_ctl_nat_sockopt;
812 netmsg_init(&msg, NULL, &curthread->td_msgport,
813 0, nat_init_ctx_dispatch);
814 netisr_domsg(&msg, 0);
816 callout_init_mp(&ipfw3_nat_cleanup_callout);
817 callout_reset(&ipfw3_nat_cleanup_callout,
818 fw3_nat_cleanup_interval * hz,
819 ipfw3_nat_cleanup_func,
820 NULL);
821 return 0;
824 static int
825 ipfw_nat_fini(void)
827 struct cfg_nat *ptr, *tmp;
828 struct ipfw_nat_context *ctx;
829 int cpu;
831 callout_stop(&ipfw3_nat_cleanup_callout);
833 for (cpu = 0; cpu < ncpus; cpu++) {
834 ctx = ipfw_nat_ctx[cpu];
835 if(ctx != NULL) {
836 LIST_FOREACH_MUTABLE(ptr, &(ctx->nat), _next, tmp) {
837 LIST_REMOVE(ptr, _next);
838 del_redir_spool_cfg(ptr, &ptr->redir_chain);
839 LibAliasUninit(ptr->lib);
840 kfree(ptr, M_IPFW_NAT);
843 kfree(ctx, M_IPFW_NAT);
844 ipfw_nat_ctx[cpu] = NULL;
847 ipfw_ctl_nat_ptr = NULL;
849 return unregister_ipfw_module(MODULE_NAT_ID);
852 static int
853 ipfw_nat_modevent(module_t mod, int type, void *data)
855 switch (type) {
856 case MOD_LOAD:
857 return ipfw_nat_init();
858 case MOD_UNLOAD:
859 return ipfw_nat_fini();
860 default:
861 break;
863 return 0;
866 moduledata_t ipfw_nat_mod = {
867 "ipfw3_nat",
868 ipfw_nat_modevent,
869 NULL
872 DECLARE_MODULE(ipfw3_nat, ipfw_nat_mod,
873 SI_SUB_PROTO_IFATTACHDOMAIN, SI_ORDER_ANY);
874 MODULE_DEPEND(ipfw3_nat, libalias, 1, 1, 1);
875 MODULE_DEPEND(ipfw3_nat, ipfw3_basic, 1, 1, 1);
876 MODULE_VERSION(ipfw3_nat, 1);