Skip site navigation (1)Skip section navigation (2)
Date:      Thu, 25 Jun 2009 13:32:58 GMT
From:      Zachariah Riggle <zjriggl@FreeBSD.org>
To:        Perforce Change Reviews <perforce@FreeBSD.org>
Subject:   PERFORCE change 165159 for review
Message-ID:  <200906251332.n5PDWw23099229@repoman.freebsd.org>

next in thread | raw e-mail | index | archive | help
http://perforce.freebsd.org/chv.cgi?CH=165159

Change 165159 by zjriggl@zjriggl_tcpregression on 2009/06/25 13:32:01

	Periodic commit

Affected files ...

.. //depot/projects/soc2009/zjriggl_tcpregression/src/tcpregression/pcsextension/__init__.py#5 edit
.. //depot/projects/soc2009/zjriggl_tcpregression/src/tcpregression/pcsextension/ipAddress.py#3 edit
.. //depot/projects/soc2009/zjriggl_tcpregression/src/tcpregression/tcpFilter.py#5 edit
.. //depot/projects/soc2009/zjriggl_tcpregression/src/tcpregression/tcpstatemachine.py#4 edit
.. //depot/projects/soc2009/zjriggl_tcpregression/src/tcpregression/testconfig.py#5 edit

Differences ...

==== //depot/projects/soc2009/zjriggl_tcpregression/src/tcpregression/pcsextension/__init__.py#5 (text+ko) ====

@@ -9,49 +9,49 @@
 from pcs.packets.tcpv6 import tcpv6
 
 # Configure logging
-logging.config.fileConfig("logging.conf")
+logging.config.fileConfig( "logging.conf" )
 
-(logging.FIELD_CHANGE,
+( logging.FIELD_CHANGE,
  logging.RESPONSE_GENERATION,
  logging.PACKET_TRANSMIT,
  logging.PACKET_RECEIVED,
  logging.PACKET_SENT,
  logging.VALIDATE,
- logging.STATE_CHANGE) = range(logging.INFO-7, logging.INFO)
+ logging.STATE_CHANGE ) = range( logging.INFO - 7, logging.INFO )
 
-logging.addLevelName(logging.FIELD_CHANGE, "FIELD")
-logging.addLevelName(logging.RESPONSE_GENERATION, "RESPONSE")
-logging.addLevelName(logging.PACKET_TRANSMIT, "XMIT")
-logging.addLevelName(logging.PACKET_RECEIVED, "RECEIVED")
-logging.addLevelName(logging.PACKET_SENT, "SENT")
-logging.addLevelName(logging.VALIDATE, "VALIDATE")
-logging.addLevelName(logging.STATE_CHANGE, "STATE")
+logging.addLevelName( logging.FIELD_CHANGE, "FIELD" )
+logging.addLevelName( logging.RESPONSE_GENERATION, "GENERATE" )
+logging.addLevelName( logging.PACKET_TRANSMIT, "XMIT" )
+logging.addLevelName( logging.PACKET_RECEIVED, "RECEIVED" )
+logging.addLevelName( logging.PACKET_SENT, "SENT" )
+logging.addLevelName( logging.VALIDATE, "VALIDATE" )
+logging.addLevelName( logging.STATE_CHANGE, "STATE" )
 
 # Find the TCP layer in a packet.
-def findTcpLayer(packet):
-    return findPacketLayer(packet,tcp)
+def findTcpLayer( packet ):
+    return findPacketLayer( packet, tcp )
 
-def findIpLayer(packet):
-    return findPacketLayer(packet,ipv4)
+def findIpLayer( packet ):
+    return findPacketLayer( packet, ipv4 )
 
-def findPacketLayer(packet, _class):
+def findPacketLayer( packet, _class ):
     p = packet
     while p is not None:
-        if isinstance(p,_class):
+        if isinstance( p, _class ):
             return p
         p = p.data
     return None
