From ba611a2d2ce3d442bc35277f7260f0373f561b8d Mon Sep 17 00:00:00 2001 From: Damien Stuart Date: Sun, 20 Dec 2015 21:58:31 -0500 Subject: [PATCH] Many additions and modifications for support of capture via Netfilter Queue. --- configure.ac | 4 +- server/Makefile.am | 4 +- server/cmd_opts.h | 9 ++ server/config_init.c | 49 +++++++ server/fw_util_firewalld.c | 68 ++++++--- server/fw_util_firewalld.h | 5 +- server/fw_util_iptables.c | 72 ++++++--- server/fw_util_iptables.h | 4 +- server/fwknopd.c | 5 +- server/fwknopd.conf | 35 +++++ server/fwknopd_common.h | 27 +++- server/nfq_capture.c | 291 +++++++++++++++++++++++++++++++++++++ server/nfq_capture.h | 43 ++++++ server/process_packet.c | 36 ++--- server/process_packet.h | 15 +- 15 files changed, 582 insertions(+), 85 deletions(-) create mode 100755 server/nfq_capture.c create mode 100755 server/nfq_capture.h diff --git a/configure.ac b/configure.ac index 0e72f22c..79758426 100644 --- a/configure.ac +++ b/configure.ac @@ -242,8 +242,8 @@ AM_CONDITIONAL([UDP_SERVER], [test "$want_udp_server" = yes]) dnl Decide whether or not to enable UDP server mode (no libpcap dependency) dnl want_nfq_capture=no -AC_ARG_ENABLE([nfq-server], - [AS_HELP_STRING([--enable-nfq-server], +AC_ARG_ENABLE([nfq-capture], + [AS_HELP_STRING([--enable-nfq-capture], [Enable NF_QUEUE server mode for no libpcap dependency @<:@default is to disable@:>@])], [want_nfq_capture=$enableval], []) diff --git a/server/Makefile.am b/server/Makefile.am index 38c5571e..cad56130 100644 --- a/server/Makefile.am +++ b/server/Makefile.am @@ -30,7 +30,7 @@ if WANT_C_UNIT_TESTS fwknopd_utests_LDFLAGS = -lcunit $(GPGME_LIBS) if NFQ_CAPTURE - fwknop_utests_LDFLAGS += -lnetfilter_queue + fwknopd_utests_LDFLAGS += -lnetfilter_queue else if !UDP_SERVER fwknopd_utests_LDFLAGS += -lpcap @@ -40,7 +40,7 @@ endif endif if NFQ_CAPTURE - fwknop_LDADD += -lnetfilter_queue + fwknopd_LDADD += -lnetfilter_queue else if !UDP_SERVER fwknopd_LDADD += -lpcap diff --git a/server/cmd_opts.h b/server/cmd_opts.h index 3af9adbc..8a5be685 100644 --- a/server/cmd_opts.h +++ b/server/cmd_opts.h @@ -62,6 +62,15 @@ static char *config_map[NUMBER_OF_CONFIG_ENTRIES] = { "ENABLE_UDP_SERVER", "UDPSERV_PORT", "UDPSERV_SELECT_TIMEOUT", +#if USE_LIBNETFILTER_QUEUE + "ENABLE_NFQ_CAPTURE", + "NFQ_INTERFACE", + "NFQ_PORT", + "NFQ_TABLE", + "NFQ_CHAIN", + "NFQ_QUEUE_NUMBER", + "NFQ_LOOP_SLEEP", +#endif "LOCALE", "SYSLOG_IDENTITY", "SYSLOG_FACILITY", diff --git a/server/config_init.c b/server/config_init.c index 1686a42c..0af6c7cb 100644 --- a/server/config_init.c +++ b/server/config_init.c @@ -901,6 +901,51 @@ validate_options(fko_srv_options_t *opts) if(opts->config[CONF_TCPSERV_PORT] == NULL) set_config_entry(opts, CONF_TCPSERV_PORT, DEF_TCPSERV_PORT); +#if USE_LIBNETFILTER_QUEUE + /* Enable NFQ Capture + */ + if(opts->config[CONF_ENABLE_NFQ_CAPTURE] == NULL) + { + if((strncasecmp(DEF_ENABLE_NFQ_CAPTURE, "Y", 1) == 0) && + !opts->enable_nfq_capture) + { + opts->enable_nfq_capture = 1; + } + set_config_entry(opts, CONF_ENABLE_NFQ_CAPTURE, DEF_ENABLE_NFQ_CAPTURE); + } + + /* NFQ Interface + */ + if(opts->config[CONF_NFQ_INTERFACE] == NULL) + set_config_entry(opts, CONF_NFQ_INTERFACE, DEF_NFQ_INTERFACE); + + /* NFQ port. + */ + if(opts->config[CONF_NFQ_PORT] == NULL) + set_config_entry(opts, CONF_NFQ_PORT, DEF_NFQ_PORT); + + /* NFQ Queue Number + */ + if(opts->config[CONF_NFQ_QUEUE_NUMBER] == NULL) + set_config_entry(opts, CONF_NFQ_QUEUE_NUMBER, + DEF_NFQ_QUEUE_NUMBER); + + /* NFQ Chain + */ + if(opts->config[CONF_NFQ_CHAIN] == NULL) + set_config_entry(opts, CONF_NFQ_CHAIN, DEF_NFQ_CHAIN); + + /* NFQ Table + */ + if(opts->config[CONF_NFQ_TABLE] == NULL) + set_config_entry(opts, CONF_NFQ_TABLE, DEF_NFQ_TABLE); + + /* NFQ loop delay + */ + if(opts->config[CONF_NFQ_LOOP_SLEEP] == NULL) + set_config_entry(opts, CONF_NFQ_LOOP_SLEEP, DEF_CONF_NFQ_LOOP_SLEEP); +#endif + /* Enable UDP server. */ if(opts->config[CONF_ENABLE_UDP_SERVER] == NULL) @@ -1409,6 +1454,10 @@ usage(void) " -K, --kill - Kill the currently running fwknopd.\n" " -l, --locale - Provide a locale setting other than the system\n" " default.\n" +#if USE_LIBNETFILTER_QUEUE + " -n, --nfq-capture - Capture packets using libnetfilter_queue (falls\n" + " back to UDP server mode if not used).\n" +#endif " -O, --override-config - Specify a file with configuration entries that will\n" " overide those in fwknopd.conf\n" " -p, --pid-file - Specify an alternate fwknopd.pid file.\n" diff --git a/server/fw_util_firewalld.c b/server/fw_util_firewalld.c index 0cf3724e..3726d51a 100644 --- a/server/fw_util_firewalld.c +++ b/server/fw_util_firewalld.c @@ -711,11 +711,12 @@ delete_all_chains(const fko_srv_options_t * const opts) */ snprintf(cmd_buf, CMD_BUFSIZE-1, "%s " IPT_DEL_RULE_ARGS, fwc.fw_command, - NFQ_TABLE, + opts->config[CONF_NFQ_TABLE], "INPUT", 1 ); - res = run_extcmd(cmd_buf, err_buf, CMD_BUFSIZE, 0); + res = run_extcmd(cmd_buf, err_buf, CMD_BUFSIZE, WANT_STDERR, + NO_TIMEOUT, &pid_status, opts); if (opts->verbose) log_msg(LOG_INFO, "delete_all_chains() CMD: '%s' (res: %d, err: %s)", @@ -731,10 +732,11 @@ delete_all_chains(const fko_srv_options_t * const opts) */ snprintf(cmd_buf, CMD_BUFSIZE-1, "%s " IPT_FLUSH_CHAIN_ARGS, fwc.fw_command, - NFQ_TABLE, - NFQ_CHAIN + opts->config[CONF_NFQ_TABLE], + opts->config[CONF_NFQ_CHAIN] ); - res = run_extcmd(cmd_buf, err_buf, CMD_BUFSIZE, 0); + res = run_extcmd(cmd_buf, err_buf, CMD_BUFSIZE, WANT_STDERR, + NO_TIMEOUT, &pid_status, opts); if (opts->verbose) log_msg(LOG_INFO, "delete_all_chains() CMD: '%s' (res: %d, err: %s)", @@ -746,14 +748,15 @@ delete_all_chains(const fko_srv_options_t * const opts) zero_cmd_buffers(); - /* Create the NF_QUEUE chains and rules + /* Delete the NF_QUEUE chains and rules */ snprintf(cmd_buf, CMD_BUFSIZE-1, "%s " IPT_DEL_CHAIN_ARGS, fwc.fw_command, - NFQ_TABLE, - NFQ_CHAIN + opts->config[CONF_NFQ_TABLE], + opts->config[CONF_NFQ_CHAIN] ); - res = run_extcmd(cmd_buf, err_buf, CMD_BUFSIZE, 0); + res = run_extcmd(cmd_buf, err_buf, CMD_BUFSIZE, WANT_STDERR, + NO_TIMEOUT, &pid_status, opts); if (opts->verbose) log_msg(LOG_INFO, "delete_all_chains() CMD: '%s' (res: %d, err: %s)", @@ -841,10 +844,11 @@ create_fw_chains(const fko_srv_options_t * const opts) */ snprintf(cmd_buf, CMD_BUFSIZE-1, "%s " IPT_NEW_CHAIN_ARGS, fwc.fw_command, - NFQ_TABLE, - NFQ_CHAIN + opts->config[CONF_NFQ_TABLE], + opts->config[CONF_NFQ_CHAIN] ); - res = run_extcmd(cmd_buf, err_buf, CMD_BUFSIZE, 0); + res = run_extcmd(cmd_buf, err_buf, CMD_BUFSIZE, WANT_STDERR, + NO_TIMEOUT, &pid_status, opts); if (opts->verbose) log_msg(LOG_INFO, "create_fw_chains() CMD: '%s' (res: %d, err: %s)", @@ -863,12 +867,13 @@ create_fw_chains(const fko_srv_options_t * const opts) */ snprintf(cmd_buf, CMD_BUFSIZE-1, "%s " IPT_ADD_JUMP_RULE_ARGS, fwc.fw_command, - NFQ_TABLE, + opts->config[CONF_NFQ_TABLE], "INPUT", 1, - NFQ_CHAIN + opts->config[CONF_NFQ_CHAIN] ); - res = run_extcmd(cmd_buf, err_buf, CMD_BUFSIZE, 0); + res = run_extcmd(cmd_buf, err_buf, CMD_BUFSIZE, WANT_STDERR, + NO_TIMEOUT, &pid_status, opts); if (opts->verbose) log_msg(LOG_INFO, "create_fw_chains() CMD: '%s' (res: %d, err: %s)", @@ -884,15 +889,32 @@ create_fw_chains(const fko_srv_options_t * const opts) zero_cmd_buffers(); /* Create the rule to direct SPA packets to the queue. + * If an interface is specified use the "_WITH_IF" version + * of the command. */ - snprintf(cmd_buf, CMD_BUFSIZE-1, "%s " FIREWD_NFQ_ADD_ARGS, - fwc.fw_command, - NFQ_TABLE, - NFQ_CHAIN, - NFQ_PORT, - NFQ_QUEUE_NUM - ); - res = run_extcmd(cmd_buf, err_buf, CMD_BUFSIZE, 0); + if(strlen(opts->config[CONF_NFQ_INTERFACE]) > 0) + { + snprintf(cmd_buf, CMD_BUFSIZE-1, "%s " FIREWD_NFQ_ADD_ARGS_WITH_IF, + fwc.fw_command, + opts->config[CONF_NFQ_TABLE], + opts->config[CONF_NFQ_CHAIN], + opts->config[CONF_NFQ_INTERFACE], + opts->config[CONF_NFQ_PORT], + opts->config[CONF_NFQ_QUEUE_NUMBER] + ); + } + else + { + snprintf(cmd_buf, CMD_BUFSIZE-1, "%s " FIREWD_NFQ_ADD_ARGS, + fwc.fw_command, + opts->config[CONF_NFQ_TABLE], + opts->config[CONF_NFQ_CHAIN], + opts->config[CONF_NFQ_PORT], + opts->config[CONF_NFQ_QUEUE_NUMBER] + ); + } + res = run_extcmd(cmd_buf, err_buf, CMD_BUFSIZE, WANT_STDERR, + NO_TIMEOUT, &pid_status, opts); if (opts->verbose) log_msg(LOG_INFO, "create_fw_chains() CMD: '%s' (res: %d, err: %s)", diff --git a/server/fw_util_firewalld.h b/server/fw_util_firewalld.h index 1eaa7d63..6aa5784c 100644 --- a/server/fw_util_firewalld.h +++ b/server/fw_util_firewalld.h @@ -69,8 +69,9 @@ #define FIREWD_ANY_IP "0.0.0.0/0" #if USE_LIBNETFILTER_QUEUE - #define FIREWD_NFQ_ADD_ARGS "-t %s -A %s -p udp -m udp --dport %i -j NFQUEUE --queue-num %i" - #define FIREWD_NFQ_DEL_ARGS "-t %s -D %s -p udp -m udp --dport %i -j NFQUEUE --queue-num %i" + #define FIREWD_NFQ_ADD_ARGS "-t %s -A %s -p udp -m udp --dport %s -j NFQUEUE --queue-num %s" + #define FIREWD_NFQ_ADD_ARGS_WITH_IF "-t %s -A %s -i %s -p udp -m udp --dport %s -j NFQUEUE --queue-num %s" + #define FIREWD_NFQ_DEL_ARGS "-t %s -D %s -p udp -m udp --dport %s -j NFQUEUE --queue-num %s" #endif int validate_firewd_chain_conf(const char * const chain_str); diff --git a/server/fw_util_iptables.c b/server/fw_util_iptables.c index 08b3c162..83ef5a97 100644 --- a/server/fw_util_iptables.c +++ b/server/fw_util_iptables.c @@ -705,11 +705,12 @@ delete_all_chains(const fko_srv_options_t * const opts) */ snprintf(cmd_buf, CMD_BUFSIZE-1, "%s " IPT_DEL_RULE_ARGS, fwc.fw_command, - NFQ_TABLE, + opts->config[CONF_NFQ_TABLE], "INPUT", 1 ); - res = run_extcmd(cmd_buf, err_buf, CMD_BUFSIZE, 0); + res = run_extcmd(cmd_buf, err_buf, CMD_BUFSIZE, WANT_STDERR, + NO_TIMEOUT, &pid_status, opts); if (opts->verbose) log_msg(LOG_INFO, "delete_all_chains() CMD: '%s' (res: %d, err: %s)", @@ -725,10 +726,11 @@ delete_all_chains(const fko_srv_options_t * const opts) */ snprintf(cmd_buf, CMD_BUFSIZE-1, "%s " IPT_FLUSH_CHAIN_ARGS, fwc.fw_command, - NFQ_TABLE, - NFQ_CHAIN + opts->config[CONF_NFQ_TABLE], + opts->config[CONF_NFQ_CHAIN] ); - res = run_extcmd(cmd_buf, err_buf, CMD_BUFSIZE, 0); + res = run_extcmd(cmd_buf, err_buf, CMD_BUFSIZE, WANT_STDERR, + NO_TIMEOUT, &pid_status, opts); if (opts->verbose) log_msg(LOG_INFO, "delete_all_chains() CMD: '%s' (res: %d, err: %s)", @@ -740,14 +742,15 @@ delete_all_chains(const fko_srv_options_t * const opts) zero_cmd_buffers(); - /* Create the NF_QUEUE chains and rules + /* Delete the NF_QUEUE chains and rules */ snprintf(cmd_buf, CMD_BUFSIZE-1, "%s " IPT_DEL_CHAIN_ARGS, fwc.fw_command, - NFQ_TABLE, - NFQ_CHAIN + opts->config[CONF_NFQ_TABLE], + opts->config[CONF_NFQ_CHAIN] ); - res = run_extcmd(cmd_buf, err_buf, CMD_BUFSIZE, 0); + res = run_extcmd(cmd_buf, err_buf, CMD_BUFSIZE, WANT_STDERR, + NO_TIMEOUT, &pid_status, opts); if (opts->verbose) log_msg(LOG_INFO, "delete_all_chains() CMD: '%s' (res: %d, err: %s)", @@ -817,6 +820,9 @@ static int create_fw_chains(const fko_srv_options_t * const opts) { int i, got_err = 0; +#if USE_LIBNETFILTER_QUEUE + int res = 0; +#endif for(i=0; i < NUM_FWKNOP_ACCESS_TYPES; i++) { @@ -835,10 +841,11 @@ create_fw_chains(const fko_srv_options_t * const opts) */ snprintf(cmd_buf, CMD_BUFSIZE-1, "%s " IPT_NEW_CHAIN_ARGS, fwc.fw_command, - NFQ_TABLE, - NFQ_CHAIN + opts->config[CONF_NFQ_TABLE], + opts->config[CONF_NFQ_CHAIN] ); - res = run_extcmd(cmd_buf, err_buf, CMD_BUFSIZE, 0); + res = run_extcmd(cmd_buf, err_buf, CMD_BUFSIZE, WANT_STDERR, + NO_TIMEOUT, &pid_status, opts); if (opts->verbose) log_msg(LOG_INFO, "create_fw_chains() CMD: '%s' (res: %d, err: %s)", @@ -857,12 +864,13 @@ create_fw_chains(const fko_srv_options_t * const opts) */ snprintf(cmd_buf, CMD_BUFSIZE-1, "%s " IPT_ADD_JUMP_RULE_ARGS, fwc.fw_command, - NFQ_TABLE, + opts->config[CONF_NFQ_TABLE], "INPUT", 1, - NFQ_CHAIN + opts->config[CONF_NFQ_CHAIN] ); - res = run_extcmd(cmd_buf, err_buf, CMD_BUFSIZE, 0); + res = run_extcmd(cmd_buf, err_buf, CMD_BUFSIZE, WANT_STDERR, + NO_TIMEOUT, &pid_status, opts); if (opts->verbose) log_msg(LOG_INFO, "create_fw_chains() CMD: '%s' (res: %d, err: %s)", @@ -878,15 +886,33 @@ create_fw_chains(const fko_srv_options_t * const opts) zero_cmd_buffers(); /* Create the rule to direct SPA packets to the queue. + * If an interface is specified use the "_WITH_IF" version + * of the command. */ - snprintf(cmd_buf, CMD_BUFSIZE-1, "%s " IPT_NFQ_ADD_ARGS, - fwc.fw_command, - NFQ_TABLE, - NFQ_CHAIN, - NFQ_PORT, - NFQ_QUEUE_NUM - ); - res = run_extcmd(cmd_buf, err_buf, CMD_BUFSIZE, 0); + if(strlen(opts->config[CONF_NFQ_INTERFACE]) > 0) + { + snprintf(cmd_buf, CMD_BUFSIZE-1, "%s " IPT_NFQ_ADD_ARGS_WITH_IF, + fwc.fw_command, + opts->config[CONF_NFQ_TABLE], + opts->config[CONF_NFQ_CHAIN], + opts->config[CONF_NFQ_INTERFACE], + opts->config[CONF_NFQ_PORT], + opts->config[CONF_NFQ_QUEUE_NUMBER] + ); + } + else + { + snprintf(cmd_buf, CMD_BUFSIZE-1, "%s " IPT_NFQ_ADD_ARGS, + fwc.fw_command, + opts->config[CONF_NFQ_TABLE], + opts->config[CONF_NFQ_CHAIN], + opts->config[CONF_NFQ_PORT], + opts->config[CONF_NFQ_QUEUE_NUMBER] + ); + } + + res = run_extcmd(cmd_buf, err_buf, CMD_BUFSIZE, WANT_STDERR, + NO_TIMEOUT, &pid_status, opts); if (opts->verbose) log_msg(LOG_INFO, "create_fw_chains() CMD: '%s' (res: %d, err: %s)", diff --git a/server/fw_util_iptables.h b/server/fw_util_iptables.h index 368d7950..a92a1960 100644 --- a/server/fw_util_iptables.h +++ b/server/fw_util_iptables.h @@ -66,8 +66,8 @@ #define IPT_ANY_IP "0.0.0.0/0" #if USE_LIBNETFILTER_QUEUE - #define IPT_NFQ_ADD_ARGS "-t %s -A %s -p udp -m udp --dport %i -j NFQUEUE --queue-num %i" - #define IPT_NFQ_DEL_ARGS "-t %s -D %s -p udp -m udp --dport %i -j NFQUEUE --queue-num %i" + #define IPT_NFQ_ADD_ARGS "-t %s -A %s -p udp -m udp --dport %s -j NFQUEUE --queue-num %s" + #define IPT_NFQ_ADD_ARGS_WITH_IF "-t %s -A %s -i %s -p udp -m udp --dport %s -j NFQUEUE --queue-num %s" #endif int validate_ipt_chain_conf(const char * const chain_str); diff --git a/server/fwknopd.c b/server/fwknopd.c index 8c2405d5..72aede82 100644 --- a/server/fwknopd.c +++ b/server/fwknopd.c @@ -255,9 +255,8 @@ main(int argc, char **argv) #if USE_LIBPCAP /* Intiate pcap capture mode... */ - if(!opts.enable_udp_server && !opts.enable_nfq_server - && strncasecmp(opts.config[CONF_ENABLE_UDP_SERVER], "N", 1) == 0 - && strncasecmp(opts.config[CONF_ENABLE_NFQ_SERVER], "N", 1) == 0) + if(!opts.enable_udp_server + && strncasecmp(opts.config[CONF_ENABLE_UDP_SERVER], "N", 1) == 0) { pcap_capture(&opts); } diff --git a/server/fwknopd.conf b/server/fwknopd.conf index ed2afeb1..65beda9c 100644 --- a/server/fwknopd.conf +++ b/server/fwknopd.conf @@ -52,6 +52,41 @@ # #PCAP_FILTER udp port 62201; +### Netfilter Queue (NFQ) Parameters ### +# +# These settings apply only if fwknopd was compiled with libnetfilter_queue +# support (configure with --enable-libnetfilter_queue). If this was not +# enabled, leave these commented out. +# +# Uncomment and set to "Y" to capture via libnetfilter_queue. This is the +# only option that must be set in order for NFQ capture. The remaining +# options have reasonable default values. +# +#ENABLE_NFQ_CAPTURE Y; + +# If you want to limit capture to a specific network interface, specify it +# here. If NFQ is enabled and this is left commented out, SPA packets will +# be captured on any/all network interfaces (which is the default). +# +#NFQ_INTERFACE eth0; + +# Specify the UDP port for incoming SPA packets (default is 62201). +# +#NFQ_PORT 62201; + +# Specify the iptable table for NFQ use (should stay the default of "mangle"). +# +#NFQ_TABLE mangle; + +# The name for the chain we will use for NFQ (default is "FWKNOP_NFQ"). +#NFQ_CHAIN + +# Specify the NFQ queue number. The default is "1". +# +#NFQ_QUEUE_NUMBER 1; +# +### End of Netfilter Queue (NFQ) Options ### + # This instructs fwknopd to not honor SPA packets that have an old time # stamp. The value for "old" is defined by the MAX_SPA_PACKET_AGE variable. # If ENABLE_SPA_PACKET_AGING is set to "N", fwknopd will not use the client diff --git a/server/fwknopd_common.h b/server/fwknopd_common.h index e5b691ca..3a24e5f3 100644 --- a/server/fwknopd_common.h +++ b/server/fwknopd_common.h @@ -113,6 +113,16 @@ #else #define DEF_ENABLE_UDP_SERVER "Y" #endif +#if USE_LIBNETFILTER_QUEUE + #define DEF_ENABLE_NFQ_CAPTURE "N" + #define DEF_NFQ_INTERFACE "" + #define DEF_NFQ_PORT "62201" + #define DEF_NFQ_TABLE "mangle" + #define DEF_NFQ_CHAIN "FWKNOP_NFQ" + #define DEF_NFQ_QUEUE_NUMBER "1" + #define DEF_CONF_NFQ_LOOP_SLEEP "500000" /* half a second (in microseconds) */ + +#endif #define DEF_UDPSERV_PORT "62201" #define DEF_UDPSERV_SELECT_TIMEOUT "500000" /* half a second (in microseconds) */ #define DEF_SYSLOG_IDENTITY MY_NAME @@ -250,6 +260,15 @@ enum { CONF_ENABLE_UDP_SERVER, CONF_UDPSERV_PORT, CONF_UDPSERV_SELECT_TIMEOUT, +#if USE_LIBNETFILTER_QUEUE + CONF_ENABLE_NFQ_CAPTURE, + CONF_NFQ_INTERFACE, + CONF_NFQ_PORT, + CONF_NFQ_TABLE, + CONF_NFQ_CHAIN, + CONF_NFQ_QUEUE_NUMBER, + CONF_NFQ_LOOP_SLEEP, +#endif CONF_LOCALE, CONF_SYSLOG_IDENTITY, CONF_SYSLOG_FACILITY, @@ -437,13 +456,6 @@ typedef struct cmd_cycle_list struct cmd_cycle_list *next; } cmd_cycle_list_t; -#if USE_LIBNETFILTER_QUEUE - #define NFQ_TABLE "mangle" - #define NFQ_CHAIN "FWKNOP_NFQ" - #define NFQ_QUEUE_NUM 1 - #define NFQ_PORT 62201 -#endif - /* Firewall-related data and types. */ #if FIREWALL_FIREWALLD @@ -633,6 +645,7 @@ typedef struct fko_srv_options unsigned char afl_fuzzing; /* SPA pkts from stdin for AFL fuzzing */ unsigned char verbose; /* Verbose mode flag */ unsigned char enable_udp_server; /* Enable UDP server mode */ + unsigned char enable_nfq_capture; /* Enable Netfilter Queue capture mode */ unsigned char enable_fw; /* Command modes by themselves don't need firewall support. */ diff --git a/server/nfq_capture.c b/server/nfq_capture.c new file mode 100755 index 00000000..11b6eda5 --- /dev/null +++ b/server/nfq_capture.c @@ -0,0 +1,291 @@ +/* + ***************************************************************************** + * + * File: nfq_capture.c + * + * Purpose: Capture routine for fwknopd that uses libnetfilter_queue. + * + * Fwknop is developed primarily by the people listed in the file 'AUTHORS'. + * Copyright (C) 2009-2014 fwknop developers and contributors. For a full + * list of contributors, see the file 'CREDITS'. + * + * License (GNU General Public License): + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + * USA + * + ***************************************************************************** +*/ +#include "fwknopd_common.h" +#include "nfq_capture.h" +#include "process_packet.h" +#include "sig_handler.h" +#include "fw_util.h" +#include "log_msg.h" +#include "fwknopd_errors.h" +#include "sig_handler.h" +#include "tcp_server.h" +#include +#if HAVE_SYS_WAIT_H + #include +#endif + +#include +#include +#include + +static int process_nfq_packet(struct nfq_q_handle *qh, + struct nfgenmsg *nfmsg, + struct nfq_data *nfa, + void *data) +{ + struct nfqnl_msg_packet_hdr *ph; + int pkt_len = 0; + int verdict; + unsigned char *full_packet; + fko_srv_options_t *opts = (fko_srv_options_t *)data; + + ph = nfq_get_msg_packet_hdr(nfa); + if (ph) { + + /* --DSS for ref + id = ntohl(ph->packet_id); + hook = ph->hook; + hw_proto = ph->protocol; + */ + + /* Retrieve packet payload + */ + pkt_len = nfq_get_payload(nfa, &full_packet); + + process_packet(opts, pkt_len, full_packet); + + /* Verdict on what to do with the packet. If it is coming from + * the INPUT chain (NF_IP_LOCAL_IN), then it is assumed to be + * a spa packet and can be dropped. Otherwise, let it through. + */ + verdict = (ph->hook == NF_IP_LOCAL_IN) ? NF_DROP : NF_ACCEPT; + nfq_set_verdict(qh, ph->packet_id, verdict, 0, NULL); + } + return 0; +} + + +/* The nfq capture routine. +*/ +int +nfq_capture(fko_srv_options_t *opts) +{ + int res, child_pid, fd_flags; + int nfq_errcnt = 0; + int pending_break = 0; + int status; + char nfq_buf[1500]; + int chk_rm_all = 0; + + /* Netfilter-related handles + */ + int nfq_fd; + struct nfq_handle *nfq_h; + struct nfq_q_handle *nfq_qh; + struct nfnl_handle *nfq_nh; + + nfq_h = nfq_open(); + if (!nfq_h) { + log_msg(LOG_ERR, "[*] nfq_open error\n"); + clean_exit(opts, FW_CLEANUP, EXIT_FAILURE); + } + + /* Unbind existing nf_queue handler for AF_INET (if any) + */ + res = nfq_unbind_pf(nfq_h, AF_INET); + if (res < 0) { + log_msg(LOG_WARNING, "[*] Error during nfq_unbind_pf() error: %d\n", res); + } + + /* Bind the given queue connection handle to process packets. + */ + res = nfq_bind_pf(nfq_h, AF_INET); + if ( res < 0) { + log_msg(LOG_ERR, "Error during nfq_bind_pf(), error: %d\n", res); + clean_exit(opts, FW_CLEANUP, EXIT_FAILURE); + } + + /* Create queue + */ + nfq_qh = nfq_create_queue(nfq_h, atoi(opts->config[CONF_NFQ_QUEUE_NUMBER]), &process_nfq_packet, opts); + if (!nfq_qh) { + log_msg(LOG_ERR, "Error during nfq_create_queue()\n"); + clean_exit(opts, FW_CLEANUP, EXIT_FAILURE); + } + + /* Set the amount of data to be copied to userspace for each packet + * queued to the given queue. + */ + if (nfq_set_mode(nfq_qh, NFQNL_COPY_PACKET, 0xffff) < 0) { + log_msg(LOG_ERR, "Can't set packet_copy mode\n"); + clean_exit(opts, FW_CLEANUP, EXIT_FAILURE); + } + + /* Get the netlink handle associated with the given queue connection + * handle. Then use it to get the file descriptor we will use for + * receiving the queued packets + */ + nfq_nh = nfq_nfnlh(nfq_h); + nfq_fd = nfnl_fd(nfq_nh); + + /* Set our nfq handle nonblocking mode. + * + */ + if((fd_flags = fcntl(nfq_fd, F_GETFL, 0)) < 0) + { + log_msg(LOG_ERR, "nfq_capture: fcntl F_GETFL error: %s", + strerror(errno)); + clean_exit(opts, FW_CLEANUP, EXIT_FAILURE); + } + + fd_flags |= O_NONBLOCK; + + if(fcntl(nfq_fd, F_SETFL, fd_flags) < 0) + { + log_msg(LOG_ERR, "nfq_capture: fcntl F_SETFL error setting O_NONBLOCK: %s", + strerror(errno)); + exit(EXIT_FAILURE); + } + + /* Initialize our signal handlers. You can check the return value for + * the number of signals that were *not* set. Those that were not set + * will be listed in the log/stderr output. + */ + if(set_sig_handlers() > 0) + log_msg(LOG_ERR, "Errors encountered when setting signal handlers."); + + log_msg(LOG_INFO, "Starting fwknopd main event loop."); + + /* Jump into our home-grown packet cature loop. + */ + while(1) + { + /* If we got a SIGCHLD and it was the tcp server, then handle it here. + ** XXX: --DSS Do we need this here? I'm guessing we would not be using + ** the TCP server in NF_QUEUE capture mode. + */ + if(got_sigchld) + { + if(opts->tcp_server_pid > 0) + { + child_pid = waitpid(0, &status, WNOHANG); + + if(child_pid == opts->tcp_server_pid) + { + if(WIFSIGNALED(status)) + log_msg(LOG_WARNING, "TCP server got signal: %i", WTERMSIG(status)); + + log_msg(LOG_WARNING, + "TCP server exited with status of %i. Attempting restart.", + WEXITSTATUS(status) + ); + + opts->tcp_server_pid = 0; + + /* Attempt to restart tcp server ? */ + usleep(1000000); + run_tcp_server(opts); + } + } + + got_sigchld = 0; + } + + /* Any signal except USR1, USR2, and SIGCHLD mean break the loop. + */ + if(got_signal != 0) + { + if(got_sigint || got_sigterm || got_sighup) + { + pending_break = 1; + } + else if(got_sigusr1 || got_sigusr2) + { + /* Not doing anything with these yet. + */ + got_sigusr1 = got_sigusr2 = 0; + got_signal = 0; + } + else + got_signal = 0; + } + + res = recv(nfq_fd, nfq_buf, sizeof(nfq_buf), 0); + + /* Count processed packets + */ + if(res > 0) + { + nfq_handle_packet(nfq_h, nfq_buf, res); + + /* Count the set of processed packets (nfq_dispatch() return + * value) - we use this as a comparison for --packet-limit regardless + * of SPA packet validity at this point. + */ + opts->packet_ctr += res; + if (opts->packet_ctr_limit && opts->packet_ctr >= opts->packet_ctr_limit) + { + log_msg(LOG_WARNING, + "* Incoming packet count limit of %i reached", + opts->packet_ctr_limit + ); + + pending_break = 1; + } + } + /* If there was an error, complain and go on (to an extent before + * giving up). + */ + else if(res < 0 && errno != EAGAIN) + { + + log_msg(LOG_ERR, "[*] Error reading from nfq descriptor: %s", strerror); + + if(nfq_errcnt++ > MAX_NFQ_ERRORS_BEFORE_BAIL) + { + log_msg(LOG_ERR, "[*] %i consecutive nfq errors. Giving up", + nfq_errcnt + ); + clean_exit(opts, FW_CLEANUP, EXIT_FAILURE); + } + + } + else if(pending_break == 1 || res == -2) + { + log_msg(LOG_INFO, "Gracefully leaving the fwknopd event loop."); + break; + } + else + nfq_errcnt = 0; + + /* Check for any expired firewall rules and deal with them. + */ + check_firewall_rules(opts, chk_rm_all); + + usleep(atoi(opts->config[CONF_NFQ_LOOP_SLEEP])); + } + + nfq_destroy_queue(nfq_qh); + nfq_close(nfq_h); + + return(0); +} +/***EOF***/ diff --git a/server/nfq_capture.h b/server/nfq_capture.h new file mode 100755 index 00000000..5d182f0c --- /dev/null +++ b/server/nfq_capture.h @@ -0,0 +1,43 @@ +/* + ***************************************************************************** + * + * File: nfq_capture.h + * + * Purpose: Header file for nfq_capture.c. + * + * Fwknop is developed primarily by the people listed in the file 'AUTHORS'. + * Copyright (C) 2009-2014 fwknop developers and contributors. For a full + * list of contributors, see the file 'CREDITS'. + * + * License (GNU General Public License): + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + * USA + * + ***************************************************************************** +*/ +#ifndef NFQ_CAPTURE_H +#define NFQ_CAPTURE_H + +/* How many consecutive capture errors will we allow + * before giving up and bailing out. +*/ +#define MAX_NFQ_ERRORS_BEFORE_BAIL 100 + +/* Prototypes +*/ +int nfq_capture(fko_srv_options_t *opts); + +#endif /* NFQ_CAPTURE_H */ diff --git a/server/process_packet.c b/server/process_packet.c index 5ccb5e0a..cd290e79 100644 --- a/server/process_packet.c +++ b/server/process_packet.c @@ -32,10 +32,6 @@ ***************************************************************************** */ -#if USE_LIBPCAP - #include -#endif - #include "fwknopd_common.h" #include "netinet_common.h" #include "process_packet.h" @@ -43,11 +39,10 @@ #include "utils.h" #include "log_msg.h" -#if USE_LIBPCAP void -process_packet(unsigned char *args, const struct pcap_pkthdr *packet_header, - const unsigned char *packet) +process_packet(PROCESS_PKT_ARGS_TYPE *args, PACKET_HEADER_META, + const unsigned char *packet) { struct ether_header *eth_p; struct iphdr *iph_p; @@ -75,8 +70,25 @@ process_packet(unsigned char *args, const struct pcap_pkthdr *packet_header, int offset = opts->data_link_offset; +#if USE_LIBPCAP unsigned short pkt_len = packet_header->len; + /* Gotta have a complete ethernet header. + */ + if (packet_header->caplen < ETHER_HDR_LEN) + return; + + /* Determine packet end. + */ + fr_end = (unsigned char *) packet + packet_header->caplen; +#else + /* This is coming from NFQ and we get the packet lentgh as an arg. + */ + if (pkt_len < ETHER_HDR_LEN) + return; + fr_end = (unsigned char *) packet + pkt_len; +#endif + /* This is a hack to determine if we are using the linux cooked * interface. We base it on the offset being 16 which is the * value it would be if the datalink is DLT_LINUX_SLL. I don't @@ -84,19 +96,10 @@ process_packet(unsigned char *args, const struct pcap_pkthdr *packet_header, */ unsigned char assume_cooked = (offset == 16 ? 1 : 0); - /* Determine packet end. - */ - fr_end = (unsigned char *) packet + packet_header->caplen; - /* The ethernet header. */ eth_p = (struct ether_header*) packet; - /* Gotta have a complete ethernet header. - */ - if (packet_header->caplen < ETHER_HDR_LEN) - return; - eth_type = ntohs(*((unsigned short*)ð_p->ether_type)); if(eth_type == 0x8100) /* 802.1q encapsulated */ @@ -229,6 +232,5 @@ process_packet(unsigned char *args, const struct pcap_pkthdr *packet_header, return; } -#endif /* USE_LIBPCAP */ /***EOF***/ diff --git a/server/process_packet.h b/server/process_packet.h index 0e8946cc..f2ab48fb 100644 --- a/server/process_packet.h +++ b/server/process_packet.h @@ -31,6 +31,15 @@ #ifndef PROCESS_PACKET_H #define PROCESS_PACKET_H +#if USE_LIBPCAP + #include + #define PACKET_HEADER_META const struct pcap_pkthdr *packet_header + #define PROCESS_PKT_ARGS_TYPE unsigned char +#else + #define PACKET_HEADER_META unsigned short pkt_len + #define PROCESS_PKT_ARGS_TYPE fko_srv_options_t +#endif + #define IPV4_VER_MASK 0x15 #define MIN_IPV4_WORDS 0x05 @@ -45,9 +54,7 @@ /* Prototypes */ -#if USE_LIBPCAP -void process_packet(unsigned char *args, - const struct pcap_pkthdr *packet_header, const unsigned char *packet); -#endif +void process_packet(PROCESS_PKT_ARGS_TYPE *opts, PACKET_HEADER_META, + const unsigned char *packet); #endif /* PROCESS_PACKET_H */