PERFORCE change 166198 for review

Zachariah Riggle zjriggl at FreeBSD.org
Fri Jul 17 12:16:11 UTC 2009


http://perforce.freebsd.org/chv.cgi?CH=166198

Change 166198 by zjriggl at zjriggl_tcpregression on 2009/07/17 12:15:58

	Periodic checkin

Affected files ...

.. //depot/projects/soc2009/zjriggl_tcpregression/src/tcpregression/pcsextension/__init__.py#7 edit
.. //depot/projects/soc2009/zjriggl_tcpregression/src/tcpregression/pcsextension/decorators.py#3 edit
.. //depot/projects/soc2009/zjriggl_tcpregression/src/tcpregression/segmentBuffer.py#1 add
.. //depot/projects/soc2009/zjriggl_tcpregression/src/tcpregression/tcprecvdaemon.py#2 edit
.. //depot/projects/soc2009/zjriggl_tcpregression/src/tcpregression/tcpstatemachine.py#6 edit
.. //depot/projects/soc2009/zjriggl_tcpregression/src/tcpregression/tcpstates.py#3 edit

Differences ...

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


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

@@ -190,4 +190,86 @@
     fget=ops.get('fget',lambda self:getattr(self, name))
     fset=ops.get('fset',lambda self,value:setattr(self,name,value))
     fdel=ops.get('fdel',lambda self:delattr(self,name))
-    return property ( fget, fset, fdel, ops.get('doc','') )+    return property ( fget, fset, fdel, ops.get('doc',func.__doc__ or '') )
+
+def boundedInt(func):
+    '''
+    A bounded integer.  See @prop for syntax.
+    Set the 'max' field to set a wrap-around value.
+    
+    >>> class example(object):
+    ...  @boundedInt
+    ...  def n():
+    ...   return {'max': 10}
+    ...  _n = 0
+    ... 
+    >>> ex = example()
+    >>> l = []
+    >>> for i in range(0,20):
+    ...  l += [ex.n]
+    ...  ex.n += 1
+    ... 
+    >>> l
+    [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
+    '''
+    ops = func() or {}
+    upperBound = ops.get('max',1) # default to bound it to 2
+    wrapValue = upperBound + 1
+    
+    name=ops.get('prefix','_')+func.__name__ # property name
+    fget = lambda self: getattr(self,name) % upperBound
+    fset = lambda self,value: setattr(self,name, (value % wrapValue))
+    fdel = lambda self: delattr(self,value)
+    return property (fget, fset, fdel, ops.get('doc','') )
+
+def uint32(func):
+    max = 2**32    
+    ops = func() or {}
+    name=ops.get('prefix','_')+func.__name__ # property name
+    fget = lambda self: getattr(self,name)
+    fset = lambda self,value: setattr(self, name, (value % max))
+    fdel = lambda self: delattr(self,value)
+    return property (fget, fset, fdel, func.__doc__)
+
+def uint16(func):
+    max = 2**16
+    ops = func() or {}
+    name=ops.get('prefix','_')+func.__name__ # property name
+    fget = lambda self: getattr(self,name)
+    fset = lambda self,value: setattr(self, name, (value % max))
+    fdel = lambda self: delattr(self,value)
+    return property (fget, fset, fdel, func.__doc__)
+
+
+def uint(max=2**32):
+    '''
+    >>> from pcsextension.decorators import *
+    >>> lim = 5
+    >>> class A(object):
+    ...   @uint(lim)
+    ...   def x(): pass
+    ...   _x = 0
+    ... 
+    _x
+    5
+    >>> a = A()
+    >>> l = []
+    >>> for i in range(0,lim*2):
+    ...   a.x = i
+    ...   l += [a.x]
+    ... 
+    >>> print l
+    [0, 1, 2, 3, 4, 0, 1, 2, 3, 4]
+    '''
+    def unsignedInteger(func):
+        ops = func() or {}
+        name=ops.get('prefix','_')+func.__name__ # property name
+        
+        print name
+        print max
+        
+        fget = lambda self: getattr(self,name)
+        fset = lambda self,value: setattr(self, name, (value % max))
+        fdel = lambda self: delattr(self, value)
+        return property(fget, fset, fdel)
+    return unsignedInteger

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

@@ -7,7 +7,7 @@
 import threading
 import loggable
 import time
