Skip site navigation (1)Skip section navigation (2)
Date:      Fri, 10 Jul 2009 01:35:43 GMT
From:      Zachariah Riggle <zjriggl@FreeBSD.org>
To:        Perforce Change Reviews <perforce@FreeBSD.org>
Subject:   PERFORCE change 165891 for review
Message-ID:  <200907100135.n6A1Zh1W033991@repoman.freebsd.org>

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

Change 165891 by zjriggl@zjriggl_tcpregression on 2009/07/10 01:35:37

	Just a random commit.  Realized that I hadn't in a while...

Affected files ...

.. //depot/projects/soc2009/zjriggl_tcpregression/src/tcpregression/echoServer.py#4 edit
.. //depot/projects/soc2009/zjriggl_tcpregression/src/tcpregression/loggable.py#4 edit
.. //depot/projects/soc2009/zjriggl_tcpregression/src/tcpregression/logging.conf#4 edit
.. //depot/projects/soc2009/zjriggl_tcpregression/src/tcpregression/pcsextension/StringField.py#2 edit
.. //depot/projects/soc2009/zjriggl_tcpregression/src/tcpregression/pcsextension/__init__.py#6 edit
.. //depot/projects/soc2009/zjriggl_tcpregression/src/tcpregression/pcsextension/backup.tar#2 edit
.. //depot/projects/soc2009/zjriggl_tcpregression/src/tcpregression/pcsextension/decorators.py#2 edit
.. //depot/projects/soc2009/zjriggl_tcpregression/src/tcpregression/pcsextension/hwAddress.py#3 edit
.. //depot/projects/soc2009/zjriggl_tcpregression/src/tcpregression/pcsextension/ipAddress.py#4 edit
.. //depot/projects/soc2009/zjriggl_tcpregression/src/tcpregression/pcsextension/networkPort.py#3 edit
.. //depot/projects/soc2009/zjriggl_tcpregression/src/tcpregression/pcsextension/payload.py#3 edit
.. //depot/projects/soc2009/zjriggl_tcpregression/src/tcpregression/pcsextension/pseudoipv4.py#2 edit
.. //depot/projects/soc2009/zjriggl_tcpregression/src/tcpregression/sniffLocalhost.py#4 edit
.. //depot/projects/soc2009/zjriggl_tcpregression/src/tcpregression/tcpConstructor.py#5 edit
.. //depot/projects/soc2009/zjriggl_tcpregression/src/tcpregression/tcpFilter.py#6 edit
.. //depot/projects/soc2009/zjriggl_tcpregression/src/tcpregression/tcpHandshake.py#4 delete
.. //depot/projects/soc2009/zjriggl_tcpregression/src/tcpregression/tcpstatemachine.py#5 edit
.. //depot/projects/soc2009/zjriggl_tcpregression/src/tcpregression/tcpstates.py#2 edit
.. //depot/projects/soc2009/zjriggl_tcpregression/src/tcpregression/test.html#4 edit
.. //depot/projects/soc2009/zjriggl_tcpregression/src/tcpregression/testconfig.py#6 edit

Differences ...

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


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

@@ -1,7 +1,31 @@
 import logging
 import pcsextension
