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;
};
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;
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;
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;