ip_fw_nat.c revision 200909
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: head/sys/netinet/ipfw/ip_fw_nat.c 200909 2009-12-23 18:53:11Z luigi $"); 29176669Spiso 30176669Spiso#include <sys/param.h> 31176669Spiso#include <sys/systm.h> 32176669Spiso#include <sys/condvar.h> 33176669Spiso#include <sys/eventhandler.h> 34176669Spiso#include <sys/malloc.h> 35176669Spiso#include <sys/kernel.h> 36176669Spiso#include <sys/lock.h> 37176669Spiso#include <sys/module.h> 38176669Spiso#include <sys/priv.h> 39176669Spiso#include <sys/proc.h> 40176669Spiso#include <sys/rwlock.h> 41176669Spiso 42200580Sluigi#define IPFW_INTERNAL /* Access to protected data structures in ip_fw.h. */ 43200580Sluigi 44176669Spiso#include <netinet/libalias/alias.h> 45176669Spiso#include <netinet/libalias/alias_local.h> 46176669Spiso 47176669Spiso#include <net/if.h> 48176669Spiso#include <netinet/in.h> 49176669Spiso#include <netinet/ip.h> 50176669Spiso#include <netinet/ip_var.h> 51176669Spiso#include <netinet/ip_fw.h> 52200580Sluigi#include <netinet/ipfw/ip_fw_private.h> 53176669Spiso#include <netinet/tcp.h> 54176669Spiso#include <netinet/udp.h> 55176669Spiso 56176669Spiso#include <machine/in_cksum.h> /* XXX for in_cksum */ 57176669Spiso 58195699Srwatsonstatic VNET_DEFINE(eventhandler_tag, ifaddr_event_tag); 59195727Srwatson#define V_ifaddr_event_tag VNET(ifaddr_event_tag) 60176669Spiso 61200909Sluigistatic void 62176669Spisoifaddr_change(void *arg __unused, struct ifnet *ifp) 63176669Spiso{ 64176669Spiso struct cfg_nat *ptr; 65176669Spiso struct ifaddr *ifa; 66200897Sluigi struct ip_fw_chain *chain; 67176669Spiso 68200897Sluigi chain = &V_layer3_chain; 69200909Sluigi IPFW_WLOCK(chain); 70176669Spiso /* Check every nat entry... */ 71200897Sluigi LIST_FOREACH(ptr, &chain->nat, _next) { 72176669Spiso /* ...using nic 'ifp->if_xname' as dynamic alias address. */ 73200909Sluigi if (strncmp(ptr->if_name, ifp->if_xname, IF_NAMESIZE) != 0) 74200909Sluigi continue; 75200909Sluigi if_addr_rlock(ifp); 76200909Sluigi TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) { 77200909Sluigi if (ifa->ifa_addr == NULL) 78200909Sluigi continue; 79200909Sluigi if (ifa->ifa_addr->sa_family != AF_INET) 80200909Sluigi continue; 81200909Sluigi ptr->ip = ((struct sockaddr_in *) 82200909Sluigi (ifa->ifa_addr))->sin_addr; 83200909Sluigi LibAliasSetAddress(ptr->lib, ptr->ip); 84176669Spiso } 85200909Sluigi if_addr_runlock(ifp); 86176669Spiso } 87200909Sluigi IPFW_WUNLOCK(chain); 88176669Spiso} 89176669Spiso 90200897Sluigi/* 91200897Sluigi * delete the pointers for nat entry ix, or all of them if ix < 0 92200897Sluigi */ 93176669Spisostatic void 94200897Sluigiflush_nat_ptrs(struct ip_fw_chain *chain, const int ix) 95176669Spiso{ 96200897Sluigi int i; 97200909Sluigi ipfw_insn_nat *cmd; 98176669Spiso 99200897Sluigi IPFW_WLOCK_ASSERT(chain); 100200897Sluigi for (i = 0; i < chain->n_rules; i++) { 101200909Sluigi cmd = (ipfw_insn_nat *)ACTION_PTR(chain->map[i]); 102200909Sluigi /* XXX skip log and the like ? */ 103200897Sluigi if (cmd->o.opcode == O_NAT && cmd->nat != NULL && 104200897Sluigi (ix < 0 || cmd->nat->id == ix)) 105176669Spiso cmd->nat = NULL; 106176669Spiso } 107176669Spiso} 108176669Spiso 109176669Spisostatic void 110176669Spisodel_redir_spool_cfg(struct cfg_nat *n, struct redir_chain *head) 111176669Spiso{ 112176669Spiso struct cfg_redir *r, *tmp_r; 113176669Spiso struct cfg_spool *s, *tmp_s; 114176669Spiso int i, num; 115176669Spiso 116176669Spiso LIST_FOREACH_SAFE(r, head, _next, tmp_r) { 117176669Spiso num = 1; /* Number of alias_link to delete. */ 118176669Spiso switch (r->mode) { 119176669Spiso case REDIR_PORT: 120176669Spiso num = r->pport_cnt; 121176669Spiso /* FALLTHROUGH */ 122176669Spiso case REDIR_ADDR: 123176669Spiso case REDIR_PROTO: 124176669Spiso /* Delete all libalias redirect entry. */ 125176669Spiso for (i = 0; i < num; i++) 126176669Spiso LibAliasRedirectDelete(n->lib, r->alink[i]); 127176669Spiso /* Del spool cfg if any. */ 128176669Spiso LIST_FOREACH_SAFE(s, &r->spool_chain, _next, tmp_s) { 129176669Spiso LIST_REMOVE(s, _next); 130176669Spiso free(s, M_IPFW); 131176669Spiso } 132176669Spiso free(r->alink, M_IPFW); 133176669Spiso LIST_REMOVE(r, _next); 134176669Spiso free(r, M_IPFW); 135176669Spiso break; 136176669Spiso default: 137200909Sluigi printf("unknown redirect mode: %u\n", r->mode); 138176669Spiso /* XXX - panic?!?!? */ 139200909Sluigi break; 140176669Spiso } 141176669Spiso } 142176669Spiso} 143176669Spiso 144176669Spisostatic int 145176669Spisoadd_redir_spool_cfg(char *buf, struct cfg_nat *ptr) 146176669Spiso{ 147176669Spiso struct cfg_redir *r, *ser_r; 148176669Spiso struct cfg_spool *s, *ser_s; 149176669Spiso int cnt, off, i; 150176669Spiso 151176669Spiso for (cnt = 0, off = 0; cnt < ptr->redir_cnt; cnt++) { 152176669Spiso ser_r = (struct cfg_redir *)&buf[off]; 153176669Spiso r = malloc(SOF_REDIR, M_IPFW, M_WAITOK | M_ZERO); 154176669Spiso memcpy(r, ser_r, SOF_REDIR); 155176669Spiso LIST_INIT(&r->spool_chain); 156176669Spiso off += SOF_REDIR; 157176669Spiso r->alink = malloc(sizeof(struct alias_link *) * r->pport_cnt, 158176669Spiso M_IPFW, M_WAITOK | M_ZERO); 159176669Spiso switch (r->mode) { 160176669Spiso case REDIR_ADDR: 161176669Spiso r->alink[0] = LibAliasRedirectAddr(ptr->lib, r->laddr, 162176669Spiso r->paddr); 163176669Spiso break; 164176669Spiso case REDIR_PORT: 165176669Spiso for (i = 0 ; i < r->pport_cnt; i++) { 166176669Spiso /* If remotePort is all ports, set it to 0. */ 167176669Spiso u_short remotePortCopy = r->rport + i; 168176669Spiso if (r->rport_cnt == 1 && r->rport == 0) 169176669Spiso remotePortCopy = 0; 170176669Spiso r->alink[i] = LibAliasRedirectPort(ptr->lib, 171176669Spiso r->laddr, htons(r->lport + i), r->raddr, 172200909Sluigi htons(remotePortCopy), r->paddr, 173176669Spiso htons(r->pport + i), r->proto); 174176669Spiso if (r->alink[i] == NULL) { 175176669Spiso r->alink[0] = NULL; 176176669Spiso break; 177176669Spiso } 178176669Spiso } 179176669Spiso break; 180176669Spiso case REDIR_PROTO: 181176669Spiso r->alink[0] = LibAliasRedirectProto(ptr->lib ,r->laddr, 182176669Spiso r->raddr, r->paddr, r->proto); 183176669Spiso break; 184176669Spiso default: 185176669Spiso printf("unknown redirect mode: %u\n", r->mode); 186200909Sluigi break; 187176669Spiso } 188200909Sluigi /* XXX perhaps return an error instead of panic ? */ 189200909Sluigi if (r->alink[0] == NULL) 190200909Sluigi panic("LibAliasRedirect* returned NULL"); 191200909Sluigi /* LSNAT handling. */ 192200909Sluigi for (i = 0; i < r->spool_cnt; i++) { 193200909Sluigi ser_s = (struct cfg_spool *)&buf[off]; 194200909Sluigi s = malloc(SOF_REDIR, M_IPFW, M_WAITOK | M_ZERO); 195200909Sluigi memcpy(s, ser_s, SOF_SPOOL); 196200909Sluigi LibAliasAddServer(ptr->lib, r->alink[0], 197200909Sluigi s->addr, htons(s->port)); 198200909Sluigi off += SOF_SPOOL; 199200909Sluigi /* Hook spool entry. */ 200200909Sluigi LIST_INSERT_HEAD(&r->spool_chain, s, _next); 201200909Sluigi } 202176669Spiso /* And finally hook this redir entry. */ 203200897Sluigi LIST_INSERT_HEAD(&ptr->redir_chain, r, _next); 204176669Spiso } 205176669Spiso return (1); 206176669Spiso} 207176669Spiso 208176669Spisostatic int 209176669Spisoipfw_nat(struct ip_fw_args *args, struct cfg_nat *t, struct mbuf *m) 210176669Spiso{ 211176669Spiso struct mbuf *mcl; 212176669Spiso struct ip *ip; 213176669Spiso /* XXX - libalias duct tape */ 214176669Spiso int ldt, retval; 215176669Spiso char *c; 216176669Spiso 217176669Spiso ldt = 0; 218176669Spiso retval = 0; 219200909Sluigi mcl = m_megapullup(m, m->m_pkthdr.len); 220200909Sluigi if (mcl == NULL) { 221200909Sluigi args->m = NULL; 222200909Sluigi return (IP_FW_DENY); 223200909Sluigi } 224176669Spiso ip = mtod(mcl, struct ip *); 225176669Spiso if (args->eh == NULL) { 226176669Spiso ip->ip_len = htons(ip->ip_len); 227176669Spiso ip->ip_off = htons(ip->ip_off); 228176669Spiso } 229176669Spiso 230200909Sluigi /* 231176669Spiso * XXX - Libalias checksum offload 'duct tape': 232176669Spiso * 233200909Sluigi * locally generated packets have only pseudo-header checksum 234200909Sluigi * calculated and libalias will break it[1], so mark them for 235200909Sluigi * later fix. Moreover there are cases when libalias modifies 236200909Sluigi * tcp packet data[2], mark them for later fix too. 237176669Spiso * 238200909Sluigi * [1] libalias was never meant to run in kernel, so it does 239200909Sluigi * not have any knowledge about checksum offloading, and 240200909Sluigi * expects a packet with a full internet checksum. 241200909Sluigi * Unfortunately, packets generated locally will have just the 242200909Sluigi * pseudo header calculated, and when libalias tries to adjust 243200909Sluigi * the checksum it will actually compute a wrong value. 244176669Spiso * 245200909Sluigi * [2] when libalias modifies tcp's data content, full TCP 246200909Sluigi * checksum has to be recomputed: the problem is that 247200909Sluigi * libalias does not have any idea about checksum offloading. 248200909Sluigi * To work around this, we do not do checksumming in LibAlias, 249200909Sluigi * but only mark the packets in th_x2 field. If we receive a 250200909Sluigi * marked packet, we calculate correct checksum for it 251200909Sluigi * aware of offloading. Why such a terrible hack instead of 252200909Sluigi * recalculating checksum for each packet? 253200909Sluigi * Because the previous checksum was not checked! 254200909Sluigi * Recalculating checksums for EVERY packet will hide ALL 255200909Sluigi * transmission errors. Yes, marked packets still suffer from 256200909Sluigi * this problem. But, sigh, natd(8) has this problem, too. 257200909Sluigi * 258176669Spiso * TODO: -make libalias mbuf aware (so 259176669Spiso * it can handle delayed checksum and tso) 260176669Spiso */ 261176669Spiso 262200909Sluigi if (mcl->m_pkthdr.rcvif == NULL && 263200909Sluigi mcl->m_pkthdr.csum_flags & CSUM_DELAY_DATA) 264176669Spiso ldt = 1; 265176669Spiso 266176669Spiso c = mtod(mcl, char *); 267176669Spiso if (args->oif == NULL) 268200909Sluigi retval = LibAliasIn(t->lib, c, 269179473Smav mcl->m_len + M_TRAILINGSPACE(mcl)); 270176669Spiso else 271200909Sluigi retval = LibAliasOut(t->lib, c, 272179473Smav mcl->m_len + M_TRAILINGSPACE(mcl)); 273188294Spiso if (retval == PKT_ALIAS_RESPOND) { 274200909Sluigi m->m_flags |= M_SKIP_FIREWALL; 275200909Sluigi retval = PKT_ALIAS_OK; 276188294Spiso } 277179473Smav if (retval != PKT_ALIAS_OK && 278179473Smav retval != PKT_ALIAS_FOUND_HEADER_FRAGMENT) { 279176669Spiso /* XXX - should i add some logging? */ 280176669Spiso m_free(mcl); 281176669Spiso args->m = NULL; 282176669Spiso return (IP_FW_DENY); 283176669Spiso } 284200909Sluigi mcl->m_pkthdr.len = mcl->m_len = ntohs(ip->ip_len); 285176669Spiso 286200909Sluigi /* 287200909Sluigi * XXX - libalias checksum offload 288200909Sluigi * 'duct tape' (see above) 289176669Spiso */ 290176669Spiso 291200909Sluigi if ((ip->ip_off & htons(IP_OFFMASK)) == 0 && 292176669Spiso ip->ip_p == IPPROTO_TCP) { 293200909Sluigi struct tcphdr *th; 294176669Spiso 295176669Spiso th = (struct tcphdr *)(ip + 1); 296200909Sluigi if (th->th_x2) 297176669Spiso ldt = 1; 298176669Spiso } 299176669Spiso 300176669Spiso if (ldt) { 301176669Spiso struct tcphdr *th; 302176669Spiso struct udphdr *uh; 303176669Spiso u_short cksum; 304176669Spiso 305176669Spiso ip->ip_len = ntohs(ip->ip_len); 306176669Spiso cksum = in_pseudo( 307176669Spiso ip->ip_src.s_addr, 308200909Sluigi ip->ip_dst.s_addr, 309176669Spiso htons(ip->ip_p + ip->ip_len - (ip->ip_hl << 2)) 310176669Spiso ); 311200909Sluigi 312176669Spiso switch (ip->ip_p) { 313176669Spiso case IPPROTO_TCP: 314176669Spiso th = (struct tcphdr *)(ip + 1); 315200909Sluigi /* 316200909Sluigi * Maybe it was set in 317200909Sluigi * libalias... 318176669Spiso */ 319176669Spiso th->th_x2 = 0; 320176669Spiso th->th_sum = cksum; 321200909Sluigi mcl->m_pkthdr.csum_data = 322176669Spiso offsetof(struct tcphdr, th_sum); 323176669Spiso break; 324176669Spiso case IPPROTO_UDP: 325176669Spiso uh = (struct udphdr *)(ip + 1); 326176669Spiso uh->uh_sum = cksum; 327200909Sluigi mcl->m_pkthdr.csum_data = 328176669Spiso offsetof(struct udphdr, uh_sum); 329200909Sluigi break; 330176669Spiso } 331200909Sluigi /* No hw checksum offloading: do it ourselves */ 332200909Sluigi if ((mcl->m_pkthdr.csum_flags & CSUM_DELAY_DATA) == 0) { 333176669Spiso in_delayed_cksum(mcl); 334200909Sluigi mcl->m_pkthdr.csum_flags &= ~CSUM_DELAY_DATA; 335176669Spiso } 336176669Spiso ip->ip_len = htons(ip->ip_len); 337176669Spiso } 338176669Spiso 339176669Spiso if (args->eh == NULL) { 340176669Spiso ip->ip_len = ntohs(ip->ip_len); 341176669Spiso ip->ip_off = ntohs(ip->ip_off); 342176669Spiso } 343176669Spiso 344176669Spiso args->m = mcl; 345176669Spiso return (IP_FW_NAT); 346176669Spiso} 347176669Spiso 348200580Sluigistatic struct cfg_nat * 349200580Sluigilookup_nat(struct nat_list *l, int nat_id) 350200580Sluigi{ 351200580Sluigi struct cfg_nat *res; 352200580Sluigi 353200909Sluigi LIST_FOREACH(res, l, _next) { 354200909Sluigi if (res->id == nat_id) 355200909Sluigi break; 356200909Sluigi } 357200580Sluigi return res; 358200580Sluigi} 359200580Sluigi 360200909Sluigistatic int 361176669Spisoipfw_nat_cfg(struct sockopt *sopt) 362176669Spiso{ 363176669Spiso struct cfg_nat *ptr, *ser_n; 364176669Spiso char *buf; 365200897Sluigi struct ip_fw_chain *chain = &V_layer3_chain; 366176669Spiso 367176669Spiso buf = malloc(NAT_BUF_LEN, M_IPFW, M_WAITOK | M_ZERO); 368200909Sluigi sooptcopyin(sopt, buf, NAT_BUF_LEN, sizeof(struct cfg_nat)); 369176669Spiso ser_n = (struct cfg_nat *)buf; 370176669Spiso 371200897Sluigi /* check valid parameter ser_n->id > 0 ? */ 372200909Sluigi /* 373176669Spiso * Find/create nat rule. 374176669Spiso */ 375200897Sluigi IPFW_WLOCK(chain); 376200909Sluigi ptr = lookup_nat(&chain->nat, ser_n->id); 377176669Spiso if (ptr == NULL) { 378176669Spiso /* New rule: allocate and init new instance. */ 379200909Sluigi ptr = malloc(sizeof(struct cfg_nat), 380176669Spiso M_IPFW, M_NOWAIT | M_ZERO); 381176669Spiso if (ptr == NULL) { 382200909Sluigi IPFW_WUNLOCK(chain); 383176669Spiso free(buf, M_IPFW); 384176669Spiso return (ENOSPC); 385176669Spiso } 386176669Spiso ptr->lib = LibAliasInit(NULL); 387176669Spiso if (ptr->lib == NULL) { 388200897Sluigi IPFW_WUNLOCK(chain); 389176669Spiso free(ptr, M_IPFW); 390176669Spiso free(buf, M_IPFW); 391176669Spiso return (EINVAL); 392176669Spiso } 393176669Spiso LIST_INIT(&ptr->redir_chain); 394176669Spiso } else { 395176669Spiso /* Entry already present: temporarly unhook it. */ 396200897Sluigi LIST_REMOVE(ptr, _next); 397200897Sluigi flush_nat_ptrs(chain, ser_n->id); 398176669Spiso } 399200897Sluigi IPFW_WUNLOCK(chain); 400176669Spiso 401200909Sluigi /* 402176669Spiso * Basic nat configuration. 403176669Spiso */ 404176669Spiso ptr->id = ser_n->id; 405200909Sluigi /* 406200909Sluigi * XXX - what if this rule doesn't nat any ip and just 407200909Sluigi * redirect? 408176669Spiso * do we set aliasaddress to 0.0.0.0? 409176669Spiso */ 410176669Spiso ptr->ip = ser_n->ip; 411176669Spiso ptr->redir_cnt = ser_n->redir_cnt; 412176669Spiso ptr->mode = ser_n->mode; 413176669Spiso LibAliasSetMode(ptr->lib, ser_n->mode, ser_n->mode); 414176669Spiso LibAliasSetAddress(ptr->lib, ptr->ip); 415176669Spiso memcpy(ptr->if_name, ser_n->if_name, IF_NAMESIZE); 416176669Spiso 417200909Sluigi /* 418176669Spiso * Redir and LSNAT configuration. 419176669Spiso */ 420176669Spiso /* Delete old cfgs. */ 421176669Spiso del_redir_spool_cfg(ptr, &ptr->redir_chain); 422176669Spiso /* Add new entries. */ 423176669Spiso add_redir_spool_cfg(&buf[(sizeof(struct cfg_nat))], ptr); 424176669Spiso free(buf, M_IPFW); 425200897Sluigi IPFW_WLOCK(chain); 426200897Sluigi LIST_INSERT_HEAD(&chain->nat, ptr, _next); 427200897Sluigi IPFW_WUNLOCK(chain); 428176669Spiso return (0); 429176669Spiso} 430176669Spiso 431176669Spisostatic int 432176669Spisoipfw_nat_del(struct sockopt *sopt) 433176669Spiso{ 434176669Spiso struct cfg_nat *ptr; 435200897Sluigi struct ip_fw_chain *chain = &V_layer3_chain; 436176669Spiso int i; 437200909Sluigi 438176669Spiso sooptcopyin(sopt, &i, sizeof i, sizeof i); 439200909Sluigi /* XXX validate i */ 440200897Sluigi IPFW_WLOCK(chain); 441200909Sluigi ptr = lookup_nat(&chain->nat, i); 442176669Spiso if (ptr == NULL) { 443200897Sluigi IPFW_WUNLOCK(chain); 444176669Spiso return (EINVAL); 445176669Spiso } 446200897Sluigi LIST_REMOVE(ptr, _next); 447200897Sluigi flush_nat_ptrs(chain, i); 448200897Sluigi IPFW_WUNLOCK(chain); 449176669Spiso del_redir_spool_cfg(ptr, &ptr->redir_chain); 450176669Spiso LibAliasUninit(ptr->lib); 451176669Spiso free(ptr, M_IPFW); 452176669Spiso return (0); 453176669Spiso} 454176669Spiso 455176669Spisostatic int 456176669Spisoipfw_nat_get_cfg(struct sockopt *sopt) 457200909Sluigi{ 458176669Spiso uint8_t *data; 459176669Spiso struct cfg_nat *n; 460176669Spiso struct cfg_redir *r; 461176669Spiso struct cfg_spool *s; 462176669Spiso int nat_cnt, off; 463200897Sluigi struct ip_fw_chain *chain; 464200909Sluigi int err = ENOSPC; 465200897Sluigi 466200897Sluigi chain = &V_layer3_chain; 467176669Spiso nat_cnt = 0; 468176669Spiso off = sizeof(nat_cnt); 469176669Spiso 470176669Spiso data = malloc(NAT_BUF_LEN, M_IPFW, M_WAITOK | M_ZERO); 471200897Sluigi IPFW_RLOCK(chain); 472176669Spiso /* Serialize all the data. */ 473200897Sluigi LIST_FOREACH(n, &chain->nat, _next) { 474176669Spiso nat_cnt++; 475200909Sluigi if (off + SOF_NAT >= NAT_BUF_LEN) 476200909Sluigi goto nospace; 477200909Sluigi bcopy(n, &data[off], SOF_NAT); 478200909Sluigi off += SOF_NAT; 479200909Sluigi LIST_FOREACH(r, &n->redir_chain, _next) { 480200909Sluigi if (off + SOF_REDIR >= NAT_BUF_LEN) 481200909Sluigi goto nospace; 482200909Sluigi bcopy(r, &data[off], SOF_REDIR); 483200909Sluigi off += SOF_REDIR; 484200909Sluigi LIST_FOREACH(s, &r->spool_chain, _next) { 485200909Sluigi if (off + SOF_SPOOL >= NAT_BUF_LEN) 486176669Spiso goto nospace; 487200909Sluigi bcopy(s, &data[off], SOF_SPOOL); 488200909Sluigi off += SOF_SPOOL; 489176669Spiso } 490200909Sluigi } 491176669Spiso } 492200909Sluigi err = 0; /* all good */ 493176669Spisonospace: 494200897Sluigi IPFW_RUNLOCK(chain); 495200909Sluigi if (err == 0) { 496200909Sluigi bcopy(&nat_cnt, data, sizeof(nat_cnt)); 497200909Sluigi sooptcopyout(sopt, data, NAT_BUF_LEN); 498200909Sluigi } else { 499200909Sluigi printf("serialized data buffer not big enough:" 500200909Sluigi "please increase NAT_BUF_LEN\n"); 501200909Sluigi } 502176669Spiso free(data, M_IPFW); 503200909Sluigi return (err); 504176669Spiso} 505176669Spiso 506176669Spisostatic int 507176669Spisoipfw_nat_get_log(struct sockopt *sopt) 508176669Spiso{ 509176669Spiso uint8_t *data; 510176669Spiso struct cfg_nat *ptr; 511200909Sluigi int i, size; 512200897Sluigi struct ip_fw_chain *chain; 513176669Spiso 514200897Sluigi chain = &V_layer3_chain; 515176669Spiso 516200897Sluigi IPFW_RLOCK(chain); 517200909Sluigi /* one pass to count, one to copy the data */ 518200909Sluigi i = 0; 519200897Sluigi LIST_FOREACH(ptr, &chain->nat, _next) { 520200909Sluigi if (ptr->lib->logDesc == NULL) 521176669Spiso continue; 522200909Sluigi i++; 523200909Sluigi } 524200909Sluigi size = i * (LIBALIAS_BUF_SIZE + sizeof(int)); 525200909Sluigi data = malloc(size, M_IPFW, M_NOWAIT | M_ZERO); 526200909Sluigi if (data == NULL) { 527200909Sluigi IPFW_RUNLOCK(chain); 528200909Sluigi return (ENOSPC); 529200909Sluigi } 530200909Sluigi i = 0; 531200909Sluigi LIST_FOREACH(ptr, &chain->nat, _next) { 532200909Sluigi if (ptr->lib->logDesc == NULL) 533200909Sluigi continue; 534176669Spiso bcopy(&ptr->id, &data[i], sizeof(int)); 535176669Spiso i += sizeof(int); 536200909Sluigi bcopy(ptr->lib->logDesc, &data[i], LIBALIAS_BUF_SIZE); 537200909Sluigi i += LIBALIAS_BUF_SIZE; 538176669Spiso } 539200897Sluigi IPFW_RUNLOCK(chain); 540176669Spiso sooptcopyout(sopt, data, size); 541176669Spiso free(data, M_IPFW); 542176669Spiso return(0); 543176669Spiso} 544176669Spiso 545176669Spisostatic void 546176669Spisoipfw_nat_init(void) 547176669Spiso{ 548176669Spiso 549181803Sbz IPFW_WLOCK(&V_layer3_chain); 550176669Spiso /* init ipfw hooks */ 551176669Spiso ipfw_nat_ptr = ipfw_nat; 552200580Sluigi lookup_nat_ptr = lookup_nat; 553176669Spiso ipfw_nat_cfg_ptr = ipfw_nat_cfg; 554176669Spiso ipfw_nat_del_ptr = ipfw_nat_del; 555176669Spiso ipfw_nat_get_cfg_ptr = ipfw_nat_get_cfg; 556176669Spiso ipfw_nat_get_log_ptr = ipfw_nat_get_log; 557181803Sbz IPFW_WUNLOCK(&V_layer3_chain); 558200909Sluigi V_ifaddr_event_tag = EVENTHANDLER_REGISTER( 559200909Sluigi ifaddr_event, ifaddr_change, 560176669Spiso NULL, EVENTHANDLER_PRI_ANY); 561176669Spiso} 562176669Spiso 563176669Spisostatic void 564176669Spisoipfw_nat_destroy(void) 565176669Spiso{ 566176669Spiso struct cfg_nat *ptr, *ptr_temp; 567200897Sluigi struct ip_fw_chain *chain; 568200897Sluigi 569200897Sluigi chain = &V_layer3_chain; 570200897Sluigi IPFW_WLOCK(chain); 571200897Sluigi LIST_FOREACH_SAFE(ptr, &chain->nat, _next, ptr_temp) { 572176669Spiso LIST_REMOVE(ptr, _next); 573176669Spiso del_redir_spool_cfg(ptr, &ptr->redir_chain); 574176669Spiso LibAliasUninit(ptr->lib); 575176669Spiso free(ptr, M_IPFW); 576176669Spiso } 577181803Sbz EVENTHANDLER_DEREGISTER(ifaddr_event, V_ifaddr_event_tag); 578200897Sluigi flush_nat_ptrs(chain, -1 /* flush all */); 579176669Spiso /* deregister ipfw_nat */ 580176669Spiso ipfw_nat_ptr = NULL; 581200580Sluigi lookup_nat_ptr = NULL; 582200580Sluigi ipfw_nat_cfg_ptr = NULL; 583200580Sluigi ipfw_nat_del_ptr = NULL; 584200580Sluigi ipfw_nat_get_cfg_ptr = NULL; 585200580Sluigi ipfw_nat_get_log_ptr = NULL; 586200897Sluigi IPFW_WUNLOCK(chain); 587176669Spiso} 588176669Spiso 589176669Spisostatic int 590176669Spisoipfw_nat_modevent(module_t mod, int type, void *unused) 591176669Spiso{ 592176669Spiso int err = 0; 593176669Spiso 594176669Spiso switch (type) { 595176669Spiso case MOD_LOAD: 596176669Spiso ipfw_nat_init(); 597176669Spiso break; 598176669Spiso 599176669Spiso case MOD_UNLOAD: 600176669Spiso ipfw_nat_destroy(); 601176669Spiso break; 602176669Spiso 603176669Spiso default: 604176669Spiso return EOPNOTSUPP; 605176669Spiso break; 606176669Spiso } 607176669Spiso return err; 608176669Spiso} 609176669Spiso 610176669Spisostatic moduledata_t ipfw_nat_mod = { 611176669Spiso "ipfw_nat", 612176669Spiso ipfw_nat_modevent, 613176669Spiso 0 614176669Spiso}; 615176669Spiso 616176669SpisoDECLARE_MODULE(ipfw_nat, ipfw_nat_mod, SI_SUB_PROTO_IFATTACHDOMAIN, SI_ORDER_ANY); 617176669SpisoMODULE_DEPEND(ipfw_nat, libalias, 1, 1, 1); 618176669SpisoMODULE_DEPEND(ipfw_nat, ipfw, 2, 2, 2); 619176669SpisoMODULE_VERSION(ipfw_nat, 1); 620200601Sluigi/* end of file */ 621