-from tcpstatemachine import TcpStateMachine
+# from tcpstatemachine import TcpStateMachine
 
 class TcpRecvDaemon( threading.Thread ):
     '''
@@ -16,83 +16,84 @@
     just creating a thread object, is to provide [1] separation of duties and
     [2] to ease further expansion.  Maybe later we want to change it to a process
     or something.
-    
-    The flow of packets is as follows:
-    
-    - Packet arrives on interface
-    - Packet is queued by PCAP library
-    - IF processing is disabled, sleep, and continue loop.
-    - Packet is pulled of PCAP queue by thread funning recvThread() method
-    - Packets are validated (i.e. ensure checksums) 
-    - Packet is pushed onto the 'queuedPackets' list if validated.
-    - The packet is inspected to see if its sequence number is the sequence number that
-      the TCP state machine was expecting.
-    -- IF the sequence # is NOT the expected sequence, continue loop.
-    -- The sequence # matches.  Iterate through all packets in queuedPackets.
-    --- If the iterated packet matches the next sequence #, send it to the 
-        '_handleRecvdPacket' method, and push it onto the 'queuedPackets' list.
-    
     '''
+#    The flow of packets is as follows:
+#    
+#    - Packet arrives on interface
+#    - Packet is queued by PCAP library
+#    - IF processing is disabled, sleep, and continue loop.
+#    - Packet is pulled of PCAP queue by thread funning recvThread() method
+#    - Packets are validated (i.e. ensure checksums) 
+#    - Packet is pushed onto the 'queuedPackets' list if validated.
+#    - The packet is inspected to see if its sequence number is the sequence number that
+#      the TCP state machine was expecting.
+#    -- IF the sequence # is NOT the expected sequence, continue loop.
+#    -- The sequence # matches.  Iterate through all packets in queuedPackets.
+#    --- If the iterated packet matches the next sequence #, send it to the 
+#        '_handleRecvdPacket' method, and push it onto the 'queuedPackets' list.
+#    
+#    '''
 
     def __init__( self, target ):
         self.log = loggable.tcplog( self )
         threading.Thread.__init__( self, None, self.recvThread, None, ( target, ) )
+        self.log.info('Starting receive thread for object %s' % repr(target))
 
-    # List of packets that have been pulled off the interface, but were received out-
-    # of-order.
-    queuedPackets = []
+#    # List of packets that have been pulled off the interface, but were received out-
+#    # of-order.
+#    queuedPackets = []
+#
+#    # List of in-order packets to be returned by calls to recv() by a test-writer
+#    orderedPackets = []
+#
+#    # List of in-order packets to be processed.
+#    packetsToBeProcessed = []
 
-    # List of in-order packets to be returned by calls to recv() by a test-writer
-    orderedPackets = []
-
-    # List of in-order packets to be processed.
-    packetsToBeProcessed = []
-
-    def recvThread( self, tx ):
+    def recvThread( self, t ):
         '''
         Takes a TCP State Machine object as an argument.  Performs the collection and organization of packets.
         '''
-        t = TcpStateMachine()
-
         # Only process packets as long as we are told to.
         while True:
 
             # If we are not supposed to be processing packets, DON'T PROCESS PACKETS.
             if not t.processPackets:
-                time.sleep( 0.5 )
+                time.sleep( 0.05 )
                 continue
 
             # Get the next packet from PCAP
             packet = t.recvRawTcp()
+            
+            # 'Arrive' it
+            t.segmentArrives(packet)
 
-            # Iterate over all of the packets in our list.  If we find ONE packet
-            # that is the 'next' packet that the TCP State Machine is expecting, then
-            # we have to re-process all of them. 
-            repeat = True
-            while repeat:
-                # By default, don't repeat.
-                repeat = False
-
-                # List of packets to remove.  We cannot modify the list of packets while
-                # iterating over it.
-                toRemove = []
-
-                # Iterate over the packets backwards.  We will almost always find the packet
-                # that we want at the back of the list, as it will be the most recently-added.
-                for packet in self.packets.__reversed__():
-
-                    # Found a match?
-                    if packet.sequence == tsm.rcv_nxt:
-                        # Handle it.
-                        t._handleRecvdPacket( packet )
-
-                        # Queue the item for removal
-                        toRemove.append( packet )
-
-                        # Have to re-iterate over everything.
-                        repeat = True
-
-
-                # Remove everything that needs to be removed
-                for packet in toRemove:
-                    self.packets.remove( packet )
+#            # Iterate over all of the packets in our list.  If we find ONE packet
+#            # that is the 'next' packet that the TCP State Machine is expecting, then
+#            # we have to re-process all of them. 
+#            repeat = True
+#            while repeat:
+#                # By default, don't repeat.
+#                repeat = False
+#
+#                # List of packets to remove.  We cannot modify the list of packets while
+#                # iterating over it.
+#                toRemove = []
+#
+#                # Iterate over the packets backwards.  We will almost always find the packet
+#                # that we want at the back of the list, as it will be the most recently-added.
+#                for packet in self.packets.__reversed__():
+#
+#                    # Found a match?
+#                    if packet.sequence == tsm.rcv_nxt:
+#                        # Handle it.
+#                        t.segmentArrives( packet )
+#                        # Queue the item for removal
+#                        toRemove.append( packet )
+#
+#                        # Have to re-iterate over everything.
+#                        repeat = True
+#
+#
+#                # Remove everything that needs to be removed
+#                for packet in toRemove:
+#                    self.packets.remove( packet )

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

