blob: ca31677da78de9f45327b215e9659682dc70145a [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 **/
jjako3cfb81c2004-01-29 09:13:55 +000032unsigned long int static lookup( k, length, level)
jjako88c22162003-07-06 19:33:18 +000033register 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
jjakoc6762cf2004-04-28 14:52:58 +0000241 /* Set IPPOOL_NOGATEWAY if IPPOOL_NODESTADDR is set */
242 if (flags & IPPOOL_NODESTADDR) {
243 flags |= IPPOOL_NOGATEWAY;
244 }
245
jjako504ee452003-08-20 15:25:54 +0000246 /* Set IPPOOL_NONETWORK if IPPOOL_NOGATEWAY is set */
247 if (flags & IPPOOL_NOGATEWAY) {
248 flags |= IPPOOL_NONETWORK;
249 }
jjako88c22162003-07-06 19:33:18 +0000250
251 m = ntohl(mask.s_addr);
252 dynsize = ((~m)+1);
253 if (flags & IPPOOL_NONETWORK) /* Exclude network address from pool */
254 dynsize--;
jjako504ee452003-08-20 15:25:54 +0000255 if (flags & IPPOOL_NOGATEWAY) /* Exclude gateway address from pool */
256 dynsize--;
jjakoc6762cf2004-04-28 14:52:58 +0000257 if (flags & IPPOOL_NODESTADDR) /* Exclude destination address from pool */
258 dynsize--;
jjako88c22162003-07-06 19:33:18 +0000259 if (flags & IPPOOL_NOBROADCAST) /* Exclude broadcast address from pool */
260 dynsize--;
261 }
jjakoa7cd2492003-04-11 09:40:12 +0000262
jjako88c22162003-07-06 19:33:18 +0000263 if (!allowstat) {
264 statsize = 0;
265 stataddr.s_addr = 0;
266 statmask.s_addr = 0;
267 }
268 else {
269 if (ippool_aton(&stataddr, &statmask, stat, 0))
270 return -1; /* Failed to parse static range */
271
272 m = ntohl(statmask.s_addr);
273 statsize = ((~m)+1);
274 if (statsize > IPPOOL_STATSIZE) statsize = IPPOOL_STATSIZE;
275 }
276
277 listsize = dynsize + statsize; /* Allocate space for static IP addresses */
jjakoa7cd2492003-04-11 09:40:12 +0000278
279 if (!(*this = calloc(sizeof(struct ippool_t), 1))) {
280 /* Failed to allocate memory for ippool */
281 return -1;
282 }
283
jjako88c22162003-07-06 19:33:18 +0000284 (*this)->allowdyn = allowdyn;
285 (*this)->allowstat = allowstat;
286 (*this)->stataddr = stataddr;
287 (*this)->statmask = statmask;
288
jjakoa7cd2492003-04-11 09:40:12 +0000289 (*this)->listsize += listsize;
jjako88c22162003-07-06 19:33:18 +0000290 if (!((*this)->member = calloc(sizeof(struct ippoolm_t), listsize))){
jjakoa7cd2492003-04-11 09:40:12 +0000291 /* Failed to allocate memory for members in ippool */
292 return -1;
293 }
294
295 for ((*this)->hashlog = 0;
296 ((1 << (*this)->hashlog) < listsize);
297 (*this)->hashlog++);
298
299 /* printf ("Hashlog %d %d %d\n", (*this)->hashlog, listsize, (1 << (*this)->hashlog)); */
300
301 /* Determine hashsize */
302 (*this)->hashsize = 1 << (*this)->hashlog; /* Fails if mask=0: All Internet*/
303 (*this)->hashmask = (*this)->hashsize -1;
304
305 /* Allocate hash table */
306 if (!((*this)->hash = calloc(sizeof(struct ippoolm_t), (*this)->hashsize))){
307 /* Failed to allocate memory for hash members in ippool */
308 return -1;
309 }
310
jjako88c22162003-07-06 19:33:18 +0000311 (*this)->firstdyn = NULL;
312 (*this)->lastdyn = NULL;
313 for (i = 0; i<dynsize; i++) {
jjakoa7cd2492003-04-11 09:40:12 +0000314
jjakoc6762cf2004-04-28 14:52:58 +0000315 if (flags & IPPOOL_NODESTADDR)
316 (*this)->member[i].addr.s_addr = htonl(ntohl(addr.s_addr) + i + 3);
317 else if (flags & IPPOOL_NOGATEWAY)
jjako504ee452003-08-20 15:25:54 +0000318 (*this)->member[i].addr.s_addr = htonl(ntohl(addr.s_addr) + i + 2);
319 else if (flags & IPPOOL_NONETWORK)
jjakoa7cd2492003-04-11 09:40:12 +0000320 (*this)->member[i].addr.s_addr = htonl(ntohl(addr.s_addr) + i + 1);
321 else
322 (*this)->member[i].addr.s_addr = htonl(ntohl(addr.s_addr) + i);
jjako504ee452003-08-20 15:25:54 +0000323
jjakoa7cd2492003-04-11 09:40:12 +0000324 (*this)->member[i].inuse = 0;
jjakoa7cd2492003-04-11 09:40:12 +0000325
326 /* Insert into list of unused */
jjako88c22162003-07-06 19:33:18 +0000327 (*this)->member[i].prev = (*this)->lastdyn;
328 if ((*this)->lastdyn) {
329 (*this)->lastdyn->next = &((*this)->member[i]);
jjakoa7cd2492003-04-11 09:40:12 +0000330 }
331 else {
jjako88c22162003-07-06 19:33:18 +0000332 (*this)->firstdyn = &((*this)->member[i]);
jjakoa7cd2492003-04-11 09:40:12 +0000333 }
jjako88c22162003-07-06 19:33:18 +0000334 (*this)->lastdyn = &((*this)->member[i]);
jjakoa7cd2492003-04-11 09:40:12 +0000335 (*this)->member[i].next = NULL; /* Redundant */
336
jjako88c22162003-07-06 19:33:18 +0000337 ippool_hashadd(*this, &(*this)->member[i]);
jjakoa7cd2492003-04-11 09:40:12 +0000338 }
jjako88c22162003-07-06 19:33:18 +0000339
340 (*this)->firststat = NULL;
341 (*this)->laststat = NULL;
342 for (i = dynsize; i<listsize; i++) {
343
344 (*this)->member[i].addr.s_addr = 0;
345 (*this)->member[i].inuse = 0;
346
347 /* Insert into list of unused */
348 (*this)->member[i].prev = (*this)->laststat;
349 if ((*this)->laststat) {
350 (*this)->laststat->next = &((*this)->member[i]);
351 }
352 else {
353 (*this)->firststat = &((*this)->member[i]);
354 }
355 (*this)->laststat = &((*this)->member[i]);
356 (*this)->member[i].next = NULL; /* Redundant */
357 }
358
359 if (0) ippool_printaddr(*this);
jjakoa7cd2492003-04-11 09:40:12 +0000360 return 0;
361}
362
363/* Delete existing address pool */
364int ippool_free(struct ippool_t *this) {
365 free(this->hash);
366 free(this->member);
367 free(this);
368 return 0; /* Always OK */
369}
370
371/* Find an IP address in the pool */
372int ippool_getip(struct ippool_t *this, struct ippoolm_t **member,
373 struct in_addr *addr) {
374 struct ippoolm_t *p;
375 uint32_t hash;
376
377 /* Find in hash table */
378 hash = ippool_hash4(addr) & this->hashmask;
379 for (p = this->hash[hash]; p; p = p->nexthash) {
380 if ((p->addr.s_addr == addr->s_addr) && (p->inuse)) {
381 *member = p;
382 return 0;
383 }
384 }
385 *member = NULL;
386 return -1; /* Address could not be found */
387}
388
jjako88c22162003-07-06 19:33:18 +0000389/**
390 * ippool_newip
391 * Get an IP address. If addr = 0.0.0.0 get a dynamic IP address. Otherwise
392 * check to see if the given address is available. If available within
393 * dynamic address space allocate it there, otherwise allocate within static
394 * address space.
395**/
jjakoa7cd2492003-04-11 09:40:12 +0000396int ippool_newip(struct ippool_t *this, struct ippoolm_t **member,
397 struct in_addr *addr) {
398 struct ippoolm_t *p;
399 struct ippoolm_t *p2 = NULL;
400 uint32_t hash;
401
jjako88c22162003-07-06 19:33:18 +0000402 /* If static:
403 * Look in dynaddr.
404 * If found remove from firstdyn/lastdyn linked list.
405 * Else allocate from stataddr.
406 * Remove from firststat/laststat linked list.
407 * Insert into hash table.
408 *
409 * If dynamic
410 * Remove from firstdyn/lastdyn linked list.
411 *
412 */
413
414 if (0) ippool_printaddr(this);
415
416 /* First check to see if this type of address is allowed */
417 if ((addr) && (addr->s_addr)) { /* IP address given */
418 if (!this->allowstat) {
419 return -1; /* Static not allowed */
420 }
421 if ((addr->s_addr & this->statmask.s_addr) != this->stataddr.s_addr) {
422 return -1; /* Static out of range */
423 }
424 }
425 else {
426 if (!this->allowdyn) {
427 return -1; /* Dynamic not allowed */
428 }
429 }
jjakoa7cd2492003-04-11 09:40:12 +0000430
431 if ((addr) && (addr->s_addr)) { /* IP address given */
432 /* Find in hash table */
433 hash = ippool_hash4(addr) & this->hashmask;
434 for (p = this->hash[hash]; p; p = p->nexthash) {
435 if ((p->addr.s_addr == addr->s_addr)) {
436 p2 = p;
437 break;
438 }
439 }
440 }
441 else { /* No ip address given */
jjako88c22162003-07-06 19:33:18 +0000442 if (!this ->firstdyn)
443 return -1; /* No more available */
444 else
445 p2 = this ->firstdyn;
jjakoa7cd2492003-04-11 09:40:12 +0000446 }
447
jjako88c22162003-07-06 19:33:18 +0000448 if (p2) { /* Was allocated from dynamic address pool */
449 if (p2->inuse) return -1; /* Allready in use / Should not happen */
jjakoa7cd2492003-04-11 09:40:12 +0000450
jjako88c22162003-07-06 19:33:18 +0000451 /* Remove from linked list of free dynamic addresses */
452 if (p2->prev)
453 p2->prev->next = p2->next;
454 else
455 this->firstdyn = p2->next;
456 if (p2->next)
457 p2->next->prev = p2->prev;
458 else
459 this->lastdyn = p2->prev;
460 p2->next = NULL;
461 p2->prev = NULL;
462 p2->inuse = 1; /* Dynamic address in use */
463
464 *member = p2;
465 if (0) ippool_printaddr(this);
466 return 0; /* Success */
467 }
468
469 /* It was not possible to allocate from dynamic address pool */
470 /* Try to allocate from static address space */
471
472 if ((addr) && (addr->s_addr)) { /* IP address given */
473 if (this->firststat)
474 return -1; /* No more available */
475 else
476 p2 = this ->firststat;
477
478 /* Remove from linked list of free static addresses */
479 if (p2->prev)
480 p2->prev->next = p2->next;
481 else
482 this->firststat = p2->next;
483 if (p2->next)
484 p2->next->prev = p2->prev;
485 else
486 this->laststat = p2->prev;
487 p2->next = NULL;
488 p2->prev = NULL;
489 p2->inuse = 1; /* Static address in use */
490
491 *member = p2;
492 if (0) ippool_printaddr(this);
493 return 0; /* Success */
494 }
495
496 return -1; /* Should never get here. TODO: Bad code */
jjakoa7cd2492003-04-11 09:40:12 +0000497}
498
499
jjako88c22162003-07-06 19:33:18 +0000500int ippool_freeip(struct ippool_t *this, struct ippoolm_t *member) {
jjakoa7cd2492003-04-11 09:40:12 +0000501
jjako88c22162003-07-06 19:33:18 +0000502 if (0) ippool_printaddr(this);
jjakoa7cd2492003-04-11 09:40:12 +0000503
504 if (!member->inuse) return -1; /* Not in use: Should not happen */
505
jjako88c22162003-07-06 19:33:18 +0000506 switch (member->inuse) {
507 case 0: /* Not in use: Should not happen */
508 return -1;
509 case 1: /* Allocated from dynamic address space */
510 /* Insert into list of unused */
511 member->prev = this->lastdyn;
512 if (this->lastdyn) {
513 this->lastdyn->next = member;
514 }
515 else {
516 this->firstdyn = member;
517 }
518 this->lastdyn = member;
519
520 member->inuse = 0;
521 member->peer = NULL;
522 if (0) ippool_printaddr(this);
523 return 0;
524 case 2: /* Allocated from static address space */
525 if (ippool_hashdel(this, member))
526 return -1;
527 /* Insert into list of unused */
528 member->prev = this->laststat;
529 if (this->laststat) {
530 this->laststat->next = member;
531 }
532 else {
533 this->firststat = member;
534 }
535 this->laststat = member;
536
537 member->inuse = 0;
538 member->addr.s_addr = 0;
539 member->peer = NULL;
540 member->nexthash = NULL;
541 if (0) ippool_printaddr(this);
542 return 0;
543 default: /* Should not happen */
544 return -1;
jjakoa7cd2492003-04-11 09:40:12 +0000545 }
jjakoa7cd2492003-04-11 09:40:12 +0000546}
547
548
549#ifndef IPPOOL_NOIP6
550extern unsigned long int ippool_hash6(struct in6_addr *addr);
551extern int ippool_getip6(struct ippool_t *this, struct in6_addr *addr);
552extern int ippool_returnip6(struct ippool_t *this, struct in6_addr *addr);
553#endif