3 Copyright (C) 2014 Proxmox Server Solutions GmbH
5 This software is written by Proxmox Server Solutions GmbH <support@proxmox.com>
7 This program is free software: you can redistribute it and/or modify
8 it under the terms of the GNU Affero General Public License as published by
9 the Free Software Foundation, either version 3 of the License, or
10 (at your option) any later version.
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU Affero General Public License for more details.
17 You should have received a copy of the GNU Affero General Public License
18 along with this program. If not, see <http://www.gnu.org/licenses/>.
20 Author: Dietmar Maurer <dietmar@proxmox.com>
32 #include <sys/types.h>
36 #include <arpa/inet.h>
37 #include <sys/socket.h>
39 #include <linux/netlink.h>
40 #include <libnfnetlink/libnfnetlink.h>
41 #include <libnetfilter_log/libnetfilter_log.h>
42 #include <netinet/ip.h>
43 #include <netinet/ip_icmp.h>
44 #include <netinet/udp.h>
45 #include <netinet/tcp.h>
46 #include <netinet/if_ether.h>
50 #include <glib-unix.h>
52 static struct nflog_handle *logh = NULL;
53 static struct nlif_handle *nlifh = NULL;
60 Special care was taken to allow fast parsing (and filer messages for a singl VM).
62 <VMID> <LOGLEVEL> <CHAIN> <TIME> <TIMEZONE> <MSG>
66 117 6 tap117i0-IN 14/Mar/2014:12:47:07 +0100 policy REJECT: IN=vmbr1 ...
70 #define LOGFILE "/var/log/pve-firewall.log"
72 #define LOCKFILE "/var/lock/pvefw-logger.lck"
73 #define PIDFILE "/var/run/pvefw-logger.pid"
76 #define LE_MAX (512 - 16) // try to fit into 512 bytes
78 #define MAX_CHAIN_LEN 28
81 guint32 len; // max LE_MAX chars
87 gboolean terminate_threads = FALSE;
89 static gboolean write_pidfile(pid_t pid)
93 char *strpid = g_strdup_printf("%d\n", pid);
94 res = g_file_set_contents(PIDFILE, strpid, strlen(strpid), NULL);
100 static GAsyncQueue *queue;
103 safe_write(int fd, char *buf, size_t count)
108 n = write(fd, buf, count);
109 } while (n < 0 && errno == EINTR);
115 log_writer_thread(gpointer data)
118 struct log_entry *le = (struct log_entry *)g_async_queue_timeout_pop(queue, 250000);
120 if (terminate_threads) {
126 int res = safe_write(outfd, le->buf, le->len);
131 syslog(3, "writing log failed, stopping daemon - %s", strerror (errno));
132 g_main_loop_quit(main_loop);
140 static int skipped_logs = 0;
142 static void log_status_message(guint loglevel, const char *fmt, ...);
145 queue_log_entry(struct log_entry *le)
147 gint len = g_async_queue_length(queue);
149 if (skipped_logs > 0) {
150 if (len >= (LQ_LEN - 1)) {
153 int skip_tmp = skipped_logs;
154 skipped_logs = 0; // clear before calling log_status_message()
155 log_status_message(3, "skipped %d log entries (queue full)", skip_tmp);
156 g_async_queue_push(queue, le);
162 g_async_queue_push(queue, le);
168 #define LEPRINTF(format, ...) { if (le->len < LE_MAX) le->len += snprintf(le->buf + le->len, LE_MAX - le->len, format, ##__VA_ARGS__); }
169 #define LEPRINTTIME(sec) { time_t tmp_sec = sec; if (le->len < (LE_MAX - 30)) le->len += strftime(le->buf + le->len, LE_MAX - le->len, "%d/%b/%Y:%H:%M:%S %z ", localtime(&tmp_sec)); }
172 log_status_message(guint loglevel, const char *fmt, ...)
177 if (loglevel > 7 ) loglevel = 7; // syslog defines level 0-7
179 struct log_entry *le = g_new0(struct log_entry, 1);
181 LEPRINTF("0 %d - ", loglevel);
183 LEPRINTTIME(time(NULL));
185 le->len += vsnprintf(le->buf + le->len, LE_MAX - le->len, fmt, ap);
191 // also log to syslog
193 vsyslog(loglevel, fmt, ap);
197 print_tcp(struct log_entry *le, struct tcphdr *h, int payload_len)
199 LEPRINTF("PROTO=TCP ");
201 if (payload_len < sizeof(struct tcphdr)) {
202 LEPRINTF("LEN=%d ", payload_len);
203 LEPRINTF("INVALID=LEN ");
207 LEPRINTF("SPT=%u DPT=%u ", ntohs(h->source), ntohs(h->dest));
208 LEPRINTF("SEQ=%u ACK=%u ", ntohl(h->seq), ntohl(h->ack_seq));
209 LEPRINTF("WINDOW=%u ", ntohs(h->window));
211 if (h->urg) LEPRINTF("URG ");
212 if (h->ack) LEPRINTF("ACK ");
213 if (h->psh) LEPRINTF("PSH ");
214 if (h->rst) LEPRINTF("RST ");
215 if (h->syn) LEPRINTF("SYN ");
216 if (h->fin) LEPRINTF("FIN ");
218 if (h->urg) LEPRINTF("URGP=%u ",ntohs(h->urg_ptr));
224 print_udp(struct log_entry *le, struct udphdr *h, int payload_len)
226 LEPRINTF("PROTO=UDP ");
228 if (payload_len < sizeof(struct udphdr)) {
229 LEPRINTF("LEN=%d ", payload_len);
230 LEPRINTF("INVALID=LEN ");
234 LEPRINTF("SPT=%u DPT=%u LEN=%u", ntohs(h->source), ntohs(h->dest), ntohs(h->len));
240 print_icmp(struct log_entry *le, struct icmphdr *h, int payload_len)
242 char tmp[INET_ADDRSTRLEN];
245 LEPRINTF("PROTO=ICMP ");
247 if (payload_len < sizeof(struct icmphdr)) {
248 LEPRINTF("LEN=%d ", payload_len);
249 LEPRINTF("INVALID=LEN ");
253 LEPRINTF("TYPE=%u CODE=%u ", h->type, h->code);
258 LEPRINTF("ID=%u SEQ=%u ", ntohs(h->un.echo.id), ntohs(h->un.echo.sequence));
260 case ICMP_PARAMETERPROB:
261 LEPRINTF("PARAMETER=%u ", ntohl(h->un.gateway) >> 24);
264 gateway = ntohl(h->un.gateway);
265 inet_ntop(AF_INET, &gateway, tmp, sizeof(tmp));
266 LEPRINTF("GATEWAY=%s ", tmp);
268 case ICMP_DEST_UNREACH:
269 if (h->code == ICMP_FRAG_NEEDED) {
270 LEPRINTF("MTU=%u ", ntohs(h->un.frag.mtu));
278 /* Section 3.1. SCTP Common Header Format */
279 typedef struct sctphdr {
284 } __attribute__((packed)) sctp_sctphdr_t;
287 print_sctp(struct log_entry *le, struct sctphdr *h, int payload_len)
289 LEPRINTF("PROTO=SCTP ");
291 if (payload_len < sizeof(struct sctphdr)) {
292 LEPRINTF("LEN=%d ", payload_len);
293 LEPRINTF("INVALID=LEN ");
297 LEPRINTF("SPT=%u DPT=%u ", ntohs(h->source), ntohs(h->dest));
303 print_iphdr(struct log_entry *le, char * payload, int payload_len)
305 if (payload_len < sizeof(struct iphdr)) {
306 LEPRINTF("LEN=%d ", payload_len);
307 LEPRINTF("INVALID=LEN ");
311 struct iphdr *h = (struct iphdr *)payload;
313 if (payload_len <= (u_int32_t)(h->ihl * 4)) {
314 LEPRINTF("INVALID=IHL ");
318 char tmp[INET_ADDRSTRLEN];
320 inet_ntop(AF_INET, &h->saddr, tmp, sizeof(tmp));
321 LEPRINTF("SRC=%s ", tmp);
322 inet_ntop(AF_INET, &h->daddr, tmp, sizeof(tmp));
323 LEPRINTF("DST=%s ", tmp);
325 LEPRINTF("LEN=%u TOS=0x%02X PREC=0x%02X TTL=%u ID=%u ",
326 ntohs(h->tot_len), h->tos & IPTOS_TOS_MASK,
327 h->tos & IPTOS_PREC_MASK, h->ttl, ntohs(h->id));
329 short ip_off = ntohs(h->frag_off);
330 if (ip_off & IP_OFFMASK)
331 LEPRINTF("FRAG=%u ", ip_off & IP_OFFMASK);
333 if (ip_off & IP_DF) LEPRINTF("DF ");
334 if (ip_off & IP_MF) LEPRINTF("MF ");
336 void *nexthdr = (u_int32_t *)h + h->ihl;
337 payload_len -= h->ihl * 4;
339 switch (h->protocol) {
341 print_tcp(le, (struct tcphdr *)nexthdr, payload_len);
344 print_udp(le, (struct udphdr *)nexthdr, payload_len);
347 print_icmp(le, (struct icmphdr *)nexthdr, payload_len);
350 print_sctp(le, (struct sctphdr *)nexthdr, payload_len);
353 LEPRINTF("PROTO=AH ");
356 LEPRINTF("PROTO=ESP ");
359 LEPRINTF("PROTO=IGMP ");
362 LEPRINTF("PROTO=%u ", h->protocol);
369 print_ip6hdr(struct log_entry *le, char * payload, int payload_len)
371 LEPRINTF("IPV6 logging not implemented ");
376 // ebtables -I FORWARD --nflog --nflog-group 0
378 print_arp(struct log_entry *le, struct ether_arp *h, int payload_len)
380 if (payload_len < sizeof(struct ether_arp)) {
381 LEPRINTF("LEN=%d ", payload_len);
382 LEPRINTF("INVALID=LEN ");
386 LEPRINTF("SRC=%u.%u.%u.%u ", h->arp_spa[0], h->arp_spa[1],
387 h->arp_spa[2], h->arp_spa[3]);
389 LEPRINTF("DST=%u.%u.%u.%u ", h->arp_tpa[0], h->arp_tpa[1],
390 h->arp_tpa[2], h->arp_tpa[3]);
392 LEPRINTF("PROTO=ARP ");
394 unsigned short code = ntohs(h->arp_op);
397 LEPRINTF("REQUEST ");
400 LEPRINTF("REPLY MAC=%02x:%02x:%02x:%02x:%02x:%02x ",
401 h->arp_sha[0], h->arp_sha[1], h->arp_sha[2],
402 h->arp_sha[3], h->arp_sha[4], h->arp_sha[5]);
408 LEPRINTF("CODE=%u ", code);
412 // LEPRINTF("HTYPE=%u ", ntohs(h->arp_hrd));
414 // LEPRINTF("PTYPE=%u ", ntohs(h->arp_pro));
420 static int print_pkt(struct log_entry *le, struct nflog_data *ldata, u_int8_t family)
422 u_int32_t mark = nflog_get_nfmark(ldata);
423 u_int32_t indev = nflog_get_indev(ldata);
424 u_int32_t outdev = nflog_get_outdev(ldata);
425 u_int32_t physindev = nflog_get_physindev(ldata);
426 u_int32_t physoutdev = nflog_get_physoutdev(ldata);
428 char *prefix = nflog_get_prefix(ldata);
434 guint8 log_level = 6; // info
436 char *chain_name = "-";
438 if (prefix != NULL) {
439 // Note: parse ":$vmid:$loglevel:$chain: $msg"
440 if (prefix[0] == ':') {
441 char *p = prefix + 1;
443 while(*p >= '0' && *p <= '9') { tmpid *= 10; tmpid += *p - '0'; p++; }
446 (p[1] >= '0' && p[1] <= '7') &&
449 guint8 tmp_level = p[1] - '0'; // store for later use
450 char *chain_start = p + 3; // store for later use
452 while (*p && *p != ':' && *p != ' ') p++;
453 int len = p - chain_start;
455 if (*p == ':' && p[1] == ' ' && len && (len <= MAX_CHAIN_LEN)) {
456 // parsing successful
458 *p = 0; // terminate string
461 log_level = tmp_level;
462 chain_name = chain_start;
463 prefix = p + 2; // the rest
469 LEPRINTF("%d ", vmid);
471 LEPRINTF("%d ", log_level);
473 LEPRINTF("%s ", chain_name);
476 nflog_get_timestamp(ldata, &ts);
478 LEPRINTTIME(ts.tv_sec);
480 if (prefix != NULL) {
481 LEPRINTF("%s", prefix);
485 if (nlif_index2name(nlifh, indev, devname) != -1) {
486 LEPRINTF("IN=%s ", devname);
488 LEPRINTF("IN=%u ", indev);
493 if (nlif_index2name(nlifh, outdev, devname) != -1) {
494 LEPRINTF("OUT=%s ", devname);
496 LEPRINTF("OUT=%u ", outdev);
501 if (nlif_index2name(nlifh, physindev, devname) != -1) {
502 LEPRINTF("PHYSIN=%s ", devname);
504 LEPRINTF("PHYSIN=%u ", physindev);
508 if (physoutdev > 0) {
509 if (nlif_index2name(nlifh, physoutdev, devname) != -1) {
510 LEPRINTF("PHYSOUT=%s ", devname);
512 LEPRINTF("PHYSOUT=%u ", physoutdev);
516 int payload_len = nflog_get_payload(ldata, &payload);
518 int hwhdrlen = nflog_get_msg_packet_hwhdrlen(ldata);
520 unsigned char *hwhdr = (unsigned char *)nflog_get_msg_packet_hwhdr(ldata);
524 for (i = 0; i < hwhdrlen; i++) {
525 LEPRINTF("%02x", hwhdr[i]);
526 if (i < (hwhdrlen -1 )) LEPRINTF(":");
532 u_int16_t hw_protocol = 0;
533 struct nfulnl_msg_packet_hdr *ph = NULL;
537 print_iphdr(le, payload, payload_len);
540 print_ip6hdr(le, payload, payload_len);
543 ph = nflog_get_msg_packet_hdr(ldata);
544 if (ph) hw_protocol = ntohs(ph->hw_protocol);
546 switch (hw_protocol) {
548 print_iphdr(le, payload, payload_len);
551 print_ip6hdr(le, payload, payload_len);
554 print_arp(le, (struct ether_arp *)payload, payload_len);
560 if (mark) LEPRINTF("mark=%u ", mark);
568 nflog_cb(struct nflog_g_handle *gh, struct nfgenmsg *nfmsg,
569 struct nflog_data *nfa, void *data)
571 struct log_entry *le = g_new0(struct log_entry, 1);
573 print_pkt(le, nfa, nfmsg->nfgen_family);
575 LEPRINTF("\n"); // add newline
583 nflog_read_cb(GIOChannel *source,
584 GIOCondition condition,
590 int fd = g_io_channel_unix_get_fd(source);
592 if ((rv = recv(fd, buf, sizeof(buf), 0)) && rv >= 0) {
593 nflog_handle_packet(logh, buf, rv);
600 nlif_read_cb(GIOChannel *source,
601 GIOCondition condition,
604 static int last_res = 0;
607 if ((res = nlif_catch(nlifh)) < 0) {
608 if (last_res == 0) { // only report once
609 log_status_message(3, "nlif_catch failed (res = %d)", res);
620 terminate_request(gpointer data)
622 terminate_threads = TRUE;
624 log_status_message(5, "received terminate request (signal)");
626 g_main_loop_quit(main_loop);
633 main(int argc, char *argv[])
636 gboolean foreground = FALSE;
637 gboolean wrote_pidfile = FALSE;
641 openlog("pvepw-logger", LOG_CONS|LOG_PID, LOG_DAEMON);
643 if ((lockfd = open(LOCKFILE, O_RDWR|O_CREAT|O_APPEND, 0644)) == -1) {
644 fprintf(stderr, "unable to create lock '%s': %s", LOCKFILE, strerror (errno) );
648 for (int i = 10; i >= 0; i--) {
649 if (flock(lockfd, LOCK_EX|LOCK_NB) != 0) {
651 fprintf(stderr, "unable to aquire lock '%s': %s", LOCKFILE, strerror (errno));
655 fprintf(stderr, "unable to aquire lock '%s' - trying again.\n", LOCKFILE);
661 if ((outfd = open(LOGFILE, O_WRONLY|O_CREAT|O_APPEND, 0644)) == -1) {
662 fprintf(stderr, "unable to open file '%s': %s", LOGFILE, strerror (errno));
666 if ((logh = nflog_open()) == NULL) {
667 fprintf(stderr, "unable to open nflog\n");
671 if (!nflog_bind_pf(logh, AF_INET) <= 0) {
672 fprintf(stderr, "nflog_bind_pf AF_INET failed\n");
677 if (!nflog_bind_pf(logh, AF_INET6) <= 0) {
678 fprintf(stderr, "nflog_bind_pf AF_INET6 failed\n");
683 if (!nflog_bind_pf(logh, AF_BRIDGE) <= 0) {
684 fprintf(stderr, "nflog_bind_pf AF_BRIDGE failed\n");
688 struct nflog_g_handle *qh = nflog_bind_group(logh, 0);
690 fprintf(stderr, "no handle for group 1\n");
694 if (nflog_set_mode(qh, NFULNL_COPY_PACKET, 0xffff) < 0) {
695 fprintf(stderr, "can't set packet copy mode\n");
699 if ((nlifh = nlif_open()) == NULL) {
700 fprintf(stderr, "unable to open netlink interface handle\n");
708 fprintf(stderr, "failed to daemonize program - %s\n", strerror (errno));
716 if (chroot("/") != 0) fprintf(stderr, "chroot '/' failed - %s\n", strerror (errno));
718 if ((nullfd = open("/dev/null", O_RDWR, 0)) != -1) {
729 write_pidfile(getpid());
732 wrote_pidfile = TRUE;
734 nflog_callback_register(qh, &nflog_cb, logh);
736 queue = g_async_queue_new_full(g_free);
738 log_status_message(5, "starting pvefw logger");
742 GIOChannel *nlif_ch = g_io_channel_unix_new(nlif_fd(nlifh));
744 g_io_add_watch(nlif_ch, G_IO_IN, nlif_read_cb, NULL);
746 int logfd = nflog_fd(logh);
747 GIOChannel *nflog_ch = g_io_channel_unix_new(logfd);
749 g_io_add_watch(nflog_ch, G_IO_IN, nflog_read_cb, NULL);
751 GThread *wthread = g_thread_new("log_writer_thread", log_writer_thread, NULL);
753 main_loop = g_main_loop_new(NULL, TRUE);
755 g_unix_signal_add(SIGINT, terminate_request, NULL);
756 g_unix_signal_add(SIGTERM, terminate_request, NULL);
758 g_main_loop_run(main_loop);
760 log_status_message(5, "stopping pvefw logger");
762 g_thread_join(wthread);