1 /*
2  * scamper_do_tracelb.c
3  *
4  * $Id: scamper_tracelb_do.c,v 1.283 2020/06/12 21:11:55 mjl Exp $
5  *
6  * Copyright (C) 2008-2011 The University of Waikato
7  * Copyright (C) 2012      The Regents of the University of California
8  * Copyright (C) 2016-2020 Matthew Luckie
9  * Author: Matthew Luckie
10  *
11  * MDA traceroute technique authored by
12  * Brice Augustin, Timur Friedman, Renata Teixeira; "Measuring Load-balanced
13  *  Paths in the Internet", in Proc. Internet Measurement Conference 2007.
14  *
15  * This program is free software; you can redistribute it and/or modify
16  * it under the terms of the GNU General Public License as published by
17  * the Free Software Foundation, version 2.
18  *
19  * This program is distributed in the hope that it will be useful,
20  * but WITHOUT ANY WARRANTY; without even the implied warranty of
21  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
22  * GNU General Public License for more details.
23  *
24  * You should have received a copy of the GNU General Public License
25  * along with this program; if not, write to the Free Software
26  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
27  *
28  */
29 
30 #ifdef HAVE_CONFIG_H
31 #include "config.h"
32 #endif
33 #include "internal.h"
34 
35 #include "scamper.h"
36 #include "scamper_addr.h"
37 #include "scamper_list.h"
38 #include "scamper_icmpext.h"
39 #include "scamper_tracelb.h"
40 #include "scamper_task.h"
41 #include "scamper_icmp_resp.h"
42 #include "scamper_dl.h"
43 #include "scamper_fds.h"
44 #include "scamper_dlhdr.h"
45 #include "scamper_rtsock.h"
46 #include "scamper_probe.h"
47 #include "scamper_getsrc.h"
48 #include "scamper_udp4.h"
49 #include "scamper_udp6.h"
50 #include "scamper_icmp4.h"
51 #include "scamper_icmp6.h"
52 #include "scamper_queue.h"
53 #include "scamper_file.h"
54 #include "scamper_options.h"
55 #include "scamper_debug.h"
56 #include "host/scamper_host_do.h"
57 #include "scamper_tracelb_do.h"
58 #include "utils.h"
59 #include "mjl_list.h"
60 #include "mjl_heap.h"
61 #include "mjl_splaytree.h"
62 
63 #define SCAMPER_DO_TRACELB_ATTEMPTS_MIN    1
64 #define SCAMPER_DO_TRACELB_ATTEMPTS_DEF    2
65 #define SCAMPER_DO_TRACELB_ATTEMPTS_MAX    5
66 
67 #define SCAMPER_DO_TRACELB_PORT_MIN        1
68 #define SCAMPER_DO_TRACELB_PORT_MAX        65535
69 
70 #define SCAMPER_DO_TRACELB_DPORT_DEF       (32768+666+1)
71 
72 #define SCAMPER_DO_TRACELB_FIRSTHOP_MIN    1
73 #define SCAMPER_DO_TRACELB_FIRSTHOP_DEF    1
74 #define SCAMPER_DO_TRACELB_FIRSTHOP_MAX    254
75 
76 #define SCAMPER_DO_TRACELB_GAPLIMIT_MIN    1
77 #define SCAMPER_DO_TRACELB_GAPLIMIT_DEF    3
78 #define SCAMPER_DO_TRACELB_GAPLIMIT_MAX    5
79 
80 #define SCAMPER_DO_TRACELB_PROBECMAX_MIN   50
81 #define SCAMPER_DO_TRACELB_PROBECMAX_DEF   3000
82 #define SCAMPER_DO_TRACELB_PROBECMAX_MAX   65535
83 
84 #define SCAMPER_DO_TRACELB_TOS_MIN         0
85 #define SCAMPER_DO_TRACELB_TOS_DEF         0
86 #define SCAMPER_DO_TRACELB_TOS_MAX         255
87 
88 #define SCAMPER_DO_TRACELB_WAITPROBE_MIN   15
89 #define SCAMPER_DO_TRACELB_WAITPROBE_DEF   25
90 #define SCAMPER_DO_TRACELB_WAITPROBE_MAX   200
91 
92 #define SCAMPER_DO_TRACELB_WAITTIMEOUT_MIN 1
93 #define SCAMPER_DO_TRACELB_WAITTIMEOUT_DEF 5
94 #define SCAMPER_DO_TRACELB_WAITTIMEOUT_MAX 10
95 
96 static const uint8_t MODE_RTSOCK     = 0; /* need to determine outgoing if */
97 static const uint8_t MODE_DLHDR      = 1; /* need to determine datalink hdr */
98 static const uint8_t MODE_FIRSTADDR  = 2; /* probing for the first address */
99 static const uint8_t MODE_FIRSTHOP   = 3; /* probing for the first hop */
100 static const uint8_t MODE_HOPPROBE   = 4; /* probing a hop for lb paths */
101 static const uint8_t MODE_PERPACKET  = 5; /* determine if router lb per pkt */
102 static const uint8_t MODE_BRINGFWD   = 6; /* need more flowids for hop-probe */
103 static const uint8_t MODE_BRINGFWD0  = 7; /* when first addr branches */
104 static const uint8_t MODE_CLUMP      = 8; /* clump of nodes */
105 
106 /* forward declare some structs that are used to keep state */
107 typedef struct tracelb_link  tracelb_link_t;
108 typedef struct tracelb_path  tracelb_path_t;
109 typedef struct tracelb_probe tracelb_probe_t;
110 
111 /*
112  * tracelb_link
113  *
114  * keep track of links discovered, the probes that discovered them, and
115  * where in the path they are.
116  */
117 struct tracelb_link
118 {
119   scamper_tracelb_link_t  *link;
120   slist_t                 *flowids;
121   struct tracelb_path     *path;
122 };
123 
124 /*
125  * tracelb_path
126  *
127  * keep track of unique paths to probe and the probes which find them.
128  *
129  */
130 struct tracelb_path
131 {
132   /*
133    * these fields maintain a graph structure
134    *
135    * the 'back' fields keep track of the paths that lead up to this path.
136    * the 'fwd' fields keep track of the paths that follow on from this path.
137    * the 'link' fields keep track of the links discovered in this path segment.
138    */
139   tracelb_path_t         **back;
140   int                      backc;
141   tracelb_link_t         **links;
142   int                      linkc;
143   tracelb_path_t         **fwd;
144   int                      fwdc;
145 
146   /*
147    * these fields maintain other useful items of state
148    *
149    * the 'visited' field says whether or not the path has been visited in DFS
150    * the 'distance' field records the maximum distance from the root
151    */
152   int                      visited;
153   int                      distance;
154 };
155 
156 /*
157  * tracelb_bringfwd
158  *
159  * this structure keeps track at the number of times a particular subpath has
160  * been probed when attempting to bring a particular flowid forward through a
161  * path so it can be used to probe a latter portion of the path.
162  *
163  * when k reaches the corresponding value in the confidence table, the attempt
164  * to bring a flowid forward for the path ceases.
165  */
166 typedef struct tracelb_bringfwd
167 {
168   tracelb_path_t          *path;
169   int                      k;
170 } tracelb_bringfwd_t;
171 
172 /*
173  * tracelb_flowid
174  *
175  * keep track of the flowid values known to be useful in probing a path.
176  */
177 typedef struct tracelb_flowid
178 {
179   uint16_t                 id;
180   uint8_t                  ttl;
181 } tracelb_flowid_t;
182 
183 
184 typedef struct tracelb_newnode
185 {
186   scamper_tracelb_node_t  *node;
187   scamper_tracelb_probe_t *probe;
188 } tracelb_newnode_t;
189 
190 /*
191  * tracelb_branch
192  *
193  * state to keep while probing a branch in the path.
194  */
195 typedef struct tracelb_branch
196 {
197   tracelb_path_t          *path;         /* path being probed forward */
198   struct timeval           last_tx;      /* time last probe sent */
199   struct timeval           next_tx;      /* when to next probe */
200   uint8_t                  mode;         /* mode this branch is in */
201   int                      k;            /* # of probes replied to */
202   int                      l;            /* # of lost probes */
203   int                      n;            /* # of loadbal paths to rule out */
204   tracelb_bringfwd_t     **bringfwd;     /* paths leading to this path */
205   int                      bringfwdc;    /* # of paths in subset */
206   int                      bringfwd0;    /* number of probes in this state */
207   heap_node_t             *heapnode;     /* corresponding node in heap */
208   tracelb_newnode_t      **newnodes;
209   int                      newnodec;
210   tracelb_probe_t        **probes;
211   int                      probec;
212 } tracelb_branch_t;
213 
214 /*
215  * tracelb_probe
216  *
217  * keep track of probes and the paths they probe.  the tracelb_state structure
218  * holds an array of these probe structures indexed by probe id.
219  */
220 struct tracelb_probe
221 {
222   tracelb_link_t          *link;     /* the link found one ttl earlier */
223   tracelb_branch_t        *branch;   /* which branch is being probed */
224   scamper_tracelb_probe_t *probe;    /* probe record to be held as data */
225   uint16_t                 id;       /* id of the probe */
226   uint8_t                  mode;     /* mode the probe was sent in */
227 };
228 
229 typedef struct tracelb_host
230 {
231   scamper_task_t          *task;
232   scamper_host_do_t       *hostdo;
233   scamper_tracelb_node_t  *node;
234   dlist_node_t            *dn;
235 } tracelb_host_t;
236 
237 /*
238  * tracelb_state
239  *
240  * this keeps state for the load-balancer traceroute process.
241  */
242 typedef struct tracelb_state
243 {
244   uint8_t                  confidence;   /* index into k[] */
245   scamper_fd_t            *icmp;         /* fd to listen to icmp packets */
246   scamper_fd_t            *probe;        /* fd to probe with */
247   scamper_fd_t            *dl;           /* datalink fd to tx on */
248   splaytree_t             *addrs;        /* set of addresses */
249 
250 #ifndef _WIN32
251   scamper_fd_t            *rtsock;       /* route socket */
252 #endif
253 
254   scamper_route_t         *route;
255   scamper_dlhdr_t         *dlhdr;        /* datalink header to prepend on tx */
256   struct timeval           next_tx;      /* time to send the next probe */
257   uint16_t                 payload_size; /* size of the probe packet payload */
258   tracelb_probe_t        **probes;       /* probes sent so far */
259   uint16_t                 id_next;      /* next id to use in a probe */
260   uint16_t                 flowid_next;  /* next flow-id to use in a probe */
261   heap_t                  *active;       /* heap of active branches */
262   heap_t                  *waiting;      /* heap of queued branches */
263   tracelb_link_t         **links;        /* links established */
264   int                      linkc;        /* count of links */
265   tracelb_path_t         **paths;        /* paths established */
266   int                      pathc;        /* count of paths */
267   dlist_t                 *ths;          /* tracelb_host_t */
268 } tracelb_state_t;
269 
270 /* temporary buffer shared amongst traceroutes */
271 static uint8_t             *pktbuf     = NULL;
272 static size_t               pktbuf_len = 0;
273 
274 /* the callback functions registered with the tracelb task */
275 static scamper_task_funcs_t funcs;
276 
277 #define TRACE_OPT_CONFIDENCE   1
278 #define TRACE_OPT_DPORT        2
279 #define TRACE_OPT_FIRSTHOP     3
280 #define TRACE_OPT_GAPLIMIT     4
281 #define TRACE_OPT_OPTION       5
282 #define TRACE_OPT_PROTOCOL     6
283 #define TRACE_OPT_ATTEMPTS     7
284 #define TRACE_OPT_PROBECMAX    8
285 #define TRACE_OPT_SPORT        9
286 #define TRACE_OPT_TOS          10
287 #define TRACE_OPT_USERID       11
288 #define TRACE_OPT_WAITTIMEOUT  12
289 #define TRACE_OPT_WAITPROBE    13
290 #define TRACE_OPT_RTRADDR      14
291 
292 static const scamper_option_in_t opts[] = {
293   {'c', NULL, TRACE_OPT_CONFIDENCE,  SCAMPER_OPTION_TYPE_NUM},
294   {'d', NULL, TRACE_OPT_DPORT,       SCAMPER_OPTION_TYPE_NUM},
295   {'f', NULL, TRACE_OPT_FIRSTHOP,    SCAMPER_OPTION_TYPE_NUM},
296   {'g', NULL, TRACE_OPT_GAPLIMIT,    SCAMPER_OPTION_TYPE_NUM},
297   {'O', NULL, TRACE_OPT_OPTION,      SCAMPER_OPTION_TYPE_STR},
298   {'P', NULL, TRACE_OPT_PROTOCOL,    SCAMPER_OPTION_TYPE_STR},
299   {'q', NULL, TRACE_OPT_ATTEMPTS,    SCAMPER_OPTION_TYPE_NUM},
300   {'Q', NULL, TRACE_OPT_PROBECMAX,   SCAMPER_OPTION_TYPE_NUM},
301   {'r', NULL, TRACE_OPT_RTRADDR,     SCAMPER_OPTION_TYPE_STR},
302   {'s', NULL, TRACE_OPT_SPORT,       SCAMPER_OPTION_TYPE_NUM},
303   {'t', NULL, TRACE_OPT_TOS,         SCAMPER_OPTION_TYPE_NUM},
304   {'U', NULL, TRACE_OPT_USERID,      SCAMPER_OPTION_TYPE_NUM},
305   {'w', NULL, TRACE_OPT_WAITTIMEOUT, SCAMPER_OPTION_TYPE_NUM},
306   {'W', NULL, TRACE_OPT_WAITPROBE,   SCAMPER_OPTION_TYPE_NUM},
307 };
308 static const int opts_cnt = SCAMPER_OPTION_COUNT(opts);
309 
scamper_do_tracelb_usage(void)310 const char *scamper_do_tracelb_usage(void)
311 {
312   return "tracelb [-c confidence] [-d dport] [-f firsthop] [-g gaplimit]\n"
313          "        [-O option] [-P method] [-q attempts] [-Q maxprobec]\n"
314          "        [-r rtraddr] [-s sport] [-t tos] [-U userid]\n"
315          "        [-w wait-timeout] [-W wait-probe]";
316 }
317 
tracelb_getstate(const scamper_task_t * task)318 static tracelb_state_t *tracelb_getstate(const scamper_task_t *task)
319 {
320   return scamper_task_getstate(task);
321 }
322 
tracelb_getdata(const scamper_task_t * task)323 static scamper_tracelb_t *tracelb_getdata(const scamper_task_t *task)
324 {
325   return scamper_task_getdata(task);
326 }
327 
k(tracelb_state_t * state,int n)328 static int k(tracelb_state_t *state, int n)
329 {
330   /*
331    * number of probes (k) to send to rule out a load-balancer having n hops;
332    * 95% confidence level first from 823-augustin-e2emon.pdf, then extended
333    * with gmp-based code.
334    * 99% confidence derived with gmp-based code.
335    */
336   static const int k[][2] = {
337     {   0,   0 }, {   0,   0 }, {   6,   8 }, {  11,  15 }, {  16,  21 },
338     {  21,  28 }, {  27,  36 }, {  33,  43 }, {  38,  51 }, {  44,  58 },
339     {  51,  66 }, {  57,  74 }, {  63,  82 }, {  70,  90 }, {  76,  98 },
340     {  83, 106 }, {  90, 115 }, {  96, 123 }, { 103, 132 }, { 110, 140 },
341     { 117, 149 }, { 124, 157 }, { 131, 166 }, { 138, 175 }, { 145, 183 },
342     { 152, 192 }, { 159, 201 }, { 167, 210 }, { 174, 219 }, { 181, 228 },
343     { 189, 237 }, { 196, 246 }, { 203, 255 }, { 211, 264 }, { 218, 273 },
344     { 226, 282 }, { 233, 291 }, { 241, 300 }, { 248, 309 }, { 256, 319 },
345     { 264, 328 }, { 271, 337 }, { 279, 347 }, { 287, 356 }, { 294, 365 },
346     { 302, 375 }, { 310, 384 }, { 318, 393 }, { 326, 403 }, { 333, 412 },
347     { 341, 422 }, { 349, 431 }, { 357, 441 }, { 365, 450 }, { 373, 460 },
348     { 381, 470 }, { 389, 479 }, { 397, 489 }, { 405, 499 }, { 413, 508 },
349     { 421, 518 }, { 429, 528 }, { 437, 537 }, { 445, 547 }, { 453, 557 },
350     { 462, 566 }, { 470, 576 }, { 478, 586 }, { 486, 596 }, { 494, 606 },
351     { 502, 616 }, { 511, 625 }, { 519, 635 }, { 527, 645 }, { 535, 655 },
352     { 544, 665 }, { 552, 675 }, { 560, 685 }, { 569, 695 }, { 577, 705 },
353     { 585, 715 }, { 594, 725 }, { 602, 735 }, { 610, 745 }, { 619, 755 },
354     { 627, 765 }, { 635, 775 }, { 644, 785 }, { 652, 795 }, { 661, 805 },
355     { 669, 815 }, { 678, 825 }, { 686, 835 }, { 695, 845 }, { 703, 855 },
356     { 712, 866 }, { 720, 876 }, { 729, 886 }, { 737, 896 }, { 746, 906 },
357   };
358 
359 #define TRACELB_CONFIDENCE_MAX_N 99
360 #define TRACELB_CONFIDENCE_NLIMIT(v) \
361   ((v) <= TRACELB_CONFIDENCE_MAX_N ? (v) : TRACELB_CONFIDENCE_MAX_N)
362 
363   assert(state->confidence < 2);
364   assert(n >= 2);
365   assert(n <= TRACELB_CONFIDENCE_MAX_N);
366 
367   return k[n][state->confidence];
368 }
369 
tracelb_handleerror(scamper_task_t * task,int err)370 static void tracelb_handleerror(scamper_task_t *task, int err)
371 {
372   scamper_tracelb_t *trace = tracelb_getdata(task);
373   trace->error = err;
374   scamper_debug(__func__, "%d", err);
375   scamper_task_queue_done(task, 0);
376   return;
377 }
378 
379 #ifdef NDEBUG
380 #define tracelb_paths_assert(state) ((void)0)
381 #define tracelb_paths_dump(state) ((void)0)
382 #define tracelb_links_dump(state) ((void)0)
383 #endif
384 
385 #ifndef NDEBUG
tracelb_paths_loop_fwd(tracelb_path_t * path,tracelb_path_t * node)386 static int tracelb_paths_loop_fwd(tracelb_path_t *path, tracelb_path_t *node)
387 {
388   int i;
389   for(i=0; i<path->fwdc; i++)
390     {
391       if(path->fwd[i] == node)
392 	return 1;
393       if(tracelb_paths_loop_fwd(path->fwd[i], node) != 0)
394 	return 1;
395     }
396   return 0;
397 }
398 
tracelb_paths_loop_back(tracelb_path_t * path,tracelb_path_t * node)399 static int tracelb_paths_loop_back(tracelb_path_t *path, tracelb_path_t *node)
400 {
401   int i;
402   for(i=0; i<path->backc; i++)
403     {
404       if(path->back[i] == node)
405 	return 1;
406       if(tracelb_paths_loop_back(path->back[i], node) != 0)
407 	return 1;
408     }
409   return 0;
410 }
411 
tracelb_paths_assert(tracelb_state_t * state)412 static void tracelb_paths_assert(tracelb_state_t *state)
413 {
414   tracelb_path_t *path;
415   int i, j, d, p;
416 
417   for(p=0; p<state->pathc; p++)
418     {
419       path = state->paths[p];
420 
421       assert(path->linkc >= 0);
422       if(path->linkc == 0)
423 	assert(path->links == NULL);
424       else
425 	assert(path->links != NULL);
426 
427       assert(path->fwdc >= 0);
428       if(path->fwdc == 0)
429 	assert(path->fwd == NULL);
430       else
431 	assert(path->fwd != NULL);
432 
433       assert(path->backc >= 0);
434       if(path->backc == 0)
435 	assert(path->back == NULL);
436       else
437 	assert(path->back != NULL);
438 
439       if(path->linkc == 0 && path->backc > 0)
440 	{
441 	  for(i=0; i<path->backc; i++)
442 	    assert(path->back[i]->linkc > 0);
443 	}
444 
445       for(i=0; i<path->linkc; i++)
446 	{
447 	  assert(path->links[i]->path == path);
448 	  assert(path->links[i]->link != NULL);
449 	  assert(path->links[i]->link->from != NULL);
450 	  if(i+1 != path->linkc || path->fwdc > 0)
451 	    assert(path->links[i]->link->to != NULL);
452 	}
453 
454       d = 0;
455       for(i=0; i<path->backc; i++)
456 	{
457 	  assert(path->back[i]->fwdc > 0);
458 
459 	  if(path->back[i]->distance > d)
460 	    d = path->back[i]->distance;
461 
462 	  for(j=0; j<path->back[i]->fwdc; j++)
463 	    {
464 	      assert(path->distance > path->back[i]->distance);
465 	      if(path->back[i]->fwd[j] == path)
466 		break;
467 	    }
468 
469 	  assert(j != path->back[i]->fwdc);
470 	}
471 
472       if(path->backc > 0)
473 	assert(d + 1 == path->distance);
474 
475       for(i=0; i<path->fwdc; i++)
476 	{
477 	  assert(path->fwd[i]->backc > 0);
478 	  for(j=0; j<path->fwd[i]->backc; j++)
479 	    {
480 	      if(path->fwd[i]->back[j] == path)
481 		break;
482 	    }
483 	  assert(j != path->fwd[i]->backc);
484 	}
485 
486       assert(tracelb_paths_loop_fwd(path, path) == 0);
487       assert(tracelb_paths_loop_back(path, path) == 0);
488     }
489 
490   return;
491 }
492 
tracelb_paths_dump(tracelb_state_t * state)493 static void tracelb_paths_dump(tracelb_state_t *state)
494 {
495   tracelb_path_t *path;
496   tracelb_link_t *link;
497   char buf[4096], addr[64];
498   size_t off;
499   int i, c, p;
500 
501   for(p=0; p<state->pathc; p++)
502     {
503       path = state->paths[p];
504       off  = 0;
505 
506       string_concat(buf, sizeof(buf), &off, "%p: %d %d %d %d", path,
507 		    path->distance, path->backc, path->linkc, path->fwdc);
508       if(path->linkc > 0)
509 	{
510 	  if(path->links[0]->link->from->addr != NULL)
511 	    {
512 	      scamper_addr_tostr(path->links[0]->link->from->addr,
513 				 addr, sizeof(addr));
514 	      string_concat(buf, sizeof(buf), &off, " %s", addr);
515 	    }
516 	  else
517 	    {
518 	      string_concat(buf, sizeof(buf), &off, " +");
519 	    }
520 
521 	  for(i=0; i<path->linkc; i++)
522 	    {
523 	      link = path->links[i];
524 
525 	      if(link->link->to == NULL)
526 		{
527 		  for(c=0; c<link->link->hopc; c++)
528 		    string_concat(buf, sizeof(buf), &off, " +");
529 		}
530 	      else
531 		{
532 		  for(c=1; c<link->link->hopc; c++)
533 		    string_concat(buf, sizeof(buf), &off, " +");
534 		  scamper_addr_tostr(link->link->to->addr, addr, sizeof(addr));
535 		  string_concat(buf, sizeof(buf), &off, " %s", addr);
536 		}
537 
538 	      if(link->flowids != NULL && (c=slist_count(link->flowids)) != 0)
539 		string_concat(buf, sizeof(buf), &off, " (%d)", c);
540 	    }
541 	}
542 
543       for(i=0; i<path->fwdc; i++)
544 	string_concat(buf, sizeof(buf), &off, " %p", path->fwd[i]);
545 
546       scamper_debug(__func__, "%s", buf);
547     }
548 
549   return;
550 }
551 
tracelb_links_dump(tracelb_state_t * state)552 static void tracelb_links_dump(tracelb_state_t *state)
553 {
554   scamper_tracelb_link_t *link;
555   scamper_tracelb_node_t *node;
556   tracelb_link_t *tlbl;
557   char buf[256], addr[64];
558   size_t off;
559   int i, j;
560 
561   for(i=0; i<state->linkc; i++)
562     {
563       tlbl = state->links[i];
564       link = tlbl->link;
565       off = 0;
566 
567       node = link->from;
568       if(node->addr != NULL)
569 	{
570 	  scamper_addr_tostr(node->addr, addr, sizeof(addr));
571 	  string_concat(buf, sizeof(buf), &off, "%s", addr);
572 	  if(SCAMPER_TRACELB_NODE_QTTL(node))
573 	    string_concat(buf, sizeof(buf), &off, ",%d", node->q_ttl);
574 	}
575       else
576 	string_concat(buf, sizeof(buf), &off, "*");
577 
578       for(j=1; j<link->hopc; j++)
579 	string_concat(buf, sizeof(buf), &off, " +");
580 
581       if(link->to == NULL)
582 	{
583 	  string_concat(buf, sizeof(buf), &off, " *");
584 	}
585       else
586 	{
587 	  node = link->to;
588 	  scamper_addr_tostr(node->addr, addr, sizeof(addr));
589 	  string_concat(buf, sizeof(buf), &off, " %s", addr);
590 	  if(SCAMPER_TRACELB_NODE_QTTL(node))
591 	    string_concat(buf, sizeof(buf), &off, ",%d", node->q_ttl);
592 	}
593 
594       scamper_debug(__func__, "%s", buf);
595     }
596 
597   return;
598 }
599 
600 #endif
601 
602 /*
603  * tracelb_isloop_addr
604  *
605  * small utility function to determine if a loop exists.
606  * called by tracelb_isloop.
607  * this function does not have to do the zero-ttl check that trace_isloop
608  * does and so should be faster.
609  */
tracelb_isloop_addr(const tracelb_path_t * path,const scamper_addr_t * addr)610 static tracelb_link_t *tracelb_isloop_addr(const tracelb_path_t *path,
611 					   const scamper_addr_t *addr)
612 {
613   scamper_tracelb_probeset_t *set;
614   scamper_tracelb_probe_t *probe;
615   scamper_tracelb_link_t *link;
616   tracelb_link_t *tlbl;
617   int i, j;
618   uint16_t k, l;
619 
620   if(path->linkc > 0)
621     {
622       i = path->linkc-1;
623       for(;;)
624 	{
625 	  /* a tracelb_link_t state is required at each spot in the array */
626 	  tlbl = path->links[i]; assert(tlbl != NULL);
627 
628 	  /* but there does not have to be an actual link with the state */
629 	  if((link = tlbl->link) == NULL || link->from->addr == NULL)
630 	    goto next;
631 
632 	  /* if the from address is the same, then we have a loop */
633 	  if(scamper_addr_cmp(link->from->addr, addr) == 0)
634 	    return tlbl;
635 
636 	  /*
637 	   * if the link includes a clump, then check the replies in
638 	   * the clump
639 	   */
640 	  if(link->hopc > 0)
641 	    {
642 	      for(j=0; j<link->hopc-1; j++)
643 		{
644 		  set = link->sets[j];
645 		  for(k=0; k<set->probec; k++)
646 		    {
647 		      probe = set->probes[k];
648 		      for(l=0; l<probe->rxc; l++)
649 			if(scamper_addr_cmp(probe->rxs[l]->reply_from,
650 					    addr) == 0)
651 			  return tlbl;
652 		    }
653 		}
654 	    }
655 
656 	next:
657 	  if(i == 0)
658 	    break;
659 	  i--;
660 	}
661     }
662 
663   for(i=0; i<path->backc; i++)
664     if((tlbl = tracelb_isloop_addr(path->back[i], addr)) != NULL)
665       return tlbl;
666 
667   return NULL;
668 }
669 
670 /*
671  * tracelb_isloop
672  *
673  * check to see if the link that has just been added to the path has an
674  * inferred loop.
675  */
tracelb_isloop(const tracelb_path_t * path)676 static tracelb_link_t *tracelb_isloop(const tracelb_path_t *path)
677 {
678   /*
679    * the link has been added to this path before calling, so there should
680    * be at least one link here
681    */
682   assert(path->linkc > 0);
683 
684   /*
685    * if the link is a case of zero-ttl forwarding, then there is no loop
686    * at this link, or in any prior part, as the link->from address has
687    * already been tested for a loop.
688    */
689   if(scamper_tracelb_link_zerottlfwd(path->links[path->linkc-1]->link) != 0)
690     {
691       return 0;
692     }
693 
694   return tracelb_isloop_addr(path, path->links[path->linkc-1]->link->to->addr);
695 }
696 
697 /*
698  * tracelb_link_continue
699  *
700  * iterate through the replies for the link.  if any of the replies indicates
701  * that the router won't forward packets any further, tell the caller not
702  * to continue probing the path.
703  */
tracelb_link_continue(const scamper_tracelb_t * trace,const tracelb_path_t * path,const scamper_tracelb_link_t * link)704 static int tracelb_link_continue(const scamper_tracelb_t *trace,
705 				 const tracelb_path_t *path,
706 				 const scamper_tracelb_link_t *link)
707 {
708   scamper_tracelb_probeset_t *set;
709   scamper_tracelb_probe_t *probe;
710   scamper_tracelb_reply_t *reply;
711   uint16_t i, j;
712 
713   /* don't continue probing if the destination address has been reached */
714   if(link->to != NULL && scamper_addr_cmp(trace->dst, link->to->addr) == 0)
715     {
716       scamper_debug(__func__, "reached destination");
717       return 0;
718     }
719 
720   /* if any of the replies are not time exceeded, don't continue probing */
721   set = link->sets[link->hopc-1];
722   for(i=0; i<set->probec; i++)
723     {
724       probe = set->probes[i];
725       for(j=0; j<probe->rxc; j++)
726 	{
727 	  reply = probe->rxs[j];
728 	  if(SCAMPER_TRACELB_REPLY_IS_ICMP_TTL_EXP(reply) == 0)
729 	    {
730 	      scamper_debug(__func__, "%d/%d not time exceeded", i, j);
731 	      return 0;
732 	    }
733 	}
734     }
735 
736   /* if a loop is encountered, then don't continue probing */
737   if(tracelb_isloop(path) != NULL)
738     {
739       scamper_debug(__func__, "loop encountered");
740       return 0;
741     }
742 
743   return 1;
744 }
745 
746 /*
747  * tracelb_addr
748  *
749  * keep a per-tracelb cache of addresses to avoid unnecessary malloc
750  * of addresses.
751  */
tracelb_addr(tracelb_state_t * state,int type,void * addr)752 static scamper_addr_t *tracelb_addr(tracelb_state_t *state,int type,void *addr)
753 {
754   scamper_addr_t *a, fm;
755 
756   fm.type = type;
757   fm.addr = addr;
758   if((a = splaytree_find(state->addrs, &fm)) != NULL)
759     return a;
760 
761   if((a = scamper_addr_alloc(type, addr)) == NULL)
762     {
763       printerror(__func__, "could not alloc addr");
764       return NULL;
765     }
766 
767   if(splaytree_insert(state->addrs, a) == NULL)
768     {
769       printerror(__func__, "could not insert addr");
770       scamper_addr_free(a);
771       return NULL;
772     }
773 
774   return a;
775 }
776 
777 /*
778  * tracelb_set_visited0
779  *
780  * set the visited field of all tracelb_path_t structures to zero
781  */
tracelb_set_visited0(tracelb_state_t * state)782 static void tracelb_set_visited0(tracelb_state_t *state)
783 {
784   int i;
785   for(i=0; i<state->pathc; i++)
786     state->paths[i]->visited = 0;
787   return;
788 }
789 
tracelb_bringfwd_free(tracelb_branch_t * br)790 static void tracelb_bringfwd_free(tracelb_branch_t *br)
791 {
792   int i;
793   if(br->bringfwd != NULL)
794     {
795       for(i=0; i<br->bringfwdc; i++)
796 	if(br->bringfwd[i] != NULL)
797 	  free(br->bringfwd[i]);
798       free(br->bringfwd);
799       br->bringfwd = NULL;
800     }
801   br->bringfwdc = 0;
802   return;
803 }
804 
tracelb_bringfwd_add(tracelb_branch_t * br,tracelb_path_t * path)805 static int tracelb_bringfwd_add(tracelb_branch_t *br, tracelb_path_t *path)
806 {
807   tracelb_bringfwd_t *bf;
808   if((bf = malloc_zero(sizeof(tracelb_bringfwd_t))) == NULL)
809     {
810       printerror(__func__, "could not malloc");
811       return -1;
812     }
813   bf->path = path;
814   br->bringfwd[br->bringfwdc++] = bf;
815   return 0;
816 }
817 
818 /*
819  * tracelb_bringfwd_dft
820  *
821  * depth-first traversal of the paths leading to the specified path, for the
822  * purposes of bring a flowid forward through the path to path[0].
823  */
tracelb_bringfwd_dft(tracelb_branch_t * branch,tracelb_path_t * path)824 static int tracelb_bringfwd_dft(tracelb_branch_t *branch, tracelb_path_t *path)
825 {
826   int i, x, rc = 0;
827 
828   assert(path != NULL);
829 
830   if(path->visited != 0)
831     return 1;
832 
833   if(path->backc == 0)
834     {
835       if(tracelb_bringfwd_add(branch, path) != 0)
836 	return -1;
837       path->visited = 1;
838       return 1;
839     }
840 
841   for(i=0; i<path->backc; i++)
842     {
843       if((x = tracelb_bringfwd_dft(branch, path->back[i])) == 1)
844 	rc = 1;
845       else if(x == -1)
846 	return -1;
847     }
848 
849   if(rc != 0)
850     {
851       if(tracelb_bringfwd_add(branch, path) != 0)
852 	return -1;
853       path->visited = 1;
854     }
855 
856   return rc;
857 }
858 
859 /*
860  * tracelb_bringfwd_set
861  *
862  *
863  */
tracelb_bringfwd_set(tracelb_state_t * state,tracelb_branch_t * br,tracelb_link_t * tlbl,int set)864 static int tracelb_bringfwd_set(tracelb_state_t *state, tracelb_branch_t *br,
865 				tracelb_link_t *tlbl, int set)
866 {
867   tracelb_path_t *path;
868   int i, n;
869 
870   for(i=0; i<br->bringfwdc; i++)
871     {
872       path = br->bringfwd[i]->path;
873       if(path == tlbl->path)
874 	{
875 	  assert(path->links[path->linkc-1] == tlbl);
876 	  n = TRACELB_CONFIDENCE_NLIMIT(path->fwdc+2);
877 
878 	  if(set != 0)
879 	    {
880 	      br->bringfwd[i]->k++;
881 	      if(br->bringfwd[i]->k >= k(state, n))
882 		{
883 		  return 1;
884 		}
885 	    }
886 	  else
887 	    br->bringfwd[i]->k = 0;
888 
889 	  scamper_debug(__func__, "set %d i %d k %d < %d",
890 			set, i, br->bringfwd[i]->k, k(state, n));
891 	  break;
892 	}
893     }
894 
895   assert(i != br->bringfwdc);
896 
897   return 0;
898 }
899 
tracelb_flowids_list_add(slist_t * list,tracelb_probe_t * pr)900 static int tracelb_flowids_list_add(slist_t *list, tracelb_probe_t *pr)
901 {
902   tracelb_flowid_t *tf;
903 
904   assert(pr->mode != MODE_PERPACKET);
905   assert(pr->mode != MODE_BRINGFWD0);
906   assert(pr->mode != MODE_BRINGFWD);
907 
908   if((tf = malloc_zero(sizeof(tracelb_flowid_t))) == NULL)
909     {
910       printerror(__func__, "could not malloc flowid");
911       return -1;
912     }
913   tf->id  = pr->probe->flowid;
914   tf->ttl = pr->probe->ttl;
915 
916   if(slist_tail_push(list, tf) == NULL)
917     {
918       free(tf);
919       printerror(__func__, "could not slist_tail_push");
920       return -1;
921     }
922 
923   return 0;
924 }
925 
926 /*
927  * tracelb_link_flowid_get
928  *
929  * return a flowid from the link, if there is one.  when returning the last
930  * flowid free the list structure.
931  */
tracelb_link_flowid_get(tracelb_link_t * link)932 static tracelb_flowid_t *tracelb_link_flowid_get(tracelb_link_t *link)
933 {
934   tracelb_flowid_t *tf;
935 
936   if(link->flowids == NULL)
937     return NULL;
938 
939   tf = slist_head_pop(link->flowids);
940 
941   if(slist_count(link->flowids) == 0)
942     {
943       slist_free(link->flowids);
944       link->flowids = NULL;
945     }
946 
947   return tf;
948 }
949 
950 /*
951  * tracelb_link_cmp
952  *
953  * useful for providing as a sort callback method for qsort on state->links
954  */
tracelb_link_cmp(const tracelb_link_t * a,const tracelb_link_t * b)955 static int tracelb_link_cmp(const tracelb_link_t *a, const tracelb_link_t *b)
956 {
957   assert(a != NULL); assert(a->link != NULL);
958   assert(b != NULL); assert(b->link != NULL);
959   return scamper_tracelb_link_cmp(a->link, b->link);
960 }
961 
962 /*
963  * tracelb_cmp_node2reply
964  *
965  * determine if a reply could be matched against a node.
966  */
tracelb_cmp_node2reply(const scamper_tracelb_node_t * node,const scamper_tracelb_reply_t * reply)967 static int tracelb_cmp_node2reply(const scamper_tracelb_node_t *node,
968 				  const scamper_tracelb_reply_t *reply)
969 {
970   scamper_tracelb_node_t cmp;
971 
972   memset(&cmp, 0, sizeof(cmp));
973   cmp.addr = reply->reply_from;
974   if(SCAMPER_TRACELB_REPLY_IS_ICMP_TTL_EXP(reply) ||
975      SCAMPER_TRACELB_REPLY_IS_ICMP_UNREACH(reply))
976     {
977       cmp.q_ttl  = reply->reply_icmp_q_ttl;
978       cmp.flags |= SCAMPER_TRACELB_NODE_FLAG_QTTL;
979     }
980 
981   return scamper_tracelb_node_cmp(node, &cmp);
982 }
983 
tracelb_newnode_cmp(const tracelb_newnode_t * a,const tracelb_newnode_t * b)984 static int tracelb_newnode_cmp(const tracelb_newnode_t *a,
985 			       const tracelb_newnode_t *b)
986 {
987   return scamper_tracelb_node_cmp(a->node, b->node);
988 }
989 
tracelb_newnode_find(tracelb_branch_t * br,scamper_tracelb_reply_t * reply)990 static tracelb_newnode_t *tracelb_newnode_find(tracelb_branch_t *br,
991 					       scamper_tracelb_reply_t *reply)
992 {
993   tracelb_newnode_t findme;
994   scamper_tracelb_node_t node;
995 
996   memset(&node, 0, sizeof(node));
997   findme.node = &node;
998 
999   if(SCAMPER_TRACELB_REPLY_IS_ICMP_TTL_EXP(reply) ||
1000      SCAMPER_TRACELB_REPLY_IS_ICMP_UNREACH(reply))
1001     {
1002       node.q_ttl  = reply->reply_icmp_q_ttl;
1003       node.flags |= SCAMPER_TRACELB_NODE_FLAG_QTTL;
1004     }
1005   node.addr = reply->reply_from;
1006 
1007   return array_find((void **)br->newnodes, br->newnodec, &findme,
1008 		    (array_cmp_t)tracelb_newnode_cmp);
1009 }
1010 
tracelb_newnode_add(tracelb_branch_t * br,scamper_tracelb_probe_t * probe)1011 static int tracelb_newnode_add(tracelb_branch_t *br,
1012 			       scamper_tracelb_probe_t *probe)
1013 {
1014   scamper_tracelb_reply_t *reply;
1015   tracelb_newnode_t *newnode;
1016 
1017   assert(probe->rxc == 1);
1018 
1019   if((newnode = malloc_zero(sizeof(tracelb_newnode_t))) == NULL)
1020     {
1021       printerror(__func__, "could not alloc newnode");
1022       goto err;
1023     }
1024   newnode->probe = probe;
1025 
1026   reply = probe->rxs[0];
1027   if((newnode->node = scamper_tracelb_node_alloc(reply->reply_from)) == NULL)
1028     {
1029       printerror(__func__, "could not alloc node");
1030       goto err;
1031     }
1032   if(SCAMPER_TRACELB_REPLY_IS_ICMP_TTL_EXP(reply) ||
1033      SCAMPER_TRACELB_REPLY_IS_ICMP_UNREACH(reply))
1034     {
1035       newnode->node->q_ttl  = reply->reply_icmp_q_ttl;
1036       newnode->node->flags |= SCAMPER_TRACELB_NODE_FLAG_QTTL;
1037     }
1038 
1039   if(array_insert((void ***)&br->newnodes, &br->newnodec, newnode,
1040 		  (array_cmp_t)tracelb_newnode_cmp) != 0)
1041     {
1042       printerror(__func__, "could not add node to branch");
1043       goto err;
1044     }
1045 
1046   return 0;
1047 
1048  err:
1049   if(newnode != NULL)
1050     {
1051       if(newnode->node != NULL)
1052 	scamper_tracelb_node_free(newnode->node);
1053       free(newnode);
1054     }
1055   return -1;
1056 }
1057 
1058 /*
1059  * tracelb_link_alloc
1060  *
1061  * simple function to allocate a new link.
1062  */
tracelb_link_alloc(tracelb_state_t * state,scamper_tracelb_link_t * link,tracelb_path_t * path)1063 static tracelb_link_t *tracelb_link_alloc(tracelb_state_t *state,
1064 					  scamper_tracelb_link_t *link,
1065 					  tracelb_path_t *path)
1066 {
1067   tracelb_link_t *tlbl;
1068 
1069   if((tlbl = malloc_zero(sizeof(tracelb_link_t))) == NULL)
1070     {
1071       printerror(__func__, "could not alloc tlbl");
1072       return NULL;
1073     }
1074   tlbl->link = link;
1075   tlbl->path = path;
1076 
1077   if(array_insert((void ***)&state->links, &state->linkc, tlbl,
1078 		  (array_cmp_t)tracelb_link_cmp) != 0)
1079     {
1080       printerror(__func__, "could not insert tlbl");
1081       free(tlbl);
1082       return NULL;;
1083     }
1084 
1085   return tlbl;
1086 }
1087 
tracelb_link_find(tracelb_state_t * state,scamper_tracelb_link_t * link)1088 static tracelb_link_t *tracelb_link_find(tracelb_state_t *state,
1089 					 scamper_tracelb_link_t *link)
1090 {
1091   tracelb_link_t findme;
1092   findme.link = link;
1093   return (tracelb_link_t *)array_find((void **)state->links, state->linkc,
1094 				      &findme, (array_cmp_t)tracelb_link_cmp);
1095 }
1096 
1097 /*
1098  * tracelb_link_free
1099  *
1100  * free up a tracelb_link structure.
1101  */
tracelb_link_free(tracelb_link_t * link)1102 static void tracelb_link_free(tracelb_link_t *link)
1103 {
1104   tracelb_flowid_t *tf;
1105   while((tf = tracelb_link_flowid_get(link)) != NULL)
1106     {
1107       free(tf);
1108     }
1109   free(link);
1110   return;
1111 }
1112 
1113 /*
1114  * tracelb_link_flowid_add_tail
1115  *
1116  * record probe flowid/ttl details of a reply with a link
1117  */
tracelb_link_flowid_add_tail(tracelb_link_t * link,scamper_tracelb_probe_t * probe)1118 static int tracelb_link_flowid_add_tail(tracelb_link_t *link,
1119 					scamper_tracelb_probe_t *probe)
1120 {
1121   tracelb_flowid_t *tf;
1122 
1123   /* allocate a list structure, if necessary, to store the flowids */
1124   if(link->flowids == NULL && (link->flowids = slist_alloc()) == NULL)
1125     {
1126       printerror(__func__, "could not alloc flowids list");
1127       return -1;
1128     }
1129 
1130   if((tf = malloc_zero(sizeof(tracelb_flowid_t))) == NULL)
1131     {
1132       printerror(__func__, "could not malloc flowid");
1133       return -1;
1134     }
1135   tf->id  = probe->flowid;
1136   tf->ttl = probe->ttl;
1137 
1138   if(slist_tail_push(link->flowids, tf) == NULL)
1139     {
1140       free(tf);
1141       printerror(__func__, "could not slist_tail_push");
1142       return -1;
1143     }
1144 
1145   return 0;
1146 }
1147 
tracelb_link_flowids_inc(tracelb_link_t * tlbl)1148 static void tracelb_link_flowids_inc(tracelb_link_t *tlbl)
1149 {
1150   tracelb_flowid_t *tf;
1151   slist_node_t *node;
1152 
1153   if(tlbl->flowids == NULL)
1154     return;
1155 
1156   node = slist_head_node(tlbl->flowids);
1157   while(node != NULL)
1158     {
1159       tf = slist_node_item(node);
1160       tf->ttl++;
1161       node = slist_node_next(node);
1162     }
1163 
1164   return;
1165 }
1166 
1167 /*
1168  * tracelb_link_flowids_add_list
1169  *
1170  */
tracelb_link_flowids_add_list(tracelb_link_t * tlbl,slist_t * list)1171 static void tracelb_link_flowids_add_list(tracelb_link_t *tlbl, slist_t *list)
1172 {
1173   /* allocate a list structure, if necessary, to store the flowids */
1174   if(tlbl->flowids == NULL)
1175     {
1176       tlbl->flowids = list;
1177     }
1178   else
1179     {
1180       slist_concat(list, tlbl->flowids);
1181       slist_free(tlbl->flowids);
1182       tlbl->flowids = list;
1183     }
1184   return;
1185 }
1186 
1187 /*
1188  * tracelb_probe_add
1189  *
1190  * simple function to add a probe to the array of probes sent
1191  */
tracelb_probe_add(tracelb_state_t * state,tracelb_branch_t * br,tracelb_probe_t * pr)1192 static int tracelb_probe_add(tracelb_state_t *state, tracelb_branch_t *br,
1193 			     tracelb_probe_t *pr)
1194 {
1195   size_t len = sizeof(tracelb_probe_t *) * (state->id_next + 1);
1196   if(realloc_wrap((void **)&state->probes, len) != 0)
1197     {
1198       printerror(__func__, "could not realloc %d bytes", len);
1199       return -1;
1200     }
1201   pr->id = state->id_next;
1202 
1203   state->probes[state->id_next] = pr;
1204   state->id_next++;
1205 
1206   if(array_insert((void ***)&br->probes, &br->probec, pr, NULL) != 0)
1207     {
1208       printerror(__func__, "could not add probe to branch");
1209       return -1;
1210     }
1211   pr->branch = br;
1212 
1213   return 0;
1214 }
1215 
1216 /*
1217  * tracelb_path_length
1218  *
1219  * return the length of the shortest sequence of path lengths back to the head
1220  */
tracelb_path_length(const tracelb_path_t * path)1221 static uint8_t tracelb_path_length(const tracelb_path_t *path)
1222 {
1223   const tracelb_path_t *shortest;
1224   uint8_t len = path->linkc;
1225   int i;
1226 
1227   while(path->backc > 0)
1228     {
1229       shortest = path->back[0];
1230       for(i=1; i<path->backc; i++)
1231 	{
1232 	  if(shortest->linkc > path->back[i]->linkc)
1233 	    shortest = path->back[i];
1234 	}
1235       len += shortest->linkc;
1236       path = shortest;
1237     }
1238 
1239   return len;
1240 }
1241 
1242 /*
1243  * tracelb_path_add_link
1244  *
1245  * simple function to add a link to an existing path
1246  */
tracelb_path_add_link(tracelb_path_t * path,tracelb_link_t * link)1247 static int tracelb_path_add_link(tracelb_path_t *path, tracelb_link_t *link)
1248 {
1249   if(array_insert((void ***)&path->links, &path->linkc, link, NULL) != 0)
1250     {
1251       printerror(__func__, "could not add link");
1252       return -1;
1253     }
1254   return 0;
1255 }
1256 
1257 /*
1258  * tracelb_path_add_fwd
1259  *
1260  * simple function to add a forward path to another path.
1261  */
tracelb_path_add_fwd(tracelb_path_t * path,tracelb_path_t * fwd)1262 static int tracelb_path_add_fwd(tracelb_path_t *path, tracelb_path_t *fwd)
1263 {
1264   if(array_insert((void ***)&path->fwd, &path->fwdc, fwd, NULL) != 0)
1265     {
1266       printerror(__func__, "could not add fwd");
1267       return -1;
1268     }
1269   return 0;
1270 }
1271 
1272 /*
1273  * tracelb_path_add_back
1274  *
1275  * simple function to add a back path to another path.
1276  */
tracelb_path_add_back(tracelb_path_t * path,tracelb_path_t * back)1277 static int tracelb_path_add_back(tracelb_path_t *path, tracelb_path_t *back)
1278 {
1279   if(array_insert((void ***)&path->back, &path->backc, back, NULL) != 0)
1280     {
1281       printerror(__func__, "could not add back");
1282       return -1;
1283     }
1284 
1285   return tracelb_path_add_fwd(back, path);
1286 }
1287 
tracelb_path_free(tracelb_path_t * path)1288 static void tracelb_path_free(tracelb_path_t *path)
1289 {
1290   if(path == NULL)
1291     return;
1292 
1293   if(path->back  != NULL) free(path->back);
1294   if(path->fwd   != NULL) free(path->fwd);
1295   if(path->links != NULL) free(path->links);
1296   free(path);
1297 
1298   return;
1299 }
1300 
1301 /*
1302  * tracelb_path_alloc
1303  *
1304  * allocate a new path structure; advise how many backc/linkc entries
1305  * to pre-allocate, if any.
1306  */
tracelb_path_alloc(tracelb_state_t * state,int linkc)1307 static tracelb_path_t *tracelb_path_alloc(tracelb_state_t *state, int linkc)
1308 {
1309   tracelb_path_t *path;
1310   size_t len;
1311 
1312   assert(linkc >= 0);
1313 
1314   if((path = malloc_zero(sizeof(tracelb_path_t))) == NULL)
1315     {
1316       printerror(__func__, "could not malloc path");
1317       goto err;
1318     }
1319 
1320   len = sizeof(scamper_tracelb_link_t *) * linkc;
1321   if(linkc != 0 && (path->links = malloc_zero(len)) == NULL)
1322     {
1323       printerror(__func__, "could not malloc path->links");
1324       goto err;
1325     }
1326 
1327   if(array_insert((void ***)&state->paths, &state->pathc, path, NULL) != 0)
1328     {
1329       printerror(__func__, "could not insert path");
1330       goto err;
1331     }
1332 
1333   return path;
1334 
1335  err:
1336   tracelb_path_free(path);
1337   return NULL;
1338 }
1339 
1340 /*
1341  * tracelb_bringfwd_cmp
1342  *
1343  * callback sort function used to sort the paths by distance (in branches)
1344  * from the top of the tree.
1345  */
tracelb_bringfwd_cmp(const tracelb_bringfwd_t * a,const tracelb_bringfwd_t * b)1346 static int tracelb_bringfwd_cmp(const tracelb_bringfwd_t *a,
1347 				const tracelb_bringfwd_t *b)
1348 {
1349   if(a->path->distance < b->path->distance)
1350     return 1;
1351   if(a->path->distance > b->path->distance)
1352     return -1;
1353   return 0;
1354 }
1355 
1356 /*
1357  * tracelb_branch_active_cmp
1358  *
1359  * this function is used to sort the state->active heap so that the branch
1360  * due to be probed next is at the top of the heap.
1361  */
tracelb_branch_active_cmp(const tracelb_branch_t * a,const tracelb_branch_t * b)1362 static int tracelb_branch_active_cmp(const tracelb_branch_t *a,
1363 				     const tracelb_branch_t *b)
1364 {
1365   return timeval_cmp(&b->next_tx, &a->next_tx);
1366 }
1367 
tracelb_branch_active(tracelb_state_t * state,tracelb_branch_t * br)1368 static int tracelb_branch_active(tracelb_state_t *state, tracelb_branch_t *br)
1369 {
1370   assert(heap_count(state->active) == 0);
1371   assert(br->probec > 0 || br->mode == MODE_RTSOCK || br->mode == MODE_DLHDR);
1372   if((br->heapnode = heap_insert(state->active, br)) != NULL)
1373     {
1374       return 0;
1375     }
1376   printerror(__func__, "could not insert branch on active");
1377   return -1;
1378 }
1379 
1380 /*
1381  * tracelb_branch_waiting_cmp
1382  *
1383  * this function is used to sort the state->branch heap so that next the
1384  * branch introduced to active probing is the one closest to the root, in
1385  * ttl terms.
1386  */
tracelb_branch_waiting_cmp(const tracelb_branch_t * a,const tracelb_branch_t * b)1387 static int tracelb_branch_waiting_cmp(const tracelb_branch_t *a,
1388 				      const tracelb_branch_t *b)
1389 {
1390   return tracelb_path_length(b->path) - tracelb_path_length(a->path);
1391 }
1392 
tracelb_branch_waiting(tracelb_state_t * state,tracelb_branch_t * br)1393 static int tracelb_branch_waiting(tracelb_state_t *state, tracelb_branch_t *br)
1394 {
1395   if((br->heapnode = heap_insert(state->waiting, br)) != NULL)
1396     {
1397       return 0;
1398     }
1399   printerror(__func__, "could not insert branch on waiting");
1400   return -1;
1401 }
1402 
1403 /*
1404  * tracelb_branch_reset
1405  *
1406  * reset probing state on the branch, but do not reset the links/nodes
1407  * inferred so far.  this allows us to determine per-packet load balancing
1408  * details for a set of
1409  */
tracelb_branch_reset(tracelb_branch_t * branch)1410 static void tracelb_branch_reset(tracelb_branch_t *branch)
1411 {
1412   int i;
1413 
1414   branch->l = 0;
1415   branch->k = 0;
1416   branch->n = 2;
1417 
1418   if(branch->mode != MODE_PERPACKET)
1419     {
1420       /* don't want the probes */
1421       if(branch->probes != NULL)
1422 	{
1423 	  for(i=0; i<branch->probec; i++)
1424 	    branch->probes[i]->branch = NULL;
1425 	  free(branch->probes);
1426 	  branch->probes = NULL;
1427 	}
1428       branch->probec = 0;
1429     }
1430 
1431   /* won't need any state associated with bringing probes forward */
1432   tracelb_bringfwd_free(branch);
1433 
1434   return;
1435 }
1436 
tracelb_branch_free(tracelb_state_t * state,tracelb_branch_t * br)1437 static void tracelb_branch_free(tracelb_state_t *state, tracelb_branch_t *br)
1438 {
1439   int i;
1440 
1441   if(br->probes != NULL)
1442     {
1443       for(i=0; i<br->probec; i++)
1444 	br->probes[i]->branch = NULL;
1445       free(br->probes);
1446     }
1447 
1448   tracelb_bringfwd_free(br);
1449 
1450   if(br->newnodes != NULL)
1451     free(br->newnodes);
1452 
1453   free(br);
1454   return;
1455 }
1456 
1457 /*
1458  * tracelb_path_add
1459  *
1460  * add a path to the heap of currently traced paths.
1461  */
tracelb_path_add(tracelb_state_t * state,tracelb_path_t * path)1462 static int tracelb_path_add(tracelb_state_t *state, tracelb_path_t *path)
1463 {
1464   tracelb_branch_t *branch = NULL;
1465 
1466   if((branch = malloc_zero(sizeof(tracelb_branch_t))) == NULL)
1467     {
1468       printerror(__func__, "could not alloc branch");
1469       goto err;
1470     }
1471   branch->mode = MODE_HOPPROBE;
1472   branch->path = path;
1473   tracelb_branch_reset(branch);
1474   if(tracelb_branch_waiting(state, branch) != 0)
1475     goto err;
1476 
1477   return 0;
1478 
1479  err:
1480   if(branch != NULL) tracelb_branch_free(state, branch);
1481   return -1;
1482 }
1483 
1484 /*
1485  * tracelb_path_distance_1
1486  *
1487  * recursive function to add a particular value to the distance field of
1488  * all paths ahead of a particular path.
1489  */
tracelb_path_distance_1(tracelb_path_t * path,int distance)1490 static void tracelb_path_distance_1(tracelb_path_t *path, int distance)
1491 {
1492   int i;
1493 
1494   scamper_debug(__func__, "path %p,%d %d->%d", path, path->visited,
1495 		path->distance, distance);
1496 
1497   path->visited++;
1498   if(path->distance >= distance)
1499     return;
1500 
1501   path->distance = distance;
1502 
1503   for(i=0; i<path->fwdc; i++)
1504     {
1505       assert(path != path->fwd[i]);
1506       tracelb_path_distance_1(path->fwd[i], distance+1);
1507     }
1508 
1509   return;
1510 }
1511 
tracelb_path_cmp(const tracelb_path_t * a,const tracelb_path_t * b)1512 static int tracelb_path_cmp(const tracelb_path_t *a, const tracelb_path_t *b)
1513 {
1514   if(a->distance < b->distance)
1515     return -1;
1516   if(a->distance > b->distance)
1517     return 1;
1518   return 0;
1519 }
1520 
tracelb_paths_sort(tracelb_state_t * state)1521 static void tracelb_paths_sort(tracelb_state_t *state)
1522 {
1523   array_qsort((void **)state->paths, state->pathc,
1524 	      (array_cmp_t)tracelb_path_cmp);
1525   return;
1526 }
1527 
1528 /*
1529  * tracelb_path_distance
1530  *
1531  * function to add a particular value to the distance field of all paths
1532  * ahead of a particular path.  uses the tracelb_path_distance_1 function
1533  * above, but first sets all of the visited values to zero so loops can
1534  * be detected when built with debugging.
1535  */
tracelb_path_distance(tracelb_state_t * state,tracelb_path_t * path,int distance)1536 static void tracelb_path_distance(tracelb_state_t *state,
1537 				  tracelb_path_t *path, int distance)
1538 {
1539   tracelb_set_visited0(state);
1540   tracelb_path_distance_1(path, distance);
1541   heap_remake(state->waiting);
1542   tracelb_paths_sort(state);
1543   return;
1544 }
1545 
1546 /*
1547  * tracelb_paths_splice
1548  *
1549  * `path0' shares a link or node in common with `path'.  take path, split
1550  * it into two parts.
1551  */
tracelb_paths_splice(tracelb_state_t * state,tracelb_path_t * path0,tracelb_path_t * path,int linkc)1552 static int tracelb_paths_splice(tracelb_state_t *state, tracelb_path_t *path0,
1553 				tracelb_path_t *path, int linkc)
1554 {
1555   tracelb_path_t *newp;
1556   int i, j, d;
1557 
1558   /*
1559    * allocate a new path.  the new path will have the first half of `path'
1560    * by the end of this routine
1561    */
1562   if((newp = tracelb_path_alloc(state, linkc)) == NULL)
1563     {
1564       return -1;
1565     }
1566 
1567   /* the new path inherits the back paths from the existing `path' */
1568   newp->back  = path->back;
1569   newp->backc = path->backc;
1570   path->back  = NULL;
1571   path->backc = 0;
1572   for(i=0; i<newp->backc; i++)
1573     {
1574       for(j=0; j<newp->back[i]->fwdc; j++)
1575 	{
1576 	  if(newp->back[i]->fwd[j] == path)
1577 	    newp->back[i]->fwd[j] = newp;
1578 	}
1579     }
1580 
1581   /* the new path inherits the first portion of the links in the old path */
1582   for(newp->linkc=0; newp->linkc < linkc; newp->linkc++)
1583     {
1584       newp->links[newp->linkc] = path->links[newp->linkc];
1585       newp->links[newp->linkc]->path = newp;
1586     }
1587 
1588   /* shift the links in the second portion into place */
1589   if(path->linkc - linkc > 0)
1590     {
1591       for(i=0; i<path->linkc-linkc; i++)
1592 	{
1593 	  path->links[i] = path->links[linkc+i];
1594 	}
1595       path->linkc = path->linkc - linkc;
1596     }
1597   else
1598     {
1599       assert(path->linkc - linkc == 0);
1600       free(path->links);
1601       path->links = NULL;
1602       path->linkc = 0;
1603     }
1604 
1605   /* the path now has two back pointers; add them */
1606   if(tracelb_path_add_back(path, newp)  != 0 ||
1607      tracelb_path_add_back(path, path0) != 0)
1608     {
1609       return -1;
1610     }
1611 
1612   /* make sure the measure of distance for path segments are correct */
1613   newp->distance = path->distance;
1614   if(path0->distance > path->distance)
1615     d = path0->distance + 1;
1616   else
1617     d = path->distance + 1;
1618   tracelb_path_distance(state, path, d);
1619 
1620   return 0;
1621 }
1622 
1623 /*
1624  * tracelb_paths_splice_bynode
1625  *
1626  *
1627  */
tracelb_paths_splice_bynode(tracelb_state_t * state,tracelb_path_t * path0)1628 static int tracelb_paths_splice_bynode(tracelb_state_t *state,
1629 				       tracelb_path_t *path0)
1630 {
1631   scamper_tracelb_link_t *link;
1632   tracelb_path_t *path;
1633   tracelb_link_t *tlbl;
1634   int i, j;
1635 
1636   tracelb_paths_assert(state);
1637 
1638   /*
1639    * find a link where this node is found.
1640    * this is done before the link below is added, since we don't want to
1641    * find the new link we are adding, nor do we want the index value to be
1642    * changed by a new link being added (and sorted) into the array.
1643    */
1644   tlbl = path0->links[path0->linkc-1];
1645   link = tlbl->link;
1646   for(i=0; i<state->linkc; i++)
1647     {
1648       if(state->links[i] != tlbl &&
1649 	 state->links[i]->link->to != NULL &&
1650 	 scamper_tracelb_node_cmp(state->links[i]->link->to, link->to) == 0)
1651 	{
1652 	  break;
1653 	}
1654     }
1655   assert(i != state->linkc);
1656 
1657   /* find where abouts in the path segment the node is found */
1658   tlbl = state->links[i];
1659   path = tlbl->path;
1660   for(i=0; i<path->linkc; i++)
1661     {
1662       if(path->links[i] == tlbl)
1663 	break;
1664     }
1665   assert(i != path->linkc);
1666 
1667   if(i+1 == path->linkc && path->fwdc != 0)
1668     {
1669       for(j=0; j<path->fwdc; j++)
1670 	{
1671 	  tracelb_path_add_back(path->fwd[j], path0);
1672 	  tracelb_path_distance(state, path->fwd[j], path0->distance + 1);
1673 	}
1674     }
1675   else
1676     {
1677       tracelb_paths_splice(state, path0, path, i+1);
1678     }
1679 
1680   tracelb_paths_assert(state);
1681   return 0;
1682 }
1683 
1684 /*
1685  * tracelb_queue
1686  *
1687  * the task is ready to be probed again.  put it in a queue to wait a little
1688  * longer, or put it into the queue to be probed asap.
1689  */
tracelb_queue(scamper_task_t * task)1690 static void tracelb_queue(scamper_task_t *task)
1691 {
1692   scamper_tracelb_t *trace = tracelb_getdata(task);
1693   tracelb_state_t *state = tracelb_getstate(task);
1694   tracelb_branch_t *branch;
1695   struct timeval now, next_tx;
1696 
1697   if(scamper_task_queue_isdone(task))
1698     return;
1699 
1700   /* if there are no branches to probe, then we're done */
1701   if(heap_count(state->active) == 0 && heap_count(state->waiting) == 0)
1702     {
1703       scamper_task_queue_done(task, 0);
1704       return;
1705     }
1706 
1707   if((branch = heap_head_item(state->active)) != NULL)
1708     {
1709       timeval_cpy(&next_tx, &branch->next_tx);
1710     }
1711   else
1712     {
1713       timeval_cpy(&next_tx, &state->next_tx);
1714     }
1715 
1716   /* get the current time */
1717   gettimeofday_wrap(&now);
1718 
1719   /* if the time to probe has already passed, queue it up */
1720   if(timeval_cmp(&next_tx, &now) <= 0)
1721     {
1722       /* check to see if we're permitted to send another probe */
1723       if(trace->probec >= trace->probec_max)
1724 	{
1725 	  scamper_task_queue_done(task, 0);
1726 	  return;
1727 	}
1728 
1729       scamper_task_queue_probe(task);
1730       return;
1731     }
1732 
1733   scamper_task_queue_wait_tv(task, &next_tx);
1734   return;
1735 }
1736 
tracelb_host_free(tracelb_state_t * state,tracelb_host_t * th)1737 static void tracelb_host_free(tracelb_state_t *state, tracelb_host_t *th)
1738 {
1739   if(th->dn != NULL) dlist_node_pop(state->ths, th->dn);
1740   if(th->hostdo != NULL) scamper_host_do_free(th->hostdo);
1741   free(th);
1742   return;
1743 }
1744 
tracelb_node_ptr_cb(void * param,const char * name)1745 static void tracelb_node_ptr_cb(void *param, const char *name)
1746 {
1747   tracelb_host_t *th = param;
1748   scamper_task_t *task = th->task;
1749   tracelb_state_t *state = tracelb_getstate(task);
1750 
1751   th->hostdo = NULL;
1752 
1753   if(name != NULL)
1754     th->node->name = strdup(name);
1755 
1756   tracelb_host_free(state, th);
1757 
1758   return;
1759 }
1760 
tracelb_node_ptr(scamper_task_t * task,scamper_tracelb_node_t * node)1761 static int tracelb_node_ptr(scamper_task_t *task, scamper_tracelb_node_t *node)
1762 {
1763   scamper_tracelb_t *trace = tracelb_getdata(task);
1764   tracelb_state_t *state = tracelb_getstate(task);
1765   tracelb_host_t *th = NULL;
1766 
1767   if((trace->flags & SCAMPER_TRACELB_FLAG_PTR) == 0 ||
1768      node->addr == NULL)
1769     return 0;
1770 
1771   if((state->ths == NULL && (state->ths = dlist_alloc()) == NULL))
1772     {
1773       printerror(__func__, "could not alloc ths");
1774       goto err;
1775     }
1776   if((th = malloc_zero(sizeof(tracelb_host_t))) == NULL)
1777     {
1778       printerror(__func__, "could not alloc th");
1779       goto err;
1780     }
1781   th->node = node;
1782   th->task = task;
1783   th->hostdo = scamper_do_host_do_ptr(node->addr, th, tracelb_node_ptr_cb);
1784   if(th->hostdo == NULL)
1785     {
1786       printerror(__func__, "could not scamper_do_host_do_ptr");
1787       goto err;
1788     }
1789   if((th->dn = dlist_tail_push(state->ths, th)) == NULL)
1790     {
1791       printerror(__func__, "could not push th");
1792       goto err;
1793     }
1794   return 0;
1795 
1796  err:
1797   if(th != NULL) tracelb_host_free(state, th);
1798   return -1;
1799 }
1800 
tracelb_process_hops(scamper_task_t * task,tracelb_branch_t * br)1801 static int tracelb_process_hops(scamper_task_t *task, tracelb_branch_t *br)
1802 {
1803   scamper_tracelb_t *trace = tracelb_getdata(task);
1804   tracelb_state_t *state = tracelb_getstate(task);
1805   tracelb_path_t *path0 = br->path;
1806   scamper_tracelb_probeset_t *set;
1807   scamper_tracelb_probe_t *probe;
1808   scamper_tracelb_node_t *from, *to;
1809   scamper_tracelb_link_t *link;
1810   tracelb_link_t *tlbl;
1811   tracelb_path_t *newp;
1812   tracelb_probe_t *pr;
1813   uint16_t flowid;
1814   slist_t *flowids = NULL;
1815   int i, j, k, splice, record, timxceed;
1816 
1817   assert(br->probec > 0);
1818 
1819   /*
1820    * get the from node.  the algorithm to obtain it depends on exactly what
1821    * happened prior to reaching here.
1822    */
1823   if(br->mode == MODE_FIRSTHOP)
1824     from = trace->nodes[0];
1825   else if(br->mode == MODE_CLUMP)
1826     from = NULL;
1827   else if(path0->linkc > 0)
1828     from = path0->links[path0->linkc-1]->link->to;
1829   else
1830     from = path0->back[0]->links[path0->back[0]->linkc-1]->link->to;
1831 
1832   for(i=0; i<br->newnodec; i++)
1833     {
1834       /* get details about the far end of the link */
1835       to = scamper_tracelb_node_find(trace, br->newnodes[i]->node);
1836       if(to == NULL)
1837 	{
1838 	  to = br->newnodes[i]->node;
1839 	  if(tracelb_node_ptr(task, to) != 0 ||
1840 	     scamper_tracelb_node_add(trace, to) != 0)
1841 	    goto err;
1842 	  splice = 0;
1843 	}
1844       else
1845 	{
1846 	  scamper_tracelb_node_free(br->newnodes[i]->node);
1847 	  splice = 1;
1848 	}
1849       free(br->newnodes[i]);
1850       br->newnodes[i] = NULL;
1851 
1852       /* create a link to store in the trace */
1853       if(br->mode != MODE_CLUMP)
1854 	{
1855 	  assert(from != NULL);
1856 	  if((link = scamper_tracelb_link_alloc()) == NULL)
1857 	    {
1858 	      goto err;
1859 	    }
1860 	  link->from = from;
1861 	  link->to   = to;
1862 	}
1863       else
1864 	{
1865 	  link = path0->links[path0->linkc-1]->link;
1866 	  assert(link->to == NULL);
1867 	  link->to = to;
1868 	}
1869 
1870       /*
1871        * try and allocate a probeset to record details of probes.
1872        * if it fails, then we have to free the link allocated above
1873        */
1874       if((set = scamper_tracelb_probeset_alloc()) == NULL ||
1875 	 (flowids = slist_alloc()) == NULL)
1876 	{
1877 	  if(br->mode != MODE_CLUMP)
1878 	    scamper_tracelb_link_free(link);
1879 	  goto err;
1880 	}
1881 
1882       /*
1883        * record responses with each link.  the code is relatively complicated
1884        * as we want to record all relevant probes with the link (some of which
1885        * did not obtain a response) and also figure out which flowids to use
1886        * probing forward.
1887        */
1888       flowid = br->probes[0]->probe->flowid;
1889       k = 0; record = 0; timxceed = 1;
1890       for(j=0; j<=br->probec; j++)
1891 	{
1892 	  /*
1893 	   * when we've got to the end of a sequence of probes with the same
1894 	   * flowid, look to record that sequence if applicable.
1895 	   *
1896 	   * the j == br->probec is a hack to make sure the last probe is
1897 	   * counted when processing all probes.
1898 	   */
1899 	  if(j == br->probec || br->probes[j]->probe->flowid != flowid)
1900 	    {
1901 	      /*
1902 	       * record only if the response(s) came from the node we are
1903 	       * recording to
1904 	       */
1905 	      if(record != 0)
1906 		{
1907 		  /*
1908 		   * record the flowid for further use if we have only
1909 		   * received time exceeded responses when probing
1910 		   */
1911 		  if(timxceed != 0 && br->probes[k]->mode != MODE_PERPACKET)
1912 		    {
1913 		      pr = br->probes[k];
1914 		      if(tracelb_flowids_list_add(flowids, pr) != 0)
1915 			goto err;
1916 		    }
1917 
1918 		  /*
1919 		   * record all attempts with a particular flowid/ttl
1920 		   * combination
1921 		   */
1922 		  while(k<j)
1923 		    {
1924 		      pr = br->probes[k];
1925 		      if(scamper_tracelb_probeset_add(set, pr->probe) != 0)
1926 			goto err;
1927 		      k++;
1928 		    }
1929 		}
1930 
1931 	      if(j == br->probec)
1932 		break;
1933 
1934 	      /* moving onto the next flowid now.  reset variables */
1935 	      timxceed = 1;
1936 	      record = 0;
1937 	      flowid = br->probes[j]->probe->flowid;
1938 	      k = j;
1939 	    }
1940 
1941 	  pr = br->probes[j];
1942 	  probe = pr->probe;
1943 
1944 	  /* if this probe is one which was brought forward, then skip it */
1945 	  if(pr->mode == MODE_BRINGFWD || pr->mode == MODE_BRINGFWD0)
1946 	    {
1947 	      k++;
1948 	      continue;
1949 	    }
1950 
1951 	  /* check to see if this probe got any responses. */
1952 	  if(probe->rxc == 0)
1953 	    continue;
1954 
1955 	  /*
1956 	   * if we're in this function, it is because no probe got more than
1957 	   * a single response
1958 	   */
1959 	  assert(probe->rxc == 1 || pr->mode == MODE_PERPACKET);
1960 
1961 	  /*
1962 	   * if this reply matches the node we're processing, then set a
1963 	   * variable to say that we want to record this set of probes.
1964 	   */
1965 	  if(tracelb_cmp_node2reply(to, probe->rxs[0]) == 0)
1966 	    {
1967 	      record = 1;
1968 	      if(SCAMPER_TRACELB_REPLY_IS_ICMP_TTL_EXP(probe->rxs[0]) == 0)
1969 		timxceed = 0;
1970 	    }
1971 	}
1972 
1973       /* record the probeset with the link */
1974       if(scamper_tracelb_link_probeset(link, set) != 0)
1975 	{
1976 	  printerror(__func__, "could not add probeset");
1977 	  goto err;
1978 	}
1979 
1980       /* record the link in the trace */
1981       if(link->hopc == 1 && scamper_tracelb_link_add(trace, link) != 0)
1982 	{
1983 	  printerror(__func__, "could not add new link");
1984 	  goto err;
1985 	}
1986 
1987       if(br->mode == MODE_FIRSTHOP)
1988 	{
1989 	  assert(splice == 0 || link->from == link->to);
1990 	  if((newp = tracelb_path_alloc(state, 0)) == NULL ||
1991 	     (tlbl = tracelb_link_alloc(state, link, newp)) == NULL ||
1992 	     tracelb_path_add_link(newp, tlbl) != 0)
1993 	    {
1994 	      goto err;
1995 	    }
1996 	}
1997       else if(br->mode == MODE_CLUMP)
1998 	{
1999 	  tlbl = path0->links[path0->linkc-1];
2000 	  tracelb_link_flowids_inc(tlbl);
2001 	  newp = path0;
2002 	}
2003       else if(br->newnodec == 1)
2004 	{
2005 	  /*
2006 	   * (1) allocate a tracelb_link_t structure to keep link state with
2007 	   * (2) add the link to the path being probed
2008 	   * (3) note the successful probes with the link state
2009 	   */
2010 	  if((tlbl = tracelb_link_alloc(state, link, path0)) == NULL ||
2011 	     tracelb_path_add_link(path0, tlbl) != 0)
2012 	    {
2013 	      goto err;
2014 	    }
2015 	  newp = path0;
2016 	}
2017       else
2018 	{
2019 	  if((newp = tracelb_path_alloc(state, 0)) == NULL ||
2020 	     (tlbl = tracelb_link_alloc(state, link, newp)) == NULL ||
2021 	     tracelb_path_add_link(newp, tlbl) != 0 ||
2022 	     tracelb_path_add_back(newp, path0) != 0)
2023 	    {
2024 	      goto err;
2025 	    }
2026 
2027 	  /* the distance to the root is one more than the last path segment */
2028 	  newp->distance = path0->distance + 1;
2029 	  tracelb_paths_sort(state);
2030 	}
2031 
2032       tracelb_link_flowids_add_list(tlbl, flowids);
2033       flowids = NULL;
2034 
2035       if(tracelb_link_continue(trace, newp, link) == 0)
2036 	continue;
2037 
2038       if(splice == 0)
2039 	{
2040 	  if(tracelb_path_add(state, newp) != 0)
2041 	    {
2042 	      goto err;
2043 	    }
2044 	}
2045       else
2046 	{
2047 	  if(tracelb_paths_splice_bynode(state, newp) != 0)
2048 	    {
2049 	      goto err;
2050 	    }
2051 	}
2052     }
2053 
2054   tracelb_branch_free(state, br);
2055   tracelb_paths_assert(state);
2056   return 0;
2057 
2058  err:
2059   return -1;
2060 }
2061 
tracelb_process_clump(scamper_task_t * task,tracelb_branch_t * br)2062 static int tracelb_process_clump(scamper_task_t *task, tracelb_branch_t *br)
2063 {
2064   scamper_tracelb_t *trace = tracelb_getdata(task);
2065   tracelb_state_t *state = tracelb_getstate(task);
2066   tracelb_path_t *path0 = br->path;
2067   scamper_tracelb_probe_t *probe;
2068   scamper_tracelb_node_t *node;
2069   scamper_tracelb_link_t *link;
2070   scamper_tracelb_probeset_t *set = NULL;
2071   tracelb_probe_t *pr;
2072   tracelb_link_t *tlbl = NULL;
2073   slist_t *flowids = NULL;
2074   uint16_t flowid;
2075   int i, j, k, halt = 0;
2076 
2077   tracelb_paths_dump(state);
2078 
2079   if(path0 == NULL)
2080     {
2081       assert(trace->nodec == 1);
2082       for(i=0; i<br->newnodec; i++)
2083 	{
2084 	  node = br->newnodes[i]->node;
2085 	  if(scamper_tracelb_node_cmp(trace->nodes[0], node) == 0)
2086 	    {
2087 	      scamper_debug(__func__, "node %d loop", i);
2088 	      halt = 1;
2089 	    }
2090 	  scamper_tracelb_node_free(br->newnodes[i]->node);
2091 	  free(br->newnodes[i]); br->newnodes[i] = NULL;
2092 	}
2093       free(br->newnodes);
2094       br->newnodes = NULL;
2095       br->newnodec = 0;
2096 
2097       if((path0 = tracelb_path_alloc(state, 0)) == NULL)
2098 	goto err;
2099       br->path = path0;
2100     }
2101   else if(br->newnodec > 0)
2102     {
2103       /*
2104        * check if any of the nodes would indicate a loop.  this is the only
2105        * thing we use the nodes for, so free them as we go.
2106        */
2107       for(i=0; i<br->newnodec; i++)
2108 	{
2109 	  node = br->newnodes[i]->node;
2110 	  if(halt == 0 && tracelb_isloop_addr(path0, node->addr) != NULL)
2111 	    {
2112 	      scamper_debug(__func__, "node %d loop", i);
2113 	      halt = 1;
2114 	    }
2115 	  scamper_tracelb_node_free(br->newnodes[i]->node);
2116 	  free(br->newnodes[i]); br->newnodes[i] = NULL;
2117 	}
2118       free(br->newnodes);
2119       br->newnodes = NULL;
2120       br->newnodec = 0;
2121     }
2122   else if(path0->linkc > 0)
2123     {
2124       link = path0->links[path0->linkc-1]->link;
2125       if(trace->gaplimit < 2)
2126 	{
2127 	  halt = 1;
2128 	}
2129       else if(link->to == NULL && link->hopc+1 >= trace->gaplimit)
2130 	{
2131 	  k = 1;
2132 	  for(i=link->hopc-1; i>=0; i--)
2133 	    {
2134 	      if(k == trace->gaplimit)
2135 		break;
2136 
2137 	      set = link->sets[i];
2138 	      for(j=0; j<set->probec; j++)
2139 		if(set->probes[j]->rxc > 0)
2140 		  break;
2141 
2142 	      if(j == set->probec)
2143 		k++;
2144 	    }
2145 	  if(k == trace->gaplimit)
2146 	    halt = 1;
2147 	}
2148     }
2149 
2150   /* allocate a list to store flowids in */
2151   if((flowids = slist_alloc()) == NULL)
2152     {
2153       printerror(__func__, "could not alloc list");
2154       goto err;
2155     }
2156 
2157   /* allocate a probeset and add all probes sent in the round */
2158   if((set = scamper_tracelb_probeset_alloc()) == NULL)
2159     {
2160       printerror(__func__, "could not alloc probeset");
2161       goto err;
2162     }
2163 
2164   flowid = br->probes[0]->probe->flowid; k = 0;
2165   for(i=0; i<=br->probec; i++)
2166     {
2167       /*
2168        * record the flowid in the branch so it will be used for further
2169        * probing, unless its already been noted the branch will not be
2170        * probed further.
2171        */
2172       if(i == br->probec || flowid != br->probes[i]->probe->flowid)
2173 	{
2174 	  if(halt == 0 && k<i)
2175 	    {
2176 	      pr = br->probes[k];
2177 	      if(pr->mode != MODE_PERPACKET &&
2178 		 tracelb_flowids_list_add(flowids, pr) != 0)
2179 		goto err;
2180 	    }
2181 	  if(i == br->probec)
2182 	    break;
2183 
2184 	  flowid = br->probes[i]->probe->flowid;
2185 	  k = i;
2186 	}
2187 
2188       pr = br->probes[i];
2189       if(pr->mode == MODE_BRINGFWD || pr->mode == MODE_BRINGFWD0)
2190 	{
2191 	  k++;
2192 	  continue;
2193 	}
2194 
2195       probe = pr->probe;
2196       if(scamper_tracelb_probeset_add(set, probe) != 0)
2197 	{
2198 	  printerror(__func__, "could not add probe %d", i);
2199 	  goto err;
2200 	}
2201 
2202       /*
2203        * if any of the responses observed are not a time exceeded message,
2204        * then halt this branch of probing
2205        */
2206       if(halt != 0)
2207 	continue;
2208       for(j=0; j<probe->rxc; j++)
2209 	{
2210 	  if(SCAMPER_TRACELB_REPLY_IS_ICMP_TTL_EXP(probe->rxs[j]) == 0)
2211 	    halt = 1;
2212 	}
2213     }
2214 
2215   /*
2216    * determine if we have to create a new link or if the clump merely extends
2217    * an earlier one
2218    */
2219   if(path0->linkc == 0 || path0->links[path0->linkc-1]->link->to != NULL)
2220     {
2221       if((link = scamper_tracelb_link_alloc()) == NULL)
2222 	{
2223 	  printerror(__func__, "could not alloc link");
2224 	  goto err;
2225 	}
2226 
2227       if(path0->linkc > 0)
2228 	link->from = path0->links[path0->linkc-1]->link->to;
2229       else if(path0->backc > 0)
2230 	link->from = path0->back[0]->links[path0->back[0]->linkc-1]->link->to;
2231       else
2232 	link->from = trace->nodes[0];
2233     }
2234   else
2235     {
2236       tlbl = path0->links[path0->linkc-1];
2237       link = tlbl->link;
2238       tracelb_link_flowids_inc(tlbl);
2239     }
2240 
2241   /* add the probeset to the link */
2242   if(scamper_tracelb_link_probeset(link, set) != 0)
2243     {
2244       printerror(__func__, "could not add probeset");
2245       goto err;
2246     }
2247 
2248   /* if this is the first probeset to be added, then record the link */
2249   if(link->hopc == 1)
2250     {
2251       if(scamper_tracelb_link_add(trace, link) != 0)
2252 	{
2253 	  printerror(__func__, "could not add new link");
2254 	  goto err;
2255 	}
2256 
2257       if((tlbl = tracelb_link_alloc(state, link, path0)) == NULL ||
2258 	 tracelb_path_add_link(path0, tlbl) != 0)
2259 	{
2260 	  goto err;
2261 	}
2262     }
2263 
2264   if(halt == 0)
2265     {
2266       tracelb_link_flowids_add_list(tlbl, flowids);
2267 
2268       /* put the branch back in for probing */
2269       br->mode = MODE_CLUMP;
2270       tracelb_branch_reset(br);
2271       timeval_add_cs(&br->next_tx, &br->last_tx, trace->wait_probe);
2272       if(tracelb_branch_waiting(state, br) != 0)
2273 	goto err;
2274     }
2275   else
2276     {
2277       if(flowids != NULL) slist_free_cb(flowids, free);
2278       tracelb_branch_free(state, br);
2279     }
2280 
2281   return 0;
2282 
2283  err:
2284   return -1;
2285 }
2286 
tracelb_process_perpacket(scamper_task_t * task,tracelb_branch_t * br)2287 static int tracelb_process_perpacket(scamper_task_t *task,tracelb_branch_t *br)
2288 {
2289   scamper_tracelb_t *trace = tracelb_getdata(task);
2290   tracelb_state_t *state = tracelb_getstate(task);
2291 
2292   br->mode = MODE_PERPACKET;
2293   tracelb_branch_reset(br);
2294   timeval_add_cs(&br->next_tx, &br->last_tx, trace->wait_probe);
2295   if(tracelb_branch_active(state, br) != 0)
2296     return -1;
2297 
2298   return 0;
2299 }
2300 
tracelb_process_probes(scamper_task_t * task,tracelb_branch_t * br)2301 static void tracelb_process_probes(scamper_task_t *task, tracelb_branch_t *br)
2302 {
2303   tracelb_state_t *state = tracelb_getstate(task);
2304   tracelb_probe_t *pr;
2305   scamper_addr_t *addr = NULL;
2306   scamper_tracelb_probe_t *probe;
2307   scamper_tracelb_reply_t *reply;
2308   uint16_t flowid;
2309   uint8_t mode;
2310   int i, n = 0, c = 0, x = 0, hopprobe = 0;
2311 
2312 #ifndef NDEBUG
2313   char *ms;
2314   for(i=0; i<br->probec; i++)
2315     {
2316       pr = br->probes[i]; probe = pr->probe;
2317       if(pr->mode == MODE_FIRSTADDR)      ms = "firstaddr";
2318       else if(pr->mode == MODE_FIRSTHOP)  ms = "firsthop";
2319       else if(pr->mode == MODE_HOPPROBE)  ms = "hopprobe";
2320       else if(pr->mode == MODE_BRINGFWD)  ms = "bringfwd";
2321       else if(pr->mode == MODE_BRINGFWD0) ms = "bringfwd0";
2322       else if(pr->mode == MODE_CLUMP)     ms = "clump";
2323       else if(pr->mode == MODE_PERPACKET) ms = "perpacket";
2324       else                                ms = "???";
2325       scamper_debug(__func__, "flow %d ttl %d rx %d %s",
2326 		    probe->flowid, probe->ttl, probe->rxc, ms);
2327     }
2328 #endif
2329 
2330   /* remove the branch from the active heap, if it is not already removed */
2331   if(br->heapnode != NULL)
2332     heap_delete(state->active, br->heapnode);
2333 
2334   assert(br->mode == MODE_FIRSTHOP  || br->mode == MODE_HOPPROBE ||
2335 	 br->mode == MODE_PERPACKET || br->mode == MODE_CLUMP);
2336 
2337   /*
2338    * first, check the nature of the replies received; i.e. count the
2339    * number of probes which solicited multiple responses, and count the
2340    * number of probes which solicited no response.
2341    *
2342    * we will do this step twice in the face of a branch where we also
2343    * check for per-packet forwarding.  this is because a second
2344    * response may arrive to the last probe (but not any earlier ones)
2345    * but the decision about checking for per-packet behaviour is done
2346    * before the second response arrives.
2347    */
2348   flowid = br->probes[0]->probe->flowid;
2349   for(i=0; i<=br->probec; i++)
2350     {
2351       if(i == br->probec)
2352 	{
2353 	  if(x == 0 && hopprobe != 0)
2354 	    n++;
2355 	  break;
2356 	}
2357 
2358       pr    = br->probes[i];
2359       probe = pr->probe;
2360 
2361       if(probe->flowid != flowid)
2362 	{
2363 	  /*
2364 	   * if a unique flowid had no response (even with multiple
2365 	   * attempts) then make a note of that.
2366 	   */
2367 	  if(x == 0 && hopprobe != 0)
2368 	    n++;
2369 
2370 	  flowid = probe->flowid;
2371 	  x = 0; hopprobe = 0; addr = NULL;
2372 	}
2373 
2374       /*
2375        * ignore what happened to probes if it isn't a probe that was
2376        * trying to enumerate hops.
2377        */
2378       if(pr->mode == MODE_HOPPROBE || pr->mode == MODE_FIRSTHOP ||
2379 	 pr->mode == MODE_CLUMP)
2380 	{
2381 	  hopprobe = 1;
2382 
2383 	  /* make a note that this probe got a response */
2384 	  if(probe->rxc > 0)
2385 	    x++;
2386 
2387 	  /* make a note that this probe got multiple responses */
2388 	  if(probe->rxc > 1)
2389 	    c++;
2390 
2391 	  /*
2392 	   * check if probes with the same flowid got responses
2393 	   * from different addresses.
2394 	   */
2395 	  if(probe->rxc == 1)
2396 	    {
2397 	      reply = probe->rxs[0];
2398 	      if(addr == NULL)
2399 		addr = reply->reply_from;
2400 	      else if(scamper_addr_cmp(addr, reply->reply_from) != 0)
2401 		c++;
2402 	    }
2403 	}
2404     }
2405 
2406   if(br->mode == MODE_PERPACKET)
2407     {
2408       if(br->k <= k(state, 2) || c > 0 || n > 0)
2409 	mode = MODE_CLUMP;
2410       else
2411 	mode = MODE_HOPPROBE;
2412     }
2413   else
2414     {
2415       if(c > 0 || n > 0)
2416 	{
2417 	  mode = MODE_CLUMP;
2418 	}
2419       else if(br->newnodec > 1)
2420 	{
2421 	  if(br->mode == MODE_HOPPROBE)
2422 	    mode = MODE_PERPACKET;
2423 	  else
2424 	    mode = MODE_CLUMP;
2425 	}
2426       else
2427 	{
2428 	  mode = MODE_HOPPROBE;
2429 	}
2430     }
2431 
2432   if(mode == MODE_HOPPROBE)
2433     tracelb_process_hops(task, br);
2434   else if(mode == MODE_CLUMP)
2435     tracelb_process_clump(task, br);
2436   else
2437     tracelb_process_perpacket(task, br);
2438 
2439   tracelb_paths_dump(state);
2440   tracelb_queue(task);
2441   return;
2442 }
2443 
2444 /*
2445  * tracelb_path_flowid
2446  *
2447  * determine if the path has a usable flowid that does not need to be
2448  * brought forward.
2449  */
tracelb_path_flowid(tracelb_path_t * path,scamper_tracelb_probe_t * probe)2450 static int tracelb_path_flowid(tracelb_path_t *path,
2451 			       scamper_tracelb_probe_t *probe)
2452 {
2453   tracelb_flowid_t *flowid;
2454   int i;
2455 
2456   /* check the path for a flow-id */
2457   if(path->linkc > 0)
2458     {
2459       i = path->linkc - 1;
2460       for(;;)
2461 	{
2462 	  if((flowid = tracelb_link_flowid_get(path->links[i])) != NULL)
2463 	    {
2464 	      probe->flowid = flowid->id;
2465 	      probe->ttl    = flowid->ttl + 1;
2466 	      free(flowid);
2467 	      for(i=i+1; i<path->linkc; i++)
2468 		probe->ttl += path->links[i]->link->hopc;
2469 	      return 1;
2470 	    }
2471 
2472 	  if(i == 0)
2473 	    break;
2474 	  i--;
2475 	}
2476     }
2477 
2478   /*
2479    * if the path does not have a flow-id, descend through any prior paths
2480    * that only have one forward branch (to this path) for a usable flowid.
2481    * if one is found, then as the recursive function unwinds, the ttl is
2482    * incremented by the ttl of each path segment leading to it.
2483    */
2484   for(i=0; i<path->backc; i++)
2485     {
2486       if(path->back[i]->fwdc == 1 &&
2487 	 tracelb_path_flowid(path->back[i], probe) != 0)
2488 	{
2489 	  for(i=0; i<path->linkc; i++)
2490 	    probe->ttl += path->links[i]->link->hopc;
2491 
2492 	  return 1;
2493 	}
2494     }
2495 
2496   return 0;
2497 }
2498 
2499 /*
2500  * tracelb_probe_vals
2501  *
2502  * for the current path being traced, determine the flowid and
2503  * ttl values to use next.
2504  *
2505  */
tracelb_probe_vals(scamper_task_t * task,tracelb_branch_t * branch,tracelb_probe_t * pr)2506 static int tracelb_probe_vals(scamper_task_t *task, tracelb_branch_t *branch,
2507 			      tracelb_probe_t *pr)
2508 {
2509   scamper_tracelb_t *trace = tracelb_getdata(task);
2510   tracelb_state_t *state = tracelb_getstate(task);
2511   tracelb_path_t *path0, *path;
2512   size_t len;
2513   int i;
2514 
2515 #ifndef NDEBUG
2516   scamper_tracelb_node_t *node;
2517   char from[64], to[64];
2518 #endif
2519 
2520   tracelb_paths_assert(state);
2521 
2522   /* the current path being traced is found in position zero */
2523   path0 = branch->path;
2524 
2525   /* this will be the first attempt made */
2526   pr->mode = branch->mode;
2527   pr->probe->attempt = 0;
2528 
2529   /*
2530    * look through the links traced so far in this path, starting at the most
2531    * recent link prior to the current link.  if a flow-id is found in
2532    * this link sequence then we can just probe with that at the current TTL
2533    * as there is some confidence there are no branches in this sequence.
2534    */
2535   if(tracelb_path_flowid(path0, pr->probe) != 0)
2536     {
2537       /* this flowid has been brought all the way forward */
2538       for(i=0; i<branch->bringfwdc; i++)
2539 	branch->bringfwd[i]->k = 0;
2540 
2541       if(path0->linkc > 0)
2542 	pr->link = path0->links[path0->linkc-1];
2543       else if(path0->backc > 0)
2544 	pr->link = path0->back[0]->links[path0->back[0]->linkc-1];
2545       else
2546 	return -1;
2547       return 0;
2548     }
2549 
2550   assert(trace->nodec > 0);
2551   assert(trace->nodes[0] != NULL);
2552   assert(trace->nodes[0]->linkc > 0);
2553 
2554   /*
2555    * if there are no prior path segments, then the probe is going to have
2556    * a brand new flowid
2557    */
2558   if(path0->back == NULL && trace->nodes[0]->linkc == 1)
2559     {
2560       pr->probe->ttl = trace->firsthop + 1;
2561       for(i=0; i<path0->linkc; i++)
2562 	pr->probe->ttl += path0->links[i]->link->hopc;
2563 
2564       pr->probe->flowid = state->flowid_next++;
2565       pr->link          = path0->links[path0->linkc-1];
2566       return 0;
2567     }
2568 
2569   /* build the set of paths leading to the current path, if necessary */
2570   if(branch->bringfwd == NULL || branch->bringfwd[0]->path != path0)
2571     {
2572       /* make sure any existing state is removed */
2573       tracelb_bringfwd_free(branch);
2574 
2575       /* allocate enough entries to record all segments, if necessary */
2576       len = sizeof(tracelb_bringfwd_t *) * state->pathc;
2577       if((branch->bringfwd = malloc_zero(len)) == NULL)
2578 	{
2579 	  printerror(__func__, "could not malloc set");
2580 	  return -1;
2581 	}
2582 
2583       /*
2584        * do a depth-first traversal of the path, figuring out what sequence
2585        * of path segments are visited on the way up.  sort the sequence
2586        * into the distance from the root of the tree, so that the root of
2587        * the tree is the last item in the array, and the current path is
2588        * found at the head.
2589        */
2590       tracelb_set_visited0(state);
2591       if(tracelb_bringfwd_dft(branch, path0) == -1)
2592 	return -1;
2593       array_qsort((void **)branch->bringfwd, branch->bringfwdc,
2594 		  (array_cmp_t)tracelb_bringfwd_cmp);
2595 
2596 #ifndef NDEBUG
2597       if(branch->bringfwdc > 0)
2598 	{
2599 	  i = branch->bringfwdc - 1;
2600 	  for(;;)
2601 	    {
2602 	      path = branch->bringfwd[i]->path;
2603 	      if(path->linkc > 0)
2604 		{
2605 		  node = path->links[0]->link->from;
2606 		  if(node->addr != NULL)
2607 		    scamper_addr_tostr(node->addr, from, sizeof(from));
2608 		  else
2609 		    snprintf(from, sizeof(from), "*");
2610 		  if((node = path->links[path->linkc-1]->link->to) != NULL)
2611 		    scamper_addr_tostr(node->addr, to, sizeof(to));
2612 		  else
2613 		    snprintf(to, sizeof(to), "*");
2614 		  scamper_debug(__func__, "%d %s %s",path->distance,from,to);
2615 		}
2616 
2617 	      if(i == 0)
2618 		break;
2619 	      i--;
2620 	    }
2621 	}
2622 #endif
2623     }
2624 
2625   /*
2626    * sanity check that the table starts with the current path being probed
2627    * and extends back to the first path segment
2628    */
2629   assert(branch->bringfwd[0]->path == path0);
2630   assert(branch->bringfwdc > 0);
2631   assert(branch->bringfwd[branch->bringfwdc-1]->path->backc == 0);
2632 
2633   /* reset the visited members to zero */
2634   for(i=0; i<branch->bringfwdc; i++)
2635     branch->bringfwd[i]->path->visited = 0;
2636 
2637   /* we now have to bring a flowid forward through the path to probe a hop */
2638   pr->mode = MODE_BRINGFWD;
2639 
2640   /*
2641    * descend through the table of paths, checking to see if there is any
2642    * flowid available for use
2643    */
2644   for(i=1; i<branch->bringfwdc; i++)
2645     {
2646       path = branch->bringfwd[i]->path;
2647       if(path->visited != 0)
2648 	{
2649 	  continue;
2650 	}
2651 
2652       if(tracelb_path_flowid(path, pr->probe) != 0)
2653 	{
2654 	  if(path->linkc != 0)
2655 	    pr->link = path->links[path->linkc-1];
2656 	  else
2657 	    pr->link = path->back[0]->links[path->back[0]->linkc-1];
2658 	  goto done;
2659 	}
2660     }
2661 
2662   /*
2663    * there is no flowid available for use.
2664    * create a new one to bring forward
2665    */
2666   pr->probe->flowid = state->flowid_next++;
2667   pr->probe->ttl = trace->firsthop + 1;
2668 
2669   if(trace->nodes[0]->linkc == 1)
2670     {
2671       path = branch->bringfwd[branch->bringfwdc-1]->path;
2672       pr->link = path->links[path->linkc-1];
2673 
2674       for(i=0; i<path->linkc; i++)
2675 	pr->probe->ttl += path->links[i]->link->hopc;
2676     }
2677   else
2678     {
2679       pr->mode = MODE_BRINGFWD0;
2680       pr->link = NULL;
2681     }
2682 
2683  done:
2684   scamper_debug(__func__, "bringfwd: ttl %d flowid %d",
2685 		pr->probe->ttl, pr->probe->flowid);
2686   return 0;
2687 }
2688 
tracelb_branch_cancel(scamper_task_t * task,tracelb_branch_t * br)2689 static void tracelb_branch_cancel(scamper_task_t *task, tracelb_branch_t *br)
2690 {
2691   scamper_debug(__func__, "cancelling path %p", br->path);
2692   tracelb_process_probes(task, br);
2693   return;
2694 }
2695 
2696 /*
2697  * handleicmp_reply
2698  *
2699  * add details of the reply to the link
2700  */
handleicmp_reply(const scamper_icmp_resp_t * ir,scamper_addr_t * from)2701 static scamper_tracelb_reply_t *handleicmp_reply(const scamper_icmp_resp_t *ir,
2702 						 scamper_addr_t *from)
2703 {
2704   scamper_tracelb_reply_t *reply;
2705 
2706   if((reply = scamper_tracelb_reply_alloc(from)) == NULL)
2707     {
2708       printerror(__func__, "could not allocate reply");
2709       return NULL;
2710     }
2711 
2712   timeval_cpy(&reply->reply_rx, &ir->ir_rx);
2713   reply->reply_ipid       = ir->ir_ip_id;
2714   reply->reply_icmp_type  = ir->ir_icmp_type;
2715   reply->reply_icmp_code  = ir->ir_icmp_code;
2716   reply->reply_icmp_q_ttl = ir->ir_inner_ip_ttl;
2717   reply->reply_icmp_q_tos = ir->ir_inner_ip_tos;
2718 
2719   if(ir->ir_ip_ttl >= 0)
2720     {
2721       reply->reply_ttl    = (uint8_t)ir->ir_ip_ttl;
2722       reply->reply_flags |= SCAMPER_TRACELB_REPLY_FLAG_REPLY_TTL;
2723     }
2724 
2725   if(ir->ir_ext != NULL &&
2726      scamper_icmpext_parse(&reply->reply_icmp_ext,
2727 			   ir->ir_ext, ir->ir_extlen) != 0)
2728     {
2729       scamper_debug(__func__, "could not include icmp extension data");
2730       scamper_tracelb_reply_free(reply);
2731       return NULL;
2732     }
2733 
2734   return reply;
2735 }
2736 
handletcp_reply(const scamper_dl_rec_t * dl,scamper_addr_t * from)2737 static scamper_tracelb_reply_t *handletcp_reply(const scamper_dl_rec_t *dl,
2738 						scamper_addr_t *from)
2739 {
2740   scamper_tracelb_reply_t *reply;
2741 
2742   if((reply = scamper_tracelb_reply_alloc(from)) == NULL)
2743     {
2744       printerror(__func__, "could not allocate reply");
2745       return NULL;
2746     }
2747 
2748   timeval_cpy(&reply->reply_rx, &dl->dl_tv);
2749   reply->reply_flags      = SCAMPER_TRACELB_REPLY_FLAG_TCP;
2750   reply->reply_flags     |= SCAMPER_TRACELB_REPLY_FLAG_REPLY_TTL;
2751   reply->reply_ttl        = dl->dl_ip_ttl;
2752   reply->reply_tcp_flags  = dl->dl_tcp_flags;
2753   reply->reply_ipid       = dl->dl_ip_id;
2754 
2755   return reply;
2756 }
2757 
2758 /*
2759  * handleicmp_firstaddr
2760  *
2761  * handle recording the first address discovered in the IP path.
2762  */
handleicmp_firstaddr(scamper_task_t * task,scamper_icmp_resp_t * ir,tracelb_probe_t * pr,scamper_addr_t * from)2763 static void handleicmp_firstaddr(scamper_task_t *task, scamper_icmp_resp_t *ir,
2764 				 tracelb_probe_t *pr, scamper_addr_t *from)
2765 {
2766   scamper_tracelb_t *trace = tracelb_getdata(task);
2767   tracelb_state_t *state = tracelb_getstate(task);
2768   tracelb_branch_t *branch = pr->branch;
2769   scamper_tracelb_node_t *node = NULL;
2770 
2771   assert(pr->probe->ttl == trace->firsthop);
2772   assert(trace->nodes == NULL);
2773 
2774   heap_delete(state->active, branch->heapnode);
2775 
2776   /* record the details of the first hop */
2777   if((node = scamper_tracelb_node_alloc(from)) == NULL ||
2778      tracelb_node_ptr(task, node) != 0 ||
2779      scamper_tracelb_node_add(trace, node) != 0)
2780     {
2781       printerror(__func__, "could not alloc node");
2782       goto err;
2783     }
2784   if(SCAMPER_ICMP_RESP_IS_TTL_EXP(ir) || SCAMPER_ICMP_RESP_IS_UNREACH(ir))
2785     {
2786       node->flags |= SCAMPER_TRACELB_NODE_FLAG_QTTL;
2787       node->q_ttl  = ir->ir_inner_ip_ttl;
2788     }
2789   node = NULL;
2790 
2791   /* if we can't probe on, then we're done */
2792   if(SCAMPER_ICMP_RESP_IS_TTL_EXP(ir) == 0 ||
2793      scamper_addr_cmp(trace->dst, from) == 0)
2794     {
2795       tracelb_branch_free(state, branch);
2796       tracelb_queue(task);
2797       return;
2798     }
2799 
2800   /* we've got the first address; now probe active branches from here */
2801   branch->mode = MODE_FIRSTHOP;
2802   tracelb_branch_reset(branch);
2803   timeval_add_cs(&branch->next_tx, &branch->last_tx, trace->wait_probe);
2804   if(tracelb_branch_waiting(state, branch) != 0)
2805     goto err;
2806 
2807   tracelb_queue(task);
2808   return;
2809 
2810  err:
2811   scamper_tracelb_node_free(node);
2812   tracelb_branch_free(state, branch);
2813   tracelb_handleerror(task, errno);
2814   return;
2815 }
2816 
hopprobe_handlereply(scamper_task_t * task,tracelb_probe_t * pr,scamper_tracelb_reply_t * reply)2817 static int hopprobe_handlereply(scamper_task_t *task, tracelb_probe_t *pr,
2818 				scamper_tracelb_reply_t *reply)
2819 {
2820   tracelb_branch_t *branch = pr->branch;
2821   scamper_tracelb_t *trace = tracelb_getdata(task);
2822   tracelb_state_t *state = tracelb_getstate(task);
2823 
2824   if(scamper_tracelb_probe_reply(pr->probe, reply) != 0)
2825     {
2826       printerror(__func__, "could not add reply to probe");
2827       scamper_tracelb_reply_free(reply);
2828       return -1;
2829     }
2830 
2831   /*
2832    * if this was not the most recent probe to be sent, or it was not the first
2833    * response to the probe, we're done.
2834    */
2835   if(branch->probes[branch->probec-1] != pr || pr->probe->rxc > 1)
2836     {
2837       return 0;
2838     }
2839 
2840   if(tracelb_newnode_find(branch, reply) == NULL)
2841     {
2842       if(tracelb_newnode_add(branch, pr->probe) != 0)
2843 	return -1;
2844 
2845       if(branch->n == branch->newnodec)
2846 	{
2847 	  scamper_debug(__func__, "branch->n %d", branch->n);
2848 	  branch->n++;
2849 	}
2850     }
2851 
2852   heap_delete(state->active, branch->heapnode);
2853   branch->k++;
2854 
2855   /*
2856    * if a reply from the destination is received, assume that there is only
2857    * one link used to forward to the destination (in this case the directly
2858    * connected interface) and process that link now.
2859    *
2860    * otherwise, if the hop has been probed enough to the appropriate level
2861    * of confidence, process the links discovered.
2862    */
2863   if(branch->n >= TRACELB_CONFIDENCE_MAX_N ||
2864      (scamper_addr_cmp(reply->reply_from, trace->dst) == 0 &&
2865       branch->newnodec < 2) ||
2866      branch->k >= k(state, branch->n))
2867     {
2868       tracelb_process_probes(task, branch);
2869     }
2870   else
2871     {
2872       timeval_add_cs(&branch->next_tx, &branch->last_tx, trace->wait_probe);
2873       if(tracelb_branch_active(state, branch) != 0)
2874 	return -1;
2875 
2876       tracelb_queue(task);
2877     }
2878 
2879   return 0;
2880 }
2881 
2882 /*
2883  * handleicmp_hopprobe
2884  *
2885  * handle processing a reply, including recording details of the reply and
2886  * deciding what to do next
2887  */
handleicmp_hopprobe(scamper_task_t * task,scamper_icmp_resp_t * ir,tracelb_probe_t * pr,scamper_addr_t * irfrom)2888 static void handleicmp_hopprobe(scamper_task_t *task, scamper_icmp_resp_t *ir,
2889 				tracelb_probe_t *pr, scamper_addr_t *irfrom)
2890 {
2891   scamper_tracelb_reply_t *reply;
2892 
2893   if(pr->branch == NULL)
2894     return;
2895 
2896   /*
2897    * generate a reply to store with the probe, and then record the reply with
2898    * the probe
2899    */
2900   if((reply = handleicmp_reply(ir, irfrom)) == NULL ||
2901      hopprobe_handlereply(task, pr, reply) != 0)
2902     {
2903       tracelb_handleerror(task, errno);
2904     }
2905 
2906   return;
2907 }
2908 
2909 /*
2910  * handleicmp_perpacket
2911  *
2912  * this routine is used to check if a load balancing router at a particular
2913  * hop forwards on a per-packet basis.
2914  */
handleicmp_perpacket(scamper_task_t * task,scamper_icmp_resp_t * ir,tracelb_probe_t * pr,scamper_addr_t * from)2915 static void handleicmp_perpacket(scamper_task_t *task, scamper_icmp_resp_t *ir,
2916 				 tracelb_probe_t *pr, scamper_addr_t *from)
2917 {
2918   scamper_tracelb_t *trace     = tracelb_getdata(task);
2919   tracelb_state_t   *state     = tracelb_getstate(task);
2920   tracelb_branch_t  *branch    = pr->branch;
2921   scamper_tracelb_node_t *node = branch->newnodes[0]->node;
2922   scamper_tracelb_reply_t *reply;
2923   int process = 0;
2924 
2925   if(pr->branch == NULL)
2926     return;
2927 
2928   assert(branch->newnodec > 1);
2929 
2930   if((reply = handleicmp_reply(ir, from)) == NULL)
2931     {
2932       goto err;
2933     }
2934   if(scamper_tracelb_probe_reply(pr->probe, reply) != 0)
2935     {
2936       scamper_tracelb_reply_free(reply);
2937       goto err;
2938     }
2939 
2940   if(pr->probe->rxc == 1)
2941     branch->k++;
2942 
2943   if(tracelb_cmp_node2reply(node, reply) != 0)
2944     {
2945       process = 1;
2946     }
2947   else if(pr->probe->rxc == 1 && branch->k == k(state, 2))
2948     {
2949       process = 1;
2950       branch->k++;
2951     }
2952 
2953   if(process == 0)
2954     {
2955       heap_delete(state->active, branch->heapnode);
2956       timeval_add_cs(&branch->next_tx, &branch->last_tx, trace->wait_probe);
2957       if(tracelb_branch_active(state, branch) != 0)
2958 	goto err;
2959       tracelb_queue(task);
2960     }
2961   else
2962     {
2963       tracelb_process_probes(task, pr->branch);
2964     }
2965 
2966   return;
2967 
2968  err:
2969   tracelb_handleerror(task, errno);
2970   return;
2971 }
2972 
2973 /*
2974  * handleicmp_bringfwd
2975  *
2976  * handle an ICMP reply in the bringfwd mode.
2977  */
handleicmp_bringfwd(scamper_task_t * task,scamper_icmp_resp_t * ir,tracelb_probe_t * pr,scamper_addr_t * from)2978 static void handleicmp_bringfwd(scamper_task_t *task, scamper_icmp_resp_t *ir,
2979 				tracelb_probe_t *pr, scamper_addr_t *from)
2980 {
2981   scamper_tracelb_t *trace = tracelb_getdata(task);
2982   tracelb_state_t *state = tracelb_getstate(task);
2983   tracelb_branch_t *branch = pr->branch;
2984   tracelb_probe_t *cpr;
2985   scamper_tracelb_reply_t *reply;
2986   scamper_tracelb_link_t findme;
2987   scamper_tracelb_node_t node;
2988   tracelb_link_t *tlbl;
2989   int i, rx, set, n;
2990 
2991 #ifdef HAVE_SCAMPER_DEBUG
2992   char f[64], t[64];
2993 #endif
2994 
2995   if((reply = handleicmp_reply(ir, from)) == NULL)
2996     {
2997       goto err;
2998     }
2999   if(scamper_tracelb_probe_reply(pr->probe, reply) != 0)
3000     {
3001       scamper_tracelb_reply_free(reply);
3002       goto err;
3003     }
3004 
3005   /*
3006    * check that the reply is for the current flowid/ttl combination, and
3007    * that the reply is the first reply for this flowid/ttl combination
3008    */
3009   cpr = branch->probes[branch->probec-1];
3010   if(pr->mode != cpr->mode ||
3011      pr->probe->flowid != cpr->probe->flowid ||
3012      pr->probe->ttl != cpr->probe->ttl)
3013     {
3014       return;
3015     }
3016   for(i=0, rx=0; i<=cpr->probe->attempt; i++)
3017     {
3018       rx += branch->probes[branch->probec-1-i]->probe->rxc;
3019     }
3020   if(rx != 1)
3021     {
3022       return;
3023     }
3024 
3025   memset(&node, 0, sizeof(node));
3026   if(SCAMPER_ICMP_RESP_IS_TTL_EXP(ir) || SCAMPER_ICMP_RESP_IS_UNREACH(ir))
3027     {
3028       node.q_ttl |= ir->ir_inner_ip_ttl;
3029       node.flags  = SCAMPER_TRACELB_NODE_FLAG_QTTL;
3030     }
3031   node.addr = from;
3032   findme.to = &node;
3033 
3034   assert(pr->mode == MODE_BRINGFWD || pr->mode == MODE_BRINGFWD0);
3035   if(pr->mode == MODE_BRINGFWD)
3036     findme.from = pr->link->link->to;
3037   else
3038     findme.from = trace->nodes[0];
3039 
3040   scamper_debug(__func__, "reply link %s %s",
3041 		scamper_addr_tostr(findme.from->addr, f, sizeof(f)),
3042 		scamper_addr_tostr(findme.to->addr, t, sizeof(t)));
3043 
3044   if((tlbl = tracelb_link_find(state, &findme)) == NULL)
3045     {
3046       scamper_debug(__func__, "no matching link in trace");
3047       set = 1;
3048     }
3049   else
3050     {
3051       if(tracelb_link_flowid_add_tail(tlbl, pr->probe) != 0)
3052 	goto err;
3053 
3054       for(i=0; i<branch->bringfwdc; i++)
3055 	{
3056 	  if(branch->bringfwd[i]->path->linkc == 0)
3057 	    continue;
3058 
3059 	  if(branch->bringfwd[i]->path->links[0] == tlbl)
3060 	    break;
3061 	}
3062 
3063       if(i != branch->bringfwdc)
3064 	set = 0;
3065       else
3066 	set = 1;
3067     }
3068 
3069   /*
3070    * if after trying to bring a flowid past a particular router we can't,
3071    * stop trying
3072    */
3073   if(pr->mode == MODE_BRINGFWD)
3074     {
3075       if(tracelb_bringfwd_set(state, branch, pr->link, set) != 0)
3076 	{
3077 	  tracelb_branch_cancel(task, branch);
3078 	  branch = NULL;
3079 	}
3080     }
3081   else
3082     {
3083       assert(pr->mode == MODE_BRINGFWD0);
3084       n = TRACELB_CONFIDENCE_NLIMIT(trace->nodes[0]->linkc+2);
3085 
3086       if(set == 0)
3087 	branch->bringfwd0 = 0;
3088       else
3089 	branch->bringfwd0++;
3090 
3091       scamper_debug(__func__, "i 0 k %d : %d", branch->bringfwd0, k(state, n));
3092 
3093       if(branch->bringfwd0 >= k(state, n))
3094 	{
3095 	  tracelb_branch_cancel(task, branch);
3096 	  branch = NULL;
3097 	}
3098     }
3099 
3100   if(branch != NULL)
3101     {
3102       heap_delete(state->active, branch->heapnode);
3103       timeval_add_cs(&branch->next_tx, &branch->last_tx, trace->wait_probe);
3104       if(tracelb_branch_active(state, branch) != 0)
3105 	goto err;
3106     }
3107 
3108   tracelb_paths_dump(state);
3109   tracelb_queue(task);
3110   return;
3111 
3112  err:
3113   tracelb_handleerror(task, errno);
3114   return;
3115 }
3116 
do_tracelb_handle_icmp(scamper_task_t * task,scamper_icmp_resp_t * ir)3117 static void do_tracelb_handle_icmp(scamper_task_t *task,
3118 				   scamper_icmp_resp_t *ir)
3119 {
3120   static void (*const func[])(scamper_task_t *, scamper_icmp_resp_t *,
3121 			      tracelb_probe_t *, scamper_addr_t *) = {
3122     NULL,                 /* MODE_RTSOCK    */
3123     NULL,                 /* MODE_DLHDR     */
3124     handleicmp_firstaddr, /* MODE_FIRSTADDR */
3125     handleicmp_hopprobe,  /* MODE_FIRSTHOP  */
3126     handleicmp_hopprobe,  /* MODE_HOPPROBE  */
3127     handleicmp_perpacket, /* MODE_PERPACKET */
3128     handleicmp_bringfwd,  /* MODE_BRINGFWD  */
3129     handleicmp_bringfwd,  /* MODE_BRINGFWD0 */
3130     handleicmp_hopprobe,  /* MODE_CLUMP     */
3131   };
3132   scamper_tracelb_t *trace = tracelb_getdata(task);
3133   tracelb_state_t *state = tracelb_getstate(task);
3134   tracelb_probe_t *pr;
3135   scamper_addr_t *icmpfrom = NULL;
3136   uint16_t id;
3137   uint8_t proto;
3138   void *addr;
3139   int type;
3140 
3141   assert(ir->ir_af == AF_INET || ir->ir_af == AF_INET6);
3142 
3143   /*
3144    * if the first probe has not been sent yet, then this cannot be a reply
3145    * for anything we sent.
3146    */
3147   if(state->id_next == 0)
3148     return;
3149 
3150   /*
3151    * ignore the message if it is received on an fd that we didn't use to send
3152    * it.  this is to avoid recording duplicate replies if an unbound socket
3153    * is in use.
3154    */
3155   if(ir->ir_fd != scamper_fd_fd_get(state->icmp))
3156     return;
3157 
3158   scamper_icmp_resp_print(ir);
3159 
3160   /* if the ICMP type is not something that we care for, then drop it */
3161   if(!((SCAMPER_ICMP_RESP_IS_TTL_EXP(ir) ||
3162 	SCAMPER_ICMP_RESP_IS_UNREACH(ir) ||
3163 	SCAMPER_ICMP_RESP_IS_PACKET_TOO_BIG(ir)) &&
3164        SCAMPER_ICMP_RESP_INNER_IS_SET(ir) && ir->ir_inner_ip_off == 0) &&
3165      !(SCAMPER_TRACELB_TYPE_IS_ICMP(trace) &&
3166        SCAMPER_ICMP_RESP_IS_ECHO_REPLY(ir)))
3167     {
3168       return;
3169     }
3170 
3171   if(SCAMPER_TRACELB_TYPE_IS_UDP(trace))
3172     {
3173       /*
3174        * if the ICMP response does not reference a UDP probe sent from our
3175        * source port to a destination probe we're likely to have probed, then
3176        * ignore the packet
3177        */
3178       if(ir->ir_inner_ip_proto != IPPROTO_UDP)
3179 	return;
3180 
3181       if(trace->type == SCAMPER_TRACELB_TYPE_UDP_DPORT)
3182 	{
3183 	  /* if the dport varies, the sport should match */
3184 	  if(ir->ir_inner_udp_sport != trace->sport)
3185 	    return;
3186 	}
3187       else
3188 	{
3189 	  /* if the sport varies, the dport should match */
3190 	  if(ir->ir_inner_udp_dport != trace->dport)
3191 	    return;
3192 	}
3193 
3194       /* extract the id of the probe */
3195       if(ir->ir_af == AF_INET)
3196 	{
3197 	  if(ntohs(ir->ir_inner_udp_sum) == ir->ir_inner_ip_id &&
3198 	     ir->ir_inner_udp_sum != 0)
3199 	    {
3200 	      id = ntohs(ir->ir_inner_udp_sum) - 1;
3201 	    }
3202 	  else if(ir->ir_inner_ip_id != 0)
3203 	    {
3204 	      id = ir->ir_inner_ip_id - 1;
3205 	    }
3206 	  else
3207 	    {
3208 	      return;
3209 	    }
3210 	}
3211       else if(ir->ir_af == AF_INET6)
3212 	{
3213 	  if(ir->ir_inner_udp_sum == 0)
3214 	    return;
3215 	  id = ntohs(ir->ir_inner_udp_sum) - 1;
3216 	}
3217       else return;
3218     }
3219   else if(SCAMPER_TRACELB_TYPE_IS_ICMP(trace))
3220     {
3221       if(SCAMPER_ICMP_RESP_IS_ECHO_REPLY(ir) == 0)
3222 	{
3223 	  if(ir->ir_af == AF_INET) proto = IPPROTO_ICMP;
3224 	  else if(ir->ir_af == AF_INET6) proto = IPPROTO_ICMPV6;
3225 	  else return;
3226 
3227 	  if(ir->ir_inner_ip_proto != proto          ||
3228 	     ir->ir_inner_icmp_id  != trace->sport   ||
3229 	     ir->ir_inner_icmp_seq >= state->id_next)
3230 	    {
3231 	      return;
3232 	    }
3233 
3234 	  id = ir->ir_inner_icmp_seq;
3235 	}
3236       else
3237 	{
3238 	  if(ir->ir_icmp_id  != trace->sport ||
3239 	     ir->ir_icmp_seq >= state->id_next)
3240 	    {
3241 	      return;
3242 	    }
3243 
3244 	  id = ir->ir_icmp_seq;
3245 	}
3246     }
3247   else if(SCAMPER_TRACELB_TYPE_IS_TCP(trace))
3248     {
3249       /*
3250        * if the ICMP response does not reference a UDP probe sent from our
3251        * source port to a destination probe we're likely to have probed, then
3252        * ignore the packet
3253        */
3254       if(ir->ir_inner_ip_proto != IPPROTO_TCP ||
3255 	 ir->ir_inner_tcp_dport != trace->dport)
3256 	{
3257 	  return;
3258 	}
3259 
3260       if(ir->ir_inner_ip_id != 0)
3261 	id = ir->ir_inner_ip_id - 1;
3262       else
3263 	return;
3264     }
3265   else return;
3266 
3267   /* make sure the id is in range */
3268   if(id >= state->id_next)
3269     {
3270       return;
3271     }
3272   pr = state->probes[id];
3273 
3274   if(pr->branch == NULL)
3275     {
3276       scamper_debug(__func__, "pr->branch is null");
3277       return;
3278     }
3279 
3280   /* get the address of the icmp response */
3281   if(ir->ir_af == AF_INET)
3282     {
3283       type = SCAMPER_ADDR_TYPE_IPV4;
3284       addr = &ir->ir_ip_src.v4;
3285     }
3286   else
3287     {
3288       type = SCAMPER_ADDR_TYPE_IPV6;
3289       addr = &ir->ir_ip_src.v6;
3290     }
3291 
3292   if((icmpfrom = tracelb_addr(state, type, addr)) == NULL)
3293     {
3294       tracelb_handleerror(task, errno);
3295       return;
3296     }
3297 
3298   func[pr->mode](task, ir, pr, icmpfrom);
3299   return;
3300 }
3301 
3302 /*
3303  * handletimeout_firstaddr
3304  *
3305  * if a reply for the first set of probes sent into the network is not
3306  * received, then abandon
3307  */
handletimeout_firstaddr(scamper_task_t * task,tracelb_branch_t * br)3308 static void handletimeout_firstaddr(scamper_task_t *task, tracelb_branch_t *br)
3309 {
3310   scamper_tracelb_t *trace = tracelb_getdata(task);
3311   tracelb_state_t *state = tracelb_getstate(task);
3312   scamper_tracelb_node_t *node = NULL;
3313 
3314   heap_delete(state->active, br->heapnode);
3315 
3316   if((node = scamper_tracelb_node_alloc(NULL)) == NULL ||
3317      tracelb_node_ptr(task, node) != 0 ||
3318      scamper_tracelb_node_add(trace, node) != 0)
3319     {
3320       printerror(__func__, "could not alloc node");
3321       goto err;
3322     }
3323   node = NULL;
3324 
3325   br->mode = MODE_FIRSTHOP;
3326   tracelb_branch_reset(br);
3327   timeval_add_cs(&br->next_tx, &br->last_tx, trace->wait_probe);
3328   if(tracelb_branch_waiting(state, br) != 0)
3329     goto err;
3330 
3331   tracelb_queue(task);
3332   return;
3333 
3334  err:
3335   scamper_tracelb_node_free(node);
3336   tracelb_branch_free(state, br);
3337   tracelb_handleerror(task, errno);
3338   return;
3339 }
3340 
3341 /*
3342  * handletimeout_abandon
3343  *
3344  * if no route is received in response to a route socket query or datalink
3345  * header request, then abandon
3346  */
handletimeout_abandon(scamper_task_t * task,tracelb_branch_t * br)3347 static void handletimeout_abandon(scamper_task_t *task, tracelb_branch_t *br)
3348 {
3349   tracelb_state_t *state = tracelb_getstate(task);
3350   heap_delete(state->active, br->heapnode);
3351   tracelb_branch_free(state, br);
3352   tracelb_queue(task);
3353   return;
3354 }
3355 
3356 /*
3357  * handletimeout_hopprobe
3358  *
3359  * if a reply for a probe is not received, record that fact with a null
3360  * record.  if the number of lost probes now takes us to the confidence level
3361  * required, then halt further probing of this particular hop.
3362  */
handletimeout_hopprobe(scamper_task_t * task,tracelb_branch_t * br)3363 static void handletimeout_hopprobe(scamper_task_t *task, tracelb_branch_t *br)
3364 {
3365   tracelb_state_t *state = tracelb_getstate(task);
3366 
3367   br->l++;
3368 
3369   assert(br->mode == MODE_FIRSTHOP || br->mode == MODE_HOPPROBE ||
3370 	 br->mode == MODE_CLUMP);
3371 
3372   /*
3373    * stop probing the link when the number of replies and the number of
3374    * lost probes reach the required confidence level
3375    */
3376   if((br->k + br->l) >= k(state, br->n))
3377     {
3378       tracelb_process_probes(task, br);
3379     }
3380   else
3381     {
3382       tracelb_queue(task);
3383     }
3384   return;
3385 }
3386 
3387 /*
3388  * handletimeout_perpacket
3389  *
3390  */
handletimeout_perpacket(scamper_task_t * task,tracelb_branch_t * br)3391 static void handletimeout_perpacket(scamper_task_t *task, tracelb_branch_t *br)
3392 {
3393   assert(br->newnodec > 1);
3394   tracelb_process_probes(task, br);
3395   return;
3396 }
3397 
handletimeout_bringfwd(scamper_task_t * task,tracelb_branch_t * br)3398 static void handletimeout_bringfwd(scamper_task_t *task, tracelb_branch_t *br)
3399 {
3400   scamper_tracelb_t *trace = tracelb_getdata(task);
3401   tracelb_state_t *state = tracelb_getstate(task);
3402   tracelb_probe_t *pr = br->probes[br->probec-1];
3403   int cancel = 0, n;
3404 
3405   assert(pr->mode == MODE_BRINGFWD || pr->mode == MODE_BRINGFWD0);
3406 
3407   /*
3408    * if after trying to bring a flowid past a particular router we can't,
3409    * stop trying
3410    */
3411   if(pr->mode == MODE_BRINGFWD)
3412     {
3413       if(tracelb_bringfwd_set(state, br, pr->link, 1) != 0)
3414 	{
3415 	  cancel = 1;
3416 	}
3417     }
3418   else
3419     {
3420       br->bringfwd0++;
3421 
3422       n = TRACELB_CONFIDENCE_NLIMIT(trace->nodes[0]->linkc+2);
3423       if(br->bringfwd0 >= k(state, n))
3424 	{
3425 	  cancel = 1;
3426 	}
3427     }
3428 
3429   if(cancel != 0)
3430     {
3431       tracelb_branch_cancel(task, br);
3432     }
3433 
3434   tracelb_queue(task);
3435   return;
3436 }
3437 
3438 /*
3439  * do_tracelb_handle_timeout
3440  *
3441  *
3442  */
do_tracelb_handle_timeout(scamper_task_t * task)3443 static void do_tracelb_handle_timeout(scamper_task_t *task)
3444 {
3445   static void (*const func[])(scamper_task_t *, tracelb_branch_t *) = {
3446     NULL,                    /* MODE_RTSOCK    */
3447     NULL,                    /* MODE_DLHDR     */
3448     handletimeout_firstaddr, /* MODE_FIRSTADDR */
3449     handletimeout_hopprobe,  /* MODE_FIRSTHOP  */
3450     handletimeout_hopprobe,  /* MODE_HOPPROBE  */
3451     handletimeout_perpacket, /* MODE_PERPACKET */
3452     handletimeout_bringfwd,  /* MODE_BRINGFWD  */
3453     handletimeout_bringfwd,  /* MODE_BRINGFWD0 */
3454     handletimeout_hopprobe,  /* MODE_CLUMP     */
3455   };
3456   scamper_tracelb_t *trace = tracelb_getdata(task);
3457   tracelb_state_t *state = tracelb_getstate(task);
3458   tracelb_branch_t *br;
3459   tracelb_probe_t *pr;
3460 
3461   assert(heap_count(state->active) > 0 || heap_count(state->waiting) > 0);
3462 
3463   /*
3464    * if there is nothing in the active heap, then its time to try something
3465    * from the waiting heap.
3466    */
3467   if((br = heap_head_item(state->active)) == NULL)
3468     {
3469       tracelb_queue(task);
3470       return;
3471     }
3472 
3473   if(br->mode == MODE_RTSOCK || br->mode == MODE_DLHDR)
3474     {
3475       handletimeout_abandon(task, br);
3476       return;
3477     }
3478 
3479   /*
3480    * check to see if the last probe received any replies.  if it did not,
3481    * then we leave the decision about what to do next to other code
3482    */
3483   assert(br->probec > 0);
3484   pr = br->probes[br->probec-1];
3485   if(pr->probe->rxc == 0 && pr->probe->attempt + 1 >= trace->attempts)
3486     {
3487       func[pr->mode](task, br);
3488       return;
3489     }
3490 
3491   tracelb_queue(task);
3492   return;
3493 }
3494 
handletcp_firstaddr(scamper_task_t * task,scamper_dl_rec_t * dl,tracelb_probe_t * pr,scamper_addr_t * from)3495 static void handletcp_firstaddr(scamper_task_t *task, scamper_dl_rec_t *dl,
3496 				tracelb_probe_t *pr, scamper_addr_t *from)
3497 {
3498   scamper_tracelb_t *trace = tracelb_getdata(task);
3499   tracelb_state_t *state = tracelb_getstate(task);
3500   scamper_tracelb_node_t *node = NULL;
3501   tracelb_branch_t *br = pr->branch;
3502 
3503   assert(pr->probe->ttl == trace->firsthop);
3504   assert(trace->nodes == NULL);
3505 
3506   /* don't need the branch any more */
3507   if(br->heapnode != NULL)
3508     heap_delete(state->active, br->heapnode);
3509   tracelb_branch_free(state, br);
3510 
3511   /* record the details of the first hop */
3512   if((node = scamper_tracelb_node_alloc(from)) == NULL ||
3513      tracelb_node_ptr(task, node) != 0 ||
3514      scamper_tracelb_node_add(trace, node) != 0)
3515     {
3516       printerror(__func__, "could not alloc node");
3517       goto err;
3518     }
3519   node = NULL;
3520 
3521   tracelb_paths_assert(state);
3522   tracelb_queue(task);
3523   return;
3524 
3525  err:
3526   scamper_tracelb_node_free(node);
3527   tracelb_handleerror(task, errno);
3528   return;
3529 }
3530 
handletcp_hopprobe(scamper_task_t * task,scamper_dl_rec_t * dl,tracelb_probe_t * pr,scamper_addr_t * tcpfrom)3531 static void handletcp_hopprobe(scamper_task_t *task, scamper_dl_rec_t *dl,
3532 			       tracelb_probe_t *pr, scamper_addr_t *tcpfrom)
3533 {
3534   scamper_tracelb_reply_t *reply;
3535 
3536   if(pr->branch == NULL)
3537     return;
3538 
3539   if((reply = handletcp_reply(dl, tcpfrom)) == NULL ||
3540      hopprobe_handlereply(task, pr, reply) != 0)
3541     {
3542       tracelb_handleerror(task, errno);
3543     }
3544 
3545   return;
3546 }
3547 
3548 /*
3549  * handletcp_perpacket
3550  *
3551  * got a TCP response when doing the per-packet test.  this is unexpected
3552  * because we would have been obtaining ICMP time exceeded messages before
3553  * we did this test.  ignore response for now, but this should probably be
3554  * revised.
3555  */
handletcp_perpacket(scamper_task_t * task,scamper_dl_rec_t * dl,tracelb_probe_t * probe,scamper_addr_t * from)3556 static void handletcp_perpacket(scamper_task_t *task, scamper_dl_rec_t *dl,
3557 				tracelb_probe_t *probe, scamper_addr_t *from)
3558 {
3559   return;
3560 }
3561 
do_tracelb_handle_dl(scamper_task_t * task,scamper_dl_rec_t * dl)3562 static void do_tracelb_handle_dl(scamper_task_t *task, scamper_dl_rec_t *dl)
3563 {
3564   static void (* const handletcp_func[])(scamper_task_t *, scamper_dl_rec_t *,
3565 					 tracelb_probe_t *, scamper_addr_t *) =
3566   {
3567     NULL,                /* MODE_RTSOCK    */
3568     NULL,                /* MODE_DLHDR     */
3569     handletcp_firstaddr, /* MODE_FIRSTADDR */
3570     handletcp_hopprobe,  /* MODE_FIRSTHOP  */
3571     handletcp_hopprobe,  /* MODE_HOPPROBE  */
3572     handletcp_perpacket, /* MODE_PERPACKET */
3573     NULL,                /* MODE_BRINGFWD  */
3574     NULL,                /* MODE_BRINGFWD0 */
3575     handletcp_hopprobe,  /* MODE_CLUMP     */
3576   };
3577 
3578   scamper_tracelb_t *trace = tracelb_getdata(task);
3579   tracelb_state_t   *state = tracelb_getstate(task);
3580   scamper_addr_t    *from;
3581   tracelb_probe_t   *pr;
3582 
3583   if(SCAMPER_DL_IS_TCP(dl) == 0)
3584     return;
3585 
3586   /* ignore outgoing probes observed on the datalink socket */
3587   if(trace->type == SCAMPER_TRACELB_TYPE_TCP_SPORT)
3588     {
3589       /*
3590        * if the syn flag (and only the syn flag is set) then the probe
3591        * is probably an outgoing one
3592        */
3593       if(dl->dl_tcp_flags == TH_SYN)
3594 	return;
3595     }
3596   else if(trace->type == SCAMPER_TRACELB_TYPE_TCP_ACK_SPORT)
3597     {
3598       /*
3599        * if the ack flag (and only the ack flag is set) then the probe
3600        * is probably an outgoing one
3601        */
3602       if(dl->dl_tcp_flags == TH_ACK)
3603 	return;
3604     }
3605 
3606   scamper_dl_rec_tcp_print(dl);
3607 
3608   if(dl->dl_tcp_sport != trace->dport)
3609     {
3610       scamper_debug(__func__, "ignoring reply sport %d dport %d",
3611 		    dl->dl_tcp_sport, trace->dport);
3612       return;
3613     }
3614 
3615   /*
3616    * there is no easy way to determine which probe the reply is
3617    * for, so assume it was for the last one
3618    */
3619   assert(state->id_next > 0);
3620   pr = state->probes[state->id_next-1];
3621 
3622   if(pr->branch == NULL)
3623     {
3624       scamper_debug(__func__, "pr->branch is null");
3625       return;
3626     }
3627 
3628   /* if the port doesn't match the flowid sent to, ignore */
3629   if(pr->probe->flowid != dl->dl_tcp_dport)
3630     {
3631       scamper_debug(__func__, "ignoring reply flowid %d dport %d",
3632 		    pr->probe->flowid, dl->dl_tcp_dport);
3633       return;
3634     }
3635 
3636   /* if this is an inbound packet with a timestamp attached */
3637   if(handletcp_func[pr->mode] != NULL)
3638     {
3639       if((from = tracelb_addr(state, trace->dst->type, dl->dl_ip_src)) == NULL)
3640 	goto err;
3641       handletcp_func[pr->mode](task, dl, pr, from);
3642     }
3643 
3644   return;
3645 
3646  err:
3647   tracelb_handleerror(task, errno);
3648   return;
3649 }
3650 
3651 /*
3652  * tracelb_handle_dlhdr
3653  *
3654  * a datalink header has come in.  now move into probing.
3655  */
tracelb_handle_dlhdr(scamper_dlhdr_t * dlhdr)3656 static void tracelb_handle_dlhdr(scamper_dlhdr_t *dlhdr)
3657 {
3658   scamper_task_t *task = dlhdr->param;
3659   tracelb_state_t *state = tracelb_getstate(task);
3660   tracelb_branch_t *br;
3661 
3662   if(dlhdr->error != 0)
3663     {
3664       scamper_task_queue_done(task, 0);
3665       return;
3666     }
3667 
3668   /* there is one head item, and it is in datalink header mode */
3669   assert(heap_count(state->active) == 1);
3670   br = heap_head_item(state->active);
3671   assert(br->mode == MODE_DLHDR);
3672 
3673   /* move into probing */
3674   br->mode = MODE_FIRSTADDR;
3675   tracelb_branch_reset(br);
3676   scamper_task_queue_probe(task);
3677 
3678   return;
3679 }
3680 
3681 /*
3682  * tracelb_handle_rt
3683  *
3684  * process a route record: open the appropriate interface and determine
3685  * the appropriate datalink header to use when transmitting a frame
3686  */
tracelb_handle_rt(scamper_route_t * rt)3687 static void tracelb_handle_rt(scamper_route_t *rt)
3688 {
3689   scamper_task_t *task = rt->param;
3690   scamper_tracelb_t *trace = tracelb_getdata(task);
3691   tracelb_state_t *state = tracelb_getstate(task);
3692   tracelb_branch_t *br;
3693   scamper_dl_t *dl;
3694 
3695 #ifndef _WIN32
3696   if(state->rtsock == NULL)
3697     goto done;
3698 #endif
3699 
3700   if(state->route != rt)
3701     goto done;
3702 
3703   assert(heap_count(state->active) == 1);
3704   br = heap_head_item(state->active);
3705   assert(br->mode == MODE_RTSOCK);
3706 
3707 #ifndef _WIN32
3708   scamper_fd_free(state->rtsock);
3709   state->rtsock = NULL;
3710 #endif
3711 
3712   /* if there was a problem getting the ifindex, handle that */
3713   if(rt->error != 0 || rt->ifindex < 0)
3714     {
3715       printerror(__func__, "could not get ifindex");
3716       tracelb_handleerror(task, errno);
3717       goto done;
3718     }
3719 
3720   if((state->dl = scamper_fd_dl(rt->ifindex)) == NULL)
3721     {
3722       scamper_debug(__func__, "could not get dl for %d", rt->ifindex);
3723       tracelb_handleerror(task, errno);
3724       goto done;
3725     }
3726 
3727   /*
3728    * determine the underlying framing to use with each probe packet that will
3729    * be sent on the datalink.
3730    */
3731   br->mode = MODE_DLHDR;
3732   if((state->dlhdr = scamper_dlhdr_alloc()) == NULL)
3733     {
3734       tracelb_handleerror(task, errno);
3735       goto done;
3736     }
3737   dl = scamper_fd_dl_get(state->dl);
3738   if(trace->rtr != NULL)
3739     state->dlhdr->dst = scamper_addr_use(trace->rtr);
3740   else
3741     state->dlhdr->dst = scamper_addr_use(trace->dst);
3742   state->dlhdr->gw = rt->gw != NULL ? scamper_addr_use(rt->gw) : NULL;
3743   state->dlhdr->ifindex = rt->ifindex;
3744   state->dlhdr->txtype = scamper_dl_tx_type(dl);
3745   state->dlhdr->param = task;
3746   state->dlhdr->cb = tracelb_handle_dlhdr;
3747   if(scamper_dlhdr_get(state->dlhdr) != 0)
3748     {
3749       tracelb_handleerror(task, errno);
3750       goto done;
3751     }
3752 
3753   /* we are ready to probe, so do so */
3754   if(br->mode == MODE_FIRSTADDR)
3755     {
3756       timeval_cpy(&state->next_tx, &br->last_tx);
3757       timeval_cpy(&br->next_tx, &br->last_tx);
3758     }
3759 
3760   tracelb_queue(task);
3761 
3762  done:
3763   scamper_route_free(rt);
3764   if(state->route == rt)
3765     state->route = NULL;
3766   return;
3767 }
3768 
do_tracelb_write(scamper_file_t * sf,scamper_task_t * task)3769 static void do_tracelb_write(scamper_file_t *sf, scamper_task_t *task)
3770 {
3771   scamper_tracelb_t *trace = tracelb_getdata(task);
3772   scamper_file_write_tracelb(sf, trace);
3773   return;
3774 }
3775 
tracelb_state_free(scamper_tracelb_t * trace,tracelb_state_t * state)3776 static void tracelb_state_free(scamper_tracelb_t *trace,tracelb_state_t *state)
3777 {
3778   tracelb_branch_t *br;
3779   tracelb_probe_t *pr;
3780   tracelb_host_t *th;
3781   int i;
3782 
3783   tracelb_paths_dump(state);
3784   tracelb_links_dump(state);
3785 
3786   /* free the address tree */
3787   if(state->addrs != NULL)
3788     splaytree_free(state->addrs, (splaytree_free_t)scamper_addr_free);
3789 
3790   /* free any outstanding ptr requests */
3791   if(state->ths != NULL)
3792     {
3793       while((th = dlist_head_pop(state->ths)) != NULL)
3794 	{
3795 	  th->dn = NULL;
3796 	  tracelb_host_free(state, th);
3797 	}
3798       dlist_free(state->ths);
3799     }
3800 
3801   /* free the active branch records */
3802   if(state->active != NULL)
3803     {
3804       while((br = heap_remove(state->active)) != NULL)
3805 	tracelb_branch_free(state, br);
3806       heap_free(state->active, NULL);
3807     }
3808 
3809   /* free the waiting branch records */
3810   if(state->waiting != NULL)
3811     {
3812       while((br = heap_remove(state->waiting)) != NULL)
3813 	tracelb_branch_free(state, br);
3814       heap_free(state->waiting, NULL);
3815     }
3816 
3817   /* free the probe records */
3818   if(state->probes != NULL)
3819     {
3820       for(i=0; i<state->id_next; i++)
3821 	{
3822 	  pr = state->probes[i];
3823 	  if(pr->mode == MODE_FIRSTADDR || pr->mode == MODE_BRINGFWD ||
3824 	     pr->mode == MODE_BRINGFWD0)
3825 	    {
3826 	      scamper_tracelb_probe_free(pr->probe);
3827 	    }
3828 	  free(pr);
3829 	}
3830       free(state->probes);
3831     }
3832 
3833   /* free the path segments built up as the tracelb progressed */
3834   if(state->paths != NULL)
3835     {
3836       for(i=0; i<state->pathc; i++)
3837 	tracelb_path_free(state->paths[i]);
3838       free(state->paths);
3839     }
3840 
3841   /* free the link records */
3842   if(state->links != NULL)
3843     {
3844       for(i=0; i<state->linkc; i++)
3845 	tracelb_link_free(state->links[i]);
3846       free(state->links);
3847     }
3848 
3849   if(state->icmp != NULL)     scamper_fd_free(state->icmp);
3850   if(state->dl != NULL)       scamper_fd_free(state->dl);
3851 #ifndef _WIN32
3852   if(state->rtsock != NULL)   scamper_fd_free(state->rtsock);
3853 #endif
3854   if(state->dlhdr != NULL)    scamper_dlhdr_free(state->dlhdr);
3855   if(state->route != NULL)    scamper_route_free(state->route);
3856 
3857   switch(trace->type)
3858     {
3859     case SCAMPER_TRACELB_TYPE_UDP_DPORT:
3860     case SCAMPER_TRACELB_TYPE_ICMP_ECHO:
3861       if(state->probe != NULL)
3862 	scamper_fd_free(state->probe);
3863       break;
3864 
3865     case SCAMPER_TRACELB_TYPE_UDP_SPORT:
3866     case SCAMPER_TRACELB_TYPE_TCP_SPORT:
3867     case SCAMPER_TRACELB_TYPE_TCP_ACK_SPORT:
3868       break;
3869     }
3870 
3871   free(state);
3872   return;
3873 }
3874 
do_tracelb_halt(scamper_task_t * task)3875 static void do_tracelb_halt(scamper_task_t *task)
3876 {
3877   scamper_task_queue_done(task, 0);
3878   return;
3879 }
3880 
do_tracelb_free(scamper_task_t * task)3881 static void do_tracelb_free(scamper_task_t *task)
3882 {
3883   scamper_tracelb_t *trace = tracelb_getdata(task);
3884   tracelb_state_t *state = tracelb_getstate(task);
3885 
3886   if(trace == NULL)
3887     {
3888       assert(state == NULL);
3889       return;
3890     }
3891 
3892   /* free any state kept */
3893   if(state != NULL)
3894     tracelb_state_free(trace, state);
3895 
3896   /* free trace data collected */
3897   scamper_tracelb_free(trace);
3898 
3899   return;
3900 }
3901 
tracelb_branch_onremove(void * item)3902 static void tracelb_branch_onremove(void *item)
3903 {
3904   ((tracelb_branch_t *)item)->heapnode = NULL;
3905   return;
3906 }
3907 
3908 /*
3909  * tracelb_state_alloc
3910  *
3911  * allocate the per-tracelb state data structures to be kept.
3912  * also decide on where to start.
3913  */
tracelb_state_alloc(scamper_task_t * task)3914 static int tracelb_state_alloc(scamper_task_t *task)
3915 {
3916   scamper_tracelb_t *trace  = tracelb_getdata(task);
3917   tracelb_state_t   *state  = NULL;
3918   void              *addr   = trace->src->addr;
3919   tracelb_branch_t  *branch;
3920 
3921   assert(trace != NULL);
3922 
3923   if((state = malloc_zero(sizeof(tracelb_state_t))) == NULL)
3924     {
3925       printerror(__func__, "could not malloc state");
3926       goto err;
3927     }
3928 
3929   switch(trace->confidence)
3930     {
3931     case 95:
3932       state->confidence = 0;
3933       break;
3934 
3935     case 99:
3936       state->confidence = 1;
3937       break;
3938 
3939     default:
3940       goto err;
3941     }
3942 
3943   if((state->addrs = splaytree_alloc((splaytree_cmp_t)scamper_addr_cmp))==NULL)
3944     {
3945       printerror(__func__, "could not alloc addr tree");
3946       goto err;
3947     }
3948 
3949   if((state->active = heap_alloc((heap_cmp_t)tracelb_branch_active_cmp))==NULL)
3950     {
3951       printerror(__func__, "could not alloc active heap");
3952       goto err;
3953     }
3954   heap_onremove(state->active, tracelb_branch_onremove);
3955   if((state->waiting=heap_alloc((heap_cmp_t)tracelb_branch_waiting_cmp))==NULL)
3956     {
3957       printerror(__func__, "could not alloc waiting heap");
3958       goto err;
3959     }
3960   heap_onremove(state->waiting, tracelb_branch_onremove);
3961 
3962   if((branch = malloc_zero(sizeof(tracelb_branch_t))) == NULL)
3963     {
3964       printerror(__func__, "could not alloc branch");
3965       goto err;
3966     }
3967 
3968   if(SCAMPER_TRACELB_TYPE_VARY_SPORT(trace) || trace->rtr != NULL)
3969     {
3970       branch->mode = MODE_RTSOCK;
3971 
3972 #ifndef _WIN32
3973       if((state->rtsock = scamper_fd_rtsock()) == NULL)
3974 	{
3975 	  goto err;
3976 	}
3977 #endif
3978     }
3979   else
3980     {
3981       branch->mode = MODE_FIRSTADDR;
3982     }
3983   tracelb_branch_reset(branch);
3984   if(tracelb_branch_waiting(state, branch) != 0)
3985     goto err;
3986 
3987   /* get the fds to probe and listen with */
3988   if(trace->dst->type == SCAMPER_ADDR_TYPE_IPV4)
3989     {
3990       if(trace->type == SCAMPER_TRACELB_TYPE_UDP_DPORT)
3991 	{
3992 	  if((state->probe = scamper_fd_udp4(addr, trace->sport)) == NULL)
3993 	    goto err;
3994 	}
3995       else if(trace->type == SCAMPER_TRACELB_TYPE_ICMP_ECHO)
3996 	{
3997 	  if((state->probe = scamper_fd_icmp4(addr)) == NULL)
3998 	    goto err;
3999 	}
4000       else if(SCAMPER_TRACELB_TYPE_VARY_SPORT(trace) == 0)
4001 	{
4002 	  goto err;
4003 	}
4004 
4005       if(SCAMPER_TRACELB_TYPE_IS_TCP(trace))
4006 	state->payload_size = 0;
4007       else
4008 	state->payload_size = trace->probe_size - 28;
4009 
4010       state->icmp = scamper_fd_icmp4(addr);
4011     }
4012   else if(trace->dst->type == SCAMPER_ADDR_TYPE_IPV6)
4013     {
4014       if(trace->type == SCAMPER_TRACELB_TYPE_UDP_DPORT)
4015 	{
4016 	  if((state->probe = scamper_fd_udp6(addr, trace->sport)) == NULL)
4017 	    goto err;
4018 	}
4019       else if(trace->type == SCAMPER_TRACELB_TYPE_ICMP_ECHO)
4020 	{
4021 	  if((state->probe = scamper_fd_icmp6(addr)) == NULL)
4022 	    goto err;
4023 	}
4024       else if(trace->type != SCAMPER_TRACELB_TYPE_UDP_SPORT)
4025 	{
4026 	  goto err;
4027 	}
4028 
4029       state->icmp         = scamper_fd_icmp6(addr);
4030       state->payload_size = trace->probe_size - 48;
4031     }
4032   else goto err;
4033 
4034   /* allocate a larger global pktbuf if needed */
4035   if(pktbuf_len < state->payload_size)
4036     {
4037       if(realloc_wrap((void **)&pktbuf, state->payload_size) != 0)
4038 	{
4039 	  printerror(__func__, "could not realloc");
4040 	  goto err;
4041 	}
4042       pktbuf_len = state->payload_size;
4043     }
4044 
4045   if(state->icmp == NULL)
4046     {
4047       goto err;
4048     }
4049 
4050   switch(trace->type)
4051     {
4052     case SCAMPER_TRACELB_TYPE_UDP_DPORT:
4053       state->flowid_next = trace->dport;
4054       break;
4055 
4056     case SCAMPER_TRACELB_TYPE_ICMP_ECHO:
4057       state->flowid_next = 1;
4058       break;
4059 
4060     case SCAMPER_TRACELB_TYPE_UDP_SPORT:
4061     case SCAMPER_TRACELB_TYPE_TCP_SPORT:
4062     case SCAMPER_TRACELB_TYPE_TCP_ACK_SPORT:
4063       state->flowid_next = trace->sport;
4064       break;
4065 
4066     default:
4067       goto err;
4068     }
4069 
4070   scamper_task_setstate(task, state);
4071   return 0;
4072 
4073  err:
4074   tracelb_state_free(trace, state);
4075   return -1;
4076 }
4077 
do_tracelb_probe(scamper_task_t * task)4078 static void do_tracelb_probe(scamper_task_t *task)
4079 {
4080   scamper_tracelb_t *trace = tracelb_getdata(task);
4081   tracelb_state_t   *state = tracelb_getstate(task);
4082   tracelb_branch_t  *branch;
4083   tracelb_probe_t   *tp = NULL;
4084   tracelb_probe_t   *tpl;
4085   scamper_probe_t    probe;
4086   uint16_t           u16;
4087 
4088   assert(trace != NULL);
4089 
4090   if(state == NULL)
4091     {
4092       /* timestamp when the trace began */
4093       gettimeofday_wrap(&trace->start);
4094 
4095       /* allocate state and store it with the task */
4096       if(tracelb_state_alloc(task) != 0)
4097 	goto err;
4098 
4099       state = tracelb_getstate(task);
4100     }
4101 
4102   /* select an appropriate branch to probe */
4103   if((branch = heap_remove(state->active)) == NULL)
4104     {
4105       branch = heap_remove(state->waiting);
4106     }
4107   assert(branch != NULL);
4108 
4109   if(branch->mode == MODE_RTSOCK)
4110     {
4111       if(trace->rtr != NULL)
4112 	state->route = scamper_route_alloc(trace->rtr,task,tracelb_handle_rt);
4113       else
4114 	state->route = scamper_route_alloc(trace->dst,task,tracelb_handle_rt);
4115       if(state->route == NULL)
4116 	goto err;
4117 
4118 #ifndef _WIN32
4119       if(scamper_rtsock_getroute(state->rtsock, state->route) != 0)
4120 	goto err;
4121 #else
4122       if(scamper_rtsock_getroute(state->route) != 0)
4123 	goto err;
4124 #endif
4125 
4126       if(scamper_task_queue_isdone(task))
4127 	{
4128 	  tracelb_branch_free(state, branch);
4129 	  return;
4130 	}
4131 
4132       if(branch->mode == MODE_RTSOCK || branch->mode == MODE_DLHDR)
4133 	{
4134 	  /*
4135 	   * re-queue the tracelb. have to setup the time values in the
4136 	   * branch and state structs as part of this.
4137 	   */
4138 	  gettimeofday_wrap(&branch->last_tx);
4139 	  timeval_add_cs(&state->next_tx,&branch->last_tx,trace->wait_probe);
4140 	  timeval_add_s(&branch->next_tx,&branch->last_tx,trace->wait_timeout);
4141 	  if(tracelb_branch_active(state, branch) != 0)
4142 	    goto err;
4143 	  tracelb_queue(task);
4144 	  return;
4145 	}
4146     }
4147 
4148   /* get a reference to the previous probe sent, if there is one */
4149   if(branch->probec > 0)
4150     {
4151       tpl = branch->probes[branch->probec-1];
4152     }
4153   else tpl = NULL;
4154 
4155   /* allocate a probe structure to record state of the probe to be sent */
4156   if((tp = malloc_zero(sizeof(tracelb_probe_t))) == NULL ||
4157      (tp->probe = scamper_tracelb_probe_alloc()) == NULL)
4158     {
4159       printerror(__func__, "could not alloc probe");
4160       goto err;
4161     }
4162 
4163   assert((tpl != NULL && tpl->probe->rxc == 0 &&
4164 	  tpl->probe->attempt+1 < trace->attempts) ||
4165 	 branch->mode == MODE_FIRSTADDR || branch->mode == MODE_FIRSTHOP ||
4166 	 branch->mode == MODE_HOPPROBE  || branch->mode == MODE_PERPACKET ||
4167 	 branch->mode == MODE_CLUMP);
4168 
4169   /* default mode, can be changed to something more specific if appropriate */
4170   tp->mode = branch->mode;
4171 
4172   if(tpl != NULL && tpl->probe->rxc == 0 &&
4173      tpl->probe->attempt+1 < trace->attempts)
4174     {
4175       /*
4176        * if a reply to the previous probe was not received and the allotted
4177        * number of attempts is not yet reached, retransmit
4178        */
4179       tp->probe->flowid  = tpl->probe->flowid;
4180       tp->probe->ttl     = tpl->probe->ttl;
4181       tp->probe->attempt = tpl->probe->attempt + 1;
4182       tp->link           = tpl->link;
4183       tp->mode           = tpl->mode;
4184     }
4185   else if(branch->mode == MODE_FIRSTADDR)
4186     {
4187       /* probe to determine the address of the first hop. */
4188       assert(trace->nodec == 0);
4189       tp->probe->flowid  = state->flowid_next;
4190       tp->probe->ttl     = trace->firsthop;
4191       tp->probe->attempt = 0;
4192     }
4193   else if(branch->mode == MODE_FIRSTHOP)
4194     {
4195       /* still enumerating the first set of links */
4196       assert(trace->nodec > 0);
4197       tp->probe->flowid  = state->flowid_next++;
4198       tp->probe->ttl     = trace->firsthop + 1;
4199       tp->probe->attempt = 0;
4200     }
4201   else if(branch->mode == MODE_HOPPROBE || branch->mode == MODE_CLUMP)
4202     {
4203       /*
4204        * call a function that can deal with the possibly complex details of
4205        * selecting a flowid/ttl to probe with
4206        */
4207       if(tracelb_probe_vals(task, branch, tp) < 0)
4208 	goto err;
4209     }
4210   else if(branch->mode == MODE_PERPACKET)
4211     {
4212       assert(branch->newnodec > 1);
4213       tp->probe->flowid  = branch->newnodes[0]->probe->flowid;
4214       tp->probe->ttl     = branch->newnodes[0]->probe->ttl;
4215       tp->probe->attempt = 0;
4216     }
4217 
4218   memset(&probe, 0, sizeof(probe));
4219   probe.pr_ip_src    = trace->src;
4220   probe.pr_ip_dst    = trace->dst;
4221   probe.pr_ip_tos    = trace->tos;
4222   probe.pr_data      = pktbuf;
4223   probe.pr_len       = state->payload_size;
4224   probe.pr_ip_ttl    = tp->probe->ttl;
4225 
4226   if(trace->dst->type == SCAMPER_ADDR_TYPE_IPV4)
4227     probe.pr_ip_off  = IP_DF;
4228 
4229   /* reset the payload of the packet */
4230   memset(probe.pr_data, 0, probe.pr_len);
4231 
4232   if(SCAMPER_TRACELB_TYPE_IS_UDP(trace))
4233     {
4234       probe.pr_ip_proto = IPPROTO_UDP;
4235       probe.pr_ip_id    = state->id_next + 1;
4236 
4237       if(trace->type == SCAMPER_TRACELB_TYPE_UDP_DPORT)
4238 	{
4239 	  probe.pr_fd        = scamper_fd_fd_get(state->probe);
4240 	  probe.pr_udp_sport = trace->sport;
4241 	  probe.pr_udp_dport = tp->probe->flowid;
4242 	}
4243       else
4244 	{
4245 	  probe.pr_udp_dport = trace->dport;
4246 	  probe.pr_udp_sport = tp->probe->flowid;
4247 	}
4248 
4249       /*
4250        * fudge it so the probe id goes into the checksum field.
4251        * the probe id also goes in the IP-ID field to guard against FreeBSD
4252        * systems that munge the checksum on rx when checking the packet.
4253        */
4254       u16 = htons(state->id_next + 1);
4255       memcpy(probe.pr_data, &u16, 2);
4256       if(trace->dst->type == SCAMPER_ADDR_TYPE_IPV4)
4257 	{
4258 	  probe.pr_ip_id = state->id_next + 1;
4259 	  u16 = scamper_udp4_cksum(&probe);
4260 	}
4261       else
4262 	{
4263 	  u16 = scamper_udp6_cksum(&probe);
4264 	}
4265       memcpy(probe.pr_data, &u16, 2);
4266     }
4267   else if(SCAMPER_TRACELB_TYPE_IS_ICMP(trace))
4268     {
4269       probe.pr_fd = scamper_fd_fd_get(state->probe);
4270       SCAMPER_PROBE_ICMP_ECHO(&probe, trace->sport, state->id_next);
4271 
4272       /* this is the flow-id to use -- the ICMP checksum */
4273       u16 = htons(tp->probe->flowid);
4274       probe.pr_icmp_sum = u16;
4275 
4276       /* fudge the checksum field so it is used as the flow id */
4277       memcpy(probe.pr_data, &u16, 2);
4278       if(trace->dst->type == SCAMPER_ADDR_TYPE_IPV4)
4279 	u16 = scamper_icmp4_cksum(&probe);
4280       else
4281 	u16 = scamper_icmp6_cksum(&probe);
4282       memcpy(probe.pr_data, &u16, 2);
4283     }
4284   else
4285     {
4286       assert(SCAMPER_TRACELB_TYPE_IS_TCP(trace));
4287 
4288       probe.pr_ip_proto  = IPPROTO_TCP;
4289       probe.pr_tcp_dport = trace->dport;
4290       probe.pr_tcp_sport = tp->probe->flowid;
4291       probe.pr_tcp_seq   = 0;
4292       probe.pr_tcp_ack   = 0;
4293       probe.pr_tcp_win   = 0;
4294       probe.pr_ip_id     = state->id_next + 1;
4295 
4296       if(trace->type == SCAMPER_TRACELB_TYPE_TCP_SPORT)
4297 	probe.pr_tcp_flags = TH_SYN;
4298       else
4299 	probe.pr_tcp_flags = TH_ACK;
4300     }
4301 
4302   if(state->dl != NULL)
4303     {
4304       probe.pr_dl        = scamper_fd_dl_get(state->dl);
4305       probe.pr_dl_buf    = state->dlhdr->buf;
4306       probe.pr_dl_len    = state->dlhdr->len;
4307     }
4308 
4309   if(scamper_probe(&probe) == -1)
4310     {
4311       errno = probe.pr_errno;
4312       goto err;
4313     }
4314 
4315   /* another probe sent */
4316   trace->probec++;
4317 
4318   /* make a note that the probe was sent */
4319   timeval_cpy(&tp->probe->tx, &probe.pr_tx);
4320 
4321   /* record the probe in state */
4322   if(tracelb_probe_add(state, branch, tp) != 0)
4323     goto err;
4324 
4325   /* figure out when the next probe may be sent */
4326   timeval_add_cs(&state->next_tx, &probe.pr_tx, trace->wait_probe);
4327 
4328   /*
4329    * put the branch back in the heap structure, but with appropriate
4330    * timestamps
4331    */
4332   timeval_cpy(&branch->last_tx, &probe.pr_tx);
4333   branch->next_tx.tv_sec  = probe.pr_tx.tv_sec + trace->wait_timeout;
4334   branch->next_tx.tv_usec = probe.pr_tx.tv_usec;
4335   if(tracelb_branch_active(state, branch) != 0)
4336     goto err;
4337 
4338   tracelb_queue(task);
4339   return;
4340 
4341  err:
4342   if(tp != NULL) free(tp);
4343   tracelb_handleerror(task, errno);
4344   return;
4345 }
4346 
tracelb_arg_param_validate(int optid,char * param,long long * out)4347 static int tracelb_arg_param_validate(int optid, char *param, long long *out)
4348 {
4349   long tmp = 0;
4350 
4351   switch(optid)
4352     {
4353     case TRACE_OPT_CONFIDENCE:
4354       if(string_tolong(param, &tmp) != 0 || (tmp != 95 && tmp != 99))
4355 	{
4356 	  goto err;
4357 	}
4358       break;
4359 
4360     case TRACE_OPT_SPORT:
4361     case TRACE_OPT_DPORT:
4362       if(string_tolong(param, &tmp) != 0 ||
4363 	 tmp < SCAMPER_DO_TRACELB_PORT_MIN ||
4364 	 tmp > SCAMPER_DO_TRACELB_PORT_MAX)
4365 	{
4366 	  goto err;
4367 	}
4368       break;
4369 
4370     case TRACE_OPT_FIRSTHOP:
4371       if(string_tolong(param, &tmp) != 0 ||
4372 	 tmp < SCAMPER_DO_TRACELB_FIRSTHOP_MIN ||
4373 	 tmp > SCAMPER_DO_TRACELB_FIRSTHOP_MAX)
4374 	{
4375 	  goto err;
4376 	}
4377       break;
4378 
4379     case TRACE_OPT_GAPLIMIT:
4380       if(string_tolong(param, &tmp) != 0 ||
4381 	 tmp < SCAMPER_DO_TRACELB_GAPLIMIT_MIN ||
4382 	 tmp > SCAMPER_DO_TRACELB_GAPLIMIT_MAX)
4383 	{
4384 	  goto err;
4385 	}
4386       break;
4387 
4388     case TRACE_OPT_OPTION:
4389       if(strcasecmp(param, "ptr") != 0)
4390 	goto err;
4391       break;
4392 
4393     case TRACE_OPT_PROTOCOL:
4394       if(strcasecmp(param, "udp-dport") == 0)
4395 	tmp = SCAMPER_TRACELB_TYPE_UDP_DPORT;
4396       else if(strcasecmp(param, "icmp-echo") == 0)
4397 	tmp = SCAMPER_TRACELB_TYPE_ICMP_ECHO;
4398       else if(strcasecmp(param, "udp-sport") == 0)
4399 	tmp = SCAMPER_TRACELB_TYPE_UDP_SPORT;
4400       else if(strcasecmp(param, "tcp-sport") == 0)
4401 	tmp = SCAMPER_TRACELB_TYPE_TCP_SPORT;
4402       else if(strcasecmp(param, "tcp-ack-sport") == 0)
4403 	tmp = SCAMPER_TRACELB_TYPE_TCP_ACK_SPORT;
4404       else
4405 	goto err;
4406       break;
4407 
4408     case TRACE_OPT_TOS:
4409       if(string_tolong(param, &tmp) != 0 ||
4410 	 tmp < SCAMPER_DO_TRACELB_TOS_MIN || tmp > SCAMPER_DO_TRACELB_TOS_MAX)
4411 	{
4412 	  goto err;
4413 	}
4414       break;
4415 
4416     case TRACE_OPT_ATTEMPTS:
4417       if(string_tolong(param, &tmp) != 0 ||
4418 	 tmp < SCAMPER_DO_TRACELB_ATTEMPTS_MIN ||
4419 	 tmp > SCAMPER_DO_TRACELB_ATTEMPTS_MAX)
4420 	{
4421 	  goto err;
4422 	}
4423       break;
4424 
4425     case TRACE_OPT_PROBECMAX:
4426       if(string_tolong(param, &tmp) != 0 ||
4427 	 tmp < SCAMPER_DO_TRACELB_PROBECMAX_MIN ||
4428 	 tmp > SCAMPER_DO_TRACELB_PROBECMAX_MAX)
4429 	{
4430 	  goto err;
4431 	}
4432       break;
4433 
4434     case TRACE_OPT_USERID:
4435       if(string_tolong(param, &tmp) != 0 || tmp < 0)
4436 	{
4437 	  goto err;
4438 	}
4439       break;
4440 
4441     case TRACE_OPT_WAITPROBE:
4442       if(string_tolong(param, &tmp) != 0 ||
4443 	 tmp < SCAMPER_DO_TRACELB_WAITPROBE_MIN ||
4444 	 tmp > SCAMPER_DO_TRACELB_WAITPROBE_MAX)
4445 	{
4446 	  goto err;
4447 	}
4448       break;
4449 
4450     case TRACE_OPT_WAITTIMEOUT:
4451       if(string_tolong(param, &tmp) != 0 ||
4452 	 tmp < SCAMPER_DO_TRACELB_WAITTIMEOUT_MIN ||
4453 	 tmp > SCAMPER_DO_TRACELB_WAITTIMEOUT_MAX)
4454 	{
4455 	  goto err;
4456 	}
4457       break;
4458 
4459       /* these parameters are validated at execution time */
4460     case TRACE_OPT_RTRADDR:
4461       break;
4462 
4463     default:
4464       return -1;
4465     }
4466 
4467   /* valid parameter */
4468   if(out != NULL)
4469     *out = (long long)tmp;
4470   return 0;
4471 
4472  err:
4473   return -1;
4474 }
4475 
4476 /*
4477  * scamper_do_tracelb_alloc
4478  *
4479  * given a string representing a traceroute task, parse the parameters and
4480  * assemble a trace.  return the trace structure so that it is all ready to
4481  * go.
4482  */
scamper_do_tracelb_alloc(char * str)4483 void *scamper_do_tracelb_alloc(char *str)
4484 {
4485   scamper_option_out_t *opts_out = NULL, *opt;
4486   scamper_tracelb_t *trace = NULL;
4487   uint8_t  type         = SCAMPER_TRACELB_TYPE_UDP_DPORT;
4488   uint16_t sport        = scamper_sport_default();
4489   uint8_t  confidence   = 95;
4490   uint16_t dport        = SCAMPER_DO_TRACELB_DPORT_DEF;
4491   uint8_t  attempts     = SCAMPER_DO_TRACELB_ATTEMPTS_DEF;
4492   uint8_t  firsthop     = SCAMPER_DO_TRACELB_FIRSTHOP_DEF;
4493   uint8_t  wait_timeout = SCAMPER_DO_TRACELB_WAITTIMEOUT_DEF;
4494   uint8_t  wait_probe   = SCAMPER_DO_TRACELB_WAITPROBE_DEF;
4495   uint8_t  tos          = SCAMPER_DO_TRACELB_TOS_DEF;
4496   uint32_t probec_max   = SCAMPER_DO_TRACELB_PROBECMAX_DEF;
4497   uint8_t  gaplimit     = SCAMPER_DO_TRACELB_GAPLIMIT_DEF;
4498   uint32_t userid       = 0;
4499   uint8_t  flags        = 0;
4500   char *rtr = NULL, *addr;
4501   long long tmp = 0;
4502   int af;
4503 
4504   /* try and parse the string passed in */
4505   if(scamper_options_parse(str, opts, opts_cnt, &opts_out, &addr) != 0)
4506     {
4507       goto err;
4508     }
4509 
4510   /* if there is no IP address after the options string, then stop now */
4511   if(addr == NULL)
4512     {
4513       goto err;
4514     }
4515 
4516   for(opt = opts_out; opt != NULL; opt = opt->next)
4517     {
4518       if(opt->type != SCAMPER_OPTION_TYPE_NULL &&
4519 	 tracelb_arg_param_validate(opt->id, opt->str, &tmp) != 0)
4520 	{
4521 	  scamper_debug(__func__, "validation of optid %d failed", opt->id);
4522 	  goto err;
4523 	}
4524 
4525       switch(opt->id)
4526 	{
4527 	case TRACE_OPT_CONFIDENCE:
4528 	  confidence = (uint8_t)tmp;
4529 	  break;
4530 
4531 	case TRACE_OPT_DPORT:
4532 	  dport = (uint16_t)tmp;
4533 	  break;
4534 
4535 	case TRACE_OPT_FIRSTHOP:
4536 	  firsthop = (uint8_t)tmp;
4537 	  break;
4538 
4539 	case TRACE_OPT_GAPLIMIT:
4540 	  gaplimit = (uint8_t)tmp;
4541 	  break;
4542 
4543 	case TRACE_OPT_OPTION:
4544 	  if(strcasecmp(opt->str, "ptr") == 0)
4545 	    flags |= SCAMPER_TRACELB_FLAG_PTR;
4546 	  else
4547 	    {
4548 	      scamper_debug(__func__, "unknown option %s", opt->str);
4549 	      goto err;
4550 	    }
4551 	  break;
4552 
4553 	case TRACE_OPT_PROTOCOL:
4554 	  type = (uint8_t)tmp;
4555 	  break;
4556 
4557 	case TRACE_OPT_TOS:
4558 	  tos = (uint8_t)tmp;
4559 	  break;
4560 
4561 	case TRACE_OPT_ATTEMPTS:
4562 	  attempts = (uint8_t)tmp;
4563 	  break;
4564 
4565 	case TRACE_OPT_PROBECMAX:
4566 	  probec_max = (uint32_t)tmp;
4567 	  break;
4568 
4569 	case TRACE_OPT_USERID:
4570 	  userid = (uint32_t)tmp;
4571 	  break;
4572 
4573 	case TRACE_OPT_WAITPROBE:
4574 	  wait_probe = (uint8_t)tmp;
4575 	  break;
4576 
4577 	case TRACE_OPT_WAITTIMEOUT:
4578 	  wait_timeout = (uint8_t)tmp;
4579 	  break;
4580 
4581 	case TRACE_OPT_RTRADDR:
4582 	  if(rtr != NULL)
4583 	    goto err;
4584 	  rtr = opt->str;
4585 	  break;
4586 	}
4587     }
4588 
4589   scamper_options_free(opts_out); opts_out = NULL;
4590 
4591   if((trace = scamper_tracelb_alloc()) == NULL)
4592     {
4593       goto err;
4594     }
4595 
4596   if((trace->dst = scamper_addr_resolve(AF_UNSPEC, addr)) == NULL)
4597     {
4598       goto err;
4599     }
4600 
4601   if(trace->dst->type == SCAMPER_ADDR_TYPE_IPV6 &&
4602      SCAMPER_TRACELB_TYPE_IS_TCP(trace))
4603     {
4604       goto err;
4605     }
4606 
4607   af = scamper_addr_af(trace->dst);
4608   if(af != AF_INET && af != AF_INET6)
4609     goto err;
4610 
4611   if(rtr != NULL &&
4612      (trace->rtr = scamper_addr_resolve(af, rtr)) == NULL)
4613     goto err;
4614 
4615   trace->sport        = sport;
4616   trace->dport        = dport;
4617   trace->tos          = tos;
4618   trace->firsthop     = firsthop;
4619   trace->wait_timeout = wait_timeout;
4620   trace->wait_probe   = wait_probe;
4621   trace->attempts     = attempts;
4622   trace->confidence   = confidence;
4623   trace->type         = type;
4624   trace->probec_max   = probec_max;
4625   trace->gaplimit     = gaplimit;
4626   trace->userid       = userid;
4627   trace->flags        = flags;
4628 
4629   switch(trace->dst->type)
4630     {
4631     case SCAMPER_ADDR_TYPE_IPV4:
4632       if(SCAMPER_TRACELB_TYPE_IS_TCP(trace))
4633 	trace->probe_size = 40;
4634       else
4635 	trace->probe_size = 44;
4636       break;
4637 
4638     case SCAMPER_ADDR_TYPE_IPV6:
4639       trace->probe_size = 60;
4640       break;
4641 
4642     default:
4643       goto err;
4644     }
4645 
4646   return trace;
4647 
4648  err:
4649   if(trace != NULL) scamper_tracelb_free(trace);
4650   if(opts_out != NULL) scamper_options_free(opts_out);
4651   return NULL;
4652 }
4653 
scamper_do_tracelb_alloctask(void * data,scamper_list_t * list,scamper_cycle_t * cycle)4654 scamper_task_t *scamper_do_tracelb_alloctask(void *data,
4655 					     scamper_list_t *list,
4656 					     scamper_cycle_t *cycle)
4657 {
4658   scamper_tracelb_t *trace = (scamper_tracelb_t *)data;
4659   scamper_task_sig_t *sig = NULL;
4660   scamper_task_t *task = NULL;
4661 
4662   /* allocate a task structure and store the trace with it */
4663   if((task = scamper_task_alloc(trace, &funcs)) == NULL)
4664     goto err;
4665 
4666   /* declare the signature of the task */
4667   if((sig = scamper_task_sig_alloc(SCAMPER_TASK_SIG_TYPE_TX_IP)) == NULL)
4668     goto err;
4669   sig->sig_tx_ip_dst = scamper_addr_use(trace->dst);
4670   if(trace->src == NULL && (trace->src = scamper_getsrc(trace->dst,0)) == NULL)
4671     goto err;
4672   sig->sig_tx_ip_src = scamper_addr_use(trace->src);
4673   if(scamper_task_sig_add(task, sig) != 0)
4674     goto err;
4675   sig = NULL;
4676 
4677   /* associate the list and cycle with the trace */
4678   trace->list  = scamper_list_use(list);
4679   trace->cycle = scamper_cycle_use(cycle);
4680 
4681   return task;
4682 
4683  err:
4684   if(sig != NULL) scamper_task_sig_free(sig);
4685   if(task != NULL)
4686     {
4687       scamper_task_setdatanull(task);
4688       scamper_task_free(task);
4689     }
4690   return NULL;
4691 }
4692 
4693 /*
4694  * scamper_do_tracelb_arg_validate
4695  *
4696  *
4697  */
scamper_do_tracelb_arg_validate(int argc,char * argv[],int * stop)4698 int scamper_do_tracelb_arg_validate(int argc, char *argv[], int *stop)
4699 {
4700   return scamper_options_validate(opts, opts_cnt, argc, argv, stop,
4701 				  tracelb_arg_param_validate);
4702 }
4703 
scamper_do_tracelb_free(void * data)4704 void scamper_do_tracelb_free(void *data)
4705 {
4706   scamper_tracelb_free((scamper_tracelb_t *)data);
4707   return;
4708 }
4709 
scamper_do_tracelb_cleanup(void)4710 void scamper_do_tracelb_cleanup(void)
4711 {
4712   if(pktbuf != NULL)
4713     {
4714       free(pktbuf);
4715       pktbuf = NULL;
4716     }
4717 
4718   return;
4719 }
4720 
scamper_do_tracelb_init(void)4721 int scamper_do_tracelb_init(void)
4722 {
4723   funcs.probe          = do_tracelb_probe;
4724   funcs.handle_icmp    = do_tracelb_handle_icmp;
4725   funcs.handle_dl      = do_tracelb_handle_dl;
4726   funcs.handle_timeout = do_tracelb_handle_timeout;
4727   funcs.write          = do_tracelb_write;
4728   funcs.task_free      = do_tracelb_free;
4729   funcs.halt           = do_tracelb_halt;
4730 
4731   return 0;
4732 }
4733