-        
-        
-    
-def inet_lton(integer):
-    return struct.pack(">L",integer)
+
+
+
+def inet_lton( integer ):
+    return struct.pack( ">L", integer )
+
+def inet_ltoa( integer ):
+    return socket.inet_ntoa( inet_lton( integer ) )
 
-def inet_ltoa(integer):
-    return socket.inet_ntoa(inet_lton(integer))
-    
-def inet_ntol(byteString):
-    return struct.unpack(">L",byteString)[0]
+def inet_ntol( byteString ):
+    return struct.unpack( ">L", byteString )[0]
 
-def inet_atol(ipString):
-    return inet_ntol(socket.inet_aton(ipString))+def inet_atol( ipString ):
+    return inet_ntol( socket.inet_aton( ipString ) )

==== //depot/projects/soc2009/zjriggl_tcpregression/src/tcpregression/pcsextension/ipAddress.py#3 (text+ko) ====

@@ -52,7 +52,7 @@
         return htonl( unpack( "!L", self.nbo )[0] )
 
     def getPCS( self ):
-        return self.getNetworkInteger()
+        return self.getInteger()
 
     def setPCS( self, x ):
-        self.setNetworkInteger( x )
+        self.setInteger( x )

==== //depot/projects/soc2009/zjriggl_tcpregression/src/tcpregression/tcpFilter.py#5 (text+ko) ====

@@ -14,65 +14,68 @@
 from pcsextension.networkPort import NetworkPort
 from pcsextension import findIpLayer, findTcpLayer
 
-class tcpFilter(object):
+class tcpFilter( object ):
 
     log = None
     pcapHandle = None
     doRead = False
-    
-    def __init__(self, interfaceName):
-        self.log = tcplog(self)
-        self.openInterface(interfaceName)
+
+    def __init__( self, interfaceName ):
+        self.log = tcplog( self )
+        self.openInterface( interfaceName )
 
-    def openInterface(self, interfaceName):
+    def openInterface( self, interfaceName ):
         try:
-            self.pcapHandle = PcapConnector(interfaceName)
+            self.pcapHandle = PcapConnector( interfaceName )
             # self.pcapHandle = IP4Connector();
         except:
-            self.log.error("Could not open interface %s" % interfaceName)
-            
-    def read(self):
+            self.log.error( "Could not open interface %s" % interfaceName )
+
+    def read( self ):
         return self.pcapHandle.readpkt()
-        
-    def write(self,bytes):
-        self.pcapHandle.write(bytes,len(bytes))
-        
-    def readFilteredByIP(self, ip):
+
+    def write( self, bytes ):
+        self.pcapHandle.write( bytes, len( bytes ) )
+
+    def readFilteredByIP( self, ip ):
         """
         Reads packets until a packet is found going either to or from the specified IP address
         is discovered.  Returns the first matching packet.
         @param ip IpAddress or dotted-quad string
         @return A pcs.Packet object
         """
-        
+
 #        if isinstance(ipAddress,int):
 #            ipAddress = intToBytes(ipAddress)
 
         # If the IP address is a string ("127.0.0.1") or byte array ('\x7f\x00\x00\x01')
         # we need to convert it into an integer representation of the same.
-        if isinstance(ip,str):
+        if isinstance( ip, str ):
             tmp = IpAddress()
-            tmp.setAscii(ip)
+            tmp.setAscii( ip )
             ip = tmp
 
         srcIP = IpAddress()
         dstIP = IpAddress()
-        
+
         while True:
             packet = self.read()
-            
-            ipLayer = findIpLayer(packet)
-            
+
+            ipLayer = findIpLayer( packet )
+
             if ipLayer == None:
                 continue
-            
-            srcIP.setNetworkInteger(ipLayer.src)
-            dstIP.setNetworkInteger(ipLayer.dst)
-            
+
+            srcIP.setPCS( ipLayer.src )
+            dstIP.setPCS( ipLayer.dst )
+
+            print "Saw %s" % str( srcIP )
+            print "Saw %s" % str( dstIP )
+
             if ipLayer.src == ip.getPCS() or ipLayer.dst == ip.getPCS():
