1176669Spiso/*- 2176669Spiso * Copyright (c) 2008 Paolo Pisati 3176669Spiso * All rights reserved. 4176669Spiso * 5176669Spiso * Redistribution and use in source and binary forms, with or without 6176669Spiso * modification, are permitted provided that the following conditions 7176669Spiso * are met: 8176669Spiso * 1. Redistributions of source code must retain the above copyright 9176669Spiso * notice, this list of conditions and the following disclaimer. 10176669Spiso * 2. Redistributions in binary form must reproduce the above copyright 11176669Spiso * notice, this list of conditions and the following disclaimer in the 12176669Spiso * documentation and/or other materials provided with the distribution. 13176669Spiso * 14176669Spiso * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15176669Spiso * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16176669Spiso * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17176669Spiso * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18176669Spiso * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19176669Spiso * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20176669Spiso * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21176669Spiso * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22176669Spiso * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23176669Spiso * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24176669Spiso * SUCH DAMAGE. 25176669Spiso */ 26176669Spiso 27176669Spiso#include <sys/cdefs.h> 28176669Spiso__FBSDID("$FreeBSD$"); 29176669Spiso 30176669Spiso#include <sys/param.h> 31176669Spiso#include <sys/systm.h> 32176669Spiso#include <sys/eventhandler.h> 33176669Spiso#include <sys/malloc.h> 34176669Spiso#include <sys/kernel.h> 35176669Spiso#include <sys/lock.h> 36176669Spiso#include <sys/module.h> 37176669Spiso#include <sys/rwlock.h> 38176669Spiso 39200580Sluigi#define IPFW_INTERNAL /* Access to protected data structures in ip_fw.h. */ 40200580Sluigi 41176669Spiso#include <netinet/libalias/alias.h> 42176669Spiso#include <netinet/libalias/alias_local.h> 43176669Spiso 44176669Spiso#include <net/if.h> 45176669Spiso#include <netinet/in.h> 46176669Spiso#include <netinet/ip.h> 47176669Spiso#include <netinet/ip_var.h> 48176669Spiso#include <netinet/ip_fw.h> 49176669Spiso#include <netinet/tcp.h> 50176669Spiso#include <netinet/udp.h> 51176669Spiso 52243401Sglebius#include <netpfil/ipfw/ip_fw_private.h> 53243401Sglebius 54176669Spiso#include <machine/in_cksum.h> /* XXX for in_cksum */ 55176669Spiso 56255395Strocinystatic eventhandler_tag ifaddr_event_tag; 57176669Spiso 58200909Sluigistatic void 59176669Spisoifaddr_change(void *arg __unused, struct ifnet *ifp) 60176669Spiso{ 61176669Spiso struct cfg_nat *ptr; 62176669Spiso struct ifaddr *ifa; 63200897Sluigi struct ip_fw_chain *chain; 64176669Spiso 65255395Strociny KASSERT(curvnet == ifp->if_vnet, 66255395Strociny ("curvnet(%p) differs from iface vnet(%p)", curvnet, ifp->if_vnet)); 67200897Sluigi chain = &V_layer3_chain; 68200909Sluigi IPFW_WLOCK(chain); 69176669Spiso /* Check every nat entry... */ 70200897Sluigi LIST_FOREACH(ptr, &chain->nat, _next) { 71176669Spiso /* ...using nic 'ifp->if_xname' as dynamic alias address. */ 72200909Sluigi if (strncmp(ptr->if_name, ifp->if_xname, IF_NAMESIZE) != 0) 73200909Sluigi continue; 74200909Sluigi if_addr_rlock(ifp); 75200909Sluigi TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) { 76200909Sluigi if (ifa->ifa_addr == NULL) 77200909Sluigi continue; 78200909Sluigi if (ifa->ifa_addr->sa_family != AF_INET) 79200909Sluigi continue; 80200909Sluigi ptr->ip = ((struct sockaddr_in *) 81200909Sluigi (ifa->ifa_addr))->sin_addr; 82200909Sluigi LibAliasSetAddress(ptr->lib, ptr->ip); 83176669Spiso } 84200909Sluigi if_addr_runlock(ifp); 85176669Spiso } 86200909Sluigi IPFW_WUNLOCK(chain); 87176669Spiso} 88176669Spiso 89200897Sluigi/* 90200897Sluigi * delete the pointers for nat entry ix, or all of them if ix < 0 91200897Sluigi */ 92176669Spisostatic void 93200897Sluigiflush_nat_ptrs(struct ip_fw_chain *chain, const int ix) 94176669Spiso{ 95200897Sluigi int i; 96200909Sluigi ipfw_insn_nat *cmd; 97176669Spiso 98200897Sluigi IPFW_WLOCK_ASSERT(chain); 99200897Sluigi for (i = 0; i < chain->n_rules; i++) { 100200909Sluigi cmd = (ipfw_insn_nat *)ACTION_PTR(chain->map[i]); 101200909Sluigi /* XXX skip log and the like ? */ 102200897Sluigi if (cmd->o.opcode == O_NAT && cmd->nat != NULL && 103200897Sluigi (ix < 0 || cmd->nat->id == ix)) 104176669Spiso cmd->nat = NULL; 105176669Spiso } 106176669Spiso} 107176669Spiso 108176669Spisostatic void 109176669Spisodel_redir_spool_cfg(struct cfg_nat *n, struct redir_chain *head) 110176669Spiso{ 111176669Spiso struct cfg_redir *r, *tmp_r; 112176669Spiso struct cfg_spool *s, *tmp_s; 113176669Spiso int i, num; 114176669Spiso 115176669Spiso LIST_FOREACH_SAFE(r, head, _next, tmp_r) { 116176669Spiso num = 1; /* Number of alias_link to delete. */ 117176669Spiso switch (r->mode) { 118176669Spiso case REDIR_PORT: 119176669Spiso num = r->pport_cnt; 120176669Spiso /* FALLTHROUGH */ 121176669Spiso case REDIR_ADDR: 122176669Spiso case REDIR_PROTO: 123176669Spiso /* Delete all libalias redirect entry. */ 124176669Spiso for (i = 0; i < num; i++) 125176669Spiso LibAliasRedirectDelete(n->lib, r->alink[i]); 126176669Spiso /* Del spool cfg if any. */ 127176669Spiso LIST_FOREACH_SAFE(s, &r->spool_chain, _next, tmp_s) { 128176669Spiso LIST_REMOVE(s, _next); 129176669Spiso free(s, M_IPFW); 130176669Spiso } 131176669Spiso free(r->alink, M_IPFW); 132176669Spiso LIST_REMOVE(r, _next); 133176669Spiso free(r, M_IPFW); 134176669Spiso break; 135176669Spiso default: 136200909Sluigi printf("unknown redirect mode: %u\n", r->mode); 137176669Spiso /* XXX - panic?!?!? */ 138200909Sluigi break; 139176669Spiso } 140176669Spiso } 141176669Spiso} 142176669Spiso 143220837Sglebiusstatic void 144176669Spisoadd_redir_spool_cfg(char *buf, struct cfg_nat *ptr) 145176669Spiso{ 146176669Spiso struct cfg_redir *r, *ser_r; 147176669Spiso struct cfg_spool *s, *ser_s; 148176669Spiso int cnt, off, i; 149176669Spiso 150176669Spiso for (cnt = 0, off = 0; cnt < ptr->redir_cnt; cnt++) { 151176669Spiso ser_r = (struct cfg_redir *)&buf[off]; 152176669Spiso r = malloc(SOF_REDIR, M_IPFW, M_WAITOK | M_ZERO); 153176669Spiso memcpy(r, ser_r, SOF_REDIR); 154176669Spiso LIST_INIT(&r->spool_chain); 155176669Spiso off += SOF_REDIR; 156176669Spiso r->alink = malloc(sizeof(struct alias_link *) * r->pport_cnt, 157176669Spiso M_IPFW, M_WAITOK | M_ZERO); 158176669Spiso switch (r->mode) { 159176669Spiso case REDIR_ADDR: 160176669Spiso r->alink[0] = LibAliasRedirectAddr(ptr->lib, r->laddr, 161176669Spiso r->paddr); 162176669Spiso break; 163176669Spiso case REDIR_PORT: 164176669Spiso for (i = 0 ; i < r->pport_cnt; i++) { 165176669Spiso /* If remotePort is all ports, set it to 0. */ 166176669Spiso u_short remotePortCopy = r->rport + i; 167176669Spiso if (r->rport_cnt == 1 && r->rport == 0) 168176669Spiso remotePortCopy = 0; 169176669Spiso r->alink[i] = LibAliasRedirectPort(ptr->lib, 170176669Spiso r->laddr, htons(r->lport + i), r->raddr, 171200909Sluigi htons(remotePortCopy), r->paddr, 172176669Spiso htons(r->pport + i), r->proto); 173176669Spiso if (r->alink[i] == NULL) { 174176669Spiso r->alink[0] = NULL; 175176669Spiso break; 176176669Spiso } 177176669Spiso } 178176669Spiso break; 179176669Spiso case REDIR_PROTO: 180176669Spiso r->alink[0] = LibAliasRedirectProto(ptr->lib ,r->laddr, 181176669Spiso r->raddr, r->paddr, r->proto); 182176669Spiso break; 183176669Spiso default: 184176669Spiso printf("unknown redirect mode: %u\n", r->mode); 185200909Sluigi break; 186176669Spiso } 187200909Sluigi /* XXX perhaps return an error instead of panic ? */ 188200909Sluigi if (r->alink[0] == NULL) 189200909Sluigi panic("LibAliasRedirect* returned NULL"); 190200909Sluigi /* LSNAT handling. */ 191200909Sluigi for (i = 0; i < r->spool_cnt; i++) { 192200909Sluigi ser_s = (struct cfg_spool *)&buf[off]; 193200909Sluigi s = malloc(SOF_REDIR, M_IPFW, M_WAITOK | M_ZERO); 194200909Sluigi memcpy(s, ser_s, SOF_SPOOL); 195200909Sluigi LibAliasAddServer(ptr->lib, r->alink[0], 196200909Sluigi s->addr, htons(s->port)); 197200909Sluigi off += SOF_SPOOL; 198200909Sluigi /* Hook spool entry. */ 199200909Sluigi LIST_INSERT_HEAD(&r->spool_chain, s, _next); 200200909Sluigi } 201176669Spiso /* And finally hook this redir entry. */ 202200897Sluigi LIST_INSERT_HEAD(&ptr->redir_chain, r, _next); 203176669Spiso } 204176669Spiso} 205176669Spiso 206244569Smelifaro/* 207244569Smelifaro * ipfw_nat - perform mbuf header translation. 208244569Smelifaro * 209244569Smelifaro * Note V_layer3_chain has to be locked while calling ipfw_nat() in 210244569Smelifaro * 'global' operation mode (t == NULL). 211244569Smelifaro * 212244569Smelifaro */ 213176669Spisostatic int 214176669Spisoipfw_nat(struct ip_fw_args *args, struct cfg_nat *t, struct mbuf *m) 215176669Spiso{ 216176669Spiso struct mbuf *mcl; 217176669Spiso struct ip *ip; 218176669Spiso /* XXX - libalias duct tape */ 219223080Sae int ldt, retval, found; 220223080Sae struct ip_fw_chain *chain; 221176669Spiso char *c; 222176669Spiso 223176669Spiso ldt = 0; 224176669Spiso retval = 0; 225200909Sluigi mcl = m_megapullup(m, m->m_pkthdr.len); 226200909Sluigi if (mcl == NULL) { 227200909Sluigi args->m = NULL; 228200909Sluigi return (IP_FW_DENY); 229200909Sluigi } 230176669Spiso ip = mtod(mcl, struct ip *); 231176669Spiso 232200909Sluigi /* 233176669Spiso * XXX - Libalias checksum offload 'duct tape': 234176669Spiso * 235200909Sluigi * locally generated packets have only pseudo-header checksum 236200909Sluigi * calculated and libalias will break it[1], so mark them for 237200909Sluigi * later fix. Moreover there are cases when libalias modifies 238200909Sluigi * tcp packet data[2], mark them for later fix too. 239176669Spiso * 240200909Sluigi * [1] libalias was never meant to run in kernel, so it does 241200909Sluigi * not have any knowledge about checksum offloading, and 242200909Sluigi * expects a packet with a full internet checksum. 243200909Sluigi * Unfortunately, packets generated locally will have just the 244200909Sluigi * pseudo header calculated, and when libalias tries to adjust 245200909Sluigi * the checksum it will actually compute a wrong value. 246176669Spiso * 247200909Sluigi * [2] when libalias modifies tcp's data content, full TCP 248200909Sluigi * checksum has to be recomputed: the problem is that 249200909Sluigi * libalias does not have any idea about checksum offloading. 250200909Sluigi * To work around this, we do not do checksumming in LibAlias, 251200909Sluigi * but only mark the packets in th_x2 field. If we receive a 252200909Sluigi * marked packet, we calculate correct checksum for it 253200909Sluigi * aware of offloading. Why such a terrible hack instead of 254200909Sluigi * recalculating checksum for each packet? 255200909Sluigi * Because the previous checksum was not checked! 256200909Sluigi * Recalculating checksums for EVERY packet will hide ALL 257200909Sluigi * transmission errors. Yes, marked packets still suffer from 258200909Sluigi * this problem. But, sigh, natd(8) has this problem, too. 259200909Sluigi * 260176669Spiso * TODO: -make libalias mbuf aware (so 261176669Spiso * it can handle delayed checksum and tso) 262176669Spiso */ 263176669Spiso 264200909Sluigi if (mcl->m_pkthdr.rcvif == NULL && 265200909Sluigi mcl->m_pkthdr.csum_flags & CSUM_DELAY_DATA) 266176669Spiso ldt = 1; 267176669Spiso 268176669Spiso c = mtod(mcl, char *); 269222806Sae 270223080Sae /* Check if this is 'global' instance */ 271223080Sae if (t == NULL) { 272223080Sae if (args->oif == NULL) { 273223080Sae /* Wrong direction, skip processing */ 274223080Sae args->m = mcl; 275223080Sae return (IP_FW_NAT); 276223080Sae } 277223080Sae 278223080Sae found = 0; 279223080Sae chain = &V_layer3_chain; 280244569Smelifaro IPFW_RLOCK_ASSERT(chain); 281223080Sae /* Check every nat entry... */ 282223080Sae LIST_FOREACH(t, &chain->nat, _next) { 283223080Sae if ((t->mode & PKT_ALIAS_SKIP_GLOBAL) != 0) 284223080Sae continue; 285223080Sae retval = LibAliasOutTry(t->lib, c, 286223080Sae mcl->m_len + M_TRAILINGSPACE(mcl), 0); 287223080Sae if (retval == PKT_ALIAS_OK) { 288223080Sae /* Nat instance recognises state */ 289223080Sae found = 1; 290223080Sae break; 291223080Sae } 292223080Sae } 293223080Sae if (found != 1) { 294223080Sae /* No instance found, return ignore */ 295223080Sae args->m = mcl; 296223080Sae return (IP_FW_NAT); 297223080Sae } 298223080Sae } else { 299223080Sae if (args->oif == NULL) 300223080Sae retval = LibAliasIn(t->lib, c, 301223080Sae mcl->m_len + M_TRAILINGSPACE(mcl)); 302223080Sae else 303223080Sae retval = LibAliasOut(t->lib, c, 304223080Sae mcl->m_len + M_TRAILINGSPACE(mcl)); 305223080Sae } 306223080Sae 307222806Sae /* 308222806Sae * We drop packet when: 309222806Sae * 1. libalias returns PKT_ALIAS_ERROR; 310222806Sae * 2. For incoming packets: 311222806Sae * a) for unresolved fragments; 312222806Sae * b) libalias returns PKT_ALIAS_IGNORED and 313222806Sae * PKT_ALIAS_DENY_INCOMING flag is set. 314222806Sae */ 315222806Sae if (retval == PKT_ALIAS_ERROR || 316222806Sae (args->oif == NULL && (retval == PKT_ALIAS_UNRESOLVED_FRAGMENT || 317222806Sae (retval == PKT_ALIAS_IGNORED && 318223080Sae (t->mode & PKT_ALIAS_DENY_INCOMING) != 0)))) { 319176669Spiso /* XXX - should i add some logging? */ 320176669Spiso m_free(mcl); 321176669Spiso args->m = NULL; 322176669Spiso return (IP_FW_DENY); 323176669Spiso } 324222806Sae 325222806Sae if (retval == PKT_ALIAS_RESPOND) 326232171Sae mcl->m_flags |= M_SKIP_FIREWALL; 327200909Sluigi mcl->m_pkthdr.len = mcl->m_len = ntohs(ip->ip_len); 328176669Spiso 329200909Sluigi /* 330200909Sluigi * XXX - libalias checksum offload 331200909Sluigi * 'duct tape' (see above) 332176669Spiso */ 333176669Spiso 334200909Sluigi if ((ip->ip_off & htons(IP_OFFMASK)) == 0 && 335176669Spiso ip->ip_p == IPPROTO_TCP) { 336200909Sluigi struct tcphdr *th; 337176669Spiso 338176669Spiso th = (struct tcphdr *)(ip + 1); 339200909Sluigi if (th->th_x2) 340176669Spiso ldt = 1; 341176669Spiso } 342176669Spiso 343176669Spiso if (ldt) { 344176669Spiso struct tcphdr *th; 345176669Spiso struct udphdr *uh; 346176669Spiso u_short cksum; 347176669Spiso 348212256Sglebius ip->ip_len = ntohs(ip->ip_len); 349212256Sglebius cksum = in_pseudo(ip->ip_src.s_addr, ip->ip_dst.s_addr, 350212256Sglebius htons(ip->ip_p + ip->ip_len - (ip->ip_hl << 2))); 351200909Sluigi 352176669Spiso switch (ip->ip_p) { 353176669Spiso case IPPROTO_TCP: 354176669Spiso th = (struct tcphdr *)(ip + 1); 355200909Sluigi /* 356200909Sluigi * Maybe it was set in 357200909Sluigi * libalias... 358176669Spiso */ 359176669Spiso th->th_x2 = 0; 360176669Spiso th->th_sum = cksum; 361200909Sluigi mcl->m_pkthdr.csum_data = 362176669Spiso offsetof(struct tcphdr, th_sum); 363176669Spiso break; 364176669Spiso case IPPROTO_UDP: 365176669Spiso uh = (struct udphdr *)(ip + 1); 366176669Spiso uh->uh_sum = cksum; 367200909Sluigi mcl->m_pkthdr.csum_data = 368176669Spiso offsetof(struct udphdr, uh_sum); 369200909Sluigi break; 370176669Spiso } 371200909Sluigi /* No hw checksum offloading: do it ourselves */ 372200909Sluigi if ((mcl->m_pkthdr.csum_flags & CSUM_DELAY_DATA) == 0) { 373176669Spiso in_delayed_cksum(mcl); 374200909Sluigi mcl->m_pkthdr.csum_flags &= ~CSUM_DELAY_DATA; 375176669Spiso } 376212256Sglebius ip->ip_len = htons(ip->ip_len); 377176669Spiso } 378176669Spiso args->m = mcl; 379176669Spiso return (IP_FW_NAT); 380176669Spiso} 381176669Spiso 382200580Sluigistatic struct cfg_nat * 383200580Sluigilookup_nat(struct nat_list *l, int nat_id) 384200580Sluigi{ 385200580Sluigi struct cfg_nat *res; 386200580Sluigi 387200909Sluigi LIST_FOREACH(res, l, _next) { 388200909Sluigi if (res->id == nat_id) 389200909Sluigi break; 390200909Sluigi } 391200580Sluigi return res; 392200580Sluigi} 393200580Sluigi 394200909Sluigistatic int 395176669Spisoipfw_nat_cfg(struct sockopt *sopt) 396176669Spiso{ 397220837Sglebius struct cfg_nat *cfg, *ptr; 398176669Spiso char *buf; 399200897Sluigi struct ip_fw_chain *chain = &V_layer3_chain; 400220914Sglebius size_t len; 401220914Sglebius int gencnt, error = 0; 402176669Spiso 403220837Sglebius len = sopt->sopt_valsize; 404220837Sglebius buf = malloc(len, M_TEMP, M_WAITOK | M_ZERO); 405220837Sglebius if ((error = sooptcopyin(sopt, buf, len, sizeof(struct cfg_nat))) != 0) 406220837Sglebius goto out; 407176669Spiso 408220837Sglebius cfg = (struct cfg_nat *)buf; 409220837Sglebius if (cfg->id < 0) { 410220837Sglebius error = EINVAL; 411220837Sglebius goto out; 412220837Sglebius } 413220837Sglebius 414200909Sluigi /* 415176669Spiso * Find/create nat rule. 416176669Spiso */ 417200897Sluigi IPFW_WLOCK(chain); 418220837Sglebius gencnt = chain->gencnt; 419220837Sglebius ptr = lookup_nat(&chain->nat, cfg->id); 420176669Spiso if (ptr == NULL) { 421220800Sglebius IPFW_WUNLOCK(chain); 422176669Spiso /* New rule: allocate and init new instance. */ 423220800Sglebius ptr = malloc(sizeof(struct cfg_nat), M_IPFW, M_WAITOK | M_ZERO); 424176669Spiso ptr->lib = LibAliasInit(NULL); 425176669Spiso LIST_INIT(&ptr->redir_chain); 426176669Spiso } else { 427220837Sglebius /* Entry already present: temporarily unhook it. */ 428200897Sluigi LIST_REMOVE(ptr, _next); 429220837Sglebius flush_nat_ptrs(chain, cfg->id); 430220800Sglebius IPFW_WUNLOCK(chain); 431176669Spiso } 432176669Spiso 433200909Sluigi /* 434176669Spiso * Basic nat configuration. 435176669Spiso */ 436220837Sglebius ptr->id = cfg->id; 437200909Sluigi /* 438200909Sluigi * XXX - what if this rule doesn't nat any ip and just 439200909Sluigi * redirect? 440176669Spiso * do we set aliasaddress to 0.0.0.0? 441176669Spiso */ 442220837Sglebius ptr->ip = cfg->ip; 443220837Sglebius ptr->redir_cnt = cfg->redir_cnt; 444220837Sglebius ptr->mode = cfg->mode; 445266680Sae LibAliasSetMode(ptr->lib, cfg->mode, ~0); 446176669Spiso LibAliasSetAddress(ptr->lib, ptr->ip); 447220837Sglebius memcpy(ptr->if_name, cfg->if_name, IF_NAMESIZE); 448176669Spiso 449200909Sluigi /* 450176669Spiso * Redir and LSNAT configuration. 451176669Spiso */ 452176669Spiso /* Delete old cfgs. */ 453176669Spiso del_redir_spool_cfg(ptr, &ptr->redir_chain); 454176669Spiso /* Add new entries. */ 455176669Spiso add_redir_spool_cfg(&buf[(sizeof(struct cfg_nat))], ptr); 456220837Sglebius 457200897Sluigi IPFW_WLOCK(chain); 458220837Sglebius /* Extra check to avoid race with another ipfw_nat_cfg() */ 459220837Sglebius if (gencnt != chain->gencnt && 460220837Sglebius ((cfg = lookup_nat(&chain->nat, ptr->id)) != NULL)) 461220837Sglebius LIST_REMOVE(cfg, _next); 462200897Sluigi LIST_INSERT_HEAD(&chain->nat, ptr, _next); 463220837Sglebius chain->gencnt++; 464200897Sluigi IPFW_WUNLOCK(chain); 465220837Sglebius 466220837Sglebiusout: 467220837Sglebius free(buf, M_TEMP); 468220837Sglebius return (error); 469176669Spiso} 470176669Spiso 471176669Spisostatic int 472176669Spisoipfw_nat_del(struct sockopt *sopt) 473176669Spiso{ 474176669Spiso struct cfg_nat *ptr; 475200897Sluigi struct ip_fw_chain *chain = &V_layer3_chain; 476176669Spiso int i; 477200909Sluigi 478176669Spiso sooptcopyin(sopt, &i, sizeof i, sizeof i); 479200909Sluigi /* XXX validate i */ 480200897Sluigi IPFW_WLOCK(chain); 481200909Sluigi ptr = lookup_nat(&chain->nat, i); 482176669Spiso if (ptr == NULL) { 483200897Sluigi IPFW_WUNLOCK(chain); 484176669Spiso return (EINVAL); 485176669Spiso } 486200897Sluigi LIST_REMOVE(ptr, _next); 487200897Sluigi flush_nat_ptrs(chain, i); 488200897Sluigi IPFW_WUNLOCK(chain); 489176669Spiso del_redir_spool_cfg(ptr, &ptr->redir_chain); 490176669Spiso LibAliasUninit(ptr->lib); 491176669Spiso free(ptr, M_IPFW); 492176669Spiso return (0); 493176669Spiso} 494176669Spiso 495176669Spisostatic int 496176669Spisoipfw_nat_get_cfg(struct sockopt *sopt) 497200909Sluigi{ 498220837Sglebius struct ip_fw_chain *chain = &V_layer3_chain; 499176669Spiso struct cfg_nat *n; 500176669Spiso struct cfg_redir *r; 501176669Spiso struct cfg_spool *s; 502220837Sglebius char *data; 503220837Sglebius int gencnt, nat_cnt, len, error; 504200897Sluigi 505176669Spiso nat_cnt = 0; 506220837Sglebius len = sizeof(nat_cnt); 507176669Spiso 508200897Sluigi IPFW_RLOCK(chain); 509220837Sglebiusretry: 510220837Sglebius gencnt = chain->gencnt; 511220837Sglebius /* Estimate memory amount */ 512200897Sluigi LIST_FOREACH(n, &chain->nat, _next) { 513176669Spiso nat_cnt++; 514220837Sglebius len += sizeof(struct cfg_nat); 515200909Sluigi LIST_FOREACH(r, &n->redir_chain, _next) { 516220837Sglebius len += sizeof(struct cfg_redir); 517220837Sglebius LIST_FOREACH(s, &r->spool_chain, _next) 518220837Sglebius len += sizeof(struct cfg_spool); 519220837Sglebius } 520220837Sglebius } 521220837Sglebius IPFW_RUNLOCK(chain); 522220837Sglebius 523220837Sglebius data = malloc(len, M_TEMP, M_WAITOK | M_ZERO); 524220837Sglebius bcopy(&nat_cnt, data, sizeof(nat_cnt)); 525220837Sglebius 526220837Sglebius nat_cnt = 0; 527220837Sglebius len = sizeof(nat_cnt); 528220837Sglebius 529220837Sglebius IPFW_RLOCK(chain); 530220837Sglebius if (gencnt != chain->gencnt) { 531220837Sglebius free(data, M_TEMP); 532220837Sglebius goto retry; 533220837Sglebius } 534220837Sglebius /* Serialize all the data. */ 535220837Sglebius LIST_FOREACH(n, &chain->nat, _next) { 536220837Sglebius bcopy(n, &data[len], sizeof(struct cfg_nat)); 537220837Sglebius len += sizeof(struct cfg_nat); 538220837Sglebius LIST_FOREACH(r, &n->redir_chain, _next) { 539220837Sglebius bcopy(r, &data[len], sizeof(struct cfg_redir)); 540220837Sglebius len += sizeof(struct cfg_redir); 541200909Sluigi LIST_FOREACH(s, &r->spool_chain, _next) { 542220837Sglebius bcopy(s, &data[len], sizeof(struct cfg_spool)); 543220837Sglebius len += sizeof(struct cfg_spool); 544176669Spiso } 545200909Sluigi } 546176669Spiso } 547200897Sluigi IPFW_RUNLOCK(chain); 548220837Sglebius 549220837Sglebius error = sooptcopyout(sopt, data, len); 550220837Sglebius free(data, M_TEMP); 551220837Sglebius 552220837Sglebius return (error); 553176669Spiso} 554176669Spiso 555176669Spisostatic int 556176669Spisoipfw_nat_get_log(struct sockopt *sopt) 557176669Spiso{ 558176669Spiso uint8_t *data; 559176669Spiso struct cfg_nat *ptr; 560200909Sluigi int i, size; 561200897Sluigi struct ip_fw_chain *chain; 562176669Spiso 563200897Sluigi chain = &V_layer3_chain; 564176669Spiso 565200897Sluigi IPFW_RLOCK(chain); 566200909Sluigi /* one pass to count, one to copy the data */ 567200909Sluigi i = 0; 568200897Sluigi LIST_FOREACH(ptr, &chain->nat, _next) { 569200909Sluigi if (ptr->lib->logDesc == NULL) 570176669Spiso continue; 571200909Sluigi i++; 572200909Sluigi } 573200909Sluigi size = i * (LIBALIAS_BUF_SIZE + sizeof(int)); 574200909Sluigi data = malloc(size, M_IPFW, M_NOWAIT | M_ZERO); 575200909Sluigi if (data == NULL) { 576200909Sluigi IPFW_RUNLOCK(chain); 577200909Sluigi return (ENOSPC); 578200909Sluigi } 579200909Sluigi i = 0; 580200909Sluigi LIST_FOREACH(ptr, &chain->nat, _next) { 581200909Sluigi if (ptr->lib->logDesc == NULL) 582200909Sluigi continue; 583176669Spiso bcopy(&ptr->id, &data[i], sizeof(int)); 584176669Spiso i += sizeof(int); 585200909Sluigi bcopy(ptr->lib->logDesc, &data[i], LIBALIAS_BUF_SIZE); 586200909Sluigi i += LIBALIAS_BUF_SIZE; 587176669Spiso } 588200897Sluigi IPFW_RUNLOCK(chain); 589176669Spiso sooptcopyout(sopt, data, size); 590176669Spiso free(data, M_IPFW); 591176669Spiso return(0); 592176669Spiso} 593176669Spiso 594255395Strocinystatic int 595255395Strocinyvnet_ipfw_nat_init(const void *arg __unused) 596255395Strociny{ 597255395Strociny 598255395Strociny V_ipfw_nat_ready = 1; 599255395Strociny return (0); 600255395Strociny} 601255395Strociny 602255395Strocinystatic int 603255395Strocinyvnet_ipfw_nat_uninit(const void *arg __unused) 604255395Strociny{ 605255395Strociny struct cfg_nat *ptr, *ptr_temp; 606255395Strociny struct ip_fw_chain *chain; 607255395Strociny 608255395Strociny chain = &V_layer3_chain; 609255395Strociny IPFW_WLOCK(chain); 610255395Strociny LIST_FOREACH_SAFE(ptr, &chain->nat, _next, ptr_temp) { 611255395Strociny LIST_REMOVE(ptr, _next); 612255395Strociny del_redir_spool_cfg(ptr, &ptr->redir_chain); 613255395Strociny LibAliasUninit(ptr->lib); 614255395Strociny free(ptr, M_IPFW); 615255395Strociny } 616255395Strociny flush_nat_ptrs(chain, -1 /* flush all */); 617255395Strociny V_ipfw_nat_ready = 0; 618255395Strociny IPFW_WUNLOCK(chain); 619255395Strociny return (0); 620255395Strociny} 621255395Strociny 622176669Spisostatic void 623176669Spisoipfw_nat_init(void) 624176669Spiso{ 625176669Spiso 626176669Spiso /* init ipfw hooks */ 627176669Spiso ipfw_nat_ptr = ipfw_nat; 628200580Sluigi lookup_nat_ptr = lookup_nat; 629176669Spiso ipfw_nat_cfg_ptr = ipfw_nat_cfg; 630176669Spiso ipfw_nat_del_ptr = ipfw_nat_del; 631176669Spiso ipfw_nat_get_cfg_ptr = ipfw_nat_get_cfg; 632176669Spiso ipfw_nat_get_log_ptr = ipfw_nat_get_log; 633255395Strociny 634255395Strociny ifaddr_event_tag = EVENTHANDLER_REGISTER(ifaddr_event, ifaddr_change, 635176669Spiso NULL, EVENTHANDLER_PRI_ANY); 636176669Spiso} 637176669Spiso 638176669Spisostatic void 639176669Spisoipfw_nat_destroy(void) 640176669Spiso{ 641200897Sluigi 642255395Strociny EVENTHANDLER_DEREGISTER(ifaddr_event, ifaddr_event_tag); 643176669Spiso /* deregister ipfw_nat */ 644176669Spiso ipfw_nat_ptr = NULL; 645200580Sluigi lookup_nat_ptr = NULL; 646200580Sluigi ipfw_nat_cfg_ptr = NULL; 647200580Sluigi ipfw_nat_del_ptr = NULL; 648200580Sluigi ipfw_nat_get_cfg_ptr = NULL; 649200580Sluigi ipfw_nat_get_log_ptr = NULL; 650176669Spiso} 651176669Spiso 652176669Spisostatic int 653176669Spisoipfw_nat_modevent(module_t mod, int type, void *unused) 654176669Spiso{ 655176669Spiso int err = 0; 656176669Spiso 657176669Spiso switch (type) { 658176669Spiso case MOD_LOAD: 659176669Spiso break; 660176669Spiso 661176669Spiso case MOD_UNLOAD: 662176669Spiso break; 663176669Spiso 664176669Spiso default: 665176669Spiso return EOPNOTSUPP; 666176669Spiso break; 667176669Spiso } 668176669Spiso return err; 669176669Spiso} 670176669Spiso 671176669Spisostatic moduledata_t ipfw_nat_mod = { 672176669Spiso "ipfw_nat", 673176669Spiso ipfw_nat_modevent, 674176669Spiso 0 675176669Spiso}; 676176669Spiso 677255395Strociny/* Define startup order. */ 678255395Strociny#define IPFW_NAT_SI_SUB_FIREWALL SI_SUB_PROTO_IFATTACHDOMAIN 679264247Sdteske#define IPFW_NAT_MODEVENT_ORDER (SI_ORDER_ANY - 128) 680255395Strociny#define IPFW_NAT_MODULE_ORDER (IPFW_NAT_MODEVENT_ORDER + 1) 681255395Strociny#define IPFW_NAT_VNET_ORDER (IPFW_NAT_MODEVENT_ORDER + 2) 682255395Strociny 683255395StrocinyDECLARE_MODULE(ipfw_nat, ipfw_nat_mod, IPFW_NAT_SI_SUB_FIREWALL, SI_ORDER_ANY); 684176669SpisoMODULE_DEPEND(ipfw_nat, libalias, 1, 1, 1); 685176669SpisoMODULE_DEPEND(ipfw_nat, ipfw, 2, 2, 2); 686176669SpisoMODULE_VERSION(ipfw_nat, 1); 687255395Strociny 688255395StrocinySYSINIT(ipfw_nat_init, IPFW_NAT_SI_SUB_FIREWALL, IPFW_NAT_MODULE_ORDER, 689255395Strociny ipfw_nat_init, NULL); 690255395StrocinyVNET_SYSINIT(vnet_ipfw_nat_init, IPFW_NAT_SI_SUB_FIREWALL, IPFW_NAT_VNET_ORDER, 691255395Strociny vnet_ipfw_nat_init, NULL); 692255395Strociny 693255395StrocinySYSUNINIT(ipfw_nat_destroy, IPFW_NAT_SI_SUB_FIREWALL, IPFW_NAT_MODULE_ORDER, 694255395Strociny ipfw_nat_destroy, NULL); 695255395StrocinyVNET_SYSUNINIT(vnet_ipfw_nat_uninit, IPFW_NAT_SI_SUB_FIREWALL, 696255395Strociny IPFW_NAT_VNET_ORDER, vnet_ipfw_nat_uninit, NULL); 697255395Strociny 698200601Sluigi/* end of file */ 699