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