All Classes Namespaces Files Functions Pages
tcp-ack-suppression.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 Core module that provides testing for TCP ack suppression scenarios.
18 #
19 # @copyright Excentis nv - www.excentis.com
20 #
21 
22 proc TcpAckSuppression { args } {
23  # This procedure will perform the TCP Ack Suppression test.
24  # 1 ByteBlower port is given as a HTTP server and 1 or multiple ByteBlower ports
25  # can be given as HTTP Clients.
26  #
27  # The HTTP Server and Clients will be automtically configured.
28  # The test itself consist of starting the HTTP Server and Let all Clients simultaneouly
29  # request a page for the given page size.
30  # After all requests are finished, the number of TCP ACKs for each Client are counted
31  # on the Client and Server and the nrOfSuppressed ACKs is calculated.
32  #
33  # A summary is printed for every Client.
34  #
35  # @param args
36  # @option -tx
37  # @desctiption Options for the transmitting port (server port).
38  # @note Only one transmitting port can be used in this procedure.
39  # The Tx port configuration consists of a ByteBlower port Object
40  # and the TCP port for the server.
41  # @note The port Object MUST be Layer2 and Layer3 configured.
42  # -port <serverPortObject> -portNumber <tcpPortNumber>
43  #
44  # @option -rx
45  # @desctiption Options for the receiving port (client port).
46  # Multiple receiving ports can be used in this procedure.
47  # The Rx port configuration consists of a ByteBlower port Object,
48  # the TCP port for the client and (optional) the requestSize.
49  # This last is the page size (in bits) the HTTP request will request
50  # for, the default value is 1000000 (1Mb).
51  # @note The port Object MUST be Layer2 and Layer3 configured.
52  # -port <clientPortObject> -portNumber <tcpPortNumber> -requestSize <nrOfBytes>
53  #
54  # @return
55  # @description List of resultInfo as returned from @see.proc getHttpClientResults for each Client.
56 
57  set resultInfoList [ list ]
58 
59  # server object + server listen port
60  set serverPortInfoList [ list ]
61  set nrOfServerPorts -1
62  # list of client objects + client source TCP port
63  set clientPortInfoList [ list ]
64  set nrOfClientPorts -1
65 
66  set httpServerInfo [ list ]
67  set httpClientInfoList [ list ]
68 
69  #- Parse arguments
70  set portInfoList [ eval parseTcpAckSuppressionParams $args ]
71  set serverPortInfoList [ lindex $portInfoList 0 ]
72  set clientPortInfoList [ lindex $portInfoList 1 ]
73 
74  if { [ set nrOfServerPorts [ llength $serverPortInfoList ] ] == 0 || [ set nrOfClientPorts [ llength $clientPortInfoList ] ] == 0} {
75  error "Please provide at least one option -rx and one option -tx"
76  }
77  puts "Performing ACK Suppression Test on $nrOfServerPorts HTTP Servers and $nrOfClientPorts HTTP Clients"
78 
79  #- Initialize HTTP Server
80  puts "Initializing HTTP Server"
81  set serverPortInfo [ lindex $serverPortInfoList 0 ]
82  set httpServerInfo [ initializeHttpServer $serverPortInfo ]
83 
84  #- Initialize HTTP Clients
85  set httpClientNr 0
86  foreach clientPortInfo $clientPortInfoList {
87  puts "Initializing HTTP Client [ incr httpClientNr ]"
88  lappend httpClientInfoList [ initializeHttpClient $clientPortInfo $serverPortInfo ]
89  }
90 
91  #- Start HTTP Server
92  puts "Starting HTTP Server"
93  startHttpServer $httpServerInfo
94 
95  #- Start HTTP Clients
96  set httpClientNr 0
97  foreach httpClientInfo $httpClientInfoList {
98  puts "Starting HTTP Client [ incr httpClientNr ]"
99  startHttpClient $httpClientInfo
100  }
101 
102  #- Wait for HTTP Clients to finish
103  set httpClientNr 0
104  foreach httpClientInfo $httpClientInfoList {
105  puts "Waiting for HTTP Client [ incr httpClientNr ] to finish..."
106  set httpClientStatus [ waitForHttpClientFinish $httpClientInfo ]
107  puts "Current Status $httpClientStatus"
108  set httpClientStatus [ stopHttpClient $httpClientInfo $httpClientNr ]
109  puts "Final Status : $httpClientStatus"
110  }
111 
112  #- Start HTTP Clients - 2
113  #set httpClientNr 0
114  #foreach httpClientInfo $httpClientInfoList {
115  # puts "Starting HTTP Client [ incr httpClientNr ]"
116  # startHttpClient $httpClientInfo
117  #}
118 
119  #- Wait for HTTP Clients to finish - 2
120  #set httpClientNr 0
121  #foreach httpClientInfo $httpClientInfoList {
122  # puts "Waiting for HTTP Client [ incr httpClientNr ] to finish..."
123  # set httpClientStatus [ waitForHttpClientFinish $httpClientInfo ]
124  # set httpClientStatus [ stopHttpClient $httpClientInfo $httpClientNr ]
125  # puts "Final Status : $httpClientStatus"
126  #}
127 
128 
129  #- Getting HTTP Server Results ---
130  #- WARNING this can slow the code down
131  #puts "Getting HTTP Server 1 results"
132  #puts [ [ lindex $serverPortInfo 0 ] Description.Get ]
133 
134  #- Getting HTTP Client Results
135  set httpClientNr 0
136  foreach httpClientInfo $httpClientInfoList {
137  puts "Getting HTTP Client [ incr httpClientNr ] results"
138 
139  # WARNING: This can slow the code down since
140  # the capture description will contain the captured frames
141  # puts [ [ lindex $clientPortInfo 0 ] Description.Get ]
142 
143  set resultInfo [ getHttpClientResults $httpClientInfo $httpServerInfo ]
144 
145  set statusInfo [ lindex $resultInfo 0 ]
146  set tcpAckInfo [ lindex $resultInfo 1 ]
147 
148  if { [ lindex $statusInfo 0 ] == 1} {
149  set statusIoChannel stdout
150  } else {
151  set statusIoChannel stderr
152  }
153 
154  if { [ lindex $statusInfo 0 ] == 1} {
155  set tcpAckIoChannel stdout
156  } else {
157  set tcpAckIoChannel stderr
158  }
159 
160  puts $statusIoChannel "Final Client Status : [ lindex $statusInfo 1 ]"
161  puts $statusIoChannel "Received bytes : [ lindex $statusInfo 2 ]"
162  puts $tcpAckIoChannel "Transmitted TCP ACKs : [ lindex $tcpAckInfo 1 ]"
163  puts $tcpAckIoChannel "Received TCP ACKs : [ lindex $tcpAckInfo 2 ]"
164  puts $tcpAckIoChannel "Suppressed TCP ACKs : [ lindex $tcpAckInfo 3 ]"
165 
166  lappend resultInfoList $resultInfo
167 
168  }
169  #- Stopping HTTP Server
170  stopHttpServer $httpServerInfo
171 
172  return $resultInfoList
173 
174 } ;# proc TcpAckSuppression
175 
176 proc parseTcpAckSuppressionParams { args } {
177  # See also @see.proc TcpAckSuppression for more information about the params
178  #
179  # @return
180  # @type list { list{<serverPortInfo> ...} list {<clientPortInfo> ...} }
181 
182  set serverPortInfoList [ list ]
183  set clientPortInfoList [ list ]
184 
185  if { [ llength $args ] == 0 || [ llength $args ] % 2 != 0} {
186  # TODO: Convert this to TCL Throw...
187  error "Invalid number of arguments"
188  }
189  foreach {option value} $args {
190  set portObject null
191  set portNumber null
192  set requestSize 1000000 ;# 1MB
193  switch -- $option {
194  -rx {
195  set addCommand {lappend clientPortInfoList [ list $portObject $portNumber $requestSize ]}
196  }
197  -tx {
198  if { [ llength $serverPortInfoList ] > 0 } {
199  error "Only one server is allowed."
200  }
201  #if { [ llength $value ] == 0 || [ llength $value ] % 2 != 0 } {
202  # # TODO: Convert this to TCL Throw...
203  # error "Invalid number of option <$option> arguments"
204  #}
205  set addCommand {lappend serverPortInfoList [ list $portObject $portNumber ]}
206  }
207  default {
208  error "Unknown option <$option> = <$value>"
209  }
210  }
211  if { [ llength $value ] == 0 || [ llength $value ] % 2 != 0} {
212  # TODO: Convert this to TCL Throw...
213  error "Invalid number of option <$option> arguments"
214  }
215  foreach {option2 value2} $value {
216  switch -- $option2 {
217  -port {
218  set portObject $value2
219  }
220  -portNumber {
221  set portNumber $value2
222  }
223  -requestSize {
224  if { [ string equal $option "-tx" ] } {
225  error "Unknown option <$option> value argument <$option2> = <$value2>"
226  }
227  set requestSize $value2
228  }
229  default {
230  error "Unknown option <$option> value argument <$option2> = <$value2>"
231  }
232  }
233  }
234  if { [ string equal $portObject "null" ] || [ string equal $portNumber "null" ]} {
235  error "Please provide -port AND -portNumber to option <$option>"
236  }
237  eval $addCommand
238  }
239 
240  return [ list $serverPortInfoList $clientPortInfoList ]
241 }
242 
243 proc initializeHttpServer { serverPortInfo } {
244  # Initialize the HTTP Server.
245  #
246  # @param serverPortInfo
247  # @description Port info as returned from @see.proc parseTcpAckSuppressionParams
248  # List of ByteBlower Port and TCP port for the HTTP server.
249  #
250  # @return list {<httpServerObject>}
251 
252  set httpServer {}
253 
254  set serverPortObject [ lindex $serverPortInfo 0 ]
255  set serverTcpPort [ lindex $serverPortInfo 1 ]
256 
257  puts stderr "Server Port $serverPortObject"
258 
259  #- Check current Servers
260  set currentHttpServerList [ $serverPortObject Protocol.Http.Server.Get ]
261  foreach currentHttpServer $currentHttpServerList {
262  if { [ $currentHttpServer Port.Get ] == $serverTcpPort} {
263  set httpServer $currentHttpServer
264  puts "Initializing EXISTING HTTP SERVER : Listen Port == $serverTcpPort"
265  #puts stderr "Using Previous HTTP Server $httpServer"
266  break;
267  }
268  }
269  if { $httpServer == {}} {
270  puts "Initializing NEW HTTP SERVER : Listen Port == $serverTcpPort"
271  set httpServer [ $serverPortObject Protocol.Http.Server.Add ]
272  #puts stderr "Added New HTTP Server $httpServer"
273  $httpServer Port.Set $serverTcpPort
274  }
275 
276  puts stderr "HTTP Server Port $httpServer"
277 
278  return [ list $httpServer ]
279 
280 }
281 
282 proc initializeHttpClient { clientPortInfo serverPortInfo } {
283  # Initialize the HTTP Client.
284  #
285  # @param clientPortInfo
286  # @description Client Port info as returned from @see.proc parseTcpAckSuppressionParams
287  # List of ByteBlower Port, TCP port for the HTTP client and request size for the HTTP Client.
288  # @param serverPortInfo
289  # @description Server Port info as returned from @see.proc parseTcpAckSuppressionParams
290  # List of ByteBlower Port and TCP port for the HTTP server.
291  #
292  # @return
293  # list {<httpClient> <requestSize> <clientCapture> <serverCapture>}
294 
295  set httpClientInfo [ list ]
296 
297  set httpClient {}
298 
299  set serverPortObject [ lindex $serverPortInfo 0 ]
300  # puts stderr "Server Port $serverPortObject"
301  set serverTcpPort [ lindex $serverPortInfo 1 ]
302 
303  #- Get HTTP Server Layer3 configuration
304  set serverLayer3 [ $serverPortObject Layer3.IPv4.Get ]
305  set serverIpAddress [ $serverLayer3 Ip.Get ]
306 
307  #- Initialize HTTP Clients
308  set clientPortObject [ lindex $clientPortInfo 0 ]
309  set clientTcpPort [ lindex $clientPortInfo 1 ]
310  set requestSize [ lindex $clientPortInfo 2 ]
311 
312  puts stderr "Client Port $clientPortObject"
313 
314  #- Get Layer3 configuration
315  set clientLayer3 [ $clientPortObject Layer3.IPv4.Get ]
316 
317  #- Check current HTTP Clients
318  set currentHttpClientList [ $clientPortObject Protocol.Http.Client.Get ]
319  foreach currentHttpClient $currentHttpClientList {
320  if { [ $currentHttpClient Local.Port.Get ] == $clientTcpPort &&
321  [ $currentHttpClient Remote.Address.Get ] == $serverIpAddress &&
322  [ $currentHttpClient Remote.Port.Get ] == $serverTcpPort} {
323  set httpClient $currentHttpClient
324  puts "Initializing EXISTING HTTP Client : Local Port == $clientTcpPort, Remote Address == $serverIpAddress, Remote Port == $serverTcpPort"
325 
326  #puts stderr "Using Previous HTTP Client $httpClient"
327  break;
328  }
329  }
330  if { $httpClient == {}} {
331  puts "Initializing NEW HTTP Client : Local Port == $clientTcpPort, Remote Address == $serverIpAddress, Remote Port == $serverTcpPort"
332  set httpClient [ $clientPortObject Protocol.Http.Client.Add ]
333  #puts stderr "Added New HTTP Client $httpClient"
334 
335  $httpClient Remote.Address.Set $serverIpAddress
336  $httpClient Remote.Port.Set $serverTcpPort
337  #$httpClient Remote.Port.Set 80
338  $httpClient Local.Port.Set $clientTcpPort
339  }
340 
341  puts stderr "HTTP Client Port $httpClient"
342 
343  #- Create Captures
344  set serverCapture [ $serverPortObject Rx.Capture.Basic.Add ]
345  set clientCapture [ $clientPortObject Rx.Capture.Basic.Add ]
346 
347  #- Set Capture Filter
348  $serverCapture Filter.Set "host [ $clientLayer3 Ip.Get ]"
349  $clientCapture Filter.Set "host [ $clientLayer3 Ip.Get ]"
350 
351  #lappend httpClientInfoList [ list $httpClient $requestSize ]
352  return [ list $httpClient $requestSize $clientCapture $serverCapture ]
353 
354 }
355 
356 proc startHttpServer { httpServerInfo } {
357  # Start the HTTP Server.
358  #
359  # @param httpServerInfo
360  # @description List as returned from @see.proc initializeHttpServer
361  #
362  # @return void
363 
364  set httpServer [ lindex $httpServerInfo 0 ]
365 
366  $httpServer Start
367 
368  return
369 
370 }
371 
372 proc startHttpClient { httpClientInfo } {
373  # Start the HTTP Client.
374  # Also starts capturing.
375  #
376  # @param httpClientInfo
377  # @description List as returned from @see.proc initializeHttpClient
378  #
379  # @return void
380 
381  set httpClient [ lindex $httpClientInfo 0 ]
382  set requestSize [ lindex $httpClientInfo 1 ]
383  set clientCapture [ lindex $httpClientInfo 2 ]
384  set serverCapture [ lindex $httpClientInfo 3 ]
385 
386  #- Start Captures
387  $serverCapture Start
388  $clientCapture Start
389 
390 
391 
392  #puts [ $httpClient Request.Page 1 ] ;# -> SERVER CRASH
393  $httpClient Request.Size.Set $requestSize
394  $httpClient Request.Start
395 
396  return
397 
398 }
399 
400 proc waitForHttpClientFinish { httpClientInfo } {
401  # Wait for the HTTP Client to Finish its request.
402  #
403  # @param httpClientInfo
404  # @description List as returned from @see.proc initializeHttpClient
405  #
406  # @return
407  # @description Final Client status
408 
409  set httpClient [ lindex $httpClientInfo 0 ]
410  #set requestSize [ lindex $httpClientInfo 1 ]
411  set clientCapture [ lindex $httpClientInfo 2 ]
412  set serverCapture [ lindex $httpClientInfo 3 ]
413 
414  set httpClientStatus "<UNKNOWN>"
415 
416  while { ![ $httpClient Finished.Get ]} {
417  set wait 0
418  after 1000 "set wait 1"
419  vwait wait
420  puts -nonewline "*"; flush stdout
421  unset wait
422  $httpClient Refresh
423  }
424  puts ""
425 
426  $httpClient Refresh
427  return [ $httpClient Request.Status.Get ]
428 
429 }
430 
431 proc stopHttpClient { httpClientInfo httpClientId } {
432  # Stop the HTTP Client.
433  # Also stops the capturing.
434  #
435  # @param httpClientInfo
436  # @description List as returned from @see.proc initializeHttpClient
437  # @param httpClientId
438  # @description An ID to add to the capture file name to uniquely
439  # identify the client captures.
440  #
441  # @return
442  # @description Final Client status
443 
444  set httpClient [ lindex $httpClientInfo 0 ]
445  set clientCapture [ lindex $httpClientInfo 2 ]
446  set serverCapture [ lindex $httpClientInfo 3 ]
447 
448  #- Stopping HTTP client may reset (possible) error status
449  #puts [ $httpClient Request.Stop ]
450 
451  set wait 0
452  after 200 "set wait 1"
453  vwait wait
454  unset wait
455 
456  puts "Stopping captures.."
457  $serverCapture Stop
458  $clientCapture Stop
459 
460  puts "Saving captures.."; flush stdout
461  puts "Saving captures: server"; flush stdout
462  [ $serverCapture Result.Get ] Pcap.Save "httpServer${httpClientId}.pcap"
463  puts "Saving captures: client"; flush stdout
464  [ $clientCapture Result.Get ] Pcap.Save "httpClient${httpClientId}.pcap"
465  puts "Saving captures: done"; flush stdout
466  return [ $httpClient Request.Status.Get ]
467 
468 }
469 
470 proc stopHttpServer { httpServerInfo } {
471  # Stop the HTTP Server.
472  #
473  # @param httpServerInfo
474  # @description List as returned from @see.proc initializeHttpServer
475  #
476  # @return void
477 
478  set httpServer [ lindex $httpServerInfo 0 ]
479 
480  $httpServer Stop
481 
482  return
483 
484 }
485 
486 proc getHttpClientResults { httpClientInfo httpServerInfo } {
487  # Get the Test results for the HTTP Client.
488  #
489  # @param httpClientInfo
490  # @description List as returned from @see.proc initializeHttpClient
491  #
492  # @return
493  # @description List of results from @see.proc parseHttpClientStatusResults and
494  # @see.proc parseHttpClientTcpAckResults
495  # @type list {<statusInfo> <tcpAckInfo>}
496 
497  set resultInfo [ list ]
498 
499  set httpServer [ lindex $httpServerInfo 0 ]
500  set httpClient [ lindex $httpClientInfo 0 ]
501  set requestSize [ lindex $httpClientInfo 1 ]
502 
503  set statusInfo [ parseHttpClientStatusResult $httpClient $requestSize ]
504  set tcpAckInfo [ parseHttpClientTcpAckResult $httpClient $httpServer ]
505 
506  lappend resultInfo $statusInfo
507  lappend resultInfo $tcpAckInfo
508 
509  return $resultInfo
510 
511 }
512 
513 proc parseHttpClientStatusResult { httpClient requestSize } {
514  # The status of the httpClient @see.param httpClient is processed.
515  # The received number of bits is compared with the requested number
516  # of bits @see.param requestSize
517  #
518  # @param httpClient
519  # @description The ByteBlower HTTP Client Object
520  # @param requestSize
521  # @description The requested number of bits.
522  #
523  # @return list {<passOrFail> <statusValue> <rxBytes>}
524 
525  set statusInfo [ list ]
526  set pf 1
527 
528  set httpClientSessionInfo [ $httpClient Http.Session.Info.Get ]
529 
530  set result [ $httpClientSessionInfo Result.Get ]
531  $result Refresh
532  set rxBytes [ $result Rx.ByteCount.Total.Get ]
533 
534  set requestStatus [ $httpClient Request.Status.Get ]
535  if { ![ string equal $requestStatus "finished" ] && ! [ string equal $requestStatus "stopped" ]} {
536  puts stderr "The Client is not in a <finished> state"
537  set pf 0
538  }
539 
540  if { $rxBytes != $requestSize} {
541  puts stderr "The Number of received bits is not the same as the number of requested bits."
542  set pf 0
543  }
544 
545  lappend statusInfo $pf
546  lappend statusInfo $requestStatus
547  lappend statusInfo $rxBytes
548 
549  return $statusInfo
550 
551 }
552 
553 proc parseHttpClientTcpAckResult { httpClient httpServer } {
554  # The HTTP Client and Server http Client and Server are used to check the
555  # number of transmitted and received TCP ACKs
556  #
557  # @param httpClient
558  # @description The HTTP Client Object.
559  # @param httpServer
560  # @description The HTTP Server Object.
561  #
562  # @return list {<passOrFail> <txACKs> <rxACKs> <nrOfSuppressedACKs> }
563 
564  puts "HTTP Sessions available on the server: [ $httpServer Client.Identifiers.Get ]"
565  set clientTcpSessionResult [ [ [ $httpClient Http.Session.Info.Get ] Tcp.Session.Info.Get ] Result.Get ]
566 
567  # We request the HTTP Server's Client Session Info from the HTTP Server.
568  set scId [ $httpClient ServerClientId.Get ]
569  set hasSession 0
570  # Wait until the client session has been created:
571  while { $hasSession == 0} {
572  set hasSession [ $httpServer HasSession $scId ]
573  if { $hasSession == 0} {
574  set wait 0 ; after 500 "set wait 1" ; vwait wait ; unset wait
575  }
576  }
577  set httpServerSessionInfo [ $httpServer Http.Session.Info.Get $scId ]
578 
579  set serverTcpSessionResult [ [ $httpServerSessionInfo Tcp.Session.Info.Get ] Result.Get ]
580  $clientTcpSessionResult Refresh
581  $serverTcpSessionResult Refresh
582 
583  puts stderr "clientTcpSession : [ $clientTcpSessionResult Description.Get ]"
584  puts stderr "serverTcpSession : [ $serverTcpSessionResult Description.Get ]"
585 
586  set tcpAckInfo [ list ]
587  set pf 1
588 
589 
590  set nrOfSuppressedACKs -1
591 
592 
593  set txSyns [ $clientTcpSessionResult NumberOfSynSent.Get ]
594  set txSegments [ $clientTcpSessionResult Tx.SegmentCount.Total.Get ]
595  set txACKs [ expr $txSegments - $txSyns ]
596  puts stderr "Sent TCP ACKs : $txACKs"
597 
598  set rxSyns [ $serverTcpSessionResult NumberOfSynReceived.Get ]
599  set rxSegments [ $serverTcpSessionResult Rx.SegmentCount.Total.Get ]
600  set rxACKs [ expr $rxSegments - $rxSyns ]
601  puts stderr "Received TCP ACKs : $rxACKs"
602 
603 
604  if { $txACKs <= 0} {
605  puts stderr "No ACKs transmitted"
606  set pf 0
607  } else {
608  set nrOfSuppressedACKs [ expr $txACKs - $rxACKs ]
609  puts stderr "$nrOfSuppressedACKs ACKs suppressed ($txACKs transmitted)"
610  }
611 
612  lappend tcpAckInfo $pf
613  lappend tcpAckInfo $txACKs
614  lappend tcpAckInfo $rxACKs
615  lappend tcpAckInfo $nrOfSuppressedACKs
616 
617  return $tcpAckInfo
618 
619 }
620 
621 
622 }
623 
624 }