1#!/usr/bin/python3 2# 3# Copyright 2016 The Android Open Source Project 4# 5# Licensed under the Apache License, Version 2.0 (the "License"); 6# you may not use this file except in compliance with the License. 7# You may obtain a copy of the License at 8# 9# http://www.apache.org/licenses/LICENSE-2.0 10# 11# Unless required by applicable law or agreed to in writing, software 12# distributed under the License is distributed on an "AS IS" BASIS, 13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14# See the License for the specific language governing permissions and 15# limitations under the License. 16 17"""kernel net test library for bpf testing.""" 18 19import ctypes 20import os 21import resource 22import socket 23import sys 24 25import csocket 26import cstruct 27import net_test 28 29# __NR_bpf syscall numbers for various architectures. 30# NOTE: If python inherited COMPAT_UTS_MACHINE, uname's 'machine' field will 31# return the 32-bit architecture name, even if python itself is 64-bit. To work 32# around this problem and pick the right syscall nr, we can additionally check 33# the bitness of the python interpreter. Assume that the 64-bit architectures 34# are not running with COMPAT_UTS_MACHINE and must be 64-bit at all times. 35__NR_bpf = { # pylint: disable=invalid-name 36 "aarch64-32bit": 386, 37 "aarch64-64bit": 280, 38 "armv7l-32bit": 386, 39 "armv8l-32bit": 386, 40 "armv8l-64bit": 280, 41 "i686-32bit": 357, 42 "i686-64bit": 321, 43 "x86_64-32bit": 357, 44 "x86_64-64bit": 321, 45 "riscv64-64bit": 280, 46}[os.uname()[4] + "-" + ("64" if sys.maxsize > 0x7FFFFFFF else "32") + "bit"] 47 48# After ACK merge of 5.10.168 is when support for this was backported from 49# upstream Linux 5.14 and was merged into ACK android{12,13}-5.10 branches. 50# ACK android12-5.10 was >= 5.10.168 without this support only for ~4.5 hours 51# ACK android13-4.10 was >= 5.10.168 without this support only for ~25 hours 52# as such we can >= 5.10.168 instead of > 5.10.168 53HAVE_SO_NETNS_COOKIE = net_test.LINUX_VERSION >= (5, 10, 168) 54 55# Note: This is *not* correct for parisc & sparc architectures 56SO_NETNS_COOKIE = 71 57 58LOG_LEVEL = 1 59LOG_SIZE = 65536 60 61# BPF syscall commands constants. 62BPF_MAP_CREATE = 0 63BPF_MAP_LOOKUP_ELEM = 1 64BPF_MAP_UPDATE_ELEM = 2 65BPF_MAP_DELETE_ELEM = 3 66BPF_MAP_GET_NEXT_KEY = 4 67BPF_PROG_LOAD = 5 68BPF_OBJ_PIN = 6 69BPF_OBJ_GET = 7 70BPF_PROG_ATTACH = 8 71BPF_PROG_DETACH = 9 72SO_ATTACH_BPF = 50 73 74# BPF map type constant. 75BPF_MAP_TYPE_UNSPEC = 0 76BPF_MAP_TYPE_HASH = 1 77BPF_MAP_TYPE_ARRAY = 2 78BPF_MAP_TYPE_PROG_ARRAY = 3 79BPF_MAP_TYPE_PERF_EVENT_ARRAY = 4 80 81# BPF program type constant. 82BPF_PROG_TYPE_UNSPEC = 0 83BPF_PROG_TYPE_SOCKET_FILTER = 1 84BPF_PROG_TYPE_KPROBE = 2 85BPF_PROG_TYPE_SCHED_CLS = 3 86BPF_PROG_TYPE_SCHED_ACT = 4 87BPF_PROG_TYPE_TRACEPOINT = 5 88BPF_PROG_TYPE_XDP = 6 89BPF_PROG_TYPE_PERF_EVENT = 7 90BPF_PROG_TYPE_CGROUP_SKB = 8 91BPF_PROG_TYPE_CGROUP_SOCK = 9 92 93# BPF program attach type. 94BPF_CGROUP_INET_INGRESS = 0 95BPF_CGROUP_INET_EGRESS = 1 96BPF_CGROUP_INET_SOCK_CREATE = 2 97 98# BPF register constant 99BPF_REG_0 = 0 100BPF_REG_1 = 1 101BPF_REG_2 = 2 102BPF_REG_3 = 3 103BPF_REG_4 = 4 104BPF_REG_5 = 5 105BPF_REG_6 = 6 106BPF_REG_7 = 7 107BPF_REG_8 = 8 108BPF_REG_9 = 9 109BPF_REG_10 = 10 110 111# BPF instruction constants 112BPF_PSEUDO_MAP_FD = 1 113BPF_LD = 0x00 114BPF_LDX = 0x01 115BPF_ST = 0x02 116BPF_STX = 0x03 117BPF_ALU = 0x04 118BPF_JMP = 0x05 119BPF_RET = 0x06 120BPF_MISC = 0x07 121BPF_W = 0x00 122BPF_H = 0x08 123BPF_B = 0x10 124BPF_IMM = 0x00 125BPF_ABS = 0x20 126BPF_IND = 0x40 127BPF_MEM = 0x60 128BPF_LEN = 0x80 129BPF_MSH = 0xa0 130BPF_ADD = 0x00 131BPF_SUB = 0x10 132BPF_MUL = 0x20 133BPF_DIV = 0x30 134BPF_OR = 0x40 135BPF_AND = 0x50 136BPF_LSH = 0x60 137BPF_RSH = 0x70 138BPF_NEG = 0x80 139BPF_MOD = 0x90 140BPF_XOR = 0xa0 141BPF_JA = 0x00 142BPF_JEQ = 0x10 143BPF_JGT = 0x20 144BPF_JGE = 0x30 145BPF_JSET = 0x40 146BPF_K = 0x00 147BPF_X = 0x08 148BPF_ALU64 = 0x07 149BPF_DW = 0x18 150BPF_XADD = 0xc0 151BPF_MOV = 0xb0 152 153BPF_ARSH = 0xc0 154BPF_END = 0xd0 155BPF_TO_LE = 0x00 156BPF_TO_BE = 0x08 157 158BPF_JNE = 0x50 159BPF_JSGT = 0x60 160 161BPF_JSGE = 0x70 162BPF_CALL = 0x80 163BPF_EXIT = 0x90 164 165# BPF helper function constants 166# pylint: disable=invalid-name 167BPF_FUNC_unspec = 0 168BPF_FUNC_map_lookup_elem = 1 169BPF_FUNC_map_update_elem = 2 170BPF_FUNC_map_delete_elem = 3 171BPF_FUNC_ktime_get_ns = 5 172BPF_FUNC_get_current_uid_gid = 15 173BPF_FUNC_skb_change_head = 43 174BPF_FUNC_get_socket_cookie = 46 175BPF_FUNC_get_socket_uid = 47 176BPF_FUNC_ktime_get_boot_ns = 125 177# pylint: enable=invalid-name 178 179BPF_F_RDONLY = 1 << 3 180BPF_F_WRONLY = 1 << 4 181 182# These object below belongs to the same kernel union and the types below 183# (e.g., bpf_attr_create) aren't kernel struct names but just different 184# variants of the union. 185# pylint: disable=invalid-name 186BpfAttrCreate = cstruct.Struct( 187 "bpf_attr_create", "=IIIII", 188 "map_type key_size value_size max_entries, map_flags") 189BpfAttrOps = cstruct.Struct( 190 "bpf_attr_ops", "=QQQQ", 191 "map_fd key_ptr value_ptr flags") 192BpfAttrProgLoad = cstruct.Struct( 193 "bpf_attr_prog_load", "=IIQQIIQI", "prog_type insn_cnt insns" 194 " license log_level log_size log_buf kern_version") 195BpfAttrProgAttach = cstruct.Struct( 196 "bpf_attr_prog_attach", "=III", "target_fd attach_bpf_fd attach_type") 197BpfInsn = cstruct.Struct("bpf_insn", "=BBhi", "code dst_src_reg off imm") 198# pylint: enable=invalid-name 199 200libc = ctypes.CDLL(ctypes.util.find_library("c"), use_errno=True) 201HAVE_EBPF_4_19 = net_test.LINUX_VERSION >= (4, 19, 0) 202HAVE_EBPF_5_4 = net_test.LINUX_VERSION >= (5, 4, 0) 203 204# set memlock resource 1 GiB 205resource.setrlimit(resource.RLIMIT_MEMLOCK, (1073741824, 1073741824)) 206 207 208# BPF program syscalls 209def BpfSyscall(op, attr): 210 ret = libc.syscall(__NR_bpf, op, csocket.VoidPointer(attr), len(attr)) 211 csocket.MaybeRaiseSocketError(ret) 212 return ret 213 214 215def CreateMap(map_type, key_size, value_size, max_entries, map_flags=0): 216 attr = BpfAttrCreate((map_type, key_size, value_size, max_entries, map_flags)) 217 return BpfSyscall(BPF_MAP_CREATE, attr) 218 219 220def UpdateMap(map_fd, key, value, flags=0): 221 c_value = ctypes.c_uint32(value) 222 c_key = ctypes.c_uint32(key) 223 value_ptr = ctypes.addressof(c_value) 224 key_ptr = ctypes.addressof(c_key) 225 attr = BpfAttrOps((map_fd, key_ptr, value_ptr, flags)) 226 BpfSyscall(BPF_MAP_UPDATE_ELEM, attr) 227 228 229def LookupMap(map_fd, key): 230 c_value = ctypes.c_uint32(0) 231 c_key = ctypes.c_uint32(key) 232 attr = BpfAttrOps( 233 (map_fd, ctypes.addressof(c_key), ctypes.addressof(c_value), 0)) 234 BpfSyscall(BPF_MAP_LOOKUP_ELEM, attr) 235 return c_value 236 237 238def GetNextKey(map_fd, key): 239 """Get the next key in the map after the specified key.""" 240 if key is not None: 241 c_key = ctypes.c_uint32(key) 242 c_next_key = ctypes.c_uint32(0) 243 key_ptr = ctypes.addressof(c_key) 244 else: 245 key_ptr = 0 246 c_next_key = ctypes.c_uint32(0) 247 attr = BpfAttrOps( 248 (map_fd, key_ptr, ctypes.addressof(c_next_key), 0)) 249 BpfSyscall(BPF_MAP_GET_NEXT_KEY, attr) 250 return c_next_key 251 252 253def GetFirstKey(map_fd): 254 return GetNextKey(map_fd, None) 255 256 257def DeleteMap(map_fd, key): 258 c_key = ctypes.c_uint32(key) 259 attr = BpfAttrOps((map_fd, ctypes.addressof(c_key), 0, 0)) 260 BpfSyscall(BPF_MAP_DELETE_ELEM, attr) 261 262 263def BpfProgLoad(prog_type, instructions, prog_license=b"GPL"): 264 bpf_prog = b"".join(instructions) 265 insn_buff = ctypes.create_string_buffer(bpf_prog) 266 gpl_license = ctypes.create_string_buffer(prog_license) 267 log_buf = ctypes.create_string_buffer(b"", LOG_SIZE) 268 attr = BpfAttrProgLoad((prog_type, len(insn_buff) // len(BpfInsn), 269 ctypes.addressof(insn_buff), 270 ctypes.addressof(gpl_license), LOG_LEVEL, 271 LOG_SIZE, ctypes.addressof(log_buf), 0)) 272 return BpfSyscall(BPF_PROG_LOAD, attr) 273 274 275# Attach a socket eBPF filter to a target socket 276def BpfProgAttachSocket(sock_fd, prog_fd): 277 uint_fd = ctypes.c_uint32(prog_fd) 278 ret = libc.setsockopt(sock_fd, socket.SOL_SOCKET, SO_ATTACH_BPF, 279 ctypes.pointer(uint_fd), ctypes.sizeof(uint_fd)) 280 csocket.MaybeRaiseSocketError(ret) 281 282 283# Attach a eBPF filter to a cgroup 284def BpfProgAttach(prog_fd, target_fd, prog_type): 285 attr = BpfAttrProgAttach((target_fd, prog_fd, prog_type)) 286 return BpfSyscall(BPF_PROG_ATTACH, attr) 287 288 289# Detach a eBPF filter from a cgroup 290def BpfProgDetach(target_fd, prog_type): 291 attr = BpfAttrProgAttach((target_fd, 0, prog_type)) 292 return BpfSyscall(BPF_PROG_DETACH, attr) 293 294 295# BPF program command constructors 296def BpfMov64Reg(dst, src): 297 code = BPF_ALU64 | BPF_MOV | BPF_X 298 dst_src = src << 4 | dst 299 ret = BpfInsn((code, dst_src, 0, 0)) 300 return ret.Pack() 301 302 303def BpfLdxMem(size, dst, src, off): 304 code = BPF_LDX | (size & 0x18) | BPF_MEM 305 dst_src = src << 4 | dst 306 ret = BpfInsn((code, dst_src, off, 0)) 307 return ret.Pack() 308 309 310def BpfStxMem(size, dst, src, off): 311 code = BPF_STX | (size & 0x18) | BPF_MEM 312 dst_src = src << 4 | dst 313 ret = BpfInsn((code, dst_src, off, 0)) 314 return ret.Pack() 315 316 317def BpfStMem(size, dst, off, imm): 318 code = BPF_ST | (size & 0x18) | BPF_MEM 319 dst_src = dst 320 ret = BpfInsn((code, dst_src, off, imm)) 321 return ret.Pack() 322 323 324def BpfAlu64Imm(op, dst, imm): 325 code = BPF_ALU64 | (op & 0xf0) | BPF_K 326 dst_src = dst 327 ret = BpfInsn((code, dst_src, 0, imm)) 328 return ret.Pack() 329 330 331def BpfJumpImm(op, dst, imm, off): 332 code = BPF_JMP | (op & 0xf0) | BPF_K 333 dst_src = dst 334 ret = BpfInsn((code, dst_src, off, imm)) 335 return ret.Pack() 336 337 338def BpfRawInsn(code, dst, src, off, imm): 339 ret = BpfInsn((code, (src << 4 | dst), off, imm)) 340 return ret.Pack() 341 342 343def BpfMov64Imm(dst, imm): 344 code = BPF_ALU64 | BPF_MOV | BPF_K 345 dst_src = dst 346 ret = BpfInsn((code, dst_src, 0, imm)) 347 return ret.Pack() 348 349 350def BpfExitInsn(): 351 code = BPF_JMP | BPF_EXIT 352 ret = BpfInsn((code, 0, 0, 0)) 353 return ret.Pack() 354 355 356def BpfLoadMapFd(map_fd, dst): 357 code = BPF_LD | BPF_DW | BPF_IMM 358 dst_src = BPF_PSEUDO_MAP_FD << 4 | dst 359 insn1 = BpfInsn((code, dst_src, 0, map_fd)) 360 insn2 = BpfInsn((0, 0, 0, map_fd >> 32)) 361 return insn1.Pack() + insn2.Pack() 362 363 364def BpfFuncCall(func): 365 code = BPF_JMP | BPF_CALL 366 dst_src = 0 367 ret = BpfInsn((code, dst_src, 0, func)) 368 return ret.Pack() 369