Path: blob/main/sys/netpfil/ipfw/ip_fw_table_algo.c
39478 views
/*-1* SPDX-License-Identifier: BSD-2-Clause2*3* Copyright (c) 2014-2025 Yandex LLC4* Copyright (c) 2014 Alexander V. Chernikov5*6* Redistribution and use in source and binary forms, with or without7* modification, are permitted provided that the following conditions8* are met:9* 1. Redistributions of source code must retain the above copyright10* notice, this list of conditions and the following disclaimer.11* 2. Redistributions in binary form must reproduce the above copyright12* notice, this list of conditions and the following disclaimer in the13* documentation and/or other materials provided with the distribution.14*15* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND16* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE17* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE18* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE19* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL20* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS21* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)22* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT23* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY24* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF25* SUCH DAMAGE.26*/2728#include <sys/cdefs.h>29/*30* Lookup table algorithms.31*32*/3334#include "opt_ipfw.h"35#include "opt_inet.h"36#ifndef INET37#error IPFIREWALL requires INET.38#endif /* INET */39#include "opt_inet6.h"4041#include <sys/param.h>42#include <sys/systm.h>43#include <sys/malloc.h>44#include <sys/kernel.h>45#include <sys/lock.h>46#include <sys/rwlock.h>47#include <sys/rmlock.h>48#include <sys/socket.h>49#include <sys/queue.h>50#include <net/ethernet.h>51#include <net/if.h> /* ip_fw.h requires IFNAMSIZ */52#include <net/radix.h>53#include <net/route.h>54#include <net/route/nhop.h>55#include <net/route/route_ctl.h>5657#include <netinet/in.h>58#include <netinet/in_fib.h>59#include <netinet/ip_var.h> /* struct ipfw_rule_ref */60#include <netinet/ip_fw.h>61#include <netinet6/in6_fib.h>6263#include <netpfil/ipfw/ip_fw_private.h>64#include <netpfil/ipfw/ip_fw_table.h>6566/*67* IPFW table lookup algorithms.68*69* What is needed to add another table algo?70*71* Algo init:72* * struct table_algo has to be filled with:73* name: "type:algoname" format, e.g. "addr:radix". Currently74* there are the following types: "addr", "iface", "number" and "flow".75* type: one of IPFW_TABLE_* types76* flags: one or more TA_FLAGS_*77* ta_buf_size: size of structure used to store add/del item state.78* Needs to be less than TA_BUF_SZ.79* callbacks: see below for description.80* * ipfw_add_table_algo / ipfw_del_table_algo has to be called81*82* Callbacks description:83*84* -init: request to initialize new table instance.85* typedef int (ta_init)(struct ip_fw_chain *ch, void **ta_state,86* struct table_info *ti, char *data, uint8_t tflags);87* MANDATORY, unlocked. (M_WAITOK). Returns 0 on success.88*89* Allocate all structures needed for normal operations.90* * Caller may want to parse @data for some algo-specific91* options provided by userland.92* * Caller may want to save configuration state pointer to @ta_state93* * Caller needs to save desired runtime structure pointer(s)94* inside @ti fields. Note that it is not correct to save95* @ti pointer at this moment. Use -change_ti hook for that.96* * Caller has to fill in ti->lookup to appropriate function97* pointer.98*99*100*101* -destroy: request to destroy table instance.102* typedef void (ta_destroy)(void *ta_state, struct table_info *ti);103* MANDATORY, unlocked. (M_WAITOK).104*105* Frees all table entries and all tables structures allocated by -init.106*107*108*109* -prepare_add: request to allocate state for adding new entry.110* typedef int (ta_prepare_add)(struct ip_fw_chain *ch, struct tentry_info *tei,111* void *ta_buf);112* MANDATORY, unlocked. (M_WAITOK). Returns 0 on success.113*114* Allocates state and fills it in with all necessary data (EXCEPT value)115* from @tei to minimize operations needed to be done under WLOCK.116* "value" field has to be copied to new entry in @add callback.117* Buffer ta_buf of size ta->ta_buf_sz may be used to store118* allocated state.119*120*121*122* -prepare_del: request to set state for deleting existing entry.123* typedef int (ta_prepare_del)(struct ip_fw_chain *ch, struct tentry_info *tei,124* void *ta_buf);125* MANDATORY, locked, UH. (M_NOWAIT). Returns 0 on success.126*127* Buffer ta_buf of size ta->ta_buf_sz may be used to store128* allocated state. Caller should use on-stack ta_buf allocation129* instead of doing malloc().130*131*132*133* -add: request to insert new entry into runtime/config structures.134* typedef int (ta_add)(void *ta_state, struct table_info *ti,135* struct tentry_info *tei, void *ta_buf, uint32_t *pnum);136* MANDATORY, UH+WLOCK. (M_NOWAIT). Returns 0 on success.137*138* Insert new entry using previously-allocated state in @ta_buf.139* * @tei may have the following flags:140* TEI_FLAGS_UPDATE: request to add or update entry.141* TEI_FLAGS_DONTADD: request to update (but not add) entry.142* * Caller is required to do the following:143* copy real entry value from @tei144* entry added: return 0, set 1 to @pnum145* entry updated: return 0, store 0 to @pnum, store old value in @tei,146* add TEI_FLAGS_UPDATED flag to @tei.147* entry exists: return EEXIST148* entry not found: return ENOENT149* other error: return non-zero error code.150*151*152*153* -del: request to delete existing entry from runtime/config structures.154* typedef int (ta_del)(void *ta_state, struct table_info *ti,155* struct tentry_info *tei, void *ta_buf, uint32_t *pnum);156* MANDATORY, UH+WLOCK. (M_NOWAIT). Returns 0 on success.157*158* Delete entry using previously set up in @ta_buf.159* * Caller is required to do the following:160* entry deleted: return 0, set 1 to @pnum, store old value in @tei.161* entry not found: return ENOENT162* other error: return non-zero error code.163*164*165*166* -flush_entry: flush entry state created by -prepare_add / -del / others167* typedef void (ta_flush_entry)(struct ip_fw_chain *ch,168* struct tentry_info *tei, void *ta_buf);169* MANDATORY, may be locked. (M_NOWAIT).170*171* Delete state allocated by:172* -prepare_add (-add returned EEXIST|UPDATED)173* -prepare_del (if any)174* -del175* * Caller is required to handle empty @ta_buf correctly.176*177*178* -find_tentry: finds entry specified by key @tei179* typedef int ta_find_tentry(void *ta_state, struct table_info *ti,180* ipfw_obj_tentry *tent);181* OPTIONAL, locked (UH). (M_NOWAIT). Returns 0 on success.182*183* Finds entry specified by given key.184* * Caller is required to do the following:185* entry found: returns 0, export entry to @tent186* entry not found: returns ENOENT187*188*189* -need_modify: checks if @ti has enough space to hold another @count items.190* typedef int (ta_need_modify)(void *ta_state, struct table_info *ti,191* uint32_t count, uint64_t *pflags);192* OPTIONAL, locked (UH). (M_NOWAIT). Returns 0 if has.193*194* Checks if given table has enough space to add @count items without195* resize. Caller may use @pflags to store desired modification data.196*197*198*199* -prepare_mod: allocate structures for table modification.200* typedef int (ta_prepare_mod)(void *ta_buf, uint64_t *pflags);201* OPTIONAL(need_modify), unlocked. (M_WAITOK). Returns 0 on success.202*203* Allocate all needed state for table modification. Caller204* should use `struct mod_item` to store new state in @ta_buf.205* Up to TA_BUF_SZ (128 bytes) can be stored in @ta_buf.206*207*208*209* -fill_mod: copy some data to new state/210* typedef int (ta_fill_mod)(void *ta_state, struct table_info *ti,211* void *ta_buf, uint64_t *pflags);212* OPTIONAL(need_modify), locked (UH). (M_NOWAIT). Returns 0 on success.213*214* Copy as much data as we can to minimize changes under WLOCK.215* For example, array can be merged inside this callback.216*217*218*219* -modify: perform final modification.220* typedef void (ta_modify)(void *ta_state, struct table_info *ti,221* void *ta_buf, uint64_t pflags);222* OPTIONAL(need_modify), locked (UH+WLOCK). (M_NOWAIT).223*224* Performs all changes necessary to switch to new structures.225* * Caller should save old pointers to @ta_buf storage.226*227*228*229* -flush_mod: flush table modification state.230* typedef void (ta_flush_mod)(void *ta_buf);231* OPTIONAL(need_modify), unlocked. (M_WAITOK).232*233* Performs flush for the following:234* - prepare_mod (modification was not necessary)235* - modify (for the old state)236*237*238*239* -change_gi: monitor table info pointer changes240* typedef void (ta_change_ti)(void *ta_state, struct table_info *ti);241* OPTIONAL, locked (UH). (M_NOWAIT).242*243* Called on @ti pointer changed. Called immediately after -init244* to set initial state.245*246*247*248* -foreach: calls @f for each table entry249* typedef void ta_foreach(void *ta_state, struct table_info *ti,250* ta_foreach_f *f, void *arg);251* MANDATORY, locked(UH). (M_NOWAIT).252*253* Runs callback with specified argument for each table entry,254* Typically used for dumping table entries.255*256*257*258* -dump_tentry: dump table entry in current @tentry format.259* typedef int ta_dump_tentry(void *ta_state, struct table_info *ti, void *e,260* ipfw_obj_tentry *tent);261* MANDATORY, locked(UH). (M_NOWAIT). Returns 0 on success.262*263* Dumps entry @e to @tent.264*265*266* -print_config: prints custom algorithm options into buffer.267* typedef void (ta_print_config)(void *ta_state, struct table_info *ti,268* char *buf, size_t bufsize);269* OPTIONAL. locked(UH). (M_NOWAIT).270*271* Prints custom algorithm options in the format suitable to pass272* back to -init callback.273*274*275*276* -dump_tinfo: dumps algo-specific info.277* typedef void ta_dump_tinfo(void *ta_state, struct table_info *ti,278* ipfw_ta_tinfo *tinfo);279* OPTIONAL. locked(UH). (M_NOWAIT).280*281* Dumps options like items size/hash size, etc.282*/283284MALLOC_DEFINE(M_IPFW_TBL, "ipfw_tbl", "IpFw tables");285286/*287* Utility structures/functions common to more than one algo288*/289290struct mod_item {291void *main_ptr;292size_t size;293void *main_ptr6;294size_t size6;295};296297static int badd(const void *key, void *item, void *base, size_t nmemb,298size_t size, int (*compar) (const void *, const void *));299static int bdel(const void *key, void *base, size_t nmemb, size_t size,300int (*compar) (const void *, const void *));301302/*303* ADDR implementation using radix304*305*/306307/*308* The radix code expects addr and mask to be array of bytes,309* with the first byte being the length of the array. rn_inithead310* is called with the offset in bits of the lookup key within the311* array. If we use a sockaddr_in as the underlying type,312* sin_len is conveniently located at offset 0, sin_addr is at313* offset 4 and normally aligned.314* But for portability, let's avoid assumption and make the code explicit315*/316#define KEY_LEN(v) *((uint8_t *)&(v))317/*318* Do not require radix to compare more than actual IPv4/IPv6/MAC address319*/320#define KEY_LEN_INET (offsetof(struct sockaddr_in, sin_addr) + sizeof(in_addr_t))321#define KEY_LEN_INET6 (offsetof(struct sa_in6, sin6_addr) + sizeof(struct in6_addr))322#define KEY_LEN_MAC (offsetof(struct sa_mac, mac_addr) + ETHER_ADDR_LEN)323324#define OFF_LEN_INET (8 * offsetof(struct sockaddr_in, sin_addr))325#define OFF_LEN_INET6 (8 * offsetof(struct sa_in6, sin6_addr))326#define OFF_LEN_MAC (8 * offsetof(struct sa_mac, mac_addr))327328struct addr_radix_entry {329struct radix_node rn[2];330struct sockaddr_in addr;331uint32_t value;332uint8_t masklen;333};334335struct sa_in6 {336uint8_t sin6_len;337uint8_t sin6_family;338uint8_t pad[2];339struct in6_addr sin6_addr;340};341342struct addr_radix_xentry {343struct radix_node rn[2];344struct sa_in6 addr6;345uint32_t value;346uint8_t masklen;347};348349struct addr_radix_cfg {350struct radix_node_head *head4;351struct radix_node_head *head6;352size_t count4;353size_t count6;354};355356struct sa_mac {357uint8_t mac_len;358struct ether_addr mac_addr;359};360361struct ta_buf_radix362{363void *ent_ptr;364struct sockaddr *addr_ptr;365struct sockaddr *mask_ptr;366union {367struct {368struct sockaddr_in sa;369struct sockaddr_in ma;370} a4;371struct {372struct sa_in6 sa;373struct sa_in6 ma;374} a6;375struct {376struct sa_mac sa;377struct sa_mac ma;378} mac;379} addr;380};381382static int ta_lookup_addr_radix(struct table_info *ti, void *key, uint32_t keylen,383uint32_t *val);384static int ta_init_addr_radix(struct ip_fw_chain *ch, void **ta_state,385struct table_info *ti, char *data, uint8_t tflags);386static int flush_radix_entry(struct radix_node *rn, void *arg);387static void ta_destroy_addr_radix(void *ta_state, struct table_info *ti);388static void ta_dump_addr_radix_tinfo(void *ta_state, struct table_info *ti,389ipfw_ta_tinfo *tinfo);390static int ta_dump_addr_radix_tentry(void *ta_state, struct table_info *ti,391void *e, ipfw_obj_tentry *tent);392static int ta_find_addr_radix_tentry(void *ta_state, struct table_info *ti,393ipfw_obj_tentry *tent);394static void ta_foreach_addr_radix(void *ta_state, struct table_info *ti,395ta_foreach_f *f, void *arg);396static void tei_to_sockaddr_ent_addr(struct tentry_info *tei, struct sockaddr *sa,397struct sockaddr *ma, int *set_mask);398static int ta_prepare_add_addr_radix(struct ip_fw_chain *ch, struct tentry_info *tei,399void *ta_buf);400static int ta_add_addr_radix(void *ta_state, struct table_info *ti,401struct tentry_info *tei, void *ta_buf, uint32_t *pnum);402static int ta_prepare_del_addr_radix(struct ip_fw_chain *ch, struct tentry_info *tei,403void *ta_buf);404static int ta_del_addr_radix(void *ta_state, struct table_info *ti,405struct tentry_info *tei, void *ta_buf, uint32_t *pnum);406static void ta_flush_radix_entry(struct ip_fw_chain *ch, struct tentry_info *tei,407void *ta_buf);408static int ta_need_modify_radix(void *ta_state, struct table_info *ti,409uint32_t count, uint64_t *pflags);410411static int412ta_lookup_addr_radix(struct table_info *ti, void *key, uint32_t keylen,413uint32_t *val)414{415struct radix_node_head *rnh;416417if (keylen == sizeof(in_addr_t)) {418struct addr_radix_entry *ent;419struct sockaddr_in sa;420KEY_LEN(sa) = KEY_LEN_INET;421sa.sin_addr.s_addr = *((in_addr_t *)key);422rnh = (struct radix_node_head *)ti->state;423ent = (struct addr_radix_entry *)(rnh->rnh_matchaddr(&sa, &rnh->rh));424if (ent != NULL) {425*val = ent->value;426return (1);427}428} else if (keylen == sizeof(struct in6_addr)) {429struct addr_radix_xentry *xent;430struct sa_in6 sa6;431KEY_LEN(sa6) = KEY_LEN_INET6;432memcpy(&sa6.sin6_addr, key, sizeof(struct in6_addr));433rnh = (struct radix_node_head *)ti->xstate;434xent = (struct addr_radix_xentry *)(rnh->rnh_matchaddr(&sa6, &rnh->rh));435if (xent != NULL) {436*val = xent->value;437return (1);438}439}440441return (0);442}443444/*445* New table446*/447static int448ta_init_addr_radix(struct ip_fw_chain *ch, void **ta_state, struct table_info *ti,449char *data, uint8_t tflags)450{451struct addr_radix_cfg *cfg;452453if (!rn_inithead(&ti->state, OFF_LEN_INET))454return (ENOMEM);455if (!rn_inithead(&ti->xstate, OFF_LEN_INET6)) {456rn_detachhead(&ti->state);457return (ENOMEM);458}459460cfg = malloc(sizeof(struct addr_radix_cfg), M_IPFW, M_WAITOK | M_ZERO);461462*ta_state = cfg;463ti->lookup = ta_lookup_addr_radix;464465return (0);466}467468static int469flush_radix_entry(struct radix_node *rn, void *arg)470{471struct radix_node_head * const rnh = arg;472struct addr_radix_entry *ent;473474ent = (struct addr_radix_entry *)475rnh->rnh_deladdr(rn->rn_key, rn->rn_mask, &rnh->rh);476if (ent != NULL)477free(ent, M_IPFW_TBL);478return (0);479}480481static void482ta_destroy_addr_radix(void *ta_state, struct table_info *ti)483{484struct addr_radix_cfg *cfg;485struct radix_node_head *rnh;486487cfg = (struct addr_radix_cfg *)ta_state;488489rnh = (struct radix_node_head *)(ti->state);490rnh->rnh_walktree(&rnh->rh, flush_radix_entry, rnh);491rn_detachhead(&ti->state);492493rnh = (struct radix_node_head *)(ti->xstate);494rnh->rnh_walktree(&rnh->rh, flush_radix_entry, rnh);495rn_detachhead(&ti->xstate);496497free(cfg, M_IPFW);498}499500/*501* Provide algo-specific table info502*/503static void504ta_dump_addr_radix_tinfo(void *ta_state, struct table_info *ti, ipfw_ta_tinfo *tinfo)505{506struct addr_radix_cfg *cfg;507508cfg = (struct addr_radix_cfg *)ta_state;509510tinfo->flags = IPFW_TATFLAGS_AFDATA | IPFW_TATFLAGS_AFITEM;511tinfo->taclass4 = IPFW_TACLASS_RADIX;512tinfo->count4 = cfg->count4;513tinfo->itemsize4 = sizeof(struct addr_radix_entry);514tinfo->taclass6 = IPFW_TACLASS_RADIX;515tinfo->count6 = cfg->count6;516tinfo->itemsize6 = sizeof(struct addr_radix_xentry);517}518519static int520ta_dump_addr_radix_tentry(void *ta_state, struct table_info *ti, void *e,521ipfw_obj_tentry *tent)522{523struct addr_radix_entry *n;524#ifdef INET6525struct addr_radix_xentry *xn;526#endif527528n = (struct addr_radix_entry *)e;529530/* Guess IPv4/IPv6 radix by sockaddr family */531if (n->addr.sin_family == AF_INET) {532tent->k.addr.s_addr = n->addr.sin_addr.s_addr;533tent->masklen = n->masklen;534tent->subtype = AF_INET;535tent->v.kidx = n->value;536#ifdef INET6537} else {538xn = (struct addr_radix_xentry *)e;539memcpy(&tent->k.addr6, &xn->addr6.sin6_addr,540sizeof(struct in6_addr));541tent->masklen = xn->masklen;542tent->subtype = AF_INET6;543tent->v.kidx = xn->value;544#endif545}546547return (0);548}549550static int551ta_find_addr_radix_tentry(void *ta_state, struct table_info *ti,552ipfw_obj_tentry *tent)553{554struct radix_node_head *rnh;555void *e;556557e = NULL;558if (tent->subtype == AF_INET) {559struct sockaddr_in sa;560KEY_LEN(sa) = KEY_LEN_INET;561sa.sin_addr.s_addr = tent->k.addr.s_addr;562rnh = (struct radix_node_head *)ti->state;563e = rnh->rnh_matchaddr(&sa, &rnh->rh);564} else if (tent->subtype == AF_INET6) {565struct sa_in6 sa6;566KEY_LEN(sa6) = KEY_LEN_INET6;567memcpy(&sa6.sin6_addr, &tent->k.addr6, sizeof(struct in6_addr));568rnh = (struct radix_node_head *)ti->xstate;569e = rnh->rnh_matchaddr(&sa6, &rnh->rh);570}571572if (e != NULL) {573ta_dump_addr_radix_tentry(ta_state, ti, e, tent);574return (0);575}576577return (ENOENT);578}579580static void581ta_foreach_addr_radix(void *ta_state, struct table_info *ti, ta_foreach_f *f,582void *arg)583{584struct radix_node_head *rnh;585586rnh = (struct radix_node_head *)(ti->state);587rnh->rnh_walktree(&rnh->rh, (walktree_f_t *)f, arg);588589rnh = (struct radix_node_head *)(ti->xstate);590rnh->rnh_walktree(&rnh->rh, (walktree_f_t *)f, arg);591}592593#ifdef INET6594static inline void ipv6_writemask(struct in6_addr *addr6, uint8_t mask);595596static inline void597ipv6_writemask(struct in6_addr *addr6, uint8_t mask)598{599uint32_t *cp;600601for (cp = (uint32_t *)addr6; mask >= 32; mask -= 32)602*cp++ = 0xFFFFFFFF;603if (mask > 0)604*cp = htonl(mask ? ~((1 << (32 - mask)) - 1) : 0);605}606#endif607608static void609tei_to_sockaddr_ent_addr(struct tentry_info *tei, struct sockaddr *sa,610struct sockaddr *ma, int *set_mask)611{612int mlen;613#ifdef INET614struct sockaddr_in *addr, *mask;615#endif616#ifdef INET6617struct sa_in6 *addr6, *mask6;618#endif619in_addr_t a4;620621mlen = tei->masklen;622623if (tei->subtype == AF_INET) {624#ifdef INET625addr = (struct sockaddr_in *)sa;626mask = (struct sockaddr_in *)ma;627/* Set 'total' structure length */628KEY_LEN(*addr) = KEY_LEN_INET;629KEY_LEN(*mask) = KEY_LEN_INET;630addr->sin_family = AF_INET;631mask->sin_addr.s_addr =632htonl(mlen ? ~((1 << (32 - mlen)) - 1) : 0);633a4 = *((in_addr_t *)tei->paddr);634addr->sin_addr.s_addr = a4 & mask->sin_addr.s_addr;635if (mlen != 32)636*set_mask = 1;637else638*set_mask = 0;639#endif640#ifdef INET6641} else if (tei->subtype == AF_INET6) {642/* IPv6 case */643addr6 = (struct sa_in6 *)sa;644mask6 = (struct sa_in6 *)ma;645/* Set 'total' structure length */646KEY_LEN(*addr6) = KEY_LEN_INET6;647KEY_LEN(*mask6) = KEY_LEN_INET6;648addr6->sin6_family = AF_INET6;649ipv6_writemask(&mask6->sin6_addr, mlen);650memcpy(&addr6->sin6_addr, tei->paddr, sizeof(struct in6_addr));651APPLY_MASK(&addr6->sin6_addr, &mask6->sin6_addr);652if (mlen != 128)653*set_mask = 1;654else655*set_mask = 0;656#endif657}658}659660static int661ta_prepare_add_addr_radix(struct ip_fw_chain *ch, struct tentry_info *tei,662void *ta_buf)663{664struct ta_buf_radix *tb;665struct addr_radix_entry *ent;666#ifdef INET6667struct addr_radix_xentry *xent;668#endif669struct sockaddr *addr, *mask;670int mlen, set_mask;671672tb = (struct ta_buf_radix *)ta_buf;673674mlen = tei->masklen;675set_mask = 0;676677if (tei->subtype == AF_INET) {678#ifdef INET679if (mlen > 32)680return (EINVAL);681ent = malloc(sizeof(*ent), M_IPFW_TBL, M_WAITOK | M_ZERO);682ent->masklen = mlen;683684addr = (struct sockaddr *)&ent->addr;685mask = (struct sockaddr *)&tb->addr.a4.ma;686tb->ent_ptr = ent;687#endif688#ifdef INET6689} else if (tei->subtype == AF_INET6) {690/* IPv6 case */691if (mlen > 128)692return (EINVAL);693xent = malloc(sizeof(*xent), M_IPFW_TBL, M_WAITOK | M_ZERO);694xent->masklen = mlen;695696addr = (struct sockaddr *)&xent->addr6;697mask = (struct sockaddr *)&tb->addr.a6.ma;698tb->ent_ptr = xent;699#endif700} else {701/* Unknown CIDR type */702return (EINVAL);703}704705tei_to_sockaddr_ent_addr(tei, addr, mask, &set_mask);706/* Set pointers */707tb->addr_ptr = addr;708if (set_mask != 0)709tb->mask_ptr = mask;710711return (0);712}713714static int715ta_add_addr_radix(void *ta_state, struct table_info *ti, struct tentry_info *tei,716void *ta_buf, uint32_t *pnum)717{718struct addr_radix_cfg *cfg;719struct radix_node_head *rnh;720struct radix_node *rn;721struct ta_buf_radix *tb;722uint32_t *old_value, value;723724cfg = (struct addr_radix_cfg *)ta_state;725tb = (struct ta_buf_radix *)ta_buf;726727/* Save current entry value from @tei */728if (tei->subtype == AF_INET) {729rnh = ti->state;730((struct addr_radix_entry *)tb->ent_ptr)->value = tei->value;731} else {732rnh = ti->xstate;733((struct addr_radix_xentry *)tb->ent_ptr)->value = tei->value;734}735736/* Search for an entry first */737rn = rnh->rnh_lookup(tb->addr_ptr, tb->mask_ptr, &rnh->rh);738if (rn != NULL) {739if ((tei->flags & TEI_FLAGS_UPDATE) == 0)740return (EEXIST);741/* Record already exists. Update value if we're asked to */742if (tei->subtype == AF_INET)743old_value = &((struct addr_radix_entry *)rn)->value;744else745old_value = &((struct addr_radix_xentry *)rn)->value;746747value = *old_value;748*old_value = tei->value;749tei->value = value;750751/* Indicate that update has happened instead of addition */752tei->flags |= TEI_FLAGS_UPDATED;753*pnum = 0;754755return (0);756}757758if ((tei->flags & TEI_FLAGS_DONTADD) != 0)759return (EFBIG);760761rn = rnh->rnh_addaddr(tb->addr_ptr, tb->mask_ptr, &rnh->rh,tb->ent_ptr);762if (rn == NULL) {763/* Unknown error */764return (EINVAL);765}766767if (tei->subtype == AF_INET)768cfg->count4++;769else770cfg->count6++;771tb->ent_ptr = NULL;772*pnum = 1;773774return (0);775}776777static int778ta_prepare_del_addr_radix(struct ip_fw_chain *ch, struct tentry_info *tei,779void *ta_buf)780{781struct ta_buf_radix *tb;782struct sockaddr *addr, *mask;783int mlen, set_mask;784785tb = (struct ta_buf_radix *)ta_buf;786787mlen = tei->masklen;788set_mask = 0;789790if (tei->subtype == AF_INET) {791if (mlen > 32)792return (EINVAL);793794addr = (struct sockaddr *)&tb->addr.a4.sa;795mask = (struct sockaddr *)&tb->addr.a4.ma;796#ifdef INET6797} else if (tei->subtype == AF_INET6) {798if (mlen > 128)799return (EINVAL);800801addr = (struct sockaddr *)&tb->addr.a6.sa;802mask = (struct sockaddr *)&tb->addr.a6.ma;803#endif804} else805return (EINVAL);806807tei_to_sockaddr_ent_addr(tei, addr, mask, &set_mask);808tb->addr_ptr = addr;809if (set_mask != 0)810tb->mask_ptr = mask;811812return (0);813}814815static int816ta_del_addr_radix(void *ta_state, struct table_info *ti, struct tentry_info *tei,817void *ta_buf, uint32_t *pnum)818{819struct addr_radix_cfg *cfg;820struct radix_node_head *rnh;821struct radix_node *rn;822struct ta_buf_radix *tb;823824cfg = (struct addr_radix_cfg *)ta_state;825tb = (struct ta_buf_radix *)ta_buf;826827if (tei->subtype == AF_INET)828rnh = ti->state;829else830rnh = ti->xstate;831832rn = rnh->rnh_deladdr(tb->addr_ptr, tb->mask_ptr, &rnh->rh);833834if (rn == NULL)835return (ENOENT);836837/* Save entry value to @tei */838if (tei->subtype == AF_INET)839tei->value = ((struct addr_radix_entry *)rn)->value;840else841tei->value = ((struct addr_radix_xentry *)rn)->value;842843tb->ent_ptr = rn;844845if (tei->subtype == AF_INET)846cfg->count4--;847else848cfg->count6--;849*pnum = 1;850851return (0);852}853854static void855ta_flush_radix_entry(struct ip_fw_chain *ch, struct tentry_info *tei,856void *ta_buf)857{858struct ta_buf_radix *tb;859860tb = (struct ta_buf_radix *)ta_buf;861862if (tb->ent_ptr != NULL)863free(tb->ent_ptr, M_IPFW_TBL);864}865866static int867ta_need_modify_radix(void *ta_state, struct table_info *ti, uint32_t count,868uint64_t *pflags)869{870871/*872* radix does not require additional memory allocations873* other than nodes itself. Adding new masks to the tree do874* but we don't have any API to call (and we don't known which875* sizes do we need).876*/877return (0);878}879880struct table_algo addr_radix = {881.name = "addr:radix",882.type = IPFW_TABLE_ADDR,883.flags = TA_FLAG_DEFAULT,884.ta_buf_size = sizeof(struct ta_buf_radix),885.init = ta_init_addr_radix,886.destroy = ta_destroy_addr_radix,887.prepare_add = ta_prepare_add_addr_radix,888.prepare_del = ta_prepare_del_addr_radix,889.add = ta_add_addr_radix,890.del = ta_del_addr_radix,891.flush_entry = ta_flush_radix_entry,892.foreach = ta_foreach_addr_radix,893.dump_tentry = ta_dump_addr_radix_tentry,894.find_tentry = ta_find_addr_radix_tentry,895.dump_tinfo = ta_dump_addr_radix_tinfo,896.need_modify = ta_need_modify_radix,897};898899/*900* addr:hash cmds901*902*903* ti->data:904* [inv.mask4][inv.mask6][log2hsize4][log2hsize6]905* [ 8][ 8[ 8][ 8]906*907* inv.mask4: 32 - mask908* inv.mask6:909* 1) _slow lookup: mask910* 2) _aligned: (128 - mask) / 8911* 3) _64: 8912*913*914* pflags:915* [v4=1/v6=0][hsize]916* [ 32][ 32]917*/918919struct chashentry;920921SLIST_HEAD(chashbhead, chashentry);922923struct chash_cfg {924struct chashbhead *head4;925struct chashbhead *head6;926size_t size4;927size_t size6;928size_t items4;929size_t items6;930uint8_t mask4;931uint8_t mask6;932};933934struct chashentry {935SLIST_ENTRY(chashentry) next;936uint32_t value;937uint32_t type;938union {939uint32_t a4; /* Host format */940struct in6_addr a6; /* Network format */941} a;942};943944struct ta_buf_chash945{946void *ent_ptr;947struct chashentry ent;948};949950#ifdef INET951static __inline uint32_t hash_ip(uint32_t addr, int hsize);952#endif953#ifdef INET6954static __inline uint32_t hash_ip6(struct in6_addr *addr6, int hsize);955static __inline uint16_t hash_ip64(struct in6_addr *addr6, int hsize);956static __inline uint32_t hash_ip6_slow(struct in6_addr *addr6, void *key,957int mask, int hsize);958static __inline uint32_t hash_ip6_al(struct in6_addr *addr6, void *key, int mask,959int hsize);960#endif961static int ta_lookup_chash_slow(struct table_info *ti, void *key, uint32_t keylen,962uint32_t *val);963static int ta_lookup_chash_aligned(struct table_info *ti, void *key,964uint32_t keylen, uint32_t *val);965static int ta_lookup_chash_64(struct table_info *ti, void *key, uint32_t keylen,966uint32_t *val);967static int chash_parse_opts(struct chash_cfg *cfg, char *data);968static void ta_print_chash_config(void *ta_state, struct table_info *ti,969char *buf, size_t bufsize);970static int ta_log2(uint32_t v);971static int ta_init_chash(struct ip_fw_chain *ch, void **ta_state,972struct table_info *ti, char *data, uint8_t tflags);973static void ta_destroy_chash(void *ta_state, struct table_info *ti);974static void ta_dump_chash_tinfo(void *ta_state, struct table_info *ti,975ipfw_ta_tinfo *tinfo);976static int ta_dump_chash_tentry(void *ta_state, struct table_info *ti,977void *e, ipfw_obj_tentry *tent);978static uint32_t hash_ent(struct chashentry *ent, int af, int mlen,979uint32_t size);980static int tei_to_chash_ent(struct tentry_info *tei, struct chashentry *ent);981static int ta_find_chash_tentry(void *ta_state, struct table_info *ti,982ipfw_obj_tentry *tent);983static void ta_foreach_chash(void *ta_state, struct table_info *ti,984ta_foreach_f *f, void *arg);985static int ta_prepare_add_chash(struct ip_fw_chain *ch, struct tentry_info *tei,986void *ta_buf);987static int ta_add_chash(void *ta_state, struct table_info *ti,988struct tentry_info *tei, void *ta_buf, uint32_t *pnum);989static int ta_prepare_del_chash(struct ip_fw_chain *ch, struct tentry_info *tei,990void *ta_buf);991static int ta_del_chash(void *ta_state, struct table_info *ti,992struct tentry_info *tei, void *ta_buf, uint32_t *pnum);993static void ta_flush_chash_entry(struct ip_fw_chain *ch, struct tentry_info *tei,994void *ta_buf);995static int ta_need_modify_chash(void *ta_state, struct table_info *ti,996uint32_t count, uint64_t *pflags);997static int ta_prepare_mod_chash(void *ta_buf, uint64_t *pflags);998static int ta_fill_mod_chash(void *ta_state, struct table_info *ti, void *ta_buf,999uint64_t *pflags);1000static void ta_modify_chash(void *ta_state, struct table_info *ti, void *ta_buf,1001uint64_t pflags);1002static void ta_flush_mod_chash(void *ta_buf);10031004#ifdef INET1005static __inline uint32_t1006hash_ip(uint32_t addr, int hsize)1007{10081009return (addr % (hsize - 1));1010}1011#endif10121013#ifdef INET61014static __inline uint32_t1015hash_ip6(struct in6_addr *addr6, int hsize)1016{1017uint32_t i;10181019i = addr6->s6_addr32[0] ^ addr6->s6_addr32[1] ^1020addr6->s6_addr32[2] ^ addr6->s6_addr32[3];10211022return (i % (hsize - 1));1023}10241025static __inline uint16_t1026hash_ip64(struct in6_addr *addr6, int hsize)1027{1028uint32_t i;10291030i = addr6->s6_addr32[0] ^ addr6->s6_addr32[1];10311032return (i % (hsize - 1));1033}10341035static __inline uint32_t1036hash_ip6_slow(struct in6_addr *addr6, void *key, int mask, int hsize)1037{1038struct in6_addr mask6;10391040ipv6_writemask(&mask6, mask);1041memcpy(addr6, key, sizeof(struct in6_addr));1042APPLY_MASK(addr6, &mask6);1043return (hash_ip6(addr6, hsize));1044}10451046static __inline uint32_t1047hash_ip6_al(struct in6_addr *addr6, void *key, int mask, int hsize)1048{1049uint64_t *paddr;10501051paddr = (uint64_t *)addr6;1052*paddr = 0;1053*(paddr + 1) = 0;1054memcpy(addr6, key, mask);1055return (hash_ip6(addr6, hsize));1056}1057#endif10581059static int1060ta_lookup_chash_slow(struct table_info *ti, void *key, uint32_t keylen,1061uint32_t *val)1062{1063struct chashbhead *head;1064struct chashentry *ent;1065uint16_t hash, hsize;1066uint8_t imask;10671068if (keylen == sizeof(in_addr_t)) {1069#ifdef INET1070head = (struct chashbhead *)ti->state;1071imask = ti->data >> 24;1072hsize = 1 << ((ti->data & 0xFFFF) >> 8);1073uint32_t a;1074a = ntohl(*((in_addr_t *)key));1075a = a >> imask;1076hash = hash_ip(a, hsize);1077SLIST_FOREACH(ent, &head[hash], next) {1078if (ent->a.a4 == a) {1079*val = ent->value;1080return (1);1081}1082}1083#endif1084} else {1085#ifdef INET61086/* IPv6: worst scenario: non-round mask */1087struct in6_addr addr6;1088head = (struct chashbhead *)ti->xstate;1089imask = (ti->data & 0xFF0000) >> 16;1090hsize = 1 << (ti->data & 0xFF);1091hash = hash_ip6_slow(&addr6, key, imask, hsize);1092SLIST_FOREACH(ent, &head[hash], next) {1093if (memcmp(&ent->a.a6, &addr6, 16) == 0) {1094*val = ent->value;1095return (1);1096}1097}1098#endif1099}11001101return (0);1102}11031104static int1105ta_lookup_chash_aligned(struct table_info *ti, void *key, uint32_t keylen,1106uint32_t *val)1107{1108struct chashbhead *head;1109struct chashentry *ent;1110uint16_t hash, hsize;1111uint8_t imask;11121113if (keylen == sizeof(in_addr_t)) {1114#ifdef INET1115head = (struct chashbhead *)ti->state;1116imask = ti->data >> 24;1117hsize = 1 << ((ti->data & 0xFFFF) >> 8);1118uint32_t a;1119a = ntohl(*((in_addr_t *)key));1120a = a >> imask;1121hash = hash_ip(a, hsize);1122SLIST_FOREACH(ent, &head[hash], next) {1123if (ent->a.a4 == a) {1124*val = ent->value;1125return (1);1126}1127}1128#endif1129} else {1130#ifdef INET61131/* IPv6: aligned to 8bit mask */1132struct in6_addr addr6;1133uint64_t *paddr, *ptmp;1134head = (struct chashbhead *)ti->xstate;1135imask = (ti->data & 0xFF0000) >> 16;1136hsize = 1 << (ti->data & 0xFF);11371138hash = hash_ip6_al(&addr6, key, imask, hsize);1139paddr = (uint64_t *)&addr6;1140SLIST_FOREACH(ent, &head[hash], next) {1141ptmp = (uint64_t *)&ent->a.a6;1142if (paddr[0] == ptmp[0] && paddr[1] == ptmp[1]) {1143*val = ent->value;1144return (1);1145}1146}1147#endif1148}11491150return (0);1151}11521153static int1154ta_lookup_chash_64(struct table_info *ti, void *key, uint32_t keylen,1155uint32_t *val)1156{1157struct chashbhead *head;1158struct chashentry *ent;1159uint16_t hash, hsize;1160uint8_t imask;11611162if (keylen == sizeof(in_addr_t)) {1163#ifdef INET1164head = (struct chashbhead *)ti->state;1165imask = ti->data >> 24;1166hsize = 1 << ((ti->data & 0xFFFF) >> 8);1167uint32_t a;1168a = ntohl(*((in_addr_t *)key));1169a = a >> imask;1170hash = hash_ip(a, hsize);1171SLIST_FOREACH(ent, &head[hash], next) {1172if (ent->a.a4 == a) {1173*val = ent->value;1174return (1);1175}1176}1177#endif1178} else {1179#ifdef INET61180/* IPv6: /64 */1181uint64_t a6, *paddr;1182head = (struct chashbhead *)ti->xstate;1183paddr = (uint64_t *)key;1184hsize = 1 << (ti->data & 0xFF);1185a6 = *paddr;1186hash = hash_ip64((struct in6_addr *)key, hsize);1187SLIST_FOREACH(ent, &head[hash], next) {1188paddr = (uint64_t *)&ent->a.a6;1189if (a6 == *paddr) {1190*val = ent->value;1191return (1);1192}1193}1194#endif1195}11961197return (0);1198}11991200static int1201chash_parse_opts(struct chash_cfg *cfg, char *data)1202{1203char *pdel, *pend, *s;1204int mask4, mask6;12051206mask4 = cfg->mask4;1207mask6 = cfg->mask6;12081209if (data == NULL)1210return (0);1211if ((pdel = strchr(data, ' ')) == NULL)1212return (0);1213while (*pdel == ' ')1214pdel++;1215if (strncmp(pdel, "masks=", 6) != 0)1216return (EINVAL);1217if ((s = strchr(pdel, ' ')) != NULL)1218*s++ = '\0';12191220pdel += 6;1221/* Need /XX[,/YY] */1222if (*pdel++ != '/')1223return (EINVAL);1224mask4 = strtol(pdel, &pend, 10);1225if (*pend == ',') {1226/* ,/YY */1227pdel = pend + 1;1228if (*pdel++ != '/')1229return (EINVAL);1230mask6 = strtol(pdel, &pend, 10);1231if (*pend != '\0')1232return (EINVAL);1233} else if (*pend != '\0')1234return (EINVAL);12351236if (mask4 < 0 || mask4 > 32 || mask6 < 0 || mask6 > 128)1237return (EINVAL);12381239cfg->mask4 = mask4;1240cfg->mask6 = mask6;12411242return (0);1243}12441245static void1246ta_print_chash_config(void *ta_state, struct table_info *ti, char *buf,1247size_t bufsize)1248{1249struct chash_cfg *cfg;12501251cfg = (struct chash_cfg *)ta_state;12521253if (cfg->mask4 != 32 || cfg->mask6 != 128)1254snprintf(buf, bufsize, "%s masks=/%d,/%d", "addr:hash",1255cfg->mask4, cfg->mask6);1256else1257snprintf(buf, bufsize, "%s", "addr:hash");1258}12591260static int1261ta_log2(uint32_t v)1262{1263uint32_t r;12641265r = 0;1266while (v >>= 1)1267r++;12681269return (r);1270}12711272/*1273* New table.1274* We assume 'data' to be either NULL or the following format:1275* 'addr:hash [masks=/32[,/128]]'1276*/1277static int1278ta_init_chash(struct ip_fw_chain *ch, void **ta_state, struct table_info *ti,1279char *data, uint8_t tflags)1280{1281int error, i;1282uint32_t hsize;1283struct chash_cfg *cfg;12841285cfg = malloc(sizeof(struct chash_cfg), M_IPFW, M_WAITOK | M_ZERO);12861287cfg->mask4 = 32;1288cfg->mask6 = 128;12891290if ((error = chash_parse_opts(cfg, data)) != 0) {1291free(cfg, M_IPFW);1292return (error);1293}12941295cfg->size4 = 128;1296cfg->size6 = 128;12971298cfg->head4 = malloc(sizeof(struct chashbhead) * cfg->size4, M_IPFW,1299M_WAITOK | M_ZERO);1300cfg->head6 = malloc(sizeof(struct chashbhead) * cfg->size6, M_IPFW,1301M_WAITOK | M_ZERO);1302for (i = 0; i < cfg->size4; i++)1303SLIST_INIT(&cfg->head4[i]);1304for (i = 0; i < cfg->size6; i++)1305SLIST_INIT(&cfg->head6[i]);13061307*ta_state = cfg;1308ti->state = cfg->head4;1309ti->xstate = cfg->head6;13101311/* Store data depending on v6 mask length */1312hsize = ta_log2(cfg->size4) << 8 | ta_log2(cfg->size6);1313if (cfg->mask6 == 64) {1314ti->data = (32 - cfg->mask4) << 24 | (128 - cfg->mask6) << 16|1315hsize;1316ti->lookup = ta_lookup_chash_64;1317} else if ((cfg->mask6 % 8) == 0) {1318ti->data = (32 - cfg->mask4) << 24 |1319cfg->mask6 << 13 | hsize;1320ti->lookup = ta_lookup_chash_aligned;1321} else {1322/* don't do that! */1323ti->data = (32 - cfg->mask4) << 24 |1324cfg->mask6 << 16 | hsize;1325ti->lookup = ta_lookup_chash_slow;1326}13271328return (0);1329}13301331static void1332ta_destroy_chash(void *ta_state, struct table_info *ti)1333{1334struct chash_cfg *cfg;1335struct chashentry *ent, *ent_next;1336int i;13371338cfg = (struct chash_cfg *)ta_state;13391340for (i = 0; i < cfg->size4; i++)1341SLIST_FOREACH_SAFE(ent, &cfg->head4[i], next, ent_next)1342free(ent, M_IPFW_TBL);13431344for (i = 0; i < cfg->size6; i++)1345SLIST_FOREACH_SAFE(ent, &cfg->head6[i], next, ent_next)1346free(ent, M_IPFW_TBL);13471348free(cfg->head4, M_IPFW);1349free(cfg->head6, M_IPFW);13501351free(cfg, M_IPFW);1352}13531354static void1355ta_dump_chash_tinfo(void *ta_state, struct table_info *ti, ipfw_ta_tinfo *tinfo)1356{1357struct chash_cfg *cfg;13581359cfg = (struct chash_cfg *)ta_state;13601361tinfo->flags = IPFW_TATFLAGS_AFDATA | IPFW_TATFLAGS_AFITEM;1362tinfo->taclass4 = IPFW_TACLASS_HASH;1363tinfo->size4 = cfg->size4;1364tinfo->count4 = cfg->items4;1365tinfo->itemsize4 = sizeof(struct chashentry);1366tinfo->taclass6 = IPFW_TACLASS_HASH;1367tinfo->size6 = cfg->size6;1368tinfo->count6 = cfg->items6;1369tinfo->itemsize6 = sizeof(struct chashentry);1370}13711372static int1373ta_dump_chash_tentry(void *ta_state, struct table_info *ti, void *e,1374ipfw_obj_tentry *tent)1375{1376struct chash_cfg *cfg;1377struct chashentry *ent;13781379cfg = (struct chash_cfg *)ta_state;1380ent = (struct chashentry *)e;13811382if (ent->type == AF_INET) {1383tent->k.addr.s_addr = htonl(ent->a.a4 << (32 - cfg->mask4));1384tent->masklen = cfg->mask4;1385tent->subtype = AF_INET;1386tent->v.kidx = ent->value;1387#ifdef INET61388} else {1389memcpy(&tent->k.addr6, &ent->a.a6, sizeof(struct in6_addr));1390tent->masklen = cfg->mask6;1391tent->subtype = AF_INET6;1392tent->v.kidx = ent->value;1393#endif1394}13951396return (0);1397}13981399static uint32_t1400hash_ent(struct chashentry *ent, int af, int mlen, uint32_t size)1401{1402uint32_t hash;14031404hash = 0;14051406if (af == AF_INET) {1407#ifdef INET1408hash = hash_ip(ent->a.a4, size);1409#endif1410} else {1411#ifdef INET61412if (mlen == 64)1413hash = hash_ip64(&ent->a.a6, size);1414else1415hash = hash_ip6(&ent->a.a6, size);1416#endif1417}14181419return (hash);1420}14211422static int1423tei_to_chash_ent(struct tentry_info *tei, struct chashentry *ent)1424{1425int mlen;1426#ifdef INET61427struct in6_addr mask6;1428#endif14291430mlen = tei->masklen;14311432if (tei->subtype == AF_INET) {1433#ifdef INET1434if (mlen > 32)1435return (EINVAL);1436ent->type = AF_INET;14371438/* Calculate masked address */1439ent->a.a4 = ntohl(*((in_addr_t *)tei->paddr)) >> (32 - mlen);1440#endif1441#ifdef INET61442} else if (tei->subtype == AF_INET6) {1443/* IPv6 case */1444if (mlen > 128)1445return (EINVAL);1446ent->type = AF_INET6;14471448ipv6_writemask(&mask6, mlen);1449memcpy(&ent->a.a6, tei->paddr, sizeof(struct in6_addr));1450APPLY_MASK(&ent->a.a6, &mask6);1451#endif1452} else {1453/* Unknown CIDR type */1454return (EINVAL);1455}14561457return (0);1458}14591460static int1461ta_find_chash_tentry(void *ta_state, struct table_info *ti,1462ipfw_obj_tentry *tent)1463{1464struct chash_cfg *cfg;1465struct chashbhead *head;1466struct chashentry ent, *tmp;1467struct tentry_info tei;1468int error;1469uint32_t hash;14701471cfg = (struct chash_cfg *)ta_state;14721473memset(&ent, 0, sizeof(ent));1474memset(&tei, 0, sizeof(tei));14751476if (tent->subtype == AF_INET) {1477tei.paddr = &tent->k.addr;1478tei.masklen = cfg->mask4;1479tei.subtype = AF_INET;14801481if ((error = tei_to_chash_ent(&tei, &ent)) != 0)1482return (error);14831484head = cfg->head4;1485hash = hash_ent(&ent, AF_INET, cfg->mask4, cfg->size4);1486/* Check for existence */1487SLIST_FOREACH(tmp, &head[hash], next) {1488if (tmp->a.a4 != ent.a.a4)1489continue;14901491ta_dump_chash_tentry(ta_state, ti, tmp, tent);1492return (0);1493}1494} else {1495tei.paddr = &tent->k.addr6;1496tei.masklen = cfg->mask6;1497tei.subtype = AF_INET6;14981499if ((error = tei_to_chash_ent(&tei, &ent)) != 0)1500return (error);15011502head = cfg->head6;1503hash = hash_ent(&ent, AF_INET6, cfg->mask6, cfg->size6);1504/* Check for existence */1505SLIST_FOREACH(tmp, &head[hash], next) {1506if (memcmp(&tmp->a.a6, &ent.a.a6, 16) != 0)1507continue;1508ta_dump_chash_tentry(ta_state, ti, tmp, tent);1509return (0);1510}1511}15121513return (ENOENT);1514}15151516static void1517ta_foreach_chash(void *ta_state, struct table_info *ti, ta_foreach_f *f,1518void *arg)1519{1520struct chash_cfg *cfg;1521struct chashentry *ent, *ent_next;1522int i;15231524cfg = (struct chash_cfg *)ta_state;15251526for (i = 0; i < cfg->size4; i++)1527SLIST_FOREACH_SAFE(ent, &cfg->head4[i], next, ent_next)1528f(ent, arg);15291530for (i = 0; i < cfg->size6; i++)1531SLIST_FOREACH_SAFE(ent, &cfg->head6[i], next, ent_next)1532f(ent, arg);1533}15341535static int1536ta_prepare_add_chash(struct ip_fw_chain *ch, struct tentry_info *tei,1537void *ta_buf)1538{1539struct ta_buf_chash *tb;1540struct chashentry *ent;1541int error;15421543tb = (struct ta_buf_chash *)ta_buf;15441545ent = malloc(sizeof(*ent), M_IPFW_TBL, M_WAITOK | M_ZERO);15461547error = tei_to_chash_ent(tei, ent);1548if (error != 0) {1549free(ent, M_IPFW_TBL);1550return (error);1551}1552tb->ent_ptr = ent;15531554return (0);1555}15561557static int1558ta_add_chash(void *ta_state, struct table_info *ti, struct tentry_info *tei,1559void *ta_buf, uint32_t *pnum)1560{1561struct chash_cfg *cfg;1562struct chashbhead *head;1563struct chashentry *ent, *tmp;1564struct ta_buf_chash *tb;1565int exists;1566uint32_t hash, value;15671568cfg = (struct chash_cfg *)ta_state;1569tb = (struct ta_buf_chash *)ta_buf;1570ent = (struct chashentry *)tb->ent_ptr;1571hash = 0;1572exists = 0;15731574/* Read current value from @tei */1575ent->value = tei->value;15761577/* Read cuurrent value */1578if (tei->subtype == AF_INET) {1579if (tei->masklen != cfg->mask4)1580return (EINVAL);1581head = cfg->head4;1582hash = hash_ent(ent, AF_INET, cfg->mask4, cfg->size4);15831584/* Check for existence */1585SLIST_FOREACH(tmp, &head[hash], next) {1586if (tmp->a.a4 == ent->a.a4) {1587exists = 1;1588break;1589}1590}1591} else {1592if (tei->masklen != cfg->mask6)1593return (EINVAL);1594head = cfg->head6;1595hash = hash_ent(ent, AF_INET6, cfg->mask6, cfg->size6);1596/* Check for existence */1597SLIST_FOREACH(tmp, &head[hash], next) {1598if (memcmp(&tmp->a.a6, &ent->a.a6, 16) == 0) {1599exists = 1;1600break;1601}1602}1603}16041605if (exists == 1) {1606if ((tei->flags & TEI_FLAGS_UPDATE) == 0)1607return (EEXIST);1608/* Record already exists. Update value if we're asked to */1609value = tmp->value;1610tmp->value = tei->value;1611tei->value = value;1612/* Indicate that update has happened instead of addition */1613tei->flags |= TEI_FLAGS_UPDATED;1614*pnum = 0;1615} else {1616if ((tei->flags & TEI_FLAGS_DONTADD) != 0)1617return (EFBIG);1618SLIST_INSERT_HEAD(&head[hash], ent, next);1619tb->ent_ptr = NULL;1620*pnum = 1;16211622/* Update counters */1623if (tei->subtype == AF_INET)1624cfg->items4++;1625else1626cfg->items6++;1627}16281629return (0);1630}16311632static int1633ta_prepare_del_chash(struct ip_fw_chain *ch, struct tentry_info *tei,1634void *ta_buf)1635{1636struct ta_buf_chash *tb;16371638tb = (struct ta_buf_chash *)ta_buf;16391640return (tei_to_chash_ent(tei, &tb->ent));1641}16421643static int1644ta_del_chash(void *ta_state, struct table_info *ti, struct tentry_info *tei,1645void *ta_buf, uint32_t *pnum)1646{1647struct chash_cfg *cfg;1648struct chashbhead *head;1649struct chashentry *tmp, *tmp_next, *ent;1650struct ta_buf_chash *tb;1651uint32_t hash;16521653cfg = (struct chash_cfg *)ta_state;1654tb = (struct ta_buf_chash *)ta_buf;1655ent = &tb->ent;16561657if (tei->subtype == AF_INET) {1658if (tei->masklen != cfg->mask4)1659return (EINVAL);1660head = cfg->head4;1661hash = hash_ent(ent, AF_INET, cfg->mask4, cfg->size4);16621663SLIST_FOREACH_SAFE(tmp, &head[hash], next, tmp_next) {1664if (tmp->a.a4 != ent->a.a4)1665continue;16661667SLIST_REMOVE(&head[hash], tmp, chashentry, next);1668cfg->items4--;1669tb->ent_ptr = tmp;1670tei->value = tmp->value;1671*pnum = 1;1672return (0);1673}1674} else {1675if (tei->masklen != cfg->mask6)1676return (EINVAL);1677head = cfg->head6;1678hash = hash_ent(ent, AF_INET6, cfg->mask6, cfg->size6);1679SLIST_FOREACH_SAFE(tmp, &head[hash], next, tmp_next) {1680if (memcmp(&tmp->a.a6, &ent->a.a6, 16) != 0)1681continue;16821683SLIST_REMOVE(&head[hash], tmp, chashentry, next);1684cfg->items6--;1685tb->ent_ptr = tmp;1686tei->value = tmp->value;1687*pnum = 1;1688return (0);1689}1690}16911692return (ENOENT);1693}16941695static void1696ta_flush_chash_entry(struct ip_fw_chain *ch, struct tentry_info *tei,1697void *ta_buf)1698{1699struct ta_buf_chash *tb;17001701tb = (struct ta_buf_chash *)ta_buf;17021703if (tb->ent_ptr != NULL)1704free(tb->ent_ptr, M_IPFW_TBL);1705}17061707/*1708* Hash growing callbacks.1709*/17101711static int1712ta_need_modify_chash(void *ta_state, struct table_info *ti, uint32_t count,1713uint64_t *pflags)1714{1715struct chash_cfg *cfg;1716uint64_t data;17171718/*1719* Since we don't know exact number of IPv4/IPv6 records in @count,1720* ignore non-zero @count value at all. Check current hash sizes1721* and return appropriate data.1722*/17231724cfg = (struct chash_cfg *)ta_state;17251726data = 0;1727if (cfg->items4 > cfg->size4 && cfg->size4 < 65536)1728data |= (cfg->size4 * 2) << 16;1729if (cfg->items6 > cfg->size6 && cfg->size6 < 65536)1730data |= cfg->size6 * 2;17311732if (data != 0) {1733*pflags = data;1734return (1);1735}17361737return (0);1738}17391740/*1741* Allocate new, larger chash.1742*/1743static int1744ta_prepare_mod_chash(void *ta_buf, uint64_t *pflags)1745{1746struct mod_item *mi;1747struct chashbhead *head;1748int i;17491750mi = (struct mod_item *)ta_buf;17511752memset(mi, 0, sizeof(struct mod_item));1753mi->size = (*pflags >> 16) & 0xFFFF;1754mi->size6 = *pflags & 0xFFFF;1755if (mi->size > 0) {1756head = malloc(sizeof(struct chashbhead) * mi->size,1757M_IPFW, M_WAITOK | M_ZERO);1758for (i = 0; i < mi->size; i++)1759SLIST_INIT(&head[i]);1760mi->main_ptr = head;1761}17621763if (mi->size6 > 0) {1764head = malloc(sizeof(struct chashbhead) * mi->size6,1765M_IPFW, M_WAITOK | M_ZERO);1766for (i = 0; i < mi->size6; i++)1767SLIST_INIT(&head[i]);1768mi->main_ptr6 = head;1769}17701771return (0);1772}17731774/*1775* Copy data from old runtime array to new one.1776*/1777static int1778ta_fill_mod_chash(void *ta_state, struct table_info *ti, void *ta_buf,1779uint64_t *pflags)1780{17811782/* In is not possible to do rehash if we're not holidng WLOCK. */1783return (0);1784}17851786/*1787* Switch old & new arrays.1788*/1789static void1790ta_modify_chash(void *ta_state, struct table_info *ti, void *ta_buf,1791uint64_t pflags)1792{1793struct mod_item *mi;1794struct chash_cfg *cfg;1795struct chashbhead *old_head, *new_head;1796struct chashentry *ent, *ent_next;1797int af, i, mlen;1798uint32_t nhash;1799size_t old_size, new_size;18001801mi = (struct mod_item *)ta_buf;1802cfg = (struct chash_cfg *)ta_state;18031804/* Check which hash we need to grow and do we still need that */1805if (mi->size > 0 && cfg->size4 < mi->size) {1806new_head = (struct chashbhead *)mi->main_ptr;1807new_size = mi->size;1808old_size = cfg->size4;1809old_head = ti->state;1810mlen = cfg->mask4;1811af = AF_INET;18121813for (i = 0; i < old_size; i++) {1814SLIST_FOREACH_SAFE(ent, &old_head[i], next, ent_next) {1815nhash = hash_ent(ent, af, mlen, new_size);1816SLIST_INSERT_HEAD(&new_head[nhash], ent, next);1817}1818}18191820ti->state = new_head;1821cfg->head4 = new_head;1822cfg->size4 = mi->size;1823mi->main_ptr = old_head;1824}18251826if (mi->size6 > 0 && cfg->size6 < mi->size6) {1827new_head = (struct chashbhead *)mi->main_ptr6;1828new_size = mi->size6;1829old_size = cfg->size6;1830old_head = ti->xstate;1831mlen = cfg->mask6;1832af = AF_INET6;18331834for (i = 0; i < old_size; i++) {1835SLIST_FOREACH_SAFE(ent, &old_head[i], next, ent_next) {1836nhash = hash_ent(ent, af, mlen, new_size);1837SLIST_INSERT_HEAD(&new_head[nhash], ent, next);1838}1839}18401841ti->xstate = new_head;1842cfg->head6 = new_head;1843cfg->size6 = mi->size6;1844mi->main_ptr6 = old_head;1845}18461847/* Update lower 32 bits with new values */1848ti->data &= 0xFFFFFFFF00000000;1849ti->data |= ta_log2(cfg->size4) << 8 | ta_log2(cfg->size6);1850}18511852/*1853* Free unneded array.1854*/1855static void1856ta_flush_mod_chash(void *ta_buf)1857{1858struct mod_item *mi;18591860mi = (struct mod_item *)ta_buf;1861if (mi->main_ptr != NULL)1862free(mi->main_ptr, M_IPFW);1863if (mi->main_ptr6 != NULL)1864free(mi->main_ptr6, M_IPFW);1865}18661867struct table_algo addr_hash = {1868.name = "addr:hash",1869.type = IPFW_TABLE_ADDR,1870.ta_buf_size = sizeof(struct ta_buf_chash),1871.init = ta_init_chash,1872.destroy = ta_destroy_chash,1873.prepare_add = ta_prepare_add_chash,1874.prepare_del = ta_prepare_del_chash,1875.add = ta_add_chash,1876.del = ta_del_chash,1877.flush_entry = ta_flush_chash_entry,1878.foreach = ta_foreach_chash,1879.dump_tentry = ta_dump_chash_tentry,1880.find_tentry = ta_find_chash_tentry,1881.print_config = ta_print_chash_config,1882.dump_tinfo = ta_dump_chash_tinfo,1883.need_modify = ta_need_modify_chash,1884.prepare_mod = ta_prepare_mod_chash,1885.fill_mod = ta_fill_mod_chash,1886.modify = ta_modify_chash,1887.flush_mod = ta_flush_mod_chash,1888};18891890/*1891* Iface table cmds.1892*1893* Implementation:1894*1895* Runtime part:1896* - sorted array of "struct ifidx" pointed by ti->state.1897* Array is allocated with rounding up to IFIDX_CHUNK. Only existing1898* interfaces are stored in array, however its allocated size is1899* sufficient to hold all table records if needed.1900* - current array size is stored in ti->data1901*1902* Table data:1903* - "struct iftable_cfg" is allocated to store table state (ta_state).1904* - All table records are stored inside namedobj instance.1905*1906*/19071908struct ifidx {1909uint16_t kidx;1910uint16_t spare;1911uint32_t value;1912};1913#define DEFAULT_IFIDX_SIZE 6419141915struct iftable_cfg;19161917struct ifentry {1918struct named_object no;1919struct ipfw_ifc ic;1920struct iftable_cfg *icfg;1921uint32_t value;1922int linked;1923};19241925struct iftable_cfg {1926struct namedobj_instance *ii;1927struct ip_fw_chain *ch;1928struct table_info *ti;1929void *main_ptr;1930size_t size; /* Number of items allocated in array */1931size_t count; /* Number of all items */1932size_t used; /* Number of items _active_ now */1933};19341935struct ta_buf_ifidx1936{1937struct ifentry *ife;1938uint32_t value;1939};19401941int compare_ifidx(const void *k, const void *v);1942static struct ifidx * ifidx_find(struct table_info *ti, void *key);1943static int ta_lookup_ifidx(struct table_info *ti, void *key, uint32_t keylen,1944uint32_t *val);1945static int ta_init_ifidx(struct ip_fw_chain *ch, void **ta_state,1946struct table_info *ti, char *data, uint8_t tflags);1947static void ta_change_ti_ifidx(void *ta_state, struct table_info *ti);1948static int destroy_ifidx_locked(struct namedobj_instance *ii,1949struct named_object *no, void *arg);1950static void ta_destroy_ifidx(void *ta_state, struct table_info *ti);1951static void ta_dump_ifidx_tinfo(void *ta_state, struct table_info *ti,1952ipfw_ta_tinfo *tinfo);1953static int ta_prepare_add_ifidx(struct ip_fw_chain *ch, struct tentry_info *tei,1954void *ta_buf);1955static int ta_add_ifidx(void *ta_state, struct table_info *ti,1956struct tentry_info *tei, void *ta_buf, uint32_t *pnum);1957static int ta_prepare_del_ifidx(struct ip_fw_chain *ch, struct tentry_info *tei,1958void *ta_buf);1959static int ta_del_ifidx(void *ta_state, struct table_info *ti,1960struct tentry_info *tei, void *ta_buf, uint32_t *pnum);1961static void ta_flush_ifidx_entry(struct ip_fw_chain *ch,1962struct tentry_info *tei, void *ta_buf);1963static void if_notifier(struct ip_fw_chain *ch, void *cbdata, uint16_t ifindex);1964static int ta_need_modify_ifidx(void *ta_state, struct table_info *ti,1965uint32_t count, uint64_t *pflags);1966static int ta_prepare_mod_ifidx(void *ta_buf, uint64_t *pflags);1967static int ta_fill_mod_ifidx(void *ta_state, struct table_info *ti,1968void *ta_buf, uint64_t *pflags);1969static void ta_modify_ifidx(void *ta_state, struct table_info *ti, void *ta_buf,1970uint64_t pflags);1971static void ta_flush_mod_ifidx(void *ta_buf);1972static int ta_dump_ifidx_tentry(void *ta_state, struct table_info *ti, void *e,1973ipfw_obj_tentry *tent);1974static int ta_find_ifidx_tentry(void *ta_state, struct table_info *ti,1975ipfw_obj_tentry *tent);1976static int foreach_ifidx(struct namedobj_instance *ii, struct named_object *no,1977void *arg);1978static void ta_foreach_ifidx(void *ta_state, struct table_info *ti,1979ta_foreach_f *f, void *arg);19801981int1982compare_ifidx(const void *k, const void *v)1983{1984const struct ifidx *ifidx;1985uint16_t key;19861987key = *((const uint16_t *)k);1988ifidx = (const struct ifidx *)v;19891990if (key < ifidx->kidx)1991return (-1);1992else if (key > ifidx->kidx)1993return (1);19941995return (0);1996}19971998/*1999* Adds item @item with key @key into ascending-sorted array @base.2000* Assumes @base has enough additional storage.2001*2002* Returns 1 on success, 0 on duplicate key.2003*/2004static int2005badd(const void *key, void *item, void *base, size_t nmemb,2006size_t size, int (*compar) (const void *, const void *))2007{2008int min, max, mid, shift, res;2009caddr_t paddr;20102011if (nmemb == 0) {2012memcpy(base, item, size);2013return (1);2014}20152016/* Binary search */2017min = 0;2018max = nmemb - 1;2019mid = 0;2020while (min <= max) {2021mid = (min + max) / 2;2022res = compar(key, (const void *)((caddr_t)base + mid * size));2023if (res == 0)2024return (0);20252026if (res > 0)2027min = mid + 1;2028else2029max = mid - 1;2030}20312032/* Item not found. */2033res = compar(key, (const void *)((caddr_t)base + mid * size));2034if (res > 0)2035shift = mid + 1;2036else2037shift = mid;20382039paddr = (caddr_t)base + shift * size;2040if (nmemb > shift)2041memmove(paddr + size, paddr, (nmemb - shift) * size);20422043memcpy(paddr, item, size);20442045return (1);2046}20472048/*2049* Deletes item with key @key from ascending-sorted array @base.2050*2051* Returns 1 on success, 0 for non-existent key.2052*/2053static int2054bdel(const void *key, void *base, size_t nmemb, size_t size,2055int (*compar) (const void *, const void *))2056{2057caddr_t item;2058size_t sz;20592060item = (caddr_t)bsearch(key, base, nmemb, size, compar);20612062if (item == NULL)2063return (0);20642065sz = (caddr_t)base + nmemb * size - item;20662067if (sz > 0)2068memmove(item, item + size, sz);20692070return (1);2071}20722073static struct ifidx *2074ifidx_find(struct table_info *ti, void *key)2075{2076struct ifidx *ifi;20772078ifi = bsearch(key, ti->state, ti->data, sizeof(struct ifidx),2079compare_ifidx);20802081return (ifi);2082}20832084static int2085ta_lookup_ifidx(struct table_info *ti, void *key, uint32_t keylen,2086uint32_t *val)2087{2088struct ifidx *ifi;20892090ifi = ifidx_find(ti, key);20912092if (ifi != NULL) {2093*val = ifi->value;2094return (1);2095}20962097return (0);2098}20992100static int2101ta_init_ifidx(struct ip_fw_chain *ch, void **ta_state, struct table_info *ti,2102char *data, uint8_t tflags)2103{2104struct iftable_cfg *icfg;21052106icfg = malloc(sizeof(struct iftable_cfg), M_IPFW, M_WAITOK | M_ZERO);21072108icfg->ii = ipfw_objhash_create(DEFAULT_IFIDX_SIZE, DEFAULT_OBJHASH_SIZE);2109icfg->size = DEFAULT_IFIDX_SIZE;2110icfg->main_ptr = malloc(sizeof(struct ifidx) * icfg->size, M_IPFW,2111M_WAITOK | M_ZERO);2112icfg->ch = ch;21132114*ta_state = icfg;2115ti->state = icfg->main_ptr;2116ti->lookup = ta_lookup_ifidx;21172118return (0);2119}21202121/*2122* Handle tableinfo @ti pointer change (on table array resize).2123*/2124static void2125ta_change_ti_ifidx(void *ta_state, struct table_info *ti)2126{2127struct iftable_cfg *icfg;21282129icfg = (struct iftable_cfg *)ta_state;2130icfg->ti = ti;2131}21322133static int2134destroy_ifidx_locked(struct namedobj_instance *ii, struct named_object *no,2135void *arg)2136{2137struct ifentry *ife;2138struct ip_fw_chain *ch;21392140ch = (struct ip_fw_chain *)arg;2141ife = (struct ifentry *)no;21422143ipfw_iface_del_notify(ch, &ife->ic);2144ipfw_iface_unref(ch, &ife->ic);2145free(ife, M_IPFW_TBL);2146return (0);2147}21482149/*2150* Destroys table @ti2151*/2152static void2153ta_destroy_ifidx(void *ta_state, struct table_info *ti)2154{2155struct iftable_cfg *icfg;2156struct ip_fw_chain *ch;21572158icfg = (struct iftable_cfg *)ta_state;2159ch = icfg->ch;21602161if (icfg->main_ptr != NULL)2162free(icfg->main_ptr, M_IPFW);21632164IPFW_UH_WLOCK(ch);2165ipfw_objhash_foreach(icfg->ii, destroy_ifidx_locked, ch);2166IPFW_UH_WUNLOCK(ch);21672168ipfw_objhash_destroy(icfg->ii);21692170free(icfg, M_IPFW);2171}21722173/*2174* Provide algo-specific table info2175*/2176static void2177ta_dump_ifidx_tinfo(void *ta_state, struct table_info *ti, ipfw_ta_tinfo *tinfo)2178{2179struct iftable_cfg *cfg;21802181cfg = (struct iftable_cfg *)ta_state;21822183tinfo->taclass4 = IPFW_TACLASS_ARRAY;2184tinfo->size4 = cfg->size;2185tinfo->count4 = cfg->used;2186tinfo->itemsize4 = sizeof(struct ifidx);2187}21882189/*2190* Prepare state to add to the table:2191* allocate ifentry and reference needed interface.2192*/2193static int2194ta_prepare_add_ifidx(struct ip_fw_chain *ch, struct tentry_info *tei,2195void *ta_buf)2196{2197struct ta_buf_ifidx *tb;2198char *ifname;2199struct ifentry *ife;22002201tb = (struct ta_buf_ifidx *)ta_buf;22022203/* Check if string is terminated */2204ifname = (char *)tei->paddr;2205if (strnlen(ifname, IF_NAMESIZE) == IF_NAMESIZE)2206return (EINVAL);22072208ife = malloc(sizeof(struct ifentry), M_IPFW_TBL, M_WAITOK | M_ZERO);2209ife->ic.cb = if_notifier;2210ife->ic.cbdata = ife;22112212if (ipfw_iface_ref(ch, ifname, &ife->ic) != 0) {2213free(ife, M_IPFW_TBL);2214return (EINVAL);2215}22162217/* Use ipfw_iface 'ifname' field as stable storage */2218ife->no.name = ife->ic.iface->ifname;22192220tb->ife = ife;22212222return (0);2223}22242225static int2226ta_add_ifidx(void *ta_state, struct table_info *ti, struct tentry_info *tei,2227void *ta_buf, uint32_t *pnum)2228{2229struct iftable_cfg *icfg;2230struct ifentry *ife, *tmp;2231struct ta_buf_ifidx *tb;2232struct ipfw_iface *iif;2233struct ifidx *ifi;2234char *ifname;2235uint32_t value;22362237tb = (struct ta_buf_ifidx *)ta_buf;2238ifname = (char *)tei->paddr;2239icfg = (struct iftable_cfg *)ta_state;2240ife = tb->ife;22412242ife->icfg = icfg;2243ife->value = tei->value;22442245tmp = (struct ifentry *)ipfw_objhash_lookup_name(icfg->ii, 0, ifname);22462247if (tmp != NULL) {2248if ((tei->flags & TEI_FLAGS_UPDATE) == 0)2249return (EEXIST);22502251/* Exchange values in @tmp and @tei */2252value = tmp->value;2253tmp->value = tei->value;2254tei->value = value;22552256iif = tmp->ic.iface;2257if (iif->resolved != 0) {2258/* We have to update runtime value, too */2259ifi = ifidx_find(ti, &iif->ifindex);2260ifi->value = ife->value;2261}22622263/* Indicate that update has happened instead of addition */2264tei->flags |= TEI_FLAGS_UPDATED;2265*pnum = 0;2266return (0);2267}22682269if ((tei->flags & TEI_FLAGS_DONTADD) != 0)2270return (EFBIG);22712272/* Link to internal list */2273ipfw_objhash_add(icfg->ii, &ife->no);22742275/* Link notifier (possible running its callback) */2276ipfw_iface_add_notify(icfg->ch, &ife->ic);2277icfg->count++;22782279tb->ife = NULL;2280*pnum = 1;22812282return (0);2283}22842285/*2286* Prepare to delete key from table.2287* Do basic interface name checks.2288*/2289static int2290ta_prepare_del_ifidx(struct ip_fw_chain *ch, struct tentry_info *tei,2291void *ta_buf)2292{2293char *ifname;22942295/* Check if string is terminated */2296ifname = (char *)tei->paddr;2297if (strnlen(ifname, IF_NAMESIZE) == IF_NAMESIZE)2298return (EINVAL);22992300return (0);2301}23022303/*2304* Remove key from both configuration list and2305* runtime array. Removed interface notification.2306*/2307static int2308ta_del_ifidx(void *ta_state, struct table_info *ti, struct tentry_info *tei,2309void *ta_buf, uint32_t *pnum)2310{2311struct iftable_cfg *icfg;2312struct ifentry *ife;2313struct ta_buf_ifidx *tb;2314char *ifname;2315uint16_t ifindex;2316int res __diagused;23172318tb = (struct ta_buf_ifidx *)ta_buf;2319ifname = (char *)tei->paddr;2320icfg = (struct iftable_cfg *)ta_state;23212322ife = (struct ifentry *)ipfw_objhash_lookup_name(icfg->ii, 0, ifname);23232324if (ife == NULL)2325return (ENOENT);23262327if (ife->linked != 0) {2328/* We have to remove item from runtime */2329ifindex = ife->ic.iface->ifindex;23302331res = bdel(&ifindex, icfg->main_ptr, icfg->used,2332sizeof(struct ifidx), compare_ifidx);23332334KASSERT(res == 1, ("index %d does not exist", ifindex));2335icfg->used--;2336ti->data = icfg->used;2337ife->linked = 0;2338}23392340/* Unlink from local list */2341ipfw_objhash_del(icfg->ii, &ife->no);2342/* Unlink notifier and deref */2343ipfw_iface_del_notify(icfg->ch, &ife->ic);2344ipfw_iface_unref(icfg->ch, &ife->ic);23452346icfg->count--;2347tei->value = ife->value;23482349tb->ife = ife;2350*pnum = 1;23512352return (0);2353}23542355/*2356* Flush deleted entry.2357* Drops interface reference and frees entry.2358*/2359static void2360ta_flush_ifidx_entry(struct ip_fw_chain *ch, struct tentry_info *tei,2361void *ta_buf)2362{2363struct ta_buf_ifidx *tb;23642365tb = (struct ta_buf_ifidx *)ta_buf;23662367if (tb->ife != NULL)2368free(tb->ife, M_IPFW_TBL);2369}23702371/*2372* Handle interface announce/withdrawal for particular table.2373* Every real runtime array modification happens here.2374*/2375static void2376if_notifier(struct ip_fw_chain *ch, void *cbdata, uint16_t ifindex)2377{2378struct ifentry *ife;2379struct ifidx ifi;2380struct iftable_cfg *icfg;2381struct table_info *ti;2382int res __diagused;23832384ife = (struct ifentry *)cbdata;2385icfg = ife->icfg;2386ti = icfg->ti;23872388KASSERT(ti != NULL, ("ti=NULL, check change_ti handler"));23892390if (ife->linked == 0 && ifindex != 0) {2391/* Interface announce */2392ifi.kidx = ifindex;2393ifi.spare = 0;2394ifi.value = ife->value;2395res = badd(&ifindex, &ifi, icfg->main_ptr, icfg->used,2396sizeof(struct ifidx), compare_ifidx);2397KASSERT(res == 1, ("index %d already exists", ifindex));2398icfg->used++;2399ti->data = icfg->used;2400ife->linked = 1;2401} else if (ife->linked != 0 && ifindex == 0) {2402/* Interface withdrawal */2403ifindex = ife->ic.iface->ifindex;24042405res = bdel(&ifindex, icfg->main_ptr, icfg->used,2406sizeof(struct ifidx), compare_ifidx);24072408KASSERT(res == 1, ("index %d does not exist", ifindex));2409icfg->used--;2410ti->data = icfg->used;2411ife->linked = 0;2412}2413}24142415/*2416* Table growing callbacks.2417*/24182419static int2420ta_need_modify_ifidx(void *ta_state, struct table_info *ti, uint32_t count,2421uint64_t *pflags)2422{2423struct iftable_cfg *cfg;2424uint32_t size;24252426cfg = (struct iftable_cfg *)ta_state;24272428size = cfg->size;2429while (size < cfg->count + count)2430size *= 2;24312432if (size != cfg->size) {2433*pflags = size;2434return (1);2435}24362437return (0);2438}24392440/*2441* Allocate ned, larger runtime ifidx array.2442*/2443static int2444ta_prepare_mod_ifidx(void *ta_buf, uint64_t *pflags)2445{2446struct mod_item *mi;24472448mi = (struct mod_item *)ta_buf;24492450memset(mi, 0, sizeof(struct mod_item));2451mi->size = *pflags;2452mi->main_ptr = malloc(sizeof(struct ifidx) * mi->size, M_IPFW,2453M_WAITOK | M_ZERO);24542455return (0);2456}24572458/*2459* Copy data from old runtime array to new one.2460*/2461static int2462ta_fill_mod_ifidx(void *ta_state, struct table_info *ti, void *ta_buf,2463uint64_t *pflags)2464{2465struct mod_item *mi;2466struct iftable_cfg *icfg;24672468mi = (struct mod_item *)ta_buf;2469icfg = (struct iftable_cfg *)ta_state;24702471/* Check if we still need to grow array */2472if (icfg->size >= mi->size) {2473*pflags = 0;2474return (0);2475}24762477memcpy(mi->main_ptr, icfg->main_ptr, icfg->used * sizeof(struct ifidx));24782479return (0);2480}24812482/*2483* Switch old & new arrays.2484*/2485static void2486ta_modify_ifidx(void *ta_state, struct table_info *ti, void *ta_buf,2487uint64_t pflags)2488{2489struct mod_item *mi;2490struct iftable_cfg *icfg;2491void *old_ptr;24922493mi = (struct mod_item *)ta_buf;2494icfg = (struct iftable_cfg *)ta_state;24952496old_ptr = icfg->main_ptr;2497icfg->main_ptr = mi->main_ptr;2498icfg->size = mi->size;2499ti->state = icfg->main_ptr;25002501mi->main_ptr = old_ptr;2502}25032504/*2505* Free unneded array.2506*/2507static void2508ta_flush_mod_ifidx(void *ta_buf)2509{2510struct mod_item *mi;25112512mi = (struct mod_item *)ta_buf;2513if (mi->main_ptr != NULL)2514free(mi->main_ptr, M_IPFW);2515}25162517static int2518ta_dump_ifidx_tentry(void *ta_state, struct table_info *ti, void *e,2519ipfw_obj_tentry *tent)2520{2521struct ifentry *ife;25222523ife = (struct ifentry *)e;25242525tent->masklen = 8 * IF_NAMESIZE;2526memcpy(&tent->k, ife->no.name, IF_NAMESIZE);2527tent->v.kidx = ife->value;25282529return (0);2530}25312532static int2533ta_find_ifidx_tentry(void *ta_state, struct table_info *ti,2534ipfw_obj_tentry *tent)2535{2536struct iftable_cfg *icfg;2537struct ifentry *ife;2538char *ifname;25392540icfg = (struct iftable_cfg *)ta_state;2541ifname = tent->k.iface;25422543if (strnlen(ifname, IF_NAMESIZE) == IF_NAMESIZE)2544return (EINVAL);25452546ife = (struct ifentry *)ipfw_objhash_lookup_name(icfg->ii, 0, ifname);25472548if (ife != NULL) {2549ta_dump_ifidx_tentry(ta_state, ti, ife, tent);2550return (0);2551}25522553return (ENOENT);2554}25552556struct wa_ifidx {2557ta_foreach_f *f;2558void *arg;2559};25602561static int2562foreach_ifidx(struct namedobj_instance *ii, struct named_object *no,2563void *arg)2564{2565struct ifentry *ife;2566struct wa_ifidx *wa;25672568ife = (struct ifentry *)no;2569wa = (struct wa_ifidx *)arg;25702571wa->f(ife, wa->arg);2572return (0);2573}25742575static void2576ta_foreach_ifidx(void *ta_state, struct table_info *ti, ta_foreach_f *f,2577void *arg)2578{2579struct iftable_cfg *icfg;2580struct wa_ifidx wa;25812582icfg = (struct iftable_cfg *)ta_state;25832584wa.f = f;2585wa.arg = arg;25862587ipfw_objhash_foreach(icfg->ii, foreach_ifidx, &wa);2588}25892590struct table_algo iface_idx = {2591.name = "iface:array",2592.type = IPFW_TABLE_INTERFACE,2593.flags = TA_FLAG_DEFAULT,2594.ta_buf_size = sizeof(struct ta_buf_ifidx),2595.init = ta_init_ifidx,2596.destroy = ta_destroy_ifidx,2597.prepare_add = ta_prepare_add_ifidx,2598.prepare_del = ta_prepare_del_ifidx,2599.add = ta_add_ifidx,2600.del = ta_del_ifidx,2601.flush_entry = ta_flush_ifidx_entry,2602.foreach = ta_foreach_ifidx,2603.dump_tentry = ta_dump_ifidx_tentry,2604.find_tentry = ta_find_ifidx_tentry,2605.dump_tinfo = ta_dump_ifidx_tinfo,2606.need_modify = ta_need_modify_ifidx,2607.prepare_mod = ta_prepare_mod_ifidx,2608.fill_mod = ta_fill_mod_ifidx,2609.modify = ta_modify_ifidx,2610.flush_mod = ta_flush_mod_ifidx,2611.change_ti = ta_change_ti_ifidx,2612};26132614/*2615* Number array cmds.2616*2617* Implementation:2618*2619* Runtime part:2620* - sorted array of "struct numarray" pointed by ti->state.2621* Array is allocated with rounding up to NUMARRAY_CHUNK.2622* - current array size is stored in ti->data2623*2624*/26252626struct numarray {2627uint32_t number;2628uint32_t value;2629};26302631struct numarray_cfg {2632void *main_ptr;2633size_t size; /* Number of items allocated in array */2634size_t used; /* Number of items _active_ now */2635};26362637struct ta_buf_numarray2638{2639struct numarray na;2640};26412642int compare_numarray(const void *k, const void *v);2643static struct numarray *numarray_find(struct table_info *ti, void *key);2644static int ta_lookup_numarray(struct table_info *ti, void *key,2645uint32_t keylen, uint32_t *val);2646static int ta_init_numarray(struct ip_fw_chain *ch, void **ta_state,2647struct table_info *ti, char *data, uint8_t tflags);2648static void ta_destroy_numarray(void *ta_state, struct table_info *ti);2649static void ta_dump_numarray_tinfo(void *ta_state, struct table_info *ti,2650ipfw_ta_tinfo *tinfo);2651static int ta_prepare_add_numarray(struct ip_fw_chain *ch,2652struct tentry_info *tei, void *ta_buf);2653static int ta_add_numarray(void *ta_state, struct table_info *ti,2654struct tentry_info *tei, void *ta_buf, uint32_t *pnum);2655static int ta_del_numarray(void *ta_state, struct table_info *ti,2656struct tentry_info *tei, void *ta_buf, uint32_t *pnum);2657static void ta_flush_numarray_entry(struct ip_fw_chain *ch,2658struct tentry_info *tei, void *ta_buf);2659static int ta_need_modify_numarray(void *ta_state, struct table_info *ti,2660uint32_t count, uint64_t *pflags);2661static int ta_prepare_mod_numarray(void *ta_buf, uint64_t *pflags);2662static int ta_fill_mod_numarray(void *ta_state, struct table_info *ti,2663void *ta_buf, uint64_t *pflags);2664static void ta_modify_numarray(void *ta_state, struct table_info *ti,2665void *ta_buf, uint64_t pflags);2666static void ta_flush_mod_numarray(void *ta_buf);2667static int ta_dump_numarray_tentry(void *ta_state, struct table_info *ti,2668void *e, ipfw_obj_tentry *tent);2669static int ta_find_numarray_tentry(void *ta_state, struct table_info *ti,2670ipfw_obj_tentry *tent);2671static void ta_foreach_numarray(void *ta_state, struct table_info *ti,2672ta_foreach_f *f, void *arg);26732674int2675compare_numarray(const void *k, const void *v)2676{2677const struct numarray *na;2678uint32_t key;26792680key = *((const uint32_t *)k);2681na = (const struct numarray *)v;26822683if (key < na->number)2684return (-1);2685else if (key > na->number)2686return (1);26872688return (0);2689}26902691static struct numarray *2692numarray_find(struct table_info *ti, void *key)2693{2694struct numarray *ri;26952696ri = bsearch(key, ti->state, ti->data, sizeof(struct numarray),2697compare_numarray);26982699return (ri);2700}27012702static int2703ta_lookup_numarray(struct table_info *ti, void *key, uint32_t keylen,2704uint32_t *val)2705{2706struct numarray *ri;27072708ri = numarray_find(ti, key);27092710if (ri != NULL) {2711*val = ri->value;2712return (1);2713}27142715return (0);2716}27172718static int2719ta_init_numarray(struct ip_fw_chain *ch, void **ta_state, struct table_info *ti,2720char *data, uint8_t tflags)2721{2722struct numarray_cfg *cfg;27232724cfg = malloc(sizeof(*cfg), M_IPFW, M_WAITOK | M_ZERO);27252726cfg->size = 16;2727cfg->main_ptr = malloc(sizeof(struct numarray) * cfg->size, M_IPFW,2728M_WAITOK | M_ZERO);27292730*ta_state = cfg;2731ti->state = cfg->main_ptr;2732ti->lookup = ta_lookup_numarray;27332734return (0);2735}27362737/*2738* Destroys table @ti2739*/2740static void2741ta_destroy_numarray(void *ta_state, struct table_info *ti)2742{2743struct numarray_cfg *cfg;27442745cfg = (struct numarray_cfg *)ta_state;27462747if (cfg->main_ptr != NULL)2748free(cfg->main_ptr, M_IPFW);27492750free(cfg, M_IPFW);2751}27522753/*2754* Provide algo-specific table info2755*/2756static void2757ta_dump_numarray_tinfo(void *ta_state, struct table_info *ti, ipfw_ta_tinfo *tinfo)2758{2759struct numarray_cfg *cfg;27602761cfg = (struct numarray_cfg *)ta_state;27622763tinfo->taclass4 = IPFW_TACLASS_ARRAY;2764tinfo->size4 = cfg->size;2765tinfo->count4 = cfg->used;2766tinfo->itemsize4 = sizeof(struct numarray);2767}27682769/*2770* Prepare for addition/deletion to an array.2771*/2772static int2773ta_prepare_add_numarray(struct ip_fw_chain *ch, struct tentry_info *tei,2774void *ta_buf)2775{2776struct ta_buf_numarray *tb;27772778tb = (struct ta_buf_numarray *)ta_buf;27792780tb->na.number = *((uint32_t *)tei->paddr);27812782return (0);2783}27842785static int2786ta_add_numarray(void *ta_state, struct table_info *ti, struct tentry_info *tei,2787void *ta_buf, uint32_t *pnum)2788{2789struct numarray_cfg *cfg;2790struct ta_buf_numarray *tb;2791struct numarray *ri;2792int res __diagused;2793uint32_t value;27942795tb = (struct ta_buf_numarray *)ta_buf;2796cfg = (struct numarray_cfg *)ta_state;27972798/* Read current value from @tei */2799tb->na.value = tei->value;28002801ri = numarray_find(ti, &tb->na.number);28022803if (ri != NULL) {2804if ((tei->flags & TEI_FLAGS_UPDATE) == 0)2805return (EEXIST);28062807/* Exchange values between ri and @tei */2808value = ri->value;2809ri->value = tei->value;2810tei->value = value;2811/* Indicate that update has happened instead of addition */2812tei->flags |= TEI_FLAGS_UPDATED;2813*pnum = 0;2814return (0);2815}28162817if ((tei->flags & TEI_FLAGS_DONTADD) != 0)2818return (EFBIG);28192820res = badd(&tb->na.number, &tb->na, cfg->main_ptr, cfg->used,2821sizeof(struct numarray), compare_numarray);28222823KASSERT(res == 1, ("number %d already exists", tb->na.number));2824cfg->used++;2825ti->data = cfg->used;2826*pnum = 1;28272828return (0);2829}28302831/*2832* Remove key from both configuration list and2833* runtime array. Removed interface notification.2834*/2835static int2836ta_del_numarray(void *ta_state, struct table_info *ti, struct tentry_info *tei,2837void *ta_buf, uint32_t *pnum)2838{2839struct numarray_cfg *cfg;2840struct ta_buf_numarray *tb;2841struct numarray *ri;2842int res __diagused;28432844tb = (struct ta_buf_numarray *)ta_buf;2845cfg = (struct numarray_cfg *)ta_state;28462847ri = numarray_find(ti, &tb->na.number);2848if (ri == NULL)2849return (ENOENT);28502851tei->value = ri->value;28522853res = bdel(&tb->na.number, cfg->main_ptr, cfg->used,2854sizeof(struct numarray), compare_numarray);28552856KASSERT(res == 1, ("number %u does not exist", tb->na.number));2857cfg->used--;2858ti->data = cfg->used;2859*pnum = 1;28602861return (0);2862}28632864static void2865ta_flush_numarray_entry(struct ip_fw_chain *ch, struct tentry_info *tei,2866void *ta_buf)2867{28682869/* We don't have any state, do nothing */2870}28712872/*2873* Table growing callbacks.2874*/28752876static int2877ta_need_modify_numarray(void *ta_state, struct table_info *ti, uint32_t count,2878uint64_t *pflags)2879{2880struct numarray_cfg *cfg;2881size_t size;28822883cfg = (struct numarray_cfg *)ta_state;28842885size = cfg->size;2886while (size < cfg->used + count)2887size *= 2;28882889if (size != cfg->size) {2890*pflags = size;2891return (1);2892}28932894return (0);2895}28962897/*2898* Allocate new, larger runtime array.2899*/2900static int2901ta_prepare_mod_numarray(void *ta_buf, uint64_t *pflags)2902{2903struct mod_item *mi;29042905mi = (struct mod_item *)ta_buf;29062907memset(mi, 0, sizeof(struct mod_item));2908mi->size = *pflags;2909mi->main_ptr = malloc(sizeof(struct numarray) * mi->size, M_IPFW,2910M_WAITOK | M_ZERO);29112912return (0);2913}29142915/*2916* Copy data from old runtime array to new one.2917*/2918static int2919ta_fill_mod_numarray(void *ta_state, struct table_info *ti, void *ta_buf,2920uint64_t *pflags)2921{2922struct mod_item *mi;2923struct numarray_cfg *cfg;29242925mi = (struct mod_item *)ta_buf;2926cfg = (struct numarray_cfg *)ta_state;29272928/* Check if we still need to grow array */2929if (cfg->size >= mi->size) {2930*pflags = 0;2931return (0);2932}29332934memcpy(mi->main_ptr, cfg->main_ptr, cfg->used * sizeof(struct numarray));29352936return (0);2937}29382939/*2940* Switch old & new arrays.2941*/2942static void2943ta_modify_numarray(void *ta_state, struct table_info *ti, void *ta_buf,2944uint64_t pflags)2945{2946struct mod_item *mi;2947struct numarray_cfg *cfg;2948void *old_ptr;29492950mi = (struct mod_item *)ta_buf;2951cfg = (struct numarray_cfg *)ta_state;29522953old_ptr = cfg->main_ptr;2954cfg->main_ptr = mi->main_ptr;2955cfg->size = mi->size;2956ti->state = cfg->main_ptr;29572958mi->main_ptr = old_ptr;2959}29602961/*2962* Free unneded array.2963*/2964static void2965ta_flush_mod_numarray(void *ta_buf)2966{2967struct mod_item *mi;29682969mi = (struct mod_item *)ta_buf;2970if (mi->main_ptr != NULL)2971free(mi->main_ptr, M_IPFW);2972}29732974static int2975ta_dump_numarray_tentry(void *ta_state, struct table_info *ti, void *e,2976ipfw_obj_tentry *tent)2977{2978struct numarray *na;29792980na = (struct numarray *)e;29812982tent->k.key = na->number;2983tent->v.kidx = na->value;29842985return (0);2986}29872988static int2989ta_find_numarray_tentry(void *ta_state, struct table_info *ti,2990ipfw_obj_tentry *tent)2991{2992struct numarray *ri;29932994ri = numarray_find(ti, &tent->k.key);29952996if (ri != NULL) {2997ta_dump_numarray_tentry(ta_state, ti, ri, tent);2998return (0);2999}30003001return (ENOENT);3002}30033004static void3005ta_foreach_numarray(void *ta_state, struct table_info *ti, ta_foreach_f *f,3006void *arg)3007{3008struct numarray_cfg *cfg;3009struct numarray *array;3010int i;30113012cfg = (struct numarray_cfg *)ta_state;3013array = cfg->main_ptr;30143015for (i = 0; i < cfg->used; i++)3016f(&array[i], arg);3017}30183019struct table_algo number_array = {3020.name = "number:array",3021.type = IPFW_TABLE_NUMBER,3022.ta_buf_size = sizeof(struct ta_buf_numarray),3023.init = ta_init_numarray,3024.destroy = ta_destroy_numarray,3025.prepare_add = ta_prepare_add_numarray,3026.prepare_del = ta_prepare_add_numarray,3027.add = ta_add_numarray,3028.del = ta_del_numarray,3029.flush_entry = ta_flush_numarray_entry,3030.foreach = ta_foreach_numarray,3031.dump_tentry = ta_dump_numarray_tentry,3032.find_tentry = ta_find_numarray_tentry,3033.dump_tinfo = ta_dump_numarray_tinfo,3034.need_modify = ta_need_modify_numarray,3035.prepare_mod = ta_prepare_mod_numarray,3036.fill_mod = ta_fill_mod_numarray,3037.modify = ta_modify_numarray,3038.flush_mod = ta_flush_mod_numarray,3039};30403041/*3042* flow:hash cmds3043*3044*3045* ti->data:3046* [inv.mask4][inv.mask6][log2hsize4][log2hsize6]3047* [ 8][ 8[ 8][ 8]3048*3049* inv.mask4: 32 - mask3050* inv.mask6:3051* 1) _slow lookup: mask3052* 2) _aligned: (128 - mask) / 83053* 3) _64: 83054*3055*3056* pflags:3057* [hsize4][hsize6]3058* [ 16][ 16]3059*/30603061struct fhashentry;30623063SLIST_HEAD(fhashbhead, fhashentry);30643065struct fhashentry {3066SLIST_ENTRY(fhashentry) next;3067uint8_t af;3068uint8_t proto;3069uint16_t spare0;3070uint16_t dport;3071uint16_t sport;3072uint32_t value;3073uint32_t spare1;3074};30753076struct fhashentry4 {3077struct fhashentry e;3078struct in_addr dip;3079struct in_addr sip;3080};30813082struct fhashentry6 {3083struct fhashentry e;3084struct in6_addr dip6;3085struct in6_addr sip6;3086};30873088struct fhash_cfg {3089struct fhashbhead *head;3090size_t size;3091size_t items;3092struct fhashentry4 fe4;3093struct fhashentry6 fe6;3094};30953096struct ta_buf_fhash {3097void *ent_ptr;3098struct fhashentry6 fe6;3099};31003101static __inline int cmp_flow_ent(struct fhashentry *a,3102struct fhashentry *b, size_t sz);3103static __inline uint32_t hash_flow4(struct fhashentry4 *f, int hsize);3104static __inline uint32_t hash_flow6(struct fhashentry6 *f, int hsize);3105static uint32_t hash_flow_ent(struct fhashentry *ent, uint32_t size);3106static int ta_lookup_fhash(struct table_info *ti, void *key, uint32_t keylen,3107uint32_t *val);3108static int ta_init_fhash(struct ip_fw_chain *ch, void **ta_state,3109struct table_info *ti, char *data, uint8_t tflags);3110static void ta_destroy_fhash(void *ta_state, struct table_info *ti);3111static void ta_dump_fhash_tinfo(void *ta_state, struct table_info *ti,3112ipfw_ta_tinfo *tinfo);3113static int ta_dump_fhash_tentry(void *ta_state, struct table_info *ti,3114void *e, ipfw_obj_tentry *tent);3115static int tei_to_fhash_ent(struct tentry_info *tei, struct fhashentry *ent);3116static int ta_find_fhash_tentry(void *ta_state, struct table_info *ti,3117ipfw_obj_tentry *tent);3118static void ta_foreach_fhash(void *ta_state, struct table_info *ti,3119ta_foreach_f *f, void *arg);3120static int ta_prepare_add_fhash(struct ip_fw_chain *ch,3121struct tentry_info *tei, void *ta_buf);3122static int ta_add_fhash(void *ta_state, struct table_info *ti,3123struct tentry_info *tei, void *ta_buf, uint32_t *pnum);3124static int ta_prepare_del_fhash(struct ip_fw_chain *ch, struct tentry_info *tei,3125void *ta_buf);3126static int ta_del_fhash(void *ta_state, struct table_info *ti,3127struct tentry_info *tei, void *ta_buf, uint32_t *pnum);3128static void ta_flush_fhash_entry(struct ip_fw_chain *ch, struct tentry_info *tei,3129void *ta_buf);3130static int ta_need_modify_fhash(void *ta_state, struct table_info *ti,3131uint32_t count, uint64_t *pflags);3132static int ta_prepare_mod_fhash(void *ta_buf, uint64_t *pflags);3133static int ta_fill_mod_fhash(void *ta_state, struct table_info *ti,3134void *ta_buf, uint64_t *pflags);3135static void ta_modify_fhash(void *ta_state, struct table_info *ti, void *ta_buf,3136uint64_t pflags);3137static void ta_flush_mod_fhash(void *ta_buf);31383139static __inline int3140cmp_flow_ent(struct fhashentry *a, struct fhashentry *b, size_t sz)3141{3142uint64_t *ka, *kb;31433144ka = (uint64_t *)(&a->next + 1);3145kb = (uint64_t *)(&b->next + 1);31463147if (*ka == *kb && (memcmp(a + 1, b + 1, sz) == 0))3148return (1);31493150return (0);3151}31523153static __inline uint32_t3154hash_flow4(struct fhashentry4 *f, int hsize)3155{3156uint32_t i;31573158i = (f->dip.s_addr) ^ (f->sip.s_addr) ^ (f->e.dport) ^ (f->e.sport);31593160return (i % (hsize - 1));3161}31623163static __inline uint32_t3164hash_flow6(struct fhashentry6 *f, int hsize)3165{3166uint32_t i;31673168i = (f->dip6.__u6_addr.__u6_addr32[2]) ^3169(f->dip6.__u6_addr.__u6_addr32[3]) ^3170(f->sip6.__u6_addr.__u6_addr32[2]) ^3171(f->sip6.__u6_addr.__u6_addr32[3]) ^3172(f->e.dport) ^ (f->e.sport);31733174return (i % (hsize - 1));3175}31763177static uint32_t3178hash_flow_ent(struct fhashentry *ent, uint32_t size)3179{3180uint32_t hash;31813182if (ent->af == AF_INET) {3183hash = hash_flow4((struct fhashentry4 *)ent, size);3184} else {3185hash = hash_flow6((struct fhashentry6 *)ent, size);3186}31873188return (hash);3189}31903191static int3192ta_lookup_fhash(struct table_info *ti, void *key, uint32_t keylen,3193uint32_t *val)3194{3195struct fhashbhead *head;3196struct fhashentry *ent;3197struct fhashentry4 *m4;3198struct ipfw_flow_id *id;3199uint32_t hash, hsize;32003201id = (struct ipfw_flow_id *)key;3202head = (struct fhashbhead *)ti->state;3203hsize = ti->data;3204m4 = (struct fhashentry4 *)ti->xstate;32053206if (id->addr_type == 4) {3207struct fhashentry4 f;32083209/* Copy hash mask */3210f = *m4;32113212f.dip.s_addr &= id->dst_ip;3213f.sip.s_addr &= id->src_ip;3214f.e.dport &= id->dst_port;3215f.e.sport &= id->src_port;3216f.e.proto &= id->proto;3217hash = hash_flow4(&f, hsize);3218SLIST_FOREACH(ent, &head[hash], next) {3219if (cmp_flow_ent(ent, &f.e, 2 * 4) != 0) {3220*val = ent->value;3221return (1);3222}3223}3224} else if (id->addr_type == 6) {3225struct fhashentry6 f;3226uint64_t *fp, *idp;32273228/* Copy hash mask */3229f = *((struct fhashentry6 *)(m4 + 1));32303231/* Handle lack of __u6_addr.__u6_addr64 */3232fp = (uint64_t *)&f.dip6;3233idp = (uint64_t *)&id->dst_ip6;3234/* src IPv6 is stored after dst IPv6 */3235*fp++ &= *idp++;3236*fp++ &= *idp++;3237*fp++ &= *idp++;3238*fp &= *idp;3239f.e.dport &= id->dst_port;3240f.e.sport &= id->src_port;3241f.e.proto &= id->proto;3242hash = hash_flow6(&f, hsize);3243SLIST_FOREACH(ent, &head[hash], next) {3244if (cmp_flow_ent(ent, &f.e, 2 * 16) != 0) {3245*val = ent->value;3246return (1);3247}3248}3249}32503251return (0);3252}32533254/*3255* New table.3256*/3257static int3258ta_init_fhash(struct ip_fw_chain *ch, void **ta_state, struct table_info *ti,3259char *data, uint8_t tflags)3260{3261struct fhash_cfg *cfg;3262struct fhashentry4 *fe4;3263struct fhashentry6 *fe6;3264u_int i;32653266cfg = malloc(sizeof(struct fhash_cfg), M_IPFW, M_WAITOK | M_ZERO);32673268cfg->size = 512;32693270cfg->head = malloc(sizeof(struct fhashbhead) * cfg->size, M_IPFW,3271M_WAITOK | M_ZERO);3272for (i = 0; i < cfg->size; i++)3273SLIST_INIT(&cfg->head[i]);32743275/* Fill in fe masks based on @tflags */3276fe4 = &cfg->fe4;3277fe6 = &cfg->fe6;3278if (tflags & IPFW_TFFLAG_SRCIP) {3279memset(&fe4->sip, 0xFF, sizeof(fe4->sip));3280memset(&fe6->sip6, 0xFF, sizeof(fe6->sip6));3281}3282if (tflags & IPFW_TFFLAG_DSTIP) {3283memset(&fe4->dip, 0xFF, sizeof(fe4->dip));3284memset(&fe6->dip6, 0xFF, sizeof(fe6->dip6));3285}3286if (tflags & IPFW_TFFLAG_SRCPORT) {3287memset(&fe4->e.sport, 0xFF, sizeof(fe4->e.sport));3288memset(&fe6->e.sport, 0xFF, sizeof(fe6->e.sport));3289}3290if (tflags & IPFW_TFFLAG_DSTPORT) {3291memset(&fe4->e.dport, 0xFF, sizeof(fe4->e.dport));3292memset(&fe6->e.dport, 0xFF, sizeof(fe6->e.dport));3293}3294if (tflags & IPFW_TFFLAG_PROTO) {3295memset(&fe4->e.proto, 0xFF, sizeof(fe4->e.proto));3296memset(&fe6->e.proto, 0xFF, sizeof(fe6->e.proto));3297}32983299fe4->e.af = AF_INET;3300fe6->e.af = AF_INET6;33013302*ta_state = cfg;3303ti->state = cfg->head;3304ti->xstate = &cfg->fe4;3305ti->data = cfg->size;3306ti->lookup = ta_lookup_fhash;33073308return (0);3309}33103311static void3312ta_destroy_fhash(void *ta_state, struct table_info *ti)3313{3314struct fhash_cfg *cfg;3315struct fhashentry *ent, *ent_next;3316int i;33173318cfg = (struct fhash_cfg *)ta_state;33193320for (i = 0; i < cfg->size; i++)3321SLIST_FOREACH_SAFE(ent, &cfg->head[i], next, ent_next)3322free(ent, M_IPFW_TBL);33233324free(cfg->head, M_IPFW);3325free(cfg, M_IPFW);3326}33273328/*3329* Provide algo-specific table info3330*/3331static void3332ta_dump_fhash_tinfo(void *ta_state, struct table_info *ti, ipfw_ta_tinfo *tinfo)3333{3334struct fhash_cfg *cfg;33353336cfg = (struct fhash_cfg *)ta_state;33373338tinfo->flags = IPFW_TATFLAGS_AFITEM;3339tinfo->taclass4 = IPFW_TACLASS_HASH;3340tinfo->size4 = cfg->size;3341tinfo->count4 = cfg->items;3342tinfo->itemsize4 = sizeof(struct fhashentry4);3343tinfo->itemsize6 = sizeof(struct fhashentry6);3344}33453346static int3347ta_dump_fhash_tentry(void *ta_state, struct table_info *ti, void *e,3348ipfw_obj_tentry *tent)3349{3350struct fhashentry *ent;3351struct fhashentry4 *fe4;3352#ifdef INET63353struct fhashentry6 *fe6;3354#endif3355struct tflow_entry *tfe;33563357ent = (struct fhashentry *)e;3358tfe = &tent->k.flow;33593360tfe->af = ent->af;3361tfe->proto = ent->proto;3362tfe->dport = htons(ent->dport);3363tfe->sport = htons(ent->sport);3364tent->v.kidx = ent->value;3365tent->subtype = ent->af;33663367if (ent->af == AF_INET) {3368fe4 = (struct fhashentry4 *)ent;3369tfe->a.a4.sip.s_addr = htonl(fe4->sip.s_addr);3370tfe->a.a4.dip.s_addr = htonl(fe4->dip.s_addr);3371tent->masklen = 32;3372#ifdef INET63373} else {3374fe6 = (struct fhashentry6 *)ent;3375tfe->a.a6.sip6 = fe6->sip6;3376tfe->a.a6.dip6 = fe6->dip6;3377tent->masklen = 128;3378#endif3379}33803381return (0);3382}33833384static int3385tei_to_fhash_ent(struct tentry_info *tei, struct fhashentry *ent)3386{3387#ifdef INET3388struct fhashentry4 *fe4;3389#endif3390#ifdef INET63391struct fhashentry6 *fe6;3392#endif3393struct tflow_entry *tfe;33943395tfe = (struct tflow_entry *)tei->paddr;33963397ent->af = tei->subtype;3398ent->proto = tfe->proto;3399ent->dport = ntohs(tfe->dport);3400ent->sport = ntohs(tfe->sport);34013402if (tei->subtype == AF_INET) {3403#ifdef INET3404fe4 = (struct fhashentry4 *)ent;3405fe4->sip.s_addr = ntohl(tfe->a.a4.sip.s_addr);3406fe4->dip.s_addr = ntohl(tfe->a.a4.dip.s_addr);3407#endif3408#ifdef INET63409} else if (tei->subtype == AF_INET6) {3410fe6 = (struct fhashentry6 *)ent;3411fe6->sip6 = tfe->a.a6.sip6;3412fe6->dip6 = tfe->a.a6.dip6;3413#endif3414} else {3415/* Unknown CIDR type */3416return (EINVAL);3417}34183419return (0);3420}34213422static int3423ta_find_fhash_tentry(void *ta_state, struct table_info *ti,3424ipfw_obj_tentry *tent)3425{3426struct fhash_cfg *cfg;3427struct fhashbhead *head;3428struct fhashentry *ent, *tmp;3429struct fhashentry6 fe6;3430struct tentry_info tei;3431int error;3432uint32_t hash;3433size_t sz;34343435cfg = (struct fhash_cfg *)ta_state;34363437ent = &fe6.e;34383439memset(&fe6, 0, sizeof(fe6));3440memset(&tei, 0, sizeof(tei));34413442tei.paddr = &tent->k.flow;3443tei.subtype = tent->subtype;34443445if ((error = tei_to_fhash_ent(&tei, ent)) != 0)3446return (error);34473448head = cfg->head;3449hash = hash_flow_ent(ent, cfg->size);34503451if (tei.subtype == AF_INET)3452sz = 2 * sizeof(struct in_addr);3453else3454sz = 2 * sizeof(struct in6_addr);34553456/* Check for existence */3457SLIST_FOREACH(tmp, &head[hash], next) {3458if (cmp_flow_ent(tmp, ent, sz) != 0) {3459ta_dump_fhash_tentry(ta_state, ti, tmp, tent);3460return (0);3461}3462}34633464return (ENOENT);3465}34663467static void3468ta_foreach_fhash(void *ta_state, struct table_info *ti, ta_foreach_f *f,3469void *arg)3470{3471struct fhash_cfg *cfg;3472struct fhashentry *ent, *ent_next;3473int i;34743475cfg = (struct fhash_cfg *)ta_state;34763477for (i = 0; i < cfg->size; i++)3478SLIST_FOREACH_SAFE(ent, &cfg->head[i], next, ent_next)3479f(ent, arg);3480}34813482static int3483ta_prepare_add_fhash(struct ip_fw_chain *ch, struct tentry_info *tei,3484void *ta_buf)3485{3486struct ta_buf_fhash *tb;3487struct fhashentry *ent;3488size_t sz;3489int error;34903491tb = (struct ta_buf_fhash *)ta_buf;34923493if (tei->subtype == AF_INET)3494sz = sizeof(struct fhashentry4);3495else if (tei->subtype == AF_INET6)3496sz = sizeof(struct fhashentry6);3497else3498return (EINVAL);34993500ent = malloc(sz, M_IPFW_TBL, M_WAITOK | M_ZERO);35013502error = tei_to_fhash_ent(tei, ent);3503if (error != 0) {3504free(ent, M_IPFW_TBL);3505return (error);3506}3507tb->ent_ptr = ent;35083509return (0);3510}35113512static int3513ta_add_fhash(void *ta_state, struct table_info *ti, struct tentry_info *tei,3514void *ta_buf, uint32_t *pnum)3515{3516struct fhash_cfg *cfg;3517struct fhashbhead *head;3518struct fhashentry *ent, *tmp;3519struct ta_buf_fhash *tb;3520int exists;3521uint32_t hash, value;3522size_t sz;35233524cfg = (struct fhash_cfg *)ta_state;3525tb = (struct ta_buf_fhash *)ta_buf;3526ent = (struct fhashentry *)tb->ent_ptr;3527exists = 0;35283529/* Read current value from @tei */3530ent->value = tei->value;35313532head = cfg->head;3533hash = hash_flow_ent(ent, cfg->size);35343535if (tei->subtype == AF_INET)3536sz = 2 * sizeof(struct in_addr);3537else3538sz = 2 * sizeof(struct in6_addr);35393540/* Check for existence */3541SLIST_FOREACH(tmp, &head[hash], next) {3542if (cmp_flow_ent(tmp, ent, sz) != 0) {3543exists = 1;3544break;3545}3546}35473548if (exists == 1) {3549if ((tei->flags & TEI_FLAGS_UPDATE) == 0)3550return (EEXIST);3551/* Record already exists. Update value if we're asked to */3552/* Exchange values between tmp and @tei */3553value = tmp->value;3554tmp->value = tei->value;3555tei->value = value;3556/* Indicate that update has happened instead of addition */3557tei->flags |= TEI_FLAGS_UPDATED;3558*pnum = 0;3559} else {3560if ((tei->flags & TEI_FLAGS_DONTADD) != 0)3561return (EFBIG);35623563SLIST_INSERT_HEAD(&head[hash], ent, next);3564tb->ent_ptr = NULL;3565*pnum = 1;35663567/* Update counters and check if we need to grow hash */3568cfg->items++;3569}35703571return (0);3572}35733574static int3575ta_prepare_del_fhash(struct ip_fw_chain *ch, struct tentry_info *tei,3576void *ta_buf)3577{3578struct ta_buf_fhash *tb;35793580tb = (struct ta_buf_fhash *)ta_buf;35813582return (tei_to_fhash_ent(tei, &tb->fe6.e));3583}35843585static int3586ta_del_fhash(void *ta_state, struct table_info *ti, struct tentry_info *tei,3587void *ta_buf, uint32_t *pnum)3588{3589struct fhash_cfg *cfg;3590struct fhashbhead *head;3591struct fhashentry *ent, *tmp;3592struct ta_buf_fhash *tb;3593uint32_t hash;3594size_t sz;35953596cfg = (struct fhash_cfg *)ta_state;3597tb = (struct ta_buf_fhash *)ta_buf;3598ent = &tb->fe6.e;35993600head = cfg->head;3601hash = hash_flow_ent(ent, cfg->size);36023603if (tei->subtype == AF_INET)3604sz = 2 * sizeof(struct in_addr);3605else3606sz = 2 * sizeof(struct in6_addr);36073608/* Check for existence */3609SLIST_FOREACH(tmp, &head[hash], next) {3610if (cmp_flow_ent(tmp, ent, sz) == 0)3611continue;36123613SLIST_REMOVE(&head[hash], tmp, fhashentry, next);3614tei->value = tmp->value;3615*pnum = 1;3616cfg->items--;3617tb->ent_ptr = tmp;3618return (0);3619}36203621return (ENOENT);3622}36233624static void3625ta_flush_fhash_entry(struct ip_fw_chain *ch, struct tentry_info *tei,3626void *ta_buf)3627{3628struct ta_buf_fhash *tb;36293630tb = (struct ta_buf_fhash *)ta_buf;36313632if (tb->ent_ptr != NULL)3633free(tb->ent_ptr, M_IPFW_TBL);3634}36353636/*3637* Hash growing callbacks.3638*/36393640static int3641ta_need_modify_fhash(void *ta_state, struct table_info *ti, uint32_t count,3642uint64_t *pflags)3643{3644struct fhash_cfg *cfg;36453646cfg = (struct fhash_cfg *)ta_state;36473648if (cfg->items > cfg->size && cfg->size < 65536) {3649*pflags = cfg->size * 2;3650return (1);3651}36523653return (0);3654}36553656/*3657* Allocate new, larger fhash.3658*/3659static int3660ta_prepare_mod_fhash(void *ta_buf, uint64_t *pflags)3661{3662struct mod_item *mi;3663struct fhashbhead *head;3664u_int i;36653666mi = (struct mod_item *)ta_buf;36673668memset(mi, 0, sizeof(struct mod_item));3669mi->size = *pflags;3670head = malloc(sizeof(struct fhashbhead) * mi->size, M_IPFW,3671M_WAITOK | M_ZERO);3672for (i = 0; i < mi->size; i++)3673SLIST_INIT(&head[i]);36743675mi->main_ptr = head;36763677return (0);3678}36793680/*3681* Copy data from old runtime array to new one.3682*/3683static int3684ta_fill_mod_fhash(void *ta_state, struct table_info *ti, void *ta_buf,3685uint64_t *pflags)3686{36873688/* In is not possible to do rehash if we're not holidng WLOCK. */3689return (0);3690}36913692/*3693* Switch old & new arrays.3694*/3695static void3696ta_modify_fhash(void *ta_state, struct table_info *ti, void *ta_buf,3697uint64_t pflags)3698{3699struct mod_item *mi;3700struct fhash_cfg *cfg;3701struct fhashbhead *old_head, *new_head;3702struct fhashentry *ent, *ent_next;3703int i;3704uint32_t nhash;3705size_t old_size;37063707mi = (struct mod_item *)ta_buf;3708cfg = (struct fhash_cfg *)ta_state;37093710old_size = cfg->size;3711old_head = ti->state;37123713new_head = (struct fhashbhead *)mi->main_ptr;3714for (i = 0; i < old_size; i++) {3715SLIST_FOREACH_SAFE(ent, &old_head[i], next, ent_next) {3716nhash = hash_flow_ent(ent, mi->size);3717SLIST_INSERT_HEAD(&new_head[nhash], ent, next);3718}3719}37203721ti->state = new_head;3722ti->data = mi->size;3723cfg->head = new_head;3724cfg->size = mi->size;37253726mi->main_ptr = old_head;3727}37283729/*3730* Free unneded array.3731*/3732static void3733ta_flush_mod_fhash(void *ta_buf)3734{3735struct mod_item *mi;37363737mi = (struct mod_item *)ta_buf;3738if (mi->main_ptr != NULL)3739free(mi->main_ptr, M_IPFW);3740}37413742struct table_algo flow_hash = {3743.name = "flow:hash",3744.type = IPFW_TABLE_FLOW,3745.flags = TA_FLAG_DEFAULT,3746.ta_buf_size = sizeof(struct ta_buf_fhash),3747.init = ta_init_fhash,3748.destroy = ta_destroy_fhash,3749.prepare_add = ta_prepare_add_fhash,3750.prepare_del = ta_prepare_del_fhash,3751.add = ta_add_fhash,3752.del = ta_del_fhash,3753.flush_entry = ta_flush_fhash_entry,3754.foreach = ta_foreach_fhash,3755.dump_tentry = ta_dump_fhash_tentry,3756.find_tentry = ta_find_fhash_tentry,3757.dump_tinfo = ta_dump_fhash_tinfo,3758.need_modify = ta_need_modify_fhash,3759.prepare_mod = ta_prepare_mod_fhash,3760.fill_mod = ta_fill_mod_fhash,3761.modify = ta_modify_fhash,3762.flush_mod = ta_flush_mod_fhash,3763};37643765/*3766* Kernel fibs bindings.3767*3768* Implementation:3769*3770* Runtime part:3771* - fully relies on route API3772* - fib number is stored in ti->data3773*3774*/37753776static int ta_lookup_kfib(struct table_info *ti, void *key, uint32_t keylen,3777uint32_t *val);3778static int kfib_parse_opts(int *pfib, char *data);3779static void ta_print_kfib_config(void *ta_state, struct table_info *ti,3780char *buf, size_t bufsize);3781static int ta_init_kfib(struct ip_fw_chain *ch, void **ta_state,3782struct table_info *ti, char *data, uint8_t tflags);3783static void ta_destroy_kfib(void *ta_state, struct table_info *ti);3784static void ta_dump_kfib_tinfo(void *ta_state, struct table_info *ti,3785ipfw_ta_tinfo *tinfo);3786static int ta_dump_kfib_tentry(void *ta_state, struct table_info *ti, void *e,3787ipfw_obj_tentry *tent);3788static int ta_dump_kfib_tentry_int(int familt, const struct rtentry *rt,3789ipfw_obj_tentry *tent);3790static int ta_find_kfib_tentry(void *ta_state, struct table_info *ti,3791ipfw_obj_tentry *tent);3792static void ta_foreach_kfib(void *ta_state, struct table_info *ti,3793ta_foreach_f *f, void *arg);37943795static int3796ta_lookup_kfib(struct table_info *ti, void *key, uint32_t keylen,3797uint32_t *val)3798{3799#ifdef INET3800struct in_addr in;3801#endif3802int error;38033804error = ENOENT;3805#ifdef INET3806if (keylen == 4) {3807in.s_addr = *(in_addr_t *)key;3808NET_EPOCH_ASSERT();3809error = fib4_lookup(ti->data, in, 0, NHR_NONE, 0) != NULL;3810}3811#endif3812#ifdef INET63813if (keylen == 6)3814error = fib6_lookup(ti->data, (struct in6_addr *)key,38150, NHR_NONE, 0) != NULL;3816#endif38173818if (error != 0)3819return (0);38203821*val = 0;38223823return (1);3824}38253826/* Parse 'fib=%d' */3827static int3828kfib_parse_opts(int *pfib, char *data)3829{3830char *pdel, *pend, *s;3831int fibnum;38323833if (data == NULL)3834return (0);3835if ((pdel = strchr(data, ' ')) == NULL)3836return (0);3837while (*pdel == ' ')3838pdel++;3839if (strncmp(pdel, "fib=", 4) != 0)3840return (EINVAL);3841if ((s = strchr(pdel, ' ')) != NULL)3842*s++ = '\0';38433844pdel += 4;3845/* Need \d+ */3846fibnum = strtol(pdel, &pend, 10);3847if (*pend != '\0')3848return (EINVAL);38493850*pfib = fibnum;38513852return (0);3853}38543855static void3856ta_print_kfib_config(void *ta_state, struct table_info *ti, char *buf,3857size_t bufsize)3858{38593860if (ti->data != 0)3861snprintf(buf, bufsize, "%s fib=%lu", "addr:kfib", ti->data);3862else3863snprintf(buf, bufsize, "%s", "addr:kfib");3864}38653866static int3867ta_init_kfib(struct ip_fw_chain *ch, void **ta_state, struct table_info *ti,3868char *data, uint8_t tflags)3869{3870int error, fibnum;38713872fibnum = 0;3873if ((error = kfib_parse_opts(&fibnum, data)) != 0)3874return (error);38753876if (fibnum >= rt_numfibs)3877return (E2BIG);38783879ti->data = fibnum;3880ti->lookup = ta_lookup_kfib;38813882return (0);3883}38843885/*3886* Destroys table @ti3887*/3888static void3889ta_destroy_kfib(void *ta_state, struct table_info *ti)3890{38913892}38933894/*3895* Provide algo-specific table info3896*/3897static void3898ta_dump_kfib_tinfo(void *ta_state, struct table_info *ti, ipfw_ta_tinfo *tinfo)3899{39003901tinfo->flags = IPFW_TATFLAGS_AFDATA;3902tinfo->taclass4 = IPFW_TACLASS_RADIX;3903tinfo->count4 = 0;3904tinfo->itemsize4 = 128; /* table is readonly, value does not matter */3905tinfo->taclass6 = IPFW_TACLASS_RADIX;3906tinfo->count6 = 0;3907tinfo->itemsize6 = 128;3908}39093910static int3911ta_dump_kfib_tentry_int(int family, const struct rtentry *rt,3912ipfw_obj_tentry *tent)3913{3914uint32_t scopeid;3915int plen;39163917#ifdef INET3918if (family == AF_INET) {3919rt_get_inet_prefix_plen(rt, &tent->k.addr, &plen, &scopeid);3920tent->masklen = plen;3921tent->subtype = AF_INET;3922tent->v.kidx = 0;3923}3924#endif3925#ifdef INET63926if (family == AF_INET6) {3927rt_get_inet6_prefix_plen(rt, &tent->k.addr6, &plen, &scopeid);3928tent->masklen = plen;3929tent->subtype = AF_INET6;3930tent->v.kidx = 0;3931}3932#endif3933return (0);3934}39353936static int3937ta_find_kfib_tentry(void *ta_state, struct table_info *ti,3938ipfw_obj_tentry *tent)3939{3940struct rtentry *rt = NULL;3941struct route_nhop_data rnd;3942struct epoch_tracker et;3943int error;39443945NET_EPOCH_ENTER(et);39463947switch (tent->subtype) {3948#ifdef INET3949case AF_INET:3950rt = fib4_lookup_rt(ti->data, tent->k.addr, 0, 0, &rnd);3951break;3952#endif3953#ifdef INET63954case AF_INET6:3955rt = fib6_lookup_rt(ti->data, &tent->k.addr6, 0, 0, &rnd);3956break;3957#endif3958}3959if (rt != NULL)3960error = ta_dump_kfib_tentry_int(tent->subtype, rt, tent);3961else3962error = ENOENT;3963NET_EPOCH_EXIT(et);39643965return (error);3966}39673968struct kfib_dump_arg {3969struct rtentry *rt;3970int family;3971ta_foreach_f *f;3972void *arg;3973};39743975static int3976ta_dump_kfib_tentry(void *ta_state, struct table_info *ti, void *e,3977ipfw_obj_tentry *tent)3978{3979struct kfib_dump_arg *karg = (struct kfib_dump_arg *)e;39803981return (ta_dump_kfib_tentry_int(karg->family, karg->rt, tent));3982}39833984static int3985walk_wrapper_f(struct rtentry *rt, void *arg)3986{3987struct kfib_dump_arg *karg = (struct kfib_dump_arg *)arg;39883989karg->rt = rt;3990return (karg->f(karg, karg->arg));3991}39923993static void3994ta_foreach_kfib(void *ta_state, struct table_info *ti, ta_foreach_f *f,3995void *arg)3996{3997struct kfib_dump_arg karg = { .f = f, .arg = arg };39983999karg.family = AF_INET;4000rib_walk(ti->data, AF_INET, false, walk_wrapper_f, &karg);4001karg.family = AF_INET6;4002rib_walk(ti->data, AF_INET6, false, walk_wrapper_f, &karg);4003}40044005struct table_algo addr_kfib = {4006.name = "addr:kfib",4007.type = IPFW_TABLE_ADDR,4008.flags = TA_FLAG_READONLY,4009.ta_buf_size = 0,4010.init = ta_init_kfib,4011.destroy = ta_destroy_kfib,4012.foreach = ta_foreach_kfib,4013.dump_tentry = ta_dump_kfib_tentry,4014.find_tentry = ta_find_kfib_tentry,4015.dump_tinfo = ta_dump_kfib_tinfo,4016.print_config = ta_print_kfib_config,4017};40184019struct mac_radix_entry {4020struct radix_node rn[2];4021struct sa_mac sa;4022uint32_t value;4023uint8_t masklen;4024};40254026struct mac_radix_cfg {4027struct radix_node_head *head;4028size_t count;4029};40304031static int4032ta_lookup_mac_radix(struct table_info *ti, void *key, uint32_t keylen,4033uint32_t *val)4034{4035struct radix_node_head *rnh;40364037if (keylen == ETHER_ADDR_LEN) {4038struct mac_radix_entry *ent;4039struct sa_mac sa;4040KEY_LEN(sa) = KEY_LEN_MAC;4041memcpy(sa.mac_addr.octet, key, ETHER_ADDR_LEN);4042rnh = (struct radix_node_head *)ti->state;4043ent = (struct mac_radix_entry *)(rnh->rnh_matchaddr(&sa, &rnh->rh));4044if (ent != NULL) {4045*val = ent->value;4046return (1);4047}4048}4049return (0);4050}40514052static int4053ta_init_mac_radix(struct ip_fw_chain *ch, void **ta_state, struct table_info *ti,4054char *data, uint8_t tflags)4055{4056struct mac_radix_cfg *cfg;40574058if (!rn_inithead(&ti->state, OFF_LEN_MAC))4059return (ENOMEM);40604061cfg = malloc(sizeof(struct mac_radix_cfg), M_IPFW, M_WAITOK | M_ZERO);40624063*ta_state = cfg;4064ti->lookup = ta_lookup_mac_radix;40654066return (0);4067}40684069static void4070ta_destroy_mac_radix(void *ta_state, struct table_info *ti)4071{4072struct mac_radix_cfg *cfg;4073struct radix_node_head *rnh;40744075cfg = (struct mac_radix_cfg *)ta_state;40764077rnh = (struct radix_node_head *)(ti->state);4078rnh->rnh_walktree(&rnh->rh, flush_radix_entry, rnh);4079rn_detachhead(&ti->state);40804081free(cfg, M_IPFW);4082}40834084static void4085tei_to_sockaddr_ent_mac(struct tentry_info *tei, struct sockaddr *sa,4086struct sockaddr *ma, int *set_mask)4087{4088int mlen, i;4089struct sa_mac *addr, *mask;4090u_char *cp;40914092mlen = tei->masklen;4093addr = (struct sa_mac *)sa;4094mask = (struct sa_mac *)ma;4095/* Set 'total' structure length */4096KEY_LEN(*addr) = KEY_LEN_MAC;4097KEY_LEN(*mask) = KEY_LEN_MAC;40984099for (i = mlen, cp = mask->mac_addr.octet; i >= 8; i -= 8)4100*cp++ = 0xFF;4101if (i > 0)4102*cp = ~((1 << (8 - i)) - 1);41034104addr->mac_addr = *((struct ether_addr *)tei->paddr);4105for (i = 0; i < ETHER_ADDR_LEN; ++i)4106addr->mac_addr.octet[i] &= mask->mac_addr.octet[i];41074108if (mlen != 8 * ETHER_ADDR_LEN)4109*set_mask = 1;4110else4111*set_mask = 0;4112}41134114static int4115ta_prepare_add_mac_radix(struct ip_fw_chain *ch, struct tentry_info *tei,4116void *ta_buf)4117{4118struct ta_buf_radix *tb;4119struct mac_radix_entry *ent;4120struct sockaddr *addr, *mask;4121int mlen, set_mask;41224123tb = (struct ta_buf_radix *)ta_buf;41244125mlen = tei->masklen;4126set_mask = 0;41274128if (tei->subtype == AF_LINK) {4129if (mlen > 8 * ETHER_ADDR_LEN)4130return (EINVAL);4131ent = malloc(sizeof(*ent), M_IPFW_TBL, M_WAITOK | M_ZERO);4132ent->masklen = mlen;41334134addr = (struct sockaddr *)&ent->sa;4135mask = (struct sockaddr *)&tb->addr.mac.ma;4136tb->ent_ptr = ent;4137} else {4138/* Unknown CIDR type */4139return (EINVAL);4140}41414142tei_to_sockaddr_ent_mac(tei, addr, mask, &set_mask);4143/* Set pointers */4144tb->addr_ptr = addr;4145if (set_mask != 0)4146tb->mask_ptr = mask;41474148return (0);4149}41504151static int4152ta_add_mac_radix(void *ta_state, struct table_info *ti, struct tentry_info *tei,4153void *ta_buf, uint32_t *pnum)4154{4155struct mac_radix_cfg *cfg;4156struct radix_node_head *rnh;4157struct radix_node *rn;4158struct ta_buf_radix *tb;4159uint32_t *old_value, value;41604161cfg = (struct mac_radix_cfg *)ta_state;4162tb = (struct ta_buf_radix *)ta_buf;41634164/* Save current entry value from @tei */4165rnh = ti->state;4166((struct mac_radix_entry *)tb->ent_ptr)->value = tei->value;41674168/* Search for an entry first */4169rn = rnh->rnh_lookup(tb->addr_ptr, tb->mask_ptr, &rnh->rh);4170if (rn != NULL) {4171if ((tei->flags & TEI_FLAGS_UPDATE) == 0)4172return (EEXIST);4173/* Record already exists. Update value if we're asked to */4174old_value = &((struct mac_radix_entry *)rn)->value;41754176value = *old_value;4177*old_value = tei->value;4178tei->value = value;41794180/* Indicate that update has happened instead of addition */4181tei->flags |= TEI_FLAGS_UPDATED;4182*pnum = 0;41834184return (0);4185}41864187if ((tei->flags & TEI_FLAGS_DONTADD) != 0)4188return (EFBIG);41894190rn = rnh->rnh_addaddr(tb->addr_ptr, tb->mask_ptr, &rnh->rh, tb->ent_ptr);4191if (rn == NULL) {4192/* Unknown error */4193return (EINVAL);4194}41954196cfg->count++;4197tb->ent_ptr = NULL;4198*pnum = 1;41994200return (0);4201}42024203static int4204ta_prepare_del_mac_radix(struct ip_fw_chain *ch, struct tentry_info *tei,4205void *ta_buf)4206{4207struct ta_buf_radix *tb;4208struct sockaddr *addr, *mask;4209int mlen, set_mask;42104211tb = (struct ta_buf_radix *)ta_buf;42124213mlen = tei->masklen;4214set_mask = 0;42154216if (tei->subtype == AF_LINK) {4217if (mlen > 8 * ETHER_ADDR_LEN)4218return (EINVAL);42194220addr = (struct sockaddr *)&tb->addr.mac.sa;4221mask = (struct sockaddr *)&tb->addr.mac.ma;4222} else4223return (EINVAL);42244225tei_to_sockaddr_ent_mac(tei, addr, mask, &set_mask);4226tb->addr_ptr = addr;4227if (set_mask != 0)4228tb->mask_ptr = mask;42294230return (0);4231}42324233static int4234ta_del_mac_radix(void *ta_state, struct table_info *ti, struct tentry_info *tei,4235void *ta_buf, uint32_t *pnum)4236{4237struct mac_radix_cfg *cfg;4238struct radix_node_head *rnh;4239struct radix_node *rn;4240struct ta_buf_radix *tb;42414242cfg = (struct mac_radix_cfg *)ta_state;4243tb = (struct ta_buf_radix *)ta_buf;4244rnh = ti->state;42454246rn = rnh->rnh_deladdr(tb->addr_ptr, tb->mask_ptr, &rnh->rh);42474248if (rn == NULL)4249return (ENOENT);42504251/* Save entry value to @tei */4252tei->value = ((struct mac_radix_entry *)rn)->value;42534254tb->ent_ptr = rn;4255cfg->count--;4256*pnum = 1;42574258return (0);4259}42604261static void4262ta_foreach_mac_radix(void *ta_state, struct table_info *ti, ta_foreach_f *f,4263void *arg)4264{4265struct radix_node_head *rnh;42664267rnh = (struct radix_node_head *)(ti->state);4268rnh->rnh_walktree(&rnh->rh, (walktree_f_t *)f, arg);4269}42704271static void4272ta_dump_mac_radix_tinfo(void *ta_state, struct table_info *ti, ipfw_ta_tinfo *tinfo)4273{4274struct mac_radix_cfg *cfg;42754276cfg = (struct mac_radix_cfg *)ta_state;42774278tinfo->flags = IPFW_TATFLAGS_AFDATA | IPFW_TATFLAGS_AFITEM;4279tinfo->taclass4 = IPFW_TACLASS_RADIX;4280tinfo->count4 = cfg->count;4281tinfo->itemsize4 = sizeof(struct mac_radix_entry);4282}42834284static int4285ta_dump_mac_radix_tentry(void *ta_state, struct table_info *ti, void *e,4286ipfw_obj_tentry *tent)4287{4288struct mac_radix_entry *n = (struct mac_radix_entry *)e;42894290memcpy(tent->k.mac, n->sa.mac_addr.octet, ETHER_ADDR_LEN);4291tent->masklen = n->masklen;4292tent->subtype = AF_LINK;4293tent->v.kidx = n->value;42944295return (0);4296}42974298static int4299ta_find_mac_radix_tentry(void *ta_state, struct table_info *ti,4300ipfw_obj_tentry *tent)4301{4302struct radix_node_head *rnh;4303void *e;43044305e = NULL;4306if (tent->subtype == AF_LINK) {4307struct sa_mac sa;4308KEY_LEN(sa) = KEY_LEN_MAC;4309memcpy(sa.mac_addr.octet, tent->k.mac, ETHER_ADDR_LEN);4310rnh = (struct radix_node_head *)ti->state;4311e = rnh->rnh_matchaddr(&sa, &rnh->rh);4312}43134314if (e != NULL) {4315ta_dump_mac_radix_tentry(ta_state, ti, e, tent);4316return (0);4317}43184319return (ENOENT);4320}43214322struct table_algo mac_radix = {4323.name = "mac:radix",4324.type = IPFW_TABLE_MAC,4325.flags = TA_FLAG_DEFAULT,4326.ta_buf_size = sizeof(struct ta_buf_radix),4327.init = ta_init_mac_radix,4328.destroy = ta_destroy_mac_radix,4329.prepare_add = ta_prepare_add_mac_radix,4330.prepare_del = ta_prepare_del_mac_radix,4331.add = ta_add_mac_radix,4332.del = ta_del_mac_radix,4333.flush_entry = ta_flush_radix_entry,4334.foreach = ta_foreach_mac_radix,4335.dump_tentry = ta_dump_mac_radix_tentry,4336.find_tentry = ta_find_mac_radix_tentry,4337.dump_tinfo = ta_dump_mac_radix_tinfo,4338.need_modify = ta_need_modify_radix,4339};43404341void4342ipfw_table_algo_init(struct ip_fw_chain *ch)4343{4344size_t sz;43454346/*4347* Register all algorithms presented here.4348*/4349sz = sizeof(struct table_algo);4350ipfw_add_table_algo(ch, &addr_radix, sz, &addr_radix.idx);4351ipfw_add_table_algo(ch, &addr_hash, sz, &addr_hash.idx);4352ipfw_add_table_algo(ch, &iface_idx, sz, &iface_idx.idx);4353ipfw_add_table_algo(ch, &number_array, sz, &number_array.idx);4354ipfw_add_table_algo(ch, &flow_hash, sz, &flow_hash.idx);4355ipfw_add_table_algo(ch, &addr_kfib, sz, &addr_kfib.idx);4356ipfw_add_table_algo(ch, &mac_radix, sz, &mac_radix.idx);4357}43584359void4360ipfw_table_algo_destroy(struct ip_fw_chain *ch)4361{43624363ipfw_del_table_algo(ch, addr_radix.idx);4364ipfw_del_table_algo(ch, addr_hash.idx);4365ipfw_del_table_algo(ch, iface_idx.idx);4366ipfw_del_table_algo(ch, number_array.idx);4367ipfw_del_table_algo(ch, flow_hash.idx);4368ipfw_del_table_algo(ch, addr_kfib.idx);4369ipfw_del_table_algo(ch, mac_radix.idx);4370}437143724373