1 /*
2  * sc_wartsfilter
3  *
4  * $Id: sc_wartsfilter.c,v 1.6 2021/08/22 08:11:53 mjl Exp $
5  *
6  *        Matthew Luckie
7  *        mjl@luckie.org.nz
8  *
9  * Copyright (C) 2019-2020 The University of Waikato
10  *
11  * This program is free software; you can redistribute it and/or modify
12  * it under the terms of the GNU General Public License as published by
13  * the Free Software Foundation, version 2.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
23  *
24  */
25 
26 #ifdef HAVE_CONFIG_H
27 #include "config.h"
28 #endif
29 #include "internal.h"
30 
31 #include "scamper_file.h"
32 #include "scamper_addr.h"
33 #include "scamper_list.h"
34 #include "dealias/scamper_dealias.h"
35 #include "ping/scamper_ping.h"
36 #include "tbit/scamper_tbit.h"
37 #include "trace/scamper_trace.h"
38 #include "tracelb/scamper_tracelb.h"
39 #include "mjl_list.h"
40 #include "mjl_prefixtree.h"
41 #include "utils.h"
42 
43 #define OPT_OUTFILE 0x0001
44 #define OPT_INFILE  0x0002
45 #define OPT_ADDR    0x0004
46 #define OPT_TYPE    0x0008
47 
48 static scamper_file_t        *infile   = NULL;
49 static scamper_file_t        *outfile  = NULL;
50 static prefixtree_t          *addr_pt4 = NULL;
51 static prefixtree_t          *addr_pt6 = NULL;
52 static int                    addrc    = 0;
53 static scamper_file_filter_t *filter   = NULL;
54 static int                    check_hops = 0;
55 
usage(uint32_t opts)56 static void usage(uint32_t opts)
57 {
58   fprintf(stderr,
59           "usage: sc_wartsfilter [-a address] [-i infile] [-o outfile]\n"
60 	  "                      [-O options] [-t type]\n");
61   return;
62 }
63 
check_options(int argc,char * argv[])64 static int check_options(int argc, char *argv[])
65 {
66   char *opt_infile = NULL, *opt_outfile = NULL;
67   uint32_t type_mask = 0;
68   slist_t *addrs = NULL;
69   slist_node_t *sn;
70   char *opts = "a:i:o:O:t:?";
71   char *addr, *ptr, *dup = NULL;
72   prefix4_t *pfx4 = NULL;
73   prefix6_t *pfx6 = NULL;
74   struct in_addr in4;
75   struct in6_addr in6;
76   uint16_t types_def[] = {
77     SCAMPER_FILE_OBJ_DEALIAS,
78     SCAMPER_FILE_OBJ_PING,
79     SCAMPER_FILE_OBJ_TBIT,
80     SCAMPER_FILE_OBJ_TRACE,
81     SCAMPER_FILE_OBJ_TRACELB,
82   };
83   uint16_t *types = NULL;
84   int i, typec;
85   long lo;
86   int ch;
87 
88   if((addrs = slist_alloc()) == NULL)
89     {
90       fprintf(stderr, "%s: could not alloc addrs\n", __func__);
91       goto err;
92     }
93 
94   while((ch = getopt(argc, argv, opts)) != -1)
95     {
96       switch(ch)
97         {
98         case 'a':
99 	  if((dup = strdup(optarg)) == NULL ||
100 	     slist_tail_push(addrs, dup) == NULL)
101 	    {
102 	      fprintf(stderr, "%s: error handling -a\n", __func__);
103 	      goto err;
104 	    }
105 	  dup = NULL;
106 	  break;
107 
108 	case 'i':
109 	  opt_infile = optarg;
110 	  break;
111 
112 	case 'o':
113 	  opt_outfile = optarg;
114 	  break;
115 
116 	case 'O':
117 	  if(strcasecmp(optarg, "check-hops") == 0)
118 	    check_hops = 1;
119 	  else
120 	    {
121 	      fprintf(stderr, "%s: unknown -O %s\n", __func__, optarg);
122 	      goto err;
123 	    }
124 	  break;
125 
126 	case 't':
127 	  if(strcasecmp(optarg, "dealias") == 0)
128 	    type_mask |= (1 << SCAMPER_FILE_OBJ_DEALIAS);
129 	  else if(strcasecmp(optarg, "ping") == 0)
130 	    type_mask |= (1 << SCAMPER_FILE_OBJ_PING);
131 	  else if(strcasecmp(optarg, "tbit") == 0)
132 	    type_mask |= (1 << SCAMPER_FILE_OBJ_TBIT);
133 	  else if(strcasecmp(optarg, "trace") == 0)
134 	    type_mask |= (1 << SCAMPER_FILE_OBJ_TRACE);
135 	  else if(strcasecmp(optarg, "tracelb") == 0)
136 	    type_mask |= (1 << SCAMPER_FILE_OBJ_TRACELB);
137 	  else
138 	    {
139 	      usage(0);
140 	      goto err;
141 	    }
142 	  break;
143 
144 	case '?':
145           usage(0xffffffff);
146 	  goto err;
147 
148         default:
149           usage(0);
150 	  goto err;
151 	}
152     }
153 
154   /* make sure there is some filter */
155   if((addrc = slist_count(addrs)) == 0 && type_mask == 0)
156     {
157       usage(OPT_ADDR | OPT_TYPE);
158       goto err;
159     }
160 
161   /* go through the filter types */
162   if(type_mask == 0)
163     {
164       typec = sizeof(types_def) / sizeof(uint16_t);
165       if((filter = scamper_file_filter_alloc(types_def, typec)) == NULL)
166 	{
167 	  fprintf(stderr, "%s: could not alloc default filter\n", __func__);
168 	  goto err;
169 	}
170     }
171   else
172     {
173       typec = countbits32(type_mask);
174       if((types = malloc(sizeof(uint16_t) * typec)) == NULL)
175 	{
176 	  fprintf(stderr, "%s: could not malloc %d types\n", __func__, typec);
177 	  goto err;
178 	}
179       i = 0;
180       if(type_mask & (1 << SCAMPER_FILE_OBJ_DEALIAS))
181 	types[i++] = SCAMPER_FILE_OBJ_DEALIAS;
182       if(type_mask & (1 << SCAMPER_FILE_OBJ_PING))
183 	types[i++] = SCAMPER_FILE_OBJ_PING;
184       if(type_mask & (1 << SCAMPER_FILE_OBJ_TBIT))
185 	types[i++] = SCAMPER_FILE_OBJ_TBIT;
186       if(type_mask & (1 << SCAMPER_FILE_OBJ_TRACE))
187 	types[i++] = SCAMPER_FILE_OBJ_TRACE;
188       if(type_mask & (1 << SCAMPER_FILE_OBJ_TRACELB))
189 	types[i++] = SCAMPER_FILE_OBJ_TRACELB;
190       assert(i == typec);
191       if((filter = scamper_file_filter_alloc(types, typec)) == NULL)
192 	{
193 	  fprintf(stderr, "%s: could not alloc filter\n", __func__);
194 	  goto err;
195 	}
196       free(types); types = NULL;
197     }
198 
199   /* go through the list of addresses */
200   for(sn=slist_head_node(addrs); sn != NULL; sn=slist_node_next(sn))
201     {
202       addr = slist_node_item(sn);
203 
204       /* if address is not a prefix, install a specific /32 or /128 */
205       if((ptr = string_firstof_char(addr, '/')) == NULL)
206 	{
207 	  if(inet_pton(AF_INET, addr, &in4) == 1)
208 	    {
209 	      if((pfx4 = prefix4_alloc(&in4, 32, NULL)) == NULL ||
210 		 prefixtree_insert4(addr_pt4, pfx4) == NULL)
211 		{
212 		  fprintf(stderr, "%s: could not alloc prefix for %s\n",
213 			  __func__, addr);
214 		  goto err;
215 		}
216 	    }
217 	  else if(inet_pton(AF_INET6, addr, &in6) == 1)
218 	    {
219 	      if((pfx6 = prefix6_alloc(&in6, 128, NULL)) == NULL ||
220 		 prefixtree_insert6(addr_pt6, pfx6) == NULL)
221 		{
222 		  fprintf(stderr, "%s: could not alloc prefix for %s\n",
223 			  __func__, addr);
224 		  goto err;
225 		}
226 	    }
227 	  else
228 	    {
229 	      fprintf(stderr, "%s: could not parse addr %s\n", __func__, addr);
230 	      goto err;
231 	    }
232 	  continue;
233 	}
234 
235       *ptr = '\0'; ptr++;
236       if(string_tolong(ptr, &lo) != 0 || lo < 1)
237 	{
238 	  fprintf(stderr, "%s: invalid prefix length %s\n", __func__, ptr);
239 	  goto err;
240 	}
241       if(inet_pton(AF_INET, addr, &in4) == 1)
242 	{
243 	  if(lo > 32)
244 	    {
245 	      fprintf(stderr, "%s: invalid IPv4 prefix length %ld\n",
246 		      __func__, lo);
247 	      goto err;
248 	    }
249 	  if((pfx4 = prefix4_alloc(&in4, lo, NULL)) == NULL)
250 	    {
251 	      fprintf(stderr, "%s: could not alloc IPv4 prefix\n", __func__);
252 	      goto err;
253 	    }
254 	  pfx4->ptr = pfx4;
255 	  if(prefixtree_insert4(addr_pt4, pfx4) == NULL)
256 	    {
257 	      fprintf(stderr, "%s: could not insert IPv4 prefix\n", __func__);
258 	      goto err;
259 	    }
260 	}
261       else if(inet_pton(AF_INET6, addr, &in6) == 1)
262 	{
263 	  if(lo > 128)
264 	    {
265 	      fprintf(stderr, "%s: invalid IPv6 prefix length %ld\n",
266 		      __func__, lo);
267 	      goto err;
268 	    }
269 	  if((pfx6 = prefix6_alloc(&in6, lo, NULL)) == NULL)
270 	    {
271 	      fprintf(stderr, "%s: could not alloc IPv6 prefix\n", __func__);
272 	      goto err;
273 	    }
274 	  pfx6->ptr = pfx6;
275 	  if(prefixtree_insert6(addr_pt6, pfx6) == NULL)
276 	    {
277 	      fprintf(stderr, "%s: could not insert IPv6 prefix\n", __func__);
278 	      goto err;
279 	    }
280 	}
281       else
282 	{
283 	  fprintf(stderr, "%s: could not parse prefix %s/%s\n",
284 		  __func__, addr, ptr);
285 	  goto err;
286 	}
287     }
288   slist_free_cb(addrs, free); addrs = NULL;
289 
290   /* determine where to read the warts file */
291   if(opt_infile == NULL)
292     {
293       if((infile = scamper_file_openfd(STDIN_FILENO,"-",'r',"warts")) == NULL)
294 	{
295 	  fprintf(stderr, "could not open stdin\n");
296 	  goto err;
297 	}
298     }
299   else
300     {
301       if((infile = scamper_file_open(opt_infile, 'r', "warts")) == NULL)
302 	{
303 	  fprintf(stderr, "could not open %s\n", opt_infile);
304 	  goto err;
305 	}
306     }
307 
308   /* determine where to write the filtered records */
309   if(opt_outfile == NULL || string_isdash(opt_outfile) != 0)
310     {
311       /* writing to stdout; don't dump a binary structure to a tty. */
312       if(isatty(STDOUT_FILENO) != 0)
313         {
314           fprintf(stderr, "not going to dump warts to a tty\n");
315 	  goto err;
316         }
317       if((outfile = scamper_file_openfd(STDOUT_FILENO,"-",'w',"warts")) == NULL)
318         {
319           fprintf(stderr, "could not open stdout\n");
320 	  goto err;
321         }
322     }
323   else
324     {
325       if((outfile = scamper_file_open(opt_outfile, 'w', "warts")) == NULL)
326         {
327           usage(OPT_OUTFILE);
328 	  goto err;
329         }
330     }
331 
332   return 0;
333 
334  err:
335   if(dup != NULL) free(dup);
336   if(types != NULL) free(types);
337   if(addrs != NULL) slist_free_cb(addrs, free);
338   return -1;
339 }
340 
addr_matched(scamper_addr_t * addr)341 static int addr_matched(scamper_addr_t *addr)
342 {
343   if(SCAMPER_ADDR_TYPE_IS_IPV4(addr))
344     {
345       if(prefixtree_find_ip4(addr_pt4, addr->addr) == NULL)
346 	return 0;
347     }
348   else if(SCAMPER_ADDR_TYPE_IS_IPV6(addr))
349     {
350       if(prefixtree_find_ip6(addr_pt6, addr->addr) == NULL)
351 	return 0;
352     }
353   else return 0;
354   return 1;
355 }
356 
process_dealias(scamper_dealias_t * dealias)357 static void process_dealias(scamper_dealias_t *dealias)
358 {
359   scamper_dealias_mercator_t *mc;
360   scamper_dealias_ally_t *ally;
361   scamper_dealias_radargun_t *rg;
362   scamper_dealias_prefixscan_t *pfs;
363   uint32_t i;
364 
365   if(addrc > 0)
366     {
367       if(SCAMPER_DEALIAS_METHOD_IS_MERCATOR(dealias))
368 	{
369 	  mc = dealias->data;
370 	  if(addr_matched(mc->probedef.dst) == 0)
371 	    goto done;
372 	}
373       else if(SCAMPER_DEALIAS_METHOD_IS_ALLY(dealias))
374 	{
375 	  ally = dealias->data;
376 	  if(addr_matched(ally->probedefs[0].dst) == 0 &&
377 	     addr_matched(ally->probedefs[1].dst) == 0)
378 	    goto done;
379 	}
380       else if(SCAMPER_DEALIAS_METHOD_IS_RADARGUN(dealias))
381 	{
382 	  rg = dealias->data;
383 	  for(i=0; i<rg->probedefc; i++)
384 	    if(addr_matched(rg->probedefs[i].dst) != 0)
385 	      break;
386 	  if(i == rg->probedefc)
387 	    goto done;
388 	}
389       else if(SCAMPER_DEALIAS_METHOD_IS_PREFIXSCAN(dealias))
390 	{
391 	  pfs = dealias->data;
392 	  for(i=0; i<pfs->probedefc; i++)
393 	    if(addr_matched(pfs->probedefs[i].dst) != 0)
394 	      break;
395 	  if(i == pfs->probedefc)
396 	    goto done;
397 	}
398       else goto done;
399     }
400   scamper_file_write_dealias(outfile, dealias);
401 
402  done:
403   scamper_dealias_free(dealias);
404   return;
405 }
406 
process_ping(scamper_ping_t * ping)407 static void process_ping(scamper_ping_t *ping)
408 {
409   if(addrc > 0 && addr_matched(ping->dst) == 0)
410     goto done;
411   scamper_file_write_ping(outfile, ping);
412 
413  done:
414   scamper_ping_free(ping);
415   return;
416 }
417 
process_tbit(scamper_tbit_t * tbit)418 static void process_tbit(scamper_tbit_t *tbit)
419 {
420   if(addrc > 0 && addr_matched(tbit->dst) == 0)
421     goto done;
422   scamper_file_write_tbit(outfile, tbit);
423 
424  done:
425   scamper_tbit_free(tbit);
426   return;
427 }
428 
process_trace(scamper_trace_t * trace)429 static void process_trace(scamper_trace_t *trace)
430 {
431   scamper_trace_hop_t *hop;
432   uint16_t i;
433 
434   if(addrc == 0)
435     {
436       scamper_file_write_trace(outfile, trace);
437       goto done;
438     }
439   else if(addr_matched(trace->dst) != 0)
440     {
441       scamper_file_write_trace(outfile, trace);
442       goto done;
443     }
444   else if(check_hops != 0)
445     {
446       for(i=0; i<trace->hop_count; i++)
447 	{
448 	  for(hop=trace->hops[i]; hop != NULL; hop=hop->hop_next)
449 	    {
450 	      if(addr_matched(hop->hop_addr) != 0)
451 		{
452 		  scamper_file_write_trace(outfile, trace);
453 		  goto done;
454 		}
455 	    }
456 	}
457     }
458 
459  done:
460   scamper_trace_free(trace);
461   return;
462 }
463 
process_tracelb(scamper_tracelb_t * tracelb)464 static int process_tracelb(scamper_tracelb_t *tracelb)
465 {
466   scamper_tracelb_node_t *node;
467   scamper_tracelb_link_t *link;
468   scamper_tracelb_probeset_t *set;
469   scamper_tracelb_probe_t *probe;
470   scamper_tracelb_reply_t *reply;
471   uint32_t i, j, k, l, m;
472 
473   if(addrc == 0)
474     {
475       scamper_file_write_tracelb(outfile, tracelb);
476       goto done;
477     }
478   else if(addr_matched(tracelb->dst) != 0)
479     {
480       scamper_file_write_tracelb(outfile, tracelb);
481       goto done;
482     }
483   else if(check_hops != 0)
484     {
485       for(i=0; i<tracelb->nodec; i++)
486 	{
487 	  node = tracelb->nodes[i];
488 	  if(node->addr != NULL && addr_matched(node->addr) != 0)
489 	    {
490 	      scamper_file_write_tracelb(outfile, tracelb);
491 	      goto done;
492 	    }
493 	  for(j=0; j<node->linkc; j++)
494 	    {
495 	      link = node->links[j];
496 	      if(link->to != NULL && addr_matched(link->to->addr) != 0)
497 		{
498 		  scamper_file_write_tracelb(outfile, tracelb);
499 		  goto done;
500 		}
501 	      for(k=0; k<link->hopc-1; k++)
502 		{
503 		  set = link->sets[k];
504 		  for(l=0; l<set->probec; l++)
505 		    {
506 		      probe = set->probes[l];
507 		      for(m=0; m<probe->rxc; m++)
508 			{
509 			  reply = probe->rxs[m];
510 			  if(addr_matched(reply->reply_from) != 0)
511 			    {
512 			      scamper_file_write_tracelb(outfile, tracelb);
513 			      goto done;
514 			    }
515 			}
516 		    }
517 		}
518 	    }
519 	}
520     }
521 
522  done:
523   scamper_tracelb_free(tracelb);
524   return 0;
525 }
526 
cleanup(void)527 static void cleanup(void)
528 {
529   if(infile != NULL)
530     {
531       scamper_file_close(infile);
532       infile = NULL;
533     }
534 
535   if(filter != NULL)
536     {
537       scamper_file_filter_free(filter);
538       filter = NULL;
539     }
540 
541   if(outfile != NULL)
542     {
543       scamper_file_close(outfile);
544       outfile = NULL;
545     }
546 
547   if(addr_pt4 != NULL)
548     {
549       prefixtree_free_cb(addr_pt4, (prefix_free_t)prefix4_free);
550       addr_pt4 = NULL;
551     }
552 
553   if(addr_pt6 != NULL)
554     {
555       prefixtree_free_cb(addr_pt6, (prefix_free_t)prefix6_free);
556       addr_pt6 = NULL;
557     }
558 
559   return;
560 }
561 
main(int argc,char * argv[])562 int main(int argc, char *argv[])
563 {
564   uint16_t type;
565   void *data;
566 
567 #ifdef DMALLOC
568   free(malloc(1));
569 #endif
570 
571   atexit(cleanup);
572 
573   if((addr_pt4 = prefixtree_alloc(AF_INET)) == NULL ||
574      (addr_pt6 = prefixtree_alloc(AF_INET6)) == NULL)
575     {
576       fprintf(stderr, "%s: could not alloc prefixtrees\n", __func__);
577       goto err;
578     }
579 
580   if(check_options(argc, argv) != 0)
581     goto err;
582 
583   while(scamper_file_read(infile, filter, &type, (void *)&data) == 0)
584     {
585       if(data == NULL)
586 	break; /* EOF */
587 
588       if(type == SCAMPER_FILE_OBJ_DEALIAS)
589 	process_dealias(data);
590       else if(type == SCAMPER_FILE_OBJ_PING)
591 	process_ping(data);
592       else if(type == SCAMPER_FILE_OBJ_TRACE)
593 	process_trace(data);
594       else if(type == SCAMPER_FILE_OBJ_TBIT)
595 	process_tbit(data);
596       else if(type == SCAMPER_FILE_OBJ_TRACELB)
597 	process_tracelb(data);
598     }
599 
600   return 0;
601 
602  err:
603   return -1;
604 }
605