###############################################################################
#                                                                             #
#   Copyright 2005 University of Cambridge Computer Laboratory.               #
#                                                                             #
#   This file is part of Nprobe.                                              #
#                                                                             #
#   Nprobe is free software; you can redistribute it and/or modify            #
#   it under the terms of the GNU General Public License as published by      #
#   the Free Software Foundation; either version 2 of the License, or         #
#   (at your option) any later version.                                       #
#                                                                             #
#   Nprobe is distributed in the hope that it will be useful,                 #
#   but WITHOUT ANY WARRANTY; without even the implied warranty of            #
#   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the             #
#   GNU General Public License for more details.                              #
#                                                                             #
#   You should have received a copy of the GNU General Public License         #
#   along with Nprobe; if not, write to the Free Software                     #
#   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA #
#                                                                             #
###############################################################################


#
# Implement TCP state as observed from mid-point packet collection
#

##############################################################################
##############################################################################
import sys
#from nprobe import *
from nprobe import TRANS_INCOMPLETE, TRANS_FINISHED, SERVER, CLIENT
from np_packet_markers import *
#from np_obnode import *
from  np_longutil import ul2l
from np_seq import  SEQ_MAX, seq_add, seq_sub, seq_diff, seq_gt, seq_gte, \
     seq_lt, seq_lte
from print_col import *
from TCP_Imp import TCP_Imps, MAX_IWF
#from np_Connstats import Connstats
#from np_tcpstats import TCPStats
from minmax import BIGNUMBER, MIN, MAX, MAX3, MAX4

##############################################################################
##############################################################################

#
# TCP flags
#

TH_FIN  = 0x01
TH_SYN  = 0x02
TH_RST  = 0x04
TH_PUSH = 0x08
TH_ACK  = 0x10
TH_URG  = 0x20

##############################################################################
##############################################################################

#
# TCP state connection_state values
#

SYN_RECD = 0x1
SYN_SENT = 0x2
SYN_ACK_RECD = 0x4
SYN_ACK_SENT = 0x8

FIN_RECD = 0x10
FIN_SENT = 0x20
FIN_ACK_RECD = 0x40
FIN_ACK_SENT = 0x80

CONNECTED = 0x100

##############################################################################
##############################################################################

#
# All pkts run through machine?
#

PASSES_INCOMPLETE = 0
PASSES_COMPLETE = 1

##############################################################################
##############################################################################

CPKTS_THRESH = 1
SPKTS_THRESH = 1

##############################################################################
##############################################################################


def trig_str(p):

    r = 'trig='
    c = p.trig

    if c == 0: r += 'none '
    elif (c & TRIG_RTT_INVALID): r += 'rtt inval '
    elif (c & TRIG_CAUSE_LATER): r += 'cause later '
    elif (c & TRIG_ACK): r += 'ACK '
    elif (c & TRIG_SYNACK): r += 'SYNACK '
    elif (c & TRIG_FINACK): r += 'FINACK '
    elif (c & TRIG_DEL_ACK): r += 'del ACK '
    elif (c & TRIG_REL): r += 'ACK-> '
    elif (c & TRIG_PREV): r += 'prev-> '
    elif (c & TRIG_RESP_DEL): r += 'req-> '
    elif (c & TRIG_RESP_Q): r += 'reply_q-> '
    elif (c & TRIG_RESP_FIRST): r += 'first '
    elif (c & TRIG_RESP_REL): r += 'ACK-> '
    elif (c & TRIG_REQ_DEL): r += 'reply-> '
    elif (c & TRIG_REQ_Q): r += 'req_q-> '
    elif (c & TRIG_REQ_FIRST): r += 'first '
    elif (c & TRIG_REQ_REL): r += 'ACK-> '
    elif (c & TRIG_SERV_DEL): r += 'serv del '
    elif (c & TRIG_CLI_DEL): r += 'cli del '
    else:
	r += 'unknown (%x) ' % (c)

    r += '(%x) ' % (c)
    r += 'lag='
    if p.lag != None:
	r += '%.3f' % (p.lag/1000.0)
    else:
	r += 'none'

    return r


##############################################################################
##############################################################################

#
# Identifiers for transaction events - value order counts for sorting
#

REQSTART = 0x1
REQEND = 0x2
REPSTART = 0x4
REPEND = 0x8

##############################################################################
##############################################################################

#
# Sequence range released by an ACK arrival
#

class seq_Release:

    def __init__(self, start, end, pkt):

	self.start = start
	self.end = end
	self.range = seq_diff(end, start)
	self.ack_pkt = pkt
	self.pkts_released = 0
	self.highseq = start
	self.rtt = 0

	#print '#%d releasing %d - %d' % (pkt.indx, start, end)


##############################################################################

    # How does a segment compare with a release
    # - return start and end differences 
    def seq_cmp(self, start, end):

	return (seq_diff(self.highseq, start), seq_diff(self.end, end))
	
	    
		     
##############################################################################
##############################################################################

#
# Significant transaction timings
#

class Trans_Tms_t:

    def __init__(self, trans, i, open):

	self.rqs = ul2l(trans.http_reqstart_us()) + open
	self.rqe = ul2l(trans.http_reqend_us()) + open
	self.rps = ul2l(trans.http_repstart_us()) + open
	self.rpe = ul2l(trans.http_repend_us()) + open
        
	self.obsz = trans.http_obj_bytes()

	self.sstatus = trans.http_serv_status()
	self.cstatus = trans.http_cli_status()
	self.indx = i

        #
        # XXX TODO - why does rep end for non-body responses, eg code 304
        #  show as following packet time on pers conns
        # - s/be same as start time
        if (self.sstatus & TRANS_INCOMPLETE) \
           and (self.sstatus & TRANS_FINISHED) \
           and self.obsz == 0:
            self.rpe = self.rps

	#if self.sstatus & TRANS_INCOMPLETE:
	    #self.rpe = -1 # make sure never matched

    def __str__(self):

        return 'Trans_Tms[%d] = reqs %.3f reqe %.3f reps %.3f repe %.3f sz %d' % (self.indx, self.rqs/1000.0, self.rqe/1000.0, self.rps/1000.0, self.rpe/1000.0, self.obsz)

    def printself(self):

	print 'Trans_Tms[%d] = reqs %.3f reqe %.3f reps %.3f repe %.3f sz %d' % (self.indx, self.rqs/1000.0, self.rqe/1000.0, self.rps/1000.0, self.rpe/1000.0, self.obsz)
#

class Trans_Tms_T(Trans_Tms_t):

    def __init__(self, Trans, i):

	self.rqs = Trans.reqstart
	self.rqe = Trans.reqend
	self.rps = Trans.repstart
	self.rpe = Trans.repend
        
	self.obsz = Trans.nbytes

	self.sstatus = Trans.sstatus
	self.cstatus = Trans.cstatus
	self.indx = i
        
        if (self.sstatus & TRANS_INCOMPLETE) \
           and (self.sstatus & TRANS_FINISHED) \
           and self.obsz == 0:
            self.rpe = self.rps

    

##############################################################################
##############################################################################

#
# Set up TCP state and run through packets
#

