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;