#include <linux/netlink.h>
#include <libnfnetlink/libnfnetlink.h>
#include <libnetfilter_log/libnetfilter_log.h>
+#include <libnetfilter_conntrack/libnetfilter_conntrack.h>
#include <netinet/ip.h>
#include <netinet/ip_icmp.h>
#include <netinet/ip6.h>
static struct nflog_handle *logh = NULL;
static struct nlif_handle *nlifh = NULL;
+static struct nfct_handle *nfcth = NULL;
GMainLoop *main_loop;
gboolean foreground = FALSE;
gboolean debug = FALSE;
+gboolean conntrack = FALSE;
/*
#define LOCKFILE "/var/lock/pvefw-logger.lck"
#define PIDFILE "/var/run/pvefw-logger.pid"
+#define LOG_CONNTRACK_FILE "/var/lib/pve-firewall/log_nf_conntrack"
#define LQ_LEN 512
#define LE_MAX (512 - 4) // try to fit into 512 bytes
static void
log_status_message(guint loglevel, const char *fmt, ...)
{
- va_list ap;
- va_start(ap, fmt);
+ va_list ap, ap2;
if (loglevel > 7 ) loglevel = 7; // syslog defines level 0-7
LEPRINTTIME(time(NULL));
+ va_start(ap, fmt);
+ va_copy(ap2, ap);
le->len += vsnprintf(le->buf + le->len, LE_MAX - le->len, fmt, ap);
+ va_end(ap);
LEPRINTF("\n");
// also log to syslog
- vsyslog(loglevel, fmt, ap);
+ vsyslog(loglevel, fmt, ap2);
+ va_end(ap2);
}
static int
return 0;
struct ip6_ext *exthdr = (struct ip6_ext*)hdr;
+ int ext_len = 0;
switch (proto) {
/* protocols (these return) */
return -1;
if (print_fragment(le, (struct ip6_frag*)hdr, payload_len) < 0)
return -1;
+ ext_len = sizeof(struct ip6_frag);
break;
case IPPROTO_HOPOPTS:
LEPRINTF("NEXTHDR=HOPOPTS ");
/* next header: */
if (check_ip6ext(le, exthdr, payload_len) < 0)
return -1;
- hdr += exthdr->ip6e_len;
- payload_len -= exthdr->ip6e_len;
+ if(ext_len == 0) {
+ ext_len = (exthdr->ip6e_len+1) * 8;
+ }
+ hdr += ext_len;
+ payload_len -= ext_len;
+ proto = exthdr->ip6e_nxt;
}
}
LEPRINTF("%s ", chain_name);
struct timeval ts;
- nflog_get_timestamp(ldata, &ts);
-
- LEPRINTTIME(ts.tv_sec);
+ if (nflog_get_timestamp(ldata, &ts) == 0) {
+ LEPRINTTIME(ts.tv_sec);
+ } else {
+ LEPRINTTIME(time(NULL));
+ }
if (prefix != NULL) {
LEPRINTF("%s", prefix);
return TRUE;
}
+static int
+nfct_cb(const struct nlmsghdr *nlh,
+ enum nf_conntrack_msg_type type,
+ struct nf_conntrack *ct,
+ void *data)
+{
+ struct log_entry *le = g_new0(struct log_entry, 1);
+ int len = nfct_snprintf(&le->buf[le->len], LE_MAX - le->len,
+ ct, type, NFCT_O_DEFAULT,
+ NFCT_OF_SHOW_LAYER3|NFCT_OF_TIMESTAMP);
+ le->len += len;
+
+ if (le->len == LE_MAX) {
+ le->buf[le->len-1] = '\n';
+ } else { // le->len < LE_MAX
+ le->buf[le->len++] = '\n';
+ }
+
+ queue_log_entry(le);
+
+ return NFCT_CB_STOP;
+}
+
+static gboolean
+nfct_read_cb(GIOChannel *source,
+ GIOCondition condition,
+ gpointer data)
+{
+ int res;
+ if ((res = nfct_catch(nfcth)) < 0) {
+ if (errno == ENOBUFS) {
+ log_status_message(3, "nfct_catch returned ENOBUFS: conntrack information may be incomplete");
+ } else {
+ log_status_message(3, "error catching nfct: %s", strerror(errno));
+ return FALSE;
+ }
+ }
+ return TRUE;
+}
+
int
main(int argc, char *argv[])
{
gboolean wrote_pidfile = FALSE;
- openlog("pvepw-logger", LOG_CONS|LOG_PID, LOG_DAEMON);
+ openlog("pvefw-logger", LOG_CONS|LOG_PID, LOG_DAEMON);
GOptionContext *context;
GOptionEntry entries[] = {
{ "debug", 'd', 0, G_OPTION_ARG_NONE, &debug, "Turn on debug messages", NULL },
{ "foreground", 'f', 0, G_OPTION_ARG_NONE, &foreground, "Do not daemonize server", NULL },
+ { "conntrack", 0, 0, G_OPTION_ARG_NONE, &conntrack, "Add conntrack logging", NULL },
{ NULL },
};
g_option_context_free(context);
+ if (!conntrack) {
+ int log_nf_conntrackfd = open(LOG_CONNTRACK_FILE, O_RDONLY);
+ if (log_nf_conntrackfd == -1) {
+ if (errno != ENOENT) {
+ fprintf(stderr, "error: failed to open "LOG_CONNTRACK_FILE": %s\n", strerror(errno));
+ }
+ } else {
+ char c = '0';
+ ssize_t bytes = read(log_nf_conntrackfd, &c, sizeof(c));
+ if (bytes < 0) {
+ fprintf(stderr, "error: failed to read value in log_nf_conntrack: %s\n", strerror(errno));
+ } else {
+ conntrack = (c == '1');
+ }
+ }
+ }
+
if (debug) foreground = TRUE;
if ((lockfd = open(LOCKFILE, O_RDWR|O_CREAT|O_APPEND, 0644)) == -1) {
for (int i = 10; i >= 0; i--) {
if (flock(lockfd, LOCK_EX|LOCK_NB) != 0) {
if (!i) {
- fprintf(stderr, "unable to aquire lock '%s': %s\n", LOCKFILE, strerror (errno));
+ fprintf(stderr, "unable to acquire lock '%s': %s\n", LOCKFILE, strerror (errno));
exit(-1);
}
if (i == 10)
- fprintf(stderr, "unable to aquire lock '%s' - trying again.\n", LOCKFILE);
+ fprintf(stderr, "unable to acquire lock '%s' - trying again.\n", LOCKFILE);
sleep(1);
}
exit(-1);
}
+ if (conntrack) {
+ if ((nfcth = nfct_open(CONNTRACK, NF_NETLINK_CONNTRACK_NEW|NF_NETLINK_CONNTRACK_DESTROY)) == NULL) {
+ fprintf(stderr, "unable to open netfilter conntrack\n");
+ exit(-1);
+ }
+ }
+
sigset_t mask;
sigemptyset(&mask);
sigaddset(&mask, SIGINT);
g_io_add_watch(nflog_ch, G_IO_IN, nflog_read_cb, NULL);
+ if (conntrack) {
+ nfct_callback_register2(nfcth, NFCT_T_NEW|NFCT_T_DESTROY, &nfct_cb, NULL);
+ int nfctfd = nfct_fd(nfcth);
+ GIOChannel *nfct_ch = g_io_channel_unix_new(nfctfd);
+ g_io_add_watch(nfct_ch, G_IO_IN, nfct_read_cb, NULL);
+ }
+
GIOChannel *sig_ch = g_io_channel_unix_new(sigfd);
if (!g_io_add_watch(sig_ch, G_IO_IN, signal_read_cb, NULL)) {
exit(-1);
close(outfd);
+ if (conntrack) {
+ nfct_callback_unregister2(nfcth);
+ nfct_close(nfcth);
+ }
+
nflog_close(logh);
if (wrote_pidfile)