From: Felix Fietkau Date: Sat, 9 Jul 2022 15:44:12 +0000 (+0200) Subject: mss-bpf: rework the code to use a common skb parser header file X-Git-Url: http://git.openwrt.org/project/luci.git;master?a=commitdiff_plain;h=2c98848708771228066a9181ba11c0bc0bc8c22d;p=project%2Funetd.git mss-bpf: rework the code to use a common skb parser header file Signed-off-by: Felix Fietkau --- diff --git a/bpf_skb_utils.h b/bpf_skb_utils.h new file mode 100644 index 0000000..4953c86 --- /dev/null +++ b/bpf_skb_utils.h @@ -0,0 +1,167 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2022 Felix Fietkau + */ +#ifndef __BPF_SKB_UTILS_H +#define __BPF_SKB_UTILS_H + +#include +#include +#include +#include +#include +#include +#include +#include + +struct skb_parser_info { + struct __sk_buff *skb; + __u32 offset; + int proto; +}; + +static __always_inline void *__skb_data(struct __sk_buff *skb) +{ + return (void *)(long)READ_ONCE(skb->data); +} + +static __always_inline void * +skb_ptr(struct __sk_buff *skb, __u32 offset, __u32 len) +{ + void *ptr = __skb_data(skb) + offset; + void *end = (void *)(long)(skb->data_end); + + if (ptr + len >= end) + return NULL; + + return ptr; +} + +static __always_inline void * +skb_info_ptr(struct skb_parser_info *info, __u32 len) +{ + __u32 offset = info->offset; + return skb_ptr(info->skb, offset, len); +} + +static __always_inline void +skb_parse_init(struct skb_parser_info *info, struct __sk_buff *skb) +{ + *info = (struct skb_parser_info){ + .skb = skb + }; +} + +static __always_inline struct ethhdr * +skb_parse_ethernet(struct skb_parser_info *info) +{ + struct ethhdr *eth; + + eth = skb_info_ptr(info, sizeof(*eth)); + if (!eth) + return NULL; + + info->proto = eth->h_proto; + info->offset += sizeof(*eth); + + return eth; +} + +static __always_inline struct vlan_hdr * +skb_parse_vlan(struct skb_parser_info *info) +{ + struct vlan_hdr *vlh; + + if (info->proto != bpf_htons(ETH_P_8021Q) && + info->proto != bpf_htons(ETH_P_8021AD)) + return NULL; + + vlh = skb_info_ptr(info, sizeof(*vlh)); + if (!vlh) + return NULL; + + info->proto = vlh->h_vlan_encapsulated_proto; + info->offset += sizeof(*vlh); + + return vlh; +} + +static __always_inline struct iphdr * +skb_parse_ipv4(struct skb_parser_info *info, int min_l4_bytes) +{ + struct iphdr *iph; + int proto, hdr_len; + __u32 pull_len; + + if (info->proto != bpf_htons(ETH_P_IP)) + return NULL; + + iph = skb_info_ptr(info, sizeof(*iph)); + if (!iph) + return NULL; + + hdr_len = iph->ihl * 4; + if (hdr_len < sizeof(*iph)) + return NULL; + + pull_len = info->offset + hdr_len + min_l4_bytes; + if (pull_len > info->skb->len) + pull_len = info->skb->len; + + if (bpf_skb_pull_data(info->skb, pull_len)) + return NULL; + + iph = skb_info_ptr(info, sizeof(*iph)); + if (!iph) + return NULL; + + info->proto = iph->protocol; + info->offset += hdr_len; + + return iph; +} + +static __always_inline struct ipv6hdr * +skb_parse_ipv6(struct skb_parser_info *info, int max_l4_bytes) +{ + struct ipv6hdr *ip6h; + __u32 pull_len; + + if (info->proto != bpf_htons(ETH_P_IPV6)) + return NULL; + + pull_len = info->offset + sizeof(*ip6h) + max_l4_bytes; + if (pull_len > info->skb->len) + pull_len = info->skb->len; + + if (bpf_skb_pull_data(info->skb, pull_len)) + return NULL; + + ip6h = skb_info_ptr(info, sizeof(*ip6h)); + if (!ip6h) + return NULL; + + info->proto = READ_ONCE(ip6h->nexthdr); + info->offset += sizeof(*ip6h); + + return ip6h; +} + +static __always_inline struct tcphdr * +skb_parse_tcp(struct skb_parser_info *info) +{ + struct tcphdr *tcph; + + if (info->proto != IPPROTO_TCP) + return NULL; + + tcph = skb_info_ptr(info, sizeof(*tcph)); + if (!tcph) + return NULL; + + info->offset += tcph->doff * 4; + + return tcph; +} + +#endif diff --git a/mss-bpf.c b/mss-bpf.c index 5f5cb61..26f13ae 100644 --- a/mss-bpf.c +++ b/mss-bpf.c @@ -17,113 +17,11 @@ #include #include #include +#include "bpf_skb_utils.h" const volatile static uint32_t mtu = 1420; -static __always_inline int proto_is_vlan(__u16 h_proto) -{ - return !!(h_proto == bpf_htons(ETH_P_8021Q) || - h_proto == bpf_htons(ETH_P_8021AD)); -} - -static __always_inline int proto_is_ip(__u16 h_proto) -{ - return !!(h_proto == bpf_htons(ETH_P_IP) || - h_proto == bpf_htons(ETH_P_IPV6)); -} - -static __always_inline void *skb_ptr(struct __sk_buff *skb, __u32 offset) -{ - void *start = (void *)(unsigned long long)skb->data; - - return start + offset; -} - -static __always_inline void *skb_end_ptr(struct __sk_buff *skb) -{ - return (void *)(unsigned long long)skb->data_end; -} - -static __always_inline int skb_check(struct __sk_buff *skb, void *ptr) -{ - if (ptr > skb_end_ptr(skb)) - return -1; - - return 0; -} - -static __always_inline int -parse_ethernet(struct __sk_buff *skb, __u32 *offset) -{ - struct ethhdr *eth; - __u16 h_proto; - int i; - - eth = skb_ptr(skb, *offset); - if (skb_check(skb, eth + 1)) - return -1; - - h_proto = eth->h_proto; - *offset += sizeof(*eth); - -#pragma unroll - for (i = 0; i < 2; i++) { - struct vlan_hdr *vlh = skb_ptr(skb, *offset); - - if (!proto_is_vlan(h_proto)) - break; - - if (skb_check(skb, vlh + 1)) - return -1; - - h_proto = vlh->h_vlan_encapsulated_proto; - *offset += sizeof(*vlh); - } - - return h_proto; -} - -static __always_inline int -parse_ipv4(struct __sk_buff *skb, __u32 *offset) -{ - struct iphdr *iph; - int hdr_len; - - iph = skb_ptr(skb, *offset); - if (skb_check(skb, iph + 1)) - return -1; - - hdr_len = iph->ihl * 4; - if (bpf_skb_pull_data(skb, *offset + hdr_len + sizeof(struct tcphdr) + 20)) - return -1; - - iph = skb_ptr(skb, *offset); - *offset += hdr_len; - - if (skb_check(skb, (void *)(iph + 1))) - return -1; - - return READ_ONCE(iph->protocol); -} - -static __always_inline bool -parse_ipv6(struct __sk_buff *skb, __u32 *offset) -{ - struct ipv6hdr *iph; - - if (bpf_skb_pull_data(skb, *offset + sizeof(*iph) + sizeof(struct tcphdr) + 20)) - return -1; - - iph = skb_ptr(skb, *offset); - *offset += sizeof(*iph); - - if (skb_check(skb, (void *)(iph + 1))) - return -1; - - return READ_ONCE(iph->nexthdr); -} - -static inline unsigned int +static __always_inline unsigned int optlen(const u_int8_t *opt) { if (opt[0] <= TCPOPT_NOP || opt[1] == 0) @@ -133,43 +31,35 @@ optlen(const u_int8_t *opt) } static __always_inline void -fixup_tcp(struct __sk_buff *skb, __u32 offset, __u16 mss) +fixup_tcp(struct skb_parser_info *info, __u16 mss) { struct tcphdr *tcph; + __u32 end, offset = info->offset + sizeof(*tcph); __u16 oldmss; + __u8 flags; __u8 *opt; - u8 flags; - int hdrlen; int i; - tcph = skb_ptr(skb, offset); - if (skb_check(skb, tcph + 1)) + tcph = skb_parse_tcp(info); + if (!tcph) return; flags = tcp_flag_byte(tcph); if (!(flags & TCPHDR_SYN)) return; - hdrlen = tcph->doff * 4; - if (hdrlen <= sizeof(struct tcphdr)) - return; - - hdrlen += offset; - offset += sizeof(*tcph); + end = info->offset; #pragma unroll for (i = 0; i < 5; i++) { - unsigned int len; - - if (offset >= hdrlen) + if (offset + 4 > end) return; - opt = skb_ptr(skb, offset); - if (skb_check(skb, opt + TCPOLEN_MSS)) + opt = skb_ptr(info->skb, offset, 4); + if (!opt) return; - len = optlen(opt); - offset += len; + offset += optlen(opt); if (opt[0] != TCPOPT_MSS || opt[1] != TCPOLEN_MSS) continue; @@ -190,25 +80,28 @@ found: SEC("tc") int mssfix(struct __sk_buff *skb) { - __u32 offset = 0; - __u8 ipproto; + struct skb_parser_info info; + u32 offset_eth; __u16 mss; int type; - type = parse_ethernet(skb, &offset); - if (type == bpf_htons(ETH_P_IP)) - type = parse_ipv4(skb, &offset); - else if (type == bpf_htons(ETH_P_IPV6)) - type = parse_ipv6(skb, &offset); - else + skb_parse_init(&info, skb); + if (!skb_parse_ethernet(&info)) + return TC_ACT_UNSPEC; + + skb_parse_vlan(&info); + skb_parse_vlan(&info); + + offset_eth = info.offset; + if (!skb_parse_ipv4(&info, 60) && !skb_parse_ipv6(&info, 60)) return TC_ACT_UNSPEC; - if (type != IPPROTO_TCP) + if (info.proto != IPPROTO_TCP) return TC_ACT_UNSPEC; mss = mtu; - mss -= offset + sizeof(struct tcphdr); - fixup_tcp(skb, offset, mss); + mss -= info.offset + sizeof(struct tcphdr); + fixup_tcp(&info, mss); return TC_ACT_UNSPEC; }