@@ -6,7 +6,7 @@
 
 from loggable import tcplog
 from pcs.packets import ethernet, ipv4, tcp
-from pcsextension.decorators import prop, validateTypes
+from pcsextension.decorators import prop, validateTypes, uint16, uint32
 from pcsextension.hwAddress import HwAddress
 from pcsextension.ipAddress import IpAddress
 from pcsextension.networkPort import NetworkPort
@@ -25,6 +25,7 @@
 import testconfig
 import time
 from tcprecvdaemon import TcpRecvDaemon
+import timer
 
 # Valid state transitions, as defined by the diagram on RFC 793 pp. 23:
 #    September 1981                                                          
@@ -81,6 +82,40 @@
 #    
 #                                                                   [Page 23]
 
+def seq(x):
+    # It is essential to remember that the actual sequence number space is
+    # finite, though very large.  This space ranges from 0 to 2**32 - 1.
+    # Since the space is finite, all arithmetic dealing with sequence
+    # numbers must be performed modulo 2**32.  This unsigned arithmetic
+    # preserves the relationship of sequence numbers as they cycle from
+    # 2**32 - 1 to 0 again.
+    return x % (2**32)
+
+class sequenced(str):
+    '''
+    This class exists to encapsulate sequenced items in the TCP stream.  Each
+    byte (octet) is assigned a sequence number, as is the original SYN and final
+    FIN. 
+    '''
+    syn = False
+    fin = False
+    
+    def __str__(self):
+        if self.syn:
+            return "SYN"
+        if self.fin:
+            return "FIN"
+        
+        return str.__str__(self)
+    
+    def __repr__(self):
+        if self.syn:
+            return "SYN".__repr__()
+        if self.fin:
+            return "FIN".__repr__()
+        return str.__repr__(self)
+    
+
 class TcpStateMachine( object ):
     '''
     Enumerates the various states of a TCP connection as defined by RFC 793, 
@@ -117,55 +152,91 @@
     __connector = tcpFilter( testconfig.interface )
     __recvThread = None
 
-    snd_nxt = 0       # Next available send sequence #
-    snd_una = 0         # Unacknowledge send sequence #
-    snd_wnd = 128 * 1024  # Send window
-    snd_up = 0         # Seng urgent pointer
-    snd_wl1 = 0         # Sequence number used for last window update
-    snd_wl2 = 0         # Ack number used for last window update
-    iss = -1        # Initial sequence number
-
-    rcv_wnd = 128 * 1024  # Recv window size
-    rcv_up = 0         # Recv urgent pointer
-    irs = 0        # Initial receive sequence number
-    rcv_nxt = irs       # Expected next recv sequence #
+    @uint32
+    def snd_nxt(): '''Next sequence to be sent (SND.NXT) '''
+    _snd_nxt = 0
+    
+    @uint32
+    def snd_una(): ''' First (i.e. oldest) unacknowledged sequence (SND.UNA) '''
+    _snd_una = 0
+    
+    @uint16
+    def snd_wnd(): ''' Send window size (SND.WND) '''
+    _snd_wnd = 0
+        
+    @uint16
+    def snd_up(): ''' Send urgent pointer '''
+    _snd_up = 0
+    
+    @uint32
+    def snd_wl1(): ''' Sequence number used for last window update. ''' 
+    _snd_wl1 = 0
+    
+    @uint32
+    def snd_wl2(): ''' Ack number used for last window update '''
+    _snd_wl2 = 0
+    
+    @uint32
+    def iss(): ''' Initial Send Sequence (ISS) '''
+    _iss = 0
+    
+    @uint16
+    def rcv_wnd(): ''' Receive Window (RCV.WND) '''
+    _rcv_wnd = 2**16 - 1
+    
+    @uint16
+    def rcv_up(): ''' Receive Urgent Pointer '''
+    _rcv_up = 0
+    
+    @uint32
+    def irs(): ''' Initial Receive Sequence (IRS) '''
+    _irs = 0
+    
+    @uint32
+    def rcv_nxt(): ''' Sequence expected in next segment (RCV.NXT) '''
+    _rcv_nxt = 0
 
     msl = 2 * 60        # Maximum Segment Lifetime. Arbitrarily defined in the RFC to 2 minutes
     timeout = 2 * msl     # Timeout
 
-    # Flag used to stop the recv'er thread from processing additional packets.
-    processPackets = True
+    @prop 
+    def processPackets(): ''' Flag used to stop the recv'er thread from processing additional packets. '''
+    _processPackets = True
 
     # Ethernet stuff
     @prop
