All Classes Namespaces Files Functions Pages
NAT.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 This namespace holds all ByteBlower-related Tcl extensions, including the ByteBlower higher-layer Tcl API.
8 #
9 # These procedures are developed for higher-level usage of the ByteBlower API.
10 # They are mainly used to simplify running specific Test Scenarios similar to
11 # scenarios you know from the ByteBlower GUI.
12 #
13 namespace eval ByteBlower {
14 
15 ## \file
16 #
17 # @brief Utility module that provides functionality for <b>NAT scenarios</b>, such as detecting the NAT public IP and public port number.
18 #
19 # @copyright Excentis nv - www.excentis.com
20 #
21 package require excentis_basic
22 
23 ##
24 # To find out which ip address and port is used by the nat device, we first send
25 # some packets from the private to the public side
26 #
27 # The used discovery packet size is 256 Bytes.
28 #
29 # @param publicPort
30 # ByteBlower Port connected to the public side of the NAT device
31 #
32 # @param nattedPort
33 # ByteBlower Port connected to the private side of the NAT device
34 #
35 # @param natUdpPort
36 # is the UDP SOURCE port for the nattedPort.
37 #
38 # @param publicUdpPort
39 # is the UDP SOURCE port for the publicPort.
40 #
41 # @param publicIpAddress
42 # is the public IP address to use instead of the public IP address that is configured at the publicPort.
43 #
44 # @return
45 # List of:
46 # 1. NAT public IP address
47 # 2. NAT public UDP port
48 proc NatDevice.IP.Get { publicPort nattedPort publicUdpPort natUdpPort { publicIpAddress null } } {
49 
50  if { [ $publicPort Info -implements WirelessEndpoint ]} {
51  error "Public port cannot be a WirelessEndpoint!"
52  }
53 
54  # Is our natted port a Wireless Endpoint?
55  set nattedPortIsWE [ $nattedPort Info -implements WirelessEndpoint ]
56 
57  # Collect generic information:
58  set publicportIP [ $publicPort Layer3.IPv4.Get ]
59  set publicportL25List [ $publicPort Layer2_5.Vlan.Get ]
60  if { [ string equal $publicIpAddress "null" ]} {
61  set publicIpAddress [ $publicportIP Ip.Get ]
62  }
63 
64  if { ! $nattedPortIsWE} {
65  set nattedPortL3 [ $nattedPort Layer3.IPv4.Get ]
66  set nattedPortIP [ $nattedPortL3 Ip.Get ]
67  set nattedPortL2 [ $nattedPort Layer2.EthII.Get ]
68  set nattedPortMac [ $nattedPortL2 Mac.Get ]
69  set nattedPortL25List [ $nattedPort Layer2_5.Vlan.Get ]
70  } else {
71  set nattedPortIP [ [ [ $nattedPort Device.Info.Get ] Network.Info.Get ] IPv4.Get ]
72  }
73 
74  # Configure stream
75  set stream [ $nattedPort Tx.Stream.Add ]
76  $stream InterFrameGap.Set 10ms
77  $stream NumberOfFrames.Set 100
78 
79  set txFrame [ $stream Frame.Add ]
80 
81  if { ! $nattedPortIsWE} {
82  # send arp to find mac address of nat device
83  set mac [ $nattedPortL3 Protocol.Arp [ $publicportIP Ip.Get ] ]
84 
85  puts "== NATTED PORT DEST MAC : $mac =="
86 
87  # configure frame
88  set srcFrame [ ::excentis::basic::Frame.Udp.Set $mac $nattedPortMac $publicIpAddress $nattedPortIP $publicUdpPort $natUdpPort { -Length 256 } ]
89  for { set i [expr [llength $nattedPortL25List] - 1]} { $i >= 0} { incr i -1} {
90  set nattedPortL25 [lindex $nattedPortL25List $i]
91  set srcFrame [::excentis::basic::Frame.Vlan.Insert $srcFrame [ $nattedPortL25 ID.Get ] [ $nattedPortL25 Priority.Get ] [ $nattedPortL25 DropEligible.Get ]]
92  }
93  $txFrame Bytes.Set $srcFrame
94  } else {
95  # Much easier for wireless endpoints, since these do not support Full frames and VLAN tagging
96  $stream Destination.Address.Set $publicIpAddress
97  $stream Destination.Port.Set $publicUdpPort
98  $stream Source.Port.Set $natUdpPort
99  $txFrame Payload.Set [ string repeat "AA" [ expr 256 - 42 ] ]
100  }
101 
102  # capture packet at receive port
103  set capture [ $publicPort Rx.Capture.Basic.Add ]
104  set publicportVlanCount 0
105  set vlanFilters [ list ]
106  foreach publicportL25 $publicportL25List {
107  incr publicportVlanCount
108  set vlanId [ $publicportL25 ID.Get ]
109  lappend vlanFilters "vlan $vlanId"
110  }
111 
112  if { $publicportVlanCount > 0} {
113  set vlanFilter [ join $vlanFilters " and " ]
114  set filter "$vlanFilter and dst host $publicIpAddress and udp"
115  } else {
116  set filter "dst host $publicIpAddress and udp"
117  }
118 
119  $capture Filter.Set $filter
120  $capture Start
121 
122  # start stream
123  if { $nattedPortIsWE} {
124  $nattedPort Lock 1
125  $nattedPort Prepare
126  set ts [ $nattedPort Start ]
127  set mpTs [ [ $nattedPort Parent.Get ] Timestamp.Get ]
128  set timeToWait [ expr int((double($ts) - double($mpTs)) / 1000000) ]
129  after $timeToWait
130  } else {
131  $nattedPort Start
132  }
133  after 1000
134 
135  # stop stream - should have stopped by itself already.
136  if { ! $nattedPortIsWE} {
137  $nattedPort Stop
138  }
139 
140  # stop capture
141  $capture Stop
142 
143  if { $nattedPortIsWE} {
144  $nattedPort Result.Clear
145  $nattedPort Lock 0
146  }
147 
148  set captureResult [ $capture Result.Get ]
149  set capturedFrames [ $captureResult Frames.Get ]
150  if { [ llength $capturedFrames ] == 0} {
151  # Delete the stream and the capture
152  $capture Destructor
153  $stream Destructor
154  error "NAT failed, no packets received on public port for NATted port $nattedPort"
155  }
156  set capturedFrame [ lindex $capturedFrames 0 ]
157  set bytes [ $capturedFrame Bytes.Get ]
158 
159  # get source ip and udp port from captured packet
160  # Layer 2 decoding
161  # -- decoding LENGTH/TYPE field
162  set lentype [ string range $bytes 24 27 ]
163  scan $lentype "%x" lentypeDec
164  if { $lentypeDec <= 1500} {
165  set ethernetLength $lentypeDec
166  set ethernetPayload [ string range $bytes 44 [ expr 44 + $lentypeDec * 2 - 1 ] ]
167  } else {
168  set ethernetType $lentype
169  set ethernetPayload [ string range $bytes 28 end ]
170  }
171  # Layer 2.5 decoding
172  set ethernetPayload [ string range $ethernetPayload [expr 2 * 4 * $publicportVlanCount] end ]
173  # Layer 3 decoding
174  scan [ string range $ethernetPayload 0 1 ] "%x" temp
175  set ipIhl [ expr $temp & 15 ]
176 
177  # -- decoding Total Length
178  scan [ string range $ethernetPayload 4 7 ] "%x" ipLength
179  # -- decoding IP Source Address
180  set ipSa [ string range $ethernetPayload 24 31 ]
181  set ipPayload [ string range $ethernetPayload [ expr $ipIhl * 4 * 2 ] [ expr $ipLength * 2 - 1 ] ]
182  # Layer 4 decoding
183  # -- decoding Source Port
184  scan [ string range $ipPayload 0 3 ] "%x" udpSp
185 
186  set natPublicIP [ ::excentis::basic::Hex.To.IP $ipSa ]
187  set natPublicPort $udpSp
188 
189  # clean up streams/triggers/captures
190  unset capturedFrames
191  $capture Destructor
192  $stream Destructor
193 
194  return [ list $natPublicIP $natPublicPort ]
195 }
196 
197 
198 }
199 
200 }