+import inspect
+
+# Configure logging
+logging.config.fileConfig( "logging.conf" )
+
+( 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.addLevelName( logging.FIELD_CHANGE, "FIELD" )
+logging.addLevelName( logging.RESPONSE_GENERATION, "\033[1;36mGENERATE\033[1;m" )
+logging.addLevelName( logging.PACKET_TRANSMIT, "XMIT" )
+logging.addLevelName( logging.PACKET_RECEIVED, "\033[1;31mRECVD\033[1;m" )
+logging.addLevelName( logging.PACKET_SENT, "\033[1;31mSENT\033[1;m" )
+logging.addLevelName( logging.VALIDATE, "VALIDATE" )
+logging.addLevelName( logging.STATE_CHANGE, "STATE" )
+
+# print '\033[1;36mGENERATE\033[1;m'
+# print '\033[1;36mCyan like Caribbean\033[1;m'
+# '\033[1;31mRed like Radish\033[1;m'
 
-class tcplog(object):
+class tcplog( object ):
     '''
     Provides rapid access to logging mechanisms for derived classes.
     
@@ -36,22 +60,24 @@
     >>> a.log.field('test')
     2009-05-24 12:48:52,475 - __main__.A - FIELD_CHANGE - test
     '''
-    
-    def __init__(self,parent):
+
+    def __init__( self, parent ):
         # self._log = logging.getLogger(parent.__class__.__module__ + '.' + parent.__class__.__name__)
-        self._log = logging.getLogger(parent.__class__.__name__)
-        
-    debug = lambda self,x: self._log.debug(x)
-    info = lambda self,x: self._log.info(x)
-    error = lambda self,x: self._log.error(x)
-    critical = lambda self,x: self._log.critical(x)
-    fatal = lambda self,x: self._log.fatal(x)
-    warning = lambda self,x: self._log.warning(x)
-    warn = lambda self,x: self._log.warn(x)
-    state = lambda self,x: self._log.log(logging.STATE_CHANGE, x)
-    validate = lambda self,x: self._log.log(logging.VALIDATE, x)
-    pktsent  = lambda self,x: self._log.log(logging.PACKET_SENT, x)
-    pktrecv  = lambda self,x: self._log.log(logging.PACKET_RECEIVED, x)
-    generated = lambda self,x: self._log.log(logging.RESPONSE_GENERATION, x)
-    field = lambda self,x: self._log.log(logging.FIELD_CHANGE, x)
-    +        self._log = logging.getLogger( parent.__class__.__name__ )
+
+    def caller( self ):
+        return inspect.stack()[2][3]
+
+    debug = lambda self, x: self._log.debug( "%s - %s" % ( self.caller(), x ) )
+    info = lambda self, x: self._log.info( x )
+    error = lambda self, x: self._log.error( x )
+    critical = lambda self, x: self._log.critical( x )
+    fatal = lambda self, x: self._log.fatal( x )
+    warning = lambda self, x: self._log.warning( x )
+    warn = lambda self, x: self._log.warn( x )
+    state = lambda self, x: self._log.log( logging.STATE_CHANGE, x )
+    validate = lambda self, x: self._log.log( logging.VALIDATE, x )
+    pktsent = lambda self, x: self._log.log( logging.PACKET_SENT, x )
+    pktrecv = lambda self, x: self._log.log( logging.PACKET_RECEIVED, x )
+    generated = lambda self, x: self._log.log( logging.RESPONSE_GENERATION, x )
+    field = lambda self, x: self._log.log( logging.FIELD_CHANGE, x )

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


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


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

@@ -8,25 +8,6 @@
 from pcs.packets.tcp import tcp
 from pcs.packets.tcpv6 import tcpv6
 
-# Configure logging
-logging.config.fileConfig( "logging.conf" )
-
-( 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.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 )
@@ -35,6 +16,11 @@
     return findPacketLayer( packet, ipv4 )
 
 def findPacketLayer( packet, _class ):
+    # Quick return if there's an exact match
+    if type( packet ) == _class:
+        return packet
+
+    # Otherwise search through the layers
     p = packet
     while p is not None:
         if isinstance( p, _class ):

==== //depot/projects/soc2009/zjriggl_tcpregression/src/tcpregression/pcsextension/backup.tar#2 (binary) ====


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


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


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


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


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


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


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


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

@@ -10,10 +10,12 @@
 from pcs.packets.ipv4 import ipv4
 from pcs.packets.tcp import tcp
 from pcs.packets.ethernet import *
+from pcs.packets.localhost import *
 from socket import IPPROTO_IPV4, IPPROTO_IPV6, IPPROTO_TCP
 from pcsextension.ipAddress import IpAddress
 from pcsextension.hwAddress import HwAddress
-from pcsextension.checksum import *
+from pcsextension.checksum import * 
+from loggable import tcplog
 
 class tcpConstructor:
     '''
@@ -23,7 +25,11 @@
     - IPv4
     TODO IPv6
     '''
+
+    loopback = False
+
     ipVersion = IPPROTO_IPV4
+
     localIP = IpAddress()
     remoteIP = IpAddress()
 
@@ -31,6 +37,8 @@
     remoteHw = HwAddress()
 
     def __init__( self ):
+        self.log = tcplog( self )
+
         self.localIP.setAscii( testconfig.localIP )
         self.remoteIP.setAscii( testconfig.remoteIP )
 
@@ -81,10 +89,10 @@
         ip = ipv4()
         ip.version = 4
         ip.hlen = 5
-        ip.tos = 0  # Normal
+        ip.tos = 0x10  # Normal
         ip.length = 20
         ip.id = randint( 0, 65535 ) # See RFC4413, pp. 20
-        ip.flags = 0x4 # 'Dont fragment'
+        ip.flags = 2 # 'Dont fragment'
         ip.ttl = 64
         ip.protocol = IPPROTO_TCP
         ip.src = self.localIP.getPCS()
@@ -121,6 +129,20 @@
         ether.dst = self.remoteHw.getPCS()
         return ether
 
+    def generateLoopback( self ):
+        lb = localhost()
+        if self.ipVersion == IPPROTO_IPV6:
+            lb.type = socket.htonl( socket.AF_INET6 )
+        else:
+            lb.type = socket.htonl( socket.AF_INET )
+        return lb
+
+    def generateHardware( self ):
+        if self.loopback:
+            return self.generateLoopback()
+        else:
+            return self.generateEthernet()
+
     def generateChain( self, tcpPacket ):
         '''
         Generates a pcs.Chain complete with Ethernet and IP levels, that uses the provided
@@ -129,14 +151,17 @@
         perform any validation of auto-generation of ANY TCP fields.  The length of the
         TCP header + data is necessary to set the IP length field.
         '''
-        ether = self.generateEthernet()
+        hw = self.generateHardware()
         ip = self.generateIP()
 
-        ether.data = ip
+        hw.data = ip
         ip.data = tcpPacket
 
         # Set the proper IP length and checksum
         ip.length = ip.length + len( tcpPacket.chain().bytes )
         ip.checksum = ipChecksum( ip )
 
-        return ether.chain()
+        chain = hw.chain()
+        self.log.generated( repr( chain ) )
+
+        return chain

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

@@ -13,28 +13,45 @@
 from pcsextension.ipAddress import IpAddress
 from pcsextension.networkPort import NetworkPort
 from pcsextension import findIpLayer, findTcpLayer
-
+import pprint
 class tcpFilter( object ):
 
     log = None
     pcapHandle = None
     doRead = False
 
+    @prop
+    def interface():
+        return {'doc': 'Interface used with the TCP Filter.  If this interface is different from the'
+        ' current interface, the current one is closed, and the new one is opened automatically.',
+                'fset': lambda self, x: self.openInterface( x ) }
+    self._interface = None
+
     def __init__( self, interfaceName ):
         self.log = tcplog( self )
-        self.openInterface( interfaceName )
+        self.interface = interfaceName
 
     def openInterface( self, interfaceName ):
+        # Is it already opened with this interface?
+        if self.interface is interfaceName:
+            log.info( 'Tried to re - open same interface: % s' % self.interface )
+            return
+        else:
+            self._interface = interfaceName
+
+        # Open the interface
         try:
-            self.pcapHandle = PcapConnector( interfaceName )
+            self.pcapHandle = PcapConnector( self.interface )
+            self.log.info( "Opened %s" % self.interface )
             # self.pcapHandle = IP4Connector();
         except:
-            self.log.error( "Could not open interface %s" % interfaceName )
+            self.log.error( "Could not open interface %s" % self.interface )
 
     def read( self ):
         return self.pcapHandle.readpkt()
 
     def write( self, bytes ):
+        self.log.pktsent( pprint.pformat( bytes ) )
         self.pcapHandle.write( bytes, len( bytes ) )
 
     def readFilteredByIP( self, ip ):
@@ -55,9 +72,6 @@
             tmp.setAscii( ip )
             ip = tmp
 
-        srcIP = IpAddress()
-        dstIP = IpAddress()
-
         while True:
             packet = self.read()
 
@@ -66,11 +80,8 @@
             if ipLayer == None:
                 continue
 
-            srcIP.setPCS( ipLayer.src )
-            dstIP.setPCS( ipLayer.dst )
-
-            print "Saw %s" % str( srcIP )
-            print "Saw %s" % str( dstIP )
+            # srcIP.setPCS( ipLayer.src )
+            # dstIP.setPCS( ipLayer.dst )
 
             if ipLayer.src == ip.getPCS() or ipLayer.dst == ip.getPCS():
                 return packet
@@ -90,13 +101,6 @@
 
         while True:
             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():
+            if tcpLayer and ( tcpLayer.sport == port.getInteger() or tcpLayer.dport == port.getInteger() ):
                 return packet

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

@@ -12,6 +12,7 @@
 from pcsextension.networkPort import NetworkPort
 from pcsextension.pseudoipv4 import pseudoipv4, ipv4_cksum
 from pcsextension.checksum import tcpChecksum
+from multiprocessing import Process, Queue
 from random import randint
 from socket import IPPROTO_TCP
 from tcpFilter import tcpFilter
@@ -22,6 +23,8 @@
 import binhex
 import pcs
 import testconfig
+import time
+from tcprecvdaemon import TcpRecvDaemon
 
 # Valid state transitions, as defined by the diagram on RFC 793 pp. 23:
 #    September 1981                                                          
@@ -110,11 +113,9 @@
     3
     '''
 
-    _constructor = tcpConstructor()
-    _connector = tcpFilter( testconfig.interface )
-
-    def generateInitialSequence( self ):
-        return 0
+    __constructor = tcpConstructor()
+    __connector = tcpFilter( testconfig.interface )
+    __recvThread = None
 
     snd_nxt = 0       # Next available send sequence #
     snd_una = 0         # Unacknowledge send sequence #
@@ -129,17 +130,19 @@
     irs = 0        # Initial receive sequence number
     rcv_nxt = irs       # Expected next recv sequence #
 
-    ack_nxt = 0         # Next ACK number to send.
-    ack_lst = 0         # Last ACK number sent.
-
     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
+
     # Ethernet stuff
+    @prop
     def localEthernet():
         return {'doc': 'Local hardware ethernet address'}
     _localEthernet = HwAddress( default = testconfig.localMAC )
 
+    @prop
     def remoteEthernet():
         return {'doc': 'Remote hardware ethernet address'}
     remoteEthernet = HwAddress( default = testconfig.remoteMAC )
@@ -164,6 +167,27 @@
         return {'doc': 'Remote port.'}
     _remotePort = NetworkPort( default = testconfig.remotePort )
 
+    def setLoopback( self, lb = True ):
+        '''
+        Call with lb=True to omit the ethernet layer with a loopback layer in its place.
+        This should be done when using lo0 instead of eth0, or the source and destination
+        IP address are the same. 
+        '''
+        self.__constructor.loopback = lb
+
+        # Override the interface
+        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]
+
+            # 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
+    loopbackInterfaces = ['lo0', 'lo']
+
     @prop
     def mtu():
         return {'doc': 'Maximum Tranmission Unit'}
@@ -171,26 +195,35 @@
 
     @prop
     def generate():
-        return {'doc': 'What fields of outgoing TCP packets should be auto-generated.'}
-    _generate = {'cksum': True,
-                'off': True,
-                'sequence': True,
-                'ack_number': True,
-                'sport': True,
-                'dport': True,
-                'window': True,
-                'urg_pointer': True }
+        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 - }
+    _generate = {tcp.f_checksum: True,
+                tcp.f_offset: True,
+                tcp.f_sequence: True,
+                tcp.f_ack_number: True,
+                tcp.f_sport: True,
+                tcp.f_dport: True,
+                tcp.f_window: True,
+                tcp.f_urg_pointer: True }
 
     @prop
     def validate():
-        return {'doc': 'What fields of incoming TCP packets should be validated.'}
-    _validate = { 'cksum': True,
-                'sequence': True,
-                'ack_number': True,
-                'sport': True,
-                'dport': True,
+        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'}
+    _validate = { tcp.f_checksum: True,
+                tcp.f_sequence: True,
+                tcp.f_ack_number: True,
+                #tcp.f_sport: True,
+                #tcp.f_dport: True,
                 'transition': True }
 
+
     @prop
     def packetsToSend():
         return {'doc': 'List of all packets to be sent.' }
@@ -213,6 +246,7 @@
                 '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():
@@ -240,7 +274,7 @@
           number of buffers pending receipt,
           urgent state,
           precedence,
-          security/compartment,
+          security / compartment,
           and transmission timeout )
         '''
         localSocket = self.localIP.getAscii() + ':' + self.localPort.getAscii()
@@ -262,7 +296,7 @@
     def __str__( self ):
         '''
         Prints out the annotated status.
-        
+
         '''
         statusNames = ['local socket',
           'foreign socket',
@@ -287,23 +321,15 @@
         '''
         Sets the current state of the state machine.
         '''
+        self.log.debug( state )
         # Quick bail-out
         if self.state == state:
             return
 
         validateTypes( {state:TcpState} )
 
-        if ( state == LISTEN or
-                state == SYN_SENT or
-                state == SYN_RECEIVED or
-                state == ESTABLISHED or
-                state == FIN_WAIT_1 or
-                state == FIN_WAIT_2 or
-                state == CLOSE_WAIT or
-                state == CLOSING or
-                state == CLOSED or
-                state == TIME_WAIT or
-                state == LAST_ACK ):
+        # Is the state a valid state?
+        if state in tcpStates:
 
             if ( not self.validate['transition'] ) or ( state in self.state.next ):
             # if ( not validateTransition ) or ( state in self.state.next ):
@@ -312,42 +338,35 @@
                     action = "Advancing"
 
                 self.log.state( "%s state from %s to %s" % ( action, self.state, state ) )
-                self._state = state
+                self.__state = state
             else:
                 self.log.state( "Attempted invalid state transition from %s to %s" %
                            ( self.state, state ) )
-
-#        
-#    def advanceState(self, state):
-#        '''
-#        Sets the currents state of the state machine, checking to make
-#        sure that the provided state is a valid state to transition to,
-#        given the current state.
-#        '''
-#        validateTypes({state:TcpState})
-#        # validateTypes(state,TcpState)
-#        
-#        if state in self.state.next:
-#            self.state = state
-#        else:
-#            self.log.state("Attempted invalid state transition from %s to %s" %
-#                           (self.state.name, state.name))
-#        
-#        return self.state == state
+        else:
+            self.log.error( 'Attempted to change to invalid state %s' % 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
+            # "Create a new transmission control block (TCB) to hold connection
+            # state information.  Fill in local socket identifier, foreign
+            # socket, precedence, security/compartment, and user timeout
+            # information." 
+            # Layman's Terms: 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 )
+            # "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."
+            # 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.state = SYN_SENT
@@ -355,18 +374,25 @@
             # Recv the SYN-ACK packet.
             start = time()
             while self.state == SYN_SENT:
-                synAck = self.recv()
+                synAck = findTcpLayer( self.recv() )
+
+                self.log.debug( 'received packet %s' % repr( synAck ) )
 
-                if synAck.ack and synAck.syn and synAck.ack_number == synPacket.sequence:
-                    state = ESTABLISHED
+                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 ):
+                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 )
+            ackPacket = self.newPacket( {tcp.f_ack:1, 'seq': synPacket.sequence + 1, tcp.f_ack_number: synAck.ack_number + 1} )
+
+            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
@@ -401,9 +427,9 @@
         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.snd_wl1 = self.snd_wnd
 
         self.rcv_nxt = 0
         self.rcv_up = 0
@@ -411,18 +437,18 @@
 
         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.
+        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} )
 
