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