]>
git.ipfire.org Git - people/ms/libloc.git/blob - src/network-list.c
2 libloc - A library to determine the location of someone on the Internet
4 Copyright (C) 2020 IPFire Development Team <info@ipfire.org>
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.
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.
21 #include <libloc/address.h>
22 #include <libloc/libloc.h>
23 #include <libloc/network.h>
24 #include <libloc/private.h>
26 struct loc_network_list
{
30 struct loc_network
** elements
;
36 static int loc_network_list_grow(struct loc_network_list
* list
, size_t size
) {
37 DEBUG(list
->ctx
, "Growing network list %p by %zu to %zu\n",
38 list
, size
, list
->elements_size
+ size
);
40 struct loc_network
** elements
= reallocarray(list
->elements
,
41 list
->elements_size
+ size
, sizeof(*list
->elements
));
45 list
->elements
= elements
;
46 list
->elements_size
+= size
;
51 LOC_EXPORT
int loc_network_list_new(struct loc_ctx
* ctx
,
52 struct loc_network_list
** list
) {
53 struct loc_network_list
* l
= calloc(1, sizeof(*l
));
57 l
->ctx
= loc_ref(ctx
);
60 DEBUG(l
->ctx
, "Network list allocated at %p\n", l
);
65 LOC_EXPORT
struct loc_network_list
* loc_network_list_ref(struct loc_network_list
* list
) {
71 static void loc_network_list_free(struct loc_network_list
* list
) {
72 DEBUG(list
->ctx
, "Releasing network list at %p\n", list
);
75 loc_network_list_clear(list
);
81 LOC_EXPORT
struct loc_network_list
* loc_network_list_unref(struct loc_network_list
* list
) {
85 if (--list
->refcount
> 0)
88 loc_network_list_free(list
);
92 LOC_EXPORT
size_t loc_network_list_size(struct loc_network_list
* list
) {
96 LOC_EXPORT
int loc_network_list_empty(struct loc_network_list
* list
) {
97 return list
->size
== 0;
100 LOC_EXPORT
void loc_network_list_clear(struct loc_network_list
* list
) {
104 for (unsigned int i
= 0; i
< list
->size
; i
++)
105 loc_network_unref(list
->elements
[i
]);
107 free(list
->elements
);
108 list
->elements
= NULL
;
109 list
->elements_size
= 0;
114 LOC_EXPORT
void loc_network_list_dump(struct loc_network_list
* list
) {
115 struct loc_network
* network
;
117 for (unsigned int i
= 0; i
< list
->size
; i
++) {
118 network
= list
->elements
[i
];
120 INFO(list
->ctx
, "%4d: %s\n",
121 i
, loc_network_str(network
));
125 LOC_EXPORT
struct loc_network
* loc_network_list_get(struct loc_network_list
* list
, size_t index
) {
127 if (index
>= list
->size
)
130 return loc_network_ref(list
->elements
[index
]);
133 static off_t
loc_network_list_find(struct loc_network_list
* list
,
134 struct loc_network
* network
, int* found
) {
135 // Insert at the beginning for an empty list
136 if (loc_network_list_empty(list
))
140 off_t hi
= list
->size
- 1;
143 // Since we are working on an ordered list, there is often a good chance that
144 // the network we are looking for is at the end or has to go to the end.
146 result
= loc_network_cmp(network
, list
->elements
[hi
]);
148 // Match, so we are done
154 // This needs to be added after the last one
155 } else if (result
> 0) {
164 clock_t start
= clock();
172 // Check if this is a match
173 result
= loc_network_cmp(network
, list
->elements
[i
]);
179 clock_t end
= clock();
181 // Log how fast this has been
182 DEBUG(list
->ctx
, "Found network in %.4fms at %jd\n",
183 (double)(end
- start
) / CLOCKS_PER_SEC
* 1000, (intmax_t)i
);
200 clock_t end
= clock();
202 // Log how fast this has been
203 DEBUG(list
->ctx
, "Did not find network in %.4fms (last i = %jd)\n",
204 (double)(end
- start
) / CLOCKS_PER_SEC
* 1000, (intmax_t)i
);
210 LOC_EXPORT
int loc_network_list_push(struct loc_network_list
* list
, struct loc_network
* network
) {
213 off_t index
= loc_network_list_find(list
, network
, &found
);
215 // The network has been found on the list. Nothing to do.
219 DEBUG(list
->ctx
, "%p: Inserting network %p at index %jd\n",
220 list
, network
, (intmax_t)index
);
222 // Check if we have space left
223 if (list
->size
>= list
->elements_size
) {
224 int r
= loc_network_list_grow(list
, 64);
229 // The list is now larger
232 // Move all elements out of the way
233 for (unsigned int i
= list
->size
- 1; i
> index
; i
--)
234 list
->elements
[i
] = list
->elements
[i
- 1];
236 // Add the new element at the right place
237 list
->elements
[index
] = loc_network_ref(network
);
242 LOC_EXPORT
struct loc_network
* loc_network_list_pop(struct loc_network_list
* list
) {
243 // Return nothing when empty
244 if (loc_network_list_empty(list
)) {
245 DEBUG(list
->ctx
, "%p: Popped empty stack\n", list
);
249 struct loc_network
* network
= list
->elements
[--list
->size
];
251 DEBUG(list
->ctx
, "%p: Popping network %p from stack\n", list
, network
);
256 LOC_EXPORT
struct loc_network
* loc_network_list_pop_first(struct loc_network_list
* list
) {
257 // Return nothing when empty
258 if (loc_network_list_empty(list
)) {
259 DEBUG(list
->ctx
, "%p: Popped empty stack\n", list
);
263 struct loc_network
* network
= list
->elements
[0];
265 // Move all elements to the top of the stack
266 for (unsigned int i
= 0; i
< list
->size
- 1; i
++) {
267 list
->elements
[i
] = list
->elements
[i
+1];
270 // The list is shorter now
273 DEBUG(list
->ctx
, "%p: Popping network %p from stack\n", list
, network
);
278 LOC_EXPORT
int loc_network_list_contains(struct loc_network_list
* list
, struct loc_network
* network
) {
281 loc_network_list_find(list
, network
, &found
);
286 LOC_EXPORT
int loc_network_list_merge(
287 struct loc_network_list
* self
, struct loc_network_list
* other
) {
290 for (unsigned int i
= 0; i
< other
->size
; i
++) {
291 r
= loc_network_list_push(self
, other
->elements
[i
]);
299 int loc_network_list_summarize(struct loc_ctx
* ctx
,
300 const struct in6_addr
* first
, const struct in6_addr
* last
, struct loc_network_list
** list
) {
308 DEBUG(ctx
, "Summarizing %s - %s\n", loc_address_str(first
), loc_address_str(last
));
310 const int family1
= loc_address_family(first
);
311 const int family2
= loc_address_family(last
);
313 // Check if address families match
314 if (family1
!= family2
) {
315 ERROR(ctx
, "Address families do not match\n");
320 // Check if the last address is larger than the first address
321 if (loc_address_cmp(first
, last
) >= 0) {
322 ERROR(ctx
, "The first address must be smaller than the last address\n");
327 struct loc_network
* network
= NULL
;
328 struct in6_addr start
= *first
;
330 const int family_bit_length
= loc_address_family_bit_length(family1
);
332 while (loc_address_cmp(&start
, last
) <= 0) {
336 // Find the number of trailing zeroes of the start address
337 if (loc_address_all_zeroes(&start
))
338 bits1
= family_bit_length
;
340 bits1
= loc_address_count_trailing_zero_bits(&start
);
341 if (bits1
> family_bit_length
)
342 bits1
= family_bit_length
;
345 // Subtract the start address from the last address and add one
346 // (i.e. how many addresses are in this network?)
347 r
= loc_address_sub(&num
, last
, &start
);
351 loc_address_increment(&num
);
353 // How many bits do we need to represent this address?
354 int bits2
= loc_address_bit_length(&num
) - 1;
356 // Select the smaller one
357 int bits
= (bits1
> bits2
) ? bits2
: bits1
;
359 printf("prefix = %d, bits1 = %d, bits2 = %d\n", family_bit_length
, bits1
, bits2
);
362 r
= loc_network_new(ctx
, &network
, &start
, family_bit_length
- bits
);
366 DEBUG(ctx
, "Found network %s\n", loc_network_str(network
));
368 // Push network on the list
369 r
= loc_network_list_push(*list
, network
);
371 loc_network_unref(network
);
375 // The next network starts right after this one
376 start
= *loc_network_get_last_address(network
);
378 // If we have reached the end of possible IP addresses, we stop
379 if (loc_address_all_ones(&start
))
382 loc_address_increment(&start
);