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