1 /* Copyright © 2012 Brandon L Black <blblack@gmail.com>
2 *
3 * This file is part of gdnsd.
4 *
5 * gdnsd-plugin-geoip is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * gdnsd-plugin-geoip is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with gdnsd. If not, see <http://www.gnu.org/licenses/>.
17 *
18 */
19
20 #include <config.h>
21 #include "nlist.h"
22
23 #include "dclists.h"
24
25 #include <gdnsd/alloc.h>
26 #include <gdnsd/log.h>
27 #include <gdnsd/misc.h>
28
29 #include <string.h>
30 #include <stdlib.h>
31
32 #define NLIST_INITSIZE 64
33
34 typedef struct {
35 uint8_t ipv6[16];
36 unsigned mask;
37 unsigned dclist;
38 } net_t;
39
40 struct _nlist {
41 net_t* nets;
42 char* map_name;
43 unsigned alloc;
44 unsigned count;
45 bool normalized;
46 };
47
nlist_new(const char * map_name,const bool pre_norm)48 nlist_t* nlist_new(const char* map_name, const bool pre_norm) {
49 nlist_t* nl = xmalloc(sizeof(nlist_t));
50 nl->nets = xmalloc(sizeof(net_t) * NLIST_INITSIZE);
51 nl->map_name = strdup(map_name);
52 nl->alloc = NLIST_INITSIZE;
53 nl->count = 0;
54 nl->normalized = pre_norm;
55 return nl;
56 }
57
58 // only used for normalization assertions in debug builds...
59 F_UNUSED F_NONNULL
nlist_clone(const nlist_t * nl)60 static nlist_t* nlist_clone(const nlist_t* nl) {
61 nlist_t* nlc = xmalloc(sizeof(nlist_t));
62 nlc->map_name = strdup(nl->map_name);
63 nlc->alloc = nl->alloc;
64 nlc->count = nl->count;
65 nlc->normalized = nl->normalized;
66 nlc->nets = xmalloc(sizeof(net_t) * nlc->alloc);
67 memcpy(nlc->nets, nl->nets, sizeof(net_t) * nlc->count);
68 return nlc;
69 }
70
nlist_debug_dump(const nlist_t * nl)71 void nlist_debug_dump(const nlist_t* nl) {
72 log_debug(" --- nlist debug on %s --- ", nl->map_name);
73 for(unsigned i = 0; i < nl->count; i++)
74 log_debug(" %s/%u -> %u", logf_ipv6(nl->nets[i].ipv6), nl->nets[i].mask, nl->nets[i].dclist);
75 }
76
nlist_destroy(nlist_t * nl)77 void nlist_destroy(nlist_t* nl) {
78 free(nl->map_name);
79 free(nl->nets);
80 free(nl);
81 }
82
83 #ifndef NDEBUG
84 F_NONNULL
assert_clear_mask_bits(uint8_t * ipv6,const unsigned mask)85 static void assert_clear_mask_bits(uint8_t* ipv6, const unsigned mask) {
86 dmn_assert(mask < 129);
87
88 if(likely(mask)) {
89 const unsigned revmask = 128 - mask;
90 const unsigned byte_mask = ~(0xFFu << (revmask & 7)) & 0xFF;
91 unsigned bbyte = 15 - (revmask >> 3);
92
93 dmn_assert(!(ipv6[bbyte] & byte_mask));
94 while(++bbyte < 16)
95 dmn_assert(!ipv6[bbyte]);
96 }
97 else {
98 dmn_assert(!memcmp(ipv6, &ip6_zero, 16));
99 }
100 }
101 #else
102 #define assert_clear_mask_bits(x, y)
103 #endif
104
105 F_NONNULL
clear_mask_bits(const char * map_name,uint8_t * ipv6,const unsigned mask)106 static void clear_mask_bits(const char* map_name, uint8_t* ipv6, const unsigned mask) {
107 dmn_assert(mask < 129);
108
109 bool maskbad = false;
110
111 if(likely(mask)) {
112 const unsigned revmask = 128 - mask;
113 const unsigned byte_mask = ~(0xFFu << (revmask & 7)) & 0xFF;
114 unsigned bbyte = 15 - (revmask >> 3);
115
116 if(ipv6[bbyte] & byte_mask) {
117 maskbad = true;
118 ipv6[bbyte] &= ~byte_mask;
119 }
120
121 while(++bbyte < 16) {
122 if(ipv6[bbyte]) {
123 maskbad = true;
124 ipv6[bbyte] = 0;
125 }
126 }
127 }
128 else if(memcmp(ipv6, &ip6_zero, 16)) {
129 maskbad = true;
130 memset(ipv6, 0, 16);
131 }
132
133 if(maskbad)
134 log_warn("plugin_geoip: map '%s': input network %s/%u had illegal bits beyond mask, which were cleared", map_name, logf_ipv6(ipv6), mask);
135 }
136
137 // Sort an array of net_t. Sort prefers
138 // lowest network number, smallest mask.
139 F_NONNULL F_PURE
net_sorter(const void * a_void,const void * b_void)140 static int net_sorter(const void* a_void, const void* b_void) {
141 const net_t* a = a_void;
142 const net_t* b = b_void;
143 int rv = memcmp(a->ipv6, b->ipv6, 16);
144 if(!rv)
145 rv = (int)a->mask - (int)b->mask;
146 return rv;
147 }
148
149 F_NONNULL F_PURE
masked_net_eq(const uint8_t * v6a,const uint8_t * v6b,const unsigned mask)150 static bool masked_net_eq(const uint8_t* v6a, const uint8_t* v6b, const unsigned mask) {
151 dmn_assert(mask < 128U); // 2x128 would call here w/ 127...
152
153 const unsigned bytes = mask >> 3;
154 dmn_assert(bytes < 16U);
155
156 const unsigned bytemask = (0xFF00u >> (mask & 7)) & 0xFF;
157 return !memcmp(v6a, v6b, bytes)
158 && (v6a[bytes] & bytemask) == (v6b[bytes] & bytemask);
159 }
160
161 F_NONNULL F_PURE
mergeable_nets(const net_t * na,const net_t * nb)162 static bool mergeable_nets(const net_t* na, const net_t* nb) {
163 bool rv = false;
164 if(na->dclist == nb->dclist) {
165 if(na->mask == nb->mask)
166 rv = masked_net_eq(na->ipv6, nb->ipv6, na->mask - 1);
167 else if(na->mask < nb->mask)
168 rv = masked_net_eq(na->ipv6, nb->ipv6, na->mask);
169 }
170 return rv;
171 }
172
nlist_append(nlist_t * nl,const uint8_t * ipv6,const unsigned mask,const unsigned dclist)173 void nlist_append(nlist_t* nl, const uint8_t* ipv6, const unsigned mask, const unsigned dclist) {
174 if(unlikely(nl->count == nl->alloc)) {
175 nl->alloc <<= 1U;
176 nl->nets = xrealloc(nl->nets, sizeof(net_t) * nl->alloc);
177 }
178 net_t* this_net = &nl->nets[nl->count++];
179 memcpy(this_net->ipv6, ipv6, 16U);
180 this_net->mask = mask;
181 this_net->dclist = dclist;
182
183 // In the pre-norm case, we can keep the list in fully-normalized
184 // form as we go by doing this merge op after each append and
185 // keeping the list minimized. What we're catching here is adjacent
186 // networks which share a dclist and mask and are thus mergeable,
187 // and we do so by deleting the most-recently added one and decrementing
188 // the subnet mask of the older one.
189 // Because this is happening back-to-front after each append, there's
190 // no need to create (or later deal with) holes in the array.
191 if(nl->normalized) {
192 assert_clear_mask_bits(this_net->ipv6, mask);
193 unsigned idx = nl->count;
194 while(--idx > 0) {
195 net_t* nb = &nl->nets[idx];
196 net_t* na = &nl->nets[idx - 1];
197 if(mergeable_nets(na, nb)) {
198 if(na->mask == nb->mask)
199 na->mask--;
200 nl->count--;
201 }
202 else {
203 break;
204 }
205 }
206 }
207 // for raw input, just correct any netmask errors as we insert,
208 // as these will screw up later sorting for normalization
209 else {
210 clear_mask_bits(nl->map_name, this_net->ipv6, mask);
211 }
212 }
213
214 F_NONNULL F_PURE
net_eq(const net_t * na,const net_t * nb)215 static bool net_eq(const net_t* na, const net_t* nb) {
216 return na->mask == nb->mask && !memcmp(na->ipv6, nb->ipv6, 16);
217 }
218
219 // do a single pass of forward-normalization
220 // on a sorted nlist, then sort the result.
221 F_NONNULL
nlist_normalize_1pass(nlist_t * nl)222 static bool nlist_normalize_1pass(nlist_t* nl) {
223 dmn_assert(nl->count);
224
225 bool rv = false;
226
227 const unsigned oldcount = nl->count;
228 unsigned newcount = nl->count;
229 unsigned i = 0;
230 while(i < oldcount) {
231 net_t* na = &nl->nets[i];
232 unsigned j = i + 1;
233 while(j < oldcount) {
234 net_t* nb = &nl->nets[j];
235 if(net_eq(na, nb)) { // net+mask match, dclist may or may not match
236 if(na->dclist != nb->dclist)
237 log_warn("plugin_geoip: map '%s' nets: Exact duplicate networks with conflicting dclists at %s/%u", nl->map_name, logf_ipv6(na->ipv6), na->mask);
238 }
239 else if(mergeable_nets(na, nb)) { // dclists match, nets adjacent (masks equal) or subnet-of
240 if(na->mask == nb->mask)
241 na->mask--;
242 }
243 else {
244 break;
245 }
246 nb->mask = 0xFFFF; // illegally-huge, to sort deletes later
247 memset(nb->ipv6, 0xFF, 16); // all-1's, also for sort...
248 newcount--;
249 j++;
250 }
251 i = j;
252 }
253
254 if(newcount != oldcount) { // merges happened above
255 // the "deleted" entries have all-1's IPs and >legal masks, so they
256 // sort to the end...
257 qsort(nl->nets, oldcount, sizeof(net_t), net_sorter);
258
259 // reset the count to ignore the deleted entries at the end
260 nl->count = newcount;
261
262 // signal need for another pass
263 rv = true;
264 }
265
266 return rv;
267 }
268
269 F_NONNULL
nlist_normalize(nlist_t * nl,const bool post_merge)270 static void nlist_normalize(nlist_t* nl, const bool post_merge) {
271 if(nl->count) {
272 // initial sort, unless already sorted by the merge process
273 if(!post_merge)
274 qsort(nl->nets, nl->count, sizeof(net_t), net_sorter);
275
276 // iterate merge+sort passes until no further merges are found
277 while(nlist_normalize_1pass(nl))
278 ; // empty
279
280 // optimize storage space
281 if(nl->count != nl->alloc) {
282 dmn_assert(nl->count < nl->alloc);
283 nl->alloc = nl->count;
284 nl->nets = xrealloc(nl->nets, nl->alloc * sizeof(net_t));
285 }
286 }
287
288 nl->normalized = true;
289 }
290
291 F_NONNULL
nlist_finish(nlist_t * nl)292 void nlist_finish(nlist_t* nl) {
293 if(nl->normalized) {
294 #ifndef NDEBUG
295 // assert normalization in debug builds via clone->normalize->compare
296 nlist_t* nlc = nlist_clone(nl);
297 nlist_normalize(nlc, false);
298 dmn_assert(nlc->count == nl->count);
299 dmn_assert(!memcmp(nlc->nets, nl->nets, sizeof(net_t) * nlc->count));
300 nlist_destroy(nlc);
301 #endif
302 }
303 else {
304 nlist_normalize(nl, false);
305 }
306 }
307
308 F_NONNULL F_PURE
net_subnet_of(const net_t * sub,const net_t * super)309 static bool net_subnet_of(const net_t* sub, const net_t* super) {
310 dmn_assert(sub->mask < 129);
311 dmn_assert(super->mask < 129);
312
313 bool rv = false;
314 if(sub->mask >= super->mask) {
315 const unsigned wbyte = (super->mask >> 3);
316 const unsigned byte_mask = (0xFFu << (8u - (super->mask & 7))) & 0xFF;
317 if(!memcmp(sub->ipv6, super->ipv6, wbyte)) {
318 if(wbyte == 16 || (super->ipv6[wbyte] & byte_mask) == (sub->ipv6[wbyte] & byte_mask))
319 rv = true;
320 }
321 }
322
323 return rv;
324 }
325
326 F_NONNULL
nlist_merge(const nlist_t * nl_a,const nlist_t * nl_b)327 static nlist_t* nlist_merge(const nlist_t* nl_a, const nlist_t* nl_b) {
328 dmn_assert(nl_a->normalized);
329 dmn_assert(nl_b->normalized);
330
331 nlist_t* merged = nlist_new(nl_a->map_name, false);
332
333 const net_t* n_a = &nl_a->nets[0];
334 const net_t* n_b = &nl_b->nets[0];
335 const net_t* end_a = &nl_a->nets[nl_a->count];
336 const net_t* end_b = &nl_b->nets[nl_b->count];
337
338 while(n_a < end_a && n_b < end_b) {
339 if(net_sorter(n_a, n_b) < 0) {
340 // n_a < n_b
341 // therefore n_a is a supernet of the next n_b,
342 // or an unrelated predecessor, copy it...
343 nlist_append(merged, n_a->ipv6, n_a->mask, n_a->dclist);
344 n_a++;
345 }
346 else { // n_a >= n_b
347 nlist_append(merged, n_b->ipv6, n_b->mask, n_b->dclist);
348 // this is where we skip networks from the first list
349 // that are effectively masked out by entries in the second
350 while(n_a < end_a && net_subnet_of(n_a, n_b))
351 n_a++;
352 n_b++;
353 }
354 }
355
356 // Usually only one of the lists will have remaining entries,
357 // which should be copyable. Rarely, both will already be finished.
358 while(n_b < end_b) {
359 nlist_append(merged, n_b->ipv6, n_b->mask, n_b->dclist);
360 n_b++;
361 }
362 while(n_a < end_a) {
363 nlist_append(merged, n_a->ipv6, n_a->mask, n_a->dclist);
364 n_a++;
365 }
366
367 nlist_normalize(merged, true);
368 return merged;
369 }
370
371 F_NONNULL
372 static unsigned nxt_rec(const net_t** nl, const net_t* const nl_end, ntree_t* nt, net_t tree_net);
373
374 F_NONNULL
nxt_rec_dir(const net_t ** nlp,const net_t * const nl_end,ntree_t * nt,net_t tree_net,const unsigned nt_idx,const bool direction)375 static void nxt_rec_dir(const net_t** nlp, const net_t* const nl_end, ntree_t* nt, net_t tree_net, const unsigned nt_idx, const bool direction) {
376 dmn_assert(tree_net.mask < 129 && tree_net.mask > 0);
377
378 const net_t* nl = *nlp;
379 unsigned cnode;
380
381 // If items remain in the list, and the next list item
382 // is a subnet of (including exact match for) the current
383 // ntree node...
384 if(nl < nl_end && net_subnet_of(nl, &tree_net)) {
385 // exact match, consume...
386 if(tree_net.mask == nl->mask) {
387 (*nlp)++; // consume *nlp and move to next
388 // need to pre-check for a deeper subnet next in the list.
389 // We use the consumed entry as the new default and keep recursing
390 // if deeper subnets exist. If they don't, we assign and end recursion...
391 const net_t* nl_next = *nlp;
392 if(nl_next < nl_end && net_subnet_of(nl_next, nl)) {
393 tree_net.dclist = nl->dclist;
394 cnode = nxt_rec(nlp, nl_end, nt, tree_net);
395 }
396 else {
397 cnode = NN_SET_DCLIST(nl->dclist);
398 }
399 }
400 // Not an exact match, so just keep recursing towards such a match...
401 else {
402 cnode = nxt_rec(nlp, nl_end, nt, tree_net);
403 }
404 }
405 // list item isn't a subnet of the current tree node, and due to our
406 // normalization that means there are no such list items remaining,
407 // so terminate the recursion with the current default dclist.
408 else {
409 cnode = NN_SET_DCLIST(tree_net.dclist);
410 }
411
412 // store direct or recursed result. Note we have to wait until
413 // here to deref nt->store[nt_idx] because recursion could
414 // re-allocate nt->store[] during nxt_rec()'s ntree_add_node() call.
415 if(direction)
416 nt->store[nt_idx].one = cnode;
417 else
418 nt->store[nt_idx].zero = cnode;
419 }
420
421 F_NONNULL
nxt_rec(const net_t ** nl,const net_t * const nl_end,ntree_t * nt,net_t tree_net)422 static unsigned nxt_rec(const net_t** nl, const net_t* const nl_end, ntree_t* nt, net_t tree_net) {
423 dmn_assert(tree_net.mask < 128);
424 tree_net.mask++; // now mask for zero/one stubs
425
426 const unsigned nt_idx = ntree_add_node(nt);
427 nxt_rec_dir(nl, nl_end, nt, tree_net, nt_idx, false);
428 SETBIT_v6(tree_net.ipv6, tree_net.mask - 1);
429 nxt_rec_dir(nl, nl_end, nt, tree_net, nt_idx, true);
430
431 unsigned rv = nt_idx;
432
433 // catch missed optimizations during final translation
434 if(unlikely(nt->store[nt_idx].zero == nt->store[nt_idx].one) && likely(nt_idx > 0)) {
435 nt->count--; // delete the just-added node
436 rv = nt->store[nt_idx].zero;
437 }
438
439 return rv;
440 }
441
nlist_xlate_tree(const nlist_t * nl)442 ntree_t* nlist_xlate_tree(const nlist_t* nl) {
443 dmn_assert(nl->normalized);
444
445 ntree_t* nt = ntree_new();
446 const net_t* nlnet = &nl->nets[0];
447 const net_t* const nlnet_end = &nl->nets[nl->count];
448 net_t tree_net = {
449 .ipv6 = { 0 },
450 .mask = 0,
451 .dclist = 0
452 };
453
454 // Special-case: if a list entry for ::/0 exists, it will
455 // be first in the list, and it needs to be skipped
456 // over (with its dclist as the new default) before
457 // recursing (because ::/0 is the first node of the
458 // tree itself).
459 if(nl->count && !nl->nets[0].mask) {
460 tree_net.dclist = nl->nets[0].dclist;
461 nlnet++;
462 }
463
464 // recursively build the tree from the list
465 nxt_rec(&nlnet, nlnet_end, nt, tree_net);
466
467 // assert that the whole list was consumed
468 dmn_assert(nlnet == nlnet_end);
469
470 // finalize the tree
471 ntree_finish(nt);
472
473 // make sure all our logic worked out sanely
474 ntree_assert_optimal(nt);
475
476 return nt;
477 }
478
nlist_merge2_tree(const nlist_t * nl_a,const nlist_t * nl_b)479 ntree_t* nlist_merge2_tree(const nlist_t* nl_a, const nlist_t* nl_b) {
480 nlist_t* merged = nlist_merge(nl_a, nl_b);
481 ntree_t* rv = nlist_xlate_tree(merged);
482 nlist_destroy(merged);
483 return rv;
484 }
485
nlist_merge3_tree(const nlist_t * nl_a,const nlist_t * nl_b,const nlist_t * nl_c)486 ntree_t* nlist_merge3_tree(const nlist_t* nl_a, const nlist_t* nl_b, const nlist_t* nl_c) {
487 nlist_t* merge1 = nlist_merge(nl_a, nl_b);
488 nlist_t* merge2 = nlist_merge(merge1, nl_c);
489 nlist_destroy(merge1);
490 ntree_t* rv = nlist_xlate_tree(merge2);
491 nlist_destroy(merge2);
492 return rv;
493 }
494