13b3a8eb9SGleb Smirnoff /*- 2fe267a55SPedro F. Giffuni * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 3fe267a55SPedro F. Giffuni * 43b3a8eb9SGleb Smirnoff * Copyright (c) 2008 Paolo Pisati 53b3a8eb9SGleb Smirnoff * All rights reserved. 63b3a8eb9SGleb Smirnoff * 73b3a8eb9SGleb Smirnoff * Redistribution and use in source and binary forms, with or without 83b3a8eb9SGleb Smirnoff * modification, are permitted provided that the following conditions 93b3a8eb9SGleb Smirnoff * are met: 103b3a8eb9SGleb Smirnoff * 1. Redistributions of source code must retain the above copyright 113b3a8eb9SGleb Smirnoff * notice, this list of conditions and the following disclaimer. 123b3a8eb9SGleb Smirnoff * 2. Redistributions in binary form must reproduce the above copyright 133b3a8eb9SGleb Smirnoff * notice, this list of conditions and the following disclaimer in the 143b3a8eb9SGleb Smirnoff * documentation and/or other materials provided with the distribution. 153b3a8eb9SGleb Smirnoff * 163b3a8eb9SGleb Smirnoff * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 173b3a8eb9SGleb Smirnoff * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 183b3a8eb9SGleb Smirnoff * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 193b3a8eb9SGleb Smirnoff * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 203b3a8eb9SGleb Smirnoff * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 213b3a8eb9SGleb Smirnoff * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 223b3a8eb9SGleb Smirnoff * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 233b3a8eb9SGleb Smirnoff * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 243b3a8eb9SGleb Smirnoff * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 253b3a8eb9SGleb Smirnoff * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 263b3a8eb9SGleb Smirnoff * SUCH DAMAGE. 273b3a8eb9SGleb Smirnoff */ 283b3a8eb9SGleb Smirnoff 293b3a8eb9SGleb Smirnoff #include <sys/cdefs.h> 303b3a8eb9SGleb Smirnoff __FBSDID("$FreeBSD$"); 313b3a8eb9SGleb Smirnoff 323b3a8eb9SGleb Smirnoff #include <sys/param.h> 333b3a8eb9SGleb Smirnoff #include <sys/systm.h> 343b3a8eb9SGleb Smirnoff #include <sys/eventhandler.h> 353b3a8eb9SGleb Smirnoff #include <sys/malloc.h> 3676039bc8SGleb Smirnoff #include <sys/mbuf.h> 373b3a8eb9SGleb Smirnoff #include <sys/kernel.h> 383b3a8eb9SGleb Smirnoff #include <sys/lock.h> 393b3a8eb9SGleb Smirnoff #include <sys/module.h> 403b3a8eb9SGleb Smirnoff #include <sys/rwlock.h> 41ccba94b8SAlexander V. Chernikov #include <sys/rmlock.h> 423b3a8eb9SGleb Smirnoff 433b3a8eb9SGleb Smirnoff #include <netinet/libalias/alias.h> 443b3a8eb9SGleb Smirnoff #include <netinet/libalias/alias_local.h> 453b3a8eb9SGleb Smirnoff 463b3a8eb9SGleb Smirnoff #include <net/if.h> 4776039bc8SGleb Smirnoff #include <net/if_var.h> 483b3a8eb9SGleb Smirnoff #include <netinet/in.h> 493b3a8eb9SGleb Smirnoff #include <netinet/ip.h> 503b3a8eb9SGleb Smirnoff #include <netinet/ip_var.h> 513b3a8eb9SGleb Smirnoff #include <netinet/ip_fw.h> 523b3a8eb9SGleb Smirnoff #include <netinet/tcp.h> 533b3a8eb9SGleb Smirnoff #include <netinet/udp.h> 543b3a8eb9SGleb Smirnoff 553b3a8eb9SGleb Smirnoff #include <netpfil/ipfw/ip_fw_private.h> 563b3a8eb9SGleb Smirnoff 573b3a8eb9SGleb Smirnoff #include <machine/in_cksum.h> /* XXX for in_cksum */ 583b3a8eb9SGleb Smirnoff 59d6164b77SAlexander V. Chernikov struct cfg_spool { 60d6164b77SAlexander V. Chernikov LIST_ENTRY(cfg_spool) _next; /* chain of spool instances */ 61d6164b77SAlexander V. Chernikov struct in_addr addr; 62d6164b77SAlexander V. Chernikov uint16_t port; 63d6164b77SAlexander V. Chernikov }; 64d6164b77SAlexander V. Chernikov 65d6164b77SAlexander V. Chernikov /* Nat redirect configuration. */ 66d6164b77SAlexander V. Chernikov struct cfg_redir { 67d6164b77SAlexander V. Chernikov LIST_ENTRY(cfg_redir) _next; /* chain of redir instances */ 68d6164b77SAlexander V. Chernikov uint16_t mode; /* type of redirect mode */ 69d6164b77SAlexander V. Chernikov uint16_t proto; /* protocol: tcp/udp */ 70d6164b77SAlexander V. Chernikov struct in_addr laddr; /* local ip address */ 71d6164b77SAlexander V. Chernikov struct in_addr paddr; /* public ip address */ 72d6164b77SAlexander V. Chernikov struct in_addr raddr; /* remote ip address */ 73d6164b77SAlexander V. Chernikov uint16_t lport; /* local port */ 74d6164b77SAlexander V. Chernikov uint16_t pport; /* public port */ 75d6164b77SAlexander V. Chernikov uint16_t rport; /* remote port */ 76d6164b77SAlexander V. Chernikov uint16_t pport_cnt; /* number of public ports */ 77d6164b77SAlexander V. Chernikov uint16_t rport_cnt; /* number of remote ports */ 78d6164b77SAlexander V. Chernikov struct alias_link **alink; 79d6164b77SAlexander V. Chernikov u_int16_t spool_cnt; /* num of entry in spool chain */ 80d6164b77SAlexander V. Chernikov /* chain of spool instances */ 81d6164b77SAlexander V. Chernikov LIST_HEAD(spool_chain, cfg_spool) spool_chain; 82d6164b77SAlexander V. Chernikov }; 83d6164b77SAlexander V. Chernikov 84d6164b77SAlexander V. Chernikov /* Nat configuration data struct. */ 85d6164b77SAlexander V. Chernikov struct cfg_nat { 86d6164b77SAlexander V. Chernikov /* chain of nat instances */ 87d6164b77SAlexander V. Chernikov LIST_ENTRY(cfg_nat) _next; 88d6164b77SAlexander V. Chernikov int id; /* nat id */ 89d6164b77SAlexander V. Chernikov struct in_addr ip; /* nat ip address */ 90d6164b77SAlexander V. Chernikov struct libalias *lib; /* libalias instance */ 91d6164b77SAlexander V. Chernikov int mode; /* aliasing mode */ 92d6164b77SAlexander V. Chernikov int redir_cnt; /* number of entry in spool chain */ 93d6164b77SAlexander V. Chernikov /* chain of redir instances */ 94d6164b77SAlexander V. Chernikov LIST_HEAD(redir_chain, cfg_redir) redir_chain; 95d6164b77SAlexander V. Chernikov char if_name[IF_NAMESIZE]; /* interface name */ 96d6164b77SAlexander V. Chernikov }; 97d6164b77SAlexander V. Chernikov 988856400bSMikolaj Golub static eventhandler_tag ifaddr_event_tag; 993b3a8eb9SGleb Smirnoff 1003b3a8eb9SGleb Smirnoff static void 1013b3a8eb9SGleb Smirnoff ifaddr_change(void *arg __unused, struct ifnet *ifp) 1023b3a8eb9SGleb Smirnoff { 1033b3a8eb9SGleb Smirnoff struct cfg_nat *ptr; 1043b3a8eb9SGleb Smirnoff struct ifaddr *ifa; 1053b3a8eb9SGleb Smirnoff struct ip_fw_chain *chain; 1063b3a8eb9SGleb Smirnoff 1078856400bSMikolaj Golub KASSERT(curvnet == ifp->if_vnet, 1088856400bSMikolaj Golub ("curvnet(%p) differs from iface vnet(%p)", curvnet, ifp->if_vnet)); 1099ac51e79SBjoern A. Zeeb 1109ac51e79SBjoern A. Zeeb if (V_ipfw_vnet_ready == 0 || V_ipfw_nat_ready == 0) 1119ac51e79SBjoern A. Zeeb return; 1129ac51e79SBjoern A. Zeeb 1133b3a8eb9SGleb Smirnoff chain = &V_layer3_chain; 1140b47e42bSAlexander V. Chernikov IPFW_UH_WLOCK(chain); 1153b3a8eb9SGleb Smirnoff /* Check every nat entry... */ 1163b3a8eb9SGleb Smirnoff LIST_FOREACH(ptr, &chain->nat, _next) { 117*16a72f53SGleb Smirnoff struct epoch_tracker et; 118*16a72f53SGleb Smirnoff 1193b3a8eb9SGleb Smirnoff /* ...using nic 'ifp->if_xname' as dynamic alias address. */ 120c254a209SAlexander V. Chernikov if (strncmp(ptr->if_name, ifp->if_xname, IF_NAMESIZE) != 0) 1213b3a8eb9SGleb Smirnoff continue; 122*16a72f53SGleb Smirnoff NET_EPOCH_ENTER(et); 123d7c5a620SMatt Macy CK_STAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) { 1243b3a8eb9SGleb Smirnoff if (ifa->ifa_addr == NULL) 1253b3a8eb9SGleb Smirnoff continue; 1263b3a8eb9SGleb Smirnoff if (ifa->ifa_addr->sa_family != AF_INET) 1273b3a8eb9SGleb Smirnoff continue; 1280b47e42bSAlexander V. Chernikov IPFW_WLOCK(chain); 1293b3a8eb9SGleb Smirnoff ptr->ip = ((struct sockaddr_in *) 1303b3a8eb9SGleb Smirnoff (ifa->ifa_addr))->sin_addr; 1313b3a8eb9SGleb Smirnoff LibAliasSetAddress(ptr->lib, ptr->ip); 1320b47e42bSAlexander V. Chernikov IPFW_WUNLOCK(chain); 1333b3a8eb9SGleb Smirnoff } 134*16a72f53SGleb Smirnoff NET_EPOCH_EXIT(et); 1353b3a8eb9SGleb Smirnoff } 1360b47e42bSAlexander V. Chernikov IPFW_UH_WUNLOCK(chain); 1373b3a8eb9SGleb Smirnoff } 1383b3a8eb9SGleb Smirnoff 1393b3a8eb9SGleb Smirnoff /* 1403b3a8eb9SGleb Smirnoff * delete the pointers for nat entry ix, or all of them if ix < 0 1413b3a8eb9SGleb Smirnoff */ 1423b3a8eb9SGleb Smirnoff static void 1433b3a8eb9SGleb Smirnoff flush_nat_ptrs(struct ip_fw_chain *chain, const int ix) 1443b3a8eb9SGleb Smirnoff { 1453b3a8eb9SGleb Smirnoff ipfw_insn_nat *cmd; 146e758846cSAndrey V. Elsukov int i; 1473b3a8eb9SGleb Smirnoff 1483b3a8eb9SGleb Smirnoff IPFW_WLOCK_ASSERT(chain); 1493b3a8eb9SGleb Smirnoff for (i = 0; i < chain->n_rules; i++) { 150e758846cSAndrey V. Elsukov cmd = (ipfw_insn_nat *)ipfw_get_action(chain->map[i]); 1513b3a8eb9SGleb Smirnoff if (cmd->o.opcode == O_NAT && cmd->nat != NULL && 1523b3a8eb9SGleb Smirnoff (ix < 0 || cmd->nat->id == ix)) 1533b3a8eb9SGleb Smirnoff cmd->nat = NULL; 1543b3a8eb9SGleb Smirnoff } 1553b3a8eb9SGleb Smirnoff } 1563b3a8eb9SGleb Smirnoff 1573b3a8eb9SGleb Smirnoff static void 1583b3a8eb9SGleb Smirnoff del_redir_spool_cfg(struct cfg_nat *n, struct redir_chain *head) 1593b3a8eb9SGleb Smirnoff { 1603b3a8eb9SGleb Smirnoff struct cfg_redir *r, *tmp_r; 1613b3a8eb9SGleb Smirnoff struct cfg_spool *s, *tmp_s; 1623b3a8eb9SGleb Smirnoff int i, num; 1633b3a8eb9SGleb Smirnoff 1643b3a8eb9SGleb Smirnoff LIST_FOREACH_SAFE(r, head, _next, tmp_r) { 1653b3a8eb9SGleb Smirnoff num = 1; /* Number of alias_link to delete. */ 1663b3a8eb9SGleb Smirnoff switch (r->mode) { 167d6164b77SAlexander V. Chernikov case NAT44_REDIR_PORT: 1683b3a8eb9SGleb Smirnoff num = r->pport_cnt; 1693b3a8eb9SGleb Smirnoff /* FALLTHROUGH */ 170d6164b77SAlexander V. Chernikov case NAT44_REDIR_ADDR: 171d6164b77SAlexander V. Chernikov case NAT44_REDIR_PROTO: 1723b3a8eb9SGleb Smirnoff /* Delete all libalias redirect entry. */ 1733b3a8eb9SGleb Smirnoff for (i = 0; i < num; i++) 1743b3a8eb9SGleb Smirnoff LibAliasRedirectDelete(n->lib, r->alink[i]); 1753b3a8eb9SGleb Smirnoff /* Del spool cfg if any. */ 1763b3a8eb9SGleb Smirnoff LIST_FOREACH_SAFE(s, &r->spool_chain, _next, tmp_s) { 1773b3a8eb9SGleb Smirnoff LIST_REMOVE(s, _next); 1783b3a8eb9SGleb Smirnoff free(s, M_IPFW); 1793b3a8eb9SGleb Smirnoff } 1803b3a8eb9SGleb Smirnoff free(r->alink, M_IPFW); 1813b3a8eb9SGleb Smirnoff LIST_REMOVE(r, _next); 1823b3a8eb9SGleb Smirnoff free(r, M_IPFW); 1833b3a8eb9SGleb Smirnoff break; 1843b3a8eb9SGleb Smirnoff default: 1853b3a8eb9SGleb Smirnoff printf("unknown redirect mode: %u\n", r->mode); 1863b3a8eb9SGleb Smirnoff /* XXX - panic?!?!? */ 1873b3a8eb9SGleb Smirnoff break; 1883b3a8eb9SGleb Smirnoff } 1893b3a8eb9SGleb Smirnoff } 1903b3a8eb9SGleb Smirnoff } 1913b3a8eb9SGleb Smirnoff 192d6164b77SAlexander V. Chernikov static int 1933b3a8eb9SGleb Smirnoff add_redir_spool_cfg(char *buf, struct cfg_nat *ptr) 1943b3a8eb9SGleb Smirnoff { 195d6164b77SAlexander V. Chernikov struct cfg_redir *r; 196d6164b77SAlexander V. Chernikov struct cfg_spool *s; 197d6164b77SAlexander V. Chernikov struct nat44_cfg_redir *ser_r; 198d6164b77SAlexander V. Chernikov struct nat44_cfg_spool *ser_s; 199d6164b77SAlexander V. Chernikov 2003b3a8eb9SGleb Smirnoff int cnt, off, i; 2013b3a8eb9SGleb Smirnoff 2023b3a8eb9SGleb Smirnoff for (cnt = 0, off = 0; cnt < ptr->redir_cnt; cnt++) { 203d6164b77SAlexander V. Chernikov ser_r = (struct nat44_cfg_redir *)&buf[off]; 204d6164b77SAlexander V. Chernikov r = malloc(sizeof(*r), M_IPFW, M_WAITOK | M_ZERO); 205d6164b77SAlexander V. Chernikov r->mode = ser_r->mode; 206d6164b77SAlexander V. Chernikov r->laddr = ser_r->laddr; 207d6164b77SAlexander V. Chernikov r->paddr = ser_r->paddr; 208d6164b77SAlexander V. Chernikov r->raddr = ser_r->raddr; 209d6164b77SAlexander V. Chernikov r->lport = ser_r->lport; 210d6164b77SAlexander V. Chernikov r->pport = ser_r->pport; 211d6164b77SAlexander V. Chernikov r->rport = ser_r->rport; 212d6164b77SAlexander V. Chernikov r->pport_cnt = ser_r->pport_cnt; 213d6164b77SAlexander V. Chernikov r->rport_cnt = ser_r->rport_cnt; 214d6164b77SAlexander V. Chernikov r->proto = ser_r->proto; 215d6164b77SAlexander V. Chernikov r->spool_cnt = ser_r->spool_cnt; 216d6164b77SAlexander V. Chernikov //memcpy(r, ser_r, SOF_REDIR); 2173b3a8eb9SGleb Smirnoff LIST_INIT(&r->spool_chain); 218d6164b77SAlexander V. Chernikov off += sizeof(struct nat44_cfg_redir); 2193b3a8eb9SGleb Smirnoff r->alink = malloc(sizeof(struct alias_link *) * r->pport_cnt, 2203b3a8eb9SGleb Smirnoff M_IPFW, M_WAITOK | M_ZERO); 2213b3a8eb9SGleb Smirnoff switch (r->mode) { 222d6164b77SAlexander V. Chernikov case NAT44_REDIR_ADDR: 2233b3a8eb9SGleb Smirnoff r->alink[0] = LibAliasRedirectAddr(ptr->lib, r->laddr, 2243b3a8eb9SGleb Smirnoff r->paddr); 2253b3a8eb9SGleb Smirnoff break; 226d6164b77SAlexander V. Chernikov case NAT44_REDIR_PORT: 2273b3a8eb9SGleb Smirnoff for (i = 0 ; i < r->pport_cnt; i++) { 2283b3a8eb9SGleb Smirnoff /* If remotePort is all ports, set it to 0. */ 2293b3a8eb9SGleb Smirnoff u_short remotePortCopy = r->rport + i; 2303b3a8eb9SGleb Smirnoff if (r->rport_cnt == 1 && r->rport == 0) 2313b3a8eb9SGleb Smirnoff remotePortCopy = 0; 2323b3a8eb9SGleb Smirnoff r->alink[i] = LibAliasRedirectPort(ptr->lib, 2333b3a8eb9SGleb Smirnoff r->laddr, htons(r->lport + i), r->raddr, 2343b3a8eb9SGleb Smirnoff htons(remotePortCopy), r->paddr, 2353b3a8eb9SGleb Smirnoff htons(r->pport + i), r->proto); 2363b3a8eb9SGleb Smirnoff if (r->alink[i] == NULL) { 2373b3a8eb9SGleb Smirnoff r->alink[0] = NULL; 2383b3a8eb9SGleb Smirnoff break; 2393b3a8eb9SGleb Smirnoff } 2403b3a8eb9SGleb Smirnoff } 2413b3a8eb9SGleb Smirnoff break; 242d6164b77SAlexander V. Chernikov case NAT44_REDIR_PROTO: 2433b3a8eb9SGleb Smirnoff r->alink[0] = LibAliasRedirectProto(ptr->lib ,r->laddr, 2443b3a8eb9SGleb Smirnoff r->raddr, r->paddr, r->proto); 2453b3a8eb9SGleb Smirnoff break; 2463b3a8eb9SGleb Smirnoff default: 2473b3a8eb9SGleb Smirnoff printf("unknown redirect mode: %u\n", r->mode); 2483b3a8eb9SGleb Smirnoff break; 2493b3a8eb9SGleb Smirnoff } 250d6164b77SAlexander V. Chernikov if (r->alink[0] == NULL) { 251d6164b77SAlexander V. Chernikov printf("LibAliasRedirect* returned NULL\n"); 252fdf6290eSGleb Smirnoff free(r->alink, M_IPFW); 253fdf6290eSGleb Smirnoff free(r, M_IPFW); 254d6164b77SAlexander V. Chernikov return (EINVAL); 255d6164b77SAlexander V. Chernikov } 2563b3a8eb9SGleb Smirnoff /* LSNAT handling. */ 2573b3a8eb9SGleb Smirnoff for (i = 0; i < r->spool_cnt; i++) { 258d6164b77SAlexander V. Chernikov ser_s = (struct nat44_cfg_spool *)&buf[off]; 259d6164b77SAlexander V. Chernikov s = malloc(sizeof(*s), M_IPFW, M_WAITOK | M_ZERO); 260d6164b77SAlexander V. Chernikov s->addr = ser_s->addr; 261d6164b77SAlexander V. Chernikov s->port = ser_s->port; 2623b3a8eb9SGleb Smirnoff LibAliasAddServer(ptr->lib, r->alink[0], 2633b3a8eb9SGleb Smirnoff s->addr, htons(s->port)); 264d6164b77SAlexander V. Chernikov off += sizeof(struct nat44_cfg_spool); 2653b3a8eb9SGleb Smirnoff /* Hook spool entry. */ 2663b3a8eb9SGleb Smirnoff LIST_INSERT_HEAD(&r->spool_chain, s, _next); 2673b3a8eb9SGleb Smirnoff } 2683b3a8eb9SGleb Smirnoff /* And finally hook this redir entry. */ 2693b3a8eb9SGleb Smirnoff LIST_INSERT_HEAD(&ptr->redir_chain, r, _next); 2703b3a8eb9SGleb Smirnoff } 271d6164b77SAlexander V. Chernikov 272d6164b77SAlexander V. Chernikov return (0); 2733b3a8eb9SGleb Smirnoff } 2743b3a8eb9SGleb Smirnoff 2751a458088SAlexander V. Chernikov static void 2761a458088SAlexander V. Chernikov free_nat_instance(struct cfg_nat *ptr) 2771a458088SAlexander V. Chernikov { 2781a458088SAlexander V. Chernikov 2791a458088SAlexander V. Chernikov del_redir_spool_cfg(ptr, &ptr->redir_chain); 2801a458088SAlexander V. Chernikov LibAliasUninit(ptr->lib); 2811a458088SAlexander V. Chernikov free(ptr, M_IPFW); 2821a458088SAlexander V. Chernikov } 2831a458088SAlexander V. Chernikov 2841a458088SAlexander V. Chernikov 28510ab2de0SAlexander V. Chernikov /* 28610ab2de0SAlexander V. Chernikov * ipfw_nat - perform mbuf header translation. 28710ab2de0SAlexander V. Chernikov * 28810ab2de0SAlexander V. Chernikov * Note V_layer3_chain has to be locked while calling ipfw_nat() in 28910ab2de0SAlexander V. Chernikov * 'global' operation mode (t == NULL). 29010ab2de0SAlexander V. Chernikov * 29110ab2de0SAlexander V. Chernikov */ 2923b3a8eb9SGleb Smirnoff static int 2933b3a8eb9SGleb Smirnoff ipfw_nat(struct ip_fw_args *args, struct cfg_nat *t, struct mbuf *m) 2943b3a8eb9SGleb Smirnoff { 2953b3a8eb9SGleb Smirnoff struct mbuf *mcl; 2963b3a8eb9SGleb Smirnoff struct ip *ip; 2973b3a8eb9SGleb Smirnoff /* XXX - libalias duct tape */ 2983b3a8eb9SGleb Smirnoff int ldt, retval, found; 2993b3a8eb9SGleb Smirnoff struct ip_fw_chain *chain; 3003b3a8eb9SGleb Smirnoff char *c; 3013b3a8eb9SGleb Smirnoff 3023b3a8eb9SGleb Smirnoff ldt = 0; 3033b3a8eb9SGleb Smirnoff retval = 0; 3043b3a8eb9SGleb Smirnoff mcl = m_megapullup(m, m->m_pkthdr.len); 3053b3a8eb9SGleb Smirnoff if (mcl == NULL) { 3063b3a8eb9SGleb Smirnoff args->m = NULL; 3073b3a8eb9SGleb Smirnoff return (IP_FW_DENY); 3083b3a8eb9SGleb Smirnoff } 3093b3a8eb9SGleb Smirnoff ip = mtod(mcl, struct ip *); 3103b3a8eb9SGleb Smirnoff 3113b3a8eb9SGleb Smirnoff /* 3123b3a8eb9SGleb Smirnoff * XXX - Libalias checksum offload 'duct tape': 3133b3a8eb9SGleb Smirnoff * 3143b3a8eb9SGleb Smirnoff * locally generated packets have only pseudo-header checksum 3153b3a8eb9SGleb Smirnoff * calculated and libalias will break it[1], so mark them for 3163b3a8eb9SGleb Smirnoff * later fix. Moreover there are cases when libalias modifies 3173b3a8eb9SGleb Smirnoff * tcp packet data[2], mark them for later fix too. 3183b3a8eb9SGleb Smirnoff * 3193b3a8eb9SGleb Smirnoff * [1] libalias was never meant to run in kernel, so it does 3203b3a8eb9SGleb Smirnoff * not have any knowledge about checksum offloading, and 3213b3a8eb9SGleb Smirnoff * expects a packet with a full internet checksum. 3223b3a8eb9SGleb Smirnoff * Unfortunately, packets generated locally will have just the 3233b3a8eb9SGleb Smirnoff * pseudo header calculated, and when libalias tries to adjust 3243b3a8eb9SGleb Smirnoff * the checksum it will actually compute a wrong value. 3253b3a8eb9SGleb Smirnoff * 3263b3a8eb9SGleb Smirnoff * [2] when libalias modifies tcp's data content, full TCP 3273b3a8eb9SGleb Smirnoff * checksum has to be recomputed: the problem is that 3283b3a8eb9SGleb Smirnoff * libalias does not have any idea about checksum offloading. 3293b3a8eb9SGleb Smirnoff * To work around this, we do not do checksumming in LibAlias, 3303b3a8eb9SGleb Smirnoff * but only mark the packets in th_x2 field. If we receive a 3313b3a8eb9SGleb Smirnoff * marked packet, we calculate correct checksum for it 3323b3a8eb9SGleb Smirnoff * aware of offloading. Why such a terrible hack instead of 3333b3a8eb9SGleb Smirnoff * recalculating checksum for each packet? 3343b3a8eb9SGleb Smirnoff * Because the previous checksum was not checked! 3353b3a8eb9SGleb Smirnoff * Recalculating checksums for EVERY packet will hide ALL 3363b3a8eb9SGleb Smirnoff * transmission errors. Yes, marked packets still suffer from 3373b3a8eb9SGleb Smirnoff * this problem. But, sigh, natd(8) has this problem, too. 3383b3a8eb9SGleb Smirnoff * 3393b3a8eb9SGleb Smirnoff * TODO: -make libalias mbuf aware (so 3403b3a8eb9SGleb Smirnoff * it can handle delayed checksum and tso) 3413b3a8eb9SGleb Smirnoff */ 3423b3a8eb9SGleb Smirnoff 3433b3a8eb9SGleb Smirnoff if (mcl->m_pkthdr.rcvif == NULL && 3443b3a8eb9SGleb Smirnoff mcl->m_pkthdr.csum_flags & CSUM_DELAY_DATA) 3453b3a8eb9SGleb Smirnoff ldt = 1; 3463b3a8eb9SGleb Smirnoff 3473b3a8eb9SGleb Smirnoff c = mtod(mcl, char *); 3483b3a8eb9SGleb Smirnoff 3493b3a8eb9SGleb Smirnoff /* Check if this is 'global' instance */ 3503b3a8eb9SGleb Smirnoff if (t == NULL) { 351b7795b67SGleb Smirnoff if (args->flags & IPFW_ARGS_IN) { 3523b3a8eb9SGleb Smirnoff /* Wrong direction, skip processing */ 3533b3a8eb9SGleb Smirnoff args->m = mcl; 3543b3a8eb9SGleb Smirnoff return (IP_FW_NAT); 3553b3a8eb9SGleb Smirnoff } 3563b3a8eb9SGleb Smirnoff 3573b3a8eb9SGleb Smirnoff found = 0; 3583b3a8eb9SGleb Smirnoff chain = &V_layer3_chain; 3595d0cd926SAlexander V. Chernikov IPFW_RLOCK_ASSERT(chain); 3603b3a8eb9SGleb Smirnoff /* Check every nat entry... */ 3613b3a8eb9SGleb Smirnoff LIST_FOREACH(t, &chain->nat, _next) { 3623b3a8eb9SGleb Smirnoff if ((t->mode & PKT_ALIAS_SKIP_GLOBAL) != 0) 3633b3a8eb9SGleb Smirnoff continue; 3643b3a8eb9SGleb Smirnoff retval = LibAliasOutTry(t->lib, c, 3653b3a8eb9SGleb Smirnoff mcl->m_len + M_TRAILINGSPACE(mcl), 0); 3663b3a8eb9SGleb Smirnoff if (retval == PKT_ALIAS_OK) { 3673b3a8eb9SGleb Smirnoff /* Nat instance recognises state */ 3683b3a8eb9SGleb Smirnoff found = 1; 3693b3a8eb9SGleb Smirnoff break; 3703b3a8eb9SGleb Smirnoff } 3713b3a8eb9SGleb Smirnoff } 3723b3a8eb9SGleb Smirnoff if (found != 1) { 3733b3a8eb9SGleb Smirnoff /* No instance found, return ignore */ 3743b3a8eb9SGleb Smirnoff args->m = mcl; 3753b3a8eb9SGleb Smirnoff return (IP_FW_NAT); 3763b3a8eb9SGleb Smirnoff } 3773b3a8eb9SGleb Smirnoff } else { 378b7795b67SGleb Smirnoff if (args->flags & IPFW_ARGS_IN) 3793b3a8eb9SGleb Smirnoff retval = LibAliasIn(t->lib, c, 3803b3a8eb9SGleb Smirnoff mcl->m_len + M_TRAILINGSPACE(mcl)); 3813b3a8eb9SGleb Smirnoff else 3823b3a8eb9SGleb Smirnoff retval = LibAliasOut(t->lib, c, 3833b3a8eb9SGleb Smirnoff mcl->m_len + M_TRAILINGSPACE(mcl)); 3843b3a8eb9SGleb Smirnoff } 3853b3a8eb9SGleb Smirnoff 3863b3a8eb9SGleb Smirnoff /* 3873b3a8eb9SGleb Smirnoff * We drop packet when: 3883b3a8eb9SGleb Smirnoff * 1. libalias returns PKT_ALIAS_ERROR; 3893b3a8eb9SGleb Smirnoff * 2. For incoming packets: 3903b3a8eb9SGleb Smirnoff * a) for unresolved fragments; 3913b3a8eb9SGleb Smirnoff * b) libalias returns PKT_ALIAS_IGNORED and 3923b3a8eb9SGleb Smirnoff * PKT_ALIAS_DENY_INCOMING flag is set. 3933b3a8eb9SGleb Smirnoff */ 3943b3a8eb9SGleb Smirnoff if (retval == PKT_ALIAS_ERROR || 395b7795b67SGleb Smirnoff ((args->flags & IPFW_ARGS_IN) && 396b7795b67SGleb Smirnoff (retval == PKT_ALIAS_UNRESOLVED_FRAGMENT || 3973b3a8eb9SGleb Smirnoff (retval == PKT_ALIAS_IGNORED && 3983b3a8eb9SGleb Smirnoff (t->mode & PKT_ALIAS_DENY_INCOMING) != 0)))) { 3993b3a8eb9SGleb Smirnoff /* XXX - should i add some logging? */ 4003b3a8eb9SGleb Smirnoff m_free(mcl); 4013b3a8eb9SGleb Smirnoff args->m = NULL; 4023b3a8eb9SGleb Smirnoff return (IP_FW_DENY); 4033b3a8eb9SGleb Smirnoff } 4043b3a8eb9SGleb Smirnoff 4053b3a8eb9SGleb Smirnoff if (retval == PKT_ALIAS_RESPOND) 4063b3a8eb9SGleb Smirnoff mcl->m_flags |= M_SKIP_FIREWALL; 4073b3a8eb9SGleb Smirnoff mcl->m_pkthdr.len = mcl->m_len = ntohs(ip->ip_len); 4083b3a8eb9SGleb Smirnoff 4093b3a8eb9SGleb Smirnoff /* 4103b3a8eb9SGleb Smirnoff * XXX - libalias checksum offload 4113b3a8eb9SGleb Smirnoff * 'duct tape' (see above) 4123b3a8eb9SGleb Smirnoff */ 4133b3a8eb9SGleb Smirnoff 4143b3a8eb9SGleb Smirnoff if ((ip->ip_off & htons(IP_OFFMASK)) == 0 && 4153b3a8eb9SGleb Smirnoff ip->ip_p == IPPROTO_TCP) { 4163b3a8eb9SGleb Smirnoff struct tcphdr *th; 4173b3a8eb9SGleb Smirnoff 4183b3a8eb9SGleb Smirnoff th = (struct tcphdr *)(ip + 1); 4193b3a8eb9SGleb Smirnoff if (th->th_x2) 4203b3a8eb9SGleb Smirnoff ldt = 1; 4213b3a8eb9SGleb Smirnoff } 4223b3a8eb9SGleb Smirnoff 4233b3a8eb9SGleb Smirnoff if (ldt) { 4243b3a8eb9SGleb Smirnoff struct tcphdr *th; 4253b3a8eb9SGleb Smirnoff struct udphdr *uh; 42623e9c6dcSGleb Smirnoff uint16_t ip_len, cksum; 4273b3a8eb9SGleb Smirnoff 42823e9c6dcSGleb Smirnoff ip_len = ntohs(ip->ip_len); 4293b3a8eb9SGleb Smirnoff cksum = in_pseudo(ip->ip_src.s_addr, ip->ip_dst.s_addr, 43023e9c6dcSGleb Smirnoff htons(ip->ip_p + ip_len - (ip->ip_hl << 2))); 4313b3a8eb9SGleb Smirnoff 4323b3a8eb9SGleb Smirnoff switch (ip->ip_p) { 4333b3a8eb9SGleb Smirnoff case IPPROTO_TCP: 4343b3a8eb9SGleb Smirnoff th = (struct tcphdr *)(ip + 1); 4353b3a8eb9SGleb Smirnoff /* 4363b3a8eb9SGleb Smirnoff * Maybe it was set in 4373b3a8eb9SGleb Smirnoff * libalias... 4383b3a8eb9SGleb Smirnoff */ 4393b3a8eb9SGleb Smirnoff th->th_x2 = 0; 4403b3a8eb9SGleb Smirnoff th->th_sum = cksum; 4413b3a8eb9SGleb Smirnoff mcl->m_pkthdr.csum_data = 4423b3a8eb9SGleb Smirnoff offsetof(struct tcphdr, th_sum); 4433b3a8eb9SGleb Smirnoff break; 4443b3a8eb9SGleb Smirnoff case IPPROTO_UDP: 4453b3a8eb9SGleb Smirnoff uh = (struct udphdr *)(ip + 1); 4463b3a8eb9SGleb Smirnoff uh->uh_sum = cksum; 4473b3a8eb9SGleb Smirnoff mcl->m_pkthdr.csum_data = 4483b3a8eb9SGleb Smirnoff offsetof(struct udphdr, uh_sum); 4493b3a8eb9SGleb Smirnoff break; 4503b3a8eb9SGleb Smirnoff } 4513b3a8eb9SGleb Smirnoff /* No hw checksum offloading: do it ourselves */ 4523b3a8eb9SGleb Smirnoff if ((mcl->m_pkthdr.csum_flags & CSUM_DELAY_DATA) == 0) { 4533b3a8eb9SGleb Smirnoff in_delayed_cksum(mcl); 4543b3a8eb9SGleb Smirnoff mcl->m_pkthdr.csum_flags &= ~CSUM_DELAY_DATA; 4553b3a8eb9SGleb Smirnoff } 4563b3a8eb9SGleb Smirnoff } 4573b3a8eb9SGleb Smirnoff args->m = mcl; 4583b3a8eb9SGleb Smirnoff return (IP_FW_NAT); 4593b3a8eb9SGleb Smirnoff } 4603b3a8eb9SGleb Smirnoff 4613b3a8eb9SGleb Smirnoff static struct cfg_nat * 4623b3a8eb9SGleb Smirnoff lookup_nat(struct nat_list *l, int nat_id) 4633b3a8eb9SGleb Smirnoff { 4643b3a8eb9SGleb Smirnoff struct cfg_nat *res; 4653b3a8eb9SGleb Smirnoff 4663b3a8eb9SGleb Smirnoff LIST_FOREACH(res, l, _next) { 4673b3a8eb9SGleb Smirnoff if (res->id == nat_id) 4683b3a8eb9SGleb Smirnoff break; 4693b3a8eb9SGleb Smirnoff } 4703b3a8eb9SGleb Smirnoff return res; 4713b3a8eb9SGleb Smirnoff } 4723b3a8eb9SGleb Smirnoff 473d6164b77SAlexander V. Chernikov static struct cfg_nat * 474d6164b77SAlexander V. Chernikov lookup_nat_name(struct nat_list *l, char *name) 4753b3a8eb9SGleb Smirnoff { 476d6164b77SAlexander V. Chernikov struct cfg_nat *res; 477d6164b77SAlexander V. Chernikov int id; 478d6164b77SAlexander V. Chernikov char *errptr; 4793b3a8eb9SGleb Smirnoff 480d6164b77SAlexander V. Chernikov id = strtol(name, &errptr, 10); 481d6164b77SAlexander V. Chernikov if (id == 0 || *errptr != '\0') 482d6164b77SAlexander V. Chernikov return (NULL); 4833b3a8eb9SGleb Smirnoff 484d6164b77SAlexander V. Chernikov LIST_FOREACH(res, l, _next) { 485d6164b77SAlexander V. Chernikov if (res->id == id) 486d6164b77SAlexander V. Chernikov break; 4873b3a8eb9SGleb Smirnoff } 488d6164b77SAlexander V. Chernikov return (res); 489d6164b77SAlexander V. Chernikov } 490d6164b77SAlexander V. Chernikov 491d6164b77SAlexander V. Chernikov /* IP_FW3 configuration routines */ 492d6164b77SAlexander V. Chernikov 493d6164b77SAlexander V. Chernikov static void 494d6164b77SAlexander V. Chernikov nat44_config(struct ip_fw_chain *chain, struct nat44_cfg_nat *ucfg) 495d6164b77SAlexander V. Chernikov { 496d6164b77SAlexander V. Chernikov struct cfg_nat *ptr, *tcfg; 497d6164b77SAlexander V. Chernikov int gencnt; 4983b3a8eb9SGleb Smirnoff 4993b3a8eb9SGleb Smirnoff /* 5003b3a8eb9SGleb Smirnoff * Find/create nat rule. 5013b3a8eb9SGleb Smirnoff */ 502d6164b77SAlexander V. Chernikov IPFW_UH_WLOCK(chain); 5033b3a8eb9SGleb Smirnoff gencnt = chain->gencnt; 504d6164b77SAlexander V. Chernikov ptr = lookup_nat_name(&chain->nat, ucfg->name); 5053b3a8eb9SGleb Smirnoff if (ptr == NULL) { 506d6164b77SAlexander V. Chernikov IPFW_UH_WUNLOCK(chain); 5073b3a8eb9SGleb Smirnoff /* New rule: allocate and init new instance. */ 5083b3a8eb9SGleb Smirnoff ptr = malloc(sizeof(struct cfg_nat), M_IPFW, M_WAITOK | M_ZERO); 5093b3a8eb9SGleb Smirnoff ptr->lib = LibAliasInit(NULL); 5103b3a8eb9SGleb Smirnoff LIST_INIT(&ptr->redir_chain); 5113b3a8eb9SGleb Smirnoff } else { 5123b3a8eb9SGleb Smirnoff /* Entry already present: temporarily unhook it. */ 513d6164b77SAlexander V. Chernikov IPFW_WLOCK(chain); 5143b3a8eb9SGleb Smirnoff LIST_REMOVE(ptr, _next); 515d6164b77SAlexander V. Chernikov flush_nat_ptrs(chain, ptr->id); 5163b3a8eb9SGleb Smirnoff IPFW_WUNLOCK(chain); 517d6164b77SAlexander V. Chernikov IPFW_UH_WUNLOCK(chain); 5183b3a8eb9SGleb Smirnoff } 5193b3a8eb9SGleb Smirnoff 5203b3a8eb9SGleb Smirnoff /* 521d6164b77SAlexander V. Chernikov * Basic nat (re)configuration. 5223b3a8eb9SGleb Smirnoff */ 523d6164b77SAlexander V. Chernikov ptr->id = strtol(ucfg->name, NULL, 10); 5243b3a8eb9SGleb Smirnoff /* 5253b3a8eb9SGleb Smirnoff * XXX - what if this rule doesn't nat any ip and just 5263b3a8eb9SGleb Smirnoff * redirect? 5273b3a8eb9SGleb Smirnoff * do we set aliasaddress to 0.0.0.0? 5283b3a8eb9SGleb Smirnoff */ 529d6164b77SAlexander V. Chernikov ptr->ip = ucfg->ip; 530d6164b77SAlexander V. Chernikov ptr->redir_cnt = ucfg->redir_cnt; 531d6164b77SAlexander V. Chernikov ptr->mode = ucfg->mode; 532d6164b77SAlexander V. Chernikov strlcpy(ptr->if_name, ucfg->if_name, sizeof(ptr->if_name)); 533d6164b77SAlexander V. Chernikov LibAliasSetMode(ptr->lib, ptr->mode, ~0); 5343b3a8eb9SGleb Smirnoff LibAliasSetAddress(ptr->lib, ptr->ip); 5353b3a8eb9SGleb Smirnoff 5363b3a8eb9SGleb Smirnoff /* 5373b3a8eb9SGleb Smirnoff * Redir and LSNAT configuration. 5383b3a8eb9SGleb Smirnoff */ 5393b3a8eb9SGleb Smirnoff /* Delete old cfgs. */ 5403b3a8eb9SGleb Smirnoff del_redir_spool_cfg(ptr, &ptr->redir_chain); 5413b3a8eb9SGleb Smirnoff /* Add new entries. */ 542d6164b77SAlexander V. Chernikov add_redir_spool_cfg((char *)(ucfg + 1), ptr); 543d6164b77SAlexander V. Chernikov IPFW_UH_WLOCK(chain); 5443b3a8eb9SGleb Smirnoff 5453b3a8eb9SGleb Smirnoff /* Extra check to avoid race with another ipfw_nat_cfg() */ 546d6164b77SAlexander V. Chernikov tcfg = NULL; 547d6164b77SAlexander V. Chernikov if (gencnt != chain->gencnt) 548d6164b77SAlexander V. Chernikov tcfg = lookup_nat_name(&chain->nat, ucfg->name); 549d6164b77SAlexander V. Chernikov IPFW_WLOCK(chain); 550d6164b77SAlexander V. Chernikov if (tcfg != NULL) 551d6164b77SAlexander V. Chernikov LIST_REMOVE(tcfg, _next); 5523b3a8eb9SGleb Smirnoff LIST_INSERT_HEAD(&chain->nat, ptr, _next); 5533b3a8eb9SGleb Smirnoff IPFW_WUNLOCK(chain); 554d6164b77SAlexander V. Chernikov chain->gencnt++; 555d6164b77SAlexander V. Chernikov 556d6164b77SAlexander V. Chernikov IPFW_UH_WUNLOCK(chain); 557d6164b77SAlexander V. Chernikov 558d6164b77SAlexander V. Chernikov if (tcfg != NULL) 5591a458088SAlexander V. Chernikov free_nat_instance(ptr); 560d6164b77SAlexander V. Chernikov } 561d6164b77SAlexander V. Chernikov 562d6164b77SAlexander V. Chernikov /* 563d6164b77SAlexander V. Chernikov * Creates/configure nat44 instance 564d6164b77SAlexander V. Chernikov * Data layout (v0)(current): 565d6164b77SAlexander V. Chernikov * Request: [ ipfw_obj_header nat44_cfg_nat .. ] 566d6164b77SAlexander V. Chernikov * 567d6164b77SAlexander V. Chernikov * Returns 0 on success 568d6164b77SAlexander V. Chernikov */ 569d6164b77SAlexander V. Chernikov static int 570d6164b77SAlexander V. Chernikov nat44_cfg(struct ip_fw_chain *chain, ip_fw3_opheader *op3, 571d6164b77SAlexander V. Chernikov struct sockopt_data *sd) 572d6164b77SAlexander V. Chernikov { 573d6164b77SAlexander V. Chernikov ipfw_obj_header *oh; 574d6164b77SAlexander V. Chernikov struct nat44_cfg_nat *ucfg; 575d6164b77SAlexander V. Chernikov int id; 576d6164b77SAlexander V. Chernikov size_t read; 577d6164b77SAlexander V. Chernikov char *errptr; 578d6164b77SAlexander V. Chernikov 579d6164b77SAlexander V. Chernikov /* Check minimum header size */ 580d6164b77SAlexander V. Chernikov if (sd->valsize < (sizeof(*oh) + sizeof(*ucfg))) 581d6164b77SAlexander V. Chernikov return (EINVAL); 582d6164b77SAlexander V. Chernikov 583d6164b77SAlexander V. Chernikov oh = (ipfw_obj_header *)sd->kbuf; 584d6164b77SAlexander V. Chernikov 585d6164b77SAlexander V. Chernikov /* Basic length checks for TLVs */ 586d6164b77SAlexander V. Chernikov if (oh->ntlv.head.length != sizeof(oh->ntlv)) 587d6164b77SAlexander V. Chernikov return (EINVAL); 588d6164b77SAlexander V. Chernikov 589d6164b77SAlexander V. Chernikov ucfg = (struct nat44_cfg_nat *)(oh + 1); 590d6164b77SAlexander V. Chernikov 591d6164b77SAlexander V. Chernikov /* Check if name is properly terminated and looks like number */ 592d6164b77SAlexander V. Chernikov if (strnlen(ucfg->name, sizeof(ucfg->name)) == sizeof(ucfg->name)) 593d6164b77SAlexander V. Chernikov return (EINVAL); 594d6164b77SAlexander V. Chernikov id = strtol(ucfg->name, &errptr, 10); 595d6164b77SAlexander V. Chernikov if (id == 0 || *errptr != '\0') 596d6164b77SAlexander V. Chernikov return (EINVAL); 597d6164b77SAlexander V. Chernikov 598d6164b77SAlexander V. Chernikov read = sizeof(*oh) + sizeof(*ucfg); 599d6164b77SAlexander V. Chernikov /* Check number of redirs */ 600d6164b77SAlexander V. Chernikov if (sd->valsize < read + ucfg->redir_cnt*sizeof(struct nat44_cfg_redir)) 601d6164b77SAlexander V. Chernikov return (EINVAL); 602d6164b77SAlexander V. Chernikov 603d6164b77SAlexander V. Chernikov nat44_config(chain, ucfg); 604d6164b77SAlexander V. Chernikov return (0); 605d6164b77SAlexander V. Chernikov } 606d6164b77SAlexander V. Chernikov 607d6164b77SAlexander V. Chernikov /* 608d6164b77SAlexander V. Chernikov * Destroys given nat instances. 609d6164b77SAlexander V. Chernikov * Data layout (v0)(current): 610d6164b77SAlexander V. Chernikov * Request: [ ipfw_obj_header ] 611d6164b77SAlexander V. Chernikov * 612d6164b77SAlexander V. Chernikov * Returns 0 on success 613d6164b77SAlexander V. Chernikov */ 614d6164b77SAlexander V. Chernikov static int 615d6164b77SAlexander V. Chernikov nat44_destroy(struct ip_fw_chain *chain, ip_fw3_opheader *op3, 616d6164b77SAlexander V. Chernikov struct sockopt_data *sd) 617d6164b77SAlexander V. Chernikov { 618d6164b77SAlexander V. Chernikov ipfw_obj_header *oh; 619d6164b77SAlexander V. Chernikov struct cfg_nat *ptr; 620d6164b77SAlexander V. Chernikov ipfw_obj_ntlv *ntlv; 621d6164b77SAlexander V. Chernikov 622d6164b77SAlexander V. Chernikov /* Check minimum header size */ 623d6164b77SAlexander V. Chernikov if (sd->valsize < sizeof(*oh)) 624d6164b77SAlexander V. Chernikov return (EINVAL); 625d6164b77SAlexander V. Chernikov 626d6164b77SAlexander V. Chernikov oh = (ipfw_obj_header *)sd->kbuf; 627d6164b77SAlexander V. Chernikov 628d6164b77SAlexander V. Chernikov /* Basic length checks for TLVs */ 629d6164b77SAlexander V. Chernikov if (oh->ntlv.head.length != sizeof(oh->ntlv)) 630d6164b77SAlexander V. Chernikov return (EINVAL); 631d6164b77SAlexander V. Chernikov 632d6164b77SAlexander V. Chernikov ntlv = &oh->ntlv; 633d6164b77SAlexander V. Chernikov /* Check if name is properly terminated */ 634d6164b77SAlexander V. Chernikov if (strnlen(ntlv->name, sizeof(ntlv->name)) == sizeof(ntlv->name)) 635d6164b77SAlexander V. Chernikov return (EINVAL); 636d6164b77SAlexander V. Chernikov 637d6164b77SAlexander V. Chernikov IPFW_UH_WLOCK(chain); 638d6164b77SAlexander V. Chernikov ptr = lookup_nat_name(&chain->nat, ntlv->name); 639d6164b77SAlexander V. Chernikov if (ptr == NULL) { 640d6164b77SAlexander V. Chernikov IPFW_UH_WUNLOCK(chain); 641d6164b77SAlexander V. Chernikov return (ESRCH); 642d6164b77SAlexander V. Chernikov } 643d6164b77SAlexander V. Chernikov IPFW_WLOCK(chain); 644d6164b77SAlexander V. Chernikov LIST_REMOVE(ptr, _next); 645d6164b77SAlexander V. Chernikov flush_nat_ptrs(chain, ptr->id); 646d6164b77SAlexander V. Chernikov IPFW_WUNLOCK(chain); 647d6164b77SAlexander V. Chernikov IPFW_UH_WUNLOCK(chain); 648d6164b77SAlexander V. Chernikov 6491a458088SAlexander V. Chernikov free_nat_instance(ptr); 650d6164b77SAlexander V. Chernikov 651d6164b77SAlexander V. Chernikov return (0); 652d6164b77SAlexander V. Chernikov } 653d6164b77SAlexander V. Chernikov 654d6164b77SAlexander V. Chernikov static void 655d6164b77SAlexander V. Chernikov export_nat_cfg(struct cfg_nat *ptr, struct nat44_cfg_nat *ucfg) 656d6164b77SAlexander V. Chernikov { 657d6164b77SAlexander V. Chernikov 658d6164b77SAlexander V. Chernikov snprintf(ucfg->name, sizeof(ucfg->name), "%d", ptr->id); 659d6164b77SAlexander V. Chernikov ucfg->ip = ptr->ip; 660d6164b77SAlexander V. Chernikov ucfg->redir_cnt = ptr->redir_cnt; 661d6164b77SAlexander V. Chernikov ucfg->mode = ptr->mode; 662d6164b77SAlexander V. Chernikov strlcpy(ucfg->if_name, ptr->if_name, sizeof(ucfg->if_name)); 663d6164b77SAlexander V. Chernikov } 664d6164b77SAlexander V. Chernikov 665d6164b77SAlexander V. Chernikov /* 666d6164b77SAlexander V. Chernikov * Gets config for given nat instance 667d6164b77SAlexander V. Chernikov * Data layout (v0)(current): 668d6164b77SAlexander V. Chernikov * Request: [ ipfw_obj_header nat44_cfg_nat .. ] 669d6164b77SAlexander V. Chernikov * 670d6164b77SAlexander V. Chernikov * Returns 0 on success 671d6164b77SAlexander V. Chernikov */ 672d6164b77SAlexander V. Chernikov static int 673d6164b77SAlexander V. Chernikov nat44_get_cfg(struct ip_fw_chain *chain, ip_fw3_opheader *op3, 674d6164b77SAlexander V. Chernikov struct sockopt_data *sd) 675d6164b77SAlexander V. Chernikov { 676d6164b77SAlexander V. Chernikov ipfw_obj_header *oh; 677d6164b77SAlexander V. Chernikov struct nat44_cfg_nat *ucfg; 678d6164b77SAlexander V. Chernikov struct cfg_nat *ptr; 679d6164b77SAlexander V. Chernikov struct cfg_redir *r; 680d6164b77SAlexander V. Chernikov struct cfg_spool *s; 681d6164b77SAlexander V. Chernikov struct nat44_cfg_redir *ser_r; 682d6164b77SAlexander V. Chernikov struct nat44_cfg_spool *ser_s; 683d6164b77SAlexander V. Chernikov size_t sz; 684d6164b77SAlexander V. Chernikov 685d6164b77SAlexander V. Chernikov sz = sizeof(*oh) + sizeof(*ucfg); 686d6164b77SAlexander V. Chernikov /* Check minimum header size */ 687d6164b77SAlexander V. Chernikov if (sd->valsize < sz) 688d6164b77SAlexander V. Chernikov return (EINVAL); 689d6164b77SAlexander V. Chernikov 690d6164b77SAlexander V. Chernikov oh = (struct _ipfw_obj_header *)ipfw_get_sopt_header(sd, sz); 691d6164b77SAlexander V. Chernikov 692d6164b77SAlexander V. Chernikov /* Basic length checks for TLVs */ 693d6164b77SAlexander V. Chernikov if (oh->ntlv.head.length != sizeof(oh->ntlv)) 694d6164b77SAlexander V. Chernikov return (EINVAL); 695d6164b77SAlexander V. Chernikov 696d6164b77SAlexander V. Chernikov ucfg = (struct nat44_cfg_nat *)(oh + 1); 697d6164b77SAlexander V. Chernikov 698d6164b77SAlexander V. Chernikov /* Check if name is properly terminated */ 699d6164b77SAlexander V. Chernikov if (strnlen(ucfg->name, sizeof(ucfg->name)) == sizeof(ucfg->name)) 700d6164b77SAlexander V. Chernikov return (EINVAL); 701d6164b77SAlexander V. Chernikov 702d6164b77SAlexander V. Chernikov IPFW_UH_RLOCK(chain); 703d6164b77SAlexander V. Chernikov ptr = lookup_nat_name(&chain->nat, ucfg->name); 704d6164b77SAlexander V. Chernikov if (ptr == NULL) { 705d6164b77SAlexander V. Chernikov IPFW_UH_RUNLOCK(chain); 706d6164b77SAlexander V. Chernikov return (ESRCH); 707d6164b77SAlexander V. Chernikov } 708d6164b77SAlexander V. Chernikov 709d6164b77SAlexander V. Chernikov export_nat_cfg(ptr, ucfg); 710d6164b77SAlexander V. Chernikov 711d6164b77SAlexander V. Chernikov /* Estimate memory amount */ 7129f925e8aSAlexander V. Chernikov sz = sizeof(ipfw_obj_header) + sizeof(struct nat44_cfg_nat); 713d6164b77SAlexander V. Chernikov LIST_FOREACH(r, &ptr->redir_chain, _next) { 714d6164b77SAlexander V. Chernikov sz += sizeof(struct nat44_cfg_redir); 715d6164b77SAlexander V. Chernikov LIST_FOREACH(s, &r->spool_chain, _next) 716d6164b77SAlexander V. Chernikov sz += sizeof(struct nat44_cfg_spool); 717d6164b77SAlexander V. Chernikov } 718d6164b77SAlexander V. Chernikov 719d6164b77SAlexander V. Chernikov ucfg->size = sz; 7209f925e8aSAlexander V. Chernikov if (sd->valsize < sz) { 721d6164b77SAlexander V. Chernikov 722d6164b77SAlexander V. Chernikov /* 723d6164b77SAlexander V. Chernikov * Submitted buffer size is not enough. 724d6164b77SAlexander V. Chernikov * WE've already filled in @ucfg structure with 725d6164b77SAlexander V. Chernikov * relevant info including size, so we 726d6164b77SAlexander V. Chernikov * can return. Buffer will be flushed automatically. 727d6164b77SAlexander V. Chernikov */ 728d6164b77SAlexander V. Chernikov IPFW_UH_RUNLOCK(chain); 729d6164b77SAlexander V. Chernikov return (ENOMEM); 730d6164b77SAlexander V. Chernikov } 731d6164b77SAlexander V. Chernikov 732d6164b77SAlexander V. Chernikov /* Size OK, let's copy data */ 733d6164b77SAlexander V. Chernikov LIST_FOREACH(r, &ptr->redir_chain, _next) { 734d6164b77SAlexander V. Chernikov ser_r = (struct nat44_cfg_redir *)ipfw_get_sopt_space(sd, 735d6164b77SAlexander V. Chernikov sizeof(*ser_r)); 736d6164b77SAlexander V. Chernikov ser_r->mode = r->mode; 737d6164b77SAlexander V. Chernikov ser_r->laddr = r->laddr; 738d6164b77SAlexander V. Chernikov ser_r->paddr = r->paddr; 739d6164b77SAlexander V. Chernikov ser_r->raddr = r->raddr; 740d6164b77SAlexander V. Chernikov ser_r->lport = r->lport; 741d6164b77SAlexander V. Chernikov ser_r->pport = r->pport; 742d6164b77SAlexander V. Chernikov ser_r->rport = r->rport; 743d6164b77SAlexander V. Chernikov ser_r->pport_cnt = r->pport_cnt; 744d6164b77SAlexander V. Chernikov ser_r->rport_cnt = r->rport_cnt; 745d6164b77SAlexander V. Chernikov ser_r->proto = r->proto; 746d6164b77SAlexander V. Chernikov ser_r->spool_cnt = r->spool_cnt; 747d6164b77SAlexander V. Chernikov 748d6164b77SAlexander V. Chernikov LIST_FOREACH(s, &r->spool_chain, _next) { 749d6164b77SAlexander V. Chernikov ser_s = (struct nat44_cfg_spool *)ipfw_get_sopt_space( 750d6164b77SAlexander V. Chernikov sd, sizeof(*ser_s)); 751d6164b77SAlexander V. Chernikov 752d6164b77SAlexander V. Chernikov ser_s->addr = s->addr; 753d6164b77SAlexander V. Chernikov ser_s->port = s->port; 754d6164b77SAlexander V. Chernikov } 755d6164b77SAlexander V. Chernikov } 756d6164b77SAlexander V. Chernikov 757d6164b77SAlexander V. Chernikov IPFW_UH_RUNLOCK(chain); 758d6164b77SAlexander V. Chernikov 759d6164b77SAlexander V. Chernikov return (0); 760d6164b77SAlexander V. Chernikov } 761d6164b77SAlexander V. Chernikov 762d6164b77SAlexander V. Chernikov /* 763d6164b77SAlexander V. Chernikov * Lists all nat44 instances currently available in kernel. 764d6164b77SAlexander V. Chernikov * Data layout (v0)(current): 765d6164b77SAlexander V. Chernikov * Request: [ ipfw_obj_lheader ] 766d6164b77SAlexander V. Chernikov * Reply: [ ipfw_obj_lheader nat44_cfg_nat x N ] 767d6164b77SAlexander V. Chernikov * 768d6164b77SAlexander V. Chernikov * Returns 0 on success 769d6164b77SAlexander V. Chernikov */ 770d6164b77SAlexander V. Chernikov static int 771d6164b77SAlexander V. Chernikov nat44_list_nat(struct ip_fw_chain *chain, ip_fw3_opheader *op3, 772d6164b77SAlexander V. Chernikov struct sockopt_data *sd) 773d6164b77SAlexander V. Chernikov { 774d6164b77SAlexander V. Chernikov ipfw_obj_lheader *olh; 775d6164b77SAlexander V. Chernikov struct nat44_cfg_nat *ucfg; 776d6164b77SAlexander V. Chernikov struct cfg_nat *ptr; 777d6164b77SAlexander V. Chernikov int nat_count; 778d6164b77SAlexander V. Chernikov 779d6164b77SAlexander V. Chernikov /* Check minimum header size */ 780d6164b77SAlexander V. Chernikov if (sd->valsize < sizeof(ipfw_obj_lheader)) 781d6164b77SAlexander V. Chernikov return (EINVAL); 782d6164b77SAlexander V. Chernikov 783d6164b77SAlexander V. Chernikov olh = (ipfw_obj_lheader *)ipfw_get_sopt_header(sd, sizeof(*olh)); 784d6164b77SAlexander V. Chernikov IPFW_UH_RLOCK(chain); 785d6164b77SAlexander V. Chernikov nat_count = 0; 786d6164b77SAlexander V. Chernikov LIST_FOREACH(ptr, &chain->nat, _next) 787d6164b77SAlexander V. Chernikov nat_count++; 788d6164b77SAlexander V. Chernikov 789d6164b77SAlexander V. Chernikov olh->count = nat_count; 790d6164b77SAlexander V. Chernikov olh->objsize = sizeof(struct nat44_cfg_nat); 791d6164b77SAlexander V. Chernikov olh->size = sizeof(*olh) + olh->count * olh->objsize; 792d6164b77SAlexander V. Chernikov 793d6164b77SAlexander V. Chernikov if (sd->valsize < olh->size) { 794d6164b77SAlexander V. Chernikov IPFW_UH_RUNLOCK(chain); 795d6164b77SAlexander V. Chernikov return (ENOMEM); 796d6164b77SAlexander V. Chernikov } 797d6164b77SAlexander V. Chernikov 798d6164b77SAlexander V. Chernikov LIST_FOREACH(ptr, &chain->nat, _next) { 799d6164b77SAlexander V. Chernikov ucfg = (struct nat44_cfg_nat *)ipfw_get_sopt_space(sd, 800d6164b77SAlexander V. Chernikov sizeof(*ucfg)); 801d6164b77SAlexander V. Chernikov export_nat_cfg(ptr, ucfg); 802d6164b77SAlexander V. Chernikov } 803d6164b77SAlexander V. Chernikov 804d6164b77SAlexander V. Chernikov IPFW_UH_RUNLOCK(chain); 805d6164b77SAlexander V. Chernikov 806d6164b77SAlexander V. Chernikov return (0); 807d6164b77SAlexander V. Chernikov } 808d6164b77SAlexander V. Chernikov 809d6164b77SAlexander V. Chernikov /* 810d6164b77SAlexander V. Chernikov * Gets log for given nat instance 811d6164b77SAlexander V. Chernikov * Data layout (v0)(current): 812d6164b77SAlexander V. Chernikov * Request: [ ipfw_obj_header nat44_cfg_nat ] 813d6164b77SAlexander V. Chernikov * Reply: [ ipfw_obj_header nat44_cfg_nat LOGBUFFER ] 814d6164b77SAlexander V. Chernikov * 815d6164b77SAlexander V. Chernikov * Returns 0 on success 816d6164b77SAlexander V. Chernikov */ 817d6164b77SAlexander V. Chernikov static int 818d6164b77SAlexander V. Chernikov nat44_get_log(struct ip_fw_chain *chain, ip_fw3_opheader *op3, 819d6164b77SAlexander V. Chernikov struct sockopt_data *sd) 820d6164b77SAlexander V. Chernikov { 821d6164b77SAlexander V. Chernikov ipfw_obj_header *oh; 822d6164b77SAlexander V. Chernikov struct nat44_cfg_nat *ucfg; 823d6164b77SAlexander V. Chernikov struct cfg_nat *ptr; 824d6164b77SAlexander V. Chernikov void *pbuf; 825d6164b77SAlexander V. Chernikov size_t sz; 826d6164b77SAlexander V. Chernikov 827d6164b77SAlexander V. Chernikov sz = sizeof(*oh) + sizeof(*ucfg); 828d6164b77SAlexander V. Chernikov /* Check minimum header size */ 829d6164b77SAlexander V. Chernikov if (sd->valsize < sz) 830d6164b77SAlexander V. Chernikov return (EINVAL); 831d6164b77SAlexander V. Chernikov 832d6164b77SAlexander V. Chernikov oh = (struct _ipfw_obj_header *)ipfw_get_sopt_header(sd, sz); 833d6164b77SAlexander V. Chernikov 834d6164b77SAlexander V. Chernikov /* Basic length checks for TLVs */ 835d6164b77SAlexander V. Chernikov if (oh->ntlv.head.length != sizeof(oh->ntlv)) 836d6164b77SAlexander V. Chernikov return (EINVAL); 837d6164b77SAlexander V. Chernikov 838d6164b77SAlexander V. Chernikov ucfg = (struct nat44_cfg_nat *)(oh + 1); 839d6164b77SAlexander V. Chernikov 840d6164b77SAlexander V. Chernikov /* Check if name is properly terminated */ 841d6164b77SAlexander V. Chernikov if (strnlen(ucfg->name, sizeof(ucfg->name)) == sizeof(ucfg->name)) 842d6164b77SAlexander V. Chernikov return (EINVAL); 843d6164b77SAlexander V. Chernikov 844d6164b77SAlexander V. Chernikov IPFW_UH_RLOCK(chain); 845d6164b77SAlexander V. Chernikov ptr = lookup_nat_name(&chain->nat, ucfg->name); 846d6164b77SAlexander V. Chernikov if (ptr == NULL) { 847d6164b77SAlexander V. Chernikov IPFW_UH_RUNLOCK(chain); 848d6164b77SAlexander V. Chernikov return (ESRCH); 849d6164b77SAlexander V. Chernikov } 850d6164b77SAlexander V. Chernikov 851d6164b77SAlexander V. Chernikov if (ptr->lib->logDesc == NULL) { 852d6164b77SAlexander V. Chernikov IPFW_UH_RUNLOCK(chain); 853d6164b77SAlexander V. Chernikov return (ENOENT); 854d6164b77SAlexander V. Chernikov } 855d6164b77SAlexander V. Chernikov 856d6164b77SAlexander V. Chernikov export_nat_cfg(ptr, ucfg); 857d6164b77SAlexander V. Chernikov 858d6164b77SAlexander V. Chernikov /* Estimate memory amount */ 859d6164b77SAlexander V. Chernikov ucfg->size = sizeof(struct nat44_cfg_nat) + LIBALIAS_BUF_SIZE; 860d6164b77SAlexander V. Chernikov if (sd->valsize < sz + sizeof(*oh)) { 861d6164b77SAlexander V. Chernikov 862d6164b77SAlexander V. Chernikov /* 863d6164b77SAlexander V. Chernikov * Submitted buffer size is not enough. 864d6164b77SAlexander V. Chernikov * WE've already filled in @ucfg structure with 865d6164b77SAlexander V. Chernikov * relevant info including size, so we 866d6164b77SAlexander V. Chernikov * can return. Buffer will be flushed automatically. 867d6164b77SAlexander V. Chernikov */ 868d6164b77SAlexander V. Chernikov IPFW_UH_RUNLOCK(chain); 869d6164b77SAlexander V. Chernikov return (ENOMEM); 870d6164b77SAlexander V. Chernikov } 871d6164b77SAlexander V. Chernikov 872d6164b77SAlexander V. Chernikov pbuf = (void *)ipfw_get_sopt_space(sd, LIBALIAS_BUF_SIZE); 873d6164b77SAlexander V. Chernikov memcpy(pbuf, ptr->lib->logDesc, LIBALIAS_BUF_SIZE); 874d6164b77SAlexander V. Chernikov 875d6164b77SAlexander V. Chernikov IPFW_UH_RUNLOCK(chain); 876d6164b77SAlexander V. Chernikov 877d6164b77SAlexander V. Chernikov return (0); 878d6164b77SAlexander V. Chernikov } 879d6164b77SAlexander V. Chernikov 880d6164b77SAlexander V. Chernikov static struct ipfw_sopt_handler scodes[] = { 881d6164b77SAlexander V. Chernikov { IP_FW_NAT44_XCONFIG, 0, HDIR_SET, nat44_cfg }, 882d6164b77SAlexander V. Chernikov { IP_FW_NAT44_DESTROY, 0, HDIR_SET, nat44_destroy }, 883d6164b77SAlexander V. Chernikov { IP_FW_NAT44_XGETCONFIG, 0, HDIR_GET, nat44_get_cfg }, 884d6164b77SAlexander V. Chernikov { IP_FW_NAT44_LIST_NAT, 0, HDIR_GET, nat44_list_nat }, 885d6164b77SAlexander V. Chernikov { IP_FW_NAT44_XGETLOG, 0, HDIR_GET, nat44_get_log }, 886d6164b77SAlexander V. Chernikov }; 887d6164b77SAlexander V. Chernikov 888d6164b77SAlexander V. Chernikov 889d6164b77SAlexander V. Chernikov /* 890d6164b77SAlexander V. Chernikov * Legacy configuration routines 891d6164b77SAlexander V. Chernikov */ 892d6164b77SAlexander V. Chernikov 893d6164b77SAlexander V. Chernikov struct cfg_spool_legacy { 894d6164b77SAlexander V. Chernikov LIST_ENTRY(cfg_spool_legacy) _next; 895d6164b77SAlexander V. Chernikov struct in_addr addr; 896d6164b77SAlexander V. Chernikov u_short port; 897d6164b77SAlexander V. Chernikov }; 898d6164b77SAlexander V. Chernikov 899d6164b77SAlexander V. Chernikov struct cfg_redir_legacy { 900d6164b77SAlexander V. Chernikov LIST_ENTRY(cfg_redir) _next; 901d6164b77SAlexander V. Chernikov u_int16_t mode; 902d6164b77SAlexander V. Chernikov struct in_addr laddr; 903d6164b77SAlexander V. Chernikov struct in_addr paddr; 904d6164b77SAlexander V. Chernikov struct in_addr raddr; 905d6164b77SAlexander V. Chernikov u_short lport; 906d6164b77SAlexander V. Chernikov u_short pport; 907d6164b77SAlexander V. Chernikov u_short rport; 908d6164b77SAlexander V. Chernikov u_short pport_cnt; 909d6164b77SAlexander V. Chernikov u_short rport_cnt; 910d6164b77SAlexander V. Chernikov int proto; 911d6164b77SAlexander V. Chernikov struct alias_link **alink; 912d6164b77SAlexander V. Chernikov u_int16_t spool_cnt; 913d6164b77SAlexander V. Chernikov LIST_HEAD(, cfg_spool_legacy) spool_chain; 914d6164b77SAlexander V. Chernikov }; 915d6164b77SAlexander V. Chernikov 916d6164b77SAlexander V. Chernikov struct cfg_nat_legacy { 917d6164b77SAlexander V. Chernikov LIST_ENTRY(cfg_nat_legacy) _next; 918d6164b77SAlexander V. Chernikov int id; 919d6164b77SAlexander V. Chernikov struct in_addr ip; 920d6164b77SAlexander V. Chernikov char if_name[IF_NAMESIZE]; 921d6164b77SAlexander V. Chernikov int mode; 922d6164b77SAlexander V. Chernikov struct libalias *lib; 923d6164b77SAlexander V. Chernikov int redir_cnt; 924d6164b77SAlexander V. Chernikov LIST_HEAD(, cfg_redir_legacy) redir_chain; 925d6164b77SAlexander V. Chernikov }; 926d6164b77SAlexander V. Chernikov 927d6164b77SAlexander V. Chernikov static int 928d6164b77SAlexander V. Chernikov ipfw_nat_cfg(struct sockopt *sopt) 929d6164b77SAlexander V. Chernikov { 930d6164b77SAlexander V. Chernikov struct cfg_nat_legacy *cfg; 931d6164b77SAlexander V. Chernikov struct nat44_cfg_nat *ucfg; 932d6164b77SAlexander V. Chernikov struct cfg_redir_legacy *rdir; 933d6164b77SAlexander V. Chernikov struct nat44_cfg_redir *urdir; 934d6164b77SAlexander V. Chernikov char *buf; 935d6164b77SAlexander V. Chernikov size_t len, len2; 936d6164b77SAlexander V. Chernikov int error, i; 937d6164b77SAlexander V. Chernikov 938d6164b77SAlexander V. Chernikov len = sopt->sopt_valsize; 939d6164b77SAlexander V. Chernikov len2 = len + 128; 940d6164b77SAlexander V. Chernikov 941d6164b77SAlexander V. Chernikov /* 942d6164b77SAlexander V. Chernikov * Allocate 2x buffer to store converted structures. 943a4641f4eSPedro F. Giffuni * new redir_cfg has shrunk, so we're sure that 944d6164b77SAlexander V. Chernikov * new buffer size is enough. 945d6164b77SAlexander V. Chernikov */ 946d6164b77SAlexander V. Chernikov buf = malloc(roundup2(len, 8) + len2, M_TEMP, M_WAITOK | M_ZERO); 947d6164b77SAlexander V. Chernikov error = sooptcopyin(sopt, buf, len, sizeof(struct cfg_nat_legacy)); 948d6164b77SAlexander V. Chernikov if (error != 0) 949d6164b77SAlexander V. Chernikov goto out; 950d6164b77SAlexander V. Chernikov 951d6164b77SAlexander V. Chernikov cfg = (struct cfg_nat_legacy *)buf; 952d6164b77SAlexander V. Chernikov if (cfg->id < 0) { 953d6164b77SAlexander V. Chernikov error = EINVAL; 954d6164b77SAlexander V. Chernikov goto out; 955d6164b77SAlexander V. Chernikov } 956d6164b77SAlexander V. Chernikov 957d6164b77SAlexander V. Chernikov ucfg = (struct nat44_cfg_nat *)&buf[roundup2(len, 8)]; 958d6164b77SAlexander V. Chernikov snprintf(ucfg->name, sizeof(ucfg->name), "%d", cfg->id); 959d6164b77SAlexander V. Chernikov strlcpy(ucfg->if_name, cfg->if_name, sizeof(ucfg->if_name)); 960d6164b77SAlexander V. Chernikov ucfg->ip = cfg->ip; 961d6164b77SAlexander V. Chernikov ucfg->mode = cfg->mode; 962d6164b77SAlexander V. Chernikov ucfg->redir_cnt = cfg->redir_cnt; 963d6164b77SAlexander V. Chernikov 964d6164b77SAlexander V. Chernikov if (len < sizeof(*cfg) + cfg->redir_cnt * sizeof(*rdir)) { 965d6164b77SAlexander V. Chernikov error = EINVAL; 966d6164b77SAlexander V. Chernikov goto out; 967d6164b77SAlexander V. Chernikov } 968d6164b77SAlexander V. Chernikov 969d6164b77SAlexander V. Chernikov urdir = (struct nat44_cfg_redir *)(ucfg + 1); 970d6164b77SAlexander V. Chernikov rdir = (struct cfg_redir_legacy *)(cfg + 1); 971d6164b77SAlexander V. Chernikov for (i = 0; i < cfg->redir_cnt; i++) { 972d6164b77SAlexander V. Chernikov urdir->mode = rdir->mode; 973d6164b77SAlexander V. Chernikov urdir->laddr = rdir->laddr; 974d6164b77SAlexander V. Chernikov urdir->paddr = rdir->paddr; 975d6164b77SAlexander V. Chernikov urdir->raddr = rdir->raddr; 976d6164b77SAlexander V. Chernikov urdir->lport = rdir->lport; 977d6164b77SAlexander V. Chernikov urdir->pport = rdir->pport; 978d6164b77SAlexander V. Chernikov urdir->rport = rdir->rport; 979d6164b77SAlexander V. Chernikov urdir->pport_cnt = rdir->pport_cnt; 980d6164b77SAlexander V. Chernikov urdir->rport_cnt = rdir->rport_cnt; 981d6164b77SAlexander V. Chernikov urdir->proto = rdir->proto; 982d6164b77SAlexander V. Chernikov urdir->spool_cnt = rdir->spool_cnt; 983d6164b77SAlexander V. Chernikov 984d6164b77SAlexander V. Chernikov urdir++; 985d6164b77SAlexander V. Chernikov rdir++; 986d6164b77SAlexander V. Chernikov } 987d6164b77SAlexander V. Chernikov 988d6164b77SAlexander V. Chernikov nat44_config(&V_layer3_chain, ucfg); 9893b3a8eb9SGleb Smirnoff 9903b3a8eb9SGleb Smirnoff out: 9913b3a8eb9SGleb Smirnoff free(buf, M_TEMP); 9923b3a8eb9SGleb Smirnoff return (error); 9933b3a8eb9SGleb Smirnoff } 9943b3a8eb9SGleb Smirnoff 9953b3a8eb9SGleb Smirnoff static int 9963b3a8eb9SGleb Smirnoff ipfw_nat_del(struct sockopt *sopt) 9973b3a8eb9SGleb Smirnoff { 9983b3a8eb9SGleb Smirnoff struct cfg_nat *ptr; 9993b3a8eb9SGleb Smirnoff struct ip_fw_chain *chain = &V_layer3_chain; 10003b3a8eb9SGleb Smirnoff int i; 10013b3a8eb9SGleb Smirnoff 10023b3a8eb9SGleb Smirnoff sooptcopyin(sopt, &i, sizeof i, sizeof i); 10033b3a8eb9SGleb Smirnoff /* XXX validate i */ 1004d6164b77SAlexander V. Chernikov IPFW_UH_WLOCK(chain); 10053b3a8eb9SGleb Smirnoff ptr = lookup_nat(&chain->nat, i); 10063b3a8eb9SGleb Smirnoff if (ptr == NULL) { 1007d6164b77SAlexander V. Chernikov IPFW_UH_WUNLOCK(chain); 10083b3a8eb9SGleb Smirnoff return (EINVAL); 10093b3a8eb9SGleb Smirnoff } 1010d6164b77SAlexander V. Chernikov IPFW_WLOCK(chain); 10113b3a8eb9SGleb Smirnoff LIST_REMOVE(ptr, _next); 10123b3a8eb9SGleb Smirnoff flush_nat_ptrs(chain, i); 10133b3a8eb9SGleb Smirnoff IPFW_WUNLOCK(chain); 1014d6164b77SAlexander V. Chernikov IPFW_UH_WUNLOCK(chain); 10151a458088SAlexander V. Chernikov free_nat_instance(ptr); 10163b3a8eb9SGleb Smirnoff return (0); 10173b3a8eb9SGleb Smirnoff } 10183b3a8eb9SGleb Smirnoff 10193b3a8eb9SGleb Smirnoff static int 10203b3a8eb9SGleb Smirnoff ipfw_nat_get_cfg(struct sockopt *sopt) 10213b3a8eb9SGleb Smirnoff { 10223b3a8eb9SGleb Smirnoff struct ip_fw_chain *chain = &V_layer3_chain; 10233b3a8eb9SGleb Smirnoff struct cfg_nat *n; 1024d6164b77SAlexander V. Chernikov struct cfg_nat_legacy *ucfg; 10253b3a8eb9SGleb Smirnoff struct cfg_redir *r; 10263b3a8eb9SGleb Smirnoff struct cfg_spool *s; 1027d6164b77SAlexander V. Chernikov struct cfg_redir_legacy *ser_r; 1028d6164b77SAlexander V. Chernikov struct cfg_spool_legacy *ser_s; 10293b3a8eb9SGleb Smirnoff char *data; 10303b3a8eb9SGleb Smirnoff int gencnt, nat_cnt, len, error; 10313b3a8eb9SGleb Smirnoff 10323b3a8eb9SGleb Smirnoff nat_cnt = 0; 10333b3a8eb9SGleb Smirnoff len = sizeof(nat_cnt); 10343b3a8eb9SGleb Smirnoff 1035d6164b77SAlexander V. Chernikov IPFW_UH_RLOCK(chain); 10363b3a8eb9SGleb Smirnoff retry: 10373b3a8eb9SGleb Smirnoff gencnt = chain->gencnt; 10383b3a8eb9SGleb Smirnoff /* Estimate memory amount */ 10393b3a8eb9SGleb Smirnoff LIST_FOREACH(n, &chain->nat, _next) { 10403b3a8eb9SGleb Smirnoff nat_cnt++; 1041d6164b77SAlexander V. Chernikov len += sizeof(struct cfg_nat_legacy); 10423b3a8eb9SGleb Smirnoff LIST_FOREACH(r, &n->redir_chain, _next) { 1043d6164b77SAlexander V. Chernikov len += sizeof(struct cfg_redir_legacy); 10443b3a8eb9SGleb Smirnoff LIST_FOREACH(s, &r->spool_chain, _next) 1045d6164b77SAlexander V. Chernikov len += sizeof(struct cfg_spool_legacy); 10463b3a8eb9SGleb Smirnoff } 10473b3a8eb9SGleb Smirnoff } 1048d6164b77SAlexander V. Chernikov IPFW_UH_RUNLOCK(chain); 10493b3a8eb9SGleb Smirnoff 10503b3a8eb9SGleb Smirnoff data = malloc(len, M_TEMP, M_WAITOK | M_ZERO); 10513b3a8eb9SGleb Smirnoff bcopy(&nat_cnt, data, sizeof(nat_cnt)); 10523b3a8eb9SGleb Smirnoff 10533b3a8eb9SGleb Smirnoff nat_cnt = 0; 10543b3a8eb9SGleb Smirnoff len = sizeof(nat_cnt); 10553b3a8eb9SGleb Smirnoff 1056d6164b77SAlexander V. Chernikov IPFW_UH_RLOCK(chain); 10573b3a8eb9SGleb Smirnoff if (gencnt != chain->gencnt) { 10583b3a8eb9SGleb Smirnoff free(data, M_TEMP); 10593b3a8eb9SGleb Smirnoff goto retry; 10603b3a8eb9SGleb Smirnoff } 10613b3a8eb9SGleb Smirnoff /* Serialize all the data. */ 10623b3a8eb9SGleb Smirnoff LIST_FOREACH(n, &chain->nat, _next) { 1063d6164b77SAlexander V. Chernikov ucfg = (struct cfg_nat_legacy *)&data[len]; 1064d6164b77SAlexander V. Chernikov ucfg->id = n->id; 1065d6164b77SAlexander V. Chernikov ucfg->ip = n->ip; 1066d6164b77SAlexander V. Chernikov ucfg->redir_cnt = n->redir_cnt; 1067d6164b77SAlexander V. Chernikov ucfg->mode = n->mode; 1068d6164b77SAlexander V. Chernikov strlcpy(ucfg->if_name, n->if_name, sizeof(ucfg->if_name)); 1069d6164b77SAlexander V. Chernikov len += sizeof(struct cfg_nat_legacy); 10703b3a8eb9SGleb Smirnoff LIST_FOREACH(r, &n->redir_chain, _next) { 1071d6164b77SAlexander V. Chernikov ser_r = (struct cfg_redir_legacy *)&data[len]; 1072d6164b77SAlexander V. Chernikov ser_r->mode = r->mode; 1073d6164b77SAlexander V. Chernikov ser_r->laddr = r->laddr; 1074d6164b77SAlexander V. Chernikov ser_r->paddr = r->paddr; 1075d6164b77SAlexander V. Chernikov ser_r->raddr = r->raddr; 1076d6164b77SAlexander V. Chernikov ser_r->lport = r->lport; 1077d6164b77SAlexander V. Chernikov ser_r->pport = r->pport; 1078d6164b77SAlexander V. Chernikov ser_r->rport = r->rport; 1079d6164b77SAlexander V. Chernikov ser_r->pport_cnt = r->pport_cnt; 1080d6164b77SAlexander V. Chernikov ser_r->rport_cnt = r->rport_cnt; 1081d6164b77SAlexander V. Chernikov ser_r->proto = r->proto; 1082d6164b77SAlexander V. Chernikov ser_r->spool_cnt = r->spool_cnt; 1083d6164b77SAlexander V. Chernikov len += sizeof(struct cfg_redir_legacy); 10843b3a8eb9SGleb Smirnoff LIST_FOREACH(s, &r->spool_chain, _next) { 1085d6164b77SAlexander V. Chernikov ser_s = (struct cfg_spool_legacy *)&data[len]; 1086d6164b77SAlexander V. Chernikov ser_s->addr = s->addr; 1087d6164b77SAlexander V. Chernikov ser_s->port = s->port; 1088d6164b77SAlexander V. Chernikov len += sizeof(struct cfg_spool_legacy); 10893b3a8eb9SGleb Smirnoff } 10903b3a8eb9SGleb Smirnoff } 10913b3a8eb9SGleb Smirnoff } 1092d6164b77SAlexander V. Chernikov IPFW_UH_RUNLOCK(chain); 10933b3a8eb9SGleb Smirnoff 10943b3a8eb9SGleb Smirnoff error = sooptcopyout(sopt, data, len); 10953b3a8eb9SGleb Smirnoff free(data, M_TEMP); 10963b3a8eb9SGleb Smirnoff 10973b3a8eb9SGleb Smirnoff return (error); 10983b3a8eb9SGleb Smirnoff } 10993b3a8eb9SGleb Smirnoff 11003b3a8eb9SGleb Smirnoff static int 11013b3a8eb9SGleb Smirnoff ipfw_nat_get_log(struct sockopt *sopt) 11023b3a8eb9SGleb Smirnoff { 11033b3a8eb9SGleb Smirnoff uint8_t *data; 11043b3a8eb9SGleb Smirnoff struct cfg_nat *ptr; 11053b3a8eb9SGleb Smirnoff int i, size; 11063b3a8eb9SGleb Smirnoff struct ip_fw_chain *chain; 1107ccba94b8SAlexander V. Chernikov IPFW_RLOCK_TRACKER; 11083b3a8eb9SGleb Smirnoff 11093b3a8eb9SGleb Smirnoff chain = &V_layer3_chain; 11103b3a8eb9SGleb Smirnoff 11113b3a8eb9SGleb Smirnoff IPFW_RLOCK(chain); 11123b3a8eb9SGleb Smirnoff /* one pass to count, one to copy the data */ 11133b3a8eb9SGleb Smirnoff i = 0; 11143b3a8eb9SGleb Smirnoff LIST_FOREACH(ptr, &chain->nat, _next) { 11153b3a8eb9SGleb Smirnoff if (ptr->lib->logDesc == NULL) 11163b3a8eb9SGleb Smirnoff continue; 11173b3a8eb9SGleb Smirnoff i++; 11183b3a8eb9SGleb Smirnoff } 11193b3a8eb9SGleb Smirnoff size = i * (LIBALIAS_BUF_SIZE + sizeof(int)); 11203b3a8eb9SGleb Smirnoff data = malloc(size, M_IPFW, M_NOWAIT | M_ZERO); 11213b3a8eb9SGleb Smirnoff if (data == NULL) { 11223b3a8eb9SGleb Smirnoff IPFW_RUNLOCK(chain); 11233b3a8eb9SGleb Smirnoff return (ENOSPC); 11243b3a8eb9SGleb Smirnoff } 11253b3a8eb9SGleb Smirnoff i = 0; 11263b3a8eb9SGleb Smirnoff LIST_FOREACH(ptr, &chain->nat, _next) { 11273b3a8eb9SGleb Smirnoff if (ptr->lib->logDesc == NULL) 11283b3a8eb9SGleb Smirnoff continue; 11293b3a8eb9SGleb Smirnoff bcopy(&ptr->id, &data[i], sizeof(int)); 11303b3a8eb9SGleb Smirnoff i += sizeof(int); 11313b3a8eb9SGleb Smirnoff bcopy(ptr->lib->logDesc, &data[i], LIBALIAS_BUF_SIZE); 11323b3a8eb9SGleb Smirnoff i += LIBALIAS_BUF_SIZE; 11333b3a8eb9SGleb Smirnoff } 11343b3a8eb9SGleb Smirnoff IPFW_RUNLOCK(chain); 11353b3a8eb9SGleb Smirnoff sooptcopyout(sopt, data, size); 11363b3a8eb9SGleb Smirnoff free(data, M_IPFW); 11373b3a8eb9SGleb Smirnoff return(0); 11383b3a8eb9SGleb Smirnoff } 11393b3a8eb9SGleb Smirnoff 11408856400bSMikolaj Golub static int 11418856400bSMikolaj Golub vnet_ipfw_nat_init(const void *arg __unused) 11423b3a8eb9SGleb Smirnoff { 11433b3a8eb9SGleb Smirnoff 11448856400bSMikolaj Golub V_ipfw_nat_ready = 1; 11458856400bSMikolaj Golub return (0); 11463b3a8eb9SGleb Smirnoff } 11473b3a8eb9SGleb Smirnoff 11488856400bSMikolaj Golub static int 11498856400bSMikolaj Golub vnet_ipfw_nat_uninit(const void *arg __unused) 11503b3a8eb9SGleb Smirnoff { 11513b3a8eb9SGleb Smirnoff struct cfg_nat *ptr, *ptr_temp; 11523b3a8eb9SGleb Smirnoff struct ip_fw_chain *chain; 11533b3a8eb9SGleb Smirnoff 11543b3a8eb9SGleb Smirnoff chain = &V_layer3_chain; 11553b3a8eb9SGleb Smirnoff IPFW_WLOCK(chain); 11569ac51e79SBjoern A. Zeeb V_ipfw_nat_ready = 0; 11573b3a8eb9SGleb Smirnoff LIST_FOREACH_SAFE(ptr, &chain->nat, _next, ptr_temp) { 11583b3a8eb9SGleb Smirnoff LIST_REMOVE(ptr, _next); 11591a458088SAlexander V. Chernikov free_nat_instance(ptr); 11603b3a8eb9SGleb Smirnoff } 11613b3a8eb9SGleb Smirnoff flush_nat_ptrs(chain, -1 /* flush all */); 11628856400bSMikolaj Golub IPFW_WUNLOCK(chain); 11638856400bSMikolaj Golub return (0); 11648856400bSMikolaj Golub } 11658856400bSMikolaj Golub 11668856400bSMikolaj Golub static void 11678856400bSMikolaj Golub ipfw_nat_init(void) 11688856400bSMikolaj Golub { 11698856400bSMikolaj Golub 11708856400bSMikolaj Golub /* init ipfw hooks */ 11718856400bSMikolaj Golub ipfw_nat_ptr = ipfw_nat; 11728856400bSMikolaj Golub lookup_nat_ptr = lookup_nat; 11738856400bSMikolaj Golub ipfw_nat_cfg_ptr = ipfw_nat_cfg; 11748856400bSMikolaj Golub ipfw_nat_del_ptr = ipfw_nat_del; 11758856400bSMikolaj Golub ipfw_nat_get_cfg_ptr = ipfw_nat_get_cfg; 11768856400bSMikolaj Golub ipfw_nat_get_log_ptr = ipfw_nat_get_log; 1177d6164b77SAlexander V. Chernikov IPFW_ADD_SOPT_HANDLER(1, scodes); 11788856400bSMikolaj Golub 11798856400bSMikolaj Golub ifaddr_event_tag = EVENTHANDLER_REGISTER(ifaddr_event, ifaddr_change, 11808856400bSMikolaj Golub NULL, EVENTHANDLER_PRI_ANY); 11818856400bSMikolaj Golub } 11828856400bSMikolaj Golub 11838856400bSMikolaj Golub static void 11848856400bSMikolaj Golub ipfw_nat_destroy(void) 11858856400bSMikolaj Golub { 11868856400bSMikolaj Golub 11878856400bSMikolaj Golub EVENTHANDLER_DEREGISTER(ifaddr_event, ifaddr_event_tag); 11883b3a8eb9SGleb Smirnoff /* deregister ipfw_nat */ 1189d6164b77SAlexander V. Chernikov IPFW_DEL_SOPT_HANDLER(1, scodes); 11903b3a8eb9SGleb Smirnoff ipfw_nat_ptr = NULL; 11913b3a8eb9SGleb Smirnoff lookup_nat_ptr = NULL; 11923b3a8eb9SGleb Smirnoff ipfw_nat_cfg_ptr = NULL; 11933b3a8eb9SGleb Smirnoff ipfw_nat_del_ptr = NULL; 11943b3a8eb9SGleb Smirnoff ipfw_nat_get_cfg_ptr = NULL; 11953b3a8eb9SGleb Smirnoff ipfw_nat_get_log_ptr = NULL; 11963b3a8eb9SGleb Smirnoff } 11973b3a8eb9SGleb Smirnoff 11983b3a8eb9SGleb Smirnoff static int 11993b3a8eb9SGleb Smirnoff ipfw_nat_modevent(module_t mod, int type, void *unused) 12003b3a8eb9SGleb Smirnoff { 12013b3a8eb9SGleb Smirnoff int err = 0; 12023b3a8eb9SGleb Smirnoff 12033b3a8eb9SGleb Smirnoff switch (type) { 12043b3a8eb9SGleb Smirnoff case MOD_LOAD: 12053b3a8eb9SGleb Smirnoff break; 12063b3a8eb9SGleb Smirnoff 12073b3a8eb9SGleb Smirnoff case MOD_UNLOAD: 12083b3a8eb9SGleb Smirnoff break; 12093b3a8eb9SGleb Smirnoff 12103b3a8eb9SGleb Smirnoff default: 12113b3a8eb9SGleb Smirnoff return EOPNOTSUPP; 12123b3a8eb9SGleb Smirnoff break; 12133b3a8eb9SGleb Smirnoff } 12143b3a8eb9SGleb Smirnoff return err; 12153b3a8eb9SGleb Smirnoff } 12163b3a8eb9SGleb Smirnoff 12173b3a8eb9SGleb Smirnoff static moduledata_t ipfw_nat_mod = { 12183b3a8eb9SGleb Smirnoff "ipfw_nat", 12193b3a8eb9SGleb Smirnoff ipfw_nat_modevent, 12209823d527SKevin Lo 0 12213b3a8eb9SGleb Smirnoff }; 12223b3a8eb9SGleb Smirnoff 12238856400bSMikolaj Golub /* Define startup order. */ 122489856f7eSBjoern A. Zeeb #define IPFW_NAT_SI_SUB_FIREWALL SI_SUB_PROTO_FIREWALL 12258a477d48SMikolaj Golub #define IPFW_NAT_MODEVENT_ORDER (SI_ORDER_ANY - 128) /* after ipfw */ 12268856400bSMikolaj Golub #define IPFW_NAT_MODULE_ORDER (IPFW_NAT_MODEVENT_ORDER + 1) 12278856400bSMikolaj Golub #define IPFW_NAT_VNET_ORDER (IPFW_NAT_MODEVENT_ORDER + 2) 12288856400bSMikolaj Golub 12298856400bSMikolaj Golub DECLARE_MODULE(ipfw_nat, ipfw_nat_mod, IPFW_NAT_SI_SUB_FIREWALL, SI_ORDER_ANY); 12303b3a8eb9SGleb Smirnoff MODULE_DEPEND(ipfw_nat, libalias, 1, 1, 1); 1231f9ab623bSAlexander V. Chernikov MODULE_DEPEND(ipfw_nat, ipfw, 3, 3, 3); 12323b3a8eb9SGleb Smirnoff MODULE_VERSION(ipfw_nat, 1); 12338856400bSMikolaj Golub 12348856400bSMikolaj Golub SYSINIT(ipfw_nat_init, IPFW_NAT_SI_SUB_FIREWALL, IPFW_NAT_MODULE_ORDER, 12358856400bSMikolaj Golub ipfw_nat_init, NULL); 12368856400bSMikolaj Golub VNET_SYSINIT(vnet_ipfw_nat_init, IPFW_NAT_SI_SUB_FIREWALL, IPFW_NAT_VNET_ORDER, 12378856400bSMikolaj Golub vnet_ipfw_nat_init, NULL); 12388856400bSMikolaj Golub 12398856400bSMikolaj Golub SYSUNINIT(ipfw_nat_destroy, IPFW_NAT_SI_SUB_FIREWALL, IPFW_NAT_MODULE_ORDER, 12408856400bSMikolaj Golub ipfw_nat_destroy, NULL); 12418856400bSMikolaj Golub VNET_SYSUNINIT(vnet_ipfw_nat_uninit, IPFW_NAT_SI_SUB_FIREWALL, 12428856400bSMikolaj Golub IPFW_NAT_VNET_ORDER, vnet_ipfw_nat_uninit, NULL); 12438856400bSMikolaj Golub 12443b3a8eb9SGleb Smirnoff /* end of file */ 1245