+        # Log
+        self.log.debug( repr( packet ) )
+
         # Get the TCP layer
         tcpLayer = findTcpLayer( packet )
         if tcpLayer == None:
@@ -452,7 +478,7 @@
             # acknowledgment (acknowledgment value = RCV.NXT).  If there is
             # insufficient space to remember this buffer, simply return "error:
             # insufficient resources".
-            if self.generate['ack_number']:
+            if self.generate.get( tcp.f_ack_number ):
                 packet.ack_number = self.rcv_nxt
 
             # If the urgent flag is set, then SND.UP <- SND.NXT-1 and set the
@@ -470,61 +496,99 @@
         # Send all queued packets
         self.sendQueuedPackets()
 
+    def recvRawTcp( self ):
+        return findTcpLayer( self.__connector.readFilteredByTuple( self.remoteIP, self.remotePort ) )
+
     def sendRawTcp( self, tcpLayer ):
+        self.log.debug( repr( tcpLayer ) )
+
         if not validateTypes( {tcpLayer:tcp.tcp} ):
             return
 
-        # self._socket.sendto(tcpLayer.chain().bytes, (self.remoteIP().getAscii(),self.remotePort().getAscii()))
-        packet = self._constructor
-        self._connector.send( tcpLayer.chain().bytes )
+        # self.__socket.sendto(tcpLayer.chain().bytes, (self.remoteIP().getAscii(),self.remotePort().getAscii()))
+        packet = self.__constructor.generateChain( tcpLayer )
+        self.log.pktsent( repr ( packet ) )
+        self.__connector.write( packet.bytes )
 
         pass
 
     def sendQueuedPackets( self ):