-    def localEthernet():
-        return {'doc': 'Local hardware ethernet address'}
+    def localEthernet(): ''' Local hardware ethernet address '''
     _localEthernet = HwAddress( default = testconfig.localMAC )
 
     @prop
-    def remoteEthernet():
-        return {'doc': 'Remote hardware ethernet address'}
-    remoteEthernet = HwAddress( default = testconfig.remoteMAC )
+    def remoteEthernet(): '''Remote hardware ethernet address'''
+    _remoteEthernet = HwAddress( default = testconfig.remoteMAC )
 
     @prop
-    def localIP():
-        return {'doc': 'Local IP address.'}
+    def localIP(): '''Local IP address.'''
     _localIP = IpAddress( default = testconfig.localIP )
 
     @prop
-    def remoteIP():
-        return {'doc': 'Remote IP address.' }
+    def remoteIP(): '''Remote IP address.'''
     _remoteIP = IpAddress( default = testconfig.remoteIP )
 
     @prop
-    def localPort():
-        return {'doc': 'Local port.'}
+    def localPort(): '''Local port.'''
     _localPort = NetworkPort( default = testconfig.localPort )
 
     @prop
-    def remotePort():
-        return {'doc': 'Remote port.'}
+    def remotePort(): '''Remote port.'''
     _remotePort = NetworkPort( default = testconfig.remotePort )
