All Classes Namespaces Files Functions Pages
framev4v6.tcl
Go to the documentation of this file.
1 ##
2 # @brief Basic namespace for all Excentis Tcl extensions
3 #
4 namespace eval excentis {
5 
6 ##
7 # @brief Namespace for Excentis Tcl extensions for basic functions.
8 #
9 # This includes functionality such as:
10 # - Network frame generation
11 # - Address parsing and conversion
12 # - ...
13 #
14 namespace eval basic {
15 
16 ## \file
17 #
18 # @brief Utility module that eases the <b>construction of</b> all types of valid <b>network packets</b> as bytelists, which is the format used by the ByteBlower.
19 #
20 # The methods in this module typically take various header field values (such as source and destination IP) as arguments. Using these values, they can generate
21 # a valid ethernet frame. This created frame is then returned in a hexadecimal bytelist format ('0x01 0x02 0xFF...').
22 #
23 # @copyright Excentis nv - www.excentis.com
24 #
25 
26 proc Frame.Udpv6.Set { DMAC SMAC DIP SIP DP SP dataOrLength {params DEFAULT} } {
27  # Sets content of the frame to a UDP frame.
28  #
29  # @param DMAC Destination MAC address
30  # @param SMAC Source MAC address
31  # @param DIP Destination IP6 address (colon notation)
32  # @param SIP Source IPv6 address (colon notation)
33  # @param DP Destination UDP port (decimal)
34  # @param SP Source UDP port (decimal)
35  # @param dataOrLength data of the UDP frame.
36  # Can also specify a length if <dataOrLength> is a list
37  # with '-length' as first element and an integer
38  # specifying the datalength as a second element
39  #
40  # @param params A pair list containing alternating a parameter and a value
41  # Parameters are expected to be :
42  # -IP:
43  # -version : 6 (4 bits)
44  # -trafClass : 0x00 (8 bits)
45  # -flowLabel : 0x0 0x00 0x00 (20 bits)
46  # -hopLimit : 64 (8 bits)
47  # -jumbo : 0 or 1 (indicates the use of the Jumbo Payload option in the Hop-by-hop
48  # extension header. 1 -> jumbo payload is used)
49  # -Ethernet
50  # -MFT : EtherII
51  # SNAP
52  # 802.3
53  # -Length : Auto : If no value is given then the frame will nicely encapsulate the <data>
54  # If total length exceeds maximum MTU (1518) then an error will be generated
55  # : x : Ethernet data Length will be adjusted to <x>.
56  # If data exceeds <x> boundary then an error will be generated.
57  # If data is smaller than <x> then data will be stuffed with 0x00
58  #
59  # @apiexample
60  # @apisnippet{Frame.Udpv6.Set 00ff12000001 00ff12000002 2001:db8:1:81::1 2001:db8:1:81::2 10000 20000 {0xAA 0xAA 0xAA 0xAA 0xAA}}
61  # @apisnippet{Frame.Udpv6.Set 00ff12000001 00ff12000002 2001:db8:1:81::1 2001:db8:1:81::2 10000 20000 {-length 92}}
62  # @endapiexample
63  #
64  ##
65 
66  set Ethernet DEFAULT
67  set IP DEFAULT
68 
69  if { ![ string equal -nocase $params "DEFAULT" ]} {
70  foreach {item value} $params {
71  switch -- [ string tolower $item ] {
72  -ip { set IP $value }
73  -ethernet { set Ethernet $value }
74  default { error "Frame.Udpv6.Set: Invalid param '${item}'" }
75  } ;# switch close
76  }
77  }
78 
79  # Check and convert the data
80  set data [ x.Frame.DataOrLength.Parse $dataOrLength ]
81  set dataLength [ llength $data ]
82 
83  # PseudoHeader fields in PH and header fields in H
84  set length [ expr ( 8 + $dataLength ) ]
85  set PH1 [ IPv6.To.Hex $SIP ]
86  set PH2 [ IPv6.To.Hex $DIP ]
87  set PH3 [ concat [ Short.To.Hex $length ] 0x00 0x00 0x00 [ Byte.To.Hex 17 ] ]
88  set PH [ concat $PH1 $PH2 $PH3 ]
89  set H1 [ concat [ Short.To.Hex $SP ] [ Short.To.Hex $DP ] ]
90  set H2 [ concat [ Short.To.Hex $length ] 0x00 0x00 ]
91  set PHH [ concat $PH $H1 $H2 ]
92  set H [ concat $H1 $H2 ]
93  set myContent [ concat $PHH $data ]
94  if { [ expr ( [ llength $myContent ] % 2 ) ]} {
95  lappend myContent 0x00
96  }
97  set checkSum [ CheckSum.16 $myContent ]
98  set myContent [ concat $H $data ]
99  set myContent [ eval lreplace {$myContent} 6 7 $checkSum ]
100  # myContent now contains the UDP message
101 
102  set myContent [ Frame.Ipv6.Set $DMAC $SMAC $DIP $SIP 17 $myContent [ list -IP $IP -Ethernet $Ethernet ] ]
103 
104  return $myContent
105 
106 }
107 
108 #**************************************************************************************************
109 
110 proc Frame.Udp.Set {DMAC SMAC DIP SIP DP SP dataOrLength { params DEFAULT }} {
111  # Sets content of the frame to a UDP frame.
112  #
113  # @param DMAC Destination MAC address
114  # @param SMAC Source MAC address
115  # @param DIP Destination IPv4 address (xxx.xxx.xxx.xxx formatted)
116  # @param SIP Source IPv4 address (xxx.xxx.xxx.xxx formatted)
117  # @param DP Destination UDP port (decimal)
118  # @param SP Source UDP port (decimal)
119  # @param dataOrLength Data of the IP frame.
120  # Can also specify a length if <dataOrLength> is a list
121  # with '-length' as first element and an integer
122  # specifying the datalength as a second element
123  #
124  # @param params A pair list containing alternating a parameter and a value
125  # Parameters are expected to be :
126  # -IP:
127  # -Version : 4 : IP version
128  # -IHL : 5
129  # -IPIdentif : 0x00 0x00
130  # -TTL : 20
131  # -FF : 0x00 0x00
132  # -ToS : 0x00
133  # -Ethernet
134  # -MFT : EtherII
135  # SNAP
136  # 802.3
137  # -Length : Auto : If no value is given then the frame will nicely encapsulate the <data>
138  # If total length exceeds maximum MTU (1518) then an error will be generated
139  # : x : Ethernet data Length will be adjusted to <x>.
140  # If data exceeds <x> boundary then an error will be generated.
141  # If data is smaller than <x> then data will be stuffed with 0x00
142  #
143  # @example Frame.Udp.Set 00ff12000001 00ff12000002 10.11.0.1 10.11.0.2 10000 20000 {0xAA 0xAA 0xAA 0xAA 0xAA}
144  # @example Frame.Udp.Set 00ff12000001 00ff12000002 10.11.0.1 10.11.0.2 10000 20000 {-length 64}
145  #
146  ##
147 
148  set Ethernet DEFAULT
149  set IP DEFAULT
150 
151  if { ![ string equal -nocase $params "DEFAULT" ]} {
152  foreach {item value} $params {
153  switch -- [ string tolower $item ] {
154  -ip { set IP $value }
155  -ethernet { set Ethernet $value }
156  default { error "Frame.Udp.Set: Invalid param '${item}'" }
157  } ;#switch close
158  }
159  }
160 
161  # Check and convert the data
162  set data [ x.Frame.DataOrLength.Parse $dataOrLength ]
163  set dataLength [ llength $data ]
164  if { $dataLength < 4} {
165  error "Frame.Udp.Set: data length must be > 4"
166  }
167 
168  # pseudoheader fields in PH and header fields in H
169  set length [ expr ( 8 + $dataLength ) ]
170  set PH1 [ IP.To.Hex $SIP ]
171  set PH2 [ IP.To.Hex $DIP ]
172  set PH3 [ concat 0x00 0x11 [ Short.To.Hex $length ] ]
173  set PH [ concat $PH1 $PH2 $PH3 ]
174  set H1 [ concat [ Short.To.Hex $SP ] [ Short.To.Hex $DP ] ]
175  set H2 [ concat [ Short.To.Hex $length ] 0x00 0x00 ]
176  set PHH [ concat $PH $H1 $H2 ]
177  set H [ concat $H1 $H2 ]
178  set myContent [ concat $PHH $data ]
179  if { [ expr ( $length % 2 ) ]} {
180  lappend myContent 0x00
181  }
182  set checkSum [ CheckSum.16 $myContent ]
183  set myContent [ concat $H $data ]
184  set myContent [ eval lreplace {$myContent} 6 7 $checkSum ]
185  # myContent now contains the UDP message
186 
187  set myContent [ Frame.Ipv4.Set $DMAC $SMAC $DIP $SIP 17 $myContent [ list -IP $IP -Ethernet $Ethernet ] ]
188 
189  return $myContent
190 
191 }
192 
193 #**************************************************************************************************
194 
195 proc Frame.Tcp.Set { DMAC SMAC DIP SIP DP SP dataOrLength { params DEFAULT } } {
196  # Sets content of the frame to a TCP frame.
197  #
198  # @param DMAC Destination MAC address
199  # @param SMAC Source MAC address
200  # @param DIP Destination IPv4 address (xxx.xxx.xxx.xxx formatted)
201  # @param SIP Source IPv4 address (xxx.xxx.xxx.xxx formatted)
202  # @param DP Destination TCP port (decimal)
203  # @param SP Source TCP port (decimal)
204  # @param dataOrLength Data of the TCP frame.
205  # Can also specify a length if <dataOrLength> is a list
206  # with '-length' as first element and an integer
207  # specifying the datalength as a second element
208  #
209  # @param params A pair list containing alternating a parameter and a value
210  # Parameters are expected to be :
211  # -TCP
212  # -TCPSeq : 0x10 0x00 0x01 0xa3
213  # -TCPAck : 0x10 0x00 0x01 0xa0
214  # -TCPWindow : 16384
215  # -TCPByte1314 : 0x00 0x00
216  # -TCPUrgent : 0x00 0x00
217  # -IP:
218  # -Version : 4 : IP version
219  # -IHL : 5
220  # -IPIdentif : 0x00 0x00
221  # -TTL : 20
222  # -FF : 0x00 0x00
223  # -ToS : 0x00
224  # -Ethernet
225  # -MFT : EtherII
226  # SNAP
227  # 802.3
228  # -Length : Auto : If no value is given then the frame will nicely encapsulate the <data>
229  # If total length exceeds maximum MTU (1518) then an error will be generated
230  # : x : Ethernet data Length will be adjusted to <x>.
231  # If data exceeds <x> boundary then an error will be generated.
232  # If data is smaller than <x> then data will be stuffed with 0x00
233  #
234  # @example Frame.Tcp.Set 00ff02000002 00ff02000001 10.11.0.2 10.11.0.1 1235 1234 {0x00 0x00} {-TCP {-TCPAck {0x10 0x00 0x01 0xa3}}}
235  ##
236 
237  set Ethernet DEFAULT
238  set IP DEFAULT
239  set TCP DEFAULT
240 
241  set TCPSeq [ list 0x10 0x00 0x01 0xa3 ]
242  set TCPAck [ list 0x10 0x00 0x01 0xa0 ]
243  set TCPWindow 16384
244  set TCPByte1314 [ list 0x50 0x00 ]
245  set TCPUrgent [ list 0x00 0x00 ]
246 
247  if { ![ string equal -nocase $params "DEFAULT" ]} {
248  foreach {item value} $params {
249  switch -- [ string tolower $item ] {
250  -tcp { set TCP $value }
251  -ip { set IP $value }
252  -ethernet { set Ethernet $value }
253  default { error "Frame.Tcp.Set: Invalid param '${item}'" }
254  } ;# switch close
255  }
256  }
257 
258  if { ![ string equal -nocase $TCP "DEFAULT" ]} {
259  foreach {item value} $TCP {
260  switch -- [ string tolower $item ] {
261  -tcpseq { set TCPSeq $value }
262  -tcpack { set TCPAck $value }
263  -tcpwindow { set TCPWindow $value }
264  -tcpbyte1314 { set TCPByte1314 $value }
265  -tcpurgent { set TCPUrgent $value }
266  default { error "Frame.Tcp.Set: Invalid TCP option '${item}'" }
267  } ;# switch close
268  }
269  }
270 
271  # Check the TcpSeq
272  if { [ llength $TCPSeq ] != 4 || ![ ByteList.Check $TCPSeq ]} {
273  error "Frame.Tcp.Set: Invalid TCPSeq '$TCPSeq', expecting 4 bytes"
274  }
275 
276  # Check the TCPAck
277  if { [ llength $TCPAck ] != 4 || ![ ByteList.Check $TCPAck ]} {
278  error "Frame.Tcp.Set: Invalid TCPAck '$TCPAck', expecting 4 bytes"
279  }
280 
281  # Check the TCPWindow
282  if { [ string equal $TCPWindow "" ] || ![ string is integer $TCPWindow ] || $TCPWindow < 0 || $TCPWindow > 0xFFFF} {
283  error "Frame.Tcp.Set: Invalid TCPWindow value '$TCPWindow', MUST be <= 0xFFFF"
284  }
285 
286  # Check the TCPByte1314
287  if { [ llength $TCPByte1314 ] != 2 || ![ ByteList.Check $TCPByte1314 ]} {
288  error "Frame.Tcp.Set: Invalid TCPByte1314 '$TCPByte1314', expecting 2 bytes"
289  }
290 
291  # Check the TCPUrgent
292  if { [ llength $TCPUrgent ] != 2 || ![ ByteList.Check $TCPUrgent ]} {
293  error "Frame.Tcp.Set: Invalid TCPUrgent '$TCPUrgent', expecting 2 bytes"
294  }
295 
296  # Check and convert the data
297  set data [ x.Frame.DataOrLength.Parse $dataOrLength ]
298 
299  set length [ expr ( 20 + [ llength $data ] ) ]
300 
301  set PH1 [ IP.To.Hex $SIP ]
302  set PH2 [ IP.To.Hex $DIP ]
303  set PH3 [ concat 0x00 0x06 [ Short.To.Hex $length ] ]
304  set PH [ concat $PH1 $PH2 $PH3 ]
305 
306  set H1 [ concat [ Short.To.Hex $SP ] [ Short.To.Hex $DP ] ]
307  set H2 $TCPSeq
308  set H3 $TCPAck
309  set H4 [ concat $TCPByte1314 [ Short.To.Hex $TCPWindow ] ]
310  set H5 [ concat 0x00 0x00 $TCPUrgent ]
311 
312  set PHH [ concat $PH $H1 $H2 $H3 $H4 $H5 ]
313  set H [ concat $H1 $H2 $H3 $H4 $H5 ]
314  set myContent [ concat $PHH $data ]
315  if { [ expr ( $length % 2 ) ]} {
316  lappend myContent 0x00
317  }
318  set checkSum [ CheckSum.16 $myContent ]
319  set myContent [ concat $H $data ]
320  set myContent [ eval lreplace {$myContent} 16 17 $checkSum ]
321 
322  set myContent [ Frame.Ipv4.Set $DMAC $SMAC $DIP $SIP 6 $myContent [ list -IP $IP -Ethernet $Ethernet ] ]
323 
324  return $myContent
325 
326 }
327 
328 #**************************************************************************************************
329 
330 proc Frame.Tcpv6.Set { DMAC SMAC DIP SIP DP SP dataOrLength { params DEFAULT } } {
331  # Sets content of the frame to a TCP frame.
332  #
333  # @param DMAC Destination MAC address
334  # @param SMAC Source MAC address
335  # @param DIP Destination IPv6 address (colon notation)
336  # @param SIP Source IPv6 address (colon notation)
337  # @param DP Destination TCP port (decimal)
338  # @param SP Source TCP port (decimal)
339  # @param dataOrLength Data of the TCP frame.
340  # Can also specify a length if <dataOrLength> is a list
341  # with '-length' as first element and an integer
342  # specifying the datalength as a second element
343  #
344  # @param params A pair list containing alternating a parameter and a value
345  # Parameters are expected to be :
346  # -TCP
347  # -TCPSeq : 0x10 0x00 0x01 0xa3
348  # -TCPAck : 0x10 0x00 0x01 0xa0
349  # -TCPWindow : 16384
350  # -TCPByte1314 : 0x00 0x00
351  # -TCPUrgent : 0x00 0x00
352  # -IP:
353  # -version : 6 (4 bits)
354  # -trafClass : 0x00 (8 bits)
355  # -flowLabel : 0x0 0x00 0x00 (20 bits)
356  # -hopLimit : 64 (8 bits)
357  # -jumbo : 0 or 1 (indicates the use of the Jumbo Payload option in the Hop-by-hop
358  # extension header. 1 -> jumbo payload is used)
359  # -Ethernet
360  # -MFT : EtherII
361  # SNAP
362  # 802.3
363  # -Length : Auto : If no value is given then the frame will nicely encapsulate the <data>
364  # If total length exceeds maximum MTU (1518) then an error will be generated
365  # : x : Ethernet data Length will be adjusted to <x>.
366  # If data exceeds <x> boundary then an error will be generated.
367  # If data is smaller than <x> then data will be stuffed with 0x00
368  #
369  # @example Frame.Tcpv6.Set 00ff02000002 00ff02000001 fe80::1 fe80::2 5678 1234 {-LENGTH 256} {-TCP {-TCPAck {0x09 0xa0 0x30 0xb8}}}
370  #
371  ##
372 
373  set Ethernet DEFAULT
374  set IP DEFAULT
375  set TCP DEFAULT
376 
377  set TCPSeq [ list 0x10 0x00 0x01 0xa3 ]
378  set TCPAck [ list 0x10 0x00 0x01 0xa0 ]
379  set TCPWindow 16384
380  set TCPByte1314 [ list 0x50 0x00 ]
381  set TCPUrgent [ list 0x00 0x00 ]
382 
383  if { ![ string equal -nocase $params "DEFAULT" ]} {
384  foreach {item value} $params {
385  switch -- [ string tolower $item ] {
386  -tcp { set TCP $value }
387  -ip { set IP $value }
388  -ethernet { set Ethernet $value }
389  default { error "Frame.Tcpv6.Set : Invalid param '${item}'" }
390  } ;# switch close
391  }
392  }
393 
394  if { ![ string equal -nocase $TCP "DEFAULT" ]} {
395  foreach {item value} $TCP {
396  switch -- [ string tolower $item ] {
397  -tcpseq { set TCPSeq $value }
398  -tcpack { set TCPAck $value }
399  -tcpwindow { set TCPWindow $value }
400  -tcpbyte1314 { set TCPByte1314 $value }
401  -tcpurgent { set TCPUrgent $value }
402  default { error "Frame.Tcpv6.Set: Invalid TCP option '${item}'" }
403  } ;# switch close
404  }
405  }
406 
407  # Check the TcpSeq
408  if { [ llength $TCPSeq ] != 4 || ![ ByteList.Check $TCPSeq ]} {
409  error "Frame.Tcpv6.Set: Invalid TCPSeq '$TCPSeq', expecting 4 bytes"
410  }
411 
412  # Check the TCPAck
413  if { [ llength $TCPAck ] != 4 || ![ ByteList.Check $TCPAck ]} {
414  error "Frame.Tcpv6.Set: Invalid TCPAck '$TCPAck', expecting 4 bytes"
415  }
416 
417  # Check the TCPWindow
418  if { [ string equal $TCPWindow "" ] || ![ string is integer $TCPWindow ] || $TCPWindow < 0 || $TCPWindow > 0xFFFF} {
419  error "Frame.Tcpv6.Set: Invalid TCPWindow value '$TCPWindow', MUST be <= 0xFFFF"
420  }
421 
422  # Check the TCPByte1314
423  if { [ llength $TCPByte1314 ] != 2 || ![ ByteList.Check $TCPByte1314 ]} {
424  error "Frame.Tcpv6.Set: Invalid TCPByte1314 '$TCPByte1314', expecting 2 bytes"
425  }
426 
427  # Check the TCPUrgent
428  if { [ llength $TCPUrgent ] != 2 || ![ ByteList.Check $TCPUrgent ]} {
429  error "Frame.Tcpv6.Set: Invalid TCPUrgent '$TCPUrgent', expecting 2 bytes"
430  }
431 
432  # Check and convert the data
433  set data [ x.Frame.DataOrLength.Parse $dataOrLength ]
434 
435  set length [ expr ( 20 + [ llength $data ] ) ]
436  set PH1 [ IPv6.To.Hex $SIP ]
437  set PH2 [ IPv6.To.Hex $DIP ]
438  set PH3 [ concat [ Short.To.Hex $length ] 0x00 0x00 0x00 [ Byte.To.Hex 6 ] ]
439  set PH [ concat $PH1 $PH2 $PH3 ]
440 
441  set H1 [ concat [ Short.To.Hex $SP ] [ Short.To.Hex $DP ] ]
442  set H2 $TCPSeq
443  set H3 $TCPAck
444  set H4 [ concat $TCPByte1314 [ Short.To.Hex $TCPWindow ] ]
445  set H5 [ concat 0x00 0x00 $TCPUrgent ]
446 
447  set PHH [ concat $PH $H1 $H2 $H3 $H4 $H5 ]
448  set H [ concat $H1 $H2 $H3 $H4 $H5 ]
449  set myContent [ concat $PHH $data ]
450  if { [ expr ( $length % 2 ) ]} {
451  lappend myContent 0x00
452  }
453  set checkSum [ CheckSum.16 $myContent ]
454  set myContent [ concat $H $data ]
455  set myContent [ eval lreplace {$myContent} 16 17 $checkSum ]
456 
457  set myContent [ Frame.Ipv6.Set $DMAC $SMAC $DIP $SIP 6 $myContent [ list -IP $IP -Ethernet $Ethernet ] ]
458 
459  return $myContent
460 
461 }
462 
463 #**************************************************************************************************
464 
465 proc Frame.Igmp.Set { SMAC SIP Type GroupAddress { params DEFAULT } } {
466 # Sets content of the frame to an IGMP frame.
467 #
468 #------------------------------------------------------------------------------
469 # @param SMAC Source MAC address
470 # @param SIP Source IPv4 address (xxx.xxx.xxx.xxx formatted)
471 # @param Type
472 # 0 - IGMPv1 Join
473 # 1 - IGMPv2 Join
474 # 2 - IGMPv2 Leave
475 # 3 - IGMPv1 / IGMPv2 General Membership Query message
476 # 4 - IGMPv1 / IGMPv2 Group-Specific Membership Query message
477 # 5 - IGMPv3 Membership Report message
478 # 6 - IGMPv3 Leave Group message
479 # 7 - IGMPv3 General Membership Query message
480 # 8 - IGMPv3 Group-Specific Membership Query message
481 # @param GroupAddress Multicast Group Address to create the IGMP Frame for
482 # NOTE: The GroupAddress is unused for Type 3 (IGMPv1 /IGMPv2 General Membership Query)
483 # and Type 7 (IGMPv3 General Membership Query))
484 # @param params A pair list containing alternating a parameter and a value
485 # Parameters are expected to be :
486 # -IGMP
487 # -maxRespTime : 0
488 # -sourceAddressList : [ list ]
489 # -IP
490 # -Version : 4 : IP version
491 # -IHL : 5
492 # -IPIdentif : 0x00 0x00
493 # -TTL : 20
494 # -FF : 0x00 0x00
495 # -ToS : 0x00
496 # -Ethernet
497 # -MFT : EtherII
498 # SNAP
499 # 802.3
500 # -Length : Auto : If no value is given then the frame will nicely encapsulate the <data>
501 # If total length exceeds maximum MTU (1518) then an error will be generated
502 # : x : Ethernet data Length will be adjusted to <x>.
503 # If data exceeds <x> boundary then an error will be generated.
504 # If data is smaller than <x> then data will be stuffed with 0x00
505 #
506 #------------------------------------------------------------------------------
507 # Examples:
508 #
509 # @example Create an IGMPv1 Join frame:
510 # Frame.Igmp.Set 00ff21000001 10.142.1.142 0 224.0.100.12
511 # --> Remark: IGMPv1 Leave does not exist. In the first IGMP version the session has to time-out.
512 # @example Create an IGMPv2 Join frame:
513 # Frame.Igmp.Set 00ff21000001 10.142.1.142 1 224.0.100.12
514 # @example Create an IGMPv2 Leave:
515 # Frame.Igmp.Set 00ff21000001 10.142.1.142 2 224.0.100.12
516 # @example Create an IGMPv1 General Query frame:
517 # Frame.Igmp.Set 00ff21000001 10.142.1.142 3 0.0.0.0
518 # --> Remark: GroupAddress is unused here
519 # @example Create an IGMPv1 Group-Specific Query frame:
520 # Frame.Igmp.Set 00ff21000001 10.142.1.142 4 224.0.100.12
521 # @example Create an IGMPv2 General Query frame:
522 # Frame.Igmp.Set 00ff21000001 10.142.1.142 3 0.0.0.0 [ list -IGMP [ list -maxRespTime 10 ] ]
523 # --> Remark: respTime is only available from version 2
524 # @example Create an IGMPv2 Group-Specific Query frame:
525 # Frame.Igmp.Set 00ff21000001 10.142.1.142 4 224.0.100.12 [ list -IGMP [ list -maxRespTime 10 ] ]
526 # @example Create an IGMPv3 Membership Report message frame:
527 # Frame.Igmp.Set 00ff21000001 10.142.1.142 5 224.0.100.12
528 # Frame.Igmp.Set 00ff21000001 10.142.1.142 5 224.0.100.12 [ list -IGMP [ list -sourceAddressList [ list "10.3.3.71" "10.3.3.72" ] ] ]
529 # @example Create an IGMPv3 Leave Group message frame:
530 # Frame.Igmp.Set 00ff21000001 10.142.1.142 6 224.0.100.12
531 # Frame.Igmp.Set 00ff21000001 10.142.1.142 6 224.0.100.12 [ list -IGMP [ list -sourceAddressList [ list "10.3.3.71" "10.3.3.72" ] ] ]
532 # @example Create an IGMPv3 General Query message frame:
533 # Frame.Igmp.Set 00ff21000001 10.142.1.142 7 0.0.0.0
534 # --> Remark: GroupAddress is unused here
535 # @example Create an IGMPv3 Group-Specific Query message frame:
536 # Frame.Igmp.Set 00ff21000001 10.142.1.142 8 224.0.100.12
537 # Frame.Igmp.Set 00ff21000001 10.142.1.142 8 224.0.100.12 [ list -IGMP [ list -sourceAddressList [ list "10.3.3.71" "10.3.3.72" ] ] ]
538 #
539 #------------------------------------------------------------------------------
540 # Wireshark Remarks:
541 #
542 # Wireshark uses the IP Total Length for differentiating IGMPv1v2 from IGMPv3 Query messages.
543 # Wireshark uses the respTime field for differentiating IGMPv1 from IGMPv2 Query messages (if <> 0, then it is IGMPv2 otherwise it is decoded as IGMPv1).
544 #
545 #------------------------------------------------------------------------------
546 # From IGMPv2 RFC-2236 / RFC-3376:
547 #
548 # Message Type Destination IP Address Destination Group Address
549 # ------------ ---------------------- -------------------------
550 # General Query ALL-SYSTEMS (224.0.0.1) 0.0.0.0
551 # Group-Specific Query The group being queried The group being queried
552 # Membership Report The group being reported The group being reported
553 # Leave Message ALL-ROUTERS (224.0.0.2) The group being left
554 #
555 #------------------------------------------------------------------------------
556 # From IGMPv3 RFC-3376:
557 #
558 # Version 3 Reports are sent with an IP destination address of 224.0.0.22
559 #
560 #------------------------------------------------------------------------------
561 
562  set Ethernet DEFAULT
563  set IP DEFAULT
564  set IGMP DEFAULT
565 
566  if { ![ string equal -nocase $params "DEFAULT" ]} {
567  foreach {item value} $params {
568  switch -- [ string tolower $item ] {
569  -igmp { set IGMP $value }
570  -ip { set IP $value }
571  -ethernet { set Ethernet $value }
572  default { error "Frame.Igmp.Set: Invalid param '${item}'" }
573  } ;#switch close
574  }
575  }
576 
577  set maxResponseTime 0
578  set sourceAddressList [ list ]
579  if { ![ string equal -nocase $IGMP "DEFAULT" ]} {
580  foreach {item value} $IGMP {
581  switch -- [ string tolower $item ] {
582  -maxresptime { set maxResponseTime $value }
583  -sourceaddresslist { set sourceAddressList $value }
584  default { error "Frame.Igmp.Set: Invalid IGMP option '${item}'" }
585  } ;# switch close
586  }
587  }
588 
589  switch -- $Type {
590  0 { set DIP $GroupAddress }
591  1 { set DIP $GroupAddress }
592  2 { set DIP 224.0.0.2 }
593  3 { set DIP 224.0.0.1 }
594  4 { set DIP $GroupAddress }
595  5 { set DIP 224.0.0.22 }
596  6 { set DIP 224.0.0.22 }
597  7 { set DIP 224.0.0.1 }
598  8 { set DIP $GroupAddress }
599  }
600 
601 
602  # --- Ethernet
603 
604  # --- This converts the destination IP address to a multicast MAC-address
605  # + IANA [http://www.iana.org/assignments/ethernet-numbers]:
606  # An Ethernet multicast address consists of the multicast bit, the
607  # 23-bit vendor component, and the 24-bit group identifier assigned by
608  # the vendor.
609  # + RFC-1112 :
610  # An IP host group address is mapped to an Ethernet multicast address
611  # by placing the low-order 23-bits of the IP address into the low-order
612  # 23 bits of the Ethernet multicast address 01-00-5E-00-00-00 (hex).
613  set DMAC [ Multicast.IP.To.Mac $DIP ]
614 
615  # --- IPv4
616  set ipProtocol 0x02 ;# IGMP
617 
618  # --- IP Options : Router Alert
619  # + RFC-2236 : Every IGMP message described in this document is sent with an IP Time-to-Live of 1
620  # + RFC-2236 : Every IGMP message described in this document carries an IP Router Alert option [RFC-2113] in its IP header.
621  set IgmpIpOptions [ list -TTL 1 -IPOptions [ list 0x94 0x04 0x00 0x00 ] ]
622  if { [ string equal -nocase $IP "DEFAULT" ]} {
623  set IP $IgmpIpOptions
624  } else {
625  set IP [ eval list $IgmpIpOptions $IP ]
626  }
627 
628  # --- IGMP
629 
630  # + IGMPv2 RFC-2236:
631  # 0x11 = Membership Query
632  # 0x16 = Version 2 Membership Report
633  # 0x17 = Leave Group
634  # There is an additional type of message:
635  # (for backwards-compatibility with IGMPv1)
636  # 0x12 = Version 1 Membership Report
637 
638  # + IGMPv3 RFC-3376:
639  # 0x11 = Membership Query
640  # 0x22 = Version 3 Membership Report
641  switch -- $Type {
642  0 {
643  # --- IGMPv1 Join
644  set IGMPPart1 { 0x12 0x00 }
645  set IGMPPart2 [ IP.To.Hex $GroupAddress ]
646  }
647  1 {
648  # --- IGMPv2 Join
649  set IGMPPart1 { 0x16 0x00 }
650  set IGMPPart2 [ IP.To.Hex $GroupAddress ]
651  }
652  2 {
653  set IGMPPart1 { 0x17 0x00 }
654  set IGMPPart2 [ IP.To.Hex $GroupAddress ]
655  }
656  3 {
657  set IGMPPart1 [ list 0x11 [ Byte.To.Hex $maxResponseTime ] ]
658  set IGMPPart2 [ list 0x00 0x00 0x00 0x00 ]
659  }
660  4 {
661  set IGMPPart1 [ list 0x11 [ Byte.To.Hex $maxResponseTime ] ]
662  set IGMPPart2 [ IP.To.Hex $GroupAddress ]
663  }
664  5 {
665  # --- IGMPv3 Membership Report message
666  set IGMPPart1 [ list 0x22 0x00 ]
667  set nrOfGroupRecords 1
668  if { [ llength $sourceAddressList ] > 0 } { set recordType 0x05 } else { set recordType 0x02 }
669  set IGMPPart2 " 0x00 0x00 0x00 [ Short.To.Hex $nrOfGroupRecords ] $recordType 0x00 [ Short.To.Hex [ llength $sourceAddressList ] ] [ IP.To.Hex $GroupAddress ]"
670  }
671  6 {
672  # --- IGMPv3 Leave Group message
673  set IGMPPart1 [ list 0x22 0x00 ]
674  if { [ llength $sourceAddressList ] > 0 } { set recordType 0x06 } else { set recordType 0x01 }
675  set nrOfGroupRecords 1
676  set IGMPPart2 " 0x00 0x00 [ Short.To.Hex $nrOfGroupRecords ] $recordType 0x00 [ Short.To.Hex [ llength $sourceAddressList ] ] [ IP.To.Hex $GroupAddress ]"
677  }
678  7 {
679  # --- IGMPv3 General Membership Query
680  set IGMPPart1 [ list 0x11 [ Byte.To.Hex $maxResponseTime ] ]
681  set IGMPPart2 " 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 "
682  }
683  8 {
684  # --- IGMPv3 Group-Specific Membership Query and Group-and-Source-Specific Membership Query
685  set IGMPPart1 [ list 0x11 [ Byte.To.Hex $maxResponseTime ] ]
686  set IGMPPart2 " [ IP.To.Hex $GroupAddress ] 0x00 0x00 [ Short.To.Hex [ llength $sourceAddressList ] ]"
687  }
688  }
689 
690  set myContent [ concat $IGMPPart1 { 0x00 0x00 } $IGMPPart2 ]
691  set sourceSpecificAdresses [ list ]
692  foreach sourceAddress $sourceAddressList {
693  eval lappend myContent [ IP.To.Hex $address ]
694  }
695 
696  # --- IGMP Checksum
697  set checkSum [ CheckSum.16 $myContent ]
698  set myContent [ eval lreplace { $myContent } 2 3 $checkSum ]
699  puts "IGMP : $myContent"
700 
701  set myContent [ Frame.Ipv4.Set $DMAC $SMAC $DIP $SIP $ipProtocol $myContent [ list -IP $IP -Ethernet $Ethernet ] ]
702 
703  return $myContent
704 }
705 
706 #**************************************************************************************************
707 
708 proc Frame.Icmp.Echo.Set { DMAC SMAC DIP SIP ID seqNr dataOrLength { params DEFAULT } } {
709  # Sets content of the frame to an ICMP Echo Request/Reply frame.
710  #
711  # @param DMAC Destination MAC address
712  # @param SMAC Source MAC address
713  # @param DIP Destination IPv4 address (xxx.xxx.xxx.xxx formatted)
714  # @param SIP Source IPv4 address (xxx.xxx.xxx.xxx formatted)
715  # @param ID Icmp Echo Identifier field (decimal)
716  # @param seqNr Icmp Echo Sequence Number field (decimal)
717  # @param dataOrLength Data of the ICMP Echo frame.
718  # Can also specify a length if <dataOrLength> is a list
719  # with '-length' as first element and an integer
720  # specifying the datalength as a second element
721  #
722  # @param params A pair list containing alternating a parameter and a value
723  # Parameters are expected to be :
724  # -ICMP
725  # -Type : 8 : Echo Request
726  # -Code : 0
727  # -IP
728  # -Version : 4 : IP version
729  # -IHL : 5
730  # -IPIdentif : 0x00 0x00
731  # -TTL : 20
732  # -FF : 0x00 0x00
733  # -ToS : 0x00
734  # -Ethernet
735  # -MFT : EtherII
736  # SNAP
737  # 802.3
738  # -Length : Auto : If no value is given then the frame will nicely encapsulate the <data>
739  # If total length exceeds maximum MTU (1518) then an error will be generated
740  # : x : Ethernet data Length will be adjusted to <x>.
741  # If data exceeds <x> boundary then an error will be generated.
742  # If data is smaller than <x> then data will be stuffed with 0x00
743  #
744  # e.g.: ICMP Echo Request Frame with Identifier 0x1234, Sequence Number 0x5678 and Data 0xdd 0xdd 0xdd 0xdd 0xdd 0xdd
745  # @example Frame.Icmp.Set 00ff02000002 00ff02000001 10.11.0.2 10.11.0.1 0x1234 0x5678 {-length 6}
746  ##
747 
748  set icmpType 8
749  set icmpCode 0
750 
751  set Ethernet DEFAULT
752  set IP DEFAULT
753  set ICMP DEFAULT
754 
755  if { ![ string equal -nocase $params "DEFAULT" ]} {
756  foreach {item value} $params {
757  switch -- [ string tolower $item ] {
758  -icmp { set ICMP $value }
759  -ip { set IP $value }
760  -ethernet { set Ethernet $value }
761  default { error "Frame.Icmp.Echo.Set : Invalid param '${item}'" }
762  } ;# switch close
763  }
764  }
765 
766  if { ![ string equal -nocase $ICMP "DEFAULT" ]} {
767  foreach {item value} $ICMP {
768  switch -- [ string tolower $item ] {
769  -type { set icmpType $value }
770  -code { set icmpCode $value }
771  default { error "Frame.Icmp.Echo.Set: Invalid ICMP option '${item}'" }
772  } ;# switch close
773  }
774  }
775 
776  # Check the ID
777  if { [ string equal $ID "" ] || ![ string is integer $ID ] || $ID < 0 || $ID > 0xFFFF} {
778  error "Frame.Icmp.Echo.Set : Invalid ID value '$ID', MUST be <= 0xFFFF"
779  }
780 
781  # Check the seqNr
782  if { [ string equal $seqNr "" ] || ![ string is integer $seqNr ] || $seqNr < 0 || $seqNr > 0xFFFF} {
783  error "Frame.Icmp.Echo.Set : Invalid seqNr value '$seqNr', MUST be <= 0xFFFF"
784  }
785 
786  # Check and convert the data
787  set data [ x.Frame.DataOrLength.Parse $dataOrLength ]
788 
789  # ICMP ECHO
790  set H1 [ Short.To.Hex $ID ]
791  set H2 [ Short.To.Hex $seqNr ]
792  set H [ concat $H1 $H2 ]
793  set myContent [ concat $H $data ]
794 
795  set myContent [ Frame.Icmp.Set $DMAC $SMAC $DIP $SIP $icmpType $icmpCode $myContent [ list -IP $IP -Ethernet $Ethernet ] ]
796 
797  return $myContent
798 
799 }
800 
801 #**************************************************************************************************
802 
803 proc Frame.Icmp.Set { DMAC SMAC DIP SIP Type Code dataOrLength { params DEFAULT } } {
804  # Sets content of the frame to an ICMP frame.
805  #
806  # @param DMAC Destination MAC address
807  # @param SMAC Source MAC address
808  # @param DIP Destination IPv4 address (xxx.xxx.xxx.xxx formatted)
809  # @param SIP Source IPv4 address (xxx.xxx.xxx.xxx formatted)
810  # @param Type Icmp Type field (decimal)
811  # @param Code Icmp Code field (decimal)
812  # @param dataOrLength Data of the ICMP frame.
813  # Can also specify a length if <dataOrLength> is a list
814  # with '-length' as first element and an integer
815  # specifying the datalength as a second element
816  #
817  # @param params A pair list containing alternating a parameter and a value
818  # Parameters are expected to be :
819  # -IP:
820  # -Version : 4 : IP version
821  # -IHL : 5
822  # -IPIdentif : 0x00 0x00
823  # -TTL : 20
824  # -FF : 0x00 0x00
825  # -ToS : 0x00
826  # -Ethernet
827  # -MFT : EtherII
828  # SNAP
829  # 802.3
830  # -Length : Auto : If no value is given then the frame will nicely encapsulate the <data>
831  # If total length exceeds maximum MTU (1518) then an error will be generated
832  # : x : Ethernet data Length will be adjusted to <x>.
833  # If data exceeds <x> boundary then an error will be generated.
834  # If data is smaller than <x> then data will be stuffed with 0x00
835  #
836  # e.g.: ICMP Echo Request Frame with Identifier 0x1234, Sequence Number 0x5678 and Data 0xdd 0xdd 0xdd 0xdd 0xdd 0xdd
837  # @example Frame.Icmp.Set 00ff02000002 00ff02000001 10.11.0.2 10.11.0.1 0x08 0x00 {0x12 0x34 0x56 0x78 0xdd 0xdd 0xdd 0xdd 0xdd 0xdd}
838  ##
839 
840  set Ethernet DEFAULT
841  set IP DEFAULT
842 
843  if { ![ string equal -nocase $params "DEFAULT" ]} {
844  foreach {item value} $params {
845  switch -- [ string tolower $item ] {
846  -ip { set IP $value }
847  -ethernet { set Ethernet $value }
848  default { error "Frame.Icmp.Set : Invalid param '${item}'" }
849  } ;# switch close
850  }
851  }
852 
853  # Check the Type
854  if { [ string equal $Type "" ] || ![ string is integer $Type ] || $Type < 0 || $Type > 0xFF} {
855  error "Frame.Icmp.Set : Invalid Type value '$Type', MUST be <= 0xFF"
856  }
857 
858  # Check the Code
859  if { [ string equal $Code "" ] || ![ string is integer $Code ] || $Code < 0 || $Code > 0xFF} {
860  error "Frame.Icmp.Set : Invalid Code value '$Code', MUST be <= 0xFF"
861  }
862 
863  # Check and convert the data
864  set data [ x.Frame.DataOrLength.Parse $dataOrLength ]
865 
866  # ICMP
867  set H1 [ Byte.To.Hex $Type ]
868  set H2 [ Byte.To.Hex $Code ]
869  set H [ concat $H1 $H2 ]
870  set PlaceHolder [ list 0x00 0x00 ]
871  set myContent [ concat $H $PlaceHolder $data ]
872  set checkSum [ CheckSum.16 $myContent ]
873  set myContent [ eval lreplace {$myContent} 2 3 $checkSum ]
874 
875  set myContent [ Frame.Ipv4.Set $DMAC $SMAC $DIP $SIP 0x01 $myContent [ list -IP $IP -Ethernet $Ethernet ] ]
876 
877  return $myContent
878 
879 }
880 
881 #**************************************************************************************************
882 
883 proc Frame.Ipv4.Set { DMAC SMAC DIP SIP protocol dataOrLength { params DEFAULT } } {
884  # Sets the content of frame to the IPv4 frame specified by the parameters in @see.param params
885  #
886  # @param DMAC Destination MAC address
887  # @param SMAC Source MAC address
888  # @param SIP Source IPv4 address (xxx.xxx.xxx.xxx formatted)
889  # @param DIP Destination IPv4 address (xxx.xxx.xxx.xxx formatted)
890  # @param protocol Protocol field in IP header (6:TCP, 17:UDP) (decimal)
891  # @param dataOrLength IP Data part (TCP frame, UDP frame, ...)
892  # Can also specify a length if <dataOrLength> is a list
893  # with '-length' as first element and an integer
894  # specifying the datalength as a second element
895  #
896  # @param params A pair list containing alternating a parameter and a value
897  # Parameters are expected to be :
898  # -Version : 4
899  # -IHL : 5
900  # -IPIdentif : 0x00 0x00
901  # -TTL : 20
902  # -FF : 0x00 0x00
903  # -ToS : 0x00
904  #
905  # @example Frame.Ipv4.Set 10.11.0.1 10.2.3.4 6 {0x00 0x00 0x00} {-ToS 0x01}
906  #
907  ##
908 
909  set Ethernet DEFAULT
910  set IP DEFAULT
911 
912  set Version 4
913  set IHL 5
914  set ToS 0x00
915  set IPIdentif [ list 0x00 0x00 ]
916  set FF [ list 0x00 0x00 ]
917  set TTL 20
918  set IPOptions [ list ]
919 
920  if { ![ string equal -nocase $params "DEFAULT" ]} {
921  foreach { item value } $params {
922  switch -- [ string tolower $item ] {
923  -ip { set IP $value }
924  -ethernet { set Ethernet $value }
925  default { error "Frame.Ipv4.Set: Invalid param '${item}'" }
926  } ;# switch close
927  }
928  }
929 
930  set overrideIHL 0
931  if { ![ string equal -nocase $IP "DEFAULT" ]} {
932  foreach {item value} $IP {
933  switch -- [ string tolower $item ] {
934  -version { set Version $value }
935  -ihl { set IHL $value; set overrideIHL 1 }
936  -ipidentif { set IPIdentif $value }
937  -ttl { set TTL $value }
938  -ff { set FF $value }
939  -tos { set ToS $value }
940  -ipoptions { set IPOptions $value }
941  default { error "Frame.Ipv4.Set: Invalid param '$item'" }
942  } ;#switch close
943  }
944  }
945 
946  # Check the Version
947  if { [ string equal $Version "" ] || ![ string is integer $Version ] || $Version < 0 || $Version > 0xF} {
948  error "Frame.Ipv4.Set: Invalid Version value '$Version', MUST be <= 0xF"
949  }
950 
951  # Check the IPOptions
952  if { [ expr ( [ llength $IPOptions ] % 4 ) ] != 0 || ![ ByteList.Check $IPOptions ]} {
953  error "Frame.Ipv4.Set: Invalid IPOptions \`$IPOptions', expecting multiple of 4 bytes"
954  }
955 
956  # Check the IHL
957  if { $overrideIHL != 1} {
958  set IHL [ expr 5 + ( [ llength $IPOptions ] / 4 ) ]
959  }
960  if { [ string equal $IHL "" ] || ![ string is integer $IHL ] || $IHL < 0 || $IHL > 0xF} {
961  error "Frame.Ipv4.Set: Invalid IHL value '$IHL', MUST be <= 0xF"
962  }
963 
964  # Check the IPIdentif
965  if { [ llength $IPIdentif ] != 2 || ![ ByteList.Check $IPIdentif ]} {
966  error "Frame.Ipv4.Set: Invalid IPIdentif '$IPIdentif', expecting 2 bytes"
967  }
968 
969  # Check the TTL
970  if { [ string equal $TTL "" ] || ![ string is integer $TTL ] || $TTL < 0 || $TTL > 0xFF} {
971  error "Frame.Ipv4.Set: Invalid TTL value '$TTL', MUST be <= 0xFF"
972  }
973 
974  # Check the FF value
975  if { [ llength $FF ] != 2 || ![ ByteList.Check $FF ]} {
976  error "Frame.Ipv4.Set: Invalid FF '$FF', expecting 2 bytes"
977  }
978 
979  # Check the ToS
980  if { [ string equal $ToS "" ] || ![ string is integer $ToS ] || $ToS < 0 || $ToS > 0xFF} {
981  error "Frame.Ipv4.Set: Invalid ToS value '$Tos', MUST be <= 0xFF"
982  }
983  set ToS [ format "0x%02x" [ expr $ToS * 1 ] ]
984 
985  # Check and convert the data
986  set data [ x.Frame.DataOrLength.Parse $dataOrLength ]
987  set length [ Short.To.Hex [ expr ( 4 * $IHL + [ llength $data ] ) ] ]
988 
989  set H1 [ concat [ format "0x%02x" [ expr ( $Version << 4 ) & 0xF0 | ( $IHL & 0x0F ) ] ] "$ToS" $length ]
990  set H2 [ concat $IPIdentif $FF ]
991  set H3 [ list [ Byte.To.Hex $TTL ] [ Byte.To.Hex $protocol ] 0x00 0x00 ]
992  set H4 [ IP.To.Hex $SIP ]
993  set H5 [ IP.To.Hex $DIP ]
994  set H6 $IPOptions
995  set myContent [ concat $H1 $H2 $H3 $H4 $H5 $H6 ]
996 
997  set IPCheckSum [ CheckSum.16 $myContent ]
998  set myContent [ eval lreplace {$myContent} 10 11 $IPCheckSum ]
999  set myContent [ concat $myContent $data ]
1000 
1001  set myContent [ Frame.Mac.Set $DMAC $SMAC 0x0800 $myContent $Ethernet ]
1002 
1003  return $myContent
1004 
1005 }
1006 
1007 #**************************************************************************************************
1008 
1009 proc Frame.Ipv6.Set {DMAC SMAC DIP SIP nextHeader dataOrLength {params DEFAULT}} {
1010  # Sets the content of frame to the IPv6 frame specified by the parameters in @see.param params
1011  #
1012  # @param DMAC Destination MAC address
1013  # @param SMAC Source MAC address
1014  # @param SIP Source IPv6 address (colon notation)
1015  # @param DIP Destination IPv6 address (colon notation)
1016  # @param nextHeader Next-Header field in IP header (6: TCP, 17: UDP, 0: hop-by-hop options, 58: ICMPv6, ...) (decimal)
1017  # @param dataOrLength IP Data part (TCP frame, UDP frame, ...)
1018  # Can also specify a length if <dataOrLength> is a list
1019  # with '-length' as first element and an integer
1020  # specifying the datalength as a second element
1021  #
1022  # @param params A pair list containing alternating a parameter and a value
1023  # Parameters are expected to be :
1024  # -version : 6 (4 bits)
1025  # -trafClass : 0x00 (8 bits)
1026  # -flowLabel : 0x0X 0xXX 0xXX (default: 0x00 0x00 0x00) (20 bits)
1027  # -hopLimit : 64 (8 bits)
1028  # -jumbo : 0 or 1 (indicates the use of the Jumbo Payload option in the Hop-by-hop
1029  # extension header. 1 -> jumbo payload is used)
1030  #
1031  # @example Frame.Ipv6.Set 2001:db8:1:55:e191:2216:e96:6038 2001:db8:1:55:f4fe:2d09:6631:9162 6 {0x00 0x00 0x00} {-hopLimit 32}
1032  #
1033  ##
1034 
1035  set Ethernet DEFAULT
1036  set IP DEFAULT
1037 
1038  set version 6
1039  set trafClass 0x00
1040  set flowLabel [ list 0x00 0x00 0x00 ]
1041  set hopLimit 64
1042  set jumbo 0
1043 
1044  if { ![ string equal -nocase $params "DEFAULT" ]} {
1045  foreach {item value} $params {
1046  switch -- [ string tolower $item ] {
1047  -ip { set IP $value }
1048  -ethernet { set Ethernet $value }
1049  default { error "Frame.Ipv6.Set : Invalid param '${item}'" }
1050  } ;# switch close
1051  }
1052  }
1053 
1054  if { ![ string equal -nocase $IP "DEFAULT" ]} {
1055  foreach {item value} $IP {
1056  switch -- [ string tolower $item ] {
1057  -version { set version $value }
1058  -trafclass { set trafClass $value }
1059  -flowlabel { set flowLabel $value }
1060  -hoplimit { set hopLimit $value }
1061  -jumbo { set jumbo $value }
1062  default { error "Frame.Ipv6.Set: Invalid param '$item'" }
1063  } ;#switch close
1064  }
1065  }
1066 
1067  # Check the version
1068  if { [ string equal $version "" ] || ![ string is integer $version ] || $version < 0 || $version > 0xF} {
1069  error "Frame.Ipv6.Set: Invalid version value '$version', MUST be <= 0xF"
1070  }
1071 
1072  # Check the trafClass
1073  if { [ string equal $trafClass "" ] || ![ string is integer $trafClass ] || $trafClass < 0 || $trafClass > 0xFF} {
1074  error "Frame.Ipv6.Set: Invalid trafClass value '$trafClass', MUST be <= 0xFF"
1075  }
1076 
1077  # Check the flowLabel
1078  if { [ llength $flowLabel ] != 3 || ![ ByteList.Check $flowLabel ] || [ lindex $flowLabel 0 ] > 0xF} {
1079  error "Frame.Ipv6.Set: Invalid flowLabel '$flowLabel', expecting 20 bits"
1080  }
1081 
1082  # Check the hopLimit
1083  if { [ string equal $hopLimit "" ] || ![ string is integer $hopLimit ] || $hopLimit < 0 || $hopLimit > 0xFF} {
1084  error "Frame.Ipv6.Set: Invalid hopLimit value '$hopLimit', MUST be <= 0xFF"
1085  }
1086 
1087  # Check and convert the data
1088  set data [ x.Frame.DataOrLength.Parse $dataOrLength ]
1089 
1090  set H1 [ list ]
1091  lappend H1 [ format "0x%02x" [ expr ( ( $version << 4 ) & 0xF0 ) | ( ( $trafClass >> 4 ) & 0x0F ) ] ]
1092  lappend H1 [ format "0x%02x" [ expr ( ( $trafClass << 4 ) & 0xF0 ) | [ lindex $flowLabel 0 ] & 0x0F ] ]
1093  eval lappend H1 [ lrange $flowLabel 1 end ]
1094  if { !$jumbo} {
1095  set length [ Short.To.Hex [ llength $data ] ]
1096  } else {
1097  # For payload lengths > 65535, the payload length is set to 0 and the Jumbo Payload option is used in the
1098  # Hop-by-hop options extension header
1099  set length [ Short.To.Hex 0 ]
1100  }
1101 
1102  set H2 [ concat $length [ Byte.To.Hex $nextHeader ] [ Byte.To.Hex $hopLimit ] ]
1103  set H3 [ IPv6.To.Hex $SIP ]
1104  set H4 [ IPv6.To.Hex $DIP ]
1105  set myContent [ concat $H1 $H2 $H3 $H4 ]
1106  set myContent [ concat $myContent $data ]
1107 
1108  set myContent [ Frame.Mac.Set $DMAC $SMAC 0x86dd $myContent $Ethernet ]
1109 
1110  return $myContent
1111 
1112 }
1113 
1114 #**************************************************************************************************
1115 
1116 proc Frame.Mac.Set { DMAC SMAC EtherType data { params DEFAULT } } {
1117  # Sets frame content to ethernet frame.
1118  #
1119  # @param SMAC Source MAC address
1120  # @param DMAC Destination MAC address
1121  # @param EtherType : 0x0800 : IPv4
1122  # 0x0806 : Arp
1123  # 0x814C : SNMP
1124  # 0x86DD : IPv6
1125  # @param data payload, sometimes referred to as MACData (ByteList)
1126  #
1127  # @param params A pair list containing alternating a parameter and a value
1128  # Parameters are expected to be :
1129  # -length : Auto : Total length of frame, including CRC
1130  # If needed the length of the frame can be padded with 0x00
1131  # Note that an Ethernet II frame has no length specified. If total length
1132  # however exceeds 1518 (MTU ethernet) then an error will be generated.
1133  # -MFT : Media Frame Type - What encoding on ethernet level must be used?
1134  # EtherII : DMAC(6) SMAC(6) EtherType(2) Data(x)
1135  # SNAP : DMAC(6) SMAC(6) LengthOfMacDataThatFollowsHere(2) LLC(3+5) Data(x)
1136  # 802.3 : DMAC(6) SMAC(6) LengthOfMacDataThatFollowsHere(2) Data(x)
1137  #
1138  # @example Frame.Mac.Set 00-00-00-00-00-02 00-00-00-00-00-02 0x806 "0x00 0x00" {-Length Auto -MFT SNAP }
1139  #
1140  ##
1141 
1142  set EMFT EtherII
1143  set ELength auto
1144 
1145  if { ![ string equal -nocase $params "DEFAULT" ]} {
1146  foreach {item value} $params {
1147  switch -- [ string tolower $item ] {
1148  -mft { set EMFT $value }
1149  -length { set ELength $value }
1150  default { error "Frame.Mac.Set : Invalid Ethernet param '$item'" }
1151  } ;# switch close
1152  }
1153  }
1154 
1155  switch -- [ string tolower $EMFT ] {
1156  snap { set myContent [ x.Frame.SNAP.Set $DMAC $SMAC $EtherType $data $ELength ] }
1157  etherii { set myContent [ x.Frame.EtherII.Set $DMAC $SMAC $EtherType $data $ELength ] }
1158  802.3 { set myContent [ x.Frame.802.3.Set $DMAC $SMAC $data $ELength ] }
1159  default { error "Frame.Mac.Set : Invalid Ethernet MFT (Media Frame Type) '$EMFT'" }
1160  }
1161 
1162  return $myContent
1163 
1164 }
1165 
1166 #**************************************************************************************************
1167 
1168 proc Frame.Pad { frame {length 64} {padByte 0x00} } {
1169  # Will pad the @see.param frame's content with 0x00 till length matches @see.param length Bytes
1170  # if framelength already exceeds this new @see.param length then nothing will happen to the frame.
1171  #
1172  # @param frame The input frame.
1173  # @param length The length of the output frame.
1174  #
1175  # @note On Ethernet: Minimum length is 64 bytes (incl. 4 bytes CRC)
1176  # Maximum is 1518 bytes (incl. 4 bytes CRC)
1177  #
1178  ##
1179 
1180  # Check the padding byte
1181  set padByte [ Byte.To.Hex $padByte ]
1182 
1183  # Check the frame ByteList
1184  if { ![ ByteList.Check $frame ]} {
1185  error "Frame.Pad: Invalid frame ByteList: '${frame}'"
1186  }
1187 
1188  set frameLength [ llength $frame ]
1189  while { $frameLength < $length} {
1190  lappend frame $padByte
1191  incr frameLength
1192  }
1193 
1194  return $frame
1195 
1196 }
1197 
1198 #**************************************************************************************************
1199 
1200 proc Frame.Vlan.Tag.Create { id priority dropeligable tpid } {
1201 # Create a 802.1q VLAN tag.
1202 #
1203 # This tag can be inserted at the correct position in the MAC header.
1204 # It is inserted just before the Ethernet header's EtherType bytes.
1205 #
1206 # These tags can be added multiple times to stack tags.
1207 # In this case, the innnermost should be added first.
1208 #
1209 # @param id
1210 # The VLAN id, with values ranging from 0 to 4095.
1211 #
1212 # @param priority
1213 # The priority class (values 0-7).
1214 #
1215 # @param dropeligable
1216 # A drop eligable flag (value 0 or 1).
1217 #
1218 # @param tpid
1219 # The VLAN TPID to use. Value should be either C-TAG (customer tag) 0x8100
1220 # or S-TAG (service-provider tag) 0x88a8.
1221 # When stacking VLANs, the innermost VLAN should use the C-TAG 0x8100.
1222 # All other VLAN tags should use the S-TAG 0x88a8.
1223 #
1224 # @return
1225 # Byte list containing the VLAN tag
1226 #
1227 # @example
1228 # Frame.Vlan.Tag.Create 23 2 0
1229 ##
1230 
1231  # Check the id
1232  if { [ string equal $id "" ] || ![ string is integer $id ] || $id < 0 || $id > 0xFFF} {
1233  error "Frame.Vlan.Insert: Invalid id value '$id', MUST be <= 4095 (0xFFF)"
1234  }
1235 
1236  # Check the priority
1237  if { [ string equal $priority "" ] || ![ string is integer $priority ] || $priority < 0 || $priority > 7} {
1238  error "Frame.Vlan.Insert: Invalid priority value '$priority', MUST be <= 7"
1239  }
1240 
1241  # Check the dropeligable
1242  if { [ string equal $dropeligable "" ] || ![ string is integer $dropeligable ] || $dropeligable < 0 || $dropeligable > 1} {
1243  error "Frame.Vlan.Insert: Invalid dropeligable value '$dropeligable', MUST be 0 or 1"
1244  }
1245 
1246  # Check the tpid
1247  if { [ string equal $tpid "" ] || ![ string is integer $tpid ] || $tpid < 0 || $tpid > 0xFFFF} {
1248  error "Frame.Vlan.Insert: Invalid tpid value '$tpid', MUST be 0x8100 or 0x88a8"
1249  }
1250  if { $tpid != 0x8100 && $tpid != 0x88a8} {
1251  puts stderr "Frame.Vlan.Insert: WARNING: Using unsupported tpid value '$tpid', should be 0x8100 or 0x88a8."
1252  }
1253 
1254  # Create the VLAN Tag
1255  # Bit-level structure of T1 and T2: PPPDIIII IIIIIIII (P=priority, D=dropeligable, I= id)
1256  set T1 [ format "0x%02x" [ expr ( ( $priority << 5 ) & 0xE0 ) | ( ( $dropeligable << 4 ) & 0x10 ) | ( ( $id >> 8 ) & 0x0F ) ] ]
1257  set T2 [ format "0x%02x" [ expr ( $id % 256 ) ] ]
1258 
1259  return [ concat [ eval list [ Short.To.Hex $tpid ] $T1 $T2 ] ]
1260 }
1261 
1262 #**************************************************************************************************
1263 
1264 proc Frame.Vlan.Insert { frame id priority dropeligable { tpid 0x8100 } } {
1265 # Will insert the 802.1q VLAN tag into the @see.param frame content, at the correct position in
1266 # the MAC header. It is inserted just before the EtherType bytes. Can be called multiple times
1267 # to stack tags. In this case, the innnermost should be called first.
1268 #
1269 # @param id
1270 # The VLAN id, with values ranging from 0 to 4095.
1271 #
1272 # @param priority
1273 # The priority class (values 0-7).
1274 #
1275 # @param dropeligable
1276 # A drop eligable flag (value 0 or 1).
1277 #
1278 # @param tpid
1279 # The VLAN TPID to use. Value should be either C-TAG (customer tag) 0x8100
1280 # or S-TAG (service-provider tag) 0x88a8.
1281 # When stacking VLANs, the innermost VLAN should use the C-TAG 0x8100.
1282 # All other VLAN tags should use the S-TAG 0x88a8.
1283 # I.e. the S-TAG should be used for all but the first call to @ref Frame.Vlan.Insert
1284 # Default: 0x8100 (C-TAG)
1285 #
1286 # @example
1287 # Frame.Vlan.Insert $baseEthFrame 23 2 0
1288 ##
1289 
1290  # Check the frame ByteList
1291  if { ![ ByteList.Check $frame ]} {
1292  error "Frame.Vlan.Insert: Invalid frame ByteList: '${frame}'"
1293  }
1294 
1295  # Check if frame is long enough to contain a MAC header (14 bytes)
1296  if { [ llength $frame ] < 14} {
1297  error "Frame.Vlan.Insert: Could not insert Tag. ByteList is shorter than a MAC header (14 bytes)."
1298  }
1299 
1300  # Create the VLAN Tag
1301  set tag [ Frame.Vlan.Tag.Create $id $priority $dropeligable $tpid ]
1302 
1303  # Insert it into the MAC header, just before the Ethertype tag:
1304  return [ join [ linsert $frame 12 $tag ] ]
1305 }
1306 
1307 #**************************************************************************************************
1308 
1309 proc Frame.PPP.ProtocolId.FromEtherType { ethernetEtherType } {
1310 # Returns the "uncompressed" protocol Id, mapped from the Ethernet's EtherType.
1311 #
1312 ##
1313  switch -- [ string tolower $ethernetEtherType ] {
1314  0x0800 { return 0x0021 }
1315  0x86dd { return 0x0057 }
1316  }
1317 
1318  error "Unsupported conversion from Ethernet EtherType '${ethernetEtherType}' to PPP Protocol ID"
1319 }
1320 
1321 #**************************************************************************************************
1322 
1323 proc Frame.PPPoE.Session.Header.Create { sessionId payloadLength ethernetEtherType } {
1324 # Creates PPPoE + PPP header for active PPPoE session.
1325 #
1326 # This method generates a PPPoE header which can be used to insert into an Ethernet frame.
1327 #
1328 # You can provide the EtherType of the higher-layer. It will be converted to a valid
1329 # Protocol ID for the PPP header.
1330 #
1331 # When inserted into the Ethernet frame:
1332 # - The EtherType for this header MUST be 0x8864 (PPPoE Session)
1333 # - The header returned MUST be inserted right before the Ethernet payload.
1334 # (after possible VLAN tags and final EtherType).
1335 # See also https://tools.ietf.org/html/rfc2516#page-4
1336 #
1337 # @param sessionId
1338 # PPPoE session ID (0-65535)
1339 #
1340 # @param payloadLength
1341 # Length of the payload above PPPoE/PPP headers (thus excluding the PPP headers).
1342 #
1343 # @param ethernetEtherType
1344 # Ethernet EtherType which will be converted to (uncompressed!) Protocol ID for PPP Header.
1345 # See also @ref Frame.PPP.ProtocolId.FromEtherType
1346 #
1347 # @return
1348 # PPPoE Session header including PPP Header
1349 ##
1350 
1351  set Version "0x01"
1352  set Type "0x01"
1353  set Code "0x00"; # PPPoE Session Data
1354  # @warning
1355  # Since we only support IPv4 and IPv6 now (see Frame.PPP.ProtocolId.FromEtherType),
1356  # we set a fixed length of 2 bytes.
1357  set PppHeaderLength 2
1358 
1359  # Check the Session ID
1360  if { [ string equal $sessionId "" ] || ![ string is integer $sessionId ] || $sessionId < 0 || $sessionId > 0xFFFF} {
1361  error "Frame.PPPoE.Session.Tag.Create: Invalid sessionId value '$sessionId', MUST be <= 65635 (0xffff)"
1362  }
1363 
1364  # Check the Payload Length
1365  if { [ string equal $payloadLength "" ] || ![ string is integer $payloadLength ] || $payloadLength < 0 || $payloadLength > ( 0xFFFF - $PppHeaderLength )} {
1366  error "Frame.PPPoE.Session.Tag.Create: Invalid payloadLength value '$payloadLength', MUST be <= [ expr 0xFFFF - $PppHeaderLength ] (0xffff - $PppHeaderLength)"
1367  }
1368 
1369  # --- Convert the UlProtocol type for PPP:
1370  if { [ catch { Frame.PPP.ProtocolId.FromEtherType $ethernetEtherType } pppProtocolId ]} {
1371  # --- Unsupported conversion
1372  # FIXME: What to do with this one?
1373  # How do we send an unknown frame type over PPPoE???
1374  error "LinkLayer.AutoComplete: ${pppProtocolId}"
1375  }
1376 
1377  # --- Generate the PPPoE header
1378 
1379  # Bit-level structure of T1: VVVVTTTT (V=Version, T=Type)
1380  set versionAndType [ format "0x%02x" [ expr ( ( $Version << 4 ) & 0xF0 ) | ( $Type & 0x0F ) ] ]
1381  set Code [ format "0x%02x" [ expr ( $Code % 256 ) ] ]
1382  set sessionId [ Short.To.Hex $sessionId ]
1383 
1384  # ... + PPP UL Protocol length
1385  set payloadLength [ Short.To.Hex [ expr $payloadLength + $PppHeaderLength ] ]
1386 
1387  # Fill in the PPP header
1388  set pppProtocolHeader [ Short.To.Hex $pppProtocolId ]
1389 
1390  return [ concat [ eval list $versionAndType $Code $sessionId $payloadLength $pppProtocolHeader ] ]
1391 }
1392 
1393 #**************************************************************************************************
1394 
1395 proc x.Frame.EtherII.Set { DMAC SMAC type data {length auto} } {
1396  # Sets frame content to ethernet frame (Ethernet II, where the <Type> Field > 0x05dc or 1500)
1397  # without CRC.
1398  # <SMAC> <DMAC> : Mac address
1399  # <Type> :
1400  # IPv4 - 0x0800
1401  # Arp - 0x0806
1402  # SNMP - 0x814C
1403  # IPv6 - 0x86DD ...
1404  # <Data> : payload in 0x00 0x00 format
1405  # <Length> : If needed the length of the frame can be padded with 0x00
1406  #
1407  # @example x.Frame.EtherII.Set 00-00-00-00-00-02 00-00-00-00-00-02 0x806 "0x00 0x00"
1408  #
1409  # Note that an Ethernet II frame has no length specified. If total length however exceeds
1410  # 1514 (MTU ethernet) then an error will be generated.
1411  ##
1412 
1413  set myContent [ list ]
1414  eval lappend myContent [ Mac.To.Hex $DMAC ] [ Mac.To.Hex $SMAC ]
1415  if { $type <= 0x5dc} {
1416  error "x.Frame.EtherII.Set: EtherType '$type' must be higher than 0x5dc"
1417  }
1418  eval lappend myContent [ Short.To.Hex $type ]
1419  eval lappend myContent $data ;# Ethernet frame without CRC
1420 
1421  if { [ string equal -nocase $length "auto" ]} {
1422  set myContent [ Frame.Pad $myContent 60 ]
1423  } else {
1424  if { [ string equal $length "" ] || ![ string is integer $length ] || $length < 0} {
1425  error "x.Frame.EtherII.Set: Invalid integer value length='$length'"
1426  }
1427  if { [ llength $myContent ] > $length} {
1428  error "x.Frame.EtherII.Set: Can't reduce length to '$length', data already exceeds this value <[ llength $myContent ]>"
1429  }
1430  set myContent [ Frame.Pad $myContent $length ]
1431  }
1432 
1433 # set MTU 1514 ;# Ethernet frame without CRC
1434 # if { [ llength $myContent ] > $MTU } {
1435 # error "x.Frame.EtherII.Set: MTU exceeded, length was '[ llength $myContent ]', maximum is ${MTU}"
1436 # }
1437 
1438  return $myContent
1439 
1440 }
1441 
1442 #**************************************************************************************************
1443 
1444 proc x.Frame.SNAP.Set { DMAC SMAC type data {length auto} } {
1445  # Sets the frame content to an ethernet SNAP frame (SubNetwork Access Protocol)
1446  # SNAP frames have DSAP and SSAP = 0xAA, Control is always 0x03
1447  #
1448  # <SMAC> <DMAC> : Mac address
1449  # <Type> : = EtherType
1450  # IPv4 - 0x0800
1451  # Arp - 0x0806
1452  # SNMP - 0x814C
1453  # IPv6 - 0x86DD ...
1454  # <Data> : payload in 0x00 0x00 format
1455  # <Length> : Auto : If no value is given then the SNAP frame will nicely encapsulate the <Data>
1456  # If total length exceeds maximum MTU (1518) then an error will be generated
1457  # x : Length will be adjusted to <x>.
1458  # If data exceeds <x> boundary then an error will be generated.
1459  # If data is smaller than <x> then data will be stuffed with 0x00
1460  #
1461  # eg: x.Frame.SNAP.Set 00-00-00-00-00-02 00-00-00-00-00-02 0x806 "0x00 0x00"
1462  # x.Frame.SNAP.Set 00-00-00-00-00-02 00-00-00-00-00-02 0x806 "0x00 0x00" 600
1463  ##
1464 
1465  set myContent [ list ]
1466  eval lappend myContent [ Mac.To.Hex $DMAC ] [ Mac.To.Hex $SMAC ]
1467  lappend myContent 0x00 0x00 ;# Needs to be replaced in the future if length is known
1468  lappend myContent 0xaa 0xaa 0x03 0x00 0x00 0x00
1469  eval lappend myContent [ Short.To.Hex $type ]
1470  eval lappend myContent $data ;# Ethernet frame without CRC
1471  set frameWithoutDataLength [ expr [ llength $myContent ] - 8 - [ llength $data ] ]
1472 
1473  if { [ string equal -nocase $length "auto" ]} {
1474  set myContent [ Frame.Pad $myContent 64 ]
1475  } else {
1476  if { [ string equal $length "" ] || ![ string is integer $length ] || $length < 0} {
1477  error "x.Frame.SNAP.Set: Invalid integer value length='$length'"
1478  }
1479  if { [ llength $myContent ] > $length} {
1480  error "x.Frame.SNAP.Set: Can't set Length to '$length', data already exceeds this value '[ llength $myContent ]'"
1481  }
1482  set myContent [ Frame.Pad $myContent $length ]
1483  }
1484 
1485  set finalLength [ expr ( [ llength $myContent ] - $frameWithoutDataLength ) ]
1486  set finalLengthHex [ Short.To.Hex $finalLength ]
1487  set myContent [ eval lreplace {$myContent} 12 13 $finalLengthHex ]
1488 
1489  set MTU 1514 ;# Ethernet frame without CRC
1490  if { $finalLength > $MTU - $frameWithoutDataLength} {
1491  error "x.Frame.802.3.Set: MTU exceeded, length was '[ expr $finalLength + $frameWithoutDataLength ]', maximum is ${MTU}"
1492  }
1493 
1494  return $myContent
1495 
1496 }
1497 
1498 #**************************************************************************************************
1499 
1500 proc x.Frame.802.3.Set { DMAC SMAC data {length auto} } {
1501  # Sets the frame content to an ethernet 802.3 encoded frame
1502  # Note that LLC header consists of the first 8 bytes of data
1503  # <SMAC> <DMAC> : Mac address
1504  # <Data> : payload in 0x00 0x00 format
1505  # <Length> : Auto : If no value is given then the SNAP frame will nicely encapsulate the <Data>
1506  # If total length exceeds maximum MTU (1518) then an error will be generated
1507  # x : Length will be adjusted to <x>.
1508  # If data exceeds <x> boundary then an error will be generated.
1509  # If data is smaller than <x> then data will be stuffed with 0x00
1510  #
1511  # eg: x.Frame.802.3.Set 00-00-00-00-00-02 00-00-00-00-00-02 "0x00 0x00"
1512  # x.Frame.802.3.Set 00-00-00-00-00-02 00-00-00-00-00-02 "0x00 0x00" 600
1513  ##
1514 
1515  set myContent [ list ]
1516  eval lappend myContent [ Mac.To.Hex $DMAC ] [ Mac.To.Hex $SMAC ]
1517  lappend myContent 0x00 0x00 ;# Needs to be replaced in the future if length is known
1518  eval lappend myContent $data ;# Ethernet frame without CRC
1519  set frameWithoutDataLength [ expr [ llength $myContent ] - [ llength $data ] ]
1520 
1521  if { [ string equal -nocase $length "auto" ]} {
1522  set myContent [ Frame.Pad $myContent 64 ]
1523  } else {
1524  if { [ string equal $length "" ] || ![ string is integer $length ] || $length < 0} {
1525  error "x.Frame.802.3.Set: Invalid integer value length='$length'"
1526  }
1527  if { [ llength $myContent ] > $length} {
1528  error "x.Frame.802.3.Set: Can't set Length to '$length', data already exceeds this value '[ llength $myContent ]'"
1529  }
1530  set myContent [ Frame.Pad $myContent $length ]
1531  }
1532 
1533  set finalLength [ expr ( [ llength $myContent ] - $frameWithoutDataLength ) ]
1534  set finalLengthHex [ Short.To.Hex $finalLength ]
1535  set myContent [ eval lreplace {$myContent} 12 13 $finalLengthHex ]
1536 
1537  set MTU 1514 ;# Ethernet frame without CRC
1538  if { $finalLength > $MTU - $frameWithoutDataLength} {
1539  error "x.Frame.802.3.Set: MTU exceeded, length was '[ expr $finalLength + $frameWithoutDataLength ]', maximum is ${MTU}"
1540  }
1541 
1542  return $myContent
1543 
1544 }
1545 
1546 #**************************************************************************************************
1547 
1548 proc x.Frame.DataOrLength.Parse { dataOrLength } {
1549  # Parse the dataOrLength Parameter and convert it to the real data.
1550  #
1551  # @param dataOrLength Data to return.
1552  # Can also specify a length if <dataOrLength> is a list
1553  # with '-length' as first element and an integer
1554  # specifying the datalength as a second element
1555  #
1556  # @return The data or the number of data bytes requested.
1557  #
1558  ##
1559 
1560  if { [ string equal -nocase [ lindex $dataOrLength 0 ] "-length" ]} {
1561  if { [ llength $dataOrLength ] != 2} {
1562  error "x.Frame.DataOrLength.Parse: dataOrLength MUST be '-length <integer length>' OR ByteList of data"
1563  }
1564  set data [list]
1565  set length [ lindex $dataOrLength 1 ]
1566 
1567  if { [ string equal $length "" ] || ![ string is integer $length ] || $length < 0} {
1568  error "x.Frame.DataOrLength.Parse: Invalid length '${length}'"
1569  }
1570  while { [ llength $data ] < $length} {
1571  lappend data 0xdd
1572  }
1573  } else {
1574  if { ![ ByteList.Check $dataOrLength ]} {
1575  error "x.Frame.DataOrLength.Parse: Invalid ByteList data == '${dataOrLength}"
1576  }
1577  set data [ string tolower $dataOrLength ]
1578  }
1579 
1580  return ${data}
1581 
1582 }
1583 
1584 #**************************************************************************************************
1585 
1586 }
1587 
1588 }