Wednesday 31 March 2010

Faster Payments - ISO 8583 Resilience

What is it?
Today I was asked to provide a resilient solution for a Point-to-Point system using the FasterPayments Service. I have to admit that my knowledge of FasterPayments was limited to begin with but a quick Google search revealed that it is none other that ISO8583 - http://en.wikipedia.org/wiki/ISO_8583 - and, the more I read, the more I realised this is BIG!

From the WikiPedia article referenced above, "The vast majority of transactions made at Automated Teller Machines use ISO 8583 at some point in the communication chain, as do transactions made when a customer uses a card to make a payment in a store. In particular, both the MasterCard and Visa networks base their authorization communications on the ISO 8583 standard, as do many other institutions and networks".

Two quick thoughts: 1) this is obviously quite important, 2) there must be a lot of organisations wanting to ensure the maximum uptime of services utilising ISO8583.

What do we know
ISO8583 messages:
1) Run over TCP/IP.
2) Work on a Request/Response message acknowledgment system
3) Have very short payloads: 1 packet = 1 message.
4) Use a bit value to identify what message type!!

The fourth point is what we can use to implement some service resilience.

Delivering a resilient solution requires knowledge of the application/service being delivered. ASCII-based protocols like HTTP and SMTP are simple for different reasons: a) most Application Delivery solutions have interpreters for HTTP, b) if you don't have an SMTP Interpreter, its text-based messaging.... a packet trace will reveal all.

In the case of ISO8583, its much more efficient and uses a numeric value to identify the message type instead of the Human readable format used by HTTP. Some examples:

Example 1: ASCII-based message: Try the following commands (NOTE: Press return twice after the 'GET' command):
telnet www.google.com 80
GET / HTTP/1.0

As you will see from this example, you can communicate with the Web Server in the same way that your web browser does. Telnet creates a TCP/IP Socket to the Web Server on TCP Port 80 and then the String "GET / HTTP/1.0" returns a valid HTTP Response.

The customer that raised the query has a number of F5 LTM's (Local Traffic Manager) installed. Adding resilience to their Credit Card transaction service is a matter of making the LTM aware of the service. This calls for a ISO8583 Service Monitor. This monitor will send a properly formatted ISO8583 'Echo Test' message and verify that it receives a properly formatted Echo Response.:

1) How to talk ISO8583:
Unfortunately, you can't just open up a TCP socket and enter a String like a HTTP monitor does. ISO8583 has its own set of special requirements. Fortunately, there is a python library for generating ISO8583 formatted messages which can be found here: http://code.google.com/p/iso8583py/

many thanks "igorvc"


uncomress files to /var/tmp
 cd /var/tmp/ISO8583 Module-1.1/
python setup.py build
mount -o rw,remount /usr
python setup.py install
mount -o ro,remount /usr



2) Provide your Big-IP the tools to construct an ISO8583 'echo' message:
a) install a python script onto your Big-IP in the /config/monitors/ directory named mon_ISO8583.py  and with the contents (Thanks again "igorvc"):






"""

(C) Copyright 2009 Igor V. Custodio

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.

"""


from ISO8583.ISO8583 import ISO8583
from ISO8583.ISOErrors import *
import socket
import sys
import time

# Configure the client
serverIP6 = sys.argv[1]
serverIP4 = serverIP6.lstrip("::ffff:")
serverPort = sys.argv[2]
numberEcho = 1
timeBetweenEcho = 0 # in seconds

bigEndian = True
#bigEndian = False

s = None
for res in socket.getaddrinfo(serverIP, serverPort, socket.AF_UNSPEC, socket.SOCK_STREAM):
    af, socktype, proto, canonname, sa = res
    try:
                s = socket.socket(af, socktype, proto)
    except socket.error, msg:
        s = None
        continue
    try:
                s.connect(sa)
    except socket.error, msg:
        s.close()
        s = None
        continue
    break
if s is None:
    print 'Could not connect :('
    sys.exit(1)
        
        
