diff --git a/main.c b/main.c index 5bac218..7478870 100644 --- a/main.c +++ b/main.c @@ -23,6 +23,7 @@ #define IPV6_FLOWINFO_MASK bpf_htonl(0x0FFFFFFF) #define VLAN_MAX_DEPTH 2 /* Support double-tagged VLANs */ +#define IPV6_EXT_MAX_CHAIN 6 /* Max IPv6 extension headers to parse */ /* Forwarding ports */ struct { @@ -91,7 +92,8 @@ static __always_inline void record_stats(struct xdp_md *ctx, struct flow_key *ke .packets = 1, .bytes = bytes, }; - bpf_map_update_elem(&xdp_flow_stats, key, &new_stats, BPF_NOEXIST); + /* Use BPF_ANY instead of BPF_NOEXIST for better performance */ + bpf_map_update_elem(&xdp_flow_stats, key, &new_stats, BPF_ANY); } } @@ -125,6 +127,47 @@ static __always_inline int parse_vlan(void *data, void *data_end, __u64 *nh_off, return 0; } +/* Skip IPv6 extension headers to find the actual L4 protocol */ +static __always_inline int skip_ip6hdrext(void *data, void *data_end, __u64 *nh_off, __u8 next_hdr_type) +{ + struct ipv6_opt_hdr { + __u8 nexthdr; + __u8 hdrlen; + } *hdr; + int i; + + #pragma unroll + for (i = 0; i < IPV6_EXT_MAX_CHAIN; i++) { + hdr = data + *nh_off; + + if ((void *)(hdr + 1) > data_end) + return -1; + + switch (next_hdr_type) { + case IPPROTO_HOPOPTS: + case IPPROTO_DSTOPTS: + case IPPROTO_ROUTING: + case IPPROTO_MH: + *nh_off += (hdr->hdrlen + 1) * 8; + next_hdr_type = hdr->nexthdr; + break; + case IPPROTO_AH: + *nh_off += (hdr->hdrlen + 2) * 4; + next_hdr_type = hdr->nexthdr; + break; + case IPPROTO_FRAGMENT: + *nh_off += 8; + next_hdr_type = hdr->nexthdr; + break; + default: + /* Found a header that is not an IPv6 extension header */ + return next_hdr_type; + } + } + + return -1; +} + static __always_inline int xdp_l3fwd_flags(struct xdp_md *ctx, __u32 flags) { void *data_end = (void *)(long)ctx->data_end; @@ -165,14 +208,17 @@ static __always_inline int xdp_l3fwd_flags(struct xdp_md *ctx, __u32 flags) key.ipv4_src = iph->saddr; key.ipv4_dst = iph->daddr; - void *l4_hdr = (void *)iph + (iph->ihl * 4); - if (l4_hdr + sizeof(struct udphdr) <= data_end) { - if (iph->protocol == IPPROTO_TCP) { - struct tcphdr *tcph = l4_hdr; + /* FIXED: Proper bounds checking for L4 headers */ + __u64 l4_off = nh_off + (iph->ihl * 4); + if (iph->protocol == IPPROTO_TCP) { + struct tcphdr *tcph = data + l4_off; + if ((void *)(tcph + 1) <= data_end) { key.sport = tcph->source; key.dport = tcph->dest; - } else if (iph->protocol == IPPROTO_UDP) { - struct udphdr *udph = l4_hdr; + } + } else if (iph->protocol == IPPROTO_UDP) { + struct udphdr *udph = data + l4_off; + if ((void *)(udph + 1) <= data_end) { key.sport = udph->source; key.dport = udph->dest; } @@ -193,18 +239,28 @@ static __always_inline int xdp_l3fwd_flags(struct xdp_md *ctx, __u32 flags) if (ip6h->hop_limit <= 1) return XDP_PASS; - key.proto = ip6h->nexthdr; __builtin_memcpy(key.ipv6_src, &ip6h->saddr, 16); __builtin_memcpy(key.ipv6_dst, &ip6h->daddr, 16); - void *l4_hdr = (void *)(ip6h + 1); - if (l4_hdr + sizeof(struct udphdr) <= data_end) { - if (ip6h->nexthdr == IPPROTO_TCP) { - struct tcphdr *tcph = l4_hdr; + /* FIXED: Parse IPv6 extension headers to get actual L4 protocol */ + __u64 l4_off = nh_off + sizeof(*ip6h); + int l4_proto = skip_ip6hdrext(data, data_end, &l4_off, ip6h->nexthdr); + + if (l4_proto < 0) + l4_proto = ip6h->nexthdr; /* Fallback if parsing fails */ + + key.proto = l4_proto; + + /* FIXED: Proper bounds checking for L4 headers */ + if (l4_proto == IPPROTO_TCP) { + struct tcphdr *tcph = data + l4_off; + if ((void *)(tcph + 1) <= data_end) { key.sport = tcph->source; key.dport = tcph->dest; - } else if (ip6h->nexthdr == IPPROTO_UDP) { - struct udphdr *udph = l4_hdr; + } + } else if (l4_proto == IPPROTO_UDP) { + struct udphdr *udph = data + l4_off; + if ((void *)(udph + 1) <= data_end) { key.sport = udph->source; key.dport = udph->dest; } @@ -212,7 +268,7 @@ static __always_inline int xdp_l3fwd_flags(struct xdp_md *ctx, __u32 flags) fib_params.family = AF_INET6; fib_params.flowinfo = *(__be32 *)ip6h & IPV6_FLOWINFO_MASK; - fib_params.l4_protocol = ip6h->nexthdr; + fib_params.l4_protocol = l4_proto; fib_params.tot_len = bpf_ntohs(ip6h->payload_len); __builtin_memcpy(fib_params.ipv6_src, &ip6h->saddr, 16); __builtin_memcpy(fib_params.ipv6_dst, &ip6h->daddr, 16); @@ -227,9 +283,10 @@ static __always_inline int xdp_l3fwd_flags(struct xdp_md *ctx, __u32 flags) if (!bpf_map_lookup_elem(&xdp_l3fwd_ports, &fib_params.ifindex)) return XDP_PASS; - /* Record stats AFTER fib lookup */ + /* Record stats AFTER fib lookup and port validation */ record_stats(ctx, &key, bytes); + /* Update TTL/hop limit and MAC addresses */ if (h_proto == bpf_htons(ETH_P_IP)) ip_decrease_ttl(iph); else if (h_proto == bpf_htons(ETH_P_IPV6)) @@ -255,4 +312,4 @@ int xdp_l3fwd_direct_prog(struct xdp_md *ctx) return xdp_l3fwd_flags(ctx, BPF_FIB_LOOKUP_DIRECT); } -char _license[] SEC("license") = "GPL"; +char _license[] SEC("license") = "GPL"; \ No newline at end of file