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:
parent
1a33e79969
commit
d6164b77f8
355
sbin/ipfw/nat.c
355
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;
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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 @@ 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;
|
||||
|
Loading…
x
Reference in New Issue
Block a user