1 // SPDX-License-Identifier: GPL-2.0+
3 * Copyright (C) 2021 Felix Fietkau <nbd@nbd.name>
5 #define KBUILD_MODNAME "foo"
6 #include <uapi/linux/bpf.h>
7 #include <uapi/linux/if_ether.h>
8 #include <uapi/linux/if_packet.h>
9 #include <uapi/linux/ip.h>
10 #include <uapi/linux/ipv6.h>
11 #include <uapi/linux/in.h>
12 #include <uapi/linux/tcp.h>
13 #include <uapi/linux/filter.h>
14 #include <uapi/linux/pkt_cls.h>
18 #include <bpf/bpf_helpers.h>
19 #include <bpf/bpf_endian.h>
21 const volatile static uint32_t mtu
= 1420;
23 static __always_inline
int proto_is_vlan(__u16 h_proto
)
25 return !!(h_proto
== bpf_htons(ETH_P_8021Q
) ||
26 h_proto
== bpf_htons(ETH_P_8021AD
));
29 static __always_inline
int proto_is_ip(__u16 h_proto
)
31 return !!(h_proto
== bpf_htons(ETH_P_IP
) ||
32 h_proto
== bpf_htons(ETH_P_IPV6
));
35 static __always_inline
void *skb_ptr(struct __sk_buff
*skb
, __u32 offset
)
37 void *start
= (void *)(unsigned long long)skb
->data
;
39 return start
+ offset
;
42 static __always_inline
void *skb_end_ptr(struct __sk_buff
*skb
)
44 return (void *)(unsigned long long)skb
->data_end
;
47 static __always_inline
int skb_check(struct __sk_buff
*skb
, void *ptr
)
49 if (ptr
> skb_end_ptr(skb
))
55 static __always_inline
int
56 parse_ethernet(struct __sk_buff
*skb
, __u32
*offset
)
62 eth
= skb_ptr(skb
, *offset
);
63 if (skb_check(skb
, eth
+ 1))
66 h_proto
= eth
->h_proto
;
67 *offset
+= sizeof(*eth
);
70 for (i
= 0; i
< 2; i
++) {
71 struct vlan_hdr
*vlh
= skb_ptr(skb
, *offset
);
73 if (!proto_is_vlan(h_proto
))
76 if (skb_check(skb
, vlh
+ 1))
79 h_proto
= vlh
->h_vlan_encapsulated_proto
;
80 *offset
+= sizeof(*vlh
);
86 static __always_inline
int
87 parse_ipv4(struct __sk_buff
*skb
, __u32
*offset
)
92 iph
= skb_ptr(skb
, *offset
);
93 if (skb_check(skb
, iph
+ 1))
96 hdr_len
= iph
->ihl
* 4;
97 if (bpf_skb_pull_data(skb
, *offset
+ hdr_len
+ sizeof(struct tcphdr
) + 20))
100 iph
= skb_ptr(skb
, *offset
);
103 if (skb_check(skb
, (void *)(iph
+ 1)))
106 return READ_ONCE(iph
->protocol
);
109 static __always_inline
bool
110 parse_ipv6(struct __sk_buff
*skb
, __u32
*offset
)
114 if (bpf_skb_pull_data(skb
, *offset
+ sizeof(*iph
) + sizeof(struct tcphdr
) + 20))
117 iph
= skb_ptr(skb
, *offset
);
118 *offset
+= sizeof(*iph
);
120 if (skb_check(skb
, (void *)(iph
+ 1)))
123 return READ_ONCE(iph
->nexthdr
);
126 static inline unsigned int
127 optlen(const u_int8_t
*opt
)
129 if (opt
[0] <= TCPOPT_NOP
|| opt
[1] == 0)
135 static __always_inline
void
136 fixup_tcp(struct __sk_buff
*skb
, __u32 offset
, __u16 mss
)
145 tcph
= skb_ptr(skb
, offset
);
146 if (skb_check(skb
, tcph
+ 1))
149 flags
= tcp_flag_byte(tcph
);
150 if (!(flags
& TCPHDR_SYN
))
153 hdrlen
= tcph
->doff
* 4;
154 if (hdrlen
<= sizeof(struct tcphdr
))
158 offset
+= sizeof(*tcph
);
161 for (i
= 0; i
< 5; i
++) {
164 if (offset
>= hdrlen
)
167 opt
= skb_ptr(skb
, offset
);
168 if (skb_check(skb
, opt
+ TCPOLEN_MSS
))
173 if (opt
[0] != TCPOPT_MSS
|| opt
[1] != TCPOLEN_MSS
)
181 oldmss
= (opt
[2] << 8) | opt
[3];
187 csum_replace2(&tcph
->check
, bpf_htons(oldmss
), bpf_htons(mss
));
191 int mssfix(struct __sk_buff
*skb
)
198 type
= parse_ethernet(skb
, &offset
);
199 if (type
== bpf_htons(ETH_P_IP
))
200 type
= parse_ipv4(skb
, &offset
);
201 else if (type
== bpf_htons(ETH_P_IPV6
))
202 type
= parse_ipv6(skb
, &offset
);
204 return TC_ACT_UNSPEC
;
206 if (type
!= IPPROTO_TCP
)
207 return TC_ACT_UNSPEC
;
210 mss
-= offset
+ sizeof(struct tcphdr
);
211 fixup_tcp(skb
, offset
, mss
);
213 return TC_ACT_UNSPEC
;