Copyright (C) 1983, 1984, 1985 by Xerox Corporation. All rights reserved. The following program was created in 1983 but has not been published within the meaning of the copyright law, is furnished under license, and may not be used, copied and/or disclosed except in accordance with the terms of said license.
TCPLoggingImpl.mesa
Last Edited by: Nichols, August 25, 1983 2:37 pm
Last Edited by: Taft, January 4, 1984 12:59 pm
Hal Murray May 16, 1985 3:15:02 am PDT
John Larson, April 14, 1986 11:24:32 pm PST
DIRECTORY
BasicTime USING [Now],
Convert USING [RopeFromTime],
IO USING [Flush, int, rope, PutChar, PutF, PutRope, STREAM],
IPDefs USING [Datagram, Address],
Rope USING [ROPE, Cat],
TCPLogging,
TCPOps USING [ConnectionState, Flip, TCPHeaderP, TCPHandle];
TCPLoggingImpl: CEDAR MONITOR
IMPORTS BasicTime, Convert, IO, Rope, TCPOps
EXPORTS TCPLogging =
BEGIN OPEN TCPOps, TCPLogging;
pktFile, logFile: PUBLIC IO.STREAMNIL;
StateToRope: PROC [state: ConnectionState] RETURNS [stateRope: Rope.ROPE] = {
SELECT state FROM
closed => stateRope ← "CLS";
listen => stateRope ← "LSN";
synSent => stateRope ← "SSN";
synRcvd => stateRope ← "SRD";
established => stateRope ← "EST";
finWait1 => stateRope ← "FW1";
finWait2 => stateRope ← "FW2";
closeWait => stateRope ← "CWT";
closing => stateRope ← "CLG";
lastAck => stateRope ← "LAK";
timeWait => stateRope ← "TWT"
ENDCASE; };
PutAddr: PROC [s: IO.STREAM, addr: IPDefs.Address] = {
s.PutF["[%03g.%03g.%03g.%03g]",
IO.int[addr[0]], IO.int[addr[1]], IO.int[addr[2]], IO.int[addr[3]]]; };
PutData: PROC [s: IO.STREAM, data: IPDefs.Datagram, fudge: BOOL] = {
cruft: INT ← 0;
length: INT ← data.dataLength;
c: INT;
tcpDataStart: INT;
tcpHdrPtr: TCPHeaderP;
TRUSTED {
tcpHdrPtr ← LOOPHOLE[@data.data];
tcpDataStart ← tcpHdrPtr.dataWordOffset*4;
IF fudge THEN { -- Recv case ??
IF tcpHdrPtr.dataWordOffset # 5 THEN { -- TCP Options
end: INT = tcpHdrPtr.dataWordOffset*4;
s.PutRope["Options: "];
FOR i: INT IN [4*5..end) DO
c ← data.data[tcpDataStart+i];
s.PutF["%b ", IO.int[c]];
ENDLOOP;
s.PutChar['\n]; };
length ← length - tcpHdrPtr.dataWordOffset*4; }; };
IF length = 0 THEN RETURN;
FOR i: INT IN [0..length) DO
c ← data.data[tcpDataStart+i];
IF c IN [40b..176b] THEN
s.PutChar['\000 + c]
ELSE {
cruft ← cruft + 1;
IF cruft > 10 THEN { s.PutRope["(more)"]; EXIT; };
s.PutF["<%b>", IO.int[c]]; };
ENDLOOP;
s.PutChar['\n]; };
PrintTCPPacket: PUBLIC ENTRY PROC [handle: TCPOps.TCPHandle, data: IPDefs.Datagram, dir: Direction] = TRUSTED {
Print or log TCP packets. The following information is printed for each packet:
state of Connection
Received, Sent or Retransmitted (R, S or X)
Control Flags (Urgent, Ack, Push, Reset, SYN, FIN)
Sequence number mod 100000
Ack number mod 100000
Window
length (number of bytes of TCP header and data)
source Address and Port -> Destination Address and Port
tcpHdrPtr: TCPHeaderP ← LOOPHOLE[@data.data];
dirString, stateString, ctlString, time: Rope.ROPE;
IF pktFile = NIL AND logFile = NIL THEN RETURN;
time ← Convert.RopeFromTime[
from: BasicTime.Now[], start: hours, end: seconds, useAMPM: FALSE, includeZone: FALSE];
SELECT dir FROM
fromNet => dirString ← "R";
toNet => dirString ← "S";
rexmitToNet => dirString ← "X"
ENDCASE;
IF handle = NIL THEN
stateString ← "CLS"
ELSE
stateString ← StateToRope[handle.state];
ctlString ← "";
IF tcpHdrPtr.urg THEN ctlString ← ctlString.Cat["U"];
IF tcpHdrPtr.ack THEN ctlString ← ctlString.Cat["A"];
IF tcpHdrPtr.psh THEN ctlString ← ctlString.Cat["P"];
IF tcpHdrPtr.rst THEN ctlString ← ctlString.Cat["R"];
IF tcpHdrPtr.syn THEN ctlString ← ctlString.Cat["S"];
IF tcpHdrPtr.fin THEN ctlString ← ctlString.Cat["F"];
IF pktFile # NIL THEN {
pktFile.PutChar['\n];
PutAddr[pktFile, data.inHdr.source];
pktFile.PutF[" %g -> ", IO.int[tcpHdrPtr.sourcePort]];
PutAddr[pktFile, data.inHdr.destination];
pktFile.PutF[" %g\n", IO.int[tcpHdrPtr.dstnPort]];
pktFile.PutF[" %g %g %6g ", IO.rope[stateString], IO.rope[dirString], IO.rope[ctlString]];
pktFile.PutF["%7g %7g %7g %7g\n", IO.int[Flip[tcpHdrPtr.seqNumber] MOD 100000], IO.int[Flip[tcpHdrPtr.ackNumber] MOD 100000], IO.int[tcpHdrPtr.window], IO.int[data.dataLength]];
PutData[pktFile, data, dir = fromNet]; };
IF logFile # NIL THEN {
logFile.PutChar['\n];
logFile.PutF["%g ", IO.rope[time]];
PutAddr[logFile, data.inHdr.source];
logFile.PutF[" %g -> ", IO.int[tcpHdrPtr.sourcePort]];
PutAddr[logFile, data.inHdr.destination];
logFile.PutF[" %g\n", IO.int[tcpHdrPtr.dstnPort]];
logFile.PutF[" %g %g %6g ", IO.rope[stateString], IO.rope[dirString], IO.rope[ctlString]];
logFile.PutF["%7g %7g %7g %7g\n", IO.int[Flip[tcpHdrPtr.seqNumber] MOD 100000], IO.int[Flip[tcpHdrPtr.ackNumber] MOD 100000], IO.int[tcpHdrPtr.window], IO.int[data.dataLength]];
PutData[logFile, data, dir = fromNet];
logFile.Flush[]; }; };
PrintStateChange: PUBLIC ENTRY PROC [handle: TCPOps.TCPHandle, newState: TCPOps.ConnectionState] = {
Print or log state transitions.
old, new, time: Rope.ROPE;
IF pktFile = NIL AND logFile = NIL THEN RETURN;
time ← Convert.RopeFromTime[
from: BasicTime.Now[], start: hours, end: seconds, useAMPM: FALSE, includeZone: FALSE];
old ← StateToRope[handle.state];
new ← StateToRope[newState];
IF pktFile # NIL THEN {
pktFile.PutChar['\n];
pktFile.PutF["[ME] %g <-> ", IO.int[handle.localPort]];
PutAddr[pktFile, handle.foreignAddr];
pktFile.PutF[" %g", IO.int[handle.foreignPort]];
pktFile.PutF[" %g -> %g\n", IO.rope[old], IO.rope[new]]; };
IF logFile # NIL THEN {
logFile.PutF["\n%g ", IO.rope[time]];
logFile.PutF["[ME] %g <-> ", IO.int[handle.localPort]];
PutAddr[logFile, handle.foreignAddr];
logFile.PutF[" %g", IO.int[handle.foreignPort]];
logFile.PutF[" %g -> %g\n", IO.rope[old], IO.rope[new]];
logFile.Flush[]; }; };
PrintMessage: PUBLIC ENTRY PROC [r: Rope.ROPE] = {
IF pktFile = NIL AND logFile = NIL THEN RETURN;
IF pktFile # NIL THEN pktFile.PutF["%g\n", IO.rope[r]];
IF logFile # NIL THEN { logFile.PutF["%g\n", IO.rope[r]]; logFile.Flush[]; }; };
END.