Makefile.in: Drop dist target
[secnet] / ipaddr.c
CommitLineData
794f2398
SE
1/* The 'ipset' data structure and related algorithms in this file were
2 inspired by the 'ipaddr.py' library from Cendio Systems AB. */
c215a4bc
IJ
3/*
4 * This file is part of secnet.
5 * See README for full list of copyright holders.
6 *
7 * secnet is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU General Public License as published by
9c6a8729 9 * the Free Software Foundation; either version 3 of the License, or
c215a4bc
IJ
10 * (at your option) any later version.
11 *
12 * secnet is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * version 3 along with secnet; if not, see
19 * https://www.gnu.org/licenses/gpl.html.
20 */
794f2398 21
7138d0c5 22#include "secnet.h"
59230b9b
IJ
23#include <limits.h>
24#include <assert.h>
7138d0c5 25#include <stdio.h>
3b83c932 26#include <string.h>
794f2398 27#include "ipaddr.h"
c9c047dc 28#include "util.h"
794f2398
SE
29
30#define DEFAULT_ALLOC 2
31#define EXTEND_ALLOC_BY 4
32
33struct subnet_list *subnet_list_new(void)
34{
35 struct subnet_list *r;
b7886fd4 36 NEW(r);
794f2398
SE
37 r->entries=0;
38 r->alloc=DEFAULT_ALLOC;
b4ececfc 39 NEW_ARY(r->list,r->alloc);
794f2398
SE
40 return r;
41}
42
43void subnet_list_free(struct subnet_list *a)
44{
45 if (a->list) free(a->list);
46 free(a);
47}
48
1caa23ff 49static void subnet_list_set_len(struct subnet_list *a, int32_t l)
794f2398 50{
1caa23ff 51 int32_t na;
794f2398
SE
52
53 if (l>a->alloc) {
2f9fa75f 54 assert(a->alloc < INT_MAX-EXTEND_ALLOC_BY);
794f2398 55 na=a->alloc+EXTEND_ALLOC_BY;
2f9fa75f 56 REALLOC_ARY(a->list,na);
794f2398 57 a->alloc=na;
794f2398
SE
58 }
59 a->entries=l;
60}
61
1caa23ff 62void subnet_list_append(struct subnet_list *a, uint32_t prefix, int len)
794f2398
SE
63{
64 struct subnet *sn;
59230b9b 65 assert(a->entries < INT_MAX);
794f2398
SE
66 subnet_list_set_len(a,a->entries+1);
67 sn=&a->list[a->entries-1];
68 sn->prefix=prefix;
69 sn->len=len;
70 sn->mask=len?(0xffffffff << (32-len)):0;
71}
7138d0c5 72
794f2398
SE
73struct ipset *ipset_new(void)
74{
75 struct ipset *r;
b7886fd4 76 NEW(r);
794f2398
SE
77 r->l=0;
78 r->a=DEFAULT_ALLOC;
952f601f 79 NEW_ARY(r->d,r->a);
794f2398
SE
80 return r;
81}
7138d0c5 82
794f2398 83void ipset_free(struct ipset *a)
7138d0c5 84{
794f2398
SE
85 if (a->d) free(a->d);
86 free(a);
7138d0c5
SE
87}
88
794f2398
SE
89#ifdef DEBUG
90static void ipset_dump(struct ipset *a, string_t name)
7138d0c5 91{
1caa23ff 92 int32_t i;
794f2398
SE
93
94 printf("%s: ",name);
95 for (i=0; i<a->l; i++) {
96 printf("[%08x-%08x] ",a->d[i].a,a->d[i].b);
7138d0c5 97 }
794f2398 98 printf("\n");
7138d0c5 99}
794f2398 100#endif
7138d0c5 101
794f2398 102struct ipset *ipset_from_subnet(struct subnet s)
7138d0c5 103{
794f2398
SE
104 struct ipset *r;
105
106 r=ipset_new();
107 r->l=1;
108 r->d[0].a=s.prefix;
109 r->d[0].b=s.prefix | (~s.mask);
110 return r;
7138d0c5
SE
111}
112
794f2398 113struct ipset *ipset_from_subnet_list(struct subnet_list *l)
7138d0c5 114{
794f2398 115 struct ipset *r, *a, *b;
1caa23ff 116 int32_t i;
7138d0c5 117
794f2398
SE
118 r=ipset_new();
119 for (i=0; i<l->entries; i++) {
120 a=ipset_from_subnet(l->list[i]);
121 b=ipset_union(r,a);
122 ipset_free(a);
123 ipset_free(r);
124 r=b;
7138d0c5 125 }
794f2398 126 return r;
7138d0c5
SE
127}
128
1caa23ff 129static void ipset_set_len(struct ipset *a, int32_t l)
794f2398 130{
1caa23ff 131 int32_t na;
794f2398
SE
132
133 if (l>a->a) {
59230b9b 134 assert(a->a < INT_MAX-EXTEND_ALLOC_BY);
794f2398 135 na=a->a+EXTEND_ALLOC_BY;
2f9fa75f 136 REALLOC_ARY(a->d,na);
794f2398 137 a->a=na;
794f2398
SE
138 }
139 a->l=l;
140}
141
142static void ipset_append_range(struct ipset *a, struct iprange r)
143{
144 ipset_set_len(a,a->l+1);
145 a->d[a->l-1]=r;
146}
147
794f2398
SE
148struct ipset *ipset_union(struct ipset *a, struct ipset *b)
149{
150 struct ipset *c;
151 struct iprange r;
1caa23ff 152 int32_t ia,ib;
794f2398
SE
153
154 c=ipset_new();
155 ia=0; ib=0;
156 while (ia<a->l || ib<b->l) {
157 if (ia<a->l)
158 if (ib<b->l)
159 if (a->d[ia].a < b->d[ib].a)
160 r=a->d[ia++];
161 else
162 r=b->d[ib++];
163 else
164 r=a->d[ia++];
165 else
166 r=b->d[ib++];
167
168 if (c->l==0)
169 ipset_append_range(c,r);
170 else if (r.a <= c->d[c->l-1].b+1)
171 /* Extends (or is consumed by) the last range */
c9c047dc 172 c->d[c->l-1].b=MAX(c->d[c->l-1].b, r.b);
794f2398
SE
173 else
174 ipset_append_range(c,r);
175 }
176 return c;
177}
178
179struct ipset *ipset_intersection(struct ipset *a, struct ipset *b)
180{
181 struct ipset *r;
182 struct iprange ra, rb;
1caa23ff 183 int32_t ia,ib;
794f2398
SE
184
185 r=ipset_new();
186 ia=0; ib=0;
187
188 while (ia<a->l && ib<b->l) {
189 ra=a->d[ia];
190 rb=b->d[ib];
191 if (ra.b < rb.a)
192 /* The first entry of a doesn't overlap with any part of b */
193 ia++;
194 else if (ra.a > rb.b)
195 /* The first entry of b doesn't overlap with any part of a */
196 ib++;
197 else {
198 /* Trim away any leading edges */
199 if (ra.a < rb.a)
200 /* a starts before b */
201 ra.a=rb.a;
202 else if (ra.a > rb.a)
203 /* b starts before a */
204 rb.a=ra.a;
205
206 /* Now the ranges start at the same point */
207 if (ra.b == rb.b) {
208 /* The ranges are equal */
209 ipset_append_range(r,ra);
210 ia++;
211 ib++;
212 } else if (ra.b < rb.b) {
213 /* a is the smaller range */
214 ipset_append_range(r,ra);
215 ia++;
216 } else {
217 /* y is the smaller range */
218 ipset_append_range(r,rb);
219 ib++;
220 }
221 }
222 }
223 return r;
224}
225
226struct ipset *ipset_complement(struct ipset *a)
227{
228 struct ipset *r;
229 struct iprange n;
230 int64_t pre;
1caa23ff
IJ
231 int32_t i;
232 uint32_t lo,hi;
794f2398
SE
233
234 r=ipset_new();
235 pre=-1;
236 for (i=0; i<a->l; i++) {
237 lo=a->d[i].a;
238 hi=a->d[i].b;
239 if (lo!=0) {
240 n.a=pre+1;
241 n.b=lo-1;
242 ipset_append_range(r,n);
243 }
244 pre=hi;
245 }
246 if (pre!=0xffffffff) {
247 n.a=pre+1;
248 n.b=0xffffffff;
249 ipset_append_range(r,n);
250 }
251 return r;
252}
253
254/* Return a-b */
255struct ipset *ipset_subtract(struct ipset *a, struct ipset *b)
256{
257 struct ipset *c, *r;
258 c=ipset_complement(b);
259 r=ipset_intersection(a,c);
260 ipset_free(c);
261 return r;
262}
263
264bool_t ipset_is_empty(struct ipset *a)
265{
266 return (a->l==0);
267}
268
269bool_t ipset_contains_addr(struct ipset *a, uint32_t addr)
7138d0c5 270{
1caa23ff 271 int32_t i;
794f2398
SE
272 struct iprange r;
273
274 for (i=0; i<a->l; i++) {
275 r=a->d[i];
276 if (addr>=r.a && addr<=r.b) return True;
277 if (addr<r.a) return False;
7138d0c5
SE
278 }
279 return False;
280}
281
794f2398
SE
282/* sub is a subset of super if it does not intersect with the complement
283 of super */
284bool_t ipset_is_subset(struct ipset *super, struct ipset *sub)
285{
286 struct ipset *superc;
287 struct ipset *inter;
288 bool_t empty;
289
290 superc=ipset_complement(super);
291 inter=ipset_intersection(superc,sub);
292 empty=ipset_is_empty(inter);
293 ipset_free(inter);
294 ipset_free(superc);
295 return empty;
296}
297
298struct subnet_list *ipset_to_subnet_list(struct ipset *is)
299{
300 struct subnet_list *r;
ff05a229 301 int64_t a,b,lobit,himask,lomask;
1caa23ff
IJ
302 int bits;
303 int32_t i;
794f2398
SE
304
305 r=subnet_list_new();
306 for (i=0; i<is->l; i++) {
307 a=is->d[i].a;
308 b=is->d[i].b;
309
310 lomask=1;
311 lobit=1;
312 himask=0xfffffffe;
313 bits=32;
314 while (a<=b) {
315 if ((a & lomask) != 0) {
316 subnet_list_append(r,a,bits);
317 a=a+lobit;
318 } else if ((b & lomask) != lomask) {
319 subnet_list_append(r,b&himask,bits);
320 b=b-lobit;
321 } else {
322 lomask = (lomask << 1) | 1;
323 lobit = (lobit << 1);
324 himask = himask ^ lobit;
325 bits = bits - 1;
326 ASSERT(bits>=0);
327 }
328 }
329 }
330 /* Sort the list? */
331 return r;
332}
333
5540bab7
IJ
334#define IPADDR_BUFLEN 20
335
336static char *ipaddr_getbuf(void)
337{
5e7a63be 338 SBUF_DEFINE(16, IPADDR_BUFLEN);
5f37eb10 339 return SBUF;
5540bab7
IJ
340}
341
7138d0c5
SE
342/* The string buffer must be at least 16 bytes long */
343string_t ipaddr_to_string(uint32_t addr)
344{
345 uint8_t a,b,c,d;
346 string_t s;
347
5540bab7 348 s=ipaddr_getbuf();
7138d0c5
SE
349 a=addr>>24;
350 b=addr>>16;
351 c=addr>>8;
352 d=addr;
353 snprintf(s, 16, "%d.%d.%d.%d", a, b, c, d);
354 return s;
355}
356
794f2398 357string_t subnet_to_string(struct subnet sn)
7138d0c5 358{
794f2398 359 uint32_t addr=sn.prefix;
7138d0c5
SE
360 uint8_t a,b,c,d;
361 string_t s;
7138d0c5 362
322a0105 363 s=ipaddr_getbuf();
7138d0c5
SE
364 a=addr>>24;
365 b=addr>>16;
366 c=addr>>8;
367 d=addr;
794f2398
SE
368 snprintf(s, 19, "%d.%d.%d.%d/%d", a, b, c, d, sn.len);
369 return s;
370}
371
fe5e9cc4 372static struct subnet string_item_to_subnet(item_t *i, cstring_t desc,
794f2398
SE
373 bool_t *invert)
374{
375 struct subnet s;
376 uint32_t a, b, c, d, n;
1caa23ff 377 int match;
fe5e9cc4 378 cstring_t in;
794f2398
SE
379
380 *invert=False;
381
382 /* i is not guaranteed to be a string */
383 if (i->type!=t_string) {
384 cfgfatal(i->loc,desc,"expecting a string (subnet specification)\n");
385 }
386 in=i->data.string;
387
388 if (strcmp(in,"default")==0) {
389 s.prefix=0;
390 s.mask=0;
391 s.len=0;
392 return s;
393 }
394
395 if (*in=='!') {
396 *invert=True;
397 in++;
398 }
399 /* We expect strings of the form "a.b.c.d[/n]", i.e. the dots are
400 NOT optional. The subnet mask is optional; if missing it is assumed
401 to be /32. */
402 match=sscanf(in,"%u.%u.%u.%u/%u", &a, &b, &c, &d, &n);
403 if (match<4) {
404 cfgfatal(i->loc,desc,"\"%s\" is not a valid "
405 "subnet specification\n",in);
406 }
407 if (match<5) {
408 n=32;
7138d0c5 409 }
794f2398
SE
410 if (a>255 || b>255 || c>255 || d>255 || n>32) {
411 cfgfatal(i->loc,desc,"\"%s\": range error\n",in);
412 }
413 s.prefix=(a<<24)|(b<<16)|(c<<8)|(d);
414 s.mask=n?(~0UL << (32-n)):0;
415 s.len=n;
416 if (s.prefix & ~s.mask) {
417 cfgfatal(i->loc,desc,"\"%s\": prefix not fully contained "
418 "in mask\n",in);
7138d0c5 419 }
7138d0c5
SE
420 return s;
421}
794f2398 422
a094a1ba 423uint32_t string_item_to_ipaddr(const item_t *i, cstring_t desc)
794f2398
SE
424{
425 uint32_t a, b, c, d;
1caa23ff 426 int match;
794f2398
SE
427
428 /* i is not guaranteed to be a string */
429 if (i->type!=t_string) {
430 cfgfatal(i->loc,desc,"expecting a string (IP address)\n");
431 }
432
433 match=sscanf(i->data.string,"%u.%u.%u.%u", &a, &b, &c, &d);
434 if (match<4) {
435 cfgfatal(i->loc,desc,"\"%s\" is not a valid "
436 "IP address\n",i->data.string);
437 }
438 if (a>255 || b>255 || c>255 || d>255) {
439 cfgfatal(i->loc,desc,"\"%s\": range error\n",i->data.string);
440 }
441 return (a<<24)|(b<<16)|(c<<8)|(d);
442}
443
444struct ipset *string_list_to_ipset(list_t *l, struct cloc loc,
fe5e9cc4 445 cstring_t module, cstring_t param)
794f2398
SE
446{
447 struct ipset *r, *n, *isn;
448 uint32_t e,i;
449 item_t *item;
450 bool_t inv;
451
452 r=ipset_new();
453 e=list_length(l);
454 for (i=0; i<e; i++) {
455 item=list_elem(l,i);
456 isn=ipset_from_subnet(string_item_to_subnet(item,param,&inv));
457 if (inv) {
458 n=ipset_subtract(r,isn);
459 } else {
460 n=ipset_union(r,isn);
461 }
462 ipset_free(r);
463 ipset_free(isn);
464 r=n;
465 }
466 return r;
467}