+    
+    # Interface is actually a shortcut to the connector's interface field,
+    # which is itself a property.  Setting a TcpStateMachine's interface will
+    # effectively trigger the tcpFilter object to switch interfaces to the 
+    # specified interface.
+    @prop
+    def interface(): 
+        '''Interface to use for sending/recving data'''
+        return {'fget': lambda self: self.__connector.interface,
+                'fset': lambda self,x: setattr(self.__connector,'interface',x)}
 
     def setLoopback( self, lb = True ):
         '''
@@ -176,16 +247,21 @@
         self.__constructor.loopback = lb
 
         # Override the interface
-        if lb and self.interface not in self.loopbackInterfaces:
+        if lb and (self.interface not in self.loopbackInterfaces):
             self.log.warn( 'Overriding interface to be %s' % self.loopbackInterfaces[0] )
-            self.interface = self.loopbackInterfaces[0]
+            
+            devs = (dev[i] for dev in pcap.findalldevs())
+            loInterface = (iface for iface in self.loopbackInterfaces if iface in devs)
+            
+            if len(loInterface) < 1:
+                self.log.error('cannot set loopback, could not identify any '
+                               ' loopback interfaces in available interfaces (%s) out '
+                               ' of known loopback interfaces (%s)' % (devs, self.loopbackInterfaces))
+            else :
+                # Select the first interface
+                self.interface = loInterface[0]
 
-            # If the connector is already active AND it is not on the loopback interface,
-            # re-open it on the loopback interface.
-            if self.__connector is not None and self.__connector.interface is not self.interface:
-                self.__connector = tcpFilter( self.interface )
-
-    # Used by setLoopback
+    # Used by setLoopback, this should be a list of all known loopback interface names.
     loopbackInterfaces = ['lo0', 'lo']
 
     @prop
@@ -193,12 +269,21 @@
         return {'doc': 'Maximum Tranmission Unit'}
     _mtu = testconfig.mtu
 
+    def isSynchronized(self):
+        '''
+        Is the connection in a synchronized state?
+        Return True if yes, otherwise False.
+        
+        @see tcpstates.synchronizedStates
+        '''
+        return self.state in synchronizedStates
+
     @prop
     def generate():
-        return {'doc': 'What fields of outgoing TCP packets should be auto-generated,'
-        ' and various packet-generation toggles.  Accepted Values:\n'
-        '%s - Outgoing packet TCP Checksum\n'
-        '%s - }
+        '''
+        Dictionary of fields of outgoing TCP packets should be auto-generated,
+        and various packet-generation toggles.
+        '''
     _generate = {tcp.f_checksum: True,
                 tcp.f_offset: True,
                 tcp.f_sequence: True,
@@ -210,12 +295,14 @@
 
     @prop
     def validate():
-        return {'doc': 'Fields to be validated.  Non - valid packets are dropped, non - valid'
-                'settings throw an error.  Accepted Values:\n'
-                ' % s - Incoming packet TCP Checksum\n'
-                ' % s - Incoming packet TCP Sequence Number\n'
-                ' % s - Incoming packet TCP Ack number\n'
-                ' % s - TCP State Machine transition\n'}
+        '''
+        Dictionary of fields to be validated.  Non - valid packets are dropped, non - valid
+        settings throw an error.  Accepted Values:
+        %s - Incoming packet TCP Checksum
+        %s - Incoming packet TCP Sequence Number
+        %s - Incoming packet TCP Ack number
+        %s - TCP State Machine transition
+        '''
     _validate = { tcp.f_checksum: True,
                 tcp.f_sequence: True,
                 tcp.f_ack_number: True,
@@ -223,36 +310,91 @@
                 #tcp.f_dport: True,
                 'transition': True }
 
-
+    def generateISS(self):
+        '''
+        Generates a new Initial Sequence Number (ISS).
+        '''
+        return seq(0)
+    
     @prop
-    def packetsToSend():
-        return {'doc': 'List of all packets to be sent.' }
-    _packetsToSend = []
+    def outboundSequences():
+        '''
+        List of all outbound sequences.  This includes data sent from ISS onward.
+        
+        The first sequence should be an instance of a 'sequenced' object, with syn=1.
+        The last sequence should be an instance of a 'sequenced' object, with fin=1.
+        
+        All other sequences can be either a string object, or a 'sequenced' object (which
+        is simply a string with a 'syn' and 'fin' property).
+        
+        The first item in the list is the sequence with SEQ=ISS (the SYN sequence).
 
+        Note that this implementation does not gracefully deal with wrap, i.e. when the
+        sequence number overflows 2**32.
+        '''
+    _outboundSequences = []
+    
     @prop
-    def packetsSent():
-        return {'doc': 'List of all packets the have been sent.' }
-    _packetsSent = []
-
+    # def recvd():
+    def inboundSequences():
+        '''
+        List of all received sequences.  This includes data recv'd fron IRS onward.
+        @see outboundSequences for more information.
+        '''
+    _inboundSequences = []
+    
     @prop
-    def packetsSentAcked():
-        return {'doc': 'List of all packets the have been sent, for which '
-                'an ACKnowledgement message has not been received.'}
-    _packetsSentAcked = []
-
+    def retransmissionQ():
+        '''
+        Sent data that has not been acknowledged.
+        '''
+        return {'fget': lambda self: self.outboundData[self.snd_una:]}
+        
     @prop
-    def packetsRecvd():
-        return {'doc': 'List of all packets the have been received, but have not been ACKnowledged.  '
-                'Upon receiving, a packet will be put into this buffer.  If its sequence number is rcv_nxt, '
-                'it is moved to packetsRecvdAcked, and rcv_next is updated.'}
-    _packetsRecvd = []
-    _packetsRecvdOffset = 0
+    def recvBuffer():
+        '''
+        Recv buffer of octets waiting for the user to call recv().
+        Note that this buffer will explicitly exclude all SYN and FIN sequences.
+        @see inboundSequences
+        '''
+        return {'fget':        
+        lambda self: 
+            [octet for octet in self.inboundSequences[seq( self._recvBufferOffset + self.irs ):] \
+             if type(octet) != sequenced or not (octet.syn or octet.ack) ] 
+        }
+        
+    @uint32
+    def _recvBufferOffset(): 
+        '''
+        Offset to the 'read' pointer in recvBuffer.  This offset is relative to IRS,
+        which is the first item in inboundSequences
+        ''' 
+    __recvBufferOffset = 0
 
-    @prop
-    def packetsRecvdAcked():
-        return {'doc': 'List of all packets the have been received, but have not '
-                'been ACKnowledged.'}
-    _packetsRecvdAcked = []
+#    @prop
+#    def packetsSent():
+#        return {'doc': 'List of all packets the have been sent.' }
+#    _packetsSent = []
+#
+#    @prop
+#    def packetsSentAcked():
+#        return {'doc': 'List of all packets the have been sent, for which '
+#                'an ACKnowledgement message has not been received.'}
+#    _packetsSentAcked = []
+#
+#    @prop
+#    def packetsRecvd():
+#        return {'doc': 'List of all packets the have been received, but have not been ACKnowledged.  '
+#                'Upon receiving, a packet will be put into this buffer.  If its sequence number is rcv_nxt, '
+#                'it is moved to packetsRecvdAcked, and rcv_next is updated.'}
+#    _packetsRecvd = []
+#    _packetsRecvdOffset = 0
+#
+#    @prop
+#    def packetsRecvdAcked():
+#        return {'doc': 'List of all packets the have been received, but have not '
+#                'been ACKnowledged.'}
+#    _packetsRecvdAcked = []
 
 
     @prop
@@ -260,6 +402,7 @@
         return {'fset': lambda self, x: self.setState( x ),
                 'doc': 'The current state of the TCP State Machine'}
     _state = CLOSED
+    _lastState = None   # Used to store the previous state.
 
     def status( self ):
         '''
