Skip to content

Commit

Permalink
implement enhanced packet block
Browse files Browse the repository at this point in the history
  • Loading branch information
compscidr committed Aug 8, 2024
1 parent b0462d8 commit 0487c48
Show file tree
Hide file tree
Showing 3 changed files with 170 additions and 4 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package com.jasonernst.packetdumper.filedumper

import com.jasonernst.packetdumper.ethernet.EtherType
import com.jasonernst.packetdumper.ethernet.EthernetHeader.Companion.prependDummyHeader
import com.jasonernst.packetdumper.pcapng.PcapNgEnhancedPacketBlock
import com.jasonernst.packetdumper.pcapng.PcapNgInterfaceDescriptionBlock
import com.jasonernst.packetdumper.pcapng.PcapNgSectionHeaderBlockLive
import com.jasonernst.packetdumper.pcapng.PcapNgSimplePacketBlock
Expand Down Expand Up @@ -61,7 +62,10 @@ class PcapNgFilePacketDumper(
outputStreamWriter.write(packetBlock.toBytes())
outputStreamWriter.flush()
} else {
TODO()
// todo: timestamp
val packetBlock = PcapNgEnhancedPacketBlock(conversionBuffer.array())
outputStreamWriter.write(packetBlock.toBytes())
outputStreamWriter.flush()
}
}
}
Original file line number Diff line number Diff line change
@@ -1,3 +1,89 @@
package com.jasonernst.packetdumper.pcapng

class PcapNgEnhancedPacketBlock
import java.nio.ByteBuffer
import java.nio.ByteOrder
import kotlin.math.abs
import kotlin.math.ceil

