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>
49 #include <glib-unix.h>
51 static struct nflog_handle
*logh
= NULL
;
52 static struct nlif_handle
*nlifh
= NULL
;
58 Special care was taken to allow fast parsing (and filer messages for a singl VM).
60 <VMID> <LOGLEVEL> <CHAIN> <TIME> <TIMEZONE> <MSG>
64 117 6 tap117i0-IN 14/Mar/2014:12:47:07 +0100 policy REJECT: IN=vmbr1 ...
68 #define LOGFILE "/var/log/pve-firewall.log"
70 #define LOCKFILE "/var/lock/pvefw-logger.lck"
71 #define PIDFILE "/var/run/pvefw-logger.pid"
74 #define LE_MAX (512 - 16) // try to fit into 512 bytes
76 #define MAX_CHAIN_LEN 28
79 guint32 len
; // max LE_MAX chars
85 gboolean terminate_threads
= FALSE
;
87 static gboolean
write_pidfile(pid_t pid
)
91 char *strpid
= g_strdup_printf("%d\n", pid
);
92 res
= g_file_set_contents(PIDFILE
, strpid
, strlen(strpid
), NULL
);
98 static GAsyncQueue
*queue
;
101 safe_write(int fd
, char *buf
, size_t count
)
106 n
= write(fd
, buf
, count
);
107 } while (n
< 0 && errno
== EINTR
);
113 log_writer_thread(gpointer data
)
116 struct log_entry
*le
= (struct log_entry
*)g_async_queue_timeout_pop(queue
, 250000);
118 if (terminate_threads
) {
124 printf("%s", le
->buf
);
125 int res
= safe_write(outfd
, le
->buf
, le
->len
);
130 // printf("write failed\n"); // fixme??
137 static int skipped_logs
= 0;
139 static void log_status_message(guint loglevel
, const char *fmt
, ...);
142 queue_log_entry(struct log_entry
*le
)
144 gint len
= g_async_queue_length(queue
);
146 if (skipped_logs
> 0) {
147 if (len
>= (LQ_LEN
- 1)) {
150 int skip_tmp
= skipped_logs
;
151 skipped_logs
= 0; // clear before calling log_status_message()
152 log_status_message(3, "skipped %d log entries (queue full)", skip_tmp
);
153 g_async_queue_push(queue
, le
);
159 g_async_queue_push(queue
, le
);
165 #define LEPRINTF(format, ...) { if (le->len < LE_MAX) le->len += snprintf(le->buf + le->len, LE_MAX - le->len, format, ##__VA_ARGS__); }
166 #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)); }
169 log_status_message(guint loglevel
, const char *fmt
, ...)
174 if (loglevel
> 7 ) loglevel
= 7; // syslog defines level 0-7
176 struct log_entry
*le
= g_new0(struct log_entry
, 1);
178 LEPRINTF("0 %d - ", loglevel
);
180 LEPRINTTIME(time(NULL
));
182 le
->len
+= vsnprintf(le
->buf
+ le
->len
, LE_MAX
- le
->len
, fmt
, ap
);
190 print_tcp(struct log_entry
*le
, struct tcphdr
*h
, int payload_len
)
192 LEPRINTF("PROTO=TCP ");
194 if (payload_len
< sizeof(struct tcphdr
)) {
195 LEPRINTF("LEN=%d ", payload_len
);
196 LEPRINTF("INVALID=LEN ");
200 LEPRINTF("SPT=%u DPT=%u ", ntohs(h
->source
), ntohs(h
->dest
));
201 LEPRINTF("SEQ=%u ACK=%u ", ntohl(h
->seq
), ntohl(h
->ack_seq
));
202 LEPRINTF("WINDOW=%u ", ntohs(h
->window
));
204 if (h
->urg
) LEPRINTF("URG ");
205 if (h
->ack
) LEPRINTF("ACK ");
206 if (h
->psh
) LEPRINTF("PSH ");
207 if (h
->rst
) LEPRINTF("RST ");
208 if (h
->syn
) LEPRINTF("SYN ");
209 if (h
->fin
) LEPRINTF("FIN ");
211 if (h
->urg
) LEPRINTF("URGP=%u ",ntohs(h
->urg_ptr
));
217 print_udp(struct log_entry
*le
, struct udphdr
*h
, int payload_len
)
219 LEPRINTF("PROTO=UDP ");
221 if (payload_len
< sizeof(struct udphdr
)) {
222 LEPRINTF("LEN=%d ", payload_len
);
223 LEPRINTF("INVALID=LEN ");
227 LEPRINTF("SPT=%u DPT=%u LEN=%u", ntohs(h
->source
), ntohs(h
->dest
), ntohs(h
->len
));
233 print_icmp(struct log_entry
*le
, struct icmphdr
*h
, int payload_len
)
235 char tmp
[INET_ADDRSTRLEN
];
238 LEPRINTF("PROTO=ICMP ");
240 if (payload_len
< sizeof(struct icmphdr
)) {
241 LEPRINTF("LEN=%d ", payload_len
);
242 LEPRINTF("INVALID=LEN ");
246 LEPRINTF("TYPE=%u CODE=%u ", h
->type
, h
->code
);
251 LEPRINTF("ID=%u SEQ=%u ", ntohs(h
->un
.echo
.id
), ntohs(h
->un
.echo
.sequence
));
253 case ICMP_PARAMETERPROB
:
254 LEPRINTF("PARAMETER=%u ", ntohl(h
->un
.gateway
) >> 24);
257 gateway
= ntohl(h
->un
.gateway
);
258 inet_ntop(AF_INET
, &gateway
, tmp
, sizeof(tmp
));
259 LEPRINTF("GATEWAY=%s ", tmp
);
261 case ICMP_DEST_UNREACH
:
262 if (h
->code
== ICMP_FRAG_NEEDED
) {
263 LEPRINTF("MTU=%u ", ntohs(h
->un
.frag
.mtu
));
271 /* Section 3.1. SCTP Common Header Format */
272 typedef struct sctphdr
{
277 } __attribute__((packed
)) sctp_sctphdr_t
;
280 print_sctp(struct log_entry
*le
, struct sctphdr
*h
, int payload_len
)
282 LEPRINTF("PROTO=SCTP ");
284 if (payload_len
< sizeof(struct sctphdr
)) {
285 LEPRINTF("LEN=%d ", payload_len
);
286 LEPRINTF("INVALID=LEN ");
290 LEPRINTF("SPT=%u DPT=%u ", ntohs(h
->source
), ntohs(h
->dest
));
296 print_iphdr(struct log_entry
*le
, char * payload
, int payload_len
)
298 if (payload_len
< sizeof(struct iphdr
)) {
299 LEPRINTF("LEN=%d ", payload_len
);
300 LEPRINTF("INVALID=LEN ");
304 struct iphdr
*h
= (struct iphdr
*)payload
;
306 if (payload_len
<= (u_int32_t
)(h
->ihl
* 4)) {
307 LEPRINTF("INVALID=IHL ");
311 char tmp
[INET_ADDRSTRLEN
];
313 inet_ntop(AF_INET
, &h
->saddr
, tmp
, sizeof(tmp
));
314 LEPRINTF("SRC=%s ", tmp
);
315 inet_ntop(AF_INET
, &h
->daddr
, tmp
, sizeof(tmp
));
316 LEPRINTF("DST=%s ", tmp
);
318 LEPRINTF("LEN=%u TOS=0x%02X PREC=0x%02X TTL=%u ID=%u ",
319 ntohs(h
->tot_len
), h
->tos
& IPTOS_TOS_MASK
,
320 h
->tos
& IPTOS_PREC_MASK
, h
->ttl
, ntohs(h
->id
));
322 short ip_off
= ntohs(h
->frag_off
);
323 if (ip_off
& IP_OFFMASK
)
324 LEPRINTF("FRAG=%u ", ip_off
& IP_OFFMASK
);
326 if (ip_off
& IP_DF
) LEPRINTF("DF ");
327 if (ip_off
& IP_MF
) LEPRINTF("MF ");
329 void *nexthdr
= (u_int32_t
*)h
+ h
->ihl
;
330 payload_len
-= h
->ihl
* 4;
332 switch (h
->protocol
) {
334 print_tcp(le
, (struct tcphdr
*)nexthdr
, payload_len
);
337 print_udp(le
, (struct udphdr
*)nexthdr
, payload_len
);
340 print_icmp(le
, (struct icmphdr
*)nexthdr
, payload_len
);
343 print_sctp(le
, (struct sctphdr
*)nexthdr
, payload_len
);
346 LEPRINTF("PROTO=AH ");
349 LEPRINTF("PROTO=ESP ");
352 LEPRINTF("PROTO=IGMP ");
355 LEPRINTF("PROTO=%u ", h
->protocol
);
362 print_ip6hdr(struct log_entry
*le
, char * payload
, int payload_len
)
364 LEPRINTF("IPV6 logging not implemented ");
369 // ebtables -I FORWARD --nflog --nflog-group 0
371 print_arp(struct log_entry
*le
, struct ether_arp
*h
, int payload_len
)
373 if (payload_len
< sizeof(struct ether_arp
)) {
374 LEPRINTF("LEN=%d ", payload_len
);
375 LEPRINTF("INVALID=LEN ");
379 LEPRINTF("SRC=%u.%u.%u.%u ", h
->arp_spa
[0], h
->arp_spa
[1],
380 h
->arp_spa
[2], h
->arp_spa
[3]);
382 LEPRINTF("DST=%u.%u.%u.%u ", h
->arp_tpa
[0], h
->arp_tpa
[1],
383 h
->arp_tpa
[2], h
->arp_tpa
[3]);
385 LEPRINTF("PROTO=ARP ");
387 unsigned short code
= ntohs(h
->arp_op
);
390 LEPRINTF("REQUEST ");
393 LEPRINTF("REPLY MAC=%02x:%02x:%02x:%02x:%02x:%02x ",
394 h
->arp_sha
[0], h
->arp_sha
[1], h
->arp_sha
[2],
395 h
->arp_sha
[3], h
->arp_sha
[4], h
->arp_sha
[5]);
401 LEPRINTF("CODE=%u ", code
);
405 // LEPRINTF("HTYPE=%u ", ntohs(h->arp_hrd));
407 // LEPRINTF("PTYPE=%u ", ntohs(h->arp_pro));
413 static int print_pkt(struct log_entry
*le
, struct nflog_data
*ldata
, u_int8_t family
)
415 u_int32_t mark
= nflog_get_nfmark(ldata
);
416 u_int32_t indev
= nflog_get_indev(ldata
);
417 u_int32_t outdev
= nflog_get_outdev(ldata
);
418 u_int32_t physindev
= nflog_get_physindev(ldata
);
419 u_int32_t physoutdev
= nflog_get_physoutdev(ldata
);
421 char *prefix
= nflog_get_prefix(ldata
);
427 guint8 log_level
= 6; // info
429 char *chain_name
= "-";
431 if (prefix
!= NULL
) {
432 // Note: parse ":$vmid:$loglevel:$chain: $msg"
433 if (prefix
[0] == ':') {
434 char *p
= prefix
+ 1;
436 while(*p
>= '0' && *p
<= '9') { tmpid
*= 10; tmpid
+= *p
- '0'; p
++; }
439 (p
[1] >= '0' && p
[1] <= '7') &&
442 guint8 tmp_level
= p
[1] - '0'; // store for later use
443 char *chain_start
= p
+ 3; // store for later use
445 while (*p
&& *p
!= ':' && *p
!= ' ') p
++;
446 int len
= p
- chain_start
;
448 if (*p
== ':' && p
[1] == ' ' && len
&& (len
<= MAX_CHAIN_LEN
)) {
449 // parsing successful
451 *p
= 0; // terminate string
454 log_level
= tmp_level
;
455 chain_name
= chain_start
;
456 prefix
= p
+ 2; // the rest
462 LEPRINTF("%d ", vmid
);
464 LEPRINTF("%d ", log_level
);
466 LEPRINTF("%s ", chain_name
);
469 nflog_get_timestamp(ldata
, &ts
);
471 LEPRINTTIME(ts
.tv_sec
);
473 if (prefix
!= NULL
) {
474 LEPRINTF("%s", prefix
);
477 if ((indev
> 0) && (nlif_index2name(nlifh
, indev
, devname
) != -1)) {
478 LEPRINTF("IN=%s ", devname
);
481 if ((outdev
> 0) && (nlif_index2name(nlifh
, outdev
, devname
) != -1)) {
482 LEPRINTF("OUT=%s ", devname
);
485 if ((physindev
> 0) && (nlif_index2name(nlifh
, physindev
, devname
) != -1)) {
486 LEPRINTF("PHYSIN=%s ", devname
);
489 if ((physoutdev
> 0) && (nlif_index2name(nlifh
, physoutdev
, devname
) != -1)) {
490 LEPRINTF("PHYSOUT=%s ", devname
);
493 int payload_len
= nflog_get_payload(ldata
, &payload
);
495 int hwhdrlen
= nflog_get_msg_packet_hwhdrlen(ldata
);
497 unsigned char *hwhdr
= (unsigned char *)nflog_get_msg_packet_hwhdr(ldata
);
501 for (i
= 0; i
< hwhdrlen
; i
++) {
502 LEPRINTF("%02x", hwhdr
[i
]);
503 if (i
< (hwhdrlen
-1 )) LEPRINTF(":");
509 u_int16_t hw_protocol
= 0;
510 struct nfulnl_msg_packet_hdr
*ph
= NULL
;
514 print_iphdr(le
, payload
, payload_len
);
517 print_ip6hdr(le
, payload
, payload_len
);
520 ph
= nflog_get_msg_packet_hdr(ldata
);
521 if (ph
) hw_protocol
= ntohs(ph
->hw_protocol
);
523 switch (hw_protocol
) {
525 print_iphdr(le
, payload
, payload_len
);
528 print_ip6hdr(le
, payload
, payload_len
);
531 print_arp(le
, (struct ether_arp
*)payload
, payload_len
);
537 if (mark
) LEPRINTF("mark=%u ", mark
);
545 nflog_cb(struct nflog_g_handle
*gh
, struct nfgenmsg
*nfmsg
,
546 struct nflog_data
*nfa
, void *data
)
548 struct log_entry
*le
= g_new0(struct log_entry
, 1);
550 print_pkt(le
, nfa
, nfmsg
->nfgen_family
);
552 LEPRINTF("\n"); // add newline
560 nflog_read_cb(GIOChannel
*source
,
561 GIOCondition condition
,
567 int fd
= g_io_channel_unix_get_fd(source
);
569 if ((rv
= recv(fd
, buf
, sizeof(buf
), 0)) && rv
>= 0) {
570 nflog_handle_packet(logh
, buf
, rv
);
577 nlif_read_cb(GIOChannel
*source
,
578 GIOCondition condition
,
582 // fixme: report errors
586 GMainLoop
*main_loop
;
589 terminate_request(gpointer data
)
591 terminate_threads
= TRUE
;
593 log_status_message(5, "received terminate request (signal)");
595 g_main_loop_quit(main_loop
);
602 main(int argc
, char *argv
[])
605 gboolean foreground
= FALSE
;
606 gboolean wrote_pidfile
= FALSE
;
610 if ((lockfd
= open(LOCKFILE
, O_RDWR
|O_CREAT
|O_APPEND
, 0644)) == -1) {
611 fprintf(stderr
, "unable to create lock '%s': %s", LOCKFILE
, strerror (errno
));
615 for (int i
= 10; i
>= 0; i
--) {
616 if (flock(lockfd
, LOCK_EX
|LOCK_NB
) != 0) {
618 fprintf(stderr
, "unable to aquire lock '%s': %s", LOCKFILE
, strerror (errno
));
622 fprintf(stderr
, "unable to aquire lock '%s' - trying again.\n", LOCKFILE
);
628 if ((outfd
= open(LOGFILE
, O_WRONLY
|O_CREAT
|O_APPEND
, 0644)) == -1) {
629 fprintf(stderr
, "unable to open file '%s': %s", LOGFILE
, strerror (errno
));
633 if ((logh
= nflog_open()) == NULL
) {
634 fprintf(stderr
, "unable to open nflog\n");
638 if (!nflog_bind_pf(logh
, AF_INET
) <= 0) {
639 fprintf(stderr
, "nflog_bind_pf AF_INET failed\n");
644 if (!nflog_bind_pf(logh
, AF_INET6
) <= 0) {
645 fprintf(stderr
, "nflog_bind_pf AF_INET6 failed\n");
650 if (!nflog_bind_pf(logh
, AF_BRIDGE
) <= 0) {
651 fprintf(stderr
, "nflog_bind_pf AF_BRIDGE failed\n");
655 struct nflog_g_handle
*qh
= nflog_bind_group(logh
, 0);
657 fprintf(stderr
, "no handle for group 1\n");
661 if (nflog_set_mode(qh
, NFULNL_COPY_PACKET
, 0xffff) < 0) {
662 fprintf(stderr
, "can't set packet copy mode\n");
666 if ((nlifh
= nlif_open()) == NULL
) {
667 fprintf(stderr
, "unable to open netlink interface handle\n");
675 fprintf(stderr
, "failed to daemonize program - %s\n", strerror (errno
));
683 if (chroot("/") != 0) fprintf(stderr
, "chroot '/' failed - %s\n", strerror (errno
));
685 if ((nullfd
= open("/dev/null", O_RDWR
, 0)) != -1) {
696 write_pidfile(getpid());
699 wrote_pidfile
= TRUE
;
701 nflog_callback_register(qh
, &nflog_cb
, logh
);
703 queue
= g_async_queue_new_full(g_free
);
705 log_status_message(5, "starting pvefw logger");
709 GIOChannel
*nlif_ch
= g_io_channel_unix_new(nlif_fd(nlifh
));
711 g_io_add_watch(nlif_ch
, G_IO_IN
, nlif_read_cb
, NULL
);
713 int logfd
= nflog_fd(logh
);
714 GIOChannel
*nflog_ch
= g_io_channel_unix_new(logfd
);
716 g_io_add_watch(nflog_ch
, G_IO_IN
, nflog_read_cb
, NULL
);
718 GThread
*wthread
= g_thread_new("log_writer_thread", log_writer_thread
, NULL
);
720 main_loop
= g_main_loop_new(NULL
, TRUE
);
722 g_unix_signal_add(SIGINT
, terminate_request
, NULL
);
723 g_unix_signal_add(SIGTERM
, terminate_request
, NULL
);
725 g_main_loop_run(main_loop
);
727 log_status_message(5, "stopping pvefw logger");
729 g_thread_join(wthread
);