From: Michael Altizer (mialtize) Date: Tue, 9 Mar 2021 03:01:53 +0000 (+0000) Subject: Merge pull request #2729 in SNORT/snort3 from ~MIALTIZE/snort3:compound_codec to... X-Git-Tag: 3.1.2.0~8 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=440046e47b731bfc734dde2c63420ff85d9025b5;p=thirdparty%2Fsnort3.git Merge pull request #2729 in SNORT/snort3 from ~MIALTIZE/snort3:compound_codec to master Squashed commit of the following: commit d38e1757de753e33fbd7eb86fdd47e7005367ba4 Author: Michael Altizer Date: Mon Mar 8 17:32:42 2021 -0500 snort_config: Clean up and annotate command line config merge process commit 7ddcab755604935be48973c78b17ca70a1dc3eb4 Author: Michael Altizer Date: Tue Mar 2 15:30:14 2021 -0500 protocols: Add peg count for decodes that exceeded the max layers Also, make sure that the alert for doing so only triggers once per packet being decoded. commit 4dbd0f9718ee3160864c760632dc8e4611101899 Author: Michael Altizer Date: Tue Feb 2 18:25:29 2021 -0500 protocols: Add initial support for multilayer compound codecs commit 6903a09c81e02f8dce04becc393edc26c1ce3b48 Author: Michael Altizer Date: Mon Feb 1 12:29:19 2021 -0500 protocols: Consistently encapsulate exported protocol headers in the snort namespace commit e4f056d9fb416c0aaab573f6fa8d81c8f58367d1 Author: Michael Altizer Date: Wed Jan 27 13:24:22 2021 -0500 log: Base logging the Ethernet header on proto bits rather than DLT commit d80dc65860f76d1f28e8c93dc832d66d65169e3e Author: Michael Altizer Date: Mon Jan 11 20:43:46 2021 -0500 main: Fix accumulating and printing codec stats at run time --- diff --git a/src/codecs/link/cd_mpls.cc b/src/codecs/link/cd_mpls.cc index 22a0c97ba..45fd02e1d 100644 --- a/src/codecs/link/cd_mpls.cc +++ b/src/codecs/link/cd_mpls.cc @@ -38,7 +38,7 @@ namespace { enum MplsPayloadType : uint8_t { - // Entries must align with mpls_payload_type enum parameter in mpls_params + // Entries must align with payload_type enum parameter in mpls_params MPLS_PAYLOADTYPE_AUTODETECT = 0, MPLS_PAYLOADTYPE_ETHERNET, MPLS_PAYLOADTYPE_IPV4, diff --git a/src/framework/codec.h b/src/framework/codec.h index 75a72c8d2..c3481561d 100644 --- a/src/framework/codec.h +++ b/src/framework/codec.h @@ -28,6 +28,7 @@ #include "framework/base_api.h" #include "framework/decode_data.h" +#include "protocols/layer.h" #include "utils/cpp_macros.h" struct TextLog; @@ -55,7 +56,6 @@ struct ICMPHdr; } class Flow; -struct Layer; // Used by root codecs to add their DLT to their HELP string #define ADD_DLT(help, x) help " (DLT " STRINGIFY_MX(x) ")" @@ -112,26 +112,36 @@ constexpr uint16_t CODEC_IP6_EXT_OOO = 0x0400; constexpr uint16_t CODEC_IP6_BAD_OPT = 0x0800; constexpr uint16_t CODEC_ETHER_NEXT = 0x1000; +constexpr uint16_t CODEC_COMPOUND = 0x2000; +constexpr uint16_t CODEC_LAYERS_EXCEEDED = 0x400; constexpr uint16_t CODEC_IPOPT_FLAGS = (CODEC_IPOPT_RR_SEEN | CODEC_IPOPT_RTRALT_SEEN | CODEC_IPOPT_LEN_THREE); struct SnortConfig; +constexpr uint8_t COMPOUND_LAYERS_MAX = 8; + +struct CompoundLayer +{ + Layer layer; + uint32_t proto_bits; +}; + struct CodecData { const SnortConfig* conf; /* This section will get reset before every decode() function call */ - ProtocolId next_prot_id; /* protocol type of the next layer */ - uint16_t lyr_len = 0; /* The length of the valid part layer */ - uint16_t invalid_bytes = 0; /* the length of the INVALID part of this layer */ + ProtocolId next_prot_id; /* protocol type of the next layer */ + uint16_t lyr_len = 0; /* length of the valid portion of this layer */ + uint16_t invalid_bytes = 0; /* length of the INVALID part of this layer */ + uint32_t proto_bits = 0; /* protocols contained within this packet + -- will be propogated to Snort++ Packet struct*/ /* Reset before each decode of packet begins */ /* Codec specific fields. These fields are only relevant to codecs. */ - uint32_t proto_bits = 0; /* protocols contained within this packet - -- will be propogated to Snort++ Packet struct*/ uint16_t codec_flags = 0; /* flags used while decoding */ uint8_t ip_layer_cnt = 0; @@ -140,6 +150,9 @@ struct CodecData IpProtocol ip6_csum_proto = IpProtocol::IP; /* Used for IPv6 checksums */ bool tunnel_bypass = false; + CompoundLayer compound_layers[COMPOUND_LAYERS_MAX]{}; + uint8_t compound_layer_cnt = 0; + CodecData(const SnortConfig* sc, ProtocolId init_prot) : conf(sc), next_prot_id(init_prot) { } diff --git a/src/log/log_text.cc b/src/log/log_text.cc index 9ff4c4ac4..abc64c500 100644 --- a/src/log/log_text.cc +++ b/src/log/log_text.cc @@ -150,7 +150,7 @@ void Log2ndHeader(TextLog* log, Packet* p) if ( !p or !p->num_layers ) return; - if ( SFDAQ::get_base_protocol() == DLT_EN10MB ) + if ( p->is_eth() ) LogEthHeader(log, p); else if ( SnortConfig::log_verbose() ) diff --git a/src/main/analyzer_command.cc b/src/main/analyzer_command.cc index 1afa76eb8..4f5fa5b07 100644 --- a/src/main/analyzer_command.cc +++ b/src/main/analyzer_command.cc @@ -28,6 +28,7 @@ #include "framework/module.h" #include "log/messages.h" #include "managers/module_manager.h" +#include "protocols/packet_manager.h" #include "target_based/host_attributes.h" #include "utils/stats.h" @@ -77,10 +78,10 @@ bool ACRotate::execute(Analyzer& analyzer, void**) bool ACGetStats::execute(Analyzer&, void**) { - // FIXIT-P This incurs locking on all threads to retrieve stats. It // could be reimplemented to optimize for large thread counts by // retrieving stats in the command and accumulating in the main thread. + PacketManager::accumulate(); ModuleManager::accumulate(); return true; } diff --git a/src/main/snort.cc b/src/main/snort.cc index bd3b901e9..71fa28bdb 100644 --- a/src/main/snort.cc +++ b/src/main/snort.cc @@ -152,10 +152,7 @@ void Snort::init(int argc, char** argv) parser_init(); SnortConfig* sc = ParseSnortConf(snort_cmd_line_conf); - /* Merge the command line and config file confs to take care of - * command line overriding config file. - * Set the global snort_conf that will be used during run time */ - sc->merge(snort_cmd_line_conf); + /* Set the global snort_conf that will be used during run time */ SnortConfig::set_conf(sc); // This call must be immediately after "SnortConfig::set_conf(sc)" @@ -460,7 +457,6 @@ SnortConfig* Snort::get_reload_config(const char* fname, const char* plugin_path parser_init(); SnortConfig* sc = ParseSnortConf(snort_cmd_line_conf, fname, false); - sc->merge(snort_cmd_line_conf); if ( get_parse_errors() || ModuleManager::get_errors() || !sc->verify() ) { diff --git a/src/main/snort_config.cc b/src/main/snort_config.cc index 3b2fbfb05..d0e6a76d2 100644 --- a/src/main/snort_config.cc +++ b/src/main/snort_config.cc @@ -336,113 +336,120 @@ void SnortConfig::clone(const SnortConfig* const conf) } // merge in everything from the command line config -void SnortConfig::merge(SnortConfig* cmd_line) +void SnortConfig::merge(const SnortConfig* cmd_line_conf) { - if ( !cmd_line->log_dir.empty() ) - log_dir = cmd_line->log_dir; + // -D / -H / -Q / -r / -T / -x / --alert-before-pass / --create-pidfile / --enable-inline-test / --mem-check / + // --nolock-pidfile / --pause / --pcap-file / --pcap-dir / --pcap-list / --pcap-show / --pedantic / --piglet / + // --shell / --show-file-codes + run_flags |= cmd_line_conf->run_flags; - if ( log_dir.empty() ) - log_dir = DEFAULT_LOG_DIR; - - run_prefix = cmd_line->run_prefix; - id_offset = cmd_line->id_offset; - id_subdir = cmd_line->id_subdir; - id_zero = cmd_line->id_zero; - - /* Used because of a potential chroot */ - orig_log_dir = log_dir; - event_log_id = cmd_line->event_log_id; + // -A / -C / -d / -e / -f / -O / -U / -X / -y / --nostamps + output_flags |= cmd_line_conf->output_flags; - run_flags |= cmd_line->run_flags; - output_flags |= cmd_line->output_flags; + // -B + if (cmd_line_conf->obfuscation_net.get_family() != 0) + memcpy(&obfuscation_net, &cmd_line_conf->obfuscation_net, sizeof(obfuscation_net)); - include_path = cmd_line->include_path; - stdin_rules = cmd_line->stdin_rules; + // -g + if (cmd_line_conf->group_id != -1) + group_id = cmd_line_conf->group_id; - // only set by cmd_line to override other conf output settings - output = cmd_line->output; + // -G / --logid + event_log_id = cmd_line_conf->event_log_id; - /* Merge checksum flags. If command line modified them, use from the - * command line, else just use from config_file. */ + // -i / -s / --daq / --daq-batch-size / --daq-dir / --daq-list / --daq-mode / --daq-var / --snaplen + daq_config->overlay(cmd_line_conf->daq_config); - int cl_chk = cmd_line->policy_map->get_network_policy()->checksum_eval; - int cl_drop = cmd_line->policy_map->get_network_policy()->checksum_drop; - - NetworkPolicy* nw_policy = nullptr; - - for ( unsigned idx = 0; idx < policy_map->network_policy_count(); ++idx ) + // -k (only configures eval, not drop) + int cl_chk = cmd_line_conf->policy_map->get_network_policy()->checksum_eval; + if (!(cl_chk & CHECKSUM_FLAG__DEF)) { - nw_policy = policy_map->get_network_policy(idx); - - if ( !(cl_chk & CHECKSUM_FLAG__DEF) ) - nw_policy->checksum_eval = cl_chk; - - if ( !(cl_drop & CHECKSUM_FLAG__DEF) ) - nw_policy->checksum_drop = cl_drop; + for (unsigned idx = 0; idx < policy_map->network_policy_count(); ++idx) + { + NetworkPolicy* nw_policy = policy_map->get_network_policy(idx); + if (!(cl_chk & CHECKSUM_FLAG__DEF)) + nw_policy->checksum_eval = cl_chk; + } } - /* FIXIT-L do these belong in network policy? */ - if (cmd_line->num_layers != 0) - num_layers = cmd_line->num_layers; - - if (cmd_line->max_ip6_extensions != 0) - max_ip6_extensions = cmd_line->max_ip6_extensions; - - if (cmd_line->max_ip_layers != 0) - max_ip_layers = cmd_line->max_ip_layers; + // -l + if ( !cmd_line_conf->log_dir.empty() ) + log_dir = cmd_line_conf->log_dir; - if (cmd_line->obfuscation_net.get_family() != 0) - memcpy(&obfuscation_net, &cmd_line->obfuscation_net, sizeof(obfuscation_net)); + // -L (output is only set by cmd_line_conf to override other conf output settings) + output = cmd_line_conf->output; - if (cmd_line->homenet.get_family() != 0) - memcpy(&homenet, &cmd_line->homenet, sizeof(homenet)); + // -m + if (cmd_line_conf->file_mask != 0) + file_mask = cmd_line_conf->file_mask; - if ( !cmd_line->bpf_file.empty() ) - bpf_file = cmd_line->bpf_file; + // -n + if (cmd_line_conf->pkt_cnt != 0) + pkt_cnt = cmd_line_conf->pkt_cnt; - if ( !cmd_line->bpf_filter.empty() ) - bpf_filter = cmd_line->bpf_filter; + // -t + if (!cmd_line_conf->chroot_dir.empty()) + chroot_dir = cmd_line_conf->chroot_dir; - if (cmd_line->pkt_cnt != 0) - pkt_cnt = cmd_line->pkt_cnt; + // -u + if (cmd_line_conf->user_id != -1) + user_id = cmd_line_conf->user_id; - if (cmd_line->pkt_skip != 0) - pkt_skip = cmd_line->pkt_skip; + // --bpf + if (!cmd_line_conf->bpf_filter.empty()) + bpf_filter = cmd_line_conf->bpf_filter; - if (cmd_line->pkt_pause_cnt != 0) - pkt_pause_cnt = cmd_line->pkt_pause_cnt; + // --dirty-pig + if (cmd_line_conf->dirty_pig) + dirty_pig = cmd_line_conf->dirty_pig; - if (cmd_line->group_id != -1) - group_id = cmd_line->group_id; + // --id-offset + id_offset = cmd_line_conf->id_offset; + // --id-subdir + id_subdir = cmd_line_conf->id_subdir; + // --id-zero + id_zero = cmd_line_conf->id_zero; - if (cmd_line->user_id != -1) - user_id = cmd_line->user_id; + // --include-path + include_path = cmd_line_conf->include_path; - /* Only configurable on command line */ - if (cmd_line->file_mask != 0) - file_mask = cmd_line->file_mask; + // --metadata-filter + if (!cmd_line_conf->metadata_filter.empty()) + metadata_filter = cmd_line_conf->metadata_filter; - if ( !cmd_line->chroot_dir.empty() ) - chroot_dir = cmd_line->chroot_dir; + // --pause-after-n + if (cmd_line_conf->pkt_pause_cnt != 0) + pkt_pause_cnt = cmd_line_conf->pkt_pause_cnt; - if ( cmd_line->dirty_pig ) - dirty_pig = cmd_line->dirty_pig; + // --process-all-events + if (cmd_line_conf->run_flags & RUN_FLAG__PROCESS_ALL_EVENTS) + event_queue_config->process_all_events = 1; - if ( !cmd_line->metadata_filter.empty() ) - metadata_filter = cmd_line->metadata_filter; + // --run-prefix + run_prefix = cmd_line_conf->run_prefix; - daq_config->overlay(cmd_line->daq_config); + // --skip + if (cmd_line_conf->pkt_skip != 0) + pkt_skip = cmd_line_conf->pkt_skip; - if (cmd_line->run_flags & RUN_FLAG__PROCESS_ALL_EVENTS) - event_queue_config->process_all_events = 1; + // --stdin-rules + stdin_rules = cmd_line_conf->stdin_rules; #ifdef SHELL - if ( cmd_line->remote_control_port ) - remote_control_port = cmd_line->remote_control_port; - else if ( !cmd_line->remote_control_socket.empty() ) - remote_control_socket = cmd_line->remote_control_socket; + // -j + if (cmd_line_conf->remote_control_port) + remote_control_port = cmd_line_conf->remote_control_port; + // --control-socket + else if (!cmd_line_conf->remote_control_socket.empty()) + remote_control_socket = cmd_line_conf->remote_control_socket; #endif + // Finalize the log directory, save a copy in case we need to chroot + if ( log_dir.empty() ) + log_dir = DEFAULT_LOG_DIR; + orig_log_dir = log_dir; + + // Initialize the slotted state memory for threads assert(!state); num_slots = offload_threads + ThreadConfig::get_instance_max(); state = new std::vector[num_slots]; diff --git a/src/main/snort_config.h b/src/main/snort_config.h index efa834503..24a2a9e70 100644 --- a/src/main/snort_config.h +++ b/src/main/snort_config.h @@ -202,7 +202,7 @@ public: void post_setup(); bool verify() const; - void merge(SnortConfig*); + void merge(const SnortConfig*); void clone(const SnortConfig* const); private: diff --git a/src/parser/parser.cc b/src/parser/parser.cc index 315a43fea..71c5ea18b 100644 --- a/src/parser/parser.cc +++ b/src/parser/parser.cc @@ -313,14 +313,15 @@ void parser_term(SnortConfig*) ruleIndexMap = nullptr; } -SnortConfig* ParseSnortConf(const SnortConfig* boot_conf, const char* fname, bool is_fatal) +SnortConfig* ParseSnortConf(const SnortConfig* cmd_line_conf, const char* fname, bool is_fatal) { - SnortConfig* sc = new SnortConfig(SnortConfig::get_conf()->proto_ref); + const SnortConfig* current_conf = SnortConfig::get_conf(); + SnortConfig* sc = new SnortConfig(current_conf->proto_ref); - sc->run_flags = boot_conf->run_flags; - sc->output_flags = boot_conf->output_flags; - sc->tweaks = boot_conf->tweaks; - sc->dump_config_type = boot_conf->dump_config_type; + sc->run_flags = cmd_line_conf->run_flags; + sc->output_flags = cmd_line_conf->output_flags; + sc->tweaks = cmd_line_conf->tweaks; + sc->dump_config_type = cmd_line_conf->dump_config_type; if ( !fname ) fname = get_snort_conf(); @@ -339,7 +340,7 @@ SnortConfig* ParseSnortConf(const SnortConfig* boot_conf, const char* fname, boo sc->detection_filter_config = DetectionFilterConfigNew(); // get overrides from cmd line - Shell* sh = boot_conf->policy_map->get_shell(); + Shell* sh = cmd_line_conf->policy_map->get_shell(); sc->policy_map->get_shell()->set_overrides(sh); if ( *fname ) @@ -349,8 +350,8 @@ SnortConfig* ParseSnortConf(const SnortConfig* boot_conf, const char* fname, boo } bool parse_file_failed = false; - auto output = SnortConfig::get_conf()->create_config_output(); - bool is_top = SnortConfig::get_conf()->dump_config_type == DUMP_CONFIG_JSON_TOP; + auto output = current_conf->create_config_output(); + bool is_top = current_conf->dump_config_type == DUMP_CONFIG_JSON_TOP; for ( unsigned i = 0; true; i++ ) { sh = sc->policy_map->get_shell(i); @@ -375,6 +376,9 @@ SnortConfig* ParseSnortConf(const SnortConfig* boot_conf, const char* fname, boo if ( !parse_file_failed ) set_default_policy(sc); + // Merge in any overrides from the command line + sc->merge(cmd_line_conf); + return sc; } diff --git a/src/protocols/eapol.h b/src/protocols/eapol.h index 54ea71d43..9c1945eb3 100644 --- a/src/protocols/eapol.h +++ b/src/protocols/eapol.h @@ -20,6 +20,8 @@ #ifndef PROTOCOLS_EAPOL_H #define PROTOCOLS_EAPOL_H +namespace snort +{ namespace eapol { struct EtherEapol @@ -80,7 +82,8 @@ struct EapolKey #define EAP_TYPE_OTP 0x05 #define EAP_TYPE_GTC 0x06 #define EAP_TYPE_TLS 0x0d -} +} // namespace eapol +} // namespace snort #endif /* EAPOL_H */ diff --git a/src/protocols/linux_sll.h b/src/protocols/linux_sll.h index d673a29c4..e877c4e72 100644 --- a/src/protocols/linux_sll.h +++ b/src/protocols/linux_sll.h @@ -20,6 +20,8 @@ #ifndef PROTOCOLS_LINUX_SLL_H #define PROTOCOLS_LINUX_SLL_H +namespace snort +{ namespace linux_sll { /* 'Linux cooked captures' data @@ -39,7 +41,7 @@ struct SLLHdr }; /* - * ssl_pkttype values. + * sll_pkttype values. */ #define LINUX_SLL_HOST 0 @@ -48,11 +50,12 @@ struct SLLHdr #define LINUX_SLL_OTHERHOST 3 #define LINUX_SLL_OUTGOING 4 -/* ssl protocol values */ +/* sll protocol values */ #define LINUX_SLL_P_802_3 0x0001 /* Novell 802.3 frames without 802.2 LLC header */ #define LINUX_SLL_P_802_2 0x0004 /* 802.2 frames (not D/I/X Ethernet) */ -} // namespace ssl +} // namespace linux_sll +} // namespace snort #endif /* LINUX_SLL_H */ diff --git a/src/protocols/packet_manager.cc b/src/protocols/packet_manager.cc index 8142f1aec..7fd5fd503 100644 --- a/src/protocols/packet_manager.cc +++ b/src/protocols/packet_manager.cc @@ -63,7 +63,8 @@ const std::array PacketManager::stat_na { "total", "other", - "discards" + "discards", + "depth_exceeded" } }; @@ -85,17 +86,38 @@ void PacketManager::thread_term() // Private helper functions //------------------------------------------------------------------------- -static inline void push_layer(Packet* p, - ProtocolId prot_id, - const uint8_t* hdr_start, - uint32_t len) +inline bool PacketManager::push_layer(Packet* p, CodecData& codec_data, ProtocolId prot_id, + const uint8_t* hdr_start, uint32_t len) { - // We check to ensure num_layer < MAX_LAYERS before this function call + if ( p->num_layers == CodecManager::get_max_layers() ) + { + if (!(codec_data.codec_flags & CODEC_LAYERS_EXCEEDED)) + { + codec_data.codec_flags |= CODEC_LAYERS_EXCEEDED; + DetectionEngine::queue_event(GID_DECODE, DECODE_TOO_MANY_LAYERS); + s_stats[depth_exceeded]++; + } + return false; + } + Layer& lyr = p->layers[p->num_layers++]; lyr.prot_id = prot_id; lyr.start = hdr_start; lyr.length = (uint16_t)len; // lyr.invalid_bits = p->byte_skip; -- currently unused + + return true; +} + +inline Codec* PacketManager::get_layer_codec(const Layer& lyr, int idx) +{ + ProtocolIndex mapped_prot; + // prot_id == ProtocolId::FINISHED_DECODE is a special case for root codecs not registering a protocol ID + if (idx == 0 && (lyr.prot_id == CodecManager::grinder_id || lyr.prot_id == ProtocolId::FINISHED_DECODE)) + mapped_prot = CodecManager::grinder; + else + mapped_prot = CodecManager::s_proto_map[to_utype(lyr.prot_id)]; + return CodecManager::s_protocols[mapped_prot]; } void PacketManager::pop_teredo(Packet* p, RawData& raw) @@ -114,6 +136,56 @@ void PacketManager::pop_teredo(Packet* p, RawData& raw) raw.len += lyr_len; } +void PacketManager::handle_decode_failure(Packet* p, RawData& raw, const CodecData& codec_data, + const DecodeData& unsure_encap_ptrs, ProtocolId prev_prot_id) +{ + if (codec_data.codec_flags & CODEC_UNSURE_ENCAP) + { + p->ptrs = unsure_encap_ptrs; + + switch (p->layers[p->num_layers - 1].prot_id) + { + case ProtocolId::ESP: + // Hardcoding ESP because we trust iff the layer + // immediately preceding the fail is ESP. + p->ptrs.decode_flags |= DECODE_PKT_TRUST; + break; + + case ProtocolId::TEREDO: + // if we just decoded teredo and the next + // layer fails, we made a mistake. Therefore, + // remove this bit. + pop_teredo(p, raw); + break; + default: + break; + } + return; + } + + if ( (p->num_layers > 0) && (p->layers[p->num_layers - 1].prot_id == ProtocolId::TEREDO) && + (prev_prot_id == ProtocolId::IPV6) ) + { + pop_teredo(p, raw); + } + + // if the codec exists, it failed + if (CodecManager::s_proto_map[to_utype(prev_prot_id)]) + { + s_stats[discards]++; + } + else + { + s_stats[other_codecs]++; + + if ( (to_utype(ProtocolId::MIN_UNASSIGNED_IP_PROTO) <= to_utype(prev_prot_id)) && + (to_utype(prev_prot_id) <= std::numeric_limits::max()) ) + { + DetectionEngine::queue_event(GID_DECODE, DECODE_IP_UNASSIGNED_PROTO); + } + } +} + static inline bool payload_offset_from_daq_mismatch(const uint8_t* pkt, const RawData& raw) { const DAQ_PktDecodeData_t* pdd = @@ -153,7 +225,7 @@ void PacketManager::decode( RawData raw(p->daq_msg, pkt, pktlen); CodecData codec_data(p->context->conf, ProtocolId::FINISHED_DECODE); - if ( cooked ) + if (cooked) codec_data.codec_flags |= CODEC_STREAM_REBUILT; // initialize all Packet information @@ -170,20 +242,82 @@ void PacketManager::decode( // loop until the protocol id is no longer valid while (CodecManager::s_protocols[mapped_prot]->decode(raw, codec_data, p->ptrs)) { - debug_logf(decode_trace, nullptr, "Codec %s (protocol_id: %hu) " - "ip header starts at: %p, length is %d\n", + debug_logf(decode_trace, nullptr, + "Codec %s (0x%0*hx) starts at %u, length is %hu\n", CodecManager::s_protocols[mapped_prot]->get_name(), - static_cast(codec_data.next_prot_id), pkt, codec_data.lyr_len); + (static_cast(prev_prot_id) < 0xFF) ? 2 : 4, + static_cast(prev_prot_id), + pktlen - raw.len, codec_data.lyr_len); + + if (codec_data.codec_flags & CODEC_COMPOUND) + { + for (int idx = 0; idx < codec_data.compound_layer_cnt; idx++) + { + CompoundLayer* clyr = &codec_data.compound_layers[idx]; + + // If this was an IP layer, stash the next protocol in the Packet for later + if (clyr->proto_bits & (PROTO_BIT__IP | PROTO_BIT__IP6_EXT) && + idx + 1 < codec_data.compound_layer_cnt) + { + CompoundLayer* nclyr = &codec_data.compound_layers[idx + 1]; + p->ip_proto_next = convert_protocolid_to_ipprotocol(nclyr->layer.prot_id); + } + p->proto_bits |= clyr->proto_bits; + + // If we have reached the MAX_LAYERS, we keep decoding + // but no longer keep track of the layers. + if (!push_layer(p, codec_data, clyr->layer.prot_id, clyr->layer.start, clyr->layer.length)) + continue; + + // Cache the index of the vlan layer for quick access. + if (clyr->proto_bits == PROTO_BIT__VLAN) + p->vlan_idx = p->num_layers - 1; + } + codec_data.codec_flags &= ~CODEC_COMPOUND; + } + else + { + // If this was an IP layer, stash the next protocol in the Packet for later + if (codec_data.proto_bits & (PROTO_BIT__IP | PROTO_BIT__IP6_EXT)) + { + // FIXIT-M refactor when ip_proto's become an array + if (p->is_fragment()) + { + if (prev_prot_id == ProtocolId::FRAGMENT) + { + const ip::IP6Frag* const fragh = reinterpret_cast(raw.data); + p->ip_proto_next = fragh->next(); + } + else + p->ip_proto_next = p->ptrs.ip_api.get_ip4h()->proto(); + } + else + { + if (codec_data.next_prot_id != ProtocolId::FINISHED_DECODE) + p->ip_proto_next = convert_protocolid_to_ipprotocol(codec_data.next_prot_id); + } + } + + // If we have reached the MAX_LAYERS, we keep decoding + // but no longer keep track of the layers. + if (push_layer(p, codec_data, prev_prot_id, raw.data, codec_data.lyr_len)) + { + // Cache the index of the vlan layer for quick access. + if (codec_data.proto_bits == PROTO_BIT__VLAN) + p->vlan_idx = p->num_layers - 1; + } + } - if ( codec_data.tunnel_bypass ) + if (codec_data.tunnel_bypass) { p->active->set_tunnel_bypass(); codec_data.tunnel_bypass = false; } - if ( codec_data.codec_flags & CODEC_ETHER_NEXT ) + // Sanity check the next protocol ID is a valid ethertype + if (codec_data.codec_flags & CODEC_ETHER_NEXT) { - if ( codec_data.next_prot_id < ProtocolId::ETHERTYPE_MINIMUM ) + if (codec_data.next_prot_id < ProtocolId::ETHERTYPE_MINIMUM) { DetectionEngine::queue_event(GID_DECODE, DECODE_BAD_ETHER_TYPE); break; @@ -202,125 +336,39 @@ void PacketManager::decode( codec_data.codec_flags &= ~CODEC_SAVE_LAYER; unsure_encap_ptrs = p->ptrs; } - else - { + else if (codec_data.codec_flags & CODEC_UNSURE_ENCAP) codec_data.codec_flags &= ~CODEC_UNSURE_ENCAP; - } - - if (codec_data.proto_bits & (PROTO_BIT__IP | PROTO_BIT__IP6_EXT)) - { - // FIXIT-M refactor when ip_proto's become an array - if ( p->is_fragment() ) - { - if ( prev_prot_id == ProtocolId::FRAGMENT ) - { - const ip::IP6Frag* const fragh = - reinterpret_cast(raw.data); - p->ip_proto_next = fragh->next(); - } - else - { - p->ip_proto_next = p->ptrs.ip_api.get_ip4h()->proto(); - } - } - else - { - if(codec_data.next_prot_id != ProtocolId::FINISHED_DECODE) - p->ip_proto_next = convert_protocolid_to_ipprotocol(codec_data.next_prot_id); - } - } - - // If we have reached the MAX_LAYERS, we keep decoding - // but no longer keep track of the layers. - if ( p->num_layers == CodecManager::max_layers ) - DetectionEngine::queue_event(GID_DECODE, DECODE_TOO_MANY_LAYERS); - else - { - push_layer(p, prev_prot_id, raw.data, codec_data.lyr_len); - - // Cache the index of the vlan layer for quick access. - if ( codec_data.proto_bits == PROTO_BIT__VLAN ) - p->vlan_idx = p->num_layers-1; - } // internal statistics and record keeping s_stats[mapped_prot + stat_offset]++; // add correct decode for previous layer mapped_prot = CodecManager::s_proto_map[to_utype(codec_data.next_prot_id)]; prev_prot_id = codec_data.next_prot_id; - // set for next call + // Shrink the buffer of undecoded data const uint16_t curr_lyr_len = codec_data.lyr_len + codec_data.invalid_bytes; assert(curr_lyr_len <= raw.len); raw.len -= curr_lyr_len; raw.data += curr_lyr_len; + p->proto_bits |= codec_data.proto_bits; + + // Reset the volatile part of the codec data for the next codec to decode into codec_data.next_prot_id = ProtocolId::FINISHED_DECODE; codec_data.lyr_len = 0; codec_data.invalid_bytes = 0; codec_data.proto_bits = 0; } - debug_logf(decode_trace, nullptr, "Codec %s (protocol_id: %hu) ip header" - " starts at: %p, length is %lu\n", - CodecManager::s_protocols[mapped_prot]->get_name(), - static_cast(prev_prot_id), pkt, (unsigned long)codec_data.lyr_len); + debug_logf(decode_trace, nullptr, "Payload starts at %u, length is %u\n", pktlen - raw.len, raw.len); - if ( p->num_layers > 0 ) + if (p->num_layers > 0) s_stats[mapped_prot + stat_offset]++; // if the final protocol ID is not the default codec, a Codec failed - if (prev_prot_id != ProtocolId::FINISHED_DECODE or p->num_layers == 0 ) - { - if (codec_data.codec_flags & CODEC_UNSURE_ENCAP) - { - p->ptrs = unsure_encap_ptrs; + if (prev_prot_id != ProtocolId::FINISHED_DECODE || p->num_layers == 0 ) + handle_decode_failure(p, raw, codec_data, unsure_encap_ptrs, prev_prot_id); - switch (p->layers[p->num_layers-1].prot_id) - { - case ProtocolId::ESP: - // Hardcoding ESP because we trust iff the layer - // immediately preceding the fail is ESP. - p->ptrs.decode_flags |= DECODE_PKT_TRUST; - break; - - case ProtocolId::TEREDO: - // if we just decoded teredo and the next - // layer fails, we made a mistake. Therefore, - // remove this bit. - pop_teredo(p, raw); - break; - default: - ; - } /* switch */ - } - else - { - if ( (p->num_layers > 0) && - (p->layers[p->num_layers-1].prot_id == ProtocolId::TEREDO) && - (prev_prot_id == ProtocolId::IPV6) ) - { - pop_teredo(p, raw); - } - - // if the codec exists, it failed - if (CodecManager::s_proto_map[to_utype(prev_prot_id)]) - { - s_stats[discards]++; - } - else - { - s_stats[other_codecs]++; - - if ( (to_utype(ProtocolId::MIN_UNASSIGNED_IP_PROTO) <= to_utype(prev_prot_id)) && - (to_utype(prev_prot_id) <= std::numeric_limits::max()) ) - { - DetectionEngine::queue_event(GID_DECODE, DECODE_IP_UNASSIGNED_PROTO); - } - } - } - } - - if ( payload_offset_from_daq_mismatch(pkt, raw) ) + if (payload_offset_from_daq_mismatch(pkt, raw)) p->active->set_tunnel_bypass(); // set any final Packet fields @@ -328,7 +376,7 @@ void PacketManager::decode( p->dsize = (uint16_t)raw.len; p->proto_bits |= codec_data.proto_bits; - if ( !p->proto_bits ) + if (!p->proto_bits) p->proto_bits = PROTO_BIT__OTHER; } @@ -419,12 +467,9 @@ bool PacketManager::encode(const Packet* p, for (int i = outer_layer; i > inner_layer; --i) { const Layer& l = lyrs[i]; - ProtocolIndex mapped_prot = - i ? CodecManager::s_proto_map[to_utype(l.prot_id)] : CodecManager::grinder; - if (!CodecManager::s_protocols[mapped_prot]->encode(l.start, l.length, enc, buf, p->flow)) - { + Codec* cd = get_layer_codec(l, i); + if (!cd->encode(l.start, l.length, enc, buf, p->flow)) return false; - } } outer_layer = inner_layer; // inner_layer is set in 'layer::set_inner_ip_api' @@ -436,13 +481,9 @@ bool PacketManager::encode(const Packet* p, for (int i = outer_layer; i >= 0; --i) { const Layer& l = lyrs[i]; - ProtocolIndex mapped_prot = - i ? CodecManager::s_proto_map[to_utype(l.prot_id)] : CodecManager::grinder; - - if (!CodecManager::s_protocols[mapped_prot]->encode(l.start, l.length, enc, buf, p->flow)) - { + Codec* cd = get_layer_codec(l, i); + if (!cd->encode(l.start, l.length, enc, buf, p->flow)) return false; - } } return true; @@ -719,7 +760,7 @@ int PacketManager::encode_format( init_daq_pkthdr(p, c, phdr, opaque); // copy raw packet data to clone - Layer* lyr = (Layer*)p->layers + num_layers - 1; + Layer* lyr = &p->layers[num_layers - 1]; int len = lyr->start - p->pkt + lyr->length; memcpy((void*)c->pkt, p->pkt, len); @@ -729,7 +770,7 @@ int PacketManager::encode_format( for ( int i = 0; i < num_layers; i++ ) { const uint8_t* b = c->pkt + (p->layers[i].start - p->pkt); // == c->pkt + p->layers[i].len - lyr = c->layers + i; + lyr = &c->layers[i]; lyr->prot_id = p->layers[i].prot_id; lyr->length = p->layers[i].length; @@ -737,16 +778,13 @@ int PacketManager::encode_format( // NOTE: this must always go from outer to inner // to ensure a valid ip header - ProtocolIndex mapped_prot = - i ? CodecManager::s_proto_map[to_utype(lyr->prot_id)] : CodecManager::grinder; - - CodecManager::s_protocols[mapped_prot]->format( - reverse, const_cast(lyr->start), c->ptrs); + Codec* cd = get_layer_codec(*lyr, i); + cd->format(reverse, const_cast(lyr->start), c->ptrs); } if ( update_ip4_len ) { - lyr = (Layer*)c->layers + num_layers - 1; + lyr = &c->layers[num_layers - 1]; ip::IP4Hdr* ip4h = reinterpret_cast(const_cast(lyr->start)); lyr->length = ip::IP4_HEADER_LEN; ip4h->set_ip_len(ip::IP4_HEADER_LEN); @@ -818,11 +856,8 @@ void PacketManager::encode_update(Packet* p) for (int i = outer_layer; i > inner_layer; --i) { const Layer& l = lyr[i]; - ProtocolIndex mapped_prot = i ? - CodecManager::s_proto_map[to_utype(l.prot_id)] : CodecManager::grinder; - - CodecManager::s_protocols[mapped_prot]->update( - tmp_api, flags, const_cast(l.start), l.length, len); + Codec* cd = get_layer_codec(l, i); + cd->update(tmp_api, flags, const_cast(l.start), l.length, len); } outer_layer = inner_layer; // inner_layer is set in 'layer::set_inner_ip_api' @@ -870,7 +905,7 @@ void PacketManager::dump_stats() std::vector pkt_names; // zero out the default codecs - g_stats[3] = 0; + g_stats[stat_offset] = 0; g_stats[CodecManager::s_proto_map[to_utype(ProtocolId::FINISHED_DECODE)] + stat_offset] = 0; for (unsigned int i = 0; i < stat_names.size(); i++) @@ -913,20 +948,24 @@ void PacketManager::log_protocols(TextLog* const text_log, if (num_layers != 0) { - // Grinder is not in the layer array - Codec* cd = CodecManager::s_protocols[CodecManager::grinder]; - - TextLog_Print(text_log, "%-.6s(DLT): ", cd->get_name()); - cd->log(text_log, lyr[0].start, lyr[0].length); + int i = 0; + // Special case for root codecs not registering a protocol ID + if (lyr[0].prot_id == CodecManager::grinder_id || lyr[0].prot_id == ProtocolId::FINISHED_DECODE) + { + Codec* cd = CodecManager::s_protocols[CodecManager::grinder]; + TextLog_Print(text_log, "%s(DLT): ", cd->get_name()); + cd->log(text_log, lyr[0].start, lyr[0].length); + i++; + } - for (int i = 1; i < num_layers; i++) + for (; i < num_layers; i++) { const auto protocol = to_utype(lyr[i].prot_id); const uint8_t codec_offset = CodecManager::s_proto_map[protocol]; - cd = CodecManager::s_protocols[codec_offset]; + Codec* cd = CodecManager::s_protocols[codec_offset]; TextLog_NewLine(text_log); - TextLog_Print(text_log, "%-.*s", 6, cd->get_name()); + TextLog_Print(text_log, "%s", cd->get_name()); if (protocol <= 0xFF) TextLog_Print(text_log, "(0x%02x)", protocol); diff --git a/src/protocols/packet_manager.h b/src/protocols/packet_manager.h index c9b395ddc..27e85c552 100644 --- a/src/protocols/packet_manager.h +++ b/src/protocols/packet_manager.h @@ -134,11 +134,13 @@ public: static ProtocolIndex proto_idx(ProtocolId prot_id) { return CodecManager::s_proto_map[to_utype(prot_id)]; } -private: - // The only time we should accumulate is when CodecManager tells us too - friend void CodecManager::thread_term(); static void accumulate(); + +private: + static bool push_layer(Packet*, CodecData&, ProtocolId, const uint8_t* hdr_start, uint32_t len); + static Codec* get_layer_codec(const Layer&, int idx); static void pop_teredo(Packet*, RawData&); + static void handle_decode_failure(Packet*, RawData&, const CodecData&, const DecodeData&, ProtocolId); static bool encode(const Packet*, EncodeFlags, uint8_t lyr_start, IpProtocol next_prot, Buffer& buf); @@ -148,7 +150,8 @@ private: static const uint8_t total_processed = 0; static const uint8_t other_codecs = 1; static const uint8_t discards = 2; - static const uint8_t stat_offset = 3; + static const uint8_t depth_exceeded = 3; + static const uint8_t stat_offset = 4; // declared in header so it can access s_protocols static THREAD_LOCAL std::array +namespace snort +{ namespace teredo { constexpr uint16_t TEREDO_PORT = 3544; @@ -34,6 +36,7 @@ constexpr uint16_t MIN_HDR_LEN = 2; inline bool is_teredo_port(uint16_t port) { return port == TEREDO_PORT; } } // namespace teredo +} // namespace snort #endif diff --git a/src/protocols/token_ring.h b/src/protocols/token_ring.h index 93d6759dd..b435c76f3 100644 --- a/src/protocols/token_ring.h +++ b/src/protocols/token_ring.h @@ -24,6 +24,8 @@ #include "protocols/protocol_ids.h" +namespace snort +{ namespace token_ring { /* LLC structure */ @@ -98,6 +100,7 @@ inline const Trh_mr* get_trhmr(const Trh_llc* llc) return nullptr; } } // namespace token_ring +} // namespace snort #endif diff --git a/src/target_based/snort_protocols.cc b/src/target_based/snort_protocols.cc index 6b120188c..3de53910c 100644 --- a/src/target_based/snort_protocols.cc +++ b/src/target_based/snort_protocols.cc @@ -35,10 +35,10 @@ using namespace snort; using namespace std; -SnortProtocolId ProtocolReference::get_count() +SnortProtocolId ProtocolReference::get_count() const { return protocol_number; } -const char* ProtocolReference::get_name(SnortProtocolId id) +const char* ProtocolReference::get_name(SnortProtocolId id) const { if ( id >= id_map.size() ) id = 0; @@ -86,7 +86,7 @@ SnortProtocolId ProtocolReference::add(const char* protocol) return snort_protocol_id; } -SnortProtocolId ProtocolReference::find(const char* protocol) +SnortProtocolId ProtocolReference::find(const char* protocol) const { auto protocol_ref = ref_table.find(protocol); if ( protocol_ref != ref_table.end() ) @@ -95,7 +95,7 @@ SnortProtocolId ProtocolReference::find(const char* protocol) return UNKNOWN_PROTOCOL_ID; } -void ProtocolReference::init(ProtocolReference* old_proto_ref) +void ProtocolReference::init(const ProtocolReference* old_proto_ref) { if ( !old_proto_ref ) { @@ -109,7 +109,7 @@ void ProtocolReference::init(ProtocolReference* old_proto_ref) } else { - for(SnortProtocolId id = 0; id < old_proto_ref->get_count(); id++) + for (SnortProtocolId id = 0; id < old_proto_ref->get_count(); id++) add(old_proto_ref->get_name(id)); } } diff --git a/src/target_based/snort_protocols.h b/src/target_based/snort_protocols.h index 74c1d3836..4cb408592 100644 --- a/src/target_based/snort_protocols.h +++ b/src/target_based/snort_protocols.h @@ -70,13 +70,13 @@ public: ProtocolReference(const ProtocolReference&) = delete; ProtocolReference& operator=(const ProtocolReference&) = delete; - SnortProtocolId get_count(); + SnortProtocolId get_count() const; - const char* get_name(SnortProtocolId id); + const char* get_name(SnortProtocolId id) const; const char* get_name_sorted(SnortProtocolId id); SnortProtocolId add(const char* protocol); - SnortProtocolId find(const char* protocol); + SnortProtocolId find(const char* protocol) const; bool operator()(SnortProtocolId a, SnortProtocolId b); @@ -87,7 +87,7 @@ private: SnortProtocolId protocol_number = 0; - void init(ProtocolReference* old_proto_ref); + void init(const ProtocolReference* old_proto_ref); }; } #endif