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> 393b3a8eb9SGleb Smirnoff 403b3a8eb9SGleb Smirnoff #include <netinet/libalias/alias.h> 413b3a8eb9SGleb Smirnoff #include <netinet/libalias/alias_local.h> 423b3a8eb9SGleb Smirnoff 433b3a8eb9SGleb Smirnoff #include <net/if.h> 4476039bc8SGleb Smirnoff #include <net/if_var.h> 453b3a8eb9SGleb Smirnoff #include <netinet/in.h> 463b3a8eb9SGleb Smirnoff #include <netinet/ip.h> 473b3a8eb9SGleb Smirnoff #include <netinet/ip_var.h> 483b3a8eb9SGleb Smirnoff #include <netinet/ip_fw.h> 493b3a8eb9SGleb Smirnoff #include <netinet/tcp.h> 503b3a8eb9SGleb Smirnoff #include <netinet/udp.h> 513b3a8eb9SGleb Smirnoff 523b3a8eb9SGleb Smirnoff #include <netpfil/ipfw/ip_fw_private.h> 533b3a8eb9SGleb Smirnoff 543b3a8eb9SGleb Smirnoff #include <machine/in_cksum.h> /* XXX for in_cksum */ 553b3a8eb9SGleb Smirnoff 56*d6164b77SAlexander V. Chernikov struct cfg_spool { 57*d6164b77SAlexander V. Chernikov LIST_ENTRY(cfg_spool) _next; /* chain of spool instances */ 58*d6164b77SAlexander V. Chernikov struct in_addr addr; 59*d6164b77SAlexander V. Chernikov uint16_t port; 60*d6164b77SAlexander V. Chernikov }; 61*d6164b77SAlexander V. Chernikov 62*d6164b77SAlexander V. Chernikov /* Nat redirect configuration. */ 63*d6164b77SAlexander V. Chernikov struct cfg_redir { 64*d6164b77SAlexander V. Chernikov LIST_ENTRY(cfg_redir) _next; /* chain of redir instances */ 65*d6164b77SAlexander V. Chernikov uint16_t mode; /* type of redirect mode */ 66*d6164b77SAlexander V. Chernikov uint16_t proto; /* protocol: tcp/udp */ 67*d6164b77SAlexander V. Chernikov struct in_addr laddr; /* local ip address */ 68*d6164b77SAlexander V. Chernikov struct in_addr paddr; /* public ip address */ 69*d6164b77SAlexander V. Chernikov struct in_addr raddr; /* remote ip address */ 70*d6164b77SAlexander V. Chernikov uint16_t lport; /* local port */ 71*d6164b77SAlexander V. Chernikov uint16_t pport; /* public port */ 72*d6164b77SAlexander V. Chernikov uint16_t rport; /* remote port */ 73*d6164b77SAlexander V. Chernikov uint16_t pport_cnt; /* number of public ports */ 74*d6164b77SAlexander V. Chernikov uint16_t rport_cnt; /* number of remote ports */ 75*d6164b77SAlexander V. Chernikov struct alias_link **alink; 76*d6164b77SAlexander V. Chernikov u_int16_t spool_cnt; /* num of entry in spool chain */ 77*d6164b77SAlexander V. Chernikov /* chain of spool instances */ 78*d6164b77SAlexander V. Chernikov LIST_HEAD(spool_chain, cfg_spool) spool_chain; 79*d6164b77SAlexander V. Chernikov }; 80*d6164b77SAlexander V. Chernikov 81*d6164b77SAlexander V. Chernikov /* Nat configuration data struct. */ 82*d6164b77SAlexander V. Chernikov struct cfg_nat { 83*d6164b77SAlexander V. Chernikov /* chain of nat instances */ 84*d6164b77SAlexander V. Chernikov LIST_ENTRY(cfg_nat) _next; 85*d6164b77SAlexander V. Chernikov int id; /* nat id */ 86*d6164b77SAlexander V. Chernikov struct in_addr ip; /* nat ip address */ 87*d6164b77SAlexander V. Chernikov struct libalias *lib; /* libalias instance */ 88*d6164b77SAlexander V. Chernikov int mode; /* aliasing mode */ 89*d6164b77SAlexander V. Chernikov int redir_cnt; /* number of entry in spool chain */ 90*d6164b77SAlexander V. Chernikov /* chain of redir instances */ 91*d6164b77SAlexander V. Chernikov LIST_HEAD(redir_chain, cfg_redir) redir_chain; 92*d6164b77SAlexander V. Chernikov char if_name[IF_NAMESIZE]; /* interface name */ 93*d6164b77SAlexander V. Chernikov }; 94*d6164b77SAlexander V. Chernikov 958856400bSMikolaj Golub static eventhandler_tag ifaddr_event_tag; 963b3a8eb9SGleb Smirnoff 973b3a8eb9SGleb Smirnoff static void 983b3a8eb9SGleb Smirnoff ifaddr_change(void *arg __unused, struct ifnet *ifp) 993b3a8eb9SGleb Smirnoff { 1003b3a8eb9SGleb Smirnoff struct cfg_nat *ptr; 1013b3a8eb9SGleb Smirnoff struct ifaddr *ifa; 1023b3a8eb9SGleb Smirnoff struct ip_fw_chain *chain; 1033b3a8eb9SGleb Smirnoff 1048856400bSMikolaj Golub KASSERT(curvnet == ifp->if_vnet, 1058856400bSMikolaj Golub ("curvnet(%p) differs from iface vnet(%p)", curvnet, ifp->if_vnet)); 1063b3a8eb9SGleb Smirnoff chain = &V_layer3_chain; 107c254a209SAlexander V. Chernikov IPFW_WLOCK(chain); 1083b3a8eb9SGleb Smirnoff /* Check every nat entry... */ 1093b3a8eb9SGleb Smirnoff LIST_FOREACH(ptr, &chain->nat, _next) { 1103b3a8eb9SGleb Smirnoff /* ...using nic 'ifp->if_xname' as dynamic alias address. */ 111c254a209SAlexander V. Chernikov if (strncmp(ptr->if_name, ifp->if_xname, IF_NAMESIZE) != 0) 1123b3a8eb9SGleb Smirnoff continue; 1133b3a8eb9SGleb Smirnoff if_addr_rlock(ifp); 1143b3a8eb9SGleb Smirnoff TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) { 1153b3a8eb9SGleb Smirnoff if (ifa->ifa_addr == NULL) 1163b3a8eb9SGleb Smirnoff continue; 1173b3a8eb9SGleb Smirnoff if (ifa->ifa_addr->sa_family != AF_INET) 1183b3a8eb9SGleb Smirnoff continue; 1193b3a8eb9SGleb Smirnoff ptr->ip = ((struct sockaddr_in *) 1203b3a8eb9SGleb Smirnoff (ifa->ifa_addr))->sin_addr; 1213b3a8eb9SGleb Smirnoff LibAliasSetAddress(ptr->lib, ptr->ip); 1223b3a8eb9SGleb Smirnoff } 1233b3a8eb9SGleb Smirnoff if_addr_runlock(ifp); 1243b3a8eb9SGleb Smirnoff } 125c254a209SAlexander V. Chernikov IPFW_WUNLOCK(chain); 1263b3a8eb9SGleb Smirnoff } 1273b3a8eb9SGleb Smirnoff 1283b3a8eb9SGleb Smirnoff /* 1293b3a8eb9SGleb Smirnoff * delete the pointers for nat entry ix, or all of them if ix < 0 1303b3a8eb9SGleb Smirnoff */ 1313b3a8eb9SGleb Smirnoff static void 1323b3a8eb9SGleb Smirnoff flush_nat_ptrs(struct ip_fw_chain *chain, const int ix) 1333b3a8eb9SGleb Smirnoff { 1343b3a8eb9SGleb Smirnoff int i; 1353b3a8eb9SGleb Smirnoff ipfw_insn_nat *cmd; 1363b3a8eb9SGleb Smirnoff 1373b3a8eb9SGleb Smirnoff IPFW_WLOCK_ASSERT(chain); 1383b3a8eb9SGleb Smirnoff for (i = 0; i < chain->n_rules; i++) { 1393b3a8eb9SGleb Smirnoff cmd = (ipfw_insn_nat *)ACTION_PTR(chain->map[i]); 1403b3a8eb9SGleb Smirnoff /* XXX skip log and the like ? */ 1413b3a8eb9SGleb Smirnoff if (cmd->o.opcode == O_NAT && cmd->nat != NULL && 1423b3a8eb9SGleb Smirnoff (ix < 0 || cmd->nat->id == ix)) 1433b3a8eb9SGleb Smirnoff cmd->nat = NULL; 1443b3a8eb9SGleb Smirnoff } 1453b3a8eb9SGleb Smirnoff } 1463b3a8eb9SGleb Smirnoff 1473b3a8eb9SGleb Smirnoff static void 1483b3a8eb9SGleb Smirnoff del_redir_spool_cfg(struct cfg_nat *n, struct redir_chain *head) 1493b3a8eb9SGleb Smirnoff { 1503b3a8eb9SGleb Smirnoff struct cfg_redir *r, *tmp_r; 1513b3a8eb9SGleb Smirnoff struct cfg_spool *s, *tmp_s; 1523b3a8eb9SGleb Smirnoff int i, num; 1533b3a8eb9SGleb Smirnoff 1543b3a8eb9SGleb Smirnoff LIST_FOREACH_SAFE(r, head, _next, tmp_r) { 1553b3a8eb9SGleb Smirnoff num = 1; /* Number of alias_link to delete. */ 1563b3a8eb9SGleb Smirnoff switch (r->mode) { 157*d6164b77SAlexander V. Chernikov case NAT44_REDIR_PORT: 1583b3a8eb9SGleb Smirnoff num = r->pport_cnt; 1593b3a8eb9SGleb Smirnoff /* FALLTHROUGH */ 160*d6164b77SAlexander V. Chernikov case NAT44_REDIR_ADDR: 161*d6164b77SAlexander V. Chernikov case NAT44_REDIR_PROTO: 1623b3a8eb9SGleb Smirnoff /* Delete all libalias redirect entry. */ 1633b3a8eb9SGleb Smirnoff for (i = 0; i < num; i++) 1643b3a8eb9SGleb Smirnoff LibAliasRedirectDelete(n->lib, r->alink[i]); 1653b3a8eb9SGleb Smirnoff /* Del spool cfg if any. */ 1663b3a8eb9SGleb Smirnoff LIST_FOREACH_SAFE(s, &r->spool_chain, _next, tmp_s) { 1673b3a8eb9SGleb Smirnoff LIST_REMOVE(s, _next); 1683b3a8eb9SGleb Smirnoff free(s, M_IPFW); 1693b3a8eb9SGleb Smirnoff } 1703b3a8eb9SGleb Smirnoff free(r->alink, M_IPFW); 1713b3a8eb9SGleb Smirnoff LIST_REMOVE(r, _next); 1723b3a8eb9SGleb Smirnoff free(r, M_IPFW); 1733b3a8eb9SGleb Smirnoff break; 1743b3a8eb9SGleb Smirnoff default: 1753b3a8eb9SGleb Smirnoff printf("unknown redirect mode: %u\n", r->mode); 1763b3a8eb9SGleb Smirnoff /* XXX - panic?!?!? */ 1773b3a8eb9SGleb Smirnoff break; 1783b3a8eb9SGleb Smirnoff } 1793b3a8eb9SGleb Smirnoff } 1803b3a8eb9SGleb Smirnoff } 1813b3a8eb9SGleb Smirnoff 182*d6164b77SAlexander V. Chernikov static int 1833b3a8eb9SGleb Smirnoff add_redir_spool_cfg(char *buf, struct cfg_nat *ptr) 1843b3a8eb9SGleb Smirnoff { 185*d6164b77SAlexander V. Chernikov struct cfg_redir *r; 186*d6164b77SAlexander V. Chernikov struct cfg_spool *s; 187*d6164b77SAlexander V. Chernikov struct nat44_cfg_redir *ser_r; 188*d6164b77SAlexander V. Chernikov struct nat44_cfg_spool *ser_s; 189*d6164b77SAlexander V. Chernikov 1903b3a8eb9SGleb Smirnoff int cnt, off, i; 1913b3a8eb9SGleb Smirnoff 1923b3a8eb9SGleb Smirnoff for (cnt = 0, off = 0; cnt < ptr->redir_cnt; cnt++) { 193*d6164b77SAlexander V. Chernikov ser_r = (struct nat44_cfg_redir *)&buf[off]; 194*d6164b77SAlexander V. Chernikov r = malloc(sizeof(*r), M_IPFW, M_WAITOK | M_ZERO); 195*d6164b77SAlexander V. Chernikov r->mode = ser_r->mode; 196*d6164b77SAlexander V. Chernikov r->laddr = ser_r->laddr; 197*d6164b77SAlexander V. Chernikov r->paddr = ser_r->paddr; 198*d6164b77SAlexander V. Chernikov r->raddr = ser_r->raddr; 199*d6164b77SAlexander V. Chernikov r->lport = ser_r->lport; 200*d6164b77SAlexander V. Chernikov r->pport = ser_r->pport; 201*d6164b77SAlexander V. Chernikov r->rport = ser_r->rport; 202*d6164b77SAlexander V. Chernikov r->pport_cnt = ser_r->pport_cnt; 203*d6164b77SAlexander V. Chernikov r->rport_cnt = ser_r->rport_cnt; 204*d6164b77SAlexander V. Chernikov r->proto = ser_r->proto; 205*d6164b77SAlexander V. Chernikov r->spool_cnt = ser_r->spool_cnt; 206*d6164b77SAlexander V. Chernikov //memcpy(r, ser_r, SOF_REDIR); 2073b3a8eb9SGleb Smirnoff LIST_INIT(&r->spool_chain); 208*d6164b77SAlexander V. Chernikov off += sizeof(struct nat44_cfg_redir); 2093b3a8eb9SGleb Smirnoff r->alink = malloc(sizeof(struct alias_link *) * r->pport_cnt, 2103b3a8eb9SGleb Smirnoff M_IPFW, M_WAITOK | M_ZERO); 2113b3a8eb9SGleb Smirnoff switch (r->mode) { 212*d6164b77SAlexander V. Chernikov case NAT44_REDIR_ADDR: 2133b3a8eb9SGleb Smirnoff r->alink[0] = LibAliasRedirectAddr(ptr->lib, r->laddr, 2143b3a8eb9SGleb Smirnoff r->paddr); 2153b3a8eb9SGleb Smirnoff break; 216*d6164b77SAlexander V. Chernikov case NAT44_REDIR_PORT: 2173b3a8eb9SGleb Smirnoff for (i = 0 ; i < r->pport_cnt; i++) { 2183b3a8eb9SGleb Smirnoff /* If remotePort is all ports, set it to 0. */ 2193b3a8eb9SGleb Smirnoff u_short remotePortCopy = r->rport + i; 2203b3a8eb9SGleb Smirnoff if (r->rport_cnt == 1 && r->rport == 0) 2213b3a8eb9SGleb Smirnoff remotePortCopy = 0; 2223b3a8eb9SGleb Smirnoff r->alink[i] = LibAliasRedirectPort(ptr->lib, 2233b3a8eb9SGleb Smirnoff r->laddr, htons(r->lport + i), r->raddr, 2243b3a8eb9SGleb Smirnoff htons(remotePortCopy), r->paddr, 2253b3a8eb9SGleb Smirnoff htons(r->pport + i), r->proto); 2263b3a8eb9SGleb Smirnoff if (r->alink[i] == NULL) { 2273b3a8eb9SGleb Smirnoff r->alink[0] = NULL; 2283b3a8eb9SGleb Smirnoff break; 2293b3a8eb9SGleb Smirnoff } 2303b3a8eb9SGleb Smirnoff } 2313b3a8eb9SGleb Smirnoff break; 232*d6164b77SAlexander V. Chernikov case NAT44_REDIR_PROTO: 2333b3a8eb9SGleb Smirnoff r->alink[0] = LibAliasRedirectProto(ptr->lib ,r->laddr, 2343b3a8eb9SGleb Smirnoff r->raddr, r->paddr, r->proto); 2353b3a8eb9SGleb Smirnoff break; 2363b3a8eb9SGleb Smirnoff default: 2373b3a8eb9SGleb Smirnoff printf("unknown redirect mode: %u\n", r->mode); 2383b3a8eb9SGleb Smirnoff break; 2393b3a8eb9SGleb Smirnoff } 240*d6164b77SAlexander V. Chernikov if (r->alink[0] == NULL) { 241*d6164b77SAlexander V. Chernikov printf("LibAliasRedirect* returned NULL\n"); 242*d6164b77SAlexander V. Chernikov return (EINVAL); 243*d6164b77SAlexander V. Chernikov } 2443b3a8eb9SGleb Smirnoff /* LSNAT handling. */ 2453b3a8eb9SGleb Smirnoff for (i = 0; i < r->spool_cnt; i++) { 246*d6164b77SAlexander V. Chernikov ser_s = (struct nat44_cfg_spool *)&buf[off]; 247*d6164b77SAlexander V. Chernikov s = malloc(sizeof(*s), M_IPFW, M_WAITOK | M_ZERO); 248*d6164b77SAlexander V. Chernikov s->addr = ser_s->addr; 249*d6164b77SAlexander V. Chernikov s->port = ser_s->port; 2503b3a8eb9SGleb Smirnoff LibAliasAddServer(ptr->lib, r->alink[0], 2513b3a8eb9SGleb Smirnoff s->addr, htons(s->port)); 252*d6164b77SAlexander V. Chernikov off += sizeof(struct nat44_cfg_spool); 2533b3a8eb9SGleb Smirnoff /* Hook spool entry. */ 2543b3a8eb9SGleb Smirnoff LIST_INSERT_HEAD(&r->spool_chain, s, _next); 2553b3a8eb9SGleb Smirnoff } 2563b3a8eb9SGleb Smirnoff /* And finally hook this redir entry. */ 2573b3a8eb9SGleb Smirnoff LIST_INSERT_HEAD(&ptr->redir_chain, r, _next); 2583b3a8eb9SGleb Smirnoff } 259*d6164b77SAlexander V. Chernikov 260*d6164b77SAlexander V. Chernikov return (0); 2613b3a8eb9SGleb Smirnoff } 2623b3a8eb9SGleb Smirnoff 26310ab2de0SAlexander V. Chernikov /* 26410ab2de0SAlexander V. Chernikov * ipfw_nat - perform mbuf header translation. 26510ab2de0SAlexander V. Chernikov * 26610ab2de0SAlexander V. Chernikov * Note V_layer3_chain has to be locked while calling ipfw_nat() in 26710ab2de0SAlexander V. Chernikov * 'global' operation mode (t == NULL). 26810ab2de0SAlexander V. Chernikov * 26910ab2de0SAlexander V. Chernikov */ 2703b3a8eb9SGleb Smirnoff static int 2713b3a8eb9SGleb Smirnoff ipfw_nat(struct ip_fw_args *args, struct cfg_nat *t, struct mbuf *m) 2723b3a8eb9SGleb Smirnoff { 2733b3a8eb9SGleb Smirnoff struct mbuf *mcl; 2743b3a8eb9SGleb Smirnoff struct ip *ip; 2753b3a8eb9SGleb Smirnoff /* XXX - libalias duct tape */ 2763b3a8eb9SGleb Smirnoff int ldt, retval, found; 2773b3a8eb9SGleb Smirnoff struct ip_fw_chain *chain; 2783b3a8eb9SGleb Smirnoff char *c; 2793b3a8eb9SGleb Smirnoff 2803b3a8eb9SGleb Smirnoff ldt = 0; 2813b3a8eb9SGleb Smirnoff retval = 0; 2823b3a8eb9SGleb Smirnoff mcl = m_megapullup(m, m->m_pkthdr.len); 2833b3a8eb9SGleb Smirnoff if (mcl == NULL) { 2843b3a8eb9SGleb Smirnoff args->m = NULL; 2853b3a8eb9SGleb Smirnoff return (IP_FW_DENY); 2863b3a8eb9SGleb Smirnoff } 2873b3a8eb9SGleb Smirnoff ip = mtod(mcl, struct ip *); 2883b3a8eb9SGleb Smirnoff 2893b3a8eb9SGleb Smirnoff /* 2903b3a8eb9SGleb Smirnoff * XXX - Libalias checksum offload 'duct tape': 2913b3a8eb9SGleb Smirnoff * 2923b3a8eb9SGleb Smirnoff * locally generated packets have only pseudo-header checksum 2933b3a8eb9SGleb Smirnoff * calculated and libalias will break it[1], so mark them for 2943b3a8eb9SGleb Smirnoff * later fix. Moreover there are cases when libalias modifies 2953b3a8eb9SGleb Smirnoff * tcp packet data[2], mark them for later fix too. 2963b3a8eb9SGleb Smirnoff * 2973b3a8eb9SGleb Smirnoff * [1] libalias was never meant to run in kernel, so it does 2983b3a8eb9SGleb Smirnoff * not have any knowledge about checksum offloading, and 2993b3a8eb9SGleb Smirnoff * expects a packet with a full internet checksum. 3003b3a8eb9SGleb Smirnoff * Unfortunately, packets generated locally will have just the 3013b3a8eb9SGleb Smirnoff * pseudo header calculated, and when libalias tries to adjust 3023b3a8eb9SGleb Smirnoff * the checksum it will actually compute a wrong value. 3033b3a8eb9SGleb Smirnoff * 3043b3a8eb9SGleb Smirnoff * [2] when libalias modifies tcp's data content, full TCP 3053b3a8eb9SGleb Smirnoff * checksum has to be recomputed: the problem is that 3063b3a8eb9SGleb Smirnoff * libalias does not have any idea about checksum offloading. 3073b3a8eb9SGleb Smirnoff * To work around this, we do not do checksumming in LibAlias, 3083b3a8eb9SGleb Smirnoff * but only mark the packets in th_x2 field. If we receive a 3093b3a8eb9SGleb Smirnoff * marked packet, we calculate correct checksum for it 3103b3a8eb9SGleb Smirnoff * aware of offloading. Why such a terrible hack instead of 3113b3a8eb9SGleb Smirnoff * recalculating checksum for each packet? 3123b3a8eb9SGleb Smirnoff * Because the previous checksum was not checked! 3133b3a8eb9SGleb Smirnoff * Recalculating checksums for EVERY packet will hide ALL 3143b3a8eb9SGleb Smirnoff * transmission errors. Yes, marked packets still suffer from 3153b3a8eb9SGleb Smirnoff * this problem. But, sigh, natd(8) has this problem, too. 3163b3a8eb9SGleb Smirnoff * 3173b3a8eb9SGleb Smirnoff * TODO: -make libalias mbuf aware (so 3183b3a8eb9SGleb Smirnoff * it can handle delayed checksum and tso) 3193b3a8eb9SGleb Smirnoff */ 3203b3a8eb9SGleb Smirnoff 3213b3a8eb9SGleb Smirnoff if (mcl->m_pkthdr.rcvif == NULL && 3223b3a8eb9SGleb Smirnoff mcl->m_pkthdr.csum_flags & CSUM_DELAY_DATA) 3233b3a8eb9SGleb Smirnoff ldt = 1; 3243b3a8eb9SGleb Smirnoff 3253b3a8eb9SGleb Smirnoff c = mtod(mcl, char *); 3263b3a8eb9SGleb Smirnoff 3273b3a8eb9SGleb Smirnoff /* Check if this is 'global' instance */ 3283b3a8eb9SGleb Smirnoff if (t == NULL) { 3293b3a8eb9SGleb Smirnoff if (args->oif == NULL) { 3303b3a8eb9SGleb Smirnoff /* Wrong direction, skip processing */ 3313b3a8eb9SGleb Smirnoff args->m = mcl; 3323b3a8eb9SGleb Smirnoff return (IP_FW_NAT); 3333b3a8eb9SGleb Smirnoff } 3343b3a8eb9SGleb Smirnoff 3353b3a8eb9SGleb Smirnoff found = 0; 3363b3a8eb9SGleb Smirnoff chain = &V_layer3_chain; 3375d0cd926SAlexander V. Chernikov IPFW_RLOCK_ASSERT(chain); 3383b3a8eb9SGleb Smirnoff /* Check every nat entry... */ 3393b3a8eb9SGleb Smirnoff LIST_FOREACH(t, &chain->nat, _next) { 3403b3a8eb9SGleb Smirnoff if ((t->mode & PKT_ALIAS_SKIP_GLOBAL) != 0) 3413b3a8eb9SGleb Smirnoff continue; 3423b3a8eb9SGleb Smirnoff retval = LibAliasOutTry(t->lib, c, 3433b3a8eb9SGleb Smirnoff mcl->m_len + M_TRAILINGSPACE(mcl), 0); 3443b3a8eb9SGleb Smirnoff if (retval == PKT_ALIAS_OK) { 3453b3a8eb9SGleb Smirnoff /* Nat instance recognises state */ 3463b3a8eb9SGleb Smirnoff found = 1; 3473b3a8eb9SGleb Smirnoff break; 3483b3a8eb9SGleb Smirnoff } 3493b3a8eb9SGleb Smirnoff } 3503b3a8eb9SGleb Smirnoff if (found != 1) { 3513b3a8eb9SGleb Smirnoff /* No instance found, return ignore */ 3523b3a8eb9SGleb Smirnoff args->m = mcl; 3533b3a8eb9SGleb Smirnoff return (IP_FW_NAT); 3543b3a8eb9SGleb Smirnoff } 3553b3a8eb9SGleb Smirnoff } else { 3563b3a8eb9SGleb Smirnoff if (args->oif == NULL) 3573b3a8eb9SGleb Smirnoff retval = LibAliasIn(t->lib, c, 3583b3a8eb9SGleb Smirnoff mcl->m_len + M_TRAILINGSPACE(mcl)); 3593b3a8eb9SGleb Smirnoff else 3603b3a8eb9SGleb Smirnoff retval = LibAliasOut(t->lib, c, 3613b3a8eb9SGleb Smirnoff mcl->m_len + M_TRAILINGSPACE(mcl)); 3623b3a8eb9SGleb Smirnoff } 3633b3a8eb9SGleb Smirnoff 3643b3a8eb9SGleb Smirnoff /* 3653b3a8eb9SGleb Smirnoff * We drop packet when: 3663b3a8eb9SGleb Smirnoff * 1. libalias returns PKT_ALIAS_ERROR; 3673b3a8eb9SGleb Smirnoff * 2. For incoming packets: 3683b3a8eb9SGleb Smirnoff * a) for unresolved fragments; 3693b3a8eb9SGleb Smirnoff * b) libalias returns PKT_ALIAS_IGNORED and 3703b3a8eb9SGleb Smirnoff * PKT_ALIAS_DENY_INCOMING flag is set. 3713b3a8eb9SGleb Smirnoff */ 3723b3a8eb9SGleb Smirnoff if (retval == PKT_ALIAS_ERROR || 3733b3a8eb9SGleb Smirnoff (args->oif == NULL && (retval == PKT_ALIAS_UNRESOLVED_FRAGMENT || 3743b3a8eb9SGleb Smirnoff (retval == PKT_ALIAS_IGNORED && 3753b3a8eb9SGleb Smirnoff (t->mode & PKT_ALIAS_DENY_INCOMING) != 0)))) { 3763b3a8eb9SGleb Smirnoff /* XXX - should i add some logging? */ 3773b3a8eb9SGleb Smirnoff m_free(mcl); 3783b3a8eb9SGleb Smirnoff args->m = NULL; 3793b3a8eb9SGleb Smirnoff return (IP_FW_DENY); 3803b3a8eb9SGleb Smirnoff } 3813b3a8eb9SGleb Smirnoff 3823b3a8eb9SGleb Smirnoff if (retval == PKT_ALIAS_RESPOND) 3833b3a8eb9SGleb Smirnoff mcl->m_flags |= M_SKIP_FIREWALL; 3843b3a8eb9SGleb Smirnoff mcl->m_pkthdr.len = mcl->m_len = ntohs(ip->ip_len); 3853b3a8eb9SGleb Smirnoff 3863b3a8eb9SGleb Smirnoff /* 3873b3a8eb9SGleb Smirnoff * XXX - libalias checksum offload 3883b3a8eb9SGleb Smirnoff * 'duct tape' (see above) 3893b3a8eb9SGleb Smirnoff */ 3903b3a8eb9SGleb Smirnoff 3913b3a8eb9SGleb Smirnoff if ((ip->ip_off & htons(IP_OFFMASK)) == 0 && 3923b3a8eb9SGleb Smirnoff ip->ip_p == IPPROTO_TCP) { 3933b3a8eb9SGleb Smirnoff struct tcphdr *th; 3943b3a8eb9SGleb Smirnoff 3953b3a8eb9SGleb Smirnoff th = (struct tcphdr *)(ip + 1); 3963b3a8eb9SGleb Smirnoff if (th->th_x2) 3973b3a8eb9SGleb Smirnoff ldt = 1; 3983b3a8eb9SGleb Smirnoff } 3993b3a8eb9SGleb Smirnoff 4003b3a8eb9SGleb Smirnoff if (ldt) { 4013b3a8eb9SGleb Smirnoff struct tcphdr *th; 4023b3a8eb9SGleb Smirnoff struct udphdr *uh; 40323e9c6dcSGleb Smirnoff uint16_t ip_len, cksum; 4043b3a8eb9SGleb Smirnoff 40523e9c6dcSGleb Smirnoff ip_len = ntohs(ip->ip_len); 4063b3a8eb9SGleb Smirnoff cksum = in_pseudo(ip->ip_src.s_addr, ip->ip_dst.s_addr, 40723e9c6dcSGleb Smirnoff htons(ip->ip_p + ip_len - (ip->ip_hl << 2))); 4083b3a8eb9SGleb Smirnoff 4093b3a8eb9SGleb Smirnoff switch (ip->ip_p) { 4103b3a8eb9SGleb Smirnoff case IPPROTO_TCP: 4113b3a8eb9SGleb Smirnoff th = (struct tcphdr *)(ip + 1); 4123b3a8eb9SGleb Smirnoff /* 4133b3a8eb9SGleb Smirnoff * Maybe it was set in 4143b3a8eb9SGleb Smirnoff * libalias... 4153b3a8eb9SGleb Smirnoff */ 4163b3a8eb9SGleb Smirnoff th->th_x2 = 0; 4173b3a8eb9SGleb Smirnoff th->th_sum = cksum; 4183b3a8eb9SGleb Smirnoff mcl->m_pkthdr.csum_data = 4193b3a8eb9SGleb Smirnoff offsetof(struct tcphdr, th_sum); 4203b3a8eb9SGleb Smirnoff break; 4213b3a8eb9SGleb Smirnoff case IPPROTO_UDP: 4223b3a8eb9SGleb Smirnoff uh = (struct udphdr *)(ip + 1); 4233b3a8eb9SGleb Smirnoff uh->uh_sum = cksum; 4243b3a8eb9SGleb Smirnoff mcl->m_pkthdr.csum_data = 4253b3a8eb9SGleb Smirnoff offsetof(struct udphdr, uh_sum); 4263b3a8eb9SGleb Smirnoff break; 4273b3a8eb9SGleb Smirnoff } 4283b3a8eb9SGleb Smirnoff /* No hw checksum offloading: do it ourselves */ 4293b3a8eb9SGleb Smirnoff if ((mcl->m_pkthdr.csum_flags & CSUM_DELAY_DATA) == 0) { 4303b3a8eb9SGleb Smirnoff in_delayed_cksum(mcl); 4313b3a8eb9SGleb Smirnoff mcl->m_pkthdr.csum_flags &= ~CSUM_DELAY_DATA; 4323b3a8eb9SGleb Smirnoff } 4333b3a8eb9SGleb Smirnoff } 4343b3a8eb9SGleb Smirnoff args->m = mcl; 4353b3a8eb9SGleb Smirnoff return (IP_FW_NAT); 4363b3a8eb9SGleb Smirnoff } 4373b3a8eb9SGleb Smirnoff 4383b3a8eb9SGleb Smirnoff static struct cfg_nat * 4393b3a8eb9SGleb Smirnoff lookup_nat(struct nat_list *l, int nat_id) 4403b3a8eb9SGleb Smirnoff { 4413b3a8eb9SGleb Smirnoff struct cfg_nat *res; 4423b3a8eb9SGleb Smirnoff 4433b3a8eb9SGleb Smirnoff LIST_FOREACH(res, l, _next) { 4443b3a8eb9SGleb Smirnoff if (res->id == nat_id) 4453b3a8eb9SGleb Smirnoff break; 4463b3a8eb9SGleb Smirnoff } 4473b3a8eb9SGleb Smirnoff return res; 4483b3a8eb9SGleb Smirnoff } 4493b3a8eb9SGleb Smirnoff 450*d6164b77SAlexander V. Chernikov static struct cfg_nat * 451*d6164b77SAlexander V. Chernikov lookup_nat_name(struct nat_list *l, char *name) 4523b3a8eb9SGleb Smirnoff { 453*d6164b77SAlexander V. Chernikov struct cfg_nat *res; 454*d6164b77SAlexander V. Chernikov int id; 455*d6164b77SAlexander V. Chernikov char *errptr; 4563b3a8eb9SGleb Smirnoff 457*d6164b77SAlexander V. Chernikov id = strtol(name, &errptr, 10); 458*d6164b77SAlexander V. Chernikov if (id == 0 || *errptr != '\0') 459*d6164b77SAlexander V. Chernikov return (NULL); 4603b3a8eb9SGleb Smirnoff 461*d6164b77SAlexander V. Chernikov LIST_FOREACH(res, l, _next) { 462*d6164b77SAlexander V. Chernikov if (res->id == id) 463*d6164b77SAlexander V. Chernikov break; 4643b3a8eb9SGleb Smirnoff } 465*d6164b77SAlexander V. Chernikov return (res); 466*d6164b77SAlexander V. Chernikov } 467*d6164b77SAlexander V. Chernikov 468*d6164b77SAlexander V. Chernikov /* IP_FW3 configuration routines */ 469*d6164b77SAlexander V. Chernikov 470*d6164b77SAlexander V. Chernikov static void 471*d6164b77SAlexander V. Chernikov nat44_config(struct ip_fw_chain *chain, struct nat44_cfg_nat *ucfg) 472*d6164b77SAlexander V. Chernikov { 473*d6164b77SAlexander V. Chernikov struct cfg_nat *ptr, *tcfg; 474*d6164b77SAlexander V. Chernikov int gencnt; 4753b3a8eb9SGleb Smirnoff 4763b3a8eb9SGleb Smirnoff /* 4773b3a8eb9SGleb Smirnoff * Find/create nat rule. 4783b3a8eb9SGleb Smirnoff */ 479*d6164b77SAlexander V. Chernikov IPFW_UH_WLOCK(chain); 4803b3a8eb9SGleb Smirnoff gencnt = chain->gencnt; 481*d6164b77SAlexander V. Chernikov ptr = lookup_nat_name(&chain->nat, ucfg->name); 4823b3a8eb9SGleb Smirnoff if (ptr == NULL) { 483*d6164b77SAlexander V. Chernikov IPFW_UH_WUNLOCK(chain); 4843b3a8eb9SGleb Smirnoff /* New rule: allocate and init new instance. */ 4853b3a8eb9SGleb Smirnoff ptr = malloc(sizeof(struct cfg_nat), M_IPFW, M_WAITOK | M_ZERO); 4863b3a8eb9SGleb Smirnoff ptr->lib = LibAliasInit(NULL); 4873b3a8eb9SGleb Smirnoff LIST_INIT(&ptr->redir_chain); 4883b3a8eb9SGleb Smirnoff } else { 4893b3a8eb9SGleb Smirnoff /* Entry already present: temporarily unhook it. */ 490*d6164b77SAlexander V. Chernikov IPFW_WLOCK(chain); 4913b3a8eb9SGleb Smirnoff LIST_REMOVE(ptr, _next); 492*d6164b77SAlexander V. Chernikov flush_nat_ptrs(chain, ptr->id); 4933b3a8eb9SGleb Smirnoff IPFW_WUNLOCK(chain); 494*d6164b77SAlexander V. Chernikov IPFW_UH_WUNLOCK(chain); 4953b3a8eb9SGleb Smirnoff } 4963b3a8eb9SGleb Smirnoff 4973b3a8eb9SGleb Smirnoff /* 498*d6164b77SAlexander V. Chernikov * Basic nat (re)configuration. 4993b3a8eb9SGleb Smirnoff */ 500*d6164b77SAlexander V. Chernikov ptr->id = strtol(ucfg->name, NULL, 10); 5013b3a8eb9SGleb Smirnoff /* 5023b3a8eb9SGleb Smirnoff * XXX - what if this rule doesn't nat any ip and just 5033b3a8eb9SGleb Smirnoff * redirect? 5043b3a8eb9SGleb Smirnoff * do we set aliasaddress to 0.0.0.0? 5053b3a8eb9SGleb Smirnoff */ 506*d6164b77SAlexander V. Chernikov ptr->ip = ucfg->ip; 507*d6164b77SAlexander V. Chernikov ptr->redir_cnt = ucfg->redir_cnt; 508*d6164b77SAlexander V. Chernikov ptr->mode = ucfg->mode; 509*d6164b77SAlexander V. Chernikov strlcpy(ptr->if_name, ucfg->if_name, sizeof(ptr->if_name)); 510*d6164b77SAlexander V. Chernikov LibAliasSetMode(ptr->lib, ptr->mode, ~0); 5113b3a8eb9SGleb Smirnoff LibAliasSetAddress(ptr->lib, ptr->ip); 5123b3a8eb9SGleb Smirnoff 5133b3a8eb9SGleb Smirnoff /* 5143b3a8eb9SGleb Smirnoff * Redir and LSNAT configuration. 5153b3a8eb9SGleb Smirnoff */ 5163b3a8eb9SGleb Smirnoff /* Delete old cfgs. */ 5173b3a8eb9SGleb Smirnoff del_redir_spool_cfg(ptr, &ptr->redir_chain); 5183b3a8eb9SGleb Smirnoff /* Add new entries. */ 519*d6164b77SAlexander V. Chernikov add_redir_spool_cfg((char *)(ucfg + 1), ptr); 520*d6164b77SAlexander V. Chernikov IPFW_UH_WLOCK(chain); 5213b3a8eb9SGleb Smirnoff 5223b3a8eb9SGleb Smirnoff /* Extra check to avoid race with another ipfw_nat_cfg() */ 523*d6164b77SAlexander V. Chernikov tcfg = NULL; 524*d6164b77SAlexander V. Chernikov if (gencnt != chain->gencnt) 525*d6164b77SAlexander V. Chernikov tcfg = lookup_nat_name(&chain->nat, ucfg->name); 526*d6164b77SAlexander V. Chernikov IPFW_WLOCK(chain); 527*d6164b77SAlexander V. Chernikov if (tcfg != NULL) 528*d6164b77SAlexander V. Chernikov LIST_REMOVE(tcfg, _next); 5293b3a8eb9SGleb Smirnoff LIST_INSERT_HEAD(&chain->nat, ptr, _next); 5303b3a8eb9SGleb Smirnoff IPFW_WUNLOCK(chain); 531*d6164b77SAlexander V. Chernikov chain->gencnt++; 532*d6164b77SAlexander V. Chernikov 533*d6164b77SAlexander V. Chernikov IPFW_UH_WUNLOCK(chain); 534*d6164b77SAlexander V. Chernikov 535*d6164b77SAlexander V. Chernikov if (tcfg != NULL) 536*d6164b77SAlexander V. Chernikov free(tcfg, M_IPFW); 537*d6164b77SAlexander V. Chernikov } 538*d6164b77SAlexander V. Chernikov 539*d6164b77SAlexander V. Chernikov /* 540*d6164b77SAlexander V. Chernikov * Creates/configure nat44 instance 541*d6164b77SAlexander V. Chernikov * Data layout (v0)(current): 542*d6164b77SAlexander V. Chernikov * Request: [ ipfw_obj_header nat44_cfg_nat .. ] 543*d6164b77SAlexander V. Chernikov * 544*d6164b77SAlexander V. Chernikov * Returns 0 on success 545*d6164b77SAlexander V. Chernikov */ 546*d6164b77SAlexander V. Chernikov static int 547*d6164b77SAlexander V. Chernikov nat44_cfg(struct ip_fw_chain *chain, ip_fw3_opheader *op3, 548*d6164b77SAlexander V. Chernikov struct sockopt_data *sd) 549*d6164b77SAlexander V. Chernikov { 550*d6164b77SAlexander V. Chernikov ipfw_obj_header *oh; 551*d6164b77SAlexander V. Chernikov struct nat44_cfg_nat *ucfg; 552*d6164b77SAlexander V. Chernikov int id; 553*d6164b77SAlexander V. Chernikov size_t read; 554*d6164b77SAlexander V. Chernikov char *errptr; 555*d6164b77SAlexander V. Chernikov 556*d6164b77SAlexander V. Chernikov /* Check minimum header size */ 557*d6164b77SAlexander V. Chernikov if (sd->valsize < (sizeof(*oh) + sizeof(*ucfg))) 558*d6164b77SAlexander V. Chernikov return (EINVAL); 559*d6164b77SAlexander V. Chernikov 560*d6164b77SAlexander V. Chernikov oh = (ipfw_obj_header *)sd->kbuf; 561*d6164b77SAlexander V. Chernikov 562*d6164b77SAlexander V. Chernikov /* Basic length checks for TLVs */ 563*d6164b77SAlexander V. Chernikov if (oh->ntlv.head.length != sizeof(oh->ntlv)) 564*d6164b77SAlexander V. Chernikov return (EINVAL); 565*d6164b77SAlexander V. Chernikov 566*d6164b77SAlexander V. Chernikov ucfg = (struct nat44_cfg_nat *)(oh + 1); 567*d6164b77SAlexander V. Chernikov 568*d6164b77SAlexander V. Chernikov /* Check if name is properly terminated and looks like number */ 569*d6164b77SAlexander V. Chernikov if (strnlen(ucfg->name, sizeof(ucfg->name)) == sizeof(ucfg->name)) 570*d6164b77SAlexander V. Chernikov return (EINVAL); 571*d6164b77SAlexander V. Chernikov id = strtol(ucfg->name, &errptr, 10); 572*d6164b77SAlexander V. Chernikov if (id == 0 || *errptr != '\0') 573*d6164b77SAlexander V. Chernikov return (EINVAL); 574*d6164b77SAlexander V. Chernikov 575*d6164b77SAlexander V. Chernikov read = sizeof(*oh) + sizeof(*ucfg); 576*d6164b77SAlexander V. Chernikov /* Check number of redirs */ 577*d6164b77SAlexander V. Chernikov if (sd->valsize < read + ucfg->redir_cnt*sizeof(struct nat44_cfg_redir)) 578*d6164b77SAlexander V. Chernikov return (EINVAL); 579*d6164b77SAlexander V. Chernikov 580*d6164b77SAlexander V. Chernikov nat44_config(chain, ucfg); 581*d6164b77SAlexander V. Chernikov return (0); 582*d6164b77SAlexander V. Chernikov } 583*d6164b77SAlexander V. Chernikov 584*d6164b77SAlexander V. Chernikov /* 585*d6164b77SAlexander V. Chernikov * Destroys given nat instances. 586*d6164b77SAlexander V. Chernikov * Data layout (v0)(current): 587*d6164b77SAlexander V. Chernikov * Request: [ ipfw_obj_header ] 588*d6164b77SAlexander V. Chernikov * 589*d6164b77SAlexander V. Chernikov * Returns 0 on success 590*d6164b77SAlexander V. Chernikov */ 591*d6164b77SAlexander V. Chernikov static int 592*d6164b77SAlexander V. Chernikov nat44_destroy(struct ip_fw_chain *chain, ip_fw3_opheader *op3, 593*d6164b77SAlexander V. Chernikov struct sockopt_data *sd) 594*d6164b77SAlexander V. Chernikov { 595*d6164b77SAlexander V. Chernikov ipfw_obj_header *oh; 596*d6164b77SAlexander V. Chernikov struct cfg_nat *ptr; 597*d6164b77SAlexander V. Chernikov ipfw_obj_ntlv *ntlv; 598*d6164b77SAlexander V. Chernikov 599*d6164b77SAlexander V. Chernikov /* Check minimum header size */ 600*d6164b77SAlexander V. Chernikov if (sd->valsize < sizeof(*oh)) 601*d6164b77SAlexander V. Chernikov return (EINVAL); 602*d6164b77SAlexander V. Chernikov 603*d6164b77SAlexander V. Chernikov oh = (ipfw_obj_header *)sd->kbuf; 604*d6164b77SAlexander V. Chernikov 605*d6164b77SAlexander V. Chernikov /* Basic length checks for TLVs */ 606*d6164b77SAlexander V. Chernikov if (oh->ntlv.head.length != sizeof(oh->ntlv)) 607*d6164b77SAlexander V. Chernikov return (EINVAL); 608*d6164b77SAlexander V. Chernikov 609*d6164b77SAlexander V. Chernikov ntlv = &oh->ntlv; 610*d6164b77SAlexander V. Chernikov /* Check if name is properly terminated */ 611*d6164b77SAlexander V. Chernikov if (strnlen(ntlv->name, sizeof(ntlv->name)) == sizeof(ntlv->name)) 612*d6164b77SAlexander V. Chernikov return (EINVAL); 613*d6164b77SAlexander V. Chernikov 614*d6164b77SAlexander V. Chernikov IPFW_UH_WLOCK(chain); 615*d6164b77SAlexander V. Chernikov ptr = lookup_nat_name(&chain->nat, ntlv->name); 616*d6164b77SAlexander V. Chernikov if (ptr == NULL) { 617*d6164b77SAlexander V. Chernikov IPFW_UH_WUNLOCK(chain); 618*d6164b77SAlexander V. Chernikov return (ESRCH); 619*d6164b77SAlexander V. Chernikov } 620*d6164b77SAlexander V. Chernikov IPFW_WLOCK(chain); 621*d6164b77SAlexander V. Chernikov LIST_REMOVE(ptr, _next); 622*d6164b77SAlexander V. Chernikov flush_nat_ptrs(chain, ptr->id); 623*d6164b77SAlexander V. Chernikov IPFW_WUNLOCK(chain); 624*d6164b77SAlexander V. Chernikov IPFW_UH_WUNLOCK(chain); 625*d6164b77SAlexander V. Chernikov 626*d6164b77SAlexander V. Chernikov del_redir_spool_cfg(ptr, &ptr->redir_chain); 627*d6164b77SAlexander V. Chernikov LibAliasUninit(ptr->lib); 628*d6164b77SAlexander V. Chernikov free(ptr, M_IPFW); 629*d6164b77SAlexander V. Chernikov 630*d6164b77SAlexander V. Chernikov return (0); 631*d6164b77SAlexander V. Chernikov } 632*d6164b77SAlexander V. Chernikov 633*d6164b77SAlexander V. Chernikov static void 634*d6164b77SAlexander V. Chernikov export_nat_cfg(struct cfg_nat *ptr, struct nat44_cfg_nat *ucfg) 635*d6164b77SAlexander V. Chernikov { 636*d6164b77SAlexander V. Chernikov 637*d6164b77SAlexander V. Chernikov snprintf(ucfg->name, sizeof(ucfg->name), "%d", ptr->id); 638*d6164b77SAlexander V. Chernikov ucfg->ip = ptr->ip; 639*d6164b77SAlexander V. Chernikov ucfg->redir_cnt = ptr->redir_cnt; 640*d6164b77SAlexander V. Chernikov ucfg->mode = ptr->mode; 641*d6164b77SAlexander V. Chernikov strlcpy(ucfg->if_name, ptr->if_name, sizeof(ucfg->if_name)); 642*d6164b77SAlexander V. Chernikov } 643*d6164b77SAlexander V. Chernikov 644*d6164b77SAlexander V. Chernikov /* 645*d6164b77SAlexander V. Chernikov * Gets config for given nat instance 646*d6164b77SAlexander V. Chernikov * Data layout (v0)(current): 647*d6164b77SAlexander V. Chernikov * Request: [ ipfw_obj_header nat44_cfg_nat .. ] 648*d6164b77SAlexander V. Chernikov * 649*d6164b77SAlexander V. Chernikov * Returns 0 on success 650*d6164b77SAlexander V. Chernikov */ 651*d6164b77SAlexander V. Chernikov static int 652*d6164b77SAlexander V. Chernikov nat44_get_cfg(struct ip_fw_chain *chain, ip_fw3_opheader *op3, 653*d6164b77SAlexander V. Chernikov struct sockopt_data *sd) 654*d6164b77SAlexander V. Chernikov { 655*d6164b77SAlexander V. Chernikov ipfw_obj_header *oh; 656*d6164b77SAlexander V. Chernikov struct nat44_cfg_nat *ucfg; 657*d6164b77SAlexander V. Chernikov struct cfg_nat *ptr; 658*d6164b77SAlexander V. Chernikov struct cfg_redir *r; 659*d6164b77SAlexander V. Chernikov struct cfg_spool *s; 660*d6164b77SAlexander V. Chernikov struct nat44_cfg_redir *ser_r; 661*d6164b77SAlexander V. Chernikov struct nat44_cfg_spool *ser_s; 662*d6164b77SAlexander V. Chernikov size_t sz; 663*d6164b77SAlexander V. Chernikov 664*d6164b77SAlexander V. Chernikov sz = sizeof(*oh) + sizeof(*ucfg); 665*d6164b77SAlexander V. Chernikov /* Check minimum header size */ 666*d6164b77SAlexander V. Chernikov if (sd->valsize < sz) 667*d6164b77SAlexander V. Chernikov return (EINVAL); 668*d6164b77SAlexander V. Chernikov 669*d6164b77SAlexander V. Chernikov oh = (struct _ipfw_obj_header *)ipfw_get_sopt_header(sd, sz); 670*d6164b77SAlexander V. Chernikov 671*d6164b77SAlexander V. Chernikov /* Basic length checks for TLVs */ 672*d6164b77SAlexander V. Chernikov if (oh->ntlv.head.length != sizeof(oh->ntlv)) 673*d6164b77SAlexander V. Chernikov return (EINVAL); 674*d6164b77SAlexander V. Chernikov 675*d6164b77SAlexander V. Chernikov ucfg = (struct nat44_cfg_nat *)(oh + 1); 676*d6164b77SAlexander V. Chernikov 677*d6164b77SAlexander V. Chernikov /* Check if name is properly terminated */ 678*d6164b77SAlexander V. Chernikov if (strnlen(ucfg->name, sizeof(ucfg->name)) == sizeof(ucfg->name)) 679*d6164b77SAlexander V. Chernikov return (EINVAL); 680*d6164b77SAlexander V. Chernikov 681*d6164b77SAlexander V. Chernikov IPFW_UH_RLOCK(chain); 682*d6164b77SAlexander V. Chernikov ptr = lookup_nat_name(&chain->nat, ucfg->name); 683*d6164b77SAlexander V. Chernikov if (ptr == NULL) { 684*d6164b77SAlexander V. Chernikov IPFW_UH_RUNLOCK(chain); 685*d6164b77SAlexander V. Chernikov return (ESRCH); 686*d6164b77SAlexander V. Chernikov } 687*d6164b77SAlexander V. Chernikov 688*d6164b77SAlexander V. Chernikov export_nat_cfg(ptr, ucfg); 689*d6164b77SAlexander V. Chernikov 690*d6164b77SAlexander V. Chernikov /* Estimate memory amount */ 691*d6164b77SAlexander V. Chernikov sz = sizeof(struct nat44_cfg_nat); 692*d6164b77SAlexander V. Chernikov LIST_FOREACH(r, &ptr->redir_chain, _next) { 693*d6164b77SAlexander V. Chernikov sz += sizeof(struct nat44_cfg_redir); 694*d6164b77SAlexander V. Chernikov LIST_FOREACH(s, &r->spool_chain, _next) 695*d6164b77SAlexander V. Chernikov sz += sizeof(struct nat44_cfg_spool); 696*d6164b77SAlexander V. Chernikov } 697*d6164b77SAlexander V. Chernikov 698*d6164b77SAlexander V. Chernikov ucfg->size = sz; 699*d6164b77SAlexander V. Chernikov if (sd->valsize < sz + sizeof(*oh)) { 700*d6164b77SAlexander V. Chernikov 701*d6164b77SAlexander V. Chernikov /* 702*d6164b77SAlexander V. Chernikov * Submitted buffer size is not enough. 703*d6164b77SAlexander V. Chernikov * WE've already filled in @ucfg structure with 704*d6164b77SAlexander V. Chernikov * relevant info including size, so we 705*d6164b77SAlexander V. Chernikov * can return. Buffer will be flushed automatically. 706*d6164b77SAlexander V. Chernikov */ 707*d6164b77SAlexander V. Chernikov IPFW_UH_RUNLOCK(chain); 708*d6164b77SAlexander V. Chernikov return (ENOMEM); 709*d6164b77SAlexander V. Chernikov } 710*d6164b77SAlexander V. Chernikov 711*d6164b77SAlexander V. Chernikov /* Size OK, let's copy data */ 712*d6164b77SAlexander V. Chernikov LIST_FOREACH(r, &ptr->redir_chain, _next) { 713*d6164b77SAlexander V. Chernikov ser_r = (struct nat44_cfg_redir *)ipfw_get_sopt_space(sd, 714*d6164b77SAlexander V. Chernikov sizeof(*ser_r)); 715*d6164b77SAlexander V. Chernikov ser_r->mode = r->mode; 716*d6164b77SAlexander V. Chernikov ser_r->laddr = r->laddr; 717*d6164b77SAlexander V. Chernikov ser_r->paddr = r->paddr; 718*d6164b77SAlexander V. Chernikov ser_r->raddr = r->raddr; 719*d6164b77SAlexander V. Chernikov ser_r->lport = r->lport; 720*d6164b77SAlexander V. Chernikov ser_r->pport = r->pport; 721*d6164b77SAlexander V. Chernikov ser_r->rport = r->rport; 722*d6164b77SAlexander V. Chernikov ser_r->pport_cnt = r->pport_cnt; 723*d6164b77SAlexander V. Chernikov ser_r->rport_cnt = r->rport_cnt; 724*d6164b77SAlexander V. Chernikov ser_r->proto = r->proto; 725*d6164b77SAlexander V. Chernikov ser_r->spool_cnt = r->spool_cnt; 726*d6164b77SAlexander V. Chernikov 727*d6164b77SAlexander V. Chernikov LIST_FOREACH(s, &r->spool_chain, _next) { 728*d6164b77SAlexander V. Chernikov ser_s = (struct nat44_cfg_spool *)ipfw_get_sopt_space( 729*d6164b77SAlexander V. Chernikov sd, sizeof(*ser_s)); 730*d6164b77SAlexander V. Chernikov 731*d6164b77SAlexander V. Chernikov ser_s->addr = s->addr; 732*d6164b77SAlexander V. Chernikov ser_s->port = s->port; 733*d6164b77SAlexander V. Chernikov } 734*d6164b77SAlexander V. Chernikov } 735*d6164b77SAlexander V. Chernikov 736*d6164b77SAlexander V. Chernikov IPFW_UH_RUNLOCK(chain); 737*d6164b77SAlexander V. Chernikov 738*d6164b77SAlexander V. Chernikov return (0); 739*d6164b77SAlexander V. Chernikov } 740*d6164b77SAlexander V. Chernikov 741*d6164b77SAlexander V. Chernikov /* 742*d6164b77SAlexander V. Chernikov * Lists all nat44 instances currently available in kernel. 743*d6164b77SAlexander V. Chernikov * Data layout (v0)(current): 744*d6164b77SAlexander V. Chernikov * Request: [ ipfw_obj_lheader ] 745*d6164b77SAlexander V. Chernikov * Reply: [ ipfw_obj_lheader nat44_cfg_nat x N ] 746*d6164b77SAlexander V. Chernikov * 747*d6164b77SAlexander V. Chernikov * Returns 0 on success 748*d6164b77SAlexander V. Chernikov */ 749*d6164b77SAlexander V. Chernikov static int 750*d6164b77SAlexander V. Chernikov nat44_list_nat(struct ip_fw_chain *chain, ip_fw3_opheader *op3, 751*d6164b77SAlexander V. Chernikov struct sockopt_data *sd) 752*d6164b77SAlexander V. Chernikov { 753*d6164b77SAlexander V. Chernikov ipfw_obj_lheader *olh; 754*d6164b77SAlexander V. Chernikov struct nat44_cfg_nat *ucfg; 755*d6164b77SAlexander V. Chernikov struct cfg_nat *ptr; 756*d6164b77SAlexander V. Chernikov int nat_count; 757*d6164b77SAlexander V. Chernikov 758*d6164b77SAlexander V. Chernikov /* Check minimum header size */ 759*d6164b77SAlexander V. Chernikov if (sd->valsize < sizeof(ipfw_obj_lheader)) 760*d6164b77SAlexander V. Chernikov return (EINVAL); 761*d6164b77SAlexander V. Chernikov 762*d6164b77SAlexander V. Chernikov olh = (ipfw_obj_lheader *)ipfw_get_sopt_header(sd, sizeof(*olh)); 763*d6164b77SAlexander V. Chernikov IPFW_UH_RLOCK(chain); 764*d6164b77SAlexander V. Chernikov nat_count = 0; 765*d6164b77SAlexander V. Chernikov LIST_FOREACH(ptr, &chain->nat, _next) 766*d6164b77SAlexander V. Chernikov nat_count++; 767*d6164b77SAlexander V. Chernikov 768*d6164b77SAlexander V. Chernikov olh->count = nat_count; 769*d6164b77SAlexander V. Chernikov olh->objsize = sizeof(struct nat44_cfg_nat); 770*d6164b77SAlexander V. Chernikov olh->size = sizeof(*olh) + olh->count * olh->objsize; 771*d6164b77SAlexander V. Chernikov 772*d6164b77SAlexander V. Chernikov if (sd->valsize < olh->size) { 773*d6164b77SAlexander V. Chernikov IPFW_UH_RUNLOCK(chain); 774*d6164b77SAlexander V. Chernikov return (ENOMEM); 775*d6164b77SAlexander V. Chernikov } 776*d6164b77SAlexander V. Chernikov 777*d6164b77SAlexander V. Chernikov LIST_FOREACH(ptr, &chain->nat, _next) { 778*d6164b77SAlexander V. Chernikov ucfg = (struct nat44_cfg_nat *)ipfw_get_sopt_space(sd, 779*d6164b77SAlexander V. Chernikov sizeof(*ucfg)); 780*d6164b77SAlexander V. Chernikov export_nat_cfg(ptr, ucfg); 781*d6164b77SAlexander V. Chernikov } 782*d6164b77SAlexander V. Chernikov 783*d6164b77SAlexander V. Chernikov IPFW_UH_RUNLOCK(chain); 784*d6164b77SAlexander V. Chernikov 785*d6164b77SAlexander V. Chernikov return (0); 786*d6164b77SAlexander V. Chernikov } 787*d6164b77SAlexander V. Chernikov 788*d6164b77SAlexander V. Chernikov /* 789*d6164b77SAlexander V. Chernikov * Gets log for given nat instance 790*d6164b77SAlexander V. Chernikov * Data layout (v0)(current): 791*d6164b77SAlexander V. Chernikov * Request: [ ipfw_obj_header nat44_cfg_nat ] 792*d6164b77SAlexander V. Chernikov * Reply: [ ipfw_obj_header nat44_cfg_nat LOGBUFFER ] 793*d6164b77SAlexander V. Chernikov * 794*d6164b77SAlexander V. Chernikov * Returns 0 on success 795*d6164b77SAlexander V. Chernikov */ 796*d6164b77SAlexander V. Chernikov static int 797*d6164b77SAlexander V. Chernikov nat44_get_log(struct ip_fw_chain *chain, ip_fw3_opheader *op3, 798*d6164b77SAlexander V. Chernikov struct sockopt_data *sd) 799*d6164b77SAlexander V. Chernikov { 800*d6164b77SAlexander V. Chernikov ipfw_obj_header *oh; 801*d6164b77SAlexander V. Chernikov struct nat44_cfg_nat *ucfg; 802*d6164b77SAlexander V. Chernikov struct cfg_nat *ptr; 803*d6164b77SAlexander V. Chernikov void *pbuf; 804*d6164b77SAlexander V. Chernikov size_t sz; 805*d6164b77SAlexander V. Chernikov 806*d6164b77SAlexander V. Chernikov sz = sizeof(*oh) + sizeof(*ucfg); 807*d6164b77SAlexander V. Chernikov /* Check minimum header size */ 808*d6164b77SAlexander V. Chernikov if (sd->valsize < sz) 809*d6164b77SAlexander V. Chernikov return (EINVAL); 810*d6164b77SAlexander V. Chernikov 811*d6164b77SAlexander V. Chernikov oh = (struct _ipfw_obj_header *)ipfw_get_sopt_header(sd, sz); 812*d6164b77SAlexander V. Chernikov 813*d6164b77SAlexander V. Chernikov /* Basic length checks for TLVs */ 814*d6164b77SAlexander V. Chernikov if (oh->ntlv.head.length != sizeof(oh->ntlv)) 815*d6164b77SAlexander V. Chernikov return (EINVAL); 816*d6164b77SAlexander V. Chernikov 817*d6164b77SAlexander V. Chernikov ucfg = (struct nat44_cfg_nat *)(oh + 1); 818*d6164b77SAlexander V. Chernikov 819*d6164b77SAlexander V. Chernikov /* Check if name is properly terminated */ 820*d6164b77SAlexander V. Chernikov if (strnlen(ucfg->name, sizeof(ucfg->name)) == sizeof(ucfg->name)) 821*d6164b77SAlexander V. Chernikov return (EINVAL); 822*d6164b77SAlexander V. Chernikov 823*d6164b77SAlexander V. Chernikov IPFW_UH_RLOCK(chain); 824*d6164b77SAlexander V. Chernikov ptr = lookup_nat_name(&chain->nat, ucfg->name); 825*d6164b77SAlexander V. Chernikov if (ptr == NULL) { 826*d6164b77SAlexander V. Chernikov IPFW_UH_RUNLOCK(chain); 827*d6164b77SAlexander V. Chernikov return (ESRCH); 828*d6164b77SAlexander V. Chernikov } 829*d6164b77SAlexander V. Chernikov 830*d6164b77SAlexander V. Chernikov if (ptr->lib->logDesc == NULL) { 831*d6164b77SAlexander V. Chernikov IPFW_UH_RUNLOCK(chain); 832*d6164b77SAlexander V. Chernikov return (ENOENT); 833*d6164b77SAlexander V. Chernikov } 834*d6164b77SAlexander V. Chernikov 835*d6164b77SAlexander V. Chernikov export_nat_cfg(ptr, ucfg); 836*d6164b77SAlexander V. Chernikov 837*d6164b77SAlexander V. Chernikov /* Estimate memory amount */ 838*d6164b77SAlexander V. Chernikov ucfg->size = sizeof(struct nat44_cfg_nat) + LIBALIAS_BUF_SIZE; 839*d6164b77SAlexander V. Chernikov if (sd->valsize < sz + sizeof(*oh)) { 840*d6164b77SAlexander V. Chernikov 841*d6164b77SAlexander V. Chernikov /* 842*d6164b77SAlexander V. Chernikov * Submitted buffer size is not enough. 843*d6164b77SAlexander V. Chernikov * WE've already filled in @ucfg structure with 844*d6164b77SAlexander V. Chernikov * relevant info including size, so we 845*d6164b77SAlexander V. Chernikov * can return. Buffer will be flushed automatically. 846*d6164b77SAlexander V. Chernikov */ 847*d6164b77SAlexander V. Chernikov IPFW_UH_RUNLOCK(chain); 848*d6164b77SAlexander V. Chernikov return (ENOMEM); 849*d6164b77SAlexander V. Chernikov } 850*d6164b77SAlexander V. Chernikov 851*d6164b77SAlexander V. Chernikov pbuf = (void *)ipfw_get_sopt_space(sd, LIBALIAS_BUF_SIZE); 852*d6164b77SAlexander V. Chernikov memcpy(pbuf, ptr->lib->logDesc, LIBALIAS_BUF_SIZE); 853*d6164b77SAlexander V. Chernikov 854*d6164b77SAlexander V. Chernikov IPFW_UH_RUNLOCK(chain); 855*d6164b77SAlexander V. Chernikov 856*d6164b77SAlexander V. Chernikov return (0); 857*d6164b77SAlexander V. Chernikov } 858*d6164b77SAlexander V. Chernikov 859*d6164b77SAlexander V. Chernikov static struct ipfw_sopt_handler scodes[] = { 860*d6164b77SAlexander V. Chernikov { IP_FW_NAT44_XCONFIG, 0, HDIR_SET, nat44_cfg }, 861*d6164b77SAlexander V. Chernikov { IP_FW_NAT44_DESTROY, 0, HDIR_SET, nat44_destroy }, 862*d6164b77SAlexander V. Chernikov { IP_FW_NAT44_XGETCONFIG, 0, HDIR_GET, nat44_get_cfg }, 863*d6164b77SAlexander V. Chernikov { IP_FW_NAT44_LIST_NAT, 0, HDIR_GET, nat44_list_nat }, 864*d6164b77SAlexander V. Chernikov { IP_FW_NAT44_XGETLOG, 0, HDIR_GET, nat44_get_log }, 865*d6164b77SAlexander V. Chernikov }; 866*d6164b77SAlexander V. Chernikov 867*d6164b77SAlexander V. Chernikov 868*d6164b77SAlexander V. Chernikov /* 869*d6164b77SAlexander V. Chernikov * Legacy configuration routines 870*d6164b77SAlexander V. Chernikov */ 871*d6164b77SAlexander V. Chernikov 872*d6164b77SAlexander V. Chernikov struct cfg_spool_legacy { 873*d6164b77SAlexander V. Chernikov LIST_ENTRY(cfg_spool_legacy) _next; 874*d6164b77SAlexander V. Chernikov struct in_addr addr; 875*d6164b77SAlexander V. Chernikov u_short port; 876*d6164b77SAlexander V. Chernikov }; 877*d6164b77SAlexander V. Chernikov 878*d6164b77SAlexander V. Chernikov struct cfg_redir_legacy { 879*d6164b77SAlexander V. Chernikov LIST_ENTRY(cfg_redir) _next; 880*d6164b77SAlexander V. Chernikov u_int16_t mode; 881*d6164b77SAlexander V. Chernikov struct in_addr laddr; 882*d6164b77SAlexander V. Chernikov struct in_addr paddr; 883*d6164b77SAlexander V. Chernikov struct in_addr raddr; 884*d6164b77SAlexander V. Chernikov u_short lport; 885*d6164b77SAlexander V. Chernikov u_short pport; 886*d6164b77SAlexander V. Chernikov u_short rport; 887*d6164b77SAlexander V. Chernikov u_short pport_cnt; 888*d6164b77SAlexander V. Chernikov u_short rport_cnt; 889*d6164b77SAlexander V. Chernikov int proto; 890*d6164b77SAlexander V. Chernikov struct alias_link **alink; 891*d6164b77SAlexander V. Chernikov u_int16_t spool_cnt; 892*d6164b77SAlexander V. Chernikov LIST_HEAD(, cfg_spool_legacy) spool_chain; 893*d6164b77SAlexander V. Chernikov }; 894*d6164b77SAlexander V. Chernikov 895*d6164b77SAlexander V. Chernikov struct cfg_nat_legacy { 896*d6164b77SAlexander V. Chernikov LIST_ENTRY(cfg_nat_legacy) _next; 897*d6164b77SAlexander V. Chernikov int id; 898*d6164b77SAlexander V. Chernikov struct in_addr ip; 899*d6164b77SAlexander V. Chernikov char if_name[IF_NAMESIZE]; 900*d6164b77SAlexander V. Chernikov int mode; 901*d6164b77SAlexander V. Chernikov struct libalias *lib; 902*d6164b77SAlexander V. Chernikov int redir_cnt; 903*d6164b77SAlexander V. Chernikov LIST_HEAD(, cfg_redir_legacy) redir_chain; 904*d6164b77SAlexander V. Chernikov }; 905*d6164b77SAlexander V. Chernikov 906*d6164b77SAlexander V. Chernikov static int 907*d6164b77SAlexander V. Chernikov ipfw_nat_cfg(struct sockopt *sopt) 908*d6164b77SAlexander V. Chernikov { 909*d6164b77SAlexander V. Chernikov struct cfg_nat_legacy *cfg; 910*d6164b77SAlexander V. Chernikov struct nat44_cfg_nat *ucfg; 911*d6164b77SAlexander V. Chernikov struct cfg_redir_legacy *rdir; 912*d6164b77SAlexander V. Chernikov struct nat44_cfg_redir *urdir; 913*d6164b77SAlexander V. Chernikov char *buf; 914*d6164b77SAlexander V. Chernikov size_t len, len2; 915*d6164b77SAlexander V. Chernikov int error, i; 916*d6164b77SAlexander V. Chernikov 917*d6164b77SAlexander V. Chernikov len = sopt->sopt_valsize; 918*d6164b77SAlexander V. Chernikov len2 = len + 128; 919*d6164b77SAlexander V. Chernikov 920*d6164b77SAlexander V. Chernikov /* 921*d6164b77SAlexander V. Chernikov * Allocate 2x buffer to store converted structures. 922*d6164b77SAlexander V. Chernikov * new redir_cfg has shrinked, so we're sure that 923*d6164b77SAlexander V. Chernikov * new buffer size is enough. 924*d6164b77SAlexander V. Chernikov */ 925*d6164b77SAlexander V. Chernikov buf = malloc(roundup2(len, 8) + len2, M_TEMP, M_WAITOK | M_ZERO); 926*d6164b77SAlexander V. Chernikov error = sooptcopyin(sopt, buf, len, sizeof(struct cfg_nat_legacy)); 927*d6164b77SAlexander V. Chernikov if (error != 0) 928*d6164b77SAlexander V. Chernikov goto out; 929*d6164b77SAlexander V. Chernikov 930*d6164b77SAlexander V. Chernikov cfg = (struct cfg_nat_legacy *)buf; 931*d6164b77SAlexander V. Chernikov if (cfg->id < 0) { 932*d6164b77SAlexander V. Chernikov error = EINVAL; 933*d6164b77SAlexander V. Chernikov goto out; 934*d6164b77SAlexander V. Chernikov } 935*d6164b77SAlexander V. Chernikov 936*d6164b77SAlexander V. Chernikov ucfg = (struct nat44_cfg_nat *)&buf[roundup2(len, 8)]; 937*d6164b77SAlexander V. Chernikov snprintf(ucfg->name, sizeof(ucfg->name), "%d", cfg->id); 938*d6164b77SAlexander V. Chernikov strlcpy(ucfg->if_name, cfg->if_name, sizeof(ucfg->if_name)); 939*d6164b77SAlexander V. Chernikov ucfg->ip = cfg->ip; 940*d6164b77SAlexander V. Chernikov ucfg->mode = cfg->mode; 941*d6164b77SAlexander V. Chernikov ucfg->redir_cnt = cfg->redir_cnt; 942*d6164b77SAlexander V. Chernikov 943*d6164b77SAlexander V. Chernikov if (len < sizeof(*cfg) + cfg->redir_cnt * sizeof(*rdir)) { 944*d6164b77SAlexander V. Chernikov error = EINVAL; 945*d6164b77SAlexander V. Chernikov goto out; 946*d6164b77SAlexander V. Chernikov } 947*d6164b77SAlexander V. Chernikov 948*d6164b77SAlexander V. Chernikov urdir = (struct nat44_cfg_redir *)(ucfg + 1); 949*d6164b77SAlexander V. Chernikov rdir = (struct cfg_redir_legacy *)(cfg + 1); 950*d6164b77SAlexander V. Chernikov for (i = 0; i < cfg->redir_cnt; i++) { 951*d6164b77SAlexander V. Chernikov urdir->mode = rdir->mode; 952*d6164b77SAlexander V. Chernikov urdir->laddr = rdir->laddr; 953*d6164b77SAlexander V. Chernikov urdir->paddr = rdir->paddr; 954*d6164b77SAlexander V. Chernikov urdir->raddr = rdir->raddr; 955*d6164b77SAlexander V. Chernikov urdir->lport = rdir->lport; 956*d6164b77SAlexander V. Chernikov urdir->pport = rdir->pport; 957*d6164b77SAlexander V. Chernikov urdir->rport = rdir->rport; 958*d6164b77SAlexander V. Chernikov urdir->pport_cnt = rdir->pport_cnt; 959*d6164b77SAlexander V. Chernikov urdir->rport_cnt = rdir->rport_cnt; 960*d6164b77SAlexander V. Chernikov urdir->proto = rdir->proto; 961*d6164b77SAlexander V. Chernikov urdir->spool_cnt = rdir->spool_cnt; 962*d6164b77SAlexander V. Chernikov 963*d6164b77SAlexander V. Chernikov urdir++; 964*d6164b77SAlexander V. Chernikov rdir++; 965*d6164b77SAlexander V. Chernikov } 966*d6164b77SAlexander V. Chernikov 967*d6164b77SAlexander V. Chernikov nat44_config(&V_layer3_chain, ucfg); 9683b3a8eb9SGleb Smirnoff 9693b3a8eb9SGleb Smirnoff out: 9703b3a8eb9SGleb Smirnoff free(buf, M_TEMP); 9713b3a8eb9SGleb Smirnoff return (error); 9723b3a8eb9SGleb Smirnoff } 9733b3a8eb9SGleb Smirnoff 9743b3a8eb9SGleb Smirnoff static int 9753b3a8eb9SGleb Smirnoff ipfw_nat_del(struct sockopt *sopt) 9763b3a8eb9SGleb Smirnoff { 9773b3a8eb9SGleb Smirnoff struct cfg_nat *ptr; 9783b3a8eb9SGleb Smirnoff struct ip_fw_chain *chain = &V_layer3_chain; 9793b3a8eb9SGleb Smirnoff int i; 9803b3a8eb9SGleb Smirnoff 9813b3a8eb9SGleb Smirnoff sooptcopyin(sopt, &i, sizeof i, sizeof i); 9823b3a8eb9SGleb Smirnoff /* XXX validate i */ 983*d6164b77SAlexander V. Chernikov IPFW_UH_WLOCK(chain); 9843b3a8eb9SGleb Smirnoff ptr = lookup_nat(&chain->nat, i); 9853b3a8eb9SGleb Smirnoff if (ptr == NULL) { 986*d6164b77SAlexander V. Chernikov IPFW_UH_WUNLOCK(chain); 9873b3a8eb9SGleb Smirnoff return (EINVAL); 9883b3a8eb9SGleb Smirnoff } 989*d6164b77SAlexander V. Chernikov IPFW_WLOCK(chain); 9903b3a8eb9SGleb Smirnoff LIST_REMOVE(ptr, _next); 9913b3a8eb9SGleb Smirnoff flush_nat_ptrs(chain, i); 9923b3a8eb9SGleb Smirnoff IPFW_WUNLOCK(chain); 993*d6164b77SAlexander V. Chernikov IPFW_UH_WUNLOCK(chain); 9943b3a8eb9SGleb Smirnoff del_redir_spool_cfg(ptr, &ptr->redir_chain); 9953b3a8eb9SGleb Smirnoff LibAliasUninit(ptr->lib); 9963b3a8eb9SGleb Smirnoff free(ptr, M_IPFW); 9973b3a8eb9SGleb Smirnoff return (0); 9983b3a8eb9SGleb Smirnoff } 9993b3a8eb9SGleb Smirnoff 10003b3a8eb9SGleb Smirnoff static int 10013b3a8eb9SGleb Smirnoff ipfw_nat_get_cfg(struct sockopt *sopt) 10023b3a8eb9SGleb Smirnoff { 10033b3a8eb9SGleb Smirnoff struct ip_fw_chain *chain = &V_layer3_chain; 10043b3a8eb9SGleb Smirnoff struct cfg_nat *n; 1005*d6164b77SAlexander V. Chernikov struct cfg_nat_legacy *ucfg; 10063b3a8eb9SGleb Smirnoff struct cfg_redir *r; 10073b3a8eb9SGleb Smirnoff struct cfg_spool *s; 1008*d6164b77SAlexander V. Chernikov struct cfg_redir_legacy *ser_r; 1009*d6164b77SAlexander V. Chernikov struct cfg_spool_legacy *ser_s; 10103b3a8eb9SGleb Smirnoff char *data; 10113b3a8eb9SGleb Smirnoff int gencnt, nat_cnt, len, error; 10123b3a8eb9SGleb Smirnoff 10133b3a8eb9SGleb Smirnoff nat_cnt = 0; 10143b3a8eb9SGleb Smirnoff len = sizeof(nat_cnt); 10153b3a8eb9SGleb Smirnoff 1016*d6164b77SAlexander V. Chernikov IPFW_UH_RLOCK(chain); 10173b3a8eb9SGleb Smirnoff retry: 10183b3a8eb9SGleb Smirnoff gencnt = chain->gencnt; 10193b3a8eb9SGleb Smirnoff /* Estimate memory amount */ 10203b3a8eb9SGleb Smirnoff LIST_FOREACH(n, &chain->nat, _next) { 10213b3a8eb9SGleb Smirnoff nat_cnt++; 1022*d6164b77SAlexander V. Chernikov len += sizeof(struct cfg_nat_legacy); 10233b3a8eb9SGleb Smirnoff LIST_FOREACH(r, &n->redir_chain, _next) { 1024*d6164b77SAlexander V. Chernikov len += sizeof(struct cfg_redir_legacy); 10253b3a8eb9SGleb Smirnoff LIST_FOREACH(s, &r->spool_chain, _next) 1026*d6164b77SAlexander V. Chernikov len += sizeof(struct cfg_spool_legacy); 10273b3a8eb9SGleb Smirnoff } 10283b3a8eb9SGleb Smirnoff } 1029*d6164b77SAlexander V. Chernikov IPFW_UH_RUNLOCK(chain); 10303b3a8eb9SGleb Smirnoff 10313b3a8eb9SGleb Smirnoff data = malloc(len, M_TEMP, M_WAITOK | M_ZERO); 10323b3a8eb9SGleb Smirnoff bcopy(&nat_cnt, data, sizeof(nat_cnt)); 10333b3a8eb9SGleb Smirnoff 10343b3a8eb9SGleb Smirnoff nat_cnt = 0; 10353b3a8eb9SGleb Smirnoff len = sizeof(nat_cnt); 10363b3a8eb9SGleb Smirnoff 1037*d6164b77SAlexander V. Chernikov IPFW_UH_RLOCK(chain); 10383b3a8eb9SGleb Smirnoff if (gencnt != chain->gencnt) { 10393b3a8eb9SGleb Smirnoff free(data, M_TEMP); 10403b3a8eb9SGleb Smirnoff goto retry; 10413b3a8eb9SGleb Smirnoff } 10423b3a8eb9SGleb Smirnoff /* Serialize all the data. */ 10433b3a8eb9SGleb Smirnoff LIST_FOREACH(n, &chain->nat, _next) { 1044*d6164b77SAlexander V. Chernikov ucfg = (struct cfg_nat_legacy *)&data[len]; 1045*d6164b77SAlexander V. Chernikov ucfg->id = n->id; 1046*d6164b77SAlexander V. Chernikov ucfg->ip = n->ip; 1047*d6164b77SAlexander V. Chernikov ucfg->redir_cnt = n->redir_cnt; 1048*d6164b77SAlexander V. Chernikov ucfg->mode = n->mode; 1049*d6164b77SAlexander V. Chernikov strlcpy(ucfg->if_name, n->if_name, sizeof(ucfg->if_name)); 1050*d6164b77SAlexander V. Chernikov len += sizeof(struct cfg_nat_legacy); 10513b3a8eb9SGleb Smirnoff LIST_FOREACH(r, &n->redir_chain, _next) { 1052*d6164b77SAlexander V. Chernikov ser_r = (struct cfg_redir_legacy *)&data[len]; 1053*d6164b77SAlexander V. Chernikov ser_r->mode = r->mode; 1054*d6164b77SAlexander V. Chernikov ser_r->laddr = r->laddr; 1055*d6164b77SAlexander V. Chernikov ser_r->paddr = r->paddr; 1056*d6164b77SAlexander V. Chernikov ser_r->raddr = r->raddr; 1057*d6164b77SAlexander V. Chernikov ser_r->lport = r->lport; 1058*d6164b77SAlexander V. Chernikov ser_r->pport = r->pport; 1059*d6164b77SAlexander V. Chernikov ser_r->rport = r->rport; 1060*d6164b77SAlexander V. Chernikov ser_r->pport_cnt = r->pport_cnt; 1061*d6164b77SAlexander V. Chernikov ser_r->rport_cnt = r->rport_cnt; 1062*d6164b77SAlexander V. Chernikov ser_r->proto = r->proto; 1063*d6164b77SAlexander V. Chernikov ser_r->spool_cnt = r->spool_cnt; 1064*d6164b77SAlexander V. Chernikov len += sizeof(struct cfg_redir_legacy); 10653b3a8eb9SGleb Smirnoff LIST_FOREACH(s, &r->spool_chain, _next) { 1066*d6164b77SAlexander V. Chernikov ser_s = (struct cfg_spool_legacy *)&data[len]; 1067*d6164b77SAlexander V. Chernikov ser_s->addr = s->addr; 1068*d6164b77SAlexander V. Chernikov ser_s->port = s->port; 1069*d6164b77SAlexander V. Chernikov len += sizeof(struct cfg_spool_legacy); 10703b3a8eb9SGleb Smirnoff } 10713b3a8eb9SGleb Smirnoff } 10723b3a8eb9SGleb Smirnoff } 1073*d6164b77SAlexander V. Chernikov IPFW_UH_RUNLOCK(chain); 10743b3a8eb9SGleb Smirnoff 10753b3a8eb9SGleb Smirnoff error = sooptcopyout(sopt, data, len); 10763b3a8eb9SGleb Smirnoff free(data, M_TEMP); 10773b3a8eb9SGleb Smirnoff 10783b3a8eb9SGleb Smirnoff return (error); 10793b3a8eb9SGleb Smirnoff } 10803b3a8eb9SGleb Smirnoff 10813b3a8eb9SGleb Smirnoff static int 10823b3a8eb9SGleb Smirnoff ipfw_nat_get_log(struct sockopt *sopt) 10833b3a8eb9SGleb Smirnoff { 10843b3a8eb9SGleb Smirnoff uint8_t *data; 10853b3a8eb9SGleb Smirnoff struct cfg_nat *ptr; 10863b3a8eb9SGleb Smirnoff int i, size; 10873b3a8eb9SGleb Smirnoff struct ip_fw_chain *chain; 10883b3a8eb9SGleb Smirnoff 10893b3a8eb9SGleb Smirnoff chain = &V_layer3_chain; 10903b3a8eb9SGleb Smirnoff 10913b3a8eb9SGleb Smirnoff IPFW_RLOCK(chain); 10923b3a8eb9SGleb Smirnoff /* one pass to count, one to copy the data */ 10933b3a8eb9SGleb Smirnoff i = 0; 10943b3a8eb9SGleb Smirnoff LIST_FOREACH(ptr, &chain->nat, _next) { 10953b3a8eb9SGleb Smirnoff if (ptr->lib->logDesc == NULL) 10963b3a8eb9SGleb Smirnoff continue; 10973b3a8eb9SGleb Smirnoff i++; 10983b3a8eb9SGleb Smirnoff } 10993b3a8eb9SGleb Smirnoff size = i * (LIBALIAS_BUF_SIZE + sizeof(int)); 11003b3a8eb9SGleb Smirnoff data = malloc(size, M_IPFW, M_NOWAIT | M_ZERO); 11013b3a8eb9SGleb Smirnoff if (data == NULL) { 11023b3a8eb9SGleb Smirnoff IPFW_RUNLOCK(chain); 11033b3a8eb9SGleb Smirnoff return (ENOSPC); 11043b3a8eb9SGleb Smirnoff } 11053b3a8eb9SGleb Smirnoff i = 0; 11063b3a8eb9SGleb Smirnoff LIST_FOREACH(ptr, &chain->nat, _next) { 11073b3a8eb9SGleb Smirnoff if (ptr->lib->logDesc == NULL) 11083b3a8eb9SGleb Smirnoff continue; 11093b3a8eb9SGleb Smirnoff bcopy(&ptr->id, &data[i], sizeof(int)); 11103b3a8eb9SGleb Smirnoff i += sizeof(int); 11113b3a8eb9SGleb Smirnoff bcopy(ptr->lib->logDesc, &data[i], LIBALIAS_BUF_SIZE); 11123b3a8eb9SGleb Smirnoff i += LIBALIAS_BUF_SIZE; 11133b3a8eb9SGleb Smirnoff } 11143b3a8eb9SGleb Smirnoff IPFW_RUNLOCK(chain); 11153b3a8eb9SGleb Smirnoff sooptcopyout(sopt, data, size); 11163b3a8eb9SGleb Smirnoff free(data, M_IPFW); 11173b3a8eb9SGleb Smirnoff return(0); 11183b3a8eb9SGleb Smirnoff } 11193b3a8eb9SGleb Smirnoff 11208856400bSMikolaj Golub static int 11218856400bSMikolaj Golub vnet_ipfw_nat_init(const void *arg __unused) 11223b3a8eb9SGleb Smirnoff { 11233b3a8eb9SGleb Smirnoff 11248856400bSMikolaj Golub V_ipfw_nat_ready = 1; 11258856400bSMikolaj Golub return (0); 11263b3a8eb9SGleb Smirnoff } 11273b3a8eb9SGleb Smirnoff 11288856400bSMikolaj Golub static int 11298856400bSMikolaj Golub vnet_ipfw_nat_uninit(const void *arg __unused) 11303b3a8eb9SGleb Smirnoff { 11313b3a8eb9SGleb Smirnoff struct cfg_nat *ptr, *ptr_temp; 11323b3a8eb9SGleb Smirnoff struct ip_fw_chain *chain; 11333b3a8eb9SGleb Smirnoff 11343b3a8eb9SGleb Smirnoff chain = &V_layer3_chain; 11353b3a8eb9SGleb Smirnoff IPFW_WLOCK(chain); 11363b3a8eb9SGleb Smirnoff LIST_FOREACH_SAFE(ptr, &chain->nat, _next, ptr_temp) { 11373b3a8eb9SGleb Smirnoff LIST_REMOVE(ptr, _next); 11383b3a8eb9SGleb Smirnoff del_redir_spool_cfg(ptr, &ptr->redir_chain); 11393b3a8eb9SGleb Smirnoff LibAliasUninit(ptr->lib); 11403b3a8eb9SGleb Smirnoff free(ptr, M_IPFW); 11413b3a8eb9SGleb Smirnoff } 11423b3a8eb9SGleb Smirnoff flush_nat_ptrs(chain, -1 /* flush all */); 11438856400bSMikolaj Golub V_ipfw_nat_ready = 0; 11448856400bSMikolaj Golub IPFW_WUNLOCK(chain); 11458856400bSMikolaj Golub return (0); 11468856400bSMikolaj Golub } 11478856400bSMikolaj Golub 11488856400bSMikolaj Golub static void 11498856400bSMikolaj Golub ipfw_nat_init(void) 11508856400bSMikolaj Golub { 11518856400bSMikolaj Golub 11528856400bSMikolaj Golub /* init ipfw hooks */ 11538856400bSMikolaj Golub ipfw_nat_ptr = ipfw_nat; 11548856400bSMikolaj Golub lookup_nat_ptr = lookup_nat; 11558856400bSMikolaj Golub ipfw_nat_cfg_ptr = ipfw_nat_cfg; 11568856400bSMikolaj Golub ipfw_nat_del_ptr = ipfw_nat_del; 11578856400bSMikolaj Golub ipfw_nat_get_cfg_ptr = ipfw_nat_get_cfg; 11588856400bSMikolaj Golub ipfw_nat_get_log_ptr = ipfw_nat_get_log; 1159*d6164b77SAlexander V. Chernikov IPFW_ADD_SOPT_HANDLER(1, scodes); 11608856400bSMikolaj Golub 11618856400bSMikolaj Golub ifaddr_event_tag = EVENTHANDLER_REGISTER(ifaddr_event, ifaddr_change, 11628856400bSMikolaj Golub NULL, EVENTHANDLER_PRI_ANY); 11638856400bSMikolaj Golub } 11648856400bSMikolaj Golub 11658856400bSMikolaj Golub static void 11668856400bSMikolaj Golub ipfw_nat_destroy(void) 11678856400bSMikolaj Golub { 11688856400bSMikolaj Golub 11698856400bSMikolaj Golub EVENTHANDLER_DEREGISTER(ifaddr_event, ifaddr_event_tag); 11703b3a8eb9SGleb Smirnoff /* deregister ipfw_nat */ 1171*d6164b77SAlexander V. Chernikov IPFW_DEL_SOPT_HANDLER(1, scodes); 11723b3a8eb9SGleb Smirnoff ipfw_nat_ptr = NULL; 11733b3a8eb9SGleb Smirnoff lookup_nat_ptr = NULL; 11743b3a8eb9SGleb Smirnoff ipfw_nat_cfg_ptr = NULL; 11753b3a8eb9SGleb Smirnoff ipfw_nat_del_ptr = NULL; 11763b3a8eb9SGleb Smirnoff ipfw_nat_get_cfg_ptr = NULL; 11773b3a8eb9SGleb Smirnoff ipfw_nat_get_log_ptr = NULL; 11783b3a8eb9SGleb Smirnoff } 11793b3a8eb9SGleb Smirnoff 11803b3a8eb9SGleb Smirnoff static int 11813b3a8eb9SGleb Smirnoff ipfw_nat_modevent(module_t mod, int type, void *unused) 11823b3a8eb9SGleb Smirnoff { 11833b3a8eb9SGleb Smirnoff int err = 0; 11843b3a8eb9SGleb Smirnoff 11853b3a8eb9SGleb Smirnoff switch (type) { 11863b3a8eb9SGleb Smirnoff case MOD_LOAD: 11873b3a8eb9SGleb Smirnoff break; 11883b3a8eb9SGleb Smirnoff 11893b3a8eb9SGleb Smirnoff case MOD_UNLOAD: 11903b3a8eb9SGleb Smirnoff break; 11913b3a8eb9SGleb Smirnoff 11923b3a8eb9SGleb Smirnoff default: 11933b3a8eb9SGleb Smirnoff return EOPNOTSUPP; 11943b3a8eb9SGleb Smirnoff break; 11953b3a8eb9SGleb Smirnoff } 11963b3a8eb9SGleb Smirnoff return err; 11973b3a8eb9SGleb Smirnoff } 11983b3a8eb9SGleb Smirnoff 11993b3a8eb9SGleb Smirnoff static moduledata_t ipfw_nat_mod = { 12003b3a8eb9SGleb Smirnoff "ipfw_nat", 12013b3a8eb9SGleb Smirnoff ipfw_nat_modevent, 12029823d527SKevin Lo 0 12033b3a8eb9SGleb Smirnoff }; 12043b3a8eb9SGleb Smirnoff 12058856400bSMikolaj Golub /* Define startup order. */ 12068a477d48SMikolaj Golub #define IPFW_NAT_SI_SUB_FIREWALL SI_SUB_PROTO_IFATTACHDOMAIN 12078a477d48SMikolaj Golub #define IPFW_NAT_MODEVENT_ORDER (SI_ORDER_ANY - 128) /* after ipfw */ 12088856400bSMikolaj Golub #define IPFW_NAT_MODULE_ORDER (IPFW_NAT_MODEVENT_ORDER + 1) 12098856400bSMikolaj Golub #define IPFW_NAT_VNET_ORDER (IPFW_NAT_MODEVENT_ORDER + 2) 12108856400bSMikolaj Golub 12118856400bSMikolaj Golub DECLARE_MODULE(ipfw_nat, ipfw_nat_mod, IPFW_NAT_SI_SUB_FIREWALL, SI_ORDER_ANY); 12123b3a8eb9SGleb Smirnoff MODULE_DEPEND(ipfw_nat, libalias, 1, 1, 1); 12133b3a8eb9SGleb Smirnoff MODULE_DEPEND(ipfw_nat, ipfw, 2, 2, 2); 12143b3a8eb9SGleb Smirnoff MODULE_VERSION(ipfw_nat, 1); 12158856400bSMikolaj Golub 12168856400bSMikolaj Golub SYSINIT(ipfw_nat_init, IPFW_NAT_SI_SUB_FIREWALL, IPFW_NAT_MODULE_ORDER, 12178856400bSMikolaj Golub ipfw_nat_init, NULL); 12188856400bSMikolaj Golub VNET_SYSINIT(vnet_ipfw_nat_init, IPFW_NAT_SI_SUB_FIREWALL, IPFW_NAT_VNET_ORDER, 12198856400bSMikolaj Golub vnet_ipfw_nat_init, NULL); 12208856400bSMikolaj Golub 12218856400bSMikolaj Golub SYSUNINIT(ipfw_nat_destroy, IPFW_NAT_SI_SUB_FIREWALL, IPFW_NAT_MODULE_ORDER, 12228856400bSMikolaj Golub ipfw_nat_destroy, NULL); 12238856400bSMikolaj Golub VNET_SYSUNINIT(vnet_ipfw_nat_uninit, IPFW_NAT_SI_SUB_FIREWALL, 12248856400bSMikolaj Golub IPFW_NAT_VNET_ORDER, vnet_ipfw_nat_uninit, NULL); 12258856400bSMikolaj Golub 12263b3a8eb9SGleb Smirnoff /* end of file */ 1227