1c43cef4e5
Signed-off-by: Nico Schottelius <nico@nico-notebook.schottelius.org>
631 lines
15 KiB
C
631 lines
15 KiB
C
/*
|
|
* addrmap.c -- address mapping routines
|
|
*
|
|
* part of TAYGA <http://www.litech.org/tayga/>
|
|
* Copyright (C) 2010 Nathan Lutchansky <lutchann@litech.org>
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*/
|
|
|
|
#include <tayga.h>
|
|
|
|
extern struct config *gcfg;
|
|
extern time_t now;
|
|
|
|
int validate_ip4_addr(const struct in_addr *a)
|
|
{
|
|
/* First octet == 0 */
|
|
if (!(a->s_addr & htonl(0xff000000)))
|
|
return -1;
|
|
|
|
/* First octet == 127 */
|
|
if ((a->s_addr & htonl(0xff000000)) == htonl(0x7f000000))
|
|
return -1;
|
|
|
|
/* Link-local block 169.254.0.0/16 */
|
|
if ((a->s_addr & htonl(0xffff0000)) == htonl(0xa9fe0000))
|
|
return -1;
|
|
|
|
/* Class D & E */
|
|
if ((a->s_addr & htonl(0xe0000000)) == htonl(0xe0000000))
|
|
return -1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int validate_ip6_addr(const struct in6_addr *a)
|
|
{
|
|
/* Well-known prefix for NAT64 */
|
|
if (a->s6_addr32[0] == WKPF && !a->s6_addr32[1] && !a->s6_addr32[2])
|
|
return 0;
|
|
|
|
/* Reserved per RFC 2373 */
|
|
if (!a->s6_addr[0])
|
|
return -1;
|
|
|
|
/* Multicast addresses */
|
|
if (a->s6_addr[0] == 0xff)
|
|
return -1;
|
|
|
|
/* Link-local unicast addresses */
|
|
if ((a->s6_addr16[0] & htons(0xffc0)) == htons(0xfe80))
|
|
return -1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int is_private_ip4_addr(const struct in_addr *a)
|
|
{
|
|
/* 10.0.0.0/8 */
|
|
if ((a->s_addr & htonl(0xff000000)) == htonl(0x0a000000))
|
|
return -1;
|
|
|
|
/* 172.16.0.0/12 */
|
|
if ((a->s_addr & htonl(0xfff00000)) == htonl(0xac100000))
|
|
return -1;
|
|
|
|
/* 192.0.2.0/24 */
|
|
if ((a->s_addr & htonl(0xffffff00)) == htonl(0xc0000200))
|
|
return -1;
|
|
|
|
/* 192.168.0.0/16 */
|
|
if ((a->s_addr & htonl(0xffff0000)) == htonl(0xc0a80000))
|
|
return -1;
|
|
|
|
/* 198.18.0.0/15 */
|
|
if ((a->s_addr & htonl(0xfffe0000)) == htonl(0xc6120000))
|
|
return -1;
|
|
|
|
/* 198.51.100.0/24 */
|
|
if ((a->s_addr & htonl(0xffffff00)) == htonl(0xc6336400))
|
|
return -1;
|
|
|
|
/* 203.0.113.0/24 */
|
|
if ((a->s_addr & htonl(0xffffff00)) == htonl(0xcb007100))
|
|
return -1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int calc_ip4_mask(struct in_addr *mask, const struct in_addr *addr, int len)
|
|
{
|
|
mask->s_addr = htonl(~((1 << (32 - len)) - 1));
|
|
if (addr && (addr->s_addr & ~mask->s_addr))
|
|
return -1;
|
|
return 0;
|
|
|
|
}
|
|
|
|
int calc_ip6_mask(struct in6_addr *mask, const struct in6_addr *addr, int len)
|
|
{
|
|
if (len > 32) {
|
|
mask->s6_addr32[0] = ~0;
|
|
if (len > 64) {
|
|
mask->s6_addr32[1] = ~0;
|
|
if (len > 96) {
|
|
mask->s6_addr32[2] = ~0;
|
|
mask->s6_addr32[3] =
|
|
htonl(~((1 << (128 - len)) - 1));
|
|
} else {
|
|
mask->s6_addr32[2] =
|
|
htonl(~((1 << (96 - len)) - 1));
|
|
mask->s6_addr32[3] = 0;
|
|
}
|
|
} else {
|
|
mask->s6_addr32[1] = htonl(~((1 << (64 - len)) - 1));
|
|
mask->s6_addr32[2] = 0;
|
|
mask->s6_addr32[3] = 0;
|
|
}
|
|
} else {
|
|
mask->s6_addr32[0] = htonl(~((1 << (32 - len)) - 1));
|
|
mask->s6_addr32[1] = 0;
|
|
mask->s6_addr32[2] = 0;
|
|
mask->s6_addr32[3] = 0;
|
|
}
|
|
if (!addr)
|
|
return 0;
|
|
if ((addr->s6_addr32[0] & ~mask->s6_addr32[0]) ||
|
|
(addr->s6_addr32[1] & ~mask->s6_addr32[1]) ||
|
|
(addr->s6_addr32[2] & ~mask->s6_addr32[2]) ||
|
|
(addr->s6_addr32[3] & ~mask->s6_addr32[3]))
|
|
return -1;
|
|
return 0;
|
|
}
|
|
|
|
static uint32_t hash_ip4(const struct in_addr *addr4)
|
|
{
|
|
return ((uint32_t)(addr4->s_addr *
|
|
gcfg->rand[0])) >> (32 - gcfg->hash_bits);
|
|
}
|
|
|
|
static uint32_t hash_ip6(const struct in6_addr *addr6)
|
|
{
|
|
uint32_t h;
|
|
|
|
h = ((uint32_t)addr6->s6_addr16[0] + gcfg->rand[0]) *
|
|
((uint32_t)addr6->s6_addr16[1] + gcfg->rand[1]);
|
|
h ^= ((uint32_t)addr6->s6_addr16[2] + gcfg->rand[2]) *
|
|
((uint32_t)addr6->s6_addr16[3] + gcfg->rand[3]);
|
|
h ^= ((uint32_t)addr6->s6_addr16[4] + gcfg->rand[4]) *
|
|
((uint32_t)addr6->s6_addr16[5] + gcfg->rand[5]);
|
|
h ^= ((uint32_t)addr6->s6_addr16[6] + gcfg->rand[6]) *
|
|
((uint32_t)addr6->s6_addr16[7] + gcfg->rand[7]);
|
|
return h >> (32 - gcfg->hash_bits);
|
|
}
|
|
|
|
static void add_to_hash_table(struct cache_entry *c, uint32_t hash4,
|
|
uint32_t hash6)
|
|
{
|
|
list_add(&c->hash4, &gcfg->hash_table4[hash4]);
|
|
list_add(&c->hash6, &gcfg->hash_table6[hash6]);
|
|
}
|
|
|
|
void create_cache(void)
|
|
{
|
|
int i, hash_size = 1 << gcfg->hash_bits;
|
|
struct list_head *entry;
|
|
struct cache_entry *c;
|
|
|
|
if (gcfg->hash_table4) {
|
|
free(gcfg->hash_table4);
|
|
free(gcfg->hash_table6);
|
|
}
|
|
|
|
gcfg->hash_table4 = (struct list_head *)
|
|
malloc(hash_size * sizeof(struct list_head));
|
|
gcfg->hash_table6 = (struct list_head *)
|
|
malloc(hash_size * sizeof(struct list_head));
|
|
if (!gcfg->hash_table4 || !gcfg->hash_table6) {
|
|
slog(LOG_CRIT, "unable to allocate %d bytes for hash table\n",
|
|
hash_size * sizeof(struct list_head));
|
|
exit(1);
|
|
}
|
|
for (i = 0; i < hash_size; ++i) {
|
|
INIT_LIST_HEAD(&gcfg->hash_table4[i]);
|
|
INIT_LIST_HEAD(&gcfg->hash_table6[i]);
|
|
}
|
|
|
|
if (list_empty(&gcfg->cache_pool) && list_empty(&gcfg->cache_active)) {
|
|
c = calloc(gcfg->cache_size, sizeof(struct cache_entry));
|
|
for (i = 0; i < gcfg->cache_size; ++i) {
|
|
INIT_LIST_HEAD(&c->list);
|
|
INIT_LIST_HEAD(&c->hash4);
|
|
INIT_LIST_HEAD(&c->hash6);
|
|
list_add_tail(&c->list, &gcfg->cache_pool);
|
|
++c;
|
|
}
|
|
} else {
|
|
list_for_each(entry, &gcfg->cache_active) {
|
|
c = list_entry(entry, struct cache_entry, list);
|
|
INIT_LIST_HEAD(&c->hash4);
|
|
INIT_LIST_HEAD(&c->hash6);
|
|
add_to_hash_table(c, hash_ip4(&c->addr4),
|
|
hash_ip6(&c->addr6));
|
|
}
|
|
}
|
|
}
|
|
|
|
static struct cache_entry *cache_insert(const struct in_addr *addr4,
|
|
const struct in6_addr *addr6,
|
|
uint32_t hash4, uint32_t hash6)
|
|
{
|
|
struct cache_entry *c;
|
|
|
|
if (list_empty(&gcfg->cache_pool))
|
|
return NULL;
|
|
c = list_entry(gcfg->cache_pool.next, struct cache_entry, list);
|
|
c->addr4 = *addr4;
|
|
c->addr6 = *addr6;
|
|
c->last_use = now;
|
|
c->flags = 0;
|
|
c->ip4_ident = 1;
|
|
list_add(&c->list, &gcfg->cache_active);
|
|
add_to_hash_table(c, hash4, hash6);
|
|
return c;
|
|
}
|
|
|
|
struct map4 *find_map4(const struct in_addr *addr4)
|
|
{
|
|
struct list_head *entry;
|
|
struct map4 *m;
|
|
|
|
list_for_each(entry, &gcfg->map4_list) {
|
|
m = list_entry(entry, struct map4, list);
|
|
if (m->addr.s_addr == (m->mask.s_addr & addr4->s_addr))
|
|
return m;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
struct map6 *find_map6(const struct in6_addr *addr6)
|
|
{
|
|
struct list_head *entry;
|
|
struct map6 *m;
|
|
|
|
list_for_each(entry, &gcfg->map6_list) {
|
|
m = list_entry(entry, struct map6, list);
|
|
if (IN6_IS_IN_NET(addr6, &m->addr, &m->mask))
|
|
return m;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
int insert_map4(struct map4 *m, struct map4 **conflict)
|
|
{
|
|
struct list_head *entry;
|
|
struct map4 *s;
|
|
|
|
list_for_each(entry, &gcfg->map4_list) {
|
|
s = list_entry(entry, struct map4, list);
|
|
if (s->prefix_len < m->prefix_len)
|
|
break;
|
|
if (s->prefix_len == m->prefix_len &&
|
|
s->addr.s_addr == m->addr.s_addr)
|
|
goto conflict;
|
|
}
|
|
list_add_tail(&m->list, entry);
|
|
return 0;
|
|
|
|
conflict:
|
|
if (conflict)
|
|
*conflict = s;
|
|
return -1;
|
|
}
|
|
|
|
int insert_map6(struct map6 *m, struct map6 **conflict)
|
|
{
|
|
struct list_head *entry, *insert_pos = NULL;
|
|
struct map6 *s;
|
|
|
|
list_for_each(entry, &gcfg->map6_list) {
|
|
s = list_entry(entry, struct map6, list);
|
|
if (s->prefix_len < m->prefix_len) {
|
|
if (IN6_IS_IN_NET(&m->addr, &s->addr, &s->mask))
|
|
goto conflict;
|
|
if (!insert_pos)
|
|
insert_pos = entry;
|
|
} else {
|
|
if (IN6_IS_IN_NET(&s->addr, &m->addr, &m->mask))
|
|
goto conflict;
|
|
}
|
|
}
|
|
list_add_tail(&m->list, insert_pos ? insert_pos : &gcfg->map6_list);
|
|
return 0;
|
|
|
|
conflict:
|
|
if (conflict)
|
|
*conflict = s;
|
|
return -1;
|
|
}
|
|
|
|
int append_to_prefix(struct in6_addr *addr6, const struct in_addr *addr4,
|
|
const struct in6_addr *prefix, int prefix_len)
|
|
{
|
|
switch (prefix_len) {
|
|
case 32:
|
|
addr6->s6_addr32[0] = prefix->s6_addr32[0];
|
|
addr6->s6_addr32[1] = addr4->s_addr;
|
|
addr6->s6_addr32[2] = 0;
|
|
addr6->s6_addr32[3] = 0;
|
|
return 0;
|
|
case 40:
|
|
addr6->s6_addr32[0] = prefix->s6_addr32[0];
|
|
#if __BYTE_ORDER == __BIG_ENDIAN
|
|
addr6->s6_addr32[1] = prefix->s6_addr32[1] |
|
|
(addr4->s_addr >> 8);
|
|
addr6->s6_addr32[2] = (addr4->s_addr << 16) & 0x00ff0000;
|
|
#else
|
|
# if __BYTE_ORDER == __LITTLE_ENDIAN
|
|
addr6->s6_addr32[1] = prefix->s6_addr32[1] |
|
|
(addr4->s_addr << 8);
|
|
addr6->s6_addr32[2] = (addr4->s_addr >> 16) & 0x0000ff00;
|
|
# endif
|
|
#endif
|
|
addr6->s6_addr32[3] = 0;
|
|
return 0;
|
|
case 48:
|
|
addr6->s6_addr32[0] = prefix->s6_addr32[0];
|
|
#if __BYTE_ORDER == __BIG_ENDIAN
|
|
addr6->s6_addr32[1] = prefix->s6_addr32[1] |
|
|
(addr4->s_addr >> 16);
|
|
addr6->s6_addr32[2] = (addr4->s_addr << 8) & 0x00ffff00;
|
|
#else
|
|
# if __BYTE_ORDER == __LITTLE_ENDIAN
|
|
addr6->s6_addr32[1] = prefix->s6_addr32[1] |
|
|
(addr4->s_addr << 16);
|
|
addr6->s6_addr32[2] = (addr4->s_addr >> 8) & 0x00ffff00;
|
|
# endif
|
|
#endif
|
|
addr6->s6_addr32[3] = 0;
|
|
return 0;
|
|
case 56:
|
|
addr6->s6_addr32[0] = prefix->s6_addr32[0];
|
|
#if __BYTE_ORDER == __BIG_ENDIAN
|
|
addr6->s6_addr32[1] = prefix->s6_addr32[1] |
|
|
(addr4->s_addr >> 24);
|
|
addr6->s6_addr32[2] = addr4->s_addr & 0x00ffffff;
|
|
#else
|
|
# if __BYTE_ORDER == __LITTLE_ENDIAN
|
|
addr6->s6_addr32[1] = prefix->s6_addr32[1] |
|
|
(addr4->s_addr << 24);
|
|
addr6->s6_addr32[2] = addr4->s_addr & 0xffffff00;
|
|
# endif
|
|
#endif
|
|
addr6->s6_addr32[3] = 0;
|
|
return 0;
|
|
case 64:
|
|
addr6->s6_addr32[0] = prefix->s6_addr32[0];
|
|
addr6->s6_addr32[1] = prefix->s6_addr32[1];
|
|
#if __BYTE_ORDER == __BIG_ENDIAN
|
|
addr6->s6_addr32[2] = addr4->s_addr >> 8;
|
|
addr6->s6_addr32[3] = addr4->s_addr << 24;
|
|
#else
|
|
# if __BYTE_ORDER == __LITTLE_ENDIAN
|
|
addr6->s6_addr32[2] = addr4->s_addr << 8;
|
|
addr6->s6_addr32[3] = addr4->s_addr >> 24;
|
|
# endif
|
|
#endif
|
|
return 0;
|
|
case 96:
|
|
if (prefix->s6_addr32[0] == WKPF &&
|
|
is_private_ip4_addr(addr4))
|
|
return -1;
|
|
addr6->s6_addr32[0] = prefix->s6_addr32[0];
|
|
addr6->s6_addr32[1] = prefix->s6_addr32[1];
|
|
addr6->s6_addr32[2] = prefix->s6_addr32[2];
|
|
addr6->s6_addr32[3] = addr4->s_addr;
|
|
return 0;
|
|
default:
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
int map_ip4_to_ip6(struct in6_addr *addr6, const struct in_addr *addr4,
|
|
struct cache_entry **c_ptr)
|
|
{
|
|
uint32_t hash;
|
|
struct list_head *entry;
|
|
struct cache_entry *c;
|
|
struct map4 *map4;
|
|
struct map_static *s;
|
|
struct map_dynamic *d = NULL;
|
|
|
|
if (gcfg->cache_size) {
|
|
hash = hash_ip4(addr4);
|
|
|
|
list_for_each(entry, &gcfg->hash_table4[hash]) {
|
|
c = list_entry(entry, struct cache_entry, hash4);
|
|
if (addr4->s_addr == c->addr4.s_addr) {
|
|
*addr6 = c->addr6;
|
|
c->last_use = now;
|
|
if (c_ptr)
|
|
*c_ptr = c;
|
|
return 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
map4 = find_map4(addr4);
|
|
|
|
if (!map4)
|
|
return -1;
|
|
|
|
switch (map4->type) {
|
|
case MAP_TYPE_STATIC:
|
|
s = container_of(map4, struct map_static, map4);
|
|
*addr6 = s->map6.addr;
|
|
break;
|
|
case MAP_TYPE_RFC6052:
|
|
s = container_of(map4, struct map_static, map4);
|
|
if (append_to_prefix(addr6, addr4, &s->map6.addr,
|
|
s->map6.prefix_len) < 0)
|
|
return -1;
|
|
break;
|
|
case MAP_TYPE_DYNAMIC_POOL:
|
|
return -1;
|
|
case MAP_TYPE_DYNAMIC_HOST:
|
|
d = container_of(map4, struct map_dynamic, map4);
|
|
*addr6 = d->map6.addr;
|
|
d->last_use = now;
|
|
break;
|
|
default:
|
|
return -1;
|
|
}
|
|
|
|
if (gcfg->cache_size) {
|
|
c = cache_insert(addr4, addr6, hash, hash_ip6(addr6));
|
|
|
|
if (c_ptr)
|
|
*c_ptr = c;
|
|
if (d) {
|
|
d->cache_entry = c;
|
|
if (c)
|
|
c->flags |= CACHE_F_REP_AGEOUT;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int extract_from_prefix(struct in_addr *addr4,
|
|
const struct in6_addr *addr6, int prefix_len)
|
|
{
|
|
switch (prefix_len) {
|
|
case 32:
|
|
if (addr6->s6_addr32[2] || addr6->s6_addr32[3])
|
|
return -1;
|
|
addr4->s_addr = addr6->s6_addr32[1];
|
|
break;
|
|
case 40:
|
|
if (addr6->s6_addr32[2] & htonl(0xff00ffff) ||
|
|
addr6->s6_addr32[3])
|
|
return -1;
|
|
#if __BYTE_ORDER == __BIG_ENDIAN
|
|
addr4->s_addr = (addr6->s6_addr32[1] << 8) | addr6->s6_addr[9];
|
|
#else
|
|
# if __BYTE_ORDER == __LITTLE_ENDIAN
|
|
addr4->s_addr = (addr6->s6_addr32[1] >> 8) |
|
|
(addr6->s6_addr32[2] << 16);
|
|
# endif
|
|
#endif
|
|
break;
|
|
case 48:
|
|
if (addr6->s6_addr32[2] & htonl(0xff0000ff) ||
|
|
addr6->s6_addr32[3])
|
|
return -1;
|
|
#if __BYTE_ORDER == __BIG_ENDIAN
|
|
addr4->s_addr = (addr6->s6_addr16[3] << 16) |
|
|
(addr6->s6_addr32[2] >> 8);
|
|
#else
|
|
# if __BYTE_ORDER == __LITTLE_ENDIAN
|
|
addr4->s_addr = addr6->s6_addr16[3] |
|
|
(addr6->s6_addr32[2] << 8);
|
|
# endif
|
|
#endif
|
|
break;
|
|
case 56:
|
|
if (addr6->s6_addr[8] || addr6->s6_addr32[3])
|
|
return -1;
|
|
#if __BYTE_ORDER == __BIG_ENDIAN
|
|
addr4->s_addr = (addr6->s6_addr[7] << 24) |
|
|
addr6->s6_addr32[2];
|
|
#else
|
|
# if __BYTE_ORDER == __LITTLE_ENDIAN
|
|
addr4->s_addr = addr6->s6_addr[7] |
|
|
addr6->s6_addr32[2];
|
|
# endif
|
|
#endif
|
|
break;
|
|
case 64:
|
|
if (addr6->s6_addr[8] ||
|
|
addr6->s6_addr32[3] & htonl(0x00ffffff))
|
|
return -1;
|
|
#if __BYTE_ORDER == __BIG_ENDIAN
|
|
addr4->s_addr = (addr6->s6_addr32[2] << 8) |
|
|
addr6->s6_addr[12];
|
|
#else
|
|
# if __BYTE_ORDER == __LITTLE_ENDIAN
|
|
addr4->s_addr = (addr6->s6_addr32[2] >> 8) |
|
|
(addr6->s6_addr32[3] << 24);
|
|
# endif
|
|
#endif
|
|
break;
|
|
case 96:
|
|
addr4->s_addr = addr6->s6_addr32[3];
|
|
break;
|
|
default:
|
|
return -1;
|
|
}
|
|
return validate_ip4_addr(addr4);
|
|
}
|
|
|
|
int map_ip6_to_ip4(struct in_addr *addr4, const struct in6_addr *addr6,
|
|
struct cache_entry **c_ptr, int dyn_alloc)
|
|
{
|
|
uint32_t hash;
|
|
struct list_head *entry;
|
|
struct cache_entry *c;
|
|
struct map6 *map6;
|
|
struct map_static *s;
|
|
struct map_dynamic *d = NULL;
|
|
|
|
if (gcfg->cache_size) {
|
|
hash = hash_ip6(addr6);
|
|
|
|
list_for_each(entry, &gcfg->hash_table6[hash]) {
|
|
c = list_entry(entry, struct cache_entry, hash6);
|
|
if (IN6_ARE_ADDR_EQUAL(addr6, &c->addr6)) {
|
|
*addr4 = c->addr4;
|
|
c->last_use = now;
|
|
if (c_ptr)
|
|
*c_ptr = c;
|
|
return 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
map6 = find_map6(addr6);
|
|
|
|
if (!map6) {
|
|
if (dyn_alloc)
|
|
map6 = assign_dynamic(addr6);
|
|
if (!map6)
|
|
return -1;
|
|
}
|
|
|
|
switch (map6->type) {
|
|
case MAP_TYPE_STATIC:
|
|
s = container_of(map6, struct map_static, map6);
|
|
*addr4 = s->map4.addr;
|
|
break;
|
|
case MAP_TYPE_RFC6052:
|
|
if (extract_from_prefix(addr4, addr6, map6->prefix_len) < 0)
|
|
return -1;
|
|
if (map6->addr.s6_addr32[0] == WKPF &&
|
|
is_private_ip4_addr(addr4))
|
|
return -1;
|
|
s = container_of(map6, struct map_static, map6);
|
|
if (find_map4(addr4) != &s->map4)
|
|
return -1;
|
|
break;
|
|
case MAP_TYPE_DYNAMIC_HOST:
|
|
d = container_of(map6, struct map_dynamic, map6);
|
|
*addr4 = d->map4.addr;
|
|
d->last_use = now;
|
|
break;
|
|
default:
|
|
return -1;
|
|
}
|
|
|
|
if (gcfg->cache_size) {
|
|
c = cache_insert(addr4, addr6, hash_ip4(addr4), hash);
|
|
|
|
if (c_ptr)
|
|
*c_ptr = c;
|
|
if (d) {
|
|
d->cache_entry = c;
|
|
if (c)
|
|
c->flags |= CACHE_F_REP_AGEOUT;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void report_ageout(struct cache_entry *c)
|
|
{
|
|
struct map4 *m4;
|
|
struct map_dynamic *d;
|
|
|
|
m4 = find_map4(&c->addr4);
|
|
if (!m4 || m4->type != MAP_TYPE_DYNAMIC_HOST)
|
|
return;
|
|
d = container_of(m4, struct map_dynamic, map4);
|
|
d->last_use = c->last_use;
|
|
d->cache_entry = NULL;
|
|
}
|
|
|
|
void addrmap_maint(void)
|
|
{
|
|
struct list_head *entry, *next;
|
|
struct cache_entry *c;
|
|
|
|
list_for_each_safe(entry, next, &gcfg->cache_active) {
|
|
c = list_entry(entry, struct cache_entry, list);
|
|
if (c->last_use + CACHE_MAX_AGE < now) {
|
|
if (c->flags & CACHE_F_REP_AGEOUT)
|
|
report_ageout(c);
|
|
list_add(&c->list, &gcfg->cache_pool);
|
|
list_del(&c->hash4);
|
|
list_del(&c->hash6);
|
|
}
|
|
}
|
|
}
|