+        '''
+        Sends all packets in the queue.
+        '''
+        self.log.debug( "called" )
         for tcpLayer in self.packetsToSend:
             send = False
             # If the packet is the next packet that we are supposed to send, send it.
-            if self.snd_una == tcpLayer.sequence:
+            if self.snd_una < tcpLayer.sequence or not self.generate.get( tcp.f_sequence ):
                 # Make sure we are allowed to send it (i.e. smaller than rcv window)
 
-                # Set the ACK to acknowledge any packets that we've received
-                if self.generate['ack_number'] and self.ack_nxt > self.ack_lst:
-                # if self.isAutomaticAckEnabled() and self.ack_nxt > self.ack_lst:
-                    tcpLayer.ack_number = self.rcv_nxt
-                    tcpLayer.ack = 1
+#                # Set the ACK to acknowledge any packets that we've received
+#                if self.generate[tcp.f_ack_number] and self.ack_nxt > self.ack_lst:
+#                # if self.isAutomaticAckEnabled() and self.ack_nxt > self.ack_lst:
+#                    tcpLayer.ack_number = self.rcv_nxt
+#                    tcpLayer.ack = 1
+#
+#                # Set the correct checksum
+#                if self.generate[tcp.f_checksum]:
+#                # if self.isChecksumValidationEnabled():
+#                    tcpLayer.cksum = tcpChecksum( tcpLayer, src = self.localIP, dst = self.remoteIP )
+                    #self.generateChecksum( tcpLayer )
 
