18c2ecf20Sopenharmony_ci/* SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci * Copyright (c) 2018 Jesper Dangaard Brouer, Red Hat Inc.
38c2ecf20Sopenharmony_ci *
48c2ecf20Sopenharmony_ci * Example howto transfer info from XDP to SKB, e.g. skb->mark
58c2ecf20Sopenharmony_ci * -----------------------------------------------------------
68c2ecf20Sopenharmony_ci * This uses the XDP data_meta infrastructure, and is a cooperation
78c2ecf20Sopenharmony_ci * between two bpf-programs (1) XDP and (2) clsact at TC-ingress hook.
88c2ecf20Sopenharmony_ci *
98c2ecf20Sopenharmony_ci * Notice: This example does not use the BPF C-loader (bpf_load.c),
108c2ecf20Sopenharmony_ci * but instead rely on the iproute2 TC tool for loading BPF-objects.
118c2ecf20Sopenharmony_ci */
128c2ecf20Sopenharmony_ci#include <uapi/linux/bpf.h>
138c2ecf20Sopenharmony_ci#include <uapi/linux/pkt_cls.h>
148c2ecf20Sopenharmony_ci
158c2ecf20Sopenharmony_ci#include <bpf/bpf_helpers.h>
168c2ecf20Sopenharmony_ci
178c2ecf20Sopenharmony_ci/*
188c2ecf20Sopenharmony_ci * This struct is stored in the XDP 'data_meta' area, which is located
198c2ecf20Sopenharmony_ci * just in-front-of the raw packet payload data.  The meaning is
208c2ecf20Sopenharmony_ci * specific to these two BPF programs that use it as a communication
218c2ecf20Sopenharmony_ci * channel.  XDP adjust/increase the area via a bpf-helper, and TC use
228c2ecf20Sopenharmony_ci * boundary checks to see if data have been provided.
238c2ecf20Sopenharmony_ci *
248c2ecf20Sopenharmony_ci * The struct must be 4 byte aligned, which here is enforced by the
258c2ecf20Sopenharmony_ci * struct __attribute__((aligned(4))).
268c2ecf20Sopenharmony_ci */
278c2ecf20Sopenharmony_cistruct meta_info {
288c2ecf20Sopenharmony_ci	__u32 mark;
298c2ecf20Sopenharmony_ci} __attribute__((aligned(4)));
308c2ecf20Sopenharmony_ci
318c2ecf20Sopenharmony_ciSEC("xdp_mark")
328c2ecf20Sopenharmony_ciint _xdp_mark(struct xdp_md *ctx)
338c2ecf20Sopenharmony_ci{
348c2ecf20Sopenharmony_ci	struct meta_info *meta;
358c2ecf20Sopenharmony_ci	void *data, *data_end;
368c2ecf20Sopenharmony_ci	int ret;
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_ci	/* Reserve space in-front of data pointer for our meta info.
398c2ecf20Sopenharmony_ci	 * (Notice drivers not supporting data_meta will fail here!)
408c2ecf20Sopenharmony_ci	 */
418c2ecf20Sopenharmony_ci	ret = bpf_xdp_adjust_meta(ctx, -(int)sizeof(*meta));
428c2ecf20Sopenharmony_ci	if (ret < 0)
438c2ecf20Sopenharmony_ci		return XDP_ABORTED;
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_ci	/* Notice: Kernel-side verifier requires that loading of
468c2ecf20Sopenharmony_ci	 * ctx->data MUST happen _after_ helper bpf_xdp_adjust_meta(),
478c2ecf20Sopenharmony_ci	 * as pkt-data pointers are invalidated.  Helpers that require
488c2ecf20Sopenharmony_ci	 * this are determined/marked by bpf_helper_changes_pkt_data()
498c2ecf20Sopenharmony_ci	 */
508c2ecf20Sopenharmony_ci	data = (void *)(unsigned long)ctx->data;
518c2ecf20Sopenharmony_ci
528c2ecf20Sopenharmony_ci	/* Check data_meta have room for meta_info struct */
538c2ecf20Sopenharmony_ci	meta = (void *)(unsigned long)ctx->data_meta;
548c2ecf20Sopenharmony_ci	if (meta + 1 > data)
558c2ecf20Sopenharmony_ci		return XDP_ABORTED;
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_ci	meta->mark = 42;
588c2ecf20Sopenharmony_ci
598c2ecf20Sopenharmony_ci	return XDP_PASS;
608c2ecf20Sopenharmony_ci}
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_ciSEC("tc_mark")
638c2ecf20Sopenharmony_ciint _tc_mark(struct __sk_buff *ctx)
648c2ecf20Sopenharmony_ci{
658c2ecf20Sopenharmony_ci	void *data      = (void *)(unsigned long)ctx->data;
668c2ecf20Sopenharmony_ci	void *data_end  = (void *)(unsigned long)ctx->data_end;
678c2ecf20Sopenharmony_ci	void *data_meta = (void *)(unsigned long)ctx->data_meta;
688c2ecf20Sopenharmony_ci	struct meta_info *meta = data_meta;
698c2ecf20Sopenharmony_ci
708c2ecf20Sopenharmony_ci	/* Check XDP gave us some data_meta */
718c2ecf20Sopenharmony_ci	if (meta + 1 > data) {
728c2ecf20Sopenharmony_ci		ctx->mark = 41;
738c2ecf20Sopenharmony_ci		 /* Skip "accept" if no data_meta is avail */
748c2ecf20Sopenharmony_ci		return TC_ACT_OK;
758c2ecf20Sopenharmony_ci	}
768c2ecf20Sopenharmony_ci
778c2ecf20Sopenharmony_ci	/* Hint: See func tc_cls_act_is_valid_access() for BPF_WRITE access */
788c2ecf20Sopenharmony_ci	ctx->mark = meta->mark; /* Transfer XDP-mark to SKB-mark */
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_ci	return TC_ACT_OK;
818c2ecf20Sopenharmony_ci}
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_ci/* Manually attaching these programs:
848c2ecf20Sopenharmony_ciexport DEV=ixgbe2
858c2ecf20Sopenharmony_ciexport FILE=xdp2skb_meta_kern.o
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_ci# via TC command
888c2ecf20Sopenharmony_citc qdisc del dev $DEV clsact 2> /dev/null
898c2ecf20Sopenharmony_citc qdisc add dev $DEV clsact
908c2ecf20Sopenharmony_citc filter  add dev $DEV ingress prio 1 handle 1 bpf da obj $FILE sec tc_mark
918c2ecf20Sopenharmony_citc filter show dev $DEV ingress
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_ci# XDP via IP command:
948c2ecf20Sopenharmony_ciip link set dev $DEV xdp off
958c2ecf20Sopenharmony_ciip link set dev $DEV xdp obj $FILE sec xdp_mark
968c2ecf20Sopenharmony_ci
978c2ecf20Sopenharmony_ci# Use iptable to "see" if SKBs are marked
988c2ecf20Sopenharmony_ciiptables -I INPUT -p icmp -m mark --mark 41  # == 0x29
998c2ecf20Sopenharmony_ciiptables -I INPUT -p icmp -m mark --mark 42  # == 0x2a
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_ci# Hint: catch XDP_ABORTED errors via
1028c2ecf20Sopenharmony_ciperf record -e xdp:*
1038c2ecf20Sopenharmony_ciperf script
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_ci*/
106