blob: 99842c5a8ef9cf862969b94f090a54d63c203b2a [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
17#include <netinet/in.h> /* in_addr */
18#include <stdlib.h> /* calloc */
19#include <stdio.h> /* sscanf */
20
21#include "ippool.h"
22
jjako88c22162003-07-06 19:33:18 +000023/**
24 * lookup()
25 * Generates a 32 bit hash.
26 * Based on public domain code by Bob Jenkins
27 * It should be one of the best hash functions around in terms of both
28 * statistical properties and speed. It is NOT recommended for cryptographic
29 * purposes.
30 **/
31unsigned long int lookup( k, length, level)
32register unsigned char *k; /* the key */
33register unsigned long int length; /* the length of the key */
34register unsigned long int level; /* the previous hash, or an arbitrary value*/
35{
jjakoa7cd2492003-04-11 09:40:12 +000036
jjakoa7cd2492003-04-11 09:40:12 +000037#define mix(a,b,c) \
38{ \
39 a -= b; a -= c; a ^= (c>>13); \
40 b -= c; b -= a; b ^= (a<<8); \
41 c -= a; c -= b; c ^= (b>>13); \
42 a -= b; a -= c; a ^= (c>>12); \
43 b -= c; b -= a; b ^= (a<<16); \
44 c -= a; c -= b; c ^= (b>>5); \
45 a -= b; a -= c; a ^= (c>>3); \
46 b -= c; b -= a; b ^= (a<<10); \
47 c -= a; c -= b; c ^= (b>>15); \
48}
jjakoa7cd2492003-04-11 09:40:12 +000049
jjako88c22162003-07-06 19:33:18 +000050 typedef unsigned long int ub4; /* unsigned 4-byte quantities */
51 typedef unsigned char ub1; /* unsigned 1-byte quantities */
52 register unsigned long int a,b,c,len;
53
54 /* Set up the internal state */
55 len = length;
56 a = b = 0x9e3779b9; /* the golden ratio; an arbitrary value */
57 c = level; /* the previous hash value */
58
59 /*---------------------------------------- handle most of the key */
60 while (len >= 12)
61 {
jjakoa7cd2492003-04-11 09:40:12 +000062 a += (k[0] +((ub4)k[1]<<8) +((ub4)k[2]<<16) +((ub4)k[3]<<24));
63 b += (k[4] +((ub4)k[5]<<8) +((ub4)k[6]<<16) +((ub4)k[7]<<24));
64 c += (k[8] +((ub4)k[9]<<8) +((ub4)k[10]<<16)+((ub4)k[11]<<24));
65 mix(a,b,c);
66 k += 12; len -= 12;
jjako88c22162003-07-06 19:33:18 +000067 }
68
69 /*------------------------------------- handle the last 11 bytes */
70 c += length;
71 switch(len) /* all the case statements fall through */
72 {
73 case 11: c+=((ub4)k[10]<<24);
74 case 10: c+=((ub4)k[9]<<16);
75 case 9 : c+=((ub4)k[8]<<8);
jjakoa7cd2492003-04-11 09:40:12 +000076 /* the first byte of c is reserved for the length */
jjako88c22162003-07-06 19:33:18 +000077 case 8 : b+=((ub4)k[7]<<24);
78 case 7 : b+=((ub4)k[6]<<16);
79 case 6 : b+=((ub4)k[5]<<8);
80 case 5 : b+=k[4];
81 case 4 : a+=((ub4)k[3]<<24);
82 case 3 : a+=((ub4)k[2]<<16);
83 case 2 : a+=((ub4)k[1]<<8);
84 case 1 : a+=k[0];
85 /* case 0: nothing left to add */
86 }
87 mix(a,b,c);
88 /*-------------------------------------------- report the result */
89 return c;
jjakoa7cd2492003-04-11 09:40:12 +000090}
91
jjakoa7cd2492003-04-11 09:40:12 +000092
93int ippool_printaddr(struct ippool_t *this) {
94 int n;
95 printf("ippool_printaddr\n");
jjako88c22162003-07-06 19:33:18 +000096 printf("Firstdyn %d\n", this->firstdyn - this->member);
97 printf("Lastdyn %d\n", this->lastdyn - this->member);
98 printf("Firststat %d\n", this->firststat - this->member);
99 printf("Laststat %d\n", this->laststat - this->member);
jjakoa7cd2492003-04-11 09:40:12 +0000100 printf("Listsize %d\n", this->listsize);
101
102 for (n=0; n<this->listsize; n++) {
103 printf("Unit %d inuse %d prev %d next %d addr %x\n",
104 n,
105 this->member[n].inuse,
106 this->member[n].prev - this->member,
107 this->member[n].next - this->member,
108 this->member[n].addr.s_addr
109 );
110 }
111 return 0;
112}
113
114
jjako88c22162003-07-06 19:33:18 +0000115int ippool_hashadd(struct ippool_t *this, struct ippoolm_t *member) {
116 uint32_t hash;
117 struct ippoolm_t *p;
118 struct ippoolm_t *p_prev = NULL;
119
120 /* Insert into hash table */
121 hash = ippool_hash4(&member->addr) & this->hashmask;
122 for (p = this->hash[hash]; p; p = p->nexthash)
123 p_prev = p;
124 if (!p_prev)
125 this->hash[hash] = member;
126 else
127 p_prev->nexthash = member;
128 return 0; /* Always OK to insert */
129}
130
131int ippool_hashdel(struct ippool_t *this, struct ippoolm_t *member) {
132 uint32_t hash;
133 struct ippoolm_t *p;
134 struct ippoolm_t *p_prev = NULL;
135
136 /* Find in hash table */
137 hash = ippool_hash4(&member->addr) & this->hashmask;
138 for (p = this->hash[hash]; p; p = p->nexthash) {
139 if (p == member) {
140 break;
141 }
142 p_prev = p;
143 }
144
145 if (p!= member) {
146 printf("ippool_hashdel: Tried to delete member not in hash table\n");
147 return -1; /* Member was not in hash table !!! */
148 }
149
150 if (!p_prev)
151 this->hash[hash] = 0;
152 else
153 p_prev->nexthash = 0;
154
155 return 0;
156}
157
158
jjakoa7cd2492003-04-11 09:40:12 +0000159unsigned long int ippool_hash4(struct in_addr *addr) {
160 return lookup(&addr->s_addr, sizeof(addr->s_addr), 0);
161}
162
163#ifndef IPPOOL_NOIP6
164unsigned long int ippool_hash6(struct in6_addr *addr) {
165 return lookup(addr->u6_addr8, sizeof(addr->u6_addr8), 0);
166}
167#endif
168
169
170/* Get IP address and mask */
171int ippool_aton(struct in_addr *addr, struct in_addr *mask,
172 char *pool, int number) {
173
174 /* Parse only first instance of network for now */
175 /* Eventually "number" will indicate the token which we want to parse */
176
177 unsigned int a1, a2, a3, a4;
178 unsigned int m1, m2, m3, m4;
179 int c;
180 unsigned int m;
181 int masklog;
182
183 c = sscanf(pool, "%u.%u.%u.%u/%u.%u.%u.%u",
184 &a1, &a2, &a3, &a4,
185 &m1, &m2, &m3, &m4);
186 switch (c) {
187 case 4:
188 if (a1 == 0 && a2 == 0 && a3 == 0 && a4 == 0) /* Full Internet */
189 mask->s_addr = 0x00000000;
190 else if (a2 == 0 && a3 == 0 && a4 == 0) /* class A */
191 mask->s_addr = htonl(0xff000000);
192 else if (a3 == 0 && a4 == 0) /* class B */
193 mask->s_addr = htonl(0xffff0000);
194 else if (a4 == 0) /* class C */
195 mask->s_addr = htonl(0xffffff00);
196 else
197 mask->s_addr = 0xffffffff;
198 break;
199 case 5:
200 if (m1 < 0 || m1 > 32) {
201 return -1; /* Invalid mask */
202 }
203 mask->s_addr = htonl(0xffffffff << (32 - m1));
204 break;
205 case 8:
206 if (m1 >= 256 || m2 >= 256 || m3 >= 256 || m4 >= 256)
207 return -1; /* Wrong mask format */
208 m = m1 * 0x1000000 + m2 * 0x10000 + m3 * 0x100 + m4;
209 for (masklog = 0; ((1 << masklog) < ((~m)+1)); masklog++);
210 if (((~m)+1) != (1 << masklog))
211 return -1; /* Wrong mask format (not all ones followed by all zeros)*/
212 mask->s_addr = htonl(m);
213 break;
214 default:
215 return -1; /* Invalid mask */
216 }
217
218 if (a1 >= 256 || a2 >= 256 || a3 >= 256 || a4 >= 256)
219 return -1; /* Wrong IP address format */
220 else
221 addr->s_addr = htonl(a1 * 0x1000000 + a2 * 0x10000 + a3 * 0x100 + a4);
222
223 return 0;
224}
225
226/* Create new address pool */
jjako88c22162003-07-06 19:33:18 +0000227int ippool_new(struct ippool_t **this, char *dyn, char *stat,
228 int allowdyn, int allowstat, int flags) {
jjakoa7cd2492003-04-11 09:40:12 +0000229
jjako88c22162003-07-06 19:33:18 +0000230 /* Parse only first instance of pool for now */
jjakoa7cd2492003-04-11 09:40:12 +0000231
232 int i;
jjakoa7cd2492003-04-11 09:40:12 +0000233 struct in_addr addr;
234 struct in_addr mask;
jjako88c22162003-07-06 19:33:18 +0000235 struct in_addr stataddr;
236 struct in_addr statmask;
jjakoa7cd2492003-04-11 09:40:12 +0000237 unsigned int m;
238 unsigned int listsize;
jjako88c22162003-07-06 19:33:18 +0000239 unsigned int dynsize;
240 unsigned int statsize;
jjakoa7cd2492003-04-11 09:40:12 +0000241
jjako88c22162003-07-06 19:33:18 +0000242 if (!allowdyn) {
243 dynsize = 0;
244 }
245 else {
246 if (ippool_aton(&addr, &mask, dyn, 0))
247 return -1; /* Failed to parse dynamic pool */
248
249 m = ntohl(mask.s_addr);
250 dynsize = ((~m)+1);
251 if (flags & IPPOOL_NONETWORK) /* Exclude network address from pool */
252 dynsize--;
253 if (flags & IPPOOL_NOBROADCAST) /* Exclude broadcast address from pool */
254 dynsize--;
255 }
jjakoa7cd2492003-04-11 09:40:12 +0000256
jjako88c22162003-07-06 19:33:18 +0000257 if (!allowstat) {
258 statsize = 0;
259 stataddr.s_addr = 0;
260 statmask.s_addr = 0;
261 }
262 else {
263 if (ippool_aton(&stataddr, &statmask, stat, 0))
264 return -1; /* Failed to parse static range */
265
266 m = ntohl(statmask.s_addr);
267 statsize = ((~m)+1);
268 if (statsize > IPPOOL_STATSIZE) statsize = IPPOOL_STATSIZE;
269 }
270
271 listsize = dynsize + statsize; /* Allocate space for static IP addresses */
jjakoa7cd2492003-04-11 09:40:12 +0000272
273 if (!(*this = calloc(sizeof(struct ippool_t), 1))) {
274 /* Failed to allocate memory for ippool */
275 return -1;
276 }
277
jjako88c22162003-07-06 19:33:18 +0000278 (*this)->allowdyn = allowdyn;
279 (*this)->allowstat = allowstat;
280 (*this)->stataddr = stataddr;
281 (*this)->statmask = statmask;
282
jjakoa7cd2492003-04-11 09:40:12 +0000283 (*this)->listsize += listsize;
jjako88c22162003-07-06 19:33:18 +0000284 if (!((*this)->member = calloc(sizeof(struct ippoolm_t), listsize))){
jjakoa7cd2492003-04-11 09:40:12 +0000285 /* Failed to allocate memory for members in ippool */
286 return -1;
287 }
288
289 for ((*this)->hashlog = 0;
290 ((1 << (*this)->hashlog) < listsize);
291 (*this)->hashlog++);
292
293 /* printf ("Hashlog %d %d %d\n", (*this)->hashlog, listsize, (1 << (*this)->hashlog)); */
294
295 /* Determine hashsize */
296 (*this)->hashsize = 1 << (*this)->hashlog; /* Fails if mask=0: All Internet*/
297 (*this)->hashmask = (*this)->hashsize -1;
298
299 /* Allocate hash table */
300 if (!((*this)->hash = calloc(sizeof(struct ippoolm_t), (*this)->hashsize))){
301 /* Failed to allocate memory for hash members in ippool */
302 return -1;
303 }
304
jjako88c22162003-07-06 19:33:18 +0000305 (*this)->firstdyn = NULL;
306 (*this)->lastdyn = NULL;
307 for (i = 0; i<dynsize; i++) {
jjakoa7cd2492003-04-11 09:40:12 +0000308
309 if (flags & IPPOOL_NONETWORK)
310 (*this)->member[i].addr.s_addr = htonl(ntohl(addr.s_addr) + i + 1);
311 else
312 (*this)->member[i].addr.s_addr = htonl(ntohl(addr.s_addr) + i);
313
314 (*this)->member[i].inuse = 0;
jjakoa7cd2492003-04-11 09:40:12 +0000315
316 /* Insert into list of unused */
jjako88c22162003-07-06 19:33:18 +0000317 (*this)->member[i].prev = (*this)->lastdyn;
318 if ((*this)->lastdyn) {
319 (*this)->lastdyn->next = &((*this)->member[i]);
jjakoa7cd2492003-04-11 09:40:12 +0000320 }
321 else {
jjako88c22162003-07-06 19:33:18 +0000322 (*this)->firstdyn = &((*this)->member[i]);
jjakoa7cd2492003-04-11 09:40:12 +0000323 }
jjako88c22162003-07-06 19:33:18 +0000324 (*this)->lastdyn = &((*this)->member[i]);
jjakoa7cd2492003-04-11 09:40:12 +0000325 (*this)->member[i].next = NULL; /* Redundant */
326
jjako88c22162003-07-06 19:33:18 +0000327 ippool_hashadd(*this, &(*this)->member[i]);
jjakoa7cd2492003-04-11 09:40:12 +0000328 }
jjako88c22162003-07-06 19:33:18 +0000329
330 (*this)->firststat = NULL;
331 (*this)->laststat = NULL;
332 for (i = dynsize; i<listsize; i++) {
333
334 (*this)->member[i].addr.s_addr = 0;
335 (*this)->member[i].inuse = 0;
336
337 /* Insert into list of unused */
338 (*this)->member[i].prev = (*this)->laststat;
339 if ((*this)->laststat) {
340 (*this)->laststat->next = &((*this)->member[i]);
341 }
342 else {
343 (*this)->firststat = &((*this)->member[i]);
344 }
345 (*this)->laststat = &((*this)->member[i]);
346 (*this)->member[i].next = NULL; /* Redundant */
347 }
348
349 if (0) ippool_printaddr(*this);
jjakoa7cd2492003-04-11 09:40:12 +0000350 return 0;
351}
352
353/* Delete existing address pool */
354int ippool_free(struct ippool_t *this) {
355 free(this->hash);
356 free(this->member);
357 free(this);
358 return 0; /* Always OK */
359}
360
361/* Find an IP address in the pool */
362int ippool_getip(struct ippool_t *this, struct ippoolm_t **member,
363 struct in_addr *addr) {
364 struct ippoolm_t *p;
365 uint32_t hash;
366
367 /* Find in hash table */
368 hash = ippool_hash4(addr) & this->hashmask;
369 for (p = this->hash[hash]; p; p = p->nexthash) {
370 if ((p->addr.s_addr == addr->s_addr) && (p->inuse)) {
371 *member = p;
372 return 0;
373 }
374 }
375 *member = NULL;
376 return -1; /* Address could not be found */
377}
378
jjako88c22162003-07-06 19:33:18 +0000379/**
380 * ippool_newip
381 * Get an IP address. If addr = 0.0.0.0 get a dynamic IP address. Otherwise
382 * check to see if the given address is available. If available within
383 * dynamic address space allocate it there, otherwise allocate within static
384 * address space.
385**/
jjakoa7cd2492003-04-11 09:40:12 +0000386int ippool_newip(struct ippool_t *this, struct ippoolm_t **member,
387 struct in_addr *addr) {
388 struct ippoolm_t *p;
389 struct ippoolm_t *p2 = NULL;
390 uint32_t hash;
391
jjako88c22162003-07-06 19:33:18 +0000392 /* If static:
393 * Look in dynaddr.
394 * If found remove from firstdyn/lastdyn linked list.
395 * Else allocate from stataddr.
396 * Remove from firststat/laststat linked list.
397 * Insert into hash table.
398 *
399 * If dynamic
400 * Remove from firstdyn/lastdyn linked list.
401 *
402 */
403
404 if (0) ippool_printaddr(this);
405
406 /* First check to see if this type of address is allowed */
407 if ((addr) && (addr->s_addr)) { /* IP address given */
408 if (!this->allowstat) {
409 return -1; /* Static not allowed */
410 }
411 if ((addr->s_addr & this->statmask.s_addr) != this->stataddr.s_addr) {
412 return -1; /* Static out of range */
413 }
414 }
415 else {
416 if (!this->allowdyn) {
417 return -1; /* Dynamic not allowed */
418 }
419 }
jjakoa7cd2492003-04-11 09:40:12 +0000420
421 if ((addr) && (addr->s_addr)) { /* IP address given */
422 /* Find in hash table */
423 hash = ippool_hash4(addr) & this->hashmask;
424 for (p = this->hash[hash]; p; p = p->nexthash) {
425 if ((p->addr.s_addr == addr->s_addr)) {
426 p2 = p;
427 break;
428 }
429 }
430 }
431 else { /* No ip address given */
jjako88c22162003-07-06 19:33:18 +0000432 if (!this ->firstdyn)
433 return -1; /* No more available */
434 else
435 p2 = this ->firstdyn;
jjakoa7cd2492003-04-11 09:40:12 +0000436 }
437
jjako88c22162003-07-06 19:33:18 +0000438 if (p2) { /* Was allocated from dynamic address pool */
439 if (p2->inuse) return -1; /* Allready in use / Should not happen */
jjakoa7cd2492003-04-11 09:40:12 +0000440
jjako88c22162003-07-06 19:33:18 +0000441 /* Remove from linked list of free dynamic addresses */
442 if (p2->prev)
443 p2->prev->next = p2->next;
444 else
445 this->firstdyn = p2->next;
446 if (p2->next)
447 p2->next->prev = p2->prev;
448 else
449 this->lastdyn = p2->prev;
450 p2->next = NULL;
451 p2->prev = NULL;
452 p2->inuse = 1; /* Dynamic address in use */
453
454 *member = p2;
455 if (0) ippool_printaddr(this);
456 return 0; /* Success */
457 }
458
459 /* It was not possible to allocate from dynamic address pool */
460 /* Try to allocate from static address space */
461
462 if ((addr) && (addr->s_addr)) { /* IP address given */
463 if (this->firststat)
464 return -1; /* No more available */
465 else
466 p2 = this ->firststat;
467
468 /* Remove from linked list of free static addresses */
469 if (p2->prev)
470 p2->prev->next = p2->next;
471 else
472 this->firststat = p2->next;
473 if (p2->next)
474 p2->next->prev = p2->prev;
475 else
476 this->laststat = p2->prev;
477 p2->next = NULL;
478 p2->prev = NULL;
479 p2->inuse = 1; /* Static address in use */
480
481 *member = p2;
482 if (0) ippool_printaddr(this);
483 return 0; /* Success */
484 }
485
486 return -1; /* Should never get here. TODO: Bad code */
jjakoa7cd2492003-04-11 09:40:12 +0000487}
488
489
jjako88c22162003-07-06 19:33:18 +0000490int ippool_freeip(struct ippool_t *this, struct ippoolm_t *member) {
jjakoa7cd2492003-04-11 09:40:12 +0000491
jjako88c22162003-07-06 19:33:18 +0000492 if (0) ippool_printaddr(this);
jjakoa7cd2492003-04-11 09:40:12 +0000493
494 if (!member->inuse) return -1; /* Not in use: Should not happen */
495
jjako88c22162003-07-06 19:33:18 +0000496 switch (member->inuse) {
497 case 0: /* Not in use: Should not happen */
498 return -1;
499 case 1: /* Allocated from dynamic address space */
500 /* Insert into list of unused */
501 member->prev = this->lastdyn;
502 if (this->lastdyn) {
503 this->lastdyn->next = member;
504 }
505 else {
506 this->firstdyn = member;
507 }
508 this->lastdyn = member;
509
510 member->inuse = 0;
511 member->peer = NULL;
512 if (0) ippool_printaddr(this);
513 return 0;
514 case 2: /* Allocated from static address space */
515 if (ippool_hashdel(this, member))
516 return -1;
517 /* Insert into list of unused */
518 member->prev = this->laststat;
519 if (this->laststat) {
520 this->laststat->next = member;
521 }
522 else {
523 this->firststat = member;
524 }
525 this->laststat = member;
526
527 member->inuse = 0;
528 member->addr.s_addr = 0;
529 member->peer = NULL;
530 member->nexthash = NULL;
531 if (0) ippool_printaddr(this);
532 return 0;
533 default: /* Should not happen */
534 return -1;
jjakoa7cd2492003-04-11 09:40:12 +0000535 }
jjakoa7cd2492003-04-11 09:40:12 +0000536}
537
538
539#ifndef IPPOOL_NOIP6
540extern unsigned long int ippool_hash6(struct in6_addr *addr);
541extern int ippool_getip6(struct ippool_t *this, struct in6_addr *addr);
542extern int ippool_returnip6(struct ippool_t *this, struct in6_addr *addr);
543#endif