@@ -282,9 +425,18 @@
         connName = 'Connection naming not supported'
         recvWindow = str( self.rcv_wnd )
         sendWindow = str( self.snd_wnd )
-        state = self.state.name
-        nbaAck = str( len( self.packetsSent ) )
-        nbpRecpt = str( len( self.packetsRecvd ) )
+        state = str(self.state)
+        nbaAck = len(self.retransmissionQ)
+        # TODO TODO TODO 
+        # This should be the number of octets that have been received, but have
+        # not been accounted for in an 'ACK' message to the other side.
+        # TODO TODO TODO
+        nbpRecpt = 0 
+        
+        # TODO TODO TODO
+        # Urgent state is not currently supported, and will be added later as
+        # tests call for its use.
+        # TODO TODO TODO
         urgState = ()
         precedence = 'Precedence not supported'
         security = 'Security not supported'
@@ -293,6 +445,11 @@
         return ( localSocket, remoteSocket, connName, recvWindow, sendWindow, state,
                 nbaAck, nbpRecpt, urgState, precedence, security, timeout )
 
+    def __repr__(self):
+        
+        return self.__class__.__name__ + '( (%s,%s), (%s,%s) )' % \
+            ( self.localIP, self.localPort, self.remoteIP, self.remotePort )
+
     def __str__( self ):
         '''
         Prints out the annotated status.
@@ -321,7 +478,6 @@
         '''
         Sets the current state of the state machine.
         '''
-        self.log.debug( state )
         # Quick bail-out
         if self.state == state:
             return
@@ -331,17 +487,20 @@
         # Is the state a valid state?
         if state in tcpStates:
 
-            if ( not self.validate['transition'] ) or ( state in self.state.next ):
+           #  if ( not self.validate['transition'] ) or ( state in self.state.next ):
             # if ( not validateTransition ) or ( state in self.state.next ):
-                action = "Setting"
-                if validateTransition:
-                    action = "Advancing"
+            action = "Setting"
+            if validateTransition:
+                action = "Advancing"
 
-                self.log.state( "%s state from %s to %s" % ( action, self.state, state ) )
-                self.__state = state
-            else:
-                self.log.state( "Attempted invalid state transition from %s to %s" %
-                           ( self.state, state ) )
+            self.log.state( "%s state from %s to %s" % ( action, self.state, state ) )
+            
+            self._lastState = self._state
+            self._state = state
+                
+            if not state in self.state.next:
+                self.log.state( "Performed non-valid state transition from %s to %s" %
+                           ( self._lastState, self.state ) )
         else:
             self.log.error( 'Attempted to change to invalid state %s' % state )
 
@@ -350,6 +509,8 @@
         Open the socket connection.  This is synonymous with the
         'connect' function used by normal UNIX sockets.
         '''
