285 lines
8.4 KiB
C
285 lines
8.4 KiB
C
/**
|
|
* \file server/nfq_capture.c
|
|
*
|
|
* \brief 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 <fcntl.h>
|
|
#if HAVE_SYS_WAIT_H
|
|
#include <sys/wait.h>
|
|
#endif
|
|
|
|
#include <limits.h>
|
|
#include <linux/netfilter_ipv4.h>
|
|
#include <libnetfilter_queue/libnetfilter_queue.h>
|
|
|
|
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);
|
|
}
|
|
|
|
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***/
|