// 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 <bbantwal@cisco.com>
+// author Devendra Dahiphale <ddahipha@cisco.com>
#ifdef HAVE_CONFIG_H
#include "config.h"
#include "log/messages.h"
#include "utils/stats.h"
+#include "utils/util.h"
#include "control.h"
#include "request.h"
#include "snort_config.h"
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 <sys/epoll.h>
+#include <unordered_map>
+
+static int epoll_fd = -1;
+static unordered_map<int, ControlConn*> 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<epoll_event> 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<ControlConn*> controls;
+static vector<ControlConn*> controls;
void ControlMgmt::add_control(int fd, bool local)
{
controls.emplace_back(new ControlConn(fd, local));
}
-bool ControlMgmt::find_control(int fd, std::vector<ControlConn*>::iterator& control)
+bool ControlMgmt::find_control(int fd, vector<ControlConn*>::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;
ControlConn* ControlMgmt::find_control(int fd)
{
- std::vector<ControlConn*>::iterator it;
+ vector<ControlConn*>::iterator it;
ControlConn* control = find_control(fd, it) ? (*it) : nullptr;
return control;
}
-void ControlMgmt::delete_control(std::vector<ControlConn*>::iterator& control)
+void ControlMgmt::delete_control(vector<ControlConn*>::iterator& control)
{
delete *control;
control = controls.erase(control);
void ControlMgmt::delete_control(int fd)
{
- std::vector<ControlConn*>::iterator control;
- if ( find_control(fd, control) )
+ vector<ControlConn*>::iterator control;
+ if (find_control(fd, control))
delete_control(control);
}
void ControlMgmt::reconfigure_controls()
{
- for ( auto control : controls )
+ for (auto control : controls)
{
control->configure();
}
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);
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;
{
delete_controls();
- if ( listener >= 0 )
+ if (listener >= 0)
close(listener);
listener = -1;
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<ControlConn*>::iterator control =
+ for (vector<ControlConn*>::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;
}
else
{
- if ( (*control)->is_local_control() )
+ if ((*control)->is_local_control())
proc_stats.local_commands++;
else
proc_stats.remote_commands++;
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;
}
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;
}
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;
}
+
+