extern const BaseApi* cd_eth[];
extern const BaseApi* cd_fabricpath[];
extern const BaseApi* cd_frag[];
+extern const BaseApi* cd_geneve[];
extern const BaseApi* cd_gre[];
extern const BaseApi* cd_gtp[];
extern const BaseApi* cd_icmp4[];
PluginManager::load_plugins(cd_eth);
PluginManager::load_plugins(cd_fabricpath);
PluginManager::load_plugins(cd_frag);
+ PluginManager::load_plugins(cd_geneve);
PluginManager::load_plugins(cd_gre);
PluginManager::load_plugins(cd_gtp);
PluginManager::load_plugins(cd_icmp4);
DECODE_MPLS_RESERVED_LABEL = 175,
DECODE_MPLS_LABEL_STACK = 176,
+ DECODE_GENEVE_DGRAM_LT_GENEVE_HDR = 180,
+ DECODE_GENEVE_INVALID_VERSION,
+ DECODE_GENEVE_INVALID_HEADER,
+ DECODE_GENEVE_INVALID_FLAGS,
+ DECODE_GENEVE_INVALID_OPTIONS,
+
DECODE_ICMP_ORIG_IP_TRUNCATED = 250,
DECODE_ICMP_ORIG_IP_VER_MISMATCH = 251,
DECODE_ICMP_ORIG_DGRAM_LT_ORIG_IP = 252,
{ "vxlan_ports", Parameter::PT_BIT_LIST, "65535",
"4789", "set VXLAN ports" },
+ { "geneve_ports", Parameter::PT_BIT_LIST, "65535",
+ "6081", "set Geneve ports" },
+
{ nullptr, Parameter::PT_MAX, nullptr, nullptr, nullptr }
};
constexpr uint16_t GTP_U_PORT = 2152;
constexpr uint16_t GTP_U_PORT_V0 = 3386;
constexpr uint16_t VXLAN_U_PORT = 4789;
+constexpr uint16_t GENEVE_U_PORT = 6081;
class UdpCodecConfig
{
gtp_ports.set(GTP_U_PORT);
gtp_ports.set(GTP_U_PORT_V0);
vxlan_ports.set(VXLAN_U_PORT);
+ geneve_ports.set(GENEVE_U_PORT);
}
bool deep_teredo_inspection()
bool vxlan_decoding()
{ return vxlan_decode; }
+ bool geneve_decoding()
+ { return geneve_decode; }
+
bool is_vxlan_port(uint16_t port)
{ return vxlan_ports.test(port); }
+ bool is_geneve_port(uint16_t port)
+ { return geneve_ports.test(port); }
+
void set_gtp_ports(const PortBitSet& ports)
{
gtp_ports = ports;
vxlan_decode = ports.any();
}
+ void set_geneve_ports(const PortBitSet& ports)
+ {
+ geneve_ports = ports;
+ geneve_decode = ports.any();
+ }
+
private:
bool enable_teredo = false;
PortBitSet gtp_ports;
PortBitSet vxlan_ports;
+ PortBitSet geneve_ports;
bool gtp_decode = true;
bool vxlan_decode = true;
+ bool geneve_decode = true;
};
class UdpModule : public BaseCodecModule
v.get_bits(ports);
config->set_vxlan_ports(ports);
}
+ else if ( v.is("geneve_ports") )
+ {
+ v.get_bits(ports);
+ config->set_geneve_ports(ports);
+ }
else
return false;
{
codec.next_prot_id = ProtocolId::VXLAN;
}
+ else if (config->geneve_decoding() and
+ (config->is_geneve_port(src_port) || config->is_geneve_port(dst_port)))
+ {
+ codec.next_prot_id = ProtocolId::GENEVE;
+ }
if (codec.next_prot_id != ProtocolId::FINISHED_DECODE)
codec.proto_bits |= PROTO_BIT__UDP_TUNNELED;
cd_teredo.cc
cd_user.cc
cd_vxlan.cc
+ cd_geneve.cc
)
else(STATIC_CODECS)
add_dynamic_module(cd_teredo codecs cd_teredo.cc)
add_dynamic_module(cd_user codecs cd_user.cc)
add_dynamic_module(cd_vxlan codecs cd_vxlan.cc)
+ add_dynamic_module(cd_geneve codecs cd_geneve.cc)
endif(STATIC_CODECS)
--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2021-2021 Cisco and/or its affiliates. All rights reserved.
+//
+// This program is free software; you can redistribute it and/or modify it
+// under the terms of the GNU General Public License Version 2 as published
+// by the Free Software Foundation. You may not use, modify or distribute
+// this program under any other version of the GNU General Public License.
+//
+// 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, write to the Free Software Foundation, Inc.,
+// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+//--------------------------------------------------------------------------
+// cd_geneve.cc author Raman S. Krishnan <ramanks@cisco.com>
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "codecs/codec_module.h"
+#include "framework/codec.h"
+#include "log/text_log.h"
+#include "main/snort_config.h"
+#include "packet_io/active.h"
+#include "protocols/geneve.h"
+
+using namespace snort;
+
+#define CD_GENEVE_NAME "geneve"
+#define CD_GENEVE_HELP "support for Geneve: Generic Network Virtualization Encapsulation"
+
+#define RFC_8926_GENEVE_VERSION 0
+#define GENEVE_ETH_TYPE 0x6558
+
+#define GENEVE_FLAG_O 0x80
+#define GENEVE_FLAG_C 0x40
+
+#define GENEVE_OPT_TYPE_C 0x80
+
+namespace
+{
+struct GeneveOpt
+{
+ uint16_t g_class;
+ uint8_t g_type;
+ uint8_t g_len;
+
+ uint16_t optclass() const
+ { return (ntohs(g_class)); }
+
+ bool is_set(uint16_t which) const
+ { return (g_type & which); }
+
+ uint8_t type() const
+ { return (g_type); }
+
+ uint8_t olen() const
+ { return (sizeof(GeneveOpt) + (g_len * 4)); }
+
+ uint8_t len() const
+ { return (g_len * 4); }
+};
+
+static const RuleMap geneve_rules[] =
+{
+ { DECODE_GENEVE_DGRAM_LT_GENEVE_HDR, "insufficient room for geneve header" },
+ { DECODE_GENEVE_INVALID_VERSION, "invalid version" },
+ { DECODE_GENEVE_INVALID_HEADER, "invalid header" },
+ { DECODE_GENEVE_INVALID_FLAGS, "invalid flags" },
+ { DECODE_GENEVE_INVALID_OPTIONS, "invalid options" },
+ { 0, nullptr }
+};
+
+class GeneveModule : public BaseCodecModule
+{
+public:
+ GeneveModule() : BaseCodecModule(CD_GENEVE_NAME, CD_GENEVE_HELP) { }
+
+ const RuleMap* get_rules() const override
+ { return geneve_rules; }
+};
+
+class GeneveCodec : public Codec
+{
+public:
+ GeneveCodec() : Codec(CD_GENEVE_NAME) { }
+
+ void get_protocol_ids(std::vector<ProtocolId>& v) override;
+ bool decode(const RawData&, CodecData&, DecodeData&) override;
+ void log(TextLog* const, const uint8_t* pkt, const uint16_t len) override;
+ bool encode(const uint8_t* const raw_in, const uint16_t raw_len,
+ EncState&, Buffer&, Flow*) override;
+
+private:
+
+ void log_opts(TextLog* const, const uint8_t*, const uint16_t len);
+ bool validate_options(const uint8_t* rptr, uint16_t optlen, CodecData& codec);
+};
+
+} // namespace
+
+void GeneveCodec::get_protocol_ids(std::vector<ProtocolId>& v)
+{
+ v.push_back(ProtocolId::GENEVE);
+}
+
+bool GeneveCodec::validate_options(const uint8_t* rptr, uint16_t hdrlen, CodecData& codec)
+{
+ const geneve::GeneveHdr* const hdr = reinterpret_cast<const geneve::GeneveHdr*>(rptr);
+ uint16_t offset = sizeof(geneve::GeneveHdr);
+ uint16_t copts = 0;
+
+ /* Skip past tunnel header */
+ rptr += sizeof(geneve::GeneveHdr);
+
+ while (offset < hdrlen)
+ {
+ const GeneveOpt* const opt = reinterpret_cast<const GeneveOpt*>(rptr);
+ uint8_t olen = opt->olen();
+
+ if ((offset + olen) > hdrlen)
+ {
+ codec_event(codec, DECODE_GENEVE_INVALID_OPTIONS);
+ return false;
+ }
+
+ if (opt->is_set(GENEVE_OPT_TYPE_C))
+ copts++;
+
+ rptr += olen;
+ offset += olen;
+ }
+
+ /*
+ * Generate event if
+ * C flag is clear but critical options are present
+ * C flag is set but critical options are absent
+ */
+ if ((!copts && hdr->is_set(GENEVE_FLAG_C)) || (copts && !hdr->is_set(GENEVE_FLAG_C)))
+ {
+ codec_event(codec, DECODE_GENEVE_INVALID_FLAGS);
+ return false;
+ }
+
+ return true;
+}
+
+bool GeneveCodec::decode(const RawData& raw, CodecData& codec, DecodeData&)
+{
+ if ( raw.len < sizeof(geneve::GeneveHdr) )
+ {
+ codec_event(codec, DECODE_GENEVE_DGRAM_LT_GENEVE_HDR);
+ return false;
+ }
+
+ const geneve::GeneveHdr* const hdr = reinterpret_cast<const geneve::GeneveHdr*>(raw.data);
+
+ if (hdr->version() != RFC_8926_GENEVE_VERSION)
+ {
+ codec_event(codec, DECODE_GENEVE_INVALID_VERSION);
+ return false;
+ }
+
+ const uint16_t optlen = hdr->optlen();
+ const uint32_t hdrlen = hdr->hlen();
+ if (raw.len < hdrlen)
+ {
+ codec_event(codec, DECODE_GENEVE_INVALID_HEADER);
+ return false;
+ }
+
+ /* If critical header present bit is set, optlen cannot be 0 */
+ if (hdr->is_set(GENEVE_FLAG_C) && (optlen == 0))
+ {
+ codec_event(codec, DECODE_GENEVE_INVALID_FLAGS);
+ return false;
+ }
+
+ if (!validate_options(raw.data, hdrlen, codec))
+ {
+ return false;
+ }
+
+ if ( codec.conf->tunnel_bypass_enabled(TUNNEL_GENEVE) )
+ codec.tunnel_bypass = true;
+
+ uint16_t next = hdr->proto();
+ ProtocolId proto = (next == GENEVE_ETH_TYPE) ? ProtocolId::ETHERNET_802_3 : (ProtocolId) next;
+
+ codec.lyr_len = hdrlen;
+ codec.proto_bits |= PROTO_BIT__GENEVE;
+ codec.next_prot_id = proto;
+ codec.codec_flags |= CODEC_NON_IP_TUNNEL;
+
+ return true;
+}
+
+void GeneveCodec::log_opts(TextLog* const text_log, const uint8_t *rptr, uint16_t optlen)
+{
+ uint16_t offset = 0;
+
+ while (offset < optlen)
+ {
+ const GeneveOpt* const opt = reinterpret_cast<const GeneveOpt*>(rptr);
+ uint8_t olen = opt->olen();
+
+ TextLog_Print(text_log, "\n\tclass 0x%04x, type 0x%02x%s, len %3u%s", opt->optclass(),
+ opt->type(), (opt->is_set(GENEVE_OPT_TYPE_C) ? " (C)" : ""), olen, (olen ? " value " : ""));
+
+ rptr += sizeof(GeneveOpt);
+
+ for (int idx=0; idx < opt->len(); idx++)
+ TextLog_Print(text_log, "%02x ", *rptr++);
+
+ offset += olen;
+ }
+}
+
+void GeneveCodec::log(TextLog* const text_log, const uint8_t* raw_pkt,
+ const uint16_t /*lyr_len*/)
+{
+ const uint8_t* rptr = raw_pkt;
+ const geneve::GeneveHdr* const hdr = reinterpret_cast<const geneve::GeneveHdr*>(rptr);
+ rptr += sizeof(geneve::GeneveHdr);
+
+ std::string flags = "";
+
+ if (hdr->is_set(GENEVE_FLAG_O))
+ flags += "O";
+
+ if (hdr->is_set(GENEVE_FLAG_C))
+ flags += "C";
+
+ if (flags == "")
+ flags = "none";
+
+ TextLog_Print(text_log, "version %u, optlen %u flags [%s]", hdr->version(), hdr->optlen(), flags.c_str());
+ TextLog_Print(text_log, " network id %u, next protocol: 0x%04x", hdr->vni(), hdr->proto());
+
+ log_opts(text_log, rptr, hdr->optlen());
+}
+
+bool GeneveCodec::encode(const uint8_t* const raw_in, const uint16_t raw_len,
+ EncState&, Buffer& buf, Flow*)
+{
+ if (!buf.allocate(raw_len))
+ return false;
+
+ geneve::GeneveHdr* const hdr = reinterpret_cast<geneve::GeneveHdr*>(buf.data());
+ memcpy(hdr, raw_in, raw_len);
+ return true;
+}
+
+//-------------------------------------------------------------------------
+// api
+//-------------------------------------------------------------------------
+
+static Module* mod_ctor()
+{ return new GeneveModule; }
+
+static void mod_dtor(Module* m)
+{ delete m; }
+
+static Codec* ctor(Module*)
+{ return new GeneveCodec(); }
+
+static void dtor(Codec* cd)
+{ delete cd; }
+
+static const CodecApi geneve_api =
+{
+ {
+ PT_CODEC,
+ sizeof(CodecApi),
+ CDAPI_VERSION,
+ 0,
+ API_RESERVED,
+ API_OPTIONS,
+ CD_GENEVE_NAME,
+ CD_GENEVE_HELP,
+ mod_ctor,
+ mod_dtor
+ },
+ nullptr, // pinit
+ nullptr, // pterm
+ nullptr, // tinit
+ nullptr, // tterm
+ ctor, // ctor
+ dtor, // dtor
+};
+
+#ifdef BUILDING_SO
+SO_PUBLIC const BaseApi* snort_plugins[] =
+#else
+const BaseApi* cd_geneve[] =
+#endif
+{
+ &geneve_api.base,
+ nullptr
+};
+
#define PROTO_BIT__VXLAN 0x040000
#define PROTO_BIT__UDP_TUNNELED 0x080000
#define PROTO_BIT__OTHER 0x100000
-#define PROTO_BIT__ALL 0x1FFFFF
+#define PROTO_BIT__GENEVE 0x200000
+#define PROTO_BIT__ALL 0x3FFFFF
#define PROTO_BIT__ICMP_EMBED \
(PROTO_BIT__TCP_EMBED_ICMP | PROTO_BIT__UDP_EMBED_ICMP | \
#include "protocols/tcp.h"
#include "protocols/udp.h"
#include "protocols/vlan.h"
+#include "protocols/geneve.h"
#include "utils/stats.h"
using namespace snort;
TextLog_Print(csv_log, "%ld", a.pkt->flow->flowstats.start_time.tv_sec);
}
+static void ff_geneve_vni(const Args& a)
+{
+ if (a.pkt->proto_bits & PROTO_BIT__GENEVE)
+ TextLog_Print(csv_log, "%u", a.pkt->get_flow_geneve_vni());
+}
+
static void ff_gid(const Args& a)
{
TextLog_Print(csv_log, "%u", a.event.sig_info->gid);
{
ff_action, ff_class, ff_b64_data, ff_client_bytes, ff_client_pkts, ff_dir,
ff_dst_addr, ff_dst_ap, ff_dst_port, ff_eth_dst, ff_eth_len, ff_eth_src,
- ff_eth_type, ff_flowstart_time, ff_gid, ff_icmp_code, ff_icmp_id, ff_icmp_seq,
+ ff_eth_type, ff_flowstart_time, ff_geneve_vni, ff_gid, ff_icmp_code, ff_icmp_id, ff_icmp_seq,
ff_icmp_type, ff_iface, ff_ip_id, ff_ip_len, ff_msg, ff_mpls, ff_pkt_gen, ff_pkt_len,
ff_pkt_num, ff_priority, ff_proto, ff_rev, ff_rule, ff_seconds, ff_server_bytes,
ff_server_pkts, ff_service, ff_sgt, ff_sid, ff_src_addr, ff_src_ap, ff_src_port,
#define csv_range \
"action | class | b64_data | client_bytes | client_pkts | dir | " \
"dst_addr | dst_ap | dst_port | eth_dst | eth_len | eth_src | " \
- "eth_type | flowstart_time | gid | icmp_code | icmp_id | icmp_seq | " \
+ "eth_type | flowstart_time | geneve_vni | gid | icmp_code | icmp_id | icmp_seq | " \
"icmp_type | iface | ip_id | ip_len | msg | mpls | pkt_gen | pkt_len | " \
"pkt_num | priority | proto | rev | rule | seconds | server_bytes | " \
"server_pkts | service | sgt| sid | src_addr | src_ap | src_port | " \
return false;
}
+static bool ff_geneve_vni(const Args& a)
+{
+ if (a.pkt->proto_bits & PROTO_BIT__GENEVE)
+ {
+ print_label(a, "geneve_vni");
+ TextLog_Print(json_log, "%u", a.pkt->get_flow_geneve_vni());
+ }
+ return true;
+}
+
static bool ff_gid(const Args& a)
{
print_label(a, "gid");
{
ff_action, ff_class, ff_b64_data, ff_client_bytes, ff_client_pkts, ff_dir,
ff_dst_addr, ff_dst_ap, ff_dst_port, ff_eth_dst, ff_eth_len, ff_eth_src,
- ff_eth_type, ff_flowstart_time, ff_gid, ff_icmp_code, ff_icmp_id, ff_icmp_seq,
+ ff_eth_type, ff_flowstart_time, ff_geneve_vni, ff_gid, ff_icmp_code, ff_icmp_id, ff_icmp_seq,
ff_icmp_type, ff_iface, ff_ip_id, ff_ip_len, ff_msg, ff_mpls, ff_pkt_gen, ff_pkt_len,
ff_pkt_num, ff_priority, ff_proto, ff_rev, ff_rule, ff_seconds, ff_server_bytes,
ff_server_pkts, ff_service, ff_sgt, ff_sid, ff_src_addr, ff_src_ap, ff_src_port,
#define json_range \
"action | class | b64_data | client_bytes | client_pkts | dir | " \
"dst_addr | dst_ap | dst_port | eth_dst | eth_len | eth_src | " \
- "eth_type | flowstart_time | gid | icmp_code | icmp_id | icmp_seq | " \
+ "eth_type | flowstart_time | geneve_vni | gid | icmp_code | icmp_id | icmp_seq | " \
"icmp_type | iface | ip_id | ip_len | msg | mpls | pkt_gen | pkt_len | " \
"pkt_num | priority | proto | rev | rule | seconds | server_bytes | " \
"server_pkts | service | sgt| sid | src_addr | src_ap | src_port | " \
else if (!strcasecmp(tok, "mpls"))
tunnel_mask |= TUNNEL_MPLS;
+ else if (!strcasecmp(tok, "geneve"))
+ tunnel_mask |= TUNNEL_GENEVE;
+
else
{
ParseError("unknown tunnel bypass protocol");
TUNNEL_6IN6 = 0x20,
TUNNEL_GRE = 0x40,
TUNNEL_MPLS = 0x80,
- TUNNEL_VXLAN = 0x100
+ TUNNEL_VXLAN = 0x100,
+ TUNNEL_GENEVE = 0x200
};
enum DumpConfigType
ipv4.h
ipv4_options.h
ipv6.h
+ geneve.h
gre.h
layer.h
linux_sll.h
--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2021-2021 Cisco and/or its affiliates. All rights reserved.
+//
+// This program is free software; you can redistribute it and/or modify it
+// under the terms of the GNU General Public License Version 2 as published
+// by the Free Software Foundation. You may not use, modify or distribute
+// this program under any other version of the GNU General Public License.
+//
+// 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, write to the Free Software Foundation, Inc.,
+// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+//--------------------------------------------------------------------------
+// geneve.h author Raman S. Krishnan <ramanks@cisco.com>
+
+#ifndef PROTOCOLS_GENEVE_H
+#define PROTOCOLS_GENEVE_H
+
+namespace snort
+{
+namespace geneve
+{
+struct GeneveHdr
+{
+ uint8_t g_vl;
+ uint8_t g_flags;
+ uint16_t g_proto;
+ uint8_t g_vni[ 3 ];
+ uint8_t g_rsvd;
+
+ uint16_t hlen() const
+ { return (sizeof(GeneveHdr) + ((g_vl & 0x3f) * 4)); }
+
+ uint8_t version() const
+ { return (g_vl >> 6); }
+
+ uint8_t optlen() const
+ { return ((g_vl & 0x3f) * 4); }
+
+ bool is_set(uint16_t which) const
+ { return (g_flags & which); }
+
+ uint16_t proto() const
+ { return (ntohs(g_proto)); }
+
+ uint32_t vni() const
+ { return ((g_vni[0] << 16) | (g_vni[1] << 8) | g_vni[2]); }
+};
+
+} // namespace geneve
+} // namespace snort
+
+#endif
ProtocolId::ETHERTYPE_REVARP));
}
+const geneve::GeneveHdr* get_geneve_layer(const Packet* const p)
+{
+ uint8_t num_layers = p->num_layers;
+ const Layer* lyr = p->layers;
+
+ return reinterpret_cast<const geneve::GeneveHdr*>(
+ find_inner_layer(lyr, num_layers, ProtocolId::GENEVE));
+}
+
const gre::GREHdr* get_gre_layer(const Packet* const p)
{
uint8_t num_layers = p->num_layers;
struct EtherHdr;
}
+namespace geneve
+{
+struct GeneveHdr;
+}
+
namespace gre
{
struct GREHdr;
SO_PUBLIC const cisco_meta_data::CiscoMetaDataHdr* get_cisco_meta_data_layer(const Packet* const);
SO_PUBLIC const eapol::EtherEapol* get_eapol_layer(const Packet*);
SO_PUBLIC const eth::EtherHdr* get_eth_layer(const Packet*);
+SO_PUBLIC const geneve::GeneveHdr* get_geneve_layer(const Packet*);
SO_PUBLIC const gre::GREHdr* get_gre_layer(const Packet*);
SO_PUBLIC const vlan::VlanTagHdr* get_vlan_layer(const Packet*);
SO_PUBLIC const wlan::WifiHdr* get_wifi_layer(const Packet*);
#include "packet_manager.h"
#include "vlan.h"
+#include "geneve.h"
namespace snort
{
return vid;
}
+uint32_t Packet::get_flow_geneve_vni() const
+{
+ uint32_t vni = 0;
+
+ if (proto_bits & PROTO_BIT__GENEVE)
+ vni = layer::get_geneve_layer(this)->vni();
+
+ return vni;
+}
+
bool Packet::is_from_application_client() const
{
if (flow)
}
uint16_t get_flow_vlan_id() const;
+ uint32_t get_flow_geneve_vni() const;
int16_t get_ingress_group() const
{
ETHERNET_802_11 = 0x0106,
ETHERNET_LLC = 0x0107,
VXLAN = 0x0108,
+ GENEVE = 0x0109,
/*
* Below is a partial list of ethertypes.