1 # Copyright 2019 Clevernet
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at
7 # http://www.apache.org/licenses/LICENSE-2.0
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 # See the License for the specific language governing permissions and
13 # limitations under the License.
15 from os import linesep
17 from .table import get_table_type_name
19 class OffsetUnion(ct.Union):
20 _fields_ = [('offsetu', ct.c_uint16), ('offset', ct.c_int16)]
22 class ImmUnion(ct.Union):
23 _fields_ = [('immu', ct.c_uint32), ('imm', ct.c_int32)]
25 class BPFInstrFields(ct.Structure):
27 _anonymous_ = ('o', 'i')
28 _fields_ = [('opcode', ct.c_uint8),
29 ('dst', ct.c_uint8, 4),
30 ('src', ct.c_uint8, 4),
34 class BPFInstr(ct.Union):
37 _fields_ = [('s', BPFInstrFields), ('instr', ct.c_uint64)]
41 bpf_helpers = ['unspec',
49 'get_smp_processor_id',
55 'get_current_pid_tgid',
56 'get_current_uid_gid',
78 'current_task_under_cgroup',
96 'perf_event_read_value',
97 'perf_prog_read_value',
100 'sock_ops_cb_flags_set',
107 'skb_get_xfrm_state',
109 'skb_load_bytes_relative',
115 'lwt_seg6_store_bytes',
116 'lwt_seg6_adjust_srh',
121 'get_current_cgroup_id',
123 'sk_select_reuseport',
124 'skb_ancestor_cgroup_id',
135 opcodes = {0x04: ('add32', 'dstimm', '+=', 32),
136 0x05: ('ja', 'joff', None, 64),
137 0x07: ('add', 'dstimm', '+=', 64),
138 0x0c: ('add32', 'dstsrc', '+=', 32),
139 0x0f: ('add', 'dstsrc', '+=', 64),
140 0x14: ('sub32', 'dstimm', '-=', 32),
141 0x15: ('jeq', 'jdstimmoff', '==', 64),
142 0x17: ('sub', 'dstimm', '-=', 64),
143 0x18: ('lddw', 'lddw', None, 64),
144 0x1c: ('sub32', 'dstsrc', '-=', 32),
145 0x1d: ('jeq', 'jdstsrcoff', '==', 64),
146 0x1f: ('sub', 'dstsrc', '-=', 64),
147 0x20: ('ldabsw', 'ldabs', None, 32),
148 0x24: ('mul32', 'dstimm', '*=', 32),
149 0x25: ('jgt', 'jdstimmoff', '>', 64),
150 0x27: ('mul', 'dstimm', '*=', 64),
151 0x28: ('ldabsh', 'ldabs', None, 16),
152 0x2c: ('mul32', 'dstsrc', '*=', 32),
153 0x2d: ('jgt', 'jdstsrcoff', '>', 64),
154 0x2f: ('mul', 'dstsrc', '*=', 64),
155 0x30: ('ldabsb', 'ldabs', None, 8),
156 0x34: ('div32', 'dstimm', '/=', 32),
157 0x35: ('jge', 'jdstimmoff', '>=', 64),
158 0x37: ('div', 'dstimm', '/=', 64),
159 0x38: ('ldabsdw', 'ldabs', None, 64),
160 0x3c: ('div32', 'dstsrc', '/=', 32),
161 0x3d: ('jge', 'jdstsrcoff', '>=', 64),
162 0x3f: ('div', 'dstsrc', '/=', 64),
163 0x40: ('ldindw', 'ldind', None, 32),
164 0x44: ('or32', 'dstimm_bw', '|=', 32),
165 0x45: ('jset', 'jdstimmoff', '&', 64),
166 0x47: ('or', 'dstimm_bw', '|=', 64),
167 0x48: ('ldindh', 'ldind', None, 16),
168 0x4c: ('or32', 'dstsrc', '|=', 32),
169 0x4d: ('jset', 'jdstsrcoff', '&', 64),
170 0x4f: ('or', 'dstsrc', '|=', 64),
171 0x50: ('ldindb', 'ldind', None, 8),
172 0x54: ('and32', 'dstimm_bw', '&=', 32),
173 0x55: ('jne', 'jdstimmoff', '!=', 64),
174 0x57: ('and', 'dstimm_bw', '&=', 64),
175 0x58: ('ldinddw', 'ldind', None, 64),
176 0x5c: ('and32', 'dstsrc', '&=', 32),
177 0x5d: ('jne', 'jdstsrcoff', '!=', 64),
178 0x5f: ('and', 'dstsrc', '&=', 64),
179 0x61: ('ldxw', 'ldstsrcoff', None, 32),
180 0x62: ('stw', 'sdstoffimm', None, 32),
181 0x63: ('stxw', 'sdstoffsrc', None, 32),
182 0x64: ('lsh32', 'dstimm', '<<=', 32),
183 0x65: ('jsgt', 'jdstimmoff', 's>', 64),
184 0x67: ('lsh', 'dstimm', '<<=', 64),
185 0x69: ('ldxh', 'ldstsrcoff', None, 16),
186 0x6a: ('sth', 'sdstoffimm', None, 16),
187 0x6b: ('stxh', 'sdstoffsrc', None, 16),
188 0x6c: ('lsh32', 'dstsrc', '<<=', 32),
189 0x6d: ('jsgt', 'jdstsrcoff', 's>', 64),
190 0x6f: ('lsh', 'dstsrc', '<<=', 64),
191 0x71: ('ldxb', 'ldstsrcoff', None, 8),
192 0x72: ('stb', 'sdstoffimm', None, 8),
193 0x73: ('stxb', 'sdstoffsrc', None, 8),
194 0x74: ('rsh32', 'dstimm', '>>=', 32),
195 0x75: ('jsge', 'jdstimmoff', 's>=', 64),
196 0x77: ('rsh', 'dstimm', '>>=', 64),
197 0x79: ('ldxdw', 'ldstsrcoff', None, 64),
198 0x7a: ('stdw', 'sdstoffimm', None, 64),
199 0x7b: ('stxdw', 'sdstoffsrc', None, 64),
200 0x7c: ('rsh32', 'dstsrc', '>>=', 32),
201 0x7d: ('jsge', 'jdstsrcoff', 's>=', 64),
202 0x7f: ('rsh', 'dstsrc', '>>=', 64),
203 0x84: ('neg32', 'dst', '~', 32),
204 0x85: ('call', 'call', None, 64),
205 0x87: ('neg', 'dst', '~', 64),
206 0x94: ('mod32', 'dstimm', '%=', 32),
207 0x95: ('exit', 'exit', None, 64),
208 0x97: ('mod', 'dstimm', '%=', 64),
209 0x9c: ('mod32', 'dstsrc', '%=', 32),
210 0x9f: ('mod', 'dstsrc', '%=', 64),
211 0xa4: ('xor32', 'dstimm_bw', '^=', 32),
212 0xa5: ('jlt', 'jdstimmoff', '<', 64),
213 0xa7: ('xor', 'dstimm_bw', '^=', 64),
214 0xac: ('xor32', 'dstsrc', '^=', 32),
215 0xad: ('jlt', 'jdstsrcoff', '<', 64),
216 0xaf: ('xor', 'dstsrc', '^=', 64),
217 0xb4: ('mov32', 'dstimm', '=', 32),
218 0xb5: ('jle', 'jdstimmoff', '<=', 64),
219 0xb7: ('mov', 'dstimm', '=', 64),
220 0xbc: ('mov32', 'dstsrc', '=', 32),
221 0xbd: ('jle', 'jdstsrcoff', '<=', 64),
222 0xbf: ('mov', 'dstsrc', '=', 64),
223 0xc4: ('arsh32', 'dstimm', 's>>=', 32),
224 0xc5: ('jslt', 'jdstimmoff', 's<', 64),
225 0xc7: ('arsh', 'dstimm', 's>>=', 64),
226 0xcc: ('arsh32', 'dstsrc', 's>>=', 32),
227 0xcd: ('jslt', 'jdstsrcoff', 's<', 64),
228 0xcf: ('arsh', 'dstsrc', 's>>=', 64),
229 0xd5: ('jsle', 'jdstimmoff', 's<=', 64),
230 0xdc: ('endian32', 'dstsrc', 'endian', 32),
231 0xdd: ('jsle', 'jdstimmoff', 's<=', 64),}
234 def decode(cls, i, w, w1):
236 name, opclass, op, bits = cls.opcodes[w.opcode]
237 if opclass == 'dstimm':
238 return 'r%d %s %d' % (w.dst, op, w.imm), 0
240 elif opclass == 'dstimm_bw':
241 return 'r%d %s 0x%x' % (w.dst, op, w.immu), 0
243 elif opclass == 'joff':
244 return 'goto %s <%d>' % ('%+d' % (w.offset),
247 elif opclass == 'dstsrc':
248 return 'r%d %s r%d' % (w.dst, op, w.src), 0
250 elif opclass == 'jdstimmoff':
251 return 'if r%d %s %d goto pc%s <%d>' % (w.dst, op, w.imm,
255 elif opclass == 'jdstsrcoff':
256 return 'if r%d %s r%d goto pc%s <%d>' % (w.dst, op, w.src,
260 elif opclass == 'lddw':
261 # imm contains the file descriptor (FD) of the map being loaded;
262 # the kernel will translate this into the proper address
264 raise Exception("lddw requires two instructions to be disassembled")
266 return 'r%d = <map at fd #%d>' % (w.dst, w.imm), 1
267 imm = (w1.imm << 32) | w.imm
268 return 'r%d = 0x%x' % (w.dst, imm), 1
270 elif opclass == 'ldabs':
271 return 'r0 = *(u%s*)skb[%s]' % (bits, w.imm), 0
273 elif opclass == 'ldind':
274 return 'r0 = *(u%d*)skb[r%d %s]' % (bits, w.src,
277 elif opclass == 'ldstsrcoff':
278 return 'r%d = *(u%d*)(r%d %s)' % (w.dst, bits, w.src,
279 '%+d' % (w.offset)), 0
281 elif opclass == 'sdstoffimm':
282 return '*(u%d*)(r%d %s) = %d' % (bits, w.dst,
283 '%+d' % (w.offset), w.imm), 0
285 elif opclass == 'sdstoffsrc':
286 return '*(u%d*)(r%d %s) = r%d' % (bits, w.dst,
287 '%+d' % (w.offset), w.src), 0
289 elif opclass == 'dst':
290 return 'r%d = %s (u%s)r%d' % (w.dst, op, bits, w.dst), 0
292 elif opclass == 'call':
293 if w.src != cls.BPF_PSEUDO_CALL:
295 return '%s bpf_%s#%d' % (name, cls.bpf_helpers[w.immu], w.immu), 0
297 return '%s <unknown helper #%d>' % (op, w.immu), 0
298 return '%s %s' % (name, '%+d' % (w.imm)), 0
299 elif opclass == 'exit':
302 raise Exception('unknown opcode class')
305 return 'unknown <0x%x>' % (w.opcode)
307 def disassemble_instruction(i, w0, w1=None):
308 instr, skip = BPFDecoder.decode(i, w0, w1)
309 return "%4d: (%02x) %s" % (i, w0.opcode, instr), skip
311 def disassemble_str(bpfstr):
312 ptr = ct.cast(ct.c_char_p(bpfstr), ct.POINTER(BPFInstr))
313 numinstr = int(len(bpfstr) / 8)
317 for i in range(1, numinstr):
321 instr_str = "%4d: (64-bit upper word)" % (i)
323 instr_str, skip = disassemble_instruction(i - 1, w0, w1)
324 instr_list.append(instr_str)
326 instr_str, skip = disassemble_instruction(numinstr - 1, w0, None)
327 instr_list.append(instr_str)
330 def disassemble_prog(func_name, bpfstr):
331 instr_list = ["Disassemble of BPF program %s:" % (func_name)]
332 instr_list += disassemble_str(bpfstr)
333 return linesep.join(instr_list)
336 ctype2str = {ct.c_bool: u"_Bool",
338 ct.c_wchar: u"wchar_t",
339 ct.c_ubyte: u"unsigned char",
340 ct.c_short: u"short",
341 ct.c_ushort: u"unsigned short",
343 ct.c_uint: u"unsigned int",
345 ct.c_ulong: u"unsigned long",
346 ct.c_longlong: u"long long",
347 ct.c_ulonglong: u"unsigned long long",
348 ct.c_float: u"float",
349 ct.c_double: u"double",
350 ct.c_longdouble: u"long double",
351 ct.c_int64 * 2: u"__int128",
352 ct.c_uint64 * 2: u"unsigned __int128",}
355 def get_ct_name(cls, t):
357 if issubclass(t, ct.Structure):
358 field_type_name = "struct"
359 elif issubclass(t, ct.Union):
360 field_type_name = "union"
361 elif issubclass(t, ct.Array):
362 field_type_name = cls.ctype2str[t._type_] + "[" + str(t._length_) + "]"
364 field_type_name = cls.ctype2str[t]
366 field_type_name = str(t)
367 return field_type_name
370 def format_size_info(cls, offset, size, enabled=False, bitoffset=None):
373 if bitoffset is not None:
374 return "[%d,%d +%d bit]" % (offset, bitoffset, size)
375 return "[%d +%d] " % (offset, size)
378 def print_ct_map(cls, t, indent="", offset=0, sizeinfo=False):
381 for field_name, field_type in t._fields_:
382 is_structured = (issubclass(field_type, ct.Structure) or
383 issubclass(field_type, ct.Union))
384 field_type_name = cls.get_ct_name(field_type)
385 field_offset = getattr(t, field_name).offset
386 field_size = ct.sizeof(field_type)
387 sizedesc = cls.format_size_info(offset + field_offset,
388 field_size, sizeinfo)
390 map_lines.append("%s%s%s {" % (indent, sizedesc, field_type_name))
391 map_lines += cls.print_ct_map(field_type,
393 offset + field_offset)
394 map_lines.append("%s} %s;" % (indent, field_name))
396 map_lines.append("%s%s%s %s;" % (indent, sizedesc,
402 for field in t._fields_:
404 field_name, field_type, field_bits = field
405 field_type_name = cls.get_ct_name(field_type)
406 sizedesc = cls.format_size_info(offset, offset_bits,
407 sizeinfo, field_bits)
408 map_lines.append("%s%s%s %s:%d;" % (indent, sizedesc,
413 # end of previous bit field
414 field_name, field_type = field
415 field_type_name = cls.get_ct_name(field_type)
416 field_offset = getattr(t, field_name).offset
417 field_size = ct.sizeof(field_type)
420 sizedesc = cls.format_size_info(offset + field_offset,
421 field_size, sizeinfo)
422 map_lines.append("%s%s%s %s;" % (indent, sizedesc,
425 offset += field_offset
426 offset_bits += field_bits
430 def print_map_ctype(cls, t, field_name, sizeinfo):
431 is_structured = (issubclass(t, ct.Structure) or
432 issubclass(t, ct.Union))
433 type_name = cls.get_ct_name(t)
435 map_lines = [" %s {" % (type_name)]
436 map_lines += cls.print_ct_map(t, " ", sizeinfo=sizeinfo)
437 map_lines.append(" } %s;" % (field_name))
439 map_lines = [" %s %s;" % (type_name, field_name)]
443 def decode_map(cls, map_name, map_obj, map_type, sizeinfo=False):
444 map_lines = ['Layout of BPF map %s (type %s, FD %d, ID %d):' % (map_name,
448 map_lines += cls.print_map_ctype(map_obj.Key, 'key', sizeinfo=sizeinfo)
449 map_lines += cls.print_map_ctype(map_obj.Leaf, 'value', sizeinfo=sizeinfo)
450 return linesep.join(map_lines)
452 def decode_map(map_name, map_obj, map_type, sizeinfo=False):
453 map_type_name = get_table_type_name(map_type)
454 return MapDecoder.decode_map(map_name, map_obj, map_type_name, sizeinfo=sizeinfo)