class TCP_Machine:

    def __init__(self, conn, imps, logwrite, trace=0,
                 cpkts_thresh=CPKTS_THRESH, spkts_thresh=SPKTS_THRESH):

        if conn.sdpkts < spkts_thresh or conn.cdpkts < cpkts_thresh:
            raise TCPModelPkts 
	
	self.trace = trace
	if trace:
	    conn.printself()
	#else:
	    #print 'Conn #%d' % (conn.id)

	try:
	    print conn.notes
	except AttributeError:
	    pass

	#print logwrite
	self.logwrite = logwrite

	if logwrite == None:
	    self.logfun = self.null_f
	else:
	    self.logfun = self.buffer_log

	# Only write log messages to file when found final solution
	self.log_mssgs = []
	self.self_mssgs = []

	self.conn = conn
 	self.imps = imps
	self.made_imps = []

	self.sauto = 0
	self.cauto = 0

	self.valid = 1

	#
	# Get any info we have on usage of the connection
	#
        self.ttms = conn.ttms

 ##############################################################################

    def null_f(self, arg):

        pass


##############################################################################

    #
    # suggested imps already tried? - return 1 if so
    #
    def imps_tried(self, simp, cimp):
	#print 'tried %s %s?' % (simp.name, cimp.name)
	#print simp,; print cimp
	for imps in self.imps_attempted:
	    #print '\t%s %s' % (imps[0].name, imps[1].name)
	    #print imps[0],; print imps[1]
	    if simp == imps[0] and cimp == imps[1]:
		#print 'yes'
		return 1

	#print 'no'
	return 0


##############################################################################
    #
    #  Run the packets through the machine
    #
    def run_pkts(self):

	sstate = self.sstate
	cstate = self.cstate

	i = 0

	for p in self.conn.pktlist:
	    p.indx = i
	    i = i+1
	    p.markers = 0
	    p.trig = 0
	    p.remark = 0
	    p.rel = None
	    p.highrel = None
	    p.lag = None
	    p.trig = 0
	    p.prtt = p.trtt = p.delay = 0
	    p.tstr = ''
	    #print '#%d' % (i),

	    if p.dir == SERVER:
		depstate = self.sstate
		arrstate = self.cstate
	    else:
		depstate = self.cstate
		arrstate = self.sstate

	    try:
		depstate.departure(p)
		arrstate.arrival(p)
	    except TCPModelFailed, e:
		# goint to retry with new model
		state = e.state
		self.imps_attempted[-1][2] = PASSES_INCOMPLETE
		break
	    except TCPNoModel:
		# can't do - log it
		self.writelog()
		raise

	    self.lag2prtt(p)

	

##############################################################################
    #
    #  Run the connection through the machine - attemp to find best
    #   implementation fit if running on auto
    #
    #  - return any good implementations found
    #
    def do_pkts(self, recursing = 0):

	trace = 1
	conn = self.conn
	#conn.make_clusters()


	if not recursing:

	    if trace and self.trace:
		print 'Starting with imps %s %s' % (self.imps.simp_curr.name, 
						    self.imps.cimp_curr.name)
						
	    if self.imps.simp_curr.name == 'Auto':
		self.sauto = 1
		self.simp = self.imps.simp_default
		self.imps.simp_curr = self.simp
	    else:
		self.simp = self.imps.simp_curr
	    if self.imps.cimp_curr.name == 'Auto':
		self.cauto = 1
		self.cimp = self.imps.cimp_default
		self.imps.cimp_curr = self.cimp
	    else:
		self.cimp = self.imps.cimp_curr

	    #entry fields are [simp, cimp, complete, confidence]
	self.imps_attempted = []

	simp = self.simp
	cimp = self.cimp
	ret_imps = [None, None]
	
	while 1:
	    if trace and self.trace:
		inform('trying S imp %s C imp %s' % (simp.make_sig(), cimp.make_sig()))
	    if not recursing and self.imps_tried(simp, cimp):
		# looping - run out of ideas for improved fit
		if trace and self.trace:
		    inform('- already tried')
		break
## 	    if simp == self.imps.give_up or cimp == self.imps.give_up:
## 		break
	    self.log_mssgs = [] # clear
	    self.imps_attempted.append([simp, cimp, PASSES_COMPLETE, 0])
	    sstate = self.sstate = conn.sstate = TCP_State(self, conn, SERVER, simp, trace=self.trace)
	    cstate = self.cstate = conn.cstate = TCP_State(self, conn, CLIENT, cimp, conn.sstate, trace=self.trace)
	    sstate.new_imp = None
	    cstate.new_imp = None

	    self.run_pkts()
	    if trace and self.trace:
		print

	    
	    if sstate.new_imp != None:
		if self.sauto:
		    simp = self.imps.have_equiv(sstate.new_imp)
		    simp = simp.already_have(self.made_imps)
		    if trace and self.trace:
			inform('S new imp \'%s\'' % (simp.make_sig()))
		    continue
	    else:
		self.imps_attempted[-1][3] = self.imps_attempted[-1][3] + 50
		ret_imps[0] = sstate.imp
		

	    if cstate.new_imp != None:
		if self.cauto:
		    cimp = self.imps.have_equiv(cstate.new_imp)
		    cimp = cimp.already_have(self.made_imps)
		    if trace and self.trace:
			inform('C new imp \'%s\'' % (cimp.make_sig()))
		    continue
	    else:
		self.imps_attempted[-1][3] = self.imps_attempted[-1][3] + 50
		ret_imps[1] = cstate.imp

	    break


	if not recursing:
	    best_imp = self.imps_attempted[0]
	    for imp in self.imps_attempted:
		if imp[3] > best_imp[3]:
		    best_imp = imp
		
	    if trace and self.trace:    
		print 'Conn #%d TCP implementations tried:' % (conn.id)
	    for imp in self.imps_attempted:
		if trace and self.trace:
		    print '\tServer %s Client %s confidence %d' % (imp[0].make_sig(), imp[1].make_sig(), imp[3]),
		    if imp == best_imp:
			print ' - accepted',
			if imp[2] == PASSES_COMPLETE:
			    print ' - complete'
		    print

	    if best_imp[2] != PASSES_COMPLETE:
		# run it right through
		simp = self.simp = best_imp[0]
		cimp = self.cimp = best_imp[1]
		ret_imps = [simp, cimp]
		self.sauto = self.cauto = 0
		if trace and self.trace:
		    print 'redoing state with best fit %s %s' % (simp.make_sig(), cimp.make_sig())
		self.do_pkts(recursing = 1)

	    #for d in self.sdels:
		#print '#%4d %.3f %.3f' % (self.conn.id, d[0]/1000.0, d[1]/1000.0)
	    #self.do_rtts()
	    self.do_timing()
	    # write out any messages from final model
	    self.writelog()

	return ret_imps[0], ret_imps[1]


##############################################################################

    def writelog(self):

	#print 'writelog'
	if self.logwrite != None:
	    #print 'yes - %d' % (len(self.log_mssgs))
	    if len(self.log_mssgs):
		for m in self.log_mssgs:
		    #print 'LOG ' + m
		    self.logwrite(m)
	    else:
		pass
		#self.logwrite('#%d Zero messages\n' % self.conn.id)
	#raw_input('...')

	    for m in self.self_mssgs:
		self.logwrite(m)

	self.log_mssgs = []


##############################################################################

    def buffer_log(self, s):
	
	self.log_mssgs.append(s)
	#print 'LOG buffer %s len now %d' % (s, len(self.log_mssgs))