-                return packet            
-        
-    def readFilteredByTuple(self, ipAddress, port):
+                return packet
+
+    def readFilteredByTuple( self, ipAddress, port ):
         """
         Reads packets until a packet is found going either to or from [1] the specified
         IP address and [2] the specified port.  Returns the first matching packet.
@@ -81,21 +84,19 @@
         @return A pcs.Packet object.
         @see readFilteredByIP
         """
-        
-        if isinstance(port,int):
-            tmp = NetworkPort()
-            tmp.setInteger(port)
-            port = tmp
-        
+
+        if isinstance( port, int ):
+            port = NetworkPort( port )
+
         while True:
-            packet = self.readFilteredByIP(ipAddress)
-            
-            tcpLayer = findTcpLayer(packet)
+            packet = self.readFilteredByIP( ipAddress )
+
+            tcpLayer = findTcpLayer( packet )
             if tcpLayer == None:
                 continue
-            
+
             print tcpLayer.sport
             print port.getNetworkInteger()
-            
+
             if tcpLayer.sport == port.getInteger() or tcpLayer.dport == port.getInteger():
-                return packet+                return packet

==== //depot/projects/soc2009/zjriggl_tcpregression/src/tcpregression/tcpstatemachine.py#4 (text+ko) ====

@@ -11,10 +11,13 @@
 from pcsextension.ipAddress import IpAddress
 from pcsextension.networkPort import NetworkPort
 from pcsextension.pseudoipv4 import pseudoipv4, ipv4_cksum
+from pcsextension.checksum import tcpChecksum
+from random import randint
 from socket import IPPROTO_TCP
 from tcpFilter import tcpFilter
 from tcpConstructor import tcpConstructor
-from tcpstates import
+from tcpstates import *
+from time import time
 import binascii
 import binhex
 import pcs
@@ -123,7 +126,7 @@
 
     rcv_wnd = 128 * 1024  # Recv window size
     rcv_up = 0         # Recv urgent pointer
-    irs = -1        # Initial receive sequence number
+    irs = 0        # Initial receive sequence number
     rcv_nxt = irs       # Expected next recv sequence #
 
     ack_nxt = 0         # Next ACK number to send.
@@ -166,31 +169,27 @@
         return {'doc': 'Maximum Tranmission Unit'}
     _mtu = testconfig.mtu
 
+    @prop
     def generate():
         return {'doc': 'What fields of outgoing TCP packets should be auto-generated.'}
     _generate = {'cksum': True,
                 'off': True,
-                'seq': True,
-                'acknum': True,
+                'sequence': True,
+                'ack_number': True,
                 'sport': True,
                 'dport': True,
                 'window': True,
-                'urg': True,
-                'ack': True,
-                'syn': True,
-                'fin': True,
-                'rst': True,
-                'psh': True,
-                'urgp': True }
+                'urg_pointer': True }
 
     @prop
     def validate():
         return {'doc': 'What fields of incoming TCP packets should be validated.'}
     _validate = { 'cksum': True,
-                'seq': True,
-                'acknum': True,
+                'sequence': True,
+                'ack_number': True,
                 'sport': True,
-                'dport': True }
+                'dport': True,
+                'transition': True }
 
     @prop
     def packetsToSend():
@@ -336,10 +335,90 @@
 #        
 #        return self.state == state
 
