summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDaniel Salzman <daniel.salzman@nic.cz>2025-01-03 16:31:51 +0100
committerDaniel Salzman <daniel.salzman@nic.cz>2025-01-05 18:27:12 +0100
commit57b57eef49a2beb27cd8536c7380b141660f64bb (patch)
tree25a42524524231f9dec89f79d22ded79c49ede0f
parentxdp: unify used byte-order conversion functions (diff)
downloadknot-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.c61
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;
}