-- File: TcpUtilImpl.mesa - last edit:
-- JAV                 19-Nov-87 14:04:54
-- AOF                  2-Jun-87 12:18:06
-- sma                  9-Feb-87 14:51:11

-- Copyright (C) 1985, 1986, 1987 by Xerox Corporation. All rights reserved.

DIRECTORY
  ArpaBuffer USING [Body, Buffer],
  ArpaPort USING [
    GetAssignedAddress, GetBufferPool, SetIPLengths, SetWaitTime, WaitTime],
  ArpaPortInternal USING [AddrMismatch, BuildMasks, GetSubnetMask],
  ArpaRouter USING [InternetAddress, Port, unknownInternetAddress],
  ArpaTypes USING [InternetAddress, Port],
  CommUtil USING [PulsesToTicks],
  Environment USING [Block, bytesPerPage, bytesPerWord],
  File USING [nullFile],
  Process USING [DisableTimeout, EnableAborts, MsecToTicks, SetTimeout],
  Protocol1 USING [GetFamilyUnit],
  Runtime USING [GetCaller],
  Space USING [Map],
  System USING [GetClockPulses, MicrosecondsToPulses],
  TcpImpl USING [connection, rcvr, rexmtr, xmtr],
  TcpPort USING [CreateTcpPort],
  TcpStream USING [Precedence, Security, WaitTime],
  TcpStreamInternal USING [
    LowHalf, inputSpacePages, rcvrDefaults, rexmtrDefaults, maxTcpDataBytes,
    xmtrAlloc, send, receive, tos, ttl, fragAllowed, minTcpHeaderBytes];

