Search This Blog

Showing posts with label load balancer. Show all posts
Showing posts with label load balancer. Show all posts

Tuesday, May 27, 2014

How to use F5 Wireshark Plugin for LTM troubleshooting

In this post we are going to look how to use F5 Wireshark Plugin to troubleshoot networking issues on BigIP LTM.
  • Download the and install the plugin in your Wireshark
The full instruction are here F5 Wireshark Plugin. In essence you needed to copy the f5ethtrailer.dll file into C:\Program Files (x86)\wireshark\wireshark16\WiresharkPortable\ and restart my Wireshark.

Once you restart wireshark go to menu Help - About Wireshark, Plugins tab. You should be able to see the plugin listed there if properly installed.

  • The plugin is useful only if you take a capture on LTM with 'noise' information.
The noise is an internal information that TMM is attaching and managing for every packet when is being processed. To have a capture with noise these are the minimal options you need to specify:

tcpdump -w /var/tmp/capture.pcap -s0 -i _interface_:nnn

where the _interface_ can be:
    •  1.1 - example of an physical interface
    • dmz_vlan - a name you gave to your vlan when created
    • 0.0 - is the equivalent of 'any' interface what means capture on all interfaces and all vlans
My favourite syntax is usually something like this:

tcpdump -s0 -nn -w /var/tmp/test1-$(date +%s).pcap -i 0.0:nnn '(host _ip_ and port _port_ ) or arp or not ip' 
  • Open the capture in wireshark as normal
Once you open you will noticed that there is additional section in the packet details.

  • The most useful part of using this plugin is that you can quickly and easily find the client and server site traffic in the capture (It can be a challenging when you have multiple tcp streams and OneConnect profile):
    • Find a single packet of the flow you are interested in (search for VIP or client ip for example).
    • Find the "Flow ID" from the F5 Ethernet trailer (see the picture above for example).
    • Click with right mouse taste on the Flow ID field and select "Prepare as Filter".
    • In the Filter box (on top ) it will pre-populate the syntax for you.
    • Copy the hex value and delete the '.flowid == hex' part and start typing '.'  (dot).
    • It will mediately give you a list of possible options, select anyflowid and copy the hex back as it was originally. Example:
The original filter         : f5ethtrailer.flowid == 0x0d2e6dc0
Filter after modifications  : f5ethtrailer.anyflowid == 0x0d2e6dc0
    • Press Apply button
This filter is gong to find the client and server site flows for you. You can then analyse them packet by packet to find out and understand how and why LTM load balance it to one or another pool member.

References

https://devcentral.f5.com/wiki/advdesignconfig.F5WiresharkPlugin.ashx
https://devcentral.f5.com/questions/tcpdump-with-multiple-pool-members
SOL13637: Capturing internal TMM information with tcpdump

Thursday, July 11, 2013

What is AppFlow

In networking one of the words that has recently become popular is 'flow'. We have OpenFlow protocols that is a new emerging standard in network visualization that uses flows to describe what action should happen to a packet in OVS switch. We have sFlow and Netflow that are widely used by networking vendors for statistics information and performance data gathering from network devices. I've come recently across a new term: AppFlow.

Problem

What is AppFlow?

Analisis

In the video below we can learn that AppFlow is a protocols that can be used for application monitoring and visibility. With the industry acceptance of ADS/ADC systems (Application Delivery System and Application Delivery Controller) it is therefor logically rational to extend load balancers functionality to allow them to collect application specific data. The idea is similar how sFlow/Netflow work where additionally to IP and TCP based information we want to collect application specify information as well.


References
  1. http://blogs.citrix.com/2011/05/09/introducing-appflow-netflow-for-apps/
  2. http://www.youtube.com/watch?v=MNbLi-t7ZPE
  3. http://www.appflow.org/
  4. http://www.citrix.com/products/netscaler-application-delivery-controller/features/visibility.html
  5. http://www.citrix.com/products/netscaler-application-delivery-controller/how-it-works/technologies.html

Monday, April 29, 2013

Challenge 11 script

The full summary of all posts for API Rackspace challenge can be found here:
Rackspace api-challenge summary

Challenge 11 script

Below is the output and results from script #11 from the api-challenge.

The script can be repeatedly run with the option -d otherwise it stops if any of the cloud objects it wants to create already exist.

    Challenge 11: Write an application that will: Create an SSL terminated load balancer 
    (Create self-signed certificate).
    Create a DNS record that should be pointed to the load balancer. Create Three servers 
    as nodes behind the LB.

    Each server should have a CBS volume attached to it. (Size and type are irrelevant.)
    All three servers should have a private Cloud Network shared between them.
    Login information to all three servers returned in a readable format as the result 
    of the script, including connection information. Worth 6 points


Depending on the obligatory -n options it will create cloud objects with predictable names.
Below is an example what objects it creates and what the naming conversion is.

# # python challenge11.py

    usage: challenge11.py [-h] [-v] [-d] [ -k key-key  ] [ -c cert ] [ -i image-id ][ -f flavor-id ] -n FQDN-name

    image-id -f flavor-id
        -h - usage help
        -v - verbose / debug output
        -d - delete objects if they existed in cloud before creating new one
        -k - certificate pritate key (see -c below)
        -c - public certificate ( see -k above)
        -n - FQDN name like www.myexample.com
        -i - specify image-id or use the default for Ubuntu 10.04
        -f - specify flavor-id or use the default for the smallest cloud server


# python challenge11.py -d -n www.challenge11.myrado.net