+    def open( self ):
+        '''
+        Open the socket connection.  This is synonymous with the
+        'connect' function used by normal UNIX sockets.
+        S
+        '''
+        if self.state == CLOSED or self.state == LISTEN:
+            # Reset the connection state
+            self.reset()
+
+            # Send the SYN packet.
+            synPacket = self.newPacket( {'syn':1} )
+            self.log.generated( "Sending generated SYN packet to initiate connection: %s" % synPacket )
+            self.packetsToSend.append( synPacket )
+            self.sendQueuedPackets()
+            self.state = SYN_SENT
+
+            # Recv the SYN-ACK packet.
+            start = time()
+            while self.state == SYN_SENT:
+                synAck = self.recv()
+
+                if synAck.ack and synAck.syn and synAck.ack_number == synPacket.sequence:
+                    state = ESTABLISHED
+
+                elif time() < ( start + testconfig.timeout ):
+                    self.log.info( 'open() timeout after %s seconds' % testconfig.timeout )
+                    return False
+
+            # Send the ACK packet.
+            ackPacket = self.newPacket( {'ack':1} )
+            self.log.generated( "Sending generated ACK packet in response to SYN/ACK: %s" % ackPacket )
+            self.packetsToSend.append( appPacket )
+            self.sendQueuedPackets()
+            self.state = ESTABLISHED
+            return True
+        else:
+            self.log.error( "connection already exists" )
+            return False
+
+        # Default...
+        return False
+
+    def reset( self, iss = None ):
+        '''
+        Resets all of the internal variables, sets the state to CLOSED.
+        @param iss Override the default ISS.
+        '''
+        # ...if active and the foreign socket is
+        # specified, issue a SYN segment.  An initial send sequence number
+        # (ISS) is selected.  A SYN segment of the form <SEQ=ISS><CTL=SYN>
+        # is sent.  Set SND.UNA to ISS, SND.NXT to ISS+1, enter SYN-SENT
+        # state, and return.
+        self.log.state( "Resetting state" )
+        if iss is not None:
+            self.iss = iss
+        else:
+            if testconfig.randomISS:
+                self.iss = randint( 0, ( 2 ** 32 ) - 1 )
+            else:
+                self.iss = testconfig.staticISS
+            self.logGenerated( self, 'iss' )
+
+        self.snd_una = self.iss
+        self.snd_nxt = self.iss + 1
+        self.snd_up = 0
+        self.snd_wl1 = 0
+        self.snd_wl2 = 0
+        self.snd_wnd = 0xffff
+
+        self.rcv_nxt = 0
+        self.rcv_up = 0
+        self.rcv_wnd = testconfig.recvWindow
+
+        self.state = CLOSED
+
+    def recv( self ):
+        self._connector.readFilteredByTuple( self.localIP, self.localPort )
+
     def send( self, packet ):
         '''
         Inform the TCP State Machine about packets that have been transmitted.
         This is necessary to keep the state up-to-date.
+        @param packet pcs.Packet object to send.  Must be, or contain, a TCP packet.
         '''
         # Check the arg type
         validateTypes( {packet:pcs.Packet} )
@@ -349,28 +428,47 @@
         if tcpLayer == None:
             self.log.error( "Could not find TCP layer in packet %s" % packet )
 
-        # Update list of packets sent
-        self.packetsSent += [tcpLayer]
+        # Check the state
+        if self.state == CLOSED:
+            #  Otherwise, return "error:  connection does not exist".
+            self.log.error( "connection does not exist" )
+
+        if self.state == LISTEN:
+            # If the foreign socket is specified, then change the connection
+            # from passive to active, select an ISS.  Send a SYN segment, set
+            # SND.UNA to ISS, SND.NXT to ISS+1.  Enter SYN-SENT state.  Data
+            # associated with SEND may be sent with SYN segment or queued for
+            # transmission after entering ESTABLISHED state.
+            self.open()
+
+        if self.state == SYN_SENT or self.state == SYN_RECEIVED:
+            # Queue the data for transmission after entering ESTABLISHED state.
+            # If no space to queue, respond with "error:  insufficient
+            # resources".
+            self.packetsToSend.append( tcpLayer )
 
-        # Get the bytes...
-        bytes = tcpLayer.chain().bytes
+        if self.state == ESTABLISHED or self.state == CLOSE_WAIT:
+            # Segmentize the buffer and send it with a piggybacked
+            # acknowledgment (acknowledgment value = RCV.NXT).  If there is
+            # insufficient space to remember this buffer, simply return "error:
+            # insufficient resources".
+            if self.generate['ack_number']:
+                packet.ack_number = self.rcv_nxt
 
