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.
This commit is contained in:
Alexander V. Chernikov 2014-09-07 18:30:29 +00:00
parent 1a33e79969
commit d6164b77f8
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/projects/ipfw/; revision=271231
3 changed files with 882 additions and 184 deletions

View file

@ -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;
ipfw_obj_header *oh;
char *name;
int cmd;
struct nat_list_arg nla;
do_rule = 0;
nalloc = 1024;
size = 0;
data = NULL;
frule = 0;
lrule = IPFW_DEFAULT_RULE; /* max ipfw rule number */
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;
}
/* 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 (strcmp(av[0], "log") == 0) {
cmd = IP_FW_NAT44_XGETLOG;
continue;
}
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);
}
}

View file

@ -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;

View file

@ -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);
chain->gencnt++;
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 @@ ipfw_nat_get_cfg(struct sockopt *sopt)
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;