Skip to content
This repository was archived by the owner on Jul 3, 2020. It is now read-only.

Commit 66838f5

Browse files
committed
IP4 fragmented packets reassembly
1 parent 67ba182 commit 66838f5

14 files changed

Lines changed: 951 additions & 8 deletions

js/core/net/interface.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ function Interface(macAddr) {
3535
this.bufferDataOffset = 0;
3636
this.arp = new ARPResolver(this);
3737
this.isNetworkEnabled = false;
38+
this.fragments = new Map();
3839
}
3940

4041
Interface.prototype.disableArp = function() {

js/core/net/ip4-fragments.js

Lines changed: 164 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,164 @@
1+
// Copyright 2015 runtime.js project authors
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
'use strict';
16+
17+
var ip4header = require('./ip4-header');
18+
var ip4receive = require('./ip4-receive');
19+
20+
function fragmentHash(srcIP, destIP, protocolId, packetId) {
21+
return srcIP.toInteger() + '-' + destIP.toInteger() + '-' + (packetId + (protocolId << 16));
22+
}
23+
24+
function dropFragmentQueue(intf, hash) {
25+
intf.fragments.delete(hash);
26+
}
27+
28+
exports.addFragment = function(intf, u8, headerOffset, fragmentOffset, isMoreFragments) {
29+
var headerLength = ip4header.getHeaderLength(u8, headerOffset);
30+
var protocolId = ip4header.getProtocolId(u8, headerOffset);
31+
var srcIP = ip4header.getSrcIP(u8, headerOffset);
32+
var destIP = ip4header.getDestIP(u8, headerOffset);
33+
var packetId = ip4header.getIdentification(u8, headerOffset);
34+
var nextOffset = headerOffset + headerLength;
35+
36+
var hash = fragmentHash(srcIP, destIP, packetId, protocolId);
37+
38+
var firstFragment = false;
39+
var fragmentQueue = intf.fragments.get(hash);
40+
if (!fragmentQueue) {
41+
firstFragment = true;
42+
fragmentQueue = {
43+
receivedLength: 0,
44+
totalLength: 0,
45+
fragments: []
46+
};
47+
}
48+
49+
var fragmentLength = u8.length - nextOffset;
50+
if (fragmentLength <= 0) {
51+
return;
52+
}
53+
54+
var fragmentEnd = fragmentOffset + fragmentLength;
55+
56+
if (fragmentEnd > 0xffff) {
57+
return;
58+
}
59+
60+
// Locate non overlapping portion of new fragment
61+
var newOffset = fragmentOffset;
62+
var newEnd = fragmentEnd;
63+
var newNextOffset = nextOffset;
64+
for (var i = 0, l = fragmentQueue.fragments.length; i < l; ++i) {
65+
var fragment = fragmentQueue.fragments[i];
66+
if (!fragment) {
67+
continue;
68+
}
69+
70+
var fragBegin = fragment[0];
71+
var fragEnd = fragBegin + fragment[1];
72+
73+
var overlapOffset = newOffset >= fragBegin && newOffset <= fragEnd;
74+
var overlapEnd = newEnd >= fragBegin && newEnd <= fragEnd;
75+
76+
// New fragment is fully contained within another fragment,
77+
// just ignore it
78+
if (overlapOffset && overlapEnd) {
79+
return;
80+
}
81+
82+
// First fragment byte is somewhere withing existing fragment
83+
if (overlapOffset && newOffset < fragEnd) {
84+
newNextOffset += fragEnd - newOffset;
85+
newOffset = fragEnd;
86+
}
87+
88+
// Last fragment byte is somewhere withing existing fragment
89+
if (overlapEnd && newEnd > fragBegin) {
90+
newEnd = fragBegin;
91+
}
92+
}
93+
94+
// Remove old fragments fully contained within the new one
95+
// By doing this we can avoid splitting big new fragments into
96+
// smaller chunks
97+
var removedIndex = -1;
98+
for (var i = 0, l = fragmentQueue.fragments.length; i < l; ++i) {
99+
var fragment = fragmentQueue.fragments[i];
100+
if (!fragment) {
101+
continue;
102+
}
103+
104+
var fragBegin = fragment[0];
105+
var fragEnd = fragBegin + fragment[1];
106+
107+
if (fragBegin >= newOffset && fragBegin <= newEnd &&
108+
fragEnd >= newOffset && fragEnd <= newEnd) {
109+
// remove this old fragment
110+
fragmentQueue.fragments[i] = null;
111+
fragmentQueue.receivedLength -= fragment[1];
112+
removedIndex = i;
113+
}
114+
}
115+
116+
var newLength = newEnd - newOffset;
117+
118+
// fragment offset - fragement length - buffer data offset - buffer
119+
var newFragment = [newOffset, newLength, newNextOffset, u8];
120+
121+
if (removedIndex >= 0) {
122+
fragmentQueue.fragments[removedIndex] = newFragment;
123+
} else {
124+
fragmentQueue.fragments.push(newFragment);
125+
}
126+
127+
fragmentQueue.receivedLength += newLength;
128+
129+
// Last fragment?
130+
if (!isMoreFragments) {
131+
132+
// Another last fragment?
133+
if (fragmentQueue.totalLength > fragmentEnd) {
134+
// Wrong len
135+
return;
136+
}
137+
138+
fragmentQueue.totalLength = fragmentEnd;
139+
}
140+
141+
if (firstFragment) {
142+
intf.fragments.set(hash, fragmentQueue);
143+
}
144+
145+
if (fragmentQueue.totalLength === fragmentQueue.receivedLength) {
146+
var u8asm = new Uint8Array(fragmentQueue.totalLength);
147+
for (var i = 0, l = fragmentQueue.fragments.length; i < l; ++i) {
148+
var fragment = fragmentQueue.fragments[i];
149+
if (!fragment) {
150+
continue;
151+
}
152+
153+
var itemOffset = fragment[0];
154+
var itemLength = fragment[1];
155+
var itemNextOffset = fragment[2];
156+
var itemBuffer = fragment[3];
157+
u8asm.set(itemBuffer.subarray(itemNextOffset, itemNextOffset + itemLength), itemOffset);
158+
}
159+
160+
dropFragmentQueue(intf, hash);
161+
ip4receive(intf, srcIP, destIP, protocolId, u8asm, 0);
162+
return;
163+
}
164+
};

js/core/net/ip4-header.js

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,26 @@ exports.getHeaderLength = function(u8, headerOffset) {
4646
return (u8[headerOffset] & 0xf) << 2;
4747
};
4848

49+
exports.getFragmentationData = function(u8, headerOffset) {
50+
return u8view.getUint16BE(u8, headerOffset + 6);
51+
};
52+
53+
exports.getIdentification = function(u8, headerOffset) {
54+
return u8view.getUint16BE(u8, headerOffset + 4);
55+
};
56+
57+
exports.fragmentationDataIsMoreFragments = function(value) {
58+
return !!((value >>> 13) & 0x1);
59+
};
60+
61+
exports.fragmentationDataIsDontFragment = function(value) {
62+
return !!((value >>> 14) & 0x1);
63+
};
64+
65+
exports.fragmentationDataOffset = function(value) {
66+
return (value & 0x1fff) * 8;
67+
};
68+
4969
exports.minHeaderLength = minHeaderLength;
5070

5171
exports.write = function(u8, headerOffset, protocolId, srcIP, destIP, packetLength) {

js/core/net/ip4-receive.js

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
// Copyright 2015 runtime.js project authors
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
'use strict';
16+
17+
var udp = require('./udp');
18+
var tcp = require('./tcp');
19+
var icmp = require('./icmp');
20+
21+
module.exports = function(intf, srcIP, destIP, protocolId, u8, nextOffset) {
22+
switch (protocolId) {
23+
case 0x01: return icmp.receive(intf, srcIP, destIP, u8, nextOffset);
24+
case 0x06: return tcp.receive(intf, srcIP, destIP, u8, nextOffset);
25+
case 0x11: return udp.receive(intf, srcIP, destIP, u8, nextOffset);
26+
}
27+
};

js/core/net/ip4.js

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -15,20 +15,27 @@
1515
'use strict';
1616
var IP4Address = require('./ip4-address');
1717
var ip4header = require('./ip4-header');
18-
var udp = require('./udp');
19-
var tcp = require('./tcp');
20-
var icmp = require('./icmp');
18+
var ip4fragments = require('./ip4-fragments');
19+
var ip4receive = require('./ip4-receive');
2120

22-
exports.receive = function(intf, u8, headerOffset) {
21+
function handleReceive(intf, u8, headerOffset) {
2322
var headerLength = ip4header.getHeaderLength(u8, headerOffset);
2423
var protocolId = ip4header.getProtocolId(u8, headerOffset);
2524
var srcIP = ip4header.getSrcIP(u8, headerOffset);
2625
var destIP = ip4header.getDestIP(u8, headerOffset);
2726
var nextOffset = headerOffset + headerLength;
27+
ip4receive(intf, srcIP, destIP, protocolId, u8, nextOffset);
28+
}
29+
30+
exports.receive = function(intf, u8, headerOffset) {
31+
var fragmentData = ip4header.getFragmentationData(u8, headerOffset);
32+
var isMoreFragments = ip4header.fragmentationDataIsMoreFragments(fragmentData);
33+
var fragmentOffset = ip4header.fragmentationDataOffset(fragmentData);
2834

29-
switch (protocolId) {
30-
case 0x01: return icmp.receive(intf, srcIP, destIP, u8, nextOffset);
31-
case 0x06: return tcp.receive(intf, srcIP, destIP, u8, nextOffset);
32-
case 0x11: return udp.receive(intf, srcIP, destIP, u8, nextOffset);
35+
if (!isMoreFragments && fragmentOffset === 0) {
36+
handleReceive(intf, u8, headerOffset);
37+
return;
3338
}
39+
40+
ip4fragments.addFragment(intf, u8, headerOffset, fragmentOffset, isMoreFragments);
3441
};

js/core/net/mac-address.js

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,4 +42,31 @@ MACAddress.prototype.equals = function(that) {
4242
MACAddress.BROADCAST = new MACAddress(0xff, 0xff, 0xff, 0xff, 0xff, 0xff);
4343
MACAddress.ZERO = new MACAddress(0, 0, 0, 0, 0, 0);
4444

45+
MACAddress.parse = function(str) {
46+
if (str instanceof MACAddress) {
47+
return str;
48+
}
49+
50+
if ('string' !== typeof str) {
51+
return null;
52+
}
53+
54+
var p = str.trim().split(':');
55+
if (6 !== p.length) {
56+
return null;
57+
}
58+
59+
var a = new Array(6);
60+
for (var i = 0; i < 6; ++i) {
61+
var v = parseInt(p[i], 16) | 0;
62+
if (v !== parseInt(p[i], 16) || v < 0 || v > 255) {
63+
return null;
64+
}
65+
66+
a[i] = v;
67+
}
68+
69+
return new MACAddress(a[0], a[1], a[2], a[3], a[4], a[5]);
70+
};
71+
4572
module.exports = MACAddress;

js/test/unit/index.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ stream.on('data', function(v) {
2828
stream.on('end', shutdown);
2929

3030
require('./script');
31+
require('./lib/test');
3132
require('./buffers');
3233
require('./platform');
3334
require('./timers');

0 commit comments

Comments
 (0)