-                # Set the correct checksum
-                if self.generate['checksum']:
-                # if self.isChecksumValidationEnabled():
-                    tcpLayer.cksum = tcpChecksum( tcpLayer, src = self.localIP, dst = self.remoteIP ) #self.generateChecksum( tcpLayer )
+                self.sendRawTcp( tcpLayer )
+                # Update internal stuff.
+#                length = 0 # TODO TODO TODO TODO
+#                if self.snd_nxt
+            else:
+                self.log.error( "Did not send packet because sequence (%s) > snd_una (%s): %s"
+                                % ( tcpLayer.sequence, self.snd_una, repr( tcpLayer ) ) )
 
-                self.sendRawTcp( tcpLayer )
+    def logGenerated( self, packet = None, fieldname = None ):
+        '''
+        Helper method to log generated field data.
+        '''
+        # If a packet was specified, print the packet.
+        if packet:
+            self.log.generated( "Automatically set field %s to %s" % ( fieldname, hex( getattr( packet, fieldname ) ) ) )
 
-    def logGenerated( self, packet, fieldname ):
-        self.log.generated( "Automatically set field %s to %s" % ( fieldname, hex( getattr( packet, fieldname ) ) ) )
+        # Otherwise, assume it was some internal setting to the state, i.e. ISS, rcv_nxt, et. al
+        else:
+            self.log.generated( "Automatically set %s to %s" % ( fieldname, hex( getattr( self, fieldname ) ) ) )
 
     def logField( self, packet, fieldname ):
