Copyright (C) 1983, 1984 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.
IPReassemblyImpl.mesa
Last Edited by: HGM, October 7, 1984 7:20:03 am PDT
Last Edited by: Nichols, August 24, 1983 6:57 pm
Last Edited by: Taft, January 5, 1984 5:16 pm
Hal Murray May 7, 1985 10:30:41 pm PDT
John Larson, July 23, 1987 12:56:01 pm PDT
DIRECTORY
IPDefs USING [Datagram, maxDataLength],
IPOps USING [HeaderChecksum, MoveBytes],
IPReassembly;
IPReassemblyImpl: CEDAR MONITOR
IMPORTS IPOps
EXPORTS IPReassembly =
BEGIN OPEN IPReassembly;
Clumps: TYPE = LIST OF IPDefs.Datagram;
DatagramTooLong: ERROR = CODE;
single, arrived, merged, finished, died, bad: PUBLIC INT ← 0;
reassemblyQueue: LIST OF Clumps ← NIL; -- Packets on this queue may have incorrect checksums.
IsFragment: PUBLIC PROC [data: IPDefs.Datagram] RETURNS [BOOL] = {
RETURN [data.inHdr.moreFragments OR data.inHdr.fragmentOffset # 0];
};
Reassemble: PUBLIC ENTRY PROC [data: IPDefs.Datagram] RETURNS [newData: IPDefs.Datagram] = {
We do fragment reassembly here. Returns NIL or a reassembled datagram.
Find a fragment list that matches this one. Create one if need be.
Put this fragment on the list, merging it with the preceding and following fragments if we should.
Check to see if this has left us with a reassembled datagram. If so, rechecksum it and return it.
ENABLE UNWIND => NULL;
Adjacent: PROC [d1, d2: IPDefs.Datagram] RETURNS [BOOL] = {
Return TRUE if d1 and d2 are adjacent fragments.
RETURN [(d1.inHdr.fragmentOffset + (INT[d1.inHdr.packetLength] - d1.inHdr.IHL*4) / 8) = d2.inHdr.fragmentOffset]; };
Merge: PROC [d1, d2: IPDefs.Datagram] RETURNS [newData: IPDefs.Datagram] = {
Merge the two fragments and return the result. Execptions?
dataLength1: INT ← d1.inHdr.packetLength - d1.inHdr.IHL*4;
dataLength2: INT ← d2.inHdr.packetLength - d2.inHdr.IHL*4;
IF dataLength1+dataLength2 > IPDefs.maxDataLength THEN
ERROR DatagramTooLong;
Fixup the header.
d1.inHdr.packetLength ← d1.inHdr.packetLength+dataLength2;
d1.inHdr.moreFragments ← d2.inHdr.moreFragments;
d1.inHdr.timeToLive ← MAX[d1.inHdr.timeToLive, d2.inHdr.timeToLive];
and the data.
TRUSTED {IPOps.MoveBytes[@d1.data, dataLength1, @d2.data, 0, dataLength2]};
d1.dataLength ← d1.inHdr.packetLength-d1.inHdr.IHL*4;
merged ← merged + 1;
RETURN [d1]; };
IF ~IsFragment[data] THEN {
single ← single + 1;
RETURN [data]; };
newData ← NIL; -- assume we won't get a reassembled packet
arrived ← arrived + 1;
FOR q: LIST OF Clumps ← reassemblyQueue, q.rest WHILE q # NIL DO
fq: LIST OF IPDefs.Datagram ← q.first;
d: IPDefs.Datagram ← fq.first;
IF d.inHdr.fragmentId = data.inHdr.fragmentId AND d.inHdr.protocol = data.inHdr.protocol AND d.inHdr.source = data.inHdr.source AND d.inHdr.destination = data.inHdr.destination THEN {
ENABLE
DatagramTooLong => {
Give up on this set of fragments.
reassemblyQueue ← RemoveClump[fq, reassemblyQueue];
bad ← bad + 1;
GOTO Quit};
Now scan down the fragment list trying to fit this one in.
FOR q2: LIST OF IPDefs.Datagram ← fq, q2.rest UNTIL q2 = NIL DO
d2: IPDefs.Datagram ← q2.first;
SELECT TRUE FROM
Adjacent[d2, data] => {
q2.first ← data ← Merge[d2, data];
IF q2.rest # NIL THEN {
d2 ← q2.rest.first;
IF Adjacent[data, d2] THEN {
q2.first ← data ← Merge[data, d2];
q2.rest ← q2.rest.rest; }; };
EXIT; };
data.inHdr.fragmentOffset < d2.inHdr.fragmentOffset => {
Splice it in before the current fragment.
q2.rest ← CONS[d2, q2.rest];
q2.first ← data;
EXIT; };
Adjacent[data, d2] => { q2.first ← data ← Merge[data, d2]; EXIT; };
q2.rest = NIL => { q2.rest ← CONS[data, NIL]; EXIT; };
ENDCASE => NULL;
ENDLOOP;
Now data points to the merged packet (or to the original which is a fragment).
IF NOT IsFragment[data] THEN {
finished ← finished + 1;
reassemblyQueue ← RemoveClump[fq, reassemblyQueue];
data.inHdr.checksum ← IPOps.HeaderChecksum[data];
newData ← data; };
RETURN; };
REPEAT
FINISHED =>
Create a new list for this fragment.
reassemblyQueue ← CONS [LIST[data], reassemblyQueue];
ENDLOOP;
EXITS
Quit => RETURN;
};
AgeFragments: PUBLIC ENTRY PROC [nSeconds: INT] =
BEGIN
Adjust the timeToLive fields in the fragments and toss any that are too old.
ENABLE UNWIND => NULL;
FOR q: LIST OF LIST OF IPDefs.Datagram ← reassemblyQueue, q.rest WHILE q # NIL DO
fq: LIST OF IPDefs.Datagram ← q.first;
FOR q2: LIST OF IPDefs.Datagram ← fq, q2.rest WHILE q2 # NIL DO
d: IPDefs.Datagram ← q2.first;
IF INT[d.inHdr.timeToLive]-nSeconds <= 0 THEN {
fq.first ← NIL;
died ← died + 1; }
ELSE d.inHdr.timeToLive ← d.inHdr.timeToLive-nSeconds;
ENDLOOP;
q.first ← RemoveNilDatagrams[fq];
ENDLOOP;
reassemblyQueue ← RemoveNilClumps[reassemblyQueue];
END;
RemoveNilDatagrams: PROC [l: LIST OF IPDefs.Datagram] RETURNS [newL: LIST OF IPDefs.Datagram] =
BEGIN
newL ← NIL;
WHILE l # NIL DO
IF l.first # NIL THEN newL ← AppendDatagram[newL, l.first];
l ← l.rest;
ENDLOOP;
END;
AppendDatagram: PROC [list: LIST OF IPDefs.Datagram, ref: IPDefs.Datagram] RETURNS[LIST OF IPDefs.Datagram] = INLINE
BEGIN
z: LIST OF IPDefs.Datagram ← list;
IF z = NIL THEN RETURN[CONS[ref, NIL]];
UNTIL z.rest = NIL DO z ← z.rest; ENDLOOP;
z.rest ← CONS[ref, NIL];
RETURN[list];
END;
RemoveDatagram: PUBLIC PROC [ref: IPDefs.Datagram, list: LIST OF IPDefs.Datagram] RETURNS[val: LIST OF IPDefs.Datagram] =
BEGIN
z: LIST OF IPDefs.Datagram ← NIL;
val ← NIL;
UNTIL list = NIL DO
IF list.first # ref THEN
{IF val = NIL THEN {val ← CONS[list.first, NIL]; z ← val}
ELSE {z.rest ← CONS[list.first, z.rest]; z ← z.rest}};
list ← list.rest;
ENDLOOP;  
END;  
RemoveNilClumps: PROC [l: LIST OF Clumps] RETURNS [newL: LIST OF Clumps] =
BEGIN
newL ← NIL;
WHILE l # NIL DO
IF l.first # NIL THEN newL ← AppendClump[newL, l.first];
l ← l.rest;
ENDLOOP;
END;
AppendClump: PROC [list: LIST OF Clumps, ref: Clumps] RETURNS[LIST OF Clumps] = INLINE
BEGIN
z: LIST OF Clumps ← list;
IF z = NIL THEN RETURN[CONS[ref, NIL]];
UNTIL z.rest = NIL DO z ← z.rest; ENDLOOP;
z.rest ← CONS[ref, NIL];
RETURN[list];
END;
RemoveClump: PUBLIC PROC [ref: Clumps, list: LIST OF Clumps] RETURNS[val: LIST OF Clumps] =
BEGIN
z: LIST OF Clumps ← NIL;
val ← NIL;
UNTIL list = NIL DO
IF list.first # ref THEN
{IF val = NIL THEN {val ← CONS[list.first, NIL]; z ← val}
ELSE {z.rest ← CONS[list.first, z.rest]; z ← z.rest}};
list ← list.rest;
ENDLOOP;  
END;  
END.