cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

test_xdp_vlan.c (7514B)


      1/* SPDX-License-Identifier: GPL-2.0
      2 *  Copyright(c) 2018 Jesper Dangaard Brouer.
      3 *
      4 * XDP/TC VLAN manipulation example
      5 *
      6 * GOTCHA: Remember to disable NIC hardware offloading of VLANs,
      7 * else the VLAN tags are NOT inlined in the packet payload:
      8 *
      9 *  # ethtool -K ixgbe2 rxvlan off
     10 *
     11 * Verify setting:
     12 *  # ethtool -k ixgbe2 | grep rx-vlan-offload
     13 *  rx-vlan-offload: off
     14 *
     15 */
     16#include <stddef.h>
     17#include <stdbool.h>
     18#include <string.h>
     19#include <linux/bpf.h>
     20#include <linux/if_ether.h>
     21#include <linux/if_vlan.h>
     22#include <linux/in.h>
     23#include <linux/pkt_cls.h>
     24
     25#include <bpf/bpf_helpers.h>
     26#include <bpf/bpf_endian.h>
     27
     28/* linux/if_vlan.h have not exposed this as UAPI, thus mirror some here
     29 *
     30 *	struct vlan_hdr - vlan header
     31 *	@h_vlan_TCI: priority and VLAN ID
     32 *	@h_vlan_encapsulated_proto: packet type ID or len
     33 */
     34struct _vlan_hdr {
     35	__be16 h_vlan_TCI;
     36	__be16 h_vlan_encapsulated_proto;
     37};
     38#define VLAN_PRIO_MASK		0xe000 /* Priority Code Point */
     39#define VLAN_PRIO_SHIFT		13
     40#define VLAN_CFI_MASK		0x1000 /* Canonical Format Indicator */
     41#define VLAN_TAG_PRESENT	VLAN_CFI_MASK
     42#define VLAN_VID_MASK		0x0fff /* VLAN Identifier */
     43#define VLAN_N_VID		4096
     44
     45struct parse_pkt {
     46	__u16 l3_proto;
     47	__u16 l3_offset;
     48	__u16 vlan_outer;
     49	__u16 vlan_inner;
     50	__u8  vlan_outer_offset;
     51	__u8  vlan_inner_offset;
     52};
     53
     54char _license[] SEC("license") = "GPL";
     55
     56static __always_inline
     57bool parse_eth_frame(struct ethhdr *eth, void *data_end, struct parse_pkt *pkt)
     58{
     59	__u16 eth_type;
     60	__u8 offset;
     61
     62	offset = sizeof(*eth);
     63	/* Make sure packet is large enough for parsing eth + 2 VLAN headers */
     64	if ((void *)eth + offset + (2*sizeof(struct _vlan_hdr)) > data_end)
     65		return false;
     66
     67	eth_type = eth->h_proto;
     68
     69	/* Handle outer VLAN tag */
     70	if (eth_type == bpf_htons(ETH_P_8021Q)
     71	    || eth_type == bpf_htons(ETH_P_8021AD)) {
     72		struct _vlan_hdr *vlan_hdr;
     73
     74		vlan_hdr = (void *)eth + offset;
     75		pkt->vlan_outer_offset = offset;
     76		pkt->vlan_outer = bpf_ntohs(vlan_hdr->h_vlan_TCI)
     77				& VLAN_VID_MASK;
     78		eth_type        = vlan_hdr->h_vlan_encapsulated_proto;
     79		offset += sizeof(*vlan_hdr);
     80	}
     81
     82	/* Handle inner (double) VLAN tag */
     83	if (eth_type == bpf_htons(ETH_P_8021Q)
     84	    || eth_type == bpf_htons(ETH_P_8021AD)) {
     85		struct _vlan_hdr *vlan_hdr;
     86
     87		vlan_hdr = (void *)eth + offset;
     88		pkt->vlan_inner_offset = offset;
     89		pkt->vlan_inner = bpf_ntohs(vlan_hdr->h_vlan_TCI)
     90				& VLAN_VID_MASK;
     91		eth_type        = vlan_hdr->h_vlan_encapsulated_proto;
     92		offset += sizeof(*vlan_hdr);
     93	}
     94
     95	pkt->l3_proto = bpf_ntohs(eth_type); /* Convert to host-byte-order */
     96	pkt->l3_offset = offset;
     97
     98	return true;
     99}
    100
    101/* Hint, VLANs are choosen to hit network-byte-order issues */
    102#define TESTVLAN 4011 /* 0xFAB */
    103// #define TO_VLAN  4000 /* 0xFA0 (hint 0xOA0 = 160) */
    104
    105SEC("xdp_drop_vlan_4011")
    106int  xdp_prognum0(struct xdp_md *ctx)
    107{
    108	void *data_end = (void *)(long)ctx->data_end;
    109	void *data     = (void *)(long)ctx->data;
    110	struct parse_pkt pkt = { 0 };
    111
    112	if (!parse_eth_frame(data, data_end, &pkt))
    113		return XDP_ABORTED;
    114
    115	/* Drop specific VLAN ID example */
    116	if (pkt.vlan_outer == TESTVLAN)
    117		return XDP_ABORTED;
    118	/*
    119	 * Using XDP_ABORTED makes it possible to record this event,
    120	 * via tracepoint xdp:xdp_exception like:
    121	 *  # perf record -a -e xdp:xdp_exception
    122	 *  # perf script
    123	 */
    124	return XDP_PASS;
    125}
    126/*
    127Commands to setup VLAN on Linux to test packets gets dropped:
    128
    129 export ROOTDEV=ixgbe2
    130 export VLANID=4011
    131 ip link add link $ROOTDEV name $ROOTDEV.$VLANID type vlan id $VLANID
    132 ip link set dev  $ROOTDEV.$VLANID up
    133
    134 ip link set dev $ROOTDEV mtu 1508
    135 ip addr add 100.64.40.11/24 dev $ROOTDEV.$VLANID
    136
    137Load prog with ip tool:
    138
    139 ip link set $ROOTDEV xdp off
    140 ip link set $ROOTDEV xdp object xdp_vlan01_kern.o section xdp_drop_vlan_4011
    141
    142*/
    143
    144/* Changing VLAN to zero, have same practical effect as removing the VLAN. */
    145#define TO_VLAN	0
    146
    147SEC("xdp_vlan_change")
    148int  xdp_prognum1(struct xdp_md *ctx)
    149{
    150	void *data_end = (void *)(long)ctx->data_end;
    151	void *data     = (void *)(long)ctx->data;
    152	struct parse_pkt pkt = { 0 };
    153
    154	if (!parse_eth_frame(data, data_end, &pkt))
    155		return XDP_ABORTED;
    156
    157	/* Change specific VLAN ID */
    158	if (pkt.vlan_outer == TESTVLAN) {
    159		struct _vlan_hdr *vlan_hdr = data + pkt.vlan_outer_offset;
    160
    161		/* Modifying VLAN, preserve top 4 bits */
    162		vlan_hdr->h_vlan_TCI =
    163			bpf_htons((bpf_ntohs(vlan_hdr->h_vlan_TCI) & 0xf000)
    164				  | TO_VLAN);
    165	}
    166
    167	return XDP_PASS;
    168}
    169
    170/*
    171 * Show XDP+TC can cooperate, on creating a VLAN rewriter.
    172 * 1. Create a XDP prog that can "pop"/remove a VLAN header.
    173 * 2. Create a TC-bpf prog that egress can add a VLAN header.
    174 */
    175
    176#ifndef ETH_ALEN /* Ethernet MAC address length */
    177#define ETH_ALEN	6	/* bytes */
    178#endif
    179#define VLAN_HDR_SZ	4	/* bytes */
    180
    181SEC("xdp_vlan_remove_outer")
    182int  xdp_prognum2(struct xdp_md *ctx)
    183{
    184	void *data_end = (void *)(long)ctx->data_end;
    185	void *data     = (void *)(long)ctx->data;
    186	struct parse_pkt pkt = { 0 };
    187	char *dest;
    188
    189	if (!parse_eth_frame(data, data_end, &pkt))
    190		return XDP_ABORTED;
    191
    192	/* Skip packet if no outer VLAN was detected */
    193	if (pkt.vlan_outer_offset == 0)
    194		return XDP_PASS;
    195
    196	/* Moving Ethernet header, dest overlap with src, memmove handle this */
    197	dest = data;
    198	dest+= VLAN_HDR_SZ;
    199	/*
    200	 * Notice: Taking over vlan_hdr->h_vlan_encapsulated_proto, by
    201	 * only moving two MAC addrs (12 bytes), not overwriting last 2 bytes
    202	 */
    203	__builtin_memmove(dest, data, ETH_ALEN * 2);
    204	/* Note: LLVM built-in memmove inlining require size to be constant */
    205
    206	/* Move start of packet header seen by Linux kernel stack */
    207	bpf_xdp_adjust_head(ctx, VLAN_HDR_SZ);
    208
    209	return XDP_PASS;
    210}
    211
    212static __always_inline
    213void shift_mac_4bytes_16bit(void *data)
    214{
    215	__u16 *p = data;
    216
    217	p[7] = p[5]; /* delete p[7] was vlan_hdr->h_vlan_TCI */
    218	p[6] = p[4]; /* delete p[6] was ethhdr->h_proto */
    219	p[5] = p[3];
    220	p[4] = p[2];
    221	p[3] = p[1];
    222	p[2] = p[0];
    223}
    224
    225static __always_inline
    226void shift_mac_4bytes_32bit(void *data)
    227{
    228	__u32 *p = data;
    229
    230	/* Assuming VLAN hdr present. The 4 bytes in p[3] that gets
    231	 * overwritten, is ethhdr->h_proto and vlan_hdr->h_vlan_TCI.
    232	 * The vlan_hdr->h_vlan_encapsulated_proto take over role as
    233	 * ethhdr->h_proto.
    234	 */
    235	p[3] = p[2];
    236	p[2] = p[1];
    237	p[1] = p[0];
    238}
    239
    240SEC("xdp_vlan_remove_outer2")
    241int  xdp_prognum3(struct xdp_md *ctx)
    242{
    243	void *data_end = (void *)(long)ctx->data_end;
    244	void *data     = (void *)(long)ctx->data;
    245	struct ethhdr *orig_eth = data;
    246	struct parse_pkt pkt = { 0 };
    247
    248	if (!parse_eth_frame(orig_eth, data_end, &pkt))
    249		return XDP_ABORTED;
    250
    251	/* Skip packet if no outer VLAN was detected */
    252	if (pkt.vlan_outer_offset == 0)
    253		return XDP_PASS;
    254
    255	/* Simply shift down MAC addrs 4 bytes, overwrite h_proto + TCI */
    256	shift_mac_4bytes_32bit(data);
    257
    258	/* Move start of packet header seen by Linux kernel stack */
    259	bpf_xdp_adjust_head(ctx, VLAN_HDR_SZ);
    260
    261	return XDP_PASS;
    262}
    263
    264/*=====================================
    265 *  BELOW: TC-hook based ebpf programs
    266 * ====================================
    267 * The TC-clsact eBPF programs (currently) need to be attach via TC commands
    268 */
    269
    270SEC("tc_vlan_push")
    271int _tc_progA(struct __sk_buff *ctx)
    272{
    273	bpf_skb_vlan_push(ctx, bpf_htons(ETH_P_8021Q), TESTVLAN);
    274
    275	return TC_ACT_OK;
    276}
    277/*
    278Commands to setup TC to use above bpf prog:
    279
    280export ROOTDEV=ixgbe2
    281export FILE=xdp_vlan01_kern.o
    282
    283# Re-attach clsact to clear/flush existing role
    284tc qdisc del dev $ROOTDEV clsact 2> /dev/null ;\
    285tc qdisc add dev $ROOTDEV clsact
    286
    287# Attach BPF prog EGRESS
    288tc filter add dev $ROOTDEV egress \
    289  prio 1 handle 1 bpf da obj $FILE sec tc_vlan_push
    290
    291tc filter show dev $ROOTDEV egress
    292*/