X-Git-Url: https://git.proxmox.com/?p=pve-firewall.git;a=blobdiff_plain;f=src%2Fpvefw-logger.c;h=181d5f1e8b7520b008b7761943ce9d04f6e3b6cc;hp=b7a2fb2983258e8cfc14d322151e0913e642e6bf;hb=6b9da9b01e944bc756d51029303eab22e9cbac8b;hpb=0b18ebe80ebaca7428dc11b13fe154104b121d50 diff --git a/src/pvefw-logger.c b/src/pvefw-logger.c index b7a2fb2..181d5f1 100644 --- a/src/pvefw-logger.c +++ b/src/pvefw-logger.c @@ -40,8 +40,11 @@ #include #include #include +#include #include #include +#include +#include #include #include #include @@ -51,10 +54,12 @@ 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; /* @@ -74,6 +79,7 @@ Example: #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 @@ -175,14 +181,22 @@ queue_log_entry(struct log_entry *le) } -#define LEPRINTF(format, ...) { if (le->len < LE_MAX) le->len += snprintf(le->buf + le->len, LE_MAX - le->len, format, ##__VA_ARGS__); } -#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)); } +#define LEPRINTF(format, ...) \ + do { \ + if (le->len < LE_MAX) \ + le->len += snprintf(le->buf + le->len, LE_MAX - le->len, format, ##__VA_ARGS__); \ + } while (0) +#define LEPRINTTIME(sec) \ + do { \ + 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)); \ + } while (0) 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 @@ -192,7 +206,10 @@ log_status_message(guint loglevel, const char *fmt, ...) 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"); @@ -200,7 +217,8 @@ log_status_message(guint loglevel, const char *fmt, ...) // also log to syslog - vsyslog(loglevel, fmt, ap); + vsyslog(loglevel, fmt, ap2); + va_end(ap2); } static int @@ -309,6 +327,37 @@ print_sctp(struct log_entry *le, struct sctphdr *h, int payload_len) return 0; } +static int +print_ipproto(struct log_entry *le, char * nexthdr, int payload_len, u_int8_t proto) +{ + switch (proto) { + case IPPROTO_TCP: + print_tcp(le, (struct tcphdr *)nexthdr, payload_len); + break; + case IPPROTO_UDP: + print_udp(le, (struct udphdr *)nexthdr, payload_len); + break; + case IPPROTO_ICMP: + print_icmp(le, (struct icmphdr *)nexthdr, payload_len); + break; + case IPPROTO_SCTP: + print_sctp(le, (struct sctphdr *)nexthdr, payload_len); + break; + case IPPROTO_AH: + LEPRINTF("PROTO=AH "); + break; + case IPPROTO_ESP: + LEPRINTF("PROTO=ESP "); + break; + case IPPROTO_IGMP: + LEPRINTF("PROTO=IGMP "); + break; + default: + return -1; + } + return 0; +} + static int print_iphdr(struct log_entry *le, char * payload, int payload_len) { @@ -346,41 +395,275 @@ print_iphdr(struct log_entry *le, char * payload, int payload_len) void *nexthdr = (u_int32_t *)h + h->ihl; payload_len -= h->ihl * 4; - switch (h->protocol) { - case IPPROTO_TCP: - print_tcp(le, (struct tcphdr *)nexthdr, payload_len); + if (print_ipproto(le, nexthdr, payload_len, h->protocol) < 0) { + LEPRINTF("PROTO=%u ", h->protocol); + } + + return 0; +} + +static int +print_routing(struct log_entry *le, struct ip6_rthdr *rthdr, int payload_len) +{ + char tmp[INET6_ADDRSTRLEN]; + LEPRINTF("TYPE=%u SEGMENTS=%u", rthdr->ip6r_type, rthdr->ip6r_segleft); + + if (payload_len < sizeof(*rthdr) || payload_len < rthdr->ip6r_len*8) { + LEPRINTF("LEN=%d ", payload_len); + LEPRINTF("INVALID=LEN "); + return -1; + } + + if (rthdr->ip6r_type == 0) { + /* Route via waypoints (deprecated), this contains a list of waypoints + * to visit. (RFC2460 (4.4)) + */ + struct ip6_rthdr0 *h = (struct ip6_rthdr0*)rthdr; + if (rthdr->ip6r_len*8 < sizeof(*h) + rthdr->ip6r_segleft * sizeof(struct in6_addr)) { + LEPRINTF("INVALID=SEGMENTS "); + return 0; + } + return 0; + } else if (rthdr->ip6r_type == 1) { + /* nimrod routing (RFC1992) */ + return 0; + } else if (rthdr->ip6r_type == 2) { + /* RFC3375 (6.4), the layout is like type-0 but with exactly 1 address */ + struct ip6_rthdr0 *h = (struct ip6_rthdr0*)rthdr; + if (rthdr->ip6r_len*8 < sizeof(*h) + sizeof(struct in6_addr)) { + LEPRINTF("LEN=%d ", payload_len); + LEPRINTF("INVALID=LEN "); + return -1; + } + inet_ntop(AF_INET6, &h->ip6r0_addr[0], tmp, sizeof(tmp)); + LEPRINTF("HOME=%s ", tmp); + return 0; + } + + return 0; +} + +static int +print_fragment(struct log_entry *le, struct ip6_frag *frag, int payload_len) +{ + u_int16_t offlg; + + if (payload_len < sizeof(*frag)) { + LEPRINTF("LEN=%d ", payload_len); + LEPRINTF("INVALID=LEN "); + return -1; + } + + offlg = ntohs(frag->ip6f_offlg); + LEPRINTF("FRAG=%d ID=%d ", (offlg&0x2FFF), ntohl(frag->ip6f_ident)); + if (offlg>>15) { + LEPRINTF("MF "); + } + return 0; +} + +static int +print_icmp6(struct log_entry *le, struct icmp6_hdr *h, int payload_len) +{ + struct nd_router_advert *ra; + struct nd_neighbor_advert *na; + struct nd_redirect *re; + char tmp[INET6_ADDRSTRLEN]; + + if (payload_len < sizeof(struct icmp6_hdr)) { + LEPRINTF("LEN=%d ", payload_len); + LEPRINTF("INVALID=LEN "); + return -1; + } + + LEPRINTF("TYPE=%u CODE=%u ", h->icmp6_type, h->icmp6_code); + + switch (h->icmp6_type) { + case ICMP6_ECHO_REQUEST: + case ICMP6_ECHO_REPLY: + LEPRINTF("ID=%u SEQ=%u ", ntohs(h->icmp6_id), ntohs(h->icmp6_seq)); break; - case IPPROTO_UDP: - print_udp(le, (struct udphdr *)nexthdr, payload_len); + + case ND_ROUTER_SOLICIT: + /* can be followed by options, otherwise nothing to print */ break; - case IPPROTO_ICMP: - print_icmp(le, (struct icmphdr *)nexthdr, payload_len); + + case ND_ROUTER_ADVERT: + ra = (struct nd_router_advert*)h; + LEPRINTF("HOPLIMIT=%d ", ra->nd_ra_curhoplimit); + /* nd_ra_flags_reserved is only 8 bit, so no swapping here as + * opposed to the neighbor advertisement flags (see below). + */ + LEPRINTF("RA=%02x LIFETIME=%d REACHABLE=%d RETRANSMIT=%d ", + ra->nd_ra_flags_reserved, + ntohs(ra->nd_ra_router_lifetime), + ntohl(ra->nd_ra_reachable), + ntohl(ra->nd_ra_retransmit)); + /* can be followed by options */ break; - case IPPROTO_SCTP: - print_sctp(le, (struct sctphdr *)nexthdr, payload_len); + + case ND_NEIGHBOR_SOLICIT: + /* can be followed by options */ break; - case IPPROTO_AH: - LEPRINTF("PROTO=AH "); + + case ND_NEIGHBOR_ADVERT: + na = (struct nd_neighbor_advert*)h; + LEPRINTF("NA=%08x ", ntohl(na->nd_na_flags_reserved)); + /* can be followed by options */ break; - case IPPROTO_ESP: - LEPRINTF("PROTO=ESP "); + + case ND_REDIRECT: + re = (struct nd_redirect*)h; + inet_ntop(AF_INET6, &re->nd_rd_target, tmp, sizeof(tmp)); + LEPRINTF("TARGET=%s ", tmp); + inet_ntop(AF_INET6, &re->nd_rd_dst, tmp, sizeof(tmp)); + LEPRINTF("GATEWAY=%s ", tmp); + /* can be followed by options */ break; - case IPPROTO_IGMP: - LEPRINTF("PROTO=IGMP "); + + case ICMP6_DST_UNREACH: + /* CODE shows the type, no extra parameters available in ipv6 */ + break; + + case ICMP6_PACKET_TOO_BIG: + LEPRINTF("MTU=%u ", ntohl(h->icmp6_mtu)); + break; + + case ICMP6_TIME_EXCEEDED: + /* CODE shows the type (0 = hop limit, 1 = reassembly timed out) */ + break; + + case ICMP6_PARAM_PROB: + switch (ntohl(h->icmp6_pptr)) { + case ICMP6_PARAMPROB_HEADER: + LEPRINTF("PARAMETER=HEADER "); /* erroneous header */ + break; + case ICMP6_PARAMPROB_NEXTHEADER: + LEPRINTF("PARAMETER=NEXTHEADER "); /* bad next-header field */ + break; + case ICMP6_PARAMPROB_OPTION: + LEPRINTF("PARAMETER=OPTION "); /* bad ipv6 option (hop/dst header?) */ + break; + default: + LEPRINTF("PARAMETER=%u ", ntohl(h->icmp6_pptr)); /* unknown */ + break; + } break; - default: - LEPRINTF("PROTO=%u ", h->protocol); } return 0; } +static int +check_ip6ext(struct log_entry *le, struct ip6_ext *exthdr, int payload_len) +{ + if (payload_len < sizeof(*exthdr) || + payload_len < exthdr->ip6e_len) + { + LEPRINTF("LEN=%d ", payload_len); + LEPRINTF("INVALID=LEN "); + return -1; + } + return 0; +} + +static int +print_nexthdr(struct log_entry *le, char *hdr, int payload_len, u_int8_t proto) +{ + while (1) { + if (print_ipproto(le, hdr, payload_len, proto) == 0) + return 0; + + struct ip6_ext *exthdr = (struct ip6_ext*)hdr; + int ext_len = 0; + + switch (proto) { + /* protocols (these return) */ + case IPPROTO_ICMPV6: + LEPRINTF("PROTO=ICMPV6 "); + if (check_ip6ext(le, exthdr, payload_len) < 0) + return -1; + if (print_icmp6(le, (struct icmp6_hdr*)(hdr + exthdr->ip6e_len), + payload_len - exthdr->ip6e_len) < 0) + { + return -1; + } + return 0; + + /* extension headers (these break to keep iterating) */ + case IPPROTO_ROUTING: + if (check_ip6ext(le, exthdr, payload_len) < 0) + return -1; + if (print_routing(le, (struct ip6_rthdr*)hdr, payload_len) < 0) + return -1; + break; + case IPPROTO_FRAGMENT: + if (check_ip6ext(le, exthdr, payload_len) < 0) + 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 "); + if (check_ip6ext(le, exthdr, payload_len) < 0) + return -1; + /* do we want to print these? */ + break; + case IPPROTO_DSTOPTS: + LEPRINTF("NEXTHDR=DSTOPTS "); + if (check_ip6ext(le, exthdr, payload_len) < 0) + return -1; + /* do we want to print these? */ + break; + case IPPROTO_MH: + LEPRINTF("NEXTHDR=MH "); + if (check_ip6ext(le, exthdr, payload_len) < 0) + return -1; + break; + + /* unknown protocol */ + default: + LEPRINTF("PROTO=%u ", proto); + return 0; /* bail */ + } + /* next header: */ + if (check_ip6ext(le, exthdr, payload_len) < 0) + return -1; + if(ext_len == 0) { + ext_len = (exthdr->ip6e_len+1) * 8; + } + hdr += ext_len; + payload_len -= ext_len; + proto = exthdr->ip6e_nxt; + } +} + static int print_ip6hdr(struct log_entry *le, char * payload, int payload_len) { - LEPRINTF("IPV6 logging not implemented "); + if (payload_len < sizeof(struct ip6_hdr)) { + LEPRINTF("LEN=%d ", payload_len); + LEPRINTF("INVALID=LEN "); + return -1; + } - return 0; + struct ip6_hdr *h = (struct ip6_hdr*)payload; + + char tmp[INET6_ADDRSTRLEN]; + inet_ntop(AF_INET6, &h->ip6_src, tmp, sizeof(tmp)); + LEPRINTF("SRC=%s ", tmp); + inet_ntop(AF_INET6, &h->ip6_dst, tmp, sizeof(tmp)); + LEPRINTF("DST=%s ", tmp); + + LEPRINTF("LEN=%u ", ntohs(h->ip6_plen)); + + u_int32_t flow = ntohl(h->ip6_flow); + LEPRINTF("TC=%d FLOWLBL=%d ", (flow>>20)&0xFF, flow&0xFFFFF); + + LEPRINTF("HOPLIMIT=%d ", h->ip6_hlim); + + return print_nexthdr(le, (char *)(h+1), payload_len - sizeof(*h), h->ip6_nxt); } // ebtables -I FORWARD --nflog --nflog-group 0 @@ -483,9 +766,11 @@ static int print_pkt(struct log_entry *le, struct nflog_data *ldata, u_int8_t fa 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); @@ -645,6 +930,46 @@ signal_read_cb(GIOChannel *source, 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[]) { @@ -653,13 +978,14 @@ 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 }, }; @@ -682,6 +1008,23 @@ main(int argc, char *argv[]) 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) { @@ -712,7 +1055,7 @@ main(int argc, char *argv[]) exit(-1); } - if (!nflog_bind_pf(logh, AF_INET) <= 0) { + if (nflog_bind_pf(logh, AF_INET) < 0) { fprintf(stderr, "nflog_bind_pf AF_INET failed\n"); exit(-1); } @@ -724,7 +1067,7 @@ main(int argc, char *argv[]) } #endif - if (!nflog_bind_pf(logh, AF_BRIDGE) <= 0) { + if (nflog_bind_pf(logh, AF_BRIDGE) < 0) { fprintf(stderr, "nflog_bind_pf AF_BRIDGE failed\n"); exit(-1); } @@ -745,6 +1088,13 @@ main(int argc, char *argv[]) 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); @@ -804,6 +1154,13 @@ main(int argc, char *argv[]) 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); @@ -821,6 +1178,11 @@ main(int argc, char *argv[]) close(outfd); + if (conntrack) { + nfct_callback_unregister2(nfcth); + nfct_close(nfcth); + } + nflog_close(logh); if (wrote_pidfile)