-        self.log.field( "Set field %s to %s" % ( fieldname, hex( getattr( packet, fieldname ) ) ) )
+        '''
+        Helper method to log user - specified field changes.
+        '''
+        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]]
+        '''
+        Returns an array of fields that need to be generated for a TCP packet.
+        '''
+        return [k for k in self.generate.iterkeys() if hasattr( self, k ) and self.generate.get( k )]
+
+    def tcpFieldsToGenerate( self ):
+        '''
+        Returns an array of fields that need to be generated for a TCP packet.
+        '''
+        return [k for k in self.generate.iterkeys() if hasattr( tcp, k ) and self.generate.get( k )]
 
     def newPacket( self, fields = None ):
         '''
         Creates a new packet with the specified fields.  Any fields that
-        are not specified are auto-generated if auto-generation is turned
+        are not specified are auto - generated if auto - generation is turned
         on for that field.
         @see #generate
         '''
+        self.log.debug( " % s" % fields )
         t = tcp.tcp()
 
         # Generate all the fields that are set up...
         t.syn = t.fin = t.rst = t.push = t.ack = t.urgent = 0
 
         # set the defaults.  don't generate the checksum yet.
-        for field in [k for k in self.fieldsToGenerate() if k != 'cksum']:
+        for field in self.tcpFieldsToGenerate():
             setattr( t, field, self.generateField( t, field ) )
