162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* Copyright (c) 2019 Facebook */ 362306a36Sopenharmony_ci#include <linux/stddef.h> 462306a36Sopenharmony_ci#include <linux/if_ether.h> 562306a36Sopenharmony_ci#include <linux/ipv6.h> 662306a36Sopenharmony_ci#include <linux/bpf.h> 762306a36Sopenharmony_ci#include <linux/tcp.h> 862306a36Sopenharmony_ci#include <bpf/bpf_helpers.h> 962306a36Sopenharmony_ci#include <bpf/bpf_endian.h> 1062306a36Sopenharmony_ci#include <bpf/bpf_tracing.h> 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_cistruct sk_buff { 1362306a36Sopenharmony_ci unsigned int len; 1462306a36Sopenharmony_ci}; 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_ci__u64 test_result = 0; 1762306a36Sopenharmony_ciSEC("fexit/test_pkt_access") 1862306a36Sopenharmony_ciint BPF_PROG(test_main, struct sk_buff *skb, int ret) 1962306a36Sopenharmony_ci{ 2062306a36Sopenharmony_ci int len; 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci __builtin_preserve_access_index(({ 2362306a36Sopenharmony_ci len = skb->len; 2462306a36Sopenharmony_ci })); 2562306a36Sopenharmony_ci if (len != 74 || ret != 0) 2662306a36Sopenharmony_ci return 0; 2762306a36Sopenharmony_ci test_result = 1; 2862306a36Sopenharmony_ci return 0; 2962306a36Sopenharmony_ci} 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci__u64 test_result_subprog1 = 0; 3262306a36Sopenharmony_ciSEC("fexit/test_pkt_access_subprog1") 3362306a36Sopenharmony_ciint BPF_PROG(test_subprog1, struct sk_buff *skb, int ret) 3462306a36Sopenharmony_ci{ 3562306a36Sopenharmony_ci int len; 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci __builtin_preserve_access_index(({ 3862306a36Sopenharmony_ci len = skb->len; 3962306a36Sopenharmony_ci })); 4062306a36Sopenharmony_ci if (len != 74 || ret != 148) 4162306a36Sopenharmony_ci return 0; 4262306a36Sopenharmony_ci test_result_subprog1 = 1; 4362306a36Sopenharmony_ci return 0; 4462306a36Sopenharmony_ci} 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci/* Though test_pkt_access_subprog2() is defined in C as: 4762306a36Sopenharmony_ci * static __attribute__ ((noinline)) 4862306a36Sopenharmony_ci * int test_pkt_access_subprog2(int val, volatile struct __sk_buff *skb) 4962306a36Sopenharmony_ci * { 5062306a36Sopenharmony_ci * return skb->len * val; 5162306a36Sopenharmony_ci * } 5262306a36Sopenharmony_ci * llvm optimizations remove 'int val' argument and generate BPF assembly: 5362306a36Sopenharmony_ci * r0 = *(u32 *)(r1 + 0) 5462306a36Sopenharmony_ci * w0 <<= 1 5562306a36Sopenharmony_ci * exit 5662306a36Sopenharmony_ci * In such case the verifier falls back to conservative and 5762306a36Sopenharmony_ci * tracing program can access arguments and return value as u64 5862306a36Sopenharmony_ci * instead of accurate types. 5962306a36Sopenharmony_ci */ 6062306a36Sopenharmony_cistruct args_subprog2 { 6162306a36Sopenharmony_ci __u64 args[5]; 6262306a36Sopenharmony_ci __u64 ret; 6362306a36Sopenharmony_ci}; 6462306a36Sopenharmony_ci__u64 test_result_subprog2 = 0; 6562306a36Sopenharmony_ciSEC("fexit/test_pkt_access_subprog2") 6662306a36Sopenharmony_ciint test_subprog2(struct args_subprog2 *ctx) 6762306a36Sopenharmony_ci{ 6862306a36Sopenharmony_ci struct sk_buff *skb = (void *)ctx->args[0]; 6962306a36Sopenharmony_ci __u64 ret; 7062306a36Sopenharmony_ci int len; 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci bpf_probe_read_kernel(&len, sizeof(len), 7362306a36Sopenharmony_ci __builtin_preserve_access_index(&skb->len)); 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci ret = ctx->ret; 7662306a36Sopenharmony_ci /* bpf_prog_test_load() loads "test_pkt_access.bpf.o" with 7762306a36Sopenharmony_ci * BPF_F_TEST_RND_HI32 which randomizes upper 32 bits after BPF_ALU32 7862306a36Sopenharmony_ci * insns. Hence after 'w0 <<= 1' upper bits of $rax are random. That is 7962306a36Sopenharmony_ci * expected and correct. Trim them. 8062306a36Sopenharmony_ci */ 8162306a36Sopenharmony_ci ret = (__u32) ret; 8262306a36Sopenharmony_ci if (len != 74 || ret != 148) 8362306a36Sopenharmony_ci return 0; 8462306a36Sopenharmony_ci test_result_subprog2 = 1; 8562306a36Sopenharmony_ci return 0; 8662306a36Sopenharmony_ci} 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci__u64 test_result_subprog3 = 0; 8962306a36Sopenharmony_ciSEC("fexit/test_pkt_access_subprog3") 9062306a36Sopenharmony_ciint BPF_PROG(test_subprog3, int val, struct sk_buff *skb, int ret) 9162306a36Sopenharmony_ci{ 9262306a36Sopenharmony_ci int len; 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci __builtin_preserve_access_index(({ 9562306a36Sopenharmony_ci len = skb->len; 9662306a36Sopenharmony_ci })); 9762306a36Sopenharmony_ci if (len != 74 || ret != 74 * val || val != 3) 9862306a36Sopenharmony_ci return 0; 9962306a36Sopenharmony_ci test_result_subprog3 = 1; 10062306a36Sopenharmony_ci return 0; 10162306a36Sopenharmony_ci} 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci__u64 test_get_skb_len = 0; 10462306a36Sopenharmony_ciSEC("freplace/get_skb_len") 10562306a36Sopenharmony_ciint new_get_skb_len(struct __sk_buff *skb) 10662306a36Sopenharmony_ci{ 10762306a36Sopenharmony_ci int len = skb->len; 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci if (len != 74) 11062306a36Sopenharmony_ci return 0; 11162306a36Sopenharmony_ci test_get_skb_len = 1; 11262306a36Sopenharmony_ci return 74; /* original get_skb_len() returns skb->len */ 11362306a36Sopenharmony_ci} 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci__u64 test_get_skb_ifindex = 0; 11662306a36Sopenharmony_ciSEC("freplace/get_skb_ifindex") 11762306a36Sopenharmony_ciint new_get_skb_ifindex(int val, struct __sk_buff *skb, int var) 11862306a36Sopenharmony_ci{ 11962306a36Sopenharmony_ci void *data_end = (void *)(long)skb->data_end; 12062306a36Sopenharmony_ci void *data = (void *)(long)skb->data; 12162306a36Sopenharmony_ci struct ipv6hdr ip6, *ip6p; 12262306a36Sopenharmony_ci int ifindex = skb->ifindex; 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci /* check that BPF extension can read packet via direct packet access */ 12562306a36Sopenharmony_ci if (data + 14 + sizeof(ip6) > data_end) 12662306a36Sopenharmony_ci return 0; 12762306a36Sopenharmony_ci ip6p = data + 14; 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci if (ip6p->nexthdr != 6 || ip6p->payload_len != __bpf_constant_htons(123)) 13062306a36Sopenharmony_ci return 0; 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci /* check that legacy packet access helper works too */ 13362306a36Sopenharmony_ci if (bpf_skb_load_bytes(skb, 14, &ip6, sizeof(ip6)) < 0) 13462306a36Sopenharmony_ci return 0; 13562306a36Sopenharmony_ci ip6p = &ip6; 13662306a36Sopenharmony_ci if (ip6p->nexthdr != 6 || ip6p->payload_len != __bpf_constant_htons(123)) 13762306a36Sopenharmony_ci return 0; 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci if (ifindex != 1 || val != 3 || var != 1) 14062306a36Sopenharmony_ci return 0; 14162306a36Sopenharmony_ci test_get_skb_ifindex = 1; 14262306a36Sopenharmony_ci return 3; /* original get_skb_ifindex() returns val * ifindex * var */ 14362306a36Sopenharmony_ci} 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_civolatile __u64 test_get_constant = 0; 14662306a36Sopenharmony_ciSEC("freplace/get_constant") 14762306a36Sopenharmony_ciint new_get_constant(long val) 14862306a36Sopenharmony_ci{ 14962306a36Sopenharmony_ci if (val != 123) 15062306a36Sopenharmony_ci return 0; 15162306a36Sopenharmony_ci test_get_constant = 1; 15262306a36Sopenharmony_ci return test_get_constant; /* original get_constant() returns val - 122 */ 15362306a36Sopenharmony_ci} 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci__u64 test_pkt_write_access_subprog = 0; 15662306a36Sopenharmony_ciSEC("freplace/test_pkt_write_access_subprog") 15762306a36Sopenharmony_ciint new_test_pkt_write_access_subprog(struct __sk_buff *skb, __u32 off) 15862306a36Sopenharmony_ci{ 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci void *data = (void *)(long)skb->data; 16162306a36Sopenharmony_ci void *data_end = (void *)(long)skb->data_end; 16262306a36Sopenharmony_ci struct tcphdr *tcp; 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci if (off > sizeof(struct ethhdr) + sizeof(struct ipv6hdr)) 16562306a36Sopenharmony_ci return -1; 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci tcp = data + off; 16862306a36Sopenharmony_ci if (tcp + 1 > data_end) 16962306a36Sopenharmony_ci return -1; 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci /* make modifications to the packet data */ 17262306a36Sopenharmony_ci tcp->check++; 17362306a36Sopenharmony_ci tcp->syn = 0; 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci test_pkt_write_access_subprog = 1; 17662306a36Sopenharmony_ci return 0; 17762306a36Sopenharmony_ci} 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_cichar _license[] SEC("license") = "GPL"; 180