1 // SPDX-License-Identifier: GPL-2.0 2 /* Copyright (c) 2024 Meta Platforms, Inc. and affiliates. */ 3 4 #include "vmlinux.h" 5 #include "bpf_tracing_net.h" 6 #include <bpf/bpf_helpers.h> 7 #include <bpf/bpf_endian.h> 8 #include <bpf/bpf_tracing.h> 9 #include "bpf_misc.h" 10 #include "bpf_kfuncs.h" 11 #include "crypto_common.h" 12 13 /* 14 * key[] and algo[] are 8-byte aligned and 'params' is kept off the stack to 15 * work around an LLVM code generation bug. clang lowers the memcpy() of these 16 * byte-aligned globals into a per-byte load/store sequence staged on the stack, 17 * and additionally materializes the on-stack 'struct bpf_crypto_params' twice. 18 * Both blow the 512-byte BPF stack limit. Aligning the sources lets clang copy 19 * word-wise, and a global 'params' removes the large object from the stack. 20 */ 21 unsigned char key[256] __attribute__((aligned(8))) = {}; 22 u16 udp_test_port = 7777; 23 u32 authsize, key_len; 24 char algo[128] __attribute__((aligned(8))) = {}; 25 char dst[16] = {}, dst_bad[8] = {}; 26 static struct bpf_crypto_params params; 27 int status; 28 29 static int skb_dynptr_validate(struct __sk_buff *skb, struct bpf_dynptr *psrc) 30 { 31 struct ipv6hdr ip6h; 32 struct udphdr udph; 33 u32 offset; 34 35 if (skb->protocol != __bpf_constant_htons(ETH_P_IPV6)) 36 return -1; 37 38 if (bpf_skb_load_bytes(skb, ETH_HLEN, &ip6h, sizeof(ip6h))) 39 return -1; 40 41 if (ip6h.nexthdr != IPPROTO_UDP) 42 return -1; 43 44 if (bpf_skb_load_bytes(skb, ETH_HLEN + sizeof(ip6h), &udph, sizeof(udph))) 45 return -1; 46 47 if (udph.dest != __bpf_htons(udp_test_port)) 48 return -1; 49 50 offset = ETH_HLEN + sizeof(ip6h) + sizeof(udph); 51 if (skb->len < offset + 16) 52 return -1; 53 54 /* let's make sure that 16 bytes of payload are in the linear part of skb */ 55 bpf_skb_pull_data(skb, offset + 16); 56 bpf_dynptr_from_skb(skb, 0, psrc); 57 bpf_dynptr_adjust(psrc, offset, offset + 16); 58 59 return 0; 60 } 61 62 SEC("syscall") 63 int skb_crypto_setup(void *ctx) 64 { 65 struct bpf_crypto_ctx *cctx; 66 int err; 67 68 status = 0; 69 if (key_len > 256) { 70 status = -EINVAL; 71 return 0; 72 } 73 74 __builtin_memcpy(¶ms.type, "skcipher", sizeof("skcipher")); 75 params.key_len = key_len; 76 params.authsize = authsize; 77 __builtin_memcpy(¶ms.algo, algo, sizeof(algo)); 78 __builtin_memcpy(¶ms.key, key, sizeof(key)); 79 80 cctx = bpf_crypto_ctx_create(¶ms, sizeof(params), &err); 81 if (!cctx) { 82 status = err; 83 return 0; 84 } 85 86 err = crypto_ctx_insert(cctx); 87 if (err && err != -EEXIST) 88 status = err; 89 return 0; 90 } 91 92 SEC("tc") 93 int decrypt_sanity(struct __sk_buff *skb) 94 { 95 struct __crypto_ctx_value *v; 96 struct bpf_crypto_ctx *ctx; 97 struct bpf_dynptr psrc, pdst; 98 int err; 99 100 status = 0; 101 err = skb_dynptr_validate(skb, &psrc); 102 if (err < 0) { 103 status = err; 104 return TC_ACT_SHOT; 105 } 106 107 v = crypto_ctx_value_lookup(); 108 if (!v) { 109 status = -ENOENT; 110 return TC_ACT_SHOT; 111 } 112 113 ctx = v->ctx; 114 if (!ctx) { 115 status = -ENOENT; 116 return TC_ACT_SHOT; 117 } 118 119 /* Check also bad case where the dst buffer is smaller than the 120 * skb's linear section. 121 */ 122 bpf_dynptr_from_mem(dst_bad, sizeof(dst_bad), 0, &pdst); 123 status = bpf_crypto_decrypt(ctx, &psrc, &pdst, NULL); 124 if (!status) 125 status = -EIO; 126 if (status != -EINVAL) 127 goto err; 128 129 /* dst is a global variable to make testing part easier to check. 130 * In real production code, a percpu map should be used to store 131 * the result. 132 */ 133 bpf_dynptr_from_mem(dst, sizeof(dst), 0, &pdst); 134 status = bpf_crypto_decrypt(ctx, &psrc, &pdst, NULL); 135 err: 136 return TC_ACT_SHOT; 137 } 138 139 SEC("tc") 140 int encrypt_sanity(struct __sk_buff *skb) 141 { 142 struct __crypto_ctx_value *v; 143 struct bpf_crypto_ctx *ctx; 144 struct bpf_dynptr psrc, pdst; 145 int err; 146 147 status = 0; 148 err = skb_dynptr_validate(skb, &psrc); 149 if (err < 0) { 150 status = err; 151 return TC_ACT_SHOT; 152 } 153 154 v = crypto_ctx_value_lookup(); 155 if (!v) { 156 status = -ENOENT; 157 return TC_ACT_SHOT; 158 } 159 160 ctx = v->ctx; 161 if (!ctx) { 162 status = -ENOENT; 163 return TC_ACT_SHOT; 164 } 165 166 /* Check also bad case where the dst buffer is smaller than the 167 * skb's linear section. 168 */ 169 bpf_dynptr_from_mem(dst_bad, sizeof(dst_bad), 0, &pdst); 170 status = bpf_crypto_encrypt(ctx, &psrc, &pdst, NULL); 171 if (!status) 172 status = -EIO; 173 if (status != -EINVAL) 174 goto err; 175 176 /* dst is a global variable to make testing part easier to check. 177 * In real production code, a percpu map should be used to store 178 * the result. 179 */ 180 bpf_dynptr_from_mem(dst, sizeof(dst), 0, &pdst); 181 status = bpf_crypto_encrypt(ctx, &psrc, &pdst, NULL); 182 err: 183 return TC_ACT_SHOT; 184 } 185 186 char __license[] SEC("license") = "GPL"; 187