/**
* Implements:
* https://www.ietf.org/archive/id/draft-tuexen-opsawg-pcapng-03.html#name-enhanced-packet-block
* @param packetData The packet data
* @param timestamp The timestamp of the packet. The units are set in the interface description block
* but regardless of the unit it starts from 1970-01-01 00:00:00 UTC
*/
class PcapNgEnhancedPacketBlock(
val packetData: ByteArray,
private val originalPacketLength: Int = packetData.size,
private val timestamp: Long = 0,
) : PcapNgBlock {
companion object {
// block type (4) + 2 * block len (4) + interface id (4) + timestamp high (4) + timestamp low (4) + captured length (4) + original length (4)
const val HEADER_BLOCK_LENGTH_WITHOUT_PACKET = 32u

fun fromStream(stream: ByteBuffer): PcapNgEnhancedPacketBlock {
stream.order(ByteOrder.LITTLE_ENDIAN)
val blockType = stream.int
if (blockType != PcapNgBlockType.ENHANCED_PACKET_BLOCK.value.toInt()) {
throw PcapNgException(
"Block type is not an enhanced packet block, expected ${PcapNgBlockType.ENHANCED_PACKET_BLOCK.value} got $blockType",
)
}
val blockLength = stream.int
val interfaceId = stream.int
val timestampHigh = stream.int.toLong()
val timestampLow = stream.int.toLong()
val capturedLength = stream.int
val originalLength = stream.int
val packetData = ByteArray(capturedLength)
stream.get(packetData)
val zeroPad = blockLength - HEADER_BLOCK_LENGTH_WITHOUT_PACKET.toInt() - capturedLength
stream.position(stream.position() + zeroPad)
val trailer = stream.int
if (trailer != blockLength) {
throw PcapNgException("Trailer is not the expected value")
}
val timestamp = (timestampHigh shl 32) or timestampLow
return PcapNgEnhancedPacketBlock(packetData, originalLength, timestamp)
}
}

/**
* The size of the block is the size of the packet data rounded up to the nearest 4 bytes, plus
* the header and trailer
*/
override fun size(): UInt {
val nearest4 = 4u * (ceil(abs(packetData.size / 4.0))).toUInt()
return HEADER_BLOCK_LENGTH_WITHOUT_PACKET + nearest4
}

private fun zeroPadSize(): Int {
val nearest4 = 4 * (ceil(abs(packetData.size / 4.0))).toInt()
return nearest4 - packetData.size
}

override fun toBytes(): ByteArray {
val buffer = ByteBuffer.allocate(size().toInt())
buffer.order(ByteOrder.LITTLE_ENDIAN)
buffer.putInt(PcapNgBlockType.ENHANCED_PACKET_BLOCK.value.toInt())
buffer.putInt(size().toInt())
buffer.putInt(0) // interface id

// timestamp
buffer.putInt((timestamp shr 32).toInt())
buffer.putInt(timestamp.toInt())

buffer.putInt(packetData.size)
buffer.putInt(originalPacketLength)

// packet data
buffer.put(packetData)
// zero pad
for (i in 0 until zeroPadSize()) {
buffer.put(0)
}
buffer.putInt(size().toInt())

return buffer.array()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package com.jasonernst.packetdumper.filedumper
import com.jasonernst.packetdumper.ethernet.EtherType
import com.jasonernst.packetdumper.ethernet.EthernetHeader
import com.jasonernst.packetdumper.pcapng.PcapNgBlock
import com.jasonernst.packetdumper.pcapng.PcapNgEnhancedPacketBlock
import com.jasonernst.packetdumper.pcapng.PcapNgInterfaceDescriptionBlock
import com.jasonernst.packetdumper.pcapng.PcapNgSectionHeaderBlockLive
import com.jasonernst.packetdumper.pcapng.PcapNgSimplePacketBlock
Expand All @@ -17,7 +18,7 @@ import java.nio.ByteBuffer

class TestPcapNgFilePacketDumper {
private val logger = LoggerFactory.getLogger(javaClass)
private val dumper = PcapNgFilePacketDumper("/tmp", "test")
private lateinit var dumper: PcapNgFilePacketDumper

@AfterEach
fun tearDown() {
Expand All @@ -27,7 +28,7 @@ class TestPcapNgFilePacketDumper {
// delete the file created for the test to cleanup
try {
// File(dumper.filename).delete()
logger.debug("Deleted file ${dumper.filename}")
// logger.debug("Deleted file ${dumper.filename}")
} catch (e: Exception) {
// ignore
}
Expand Down Expand Up @@ -60,6 +61,7 @@ class TestPcapNgFilePacketDumper {
* Test that the file is created and the correct blocks are written at the start.
*/
@Test fun testOpenClose() {
dumper = PcapNgFilePacketDumper("/tmp", "test")
dumper.open()
dumper.close()
val readBuffer = readFile()
Expand All @@ -68,6 +70,7 @@ class TestPcapNgFilePacketDumper {

@Test
fun testDumpSimplePacketBlock() {
dumper = PcapNgFilePacketDumper("/tmp", "test")
dumper.open()
val buffer = ByteBuffer.wrap(byteArrayOf(0x01, 0x02, 0x03, 0x04))
dumper.dumpBuffer(buffer, 0, buffer.limit(), false, null)
Expand All @@ -80,6 +83,7 @@ class TestPcapNgFilePacketDumper {

@Test
fun testDumpSinglePacketBlockWithDummyEth() {
dumper = PcapNgFilePacketDumper("/tmp", "test")
dumper.open()
val buffer = ByteBuffer.wrap(byteArrayOf(0x01, 0x02, 0x03, 0x04))
dumper.dumpBuffer(buffer, 0, buffer.limit(), false, EtherType.IPv4)
Expand All @@ -99,4 +103,76 @@ class TestPcapNgFilePacketDumper {

assertEquals(buffer, recvData)
}

@Test
fun testMultipleSimplePacketBlocks() {
dumper = PcapNgFilePacketDumper("/tmp", "test")
dumper.open()
val buffer = ByteBuffer.wrap(byteArrayOf(0x01, 0x02, 0x03, 0x04))
val buffer2 = ByteBuffer.wrap(byteArrayOf(0x05, 0x06, 0x07, 0x08))
dumper.dumpBuffer(buffer, 0, buffer.limit(), false, null)
dumper.dumpBuffer(buffer2, 0, buffer2.limit(), false, null)
dumper.close()
val readBuffer = readFile()
verifyHeaders(readBuffer)
val simplePacketBlock = PcapNgSimplePacketBlock.fromStream(readBuffer)
val simplePacketBlock2 = PcapNgSimplePacketBlock.fromStream(readBuffer)

assertEquals(buffer, ByteBuffer.wrap(simplePacketBlock.packetData))
assertEquals(buffer2, ByteBuffer.wrap(simplePacketBlock2.packetData))
}

@Test
fun testDumpEnhancedPacketBlock() {
dumper = PcapNgFilePacketDumper("/tmp", "test", false)
dumper.open()
val buffer = ByteBuffer.wrap(byteArrayOf(0x01, 0x02, 0x03, 0x04))
dumper.dumpBuffer(buffer, 0, buffer.limit(), false, null)
dumper.close()
val readBuffer = readFile()
verifyHeaders(readBuffer)
val enhancedPacketBlock = PcapNgEnhancedPacketBlock.fromStream(readBuffer)
assertEquals(buffer, ByteBuffer.wrap(enhancedPacketBlock.packetData))
}

@Test
fun testDumpSingleEnhancedPacketBlockWithDummyEth() {
dumper = PcapNgFilePacketDumper("/tmp", "test", false)
dumper.open()
val buffer = ByteBuffer.wrap(byteArrayOf(0x01, 0x02, 0x03, 0x04))
dumper.dumpBuffer(buffer, 0, buffer.limit(), false, EtherType.IPv4)
dumper.close()
val readBuffer = readFile()
verifyHeaders(readBuffer)
val enhancedPacketBlock = PcapNgEnhancedPacketBlock.fromStream(readBuffer)

// parse out the ethernet dummy header
val packetData = ByteBuffer.wrap(enhancedPacketBlock.packetData)
EthernetHeader.fromStream(packetData)

// remaining data in the stream should now match the original buffer
val recvData = ByteBuffer.allocate(packetData.remaining())
recvData.put(packetData)
recvData.rewind()

assertEquals(buffer, recvData)
}

@Test
fun testDumpMultipleEnhancedPacketBlocks() {
dumper = PcapNgFilePacketDumper("/tmp", "test", false)
dumper.open()
val buffer = ByteBuffer.wrap(byteArrayOf(0x01, 0x02, 0x03, 0x04))
val buffer2 = ByteBuffer.wrap(byteArrayOf(0x05, 0x06, 0x07, 0x08))
dumper.dumpBuffer(buffer, 0, buffer.limit(), false, null)
dumper.dumpBuffer(buffer2, 0, buffer2.limit(), false, null)
dumper.close()
val readBuffer = readFile()
verifyHeaders(readBuffer)
val enhancedPacketBlock = PcapNgEnhancedPacketBlock.fromStream(readBuffer)
val enhancedPacketBlock2 = PcapNgEnhancedPacketBlock.fromStream(readBuffer)

assertEquals(buffer, ByteBuffer.wrap(enhancedPacketBlock.packetData))
assertEquals(buffer2, ByteBuffer.wrap(enhancedPacketBlock2.packetData))
}
}

0 comments on commit 0487c48

Please sign in to comment.