From 363d89512a31c28d9256d2d52ce7bdd41c14e32e Mon Sep 17 00:00:00 2001 From: Victor Roemer Date: Tue, 18 Jul 2023 20:12:22 +0000 Subject: [PATCH] libpacket: Decode Geneve encapsulation Initial support for Geneve encapsulation decoding ([RFC 8926][rfc]). [rfc]: https://datatracker.ietf.org/doc/html/rfc8926 --- include/packet/protocol.h | 1 + include/packet/stats.h | 2 + src/CMakeLists.txt | 10 +++-- src/geneve.c | 69 ++++++++++++++++++++++++++++ src/geneve.h | 7 +++ src/packet.c | 3 +- src/udp.c | 15 ++++--- tests/pcaps/geneve.pcap | Bin 0 -> 9928 bytes tests/print_packet.c | 92 ++++++++++++++++++++++++++++++++++++++ tests/testme.c | 21 ++++++--- 10 files changed, 201 insertions(+), 19 deletions(-) create mode 100644 src/geneve.c create mode 100644 src/geneve.h create mode 100644 tests/pcaps/geneve.pcap create mode 100644 tests/print_packet.c diff --git a/include/packet/protocol.h b/include/packet/protocol.h index 7753e47..b01f6e9 100644 --- a/include/packet/protocol.h +++ b/include/packet/protocol.h @@ -60,6 +60,7 @@ typedef enum PROTO_SCTP, PROTO_ICMP, PROTO_ICMP6, + PROTO_GENEVE, PROTO_MAX } PROTOCOL; diff --git a/include/packet/stats.h b/include/packet/stats.h index fcd0374..262255b 100644 --- a/include/packet/stats.h +++ b/include/packet/stats.h @@ -101,6 +101,8 @@ struct packet_stats { uint32_t sctps_badsum; /* checksum errors */ uint32_t sctps_badtype; /* bad chunk type */ // XXX UNUSED uint32_t sctps_tooshort; /* not enough data */ + + uint32_t geneve_packets; /* total geneve packets */ }; #ifdef __cplusplus diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 1aba50b..d9dac09 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -20,6 +20,12 @@ target_sources( crc32.h eth.c eth.h + geneve.c + geneve.h + icmp4.c + icmp4.h + icmp6.c + icmp6.h ip4.c ip4.h ip6.c @@ -45,10 +51,6 @@ target_sources( tcp.h udp.c udp.h - icmp4.c - icmp4.h - icmp6.c - icmp6.h vlan.c vlan.h ) diff --git a/src/geneve.c b/src/geneve.c new file mode 100644 index 0000000..1ffd998 --- /dev/null +++ b/src/geneve.c @@ -0,0 +1,69 @@ +// https://datatracker.ietf.org/doc/html/rfc8926 + +#include +#include + +#include "packet_private.h" +#include "eth.h" + +extern struct packet_stats s_stats; + +#define ETHERTYPE_ETH 0x6558 + +struct geneve_header { + uint8_t opt_len; + uint8_t flags; + uint16_t proto; + uint32_t vni; +} __attribute__((packed)); + +#define GENEVE_VERSION(optlen) (((optlen) & 0xC0) >> 6) +#define GENEVE_OPTLEN(optlen) (((optlen) & 0x3F) * 4) + +#define IS_CONTROL_PACKET(flags) (((flags) & 0x80) >> 7) +#define HAS_CRITICAL_OPTIONS(flags) (((flags) & 0x40) >> 6) +#define FLAGS_RESERVED(flags) ((flags) & 0x3F) + +#define VNI(vni) (((vni) & 0xFFFFFF00) >> 8) +#define VNI_RESERVED(vni) ((vni) & 0x000000FF) + + +static bool inline +_is_valid(struct geneve_header *hdr) +{ + if (GENEVE_VERSION(hdr->opt_len) != 0) + return false; + + if (FLAGS_RESERVED(hdr->flags) != 0) + return false; + + if (VNI_RESERVED(hdr->vni) != 0) + return false; + + return true; +} + +int +decode_geneve(const uint8_t *pkt, unsigned len, Packet* packet) +{ + struct geneve_header *hdr = (struct geneve_header*)pkt; + + if (len < sizeof(*hdr)) + return -1; + + unsigned hlen = GENEVE_OPTLEN(hdr->opt_len) + sizeof(*hdr); + if (len < hlen) + return -1; + + s_stats.geneve_packets++; + + packet->payload += hlen; + packet->paysize -= hlen; + packet_layer_ins(packet, pkt, hlen, PROTO_GENEVE); + + uint16_t proto = ntohs(hdr->proto); + if (proto == ETHERTYPE_ETH) + return decode_dlt_eth(pkt + hlen, len - hlen, packet); + + return bind_eth(proto, pkt + hlen, len - hlen, packet); +} diff --git a/src/geneve.h b/src/geneve.h new file mode 100644 index 0000000..0043ec5 --- /dev/null +++ b/src/geneve.h @@ -0,0 +1,7 @@ +#ifndef DECODE_GENEVE_H +#define DECODE_GENEVE_H + +int decode_geneve(const uint8_t *pkt, unsigned len, Packet*); + +#endif /* DECODE_GENEVE_H */ + diff --git a/src/packet.c b/src/packet.c index ce35608..9ff75e4 100644 --- a/src/packet.c +++ b/src/packet.c @@ -227,7 +227,8 @@ static const char *proto_map[PROTO_MAX] = { "udp", "sctp", "icmp", - "icmp6" + "icmp6", + "geneve" }; #define ENUM2STR(num, map) \ diff --git a/src/udp.c b/src/udp.c index 1c6cc60..d1c42fa 100644 --- a/src/udp.c +++ b/src/udp.c @@ -37,9 +37,9 @@ #define __FAVOR_BSD #include -#include "packet_private.h" - #include "checksum.h" +#include "packet_private.h" +#include "geneve.h" extern struct packet_stats s_stats; @@ -78,16 +78,17 @@ decode_udp(const uint8_t *pkt, const uint32_t len, Packet *p) if (udp->uh_sum == 0 && p->version == 6) { s_stats.udps_badsum++; - //return -1; } - else if (udp->uh_sum == 0) + + if (udp->uh_sum != 0) { - return 0; + if (checksum((uint16_t *)udp, &pseudo, ntohs(pseudo.len)) != 0) + s_stats.udps_badsum++; } - if (checksum((uint16_t *)udp, &pseudo, ntohs(pseudo.len)) != 0) + if (p->dstport == 6081) { - s_stats.udps_badsum++; + return decode_geneve(pkt + sizeof(*udp), len - sizeof(*udp), p); } return 0; diff --git a/tests/pcaps/geneve.pcap b/tests/pcaps/geneve.pcap new file mode 100644 index 0000000000000000000000000000000000000000..279549356cda61b2d357a10328f7774fd474b009 GIT binary patch literal 9928 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 E-)@IWyZ`_I literal 0 HcmV?d00001 diff --git a/tests/print_packet.c b/tests/print_packet.c new file mode 100644 index 0000000..5c96804 --- /dev/null +++ b/tests/print_packet.c @@ -0,0 +1,92 @@ +#include +#include +#include +#include + +// Taken from Pcapstats BSD License +void print_data(const uint8_t *data, int length) +{ + int i, x, j, c; + int w = 0; + + for( i=0; length>0; length -= 16 ) + { + c = length >= 16 ? 16 : length; + printf("%06X ", w); + w+=16; + + for( j=0; jaddr8[0], + a->addr8[1], + a->addr8[2], + a->addr8[3]); + + printf("dst: %d.%d.%d.%d, ", + b->addr8[0], + b->addr8[1], + b->addr8[2], + b->addr8[3]); + + printf("proto: %d, id: %d, sp: %d, dp: %d, dlen: %u]\n", + proto, + packet_id(&packet), + sport, + dport, + packet_paysize(&packet)); + + if (packet_is_fragment(&packet)) + { + printf(" |ip off: %u %s\n", + packet_frag_offset(&packet), + packet_frag_mf(&packet) ? "mf" : ""); + } + + + size_t max = packet_paysize(&packet); + const uint8_t *payload = packet_payload(&packet); + max = max > 128 ? 128 : max; + + print_data(payload, max); +} + diff --git a/tests/testme.c b/tests/testme.c index 3bb294b..5f55dcd 100644 --- a/tests/testme.c +++ b/tests/testme.c @@ -7,6 +7,8 @@ int s_error = 0; +extern void print_packet(const uint8_t *data, const size_t len); + #define PERLINE 10 void print_cbuf(const uint8_t *data, int length) { @@ -26,6 +28,8 @@ void print_cbuf(const uint8_t *data, int length) } } + + static void packet_callback(uint8_t *unused, const struct pcap_pkthdr *ph, const uint8_t *pkt) @@ -33,7 +37,7 @@ packet_callback(uint8_t *unused, const struct pcap_pkthdr *ph, static int count = 0; count++; - Packet *packet = packet_create( ); + //Packet *packet = packet_create( ); /* cute way of collecting packet data for unit tests */ #if 0 @@ -42,12 +46,14 @@ packet_callback(uint8_t *unused, const struct pcap_pkthdr *ph, printf("};\n"); #endif - int error = packet_decode(packet, pkt, ph->caplen); + print_packet(pkt, ph->caplen); + //int error = packet_decode(packet, pkt, ph->caplen); - if (error) - { - s_error = error; - } + //if (error) + //{ + // s_error = error; + //} + /* not cute */ #if 0 @@ -63,7 +69,7 @@ packet_callback(uint8_t *unused, const struct pcap_pkthdr *ph, printf("\n"); #endif - packet_destroy(packet); + //packet_destroy(packet); } int main(int argc, char *argv[]) @@ -96,6 +102,7 @@ int main(int argc, char *argv[]) printf("Processed %u packets\n", ps->total_packets); printf("Rejected %u packets\n", ps->total_errors); + printf("Analyzed %u geneve packets\n", ps->geneve_packets); printf("Analyzed %u pppoe packets\n", ps->pppoes_packets); printf("Analyzed %u ppp packets\n", ps->ppps_packets); printf("Analyzed %u ip packets\n", ps->ips_packets);