/* * Lists all nat64 lsn instances currently available in kernel. * Data layout (v0)(current): * Request: [ ipfw_obj_lheader ] * Reply: [ ipfw_obj_lheader ipfw_nat64lsn_cfg x N ] * * Returns 0 on success */ static int nat64lsn_list(struct ip_fw_chain *ch, ip_fw3_opheader *op3, struct sockopt_data *sd) { ipfw_obj_lheader *olh; struct nat64_dump_arg da; /* 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(ch); olh->count = ipfw_objhash_count_type(CHAIN_TO_SRV(ch), IPFW_TLV_NAT64LSN_NAME); olh->objsize = sizeof(ipfw_nat64lsn_cfg); olh->size = sizeof(*olh) + olh->count * olh->objsize; if (sd->valsize < olh->size) { IPFW_UH_RUNLOCK(ch); return (ENOMEM); } memset(&da, 0, sizeof(da)); da.ch = ch; da.sd = sd; ipfw_objhash_foreach_type(CHAIN_TO_SRV(ch), export_config_cb, &da, IPFW_TLV_NAT64LSN_NAME); IPFW_UH_RUNLOCK(ch); return (0); }
/* * Dumps all shared/table value data * Data layout (v1)(current): * Request: [ ipfw_obj_lheader ], size = ipfw_obj_lheader.size * Reply: [ ipfw_obj_lheader ipfw_table_value x N ] * * Returns 0 on success */ static int list_table_values(struct ip_fw_chain *ch, ip_fw3_opheader *op3, struct sockopt_data *sd) { struct _ipfw_obj_lheader *olh; struct namedobj_instance *vi; struct vdump_args da; uint32_t count, size; olh = (struct _ipfw_obj_lheader *)ipfw_get_sopt_header(sd,sizeof(*olh)); if (olh == NULL) return (EINVAL); if (sd->valsize < olh->size) return (EINVAL); IPFW_UH_RLOCK(ch); vi = CHAIN_TO_VI(ch); count = ipfw_objhash_count(vi); size = count * sizeof(ipfw_table_value) + sizeof(ipfw_obj_lheader); /* Fill in header regadless of buffer size */ olh->count = count; olh->objsize = sizeof(ipfw_table_value); if (size > olh->size) { olh->size = size; IPFW_UH_RUNLOCK(ch); return (ENOMEM); } olh->size = size; /* * Do the actual value dump */ memset(&da, 0, sizeof(da)); da.ch = ch; da.sd = sd; ipfw_objhash_foreach(vi, dump_tvalue, &da); IPFW_UH_RUNLOCK(ch); return (0); }
/* * Lists all interface currently tracked by ipfw. * Data layout (v0)(current): * Request: [ ipfw_obj_lheader ], size = ipfw_obj_lheader.size * Reply: [ ipfw_obj_lheader ipfw_iface_info x N ] * * Returns 0 on success */ static int list_ifaces(struct ip_fw_chain *ch, ip_fw3_opheader *op3, struct sockopt_data *sd) { struct namedobj_instance *ii; struct _ipfw_obj_lheader *olh; struct dump_iface_args da; uint32_t count, size; olh = (struct _ipfw_obj_lheader *)ipfw_get_sopt_header(sd,sizeof(*olh)); if (olh == NULL) return (EINVAL); if (sd->valsize < olh->size) return (EINVAL); IPFW_UH_RLOCK(ch); ii = CHAIN_TO_II(ch); if (ii != NULL) count = ipfw_objhash_count(ii); else count = 0; size = count * sizeof(ipfw_iface_info) + sizeof(ipfw_obj_lheader); /* Fill in header regadless of buffer size */ olh->count = count; olh->objsize = sizeof(ipfw_iface_info); if (size > olh->size) { olh->size = size; IPFW_UH_RUNLOCK(ch); return (ENOMEM); } olh->size = size; da.ch = ch; da.sd = sd; if (ii != NULL) ipfw_objhash_foreach(ii, export_iface_internal, &da); IPFW_UH_RUNLOCK(ch); return (0); }
/* * Get nat64lsn statistics. * Data layout (v0)(current): * Request: [ ipfw_obj_header ] * Reply: [ ipfw_obj_header ipfw_counter_tlv ] * * Returns 0 on success */ static int nat64lsn_stats(struct ip_fw_chain *ch, ip_fw3_opheader *op, struct sockopt_data *sd) { struct ipfw_nat64lsn_stats stats; struct nat64lsn_cfg *cfg; ipfw_obj_header *oh; ipfw_obj_ctlv *ctlv; size_t sz; sz = sizeof(ipfw_obj_header) + sizeof(ipfw_obj_ctlv) + sizeof(stats); if (sd->valsize % sizeof(uint64_t)) return (EINVAL); if (sd->valsize < sz) return (ENOMEM); oh = (ipfw_obj_header *)ipfw_get_sopt_header(sd, sz); if (oh == NULL) return (EINVAL); memset(&stats, 0, sizeof(stats)); IPFW_UH_RLOCK(ch); cfg = nat64lsn_find(CHAIN_TO_SRV(ch), oh->ntlv.name, oh->ntlv.set); if (cfg == NULL) { IPFW_UH_RUNLOCK(ch); return (ESRCH); } export_stats(ch, cfg, &stats); IPFW_UH_RUNLOCK(ch); ctlv = (ipfw_obj_ctlv *)(oh + 1); memset(ctlv, 0, sizeof(*ctlv)); ctlv->head.type = IPFW_TLV_COUNTERS; ctlv->head.length = sz - sizeof(ipfw_obj_header); ctlv->count = sizeof(stats) / sizeof(uint64_t); ctlv->objsize = sizeof(uint64_t); ctlv->version = IPFW_NAT64_VERSION; memcpy(ctlv + 1, &stats, sizeof(stats)); return (0); }
/* * Lists nat64lsn states. * Data layout (v0)(current): * Request: [ ipfw_obj_header ipfw_obj_data [ uint64_t ]] * Reply: [ ipfw_obj_header ipfw_obj_data [ * ipfw_nat64lsn_stg ipfw_nat64lsn_state x N] ] * * Returns 0 on success */ static int nat64lsn_states(struct ip_fw_chain *ch, ip_fw3_opheader *op3, struct sockopt_data *sd) { ipfw_obj_header *oh; ipfw_obj_data *od; ipfw_nat64lsn_stg *stg; struct nat64lsn_cfg *cfg; struct nat64lsn_portgroup *pg, *pg_next; uint64_t next_idx; size_t sz; uint32_t addr, states; uint16_t port; uint8_t nat_proto; sz = sizeof(ipfw_obj_header) + sizeof(ipfw_obj_data) + sizeof(uint64_t); /* Check minimum header size */ if (sd->valsize < sz) return (EINVAL); oh = (ipfw_obj_header *)sd->kbuf; od = (ipfw_obj_data *)(oh + 1); if (od->head.type != IPFW_TLV_OBJDATA || od->head.length != sz - sizeof(ipfw_obj_header)) return (EINVAL); next_idx = *(uint64_t *)(od + 1); /* Translate index to the request position to start from */ UNPACK_IDX(next_idx, addr, nat_proto, port); if (nat_proto >= NAT_MAX_PROTO) return (EINVAL); if (nat_proto == 0 && addr != 0) return (EINVAL); IPFW_UH_RLOCK(ch); cfg = nat64lsn_find(CHAIN_TO_SRV(ch), oh->ntlv.name, oh->ntlv.set); if (cfg == NULL) { IPFW_UH_RUNLOCK(ch); return (ESRCH); } /* Fill in starting point */ if (addr == 0) { addr = cfg->prefix4; nat_proto = 1; port = 0; } if (addr < cfg->prefix4 || addr > cfg->pmask4) { IPFW_UH_RUNLOCK(ch); DPRINTF(DP_GENERIC | DP_STATE, "XXX: %lu %u %u", next_idx, addr, cfg->pmask4); return (EINVAL); } sz = sizeof(ipfw_obj_header) + sizeof(ipfw_obj_data) + sizeof(ipfw_nat64lsn_stg); if (sd->valsize < sz) return (ENOMEM); oh = (ipfw_obj_header *)ipfw_get_sopt_space(sd, sz); od = (ipfw_obj_data *)(oh + 1); od->head.type = IPFW_TLV_OBJDATA; od->head.length = sz - sizeof(ipfw_obj_header); stg = (ipfw_nat64lsn_stg *)(od + 1); pg = get_first_pg(cfg, &addr, &nat_proto, &port); if (pg == NULL) { /* No states */ stg->next_idx = 0xFF; stg->count = 0; IPFW_UH_RUNLOCK(ch); return (0); } states = 0; pg_next = NULL; while (pg != NULL) { pg_next = get_next_pg(cfg, &addr, &nat_proto, &port); if (pg_next == NULL) stg->next_idx = 0xFF; else stg->next_idx = PACK_IDX(addr, nat_proto, port); if (export_pg_states(cfg, pg, stg, sd) != 0) { IPFW_UH_RUNLOCK(ch); return (states == 0 ? ENOMEM: 0); } states += stg->count; od->head.length += stg->count * sizeof(ipfw_nat64lsn_state); sz += stg->count * sizeof(ipfw_nat64lsn_state); if (pg_next != NULL) { sz += sizeof(ipfw_nat64lsn_stg); if (sd->valsize < sz) break; stg = (ipfw_nat64lsn_stg *)ipfw_get_sopt_space(sd, sizeof(ipfw_nat64lsn_stg)); } pg = pg_next; } IPFW_UH_RUNLOCK(ch); return (0); }
/* * Change existing nat64lsn instance configuration. * Data layout (v0)(current): * Request: [ ipfw_obj_header ipfw_nat64lsn_cfg ] * Reply: [ ipfw_obj_header ipfw_nat64lsn_cfg ] * * Returns 0 on success */ static int nat64lsn_config(struct ip_fw_chain *ch, ip_fw3_opheader *op, struct sockopt_data *sd) { ipfw_obj_header *oh; ipfw_nat64lsn_cfg *uc; struct nat64lsn_cfg *cfg; struct namedobj_instance *ni; if (sd->valsize != sizeof(*oh) + sizeof(*uc)) return (EINVAL); oh = (ipfw_obj_header *)ipfw_get_sopt_space(sd, sizeof(*oh) + sizeof(*uc)); uc = (ipfw_nat64lsn_cfg *)(oh + 1); if (ipfw_check_object_name_generic(oh->ntlv.name) != 0 || oh->ntlv.set >= IPFW_MAX_SETS) return (EINVAL); ni = CHAIN_TO_SRV(ch); if (sd->sopt->sopt_dir == SOPT_GET) { IPFW_UH_RLOCK(ch); cfg = nat64lsn_find(ni, oh->ntlv.name, oh->ntlv.set); if (cfg == NULL) { IPFW_UH_RUNLOCK(ch); return (EEXIST); } nat64lsn_export_config(ch, cfg, uc); IPFW_UH_RUNLOCK(ch); return (0); } nat64lsn_default_config(uc); IPFW_UH_WLOCK(ch); cfg = nat64lsn_find(ni, oh->ntlv.name, oh->ntlv.set); if (cfg == NULL) { IPFW_UH_WUNLOCK(ch); return (EEXIST); } /* * For now allow to change only following values: * jmaxlen, nh_del_age, pg_del_age, tcp_syn_age, tcp_close_age, * tcp_est_age, udp_age, icmp_age, flags, max_ports. */ cfg->max_chunks = uc->max_ports / 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->flags = uc->flags & NAT64LSN_FLAGSMASK; IPFW_UH_WUNLOCK(ch); 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); }