##############################################################################

    def do_timing(self):

	#stats = self.cstats = self.conn.stats = Connstats(self.conn)
	#stats = self.cstats = self.conn.stats = TCPStats(self.conn)
	#sstate = self.sstate
	#cstate = self.cstate
        return
	for p in self.conn.pktlist:
	    trig = p.trig
	    if p.dir == SERVER:
		stats.allsprtts.append((p.tm, p.prtt))
		if trig & TRIG_RTT:
		    # genuine RTT
		    stats.sprtts.append((p.tm, p.prtt))
		if trig & TRIG_RESP_FIRST:
                    # initial server delay
 		    stats.slat = (p.tm, int(p.delay))
		if trig & TRIG_RESP_DEL:
                    # subsequent response delay (HTTP-P)
 		    stats.splat.append((p.tm, int(p.delay)))
	    else:
 		stats.cprtts.append((p.tm, p.prtt))

        # apparent rtts
	t =  self.ttms[0]
	if t:
	    stats.aslat = (t.rps, t.rps-t.rqe)
	    for t in self.ttms[1:]:
		stats.asplat.append((t.rps, t.rps-t.rqe))
	


############################################################################## 


    def interp(self, x1, y1, x2, y2, cut):

	if x2 == x1:
	    return (y1)

	a = x2-cut
	b = y1-y2
	c = x2-x1
	y = (a*b)/c + y2

	return y


##############################################################################


    def rtt_patch(self, start, r1, r2, curr):

	trace = 1
	trace = trace and self.trace

	if trace:
	    print 'rtt patch start=#%d ' % (start.indx),
	    if r1 != None:
		print 'r1=#%d prtt=%.3f ' % (r1.indx, r1.prtt/1000.0),
	    else:
		print 'r1 None ',
	    if r2 != None:
		print 'r2=#%d prtt=%.3f ' % (r2.indx, r2.prtt/1000.0),
	    else:
		print 'r2 None ',
	    if curr != None:
		print 'curr=#%d prtt=%.3f' % (curr.indx, curr.prtt/1000.0)
	    else:
		print 'curr None'
	
	if r1 == None:
	    # at most one to go on
	    if r2 == None:
		# nothing to go on
		base = self.tmp
	    else:
		# one to go onn
		base = r2.prtt
	else:
	    # got both to go on - interpolate in loop
	    base = None

	p = start
	while 1:
## 	    print p
## 	    print '#%d-%s' % (p.indx, trig_str(p))
	    if p == curr:
		break 
	    if (p.markers & P_FIRST):
		p = p.nxt
		continue
	    if trace:
		print '\tpatching #%d %s' % (p.indx, trig_str(p))
	    if base == None:
		prtt = self.interp(r1.tm, r1.prtt, r2.tm, r2.prtt, p.tm)
		#print 'interp (%.3f/%.3f -> %.3f ->  %.3f/%.3f) = %.3f' % (r1.tm/1000.0, r1.prtt/1000.0, p.tm/1000.0, r2.tm/1000.0, r2.prtt/1000.0, prtt/1000.0)
	    else:
		prtt = base
	    if p.trig & TRIG_RTT_DEL:
		# there's a delay inclusive of apparent prtt
		#print 'lag=',
		#print p.lag,
		#print ' prtt=',
		#print prtt
		p.delay = p.lag - prtt
	    elif p.trig & TRIG_DEL:
		# there's a delay not inclusive of apparent prtt 
		p.delay = p.lag
	    else:
		p.delay = 0

	    if p.delay < 0:
		str = 'TCPConn #%d NEGATIVE DELAY: #%d %.3f' % (self.conn.id, 
			       p.indx, p.delay/1000.0)
		if trace:
		    whoops(str)
		self.logfun(str)

	    p.prtt = prtt
	    if trace:
		print '\tpatched #%d now prtt %.3f delay %.3f %s' % (p.indx, 
		       p.prtt/1000.0, p.delay/1000.0, trig_str(p))
	    p = p.nxt
	    if p.nxt == p:
		# last pkt - exit
		break
## 	    if trace:
## 		print 'next = #%d' % (p.indx)

	    


##############################################################################
	    
	    
    def lag2prtt(self, p):

	trace = 1

	if p.dir == SERVER:
	    state = self.sstate
	else:
	    state = self.cstate

	if trace and self.trace:
	    print '%s#%d lag2prtt - %s' % (state.nm, p.indx, 
						trig_str(p))

	last = state.last_valid_prtt
	if p.trig & TRIG_RTT:
	    p.prtt = p.lag
	    if p != state.first:
		# have a valid partial RTT
		if last != None:
		    # already seen one
		    self.rtt_patch(last.nxt, last, p, p)
		else:
		    # this is the first
		    self.rtt_patch(state.first, None, p, p)
	    state.last_valid_prtt = p
		
	elif p.nxt == p and p != state.first:
	    # last packet
	    if last != None:
		# patch on basis of last valid partial RTT
 		self.rtt_patch(last.nxt, None, last, p)
		# and this one
 		self.rtt_patch(p, None, last, None)
	    else:
		# nothing to go on 
		self.tmp = state.find_min_prtt()
		self.rtt_patch(state.first, None, None, p)
		# and this one
		self.rtt_patch(p, None, None, None) 
		
		     
##############################################################################
##############################################################################

# Modelling failed exception
class TCPModelFailed:

    def __init__(self, state):
	self.state = state
##############################################################################

# Can't model exception
class TCPNoModel:

    pass
##############################################################################

# Can't model exception
class TCPModelPkts:

    pass
##############################################################################

# Can't model exception
class TCPModelNoTrans:

    pass
		     
##############################################################################
##############################################################################

class TCP_State:

    def __init__(self, mach, conn, dir, imp, reverse_state = None, trace=None):

	self.mach = mach
	self.dir = dir
	self.connid = conn.id
	self.trace = trace

	if dir == SERVER:
	    self.nm = 'S'
	    self.ndpkts = conn.sdpkts 
	    if imp.mss == None:
		self.segsz = conn.cmss # mss that can send
	    else:
		self.segsz = imp.mss
	    self.pl = conn.sbytes
	else:
	    self.nm = 'C'
	    self.ndpkts = conn.cdpkts
	    if imp.mss == None:
		self.segsz = conn.smss
	    else:
		self.segsz = imp.mss
	    self.pl = conn.cbytes

	#print '%s mss = %d' % (self.nm, self.segsz)

	self.imp = imp
	self.new_imp = None

	if reverse_state != None:
	    self.reverse = reverse_state
	    reverse_state.reverse = self

	self.IW = None
	# Initially set cwnd high - adjust when initial window known
	self.cwnd = MAX_IWF*self.segsz
	self.ssthresh = self.imp.ssthresh

	#print 'Imp is %s, IW_fact = %d' % (self.imp.name, self.imp.iw_fact)
	
	self.first = None

	self.conn_state = 0
	self.connected = 0

	self.sstart = 1
	
	self.maxseg = 0
	self.segiffy = 0
	self.segiffy_szs = {}

	self.dep_segs = 0
	self.dep_octs = 0

	self.snd_highack = -1L
	self.rcv_highack = -1L
	self.snd_nxt = -1L
	self.rcv_nxt = -1L

	self.rcv_acks = []
	self.snd_segs = []
	self.snd_synacks = []
	self.rcv_segs = []
	
	self.rcv_segheld = []
	self.snd_segheld = []
	self.too_high_acks = []
	self.too_low_acks = []

	self.releases = []

	self.rcv_olap_p = 0
	self.rcv_olap_o = 0
	self.rcv_rtmts_p = 0
	self.rcv_rtmts_o = 0

	self.snd_olap_p = 0
	self.snd_olap_o = 0
	self.snd_rtmts_p = 0
	self.snd_rtmts_o = 0

	self.dep_synps = []
	self.arr_synackps = []

	self.dup_dep_syns = 0
	self.dup_arr_syns = 0
	self.dup_dep_synacks = 0
	self.dup_arr_synacks = 0

	self.dup_syn_delay = 0
	self.dup_synack_delay = 0

	self.synunack = 0 # outstanding SYN
	self.synack = None # current
	self.finack = None # current

	self.lastp = None # last pkt to departure
	self.last_valid_prtt = None

	# current transaction times
	self.ttms = mach.ttms[0]
	self.reqi = self.repi = 0 #indices into trans times list


	return

