From: Mike Stepanek (mstepane) Date: Wed, 24 Apr 2019 19:10:45 +0000 (-0400) Subject: Merge pull request #1572 in SNORT/snort3 from ~DDAHIPHA/snort3:dev_large_fd_segfault... X-Git-Tag: 3.0.0-254~3 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=7f5b14b46d9cc051f34941a25d38f04a95ca3580;p=thirdparty%2Fsnort3.git Merge pull request #1572 in SNORT/snort3 from ~DDAHIPHA/snort3:dev_large_fd_segfault to master Squashed commit of the following: commit bcc34f2893948bf0ed49d563d576e4abf0e45626 Author: Devendra Dahiphale Date: Tue Apr 23 15:00:15 2019 -0400 main: Use epoll(for linux systems) instead of select to get rid of limit on fd-set-size and for time efficiency --- diff --git a/src/main.cc b/src/main.cc index bf232d5e3..2b56bdd73 100644 --- a/src/main.cc +++ b/src/main.cc @@ -61,6 +61,7 @@ #ifdef SHELL #include "main/control_mgmt.h" +#include "main/ac_shell_cmd.h" #endif //------------------------------------------------------------------------- diff --git a/src/main/CMakeLists.txt b/src/main/CMakeLists.txt index 9c8d9b185..2dbea9891 100644 --- a/src/main/CMakeLists.txt +++ b/src/main/CMakeLists.txt @@ -13,7 +13,7 @@ if ( ENABLE_DEBUG_MSGS ) endif ( ENABLE_DEBUG_MSGS ) if ( ENABLE_SHELL ) - set ( SHELL_SOURCES control.cc control.h control_mgmt.cc control_mgmt.h ) + set ( SHELL_SOURCES control.cc control.h control_mgmt.cc control_mgmt.h ac_shell_cmd.h ac_shell_cmd.cc) endif ( ENABLE_SHELL ) add_library (main OBJECT diff --git a/src/main/ac_shell_cmd.cc b/src/main/ac_shell_cmd.cc new file mode 100644 index 000000000..9a59bcd80 --- /dev/null +++ b/src/main/ac_shell_cmd.cc @@ -0,0 +1,64 @@ +//-------------------------------------------------------------------------- +// 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. +//-------------------------------------------------------------------------- +// ac_shell_cmd.cc author Bhagya Tholpady + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "ac_shell_cmd.h" + +#include + +#include "control_mgmt.h" +#include "control.h" + +ACShellCmd::ACShellCmd(int fd, AnalyzerCommand *ac) : ac(ac) +{ + assert(ac); + + ControlConn* control_conn = ControlMgmt::find_control(fd); + + if( control_conn ) + { + control_conn->block(); + control_fd = fd; + } +} + +void ACShellCmd::execute(Analyzer& analyzer) +{ + ControlConn* control_conn = ControlMgmt::find_control(control_fd); + + if( control_conn ) + control_conn->send_queued_response(); + + ac->execute(analyzer); +} + +ACShellCmd::~ACShellCmd() +{ + delete ac; + ControlConn* control = ControlMgmt::find_control(control_fd); + + if( control ) + { + control->send_queued_response(); + control->unblock(); + } +} diff --git a/src/main/ac_shell_cmd.h b/src/main/ac_shell_cmd.h new file mode 100644 index 000000000..c8e99ea70 --- /dev/null +++ b/src/main/ac_shell_cmd.h @@ -0,0 +1,43 @@ +//-------------------------------------------------------------------------- +// 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. +//-------------------------------------------------------------------------- +// control_mgmt.h author Bhagya Tholpady +// +// This provides functions to create and control remote/local connections, +// socket creation/deletion/management functions, and shell commands used by the analyzer. + +#ifndef AC_SHELL_CMD_H +#define AC_SHELL_CMD_H + +#include "main/analyzer.h" +#include "main/analyzer_command.h" + +class ACShellCmd : public AnalyzerCommand +{ +public: + ACShellCmd() = delete; + ACShellCmd(int fd, AnalyzerCommand* ac_cmd); + void execute(Analyzer&) override; + const char* stringify() override { return ac->stringify(); } + ~ACShellCmd() override; + +private: + int control_fd = -1; + AnalyzerCommand* ac; +}; + +#endif diff --git a/src/main/control_mgmt.cc b/src/main/control_mgmt.cc index 0386c8a39..fd28396aa 100644 --- a/src/main/control_mgmt.cc +++ b/src/main/control_mgmt.cc @@ -15,6 +15,8 @@ // with this program; if not, write to the Free Software Foundation, Inc., // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. //-------------------------------------------------------------------------- +// control_mgmt.cc author Bhagya Tholpady +// author Devendra Dahiphale #ifdef HAVE_CONFIG_H #include "config.h" @@ -30,6 +32,7 @@ #include "log/messages.h" #include "utils/stats.h" +#include "utils/util.h" #include "control.h" #include "request.h" #include "snort_config.h" @@ -42,20 +45,211 @@ static socklen_t sock_addr_size = 0; static struct sockaddr* sock_addr = nullptr; static struct sockaddr_in in_addr; static struct sockaddr_un unix_addr; + +//------------------------------------------------------------------------- +// epoll implementation (supported by only linux systems) +//------------------------------------------------------------------------- +// Only linux systems support epoll (event polling) mechanism. +// It allows a process to monitor multiple file descriptors +// and get notification (using epoll_wait) when I/O is possible on them. +#ifdef __linux__ + +#include +#include + +static int epoll_fd = -1; +static unordered_map controls; + +#define MAX_EPOLL_EVENTS 65535 + +static void add_to_epoll(const int fd) +{ + if (epoll_fd == -1) + { + epoll_fd = epoll_create1(0); + if (epoll_fd == -1) + FatalError("Failed to create epoll file descriptor: %s\n", get_error(errno)); + } + + static struct epoll_event event; + event.events = EPOLLIN | EPOLLET; + event.data.fd = fd; + + if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd, &event)) + WarningMessage("Failed to add file descriptor to epoll: %s\n", get_error(errno)); +} + +static void remove_from_epoll(const int fd) +{ + if (epoll_ctl(epoll_fd, EPOLL_CTL_DEL, fd, nullptr)) + WarningMessage("Failed to remove file descriptor from epoll\n"); +} + +void ControlMgmt::add_control(int fd, bool local) +{ + if (controls.find(fd) != controls.end()) + { + assert(0); + WarningMessage("Cannot have two active connections with the same fd\n"); + return; + } + + controls[fd] = new ControlConn(fd, local); + add_to_epoll(fd); +} + +ControlConn* ControlMgmt::find_control(int fd) +{ + auto control_conn = controls.find(fd); + if (control_conn == controls.end()) + return nullptr; + + return control_conn->second; +} + +void ControlMgmt::delete_control(int fd) +{ + auto control_conn = find_control(fd); + if (control_conn) + { + remove_from_epoll(fd); + delete control_conn; + controls.erase(fd); + } +} + +void ControlMgmt::reconfigure_controls() +{ + for (auto &control : controls) + control.second->configure(); +} + +void ControlMgmt::delete_controls() +{ + for (auto &control : controls) + { + remove_from_epoll(control.first); + delete control.second; + } + controls.clear(); +} + +int ControlMgmt::socket_init() +{ + int sock_family = setup_socket_family(); + + if (sock_family == AF_UNSPEC) + return -1; + + listener = socket(sock_family, SOCK_STREAM, 0); + + if (listener < 0) + FatalError("socket failed: %s\n", get_error(errno)); + + // FIXIT-M want to disable time wait + int on = 1; + setsockopt(listener, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); + + if (::bind(listener, sock_addr, sock_addr_size) < 0) + FatalError("bind failed: %s\n", get_error(errno)); + + // FIXIT-M configure max conns + if (listen(listener, 0) < 0) + FatalError("listen failed: %s\n", get_error(errno)); + + add_to_epoll(listener); + + return 0; +} + +int ControlMgmt::socket_term() +{ + delete_controls(); + + if (listener >= 0) + close(listener); + + listener = -1; + + if (epoll_fd >= 0) + close(epoll_fd); + + epoll_fd = -1; + + return 0; +} + +bool ControlMgmt::process_control_commands(int& current_fd, Request*& current_request, int evnt_fd) +{ + bool ret = false; + auto control_conn = controls.find(evnt_fd); + + if (control_conn == controls.end()) + return ret; + + Request* old_request = current_request; + int fd = control_conn->second->shell_execute(current_fd, current_request); + current_fd = -1; + current_request = old_request; + + if (fd >= 0) + { + if (control_conn->second->is_local_control()) + proc_stats.local_commands++; + else + proc_stats.remote_commands++; + ret = true; + } + return ret; +} + +bool ControlMgmt::service_users(int& current_fd, Request*& current_request) +{ + bool ret = false; + vector events(MAX_EPOLL_EVENTS); + + int event_count = epoll_wait(epoll_fd, events.data(), MAX_EPOLL_EVENTS, 0); + for(int i = 0; i < event_count; i++) + { + ret = process_control_commands(current_fd, current_request, events[i].data.fd); + + if (listener == events[i].data.fd) + { + // got a new connection request, accept it and store it in controls + if( !socket_conn() ) + { + ret = true; + } + } + + if (!ret && (events[i].events & EPOLLHUP)) + { + delete_control(events[i].data.fd); + } + } + return ret; +} + +#else +//------------------------------------------------------------------------- +// select implementation (default) +//------------------------------------------------------------------------- +// Default implementation using select() for monitoring multiple fds. + static fd_set inputs; -static std::vector controls; +static vector controls; void ControlMgmt::add_control(int fd, bool local) { controls.emplace_back(new ControlConn(fd, local)); } -bool ControlMgmt::find_control(int fd, std::vector::iterator& control) +bool ControlMgmt::find_control(int fd, vector::iterator& control) { - control = std::find_if(controls.begin(), controls.end(), + control = find_if(controls.begin(), controls.end(), [=](const ControlConn* c) { return c->get_fd() == fd; }); - if(control != controls.end()) + if (control != controls.end()) return true; else return false; @@ -63,13 +257,13 @@ bool ControlMgmt::find_control(int fd, std::vector::iterator& cont ControlConn* ControlMgmt::find_control(int fd) { - std::vector::iterator it; + vector::iterator it; ControlConn* control = find_control(fd, it) ? (*it) : nullptr; return control; } -void ControlMgmt::delete_control(std::vector::iterator& control) +void ControlMgmt::delete_control(vector::iterator& control) { delete *control; control = controls.erase(control); @@ -77,14 +271,14 @@ void ControlMgmt::delete_control(std::vector::iterator& control) void ControlMgmt::delete_control(int fd) { - std::vector::iterator control; - if ( find_control(fd, control) ) + vector::iterator control; + if (find_control(fd, control)) delete_control(control); } void ControlMgmt::reconfigure_controls() { - for ( auto control : controls ) + for (auto control : controls) { control->configure(); } @@ -92,60 +286,18 @@ void ControlMgmt::reconfigure_controls() void ControlMgmt::delete_controls() { - for ( auto control : controls ) + for (auto control : controls) { delete control; } controls.clear(); } -//------------------------------------------------------------------------- -// socket foo -//------------------------------------------------------------------------- -// FIXIT-M make these non-blocking -// FIXIT-M bind to configured ip including INADDR_ANY -// (default is loopback if enabled) - -int ControlMgmt::setup_socket_family() -{ - int family = AF_UNSPEC; - if ( SnortConfig::get_conf()->remote_control_port ) - { - memset(&in_addr, 0, sizeof(in_addr)); - - in_addr.sin_family = AF_INET; - in_addr.sin_addr.s_addr = htonl(0x7F000001); - in_addr.sin_port = htons(SnortConfig::get_conf()->remote_control_port); - sock_addr = (struct sockaddr*)&in_addr; - sock_addr_size = sizeof(in_addr); - family = AF_INET; - } - else if ( !SnortConfig::get_conf()->remote_control_socket.empty() ) - { - std::string fullpath; - const char* path_sep = strrchr(SnortConfig::get_conf()->remote_control_socket.c_str(), '/'); - if (path_sep != nullptr) - fullpath = SnortConfig::get_conf()->remote_control_socket; - else - get_instance_file(fullpath, SnortConfig::get_conf()->remote_control_socket.c_str()); - - memset(&unix_addr, 0, sizeof(unix_addr)); - unix_addr.sun_family = AF_UNIX; - strncpy(unix_addr.sun_path, fullpath.c_str(), sizeof(unix_addr.sun_path)-1); - sock_addr = (struct sockaddr*)&unix_addr; - sock_addr_size = sizeof(unix_addr); - unlink(fullpath.c_str()); - family = AF_UNIX; - } - - return family; -} - int ControlMgmt::socket_init() { int sock_family = setup_socket_family(); - if ( sock_family == AF_UNSPEC ) + if (sock_family == AF_UNSPEC) return -1; listener = socket(sock_family, SOCK_STREAM, 0); @@ -157,11 +309,11 @@ int ControlMgmt::socket_init() int on = 1; setsockopt(listener, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); - if ( ::bind(listener, sock_addr, sock_addr_size) < 0 ) + if (::bind(listener, sock_addr, sock_addr_size) < 0) FatalError("bind failed: %s\n", get_error(errno)); // FIXIT-M configure max conns - if ( listen(listener, 0) < 0 ) + if (listen(listener, 0) < 0) FatalError("listen failed: %s\n", get_error(errno)); return 0; @@ -171,7 +323,7 @@ int ControlMgmt::socket_term() { delete_controls(); - if ( listener >= 0 ) + if (listener >= 0) close(listener); listener = -1; @@ -179,34 +331,21 @@ int ControlMgmt::socket_term() return 0; } -int ControlMgmt::socket_conn() -{ - int remote_control = accept(listener, sock_addr, &sock_addr_size); - - if ( remote_control < 0 ) - return -1; - - add_control(remote_control, false); - - // FIXIT-L authenticate, use ssl ? - return 0; -} - bool ControlMgmt::process_control_commands(int& current_fd, Request*& current_request) { bool ret = false; - for(std::vector::iterator control = + for (vector::iterator control = controls.begin(); control != controls.end();) { int fd = (*control)->get_fd(); - if ( FD_ISSET(fd, &inputs) ) + if (FD_ISSET(fd, &inputs)) { Request* old_request = current_request; fd = (*control)->shell_execute(current_fd, current_request); current_fd = -1; current_request = old_request; - if( fd < 0 ) + if (fd < 0) { delete_control(control); ret = false; @@ -214,7 +353,7 @@ bool ControlMgmt::process_control_commands(int& current_fd, Request*& current_re } else { - if ( (*control)->is_local_control() ) + if ((*control)->is_local_control()) proc_stats.local_commands++; else proc_stats.remote_commands++; @@ -232,20 +371,20 @@ bool ControlMgmt::service_users(int& current_fd, Request*& current_request) int max_fd = -1; bool ret = false; - for ( auto control : controls ) + for (auto control : controls) { int fd = control->get_fd(); - if ( fd >= 0 and !control->is_blocked() ) + if (fd >= 0 and !control->is_blocked()) { FD_SET(fd, &inputs); - if ( fd > max_fd ) + if (fd > max_fd) max_fd = fd; } } - if ( listener >= 0 ) + if (listener >= 0) { FD_SET(listener, &inputs); - if ( listener > max_fd ) + if (listener > max_fd) max_fd = listener; } @@ -253,15 +392,15 @@ bool ControlMgmt::service_users(int& current_fd, Request*& current_request) timeout.tv_sec = 0; timeout.tv_usec = 0; - if ( select(max_fd+1, &inputs, nullptr, nullptr, &timeout) > 0 ) + if (select(max_fd+1, &inputs, nullptr, nullptr, &timeout) > 0) { ret = process_control_commands(current_fd, current_request); - if ( listener >= 0 ) + if (listener >= 0) { - if ( FD_ISSET(listener, &inputs) ) + if (FD_ISSET(listener, &inputs)) { - if ( !socket_conn() ) + if (!socket_conn()) { ret = true; } @@ -271,36 +410,59 @@ bool ControlMgmt::service_users(int& current_fd, Request*& current_request) return ret; } -ACShellCmd::ACShellCmd(int fd, AnalyzerCommand *ac) : ac(ac) -{ - assert(ac); - ControlConn* control = (fd >= 0)? (ControlMgmt::find_control(fd) ) : nullptr; - - if( control ) - { - control->block(); - control_fd = fd; - } -} +#endif -void ACShellCmd::execute(Analyzer& analyzer) +int ControlMgmt::socket_conn() { - ControlConn* control = (control_fd >= 0)? (ControlMgmt::find_control(control_fd) ) : nullptr; + int remote_control = accept(listener, sock_addr, &sock_addr_size); + + if (remote_control < 0) + return -1; - if( control ) - control->send_queued_response(); + add_control(remote_control, false); - ac->execute(analyzer); + // FIXIT-L authenticate, use ssl ? + return 0; } -ACShellCmd::~ACShellCmd() +//------------------------------------------------------------------------- +// socket foo +//------------------------------------------------------------------------- +// FIXIT-M make these non-blocking +// FIXIT-M bind to configured ip including INADDR_ANY +// (default is loopback if enabled) +int ControlMgmt::setup_socket_family() { - delete ac; - ControlConn* control = (control_fd >= 0)? (ControlMgmt::find_control(control_fd) ) : nullptr; + int family = AF_UNSPEC; + if (SnortConfig::get_conf()->remote_control_port) + { + memset(&in_addr, 0, sizeof(in_addr)); - if( control ) + in_addr.sin_family = AF_INET; + in_addr.sin_addr.s_addr = htonl(0x7F000001); + in_addr.sin_port = htons(SnortConfig::get_conf()->remote_control_port); + sock_addr = (struct sockaddr*)&in_addr; + sock_addr_size = sizeof(in_addr); + family = AF_INET; + } + else if (!SnortConfig::get_conf()->remote_control_socket.empty()) { - control->send_queued_response(); - control->unblock(); + string fullpath; + const char* path_sep = strrchr(SnortConfig::get_conf()->remote_control_socket.c_str(), '/'); + if (path_sep != nullptr) + fullpath = SnortConfig::get_conf()->remote_control_socket; + else + get_instance_file(fullpath, SnortConfig::get_conf()->remote_control_socket.c_str()); + + memset(&unix_addr, 0, sizeof(unix_addr)); + unix_addr.sun_family = AF_UNIX; + strncpy(unix_addr.sun_path, fullpath.c_str(), sizeof(unix_addr.sun_path)-1); + sock_addr = (struct sockaddr*)&unix_addr; + sock_addr_size = sizeof(unix_addr); + unlink(fullpath.c_str()); + family = AF_UNIX; } + return family; } + + diff --git a/src/main/control_mgmt.h b/src/main/control_mgmt.h index c8aa18113..08f754ccc 100644 --- a/src/main/control_mgmt.h +++ b/src/main/control_mgmt.h @@ -16,7 +16,7 @@ // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. //-------------------------------------------------------------------------- // control_mgmt.h author Bhagya Tholpady -// +// author Devendra Dahiphale // This provides functions to create and control remote/local connections, // socket creation/deletion/management functions, and shell commands used by the analyzer. @@ -25,46 +25,31 @@ #include -#include "main/analyzer.h" -#include "main/analyzer_command.h" -#include "main/snort_types.h" -#include "utils/util.h" - class ControlConn; class ControlMgmt { public: static void add_control(int fd, bool local_control); - static void delete_control(int fd); - static void delete_controls(); - static ControlConn* find_control(int fd); static void reconfigure_controls(); - static bool find_control(int fd, std::vector::iterator& control); - static void delete_control(std::vector::iterator& control); - static int socket_init(); static int socket_term(); static int socket_conn(); + static bool process_control_commands(int& current_fd, class Request*& current_request, int); static bool process_control_commands(int& current_fd, class Request*& current_request); + + static ControlConn* find_control(int fd); + static bool find_control(int fd, std::vector::iterator& control); + + static void delete_controls(); + static void delete_control(int fd); + static void delete_control(std::vector::iterator& control); + static bool service_users(int& current_fd, class Request*& current_request); -private: - static int setup_socket_family(); -}; -class ACShellCmd : public AnalyzerCommand -{ -public: - ACShellCmd() = delete; - ACShellCmd(int fd, AnalyzerCommand* ac_cmd); - void execute(Analyzer&) override; - const char* stringify() override { return ac->stringify(); } - ~ACShellCmd() override; private: - int control_fd = -1; - AnalyzerCommand* ac; + static int setup_socket_family(); }; - #endif diff --git a/src/main/snort.cc b/src/main/snort.cc index c07e01a62..3e43d7707 100644 --- a/src/main/snort.cc +++ b/src/main/snort.cc @@ -96,6 +96,7 @@ #endif #ifdef SHELL +#include "ac_shell_cmd.h" #include "control_mgmt.h" #endif