-        # Get the data length.
-        dataLength = ( len( bytes ) - tcpLayer.off )
+            # If the urgent flag is set, then SND.UP <- SND.NXT-1 and set the
+            # urgent pointer in the outgoing segments.
+            # TODO: Handle URG pointer.
+            self.packetsToSend.append( tcpLayer )
 
-        # Log packet
-        self.log.pktsent( packet )
+            # Are we closing the connection?
+            if tcpLayer.fin:
+                self.state = FIN_WAIT_1
 
-        # Set the correct sequence number.
-        x = self.snd_nxt
-        if self.validate['sequence']:
-        # if self.isValidateSeqEnabled():
-            tcpLayer.seq = self.snd_nxt
-            self.snd_nxt += len( tcpLayer.data.chain() )
+        if self.state in [FIN_WAIT_1, FIN_WAIT_2, CLOSING, LAST_ACK, TIME_WAIT]:
+            self.log.error( 'connection closing' )
 
-        # Add the packet to the outgoing queue.
-        self.packetsToSend += [tcpLayer]
-        self._sendPackets()
+        # Send all queued packets
+        self.sendQueuedPackets()
 
     def sendRawTcp( self, tcpLayer ):
         if not validateTypes( {tcpLayer:tcp.tcp} ):
@@ -382,35 +480,35 @@
 
         pass
 
-    def _sendPackets( self ):
+    def sendQueuedPackets( self ):
         for tcpLayer in self.packetsToSend:
             send = False
-            # If the packet is the next packet that we are supposed to send,
-            # and it is LARGER than the window size, send it.
-            if self.snd_una == tcpLayer.seq:
-                send = True
-            else:
-                packetLen = len( tcpLayer.chain().bytes )
-                if tcpLayer.seq + packetLen > ( self.snd_una + self.snd_wnd ):
-                    send = True
+            # If the packet is the next packet that we are supposed to send, send it.
+            if self.snd_una == tcpLayer.sequence:
+                # Make sure we are allowed to send it (i.e. smaller than rcv window)
 
-            if send:
                 # Set the ACK to acknowledge any packets that we've received
-                if self.generate['acknum'] and self.ack_nxt > self.ack_lst:
+                if self.generate['ack_number'] and self.ack_nxt > self.ack_lst:
                 # if self.isAutomaticAckEnabled() and self.ack_nxt > self.ack_lst:
-                    tcpLayer.acknum = self.rcv_nxt
+                    tcpLayer.ack_number = self.rcv_nxt
                     tcpLayer.ack = 1
 
                 # Set the correct checksum
                 if self.generate['checksum']:
                 # if self.isChecksumValidationEnabled():
-                    tcpLayer.cksum = tcpChecksum( tcpLayer ) #self.generateChecksum( tcpLayer )
+                    tcpLayer.cksum = tcpChecksum( tcpLayer, src = self.localIP, dst = self.remoteIP ) #self.generateChecksum( tcpLayer )
 
                 self.sendRawTcp( tcpLayer )
 
     def logGenerated( self, packet, fieldname ):
         self.log.generated( "Automatically set field %s to %s" % ( fieldname, hex( getattr( packet, fieldname ) ) ) )
 