[01:04:17] Checked your cert/key pair www.challenge11.myrado.net.crt/www.challenge11.myrado.net.key, ok
[01:04:18] Deleting existing domain challenge11.myrado.net
[01:04:20] Checked your FQDN www.challenge11.myrado.net, ok
[01:04:20] Checked your DNS domains challenge11.myrado.net, there is none, ok
[01:04:26] Deleted existing cloud server www.challenge11.myrado.net-0
[01:04:29] Deleted existing cloud server www.challenge11.myrado.net-1
[01:04:33] Deleted existing cloud server www.challenge11.myrado.net-2
[01:04:33] Checked your existing cloud server www.challenge11.myrado.net-*, ok
[01:04:34] Checked your block images www.challenge11.myrado.net, there is none, ok
[01:04:36] Checked your lb www.challenge11.myrado.net, there is none, ok
[01:04:37] Checked your image id d4c7b93d-9f18-45dc-aa7c-3e3b126e3792, ok
[01:04:37] Checked your flavor id 2, ok
[01:04:38] Deleted existing network obj www.challenge11.myrado.net
[01:04:38] Checked your existing network objects www.challenge11.myrado.net, ok
[01:04:38] Checked your cert/key pair www.challenge11.myrado.net.crt/www.challenge11.myrado.net.key, ok
[01:04:39] Checked your FQDN www.challenge11.myrado.net, ok
[01:04:39] Checked your DNS domains challenge11.myrado.net, there is none, ok
[01:04:45] Deleted existing cloud server www.challenge11.myrado.net-1
[01:04:50] Deleted existing cloud server www.challenge11.myrado.net-2
[01:04:50] Checked your existing cloud server www.challenge11.myrado.net-*, ok
[01:04:50] Deleted existing block storage image  www.challenge11.myrado.net-0
[01:04:51] Deleted existing block storage image  www.challenge11.myrado.net-1
[01:04:51] Deleted existing block storage image  www.challenge11.myrado.net-2
[01:04:51] Checked your block images www.challenge11.myrado.net, there is none, ok
[01:05:04] Checked your image id d4c7b93d-9f18-45dc-aa7c-3e3b126e3792, ok
[01:05:04] Checked your flavor id 2, ok
[01:05:05] Checked your existing network objects www.challenge11.myrado.net, ok
[01:05:05] Building 3 cloud servers
.
.
.
.
[01:08:20] Building and configuring lb ...
.
.
.
.
.
.
.
[01:09:09] Building and configuring dns domain ...
[01:09:13] ----------------------------------------------------------------------
[01:09:13] vip name www.challenge11.myrado.net and ip 162.13.24.49
[01:09:13] cloud server www.challenge11.myrado.net-0 added to pool as 10.179.65.126
[01:09:13] cloud server www.challenge11.myrado.net-1 added to pool as 10.179.70.78
[01:09:13] cloud server www.challenge11.myrado.net-2 added to pool as 10.179.73.93
[01:09:13] ----------------------------------------------------------------------
Server # 0: ID 3227a512-3a1c-4f68-a82a-89d15ec74341 name www.challenge11.myrado.net-0 pub IP 95.138.173.80 priv IP 192.168.100.2 password SecretP@ss1
Server # 1: ID 662606f2-66a8-4281-8025-5161bf31754c name www.challenge11.myrado.net-1 pub IP 95.138.171.48 priv IP 192.168.100.1 password SecretP@ss1
Server # 2: ID f37d3551-60f5-40f3-92eb-4cbb1f7dbcfe name www.challenge11.myrado.net-2 pub IP 95.138.175.130 priv IP 192.168.100.3 password SecretP@ss1

References
  1. https://github.com/rtomaszewski/api-challenge/tree/challenge11
  2. http://docs.rackspace.com/loadbalancers/api/v1.0/clb-devguide/content/SSLTermination-d1e2479.html

Friday, April 26, 2013

Challenge 10 script

The full summary of all posts for API Rackspace challenge can be found here:
Rackspace api-challenge summary

Challenge 10 script

Below is the output and results from script #10 from the api-challenge.
The script shows more advance example how consume cloud resources like cloud servers, cloud load balances, cloud files and cloud dns. It does it by building a simple solution.

The script can be repeatedly run with the option -d otherwise it stops if any of the cloud objects it wants to create already exist.
Depending on the obligatory -n options it will create cloud objects with predictable names.
Below is an example what objects it creates and what the naming conversion is.

# python challenge10.py

    usage: challenge10.py [-h] [-v] [-d] [ -s ssh-key ] [ -e error-page ] [ -c container-name ] [ -i image-id ] [ -f flavor-id ] -n FQDN-name

    image-id -f flavor-id
        -h - usage help
        -v - verbose / debug output
        -d - delete objects if they existed in cloud before creating new one
        -n - FQDN name like www.myexample.com
        -s - path to your ssh public key (not priv!)
        -e - path to a html file that will be served from the LB when all pool members are down
        -c - name of cloud files container-name to store the backup data
        -i - specify image-id or use the default for Ubuntu 10.04
        -f - specify flavor-id or use the default for the smallest cloud server

# python challenge10.py -d -n www.challenge10.myrado.net
[22:50:46] Checked your ssh public key /root/.ssh/id_rsa.pub, ok
[22:50:46] Checked your error page error.html, ok
[22:50:50] Deleted existing cloud server www.challenge10.myrado.net-0
[22:50:54] Deleted existing cloud server www.challenge10.myrado.net-1
[22:50:54] Checked your existing cloud server www.challenge10.myrado.net-*, ok
[22:50:56] Deleting existing domain challenge10.myrado.net
[22:50:57] Checked your FQDN www.challenge10.myrado.net, ok
[22:50:57] Checked your DNS domains challenge10.myrado.net, there is none, ok
[22:50:57] Deleted container www.challenge10.myrado.net
[22:50:57] Checked your container www.challenge10.myrado.net where we are going to keep backup data, ok
[22:50:59] Checked your lb www.challenge10.myrado.net, there is none, ok
[22:51:01] Checked your image id d4c7b93d-9f18-45dc-aa7c-3e3b126e3792, ok
[22:51:01] Checked your flavor id 2, ok
[22:51:01] Building 2 cloud servers
.
.
.
.
[22:53:37] Building and configuring lb ...
.
.
.
.
[22:54:15] Building and configuring dns domain ...
[22:54:18] Backuping files to cloud files ...
[22:54:19] ----------------------------------------------------------------------
[22:54:19] vip name www.challenge10.myrado.net and ip 162.13.24.117
[22:54:19] cloud server www.challenge10.myrado.net-0 added to pool as 10.179.73.200
[22:54:19] cloud server www.challenge10.myrado.net-1 added to pool as 10.179.73.165
[22:54:19] Error page is stored in container www.challenge10.myrado.net under name error.html
[22:54:19] to check if the config works try to: curl -v http://162.13.24.117

