Logo Search packages:      
Sourcecode: tayga version File versions  Download package

addrmap.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);
            }
      }
}

Generated by  Doxygen 1.6.0   Back to index