X-Git-Url: https://git.proxmox.com/?p=pve-firewall.git;a=blobdiff_plain;f=src%2Fpvefw-logger.c;h=f77f56f7f71be1e9eafa5f997639e3770e6e82aa;hp=5b00758239bc75d57c2b58bd6aa42ff8d26630d2;hb=056f92115a15c20c65b597dbee2e4f86bb1e2ff8;hpb=f5b3bf344df7ef631bea85f7dce78a5328a13eb7 diff --git a/src/pvefw-logger.c b/src/pvefw-logger.c index 5b00758..f77f56f 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 @@ -190,8 +196,7 @@ queue_log_entry(struct log_entry *le) 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 @@ -201,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"); @@ -209,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 @@ -394,13 +403,263 @@ print_iphdr(struct log_entry *le, char * payload, int payload_len) } static int -print_ip6hdr(struct log_entry *le, char * payload, int payload_len) +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) { - LEPRINTF("IPV6 logging not implemented "); + 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 ND_ROUTER_SOLICIT: + /* can be followed by options, otherwise nothing to print */ + break; + + 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 ND_NEIGHBOR_SOLICIT: + /* can be followed by options */ + break; + + 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 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 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; + } + + 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; + + 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; + 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; + hdr += exthdr->ip6e_len; + payload_len -= exthdr->ip6e_len; + } +} + +static int +print_ip6hdr(struct log_entry *le, char * payload, int payload_len) +{ + if (payload_len < sizeof(struct ip6_hdr)) { + LEPRINTF("LEN=%d ", payload_len); + LEPRINTF("INVALID=LEN "); + return -1; + } + + 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 static int print_arp(struct log_entry *le, struct ether_arp *h, int payload_len) @@ -501,9 +760,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); @@ -663,6 +924,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[]) { @@ -671,13 +972,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 }, }; @@ -700,6 +1002,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) { @@ -730,7 +1049,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); } @@ -742,7 +1061,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); } @@ -763,6 +1082,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); @@ -822,6 +1148,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); @@ -839,6 +1172,11 @@ main(int argc, char *argv[]) close(outfd); + if (conntrack) { + nfct_callback_unregister2(nfcth); + nfct_close(nfcth); + } + nflog_close(logh); if (wrote_pidfile)