+    def logField( self, packet, fieldname ):
+        self.log.field( "Set field %s to %s" % ( fieldname, hex( getattr( packet, fieldname ) ) ) )
+
+    def fieldsToGenerate( self ):
+        return [k for k in self.generate.keys() if self.generate[k]]
+
     def newPacket( self, fields = None ):
         '''
         Creates a new packet with the specified fields.  Any fields that
@@ -421,57 +519,55 @@
         t = tcp.tcp()
 
         # Generate all the fields that are set up...
-        t.syn = t.fin = t.rst = t.psh = t.ack = t.urg = 0
+        t.syn = t.fin = t.rst = t.push = t.ack = t.urgent = 0
 
-        # Get the defaults...
-        defaults = {'window': 0xffff,
-                    'dport': self.remotePort.getInteger(),
-                    'sport': self.localPort.getInteger(),
-                    'seq': self.snd_nxt,
-                    'acknum': self.rcv_nxt,
-                    'off': 5}
+        # set the defaults.  don't generate the checksum yet.
+        for field in [k for k in self.fieldsToGenerate() if k != 'cksum']:
+            setattr( t, field, self.generateField( t, field ) )
+            self.logGenerated( t, field )
+             #   setattr( t, field, value )
 
-        # set the defaults
-        for ( field, value ) in defaults.items():
-            if field in self.generate:
+        # set the user-provided values
+        if fields is not None:
+            for ( field, value ) in fields.items():
                 setattr( t, field, value )
-                self.logGenerated( t, field )
+                self.logField( t, field )
 
-        # set the user-provided values
-        for ( field, value ) in fields.items():
-            setattr( t, field, value )
-
-        # Do the checksum LAST
+        # Do the checksum after user-provided values
         if 'cksum' in self.generate:
-            t.cksum = tcpChecksum( t )
+            t.cksum = tcpChecksum( t, src = self.localIP, dst = self.remoteIP )
             self.logGenerated( t, 'cksum' )
 
-    def generateChecksum( self, packet ):
-        """Calculate and store the checksum for the TCP segment
-           when encapsulated as an IPv4 payload with the given header."""
-        raise NotImplementedError
+        return t
 
-        bytes = packet.chain().bytes
-        pip = pseudoipv4()
-        pip.src = self.localIP
-        pip.dst = self.remoteIP
-        pip.protocol = IPPROTO_TCP
-        pip.length = len( bytes )
-        tmpbytes = pip.getbytes() + bytes
+    def generateField( self, packet, fieldname ):
+        if fieldname == 'cksum':
+            return tcpChecksum( packet, src = self.localIP, dst = self.remoteIP )
+        if fieldname == 'window':
+            return self.snd_wl1
+        if fieldname == 'dport':
+            return self.remotePort.getInteger()
+        if fieldname == 'sport':
+            return self.localPort.getInteger()
+        if fieldname == 'sequence':
+            return self.snd_nxt
+        if fieldname == 'ack_number':
+            return self.rcv_nxt
+        if fieldname == 'off':
+            return 5
+        if fieldname == 'urg_pointer':
+            # TODO
+            return 0
 
-        cksum = ipv4_cksum( tmpbytes )
 
-        # Log the checksum
-        hexCksum = binascii.hexlify( struct.pack( "!H", cksum ) )
-        self.log.debug( "Generated cksum: %s" % hexCksum )
-
-        return cksum
-
-    def recv( self, packet ):
+    def recv( self, packet = None ):
         '''
         Inform the TCP State Machine about packets that have been received.
         This is necessary to keep the state up-to-date. 
         '''
+        if packet == None:
+            packet = self._connector.readFilteredByTuple( self.remoteIP, self.remotePort )
+
         self.packetsRecvd += [packet]
         self.log.pktrecv( packet )
 
@@ -563,3 +659,4 @@
 #        self.packetsToSend = []
 
         self._connector = tcpFilter( testconfig.interface )
+        self.reset()

==== //depot/projects/soc2009/zjriggl_tcpregression/src/tcpregression/testconfig.py#5 (text+ko) ====

@@ -1,10 +1,11 @@
-localPort = 46001
-localIP = "127.0.0.1"
+
+localPort = 54321
+localIP = "172.16.0.10"
 localIPv6 = "::1"
 localMAC = "00:1b:63:06:82:b2"
 
-remotePort = 46002
-remoteIP = "127.0.0.1"
+remotePort = 12345
+remoteIP = "172.16.0.10"
 remoteIPv6 = "::1"
 remoteMAC = "00:21:29:a5:a9:3f"
 
@@ -12,3 +13,14 @@
 
 interface = "en1"
 
+# Timeout for connect operations, in seconds
+timeout = 5
+
+# Receive window size
+recvWindow = 0xffff
+
+# Initial sequence number randomization.  If randomISS is true,
+# the ISS is initialized to a random value.  Otherwise, staticISS
+# is used as the ISS for all communication.
+randomISS = True
+staticISS = 0



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?200906251332.n5PDWw23099229>