From 896371b9871e7dc9111170d5889b02fde0dc80d2 Mon Sep 17 00:00:00 2001 From: Srivats P Date: Tue, 11 Apr 2023 18:09:14 +0530 Subject: [PATCH] Calculate L4 checksum offset for TTag packets --- common/ostproto.pro | 1 + common/packet.cpp | 117 ++++++++++++++++++++++++++++++++++++++++ common/packet.h | 112 ++++++++++++++++++++++++++++++++++++++ server/packetsequence.h | 5 +- 4 files changed, 233 insertions(+), 2 deletions(-) create mode 100644 common/packet.cpp create mode 100644 common/packet.h diff --git a/common/ostproto.pro b/common/ostproto.pro index 31f5ed9..769a1c4 100644 --- a/common/ostproto.pro +++ b/common/ostproto.pro @@ -113,6 +113,7 @@ SOURCES += \ udp.cpp \ textproto.cpp \ hexdump.cpp \ + packet.cpp \ payload.cpp \ sample.cpp \ sign.cpp \ diff --git a/common/packet.cpp b/common/packet.cpp new file mode 100644 index 0000000..a6aa136 --- /dev/null +++ b/common/packet.cpp @@ -0,0 +1,117 @@ +/* +Copyright (C) 2023 Srivats P. + +This file is part of "Ostinato" + +This is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see +*/ + +#include "packet.h" + +using namespace Packet; + +quint16 Packet::l4ChecksumOffset(const uchar *pktData, int pktLen) +{ + Parser parser(pktData, pktLen); + quint16 offset = kEthTypeOffset; + + // Skip VLANs, if any + quint16 ethType = parser.field16(offset); + if (!parser.ok()) return 0; + + // TODO: support 802.3 frames + if (ethType <= 1500) + return 0; + + while (kVlanEthTypes.contains(ethType)) { + offset += kVlanTagSize; + ethType = parser.field16(offset); + if (!parser.ok()) return 0; + } + offset += kEthTypeSize; + + // XXX: offset now points to Eth payload + + // Skip MPLS tags, if any + if (ethType == kMplsEthType) { + while (1) { + quint32 mplsTag = parser.field32(offset); + if (!parser.ok()) return 0; + offset += kMplsTagSize; + if (mplsTag & 0x100) { // BOS bit + quint32 nextWord = parser.field32(offset); + if (!parser.ok()) return 0; + if (nextWord == 0) { // PW Control Word + offset += kMplsTagSize; + ethType = 0; + break; + } + quint8 firstPayloadNibble = nextWord >> 28; + if (firstPayloadNibble == 0x4) + ethType = kIp4EthType; + else if (firstPayloadNibble == 0x6) + ethType = kIp6EthType; + else + ethType = 0; + break; + } + } + } + + quint8 ipProto = 0; + if (ethType == kIp4EthType) { + ipProto = parser.field8(offset + kIp4ProtocolOffset); + if (!parser.ok()) return 0; + + quint8 ipHdrLen = parser.field8(offset) & 0x0F; + if (!parser.ok()) return 0; + offset += 4*ipHdrLen; + } else if (ethType == kIp6EthType) { + ipProto = parser.field8(offset + kIp6NextHeaderOffset); + if (!parser.ok()) return 0; + offset += kIp6HeaderSize; + + // XXX: offset now points to IPv6 payload + + // Skip IPv6 extension headers, if any + while (kIp6ExtensionHeaders.contains(ipProto)) { + ipProto = parser.field8(offset + kIp6ExtNextHeaderOffset); + if (!parser.ok()) return 0; + + quint16 extHdrLen = parser.field8(offset + kIp6ExtLengthOffset); + offset += 8 + 8*extHdrLen; + } + } else { + // Non-IP + // TODO: support MPLS PW with Eth payload + return 0; + } + + // XXX: offset now points to IP payload + + if (ipProto == kIpProtoTcp) { + parser.field16(offset + kTcpChecksumOffset); + if (!parser.ok()) return 0; + + return offset + kTcpChecksumOffset; + } else if (ipProto == kIpProtoUdp) { + parser.field16(offset + kUdpChecksumOffset); + if (!parser.ok()) return 0; + + return offset + kUdpChecksumOffset; + } + + // No L4 + return 0; +} diff --git a/common/packet.h b/common/packet.h new file mode 100644 index 0000000..5d8609e --- /dev/null +++ b/common/packet.h @@ -0,0 +1,112 @@ +/* +Copyright (C) 2023 Srivats P. + +This file is part of "Ostinato" + +This is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see +*/ + +#ifndef _PACKET_H +#define _PACKET_H + +#include +#include + +namespace Packet { + +class Parser { +public: + Parser(const uchar *data, int length) + : pktData_(data), pktLen_(length) {} + quint8 field8(int offset) { + if (offset >= pktLen_) { + ok_ = false; + return 0; + } + ok_ = true; + return pktData_[offset]; + } + quint16 field16(int offset) { + if (offset + 1 >= pktLen_) { + ok_ = false; + return 0; + } + ok_ = true; + return pktData_[offset] << 8 + | pktData_[offset+1]; + } + quint32 field32(int offset) { + if (offset + 3 >= pktLen_) { + ok_ = false; + return 0; + } + ok_ = true; + return pktData_[offset] << 24 + | pktData_[offset+1] << 16 + | pktData_[offset+2] << 8 + | pktData_[offset+3]; + } + bool ok() { + return ok_; + } +private: + const uchar *pktData_; + int pktLen_; + bool ok_{false}; +}; + +quint16 l4ChecksumOffset(const uchar *pktData, int pktLen); + +// +// Constants +// +// Ethernet +const quint16 kEthTypeOffset = 12; +const quint16 kEthTypeSize = 2; +const quint16 kIp4EthType = 0x0800; +const quint16 kIp6EthType = 0x86dd; +const quint16 kMplsEthType = 0x8847; +const QSet kVlanEthTypes = {0x8100, 0x9100, 0x88a8}; + +// VLAN +const quint16 kVlanTagSize = 4; + +// MPLS +const quint16 kMplsTagSize = 4; + +// IPv4 +const quint16 kIp4ProtocolOffset = 9; + +// IPv6 +const quint16 kIp6HeaderSize = 40; +const quint16 kIp6NextHeaderOffset = 6; + +// IPv6 Extension Header +const quint16 kIp6ExtNextHeaderOffset = 0; +const quint16 kIp6ExtLengthOffset = 1; + +// IPv4/IPv6 Proto/NextHeader values +const quint8 kIpProtoTcp = 6; +const quint8 kIpProtoUdp = 17; + +const QSet kIp6ExtensionHeaders = {0, 60, 43, 44, 51, 50, 60, 135}; // FIXME: use names + +// TCP +const quint16 kTcpChecksumOffset = 16; + +// UDP +const quint16 kUdpChecksumOffset = 6; +}; + +#endif diff --git a/server/packetsequence.h b/server/packetsequence.h index 8962bc9..d2da717 100644 --- a/server/packetsequence.h +++ b/server/packetsequence.h @@ -20,8 +20,9 @@ along with this program. If not, see #ifndef _PACKET_SEQUENCE_H #define _PACKET_SEQUENCE_H -#include "pcapextra.h" +#include "../common/packet.h" #include "../common/sign.h" +#include "pcapextra.h" #include "streamstats.h" class PacketSequence @@ -72,7 +73,7 @@ public: } } if (trackGuidStats_ && (packets_ == 1)) // first packet of seq - ttagL4CksumOffset_ = 40; // FIXME + ttagL4CksumOffset_ = Packet::l4ChecksumOffset(pktData, pktHeader->caplen); return ret; } pcap_send_queue *sendQueue_;