+        
+        # CLOSED STATE (i.e., TCB does not exist)
         if self.state == CLOSED or self.state == LISTEN:
             # "Create a new transmission control block (TCB) to hold connection
             # state information.  Fill in local socket identifier, foreign
@@ -357,34 +518,42 @@
             # information." 
             # Layman's Terms: Reset the connection state
             self.reset()
-
+            
             # "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
+            # (ISS) is selected. 
+            self.iss = self.generateISS()
+            
+            # A SYN segment of the form <SEQ=ISS><CTL=SYN> is sent.  
+            synPacket = self.newPacket( {tcp.f_syn:1, tcp.f_sequence: self.iss} )
+            self.sendPacket(synPackets)
+            
+            # Set SND.UNA to ISS, SND.NXT to ISS+1, enter SYN-SENT
             # state, and return."
-            # Layman's Terms: Send the SYN packet.  The snd.una and snd.nxt bits
-            # are handled by the above reset() call.
-            synPacket = self.newPacket( {tcp.f_syn:1} )
-            self.log.generated( "Sending generated SYN packet to initiate connection: %s" % repr( synPacket ) )
-            self.packetsToSend.append( synPacket )
-            self.sendQueuedPackets()
+            self.snd_una = self.iss
+            self.snd_nxt = self.iss + 1
+            
             self.state = SYN_SENT
+            
+            t = timer(5.0)
+            while self.state != ESTABLISHED and not t.expired():
+                self.sendPacket(synPackets)
+                time.sleep(0.5)
 
             # Recv the SYN-ACK packet.
-            start = time()
-            while self.state == SYN_SENT:
-                synAck = findTcpLayer( self.recv() )
-
-                self.log.debug( 'received packet %s' % repr( synAck ) )
-
-                if synAck.ack and synAck.syn and synAck.ack_number == ( synPacket.sequence + 1 ):
-                    self.log.info( 'received SYN/ACK packet' )
-                    self.state = ESTABLISHED
-
-                elif time() > ( start + testconfig.timeout ):
-                    self.log.info( 'open() timeout after %s seconds' % testconfig.timeout )
-                    return False
+#            start = time()
+#            while self.state == SYN_SENT:
+#                synAck = findTcpLayer( self.recv() )
+#
+#                self.log.debug( 'received packet %s' % repr( synAck ) )
+#
+#                if synAck.ack and synAck.syn and synAck.ack_number == ( synPacket.sequence + 1 ):
+#                    self.log.info( 'received SYN/ACK packet' )
+#                    self.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( {tcp.f_ack:1, 'seq': synPacket.sequence + 1, tcp.f_ack_number: synAck.ack_number + 1} )
@@ -392,9 +561,7 @@
             self.snd_nxt = synPacket.sequence + 1
             self.rcv_nxt = synAck.ack_number + 1
 
-            self.log.generated( "Sending generated ACK packet in response to SYN/ACK: %s" % repr( ackPacket ) )
-            self.packetsToSend.append( appPacket )
-            self.sendQueuedPackets()
+            
             self.state = ESTABLISHED
             return True
         else:
@@ -403,11 +570,14 @@
 
         # Default...
         return False
+    
+    def connect(self):
+        self.open()
+        
 
     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
@@ -415,17 +585,10 @@
         # 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.iss = 0
+        self.snd_una = 0
+        self.snd_nxt = 0
         self.snd_up = 0
         self.snd_wl2 = 0
         self.snd_wnd = 0xffff
@@ -434,10 +597,15 @@
         self.rcv_nxt = 0
         self.rcv_up = 0
         self.rcv_wnd = testconfig.recvWindow
+        
+        self.outboundData = []
+        self.inboundSequences = []
+        self._recvBufferOffset = 0
 
         self.state = CLOSED
 
