summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authormelifaro <melifaro@FreeBSD.org>2014-09-07 18:30:29 +0000
committermelifaro <melifaro@FreeBSD.org>2014-09-07 18:30:29 +0000
commitf7e682304522f7227318f07a70ec0e78a6377465 (patch)
treee160314c513d0051e3a7e123db859557495ecb89
parent595fec1055729eec7c788613a0c66d8ec7cbc5ea (diff)
downloadFreeBSD-src-f7e682304522f7227318f07a70ec0e78a6377465.zip
FreeBSD-src-f7e682304522f7227318f07a70ec0e78a6377465.tar.gz
Make ipfw_nat module use IP_FW3 codes.
Kernel changes: * Split kernel/userland nat structures eliminating IPFW_INTERNAL hack. * Add IP_FW_NAT44_* codes resemblin old ones. * Assume that instances can be named (no kernel support currently). * Use both UH+WLOCK locks for all configuration changes. * Provide full ABI support for old sockopts. Userland changes: * Use IP_FW_NAT44_* codes for nat operations. * Remove undocumented ability to show ranges of nat "log" entries.
-rw-r--r--sbin/ipfw/nat.c357
-rw-r--r--sys/netinet/ip_fw.h46
-rw-r--r--sys/netpfil/ipfw/ip_fw_nat.c663
3 files changed, 882 insertions, 184 deletions
diff --git a/sbin/ipfw/nat.c b/sbin/ipfw/nat.c
index bff28e1..3bd0259 100644
--- a/sbin/ipfw/nat.c
+++ b/sbin/ipfw/nat.c
@@ -30,14 +30,13 @@
#include <ctype.h>
#include <err.h>
+#include <errno.h>
#include <netdb.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sysexits.h>
-#define IPFW_INTERNAL /* Access to protected structures in ip_fw.h. */
-
#include <net/if.h>
#include <net/if_dl.h>
#include <net/route.h> /* def. of struct route */
@@ -46,6 +45,14 @@
#include <arpa/inet.h>
#include <alias.h>
+typedef int (nat_cb_t)(struct nat44_cfg_nat *cfg, void *arg);
+static void nat_show_cfg(struct nat44_cfg_nat *n, void *arg);
+static void nat_show_log(struct nat44_cfg_nat *n, void *arg);
+static int nat_show_data(struct nat44_cfg_nat *cfg, void *arg);
+static int natname_cmp(const void *a, const void *b);
+static int nat_foreach(nat_cb_t *f, void *arg, int sort);
+static int nat_get_cmd(char *name, uint16_t cmd, ipfw_obj_header **ooh);
+
static struct _s_x nat_params[] = {
{ "ip", TOK_IP },
{ "if", TOK_IF },
@@ -71,7 +78,7 @@ static struct _s_x nat_params[] = {
* n->if_name copy of interface name "ifn"
*/
static void
-set_addr_dynamic(const char *ifn, struct cfg_nat *n)
+set_addr_dynamic(const char *ifn, struct nat44_cfg_nat *n)
{
size_t needed;
int mib[6];
@@ -288,15 +295,15 @@ StrToAddrAndPortRange (const char* str, struct in_addr* addr, char* proto,
* and SetupProtoRedirect() from natd.c.
*
* Every setup_* function fills at least one redirect entry
- * (struct cfg_redir) and zero or more server pool entry (struct cfg_spool)
- * in buf.
+ * (struct nat44_cfg_redir) and zero or more server pool entry
+ * (struct nat44_cfg_spool) in buf.
*
* The format of data in buf is:
*
- * cfg_nat cfg_redir cfg_spool ...... cfg_spool
+ * nat44_cfg_nat nat44_cfg_redir nat44_cfg_spool ...... nat44_cfg_spool
*
* ------------------------------------- ------------
- * | | .....X ... | | | | .....
+ * | | .....X ..... | | | | .....
* ------------------------------------- ...... ------------
* ^
* spool_cnt n=0 ...... n=(X-1)
@@ -314,7 +321,7 @@ StrToAddrAndPortRange (const char* str, struct in_addr* addr, char* proto,
static int
estimate_redir_addr(int *ac, char ***av)
{
- size_t space = sizeof(struct cfg_redir);
+ size_t space = sizeof(struct nat44_cfg_redir);
char *sep = **av;
u_int c = 0;
@@ -327,7 +334,7 @@ estimate_redir_addr(int *ac, char ***av)
if (c > 0)
c++;
- space += c * sizeof(struct cfg_spool);
+ space += c * sizeof(struct nat44_cfg_spool);
return (space);
}
@@ -335,31 +342,31 @@ estimate_redir_addr(int *ac, char ***av)
static int
setup_redir_addr(char *buf, int *ac, char ***av)
{
- struct cfg_redir *r;
+ struct nat44_cfg_redir *r;
char *sep;
size_t space;
- r = (struct cfg_redir *)buf;
+ r = (struct nat44_cfg_redir *)buf;
r->mode = REDIR_ADDR;
- /* Skip cfg_redir at beginning of buf. */
- buf = &buf[sizeof(struct cfg_redir)];
- space = sizeof(struct cfg_redir);
+ /* Skip nat44_cfg_redir at beginning of buf. */
+ buf = &buf[sizeof(struct nat44_cfg_redir)];
+ space = sizeof(struct nat44_cfg_redir);
/* Extract local address. */
if (strchr(**av, ',') != NULL) {
- struct cfg_spool *spool;
+ struct nat44_cfg_spool *spool;
/* Setup LSNAT server pool. */
r->laddr.s_addr = INADDR_NONE;
sep = strtok(**av, ",");
while (sep != NULL) {
- spool = (struct cfg_spool *)buf;
- space += sizeof(struct cfg_spool);
+ spool = (struct nat44_cfg_spool *)buf;
+ space += sizeof(struct nat44_cfg_spool);
StrToAddr(sep, &spool->addr);
spool->port = ~0;
r->spool_cnt++;
- /* Point to the next possible cfg_spool. */
- buf = &buf[sizeof(struct cfg_spool)];
+ /* Point to the next possible nat44_cfg_spool. */
+ buf = &buf[sizeof(struct nat44_cfg_spool)];
sep = strtok(NULL, ",");
}
} else
@@ -376,7 +383,7 @@ setup_redir_addr(char *buf, int *ac, char ***av)
static int
estimate_redir_port(int *ac, char ***av)
{
- size_t space = sizeof(struct cfg_redir);
+ size_t space = sizeof(struct nat44_cfg_redir);
char *sep = **av;
u_int c = 0;
@@ -389,7 +396,7 @@ estimate_redir_port(int *ac, char ***av)
if (c > 0)
c++;
- space += c * sizeof(struct cfg_spool);
+ space += c * sizeof(struct nat44_cfg_spool);
return (space);
}
@@ -397,7 +404,7 @@ estimate_redir_port(int *ac, char ***av)
static int
setup_redir_port(char *buf, int *ac, char ***av)
{
- struct cfg_redir *r;
+ struct nat44_cfg_redir *r;
char *sep, *protoName, *lsnat = NULL;
size_t space;
u_short numLocalPorts;
@@ -405,11 +412,11 @@ setup_redir_port(char *buf, int *ac, char ***av)
numLocalPorts = 0;
- r = (struct cfg_redir *)buf;
+ r = (struct nat44_cfg_redir *)buf;
r->mode = REDIR_PORT;
- /* Skip cfg_redir at beginning of buf. */
- buf = &buf[sizeof(struct cfg_redir)];
- space = sizeof(struct cfg_redir);
+ /* Skip nat44_cfg_redir at beginning of buf. */
+ buf = &buf[sizeof(struct nat44_cfg_redir)];
+ space = sizeof(struct nat44_cfg_redir);
/*
* Extract protocol.
@@ -516,12 +523,12 @@ setup_redir_port(char *buf, int *ac, char ***av)
/* Setup LSNAT server pool. */
if (lsnat != NULL) {
- struct cfg_spool *spool;
+ struct nat44_cfg_spool *spool;
sep = strtok(lsnat, ",");
while (sep != NULL) {
- spool = (struct cfg_spool *)buf;
- space += sizeof(struct cfg_spool);
+ spool = (struct nat44_cfg_spool *)buf;
+ space += sizeof(struct nat44_cfg_spool);
/*
* The sctp nat does not allow the port numbers to
* be mapped to new port numbers. Therefore, no ports
@@ -549,8 +556,8 @@ setup_redir_port(char *buf, int *ac, char ***av)
spool->port = GETLOPORT(portRange);
}
r->spool_cnt++;
- /* Point to the next possible cfg_spool. */
- buf = &buf[sizeof(struct cfg_spool)];
+ /* Point to the next possible nat44_cfg_spool. */
+ buf = &buf[sizeof(struct nat44_cfg_spool)];
sep = strtok(NULL, ",");
}
}
@@ -561,15 +568,15 @@ setup_redir_port(char *buf, int *ac, char ***av)
static int
setup_redir_proto(char *buf, int *ac, char ***av)
{
- struct cfg_redir *r;
+ struct nat44_cfg_redir *r;
struct protoent *protoent;
size_t space;
- r = (struct cfg_redir *)buf;
+ r = (struct nat44_cfg_redir *)buf;
r->mode = REDIR_PROTO;
- /* Skip cfg_redir at beginning of buf. */
- buf = &buf[sizeof(struct cfg_redir)];
- space = sizeof(struct cfg_redir);
+ /* Skip nat44_cfg_redir at beginning of buf. */
+ buf = &buf[sizeof(struct nat44_cfg_redir)];
+ space = sizeof(struct nat44_cfg_redir);
/*
* Extract protocol.
@@ -616,18 +623,28 @@ setup_redir_proto(char *buf, int *ac, char ***av)
}
static void
-print_nat_config(unsigned char *buf)
+nat_show_log(struct nat44_cfg_nat *n, void *arg)
+{
+ char *buf;
+
+ buf = (char *)(n + 1);
+ if (buf[0] != '\0')
+ printf("nat %s: %s\n", n->name, buf);
+}
+
+static void
+nat_show_cfg(struct nat44_cfg_nat *n, void *arg)
{
- struct cfg_nat *n;
int i, cnt, flag, off;
- struct cfg_redir *t;
- struct cfg_spool *s;
+ struct nat44_cfg_redir *t;
+ struct nat44_cfg_spool *s;
+ caddr_t buf;
struct protoent *p;
- n = (struct cfg_nat *)buf;
+ buf = (caddr_t)n;
flag = 1;
- off = sizeof(*n);
- printf("ipfw nat %u config", n->id);
+ off = sizeof(*n);
+ printf("ipfw nat %s config", n->name);
if (strlen(n->if_name) != 0)
printf(" if %s", n->if_name);
else if (n->ip.s_addr != 0)
@@ -661,8 +678,8 @@ print_nat_config(unsigned char *buf)
}
/* Print all the redirect's data configuration. */
for (cnt = 0; cnt < n->redir_cnt; cnt++) {
- t = (struct cfg_redir *)&buf[off];
- off += SOF_REDIR;
+ t = (struct nat44_cfg_redir *)&buf[off];
+ off += sizeof(struct nat44_cfg_redir);
switch (t->mode) {
case REDIR_ADDR:
printf(" redirect_addr");
@@ -670,13 +687,13 @@ print_nat_config(unsigned char *buf)
printf(" %s", inet_ntoa(t->laddr));
else
for (i = 0; i < t->spool_cnt; i++) {
- s = (struct cfg_spool *)&buf[off];
+ s = (struct nat44_cfg_spool *)&buf[off];
if (i)
printf(",");
else
printf(" ");
printf("%s", inet_ntoa(s->addr));
- off += SOF_SPOOL;
+ off += sizeof(struct nat44_cfg_spool);
}
printf(" %s", inet_ntoa(t->paddr));
break;
@@ -690,12 +707,12 @@ print_nat_config(unsigned char *buf)
t->pport_cnt - 1);
} else
for (i=0; i < t->spool_cnt; i++) {
- s = (struct cfg_spool *)&buf[off];
+ s = (struct nat44_cfg_spool *)&buf[off];
if (i)
printf(",");
printf("%s:%u", inet_ntoa(s->addr),
s->port);
- off += SOF_SPOOL;
+ off += sizeof(struct nat44_cfg_spool);
}
printf(" ");
@@ -736,7 +753,8 @@ print_nat_config(unsigned char *buf)
void
ipfw_config_nat(int ac, char **av)
{
- struct cfg_nat *n; /* Nat instance configuration. */
+ ipfw_obj_header *oh;
+ struct nat44_cfg_nat *n; /* Nat instance configuration. */
int i, off, tok, ac1;
char *id, *buf, **av1, *end;
size_t len;
@@ -755,7 +773,7 @@ ipfw_config_nat(int ac, char **av)
if (ac == 0)
errx(EX_DATAERR, "missing option");
- len = sizeof(struct cfg_nat);
+ len = sizeof(*oh) + sizeof(*n);
ac1 = ac;
av1 = av;
while (ac1 > 0) {
@@ -804,7 +822,7 @@ ipfw_config_nat(int ac, char **av)
if (ac1 < 2)
errx(EX_DATAERR, "redirect_proto: "
"not enough arguments");
- len += sizeof(struct cfg_redir);
+ len += sizeof(struct nat44_cfg_redir);
av1 += 2;
ac1 -= 2;
/* Skip optional remoteIP/port */
@@ -825,11 +843,14 @@ ipfw_config_nat(int ac, char **av)
if ((buf = malloc(len)) == NULL)
errx(EX_OSERR, "malloc failed");
- /* Offset in buf: save space for n at the beginning. */
- off = sizeof(*n);
+ /* Offset in buf: save space for header at the beginning. */
+ off = sizeof(*oh) + sizeof(*n);
memset(buf, 0, len);
- n = (struct cfg_nat *)buf;
- n->id = i;
+ oh = (ipfw_obj_header *)buf;
+ n = (struct nat44_cfg_nat *)(oh + 1);
+ oh->ntlv.head.length = sizeof(oh->ntlv);
+ snprintf(oh->ntlv.name, sizeof(oh->ntlv.name), "%d", i);
+ snprintf(n->name, sizeof(n->name), "%d", i);
while (ac > 0) {
tok = match_token(nat_params, *av);
@@ -900,9 +921,9 @@ ipfw_config_nat(int ac, char **av)
}
}
- i = do_cmd(IP_FW_NAT_CFG, buf, off);
- if (i)
- err(1, "setsockopt(%s)", "IP_FW_NAT_CFG");
+ i = do_set3(IP_FW_NAT44_XCONFIG, &oh->opheader, len);
+ if (i != 0)
+ err(1, "setsockopt(%s)", "IP_FW_NAT44_XCONFIG");
if (!co.do_quiet) {
/* After every modification, we show the resultant rule. */
@@ -912,23 +933,147 @@ ipfw_config_nat(int ac, char **av)
}
}
+struct nat_list_arg {
+ uint16_t cmd;
+ int is_all;
+};
+
+static int
+nat_show_data(struct nat44_cfg_nat *cfg, void *arg)
+{
+ struct nat_list_arg *nla;
+ ipfw_obj_header *oh;
+
+ nla = (struct nat_list_arg *)arg;
+
+ switch (nla->cmd) {
+ case IP_FW_NAT44_XGETCONFIG:
+ if (nat_get_cmd(cfg->name, nla->cmd, &oh) != 0) {
+ warnx("Error getting nat instance %s info", cfg->name);
+ break;
+ }
+ nat_show_cfg((struct nat44_cfg_nat *)(oh + 1), NULL);
+ free(oh);
+ break;
+ case IP_FW_NAT44_XGETLOG:
+ if (nat_get_cmd(cfg->name, nla->cmd, &oh) == 0) {
+ nat_show_log((struct nat44_cfg_nat *)(oh + 1), NULL);
+ free(oh);
+ break;
+ }
+ /* Handle error */
+ if (nla->is_all != 0 && errno == ENOENT)
+ break;
+ warn("Error getting nat instance %s info", cfg->name);
+ break;
+ }
+
+ return (0);
+}
+
+/*
+ * Compare nat names.
+ * Honor number comparison.
+ */
+static int
+natname_cmp(const void *a, const void *b)
+{
+ struct nat44_cfg_nat *ia, *ib;
+
+ ia = (struct nat44_cfg_nat *)a;
+ ib = (struct nat44_cfg_nat *)b;
+
+ return (stringnum_cmp(ia->name, ib->name));
+}
+
+/*
+ * Retrieves nat list from kernel,
+ * optionally sorts it and calls requested function for each table.
+ * Returns 0 on success.
+ */
+static int
+nat_foreach(nat_cb_t *f, void *arg, int sort)
+{
+ ipfw_obj_lheader *olh;
+ struct nat44_cfg_nat *cfg;
+ size_t sz;
+ int i, error;
+
+ /* Start with reasonable default */
+ sz = sizeof(*olh) + 16 * sizeof(struct nat44_cfg_nat);
+
+ for (;;) {
+ if ((olh = calloc(1, sz)) == NULL)
+ return (ENOMEM);
+
+ olh->size = sz;
+ if (do_get3(IP_FW_NAT44_LIST_NAT, &olh->opheader, &sz) != 0) {
+ free(olh);
+ if (errno == ENOMEM) {
+ sz = olh->size;
+ continue;
+ }
+ return (errno);
+ }
+
+ if (sort != 0)
+ qsort(olh + 1, olh->count, olh->objsize, natname_cmp);
+
+ cfg = (struct nat44_cfg_nat*)(olh + 1);
+ for (i = 0; i < olh->count; i++) {
+ error = f(cfg, arg); /* Ignore errors for now */
+ cfg = (struct nat44_cfg_nat *)((caddr_t)cfg +
+ olh->objsize);
+ }
+
+ free(olh);
+ break;
+ }
+
+ return (0);
+}
+
+static int
+nat_get_cmd(char *name, uint16_t cmd, ipfw_obj_header **ooh)
+{
+ ipfw_obj_header *oh;
+ struct nat44_cfg_nat *cfg;
+ size_t sz;
+
+ /* Start with reasonable default */
+ sz = sizeof(*oh) + sizeof(*cfg) + 128;
+
+ for (;;) {
+ if ((oh = calloc(1, sz)) == NULL)
+ return (ENOMEM);
+ cfg = (struct nat44_cfg_nat *)(oh + 1);
+ oh->ntlv.head.length = sizeof(oh->ntlv);
+ strlcpy(oh->ntlv.name, name, sizeof(oh->ntlv.name));
+ strlcpy(cfg->name, name, sizeof(cfg->name));
+
+ if (do_get3(cmd, &oh->opheader, &sz) != 0) {
+ sz = cfg->size;
+ free(oh);
+ if (errno == ENOMEM)
+ continue;
+ return (errno);
+ }
+
+ *ooh = oh;
+ break;
+ }
+
+ return (0);
+}
void
ipfw_show_nat(int ac, char **av)
{
- struct cfg_nat *n;
- struct cfg_redir *e;
- int cmd, i, nbytes, do_cfg, do_rule, frule, lrule, nalloc, size;
- int nat_cnt, redir_cnt, r;
- uint8_t *data, *p;
- char *endptr;
-
- do_rule = 0;
- nalloc = 1024;
- size = 0;
- data = NULL;
- frule = 0;
- lrule = IPFW_DEFAULT_RULE; /* max ipfw rule number */
+ ipfw_obj_header *oh;
+ char *name;
+ int cmd;
+ struct nat_list_arg nla;
+
ac--;
av++;
@@ -936,55 +1081,35 @@ ipfw_show_nat(int ac, char **av)
return;
/* Parse parameters. */
- for (cmd = IP_FW_NAT_GET_LOG, do_cfg = 0; ac != 0; ac--, av++) {
+ cmd = 0; /* XXX: Change to IP_FW_NAT44_XGETLOG @ MFC */
+ name = NULL;
+ for ( ; ac != 0; ac--, av++) {
if (!strncmp(av[0], "config", strlen(av[0]))) {
- cmd = IP_FW_NAT_GET_CONFIG, do_cfg = 1;
+ cmd = IP_FW_NAT44_XGETCONFIG;
+ continue;
+ }
+ if (strcmp(av[0], "log") == 0) {
+ cmd = IP_FW_NAT44_XGETLOG;
continue;
}
- /* Convert command line rule #. */
- frule = lrule = strtoul(av[0], &endptr, 10);
- if (*endptr == '-')
- lrule = strtoul(endptr+1, &endptr, 10);
- if (lrule == 0)
- err(EX_USAGE, "invalid rule number: %s", av[0]);
- do_rule = 1;
+ if (name != NULL)
+ err(EX_USAGE,"only one instance name may be specified");
+ name = av[0];
}
- nbytes = nalloc;
- while (nbytes >= nalloc) {
- nalloc = nalloc * 2;
- nbytes = nalloc;
- data = safe_realloc(data, nbytes);
- if (do_cmd(cmd, data, (uintptr_t)&nbytes) < 0)
- err(EX_OSERR, "getsockopt(IP_FW_GET_%s)",
- (cmd == IP_FW_NAT_GET_LOG) ? "LOG" : "CONFIG");
- }
- if (nbytes == 0)
- exit(0);
- if (do_cfg) {
- nat_cnt = *((int *)data);
- for (i = sizeof(nat_cnt); nat_cnt; nat_cnt--) {
- n = (struct cfg_nat *)&data[i];
- if (frule <= n->id && lrule >= n->id)
- print_nat_config(&data[i]);
- i += sizeof(struct cfg_nat);
- for (redir_cnt = 0; redir_cnt < n->redir_cnt; redir_cnt++) {
- e = (struct cfg_redir *)&data[i];
- i += sizeof(struct cfg_redir) + e->spool_cnt *
- sizeof(struct cfg_spool);
- }
- }
+ if (cmd == 0)
+ errx(EX_USAGE, "Please specify action. Available: config,log");
+
+ if (name == NULL) {
+ memset(&nla, 0, sizeof(nla));
+ nla.cmd = cmd;
+ nla.is_all = 1;
+ nat_foreach(nat_show_data, &nla, 1);
} else {
- for (i = 0; 1; i += LIBALIAS_BUF_SIZE + sizeof(int)) {
- p = &data[i];
- if (p == data + nbytes)
- break;
- bcopy(p, &r, sizeof(int));
- if (do_rule) {
- if (!(frule <= r && lrule >= r))
- continue;
- }
- printf("nat %u: %s\n", r, p+sizeof(int));
- }
+ if (nat_get_cmd(name, cmd, &oh) != 0)
+ err(EX_OSERR, "Error getting nat %s instance info", name);
+ nat_show_cfg((struct nat44_cfg_nat *)(oh + 1), NULL);
+ free(oh);
}
}
+
diff --git a/sys/netinet/ip_fw.h b/sys/netinet/ip_fw.h
index efeecfd..319bec6 100644
--- a/sys/netinet/ip_fw.h
+++ b/sys/netinet/ip_fw.h
@@ -98,6 +98,12 @@ typedef struct _ip_fw3_opheader {
#define IP_FW_TABLE_XSWAP 109 /* swap two tables */
#define IP_FW_TABLE_VLIST 110 /* dump table value hash */
+#define IP_FW_NAT44_XCONFIG 111 /* Create/modify NAT44 instance */
+#define IP_FW_NAT44_DESTROY 112 /* Destroys NAT44 instance */
+#define IP_FW_NAT44_XGETCONFIG 113 /* Get NAT44 instance config */
+#define IP_FW_NAT44_LIST_NAT 114 /* List all NAT44 instances */
+#define IP_FW_NAT44_XGETLOG 115 /* Get log from NAT44 instance */
+
/*
* The kernel representation of ipfw rules is made of a list of
* 'instructions' (for all practical purposes equivalent to BPF
@@ -402,6 +408,8 @@ typedef struct _ipfw_insn_log {
u_int32_t log_left; /* how many left to log */
} ipfw_insn_log;
+/* Legacy NAT structures, compat only */
+#ifndef _KERNEL
/*
* Data structures required by both ipfw(8) and ipfw(4) but not part of the
* management API are protected by IPFW_INTERNAL.
@@ -463,6 +471,44 @@ struct cfg_nat {
#define SOF_REDIR sizeof(struct cfg_redir)
#define SOF_SPOOL sizeof(struct cfg_spool)
+#endif /* ifndef _KERNEL */
+
+
+struct nat44_cfg_spool {
+ struct in_addr addr;
+ uint16_t port;
+ uint16_t spare;
+};
+#define NAT44_REDIR_ADDR 0x01
+#define NAT44_REDIR_PORT 0x02
+#define NAT44_REDIR_PROTO 0x04
+
+/* Nat redirect configuration. */
+struct nat44_cfg_redir {
+ struct in_addr laddr; /* local ip address */
+ struct in_addr paddr; /* public ip address */
+ struct in_addr raddr; /* remote ip address */
+ uint16_t lport; /* local port */
+ uint16_t pport; /* public port */
+ uint16_t rport; /* remote port */
+ uint16_t pport_cnt; /* number of public ports */
+ uint16_t rport_cnt; /* number of remote ports */
+ uint16_t mode; /* type of redirect mode */
+ uint16_t spool_cnt; /* num of entry in spool chain */
+ uint16_t spare;
+ uint32_t proto; /* protocol: tcp/udp */
+};
+
+/* Nat configuration data struct. */
+struct nat44_cfg_nat {
+ char name[64]; /* nat name */
+ char if_name[64]; /* interface name */
+ uint32_t size; /* structure size incl. redirs */
+ struct in_addr ip; /* nat IPv4 address */
+ uint32_t mode; /* aliasing mode */
+ uint32_t redir_cnt; /* number of entry in spool chain */
+};
+
/* Nat command. */
typedef struct _ipfw_insn_nat {
ipfw_insn o;
diff --git a/sys/netpfil/ipfw/ip_fw_nat.c b/sys/netpfil/ipfw/ip_fw_nat.c
index 2d1002f..f4abe26 100644
--- a/sys/netpfil/ipfw/ip_fw_nat.c
+++ b/sys/netpfil/ipfw/ip_fw_nat.c
@@ -37,8 +37,6 @@ __FBSDID("$FreeBSD$");
#include <sys/module.h>
#include <sys/rwlock.h>
-#define IPFW_INTERNAL /* Access to protected data structures in ip_fw.h. */
-
#include <netinet/libalias/alias.h>
#include <netinet/libalias/alias_local.h>
@@ -55,6 +53,45 @@ __FBSDID("$FreeBSD$");
#include <machine/in_cksum.h> /* XXX for in_cksum */
+struct cfg_spool {
+ LIST_ENTRY(cfg_spool) _next; /* chain of spool instances */
+ struct in_addr addr;
+ uint16_t port;
+};
+
+/* Nat redirect configuration. */
+struct cfg_redir {
+ LIST_ENTRY(cfg_redir) _next; /* chain of redir instances */
+ uint16_t mode; /* type of redirect mode */
+ uint16_t proto; /* protocol: tcp/udp */
+ struct in_addr laddr; /* local ip address */
+ struct in_addr paddr; /* public ip address */
+ struct in_addr raddr; /* remote ip address */
+ uint16_t lport; /* local port */
+ uint16_t pport; /* public port */
+ uint16_t rport; /* remote port */
+ uint16_t pport_cnt; /* number of public ports */
+ uint16_t rport_cnt; /* number of remote ports */
+ struct alias_link **alink;
+ u_int16_t spool_cnt; /* num of entry in spool chain */
+ /* chain of spool instances */
+ LIST_HEAD(spool_chain, cfg_spool) spool_chain;
+};
+
+/* 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 */
+ struct libalias *lib; /* libalias instance */
+ int mode; /* aliasing mode */
+ int redir_cnt; /* number of entry in spool chain */
+ /* chain of redir instances */
+ LIST_HEAD(redir_chain, cfg_redir) redir_chain;
+ char if_name[IF_NAMESIZE]; /* interface name */
+};
+
static eventhandler_tag ifaddr_event_tag;
static void
@@ -117,11 +154,11 @@ del_redir_spool_cfg(struct cfg_nat *n, struct redir_chain *head)
LIST_FOREACH_SAFE(r, head, _next, tmp_r) {
num = 1; /* Number of alias_link to delete. */
switch (r->mode) {
- case REDIR_PORT:
+ case NAT44_REDIR_PORT:
num = r->pport_cnt;
/* FALLTHROUGH */
- case REDIR_ADDR:
- case REDIR_PROTO:
+ case NAT44_REDIR_ADDR:
+ case NAT44_REDIR_PROTO:
/* Delete all libalias redirect entry. */
for (i = 0; i < num; i++)
LibAliasRedirectDelete(n->lib, r->alink[i]);
@@ -142,27 +179,41 @@ del_redir_spool_cfg(struct cfg_nat *n, struct redir_chain *head)
}
}
-static void
+static int
add_redir_spool_cfg(char *buf, struct cfg_nat *ptr)
{
- struct cfg_redir *r, *ser_r;
- struct cfg_spool *s, *ser_s;
+ struct cfg_redir *r;
+ struct cfg_spool *s;
+ struct nat44_cfg_redir *ser_r;
+ struct nat44_cfg_spool *ser_s;
+
int cnt, off, i;
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);
+ ser_r = (struct nat44_cfg_redir *)&buf[off];
+ r = malloc(sizeof(*r), M_IPFW, M_WAITOK | M_ZERO);
+ r->mode = ser_r->mode;
+ r->laddr = ser_r->laddr;
+ r->paddr = ser_r->paddr;
+ r->raddr = ser_r->raddr;
+ r->lport = ser_r->lport;
+ r->pport = ser_r->pport;
+ r->rport = ser_r->rport;
+ r->pport_cnt = ser_r->pport_cnt;
+ r->rport_cnt = ser_r->rport_cnt;
+ r->proto = ser_r->proto;
+ r->spool_cnt = ser_r->spool_cnt;
+ //memcpy(r, ser_r, SOF_REDIR);
LIST_INIT(&r->spool_chain);
- off += SOF_REDIR;
+ off += sizeof(struct nat44_cfg_redir);
r->alink = malloc(sizeof(struct alias_link *) * r->pport_cnt,
M_IPFW, M_WAITOK | M_ZERO);
switch (r->mode) {
- case REDIR_ADDR:
+ case NAT44_REDIR_ADDR:
r->alink[0] = LibAliasRedirectAddr(ptr->lib, r->laddr,
r->paddr);
break;
- case REDIR_PORT:
+ case NAT44_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;
@@ -178,7 +229,7 @@ add_redir_spool_cfg(char *buf, struct cfg_nat *ptr)
}
}
break;
- case REDIR_PROTO:
+ case NAT44_REDIR_PROTO:
r->alink[0] = LibAliasRedirectProto(ptr->lib ,r->laddr,
r->raddr, r->paddr, r->proto);
break;
@@ -186,23 +237,27 @@ add_redir_spool_cfg(char *buf, struct cfg_nat *ptr)
printf("unknown redirect mode: %u\n", r->mode);
break;
}
- /* XXX perhaps return an error instead of panic ? */
- if (r->alink[0] == NULL)
- panic("LibAliasRedirect* returned NULL");
+ if (r->alink[0] == NULL) {
+ printf("LibAliasRedirect* returned NULL\n");
+ return (EINVAL);
+ }
/* 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);
+ ser_s = (struct nat44_cfg_spool *)&buf[off];
+ s = malloc(sizeof(*s), M_IPFW, M_WAITOK | M_ZERO);
+ s->addr = ser_s->addr;
+ s->port = ser_s->port;
LibAliasAddServer(ptr->lib, r->alink[0],
s->addr, htons(s->port));
- off += SOF_SPOOL;
+ off += sizeof(struct nat44_cfg_spool);
/* Hook spool entry. */
LIST_INSERT_HEAD(&r->spool_chain, s, _next);
}
/* And finally hook this redir entry. */
LIST_INSERT_HEAD(&ptr->redir_chain, r, _next);
}
+
+ return (0);
}
/*
@@ -392,60 +447,68 @@ lookup_nat(struct nat_list *l, int nat_id)
return res;
}
-static int
-ipfw_nat_cfg(struct sockopt *sopt)
+static struct cfg_nat *
+lookup_nat_name(struct nat_list *l, char *name)
{
- struct cfg_nat *cfg, *ptr;
- char *buf;
- struct ip_fw_chain *chain = &V_layer3_chain;
- size_t len;
- int gencnt, error = 0;
+ struct cfg_nat *res;
+ int id;
+ char *errptr;
- len = sopt->sopt_valsize;
- buf = malloc(len, M_TEMP, M_WAITOK | M_ZERO);
- if ((error = sooptcopyin(sopt, buf, len, sizeof(struct cfg_nat))) != 0)
- goto out;
+ id = strtol(name, &errptr, 10);
+ if (id == 0 || *errptr != '\0')
+ return (NULL);
- cfg = (struct cfg_nat *)buf;
- if (cfg->id < 0) {
- error = EINVAL;
- goto out;
+ LIST_FOREACH(res, l, _next) {
+ if (res->id == id)
+ break;
}
+ return (res);
+}
+
+/* IP_FW3 configuration routines */
+
+static void
+nat44_config(struct ip_fw_chain *chain, struct nat44_cfg_nat *ucfg)
+{
+ struct cfg_nat *ptr, *tcfg;
+ int gencnt;
/*
* Find/create nat rule.
*/
- IPFW_WLOCK(chain);
+ IPFW_UH_WLOCK(chain);
gencnt = chain->gencnt;
- ptr = lookup_nat(&chain->nat, cfg->id);
+ ptr = lookup_nat_name(&chain->nat, ucfg->name);
if (ptr == NULL) {
- IPFW_WUNLOCK(chain);
+ IPFW_UH_WUNLOCK(chain);
/* New rule: allocate and init new instance. */
ptr = malloc(sizeof(struct cfg_nat), M_IPFW, M_WAITOK | M_ZERO);
ptr->lib = LibAliasInit(NULL);
LIST_INIT(&ptr->redir_chain);
} else {
/* Entry already present: temporarily unhook it. */
+ IPFW_WLOCK(chain);
LIST_REMOVE(ptr, _next);
- flush_nat_ptrs(chain, cfg->id);
+ flush_nat_ptrs(chain, ptr->id);
IPFW_WUNLOCK(chain);
+ IPFW_UH_WUNLOCK(chain);
}
/*
- * Basic nat configuration.
+ * Basic nat (re)configuration.
*/
- ptr->id = cfg->id;
+ ptr->id = strtol(ucfg->name, NULL, 10);
/*
* XXX - what if this rule doesn't nat any ip and just
* redirect?
* do we set aliasaddress to 0.0.0.0?
*/
- ptr->ip = cfg->ip;
- ptr->redir_cnt = cfg->redir_cnt;
- ptr->mode = cfg->mode;
- LibAliasSetMode(ptr->lib, cfg->mode, ~0);
+ ptr->ip = ucfg->ip;
+ ptr->redir_cnt = ucfg->redir_cnt;
+ ptr->mode = ucfg->mode;
+ strlcpy(ptr->if_name, ucfg->if_name, sizeof(ptr->if_name));
+ LibAliasSetMode(ptr->lib, ptr->mode, ~0);
LibAliasSetAddress(ptr->lib, ptr->ip);
- memcpy(ptr->if_name, cfg->if_name, IF_NAMESIZE);
/*
* Redir and LSNAT configuration.
@@ -453,16 +516,455 @@ ipfw_nat_cfg(struct sockopt *sopt)
/* Delete old cfgs. */
del_redir_spool_cfg(ptr, &ptr->redir_chain);
/* Add new entries. */
- add_redir_spool_cfg(&buf[(sizeof(struct cfg_nat))], ptr);
+ add_redir_spool_cfg((char *)(ucfg + 1), ptr);
+ IPFW_UH_WLOCK(chain);
- IPFW_WLOCK(chain);
/* Extra check to avoid race with another ipfw_nat_cfg() */
- if (gencnt != chain->gencnt &&
- ((cfg = lookup_nat(&chain->nat, ptr->id)) != NULL))
- LIST_REMOVE(cfg, _next);
+ tcfg = NULL;
+ if (gencnt != chain->gencnt)
+ tcfg = lookup_nat_name(&chain->nat, ucfg->name);
+ IPFW_WLOCK(chain);
+ if (tcfg != NULL)
+ LIST_REMOVE(tcfg, _next);
LIST_INSERT_HEAD(&chain->nat, ptr, _next);
+ IPFW_WUNLOCK(chain);
chain->gencnt++;
+
+ IPFW_UH_WUNLOCK(chain);
+
+ if (tcfg != NULL)
+ free(tcfg, M_IPFW);
+}
+
+/*
+ * Creates/configure nat44 instance
+ * Data layout (v0)(current):
+ * Request: [ ipfw_obj_header nat44_cfg_nat .. ]
+ *
+ * Returns 0 on success
+ */
+static int
+nat44_cfg(struct ip_fw_chain *chain, ip_fw3_opheader *op3,
+ struct sockopt_data *sd)
+{
+ ipfw_obj_header *oh;
+ struct nat44_cfg_nat *ucfg;
+ int id;
+ size_t read;
+ char *errptr;
+
+ /* Check minimum header size */
+ if (sd->valsize < (sizeof(*oh) + sizeof(*ucfg)))
+ return (EINVAL);
+
+ oh = (ipfw_obj_header *)sd->kbuf;
+
+ /* Basic length checks for TLVs */
+ if (oh->ntlv.head.length != sizeof(oh->ntlv))
+ return (EINVAL);
+
+ ucfg = (struct nat44_cfg_nat *)(oh + 1);
+
+ /* Check if name is properly terminated and looks like number */
+ if (strnlen(ucfg->name, sizeof(ucfg->name)) == sizeof(ucfg->name))
+ return (EINVAL);
+ id = strtol(ucfg->name, &errptr, 10);
+ if (id == 0 || *errptr != '\0')
+ return (EINVAL);
+
+ read = sizeof(*oh) + sizeof(*ucfg);
+ /* Check number of redirs */
+ if (sd->valsize < read + ucfg->redir_cnt*sizeof(struct nat44_cfg_redir))
+ return (EINVAL);
+
+ nat44_config(chain, ucfg);
+ return (0);
+}
+
+/*
+ * Destroys given nat instances.
+ * Data layout (v0)(current):
+ * Request: [ ipfw_obj_header ]
+ *
+ * Returns 0 on success
+ */
+static int
+nat44_destroy(struct ip_fw_chain *chain, ip_fw3_opheader *op3,
+ struct sockopt_data *sd)
+{
+ ipfw_obj_header *oh;
+ struct cfg_nat *ptr;
+ ipfw_obj_ntlv *ntlv;
+
+ /* Check minimum header size */
+ if (sd->valsize < sizeof(*oh))
+ return (EINVAL);
+
+ oh = (ipfw_obj_header *)sd->kbuf;
+
+ /* Basic length checks for TLVs */
+ if (oh->ntlv.head.length != sizeof(oh->ntlv))
+ return (EINVAL);
+
+ ntlv = &oh->ntlv;
+ /* Check if name is properly terminated */
+ if (strnlen(ntlv->name, sizeof(ntlv->name)) == sizeof(ntlv->name))
+ return (EINVAL);
+
+ IPFW_UH_WLOCK(chain);
+ ptr = lookup_nat_name(&chain->nat, ntlv->name);
+ if (ptr == NULL) {
+ IPFW_UH_WUNLOCK(chain);
+ return (ESRCH);
+ }
+ IPFW_WLOCK(chain);
+ LIST_REMOVE(ptr, _next);
+ flush_nat_ptrs(chain, ptr->id);
IPFW_WUNLOCK(chain);
+ IPFW_UH_WUNLOCK(chain);
+
+ del_redir_spool_cfg(ptr, &ptr->redir_chain);
+ LibAliasUninit(ptr->lib);
+ free(ptr, M_IPFW);
+
+ return (0);
+}
+
+static void
+export_nat_cfg(struct cfg_nat *ptr, struct nat44_cfg_nat *ucfg)
+{
+
+ snprintf(ucfg->name, sizeof(ucfg->name), "%d", ptr->id);
+ ucfg->ip = ptr->ip;
+ ucfg->redir_cnt = ptr->redir_cnt;
+ ucfg->mode = ptr->mode;
+ strlcpy(ucfg->if_name, ptr->if_name, sizeof(ucfg->if_name));
+}
+
+/*
+ * Gets config for given nat instance
+ * Data layout (v0)(current):
+ * Request: [ ipfw_obj_header nat44_cfg_nat .. ]
+ *
+ * Returns 0 on success
+ */
+static int
+nat44_get_cfg(struct ip_fw_chain *chain, ip_fw3_opheader *op3,
+ struct sockopt_data *sd)
+{
+ ipfw_obj_header *oh;
+ struct nat44_cfg_nat *ucfg;
+ struct cfg_nat *ptr;
+ struct cfg_redir *r;
+ struct cfg_spool *s;
+ struct nat44_cfg_redir *ser_r;
+ struct nat44_cfg_spool *ser_s;
+ size_t sz;
+
+ sz = sizeof(*oh) + sizeof(*ucfg);
+ /* Check minimum header size */
+ if (sd->valsize < sz)
+ return (EINVAL);
+
+ oh = (struct _ipfw_obj_header *)ipfw_get_sopt_header(sd, sz);
+
+ /* Basic length checks for TLVs */
+ if (oh->ntlv.head.length != sizeof(oh->ntlv))
+ return (EINVAL);
+
+ ucfg = (struct nat44_cfg_nat *)(oh + 1);
+
+ /* Check if name is properly terminated */
+ if (strnlen(ucfg->name, sizeof(ucfg->name)) == sizeof(ucfg->name))
+ return (EINVAL);
+
+ IPFW_UH_RLOCK(chain);
+ ptr = lookup_nat_name(&chain->nat, ucfg->name);
+ if (ptr == NULL) {
+ IPFW_UH_RUNLOCK(chain);
+ return (ESRCH);
+ }
+
+ export_nat_cfg(ptr, ucfg);
+
+ /* Estimate memory amount */
+ sz = sizeof(struct nat44_cfg_nat);
+ LIST_FOREACH(r, &ptr->redir_chain, _next) {
+ sz += sizeof(struct nat44_cfg_redir);
+ LIST_FOREACH(s, &r->spool_chain, _next)
+ sz += sizeof(struct nat44_cfg_spool);
+ }
+
+ ucfg->size = sz;
+ if (sd->valsize < sz + sizeof(*oh)) {
+
+ /*
+ * Submitted buffer size is not enough.
+ * WE've already filled in @ucfg structure with
+ * relevant info including size, so we
+ * can return. Buffer will be flushed automatically.
+ */
+ IPFW_UH_RUNLOCK(chain);
+ return (ENOMEM);
+ }
+
+ /* Size OK, let's copy data */
+ LIST_FOREACH(r, &ptr->redir_chain, _next) {
+ ser_r = (struct nat44_cfg_redir *)ipfw_get_sopt_space(sd,
+ sizeof(*ser_r));
+ ser_r->mode = r->mode;
+ ser_r->laddr = r->laddr;
+ ser_r->paddr = r->paddr;
+ ser_r->raddr = r->raddr;
+ ser_r->lport = r->lport;
+ ser_r->pport = r->pport;
+ ser_r->rport = r->rport;
+ ser_r->pport_cnt = r->pport_cnt;
+ ser_r->rport_cnt = r->rport_cnt;
+ ser_r->proto = r->proto;
+ ser_r->spool_cnt = r->spool_cnt;
+
+ LIST_FOREACH(s, &r->spool_chain, _next) {
+ ser_s = (struct nat44_cfg_spool *)ipfw_get_sopt_space(
+ sd, sizeof(*ser_s));
+
+ ser_s->addr = s->addr;
+ ser_s->port = s->port;
+ }
+ }
+
+ IPFW_UH_RUNLOCK(chain);
+
+ return (0);
+}
+
+/*
+ * Lists all nat44 instances currently available in kernel.
+ * Data layout (v0)(current):
+ * Request: [ ipfw_obj_lheader ]
+ * Reply: [ ipfw_obj_lheader nat44_cfg_nat x N ]
+ *
+ * Returns 0 on success
+ */
+static int
+nat44_list_nat(struct ip_fw_chain *chain, ip_fw3_opheader *op3,
+ struct sockopt_data *sd)
+{
+ ipfw_obj_lheader *olh;
+ struct nat44_cfg_nat *ucfg;
+ struct cfg_nat *ptr;
+ int nat_count;
+
+ /* Check minimum header size */
+ if (sd->valsize < sizeof(ipfw_obj_lheader))
+ return (EINVAL);
+
+ olh = (ipfw_obj_lheader *)ipfw_get_sopt_header(sd, sizeof(*olh));
+ IPFW_UH_RLOCK(chain);
+ nat_count = 0;
+ LIST_FOREACH(ptr, &chain->nat, _next)
+ nat_count++;
+
+ olh->count = nat_count;
+ olh->objsize = sizeof(struct nat44_cfg_nat);
+ olh->size = sizeof(*olh) + olh->count * olh->objsize;
+
+ if (sd->valsize < olh->size) {
+ IPFW_UH_RUNLOCK(chain);
+ return (ENOMEM);
+ }
+
+ LIST_FOREACH(ptr, &chain->nat, _next) {
+ ucfg = (struct nat44_cfg_nat *)ipfw_get_sopt_space(sd,
+ sizeof(*ucfg));
+ export_nat_cfg(ptr, ucfg);
+ }
+
+ IPFW_UH_RUNLOCK(chain);
+
+ return (0);
+}
+
+/*
+ * Gets log for given nat instance
+ * Data layout (v0)(current):
+ * Request: [ ipfw_obj_header nat44_cfg_nat ]
+ * Reply: [ ipfw_obj_header nat44_cfg_nat LOGBUFFER ]
+ *
+ * Returns 0 on success
+ */
+static int
+nat44_get_log(struct ip_fw_chain *chain, ip_fw3_opheader *op3,
+ struct sockopt_data *sd)
+{
+ ipfw_obj_header *oh;
+ struct nat44_cfg_nat *ucfg;
+ struct cfg_nat *ptr;
+ void *pbuf;
+ size_t sz;
+
+ sz = sizeof(*oh) + sizeof(*ucfg);
+ /* Check minimum header size */
+ if (sd->valsize < sz)
+ return (EINVAL);
+
+ oh = (struct _ipfw_obj_header *)ipfw_get_sopt_header(sd, sz);
+
+ /* Basic length checks for TLVs */
+ if (oh->ntlv.head.length != sizeof(oh->ntlv))
+ return (EINVAL);
+
+ ucfg = (struct nat44_cfg_nat *)(oh + 1);
+
+ /* Check if name is properly terminated */
+ if (strnlen(ucfg->name, sizeof(ucfg->name)) == sizeof(ucfg->name))
+ return (EINVAL);
+
+ IPFW_UH_RLOCK(chain);
+ ptr = lookup_nat_name(&chain->nat, ucfg->name);
+ if (ptr == NULL) {
+ IPFW_UH_RUNLOCK(chain);
+ return (ESRCH);
+ }
+
+ if (ptr->lib->logDesc == NULL) {
+ IPFW_UH_RUNLOCK(chain);
+ return (ENOENT);
+ }
+
+ export_nat_cfg(ptr, ucfg);
+
+ /* Estimate memory amount */
+ ucfg->size = sizeof(struct nat44_cfg_nat) + LIBALIAS_BUF_SIZE;
+ if (sd->valsize < sz + sizeof(*oh)) {
+
+ /*
+ * Submitted buffer size is not enough.
+ * WE've already filled in @ucfg structure with
+ * relevant info including size, so we
+ * can return. Buffer will be flushed automatically.
+ */
+ IPFW_UH_RUNLOCK(chain);
+ return (ENOMEM);
+ }
+
+ pbuf = (void *)ipfw_get_sopt_space(sd, LIBALIAS_BUF_SIZE);
+ memcpy(pbuf, ptr->lib->logDesc, LIBALIAS_BUF_SIZE);
+
+ IPFW_UH_RUNLOCK(chain);
+
+ return (0);
+}
+
+static struct ipfw_sopt_handler scodes[] = {
+ { IP_FW_NAT44_XCONFIG, 0, HDIR_SET, nat44_cfg },
+ { IP_FW_NAT44_DESTROY, 0, HDIR_SET, nat44_destroy },
+ { IP_FW_NAT44_XGETCONFIG, 0, HDIR_GET, nat44_get_cfg },
+ { IP_FW_NAT44_LIST_NAT, 0, HDIR_GET, nat44_list_nat },
+ { IP_FW_NAT44_XGETLOG, 0, HDIR_GET, nat44_get_log },
+};
+
+
+/*
+ * Legacy configuration routines
+ */
+
+struct cfg_spool_legacy {
+ LIST_ENTRY(cfg_spool_legacy) _next;
+ struct in_addr addr;
+ u_short port;
+};
+
+struct cfg_redir_legacy {
+ LIST_ENTRY(cfg_redir) _next;
+ u_int16_t mode;
+ struct in_addr laddr;
+ struct in_addr paddr;
+ struct in_addr raddr;
+ u_short lport;
+ u_short pport;
+ u_short rport;
+ u_short pport_cnt;
+ u_short rport_cnt;
+ int proto;
+ struct alias_link **alink;
+ u_int16_t spool_cnt;
+ LIST_HEAD(, cfg_spool_legacy) spool_chain;
+};
+
+struct cfg_nat_legacy {
+ LIST_ENTRY(cfg_nat_legacy) _next;
+ int id;
+ struct in_addr ip;
+ char if_name[IF_NAMESIZE];
+ int mode;
+ struct libalias *lib;
+ int redir_cnt;
+ LIST_HEAD(, cfg_redir_legacy) redir_chain;
+};
+
+static int
+ipfw_nat_cfg(struct sockopt *sopt)
+{
+ struct cfg_nat_legacy *cfg;
+ struct nat44_cfg_nat *ucfg;
+ struct cfg_redir_legacy *rdir;
+ struct nat44_cfg_redir *urdir;
+ char *buf;
+ size_t len, len2;
+ int error, i;
+
+ len = sopt->sopt_valsize;
+ len2 = len + 128;
+
+ /*
+ * Allocate 2x buffer to store converted structures.
+ * new redir_cfg has shrinked, so we're sure that
+ * new buffer size is enough.
+ */
+ buf = malloc(roundup2(len, 8) + len2, M_TEMP, M_WAITOK | M_ZERO);
+ error = sooptcopyin(sopt, buf, len, sizeof(struct cfg_nat_legacy));
+ if (error != 0)
+ goto out;
+
+ cfg = (struct cfg_nat_legacy *)buf;
+ if (cfg->id < 0) {
+ error = EINVAL;
+ goto out;
+ }
+
+ ucfg = (struct nat44_cfg_nat *)&buf[roundup2(len, 8)];
+ snprintf(ucfg->name, sizeof(ucfg->name), "%d", cfg->id);
+ strlcpy(ucfg->if_name, cfg->if_name, sizeof(ucfg->if_name));
+ ucfg->ip = cfg->ip;
+ ucfg->mode = cfg->mode;
+ ucfg->redir_cnt = cfg->redir_cnt;
+
+ if (len < sizeof(*cfg) + cfg->redir_cnt * sizeof(*rdir)) {
+ error = EINVAL;
+ goto out;
+ }
+
+ urdir = (struct nat44_cfg_redir *)(ucfg + 1);
+ rdir = (struct cfg_redir_legacy *)(cfg + 1);
+ for (i = 0; i < cfg->redir_cnt; i++) {
+ urdir->mode = rdir->mode;
+ urdir->laddr = rdir->laddr;
+ urdir->paddr = rdir->paddr;
+ urdir->raddr = rdir->raddr;
+ urdir->lport = rdir->lport;
+ urdir->pport = rdir->pport;
+ urdir->rport = rdir->rport;
+ urdir->pport_cnt = rdir->pport_cnt;
+ urdir->rport_cnt = rdir->rport_cnt;
+ urdir->proto = rdir->proto;
+ urdir->spool_cnt = rdir->spool_cnt;
+
+ urdir++;
+ rdir++;
+ }
+
+ nat44_config(&V_layer3_chain, ucfg);
out:
free(buf, M_TEMP);
@@ -478,15 +980,17 @@ ipfw_nat_del(struct sockopt *sopt)
sooptcopyin(sopt, &i, sizeof i, sizeof i);
/* XXX validate i */
- IPFW_WLOCK(chain);
+ IPFW_UH_WLOCK(chain);
ptr = lookup_nat(&chain->nat, i);
if (ptr == NULL) {
- IPFW_WUNLOCK(chain);
+ IPFW_UH_WUNLOCK(chain);
return (EINVAL);
}
+ IPFW_WLOCK(chain);
LIST_REMOVE(ptr, _next);
flush_nat_ptrs(chain, i);
IPFW_WUNLOCK(chain);
+ IPFW_UH_WUNLOCK(chain);
del_redir_spool_cfg(ptr, &ptr->redir_chain);
LibAliasUninit(ptr->lib);
free(ptr, M_IPFW);
@@ -498,28 +1002,31 @@ ipfw_nat_get_cfg(struct sockopt *sopt)
{
struct ip_fw_chain *chain = &V_layer3_chain;
struct cfg_nat *n;
+ struct cfg_nat_legacy *ucfg;
struct cfg_redir *r;
struct cfg_spool *s;
+ struct cfg_redir_legacy *ser_r;
+ struct cfg_spool_legacy *ser_s;
char *data;
int gencnt, nat_cnt, len, error;
nat_cnt = 0;
len = sizeof(nat_cnt);
- IPFW_RLOCK(chain);
+ IPFW_UH_RLOCK(chain);
retry:
gencnt = chain->gencnt;
/* Estimate memory amount */
LIST_FOREACH(n, &chain->nat, _next) {
nat_cnt++;
- len += sizeof(struct cfg_nat);
+ len += sizeof(struct cfg_nat_legacy);
LIST_FOREACH(r, &n->redir_chain, _next) {
- len += sizeof(struct cfg_redir);
+ len += sizeof(struct cfg_redir_legacy);
LIST_FOREACH(s, &r->spool_chain, _next)
- len += sizeof(struct cfg_spool);
+ len += sizeof(struct cfg_spool_legacy);
}
}
- IPFW_RUNLOCK(chain);
+ IPFW_UH_RUNLOCK(chain);
data = malloc(len, M_TEMP, M_WAITOK | M_ZERO);
bcopy(&nat_cnt, data, sizeof(nat_cnt));
@@ -527,25 +1034,43 @@ retry:
nat_cnt = 0;
len = sizeof(nat_cnt);
- IPFW_RLOCK(chain);
+ IPFW_UH_RLOCK(chain);
if (gencnt != chain->gencnt) {
free(data, M_TEMP);
goto retry;
}
/* Serialize all the data. */
LIST_FOREACH(n, &chain->nat, _next) {
- bcopy(n, &data[len], sizeof(struct cfg_nat));
- len += sizeof(struct cfg_nat);
+ ucfg = (struct cfg_nat_legacy *)&data[len];
+ ucfg->id = n->id;
+ ucfg->ip = n->ip;
+ ucfg->redir_cnt = n->redir_cnt;
+ ucfg->mode = n->mode;
+ strlcpy(ucfg->if_name, n->if_name, sizeof(ucfg->if_name));
+ len += sizeof(struct cfg_nat_legacy);
LIST_FOREACH(r, &n->redir_chain, _next) {
- bcopy(r, &data[len], sizeof(struct cfg_redir));
- len += sizeof(struct cfg_redir);
+ ser_r = (struct cfg_redir_legacy *)&data[len];
+ ser_r->mode = r->mode;
+ ser_r->laddr = r->laddr;
+ ser_r->paddr = r->paddr;
+ ser_r->raddr = r->raddr;
+ ser_r->lport = r->lport;
+ ser_r->pport = r->pport;
+ ser_r->rport = r->rport;
+ ser_r->pport_cnt = r->pport_cnt;
+ ser_r->rport_cnt = r->rport_cnt;
+ ser_r->proto = r->proto;
+ ser_r->spool_cnt = r->spool_cnt;
+ len += sizeof(struct cfg_redir_legacy);
LIST_FOREACH(s, &r->spool_chain, _next) {
- bcopy(s, &data[len], sizeof(struct cfg_spool));
- len += sizeof(struct cfg_spool);
+ ser_s = (struct cfg_spool_legacy *)&data[len];
+ ser_s->addr = s->addr;
+ ser_s->port = s->port;
+ len += sizeof(struct cfg_spool_legacy);
}
}
}
- IPFW_RUNLOCK(chain);
+ IPFW_UH_RUNLOCK(chain);
error = sooptcopyout(sopt, data, len);
free(data, M_TEMP);
@@ -631,6 +1156,7 @@ ipfw_nat_init(void)
ipfw_nat_del_ptr = ipfw_nat_del;
ipfw_nat_get_cfg_ptr = ipfw_nat_get_cfg;
ipfw_nat_get_log_ptr = ipfw_nat_get_log;
+ IPFW_ADD_SOPT_HANDLER(1, scodes);
ifaddr_event_tag = EVENTHANDLER_REGISTER(ifaddr_event, ifaddr_change,
NULL, EVENTHANDLER_PRI_ANY);
@@ -642,6 +1168,7 @@ ipfw_nat_destroy(void)
EVENTHANDLER_DEREGISTER(ifaddr_event, ifaddr_event_tag);
/* deregister ipfw_nat */
+ IPFW_DEL_SOPT_HANDLER(1, scodes);
ipfw_nat_ptr = NULL;
lookup_nat_ptr = NULL;
ipfw_nat_cfg_ptr = NULL;
OpenPOWER on IntegriCloud