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