• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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