From 93256a0bbb37e73b27e58bd568edcf0543b11125 Mon Sep 17 00:00:00 2001 From: "Russ Combs (rucombs)" Date: Mon, 25 Sep 2017 19:38:06 -0400 Subject: [PATCH] Merge pull request #1022 in SNORT/snort3 from snort_daq_packet_retry to master Squashed commit of the following: commit c5eaf9f5a8d381a829df5e159eae3fed26309171 Author: davis mcpherson Date: Thu Aug 10 15:01:28 2017 -0400 implement snort support for DAQ_VERDICT_RETRY feature add reg test inspector to facilitate regression testing, initially for the daq packet retry feature add reg test inspector service to facilated regression testing of snort++ limit check of chp match strings to clear to the ones that may have actually been set during chp processing --- extra/configure.ac | 1 + extra/src/daqs/daq_regtest/daq_regtest.c | 119 +++++++--- extra/src/inspectors/CMakeLists.txt | 1 + extra/src/inspectors/Makefile.am | 3 +- extra/src/inspectors/reg_test/CMakeLists.txt | 41 ++++ extra/src/inspectors/reg_test/Makefile.am | 8 + extra/src/inspectors/reg_test/reg_test.cc | 207 ++++++++++++++++++ src/main/snort.cc | 6 +- src/network_inspectors/appid/appid_config.h | 11 +- .../appid/appid_http_session.cc | 8 +- .../appid/appid_http_session.h | 3 +- src/packet_io/active.cc | 17 ++ src/packet_io/active.h | 6 +- src/packet_io/sfdaq.cc | 10 + src/packet_io/sfdaq.h | 2 + 15 files changed, 402 insertions(+), 41 deletions(-) create mode 100644 extra/src/inspectors/reg_test/CMakeLists.txt create mode 100644 extra/src/inspectors/reg_test/Makefile.am create mode 100644 extra/src/inspectors/reg_test/reg_test.cc diff --git a/extra/configure.ac b/extra/configure.ac index e0930e05c..af02d8c4a 100644 --- a/extra/configure.ac +++ b/extra/configure.ac @@ -48,6 +48,7 @@ src/daqs/daq_regtest/Makefile \ src/inspectors/Makefile \ src/inspectors/data_log/Makefile \ src/inspectors/dpx/Makefile \ +src/inspectors/reg_test/Makefile \ src/ips_options/Makefile \ src/ips_options/find/Makefile \ src/ips_options/ips_pkt_num/Makefile \ diff --git a/extra/src/daqs/daq_regtest/daq_regtest.c b/extra/src/daqs/daq_regtest/daq_regtest.c index 637372e46..5898ba144 100644 --- a/extra/src/daqs/daq_regtest/daq_regtest.c +++ b/extra/src/daqs/daq_regtest/daq_regtest.c @@ -22,12 +22,10 @@ #include "config.h" #endif +#include +#include #include #include -#include - -#include "daq.h" -#include "daq_api.h" #define DAQ_MOD_VERSION 0 #define DAQ_NAME "regtest" @@ -49,32 +47,49 @@ typedef struct int daq_config_reads; const DAQ_Module_t* module; void *handle; - struct - { - int skip; - int trace; - void* user; - DAQ_Analysis_Func_t orig_daq_packet_callback; - } packt_tracer_cfg; + int skip; + int trace; + DAQ_PktHdr_t retry_hdr; + uint8_t* retry_data; + unsigned packets_before_retry; + unsigned retry_packet_countdown; + void* user; + DAQ_Analysis_Func_t wrapped_packet_callback; }DAQRegTestContext; +static const DAQ_Verdict verdict_translation_table[MAX_DAQ_VERDICT] = { + DAQ_VERDICT_PASS, /* DAQ_VERDICT_PASS */ + DAQ_VERDICT_BLOCK, /* DAQ_VERDICT_BLOCK */ + DAQ_VERDICT_PASS, /* DAQ_VERDICT_REPLACE */ + DAQ_VERDICT_PASS, /* DAQ_VERDICT_WHITELIST */ + DAQ_VERDICT_BLOCK, /* DAQ_VERDICT_BLACKLIST */ + DAQ_VERDICT_PASS, /* DAQ_VERDICT_IGNORE */ + DAQ_VERDICT_BLOCK /* DAQ_VERDICT_RETRY */ +}; + // packet tracer configuration from command line daq-var skip and trace // --daq-var skip=10 --daq-var trace=5 would trace packets 11 through 15 only static void daq_regtest_get_vars(DAQRegTestContext* context, const DAQ_Config_t* cfg) { DAQ_Dict* entry; - context->packt_tracer_cfg.skip = 0; - context->packt_tracer_cfg.trace = 0; + context->skip = 0; + context->trace = 0; + context->packets_before_retry = 0; + for ( entry = cfg->values; entry; entry = entry->next) { if ( !strcmp(entry->key, "skip") ) { - context->packt_tracer_cfg.skip = atoi(entry->value); + context->skip = atoi(entry->value); } else if ( !strcmp(entry->key, "trace") ) { - context->packt_tracer_cfg.trace = atoi(entry->value); + context->trace = atoi(entry->value); + } + else if ( !strcmp(entry->key, "packets_before_retry") ) + { + context->packets_before_retry = atoi(entry->value); } } } @@ -111,7 +126,7 @@ static int daq_regtest_parse_config(DAQRegTestContext *context, DAQRegTestConfig return DAQ_ERROR_NOMEM; } rewind(fh); - if ( fgets(config->buf , size, fh) == NULL ) + if ( fgets(config->buf, size, fh) == NULL ) { if ( errBuf ) snprintf(errBuf, errMax, "%s: failed to read daq_regtest config file", DAQ_NAME); @@ -149,6 +164,7 @@ static void daq_regtest_cleanup(DAQRegTestContext* context) free(context); } + //------------------------------------------------------------------------- // daq //------------------------------------------------------------------------- @@ -246,33 +262,84 @@ static int daq_regtest_inject ( return context->module->inject(context->handle, hdr, buf, len, reverse); } +static DAQ_Verdict daq_handle_retry_request(DAQRegTestContext* context, const DAQ_PktHdr_t* hdr, + const uint8_t* data) +{ + // FIXIT-L for current reg test needs or snort only 1 pending retry is required so if we + // get a 2nd request we just let it pass. future support for >1 pending retries + // can be implemented with a list holding the hdr & data for each retry packet. + if ( !context->retry_data ) + { + context->retry_hdr = *hdr; + context->retry_data = malloc(hdr->caplen); + if ( context->retry_data ) + { + memcpy(context->retry_data, data, hdr->caplen); + context->retry_packet_countdown = context->packets_before_retry; + return DAQ_VERDICT_BLOCK; + } + } + + return DAQ_VERDICT_PASS; +} + +static void daq_handle_pending_retry(DAQRegTestContext* context) +{ + if ( !context->retry_packet_countdown ) + { + context->retry_hdr.flags |= DAQ_PKT_FLAG_RETRY_PACKET; + DAQ_Verdict verdict = context->wrapped_packet_callback(context->user, + &context->retry_hdr, context->retry_data); + + if (verdict >= MAX_DAQ_VERDICT) + verdict = DAQ_VERDICT_PASS; + verdict = verdict_translation_table[verdict]; + if ( verdict == DAQ_VERDICT_PASS ) + context->module->inject(context->handle, &context->retry_hdr, context->retry_data, + context->retry_hdr.pktlen, 0); + free(context->retry_data); + context->retry_data = NULL; + } + else + context->retry_packet_countdown--; +} + //------------------------------------------------------------------------- static DAQ_Verdict daq_regtest_packet_callback(void* user, const DAQ_PktHdr_t* hdr, const uint8_t* data) { DAQRegTestContext* context = (DAQRegTestContext*)user; - if (context->packt_tracer_cfg.skip == 0 && context->packt_tracer_cfg.trace >0) + if ( context->skip == 0 && context->trace > 0 ) { DAQ_PktHdr_t* pkthdr = (DAQ_PktHdr_t*)hdr; pkthdr->flags |= DAQ_PKT_FLAG_TRACE_ENABLED; } - if (context->packt_tracer_cfg.skip > 0) - context->packt_tracer_cfg.skip--; - else if (context->packt_tracer_cfg.trace > 0) - context->packt_tracer_cfg.trace--; + if ( context->skip > 0 ) + context->skip--; + else if ( context->trace > 0 ) + context->trace--; - return context->packt_tracer_cfg.orig_daq_packet_callback(context->packt_tracer_cfg.user, + if ( context->retry_data ) + daq_handle_pending_retry(context); + + DAQ_Verdict verdict = context->wrapped_packet_callback(context->user, hdr, data); + if ( verdict == DAQ_VERDICT_RETRY ) + verdict = daq_handle_retry_request(context, hdr, data); + + return verdict; } static int daq_regtest_acquire ( void* handle, int cnt, DAQ_Analysis_Func_t callback, DAQ_Meta_Func_t meta, void* user) { DAQRegTestContext* context = (DAQRegTestContext*)handle; - context->packt_tracer_cfg.orig_daq_packet_callback = callback; - context->packt_tracer_cfg.user = user; + context->wrapped_packet_callback = callback; + context->user = user; + context->retry_data = NULL; + return context->module->acquire(context->handle, cnt, daq_regtest_packet_callback, meta, handle); } @@ -311,7 +378,9 @@ static int daq_regtest_get_snaplen (void* handle) static uint32_t daq_regtest_get_capabilities (void* handle) { DAQRegTestContext* context = (DAQRegTestContext*)handle; - return context->module->get_capabilities(context->handle); + uint32_t caps = context->module->get_capabilities(context->handle); + caps |= DAQ_CAPA_RETRY; + return caps; } static int daq_regtest_get_datalink_type(void *handle) diff --git a/extra/src/inspectors/CMakeLists.txt b/extra/src/inspectors/CMakeLists.txt index 147773538..5eba6ddac 100644 --- a/extra/src/inspectors/CMakeLists.txt +++ b/extra/src/inspectors/CMakeLists.txt @@ -1,2 +1,3 @@ add_subdirectory ( dpx ) add_subdirectory ( data_log ) +add_subdirectory ( reg_test ) diff --git a/extra/src/inspectors/Makefile.am b/extra/src/inspectors/Makefile.am index d2ce3eb63..56a393b04 100644 --- a/extra/src/inspectors/Makefile.am +++ b/extra/src/inspectors/Makefile.am @@ -1,6 +1,7 @@ SUBDIRS = \ data_log \ -dpx +dpx \ +reg_test AM_CPPFLAGS = @AM_CPPFLAGS@ AM_CFLAGS= @AM_CFLAGS@ diff --git a/extra/src/inspectors/reg_test/CMakeLists.txt b/extra/src/inspectors/reg_test/CMakeLists.txt new file mode 100644 index 000000000..9626cdf51 --- /dev/null +++ b/extra/src/inspectors/reg_test/CMakeLists.txt @@ -0,0 +1,41 @@ +cmake_minimum_required ( VERSION 2.8.11 ) +project ( reg_test CXX ) + +if ( APPLE ) + set ( CMAKE_MACOSX_RPATH OFF ) +endif ( APPLE ) + +include ( FindPkgConfig ) +pkg_search_module ( SNORT3 REQUIRED snort>=3 ) + +add_library ( + reg_test MODULE + reg_test.cc +) + +if ( APPLE ) + set_target_properties ( + reg_test + PROPERTIES + LINK_FLAGS "-undefined dynamic_lookup" + ) +endif ( APPLE ) + +set_target_properties ( + reg_test + PROPERTIES + PREFIX "" +) + +set ( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11" ) + +target_include_directories ( + reg_test PUBLIC + ${SNORT3_INCLUDE_DIRS} +) + +install ( + TARGETS reg_test + LIBRARY + DESTINATION "lib/${CMAKE_PROJECT_NAME}/inspectors" +) diff --git a/extra/src/inspectors/reg_test/Makefile.am b/extra/src/inspectors/reg_test/Makefile.am new file mode 100644 index 000000000..50c503c21 --- /dev/null +++ b/extra/src/inspectors/reg_test/Makefile.am @@ -0,0 +1,8 @@ +reg_testlibdir = $(pkglibdir)/inspectors + +AM_CXXFLAGS = @SNORT3_CFLAGS@ -std=c++11 + +reg_testlib_LTLIBRARIES = reg_test.la +reg_test_la_CXXFLAGS = $(AM_CXXFLAGS) +reg_test_la_LDFLAGS = -module -export-dynamic -avoid-version -shared +reg_test_la_SOURCES = reg_test.cc diff --git a/extra/src/inspectors/reg_test/reg_test.cc b/extra/src/inspectors/reg_test/reg_test.cc new file mode 100644 index 000000000..4ccb0f981 --- /dev/null +++ b/extra/src/inspectors/reg_test/reg_test.cc @@ -0,0 +1,207 @@ +//-------------------------------------------------------------------------- +// Copyright (C) 2017-2017 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. +//-------------------------------------------------------------------------- +// rti_service.cc author davis mcpherson + +#include + +#include "flow/flow.h" +#include "framework/data_bus.h" +#include "framework/inspector.h" +#include "framework/module.h" +#include "log/messages.h" +#include "packet_io/active.h" +#include "pub_sub/http_events.h" +#include "time/packet_time.h" + +static const char* s_name = "reg_test"; +static const char* s_help = "The regression test inspector (rti) is used when special packet handling is required for a reg test"; + +struct RtiStats +{ + PegCount total_packets; + PegCount retry_requests; + PegCount retry_packets; +}; + +const PegInfo rti_pegs[] = +{ + { CountType::SUM, "packets", "total packets" }, + { CountType::SUM, "retry_requests", "total retry packets requested" }, + { CountType::SUM, "retry_packets", "total retried packets received" }, + { CountType::END, nullptr, nullptr } +}; + +static THREAD_LOCAL RtiStats rti_stats; + +//------------------------------------------------------------------------- +// module stuff +//------------------------------------------------------------------------- + +static const Parameter rti_params[] = +{ + { "test_daq_retry", Parameter::PT_BOOL, nullptr, "true", + "test daq packet retry feature" }, + + { nullptr, Parameter::PT_MAX, nullptr, nullptr, nullptr } +}; + +class RtiServiceModule : public Module +{ +public: + RtiServiceModule() : Module(s_name, s_help, rti_params) + { } + + const PegInfo* get_pegs() const override + { return rti_pegs; } + + PegCount* get_counts() const override + { return (PegCount*)&rti_stats; } + + bool set(const char*, Value& v, SnortConfig*) override; + + bool is_test_daq_retry() { return test_daq_retry; } + +public: + bool test_daq_retry = true; +}; + +bool RtiServiceModule::set(const char*, Value& v, SnortConfig*) +{ + if ( v.is("test_daq_retry") ) + test_daq_retry = v.get_bool(); + else + return false; + + return true; +} + +//------------------------------------------------------------------------- +// inspector stuff +//------------------------------------------------------------------------- + +class RtiService : public Inspector +{ +public: + RtiService(RtiServiceModule* mod); + + void show(SnortConfig*) override; + void eval(Packet* p) override; + bool configure(SnortConfig*) override + { return true; } + +private: + bool test_daq_retry; + void do_daq_packet_retry_test(Packet* p); +}; + +RtiService::RtiService(RtiServiceModule* mod) +{ + test_daq_retry = mod->is_test_daq_retry(); + rti_stats.total_packets = 0; +} + +void RtiService::eval(Packet* p) +{ + if ( test_daq_retry ) + do_daq_packet_retry_test(p); + + rti_stats.total_packets++; +} + +void RtiService::show(SnortConfig*) +{ + LogMessage("%s config:\n", s_name); +} + +void RtiService::do_daq_packet_retry_test(Packet* p) +{ + static bool retry_packet = true; + static bool expect_retry_packet = false; + if (p->dsize) + { + if (p->data[0] == 'A') + { + if (retry_packet) + { + Active::daq_retry_packet(p); + retry_packet = false; + expect_retry_packet = true; + rti_stats.retry_requests++; + } + else if (expect_retry_packet) + { + if ( p->pkth->flags & DAQ_PKT_FLAG_RETRY_PACKET ) + { + expect_retry_packet = false; + rti_stats.retry_packets++; + } + } + } + } +} + +//------------------------------------------------------------------------- +// api stuff +//------------------------------------------------------------------------- + +static Module* mod_ctor() +{ return new RtiServiceModule; } + +static void mod_dtor(Module* m) +{ delete m; } + +static Inspector* rti_ctor(Module* m) +{ return new RtiService((RtiServiceModule*)m); } + +static void rti_dtor(Inspector* p) +{ delete p; } + +static const InspectApi rti_api +{ + { + PT_INSPECTOR, + sizeof(InspectApi), + INSAPI_VERSION, + 0, + API_RESERVED, + API_OPTIONS, + s_name, + s_help, + mod_ctor, + mod_dtor + }, + IT_PACKET, + (uint16_t)PktType::TCP | (uint16_t)PktType::UDP | (uint16_t)PktType::PDU, + nullptr, // buffers + s_name, // service + nullptr, // pinit + nullptr, // pterm + nullptr, // tinit, + nullptr, // tterm, + rti_ctor, + rti_dtor, + nullptr, // ssn + nullptr // reset +}; + +SO_PUBLIC const BaseApi* snort_plugins[] = +{ + &rti_api.base, + nullptr +}; + diff --git a/src/main/snort.cc b/src/main/snort.cc index cb8044e43..72331ee37 100644 --- a/src/main/snort.cc +++ b/src/main/snort.cc @@ -873,7 +873,11 @@ DAQ_Verdict Snort::process_packet( } // process flow verdicts here - if ( Active::session_was_blocked() ) + if ( Active::packet_retry_requested() ) + { + return DAQ_VERDICT_RETRY; + } + else if ( Active::session_was_blocked() ) { if ( !Active::can_block() ) return DAQ_VERDICT_PASS; diff --git a/src/network_inspectors/appid/appid_config.h b/src/network_inspectors/appid/appid_config.h index 3c92b9391..38bfe2af7 100644 --- a/src/network_inspectors/appid/appid_config.h +++ b/src/network_inspectors/appid/appid_config.h @@ -125,13 +125,10 @@ public: NetworkSet* net_list = nullptr; std::array net_list_by_zone; #endif - std::array tcp_port_only; ///< Service IDs for port-only TCP - // services - std::array udp_port_only; ///< Service IDs for port-only UDP - // services - std::array ip_protocol; ///< Service IDs for non-TCP / UDP protocol - // services - SF_LIST client_app_args; ///< List of Client App arguments + std::array tcp_port_only; // port-only TCP services + std::array udp_port_only; // port-only UDP services + std::array ip_protocol; // non-TCP / UDP protocol services + SF_LIST client_app_args; // List of Client App arguments // for each potential port, an sflist of PortExclusion structs AppIdPortExclusions tcp_port_exclusions_src; AppIdPortExclusions udp_port_exclusions_src; diff --git a/src/network_inspectors/appid/appid_http_session.cc b/src/network_inspectors/appid/appid_http_session.cc index 8762a4b10..f6efee5fe 100644 --- a/src/network_inspectors/appid/appid_http_session.cc +++ b/src/network_inspectors/appid/appid_http_session.cc @@ -1,5 +1,5 @@ //-------------------------------------------------------------------------- -// Copyright (C) 2016-2017 Cisco and/or its affiliates. All rights reserved. +// Copyright (C) 2017-2017 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 @@ -76,9 +76,9 @@ AppIdHttpSession::~AppIdHttpSession() snort_free(new_field[i]); } -void AppIdHttpSession::free_chp_matches(ChpMatchDescriptor& cmd, unsigned max_matches) +void AppIdHttpSession::free_chp_matches(ChpMatchDescriptor& cmd, unsigned num_matches) { - for (unsigned i = 0; i <= max_matches; i++) + for (unsigned i = 0; i <= num_matches; i++) if ( cmd.chp_matches[i].size() ) cmd.chp_matches[i].clear(); } @@ -277,7 +277,7 @@ void AppIdHttpSession::process_chp_buffers() } } - free_chp_matches(cmd, NUMBER_OF_PTYPES-1); + free_chp_matches(cmd, MAX_PATTERN_TYPE); if ( !chp_candidate ) { diff --git a/src/network_inspectors/appid/appid_http_session.h b/src/network_inspectors/appid/appid_http_session.h index 6f9ae067a..5af7f1383 100644 --- a/src/network_inspectors/appid/appid_http_session.h +++ b/src/network_inspectors/appid/appid_http_session.h @@ -1,6 +1,5 @@ //-------------------------------------------------------------------------- -// Copyright (C) 2014-2017 Cisco and/or its affiliates. All rights reserved. -// Copyright (C) 2005-2013 Sourcefire, Inc. +// Copyright (C) 2017-2017 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 diff --git a/src/packet_io/active.cc b/src/packet_io/active.cc index fc4ae9b30..0d7b0a78c 100644 --- a/src/packet_io/active.cc +++ b/src/packet_io/active.cc @@ -413,6 +413,23 @@ void Active::daq_drop_packet(const Packet* p) daq_update_status(p); } +bool Active::daq_retry_packet(const Packet *p) +{ + bool retry_queued = false; + + // FIXIT-M may need to confirm this packet is not a retransmit...2.9.x has a check for that + if ( !p->is_rebuilt() && ( active_action == ACT_PASS ) && SFDAQ::can_retry() ) + { + if ( SFDAQ::forwarding_packet(p->pkth) ) + { + active_action = ACT_RETRY; + retry_queued = true; + } + } + + return retry_queued; +} + void Active::block_session(const Packet* p, bool force) { update_status(p, force); diff --git a/src/packet_io/active.h b/src/packet_io/active.h index 7571c020d..89da24e1a 100644 --- a/src/packet_io/active.h +++ b/src/packet_io/active.h @@ -37,7 +37,7 @@ public: { AST_ALLOW, AST_CANT, AST_WOULD, AST_FORCE, AST_MAX }; enum ActiveAction - { ACT_PASS, ACT_DROP, ACT_BLOCK, ACT_RESET, ACT_MAX }; + { ACT_PASS, ACT_DROP, ACT_BLOCK, ACT_RESET, ACT_RETRY, ACT_MAX }; public: static bool init(SnortConfig*); @@ -85,6 +85,7 @@ public: static void drop_packet(const Packet*, bool force = false); static void daq_drop_packet(const Packet*); + static bool daq_retry_packet(const Packet *p); static void block_session(const Packet* p, bool force = false); static void reset_session(const Packet* p, bool force = false); @@ -98,6 +99,9 @@ public: static bool packet_was_dropped() { return ( active_action >= ACT_DROP ); } + static bool packet_retry_requested() + { return ( active_action == ACT_RETRY ); } + static bool session_was_blocked() { return ( active_action >= ACT_BLOCK); } diff --git a/src/packet_io/sfdaq.cc b/src/packet_io/sfdaq.cc index 6f9956488..cc33f440d 100644 --- a/src/packet_io/sfdaq.cc +++ b/src/packet_io/sfdaq.cc @@ -246,6 +246,11 @@ bool SFDAQ::can_replace() return local_instance && local_instance->can_replace(); } +bool SFDAQ::can_retry() +{ + return local_instance && local_instance->can_retry(); +} + int SFDAQ::inject(const DAQ_PktHdr_t* hdr, int rev, const uint8_t* buf, uint32_t len) { return local_instance->inject(hdr, rev, buf, len); @@ -422,6 +427,11 @@ bool SFDAQInstance::can_replace() return (daq_get_capabilities(daq_mod, daq_hand) & DAQ_CAPA_REPLACE) != 0; } +bool SFDAQInstance::can_retry() +{ + return (daq_get_capabilities(daq_mod, daq_hand) & DAQ_CAPA_RETRY) != 0; +} + bool SFDAQInstance::can_start_unprivileged() { return (daq_get_capabilities(daq_mod, daq_hand) & DAQ_CAPA_UNPRIV_START) != 0; diff --git a/src/packet_io/sfdaq.h b/src/packet_io/sfdaq.h index b7a63c113..af0f31740 100644 --- a/src/packet_io/sfdaq.h +++ b/src/packet_io/sfdaq.h @@ -55,6 +55,7 @@ public: bool can_inject(); bool can_inject_raw(); bool can_replace(); + bool can_retry(); bool can_start_unprivileged(); bool can_whitelist(); @@ -100,6 +101,7 @@ public: static bool can_inject(); static bool can_inject_raw(); static bool can_replace(); + static bool can_retry(); // FIXIT-M X Temporary thread-local instance helpers to be removed when no longer needed static void set_local_instance(SFDAQInstance*); -- 2.47.3