From d6164b77f8b779cd7357387dcfcd3407f1457579 Mon Sep 17 00:00:00 2001 From: "Alexander V. Chernikov" Date: Sun, 7 Sep 2014 18:30:29 +0000 Subject: [PATCH] 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. --- sbin/ipfw/nat.c | 355 +++++++++++++------ sys/netinet/ip_fw.h | 46 +++ sys/netpfil/ipfw/ip_fw_nat.c | 665 +++++++++++++++++++++++++++++++---- 3 files changed, 882 insertions(+), 184 deletions(-) diff --git a/sbin/ipfw/nat.c b/sbin/ipfw/nat.c index bff28e156c77..3bd0259ad5ad 100644 --- a/sbin/ipfw/nat.c +++ b/sbin/ipfw/nat.c @@ -30,14 +30,13 @@ #include #include +#include #include #include #include #include #include -#define IPFW_INTERNAL /* Access to protected structures in ip_fw.h. */ - #include #include #include /* def. of struct route */ @@ -46,6 +45,14 @@ #include #include +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); } } + diff --git a/sys/netinet/ip_fw.h b/sys/netinet/ip_fw.h index efeecfd577a5..319bec65ff62 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 2d1002f6184f..f4abe26ded1a 100644 --- a/sys/netpfil/ipfw/ip_fw_nat.c +++ b/sys/netpfil/ipfw/ip_fw_nat.c @@ -37,8 +37,6 @@ __FBSDID("$FreeBSD$"); #include #include -#define IPFW_INTERNAL /* Access to protected data structures in ip_fw.h. */ - #include #include @@ -55,6 +53,45 @@ __FBSDID("$FreeBSD$"); #include /* 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;