rb-wartslib 0.9.10

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,34 @@
1
+ /*
2
+ ** Ruby bindings to scamper_list_t and scamper_cycle_t.
3
+ **
4
+ ** --------------------------------------------------------------------------
5
+ ** Copyright (C) 2007 The Regents of the University of California.
6
+ **
7
+ ** This program is free software; you can redistribute it and/or modify
8
+ ** it under the terms of the GNU General Public License as published by
9
+ ** the Free Software Foundation; either version 2 of the License, or
10
+ ** (at your option) any later version.
11
+ **
12
+ ** This program is distributed in the hope that it will be useful,
13
+ ** but WITHOUT ANY WARRANTY; without even the implied warranty of
14
+ ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
+ ** GNU General Public License for more details.
16
+ **
17
+ ** You should have received a copy of the GNU General Public License
18
+ ** along with this program; if not, write to the Free Software
19
+ ** Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20
+ **
21
+ ** $Id: sclist.h,v 1.2 2007/11/29 01:32:57 youngh Exp $
22
+ **/
23
+
24
+ #ifndef __SCLIST_H
25
+ #define __SCLIST_H
26
+
27
+ #include "scamper_list.h"
28
+
29
+ VALUE sclist_create(scamper_list_t *list);
30
+ VALUE sccycle_create(scamper_cycle_t *cycle, int type);
31
+ void Init_sclist(void);
32
+ void Init_sccycle(void);
33
+
34
+ #endif # __SCLIST_H
@@ -0,0 +1,873 @@
1
+ /*
2
+ ** Ruby bindings to scamper_trace_t and its nested structures (e.g.,
3
+ ** scamper_list_t, scamper_cycle_t, and scamper_trace_hop_t).
4
+ **
5
+ ** General implementation notes:
6
+ **
7
+ ** * The scamper library sets scamper_trace.firsthop to 0 for arts files,
8
+ ** so don't rely on it being useful in general.
9
+ ** * scamper_trace.cycle can be NULL if user invokes a run from the scamper
10
+ ** command line (and doesn't specify a cycle), or no cycle is specified
11
+ ** in a 'source add' command.
12
+ **
13
+ ** --------------------------------------------------------------------------
14
+ ** Copyright (C) 2007 The Regents of the University of California.
15
+ **
16
+ ** This program is free software; you can redistribute it and/or modify
17
+ ** it under the terms of the GNU General Public License as published by
18
+ ** the Free Software Foundation; either version 2 of the License, or
19
+ ** (at your option) any later version.
20
+ **
21
+ ** This program is distributed in the hope that it will be useful,
22
+ ** but WITHOUT ANY WARRANTY; without even the implied warranty of
23
+ ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
24
+ ** GNU General Public License for more details.
25
+ **
26
+ ** You should have received a copy of the GNU General Public License
27
+ ** along with this program; if not, write to the Free Software
28
+ ** Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
29
+ **
30
+ ** $Id: sctrace.c,v 1.45 2007/11/29 20:02:20 youngh Exp $
31
+ **/
32
+
33
+ #include "ruby.h"
34
+
35
+ #if defined(__HAVE_STDINT_H__)
36
+ #include <stdint.h>
37
+ #endif
38
+
39
+ #include "scamper_file.h"
40
+ #include "sctrace.h"
41
+ #include "sclist.h"
42
+
43
+ extern VALUE mWarts;
44
+ extern void sccycle_free(void *data); /* for type checking purposes only */
45
+
46
+ VALUE cTrace; /* Don't make this static, since it's referenced in scfile.c. */
47
+
48
+ static ID iv_element_type, iv_dest_response, iv_list, iv_cycle;
49
+ static ID meth_write_trace;
50
+
51
+ /*-------------------------------------------------------------------------*/
52
+
53
+ #define DEF_CONST(name) \
54
+ rb_define_const(cTrace, #name, INT2FIX(SCAMPER_TRACE_##name));
55
+
56
+
57
+ /*-------------------------------------------------------------------------*/
58
+
59
+ #define DEF_ATTR(name) \
60
+ rb_define_method(cTrace, #name, sctrace_attr_##name, 0);
61
+
62
+ #define NUM_ATTR_FIELD_FUN(name,field) \
63
+ static VALUE sctrace_attr_##name(VALUE self) \
64
+ { \
65
+ scamper_trace_t *trace; \
66
+ Data_Get_Struct(self, scamper_trace_t, trace); \
67
+ return UINT2NUM(trace->field); \
68
+ }
69
+
70
+ #define NUM_ATTR_FUN(name) NUM_ATTR_FIELD_FUN(name,name)
71
+
72
+ #define STRUCT_NUM_ATTR_FUN(name,sname,field) \
73
+ static VALUE sctrace_attr_##name(VALUE self) \
74
+ { \
75
+ scamper_trace_t *trace; \
76
+ Data_Get_Struct(self, scamper_trace_t, trace); \
77
+ return UINT2NUM(trace->sname ? trace->sname->field : 0); \
78
+ }
79
+
80
+ #define STRUCT_STR_ATTR_FUN(name,sname,field) \
81
+ static VALUE sctrace_attr_##name(VALUE self) \
82
+ { \
83
+ scamper_trace_t *trace; \
84
+ char *ptr = NULL; \
85
+ Data_Get_Struct(self, scamper_trace_t, trace); \
86
+ if (trace->sname) { \
87
+ ptr = trace->sname->field; \
88
+ } \
89
+ return rb_str_new2(ptr ? ptr : ""); \
90
+ }
91
+
92
+ STRUCT_NUM_ATTR_FUN(list_id, list, id);
93
+ STRUCT_STR_ATTR_FUN(list_name, list, name);
94
+ STRUCT_STR_ATTR_FUN(list_descr, list, descr);
95
+ STRUCT_STR_ATTR_FUN(list_monitor, list, monitor);
96
+
97
+ STRUCT_NUM_ATTR_FUN(cycle_id, cycle, id);
98
+ STRUCT_NUM_ATTR_FUN(cycle_start_time, cycle, start_time);
99
+ STRUCT_NUM_ATTR_FUN(cycle_stop_time, cycle, stop_time);
100
+ STRUCT_STR_ATTR_FUN(cycle_hostname, cycle, hostname);
101
+
102
+ NUM_ATTR_FIELD_FUN(start, start.tv_sec);
103
+ NUM_ATTR_FIELD_FUN(start_usec, start.tv_usec);
104
+
105
+ NUM_ATTR_FUN(hop_count);
106
+ NUM_ATTR_FUN(stop_reason);
107
+ NUM_ATTR_FUN(stop_data);
108
+ NUM_ATTR_FUN(type);
109
+ NUM_ATTR_FUN(flags);
110
+ NUM_ATTR_FUN(attempts);
111
+ NUM_ATTR_FUN(hoplimit);
112
+ NUM_ATTR_FUN(gaplimit);
113
+ NUM_ATTR_FUN(firsthop);
114
+ NUM_ATTR_FUN(tos);
115
+ NUM_ATTR_FUN(wait);
116
+ NUM_ATTR_FUN(loops);
117
+ NUM_ATTR_FUN(probe_size);
118
+ NUM_ATTR_FUN(sport);
119
+ NUM_ATTR_FUN(dport);
120
+
121
+
122
+ /*-------------------------------------------------------------------------*/
123
+
124
+ static scamper_trace_hop_t *find_trace_hop(scamper_trace_t *trace,
125
+ int hop, int attempt)
126
+ {
127
+ scamper_trace_hop_t *trace_hop;
128
+
129
+ if (hop < 0 || hop >= trace->hop_count || attempt < 0) {
130
+ return NULL;
131
+ }
132
+
133
+ trace_hop = trace->hops[hop];
134
+ while (trace_hop && attempt > 0) {
135
+ --attempt;
136
+ trace_hop = trace_hop->hop_next;
137
+ }
138
+ return trace_hop;
139
+ }
140
+
141
+
142
+ #define DEF_HOP_ATTR(name) \
143
+ rb_define_method(cTrace, #name, sctrace_attr_##name, -1);
144
+
145
+ /* Because this declares variables, this macro must occur
146
+ immediately after the variable declarations in a function. */
147
+ #define PROCESS_HOP_PARAMS \
148
+ VALUE hop, attempt; \
149
+ if (rb_scan_args(argc, argv, "11", &hop, &attempt) == 1) { \
150
+ attempt = INT2FIX(0); \
151
+ }
152
+
153
+ #define NUM_HOP_ATTR_FIELD_FUN(name,field) \
154
+ static VALUE sctrace_attr_hop_##name(int argc, VALUE *argv, VALUE self) \
155
+ { \
156
+ scamper_trace_t *trace; \
157
+ scamper_trace_hop_t *trace_hop; \
158
+ PROCESS_HOP_PARAMS; \
159
+ Data_Get_Struct(self, scamper_trace_t, trace); \
160
+ trace_hop = find_trace_hop(trace, NUM2INT(hop), NUM2INT(attempt)); \
161
+ if (!trace_hop) { \
162
+ return Qnil; \
163
+ } \
164
+ return UINT2NUM(trace_hop->hop_##field); \
165
+ }
166
+
167
+ #define NUM_HOP_ATTR_FUN(name) NUM_HOP_ATTR_FIELD_FUN(name,name)
168
+
169
+ #define STR_HOP_ATTR_FIELD_FUN(name,field) \
170
+ static VALUE sctrace_attr_hop_##name(int argc, VALUE *argv, VALUE self) \
171
+ { \
172
+ scamper_trace_t *trace; \
173
+ scamper_trace_hop_t *trace_hop; \
174
+ char *ptr; \
175
+ PROCESS_HOP_PARAMS; \
176
+ Data_Get_Struct(self, scamper_trace_t, trace); \
177
+ trace_hop = find_trace_hop(trace, NUM2INT(hop), NUM2INT(attempt)); \
178
+ if (!trace_hop) { \
179
+ return Qnil; \
180
+ } \
181
+ ptr = trace_hop->hop_##field; \
182
+ ptr = (ptr ? ptr : ""); \
183
+ return rb_str_new2(ptr); \
184
+ }
185
+
186
+ #define STR_HOP_ATTR_FUN(name) STR_HOP_ATTR_FIELD_FUN(name,name)
187
+
188
+ NUM_HOP_ATTR_FUN(flags);
189
+ NUM_HOP_ATTR_FUN(probe_id);
190
+ NUM_HOP_ATTR_FUN(probe_ttl);
191
+ NUM_HOP_ATTR_FUN(reply_ttl);
192
+ NUM_HOP_ATTR_FUN(probe_size);
193
+ NUM_HOP_ATTR_FUN(reply_size);
194
+ NUM_HOP_ATTR_FUN(icmp_type);
195
+ NUM_HOP_ATTR_FUN(icmp_code);
196
+ NUM_HOP_ATTR_FUN(tcp_flags);
197
+ NUM_HOP_ATTR_FIELD_FUN(rtt_sec, rtt.tv_sec);
198
+ NUM_HOP_ATTR_FIELD_FUN(rtt_usec, rtt.tv_usec);
199
+
200
+
201
+ /*-------------------------------------------------------------------------*/
202
+
203
+ #define DEF_HOP_ICMP_QUERY(name) \
204
+ rb_define_method(cTrace, "hop_icmp_" #name "?", sctrace_hop_icmp_##name, -1);
205
+
206
+ #define HOP_ICMP_QUERY_FUN(name,macro) \
207
+ static VALUE sctrace_hop_icmp_##name(int argc, VALUE *argv, VALUE self) \
208
+ { \
209
+ scamper_trace_t *trace; \
210
+ scamper_trace_hop_t *trace_hop; \
211
+ PROCESS_HOP_PARAMS; \
212
+ Data_Get_Struct(self, scamper_trace_t, trace); \
213
+ trace_hop = find_trace_hop(trace, NUM2INT(hop), NUM2INT(attempt)); \
214
+ if (!trace_hop) { \
215
+ return Qnil; \
216
+ } \
217
+ return SCAMPER_TRACE_HOP_IS_ICMP_##macro(trace_hop) ? Qtrue : Qfalse; \
218
+ }
219
+
220
+ HOP_ICMP_QUERY_FUN(ttl_exp, TTL_EXP);
221
+ HOP_ICMP_QUERY_FUN(ttl_exp_trans, TTL_EXP_TRANS);
222
+ HOP_ICMP_QUERY_FUN(packet_too_big, PACKET_TOO_BIG);
223
+ HOP_ICMP_QUERY_FUN(unreach, UNREACH);
224
+ HOP_ICMP_QUERY_FUN(unreach_port, UNREACH_PORT);
225
+ HOP_ICMP_QUERY_FUN(echo_reply, ECHO_REPLY);
226
+
227
+
228
+ /***************************************************************************/
229
+
230
+ static void sctrace_free(void *data)
231
+ {
232
+ if (data) {
233
+ scamper_trace_free((scamper_trace_t *)data);
234
+ }
235
+ }
236
+
237
+ static VALUE sctrace_alloc(VALUE klass)
238
+ {
239
+ return Data_Wrap_Struct(klass, 0, sctrace_free, 0);
240
+ }
241
+
242
+
243
+ static VALUE sctrace_init(VALUE self)
244
+ {
245
+ /*
246
+ ** Workaround for bug in scamper-cvs-20070523-p8: scamper wrote a trace
247
+ ** with hop_count = 5 and hops == NULL, which triggers a segfault.
248
+ *
249
+ * Program received signal SIGSEGV, Segmentation fault.
250
+ * 0x283f06cc in find_trace_hop (trace=0x8635e40, hop=4, attempt=0)
251
+ * at sctrace.c:109
252
+ * 109 trace_hop = trace->hops[hop];
253
+ * (gdb) p trace
254
+ * $1 = (scamper_trace_t *) 0x8635e40
255
+ * (gdb) p *trace
256
+ * $2 = {list = 0x829f2c0, cycle = 0x829f2e0, src = 0x82a7d50, dst = 0x86c39e0,
257
+ * start = {tv_sec = 1189778005, tv_usec = 809855}, hops = 0x0, hop_count = 5,
258
+ * stop_reason = 5 '\005', stop_data = 0 '\0', type = 2 '\002', flags = 0 '\0',
259
+ * attempts = 3 '\003', hoplimit = 0 '\0', gaplimit = 0 '\0',
260
+ * firsthop = 1 '\001', tos = 0 '\0', wait = 5 '\005', loops = 1 '\001',
261
+ * probe_size = 44, sport = 46235, dport = 33545, tlvs = 0x0, pmtud = 0x0}
262
+ */
263
+ scamper_trace_t *trace = NULL;
264
+ Data_Get_Struct(self, scamper_trace_t, trace);
265
+ if (trace && !trace->hops) {
266
+ trace->hop_count = 0;
267
+ }
268
+
269
+ rb_ivar_set(self, iv_element_type, INT2FIX(SCAMPER_FILE_OBJ_TRACE));
270
+ rb_ivar_set(self, iv_dest_response, Qnil);
271
+ rb_ivar_set(self, iv_list, Qnil);
272
+ rb_ivar_set(self, iv_cycle, Qnil);
273
+ return self;
274
+ }
275
+
276
+
277
+ static VALUE sctrace_src(VALUE self)
278
+ {
279
+ scamper_trace_t *trace;
280
+ char buf[128];
281
+
282
+ Data_Get_Struct(self, scamper_trace_t, trace);
283
+ return rb_str_new2(scamper_addr_tostr(trace->src, buf, 128));
284
+ }
285
+
286
+
287
+ static VALUE sctrace_dst(VALUE self)
288
+ {
289
+ scamper_trace_t *trace;
290
+ char buf[128];
291
+
292
+ Data_Get_Struct(self, scamper_trace_t, trace);
293
+ return rb_str_new2(scamper_addr_tostr(trace->dst, buf, 128));
294
+ }
295
+
296
+
297
+ static VALUE sctrace_src_cmp(VALUE self, VALUE other)
298
+ {
299
+ scamper_trace_t *self_trace;
300
+ scamper_trace_t *other_trace;
301
+
302
+ if (other == self) {
303
+ return INT2FIX(0);
304
+ }
305
+
306
+ /* See Pragmatic Programmer's book. */
307
+ if (TYPE(other) != T_DATA || RDATA(other)->dfree != sctrace_free) {
308
+ rb_raise(rb_eTypeError, "wrong argument type");
309
+ }
310
+
311
+ Data_Get_Struct(self, scamper_trace_t, self_trace);
312
+ Data_Get_Struct(other, scamper_trace_t, other_trace);
313
+ return INT2FIX(scamper_addr_cmp(self_trace->src, other_trace->src));
314
+ }
315
+
316
+
317
+ static VALUE sctrace_dst_cmp(VALUE self, VALUE other)
318
+ {
319
+ scamper_trace_t *self_trace;
320
+ scamper_trace_t *other_trace;
321
+
322
+ if (other == self) {
323
+ return INT2FIX(0);
324
+ }
325
+
326
+ /* See Pragmatic Programmer's book. */
327
+ if (TYPE(other) != T_DATA || RDATA(other)->dfree != sctrace_free) {
328
+ rb_raise(rb_eTypeError, "wrong argument type");
329
+ }
330
+
331
+ Data_Get_Struct(self, scamper_trace_t, self_trace);
332
+ Data_Get_Struct(other, scamper_trace_t, other_trace);
333
+ return INT2FIX(scamper_addr_cmp(self_trace->dst, other_trace->dst));
334
+ }
335
+
336
+
337
+ static VALUE sctrace_write_to(VALUE self, VALUE file)
338
+ {
339
+ rb_funcall(file, meth_write_trace, 1, self);
340
+ return self;
341
+ }
342
+
343
+
344
+ static VALUE sctrace_list(VALUE self)
345
+ {
346
+ VALUE retval = rb_ivar_get(self, iv_list);
347
+ if (NIL_P(retval)) {
348
+ scamper_trace_t *trace;
349
+ Data_Get_Struct(self, scamper_trace_t, trace);
350
+ if (!trace->list) { /* not sure this can ever be NULL; better safe than */
351
+ return Qnil;
352
+ }
353
+
354
+ retval = sclist_create(scamper_list_use(trace->list));
355
+ rb_ivar_set(self, iv_list, retval);
356
+ }
357
+ return retval;
358
+ }
359
+
360
+
361
+ static VALUE sctrace_cycle(VALUE self)
362
+ {
363
+ VALUE retval = rb_ivar_get(self, iv_cycle);
364
+ if (NIL_P(retval)) {
365
+ scamper_trace_t *trace;
366
+ Data_Get_Struct(self, scamper_trace_t, trace);
367
+ if (!trace->cycle) { /* may be NULL if no cycle specified at scamper run */
368
+ return Qnil;
369
+ }
370
+
371
+ retval = sccycle_create(scamper_cycle_use(trace->cycle),
372
+ SCAMPER_FILE_OBJ_CYCLE_DEF);
373
+ rb_ivar_set(self, iv_cycle, retval);
374
+ }
375
+ return retval;
376
+ }
377
+
378
+
379
+ static VALUE sctrace_set_cycle(VALUE self, VALUE cycle_v)
380
+ {
381
+ scamper_trace_t *trace;
382
+ scamper_cycle_t *cycle;
383
+
384
+ Data_Get_Struct(self, scamper_trace_t, trace);
385
+
386
+ /* See Pragmatic Programmer's book. */
387
+ if (TYPE(cycle_v) != T_DATA || RDATA(cycle_v)->dfree != sccycle_free) {
388
+ rb_raise(rb_eTypeError, "wrong argument type");
389
+ }
390
+
391
+ Data_Get_Struct(cycle_v, scamper_cycle_t, cycle);
392
+
393
+ scamper_list_free(trace->list);
394
+ scamper_cycle_free(trace->cycle);
395
+
396
+ trace->list = cycle->list;
397
+ trace->cycle = cycle;
398
+
399
+ scamper_list_use(trace->list);
400
+ scamper_cycle_use(trace->cycle);
401
+ }
402
+
403
+
404
+ /*-------------------------------------------------------------------------*/
405
+
406
+ static VALUE sctrace_attr_hop_addr(int argc, VALUE *argv, VALUE self)
407
+ {
408
+ scamper_trace_t *trace;
409
+ scamper_trace_hop_t *trace_hop;
410
+ char buf[128];
411
+
412
+ PROCESS_HOP_PARAMS;
413
+ Data_Get_Struct(self, scamper_trace_t, trace);
414
+ trace_hop = find_trace_hop(trace, NUM2INT(hop), NUM2INT(attempt));
415
+ if (!trace_hop) {
416
+ return Qnil;
417
+ }
418
+ return rb_str_new2(scamper_addr_tostr(trace_hop->hop_addr, buf, 128));
419
+ }
420
+
421
+
422
+ static VALUE sctrace_attr_hop_rtt(int argc, VALUE *argv, VALUE self)
423
+ {
424
+ scamper_trace_t *trace;
425
+ scamper_trace_hop_t *trace_hop;
426
+
427
+ PROCESS_HOP_PARAMS;
428
+ Data_Get_Struct(self, scamper_trace_t, trace);
429
+ trace_hop = find_trace_hop(trace, NUM2INT(hop), NUM2INT(attempt));
430
+ if (!trace_hop) {
431
+ return Qnil;
432
+ }
433
+ return rb_float_new(1000.0 * trace_hop->hop_rtt.tv_sec
434
+ + trace_hop->hop_rtt.tv_usec / 1000.0);
435
+ }
436
+
437
+
438
+ /* This is faster than calling sprintf("%0.3f"). */
439
+ static VALUE sctrace_attr_hop_rtt_str(int argc, VALUE *argv, VALUE self)
440
+ {
441
+ scamper_trace_t *trace;
442
+ scamper_trace_hop_t *trace_hop;
443
+ VALUE retval;
444
+ char buf[128];
445
+
446
+ PROCESS_HOP_PARAMS;
447
+ Data_Get_Struct(self, scamper_trace_t, trace);
448
+ trace_hop = find_trace_hop(trace, NUM2INT(hop), NUM2INT(attempt));
449
+ if (!trace_hop) {
450
+ return Qnil;
451
+ }
452
+
453
+ sprintf(buf, "%d.%03d",
454
+ 1000 * trace_hop->hop_rtt.tv_sec + trace_hop->hop_rtt.tv_usec / 1000,
455
+ trace_hop->hop_rtt.tv_usec % 1000);
456
+ return rb_str_new2(buf);
457
+ }
458
+
459
+
460
+ static VALUE sctrace_hop_exists(int argc, VALUE *argv, VALUE self)
461
+ {
462
+ scamper_trace_t *trace;
463
+ scamper_trace_hop_t *trace_hop;
464
+
465
+ PROCESS_HOP_PARAMS;
466
+ Data_Get_Struct(self, scamper_trace_t, trace);
467
+ trace_hop = find_trace_hop(trace, NUM2INT(hop), NUM2INT(attempt));
468
+ return (trace_hop ? Qtrue : Qfalse);
469
+ }
470
+
471
+
472
+ static VALUE sctrace_each_hop(VALUE self)
473
+ {
474
+ scamper_trace_t *trace;
475
+ scamper_trace_hop_t *trace_hop;
476
+ VALUE yield_args;
477
+ int hop;
478
+
479
+ Data_Get_Struct(self, scamper_trace_t, trace);
480
+
481
+ if (trace->hop_count == 0) {
482
+ return self;
483
+ }
484
+
485
+ yield_args = rb_ary_new();
486
+ for (hop = 0; hop < trace->hop_count; hop++) {
487
+ trace_hop = find_trace_hop(trace, hop, 0);
488
+
489
+ rb_ary_store(yield_args, 0, INT2FIX(hop));
490
+ rb_ary_store(yield_args, 1, (trace_hop ? Qtrue : Qfalse));
491
+ rb_yield(yield_args);
492
+ }
493
+
494
+ return self;
495
+ }
496
+
497
+
498
+ static VALUE sctrace_each_attempt(VALUE self, VALUE hop)
499
+ {
500
+ scamper_trace_t *trace;
501
+ scamper_trace_hop_t *trace_hop;
502
+
503
+ Data_Get_Struct(self, scamper_trace_t, trace);
504
+
505
+ trace_hop = find_trace_hop(trace, NUM2INT(hop), 0);
506
+ if (trace_hop) {
507
+ int attempt = 0;
508
+ rb_yield(INT2FIX(0));
509
+ while (trace_hop = trace_hop->hop_next) {
510
+ rb_yield(INT2FIX(++attempt));
511
+ }
512
+ }
513
+
514
+ return self;
515
+ }
516
+
517
+
518
+ static VALUE sctrace_each_hop_and_attempt(VALUE self)
519
+ {
520
+ scamper_trace_t *trace;
521
+ scamper_trace_hop_t *trace_hop;
522
+ VALUE yield_args;
523
+ int hop;
524
+
525
+ Data_Get_Struct(self, scamper_trace_t, trace);
526
+
527
+ yield_args = rb_ary_new();
528
+ for (hop = 0; hop < trace->hop_count; hop++) {
529
+ int attempt = 0;
530
+ trace_hop = find_trace_hop(trace, hop, 0);
531
+
532
+ rb_ary_store(yield_args, 0, INT2FIX(hop));
533
+ rb_ary_store(yield_args, 1, INT2FIX(0));
534
+ rb_ary_store(yield_args, 2, (trace_hop ? Qtrue : Qfalse));
535
+ rb_yield(yield_args);
536
+
537
+ if (trace_hop) {
538
+ rb_ary_store(yield_args, 2, Qtrue);
539
+ while (trace_hop = trace_hop->hop_next) {
540
+ rb_ary_store(yield_args, 1, INT2FIX(++attempt));
541
+ rb_yield(yield_args);
542
+ }
543
+ }
544
+ }
545
+
546
+ return self;
547
+ }
548
+
549
+
550
+ /*
551
+ ** Whether the given hop represents a response from the destination.
552
+ **
553
+ ** Note that there can be more than one response from the destination
554
+ ** in a given trace.
555
+ **
556
+ ** For compatibility, the code of this function is based on
557
+ ** scamper-cvs-20070523h:sc_analysis_dump.c:print_trace(). Therefore,
558
+ ** under the strictest interpretation of copyright, the following code
559
+ ** is licensed under GPLv2 (no later), the license of scamper itself.
560
+ */
561
+ static int hop_is_dest_response(scamper_trace_t *trace,
562
+ scamper_trace_hop_t *trace_hop)
563
+ {
564
+ if (trace->hop_count > 0 && trace->stop_reason != SCAMPER_TRACE_STOP_ERROR) {
565
+ if (SCAMPER_TRACE_HOP_IS_ICMP_UNREACH_PORT(trace_hop)) {
566
+ if (trace->type == SCAMPER_TRACE_TYPE_UDP
567
+ || trace->type == SCAMPER_TRACE_TYPE_UDP_PARIS
568
+ || trace->type == SCAMPER_TRACE_TYPE_TCP) {
569
+ return 1;
570
+ }
571
+ }
572
+
573
+ if (SCAMPER_TRACE_HOP_IS_ICMP_ECHO_REPLY(trace_hop)) {
574
+ if (trace->type == SCAMPER_TRACE_TYPE_ICMP_ECHO
575
+ || trace->type == SCAMPER_TRACE_TYPE_ICMP_ECHO_PARIS) {
576
+ return 1;
577
+ }
578
+ }
579
+
580
+ if ((trace_hop->hop_flags & SCAMPER_TRACE_HOP_FLAG_TCP) != 0 &&
581
+ trace->type == SCAMPER_TRACE_TYPE_TCP) {
582
+ return 1;
583
+ }
584
+ }
585
+
586
+ return 0;
587
+ }
588
+
589
+
590
+ static VALUE sctrace_hop_dest_response(int argc, VALUE *argv, VALUE self)
591
+ {
592
+ scamper_trace_t *trace;
593
+ scamper_trace_hop_t *trace_hop;
594
+
595
+ PROCESS_HOP_PARAMS;
596
+ Data_Get_Struct(self, scamper_trace_t, trace);
597
+ trace_hop = find_trace_hop(trace, NUM2INT(hop), NUM2INT(attempt));
598
+ if (!trace_hop) {
599
+ return Qnil;
600
+ }
601
+ return hop_is_dest_response(trace, trace_hop) ? Qtrue : Qfalse;
602
+ }
603
+
604
+
605
+ /*
606
+ ** NOTE: This uses the same algorithm as sc_analysis_dump for compatibility.
607
+ ** Specifically, it examines the hops in reverse order by hop number
608
+ ** and thus picks out the last response from the destination.
609
+ ** (Yes, there are traces with multiple responses from the destination
610
+ ** at different hop distances.)
611
+ */
612
+ static VALUE sctrace_find_dest_response(VALUE self)
613
+ {
614
+ scamper_trace_t *trace;
615
+ scamper_trace_hop_t *trace_hop;
616
+ int hop, attempt;
617
+ VALUE retval;
618
+
619
+ retval = rb_ivar_get(self, iv_dest_response);
620
+ if (!NIL_P(retval)) {
621
+ return (RARRAY(retval)->len == 0 ? Qnil : retval);
622
+ }
623
+
624
+ retval = rb_ary_new();
625
+ retval = rb_ivar_set(self, iv_dest_response, retval);
626
+
627
+ Data_Get_Struct(self, scamper_trace_t, trace);
628
+
629
+ /* sc_analysis_dump checks stop_reason like this, though I don't know why. */
630
+ if (trace->hop_count == 0
631
+ || trace->stop_reason == SCAMPER_TRACE_STOP_ERROR) {
632
+ return Qnil;
633
+ }
634
+
635
+ for (hop = trace->hop_count - 1; hop >= 0; hop--) {
636
+ attempt = 0;
637
+ trace_hop = find_trace_hop(trace, hop, 0);
638
+ while (trace_hop) {
639
+ if (hop_is_dest_response(trace, trace_hop)) {
640
+ rb_ary_push(retval, INT2FIX(hop));
641
+ rb_ary_push(retval, INT2FIX(attempt));
642
+ return retval;
643
+ }
644
+
645
+ ++attempt;
646
+ trace_hop = trace_hop->hop_next;
647
+ }
648
+ }
649
+
650
+ return Qnil;
651
+ }
652
+
653
+
654
+ static VALUE sctrace_dest_rtt(VALUE self)
655
+ {
656
+ scamper_trace_t *trace;
657
+ scamper_trace_hop_t *trace_hop;
658
+ VALUE dest_response;
659
+ int hop, attempt;
660
+
661
+ dest_response = sctrace_find_dest_response(self);
662
+ if (NIL_P(dest_response)) {
663
+ return Qnil;
664
+ }
665
+
666
+ hop = FIX2INT(rb_ary_entry(dest_response, 0));
667
+ attempt = FIX2INT(rb_ary_entry(dest_response, 1));
668
+
669
+ Data_Get_Struct(self, scamper_trace_t, trace);
670
+ trace_hop = find_trace_hop(trace, hop, attempt);
671
+ return rb_float_new(1000.0 * trace_hop->hop_rtt.tv_sec
672
+ + trace_hop->hop_rtt.tv_usec / 1000.0);
673
+ }
674
+
675
+
676
+ /* This is faster than calling sprintf("%0.3f"). */
677
+ static VALUE sctrace_dest_rtt_str(VALUE self)
678
+ {
679
+ scamper_trace_t *trace;
680
+ scamper_trace_hop_t *trace_hop;
681
+ VALUE dest_response;
682
+ char buf[128];
683
+ int hop, attempt;
684
+
685
+ dest_response = sctrace_find_dest_response(self);
686
+ if (NIL_P(dest_response)) {
687
+ return Qnil;
688
+ }
689
+
690
+ hop = FIX2INT(rb_ary_entry(dest_response, 0));
691
+ attempt = FIX2INT(rb_ary_entry(dest_response, 1));
692
+
693
+ Data_Get_Struct(self, scamper_trace_t, trace);
694
+ trace_hop = find_trace_hop(trace, hop, attempt);
695
+
696
+ sprintf(buf, "%d.%03d",
697
+ 1000 * trace_hop->hop_rtt.tv_sec + trace_hop->hop_rtt.tv_usec / 1000,
698
+ trace_hop->hop_rtt.tv_usec % 1000);
699
+ return rb_str_new2(buf);
700
+ }
701
+
702
+
703
+ /* NOTE: This uses the same algorithm as sc_analysis_dump for compatibility. */
704
+ static VALUE sctrace_complete(VALUE self)
705
+ {
706
+ scamper_trace_t *trace;
707
+ scamper_trace_hop_t *trace_hop;
708
+ VALUE dest_response;
709
+ int dest_hop;
710
+ int hop, attempt;
711
+
712
+ dest_response = sctrace_find_dest_response(self);
713
+ if (NIL_P(dest_response)) {
714
+ return Qfalse;
715
+ }
716
+ dest_hop = FIX2INT(rb_ary_entry(dest_response, 0));
717
+
718
+ Data_Get_Struct(self, scamper_trace_t, trace);
719
+
720
+ for (hop = 0; hop < trace->hop_count && hop < dest_hop; hop++) {
721
+ if (!find_trace_hop(trace, hop, 0)) {
722
+ return Qfalse;
723
+ }
724
+ }
725
+ return hop == dest_hop ? Qtrue : Qfalse;
726
+ }
727
+
728
+
729
+ /*-------------------------------------------------------------------------*/
730
+
731
+ VALUE sctrace_create(scamper_trace_t *trace)
732
+ {
733
+ VALUE obj = Data_Wrap_Struct(cTrace, 0, sctrace_free, trace);
734
+
735
+ if (NIL_P(sctrace_init(obj))) {
736
+ return Qnil;
737
+ }
738
+
739
+ return obj;
740
+ }
741
+
742
+
743
+ /***************************************************************************/
744
+ /***************************************************************************/
745
+
746
+ void Init_sctrace(void)
747
+ {
748
+ ID private_class_method_ID, private_ID;
749
+ ID new_ID, dup_ID, clone_ID;
750
+
751
+ iv_element_type = rb_intern("@element_type");
752
+ iv_dest_response = rb_intern("@dest_response");
753
+ iv_list = rb_intern("@list");
754
+ iv_cycle = rb_intern("@cycle");
755
+ meth_write_trace = rb_intern("write_trace");
756
+
757
+ cTrace = rb_define_class_under(mWarts, "Trace", rb_cObject);
758
+
759
+ DEF_CONST(STOP_NONE);
760
+ DEF_CONST(STOP_COMPLETED);
761
+ DEF_CONST(STOP_UNREACH);
762
+ DEF_CONST(STOP_ICMP);
763
+ DEF_CONST(STOP_LOOP);
764
+ DEF_CONST(STOP_DEAD);
765
+ DEF_CONST(STOP_ERROR);
766
+ #ifdef SCAMPER_TRACE_STOP_HOPLIMIT /* scamper-cvs-20070523 */
767
+ DEF_CONST(STOP_HOPLIMIT);
768
+ #endif
769
+
770
+ DEF_CONST(FLAG_ALLATTEMPTS);
771
+ DEF_CONST(FLAG_PMTUD);
772
+ DEF_CONST(FLAG_DL);
773
+
774
+ DEF_CONST(TYPE_ICMP_ECHO);
775
+ DEF_CONST(TYPE_UDP);
776
+ DEF_CONST(TYPE_TCP);
777
+ DEF_CONST(TYPE_ICMP_ECHO_PARIS);
778
+ DEF_CONST(TYPE_UDP_PARIS);
779
+
780
+ DEF_CONST(HOP_FLAG_TS_SOCK_RX);
781
+ DEF_CONST(HOP_FLAG_TS_DL_TX);
782
+ DEF_CONST(HOP_FLAG_TS_DL_RX);
783
+ DEF_CONST(HOP_FLAG_TS_TSC);
784
+ DEF_CONST(HOP_FLAG_REPLY_TTL);
785
+
786
+ DEF_ATTR(list_id);
787
+ DEF_ATTR(list_name);
788
+ DEF_ATTR(list_descr);
789
+ DEF_ATTR(list_monitor);
790
+
791
+ DEF_ATTR(cycle_id);
792
+ DEF_ATTR(cycle_start_time);
793
+ DEF_ATTR(cycle_stop_time);
794
+ DEF_ATTR(cycle_hostname);
795
+
796
+ DEF_ATTR(start);
797
+ DEF_ATTR(start_usec);
798
+
799
+ DEF_ATTR(hop_count);
800
+ DEF_ATTR(stop_reason);
801
+ DEF_ATTR(stop_data);
802
+ DEF_ATTR(type);
803
+ DEF_ATTR(flags);
804
+ DEF_ATTR(attempts);
805
+ DEF_ATTR(hoplimit);
806
+ DEF_ATTR(gaplimit);
807
+ DEF_ATTR(firsthop);
808
+ DEF_ATTR(tos);
809
+ DEF_ATTR(wait);
810
+ DEF_ATTR(loops);
811
+ DEF_ATTR(probe_size);
812
+ DEF_ATTR(sport);
813
+ DEF_ATTR(dport);
814
+
815
+ DEF_HOP_ATTR(hop_addr);
816
+ DEF_HOP_ATTR(hop_flags);
817
+ DEF_HOP_ATTR(hop_probe_id);
818
+ DEF_HOP_ATTR(hop_probe_ttl);
819
+ DEF_HOP_ATTR(hop_reply_ttl);
820
+ DEF_HOP_ATTR(hop_probe_size);
821
+ DEF_HOP_ATTR(hop_reply_size);
822
+ DEF_HOP_ATTR(hop_icmp_type);
823
+ DEF_HOP_ATTR(hop_icmp_code);
824
+ DEF_HOP_ATTR(hop_tcp_flags);
825
+ DEF_HOP_ATTR(hop_rtt);
826
+ DEF_HOP_ATTR(hop_rtt_str);
827
+ DEF_HOP_ATTR(hop_rtt_sec);
828
+ DEF_HOP_ATTR(hop_rtt_usec);
829
+
830
+ rb_define_alloc_func(cTrace, sctrace_alloc);
831
+
832
+ rb_define_attr(cTrace, "element_type", 1, 0);
833
+
834
+ rb_define_method(cTrace, "initialize", sctrace_init, 0);
835
+ rb_define_method(cTrace, "src", sctrace_src, 0);
836
+ rb_define_method(cTrace, "dst", sctrace_dst, 0);
837
+ rb_define_method(cTrace, "src_cmp", sctrace_src_cmp, 1);
838
+ rb_define_method(cTrace, "dst_cmp", sctrace_dst_cmp, 1);
839
+ rb_define_method(cTrace, "write_to", sctrace_write_to, 1);
840
+ rb_define_method(cTrace, "list", sctrace_list, 0);
841
+ rb_define_method(cTrace, "cycle", sctrace_cycle, 0);
842
+ rb_define_method(cTrace, "cycle=", sctrace_set_cycle, 1);
843
+ rb_define_method(cTrace, "hop_exists?", sctrace_hop_exists, -1);
844
+ rb_define_method(cTrace, "hop_dest_response?", sctrace_hop_dest_response,-1);
845
+ rb_define_method(cTrace, "find_dest_response", sctrace_find_dest_response,0);
846
+ rb_define_method(cTrace, "complete?", sctrace_complete, 0);
847
+ rb_define_method(cTrace, "dest_rtt", sctrace_dest_rtt, 0);
848
+ rb_define_method(cTrace, "dest_rtt_str", sctrace_dest_rtt_str, 0);
849
+ rb_define_method(cTrace, "each_hop", sctrace_each_hop, 0);
850
+ rb_define_method(cTrace, "each_attempt", sctrace_each_attempt, 1);
851
+ rb_define_method(cTrace, "each_hop_and_attempt",
852
+ sctrace_each_hop_and_attempt, 0);
853
+
854
+ rb_define_alias(cTrace, "dest_responded?", "find_dest_response");
855
+ rb_define_alias(cTrace, "each", "each_hop_and_attempt");
856
+
857
+ DEF_HOP_ICMP_QUERY(ttl_exp);
858
+ DEF_HOP_ICMP_QUERY(ttl_exp_trans);
859
+ DEF_HOP_ICMP_QUERY(packet_too_big);
860
+ DEF_HOP_ICMP_QUERY(unreach);
861
+ DEF_HOP_ICMP_QUERY(unreach_port);
862
+ DEF_HOP_ICMP_QUERY(echo_reply);
863
+
864
+ private_class_method_ID = rb_intern("private_class_method");
865
+ private_ID = rb_intern("private");
866
+ new_ID = rb_intern("new");
867
+ dup_ID = rb_intern("dup");
868
+ clone_ID = rb_intern("clone");
869
+
870
+ rb_funcall(cTrace, private_class_method_ID, 1, ID2SYM(new_ID));
871
+ rb_funcall(cTrace, private_ID, 1, ID2SYM(dup_ID));
872
+ rb_funcall(cTrace, private_ID, 1, ID2SYM(clone_ID));
873
+ }