/* * $Id: area.c,v 2.2 2000/05/23 08:04:52 masaki Exp $ */ #include "ricd.h" static u_int area_hash_fn (area_t *area, u_int size) { int val = 0; val += ip_hash_fn (area->id, size); val += area->level; val = val % size; return (val); } static int area_lookup_fn (area_t *a, area_t *b) { return (address_equal (a->id, b->id) && (a->level == b->level)); } area_t * ref_area (area_t *area) { if (area) { pthread_mutex_lock (&area->mutex_lock); assert (area->ref_count > 0); area->ref_count++; pthread_mutex_unlock (&area->mutex_lock); } return (area); } #define AREA_HASH_SIZE 1023 static mrt_hash_table_t area_hash_table; #ifdef HAVE_IPV6 static mrt_hash_table_t area_hash_table6; #endif /* HAVE_IPV6 */ area_t * add_area (int level, prefix_t *id) { area_t a, *area; mrt_hash_table_t *hash = &area_hash_table; char sbuf[128]; prefix_t sprefix; assert (id != NULL); assert (0 <= level && level <= HQLIP_AREA_LEVEL_INTERNET); #ifdef HAVE_IPV6 if (id->family == AF_INET6) hash = &area_hash_table6; #endif /* HAVE_IPV6 */ if (hash == NULL) { hash = New (mrt_hash_table_t); pthread_mutex_init (&hash->mutex_lock, NULL); } pthread_mutex_lock (&hash->mutex_lock); if (hash->table == NULL) { hash->table = HASH_Create (AREA_HASH_SIZE, HASH_EmbeddedKey, True, HASH_KeyOffset, 0, HASH_LookupFunction, area_lookup_fn, HASH_HashFunction, area_hash_fn, 0); } #ifdef HAVE_IPV6 if (id->family == AF_INET6) { struct in6_addr addr6; u_long *ul = (u_long *)&addr6; memcpy (&addr6, prefix_tochar (id), 16); if (level == 0 && (ul[0] || ul[1])) { ul[0] = 0; ul[1] = 0; id = New_Prefix2 (AF_INET6, &addr6, -1, &sprefix); } else if (level > 0 && (ul[2] || ul[3])) { ul[2] = 0; ul[3] = 0; id = New_Prefix2 (AF_INET6, &addr6, -1, &sprefix); } } #endif /* HAVE_IPV6 */ a.level = level; a.id = id; if ((area = HASH_Lookup (hash->table, &a))) { pthread_mutex_unlock (&hash->mutex_lock); area = ref_area (area); return (area); } area = New (area_t); area->level = level; area->id = Ref_Prefix (id); area->ref_count = 1; pthread_mutex_init (&area->mutex_lock, NULL); HASH_Insert (hash->table, area); pthread_mutex_unlock (&hash->mutex_lock); trace (TR_TRACE, MRT->trace, "add area: %d:%a\n", area->level, area->id); return (area); } void deref_area (area_t *area) { if (area == NULL) return; pthread_mutex_lock (&area->mutex_lock); assert (area->ref_count > 0); if (area->ref_count <= 1) { mrt_hash_table_t *hash = &area_hash_table; #ifdef HAVE_IPV6 if (area->id->family == AF_INET6) hash = &area_hash_table6; #endif /* HAVE_IPV6 */ assert (hash); pthread_mutex_lock (&hash->mutex_lock); /* someone may be searching in the table and found this at the same time */ if (area->ref_count <= 1) { HASH_Remove (hash->table, area); pthread_mutex_unlock (&hash->mutex_lock); pthread_mutex_destroy (&area->mutex_lock); Deref_Prefix (area->id); Delete (area); return; } pthread_mutex_unlock (&hash->mutex_lock); } area->ref_count--; pthread_mutex_unlock (&area->mutex_lock); return; } area_t * find_area (int level, prefix_t *id) { area_t a, *area; mrt_hash_table_t *hash = &area_hash_table; if (id == NULL) return (NULL); #ifdef HAVE_IPV6 if (id->family == AF_INET6) hash = &area_hash_table6; #endif /* HAVE_IPV6 */ if (hash == NULL || hash->table == NULL) return (NULL); a.level = level; a.id = id; pthread_mutex_lock (&hash->mutex_lock); area = HASH_Lookup (hash->table, &a); pthread_mutex_unlock (&hash->mutex_lock); return (area); } .