TcpUtilImpl: MONITOR
  IMPORTS
    ArpaPort, ArpaPortInternal, ArpaRouter, CommUtil, Process, Protocol1, Runtime,
    Space, System, TcpPort, TcpStreamInternal
  EXPORTS ArpaRouter, TcpStreamInternal =
  BEGIN
  
  seed: WORD ← TcpStreamInternal.LowHalf[System.GetClockPulses[]];
  Port: PUBLIC --ArpaRouter-- TYPE = ArpaTypes.Port;
  InternetAddress: PUBLIC --ArpaRouter-- TYPE = ArpaTypes.InternetAddress;
  bpp: NATURAL = Environment.bytesPerPage;
  bpw: NATURAL = Environment.bytesPerWord;

  InitStream: PUBLIC PROC [local, remote: ArpaRouter.InternetAddress,
    localPort, remotePort: ArpaRouter.Port, timeout: TcpStream.WaitTime,
    precedence: TcpStream.Precedence, security: TcpStream.Security,
    options: Environment.Block] =
    BEGIN
    time: LONG CARDINAL = System.GetClockPulses[];
    mask: InternetAddress = ArpaPortInternal.BuildMasks[local].netMask;
    -- set based on whether these guys are on the same network 
    delayToRemote: CARDINAL =
      IF ArpaPortInternal.AddrMismatch[
        IF ArpaPortInternal.GetSubnetMask[] # ArpaRouter.unknownInternetAddress THEN ArpaPortInternal.GetSubnetMask[] ELSE mask, local, remote] 
	  THEN 1 ELSE 0;

    him: LONG POINTER TO FRAME[TcpImpl] = LOOPHOLE[Runtime.GetCaller[]];
		      
    him.rcvr.nextSeq ← 0;
    him.rcvr.maxSeq ← him.rcvr.irs ← 0;
    --set for real when connection request arrives.
    him.rcvr.ackInterval ←
      System.MicrosecondsToPulses[LONG[1000] * LONG[
        TcpStreamInternal.rcvrDefaults.ackInterval]];
    him.rcvr.remoteBlocked ← FALSE;
    him.rcvr.urgSig ← FALSE;         --ditto.
    him.rcvr.urg ← 0;
    Process.EnableAborts[@him.rcvr.urgArrived];
    Process.DisableTimeout[@him.rcvr.urgArrived];
    him.rcvr.pushSig ← FALSE;
    him.rcvr.push ← 0;              --ditto.
    him.rcvr.finSig ← FALSE;
    him.rcvr.fin ← 0;               --ditto.
    him.rcvr.lastConsumed ← 0;
    him.rcvr.inputSpace.size ←
      (bpp * TcpStreamInternal.inputSpacePages) -
      ((bpp * TcpStreamInternal.inputSpacePages) MOD
      TcpStreamInternal.maxTcpDataBytes);
    him.rcvr.inputSpace.space ←
      Space.Map[[File.nullFile, 0, TcpStreamInternal.inputSpacePages]];
    him.rcvr.inuse ← [NIL, 0];
    him.rcvr.avail ← [NIL, 0];
    him.rcvr.process ← NIL;
    Process.EnableAborts[@him.rcvr.newInput];
    Process.DisableTimeout[@him.rcvr.newInput];
    
    him.rexmtr.list ← [0, NIL];
    him.rexmtr.ceiling ← System.MicrosecondsToPulses[
      LONG[TcpStreamInternal.rexmtrDefaults.ceiling]*LONG[1000]];
    him.rexmtr.floor ← System.MicrosecondsToPulses[
      LONG[TcpStreamInternal.rexmtrDefaults.floor]*LONG[1000]];
    him.rexmtr.giveUp ← TcpStreamInternal.rexmtrDefaults.giveUp;
    him.rexmtr.count ← 0;  him.rexmtr.delay ← 0;
    him.rexmtr.interval ← System.MicrosecondsToPulses[
      IF delayToRemote = 0 THEN
        LONG[TcpStreamInternal.rexmtrDefaults.initialLocal] * 1000
      ELSE LONG[TcpStreamInternal.rexmtrDefaults.initialRemote] * 1000];    
    him.rexmtr.calculationInterval ← System.MicrosecondsToPulses[
      LONG[TcpStreamInternal.rexmtrDefaults.calculationInterval] * 1000];
    him.rexmtr.calculation ← time;
    him.rexmtr.process ← NIL;
    Process.EnableAborts[@him.rexmtr.condition];
    Process.SetTimeout[
      @him.rexmtr.condition, CommUtil.PulsesToTicks[[him.rexmtr.floor/2]]];
    
    --set for real when syn comes in.
    him.xmtr.iss ← him.xmtr.maxSeq ← him.xmtr.unackedSeq ← him.xmtr.nextSeq ← 0;
    him.xmtr.maxAlloc ← TcpStreamInternal.xmtrAlloc;
    him.xmtr.blocked ← [NIL, 0];
    him.xmtr.clientProcess ← NIL;
    him.xmtr.timeWaitStart ← 0;
    him.xmtr.context ← NIL;
    Process.DisableTimeout[@him.xmtr.newAllocation];
    Process.EnableAborts[@him.xmtr.newAllocation];
    
    him.connection.state ← closed;
    him.connection.stateBeforeSuspension ← closed;
    him.connection.whySuspended ← notSuspended;
    him.connection.offNet ← FALSE;
    him.connection.pleaseStop ← FALSE;
    --may be changed by max size option exchange.
    him.rcvr.maxTcpBytes ← him.xmtr.maxTcpBytes ←
      TcpStreamInternal.maxTcpDataBytes;
    Process.SetTimeout[@him.connection.isEstablished, Process.MsecToTicks[2000]];
    Process.EnableAborts[@him.connection.isEstablished];
    him.connection.port ← TcpPort.CreateTcpPort[port: localPort,
      send: TcpStreamInternal.send, receive: TcpStreamInternal.receive,
      remote: remote, remotePort: remotePort];
    him.connection.localAddr ← ArpaPort.GetAssignedAddress[him.connection.port];
    him.connection.remoteAddr ← remote;
    IF delayToRemote # 0 THEN him.connection.offNet ← TRUE ELSE him.connection.offNet ← FALSE;
    him.connection.localPort ← localPort;
    him.connection.remotePort ← remotePort;
    him.connection.timeout ← timeout;
    him.connection.precedence ← precedence;
    him.connection.security ← security;
    --options are ignored right now, since the only actual option is max size,
    --set by us (not the client), in the connection request only.
    him.connection.options ← options;
    ArpaPort.SetWaitTime[him.connection.port, LAST[ArpaPort.WaitTime]];
    him.connection.pool ← ArpaPort.GetBufferPool[him.connection.port];
    him.connection.family ← Protocol1.GetFamilyUnit[arpa];  --pointer to family
    END;  --InitStream
    

  GetDataLength: PUBLIC PROC [body: ArpaBuffer.Body]
    RETURNS [l: CARDINAL, p: LONG POINTER] =
    --returns the data length in bytes and a pointer to the data in the buffer.
    BEGIN
    p ← @body.ipData + (body.tcp.dataOffset * 2);
    l ← (body.ipHeader.ihl * 4) + (body.tcp.dataOffset * 4);  --headers length
    --we don't want l going negative if remote screwed up.
    IF l > body.ipHeader.length THEN l ← 0 ELSE l ← body.ipHeader.length - l;
    END;  --GetDataLength
    
  PrecedenceMatch: PUBLIC PROC [body: ArpaBuffer.Body] RETURNS [BOOLEAN] =
    BEGIN
    RETURN[TRUE];
    END;  --PrecedenceMatch
    
  SecurityMatch: PUBLIC PROC [body: ArpaBuffer.Body] RETURNS [BOOLEAN] =
    BEGIN
    RETURN[TRUE];
    END;  --SecurityMatch

  SetHeaderFields: PUBLIC PROC[
    b: ArpaBuffer.Buffer, dataLen, optionsLen: CARDINAL] =
    BEGIN
    <<
    PROCESS: RECEIVER, CLIENT SENDER
    Sets the header fields (IP and TCP) of the packet.
    >>
    headerBytes: CARDINAL;  --for setting length, .. tcp header may be padded
    ipOptionsSize: CARDINAL ← 0;
    body: ArpaBuffer.Body = b.arpa;
    him: LONG POINTER TO FRAME[TcpImpl] = LOOPHOLE[Runtime.GetCaller[]];
     
    b.fo.time ← System.GetClockPulses[];  --record packet send time
    b.fo.tries ← 1;  --number of times we transmitted this packet.
    
    body.ipHeader.protocol ← tcp;
    body.ipHeader.destination ← him.connection.remoteAddr;
    body.ipHeader.service ← TcpStreamInternal.tos;
    body.ipHeader.identification ← UniquePktID[]; --for frag/reassembly.
    body.ipHeader.lifetime ← TcpStreamInternal.ttl;
    body.ipHeader.flags.dontFragment ← ~TcpStreamInternal.fragAllowed;
    IF him.connection.security # NIL THEN
      BEGIN
      securityPtr: TcpStream.Security ← LOOPHOLE[@body.ipHeader.options];
      securityPtr↑ ← him.connection.security↑;
      ipOptionsSize ← SIZE[TcpStream.Security] * bpw;
      END;
    body.tcp.reserved ← 0;
    headerBytes ← optionsLen + TcpStreamInternal.minTcpHeaderBytes;
    body.tcp.dataOffset ← (headerBytes + 3) / 4;
    body.tcp.destinationPort ← him.connection.remotePort;
    --we set lengths here because we needed all the other header info first.
    ArpaPort.SetIPLengths[body, ipOptionsSize, headerBytes + dataLen];
    --checksum is set when the packet is sent.
    END;  --SetHeaderFields
    
    
  UniquePktID: PUBLIC ENTRY PROC RETURNS [WORD] =
    {RETURN[(seed ← SUCC[seed])]};
    
  END..
  
LOG
  
 3-Oct-85  9:48:32  SMA  Created file - stateless procs from TcpImpl.
30-Jan-86 11:57:03  SMA  GetDataLen returns length and pointer to data.
18-Jul-86 11:37:36  AOF  32-bit math procs use 'INT' rather than 'INTEGER'.
 2-Feb-87 11:36:35  SMA  Moved stuff from TcpImpl to shut up compiler.
 5-Feb-87 10:49:50  SMA  xmtr.blocked for allocation probing.
11-Mar-87 10:00:46  AOF  Funston buffer management.
11-Mar-87 10:04:28  AOF  made UniquePktID a random seed, montonic↑ function.
11-Mar-87 10:18:05  AOF  Made low-level functions INLINEs in interface.
 2-Jun-87 10:58:40  AOF  Caching of DataObject entries.