mirror of
https://github.com/NLnetLabs/unbound.git
synced 2026-02-16 00:58:14 -05:00
infra cache config.
git-svn-id: file:///svn/unbound/trunk@322 be551aaa-1e26-0410-a405-d3ace91eadb9
This commit is contained in:
parent
cb0d7805e2
commit
0fa6818d48
7 changed files with 120 additions and 57 deletions
|
|
@ -63,6 +63,24 @@ server:
|
|||
# more slabs reduce lock contention, but fragment memory usage.
|
||||
# rrset-cache-slabs: 4
|
||||
|
||||
# the time to live (TTL) value for cached roundtrip times and
|
||||
# EDNS version information for hosts. In seconds.
|
||||
# infra-host-ttl: 900
|
||||
|
||||
# the time to live (TTL) value for cached lame delegations. In sec.
|
||||
# infra-lame-ttl: 900
|
||||
|
||||
# the number of slabs to use for the Infrastructure cache.
|
||||
# the number of slabs must be a power of 2.
|
||||
# more slabs reduce lock contention, but fragment memory usage.
|
||||
# infra-cache-slabs: 4
|
||||
|
||||
# the maximum number of hosts that are cached (roundtrip times, EDNS).
|
||||
# infra-cache-numhosts: 1000
|
||||
|
||||
# the maximum number of lame zones per host that are cached.
|
||||
# infra-cache-numlame: 1000
|
||||
|
||||
# Enable IPv4, "yes" or "no".
|
||||
# do-ip4: yes
|
||||
|
||||
|
|
|
|||
|
|
@ -79,6 +79,18 @@ Number of bytes size of the RRset cache. Default is 4 megabytes.
|
|||
.It \fBrrset-cache-slabs:\fR <number>
|
||||
Number of slabs in the RRset cache. Slabs reduce lock contention by threads.
|
||||
Must be set to a power of 2.
|
||||
.It \fBinfra-host-ttl:\fR <seconds>
|
||||
Time to live for entries in the host cache. The host cache contains
|
||||
roundtrip timing and EDNS support information. Default is 900.
|
||||
.It \fBinfra-lame-ttl:\fR <seconds>
|
||||
The time to live when a delegation is discovered to be lame. Default is 900.
|
||||
.It \fBinfra-cache-slabs:\fR <number>
|
||||
Number of slabs in the infrastructure cache. Slabs reduce lock contention
|
||||
by threads. Must be set to a power of 2.
|
||||
.It \fBinfra-cache-numhosts:\fR <number>
|
||||
Number of hosts for which information is cached. Default is 1000.
|
||||
.It \fBinfra-cache-numlame:\fR <number>
|
||||
Number of zones per host for which lameness is cached. Default is 1000.
|
||||
.It \fBdo-ip4:\fR <yes or no>
|
||||
Enable or disable whether ip4 queries are answered. Default is yes.
|
||||
.It \fBdo-ip6:\fR <yes or no>
|
||||
|
|
|
|||
72
services/cache/infra.c
vendored
72
services/cache/infra.c
vendored
|
|
@ -44,6 +44,7 @@
|
|||
#include "util/storage/lookup3.h"
|
||||
#include "util/log.h"
|
||||
#include "util/net_help.h"
|
||||
#include "util/config_file.h"
|
||||
|
||||
/** calculate size for the hashtable, does not count size of lameness,
|
||||
* so the hashtable is a fixed number of items */
|
||||
|
|
@ -87,26 +88,36 @@ infra_host_deldatafunc(void* d, void* ATTR_UNUSED(arg))
|
|||
free(data);
|
||||
}
|
||||
|
||||
struct slabhash*
|
||||
struct infra_cache*
|
||||
infra_create(struct config_file* cfg)
|
||||
{
|
||||
struct infra_cache* infra = (struct infra_cache*)calloc(1,
|
||||
sizeof(struct infra_cache));
|
||||
/* TODO: use config settings */
|
||||
/* the size of the lameness tables are not counted */
|
||||
size_t maxmem = HOST_DEFAULT_SIZE * (sizeof(struct infra_host_key) +
|
||||
sizeof(struct infra_host_data));
|
||||
struct slabhash* infra = slabhash_create(HASH_DEFAULT_SLABS,
|
||||
size_t maxmem = cfg->infra_cache_numhosts *
|
||||
(sizeof(struct infra_host_key)+sizeof(struct infra_host_data));
|
||||
infra->hosts = slabhash_create(cfg->infra_cache_slabs,
|
||||
INFRA_HOST_STARTSIZE, maxmem, &infra_host_sizefunc,
|
||||
&infra_host_compfunc, &infra_host_delkeyfunc,
|
||||
&infra_host_deldatafunc, NULL);
|
||||
if(!infra->hosts) {
|
||||
free(infra);
|
||||
return NULL;
|
||||
}
|
||||
infra->host_ttl = cfg->host_ttl;
|
||||
infra->lame_ttl = cfg->lame_ttl;
|
||||
infra->max_lame = cfg->infra_cache_numlame;
|
||||
return infra;
|
||||
}
|
||||
|
||||
void
|
||||
infra_delete(struct slabhash* infra)
|
||||
infra_delete(struct infra_cache* infra)
|
||||
{
|
||||
if(!infra)
|
||||
return;
|
||||
slabhash_delete(infra);
|
||||
slabhash_delete(infra->hosts);
|
||||
free(infra);
|
||||
}
|
||||
|
||||
/** calculate the hash value for a host key */
|
||||
|
|
@ -121,7 +132,7 @@ hash_addr(struct sockaddr_storage* addr, socklen_t addrlen)
|
|||
|
||||
/** lookup version that does not check host ttl (you check it) */
|
||||
static struct lruhash_entry*
|
||||
infra_lookup_host_nottl(struct slabhash* infra,
|
||||
infra_lookup_host_nottl(struct infra_cache* infra,
|
||||
struct sockaddr_storage* addr, socklen_t addrlen, int wr)
|
||||
{
|
||||
struct infra_host_key k;
|
||||
|
|
@ -130,11 +141,11 @@ infra_lookup_host_nottl(struct slabhash* infra,
|
|||
k.entry.hash = hash_addr(addr, addrlen);
|
||||
k.entry.key = (void*)&k;
|
||||
k.entry.data = NULL;
|
||||
return slabhash_lookup(infra, k.entry.hash, &k, wr);
|
||||
return slabhash_lookup(infra->hosts, k.entry.hash, &k, wr);
|
||||
}
|
||||
|
||||
struct infra_host_data*
|
||||
infra_lookup_host(struct slabhash* infra,
|
||||
infra_lookup_host(struct infra_cache* infra,
|
||||
struct sockaddr_storage* addr, socklen_t addrlen, int wr,
|
||||
time_t timenow, struct infra_host_key** key)
|
||||
{
|
||||
|
|
@ -162,7 +173,8 @@ infra_lookup_host(struct slabhash* infra,
|
|||
* @return: the new entry or NULL on malloc failure.
|
||||
*/
|
||||
static struct lruhash_entry*
|
||||
new_host_entry(struct sockaddr_storage* addr, socklen_t addrlen, time_t tm)
|
||||
new_host_entry(struct infra_cache* infra, struct sockaddr_storage* addr,
|
||||
socklen_t addrlen, time_t tm)
|
||||
{
|
||||
struct infra_host_data* data;
|
||||
struct infra_host_key* key = (struct infra_host_key*)malloc(
|
||||
|
|
@ -181,7 +193,7 @@ new_host_entry(struct sockaddr_storage* addr, socklen_t addrlen, time_t tm)
|
|||
key->entry.data = (void*)data;
|
||||
key->addrlen = addrlen;
|
||||
memcpy(&key->addr, addr, addrlen);
|
||||
data->ttl = tm + HOST_TTL;
|
||||
data->ttl = tm + infra->host_ttl;
|
||||
data->lameness = NULL;
|
||||
data->edns_version = 0;
|
||||
rtt_init(&data->rtt);
|
||||
|
|
@ -189,7 +201,7 @@ new_host_entry(struct sockaddr_storage* addr, socklen_t addrlen, time_t tm)
|
|||
}
|
||||
|
||||
int
|
||||
infra_host(struct slabhash* infra, struct sockaddr_storage* addr,
|
||||
infra_host(struct infra_cache* infra, struct sockaddr_storage* addr,
|
||||
socklen_t addrlen, time_t timenow, int* edns_vs, int* to)
|
||||
{
|
||||
struct lruhash_entry* e = infra_lookup_host_nottl(infra, addr,
|
||||
|
|
@ -203,7 +215,7 @@ infra_host(struct slabhash* infra, struct sockaddr_storage* addr,
|
|||
/* if its still there we have a writelock, init */
|
||||
/* re-initialise */
|
||||
data = (struct infra_host_data*)e->data;
|
||||
data->ttl = timenow + HOST_TTL;
|
||||
data->ttl = timenow + infra->host_ttl;
|
||||
rtt_init(&data->rtt);
|
||||
/* do not touch lameness, it may be valid still */
|
||||
data->edns_version = 0;
|
||||
|
|
@ -211,12 +223,12 @@ infra_host(struct slabhash* infra, struct sockaddr_storage* addr,
|
|||
}
|
||||
if(!e) {
|
||||
/* insert new entry */
|
||||
if(!(e = new_host_entry(addr, addrlen, timenow)))
|
||||
if(!(e = new_host_entry(infra, addr, addrlen, timenow)))
|
||||
return 0;
|
||||
data = (struct infra_host_data*)e->data;
|
||||
*to = rtt_timeout(&data->rtt);
|
||||
*edns_vs = data->edns_version;
|
||||
slabhash_insert(infra, e->hash, e, data, NULL);
|
||||
slabhash_insert(infra->hosts, e->hash, e, data, NULL);
|
||||
return 1;
|
||||
}
|
||||
/* use existing entry */
|
||||
|
|
@ -304,7 +316,7 @@ infra_lame_deldatafunc(void* d, void* ATTR_UNUSED(arg))
|
|||
}
|
||||
|
||||
int
|
||||
infra_set_lame(struct slabhash* infra,
|
||||
infra_set_lame(struct infra_cache* infra,
|
||||
struct sockaddr_storage* addr, socklen_t addrlen,
|
||||
uint8_t* name, size_t namelen, time_t timenow)
|
||||
{
|
||||
|
|
@ -336,12 +348,12 @@ infra_set_lame(struct slabhash* infra,
|
|||
k->entry.hash = hash_lameness(name, namelen);
|
||||
k->entry.key = (void*)k;
|
||||
k->entry.data = (void*)d;
|
||||
d->ttl = timenow + HOST_LAME_TTL;
|
||||
d->ttl = timenow + infra->lame_ttl;
|
||||
k->namelen = namelen;
|
||||
e = infra_lookup_host_nottl(infra, addr, addrlen, 1);
|
||||
if(!e) {
|
||||
/* insert it */
|
||||
if(!(e = new_host_entry(addr, addrlen, timenow))) {
|
||||
if(!(e = new_host_entry(infra, addr, addrlen, timenow))) {
|
||||
free(k->zonename);
|
||||
free(k);
|
||||
free(d);
|
||||
|
|
@ -355,14 +367,14 @@ infra_set_lame(struct slabhash* infra,
|
|||
if(!data->lameness) {
|
||||
/* create hash table if not there already */
|
||||
data->lameness = lruhash_create(INFRA_LAME_STARTSIZE,
|
||||
INFRA_LAME_MAXMEM*(sizeof(struct infra_lame_key)+
|
||||
infra->max_lame*(sizeof(struct infra_lame_key)+
|
||||
sizeof(struct infra_lame_data)), infra_lame_sizefunc,
|
||||
infra_lame_compfunc, infra_lame_delkeyfunc,
|
||||
infra_lame_deldatafunc, NULL);
|
||||
if(!data->lameness) {
|
||||
log_err("set_lame: malloc failure");
|
||||
if(needtoinsert) slabhash_insert(infra, e->hash, e,
|
||||
e->data, NULL);
|
||||
if(needtoinsert) slabhash_insert(infra->hosts,
|
||||
e->hash, e, e->data, NULL);
|
||||
else lock_rw_unlock(&e->lock);
|
||||
free(k->zonename);
|
||||
free(k);
|
||||
|
|
@ -374,13 +386,13 @@ infra_set_lame(struct slabhash* infra,
|
|||
lruhash_insert(data->lameness, k->entry.hash, &k->entry, d, NULL);
|
||||
|
||||
if(needtoinsert)
|
||||
slabhash_insert(infra, e->hash, e, e->data, NULL);
|
||||
slabhash_insert(infra->hosts, e->hash, e, e->data, NULL);
|
||||
else lock_rw_unlock(&e->lock);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int
|
||||
infra_rtt_update(struct slabhash* infra,
|
||||
infra_rtt_update(struct infra_cache* infra,
|
||||
struct sockaddr_storage* addr, socklen_t addrlen,
|
||||
int roundtrip, time_t timenow)
|
||||
{
|
||||
|
|
@ -389,25 +401,25 @@ infra_rtt_update(struct slabhash* infra,
|
|||
struct infra_host_data* data;
|
||||
int needtoinsert = 0;
|
||||
if(!e) {
|
||||
if(!(e = new_host_entry(addr, addrlen, timenow)))
|
||||
if(!(e = new_host_entry(infra, addr, addrlen, timenow)))
|
||||
return 0;
|
||||
needtoinsert = 1;
|
||||
}
|
||||
/* have an entry, update the rtt, and the ttl */
|
||||
data = (struct infra_host_data*)e->data;
|
||||
data->ttl = timenow + HOST_TTL;
|
||||
data->ttl = timenow + infra->host_ttl;
|
||||
if(roundtrip == -1)
|
||||
rtt_lost(&data->rtt);
|
||||
else rtt_update(&data->rtt, roundtrip);
|
||||
|
||||
if(needtoinsert)
|
||||
slabhash_insert(infra, e->hash, e, e->data, NULL);
|
||||
slabhash_insert(infra->hosts, e->hash, e, e->data, NULL);
|
||||
else lock_rw_unlock(&e->lock);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int
|
||||
infra_edns_update(struct slabhash* infra,
|
||||
infra_edns_update(struct infra_cache* infra,
|
||||
struct sockaddr_storage* addr, socklen_t addrlen,
|
||||
int edns_version, time_t timenow)
|
||||
{
|
||||
|
|
@ -416,17 +428,17 @@ infra_edns_update(struct slabhash* infra,
|
|||
struct infra_host_data* data;
|
||||
int needtoinsert = 0;
|
||||
if(!e) {
|
||||
if(!(e = new_host_entry(addr, addrlen, timenow)))
|
||||
if(!(e = new_host_entry(infra, addr, addrlen, timenow)))
|
||||
return 0;
|
||||
needtoinsert = 1;
|
||||
}
|
||||
/* have an entry, update the rtt, and the ttl */
|
||||
data = (struct infra_host_data*)e->data;
|
||||
data->ttl = timenow + HOST_TTL;
|
||||
data->ttl = timenow + infra->host_ttl;
|
||||
data->edns_version = edns_version;
|
||||
|
||||
if(needtoinsert)
|
||||
slabhash_insert(infra, e->hash, e, e->data, NULL);
|
||||
slabhash_insert(infra->hosts, e->hash, e, e->data, NULL);
|
||||
else lock_rw_unlock(&e->lock);
|
||||
return 1;
|
||||
}
|
||||
|
|
|
|||
36
services/cache/infra.h
vendored
36
services/cache/infra.h
vendored
|
|
@ -93,31 +93,37 @@ struct infra_lame_data {
|
|||
time_t ttl;
|
||||
};
|
||||
|
||||
/** default TTL value for host information, in seconds */
|
||||
#define HOST_TTL 900
|
||||
/** default TTL for Lameness information, in seconds */
|
||||
#define HOST_LAME_TTL 900
|
||||
/** default size of the host cache, number of entries */
|
||||
#define HOST_DEFAULT_SIZE 1000
|
||||
/**
|
||||
* Infra cache
|
||||
*/
|
||||
struct infra_cache {
|
||||
/** The hash table with hosts */
|
||||
struct slabhash* hosts;
|
||||
/** TTL value for host information, in seconds */
|
||||
int host_ttl;
|
||||
/** TTL for Lameness information, in seconds */
|
||||
int lame_ttl;
|
||||
/** infra lame cache max memory per host, for this many entries */
|
||||
size_t max_lame;
|
||||
};
|
||||
|
||||
/** infra host cache default hash lookup size */
|
||||
#define INFRA_HOST_STARTSIZE 32
|
||||
/** infra lame cache default hash lookup size */
|
||||
#define INFRA_LAME_STARTSIZE 2
|
||||
/** infra lame cache max memory per host, for this many entries */
|
||||
#define INFRA_LAME_MAXMEM 1000
|
||||
|
||||
/**
|
||||
* Create infra cache.
|
||||
* @param cfg: config parameters.
|
||||
* @return: new infra cache, or NULL.
|
||||
*/
|
||||
struct slabhash* infra_create(struct config_file* cfg);
|
||||
struct infra_cache* infra_create(struct config_file* cfg);
|
||||
|
||||
/**
|
||||
* Delete infra cache.
|
||||
* @param infra: infrastructure cache to delete.
|
||||
*/
|
||||
void infra_delete(struct slabhash* infra);
|
||||
void infra_delete(struct infra_cache* infra);
|
||||
|
||||
/**
|
||||
* Lookup host data
|
||||
|
|
@ -129,7 +135,7 @@ void infra_delete(struct slabhash* infra);
|
|||
* @param key: the key for the host, returned so caller can unlock when done.
|
||||
* @return: host data or NULL if not found or expired.
|
||||
*/
|
||||
struct infra_host_data* infra_lookup_host(struct slabhash* infra,
|
||||
struct infra_host_data* infra_lookup_host(struct infra_cache* infra,
|
||||
struct sockaddr_storage* addr, socklen_t addrlen, int wr,
|
||||
time_t timenow, struct infra_host_key** key);
|
||||
|
||||
|
|
@ -145,7 +151,7 @@ struct infra_host_data* infra_lookup_host(struct slabhash* infra,
|
|||
* @param to: timeout to use, is returned.
|
||||
* @return: 0 on error.
|
||||
*/
|
||||
int infra_host(struct slabhash* infra, struct sockaddr_storage* addr,
|
||||
int infra_host(struct infra_cache* infra, struct sockaddr_storage* addr,
|
||||
socklen_t addrlen, time_t timenow, int* edns_vs, int* to);
|
||||
|
||||
/**
|
||||
|
|
@ -170,7 +176,7 @@ int infra_lookup_lame(struct infra_host_data* host,
|
|||
* @param timenow: what time it is now.
|
||||
* @return: 0 on error.
|
||||
*/
|
||||
int infra_set_lame(struct slabhash* infra,
|
||||
int infra_set_lame(struct infra_cache* infra,
|
||||
struct sockaddr_storage* addr, socklen_t addrlen,
|
||||
uint8_t* name, size_t namelen, time_t timenow);
|
||||
|
||||
|
|
@ -184,7 +190,7 @@ int infra_set_lame(struct slabhash* infra,
|
|||
* @param timenow: what time it is now.
|
||||
* @return: 0 on error.
|
||||
*/
|
||||
int infra_rtt_update(struct slabhash* infra,
|
||||
int infra_rtt_update(struct infra_cache* infra,
|
||||
struct sockaddr_storage* addr, socklen_t addrlen,
|
||||
int roundtrip, time_t timenow);
|
||||
|
||||
|
|
@ -197,7 +203,7 @@ int infra_rtt_update(struct slabhash* infra,
|
|||
* @param timenow: what time it is now.
|
||||
* @return: 0 on error.
|
||||
*/
|
||||
int infra_edns_update(struct slabhash* infra,
|
||||
int infra_edns_update(struct infra_cache* infra,
|
||||
struct sockaddr_storage* addr, socklen_t addrlen,
|
||||
int edns_version, time_t timenow);
|
||||
|
||||
|
|
|
|||
|
|
@ -152,7 +152,7 @@ infra_test()
|
|||
int one = 1;
|
||||
uint8_t* zone = (uint8_t*)"\007example\003com\000";
|
||||
size_t zonelen = 13;
|
||||
struct slabhash* slab;
|
||||
struct infra_cache* slab;
|
||||
struct config_file* cfg = config_create();
|
||||
time_t now = 0;
|
||||
int vs, to;
|
||||
|
|
@ -161,35 +161,35 @@ infra_test()
|
|||
|
||||
slab = infra_create(cfg);
|
||||
unit_assert( infra_host(slab, (struct sockaddr_storage*)&one,
|
||||
sizeof(int), now, &vs, &to) );
|
||||
(socklen_t)sizeof(int), now, &vs, &to) );
|
||||
unit_assert( vs == 0 && to == 3000 );
|
||||
|
||||
unit_assert( infra_rtt_update(slab, (struct sockaddr_storage*)&one,
|
||||
sizeof(int), -1, now) );
|
||||
(socklen_t)sizeof(int), -1, now) );
|
||||
unit_assert( infra_host(slab, (struct sockaddr_storage*)&one,
|
||||
sizeof(int), now, &vs, &to) );
|
||||
(socklen_t)sizeof(int), now, &vs, &to) );
|
||||
unit_assert( vs == 0 && to == 6000 );
|
||||
|
||||
unit_assert( infra_edns_update(slab, (struct sockaddr_storage*)&one,
|
||||
sizeof(int), -1, now) );
|
||||
(socklen_t)sizeof(int), -1, now) );
|
||||
unit_assert( infra_host(slab, (struct sockaddr_storage*)&one,
|
||||
sizeof(int), now, &vs, &to) );
|
||||
(socklen_t)sizeof(int), now, &vs, &to) );
|
||||
unit_assert( vs == -1 && to == 6000 );
|
||||
|
||||
now += HOST_TTL + 10;
|
||||
now += cfg->host_ttl + 10;
|
||||
unit_assert( infra_host(slab, (struct sockaddr_storage*)&one,
|
||||
sizeof(int), now, &vs, &to) );
|
||||
(socklen_t)sizeof(int), now, &vs, &to) );
|
||||
unit_assert( vs == 0 && to == 3000 );
|
||||
|
||||
unit_assert( infra_set_lame(slab, (struct sockaddr_storage*)&one,
|
||||
sizeof(int), zone, zonelen, now) );
|
||||
(socklen_t)sizeof(int), zone, zonelen, now) );
|
||||
unit_assert( (d=infra_lookup_host(slab, (struct sockaddr_storage*)&one,
|
||||
sizeof(int), 0, now, &k)) );
|
||||
unit_assert( d->ttl == now+HOST_TTL );
|
||||
(socklen_t)sizeof(int), 0, now, &k)) );
|
||||
unit_assert( d->ttl == now+cfg->host_ttl );
|
||||
unit_assert( d->edns_version == 0 );
|
||||
unit_assert( infra_lookup_lame(d, zone, zonelen, now) );
|
||||
unit_assert( !infra_lookup_lame(d, zone, zonelen,
|
||||
now+HOST_LAME_TTL+10) );
|
||||
now+cfg->lame_ttl+10) );
|
||||
unit_assert( !infra_lookup_lame(d, (uint8_t*)"\000", 1, now) );
|
||||
lock_rw_unlock(&k->entry.lock);
|
||||
|
||||
|
|
|
|||
|
|
@ -83,6 +83,11 @@ config_create()
|
|||
cfg->num_queries_per_thread = 1024;
|
||||
cfg->rrset_cache_size = 4 * 1024 * 1024;
|
||||
cfg->rrset_cache_slabs = 4;
|
||||
cfg->host_ttl = 900;
|
||||
cfg->lame_ttl = 900;
|
||||
cfg->infra_cache_slabs = 4;
|
||||
cfg->infra_cache_numhosts = 1000;
|
||||
cfg->infra_cache_numlame = 1000;
|
||||
if(!(cfg->fwd_address = strdup(""))) goto error_exit;
|
||||
if(!(cfg->username = strdup(""))) goto error_exit;
|
||||
if(!(cfg->chrootdir = strdup(""))) goto error_exit;
|
||||
|
|
|
|||
|
|
@ -81,6 +81,16 @@ struct config_file {
|
|||
size_t rrset_cache_size;
|
||||
/** slabs in the rrset cache */
|
||||
size_t rrset_cache_slabs;
|
||||
/** host cache ttl in seconds */
|
||||
int host_ttl;
|
||||
/** host is lame for a zone ttl, in seconds */
|
||||
int lame_ttl;
|
||||
/** number of slabs in the infra host cache */
|
||||
size_t infra_cache_slabs;
|
||||
/** max number of hosts in the infra cache */
|
||||
size_t infra_cache_numhosts;
|
||||
/** max number of lame zones per host in the infra cache */
|
||||
size_t infra_cache_numlame;
|
||||
|
||||
/** forwarder address. string. If not NULL fwder mode is enabled. */
|
||||
char* fwd_address;
|
||||
|
|
|
|||
Loading…
Reference in a new issue