2 * Copyright (c) 2008 Paolo Pisati
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
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.
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
27 #include <sys/cdefs.h>
28 __FBSDID("$FreeBSD$");
30 #include <sys/param.h>
31 #include <sys/systm.h>
32 #include <sys/eventhandler.h>
33 #include <sys/malloc.h>
35 #include <sys/kernel.h>
37 #include <sys/module.h>
38 #include <sys/rwlock.h>
39 #include <sys/rmlock.h>
41 #include <netinet/libalias/alias.h>
42 #include <netinet/libalias/alias_local.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>
53 #include <netpfil/ipfw/ip_fw_private.h>
55 #include <machine/in_cksum.h> /* XXX for in_cksum */
58 LIST_ENTRY(cfg_spool) _next; /* chain of spool instances */
63 /* Nat redirect configuration. */
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;
82 /* Nat configuration data struct. */
84 /* chain of nat instances */
85 LIST_ENTRY(cfg_nat) _next;
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 */
96 static eventhandler_tag ifaddr_event_tag;
99 ifaddr_change(void *arg __unused, struct ifnet *ifp)
103 struct ip_fw_chain *chain;
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)
115 TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) {
116 if (ifa->ifa_addr == NULL)
118 if (ifa->ifa_addr->sa_family != AF_INET)
121 ptr->ip = ((struct sockaddr_in *)
122 (ifa->ifa_addr))->sin_addr;
123 LibAliasSetAddress(ptr->lib, ptr->ip);
126 if_addr_runlock(ifp);
128 IPFW_UH_WUNLOCK(chain);
132 * delete the pointers for nat entry ix, or all of them if ix < 0
135 flush_nat_ptrs(struct ip_fw_chain *chain, const int ix)
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))
151 del_redir_spool_cfg(struct cfg_nat *n, struct redir_chain *head)
153 struct cfg_redir *r, *tmp_r;
154 struct cfg_spool *s, *tmp_s;
157 LIST_FOREACH_SAFE(r, head, _next, tmp_r) {
158 num = 1; /* Number of alias_link to delete. */
160 case NAT44_REDIR_PORT:
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);
173 free(r->alink, M_IPFW);
174 LIST_REMOVE(r, _next);
178 printf("unknown redirect mode: %u\n", r->mode);
179 /* XXX - panic?!?!? */
186 add_redir_spool_cfg(char *buf, struct cfg_nat *ptr)
190 struct nat44_cfg_redir *ser_r;
191 struct nat44_cfg_spool *ser_s;
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);
215 case NAT44_REDIR_ADDR:
216 r->alink[0] = LibAliasRedirectAddr(ptr->lib, r->laddr,
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)
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) {
235 case NAT44_REDIR_PROTO:
236 r->alink[0] = LibAliasRedirectProto(ptr->lib ,r->laddr,
237 r->raddr, r->paddr, r->proto);
240 printf("unknown redirect mode: %u\n", r->mode);
243 if (r->alink[0] == NULL) {
244 printf("LibAliasRedirect* returned NULL\n");
245 free(r->alink, M_IPFW);
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);
261 /* And finally hook this redir entry. */
262 LIST_INSERT_HEAD(&ptr->redir_chain, r, _next);
269 free_nat_instance(struct cfg_nat *ptr)
272 del_redir_spool_cfg(ptr, &ptr->redir_chain);
273 LibAliasUninit(ptr->lib);
279 * ipfw_nat - perform mbuf header translation.
281 * Note V_layer3_chain has to be locked while calling ipfw_nat() in
282 * 'global' operation mode (t == NULL).
286 ipfw_nat(struct ip_fw_args *args, struct cfg_nat *t, struct mbuf *m)
290 /* XXX - libalias duct tape */
291 int ldt, retval, found;
292 struct ip_fw_chain *chain;
297 mcl = m_megapullup(m, m->m_pkthdr.len);
302 ip = mtod(mcl, struct ip *);
305 * XXX - Libalias checksum offload 'duct tape':
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.
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.
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.
332 * TODO: -make libalias mbuf aware (so
333 * it can handle delayed checksum and tso)
336 if (mcl->m_pkthdr.rcvif == NULL &&
337 mcl->m_pkthdr.csum_flags & CSUM_DELAY_DATA)
340 c = mtod(mcl, char *);
342 /* Check if this is 'global' instance */
344 if (args->oif == NULL) {
345 /* Wrong direction, skip processing */
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)
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 */
366 /* No instance found, return ignore */
371 if (args->oif == NULL)
372 retval = LibAliasIn(t->lib, c,
373 mcl->m_len + M_TRAILINGSPACE(mcl));
375 retval = LibAliasOut(t->lib, c,
376 mcl->m_len + M_TRAILINGSPACE(mcl));
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.
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? */
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);
402 * XXX - libalias checksum offload
403 * 'duct tape' (see above)
406 if ((ip->ip_off & htons(IP_OFFMASK)) == 0 &&
407 ip->ip_p == IPPROTO_TCP) {
410 th = (struct tcphdr *)(ip + 1);
418 uint16_t ip_len, cksum;
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)));
426 th = (struct tcphdr *)(ip + 1);
428 * Maybe it was set in
433 mcl->m_pkthdr.csum_data =
434 offsetof(struct tcphdr, th_sum);
437 uh = (struct udphdr *)(ip + 1);
439 mcl->m_pkthdr.csum_data =
440 offsetof(struct udphdr, uh_sum);
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;
453 static struct cfg_nat *
454 lookup_nat(struct nat_list *l, int nat_id)
458 LIST_FOREACH(res, l, _next) {
459 if (res->id == nat_id)
465 static struct cfg_nat *
466 lookup_nat_name(struct nat_list *l, char *name)
472 id = strtol(name, &errptr, 10);
473 if (id == 0 || *errptr != '\0')
476 LIST_FOREACH(res, l, _next) {
483 /* IP_FW3 configuration routines */
486 nat44_config(struct ip_fw_chain *chain, struct nat44_cfg_nat *ucfg)
488 struct cfg_nat *ptr, *tcfg;
492 * Find/create nat rule.
494 IPFW_UH_WLOCK(chain);
495 gencnt = chain->gencnt;
496 ptr = lookup_nat_name(&chain->nat, ucfg->name);
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);
504 /* Entry already present: temporarily unhook it. */
506 LIST_REMOVE(ptr, _next);
507 flush_nat_ptrs(chain, ptr->id);
509 IPFW_UH_WUNLOCK(chain);
513 * Basic nat (re)configuration.
515 ptr->id = strtol(ucfg->name, NULL, 10);
517 * XXX - what if this rule doesn't nat any ip and just
519 * do we set aliasaddress to 0.0.0.0?
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);
529 * Redir and LSNAT configuration.
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);
537 /* Extra check to avoid race with another ipfw_nat_cfg() */
539 if (gencnt != chain->gencnt)
540 tcfg = lookup_nat_name(&chain->nat, ucfg->name);
543 LIST_REMOVE(tcfg, _next);
544 LIST_INSERT_HEAD(&chain->nat, ptr, _next);
548 IPFW_UH_WUNLOCK(chain);
551 free_nat_instance(ptr);
555 * Creates/configure nat44 instance
556 * Data layout (v0)(current):
557 * Request: [ ipfw_obj_header nat44_cfg_nat .. ]
559 * Returns 0 on success
562 nat44_cfg(struct ip_fw_chain *chain, ip_fw3_opheader *op3,
563 struct sockopt_data *sd)
566 struct nat44_cfg_nat *ucfg;
571 /* Check minimum header size */
572 if (sd->valsize < (sizeof(*oh) + sizeof(*ucfg)))
575 oh = (ipfw_obj_header *)sd->kbuf;
577 /* Basic length checks for TLVs */
578 if (oh->ntlv.head.length != sizeof(oh->ntlv))
581 ucfg = (struct nat44_cfg_nat *)(oh + 1);
583 /* Check if name is properly terminated and looks like number */
584 if (strnlen(ucfg->name, sizeof(ucfg->name)) == sizeof(ucfg->name))
586 id = strtol(ucfg->name, &errptr, 10);
587 if (id == 0 || *errptr != '\0')
590 read = sizeof(*oh) + sizeof(*ucfg);
591 /* Check number of redirs */
592 if (sd->valsize < read + ucfg->redir_cnt*sizeof(struct nat44_cfg_redir))
595 nat44_config(chain, ucfg);
600 * Destroys given nat instances.
601 * Data layout (v0)(current):
602 * Request: [ ipfw_obj_header ]
604 * Returns 0 on success
607 nat44_destroy(struct ip_fw_chain *chain, ip_fw3_opheader *op3,
608 struct sockopt_data *sd)
614 /* Check minimum header size */
615 if (sd->valsize < sizeof(*oh))
618 oh = (ipfw_obj_header *)sd->kbuf;
620 /* Basic length checks for TLVs */
621 if (oh->ntlv.head.length != sizeof(oh->ntlv))
625 /* Check if name is properly terminated */
626 if (strnlen(ntlv->name, sizeof(ntlv->name)) == sizeof(ntlv->name))
629 IPFW_UH_WLOCK(chain);
630 ptr = lookup_nat_name(&chain->nat, ntlv->name);
632 IPFW_UH_WUNLOCK(chain);
636 LIST_REMOVE(ptr, _next);
637 flush_nat_ptrs(chain, ptr->id);
639 IPFW_UH_WUNLOCK(chain);
641 free_nat_instance(ptr);
647 export_nat_cfg(struct cfg_nat *ptr, struct nat44_cfg_nat *ucfg)
650 snprintf(ucfg->name, sizeof(ucfg->name), "%d", ptr->id);
652 ucfg->redir_cnt = ptr->redir_cnt;
653 ucfg->mode = ptr->mode;
654 strlcpy(ucfg->if_name, ptr->if_name, sizeof(ucfg->if_name));
658 * Gets config for given nat instance
659 * Data layout (v0)(current):
660 * Request: [ ipfw_obj_header nat44_cfg_nat .. ]
662 * Returns 0 on success
665 nat44_get_cfg(struct ip_fw_chain *chain, ip_fw3_opheader *op3,
666 struct sockopt_data *sd)
669 struct nat44_cfg_nat *ucfg;
673 struct nat44_cfg_redir *ser_r;
674 struct nat44_cfg_spool *ser_s;
677 sz = sizeof(*oh) + sizeof(*ucfg);
678 /* Check minimum header size */
679 if (sd->valsize < sz)
682 oh = (struct _ipfw_obj_header *)ipfw_get_sopt_header(sd, sz);
684 /* Basic length checks for TLVs */
685 if (oh->ntlv.head.length != sizeof(oh->ntlv))
688 ucfg = (struct nat44_cfg_nat *)(oh + 1);
690 /* Check if name is properly terminated */
691 if (strnlen(ucfg->name, sizeof(ucfg->name)) == sizeof(ucfg->name))
694 IPFW_UH_RLOCK(chain);
695 ptr = lookup_nat_name(&chain->nat, ucfg->name);
697 IPFW_UH_RUNLOCK(chain);
701 export_nat_cfg(ptr, ucfg);
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);
712 if (sd->valsize < sz) {
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.
720 IPFW_UH_RUNLOCK(chain);
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,
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;
740 LIST_FOREACH(s, &r->spool_chain, _next) {
741 ser_s = (struct nat44_cfg_spool *)ipfw_get_sopt_space(
744 ser_s->addr = s->addr;
745 ser_s->port = s->port;
749 IPFW_UH_RUNLOCK(chain);
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 ]
760 * Returns 0 on success
763 nat44_list_nat(struct ip_fw_chain *chain, ip_fw3_opheader *op3,
764 struct sockopt_data *sd)
766 ipfw_obj_lheader *olh;
767 struct nat44_cfg_nat *ucfg;
771 /* Check minimum header size */
772 if (sd->valsize < sizeof(ipfw_obj_lheader))
775 olh = (ipfw_obj_lheader *)ipfw_get_sopt_header(sd, sizeof(*olh));
776 IPFW_UH_RLOCK(chain);
778 LIST_FOREACH(ptr, &chain->nat, _next)
781 olh->count = nat_count;
782 olh->objsize = sizeof(struct nat44_cfg_nat);
783 olh->size = sizeof(*olh) + olh->count * olh->objsize;
785 if (sd->valsize < olh->size) {
786 IPFW_UH_RUNLOCK(chain);
790 LIST_FOREACH(ptr, &chain->nat, _next) {
791 ucfg = (struct nat44_cfg_nat *)ipfw_get_sopt_space(sd,
793 export_nat_cfg(ptr, ucfg);
796 IPFW_UH_RUNLOCK(chain);
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 ]
807 * Returns 0 on success
810 nat44_get_log(struct ip_fw_chain *chain, ip_fw3_opheader *op3,
811 struct sockopt_data *sd)
814 struct nat44_cfg_nat *ucfg;
819 sz = sizeof(*oh) + sizeof(*ucfg);
820 /* Check minimum header size */
821 if (sd->valsize < sz)
824 oh = (struct _ipfw_obj_header *)ipfw_get_sopt_header(sd, sz);
826 /* Basic length checks for TLVs */
827 if (oh->ntlv.head.length != sizeof(oh->ntlv))
830 ucfg = (struct nat44_cfg_nat *)(oh + 1);
832 /* Check if name is properly terminated */
833 if (strnlen(ucfg->name, sizeof(ucfg->name)) == sizeof(ucfg->name))
836 IPFW_UH_RLOCK(chain);
837 ptr = lookup_nat_name(&chain->nat, ucfg->name);
839 IPFW_UH_RUNLOCK(chain);
843 if (ptr->lib->logDesc == NULL) {
844 IPFW_UH_RUNLOCK(chain);
848 export_nat_cfg(ptr, ucfg);
850 /* Estimate memory amount */
851 ucfg->size = sizeof(struct nat44_cfg_nat) + LIBALIAS_BUF_SIZE;
852 if (sd->valsize < sz + sizeof(*oh)) {
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.
860 IPFW_UH_RUNLOCK(chain);
864 pbuf = (void *)ipfw_get_sopt_space(sd, LIBALIAS_BUF_SIZE);
865 memcpy(pbuf, ptr->lib->logDesc, LIBALIAS_BUF_SIZE);
867 IPFW_UH_RUNLOCK(chain);
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 },
882 * Legacy configuration routines
885 struct cfg_spool_legacy {
886 LIST_ENTRY(cfg_spool_legacy) _next;
891 struct cfg_redir_legacy {
892 LIST_ENTRY(cfg_redir) _next;
894 struct in_addr laddr;
895 struct in_addr paddr;
896 struct in_addr raddr;
903 struct alias_link **alink;
905 LIST_HEAD(, cfg_spool_legacy) spool_chain;
908 struct cfg_nat_legacy {
909 LIST_ENTRY(cfg_nat_legacy) _next;
912 char if_name[IF_NAMESIZE];
914 struct libalias *lib;
916 LIST_HEAD(, cfg_redir_legacy) redir_chain;
920 ipfw_nat_cfg(struct sockopt *sopt)
922 struct cfg_nat_legacy *cfg;
923 struct nat44_cfg_nat *ucfg;
924 struct cfg_redir_legacy *rdir;
925 struct nat44_cfg_redir *urdir;
930 len = sopt->sopt_valsize;
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.
938 buf = malloc(roundup2(len, 8) + len2, M_TEMP, M_WAITOK | M_ZERO);
939 error = sooptcopyin(sopt, buf, len, sizeof(struct cfg_nat_legacy));
943 cfg = (struct cfg_nat_legacy *)buf;
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));
953 ucfg->mode = cfg->mode;
954 ucfg->redir_cnt = cfg->redir_cnt;
956 if (len < sizeof(*cfg) + cfg->redir_cnt * sizeof(*rdir)) {
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;
980 nat44_config(&V_layer3_chain, ucfg);
988 ipfw_nat_del(struct sockopt *sopt)
991 struct ip_fw_chain *chain = &V_layer3_chain;
994 sooptcopyin(sopt, &i, sizeof i, sizeof i);
996 IPFW_UH_WLOCK(chain);
997 ptr = lookup_nat(&chain->nat, i);
999 IPFW_UH_WUNLOCK(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);
1012 ipfw_nat_get_cfg(struct sockopt *sopt)
1014 struct ip_fw_chain *chain = &V_layer3_chain;
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;
1022 int gencnt, nat_cnt, len, error;
1025 len = sizeof(nat_cnt);
1027 IPFW_UH_RLOCK(chain);
1029 gencnt = chain->gencnt;
1030 /* Estimate memory amount */
1031 LIST_FOREACH(n, &chain->nat, _next) {
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);
1040 IPFW_UH_RUNLOCK(chain);
1042 data = malloc(len, M_TEMP, M_WAITOK | M_ZERO);
1043 bcopy(&nat_cnt, data, sizeof(nat_cnt));
1046 len = sizeof(nat_cnt);
1048 IPFW_UH_RLOCK(chain);
1049 if (gencnt != chain->gencnt) {
1053 /* Serialize all the data. */
1054 LIST_FOREACH(n, &chain->nat, _next) {
1055 ucfg = (struct cfg_nat_legacy *)&data[len];
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);
1084 IPFW_UH_RUNLOCK(chain);
1086 error = sooptcopyout(sopt, data, len);
1093 ipfw_nat_get_log(struct sockopt *sopt)
1096 struct cfg_nat *ptr;
1098 struct ip_fw_chain *chain;
1101 chain = &V_layer3_chain;
1104 /* one pass to count, one to copy the data */
1106 LIST_FOREACH(ptr, &chain->nat, _next) {
1107 if (ptr->lib->logDesc == NULL)
1111 size = i * (LIBALIAS_BUF_SIZE + sizeof(int));
1112 data = malloc(size, M_IPFW, M_NOWAIT | M_ZERO);
1114 IPFW_RUNLOCK(chain);
1118 LIST_FOREACH(ptr, &chain->nat, _next) {
1119 if (ptr->lib->logDesc == NULL)
1121 bcopy(&ptr->id, &data[i], sizeof(int));
1123 bcopy(ptr->lib->logDesc, &data[i], LIBALIAS_BUF_SIZE);
1124 i += LIBALIAS_BUF_SIZE;
1126 IPFW_RUNLOCK(chain);
1127 sooptcopyout(sopt, data, size);
1133 vnet_ipfw_nat_init(const void *arg __unused)
1136 V_ipfw_nat_ready = 1;
1141 vnet_ipfw_nat_uninit(const void *arg __unused)
1143 struct cfg_nat *ptr, *ptr_temp;
1144 struct ip_fw_chain *chain;
1146 chain = &V_layer3_chain;
1148 LIST_FOREACH_SAFE(ptr, &chain->nat, _next, ptr_temp) {
1149 LIST_REMOVE(ptr, _next);
1150 free_nat_instance(ptr);
1152 flush_nat_ptrs(chain, -1 /* flush all */);
1153 V_ipfw_nat_ready = 0;
1154 IPFW_WUNLOCK(chain);
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);
1171 ifaddr_event_tag = EVENTHANDLER_REGISTER(ifaddr_event, ifaddr_change,
1172 NULL, EVENTHANDLER_PRI_ANY);
1176 ipfw_nat_destroy(void)
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;
1191 ipfw_nat_modevent(module_t mod, int type, void *unused)
1209 static moduledata_t ipfw_nat_mod = {
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)
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);
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);
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);