]> git.proxmox.com Git - pve-firewall.git/blame - src/pvefw-logger.c
add conntrack logging via libnetfilter_conntrack
[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>
fdf943fe 43#include <libnetfilter_conntrack/libnetfilter_conntrack.h>
ba0b3a0a
DM
44#include <netinet/ip.h>
45#include <netinet/ip_icmp.h>
d1b290fc
WB
46#include <netinet/ip6.h>
47#include <netinet/icmp6.h>
ba0b3a0a
DM
48#include <netinet/udp.h>
49#include <netinet/tcp.h>
50#include <netinet/if_ether.h>
9da3465c 51#include <syslog.h>
ba0b3a0a
DM
52
53#include <glib.h>
ba0b3a0a
DM
54
55static struct nflog_handle *logh = NULL;
56static struct nlif_handle *nlifh = NULL;
fdf943fe 57static struct nfct_handle *nfcth = NULL;
7bb6e8dd 58GMainLoop *main_loop;
ba0b3a0a 59
84786ec0
DM
60gboolean foreground = FALSE;
61gboolean debug = FALSE;
fdf943fe 62gboolean conntrack = FALSE;
84786ec0 63
782c4cde 64/*
7bb6e8dd 65
782c4cde
DM
66LOG FORMAT:
67
68Special care was taken to allow fast parsing (and filer messages for a singl VM).
69
7bb6e8dd 70<VMID> <LOGLEVEL> <CHAIN> <TIME> <TIMEZONE> <MSG>
782c4cde
DM
71
72Example:
73
74117 6 tap117i0-IN 14/Mar/2014:12:47:07 +0100 policy REJECT: IN=vmbr1 ...
7bb6e8dd 75
782c4cde
DM
76*/
77
ba0b3a0a
DM
78#define LOGFILE "/var/log/pve-firewall.log"
79
80#define LOCKFILE "/var/lock/pvefw-logger.lck"
81#define PIDFILE "/var/run/pvefw-logger.pid"
fdf943fe 82#define LOG_CONNTRACK_FILE "/var/lib/pve-firewall/log_nf_conntrack"
ba0b3a0a
DM
83
84#define LQ_LEN 512
5b06fd6c 85#define LE_MAX (512 - 4) // try to fit into 512 bytes
ba0b3a0a 86
782c4cde
DM
87#define MAX_CHAIN_LEN 28
88
7bb6e8dd 89struct log_entry {
ba0b3a0a
DM
90 guint32 len; // max LE_MAX chars
91 char buf[LE_MAX];
92};
93
5b06fd6c
DM
94#define STATIC_ASSERT(cond) \
95 extern void pve_static_assert(int test[(cond) ? 1 : -1])
96
97STATIC_ASSERT(sizeof(struct log_entry) == 512);
98
ba0b3a0a
DM
99int outfd = -1;
100
101gboolean terminate_threads = FALSE;
102
103static gboolean write_pidfile(pid_t pid)
104{
105 gboolean res;
106
107 char *strpid = g_strdup_printf("%d\n", pid);
108 res = g_file_set_contents(PIDFILE, strpid, strlen(strpid), NULL);
109 g_free(strpid);
110
111 return res;
112}
113
114static GAsyncQueue *queue;
115
7bb6e8dd 116ssize_t
ba0b3a0a
DM
117safe_write(int fd, char *buf, size_t count)
118{
119 ssize_t n;
120
121 do {
122 n = write(fd, buf, count);
123 } while (n < 0 && errno == EINTR);
124
125 return n;
126}
127
128static gpointer
7bb6e8dd 129log_writer_thread(gpointer data)
ba0b3a0a
DM
130{
131 while (1) {
132 struct log_entry *le = (struct log_entry *)g_async_queue_timeout_pop(queue, 250000);
133 if (le == NULL) {
134 if (terminate_threads) {
135 return NULL;
136 }
137 continue;
138 }
7bb6e8dd 139
84786ec0
DM
140 if (debug) fputs(le->buf, stdout);
141
ba0b3a0a 142 int res = safe_write(outfd, le->buf, le->len);
7bb6e8dd 143
ba0b3a0a
DM
144 g_free(le);
145
146 if (res < 0) {
9da3465c 147 syslog(3, "writing log failed, stopping daemon - %s", strerror (errno));
7bb6e8dd 148 g_main_loop_quit(main_loop);
9da3465c 149 return NULL;
ba0b3a0a
DM
150 }
151 }
152
153 return NULL;
154}
155
156static int skipped_logs = 0;
157
782c4cde 158static void log_status_message(guint loglevel, const char *fmt, ...);
7bb6e8dd 159
ba0b3a0a
DM
160static void
161queue_log_entry(struct log_entry *le)
162{
163 gint len = g_async_queue_length(queue);
164
165 if (skipped_logs > 0) {
166 if (len >= (LQ_LEN - 1)) {
167 skipped_logs++;
168 } else {
7bb6e8dd 169 int skip_tmp = skipped_logs;
ba0b3a0a 170 skipped_logs = 0; // clear before calling log_status_message()
782c4cde 171 log_status_message(3, "skipped %d log entries (queue full)", skip_tmp);
ba0b3a0a
DM
172 g_async_queue_push(queue, le);
173 }
174 } else {
175 if (len >= LQ_LEN) {
176 skipped_logs++;
177 } else {
178 g_async_queue_push(queue, le);
179 }
180 }
181}
182
183
6c293b5d
WB
184#define LEPRINTF(format, ...) \
185 do { \
186 if (le->len < LE_MAX) \
187 le->len += snprintf(le->buf + le->len, LE_MAX - le->len, format, ##__VA_ARGS__); \
188 } while (0)
189#define LEPRINTTIME(sec) \
190 do { \
191 time_t tmp_sec = sec; \
192 if (le->len < (LE_MAX - 30)) \
193 le->len += strftime(le->buf + le->len, LE_MAX - le->len, "%d/%b/%Y:%H:%M:%S %z ", localtime(&tmp_sec)); \
194 } while (0)
ba0b3a0a 195
7bb6e8dd
DM
196static void
197log_status_message(guint loglevel, const char *fmt, ...)
ba0b3a0a
DM
198{
199 va_list ap;
200 va_start(ap, fmt);
7bb6e8dd 201
782c4cde
DM
202 if (loglevel > 7 ) loglevel = 7; // syslog defines level 0-7
203
ba0b3a0a
DM
204 struct log_entry *le = g_new0(struct log_entry, 1);
205
782c4cde
DM
206 LEPRINTF("0 %d - ", loglevel);
207
ba0b3a0a
DM
208 LEPRINTTIME(time(NULL));
209
210 le->len += vsnprintf(le->buf + le->len, LE_MAX - le->len, fmt, ap);
211
212 LEPRINTF("\n");
213
214 queue_log_entry(le);
9da3465c
DM
215
216 // also log to syslog
217
218 vsyslog(loglevel, fmt, ap);
ba0b3a0a
DM
219}
220
7bb6e8dd 221static int
ba0b3a0a
DM
222print_tcp(struct log_entry *le, struct tcphdr *h, int payload_len)
223{
224 LEPRINTF("PROTO=TCP ");
225
226 if (payload_len < sizeof(struct tcphdr)) {
227 LEPRINTF("LEN=%d ", payload_len);
228 LEPRINTF("INVALID=LEN ");
229 return -1;
230 }
231
232 LEPRINTF("SPT=%u DPT=%u ", ntohs(h->source), ntohs(h->dest));
233 LEPRINTF("SEQ=%u ACK=%u ", ntohl(h->seq), ntohl(h->ack_seq));
234 LEPRINTF("WINDOW=%u ", ntohs(h->window));
235
236 if (h->urg) LEPRINTF("URG ");
237 if (h->ack) LEPRINTF("ACK ");
238 if (h->psh) LEPRINTF("PSH ");
239 if (h->rst) LEPRINTF("RST ");
240 if (h->syn) LEPRINTF("SYN ");
241 if (h->fin) LEPRINTF("FIN ");
242
243 if (h->urg) LEPRINTF("URGP=%u ",ntohs(h->urg_ptr));
244
245 return 0;
246}
247
7bb6e8dd 248static int
ba0b3a0a
DM
249print_udp(struct log_entry *le, struct udphdr *h, int payload_len)
250{
251 LEPRINTF("PROTO=UDP ");
252
253 if (payload_len < sizeof(struct udphdr)) {
254 LEPRINTF("LEN=%d ", payload_len);
255 LEPRINTF("INVALID=LEN ");
256 return -1;
257 }
258
259 LEPRINTF("SPT=%u DPT=%u LEN=%u", ntohs(h->source), ntohs(h->dest), ntohs(h->len));
260
261 return 0;
262}
263
7bb6e8dd 264static int
ba0b3a0a
DM
265print_icmp(struct log_entry *le, struct icmphdr *h, int payload_len)
266{
267 char tmp[INET_ADDRSTRLEN];
268 u_int32_t gateway;
269
270 LEPRINTF("PROTO=ICMP ");
271
272 if (payload_len < sizeof(struct icmphdr)) {
273 LEPRINTF("LEN=%d ", payload_len);
274 LEPRINTF("INVALID=LEN ");
275 return -1;
276 }
277
278 LEPRINTF("TYPE=%u CODE=%u ", h->type, h->code);
279
280 switch (h->type) {
281 case ICMP_ECHO:
282 case ICMP_ECHOREPLY:
283 LEPRINTF("ID=%u SEQ=%u ", ntohs(h->un.echo.id), ntohs(h->un.echo.sequence));
284 break;
285 case ICMP_PARAMETERPROB:
286 LEPRINTF("PARAMETER=%u ", ntohl(h->un.gateway) >> 24);
287 break;
288 case ICMP_REDIRECT:
289 gateway = ntohl(h->un.gateway);
7bb6e8dd 290 inet_ntop(AF_INET, &gateway, tmp, sizeof(tmp));
ba0b3a0a
DM
291 LEPRINTF("GATEWAY=%s ", tmp);
292 break;
293 case ICMP_DEST_UNREACH:
294 if (h->code == ICMP_FRAG_NEEDED) {
295 LEPRINTF("MTU=%u ", ntohs(h->un.frag.mtu));
296 }
297 break;
298 }
299
300 return 0;
301}
302
303/* Section 3.1. SCTP Common Header Format */
304typedef struct sctphdr {
305 __be16 source;
306 __be16 dest;
307 __be32 vtag;
308 __be32 checksum;
309} __attribute__((packed)) sctp_sctphdr_t;
310
7bb6e8dd 311static int
ba0b3a0a
DM
312print_sctp(struct log_entry *le, struct sctphdr *h, int payload_len)
313{
314 LEPRINTF("PROTO=SCTP ");
315
316 if (payload_len < sizeof(struct sctphdr)) {
317 LEPRINTF("LEN=%d ", payload_len);
318 LEPRINTF("INVALID=LEN ");
319 return -1;
320 }
321
322 LEPRINTF("SPT=%u DPT=%u ", ntohs(h->source), ntohs(h->dest));
323
324 return 0;
325}
326
f5b3bf34
WB
327static int
328print_ipproto(struct log_entry *le, char * nexthdr, int payload_len, u_int8_t proto)
329{
330 switch (proto) {
331 case IPPROTO_TCP:
332 print_tcp(le, (struct tcphdr *)nexthdr, payload_len);
333 break;
334 case IPPROTO_UDP:
335 print_udp(le, (struct udphdr *)nexthdr, payload_len);
336 break;
337 case IPPROTO_ICMP:
338 print_icmp(le, (struct icmphdr *)nexthdr, payload_len);
339 break;
340 case IPPROTO_SCTP:
341 print_sctp(le, (struct sctphdr *)nexthdr, payload_len);
342 break;
343 case IPPROTO_AH:
344 LEPRINTF("PROTO=AH ");
345 break;
346 case IPPROTO_ESP:
347 LEPRINTF("PROTO=ESP ");
348 break;
349 case IPPROTO_IGMP:
350 LEPRINTF("PROTO=IGMP ");
351 break;
352 default:
353 return -1;
354 }
355 return 0;
356}
357
7bb6e8dd 358static int
ba0b3a0a
DM
359print_iphdr(struct log_entry *le, char * payload, int payload_len)
360{
361 if (payload_len < sizeof(struct iphdr)) {
362 LEPRINTF("LEN=%d ", payload_len);
363 LEPRINTF("INVALID=LEN ");
364 return -1;
365 }
366
367 struct iphdr *h = (struct iphdr *)payload;
7bb6e8dd 368
ba0b3a0a
DM
369 if (payload_len <= (u_int32_t)(h->ihl * 4)) {
370 LEPRINTF("INVALID=IHL ");
371 return -1;
372 }
373
374 char tmp[INET_ADDRSTRLEN];
7bb6e8dd
DM
375
376 inet_ntop(AF_INET, &h->saddr, tmp, sizeof(tmp));
ba0b3a0a 377 LEPRINTF("SRC=%s ", tmp);
7bb6e8dd 378 inet_ntop(AF_INET, &h->daddr, tmp, sizeof(tmp));
ba0b3a0a
DM
379 LEPRINTF("DST=%s ", tmp);
380
381 LEPRINTF("LEN=%u TOS=0x%02X PREC=0x%02X TTL=%u ID=%u ",
382 ntohs(h->tot_len), h->tos & IPTOS_TOS_MASK,
383 h->tos & IPTOS_PREC_MASK, h->ttl, ntohs(h->id));
7bb6e8dd 384
ba0b3a0a 385 short ip_off = ntohs(h->frag_off);
7bb6e8dd 386 if (ip_off & IP_OFFMASK)
ba0b3a0a
DM
387 LEPRINTF("FRAG=%u ", ip_off & IP_OFFMASK);
388
389 if (ip_off & IP_DF) LEPRINTF("DF ");
390 if (ip_off & IP_MF) LEPRINTF("MF ");
391
392 void *nexthdr = (u_int32_t *)h + h->ihl;
393 payload_len -= h->ihl * 4;
394
f5b3bf34 395 if (print_ipproto(le, nexthdr, payload_len, h->protocol) < 0) {
ba0b3a0a
DM
396 LEPRINTF("PROTO=%u ", h->protocol);
397 }
398
399 return 0;
400}
401
7bb6e8dd 402static int
d1b290fc 403print_routing(struct log_entry *le, struct ip6_rthdr *rthdr, int payload_len)
ba0b3a0a 404{
d1b290fc
WB
405 char tmp[INET6_ADDRSTRLEN];
406 LEPRINTF("TYPE=%u SEGMENTS=%u", rthdr->ip6r_type, rthdr->ip6r_segleft);
407
408 if (payload_len < sizeof(*rthdr) || payload_len < rthdr->ip6r_len*8) {
409 LEPRINTF("LEN=%d ", payload_len);
410 LEPRINTF("INVALID=LEN ");
411 return -1;
412 }
413
414 if (rthdr->ip6r_type == 0) {
415 /* Route via waypoints (deprecated), this contains a list of waypoints
416 * to visit. (RFC2460 (4.4))
417 */
d1b290fc
WB
418 struct ip6_rthdr0 *h = (struct ip6_rthdr0*)rthdr;
419 if (rthdr->ip6r_len*8 < sizeof(*h) + rthdr->ip6r_segleft * sizeof(struct in6_addr)) {
420 LEPRINTF("INVALID=SEGMENTS ");
421 return 0;
422 }
423 return 0;
424 } else if (rthdr->ip6r_type == 1) {
425 /* nimrod routing (RFC1992) */
426 return 0;
427 } else if (rthdr->ip6r_type == 2) {
428 /* RFC3375 (6.4), the layout is like type-0 but with exactly 1 address */
429 struct ip6_rthdr0 *h = (struct ip6_rthdr0*)rthdr;
430 if (rthdr->ip6r_len*8 < sizeof(*h) + sizeof(struct in6_addr)) {
431 LEPRINTF("LEN=%d ", payload_len);
432 LEPRINTF("INVALID=LEN ");
433 return -1;
434 }
435 inet_ntop(AF_INET6, &h->ip6r0_addr[0], tmp, sizeof(tmp));
436 LEPRINTF("HOME=%s ", tmp);
437 return 0;
438 }
ba0b3a0a
DM
439
440 return 0;
441}
442
d1b290fc
WB
443static int
444print_fragment(struct log_entry *le, struct ip6_frag *frag, int payload_len)
445{
446 u_int16_t offlg;
447
448 if (payload_len < sizeof(*frag)) {
449 LEPRINTF("LEN=%d ", payload_len);
450 LEPRINTF("INVALID=LEN ");
451 return -1;
452 }
453
454 offlg = ntohs(frag->ip6f_offlg);
455 LEPRINTF("FRAG=%d ID=%d ", (offlg&0x2FFF), ntohl(frag->ip6f_ident));
456 if (offlg>>15) {
457 LEPRINTF("MF ");
458 }
459 return 0;
460}
461
462static int
463print_icmp6(struct log_entry *le, struct icmp6_hdr *h, int payload_len)
464{
465 struct nd_router_advert *ra;
466 struct nd_neighbor_advert *na;
467 struct nd_redirect *re;
468 char tmp[INET6_ADDRSTRLEN];
469
470 if (payload_len < sizeof(struct icmp6_hdr)) {
471 LEPRINTF("LEN=%d ", payload_len);
472 LEPRINTF("INVALID=LEN ");
473 return -1;
474 }
475
476 LEPRINTF("TYPE=%u CODE=%u ", h->icmp6_type, h->icmp6_code);
477
478 switch (h->icmp6_type) {
479 case ICMP6_ECHO_REQUEST:
480 case ICMP6_ECHO_REPLY:
481 LEPRINTF("ID=%u SEQ=%u ", ntohs(h->icmp6_id), ntohs(h->icmp6_seq));
482 break;
483
484 case ND_ROUTER_SOLICIT:
485 /* can be followed by options, otherwise nothing to print */
486 break;
487
488 case ND_ROUTER_ADVERT:
489 ra = (struct nd_router_advert*)h;
490 LEPRINTF("HOPLIMIT=%d ", ra->nd_ra_curhoplimit);
491 /* nd_ra_flags_reserved is only 8 bit, so no swapping here as
492 * opposed to the neighbor advertisement flags (see below).
493 */
494 LEPRINTF("RA=%02x LIFETIME=%d REACHABLE=%d RETRANSMIT=%d ",
495 ra->nd_ra_flags_reserved,
496 ntohs(ra->nd_ra_router_lifetime),
497 ntohl(ra->nd_ra_reachable),
498 ntohl(ra->nd_ra_retransmit));
499 /* can be followed by options */
500 break;
501
502 case ND_NEIGHBOR_SOLICIT:
503 /* can be followed by options */
504 break;
505
506 case ND_NEIGHBOR_ADVERT:
507 na = (struct nd_neighbor_advert*)h;
508 LEPRINTF("NA=%08x ", ntohl(na->nd_na_flags_reserved));
509 /* can be followed by options */
510 break;
511
512 case ND_REDIRECT:
513 re = (struct nd_redirect*)h;
514 inet_ntop(AF_INET6, &re->nd_rd_target, tmp, sizeof(tmp));
515 LEPRINTF("TARGET=%s ", tmp);
516 inet_ntop(AF_INET6, &re->nd_rd_dst, tmp, sizeof(tmp));
517 LEPRINTF("GATEWAY=%s ", tmp);
518 /* can be followed by options */
519 break;
520
521 case ICMP6_DST_UNREACH:
522 /* CODE shows the type, no extra parameters available in ipv6 */
523 break;
524
525 case ICMP6_PACKET_TOO_BIG:
526 LEPRINTF("MTU=%u ", ntohl(h->icmp6_mtu));
527 break;
528
529 case ICMP6_TIME_EXCEEDED:
530 /* CODE shows the type (0 = hop limit, 1 = reassembly timed out) */
531 break;
532
533 case ICMP6_PARAM_PROB:
534 switch (ntohl(h->icmp6_pptr)) {
535 case ICMP6_PARAMPROB_HEADER:
536 LEPRINTF("PARAMETER=HEADER "); /* erroneous header */
537 break;
538 case ICMP6_PARAMPROB_NEXTHEADER:
539 LEPRINTF("PARAMETER=NEXTHEADER "); /* bad next-header field */
540 break;
541 case ICMP6_PARAMPROB_OPTION:
542 LEPRINTF("PARAMETER=OPTION "); /* bad ipv6 option (hop/dst header?) */
543 break;
544 default:
545 LEPRINTF("PARAMETER=%u ", ntohl(h->icmp6_pptr)); /* unknown */
546 break;
547 }
548 break;
549 }
550
551 return 0;
552}
553
554static int
555check_ip6ext(struct log_entry *le, struct ip6_ext *exthdr, int payload_len)
556{
557 if (payload_len < sizeof(*exthdr) ||
558 payload_len < exthdr->ip6e_len)
559 {
560 LEPRINTF("LEN=%d ", payload_len);
561 LEPRINTF("INVALID=LEN ");
562 return -1;
563 }
564 return 0;
565}
566
567static int
568print_nexthdr(struct log_entry *le, char *hdr, int payload_len, u_int8_t proto)
569{
570 while (1) {
571 if (print_ipproto(le, hdr, payload_len, proto) == 0)
572 return 0;
573
574 struct ip6_ext *exthdr = (struct ip6_ext*)hdr;
575
576 switch (proto) {
577 /* protocols (these return) */
578 case IPPROTO_ICMPV6:
579 LEPRINTF("PROTO=ICMPV6 ");
580 if (check_ip6ext(le, exthdr, payload_len) < 0)
581 return -1;
582 if (print_icmp6(le, (struct icmp6_hdr*)(hdr + exthdr->ip6e_len),
583 payload_len - exthdr->ip6e_len) < 0)
584 {
585 return -1;
586 }
587 return 0;
588
589 /* extension headers (these break to keep iterating) */
590 case IPPROTO_ROUTING:
591 if (check_ip6ext(le, exthdr, payload_len) < 0)
592 return -1;
593 if (print_routing(le, (struct ip6_rthdr*)hdr, payload_len) < 0)
594 return -1;
595 break;
596 case IPPROTO_FRAGMENT:
597 if (check_ip6ext(le, exthdr, payload_len) < 0)
598 return -1;
599 if (print_fragment(le, (struct ip6_frag*)hdr, payload_len) < 0)
600 return -1;
601 break;
602 case IPPROTO_HOPOPTS:
603 LEPRINTF("NEXTHDR=HOPOPTS ");
604 if (check_ip6ext(le, exthdr, payload_len) < 0)
605 return -1;
606 /* do we want to print these? */
607 break;
608 case IPPROTO_DSTOPTS:
609 LEPRINTF("NEXTHDR=DSTOPTS ");
610 if (check_ip6ext(le, exthdr, payload_len) < 0)
611 return -1;
612 /* do we want to print these? */
613 break;
614 case IPPROTO_MH:
615 LEPRINTF("NEXTHDR=MH ");
616 if (check_ip6ext(le, exthdr, payload_len) < 0)
617 return -1;
618 break;
619
620 /* unknown protocol */
621 default:
622 LEPRINTF("PROTO=%u ", proto);
623 return 0; /* bail */
624 }
625 /* next header: */
626 if (check_ip6ext(le, exthdr, payload_len) < 0)
627 return -1;
628 hdr += exthdr->ip6e_len;
629 payload_len -= exthdr->ip6e_len;
630 }
631}
632
633static int
634print_ip6hdr(struct log_entry *le, char * payload, int payload_len)
635{
636 if (payload_len < sizeof(struct ip6_hdr)) {
637 LEPRINTF("LEN=%d ", payload_len);
638 LEPRINTF("INVALID=LEN ");
639 return -1;
640 }
641
642 struct ip6_hdr *h = (struct ip6_hdr*)payload;
643
644 char tmp[INET6_ADDRSTRLEN];
645 inet_ntop(AF_INET6, &h->ip6_src, tmp, sizeof(tmp));
646 LEPRINTF("SRC=%s ", tmp);
647 inet_ntop(AF_INET6, &h->ip6_dst, tmp, sizeof(tmp));
648 LEPRINTF("DST=%s ", tmp);
649
650 LEPRINTF("LEN=%u ", ntohs(h->ip6_plen));
651
652 u_int32_t flow = ntohl(h->ip6_flow);
653 LEPRINTF("TC=%d FLOWLBL=%d ", (flow>>20)&0xFF, flow&0xFFFFF);
654
655 LEPRINTF("HOPLIMIT=%d ", h->ip6_hlim);
656
657 return print_nexthdr(le, (char *)(h+1), payload_len - sizeof(*h), h->ip6_nxt);
658}
659
ba0b3a0a 660// ebtables -I FORWARD --nflog --nflog-group 0
7bb6e8dd 661static int
ba0b3a0a
DM
662print_arp(struct log_entry *le, struct ether_arp *h, int payload_len)
663{
664 if (payload_len < sizeof(struct ether_arp)) {
665 LEPRINTF("LEN=%d ", payload_len);
666 LEPRINTF("INVALID=LEN ");
667 return -1;
668 }
669
670 LEPRINTF("SRC=%u.%u.%u.%u ", h->arp_spa[0], h->arp_spa[1],
671 h->arp_spa[2], h->arp_spa[3]);
672
673 LEPRINTF("DST=%u.%u.%u.%u ", h->arp_tpa[0], h->arp_tpa[1],
674 h->arp_tpa[2], h->arp_tpa[3]);
675
676 LEPRINTF("PROTO=ARP ");
677
678 unsigned short code = ntohs(h->arp_op);
679 switch (code) {
680 case ARPOP_REQUEST:
681 LEPRINTF("REQUEST ");
682 break;
683 case ARPOP_REPLY:
7bb6e8dd
DM
684 LEPRINTF("REPLY MAC=%02x:%02x:%02x:%02x:%02x:%02x ",
685 h->arp_sha[0], h->arp_sha[1], h->arp_sha[2],
ba0b3a0a
DM
686 h->arp_sha[3], h->arp_sha[4], h->arp_sha[5]);
687 break;
688 case ARPOP_NAK:
689 LEPRINTF("NAK ");
690 break;
691 default:
692 LEPRINTF("CODE=%u ", code);
693 }
694
695
696 // LEPRINTF("HTYPE=%u ", ntohs(h->arp_hrd));
697
698 // LEPRINTF("PTYPE=%u ", ntohs(h->arp_pro));
699
700 return 0;
701}
702
703
704static int print_pkt(struct log_entry *le, struct nflog_data *ldata, u_int8_t family)
705{
706 u_int32_t mark = nflog_get_nfmark(ldata);
707 u_int32_t indev = nflog_get_indev(ldata);
708 u_int32_t outdev = nflog_get_outdev(ldata);
709 u_int32_t physindev = nflog_get_physindev(ldata);
710 u_int32_t physoutdev = nflog_get_physoutdev(ldata);
711
712 char *prefix = nflog_get_prefix(ldata);
713 char *payload;
714 char devname[256];
7bb6e8dd 715
782c4cde
DM
716 guint32 vmid = 0;
717
718 guint8 log_level = 6; // info
719
720 char *chain_name = "-";
721
722 if (prefix != NULL) {
723 // Note: parse ":$vmid:$loglevel:$chain: $msg"
724 if (prefix[0] == ':') {
725 char *p = prefix + 1;
726 guint32 tmpid = 0;
727 while(*p >= '0' && *p <= '9') { tmpid *= 10; tmpid += *p - '0'; p++; }
728
729 if ((*p == ':') &&
730 (p[1] >= '0' && p[1] <= '7') &&
731 (p[2] == ':')) {
732
733 guint8 tmp_level = p[1] - '0'; // store for later use
734 char *chain_start = p + 3; // store for later use
735 p = chain_start;
736 while (*p && *p != ':' && *p != ' ') p++;
737 int len = p - chain_start;
738
739 if (*p == ':' && p[1] == ' ' && len && (len <= MAX_CHAIN_LEN)) {
740 // parsing successful
741
742 *p = 0; // terminate string
743
744 vmid = tmpid;
745 log_level = tmp_level;
746 chain_name = chain_start;
747 prefix = p + 2; // the rest
7bb6e8dd 748 }
782c4cde
DM
749 }
750 }
751 }
752
753 LEPRINTF("%d ", vmid);
754
755 LEPRINTF("%d ", log_level);
756
757 LEPRINTF("%s ", chain_name);
758
ba0b3a0a 759 struct timeval ts;
2388cab1
WB
760 if (nflog_get_timestamp(ldata, &ts) == 0) {
761 LEPRINTTIME(ts.tv_sec);
762 } else {
763 LEPRINTTIME(time(NULL));
764 }
ba0b3a0a 765
ba0b3a0a
DM
766 if (prefix != NULL) {
767 LEPRINTF("%s", prefix);
768 }
7bb6e8dd
DM
769
770 if (indev > 0) {
e7ceb650 771 if (nlif_index2name(nlifh, indev, devname) != -1) {
7bb6e8dd 772 LEPRINTF("IN=%s ", devname);
e7ceb650 773 } else {
7bb6e8dd 774 LEPRINTF("IN=%u ", indev);
e7ceb650 775 }
ba0b3a0a
DM
776 }
777
e7ceb650
DM
778 if (outdev > 0) {
779 if (nlif_index2name(nlifh, outdev, devname) != -1) {
780 LEPRINTF("OUT=%s ", devname);
781 } else {
782 LEPRINTF("OUT=%u ", outdev);
783 }
ba0b3a0a
DM
784 }
785
7bb6e8dd 786 if (physindev > 0) {
e7ceb650
DM
787 if (nlif_index2name(nlifh, physindev, devname) != -1) {
788 LEPRINTF("PHYSIN=%s ", devname);
789 } else {
790 LEPRINTF("PHYSIN=%u ", physindev);
791 }
ba0b3a0a 792 }
7bb6e8dd 793
e7ceb650
DM
794 if (physoutdev > 0) {
795 if (nlif_index2name(nlifh, physoutdev, devname) != -1) {
796 LEPRINTF("PHYSOUT=%s ", devname);
797 } else {
798 LEPRINTF("PHYSOUT=%u ", physoutdev);
799 }
ba0b3a0a
DM
800 }
801
802 int payload_len = nflog_get_payload(ldata, &payload);
803
804 int hwhdrlen = nflog_get_msg_packet_hwhdrlen(ldata);
805 if (hwhdrlen > 0) {
806 unsigned char *hwhdr = (unsigned char *)nflog_get_msg_packet_hwhdr(ldata);
807 if (hwhdr != NULL) {
808 int i;
809 LEPRINTF("MAC=");
810 for (i = 0; i < hwhdrlen; i++) {
811 LEPRINTF("%02x", hwhdr[i]);
812 if (i < (hwhdrlen -1 )) LEPRINTF(":");
813 }
814 LEPRINTF(" ");
815 }
816 }
817
818 u_int16_t hw_protocol = 0;
819 struct nfulnl_msg_packet_hdr *ph = NULL;
820
821 switch (family) {
7bb6e8dd 822 case AF_INET:
ba0b3a0a
DM
823 print_iphdr(le, payload, payload_len);
824 break;
825 case AF_INET6:
826 print_ip6hdr(le, payload, payload_len);
827 break;
828 case AF_BRIDGE:
829 ph = nflog_get_msg_packet_hdr(ldata);
830 if (ph) hw_protocol = ntohs(ph->hw_protocol);
831
832 switch (hw_protocol) {
833 case ETH_P_IP:
834 print_iphdr(le, payload, payload_len);
835 break;
836 case ETH_P_IPV6:
837 print_ip6hdr(le, payload, payload_len);
838 break;
839 case ETH_P_ARP:
840 print_arp(le, (struct ether_arp *)payload, payload_len);
841 break;
842 }
843 break;
844 }
845
846 if (mark) LEPRINTF("mark=%u ", mark);
847
848
849 return 0;
850
851}
852
7bb6e8dd 853static int
ba0b3a0a
DM
854nflog_cb(struct nflog_g_handle *gh, struct nfgenmsg *nfmsg,
855 struct nflog_data *nfa, void *data)
856{
857 struct log_entry *le = g_new0(struct log_entry, 1);
7bb6e8dd 858
ba0b3a0a
DM
859 print_pkt(le, nfa, nfmsg->nfgen_family);
860
861 LEPRINTF("\n"); // add newline
862
863 queue_log_entry(le);
864
865 return 0;
866}
867
868static gboolean
869nflog_read_cb(GIOChannel *source,
870 GIOCondition condition,
871 gpointer data)
872{
873 int rv = 0;
874 gchar buf[8192];
875
876 int fd = g_io_channel_unix_get_fd(source);
7bb6e8dd 877
ba0b3a0a
DM
878 if ((rv = recv(fd, buf, sizeof(buf), 0)) && rv >= 0) {
879 nflog_handle_packet(logh, buf, rv);
880 }
881
882 return TRUE;
883}
884
885static gboolean
886nlif_read_cb(GIOChannel *source,
887 GIOCondition condition,
888 gpointer data)
889{
9da3465c
DM
890 static int last_res = 0;
891 int res;
892
893 if ((res = nlif_catch(nlifh)) < 0) {
894 if (last_res == 0) { // only report once
895 log_status_message(3, "nlif_catch failed (res = %d)", res);
896 }
897 last_res = res;
898 } else {
899 last_res = 0;
900 }
901
7bb6e8dd 902 return TRUE;
ba0b3a0a
DM
903}
904
ba0b3a0a 905static gboolean
985fd74e
DM
906signal_read_cb(GIOChannel *source,
907 GIOCondition condition,
908 gpointer data)
ba0b3a0a 909{
985fd74e
DM
910 int rv = 0;
911 struct signalfd_siginfo si;
ba0b3a0a 912
985fd74e 913 int fd = g_io_channel_unix_get_fd(source);
ba0b3a0a 914
985fd74e
DM
915 if ((rv = read(fd, &si, sizeof(si))) && rv >= 0) {
916 terminate_threads = TRUE;
917 log_status_message(5, "received terminate request (signal)");
918 g_main_loop_quit(main_loop);
919 }
7bb6e8dd 920
ba0b3a0a
DM
921 return TRUE;
922}
7bb6e8dd 923
fdf943fe
DL
924static int
925nfct_cb(const struct nlmsghdr *nlh,
926 enum nf_conntrack_msg_type type,
927 struct nf_conntrack *ct,
928 void *data)
929{
930 struct log_entry *le = g_new0(struct log_entry, 1);
931 int len = nfct_snprintf(&le->buf[le->len], LE_MAX - le->len,
932 ct, type, NFCT_O_DEFAULT,
933 NFCT_OF_SHOW_LAYER3|NFCT_OF_TIMESTAMP);
934 le->len += len;
935
936 if (le->len == LE_MAX) {
937 le->buf[le->len-1] = '\n';
938 } else { // le->len < LE_MAX
939 le->buf[le->len++] = '\n';
940 }
941
942 queue_log_entry(le);
943
944 return NFCT_CB_STOP;
945}
946
947static gboolean
948nfct_read_cb(GIOChannel *source,
949 GIOCondition condition,
950 gpointer data)
951{
952 int res;
953 if ((res = nfct_catch(nfcth)) < 0) {
954 log_status_message(3, "error catching nfct");
955 return FALSE;
956 }
957 return TRUE;
958}
959
ba0b3a0a
DM
960int
961main(int argc, char *argv[])
962{
963 int lockfd = -1;
985fd74e
DM
964 int sigfd = -1;
965
ba0b3a0a
DM
966 gboolean wrote_pidfile = FALSE;
967
dc4b58b5 968 openlog("pvefw-logger", LOG_CONS|LOG_PID, LOG_DAEMON);
9da3465c 969
84786ec0
DM
970 GOptionContext *context;
971
972 GOptionEntry entries[] = {
973 { "debug", 'd', 0, G_OPTION_ARG_NONE, &debug, "Turn on debug messages", NULL },
974 { "foreground", 'f', 0, G_OPTION_ARG_NONE, &foreground, "Do not daemonize server", NULL },
fdf943fe 975 { "conntrack", 0, 0, G_OPTION_ARG_NONE, &conntrack, "Add conntrack logging", NULL },
84786ec0
DM
976 { NULL },
977 };
978
979 context = g_option_context_new("");
980 g_option_context_add_main_entries (context, entries, NULL);
981
982 GError *err = NULL;
983 if (!g_option_context_parse (context, &argc, &argv, &err)) {
984 fprintf(stderr, "error: %s\n", err->message);
985 fprintf(stderr, "%s", g_option_context_get_help(context, FALSE, NULL));
986 g_error_free (err);
987 exit(-1);
988 }
989
990 if (optind < argc) {
991 fprintf(stderr, "error: too many arguments\n");
992 fprintf(stderr, "%s", g_option_context_get_help(context, FALSE, NULL));
993 exit(-1);
994 }
995
996 g_option_context_free(context);
997
fdf943fe
DL
998 if (!conntrack) {
999 int log_nf_conntrackfd = open(LOG_CONNTRACK_FILE, O_RDONLY);
1000 if (log_nf_conntrackfd == -1) {
1001 if (errno != ENOENT) {
1002 fprintf(stderr, "error: failed to open "LOG_CONNTRACK_FILE": %s\n", strerror(errno));
1003 }
1004 } else {
1005 char c = '0';
1006 ssize_t bytes = read(log_nf_conntrackfd, &c, sizeof(c));
1007 if (bytes < 0) {
1008 fprintf(stderr, "error: failed to read value in log_nf_conntrack: %s\n", strerror(errno));
1009 } else {
1010 conntrack = (c == '1');
1011 }
1012 }
1013 }
1014
84786ec0
DM
1015 if (debug) foreground = TRUE;
1016
ba0b3a0a 1017 if ((lockfd = open(LOCKFILE, O_RDWR|O_CREAT|O_APPEND, 0644)) == -1) {
84786ec0 1018 fprintf(stderr, "unable to create lock '%s': %s\n", LOCKFILE, strerror (errno) );
ba0b3a0a
DM
1019 exit(-1);
1020 }
1021
1022 for (int i = 10; i >= 0; i--) {
1023 if (flock(lockfd, LOCK_EX|LOCK_NB) != 0) {
1024 if (!i) {
84786ec0 1025 fprintf(stderr, "unable to aquire lock '%s': %s\n", LOCKFILE, strerror (errno));
ba0b3a0a
DM
1026 exit(-1);
1027 }
1028 if (i == 10)
1029 fprintf(stderr, "unable to aquire lock '%s' - trying again.\n", LOCKFILE);
7bb6e8dd 1030
ba0b3a0a
DM
1031 sleep(1);
1032 }
1033 }
1034
1035 if ((outfd = open(LOGFILE, O_WRONLY|O_CREAT|O_APPEND, 0644)) == -1) {
84786ec0 1036 fprintf(stderr, "unable to open file '%s': %s\n", LOGFILE, strerror (errno));
ba0b3a0a
DM
1037 exit(-1);
1038 }
1039
1040 if ((logh = nflog_open()) == NULL) {
1041 fprintf(stderr, "unable to open nflog\n");
1042 exit(-1);
1043 }
1044
0dc6e638 1045 if (nflog_bind_pf(logh, AF_INET) < 0) {
ba0b3a0a
DM
1046 fprintf(stderr, "nflog_bind_pf AF_INET failed\n");
1047 exit(-1);
1048 }
1049
1050#if 0
1051 if (!nflog_bind_pf(logh, AF_INET6) <= 0) {
1052 fprintf(stderr, "nflog_bind_pf AF_INET6 failed\n");
1053 exit(-1);
1054 }
1055#endif
7bb6e8dd 1056
0dc6e638 1057 if (nflog_bind_pf(logh, AF_BRIDGE) < 0) {
ba0b3a0a
DM
1058 fprintf(stderr, "nflog_bind_pf AF_BRIDGE failed\n");
1059 exit(-1);
1060 }
1061
1062 struct nflog_g_handle *qh = nflog_bind_group(logh, 0);
1063 if (!qh) {
84786ec0 1064 fprintf(stderr, "no nflog handle for group 0\n");
ba0b3a0a
DM
1065 exit(-1);
1066 }
7bb6e8dd 1067
ba0b3a0a
DM
1068 if (nflog_set_mode(qh, NFULNL_COPY_PACKET, 0xffff) < 0) {
1069 fprintf(stderr, "can't set packet copy mode\n");
1070 exit(-1);
1071 }
1072
1073 if ((nlifh = nlif_open()) == NULL) {
1074 fprintf(stderr, "unable to open netlink interface handle\n");
1075 exit(-1);
1076 }
1077
fdf943fe
DL
1078 if (conntrack) {
1079 if ((nfcth = nfct_open(CONNTRACK, NF_NETLINK_CONNTRACK_NEW|NF_NETLINK_CONNTRACK_DESTROY)) == NULL) {
1080 fprintf(stderr, "unable to open netfilter conntrack\n");
1081 exit(-1);
1082 }
1083 }
1084
985fd74e
DM
1085 sigset_t mask;
1086 sigemptyset(&mask);
1087 sigaddset(&mask, SIGINT);
1088 sigaddset(&mask, SIGTERM);
1089
1090 sigprocmask(SIG_BLOCK, &mask, NULL);
1091
1092 if ((sigfd = signalfd(-1, &mask, SFD_NONBLOCK)) < 0) {
1093 fprintf(stderr, "unable to open signalfd: %s\n", strerror (errno));
1094 exit(-1);
1095 }
1096
ba0b3a0a
DM
1097 if (!foreground) {
1098 pid_t cpid = fork();
1099
1100 if (cpid == -1) {
1101 fprintf(stderr, "failed to daemonize program - %s\n", strerror (errno));
1102 exit(-1);
1103 } else if (cpid) {
1104 write_pidfile(cpid);
1105 _exit(0);
1106 } else {
1107 int nullfd;
1108
1109 if (chroot("/") != 0) fprintf(stderr, "chroot '/' failed - %s\n", strerror (errno));
1110
1111 if ((nullfd = open("/dev/null", O_RDWR, 0)) != -1) {
1112 dup2(nullfd, 0);
1113 dup2(nullfd, 1);
1114 dup2(nullfd, 2);
1115 if (nullfd > 2)
1116 close (nullfd);
1117 }
1118
1119 setsid();
1120 }
1121 } else {
1122 write_pidfile(getpid());
1123 }
1124
1125 wrote_pidfile = TRUE;
1126
1127 nflog_callback_register(qh, &nflog_cb, logh);
1128
1129 queue = g_async_queue_new_full(g_free);
1130
782c4cde 1131 log_status_message(5, "starting pvefw logger");
ba0b3a0a
DM
1132
1133 nlif_query(nlifh);
1134
1135 GIOChannel *nlif_ch = g_io_channel_unix_new(nlif_fd(nlifh));
1136
1137 g_io_add_watch(nlif_ch, G_IO_IN, nlif_read_cb, NULL);
1138
1139 int logfd = nflog_fd(logh);
1140 GIOChannel *nflog_ch = g_io_channel_unix_new(logfd);
1141
1142 g_io_add_watch(nflog_ch, G_IO_IN, nflog_read_cb, NULL);
1143
fdf943fe
DL
1144 if (conntrack) {
1145 nfct_callback_register2(nfcth, NFCT_T_NEW|NFCT_T_DESTROY, &nfct_cb, NULL);
1146 int nfctfd = nfct_fd(nfcth);
1147 GIOChannel *nfct_ch = g_io_channel_unix_new(nfctfd);
1148 g_io_add_watch(nfct_ch, G_IO_IN, nfct_read_cb, NULL);
1149 }
1150
985fd74e 1151 GIOChannel *sig_ch = g_io_channel_unix_new(sigfd);
985fd74e 1152 if (!g_io_add_watch(sig_ch, G_IO_IN, signal_read_cb, NULL)) {
e2beb7aa 1153 exit(-1);
985fd74e
DM
1154 }
1155
ba0b3a0a 1156 GThread *wthread = g_thread_new("log_writer_thread", log_writer_thread, NULL);
7bb6e8dd 1157
ba0b3a0a 1158 main_loop = g_main_loop_new(NULL, TRUE);
7bb6e8dd 1159
ba0b3a0a
DM
1160 g_main_loop_run(main_loop);
1161
782c4cde 1162 log_status_message(5, "stopping pvefw logger");
ba0b3a0a
DM
1163
1164 g_thread_join(wthread);
1165
1166 close(outfd);
1167
fdf943fe
DL
1168 if (conntrack) {
1169 nfct_callback_unregister2(nfcth);
1170 nfct_close(nfcth);
1171 }
1172
ba0b3a0a
DM
1173 nflog_close(logh);
1174
1175 if (wrote_pidfile)
1176 unlink(PIDFILE);
1177
1178 exit(0);
1179}