summaryrefslogtreecommitdiffstats
path: root/sys
diff options
context:
space:
mode:
authorpiso <piso@FreeBSD.org>2006-12-29 21:59:17 +0000
committerpiso <piso@FreeBSD.org>2006-12-29 21:59:17 +0000
commit0db606a3b135b207a944e841f0142c30f4f43ceb (patch)
tree69ec3c3af60d727edf88005d9af7a9beb78e73fe /sys
parentd392a291a28a8cbedf2cc9398d4a03c8467d8c3d (diff)
downloadFreeBSD-src-0db606a3b135b207a944e841f0142c30f4f43ceb.zip
FreeBSD-src-0db606a3b135b207a944e841f0142c30f4f43ceb.tar.gz
Summer of Code 2005: improve libalias - part 2 of 2
With the second (and last) part of my previous Summer of Code work, we get: -ipfw's in kernel nat -redirect_* and LSNAT support General information about nat syntax and some examples are available in the ipfw (8) man page. The redirect and LSNAT syntax are identical to natd, so please refer to natd (8) man page. To enable in kernel nat in rc.conf, two options were added: o firewall_nat_enable: equivalent to natd_enable o firewall_nat_interface: equivalent to natd_interface Remember to set net.inet.ip.fw.one_pass to 0, if you want the packet to continue being checked by the firewall ruleset after being (de)aliased. NOTA BENE: due to some problems with libalias architecture, in kernel nat won't work with TSO enabled nic, thus you have to disable TSO via ifconfig (ifconfig foo0 -tso). Approved by: glebius (mentor)
Diffstat (limited to 'sys')
-rw-r--r--sys/netinet/in.h5
-rw-r--r--sys/netinet/ip_fw.h60
-rw-r--r--sys/netinet/ip_fw2.c548
-rw-r--r--sys/netinet/ip_fw_pfil.c6
-rw-r--r--sys/netinet/raw_ip.c4
5 files changed, 621 insertions, 2 deletions
diff --git a/sys/netinet/in.h b/sys/netinet/in.h
index 570ed33..321877c 100644
--- a/sys/netinet/in.h
+++ b/sys/netinet/in.h
@@ -410,6 +410,11 @@ __END_DECLS
#define IP_FW_GET 54 /* get entire firewall rule chain */
#define IP_FW_RESETLOG 55 /* reset logging counters */
+#define IP_FW_NAT_CFG 56 /* add/config a nat rule */
+#define IP_FW_NAT_DEL 57 /* delete a nat rule */
+#define IP_FW_NAT_GET_CONFIG 58 /* get configuration of a nat rule */
+#define IP_FW_NAT_GET_LOG 59 /* get log of a nat rule */
+
#define IP_DUMMYNET_CONFIGURE 60 /* add/configure a dummynet pipe */
#define IP_DUMMYNET_DEL 61 /* delete a dummynet pipe from chain */
#define IP_DUMMYNET_FLUSH 62 /* flush dummynet */
diff --git a/sys/netinet/ip_fw.h b/sys/netinet/ip_fw.h
index 1f50e56..199d569 100644
--- a/sys/netinet/ip_fw.h
+++ b/sys/netinet/ip_fw.h
@@ -124,6 +124,7 @@ enum ipfw_opcodes { /* arguments (4 byte each) */
O_TEE, /* arg1=port number */
O_FORWARD_IP, /* fwd sockaddr */
O_FORWARD_MAC, /* fwd mac */
+ O_NAT, /* nope */
/*
* More opcodes.
@@ -307,6 +308,64 @@ typedef struct _ipfw_insn_log {
u_int32_t log_left; /* how many left to log */
} ipfw_insn_log;
+/* Server pool support (LSNAT). */
+struct cfg_spool {
+ LIST_ENTRY(cfg_spool) _next; /* chain of spool instances */
+ struct in_addr addr;
+ u_short port;
+};
+
+/* Redirect modes id. */
+#define REDIR_ADDR 0x01
+#define REDIR_PORT 0x02
+#define REDIR_PROTO 0x04
+
+/* Nat redirect configuration. */
+struct cfg_redir {
+ LIST_ENTRY(cfg_redir) _next; /* chain of redir instances */
+ u_int16_t mode; /* type of redirect mode */
+ struct in_addr laddr; /* local ip address */
+ struct in_addr paddr; /* public ip address */
+ struct in_addr raddr; /* remote ip address */
+ u_short lport; /* local port */
+ u_short pport; /* public port */
+ u_short rport; /* remote port */
+ u_short pport_cnt; /* number of public ports */
+ u_short rport_cnt; /* number of remote ports */
+ int proto; /* protocol: tcp/udp */
+ struct alias_link **alink;
+ /* num of entry in spool chain */
+ u_int16_t spool_cnt;
+ /* chain of spool instances */
+ LIST_HEAD(spool_chain, cfg_spool) spool_chain;
+};
+
+#define NAT_BUF_LEN 1024
+/* Nat configuration data struct. */
+struct cfg_nat {
+ /* chain of nat instances */
+ LIST_ENTRY(cfg_nat) _next;
+ int id; /* nat id */
+ struct in_addr ip; /* nat ip address */
+ char if_name[IF_NAMESIZE]; /* interface name */
+ int mode; /* aliasing mode */
+ struct libalias *lib; /* libalias instance */
+ /* number of entry in spool chain */
+ int redir_cnt;
+ /* chain of redir instances */
+ LIST_HEAD(redir_chain, cfg_redir) redir_chain;
+};
+
+#define SOF_NAT sizeof(struct cfg_nat)
+#define SOF_REDIR sizeof(struct cfg_redir)
+#define SOF_SPOOL sizeof(struct cfg_spool)
+
+/* Nat command. */
+typedef struct _ipfw_insn_nat {
+ ipfw_insn o;
+ struct cfg_nat *nat;
+} ipfw_insn_nat;
+
/* Apply ipv6 mask on ipv6 addr */
#define APPLY_MASK(addr,mask) \
(addr)->__u6_addr.__u6_addr32[0] &= (mask)->__u6_addr.__u6_addr32[0]; \
@@ -483,6 +542,7 @@ enum {
IP_FW_DUMMYNET,
IP_FW_NETGRAPH,
IP_FW_NGTEE,
+ IP_FW_NAT,
};
/* flags for divert mtag */
diff --git a/sys/netinet/ip_fw2.c b/sys/netinet/ip_fw2.c
index 591e6ff..070de18 100644
--- a/sys/netinet/ip_fw2.c
+++ b/sys/netinet/ip_fw2.c
@@ -47,6 +47,7 @@
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/condvar.h>
+#include <sys/eventhandler.h>
#include <sys/malloc.h>
#include <sys/mbuf.h>
#include <sys/kernel.h>
@@ -84,6 +85,8 @@
#include <netinet/udp_var.h>
#include <netinet/sctp.h>
+#include <netinet/libalias/alias.h>
+#include <netinet/libalias/alias_local.h>
#include <netgraph/ng_ipfw.h>
#include <altq/if_altq.h>
@@ -138,6 +141,7 @@ struct ip_fw_ugid {
struct ip_fw_chain {
struct ip_fw *rules; /* list of rules */
struct ip_fw *reap; /* list of rules to reap */
+ LIST_HEAD(, cfg_nat) nat; /* list of nat entries */
struct radix_node_head *tables[IPFW_TABLES_MAX];
struct rwlock rwmtx;
};
@@ -303,6 +307,7 @@ static struct sysctl_oid *ip6_fw_sysctl_tree;
#endif /* INET6 */
#endif /* SYSCTL_NODE */
+MODULE_DEPEND(ipfw, libalias, 1, 1, 1);
static int fw_deny_unknown_exthdrs = 1;
@@ -861,6 +866,9 @@ ipfw_log(struct ip_fw *f, u_int hlen, struct ip_fw_args *args,
snprintf(SNPARGS(action2, 0), "Ngtee %d",
cmd->arg1);
break;
+ case O_NAT:
+ action = "Nat";
+ break;
default:
action = "UNKNOWN";
break;
@@ -2028,6 +2036,178 @@ check_uidgid(ipfw_insn_u32 *insn,
return match;
}
+static eventhandler_tag ifaddr_event_tag;
+
+static void
+ifaddr_change(void *arg __unused, struct ifnet *ifp) {
+ struct cfg_nat *ptr;
+ struct ifaddr *ifa;
+
+ IPFW_WLOCK(&layer3_chain);
+ /* Check every nat entry... */
+ LIST_FOREACH(ptr, &layer3_chain.nat, _next) {
+ /* ...using nic 'ifp->if_xname' as dynamic alias address. */
+ if (strncmp(ptr->if_name, ifp->if_xname, IF_NAMESIZE) == 0) {
+ mtx_lock(&ifp->if_addr_mtx);
+ TAILQ_FOREACH(ifa, &ifp->if_addrlist, ifa_list) {
+ if (ifa->ifa_addr == NULL)
+ continue;
+ if (ifa->ifa_addr->sa_family != AF_INET)
+ continue;
+ ptr->ip = ((struct sockaddr_in *)
+ (ifa->ifa_addr))->sin_addr;
+ LibAliasSetAddress(ptr->lib, ptr->ip);
+ }
+ mtx_unlock(&ifp->if_addr_mtx);
+ }
+ }
+ IPFW_WUNLOCK(&layer3_chain);
+}
+
+static void
+flush_nat_ptrs(const int i) {
+ struct ip_fw *rule;
+
+ IPFW_WLOCK_ASSERT(&layer3_chain);
+ for (rule = layer3_chain.rules; rule; rule = rule->next) {
+ ipfw_insn_nat *cmd = (ipfw_insn_nat *)ACTION_PTR(rule);
+ if (cmd->o.opcode != O_NAT)
+ continue;
+ if (cmd->nat != NULL && cmd->nat->id == i)
+ cmd->nat = NULL;
+ }
+}
+
+static struct cfg_nat *
+lookup_nat(const int i) {
+ struct cfg_nat *ptr;
+
+ LIST_FOREACH(ptr, &layer3_chain.nat, _next)
+ if (ptr->id == i)
+ return(ptr);
+ return (NULL);
+}
+
+#define HOOK_NAT(b, p) do { \
+ IPFW_WLOCK_ASSERT(&layer3_chain); \
+ LIST_INSERT_HEAD(b, p, _next); \
+} while (0)
+
+#define UNHOOK_NAT(p) do { \
+ IPFW_WLOCK_ASSERT(&layer3_chain); \
+ LIST_REMOVE(p, _next); \
+} while (0)
+
+#define HOOK_REDIR(b, p) do { \
+ LIST_INSERT_HEAD(b, p, _next); \
+} while (0)
+
+#define HOOK_SPOOL(b, p) do { \
+ LIST_INSERT_HEAD(b, p, _next); \
+} while (0)
+
+static void
+del_redir_spool_cfg(struct cfg_nat *n, struct redir_chain *head) {
+ struct cfg_redir *r, *tmp_r;
+ struct cfg_spool *s, *tmp_s;
+ int i, num;
+
+ LIST_FOREACH_SAFE(r, head, _next, tmp_r) {
+ num = 1; /* Number of alias_link to delete. */
+ switch (r->mode) {
+ case REDIR_PORT:
+ num = r->pport_cnt;
+ /* FALLTHROUGH */
+ case REDIR_ADDR:
+ case REDIR_PROTO:
+ /* Delete all libalias redirect entry. */
+ for (i = 0; i < num; i++)
+ LibAliasRedirectDelete(n->lib, r->alink[i]);
+ /* Del spool cfg if any. */
+ LIST_FOREACH_SAFE(s, &r->spool_chain, _next, tmp_s) {
+ LIST_REMOVE(s, _next);
+ free(s, M_IPFW);
+ }
+ free(r->alink, M_IPFW);
+ LIST_REMOVE(r, _next);
+ free(r, M_IPFW);
+ break;
+ default:
+ printf("unknown redirect mode: %u\n", r->mode);
+ /* XXX - panic?!?!? */
+ break;
+ }
+ }
+}
+
+static int
+add_redir_spool_cfg(char *buf, struct cfg_nat *ptr) {
+ struct cfg_redir *r, *ser_r;
+ struct cfg_spool *s, *ser_s;
+ int cnt, off, i;
+ char *panic_err;
+
+ for (cnt = 0, off = 0; cnt < ptr->redir_cnt; cnt++) {
+ ser_r = (struct cfg_redir *)&buf[off];
+ r = malloc(SOF_REDIR, M_IPFW, M_WAITOK | M_ZERO);
+ memcpy(r, ser_r, SOF_REDIR);
+ LIST_INIT(&r->spool_chain);
+ off += SOF_REDIR;
+ r->alink = malloc(sizeof(struct alias_link *) * r->pport_cnt,
+ M_IPFW, M_WAITOK | M_ZERO);
+ switch (r->mode) {
+ case REDIR_ADDR:
+ r->alink[0] = LibAliasRedirectAddr(ptr->lib, r->laddr,
+ r->paddr);
+ break;
+ case REDIR_PORT:
+ for (i = 0 ; i < r->pport_cnt; i++) {
+ /* If remotePort is all ports, set it to 0. */
+ u_short remotePortCopy = r->rport + i;
+ if (r->rport_cnt == 1 && r->rport == 0)
+ remotePortCopy = 0;
+ r->alink[i] = LibAliasRedirectPort(ptr->lib,
+ r->laddr, htons(r->lport + i), r->raddr,
+ htons(remotePortCopy), r->paddr,
+ htons(r->pport + i), r->proto);
+ if (r->alink[i] == NULL) {
+ r->alink[0] = NULL;
+ break;
+ }
+ }
+ break;
+ case REDIR_PROTO:
+ r->alink[0] = LibAliasRedirectProto(ptr->lib ,r->laddr,
+ r->raddr, r->paddr, r->proto);
+ break;
+ default:
+ printf("unknown redirect mode: %u\n", r->mode);
+ break;
+ }
+ if (r->alink[0] == NULL) {
+ panic_err = "LibAliasRedirect* returned NULL";
+ goto bad;
+ } else /* LSNAT handling. */
+ for (i = 0; i < r->spool_cnt; i++) {
+ ser_s = (struct cfg_spool *)&buf[off];
+ s = malloc(SOF_REDIR, M_IPFW,
+ M_WAITOK | M_ZERO);
+ memcpy(s, ser_s, SOF_SPOOL);
+ LibAliasAddServer(ptr->lib, r->alink[0],
+ s->addr, htons(s->port));
+ off += SOF_SPOOL;
+ /* Hook spool entry. */
+ HOOK_SPOOL(&r->spool_chain, s);
+ }
+ /* And finally hook this redir entry. */
+ HOOK_REDIR(&ptr->redir_chain, r);
+ }
+ return (1);
+bad:
+ /* something really bad happened: panic! */
+ panic("%s\n", panic_err);
+}
+
/*
* The main check routine for the firewall.
*
@@ -3257,6 +3437,177 @@ check_body:
IP_FW_NETGRAPH : IP_FW_NGTEE;
goto done;
+ case O_NAT: {
+ struct cfg_nat *t;
+ struct mbuf *mcl;
+ /* XXX - libalias duct tape */
+ int ldt;
+ char *c;
+
+ ldt = 0;
+ args->rule = f; /* Report matching rule. */
+ retval = 0;
+ t = ((ipfw_insn_nat *)cmd)->nat;
+ if (t == NULL) {
+ t = lookup_nat(cmd->arg1);
+ if (t == NULL) {
+ retval = IP_FW_DENY;
+ goto done;
+ } else
+ ((ipfw_insn_nat *)cmd)->nat =
+ t;
+ }
+ if ((mcl = m_megapullup(m, m->m_pkthdr.len)) ==
+ NULL)
+ goto badnat;
+ ip = mtod(mcl, struct ip *);
+ if (args->eh == NULL) {
+ ip->ip_len = htons(ip->ip_len);
+ ip->ip_off = htons(ip->ip_off);
+ }
+
+ /*
+ * XXX - Libalias checksum offload 'duct tape':
+ *
+ * locally generated packets have only
+ * pseudo-header checksum calculated
+ * and libalias will screw it[1], so
+ * mark them for later fix. Moreover
+ * there are cases when libalias
+ * modify tcp packet data[2], mark it
+ * for later fix too.
+ *
+ * [1] libalias was never meant to run
+ * in kernel, so it doesn't have any
+ * knowledge about checksum
+ * offloading, and it expects a packet
+ * with a full internet
+ * checksum. Unfortunately, packets
+ * generated locally will have just the
+ * pseudo header calculated, and when
+ * libalias tries to adjust the
+ * checksum it will actually screw it.
+ *
+ * [2] when libalias modify tcp's data
+ * content, full TCP checksum has to
+ * be recomputed: the problem is that
+ * libalias doesn't have any idea
+ * about checksum offloading To
+ * workaround this, we do not do
+ * checksumming in LibAlias, but only
+ * mark the packets in th_x2 field. If
+ * we receive a marked packet, we
+ * calculate correct checksum for it
+ * aware of offloading. Why such a
+ * terrible hack instead of
+ * recalculating checksum for each
+ * packet? Because the previous
+ * checksum was not checked!
+ * Recalculating checksums for EVERY
+ * packet will hide ALL transmission
+ * errors. Yes, marked packets still
+ * suffer from this problem. But,
+ * sigh, natd(8) has this problem,
+ * too.
+ *
+ * TODO: -make libalias mbuf aware (so
+ * it can handle delayed checksum and tso)
+ */
+
+ if (mcl->m_pkthdr.rcvif == NULL &&
+ mcl->m_pkthdr.csum_flags &
+ CSUM_DELAY_DATA)
+ ldt = 1;
+
+ c = mtod(mcl, char *);
+ if (oif == NULL)
+ retval = LibAliasIn(t->lib, c,
+ MCLBYTES);
+ else
+ retval = LibAliasOut(t->lib, c,
+ MCLBYTES);
+ if (retval != PKT_ALIAS_OK) {
+ /* XXX - should i add some logging? */
+ m_free(mcl);
+ badnat:
+ args->m = NULL;
+ retval = IP_FW_DENY;
+ goto done;
+ }
+ mcl->m_pkthdr.len = mcl->m_len =
+ ntohs(ip->ip_len);
+
+ /*
+ * XXX - libalias checksum offload
+ * 'duct tape' (see above)
+ */
+
+ if ((ip->ip_off & htons(IP_OFFMASK)) == 0 &&
+ ip->ip_p == IPPROTO_TCP) {
+ struct tcphdr *th;
+
+ th = (struct tcphdr *)(ip + 1);
+ if (th->th_x2)
+ ldt = 1;
+ }
+
+ if (ldt) {
+ struct tcphdr *th;
+ struct udphdr *uh;
+ u_short cksum;
+
+ ip->ip_len = ntohs(ip->ip_len);
+ cksum = in_pseudo(
+ ip->ip_src.s_addr,
+ ip->ip_dst.s_addr,
+ htons(ip->ip_p + ip->ip_len -
+ (ip->ip_hl << 2))
+ );
+
+ switch (ip->ip_p) {
+ case IPPROTO_TCP:
+ th = (struct tcphdr *)(ip + 1);
+ /*
+ * Maybe it was set in
+ * libalias...
+ */
+ th->th_x2 = 0;
+ th->th_sum = cksum;
+ mcl->m_pkthdr.csum_data =
+ offsetof(struct tcphdr,
+ th_sum);
+ break;
+ case IPPROTO_UDP:
+ uh = (struct udphdr *)(ip + 1);
+ uh->uh_sum = cksum;
+ mcl->m_pkthdr.csum_data =
+ offsetof(struct udphdr,
+ uh_sum);
+ break;
+ }
+ /*
+ * No hw checksum offloading: do it
+ * by ourself.
+ */
+ if ((mcl->m_pkthdr.csum_flags &
+ CSUM_DELAY_DATA) == 0) {
+ in_delayed_cksum(mcl);
+ mcl->m_pkthdr.csum_flags &=
+ ~CSUM_DELAY_DATA;
+ }
+ ip->ip_len = htons(ip->ip_len);
+ }
+
+ if (args->eh == NULL) {
+ ip->ip_len = ntohs(ip->ip_len);
+ ip->ip_off = ntohs(ip->ip_off);
+ }
+
+ args->m = mcl;
+ retval = IP_FW_NAT;
+ goto done;
+ }
+
default:
panic("-- unknown opcode %d\n", cmd->opcode);
} /* end of switch() on opcodes */
@@ -3826,6 +4177,10 @@ check_ipfw_struct(struct ip_fw *rule, int size)
return EINVAL;
else
goto check_size;
+ case O_NAT:
+ if (cmdlen != F_INSN_SIZE(ipfw_insn_nat))
+ goto bad_size;
+ goto check_action;
case O_FORWARD_MAC: /* XXX not implemented yet */
case O_CHECK_STATE:
case O_COUNT:
@@ -4201,6 +4556,185 @@ ipfw_ctl(struct sockopt *sopt)
}
break;
+ case IP_FW_NAT_CFG:
+ {
+ struct cfg_nat *ptr, *ser_n;
+ char *buf;
+
+ buf = malloc(NAT_BUF_LEN, M_IPFW, M_WAITOK | M_ZERO);
+ error = sooptcopyin(sopt, buf, NAT_BUF_LEN,
+ sizeof(struct cfg_nat));
+ ser_n = (struct cfg_nat *)buf;
+
+ /*
+ * Find/create nat rule.
+ */
+ IPFW_WLOCK(&layer3_chain);
+ ptr = lookup_nat(ser_n->id);
+ if (ptr == NULL) {
+ /* New rule: allocate and init new instance. */
+ ptr = malloc(sizeof(struct cfg_nat),
+ M_IPFW, M_NOWAIT | M_ZERO);
+ if (ptr == NULL) {
+ IPFW_WUNLOCK(&layer3_chain);
+ free(buf, M_IPFW);
+ return (ENOSPC);
+ }
+ ptr->lib = LibAliasInit(NULL);
+ if (ptr->lib == NULL) {
+ IPFW_WUNLOCK(&layer3_chain);
+ free(ptr, M_IPFW);
+ free(buf, M_IPFW);
+ return (EINVAL);
+ }
+ LIST_INIT(&ptr->redir_chain);
+ } else {
+ /* Entry already present: temporarly unhook it. */
+ UNHOOK_NAT(ptr);
+ flush_nat_ptrs(ser_n->id);
+ }
+ IPFW_WUNLOCK(&layer3_chain);
+
+ /*
+ * Basic nat configuration.
+ */
+ ptr->id = ser_n->id;
+ /*
+ * XXX - what if this rule doesn't nat any ip and just
+ * redirect?
+ * do we set aliasaddress to 0.0.0.0?
+ */
+ ptr->ip = ser_n->ip;
+ ptr->redir_cnt = ser_n->redir_cnt;
+ ptr->mode = ser_n->mode;
+ LibAliasSetMode(ptr->lib, ser_n->mode, ser_n->mode);
+ LibAliasSetAddress(ptr->lib, ptr->ip);
+ memcpy(ptr->if_name, ser_n->if_name, IF_NAMESIZE);
+
+ /*
+ * Redir and LSNAT configuration.
+ */
+ /* Delete old cfgs. */
+ del_redir_spool_cfg(ptr, &ptr->redir_chain);
+ /* Add new entries. */
+ add_redir_spool_cfg(&buf[(sizeof(struct cfg_nat))], ptr);
+ free(buf, M_IPFW);
+ IPFW_WLOCK(&layer3_chain);
+ HOOK_NAT(&layer3_chain.nat, ptr);
+ IPFW_WUNLOCK(&layer3_chain);
+ }
+ break;
+
+ case IP_FW_NAT_DEL:
+ {
+ struct cfg_nat *ptr;
+ int i;
+
+ error = sooptcopyin(sopt, &i, sizeof i, sizeof i);
+ IPFW_WLOCK(&layer3_chain);
+ ptr = lookup_nat(i);
+ if (ptr == NULL) {
+ error = EINVAL;
+ IPFW_WUNLOCK(&layer3_chain);
+ break;
+ }
+ UNHOOK_NAT(ptr);
+ flush_nat_ptrs(i);
+ IPFW_WUNLOCK(&layer3_chain);
+ del_redir_spool_cfg(ptr, &ptr->redir_chain);
+ LibAliasUninit(ptr->lib);
+ free(ptr, M_IPFW);
+ }
+ break;
+
+ case IP_FW_NAT_GET_CONFIG:
+ {
+ uint8_t *data;
+ struct cfg_nat *n;
+ struct cfg_redir *r;
+ struct cfg_spool *s;
+ int nat_cnt, off;
+
+ nat_cnt = 0;
+ off = sizeof(nat_cnt);
+
+ data = malloc(NAT_BUF_LEN, M_IPFW, M_WAITOK | M_ZERO);
+ IPFW_RLOCK(&layer3_chain);
+ /* Serialize all the data. */
+ LIST_FOREACH(n, &layer3_chain.nat, _next) {
+ nat_cnt++;
+ if (off + SOF_NAT < NAT_BUF_LEN) {
+ bcopy(n, &data[off], SOF_NAT);
+ off += SOF_NAT;
+ LIST_FOREACH(r, &n->redir_chain, _next) {
+ if (off + SOF_REDIR < NAT_BUF_LEN) {
+ bcopy(r, &data[off],
+ SOF_REDIR);
+ off += SOF_REDIR;
+ LIST_FOREACH(s, &r->spool_chain,
+ _next) {
+ if (off + SOF_SPOOL <
+ NAT_BUF_LEN) {
+ bcopy(s,
+ &data[off],
+ SOF_SPOOL);
+ off +=
+ SOF_SPOOL;
+ } else
+ goto nospace;
+ }
+ } else
+ goto nospace;
+ }
+ } else
+ goto nospace;
+ }
+ bcopy(&nat_cnt, data, sizeof(nat_cnt));
+ IPFW_RUNLOCK(&layer3_chain);
+ error = sooptcopyout(sopt, data, NAT_BUF_LEN);
+ free(data, M_IPFW);
+ break;
+ nospace:
+ IPFW_RUNLOCK(&layer3_chain);
+ printf("serialized data buffer not big enough:"
+ "please increase NAT_BUF_LEN\n");
+ free(data, M_IPFW);
+ }
+ break;
+
+ case IP_FW_NAT_GET_LOG:
+ {
+ uint8_t *data;
+ struct cfg_nat *ptr;
+ int i, size, cnt, sof;
+
+ data = NULL;
+ sof = LIBALIAS_BUF_SIZE;
+ cnt = 0;
+
+ IPFW_RLOCK(&layer3_chain);
+ size = i = 0;
+ LIST_FOREACH(ptr, &layer3_chain.nat, _next) {
+ if (ptr->lib->logDesc == NULL)
+ continue;
+ cnt++;
+ size = cnt * (sof + sizeof(int));
+ data = realloc(data, size, M_IPFW, M_NOWAIT | M_ZERO);
+ if (data == NULL) {
+ IPFW_RUNLOCK(&layer3_chain);
+ return (ENOSPC);
+ }
+ bcopy(&ptr->id, &data[i], sizeof(int));
+ i += sizeof(int);
+ bcopy(ptr->lib->logDesc, &data[i], sof);
+ i += sof;
+ }
+ IPFW_RUNLOCK(&layer3_chain);
+ error = sooptcopyout(sopt, data, size);
+ free(data, M_IPFW);
+ }
+ break;
+
default:
printf("ipfw: ipfw_ctl invalid option %d\n", sopt->sopt_name);
error = EINVAL;
@@ -4372,8 +4906,10 @@ ipfw_init(void)
}
ip_fw_ctl_ptr = ipfw_ctl;
ip_fw_chk_ptr = ipfw_chk;
- callout_reset(&ipfw_timeout, hz, ipfw_tick, NULL);
-
+ callout_reset(&ipfw_timeout, hz, ipfw_tick, NULL);
+ LIST_INIT(&layer3_chain.nat);
+ ifaddr_event_tag = EVENTHANDLER_REGISTER(ifaddr_event, ifaddr_change,
+ NULL, EVENTHANDLER_PRI_ANY);
return (0);
}
@@ -4381,12 +4917,20 @@ void
ipfw_destroy(void)
{
struct ip_fw *reap;
+ struct cfg_nat *ptr, *ptr_temp;
ip_fw_chk_ptr = NULL;
ip_fw_ctl_ptr = NULL;
callout_drain(&ipfw_timeout);
IPFW_WLOCK(&layer3_chain);
flush_tables(&layer3_chain);
+ LIST_FOREACH_SAFE(ptr, &layer3_chain.nat, _next, ptr_temp) {
+ LIST_REMOVE(ptr, _next);
+ del_redir_spool_cfg(ptr, &ptr->redir_chain);
+ LibAliasUninit(ptr->lib);
+ free(ptr, M_IPFW);
+ }
+ EVENTHANDLER_DEREGISTER(ifaddr_event, ifaddr_event_tag);
layer3_chain.reap = NULL;
free_chain(&layer3_chain, 1 /* kill default rule */);
reap = layer3_chain.reap, layer3_chain.reap = NULL;
diff --git a/sys/netinet/ip_fw_pfil.c b/sys/netinet/ip_fw_pfil.c
index a58f96f..d8285b5 100644
--- a/sys/netinet/ip_fw_pfil.c
+++ b/sys/netinet/ip_fw_pfil.c
@@ -189,6 +189,9 @@ again:
if (!NG_IPFW_LOADED)
goto drop;
return ng_ipfw_input_p(m0, NG_IPFW_IN, &args, 0);
+
+ case IP_FW_NAT:
+ goto again; /* continue with packet */
default:
KASSERT(0, ("%s: unknown retval", __func__));
@@ -315,6 +318,9 @@ again:
goto drop;
return ng_ipfw_input_p(m0, NG_IPFW_OUT, &args, 0);
+ case IP_FW_NAT:
+ goto again; /* continue with packet */
+
default:
KASSERT(0, ("%s: unknown retval", __func__));
}
diff --git a/sys/netinet/raw_ip.c b/sys/netinet/raw_ip.c
index c6900b2..51fe333 100644
--- a/sys/netinet/raw_ip.c
+++ b/sys/netinet/raw_ip.c
@@ -389,6 +389,8 @@ rip_ctloutput(struct socket *so, struct sockopt *sopt)
case IP_FW_GET:
case IP_FW_TABLE_GETSIZE:
case IP_FW_TABLE_LIST:
+ case IP_FW_NAT_GET_CONFIG:
+ case IP_FW_NAT_GET_LOG:
/*
* XXXRW: Isn't this checked one layer down? Yes, it
* is.
@@ -458,6 +460,8 @@ rip_ctloutput(struct socket *so, struct sockopt *sopt)
case IP_FW_TABLE_ADD:
case IP_FW_TABLE_DEL:
case IP_FW_TABLE_FLUSH:
+ case IP_FW_NAT_CFG:
+ case IP_FW_NAT_DEL:
/*
* XXXRW: Isn't this checked one layer down?
*/
OpenPOWER on IntegriCloud