From 9528e02e46d365803762ad2b4bdd296402c701c9 Mon Sep 17 00:00:00 2001 From: Breno Silva Date: Sun, 23 Aug 2009 00:24:13 -0300 Subject: [PATCH] GRE support --- src/Makefile.am | 1 + src/decode-events.h | 17 ++ src/decode-gre.c | 327 ++++++++++++++++++++++++++++++++++++++ src/decode-gre.h | 61 +++++++ src/decode.c | 3 + src/decode.h | 5 + src/detect-decode-event.h | 14 ++ src/eidps.c | 1 + 8 files changed, 429 insertions(+) create mode 100644 src/decode-gre.c create mode 100644 src/decode-gre.h diff --git a/src/Makefile.am b/src/Makefile.am index 359f8f4759..c86d9d96c6 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -8,6 +8,7 @@ source-pcap-file.c source-pcap-file.h \ decode.c decode.h \ decode-ethernet.c decode-ethernet.h \ decode-sll.c decode-sll.h \ +decode-gre.c decode-gre.h \ decode-ppp.c decode-ppp.h \ decode-pppoe.c decode-pppoe.h \ decode-ipv4.c decode-ipv4.h \ diff --git a/src/decode-events.h b/src/decode-events.h index e64070d6e8..f7c4f8b8e4 100644 --- a/src/decode-events.h +++ b/src/decode-events.h @@ -62,6 +62,23 @@ enum { /* PPPOE EVENTS */ PPPOE_PKT_TOO_SMALL, + + /* GRE EVENTS */ + GRE_PKT_TOO_SMALL, + GRE_WRONG_VERSION, + GRE_VERSION0_RECUR, + GRE_VERSION0_FLAGS, + GRE_VERSION0_HDR_TOO_BIG, + GRE_VERSION1_CHKSUM, + GRE_VERSION1_ROUTE, + GRE_VERSION1_SSR, + GRE_VERSION1_RECUR, + GRE_VERSION1_FLAGS, + GRE_VERSION1_NO_KEY, + GRE_VERSION1_WRONG_PROTOCOL, + GRE_VERSION1_MALFORMED_SRE_HDR, + GRE_VERSION1_HDR_TOO_BIG, + }; #endif /* __DECODE_EVENTS_H__ */ diff --git a/src/decode-gre.c b/src/decode-gre.c new file mode 100644 index 0000000000..9badc59571 --- /dev/null +++ b/src/decode-gre.c @@ -0,0 +1,327 @@ +/** Copyright (c) 2009 Open Information Security Foundation + * + * \author Breno Silva + */ + + +#include "eidps.h" +#include "decode.h" +#include "decode-events.h" +#include "decode-gre.h" + +#include "util-unittest.h" + +/** + * \brief Function to decode GRE packets + */ + +void DecodeGRE(ThreadVars *tv, DecodeThreadVars *dtv, Packet *p, uint8_t *pkt, uint16_t len, PacketQueue *pq) +{ + uint16_t header_len = GRE_HDR_LEN; + GRESreHdr *gsre = NULL; + + if(len < GRE_HDR_LEN) { + DECODER_SET_EVENT(p,GRE_PKT_TOO_SMALL); + return; + } + + p->greh = (GREHdr *)pkt; + if(p->greh == NULL) + return; + +#ifdef DEBUG + printf("DecodeGRE: p %p pkt %p GRE protocol %04x Len: %d GRE version %x\n", p, pkt, GRE_GET_PROTO(p->greh), len,GRE_GET_VERSION(p->greh)); +#endif + + switch (GRE_GET_VERSION(p->greh)) + { + case GRE_VERSION_0: + + /* GRE version 0 doenst support the fields below RFC 1701 */ + + /** + * \todo We need to make sure this does not allow bypassing + * inspection. A server may just ignore these and + * continue processing the packet, but we will not look + * further into it. + */ + + if (GRE_FLAG_ISSET_RECUR(p->greh)) { + DECODER_SET_EVENT(p,GRE_VERSION0_RECUR); + return; + } + + if (GREV1_FLAG_ISSET_FLAGS(p->greh)) { + DECODER_SET_EVENT(p,GRE_VERSION0_FLAGS); + return; + } + + /* Adjust header length based on content */ + + if (GRE_FLAG_ISSET_KY(p->greh)) + header_len += GRE_KEY_LEN; + + if (GRE_FLAG_ISSET_SQ(p->greh)) + header_len += GRE_SEQ_LEN; + + if (GRE_FLAG_ISSET_CHKSUM(p->greh) || GRE_FLAG_ISSET_ROUTE(p->greh)) + header_len += GRE_CHKSUM_LEN + GRE_OFFSET_LEN; + + if (header_len > len) { + DECODER_SET_EVENT(p,GRE_VERSION0_HDR_TOO_BIG); + return; + } + + if (GRE_FLAG_ISSET_ROUTE(p->greh)) + { + + gsre = (GRESreHdr *)(pkt + header_len); + + if(gsre == NULL) + return; + + while (1) + { + if ((header_len+GRE_SRE_HDR_LEN) > len) { + DECODER_SET_EVENT(p,GRE_VERSION1_MALFORMED_SRE_HDR); + break; + } + + header_len += GRE_SRE_HDR_LEN; + + if (gsre && (ntohs(gsre->af) == 0) && (gsre->sre_length == 0)) + break; + + header_len += gsre->sre_length; + gsre = (GRESreHdr *)(pkt + header_len); + + } + } + break; + + case GRE_VERSION_1: + + /* GRE version 1 doenst support the fields below RFC 1701 */ + + /** + * \todo We need to make sure this does not allow bypassing + * inspection. A server may just ignore these and + * continue processing the packet, but we will not look + * further into it. + */ + + if (GRE_FLAG_ISSET_CHKSUM(p->greh)) { + DECODER_SET_EVENT(p,GRE_VERSION1_CHKSUM); + return; + } + + if (GRE_FLAG_ISSET_ROUTE(p->greh)) { + DECODER_SET_EVENT(p,GRE_VERSION1_ROUTE); + return; + } + + if (GRE_FLAG_ISSET_SSR(p->greh)) { + DECODER_SET_EVENT(p,GRE_VERSION1_SSR); + return; + } + + if (GRE_FLAG_ISSET_RECUR(p->greh)) { + DECODER_SET_EVENT(p,GRE_VERSION1_RECUR); + return; + } + + if (GREV1_FLAG_ISSET_FLAGS(p->greh)) { + DECODER_SET_EVENT(p,GRE_VERSION1_FLAGS); + return; + } + + if (GRE_GET_PROTO(p->greh) != GRE_PROTO_PPP) { + DECODER_SET_EVENT(p,GRE_VERSION1_WRONG_PROTOCOL); + return; + } + + if (!(GRE_FLAG_ISSET_KY(p->greh))) { + DECODER_SET_EVENT(p,GRE_VERSION1_NO_KEY); + return; + } + + header_len += GRE_KEY_LEN; + + /* Adjust header length based on content */ + + if (GRE_FLAG_ISSET_SQ(p->greh)) + header_len += GRE_SEQ_LEN; + + if (GREV1_FLAG_ISSET_ACK(p->greh)) + header_len += GREV1_ACK_LEN; + + if (header_len > len) { + DECODER_SET_EVENT(p,GRE_VERSION1_HDR_TOO_BIG); + return; + } + + break; + default: + DECODER_SET_EVENT(p,GRE_WRONG_VERSION); + return; + } + + switch (GRE_GET_PROTO(p->greh)) + { + case ETHERNET_TYPE_IP: + { + PerfCounterIncr(dtv->counter_gre, tv->pca); + if (pq != NULL) { + + Packet *tp = TunnelPktSetup(tv, dtv, p, pkt + header_len, len - header_len, GRE_GET_PROTO(p->greh)); + DecodeTunnel(tv, dtv, tp, tp->pkt, tp->pktlen, pq); + PacketEnqueue(pq,tp); + + SET_TUNNEL_PKT(p); + } + break; + } + + case GRE_PROTO_PPP: + { + PerfCounterIncr(dtv->counter_gre, tv->pca); + if (pq != NULL) { + + Packet *tp = TunnelPktSetup(tv, dtv, p, pkt + header_len, len - header_len, GRE_GET_PROTO(p->greh)); + DecodeTunnel(tv, dtv, tp, tp->pkt, tp->pktlen, pq); + PacketEnqueue(pq,tp); + + SET_TUNNEL_PKT(p); + } + break; + } + + case ETHERNET_TYPE_IPV6: + { + PerfCounterIncr(dtv->counter_gre, tv->pca); + if (pq != NULL) { + + Packet *tp = TunnelPktSetup(tv, dtv, p, pkt + header_len, len - header_len, GRE_GET_PROTO(p->greh)); + DecodeTunnel(tv, dtv, tp, tp->pkt, tp->pktlen, pq); + PacketEnqueue(pq,tp); + + SET_TUNNEL_PKT(p); + } + break; + } + default: + return; + } + +} + + +/** + * \test DecodeGRETest01 is a test for small gre packet + */ + +static int DecodeGREtest01 (void) { + + uint8_t raw_gre[] = { 0x00 ,0x6e ,0x62 }; + Packet p; + ThreadVars tv; + DecodeThreadVars dtv; + + memset(&tv, 0, sizeof(ThreadVars)); + memset(&p, 0, sizeof(Packet)); + memset(&dtv, 0, sizeof(DecodeThreadVars)); + + DecodeGRE(&tv, &dtv, &p, raw_gre, sizeof(raw_gre), NULL); + + if(DECODER_ISSET_EVENT(&p,GRE_PKT_TOO_SMALL)) { + return 1; + } + + return 0; +} + +/** + * \test DecodeGRETest02 is a test for wrong gre version + */ + +static int DecodeGREtest02 (void) { + uint8_t raw_gre[] = { + 0x00, 0x6e, 0x62, 0xac, 0x40, 0x00, 0x40, 0x2f, + 0xc2, 0xc7, 0x0a, 0x00, 0x00, 0x64, 0x0a, 0x00, + 0x00, 0x8a, 0x30, 0x01, 0x0b, 0x00, 0x4e, 0x00, + 0x00, 0x00, 0x18, 0x4a, 0x50, 0xff, 0x03, 0x00, + 0x21, 0x45, 0x00, 0x00, 0x4a, 0x00, 0x00, 0x40, + 0x00, 0x40, 0x11, 0x94, 0x22, 0x50, 0x7e, 0x2b, + 0x2d, 0xc2, 0x6d, 0x68, 0x68, 0x80, 0x0e, 0x00, + 0x35, 0x00, 0x36, 0x9f, 0x18, 0xdb, 0xc4, 0x01, + 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x03, 0x73, 0x31, 0x36, 0x09, 0x73, 0x69, + 0x74, 0x65, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x03, + 0x63, 0x6f, 0x6d, 0x00, 0x00, 0x01, 0x00, 0x01, + 0x00, 0x00, 0x29, 0x10, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00 }; + Packet p; + ThreadVars tv; + DecodeThreadVars dtv; + + memset(&tv, 0, sizeof(ThreadVars)); + memset(&p, 0, sizeof(Packet)); + memset(&dtv, 0, sizeof(DecodeThreadVars)); + + DecodeGRE(&tv, &dtv, &p, raw_gre, sizeof(raw_gre), NULL); + + if(DECODER_ISSET_EVENT(&p,GRE_WRONG_VERSION)) { + return 1; + } + + return 0; +} + + +/** + * \test DecodeGRETest03 is a test for valid gre packet + */ + +static int DecodeGREtest03 (void) { + uint8_t raw_gre[] = { + 0x00, 0x6e, 0x62, 0xac, 0x40, 0x00, 0x40, 0x2f, + 0xc2, 0xc7, 0x0a, 0x00, 0x00, 0x64, 0x0a, 0x00, + 0x00, 0x8a, 0x30, 0x01, 0x88, 0x0b, 0x00, 0x4e, + 0x00, 0x00, 0x00, 0x18, 0x4a, 0x50, 0xff, 0x03, + 0x00, 0x21, 0x45, 0x00, 0x00, 0x4a, 0x00, 0x00, + 0x40, 0x00, 0x40, 0x11, 0x94, 0x22, 0x50, 0x7e, + 0x2b, 0x2d, 0xc2, 0x6d, 0x68, 0x68, 0x80, 0x0e, + 0x00, 0x35, 0x00, 0x36, 0x9f, 0x18, 0xdb, 0xc4, + 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x01, 0x03, 0x73, 0x31, 0x36, 0x09, 0x73, + 0x69, 0x74, 0x65, 0x6d, 0x65, 0x74, 0x65, 0x72, + 0x03, 0x63, 0x6f, 0x6d, 0x00, 0x00, 0x01, 0x00, + 0x01, 0x00, 0x00, 0x29, 0x10, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00 }; + Packet p; + ThreadVars tv; + DecodeThreadVars dtv; + + memset(&tv, 0, sizeof(ThreadVars)); + memset(&p, 0, sizeof(Packet)); + memset(&dtv, 0, sizeof(DecodeThreadVars)); + + DecodeGRE(&tv, &dtv, &p, raw_gre, sizeof(raw_gre), NULL); + + if(p.greh == NULL) { + return 0; + } + + + return 1; +} + +/** + * \brief this function registers unit tests for DecodeEvent + */ + +void DecodeGRERegisterTests(void) { + UtRegisterTest("DecodeGREtest01", DecodeGREtest01, 1); + UtRegisterTest("DecodeGREtest02", DecodeGREtest02, 1); + UtRegisterTest("DecodeGREtest03", DecodeGREtest03, 1); +} diff --git a/src/decode-gre.h b/src/decode-gre.h new file mode 100644 index 0000000000..ccb1efa4ee --- /dev/null +++ b/src/decode-gre.h @@ -0,0 +1,61 @@ +/** Copyright (c) 2009 Open Information Security Foundation + * + * \author Breno Silva + */ + +#ifndef __DECODE_GRE_H__ +#define __DECODE_GRE_H__ + +#ifndef IPPROTO_GRE +#define IPPROTO_GRE 47 +#endif + +#include "decode.h" +#include "threadvars.h" + +typedef struct _GREHdr +{ + uint8_t flags; + uint8_t version; + uint16_t ether_type; + +} GREHdr; + +typedef struct _GRESreHdr +{ + uint16_t af; /* address familly */ + uint8_t sre_offset; + uint8_t sre_length; + uint8_t *routing; +} GRESreHdr; + +#define GRE_VERSION_0 0x0000 +#define GRE_VERSION_1 0x0001 + +#define GRE_HDR_LEN 4 +#define GRE_CHKSUM_LEN 2 +#define GRE_OFFSET_LEN 2 +#define GRE_KEY_LEN 4 +#define GRE_SEQ_LEN 4 +#define GRE_SRE_HDR_LEN 4 +#define GRE_PROTO_PPP 0x880b + +#define GRE_FLAG_ISSET_CHKSUM(r) (r->flags & 0x80) +#define GRE_FLAG_ISSET_ROUTE(r) (r->flags & 0x40) +#define GRE_FLAG_ISSET_KY(r) (r->flags & 0x20) +#define GRE_FLAG_ISSET_SQ(r) (r->flags & 0x10) +#define GRE_FLAG_ISSET_SSR(r) (r->flags & 0x08) +#define GRE_FLAG_ISSET_RECUR(r) (r->flags & 0x07) +#define GRE_GET_VERSION(r) (r->version & 0x07) +#define GRE_GET_FLAGS(r) (r->version & 0xF8) +#define GRE_GET_PROTO(r) ntohs(r->ether_type) + +#define GREV1_HDR_LEN 8 +#define GREV1_ACK_LEN 4 +#define GREV1_FLAG_ISSET_FLAGS(r) (r->version & 0x78) +#define GREV1_FLAG_ISSET_ACK(r) (r->version & 0x80) + +void DecodeGRERegisterTests(void); + +#endif /* __DECODE_GRE_H__ */ + diff --git a/src/decode.c b/src/decode.c index 720129fd55..fdd8d6a7e9 100644 --- a/src/decode.c +++ b/src/decode.c @@ -8,6 +8,9 @@ void DecodeTunnel(ThreadVars *tv, DecodeThreadVars *dtv, Packet *p, uint8_t *pkt, uint16_t len, PacketQueue *pq) { switch (p->tunnel_proto) { + case PPP_OVER_GRE: + return DecodePPP(tv, dtv, p, pkt, len, pq); + break; case IPPROTO_IP: return DecodeIPV4(tv, dtv, p, pkt, len, pq); case IPPROTO_IPV6: diff --git a/src/decode.h b/src/decode.h index 7d79a19946..1a32332465 100644 --- a/src/decode.h +++ b/src/decode.h @@ -32,6 +32,7 @@ #include "action-globals.h" #include "decode-ethernet.h" +#include "decode-gre.h" #include "decode-ppp.h" #include "decode-pppoe.h" #include "decode-sll.h" @@ -241,6 +242,7 @@ typedef struct Packet_ EthernetHdr *ethh; PPPHdr *ppph; PPPoEHdr *pppoeh; + GREHdr *greh; IPV4Hdr *ip4h; IPV4Vars ip4vars; @@ -314,6 +316,7 @@ typedef struct DecodeThreadVars_ uint16_t counter_icmpv4; uint16_t counter_icmpv6; uint16_t counter_ppp; + uint16_t counter_gre; uint16_t counter_pppoe; uint16_t counter_avg_pkt_size; uint16_t counter_max_pkt_size; @@ -328,6 +331,7 @@ typedef struct DecodeThreadVars_ } \ (p)->ethh = NULL; \ (p)->ppph = NULL; \ + (p)->greh = NULL; \ (p)->ip4h = NULL; \ (p)->ip6h = NULL; \ (p)->action = 0; \ @@ -419,6 +423,7 @@ Packet *TunnelPktSetup(ThreadVars *, DecodeThreadVars *, Packet *, uint8_t *, ui #define LINKTYPE_ETHERNET DLT_EN10MB #define LINKTYPE_LINUX_SLL 113 #define LINKTYPE_PPP 9 +#define PPP_OVER_GRE 11 #endif /* __DECODE_H__ */ diff --git a/src/detect-decode-event.h b/src/detect-decode-event.h index 651f806e7a..0b3c6a9ba7 100644 --- a/src/detect-decode-event.h +++ b/src/detect-decode-event.h @@ -59,6 +59,20 @@ struct DetectDecodeEvents_ { { "ppp.ip6_pkt_too_small", PPPIPV6_PKT_TOO_SMALL, }, { "ppp.wrong_type", PPP_WRONG_TYPE, }, /** unknown & invalid protocol */ { "ppp.unsup_proto", PPP_UNSUP_PROTO, }, /** unsupported but valid protocol */ + { "gre.pkt_too_small", GRE_PKT_TOO_SMALL, }, + { "gre.wrong_version", GRE_WRONG_VERSION, }, + { "gre.version0_recur", GRE_VERSION0_RECUR, }, + { "gre.version0_flags", GRE_VERSION0_FLAGS, }, + { "gre.version0_hdr_too_big", GRE_VERSION0_HDR_TOO_BIG, }, + { "gre.version1_chksum", GRE_VERSION1_CHKSUM, }, + { "gre.version1_route", GRE_VERSION1_ROUTE, }, + { "gre.version1_ssr", GRE_VERSION1_SSR, }, + { "gre.version1_recur", GRE_VERSION1_RECUR, }, + { "gre.version1_flags", GRE_VERSION1_FLAGS, }, + { "gre.version1_no_key", GRE_VERSION1_NO_KEY, }, + { "gre.version1_wrong_protocol", GRE_VERSION1_WRONG_PROTOCOL, }, + { "gre.version1_malformed_sre_hdr", GRE_VERSION1_MALFORMED_SRE_HDR, }, + { "gre.version1_hdr_too_big", GRE_VERSION1_HDR_TOO_BIG, }, { NULL, 0 }, }; #endif /* DETECT_EVENTS */ diff --git a/src/eidps.c b/src/eidps.c index 96ab2547ad..25b10c7fa7 100644 --- a/src/eidps.c +++ b/src/eidps.c @@ -972,6 +972,7 @@ int main(int argc, char **argv) DecodePPPoERegisterTests(); DecodeICMPV4RegisterTests(); DecodeIPV4RegisterTests(); + DecodeGRERegisterTests(); AlpDetectRegisterTests(); ConfRegisterTests(); UtRunTests();