]> git.ipfire.org Git - people/ms/libloc.git/blame - src/network-list.c
importer: Drop EDROP as it has been merged into DROP
[people/ms/libloc.git] / src / network-list.c
CommitLineData
e0b9ff5f
MT
1/*
2 libloc - A library to determine the location of someone on the Internet
3
4 Copyright (C) 2020 IPFire Development Team <info@ipfire.org>
5
6 This library is free software; you can redistribute it and/or
7 modify it under the terms of the GNU Lesser General Public
8 License as published by the Free Software Foundation; either
9 version 2.1 of the License, or (at your option) any later version.
10
11 This library is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Lesser General Public License for more details.
15*/
16
17#include <errno.h>
18#include <stdlib.h>
af4689bf 19#include <time.h>
e0b9ff5f 20
0b1fef38 21#include <libloc/address.h>
c12a1385
MT
22#include <libloc/libloc.h>
23#include <libloc/network.h>
24#include <libloc/private.h>
e0b9ff5f
MT
25
26struct loc_network_list {
27 struct loc_ctx* ctx;
28 int refcount;
29
3b44e421
MT
30 struct loc_network** elements;
31 size_t elements_size;
32
e0b9ff5f 33 size_t size;
e0b9ff5f
MT
34};
35
635c344a
MT
36static int loc_network_list_grow(struct loc_network_list* list) {
37 size_t size = list->elements_size * 2;
38 if (size < 1024)
39 size = 1024;
40
3b44e421
MT
41 DEBUG(list->ctx, "Growing network list %p by %zu to %zu\n",
42 list, size, list->elements_size + size);
43
44 struct loc_network** elements = reallocarray(list->elements,
45 list->elements_size + size, sizeof(*list->elements));
46 if (!elements)
198e382c 47 return 1;
3b44e421
MT
48
49 list->elements = elements;
50 list->elements_size += size;
51
52 return 0;
53}
54
e0b9ff5f
MT
55LOC_EXPORT int loc_network_list_new(struct loc_ctx* ctx,
56 struct loc_network_list** list) {
57 struct loc_network_list* l = calloc(1, sizeof(*l));
58 if (!l)
59 return -ENOMEM;
60
61 l->ctx = loc_ref(ctx);
62 l->refcount = 1;
63
e0b9ff5f
MT
64 DEBUG(l->ctx, "Network list allocated at %p\n", l);
65 *list = l;
66 return 0;
67}
68
69LOC_EXPORT struct loc_network_list* loc_network_list_ref(struct loc_network_list* list) {
70 list->refcount++;
71
72 return list;
73}
74
75static void loc_network_list_free(struct loc_network_list* list) {
76 DEBUG(list->ctx, "Releasing network list at %p\n", list);
77
fff093b6
MT
78 // Remove all content
79 loc_network_list_clear(list);
e0b9ff5f
MT
80
81 loc_unref(list->ctx);
82 free(list);
83}
84
85LOC_EXPORT struct loc_network_list* loc_network_list_unref(struct loc_network_list* list) {
e0b9ff5f
MT
86 if (--list->refcount > 0)
87 return list;
88
89 loc_network_list_free(list);
90 return NULL;
91}
92
93LOC_EXPORT size_t loc_network_list_size(struct loc_network_list* list) {
94 return list->size;
95}
96
97LOC_EXPORT int loc_network_list_empty(struct loc_network_list* list) {
98 return list->size == 0;
99}
100
101LOC_EXPORT void loc_network_list_clear(struct loc_network_list* list) {
248f5e04
MT
102 if (!list->elements)
103 return;
104
e0b9ff5f 105 for (unsigned int i = 0; i < list->size; i++)
3b44e421 106 loc_network_unref(list->elements[i]);
e0b9ff5f 107
248f5e04 108 free(list->elements);
d42f34ce 109 list->elements = NULL;
248f5e04
MT
110 list->elements_size = 0;
111
e0b9ff5f
MT
112 list->size = 0;
113}
114
115LOC_EXPORT void loc_network_list_dump(struct loc_network_list* list) {
116 struct loc_network* network;
e0b9ff5f
MT
117
118 for (unsigned int i = 0; i < list->size; i++) {
3b44e421 119 network = list->elements[i];
e0b9ff5f 120
0a0a289a
MT
121 INFO(list->ctx, "%4d: %s\n",
122 i, loc_network_str(network));
e0b9ff5f
MT
123 }
124}
125
126LOC_EXPORT struct loc_network* loc_network_list_get(struct loc_network_list* list, size_t index) {
127 // Check index
128 if (index >= list->size)
129 return NULL;
130
3b44e421 131 return loc_network_ref(list->elements[index]);
e0b9ff5f
MT
132}
133
af4689bf
MT
134static off_t loc_network_list_find(struct loc_network_list* list,
135 struct loc_network* network, int* found) {
92202e5a
MT
136 // Insert at the beginning for an empty list
137 if (loc_network_list_empty(list))
138 return 0;
139
af4689bf
MT
140 off_t lo = 0;
141 off_t hi = list->size - 1;
77e6d537 142 int result;
af4689bf 143
77e6d537
MT
144 // Since we are working on an ordered list, there is often a good chance that
145 // the network we are looking for is at the end or has to go to the end.
146 if (hi >= 0) {
147 result = loc_network_cmp(network, list->elements[hi]);
148
149 // Match, so we are done
150 if (result == 0) {
151 *found = 1;
152
153 return hi;
154
155 // This needs to be added after the last one
156 } else if (result > 0) {
157 *found = 0;
158
159 return hi + 1;
160 }
161 }
af4689bf
MT
162
163#ifdef ENABLE_DEBUG
164 // Save start time
165 clock_t start = clock();
166#endif
167
168 off_t i = 0;
169
170 while (lo <= hi) {
171 i = (lo + hi) / 2;
172
173 // Check if this is a match
77e6d537 174 result = loc_network_cmp(network, list->elements[i]);
af4689bf
MT
175
176 if (result == 0) {
177 *found = 1;
178
179#ifdef ENABLE_DEBUG
180 clock_t end = clock();
181
182 // Log how fast this has been
183 DEBUG(list->ctx, "Found network in %.4fms at %jd\n",
184 (double)(end - start) / CLOCKS_PER_SEC * 1000, (intmax_t)i);
185#endif
186
187 return i;
188 }
189
775621e8 190 if (result > 0) {
af4689bf 191 lo = i + 1;
775621e8
MT
192 i++;
193 } else {
af4689bf 194 hi = i - 1;
775621e8 195 }
af4689bf
MT
196 }
197
77e6d537
MT
198 *found = 0;
199
af4689bf
MT
200#ifdef ENABLE_DEBUG
201 clock_t end = clock();
202
203 // Log how fast this has been
204 DEBUG(list->ctx, "Did not find network in %.4fms (last i = %jd)\n",
205 (double)(end - start) / CLOCKS_PER_SEC * 1000, (intmax_t)i);
206#endif
207
208 return i;
209}
210
e0b9ff5f 211LOC_EXPORT int loc_network_list_push(struct loc_network_list* list, struct loc_network* network) {
af4689bf
MT
212 int found = 0;
213
214 off_t index = loc_network_list_find(list, network, &found);
215
216 // The network has been found on the list. Nothing to do.
217 if (found)
e0b9ff5f
MT
218 return 0;
219
af4689bf
MT
220 DEBUG(list->ctx, "%p: Inserting network %p at index %jd\n",
221 list, network, (intmax_t)index);
222
e0b9ff5f 223 // Check if we have space left
3b44e421 224 if (list->size >= list->elements_size) {
635c344a 225 int r = loc_network_list_grow(list);
3b44e421
MT
226 if (r)
227 return r;
e0b9ff5f
MT
228 }
229
af4689bf
MT
230 // The list is now larger
231 list->size++;
232
233 // Move all elements out of the way
234 for (unsigned int i = list->size - 1; i > index; i--)
235 list->elements[i] = list->elements[i - 1];
e0b9ff5f 236
af4689bf
MT
237 // Add the new element at the right place
238 list->elements[index] = loc_network_ref(network);
e0b9ff5f
MT
239
240 return 0;
241}
242
243LOC_EXPORT struct loc_network* loc_network_list_pop(struct loc_network_list* list) {
244 // Return nothing when empty
245 if (loc_network_list_empty(list)) {
246 DEBUG(list->ctx, "%p: Popped empty stack\n", list);
247 return NULL;
248 }
249
3b44e421 250 struct loc_network* network = list->elements[--list->size];
e0b9ff5f
MT
251
252 DEBUG(list->ctx, "%p: Popping network %p from stack\n", list, network);
253
254 return network;
255}
256
257LOC_EXPORT struct loc_network* loc_network_list_pop_first(struct loc_network_list* list) {
258 // Return nothing when empty
259 if (loc_network_list_empty(list)) {
260 DEBUG(list->ctx, "%p: Popped empty stack\n", list);
261 return NULL;
262 }
263
3b44e421 264 struct loc_network* network = list->elements[0];
e0b9ff5f
MT
265
266 // Move all elements to the top of the stack
673e03f7 267 for (unsigned int i = 0; i < list->size - 1; i++) {
3b44e421 268 list->elements[i] = list->elements[i+1];
e0b9ff5f
MT
269 }
270
673e03f7
MT
271 // The list is shorter now
272 --list->size;
273
e0b9ff5f
MT
274 DEBUG(list->ctx, "%p: Popping network %p from stack\n", list, network);
275
276 return network;
277}
278
c1ce4349
MT
279int loc_network_list_remove(struct loc_network_list* list, struct loc_network* network) {
280 int found = 0;
281
282 // Find the network on the list
283 off_t index = loc_network_list_find(list, network, &found);
284
285 // Nothing to do if the network wasn't found
286 if (!found)
287 return 0;
288
289 // Dereference the network at the position
290 loc_network_unref(list->elements[index]);
291
292 // Move all other elements back
293 for (unsigned int i = index; i < list->size - 1; i++)
294 list->elements[i] = list->elements[i+1];
295
296 // The list is shorter now
297 --list->size;
298
299 return 0;
300}
301
e0b9ff5f 302LOC_EXPORT int loc_network_list_contains(struct loc_network_list* list, struct loc_network* network) {
af4689bf 303 int found = 0;
e0b9ff5f 304
af4689bf
MT
305 loc_network_list_find(list, network, &found);
306
307 return found;
e0b9ff5f
MT
308}
309
e0b9ff5f
MT
310LOC_EXPORT int loc_network_list_merge(
311 struct loc_network_list* self, struct loc_network_list* other) {
312 int r;
313
314 for (unsigned int i = 0; i < other->size; i++) {
3b44e421 315 r = loc_network_list_push(self, other->elements[i]);
e0b9ff5f
MT
316 if (r)
317 return r;
318 }
319
320 return 0;
321}
8c37d8a7
MT
322
323int loc_network_list_summarize(struct loc_ctx* ctx,
324 const struct in6_addr* first, const struct in6_addr* last, struct loc_network_list** list) {
325 int r;
326
327 if (!list) {
328 errno = EINVAL;
329 return 1;
330 }
331
0a0a289a
MT
332 DEBUG(ctx, "Summarizing %s - %s\n", loc_address_str(first), loc_address_str(last));
333
88a3db10
MT
334 const int family1 = loc_address_family(first);
335 const int family2 = loc_address_family(last);
8c37d8a7 336
88a3db10
MT
337 // Check if address families match
338 if (family1 != family2) {
8c37d8a7
MT
339 ERROR(ctx, "Address families do not match\n");
340 errno = EINVAL;
341 return 1;
342 }
343
344 // Check if the last address is larger than the first address
9c1e2943 345 if (loc_address_cmp(first, last) >= 0) {
8c37d8a7
MT
346 ERROR(ctx, "The first address must be smaller than the last address\n");
347 errno = EINVAL;
348 return 1;
349 }
350
351 struct loc_network* network = NULL;
8c37d8a7 352 struct in6_addr start = *first;
8c37d8a7 353
88a3db10
MT
354 const int family_bit_length = loc_address_family_bit_length(family1);
355
9c1e2943 356 while (loc_address_cmp(&start, last) <= 0) {
f39c751d 357 struct in6_addr num;
88a3db10 358 int bits1;
f39c751d 359
d701dcfd 360 // Find the number of trailing zeroes of the start address
88a3db10
MT
361 if (loc_address_all_zeroes(&start))
362 bits1 = family_bit_length;
363 else {
364 bits1 = loc_address_count_trailing_zero_bits(&start);
365 if (bits1 > family_bit_length)
366 bits1 = family_bit_length;
367 }
8c37d8a7 368
d701dcfd
MT
369 // Subtract the start address from the last address and add one
370 // (i.e. how many addresses are in this network?)
f39c751d
MT
371 r = loc_address_sub(&num, last, &start);
372 if (r)
373 return r;
374
5b72642c 375 loc_address_increment(&num);
8c37d8a7 376
d701dcfd
MT
377 // How many bits do we need to represent this address?
378 int bits2 = loc_address_bit_length(&num) - 1;
8c37d8a7 379
d701dcfd
MT
380 // Select the smaller one
381 int bits = (bits1 > bits2) ? bits2 : bits1;
382
383 // Create a network
1fd09d0b 384 r = loc_network_new(ctx, &network, &start, family_bit_length - bits);
d701dcfd
MT
385 if (r)
386 return r;
387
a7c71d80 388 DEBUG(ctx, "Found network %s\n", loc_network_str(network));
8c37d8a7 389
d701dcfd 390 // Push network on the list
8c37d8a7
MT
391 r = loc_network_list_push(*list, network);
392 if (r) {
393 loc_network_unref(network);
394 return r;
395 }
396
d701dcfd
MT
397 // The next network starts right after this one
398 start = *loc_network_get_last_address(network);
0905528e
MT
399
400 // If we have reached the end of possible IP addresses, we stop
401 if (loc_address_all_ones(&start))
402 break;
403
5b72642c 404 loc_address_increment(&start);
8c37d8a7
MT
405 }
406
407 return 0;
408}
c1ce4349
MT
409
410void loc_network_list_remove_with_prefix_smaller_than(
411 struct loc_network_list* list, const unsigned int prefix) {
412 unsigned int p = 0;
413
414 // Count how many networks were removed
415 unsigned int removed = 0;
416
417 for (unsigned int i = 0; i < list->size; i++) {
418 // Fetch the prefix
419 p = loc_network_prefix(list->elements[i]);
420
421 if (p > prefix) {
422 // Drop this network
423 loc_network_unref(list->elements[i]);
424
425 // Increment counter
426 removed++;
427
428 continue;
429 }
430
431 // Move pointers backwards to keep the list filled
432 list->elements[i - removed] = list->elements[i];
433 }
434
435 // Adjust size
436 list->size -= removed;
437
438 return;
439}