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