${PLUGIN_SOURCES}
)
endif(STATIC_CODECS)
+
+add_subdirectory(test)
#include "codecs/codec_module.h"
#include "framework/codec.h"
+#include "log/messages.h"
+#include "protocols/cisco_meta_data.h"
+#include "protocols/layer.h"
using namespace snort;
bool decode(const RawData&, CodecData&, DecodeData&) override;
};
-struct CiscoMetaDataHdr
-{
- uint8_t version; // This must be 1
- uint8_t length; //This is the header size in bytes / 8
-};
-
-struct CiscoMetaDataOpt
-{
- uint16_t opt_len_type; // 3-bit length + 13-bit type. Length of 0 = 4. Type must be 1.
- uint16_t sgt; // Can be any value except 0xFFFF
-};
-
constexpr uint8_t CISCO_META_OPT_LEN_SHIFT = 4;
constexpr uint16_t CISCO_META_OPT_TYPE_SGT = 1;
constexpr uint16_t CISCO_META_OPT_TYPE_MASK = 0x1FFF; //mask opt_len_type to get option type
+// Currently we only support one CiscoMetaDataHdr which is size of 1 byte.
+constexpr uint8_t SUPPORTED_HDR_LEN = 1;
} // namespace
void CiscoMetaDataCodec::get_protocol_ids(std::vector<ProtocolId>& v)
bool CiscoMetaDataCodec::decode(const RawData& raw, CodecData& codec, DecodeData&)
{
- if (raw.len < sizeof(CiscoMetaDataHdr))
+ uint16_t len;
+ uint16_t type;
+
+ /* 2 octets for ethertype + 2 octets for CiscoMetaDataHdr
+ * + 4 octets for CiscoMetaDataOpt
+ */
+ uint32_t total_len = (sizeof(uint16_t)
+ + sizeof(cisco_meta_data::CiscoMetaDataHdr)
+ + sizeof(cisco_meta_data::CiscoMetaDataOpt));
+
+ if (raw.len < total_len)
{
codec_event(codec, DECODE_CISCO_META_HDR_TRUNC);
return false;
}
- const CiscoMetaDataHdr* const cmdh =
- reinterpret_cast<const CiscoMetaDataHdr*>(raw.data);
- uint32_t cmdh_rem_len = cmdh->length << 3;
+ const cisco_meta_data::CiscoMetaDataHdr* const cmdh =
+ reinterpret_cast<const cisco_meta_data::CiscoMetaDataHdr*>(raw.data);
- if ( (raw.len < cmdh_rem_len) || (cmdh_rem_len == 0) )
+ if (SUPPORTED_HDR_LEN != cmdh->length)
{
codec_event(codec, DECODE_CISCO_META_HDR_TRUNC);
return false;
}
- const CiscoMetaDataOpt* cmd_options =
- reinterpret_cast<const CiscoMetaDataOpt*>(raw.data + sizeof(CiscoMetaDataHdr));
- // validate options, lengths, and SGTs
- cmdh_rem_len -= sizeof(CiscoMetaDataHdr) + sizeof(uint16_t); //2 octets for ethertype
- if(cmdh_rem_len == 0)
- cmd_options = nullptr;
+ const cisco_meta_data::CiscoMetaDataOpt* cmd_option =
+ reinterpret_cast<const cisco_meta_data::CiscoMetaDataOpt*>(raw.data
+ + sizeof(cisco_meta_data::CiscoMetaDataHdr));
+
+ // Top 3 bits (length) must be equal to 0
+ // Bottom 13 bits (type) must be 1 to indicate SGT
+ len = ntohs(cmd_option->opt_len_type) >> CISCO_META_OPT_LEN_SHIFT;
+ type = ntohs(cmd_option->opt_len_type) & CISCO_META_OPT_TYPE_MASK;
- for(int i = 0; cmdh_rem_len > 0; i++)
+ // 0 indicates 4 octets which is sizeof(CiscoMetaDataOpt)
+ if (len != 0)
{
- // Top 3 bits (length) must be equal to 0 or 4
- // Bottom 13 bits (type) must be 1 to indicate SGT
- const CiscoMetaDataOpt* opt = &cmd_options[i];
- uint16_t len = ntohs(opt->opt_len_type) >> CISCO_META_OPT_LEN_SHIFT;
- uint16_t type = ntohs(opt->opt_len_type) & CISCO_META_OPT_TYPE_MASK;
-
- // 0 indicates 4 octets
- if(len != 0 && len != 4)
- {
- codec_event(codec, DECODE_CISCO_META_HDR_OPT_LEN);
- return false;
- }
-
- if(type != CISCO_META_OPT_TYPE_SGT)
- {
- codec_event(codec, DECODE_CISCO_META_HDR_OPT_TYPE);
- return false;
- }
-
- /* Tag value 0xFFFF is invalid */
- if(opt->sgt == 0xFFFF)
- {
- codec_event(codec, DECODE_CISCO_META_HDR_SGT);
- return false;
- }
- cmdh_rem_len -= sizeof(CiscoMetaDataOpt);
+ codec_event(codec, DECODE_CISCO_META_HDR_OPT_LEN);
+ return false;
}
- codec.lyr_len = cmdh->length << 3;
+ if (type != CISCO_META_OPT_TYPE_SGT)
+ {
+ codec_event(codec, DECODE_CISCO_META_HDR_OPT_TYPE);
+ return false;
+ }
+
+ /* Tag value 0xFFFF is invalid */
+ if (cmd_option->sgt == 0xFFFF)
+ {
+ codec_event(codec, DECODE_CISCO_META_HDR_SGT);
+ return false;
+ }
+
+#ifdef REG_TEST
+ LogMessage("Value of sgt %d\n", ntohs(cmd_option->sgt));
+#endif
+
+ codec.lyr_len = total_len;
//The last 2 octets of the header will be the real ethtype
codec.next_prot_id = static_cast<ProtocolId>
(ntohs(*((const uint16_t*)(raw.data + codec.lyr_len - sizeof(uint16_t)))));
codec.codec_flags |= CODEC_ETHER_NEXT;
+ codec.proto_bits |= PROTO_BIT__CISCO_META_DATA;
+
return true;
}
--- /dev/null
+add_cpputest( cisco_meta_data_test
+ SOURCES
+ ../../../protocols/layer.cc
+)
--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2019-2019 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.
+//--------------------------------------------------------------------------
+
+// cisco_meta_data_test.cc author Sunirmal Mukherjee <sunimukh@cisco.com>
+// unit test main
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+
+#include "protocols/cisco_meta_data.h"
+#include "protocols/ip.h"
+#include "protocols/layer.h"
+#include "protocols/packet.h"
+
+#include <CppUTest/CommandLineTestRunner.h>
+#include <CppUTest/TestHarness.h>
+
+using namespace snort;
+
+//-------------------------------------------------------------------------
+// tests
+//-------------------------------------------------------------------------
+
+Packet::Packet(bool) { }
+Packet::~Packet() = default;
+
+namespace snort
+{
+void ip::IpApi::set(ip::IP4Hdr const* iph)
+{
+ return;
+}
+
+void ip::IpApi::set(ip::IP6Hdr const* iph)
+{
+ return;
+}
+
+void ip::IpApi::reset()
+{
+ return;
+}
+
+const uint8_t* ip::IpApi::ip_data() const
+{
+ return nullptr;
+}
+}
+
+static inline void push_layer(Packet* p,
+ ProtocolId prot_id,
+ const uint8_t* hdr_start,
+ uint32_t len)
+{
+ Layer& lyr = p->layers[p->num_layers++];
+ lyr.prot_id = prot_id;
+ lyr.start = hdr_start;
+ lyr.length = (uint16_t)len;
+}
+
+TEST_GROUP(cisco_meta_data_test)
+{
+};
+
+TEST(cisco_meta_data_test, cisco_meta_data_class_test)
+{
+ Packet pkt(false);
+ Layer layers;
+ uint8_t * cmd_data;
+ int len;
+ int sgt;
+
+ pkt.num_layers = 0;
+ pkt.layers = &layers;
+
+ cmd_data = new uint8_t[sizeof(cisco_meta_data::CiscoMetaDataHdr) + sizeof(cisco_meta_data::CiscoMetaDataOpt)];
+
+ cisco_meta_data::CiscoMetaDataOpt* cmd_options =
+ reinterpret_cast<cisco_meta_data::CiscoMetaDataOpt*>(cmd_data + sizeof(cisco_meta_data::CiscoMetaDataHdr));
+ cmd_options->sgt = 512;
+
+ len = (sizeof(cisco_meta_data::CiscoMetaDataHdr) + sizeof(cisco_meta_data::CiscoMetaDataOpt));
+ pkt.proto_bits |= PROTO_BIT__CISCO_META_DATA;
+ push_layer(&pkt, ProtocolId::ETHERTYPE_CISCO_META, cmd_data, len);
+ sgt = htons(layer::get_cisco_meta_data_layer(&pkt)->sgt_val());
+ CHECK(sgt == cmd_options->sgt);
+
+ delete[] cmd_data;
+}
+
+int main(int argc, char** argv)
+{
+ return CommandLineTestRunner::RunAllTests(argc, argv);
+}
+
--- /dev/null
+This is the test case to parse SGT(Security Group Tag/Trustsec) from CiscoMetaData.
+
+CiscoMetaData - A proprietary header in ethernet layer, called CiscoMetaDataHdr.
+ After CiscoMetaDataHdr, packet contains CiscoMetaDataOpt which carries
+ SGT data.
+
+Here created a dummy packet which has CiscoMetaDataHdr integrated and pushing it to layer stack.
+To validate the sgt data, parsing it from layer and comparing with the expected.
#define PROTO_BIT__ICMP_EMBED_ICMP 0x04000
#define PROTO_BIT__ICMP_EMBED_OTHER 0x08000
#define PROTO_BIT__IP6_EXT 0x10000
-#define PROTO_BIT__OTHER 0x20000
-#define PROTO_BIT__ALL 0x3FFFF
+#define PROTO_BIT__CISCO_META_DATA 0x20000
+#define PROTO_BIT__OTHER 0x40000
+#define PROTO_BIT__ALL 0x7FFFF
#define PROTO_BIT__ICMP_EMBED \
(PROTO_BIT__TCP_EMBED_ICMP | PROTO_BIT__UDP_EMBED_ICMP | \
set (PROTOCOL_HEADERS
arp.h
+ cisco_meta_data.h
eapol.h
eth.h
icmp4.h
--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2020-2020 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.
+//--------------------------------------------------------------------------
+// cisco_meta_data.h author Sunirmal Mukherjee <sunimukh@cisco.com>
+
+#ifndef PROTOCOLS_CISCO_META_DATA_H
+#define PROTOCOLS_CISCO_META_DATA_H
+
+#include <arpa/inet.h>
+
+namespace snort
+{
+namespace cisco_meta_data
+{
+struct CiscoMetaDataHdr
+{
+ uint8_t version; // This must be 1
+ uint8_t length; //This is the header size in bytes / 8
+};
+
+struct CiscoMetaDataOpt
+{
+ uint16_t opt_len_type; // 3-bit length + 13-bit type. Type must be 1
+ uint16_t sgt; // Can be any value except 0xFFFF
+
+ inline uint16_t sgt_val() const
+ { return ntohs(sgt); }
+};
+}
+}
+
+#endif
return reinterpret_cast<const vlan::VlanTagHdr*>(lyr->start);
}
+const cisco_meta_data::CiscoMetaDataOpt* get_cisco_meta_data_layer(const Packet* const p)
+{
+ assert( p->proto_bits & PROTO_BIT__CISCO_META_DATA );
+ uint8_t num_layers = p->num_layers;
+ const Layer* lyr = p->layers;
+ const Layer* cmd_lyr;
+
+ cmd_lyr = find_layer(lyr, num_layers, ProtocolId::ETHERTYPE_CISCO_META,
+ ProtocolId::ETHERTYPE_CISCO_META);
+
+ return reinterpret_cast<const snort::cisco_meta_data::CiscoMetaDataOpt*>(cmd_lyr->start
+ + sizeof(snort::cisco_meta_data::CiscoMetaDataHdr));
+}
+
const eth::EtherHdr* get_eth_layer(const Packet* const p)
{
uint8_t num_layers = p->num_layers;
#define PROTOCOLS_LAYER_H
#include "main/snort_types.h"
+#include "protocols/cisco_meta_data.h"
#include "protocols/protocol_ids.h"
namespace snort
SO_PUBLIC bool set_outer_ip_api(const Packet* const, ip::IpApi&, int8_t& curr_layer);
SO_PUBLIC bool set_outer_ip_api(const Packet* const, ip::IpApi&,
IpProtocol& next_ip_proto, int8_t& curr_layer);
+
+/* This is the api to get CiscoMetaData layer to fetch SGT value from it */
+SO_PUBLIC const cisco_meta_data::CiscoMetaDataOpt* get_cisco_meta_data_layer(const Packet* const p);
} // namespace layer
} // namespace snort
#endif