2 * IP address pool functions.
3 * Copyright (C) 2003, 2004 Mondru AB.
4 * Copyright (C) 2017 by Harald Welte <laforge@gnumonks.org>
6 * The contents of this file may be used under the terms of the GNU
7 * General Public License Version 2, provided that the above copyright
8 * notice and this permission notice is included in all copies or
9 * substantial portions of the software.
13 #include <sys/types.h>
14 #include <netinet/in.h> /* in_addr */
15 #include <stdlib.h> /* calloc */
16 #include <stdio.h> /* sscanf */
18 #include <sys/socket.h>
19 #include <arpa/inet.h>
25 int ippool_printaddr(struct ippool_t
*this)
28 printf("ippool_printaddr\n");
29 printf("Firstdyn %d\n", this->firstdyn
- this->member
);
30 printf("Lastdyn %d\n", this->lastdyn
- this->member
);
31 printf("Firststat %d\n", this->firststat
- this->member
);
32 printf("Laststat %d\n", this->laststat
- this->member
);
33 printf("Listsize %d\n", this->listsize
);
35 for (n
= 0; n
< this->listsize
; n
++) {
37 in46a_ntop(&this->member
[n
].addr
, s
, sizeof(s
));
38 printf("Unit %d inuse %d prev %d next %d addr %s\n",
40 this->member
[n
].inuse
,
41 this->member
[n
].prev
- this->member
,
42 this->member
[n
].next
- this->member
,
48 int ippool_hashadd(struct ippool_t
*this, struct ippoolm_t
*member
)
52 struct ippoolm_t
*p_prev
= NULL
;
54 /* Insert into hash table */
55 hash
= ippool_hash(&member
->addr
) & this->hashmask
;
56 for (p
= this->hash
[hash
]; p
; p
= p
->nexthash
)
59 this->hash
[hash
] = member
;
61 p_prev
->nexthash
= member
;
62 return 0; /* Always OK to insert */
65 int ippool_hashdel(struct ippool_t
*this, struct ippoolm_t
*member
)
69 struct ippoolm_t
*p_prev
= NULL
;
71 /* Find in hash table */
72 hash
= ippool_hash(&member
->addr
) & this->hashmask
;
73 for (p
= this->hash
[hash
]; p
; p
= p
->nexthash
) {
81 SYS_ERR(DIP
, LOGL_ERROR
, 0,
82 "ippool_hashdel: Tried to delete member not in hash table");
87 this->hash
[hash
] = p
->nexthash
;
89 p_prev
->nexthash
= p
->nexthash
;
94 static unsigned long int ippool_hash4(struct in_addr
*addr
)
96 return lookup((unsigned char *)&addr
->s_addr
, sizeof(addr
->s_addr
), 0);
99 static unsigned long int ippool_hash6(struct in6_addr
*addr
, unsigned int len
)
101 /* TODO: Review hash spread for IPv6 */
102 return lookup((unsigned char *)addr
->s6_addr
, len
, 0);
105 unsigned long int ippool_hash(struct in46_addr
*addr
)
108 return ippool_hash4(&addr
->v4
);
110 return ippool_hash6(&addr
->v6
, addr
->len
);
113 /* Get IP address and mask */
114 int ippool_aton(struct in46_addr
*addr
, size_t *prefixlen
, const char *pool_in
, int number
)
117 struct addrinfo hints
= {
118 .ai_family
= AF_UNSPEC
,
119 .ai_socktype
= SOCK_DGRAM
,
123 char pool
[strlen(pool_in
)+1];
125 strcpy(pool
, pool_in
);
129 /* Find '/' and point to first char after it */
130 char *prefixlen_str
= strchr(pool
, '/');
132 *prefixlen_str
= '\0';
134 if (*prefixlen_str
== '\0') {
135 SYS_ERR(DIP
, LOGL_ERROR
, 0, "Empty prefix length specified");
140 /* convert address */
141 if ((err
= getaddrinfo(pool
, NULL
, &hints
, &ai
))) {
142 SYS_ERR(DIP
, LOGL_ERROR
, 0, "Bad address");
146 /* Copy address, set lengths */
147 if (ai
->ai_family
== AF_INET
) {
149 addr
->len
= sizeof(struct in_addr
);
150 addr
->v4
= ((struct sockaddr_in
*)ai
->ai_addr
)->sin_addr
;
153 addr
->len
= sizeof(struct in6_addr
);
154 addr
->v6
= ((struct sockaddr_in6
*)ai
->ai_addr
)->sin6_addr
;
158 /* parse prefixlen */
161 *prefixlen
= strtol(prefixlen_str
, &e
, 10);
163 SYS_ERR(DIP
, LOGL_ERROR
, 0, "Prefixlen is not an int");
168 if (*prefixlen
> (addr
->len
* 8)) {
169 SYS_ERR(DIP
, LOGL_ERROR
, 0, "Perfixlen too big");
176 /* Increase IPv4/IPv6 address by 1 */
177 void in46a_inc(struct in46_addr
*addr
)
180 uint8_t *a
= (uint8_t *)&addr
->v6
;
181 for (addrlen
= addr
->len
; addrlen
> 0; addrlen
--) {
187 /* Create new address pool */
188 int ippool_new(struct ippool_t
**this, const struct in46_prefix
*dyn
, const struct in46_prefix
*stat
,
192 /* Parse only first instance of pool for now */
195 struct in46_addr addr
;
196 size_t addrprefixlen
;
197 struct in46_addr stataddr
;
198 size_t stataddrprefixlen
;
201 unsigned int statsize
;
203 if (!dyn
|| dyn
->addr
.len
== 0) {
207 addrprefixlen
= dyn
->prefixlen
;
208 /* we want to work with /64 prefixes, i.e. allocate /64 prefixes rather
209 * than /128 (single IPv6 addresses) */
210 if (addr
.len
== sizeof(struct in6_addr
))
213 /* Set IPPOOL_NONETWORK if IPPOOL_NOGATEWAY is set */
214 if (flags
& IPPOOL_NOGATEWAY
) {
215 flags
|= IPPOOL_NONETWORK
;
218 dynsize
= (1 << (addr
.len
*8 - addrprefixlen
)) -1;
219 if (flags
& IPPOOL_NONETWORK
) /* Exclude network address from pool */
221 if (flags
& IPPOOL_NOGATEWAY
) /* Exclude gateway address from pool */
223 if (flags
& IPPOOL_NOBROADCAST
) /* Exclude broadcast address from pool */
227 if (!stat
|| stat
->addr
.len
== 0) {
230 stataddrprefixlen
= 0;
232 stataddr
= stat
->addr
;
233 stataddrprefixlen
= stat
->prefixlen
;
235 statsize
= (1 << (addr
.len
- stataddrprefixlen
+ 1)) -1;
236 if (statsize
> IPPOOL_STATSIZE
)
237 statsize
= IPPOOL_STATSIZE
;
240 listsize
= dynsize
+ statsize
; /* Allocate space for static IP addresses */
242 if (!(*this = calloc(sizeof(struct ippool_t
), 1))) {
243 SYS_ERR(DIP
, LOGL_ERROR
, 0,
244 "Failed to allocate memory for ippool");
248 (*this)->allowdyn
= dyn
? 1 : 0;
249 (*this)->allowstat
= stat
? 1 : 0;
250 if (stataddr
.len
> 0)
251 (*this)->stataddr
= stataddr
;
252 (*this)->stataddrprefixlen
= stataddrprefixlen
;
254 (*this)->listsize
+= listsize
;
255 if (!((*this)->member
= calloc(sizeof(struct ippoolm_t
), listsize
))) {
256 SYS_ERR(DIP
, LOGL_ERROR
, 0,
257 "Failed to allocate memory for members in ippool");
261 for ((*this)->hashlog
= 0;
262 ((1 << (*this)->hashlog
) < listsize
); (*this)->hashlog
++) ;
264 /* printf ("Hashlog %d %d %d\n", (*this)->hashlog, listsize, (1 << (*this)->hashlog)); */
266 /* Determine hashsize */
267 (*this)->hashsize
= 1 << (*this)->hashlog
; /* Fails if mask=0: All Internet */
268 (*this)->hashmask
= (*this)->hashsize
- 1;
270 /* Allocate hash table */
273 calloc(sizeof(struct ippoolm_t
), (*this)->hashsize
))) {
274 SYS_ERR(DIP
, LOGL_ERROR
, 0,
275 "Failed to allocate memory for hash members in ippool");
279 (*this)->firstdyn
= NULL
;
280 (*this)->lastdyn
= NULL
;
281 if (flags
& IPPOOL_NOGATEWAY
) {
284 } else if (flags
& IPPOOL_NONETWORK
) {
287 for (i
= 0; i
< dynsize
; i
++) {
288 (*this)->member
[i
].addr
= addr
;
291 (*this)->member
[i
].inuse
= 0;
292 (*this)->member
[i
].pool
= *this;
294 /* Insert into list of unused */
295 (*this)->member
[i
].prev
= (*this)->lastdyn
;
296 if ((*this)->lastdyn
) {
297 (*this)->lastdyn
->next
= &((*this)->member
[i
]);
299 (*this)->firstdyn
= &((*this)->member
[i
]);
301 (*this)->lastdyn
= &((*this)->member
[i
]);
302 (*this)->member
[i
].next
= NULL
; /* Redundant */
304 (void)ippool_hashadd(*this, &(*this)->member
[i
]);
307 (*this)->firststat
= NULL
;
308 (*this)->laststat
= NULL
;
309 for (i
= dynsize
; i
< listsize
; i
++) {
310 struct in46_addr
*i6al
= &(*this)->member
[i
].addr
;
311 memset(i6al
, 0, sizeof(*i6al
));
312 (*this)->member
[i
].inuse
= 0;
313 (*this)->member
[i
].pool
= *this;
315 /* Insert into list of unused */
316 (*this)->member
[i
].prev
= (*this)->laststat
;
317 if ((*this)->laststat
) {
318 (*this)->laststat
->next
= &((*this)->member
[i
]);
320 (*this)->firststat
= &((*this)->member
[i
]);
322 (*this)->laststat
= &((*this)->member
[i
]);
323 (*this)->member
[i
].next
= NULL
; /* Redundant */
327 (void)ippool_printaddr(*this);
331 /* Delete existing address pool */
332 int ippool_free(struct ippool_t
*this)
337 return 0; /* Always OK */
340 /* Find an IP address in the pool */
341 int ippool_getip(struct ippool_t
*this, struct ippoolm_t
**member
,
342 struct in46_addr
*addr
)
347 /* Find in hash table */
348 hash
= ippool_hash(addr
) & this->hashmask
;
349 for (p
= this->hash
[hash
]; p
; p
= p
->nexthash
) {
350 if (in46a_prefix_equal(&p
->addr
, addr
)) {
358 /*SYS_ERR(DIP, LOGL_ERROR, 0, "Address could not be found"); */
364 * Get an IP address. If addr = 0.0.0.0 get a dynamic IP address. Otherwise
365 * check to see if the given address is available. If available within
366 * dynamic address space allocate it there, otherwise allocate within static
369 int ippool_newip(struct ippool_t
*this, struct ippoolm_t
**member
,
370 struct in46_addr
*addr
, int statip
)
373 struct ippoolm_t
*p2
= NULL
;
378 * If found remove from firstdyn/lastdyn linked list.
379 * Else allocate from stataddr.
380 * Remove from firststat/laststat linked list.
381 * Insert into hash table.
384 * Remove from firstdyn/lastdyn linked list.
389 (void)ippool_printaddr(this);
393 if (addr
->len
== 4 && addr
->v4
.s_addr
)
395 if (addr
->len
== 16 && !IN6_IS_ADDR_UNSPECIFIED(&addr
->v6
))
399 /* First check to see if this type of address is allowed */
400 if (specified
&& statip
) { /* IP address given */
401 if (!this->allowstat
) {
402 SYS_ERR(DIP
, LOGL_ERROR
, 0,
403 "Static IP address not allowed");
404 return -GTPCAUSE_NOT_SUPPORTED
;
406 if (!in46a_within_mask(addr
, &this->stataddr
, this->stataddrprefixlen
)) {
407 SYS_ERR(DIP
, LOGL_ERROR
, 0, "Static out of range");
411 if (!this->allowdyn
) {
412 SYS_ERR(DIP
, LOGL_ERROR
, 0,
413 "Dynamic IP address not allowed");
414 return -GTPCAUSE_NOT_SUPPORTED
;
418 /* If IP address given try to find it in dynamic address pool */
419 if (specified
) { /* IP address given */
420 /* Find in hash table */
421 hash
= ippool_hash(addr
) & this->hashmask
;
422 for (p
= this->hash
[hash
]; p
; p
= p
->nexthash
) {
423 if (in46a_prefix_equal(&p
->addr
, addr
)) {
430 /* If IP was already allocated we can not use it */
431 if ((!statip
) && (p2
) && (p2
->inuse
)) {
435 /* If not found yet and dynamic IP then allocate dynamic IP */
436 if ((!p2
) && (!statip
)) {
437 if (!this->firstdyn
) {
438 SYS_ERR(DIP
, LOGL_ERROR
, 0,
439 "No more IP addresses available");
440 return -GTPCAUSE_ADDR_OCCUPIED
;
445 if (p2
) { /* Was allocated from dynamic address pool */
447 SYS_ERR(DIP
, LOGL_ERROR
, 0,
448 "IP address allready in use");
449 return -GTPCAUSE_SYS_FAIL
; /* Allready in use / Should not happen */
452 if (p2
->addr
.len
!= addr
->len
&& !(addr
->len
== 16 && p2
->addr
.len
== 8)) {
453 SYS_ERR(DIP
, LOGL_ERROR
, 0, "MS requested unsupported PDP context type");
454 return -GTPCAUSE_UNKNOWN_PDP
;
457 /* Remove from linked list of free dynamic addresses */
459 p2
->prev
->next
= p2
->next
;
461 this->firstdyn
= p2
->next
;
463 p2
->next
->prev
= p2
->prev
;
465 this->lastdyn
= p2
->prev
;
468 p2
->inuse
= 1; /* Dynamic address in use */
472 (void)ippool_printaddr(this);
473 return 0; /* Success */
476 /* It was not possible to allocate from dynamic address pool */
477 /* Try to allocate from static address space */
479 if (specified
&& (statip
)) { /* IP address given */
480 if (!this->firststat
) {
481 SYS_ERR(DIP
, LOGL_ERROR
, 0,
482 "No more IP addresses available");
483 return -GTPCAUSE_ADDR_OCCUPIED
; /* No more available */
485 p2
= this->firststat
;
487 if (p2
->addr
.len
!= addr
->len
) {
488 SYS_ERR(DIP
, LOGL_ERROR
, 0, "MS requested unsupported PDP context type");
489 return -GTPCAUSE_UNKNOWN_PDP
;
492 /* Remove from linked list of free static addresses */
494 p2
->prev
->next
= p2
->next
;
496 this->firststat
= p2
->next
;
498 p2
->next
->prev
= p2
->prev
;
500 this->laststat
= p2
->prev
;
503 p2
->inuse
= 2; /* Static address in use */
504 memcpy(&p2
->addr
, addr
, sizeof(addr
));
506 (void)ippool_hashadd(this, *member
);
508 (void)ippool_printaddr(this);
509 return 0; /* Success */
512 SYS_ERR(DIP
, LOGL_ERROR
, 0,
513 "Could not allocate IP address");
514 return -GTPCAUSE_SYS_FAIL
; /* Should never get here. TODO: Bad code */
517 int ippool_freeip(struct ippool_t
*this, struct ippoolm_t
*member
)
521 (void)ippool_printaddr(this);
523 if (!member
->inuse
) {
524 SYS_ERR(DIP
, LOGL_ERROR
, 0, "Address not in use");
525 return -1; /* Not in use: Should not happen */
528 switch (member
->inuse
) {
529 case 0: /* Not in use: Should not happen */
530 SYS_ERR(DIP
, LOGL_ERROR
, 0, "Address not in use");
532 case 1: /* Allocated from dynamic address space */
533 /* Insert into list of unused */
534 member
->prev
= this->lastdyn
;
536 this->lastdyn
->next
= member
;
538 this->firstdyn
= member
;
540 this->lastdyn
= member
;
545 (void)ippool_printaddr(this);
547 case 2: /* Allocated from static address space */
548 if (ippool_hashdel(this, member
))
550 /* Insert into list of unused */
551 member
->prev
= this->laststat
;
552 if (this->laststat
) {
553 this->laststat
->next
= member
;
555 this->firststat
= member
;
557 this->laststat
= member
;
560 memset(&member
->addr
, 0, sizeof(member
->addr
));
562 member
->nexthash
= NULL
;
564 (void)ippool_printaddr(this);
566 default: /* Should not happen */
567 SYS_ERR(DIP
, LOGL_ERROR
, 0,
568 "Could not free IP address");