13b3a8eb9SGleb Smirnoff /*- 23b3a8eb9SGleb Smirnoff * Copyright (c) 2008 Paolo Pisati 33b3a8eb9SGleb Smirnoff * All rights reserved. 43b3a8eb9SGleb Smirnoff * 53b3a8eb9SGleb Smirnoff * Redistribution and use in source and binary forms, with or without 63b3a8eb9SGleb Smirnoff * modification, are permitted provided that the following conditions 73b3a8eb9SGleb Smirnoff * are met: 83b3a8eb9SGleb Smirnoff * 1. Redistributions of source code must retain the above copyright 93b3a8eb9SGleb Smirnoff * notice, this list of conditions and the following disclaimer. 103b3a8eb9SGleb Smirnoff * 2. Redistributions in binary form must reproduce the above copyright 113b3a8eb9SGleb Smirnoff * notice, this list of conditions and the following disclaimer in the 123b3a8eb9SGleb Smirnoff * documentation and/or other materials provided with the distribution. 133b3a8eb9SGleb Smirnoff * 143b3a8eb9SGleb Smirnoff * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 153b3a8eb9SGleb Smirnoff * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 163b3a8eb9SGleb Smirnoff * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 173b3a8eb9SGleb Smirnoff * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 183b3a8eb9SGleb Smirnoff * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 193b3a8eb9SGleb Smirnoff * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 203b3a8eb9SGleb Smirnoff * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 213b3a8eb9SGleb Smirnoff * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 223b3a8eb9SGleb Smirnoff * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 233b3a8eb9SGleb Smirnoff * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 243b3a8eb9SGleb Smirnoff * SUCH DAMAGE. 253b3a8eb9SGleb Smirnoff */ 263b3a8eb9SGleb Smirnoff 273b3a8eb9SGleb Smirnoff #include <sys/cdefs.h> 283b3a8eb9SGleb Smirnoff __FBSDID("$FreeBSD$"); 293b3a8eb9SGleb Smirnoff 303b3a8eb9SGleb Smirnoff #include <sys/param.h> 313b3a8eb9SGleb Smirnoff #include <sys/systm.h> 323b3a8eb9SGleb Smirnoff #include <sys/eventhandler.h> 333b3a8eb9SGleb Smirnoff #include <sys/malloc.h> 3476039bc8SGleb Smirnoff #include <sys/mbuf.h> 353b3a8eb9SGleb Smirnoff #include <sys/kernel.h> 363b3a8eb9SGleb Smirnoff #include <sys/lock.h> 373b3a8eb9SGleb Smirnoff #include <sys/module.h> 383b3a8eb9SGleb Smirnoff #include <sys/rwlock.h> 39ccba94b8SAlexander V. Chernikov #include <sys/rmlock.h> 403b3a8eb9SGleb Smirnoff 413b3a8eb9SGleb Smirnoff #include <netinet/libalias/alias.h> 423b3a8eb9SGleb Smirnoff #include <netinet/libalias/alias_local.h> 433b3a8eb9SGleb Smirnoff 443b3a8eb9SGleb Smirnoff #include <net/if.h> 4576039bc8SGleb Smirnoff #include <net/if_var.h> 463b3a8eb9SGleb Smirnoff #include <netinet/in.h> 473b3a8eb9SGleb Smirnoff #include <netinet/ip.h> 483b3a8eb9SGleb Smirnoff #include <netinet/ip_var.h> 493b3a8eb9SGleb Smirnoff #include <netinet/ip_fw.h> 503b3a8eb9SGleb Smirnoff #include <netinet/tcp.h> 513b3a8eb9SGleb Smirnoff #include <netinet/udp.h> 523b3a8eb9SGleb Smirnoff 533b3a8eb9SGleb Smirnoff #include <netpfil/ipfw/ip_fw_private.h> 543b3a8eb9SGleb Smirnoff 553b3a8eb9SGleb Smirnoff #include <machine/in_cksum.h> /* XXX for in_cksum */ 563b3a8eb9SGleb Smirnoff 57d6164b77SAlexander V. Chernikov struct cfg_spool { 58d6164b77SAlexander V. Chernikov LIST_ENTRY(cfg_spool) _next; /* chain of spool instances */ 59d6164b77SAlexander V. Chernikov struct in_addr addr; 60d6164b77SAlexander V. Chernikov uint16_t port; 61d6164b77SAlexander V. Chernikov }; 62d6164b77SAlexander V. Chernikov 63d6164b77SAlexander V. Chernikov /* Nat redirect configuration. */ 64d6164b77SAlexander V. Chernikov struct cfg_redir { 65d6164b77SAlexander V. Chernikov LIST_ENTRY(cfg_redir) _next; /* chain of redir instances */ 66d6164b77SAlexander V. Chernikov uint16_t mode; /* type of redirect mode */ 67d6164b77SAlexander V. Chernikov uint16_t proto; /* protocol: tcp/udp */ 68d6164b77SAlexander V. Chernikov struct in_addr laddr; /* local ip address */ 69d6164b77SAlexander V. Chernikov struct in_addr paddr; /* public ip address */ 70d6164b77SAlexander V. Chernikov struct in_addr raddr; /* remote ip address */ 71d6164b77SAlexander V. Chernikov uint16_t lport; /* local port */ 72d6164b77SAlexander V. Chernikov uint16_t pport; /* public port */ 73d6164b77SAlexander V. Chernikov uint16_t rport; /* remote port */ 74d6164b77SAlexander V. Chernikov uint16_t pport_cnt; /* number of public ports */ 75d6164b77SAlexander V. Chernikov uint16_t rport_cnt; /* number of remote ports */ 76d6164b77SAlexander V. Chernikov struct alias_link **alink; 77d6164b77SAlexander V. Chernikov u_int16_t spool_cnt; /* num of entry in spool chain */ 78d6164b77SAlexander V. Chernikov /* chain of spool instances */ 79d6164b77SAlexander V. Chernikov LIST_HEAD(spool_chain, cfg_spool) spool_chain; 80d6164b77SAlexander V. Chernikov }; 81d6164b77SAlexander V. Chernikov 82d6164b77SAlexander V. Chernikov /* Nat configuration data struct. */ 83d6164b77SAlexander V. Chernikov struct cfg_nat { 84d6164b77SAlexander V. Chernikov /* chain of nat instances */ 85d6164b77SAlexander V. Chernikov LIST_ENTRY(cfg_nat) _next; 86d6164b77SAlexander V. Chernikov int id; /* nat id */ 87d6164b77SAlexander V. Chernikov struct in_addr ip; /* nat ip address */ 88d6164b77SAlexander V. Chernikov struct libalias *lib; /* libalias instance */ 89d6164b77SAlexander V. Chernikov int mode; /* aliasing mode */ 90d6164b77SAlexander V. Chernikov int redir_cnt; /* number of entry in spool chain */ 91d6164b77SAlexander V. Chernikov /* chain of redir instances */ 92d6164b77SAlexander V. Chernikov LIST_HEAD(redir_chain, cfg_redir) redir_chain; 93d6164b77SAlexander V. Chernikov char if_name[IF_NAMESIZE]; /* interface name */ 94d6164b77SAlexander V. Chernikov }; 95d6164b77SAlexander V. Chernikov 968856400bSMikolaj Golub static eventhandler_tag ifaddr_event_tag; 973b3a8eb9SGleb Smirnoff 983b3a8eb9SGleb Smirnoff static void 993b3a8eb9SGleb Smirnoff ifaddr_change(void *arg __unused, struct ifnet *ifp) 1003b3a8eb9SGleb Smirnoff { 1013b3a8eb9SGleb Smirnoff struct cfg_nat *ptr; 1023b3a8eb9SGleb Smirnoff struct ifaddr *ifa; 1033b3a8eb9SGleb Smirnoff struct ip_fw_chain *chain; 1043b3a8eb9SGleb Smirnoff 1058856400bSMikolaj Golub KASSERT(curvnet == ifp->if_vnet, 1068856400bSMikolaj Golub ("curvnet(%p) differs from iface vnet(%p)", curvnet, ifp->if_vnet)); 1073b3a8eb9SGleb Smirnoff chain = &V_layer3_chain; 108c254a209SAlexander V. Chernikov IPFW_WLOCK(chain); 1093b3a8eb9SGleb Smirnoff /* Check every nat entry... */ 1103b3a8eb9SGleb Smirnoff LIST_FOREACH(ptr, &chain->nat, _next) { 1113b3a8eb9SGleb Smirnoff /* ...using nic 'ifp->if_xname' as dynamic alias address. */ 112c254a209SAlexander V. Chernikov if (strncmp(ptr->if_name, ifp->if_xname, IF_NAMESIZE) != 0) 1133b3a8eb9SGleb Smirnoff continue; 1143b3a8eb9SGleb Smirnoff if_addr_rlock(ifp); 1153b3a8eb9SGleb Smirnoff TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) { 1163b3a8eb9SGleb Smirnoff if (ifa->ifa_addr == NULL) 1173b3a8eb9SGleb Smirnoff continue; 1183b3a8eb9SGleb Smirnoff if (ifa->ifa_addr->sa_family != AF_INET) 1193b3a8eb9SGleb Smirnoff continue; 1203b3a8eb9SGleb Smirnoff ptr->ip = ((struct sockaddr_in *) 1213b3a8eb9SGleb Smirnoff (ifa->ifa_addr))->sin_addr; 1223b3a8eb9SGleb Smirnoff LibAliasSetAddress(ptr->lib, ptr->ip); 1233b3a8eb9SGleb Smirnoff } 1243b3a8eb9SGleb Smirnoff if_addr_runlock(ifp); 1253b3a8eb9SGleb Smirnoff } 126c254a209SAlexander V. Chernikov IPFW_WUNLOCK(chain); 1273b3a8eb9SGleb Smirnoff } 1283b3a8eb9SGleb Smirnoff 1293b3a8eb9SGleb Smirnoff /* 1303b3a8eb9SGleb Smirnoff * delete the pointers for nat entry ix, or all of them if ix < 0 1313b3a8eb9SGleb Smirnoff */ 1323b3a8eb9SGleb Smirnoff static void 1333b3a8eb9SGleb Smirnoff flush_nat_ptrs(struct ip_fw_chain *chain, const int ix) 1343b3a8eb9SGleb Smirnoff { 1353b3a8eb9SGleb Smirnoff int i; 1363b3a8eb9SGleb Smirnoff ipfw_insn_nat *cmd; 1373b3a8eb9SGleb Smirnoff 1383b3a8eb9SGleb Smirnoff IPFW_WLOCK_ASSERT(chain); 1393b3a8eb9SGleb Smirnoff for (i = 0; i < chain->n_rules; i++) { 1403b3a8eb9SGleb Smirnoff cmd = (ipfw_insn_nat *)ACTION_PTR(chain->map[i]); 1413b3a8eb9SGleb Smirnoff /* XXX skip log and the like ? */ 1423b3a8eb9SGleb Smirnoff if (cmd->o.opcode == O_NAT && cmd->nat != NULL && 1433b3a8eb9SGleb Smirnoff (ix < 0 || cmd->nat->id == ix)) 1443b3a8eb9SGleb Smirnoff cmd->nat = NULL; 1453b3a8eb9SGleb Smirnoff } 1463b3a8eb9SGleb Smirnoff } 1473b3a8eb9SGleb Smirnoff 1483b3a8eb9SGleb Smirnoff static void 1493b3a8eb9SGleb Smirnoff del_redir_spool_cfg(struct cfg_nat *n, struct redir_chain *head) 1503b3a8eb9SGleb Smirnoff { 1513b3a8eb9SGleb Smirnoff struct cfg_redir *r, *tmp_r; 1523b3a8eb9SGleb Smirnoff struct cfg_spool *s, *tmp_s; 1533b3a8eb9SGleb Smirnoff int i, num; 1543b3a8eb9SGleb Smirnoff 1553b3a8eb9SGleb Smirnoff LIST_FOREACH_SAFE(r, head, _next, tmp_r) { 1563b3a8eb9SGleb Smirnoff num = 1; /* Number of alias_link to delete. */ 1573b3a8eb9SGleb Smirnoff switch (r->mode) { 158d6164b77SAlexander V. Chernikov case NAT44_REDIR_PORT: 1593b3a8eb9SGleb Smirnoff num = r->pport_cnt; 1603b3a8eb9SGleb Smirnoff /* FALLTHROUGH */ 161d6164b77SAlexander V. Chernikov case NAT44_REDIR_ADDR: 162d6164b77SAlexander V. Chernikov case NAT44_REDIR_PROTO: 1633b3a8eb9SGleb Smirnoff /* Delete all libalias redirect entry. */ 1643b3a8eb9SGleb Smirnoff for (i = 0; i < num; i++) 1653b3a8eb9SGleb Smirnoff LibAliasRedirectDelete(n->lib, r->alink[i]); 1663b3a8eb9SGleb Smirnoff /* Del spool cfg if any. */ 1673b3a8eb9SGleb Smirnoff LIST_FOREACH_SAFE(s, &r->spool_chain, _next, tmp_s) { 1683b3a8eb9SGleb Smirnoff LIST_REMOVE(s, _next); 1693b3a8eb9SGleb Smirnoff free(s, M_IPFW); 1703b3a8eb9SGleb Smirnoff } 1713b3a8eb9SGleb Smirnoff free(r->alink, M_IPFW); 1723b3a8eb9SGleb Smirnoff LIST_REMOVE(r, _next); 1733b3a8eb9SGleb Smirnoff free(r, M_IPFW); 1743b3a8eb9SGleb Smirnoff break; 1753b3a8eb9SGleb Smirnoff default: 1763b3a8eb9SGleb Smirnoff printf("unknown redirect mode: %u\n", r->mode); 1773b3a8eb9SGleb Smirnoff /* XXX - panic?!?!? */ 1783b3a8eb9SGleb Smirnoff break; 1793b3a8eb9SGleb Smirnoff } 1803b3a8eb9SGleb Smirnoff } 1813b3a8eb9SGleb Smirnoff } 1823b3a8eb9SGleb Smirnoff 183d6164b77SAlexander V. Chernikov static int 1843b3a8eb9SGleb Smirnoff add_redir_spool_cfg(char *buf, struct cfg_nat *ptr) 1853b3a8eb9SGleb Smirnoff { 186d6164b77SAlexander V. Chernikov struct cfg_redir *r; 187d6164b77SAlexander V. Chernikov struct cfg_spool *s; 188d6164b77SAlexander V. Chernikov struct nat44_cfg_redir *ser_r; 189d6164b77SAlexander V. Chernikov struct nat44_cfg_spool *ser_s; 190d6164b77SAlexander V. Chernikov 1913b3a8eb9SGleb Smirnoff int cnt, off, i; 1923b3a8eb9SGleb Smirnoff 1933b3a8eb9SGleb Smirnoff for (cnt = 0, off = 0; cnt < ptr->redir_cnt; cnt++) { 194d6164b77SAlexander V. Chernikov ser_r = (struct nat44_cfg_redir *)&buf[off]; 195d6164b77SAlexander V. Chernikov r = malloc(sizeof(*r), M_IPFW, M_WAITOK | M_ZERO); 196d6164b77SAlexander V. Chernikov r->mode = ser_r->mode; 197d6164b77SAlexander V. Chernikov r->laddr = ser_r->laddr; 198d6164b77SAlexander V. Chernikov r->paddr = ser_r->paddr; 199d6164b77SAlexander V. Chernikov r->raddr = ser_r->raddr; 200d6164b77SAlexander V. Chernikov r->lport = ser_r->lport; 201d6164b77SAlexander V. Chernikov r->pport = ser_r->pport; 202d6164b77SAlexander V. Chernikov r->rport = ser_r->rport; 203d6164b77SAlexander V. Chernikov r->pport_cnt = ser_r->pport_cnt; 204d6164b77SAlexander V. Chernikov r->rport_cnt = ser_r->rport_cnt; 205d6164b77SAlexander V. Chernikov r->proto = ser_r->proto; 206d6164b77SAlexander V. Chernikov r->spool_cnt = ser_r->spool_cnt; 207d6164b77SAlexander V. Chernikov //memcpy(r, ser_r, SOF_REDIR); 2083b3a8eb9SGleb Smirnoff LIST_INIT(&r->spool_chain); 209d6164b77SAlexander V. Chernikov off += sizeof(struct nat44_cfg_redir); 2103b3a8eb9SGleb Smirnoff r->alink = malloc(sizeof(struct alias_link *) * r->pport_cnt, 2113b3a8eb9SGleb Smirnoff M_IPFW, M_WAITOK | M_ZERO); 2123b3a8eb9SGleb Smirnoff switch (r->mode) { 213d6164b77SAlexander V. Chernikov case NAT44_REDIR_ADDR: 2143b3a8eb9SGleb Smirnoff r->alink[0] = LibAliasRedirectAddr(ptr->lib, r->laddr, 2153b3a8eb9SGleb Smirnoff r->paddr); 2163b3a8eb9SGleb Smirnoff break; 217d6164b77SAlexander V. Chernikov case NAT44_REDIR_PORT: 2183b3a8eb9SGleb Smirnoff for (i = 0 ; i < r->pport_cnt; i++) { 2193b3a8eb9SGleb Smirnoff /* If remotePort is all ports, set it to 0. */ 2203b3a8eb9SGleb Smirnoff u_short remotePortCopy = r->rport + i; 2213b3a8eb9SGleb Smirnoff if (r->rport_cnt == 1 && r->rport == 0) 2223b3a8eb9SGleb Smirnoff remotePortCopy = 0; 2233b3a8eb9SGleb Smirnoff r->alink[i] = LibAliasRedirectPort(ptr->lib, 2243b3a8eb9SGleb Smirnoff r->laddr, htons(r->lport + i), r->raddr, 2253b3a8eb9SGleb Smirnoff htons(remotePortCopy), r->paddr, 2263b3a8eb9SGleb Smirnoff htons(r->pport + i), r->proto); 2273b3a8eb9SGleb Smirnoff if (r->alink[i] == NULL) { 2283b3a8eb9SGleb Smirnoff r->alink[0] = NULL; 2293b3a8eb9SGleb Smirnoff break; 2303b3a8eb9SGleb Smirnoff } 2313b3a8eb9SGleb Smirnoff } 2323b3a8eb9SGleb Smirnoff break; 233d6164b77SAlexander V. Chernikov case NAT44_REDIR_PROTO: 2343b3a8eb9SGleb Smirnoff r->alink[0] = LibAliasRedirectProto(ptr->lib ,r->laddr, 2353b3a8eb9SGleb Smirnoff r->raddr, r->paddr, r->proto); 2363b3a8eb9SGleb Smirnoff break; 2373b3a8eb9SGleb Smirnoff default: 2383b3a8eb9SGleb Smirnoff printf("unknown redirect mode: %u\n", r->mode); 2393b3a8eb9SGleb Smirnoff break; 2403b3a8eb9SGleb Smirnoff } 241d6164b77SAlexander V. Chernikov if (r->alink[0] == NULL) { 242d6164b77SAlexander V. Chernikov printf("LibAliasRedirect* returned NULL\n"); 243d6164b77SAlexander V. Chernikov return (EINVAL); 244d6164b77SAlexander V. Chernikov } 2453b3a8eb9SGleb Smirnoff /* LSNAT handling. */ 2463b3a8eb9SGleb Smirnoff for (i = 0; i < r->spool_cnt; i++) { 247d6164b77SAlexander V. Chernikov ser_s = (struct nat44_cfg_spool *)&buf[off]; 248d6164b77SAlexander V. Chernikov s = malloc(sizeof(*s), M_IPFW, M_WAITOK | M_ZERO); 249d6164b77SAlexander V. Chernikov s->addr = ser_s->addr; 250d6164b77SAlexander V. Chernikov s->port = ser_s->port; 2513b3a8eb9SGleb Smirnoff LibAliasAddServer(ptr->lib, r->alink[0], 2523b3a8eb9SGleb Smirnoff s->addr, htons(s->port)); 253d6164b77SAlexander V. Chernikov off += sizeof(struct nat44_cfg_spool); 2543b3a8eb9SGleb Smirnoff /* Hook spool entry. */ 2553b3a8eb9SGleb Smirnoff LIST_INSERT_HEAD(&r->spool_chain, s, _next); 2563b3a8eb9SGleb Smirnoff } 2573b3a8eb9SGleb Smirnoff /* And finally hook this redir entry. */ 2583b3a8eb9SGleb Smirnoff LIST_INSERT_HEAD(&ptr->redir_chain, r, _next); 2593b3a8eb9SGleb Smirnoff } 260d6164b77SAlexander V. Chernikov 261d6164b77SAlexander V. Chernikov return (0); 2623b3a8eb9SGleb Smirnoff } 2633b3a8eb9SGleb Smirnoff 26410ab2de0SAlexander V. Chernikov /* 26510ab2de0SAlexander V. Chernikov * ipfw_nat - perform mbuf header translation. 26610ab2de0SAlexander V. Chernikov * 26710ab2de0SAlexander V. Chernikov * Note V_layer3_chain has to be locked while calling ipfw_nat() in 26810ab2de0SAlexander V. Chernikov * 'global' operation mode (t == NULL). 26910ab2de0SAlexander V. Chernikov * 27010ab2de0SAlexander V. Chernikov */ 2713b3a8eb9SGleb Smirnoff static int 2723b3a8eb9SGleb Smirnoff ipfw_nat(struct ip_fw_args *args, struct cfg_nat *t, struct mbuf *m) 2733b3a8eb9SGleb Smirnoff { 2743b3a8eb9SGleb Smirnoff struct mbuf *mcl; 2753b3a8eb9SGleb Smirnoff struct ip *ip; 2763b3a8eb9SGleb Smirnoff /* XXX - libalias duct tape */ 2773b3a8eb9SGleb Smirnoff int ldt, retval, found; 2783b3a8eb9SGleb Smirnoff struct ip_fw_chain *chain; 2793b3a8eb9SGleb Smirnoff char *c; 2803b3a8eb9SGleb Smirnoff 2813b3a8eb9SGleb Smirnoff ldt = 0; 2823b3a8eb9SGleb Smirnoff retval = 0; 2833b3a8eb9SGleb Smirnoff mcl = m_megapullup(m, m->m_pkthdr.len); 2843b3a8eb9SGleb Smirnoff if (mcl == NULL) { 2853b3a8eb9SGleb Smirnoff args->m = NULL; 2863b3a8eb9SGleb Smirnoff return (IP_FW_DENY); 2873b3a8eb9SGleb Smirnoff } 2883b3a8eb9SGleb Smirnoff ip = mtod(mcl, struct ip *); 2893b3a8eb9SGleb Smirnoff 2903b3a8eb9SGleb Smirnoff /* 2913b3a8eb9SGleb Smirnoff * XXX - Libalias checksum offload 'duct tape': 2923b3a8eb9SGleb Smirnoff * 2933b3a8eb9SGleb Smirnoff * locally generated packets have only pseudo-header checksum 2943b3a8eb9SGleb Smirnoff * calculated and libalias will break it[1], so mark them for 2953b3a8eb9SGleb Smirnoff * later fix. Moreover there are cases when libalias modifies 2963b3a8eb9SGleb Smirnoff * tcp packet data[2], mark them for later fix too. 2973b3a8eb9SGleb Smirnoff * 2983b3a8eb9SGleb Smirnoff * [1] libalias was never meant to run in kernel, so it does 2993b3a8eb9SGleb Smirnoff * not have any knowledge about checksum offloading, and 3003b3a8eb9SGleb Smirnoff * expects a packet with a full internet checksum. 3013b3a8eb9SGleb Smirnoff * Unfortunately, packets generated locally will have just the 3023b3a8eb9SGleb Smirnoff * pseudo header calculated, and when libalias tries to adjust 3033b3a8eb9SGleb Smirnoff * the checksum it will actually compute a wrong value. 3043b3a8eb9SGleb Smirnoff * 3053b3a8eb9SGleb Smirnoff * [2] when libalias modifies tcp's data content, full TCP 3063b3a8eb9SGleb Smirnoff * checksum has to be recomputed: the problem is that 3073b3a8eb9SGleb Smirnoff * libalias does not have any idea about checksum offloading. 3083b3a8eb9SGleb Smirnoff * To work around this, we do not do checksumming in LibAlias, 3093b3a8eb9SGleb Smirnoff * but only mark the packets in th_x2 field. If we receive a 3103b3a8eb9SGleb Smirnoff * marked packet, we calculate correct checksum for it 3113b3a8eb9SGleb Smirnoff * aware of offloading. Why such a terrible hack instead of 3123b3a8eb9SGleb Smirnoff * recalculating checksum for each packet? 3133b3a8eb9SGleb Smirnoff * Because the previous checksum was not checked! 3143b3a8eb9SGleb Smirnoff * Recalculating checksums for EVERY packet will hide ALL 3153b3a8eb9SGleb Smirnoff * transmission errors. Yes, marked packets still suffer from 3163b3a8eb9SGleb Smirnoff * this problem. But, sigh, natd(8) has this problem, too. 3173b3a8eb9SGleb Smirnoff * 3183b3a8eb9SGleb Smirnoff * TODO: -make libalias mbuf aware (so 3193b3a8eb9SGleb Smirnoff * it can handle delayed checksum and tso) 3203b3a8eb9SGleb Smirnoff */ 3213b3a8eb9SGleb Smirnoff 3223b3a8eb9SGleb Smirnoff if (mcl->m_pkthdr.rcvif == NULL && 3233b3a8eb9SGleb Smirnoff mcl->m_pkthdr.csum_flags & CSUM_DELAY_DATA) 3243b3a8eb9SGleb Smirnoff ldt = 1; 3253b3a8eb9SGleb Smirnoff 3263b3a8eb9SGleb Smirnoff c = mtod(mcl, char *); 3273b3a8eb9SGleb Smirnoff 3283b3a8eb9SGleb Smirnoff /* Check if this is 'global' instance */ 3293b3a8eb9SGleb Smirnoff if (t == NULL) { 3303b3a8eb9SGleb Smirnoff if (args->oif == NULL) { 3313b3a8eb9SGleb Smirnoff /* Wrong direction, skip processing */ 3323b3a8eb9SGleb Smirnoff args->m = mcl; 3333b3a8eb9SGleb Smirnoff return (IP_FW_NAT); 3343b3a8eb9SGleb Smirnoff } 3353b3a8eb9SGleb Smirnoff 3363b3a8eb9SGleb Smirnoff found = 0; 3373b3a8eb9SGleb Smirnoff chain = &V_layer3_chain; 3385d0cd926SAlexander V. Chernikov IPFW_RLOCK_ASSERT(chain); 3393b3a8eb9SGleb Smirnoff /* Check every nat entry... */ 3403b3a8eb9SGleb Smirnoff LIST_FOREACH(t, &chain->nat, _next) { 3413b3a8eb9SGleb Smirnoff if ((t->mode & PKT_ALIAS_SKIP_GLOBAL) != 0) 3423b3a8eb9SGleb Smirnoff continue; 3433b3a8eb9SGleb Smirnoff retval = LibAliasOutTry(t->lib, c, 3443b3a8eb9SGleb Smirnoff mcl->m_len + M_TRAILINGSPACE(mcl), 0); 3453b3a8eb9SGleb Smirnoff if (retval == PKT_ALIAS_OK) { 3463b3a8eb9SGleb Smirnoff /* Nat instance recognises state */ 3473b3a8eb9SGleb Smirnoff found = 1; 3483b3a8eb9SGleb Smirnoff break; 3493b3a8eb9SGleb Smirnoff } 3503b3a8eb9SGleb Smirnoff } 3513b3a8eb9SGleb Smirnoff if (found != 1) { 3523b3a8eb9SGleb Smirnoff /* No instance found, return ignore */ 3533b3a8eb9SGleb Smirnoff args->m = mcl; 3543b3a8eb9SGleb Smirnoff return (IP_FW_NAT); 3553b3a8eb9SGleb Smirnoff } 3563b3a8eb9SGleb Smirnoff } else { 3573b3a8eb9SGleb Smirnoff if (args->oif == NULL) 3583b3a8eb9SGleb Smirnoff retval = LibAliasIn(t->lib, c, 3593b3a8eb9SGleb Smirnoff mcl->m_len + M_TRAILINGSPACE(mcl)); 3603b3a8eb9SGleb Smirnoff else 3613b3a8eb9SGleb Smirnoff retval = LibAliasOut(t->lib, c, 3623b3a8eb9SGleb Smirnoff mcl->m_len + M_TRAILINGSPACE(mcl)); 3633b3a8eb9SGleb Smirnoff } 3643b3a8eb9SGleb Smirnoff 3653b3a8eb9SGleb Smirnoff /* 3663b3a8eb9SGleb Smirnoff * We drop packet when: 3673b3a8eb9SGleb Smirnoff * 1. libalias returns PKT_ALIAS_ERROR; 3683b3a8eb9SGleb Smirnoff * 2. For incoming packets: 3693b3a8eb9SGleb Smirnoff * a) for unresolved fragments; 3703b3a8eb9SGleb Smirnoff * b) libalias returns PKT_ALIAS_IGNORED and 3713b3a8eb9SGleb Smirnoff * PKT_ALIAS_DENY_INCOMING flag is set. 3723b3a8eb9SGleb Smirnoff */ 3733b3a8eb9SGleb Smirnoff if (retval == PKT_ALIAS_ERROR || 3743b3a8eb9SGleb Smirnoff (args->oif == NULL && (retval == PKT_ALIAS_UNRESOLVED_FRAGMENT || 3753b3a8eb9SGleb Smirnoff (retval == PKT_ALIAS_IGNORED && 3763b3a8eb9SGleb Smirnoff (t->mode & PKT_ALIAS_DENY_INCOMING) != 0)))) { 3773b3a8eb9SGleb Smirnoff /* XXX - should i add some logging? */ 3783b3a8eb9SGleb Smirnoff m_free(mcl); 3793b3a8eb9SGleb Smirnoff args->m = NULL; 3803b3a8eb9SGleb Smirnoff return (IP_FW_DENY); 3813b3a8eb9SGleb Smirnoff } 3823b3a8eb9SGleb Smirnoff 3833b3a8eb9SGleb Smirnoff if (retval == PKT_ALIAS_RESPOND) 3843b3a8eb9SGleb Smirnoff mcl->m_flags |= M_SKIP_FIREWALL; 3853b3a8eb9SGleb Smirnoff mcl->m_pkthdr.len = mcl->m_len = ntohs(ip->ip_len); 3863b3a8eb9SGleb Smirnoff 3873b3a8eb9SGleb Smirnoff /* 3883b3a8eb9SGleb Smirnoff * XXX - libalias checksum offload 3893b3a8eb9SGleb Smirnoff * 'duct tape' (see above) 3903b3a8eb9SGleb Smirnoff */ 3913b3a8eb9SGleb Smirnoff 3923b3a8eb9SGleb Smirnoff if ((ip->ip_off & htons(IP_OFFMASK)) == 0 && 3933b3a8eb9SGleb Smirnoff ip->ip_p == IPPROTO_TCP) { 3943b3a8eb9SGleb Smirnoff struct tcphdr *th; 3953b3a8eb9SGleb Smirnoff 3963b3a8eb9SGleb Smirnoff th = (struct tcphdr *)(ip + 1); 3973b3a8eb9SGleb Smirnoff if (th->th_x2) 3983b3a8eb9SGleb Smirnoff ldt = 1; 3993b3a8eb9SGleb Smirnoff } 4003b3a8eb9SGleb Smirnoff 4013b3a8eb9SGleb Smirnoff if (ldt) { 4023b3a8eb9SGleb Smirnoff struct tcphdr *th; 4033b3a8eb9SGleb Smirnoff struct udphdr *uh; 40423e9c6dcSGleb Smirnoff uint16_t ip_len, cksum; 4053b3a8eb9SGleb Smirnoff 40623e9c6dcSGleb Smirnoff ip_len = ntohs(ip->ip_len); 4073b3a8eb9SGleb Smirnoff cksum = in_pseudo(ip->ip_src.s_addr, ip->ip_dst.s_addr, 40823e9c6dcSGleb Smirnoff htons(ip->ip_p + ip_len - (ip->ip_hl << 2))); 4093b3a8eb9SGleb Smirnoff 4103b3a8eb9SGleb Smirnoff switch (ip->ip_p) { 4113b3a8eb9SGleb Smirnoff case IPPROTO_TCP: 4123b3a8eb9SGleb Smirnoff th = (struct tcphdr *)(ip + 1); 4133b3a8eb9SGleb Smirnoff /* 4143b3a8eb9SGleb Smirnoff * Maybe it was set in 4153b3a8eb9SGleb Smirnoff * libalias... 4163b3a8eb9SGleb Smirnoff */ 4173b3a8eb9SGleb Smirnoff th->th_x2 = 0; 4183b3a8eb9SGleb Smirnoff th->th_sum = cksum; 4193b3a8eb9SGleb Smirnoff mcl->m_pkthdr.csum_data = 4203b3a8eb9SGleb Smirnoff offsetof(struct tcphdr, th_sum); 4213b3a8eb9SGleb Smirnoff break; 4223b3a8eb9SGleb Smirnoff case IPPROTO_UDP: 4233b3a8eb9SGleb Smirnoff uh = (struct udphdr *)(ip + 1); 4243b3a8eb9SGleb Smirnoff uh->uh_sum = cksum; 4253b3a8eb9SGleb Smirnoff mcl->m_pkthdr.csum_data = 4263b3a8eb9SGleb Smirnoff offsetof(struct udphdr, uh_sum); 4273b3a8eb9SGleb Smirnoff break; 4283b3a8eb9SGleb Smirnoff } 4293b3a8eb9SGleb Smirnoff /* No hw checksum offloading: do it ourselves */ 4303b3a8eb9SGleb Smirnoff if ((mcl->m_pkthdr.csum_flags & CSUM_DELAY_DATA) == 0) { 4313b3a8eb9SGleb Smirnoff in_delayed_cksum(mcl); 4323b3a8eb9SGleb Smirnoff mcl->m_pkthdr.csum_flags &= ~CSUM_DELAY_DATA; 4333b3a8eb9SGleb Smirnoff } 4343b3a8eb9SGleb Smirnoff } 4353b3a8eb9SGleb Smirnoff args->m = mcl; 4363b3a8eb9SGleb Smirnoff return (IP_FW_NAT); 4373b3a8eb9SGleb Smirnoff } 4383b3a8eb9SGleb Smirnoff 4393b3a8eb9SGleb Smirnoff static struct cfg_nat * 4403b3a8eb9SGleb Smirnoff lookup_nat(struct nat_list *l, int nat_id) 4413b3a8eb9SGleb Smirnoff { 4423b3a8eb9SGleb Smirnoff struct cfg_nat *res; 4433b3a8eb9SGleb Smirnoff 4443b3a8eb9SGleb Smirnoff LIST_FOREACH(res, l, _next) { 4453b3a8eb9SGleb Smirnoff if (res->id == nat_id) 4463b3a8eb9SGleb Smirnoff break; 4473b3a8eb9SGleb Smirnoff } 4483b3a8eb9SGleb Smirnoff return res; 4493b3a8eb9SGleb Smirnoff } 4503b3a8eb9SGleb Smirnoff 451d6164b77SAlexander V. Chernikov static struct cfg_nat * 452d6164b77SAlexander V. Chernikov lookup_nat_name(struct nat_list *l, char *name) 4533b3a8eb9SGleb Smirnoff { 454d6164b77SAlexander V. Chernikov struct cfg_nat *res; 455d6164b77SAlexander V. Chernikov int id; 456d6164b77SAlexander V. Chernikov char *errptr; 4573b3a8eb9SGleb Smirnoff 458d6164b77SAlexander V. Chernikov id = strtol(name, &errptr, 10); 459d6164b77SAlexander V. Chernikov if (id == 0 || *errptr != '\0') 460d6164b77SAlexander V. Chernikov return (NULL); 4613b3a8eb9SGleb Smirnoff 462d6164b77SAlexander V. Chernikov LIST_FOREACH(res, l, _next) { 463d6164b77SAlexander V. Chernikov if (res->id == id) 464d6164b77SAlexander V. Chernikov break; 4653b3a8eb9SGleb Smirnoff } 466d6164b77SAlexander V. Chernikov return (res); 467d6164b77SAlexander V. Chernikov } 468d6164b77SAlexander V. Chernikov 469d6164b77SAlexander V. Chernikov /* IP_FW3 configuration routines */ 470d6164b77SAlexander V. Chernikov 471d6164b77SAlexander V. Chernikov static void 472d6164b77SAlexander V. Chernikov nat44_config(struct ip_fw_chain *chain, struct nat44_cfg_nat *ucfg) 473d6164b77SAlexander V. Chernikov { 474d6164b77SAlexander V. Chernikov struct cfg_nat *ptr, *tcfg; 475d6164b77SAlexander V. Chernikov int gencnt; 4763b3a8eb9SGleb Smirnoff 4773b3a8eb9SGleb Smirnoff /* 4783b3a8eb9SGleb Smirnoff * Find/create nat rule. 4793b3a8eb9SGleb Smirnoff */ 480d6164b77SAlexander V. Chernikov IPFW_UH_WLOCK(chain); 4813b3a8eb9SGleb Smirnoff gencnt = chain->gencnt; 482d6164b77SAlexander V. Chernikov ptr = lookup_nat_name(&chain->nat, ucfg->name); 4833b3a8eb9SGleb Smirnoff if (ptr == NULL) { 484d6164b77SAlexander V. Chernikov IPFW_UH_WUNLOCK(chain); 4853b3a8eb9SGleb Smirnoff /* New rule: allocate and init new instance. */ 4863b3a8eb9SGleb Smirnoff ptr = malloc(sizeof(struct cfg_nat), M_IPFW, M_WAITOK | M_ZERO); 4873b3a8eb9SGleb Smirnoff ptr->lib = LibAliasInit(NULL); 4883b3a8eb9SGleb Smirnoff LIST_INIT(&ptr->redir_chain); 4893b3a8eb9SGleb Smirnoff } else { 4903b3a8eb9SGleb Smirnoff /* Entry already present: temporarily unhook it. */ 491d6164b77SAlexander V. Chernikov IPFW_WLOCK(chain); 4923b3a8eb9SGleb Smirnoff LIST_REMOVE(ptr, _next); 493d6164b77SAlexander V. Chernikov flush_nat_ptrs(chain, ptr->id); 4943b3a8eb9SGleb Smirnoff IPFW_WUNLOCK(chain); 495d6164b77SAlexander V. Chernikov IPFW_UH_WUNLOCK(chain); 4963b3a8eb9SGleb Smirnoff } 4973b3a8eb9SGleb Smirnoff 4983b3a8eb9SGleb Smirnoff /* 499d6164b77SAlexander V. Chernikov * Basic nat (re)configuration. 5003b3a8eb9SGleb Smirnoff */ 501d6164b77SAlexander V. Chernikov ptr->id = strtol(ucfg->name, NULL, 10); 5023b3a8eb9SGleb Smirnoff /* 5033b3a8eb9SGleb Smirnoff * XXX - what if this rule doesn't nat any ip and just 5043b3a8eb9SGleb Smirnoff * redirect? 5053b3a8eb9SGleb Smirnoff * do we set aliasaddress to 0.0.0.0? 5063b3a8eb9SGleb Smirnoff */ 507d6164b77SAlexander V. Chernikov ptr->ip = ucfg->ip; 508d6164b77SAlexander V. Chernikov ptr->redir_cnt = ucfg->redir_cnt; 509d6164b77SAlexander V. Chernikov ptr->mode = ucfg->mode; 510d6164b77SAlexander V. Chernikov strlcpy(ptr->if_name, ucfg->if_name, sizeof(ptr->if_name)); 511d6164b77SAlexander V. Chernikov LibAliasSetMode(ptr->lib, ptr->mode, ~0); 5123b3a8eb9SGleb Smirnoff LibAliasSetAddress(ptr->lib, ptr->ip); 5133b3a8eb9SGleb Smirnoff 5143b3a8eb9SGleb Smirnoff /* 5153b3a8eb9SGleb Smirnoff * Redir and LSNAT configuration. 5163b3a8eb9SGleb Smirnoff */ 5173b3a8eb9SGleb Smirnoff /* Delete old cfgs. */ 5183b3a8eb9SGleb Smirnoff del_redir_spool_cfg(ptr, &ptr->redir_chain); 5193b3a8eb9SGleb Smirnoff /* Add new entries. */ 520d6164b77SAlexander V. Chernikov add_redir_spool_cfg((char *)(ucfg + 1), ptr); 521d6164b77SAlexander V. Chernikov IPFW_UH_WLOCK(chain); 5223b3a8eb9SGleb Smirnoff 5233b3a8eb9SGleb Smirnoff /* Extra check to avoid race with another ipfw_nat_cfg() */ 524d6164b77SAlexander V. Chernikov tcfg = NULL; 525d6164b77SAlexander V. Chernikov if (gencnt != chain->gencnt) 526d6164b77SAlexander V. Chernikov tcfg = lookup_nat_name(&chain->nat, ucfg->name); 527d6164b77SAlexander V. Chernikov IPFW_WLOCK(chain); 528d6164b77SAlexander V. Chernikov if (tcfg != NULL) 529d6164b77SAlexander V. Chernikov LIST_REMOVE(tcfg, _next); 5303b3a8eb9SGleb Smirnoff LIST_INSERT_HEAD(&chain->nat, ptr, _next); 5313b3a8eb9SGleb Smirnoff IPFW_WUNLOCK(chain); 532d6164b77SAlexander V. Chernikov chain->gencnt++; 533d6164b77SAlexander V. Chernikov 534d6164b77SAlexander V. Chernikov IPFW_UH_WUNLOCK(chain); 535d6164b77SAlexander V. Chernikov 536d6164b77SAlexander V. Chernikov if (tcfg != NULL) 537d6164b77SAlexander V. Chernikov free(tcfg, M_IPFW); 538d6164b77SAlexander V. Chernikov } 539d6164b77SAlexander V. Chernikov 540d6164b77SAlexander V. Chernikov /* 541d6164b77SAlexander V. Chernikov * Creates/configure nat44 instance 542d6164b77SAlexander V. Chernikov * Data layout (v0)(current): 543d6164b77SAlexander V. Chernikov * Request: [ ipfw_obj_header nat44_cfg_nat .. ] 544d6164b77SAlexander V. Chernikov * 545d6164b77SAlexander V. Chernikov * Returns 0 on success 546d6164b77SAlexander V. Chernikov */ 547d6164b77SAlexander V. Chernikov static int 548d6164b77SAlexander V. Chernikov nat44_cfg(struct ip_fw_chain *chain, ip_fw3_opheader *op3, 549d6164b77SAlexander V. Chernikov struct sockopt_data *sd) 550d6164b77SAlexander V. Chernikov { 551d6164b77SAlexander V. Chernikov ipfw_obj_header *oh; 552d6164b77SAlexander V. Chernikov struct nat44_cfg_nat *ucfg; 553d6164b77SAlexander V. Chernikov int id; 554d6164b77SAlexander V. Chernikov size_t read; 555d6164b77SAlexander V. Chernikov char *errptr; 556d6164b77SAlexander V. Chernikov 557d6164b77SAlexander V. Chernikov /* Check minimum header size */ 558d6164b77SAlexander V. Chernikov if (sd->valsize < (sizeof(*oh) + sizeof(*ucfg))) 559d6164b77SAlexander V. Chernikov return (EINVAL); 560d6164b77SAlexander V. Chernikov 561d6164b77SAlexander V. Chernikov oh = (ipfw_obj_header *)sd->kbuf; 562d6164b77SAlexander V. Chernikov 563d6164b77SAlexander V. Chernikov /* Basic length checks for TLVs */ 564d6164b77SAlexander V. Chernikov if (oh->ntlv.head.length != sizeof(oh->ntlv)) 565d6164b77SAlexander V. Chernikov return (EINVAL); 566d6164b77SAlexander V. Chernikov 567d6164b77SAlexander V. Chernikov ucfg = (struct nat44_cfg_nat *)(oh + 1); 568d6164b77SAlexander V. Chernikov 569d6164b77SAlexander V. Chernikov /* Check if name is properly terminated and looks like number */ 570d6164b77SAlexander V. Chernikov if (strnlen(ucfg->name, sizeof(ucfg->name)) == sizeof(ucfg->name)) 571d6164b77SAlexander V. Chernikov return (EINVAL); 572d6164b77SAlexander V. Chernikov id = strtol(ucfg->name, &errptr, 10); 573d6164b77SAlexander V. Chernikov if (id == 0 || *errptr != '\0') 574d6164b77SAlexander V. Chernikov return (EINVAL); 575d6164b77SAlexander V. Chernikov 576d6164b77SAlexander V. Chernikov read = sizeof(*oh) + sizeof(*ucfg); 577d6164b77SAlexander V. Chernikov /* Check number of redirs */ 578d6164b77SAlexander V. Chernikov if (sd->valsize < read + ucfg->redir_cnt*sizeof(struct nat44_cfg_redir)) 579d6164b77SAlexander V. Chernikov return (EINVAL); 580d6164b77SAlexander V. Chernikov 581d6164b77SAlexander V. Chernikov nat44_config(chain, ucfg); 582d6164b77SAlexander V. Chernikov return (0); 583d6164b77SAlexander V. Chernikov } 584d6164b77SAlexander V. Chernikov 585d6164b77SAlexander V. Chernikov /* 586d6164b77SAlexander V. Chernikov * Destroys given nat instances. 587d6164b77SAlexander V. Chernikov * Data layout (v0)(current): 588d6164b77SAlexander V. Chernikov * Request: [ ipfw_obj_header ] 589d6164b77SAlexander V. Chernikov * 590d6164b77SAlexander V. Chernikov * Returns 0 on success 591d6164b77SAlexander V. Chernikov */ 592d6164b77SAlexander V. Chernikov static int 593d6164b77SAlexander V. Chernikov nat44_destroy(struct ip_fw_chain *chain, ip_fw3_opheader *op3, 594d6164b77SAlexander V. Chernikov struct sockopt_data *sd) 595d6164b77SAlexander V. Chernikov { 596d6164b77SAlexander V. Chernikov ipfw_obj_header *oh; 597d6164b77SAlexander V. Chernikov struct cfg_nat *ptr; 598d6164b77SAlexander V. Chernikov ipfw_obj_ntlv *ntlv; 599d6164b77SAlexander V. Chernikov 600d6164b77SAlexander V. Chernikov /* Check minimum header size */ 601d6164b77SAlexander V. Chernikov if (sd->valsize < sizeof(*oh)) 602d6164b77SAlexander V. Chernikov return (EINVAL); 603d6164b77SAlexander V. Chernikov 604d6164b77SAlexander V. Chernikov oh = (ipfw_obj_header *)sd->kbuf; 605d6164b77SAlexander V. Chernikov 606d6164b77SAlexander V. Chernikov /* Basic length checks for TLVs */ 607d6164b77SAlexander V. Chernikov if (oh->ntlv.head.length != sizeof(oh->ntlv)) 608d6164b77SAlexander V. Chernikov return (EINVAL); 609d6164b77SAlexander V. Chernikov 610d6164b77SAlexander V. Chernikov ntlv = &oh->ntlv; 611d6164b77SAlexander V. Chernikov /* Check if name is properly terminated */ 612d6164b77SAlexander V. Chernikov if (strnlen(ntlv->name, sizeof(ntlv->name)) == sizeof(ntlv->name)) 613d6164b77SAlexander V. Chernikov return (EINVAL); 614d6164b77SAlexander V. Chernikov 615d6164b77SAlexander V. Chernikov IPFW_UH_WLOCK(chain); 616d6164b77SAlexander V. Chernikov ptr = lookup_nat_name(&chain->nat, ntlv->name); 617d6164b77SAlexander V. Chernikov if (ptr == NULL) { 618d6164b77SAlexander V. Chernikov IPFW_UH_WUNLOCK(chain); 619d6164b77SAlexander V. Chernikov return (ESRCH); 620d6164b77SAlexander V. Chernikov } 621d6164b77SAlexander V. Chernikov IPFW_WLOCK(chain); 622d6164b77SAlexander V. Chernikov LIST_REMOVE(ptr, _next); 623d6164b77SAlexander V. Chernikov flush_nat_ptrs(chain, ptr->id); 624d6164b77SAlexander V. Chernikov IPFW_WUNLOCK(chain); 625d6164b77SAlexander V. Chernikov IPFW_UH_WUNLOCK(chain); 626d6164b77SAlexander V. Chernikov 627d6164b77SAlexander V. Chernikov del_redir_spool_cfg(ptr, &ptr->redir_chain); 628d6164b77SAlexander V. Chernikov LibAliasUninit(ptr->lib); 629d6164b77SAlexander V. Chernikov free(ptr, M_IPFW); 630d6164b77SAlexander V. Chernikov 631d6164b77SAlexander V. Chernikov return (0); 632d6164b77SAlexander V. Chernikov } 633d6164b77SAlexander V. Chernikov 634d6164b77SAlexander V. Chernikov static void 635d6164b77SAlexander V. Chernikov export_nat_cfg(struct cfg_nat *ptr, struct nat44_cfg_nat *ucfg) 636d6164b77SAlexander V. Chernikov { 637d6164b77SAlexander V. Chernikov 638d6164b77SAlexander V. Chernikov snprintf(ucfg->name, sizeof(ucfg->name), "%d", ptr->id); 639d6164b77SAlexander V. Chernikov ucfg->ip = ptr->ip; 640d6164b77SAlexander V. Chernikov ucfg->redir_cnt = ptr->redir_cnt; 641d6164b77SAlexander V. Chernikov ucfg->mode = ptr->mode; 642d6164b77SAlexander V. Chernikov strlcpy(ucfg->if_name, ptr->if_name, sizeof(ucfg->if_name)); 643d6164b77SAlexander V. Chernikov } 644d6164b77SAlexander V. Chernikov 645d6164b77SAlexander V. Chernikov /* 646d6164b77SAlexander V. Chernikov * Gets config for given nat instance 647d6164b77SAlexander V. Chernikov * Data layout (v0)(current): 648d6164b77SAlexander V. Chernikov * Request: [ ipfw_obj_header nat44_cfg_nat .. ] 649d6164b77SAlexander V. Chernikov * 650d6164b77SAlexander V. Chernikov * Returns 0 on success 651d6164b77SAlexander V. Chernikov */ 652d6164b77SAlexander V. Chernikov static int 653d6164b77SAlexander V. Chernikov nat44_get_cfg(struct ip_fw_chain *chain, ip_fw3_opheader *op3, 654d6164b77SAlexander V. Chernikov struct sockopt_data *sd) 655d6164b77SAlexander V. Chernikov { 656d6164b77SAlexander V. Chernikov ipfw_obj_header *oh; 657d6164b77SAlexander V. Chernikov struct nat44_cfg_nat *ucfg; 658d6164b77SAlexander V. Chernikov struct cfg_nat *ptr; 659d6164b77SAlexander V. Chernikov struct cfg_redir *r; 660d6164b77SAlexander V. Chernikov struct cfg_spool *s; 661d6164b77SAlexander V. Chernikov struct nat44_cfg_redir *ser_r; 662d6164b77SAlexander V. Chernikov struct nat44_cfg_spool *ser_s; 663d6164b77SAlexander V. Chernikov size_t sz; 664d6164b77SAlexander V. Chernikov 665d6164b77SAlexander V. Chernikov sz = sizeof(*oh) + sizeof(*ucfg); 666d6164b77SAlexander V. Chernikov /* Check minimum header size */ 667d6164b77SAlexander V. Chernikov if (sd->valsize < sz) 668d6164b77SAlexander V. Chernikov return (EINVAL); 669d6164b77SAlexander V. Chernikov 670d6164b77SAlexander V. Chernikov oh = (struct _ipfw_obj_header *)ipfw_get_sopt_header(sd, sz); 671d6164b77SAlexander V. Chernikov 672d6164b77SAlexander V. Chernikov /* Basic length checks for TLVs */ 673d6164b77SAlexander V. Chernikov if (oh->ntlv.head.length != sizeof(oh->ntlv)) 674d6164b77SAlexander V. Chernikov return (EINVAL); 675d6164b77SAlexander V. Chernikov 676d6164b77SAlexander V. Chernikov ucfg = (struct nat44_cfg_nat *)(oh + 1); 677d6164b77SAlexander V. Chernikov 678d6164b77SAlexander V. Chernikov /* Check if name is properly terminated */ 679d6164b77SAlexander V. Chernikov if (strnlen(ucfg->name, sizeof(ucfg->name)) == sizeof(ucfg->name)) 680d6164b77SAlexander V. Chernikov return (EINVAL); 681d6164b77SAlexander V. Chernikov 682d6164b77SAlexander V. Chernikov IPFW_UH_RLOCK(chain); 683d6164b77SAlexander V. Chernikov ptr = lookup_nat_name(&chain->nat, ucfg->name); 684d6164b77SAlexander V. Chernikov if (ptr == NULL) { 685d6164b77SAlexander V. Chernikov IPFW_UH_RUNLOCK(chain); 686d6164b77SAlexander V. Chernikov return (ESRCH); 687d6164b77SAlexander V. Chernikov } 688d6164b77SAlexander V. Chernikov 689d6164b77SAlexander V. Chernikov export_nat_cfg(ptr, ucfg); 690d6164b77SAlexander V. Chernikov 691d6164b77SAlexander V. Chernikov /* Estimate memory amount */ 692d6164b77SAlexander V. Chernikov sz = sizeof(struct nat44_cfg_nat); 693d6164b77SAlexander V. Chernikov LIST_FOREACH(r, &ptr->redir_chain, _next) { 694d6164b77SAlexander V. Chernikov sz += sizeof(struct nat44_cfg_redir); 695d6164b77SAlexander V. Chernikov LIST_FOREACH(s, &r->spool_chain, _next) 696d6164b77SAlexander V. Chernikov sz += sizeof(struct nat44_cfg_spool); 697d6164b77SAlexander V. Chernikov } 698d6164b77SAlexander V. Chernikov 699d6164b77SAlexander V. Chernikov ucfg->size = sz; 700d6164b77SAlexander V. Chernikov if (sd->valsize < sz + sizeof(*oh)) { 701d6164b77SAlexander V. Chernikov 702d6164b77SAlexander V. Chernikov /* 703d6164b77SAlexander V. Chernikov * Submitted buffer size is not enough. 704d6164b77SAlexander V. Chernikov * WE've already filled in @ucfg structure with 705d6164b77SAlexander V. Chernikov * relevant info including size, so we 706d6164b77SAlexander V. Chernikov * can return. Buffer will be flushed automatically. 707d6164b77SAlexander V. Chernikov */ 708d6164b77SAlexander V. Chernikov IPFW_UH_RUNLOCK(chain); 709d6164b77SAlexander V. Chernikov return (ENOMEM); 710d6164b77SAlexander V. Chernikov } 711d6164b77SAlexander V. Chernikov 712d6164b77SAlexander V. Chernikov /* Size OK, let's copy data */ 713d6164b77SAlexander V. Chernikov LIST_FOREACH(r, &ptr->redir_chain, _next) { 714d6164b77SAlexander V. Chernikov ser_r = (struct nat44_cfg_redir *)ipfw_get_sopt_space(sd, 715d6164b77SAlexander V. Chernikov sizeof(*ser_r)); 716d6164b77SAlexander V. Chernikov ser_r->mode = r->mode; 717d6164b77SAlexander V. Chernikov ser_r->laddr = r->laddr; 718d6164b77SAlexander V. Chernikov ser_r->paddr = r->paddr; 719d6164b77SAlexander V. Chernikov ser_r->raddr = r->raddr; 720d6164b77SAlexander V. Chernikov ser_r->lport = r->lport; 721d6164b77SAlexander V. Chernikov ser_r->pport = r->pport; 722d6164b77SAlexander V. Chernikov ser_r->rport = r->rport; 723d6164b77SAlexander V. Chernikov ser_r->pport_cnt = r->pport_cnt; 724d6164b77SAlexander V. Chernikov ser_r->rport_cnt = r->rport_cnt; 725d6164b77SAlexander V. Chernikov ser_r->proto = r->proto; 726d6164b77SAlexander V. Chernikov ser_r->spool_cnt = r->spool_cnt; 727d6164b77SAlexander V. Chernikov 728d6164b77SAlexander V. Chernikov LIST_FOREACH(s, &r->spool_chain, _next) { 729d6164b77SAlexander V. Chernikov ser_s = (struct nat44_cfg_spool *)ipfw_get_sopt_space( 730d6164b77SAlexander V. Chernikov sd, sizeof(*ser_s)); 731d6164b77SAlexander V. Chernikov 732d6164b77SAlexander V. Chernikov ser_s->addr = s->addr; 733d6164b77SAlexander V. Chernikov ser_s->port = s->port; 734d6164b77SAlexander V. Chernikov } 735d6164b77SAlexander V. Chernikov } 736d6164b77SAlexander V. Chernikov 737d6164b77SAlexander V. Chernikov IPFW_UH_RUNLOCK(chain); 738d6164b77SAlexander V. Chernikov 739d6164b77SAlexander V. Chernikov return (0); 740d6164b77SAlexander V. Chernikov } 741d6164b77SAlexander V. Chernikov 742d6164b77SAlexander V. Chernikov /* 743d6164b77SAlexander V. Chernikov * Lists all nat44 instances currently available in kernel. 744d6164b77SAlexander V. Chernikov * Data layout (v0)(current): 745d6164b77SAlexander V. Chernikov * Request: [ ipfw_obj_lheader ] 746d6164b77SAlexander V. Chernikov * Reply: [ ipfw_obj_lheader nat44_cfg_nat x N ] 747d6164b77SAlexander V. Chernikov * 748d6164b77SAlexander V. Chernikov * Returns 0 on success 749d6164b77SAlexander V. Chernikov */ 750d6164b77SAlexander V. Chernikov static int 751d6164b77SAlexander V. Chernikov nat44_list_nat(struct ip_fw_chain *chain, ip_fw3_opheader *op3, 752d6164b77SAlexander V. Chernikov struct sockopt_data *sd) 753d6164b77SAlexander V. Chernikov { 754d6164b77SAlexander V. Chernikov ipfw_obj_lheader *olh; 755d6164b77SAlexander V. Chernikov struct nat44_cfg_nat *ucfg; 756d6164b77SAlexander V. Chernikov struct cfg_nat *ptr; 757d6164b77SAlexander V. Chernikov int nat_count; 758d6164b77SAlexander V. Chernikov 759d6164b77SAlexander V. Chernikov /* Check minimum header size */ 760d6164b77SAlexander V. Chernikov if (sd->valsize < sizeof(ipfw_obj_lheader)) 761d6164b77SAlexander V. Chernikov return (EINVAL); 762d6164b77SAlexander V. Chernikov 763d6164b77SAlexander V. Chernikov olh = (ipfw_obj_lheader *)ipfw_get_sopt_header(sd, sizeof(*olh)); 764d6164b77SAlexander V. Chernikov IPFW_UH_RLOCK(chain); 765d6164b77SAlexander V. Chernikov nat_count = 0; 766d6164b77SAlexander V. Chernikov LIST_FOREACH(ptr, &chain->nat, _next) 767d6164b77SAlexander V. Chernikov nat_count++; 768d6164b77SAlexander V. Chernikov 769d6164b77SAlexander V. Chernikov olh->count = nat_count; 770d6164b77SAlexander V. Chernikov olh->objsize = sizeof(struct nat44_cfg_nat); 771d6164b77SAlexander V. Chernikov olh->size = sizeof(*olh) + olh->count * olh->objsize; 772d6164b77SAlexander V. Chernikov 773d6164b77SAlexander V. Chernikov if (sd->valsize < olh->size) { 774d6164b77SAlexander V. Chernikov IPFW_UH_RUNLOCK(chain); 775d6164b77SAlexander V. Chernikov return (ENOMEM); 776d6164b77SAlexander V. Chernikov } 777d6164b77SAlexander V. Chernikov 778d6164b77SAlexander V. Chernikov LIST_FOREACH(ptr, &chain->nat, _next) { 779d6164b77SAlexander V. Chernikov ucfg = (struct nat44_cfg_nat *)ipfw_get_sopt_space(sd, 780d6164b77SAlexander V. Chernikov sizeof(*ucfg)); 781d6164b77SAlexander V. Chernikov export_nat_cfg(ptr, ucfg); 782d6164b77SAlexander V. Chernikov } 783d6164b77SAlexander V. Chernikov 784d6164b77SAlexander V. Chernikov IPFW_UH_RUNLOCK(chain); 785d6164b77SAlexander V. Chernikov 786d6164b77SAlexander V. Chernikov return (0); 787d6164b77SAlexander V. Chernikov } 788d6164b77SAlexander V. Chernikov 789d6164b77SAlexander V. Chernikov /* 790d6164b77SAlexander V. Chernikov * Gets log for given nat instance 791d6164b77SAlexander V. Chernikov * Data layout (v0)(current): 792d6164b77SAlexander V. Chernikov * Request: [ ipfw_obj_header nat44_cfg_nat ] 793d6164b77SAlexander V. Chernikov * Reply: [ ipfw_obj_header nat44_cfg_nat LOGBUFFER ] 794d6164b77SAlexander V. Chernikov * 795d6164b77SAlexander V. Chernikov * Returns 0 on success 796d6164b77SAlexander V. Chernikov */ 797d6164b77SAlexander V. Chernikov static int 798d6164b77SAlexander V. Chernikov nat44_get_log(struct ip_fw_chain *chain, ip_fw3_opheader *op3, 799d6164b77SAlexander V. Chernikov struct sockopt_data *sd) 800d6164b77SAlexander V. Chernikov { 801d6164b77SAlexander V. Chernikov ipfw_obj_header *oh; 802d6164b77SAlexander V. Chernikov struct nat44_cfg_nat *ucfg; 803d6164b77SAlexander V. Chernikov struct cfg_nat *ptr; 804d6164b77SAlexander V. Chernikov void *pbuf; 805d6164b77SAlexander V. Chernikov size_t sz; 806d6164b77SAlexander V. Chernikov 807d6164b77SAlexander V. Chernikov sz = sizeof(*oh) + sizeof(*ucfg); 808d6164b77SAlexander V. Chernikov /* Check minimum header size */ 809d6164b77SAlexander V. Chernikov if (sd->valsize < sz) 810d6164b77SAlexander V. Chernikov return (EINVAL); 811d6164b77SAlexander V. Chernikov 812d6164b77SAlexander V. Chernikov oh = (struct _ipfw_obj_header *)ipfw_get_sopt_header(sd, sz); 813d6164b77SAlexander V. Chernikov 814d6164b77SAlexander V. Chernikov /* Basic length checks for TLVs */ 815d6164b77SAlexander V. Chernikov if (oh->ntlv.head.length != sizeof(oh->ntlv)) 816d6164b77SAlexander V. Chernikov return (EINVAL); 817d6164b77SAlexander V. Chernikov 818d6164b77SAlexander V. Chernikov ucfg = (struct nat44_cfg_nat *)(oh + 1); 819d6164b77SAlexander V. Chernikov 820d6164b77SAlexander V. Chernikov /* Check if name is properly terminated */ 821d6164b77SAlexander V. Chernikov if (strnlen(ucfg->name, sizeof(ucfg->name)) == sizeof(ucfg->name)) 822d6164b77SAlexander V. Chernikov return (EINVAL); 823d6164b77SAlexander V. Chernikov 824d6164b77SAlexander V. Chernikov IPFW_UH_RLOCK(chain); 825d6164b77SAlexander V. Chernikov ptr = lookup_nat_name(&chain->nat, ucfg->name); 826d6164b77SAlexander V. Chernikov if (ptr == NULL) { 827d6164b77SAlexander V. Chernikov IPFW_UH_RUNLOCK(chain); 828d6164b77SAlexander V. Chernikov return (ESRCH); 829d6164b77SAlexander V. Chernikov } 830d6164b77SAlexander V. Chernikov 831d6164b77SAlexander V. Chernikov if (ptr->lib->logDesc == NULL) { 832d6164b77SAlexander V. Chernikov IPFW_UH_RUNLOCK(chain); 833d6164b77SAlexander V. Chernikov return (ENOENT); 834d6164b77SAlexander V. Chernikov } 835d6164b77SAlexander V. Chernikov 836d6164b77SAlexander V. Chernikov export_nat_cfg(ptr, ucfg); 837d6164b77SAlexander V. Chernikov 838d6164b77SAlexander V. Chernikov /* Estimate memory amount */ 839d6164b77SAlexander V. Chernikov ucfg->size = sizeof(struct nat44_cfg_nat) + LIBALIAS_BUF_SIZE; 840d6164b77SAlexander V. Chernikov if (sd->valsize < sz + sizeof(*oh)) { 841d6164b77SAlexander V. Chernikov 842d6164b77SAlexander V. Chernikov /* 843d6164b77SAlexander V. Chernikov * Submitted buffer size is not enough. 844d6164b77SAlexander V. Chernikov * WE've already filled in @ucfg structure with 845d6164b77SAlexander V. Chernikov * relevant info including size, so we 846d6164b77SAlexander V. Chernikov * can return. Buffer will be flushed automatically. 847d6164b77SAlexander V. Chernikov */ 848d6164b77SAlexander V. Chernikov IPFW_UH_RUNLOCK(chain); 849d6164b77SAlexander V. Chernikov return (ENOMEM); 850d6164b77SAlexander V. Chernikov } 851d6164b77SAlexander V. Chernikov 852d6164b77SAlexander V. Chernikov pbuf = (void *)ipfw_get_sopt_space(sd, LIBALIAS_BUF_SIZE); 853d6164b77SAlexander V. Chernikov memcpy(pbuf, ptr->lib->logDesc, LIBALIAS_BUF_SIZE); 854d6164b77SAlexander V. Chernikov 855d6164b77SAlexander V. Chernikov IPFW_UH_RUNLOCK(chain); 856d6164b77SAlexander V. Chernikov 857d6164b77SAlexander V. Chernikov return (0); 858d6164b77SAlexander V. Chernikov } 859d6164b77SAlexander V. Chernikov 860d6164b77SAlexander V. Chernikov static struct ipfw_sopt_handler scodes[] = { 861d6164b77SAlexander V. Chernikov { IP_FW_NAT44_XCONFIG, 0, HDIR_SET, nat44_cfg }, 862d6164b77SAlexander V. Chernikov { IP_FW_NAT44_DESTROY, 0, HDIR_SET, nat44_destroy }, 863d6164b77SAlexander V. Chernikov { IP_FW_NAT44_XGETCONFIG, 0, HDIR_GET, nat44_get_cfg }, 864d6164b77SAlexander V. Chernikov { IP_FW_NAT44_LIST_NAT, 0, HDIR_GET, nat44_list_nat }, 865d6164b77SAlexander V. Chernikov { IP_FW_NAT44_XGETLOG, 0, HDIR_GET, nat44_get_log }, 866d6164b77SAlexander V. Chernikov }; 867d6164b77SAlexander V. Chernikov 868d6164b77SAlexander V. Chernikov 869d6164b77SAlexander V. Chernikov /* 870d6164b77SAlexander V. Chernikov * Legacy configuration routines 871d6164b77SAlexander V. Chernikov */ 872d6164b77SAlexander V. Chernikov 873d6164b77SAlexander V. Chernikov struct cfg_spool_legacy { 874d6164b77SAlexander V. Chernikov LIST_ENTRY(cfg_spool_legacy) _next; 875d6164b77SAlexander V. Chernikov struct in_addr addr; 876d6164b77SAlexander V. Chernikov u_short port; 877d6164b77SAlexander V. Chernikov }; 878d6164b77SAlexander V. Chernikov 879d6164b77SAlexander V. Chernikov struct cfg_redir_legacy { 880d6164b77SAlexander V. Chernikov LIST_ENTRY(cfg_redir) _next; 881d6164b77SAlexander V. Chernikov u_int16_t mode; 882d6164b77SAlexander V. Chernikov struct in_addr laddr; 883d6164b77SAlexander V. Chernikov struct in_addr paddr; 884d6164b77SAlexander V. Chernikov struct in_addr raddr; 885d6164b77SAlexander V. Chernikov u_short lport; 886d6164b77SAlexander V. Chernikov u_short pport; 887d6164b77SAlexander V. Chernikov u_short rport; 888d6164b77SAlexander V. Chernikov u_short pport_cnt; 889d6164b77SAlexander V. Chernikov u_short rport_cnt; 890d6164b77SAlexander V. Chernikov int proto; 891d6164b77SAlexander V. Chernikov struct alias_link **alink; 892d6164b77SAlexander V. Chernikov u_int16_t spool_cnt; 893d6164b77SAlexander V. Chernikov LIST_HEAD(, cfg_spool_legacy) spool_chain; 894d6164b77SAlexander V. Chernikov }; 895d6164b77SAlexander V. Chernikov 896d6164b77SAlexander V. Chernikov struct cfg_nat_legacy { 897d6164b77SAlexander V. Chernikov LIST_ENTRY(cfg_nat_legacy) _next; 898d6164b77SAlexander V. Chernikov int id; 899d6164b77SAlexander V. Chernikov struct in_addr ip; 900d6164b77SAlexander V. Chernikov char if_name[IF_NAMESIZE]; 901d6164b77SAlexander V. Chernikov int mode; 902d6164b77SAlexander V. Chernikov struct libalias *lib; 903d6164b77SAlexander V. Chernikov int redir_cnt; 904d6164b77SAlexander V. Chernikov LIST_HEAD(, cfg_redir_legacy) redir_chain; 905d6164b77SAlexander V. Chernikov }; 906d6164b77SAlexander V. Chernikov 907d6164b77SAlexander V. Chernikov static int 908d6164b77SAlexander V. Chernikov ipfw_nat_cfg(struct sockopt *sopt) 909d6164b77SAlexander V. Chernikov { 910d6164b77SAlexander V. Chernikov struct cfg_nat_legacy *cfg; 911d6164b77SAlexander V. Chernikov struct nat44_cfg_nat *ucfg; 912d6164b77SAlexander V. Chernikov struct cfg_redir_legacy *rdir; 913d6164b77SAlexander V. Chernikov struct nat44_cfg_redir *urdir; 914d6164b77SAlexander V. Chernikov char *buf; 915d6164b77SAlexander V. Chernikov size_t len, len2; 916d6164b77SAlexander V. Chernikov int error, i; 917d6164b77SAlexander V. Chernikov 918d6164b77SAlexander V. Chernikov len = sopt->sopt_valsize; 919d6164b77SAlexander V. Chernikov len2 = len + 128; 920d6164b77SAlexander V. Chernikov 921d6164b77SAlexander V. Chernikov /* 922d6164b77SAlexander V. Chernikov * Allocate 2x buffer to store converted structures. 923d6164b77SAlexander V. Chernikov * new redir_cfg has shrinked, so we're sure that 924d6164b77SAlexander V. Chernikov * new buffer size is enough. 925d6164b77SAlexander V. Chernikov */ 926d6164b77SAlexander V. Chernikov buf = malloc(roundup2(len, 8) + len2, M_TEMP, M_WAITOK | M_ZERO); 927d6164b77SAlexander V. Chernikov error = sooptcopyin(sopt, buf, len, sizeof(struct cfg_nat_legacy)); 928d6164b77SAlexander V. Chernikov if (error != 0) 929d6164b77SAlexander V. Chernikov goto out; 930d6164b77SAlexander V. Chernikov 931d6164b77SAlexander V. Chernikov cfg = (struct cfg_nat_legacy *)buf; 932d6164b77SAlexander V. Chernikov if (cfg->id < 0) { 933d6164b77SAlexander V. Chernikov error = EINVAL; 934d6164b77SAlexander V. Chernikov goto out; 935d6164b77SAlexander V. Chernikov } 936d6164b77SAlexander V. Chernikov 937d6164b77SAlexander V. Chernikov ucfg = (struct nat44_cfg_nat *)&buf[roundup2(len, 8)]; 938d6164b77SAlexander V. Chernikov snprintf(ucfg->name, sizeof(ucfg->name), "%d", cfg->id); 939d6164b77SAlexander V. Chernikov strlcpy(ucfg->if_name, cfg->if_name, sizeof(ucfg->if_name)); 940d6164b77SAlexander V. Chernikov ucfg->ip = cfg->ip; 941d6164b77SAlexander V. Chernikov ucfg->mode = cfg->mode; 942d6164b77SAlexander V. Chernikov ucfg->redir_cnt = cfg->redir_cnt; 943d6164b77SAlexander V. Chernikov 944d6164b77SAlexander V. Chernikov if (len < sizeof(*cfg) + cfg->redir_cnt * sizeof(*rdir)) { 945d6164b77SAlexander V. Chernikov error = EINVAL; 946d6164b77SAlexander V. Chernikov goto out; 947d6164b77SAlexander V. Chernikov } 948d6164b77SAlexander V. Chernikov 949d6164b77SAlexander V. Chernikov urdir = (struct nat44_cfg_redir *)(ucfg + 1); 950d6164b77SAlexander V. Chernikov rdir = (struct cfg_redir_legacy *)(cfg + 1); 951d6164b77SAlexander V. Chernikov for (i = 0; i < cfg->redir_cnt; i++) { 952d6164b77SAlexander V. Chernikov urdir->mode = rdir->mode; 953d6164b77SAlexander V. Chernikov urdir->laddr = rdir->laddr; 954d6164b77SAlexander V. Chernikov urdir->paddr = rdir->paddr; 955d6164b77SAlexander V. Chernikov urdir->raddr = rdir->raddr; 956d6164b77SAlexander V. Chernikov urdir->lport = rdir->lport; 957d6164b77SAlexander V. Chernikov urdir->pport = rdir->pport; 958d6164b77SAlexander V. Chernikov urdir->rport = rdir->rport; 959d6164b77SAlexander V. Chernikov urdir->pport_cnt = rdir->pport_cnt; 960d6164b77SAlexander V. Chernikov urdir->rport_cnt = rdir->rport_cnt; 961d6164b77SAlexander V. Chernikov urdir->proto = rdir->proto; 962d6164b77SAlexander V. Chernikov urdir->spool_cnt = rdir->spool_cnt; 963d6164b77SAlexander V. Chernikov 964d6164b77SAlexander V. Chernikov urdir++; 965d6164b77SAlexander V. Chernikov rdir++; 966d6164b77SAlexander V. Chernikov } 967d6164b77SAlexander V. Chernikov 968d6164b77SAlexander V. Chernikov nat44_config(&V_layer3_chain, ucfg); 9693b3a8eb9SGleb Smirnoff 9703b3a8eb9SGleb Smirnoff out: 9713b3a8eb9SGleb Smirnoff free(buf, M_TEMP); 9723b3a8eb9SGleb Smirnoff return (error); 9733b3a8eb9SGleb Smirnoff } 9743b3a8eb9SGleb Smirnoff 9753b3a8eb9SGleb Smirnoff static int 9763b3a8eb9SGleb Smirnoff ipfw_nat_del(struct sockopt *sopt) 9773b3a8eb9SGleb Smirnoff { 9783b3a8eb9SGleb Smirnoff struct cfg_nat *ptr; 9793b3a8eb9SGleb Smirnoff struct ip_fw_chain *chain = &V_layer3_chain; 9803b3a8eb9SGleb Smirnoff int i; 9813b3a8eb9SGleb Smirnoff 9823b3a8eb9SGleb Smirnoff sooptcopyin(sopt, &i, sizeof i, sizeof i); 9833b3a8eb9SGleb Smirnoff /* XXX validate i */ 984d6164b77SAlexander V. Chernikov IPFW_UH_WLOCK(chain); 9853b3a8eb9SGleb Smirnoff ptr = lookup_nat(&chain->nat, i); 9863b3a8eb9SGleb Smirnoff if (ptr == NULL) { 987d6164b77SAlexander V. Chernikov IPFW_UH_WUNLOCK(chain); 9883b3a8eb9SGleb Smirnoff return (EINVAL); 9893b3a8eb9SGleb Smirnoff } 990d6164b77SAlexander V. Chernikov IPFW_WLOCK(chain); 9913b3a8eb9SGleb Smirnoff LIST_REMOVE(ptr, _next); 9923b3a8eb9SGleb Smirnoff flush_nat_ptrs(chain, i); 9933b3a8eb9SGleb Smirnoff IPFW_WUNLOCK(chain); 994d6164b77SAlexander V. Chernikov IPFW_UH_WUNLOCK(chain); 9953b3a8eb9SGleb Smirnoff del_redir_spool_cfg(ptr, &ptr->redir_chain); 9963b3a8eb9SGleb Smirnoff LibAliasUninit(ptr->lib); 9973b3a8eb9SGleb Smirnoff free(ptr, M_IPFW); 9983b3a8eb9SGleb Smirnoff return (0); 9993b3a8eb9SGleb Smirnoff } 10003b3a8eb9SGleb Smirnoff 10013b3a8eb9SGleb Smirnoff static int 10023b3a8eb9SGleb Smirnoff ipfw_nat_get_cfg(struct sockopt *sopt) 10033b3a8eb9SGleb Smirnoff { 10043b3a8eb9SGleb Smirnoff struct ip_fw_chain *chain = &V_layer3_chain; 10053b3a8eb9SGleb Smirnoff struct cfg_nat *n; 1006d6164b77SAlexander V. Chernikov struct cfg_nat_legacy *ucfg; 10073b3a8eb9SGleb Smirnoff struct cfg_redir *r; 10083b3a8eb9SGleb Smirnoff struct cfg_spool *s; 1009d6164b77SAlexander V. Chernikov struct cfg_redir_legacy *ser_r; 1010d6164b77SAlexander V. Chernikov struct cfg_spool_legacy *ser_s; 10113b3a8eb9SGleb Smirnoff char *data; 10123b3a8eb9SGleb Smirnoff int gencnt, nat_cnt, len, error; 10133b3a8eb9SGleb Smirnoff 10143b3a8eb9SGleb Smirnoff nat_cnt = 0; 10153b3a8eb9SGleb Smirnoff len = sizeof(nat_cnt); 10163b3a8eb9SGleb Smirnoff 1017d6164b77SAlexander V. Chernikov IPFW_UH_RLOCK(chain); 10183b3a8eb9SGleb Smirnoff retry: 10193b3a8eb9SGleb Smirnoff gencnt = chain->gencnt; 10203b3a8eb9SGleb Smirnoff /* Estimate memory amount */ 10213b3a8eb9SGleb Smirnoff LIST_FOREACH(n, &chain->nat, _next) { 10223b3a8eb9SGleb Smirnoff nat_cnt++; 1023d6164b77SAlexander V. Chernikov len += sizeof(struct cfg_nat_legacy); 10243b3a8eb9SGleb Smirnoff LIST_FOREACH(r, &n->redir_chain, _next) { 1025d6164b77SAlexander V. Chernikov len += sizeof(struct cfg_redir_legacy); 10263b3a8eb9SGleb Smirnoff LIST_FOREACH(s, &r->spool_chain, _next) 1027d6164b77SAlexander V. Chernikov len += sizeof(struct cfg_spool_legacy); 10283b3a8eb9SGleb Smirnoff } 10293b3a8eb9SGleb Smirnoff } 1030d6164b77SAlexander V. Chernikov IPFW_UH_RUNLOCK(chain); 10313b3a8eb9SGleb Smirnoff 10323b3a8eb9SGleb Smirnoff data = malloc(len, M_TEMP, M_WAITOK | M_ZERO); 10333b3a8eb9SGleb Smirnoff bcopy(&nat_cnt, data, sizeof(nat_cnt)); 10343b3a8eb9SGleb Smirnoff 10353b3a8eb9SGleb Smirnoff nat_cnt = 0; 10363b3a8eb9SGleb Smirnoff len = sizeof(nat_cnt); 10373b3a8eb9SGleb Smirnoff 1038d6164b77SAlexander V. Chernikov IPFW_UH_RLOCK(chain); 10393b3a8eb9SGleb Smirnoff if (gencnt != chain->gencnt) { 10403b3a8eb9SGleb Smirnoff free(data, M_TEMP); 10413b3a8eb9SGleb Smirnoff goto retry; 10423b3a8eb9SGleb Smirnoff } 10433b3a8eb9SGleb Smirnoff /* Serialize all the data. */ 10443b3a8eb9SGleb Smirnoff LIST_FOREACH(n, &chain->nat, _next) { 1045d6164b77SAlexander V. Chernikov ucfg = (struct cfg_nat_legacy *)&data[len]; 1046d6164b77SAlexander V. Chernikov ucfg->id = n->id; 1047d6164b77SAlexander V. Chernikov ucfg->ip = n->ip; 1048d6164b77SAlexander V. Chernikov ucfg->redir_cnt = n->redir_cnt; 1049d6164b77SAlexander V. Chernikov ucfg->mode = n->mode; 1050d6164b77SAlexander V. Chernikov strlcpy(ucfg->if_name, n->if_name, sizeof(ucfg->if_name)); 1051d6164b77SAlexander V. Chernikov len += sizeof(struct cfg_nat_legacy); 10523b3a8eb9SGleb Smirnoff LIST_FOREACH(r, &n->redir_chain, _next) { 1053d6164b77SAlexander V. Chernikov ser_r = (struct cfg_redir_legacy *)&data[len]; 1054d6164b77SAlexander V. Chernikov ser_r->mode = r->mode; 1055d6164b77SAlexander V. Chernikov ser_r->laddr = r->laddr; 1056d6164b77SAlexander V. Chernikov ser_r->paddr = r->paddr; 1057d6164b77SAlexander V. Chernikov ser_r->raddr = r->raddr; 1058d6164b77SAlexander V. Chernikov ser_r->lport = r->lport; 1059d6164b77SAlexander V. Chernikov ser_r->pport = r->pport; 1060d6164b77SAlexander V. Chernikov ser_r->rport = r->rport; 1061d6164b77SAlexander V. Chernikov ser_r->pport_cnt = r->pport_cnt; 1062d6164b77SAlexander V. Chernikov ser_r->rport_cnt = r->rport_cnt; 1063d6164b77SAlexander V. Chernikov ser_r->proto = r->proto; 1064d6164b77SAlexander V. Chernikov ser_r->spool_cnt = r->spool_cnt; 1065d6164b77SAlexander V. Chernikov len += sizeof(struct cfg_redir_legacy); 10663b3a8eb9SGleb Smirnoff LIST_FOREACH(s, &r->spool_chain, _next) { 1067d6164b77SAlexander V. Chernikov ser_s = (struct cfg_spool_legacy *)&data[len]; 1068d6164b77SAlexander V. Chernikov ser_s->addr = s->addr; 1069d6164b77SAlexander V. Chernikov ser_s->port = s->port; 1070d6164b77SAlexander V. Chernikov len += sizeof(struct cfg_spool_legacy); 10713b3a8eb9SGleb Smirnoff } 10723b3a8eb9SGleb Smirnoff } 10733b3a8eb9SGleb Smirnoff } 1074d6164b77SAlexander V. Chernikov IPFW_UH_RUNLOCK(chain); 10753b3a8eb9SGleb Smirnoff 10763b3a8eb9SGleb Smirnoff error = sooptcopyout(sopt, data, len); 10773b3a8eb9SGleb Smirnoff free(data, M_TEMP); 10783b3a8eb9SGleb Smirnoff 10793b3a8eb9SGleb Smirnoff return (error); 10803b3a8eb9SGleb Smirnoff } 10813b3a8eb9SGleb Smirnoff 10823b3a8eb9SGleb Smirnoff static int 10833b3a8eb9SGleb Smirnoff ipfw_nat_get_log(struct sockopt *sopt) 10843b3a8eb9SGleb Smirnoff { 10853b3a8eb9SGleb Smirnoff uint8_t *data; 10863b3a8eb9SGleb Smirnoff struct cfg_nat *ptr; 10873b3a8eb9SGleb Smirnoff int i, size; 10883b3a8eb9SGleb Smirnoff struct ip_fw_chain *chain; 1089ccba94b8SAlexander V. Chernikov IPFW_RLOCK_TRACKER; 10903b3a8eb9SGleb Smirnoff 10913b3a8eb9SGleb Smirnoff chain = &V_layer3_chain; 10923b3a8eb9SGleb Smirnoff 10933b3a8eb9SGleb Smirnoff IPFW_RLOCK(chain); 10943b3a8eb9SGleb Smirnoff /* one pass to count, one to copy the data */ 10953b3a8eb9SGleb Smirnoff i = 0; 10963b3a8eb9SGleb Smirnoff LIST_FOREACH(ptr, &chain->nat, _next) { 10973b3a8eb9SGleb Smirnoff if (ptr->lib->logDesc == NULL) 10983b3a8eb9SGleb Smirnoff continue; 10993b3a8eb9SGleb Smirnoff i++; 11003b3a8eb9SGleb Smirnoff } 11013b3a8eb9SGleb Smirnoff size = i * (LIBALIAS_BUF_SIZE + sizeof(int)); 11023b3a8eb9SGleb Smirnoff data = malloc(size, M_IPFW, M_NOWAIT | M_ZERO); 11033b3a8eb9SGleb Smirnoff if (data == NULL) { 11043b3a8eb9SGleb Smirnoff IPFW_RUNLOCK(chain); 11053b3a8eb9SGleb Smirnoff return (ENOSPC); 11063b3a8eb9SGleb Smirnoff } 11073b3a8eb9SGleb Smirnoff i = 0; 11083b3a8eb9SGleb Smirnoff LIST_FOREACH(ptr, &chain->nat, _next) { 11093b3a8eb9SGleb Smirnoff if (ptr->lib->logDesc == NULL) 11103b3a8eb9SGleb Smirnoff continue; 11113b3a8eb9SGleb Smirnoff bcopy(&ptr->id, &data[i], sizeof(int)); 11123b3a8eb9SGleb Smirnoff i += sizeof(int); 11133b3a8eb9SGleb Smirnoff bcopy(ptr->lib->logDesc, &data[i], LIBALIAS_BUF_SIZE); 11143b3a8eb9SGleb Smirnoff i += LIBALIAS_BUF_SIZE; 11153b3a8eb9SGleb Smirnoff } 11163b3a8eb9SGleb Smirnoff IPFW_RUNLOCK(chain); 11173b3a8eb9SGleb Smirnoff sooptcopyout(sopt, data, size); 11183b3a8eb9SGleb Smirnoff free(data, M_IPFW); 11193b3a8eb9SGleb Smirnoff return(0); 11203b3a8eb9SGleb Smirnoff } 11213b3a8eb9SGleb Smirnoff 11228856400bSMikolaj Golub static int 11238856400bSMikolaj Golub vnet_ipfw_nat_init(const void *arg __unused) 11243b3a8eb9SGleb Smirnoff { 11253b3a8eb9SGleb Smirnoff 11268856400bSMikolaj Golub V_ipfw_nat_ready = 1; 11278856400bSMikolaj Golub return (0); 11283b3a8eb9SGleb Smirnoff } 11293b3a8eb9SGleb Smirnoff 11308856400bSMikolaj Golub static int 11318856400bSMikolaj Golub vnet_ipfw_nat_uninit(const void *arg __unused) 11323b3a8eb9SGleb Smirnoff { 11333b3a8eb9SGleb Smirnoff struct cfg_nat *ptr, *ptr_temp; 11343b3a8eb9SGleb Smirnoff struct ip_fw_chain *chain; 11353b3a8eb9SGleb Smirnoff 11363b3a8eb9SGleb Smirnoff chain = &V_layer3_chain; 11373b3a8eb9SGleb Smirnoff IPFW_WLOCK(chain); 11383b3a8eb9SGleb Smirnoff LIST_FOREACH_SAFE(ptr, &chain->nat, _next, ptr_temp) { 11393b3a8eb9SGleb Smirnoff LIST_REMOVE(ptr, _next); 11403b3a8eb9SGleb Smirnoff del_redir_spool_cfg(ptr, &ptr->redir_chain); 11413b3a8eb9SGleb Smirnoff LibAliasUninit(ptr->lib); 11423b3a8eb9SGleb Smirnoff free(ptr, M_IPFW); 11433b3a8eb9SGleb Smirnoff } 11443b3a8eb9SGleb Smirnoff flush_nat_ptrs(chain, -1 /* flush all */); 11458856400bSMikolaj Golub V_ipfw_nat_ready = 0; 11468856400bSMikolaj Golub IPFW_WUNLOCK(chain); 11478856400bSMikolaj Golub return (0); 11488856400bSMikolaj Golub } 11498856400bSMikolaj Golub 11508856400bSMikolaj Golub static void 11518856400bSMikolaj Golub ipfw_nat_init(void) 11528856400bSMikolaj Golub { 11538856400bSMikolaj Golub 11548856400bSMikolaj Golub /* init ipfw hooks */ 11558856400bSMikolaj Golub ipfw_nat_ptr = ipfw_nat; 11568856400bSMikolaj Golub lookup_nat_ptr = lookup_nat; 11578856400bSMikolaj Golub ipfw_nat_cfg_ptr = ipfw_nat_cfg; 11588856400bSMikolaj Golub ipfw_nat_del_ptr = ipfw_nat_del; 11598856400bSMikolaj Golub ipfw_nat_get_cfg_ptr = ipfw_nat_get_cfg; 11608856400bSMikolaj Golub ipfw_nat_get_log_ptr = ipfw_nat_get_log; 1161d6164b77SAlexander V. Chernikov IPFW_ADD_SOPT_HANDLER(1, scodes); 11628856400bSMikolaj Golub 11638856400bSMikolaj Golub ifaddr_event_tag = EVENTHANDLER_REGISTER(ifaddr_event, ifaddr_change, 11648856400bSMikolaj Golub NULL, EVENTHANDLER_PRI_ANY); 11658856400bSMikolaj Golub } 11668856400bSMikolaj Golub 11678856400bSMikolaj Golub static void 11688856400bSMikolaj Golub ipfw_nat_destroy(void) 11698856400bSMikolaj Golub { 11708856400bSMikolaj Golub 11718856400bSMikolaj Golub EVENTHANDLER_DEREGISTER(ifaddr_event, ifaddr_event_tag); 11723b3a8eb9SGleb Smirnoff /* deregister ipfw_nat */ 1173d6164b77SAlexander V. Chernikov IPFW_DEL_SOPT_HANDLER(1, scodes); 11743b3a8eb9SGleb Smirnoff ipfw_nat_ptr = NULL; 11753b3a8eb9SGleb Smirnoff lookup_nat_ptr = NULL; 11763b3a8eb9SGleb Smirnoff ipfw_nat_cfg_ptr = NULL; 11773b3a8eb9SGleb Smirnoff ipfw_nat_del_ptr = NULL; 11783b3a8eb9SGleb Smirnoff ipfw_nat_get_cfg_ptr = NULL; 11793b3a8eb9SGleb Smirnoff ipfw_nat_get_log_ptr = NULL; 11803b3a8eb9SGleb Smirnoff } 11813b3a8eb9SGleb Smirnoff 11823b3a8eb9SGleb Smirnoff static int 11833b3a8eb9SGleb Smirnoff ipfw_nat_modevent(module_t mod, int type, void *unused) 11843b3a8eb9SGleb Smirnoff { 11853b3a8eb9SGleb Smirnoff int err = 0; 11863b3a8eb9SGleb Smirnoff 11873b3a8eb9SGleb Smirnoff switch (type) { 11883b3a8eb9SGleb Smirnoff case MOD_LOAD: 11893b3a8eb9SGleb Smirnoff break; 11903b3a8eb9SGleb Smirnoff 11913b3a8eb9SGleb Smirnoff case MOD_UNLOAD: 11923b3a8eb9SGleb Smirnoff break; 11933b3a8eb9SGleb Smirnoff 11943b3a8eb9SGleb Smirnoff default: 11953b3a8eb9SGleb Smirnoff return EOPNOTSUPP; 11963b3a8eb9SGleb Smirnoff break; 11973b3a8eb9SGleb Smirnoff } 11983b3a8eb9SGleb Smirnoff return err; 11993b3a8eb9SGleb Smirnoff } 12003b3a8eb9SGleb Smirnoff 12013b3a8eb9SGleb Smirnoff static moduledata_t ipfw_nat_mod = { 12023b3a8eb9SGleb Smirnoff "ipfw_nat", 12033b3a8eb9SGleb Smirnoff ipfw_nat_modevent, 12049823d527SKevin Lo 0 12053b3a8eb9SGleb Smirnoff }; 12063b3a8eb9SGleb Smirnoff 12078856400bSMikolaj Golub /* Define startup order. */ 12088a477d48SMikolaj Golub #define IPFW_NAT_SI_SUB_FIREWALL SI_SUB_PROTO_IFATTACHDOMAIN 12098a477d48SMikolaj Golub #define IPFW_NAT_MODEVENT_ORDER (SI_ORDER_ANY - 128) /* after ipfw */ 12108856400bSMikolaj Golub #define IPFW_NAT_MODULE_ORDER (IPFW_NAT_MODEVENT_ORDER + 1) 12118856400bSMikolaj Golub #define IPFW_NAT_VNET_ORDER (IPFW_NAT_MODEVENT_ORDER + 2) 12128856400bSMikolaj Golub 12138856400bSMikolaj Golub DECLARE_MODULE(ipfw_nat, ipfw_nat_mod, IPFW_NAT_SI_SUB_FIREWALL, SI_ORDER_ANY); 12143b3a8eb9SGleb Smirnoff MODULE_DEPEND(ipfw_nat, libalias, 1, 1, 1); 1215*f9ab623bSAlexander V. Chernikov MODULE_DEPEND(ipfw_nat, ipfw, 3, 3, 3); 12163b3a8eb9SGleb Smirnoff MODULE_VERSION(ipfw_nat, 1); 12178856400bSMikolaj Golub 12188856400bSMikolaj Golub SYSINIT(ipfw_nat_init, IPFW_NAT_SI_SUB_FIREWALL, IPFW_NAT_MODULE_ORDER, 12198856400bSMikolaj Golub ipfw_nat_init, NULL); 12208856400bSMikolaj Golub VNET_SYSINIT(vnet_ipfw_nat_init, IPFW_NAT_SI_SUB_FIREWALL, IPFW_NAT_VNET_ORDER, 12218856400bSMikolaj Golub vnet_ipfw_nat_init, NULL); 12228856400bSMikolaj Golub 12238856400bSMikolaj Golub SYSUNINIT(ipfw_nat_destroy, IPFW_NAT_SI_SUB_FIREWALL, IPFW_NAT_MODULE_ORDER, 12248856400bSMikolaj Golub ipfw_nat_destroy, NULL); 12258856400bSMikolaj Golub VNET_SYSUNINIT(vnet_ipfw_nat_uninit, IPFW_NAT_SI_SUB_FIREWALL, 12268856400bSMikolaj Golub IPFW_NAT_VNET_ORDER, vnet_ipfw_nat_uninit, NULL); 12278856400bSMikolaj Golub 12283b3a8eb9SGleb Smirnoff /* end of file */ 1229