All Classes Namespaces Files Functions Pages
utils.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 provides <b>various generic helper methods</b> to ease Tcl development.
19 #
20 # It contains utilities such as data conversion methods, checksum calculation, IP validation, etc.
21 #
22 # @copyright Excentis nv - www.excentis.com
23 #
24 
25 proc ByteList.Check { byteList } {
26  # Method to check if the byteList is a valid list of hexadecimal bytes.
27  #
28  # @param byteList List of hexadecimal bytes 0xX, where X is 1 or 2 hexadecimal bytes.
29  #
30  # @return 1 for correct list of bytes,
31  # 0 for incorrect list of bytes
32  ##
33 
34  return [ regexp {^[ \t]*((0[xX][0-9a-fA-F]{1,2}[ \t]+)*)(0[xX][0-9a-fA-F]{1,2}){0,1}[ \t]*$} $byteList dummy s_stripped dummy1 dummy2 ]
35 
36 }
37 
38 proc Mac.To.Hex { macAddress } {
39  # Convert a MAC address to a ByteList of 6 bytes
40  #
41  # macAddress possible inputs:
42  # FF-FF-FF-FF-FF-FF
43  # FF:FF:FF:FF:FF:FF
44  # FF FF FF FF FF FF
45  # 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF
46  # 0xFF:0xFF:0xFF:0xFF:0xFF:0xFF
47  # 0xFF-0xFF-0xFF-0xFF-0xFF-0xFF
48  # 0xFF0xFF0xFF0xFF0xFF0xFF
49  # FFFFFFFFFFFF
50  # FFFF.FFFF.FFFF
51  # FFFF FFFF FFFF
52  # FFFF-FFFF-FFFF
53  # FFFFL:FFFF:FFFF
54  #
55  # @param macAddress: MAC address to be converted to the standard (in capitals or small letters)
56  #
57  # @return 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF ("0x", spaces and capitals)
58  ##
59 
60  # Remove leading/ending spaces
61  set macAddress [ string trim $macAddress ]
62 
63  if {[ string equal $macAddress "" ]} {
64  error "The given MAC address \"$macAddress\" is empty"
65  }
66 
67  if { [ catch {
68  if {[ regexp {^((([0-9a-fA-F]{2})[" ":-]([0-9a-fA-F]{2})[" ":-]([0-9a-fA-F]{2})[" ":-]([0-9a-fA-F]{2})[" ":-]([0-9a-fA-F]{2})[" ":-])(([0-9a-fA-F]{2})))$} $macAddress dummy1 dummy2 dummy3 a b c d e f ]} {
69  # FF-FF-FF-FF-FF-FF
70  # FF:FF:FF:FF:FF:FF
71  # FF FF FF FF FF FF
72  set macAddress ""
73  foreach letter { a b c d e } { append macAddress "0x[ subst $[ subst $letter ]] " }
74  append macAddress "0x$f"
75  } elseif {[ regexp {^(((0x[0-9a-fA-F]{2})[" ":-](0x[0-9a-fA-F]{2})[" ":-](0x[0-9a-fA-F]{2})[" ":-](0x[0-9a-fA-F]{2})[" ":-](0x[0-9a-fA-F]{2})[" ":-])((0x[0-9a-fA-F]{2})))$} $macAddress dummy1 dummy2 dummy3 a b c d e f ]} {
76  # 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF
77  # 0xFF:0xFF:0xFF:0xFF:0xFF:0xFF
78  # 0xFF-0xFF-0xFF-0xFF-0xFF-0xFF
79  set macAddress ""
80  foreach letter { a b c d e } { append macAddress "0x[ string range [ subst $[ subst $letter ]] 2 end ] " }
81  append macAddress "0x[ string range $f 2 end ]"
82  } elseif {[ regexp {^(0x[0-9a-fA-F]{2})(0x[0-9a-fA-F]{2})(0x[0-9a-fA-F]{2})(0x[0-9a-fA-F]{2})(0x[0-9a-fA-F]{2})(0x[0-9a-fA-F]{2})$} $macAddress dummy1 a b c d e f ]} {
83  # 0xFF0xFF0xFF0xFF0xFF0xFF
84  set macAddress ""
85  foreach letter { a b c d e } { append macAddress "0x[ string range [ subst $[ subst $letter ]] 2 end ] " }
86  append macAddress "0x[ string range $f 2 end ]"
87  } elseif {[ regexp {^([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})$} $macAddress dummy a b c d e f ]} {
88  # FFFFFFFFFFFF
89  set macAddress ""
90  foreach letter { a b c d e } { append macAddress "0x[ subst $[ subst $letter ]] " }
91  append macAddress "0x$f"
92  } elseif {[ regexp {^(([0-9a-fA-F]{2})([0-9a-fA-F]{2}))[" "-:.](([0-9a-fA-F]{2})([0-9a-fA-F]{2}))[" "-:.](([0-9a-fA-F]{2})([0-9a-fA-F]{2}))$} $macAddress dummy1 dummy2 a b dummy3 c d dummy4 e f ]} {
93  # FFFF.FFFF.FFFF
94  # FFFF FFFF FFFF
95  # FFFF-FFFF-FFFF
96  # FFFFL:FFFF:FFFF
97  set macAddress ""
98  foreach letter { a b c d e } { append macAddress "0x[ subst $[ subst $letter ]] " }
99  append macAddress "0x$f"
100  } else {
101  error "The given MAC address \"$macAddress\" is not in a valid format or contains wrong characters!\
102  \nAllowed formats are enumerated in the helpbox"
103  }
104  } ERROR ]
105  } {
106  #if an error occured at the regexp
107  error $ERROR
108  } else {
109  return $macAddress
110  }
111 }
112 
113 proc IPv4.IsValid { ip } {
114  # This Method checks if <ip> is a valid IPv4 address.
115  #
116  # @param ip
117  # The IPv4 address to check
118  #
119  # @return
120  # 1 for a valid IPv4 address, 0 for an invalid IPv4 address.
121  ##
122 
123  return [ regexp {^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$} $ip match ip1 ip2 ip3 ip4 ]
124 
125 }
126 
127 proc IPv4.IsNullIpAddress { ip } {
128  # Returns whether this address is the null IPv4 address 0.0.0.0/32
129  #
130  # @param ip
131  # The IPv4 address to check
132  #
133  # @return
134  # 1 for a null IPv4 address, 0 for an other or invalid IPv4 address.
135  ##
136 
137  return [ regexp {^([0]{1,3})\.([0]{1,3})\.([0]{1,3})\.([0]{1,3})$} $ip match ip1 ip2 ip3 ip4 ]
138 
139 }
140 
141 proc IPv4.IsThisNetwork { ip } {
142  # Returns whether the IPv4 address is in subnet 0.0.0.0/8
143  #
144  # @param ip
145  # The IPv4 address to check
146  #
147  # @return
148  # 1 for a "this network" IPv4 address, 0 for an other or invalid IPv4 address.
149  ##
150 
151  return [ regexp {^([0]{1,3})\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$} $ip match ip1 ip2 ip3 ip4 ]
152 
153 }
154 
155 proc IPv4.IsLoopback { ip } {
156  # Returns whether this address is in subnet 127.0.0.0/8
157  #
158  # @param ip
159  # The IPv4 address to check
160  #
161  # @return
162  # 1 for a loopback IPv4 address, 0 for an other or invalid IPv4 address.
163  ##
164 
165  return [ regexp {^(127)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$} $ip match ip1 ip2 ip3 ip4 ]
166 
167 }
168 
169 proc IPv4.IsMulticast { ip } {
170  # Returns whether this address is in subnet 224.0.0.0/4
171  #
172  # @param ip
173  # The IPv4 address to check
174  #
175  # @return
176  # 1 for a multicast IPv4 address, 0 for an other or invalid IPv4 address.
177  ##
178 
179  return [ expr [ regexp {^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$} $ip match ip1 ip2 ip3 ip4 ] && \
180  [ expr ( $ip1 & 0xf0 ) == 0xe0 ] ]
181 
182 }
183 
184 proc IPv4.IsBroadcast { ip { netmask "" } } {
185  # Check if the IPv4 address is a broadcast address 255.255.255.255/32
186  # or check if the IPv4 address is a broadcast address for the given subnet when @p netmask is given.
187  #
188  # @param ip
189  # The IPv4 address to check
190  #
191  # @param netmask
192  # Check if the IPv4Address is the given subnet's broadcast address.
193  #
194  # @return
195  # 1 for a broadcast IPv4 address, 0 for an other or invalid IPv4 address.
196  #
197  ##
198 
199  if { [ string equal "${netmask}" "" ]} {
200  return [ string equal "${ip}" "255.255.255.255" ]
201  }
202 
203  if { ! [ IPv4.IsValid "${ip}" ] || ! [ IPv4.IsValid "${netmask}" ]} {
204  return 0
205  }
206 
207  set ipBytes [ split "${ip}" "." ]
208  set netmaskBytes [ split "${netmask}" "." ]
209  foreach ipPart $ipBytes netmaskPart $netmaskBytes {
210  set broadcastPart [ expr ( ${ipPart} & ${netmaskPart} ) | ( ~ ${netmaskPart} & 0x0ff ) ]
211  if { $ipPart != $broadcastPart} {
212  return 0
213  }
214  }
215  return 1
216 
217 }
218 
219 proc IPv6.IsComplete { ip } {
220  # Check if the given IPv6 address is a complete format IPv6 address:
221  # X:X:X:X:X:X:X:X (X = 1 to 4 hexadecimal values)
222  #
223  # @param ip The IPv6 address to check
224  #
225  # @return 1 if the IPv6 address is in this format, 0 if not
226  ##
227 
228  set pattern_8hex {^([0-9A-Fa-f]{1,4}:){7}[0-9A-Fa-f]{1,4}$}
229  return [ regexp -- $pattern_8hex $ip ]
230 
231 }
232 
233 proc IPv6.IsCompressed { ip } {
234  # Check if the given IPv6 address is a compressed format IPv6 address:
235  # Compresses zeroes at one place in the address
236  # e.g. 'X:0:0:X:0:0:0:X' (X = 1 to 4 hexadecimal values)
237  # -> 'X::X:0:0:0:X' OR 'X:0:0:X::X'
238  #
239  # @param ip The IPv6 address to check
240  #
241  # @return 1 if the IPv6 address is in this format, 0 if not
242  #
243  # @apiexample
244  # @apisnippet{IPv6.IsCompressed "2001:db8:1:81:0:0:0:1"}
245  # @apisnippetretval{2001:db8:1:81::1}
246  # @endapiexample
247  ##
248 
249  # This one includes complete IPv6 address also
250  #set pattern_compressedhex {(^(([0-9A-Fa-f]{1,4}(((:[0-9A-Fa-f]{1,4}){5}::[0-9A-Fa-f]{1,4})|((:[0-9A-Fa-f]{1,4}){4}::[0-9A-Fa-f]{1,4}(:[0-9A-Fa-f]{1,4}){0,1})|((:[0-9A-Fa-f]{1,4}){3}::[0-9A-Fa-f]{1,4}(:[0-9A-Fa-f]{1,4}){0,2})|((:[0-9A-Fa-f]{1,4}){2}::[0-9A-Fa-f]{1,4}(:[0-9A-Fa-f]{1,4}){0,3})|(:[0-9A-Fa-f]{1,4}::[0-9A-Fa-f]{1,4}(:[0-9A-Fa-f]{1,4}){0,4})|(::[0-9A-Fa-f]{1,4}(:[0-9A-Fa-f]{1,4}){0,5})|(:[0-9A-Fa-f]{1,4}){7}))$|^(::[0-9A-Fa-f]{1,4}(:[0-9A-Fa-f]{1,4}){0,6})$)|^::$)}
251  set pattern_compressedhex {(^(([0-9A-Fa-f]{1,4}(((:[0-9A-Fa-f]{1,4}){5}::[0-9A-Fa-f]{1,4})|((:[0-9A-Fa-f]{1,4}){4}::[0-9A-Fa-f]{1,4}(:[0-9A-Fa-f]{1,4}){0,1})|((:[0-9A-Fa-f]{1,4}){3}::[0-9A-Fa-f]{1,4}(:[0-9A-Fa-f]{1,4}){0,2})|((:[0-9A-Fa-f]{1,4}){2}::[0-9A-Fa-f]{1,4}(:[0-9A-Fa-f]{1,4}){0,3})|(:[0-9A-Fa-f]{1,4}::[0-9A-Fa-f]{1,4}(:[0-9A-Fa-f]{1,4}){0,4})|(::[0-9A-Fa-f]{1,4}(:[0-9A-Fa-f]{1,4}){0,5})))$|^(::[0-9A-Fa-f]{1,4}(:[0-9A-Fa-f]{1,4}){0,6})$)|^::$)}
252  return [ regexp -- $pattern_compressedhex $ip ]
253 
254 }
255 
256 proc IPv6.Isv4Inv6 { ip } {
257  # Check if the given IPv6 address is the alternative IPv4 in IPv6 format:
258  # X:X:X:X:X:d.d.d.d (X = 1 to 4 hexadecimal values, d = decimal value 0-255)
259  # @param ip The IPv6 address to check
260  # @return 1 if the IPv6 address is in this format, 0 if not
261 
262  set pattern_hex6dec4 {^([0-9A-Fa-f]{1,4}):([0-9A-Fa-f]{1,4}):([0-9A-Fa-f]{1,4}):([0-9A-Fa-f]{1,4}):([0-9A-Fa-f]{1,4}):([0-9A-Fa-f]{1,4}):([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])[.]{1}([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])[.]{1}([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])[.]{1}([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])$}
263  return [ regexp -- $pattern_hex6dec4 $ip ]
264 
265 }
266 
267 proc IPv6.IsCompressedv4Inv6 { ip } {
268  # Check if the given IPv6 address is a complete format IPv6 address:
269  # X:X:X:X:X:X:X:X (X = 1 to 4 hexadecimal values)
270  #
271  # @param ip The IPv6 address to check
272  #
273  # @return 1 if the IPv6 address is in this format, 0 if not
274  ##
275 
276  set pattern_hex6dec4_compressed {^(([0-9A-Fa-f]{1,4}:)*)[:]?((:[0-9A-Fa-f]{1,4})*):([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])[.]{1}([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])[.]{1}([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])[.]{1}([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])$}
277  return [ regexp -- $pattern_hex6dec4_compressed $ip ]
278 
279 }
280 
281 proc IPv6.IsValid { ip } {
282  # This Method checks if <ip> is a valid IPv6 address.
283  #
284  # @param ip The IPv6 address to check
285  #
286  # @return 1 for a valid IPv6 address, 0 for an invalid IPv6 address.
287  ##
288 
289  return [ expr [ IPv6.IsComplete $ip ] ||\
290  [ IPv6.IsCompressed $ip ] ||\
291  [ IPv6.Isv4Inv6 $ip ] ||\
292  [ IPv6.IsCompressedv4Inv6 $ip ] ]
293 
294 }
295 
296 proc IPv6.IsMulticast { ip } {
297  return [ IPv6.IsValid $ip ] && \
298  [ regexp -- {^([Ff]{2}[0-9A-Fa-f]{2}):} $ip ]
299 }
300 
301 proc IP.IsValid { ip } {
302  # This Method checks if <ip> is a valid IPv4 address OR a valid IPv6 address.
303  #
304  # @param ip The IPv4/IPv6 address to check
305  #
306  # @return 1 for a valid IPv6 address OR a valid IPv6 address,
307  # 0 for an invalid IPv4/IPv6 address.
308  ##
309 
310  return [ expr [ IPv4.IsValid $ip ] ||\
311  [ IPv6.IsValid $ip ] ]
312 
313 }
314 
315 proc IP.To.Hex { ip } {
316  # convertor 10.11.0.2 -> 0x0a 0x0b 0x00 0x02
317  #
318  # @param ip The IPv4 address to convert
319  #
320  # @return IPv4 address as a ByteList of 4 bytes
321  ##
322 
323  if { ![ IPv4.IsValid $ip ]} {
324  error "IP.To.Hex: Invalid IPv4 Format: '$ip'"
325  }
326 
327  if { ![ regexp {^([0-9]*)\.([0-9]*)\.([0-9]*)\.([0-9]*)$} $ip dummy ip1 ip2 ip3 ip4 ]} {
328  error "IP.To.Hex: Invalid IPv4 Format: '$ip'"
329  }
330 
331  return [ list [ Byte.To.Hex $ip1 ] [ Byte.To.Hex $ip2 ] [ Byte.To.Hex $ip3 ] [ Byte.To.Hex $ip4 ] ]
332 
333 }
334 
335 proc Hex.To.IP { hex } {
336  # Convert a hexadecimal string of 4 bytes (8 hex values) to an IPv4 address
337  # XX:XX:XX:XX -> d.d.d.d
338  #
339  # @param hex hexadecimal representation of an IPv4 address will be converted into an IPv4 address
340  #
341  # @return IPv4 address, throws error on invalid <hex>
342  #
343  # @apiexample
344  # @apisnippet{01020304}
345  # @apisnippetretval{1.2.3.4}
346  # @apisnippet{Hex.To.IP 01:02:03:04}
347  # @apisnippetretval{1.2.3.4}
348  # @apisnippet{Hex.To.IP 01.02.03.04}
349  # @apisnippetretval{1.2.3.4}
350  # @apisnippet{Hex.To.IP 01-02-03-04}
351  # @apisnippetretval{1.2.3.4}
352  # @apisnippet{Hex.To.IP 01 02 03 04}
353  # @apisnippetretval{1.2.3.4}
354  # @apisnippet{Hex.To.IP 0x01 0x02 0x03 0x04}
355  # @apisnippetretval{1.2.3.4}
356  # @endapiexample
357  ##
358 
359  set hexMap [ string map {":" "" "." "" ";" "" "-" "" " " "" "0x" ""} $hex ]
360  if { [ string length $hexMap ] != 8 || [ string is xdigit $hexMap ] != 1} {
361  error "Hex.To.IP: Invalid hexadecimal IPv4 value : '${hex}'"
362  }
363 
364  set result ""
365  append result [ format "%d" 0x[ string range $hexMap 0 1 ] ] "." [ format "%d" 0x[ string range $hexMap 2 3 ] ] "."
366  append result [ format "%d" 0x[ string range $hexMap 4 5 ] ] "." [ format "%d" 0x[ string range $hexMap 6 7 ] ]
367 
368  return $result
369 
370 }
371 
372 proc Byte.To.Hex { byte } {
373  # converts the integer <byte> value to the hex notation 0xXX
374  # eg: Byte.To.Hex 2 -> 0x02
375  # Byte.To.Hex 0x8 -> 0x08
376  #
377  # @param byte Integer value, maximum 255 (0xFF)
378  #
379  # @return 0xXX
380  ##
381 
382  if { [ string equal $byte "" ] || ![ string is integer $byte ]} {
383  error "Byte.To.Hex: Invalid Byte format <$byte>"
384  }
385  if { $byte > 0xFF} {
386  error "Byte.To.Hex: Byte <$byte> greater than 255 (0xFF)"
387  }
388 
389  return [ format "0x%02x" [ expr ( $byte & 0xFF ) ] ]
390 
391 }
392 
393 proc Short.To.Hex { short } {
394  # converts the integer <short> value to the double hex notation 0xXX 0xXX list
395  # eg: Short.To.Hex 2 -> 0x00 0x02
396  # Short.To.Hex 800 -> 0x03 0x20
397  # Short.To.Hex 0x806 -> 0x08 0x06
398  #
399  # @param short Integer value, maximum 65535 (0xFFFF)
400  #
401  # @return 0xXX 0xXX
402  ##
403 
404  if { [ string equal $short "" ] || ![ string is integer $short ]} {
405  error "Short.To.Hex: Invalid Short format <$short>"
406  }
407  if { $short > 0xFFFF} {
408  error "Short.To.Hex: Short <$short> greater than 65535 (0xFFFF)"
409  }
410 
411  return [ list [ format "0x%02x" [ expr ( ( $short / 0x100 ) & 0xFF ) ] ] [ format "0x%02x" [ expr ( $short & 0xFF ) ] ] ]
412 
413 }
414 
415 proc CheckSum.16 { byteList } {
416  # This procedure calculates the 1's complement checksum for the listed ByteList
417  # The List is divided in k sections of 16 bits
418  # The 1's complement sum is made
419  # The result is complemented
420  # A list containing 2 elements checkSumH and checkSumL is returned
421  #
422  # CheckSum.16 "0x01 0x02 0x03 0x04 0x05 0x06 0x07 0x08" --> {0xef 0xeb}
423  #
424  # @param byteList The list of bytes to calculate the checksum from (multiple of 2 bytes!)
425  #
426  # @return the Checksum, ByteList of 2 bytes
427  ##
428 
429  if { ![ ByteList.Check $byteList ]} {
430  error "CheckSum.16 : Invalid ByteList, expecting list of 0xXX, where X is a hexadecimal value, got $byteList"
431  }
432 
433  set length [ llength $byteList ]
434  if { [ expr ( $length % 2 ) ]} {
435  error "CheckSum.16 : ByteList is expected to hold multiple of 2 elements"
436  }
437 
438  set twoByteList [ list ]
439  foreach {byte1 byte2} $byteList {
440  lappend twoByteList [ expr ( $byte1 * 0x100 + $byte2 ) ]
441  }
442  #puts $TwoByteList
443 
444  set checkSum 0
445  foreach twoByte $twoByteList {
446  incr checkSum $twoByte
447  if { $checkSum > 0xFFFF} {
448  set checkSum [ expr ( $checkSum & 0xFFFF ) ]
449  incr checkSum
450  }
451  }
452  #puts "0x[ format "%x" $checkSum ]"
453 
454  set checkSum [ expr ( 0xFFFF - $checkSum ) ]
455  #puts "0x[ format "%x" $checkSum ]"
456 
457  return [ list [ format "0x%02x" [ expr ( $checkSum / 0x100 ) & 0xFF ] ] [ format "0x%02x" [ expr ( $checkSum & 0xFF ) ] ] ]
458 
459 }
460 
461 proc IPv6.To.Hex { ip } {
462  # Converts an ipv6 address in colon notation to the hexadecimal notation 0xXX 0xXX ..
463  #
464  # @param ip The IPv6 address to convert
465  #
466  # @return IPv6 address as a ByteList (16 bytes)
467  ##
468 
469  set octets [ split $ip ":" ]
470  set result [ list ]
471  if { [ IPv6.IsComplete $ip ]} {
472  foreach octet $octets {
473  set decOctet [ scan $octet "%x" ]
474  lappend result [ format "0x%02x" [ expr ( $decOctet / 0x100 ) & 0xFF ] ] [ format "0x%02x" [ expr ( $decOctet & 0xFF ) ] ]
475  }
476  } elseif { [ IPv6.IsCompressed $ip ]} {
477  set length [ llength $octets ]
478  for { set teller 0} { $teller < $length} { incr teller} {
479  set octet [ lindex $octets $teller ]
480  # Replace compressed octet
481  if { [ string equal $octet "" ]} {
482  if { $teller == 0 || $teller == ( $length - 1 )} {
483  # First or last octet
484  lappend result 0x00 0x00
485  } else {
486  # Calculate the number of missing 16 bit fields. Those are all 0x00 0x00.
487  for { set i $length} { $i < 9} { incr i} {
488  lappend result 0x00 0x00
489  }
490  }
491  } else {
492  # Calculate the number of leading zeros.
493  set decOctet [ scan $octet "%x" ]
494  lappend result [ format "0x%02x" [ expr ( $decOctet / 0x100 ) & 0xFF ] ] [ format "0x%02x" [ expr ( $decOctet & 0xFF ) ] ]
495  }
496  }
497  } else {
498  # Invalid ip address
499  error "IPv6.To.Hex: Unsupported IPv6 format: '$ip'"
500  }
501 
502  return $result
503 
504 }
505 
506 proc Hex.To.IPv6 { hex } {
507  # Convert a hexadecimal string of 16 bytes (32 hex values) to a Complete IPv6 address
508  # e.g. XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
509  # -> XXXX:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX
510  # e.g. 0xXX 0xXX 0xXX 0xXX 0xXX 0xXX 0xXX 0xXX 0xXX 0xXX 0xXX 0xXX 0xXX 0xXX 0xXX 0xXX
511  # -> XXXX:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX
512  #
513  # @param hex hexstring to convert to an IPv6 address
514  #
515  # @return IPv6 address, throws error on invalid <hex>
516  ##
517 
518  set hexMap [ string map { ":" "" "." "" ";" "" " " "" "0x" ""} $hex ]
519  if { [ string length $hexMap ] != 32 || [ string is xdigit $hexMap ] != 1} {
520  error "Hex.To.IPv6: Invalid hexadecimal IPv6 value : '${hex}'"
521  }
522  set result ""
523  append result [ string range $hexMap 0 3 ] ":" [string range $hexMap 4 7 ] ":"\
524  [ string range $hexMap 8 11 ] ":" [ string range $hexMap 12 15 ] ":" \
525  [ string range $hexMap 16 19 ] ":" [ string range $hexMap 20 23 ] ":" \
526  [ string range $hexMap 24 27 ] ":" [ string range $hexMap 28 31 ]
527 
528  return $result
529 
530 }
531 
532 proc IP.Increment { address { amount 1 } } {
533  # Method which will create a new IPv4 address based on the provided address.
534  # E.g. IP.Increment 10.0.0.1
535  # will return 10.0.0.2
536  #
537  # @param address The IPv4 address to increment
538  # @param amount The amount the IPv4 address will be incremented with
539  #
540  # @return IPv4 <address> incremented by 1, throws error on invalid <address>
541  ##
542 
543  if { ![ IPv4.IsValid $address ]} {
544  error "IP.To.Hex: Invalid IPv4 Format: '$address'"
545  }
546 
547  set ipInt 0
548  foreach integer [ split $address "." ] {
549  set ipInt [ expr $ipInt * 256 + $integer ]
550  }
551  incr ipInt $amount
552  set hexIp [ format %08x [ expr $ipInt & 0xFFFFFFFF ] ]
553  return [ Hex.To.IP $hexIp ]
554 
555 }
556 
557 proc IPv6.Increment { address { amount 1 } } {
558  # Method which will create a new IPv6 address based on the provided address.
559  # E.g. IPv6.Increment 2001::5
560  # will return 2001::6 ( but in full format )
561  #
562  # @param address The IPv6 address to increment
563  # @param amount The amount the IPv6 address will be incremented with
564  #
565  # @return IPv6 <address> incremented by 1, throws error when a conversion problem occurs
566  ##
567 
568  set ipInt 0
569  set hexList [ split $address ":" ]
570 
571  if { ![ IPv6.IsValid $address ]} {
572  error "IPv6.Increment: Invalid IPv6 Format: '$address'"
573  }
574 
575  # If the IPv6 address is in compressed format,
576  # we will expand it to the full format
577  # If we have an empty element, replace it with the necessary zeros.
578  if { [ set lpos [ lsearch $hexList {} ] ] != -1} {
579  set zeros [ expr 9 - [ llength $hexList ] ]
580  for { set i 0} { $i < $zeros} { incr i} {
581  lappend zeroList 0000
582  }
583  set hexList [ eval lreplace [ list $hexList ] $lpos $lpos $zeroList ]
584  }
585 
586  if { [ llength $hexList ] != 8} {
587  error "IPv6.Increment: Error during convertion, did not receive 8 parts after decompressing address"
588  }
589 
590  # Walk the 16 byte parts, increment, and update the hex result.
591  set hexResults [ list ]
592  set carry $amount
593  for { set i 7} { $i >= 0} { incr i -1} {
594  set decPart [ expr [ scan [ lindex $hexList $i ] "%x" ] + $carry ]
595  #set decList [ lreplace $decList $i $i [ expr $decPart & 0xFFFF ] ]
596  set hexResults [ linsert $hexResults 0 [ format "%04x" [ expr $decPart & 0xFFFF ] ] ]
597  set carry [ expr ( $decPart >> 16 ) & 0xFFFF ]
598  }
599  return [ Hex.To.IPv6 [ join $hexResults "" ] ]
600 
601 }
602 
603 proc Mac.Increment { address } {
604  # Method which will create a new Mac address based on the provided address.
605  # E.g. Mac.Increment 00:ff:0a:00:00:01
606  # will return 00:ff:0a:00:00:02
607  #
608  # @param address The MAC address to increment
609  #
610  # @return MAC <address> incremented by 1, throws error on invalid <address>
611  ##
612 
613  # if { ![ regexp {^((([0-9a-fA-F]{1,2})[" ":-]([0-9a-fA-F]{1,2})[" ":-]([0-9a-fA-F]{1,2})[" ":-]([0-9a-fA-F]{1,2})[" ":-]([0-9a-fA-F]{1,2})[" ":-])(([0-9a-fA-F]{1,2})))$} $address dummy1 dummy2 dummy3 a b c d e f ] } {
614  # error "Mac.Increment: Invalid MAC address format"
615  # }
616  # set hexInt 0x[ format "%02x%02x%02x%02x" [ scan $c "%x" ] [ scan $d "%x" ] [ scan $e "%x" ] [ scan $f "%x" ] ]
617  # set macPrefix [ format "%02x:%02x" [ scan $a "%x" ] [ scan $b "%x" ] ]
618  # incr hexInt
619  # set hexString [ format %08x $hexInt ]
620  #
621  # return [ format "%s:%s:%s:%s:%s" $macPrefix [ string range $hexString end-7 end-6 ] [ string range $hexString end-5 end-4 ] [ string range $hexString end-3 end-2 ] [ string range $hexString end-1 end ] ]
622 
623  if { ![ regexp {^((([0-9a-fA-F]{1,2})[" ":-]([0-9a-fA-F]{1,2})[" ":-]([0-9a-fA-F]{1,2})[" ":-]([0-9a-fA-F]{1,2})[" ":-]([0-9a-fA-F]{1,2})[" ":-])(([0-9a-fA-F]{1,2})))$} $address dummy1 dummy2 dummy3 a b c d e f ]} {
624  error "Mac.Increment: Invalid MAC address format"
625  }
626  set hexInt1 0x[ format "%02x%02x%02x" [ scan $a "%x" ] [ scan $b "%x" ] [ scan $c "%x" ] ]
627  set hexInt2 0x[ format "%02x%02x%02x" [ scan $d "%x" ] [ scan $e "%x" ] [ scan $f "%x" ] ]
628  incr hexInt2
629  if { $hexInt2 > 0xFFFFFF} {
630  incr hexInt1
631  }
632  set hexString [ format "%06x%06x" [ expr $hexInt1 & 0xFFFFFF ] [ expr $hexInt2 & 0xFFFFFF ] ]
633 
634  return [ format "%s:%s:%s:%s:%s:%s" [ string range $hexString 0 1 ] [ string range $hexString 2 3 ] [ string range $hexString 4 5 ] [ string range $hexString 6 7 ] [ string range $hexString 8 9 ] [ string range $hexString 10 11 ] ]
635 
636 }
637 
638 proc Multicast.IP.To.Mac { multicastAddress } {
639  # Returns the multicast destination MAC Address for a given multicast IP Address
640  # mac = (01-00-5E) - (3 last bytes of the multicastAddress & 0x7FFFFF)
641  #
642  # @param multicastAddress The multicast group IP Address
643  #
644  # @return The corresponding multicast destination MAC Address
645  ##
646 
647  set ipList [ IP.To.Hex $multicastAddress ]
648 
649  set macList [ list "01" "00" "5E" ]
650  lappend macList [ format "%02X" [ expr [ lindex $ipList 1 ] & 0x7F ] ]
651  lappend macList [ format "%02X" [ lindex $ipList 2 ] ]
652  lappend macList [ format "%02X" [ lindex $ipList 3 ] ]
653 
654  set multicastMac [ string toupper [ join $macList ":" ] ]
655 
656  return $multicastMac
657 
658 }
659 
660 proc Multicast.IPv6.To.Mac { ipv6MulticastAddress } {
661  # Returns the multicast destination MAC Address for a given multicast IPv6 Address
662  # IPv6 : FF....:XXXX:XXXX
663  # |
664  # V
665  # MAC : 33:33:XX:XX:XX:XX (uses '33:33:' + 32 last bits of the ipv6MulticastAddress)
666  #
667  # @param ipv6MulticastAddress The IPv6 Multicast Address
668  #
669  # @return The corresponding multicast destination MAC Address
670  ##
671 
672  set ipList [ IPv6.To.Hex $ipv6MulticastAddress ]
673 
674  set macList [ list "33" "33" ]
675  lappend macList [ format "%02X" [ lindex $ipList end-3 ] ]
676  lappend macList [ format "%02X" [ lindex $ipList end-2 ] ]
677  lappend macList [ format "%02X" [ lindex $ipList end-1 ] ]
678  lappend macList [ format "%02X" [ lindex $ipList end ] ]
679 
680  set multicastMac [ string toupper [ join $macList ":" ] ]
681 
682  return $multicastMac
683 
684 }
685 
686 proc String.To.Hex { byteString } {
687  # converts a continuous string of bytes in hex format <byteString> to a list of bytes in hex notation 0xXX
688  # eg: String.To.Hex 00A12BCC -> 0x00 0xA1 0x2B 0xCC
689  #
690  # @param byteString Hex bytestring of any size
691  #
692  # @return The list of bytes in hex notation
693  ##
694 
695  if { [ expr [ string length $byteString ] % 2 ] != 0} {
696  error "String.To.Hex: Invalid string format, need even number of characters <$byteString>"
697  }
698 
699  set byteString [ string toupper $byteString ]
700  set result [ list ]
701  foreach {1 2} [ split $byteString {} ] { lappend result "0x$1$2"}
702  return $result
703 }
704 
705 
706 proc Hex.To.String { byteList } {
707  # converts a list of bytes in hex notation 0xXX <byteList> to a continuous string of bytes in hex format
708  # eg: Hex.To.String 0x12 0x4 0x0b 0x0C -> 12040B0C
709  #
710  # @param byteList The list of bytes in hex notation of any size
711  #
712  # @return Continuous string of bytes in hex format
713  ##
714 
715  set formattedByteList [ list]
716  foreach value $byteList { lappend formattedByteList [ format "%02X" $value ]}
717  return [ join $formattedByteList "" ]
718 }
719 
720 
721 }
722 
723 }