diff --git a/configure.ac b/configure.ac index 52e470c2..0e72f22c 100644 --- a/configure.ac +++ b/configure.ac @@ -239,6 +239,16 @@ AC_ARG_ENABLE([udp-server], []) 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], + [Enable NF_QUEUE server mode for no libpcap dependency @<:@default is to disable@:>@])], + [want_nfq_capture=$enableval], + []) +AM_CONDITIONAL([NFQ_CAPTURE], [test "$want_nfq_capture" = yes]) + dnl Decide whether or not to enable all warnings with -Wall dnl use_wall=yes @@ -517,7 +527,7 @@ dnl Check for libpcap, gdbm (or ndbm) if we are building the server component dnl AS_IF([test "$want_server" = yes], [ - AS_IF([test "$want_udp_server" = no], [ + AS_IF([test "$want_udp_server" = no -a "$want_nfq_capture" = no], [ # Looking for libpcap # AC_CHECK_LIB([pcap],[pcap_open_live], @@ -526,6 +536,15 @@ AS_IF([test "$want_server" = yes], [ ) ]) + AS_IF([test "$want_nfq_capture" = yes], [ + # Check for libnetfilter_queue + # + AC_CHECK_LIB([netfilter_queue],[nfq_open], + [ AC_DEFINE([USE_LIBNETFILTER_QUEUE], [1], [Define if you have libnetfilter_queue]) ], + [ AC_MSG_ERROR([fwknopd needs libnetfilter_queue])] + ) + ]) + AS_IF([test "$want_digest_cache" = yes], [ use_ndbm=no have_digest_cache=yes @@ -785,6 +804,10 @@ if [test "$want_server" = "yes" ]; then " if [test "$want_udp_server" = "yes" ]; then echo " UDP server mode enabled, no libpcap dependency +" + fi +if [test "$want_nfq_capture" = "yes" ]; then + echo " Netfilter Queue server mode enabled, no libpcap dependency " fi diff --git a/server/Makefile.am b/server/Makefile.am index b9cda03e..38c5571e 100644 --- a/server/Makefile.am +++ b/server/Makefile.am @@ -16,6 +16,10 @@ BASE_SOURCE_FILES = fwknopd.h config_init.c config_init.h \ fwknopd_SOURCES = fwknopd.c $(BASE_SOURCE_FILES) +if NFQ_CAPTURE + fwknopd_SOURCES += nfq_capture.c nfq_capture.h +endif + fwknopd_LDADD = $(top_builddir)/lib/libfko.la $(top_builddir)/common/libfko_util.a if WANT_C_UNIT_TESTS @@ -25,15 +29,24 @@ if WANT_C_UNIT_TESTS fwknopd_utests_LDADD = $(top_builddir)/lib/libfko.la $(top_builddir)/common/libfko_util.a fwknopd_utests_LDFLAGS = -lcunit $(GPGME_LIBS) +if NFQ_CAPTURE + fwknop_utests_LDFLAGS += -lnetfilter_queue +else if !UDP_SERVER fwknopd_utests_LDFLAGS += -lpcap endif +endif endif +if NFQ_CAPTURE + fwknop_LDADD += -lnetfilter_queue +else if !UDP_SERVER fwknopd_LDADD += -lpcap endif +endif + if !CONFIG_FILE_CACHE if USE_NDBM diff --git a/server/cmd_opts.h b/server/cmd_opts.h index cd39a7c1..3af9adbc 100644 --- a/server/cmd_opts.h +++ b/server/cmd_opts.h @@ -166,7 +166,11 @@ enum { /* Our getopt_long options string. */ -#define GETOPTS_OPTION_STRING "Aa:c:C:d:Dfhi:Kl:O:p:P:Rr:StUvV" +#if USE_LIBNETFILTER_QUEUE + #define GETOPTS_OPTION_STRING "Aa:c:C:d:Dfhi:Kl:nO:p:P:Rr:StUvV" +#else + #define GETOPTS_OPTION_STRING "Aa:c:C:d:Dfhi:Kl:O:p:P:Rr:StUvV" +#endif /* Our program command-line options... */ @@ -200,6 +204,9 @@ static struct option cmd_opts[] = {"no-firewd-check-support", 0, NULL, FIREWD_DISABLE_CHECK_SUPPORT }, {"no-ipt-check-support", 0, NULL, IPT_DISABLE_CHECK_SUPPORT }, {"locale", 1, NULL, 'l' }, +#if USE_LIBNETFILTER_QUEUE + {"nfq-capture", 0, NULL, 'n' }, +#endif {"rotate-digest-cache", 0, NULL, ROTATE_DIGEST_CACHE }, {"override-config", 1, NULL, 'O' }, {"pcap-file", 1, NULL, PCAP_FILE }, diff --git a/server/config_init.c b/server/config_init.c index d7b50440..1686a42c 100644 --- a/server/config_init.c +++ b/server/config_init.c @@ -1295,6 +1295,11 @@ config_init(fko_srv_options_t *opts, int argc, char **argv) case 'l': set_config_entry(opts, CONF_LOCALE, optarg); break; +#if USE_LIBNETFILTER_QUEUE + case 'n': + opts->enable_nfq_capture = 1; + break; +#endif case 'O': /* This was handled earlier */ break; diff --git a/server/fw_util_firewalld.c b/server/fw_util_firewalld.c index c72d7b1e..0cf3724e 100644 --- a/server/fw_util_firewalld.c +++ b/server/fw_util_firewalld.c @@ -702,6 +702,68 @@ delete_all_chains(const fko_srv_options_t * const opts) log_msg(LOG_ERR, "delete_all_chains() Error %i from cmd:'%s': %s", res, cmd_buf, err_buf); +#if USE_LIBNETFILTER_QUEUE + if(opts->enable_nfq_capture) + { + zero_cmd_buffers(); + + /* Delete the rule to direct traffic to the NFQ chain. + */ + snprintf(cmd_buf, CMD_BUFSIZE-1, "%s " IPT_DEL_RULE_ARGS, + fwc.fw_command, + NFQ_TABLE, + "INPUT", + 1 + ); + res = run_extcmd(cmd_buf, err_buf, CMD_BUFSIZE, 0); + + if (opts->verbose) + log_msg(LOG_INFO, "delete_all_chains() CMD: '%s' (res: %d, err: %s)", + cmd_buf, res, err_buf); + + /* Expect full success on this */ + if(! EXTCMD_IS_SUCCESS(res)) + log_msg(LOG_ERR, "Error %i from cmd:'%s': %s", res, cmd_buf, err_buf); + + zero_cmd_buffers(); + + /* Flush the NFQ chain + */ + snprintf(cmd_buf, CMD_BUFSIZE-1, "%s " IPT_FLUSH_CHAIN_ARGS, + fwc.fw_command, + NFQ_TABLE, + NFQ_CHAIN + ); + res = run_extcmd(cmd_buf, err_buf, CMD_BUFSIZE, 0); + + if (opts->verbose) + log_msg(LOG_INFO, "delete_all_chains() CMD: '%s' (res: %d, err: %s)", + cmd_buf, res, err_buf); + + /* Expect full success on this */ + if(! EXTCMD_IS_SUCCESS(res)) + log_msg(LOG_ERR, "Error %i from cmd:'%s': %s", res, cmd_buf, err_buf); + + zero_cmd_buffers(); + + /* Create the NF_QUEUE chains and rules + */ + snprintf(cmd_buf, CMD_BUFSIZE-1, "%s " IPT_DEL_CHAIN_ARGS, + fwc.fw_command, + NFQ_TABLE, + NFQ_CHAIN + ); + res = run_extcmd(cmd_buf, err_buf, CMD_BUFSIZE, 0); + + if (opts->verbose) + log_msg(LOG_INFO, "delete_all_chains() CMD: '%s' (res: %d, err: %s)", + cmd_buf, res, err_buf); + + /* Expect full success on this */ + if(! EXTCMD_IS_SUCCESS(res)) + log_msg(LOG_ERR, "Error %i from cmd:'%s': %s", res, cmd_buf, err_buf); + } +#endif } return; } @@ -770,7 +832,80 @@ create_fw_chains(const fko_srv_options_t * const opts) got_err += mk_chain(opts, i); } +#if USE_LIBNETFILTER_QUEUE + if(opts->enable_nfq_capture) + { + zero_cmd_buffers(); + /* Create the NF_QUEUE chains and rules + */ + snprintf(cmd_buf, CMD_BUFSIZE-1, "%s " IPT_NEW_CHAIN_ARGS, + fwc.fw_command, + NFQ_TABLE, + NFQ_CHAIN + ); + res = run_extcmd(cmd_buf, err_buf, CMD_BUFSIZE, 0); + + if (opts->verbose) + log_msg(LOG_INFO, "create_fw_chains() CMD: '%s' (res: %d, err: %s)", + cmd_buf, res, err_buf); + + /* Expect full success on this */ + if(! EXTCMD_IS_SUCCESS(res)) + { + log_msg(LOG_ERR, "Error %i from cmd:'%s': %s", res, cmd_buf, err_buf); + got_err++; + } + + zero_cmd_buffers(); + + /* Create the rule to direct traffic to the NFQ chain. + */ + snprintf(cmd_buf, CMD_BUFSIZE-1, "%s " IPT_ADD_JUMP_RULE_ARGS, + fwc.fw_command, + NFQ_TABLE, + "INPUT", + 1, + NFQ_CHAIN + ); + res = run_extcmd(cmd_buf, err_buf, CMD_BUFSIZE, 0); + + if (opts->verbose) + log_msg(LOG_INFO, "create_fw_chains() CMD: '%s' (res: %d, err: %s)", + cmd_buf, res, err_buf); + + /* Expect full success on this */ + if(! EXTCMD_IS_SUCCESS(res)) + { + log_msg(LOG_ERR, "Error %i from cmd:'%s': %s", res, cmd_buf, err_buf); + got_err++; + } + + zero_cmd_buffers(); + + /* Create the rule to direct SPA packets to the queue. + */ + 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 (opts->verbose) + log_msg(LOG_INFO, "create_fw_chains() CMD: '%s' (res: %d, err: %s)", + cmd_buf, res, err_buf); + + /* Expect full success on this */ + if(! EXTCMD_IS_SUCCESS(res)) + { + log_msg(LOG_ERR, "Error %i from cmd:'%s': %s", res, cmd_buf, err_buf); + got_err++; + } + } +#endif return(got_err); } diff --git a/server/fw_util_firewalld.h b/server/fw_util_firewalld.h index fdac0dcb..1eaa7d63 100644 --- a/server/fw_util_firewalld.h +++ b/server/fw_util_firewalld.h @@ -68,6 +68,11 @@ #define FIREWD_LIST_ALL_RULES_ARGS "-t %s -v -n -L --line-numbers" SH_REDIR #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" +#endif + int validate_firewd_chain_conf(const char * const chain_str); #endif /* FW_UTIL_FIREWALLD_H */ diff --git a/server/fw_util_iptables.c b/server/fw_util_iptables.c index a8fbb6f5..08b3c162 100644 --- a/server/fw_util_iptables.c +++ b/server/fw_util_iptables.c @@ -695,6 +695,69 @@ delete_all_chains(const fko_srv_options_t * const opts) log_msg(LOG_ERR, "delete_all_chains() Error %i from cmd:'%s': %s", res, cmd_buf, err_buf); } + +#if USE_LIBNETFILTER_QUEUE + if(opts->enable_nfq_capture) + { + zero_cmd_buffers(); + + /* Delete the rule to direct traffic to the NFQ chain. + */ + snprintf(cmd_buf, CMD_BUFSIZE-1, "%s " IPT_DEL_RULE_ARGS, + fwc.fw_command, + NFQ_TABLE, + "INPUT", + 1 + ); + res = run_extcmd(cmd_buf, err_buf, CMD_BUFSIZE, 0); + + if (opts->verbose) + log_msg(LOG_INFO, "delete_all_chains() CMD: '%s' (res: %d, err: %s)", + cmd_buf, res, err_buf); + + /* Expect full success on this */ + if(! EXTCMD_IS_SUCCESS(res)) + log_msg(LOG_ERR, "Error %i from cmd:'%s': %s", res, cmd_buf, err_buf); + + zero_cmd_buffers(); + + /* Flush the NFQ chain + */ + snprintf(cmd_buf, CMD_BUFSIZE-1, "%s " IPT_FLUSH_CHAIN_ARGS, + fwc.fw_command, + NFQ_TABLE, + NFQ_CHAIN + ); + res = run_extcmd(cmd_buf, err_buf, CMD_BUFSIZE, 0); + + if (opts->verbose) + log_msg(LOG_INFO, "delete_all_chains() CMD: '%s' (res: %d, err: %s)", + cmd_buf, res, err_buf); + + /* Expect full success on this */ + if(! EXTCMD_IS_SUCCESS(res)) + log_msg(LOG_ERR, "Error %i from cmd:'%s': %s", res, cmd_buf, err_buf); + + zero_cmd_buffers(); + + /* Create the NF_QUEUE chains and rules + */ + snprintf(cmd_buf, CMD_BUFSIZE-1, "%s " IPT_DEL_CHAIN_ARGS, + fwc.fw_command, + NFQ_TABLE, + NFQ_CHAIN + ); + res = run_extcmd(cmd_buf, err_buf, CMD_BUFSIZE, 0); + + if (opts->verbose) + log_msg(LOG_INFO, "delete_all_chains() CMD: '%s' (res: %d, err: %s)", + cmd_buf, res, err_buf); + + /* Expect full success on this */ + if(! EXTCMD_IS_SUCCESS(res)) + log_msg(LOG_ERR, "Error %i from cmd:'%s': %s", res, cmd_buf, err_buf); + } +#endif return; } @@ -763,6 +826,80 @@ create_fw_chains(const fko_srv_options_t * const opts) got_err += mk_chain(opts, i); } +#if USE_LIBNETFILTER_QUEUE + if(opts->enable_nfq_capture) + { + zero_cmd_buffers(); + + /* Create the NF_QUEUE chains and rules + */ + snprintf(cmd_buf, CMD_BUFSIZE-1, "%s " IPT_NEW_CHAIN_ARGS, + fwc.fw_command, + NFQ_TABLE, + NFQ_CHAIN + ); + res = run_extcmd(cmd_buf, err_buf, CMD_BUFSIZE, 0); + + if (opts->verbose) + log_msg(LOG_INFO, "create_fw_chains() CMD: '%s' (res: %d, err: %s)", + cmd_buf, res, err_buf); + + /* Expect full success on this */ + if(! EXTCMD_IS_SUCCESS(res)) + { + log_msg(LOG_ERR, "Error %i from cmd:'%s': %s", res, cmd_buf, err_buf); + got_err++; + } + + zero_cmd_buffers(); + + /* Create the rule to direct traffic to the NFQ chain. + */ + snprintf(cmd_buf, CMD_BUFSIZE-1, "%s " IPT_ADD_JUMP_RULE_ARGS, + fwc.fw_command, + NFQ_TABLE, + "INPUT", + 1, + NFQ_CHAIN + ); + res = run_extcmd(cmd_buf, err_buf, CMD_BUFSIZE, 0); + + if (opts->verbose) + log_msg(LOG_INFO, "create_fw_chains() CMD: '%s' (res: %d, err: %s)", + cmd_buf, res, err_buf); + + /* Expect full success on this */ + if(! EXTCMD_IS_SUCCESS(res)) + { + log_msg(LOG_ERR, "Error %i from cmd:'%s': %s", res, cmd_buf, err_buf); + got_err++; + } + + zero_cmd_buffers(); + + /* Create the rule to direct SPA packets to the queue. + */ + 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 (opts->verbose) + log_msg(LOG_INFO, "create_fw_chains() CMD: '%s' (res: %d, err: %s)", + cmd_buf, res, err_buf); + + /* Expect full success on this */ + if(! EXTCMD_IS_SUCCESS(res)) + { + log_msg(LOG_ERR, "Error %i from cmd:'%s': %s", res, cmd_buf, err_buf); + got_err++; + } + } +#endif return(got_err); } diff --git a/server/fw_util_iptables.h b/server/fw_util_iptables.h index 223c4d1b..368d7950 100644 --- a/server/fw_util_iptables.h +++ b/server/fw_util_iptables.h @@ -65,6 +65,11 @@ #define IPT_LIST_ALL_RULES_ARGS "-t %s -v -n -L --line-numbers" SH_REDIR #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" +#endif + int validate_ipt_chain_conf(const char * const chain_str); #endif /* FW_UTIL_IPTABLES_H */ diff --git a/server/fwknopd.c b/server/fwknopd.c index b5bc2115..8c2405d5 100644 --- a/server/fwknopd.c +++ b/server/fwknopd.c @@ -39,6 +39,9 @@ #include "tcp_server.h" #include "udp_server.h" +#if USE_LIBNETFILTER_QUEUE + #include "nfq_capture.h" +#endif #if USE_LIBPCAP #include "pcap_capture.h" #endif @@ -206,6 +209,16 @@ main(int argc, char **argv) if(!opts.test && opts.enable_fw && (fw_initialize(&opts) != 1)) clean_exit(&opts, FW_CLEANUP, EXIT_FAILURE); +#if USE_LIBNETFILTER_QUEUE + /* If we are to acquire SPA data via a libnetfilter_queue, start it up here. + */ + if(opts.enable_nfq_capture || + strncasecmp(opts.config[CONF_ENABLE_NFQ_CAPTURE], "Y", 1) == 0) + { + nfq_capture(&opts); + } + else +#endif /* If we are to acquire SPA data via a UDP socket, start it up here. */ if(opts.enable_udp_server || @@ -242,11 +255,17 @@ main(int argc, char **argv) #if USE_LIBPCAP /* Intiate pcap capture mode... */ - if(!opts.enable_udp_server - && strncasecmp(opts.config[CONF_ENABLE_UDP_SERVER], "N", 1) == 0) + 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) { pcap_capture(&opts); } + else + { + log_msg(LOG_ERR, "No available capture mode specified. Aborting."); + clean_exit(&opts, FW_CLEANUP, EXIT_FAILURE); + } #endif /* Deal with any signals that we've received and break out diff --git a/server/fwknopd_common.h b/server/fwknopd_common.h index 926ee92d..e5b691ca 100644 --- a/server/fwknopd_common.h +++ b/server/fwknopd_common.h @@ -437,6 +437,13 @@ 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