##############################################################################

    #
    # If no valid prtt found base calculations on minimum seen
    #
    def find_min_prtt(self):

	p = self.first
	min_rtt = BIGNUMBER
	while 1:
	    if p.prtt and p.prtt < min_rtt:
		min_rtt = p.prtt
	    if p.nxt == p:
		break
	    p = p.nxt

	if min_rtt == BIGNUMBER:
	    min_rtt = 0

	return min_rtt
	    

##############################################################################

    def give_up(self):

	self.new_imp = None
	raise TCPNoModel()
	    

##############################################################################

    def retry(self, new_imp):

	# Don't retry if not on auto
	if (self.dir == SERVER and self.mach.sauto) or (self.dir == CLIENT and self.mach.cauto):
	    self.new_imp = new_imp
	    raise TCPModelFailed(self)
	else:
	    del(new_imp)
	    return
	    

##############################################################################

    def dep_syn_pkt(self, p):

	trace = 1

	if self.dir == SERVER and not (self.reverse.conn_state & SYN_SENT):
	    str = 'TCPConn #%d SERVER ORIGINATED CONN: %s#%d' % (self.connid, self.nm,
							 p.indx)
	    if trace and self.trace:
		whoops(str)
	    self.mach.logfun(str)
	    self.give_up()
	    

	# duplicate?
	if self.conn_state & SYN_SENT:
	    if p.seq == self.iss:
		# duplicate
		#print '%s#%d DUP DEP SYN' % (self.nm, p.indx)
		p.remark =1
		self.dup_dep_syns = self.dup_dep_syns + 1
		if self.conn_state & SYN_ACK_RECD:
		    # seen the ACK - dep q popped
		    pass
		else:
		    self.snd_segs.pop(0)
		# assess delay
		self.dup_syn_delay = p.tm - self.dep_synps[0].tm

	self.conn_state = self.conn_state | SYN_SENT
	self.snd_segs.append(p)

	self.iss = p.seq
	self.rcv_highack = self.iss
	self.snd_nxt = p.end = seq_add(self.iss, 1)
	self.synack = self.snd_nxt
	self.rcv_wnd = p.window
	self.release = self.iss

	self.dep_synps.append(p)
	self.synunack = 1

## 	# dummy
## 	r = seq_Release(p.seq, self.snd_nxt, p)
## 	r.rtt = 0
## 	p.rel = r

## 	p.nrtt = 0

 	self.release = self.snd_nxt

## 	## # is this a reverse syn - if so get nrtt for this packet
## 	if self.conn_state:
## 	    p.nrtt = p.tm - self.reverse.dep_synps[-1].tm
## 	    # 

## 	if self.conn_state & SYN_RECD:
## 	    # this is a reverse
## 	    p.prtt = p.tm - self.reverse.dep_synps[-1].tm

##############################################################################

    def dep_ack_pkt(self, p):

	trace = 1
	ack = p.ack

	# looks reasonable?
	if seq_lt(ack, self.snd_highack):
	    str = 'TCPConn #%d LOW ACK departing: %s#%d - got  %d expected >= %d' % (self.connid, self.nm, p.indx, ack, self.snd_highack)
	    if trace and self.trace:
		whoops(str)
	    self.mach.logfun(str)
	    #raw_input('anything to continue..........\n')
	    return

	if self.conn_state & SYN_RECD and not self.conn_state & SYN_ACK_SENT:
	    if ack == seq_add(self.irs, 1):
		if len(self.rcv_segs) == 1 and self.rcv_segs[0].seq == self.irs:
		    self.conn_state =  self.conn_state | SYN_ACK_SENT
		    self.snd_synacks.append(p)
		    rtt =  p.tm - self.rcv_segs[0].tm
		    self.rcv_syn_rtt = rtt
		    
		    p.trig = TRIG_SYNACK
		    p.lag = p.tm - self.rcv_segs[0].tm
		    p.prtt = p.lag
		    #self.last_valid_prtt = p
		    #p.nrtt = rtt
		    self.rcv_segs.pop(0)
		    if self.reverse.conn_state & SYN_ACK_SENT:
			# catch the case of a departing seg riding on a SYNACK
			self.conn_state |= CONNECTED
			self.reverse.conn_state |= CONNECTED 
			#print '%s#%d Marking connected' % (self.nm, p.indx)
		    #print '%s#%d SYN_ACK_SENT rcv_syn_rtt %.3f' % (self.nm, p.indx, self.rcv_syn_rtt/1000.0)
	    else:
		p.remark = 1
		str = 'TCPConn #%d UNMATCHED SYNACK: %#d %s -  expected %d got %d' % (self.connid, p.indx, self.nm, seq_add(self.irs, 1), ack)
		if trace and self.trace: 
		    whoops(str)
		self.mach.logfun(str)
		 

	self.rcv_wnd = p.window
	self.snd_highack = ack

	## remove acked pkts from received list
	#print'#%d ack = %d\nrcv list =' % (p.indx, ack)
	#for p in self.rcv_segs:
	    #p.printself_rel()
	p_acked = 0
	rtt = 0
	llen = len(self.rcv_segs)
	while llen:
	    pkt = self.rcv_segs[0]
	    highseq  = seq_add(pkt.seq, pkt.len)
	    if seq_lte(highseq, ack):
		if ack == highseq:
		    rtt = p.tm - pkt.tm
		    p.arrseg2ack_rtt = rtt
		self.rcv_segs.pop(0)
		llen = llen -1
 		p_acked = p_acked + 1
	    else:
		break

## 	if p_acked:
## 	    print '%s#%d dep acked %d ack_rtt = %.3f' % (self.nm, 
## 						p.indx, p_acked, rtt/1000.0)
	

##############################################################################

# 
# Size of separting seg makes sense?
# - does it suggest advertised mss is not being used?
#

    def check_segsz(self, p):

	trace = 1

	#IFFY_SEGSZ_THRESH = 2
	IFFY_SEGSZ_THRESH = 4

	plen = p.len
	self.maxseg = MAX(self.maxseg, plen)

	if plen >= self.maxseg and plen != self.segsz:
	    #cprint(F_BLUE, '%s#%d seg %d not mss %d' % (self.nm, p.indx, plen, self.segsz))
	    self.segiffy = self.segiffy + 1
	    if not self.segiffy_szs.has_key(plen):
		self.segiffy_szs[plen] = 1
	    else:
		self.segiffy_szs[plen] =  self.segiffy_szs[plen]+1

	    if self.dep_octs > self.segsz*IFFY_SEGSZ_THRESH and self.segiffy_szs[plen]*IFFY_SEGSZ_THRESH > self.dep_segs: 
## 		nm = self.imp.new_name('_MSS', plen)
## 		if trace and self.trace:
## 		    inform('%s#%d trying new mss %d imp name %s' % (self.nm, p.indx, plen, nm))
## 		self.new_imp = self.imp.clone(nm)
## 		self.new_imp.mss = plen
		self.retry(self.imp.clone([('mss', plen), ('nmss', self.segsz)]))
		
		
		
		
##############################################################################
		