These are some steps to verify the config.
 
# dig +short @69.20.95.4 www.challenge10.myrado.net
162.13.24.117

# nova list | grep challenge10                                                                          23:03:28
| 35e831a5-cfc8-44a7-98ff-1a003cc30901 | www.challenge10.myrado.net-0 | ACTIVE | public=2a00:1a48:7805:0113:8cfc:cf10:ff08:21ff, 95.138.174.137; private=10.179.73.200 |
| 06259b99-33d0-4537-94ab-e1be032337aa | www.challenge10.myrado.net-1 | ACTIVE | public=2a00:1a48:7805:0113:8cfc:cf10:ff08:3c44, 95.138.174.236; private=10.179.73.165 |

# curl -v http://162.13.24.117
* About to connect() to 162.13.24.117 port 80 (#0)
*   Trying 162.13.24.117... connected
> GET / HTTP/1.1
> User-Agent: curl/7.22.0 (x86_64-pc-linux-gnu) libcurl/7.22.0 OpenSSL/1.0.1 zlib/1.2.3.4 libidn/1.23 librtmp/2.3
> Host: 162.13.24.117
> Accept: */*
>
< HTTP/1.1 500 Internal Server Error
< Date: Thu, 25 Apr 2013 23:26:45 GMT
< Connection: close
< Content-Type: text/html
<                                                                                                                                   23:28:27
* Closing connection #0
<html>
  <head>
    <title>Challenge 10 - Default error page on LB</title>
  </head>
  <body> Sorry but all pool members failing health checks. </body>
</html>

References
  1. https://github.com/rtomaszewski/api-challenge/tree/challenge10

Saturday, March 30, 2013

How to host 2 ssl sites on a single public IP on F5 load balancer

As the pool of free IPv4 is getting lower it is important to efficiently mange our existing assigned public IPs. One thing that can be used is to use SSL offloading and hosting multiple HTTPS sites on single public IP using wild cart certificates.

The core of this solution is the configuration of the lb and the vhost setting on your server. The server reads the HTTP Host header and base on it decide what site the user try to access.

Problem

How to configure servers and load balancer to host 2 ssl sites on a single public IP.

Analisis and example configuration
  • Example F5 configuration
For SSL to work we need a wild card certificate.
We have to import the certificate and key to F5 and create client site ssl profile.

Example wild cart certificate:
 
# openssl x509 -in /var/tmp/wildcard.rado.net.crt -noout -text
Certificate:
    Data:
        Version: 3 (0x2)
        Serial Number:
            04:9c:4a:4b:11:11:bc
        Signature Algorithm: sha1WithRSAEncryption
        Issuer: C=US, ST=Arizona, L=Scottsdale, O=GoDaddy.com, Inc., OU=http://certificates.godaddy.com/repository, CN=Go Daddy Secure Certification Authority/serialNumber=111111
        Validity
            Not Before: Nov 21 11:57:16 2012 GMT
            Not After : Nov 20 09:31:37 2013 GMT
        Subject: O=*.rado.net, OU=Domain Control Validated, CN=*.rado.net
        ....

SSL profile:
 
# tmsh list ltm profile client-ssl  wildcard-client-ssl-profile
ltm profile client-ssl wildcard-client-ssl-profile {
    cert wildcard-rado.net.crt
    chain CA.crt
    defaults-from clientssl
    key wildcard-rado.net.key
}

Virtual server to terminate and load balance the traffic:
 
# tmsh list ltm virtual VS-1.1.1.1-443
ltm virtual VS-1.1.1.1-443 {
    destination 192.168.99.68:https
    ip-protocol tcp
    mask 255.255.255.255a
    pool POOL-192.168.99.68-80
    profiles {
        http { }
        tcp { }
        wildcard-client-ssl-profile {
            context clientside
        }
    }
}

Servers where the traffic is going to be load balanced:
 
# tmsh list ltm pool POOL-192.168.99.68-80
ltm pool POOL-192.168.99.68-80 {
    load-balancing-mode least-connections-member
    members {
        10.177.1.1:http {
            session monitor-enabled
        }
        10.177.1.2:http {
            session monitor-enabled
        }
    }
    monitor http
}
  • Apache configuration on one of the servers

    This is a simple example configuration for the http server.
     
    # cat vhost1.conf
    <VirtualHost *:80>
            ServerName vhost1.rado.net
            DocumentRoot /var/www/vhost1
    </VirtualHost>
    
    # cat vhost2.conf
    <VirtualHost *:80>
            ServerName vhost2.rado.net
            DocumentRoot /var/www/vhost2
    </VirtualHost>
    
    # cat /var/www/vhost1/index.html
    <html><body><h1>It works!</h1>
    <p>
    This is VHOST 1
    </p>
    </body></html>
    
    # a2ensite vhost1.conf 
    # a2ensite vhost2.conf 
    
    # service apache2 reload
    

    A quick local test on the server will confirm if the config is correct.
     
    # curl -v -H "Host: vhost1.rado.net" http://5.1.1.1
    * About to connect() to 5.1.1.1 port 80 (#0)
    *   Trying 5.1.1.1... connected
    > GET / HTTP/1.1
    > User-Agent: curl/7.22.0 (x86_64-pc-linux-gnu) libcurl/7.22.0 OpenSSL/1.0.1 zlib/1.2.3.4 libidn/1.23 librtmp/2.3
    > Accept: */*
    > Host: vhost1.rado.net
    >
    < HTTP/1.1 200 OK
    < Date: Sat, 30 Mar 2013 23:42:20 GMT
    < Server: Apache/2.2.22 (Ubuntu)
    < Last-Modified: Sat, 30 Mar 2013 23:19:21 GMT
    < ETag: "b46bb-47-4d92c9e17f040"
    < Accept-Ranges: bytes
    < Content-Length: 71
    < Vary: Accept-Encoding
    < Content-Type: text/html
    <
    <html><body><h1>It works!</h1>
    <p>
    This is VHOST 1
    </p>
    </body></html>
    * Connection #0 to host 5.79.21.166 left intact
    * Closing connection #0
    

Friday, March 29, 2013

Rackspace and Brocade pushing ADX platform

Brocade is Rackspace partner. The company provide load balancing as well as storage solution for the company. Since Brocade ADX load balancer became standard product Rackspace sells it got a bigger momentum and traction on the market. The Gartner magic quadrants for application delivery controllers show how the company progressed and improved.

Thursday, August 16, 2012

How to calculate a number of new SSL/TCP connections per every 10ms

Hardware load balancers like F5 are a graet product that offers a lot of featreus still combined with a simple and intuitive management GUI. The only problem is the price you have to pay to buy it and than further to pay the support and the license fees.

When working with F5 I have run once into an interesting SSL/TLS problem. It is documented and described SOL6475: Overview of SSL TPS licensing limits.

The most important part from the solutions is:

The BIG-IP system measures SSL TPS based on client-side connection attempts to any
virtual server configured with a Client SSL profile. SSL TPS is enforced across a
sliding time window. The BIG-IP system utilizes a 10ms window (1/100 of a second)
to calculate the current TPS. If the number of TPS requests within any 10ms window
exceeds 1/100 of the licensed TPS, an error message regarding the TPS limit being
reached is sent to the /var/log/ltm file.

Problem

How to know what clients IPs cause the error to be logged. How to measure and calculate the number of SSL connection per seconds for even 10ms.

Solution

As there are no tools on F5 that helps you to find this out I thought that a simple way to get some visibility of it would be to capture all TCP SYN packets hitting the LB and then later do some analysis of it. An implementation of this ideas in a form of a python script can be found here [1].

Demonstration

To test sslAnalyze.py script we need first a tcpdump file. For this purpose we can use the nmap command and run a SYN flood. For the desciption of the nmap options you can take a look here [2].

$ nmap -P0 -TNormal -D 1.2.3.4,1.2.3.5,1.2.3.6,1.2.3.7,1.2.3.8,1.2.3.9,1.2.3.10 -iR 10

All what we have to do now is to run on one session a tcpump and on the other the nmap command. As we are only interested in the TCP SYN packets we should tailor the tcpdump filtering syntax properly. A tcpdump that will capture only the SYN packets:

$ tcpdump -vvv -nn -i eth0 -w /var/tmp/syn-flood-example.pcap 'tcp[13]&2!=0 and tcp[13]&16==0' 

All what we have to do is not run our script to see the statistics.

I have to quickly explain the script itself. Once run it will prints on stdout a listing of found connections and additionally will create a log file with a name sslConnHigh.txt for only these connections that are over the threshold.

The parameters that you have to specify are:
  • param1 - tcpdump file (it has to have only SYN packets) 
  • param2 - time fractions in microseconds ( 1000000 microseconds -> 1 second ) 
  • param3 - connection threshold per time to log this result to a sslConnHigh.txt file

Examples

# Example 1: to see a  number of connection per 1 second 

$ python sslAnalyze.py  syn-flood-example.pcap 1000000 1

# Example 2: to see a number of connection per every 500ms (half a second)

$ python sslAnalyze.py  syn-flood-example.pcap 500000 1

# Example3: to see a number of connection per every 500ms (half a second) and log only
# these timestamps that have more than 100 connection in a single half a second
# some example output has been attached as well below

$ python sslAnalyze.py  syn-flood-example.pcap 500000 100

keeping the line: reading from file syn-flood-example.pcap, link-type EN10MB (Ethernet)
                     date     timestamp     sumOfConn [... 500000 microsecond periods ... ]
 Tue Aug 14 23:33:30 2012    1344983610       sum:183     0  183 
 Tue Aug 14 23:33:31 2012    1344983611        sum:95     6   89 
 Tue Aug 14 23:33:32 2012    1344983612       sum:614   430  184 
 Tue Aug 14 23:33:33 2012    1344983613       sum:520   216  304 

To better understand why F5 logs the error message and what trigger the TPS log error messages we have to run this command:

# 10 milliseconds = 10000 microseconds
$ python sslAnalyze.py  syn-flood-example.pcap 10000 [F5_SSL_total_TPS]
$ cat sslConnHigh.txt

In the output you are going to see the timestamps (rounded to 1 second) where the number of connections in a single 10ms window are above the licensing limit you device has. For further analize you can extract these data from the tcpdump with a help of tcpslice tool.


# 1268649656 is an example timestamp from above
$ tcpslice 1268649656  +1 syn-flood-example.pcap -w 1268649656.pcap

$ tcpdump -tt -nr 1268649656.pcap

reading from file 1268649656.pcap, link-type EN10MB (Ethernet)
1268649656.042723 vlan 4093, p 0, IP 19.26.168.192.4598 - 19.26.225.215.443: S 2973530156:2973530156(0) win 64512 mss 1460,nop,nop,sackOK
1268649656.056163 vlan 4093, p 0, IP 19.89.139.199.1622 - 19.26.225.23.443: S 1522394445:1522394445(0) win 64512 mss 1460,nop,wscale 0,nop,nop,sackOK

References
  1. https://github.com/rtomaszewski/experiments/blob/master/sslAnalyze.py
  2. http://www.hcsw.org/reading/nmapguide.txt
  3. http://danielmiessler.com/study/tcpdump/

Wednesday, March 28, 2012

Network data analize for a scenario with two Cloud Server behind a Rackspace Cloud Load Balancer

In this post we are going to take a look at the Rackspace Cloud Load Balancer (CLB) again but this time we aim to analyze a scenario with persistence issues and 2 pool member. Particularly, we want to find out and understand how the session persistence works on CLB. The simple diagram bellow shows our small test cloud topology. We have 3 Cloud Servers (CS) (1 client + 2 pool members) and one load balancer (LB). Our LB is using Round Robin algorithm to distribute the load amount the pool members. The pool members are defined using the internal IP addresses: 10.177.132.15  and 10.177.133.12.



Test scenario #1 without session persistence
  1. The client sends 1st HTTP request to LB1.
  2. The LB1 sends the req to first pool member according to round robin state.
  3. The pool member replays with HTTP 200.
  4. LB1 sends the replay back to the client.
  5. The client sends 2th HTTP request to LB1.
  6. The LB1 sends the req to another pool member.
  7. The pool member replays with HTTP 200.
  8. LB1 sends the replay back to the client.
Because we don't have access to the LB1 we are going to collect concurrent tcpdumps from all CS. We are using the curl to simulate HTTP requests on the client.

# run on client
tcpdump -nn -s0 -i any -w /var/tmp/client.pcap  port 80

# run on server1
tcpdump -nn -s0 -i any -w /var/tmp/server-urado1.pcap  port 80

# run on server2
tcpdump -nn -s0 -i any -w /var/tmp/server-urado2.pcap  port 80

# run on client to simulate HTTP requests
curl -v http://31.222.175.142

The first dumps and data from the test can be seen and found bellow [1].

[root@crado1 tmp]# tshark  -n -r client.pcap
  1   0.000000 31.222.191.246 -> 31.222.175.142 TCP 60925 > 80 [SYN] Seq=0 Win=5840 Len=0 MSS=1460 TSV=25369178 TSER=0 WS=4
  2   0.000513 31.222.175.142 -> 31.222.191.246 TCP 80 > 60925 [SYN, ACK] Seq=0 Ack=1 Win=17896 Len=0 MSS=8960 TSV=1091232983 TSER=25369178 WS=9
  3   0.000541 31.222.191.246 -> 31.222.175.142 TCP 60925 > 80 [ACK] Seq=1 Ack=1 Win=5840 Len=0 TSV=25369178 TSER=1091232983
  4   0.000609 31.222.191.246 -> 31.222.175.142 HTTP GET / HTTP/1.1
  5   0.000841 31.222.175.142 -> 31.222.191.246 TCP 80 > 60925 [ACK] Seq=1 Ack=159 Win=19456 Len=0 TSV=1091232983 TSER=25369178
  6   0.008348 31.222.175.142 -> 31.222.191.246 HTTP HTTP/1.1 200 OK  (text/html)
  7   0.008369 31.222.191.246 -> 31.222.175.142 TCP 60925 > 80 [ACK] Seq=159 Ack=301 Win=6912 Len=0 TSV=25369180 TSER=1091232983
  8   0.008651 31.222.191.246 -> 31.222.175.142 TCP 60925 > 80 [FIN, ACK] Seq=159 Ack=301 Win=6912 Len=0 TSV=25369180 TSER=1091232983
  9   0.008841 31.222.175.142 -> 31.222.191.246 TCP 80 > 60925 [FIN, ACK] Seq=301 Ack=160 Win=19456 Len=0 TSV=1091232983 TSER=25369180
 10   0.008855 31.222.191.246 -> 31.222.175.142 TCP 60925 > 80 [ACK] Seq=160 Ack=302 Win=6912 Len=0 TSV=25369180 TSER=1091232983
 11   2.182732 31.222.191.246 -> 31.222.175.142 TCP 60926 > 80 [SYN] Seq=0 Win=5840 Len=0 MSS=1460 TSV=25369723 TSER=0 WS=4
 12   2.183220 31.222.175.142 -> 31.222.191.246 TCP 80 > 60926 [SYN, ACK] Seq=0 Ack=1 Win=17896 Len=0 MSS=8960 TSV=1091233201 TSER=25369723 WS=9
 13   2.183240 31.222.191.246 -> 31.222.175.142 TCP 60926 > 80 [ACK] Seq=1 Ack=1 Win=5840 Len=0 TSV=25369723 TSER=1091233201
 14   2.183496 31.222.191.246 -> 31.222.175.142 HTTP GET / HTTP/1.1
 15   2.183658 31.222.175.142 -> 31.222.191.246 TCP 80 > 60926 [ACK] Seq=1 Ack=159 Win=19456 Len=0 TSV=1091233201 TSER=25369724
 16   2.186699 31.222.175.142 -> 31.222.191.246 HTTP HTTP/1.1 200 OK  (text/html)
 17   2.186717 31.222.191.246 -> 31.222.175.142 TCP 60926 > 80 [ACK] Seq=159 Ack=301 Win=6912 Len=0 TSV=25369724 TSER=1091233201
 18   2.188335 31.222.191.246 -> 31.222.175.142 TCP 60926 > 80 [FIN, ACK] Seq=159 Ack=301 Win=6912 Len=0 TSV=25369725 TSER=1091233201
 19   2.188756 31.222.175.142 -> 31.222.191.246 TCP 80 > 60926 [FIN, ACK] Seq=301 Ack=160 Win=19456 Len=0 TSV=1091233201 TSER=25369725
 20   2.188771 31.222.191.246 -> 31.222.175.142 TCP 60926 > 80 [ACK] Seq=160 Ack=302 Win=6912 Len=0 TSV=25369725 TSER=1091233201

We can see that from the client point of view the data comes always from the some IP address. The 2 pool members that we have are not visible.

The dumps bellow show as well that the responses came from different servers. We can confirm as well that neither the req or replay had a cookies header that was passed to the client.

root@urado1:/var/tmp# tshark -n -r server-urado1.pcap -V http
    GET / HTTP/1.1\r\n
        [Expert Info (Chat/Sequence): GET / HTTP/1.1\r\n]
            [Message: GET / HTTP/1.1\r\n]
            [Severity level: Chat]
            [Group: Sequence]
        Request Method: GET
        Request URI: /
        Request Version: HTTP/1.1
    User-Agent: curl/7.15.5 (x86_64-redhat-linux-gnu) libcurl/7.15.5 OpenSSL/0.9.8b zlib/1.2.3 libidn/0.6.5\r\n
    X-Forwarded-For: 31.222.191.246\r\n
    Accept: */*\r\n
    X-Forwarded-Proto: http\r\n
    Host: 31.222.175.142\r\n
    X-Cluster-Client-Ip: 31.222.191.246\r\n
    \r\n

    HTTP/1.1 200 OK\r\n
        [Expert Info (Chat/Sequence): HTTP/1.1 200 OK\r\n]
            [Message: HTTP/1.1 200 OK\r\n]
            [Severity level: Chat]
            [Group: Sequence]
        Request Version: HTTP/1.1
        Status Code: 200
        Response Phrase: OK
    Date: Tue, 27 Mar 2012 22:37:17 GMT\r\n
    Server: Apache/2.2.20 (Ubuntu)\r\n
    Last-Modified: Mon, 26 Mar 2012 21:29:38 GMT\r\n
    ETag: "7c030-2c-4bc2c1245f480"\r\n
    Accept-Ranges: bytes\r\n
    Content-Length: 44\r\n
        [Content length: 44]
    Vary: Accept-Encoding\r\n
    Content-Type: text/html\r\n
    \r\n
Line-based text data: text/html
    It works! urado1\n
    \n            


root@urado2:/var/tmp# tshark -n -r server-urado2.pcap -V http
    GET / HTTP/1.1\r\n
        [Expert Info (Chat/Sequence): GET / HTTP/1.1\r\n]
            [Message: GET / HTTP/1.1\r\n]
            [Severity level: Chat]
            [Group: Sequence]
        Request Method: GET
        Request URI: /
        Request Version: HTTP/1.1
    User-Agent: curl/7.15.5 (x86_64-redhat-linux-gnu) libcurl/7.15.5 OpenSSL/0.9.8b zlib/1.2.3 libidn/0.6.5\r\n
    X-Forwarded-For: 31.222.191.246\r\n
    Accept: */*\r\n
    X-Forwarded-Proto: http\r\n
    Host: 31.222.175.142\r\n
    X-Cluster-Client-Ip: 31.222.191.246\r\n
    \r\n

    HTTP/1.1 200 OK\r\n
        [Expert Info (Chat/Sequence): HTTP/1.1 200 OK\r\n]
            [Message: HTTP/1.1 200 OK\r\n]
            [Severity level: Chat]
            [Group: Sequence]
        Request Version: HTTP/1.1
        Status Code: 200
        Response Phrase: OK
    Date: Tue, 27 Mar 2012 22:37:21 GMT\r\n
    Server: Apache/2.2.20 (Ubuntu)\r\n
    Last-Modified: Mon, 26 Mar 2012 21:30:02 GMT\r\n
    ETag: "5c033-2c-4bc2c13b42a80"\r\n
    Accept-Ranges: bytes\r\n
    Content-Length: 44\r\n
        [Content length: 44]
    Vary: Accept-Encoding\r\n
    Content-Type: text/html\r\n
    \r\n
Line-based text data: text/html
    It works! urado2\n
    \n

Test scenario #2 with session persistence enabled on LB1

The test steps are the same as the one before. The only difference is a configuration change on LB1, where we enable persistence feature. The new tcpdumps were taken as before and can be downloaded from [2]. The easy way to see the differences without repeating ourselves again is to take a look at the curl output. We see directly there all our requests and replays with the HTML payload from the servers.

First GET request without Cookie header. In the response we see this time that the LB is sending one cookie we didn't see before.

[root@crado1 ~]# curl -v http://31.222.175.142
* About to connect() to 31.222.175.142 port 80
*   Trying 31.222.175.142... connected
* Connected to 31.222.175.142 (31.222.175.142) port 80
> GET / HTTP/1.1
> User-Agent: curl/7.15.5 (x86_64-redhat-linux-gnu) libcurl/7.15.5 OpenSSL/0.9.8b zlib/1.2.3 libidn/0.6.5
> Host: 31.222.175.142
> Accept: */*
>
< HTTP/1.1 200 OK
< Server: Apache/2.2.20 (Ubuntu)
< Vary: Accept-Encoding
< Content-Type: text/html
< Date: Tue, 27 Mar 2012 22:57:56 GMT
< Accept-Ranges: bytes
< ETag: "7c030-2c-4bc2c1245f480"
< Set-Cookie: X-Mapping-fjhppofk=AEC8609A6667F8E6AC1B323F80BDF8C9; path=/
< Last-Modified: Mon, 26 Mar 2012 21:29:38 GMT
< Content-Length: 44
It works! urado1

* Connection #0 to host 31.222.175.142 left intact
* Closing connection #0

Another similar request. The response contains again Cookie header but with different value. The HTML payload from the 2 requests show that these are data from CS1 and CS2.

[root@crado1 ~]# curl -v http://31.222.175.142
* About to connect() to 31.222.175.142 port 80
*   Trying 31.222.175.142... connected
* Connected to 31.222.175.142 (31.222.175.142) port 80
> GET / HTTP/1.1
> User-Agent: curl/7.15.5 (x86_64-redhat-linux-gnu) libcurl/7.15.5 OpenSSL/0.9.8b zlib/1.2.3 libidn/0.6.5
> Host: 31.222.175.142
> Accept: */*
>
< HTTP/1.1 200 OK
< Server: Apache/2.2.20 (Ubuntu)
< Vary: Accept-Encoding
< Content-Type: text/html
< Date: Tue, 27 Mar 2012 22:58:04 GMT
< Accept-Ranges: bytes
< ETag: "5c033-2c-4bc2c13b42a80"
< Set-Cookie: X-Mapping-fjhppofk=0459144121542E9668F8271676341C7B; path=/
< Last-Modified: Mon, 26 Mar 2012 21:30:02 GMT
< Content-Length: 44
It works! urado2

* Connection #0 to host 31.222.175.142 left intact
* Closing connection #0

We sent another request but this time the client supply the Cookie header provided before. We use the value returned for the Server2. Base on the HTML payload we see that the response comes from previously selected pool member. The LB doesn't provide another cookie as it was before.

[root@crado1 ~]# curl -v -H 'Cookie: X-Mapping-fjhppofk=0459144121542E9668F8271676341C7B' 31.222.175.142 
* About to connect() to 31.222.175.142 port 80
*   Trying 31.222.175.142... connected
* Connected to 31.222.175.142 (31.222.175.142) port 80
> GET / HTTP/1.1
> User-Agent: curl/7.15.5 (x86_64-redhat-linux-gnu) libcurl/7.15.5 OpenSSL/0.9.8b zlib/1.2.3 libidn/0.6.5
> Host: 31.222.175.142
> Accept: */*
> Cookie: X-Mapping-fjhppofk=0459144121542E9668F8271676341C7B
>
< HTTP/1.1 200 OK
< Date: Tue, 27 Mar 2012 22:58:35 GMT
< Server: Apache/2.2.20 (Ubuntu)
< Last-Modified: Mon, 26 Mar 2012 21:30:02 GMT
< ETag: "5c033-2c-4bc2c13b42a80"
< Accept-Ranges: bytes
< Content-Length: 44
< Vary: Accept-Encoding
< Content-Type: text/html
It works! urado2

* Connection #0 to host 31.222.175.142 left intact
* Closing connection #0

The some request sent again returns the some result - as expected.

[root@crado1 ~]# curl -v -H 'Cookie: X-Mapping-fjhppofk=0459144121542E9668F8271676341C7B' 31.222.175.142                 
* About to connect() to 31.222.175.142 port 80
*   Trying 31.222.175.142... connected
* Connected to 31.222.175.142 (31.222.175.142) port 80
> GET / HTTP/1.1
> User-Agent: curl/7.15.5 (x86_64-redhat-linux-gnu) libcurl/7.15.5 OpenSSL/0.9.8b zlib/1.2.3 libidn/0.6.5
> Host: 31.222.175.142
> Accept: */*
> Cookie: X-Mapping-fjhppofk=0459144121542E9668F8271676341C7B
>
< HTTP/1.1 200 OK
< Date: Tue, 27 Mar 2012 22:58:52 GMT
< Server: Apache/2.2.20 (Ubuntu)
< Last-Modified: Mon, 26 Mar 2012 21:30:02 GMT
< ETag: "5c033-2c-4bc2c13b42a80"
< Accept-Ranges: bytes
< Content-Length: 44
< Vary: Accept-Encoding
< Content-Type: text/html
It works! urado2

* Connection #0 to host 31.222.175.142 left intact
* Closing connection #0

Summary
The session persistence on the Rackspace Cloud Load Balancer uses HTTP cookie header. The load balancer when it returns the response back to the client it rewrites the IP addresses and inserts a new HTTP Cookie header. As long as the client obeys the Cookie value in his subsequent request it will be load balanced to the some pool member.

References
[1] persistence disabled
[2] persistence enabled

Tuesday, March 27, 2012

Network data analize for simple scenario with Rackspace Cloud Load Balancer (CLB)

A load balancer becomes de facto a standard for many distributed deployments. You are going to need it even if you don't know it yet.

Let's take a look at the Rackspace Cloud Load Balancer (CLB) feature [1] and dig a litter bit more how it actually works. We are going to base our analysis on the diagram below. As the scenario is very simple to set up and configure I'm leaving it for the reader alone.




Test scenario
  1. The client sends HTTP request to LB1.
  2. The LB1 load balance it to his only one pool member.
  3. The pool member replays with HTTP 200.
  4. LB1 sends the replay back to the client.

As we don't have access to the LB1 itself it is hard to say what is really happening there. The only way at the moment is to take concurrent tcpdumps on the client and server. To simulate the client requests we are going to use the curl. All commands look like that:

# run on client
tcpdump -nn -s0 -i any -w /var/tmp/client.pcap  port 80

# run on server
tcpdump -nn -s0 -i any -w /var/tmp/server-urado1.pcap  port 80

# run on client on a separate session for example
curl -v http://31.222.175.142

The data from the captured tcpdump can be seen below. The original files can be found here [2].

[root@crado1 tmp]# tshark  -n -r client.pcap
  1   0.000000 31.222.191.246 -> 31.222.175.142 TCP 47267 > 80 [SYN] Seq=0 Win=5840 Len=0 MSS=1460 TSV=3064226 TSER=0 WS=4
  2   0.000324 31.222.175.142 -> 31.222.191.246 TCP 80 > 47267 [SYN, ACK] Seq=0 Ack=1 Win=17896 Len=0 MSS=8960 TSV=1082311203 TSER=3064226 WS=9
  3   0.000344 31.222.191.246 -> 31.222.175.142 TCP 47267 > 80 [ACK] Seq=1 Ack=1 Win=5840 Len=0 TSV=3064226 TSER=1082311203
  4   0.000441 31.222.191.246 -> 31.222.175.142 HTTP GET / HTTP/1.1
  5   0.000565 31.222.175.142 -> 31.222.191.246 TCP 80 > 47267 [ACK] Seq=1 Ack=159 Win=19456 Len=0 TSV=1082311203 TSER=3064226
  6   0.018854 31.222.175.142 -> 31.222.191.246 HTTP HTTP/1.1 200 OK  (text/html)
  7   0.018873 31.222.191.246 -> 31.222.175.142 TCP 47267 > 80 [ACK] Seq=159 Ack=301 Win=6912 Len=0 TSV=3064231 TSER=1082311205
  8   0.019107 31.222.191.246 -> 31.222.175.142 TCP 47267 > 80 [FIN, ACK] Seq=159 Ack=301 Win=6912 Len=0 TSV=3064231 TSER=1082311205
  9   0.019323 31.222.175.142 -> 31.222.191.246 TCP 80 > 47267 [FIN, ACK] Seq=301 Ack=160 Win=19456 Len=0 TSV=1082311205 TSER=3064231
 10   0.019337 31.222.191.246 -> 31.222.175.142 TCP 47267 > 80 [ACK] Seq=160 Ack=302 Win=6912 Len=0 TSV=3064231 TSER=1082311205

root@urado1:~/tmp# tshark -n -r server-urado1.pcap
  1   0.000000 10.190.254.7 -> 10.177.132.15 TCP 76 40293 > 80 [SYN] Seq=0 Win=17920 Len=0 MSS=8960 SACK_PERM=1 TSval=1082311203 TSecr=0 WS=512
  2   0.000061 10.177.132.15 -> 10.190.254.7 TCP 76 80 > 40293 [SYN, ACK] Seq=0 Ack=1 Win=14480 Len=0 MSS=1460 SACK_PERM=1 TSval=117399288 TSecr=1082311203 WS=4
  3   0.000382 10.190.254.7 -> 10.177.132.15 TCP 68 40293 > 80 [ACK] Seq=1 Ack=1 Win=17920 Len=0 TSval=1082311203 TSecr=117399288
  4   0.000390 10.190.254.7 -> 10.177.132.15 HTTP 321 GET / HTTP/1.1
  5   0.000422 10.177.132.15 -> 10.190.254.7 TCP 68 80 > 40293 [ACK] Seq=1 Ack=254 Win=15552 Len=0 TSval=117399288 TSecr=1082311203
  6   0.017298 10.177.132.15 -> 10.190.254.7 HTTP 368 HTTP/1.1 200 OK  (text/html)
  7   0.017790 10.190.254.7 -> 10.177.132.15 TCP 68 40293 > 80 [ACK] Seq=254 Ack=301 Win=19456 Len=0 TSval=1082311205 TSecr=117399293
  8   5.032706 10.177.132.15 -> 10.190.254.7 TCP 68 80 > 40293 [FIN, ACK] Seq=301 Ack=254 Win=15552 Len=0 TSval=117400546 TSecr=1082311205
  9   5.063446 10.190.254.7 -> 10.177.132.15 TCP 68 40293 > 80 [ACK] Seq=254 Ack=302 Win=19456 Len=0 TSval=1082311710 TSecr=117400546

Analysis

Base on the tcpdumps from the server we can clearly see that the LB1 is changing the original source ip address. It means that our server can't directly relay on the original IP of the client. 

Looking further at the payload we can see that as the traffic is sent from LB to the pool member the load balancer inserts additional headers into the original GET request.

[root@crado1 tmp]# tshark -n -r client.pcap -V http
    GET / HTTP/1.1\r\n
        Request Method: GET
        Request URI: /
        Request Version: HTTP/1.1
    User-Agent: curl/7.15.5 (x86_64-redhat-linux-gnu) libcurl/7.15.5 OpenSSL/0.9.8b zlib/1.2.3 libidn/0.6.5\r\n
    Host: 31.222.175.142\r\n
    Accept: */*\r\n
    \r\n

    HTTP/1.1 200 OK\r\n
        Request Version: HTTP/1.1
        Response Code: 200
    Date: Mon, 26 Mar 2012 21:50:16 GMT\r\n
    Server: Apache/2.2.20 (Ubuntu)\r\n
    Last-Modified: Mon, 26 Mar 2012 21:29:38 GMT\r\n
    ETag: "7c030-2c-4bc2c1245f480"\r\n
    Accept-Ranges: bytes\r\n
    Content-Length: 44\r\n
        [Content length: 44]
    Vary: Accept-Encoding\r\n
    Content-Type: text/html\r\n
    \r\n
Line-based text data: text/html
    <html><body>It works! urado1\n
    </body></html>\n

root@urado1:~/tmp# tshark -n -r server-urado1.pcap -V http
    GET / HTTP/1.1\r\n
        [Expert Info (Chat/Sequence): GET / HTTP/1.1\r\n]
            [Message: GET / HTTP/1.1\r\n]
            [Severity level: Chat]
            [Group: Sequence]
        Request Method: GET
        Request URI: /
        Request Version: HTTP/1.1
    User-Agent: curl/7.15.5 (x86_64-redhat-linux-gnu) libcurl/7.15.5 OpenSSL/0.9.8b zlib/1.2.3 libidn/0.6.5\r\n
    X-Forwarded-For: 31.222.191.246\r\n
    Accept: */*\r\n
    X-Forwarded-Proto: http\r\n
    Host: 31.222.175.142\r\n
    X-Cluster-Client-Ip: 31.222.191.246\r\n
    \r\n
    [Full request URI: http://31.222.175.142/]

    HTTP/1.1 200 OK\r\n
        [Expert Info (Chat/Sequence): HTTP/1.1 200 OK\r\n]
            [Message: HTTP/1.1 200 OK\r\n]
            [Severity level: Chat]
            [Group: Sequence]
        Request Version: HTTP/1.1
        Status Code: 200
        Response Phrase: OK
    Date: Mon, 26 Mar 2012 21:50:16 GMT\r\n
    Server: Apache/2.2.20 (Ubuntu)\r\n
    Last-Modified: Mon, 26 Mar 2012 21:29:38 GMT\r\n
    ETag: "7c030-2c-4bc2c1245f480"\r\n
    Accept-Ranges: bytes\r\n
    Content-Length: 44\r\n
        [Content length: 44]
    Vary: Accept-Encoding\r\n
    Content-Type: text/html\r\n
    \r\n
Line-based text data: text/html
    It works! urado1\n
    \n

Summary

As the load balancer manipulates the IP addresses on the client and server site it still provides a method to identify the original source ip address of the client. There will be inserted a HTTP header X-Forwarded-For with the value of the original client IP address.