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