]> Git Repo - linux.git/blob - tools/testing/selftests/bpf/progs/test_parse_tcp_hdr_opt_dynptr.c
Linux 6.14-rc3
[linux.git] / tools / testing / selftests / bpf / progs / test_parse_tcp_hdr_opt_dynptr.c
1 // SPDX-License-Identifier: GPL-2.0
2
3 /* This logic is lifted from a real-world use case of packet parsing, used in
4  * the open source library katran, a layer 4 load balancer.
5  *
6  * This test demonstrates how to parse packet contents using dynptrs. The
7  * original code (parsing without dynptrs) can be found in test_parse_tcp_hdr_opt.c
8  */
9
10 #include <linux/bpf.h>
11 #include <bpf/bpf_helpers.h>
12 #include <linux/tcp.h>
13 #include <stdbool.h>
14 #include <linux/ipv6.h>
15 #include <linux/if_ether.h>
16 #include "test_tcp_hdr_options.h"
17 #include "bpf_kfuncs.h"
18
19 char _license[] SEC("license") = "GPL";
20
21 /* Kind number used for experiments */
22 const __u32 tcp_hdr_opt_kind_tpr = 0xFD;
23 /* Length of the tcp header option */
24 const __u32 tcp_hdr_opt_len_tpr = 6;
25 /* maximum number of header options to check to lookup server_id */
26 const __u32 tcp_hdr_opt_max_opt_checks = 15;
27
28 __u32 server_id;
29
30 static int parse_hdr_opt(struct bpf_dynptr *ptr, __u32 *off, __u8 *hdr_bytes_remaining,
31                          __u32 *server_id)
32 {
33         __u8 kind, hdr_len;
34         __u8 buffer[sizeof(kind) + sizeof(hdr_len) + sizeof(*server_id)];
35         __u8 *data;
36
37         __builtin_memset(buffer, 0, sizeof(buffer));
38
39         data = bpf_dynptr_slice(ptr, *off, buffer, sizeof(buffer));
40         if (!data)
41                 return -1;
42
43         kind = data[0];
44
45         if (kind == TCPOPT_EOL)
46                 return -1;
47
48         if (kind == TCPOPT_NOP) {
49                 *off += 1;
50                 *hdr_bytes_remaining -= 1;
51                 return 0;
52         }
53
54         if (*hdr_bytes_remaining < 2)
55                 return -1;
56
57         hdr_len = data[1];
58         if (hdr_len > *hdr_bytes_remaining)
59                 return -1;
60
61         if (kind == tcp_hdr_opt_kind_tpr) {
62                 if (hdr_len != tcp_hdr_opt_len_tpr)
63                         return -1;
64
65                 __builtin_memcpy(server_id, (__u32 *)(data + 2), sizeof(*server_id));
66                 return 1;
67         }
68
69         *off += hdr_len;
70         *hdr_bytes_remaining -= hdr_len;
71         return 0;
72 }
73
74 SEC("xdp")
75 int xdp_ingress_v6(struct xdp_md *xdp)
76 {
77         __u8 buffer[sizeof(struct tcphdr)] = {};
78         __u8 hdr_bytes_remaining;
79         struct tcphdr *tcp_hdr;
80         __u8 tcp_hdr_opt_len;
81         int err = 0;
82         __u32 off;
83
84         struct bpf_dynptr ptr;
85
86         bpf_dynptr_from_xdp(xdp, 0, &ptr);
87
88         off = sizeof(struct ethhdr) + sizeof(struct ipv6hdr);
89
90         tcp_hdr = bpf_dynptr_slice(&ptr, off, buffer, sizeof(buffer));
91         if (!tcp_hdr)
92                 return XDP_DROP;
93
94         tcp_hdr_opt_len = (tcp_hdr->doff * 4) - sizeof(struct tcphdr);
95         if (tcp_hdr_opt_len < tcp_hdr_opt_len_tpr)
96                 return XDP_DROP;
97
98         hdr_bytes_remaining = tcp_hdr_opt_len;
99
100         off += sizeof(struct tcphdr);
101
102         /* max number of bytes of options in tcp header is 40 bytes */
103         for (int i = 0; i < tcp_hdr_opt_max_opt_checks; i++) {
104                 err = parse_hdr_opt(&ptr, &off, &hdr_bytes_remaining, &server_id);
105
106                 if (err || !hdr_bytes_remaining)
107                         break;
108         }
109
110         if (!server_id)
111                 return XDP_DROP;
112
113         return XDP_PASS;
114 }
This page took 0.038174 seconds and 4 git commands to generate.