-            self.logGenerated( t, field )
              #   setattr( t, field, value )
 
         # set the user-provided values
@@ -534,49 +598,407 @@
                 self.logField( t, field )
 
         # Do the checksum after user-provided values
-        if 'cksum' in self.generate:
-            t.cksum = tcpChecksum( t, src = self.localIP, dst = self.remoteIP )
-            self.logGenerated( t, 'cksum' )
+        if self.generate.get( tcp.f_checksum ):
+            t.checksum = self.generateField( t, tcp.f_checksum )
+            #t.checksum = tcpChecksum( t, src = self.localIP, dst = self.remoteIP )
+            #self.logGenerated( t, tcp.f_checksum )
 
         return t
 
     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':
+        # self.log.debug( " % s % s" % ( fieldname, repr( packet ) ) )
+        rv = None
+        if fieldname == tcp.f_checksum:
+            rv = tcpChecksum( packet, src = self.localIP, dst = self.remoteIP )
+        elif fieldname == tcp.f_window:
+            rv = self.snd_wl1
+        elif fieldname == tcp.f_dport:
+            rv = self.remotePort.getInteger()
+        elif fieldname == tcp.f_sport:
+            rv = self.localPort.getInteger()
+        elif fieldname == tcp.f_sequence:
+            rv = self.snd_nxt
+        elif fieldname == tcp.f_ack_number:
+            rv = self.rcv_nxt
+        elif fieldname == tcp.f_offset:
+            rv = 5
+        elif fieldname == tcp.f_urg_pointer:
             # TODO
+            rv = 0
+        else:
+            self.log.warn( 'generateField not defined for %s' % fieldname )
+
+        self.log.generated( '{%s: %s}' % ( fieldname, rv ) )
+        return rv
+
+    def getAckNum( self, packet ):
+        '''
+        Returns the ack_number that should be used to ACK the successful
+        receipt of the provided packet.
+        '''
+        p = findTcpLayer( packet )
+        if p is None:
             return 0
 
