From c84fbdcd2dd29d9303af978ec24481c6bd2a22bf Mon Sep 17 00:00:00 2001 From: Felipe Alexandre Ferreira Date: Wed, 5 Aug 2015 09:51:53 -0300 Subject: [PATCH 1/2] Change IPv4 to keep the IP Paylaod as a binary buffer, that allow user to reassembly the original packet, since we can't correctly decode payloads of fragments without reassembly --- decode/ipv4.js | 28 +++++++++++++++++++++++----- 1 file changed, 23 insertions(+), 5 deletions(-) diff --git a/decode/ipv4.js b/decode/ipv4.js index f50230b..6b1f173 100644 --- a/decode/ipv4.js +++ b/decode/ipv4.js @@ -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; }; From 92e245c7f24e4c8c49c90e186cfec126fc023c49 Mon Sep 17 00:00:00 2001 From: Felipe Alexandre Ferreira Date: Wed, 5 Aug 2015 15:14:08 -0300 Subject: [PATCH 2/2] A IPv4 fragments reassembler. Reassembly fragments and decodes the payload. --- examples/ipDefrag.js | 38 ++++++++++++ examples/ipDefrag/IPv4Defrag.js | 42 +++++++++++++ examples/ipDefrag/IPv4Reassembler.js | 89 ++++++++++++++++++++++++++++ examples/ipDefrag/Segment.js | 40 +++++++++++++ 4 files changed, 209 insertions(+) create mode 100644 examples/ipDefrag.js create mode 100644 examples/ipDefrag/IPv4Defrag.js create mode 100644 examples/ipDefrag/IPv4Reassembler.js create mode 100644 examples/ipDefrag/Segment.js diff --git a/examples/ipDefrag.js b/examples/ipDefrag.js new file mode 100644 index 0000000..9acde86 --- /dev/null +++ b/examples/ipDefrag.js @@ -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); + } + +}); + diff --git a/examples/ipDefrag/IPv4Defrag.js b/examples/ipDefrag/IPv4Defrag.js new file mode 100644 index 0000000..9fce877 --- /dev/null +++ b/examples/ipDefrag/IPv4Defrag.js @@ -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; diff --git a/examples/ipDefrag/IPv4Reassembler.js b/examples/ipDefrag/IPv4Reassembler.js new file mode 100644 index 0000000..03d3dd4 --- /dev/null +++ b/examples/ipDefrag/IPv4Reassembler.js @@ -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; diff --git a/examples/ipDefrag/Segment.js b/examples/ipDefrag/Segment.js new file mode 100644 index 0000000..13c45eb --- /dev/null +++ b/examples/ipDefrag/Segment.js @@ -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;