Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

IPv4 fragments reassembly and correction for decode invalid values on fragments (issue #173) #174

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 23 additions & 5 deletions decode/ipv4.js
Original file line number Diff line number Diff line change
Expand Up @@ -90,13 +90,31 @@ IPv4.prototype.decode = function (raw_packet, offset) {

offset = orig_offset + this.headerLength;

var ProtocolDecoder = protocols[this.protocol];
if(ProtocolDecoder === undefined) {
this.protocolName = "Unknown";
if(this.fragmentOffset || this.flags.moreFragments) {

/* Discussion: This is a IP fragment. We don't have the entire
* next protocol data to decode. So the user need try to decode by himself.
*
* Due to performance issues, the payload is not a copy, and is valid only
* once time in loop and needs to be copied by the user if he intends
* to reassembly the original packet.
*
* In future, we would try to decode only the first fragment, that usually
* has the entire header of the upper protocol (but don't have the whole payload),
* but we need to check all decoders of transport layer to ensure that we will not have bugs
* when trying to decode a incomplete data.
* */
var payloadSize = this.length - this.headerLength;
//Don't forget to copy the payload if you intend to defrag!
this.payload = raw_packet.slice(offset, offset + payloadSize);
} else {
this.payload = new ProtocolDecoder(this.emitter).decode(raw_packet, offset, this.length - this.headerLength);
var ProtocolDecoder = protocols[this.protocol];
if(ProtocolDecoder === undefined) {
this.protocolName = "Unknown";
} else {
this.payload = new ProtocolDecoder(this.emitter).decode(raw_packet, offset, this.length - this.headerLength);
}
}

if(this.emitter) { this.emitter.emit("ipv4", this); }
return this;
};
Expand Down
38 changes: 38 additions & 0 deletions examples/ipDefrag.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
'use strict';
var fs = require('fs');
var pcap = require('../pcap');
var IPv4Defrag = require("./ipDefrag/IPv4Defrag");

var filename = process.argv[2];
var pcap_session = pcap.createOfflineSession(filename, '');

function findIPv4Packet(pcapPacket) {
if(pcapPacket.link_type === 'LINKTYPE_RAW') {
return pcapPacket.payload;
}

if(pcapPacket.link_type === 'LINKTYPE_ETHERNET') {
var packetEthernet = pcapPacket.payload;
if(packetEthernet.ethertype === 2048) {
return packetEthernet.payload;
}
}
//TODO LINKTYPE_NULL
return null;
}

var packetCount = 0;
var iPv4Defrag = new IPv4Defrag();
pcap_session.on('packet', function filterDecodePacket(rawPacket) {
var packet = pcap.decode.packet(rawPacket);
var iPPacket = findIPv4Packet(packet);
packetCount++;
var result = iPv4Defrag.receivePart(iPPacket);

if(result !== null) {
console.log("New fragmented IPv4 packet reassembled! Last part is in the packet number", packetCount);
console.log(result.toString(), result);
}

});

42 changes: 42 additions & 0 deletions examples/ipDefrag/IPv4Defrag.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
'use strict';
var protocols = require("../../decode/ip_protocols");
var IPv4Reassembler = require("./IPv4Reassembler");

/* Handle with differents fragments of differents original parts to reassembly
* Use this class to reassembly*/

function IPv4Defrag() {
this.fragmentsById = {};
}

function isFragment(iPPacket) {
return (iPPacket.fragmentOffset > 0 || iPPacket.flags.moreFragments);
}

//If complete, returns de reassempled payload!
IPv4Defrag.prototype.receivePart = function(iPPacket) {
if(!isFragment(iPPacket)) {
return null;
}

if(this.fragmentsById[iPPacket.identification] === undefined) {
this.fragmentsById[iPPacket.identification] = new IPv4Reassembler();
}

this.fragmentsById[iPPacket.identification].newPart(iPPacket);
var buffer = this.fragmentsById[iPPacket.identification].buildBuffer();
if(buffer === null) {
return null;
}

var ProtocolDecoder = protocols[iPPacket.protocol];
if(ProtocolDecoder === undefined) {
return null;
} else {
var payload = new ProtocolDecoder(this.emitter).decode(buffer, 0, buffer.length);
return payload;
}
}

module.exports = IPv4Defrag;
module.exports.isFragment = isFragment;
89 changes: 89 additions & 0 deletions examples/ipDefrag/IPv4Reassembler.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
'use strict';
var Segment = require("./Segment");

/* Reassembly ONE original payload. Should receive only packets with the same identification
* Use IPv4Defrag to manage differents original packets. */

function IPv4Reassembler() {
this.firstSegment = new Segment(0, + Infinity);
this._emptySegmentsCounter = 1;
/*_emptySegmentsCounter is a optimization to check
* if we already have all parts, ie, when _emptySegmentsCounter be zero.
* Otherwise we would have to check the whole list searching for empty segments
* every time that we want to check if is complete (ie, every time we add a part) */
}

//Returns a segment 'actualSegment' where (actualSegment.begin <= partBegin && actualSegment.end >= partEnd)
IPv4Reassembler.prototype._getSegmentThatContainsPart = function(partBegin, partEnd) {
var actualSegment = this.firstSegment;

while(actualSegment !== null) {
if(actualSegment.begin > partBegin) {
return null;
}

if (actualSegment.end >= partEnd) {
return actualSegment;
}

actualSegment = actualSegment.nextSegment;
}

return null;
}

IPv4Reassembler.prototype.newPart = function(packet) {
var partBegin = packet.fragmentOffset;
var partEnd = packet.fragmentOffset + (packet.length - packet.headerLength); //byte in partEnd is not included in segment!

var segment = this._getSegmentThatContainsPart(partBegin, partEnd);
if(segment === null || !segment.isEmpty()) {
throw(new Error("No space avaible"));
}

segment.payload = new Buffer(packet.payload);
this._emptySegmentsCounter--;
if(segment.begin < partBegin) {//Segment starts before offset of this part, must be space avaible before this part!
/*Makes new empty space before part, starting in the old begining of actual segment (should be equal the end of the old previous)
* and ending in actual partBegin*/
new Segment(segment.begin, partBegin, segment.previousSegment, segment); //Constructor updates previous/next references of the neighborhood
segment.begin = partBegin;
this._emptySegmentsCounter++;
}

if(!packet.flags.moreFragments) {//ie, last fragment
segment.end = partEnd;
} else if(segment.end > partEnd) { //Segment ends after the end of this part, must be space avaible after this part!
/*Makes new empty space after this part, starting in the end of this segment
* and ending in the old end (that should be equal the beggining of the old next segment)*/
new Segment(partEnd, segment.end, segment, segment.nextSegment); //Constructor updates previous/next references of the neighborhood
segment.end = partEnd;
this._emptySegmentsCounter++;
}
}

IPv4Reassembler.prototype.buildBuffer = function() {
if(this._emptySegmentsCounter !== 0) {
return null; //NOT COMPLETE YET, MISSING FRAGMENTS!
}
var dataArray = [];
var segment = this.firstSegment;
while(segment !== null) {
dataArray.push(segment.payload);
segment = segment.nextSegment;
}
return Buffer.concat(dataArray);
}

/* For debug only */
IPv4Reassembler.prototype.toString = function() {
var result = "";
var actualSegment = this.firstSegment;
while(actualSegment !== null) {
result += actualSegment.toString();
actualSegment = actualSegment.nextSegment;
}
return result;
}

module.exports = IPv4Reassembler;
40 changes: 40 additions & 0 deletions examples/ipDefrag/Segment.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
'use strict';

/* A node for IPv4Reassembler */

function Segment(begin, end, previousSegment, nextSegment) {
this.begin = begin;
this.end = end;

if(previousSegment === undefined || previousSegment === null) {
this.previousSegment = null;
} else {
this.previousSegment = previousSegment;
previousSegment.nextSegment = this;
//assert(previousSegment.end === this.begin);
}

if(nextSegment === undefined || nextSegment === null) {
this.nextSegment = null;
} else {
this.nextSegment = nextSegment;
nextSegment.previousSegment = this;
//assert(nextSegment.begin === this.end);
}

this.payload = null;
}

Segment.prototype.isEmpty = function() {
return this.payload === null;
}

/* For debug only */
Segment.prototype.toString = function() {
return "Begin: " + this.begin + "\n" +
"End: " + this.end + "\n" +
"Status: " +
(this.payload ? "BUSY" : "FREE") + "\n";
}

module.exports = Segment;