blob: 6f3d06e36db2906fe18cbc0f3013063599bf938b [file] [log] [blame]
jjakoada9ffa2004-12-30 16:39:11 +00001/*
jjakoa7cd2492003-04-11 09:40:12 +00002 * IP address pool functions.
jjakoc3213962004-09-09 20:23:50 +00003 * Copyright (C) 2003, 2004 Mondru AB.
Harald Welted12eab92017-08-02 19:49:47 +02004 * Copyright (C) 2017 by Harald Welte <laforge@gnumonks.org>
jjakoada9ffa2004-12-30 16:39:11 +00005 *
jjakoa7cd2492003-04-11 09:40:12 +00006 * 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.
Pau Espin Pedrolfdd732b2017-10-13 14:32:24 +020010 *
jjakoa7cd2492003-04-11 09:40:12 +000011 */
12
jjako06e9f122004-01-19 18:37:58 +000013#include <sys/types.h>
Harald Weltebed35df2011-11-02 13:06:18 +010014#include <netinet/in.h> /* in_addr */
15#include <stdlib.h> /* calloc */
16#include <stdio.h> /* sscanf */
jjakoc3213962004-09-09 20:23:50 +000017#include <string.h>
18#include <sys/socket.h>
19#include <arpa/inet.h>
Harald Welted12eab92017-08-02 19:49:47 +020020#include <netdb.h>
jjakoc3213962004-09-09 20:23:50 +000021#include "syserr.h"
jjakoa7cd2492003-04-11 09:40:12 +000022#include "ippool.h"
jjakoada9ffa2004-12-30 16:39:11 +000023#include "lookup.h"
jjakoa7cd2492003-04-11 09:40:12 +000024
Harald Weltebed35df2011-11-02 13:06:18 +010025int ippool_printaddr(struct ippool_t *this)
26{
27 unsigned int n;
28 printf("ippool_printaddr\n");
Harald Weltefb75adf2018-10-21 13:27:58 +020029 printf("Firstdyn %td\n", this->firstdyn - this->member);
30 printf("Lastdyn %td\n", this->lastdyn - this->member);
31 printf("Firststat %td\n", this->firststat - this->member);
32 printf("Laststat %td\n", this->laststat - this->member);
33 printf("Listsize %td\n", this->listsize);
jjakoa7cd2492003-04-11 09:40:12 +000034
Harald Weltebed35df2011-11-02 13:06:18 +010035 for (n = 0; n < this->listsize; n++) {
Harald Welted12eab92017-08-02 19:49:47 +020036 char s[256];
37 in46a_ntop(&this->member[n].addr, s, sizeof(s));
Harald Weltefb75adf2018-10-21 13:27:58 +020038 printf("Unit %d inuse %d prev %td next %td addr %s\n",
Harald Weltebed35df2011-11-02 13:06:18 +010039 n,
40 this->member[n].inuse,
41 this->member[n].prev - this->member,
42 this->member[n].next - this->member,
Harald Welted12eab92017-08-02 19:49:47 +020043 s);
Harald Weltebed35df2011-11-02 13:06:18 +010044 }
45 return 0;
jjakoa7cd2492003-04-11 09:40:12 +000046}
47
Harald Weltebed35df2011-11-02 13:06:18 +010048int ippool_hashadd(struct ippool_t *this, struct ippoolm_t *member)
49{
50 uint32_t hash;
51 struct ippoolm_t *p;
52 struct ippoolm_t *p_prev = NULL;
jjako88c22162003-07-06 19:33:18 +000053
Harald Weltebed35df2011-11-02 13:06:18 +010054 /* Insert into hash table */
Harald Welted12eab92017-08-02 19:49:47 +020055 hash = ippool_hash(&member->addr) & this->hashmask;
Harald Weltebed35df2011-11-02 13:06:18 +010056 for (p = this->hash[hash]; p; p = p->nexthash)
57 p_prev = p;
58 if (!p_prev)
59 this->hash[hash] = member;
60 else
61 p_prev->nexthash = member;
62 return 0; /* Always OK to insert */
jjako88c22162003-07-06 19:33:18 +000063}
64
Harald Weltebed35df2011-11-02 13:06:18 +010065int ippool_hashdel(struct ippool_t *this, struct ippoolm_t *member)
66{
67 uint32_t hash;
68 struct ippoolm_t *p;
69 struct ippoolm_t *p_prev = NULL;
jjako88c22162003-07-06 19:33:18 +000070
Harald Weltebed35df2011-11-02 13:06:18 +010071 /* Find in hash table */
Harald Welted12eab92017-08-02 19:49:47 +020072 hash = ippool_hash(&member->addr) & this->hashmask;
Harald Weltebed35df2011-11-02 13:06:18 +010073 for (p = this->hash[hash]; p; p = p->nexthash) {
74 if (p == member) {
75 break;
76 }
77 p_prev = p;
78 }
jjako88c22162003-07-06 19:33:18 +000079
Harald Weltebed35df2011-11-02 13:06:18 +010080 if (p != member) {
Holger Hans Peter Freyther9c7fd8e2014-12-04 16:32:37 +010081 SYS_ERR(DIP, LOGL_ERROR, 0,
Harald Weltebed35df2011-11-02 13:06:18 +010082 "ippool_hashdel: Tried to delete member not in hash table");
83 return -1;
84 }
jjako88c22162003-07-06 19:33:18 +000085
Harald Weltebed35df2011-11-02 13:06:18 +010086 if (!p_prev)
87 this->hash[hash] = p->nexthash;
88 else
89 p_prev->nexthash = p->nexthash;
jjako88c22162003-07-06 19:33:18 +000090
Harald Weltebed35df2011-11-02 13:06:18 +010091 return 0;
jjako88c22162003-07-06 19:33:18 +000092}
93
Harald Welted12eab92017-08-02 19:49:47 +020094static unsigned long int ippool_hash4(struct in_addr *addr)
Harald Weltebed35df2011-11-02 13:06:18 +010095{
96 return lookup((unsigned char *)&addr->s_addr, sizeof(addr->s_addr), 0);
jjakoa7cd2492003-04-11 09:40:12 +000097}
98
Harald Welted4d6e092017-08-08 18:10:43 +020099static unsigned long int ippool_hash6(struct in6_addr *addr, unsigned int len)
Harald Weltebed35df2011-11-02 13:06:18 +0100100{
Harald Welted12eab92017-08-02 19:49:47 +0200101 /* TODO: Review hash spread for IPv6 */
Harald Welted4d6e092017-08-08 18:10:43 +0200102 return lookup((unsigned char *)addr->s6_addr, len, 0);
jjakoa7cd2492003-04-11 09:40:12 +0000103}
Harald Welted12eab92017-08-02 19:49:47 +0200104
105unsigned long int ippool_hash(struct in46_addr *addr)
106{
107 if (addr->len == 4)
108 return ippool_hash4(&addr->v4);
109 else
Harald Welted4d6e092017-08-08 18:10:43 +0200110 return ippool_hash6(&addr->v6, addr->len);
Harald Welted12eab92017-08-02 19:49:47 +0200111}
jjakoa7cd2492003-04-11 09:40:12 +0000112
jjakoa7cd2492003-04-11 09:40:12 +0000113/* Get IP address and mask */
Harald Welted12eab92017-08-02 19:49:47 +0200114int ippool_aton(struct in46_addr *addr, size_t *prefixlen, const char *pool_in, int number)
Harald Weltebed35df2011-11-02 13:06:18 +0100115{
Harald Welted12eab92017-08-02 19:49:47 +0200116 struct addrinfo *ai;
117 struct addrinfo hints = {
118 .ai_family = AF_UNSPEC,
119 .ai_socktype = SOCK_DGRAM,
120 .ai_flags = 0,
121 .ai_protocol = 0
122 };
123 char pool[strlen(pool_in)+1];
jjakoa7cd2492003-04-11 09:40:12 +0000124
Harald Welted12eab92017-08-02 19:49:47 +0200125 strcpy(pool, pool_in);
jjakoa7cd2492003-04-11 09:40:12 +0000126
Harald Welted12eab92017-08-02 19:49:47 +0200127 int err;
jjakoa7cd2492003-04-11 09:40:12 +0000128
Harald Welted12eab92017-08-02 19:49:47 +0200129 /* Find '/' and point to first char after it */
130 char *prefixlen_str = strchr(pool, '/');
131 if (prefixlen_str) {
132 *prefixlen_str = '\0';
133 prefixlen_str++;
134 if (*prefixlen_str == '\0') {
135 SYS_ERR(DIP, LOGL_ERROR, 0, "Empty prefix length specified");
136 return -1;
Harald Weltebed35df2011-11-02 13:06:18 +0100137 }
Harald Weltebed35df2011-11-02 13:06:18 +0100138 }
jjakoa7cd2492003-04-11 09:40:12 +0000139
Harald Welted12eab92017-08-02 19:49:47 +0200140 /* convert address */
141 if ((err = getaddrinfo(pool, NULL, &hints, &ai))) {
142 SYS_ERR(DIP, LOGL_ERROR, 0, "Bad address");
Harald Weltebed35df2011-11-02 13:06:18 +0100143 return -1;
Harald Welted12eab92017-08-02 19:49:47 +0200144 }
145
146 /* Copy address, set lengths */
147 if (ai->ai_family == AF_INET) {
148 *prefixlen = 32;
149 addr->len = sizeof(struct in_addr);
150 addr->v4 = ((struct sockaddr_in*)ai->ai_addr)->sin_addr;
151 } else {
152 *prefixlen = 128;
153 addr->len = sizeof(struct in6_addr);
154 addr->v6 = ((struct sockaddr_in6*)ai->ai_addr)->sin6_addr;
155 }
156 freeaddrinfo(ai);
157
158 /* parse prefixlen */
159 if (prefixlen_str) {
160 char *e;
161 *prefixlen = strtol(prefixlen_str, &e, 10);
162 if (*e != '\0') {
163 SYS_ERR(DIP, LOGL_ERROR, 0, "Prefixlen is not an int");
164 return -1;
165 }
166 }
167
168 if (*prefixlen > (addr->len * 8)) {
169 SYS_ERR(DIP, LOGL_ERROR, 0, "Perfixlen too big");
170 return -1;
171 }
jjakoa7cd2492003-04-11 09:40:12 +0000172
Harald Weltebed35df2011-11-02 13:06:18 +0100173 return 0;
jjakoa7cd2492003-04-11 09:40:12 +0000174}
175
Harald Welted12eab92017-08-02 19:49:47 +0200176/* Increase IPv4/IPv6 address by 1 */
177void in46a_inc(struct in46_addr *addr)
178{
179 size_t addrlen;
180 uint8_t *a = (uint8_t *)&addr->v6;
181 for (addrlen = addr->len; addrlen > 0; addrlen--) {
182 if (++a[addrlen-1])
183 break;
184 }
185}
186
Pau Espin Pedrol859f9b02017-10-16 14:52:25 +0200187static bool addr_in_prefix_list(struct in46_addr *addr, struct in46_prefix *list, size_t list_size)
188{
189 int i;
190 for (i = 0; i < list_size; i++) {
191 if (in46a_prefix_equal(addr, &list[i].addr))
192 return true;
193 }
194 return false;
195}
196
jjakoa7cd2492003-04-11 09:40:12 +0000197/* Create new address pool */
Harald Welte4857f3c2017-08-12 12:55:04 +0200198int ippool_new(struct ippool_t **this, const struct in46_prefix *dyn, const struct in46_prefix *stat,
Pau Espin Pedrol859f9b02017-10-16 14:52:25 +0200199 int flags, struct in46_prefix *blacklist, size_t blacklist_size)
Harald Weltebed35df2011-11-02 13:06:18 +0100200{
jjakoa7cd2492003-04-11 09:40:12 +0000201
Harald Weltebed35df2011-11-02 13:06:18 +0100202 /* Parse only first instance of pool for now */
jjakoa7cd2492003-04-11 09:40:12 +0000203
Harald Weltebed35df2011-11-02 13:06:18 +0100204 int i;
Stefan Sperling73273602018-11-22 08:12:28 +0100205 struct in46_addr addr = { 0 };
Harald Welted12eab92017-08-02 19:49:47 +0200206 size_t addrprefixlen;
207 struct in46_addr stataddr;
208 size_t stataddrprefixlen;
Harald Weltebed35df2011-11-02 13:06:18 +0100209 int listsize;
210 int dynsize;
211 unsigned int statsize;
jjakoa7cd2492003-04-11 09:40:12 +0000212
Harald Welte4857f3c2017-08-12 12:55:04 +0200213 if (!dyn || dyn->addr.len == 0) {
Harald Weltebed35df2011-11-02 13:06:18 +0100214 dynsize = 0;
215 } else {
Harald Welte4857f3c2017-08-12 12:55:04 +0200216 addr = dyn->addr;
217 addrprefixlen = dyn->prefixlen;
Harald Welted4d6e092017-08-08 18:10:43 +0200218 /* we want to work with /64 prefixes, i.e. allocate /64 prefixes rather
219 * than /128 (single IPv6 addresses) */
220 if (addr.len == sizeof(struct in6_addr))
221 addr.len = 64/8;
jjakoc6762cf2004-04-28 14:52:58 +0000222
Pau Espin Pedrol361cb9e2017-10-13 11:56:16 +0200223 dynsize = (1 << (addr.len*8 - addrprefixlen));
Harald Weltebed35df2011-11-02 13:06:18 +0100224 if (flags & IPPOOL_NONETWORK) /* Exclude network address from pool */
225 dynsize--;
Harald Weltebed35df2011-11-02 13:06:18 +0100226 if (flags & IPPOOL_NOBROADCAST) /* Exclude broadcast address from pool */
227 dynsize--;
Pau Espin Pedrol859f9b02017-10-16 14:52:25 +0200228 /* Exclude included blacklist addresses from pool */
229 for (i = 0; i < blacklist_size; i++) {
230 if (in46a_within_mask(&blacklist[i].addr, &addr, addrprefixlen))
231 dynsize--;
232 }
Harald Weltebed35df2011-11-02 13:06:18 +0100233 }
jjakoc3213962004-09-09 20:23:50 +0000234
Harald Welte4857f3c2017-08-12 12:55:04 +0200235 if (!stat || stat->addr.len == 0) {
Harald Weltebed35df2011-11-02 13:06:18 +0100236 statsize = 0;
Harald Welted12eab92017-08-02 19:49:47 +0200237 stataddr.len = 0;
238 stataddrprefixlen = 0;
Harald Weltebed35df2011-11-02 13:06:18 +0100239 } else {
Harald Welte4857f3c2017-08-12 12:55:04 +0200240 stataddr = stat->addr;
241 stataddrprefixlen = stat->prefixlen;
jjako88c22162003-07-06 19:33:18 +0000242
Harald Weltefc6676c2017-11-06 03:38:54 +0900243 statsize = (1 << (stataddr.len*8 - stataddrprefixlen));
Harald Weltebed35df2011-11-02 13:06:18 +0100244 if (statsize > IPPOOL_STATSIZE)
245 statsize = IPPOOL_STATSIZE;
246 }
jjakoa7cd2492003-04-11 09:40:12 +0000247
Harald Weltebed35df2011-11-02 13:06:18 +0100248 listsize = dynsize + statsize; /* Allocate space for static IP addresses */
jjako88c22162003-07-06 19:33:18 +0000249
Harald Weltebed35df2011-11-02 13:06:18 +0100250 if (!(*this = calloc(sizeof(struct ippool_t), 1))) {
Holger Hans Peter Freyther9c7fd8e2014-12-04 16:32:37 +0100251 SYS_ERR(DIP, LOGL_ERROR, 0,
Harald Weltebed35df2011-11-02 13:06:18 +0100252 "Failed to allocate memory for ippool");
253 return -1;
254 }
jjakoa7cd2492003-04-11 09:40:12 +0000255
Harald Welte4857f3c2017-08-12 12:55:04 +0200256 (*this)->allowdyn = dyn ? 1 : 0;
257 (*this)->allowstat = stat ? 1 : 0;
Harald Welted12eab92017-08-02 19:49:47 +0200258 if (stataddr.len > 0)
259 (*this)->stataddr = stataddr;
260 (*this)->stataddrprefixlen = stataddrprefixlen;
jjakoa7cd2492003-04-11 09:40:12 +0000261
Harald Weltebed35df2011-11-02 13:06:18 +0100262 (*this)->listsize += listsize;
263 if (!((*this)->member = calloc(sizeof(struct ippoolm_t), listsize))) {
Holger Hans Peter Freyther9c7fd8e2014-12-04 16:32:37 +0100264 SYS_ERR(DIP, LOGL_ERROR, 0,
Harald Weltebed35df2011-11-02 13:06:18 +0100265 "Failed to allocate memory for members in ippool");
266 return -1;
267 }
jjakoa7cd2492003-04-11 09:40:12 +0000268
Harald Weltebed35df2011-11-02 13:06:18 +0100269 for ((*this)->hashlog = 0;
270 ((1 << (*this)->hashlog) < listsize); (*this)->hashlog++) ;
jjakoa7cd2492003-04-11 09:40:12 +0000271
Harald Weltebed35df2011-11-02 13:06:18 +0100272 /* printf ("Hashlog %d %d %d\n", (*this)->hashlog, listsize, (1 << (*this)->hashlog)); */
jjakoa7cd2492003-04-11 09:40:12 +0000273
Harald Weltebed35df2011-11-02 13:06:18 +0100274 /* Determine hashsize */
275 (*this)->hashsize = 1 << (*this)->hashlog; /* Fails if mask=0: All Internet */
276 (*this)->hashmask = (*this)->hashsize - 1;
jjako88c22162003-07-06 19:33:18 +0000277
Harald Weltebed35df2011-11-02 13:06:18 +0100278 /* Allocate hash table */
Stefan Sperling411ff3b2018-11-21 14:26:18 +0100279 (*this)->hash = calloc((*this)->hashsize, sizeof(struct ippoolm_t *));
280 if (!(*this)->hash) {
Holger Hans Peter Freyther9c7fd8e2014-12-04 16:32:37 +0100281 SYS_ERR(DIP, LOGL_ERROR, 0,
Harald Weltebed35df2011-11-02 13:06:18 +0100282 "Failed to allocate memory for hash members in ippool");
283 return -1;
284 }
jjako88c22162003-07-06 19:33:18 +0000285
Harald Weltebed35df2011-11-02 13:06:18 +0100286 (*this)->firstdyn = NULL;
287 (*this)->lastdyn = NULL;
Pau Espin Pedrol859f9b02017-10-16 14:52:25 +0200288 if (flags & IPPOOL_NONETWORK) {
Harald Welted12eab92017-08-02 19:49:47 +0200289 in46a_inc(&addr);
290 }
Harald Weltebed35df2011-11-02 13:06:18 +0100291 for (i = 0; i < dynsize; i++) {
Pau Espin Pedrol859f9b02017-10-16 14:52:25 +0200292 if (addr_in_prefix_list(&addr, blacklist, blacklist_size)) {
293 SYS_ERR(DIP, LOGL_DEBUG, 0,
294 "addr blacklisted from pool: %s", in46a_ntoa(&addr));
295 in46a_inc(&addr);
296 i--;
297 continue;
298 }
Harald Welted12eab92017-08-02 19:49:47 +0200299 (*this)->member[i].addr = addr;
300 in46a_inc(&addr);
jjako88c22162003-07-06 19:33:18 +0000301
Harald Weltebed35df2011-11-02 13:06:18 +0100302 (*this)->member[i].inuse = 0;
Harald Welteb513b952017-08-12 12:46:39 +0200303 (*this)->member[i].pool = *this;
Harald Weltebed35df2011-11-02 13:06:18 +0100304
305 /* Insert into list of unused */
306 (*this)->member[i].prev = (*this)->lastdyn;
307 if ((*this)->lastdyn) {
308 (*this)->lastdyn->next = &((*this)->member[i]);
309 } else {
310 (*this)->firstdyn = &((*this)->member[i]);
311 }
312 (*this)->lastdyn = &((*this)->member[i]);
313 (*this)->member[i].next = NULL; /* Redundant */
314
315 (void)ippool_hashadd(*this, &(*this)->member[i]);
316 }
317
318 (*this)->firststat = NULL;
319 (*this)->laststat = NULL;
320 for (i = dynsize; i < listsize; i++) {
Harald Welted12eab92017-08-02 19:49:47 +0200321 struct in46_addr *i6al = &(*this)->member[i].addr;
322 memset(i6al, 0, sizeof(*i6al));
Harald Weltebed35df2011-11-02 13:06:18 +0100323 (*this)->member[i].inuse = 0;
Harald Welteb513b952017-08-12 12:46:39 +0200324 (*this)->member[i].pool = *this;
Harald Weltebed35df2011-11-02 13:06:18 +0100325
326 /* Insert into list of unused */
327 (*this)->member[i].prev = (*this)->laststat;
328 if ((*this)->laststat) {
329 (*this)->laststat->next = &((*this)->member[i]);
330 } else {
331 (*this)->firststat = &((*this)->member[i]);
332 }
333 (*this)->laststat = &((*this)->member[i]);
334 (*this)->member[i].next = NULL; /* Redundant */
335 }
336
337 if (0)
338 (void)ippool_printaddr(*this);
339 return 0;
jjakoa7cd2492003-04-11 09:40:12 +0000340}
341
342/* Delete existing address pool */
Harald Weltebed35df2011-11-02 13:06:18 +0100343int ippool_free(struct ippool_t *this)
344{
345 free(this->hash);
346 free(this->member);
347 free(this);
348 return 0; /* Always OK */
jjakoa7cd2492003-04-11 09:40:12 +0000349}
350
351/* Find an IP address in the pool */
352int ippool_getip(struct ippool_t *this, struct ippoolm_t **member,
Harald Welted12eab92017-08-02 19:49:47 +0200353 struct in46_addr *addr)
Harald Weltebed35df2011-11-02 13:06:18 +0100354{
355 struct ippoolm_t *p;
356 uint32_t hash;
jjakoa7cd2492003-04-11 09:40:12 +0000357
Harald Weltebed35df2011-11-02 13:06:18 +0100358 /* Find in hash table */
Harald Welted12eab92017-08-02 19:49:47 +0200359 hash = ippool_hash(addr) & this->hashmask;
Harald Weltebed35df2011-11-02 13:06:18 +0100360 for (p = this->hash[hash]; p; p = p->nexthash) {
Harald Welted4d6e092017-08-08 18:10:43 +0200361 if (in46a_prefix_equal(&p->addr, addr)) {
Harald Weltebed35df2011-11-02 13:06:18 +0100362 if (member)
363 *member = p;
364 return 0;
365 }
366 }
367 if (member)
368 *member = NULL;
Holger Hans Peter Freyther9c7fd8e2014-12-04 16:32:37 +0100369 /*SYS_ERR(DIP, LOGL_ERROR, 0, "Address could not be found"); */
Harald Weltebed35df2011-11-02 13:06:18 +0100370 return -1;
jjakoa7cd2492003-04-11 09:40:12 +0000371}
372
jjako88c22162003-07-06 19:33:18 +0000373/**
374 * ippool_newip
375 * Get an IP address. If addr = 0.0.0.0 get a dynamic IP address. Otherwise
376 * check to see if the given address is available. If available within
377 * dynamic address space allocate it there, otherwise allocate within static
378 * address space.
379**/
jjakoa7cd2492003-04-11 09:40:12 +0000380int ippool_newip(struct ippool_t *this, struct ippoolm_t **member,
Harald Welted12eab92017-08-02 19:49:47 +0200381 struct in46_addr *addr, int statip)
Harald Weltebed35df2011-11-02 13:06:18 +0100382{
383 struct ippoolm_t *p;
384 struct ippoolm_t *p2 = NULL;
385 uint32_t hash;
jjakoa7cd2492003-04-11 09:40:12 +0000386
Harald Weltebed35df2011-11-02 13:06:18 +0100387 /* If static:
Pau Espin Pedrolfdd732b2017-10-13 14:32:24 +0200388 * Look in dynaddr.
Harald Weltebed35df2011-11-02 13:06:18 +0100389 * If found remove from firstdyn/lastdyn linked list.
390 * Else allocate from stataddr.
391 * Remove from firststat/laststat linked list.
392 * Insert into hash table.
393 *
394 * If dynamic
395 * Remove from firstdyn/lastdyn linked list.
396 *
397 */
jjako88c22162003-07-06 19:33:18 +0000398
Harald Weltebed35df2011-11-02 13:06:18 +0100399 if (0)
400 (void)ippool_printaddr(this);
jjako88c22162003-07-06 19:33:18 +0000401
Harald Welted12eab92017-08-02 19:49:47 +0200402 int specified = 0;
403 if (addr) {
404 if (addr->len == 4 && addr->v4.s_addr)
405 specified = 1;
406 if (addr->len == 16 && !IN6_IS_ADDR_UNSPECIFIED(&addr->v6))
407 specified = 1;
408 }
409
Harald Weltebed35df2011-11-02 13:06:18 +0100410 /* First check to see if this type of address is allowed */
Harald Welted12eab92017-08-02 19:49:47 +0200411 if (specified && statip) { /* IP address given */
Harald Weltebed35df2011-11-02 13:06:18 +0100412 if (!this->allowstat) {
Holger Hans Peter Freyther9c7fd8e2014-12-04 16:32:37 +0100413 SYS_ERR(DIP, LOGL_ERROR, 0,
Harald Weltebed35df2011-11-02 13:06:18 +0100414 "Static IP address not allowed");
Harald Welted1bf1e12017-08-03 00:00:23 +0200415 return -GTPCAUSE_NOT_SUPPORTED;
Harald Weltebed35df2011-11-02 13:06:18 +0100416 }
Harald Welted12eab92017-08-02 19:49:47 +0200417 if (!in46a_within_mask(addr, &this->stataddr, this->stataddrprefixlen)) {
418 SYS_ERR(DIP, LOGL_ERROR, 0, "Static out of range");
Harald Weltebed35df2011-11-02 13:06:18 +0100419 return -1;
420 }
421 } else {
422 if (!this->allowdyn) {
Holger Hans Peter Freyther9c7fd8e2014-12-04 16:32:37 +0100423 SYS_ERR(DIP, LOGL_ERROR, 0,
Harald Weltebed35df2011-11-02 13:06:18 +0100424 "Dynamic IP address not allowed");
Harald Welted1bf1e12017-08-03 00:00:23 +0200425 return -GTPCAUSE_NOT_SUPPORTED;
Harald Weltebed35df2011-11-02 13:06:18 +0100426 }
427 }
jjakoa7cd2492003-04-11 09:40:12 +0000428
Harald Weltebed35df2011-11-02 13:06:18 +0100429 /* If IP address given try to find it in dynamic address pool */
Harald Welted12eab92017-08-02 19:49:47 +0200430 if (specified) { /* IP address given */
Harald Weltebed35df2011-11-02 13:06:18 +0100431 /* Find in hash table */
Harald Welted12eab92017-08-02 19:49:47 +0200432 hash = ippool_hash(addr) & this->hashmask;
Harald Weltebed35df2011-11-02 13:06:18 +0100433 for (p = this->hash[hash]; p; p = p->nexthash) {
Harald Welted4d6e092017-08-08 18:10:43 +0200434 if (in46a_prefix_equal(&p->addr, addr)) {
Harald Weltebed35df2011-11-02 13:06:18 +0100435 p2 = p;
436 break;
437 }
438 }
439 }
jjakoada9ffa2004-12-30 16:39:11 +0000440
Harald Weltebed35df2011-11-02 13:06:18 +0100441 /* If IP was already allocated we can not use it */
442 if ((!statip) && (p2) && (p2->inuse)) {
443 p2 = NULL;
444 }
jjakoada9ffa2004-12-30 16:39:11 +0000445
Harald Weltebed35df2011-11-02 13:06:18 +0100446 /* If not found yet and dynamic IP then allocate dynamic IP */
447 if ((!p2) && (!statip)) {
448 if (!this->firstdyn) {
Holger Hans Peter Freyther9c7fd8e2014-12-04 16:32:37 +0100449 SYS_ERR(DIP, LOGL_ERROR, 0,
Harald Weltebed35df2011-11-02 13:06:18 +0100450 "No more IP addresses available");
Harald Welted1bf1e12017-08-03 00:00:23 +0200451 return -GTPCAUSE_ADDR_OCCUPIED;
Harald Weltebed35df2011-11-02 13:06:18 +0100452 } else
453 p2 = this->firstdyn;
454 }
jjakoc3213962004-09-09 20:23:50 +0000455
Harald Weltebed35df2011-11-02 13:06:18 +0100456 if (p2) { /* Was allocated from dynamic address pool */
457 if (p2->inuse) {
Holger Hans Peter Freyther9c7fd8e2014-12-04 16:32:37 +0100458 SYS_ERR(DIP, LOGL_ERROR, 0,
Harald Weltebed35df2011-11-02 13:06:18 +0100459 "IP address allready in use");
Harald Welted1bf1e12017-08-03 00:00:23 +0200460 return -GTPCAUSE_SYS_FAIL; /* Allready in use / Should not happen */
Harald Weltebed35df2011-11-02 13:06:18 +0100461 }
jjako88c22162003-07-06 19:33:18 +0000462
Harald Welted4d6e092017-08-08 18:10:43 +0200463 if (p2->addr.len != addr->len && !(addr->len == 16 && p2->addr.len == 8)) {
Harald Welted12eab92017-08-02 19:49:47 +0200464 SYS_ERR(DIP, LOGL_ERROR, 0, "MS requested unsupported PDP context type");
Harald Welted1bf1e12017-08-03 00:00:23 +0200465 return -GTPCAUSE_UNKNOWN_PDP;
Harald Welted12eab92017-08-02 19:49:47 +0200466 }
467
Harald Weltebed35df2011-11-02 13:06:18 +0100468 /* Remove from linked list of free dynamic addresses */
469 if (p2->prev)
470 p2->prev->next = p2->next;
471 else
472 this->firstdyn = p2->next;
473 if (p2->next)
474 p2->next->prev = p2->prev;
475 else
476 this->lastdyn = p2->prev;
477 p2->next = NULL;
478 p2->prev = NULL;
479 p2->inuse = 1; /* Dynamic address in use */
jjako88c22162003-07-06 19:33:18 +0000480
Harald Weltebed35df2011-11-02 13:06:18 +0100481 *member = p2;
482 if (0)
483 (void)ippool_printaddr(this);
484 return 0; /* Success */
485 }
jjako88c22162003-07-06 19:33:18 +0000486
Harald Weltebed35df2011-11-02 13:06:18 +0100487 /* It was not possible to allocate from dynamic address pool */
488 /* Try to allocate from static address space */
jjako88c22162003-07-06 19:33:18 +0000489
Harald Welted12eab92017-08-02 19:49:47 +0200490 if (specified && (statip)) { /* IP address given */
Harald Weltebed35df2011-11-02 13:06:18 +0100491 if (!this->firststat) {
Holger Hans Peter Freyther9c7fd8e2014-12-04 16:32:37 +0100492 SYS_ERR(DIP, LOGL_ERROR, 0,
Harald Weltebed35df2011-11-02 13:06:18 +0100493 "No more IP addresses available");
Harald Welted1bf1e12017-08-03 00:00:23 +0200494 return -GTPCAUSE_ADDR_OCCUPIED; /* No more available */
Harald Weltebed35df2011-11-02 13:06:18 +0100495 } else
496 p2 = this->firststat;
497
Harald Welted12eab92017-08-02 19:49:47 +0200498 if (p2->addr.len != addr->len) {
499 SYS_ERR(DIP, LOGL_ERROR, 0, "MS requested unsupported PDP context type");
Harald Welted1bf1e12017-08-03 00:00:23 +0200500 return -GTPCAUSE_UNKNOWN_PDP;
Harald Welted12eab92017-08-02 19:49:47 +0200501 }
502
Harald Weltebed35df2011-11-02 13:06:18 +0100503 /* Remove from linked list of free static addresses */
504 if (p2->prev)
505 p2->prev->next = p2->next;
506 else
507 this->firststat = p2->next;
508 if (p2->next)
509 p2->next->prev = p2->prev;
510 else
511 this->laststat = p2->prev;
512 p2->next = NULL;
513 p2->prev = NULL;
514 p2->inuse = 2; /* Static address in use */
Stefan Sperlinge405c2f2018-11-21 14:52:43 +0100515 /* p2->addr.len and addr->len already match (see above). */
516 if (p2->addr.len == sizeof(struct in_addr))
517 p2->addr.v4 = addr->v4;
518 else if (p2->addr.len == sizeof(struct in6_addr))
519 p2->addr.v6 = addr->v6;
520 else {
521 SYS_ERR(DIP, LOGL_ERROR, 0, "MS requested unsupported PDP context type");
522 return -GTPCAUSE_UNKNOWN_PDP;
523 }
Harald Weltebed35df2011-11-02 13:06:18 +0100524 *member = p2;
525 (void)ippool_hashadd(this, *member);
526 if (0)
527 (void)ippool_printaddr(this);
528 return 0; /* Success */
529 }
530
Holger Hans Peter Freyther9c7fd8e2014-12-04 16:32:37 +0100531 SYS_ERR(DIP, LOGL_ERROR, 0,
Harald Weltebed35df2011-11-02 13:06:18 +0100532 "Could not allocate IP address");
Harald Welted1bf1e12017-08-03 00:00:23 +0200533 return -GTPCAUSE_SYS_FAIL; /* Should never get here. TODO: Bad code */
jjakoa7cd2492003-04-11 09:40:12 +0000534}
535
Harald Weltebed35df2011-11-02 13:06:18 +0100536int ippool_freeip(struct ippool_t *this, struct ippoolm_t *member)
537{
jjakoa7cd2492003-04-11 09:40:12 +0000538
Harald Weltebed35df2011-11-02 13:06:18 +0100539 if (0)
540 (void)ippool_printaddr(this);
jjakoa7cd2492003-04-11 09:40:12 +0000541
Harald Weltebed35df2011-11-02 13:06:18 +0100542 if (!member->inuse) {
Holger Hans Peter Freyther9c7fd8e2014-12-04 16:32:37 +0100543 SYS_ERR(DIP, LOGL_ERROR, 0, "Address not in use");
Harald Weltebed35df2011-11-02 13:06:18 +0100544 return -1; /* Not in use: Should not happen */
545 }
jjakoa7cd2492003-04-11 09:40:12 +0000546
Harald Weltebed35df2011-11-02 13:06:18 +0100547 switch (member->inuse) {
548 case 0: /* Not in use: Should not happen */
Holger Hans Peter Freyther9c7fd8e2014-12-04 16:32:37 +0100549 SYS_ERR(DIP, LOGL_ERROR, 0, "Address not in use");
Harald Weltebed35df2011-11-02 13:06:18 +0100550 return -1;
551 case 1: /* Allocated from dynamic address space */
552 /* Insert into list of unused */
553 member->prev = this->lastdyn;
554 if (this->lastdyn) {
555 this->lastdyn->next = member;
556 } else {
557 this->firstdyn = member;
558 }
559 this->lastdyn = member;
560
561 member->inuse = 0;
562 member->peer = NULL;
563 if (0)
564 (void)ippool_printaddr(this);
565 return 0;
566 case 2: /* Allocated from static address space */
567 if (ippool_hashdel(this, member))
568 return -1;
569 /* Insert into list of unused */
570 member->prev = this->laststat;
571 if (this->laststat) {
572 this->laststat->next = member;
573 } else {
574 this->firststat = member;
575 }
576 this->laststat = member;
577
578 member->inuse = 0;
Harald Welted12eab92017-08-02 19:49:47 +0200579 memset(&member->addr, 0, sizeof(member->addr));
Harald Weltebed35df2011-11-02 13:06:18 +0100580 member->peer = NULL;
581 member->nexthash = NULL;
582 if (0)
583 (void)ippool_printaddr(this);
584 return 0;
585 default: /* Should not happen */
Holger Hans Peter Freyther9c7fd8e2014-12-04 16:32:37 +0100586 SYS_ERR(DIP, LOGL_ERROR, 0,
Harald Weltebed35df2011-11-02 13:06:18 +0100587 "Could not free IP address");
588 return -1;
589 }
jjakoa7cd2492003-04-11 09:40:12 +0000590}