chiark / gitweb /
478fc3fc9b57bf59b6c92b8e252246d98eef500b
[vinegar-ip.git] / make-probes.tcl
1 #!/usr/bin/tclsh
2
3
4 proc debug {level str} {
5     global debug_level
6     if {$level <= $debug_level} { puts stderr "debug\[$level] $str" }
7 }
8
9 proc manyset {list args} {
10     foreach val $list var $args {
11         upvar 1 $var my
12         set my $val
13     }
14 }
15
16
17 proc start_gen {seed} {
18     global getlog_log
19     random-bytes-init $seed
20     set getlog_log {}
21 }
22
23 proc packet-len {p} { expr {[string length $p]/2} }
24
25 proc packet-csum-ip {packet} {
26     set cs 0
27     append packet 00
28     while {[regexp {^([0-9a-f]{4})(.*)$} $packet dummy this packet]} {
29         set cs [expr "\$cs + 0x$this"]
30         debug 7 [format "0x%s 0x%08x" $this $cs]
31     }
32     return [expr {(($cs & 0xffff) + (($cs >> 16) & 0xffff)) ^ 0xffff}]
33 }
34
35 proc packet-fromstring {s} {
36     binary scan $s H* y
37     return $y
38 }
39
40 namespace eval Random-Bytes {
41     namespace export random-bytes random-bytes-init
42
43     proc random-bytes-init {seed} {
44         variable fh
45         catch { set h $fh; unset fh; close $h }
46         set fh [open |[list openssl bf-ofb < /dev/zero -e -k " $seed"] r]
47         fconfigure $fh -translation binary
48     }
49     proc random-bytes {n} {
50         variable fh
51         set x [read $fh $n]
52         if {[string length $x] != $n} {
53             set h $fh; unset fh; close $h
54             error "openssl bf-ofb exited unexpectedly"
55         }
56         set y [packet-fromstring $x]
57         return $y
58     }
59 }
60
61 namespace import Random-Bytes::*
62
63 proc choice-int {min max} {
64     set rv 0x[random-bytes 3]
65     return [expr {
66         int( double($rv) / double(0x1000000) * double($max+1-$min) )
67         + $min
68     }]
69 }
70
71 proc choice-prob {cv def} {
72     set prob [config $cv $def]
73     set rv 0x[random-bytes 3]
74     return [expr {$rv < double($prob)*0x1000000}]
75 }
76
77 proc choice-mult {args} {
78     if {!([llength $args] % 2)} { error "choice-mult must have default" }
79     set h 0x[random-bytes 3]
80     set x [expr { double($h) / double(0x1000000) }]
81     set cump 0.0
82     set def [lindex $args end]
83     set args [lreplace $args end end]
84     foreach {val p} $args {
85         set cump [expr {$cump + double($p)}]
86         if {$x < $cump} { return $val }
87     }
88     return $def
89 }
90
91 proc getlog {msg} {
92     upvar #0 getlog_log log
93     append log " $msg"
94     debug 2 "getlog $msg"
95 }
96
97 proc config {cv def} {
98     upvar #0 config/$cv v
99     if {[info exists v]} { return $v }
100     return $def
101 }
102
103
104 proc define {enum val name argnames body} {
105     upvar #0 enum/val2name/$enum v2n
106     upvar #0 enum/name2val/$enum n2v
107     set v2n($val) $name
108     set n2v($name) $val
109     proc enum/val/$enum/$val $argnames $body
110 }
111
112 proc depending-on {scope enum_and_var mtu mtuadjust args} {
113     upvar 1 $enum_and_var val
114     set mtu [expr {$mtu + $mtuadjust}]
115     set procname enum/val/$scope-$enum_and_var/[format %d $val]
116     if {[choice-prob $enum_and_var-unstruct 0.1] ||
117             [catch { info body $procname }]} {
118         getlog (junk)
119         get-for $scope-fill
120         get data rand 0 $mtu 1
121         return $data
122     } else {
123         uplevel 1 [list $procname] $mtu $args
124     }
125 }
126
127
128 proc get-for {scope} {
129     upvar 1 get/scope ns
130     set ns $scope
131 }
132
133 proc get {variable kind args} {
134     upvar 1 get/scope scope
135     upvar 1 $variable var
136     set var [eval [list get/$kind $scope $variable] $args]
137 }
138
139 proc get-config/number {val min max} { return $val }
140 proc get-config/v4addr {val} {
141     if {![regexp {^(\d+)\.(\d+)\.(\d+)\.(\d+)$} $val dummy a b c d]} {
142         error "bad v4addr ?$val?"
143     }
144     return [format 0x%02x%02x%02x%02x $a $b $c $d]
145 }
146
147 proc get-config {variable def kind args} {
148     # args currently ignored
149     upvar 1 get/scope scope
150     upvar 1 $variable var
151     set val [config $scope-$variable $def]
152     set var [eval [list get-config/$kind $val] $args]
153 }
154
155 proc get-enum-got {s v rv} {
156     upvar #0 enum/val2name/$s-$v v2n
157     if {[info exists v2n($rv)]} {
158         getlog "$v=$v2n($rv)\[$rv]"
159     } else {
160         getlog "$v=$rv"
161     }
162     return $rv
163 }
164
165 proc get/enum-rand {s v min max} {
166     set rv [choice-int $min $max]
167     return [get-enum-got $s $v $rv]
168 }
169
170 proc get/enum-def {s v} {
171     upvar #0 enum/val2name/$s-$v v2n
172     set rv [choice-int 1 [array size v2n]]
173     set rv [lindex [lsort [array names v2n]] [expr {$rv-1}]]
174     return [get-enum-got $s $v $rv]
175 }
176
177 proc get/enum {s v min max prand} {
178     get-for $s-$v
179     get any choice $prand
180     if {$any} {
181         return [get/enum-rand $s $v $min $max]
182     } else {
183         return [get/enum-def $s $v]
184     }
185 }
186
187 proc get/number {s v min max} {
188     set rv [choice-int $min $max]
189     getlog "$v=$rv"
190     return $rv
191 }
192
193 proc get/hex {s v min max} {
194     set rv [choice-int $min $max]
195     getlog [format %s=0x%x $v $rv]
196     return $rv
197 }
198
199 proc get/hex32 {s v} {
200     set rv [random-bytes 4]
201     getlog "$v=0x$rv"
202     return 0x$rv
203 }
204
205 proc get/flag {s v defprob} {
206     set rv [choice-prob $s-$v $defprob]
207     if {$rv} { getlog "$v" } else { getlog "!$v" }
208     return $rv
209 }
210
211 proc get/choice {s v defprob} {
212     set rv [choice-prob $s-$v $defprob]
213     if {$rv} { getlog "($v)" } else { getlog "(!$v)" }
214     return $rv
215 }
216
217 proc get/rand {s v minlen maxlen blockbytes} {
218     get-for $s-$v
219     if {$maxlen<0} { getlog (full!); return {} }
220     get l number [expr {$minlen/$blockbytes}] [expr {$maxlen/$blockbytes}]
221     return [random-bytes [expr {$l*$blockbytes}]]
222 }
223
224 proc get/ip-timestamp {s v} {
225     set rv [expr {[clock seconds] | 0x80000000}]
226     getlog "$v=[format %x $rv]"
227     return $rv
228 }
229
230 proc get/v4addr {s v} {
231     set rv 0x
232     set p {}
233     set d {}
234     for {set i 0} {$i<4} {incr i} {
235         set b [random-bytes 1]
236         append rv $b
237         append p $d [format %d 0x$b]
238         set d .
239     }
240     getlog "$v=$p"
241     return $rv
242 }
243
244 proc get/choice-mult {s v args} {
245     set rv [eval choice-mult $args]
246     getlog "($rv)"
247     return $rv
248 }
249
250 proc get/string {s v minlen maxlen first rest} {
251     set o {}
252     set now $first
253     for {set l [choice-int $minlen $maxlen]} {$l>0} {incr l -1} {
254         set cn [choice-int 0 [expr {[string length $now]-1}]]
255         append o [string index $now $cn]
256         set now $rest
257     }
258     getlog "$v=\"$o\""
259     return [packet-fromstring $o]
260 }
261
262 proc get/ntstring {s v minlen maxlen first rest} {
263     set s [get/string $s $v $minlen $maxlen $first $rest]
264     append s 00
265     append s [random-bytes $maxlen]
266     return [string range $s 0 [expr {$maxlen*2-1}]]
267 }
268
269 namespace eval Assembler {
270     namespace export assemble assembly-overwrite
271
272     proc assemble {outvarname format} {
273         # format should look like those RFC diagrams.  +-+-+ stuff and
274         # good formatting is mandatory.  You can have a single data
275         # item at the end ending in ..., which means append that data
276         # item.
277         #
278         # Field names are converted to lowercase; internal spaces
279         # are replaced with _.  They are then assumed to be
280         # variable names in the caller's scope.  The packet is
281         # assembled from those values (which must all be set)
282         # and stored in $varname in the caller's scope.
283         #
284         # Variables ?_whatever will be *set* with the location of the
285         # field in the string (in internal format); the corresponding
286         # `whatever' (with the ?_ stripped) will be read when assembling.
287         #
288         # Field names starting with digits are literal values instead.
289
290         variable cache
291         upvar 1 $outvarname out
292         if {[catch { set parsed $cache($format) }]} {
293             set parsed [parse $format]
294             set cache($format) $parsed
295         }
296
297         manyset $parsed outbytes lout
298         set out [string repeat 00 $outbytes]
299         foreach {?_location varname locvarname} $lout {
300             if {[regexp {^[0-9]} $varname]} {
301                 set value $varname
302             } else {
303                 set value [uplevel 1 [list set $varname]]
304             }
305             if {[string length $locvarname]} {
306                 upvar 1 $locvarname lv
307                 set lv ${?_location}
308             }
309             if {[catch {
310                 assembly-overwrite out location $value
311             } emsg]} {
312                 global errorInfo errorCode
313                 error $emsg \
314                         "$errorInfo\n    setting\n$varname at ${?_location}" \
315                         $errorCode
316             }
317         }
318     }
319
320     proc parse {format} {
321         set lno 0
322         set outbytes 0
323         set atend 0
324         debug 7 "ASSEMBLY $format"
325         set format [exec expand << $format]
326         foreach l [split $format "\n"] {
327             incr lno
328             if {[regexp -nocase {^ *\| +\| *$} $l]} {
329                 if {![info exists wordbits]} {
330                     error "vspace not in data @$lno\n?$l?"
331                 }
332                 incr words
333             } elseif {[regexp -nocase {^ *[|? a-z0-9.]+$} $l]} {
334                 if {[info exists words]} {
335                     error "data without delimline @$lno\n?$l?"
336                 }
337                 set words 1
338                 set cue $l
339             } elseif {[regexp {^ *[-+]+ *$} $l]} {
340                 set wordbits 0
341                 set newlineform {}
342                 while {[regexp {^([-= ]+)\+(.*)$} $l dummy before after]} {
343                     set atpos([string length $before]) $wordbits
344                     incr wordbits
345                     set l "$before=$after"
346                     append newlineform "@[string length $before]:$wordbits "
347                 }
348                 incr wordbits -1
349                 append newlineform $wordbits
350                 if {[info exists lineform]} {
351                     if {"$newlineform" != "$lineform"} {
352  error "format change @$lno\n?$l?\n$wordformat != $oldwordformat"
353                     }
354                     if {![info exists words] || $words<0} {
355                         error "consecutive delimlines @$lno\n?$l?"
356                     }
357                     incr outbytes [expr {$words*$wordbits/8}]
358                     set l $cue
359                     while {[regexp -nocase \
360                             {^([ =]*)\|( *[? a-z0-9]*[a-z0-9] *\.* *)\|(.*)$} \
361                             $l dummy before midpart after]} {
362                         debug 7 "RWORKG ?$l?"
363                         if {$atend} {
364                             error "two things at end @$lno\n?$l?"
365                         }
366                         set varname [string tolower [string trim $midpart]]
367                         if {[regexp {(.*[a-z0-9])\s*\.\.\.$} $varname \
368                                 dummy realvarname]} {
369                             set varname $realvarname
370                             set atend 1
371                         }
372                         set varname [string map {{ } _} $varname]
373                         set p1 [string length $before]
374                         set p2 [expr {
375                             [string length $before] +
376                             [string length $midpart] + 1
377                         }]
378                         if {![info exists atpos($p1)] ||
379                             ![info exists atpos($p2)]} {
380  error "vdelims not found @$lno $varname $p1 $p2 -- $lineform ?$cue?"
381                         }
382                         set bit1 [expr {
383                             $outbytes*8
384                             - $words*$wordbits
385                             + $atpos($p1)
386                         }]
387                         set bitlen [expr {
388                             $atpos($p2) - $atpos($p1) + ($words-1)*$wordbits
389                         }]
390                         if {$atend} {
391                             if {$bit1 % 8} {
392                                 error "atend not at byte @$lno\n?$l?"
393                             }
394                             set outbytes [expr {$bit1/8}]
395                             set location [list $bit1 0 $varname]
396                         } else {
397                             set location [list $bit1 $bitlen $varname]
398                         }
399                         if {[regexp {^\?_(.*)} $varname dummy realvarname]} {
400                             debug 7 "LOCATING $varname $location"
401                             set locvarname $varname
402                             set varname $realvarname
403                         } else {
404                             set locvarname {}
405                         }
406                         lappend lout $location $varname $locvarname
407                         set l "$before="
408                         append l [string repeat = [string length $midpart]]
409                         append l |$after
410                     }
411                     debug 7 "REMAIN ?$l?"
412                     if {![regexp {^[ =]*\|? *$} $l]} {
413                         error "unclear @$lno\n?$l?"
414                     }
415                 } else {
416                     if {$wordbits % 8 || $wordbits >32} {
417                         error "bad wordbits $wordbits @$lno ?$l? $newlineform"
418                     }
419                     set lineform $newlineform
420                 }
421                 catch { unset words }
422             } elseif {[regexp {^ *$} $l]} {
423             } else {
424                 error "huh? @$lno ?$l?"
425             }
426         }
427         debug 7 "ASSEMBLY\n$outbytes\n$lout\n"
428         return [list $outbytes $lout]
429     }
430
431     proc assembly-overwrite {outvarname locvarnameex value} {
432         upvar 1 $outvarname out
433         upvar 1 ?_$locvarnameex location
434         manyset $location bit1 bitlen diag
435         if {$bitlen > 0 && $bitlen != 32 && $value >= (1<<$bitlen)} {
436             error "$diag $value >= 2**$bitlen"
437         }
438         if {!($bit1 % 8) && !($bitlen % 8)} {
439             set char0no [expr {$bit1/4}]
440             set charlen [expr {$bitlen/4}]
441             set chareno [expr {$char0no + $charlen -1}]
442             if {$bitlen > 0} {
443  debug 8 "ASSEMBLY $diag == $value == 0x[format %x $value] (@$bit1+$bitlen)"
444                 set repl [format %0${charlen}x $value]
445                 set out [string replace $out $char0no $chareno $repl]
446             } else {
447  debug 8 "ASSEMBLY-ADD $diag (@$bit1)"
448                 # bitlen==0 => append
449                 set out [string range $out 0 $chareno]
450                 append out $value
451             }
452         } else {
453             while {$bitlen > 0} {
454                 set byteno [expr {$bit1 / 8}]
455                 set char0no [expr {$byteno*2}]
456                 set char1no [expr {$char0no+1}]
457                 set bytebit [expr {128>>($bit1 % 8)}]
458                 set byte 0x[string range $out $char0no $char1no]
459                 debug 7 "< $bit1+$bitlen $byteno $char0no $bytebit $byte $out"
460                 set byte [expr {
461                     ($value & (1<<($bitlen-1)))
462                     ? ($byte | $bytebit)
463                     : ($byte & ~$bytebit)
464                 }]
465                 set out [string replace $out $char0no $char1no \
466                         [format %02x $byte]]
467  debug 8 "> $bit1+$bitlen $byteno $char0no $bytebit 0x[format %02x $byte] $out"
468                 incr bitlen -1
469                 incr bit1
470             }
471         }
472     }
473 }
474 namespace import Assembler::*
475
476 proc gen_1_ip {mtu source_spec dest_spec} {
477     # RFC791
478     upvar #0 ip_proto proto
479     upvar #0 ip_source source
480     upvar #0 ip_dest dest
481
482     set source $source_spec
483     set dest $dest_spec
484     
485     get-for ip
486     set version 4
487     get tos hex 0x00 0xff
488     get id number 0x0000 0xffff
489     get df flag 0.5
490     if {$df || ![choice-prob ip-midfrag 0.05]} {
491         set mf 0
492         set frag 0
493     } {
494         get mf flag 0.5
495         get frag number 0 0x1fff
496     }
497     get-config ttl 255 number 0 255
498     get proto enum 1 255 0.05
499     set flags [expr {$df*2 + $mf}]
500
501     set header_checksum 0
502     set ihl 0
503     set total_length 0
504     assemble ip {
505    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
506    |Version| ? IHL |TOS            |       ? Total Length          |
507    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
508    |         Id                    |Flags|      Frag               |
509    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
510    |  TTL          |    Proto      |      ? Header Checksum        |
511    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
512    |                       Source                                  |
513    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
514    |                    Dest                                       |
515    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
516     }
517     # we don't do any IP options
518
519     set ihl [packet-len $ip]
520     if {$ihl % 4} { error "ihl not mult of 4 bytes" }
521     assembly-overwrite ip ihl [expr {$ihl / 4}]
522
523     set body [depending-on ip proto $mtu -$ihl]
524     set total_length [expr {[packet-len $ip] + [packet-len $body]}]
525
526     assembly-overwrite ip total_length $total_length
527     assembly-overwrite ip header_checksum [packet-csum-ip $ip]
528
529     append ip $body
530     return $ip
531 }
532
533 define ip-proto 1 icmp {mtu} {
534     # RFC792
535     get-for icmp
536     get type enum 0 255 0.2
537     manyset [depending-on icmp type $mtu -4] body code
538     if {![string length $code]} { get code number 0 255 }
539     set checksum 0
540     assemble icmp {
541    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
542    |     Type      |     Code      |        ? Checksum             |
543    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
544    |     Body ...                                                  |
545    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
546     }
547     assembly-overwrite icmp checksum [packet-csum-ip $icmp]
548     return $icmp
549 }
550
551 proc define-icmp-type-vanilla {num name} {
552     define icmp-type $num $name {mbl} "icmp-vanilla \$mbl [list $name]"
553 }
554 proc icmp-vanilla {mbl typename} {
555     get-for icmp-$typename
556     get code enum 0 255 0.4
557     get body rand 0 $mbl 1
558     return [list $body $code]
559 }
560
561 define-icmp-type-vanilla 3 unreach
562 define icmp-unreach-code 0 net {} {}
563 define icmp-unreach-code 1 host {} {}
564 define icmp-unreach-code 2 proto {} {}
565 define icmp-unreach-code 3 port {} {}
566 define icmp-unreach-code 4 fragneeded {} {}
567 define icmp-unreach-code 5 sourceroutefail {} {}
568
569 define-icmp-type-vanilla 11 timeout
570 define icmp-timeout-code 0 intransit {} {}
571 define icmp-timeout-code 1 fragment {} {}
572
573 define-icmp-type-vanilla 12 parameters
574 define icmp-parameters-code 0 seepointer {} {}
575
576 define-icmp-type-vanilla 4 sourcequench
577 define icmp-sourcequench-code 0 quench {} {}
578
579 define icmp-type 5 redirect {mbl} {
580     get-for icmp-redirect
581     get code enum 0 255 0.4
582     get gateway v4addr
583     get data rand 0 [expr {$mbl-4}] 1
584     assemble body {
585    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
586    |                 Gateway                                       |
587    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
588    |     Data ...                                                  |
589    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
590     }
591     return [list $body $code]
592 }
593
594 define icmp-redirect-code 0 net {} {}
595 define icmp-redirect-code 1 host {} {}
596 define icmp-redirect-code 2 net+tos {} {}
597 define icmp-redirect-code 3 host+tos {} {}
598
599 define icmp-type 8 ping {mbl} { icmp-echo $mbl }
600 define icmp-type 0 pong {mbl} { icmp-echo $mbl }
601 proc icmp-echo {mbl} {
602     get-for icmp-echo
603     get code enum 0 255 0.4
604     get id hex 0 0xffff
605     get seq hex 0 0xffff
606     get data rand 0 [expr {$mbl-8}] 1
607     assemble body {
608    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
609    |       Id                      |        Seq                    |
610    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
611    |     Data ...                                                  |
612    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
613     }
614     return [list $body $code]
615 }
616 define icmp-echo-code 0 echo {} {}
617
618 define icmp-type 13 timestamp {mbl} { icmp-timestamp }
619 define icmp-type 14 timestampreply {mbl} { icmp-timestamp }
620 proc icmp-timestamp {} {
621     get-for icmp-timestamp
622     get code enum 0 255 0.4
623     get id hex 0 0xffff
624     get seq hex 0 0xffff
625     get originate ip-timestamp
626     get receive ip-timestamp
627     get transmit ip-timestamp
628     assemble body {
629    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
630    |           Id                  |        Seq                    |
631    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
632    |     Originate                                                 |
633    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
634    |     Receive                                                   |
635    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
636    |     Transmit                                                  |
637    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
638     }
639     return [list $body $code]
640 }
641 define icmp-timestamp-code 0 timestamp {} {}
642
643 define icmp-type 15 inforequest {mbl} { icmp-inforeq }
644 define icmp-type 16 inforeply {mbl} { icmp-inforeq }
645 proc icmp-inforeq {} {
646     get-for icmp-inforeq
647     get code enum 0 255 0.4
648     get id hex 0 0xffff
649     get seq hex 0 0xffff
650     assemble body {
651    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
652    |           Id                  |        Seq                    |
653    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
654     }
655     return [list $body $code]
656 }
657 define icmp-inforeq-code 0 timestamp {} {}
658
659 # MAYADD ICMP traceroute RFC1393
660 # MAYADD ICMP router discovery RFC1256
661
662
663 define ip-proto 4 ip {mtu} {
664     # RFC2003
665     get-for ip-ip
666     get source v4addr
667     get dest v4addr
668     gen_1_ip $mtu $source $dest
669 }
670
671
672 define ip-proto 2 igmp {mtu} {
673     get-for igmp
674     get type enum 0 255 0.5
675     get timeout number 0 255
676     get group v4addr
677     set checksum 0
678     set extra {}
679     assemble igmp {
680    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
681    |      Type     |   Timeout     |         ? Checksum            |
682    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
683    |                         Group                                 |
684    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
685    |                       ? Extra ...                             |
686    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
687     }
688
689     if {[choice-prob igmp-extra 0.3]} {
690         get extra rand 1 [expr {$mtu - [packet-len $igmp]}] 1
691         assembly-overwrite igmp extra $extra
692     }
693     
694     assembly-overwrite igmp checksum [packet-csum-ip $igmp]
695     return $igmp
696 }
697
698 define igmp-type 17 membquery {} {}
699 define igmp-type 16 membreport {} {}
700 define igmp-type 23 leavegroup {} {}
701 define igmp-type 18 membreport {} {}
702
703
704 define ip-proto 51 ah {mtu} {
705     # RFC1826
706     get-for ah
707     get next number 0 255
708     get reserved hex 0 0xffff
709     get spi hex32
710     get auth_data rand 0 [expr {$mtu-8}] 4
711     set length [packet-len $auth_data]
712     assemble ah {
713      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
714      | Next          |   Length      |           RESERVED            |
715      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
716      |                    SPI                                        |
717      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
718      |                 Auth Data ...                                 |
719      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
720     }
721     get payload rand 0 [expr {$mtu - [packet-len $ah]}] 1
722     append ah $payload
723     return $ah
724 }
725
726
727 define ip-proto 17 udp {mtu} {
728     # RFC768
729     get-for udp
730
731     set csum_mode [choice-mult \
732             checksum_bad 0.10 \
733             checksum_none 0.20 \
734             checksum_good]
735
736     get style choice-mult \
737             request 0.15 \
738             reply 0.15 \
739             servers 0.20 \
740             random
741
742     if {"$style" != "random"} {
743         get port enum-def
744         set def_port $port
745     } else {
746         set def_port x
747     }
748     if {"$style" != "servers"} {
749         get port enum-rand 0 0xffff
750         set rand_port $port
751     }
752     switch -exact $style {
753         random  { set source_port $rand_port; set dest_port $rand_port }
754         request { set source_port $rand_port; set dest_port $def_port }
755         reply   { set source_port $def_port;  set dest_port $rand_port }
756         servers { set source_port $def_port;  set dest_port $def_port }
757     }
758
759     if {"$style" != "random"} {
760         set port $def_port
761         set data [depending-on udp port $mtu -8 $style]
762     } else {
763         get data rand 0 [expr {$mtu-8}] 1
764     }
765
766     set length 0
767     set checksum 0
768     assemble udp {
769    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
770    |       Source Port             |        Dest Port              |
771    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
772    |       ? Length                |      ? Checksum               |
773    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
774    |     Data ...                                                  |
775    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
776     }
777     set udp_length [packet-len $udp]
778     assembly-overwrite udp length $udp_length
779
780     if {"$csum_mode" == "checksum_none"} {
781         set checksum 0
782         getlog (nocsum)
783     } else {
784         global ip_source ip_dest ip_proto
785         assemble pseudo {
786    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
787    |       IP Source                                               |
788    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
789    |       IP Dest                                                 |
790    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
791    |       0       | IP Proto      |        UDP length             |
792    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
793         }
794         set checksum [packet-csum-ip "$pseudo$udp"]
795         if {!$checksum} { set checksum 0xffff }
796         if {"$csum_mode" == "checksum_bad"} {
797             get csumerror hex 1 0xffff
798             set checksum [expr {$checksum ^ $csumerror}]
799         }
800     }
801     assembly-overwrite udp checksum $checksum
802     return $udp
803 }
804
805 define udp-port 50 remailck {mtu style} {
806     # RFC1339
807     get-for remailck
808     if {"$style" == "request"} {
809         get what choice-mult \
810                 req-baduser 0.15 \
811                 req-auth 0.15 \
812                 resp-ok 0.15 \
813                 resp-auth 0.15 \
814                 req-user
815     } else {
816         get what choice-mult \
817                 req-baduser 0.15 \
818                 req-auth 0.15 \
819                 resp-auth 0.15 \
820                 req-user 0.15 \
821                 resp-ok
822     }
823     switch -exact $what {
824         req-user {
825             set auth 0
826             get user string 1 8 \
827                     abcdefghijklmnopqrustuvwxyz \
828                     abcdefghijklmnopqrustuvwxyz-0123456789_
829         }
830         req-baduser {
831             set auth 0
832             get user rand 0 [expr {$mtu - 4}] 1
833         }
834         req-auth {
835             get auth enum 0 31 0.5
836             set user [depending-on remailck auth $mtu -4]
837         }
838         resp-auth {
839             get auth hex 0 0xffff
840             set modified 0
841             set read 0
842         }
843         resp-ok {
844             get mail choice-mult \
845                     newmail 0.15 \
846                     oldmail 0.15 \
847                     nomail 0.20 \
848                     times
849             set auth 0
850             switch -exact $mail {
851                 newmail {
852                     set modified 0
853                     set read 1
854                 }
855                 oldmail {
856                     set modified 1
857                     set read 0
858                 }
859                 nomail {
860                     set modified 0
861                     set read 0
862                 }
863                 times {
864                     get modified number 1 600
865                     get read number 1 600
866                 }
867                 default { error "mail? $mail" }
868             }
869         }
870         default { error "what? $what" }
871     }
872     switch -glob $what {
873         req-* {
874             assemble payload {
875    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
876    |         Auth                                                  |
877    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
878    |     User ...                                                  |
879    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
880             }
881         }
882         resp-* {
883             assemble payload {
884    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
885    |         Auth                                                  |
886    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
887    |         Modified                                              |
888    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
889    |         Read                                                  |
890    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
891             }
892         }
893         default { error "what?? $what" }
894     }
895     return $payload
896 }
897
898 define remailck-auth 31 passwd {mtu} {
899     get-for remailck-passwd
900     get passwd string 6 8 \
901             0123456789abcdefghijklmnopqrstuvxwyz \
902             0123456789abcdefghijklmnopqrstuvxwyz
903     return $passwd
904 }
905
906 define udp-port 67 dhcpserv {mtu style} { return [dhcp $mtu] }
907 define udp-port 68 dhcpclient {mtu style} { return [dhcp $mtu] }
908 proc dhcp {mtu} {
909     get-for dhcp
910     get op enum 0 255 0.2
911     get htype enum 0 255 0.2
912     set hlen 6
913     get hops number 0 255
914     get xid hex32
915     get secs number 0 300
916     get flags hex 0 255
917     get ciaddr v4addr
918     get yiaddr v4addr
919     get siaddr v4addr
920     get giaddr v4addr
921     set chaddr [random-bytes 16]
922     get sname ntstring 0 64 \
923             0123456789abcdefghijklmnopqrstuvwxyz \
924             0123456789abcdefghijklmnopqrstuvwxyz.-+
925     get file ntstring 0 128 / \
926             0123456789abcdefghijklmnopqrstuvwxyz.-+/_
927
928     assemble dhcp {
929    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
930    |     op        |   htype       |   hlen        |   hops        |
931    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
932    |                            xid                                |
933    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
934    |           secs                |           flags               |
935    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
936    |                          ciaddr                               |
937    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
938    |                          yiaddr                               |
939    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
940    |                          siaddr                               |
941    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
942    |                          giaddr                               |
943    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
944     }
945     append dhcp $chaddr $sname $file
946
947     return $dhcp
948 }
949 define dhcp-op 1 request {} {}
950 define dhcp-op 2 reply {} {}
951 define dhcp-htype 1 ethernet {} {}
952
953
954 define ip-proto 6 tcp {mtu} {
955     # RFC793
956     get-for tcp
957
958     get source_port number 0 65535
959     get dest_port number 0 65535
960     get event choice-mult \
961             connect 0.15 \
962             accept 0.15 \
963             close 0.15 \
964             reset 0.15 \
965             weird 0.15 \
966             data
967     set s 0
968     set a 1
969     set f 0
970     set r 0
971     switch -exact $event {
972         connect { set s 1; set a 0 }
973         accept { set s 1 }
974         close { set f 1 }
975         reset { set a 0; set r 1 }
976         data { }
977         weird {
978             get s flag 0.5
979             get a flag 0.5
980             get f flag 0.5
981             get r flag 0.5
982         }
983         default { error "event? $event" }
984     }
985     get seq hex32
986     get ack hex32
987     if {[choice-prob tcp-smallwindow 0.7]} {
988         get window number 0 1
989     } else {
990         get window hex 0 0xffff
991     }
992     get p flag 0.5
993     get u flag 0.3
994     get urg hex 0 0xffff
995
996     set options {}
997     get optmode choice-mult badopt 0.3 opt 0.6 noopt
998     switch -exact $optmode {
999         noopt { }
1000         badopt {
1001             get options rand 1 60 1
1002         }
1003         opt {
1004             while {[choice-prob tcp-opts-more 0.4]} {
1005                 get opt enum 1 255 0.5
1006                 if {$opt == 1} {
1007                     assemble option {
1008    +-+-+-+-+-+-+-+-+
1009    |  Opt          |
1010    +-+-+-+-+-+-+-+-+
1011                     }
1012                 } else {
1013                     set data [depending-on tcp opt 6 0]
1014                     set option_len 0
1015                     assemble option {
1016    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1017    |  Opt          | ? Option Len  |
1018    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1019    |      Data ...                 |
1020    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1021                     }
1022                     assembly-overwrite option option_len [packet-len $option]
1023                 }
1024                 append options $option
1025             }
1026         }
1027     }
1028
1029     if {[choice-prob reserved-nonzero 0.25]} {
1030         get reserved hex 0 0x3f
1031     } else {
1032         set reserved 0
1033     }
1034
1035     if {[packet-len $options] % 4 || [get optxpad choice 0.15]} {
1036         if {"$optmode" != "badopt"} { append options 00 }
1037         set padlen [expr {3 - ([packet-len $options] + 3) % 4}]
1038         append options [random-bytes $padlen]
1039     }
1040
1041     set d_off 0
1042     set checksum 0
1043     assemble packet {
1044    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1045    |          Source Port          |       Dest Port               |
1046    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1047    |                        Seq                                    |
1048    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1049    |                    Ack                                        |
1050    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1051    |? D Off| Reserved  |U|A|P|R|S|F|            Window             |
1052    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1053    |         ? Checksum            |         Urg                   |
1054    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1055    |                    Options     ...                            |
1056    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1057     }
1058
1059     set d_off [expr {([packet-len $packet]/4) & 0x0f}]
1060     assembly-overwrite packet d_off $d_off
1061
1062     if {!($s || $r) || [get unexpdata flag 0.2]} {
1063         get data rand 0 [expr {$mtu - [packet-len $packet]}] 1
1064         append packet $data
1065     }
1066     set tcp_length [packet-len $packet]
1067
1068     global ip_source ip_dest ip_proto
1069     assemble pseudo {
1070    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1071    |       IP Source                                               |
1072    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1073    |       IP Dest                                                 |
1074    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1075    |       0       | IP Proto      |        TCP length             |
1076    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1077     }
1078
1079     set csum [packet-csum-ip "$pseudo$packet"]
1080     if {[choice-prob tcp-badcsum 0.1]} {
1081         get csumerror hex 1 0xffff
1082         set csum [expr {$csum ^ $csumerror}]
1083     }
1084     assembly-overwrite packet checksum $csum
1085     return $packet
1086 }
1087
1088 define tcp-opt 2 mss {mdl} {
1089     get-for tcp-opt
1090     get mss hex 0 0xffff
1091     assemble od {
1092    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1093    |        MSS                    |
1094    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1095     }
1096     return $od
1097 }
1098
1099
1100 namespace eval PCap {
1101     namespace export pcap_open pcap_write pcap_write_raw pcap_close
1102
1103     proc pcap_open {fn} {
1104         variable fh
1105         catch { close $fh }
1106         set fh [open $fn w]
1107         fconfigure $fh -translation binary
1108     }
1109
1110     proc pcap_close {} {
1111         variable fh
1112         if {![info exists fh]} return
1113         close $fh
1114         unset fh
1115     }
1116
1117     proc pcap_write_raw {packet} {
1118         variable fh
1119         if {![info exists fh]} return
1120         puts -nonewline $fh [binary format H* $packet]
1121     }
1122
1123     proc pcap_write {valdeflist} {
1124         foreach {kind valvar} $valdeflist {
1125             if {![regexp {^([usx])([0-9]+)} $kind dummy mode bits]} {
1126                 error "unknown kind $kind for $valvar"
1127             }
1128             set value [uplevel 1 [list set $valvar]]
1129             if {$bits % 8} { error "bits must be mult 8 in $kind for $valvar" }
1130             if {"$mode" != "x"} {
1131                 set v {}
1132                 set ov $value
1133                 for {set i 0} {$i<$bits/8} {incr i} {
1134                     append v [format %02x [expr {$value & 0xff}]]
1135                     set value [expr {$value >> 8}]
1136                 }
1137                 if {$value != 0 && $value != -1} {
1138                     error "value $ov more than $bits bits (residue=$value)"
1139                 }
1140                 set value $v
1141             }
1142             if {[string length $value] != $bits/4} {
1143                 error "$valvar value $value wrong length, not $bits bits"
1144             }
1145             pcap_write_raw $value
1146         }
1147     }
1148 }
1149 namespace import PCap::*
1150
1151 proc emit {seed} {
1152     global getlog_log errorInfo mtu fake_time_t
1153     global minframelen
1154
1155     get-for ip
1156     get-config source 127.0.0.1 v4addr
1157     get-config dest 127.0.0.1 v4addr
1158
1159     if {[catch {
1160         start_gen $seed
1161         set packet [gen_1_ip $mtu $source $dest]
1162         puts stdout "[format %6s $seed] $getlog_log\n       $packet"
1163     } emsg]} {
1164         puts stderr "\nERROR\n$seed\n\n$emsg\n\n$errorInfo\n\n"
1165         puts stdout "[format %6s $seed] error"
1166     } else {
1167         set ts_sec [incr fake_time_t]
1168         set ts_usec 0
1169
1170         set l [packet-len $packet]
1171         if {$l < $minframelen} {
1172             append packet [string repeat 00 [expr {$minframelen - $l}]]
1173         }
1174
1175         # RFC894
1176         set llpkt [random-bytes 12]
1177         append llpkt 0800
1178         append llpkt $packet
1179         
1180         set len [packet-len "$llpkt"]
1181         pcap_write {
1182             u32 ts_sec
1183             u32 ts_usec
1184             u32 len
1185             u32 len
1186         }
1187         pcap_write_raw $llpkt
1188     }
1189 }
1190
1191
1192 proc nextarg {} {
1193     global argv
1194     if {![llength $argv]} { error "need another arg" }
1195     set a [lindex $argv 0]
1196     set argv [lrange $argv 1 end]
1197     return $a
1198 }
1199
1200 proc nextarg_num {} { return [expr {[nextarg] + 0}] }
1201
1202 set debug_level 0
1203 set mtu 576
1204 set upto {}
1205 set xseed {}
1206 while {[regexp {^\-\-} [lindex $argv 0]]} {
1207     set o [nextarg]
1208     switch -exact -- $o {
1209         --infinite { set upto -1 }
1210         --debug { set debug_level [nextarg_num] }
1211         --upto { set upto [nextarg_num] }
1212         --write { pcap_open [nextarg] }
1213         --mtu { set mtu [nextarg_num] }
1214         --xseed { set xseed [nextarg] }
1215         --source { set config/ip-source [nextarg] }
1216         --dest { set config/ip-dest [nextarg] }
1217         default { error "bad option $o" }
1218     }
1219 }
1220
1221 set magic d4c3b2a1
1222 set version_major 2
1223 set version_minor 4
1224 set thiszone 0
1225 set sigfigs 0
1226 set snaplen 131073
1227
1228 # RFC894
1229 set linktype 1
1230 set minframelen 46
1231
1232 pcap_write {
1233     x32 magic
1234     u16 version_major
1235     u16 version_minor
1236     s32 thiszone
1237     s32 sigfigs
1238     s32 snaplen
1239     s32 linktype
1240 }
1241
1242 set fake_time_t [clock seconds]
1243
1244 if {[llength $argv]} {
1245     foreach count $argv { emit "$xseed$count" }
1246 } else {
1247     if {![string length $upto]} { set upto 100 }
1248     for {set count 1} {$upto<0 || $count<=$upto} {incr count} {
1249         emit "$xseed$count"
1250     }
1251 }
1252
1253 pcap_close