rb-wartslib 0.9.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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
+ }