for req in range(0,numberEcho):
        iso = ISO8583()
        iso.setMTI('0800')
        iso.setBit(3,'300000')  
        iso.setBit(24,'045')    
        iso.setBit(41,'11111111')       
        iso.setBit(42,'222222222222222')        
        iso.setBit(63,'This is a Test Message')
        if bigEndian:
                try:
                        message = iso.getNetworkISO() 
                        s.send(message)
                        print 'Sending ... %s' % message
                        ans = s.recv(2048)
                        print "\nInput ASCII |%s|" % ans
                        isoAns = ISO8583()
                        isoAns.setNetworkISO(ans)
                        v1 = isoAns.getBitsAndValues()
                        for v in v1:
                                print 'Bit %s of type %s with value = %s' % (v['bit'],v['type'],v['value'])
                                
                        if isoAns.getMTI() == '0810':
                                print "OK"
                        else:
                                print "ERR"
                                        
                except InvalidIso8583, ii:
                        print ii
                        break   
                

                time.sleep(timeBetweenEcho)
                
        else:
                try:
                        message = iso.getNetworkISO(False) 
                        s.send(message)
                        print 'Sending ... %s' % message
                        ans = s.recv(2048)
                        print "\nInput ASCII |%s|" % ans
                        isoAns = ISO8583()
                        isoAns.setNetworkISO(ans,False)
                        v1 = isoAns.getBitsAndValues()
                        for v in v1:
                                print 'Bit %s of type %s with value = %s' % (v['bit'],v['type'],v['value'])
                                        
                        if isoAns.getMTI() == '0810':
                                print "\tThat's great !!! The server understand my message !!!"
                        else:
                                print "The server dosen't understand my message!"
                        
                except InvalidIso8583, ii:
                        print ii
                        break   
                
                time.sleep(timeBetweenEcho)
                
print 'Closing...'              
s.close()



b) Now, create a wrapper (LTM can't call python directly) called mon_ISO8583.sh with the contents:


#!/bin/sh
/usr/bin/python /config/monitors/mon_ISO8583.py ${1} ${2} | grep -i "OK" > /dev/null 2>&1

if [ $? -eq 0 ]
then 
echo "UP"
fi



c) Setup permissions
Now we setup the permissions by executing:
chmod 700 /config/monitors/mon_ISO8583.py
chmod 700 /config/monitors/mon_ISO8583.sh



3) Testing Comms
Execute:  /config/monitors/mon_ISO8583.sh ipAddress tcpPort <-- sub IP and Port with real values



You should receive "UP".

If you didn't receive "UP", you can get a lot more detail which might help in debugging by executing the pything script itself:
/usr/bin/python /config/monitors/mon_ISO8583.py ipAddress tcpPort <-- sub with real Addr/Port



One you have got this up and working you are ready to add the monitor to a Pool!!



4) Adding the monitor to a pool:
In your v10 Big-IP GUI, create a monitor of type 'external' and call it mon_ISO8583.

In the field 'External Program' enter:
mon_ISO8583.sh



Apply the monitor to your pool. Your pool should now turn Green.

You now have a solution that validates the ISO8583 service is operating and responding to messages ensuring that the customer transaction can be fulfilled. This solution goes above and beyond TCP Port checking systems which can reveal false positives.

4 comments:

  1. That's Great!
    Congratulation :)

    ReplyDelete
  2. The V10-Big-IP is referring to LoadBalancer or just another ordinary linux server?

    ReplyDelete
  3. Yes, the Big-IP does Load Balance but this is only a small part of it's functionality. I like to think if it as an Application Switch. Once you tell the Big-IP what the payload is, you can do anything with it.
    In the example above we are just configuring the Big-IP to understand/interpret Card Authorisations.
    Regarding Linux, no it's not just a Linux server. To get the sub-millisecond performance that Big-IP delivers it requires a combination of purpose built hardware, some very clever FPGA's and a fast microkernel architecture. But don't let that worry you. It's dead simple to manage.

    Hope that answers your questions!

    Best regards,
    Nathan

    ReplyDelete
  4. Hello, thanks por the post

    What is de "M" initial in outputs messages?

    ReplyDelete