+        sequence = p.sequence
+        headerLength = len( p.getbytes() )
+        data = p.chain().bytes
+        dataLength = len( data )
+
+        return sequence + ( dataLength - headerLength ) + 1
+
+    def ack( self, sequence ):
+        '''
+        Modify the internal state to consider the provided sequence number
+        as having been successfully acknowledged.
+        '''
+        self.snd_una = sequence + 1
+
+    def recv( self, packet = None, timeout = None ):
+        '''
+        Get the next packet that has been received.  Packets are guaranteed
+        to be valid, in - order, and non - duplicated.
 
-    def recv( self, packet = None ):
+        @param packet Specify a packet to 'simulate' the receipt of.
+        @param timeout Optionally, specify a timeout for receiving a packet.
+        @return
+            - If param 'packet' specified, the packet or None if an error occurred.
+            - Otherwise, the next packet or None if there were none available in
+              the timeout period.
+        '''
+
+        # If the user provides a packet, we are 'simulating' receipt of 
+        # a packet.  Pass it off to the packet-receipt handler.  Return
+        # the packet if it is processed successfully.
+        if packet:
+            self.log.debug( repr( packet ) )
+            if self._handleRecvdPacket( packet ) == packet:
+                return packet
+            else:
+                return None
+
+        # Otherwise, pull the packet off of the front of the packetsRecvd list.
+        else:
+            start = time.time()
+
+            # Wait until there is a 'new' packet available.
+            while self.packetsRecvd.size() <= self._packetsRecvdOffset:
+                time.sleep( 0.05 )
+
+                # Check the timeout
+                if timeout and time.time() > start + timeout:
+                    return None
+
+            # Return the next packet.
+            self._packetsRecvdOffset += 1
+            return self.packetsRecvd[self._packetsRecvdOffset]
+
+        # Return 'None'
+        return None
+
+    def _handleRecvdPacket( self, packet = None ):
         '''
         Inform the TCP State Machine about packets that have been received.
-        This is necessary to keep the state up-to-date. 
+        This is necessary to keep the state up - to - date.
         '''
-        if packet == None:
-            packet = self._connector.readFilteredByTuple( self.remoteIP, self.remotePort )
+        # Make sure that there is TCP data in the packet.
+        seg = findTcpLayer( packet )
+
+        if seg is None:
+            return None
+
+        # Set to 'True' to queue the packet in 'self.packetsRecvd' at the end of the method.
+        queuePacket = False
+
+        # If the state is CLOSED (i.e., TCB does not exist) then
+        # all data in the incoming segment is discarded.  An incoming
+        # segment containing a RST is discarded.  An incoming segment not
+        # containing a RST causes a RST to be sent in response.  The
+        # acknowledgment and sequence field values are selected to make the
+        # reset sequence acceptable to the TCP that sent the offending
+        # segment.
+        if self.state == CLOSED and not seg.reset:
+
+            log.warn( 'Called recv with state == %s' % CLOSED )
+
+            # If the ACK bit is off, sequence number zero is used,
+            #     <SEQ=0><ACK=SEG.SEQ+SEG.LEN><CTL=RST,ACK>
+            if seg.ack:
+                pkt = self.newPacket( {tcp.f_sequence: seg.sequence, 'reset': 1} )
+
+            # If the ACK bit is on,
+            #     <SEQ=SEG.ACK><CTL=RST>
+            else:
+                pkt = self.newPacket( {tcp.f_sequence:0, tcp.f_ack: 1, 'reset': 1, tcp.f_ack_number: seg.ack_number} )
+
+            self.sendRawTcp( pkt )
+            return None
+
+        # If the state is LISTEN then first check for an RST
+
+        elif self.state == LISTEN:
+            # An incoming RST should be ignored.  Return.
+            if seg.reset:
+                return None
+
+            # second check for an ACK
+            # Any acknowledgment is bad if it arrives on a connection still in
+            # the LISTEN state.  An acceptable reset segment should be formed
+            # for any arriving ACK-bearing segment.  The RST should be
+            # formatted as follows:
+            #   <SEQ=SEG.ACK><CTL=RST>
+            # Return.
+            if seg.ack:
+                pkt = self.newPacket( {tcp.f_sequence:seg.ack_number, 'reset': 1} )
+                self.sendRawTcp( pkt )
+                return None
+
+            # third check for a SYN
+            #
+            # If the SYN bit is set, check the security.  If the
+            # security/compartment on the incoming segment does not exactly
+            # match the security/compartment in the TCB then send a reset and
+            # return.
+            #
+            #  <SEQ=SEG.ACK><CTL=RST>

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



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