kernel: Don't include <sys/mutex.h> in some drivers that don't need it.
[dragonfly.git] / sys / net / ipfw3_nat / ip_fw3_nat.c
blob0b3a437efee0e8bbe15ab53e46e3adbf1bea6cbe
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 void check_nat(int *cmd_ctl, int *cmd_val, struct ip_fw_args **args,
86 struct ip_fw **f, ipfw_insn *cmd, uint16_t ip_len);
87 void add_alias_link_dispatch(netmsg_t nat_del_msg);
88 int ipfw_nat(struct ip_fw_args *args, struct cfg_nat *t, struct mbuf *m);
89 void nat_add_dispatch(netmsg_t msg);
90 int ipfw_ctl_nat_add(struct sockopt *sopt);
91 void nat_del_dispatch(netmsg_t msg);
92 int ipfw_ctl_nat_del(struct sockopt *sopt);
93 int ipfw_ctl_nat_flush(struct sockopt *sopt);
94 int ipfw_ctl_nat_sockopt(struct sockopt *sopt);
95 void nat_init_ctx_dispatch(netmsg_t msg);
96 int ipfw_ctl_nat_get_cfg(struct sockopt *sopt);
97 void del_redir_spool_cfg(struct cfg_nat *n, struct redir_chain *head);
98 int add_redir_spool_cfg(char *buf, struct cfg_nat *ptr);
99 int ipfw_ctl_nat_get_record(struct sockopt *sopt);
102 void
103 check_nat(int *cmd_ctl, int *cmd_val, struct ip_fw_args **args,
104 struct ip_fw **f, ipfw_insn *cmd, uint16_t ip_len)
106 if ((*args)->eh != NULL) {
107 *cmd_ctl = IP_FW_CTL_NO;
108 *cmd_val = IP_FW_NOT_MATCH;
109 return;
112 struct ipfw_nat_context *nat_ctx;
113 struct cfg_nat *t;
114 int nat_id;
116 nat_ctx = ipfw_nat_ctx[mycpuid];
117 (*args)->rule = *f;
118 t = ((ipfw_insn_nat *)cmd)->nat;
119 if (t == NULL) {
120 nat_id = cmd->arg1;
121 LOOKUP_NAT((*nat_ctx), nat_id, t);
122 if (t == NULL) {
123 *cmd_val = IP_FW_DENY;
124 *cmd_ctl = IP_FW_CTL_DONE;
125 return;
127 ((ipfw_insn_nat *)cmd)->nat = t;
129 *cmd_val = ipfw_nat(*args, t, (*args)->m);
130 *cmd_ctl = IP_FW_CTL_NAT;
133 /* Local prototypes */
134 u_int StartPointIn(struct in_addr, u_short, int);
136 u_int StartPointOut(struct in_addr, struct in_addr,
137 u_short, u_short, int);
139 u_int
140 StartPointIn(struct in_addr alias_addr,
141 u_short alias_port,
142 int link_type)
144 u_int n;
146 n = alias_addr.s_addr;
147 if (link_type != LINK_PPTP)
148 n += alias_port;
149 n += link_type;
150 return (n % LINK_TABLE_IN_SIZE);
154 u_int
155 StartPointOut(struct in_addr src_addr, struct in_addr dst_addr,
156 u_short src_port, u_short dst_port, int link_type)
158 u_int n;
160 n = src_addr.s_addr;
161 n += dst_addr.s_addr;
162 if (link_type != LINK_PPTP) {
163 n += src_port;
164 n += dst_port;
166 n += link_type;
168 return (n % LINK_TABLE_OUT_SIZE);
172 void
173 add_alias_link_dispatch(netmsg_t alias_link_add)
175 struct ipfw_nat_context *nat_ctx;
176 struct netmsg_alias_link_add *msg;
177 struct libalias *la;
178 struct alias_link *lnk;
179 struct cfg_nat *t;
180 struct tcp_dat *aux_tcp;
181 u_int start_point;
183 msg = (struct netmsg_alias_link_add *)alias_link_add;
184 nat_ctx = ipfw_nat_ctx[mycpuid];
185 LOOKUP_NAT((*nat_ctx), msg->id, t);
186 la = t->lib;
187 lnk = kmalloc(sizeof(struct alias_link), M_ALIAS, M_WAITOK | M_ZERO);
188 memcpy(lnk, msg->lnk, sizeof(struct alias_link));
189 lnk->la = la;
190 if (msg->is_tcp) {
191 aux_tcp = kmalloc(sizeof(struct tcp_dat),
192 M_ALIAS, M_WAITOK | M_ZERO);
193 memcpy(aux_tcp, msg->lnk->data.tcp, sizeof(struct tcp_dat));
194 lnk->data.tcp = aux_tcp;
197 /* Set up pointers for output lookup table */
198 start_point = StartPointOut(lnk->src_addr, lnk->dst_addr,
199 lnk->src_port, lnk->dst_port, lnk->link_type);
200 LIST_INSERT_HEAD(&la->linkTableOut[start_point], lnk, list_out);
202 /* Set up pointers for input lookup table */
203 start_point = StartPointIn(lnk->alias_addr,
204 lnk->alias_port, lnk->link_type);
205 LIST_INSERT_HEAD(&la->linkTableIn[start_point], lnk, list_in);
206 kfree(alias_link_add, M_LWKTMSG);
210 ipfw_nat(struct ip_fw_args *args, struct cfg_nat *t, struct mbuf *m)
212 struct alias_link *new = NULL;
213 struct mbuf *mcl;
214 struct ip *ip;
215 int ldt, retval, nextcpu;
216 char *c;
218 ldt = 0;
219 retval = 0;
220 if ((mcl = m_megapullup(m, m->m_pkthdr.len)) ==NULL)
221 goto badnat;
223 ip = mtod(mcl, struct ip *);
224 if (args->eh == NULL) {
225 ip->ip_len = htons(ip->ip_len);
226 ip->ip_off = htons(ip->ip_off);
229 if (mcl->m_pkthdr.rcvif == NULL &&
230 mcl->m_pkthdr.csum_flags & CSUM_DELAY_DATA) {
231 ldt = 1;
234 c = mtod(mcl, char *);
235 if (args->oif == NULL) {
236 retval = LibAliasIn(t->lib, c,
237 mcl->m_len + M_TRAILINGSPACE(mcl), &new);
238 } else {
239 retval = LibAliasOut(t->lib, c,
240 mcl->m_len + M_TRAILINGSPACE(mcl), &new);
242 if (retval != PKT_ALIAS_OK &&
243 retval != PKT_ALIAS_FOUND_HEADER_FRAGMENT) {
244 /* XXX - should i add some logging? */
245 m_free(mcl);
246 badnat:
247 args->m = NULL;
248 return IP_FW_DENY;
250 mcl->m_pkthdr.len = mcl->m_len = ntohs(ip->ip_len);
252 if ((ip->ip_off & htons(IP_OFFMASK)) == 0 &&
253 ip->ip_p == IPPROTO_TCP) {
254 struct tcphdr *th;
256 th = (struct tcphdr *)(ip + 1);
257 if (th->th_x2){
258 ldt = 1;
261 if (new != NULL &&
262 (ip->ip_p == IPPROTO_TCP || ip->ip_p == IPPROTO_UDP)) {
263 ip_hashfn(&mcl, 0);
264 nextcpu = netisr_hashcpu(m->m_pkthdr.hash);
265 if (nextcpu != mycpuid) {
266 struct netmsg_alias_link_add *msg;
267 msg = kmalloc(sizeof(struct netmsg_alias_link_add),
268 M_LWKTMSG, M_NOWAIT | M_ZERO);
270 netmsg_init(&msg->base, NULL,
271 &curthread->td_msgport, 0,
272 add_alias_link_dispatch);
273 msg->lnk = new;
274 msg->id = t->id;
275 if (ip->ip_p == IPPROTO_TCP) {
276 msg->is_tcp = 1;
278 if (args->oif == NULL) {
279 msg->is_outgoing = 0;
280 } else {
281 msg->is_outgoing = 1;
283 netisr_sendmsg(&msg->base, nextcpu);
286 if (ldt) {
287 struct tcphdr *th;
288 struct udphdr *uh;
289 u_short cksum;
291 ip->ip_len = ntohs(ip->ip_len);
292 cksum = in_pseudo(
293 ip->ip_src.s_addr,
294 ip->ip_dst.s_addr,
295 htons(ip->ip_p + ip->ip_len - (ip->ip_hl << 2))
298 switch (ip->ip_p) {
299 case IPPROTO_TCP:
300 th = (struct tcphdr *)(ip + 1);
301 th->th_x2 = 0;
302 th->th_sum = cksum;
303 mcl->m_pkthdr.csum_data =
304 offsetof(struct tcphdr, th_sum);
305 break;
306 case IPPROTO_UDP:
307 uh = (struct udphdr *)(ip + 1);
308 uh->uh_sum = cksum;
309 mcl->m_pkthdr.csum_data =
310 offsetof(struct udphdr, uh_sum);
311 break;
314 * No hw checksum offloading: do it
315 * by ourself.
317 if ((mcl->m_pkthdr.csum_flags & CSUM_DELAY_DATA) == 0) {
318 in_delayed_cksum(mcl);
319 mcl->m_pkthdr.csum_flags &= ~CSUM_DELAY_DATA;
321 ip->ip_len = htons(ip->ip_len);
324 if (args->eh == NULL) {
325 ip->ip_len = ntohs(ip->ip_len);
326 ip->ip_off = ntohs(ip->ip_off);
329 args->m = mcl;
330 return IP_FW_NAT;
333 void
334 del_redir_spool_cfg(struct cfg_nat *n, struct redir_chain *head)
336 struct cfg_redir *r, *tmp_r;
337 struct cfg_spool *s, *tmp_s;
338 int i, num;
340 LIST_FOREACH_MUTABLE(r, head, _next, tmp_r) {
341 num = 1; /* Number of alias_link to delete. */
342 switch (r->mode) {
343 case REDIR_PORT:
344 num = r->pport_cnt;
345 /* FALLTHROUGH */
346 case REDIR_ADDR:
347 case REDIR_PROTO:
348 /* Delete all libalias redirect entry. */
349 for (i = 0; i < num; i++)
350 LibAliasRedirectDelete(n->lib,
351 r->alink[i]);
353 /* Del spool cfg if any. */
354 LIST_FOREACH_MUTABLE(s, &r->spool_chain,
355 _next, tmp_s) {
356 LIST_REMOVE(s, _next);
357 kfree(s, M_IPFW_NAT);
359 kfree(r->alink, M_IPFW_NAT);
360 LIST_REMOVE(r, _next);
361 kfree(r, M_IPFW_NAT);
362 break;
363 default:
364 kprintf("unknown redirect mode: %u\n", r->mode);
365 /* XXX - panic?!?!? */
366 break;
372 add_redir_spool_cfg(char *buf, struct cfg_nat *ptr)
374 struct cfg_redir *r, *ser_r;
375 struct cfg_spool *s, *ser_s;
376 int cnt, off, i;
377 char *panic_err;
379 for (cnt = 0, off = 0; cnt < ptr->redir_cnt; cnt++) {
380 ser_r = (struct cfg_redir *)&buf[off];
381 r = kmalloc(SOF_REDIR, M_IPFW_NAT, M_WAITOK | M_ZERO);
382 memcpy(r, ser_r, SOF_REDIR);
383 LIST_INIT(&r->spool_chain);
384 off += SOF_REDIR;
385 r->alink = kmalloc(sizeof(struct alias_link *) * r->pport_cnt,
386 M_IPFW_NAT, M_WAITOK | M_ZERO);
387 switch (r->mode) {
388 case REDIR_ADDR:
389 r->alink[0] = LibAliasRedirectAddr(ptr->lib,
390 r->laddr, r->paddr);
391 break;
392 case REDIR_PORT:
393 for (i = 0 ; i < r->pport_cnt; i++) {
395 * If remotePort is all ports
396 * set it to 0.
398 u_short remotePortCopy = r->rport + i;
399 if (r->rport_cnt == 1 && r->rport == 0)
400 remotePortCopy = 0;
401 r->alink[i] =
403 LibAliasRedirectPort(ptr->lib,
404 r->laddr,htons(r->lport + i),
405 r->raddr,htons(remotePortCopy),
406 r->paddr,htons(r->pport + i),
407 r->proto);
409 if (r->alink[i] == NULL) {
410 r->alink[0] = NULL;
411 break;
414 break;
415 case REDIR_PROTO:
416 r->alink[0] = LibAliasRedirectProto(ptr->lib,
417 r->laddr, r->raddr, r->paddr, r->proto);
418 break;
419 default:
420 kprintf("unknown redirect mode: %u\n", r->mode);
421 break;
423 if (r->alink[0] == NULL) {
424 panic_err = "LibAliasRedirect* returned NULL";
425 goto bad;
426 } else /* LSNAT handling. */
427 for (i = 0; i < r->spool_cnt; i++) {
428 ser_s = (struct cfg_spool *)&buf[off];
429 s = kmalloc(SOF_REDIR, M_IPFW_NAT,
430 M_WAITOK | M_ZERO);
431 memcpy(s, ser_s, SOF_SPOOL);
432 LibAliasAddServer(ptr->lib, r->alink[0],
433 s->addr, htons(s->port));
434 off += SOF_SPOOL;
435 /* Hook spool entry. */
436 HOOK_SPOOL(&r->spool_chain, s);
438 /* And finally hook this redir entry. */
439 HOOK_REDIR(&ptr->redir_chain, r);
441 return 1;
442 bad:
443 /* something really bad happened: panic! */
444 panic("%s\n", panic_err);
448 ipfw_ctl_nat_get_cfg(struct sockopt *sopt)
450 struct ipfw_nat_context *nat_ctx;
451 struct cfg_nat *n;
452 struct cfg_redir *r;
453 struct cfg_spool *s;
454 int nat_cnt, off, nat_cfg_size;
455 size_t size;
456 uint8_t *data;
458 nat_cnt = 0;
459 nat_cfg_size = 0;
460 off = sizeof(nat_cnt);
462 nat_ctx = ipfw_nat_ctx[mycpuid];
463 size = sopt->sopt_valsize;
465 data = sopt->sopt_val;
466 /* count the size of nat cfg */
467 LIST_FOREACH(n, &((*nat_ctx).nat), _next) {
468 nat_cfg_size += SOF_NAT;
471 LIST_FOREACH(n, &((*nat_ctx).nat), _next) {
472 nat_cnt++;
473 if (off + SOF_NAT < size) {
474 bcopy(n, &data[off], SOF_NAT);
475 off += SOF_NAT;
476 LIST_FOREACH(r, &n->redir_chain, _next) {
477 if (off + SOF_REDIR < size) {
478 bcopy(r, &data[off], SOF_REDIR);
479 off += SOF_REDIR;
480 LIST_FOREACH(s, &r->spool_chain,
481 _next) {
482 if (off + SOF_SPOOL < size) {
483 bcopy(s, &data[off],
484 SOF_SPOOL);
485 off += SOF_SPOOL;
486 } else
487 goto nospace;
489 } else
490 goto nospace;
492 } else
493 goto nospace;
495 bcopy(&nat_cnt, data, sizeof(nat_cnt));
496 sopt->sopt_valsize = nat_cfg_size;
497 return 0;
498 nospace:
499 bzero(sopt->sopt_val, sopt->sopt_valsize);
500 sopt->sopt_valsize = nat_cfg_size;
501 return 0;
505 ipfw_ctl_nat_get_record(struct sockopt *sopt)
507 struct ipfw_nat_context *nat_ctx;
508 struct cfg_nat *t;
509 struct alias_link *lnk;
510 struct libalias *la;
511 size_t sopt_size, all_lnk_size = 0;
512 int i, *nat_id, id, n;
513 struct ipfw_ioc_nat_state *nat_state;
515 int cpu;
517 nat_id = (int *)(sopt->sopt_val);
518 n = *nat_id;
519 sopt_size = sopt->sopt_valsize;
520 nat_state = (struct ipfw_ioc_nat_state *)sopt->sopt_val;
521 for (cpu = 0; cpu < ncpus; cpu++) {
522 nat_ctx = ipfw_nat_ctx[cpu];
523 id = n;
524 LOOKUP_NAT((*nat_ctx), id, t);
525 if (t != NULL) {
526 la = t->lib;
527 LIBALIAS_LOCK_ASSERT(la);
528 for (i = 0; i < LINK_TABLE_OUT_SIZE; i++) {
529 LIST_FOREACH(lnk, &la->linkTableOut[i],
530 list_out) {
531 all_lnk_size += sizeof(*nat_state);
532 if (all_lnk_size > sopt_size)
533 goto nospace;
534 nat_state->src_addr = lnk->src_addr;
535 nat_state->dst_addr = lnk->dst_addr;
536 nat_state->alias_addr = lnk->alias_addr;
537 nat_state->src_port = lnk->src_port;
538 nat_state->dst_port = lnk->dst_port;
539 nat_state->alias_port = lnk->alias_port;
540 nat_state->link_type = lnk->link_type;
541 nat_state->timestamp = lnk->timestamp;
542 nat_state->cpuid = cpu;
543 nat_state->is_outgoing = 1;
544 nat_state++;
546 LIST_FOREACH(lnk, &la->linkTableIn[i],
547 list_out) {
548 all_lnk_size += sizeof(*nat_state);
549 if (all_lnk_size > sopt_size)
550 goto nospace;
551 nat_state->src_addr = lnk->src_addr;
552 nat_state->dst_addr = lnk->dst_addr;
553 nat_state->alias_addr = lnk->alias_addr;
554 nat_state->src_port = lnk->src_port;
555 nat_state->dst_port = lnk->dst_port;
556 nat_state->alias_port = lnk->alias_port;
557 nat_state->link_type = lnk->link_type;
558 nat_state->timestamp = lnk->timestamp;
559 nat_state->cpuid = cpu;
560 nat_state->is_outgoing = 0;
561 nat_state++;
566 sopt->sopt_valsize = all_lnk_size;
567 return 0;
568 nospace:
569 return 0;
572 void
573 nat_add_dispatch(netmsg_t nat_add_msg)
575 struct ipfw_nat_context *nat_ctx;
576 struct cfg_nat *ptr, *ser_n;
577 struct netmsg_nat_add *msg;
579 msg = (struct netmsg_nat_add *)nat_add_msg;
581 ser_n = (struct cfg_nat *)(msg->buf);
583 /* New rule: allocate and init new instance. */
584 ptr = kmalloc(sizeof(struct cfg_nat), M_IPFW_NAT, M_WAITOK | M_ZERO);
586 ptr->lib = LibAliasInit(NULL);
587 if (ptr->lib == NULL) {
588 kfree(ptr, M_IPFW_NAT);
589 kfree(msg->buf, M_IPFW_NAT);
592 LIST_INIT(&ptr->redir_chain);
594 * Basic nat configuration.
596 ptr->id = ser_n->id;
598 * XXX - what if this rule doesn't nat any ip and just
599 * redirect?
600 * do we set aliasaddress to 0.0.0.0?
602 ptr->ip = ser_n->ip;
603 ptr->redir_cnt = ser_n->redir_cnt;
604 ptr->mode = ser_n->mode;
606 LibAliasSetMode(ptr->lib, ser_n->mode, ser_n->mode);
607 LibAliasSetAddress(ptr->lib, ptr->ip);
608 memcpy(ptr->if_name, ser_n->if_name, IF_NAMESIZE);
610 /* Add new entries. */
611 add_redir_spool_cfg(&msg->buf[(sizeof(struct cfg_nat))], ptr);
613 nat_ctx = ipfw_nat_ctx[mycpuid];
614 HOOK_NAT(&(nat_ctx->nat), ptr);
615 netisr_forwardmsg(&msg->base, mycpuid + 1);
619 ipfw_ctl_nat_add(struct sockopt *sopt)
621 struct ipfw_nat_context *nat_ctx;
622 struct cfg_nat *ptr, *ser_n;
623 ser_n = (struct cfg_nat *)(sopt->sopt_val);
625 nat_ctx = ipfw_nat_ctx[mycpuid];
627 * Find/create nat rule.
629 LOOKUP_NAT((*nat_ctx), ser_n->id, ptr);
631 if (ptr == NULL) {
632 struct netmsg_nat_add nat_add_msg;
633 struct netmsg_nat_add *msg;
635 msg = &nat_add_msg;
636 msg->buf = kmalloc(sopt->sopt_valsize,
637 M_IPFW_NAT, M_WAITOK | M_ZERO);
639 sooptcopyin(sopt, msg->buf, sopt->sopt_valsize,
640 sizeof(struct cfg_nat));
642 netmsg_init(&msg->base, NULL, &curthread->td_msgport,
643 0, nat_add_dispatch);
646 netisr_domsg(&msg->base, 0);
647 kfree(msg->buf, M_IPFW_NAT);
648 } else {
649 goto done;
651 done:
652 return 0;
655 void
656 nat_del_dispatch(netmsg_t nat_del_msg)
658 struct ipfw_nat_context *nat_ctx;
659 struct ipfw_context *ctx;
660 struct cfg_nat *n, *tmp;
661 struct netmsg_nat_del *msg;
662 struct ip_fw *f;
663 ipfw_insn *cmd;
664 int id;
666 msg = (struct netmsg_nat_del *)nat_del_msg;
667 id = msg->id;
669 nat_ctx = ipfw_nat_ctx[mycpuid];
670 LOOKUP_NAT((*nat_ctx), id, n);
671 if (n == NULL) {
675 * stop deleting when this cfg_nat was in use in ipfw_ctx
677 ctx = ipfw_ctx[mycpuid];
678 for (f = ctx->ipfw_rule_chain; f; f = f->next) {
679 cmd = ACTION_PTR(f);
680 if ((int)cmd->module == MODULE_NAT_ID &&
681 (int)cmd->opcode == O_NAT_NAT) {
682 tmp = ((ipfw_insn_nat *)cmd)->nat;
683 if (tmp != NULL && tmp->id == n->id) {
688 UNHOOK_NAT(n);
689 del_redir_spool_cfg(n, &n->redir_chain);
690 LibAliasUninit(n->lib);
691 kfree(n, M_IPFW_NAT);
695 ipfw_ctl_nat_del(struct sockopt *sopt)
697 struct netmsg_nat_del nat_del_msg;
698 struct netmsg_nat_del *msg;
699 int *id;
701 msg = &nat_del_msg;
702 id = sopt->sopt_val;
703 msg->id = *id;
705 netmsg_init(&msg->base, NULL, &curthread->td_msgport,
706 0, nat_del_dispatch);
708 netisr_domsg(&msg->base, 0);
709 return 0;
713 ipfw_ctl_nat_flush(struct sockopt *sopt)
715 struct ipfw_nat_context *nat_ctx;
716 struct ipfw_context *ctx;
717 struct cfg_nat *ptr, *tmp;
718 struct ip_fw *f;
719 ipfw_insn *cmd;
720 int cpu;
723 * stop flushing when any cfg_nat was in use in ipfw_ctx
725 for (cpu = 0; cpu < ncpus; cpu++) {
726 ctx = ipfw_ctx[cpu];
727 for (f = ctx->ipfw_rule_chain; f; f = f->next) {
728 cmd = ACTION_PTR(f);
729 if ((int)cmd->module == MODULE_NAT_ID &&
730 (int)cmd->opcode == O_NAT_NAT) {
731 return EINVAL;
736 nat_ctx = ipfw_nat_ctx[mycpuid];
738 LIST_FOREACH_MUTABLE(ptr, &(nat_ctx->nat), _next, tmp) {
739 LIST_REMOVE(ptr, _next);
740 del_redir_spool_cfg(ptr, &ptr->redir_chain);
741 LibAliasUninit(ptr->lib);
742 kfree(ptr, M_IPFW_NAT);
744 return 0;
748 ipfw_ctl_nat_sockopt(struct sockopt *sopt)
750 int error = 0;
751 switch (sopt->sopt_name) {
752 case IP_FW_NAT_ADD:
753 error = ipfw_ctl_nat_add(sopt);
754 break;
755 case IP_FW_NAT_DEL:
756 error = ipfw_ctl_nat_del(sopt);
757 break;
758 case IP_FW_NAT_FLUSH:
759 error = ipfw_ctl_nat_flush(sopt);
760 break;
761 case IP_FW_NAT_GET:
762 error = ipfw_ctl_nat_get_cfg(sopt);
763 break;
764 case IP_FW_NAT_GET_RECORD:
765 error = ipfw_ctl_nat_get_record(sopt);
766 break;
767 default:
768 kprintf("ipfw3 nat invalid socket option %d\n",
769 sopt->sopt_name);
771 return error;
774 void
775 nat_init_ctx_dispatch(netmsg_t msg)
777 struct ipfw_nat_context *tmp;
778 tmp = kmalloc(sizeof(struct ipfw_nat_context),
779 M_IPFW_NAT, M_WAITOK | M_ZERO);
780 ipfw_nat_ctx[mycpuid] = tmp;
781 netisr_forwardmsg(&msg->base, mycpuid + 1);
784 static
785 int ipfw_nat_init(void)
787 struct netmsg_base msg;
788 register_ipfw_module(MODULE_NAT_ID, MODULE_NAT_NAME);
789 register_ipfw_filter_funcs(MODULE_NAT_ID, O_NAT_NAT,
790 (filter_func)check_nat);
791 ipfw_ctl_nat_ptr = ipfw_ctl_nat_sockopt;
792 netmsg_init(&msg, NULL, &curthread->td_msgport,
793 0, nat_init_ctx_dispatch);
794 netisr_domsg(&msg, 0);
795 return 0;
798 static int
799 ipfw_nat_fini(void)
801 struct cfg_nat *ptr, *tmp;
802 struct ipfw_nat_context *ctx;
803 int cpu;
805 for (cpu = 0; cpu < ncpus; cpu++) {
806 ctx = ipfw_nat_ctx[cpu];
807 if(ctx != NULL) {
808 LIST_FOREACH_MUTABLE(ptr, &(ctx->nat), _next, tmp) {
809 LIST_REMOVE(ptr, _next);
810 del_redir_spool_cfg(ptr, &ptr->redir_chain);
811 LibAliasUninit(ptr->lib);
812 kfree(ptr, M_IPFW_NAT);
815 kfree(ctx, M_IPFW_NAT);
816 ipfw_nat_ctx[cpu] = NULL;
819 ipfw_ctl_nat_ptr = NULL;
821 return unregister_ipfw_module(MODULE_NAT_ID);
824 static int
825 ipfw_nat_modevent(module_t mod, int type, void *data)
827 switch (type) {
828 case MOD_LOAD:
829 return ipfw_nat_init();
830 case MOD_UNLOAD:
831 return ipfw_nat_fini();
832 default:
833 break;
835 return 0;
838 moduledata_t ipfw_nat_mod = {
839 "ipfw3_nat",
840 ipfw_nat_modevent,
841 NULL
844 DECLARE_MODULE(ipfw3_nat, ipfw_nat_mod,
845 SI_SUB_PROTO_IFATTACHDOMAIN, SI_ORDER_ANY);
846 MODULE_DEPEND(ipfw3_nat, libalias, 1, 1, 1);
847 MODULE_DEPEND(ipfw3_nat, ipfw3_basic, 1, 1, 1);
848 MODULE_VERSION(ipfw3_nat, 1);