blob: 321824b574d1096e9c2962df899a86a4049fa17e [file] [log] [blame]
jjakoa7cd2492003-04-11 09:40:12 +00001/*
2 * IP address pool functions.
3 * Copyright (C) 2003 Mondru AB.
4 *
5 * The contents of this file may be used under the terms of the GNU
6 * General Public License Version 2, provided that the above copyright
7 * notice and this permission notice is included in all copies or
8 * substantial portions of the software.
9 *
10 * The initial developer of the original code is
11 * Jens Jakobsen <jj@openggsn.org>
12 *
13 * Contributor(s):
14 *
15 */
16
jjako06e9f122004-01-19 18:37:58 +000017#include <sys/types.h>
jjakoa7cd2492003-04-11 09:40:12 +000018#include <netinet/in.h> /* in_addr */
19#include <stdlib.h> /* calloc */
20#include <stdio.h> /* sscanf */
21
22#include "ippool.h"
23
jjako88c22162003-07-06 19:33:18 +000024/**
25 * lookup()
26 * Generates a 32 bit hash.
27 * Based on public domain code by Bob Jenkins
28 * It should be one of the best hash functions around in terms of both
29 * statistical properties and speed. It is NOT recommended for cryptographic
30 * purposes.
31 **/
32unsigned long int lookup( k, length, level)
33register unsigned char *k; /* the key */
34register unsigned long int length; /* the length of the key */
35register unsigned long int level; /* the previous hash, or an arbitrary value*/
36{
jjakoa7cd2492003-04-11 09:40:12 +000037
jjakoa7cd2492003-04-11 09:40:12 +000038#define mix(a,b,c) \
39{ \
40 a -= b; a -= c; a ^= (c>>13); \
41 b -= c; b -= a; b ^= (a<<8); \
42 c -= a; c -= b; c ^= (b>>13); \
43 a -= b; a -= c; a ^= (c>>12); \
44 b -= c; b -= a; b ^= (a<<16); \
45 c -= a; c -= b; c ^= (b>>5); \
46 a -= b; a -= c; a ^= (c>>3); \
47 b -= c; b -= a; b ^= (a<<10); \
48 c -= a; c -= b; c ^= (b>>15); \
49}
jjakoa7cd2492003-04-11 09:40:12 +000050
jjako88c22162003-07-06 19:33:18 +000051 typedef unsigned long int ub4; /* unsigned 4-byte quantities */
52 typedef unsigned char ub1; /* unsigned 1-byte quantities */
53 register unsigned long int a,b,c,len;
54
55 /* Set up the internal state */
56 len = length;
57 a = b = 0x9e3779b9; /* the golden ratio; an arbitrary value */
58 c = level; /* the previous hash value */
59
60 /*---------------------------------------- handle most of the key */
61 while (len >= 12)
62 {
jjakoa7cd2492003-04-11 09:40:12 +000063 a += (k[0] +((ub4)k[1]<<8) +((ub4)k[2]<<16) +((ub4)k[3]<<24));
64 b += (k[4] +((ub4)k[5]<<8) +((ub4)k[6]<<16) +((ub4)k[7]<<24));
65 c += (k[8] +((ub4)k[9]<<8) +((ub4)k[10]<<16)+((ub4)k[11]<<24));
66 mix(a,b,c);
67 k += 12; len -= 12;
jjako88c22162003-07-06 19:33:18 +000068 }
69
70 /*------------------------------------- handle the last 11 bytes */
71 c += length;
72 switch(len) /* all the case statements fall through */
73 {
74 case 11: c+=((ub4)k[10]<<24);
75 case 10: c+=((ub4)k[9]<<16);
76 case 9 : c+=((ub4)k[8]<<8);
jjakoa7cd2492003-04-11 09:40:12 +000077 /* the first byte of c is reserved for the length */
jjako88c22162003-07-06 19:33:18 +000078 case 8 : b+=((ub4)k[7]<<24);
79 case 7 : b+=((ub4)k[6]<<16);
80 case 6 : b+=((ub4)k[5]<<8);
81 case 5 : b+=k[4];
82 case 4 : a+=((ub4)k[3]<<24);
83 case 3 : a+=((ub4)k[2]<<16);
84 case 2 : a+=((ub4)k[1]<<8);
85 case 1 : a+=k[0];
86 /* case 0: nothing left to add */
87 }
88 mix(a,b,c);
89 /*-------------------------------------------- report the result */
90 return c;
jjakoa7cd2492003-04-11 09:40:12 +000091}
92
jjakoa7cd2492003-04-11 09:40:12 +000093
94int ippool_printaddr(struct ippool_t *this) {
95 int n;
96 printf("ippool_printaddr\n");
jjako88c22162003-07-06 19:33:18 +000097 printf("Firstdyn %d\n", this->firstdyn - this->member);
98 printf("Lastdyn %d\n", this->lastdyn - this->member);
99 printf("Firststat %d\n", this->firststat - this->member);
100 printf("Laststat %d\n", this->laststat - this->member);
jjakoa7cd2492003-04-11 09:40:12 +0000101 printf("Listsize %d\n", this->listsize);
102
103 for (n=0; n<this->listsize; n++) {
104 printf("Unit %d inuse %d prev %d next %d addr %x\n",
105 n,
106 this->member[n].inuse,
107 this->member[n].prev - this->member,
108 this->member[n].next - this->member,
109 this->member[n].addr.s_addr
110 );
111 }
112 return 0;
113}
114
115
jjako88c22162003-07-06 19:33:18 +0000116int ippool_hashadd(struct ippool_t *this, struct ippoolm_t *member) {
117 uint32_t hash;
118 struct ippoolm_t *p;
119 struct ippoolm_t *p_prev = NULL;
120
121 /* Insert into hash table */
122 hash = ippool_hash4(&member->addr) & this->hashmask;
123 for (p = this->hash[hash]; p; p = p->nexthash)
124 p_prev = p;
125 if (!p_prev)
126 this->hash[hash] = member;
127 else
128 p_prev->nexthash = member;
129 return 0; /* Always OK to insert */
130}
131
132int ippool_hashdel(struct ippool_t *this, struct ippoolm_t *member) {
133 uint32_t hash;
134 struct ippoolm_t *p;
135 struct ippoolm_t *p_prev = NULL;
136
137 /* Find in hash table */
138 hash = ippool_hash4(&member->addr) & this->hashmask;
139 for (p = this->hash[hash]; p; p = p->nexthash) {
140 if (p == member) {
141 break;
142 }
143 p_prev = p;
144 }
145
146 if (p!= member) {
147 printf("ippool_hashdel: Tried to delete member not in hash table\n");
148 return -1; /* Member was not in hash table !!! */
149 }
150
151 if (!p_prev)
152 this->hash[hash] = 0;
153 else
154 p_prev->nexthash = 0;
155
156 return 0;
157}
158
159
jjakoa7cd2492003-04-11 09:40:12 +0000160unsigned long int ippool_hash4(struct in_addr *addr) {
161 return lookup(&addr->s_addr, sizeof(addr->s_addr), 0);
162}
163
164#ifndef IPPOOL_NOIP6
165unsigned long int ippool_hash6(struct in6_addr *addr) {
166 return lookup(addr->u6_addr8, sizeof(addr->u6_addr8), 0);
167}
168#endif
169
170
171/* Get IP address and mask */
172int ippool_aton(struct in_addr *addr, struct in_addr *mask,
173 char *pool, int number) {
174
175 /* Parse only first instance of network for now */
176 /* Eventually "number" will indicate the token which we want to parse */
177
178 unsigned int a1, a2, a3, a4;
179 unsigned int m1, m2, m3, m4;
180 int c;
181 unsigned int m;
182 int masklog;
183
184 c = sscanf(pool, "%u.%u.%u.%u/%u.%u.%u.%u",
185 &a1, &a2, &a3, &a4,
186 &m1, &m2, &m3, &m4);
187 switch (c) {
188 case 4:
jjako504ee452003-08-20 15:25:54 +0000189 mask->s_addr = 0xffffffff;
jjakoa7cd2492003-04-11 09:40:12 +0000190 break;
191 case 5:
192 if (m1 < 0 || m1 > 32) {
193 return -1; /* Invalid mask */
194 }
195 mask->s_addr = htonl(0xffffffff << (32 - m1));
196 break;
197 case 8:
198 if (m1 >= 256 || m2 >= 256 || m3 >= 256 || m4 >= 256)
199 return -1; /* Wrong mask format */
200 m = m1 * 0x1000000 + m2 * 0x10000 + m3 * 0x100 + m4;
201 for (masklog = 0; ((1 << masklog) < ((~m)+1)); masklog++);
202 if (((~m)+1) != (1 << masklog))
203 return -1; /* Wrong mask format (not all ones followed by all zeros)*/
204 mask->s_addr = htonl(m);
205 break;
206 default:
207 return -1; /* Invalid mask */
208 }
209
210 if (a1 >= 256 || a2 >= 256 || a3 >= 256 || a4 >= 256)
211 return -1; /* Wrong IP address format */
212 else
213 addr->s_addr = htonl(a1 * 0x1000000 + a2 * 0x10000 + a3 * 0x100 + a4);
214
215 return 0;
216}
217
218/* Create new address pool */
jjako88c22162003-07-06 19:33:18 +0000219int ippool_new(struct ippool_t **this, char *dyn, char *stat,
220 int allowdyn, int allowstat, int flags) {
jjakoa7cd2492003-04-11 09:40:12 +0000221
jjako88c22162003-07-06 19:33:18 +0000222 /* Parse only first instance of pool for now */
jjakoa7cd2492003-04-11 09:40:12 +0000223
224 int i;
jjakoa7cd2492003-04-11 09:40:12 +0000225 struct in_addr addr;
226 struct in_addr mask;
jjako88c22162003-07-06 19:33:18 +0000227 struct in_addr stataddr;
228 struct in_addr statmask;
jjakoa7cd2492003-04-11 09:40:12 +0000229 unsigned int m;
230 unsigned int listsize;
jjako88c22162003-07-06 19:33:18 +0000231 unsigned int dynsize;
232 unsigned int statsize;
jjakoa7cd2492003-04-11 09:40:12 +0000233
jjako88c22162003-07-06 19:33:18 +0000234 if (!allowdyn) {
235 dynsize = 0;
236 }
237 else {
238 if (ippool_aton(&addr, &mask, dyn, 0))
239 return -1; /* Failed to parse dynamic pool */
jjako504ee452003-08-20 15:25:54 +0000240
241 /* Set IPPOOL_NONETWORK if IPPOOL_NOGATEWAY is set */
242 if (flags & IPPOOL_NOGATEWAY) {
243 flags |= IPPOOL_NONETWORK;
244 }
jjako88c22162003-07-06 19:33:18 +0000245
246 m = ntohl(mask.s_addr);
247 dynsize = ((~m)+1);
248 if (flags & IPPOOL_NONETWORK) /* Exclude network address from pool */
249 dynsize--;
jjako504ee452003-08-20 15:25:54 +0000250 if (flags & IPPOOL_NOGATEWAY) /* Exclude gateway address from pool */
251 dynsize--;
jjako88c22162003-07-06 19:33:18 +0000252 if (flags & IPPOOL_NOBROADCAST) /* Exclude broadcast address from pool */
253 dynsize--;
254 }
jjakoa7cd2492003-04-11 09:40:12 +0000255
jjako88c22162003-07-06 19:33:18 +0000256 if (!allowstat) {
257 statsize = 0;
258 stataddr.s_addr = 0;
259 statmask.s_addr = 0;
260 }
261 else {
262 if (ippool_aton(&stataddr, &statmask, stat, 0))
263 return -1; /* Failed to parse static range */
264
265 m = ntohl(statmask.s_addr);
266 statsize = ((~m)+1);
267 if (statsize > IPPOOL_STATSIZE) statsize = IPPOOL_STATSIZE;
268 }
269
270 listsize = dynsize + statsize; /* Allocate space for static IP addresses */
jjakoa7cd2492003-04-11 09:40:12 +0000271
272 if (!(*this = calloc(sizeof(struct ippool_t), 1))) {
273 /* Failed to allocate memory for ippool */
274 return -1;
275 }
276
jjako88c22162003-07-06 19:33:18 +0000277 (*this)->allowdyn = allowdyn;
278 (*this)->allowstat = allowstat;
279 (*this)->stataddr = stataddr;
280 (*this)->statmask = statmask;
281
jjakoa7cd2492003-04-11 09:40:12 +0000282 (*this)->listsize += listsize;
jjako88c22162003-07-06 19:33:18 +0000283 if (!((*this)->member = calloc(sizeof(struct ippoolm_t), listsize))){
jjakoa7cd2492003-04-11 09:40:12 +0000284 /* Failed to allocate memory for members in ippool */
285 return -1;
286 }
287
288 for ((*this)->hashlog = 0;
289 ((1 << (*this)->hashlog) < listsize);
290 (*this)->hashlog++);
291
292 /* printf ("Hashlog %d %d %d\n", (*this)->hashlog, listsize, (1 << (*this)->hashlog)); */
293
294 /* Determine hashsize */
295 (*this)->hashsize = 1 << (*this)->hashlog; /* Fails if mask=0: All Internet*/
296 (*this)->hashmask = (*this)->hashsize -1;
297
298 /* Allocate hash table */
299 if (!((*this)->hash = calloc(sizeof(struct ippoolm_t), (*this)->hashsize))){
300 /* Failed to allocate memory for hash members in ippool */
301 return -1;
302 }
303
jjako88c22162003-07-06 19:33:18 +0000304 (*this)->firstdyn = NULL;
305 (*this)->lastdyn = NULL;
306 for (i = 0; i<dynsize; i++) {
jjakoa7cd2492003-04-11 09:40:12 +0000307
jjako504ee452003-08-20 15:25:54 +0000308 if (flags & IPPOOL_NOGATEWAY)
309 (*this)->member[i].addr.s_addr = htonl(ntohl(addr.s_addr) + i + 2);
310 else if (flags & IPPOOL_NONETWORK)
jjakoa7cd2492003-04-11 09:40:12 +0000311 (*this)->member[i].addr.s_addr = htonl(ntohl(addr.s_addr) + i + 1);
312 else
313 (*this)->member[i].addr.s_addr = htonl(ntohl(addr.s_addr) + i);
jjako504ee452003-08-20 15:25:54 +0000314
jjakoa7cd2492003-04-11 09:40:12 +0000315 (*this)->member[i].inuse = 0;
jjakoa7cd2492003-04-11 09:40:12 +0000316
317 /* Insert into list of unused */
jjako88c22162003-07-06 19:33:18 +0000318 (*this)->member[i].prev = (*this)->lastdyn;
319 if ((*this)->lastdyn) {
320 (*this)->lastdyn->next = &((*this)->member[i]);
jjakoa7cd2492003-04-11 09:40:12 +0000321 }
322 else {
jjako88c22162003-07-06 19:33:18 +0000323 (*this)->firstdyn = &((*this)->member[i]);
jjakoa7cd2492003-04-11 09:40:12 +0000324 }
jjako88c22162003-07-06 19:33:18 +0000325 (*this)->lastdyn = &((*this)->member[i]);
jjakoa7cd2492003-04-11 09:40:12 +0000326 (*this)->member[i].next = NULL; /* Redundant */
327
jjako88c22162003-07-06 19:33:18 +0000328 ippool_hashadd(*this, &(*this)->member[i]);
jjakoa7cd2492003-04-11 09:40:12 +0000329 }
jjako88c22162003-07-06 19:33:18 +0000330
331 (*this)->firststat = NULL;
332 (*this)->laststat = NULL;
333 for (i = dynsize; i<listsize; i++) {
334
335 (*this)->member[i].addr.s_addr = 0;
336 (*this)->member[i].inuse = 0;
337
338 /* Insert into list of unused */
339 (*this)->member[i].prev = (*this)->laststat;
340 if ((*this)->laststat) {
341 (*this)->laststat->next = &((*this)->member[i]);
342 }
343 else {
344 (*this)->firststat = &((*this)->member[i]);
345 }
346 (*this)->laststat = &((*this)->member[i]);
347 (*this)->member[i].next = NULL; /* Redundant */
348 }
349
350 if (0) ippool_printaddr(*this);
jjakoa7cd2492003-04-11 09:40:12 +0000351 return 0;
352}
353
354/* Delete existing address pool */
355int ippool_free(struct ippool_t *this) {
356 free(this->hash);
357 free(this->member);
358 free(this);
359 return 0; /* Always OK */
360}
361
362/* Find an IP address in the pool */
363int ippool_getip(struct ippool_t *this, struct ippoolm_t **member,
364 struct in_addr *addr) {
365 struct ippoolm_t *p;
366 uint32_t hash;
367
368 /* Find in hash table */
369 hash = ippool_hash4(addr) & this->hashmask;
370 for (p = this->hash[hash]; p; p = p->nexthash) {
371 if ((p->addr.s_addr == addr->s_addr) && (p->inuse)) {
372 *member = p;
373 return 0;
374 }
375 }
376 *member = NULL;
377 return -1; /* Address could not be found */
378}
379
jjako88c22162003-07-06 19:33:18 +0000380/**
381 * ippool_newip
382 * Get an IP address. If addr = 0.0.0.0 get a dynamic IP address. Otherwise
383 * check to see if the given address is available. If available within
384 * dynamic address space allocate it there, otherwise allocate within static
385 * address space.
386**/
jjakoa7cd2492003-04-11 09:40:12 +0000387int ippool_newip(struct ippool_t *this, struct ippoolm_t **member,
388 struct in_addr *addr) {
389 struct ippoolm_t *p;
390 struct ippoolm_t *p2 = NULL;
391 uint32_t hash;
392
jjako88c22162003-07-06 19:33:18 +0000393 /* If static:
394 * Look in dynaddr.
395 * If found remove from firstdyn/lastdyn linked list.
396 * Else allocate from stataddr.
397 * Remove from firststat/laststat linked list.
398 * Insert into hash table.
399 *
400 * If dynamic
401 * Remove from firstdyn/lastdyn linked list.
402 *
403 */
404
405 if (0) ippool_printaddr(this);
406
407 /* First check to see if this type of address is allowed */
408 if ((addr) && (addr->s_addr)) { /* IP address given */
409 if (!this->allowstat) {
410 return -1; /* Static not allowed */
411 }
412 if ((addr->s_addr & this->statmask.s_addr) != this->stataddr.s_addr) {
413 return -1; /* Static out of range */
414 }
415 }
416 else {
417 if (!this->allowdyn) {
418 return -1; /* Dynamic not allowed */
419 }
420 }
jjakoa7cd2492003-04-11 09:40:12 +0000421
422 if ((addr) && (addr->s_addr)) { /* IP address given */
423 /* Find in hash table */
424 hash = ippool_hash4(addr) & this->hashmask;
425 for (p = this->hash[hash]; p; p = p->nexthash) {
426 if ((p->addr.s_addr == addr->s_addr)) {
427 p2 = p;
428 break;
429 }
430 }
431 }
432 else { /* No ip address given */
jjako88c22162003-07-06 19:33:18 +0000433 if (!this ->firstdyn)
434 return -1; /* No more available */
435 else
436 p2 = this ->firstdyn;
jjakoa7cd2492003-04-11 09:40:12 +0000437 }
438
jjako88c22162003-07-06 19:33:18 +0000439 if (p2) { /* Was allocated from dynamic address pool */
440 if (p2->inuse) return -1; /* Allready in use / Should not happen */
jjakoa7cd2492003-04-11 09:40:12 +0000441
jjako88c22162003-07-06 19:33:18 +0000442 /* Remove from linked list of free dynamic addresses */
443 if (p2->prev)
444 p2->prev->next = p2->next;
445 else
446 this->firstdyn = p2->next;
447 if (p2->next)
448 p2->next->prev = p2->prev;
449 else
450 this->lastdyn = p2->prev;
451 p2->next = NULL;
452 p2->prev = NULL;
453 p2->inuse = 1; /* Dynamic address in use */
454
455 *member = p2;
456 if (0) ippool_printaddr(this);
457 return 0; /* Success */
458 }
459
460 /* It was not possible to allocate from dynamic address pool */
461 /* Try to allocate from static address space */
462
463 if ((addr) && (addr->s_addr)) { /* IP address given */
464 if (this->firststat)
465 return -1; /* No more available */
466 else
467 p2 = this ->firststat;
468
469 /* Remove from linked list of free static addresses */
470 if (p2->prev)
471 p2->prev->next = p2->next;
472 else
473 this->firststat = p2->next;
474 if (p2->next)
475 p2->next->prev = p2->prev;
476 else
477 this->laststat = p2->prev;
478 p2->next = NULL;
479 p2->prev = NULL;
480 p2->inuse = 1; /* Static address in use */
481
482 *member = p2;
483 if (0) ippool_printaddr(this);
484 return 0; /* Success */
485 }
486
487 return -1; /* Should never get here. TODO: Bad code */
jjakoa7cd2492003-04-11 09:40:12 +0000488}
489
490
jjako88c22162003-07-06 19:33:18 +0000491int ippool_freeip(struct ippool_t *this, struct ippoolm_t *member) {
jjakoa7cd2492003-04-11 09:40:12 +0000492
jjako88c22162003-07-06 19:33:18 +0000493 if (0) ippool_printaddr(this);
jjakoa7cd2492003-04-11 09:40:12 +0000494
495 if (!member->inuse) return -1; /* Not in use: Should not happen */
496
jjako88c22162003-07-06 19:33:18 +0000497 switch (member->inuse) {
498 case 0: /* Not in use: Should not happen */
499 return -1;
500 case 1: /* Allocated from dynamic address space */
501 /* Insert into list of unused */
502 member->prev = this->lastdyn;
503 if (this->lastdyn) {
504 this->lastdyn->next = member;
505 }
506 else {
507 this->firstdyn = member;
508 }
509 this->lastdyn = member;
510
511 member->inuse = 0;
512 member->peer = NULL;
513 if (0) ippool_printaddr(this);
514 return 0;
515 case 2: /* Allocated from static address space */
516 if (ippool_hashdel(this, member))
517 return -1;
518 /* Insert into list of unused */
519 member->prev = this->laststat;
520 if (this->laststat) {
521 this->laststat->next = member;
522 }
523 else {
524 this->firststat = member;
525 }
526 this->laststat = member;
527
528 member->inuse = 0;
529 member->addr.s_addr = 0;
530 member->peer = NULL;
531 member->nexthash = NULL;
532 if (0) ippool_printaddr(this);
533 return 0;
534 default: /* Should not happen */
535 return -1;
jjakoa7cd2492003-04-11 09:40:12 +0000536 }
jjakoa7cd2492003-04-11 09:40:12 +0000537}
538
539
540#ifndef IPPOOL_NOIP6
541extern unsigned long int ippool_hash6(struct in6_addr *addr);
542extern int ippool_getip6(struct ippool_t *this, struct in6_addr *addr);
543extern int ippool_returnip6(struct ippool_t *this, struct in6_addr *addr);
544#endif