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