[pve-devel] [PATCH firewall 5/5] logger: basic ipv6 support
Wolfgang Bumiller
w.bumiller at proxmox.com
Tue Jan 26 10:22:53 CET 2016
Support for:
* IPv6 main header
* ICMPv6:
- echo request/reply
- NDP
- redirects
* destination unreachable message
* packet too big message
* time exceeded message
* parameter problem messages:
- erroneous header
- bad next-header
- bad ipv6 option
* extension headers:
- routing
- fragmentation
- skipping over: hopopts, destopts and mobile home
---
src/pvefw-logger.c | 309 ++++++++++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 307 insertions(+), 2 deletions(-)
diff --git a/src/pvefw-logger.c b/src/pvefw-logger.c
index 5b00758..ae3b5a0 100644
--- a/src/pvefw-logger.c
+++ b/src/pvefw-logger.c
@@ -42,6 +42,8 @@
#include <libnetfilter_log/libnetfilter_log.h>
#include <netinet/ip.h>
#include <netinet/ip_icmp.h>
+#include <netinet/ip6.h>
+#include <netinet/icmp6.h>
#include <netinet/udp.h>
#include <netinet/tcp.h>
#include <netinet/if_ether.h>
@@ -394,13 +396,316 @@ 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=%d SEGMENTS=%d", 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))
+ */
+ unsigned i;
+ 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;
+ }
+ for (i = 0; i != (unsigned)rthdr->ip6r_segleft; ++i) {
+ inet_ntop(AF_INET6, &h->ip6r0_addr[i], tmp, sizeof(tmp));
+ LEPRINTF("WAYPOINT=%s ", tmp);
+ }
+ 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)
{
- LEPRINTF("IPV6 logging not implemented ");
+ 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);
+ if (ra->nd_ra_flags_reserved) {
+ u_int8_t rest;
+ LEPRINTF("RA=");
+ if (ra->nd_ra_flags_reserved & ND_RA_FLAG_MANAGED) {
+ LEPRINTF("M");
+ }
+ if (ra->nd_ra_flags_reserved & ND_RA_FLAG_OTHER) {
+ LEPRINTF("O");
+ }
+ if (ra->nd_ra_flags_reserved & ND_RA_FLAG_HOME_AGENT) {
+ LEPRINTF("H");
+ }
+ rest = ra->nd_ra_flags_reserved & ~(ND_RA_FLAG_MANAGED |
+ ND_RA_FLAG_OTHER |
+ ND_RA_FLAG_HOME_AGENT);
+ /* nd_ra_flags_reserved is only 8 bit, so no swapping here as
+ * opposed to the neighbor advertisement flags (see below).
+ */
+ if (rest) {
+ LEPRINTF("+%02x ", rest);
+ } else {
+ LEPRINTF(" ");
+ }
+ }
+ LEPRINTF("LIFETIME=%d REACHABLE=%d RETRANSMIT=%d ",
+ 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;
+ if (na->nd_na_flags_reserved) {
+ u_int8_t rest;
+ LEPRINTF("NA=");
+ if (na->nd_na_flags_reserved & ND_NA_FLAG_ROUTER) {
+ LEPRINTF("R");
+ }
+ if (na->nd_na_flags_reserved & ND_NA_FLAG_SOLICITED) {
+ LEPRINTF("S");
+ }
+ if (na->nd_na_flags_reserved & ND_NA_FLAG_OVERRIDE) {
+ LEPRINTF("O");
+ }
+ rest = na->nd_na_flags_reserved & ~(ND_NA_FLAG_ROUTER |
+ ND_NA_FLAG_SOLICITED |
+ ND_NA_FLAG_OVERRIDE);
+ /* ND_NA_FLAG_ constants are endian-aware but we still need to
+ * swap for printing.
+ * The extra bits with no predefined constants are currently:
+ * Prf (Router selection preferences) (RFC4191)
+ * P (Neighbor Proxy flag) (RFC4389)
+ * (List is in RFC5175)
+ */
+ rest = ntohl(rest);
+ if (rest) {
+ LEPRINTF("+%08x ", rest);
+ } else {
+ LEPRINTF(" ");
+ }
+ }
+ /* 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*)(exthdr + 1),
+ payload_len - sizeof(*exthdr)) < 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)
--
2.1.4
More information about the pve-devel
mailing list