File Index
Files and directories
Sign in
FILE

test_parse_tcp_hdr_opt_dynptr.c

File preview with syntax highlighting.

linux-6.19.14/tools/testing/selftests/bpf/progs/test_parse_tcp_hdr_opt_dynptr.c 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
 19char _license[] SEC("license") = "GPL";
 20
 21/* Kind number used for experiments */
 22const __u32 tcp_hdr_opt_kind_tpr = 0xFD;
 23/* Length of the tcp header option */
 24const __u32 tcp_hdr_opt_len_tpr = 6;
 25/* maximum number of header options to check to lookup server_id */
 26const __u32 tcp_hdr_opt_max_opt_checks = 15;
 27
 28__u32 server_id;
 29
 30static 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
 74SEC("xdp")
 75int 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}
~/ Read-only file preview.