! TCP (RFC 793) module for INet process, GDMR, Feb .. May 1987 ! Revised version Feb. 1988 includes (will include): ! deferred ACK, in case it can be piggy-backed ! RTT as per Jacobson ! ? congestion avoidance, as per Jacobson ! To do: process PSH and URG ! Optional features: ! byte-swapping for network order ({N}/!N!) ! segment tracing ({T}/!T!) !%option "-NonStandard-NoCheck-NoTrace-NoDiag-NoStack-NoLine" %option "-NonStandard-NoCheck" %include "INet:Formats.Inc" %include "INet:Utility.Inc" %include "INet:INet.Inc" %include "INet:Stats.Inc" %externalrecord(INet statistics fm)%spec stats %externalroutinespec IP outbound(%record(buffer fm)%name b) %externalrecord(queue fm)%spec TCP inbound queue %externalrecord(queue fm)%spec TCP outbound queue %externalrecord(queue fm)%spec TCP packet delivered queue %externalpredicatespec TCP send to user(%record(buffer fm)%name b) %externalroutinespec TCP send close(%integer unit) %externalroutinespec TCP open response(%integer unit, error, ra, rp, lp) %externalroutinespec TCP claim response(%integer unit, error, allocated) %externalroutinespec TCP enable(%integer unit) %externalroutinespec TCP disable(%integer unit, error) %externalroutinespec TCP block(%integer unit) %externalroutinespec TCP unblock(%integer unit) %externalpredicatespec TCP interpret user request(%record(buffer fm)%name b) %externalintegerfnspec IP source for(%integer d) %externalintegerspec max TCP %systemroutinespec phex(%integer x) %externalrecord(TCB table fm)%namespec TCB table %constinteger ACK threshold = 8 %constinteger TCP retransmit limit = 8 %constinteger TCP privileged limit = 1024 %owninteger TCP last privileged = TCP privileged limit %owninteger TCP last unprivileged = TCP privileged limit %owninteger TCP tag = 0 %routine print TCB address(%record(TCB fm)%name t) print inet address(t_remote address) print symbol('.'); write(t_remote port, 0) print symbol('/'); write(t_local port, 0) %end !! %routine show TCP header(%record(TCP header fm)%name h, !! %integer n, %string(15) s, %integer tag) !! printstring("TCP "); printstring(s); space; write(tag, 0) !! printstring(" -- source: "); write(h_source, 0) !! printstring(", dest: "); write(h_destination, 0) !! printstring(", seq: "); phex(h_seq) !! printstring(", ack: "); phex(h_ack) !! printstring(" FIN") %if h_flags & FIN bit # 0 !! printstring(" SYN") %if h_flags & SYN bit # 0 !! printstring(" RST") %if h_flags & RST bit # 0 !! printstring(" PSH") %if h_flags & PSH bit # 0 !! printstring(" ACK") %if h_flags & ACK bit # 0 !! printstring(" URG") %if h_flags & URG bit # 0 !! %if n # 0 %start !! printstring(" + "); write(n, 0) !! %finish !! newline !! printstring("Window: "); write(h_window, 0) !! printstring(", urgent: ") %and write(h_urgent, 0) %if h_flags & URG bit # 0 !! newline !! %if (h_data offset >> 4) & 15 > 5 %start !! ! Options !! printstring("Options: "); phex(h_options); newline !! %finish !! %end !! %routine show TCB(%record(TCB fm)%name TCB) !! %integer now, offset !! %return %if TCB == nil !! %if TCB_local port = 0 %start !! printstring("*free*"); newline !! %return !! %finish !! print inet address(TCB_remote address); print symbol('.') !! write(TCB_remote port, 0); printstring(" <--> ") !! write(TCB_local port, 0); newline !! printstring("State "); printstring(TCP state name(TCB_state)) !! printstring(", from "); printstring(TCP state name(TCB_previous state)) !! now = msecs timestamp !! offset = now - TCB_state change stamp !! printstring(", changed ") !! %if offset >= 0 %start !! write(offset, 0); printstring(" msec") !! print symbol('s') %if offset # 1 !! %else !! printstring("ages") !! %finish !! printstring(" ago"); newline !! printstring("Snd nxt: "); phex(TCB_snd nxt) !! printstring(", ISS: "); phex(TCB_iss) !! printstring(", sent: "); write(TCB_snd nxt - TCB_iss, 0) !! printstring(", una: "); phex(TCB_snd una) !! printstring(", window: "); write(TCB_snd wnd, 0); newline !! printstring("Rcv nxt: "); phex(TCB_rcv nxt) !! printstring(", IRS: "); phex(TCB_irs) !! printstring(", received: "); write(TCB_rcv nxt - TCB_irs, 0) !! printstring(", window: "); write(TCB_rcv wnd, 0); newline !! %end ! TCP tracing. Maintain a circular list of the last "few" TCP ! operations as an aid to debugging. !T! %constinteger max TCP trace = 127; ! Suitable for masking !T! %conststring(31) TCP trace name = "INET__TCP_TRACE_BUFFER" !T! !T! %constinteger TCP trace data = 96 !T! %constinteger TCP trace in = 1 !T! %constinteger TCP trace out = 2 !T! %constinteger TCP trace reTX = 3 !T! %constinteger TCP trace check = 4 !T! %constinteger TCP trace ACK = 5 !T! !T! %recordformat TCP trace fm(%integer inout, data bytes, !T! %record(TCB fm) TCB, !T! %record(TCP header fm) TCP header, !T! %bytearray data(1 : TCP trace data)) !T! %constinteger TCP trace size = 4 + 4 + TCB size + %c !T! TCP header size + 4 {options} + TCP trace data !T! !T! %recordformat TCP trace buffer fm(%integer next, !T! %record(TCP trace fm)%array t(0 : max TCP trace)) !T! !T! %ownrecord(TCP trace buffer fm)%name TCP trace buffer == nil !T! !T! %routine TCP trace(%record(buffer fm)%name b, %integer inout) !T! ! Buffer is sufficient, as we can find everything else of interest !T! ! from there. !T! %record(TCP trace fm)%name t !T! %integer data copy, i !T! %bytename x !T! %if TCP trace buffer == nil %start !T! ! Must initialise !T! TCP trace buffer == %c !T! named heap get(TCP trace size * (max TCP trace + 1) + 4, !T! TCP trace name) !T! !! printstring("Trace buffer initialised at ") !T! !! phex(addr(TCP trace buffer)); newline !T! %finish !T! !! printstring("TCP trace: "); phex(addr(b)); newline !T! t == TCP trace buffer_t(TCP trace buffer_next & max TCP trace) !T! TCP trace buffer_next = TCP trace buffer_next + 1 !T! t_inout = inout !T! %if b_TCB == nil %then t_TCB = 0 %else t_TCB = b_TCB !T! t_TCP header = b_TCP header !T! t_data bytes = b_data bytes !T! data copy = b_data bytes !T! data copy = TCP trace data %if data copy > TCP trace data !T! %if data copy > 0 %start !T! x == b_data start !T! %for i = 1, 1, data copy %cycle !T! t_data(i) = x !T! x == x [1] !T! %repeat !T! %finish !T! %end ! TCB manipulation %record(TCB fm)%map find TCB(%integer remote address, remote port, local port) ! Try for an exact match if possible. If not, try for a wild match. %record(TCB fm)%name t, possible == nil %integer i !! printstring("Looking for TCB for "); print inet address(remote address) !! write(remote port, 1); write(local port, 1); newline %for i = 1, 1, max TCP %cycle t == TCB table_TCB(i) !! printstring("Trying "); print inet address(t_remote address) !! write(t_remote port, 1); write(t_local port, 1); newline %if t_local port = local port %start t_slot = i; ! Just in case! %result == t %if t_remote address = remote address %c %and t_remote port = remote port possible == t %if t_remote address = 0 %finish %repeat %result == possible %end %routine setup TCB(%record(TCB fm)%name t) %integer slot %signal 13, t_slot %if t_local port # 0; ! Disaster slot = t_slot; t = 0; t_slot = slot initialise queue(t_blocked send queue) initialise queue(t_retransmit queue) initialise queue(t_reordering queue) initialise queue(t_state change queue) t_avg = TCP initial RTT << 3 t_mdev = TCP initial RTT; !?? t_rto = TCP initial RTT t_rcv wnd = TCP initial receive window %end %routine flush TCB queues(%record(TCB fm)%name TCB) purge queue(TCB_retransmit queue) purge queue(TCB_reordering queue) purge queue(TCB_state change queue) purge queue(TCB_blocked send queue) %end %routine new state(%record(TCB fm)%name t, %integer new state) %record(buffer fm)%name b %signal 13, new state %unless 0 < new state <= TCP time wait !! printstring("TCP state change: "); printstring(TCP state name(t_state)) !! printstring(" -> "); printstring(TCP state name(new state)); newline t_previous state = t_state t_state = new state t_state change stamp = msecs timestamp %cycle b == dequeue buffer(t_state change queue) %exit %if b == nil %signal 13 %if b_next queue == nil enqueue buffer(b, b_next queue) %repeat %end !! %routine print state(%integer state) !! %if TCP listen <= state <= TCP time wait %start !! printstring(TCP state name(state)) !! %else !! write(state, 0) !! %finish !! %end %routine delete TCB(%record(TCB fm)%name TCB, %integer status) %if TCB_local port = 0 %start ! Not opened. Must have been a packet still in transit? pdate printstring("Attempting to delete non-open TCB at ") phex(addr(TCB)) printstring(", user status would be ") write(status, 0); printstring(" (") phex(status); print symbol(')'); newline %return %finish !! printstring("Delete TCB: state "); print state(TCB_state); newline TCP disable(TCB_slot, status) flush TCB queues(TCB) TCP enable(TCB_slot) TCB = 0 %end ! TCP processing proper.... %routine remove acknowledged(%record(TCB fm)%name TCB) %record(buffer fm)%name b %integer meas !! printstring("Remove acknowledged: "); phex(TCB_snd una); newline %cycle b == dequeue buffer(TCB_retransmit queue) %return %if b == nil; ! No more pending %if b_seq - TCB_snd una > 0 %start ! Not ACKed yet -- put it back again !! printstring("Not ACKed: "); phex(b_seq); newline requeue buffer(b, TCB_retransmit queue) %return %finish ! RTT calculation, as per Jacobsen meas = msecs timestamp - b_stamp meas = meas - (TCB_avg >> 3) TCB_avg = TCB_avg + meas meas = -meas %if meas < 0 meas = meas - (TCB_mdev >> 2) TCB_mdev = TCB_mdev + meas TCB_rto = (TCB_avg >> 3) + (TCB_mdev >> 1) ! End RTT calculation !T! TCP trace(b, TCP trace ACK) release buffer(b) %repeat %end %routine transmit segment(%record(buffer fm)%name b, %integer options, retransmit) ! Calculate the checksum, convert to net order, forward to IP %integer c, header size b_seq <- b_TCP header_seq + b_data bytes b_seq <- b_seq + 1 %if b_TCP header_flags & SYN bit # 0 b_seq <- b_seq + 1 %if b_TCP header_flags & FIN bit # 0 ! ACKed when snd una >= _seq b_stamp = msecs timestamp %if b_TCB == nil %start ! No context at this end, use default b_interval = TCP initial RTT << 2 ! (it probably won't be used anyway!) %else b_interval = b_TCB_rto %if b_interval < TCP reTX lbound %start b_interval = TCP reTX lbound %else %if b_interval > TCP reTX ubound b_interval = TCP reTX ubound %finish %finish b_life = 1 %if options = 0 %then header size = 16_50 %else header size = 16_60 b_TCP header_data offset = header size b_IP bytes = b_data bytes + 4 * (header size >> 4) b_protocol = IP TCP protocol !T! TCP trace(b, TCP trace out) !! show TCP header(b_TCP header, b_data bytes, "transmit", b_tag) !N! net order short(b_TCP header_source) !N! net order short(b_TCP header_destination) !N! net order long(b_TCP header_seq) !N! net order long(b_TCP header_ack) !N! net order short(b_TCP header_window) !N! net order short(b_TCP header_checksum) !N! net order short(b_TCP header_urgent) !N! ! Options (if any) assumed to be already in net order b_TCP header_checksum = 0; ! Initially c = calculate pseudo checksum(b_TCP header, b_IP bytes, IP source for(b_IP peer), b_IP peer, IP TCP protocol, b_IP bytes) b_TCP header_checksum <- \c b_TCP header_checksum <- 16_FFFF %if b_TCP header_checksum = 0; ! -0 safer %if retransmit = 0 %start ! Don't want to retransmit this one !! printstring("Don't retransmit: "); phex(b_seq); newline b_next queue == nil %else ! Must retransmit this one !! printstring("-> retransmit queue: "); phex(b_seq); newline b_next queue == b_TCB_retransmit queue b_flags = 0; !<<<<<<<<<<< %finish stats_TCP packets out = stats_TCP packets out + 1 stats_TCP bytes out = stats_TCP bytes out + b_IP bytes IP outbound(b) %end %routine return reset(%record(buffer fm)%name b, %integer ACK in) ! If ACK in = 0 we send 0 as seq, together with a reasonable ACK, ! otherwise we echo ACK as seq and send no ACK %integer ack value %short s s = b_TCP header_source; b_TCP header_source = b_TCP header_destination b_TCP header_destination = s %if ACK in = 0 %start ack value <- b_TCP header_seq + b_data bytes ack value <- ack value + 1 %if b_TCP header_flags & SYN bit # 0 ack value <- ack value + 1 %if b_TCP header_flags & FIN bit # 0 b_TCP header_ack = ack value b_TCP header_seq = 0 b_TCP header_flags = RST bit ! ACK bit %else b_TCP header_seq = b_TCP header_ack b_TCP header_ACK = 0 b_TCP header_flags = RST bit %finish b_data bytes = 0 transmit segment(b, 0, 0) %end %routine send ACK(%record(TCB fm)%name TCB) %record(buffer fm)%name b !! printstring("Send ACK "); phex(TCB_rcv nxt) !! printstring(" for TCB "); phex(addr(TCB)); newline b == claim buffer b_IP peer = TCB_remote address b_TCB == TCB b_data bytes = 0 b_TCP header == record(addr(b_data(64))) b_TCP header_source = TCB_local port b_TCP header_destination = TCB_remote port b_TCP header_seq <- TCB_snd nxt b_TCP header_ack <- TCB_rcv nxt b_TCP header_flags = ACK bit b_TCP header_window = TCB_rcv wnd transmit segment(b, 0, 0) TCB_ACK pending = 0 %end %routine TCP send window updated(%record(TCB fm)%name TCB) %record(buffer fm)%name b !! printstring("Send window updated: now ") !! write(TCB_snd wnd, 0); newline %cycle ! This will probably only ever go round the loop once. However, it ! is just possible that the user will send us another bufferload ! before we got round to inhibiting the unit, in which case we ! might just have several buffers queued here.... Note that we don't ! dequeue the buffer until we're sure it can be sent. b == record(addr(TCB_blocked send queue_forward)) %exit %if b == record(addr(TCB_blocked send queue)); ! Empty ! Is the window open enough? ! (snd nxt + data <= snd una + snd wnd) %return %if TCB_snd nxt + b_data bytes - TCB_snd una - TCB_snd wnd > 0 !! printstring("Send window opened for ") !! write(b_data bytes, 0); newline b == dequeue buffer(TCB_blocked send queue) b_IP peer = TCB_remote address stats_TCP data bytes sent = stats_TCP data bytes sent + b_data bytes b_TCP header == record(addr(b_data start) - TCP header size) b_TCP header_source = TCB_local port b_TCP header_destination = TCB_remote port b_TCP header_seq <- TCB_snd nxt b_TCP header_ack <- TCB_rcv nxt b_TCP header_window = TCB_rcv wnd TCB_ACK pending = 0 %if b_code = TCP send request %start b_TCP header_flags = ACK bit ! PSH bit TCB_snd nxt <- TCB_snd nxt + b_data bytes TCB_snd wnd = TCB_snd wnd - b_data bytes %else b_TCP header_flags = ACK bit ! FIN bit b_data bytes = 0; ! Unfrig TCB_FIN seq <- b_TCP header_seq TCB_snd nxt <- TCB_snd nxt + 1 TCB_snd wnd = TCB_snd wnd - 1 %if TCB_state = TCP close wait %then new state(TCB, TCP last ack) %c %else new state(TCB, TCP fin wait 1) %finish transmit segment(b, 0, 1) %repeat %if TCB_blocked send # 0 %start !! printstring("Unblock unit "); write(TCB_slot, 0); newline TCP unblock(TCB_slot) TCB_blocked send = 0 %finish %end %externalroutine TCP inbound(%record(buffer fm)%name b) ! Initial TCP segment validation and preliminary processing %record(TCB fm)%name TCB %record(buffer fm)%name x %integer length, middle, rhs, diff, c, header size %short ss %switch second(TCP listen : TCP time wait) !L! lights or A(TCP light) b_next queue == nil -> sixth %if b_flags & do sixth # 0 -> first %if b_flags & do first # 0 !! printstring("TCP inbound"); newline !! dump(byteinteger(addr(b_TCP header)), b_IP bytes) c = calculate pseudo checksum(b_TCP header, b_IP bytes, b_IP peer, IP source for(b_IP peer), IP TCP protocol, b_IP bytes) %if 0 # c # 16_FFFF %start ! Invalid checksum (not +/- 0) !! pdate !! printstring("Invalid TCP checksum ") !! write(c, 0); space; phex(c); printstring(" from ") !! print inet address(b_IP peer); newline !! phex(b_IP bytes); space; phex(b_IP peer) !! space; phex(our address); space; phex(IP TCP protocol); newline !! dump(byteinteger(addr(b_TCP header)), b_IP bytes) stats_TCP checksum errors = stats_TCP checksum errors + 1 b_TCB == nil !T! TCP trace(b, TCP trace check) release buffer(b) -> out %finish !N! net order short(b_TCP header_source) !N! net order short(b_TCP header_destination) !N! net order long(b_TCP header_seq) !N! net order long(b_TCP header_ack) !N! net order short(b_TCP header_window) !N! net order short(b_TCP header_checksum) !N! net order short(b_TCP header_urgent) header size = b_TCP header_data offset >> 2; ! >> 4, then << 2 b_data bytes = b_IP bytes - header size %if b_data bytes < 0 %start ! Something very wrong with this packet. Drop it release buffer(b) -> out %finish b_data start == byteinteger(addr(b_TCP header) + header size) !! printstring("Header size "); write(header size, 0) !! printstring(", data bytes "); write(b_data bytes, 0); newline TCP tag <- TCP tag + 1; b_tag = TCP tag !! show TCP header(b_TCP header, b_data bytes, "inbound", b_tag) stats_TCP packets in = stats_TCP packets in + 1 stats_TCP bytes in = stats_TCP bytes in + b_IP bytes b_TCB == find TCB(b_IP peer, b_TCP header_source, b_TCP header_destination) !T! TCP trace(b, TCP trace in) %if b_TCB == nil %start !! printstring("TCP for closed socket: from ") !! print inet address(b_IP peer) !! print symbol('.'); write(b_TCP header_source, 0) !! printstring(" for "); write(b_TCP header_destination, 0) !! newline stats_TCP for closed = stats_TCP for closed + 1 %if b_TCP header_flags & RST bit = 0 %start return reset(b, b_TCP header_flags & ACK bit) %else release buffer(b) %finish -> out %finish !! show TCB(b_TCB) %if b_TCB_state = TCP listen %start ! First, check for RST %if b_TCP header_flags & RST bit # 0 %start !! printstring("TCP (listen): ignoring reset from ") !! print inet address(b_IP peer); newline stats_TCP resets received = stats_TCP resets received + 1 release buffer(b) -> out %finish ! Second, check for ACK %if b_TCP header_flags & ACK bit # 0 %start !! printstring("TCP (listen): resetting ACK from ") !! print inet address(b_IP peer); newline stats_TCP junk received = stats_TCP junk received + 1 return reset(b, 1) -> out %finish ! Third, check for SYN %if b_TCP header_flags & SYN bit # 0 %start ! Should check security/precedence here. stats_TCP SYNs received = stats_TCP SYNs received + 1 b_TCB_rcv nxt <- b_TCP header_seq + 1 b_TCB_IRS <- b_TCP header_seq %if b_data bytes > 0 %start ! Some data included with this segment {>>>>>>>} x == claim buffer; copy headers(b, x); x_data bytes = 0 ! Now queue anything else in b for later b_TCP header_seq <- b_TCP header_seq + 1 b_TCP header_flags = b_TCP header_flags & (\ SYN bit) b_flags = ignore seq ! ignore SYN ! ignore ACK ! do first %else ! No data, so just use the original x == b; b == nil %finish ! Select an ISS and send a SYN ACK x_TCB_ISS <- generate ISS x_TCB_snd nxt <- x_TCB_ISS + 1 x_TCB_snd una <- x_TCB_ISS !! printstring("ISS "); phex(x_TCB_ISS) !! printstring(" for "); print inet address(x_IP peer) !! print symbol('.'); write(x_TCP header_source, 0) !! print symbol('/'); write(x_TCP header_destination, 0); newline new state(x_TCB, TCP syn received) x_TCB_remote address = x_IP peer x_TCB_remote port = x_TCP header_source x_TCP header_source = x_TCP header_destination x_TCP header_destination = x_TCB_remote port x_TCP header_seq <- x_TCB_ISS x_TCP header_ack <- x_TCB_rcv nxt x_TCP header_window = x_TCB_rcv wnd x_TCP header_flags = SYN bit ! ACK bit byteinteger (addr(x_TCP header_options) ) = 2; ! Define max segment byteinteger (addr(x_TCP header_options) + 1) = 4; ! Size of ^ shortinteger(addr(x_TCP header_options) + 2) <- x_TCB_mrss !N! net order short(shortinteger(addr(x_TCP header_options) + 2)) x_data bytes = 0 transmit segment(x, 1, 1) ! Now process the segment data, if any.... TCP inbound(b) %if b ## nil -> out %finish ! Else, something odd !! pdate !! printstring("TCP (listen): odd packet from ") !! print inet address(b_IP peer); newline stats_TCP junk received = stats_TCP junk received + 1 release buffer(b) -> out %else %if b_TCB_state = TCP syn sent ! First, check the ACK %if b_TCP header_flags & ACK bit # 0 %start %if b_TCP header_ack - b_TCB_ISS <= 0 %c %or b_TCP header_ack - b_TCB_snd nxt > 0 %start stats_TCP dud ACKs received = stats_TCP dud ACKs received + 1 %if b_TCP header_flags & RST bit = 0 %start return reset(b, 1) %else release buffer(b) %finish -> out %finish %if b_TCB_snd una - b_TCP header_ack <= 0 %c %and b_TCP header_ack - b_TCB_snd nxt <= 0 %start stats_TCP good ACKs received = stats_TCP good ACKs received + 1 b_acceptable = 1 %finish %finish ! Second, check the RST bit %if b_TCP header_flags & RST bit # 0 %start stats_TCP resets received = stats_TCP resets received + 1 %if b_acceptable # 0 %start delete TCB(b_TCB, reset error) release buffer(b) %else !! pdate !! printstring("TCP (syn sent): unacceptable ACK from ") !! print TCB address(b_TCB); newline release buffer(b) %finish -> out %finish ! Third, check security/precedence (but we don't bother) ! Fourth, check the SYN bit %if b_TCP header_flags & SYN bit # 0 %start stats_TCP SYNs received = stats_TCP SYNs received + 1 b_TCB_rcv nxt <- b_TCP header_seq + 1 b_TCB_IRS <- b_TCP header_seq %if b_TCP header_flags & ACK bit # 0 %start b_TCB_snd una <- b_TCP header_ack remove acknowledged(b_TCB) %finish %if b_TCB_snd una - b_TCB_ISS > 0 %start ! Our SYN has been ACKed stats_TCP connections established = %c stats_TCP connections established + 1 ! Update the send window stats_TCP window updates = stats_TCP window updates + 1 b_TCB_snd wnd <- b_TCP header_window b_TCB_snd wl1 <- b_TCP header_seq b_TCB_snd wl2 <- b_TCP header_ack new state(b_TCB, TCP established) TCP open response(b_TCB_slot, 0, b_TCB_remote address, b_TCB_remote port, b_TCB_local port) b_TCB_ACK pending = 1 %if b_TCB_ACK pending = 0 ! Process anything else below... -> sixth %finish ! Else no ACK new state(b_TCB, TCP syn received) {>>>>>>>} x == claim buffer; copy headers(b, x); x_data bytes = 0 ss = x_TCP header_source; x_TCP header_source = x_TCP header_destination x_TCP header_destination = ss x_TCP header_seq <- x_TCB_ISS x_TCP header_ack <- x_TCB_rcv nxt x_TCP header_flags = SYN bit ! ACK bit byteinteger (addr(x_TCP header_options) ) = 2; ! Define max segment byteinteger (addr(x_TCP header_options) + 1) = 4; ! Size of ^ shortinteger(addr(x_TCP header_options) + 2) <- x_TCB_mrss !N! net order short(shortinteger(addr(x_TCP header_options) + 2)) x_data bytes = 0 transmit segment(x, 1, 1) b_flags = b_flags ! do first b_next queue == TCP inbound queue enqueue buffer(b, b_TCB_state change queue) -> out %finish ! Fifth, no SYN or RST, so dump it !! pdate !! printstring("TCP (syn sent): no SYN or RST in packet from ") !! print TCB address(b_TCB); newline stats_TCP junk received = stats_TCP junk received + 1 release buffer(b) -> out %finish first:-> seq ignored %if b_flags & ignore seq # 0 ! Not closed, listen or syn sent, so start by checking the sequence number ! This applies to all remaining states. length = b_data bytes length = length + 1 %if b_TCP header_flags & SYN bit # 0 length = length + 1 %if b_TCP header_flags & FIN bit # 0 %if length = 0 %start %if b_TCB_rcv wnd = 0 %start !! printstring("Case 0, 0 ") %if b_TCP header_seq = b_TCB_rcv nxt %start b_acceptable = 1 %else b_acceptable = 0 %finish %else !! printstring("Case 0, >0 ") %if b_TCB_rcv nxt - b_TCP header_seq <= 0 %c %and b_TCP header_seq - b_TCB_rcv nxt - b_TCB_rcv wnd <= 0 %start b_acceptable = 1 %else b_acceptable = 0 %finish %finish %else %if b_TCB_rcv wnd = 0 %start !! printstring("Case >0, 0 ") ! Strictly, nothing should be acceptable here. However, we'll ! pretend that the segment really had no data in it at all and ! carry on -- in that case it's only acceptable if its SEQ is ! (exactly) what we're expecting, as per "Case 0, 0" above. ! Regardless, we send an ACK. b_data bytes = 0 b_TCP header_flags = b_TCP header_flags & (\ (SYN bit ! FIN bit)) %if b_TCP header_seq = b_TCB_rcv nxt %start %if b_TCP header_flags & RST bit = 0 %start b_TCB_ACK pending = 1 %if b_TCB_ACK pending = 0 b_acceptable = 1 %else ! RST set, drop the segment release buffer(b) -> out %finish %else b_acceptable = 0 %finish %else !! printstring("Case >0, >0 ") rhs <- b_TCB_rcv nxt + b_TCB_rcv wnd middle <- b_TCP header_seq + length - 1 %if (b_TCB_rcv nxt - b_TCP header_seq <= 0 %c %and b_TCP header_seq - rhs < 0) %c %or (b_TCB_rcv nxt - middle <= 0 %and middle - rhs < 0) %start b_acceptable = 1 %else b_acceptable = 0 %finish %finish %finish %if b_acceptable = 0 %start stats_TCP unacceptable segments = stats_TCP unacceptable segments + 1 !! printstring("unacceptable"); newline b_TCB_ACK pending = 1 %c %if b_TCP header_flags & RST bit = 0 %and b_TCB_ACK pending = 0 release buffer(b) -> out %else stats_TCP acceptable segments = stats_TCP acceptable segments + 1 !! printstring("acceptable"); newline %finish ! The code which checks whether the segment is what we're expecting next ! has been moved to just before we (should) check the URG bit. This ! appears not to be what the spec says, but it makes more sense just ! to get on and check RST, SYN, ACK as these aren't really related to ! the data content of the segment. We check the segment's seq field ! just before we (would) start to process data. seq ignored: ! Second stage, check the RST bit -> second(b_TCB_state) second(TCP syn received): %if b_TCP header_flags & RST bit # 0 %start !! printstring("RST received"); newline stats_TCP resets received = stats_TCP resets received + 1 %if b_TCB_previous state = TCP listen %start !! printstring("Returning to listen"); newline TCB == b_TCB release buffer(b) new state(TCB, TCP listen) TCB_remote address = 0 TCB_remote port = 0 flush TCB queues(TCB) %else TCB == b_TCB stats_TCP connections reset = stats_TCP connections reset + 1 release buffer(b) delete TCB(TCB, reset error) %finish -> out %finish -> third second(TCP established): second(TCP fin wait 1): second(TCP fin wait 2): second(TCP close wait): %if b_TCP header_flags & RST bit # 0 %start !! printstring("RST received"); newline stats_TCP resets received = stats_TCP resets received + 1 stats_TCP connections reset = stats_TCP connections reset + 1 delete TCB(b_TCB, reset error) release buffer(b) -> out %finish -> third second(TCP closing): second(TCP last ack): second(TCP time wait): %if b_TCP header_flags & RST bit # 0 %start !! printstring("RST received"); newline stats_TCP resets received = stats_TCP resets received + 1 delete TCB(b_TCB, reset error) release buffer(b) -> out %finish third: ! Third, we should check security & precedence (but we don't bother) ! Fourth (all states), we check the SYN bit %if b_flags & ignore SYN = 0 %and b_TCP header_flags & SYN bit # 0 %start !! printstring("Bogus SYN -- resetting"); newline stats_TCP connections reset = stats_TCP connections reset + 1 delete TCB(b_TCB, reset error); b_TCB == nil return reset(b, b_TCP header_flags & ACK bit) -> out %finish ! Fifth, check the ACK bit -> ACK ignored %if b_flags & ignore ACK # 0 %if b_TCP header_flags & ACK bit = 0 %start !! printstring("NO ACK -- dropping..."); newline release buffer(b) -> out %finish %if b_TCB_state = TCP last ack %start %if b_TCB_FIN seq - b_TCP header_ack < 0 %start ! Our FIN has been ACKed !! printstring("Our FIN ACKed"); newline stats_TCP good ACKs received = stats_TCP good ACKs received + 1 delete TCB(b_TCB, 0) release buffer(b) -> out ! Shouldn't this have gone to time-wait ?? %finish ! Nothing else should arrive now. If it does just throw it away !! printstring("Spurious segment in last ack"); newline release buffer(b) -> out %else %if b_TCB_state = TCP time wait b_TCB_ACK pending = 1 %if b_TCB_ACK pending = 0 release buffer(b) -> out %else %if b_TCB_state = TCP syn received %start %if b_TCB_snd una - b_TCP header_ack <= 0 %c %and b_TCP header_ack - b_TCB_snd nxt <= 0 %start stats_TCP good ACKs received = stats_TCP good ACKs received + 1 ! Update the send window stats_TCP window updates = stats_TCP window updates + 1 b_TCB_snd wnd <- b_TCP header_window b_TCB_snd wl1 <- b_TCP header_seq b_TCB_snd wl2 <- b_TCP header_ack new state(b_TCB, TCP established) TCP open response(b_TCB_slot, 0, b_TCB_remote address, b_TCB_remote port, b_TCB_local port) %else stats_TCP dud ACKs received = stats_TCP dud ACKs received + 1 return reset(b, 1) -> out %finish %finish %if b_TCP header_ack - b_TCB_snd una < 0 %start ! Old segment, ignore the ACK stats_TCP old ACKs received = stats_TCP old ACKs received + 1 !! printstring("Old TCP segment, ignoring ACK"); newline %else %if b_TCP header_ack - b_TCB_snd nxt > 0 ! ACK for something we haven't sent yet stats_TCP dud ACKs received = stats_TCP dud ACKs received + 1 !! printstring("TCP ACK for something we haven't sent yet"); newline b_TCB_ACK pending = 1 %if b_TCB_ACK pending = 0 release buffer(b) -> out %else ! Else ACK is new !! printstring("New TCP ACK"); newline stats_TCP good ACKs received = stats_TCP good ACKs received + 1 b_TCB_snd una <- b_TCP header_ack remove acknowledged(b_TCB) %if b_TCB_snd wl1 - b_TCP header_seq < 0 %c %or (b_TCB_snd wl1 = b_TCP header_seq %c %and b_TCB_snd wl2 - b_TCP header_ack <= 0) %start ! Update the send window stats_TCP window updates = stats_TCP window updates + 1 b_TCB_snd wnd <- b_TCP header_window b_TCB_snd wl1 <- b_TCP header_seq b_TCB_snd wl2 <- b_TCP header_ack TCP send window updated(b_TCB) %finish %finish %if b_TCB_state = TCP fin wait 1 %start %if b_TCB_FIN seq - b_TCP header_ack < 0 %start ! Our FIN has been ACKed ! stats_TCP good ACKs received = stats_TCP good ACKs received + 1 new state(b_TCB, TCP fin wait 2) %finish %else %if b_TCB_state = TCP closing %if b_TCB_FIN seq - b_TCP header_ack < 0 %start ! Our FIN has been ACKed stats_TCP good ACKs received = stats_TCP good ACKs received + 1 new state(b_TCB, TCP time wait) TCP disable(b_TCB_slot, 0) ! Should start the timer here, probably.... %finish %finish %finish ACK ignored: sixth:! The following sequence-checking code used to come at the end of the ! first stage, which is what the RFC appears to suggest. It was ! moved here as this seems to be a more sensible place to put it... ! Now see if the segment is what we're expecting next. If it has too ! high a sequence number we assume that we must have missed something: ! in that case we queue the segment on the pending queue, hoping that ! the other end will retransmit the missing one. If it has too low a ! sequence number we trim off the data (and controls) at the front so ! as to coerce it to what we're expecting. Note that we don't bother ! trimming it down if it's too long: instead we just accept the extra ! data, though of course we have to be careful with the window ! adjustment later.... %if b_TCP header_seq - b_TCB_rcv nxt > 0 %start ! Not what we're expecting next !! printstring("Sent ahead -- holding: "); write(b_tag, 0); space !! phex(b_TCP header_seq); space; phex(b_TCB_rcv nxt); newline stats_TCP ahead segments = stats_TCP ahead segments + 1 b_TCB_ACK pending = 1 %if b_TCB_ACK pending = 0 b_flags = b_flags ! do first b_next queue == TCP inbound queue enqueue buffer(b, b_TCB_reordering queue) -> out %finish ! Could be old data. Try to trim off a SYN first.... %if b_TCP header_flags & SYN bit # 0 %start %if b_TCP header_seq - b_TCB_rcv nxt < 0 %start ! Overlaps our window, so trim off the SYN !! printstring("Trim SYN"); newline b_TCP header_flags = b_TCP header_flags & (\ SYN bit) b_TCP header_seq <- b_TCP header_seq + 1 %finish %finish diff <- b_TCP header_seq - b_TCB_rcv nxt %if diff < 0 %start ! (Still) overlaps our window, so trim off some data. Note that ! diff is -ve, so subtracts become adds, & v.v. !! printstring("Trim "); write(-diff, 0); newline stats_TCP duplicate bytes = stats_TCP duplicate bytes - diff b_TCP header_seq <- b_TCP header_seq - diff; ! Move start b_data bytes = b_data bytes + diff; ! Shorten to compensate %signal 13, 98, b_data bytes %if b_data bytes < 0; ! Whole segment is old b_data start == byteinteger(addr(b_data start) - diff) %finish ! Sixth, check the URG bit ! Seventh, proccess the segment text b_next queue == nil %if b_data bytes > 0 %start %if b_TCB_state = TCP established %c %or b_TCB_state = TCP fin wait 1 %or b_TCB_state = TCP fin wait 2 %start ! Deliver the data, mark as to be put on delivered queue !! printstring("Forward "); write(b_data bytes, 0) !! printstring(" from seq "); phex(b_TCP header_seq) !! printstring(" to "); write(b_TCB_slot, 0); newline !! dump(b_data start, b_data bytes) stats_TCP data bytes received = stats_TCP data bytes received + b_data bytes b_next queue == TCP packet delivered queue %if TCP send to user(b) %start ! Packet has been successfully queued to the user, so update ! our receive window and ACK it b_TCB_rcv wnd = b_TCB_rcv wnd - b_data bytes b_TCB_rcv wnd = 0 %if b_TCB_rcv wnd < 0; ! Naughty?? b_TCB_rcv nxt <- b_TCP header_seq + b_data bytes b_TCB_ACK pending = 1 %if b_TCB_ACK pending = 0 ! Recycle the queue of segments pending in case one of them ! has now become current. %cycle x == dequeue buffer(b_TCB_reordering queue) %exit %if x == nil !! printstring("Recycle "); write(x_tag, 0); newline enqueue buffer(x, x_next queue) %repeat %else ! Packet wasn't deliverable for some reason. Drop it and ! hope that when it's retransmitted it will get through.... b_TCB_ACK pending = 1 %if b_TCB_ACK pending = 0 release buffer(b) %finish -> out; ! DON'T fall through %else ! Else ignore the segment text b_data bytes = 0; ! To avoid pointer update later %finish %else %if b_TCP header_flags & (FIN bit ! SYN bit) # 0 ! No data, but control: ACK it. b_TCB_ACK pending = 1 %if b_TCB_ACK pending = 0 %finish ! Enqueue unconditionally, since if the buffer contained user-data and ! was successfully sent to the user then we won't have fallen through ! to here.... enqueue buffer(b, TCP packet delivered queue) out: !L! lights and A(\TCP light) %end %externalroutine TCP packet delivered(%record(buffer fm)%name b) ! Called after the packet delivery has completed. Update our ! receive window, tell the other end, and proccess any FIN. !L!lights or A(TCP light) !! printstring("TCP packet delivered: "); write(b_tag, 0); space !! write(b_data bytes, 0); newline !! show TCB(b_TCB) release buffer(b) %and %return %if b_TCB_local port = 0; ! Aborted? b_next queue == nil %if b_data bytes > 0 %start ! The other side has actually taken delivery of the packet, so we ! can update our receive window again. Send an ack if we have ! just opened our window (??). b_TCB_rcv wnd = b_TCB_rcv wnd + b_data bytes b_TCB_ACK pending = 1 %c %if {b_TCB_rcv wnd = b_data bytes %and} b_TCB_ACK pending = 0 %finish ! Now check for a FIN bit in the last segment !! show TCP header(b_TCP header, b_data bytes, "check FIN", b_tag) !! printstring("Check FIN state: "); print state(b_TCB_state); newline %if b_TCP header_flags & FIN bit # 0 %and %c b_TCB_state # TCP listen %and b_TCB_state # TCP syn sent %start !! printstring("FIN received"); newline stats_TCP FINs received = stats_TCP FINs received + 1 b_TCB_rcv nxt <- b_TCB_rcv nxt + 1 b_TCB_ACK pending = 1 %if b_TCB_ACK pending = 0 %if b_TCB_state = TCP syn received %or b_TCB_state = TCP established %start new state(b_TCB, TCP close wait) %else %if b_TCB_state = TCP fin wait 1 %if b_TCB_FIN seq - b_TCP header_ack < 0 %start ! Our FIN has been ACKed new state(b_TCB, TCP time wait) %else ! We haven't been ACKed yet new state(b_TCB, TCP closing) %finish %else %if b_TCB_state = TCP fin wait 2 new state(b_TCB, TCP time wait) %finish TCP send close(b_TCB_slot) %finish ! Else, remain in the current state release buffer(b) !L!lights and A(\TCP light) %end %routine TCP abort TCB(%record(TCB fm)%name TCB) %record(buffer fm)%name b %switch state(TCP listen : TCP time wait) !! printstring("Abort TCB "); print TCB address(TCB); newline %if TCB_state = TCP closed %start ! Not open yet !! printstring("TCB is closed"); newline ! We have to turn the UCB on again, as otherwise we'll eventually ! run out of slave units.... %if TCB_slot = 0 %start pdate printstring("Aborting: slot = 0 ??"); newline %else TCP disable(TCB_slot, reset error) TCP enable(TCB_slot) %finish %return %finish -> state(TCB_state) %if TCP listen <= TCB_state <= TCP time wait printstring("Bad state "); write(TCB_state, 0); printstring(" in TCB ") print TCB address(TCB); newline delete TCB(TCB, bugcheck error) %return state(TCP syn received): state(TCP established): state(TCP fin wait 1): state(TCP fin wait 2): state(TCP close wait): b == claim buffer b_data bytes = 0 b_IP peer = TCB_remote address b_TCB == TCB b_TCP header == record(addr(b_data(64))) b_TCP header_source = TCB_local port b_TCP header_destination = TCB_remote port b_TCP header_seq <- TCB_snd nxt b_TCP header_ack = 0 b_TCP header_flags = RST bit transmit segment(b, 0, 0) ! Fall throuth to delete the TCB state(TCP listen): state(TCP syn sent): delete TCB(TCB, reset error) %return state(TCP closing): state(TCP last ack): state(TCP time wait): ! Turn off the slave unit -- the time-wait timeout ! will turn it on again. TCP disable(TCB_slot, 0) %end %externalroutine TCP outbound(%record(buffer fm)%name b) %switch op(TCP first request : TCP last request) %record(TCB fm)%name TCB %recordformat connect fm(%integer remote address, remote port, local port, %integer mss) %record(connect fm)%name connect %integer i, which !L! lights or B(TCP light) TCP tag <- TCP tag + 1; b_tag = TCP tag !! printstring("TCP outbound: "); write(b_tag, 0); newline -> op(b_code) %if TCP interpret user request(b) !! printstring("Unknown code "); write(b_code, 0); newline release buffer(b); ! Unknown, throw it away -> out op(TCP open request): connect == record(addr(b_data start)) !! printstring("Open request from "); write(b_TCB_slot, 0) !! printstring(" for "); print inet address(connect_remote address) !! print symbol('.'); write(connect_remote port, 0) !! print symbol('/'); write(connect_local port, 0); newline %if connect_local port <= 0 %start !! printstring("Dud local port "); write(connect_local port, 0) !! newline TCP open response(b_TCB_slot, reset error, 0, 0, 0) release buffer(b) -> out %finish %if connect_local port <= TCP privileged limit %and b_privilege = 0 %start TCP open response(b_TCB_slot, privilege error, 0, 0, 0) release buffer(b) -> out %finish %if b_TCB_local port # 0 %start ! This TCB is already in use TCP open response(b_TCB_slot, exists error, 0, 0, 0) release buffer(b) -> out %finish TCB == find TCB(connect_remote address, connect_remote port, connect_local port) %if TCB ## nil %and TCB_remote address # 0 %start ! Someone else is already using that connection !! printstring("Socket is already in use: ") !! print inet address(TCB_remote address) !! print symbol('.'); write(TCB_remote port, 0) !! print symbol('/'); write(TCB_local port, 0); newline TCP open response(b_TCB_slot, exists error, 0, 0, 0) release buffer(b) -> out %finish setup TCB(b_TCB) %if b_data bytes < 16 %start ! Short connect data -- default the rest b_TCB_mrss = TCP unspecified mrss %else %if connect_mss <= 0 ! Use default b_TCB_mrss = TCP default mrss %else ! Use specified limit b_TCB_mrss = connect_mss %finish b_TCB_remote address = connect_remote address b_TCB_remote port = connect_remote port b_TCB_local port = connect_local port b_TCB_rcv wnd = TCP initial receive window b_TCB_state = TCP listen b_TCB_state change stamp = msecs timestamp %if connect_remote address = 0 %start ! Passive open, remain in listen !! printstring("Passive open: ") !! print TCB address(b_TCB); newline release buffer(b) -> out %finish b_IP peer = connect_remote address b_TCB_rcv wnd = TCP initial receive window b_TCB_ISS = generate ISS !! printstring("Actively opening "); print TCB address(b_TCB) !! printstring(", ISS "); phex(b_TCB_ISS); newline b_TCP header == record(addr(b_data start) - TCP header size - 4) b_TCP header_source = b_TCB_local port b_TCP header_destination = b_TCB_remote port b_TCP header_seq = b_TCB_ISS b_TCP header_ack = 0 b_TCP header_flags = SYN bit b_TCP header_window = 0 byteinteger (addr(b_TCP header_options) ) = 2; ! Define max segment byteinteger (addr(b_TCP header_options) + 1) = 4; ! Size of ^ shortinteger(addr(b_TCP header_options) + 2) <- b_TCB_mrss !N! net order short(shortinteger(addr(b_TCP header_options) + 2)) b_TCB_snd una <- b_TCB_ISS b_TCB_snd nxt <- b_TCB_ISS + 1 new state(b_TCB, TCP syn sent) b_data bytes = 0 transmit segment(b, 1, 1) -> out op(TCP send request): !! printstring("Send request from "); write(b_TCB_slot, 0); newline !! dump(b_data start, b_data bytes) %if b_TCB_state = TCP established %or b_TCB_state = TCP close wait %start ! Is the window open enough? ! (snd nxt + data <= snd una + snd wnd) %if b_TCB_blocked send = 0 %and %c (b_TCB_snd nxt + b_data bytes %c - b_TCB_snd una - b_TCB_snd wnd <= 0) %start ! OK to send b_IP peer = b_TCB_remote address stats_TCP data bytes sent = stats_TCP data bytes sent + b_data bytes b_TCP header == record(addr(b_data start) - TCP header size) b_TCP header_source = b_TCB_local port b_TCP header_destination = b_TCB_remote port b_TCP header_seq <- b_TCB_snd nxt b_TCP header_ack <- b_TCB_rcv nxt b_TCP header_window = b_TCB_rcv wnd b_TCB_ACK pending = 0 b_TCP header_flags = ACK bit ! PSH bit b_TCB_snd nxt <- b_TCB_snd nxt + b_data bytes transmit segment(b, 0, 1) %else must block: ! Send window is too small !! printstring("Must block: "); write(b_data bytes, 0) !! space; phex(b_TCB_snd nxt); space; write(b_TCB_snd wnd, 0) !! space; phex(b_TCB_snd una); write(b_TCB_blocked send, 1) !! newline TCP block(b_TCB_slot) enqueue buffer(b, b_TCB_blocked send queue) b_TCB_blocked send = 1 %finish %else release buffer(b); ! Throw it away **meantime** %finish -> out op(TCP close request): !! printstring("Close request from "); write(b_TCB_slot, 0); newline %if b_TCB_state = TCP listen %or b_TCB_state = TCP syn sent %start ! Shouldn't be here! TCB == b_TCB release buffer(b) delete TCB(TCB, bugcheck error) %else %if b_TCB_state = TCP syn received %c %or b_TCB_state = TCP established %c %or b_TCB_state = TCP close wait %if b_TCB_blocked send # 0 {%or b_TCB_snd wnd = 0} %start b_data bytes = 1; ! Frig -> must block %finish b_data bytes = 0 b_IP peer = b_TCB_remote address b_TCP header == record(addr(b_data start) - TCP header size) b_TCP header_source = b_TCB_local port b_TCP header_destination = b_TCB_remote port b_TCP header_seq <- b_TCB_snd nxt b_TCP header_ack <- b_TCB_rcv nxt b_TCP header_window = b_TCB_rcv wnd b_TCB_ACK pending = 0 b_TCP header_flags = ACK bit ! FIN bit b_TCB_FIN seq <- b_TCP header_seq b_TCB_snd nxt <- b_TCB_snd nxt + 1 %if b_TCB_state = TCP close wait %start ! P.61 says "closing", P.23 says "last ack" new state(b_TCB, TCP closing) %else new state(b_TCB, TCP fin wait 1) %finish transmit segment(b, 0, 1) %else ! fin wait 1, fin wait 2, closing, last ack, time wait release buffer(b); !**Meantime** %finish -> out op(TCP abort request): !! printstring("Abort request from "); write(b_TCB_slot, 0); newline TCP abort TCB(b_TCB) release buffer(b) -> out op(TCP claim request): op(TCP claim priv request): !! printstring("Claim port request from "); write(b_TCB_slot, 0); newline try claim port again: %if b_code = TCP claim priv request %start %if b_privilege = 0 %start TCP claim response(b_TCB_slot, privilege error, 0) release buffer(b) -> out %finish TCP last privileged = TCP last privileged - 1 TCP last privileged = 1023 %if TCP last privileged < 768 which = TCP last privileged !! printstring("Looking for privileged, trying ") !! write(which, 0); newline %else TCP last unprivileged = TCP last unprivileged + 1 TCP last unprivileged = 1025 %if TCP last unprivileged > 2048 which = TCP last unprivileged !! printstring("Looking for unprivileged, trying ") !! write(which, 0); newline %finish %for i = 1, 1, max TCP %cycle -> try claim port again %if TCB table_TCB(i)_local port = which %repeat ! If we fall through here then the port was free... !! printstring("Allocated "); write(which, 0); newline TCP claim response(b_TCB_slot, 0, which) release buffer(b) out: !L! lights and B(\TCP light) %end %externalroutine TCP fast timer %record(buffer fm)%name b %record(TCB fm)%name TCB %integer i, now !L! lights or B(TCP light) now = msecs timestamp !! printstring("Retransmit timeouts: "); phex(now); newline %for i = 1, 1, max TCP %cycle TCB == TCB table_TCB(i) %if TCB_local port # 0 %start ! In use b == dequeue buffer(TCB_retransmit queue) %if b ## nil %start ! Something pending %if now - b_stamp - b_interval >= 0 %start ! This one has expired b_life = b_life + 1 %if b_life <= TCP retransmit limit %start ! Retransmit required here !! printstring("Retransmitting "); print TCB address(TCB) !! printstring(" at "); phex(now); newline b_interval = b_interval << 1; ! Double it %if b_interval < TCP reTX lbound %start b_interval = TCP reTX lbound %else %if b_interval > TCP reTX ubound b_interval = TCP reTX ubound %finish stats_TCP retransmits = stats_TCP retransmits + 1 b_flags = requeue flag; !<<<<<<<<< !T! TCP trace(b, TCP trace reTX) IP outbound(b) %else ! Time this one out !! printstring("Timed out "); print TCB address(TCB) !! newline stats_TCP retransmit timeouts = stats_TCP retransmit timeouts + 1 release buffer(b) delete TCB(TCB, timeout error) %finish %else ! Put it back to wait for next time !! printstring("Requeue "); print TCB address(TCB); newline requeue buffer(b, TCB_retransmit queue) %finish !%else %if TCB_ACK pending > 0 ! ! Pending ACK in the last clock interval. Defer it for ! ! another tick. ! TCB_ACK pending = -1 %else %if TCB_ACK pending # 0 ! Deferred pending ACK. Send it this time. send ACK(TCB) %finish %finish %repeat !L! lights and B(\TCP light) %end %externalroutine TCP slow timer %record(TCB fm)%name TCB %integer i, started !L! lights or B(TCP light) started <- msecs timestamp - time wait timeout %for i = 1, 1, max TCP %cycle TCB == TCB table_TCB(i) delete TCB(TCB, 0) %if TCB_state >= TCP closing %c %and TCB_state change stamp - started <= 0 %repeat !L! lights and B(\TCP light) %end %end %of %file