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 set gen_counter $use_gen_counter
27 proc packet-len {p} { expr {[string length $p]/2} }
29 proc packet-csum-ip {packet} {
32 while {[regexp {^([0-9a-f][0-9a-f])(.*)$} $packet dummy this packet]} {
35 return [expr {$cs & 0xffff}]
39 proc random-bytes {n} {
40 global gen_counter rand_counter rand_buf
43 while {[set w [expr {$n - [string length $o]}]] > 0} {
44 if {![string length $rand_buf]} {
45 set md5 [exec md5sum << "$gen_counter $rand_counter"]
46 regexp {^[0-9a-f]{32}} $md5 rand_buf
47 if {![string length $rand_buf]} { error "?$md5?" }
50 append o [string range $rand_buf 0 [expr {$w-1}]]
51 set rand_buf [string range $rand_buf $w end]
56 proc choice-int {min max} {
57 set rv 0x[random-bytes 3]
59 int( double($rv) / double(0xffffff) * double($max+1-$min) )
64 proc choice-prob {cv def} {
65 set prob [config $cv $def]
66 set rv 0x[random-bytes 3]
67 return [expr {$rv < double($prob)*0x1000000}]
72 upvar #0 getlog_log log
77 proc config {cv def} {
79 if {[info exists v]} { return $v }
84 proc define {enum val name argnames body} {
85 upvar #0 enum/val2name/$enum v2n
86 upvar #0 enum/name2val/$enum n2v
89 proc enum/val/$enum/$val $argnames $body
92 proc depending-on {scope enum_and_var mtu mtuadjust args} {
93 upvar 1 $enum_and_var val
94 set mtu [expr {$mtu + $mtuadjust}]
95 set procname enum/val/$scope-$enum_and_var/[format %d $val]
96 if {[choice-prob $enum_and_var-unstruct 0.1] ||
97 [catch { info body $procname }]} {
98 # half the time random
101 get data randupto $mtu
104 uplevel 1 [list $procname] $mtu $args
109 proc get-for {scope} {
114 proc get {variable kind args} {
115 upvar 1 get/scope scope
116 upvar 1 $variable var
117 set var [eval [list get/$kind $scope $variable] $args]
120 proc get-config/number {val min max} { return $val }
121 proc get-config/v4addr {val} {
122 if {![regexp {^(\d+)\.(\d+)\.(\d+)\.(\d+)$} $val dummy a b c d]} {
123 error "bad v4addr ?$val?"
125 return [format 0x%02x%02x%02x%02x $a $b $c $d]
128 proc get-config {variable def kind args} {
129 # args currently ignored
130 upvar 1 get/scope scope
131 upvar 1 $variable var
132 set val [config $scope-$variable $def]
133 set var [eval [list get-config/$kind $val] $args]
136 proc get/enum {s v min max} {
137 if {[choice-prob $s-$v-unknown 0.5]} {
138 set rv [choice-int $min $max]
140 upvar #0 enum/val2name/$s-$v v2n
141 set rv [choice-int 1 [array size v2n]]
142 set rv [lindex [array names v2n] [expr {$rv-1}]]
144 if {[info exists v2n($rv)]} {
145 getlog "$v=$v2n($rv)\[$rv]"
152 proc get/number {s v min max} {
153 set rv [choice-int $min $max]
158 proc get/flag {s v defprob} {
159 set rv [choice-prob $s-$v $defprob]
160 if {$rv} { getlog "$v" } else { getlog "!$v" }
164 proc get/choice {s v defprob} {
165 set rv [choice-prob $s-$v $defprob]
166 if {$rv} { getlog "($v)" } else { getlog "(!$v)" }
170 proc get/randupto {s v maxlen} {
172 get l number 0 $maxlen
173 return [random-bytes $l]
176 proc get/ip-timestamp {s v} {
177 set rv [expr {[clock seconds] | 0x80000000}]
178 getlog "$v=[format %x $rv]"
182 proc get/v4addr {s v} {
186 for {set i 0} {$i<4} {incr i} {
187 set b [random-bytes 1]
189 append p $d [format %d $b]
197 proc assemble {outvarname format} {
198 # format should look like those RFC diagrams.
199 # +-+-+ stuff and good formatting is mandatory.
200 # Tabs are forbidden.
202 # Field names are converted to lowercase; internal spaces
203 # are replaced with _. They are then assumed to be
204 # variable names in the caller's scope. The packet is
205 # assembled from those values (which must all be set)
206 # and stored in $varname in the caller's scope.
208 # Variables ?_whatever will be *set* with the location of the
209 # field in the string (in internal format); the corresponding
210 # `whatever' (with the ?_ stripped) will be read when assembling.
212 # Field name `0' means set the field to zero.
214 upvar 1 $outvarname out
217 debug 7 "ASSEMBLY $outvarname\n$format"
218 foreach l [split $format "\n"] {
220 if {[regexp -nocase {^ *\| +\| *$} $l]} {
221 if {![info exists wordbits]} {
222 error "vspace not in data @$lno\n?$l?"
225 } elseif {[regexp -nocase {^ *[|? a-z0-9]+$} $l]} {
226 if {[info exists words]} {
227 error "data without delimline @$lno\n?$l?"
231 } elseif {[regexp {^ *[-+]+ *$} $l]} {
234 while {[regexp {^([-= ]+)\+(.*)$} $l dummy before after]} {
235 set atpos([string length $before]) $wordbits
237 set l "$before=$after"
238 append newlineform "@[string length $before]:$wordbits "
241 append newlineform $wordbits
242 if {[info exists lineform]} {
243 if {"$newlineform" != "$lineform"} {
244 error "format change @$lno\n?$l?\n$wordformat != $oldwordformat"
246 if {![info exists words] || $words<0} {
247 error "consecutive delimlines @$lno\n?$l?"
249 append out [string repeat 00 [expr {$words*$wordbits/8}]]
251 while {[regexp -nocase \
252 {^([ =]*)\|( *[? a-z0-9]*[a-z0-9] *)\|(.*)$} \
253 $l dummy before midpart after]} {
254 debug 7 "RWORKG ?$l?"
255 set varname [string tolower [string trim $midpart]]
256 set varname [string map {{ } _} $varname]
257 set p1 [string length $before]
259 [string length $before] +
260 [string length $midpart] + 1
262 if {![info exists atpos($p1)] ||
263 ![info exists atpos($p2)]} {
264 error "vdelims not found @$lno $varname $p1 $p2 -- $lineform ?$cue?"
267 [string length $out]*4
272 $atpos($p2) - $atpos($p1) + ($words-1)*$wordbits
274 set location [list $bit1 $bitlen $outvarname-$varname]
275 if {[regexp {^\?_(.*)} $varname dummy realvarname]} {
276 debug 7 "LOCATING $varname $location"
277 upvar 1 $varname locvarname
278 set locvarname $location
279 set varname $realvarname
281 if {"$varname" == "0"} {
284 set value [uplevel 1 [list set $varname]]
286 assembly-overwrite out $location $value
288 append l [string repeat = [string length $midpart]]
291 debug 7 "REMAIN ?$l?"
293 if {$wordbits % 8 || $wordbits >32} {
294 error "bad wordbits $wordbits @$lno ?$l? $newlineform"
296 set lineform $newlineform
298 catch { unset words }
299 } elseif {[regexp {^ *$} $l]} {
301 error "huh? @$lno ?$l?"
304 debug 7 "ASSEMBLY\n$out\n"
308 proc assembly-overwrite {outvarname location value} {
309 upvar 1 $outvarname out
310 manyset $location bit1 bitlen diag
311 debug 8 "ASSEMBLY $diag == $value == 0x[format %x $value] (@$bit1+$bitlen)"
312 if {$bitlen < 32 && $value >= (1<<$bitlen)} {
313 error "$diag $value >= 2**$bitlen"
315 while {$bitlen > 0} {
316 set byteno [expr {$bit1 / 8}]
317 set char0no [expr {$byteno*2}]
318 set char1no [expr {$char0no+1}]
319 set bytebit [expr {128>>($bit1 % 8)}]
320 set byte 0x[string range $out $char0no $char1no]
321 debug 7 "< $bit1+$bitlen $byteno $char0no $bytebit $byte $out"
323 ($value & (1<<($bitlen-1)))
325 : ($byte & ~$bytebit)
327 set out [string replace $out $char0no $char1no [format %02x $byte]]
328 debug 8 "> $bit1+$bitlen $byteno $char0no $bytebit 0x[format %02x $byte] $out"
335 proc gen_1_ip {mtu} {
337 upvar #0 ip_proto proto
338 upvar #0 ip_source source
339 upvar #0 ip_dest dest
342 get tos number 0x00 0xff
343 get id number 0x0000 0xffff
350 get frag number 0 0x1fff
352 get-config ttl 255 number 0 255
354 get-config source 127.0.0.1 v4addr
355 get-config dest 127.0.0.1 v4addr
356 # we don't do any IP options
358 set body [depending-on ip proto $mtu [expr {-4*$ihl}]]
359 set total_length [expr {$ihl + [packet-len $body]}]
360 set header_checksum 0
361 set flags [expr {$df*2 + $mf}]
363 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
364 |Version| IHL |TOS | Total Length |
365 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
367 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
368 | TTL | Proto | ? Header Checksum |
369 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
371 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
373 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
375 assembly-overwrite ip ${?_header_checksum} [packet-csum-ip $ip]
380 define ip-proto 1 icmp {mtu} {
384 manyset [depending-on icmp type $mtu -4] body code
385 if {![string length $code]} { get code number 0 255 }
388 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
389 | Type | Code | ? Checksum |
390 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
393 assembly-overwrite icmp ${?_checksum} [packet-csum-ip $icmp]
397 proc define-icmp-type-vanilla {num name} {
398 define icmp-type $num $name {mbl} "icmp-vanilla \$mbl [list $name]"
400 proc icmp-vanilla {mbl typename} {
401 get-for icmp-$typename
403 get body randupto $mbl
404 return [list $body $code]
407 define-icmp-type-vanilla 3 unreach
408 define icmp-unreach-code 0 net {} {}
409 define icmp-unreach-code 1 host {} {}
410 define icmp-unreach-code 2 proto {} {}
411 define icmp-unreach-code 3 port {} {}
412 define icmp-unreach-code 4 fragneeded {} {}
413 define icmp-unreach-code 5 sourceroutefail {} {}
415 define-icmp-type-vanilla 11 timeout
416 define icmp-timeout-code 0 intransit {} {}
417 define icmp-timeout-code 1 fragment {} {}
419 define-icmp-type-vanilla 12 parameters
420 define icmp-parameters-code 0 seepointer {} {}
422 define-icmp-type-vanilla 4 sourcequench
423 define icmp-sourcequench-code 0 quench {} {}
425 define icmp-type 5 redirect {mbl} {
426 get-for icmp-redirect
430 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
432 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
434 get data randupto [expr {$mbl-4}]
436 return [list $body $code]
439 define icmp-redirect-code 0 net {} {}
440 define icmp-redirect-code 1 host {} {}
441 define icmp-redirect-code 2 net+tos {} {}
442 define icmp-redirect-code 3 host+tos {} {}
444 define icmp-type 8 ping {mbl} { icmp-echo $mbl }
445 define icmp-type 0 pong {mbl} { icmp-echo $mbl }
446 proc icmp-echo {mbl} {
449 get id number 0 0xffff
450 get seq number 0 0xffff
452 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
454 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
456 get data randupto [expr {$mbl-8}]
458 return [list $body $code]
460 define icmp-echo-code 0 echo {} {}
462 define icmp-type 13 timestamp {mbl} { icmp-timestamp }
463 define icmp-type 14 timestampreply {mbl} { icmp-timestamp }
464 proc icmp-timestamp {} {
465 get-for icmp-timestamp
467 get id number 0 0xffff
468 get seq number 0 0xffff
469 get originate ip-timestamp
470 get receive ip-timestamp
471 get transmit ip-timestamp
473 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
475 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
477 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
479 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
481 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
483 return [list $body $code]
485 define icmp-timestamp-code 0 timestamp {} {}
487 define icmp-type 15 inforequest {mbl} { icmp-inforeq }
488 define icmp-type 16 inforeply {mbl} { icmp-inforeq }
489 proc icmp-inforeq {} {
492 get id number 0 0xffff
493 get seq number 0 0xffff
495 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
497 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
499 return [list $body $code]
501 define icmp-inforeq-code 0 timestamp {} {}
503 # MAYADD ICMP traceroute RFC1393
504 # MAYADD ICMP router discovery RFC1256
506 define ip-proto 17 udp {mtu} {
509 get source_port number 0 0xffff
510 get data randupto [expr {$mtu-8}]
511 set length [packet-len $data]
514 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
515 | Source Port | Dest Port |
516 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
517 | Length | ? Checksum |
518 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
521 get checksum choice 0.75
523 set csum [packet-csum-ip $udp]
524 if {!$csum} { set csum 0xffff }
528 assembly-overwrite udp ${?_checksum} $csum
532 global getlog_log errorInfo
535 set packet [gen_1_ip 576]
536 puts stdout "[format %06d $count] $getlog_log\n $packet"
538 puts stderr "\nERROR\n$count\n\n$emsg\n\n$errorInfo\n\n"
539 puts stdout "[format %06d $count] error"
543 if {![llength $argv]} {
544 for {set count 1} {$count < 100} {incr count} { emit $count }
545 } elseif {"$argv" == "--infinite"} {
547 while 1 { emit $count; incr count }
549 foreach count $argv { emit $count }