Home | History | Annotate | Download | only in test
      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