6 proc debug {level str} {
8 if {$level < $debug_level} { puts stderr "debug\[$level] $str" }
11 proc manyset {list args} {
12 foreach val $list var $args {
19 proc start_gen {use_gen_counter} {
20 global gen_counter rand_counter getlog_log rand_buf
21 random-bytes-init $use_gen_counter
25 proc packet-len {p} { expr {[string length $p]/2} }
27 proc packet-csum-ip {packet} {
30 while {[regexp {^([0-9a-f][0-9a-f])(.*)$} $packet dummy this packet]} {
33 return [expr {$cs & 0xffff}]
37 namespace eval Random-Bytes {
38 namespace export random-bytes random-bytes-init
40 proc random-bytes-init {seed} {
43 catch { set h $fh; unset fh; close $h }
45 set fh [open |[list openssl bf-ofb < /dev/zero -e -k " $seed"] r]
46 fconfigure $fh -translation binary
48 proc random-bytes {n} {
51 if {[string length $x] != $n} {
52 set h $fh; unset fh; close $h
53 error "openssl bf-ofb exited unexpectedly"
56 if {[string length $y] != $n*2} { error "binary format failed $n $y" }
61 namespace import Random-Bytes::*
63 proc choice-int {min max} {
64 set rv 0x[random-bytes 3]
66 int( double($rv) / double(0x1000000) * double($max+1-$min) )
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}]
77 proc choice-mult {args} {
78 if {!([llength $args] % 2)} { error "choice-mult must have default" }
79 set x 0x[random-bytes 3]
80 set x [expr { double($x) / double(0x1000000) }]
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 }
92 upvar #0 getlog_log log
97 proc config {cv def} {
99 if {[info exists v]} { return $v }
104 proc define {enum val name argnames body} {
105 upvar #0 enum/val2name/$enum v2n
106 upvar #0 enum/name2val/$enum n2v
109 proc enum/val/$enum/$val $argnames $body
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 # half the time random
124 uplevel 1 [list $procname] $mtu $args
129 proc get-for {scope} {
134 proc get {variable kind args} {
135 upvar 1 get/scope scope
136 upvar 1 $variable var
137 set var [eval [list get/$kind $scope $variable] $args]
140 proc get-config/number {val min max} { return $val }
141 proc get-config/v4addr {val} {
142 if {![regexp {^(\d+)\.(\d+)\.(\d+)\.(\d+)$} $val dummy a b c d]} {
143 error "bad v4addr ?$val?"
145 return [format 0x%02x%02x%02x%02x $a $b $c $d]
148 proc get-config {variable def kind args} {
149 # args currently ignored
150 upvar 1 get/scope scope
151 upvar 1 $variable var
152 set val [config $scope-$variable $def]
153 set var [eval [list get-config/$kind $val] $args]
156 proc get-enum-got {s v rv} {
157 upvar #0 enum/val2name/$s-$v v2n
158 if {[info exists v2n($rv)]} {
159 getlog "$v=$v2n($rv)\[$rv]"
166 proc get/enum-rand {s v min max} {
167 set rv [choice-int $min $max]
168 return [get-enum-got $s $v $rv]
171 proc get/enum-def {s v} {
172 upvar #0 enum/val2name/$s-$v v2n
173 set rv [choice-int 1 [array size v2n]]
174 set rv [lindex [array names v2n] [expr {$rv-1}]]
175 return [get-enum-got $s $v $rv]
178 proc get/enum {s v min max prand} {
180 get any choice $prand
182 return [get/enum-rand $s $v $min $max]
184 return [get/enum-def $s $v]
188 proc get/number {s v min max} {
189 set rv [choice-int $min $max]
194 proc get/flag {s v defprob} {
195 set rv [choice-prob $s-$v $defprob]
196 if {$rv} { getlog "$v" } else { getlog "!$v" }
200 proc get/choice {s v defprob} {
201 set rv [choice-prob $s-$v $defprob]
202 if {$rv} { getlog "($v)" } else { getlog "(!$v)" }
206 proc get/rand {s v minlen maxlen} {
208 get l number $minlen $maxlen
209 return [random-bytes $l]
212 proc get/ip-timestamp {s v} {
213 set rv [expr {[clock seconds] | 0x80000000}]
214 getlog "$v=[format %x $rv]"
218 proc get/v4addr {s v} {
222 for {set i 0} {$i<4} {incr i} {
223 set b [random-bytes 1]
225 append p $d [format %d 0x$b]
232 proc get/choice-mult {s v args} {
233 set rv [eval choice-mult $args]
238 proc get/string {s v minlen maxlen first rest} {
241 for {set l [choice-int $minlen $maxlen]} {$l>0} {incr l -1} {
242 set cn [choice-int 0 [expr {[string length $now]-1}]]
243 append o [string index $now $cn]
251 proc assemble {outvarname format} {
252 # format should look like those RFC diagrams.
253 # +-+-+ stuff and good formatting is mandatory.
254 # Tabs are forbidden.
256 # Field names are converted to lowercase; internal spaces
257 # are replaced with _. They are then assumed to be
258 # variable names in the caller's scope. The packet is
259 # assembled from those values (which must all be set)
260 # and stored in $varname in the caller's scope.
262 # Variables ?_whatever will be *set* with the location of the
263 # field in the string (in internal format); the corresponding
264 # `whatever' (with the ?_ stripped) will be read when assembling.
266 # Field name `0' means set the field to zero.
268 upvar 1 $outvarname out
271 debug 7 "ASSEMBLY $outvarname\n$format"
272 foreach l [split $format "\n"] {
274 if {[regexp -nocase {^ *\| +\| *$} $l]} {
275 if {![info exists wordbits]} {
276 error "vspace not in data @$lno\n?$l?"
279 } elseif {[regexp -nocase {^ *[|? a-z0-9]+$} $l]} {
280 if {[info exists words]} {
281 error "data without delimline @$lno\n?$l?"
285 } elseif {[regexp {^ *[-+]+ *$} $l]} {
288 while {[regexp {^([-= ]+)\+(.*)$} $l dummy before after]} {
289 set atpos([string length $before]) $wordbits
291 set l "$before=$after"
292 append newlineform "@[string length $before]:$wordbits "
295 append newlineform $wordbits
296 if {[info exists lineform]} {
297 if {"$newlineform" != "$lineform"} {
298 error "format change @$lno\n?$l?\n$wordformat != $oldwordformat"
300 if {![info exists words] || $words<0} {
301 error "consecutive delimlines @$lno\n?$l?"
303 append out [string repeat 00 [expr {$words*$wordbits/8}]]
305 while {[regexp -nocase \
306 {^([ =]*)\|( *[? a-z0-9]*[a-z0-9] *)\|(.*)$} \
307 $l dummy before midpart after]} {
308 debug 7 "RWORKG ?$l?"
309 set varname [string tolower [string trim $midpart]]
310 set varname [string map {{ } _} $varname]
311 set p1 [string length $before]
313 [string length $before] +
314 [string length $midpart] + 1
316 if {![info exists atpos($p1)] ||
317 ![info exists atpos($p2)]} {
318 error "vdelims not found @$lno $varname $p1 $p2 -- $lineform ?$cue?"
321 [string length $out]*4
326 $atpos($p2) - $atpos($p1) + ($words-1)*$wordbits
328 set location [list $bit1 $bitlen $outvarname-$varname]
329 if {[regexp {^\?_(.*)} $varname dummy realvarname]} {
330 debug 7 "LOCATING $varname $location"
331 upvar 1 $varname locvarname
332 set locvarname $location
333 set varname $realvarname
335 if {"$varname" == "0"} {
338 set value [uplevel 1 [list set $varname]]
340 assembly-overwrite out $location $value
342 append l [string repeat = [string length $midpart]]
345 debug 7 "REMAIN ?$l?"
347 if {$wordbits % 8 || $wordbits >32} {
348 error "bad wordbits $wordbits @$lno ?$l? $newlineform"
350 set lineform $newlineform
352 catch { unset words }
353 } elseif {[regexp {^ *$} $l]} {
355 error "huh? @$lno ?$l?"
358 debug 7 "ASSEMBLY\n$out\n"
362 proc assembly-overwrite {outvarname location value} {
363 upvar 1 $outvarname out
364 manyset $location bit1 bitlen diag
365 debug 8 "ASSEMBLY $diag == $value == 0x[format %x $value] (@$bit1+$bitlen)"
366 if {$bitlen < 32 && $value >= (1<<$bitlen)} {
367 error "$diag $value >= 2**$bitlen"
369 if {!($bit1 % 8) && !($bitlen % 8)} {
370 set char0no [expr {$bit1/4}]
371 set charlen [expr {$bitlen/4}]
372 set chareno [expr {$char0no + $charlen -1}]
373 set repl [format %0${charlen}x $value]
374 set out [string replace $out $char0no $chareno $repl]
376 while {$bitlen > 0} {
377 set byteno [expr {$bit1 / 8}]
378 set char0no [expr {$byteno*2}]
379 set char1no [expr {$char0no+1}]
380 set bytebit [expr {128>>($bit1 % 8)}]
381 set byte 0x[string range $out $char0no $char1no]
382 debug 7 "< $bit1+$bitlen $byteno $char0no $bytebit $byte $out"
384 ($value & (1<<($bitlen-1)))
386 : ($byte & ~$bytebit)
388 set out [string replace $out $char0no $char1no [format %02x $byte]]
389 debug 8 "> $bit1+$bitlen $byteno $char0no $bytebit 0x[format %02x $byte] $out"
397 proc gen_1_ip {mtu} {
399 upvar #0 ip_proto proto
400 upvar #0 ip_source source
401 upvar #0 ip_dest dest
404 get tos number 0x00 0xff
405 get id number 0x0000 0xffff
412 get frag number 0 0x1fff
414 get-config ttl 255 number 0 255
415 get proto enum 1 255 0.05
416 get-config source 127.0.0.1 v4addr
417 get-config dest 127.0.0.1 v4addr
418 # we don't do any IP options
420 set body [depending-on ip proto $mtu [expr {-4*$ihl}]]
421 set total_length [expr {$ihl + [packet-len $body]}]
422 set header_checksum 0
423 set flags [expr {$df*2 + $mf}]
425 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
426 |Version| IHL |TOS | Total Length |
427 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
429 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
430 | TTL | Proto | ? Header Checksum |
431 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
433 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
435 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
437 assembly-overwrite ip ${?_header_checksum} [packet-csum-ip $ip]
442 define ip-proto 1 icmp {mtu} {
445 get type enum 0 255 0.2
446 manyset [depending-on icmp type $mtu -4] body code
447 if {![string length $code]} { get code number 0 255 }
450 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
451 | Type | Code | ? Checksum |
452 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
455 assembly-overwrite icmp ${?_checksum} [packet-csum-ip $icmp]
459 proc define-icmp-type-vanilla {num name} {
460 define icmp-type $num $name {mbl} "icmp-vanilla \$mbl [list $name]"
462 proc icmp-vanilla {mbl typename} {
463 get-for icmp-$typename
464 get code enum 0 255 0.4
466 return [list $body $code]
469 define-icmp-type-vanilla 3 unreach
470 define icmp-unreach-code 0 net {} {}
471 define icmp-unreach-code 1 host {} {}
472 define icmp-unreach-code 2 proto {} {}
473 define icmp-unreach-code 3 port {} {}
474 define icmp-unreach-code 4 fragneeded {} {}
475 define icmp-unreach-code 5 sourceroutefail {} {}
477 define-icmp-type-vanilla 11 timeout
478 define icmp-timeout-code 0 intransit {} {}
479 define icmp-timeout-code 1 fragment {} {}
481 define-icmp-type-vanilla 12 parameters
482 define icmp-parameters-code 0 seepointer {} {}
484 define-icmp-type-vanilla 4 sourcequench
485 define icmp-sourcequench-code 0 quench {} {}
487 define icmp-type 5 redirect {mbl} {
488 get-for icmp-redirect
489 get code enum 0 255 0.4
492 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
494 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
496 get data rand 0 [expr {$mbl-4}]
498 return [list $body $code]
501 define icmp-redirect-code 0 net {} {}
502 define icmp-redirect-code 1 host {} {}
503 define icmp-redirect-code 2 net+tos {} {}
504 define icmp-redirect-code 3 host+tos {} {}
506 define icmp-type 8 ping {mbl} { icmp-echo $mbl }
507 define icmp-type 0 pong {mbl} { icmp-echo $mbl }
508 proc icmp-echo {mbl} {
510 get code enum 0 255 0.4
511 get id number 0 0xffff
512 get seq number 0 0xffff
514 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
516 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
518 get data rand 0 [expr {$mbl-8}]
520 return [list $body $code]
522 define icmp-echo-code 0 echo {} {}
524 define icmp-type 13 timestamp {mbl} { icmp-timestamp }
525 define icmp-type 14 timestampreply {mbl} { icmp-timestamp }
526 proc icmp-timestamp {} {
527 get-for icmp-timestamp
528 get code enum 0 255 0.4
529 get id number 0 0xffff
530 get seq number 0 0xffff
531 get originate ip-timestamp
532 get receive ip-timestamp
533 get transmit ip-timestamp
535 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
537 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
539 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
541 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
543 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
545 return [list $body $code]
547 define icmp-timestamp-code 0 timestamp {} {}
549 define icmp-type 15 inforequest {mbl} { icmp-inforeq }
550 define icmp-type 16 inforeply {mbl} { icmp-inforeq }
551 proc icmp-inforeq {} {
553 get code enum 0 255 0.4
554 get id number 0 0xffff
555 get seq number 0 0xffff
557 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
559 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
561 return [list $body $code]
563 define icmp-inforeq-code 0 timestamp {} {}
565 # MAYADD ICMP traceroute RFC1393
566 # MAYADD ICMP router discovery RFC1256
568 proc port_pair_data {scope mtu mtuadjust} {
571 get style choice-mult \
577 if {"$style" != "random"} {
581 if {"$style" != "servers"} {
582 get port enum-rand 0 0xffff
585 switch -exact $style {
586 random { set source_port $rand_port; set dest_port $rand_port }
587 request { set source_port $rand_port; set dest_port $def_port }
588 reply { set source_port $def_port; set dest_port $rand_port }
589 servers { set source_port $def_port; set dest_port $def_port }
591 if {"$style" != "random"} {
593 set data [depending-on $scope port $mtu $mtuadjust $style]
595 get data rand 0 [expr {$mtu + $mtuadjust}]
597 return [list $source_port $dest_port $data]
600 define ip-proto 17 udp {mtu} {
602 get checksum choice-mult \
606 manyset [port_pair_data udp $mtu 8] source_port dest_port data
607 set length [packet-len $data]
610 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
611 | Source Port | Dest Port |
612 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
613 | Length | ? Checksum |
614 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
617 if {"$checksum" != "none"} {
618 set csum [packet-csum-ip $udp]
619 if {!$csum} { set csum 0xffff }
620 if {"$checksum" == "bad"} {
621 get error number 1 0xffff
622 set csum [expr {$csum ^ $error}]
627 assembly-overwrite udp ${?_checksum} $csum
630 define udp-port 50 remailck {mtu style} {
632 if {"$style" == "request"} {
633 get what choice-mult \
640 get what choice-mult \
647 switch -exact $what {
650 get user string 1 8 \
651 abcdefghijklmnopqrustuvwxyz \
652 abcdefghijklmnopqrustuvwxyz-0123456789_
656 get user rand 0 [expr {$mtu - 4}]
660 set user [depending-on auth {$mtu - 4}]
663 get auth number 0 0xffff
668 get mail choice-mult \
674 switch -exact $mail {
688 get modified number 1 600
689 get read number 1 600
691 default { error "mail? $mail" }
694 default { error "what? $what" }
699 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
701 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
707 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
709 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
711 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
713 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
716 default { error "what?? $what" }
722 global getlog_log errorInfo
725 set packet [gen_1_ip 576]
726 puts stdout "[format %06d $count] $getlog_log\n $packet"
728 puts stderr "\nERROR\n$count\n\n$emsg\n\n$errorInfo\n\n"
729 puts stdout "[format %06d $count] error"
733 if {![llength $argv]} {
734 for {set count 1} {$count < 100} {incr count} { emit $count }
735 } elseif {"$argv" == "--infinite"} {
737 while 1 { emit $count; incr count }
739 foreach count $argv { emit $count }