/* * IP address pool functions. * Copyright (C) 2003 Mondru AB. * * The contents of this file may be used under the terms of the GNU * General Public License Version 2, provided that the above copyright * notice and this permission notice is included in all copies or * substantial portions of the software. * * The initial developer of the original code is * Jens Jakobsen * * Contributor(s): * */ #include #include /* in_addr */ #include /* calloc */ #include /* sscanf */ #include "ippool.h" /** * lookup() * Generates a 32 bit hash. * Based on public domain code by Bob Jenkins * It should be one of the best hash functions around in terms of both * statistical properties and speed. It is NOT recommended for cryptographic * purposes. **/ unsigned long int static lookup( k, length, level) register unsigned char *k; /* the key */ register unsigned long int length; /* the length of the key */ register unsigned long int level; /* the previous hash, or an arbitrary value*/ { #define mix(a,b,c) \ { \ a -= b; a -= c; a ^= (c>>13); \ b -= c; b -= a; b ^= (a<<8); \ c -= a; c -= b; c ^= (b>>13); \ a -= b; a -= c; a ^= (c>>12); \ b -= c; b -= a; b ^= (a<<16); \ c -= a; c -= b; c ^= (b>>5); \ a -= b; a -= c; a ^= (c>>3); \ b -= c; b -= a; b ^= (a<<10); \ c -= a; c -= b; c ^= (b>>15); \ } typedef unsigned long int ub4; /* unsigned 4-byte quantities */ typedef unsigned char ub1; /* unsigned 1-byte quantities */ register unsigned long int a,b,c,len; /* Set up the internal state */ len = length; a = b = 0x9e3779b9; /* the golden ratio; an arbitrary value */ c = level; /* the previous hash value */ /*---------------------------------------- handle most of the key */ while (len >= 12) { a += (k[0] +((ub4)k[1]<<8) +((ub4)k[2]<<16) +((ub4)k[3]<<24)); b += (k[4] +((ub4)k[5]<<8) +((ub4)k[6]<<16) +((ub4)k[7]<<24)); c += (k[8] +((ub4)k[9]<<8) +((ub4)k[10]<<16)+((ub4)k[11]<<24)); mix(a,b,c); k += 12; len -= 12; } /*------------------------------------- handle the last 11 bytes */ c += length; switch(len) /* all the case statements fall through */ { case 11: c+=((ub4)k[10]<<24); case 10: c+=((ub4)k[9]<<16); case 9 : c+=((ub4)k[8]<<8); /* the first byte of c is reserved for the length */ case 8 : b+=((ub4)k[7]<<24); case 7 : b+=((ub4)k[6]<<16); case 6 : b+=((ub4)k[5]<<8); case 5 : b+=k[4]; case 4 : a+=((ub4)k[3]<<24); case 3 : a+=((ub4)k[2]<<16); case 2 : a+=((ub4)k[1]<<8); case 1 : a+=k[0]; /* case 0: nothing left to add */ } mix(a,b,c); /*-------------------------------------------- report the result */ return c; } int ippool_printaddr(struct ippool_t *this) { int n; printf("ippool_printaddr\n"); printf("Firstdyn %d\n", this->firstdyn - this->member); printf("Lastdyn %d\n", this->lastdyn - this->member); printf("Firststat %d\n", this->firststat - this->member); printf("Laststat %d\n", this->laststat - this->member); printf("Listsize %d\n", this->listsize); for (n=0; nlistsize; n++) { printf("Unit %d inuse %d prev %d next %d addr %x\n", n, this->member[n].inuse, this->member[n].prev - this->member, this->member[n].next - this->member, this->member[n].addr.s_addr ); } return 0; } int ippool_hashadd(struct ippool_t *this, struct ippoolm_t *member) { uint32_t hash; struct ippoolm_t *p; struct ippoolm_t *p_prev = NULL; /* Insert into hash table */ hash = ippool_hash4(&member->addr) & this->hashmask; for (p = this->hash[hash]; p; p = p->nexthash) p_prev = p; if (!p_prev) this->hash[hash] = member; else p_prev->nexthash = member; return 0; /* Always OK to insert */ } int ippool_hashdel(struct ippool_t *this, struct ippoolm_t *member) { uint32_t hash; struct ippoolm_t *p; struct ippoolm_t *p_prev = NULL; /* Find in hash table */ hash = ippool_hash4(&member->addr) & this->hashmask; for (p = this->hash[hash]; p; p = p->nexthash) { if (p == member) { break; } p_prev = p; } if (p!= member) { printf("ippool_hashdel: Tried to delete member not in hash table\n"); return -1; /* Member was not in hash table !!! */ } if (!p_prev) this->hash[hash] = 0; else p_prev->nexthash = 0; return 0; } unsigned long int ippool_hash4(struct in_addr *addr) { return lookup(&addr->s_addr, sizeof(addr->s_addr), 0); } #ifndef IPPOOL_NOIP6 unsigned long int ippool_hash6(struct in6_addr *addr) { return lookup(addr->u6_addr8, sizeof(addr->u6_addr8), 0); } #endif /* Get IP address and mask */ int ippool_aton(struct in_addr *addr, struct in_addr *mask, char *pool, int number) { /* Parse only first instance of network for now */ /* Eventually "number" will indicate the token which we want to parse */ unsigned int a1, a2, a3, a4; unsigned int m1, m2, m3, m4; int c; unsigned int m; int masklog; c = sscanf(pool, "%u.%u.%u.%u/%u.%u.%u.%u", &a1, &a2, &a3, &a4, &m1, &m2, &m3, &m4); switch (c) { case 4: mask->s_addr = 0xffffffff; break; case 5: if (m1 < 0 || m1 > 32) { return -1; /* Invalid mask */ } mask->s_addr = htonl(0xffffffff << (32 - m1)); break; case 8: if (m1 >= 256 || m2 >= 256 || m3 >= 256 || m4 >= 256) return -1; /* Wrong mask format */ m = m1 * 0x1000000 + m2 * 0x10000 + m3 * 0x100 + m4; for (masklog = 0; ((1 << masklog) < ((~m)+1)); masklog++); if (((~m)+1) != (1 << masklog)) return -1; /* Wrong mask format (not all ones followed by all zeros)*/ mask->s_addr = htonl(m); break; default: return -1; /* Invalid mask */ } if (a1 >= 256 || a2 >= 256 || a3 >= 256 || a4 >= 256) return -1; /* Wrong IP address format */ else addr->s_addr = htonl(a1 * 0x1000000 + a2 * 0x10000 + a3 * 0x100 + a4); return 0; } /* Create new address pool */ int ippool_new(struct ippool_t **this, char *dyn, char *stat, int allowdyn, int allowstat, int flags) { /* Parse only first instance of pool for now */ int i; struct in_addr addr; struct in_addr mask; struct in_addr stataddr; struct in_addr statmask; unsigned int m; unsigned int listsize; unsigned int dynsize; unsigned int statsize; if (!allowdyn) { dynsize = 0; } else { if (ippool_aton(&addr, &mask, dyn, 0)) return -1; /* Failed to parse dynamic pool */ /* Set IPPOOL_NOGATEWAY if IPPOOL_NODESTADDR is set */ if (flags & IPPOOL_NODESTADDR) { flags |= IPPOOL_NOGATEWAY; } /* Set IPPOOL_NONETWORK if IPPOOL_NOGATEWAY is set */ if (flags & IPPOOL_NOGATEWAY) { flags |= IPPOOL_NONETWORK; } m = ntohl(mask.s_addr); dynsize = ((~m)+1); if (flags & IPPOOL_NONETWORK) /* Exclude network address from pool */ dynsize--; if (flags & IPPOOL_NOGATEWAY) /* Exclude gateway address from pool */ dynsize--; if (flags & IPPOOL_NODESTADDR) /* Exclude destination address from pool */ dynsize--; if (flags & IPPOOL_NOBROADCAST) /* Exclude broadcast address from pool */ dynsize--; } if (!allowstat) { statsize = 0; stataddr.s_addr = 0; statmask.s_addr = 0; } else { if (ippool_aton(&stataddr, &statmask, stat, 0)) return -1; /* Failed to parse static range */ m = ntohl(statmask.s_addr); statsize = ((~m)+1); if (statsize > IPPOOL_STATSIZE) statsize = IPPOOL_STATSIZE; } listsize = dynsize + statsize; /* Allocate space for static IP addresses */ if (!(*this = calloc(sizeof(struct ippool_t), 1))) { /* Failed to allocate memory for ippool */ return -1; } (*this)->allowdyn = allowdyn; (*this)->allowstat = allowstat; (*this)->stataddr = stataddr; (*this)->statmask = statmask; (*this)->listsize += listsize; if (!((*this)->member = calloc(sizeof(struct ippoolm_t), listsize))){ /* Failed to allocate memory for members in ippool */ return -1; } for ((*this)->hashlog = 0; ((1 << (*this)->hashlog) < listsize); (*this)->hashlog++); /* printf ("Hashlog %d %d %d\n", (*this)->hashlog, listsize, (1 << (*this)->hashlog)); */ /* Determine hashsize */ (*this)->hashsize = 1 << (*this)->hashlog; /* Fails if mask=0: All Internet*/ (*this)->hashmask = (*this)->hashsize -1; /* Allocate hash table */ if (!((*this)->hash = calloc(sizeof(struct ippoolm_t), (*this)->hashsize))){ /* Failed to allocate memory for hash members in ippool */ return -1; } (*this)->firstdyn = NULL; (*this)->lastdyn = NULL; for (i = 0; imember[i].addr.s_addr = htonl(ntohl(addr.s_addr) + i + 3); else if (flags & IPPOOL_NOGATEWAY) (*this)->member[i].addr.s_addr = htonl(ntohl(addr.s_addr) + i + 2); else if (flags & IPPOOL_NONETWORK) (*this)->member[i].addr.s_addr = htonl(ntohl(addr.s_addr) + i + 1); else (*this)->member[i].addr.s_addr = htonl(ntohl(addr.s_addr) + i); (*this)->member[i].inuse = 0; /* Insert into list of unused */ (*this)->member[i].prev = (*this)->lastdyn; if ((*this)->lastdyn) { (*this)->lastdyn->next = &((*this)->member[i]); } else { (*this)->firstdyn = &((*this)->member[i]); } (*this)->lastdyn = &((*this)->member[i]); (*this)->member[i].next = NULL; /* Redundant */ ippool_hashadd(*this, &(*this)->member[i]); } (*this)->firststat = NULL; (*this)->laststat = NULL; for (i = dynsize; imember[i].addr.s_addr = 0; (*this)->member[i].inuse = 0; /* Insert into list of unused */ (*this)->member[i].prev = (*this)->laststat; if ((*this)->laststat) { (*this)->laststat->next = &((*this)->member[i]); } else { (*this)->firststat = &((*this)->member[i]); } (*this)->laststat = &((*this)->member[i]); (*this)->member[i].next = NULL; /* Redundant */ } if (0) ippool_printaddr(*this); return 0; } /* Delete existing address pool */ int ippool_free(struct ippool_t *this) { free(this->hash); free(this->member); free(this); return 0; /* Always OK */ } /* Find an IP address in the pool */ int ippool_getip(struct ippool_t *this, struct ippoolm_t **member, struct in_addr *addr) { struct ippoolm_t *p; uint32_t hash; /* Find in hash table */ hash = ippool_hash4(addr) & this->hashmask; for (p = this->hash[hash]; p; p = p->nexthash) { if ((p->addr.s_addr == addr->s_addr) && (p->inuse)) { *member = p; return 0; } } *member = NULL; return -1; /* Address could not be found */ } /** * ippool_newip * Get an IP address. If addr = 0.0.0.0 get a dynamic IP address. Otherwise * check to see if the given address is available. If available within * dynamic address space allocate it there, otherwise allocate within static * address space. **/ int ippool_newip(struct ippool_t *this, struct ippoolm_t **member, struct in_addr *addr) { struct ippoolm_t *p; struct ippoolm_t *p2 = NULL; uint32_t hash; /* If static: * Look in dynaddr. * If found remove from firstdyn/lastdyn linked list. * Else allocate from stataddr. * Remove from firststat/laststat linked list. * Insert into hash table. * * If dynamic * Remove from firstdyn/lastdyn linked list. * */ if (0) ippool_printaddr(this); /* First check to see if this type of address is allowed */ if ((addr) && (addr->s_addr)) { /* IP address given */ if (!this->allowstat) { return -1; /* Static not allowed */ } if ((addr->s_addr & this->statmask.s_addr) != this->stataddr.s_addr) { return -1; /* Static out of range */ } } else { if (!this->allowdyn) { return -1; /* Dynamic not allowed */ } } if ((addr) && (addr->s_addr)) { /* IP address given */ /* Find in hash table */ hash = ippool_hash4(addr) & this->hashmask; for (p = this->hash[hash]; p; p = p->nexthash) { if ((p->addr.s_addr == addr->s_addr)) { p2 = p; break; } } } else { /* No ip address given */ if (!this ->firstdyn) return -1; /* No more available */ else p2 = this ->firstdyn; } if (p2) { /* Was allocated from dynamic address pool */ if (p2->inuse) return -1; /* Allready in use / Should not happen */ /* Remove from linked list of free dynamic addresses */ if (p2->prev) p2->prev->next = p2->next; else this->firstdyn = p2->next; if (p2->next) p2->next->prev = p2->prev; else this->lastdyn = p2->prev; p2->next = NULL; p2->prev = NULL; p2->inuse = 1; /* Dynamic address in use */ *member = p2; if (0) ippool_printaddr(this); return 0; /* Success */ } /* It was not possible to allocate from dynamic address pool */ /* Try to allocate from static address space */ if ((addr) && (addr->s_addr)) { /* IP address given */ if (this->firststat) return -1; /* No more available */ else p2 = this ->firststat; /* Remove from linked list of free static addresses */ if (p2->prev) p2->prev->next = p2->next; else this->firststat = p2->next; if (p2->next) p2->next->prev = p2->prev; else this->laststat = p2->prev; p2->next = NULL; p2->prev = NULL; p2->inuse = 1; /* Static address in use */ *member = p2; if (0) ippool_printaddr(this); return 0; /* Success */ } return -1; /* Should never get here. TODO: Bad code */ } int ippool_freeip(struct ippool_t *this, struct ippoolm_t *member) { if (0) ippool_printaddr(this); if (!member->inuse) return -1; /* Not in use: Should not happen */ switch (member->inuse) { case 0: /* Not in use: Should not happen */ return -1; case 1: /* Allocated from dynamic address space */ /* Insert into list of unused */ member->prev = this->lastdyn; if (this->lastdyn) { this->lastdyn->next = member; } else { this->firstdyn = member; } this->lastdyn = member; member->inuse = 0; member->peer = NULL; if (0) ippool_printaddr(this); return 0; case 2: /* Allocated from static address space */ if (ippool_hashdel(this, member)) return -1; /* Insert into list of unused */ member->prev = this->laststat; if (this->laststat) { this->laststat->next = member; } else { this->firststat = member; } this->laststat = member; member->inuse = 0; member->addr.s_addr = 0; member->peer = NULL; member->nexthash = NULL; if (0) ippool_printaddr(this); return 0; default: /* Should not happen */ return -1; } } #ifndef IPPOOL_NOIP6 extern unsigned long int ippool_hash6(struct in6_addr *addr); extern int ippool_getip6(struct ippool_t *this, struct in6_addr *addr); extern int ippool_returnip6(struct ippool_t *this, struct in6_addr *addr); #endif