/* * IP address pool functions. * Copyright (C) 2003, 2004 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. * */ #include #include /* in_addr */ #include /* calloc */ #include /* sscanf */ #include #include #include #include "syserr.h" #include "ippool.h" #include "lookup.h" int ippool_printaddr(struct ippool_t *this) { unsigned 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; n < this->listsize; n++) { printf("Unit %d inuse %d prev %d next %d addr %s %x\n", n, this->member[n].inuse, this->member[n].prev - this->member, this->member[n].next - this->member, inet_ntoa(this->member[n].addr), 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) { SYS_ERR(DIP, LOGL_ERROR, 0, "ippool_hashdel: Tried to delete member not in hash table"); return -1; } if (!p_prev) this->hash[hash] = p->nexthash; else p_prev->nexthash = p->nexthash; return 0; } unsigned long int ippool_hash4(struct in_addr *addr) { return lookup((unsigned char *)&addr->s_addr, sizeof(addr->s_addr), 0); } #ifndef IPPOOL_NOIP6 unsigned long int ippool_hash6(struct in6_addr *addr) { return lookup((unsigned char *)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; 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 > 32) { SYS_ERR(DIP, LOGL_ERROR, 0, "Invalid mask"); return -1; /* Invalid mask */ } mask->s_addr = htonl(0xffffffff << (32 - m1)); break; case 8: if (m1 >= 256 || m2 >= 256 || m3 >= 256 || m4 >= 256) { SYS_ERR(DIP, LOGL_ERROR, 0, "Invalid mask"); 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)) { SYS_ERR(DIP, LOGL_ERROR, 0, "Invalid mask"); return -1; /* Wrong mask format (not all ones followed by all zeros) */ } mask->s_addr = htonl(m); break; default: SYS_ERR(DIP, LOGL_ERROR, 0, "Invalid mask"); return -1; /* Invalid mask */ } if (a1 >= 256 || a2 >= 256 || a3 >= 256 || a4 >= 256) { SYS_ERR(DIP, LOGL_ERROR, 0, "Wrong IP address format"); return -1; } 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; int listsize; int dynsize; unsigned int statsize; if (!allowdyn) { dynsize = 0; } else { if (ippool_aton(&addr, &mask, dyn, 0)) { SYS_ERR(DIP, LOGL_ERROR, 0, "Failed to parse dynamic pool"); return -1; } /* 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_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)) { SYS_ERR(DIP, LOGL_ERROR, 0, "Failed to parse static range"); return -1; } 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))) { SYS_ERR(DIP, LOGL_ERROR, 0, "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))) { SYS_ERR(DIP, LOGL_ERROR, 0, "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))) { SYS_ERR(DIP, LOGL_ERROR, 0, "Failed to allocate memory for hash members in ippool"); return -1; } (*this)->firstdyn = NULL; (*this)->lastdyn = NULL; for (i = 0; i < dynsize; i++) { 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 */ (void)ippool_hashadd(*this, &(*this)->member[i]); } (*this)->firststat = NULL; (*this)->laststat = NULL; for (i = dynsize; i < listsize; i++) { (*this)->member[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) (void)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)) { if (member) *member = p; return 0; } } if (member) *member = NULL; /*SYS_ERR(DIP, LOGL_ERROR, 0, "Address could not be found"); */ return -1; } /** * 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, int statip) { 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) (void)ippool_printaddr(this); /* First check to see if this type of address is allowed */ if ((addr) && (addr->s_addr) && statip) { /* IP address given */ if (!this->allowstat) { SYS_ERR(DIP, LOGL_ERROR, 0, "Static IP address not allowed"); return -1; } if ((addr->s_addr & this->statmask.s_addr) != this->stataddr.s_addr) { SYS_ERR(DIP, LOGL_ERROR, 0, "Static out of range"); return -1; } } else { if (!this->allowdyn) { SYS_ERR(DIP, LOGL_ERROR, 0, "Dynamic IP address not allowed"); return -1; } } /* If IP address given try to find it in dynamic address pool */ 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; } } } /* If IP was already allocated we can not use it */ if ((!statip) && (p2) && (p2->inuse)) { p2 = NULL; } /* If not found yet and dynamic IP then allocate dynamic IP */ if ((!p2) && (!statip)) { if (!this->firstdyn) { SYS_ERR(DIP, LOGL_ERROR, 0, "No more IP addresses available"); return -1; } else p2 = this->firstdyn; } if (p2) { /* Was allocated from dynamic address pool */ if (p2->inuse) { SYS_ERR(DIP, LOGL_ERROR, 0, "IP address allready in use"); 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) (void)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) && (statip)) { /* IP address given */ if (!this->firststat) { SYS_ERR(DIP, LOGL_ERROR, 0, "No more IP addresses available"); 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 = 2; /* Static address in use */ memcpy(&p2->addr, addr, sizeof(addr)); *member = p2; (void)ippool_hashadd(this, *member); if (0) (void)ippool_printaddr(this); return 0; /* Success */ } SYS_ERR(DIP, LOGL_ERROR, 0, "Could not allocate IP address"); return -1; /* Should never get here. TODO: Bad code */ } int ippool_freeip(struct ippool_t *this, struct ippoolm_t *member) { if (0) (void)ippool_printaddr(this); if (!member->inuse) { SYS_ERR(DIP, LOGL_ERROR, 0, "Address not in use"); return -1; /* Not in use: Should not happen */ } switch (member->inuse) { case 0: /* Not in use: Should not happen */ SYS_ERR(DIP, LOGL_ERROR, 0, "Address not in use"); 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) (void)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) (void)ippool_printaddr(this); return 0; default: /* Should not happen */ SYS_ERR(DIP, LOGL_ERROR, 0, "Could not free IP address"); 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