1*cfa7b011SJoanne Koong // SPDX-License-Identifier: GPL-2.0
2*cfa7b011SJoanne Koong 
3*cfa7b011SJoanne Koong /* This parsing logic is taken from the open source library katran, a layer 4
4*cfa7b011SJoanne Koong  * load balancer.
5*cfa7b011SJoanne Koong  *
6*cfa7b011SJoanne Koong  * This code logic using dynptrs can be found in test_parse_tcp_hdr_opt_dynptr.c
7*cfa7b011SJoanne Koong  *
8*cfa7b011SJoanne Koong  * https://github.com/facebookincubator/katran/blob/main/katran/lib/bpf/pckt_parsing.h
9*cfa7b011SJoanne Koong  */
10*cfa7b011SJoanne Koong 
11*cfa7b011SJoanne Koong #include <linux/bpf.h>
12*cfa7b011SJoanne Koong #include <bpf/bpf_helpers.h>
13*cfa7b011SJoanne Koong #include <linux/tcp.h>
14*cfa7b011SJoanne Koong #include <stdbool.h>
15*cfa7b011SJoanne Koong #include <linux/ipv6.h>
16*cfa7b011SJoanne Koong #include <linux/if_ether.h>
17*cfa7b011SJoanne Koong #include "test_tcp_hdr_options.h"
18*cfa7b011SJoanne Koong 
19*cfa7b011SJoanne Koong char _license[] SEC("license") = "GPL";
20*cfa7b011SJoanne Koong 
21*cfa7b011SJoanne Koong /* Kind number used for experiments */
22*cfa7b011SJoanne Koong const __u32 tcp_hdr_opt_kind_tpr = 0xFD;
23*cfa7b011SJoanne Koong /* Length of the tcp header option */
24*cfa7b011SJoanne Koong const __u32 tcp_hdr_opt_len_tpr = 6;
25*cfa7b011SJoanne Koong /* maximum number of header options to check to lookup server_id */
26*cfa7b011SJoanne Koong const __u32 tcp_hdr_opt_max_opt_checks = 15;
27*cfa7b011SJoanne Koong 
28*cfa7b011SJoanne Koong __u32 server_id;
29*cfa7b011SJoanne Koong 
30*cfa7b011SJoanne Koong struct hdr_opt_state {
31*cfa7b011SJoanne Koong 	__u32 server_id;
32*cfa7b011SJoanne Koong 	__u8 byte_offset;
33*cfa7b011SJoanne Koong 	__u8 hdr_bytes_remaining;
34*cfa7b011SJoanne Koong };
35*cfa7b011SJoanne Koong 
parse_hdr_opt(const struct xdp_md * xdp,struct hdr_opt_state * state)36*cfa7b011SJoanne Koong static int parse_hdr_opt(const struct xdp_md *xdp, struct hdr_opt_state *state)
37*cfa7b011SJoanne Koong {
38*cfa7b011SJoanne Koong 	const void *data = (void *)(long)xdp->data;
39*cfa7b011SJoanne Koong 	const void *data_end = (void *)(long)xdp->data_end;
40*cfa7b011SJoanne Koong 	__u8 *tcp_opt, kind, hdr_len;
41*cfa7b011SJoanne Koong 
42*cfa7b011SJoanne Koong 	tcp_opt = (__u8 *)(data + state->byte_offset);
43*cfa7b011SJoanne Koong 	if (tcp_opt + 1 > data_end)
44*cfa7b011SJoanne Koong 		return -1;
45*cfa7b011SJoanne Koong 
46*cfa7b011SJoanne Koong 	kind = tcp_opt[0];
47*cfa7b011SJoanne Koong 
48*cfa7b011SJoanne Koong 	if (kind == TCPOPT_EOL)
49*cfa7b011SJoanne Koong 		return -1;
50*cfa7b011SJoanne Koong 
51*cfa7b011SJoanne Koong 	if (kind == TCPOPT_NOP) {
52*cfa7b011SJoanne Koong 		state->hdr_bytes_remaining--;
53*cfa7b011SJoanne Koong 		state->byte_offset++;
54*cfa7b011SJoanne Koong 		return 0;
55*cfa7b011SJoanne Koong 	}
56*cfa7b011SJoanne Koong 
57*cfa7b011SJoanne Koong 	if (state->hdr_bytes_remaining < 2 ||
58*cfa7b011SJoanne Koong 	    tcp_opt + sizeof(__u8) + sizeof(__u8) > data_end)
59*cfa7b011SJoanne Koong 		return -1;
60*cfa7b011SJoanne Koong 
61*cfa7b011SJoanne Koong 	hdr_len = tcp_opt[1];
62*cfa7b011SJoanne Koong 	if (hdr_len > state->hdr_bytes_remaining)
63*cfa7b011SJoanne Koong 		return -1;
64*cfa7b011SJoanne Koong 
65*cfa7b011SJoanne Koong 	if (kind == tcp_hdr_opt_kind_tpr) {
66*cfa7b011SJoanne Koong 		if (hdr_len != tcp_hdr_opt_len_tpr)
67*cfa7b011SJoanne Koong 			return -1;
68*cfa7b011SJoanne Koong 
69*cfa7b011SJoanne Koong 		if (tcp_opt + tcp_hdr_opt_len_tpr > data_end)
70*cfa7b011SJoanne Koong 			return -1;
71*cfa7b011SJoanne Koong 
72*cfa7b011SJoanne Koong 		state->server_id = *(__u32 *)&tcp_opt[2];
73*cfa7b011SJoanne Koong 		return 1;
74*cfa7b011SJoanne Koong 	}
75*cfa7b011SJoanne Koong 
76*cfa7b011SJoanne Koong 	state->hdr_bytes_remaining -= hdr_len;
77*cfa7b011SJoanne Koong 	state->byte_offset += hdr_len;
78*cfa7b011SJoanne Koong 	return 0;
79*cfa7b011SJoanne Koong }
80*cfa7b011SJoanne Koong 
81*cfa7b011SJoanne Koong SEC("xdp")
xdp_ingress_v6(struct xdp_md * xdp)82*cfa7b011SJoanne Koong int xdp_ingress_v6(struct xdp_md *xdp)
83*cfa7b011SJoanne Koong {
84*cfa7b011SJoanne Koong 	const void *data = (void *)(long)xdp->data;
85*cfa7b011SJoanne Koong 	const void *data_end = (void *)(long)xdp->data_end;
86*cfa7b011SJoanne Koong 	struct hdr_opt_state opt_state = {};
87*cfa7b011SJoanne Koong 	__u8 tcp_hdr_opt_len = 0;
88*cfa7b011SJoanne Koong 	struct tcphdr *tcp_hdr;
89*cfa7b011SJoanne Koong 	__u64 tcp_offset = 0;
90*cfa7b011SJoanne Koong 	int err;
91*cfa7b011SJoanne Koong 
92*cfa7b011SJoanne Koong 	tcp_offset = sizeof(struct ethhdr) + sizeof(struct ipv6hdr);
93*cfa7b011SJoanne Koong 	tcp_hdr = (struct tcphdr *)(data + tcp_offset);
94*cfa7b011SJoanne Koong 	if (tcp_hdr + 1 > data_end)
95*cfa7b011SJoanne Koong 		return XDP_DROP;
96*cfa7b011SJoanne Koong 
97*cfa7b011SJoanne Koong 	tcp_hdr_opt_len = (tcp_hdr->doff * 4) - sizeof(struct tcphdr);
98*cfa7b011SJoanne Koong 	if (tcp_hdr_opt_len < tcp_hdr_opt_len_tpr)
99*cfa7b011SJoanne Koong 		return XDP_DROP;
100*cfa7b011SJoanne Koong 
101*cfa7b011SJoanne Koong 	opt_state.hdr_bytes_remaining = tcp_hdr_opt_len;
102*cfa7b011SJoanne Koong 	opt_state.byte_offset = sizeof(struct tcphdr) + tcp_offset;
103*cfa7b011SJoanne Koong 
104*cfa7b011SJoanne Koong 	/* max number of bytes of options in tcp header is 40 bytes */
105*cfa7b011SJoanne Koong 	for (int i = 0; i < tcp_hdr_opt_max_opt_checks; i++) {
106*cfa7b011SJoanne Koong 		err = parse_hdr_opt(xdp, &opt_state);
107*cfa7b011SJoanne Koong 
108*cfa7b011SJoanne Koong 		if (err || !opt_state.hdr_bytes_remaining)
109*cfa7b011SJoanne Koong 			break;
110*cfa7b011SJoanne Koong 	}
111*cfa7b011SJoanne Koong 
112*cfa7b011SJoanne Koong 	if (!opt_state.server_id)
113*cfa7b011SJoanne Koong 		return XDP_DROP;
114*cfa7b011SJoanne Koong 
115*cfa7b011SJoanne Koong 	server_id = opt_state.server_id;
116*cfa7b011SJoanne Koong 
117*cfa7b011SJoanne Koong 	return XDP_PASS;
118*cfa7b011SJoanne Koong }
119