diff options
author | melifaro <melifaro@FreeBSD.org> | 2014-09-07 18:30:29 +0000 |
---|---|---|
committer | melifaro <melifaro@FreeBSD.org> | 2014-09-07 18:30:29 +0000 |
commit | f7e682304522f7227318f07a70ec0e78a6377465 (patch) | |
tree | e160314c513d0051e3a7e123db859557495ecb89 /sys/netpfil/ipfw | |
parent | 595fec1055729eec7c788613a0c66d8ec7cbc5ea (diff) | |
download | FreeBSD-src-f7e682304522f7227318f07a70ec0e78a6377465.zip FreeBSD-src-f7e682304522f7227318f07a70ec0e78a6377465.tar.gz |
Make ipfw_nat module use IP_FW3 codes.
Kernel changes:
* Split kernel/userland nat structures eliminating IPFW_INTERNAL hack.
* Add IP_FW_NAT44_* codes resemblin old ones.
* Assume that instances can be named (no kernel support currently).
* Use both UH+WLOCK locks for all configuration changes.
* Provide full ABI support for old sockopts.
Userland changes:
* Use IP_FW_NAT44_* codes for nat operations.
* Remove undocumented ability to show ranges of nat "log" entries.
Diffstat (limited to 'sys/netpfil/ipfw')
-rw-r--r-- | sys/netpfil/ipfw/ip_fw_nat.c | 663 |
1 files changed, 595 insertions, 68 deletions
diff --git a/sys/netpfil/ipfw/ip_fw_nat.c b/sys/netpfil/ipfw/ip_fw_nat.c index 2d1002f..f4abe26 100644 --- a/sys/netpfil/ipfw/ip_fw_nat.c +++ b/sys/netpfil/ipfw/ip_fw_nat.c @@ -37,8 +37,6 @@ __FBSDID("$FreeBSD$"); #include <sys/module.h> #include <sys/rwlock.h> -#define IPFW_INTERNAL /* Access to protected data structures in ip_fw.h. */ - #include <netinet/libalias/alias.h> #include <netinet/libalias/alias_local.h> @@ -55,6 +53,45 @@ __FBSDID("$FreeBSD$"); #include <machine/in_cksum.h> /* XXX for in_cksum */ +struct cfg_spool { + LIST_ENTRY(cfg_spool) _next; /* chain of spool instances */ + struct in_addr addr; + uint16_t port; +}; + +/* Nat redirect configuration. */ +struct cfg_redir { + LIST_ENTRY(cfg_redir) _next; /* chain of redir instances */ + uint16_t mode; /* type of redirect mode */ + uint16_t proto; /* protocol: tcp/udp */ + struct in_addr laddr; /* local ip address */ + struct in_addr paddr; /* public ip address */ + struct in_addr raddr; /* remote ip address */ + uint16_t lport; /* local port */ + uint16_t pport; /* public port */ + uint16_t rport; /* remote port */ + uint16_t pport_cnt; /* number of public ports */ + uint16_t rport_cnt; /* number of remote ports */ + struct alias_link **alink; + u_int16_t spool_cnt; /* num of entry in spool chain */ + /* chain of spool instances */ + LIST_HEAD(spool_chain, cfg_spool) spool_chain; +}; + +/* Nat configuration data struct. */ +struct cfg_nat { + /* chain of nat instances */ + LIST_ENTRY(cfg_nat) _next; + int id; /* nat id */ + struct in_addr ip; /* nat ip address */ + struct libalias *lib; /* libalias instance */ + int mode; /* aliasing mode */ + int redir_cnt; /* number of entry in spool chain */ + /* chain of redir instances */ + LIST_HEAD(redir_chain, cfg_redir) redir_chain; + char if_name[IF_NAMESIZE]; /* interface name */ +}; + static eventhandler_tag ifaddr_event_tag; static void @@ -117,11 +154,11 @@ del_redir_spool_cfg(struct cfg_nat *n, struct redir_chain *head) LIST_FOREACH_SAFE(r, head, _next, tmp_r) { num = 1; /* Number of alias_link to delete. */ switch (r->mode) { - case REDIR_PORT: + case NAT44_REDIR_PORT: num = r->pport_cnt; /* FALLTHROUGH */ - case REDIR_ADDR: - case REDIR_PROTO: + case NAT44_REDIR_ADDR: + case NAT44_REDIR_PROTO: /* Delete all libalias redirect entry. */ for (i = 0; i < num; i++) LibAliasRedirectDelete(n->lib, r->alink[i]); @@ -142,27 +179,41 @@ del_redir_spool_cfg(struct cfg_nat *n, struct redir_chain *head) } } -static void +static int add_redir_spool_cfg(char *buf, struct cfg_nat *ptr) { - struct cfg_redir *r, *ser_r; - struct cfg_spool *s, *ser_s; + struct cfg_redir *r; + struct cfg_spool *s; + struct nat44_cfg_redir *ser_r; + struct nat44_cfg_spool *ser_s; + int cnt, off, i; for (cnt = 0, off = 0; cnt < ptr->redir_cnt; cnt++) { - ser_r = (struct cfg_redir *)&buf[off]; - r = malloc(SOF_REDIR, M_IPFW, M_WAITOK | M_ZERO); - memcpy(r, ser_r, SOF_REDIR); + ser_r = (struct nat44_cfg_redir *)&buf[off]; + r = malloc(sizeof(*r), M_IPFW, M_WAITOK | M_ZERO); + r->mode = ser_r->mode; + r->laddr = ser_r->laddr; + r->paddr = ser_r->paddr; + r->raddr = ser_r->raddr; + r->lport = ser_r->lport; + r->pport = ser_r->pport; + r->rport = ser_r->rport; + r->pport_cnt = ser_r->pport_cnt; + r->rport_cnt = ser_r->rport_cnt; + r->proto = ser_r->proto; + r->spool_cnt = ser_r->spool_cnt; + //memcpy(r, ser_r, SOF_REDIR); LIST_INIT(&r->spool_chain); - off += SOF_REDIR; + off += sizeof(struct nat44_cfg_redir); r->alink = malloc(sizeof(struct alias_link *) * r->pport_cnt, M_IPFW, M_WAITOK | M_ZERO); switch (r->mode) { - case REDIR_ADDR: + case NAT44_REDIR_ADDR: r->alink[0] = LibAliasRedirectAddr(ptr->lib, r->laddr, r->paddr); break; - case REDIR_PORT: + case NAT44_REDIR_PORT: for (i = 0 ; i < r->pport_cnt; i++) { /* If remotePort is all ports, set it to 0. */ u_short remotePortCopy = r->rport + i; @@ -178,7 +229,7 @@ add_redir_spool_cfg(char *buf, struct cfg_nat *ptr) } } break; - case REDIR_PROTO: + case NAT44_REDIR_PROTO: r->alink[0] = LibAliasRedirectProto(ptr->lib ,r->laddr, r->raddr, r->paddr, r->proto); break; @@ -186,23 +237,27 @@ add_redir_spool_cfg(char *buf, struct cfg_nat *ptr) printf("unknown redirect mode: %u\n", r->mode); break; } - /* XXX perhaps return an error instead of panic ? */ - if (r->alink[0] == NULL) - panic("LibAliasRedirect* returned NULL"); + if (r->alink[0] == NULL) { + printf("LibAliasRedirect* returned NULL\n"); + return (EINVAL); + } /* LSNAT handling. */ for (i = 0; i < r->spool_cnt; i++) { - ser_s = (struct cfg_spool *)&buf[off]; - s = malloc(SOF_REDIR, M_IPFW, M_WAITOK | M_ZERO); - memcpy(s, ser_s, SOF_SPOOL); + ser_s = (struct nat44_cfg_spool *)&buf[off]; + s = malloc(sizeof(*s), M_IPFW, M_WAITOK | M_ZERO); + s->addr = ser_s->addr; + s->port = ser_s->port; LibAliasAddServer(ptr->lib, r->alink[0], s->addr, htons(s->port)); - off += SOF_SPOOL; + off += sizeof(struct nat44_cfg_spool); /* Hook spool entry. */ LIST_INSERT_HEAD(&r->spool_chain, s, _next); } /* And finally hook this redir entry. */ LIST_INSERT_HEAD(&ptr->redir_chain, r, _next); } + + return (0); } /* @@ -392,60 +447,68 @@ lookup_nat(struct nat_list *l, int nat_id) return res; } -static int -ipfw_nat_cfg(struct sockopt *sopt) +static struct cfg_nat * +lookup_nat_name(struct nat_list *l, char *name) { - struct cfg_nat *cfg, *ptr; - char *buf; - struct ip_fw_chain *chain = &V_layer3_chain; - size_t len; - int gencnt, error = 0; + struct cfg_nat *res; + int id; + char *errptr; - len = sopt->sopt_valsize; - buf = malloc(len, M_TEMP, M_WAITOK | M_ZERO); - if ((error = sooptcopyin(sopt, buf, len, sizeof(struct cfg_nat))) != 0) - goto out; + id = strtol(name, &errptr, 10); + if (id == 0 || *errptr != '\0') + return (NULL); - cfg = (struct cfg_nat *)buf; - if (cfg->id < 0) { - error = EINVAL; - goto out; + LIST_FOREACH(res, l, _next) { + if (res->id == id) + break; } + return (res); +} + +/* IP_FW3 configuration routines */ + +static void +nat44_config(struct ip_fw_chain *chain, struct nat44_cfg_nat *ucfg) +{ + struct cfg_nat *ptr, *tcfg; + int gencnt; /* * Find/create nat rule. */ - IPFW_WLOCK(chain); + IPFW_UH_WLOCK(chain); gencnt = chain->gencnt; - ptr = lookup_nat(&chain->nat, cfg->id); + ptr = lookup_nat_name(&chain->nat, ucfg->name); if (ptr == NULL) { - IPFW_WUNLOCK(chain); + IPFW_UH_WUNLOCK(chain); /* New rule: allocate and init new instance. */ ptr = malloc(sizeof(struct cfg_nat), M_IPFW, M_WAITOK | M_ZERO); ptr->lib = LibAliasInit(NULL); LIST_INIT(&ptr->redir_chain); } else { /* Entry already present: temporarily unhook it. */ + IPFW_WLOCK(chain); LIST_REMOVE(ptr, _next); - flush_nat_ptrs(chain, cfg->id); + flush_nat_ptrs(chain, ptr->id); IPFW_WUNLOCK(chain); + IPFW_UH_WUNLOCK(chain); } /* - * Basic nat configuration. + * Basic nat (re)configuration. */ - ptr->id = cfg->id; + ptr->id = strtol(ucfg->name, NULL, 10); /* * XXX - what if this rule doesn't nat any ip and just * redirect? * do we set aliasaddress to 0.0.0.0? */ - ptr->ip = cfg->ip; - ptr->redir_cnt = cfg->redir_cnt; - ptr->mode = cfg->mode; - LibAliasSetMode(ptr->lib, cfg->mode, ~0); + ptr->ip = ucfg->ip; + ptr->redir_cnt = ucfg->redir_cnt; + ptr->mode = ucfg->mode; + strlcpy(ptr->if_name, ucfg->if_name, sizeof(ptr->if_name)); + LibAliasSetMode(ptr->lib, ptr->mode, ~0); LibAliasSetAddress(ptr->lib, ptr->ip); - memcpy(ptr->if_name, cfg->if_name, IF_NAMESIZE); /* * Redir and LSNAT configuration. @@ -453,16 +516,455 @@ ipfw_nat_cfg(struct sockopt *sopt) /* Delete old cfgs. */ del_redir_spool_cfg(ptr, &ptr->redir_chain); /* Add new entries. */ - add_redir_spool_cfg(&buf[(sizeof(struct cfg_nat))], ptr); + add_redir_spool_cfg((char *)(ucfg + 1), ptr); + IPFW_UH_WLOCK(chain); - IPFW_WLOCK(chain); /* Extra check to avoid race with another ipfw_nat_cfg() */ - if (gencnt != chain->gencnt && - ((cfg = lookup_nat(&chain->nat, ptr->id)) != NULL)) - LIST_REMOVE(cfg, _next); + tcfg = NULL; + if (gencnt != chain->gencnt) + tcfg = lookup_nat_name(&chain->nat, ucfg->name); + IPFW_WLOCK(chain); + if (tcfg != NULL) + LIST_REMOVE(tcfg, _next); LIST_INSERT_HEAD(&chain->nat, ptr, _next); + IPFW_WUNLOCK(chain); chain->gencnt++; + + IPFW_UH_WUNLOCK(chain); + + if (tcfg != NULL) + free(tcfg, M_IPFW); +} + +/* + * Creates/configure nat44 instance + * Data layout (v0)(current): + * Request: [ ipfw_obj_header nat44_cfg_nat .. ] + * + * Returns 0 on success + */ +static int +nat44_cfg(struct ip_fw_chain *chain, ip_fw3_opheader *op3, + struct sockopt_data *sd) +{ + ipfw_obj_header *oh; + struct nat44_cfg_nat *ucfg; + int id; + size_t read; + char *errptr; + + /* Check minimum header size */ + if (sd->valsize < (sizeof(*oh) + sizeof(*ucfg))) + return (EINVAL); + + oh = (ipfw_obj_header *)sd->kbuf; + + /* Basic length checks for TLVs */ + if (oh->ntlv.head.length != sizeof(oh->ntlv)) + return (EINVAL); + + ucfg = (struct nat44_cfg_nat *)(oh + 1); + + /* Check if name is properly terminated and looks like number */ + if (strnlen(ucfg->name, sizeof(ucfg->name)) == sizeof(ucfg->name)) + return (EINVAL); + id = strtol(ucfg->name, &errptr, 10); + if (id == 0 || *errptr != '\0') + return (EINVAL); + + read = sizeof(*oh) + sizeof(*ucfg); + /* Check number of redirs */ + if (sd->valsize < read + ucfg->redir_cnt*sizeof(struct nat44_cfg_redir)) + return (EINVAL); + + nat44_config(chain, ucfg); + return (0); +} + +/* + * Destroys given nat instances. + * Data layout (v0)(current): + * Request: [ ipfw_obj_header ] + * + * Returns 0 on success + */ +static int +nat44_destroy(struct ip_fw_chain *chain, ip_fw3_opheader *op3, + struct sockopt_data *sd) +{ + ipfw_obj_header *oh; + struct cfg_nat *ptr; + ipfw_obj_ntlv *ntlv; + + /* Check minimum header size */ + if (sd->valsize < sizeof(*oh)) + return (EINVAL); + + oh = (ipfw_obj_header *)sd->kbuf; + + /* Basic length checks for TLVs */ + if (oh->ntlv.head.length != sizeof(oh->ntlv)) + return (EINVAL); + + ntlv = &oh->ntlv; + /* Check if name is properly terminated */ + if (strnlen(ntlv->name, sizeof(ntlv->name)) == sizeof(ntlv->name)) + return (EINVAL); + + IPFW_UH_WLOCK(chain); + ptr = lookup_nat_name(&chain->nat, ntlv->name); + if (ptr == NULL) { + IPFW_UH_WUNLOCK(chain); + return (ESRCH); + } + IPFW_WLOCK(chain); + LIST_REMOVE(ptr, _next); + flush_nat_ptrs(chain, ptr->id); IPFW_WUNLOCK(chain); + IPFW_UH_WUNLOCK(chain); + + del_redir_spool_cfg(ptr, &ptr->redir_chain); + LibAliasUninit(ptr->lib); + free(ptr, M_IPFW); + + return (0); +} + +static void +export_nat_cfg(struct cfg_nat *ptr, struct nat44_cfg_nat *ucfg) +{ + + snprintf(ucfg->name, sizeof(ucfg->name), "%d", ptr->id); + ucfg->ip = ptr->ip; + ucfg->redir_cnt = ptr->redir_cnt; + ucfg->mode = ptr->mode; + strlcpy(ucfg->if_name, ptr->if_name, sizeof(ucfg->if_name)); +} + +/* + * Gets config for given nat instance + * Data layout (v0)(current): + * Request: [ ipfw_obj_header nat44_cfg_nat .. ] + * + * Returns 0 on success + */ +static int +nat44_get_cfg(struct ip_fw_chain *chain, ip_fw3_opheader *op3, + struct sockopt_data *sd) +{ + ipfw_obj_header *oh; + struct nat44_cfg_nat *ucfg; + struct cfg_nat *ptr; + struct cfg_redir *r; + struct cfg_spool *s; + struct nat44_cfg_redir *ser_r; + struct nat44_cfg_spool *ser_s; + size_t sz; + + sz = sizeof(*oh) + sizeof(*ucfg); + /* Check minimum header size */ + if (sd->valsize < sz) + return (EINVAL); + + oh = (struct _ipfw_obj_header *)ipfw_get_sopt_header(sd, sz); + + /* Basic length checks for TLVs */ + if (oh->ntlv.head.length != sizeof(oh->ntlv)) + return (EINVAL); + + ucfg = (struct nat44_cfg_nat *)(oh + 1); + + /* Check if name is properly terminated */ + if (strnlen(ucfg->name, sizeof(ucfg->name)) == sizeof(ucfg->name)) + return (EINVAL); + + IPFW_UH_RLOCK(chain); + ptr = lookup_nat_name(&chain->nat, ucfg->name); + if (ptr == NULL) { + IPFW_UH_RUNLOCK(chain); + return (ESRCH); + } + + export_nat_cfg(ptr, ucfg); + + /* Estimate memory amount */ + sz = sizeof(struct nat44_cfg_nat); + LIST_FOREACH(r, &ptr->redir_chain, _next) { + sz += sizeof(struct nat44_cfg_redir); + LIST_FOREACH(s, &r->spool_chain, _next) + sz += sizeof(struct nat44_cfg_spool); + } + + ucfg->size = sz; + if (sd->valsize < sz + sizeof(*oh)) { + + /* + * Submitted buffer size is not enough. + * WE've already filled in @ucfg structure with + * relevant info including size, so we + * can return. Buffer will be flushed automatically. + */ + IPFW_UH_RUNLOCK(chain); + return (ENOMEM); + } + + /* Size OK, let's copy data */ + LIST_FOREACH(r, &ptr->redir_chain, _next) { + ser_r = (struct nat44_cfg_redir *)ipfw_get_sopt_space(sd, + sizeof(*ser_r)); + ser_r->mode = r->mode; + ser_r->laddr = r->laddr; + ser_r->paddr = r->paddr; + ser_r->raddr = r->raddr; + ser_r->lport = r->lport; + ser_r->pport = r->pport; + ser_r->rport = r->rport; + ser_r->pport_cnt = r->pport_cnt; + ser_r->rport_cnt = r->rport_cnt; + ser_r->proto = r->proto; + ser_r->spool_cnt = r->spool_cnt; + + LIST_FOREACH(s, &r->spool_chain, _next) { + ser_s = (struct nat44_cfg_spool *)ipfw_get_sopt_space( + sd, sizeof(*ser_s)); + + ser_s->addr = s->addr; + ser_s->port = s->port; + } + } + + IPFW_UH_RUNLOCK(chain); + + return (0); +} + +/* + * Lists all nat44 instances currently available in kernel. + * Data layout (v0)(current): + * Request: [ ipfw_obj_lheader ] + * Reply: [ ipfw_obj_lheader nat44_cfg_nat x N ] + * + * Returns 0 on success + */ +static int +nat44_list_nat(struct ip_fw_chain *chain, ip_fw3_opheader *op3, + struct sockopt_data *sd) +{ + ipfw_obj_lheader *olh; + struct nat44_cfg_nat *ucfg; + struct cfg_nat *ptr; + int nat_count; + + /* Check minimum header size */ + if (sd->valsize < sizeof(ipfw_obj_lheader)) + return (EINVAL); + + olh = (ipfw_obj_lheader *)ipfw_get_sopt_header(sd, sizeof(*olh)); + IPFW_UH_RLOCK(chain); + nat_count = 0; + LIST_FOREACH(ptr, &chain->nat, _next) + nat_count++; + + olh->count = nat_count; + olh->objsize = sizeof(struct nat44_cfg_nat); + olh->size = sizeof(*olh) + olh->count * olh->objsize; + + if (sd->valsize < olh->size) { + IPFW_UH_RUNLOCK(chain); + return (ENOMEM); + } + + LIST_FOREACH(ptr, &chain->nat, _next) { + ucfg = (struct nat44_cfg_nat *)ipfw_get_sopt_space(sd, + sizeof(*ucfg)); + export_nat_cfg(ptr, ucfg); + } + + IPFW_UH_RUNLOCK(chain); + + return (0); +} + +/* + * Gets log for given nat instance + * Data layout (v0)(current): + * Request: [ ipfw_obj_header nat44_cfg_nat ] + * Reply: [ ipfw_obj_header nat44_cfg_nat LOGBUFFER ] + * + * Returns 0 on success + */ +static int +nat44_get_log(struct ip_fw_chain *chain, ip_fw3_opheader *op3, + struct sockopt_data *sd) +{ + ipfw_obj_header *oh; + struct nat44_cfg_nat *ucfg; + struct cfg_nat *ptr; + void *pbuf; + size_t sz; + + sz = sizeof(*oh) + sizeof(*ucfg); + /* Check minimum header size */ + if (sd->valsize < sz) + return (EINVAL); + + oh = (struct _ipfw_obj_header *)ipfw_get_sopt_header(sd, sz); + + /* Basic length checks for TLVs */ + if (oh->ntlv.head.length != sizeof(oh->ntlv)) + return (EINVAL); + + ucfg = (struct nat44_cfg_nat *)(oh + 1); + + /* Check if name is properly terminated */ + if (strnlen(ucfg->name, sizeof(ucfg->name)) == sizeof(ucfg->name)) + return (EINVAL); + + IPFW_UH_RLOCK(chain); + ptr = lookup_nat_name(&chain->nat, ucfg->name); + if (ptr == NULL) { + IPFW_UH_RUNLOCK(chain); + return (ESRCH); + } + + if (ptr->lib->logDesc == NULL) { + IPFW_UH_RUNLOCK(chain); + return (ENOENT); + } + + export_nat_cfg(ptr, ucfg); + + /* Estimate memory amount */ + ucfg->size = sizeof(struct nat44_cfg_nat) + LIBALIAS_BUF_SIZE; + if (sd->valsize < sz + sizeof(*oh)) { + + /* + * Submitted buffer size is not enough. + * WE've already filled in @ucfg structure with + * relevant info including size, so we + * can return. Buffer will be flushed automatically. + */ + IPFW_UH_RUNLOCK(chain); + return (ENOMEM); + } + + pbuf = (void *)ipfw_get_sopt_space(sd, LIBALIAS_BUF_SIZE); + memcpy(pbuf, ptr->lib->logDesc, LIBALIAS_BUF_SIZE); + + IPFW_UH_RUNLOCK(chain); + + return (0); +} + +static struct ipfw_sopt_handler scodes[] = { + { IP_FW_NAT44_XCONFIG, 0, HDIR_SET, nat44_cfg }, + { IP_FW_NAT44_DESTROY, 0, HDIR_SET, nat44_destroy }, + { IP_FW_NAT44_XGETCONFIG, 0, HDIR_GET, nat44_get_cfg }, + { IP_FW_NAT44_LIST_NAT, 0, HDIR_GET, nat44_list_nat }, + { IP_FW_NAT44_XGETLOG, 0, HDIR_GET, nat44_get_log }, +}; + + +/* + * Legacy configuration routines + */ + +struct cfg_spool_legacy { + LIST_ENTRY(cfg_spool_legacy) _next; + struct in_addr addr; + u_short port; +}; + +struct cfg_redir_legacy { + LIST_ENTRY(cfg_redir) _next; + u_int16_t mode; + struct in_addr laddr; + struct in_addr paddr; + struct in_addr raddr; + u_short lport; + u_short pport; + u_short rport; + u_short pport_cnt; + u_short rport_cnt; + int proto; + struct alias_link **alink; + u_int16_t spool_cnt; + LIST_HEAD(, cfg_spool_legacy) spool_chain; +}; + +struct cfg_nat_legacy { + LIST_ENTRY(cfg_nat_legacy) _next; + int id; + struct in_addr ip; + char if_name[IF_NAMESIZE]; + int mode; + struct libalias *lib; + int redir_cnt; + LIST_HEAD(, cfg_redir_legacy) redir_chain; +}; + +static int +ipfw_nat_cfg(struct sockopt *sopt) +{ + struct cfg_nat_legacy *cfg; + struct nat44_cfg_nat *ucfg; + struct cfg_redir_legacy *rdir; + struct nat44_cfg_redir *urdir; + char *buf; + size_t len, len2; + int error, i; + + len = sopt->sopt_valsize; + len2 = len + 128; + + /* + * Allocate 2x buffer to store converted structures. + * new redir_cfg has shrinked, so we're sure that + * new buffer size is enough. + */ + buf = malloc(roundup2(len, 8) + len2, M_TEMP, M_WAITOK | M_ZERO); + error = sooptcopyin(sopt, buf, len, sizeof(struct cfg_nat_legacy)); + if (error != 0) + goto out; + + cfg = (struct cfg_nat_legacy *)buf; + if (cfg->id < 0) { + error = EINVAL; + goto out; + } + + ucfg = (struct nat44_cfg_nat *)&buf[roundup2(len, 8)]; + snprintf(ucfg->name, sizeof(ucfg->name), "%d", cfg->id); + strlcpy(ucfg->if_name, cfg->if_name, sizeof(ucfg->if_name)); + ucfg->ip = cfg->ip; + ucfg->mode = cfg->mode; + ucfg->redir_cnt = cfg->redir_cnt; + + if (len < sizeof(*cfg) + cfg->redir_cnt * sizeof(*rdir)) { + error = EINVAL; + goto out; + } + + urdir = (struct nat44_cfg_redir *)(ucfg + 1); + rdir = (struct cfg_redir_legacy *)(cfg + 1); + for (i = 0; i < cfg->redir_cnt; i++) { + urdir->mode = rdir->mode; + urdir->laddr = rdir->laddr; + urdir->paddr = rdir->paddr; + urdir->raddr = rdir->raddr; + urdir->lport = rdir->lport; + urdir->pport = rdir->pport; + urdir->rport = rdir->rport; + urdir->pport_cnt = rdir->pport_cnt; + urdir->rport_cnt = rdir->rport_cnt; + urdir->proto = rdir->proto; + urdir->spool_cnt = rdir->spool_cnt; + + urdir++; + rdir++; + } + + nat44_config(&V_layer3_chain, ucfg); out: free(buf, M_TEMP); @@ -478,15 +980,17 @@ ipfw_nat_del(struct sockopt *sopt) sooptcopyin(sopt, &i, sizeof i, sizeof i); /* XXX validate i */ - IPFW_WLOCK(chain); + IPFW_UH_WLOCK(chain); ptr = lookup_nat(&chain->nat, i); if (ptr == NULL) { - IPFW_WUNLOCK(chain); + IPFW_UH_WUNLOCK(chain); return (EINVAL); } + IPFW_WLOCK(chain); LIST_REMOVE(ptr, _next); flush_nat_ptrs(chain, i); IPFW_WUNLOCK(chain); + IPFW_UH_WUNLOCK(chain); del_redir_spool_cfg(ptr, &ptr->redir_chain); LibAliasUninit(ptr->lib); free(ptr, M_IPFW); @@ -498,28 +1002,31 @@ ipfw_nat_get_cfg(struct sockopt *sopt) { struct ip_fw_chain *chain = &V_layer3_chain; struct cfg_nat *n; + struct cfg_nat_legacy *ucfg; struct cfg_redir *r; struct cfg_spool *s; + struct cfg_redir_legacy *ser_r; + struct cfg_spool_legacy *ser_s; char *data; int gencnt, nat_cnt, len, error; nat_cnt = 0; len = sizeof(nat_cnt); - IPFW_RLOCK(chain); + IPFW_UH_RLOCK(chain); retry: gencnt = chain->gencnt; /* Estimate memory amount */ LIST_FOREACH(n, &chain->nat, _next) { nat_cnt++; - len += sizeof(struct cfg_nat); + len += sizeof(struct cfg_nat_legacy); LIST_FOREACH(r, &n->redir_chain, _next) { - len += sizeof(struct cfg_redir); + len += sizeof(struct cfg_redir_legacy); LIST_FOREACH(s, &r->spool_chain, _next) - len += sizeof(struct cfg_spool); + len += sizeof(struct cfg_spool_legacy); } } - IPFW_RUNLOCK(chain); + IPFW_UH_RUNLOCK(chain); data = malloc(len, M_TEMP, M_WAITOK | M_ZERO); bcopy(&nat_cnt, data, sizeof(nat_cnt)); @@ -527,25 +1034,43 @@ retry: nat_cnt = 0; len = sizeof(nat_cnt); - IPFW_RLOCK(chain); + IPFW_UH_RLOCK(chain); if (gencnt != chain->gencnt) { free(data, M_TEMP); goto retry; } /* Serialize all the data. */ LIST_FOREACH(n, &chain->nat, _next) { - bcopy(n, &data[len], sizeof(struct cfg_nat)); - len += sizeof(struct cfg_nat); + ucfg = (struct cfg_nat_legacy *)&data[len]; + ucfg->id = n->id; + ucfg->ip = n->ip; + ucfg->redir_cnt = n->redir_cnt; + ucfg->mode = n->mode; + strlcpy(ucfg->if_name, n->if_name, sizeof(ucfg->if_name)); + len += sizeof(struct cfg_nat_legacy); LIST_FOREACH(r, &n->redir_chain, _next) { - bcopy(r, &data[len], sizeof(struct cfg_redir)); - len += sizeof(struct cfg_redir); + ser_r = (struct cfg_redir_legacy *)&data[len]; + ser_r->mode = r->mode; + ser_r->laddr = r->laddr; + ser_r->paddr = r->paddr; + ser_r->raddr = r->raddr; + ser_r->lport = r->lport; + ser_r->pport = r->pport; + ser_r->rport = r->rport; + ser_r->pport_cnt = r->pport_cnt; + ser_r->rport_cnt = r->rport_cnt; + ser_r->proto = r->proto; + ser_r->spool_cnt = r->spool_cnt; + len += sizeof(struct cfg_redir_legacy); LIST_FOREACH(s, &r->spool_chain, _next) { - bcopy(s, &data[len], sizeof(struct cfg_spool)); - len += sizeof(struct cfg_spool); + ser_s = (struct cfg_spool_legacy *)&data[len]; + ser_s->addr = s->addr; + ser_s->port = s->port; + len += sizeof(struct cfg_spool_legacy); } } } - IPFW_RUNLOCK(chain); + IPFW_UH_RUNLOCK(chain); error = sooptcopyout(sopt, data, len); free(data, M_TEMP); @@ -631,6 +1156,7 @@ ipfw_nat_init(void) ipfw_nat_del_ptr = ipfw_nat_del; ipfw_nat_get_cfg_ptr = ipfw_nat_get_cfg; ipfw_nat_get_log_ptr = ipfw_nat_get_log; + IPFW_ADD_SOPT_HANDLER(1, scodes); ifaddr_event_tag = EVENTHANDLER_REGISTER(ifaddr_event, ifaddr_change, NULL, EVENTHANDLER_PRI_ANY); @@ -642,6 +1168,7 @@ ipfw_nat_destroy(void) EVENTHANDLER_DEREGISTER(ifaddr_event, ifaddr_event_tag); /* deregister ipfw_nat */ + IPFW_DEL_SOPT_HANDLER(1, scodes); ipfw_nat_ptr = NULL; lookup_nat_ptr = NULL; ipfw_nat_cfg_ptr = NULL; |