#
# Check seq no. of segment
#
    
    def check_segseq(self, p):

	trace = 1

	seq = p.seq
	end = p.end

	if seq == self.snd_nxt:
	    self.snd_nxt = end # TODO - check len within snd wnd
	elif seq_gt(seq, self.snd_nxt):
	    # there's a gap
	    if trace and self.trace:
		print '%s#%d seg departing - seq gap' % (self.nm, p.indx)
	    #p.remark = 1
	    p.markers = p.markers | P_SEQ_HELD
	    self.snd_segheld.append(p)
	elif seq_gt(end, self.snd_nxt):
	    # some o'lap
	    if trace and self.trace:
		print '%s#%d seg departing - overlap' % (self.nm, p.indx)
	    self.snd_olap_p = self.snd_olap_p + 1 
	    self.snd_olap_o = self.snd_olap_o + seq_sub(self.snd_nxt, seq)
	    self.snd_nxt = end
	    #p.remark = 1
	    p.markers = p.markers | P_SEQ_OLAP
	else:
	    if trace and self.trace:
		cprint(F_RED, '%s#%d seg departing - dup pkt' % (self.nm, 
								 p.indx))
	    p.remark = 1
	    # totally duplicated
	    self.snd_rtmts_p = self.snd_rtmts_p + 1
	    self.snd_rtmts_o = self.snd_rtmts_o + p.len
	    p.markers = p.markers | P_SEQ_RTMT
	    # remove from sent list
	    self.snd_segs.pop()
	    #return

	

##############################################################################

# 
# Chech the departing segment against pending releases
#

    def cmp_release(self, p):

	trace = 1

	seq = p.seq
	end = p.end
	plen = p.len

	if not len(self.releases):
	    # no releases pending
	    #p.remark = 1
	    str = 'TCPConn #%d SEG DEPARTING: - No releases: %s#%d' % (self.connid, self.nm, p.indx)
	    if trace and self.trace:
		whoops(str)
	    self.mach.logfun(str)
	elif end > self.releases[-1].end:
	    # outside window
	    #p.remark = 1
	    str = 'TCPConn #%d SEG END EXCEEDS HIGH RELEASE: %s#%d seg %d-%d last release %d-%d' % (self.connid, self.nm, p.indx, seq, end, 
											    self.releases[-1].start, self.releases[-1].end)
	    if trace and self.trace:
		inform(str)
		self.print_releases()
	    self.mach.logfun(str)

	for d in range(len(self.releases)):
	    # loop to catch pkts spanning releases
	    rel = self.releases[0]
	    sdiff, ediff = rel.seq_cmp(seq, end)
	    if trace and self.trace:
		print '%s#%d rel by %d sdiff %d ediff %d' % (self.nm, p.indx, 
						   rel.ack_pkt.indx, sdiff, ediff)
	    if sdiff >= 0:
		# seg starts at or before release
		if sdiff + plen < 0:
		    #  ends before release - retransmission?
		    if not (p.markers & P_SEQ_RTMT):
			#p.remark = 1
			str = 'TCPConn #%d SEG BEFORE RELEASES - NOT RETRANSMIT: %s#%d seg %d - %d rel %d - %d (%d)' % (self.connid, self.nm, p.indx, seq,
                        end, rel.start, rel.end, rel.highseq)
			if trace and self.trace:
			    whoops(str)
			self.mach.logfun(str)
		    break
		else:
		    # its the next seg expected
		    #last part of pkt within this release? - trim
		    if trace and self.trace:
			print '%s#%d last part within release' % (self.nm, p.indx)
		    plen = plen - sdiff
		
		if trace and self.trace:
		    print '%s#%d in release %d - %d ( by %d) pkt %d of release ' % (self.nm, p.indx, rel.start, rel.end,
			    rel.ack_pkt.indx, rel.pkts_released),

		if rel.pkts_released == 0:
		    rtm = rel.ack_pkt.tm
		    if trace and self.trace:
			print ' - first of release',

		    if p.trig != TRIG_SYNACK:
			if rtm + self.last_valid_prtt.prtt > self.lastp.tm:
			    # release after prev pkt -> valid prtt
			    p.trig = TRIG_REL
			    p.lag = p.tm - rtm
			    if trace and self.trace:
				print 'rel lag = %.3f' % (p.lag/1000.0),
			else:
			    p.trig = TRIG_PREV
			    p.lag = p.tm - self.lastp.tm

		if ediff == 0:
		    # last seg of release
		    if trace and self.trace:
			print '- completes release'
		    #p.rel = rel
		    rel.pkts_released = rel.pkts_released + 1
		    rel.highseq = end
		    rel.range = rel.range - plen
		    if rel.range != 0:
			if trace and self.trace:
			    whoops('%s#%d complete rel range' % (self.nm, 
								 p.indx))
		    if trace and self.trace:
			print '%s#%d popping release %d-%d' % (self.nm, 
			       p.indx, self.releases[0].start,
					 self.releases[0].end)
		    self.releases.pop(0)

		    self.check_rel_cause(p, rel)
		    
		    break
		elif ediff > 0:
		    if trace and self.trace:
			print ' - within release'
		    # seg doesn't complete release
		    #p.rel = rel
		    rel.pkts_released = rel.pkts_released + 1
		    rel.highseq = end
		    rel.range = rel.range - plen

		    self.check_rel_cause(p, rel)
		    break
		else:
		    if trace and self.trace:
			print ' - last of release + balance of segment left' 
		    # seg o'laps next release
		    #seq = rel.end
		    #plen = plen + ediff
		    rel.highseq = end
		    rel.pkts_released = rel.pkts_released + 1
		    rel.range = rel.range - plen - ediff
		    if rel.range != 0:
			p.remark = 1
			if trace and self.trace:
			    whoops('%s#%d rel range' % (self.nm, p.indx))
		    self.releases.pop(0)
		    if not len(self.releases):
			if trace and self.trace:
			    whoops('end of segment beyond release')       
		    continue

	    else:
		# seg starts after release
		#\p.remark = 1
		str = 'TCPConn #%d SEG AFTER RELEASE: %s#%d  seq = %d r = #%d %d-%d' % (self.connid, self.nm, p.indx, seq,  rel.ack_pkt.indx, 
			 rel.start, rel.end)
		if trace and self.trace:
		    whoops(str)
		self.mach.logfun(str)
		# tmp
		#raise TCPNoModel()


