set RELEASE=4.1
[pve-firewall.git] / src / pvefw-logger.c
CommitLineData
ba0b3a0a
DM
1/*
2
3 Copyright (C) 2014 Proxmox Server Solutions GmbH
4
5 This software is written by Proxmox Server Solutions GmbH <support@proxmox.com>
6
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.
11
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.
16
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/>.
19
20 Author: Dietmar Maurer <dietmar@proxmox.com>
21
22*/
23
24#define _GNU_SOURCE
25
26#include <stdlib.h>
27#include <stdio.h>
28#include <errno.h>
29#include <stdarg.h>
30#include <string.h>
31#include <signal.h>
985fd74e 32#include <sys/signalfd.h>
7bb6e8dd 33#include <sys/types.h>
ba0b3a0a
DM
34#include <sys/stat.h>
35#include <fcntl.h>
36#include <unistd.h>
37#include <arpa/inet.h>
38#include <sys/socket.h>
39#include <sys/file.h>
40#include <linux/netlink.h>
41#include <libnfnetlink/libnfnetlink.h>
42#include <libnetfilter_log/libnetfilter_log.h>
43#include <netinet/ip.h>
44#include <netinet/ip_icmp.h>
45#include <netinet/udp.h>
46#include <netinet/tcp.h>
47#include <netinet/if_ether.h>
9da3465c 48#include <syslog.h>
ba0b3a0a
DM
49
50#include <glib.h>
ba0b3a0a
DM
51
52static struct nflog_handle *logh = NULL;
53static struct nlif_handle *nlifh = NULL;
7bb6e8dd 54GMainLoop *main_loop;
ba0b3a0a 55
84786ec0
DM
56gboolean foreground = FALSE;
57gboolean debug = FALSE;
58
782c4cde 59/*
7bb6e8dd 60
782c4cde
DM
61LOG FORMAT:
62
63Special care was taken to allow fast parsing (and filer messages for a singl VM).
64
7bb6e8dd 65<VMID> <LOGLEVEL> <CHAIN> <TIME> <TIMEZONE> <MSG>
782c4cde
DM
66
67Example:
68
69117 6 tap117i0-IN 14/Mar/2014:12:47:07 +0100 policy REJECT: IN=vmbr1 ...
7bb6e8dd 70
782c4cde
DM
71*/
72
ba0b3a0a
DM
73#define LOGFILE "/var/log/pve-firewall.log"
74
75#define LOCKFILE "/var/lock/pvefw-logger.lck"
76#define PIDFILE "/var/run/pvefw-logger.pid"
77
78#define LQ_LEN 512
5b06fd6c 79#define LE_MAX (512 - 4) // try to fit into 512 bytes
ba0b3a0a 80
782c4cde
DM
81#define MAX_CHAIN_LEN 28
82
7bb6e8dd 83struct log_entry {
ba0b3a0a
DM
84 guint32 len; // max LE_MAX chars
85 char buf[LE_MAX];
86};
87
5b06fd6c
DM
88#define STATIC_ASSERT(cond) \
89 extern void pve_static_assert(int test[(cond) ? 1 : -1])
90
91STATIC_ASSERT(sizeof(struct log_entry) == 512);
92
ba0b3a0a
DM
93int outfd = -1;
94
95gboolean terminate_threads = FALSE;
96
97static gboolean write_pidfile(pid_t pid)
98{
99 gboolean res;
100
101 char *strpid = g_strdup_printf("%d\n", pid);
102 res = g_file_set_contents(PIDFILE, strpid, strlen(strpid), NULL);
103 g_free(strpid);
104
105 return res;
106}
107
108static GAsyncQueue *queue;
109
7bb6e8dd 110ssize_t
ba0b3a0a
DM
111safe_write(int fd, char *buf, size_t count)
112{
113 ssize_t n;
114
115 do {
116 n = write(fd, buf, count);
117 } while (n < 0 && errno == EINTR);
118
119 return n;
120}
121
122static gpointer
7bb6e8dd 123log_writer_thread(gpointer data)
ba0b3a0a
DM
124{
125 while (1) {
126 struct log_entry *le = (struct log_entry *)g_async_queue_timeout_pop(queue, 250000);
127 if (le == NULL) {
128 if (terminate_threads) {
129 return NULL;
130 }
131 continue;
132 }
7bb6e8dd 133
84786ec0
DM
134 if (debug) fputs(le->buf, stdout);
135
ba0b3a0a 136 int res = safe_write(outfd, le->buf, le->len);
7bb6e8dd 137
ba0b3a0a
DM
138 g_free(le);
139
140 if (res < 0) {
9da3465c 141 syslog(3, "writing log failed, stopping daemon - %s", strerror (errno));
7bb6e8dd 142 g_main_loop_quit(main_loop);
9da3465c 143 return NULL;
ba0b3a0a
DM
144 }
145 }
146
147 return NULL;
148}
149
150static int skipped_logs = 0;
151
782c4cde 152static void log_status_message(guint loglevel, const char *fmt, ...);
7bb6e8dd 153
ba0b3a0a
DM
154static void
155queue_log_entry(struct log_entry *le)
156{
157 gint len = g_async_queue_length(queue);
158
159 if (skipped_logs > 0) {
160 if (len >= (LQ_LEN - 1)) {
161 skipped_logs++;
162 } else {
7bb6e8dd 163 int skip_tmp = skipped_logs;
ba0b3a0a 164 skipped_logs = 0; // clear before calling log_status_message()
782c4cde 165 log_status_message(3, "skipped %d log entries (queue full)", skip_tmp);
ba0b3a0a
DM
166 g_async_queue_push(queue, le);
167 }
168 } else {
169 if (len >= LQ_LEN) {
170 skipped_logs++;
171 } else {
172 g_async_queue_push(queue, le);
173 }
174 }
175}
176
177
178#define LEPRINTF(format, ...) { if (le->len < LE_MAX) le->len += snprintf(le->buf + le->len, LE_MAX - le->len, format, ##__VA_ARGS__); }
179#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)); }
180
7bb6e8dd
DM
181static void
182log_status_message(guint loglevel, const char *fmt, ...)
ba0b3a0a
DM
183{
184 va_list ap;
185 va_start(ap, fmt);
7bb6e8dd 186
782c4cde
DM
187 if (loglevel > 7 ) loglevel = 7; // syslog defines level 0-7
188
ba0b3a0a
DM
189 struct log_entry *le = g_new0(struct log_entry, 1);
190
782c4cde
DM
191 LEPRINTF("0 %d - ", loglevel);
192
ba0b3a0a
DM
193 LEPRINTTIME(time(NULL));
194
195 le->len += vsnprintf(le->buf + le->len, LE_MAX - le->len, fmt, ap);
196
197 LEPRINTF("\n");
198
199 queue_log_entry(le);
9da3465c
DM
200
201 // also log to syslog
202
203 vsyslog(loglevel, fmt, ap);
ba0b3a0a
DM
204}
205
7bb6e8dd 206static int
ba0b3a0a
DM
207print_tcp(struct log_entry *le, struct tcphdr *h, int payload_len)
208{
209 LEPRINTF("PROTO=TCP ");
210
211 if (payload_len < sizeof(struct tcphdr)) {
212 LEPRINTF("LEN=%d ", payload_len);
213 LEPRINTF("INVALID=LEN ");
214 return -1;
215 }
216
217 LEPRINTF("SPT=%u DPT=%u ", ntohs(h->source), ntohs(h->dest));
218 LEPRINTF("SEQ=%u ACK=%u ", ntohl(h->seq), ntohl(h->ack_seq));
219 LEPRINTF("WINDOW=%u ", ntohs(h->window));
220
221 if (h->urg) LEPRINTF("URG ");
222 if (h->ack) LEPRINTF("ACK ");
223 if (h->psh) LEPRINTF("PSH ");
224 if (h->rst) LEPRINTF("RST ");
225 if (h->syn) LEPRINTF("SYN ");
226 if (h->fin) LEPRINTF("FIN ");
227
228 if (h->urg) LEPRINTF("URGP=%u ",ntohs(h->urg_ptr));
229
230 return 0;
231}
232
7bb6e8dd 233static int
ba0b3a0a
DM
234print_udp(struct log_entry *le, struct udphdr *h, int payload_len)
235{
236 LEPRINTF("PROTO=UDP ");
237
238 if (payload_len < sizeof(struct udphdr)) {
239 LEPRINTF("LEN=%d ", payload_len);
240 LEPRINTF("INVALID=LEN ");
241 return -1;
242 }
243
244 LEPRINTF("SPT=%u DPT=%u LEN=%u", ntohs(h->source), ntohs(h->dest), ntohs(h->len));
245
246 return 0;
247}
248
7bb6e8dd 249static int
ba0b3a0a
DM
250print_icmp(struct log_entry *le, struct icmphdr *h, int payload_len)
251{
252 char tmp[INET_ADDRSTRLEN];
253 u_int32_t gateway;
254
255 LEPRINTF("PROTO=ICMP ");
256
257 if (payload_len < sizeof(struct icmphdr)) {
258 LEPRINTF("LEN=%d ", payload_len);
259 LEPRINTF("INVALID=LEN ");
260 return -1;
261 }
262
263 LEPRINTF("TYPE=%u CODE=%u ", h->type, h->code);
264
265 switch (h->type) {
266 case ICMP_ECHO:
267 case ICMP_ECHOREPLY:
268 LEPRINTF("ID=%u SEQ=%u ", ntohs(h->un.echo.id), ntohs(h->un.echo.sequence));
269 break;
270 case ICMP_PARAMETERPROB:
271 LEPRINTF("PARAMETER=%u ", ntohl(h->un.gateway) >> 24);
272 break;
273 case ICMP_REDIRECT:
274 gateway = ntohl(h->un.gateway);
7bb6e8dd 275 inet_ntop(AF_INET, &gateway, tmp, sizeof(tmp));
ba0b3a0a
DM
276 LEPRINTF("GATEWAY=%s ", tmp);
277 break;
278 case ICMP_DEST_UNREACH:
279 if (h->code == ICMP_FRAG_NEEDED) {
280 LEPRINTF("MTU=%u ", ntohs(h->un.frag.mtu));
281 }
282 break;
283 }
284
285 return 0;
286}
287
288/* Section 3.1. SCTP Common Header Format */
289typedef struct sctphdr {
290 __be16 source;
291 __be16 dest;
292 __be32 vtag;
293 __be32 checksum;
294} __attribute__((packed)) sctp_sctphdr_t;
295
7bb6e8dd 296static int
ba0b3a0a
DM
297print_sctp(struct log_entry *le, struct sctphdr *h, int payload_len)
298{
299 LEPRINTF("PROTO=SCTP ");
300
301 if (payload_len < sizeof(struct sctphdr)) {
302 LEPRINTF("LEN=%d ", payload_len);
303 LEPRINTF("INVALID=LEN ");
304 return -1;
305 }
306
307 LEPRINTF("SPT=%u DPT=%u ", ntohs(h->source), ntohs(h->dest));
308
309 return 0;
310}
311
7bb6e8dd 312static int
ba0b3a0a
DM
313print_iphdr(struct log_entry *le, char * payload, int payload_len)
314{
315 if (payload_len < sizeof(struct iphdr)) {
316 LEPRINTF("LEN=%d ", payload_len);
317 LEPRINTF("INVALID=LEN ");
318 return -1;
319 }
320
321 struct iphdr *h = (struct iphdr *)payload;
7bb6e8dd 322
ba0b3a0a
DM
323 if (payload_len <= (u_int32_t)(h->ihl * 4)) {
324 LEPRINTF("INVALID=IHL ");
325 return -1;
326 }
327
328 char tmp[INET_ADDRSTRLEN];
7bb6e8dd
DM
329
330 inet_ntop(AF_INET, &h->saddr, tmp, sizeof(tmp));
ba0b3a0a 331 LEPRINTF("SRC=%s ", tmp);
7bb6e8dd 332 inet_ntop(AF_INET, &h->daddr, tmp, sizeof(tmp));
ba0b3a0a
DM
333 LEPRINTF("DST=%s ", tmp);
334
335 LEPRINTF("LEN=%u TOS=0x%02X PREC=0x%02X TTL=%u ID=%u ",
336 ntohs(h->tot_len), h->tos & IPTOS_TOS_MASK,
337 h->tos & IPTOS_PREC_MASK, h->ttl, ntohs(h->id));
7bb6e8dd 338
ba0b3a0a 339 short ip_off = ntohs(h->frag_off);
7bb6e8dd 340 if (ip_off & IP_OFFMASK)
ba0b3a0a
DM
341 LEPRINTF("FRAG=%u ", ip_off & IP_OFFMASK);
342
343 if (ip_off & IP_DF) LEPRINTF("DF ");
344 if (ip_off & IP_MF) LEPRINTF("MF ");
345
346 void *nexthdr = (u_int32_t *)h + h->ihl;
347 payload_len -= h->ihl * 4;
348
349 switch (h->protocol) {
350 case IPPROTO_TCP:
351 print_tcp(le, (struct tcphdr *)nexthdr, payload_len);
352 break;
353 case IPPROTO_UDP:
354 print_udp(le, (struct udphdr *)nexthdr, payload_len);
355 break;
356 case IPPROTO_ICMP:
357 print_icmp(le, (struct icmphdr *)nexthdr, payload_len);
358 break;
359 case IPPROTO_SCTP:
360 print_sctp(le, (struct sctphdr *)nexthdr, payload_len);
361 break;
362 case IPPROTO_AH:
363 LEPRINTF("PROTO=AH ");
364 break;
365 case IPPROTO_ESP:
366 LEPRINTF("PROTO=ESP ");
367 break;
368 case IPPROTO_IGMP:
369 LEPRINTF("PROTO=IGMP ");
370 break;
371 default:
372 LEPRINTF("PROTO=%u ", h->protocol);
373 }
374
375 return 0;
376}
377
7bb6e8dd 378static int
ba0b3a0a
DM
379print_ip6hdr(struct log_entry *le, char * payload, int payload_len)
380{
381 LEPRINTF("IPV6 logging not implemented ");
382
383 return 0;
384}
385
386// ebtables -I FORWARD --nflog --nflog-group 0
7bb6e8dd 387static int
ba0b3a0a
DM
388print_arp(struct log_entry *le, struct ether_arp *h, int payload_len)
389{
390 if (payload_len < sizeof(struct ether_arp)) {
391 LEPRINTF("LEN=%d ", payload_len);
392 LEPRINTF("INVALID=LEN ");
393 return -1;
394 }
395
396 LEPRINTF("SRC=%u.%u.%u.%u ", h->arp_spa[0], h->arp_spa[1],
397 h->arp_spa[2], h->arp_spa[3]);
398
399 LEPRINTF("DST=%u.%u.%u.%u ", h->arp_tpa[0], h->arp_tpa[1],
400 h->arp_tpa[2], h->arp_tpa[3]);
401
402 LEPRINTF("PROTO=ARP ");
403
404 unsigned short code = ntohs(h->arp_op);
405 switch (code) {
406 case ARPOP_REQUEST:
407 LEPRINTF("REQUEST ");
408 break;
409 case ARPOP_REPLY:
7bb6e8dd
DM
410 LEPRINTF("REPLY MAC=%02x:%02x:%02x:%02x:%02x:%02x ",
411 h->arp_sha[0], h->arp_sha[1], h->arp_sha[2],
ba0b3a0a
DM
412 h->arp_sha[3], h->arp_sha[4], h->arp_sha[5]);
413 break;
414 case ARPOP_NAK:
415 LEPRINTF("NAK ");
416 break;
417 default:
418 LEPRINTF("CODE=%u ", code);
419 }
420
421
422 // LEPRINTF("HTYPE=%u ", ntohs(h->arp_hrd));
423
424 // LEPRINTF("PTYPE=%u ", ntohs(h->arp_pro));
425
426 return 0;
427}
428
429
430static int print_pkt(struct log_entry *le, struct nflog_data *ldata, u_int8_t family)
431{
432 u_int32_t mark = nflog_get_nfmark(ldata);
433 u_int32_t indev = nflog_get_indev(ldata);
434 u_int32_t outdev = nflog_get_outdev(ldata);
435 u_int32_t physindev = nflog_get_physindev(ldata);
436 u_int32_t physoutdev = nflog_get_physoutdev(ldata);
437
438 char *prefix = nflog_get_prefix(ldata);
439 char *payload;
440 char devname[256];
7bb6e8dd 441
782c4cde
DM
442 guint32 vmid = 0;
443
444 guint8 log_level = 6; // info
445
446 char *chain_name = "-";
447
448 if (prefix != NULL) {
449 // Note: parse ":$vmid:$loglevel:$chain: $msg"
450 if (prefix[0] == ':') {
451 char *p = prefix + 1;
452 guint32 tmpid = 0;
453 while(*p >= '0' && *p <= '9') { tmpid *= 10; tmpid += *p - '0'; p++; }
454
455 if ((*p == ':') &&
456 (p[1] >= '0' && p[1] <= '7') &&
457 (p[2] == ':')) {
458
459 guint8 tmp_level = p[1] - '0'; // store for later use
460 char *chain_start = p + 3; // store for later use
461 p = chain_start;
462 while (*p && *p != ':' && *p != ' ') p++;
463 int len = p - chain_start;
464
465 if (*p == ':' && p[1] == ' ' && len && (len <= MAX_CHAIN_LEN)) {
466 // parsing successful
467
468 *p = 0; // terminate string
469
470 vmid = tmpid;
471 log_level = tmp_level;
472 chain_name = chain_start;
473 prefix = p + 2; // the rest
7bb6e8dd 474 }
782c4cde
DM
475 }
476 }
477 }
478
479 LEPRINTF("%d ", vmid);
480
481 LEPRINTF("%d ", log_level);
482
483 LEPRINTF("%s ", chain_name);
484
ba0b3a0a
DM
485 struct timeval ts;
486 nflog_get_timestamp(ldata, &ts);
487
488 LEPRINTTIME(ts.tv_sec);
489
ba0b3a0a
DM
490 if (prefix != NULL) {
491 LEPRINTF("%s", prefix);
492 }
7bb6e8dd
DM
493
494 if (indev > 0) {
e7ceb650 495 if (nlif_index2name(nlifh, indev, devname) != -1) {
7bb6e8dd 496 LEPRINTF("IN=%s ", devname);
e7ceb650 497 } else {
7bb6e8dd 498 LEPRINTF("IN=%u ", indev);
e7ceb650 499 }
ba0b3a0a
DM
500 }
501
e7ceb650
DM
502 if (outdev > 0) {
503 if (nlif_index2name(nlifh, outdev, devname) != -1) {
504 LEPRINTF("OUT=%s ", devname);
505 } else {
506 LEPRINTF("OUT=%u ", outdev);
507 }
ba0b3a0a
DM
508 }
509
7bb6e8dd 510 if (physindev > 0) {
e7ceb650
DM
511 if (nlif_index2name(nlifh, physindev, devname) != -1) {
512 LEPRINTF("PHYSIN=%s ", devname);
513 } else {
514 LEPRINTF("PHYSIN=%u ", physindev);
515 }
ba0b3a0a 516 }
7bb6e8dd 517
e7ceb650
DM
518 if (physoutdev > 0) {
519 if (nlif_index2name(nlifh, physoutdev, devname) != -1) {
520 LEPRINTF("PHYSOUT=%s ", devname);
521 } else {
522 LEPRINTF("PHYSOUT=%u ", physoutdev);
523 }
ba0b3a0a
DM
524 }
525
526 int payload_len = nflog_get_payload(ldata, &payload);
527
528 int hwhdrlen = nflog_get_msg_packet_hwhdrlen(ldata);
529 if (hwhdrlen > 0) {
530 unsigned char *hwhdr = (unsigned char *)nflog_get_msg_packet_hwhdr(ldata);
531 if (hwhdr != NULL) {
532 int i;
533 LEPRINTF("MAC=");
534 for (i = 0; i < hwhdrlen; i++) {
535 LEPRINTF("%02x", hwhdr[i]);
536 if (i < (hwhdrlen -1 )) LEPRINTF(":");
537 }
538 LEPRINTF(" ");
539 }
540 }
541
542 u_int16_t hw_protocol = 0;
543 struct nfulnl_msg_packet_hdr *ph = NULL;
544
545 switch (family) {
7bb6e8dd 546 case AF_INET:
ba0b3a0a
DM
547 print_iphdr(le, payload, payload_len);
548 break;
549 case AF_INET6:
550 print_ip6hdr(le, payload, payload_len);
551 break;
552 case AF_BRIDGE:
553 ph = nflog_get_msg_packet_hdr(ldata);
554 if (ph) hw_protocol = ntohs(ph->hw_protocol);
555
556 switch (hw_protocol) {
557 case ETH_P_IP:
558 print_iphdr(le, payload, payload_len);
559 break;
560 case ETH_P_IPV6:
561 print_ip6hdr(le, payload, payload_len);
562 break;
563 case ETH_P_ARP:
564 print_arp(le, (struct ether_arp *)payload, payload_len);
565 break;
566 }
567 break;
568 }
569
570 if (mark) LEPRINTF("mark=%u ", mark);
571
572
573 return 0;
574
575}
576
7bb6e8dd 577static int
ba0b3a0a
DM
578nflog_cb(struct nflog_g_handle *gh, struct nfgenmsg *nfmsg,
579 struct nflog_data *nfa, void *data)
580{
581 struct log_entry *le = g_new0(struct log_entry, 1);
7bb6e8dd 582
ba0b3a0a
DM
583 print_pkt(le, nfa, nfmsg->nfgen_family);
584
585 LEPRINTF("\n"); // add newline
586
587 queue_log_entry(le);
588
589 return 0;
590}
591
592static gboolean
593nflog_read_cb(GIOChannel *source,
594 GIOCondition condition,
595 gpointer data)
596{
597 int rv = 0;
598 gchar buf[8192];
599
600 int fd = g_io_channel_unix_get_fd(source);
7bb6e8dd 601
ba0b3a0a
DM
602 if ((rv = recv(fd, buf, sizeof(buf), 0)) && rv >= 0) {
603 nflog_handle_packet(logh, buf, rv);
604 }
605
606 return TRUE;
607}
608
609static gboolean
610nlif_read_cb(GIOChannel *source,
611 GIOCondition condition,
612 gpointer data)
613{
9da3465c
DM
614 static int last_res = 0;
615 int res;
616
617 if ((res = nlif_catch(nlifh)) < 0) {
618 if (last_res == 0) { // only report once
619 log_status_message(3, "nlif_catch failed (res = %d)", res);
620 }
621 last_res = res;
622 } else {
623 last_res = 0;
624 }
625
7bb6e8dd 626 return TRUE;
ba0b3a0a
DM
627}
628
ba0b3a0a 629static gboolean
985fd74e
DM
630signal_read_cb(GIOChannel *source,
631 GIOCondition condition,
632 gpointer data)
ba0b3a0a 633{
985fd74e
DM
634 int rv = 0;
635 struct signalfd_siginfo si;
ba0b3a0a 636
985fd74e 637 int fd = g_io_channel_unix_get_fd(source);
ba0b3a0a 638
985fd74e
DM
639 if ((rv = read(fd, &si, sizeof(si))) && rv >= 0) {
640 terminate_threads = TRUE;
641 log_status_message(5, "received terminate request (signal)");
642 g_main_loop_quit(main_loop);
643 }
7bb6e8dd 644
ba0b3a0a
DM
645 return TRUE;
646}
7bb6e8dd 647
ba0b3a0a
DM
648int
649main(int argc, char *argv[])
650{
651 int lockfd = -1;
985fd74e
DM
652 int sigfd = -1;
653
ba0b3a0a
DM
654 gboolean wrote_pidfile = FALSE;
655
9da3465c
DM
656 openlog("pvepw-logger", LOG_CONS|LOG_PID, LOG_DAEMON);
657
84786ec0
DM
658 GOptionContext *context;
659
660 GOptionEntry entries[] = {
661 { "debug", 'd', 0, G_OPTION_ARG_NONE, &debug, "Turn on debug messages", NULL },
662 { "foreground", 'f', 0, G_OPTION_ARG_NONE, &foreground, "Do not daemonize server", NULL },
663 { NULL },
664 };
665
666 context = g_option_context_new("");
667 g_option_context_add_main_entries (context, entries, NULL);
668
669 GError *err = NULL;
670 if (!g_option_context_parse (context, &argc, &argv, &err)) {
671 fprintf(stderr, "error: %s\n", err->message);
672 fprintf(stderr, "%s", g_option_context_get_help(context, FALSE, NULL));
673 g_error_free (err);
674 exit(-1);
675 }
676
677 if (optind < argc) {
678 fprintf(stderr, "error: too many arguments\n");
679 fprintf(stderr, "%s", g_option_context_get_help(context, FALSE, NULL));
680 exit(-1);
681 }
682
683 g_option_context_free(context);
684
685 if (debug) foreground = TRUE;
686
ba0b3a0a 687 if ((lockfd = open(LOCKFILE, O_RDWR|O_CREAT|O_APPEND, 0644)) == -1) {
84786ec0 688 fprintf(stderr, "unable to create lock '%s': %s\n", LOCKFILE, strerror (errno) );
ba0b3a0a
DM
689 exit(-1);
690 }
691
692 for (int i = 10; i >= 0; i--) {
693 if (flock(lockfd, LOCK_EX|LOCK_NB) != 0) {
694 if (!i) {
84786ec0 695 fprintf(stderr, "unable to aquire lock '%s': %s\n", LOCKFILE, strerror (errno));
ba0b3a0a
DM
696 exit(-1);
697 }
698 if (i == 10)
699 fprintf(stderr, "unable to aquire lock '%s' - trying again.\n", LOCKFILE);
7bb6e8dd 700
ba0b3a0a
DM
701 sleep(1);
702 }
703 }
704
705 if ((outfd = open(LOGFILE, O_WRONLY|O_CREAT|O_APPEND, 0644)) == -1) {
84786ec0 706 fprintf(stderr, "unable to open file '%s': %s\n", LOGFILE, strerror (errno));
ba0b3a0a
DM
707 exit(-1);
708 }
709
710 if ((logh = nflog_open()) == NULL) {
711 fprintf(stderr, "unable to open nflog\n");
712 exit(-1);
713 }
714
715 if (!nflog_bind_pf(logh, AF_INET) <= 0) {
716 fprintf(stderr, "nflog_bind_pf AF_INET failed\n");
717 exit(-1);
718 }
719
720#if 0
721 if (!nflog_bind_pf(logh, AF_INET6) <= 0) {
722 fprintf(stderr, "nflog_bind_pf AF_INET6 failed\n");
723 exit(-1);
724 }
725#endif
7bb6e8dd 726
ba0b3a0a
DM
727 if (!nflog_bind_pf(logh, AF_BRIDGE) <= 0) {
728 fprintf(stderr, "nflog_bind_pf AF_BRIDGE failed\n");
729 exit(-1);
730 }
731
732 struct nflog_g_handle *qh = nflog_bind_group(logh, 0);
733 if (!qh) {
84786ec0 734 fprintf(stderr, "no nflog handle for group 0\n");
ba0b3a0a
DM
735 exit(-1);
736 }
7bb6e8dd 737
ba0b3a0a
DM
738 if (nflog_set_mode(qh, NFULNL_COPY_PACKET, 0xffff) < 0) {
739 fprintf(stderr, "can't set packet copy mode\n");
740 exit(-1);
741 }
742
743 if ((nlifh = nlif_open()) == NULL) {
744 fprintf(stderr, "unable to open netlink interface handle\n");
745 exit(-1);
746 }
747
985fd74e
DM
748 sigset_t mask;
749 sigemptyset(&mask);
750 sigaddset(&mask, SIGINT);
751 sigaddset(&mask, SIGTERM);
752
753 sigprocmask(SIG_BLOCK, &mask, NULL);
754
755 if ((sigfd = signalfd(-1, &mask, SFD_NONBLOCK)) < 0) {
756 fprintf(stderr, "unable to open signalfd: %s\n", strerror (errno));
757 exit(-1);
758 }
759
ba0b3a0a
DM
760 if (!foreground) {
761 pid_t cpid = fork();
762
763 if (cpid == -1) {
764 fprintf(stderr, "failed to daemonize program - %s\n", strerror (errno));
765 exit(-1);
766 } else if (cpid) {
767 write_pidfile(cpid);
768 _exit(0);
769 } else {
770 int nullfd;
771
772 if (chroot("/") != 0) fprintf(stderr, "chroot '/' failed - %s\n", strerror (errno));
773
774 if ((nullfd = open("/dev/null", O_RDWR, 0)) != -1) {
775 dup2(nullfd, 0);
776 dup2(nullfd, 1);
777 dup2(nullfd, 2);
778 if (nullfd > 2)
779 close (nullfd);
780 }
781
782 setsid();
783 }
784 } else {
785 write_pidfile(getpid());
786 }
787
788 wrote_pidfile = TRUE;
789
790 nflog_callback_register(qh, &nflog_cb, logh);
791
792 queue = g_async_queue_new_full(g_free);
793
782c4cde 794 log_status_message(5, "starting pvefw logger");
ba0b3a0a
DM
795
796 nlif_query(nlifh);
797
798 GIOChannel *nlif_ch = g_io_channel_unix_new(nlif_fd(nlifh));
799
800 g_io_add_watch(nlif_ch, G_IO_IN, nlif_read_cb, NULL);
801
802 int logfd = nflog_fd(logh);
803 GIOChannel *nflog_ch = g_io_channel_unix_new(logfd);
804
805 g_io_add_watch(nflog_ch, G_IO_IN, nflog_read_cb, NULL);
806
985fd74e 807 GIOChannel *sig_ch = g_io_channel_unix_new(sigfd);
985fd74e 808 if (!g_io_add_watch(sig_ch, G_IO_IN, signal_read_cb, NULL)) {
e2beb7aa 809 exit(-1);
985fd74e
DM
810 }
811
ba0b3a0a 812 GThread *wthread = g_thread_new("log_writer_thread", log_writer_thread, NULL);
7bb6e8dd 813
ba0b3a0a 814 main_loop = g_main_loop_new(NULL, TRUE);
7bb6e8dd 815
ba0b3a0a
DM
816 g_main_loop_run(main_loop);
817
782c4cde 818 log_status_message(5, "stopping pvefw logger");
ba0b3a0a
DM
819
820 g_thread_join(wthread);
821
822 close(outfd);
823
824 nflog_close(logh);
825
826 if (wrote_pidfile)
827 unlink(PIDFILE);
828
829 exit(0);
830}