1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* Copyright (c) 2017 Facebook |
3 | */ |
4 | #include <stddef.h> |
5 | #include <string.h> |
6 | #include <linux/bpf.h> |
7 | #include <linux/if_ether.h> |
8 | #include <linux/if_packet.h> |
9 | #include <linux/ip.h> |
10 | #include <linux/ipv6.h> |
11 | #include <linux/in.h> |
12 | #include <linux/tcp.h> |
13 | #include <linux/pkt_cls.h> |
14 | #include <bpf/bpf_helpers.h> |
15 | #include <bpf/bpf_endian.h> |
16 | #include "bpf_misc.h" |
17 | |
18 | /* llvm will optimize both subprograms into exactly the same BPF assembly |
19 | * |
20 | * Disassembly of section .text: |
21 | * |
22 | * 0000000000000000 test_pkt_access_subprog1: |
23 | * ; return skb->len * 2; |
24 | * 0: 61 10 00 00 00 00 00 00 r0 = *(u32 *)(r1 + 0) |
25 | * 1: 64 00 00 00 01 00 00 00 w0 <<= 1 |
26 | * 2: 95 00 00 00 00 00 00 00 exit |
27 | * |
28 | * 0000000000000018 test_pkt_access_subprog2: |
29 | * ; return skb->len * val; |
30 | * 3: 61 10 00 00 00 00 00 00 r0 = *(u32 *)(r1 + 0) |
31 | * 4: 64 00 00 00 01 00 00 00 w0 <<= 1 |
32 | * 5: 95 00 00 00 00 00 00 00 exit |
33 | * |
34 | * Which makes it an interesting test for BTF-enabled verifier. |
35 | */ |
36 | static __attribute__ ((noinline)) |
37 | int test_pkt_access_subprog1(volatile struct __sk_buff *skb) |
38 | { |
39 | return skb->len * 2; |
40 | } |
41 | |
42 | static __attribute__ ((noinline)) |
43 | int test_pkt_access_subprog2(int val, volatile struct __sk_buff *skb) |
44 | { |
45 | return skb->len * val; |
46 | } |
47 | |
48 | #define MAX_STACK (512 - 2 * 32) |
49 | |
50 | __attribute__ ((noinline)) |
51 | int get_skb_len(struct __sk_buff *skb) |
52 | { |
53 | volatile char buf[MAX_STACK] = {}; |
54 | |
55 | __sink(buf[MAX_STACK - 1]); |
56 | |
57 | return skb->len; |
58 | } |
59 | |
60 | __attribute__ ((noinline)) |
61 | int get_constant(long val) |
62 | { |
63 | return val - 122; |
64 | } |
65 | |
66 | int get_skb_ifindex(int, struct __sk_buff *skb, int); |
67 | |
68 | __attribute__ ((noinline)) |
69 | int test_pkt_access_subprog3(int val, struct __sk_buff *skb) |
70 | { |
71 | return get_skb_len(skb) * get_skb_ifindex(val, skb, get_constant(val: 123)); |
72 | } |
73 | |
74 | __attribute__ ((noinline)) |
75 | int get_skb_ifindex(int val, struct __sk_buff *skb, int var) |
76 | { |
77 | volatile char buf[MAX_STACK] = {}; |
78 | |
79 | __sink(buf[MAX_STACK - 1]); |
80 | |
81 | return skb->ifindex * val * var; |
82 | } |
83 | |
84 | __attribute__ ((noinline)) |
85 | int test_pkt_write_access_subprog(struct __sk_buff *skb, __u32 off) |
86 | { |
87 | void *data = (void *)(long)skb->data; |
88 | void *data_end = (void *)(long)skb->data_end; |
89 | struct tcphdr *tcp = NULL; |
90 | |
91 | if (off > sizeof(struct ethhdr) + sizeof(struct ipv6hdr)) |
92 | return -1; |
93 | |
94 | tcp = data + off; |
95 | if (tcp + 1 > data_end) |
96 | return -1; |
97 | /* make modification to the packet data */ |
98 | tcp->check++; |
99 | return 0; |
100 | } |
101 | |
102 | SEC("tc" ) |
103 | int test_pkt_access(struct __sk_buff *skb) |
104 | { |
105 | void *data_end = (void *)(long)skb->data_end; |
106 | void *data = (void *)(long)skb->data; |
107 | struct ethhdr *eth = (struct ethhdr *)(data); |
108 | struct tcphdr *tcp = NULL; |
109 | __u8 proto = 255; |
110 | __u64 ihl_len; |
111 | |
112 | if (eth + 1 > data_end) |
113 | return TC_ACT_SHOT; |
114 | |
115 | if (eth->h_proto == bpf_htons(ETH_P_IP)) { |
116 | struct iphdr *iph = (struct iphdr *)(eth + 1); |
117 | |
118 | if (iph + 1 > data_end) |
119 | return TC_ACT_SHOT; |
120 | ihl_len = iph->ihl * 4; |
121 | proto = iph->protocol; |
122 | tcp = (struct tcphdr *)((void *)(iph) + ihl_len); |
123 | } else if (eth->h_proto == bpf_htons(ETH_P_IPV6)) { |
124 | struct ipv6hdr *ip6h = (struct ipv6hdr *)(eth + 1); |
125 | |
126 | if (ip6h + 1 > data_end) |
127 | return TC_ACT_SHOT; |
128 | ihl_len = sizeof(*ip6h); |
129 | proto = ip6h->nexthdr; |
130 | tcp = (struct tcphdr *)((void *)(ip6h) + ihl_len); |
131 | } |
132 | |
133 | if (test_pkt_access_subprog1(skb) != skb->len * 2) |
134 | return TC_ACT_SHOT; |
135 | if (test_pkt_access_subprog2(val: 2, skb) != skb->len * 2) |
136 | return TC_ACT_SHOT; |
137 | if (test_pkt_access_subprog3(val: 3, skb) != skb->len * 3 * skb->ifindex) |
138 | return TC_ACT_SHOT; |
139 | if (tcp) { |
140 | if (test_pkt_write_access_subprog(skb, off: (void *)tcp - data)) |
141 | return TC_ACT_SHOT; |
142 | if (((void *)(tcp) + 20) > data_end || proto != 6) |
143 | return TC_ACT_SHOT; |
144 | barrier(); /* to force ordering of checks */ |
145 | if (((void *)(tcp) + 18) > data_end) |
146 | return TC_ACT_SHOT; |
147 | if (tcp->urg_ptr == 123) |
148 | return TC_ACT_OK; |
149 | } |
150 | |
151 | return TC_ACT_UNSPEC; |
152 | } |
153 | |