PERFORCE change 165159 for review
Zachariah Riggle
zjriggl at FreeBSD.org
Thu Jun 25 13:32:59 UTC 2009
http://perforce.freebsd.org/chv.cgi?CH=165159
Change 165159 by zjriggl at zjriggl_tcpregression on 2009/06/25 13:32:01
Periodic commit
Affected files ...
.. //depot/projects/soc2009/zjriggl_tcpregression/src/tcpregression/pcsextension/__init__.py#5 edit
.. //depot/projects/soc2009/zjriggl_tcpregression/src/tcpregression/pcsextension/ipAddress.py#3 edit
.. //depot/projects/soc2009/zjriggl_tcpregression/src/tcpregression/tcpFilter.py#5 edit
.. //depot/projects/soc2009/zjriggl_tcpregression/src/tcpregression/tcpstatemachine.py#4 edit
.. //depot/projects/soc2009/zjriggl_tcpregression/src/tcpregression/testconfig.py#5 edit
Differences ...
==== //depot/projects/soc2009/zjriggl_tcpregression/src/tcpregression/pcsextension/__init__.py#5 (text+ko) ====
@@ -9,49 +9,49 @@
from pcs.packets.tcpv6 import tcpv6
# Configure logging
-logging.config.fileConfig("logging.conf")
+logging.config.fileConfig( "logging.conf" )
-(logging.FIELD_CHANGE,
+( logging.FIELD_CHANGE,
logging.RESPONSE_GENERATION,
logging.PACKET_TRANSMIT,
logging.PACKET_RECEIVED,
logging.PACKET_SENT,
logging.VALIDATE,
- logging.STATE_CHANGE) = range(logging.INFO-7, logging.INFO)
+ logging.STATE_CHANGE ) = range( logging.INFO - 7, logging.INFO )
-logging.addLevelName(logging.FIELD_CHANGE, "FIELD")
-logging.addLevelName(logging.RESPONSE_GENERATION, "RESPONSE")
-logging.addLevelName(logging.PACKET_TRANSMIT, "XMIT")
-logging.addLevelName(logging.PACKET_RECEIVED, "RECEIVED")
-logging.addLevelName(logging.PACKET_SENT, "SENT")
-logging.addLevelName(logging.VALIDATE, "VALIDATE")
-logging.addLevelName(logging.STATE_CHANGE, "STATE")
+logging.addLevelName( logging.FIELD_CHANGE, "FIELD" )
+logging.addLevelName( logging.RESPONSE_GENERATION, "GENERATE" )
+logging.addLevelName( logging.PACKET_TRANSMIT, "XMIT" )
+logging.addLevelName( logging.PACKET_RECEIVED, "RECEIVED" )
+logging.addLevelName( logging.PACKET_SENT, "SENT" )
+logging.addLevelName( logging.VALIDATE, "VALIDATE" )
+logging.addLevelName( logging.STATE_CHANGE, "STATE" )
# Find the TCP layer in a packet.
-def findTcpLayer(packet):
- return findPacketLayer(packet,tcp)
+def findTcpLayer( packet ):
+ return findPacketLayer( packet, tcp )
-def findIpLayer(packet):
- return findPacketLayer(packet,ipv4)
+def findIpLayer( packet ):
+ return findPacketLayer( packet, ipv4 )
-def findPacketLayer(packet, _class):
+def findPacketLayer( packet, _class ):
p = packet
while p is not None:
- if isinstance(p,_class):
+ if isinstance( p, _class ):
return p
p = p.data
return None
-
-
-
-def inet_lton(integer):
- return struct.pack(">L",integer)
+
+
+
+def inet_lton( integer ):
+ return struct.pack( ">L", integer )
+
+def inet_ltoa( integer ):
+ return socket.inet_ntoa( inet_lton( integer ) )
-def inet_ltoa(integer):
- return socket.inet_ntoa(inet_lton(integer))
-
-def inet_ntol(byteString):
- return struct.unpack(">L",byteString)[0]
+def inet_ntol( byteString ):
+ return struct.unpack( ">L", byteString )[0]
-def inet_atol(ipString):
- return inet_ntol(socket.inet_aton(ipString))+def inet_atol( ipString ):
+ return inet_ntol( socket.inet_aton( ipString ) )
==== //depot/projects/soc2009/zjriggl_tcpregression/src/tcpregression/pcsextension/ipAddress.py#3 (text+ko) ====
@@ -52,7 +52,7 @@
return htonl( unpack( "!L", self.nbo )[0] )
def getPCS( self ):
- return self.getNetworkInteger()
+ return self.getInteger()
def setPCS( self, x ):
- self.setNetworkInteger( x )
+ self.setInteger( x )
==== //depot/projects/soc2009/zjriggl_tcpregression/src/tcpregression/tcpFilter.py#5 (text+ko) ====
@@ -14,65 +14,68 @@
from pcsextension.networkPort import NetworkPort
from pcsextension import findIpLayer, findTcpLayer
-class tcpFilter(object):
+class tcpFilter( object ):
log = None
pcapHandle = None
doRead = False
-
- def __init__(self, interfaceName):
- self.log = tcplog(self)
- self.openInterface(interfaceName)
+
+ def __init__( self, interfaceName ):
+ self.log = tcplog( self )
+ self.openInterface( interfaceName )
- def openInterface(self, interfaceName):
+ def openInterface( self, interfaceName ):
try:
- self.pcapHandle = PcapConnector(interfaceName)
+ self.pcapHandle = PcapConnector( interfaceName )
# self.pcapHandle = IP4Connector();
except:
- self.log.error("Could not open interface %s" % interfaceName)
-
- def read(self):
+ self.log.error( "Could not open interface %s" % interfaceName )
+
+ def read( self ):
return self.pcapHandle.readpkt()
-
- def write(self,bytes):
- self.pcapHandle.write(bytes,len(bytes))
-
- def readFilteredByIP(self, ip):
+
+ def write( self, bytes ):
+ self.pcapHandle.write( bytes, len( bytes ) )
+
+ def readFilteredByIP( self, ip ):
"""
Reads packets until a packet is found going either to or from the specified IP address
is discovered. Returns the first matching packet.
@param ip IpAddress or dotted-quad string
@return A pcs.Packet object
"""
-
+
# if isinstance(ipAddress,int):
# ipAddress = intToBytes(ipAddress)
# If the IP address is a string ("127.0.0.1") or byte array ('\x7f\x00\x00\x01')
# we need to convert it into an integer representation of the same.
- if isinstance(ip,str):
+ if isinstance( ip, str ):
tmp = IpAddress()
- tmp.setAscii(ip)
+ tmp.setAscii( ip )
ip = tmp
srcIP = IpAddress()
dstIP = IpAddress()
-
+
while True:
packet = self.read()
-
- ipLayer = findIpLayer(packet)
-
+
+ ipLayer = findIpLayer( packet )
+
if ipLayer == None:
continue
-
- srcIP.setNetworkInteger(ipLayer.src)
- dstIP.setNetworkInteger(ipLayer.dst)
-
+
+ srcIP.setPCS( ipLayer.src )
+ dstIP.setPCS( ipLayer.dst )
+
+ print "Saw %s" % str( srcIP )
+ print "Saw %s" % str( dstIP )
+
if ipLayer.src == ip.getPCS() or ipLayer.dst == ip.getPCS():
- return packet
-
- def readFilteredByTuple(self, ipAddress, port):
+ return packet
+
+ def readFilteredByTuple( self, ipAddress, port ):
"""
Reads packets until a packet is found going either to or from [1] the specified
IP address and [2] the specified port. Returns the first matching packet.
@@ -81,21 +84,19 @@
@return A pcs.Packet object.
@see readFilteredByIP
"""
-
- if isinstance(port,int):
- tmp = NetworkPort()
- tmp.setInteger(port)
- port = tmp
-
+
+ if isinstance( port, int ):
+ port = NetworkPort( port )
+
while True:
- packet = self.readFilteredByIP(ipAddress)
-
- tcpLayer = findTcpLayer(packet)
+ packet = self.readFilteredByIP( ipAddress )
+
+ tcpLayer = findTcpLayer( packet )
if tcpLayer == None:
continue
-
+
print tcpLayer.sport
print port.getNetworkInteger()
-
+
if tcpLayer.sport == port.getInteger() or tcpLayer.dport == port.getInteger():
- return packet+ return packet
==== //depot/projects/soc2009/zjriggl_tcpregression/src/tcpregression/tcpstatemachine.py#4 (text+ko) ====
@@ -11,10 +11,13 @@
from pcsextension.ipAddress import IpAddress
from pcsextension.networkPort import NetworkPort
from pcsextension.pseudoipv4 import pseudoipv4, ipv4_cksum
+from pcsextension.checksum import tcpChecksum
+from random import randint
from socket import IPPROTO_TCP
from tcpFilter import tcpFilter
from tcpConstructor import tcpConstructor
-from tcpstates import
+from tcpstates import *
+from time import time
import binascii
import binhex
import pcs
@@ -123,7 +126,7 @@
rcv_wnd = 128 * 1024 # Recv window size
rcv_up = 0 # Recv urgent pointer
- irs = -1 # Initial receive sequence number
+ irs = 0 # Initial receive sequence number
rcv_nxt = irs # Expected next recv sequence #
ack_nxt = 0 # Next ACK number to send.
@@ -166,31 +169,27 @@
return {'doc': 'Maximum Tranmission Unit'}
_mtu = testconfig.mtu
+ @prop
def generate():
return {'doc': 'What fields of outgoing TCP packets should be auto-generated.'}
_generate = {'cksum': True,
'off': True,
- 'seq': True,
- 'acknum': True,
+ 'sequence': True,
+ 'ack_number': True,
'sport': True,
'dport': True,
'window': True,
- 'urg': True,
- 'ack': True,
- 'syn': True,
- 'fin': True,
- 'rst': True,
- 'psh': True,
- 'urgp': True }
+ 'urg_pointer': True }
@prop
def validate():
return {'doc': 'What fields of incoming TCP packets should be validated.'}
_validate = { 'cksum': True,
- 'seq': True,
- 'acknum': True,
+ 'sequence': True,
+ 'ack_number': True,
'sport': True,
- 'dport': True }
+ 'dport': True,
+ 'transition': True }
@prop
def packetsToSend():
@@ -336,10 +335,90 @@
#
# return self.state == state
+ def open( self ):
+ '''
+ Open the socket connection. This is synonymous with the
+ 'connect' function used by normal UNIX sockets.
+ S
+ '''
+ if self.state == CLOSED or self.state == LISTEN:
+ # Reset the connection state
+ self.reset()
+
+ # Send the SYN packet.
+ synPacket = self.newPacket( {'syn':1} )
+ self.log.generated( "Sending generated SYN packet to initiate connection: %s" % synPacket )
+ self.packetsToSend.append( synPacket )
+ self.sendQueuedPackets()
+ self.state = SYN_SENT
+
+ # Recv the SYN-ACK packet.
+ start = time()
+ while self.state == SYN_SENT:
+ synAck = self.recv()
+
+ if synAck.ack and synAck.syn and synAck.ack_number == synPacket.sequence:
+ state = ESTABLISHED
+
+ elif time() < ( start + testconfig.timeout ):
+ self.log.info( 'open() timeout after %s seconds' % testconfig.timeout )
+ return False
+
+ # Send the ACK packet.
+ ackPacket = self.newPacket( {'ack':1} )
+ self.log.generated( "Sending generated ACK packet in response to SYN/ACK: %s" % ackPacket )
+ self.packetsToSend.append( appPacket )
+ self.sendQueuedPackets()
+ self.state = ESTABLISHED
+ return True
+ else:
+ self.log.error( "connection already exists" )
+ return False
+
+ # Default...
+ return False
+
+ def reset( self, iss = None ):
+ '''
+ Resets all of the internal variables, sets the state to CLOSED.
+ @param iss Override the default ISS.
+ '''
+ # ...if active and the foreign socket is
+ # specified, issue a SYN segment. An initial send sequence number
+ # (ISS) is selected. A SYN segment of the form <SEQ=ISS><CTL=SYN>
+ # is sent. Set SND.UNA to ISS, SND.NXT to ISS+1, enter SYN-SENT
+ # state, and return.
+ self.log.state( "Resetting state" )
+ if iss is not None:
+ self.iss = iss
+ else:
+ if testconfig.randomISS:
+ self.iss = randint( 0, ( 2 ** 32 ) - 1 )
+ else:
+ self.iss = testconfig.staticISS
+ self.logGenerated( self, 'iss' )
+
+ self.snd_una = self.iss
+ self.snd_nxt = self.iss + 1
+ self.snd_up = 0
+ self.snd_wl1 = 0
+ self.snd_wl2 = 0
+ self.snd_wnd = 0xffff
+
+ self.rcv_nxt = 0
+ self.rcv_up = 0
+ self.rcv_wnd = testconfig.recvWindow
+
+ self.state = CLOSED
+
+ def recv( self ):
+ self._connector.readFilteredByTuple( self.localIP, self.localPort )
+
def send( self, packet ):
'''
Inform the TCP State Machine about packets that have been transmitted.
This is necessary to keep the state up-to-date.
+ @param packet pcs.Packet object to send. Must be, or contain, a TCP packet.
'''
# Check the arg type
validateTypes( {packet:pcs.Packet} )
@@ -349,28 +428,47 @@
if tcpLayer == None:
self.log.error( "Could not find TCP layer in packet %s" % packet )
- # Update list of packets sent
- self.packetsSent += [tcpLayer]
+ # Check the state
+ if self.state == CLOSED:
+ # Otherwise, return "error: connection does not exist".
+ self.log.error( "connection does not exist" )
+
+ if self.state == LISTEN:
+ # If the foreign socket is specified, then change the connection
+ # from passive to active, select an ISS. Send a SYN segment, set
+ # SND.UNA to ISS, SND.NXT to ISS+1. Enter SYN-SENT state. Data
+ # associated with SEND may be sent with SYN segment or queued for
+ # transmission after entering ESTABLISHED state.
+ self.open()
+
+ if self.state == SYN_SENT or self.state == SYN_RECEIVED:
+ # Queue the data for transmission after entering ESTABLISHED state.
+ # If no space to queue, respond with "error: insufficient
+ # resources".
+ self.packetsToSend.append( tcpLayer )
- # Get the bytes...
- bytes = tcpLayer.chain().bytes
+ if self.state == ESTABLISHED or self.state == CLOSE_WAIT:
+ # Segmentize the buffer and send it with a piggybacked
+ # acknowledgment (acknowledgment value = RCV.NXT). If there is
+ # insufficient space to remember this buffer, simply return "error:
+ # insufficient resources".
+ if self.generate['ack_number']:
+ packet.ack_number = self.rcv_nxt
- # Get the data length.
- dataLength = ( len( bytes ) - tcpLayer.off )
+ # If the urgent flag is set, then SND.UP <- SND.NXT-1 and set the
+ # urgent pointer in the outgoing segments.
+ # TODO: Handle URG pointer.
+ self.packetsToSend.append( tcpLayer )
- # Log packet
- self.log.pktsent( packet )
+ # Are we closing the connection?
+ if tcpLayer.fin:
+ self.state = FIN_WAIT_1
- # Set the correct sequence number.
- x = self.snd_nxt
- if self.validate['sequence']:
- # if self.isValidateSeqEnabled():
- tcpLayer.seq = self.snd_nxt
- self.snd_nxt += len( tcpLayer.data.chain() )
+ if self.state in [FIN_WAIT_1, FIN_WAIT_2, CLOSING, LAST_ACK, TIME_WAIT]:
+ self.log.error( 'connection closing' )
- # Add the packet to the outgoing queue.
- self.packetsToSend += [tcpLayer]
- self._sendPackets()
+ # Send all queued packets
+ self.sendQueuedPackets()
def sendRawTcp( self, tcpLayer ):
if not validateTypes( {tcpLayer:tcp.tcp} ):
@@ -382,35 +480,35 @@
pass
- def _sendPackets( self ):
+ def sendQueuedPackets( self ):
for tcpLayer in self.packetsToSend:
send = False
- # If the packet is the next packet that we are supposed to send,
- # and it is LARGER than the window size, send it.
- if self.snd_una == tcpLayer.seq:
- send = True
- else:
- packetLen = len( tcpLayer.chain().bytes )
- if tcpLayer.seq + packetLen > ( self.snd_una + self.snd_wnd ):
- send = True
+ # If the packet is the next packet that we are supposed to send, send it.
+ if self.snd_una == tcpLayer.sequence:
+ # Make sure we are allowed to send it (i.e. smaller than rcv window)
- if send:
# Set the ACK to acknowledge any packets that we've received
- if self.generate['acknum'] and self.ack_nxt > self.ack_lst:
+ if self.generate['ack_number'] and self.ack_nxt > self.ack_lst:
# if self.isAutomaticAckEnabled() and self.ack_nxt > self.ack_lst:
- tcpLayer.acknum = self.rcv_nxt
+ tcpLayer.ack_number = self.rcv_nxt
tcpLayer.ack = 1
# Set the correct checksum
if self.generate['checksum']:
# if self.isChecksumValidationEnabled():
- tcpLayer.cksum = tcpChecksum( tcpLayer ) #self.generateChecksum( tcpLayer )
+ tcpLayer.cksum = tcpChecksum( tcpLayer, src = self.localIP, dst = self.remoteIP ) #self.generateChecksum( tcpLayer )
self.sendRawTcp( tcpLayer )
def logGenerated( self, packet, fieldname ):
self.log.generated( "Automatically set field %s to %s" % ( fieldname, hex( getattr( packet, fieldname ) ) ) )
+ def logField( self, packet, fieldname ):
+ self.log.field( "Set field %s to %s" % ( fieldname, hex( getattr( packet, fieldname ) ) ) )
+
+ def fieldsToGenerate( self ):
+ return [k for k in self.generate.keys() if self.generate[k]]
+
def newPacket( self, fields = None ):
'''
Creates a new packet with the specified fields. Any fields that
@@ -421,57 +519,55 @@
t = tcp.tcp()
# Generate all the fields that are set up...
- t.syn = t.fin = t.rst = t.psh = t.ack = t.urg = 0
+ t.syn = t.fin = t.rst = t.push = t.ack = t.urgent = 0
- # Get the defaults...
- defaults = {'window': 0xffff,
- 'dport': self.remotePort.getInteger(),
- 'sport': self.localPort.getInteger(),
- 'seq': self.snd_nxt,
- 'acknum': self.rcv_nxt,
- 'off': 5}
+ # set the defaults. don't generate the checksum yet.
+ for field in [k for k in self.fieldsToGenerate() if k != 'cksum']:
+ setattr( t, field, self.generateField( t, field ) )
+ self.logGenerated( t, field )
+ # setattr( t, field, value )
- # set the defaults
- for ( field, value ) in defaults.items():
- if field in self.generate:
+ # set the user-provided values
+ if fields is not None:
+ for ( field, value ) in fields.items():
setattr( t, field, value )
- self.logGenerated( t, field )
+ self.logField( t, field )
- # set the user-provided values
- for ( field, value ) in fields.items():
- setattr( t, field, value )
-
- # Do the checksum LAST
+ # Do the checksum after user-provided values
if 'cksum' in self.generate:
- t.cksum = tcpChecksum( t )
+ t.cksum = tcpChecksum( t, src = self.localIP, dst = self.remoteIP )
self.logGenerated( t, 'cksum' )
- def generateChecksum( self, packet ):
- """Calculate and store the checksum for the TCP segment
- when encapsulated as an IPv4 payload with the given header."""
- raise NotImplementedError
+ return t
- bytes = packet.chain().bytes
- pip = pseudoipv4()
- pip.src = self.localIP
- pip.dst = self.remoteIP
- pip.protocol = IPPROTO_TCP
- pip.length = len( bytes )
- tmpbytes = pip.getbytes() + bytes
+ def generateField( self, packet, fieldname ):
+ if fieldname == 'cksum':
+ return tcpChecksum( packet, src = self.localIP, dst = self.remoteIP )
+ if fieldname == 'window':
+ return self.snd_wl1
+ if fieldname == 'dport':
+ return self.remotePort.getInteger()
+ if fieldname == 'sport':
+ return self.localPort.getInteger()
+ if fieldname == 'sequence':
+ return self.snd_nxt
+ if fieldname == 'ack_number':
+ return self.rcv_nxt
+ if fieldname == 'off':
+ return 5
+ if fieldname == 'urg_pointer':
+ # TODO
+ return 0
- cksum = ipv4_cksum( tmpbytes )
- # Log the checksum
- hexCksum = binascii.hexlify( struct.pack( "!H", cksum ) )
- self.log.debug( "Generated cksum: %s" % hexCksum )
-
- return cksum
-
- def recv( self, packet ):
+ def recv( self, packet = None ):
'''
Inform the TCP State Machine about packets that have been received.
This is necessary to keep the state up-to-date.
'''
+ if packet == None:
+ packet = self._connector.readFilteredByTuple( self.remoteIP, self.remotePort )
+
self.packetsRecvd += [packet]
self.log.pktrecv( packet )
@@ -563,3 +659,4 @@
# self.packetsToSend = []
self._connector = tcpFilter( testconfig.interface )
+ self.reset()
==== //depot/projects/soc2009/zjriggl_tcpregression/src/tcpregression/testconfig.py#5 (text+ko) ====
@@ -1,10 +1,11 @@
-localPort = 46001
-localIP = "127.0.0.1"
+
+localPort = 54321
+localIP = "172.16.0.10"
localIPv6 = "::1"
localMAC = "00:1b:63:06:82:b2"
-remotePort = 46002
-remoteIP = "127.0.0.1"
+remotePort = 12345
+remoteIP = "172.16.0.10"
remoteIPv6 = "::1"
remoteMAC = "00:21:29:a5:a9:3f"
@@ -12,3 +13,14 @@
interface = "en1"
+# Timeout for connect operations, in seconds
+timeout = 5
+
+# Receive window size
+recvWindow = 0xffff
+
+# Initial sequence number randomization. If randomISS is true,
+# the ISS is initialized to a random value. Otherwise, staticISS
+# is used as the ISS for all communication.
+randomISS = True
+staticISS = 0
More information about the p4-projects
mailing list