#!/usr/bin/python # # Copyright 2016 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. import ctypes import csocket import cstruct import net_test import socket # TODO: figure out how to make this arch-dependent if we run these tests # on non-X86 __NR_bpf = 321 LOG_LEVEL = 1 LOG_SIZE = 65536 # BPF syscall commands constants. BPF_MAP_CREATE = 0 BPF_MAP_LOOKUP_ELEM = 1 BPF_MAP_UPDATE_ELEM = 2 BPF_MAP_DELETE_ELEM = 3 BPF_MAP_GET_NEXT_KEY = 4 BPF_PROG_LOAD = 5 BPF_OBJ_PIN = 6 BPF_OBJ_GET = 7 SO_ATTACH_BPF = 50 # BPF map type constant. BPF_MAP_TYPE_UNSPEC = 0 BPF_MAP_TYPE_HASH = 1 BPF_MAP_TYPE_ARRAY = 2 BPF_MAP_TYPE_PROG_ARRAY = 3 BPF_MAP_TYPE_PERF_EVENT_ARRAY = 4 # BPF program type constant. BPF_PROG_TYPE_UNSPEC = 0 BPF_PROG_TYPE_SOCKET_FILTER = 1 BPF_PROG_TYPE_KPROBE = 2 BPF_PROG_TYPE_SCHED_CLS = 3 BPF_PROG_TYPE_SCHED_ACT = 4 # BPF register constant BPF_REG_0 = 0 BPF_REG_1 = 1 BPF_REG_2 = 2 BPF_REG_3 = 3 BPF_REG_4 = 4 BPF_REG_5 = 5 BPF_REG_6 = 6 BPF_REG_7 = 7 BPF_REG_8 = 8 BPF_REG_9 = 9 BPF_REG_10 = 10 # BPF instruction constants BPF_PSEUDO_MAP_FD = 1 BPF_LD = 0x00 BPF_LDX = 0x01 BPF_ST = 0x02 BPF_STX = 0x03 BPF_ALU = 0x04 BPF_JMP = 0x05 BPF_RET = 0x06 BPF_MISC = 0x07 BPF_W = 0x00 BPF_H = 0x08 BPF_B = 0x10 BPF_IMM = 0x00 BPF_ABS = 0x20 BPF_IND = 0x40 BPF_MEM = 0x60 BPF_LEN = 0x80 BPF_MSH = 0xa0 BPF_ADD = 0x00 BPF_SUB = 0x10 BPF_MUL = 0x20 BPF_DIV = 0x30 BPF_OR = 0x40 BPF_AND = 0x50 BPF_LSH = 0x60 BPF_RSH = 0x70 BPF_NEG = 0x80 BPF_MOD = 0x90 BPF_XOR = 0xa0 BPF_JA = 0x00 BPF_JEQ = 0x10 BPF_JGT = 0x20 BPF_JGE = 0x30 BPF_JSET = 0x40 BPF_K = 0x00 BPF_X = 0x08 BPF_ALU64 = 0x07 BPF_DW = 0x18 BPF_XADD = 0xc0 BPF_MOV = 0xb0 BPF_ARSH = 0xc0 BPF_END = 0xd0 BPF_TO_LE = 0x00 BPF_TO_BE = 0x08 BPF_JNE = 0x50 BPF_JSGT = 0x60 BPF_JSGE = 0x70 BPF_CALL = 0x80 BPF_EXIT = 0x90 # BPF helper function constants BPF_FUNC_unspec = 0 BPF_FUNC_map_lookup_elem = 1 BPF_FUNC_map_update_elem = 2 BPF_FUNC_map_delete_elem = 3 # BPF attr struct BpfAttrCreate = cstruct.Struct("bpf_attr_create", "=IIII", "map_type key_size value_size max_entries") BpfAttrOps = cstruct.Struct("bpf_attr_ops", "=QQQQ", "map_fd key_ptr value_ptr flags") BpfAttrProgLoad = cstruct.Struct( "bpf_attr_prog_load", "=IIQQIIQI", "prog_type insn_cnt insns" " license log_level log_size log_buf kern_version") BpfInsn = cstruct.Struct("bpf_insn", "=BBhi", "code dst_src_reg off imm") libc = ctypes.CDLL(ctypes.util.find_library("c"), use_errno=True) HAVE_EBPF_SUPPORT = net_test.LINUX_VERSION >= (4, 4, 0) # BPF program syscalls def CreateMap(map_type, key_size, value_size, max_entries): attr = BpfAttrCreate((map_type, key_size, value_size, max_entries)) ret = libc.syscall(__NR_bpf, BPF_MAP_CREATE, attr.CPointer(), len(attr)) csocket.MaybeRaiseSocketError(ret) return ret def UpdateMap(map_fd, key, value, flags=0): c_value = ctypes.c_uint32(value) c_key = ctypes.c_uint32(key) value_ptr = ctypes.addressof(c_value) key_ptr = ctypes.addressof(c_key) attr = BpfAttrOps((map_fd, key_ptr, value_ptr, flags)) ret = libc.syscall(__NR_bpf, BPF_MAP_UPDATE_ELEM, attr.CPointer(), len(attr)) csocket.MaybeRaiseSocketError(ret) def LookupMap(map_fd, key): c_value = ctypes.c_uint32(0) c_key = ctypes.c_uint32(key) attr = BpfAttrOps( (map_fd, ctypes.addressof(c_key), ctypes.addressof(c_value), 0)) ret = libc.syscall(__NR_bpf, BPF_MAP_LOOKUP_ELEM, attr.CPointer(), len(attr)) csocket.MaybeRaiseSocketError(ret) return c_value def GetNextKey(map_fd, key): c_key = ctypes.c_uint32(key) c_next_key = ctypes.c_uint32(0) attr = BpfAttrOps( (map_fd, ctypes.addressof(c_key), ctypes.addressof(c_next_key), 0)) ret = libc.syscall(__NR_bpf, BPF_MAP_GET_NEXT_KEY, attr.CPointer(), len(attr)) csocket.MaybeRaiseSocketError(ret) return c_next_key def DeleteMap(map_fd, key): c_key = ctypes.c_uint32(key) attr = BpfAttrOps((map_fd, ctypes.addressof(c_key), 0, 0)) ret = libc.syscall(__NR_bpf, BPF_MAP_DELETE_ELEM, attr.CPointer(), len(attr)) csocket.MaybeRaiseSocketError(ret) def BpfProgLoad(prog_type, insn_ptr, prog_len, insn_len): gpl_license = ctypes.create_string_buffer(b"GPL") log_buf = ctypes.create_string_buffer(b"", LOG_SIZE) attr = BpfAttrProgLoad( (prog_type, prog_len / insn_len, insn_ptr, ctypes.addressof(gpl_license), LOG_LEVEL, LOG_SIZE, ctypes.addressof(log_buf), 0)) ret = libc.syscall(__NR_bpf, BPF_PROG_LOAD, attr.CPointer(), len(attr)) csocket.MaybeRaiseSocketError(ret) return ret def BpfProgAttach(sock_fd, prog_fd): prog_ptr = ctypes.c_uint32(prog_fd) ret = libc.setsockopt(sock_fd, socket.SOL_SOCKET, SO_ATTACH_BPF, ctypes.addressof(prog_ptr), ctypes.sizeof(prog_ptr)) csocket.MaybeRaiseSocketError(ret) # BPF program command constructors def BpfMov64Reg(dst, src): code = BPF_ALU64 | BPF_MOV | BPF_X dst_src = src << 4 | dst ret = BpfInsn((code, dst_src, 0, 0)) return ret.Pack() def BpfLdxMem(size, dst, src, off): code = BPF_LDX | (size & 0x18) | BPF_MEM dst_src = src << 4 | dst ret = BpfInsn((code, dst_src, off, 0)) return ret.Pack() def BpfStxMem(size, dst, src, off): code = BPF_STX | (size & 0x18) | BPF_MEM dst_src = src << 4 | dst ret = BpfInsn((code, dst_src, off, 0)) return ret.Pack() def BpfStMem(size, dst, off, imm): code = BPF_ST | (size & 0x18) | BPF_MEM dst_src = dst ret = BpfInsn((code, dst_src, off, imm)) return ret.Pack() def BpfAlu64Imm(op, dst, imm): code = BPF_ALU64 | (op & 0xf0) | BPF_K dst_src = dst ret = BpfInsn((code, dst_src, 0, imm)) return ret.Pack() def BpfJumpImm(op, dst, imm, off): code = BPF_JMP | (op & 0xf0) | BPF_K dst_src = dst ret = BpfInsn((code, dst_src, off, imm)) return ret.Pack() def BpfRawInsn(code, dst, src, off, imm): ret = BpfInsn((code, (src << 4 | dst), off, imm)) return ret.Pack() def BpfMov64Imm(dst, imm): code = BPF_ALU64 | BPF_MOV | BPF_K dst_src = dst ret = BpfInsn((code, dst_src, 0, imm)) return ret.Pack() def BpfExitInsn(): code = BPF_JMP | BPF_EXIT ret = BpfInsn((code, 0, 0, 0)) return ret.Pack() def BpfLoadMapFd(map_fd, dst): code = BPF_LD | BPF_DW | BPF_IMM dst_src = BPF_PSEUDO_MAP_FD << 4 | dst insn1 = BpfInsn((code, dst_src, 0, map_fd)) insn2 = BpfInsn((0, 0, 0, map_fd >> 32)) return insn1.Pack() + insn2.Pack() def BpfFuncLookupMap(): code = BPF_JMP | BPF_CALL dst_src = 0 ret = BpfInsn((code, dst_src, 0, BPF_FUNC_map_lookup_elem)) return ret.Pack() def BpfFuncUpdateMap(): code = BPF_JMP | BPF_CALL dst_src = 0 ret = BpfInsn((code, dst_src, 0, BPF_FUNC_map_update_elem)) return ret.Pack() def BpfFuncDeleteMap(): code = BPF_JMP | BPF_CALL dst_src = 0 ret = BpfInsn((code, dst_src, 0, BPF_FUNC_map_delete_elem)) return ret.Pack()