Path: blob/main/sys/netpfil/ipfw/nat64/nat64stl_control.c
39536 views
/*-1* SPDX-License-Identifier: BSD-2-Clause2*3* Copyright (c) 2015-2019 Yandex LLC4* Copyright (c) 2015 Alexander V. Chernikov <[email protected]>5* Copyright (c) 2015-2019 Andrey V. Elsukov <[email protected]>6*7* Redistribution and use in source and binary forms, with or without8* modification, are permitted provided that the following conditions9* are met:10*11* 1. Redistributions of source code must retain the above copyright12* notice, this list of conditions and the following disclaimer.13* 2. Redistributions in binary form must reproduce the above copyright14* notice, this list of conditions and the following disclaimer in the15* documentation and/or other materials provided with the distribution.16*17* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR18* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES19* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.20* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,21* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT22* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,23* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY24* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT25* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF26* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.27*/2829#include <sys/param.h>30#include <sys/systm.h>31#include <sys/counter.h>32#include <sys/errno.h>33#include <sys/kernel.h>34#include <sys/lock.h>35#include <sys/malloc.h>36#include <sys/mbuf.h>37#include <sys/module.h>38#include <sys/rmlock.h>39#include <sys/rwlock.h>40#include <sys/socket.h>41#include <sys/sockopt.h>42#include <sys/queue.h>43#include <sys/syslog.h>44#include <sys/sysctl.h>4546#include <net/if.h>47#include <net/if_var.h>48#include <net/pfil.h>49#include <net/route.h>50#include <net/vnet.h>5152#include <netinet/in.h>53#include <netinet/ip_var.h>54#include <netinet/ip_fw.h>55#include <netinet6/in6_var.h>56#include <netinet6/ip6_var.h>57#include <netinet6/ip_fw_nat64.h>5859#include <netpfil/ipfw/ip_fw_private.h>6061#include "nat64stl.h"6263VNET_DEFINE(uint32_t, nat64stl_eid) = 0;6465static struct nat64stl_cfg *nat64stl_alloc_config(const char *name,66uint8_t set);67static void nat64stl_free_config(struct nat64stl_cfg *cfg);68static struct nat64stl_cfg *nat64stl_find(struct namedobj_instance *ni,69const char *name, uint8_t set);7071static struct nat64stl_cfg *72nat64stl_alloc_config(const char *name, uint8_t set)73{74struct nat64stl_cfg *cfg;7576cfg = malloc(sizeof(struct nat64stl_cfg), M_IPFW, M_WAITOK | M_ZERO);77COUNTER_ARRAY_ALLOC(cfg->base.stats.cnt, NAT64STATS, M_WAITOK);78cfg->no.name = cfg->name;79cfg->no.etlv = IPFW_TLV_NAT64STL_NAME;80cfg->no.set = set;81strlcpy(cfg->name, name, sizeof(cfg->name));82return (cfg);83}8485static void86nat64stl_free_config(struct nat64stl_cfg *cfg)87{8889COUNTER_ARRAY_FREE(cfg->base.stats.cnt, NAT64STATS);90free(cfg, M_IPFW);91}9293static void94nat64stl_export_config(struct ip_fw_chain *ch, struct nat64stl_cfg *cfg,95ipfw_nat64stl_cfg *uc)96{97struct named_object *no;9899uc->prefix6 = cfg->base.plat_prefix;100uc->plen6 = cfg->base.plat_plen;101uc->flags = cfg->base.flags & NAT64STL_FLAGSMASK;102uc->set = cfg->no.set;103strlcpy(uc->name, cfg->no.name, sizeof(uc->name));104105no = ipfw_objhash_lookup_table_kidx(ch, cfg->map64);106ipfw_export_obj_ntlv(no, &uc->ntlv6);107no = ipfw_objhash_lookup_table_kidx(ch, cfg->map46);108ipfw_export_obj_ntlv(no, &uc->ntlv4);109}110111struct nat64stl_dump_arg {112struct ip_fw_chain *ch;113struct sockopt_data *sd;114};115116static int117export_config_cb(struct namedobj_instance *ni, struct named_object *no,118void *arg)119{120struct nat64stl_dump_arg *da = (struct nat64stl_dump_arg *)arg;121ipfw_nat64stl_cfg *uc;122123uc = (ipfw_nat64stl_cfg *)ipfw_get_sopt_space(da->sd, sizeof(*uc));124nat64stl_export_config(da->ch, (struct nat64stl_cfg *)no, uc);125return (0);126}127128static struct nat64stl_cfg *129nat64stl_find(struct namedobj_instance *ni, const char *name, uint8_t set)130{131struct nat64stl_cfg *cfg;132133cfg = (struct nat64stl_cfg *)ipfw_objhash_lookup_name_type(ni, set,134IPFW_TLV_NAT64STL_NAME, name);135136return (cfg);137}138139static int140nat64stl_create_internal(struct ip_fw_chain *ch, struct nat64stl_cfg *cfg,141ipfw_nat64stl_cfg *i)142{143144IPFW_UH_WLOCK_ASSERT(ch);145146if (ipfw_objhash_alloc_idx(CHAIN_TO_SRV(ch), &cfg->no.kidx) != 0)147return (ENOSPC);148cfg->base.flags |= NAT64STL_KIDX;149150if (ipfw_ref_table(ch, &i->ntlv4, &cfg->map46) != 0)151return (EINVAL);152cfg->base.flags |= NAT64STL_46T;153154if (ipfw_ref_table(ch, &i->ntlv6, &cfg->map64) != 0)155return (EINVAL);156cfg->base.flags |= NAT64STL_64T;157158ipfw_objhash_add(CHAIN_TO_SRV(ch), &cfg->no);159160return (0);161}162163/*164* Creates new nat64 instance.165* Data layout (v0)(current):166* Request: [ ipfw_obj_lheader ipfw_nat64stl_cfg ]167*168* Returns 0 on success169*/170static int171nat64stl_create(struct ip_fw_chain *ch, ip_fw3_opheader *op3,172struct sockopt_data *sd)173{174ipfw_obj_lheader *olh;175ipfw_nat64stl_cfg *uc;176struct namedobj_instance *ni;177struct nat64stl_cfg *cfg;178int error;179180if (sd->valsize != sizeof(*olh) + sizeof(*uc))181return (EINVAL);182183olh = (ipfw_obj_lheader *)sd->kbuf;184uc = (ipfw_nat64stl_cfg *)(olh + 1);185186if (ipfw_check_object_name_generic(uc->name) != 0)187return (EINVAL);188if (uc->set >= IPFW_MAX_SETS ||189nat64_check_prefix6(&uc->prefix6, uc->plen6) != 0)190return (EINVAL);191192/* XXX: check types of tables */193194ni = CHAIN_TO_SRV(ch);195error = 0;196197IPFW_UH_RLOCK(ch);198if (nat64stl_find(ni, uc->name, uc->set) != NULL) {199IPFW_UH_RUNLOCK(ch);200return (EEXIST);201}202IPFW_UH_RUNLOCK(ch);203204cfg = nat64stl_alloc_config(uc->name, uc->set);205cfg->base.plat_prefix = uc->prefix6;206cfg->base.plat_plen = uc->plen6;207cfg->base.flags = (uc->flags & NAT64STL_FLAGSMASK) | NAT64_PLATPFX;208if (IN6_IS_ADDR_WKPFX(&cfg->base.plat_prefix))209cfg->base.flags |= NAT64_WKPFX;210211IPFW_UH_WLOCK(ch);212213if (nat64stl_find(ni, uc->name, uc->set) != NULL) {214IPFW_UH_WUNLOCK(ch);215nat64stl_free_config(cfg);216return (EEXIST);217}218error = nat64stl_create_internal(ch, cfg, uc);219if (error == 0) {220/* Okay, let's link data */221SRV_OBJECT(ch, cfg->no.kidx) = cfg;222IPFW_UH_WUNLOCK(ch);223return (0);224}225226if (cfg->base.flags & NAT64STL_KIDX)227ipfw_objhash_free_idx(ni, cfg->no.kidx);228if (cfg->base.flags & NAT64STL_46T)229ipfw_unref_table(ch, cfg->map46);230if (cfg->base.flags & NAT64STL_64T)231ipfw_unref_table(ch, cfg->map64);232233IPFW_UH_WUNLOCK(ch);234nat64stl_free_config(cfg);235return (error);236}237238/*239* Change existing nat64stl instance configuration.240* Data layout (v0)(current):241* Request: [ ipfw_obj_header ipfw_nat64stl_cfg ]242* Reply: [ ipfw_obj_header ipfw_nat64stl_cfg ]243*244* Returns 0 on success245*/246static int247nat64stl_config(struct ip_fw_chain *ch, ip_fw3_opheader *op,248struct sockopt_data *sd)249{250ipfw_obj_header *oh;251ipfw_nat64stl_cfg *uc;252struct nat64stl_cfg *cfg;253struct namedobj_instance *ni;254255if (sd->valsize != sizeof(*oh) + sizeof(*uc))256return (EINVAL);257258oh = (ipfw_obj_header *)ipfw_get_sopt_space(sd,259sizeof(*oh) + sizeof(*uc));260uc = (ipfw_nat64stl_cfg *)(oh + 1);261262if (ipfw_check_object_name_generic(oh->ntlv.name) != 0 ||263oh->ntlv.set >= IPFW_MAX_SETS)264return (EINVAL);265266ni = CHAIN_TO_SRV(ch);267if (sd->sopt->sopt_dir == SOPT_GET) {268IPFW_UH_RLOCK(ch);269cfg = nat64stl_find(ni, oh->ntlv.name, oh->ntlv.set);270if (cfg == NULL) {271IPFW_UH_RUNLOCK(ch);272return (EEXIST);273}274nat64stl_export_config(ch, cfg, uc);275IPFW_UH_RUNLOCK(ch);276return (0);277}278279IPFW_UH_WLOCK(ch);280cfg = nat64stl_find(ni, oh->ntlv.name, oh->ntlv.set);281if (cfg == NULL) {282IPFW_UH_WUNLOCK(ch);283return (EEXIST);284}285286/*287* For now allow to change only following values:288* flags.289*/290cfg->base.flags &= ~NAT64STL_FLAGSMASK;291cfg->base.flags |= uc->flags & NAT64STL_FLAGSMASK;292293IPFW_UH_WUNLOCK(ch);294return (0);295}296297static void298nat64stl_detach_config(struct ip_fw_chain *ch, struct nat64stl_cfg *cfg)299{300301IPFW_UH_WLOCK_ASSERT(ch);302303ipfw_objhash_del(CHAIN_TO_SRV(ch), &cfg->no);304ipfw_objhash_free_idx(CHAIN_TO_SRV(ch), cfg->no.kidx);305ipfw_unref_table(ch, cfg->map46);306ipfw_unref_table(ch, cfg->map64);307}308309/*310* Destroys nat64 instance.311* Data layout (v0)(current):312* Request: [ ipfw_obj_header ]313*314* Returns 0 on success315*/316static int317nat64stl_destroy(struct ip_fw_chain *ch, ip_fw3_opheader *op3,318struct sockopt_data *sd)319{320ipfw_obj_header *oh;321struct nat64stl_cfg *cfg;322323if (sd->valsize != sizeof(*oh))324return (EINVAL);325326oh = (ipfw_obj_header *)sd->kbuf;327if (ipfw_check_object_name_generic(oh->ntlv.name) != 0)328return (EINVAL);329330IPFW_UH_WLOCK(ch);331cfg = nat64stl_find(CHAIN_TO_SRV(ch), oh->ntlv.name, oh->ntlv.set);332if (cfg == NULL) {333IPFW_UH_WUNLOCK(ch);334return (ESRCH);335}336if (cfg->no.refcnt > 0) {337IPFW_UH_WUNLOCK(ch);338return (EBUSY);339}340341ipfw_reset_eaction_instance(ch, V_nat64stl_eid, cfg->no.kidx);342SRV_OBJECT(ch, cfg->no.kidx) = NULL;343nat64stl_detach_config(ch, cfg);344IPFW_UH_WUNLOCK(ch);345346nat64stl_free_config(cfg);347return (0);348}349350/*351* Lists all nat64stl instances currently available in kernel.352* Data layout (v0)(current):353* Request: [ ipfw_obj_lheader ]354* Reply: [ ipfw_obj_lheader ipfw_nat64stl_cfg x N ]355*356* Returns 0 on success357*/358static int359nat64stl_list(struct ip_fw_chain *ch, ip_fw3_opheader *op3,360struct sockopt_data *sd)361{362ipfw_obj_lheader *olh;363struct nat64stl_dump_arg da;364365/* Check minimum header size */366if (sd->valsize < sizeof(ipfw_obj_lheader))367return (EINVAL);368369olh = (ipfw_obj_lheader *)ipfw_get_sopt_header(sd, sizeof(*olh));370371IPFW_UH_RLOCK(ch);372olh->count = ipfw_objhash_count_type(CHAIN_TO_SRV(ch),373IPFW_TLV_NAT64STL_NAME);374olh->objsize = sizeof(ipfw_nat64stl_cfg);375olh->size = sizeof(*olh) + olh->count * olh->objsize;376377if (sd->valsize < olh->size) {378IPFW_UH_RUNLOCK(ch);379return (ENOMEM);380}381memset(&da, 0, sizeof(da));382da.ch = ch;383da.sd = sd;384ipfw_objhash_foreach_type(CHAIN_TO_SRV(ch), export_config_cb,385&da, IPFW_TLV_NAT64STL_NAME);386IPFW_UH_RUNLOCK(ch);387388return (0);389}390391#define __COPY_STAT_FIELD(_cfg, _stats, _field) \392(_stats)->_field = NAT64STAT_FETCH(&(_cfg)->base.stats, _field)393static void394export_stats(struct ip_fw_chain *ch, struct nat64stl_cfg *cfg,395struct ipfw_nat64stl_stats *stats)396{397398__COPY_STAT_FIELD(cfg, stats, opcnt64);399__COPY_STAT_FIELD(cfg, stats, opcnt46);400__COPY_STAT_FIELD(cfg, stats, ofrags);401__COPY_STAT_FIELD(cfg, stats, ifrags);402__COPY_STAT_FIELD(cfg, stats, oerrors);403__COPY_STAT_FIELD(cfg, stats, noroute4);404__COPY_STAT_FIELD(cfg, stats, noroute6);405__COPY_STAT_FIELD(cfg, stats, noproto);406__COPY_STAT_FIELD(cfg, stats, nomem);407__COPY_STAT_FIELD(cfg, stats, dropped);408}409410/*411* Get nat64stl statistics.412* Data layout (v0)(current):413* Request: [ ipfw_obj_header ]414* Reply: [ ipfw_obj_header ipfw_obj_ctlv [ uint64_t x N ]]415*416* Returns 0 on success417*/418static int419nat64stl_stats(struct ip_fw_chain *ch, ip_fw3_opheader *op,420struct sockopt_data *sd)421{422struct ipfw_nat64stl_stats stats;423struct nat64stl_cfg *cfg;424ipfw_obj_header *oh;425ipfw_obj_ctlv *ctlv;426size_t sz;427428sz = sizeof(ipfw_obj_header) + sizeof(ipfw_obj_ctlv) + sizeof(stats);429if (sd->valsize % sizeof(uint64_t))430return (EINVAL);431if (sd->valsize < sz)432return (ENOMEM);433oh = (ipfw_obj_header *)ipfw_get_sopt_header(sd, sz);434if (oh == NULL)435return (EINVAL);436memset(&stats, 0, sizeof(stats));437438IPFW_UH_RLOCK(ch);439cfg = nat64stl_find(CHAIN_TO_SRV(ch), oh->ntlv.name, oh->ntlv.set);440if (cfg == NULL) {441IPFW_UH_RUNLOCK(ch);442return (ESRCH);443}444export_stats(ch, cfg, &stats);445IPFW_UH_RUNLOCK(ch);446447ctlv = (ipfw_obj_ctlv *)(oh + 1);448memset(ctlv, 0, sizeof(*ctlv));449ctlv->head.type = IPFW_TLV_COUNTERS;450ctlv->head.length = sz - sizeof(ipfw_obj_header);451ctlv->count = sizeof(stats) / sizeof(uint64_t);452ctlv->objsize = sizeof(uint64_t);453ctlv->version = IPFW_NAT64_VERSION;454memcpy(ctlv + 1, &stats, sizeof(stats));455return (0);456}457458/*459* Reset nat64stl statistics.460* Data layout (v0)(current):461* Request: [ ipfw_obj_header ]462*463* Returns 0 on success464*/465static int466nat64stl_reset_stats(struct ip_fw_chain *ch, ip_fw3_opheader *op,467struct sockopt_data *sd)468{469struct nat64stl_cfg *cfg;470ipfw_obj_header *oh;471472if (sd->valsize != sizeof(*oh))473return (EINVAL);474oh = (ipfw_obj_header *)sd->kbuf;475if (ipfw_check_object_name_generic(oh->ntlv.name) != 0 ||476oh->ntlv.set >= IPFW_MAX_SETS)477return (EINVAL);478479IPFW_UH_WLOCK(ch);480cfg = nat64stl_find(CHAIN_TO_SRV(ch), oh->ntlv.name, oh->ntlv.set);481if (cfg == NULL) {482IPFW_UH_WUNLOCK(ch);483return (ESRCH);484}485COUNTER_ARRAY_ZERO(cfg->base.stats.cnt, NAT64STATS);486IPFW_UH_WUNLOCK(ch);487return (0);488}489490static struct ipfw_sopt_handler scodes[] = {491{ IP_FW_NAT64STL_CREATE, IP_FW3_OPVER, HDIR_SET, nat64stl_create },492{ IP_FW_NAT64STL_DESTROY, IP_FW3_OPVER, HDIR_SET, nat64stl_destroy },493{ IP_FW_NAT64STL_CONFIG, IP_FW3_OPVER, HDIR_BOTH,nat64stl_config },494{ IP_FW_NAT64STL_LIST, IP_FW3_OPVER, HDIR_GET, nat64stl_list },495{ IP_FW_NAT64STL_STATS, IP_FW3_OPVER, HDIR_GET, nat64stl_stats },496{ IP_FW_NAT64STL_RESET_STATS, IP_FW3_OPVER, HDIR_SET, nat64stl_reset_stats },497};498499static int500nat64stl_manage_sets(struct ip_fw_chain *ch, uint32_t set, uint8_t new_set,501enum ipfw_sets_cmd cmd)502{503504return (ipfw_obj_manage_sets(CHAIN_TO_SRV(ch), IPFW_TLV_NAT64STL_NAME,505set, new_set, cmd));506}507NAT64_DEFINE_OPCODE_REWRITER(nat64stl, NAT64STL, opcodes);508509static int510destroy_config_cb(struct namedobj_instance *ni, struct named_object *no,511void *arg)512{513struct nat64stl_cfg *cfg;514struct ip_fw_chain *ch;515516ch = (struct ip_fw_chain *)arg;517cfg = (struct nat64stl_cfg *)SRV_OBJECT(ch, no->kidx);518SRV_OBJECT(ch, no->kidx) = NULL;519nat64stl_detach_config(ch, cfg);520nat64stl_free_config(cfg);521return (0);522}523524int525nat64stl_init(struct ip_fw_chain *ch, int first)526{527528V_nat64stl_eid = ipfw_add_eaction(ch, ipfw_nat64stl, "nat64stl");529if (V_nat64stl_eid == 0)530return (ENXIO);531IPFW_ADD_SOPT_HANDLER(first, scodes);532IPFW_ADD_OBJ_REWRITER(first, opcodes);533return (0);534}535536void537nat64stl_uninit(struct ip_fw_chain *ch, int last)538{539540IPFW_DEL_OBJ_REWRITER(last, opcodes);541IPFW_DEL_SOPT_HANDLER(last, scodes);542ipfw_del_eaction(ch, V_nat64stl_eid);543/*544* Since we already have deregistered external action,545* our named objects become unaccessible via rules, because546* all rules were truncated by ipfw_del_eaction().547* So, we can unlink and destroy our named objects without holding548* IPFW_WLOCK().549*/550IPFW_UH_WLOCK(ch);551ipfw_objhash_foreach_type(CHAIN_TO_SRV(ch), destroy_config_cb, ch,552IPFW_TLV_NAT64STL_NAME);553V_nat64stl_eid = 0;554IPFW_UH_WUNLOCK(ch);555}556557558