##############################################################################

    def check_rel_cause(self, p, rel):

	#
	# Identify packet send trigger cause over-riding TCP mechanisms
	#

	trace = 1
	gotit = 0

	p.tstr = ''

	dir = self.dir
	tm = p.tm
	ttms = self.ttms
	if ttms != None:
	    rqs = ttms.rqs
	    rqe = ttms.rqe
	    rps = ttms.rps
	    rpe = ttms.rpe
	rapt = rel.ack_pkt.tm
	#last_prtt = self.last_valid_prtt.prtt
	#raptbk = rapt + last_prtt

	# is this the first or last of an HTTP reply?
	if dir == SERVER and ttms != None:
	    gdel = TRIG_SERV_DEL
	    repi = self.repi
	    # may be > 1 per pkt
	    while 1:
		if tm == rps:
		    # first seg of response
		    if trace and self.trace:
			print '%s#%d First of response %d' % (self.nm, 
					  p.indx, repi),
			if len(p.tstr) == 12:
			    p.tstr += '\n'
			p.tstr += '(%d' % (repi)
		    if not gotit:
			gotit = 1
			last_prtt = self.last_valid_prtt.prtt
			raptbk = rapt + last_prtt
			rqebk = rqe + last_prtt
			if repi > 0:
			    #there's a previous transaction
			    latest = MAX3(self.mach.ttms[repi-1].rpe, 
					  rqebk, raptbk)
			    #print 'latest of %.3f %.3f %.3f = %.3f' % (self.mach.ttms[repi-1].rpe/1000.00, rqe/1000.00, rapt/1000.00, latest/1000.00)
			else:
			    latest = MAX(rqebk, raptbk)

			if latest == rqebk:
			    #released by request arrival
			    if trace and self.trace:
				print '%s#%d - released by req at %.3f' % (self.nm, p.indx, latest/1000.0)
			    if repi == 0:
				p.trig = TRIG_RESP_FIRST
                                #print 'TRIG_RESP_FIRST'
			    else:
				p.trig = TRIG_RESP_DEL
                                #print 'TRIG_RESP_DEL'
			    p.lag = tm - rqe
			elif latest == raptbk:
			    # released by an arriving ack
			    if trace and self.trace:
				print '%s#%d - released by ACK at %.3f pkt %d' % (self.nm, p.indx, latest/1000.0, rel.ack_pkt.indx)
			    p.trig = TRIG_RESP_REL
                            #print 'TRIG_RESP_REL'
			    p.lag = tm - rapt

			else:
			    # released by completion of last response
			    if trace and self.trace:
				print '%s#%d - released by prev response at %.3f' % (self.nm, p.indx, latest/1000.0)
			    p.trig = TRIG_RESP_Q
                            #print 'TRIG_RESP_Q'
			    p.lag = tm - rpe
		    elif trace and self.trace:
			print

		if tm == rpe:
		    # last seg of response
		    p.markers |= P_RLAST
		    if trace and self.trace:
			print '%s#%d last of response %d' % (self.nm, 
					  p.indx, repi)
			if len(p.tstr):
			    p.tstr += ')'
			else:
			    p.tstr += '%d)' % (repi)
		    repi += 1
		    try:
			ttms = self.mach.ttms[repi]
			rqs = ttms.rqs
			rqe = ttms.rqe
			rps = ttms.rps
			rpe = ttms.rpe
		    except IndexError:
			# may be out of transactions
			ttms = None
			rqs = None
			rqe = None
			rps = None
			rpe = None
			break
		    else:
			self.reverse.repi += 1*self.segsz
		else:
		    # wont be another start
		    break

	    self.ttms = ttms
	    self.repi = repi
		


	# is this the first or last of an HTTP request?
	if dir == CLIENT and ttms != None:
	    reqi = self.reqi
	    gdel = TRIG_CLI_DEL
	    while 1:
		if tm == rqs:
		    if trace and self.trace:
			print '%s#%d First of request %d' % (self.nm, p.indx, reqi),
			if len(p.tstr) == 12:
			    p.tstr += '\n'
			p.tstr += '(%d' % (reqi)
		    if not gotit:
			gotit = 1
			if reqi == 0:
			    if trace and self.trace:
				print '%s#%d - released by SYNACK at %.3f' % (self.nm, p.indx, self.snd_synacks[-1].tm/1000.0)
			    p.trig = TRIG_REQ_FIRST
			    p.lag = tm - self.snd_synacks[-1].tm
			    
			else:
			    #there's a previous transaction
			    last_prtt = self.last_valid_prtt.prtt
			    raptbk = rapt + last_prtt
			    prev_rqe = self.mach.ttms[reqi-1].rqe
			    prev_rpe = self.mach.ttms[reqi-1].rpe
			    prev_rpebk = prev_rpe + last_prtt
			    if prev_rpebk < tm:
				# prev response received
				latest = MAX3(prev_rqe, prev_rpebk, raptbk)
				#print 'latest of %.3f %.3f %.3f = %.3f' % (prev_rqe/1000.00, prev_rpe/1000.00, rapt/1000.00, latest/1000.00)
			    else:
				latest = MAX(prev_rqe, raptbk)


			    if latest == prev_rpebk:
				# released by completion of last response
				p.trig = TRIG_REQ_DEL
				p.lag = tm - prev_rpe
				if trace and self.trace:
				    print '%s#%d - released by resp at %.3f lag = %.3f' % (self.nm, p.indx, latest/1000.0, p.lag/1000.00)

			    elif latest == prev_rqe:
				# released by completion of last request departure
				if trace and self.trace:
				    print '%s#%d - released by prev request at %.3f' % (self.nm, p.indx, latest/1000.0)
				p.trig = TRIG_REQ_Q
				p.lag = tm - prev_rqe
			    else:
				# released by an arriving ack
				if trace and self.trace:
				    print '%s#%d - released by ACK at %.3f pkt %d' % (self.nm, p.indx, latest/1000.0, rel.ack_pkt.indx)
				    p.trig = TRIG_REQ_REL
				    p.lag = tm - rapt

		    elif trace and self.trace:
			print

		if tm == rqe:
		    # last seg of request
		    p.markers |= P_RLAST
		    if trace and self.trace:
			print '%s#%d last of request %d' % (self.nm, 
					  p.indx, reqi)
			if len(p.tstr):
			    p.tstr += ')'
			else:
			    p.tstr += '%d)' % (reqi)
		    reqi += 1
		    try:
			ttms = self.mach.ttms[reqi]
			rqs = ttms.rqs
			rqe = ttms.rqe
			rps = ttms.rps
			rpe = ttms.rpe
		    except IndexError:
			# may be out of transactions
			ttms = None
			rps = None
			rpe = None
			rqs = None
			rqe = None
			break
		    else:
			self.reverse.reqi += 1
		else:
		    # won't be another start
		    break

	    self.ttms = ttms
	    self.reqi = reqi

	try:
	    lp = self.lastp
	    if not (lp.markers & (P_RLAST | P_FIRST)) and lp.len != self.segsz:
		p.trig = gdel
		p.lag = tm - lp.tm
	except UnboundLocalError:
	    # more packets after apparently finished
	    str = 'TCPConn #%d DELIVERY AFTER TRANS END: %s#%d' % (self.connid, self.nm, p.indx)
	    if trace and self.trace:
		whoops(str)
	    self.mach.logfun(str)
	    p.remark =1


##############################################################################

    def dep_seg(self, p):

	trace = 1

	if not self.conn_state & CONNECTED:
	    str = 'TCPConn #%d DATA SEG WHILE UNCONNECTED: %s#%d' % (self.connid, self.nm, p.indx)
	    if trace and self.trace:
		whoops(str)
	    self.mach.logfun(str)
	    p.remark =1
	    self.give_up()

	seq = p.seq
	plen = p.len
	end = p.end = seq_add(seq, plen)

	self.dep_segs = self.dep_segs + 1
	self.dep_octs = self.dep_octs + plen
	self.snd_segs.append(p)

	self.check_segsz(p)
	self.check_segseq(p)
	# Check the segment against pending releases
	self.cmp_release(p)

##############################################################################

    def dep_fin_pkt(self, p):

	if self.conn_state & FIN_SENT:
	    if self.trace:
		inform('%s#%d DUP FIN dep' % (self.nm, p.indx))

	self.conn_state = self.conn_state | FIN_SENT
	p.end =  seq_add(self.snd_nxt, 1)
	self.finack = p.end
	self.snd_nxt = p.end
	self.snd_segs.append(p)

