int ipfw_add_table_entry(struct ip_fw_chain *ch, uint16_t tbl, in_addr_t addr, uint8_t mlen, uint32_t value) { struct radix_node_head *rnh; struct table_entry *ent; struct radix_node *rn; if (tbl >= IPFW_TABLES_MAX) return (EINVAL); rnh = ch->tables[tbl]; ent = malloc(sizeof(*ent), M_IPFW_TBL, M_NOWAIT | M_ZERO); if (ent == NULL) return (ENOMEM); ent->value = value; KEY_LEN(ent->addr) = KEY_LEN(ent->mask) = 8; ent->mask.sin_addr.s_addr = htonl(mlen ? ~((1 << (32 - mlen)) - 1) : 0); ent->addr.sin_addr.s_addr = addr & ent->mask.sin_addr.s_addr; IPFW_WLOCK(ch); rn = rnh->rnh_addaddr(&ent->addr, &ent->mask, rnh, (void *)ent); if (rn == NULL) { IPFW_WUNLOCK(ch); free(ent, M_IPFW_TBL); return (EEXIST); } IPFW_WUNLOCK(ch); return (0); }
/* * Destroys nat64 instance. * Data layout (v0)(current): * Request: [ ipfw_obj_header ] * * Returns 0 on success */ static int nat64lsn_destroy(struct ip_fw_chain *ch, ip_fw3_opheader *op3, struct sockopt_data *sd) { struct nat64lsn_cfg *cfg; ipfw_obj_header *oh; if (sd->valsize != sizeof(*oh)) return (EINVAL); oh = (ipfw_obj_header *)op3; IPFW_UH_WLOCK(ch); cfg = nat64lsn_find(CHAIN_TO_SRV(ch), oh->ntlv.name, oh->ntlv.set); if (cfg == NULL) { IPFW_UH_WUNLOCK(ch); return (ESRCH); } if (cfg->no.refcnt > 0) { IPFW_UH_WUNLOCK(ch); return (EBUSY); } IPFW_WLOCK(ch); SRV_OBJECT(ch, cfg->no.kidx) = NULL; IPFW_WUNLOCK(ch); nat64lsn_detach_config(ch, cfg); IPFW_UH_WUNLOCK(ch); nat64lsn_destroy_instance(cfg); return (0); }
static int dyn_create(struct ip_fw_chain *ch, struct tid_info *ti, uint16_t *pkidx) { struct namedobj_instance *ni; struct dyn_state_obj *obj; struct named_object *no; ipfw_obj_ntlv *ntlv; char *name; DYN_DEBUG("uidx %d", ti->uidx); if (ti->uidx != 0) { if (ti->tlvs == NULL) return (EINVAL); ntlv = ipfw_find_name_tlv_type(ti->tlvs, ti->tlen, ti->uidx, IPFW_TLV_STATE_NAME); if (ntlv == NULL) return (EINVAL); name = ntlv->name; } else name = default_state_name; ni = CHAIN_TO_SRV(ch); obj = malloc(sizeof(*obj), M_IPFW, M_WAITOK | M_ZERO); obj->no.name = obj->name; obj->no.etlv = IPFW_TLV_STATE_NAME; strlcpy(obj->name, name, sizeof(obj->name)); IPFW_UH_WLOCK(ch); no = ipfw_objhash_lookup_name_type(ni, 0, IPFW_TLV_STATE_NAME, name); if (no != NULL) { /* * Object is already created. * Just return its kidx and bump refcount. */ *pkidx = no->kidx; no->refcnt++; IPFW_UH_WUNLOCK(ch); free(obj, M_IPFW); DYN_DEBUG("\tfound kidx %d", *pkidx); return (0); } if (ipfw_objhash_alloc_idx(ni, &obj->no.kidx) != 0) { DYN_DEBUG("\talloc_idx failed for %s", name); IPFW_UH_WUNLOCK(ch); free(obj, M_IPFW); return (ENOSPC); } ipfw_objhash_add(ni, &obj->no); IPFW_WLOCK(ch); SRV_OBJECT(ch, obj->no.kidx) = obj; IPFW_WUNLOCK(ch); obj->no.refcnt++; *pkidx = obj->no.kidx; IPFW_UH_WUNLOCK(ch); DYN_DEBUG("\tcreated kidx %d", *pkidx); return (0); }
/* * Called for the removal of each instance. */ static int vnet_ipfw_uninit(const void *unused) { struct ip_fw *reap, *rule; struct ip_fw_chain *chain = &V_layer3_chain; int i; V_ipfw_vnet_ready = 0; /* tell new callers to go away */ /* * disconnect from ipv4, ipv6, layer2 and sockopt. * Then grab, release and grab again the WLOCK so we make * sure the update is propagated and nobody will be in. */ (void)ipfw_attach_hooks(0 /* detach */); V_ip_fw_ctl_ptr = NULL; IPFW_UH_WLOCK(chain); IPFW_UH_WUNLOCK(chain); IPFW_UH_WLOCK(chain); IPFW_WLOCK(chain); ipfw_dyn_uninit(0); /* run the callout_drain */ IPFW_WUNLOCK(chain); ipfw_destroy_tables(chain); reap = NULL; IPFW_WLOCK(chain); for (i = 0; i < chain->n_rules; i++) { rule = chain->map[i]; rule->x_next = reap; reap = rule; } if (chain->map) free(chain->map, M_IPFW); IPFW_WUNLOCK(chain); IPFW_UH_WUNLOCK(chain); if (reap != NULL) ipfw_reap_rules(reap); IPFW_LOCK_DESTROY(chain); ipfw_dyn_uninit(1); /* free the remaining parts */ return 0; }
int ipfw_del_table_entry(struct ip_fw_chain *ch, uint16_t tbl, in_addr_t addr, uint8_t mlen) { struct radix_node_head *rnh; struct table_entry *ent; struct sockaddr_in sa, mask; if (tbl >= IPFW_TABLES_MAX) return (EINVAL); rnh = ch->tables[tbl]; KEY_LEN(sa) = KEY_LEN(mask) = 8; mask.sin_addr.s_addr = htonl(mlen ? ~((1 << (32 - mlen)) - 1) : 0); sa.sin_addr.s_addr = addr & mask.sin_addr.s_addr; IPFW_WLOCK(ch); ent = (struct table_entry *)rnh->rnh_deladdr(&sa, &mask, rnh); if (ent == NULL) { IPFW_WUNLOCK(ch); return (ESRCH); } IPFW_WUNLOCK(ch); free(ent, M_IPFW_TBL); return (0); }
/* * Interface departure handler. */ static void handle_ifdetach(struct ip_fw_chain *ch, struct ipfw_iface *iif, uint16_t ifindex) { struct ipfw_ifc *ic; IPFW_UH_WLOCK_ASSERT(ch); IPFW_WLOCK(ch); TAILQ_FOREACH(ic, &iif->consumers, next) ic->cb(ch, ic->cbdata, 0); IPFW_WUNLOCK(ch); iif->gencnt++; iif->resolved = 0; iif->ifindex = 0; }
static void dyn_destroy(struct ip_fw_chain *ch, struct named_object *no) { struct dyn_state_obj *obj; IPFW_UH_WLOCK_ASSERT(ch); KASSERT(no->refcnt == 1, ("Destroying object '%s' (type %u, idx %u) with refcnt %u", no->name, no->etlv, no->kidx, no->refcnt)); DYN_DEBUG("kidx %d", no->kidx); IPFW_WLOCK(ch); obj = SRV_OBJECT(ch, no->kidx); SRV_OBJECT(ch, no->kidx) = NULL; IPFW_WUNLOCK(ch); ipfw_objhash_del(CHAIN_TO_SRV(ch), no); ipfw_objhash_free_idx(CHAIN_TO_SRV(ch), no->kidx); free(obj, M_IPFW); }
int ipfw_flush_table(struct ip_fw_chain *ch, uint16_t tbl) { struct radix_node_head *rnh, *xrnh; if (tbl >= V_fw_tables_max) return (EINVAL); /* * We free both (IPv4 and extended) radix trees and * clear table type here to permit table to be reused * for different type without module reload */ IPFW_WLOCK(ch); /* Set IPv4 table pointer to zero */ if ((rnh = ch->tables[tbl]) != NULL) ch->tables[tbl] = NULL; /* Set extended table pointer to zero */ if ((xrnh = ch->xtables[tbl]) != NULL) ch->xtables[tbl] = NULL; /* Zero table type */ ch->tabletype[tbl] = 0; IPFW_WUNLOCK(ch); if (rnh != NULL) { rnh->rnh_walktree(rnh, flush_table_entry, rnh); rn_detachhead((void **)&rnh); } if (xrnh != NULL) { xrnh->rnh_walktree(xrnh, flush_table_entry, xrnh); rn_detachhead((void **)&xrnh); } return (0); }
static int resize_dynamic_table(struct ip_fw_chain *chain, int nbuckets) { int i, k, nbuckets_old; ipfw_dyn_rule *q; struct ipfw_dyn_bucket *dyn_v, *dyn_v_old; /* Check if given number is power of 2 and less than 64k */ if ((nbuckets > 65536) || (!powerof2(nbuckets))) return 1; CTR3(KTR_NET, "%s: resize dynamic hash: %d -> %d", __func__, V_curr_dyn_buckets, nbuckets); /* Allocate and initialize new hash */ dyn_v = malloc(nbuckets * sizeof(ipfw_dyn_rule), M_IPFW, M_WAITOK | M_ZERO); for (i = 0 ; i < nbuckets; i++) IPFW_BUCK_LOCK_INIT(&dyn_v[i]); /* * Call upper half lock, as get_map() do to ease * read-only access to dynamic rules hash from sysctl */ IPFW_UH_WLOCK(chain); /* * Acquire chain write lock to permit hash access * for main traffic path without additional locks */ IPFW_WLOCK(chain); /* Save old values */ nbuckets_old = V_curr_dyn_buckets; dyn_v_old = V_ipfw_dyn_v; /* Skip relinking if array is not set up */ if (V_ipfw_dyn_v == NULL) V_curr_dyn_buckets = 0; /* Re-link all dynamic states */ for (i = 0 ; i < V_curr_dyn_buckets ; i++) { while (V_ipfw_dyn_v[i].head != NULL) { /* Remove from current chain */ q = V_ipfw_dyn_v[i].head; V_ipfw_dyn_v[i].head = q->next; /* Get new hash value */ k = hash_packet(&q->id, nbuckets); q->bucket = k; /* Add to the new head */ q->next = dyn_v[k].head; dyn_v[k].head = q; } } /* Update current pointers/buckets values */ V_curr_dyn_buckets = nbuckets; V_ipfw_dyn_v = dyn_v; IPFW_WUNLOCK(chain); IPFW_UH_WUNLOCK(chain); /* Start periodic callout on initial creation */ if (dyn_v_old == NULL) { callout_reset_on(&V_ipfw_timeout, hz, ipfw_dyn_tick, curvnet, 0); return (0); } /* Destroy all mutexes */ for (i = 0 ; i < nbuckets_old ; i++) IPFW_BUCK_LOCK_DESTROY(&dyn_v_old[i]); /* Free old hash */ free(dyn_v_old, M_IPFW); return 0; }
int ipfw_resize_tables(struct ip_fw_chain *ch, unsigned int ntables) { struct radix_node_head **tables, **xtables, *rnh; struct radix_node_head **tables_old, **xtables_old; uint8_t *tabletype, *tabletype_old; unsigned int ntables_old, tbl; /* Check new value for validity */ if (ntables > IPFW_TABLES_MAX) ntables = IPFW_TABLES_MAX; /* Allocate new pointers */ tables = malloc(ntables * sizeof(void *), M_IPFW, M_WAITOK | M_ZERO); xtables = malloc(ntables * sizeof(void *), M_IPFW, M_WAITOK | M_ZERO); tabletype = malloc(ntables * sizeof(uint8_t), M_IPFW, M_WAITOK | M_ZERO); IPFW_WLOCK(ch); tbl = (ntables >= V_fw_tables_max) ? V_fw_tables_max : ntables; /* Copy old table pointers */ memcpy(tables, ch->tables, sizeof(void *) * tbl); memcpy(xtables, ch->xtables, sizeof(void *) * tbl); memcpy(tabletype, ch->tabletype, sizeof(uint8_t) * tbl); /* Change pointers and number of tables */ tables_old = ch->tables; xtables_old = ch->xtables; tabletype_old = ch->tabletype; ch->tables = tables; ch->xtables = xtables; ch->tabletype = tabletype; ntables_old = V_fw_tables_max; V_fw_tables_max = ntables; IPFW_WUNLOCK(ch); /* Check if we need to destroy radix trees */ if (ntables < ntables_old) { for (tbl = ntables; tbl < ntables_old; tbl++) { if ((rnh = tables_old[tbl]) != NULL) { rnh->rnh_walktree(rnh, flush_table_entry, rnh); rn_detachhead((void **)&rnh); } if ((rnh = xtables_old[tbl]) != NULL) { rnh->rnh_walktree(rnh, flush_table_entry, rnh); rn_detachhead((void **)&rnh); } } } /* Free old pointers */ free(tables_old, M_IPFW); free(xtables_old, M_IPFW); free(tabletype_old, M_IPFW); return (0); }
int ipfw_del_table_entry(struct ip_fw_chain *ch, uint16_t tbl, void *paddr, uint8_t plen, uint8_t mlen, uint8_t type) { struct radix_node_head *rnh, **rnh_ptr; struct table_entry *ent; in_addr_t addr; struct sockaddr_in sa, mask; struct sockaddr *sa_ptr, *mask_ptr; char c; if (tbl >= V_fw_tables_max) return (EINVAL); switch (type) { case IPFW_TABLE_CIDR: if (plen == sizeof(in_addr_t)) { /* Set 'total' structure length */ KEY_LEN(sa) = KEY_LEN_INET; KEY_LEN(mask) = KEY_LEN_INET; mask.sin_addr.s_addr = htonl(mlen ? ~((1 << (32 - mlen)) - 1) : 0); addr = *((in_addr_t *)paddr); sa.sin_addr.s_addr = addr & mask.sin_addr.s_addr; rnh_ptr = &ch->tables[tbl]; sa_ptr = (struct sockaddr *)&sa; mask_ptr = (struct sockaddr *)&mask; #ifdef INET6 } else if (plen == sizeof(struct in6_addr)) { /* IPv6 case */ if (mlen > 128) return (EINVAL); struct sockaddr_in6 sa6, mask6; memset(&sa6, 0, sizeof(struct sockaddr_in6)); memset(&mask6, 0, sizeof(struct sockaddr_in6)); /* Set 'total' structure length */ KEY_LEN(sa6) = KEY_LEN_INET6; KEY_LEN(mask6) = KEY_LEN_INET6; ipv6_writemask(&mask6.sin6_addr, mlen); memcpy(&sa6.sin6_addr, paddr, sizeof(struct in6_addr)); APPLY_MASK(&sa6.sin6_addr, &mask6.sin6_addr); rnh_ptr = &ch->xtables[tbl]; sa_ptr = (struct sockaddr *)&sa6; mask_ptr = (struct sockaddr *)&mask6; #endif } else { /* Unknown CIDR type */ return (EINVAL); } break; case IPFW_TABLE_INTERFACE: /* Check if string is terminated */ c = ((char *)paddr)[IF_NAMESIZE - 1]; ((char *)paddr)[IF_NAMESIZE - 1] = '\0'; if (((mlen = strlen((char *)paddr)) == IF_NAMESIZE - 1) && (c != '\0')) return (EINVAL); struct xaddr_iface ifname, ifmask; memset(&ifname, 0, sizeof(ifname)); /* Include last \0 into comparison */ mlen++; /* Set 'total' structure length */ KEY_LEN(ifname) = KEY_LEN_IFACE + mlen; KEY_LEN(ifmask) = KEY_LEN_IFACE + mlen; /* Assume direct match */ /* FIXME: Add interface pattern matching */ #if 0 memset(ifmask.ifname, 0xFF, IF_NAMESIZE); mask_ptr = (struct sockaddr *)&ifmask; #endif mask_ptr = NULL; memcpy(ifname.ifname, paddr, mlen); /* Set pointers */ rnh_ptr = &ch->xtables[tbl]; sa_ptr = (struct sockaddr *)&ifname; break; default: return (EINVAL); } IPFW_WLOCK(ch); if ((rnh = *rnh_ptr) == NULL) { IPFW_WUNLOCK(ch); return (ESRCH); } if (ch->tabletype[tbl] != type) { IPFW_WUNLOCK(ch); return (EINVAL); } ent = (struct table_entry *)rnh->rnh_deladdr(sa_ptr, mask_ptr, rnh); IPFW_WUNLOCK(ch); if (ent == NULL) return (ESRCH); free(ent, M_IPFW_TBL); return (0); }
int ipfw_add_table_entry(struct ip_fw_chain *ch, uint16_t tbl, void *paddr, uint8_t plen, uint8_t mlen, uint8_t type, uint32_t value) { struct radix_node_head *rnh, **rnh_ptr; struct table_entry *ent; struct table_xentry *xent; struct radix_node *rn; in_addr_t addr; int offset; void *ent_ptr; struct sockaddr *addr_ptr, *mask_ptr; char c; if (tbl >= V_fw_tables_max) return (EINVAL); switch (type) { case IPFW_TABLE_CIDR: if (plen == sizeof(in_addr_t)) { #ifdef INET /* IPv4 case */ if (mlen > 32) return (EINVAL); ent = malloc(sizeof(*ent), M_IPFW_TBL, M_WAITOK | M_ZERO); ent->value = value; /* Set 'total' structure length */ KEY_LEN(ent->addr) = KEY_LEN_INET; KEY_LEN(ent->mask) = KEY_LEN_INET; /* Set offset of IPv4 address in bits */ offset = OFF_LEN_INET; ent->mask.sin_addr.s_addr = htonl(mlen ? ~((1 << (32 - mlen)) - 1) : 0); addr = *((in_addr_t *)paddr); ent->addr.sin_addr.s_addr = addr & ent->mask.sin_addr.s_addr; /* Set pointers */ rnh_ptr = &ch->tables[tbl]; ent_ptr = ent; addr_ptr = (struct sockaddr *)&ent->addr; mask_ptr = (struct sockaddr *)&ent->mask; #endif #ifdef INET6 } else if (plen == sizeof(struct in6_addr)) { /* IPv6 case */ if (mlen > 128) return (EINVAL); xent = malloc(sizeof(*xent), M_IPFW_TBL, M_WAITOK | M_ZERO); xent->value = value; /* Set 'total' structure length */ KEY_LEN(xent->a.addr6) = KEY_LEN_INET6; KEY_LEN(xent->m.mask6) = KEY_LEN_INET6; /* Set offset of IPv6 address in bits */ offset = OFF_LEN_INET6; ipv6_writemask(&xent->m.mask6.sin6_addr, mlen); memcpy(&xent->a.addr6.sin6_addr, paddr, sizeof(struct in6_addr)); APPLY_MASK(&xent->a.addr6.sin6_addr, &xent->m.mask6.sin6_addr); /* Set pointers */ rnh_ptr = &ch->xtables[tbl]; ent_ptr = xent; addr_ptr = (struct sockaddr *)&xent->a.addr6; mask_ptr = (struct sockaddr *)&xent->m.mask6; #endif } else { /* Unknown CIDR type */ return (EINVAL); } break; case IPFW_TABLE_INTERFACE: /* Check if string is terminated */ c = ((char *)paddr)[IF_NAMESIZE - 1]; ((char *)paddr)[IF_NAMESIZE - 1] = '\0'; if (((mlen = strlen((char *)paddr)) == IF_NAMESIZE - 1) && (c != '\0')) return (EINVAL); /* Include last \0 into comparison */ mlen++; xent = malloc(sizeof(*xent), M_IPFW_TBL, M_WAITOK | M_ZERO); xent->value = value; /* Set 'total' structure length */ KEY_LEN(xent->a.iface) = KEY_LEN_IFACE + mlen; KEY_LEN(xent->m.ifmask) = KEY_LEN_IFACE + mlen; /* Set offset of interface name in bits */ offset = OFF_LEN_IFACE; memcpy(xent->a.iface.ifname, paddr, mlen); /* Assume direct match */ /* TODO: Add interface pattern matching */ #if 0 memset(xent->m.ifmask.ifname, 0xFF, IF_NAMESIZE); mask_ptr = (struct sockaddr *)&xent->m.ifmask; #endif /* Set pointers */ rnh_ptr = &ch->xtables[tbl]; ent_ptr = xent; addr_ptr = (struct sockaddr *)&xent->a.iface; mask_ptr = NULL; break; default: return (EINVAL); } IPFW_WLOCK(ch); /* Check if tabletype is valid */ if ((ch->tabletype[tbl] != 0) && (ch->tabletype[tbl] != type)) { IPFW_WUNLOCK(ch); free(ent_ptr, M_IPFW_TBL); return (EINVAL); } /* Check if radix tree exists */ if ((rnh = *rnh_ptr) == NULL) { IPFW_WUNLOCK(ch); /* Create radix for a new table */ if (!rn_inithead((void **)&rnh, offset)) { free(ent_ptr, M_IPFW_TBL); return (ENOMEM); } IPFW_WLOCK(ch); if (*rnh_ptr != NULL) { /* Tree is already attached by other thread */ rn_detachhead((void **)&rnh); rnh = *rnh_ptr; /* Check table type another time */ if (ch->tabletype[tbl] != type) { IPFW_WUNLOCK(ch); free(ent_ptr, M_IPFW_TBL); return (EINVAL); } } else { *rnh_ptr = rnh; /* * Set table type. It can be set already * (if we have IPv6-only table) but setting * it another time does not hurt */ ch->tabletype[tbl] = type; } } rn = rnh->rnh_addaddr(addr_ptr, mask_ptr, rnh, ent_ptr); IPFW_WUNLOCK(ch); if (rn == NULL) { free(ent_ptr, M_IPFW_TBL); return (EEXIST); } return (0); }
/* * Creates new nat64lsn instance. * Data layout (v0)(current): * Request: [ ipfw_obj_lheader ipfw_nat64lsn_cfg ] * * Returns 0 on success */ static int nat64lsn_create(struct ip_fw_chain *ch, ip_fw3_opheader *op3, struct sockopt_data *sd) { ipfw_obj_lheader *olh; ipfw_nat64lsn_cfg *uc; struct nat64lsn_cfg *cfg; struct namedobj_instance *ni; uint32_t addr4, mask4; if (sd->valsize != sizeof(*olh) + sizeof(*uc)) return (EINVAL); olh = (ipfw_obj_lheader *)sd->kbuf; uc = (ipfw_nat64lsn_cfg *)(olh + 1); if (ipfw_check_object_name_generic(uc->name) != 0) return (EINVAL); if (uc->agg_prefix_len > 127 || uc->set >= IPFW_MAX_SETS) return (EINVAL); if (uc->plen4 > 32) return (EINVAL); if (uc->plen6 > 128 || ((uc->plen6 % 8) != 0)) return (EINVAL); /* XXX: Check prefix4 to be global */ addr4 = ntohl(uc->prefix4.s_addr); mask4 = ~((1 << (32 - uc->plen4)) - 1); if ((addr4 & mask4) != addr4) return (EINVAL); /* XXX: Check prefix6 */ if (uc->min_port == 0) uc->min_port = NAT64_MIN_PORT; if (uc->max_port == 0) uc->max_port = 65535; if (uc->min_port > uc->max_port) return (EINVAL); uc->min_port = roundup(uc->min_port, NAT64_CHUNK_SIZE); uc->max_port = roundup(uc->max_port, NAT64_CHUNK_SIZE); nat64lsn_default_config(uc); ni = CHAIN_TO_SRV(ch); IPFW_UH_RLOCK(ch); if (nat64lsn_find(ni, uc->name, uc->set) != NULL) { IPFW_UH_RUNLOCK(ch); return (EEXIST); } IPFW_UH_RUNLOCK(ch); cfg = nat64lsn_init_instance(ch, 1 << (32 - uc->plen4)); strlcpy(cfg->name, uc->name, sizeof(cfg->name)); cfg->no.name = cfg->name; cfg->no.etlv = IPFW_TLV_NAT64LSN_NAME; cfg->no.set = uc->set; cfg->prefix4 = addr4; cfg->pmask4 = addr4 | ~mask4; /* XXX: Copy 96 bits */ cfg->plen6 = 96; memcpy(&cfg->prefix6, &uc->prefix6, cfg->plen6 / 8); cfg->plen4 = uc->plen4; cfg->flags = uc->flags & NAT64LSN_FLAGSMASK; cfg->max_chunks = uc->max_ports / NAT64_CHUNK_SIZE; cfg->agg_prefix_len = uc->agg_prefix_len; cfg->agg_prefix_max = uc->agg_prefix_max; cfg->min_chunk = uc->min_port / NAT64_CHUNK_SIZE; cfg->max_chunk = uc->max_port / NAT64_CHUNK_SIZE; cfg->jmaxlen = uc->jmaxlen; cfg->nh_delete_delay = uc->nh_delete_delay; cfg->pg_delete_delay = uc->pg_delete_delay; cfg->st_syn_ttl = uc->st_syn_ttl; cfg->st_close_ttl = uc->st_close_ttl; cfg->st_estab_ttl = uc->st_estab_ttl; cfg->st_udp_ttl = uc->st_udp_ttl; cfg->st_icmp_ttl = uc->st_icmp_ttl; cfg->nomatch_verdict = IP_FW_DENY; cfg->nomatch_final = 1; /* Exit outer loop by default */ IPFW_UH_WLOCK(ch); if (nat64lsn_find(ni, uc->name, uc->set) != NULL) { IPFW_UH_WUNLOCK(ch); nat64lsn_destroy_instance(cfg); return (EEXIST); } if (ipfw_objhash_alloc_idx(CHAIN_TO_SRV(ch), &cfg->no.kidx) != 0) { IPFW_UH_WUNLOCK(ch); nat64lsn_destroy_instance(cfg); return (ENOSPC); } ipfw_objhash_add(CHAIN_TO_SRV(ch), &cfg->no); /* Okay, let's link data */ IPFW_WLOCK(ch); SRV_OBJECT(ch, cfg->no.kidx) = cfg; IPFW_WUNLOCK(ch); nat64lsn_start_instance(cfg); IPFW_UH_WUNLOCK(ch); return (0); }
/* * Grows value storage shared among all tables. * Drops/reacquires UH locks. * Notifies other running adds on @ch shared storage resize. * Note function does not guarantee that free space * will be available after invocation, so one caller needs * to roll cycle himself. * * Returns 0 if case of no errors. */ static int resize_shared_value_storage(struct ip_fw_chain *ch) { struct tables_config *tcfg; struct namedobj_instance *vi; struct table_value *pval, *valuestate, *old_valuestate; void *new_idx; struct vdump_args da; int new_blocks; int val_size, val_size_old; IPFW_UH_WLOCK_ASSERT(ch); valuestate = NULL; new_idx = NULL; pval = (struct table_value *)ch->valuestate; vi = CHAIN_TO_VI(ch); tcfg = CHAIN_TO_TCFG(ch); val_size = tcfg->val_size * 2; if (val_size == (1 << 30)) return (ENOSPC); IPFW_UH_WUNLOCK(ch); valuestate = malloc(sizeof(struct table_value) * val_size, M_IPFW, M_WAITOK | M_ZERO); ipfw_objhash_bitmap_alloc(val_size, (void *)&new_idx, &new_blocks); IPFW_UH_WLOCK(ch); /* * Check if we still need to resize */ if (tcfg->val_size >= val_size) goto done; /* Update pointers and notify everyone we're changing @ch */ pval = (struct table_value *)ch->valuestate; rollback_toperation_state(ch, ch); /* Good. Let's merge */ memcpy(valuestate, pval, sizeof(struct table_value) * tcfg->val_size); ipfw_objhash_bitmap_merge(CHAIN_TO_VI(ch), &new_idx, &new_blocks); IPFW_WLOCK(ch); /* Change pointers */ old_valuestate = ch->valuestate; ch->valuestate = valuestate; valuestate = old_valuestate; ipfw_objhash_bitmap_swap(CHAIN_TO_VI(ch), &new_idx, &new_blocks); val_size_old = tcfg->val_size; tcfg->val_size = val_size; val_size = val_size_old; IPFW_WUNLOCK(ch); /* Update pointers to reflect resize */ memset(&da, 0, sizeof(da)); da.pval = (struct table_value *)ch->valuestate; ipfw_objhash_foreach(vi, update_tvalue, &da); done: free(valuestate, M_IPFW); ipfw_objhash_bitmap_free(new_idx, new_blocks); return (0); }