mirror of
https://github.com/openappsec/openappsec.git
synced 2025-06-28 16:41:02 +03:00
611 lines
21 KiB
C++
Executable File
611 lines
21 KiB
C++
Executable File
#include "packet.h"
|
|
#include "debug.h"
|
|
#include "byteorder.h"
|
|
#include "c_common/network_defs.h"
|
|
#include "config.h"
|
|
|
|
#include <cstddef>
|
|
#include <limits>
|
|
#include <vector>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <iostream>
|
|
|
|
using namespace std;
|
|
|
|
USE_DEBUG_FLAG(D_PACKET);
|
|
|
|
ostream &
|
|
operator<<(ostream &os, const PktErr &err)
|
|
{
|
|
switch (err) {
|
|
case PktErr::UNINITIALIZED: {
|
|
return os << "Uninitialized packet";
|
|
}
|
|
case PktErr::NON_ETHERNET_FRAME: {
|
|
return os << "Layer 2 frame length does not match the Ethernet frame length";
|
|
}
|
|
case PktErr::MAC_LEN_TOO_BIG: {
|
|
return os << "Layer 2 frame length is greater than the packet length";
|
|
}
|
|
case PktErr::NON_IP_PACKET: {
|
|
return os << "Ethernet frame contains a non-IP packet";
|
|
}
|
|
case PktErr::UNKNOWN_L3_PROTOCOL: {
|
|
return os << "Unknown Layer 3 protocol type";
|
|
}
|
|
case PktErr::IP_SIZE_MISMATCH: {
|
|
return os << "Wrong IP header size";
|
|
}
|
|
case PktErr::IP_VERSION_MISMATCH: {
|
|
return os << "IP header version differs from the IP version defined by the Ethernet frame";
|
|
}
|
|
case PktErr::IP_HEADER_TOO_SMALL: {
|
|
return os << "Reported IP header length is shorter than the allowed minimum";
|
|
}
|
|
case PktErr::PKT_TOO_SHORT_FOR_IP_HEADER: {
|
|
return os << "Packet is too short for the IP header";
|
|
}
|
|
case PktErr::PKT_TOO_SHORT_FOR_IP_EXTENSION_HEADER: {
|
|
return os << "Packet is too short for the IP extension header";
|
|
}
|
|
case PktErr::PKT_TOO_SHORT_FOR_IP_EXTENSION_HEADER_BODY: {
|
|
return os << "Packet is too short for the IP extension body";
|
|
}
|
|
case PktErr::UNKNOWN_IPV6_EXTENSION_HEADER: {
|
|
return os << "Unknown IPv6 extension";
|
|
}
|
|
case PktErr::PKT_TOO_SHORT_FOR_L4_HEADER: {
|
|
return os << "IP content is too short to hold a Layer 4 header";
|
|
}
|
|
case PktErr::PKT_TOO_SHORT_FOR_TCP_OPTIONS: {
|
|
return os << "IP content is too short to hold all the TCP Options";
|
|
}
|
|
case PktErr::TCP_HEADER_TOO_SMALL: {
|
|
return os << "Reported TCP header length is shorter than the allowed minimum";
|
|
}
|
|
case PktErr::PKT_TOO_SHORT_FOR_ICMP_ERROR_DATA: {
|
|
return os << "ICMP data is too short to hold all ICMP error information";
|
|
}
|
|
case PktErr::ICMP_VERSION_MISMATCH: {
|
|
return os << "ICMP version does not match the IP version";
|
|
}
|
|
};
|
|
|
|
return os << "Unknown error: " << static_cast<uint>(err);
|
|
}
|
|
|
|
// This is common for (almost) all extension headers.
|
|
// All headers that ipv6_is_proto_extension returns true for them must have this layout
|
|
struct IPv6ExtBasic
|
|
{
|
|
u_char next_type;
|
|
};
|
|
|
|
// This is common for some extension headers
|
|
struct IPv6ExtGeneric
|
|
{
|
|
u_char next_type;
|
|
u_char ext_hdr_len; // Not in bytes! Sometimes *4, sometimes *8...
|
|
};
|
|
|
|
static const uint basic_ext_len = 8;
|
|
static const uint format_multiplier_four = 4;
|
|
static const uint format_multiplier_eight = 8;
|
|
static const char ipv4_chr = 0x40;
|
|
static const char ipv6_chr = 0x60;
|
|
static const char ipversion_mask = 0x60;
|
|
|
|
static bool
|
|
isIPv6ProtoExtension(u_char proto)
|
|
{
|
|
// ESP and None are not considered as ext headers, as their first 4 bytes are not of type IPv6ExtBasic
|
|
switch (proto) {
|
|
case IPPROTO_HOPOPTS: // 0 IPv6 hop-by-hop options - RFC2460
|
|
case IPPROTO_ROUTING: // 43 IPv6 routing header - RFC2460
|
|
case IPPROTO_FRAGMENT: // 44 IPv6 fragmentation header - RFC2460
|
|
// case IPPROTO_ESP: // 50 IPv6 encapsulation security payload header - RFC4303
|
|
case IPPROTO_AH: // 51 IPv6 authentication header - RFC4302
|
|
// case IPPROTO_NONE: // 59 IPv6 no next header - RFC2460
|
|
case IPPROTO_DSTOPTS: // 60 IPv6 destination options - RFC2460
|
|
case IPPROTO_MH: { // 135 IPv6 mobility header - RFC3775
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
Maybe<ConnKey, PktErr>
|
|
Packet::parseFromL4(const IPAddr &src, const IPAddr &dst, IPProto proto)
|
|
{
|
|
// Here so we got the l3 headers on both IPv4 and IPv6.
|
|
if (is_fragment) return ConnKey(src, 0, dst, 0, proto);
|
|
// Add ports
|
|
PortNumber sport, dport;
|
|
switch (proto) {
|
|
case IPPROTO_TCP: {
|
|
auto maybe_tcp = l3_payload.getTypePtr<struct TcpHdr>(0);
|
|
if (!maybe_tcp.ok()) {
|
|
dbgTrace(D_PACKET)
|
|
<< "TCP packet is too short ("
|
|
<< l3_payload.size()
|
|
<< ") to contain a basic TCP header";
|
|
return genError(PktErr::PKT_TOO_SHORT_FOR_L4_HEADER);
|
|
}
|
|
auto tcp = maybe_tcp.unpack();
|
|
auto l4_hdr_len = tcp->doff * sizeof(int32_t);
|
|
|
|
if (l4_hdr_len < sizeof(struct TcpHdr)) {
|
|
dbgTrace(D_PACKET) <<
|
|
"TCP header length is smaller than the minimum: " << l4_hdr_len << " < " << sizeof(struct tcphdr);
|
|
return genError(PktErr::TCP_HEADER_TOO_SMALL);
|
|
}
|
|
if (l4_hdr_len > l3_payload.size()) {
|
|
dbgTrace(D_PACKET) <<
|
|
"TCP packet is too short (" << l3_payload.size() << ") for a TCP header (" << l4_hdr_len << ")";
|
|
return genError(PktErr::PKT_TOO_SHORT_FOR_TCP_OPTIONS);
|
|
}
|
|
|
|
l4_header = l3_payload.getSubBuffer(0, l4_hdr_len);
|
|
l4_payload = l3_payload.getSubBuffer(l4_hdr_len, l3_payload.size());
|
|
sport = ntohs(tcp->source);
|
|
dport = ntohs(tcp->dest);
|
|
break;
|
|
}
|
|
case IPPROTO_UDP: {
|
|
auto maybe_udp = l3_payload.getTypePtr<struct UdpHdr>(0);
|
|
if (!maybe_udp.ok()) {
|
|
dbgTrace(D_PACKET)
|
|
<< "UDP packet is too short ("
|
|
<< l3_payload.size()
|
|
<< ") to contain a basic UDP header";
|
|
return genError(PktErr::PKT_TOO_SHORT_FOR_L4_HEADER);
|
|
}
|
|
auto udp = maybe_udp.unpack();
|
|
auto l4_hdr_len = sizeof(struct UdpHdr);
|
|
|
|
l4_header = l3_payload.getSubBuffer(0, l4_hdr_len);
|
|
l4_payload = l3_payload.getSubBuffer(l4_hdr_len, l3_payload.size());
|
|
sport = ntohs(udp->source);
|
|
dport = ntohs(udp->dest);
|
|
break;
|
|
}
|
|
case IPPROTO_ICMP:
|
|
case IPPROTO_ICMPV6: {
|
|
auto icmp_hdr_len = getIcmpHdrLen(proto, src.getType());
|
|
if (!icmp_hdr_len.ok()) return icmp_hdr_len.passErr();
|
|
auto l4_hdr_len = icmp_hdr_len.unpack();
|
|
if (l4_hdr_len > l3_payload.size()) {
|
|
dbgTrace(D_PACKET)
|
|
<< "ICMPv6 packet is too short ("
|
|
<< l3_payload.size()
|
|
<< ") to contain an ICMP header ("
|
|
<< l4_hdr_len
|
|
<< ")";
|
|
return genError(PktErr::PKT_TOO_SHORT_FOR_L4_HEADER);
|
|
}
|
|
l4_header = l3_payload.getSubBuffer(0, l4_hdr_len);
|
|
l4_payload = l3_payload.getSubBuffer(l4_hdr_len, l3_payload.size());
|
|
tie(sport, dport) = getICMPPorts(proto);
|
|
break;
|
|
}
|
|
case IPPROTO_GRE: {
|
|
auto maybe_gre = l3_payload.getTypePtr<struct GreHdr>(0);
|
|
if (!maybe_gre.ok()) {
|
|
dbgTrace(D_PACKET)
|
|
<< "GRE packet is too short ("
|
|
<< l3_payload.size()
|
|
<< ") to contain a basic GRE header";
|
|
return genError(PktErr::PKT_TOO_SHORT_FOR_L4_HEADER);
|
|
}
|
|
auto l4_hdr_len = sizeof(struct GreHdr);
|
|
|
|
l4_header = l3_payload.getSubBuffer(0, l4_hdr_len);
|
|
l4_payload = l3_payload.getSubBuffer(l4_hdr_len, l3_payload.size());
|
|
sport = 0;
|
|
dport = 0;
|
|
break;
|
|
}
|
|
case IPPROTO_SCTP: {
|
|
auto maybe_sctp = l3_payload.getTypePtr<struct SctpHdr>(0);
|
|
if (!maybe_sctp.ok()) {
|
|
dbgTrace(D_PACKET)
|
|
<< "SCTP packet is too short ("
|
|
<< l3_payload.size()
|
|
<< ") to contain a basic SCTP header";
|
|
return genError(PktErr::PKT_TOO_SHORT_FOR_L4_HEADER);
|
|
}
|
|
auto sctp = maybe_sctp.unpack();
|
|
auto l4_hdr_len = sizeof(struct SctpHdr);
|
|
|
|
l4_header = l3_payload.getSubBuffer(0, l4_hdr_len);
|
|
l4_payload = l3_payload.getSubBuffer(l4_hdr_len, l3_payload.size());
|
|
sport = ntohs(sctp->sport);
|
|
dport = ntohs(sctp->dport);
|
|
break;
|
|
}
|
|
case IPPROTO_DCCP: {
|
|
auto maybe_dccp = l3_payload.getTypePtr<struct DccpHdr>(0);
|
|
if (!maybe_dccp.ok()) {
|
|
dbgTrace(D_PACKET)
|
|
<< "DCCP packet is too short ("
|
|
<< l3_payload.size()
|
|
<< ") to contain a basic DCCP header";
|
|
return genError(PktErr::PKT_TOO_SHORT_FOR_L4_HEADER);
|
|
}
|
|
auto dccp = maybe_dccp.unpack();
|
|
auto l4_hdr_len = sizeof(struct DccpHdr);
|
|
|
|
l4_header = l3_payload.getSubBuffer(0, l4_hdr_len);
|
|
l4_payload = l3_payload.getSubBuffer(l4_hdr_len, l3_payload.size());
|
|
sport = ntohs(dccp->dccph_sport);
|
|
dport = ntohs(dccp->dccph_dport);
|
|
break;
|
|
}
|
|
// other protocols
|
|
default: {
|
|
l4_payload = l3_payload;
|
|
sport = 0;
|
|
dport = 0;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return ConnKey(src, sport, dst, dport, proto);
|
|
}
|
|
|
|
tuple<PortNumber, PortNumber>
|
|
Packet::getICMPPortsV6()
|
|
{
|
|
auto icmp_hdr = l4_header.getTypePtr<struct icmp6_hdr>(0).unpack();
|
|
PortNumber sport = 0;
|
|
PortNumber dport = 0;
|
|
switch(icmp_hdr->icmp6_type) {
|
|
case ICMP6_ECHO_REQUEST:
|
|
sport = ntohs(icmp_hdr->icmp6_id);
|
|
if (!getConfigurationWithDefault<bool>(false, "Allow simultaneous ping")) {
|
|
dport = ntohs(icmp_hdr->icmp6_seq);
|
|
}
|
|
break;
|
|
case ICMP6_ECHO_REPLY:
|
|
if (!getConfigurationWithDefault<bool>(false, "Allow simultaneous ping")) {
|
|
sport = ntohs(icmp_hdr->icmp6_seq);
|
|
}
|
|
dport = ntohs(icmp_hdr->icmp6_id);
|
|
break;
|
|
case ICMP6_DST_UNREACH:
|
|
case ICMP6_PACKET_TOO_BIG:
|
|
case ICMP6_TIME_EXCEEDED:
|
|
case ICMP6_PARAM_PROB:
|
|
case ND_REDIRECT:
|
|
sport = icmp_hdr->icmp6_code;
|
|
dport = icmp_hdr->icmp6_type;
|
|
break;
|
|
}
|
|
return make_tuple(sport, dport);
|
|
}
|
|
|
|
tuple<PortNumber, PortNumber>
|
|
Packet::getICMPPortsV4()
|
|
{
|
|
auto icmp_hdr = l4_header.getTypePtr<struct icmphdr>(0).unpack();
|
|
PortNumber sport = 0;
|
|
PortNumber dport = 0;
|
|
switch(icmp_hdr->type) {
|
|
case ICMP_ECHO:
|
|
case ICMP_TSTAMP:
|
|
case ICMP_IREQ:
|
|
case ICMP_MASKREQ:
|
|
sport = ntohs(icmp_hdr->un.echo.id);
|
|
if (!getConfigurationWithDefault<bool>(false, "Allow simultaneous ping")) {
|
|
dport = ntohs(icmp_hdr->un.echo.sequence);
|
|
}
|
|
break;
|
|
case ICMP_ECHOREPLY:
|
|
case ICMP_TSTAMPREPLY:
|
|
case ICMP_IREQREPLY:
|
|
case ICMP_MASKREPLY:
|
|
if (!getConfigurationWithDefault<bool>(false, "Allow simultaneous ping")) {
|
|
sport = ntohs(icmp_hdr->un.echo.sequence);
|
|
}
|
|
dport = ntohs(icmp_hdr->un.echo.id);
|
|
break;
|
|
case ICMP_UNREACH:
|
|
case ICMP_SOURCEQUENCH:
|
|
case ICMP_TIMXCEED:
|
|
case ICMP_PARAMPROB:
|
|
case ICMP_REDIRECT:
|
|
sport = icmp_hdr->code;
|
|
dport = icmp_hdr->type;
|
|
break;
|
|
}
|
|
return make_tuple(sport, dport);
|
|
}
|
|
|
|
tuple<PortNumber, PortNumber>
|
|
Packet::getICMPPorts(IPProto proto)
|
|
{
|
|
return proto == IPPROTO_ICMP ? getICMPPortsV4() : getICMPPortsV6();
|
|
}
|
|
|
|
Maybe<uint, PktErr>
|
|
Packet::getIcmpHdrLen(IPProto proto, IPType ip_type)
|
|
{
|
|
if (proto == IPPROTO_ICMP && ip_type == IPType::V4) {
|
|
return sizeof(struct icmphdr);
|
|
} else if (proto == IPPROTO_ICMPV6 && ip_type == IPType::V6) {
|
|
return sizeof(struct icmp6_hdr);
|
|
}
|
|
return genError(PktErr::ICMP_VERSION_MISMATCH);
|
|
}
|
|
|
|
Maybe<int, PktErr>
|
|
Packet::getIPv6GenericExtLen(uint offset_to_ext_hdr, uint length_multiplier)
|
|
{
|
|
auto maybe_header = l3.getTypePtr<IPv6ExtGeneric>(offset_to_ext_hdr);
|
|
if (!maybe_header.ok()) {
|
|
dbgTrace(D_PACKET) <<
|
|
"Not enough room for an IPv6 Extension header basic data (" << offset_to_ext_hdr << " + " <<
|
|
sizeof(IPv6ExtGeneric) << " > " << l3.size() << ")";
|
|
return genError(PktErr::PKT_TOO_SHORT_FOR_IP_EXTENSION_HEADER);
|
|
}
|
|
auto header = maybe_header.unpack();
|
|
|
|
return basic_ext_len + (header->ext_hdr_len * length_multiplier);
|
|
}
|
|
|
|
Maybe<int, PktErr>
|
|
Packet::getIPv6ExtLen(uint offset_to_ext_hdr, IPProto ext_hdr_type)
|
|
{
|
|
switch (ext_hdr_type) {
|
|
case IPPROTO_FRAGMENT: {
|
|
// The length of Fragmentation and ESP headers is always 8 bytes. They don't have a length field.
|
|
return basic_ext_len;
|
|
}
|
|
case IPPROTO_AH: {
|
|
// In AH header the length field specifies the header's length in units of 4 bytes
|
|
return getIPv6GenericExtLen(offset_to_ext_hdr, format_multiplier_four);
|
|
}
|
|
case IPPROTO_HOPOPTS:
|
|
case IPPROTO_ROUTING:
|
|
case IPPROTO_DSTOPTS:
|
|
case IPPROTO_MH: {
|
|
// For these headers, the length field specifies the header's length in units of 8 bytes
|
|
return getIPv6GenericExtLen(offset_to_ext_hdr, format_multiplier_eight);
|
|
}
|
|
}
|
|
dbgWarning(D_PACKET) << "Unknown IPv6 Extension header type" << static_cast<uint>(ext_hdr_type);
|
|
return genError(PktErr::UNKNOWN_IPV6_EXTENSION_HEADER);
|
|
}
|
|
|
|
Maybe<IPProto, PktErr>
|
|
Packet::getIPv6Proto(IPProto proto)
|
|
{
|
|
uint offset_to_ext_hdr = sizeof(struct ip6_hdr);
|
|
|
|
while (isIPv6ProtoExtension(proto)) {
|
|
auto res = getIPv6ExtLen(offset_to_ext_hdr, proto);
|
|
if (!res.ok()) return res.passErr();
|
|
auto ext_len = *res;
|
|
|
|
if (offset_to_ext_hdr + ext_len > l3.size()) {
|
|
dbgTrace(D_PACKET) <<
|
|
"IPv6 Extension header " << static_cast<uint>(proto) << " body is too long" <<
|
|
" - Body length=" << ext_len << ", offset=" << offset_to_ext_hdr << ", L3 data length=" << l3.size();
|
|
return genError(PktErr::PKT_TOO_SHORT_FOR_IP_EXTENSION_HEADER_BODY);
|
|
}
|
|
|
|
if (proto == IPPROTO_FRAGMENT) {
|
|
dbgTrace(D_PACKET) << "Fragmented IPv6 packet";
|
|
is_fragment = true;
|
|
}
|
|
|
|
auto header = l3.getTypePtr<IPv6ExtBasic>(offset_to_ext_hdr).unpack();
|
|
proto = header->next_type;
|
|
offset_to_ext_hdr += ext_len;
|
|
}
|
|
|
|
l3_header = l3.getSubBuffer(0, offset_to_ext_hdr);
|
|
l3_payload = l3.getSubBuffer(offset_to_ext_hdr, l3.size());
|
|
return proto;
|
|
}
|
|
|
|
Maybe<ConnKey, PktErr>
|
|
Packet::parseFromL3v6()
|
|
{
|
|
auto maybe_ip6 = l2_payload.getTypePtr<struct ip6_hdr>(0);
|
|
if (!maybe_ip6.ok()) {
|
|
dbgTrace(D_PACKET)
|
|
<< "IPv6 packet is too short for an IPv6 header: "
|
|
<< l2_payload.size()
|
|
<< " < "
|
|
<< sizeof(struct ip);
|
|
return genError(PktErr::PKT_TOO_SHORT_FOR_IP_HEADER);
|
|
}
|
|
auto ip6 = maybe_ip6.unpack();
|
|
|
|
uint ip_version = (ip6->ip6_vfc) >> 4;
|
|
if (ip_version != 6) {
|
|
dbgTrace(D_PACKET) << "Bad IPv6 version " << ip_version;
|
|
return genError(PktErr::IP_VERSION_MISMATCH);
|
|
}
|
|
|
|
auto l3_len_reported_by_header = sizeof(struct ip6_hdr) + ntohs(ip6->ip6_plen);
|
|
if (l3_len_reported_by_header > l2_payload.size()) {
|
|
dbgTrace(D_PACKET) <<
|
|
"IP header reports a total of " << l3_len_reported_by_header <<
|
|
" bytes, but the packet length is only " << l2_payload.size() << " bytes";
|
|
return genError(PktErr::IP_SIZE_MISMATCH);
|
|
}
|
|
|
|
l3 = l2_payload.getSubBuffer(0, l3_len_reported_by_header); // Remove padding
|
|
|
|
auto proto = getIPv6Proto(ip6->ip6_nxt);
|
|
if (!proto.ok()) return genError(proto.getErr());
|
|
|
|
return parseFromL4(IPAddr(ip6->ip6_src), IPAddr(ip6->ip6_dst), proto.unpack());
|
|
}
|
|
|
|
Maybe<ConnKey, PktErr>
|
|
Packet::parseFromL3v4()
|
|
{
|
|
auto maybe_ip4 = l2_payload.getTypePtr<struct ip>(0);
|
|
if (!maybe_ip4.ok()) {
|
|
dbgTrace(D_PACKET)
|
|
<< "IPv4 packet is too short for an IPv4 header: "
|
|
<< l2_payload.size()
|
|
<< "<"
|
|
<< sizeof(struct ip);
|
|
return genError(PktErr::PKT_TOO_SHORT_FOR_IP_HEADER);
|
|
}
|
|
auto ip4 = maybe_ip4.unpack();
|
|
|
|
uint ip_version = ip4->ip_v;
|
|
if (ip_version != 4) {
|
|
dbgTrace(D_PACKET) << "Bad IPv4 version " << ip_version << " length: " << ntohs(ip4->ip_len);
|
|
return genError(PktErr::IP_VERSION_MISMATCH);
|
|
}
|
|
|
|
auto l3_len_reported_by_header = ntohs(ip4->ip_len);
|
|
if (l3_len_reported_by_header < sizeof(struct ip)) {
|
|
dbgTrace(D_PACKET) <<
|
|
"IPv4 payload length is smaller than the IPv4 header: " <<
|
|
l3_len_reported_by_header << " < " << sizeof(struct ip);
|
|
return genError(PktErr::IP_SIZE_MISMATCH);
|
|
}
|
|
if (l3_len_reported_by_header > l2_payload.size()) {
|
|
dbgTrace(D_PACKET) <<
|
|
"IP header reports a total of " << l3_len_reported_by_header <<
|
|
" bytes, but the packet length is only " << l2_payload.size() << " bytes";
|
|
return genError(PktErr::IP_SIZE_MISMATCH);
|
|
}
|
|
|
|
auto l3_hdr_len = ip4->ip_hl * sizeof(int);
|
|
if (l3_hdr_len < sizeof(struct ip)) {
|
|
dbgTrace(D_PACKET)
|
|
<< "The reported IPv4 header length is smaller than the allowed minimum: "
|
|
<< l3_hdr_len
|
|
<< " < "
|
|
<< sizeof(struct ip);
|
|
return genError(PktErr::IP_HEADER_TOO_SMALL);
|
|
}
|
|
if (l3_hdr_len > l2_payload.size()) {
|
|
dbgTrace(D_PACKET)
|
|
<< "IPv4 header is too big for the IPv4 packet: "
|
|
<< l3_hdr_len
|
|
<< " > "
|
|
<< l2_payload.size();
|
|
return genError(PktErr::PKT_TOO_SHORT_FOR_IP_HEADER);
|
|
}
|
|
|
|
auto frag_offset = ntohs(ip4->ip_off);
|
|
if ((frag_offset & IP_OFFMASK) || (frag_offset & IP_MF)) {
|
|
dbgTrace(D_PACKET) << "Fragmented IPv4 packet";
|
|
is_fragment = true;
|
|
}
|
|
|
|
l3 = l2_payload.getSubBuffer(0, l3_len_reported_by_header); // Remove padding
|
|
l3_header = l3.getSubBuffer(0, l3_hdr_len);
|
|
l3_payload = l3.getSubBuffer(l3_hdr_len, l3.size());
|
|
|
|
return parseFromL4(IPAddr(ip4->ip_src), IPAddr(ip4->ip_dst), ip4->ip_p);
|
|
}
|
|
|
|
Maybe<ConnKey, PktErr>
|
|
Packet::parseFromL2()
|
|
{
|
|
// In case of VLAN we want to remove the additional information and pass the packet as normal.
|
|
uint _maclen = sizeof(struct ether_header) - 4; // -4 for the first do loop.
|
|
uint16_t ether_type;
|
|
do
|
|
{
|
|
_maclen += 4; // 4 is the size of vlan tag.
|
|
auto maybe_ether_type = pkt_data.getTypePtr<uint16_t>(_maclen - 2); // Last 2 Bytes contain the ether type.
|
|
if (!maybe_ether_type.ok()) {
|
|
dbgTrace(D_PACKET)
|
|
<< "VLAN tag length is greater than the packet length: "
|
|
<< _maclen
|
|
<< " > "
|
|
<< pkt_data.size();
|
|
return genError(PktErr::MAC_LEN_TOO_BIG);
|
|
}
|
|
ether_type = *(maybe_ether_type.unpack());
|
|
} while (ether_type == constHTONS(ETHERTYPE_VLAN));
|
|
|
|
l2_header = pkt_data.getSubBuffer(0, _maclen);
|
|
l2_payload = pkt_data.getSubBuffer(_maclen, pkt_data.size());
|
|
|
|
switch (ether_type) {
|
|
case constHTONS(ETHERTYPE_IP): {
|
|
return parseFromL3v4();
|
|
}
|
|
case constHTONS(ETHERTYPE_IPV6): {
|
|
return parseFromL3v6();
|
|
}
|
|
default: {
|
|
dbgTrace(D_PACKET) << "Unsupported Ethernet type: " << ether_type;
|
|
return genError(PktErr::NON_IP_PACKET);
|
|
}
|
|
}
|
|
}
|
|
|
|
Maybe<ConnKey, PktErr>
|
|
Packet::parsePacket(PktType type, IPType proto)
|
|
{
|
|
if (type == PktType::PKT_L2) return parseFromL2();
|
|
|
|
l2_payload = pkt_data;
|
|
switch (proto) {
|
|
case IPType::V4: {
|
|
return parseFromL3v4();
|
|
}
|
|
case IPType::V6: {
|
|
return parseFromL3v6();
|
|
}
|
|
default: {
|
|
dbgAssert(false)
|
|
<< AlertInfo(AlertTeam::CORE, "packet")
|
|
<< "Unknown (neither IPv4, nor IPv6), or uninitialized packet type: "
|
|
<< proto;
|
|
}
|
|
}
|
|
|
|
return genError(PktErr::UNKNOWN_L3_PROTOCOL);
|
|
}
|
|
|
|
std::vector<u_char>
|
|
Packet::getL2DataVec() const
|
|
{
|
|
auto p = pkt_data.data();
|
|
std::vector<u_char> buf(p, p+pkt_data.size());
|
|
return buf;
|
|
}
|
|
|
|
void
|
|
Packet::setInterface(NetworkIfNum value)
|
|
{
|
|
interface = value;
|
|
is_interface = true;
|
|
}
|
|
|
|
void
|
|
Packet::setZecoOpaque(u_int64_t value)
|
|
{
|
|
zeco_opaque = value;
|
|
has_zeco_opaque = true;
|
|
}
|
|
|
|
Maybe<NetworkIfNum>
|
|
Packet::getInterface() const
|
|
{
|
|
if (!is_interface) return genError("Could not set an interface to send the packet");
|
|
return interface;
|
|
}
|
|
|
|
Maybe<u_int64_t>
|
|
Packet::getZecoOpaque() const
|
|
{
|
|
if (!has_zeco_opaque) return genError("Could not get the zeco opaque");
|
|
return zeco_opaque;
|
|
}
|