PERFORCE change 165891 for review
Zachariah Riggle
zjriggl at FreeBSD.org
Fri Jul 10 01:35:44 UTC 2009
http://perforce.freebsd.org/chv.cgi?CH=165891
Change 165891 by zjriggl at 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) <<<
More information about the p4-projects
mailing list