-    def send( self, packet ):
+
+    def sendPacket( self, packet ):
         '''
         Inform the TCP State Machine about packets that have been transmitted.
         This is necessary to keep the state up - to - date.
@@ -490,7 +658,7 @@
             if tcpLayer.fin:
                 self.state = FIN_WAIT_1
 
-        if self.state in [FIN_WAIT_1, FIN_WAIT_2, CLOSING, LAST_ACK, TIME_WAIT]:
+        if self.state in (FIN_WAIT_1, FIN_WAIT_2, CLOSING, LAST_ACK, TIME_WAIT):
             self.log.error( 'connection closing' )
 
         # Send all queued packets
@@ -511,6 +679,73 @@
         self.__connector.write( packet.bytes )
 
         pass
+    
+    def send(self, data):
+        '''
+        Sends the specified data.
+        Returns -1 on errors, or else the number of bytes sent.
+        '''
+        if self.state == CLOSED:
+            self.log.error('connection does not exist')
+            return -1
+        
+        if self.state == LISTEN:
+            self.open()
+        
+        if self.state in (SYN_RECEIVED, SYN_SENT):
+            self.outboundSequences += [x for x in data]
+            
+        if self.state in (ESTABLISHED, CLOSE_WAIT):
+            pkt = self.newPacket({tcp.f_data: payload(data)})
+            self.snd_nxt += len(data)
+            
+            firstOctetSequence = seq(pkt.sequence)
+            lastOctetSequence = seq(self.snd_nxt - 1)
+            
+            
+            seq = pkt.sequence
+            end = seq + len(data)
+            
+            self.sendPacket(pkt)
+            
+            
+            self.retransmissionQ[seq:end] = [byte for byte in bytes]
+            
+            self.outboundData[firstOctetSequence : lastOctetSequence] =
+            
+            # TODO
+            # TODO
+            # TODO
+            # TODO
+            # TODO
+            # TODO
+            # TODO
+            # TODO
+            # TODO
+            # TODO
+            # TODO
+            # TODO
+            # TODO
+            # TODO
+            # TODO
+            # TODO
+            # TODO
+            # TODO
+            # TODO
+            # TODO
+            # TODO
+            # TODO
+            # TODO
+            # TODO
+                        
+            # This is a blocking call.  Sleep until all of the data has
+            # been acknowledged.
+            while self.snd_una <= end:
+                time.sleep(0.05)
+            
+        if self.state in (FIN_WAIT_1, FIN_WAIT_2, CLOSING, LAST_ACK, TIME_WAIT):
+            self.log.error('connection closing')                      
+        
 
     def sendQueuedPackets( self ):
         '''
@@ -584,7 +819,7 @@
         t = tcp.tcp()
 
         # Generate all the fields that are set up...
-        t.syn = t.fin = t.rst = t.push = t.ack = t.urgent = 0
+        t.syn = t.fin = t.reset = t.push = t.ack = t.urgent = 0
 
         # set the defaults.  don't generate the checksum yet.
         for field in self.tcpFieldsToGenerate():
@@ -609,21 +844,33 @@
         # self.log.debug( " % s % s" % ( fieldname, repr( packet ) ) )
         rv = None
         if fieldname == tcp.f_checksum:
+            # The checksum will always be the same
             rv = tcpChecksum( packet, src = self.localIP, dst = self.remoteIP )
         elif fieldname == tcp.f_window:
+            # This will always be true
             rv = self.snd_wl1
         elif fieldname == tcp.f_dport:
+            # This will always be true
             rv = self.remotePort.getInteger()
         elif fieldname == tcp.f_sport:
+            # This will always be true
             rv = self.localPort.getInteger()
         elif fieldname == tcp.f_sequence:
+            # This will always be true
             rv = self.snd_nxt
+        elif fieldname == tcp.f_ack:
+            # The ACK flag is *always* set, except on the VERY first SYN.
+            if self.state is not CLOSED:
+                rv = 1 
         elif fieldname == tcp.f_ack_number:
+            # Same with ack_num, ALWAYS set.
             rv = self.rcv_nxt
         elif fieldname == tcp.f_offset:
+            # The offset is constant until we support TCP options
             rv = 5
         elif fieldname == tcp.f_urg_pointer:
             # TODO
+            # URG is unsupported right now
             rv = 0
         else:
             self.log.warn( 'generateField not defined for %s' % fieldname )
@@ -647,14 +894,83 @@
 
         return sequence + ( dataLength - headerLength ) + 1
 
-    def ack( self, sequence ):
+    def sendAck(self, fields={}):
+        '''
+        Sends an acknowledgment packet:
+            <SEQ=SND.NXT><ACK=RCV.NXT><CTL=ACK>
+            
+        Optionally, specify field-override values.
+        '''
+        # self.snd_una = sequence + 1
+        
+        ackFields = {tcp.f_sequence: self.snd_nxt, 
+                     tcp.f_acknum: self.rcv_nxt,
+                     tcp.f_ack: 1}

>>> TRUNCATED FOR MAIL (1000 lines) <<<


More information about the p4-projects mailing list