##############################################################################

    def departure(self, p):

	#flags = ord(p.flags)
	flags = p.flags

	if self.first == None:
	    #print '%s#%d setting as first' % (self.nm, p.indx)
	    self.first = p
	    p.markers = p.markers | P_FIRST

	if flags & TH_SYN:
	    self.dep_syn_pkt(p)
	    self.lastp = p

	if flags & TH_ACK:
	    self.dep_ack_pkt(p)

	if p.len:
	    self.dep_seg(p)
	    self.lastp = p

	if flags & TH_FIN:
	    self.dep_fin_pkt(p)

	#self.lastp = p

##############################################################################

    def arr_syn(self, p):

	trace = 1

	#print '%s#%d SYN arr' % (self.nm, p.indx)

	# duplicate?
	if self.conn_state & SYN_RECD:
	    if p.seq == self.irs:
		# duplicate
		str = 'TCPConn #%d  DUP ARR SYN: %s#%d' % (self.connid, self.nm, p.indx)
		if trace and self.trace:
		    whoops(str)
		self.mach.logfun(str)
		p.remark =1
		self.dup_arr_syns = self.dup_arr_syns + 1
		if self.conn_state & SYN_ACK_SENT:
		    # seen the ACK arr q popped
		    pass
		else:
		    self.rcv_segs.pop(0)
		

	self.conn_state = self.conn_state | SYN_RECD
	self.irs = p.seq
	self.rcv_nxt = seq_add(self.irs, 1)
	self.snd_wnd = p.window

	self.snd_highack = self.irs

	self.rcv_segs.append(p)
	self.arr_synp = p

## 	#
## 	r = seq_Release(p.seq, self.rcv_nxt, p)
## 	r.rtt = 0

##############################################################################

    #
    ## Identify and check SYNACK
    #
    def arr_synack(self, p):

	trace = 1

	if trace and self.trace:
	    print '%s#%d IN SYNACK' % (self.nm, p.indx)
	ack = p.ack
	self.arr_synackps.append(p)
	if self.conn_state & SYN_SENT:
	    # duplicate ?
	    if self.conn_state & SYN_ACK_RECD:
		str = 'TCPConn #%d DUP SYNACK: %s#%d' % (self.connid, self.nm, p.indx)
		if trace and self.trace:
		    whoops(str)
		self.mach.logfun(str)
		self.dup_arr_synacks = self.dup_arr_synacks + 1
		# pop the first one
		self.rcv_acks.pop(0)
		#assess delay
		self.dup_synack_delay = p.tm - self.arr_synackps[0].tm
		self.dup_syn_delay = 0 #ACK delay takes precedence
		
	    if p.ack == self.synack:
		if trace and self.trace:
		    print '%s#%d SYNACK 0K' % (self.nm, p.indx)
		self.conn_state = self.conn_state | SYN_ACK_RECD
		#self.connected = 1
		#self.rcv_highack = ack
		if trace and self.trace:
		    print '%s#%d SYNACK %d snd_segs first seq = %d iss = %d' % (self.nm, p.indx, len(self.snd_segs), self.snd_segs[0].seq, self.iss)
		if len(self.snd_segs) == 1 and self.snd_segs[0].seq == self.iss:
		    if self.synunack:
			synp = self.dep_synps[-1]
			if trace and self.trace:
			    print '%s#%d unACKed SYN #%d' % (self.nm, 
					    p.indx, synp.indx)
			lag =  self.arr_synackps[-1].tm - synp.tm
			self.syn_rrtt = lag
			#p.prtt = rtt
			p.lag = lag
## 			if synp.prtt:
## 			    p.trtt = rtt + synp.prtt
		    #self.IW = self.segsz*self.imp.iw_fact#XX
		    #self.cwnd = self.IW#XX
		    #self.cwnd = self.segsz*self.imp.iw_fact
		    self.sstart = 1
		    p.markers = p.markers | P_PHASE_SS
		    if trace and self.trace: 
			print '%s#%d SYNACK_RECD - lag = %.3f' % (self.nm, p.indx, p.lag/1000.0)

		    if self.reverse.conn_state & SYN_ACK_RECD:
			self.conn_state |= CONNECTED
			self.reverse.conn_state |= CONNECTED
			if trace and self.trace: 
			    print '%s#%d Marking connected' % (self.nm, p.indx)
		else:
		    p.remark = 1
		    str = 'TCPConn #%d UNMATCHED SYNACK: %s#%d' % (self.connid, 
							   self.nm, p.indx)
		    if trace and self.trace:
			whoops(str)
		    self.mach.logfun(str)
		    self.giveup()
		    #sys.exit(1)
	    else:
		p.remark = 1
		str = 'TCPConn #%d SYNACK wrong: %s#%d expected %d - got %d' % (self.connid, self.nm, p.indx, seq_add(self.iss, 1), ack)
		if trace and self.trace:
		    whoops(str)
		self.mach.logfun(str)
		    #sys.exit(0)
	else:
	    p.remark = 1
	    str = 'TCPConn #%d  non SYNACK ack (unconnected): %s#%d' % (self.connid, 
				   self.nm, p.indx)
	    if trace and self.trace:
		whoops(str)

	self.synunack = 0
	

