Changeset View
Standalone View
sys/netpfil/ipfw/ip_fw_nat.c
Show First 20 Lines • Show All 69 Lines • ▼ Show 20 Lines | struct cfg_redir { | ||||
struct in_addr laddr; /* local ip address */ | struct in_addr laddr; /* local ip address */ | ||||
struct in_addr paddr; /* public ip address */ | struct in_addr paddr; /* public ip address */ | ||||
struct in_addr raddr; /* remote ip address */ | struct in_addr raddr; /* remote ip address */ | ||||
uint16_t lport; /* local port */ | uint16_t lport; /* local port */ | ||||
uint16_t pport; /* public port */ | uint16_t pport; /* public port */ | ||||
uint16_t rport; /* remote port */ | uint16_t rport; /* remote port */ | ||||
uint16_t pport_cnt; /* number of public ports */ | uint16_t pport_cnt; /* number of public ports */ | ||||
uint16_t rport_cnt; /* number of remote ports */ | uint16_t rport_cnt; /* number of remote ports */ | ||||
struct alias_link **alink; | struct alias_link **alink; | ||||
u_int16_t spool_cnt; /* num of entry in spool chain */ | u_int16_t spool_cnt; /* num of entry in spool chain */ | ||||
/* chain of spool instances */ | /* chain of spool instances */ | ||||
LIST_HEAD(spool_chain, cfg_spool) spool_chain; | LIST_HEAD(spool_chain, cfg_spool) spool_chain; | ||||
}; | }; | ||||
/* Nat configuration data struct. */ | /* Nat configuration data struct. */ | ||||
struct cfg_nat { | struct cfg_nat { | ||||
/* chain of nat instances */ | /* chain of nat instances */ | ||||
LIST_ENTRY(cfg_nat) _next; | LIST_ENTRY(cfg_nat) _next; | ||||
int id; /* nat id */ | uint16_t id; /* nat id */ | ||||
struct in_addr ip; /* nat ip address */ | struct in_addr ip; /* nat ip address */ | ||||
struct libalias *lib; /* libalias instance */ | struct libalias *lib; /* libalias instance */ | ||||
int mode; /* aliasing mode */ | int mode; /* aliasing mode */ | ||||
int redir_cnt; /* number of entry in spool chain */ | int redir_cnt; /* number of entry in spool chain */ | ||||
/* chain of redir instances */ | /* chain of redir instances */ | ||||
LIST_HEAD(redir_chain, cfg_redir) redir_chain; | LIST_HEAD(redir_chain, cfg_redir) redir_chain; | ||||
char if_name[IF_NAMESIZE]; /* interface name */ | char if_name[IF_NAMESIZE]; /* interface name */ | ||||
u_short alias_port_lo; /* low range for port aliasing */ | u_short alias_port_lo; /* low range for port aliasing */ | ||||
u_short alias_port_hi; /* high range for port aliasing */ | u_short alias_port_hi; /* high range for port aliasing */ | ||||
}; | }; | ||||
/* Nat managment. */ | |||||
struct nat_priv { | |||||
melifaro: Is it `nat_list` or `nat_mgmt` or `nat_ctl` or `nat_priv` or ? | |||||
LIST_HEAD(cfg_list, cfg_nat) list; /* table of lists of nat entries */ | |||||
Done Inline Actionsalso: why all? Both list and array have all items. Maybe it would be better to consider something like nat_list, nat_idxmap, nat_idxsize etc? melifaro: also: why `all`? Both list and array have all items.
Maybe it would be better to consider… | |||||
Done Inline ActionsNegative values is not only an issue with "size" but also with "id". donner: Negative values is not only an issue with "size" but also with "id".
This is far more serious… | |||||
struct cfg_nat **idxmap; /* map id to instance */ | |||||
uint16_t idxsize; /* size of of nat lookup table */ | |||||
uint32_t gencnt; /* NAT configuration change count */ | |||||
}; | |||||
static eventhandler_tag ifaddr_event_tag; | static eventhandler_tag ifaddr_event_tag; | ||||
static void | static void | ||||
ifaddr_change(void *arg __unused, struct ifnet *ifp) | ifaddr_change(void *arg __unused, struct ifnet *ifp) | ||||
{ | { | ||||
struct cfg_nat *ptr; | struct cfg_nat *ptr; | ||||
struct ifaddr *ifa; | struct ifaddr *ifa; | ||||
struct ip_fw_chain *chain; | struct ip_fw_chain *chain; | ||||
KASSERT(curvnet == ifp->if_vnet, | KASSERT(curvnet == ifp->if_vnet, | ||||
("curvnet(%p) differs from iface vnet(%p)", curvnet, ifp->if_vnet)); | ("curvnet(%p) differs from iface vnet(%p)", curvnet, ifp->if_vnet)); | ||||
if (V_ipfw_vnet_ready == 0 || V_ipfw_nat_ready == 0) | if (V_ipfw_vnet_ready == 0 || V_ipfw_nat_ready == 0) | ||||
return; | return; | ||||
chain = &V_layer3_chain; | chain = &V_layer3_chain; | ||||
IPFW_UH_WLOCK(chain); | IPFW_UH_WLOCK(chain); | ||||
/* Check every nat entry... */ | /* Check every nat entry... */ | ||||
LIST_FOREACH(ptr, &chain->nat, _next) { | LIST_FOREACH(ptr, &chain->nat->list, _next) { | ||||
struct epoch_tracker et; | struct epoch_tracker et; | ||||
/* ...using nic 'ifp->if_xname' as dynamic alias address. */ | /* ...using nic 'ifp->if_xname' as dynamic alias address. */ | ||||
if (strncmp(ptr->if_name, ifp->if_xname, IF_NAMESIZE) != 0) | if (strncmp(ptr->if_name, ifp->if_xname, IF_NAMESIZE) != 0) | ||||
continue; | continue; | ||||
NET_EPOCH_ENTER(et); | NET_EPOCH_ENTER(et); | ||||
CK_STAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) { | CK_STAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) { | ||||
if (ifa->ifa_addr == NULL) | if (ifa->ifa_addr == NULL) | ||||
continue; | continue; | ||||
if (ifa->ifa_addr->sa_family != AF_INET) | if (ifa->ifa_addr->sa_family != AF_INET) | ||||
continue; | continue; | ||||
IPFW_WLOCK(chain); | IPFW_WLOCK(chain); | ||||
ptr->ip = ((struct sockaddr_in *) | ptr->ip = ((struct sockaddr_in *) | ||||
(ifa->ifa_addr))->sin_addr; | (ifa->ifa_addr))->sin_addr; | ||||
LibAliasSetAddress(ptr->lib, ptr->ip); | LibAliasSetAddress(ptr->lib, ptr->ip); | ||||
IPFW_WUNLOCK(chain); | IPFW_WUNLOCK(chain); | ||||
} | } | ||||
NET_EPOCH_EXIT(et); | NET_EPOCH_EXIT(et); | ||||
} | } | ||||
IPFW_UH_WUNLOCK(chain); | IPFW_UH_WUNLOCK(chain); | ||||
} | } | ||||
/* | |||||
* delete the pointers for nat entry ix, or all of them if ix < 0 | |||||
*/ | |||||
static void | static void | ||||
flush_nat_ptrs(struct ip_fw_chain *chain, const int ix) | |||||
{ | |||||
ipfw_insn_nat *cmd; | |||||
int i; | |||||
IPFW_WLOCK_ASSERT(chain); | |||||
for (i = 0; i < chain->n_rules; i++) { | |||||
cmd = (ipfw_insn_nat *)ipfw_get_action(chain->map[i]); | |||||
if (cmd->o.opcode == O_NAT && cmd->nat != NULL && | |||||
(ix < 0 || cmd->nat->id == ix)) | |||||
cmd->nat = NULL; | |||||
} | |||||
} | |||||
static void | |||||
del_redir_spool_cfg(struct cfg_nat *n, struct redir_chain *head) | del_redir_spool_cfg(struct cfg_nat *n, struct redir_chain *head) | ||||
{ | { | ||||
struct cfg_redir *r, *tmp_r; | struct cfg_redir *r, *tmp_r; | ||||
struct cfg_spool *s, *tmp_s; | struct cfg_spool *s, *tmp_s; | ||||
int i, num; | int i, num; | ||||
LIST_FOREACH_SAFE(r, head, _next, tmp_r) { | LIST_FOREACH_SAFE(r, head, _next, tmp_r) { | ||||
num = 1; /* Number of alias_link to delete. */ | num = 1; /* Number of alias_link to delete. */ | ||||
▲ Show 20 Lines • Show All 186 Lines • ▼ Show 20 Lines | if (args->flags & IPFW_ARGS_IN) { | ||||
args->m = mcl; | args->m = mcl; | ||||
return (IP_FW_NAT); | return (IP_FW_NAT); | ||||
} | } | ||||
found = 0; | found = 0; | ||||
chain = &V_layer3_chain; | chain = &V_layer3_chain; | ||||
IPFW_RLOCK_ASSERT(chain); | IPFW_RLOCK_ASSERT(chain); | ||||
/* Check every nat entry... */ | /* Check every nat entry... */ | ||||
LIST_FOREACH(t, &chain->nat, _next) { | LIST_FOREACH(t, &chain->nat->list, _next) { | ||||
if ((t->mode & PKT_ALIAS_SKIP_GLOBAL) != 0) | if ((t->mode & PKT_ALIAS_SKIP_GLOBAL) != 0) | ||||
continue; | continue; | ||||
retval = LibAliasOutTry(t->lib, c, | retval = LibAliasOutTry(t->lib, c, | ||||
mcl->m_len + M_TRAILINGSPACE(mcl), 0); | mcl->m_len + M_TRAILINGSPACE(mcl), 0); | ||||
if (retval == PKT_ALIAS_OK) { | if (retval == PKT_ALIAS_OK) { | ||||
/* Nat instance recognises state */ | /* Nat instance recognises state */ | ||||
found = 1; | found = 1; | ||||
break; | break; | ||||
▲ Show 20 Lines • Show All 84 Lines • ▼ Show 20 Lines | if ((mcl->m_pkthdr.csum_flags & CSUM_DELAY_DATA) == 0) { | ||||
mcl->m_pkthdr.csum_flags &= ~CSUM_DELAY_DATA; | mcl->m_pkthdr.csum_flags &= ~CSUM_DELAY_DATA; | ||||
} | } | ||||
} | } | ||||
args->m = mcl; | args->m = mcl; | ||||
return (IP_FW_NAT); | return (IP_FW_NAT); | ||||
} | } | ||||
static struct cfg_nat * | static struct cfg_nat * | ||||
lookup_nat(struct nat_list *l, int nat_id) | lookup_nat(struct nat_priv *l, uint16_t nat_id) | ||||
{ | { | ||||
struct cfg_nat *res; | IPFW_UH_LOCK_ASSERT(&V_layer3_chain); | ||||
if (nat_id > l->idxsize || nat_id == 0) | |||||
return NULL; | |||||
LIST_FOREACH(res, l, _next) { | return l->idxmap[nat_id-1]; | ||||
if (res->id == nat_id) | |||||
break; | |||||
} | } | ||||
return res; | |||||
} | |||||
static struct cfg_nat * | static uint16_t | ||||
lookup_nat_name(struct nat_list *l, char *name) | validate_nat_name(char *name) | ||||
{ | { | ||||
struct cfg_nat *res; | |||||
int id; | int id; | ||||
char *errptr; | char *errptr; | ||||
id = strtol(name, &errptr, 10); | id = strtol(name, &errptr, 10); | ||||
if (id == 0 || *errptr != '\0') | if (id <= 0 || id >= 65535 || *errptr != '\0') | ||||
return 0; | |||||
return id; | |||||
} | |||||
static struct cfg_nat * | |||||
lookup_nat_name(struct nat_priv *l, char *name) | |||||
{ | |||||
uint16_t id; | |||||
id = validate_nat_name(name); | |||||
if (id == 0) | |||||
return (NULL); | return (NULL); | ||||
LIST_FOREACH(res, l, _next) { | return lookup_nat(l, id); | ||||
if (res->id == id) | |||||
break; | |||||
} | } | ||||
return (res); | |||||
Done Inline ActionsAvoid code duplication donner: Avoid code duplication | |||||
} | |||||
/* IP_FW3 configuration routines */ | /* IP_FW3 configuration routines */ | ||||
static void | static void | ||||
nat44_config(struct ip_fw_chain *chain, struct nat44_cfg_nat *ucfg) | nat44_config(struct ip_fw_chain *chain, struct nat44_cfg_nat *ucfg, uint16_t id) | ||||
{ | { | ||||
struct cfg_nat *ptr, *tcfg; | struct cfg_nat *ptr, *tcfg, **tmap; | ||||
int gencnt; | int gencnt, resize_to; | ||||
tmap = NULL; | |||||
resize_to = 0; | |||||
/* | /* | ||||
* Find/create nat rule. | * Find/create nat rule. | ||||
*/ | */ | ||||
IPFW_UH_WLOCK(chain); | IPFW_UH_WLOCK(chain); | ||||
gencnt = chain->gencnt; | gencnt = chain->nat->gencnt; | ||||
ptr = lookup_nat_name(&chain->nat, ucfg->name); | ptr = lookup_nat(chain->nat, id); | ||||
if (ptr == NULL) { | if (ptr == NULL) { | ||||
/* enough space to add entry? Test as long as we have the lock */ | |||||
if (id > chain->nat->idxsize) | |||||
resize_to = id; | |||||
IPFW_UH_WUNLOCK(chain); | IPFW_UH_WUNLOCK(chain); | ||||
/* New rule: allocate and init new instance. */ | /* New rule: allocate and init new instance. */ | ||||
ptr = malloc(sizeof(struct cfg_nat), M_IPFW, M_WAITOK | M_ZERO); | ptr = malloc(sizeof(struct cfg_nat), M_IPFW, M_WAITOK | M_ZERO); | ||||
ptr->id = id; | |||||
ptr->lib = LibAliasInit(NULL); | ptr->lib = LibAliasInit(NULL); | ||||
LIST_INIT(&ptr->redir_chain); | LIST_INIT(&ptr->redir_chain); | ||||
/* allocate new table without lockw, syscall can block */ | |||||
if (resize_to) | |||||
tmap = malloc(resize_to * sizeof(*tmap), | |||||
M_IPFW, M_WAITOK | M_ZERO); | |||||
} else { | } else { | ||||
/* Entry already present: temporarily unhook it. */ | /* Entry already present: temporarily unhook it. */ | ||||
IPFW_WLOCK(chain); | IPFW_WLOCK(chain); | ||||
LIST_REMOVE(ptr, _next); | LIST_REMOVE(ptr, _next); | ||||
flush_nat_ptrs(chain, ptr->id); | chain->nat->idxmap[ptr->id - 1] = NULL; | ||||
IPFW_WUNLOCK(chain); | IPFW_WUNLOCK(chain); | ||||
IPFW_UH_WUNLOCK(chain); | IPFW_UH_WUNLOCK(chain); | ||||
} | } | ||||
/* | /* | ||||
* Basic nat (re)configuration. | * Basic nat (re)configuration. | ||||
*/ | */ | ||||
ptr->id = strtol(ucfg->name, NULL, 10); | |||||
/* | /* | ||||
* XXX - what if this rule doesn't nat any ip and just | * XXX - what if this rule doesn't nat any ip and just | ||||
* redirect? | * redirect? | ||||
* do we set aliasaddress to 0.0.0.0? | * do we set aliasaddress to 0.0.0.0? | ||||
*/ | */ | ||||
ptr->ip = ucfg->ip; | ptr->ip = ucfg->ip; | ||||
ptr->redir_cnt = ucfg->redir_cnt; | ptr->redir_cnt = ucfg->redir_cnt; | ||||
ptr->mode = ucfg->mode; | ptr->mode = ucfg->mode; | ||||
ptr->alias_port_lo = ucfg->alias_port_lo; | ptr->alias_port_lo = ucfg->alias_port_lo; | ||||
ptr->alias_port_hi = ucfg->alias_port_hi; | ptr->alias_port_hi = ucfg->alias_port_hi; | ||||
strlcpy(ptr->if_name, ucfg->if_name, sizeof(ptr->if_name)); | strlcpy(ptr->if_name, ucfg->if_name, sizeof(ptr->if_name)); | ||||
LibAliasSetMode(ptr->lib, ptr->mode, ~0); | LibAliasSetMode(ptr->lib, ptr->mode, ~0); | ||||
LibAliasSetAddress(ptr->lib, ptr->ip); | LibAliasSetAddress(ptr->lib, ptr->ip); | ||||
LibAliasSetAliasPortRange(ptr->lib, ptr->alias_port_lo, ptr->alias_port_hi); | LibAliasSetAliasPortRange(ptr->lib, ptr->alias_port_lo, ptr->alias_port_hi); | ||||
/* | /* | ||||
* Redir and LSNAT configuration. | * Redir and LSNAT configuration. | ||||
*/ | */ | ||||
/* Delete old cfgs. */ | /* Delete old cfgs. */ | ||||
del_redir_spool_cfg(ptr, &ptr->redir_chain); | del_redir_spool_cfg(ptr, &ptr->redir_chain); | ||||
/* Add new entries. */ | /* Add new entries. */ | ||||
add_redir_spool_cfg((char *)(ucfg + 1), ptr); | add_redir_spool_cfg((char *)(ucfg + 1), ptr); | ||||
IPFW_UH_WLOCK(chain); | IPFW_UH_WLOCK(chain); | ||||
/* Extra check to avoid race with another ipfw_nat_cfg() */ | /* Extra check to avoid race with another ipfw_nat_cfg() */ | ||||
tcfg = NULL; | tcfg = NULL; | ||||
if (gencnt != chain->gencnt) | if (gencnt != chain->nat->gencnt) | ||||
tcfg = lookup_nat_name(&chain->nat, ucfg->name); | tcfg = lookup_nat(chain->nat, id); | ||||
IPFW_WLOCK(chain); | IPFW_WLOCK(chain); | ||||
/* (our) latest config wins */ | |||||
if (tcfg != NULL) | if (tcfg != NULL) | ||||
LIST_REMOVE(tcfg, _next); | LIST_REMOVE(tcfg, _next); | ||||
LIST_INSERT_HEAD(&chain->nat, ptr, _next); | |||||
/* ensure a large enough idxmap */ | |||||
if (resize_to > chain->nat->idxsize) { | |||||
struct cfg_nat *known, **swap; | |||||
/* rebuild (usually) sparse map from list */ | |||||
Done Inline ActionsYou cannot do M_WAITOK while holding non-sleepable lock. To be a bit more specific: nat list is not used in fast path anymore, hence there is no need to acquire IPFW_WLOCK for it, UH_WLOCK is sufficient. Some generic comments: you almost never want to panic in case of memory allocation failure. Typically you either retry a couple of times or return an error to the caller. melifaro: You cannot do M_WAITOK while holding non-sleepable lock.
Also, as this is configuration change… | |||||
Done Inline ActionsM_ZERO is a really good idea. I just had a beautiful panic by configuring an intermediate (go backwards below the largest nat id) unassigend instance. Thank you for the comment. Reading it solved my issue as it happend. donner: M_ZERO is a really good idea. I just had a beautiful panic by configuring an intermediate (go… | |||||
LIST_FOREACH(known, &chain->nat->list, _next) | |||||
tmap[known->id - 1] = known; | |||||
/* swap pointers, free old later */ | |||||
swap = chain->nat->idxmap; | |||||
chain->nat->idxmap = tmap; | |||||
tmap = swap; | |||||
/* new array in place */ | |||||
chain->nat->idxsize = resize_to; | |||||
} | |||||
/* add new element */ | |||||
LIST_INSERT_HEAD(&chain->nat->list, ptr, _next); | |||||
chain->nat->idxmap[ptr->id - 1] = ptr; | |||||
IPFW_WUNLOCK(chain); | IPFW_WUNLOCK(chain); | ||||
chain->gencnt++; | |||||
/* inform other, that something changed */ | |||||
chain->nat->gencnt++; | |||||
IPFW_UH_WUNLOCK(chain); | IPFW_UH_WUNLOCK(chain); | ||||
if (tcfg != NULL) | if (tcfg != NULL) | ||||
free_nat_instance(ptr); | free_nat_instance(ptr); | ||||
if (tmap != NULL) | |||||
free(tmap, M_IPFW); | |||||
} | } | ||||
/* | /* | ||||
* Creates/configure nat44 instance | * Creates/configure nat44 instance | ||||
* Data layout (v0)(current): | * Data layout (v0)(current): | ||||
* Request: [ ipfw_obj_header nat44_cfg_nat .. ] | * Request: [ ipfw_obj_header nat44_cfg_nat .. ] | ||||
* | * | ||||
* Returns 0 on success | * Returns 0 on success | ||||
*/ | */ | ||||
static int | static int | ||||
nat44_cfg(struct ip_fw_chain *chain, ip_fw3_opheader *op3, | nat44_cfg(struct ip_fw_chain *chain, ip_fw3_opheader *op3, | ||||
struct sockopt_data *sd) | struct sockopt_data *sd) | ||||
{ | { | ||||
ipfw_obj_header *oh; | ipfw_obj_header *oh; | ||||
struct nat44_cfg_nat *ucfg; | struct nat44_cfg_nat *ucfg; | ||||
int id; | uint16_t id; | ||||
size_t read; | size_t read; | ||||
char *errptr; | |||||
/* Check minimum header size */ | /* Check minimum header size */ | ||||
if (sd->valsize < (sizeof(*oh) + sizeof(*ucfg))) | if (sd->valsize < (sizeof(*oh) + sizeof(*ucfg))) | ||||
return (EINVAL); | return (EINVAL); | ||||
oh = (ipfw_obj_header *)sd->kbuf; | oh = (ipfw_obj_header *)sd->kbuf; | ||||
/* Basic length checks for TLVs */ | /* Basic length checks for TLVs */ | ||||
if (oh->ntlv.head.length != sizeof(oh->ntlv)) | if (oh->ntlv.head.length != sizeof(oh->ntlv)) | ||||
return (EINVAL); | return (EINVAL); | ||||
ucfg = (struct nat44_cfg_nat *)(oh + 1); | ucfg = (struct nat44_cfg_nat *)(oh + 1); | ||||
/* Check if name is properly terminated and looks like number */ | /* Check if name is properly terminated and looks like number */ | ||||
if (strnlen(ucfg->name, sizeof(ucfg->name)) == sizeof(ucfg->name)) | id = validate_nat_name(ucfg->name); | ||||
if(id == 0) | |||||
return (EINVAL); | return (EINVAL); | ||||
id = strtol(ucfg->name, &errptr, 10); | |||||
if (id == 0 || *errptr != '\0') | |||||
return (EINVAL); | |||||
read = sizeof(*oh) + sizeof(*ucfg); | read = sizeof(*oh) + sizeof(*ucfg); | ||||
/* Check number of redirs */ | /* Check number of redirs */ | ||||
if (sd->valsize < read + ucfg->redir_cnt*sizeof(struct nat44_cfg_redir)) | if (sd->valsize < read + ucfg->redir_cnt*sizeof(struct nat44_cfg_redir)) | ||||
return (EINVAL); | return (EINVAL); | ||||
nat44_config(chain, ucfg); | nat44_config(chain, ucfg, id); | ||||
return (0); | return (0); | ||||
} | } | ||||
/* | /* | ||||
* Destroys given nat instances. | * Destroys given nat instances. | ||||
* Data layout (v0)(current): | * Data layout (v0)(current): | ||||
* Request: [ ipfw_obj_header ] | * Request: [ ipfw_obj_header ] | ||||
* | * | ||||
Show All 18 Lines | if (oh->ntlv.head.length != sizeof(oh->ntlv)) | ||||
return (EINVAL); | return (EINVAL); | ||||
ntlv = &oh->ntlv; | ntlv = &oh->ntlv; | ||||
/* Check if name is properly terminated */ | /* Check if name is properly terminated */ | ||||
if (strnlen(ntlv->name, sizeof(ntlv->name)) == sizeof(ntlv->name)) | if (strnlen(ntlv->name, sizeof(ntlv->name)) == sizeof(ntlv->name)) | ||||
return (EINVAL); | return (EINVAL); | ||||
IPFW_UH_WLOCK(chain); | IPFW_UH_WLOCK(chain); | ||||
ptr = lookup_nat_name(&chain->nat, ntlv->name); | ptr = lookup_nat_name(chain->nat, ntlv->name); | ||||
if (ptr == NULL) { | if (ptr == NULL) { | ||||
IPFW_UH_WUNLOCK(chain); | IPFW_UH_WUNLOCK(chain); | ||||
return (ESRCH); | return (ESRCH); | ||||
} | } | ||||
IPFW_WLOCK(chain); | IPFW_WLOCK(chain); | ||||
LIST_REMOVE(ptr, _next); | LIST_REMOVE(ptr, _next); | ||||
flush_nat_ptrs(chain, ptr->id); | chain->nat->idxmap[ptr->id - 1] = NULL; | ||||
IPFW_WUNLOCK(chain); | IPFW_WUNLOCK(chain); | ||||
IPFW_UH_WUNLOCK(chain); | IPFW_UH_WUNLOCK(chain); | ||||
free_nat_instance(ptr); | free_nat_instance(ptr); | ||||
return (0); | return (0); | ||||
} | } | ||||
▲ Show 20 Lines • Show All 43 Lines • ▼ Show 20 Lines | nat44_get_cfg(struct ip_fw_chain *chain, ip_fw3_opheader *op3, | ||||
ucfg = (struct nat44_cfg_nat *)(oh + 1); | ucfg = (struct nat44_cfg_nat *)(oh + 1); | ||||
/* Check if name is properly terminated */ | /* Check if name is properly terminated */ | ||||
if (strnlen(ucfg->name, sizeof(ucfg->name)) == sizeof(ucfg->name)) | if (strnlen(ucfg->name, sizeof(ucfg->name)) == sizeof(ucfg->name)) | ||||
return (EINVAL); | return (EINVAL); | ||||
IPFW_UH_RLOCK(chain); | IPFW_UH_RLOCK(chain); | ||||
ptr = lookup_nat_name(&chain->nat, ucfg->name); | ptr = lookup_nat_name(chain->nat, ucfg->name); | ||||
if (ptr == NULL) { | if (ptr == NULL) { | ||||
IPFW_UH_RUNLOCK(chain); | IPFW_UH_RUNLOCK(chain); | ||||
return (ESRCH); | return (ESRCH); | ||||
} | } | ||||
export_nat_cfg(ptr, ucfg); | export_nat_cfg(ptr, ucfg); | ||||
/* Estimate memory amount */ | /* Estimate memory amount */ | ||||
▲ Show 20 Lines • Show All 65 Lines • ▼ Show 20 Lines | nat44_list_nat(struct ip_fw_chain *chain, ip_fw3_opheader *op3, | ||||
/* Check minimum header size */ | /* Check minimum header size */ | ||||
if (sd->valsize < sizeof(ipfw_obj_lheader)) | if (sd->valsize < sizeof(ipfw_obj_lheader)) | ||||
return (EINVAL); | return (EINVAL); | ||||
olh = (ipfw_obj_lheader *)ipfw_get_sopt_header(sd, sizeof(*olh)); | olh = (ipfw_obj_lheader *)ipfw_get_sopt_header(sd, sizeof(*olh)); | ||||
IPFW_UH_RLOCK(chain); | IPFW_UH_RLOCK(chain); | ||||
nat_count = 0; | nat_count = 0; | ||||
LIST_FOREACH(ptr, &chain->nat, _next) | LIST_FOREACH(ptr, &chain->nat->list, _next) | ||||
nat_count++; | nat_count++; | ||||
olh->count = nat_count; | olh->count = nat_count; | ||||
olh->objsize = sizeof(struct nat44_cfg_nat); | olh->objsize = sizeof(struct nat44_cfg_nat); | ||||
olh->size = sizeof(*olh) + olh->count * olh->objsize; | olh->size = sizeof(*olh) + olh->count * olh->objsize; | ||||
if (sd->valsize < olh->size) { | if (sd->valsize < olh->size) { | ||||
IPFW_UH_RUNLOCK(chain); | IPFW_UH_RUNLOCK(chain); | ||||
return (ENOMEM); | return (ENOMEM); | ||||
} | } | ||||
LIST_FOREACH(ptr, &chain->nat, _next) { | LIST_FOREACH(ptr, &chain->nat->list, _next) { | ||||
ucfg = (struct nat44_cfg_nat *)ipfw_get_sopt_space(sd, | ucfg = (struct nat44_cfg_nat *)ipfw_get_sopt_space(sd, | ||||
sizeof(*ucfg)); | sizeof(*ucfg)); | ||||
export_nat_cfg(ptr, ucfg); | export_nat_cfg(ptr, ucfg); | ||||
} | } | ||||
IPFW_UH_RUNLOCK(chain); | IPFW_UH_RUNLOCK(chain); | ||||
return (0); | return (0); | ||||
Show All 30 Lines | nat44_get_log(struct ip_fw_chain *chain, ip_fw3_opheader *op3, | ||||
ucfg = (struct nat44_cfg_nat *)(oh + 1); | ucfg = (struct nat44_cfg_nat *)(oh + 1); | ||||
/* Check if name is properly terminated */ | /* Check if name is properly terminated */ | ||||
if (strnlen(ucfg->name, sizeof(ucfg->name)) == sizeof(ucfg->name)) | if (strnlen(ucfg->name, sizeof(ucfg->name)) == sizeof(ucfg->name)) | ||||
return (EINVAL); | return (EINVAL); | ||||
IPFW_UH_RLOCK(chain); | IPFW_UH_RLOCK(chain); | ||||
ptr = lookup_nat_name(&chain->nat, ucfg->name); | ptr = lookup_nat_name(chain->nat, ucfg->name); | ||||
if (ptr == NULL) { | if (ptr == NULL) { | ||||
IPFW_UH_RUNLOCK(chain); | IPFW_UH_RUNLOCK(chain); | ||||
return (ESRCH); | return (ESRCH); | ||||
} | } | ||||
if (ptr->lib->logDesc == NULL) { | if (ptr->lib->logDesc == NULL) { | ||||
IPFW_UH_RUNLOCK(chain); | IPFW_UH_RUNLOCK(chain); | ||||
return (ENOENT); | return (ENOENT); | ||||
▲ Show 20 Lines • Show All 124 Lines • ▼ Show 20 Lines | for (i = 0; i < cfg->redir_cnt; i++) { | ||||
urdir->rport_cnt = rdir->rport_cnt; | urdir->rport_cnt = rdir->rport_cnt; | ||||
urdir->proto = rdir->proto; | urdir->proto = rdir->proto; | ||||
urdir->spool_cnt = rdir->spool_cnt; | urdir->spool_cnt = rdir->spool_cnt; | ||||
urdir++; | urdir++; | ||||
rdir++; | rdir++; | ||||
} | } | ||||
nat44_config(&V_layer3_chain, ucfg); | nat44_config(&V_layer3_chain, ucfg, cfg->id); | ||||
out: | out: | ||||
free(buf, M_TEMP); | free(buf, M_TEMP); | ||||
return (error); | return (error); | ||||
} | } | ||||
static int | static int | ||||
ipfw_nat_del(struct sockopt *sopt) | ipfw_nat_del(struct sockopt *sopt) | ||||
{ | { | ||||
struct cfg_nat *ptr; | struct cfg_nat *ptr; | ||||
struct ip_fw_chain *chain = &V_layer3_chain; | struct ip_fw_chain *chain = &V_layer3_chain; | ||||
int i; | int i; | ||||
sooptcopyin(sopt, &i, sizeof i, sizeof i); | sooptcopyin(sopt, &i, sizeof i, sizeof i); | ||||
/* XXX validate i */ | /* XXX validate i */ | ||||
IPFW_UH_WLOCK(chain); | IPFW_UH_WLOCK(chain); | ||||
ptr = lookup_nat(&chain->nat, i); | ptr = lookup_nat(chain->nat, i); | ||||
if (ptr == NULL) { | if (ptr == NULL) { | ||||
IPFW_UH_WUNLOCK(chain); | IPFW_UH_WUNLOCK(chain); | ||||
return (EINVAL); | return (EINVAL); | ||||
} | } | ||||
IPFW_WLOCK(chain); | IPFW_WLOCK(chain); | ||||
LIST_REMOVE(ptr, _next); | LIST_REMOVE(ptr, _next); | ||||
flush_nat_ptrs(chain, i); | chain->nat->idxmap[i - 1] = NULL; | ||||
IPFW_WUNLOCK(chain); | IPFW_WUNLOCK(chain); | ||||
IPFW_UH_WUNLOCK(chain); | IPFW_UH_WUNLOCK(chain); | ||||
free_nat_instance(ptr); | free_nat_instance(ptr); | ||||
return (0); | return (0); | ||||
} | } | ||||
static int | static int | ||||
ipfw_nat_get_cfg(struct sockopt *sopt) | ipfw_nat_get_cfg(struct sockopt *sopt) | ||||
{ | { | ||||
struct ip_fw_chain *chain = &V_layer3_chain; | struct ip_fw_chain *chain = &V_layer3_chain; | ||||
struct cfg_nat *n; | struct cfg_nat *n; | ||||
struct cfg_nat_legacy *ucfg; | struct cfg_nat_legacy *ucfg; | ||||
struct cfg_redir *r; | struct cfg_redir *r; | ||||
struct cfg_spool *s; | struct cfg_spool *s; | ||||
struct cfg_redir_legacy *ser_r; | struct cfg_redir_legacy *ser_r; | ||||
struct cfg_spool_legacy *ser_s; | struct cfg_spool_legacy *ser_s; | ||||
char *data; | char *data; | ||||
int gencnt, nat_cnt, len, error; | int gencnt, nat_cnt, len, error; | ||||
nat_cnt = 0; | nat_cnt = 0; | ||||
len = sizeof(nat_cnt); | len = sizeof(nat_cnt); | ||||
IPFW_UH_RLOCK(chain); | IPFW_UH_RLOCK(chain); | ||||
retry: | retry: | ||||
gencnt = chain->gencnt; | gencnt = chain->nat->gencnt; | ||||
/* Estimate memory amount */ | /* Estimate memory amount */ | ||||
LIST_FOREACH(n, &chain->nat, _next) { | LIST_FOREACH(n, &chain->nat->list, _next) { | ||||
nat_cnt++; | nat_cnt++; | ||||
len += sizeof(struct cfg_nat_legacy); | len += sizeof(struct cfg_nat_legacy); | ||||
LIST_FOREACH(r, &n->redir_chain, _next) { | LIST_FOREACH(r, &n->redir_chain, _next) { | ||||
len += sizeof(struct cfg_redir_legacy); | len += sizeof(struct cfg_redir_legacy); | ||||
LIST_FOREACH(s, &r->spool_chain, _next) | LIST_FOREACH(s, &r->spool_chain, _next) | ||||
len += sizeof(struct cfg_spool_legacy); | len += sizeof(struct cfg_spool_legacy); | ||||
} | } | ||||
} | } | ||||
IPFW_UH_RUNLOCK(chain); | IPFW_UH_RUNLOCK(chain); | ||||
data = malloc(len, M_TEMP, M_WAITOK | M_ZERO); | data = malloc(len, M_TEMP, M_WAITOK | M_ZERO); | ||||
bcopy(&nat_cnt, data, sizeof(nat_cnt)); | bcopy(&nat_cnt, data, sizeof(nat_cnt)); | ||||
nat_cnt = 0; | nat_cnt = 0; | ||||
len = sizeof(nat_cnt); | len = sizeof(nat_cnt); | ||||
IPFW_UH_RLOCK(chain); | IPFW_UH_RLOCK(chain); | ||||
if (gencnt != chain->gencnt) { | if (gencnt != chain->nat->gencnt) { | ||||
free(data, M_TEMP); | free(data, M_TEMP); | ||||
goto retry; | goto retry; | ||||
} | } | ||||
/* Serialize all the data. */ | /* Serialize all the data. */ | ||||
LIST_FOREACH(n, &chain->nat, _next) { | LIST_FOREACH(n, &chain->nat->list, _next) { | ||||
ucfg = (struct cfg_nat_legacy *)&data[len]; | ucfg = (struct cfg_nat_legacy *)&data[len]; | ||||
ucfg->id = n->id; | ucfg->id = n->id; | ||||
ucfg->ip = n->ip; | ucfg->ip = n->ip; | ||||
ucfg->redir_cnt = n->redir_cnt; | ucfg->redir_cnt = n->redir_cnt; | ||||
ucfg->mode = n->mode; | ucfg->mode = n->mode; | ||||
strlcpy(ucfg->if_name, n->if_name, sizeof(ucfg->if_name)); | strlcpy(ucfg->if_name, n->if_name, sizeof(ucfg->if_name)); | ||||
len += sizeof(struct cfg_nat_legacy); | len += sizeof(struct cfg_nat_legacy); | ||||
LIST_FOREACH(r, &n->redir_chain, _next) { | LIST_FOREACH(r, &n->redir_chain, _next) { | ||||
Show All 35 Lines | ipfw_nat_get_log(struct sockopt *sopt) | ||||
struct ip_fw_chain *chain; | struct ip_fw_chain *chain; | ||||
IPFW_RLOCK_TRACKER; | IPFW_RLOCK_TRACKER; | ||||
chain = &V_layer3_chain; | chain = &V_layer3_chain; | ||||
IPFW_RLOCK(chain); | IPFW_RLOCK(chain); | ||||
/* one pass to count, one to copy the data */ | /* one pass to count, one to copy the data */ | ||||
i = 0; | i = 0; | ||||
LIST_FOREACH(ptr, &chain->nat, _next) { | LIST_FOREACH(ptr, &chain->nat->list, _next) { | ||||
if (ptr->lib->logDesc == NULL) | if (ptr->lib->logDesc == NULL) | ||||
continue; | continue; | ||||
i++; | i++; | ||||
} | } | ||||
size = i * (LIBALIAS_BUF_SIZE + sizeof(int)); | size = i * (LIBALIAS_BUF_SIZE + sizeof(int)); | ||||
data = malloc(size, M_IPFW, M_NOWAIT | M_ZERO); | data = malloc(size, M_IPFW, M_NOWAIT | M_ZERO); | ||||
if (data == NULL) { | if (data == NULL) { | ||||
IPFW_RUNLOCK(chain); | IPFW_RUNLOCK(chain); | ||||
return (ENOSPC); | return (ENOSPC); | ||||
} | } | ||||
i = 0; | i = 0; | ||||
LIST_FOREACH(ptr, &chain->nat, _next) { | LIST_FOREACH(ptr, &chain->nat->list, _next) { | ||||
if (ptr->lib->logDesc == NULL) | if (ptr->lib->logDesc == NULL) | ||||
continue; | continue; | ||||
bcopy(&ptr->id, &data[i], sizeof(int)); | bcopy(&ptr->id, &data[i], sizeof(int)); | ||||
i += sizeof(int); | i += sizeof(int); | ||||
bcopy(ptr->lib->logDesc, &data[i], LIBALIAS_BUF_SIZE); | bcopy(ptr->lib->logDesc, &data[i], LIBALIAS_BUF_SIZE); | ||||
i += LIBALIAS_BUF_SIZE; | i += LIBALIAS_BUF_SIZE; | ||||
} | } | ||||
IPFW_RUNLOCK(chain); | IPFW_RUNLOCK(chain); | ||||
sooptcopyout(sopt, data, size); | sooptcopyout(sopt, data, size); | ||||
free(data, M_IPFW); | free(data, M_IPFW); | ||||
return(0); | return(0); | ||||
} | } | ||||
static int | static int | ||||
vnet_ipfw_nat_init(const void *arg __unused) | vnet_ipfw_nat_init(const void *arg __unused) | ||||
{ | { | ||||
struct nat_priv * priv; | |||||
struct ip_fw_chain *chain; | |||||
int err; | |||||
priv = malloc(sizeof(*priv), M_IPFW, M_WAITOK | M_ZERO); | |||||
priv->idxmap = malloc(sizeof(*(priv->idxmap)), M_IPFW, M_WAITOK | M_ZERO); | |||||
priv->idxsize = 1; | |||||
LIST_INIT(&priv->list); | |||||
Done Inline ActionsDo alloc first and then assign to chain under both UH_WLOCK and IPFW_WLOCK. Also, I'm not sure if we're allowed to sleep in vnet_init hooks. Probably yes, but better verify. melifaro: Do alloc first and then assign to chain under both UH_WLOCK and IPFW_WLOCK.
Also, I'm not sure… | |||||
chain = &V_layer3_chain; | |||||
err = 0; | |||||
IPFW_WLOCK(chain); | |||||
if(chain->nat != NULL) | |||||
err = 1; | |||||
else | |||||
chain->nat = priv; | |||||
IPFW_WUNLOCK(chain); | |||||
if(err) { | |||||
free(priv->idxmap, M_IPFW); | |||||
free(priv, M_IPFW); | |||||
} else | |||||
V_ipfw_nat_ready = 1; | V_ipfw_nat_ready = 1; | ||||
return (0); | |||||
return (err); | |||||
} | } | ||||
static int | static int | ||||
vnet_ipfw_nat_uninit(const void *arg __unused) | vnet_ipfw_nat_uninit(const void *arg __unused) | ||||
{ | { | ||||
struct cfg_nat *ptr, *ptr_temp; | struct cfg_nat *ptr, *ptr_temp; | ||||
struct ip_fw_chain *chain; | struct ip_fw_chain *chain; | ||||
chain = &V_layer3_chain; | chain = &V_layer3_chain; | ||||
IPFW_WLOCK(chain); | IPFW_WLOCK(chain); | ||||
V_ipfw_nat_ready = 0; | V_ipfw_nat_ready = 0; | ||||
LIST_FOREACH_SAFE(ptr, &chain->nat, _next, ptr_temp) { | LIST_FOREACH_SAFE(ptr, &chain->nat->list, _next, ptr_temp) { | ||||
LIST_REMOVE(ptr, _next); | LIST_REMOVE(ptr, _next); | ||||
free_nat_instance(ptr); | free_nat_instance(ptr); | ||||
} | } | ||||
flush_nat_ptrs(chain, -1 /* flush all */); | free(chain->nat->idxmap, M_IPFW); | ||||
free(chain->nat, M_IPFW); | |||||
chain->nat = NULL; | |||||
IPFW_WUNLOCK(chain); | IPFW_WUNLOCK(chain); | ||||
return (0); | return (0); | ||||
} | } | ||||
static void | static void | ||||
ipfw_nat_init(void) | ipfw_nat_init(void) | ||||
{ | { | ||||
▲ Show 20 Lines • Show All 75 Lines • Show Last 20 Lines |
Is it nat_list or nat_mgmt or nat_ctl or nat_priv or ?