diff options
author | Daniel Salzman <daniel.salzman@nic.cz> | 2025-01-03 16:31:51 +0100 |
---|---|---|
committer | Daniel Salzman <daniel.salzman@nic.cz> | 2025-01-05 18:27:12 +0100 |
commit | 57b57eef49a2beb27cd8536c7380b141660f64bb (patch) | |
tree | 25a42524524231f9dec89f79d22ded79c49ede0f | |
parent | xdp: unify used byte-order conversion functions (diff) | |
download | knot-57b57eef49a2beb27cd8536c7380b141660f64bb.tar.xz knot-57b57eef49a2beb27cd8536c7380b141660f64bb.zip |
xdp: add IPv6 extended headers parsing support with some filtering
- Any packet with more than 3 IPv6 headers is dropped.
- Any packet with an IPPROTO_NONE or a reserved header (253 and 253) is dropped.
- A DNS-related packet is dropped if it contains any extended header.
-rw-r--r-- | src/libknot/xdp/bpf-kernel.c | 61 |
1 files changed, 43 insertions, 18 deletions
diff --git a/src/libknot/xdp/bpf-kernel.c b/src/libknot/xdp/bpf-kernel.c index ccebd77c8..949e60ff7 100644 --- a/src/libknot/xdp/bpf-kernel.c +++ b/src/libknot/xdp/bpf-kernel.c @@ -35,6 +35,9 @@ /* Define maximum reasonable number of NIC queues supported. */ #define QUEUE_MAX 256 +/* Define maximum number of allowed IPv6 headers. */ +#define IPV6_HDR_MAX 3 + /* DNS header size. */ #define DNS_HDR_SIZE 12 @@ -54,11 +57,6 @@ struct { __uint(value_size, sizeof(int)); } xsks_map SEC(".maps"); -struct ipv6_frag_hdr { - unsigned char nexthdr; - unsigned char whatever[7]; -} __attribute__((packed)); - SEC("xdp") int xdp_redirect_dns_func(struct xdp_md *ctx) { @@ -95,7 +93,7 @@ int xdp_redirect_dns_func(struct xdp_md *ctx) __u16 ip_len; __u8 ipv4; __u8 ip_proto; - __u8 fragmented = 0; + __u8 fragmented = 0; /* Fragmented or IPv6 packet with a header extension. */ __u16 eth_type; /* In big endian. */ /* Parse Ethernet header. */ @@ -116,13 +114,12 @@ int xdp_redirect_dns_func(struct xdp_md *ctx) } else { eth_type = eth_hdr->h_proto; } - ip_hdr = data; /* Parse IPv4 or IPv6 header. */ switch (eth_type) { case bpf_htons(ETH_P_IP): - ip4 = ip_hdr; + ip4 = data; if ((void *)ip4 + sizeof(*ip4) > data_end) { return XDP_DROP; } @@ -146,7 +143,7 @@ int xdp_redirect_dns_func(struct xdp_md *ctx) ipv4 = 1; break; case bpf_htons(ETH_P_IPV6): - ip6 = ip_hdr; + ip6 = data; if ((void *)ip6 + sizeof(*ip6) > data_end) { return XDP_DROP; } @@ -161,17 +158,45 @@ int xdp_redirect_dns_func(struct xdp_md *ctx) return XDP_DROP; } - ip_proto = ip6->nexthdr; - data += sizeof(*ip6); - if (ip_proto == IPPROTO_FRAGMENT) { - fragmented = 1; - const struct ipv6_frag_hdr *frag = data; - if ((void *)frag + sizeof(*frag) > data_end) { + for (int i = 0; i <= IPV6_HDR_MAX; i++) { + if (i == IPV6_HDR_MAX) { return XDP_DROP; } - ip_proto = frag->nexthdr; - data += sizeof(*frag); + const struct ipv6_opt_hdr *ext_hdr = (void *)&ip6->nexthdr; + if ((void *)ext_hdr + sizeof(*ext_hdr) > data_end) { + return XDP_DROP; + } + ip_proto = ext_hdr->nexthdr; + + switch (ip_proto) { + case IPPROTO_HOPOPTS: + case IPPROTO_ROUTING: + case IPPROTO_DSTOPTS: + case IPPROTO_MH: + case 139: /* HIP */ + case 140: /* Shim6 */ + fragmented = 1; + data += (ext_hdr->hdrlen + 1) * 8; + break; + case IPPROTO_AH: + fragmented = 1; + data += (ext_hdr->hdrlen + 2) * 4; + break; + case IPPROTO_FRAGMENT: + fragmented = 1; + data += 8; + break; + case IPPROTO_NONE: + case 253: /* Reserved */ + case 254: /* Reserved */ + return XDP_DROP; + case IPPROTO_ESP: /* IPsec, different header format, stop. */ + default: /* Not an IPv6 extension header, stop. */ + i = IPV6_HDR_MAX + 1; + } + ip6 = data; } + data += sizeof(*ip6); ipv4 = 0; break; default: @@ -244,7 +269,7 @@ int xdp_redirect_dns_func(struct xdp_md *ctx) /* Drop matching packet if requested. */ return XDP_DROP; } else if (fragmented) { - /* Drop fragmented packet. */ + /* Drop fragmented/extended DNS-related (UDP, TCP, QUIC) packet. */ return XDP_DROP; } |