##############################################################################

    def arr_ack(self, p):
	#print '%s #%d ARR_ACK %d self.highack = %d' % (self.nm, p.indx, p.ack, self.rcv_highack)
	cwnd_trace = 1
	rel_trace = 1
	ack_trace = 1

	ack = p.ack

	# looks reasonable?
	if seq_lt(ack, self.rcv_highack):
	    p.remark = 1
	    str = 'TCPConn #%d LOW_ACK ARRIVED: %s#%d - got %d expected >= %d' % (self.connid, self.nm, p.indx, ack, self.rcv_highack)
	    if ack_trace and self.trace:
		whoops(str)
	    self.mach.logfun(str)
	    self.too_low_acks.append(p)
	    p.markers |= P_BOGUS_ACK
	    return
	    #sys.exit(0)
	    #raw_input('........anything to continue\n')

	if seq_gt(ack, self.snd_nxt):
	    p.remark = 1
	    str = 'TCPConn #%d  HIGH ACK ARRIVED: %s#%d - got  %d expected <= %d' % (self.connid, self.nm, p.indx, ack, self.snd_nxt)
	    if ack_trace and self.trace:
		whoops(str)
	    self.mach.logfun(str)
	    #sys.exit(0);
	    #raw_input('........anything to continue\n')
	    # TODO - something with this
	    self.too_high_acks.append(p)
	    p.markers |= P_BOGUS_ACK
	    return

	self.rcv_acks.append(p)

	self.snd_wnd = p.window 

	# First data ACK'd - establish initial window
	if self.IW == None and (self.conn_state & CONNECTED) and self.dep_octs:
	    self.set_IW(p)
	
 	if self.synunack or not self.conn_state & SYN_ACK_RECD:
	    # Check is SYNACK
	    self.arr_synack(p)
	elif ack != self.rcv_highack:
	# ACK increased
	    # Adjust cwnd
	    if cwnd_trace and self.trace:
		print '%s#%d adj cwnd + ' % (self.nm, p.indx),
	    if self.cwnd < self.ssthresh:
		if  cwnd_trace and self.trace:
		    print '%d (ss)' % (self.segsz)
		self.cwnd += self.segsz
		self.sstart = 1
		p.markers |= P_PHASE_SS
	    else:
		if cwnd_trace and self.trace:
		    print '%d (ca)' % ((self.segsz*self.segsz)/self.cwnd)
		self.cwnd += ((self.segsz*self.segsz)/self.cwnd)
		self.sstart = 0

	if ack != self.rcv_highack:
	    ## remove acked pkts from sent list
	    if ack_trace and self.trace:
		print '%s#%d arr ack ' % (self.nm, p.indx),
	    p_acked = 0
	    llen = len(self.snd_segs)
	    if llen == 0:
		if ack_trace and self.trace:
		    print ' - no sent packets'
	    while llen:
		snt = self.snd_segs[0]
		send  = snt.end
		if seq_lte(send, ack):
		    if ack_trace and self.trace:
			print 'of %d ' % (snt.indx),
		    if ack == send:
			# pkt is last acked by this ack
			if not p.len:
			    #not piggy-backed
			    p.trig = TRIG_ACK
			    p.lag = p.tm - snt.tm
			    p.pkt_acked = snt
			if snt.rel!= None:
			    if self.trace and ack_trace:
				print '(rel by %d) ' % (snt.rel.ack_pkt.indx),
				print 'lag = %.3f popped %d' % (p.lag/1000.0, snt.indx)
		    self.snd_segs.pop(0)
		    llen = llen -1
		    p_acked += 1
		else:
		    break
	    if ack_trace and self.trace:
		print 'acked = %d' % (p_acked)
	    if p_acked < 2:
		if p.ack == self.synack:
		    p.trig = TRIG_SYNACK
		elif p.ack == self.finack:
		    p.trig = TRIG_FINACK
		elif not p.len:
		    # tmp conditional
		    if p.lag:
			p.trig = TRIG_DEL_ACK
            elif p_acked > 2 and p.ack != self.finack:
                str = 'TCPConn #%d MULTI-ACK: %s#%d - %d segs' % (self.connid, self.nm, p.indx, p_acked)
                self.mach.logfun(str)

	else:
	    if not p.len:
		# repeat ack - don't use for rtt calc
		#p.dcause = p.dcause | P_RTT_INVALID
		p.trig = TRIG_RTT_INVALID

		

    ## 	if p_acked:
    ## 	    print '%s#%d arr acked %d seg_rtt = %.3f' % (self.nm, 
    ## 						  p.indx, p_acked, rtt/1000.0)



	self.rcv_highack = ack
	    

	## Highest seq released by this ack
	if self.snd_wnd < self.cwnd:
	    w = self.snd_wnd 
	    wboss = 0
	    bs = 'snd_wnd'
	else:
	    w = self.cwnd
	    wboss = P_REL_BOSS_CWND
	    bs = 'cwnd'

	
	release = seq_add(ack,w)
	#if release > self.release:
	if seq_gt(release, self.release):
	    # push onto pending releases
	    if self.trace:
		p.highrel = release
		if rel_trace:
		    print '%s#%d release %d-%d (%+d) boss %s' % (self.nm, 
			   p.indx, self.release, release, 
			       seq_diff(release, self.release), bs)
	    r = seq_Release(self.release, release, p)
	    self.releases.append(r)
	    #p.rel = r
	    #p.highrel = release
	    p.markers = p.markers | wboss
	    self.release = release 
	
	
	


##############################################################################

    def arr_seg(self, p):

	seq = p.seq
	len = p.len
	end = seq_add(seq, len)

	self.rcv_segs.append(p)


	if seq == self.rcv_nxt:
	    self.rcv_nxt = end # TODO - check len within rcv wnd
	elif seq_gt(seq, self.rcv_nxt):
	    # there's a gap
	    p.markers = p.markers | P_SEQ_HELD
	    self.rcv_segheld.append(p)
	elif seq_gt(end, self.rcv_nxt):
	    # some o'lap
	    self.rcv_olap_p = self.rcv_olap_p + 1 
	    self.rcv_olap_o = self.rcv_olap_o + seq_sub(self.rcv_nxt, seq)
	    self.rcv_nxt = end 
	else:
	    # totally duplicated
	    self.rcv_rtmts_p = self.rcv_rtmts_p + 1
	    self.rcv_rtmts_o = self.rcv_rtmts_o + len
	    # remove from received list
	    self.rcv_segs.pop()
	    

##############################################################################


    def arr_fin_pkt(self, p):

	if self.conn_state & FIN_RECD:
	    str = 'TCPConn #%d DUP FIN: %s#%d' % (self.connid, self.nm, p.indx)
	    if self.trace:
		whoops(str)
	    self.mach.logfun(str)
	    
	self.conn_state = self.conn_state | FIN_RECD

	#r = seq_Release(p.seq, seq_add(p.seq, 1), p)
	new = seq_add(self.release, 1)
	r = seq_Release(self.release, new, p)
	self.releases.append(r)
	if self.trace:
	    print '%s#%d FIN release appended %d-%d' % (self.nm, p.indx, 
			      self.release, new)
	self.release = new
	
	    

##############################################################################

    def arrival(self, p):

	flags = p.flags

	if flags & TH_SYN:
	    self.arr_syn(p)

	if flags & TH_ACK:
	    self.arr_ack(p)

	if p.len:
	    self.arr_seg(p)

	if flags & TH_FIN:
	    self.arr_fin_pkt(p)

##############################################################################

    def set_IW(self, p):

	#print '%s#%d set IWF @ %d/%d octs' % (self.nm, p.indx, self.dep_octs, self.pl)
	trace = 1

        try:
            rel = self.releases[-1]
        except IndexError:
            #print '%s#%d set IWF no releases' % (self.nm, p.indx)
            return
            
	rp = rel.ack_pkt
	octs = self.dep_octs
	if octs == self.pl:
	    # all data sent - can't tell anything
	    self.imp = self.imp.clone([('iw_fact', 0)])
            self.IW = 0
	    return
	
	q, r = divmod(self.dep_octs, self.segsz)
	if r:
	    q += 1
	self.IW = self.cwnd = q*self.segsz
        #print 'set IW', self.IW
	imp = self.imp
	if q != imp.iw_fact:
	    self.imp = imp.clone([('iw_fact', q)])
	#print '%s#%d set IWF = %d' % (self.nm, p.indx, q)
	#self.print_releases()
	#print self.snd_nxt
	if len(self.releases) > 1:
	    str = 'TCPConn #%d Set_IW - too many initial releases: %s#%d' % (self.connid, self.nm, p.indx)
	    if self.trace:
		whoops(str)
		self.print_releases()
		#raw_input('...')
	    self.mach.logfun(str)
	if rel.highseq != self.snd_nxt and not(seq_add(rel.highseq, 1) == self.snd_nxt and (self.conn_state & FIN_SENT)):
	    str = 'TCPConn #%d Set_IW - rel_highseq = %d snd_nxt = %d FIN_RECD = %x: %s#%d' % (self.connid, rel.highseq, self.snd_nxt, self.conn_state & FIN_SENT, self.nm, p.indx)
	    if self.trace:
		whoops(str)
		self.print_releases()
		#raw_input('...')
	    self.mach.logfun(str)
	    #sys.exit(1)

	self.releases = []
	sw = rp.window

	if sw < self.cwnd:
	    w = sw 
	    wboss = 0
	    bs = 'snd_wnd'
	else:
	    w = self.cwnd
	    wboss = P_REL_BOSS_CWND
	    bs = 'cwnd'

	
	self.release = seq_add(rp.ack,w)
	if self.trace:
	    rp.highrel = self.release
	    if trace:
		    print '%s resetting #%d release %d boss %s' % (self.nm, 
			   rp.indx, self.release, bs)
	    rp.markers = rp.markers | wboss 
	

	    

##############################################################################

    def print_releases(self):

	print 'current releases:'
	for r in self.releases:

	    print '\t %d-%d by %d highrel %d' % (r.start, 
		r.end, r.ack_pkt.indx, r.highseq)

##############################################################################

	    
