Linux kernel mirror (for testing)
git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel
os
linux
1#!/usr/bin/env python3
2# SPDX-License-Identifier: GPL-2.0-only
3#
4# Copyright (C) 2018-2019 Netronome Systems, Inc.
5# Copyright (C) 2021 Isovalent, Inc.
6
7# In case user attempts to run with Python 2.
8from __future__ import print_function
9
10import argparse
11import json
12import re
13import sys, os
14import subprocess
15
16helpersDocStart = 'Start of BPF helper function descriptions:'
17
18class NoHelperFound(BaseException):
19 pass
20
21class NoSyscallCommandFound(BaseException):
22 pass
23
24class ParsingError(BaseException):
25 def __init__(self, line='<line not provided>', reader=None):
26 if reader:
27 BaseException.__init__(self,
28 'Error at file offset %d, parsing line: %s' %
29 (reader.tell(), line))
30 else:
31 BaseException.__init__(self, 'Error parsing line: %s' % line)
32
33
34class APIElement(object):
35 """
36 An object representing the description of an aspect of the eBPF API.
37 @proto: prototype of the API symbol
38 @desc: textual description of the symbol
39 @ret: (optional) description of any associated return value
40 """
41 def __init__(self, proto='', desc='', ret=''):
42 self.proto = proto
43 self.desc = desc
44 self.ret = ret
45
46 def to_dict(self):
47 return {
48 'proto': self.proto,
49 'desc': self.desc,
50 'ret': self.ret
51 }
52
53
54class Helper(APIElement):
55 """
56 An object representing the description of an eBPF helper function.
57 @proto: function prototype of the helper function
58 @desc: textual description of the helper function
59 @ret: description of the return value of the helper function
60 """
61 def __init__(self, proto='', desc='', ret='', attrs=[]):
62 super().__init__(proto, desc, ret)
63 self.attrs = attrs
64 self.enum_val = None
65
66 def proto_break_down(self):
67 """
68 Break down helper function protocol into smaller chunks: return type,
69 name, distincts arguments.
70 """
71 arg_re = re.compile(r'((\w+ )*?(\w+|...))( (\**)(\w+))?$')
72 res = {}
73 proto_re = re.compile(r'(.+) (\**)(\w+)\(((([^,]+)(, )?){1,5})\)$')
74
75 capture = proto_re.match(self.proto)
76 res['ret_type'] = capture.group(1)
77 res['ret_star'] = capture.group(2)
78 res['name'] = capture.group(3)
79 res['args'] = []
80
81 args = capture.group(4).split(', ')
82 for a in args:
83 capture = arg_re.match(a)
84 res['args'].append({
85 'type' : capture.group(1),
86 'star' : capture.group(5),
87 'name' : capture.group(6)
88 })
89
90 return res
91
92 def to_dict(self):
93 d = super().to_dict()
94 d["attrs"] = self.attrs
95 d.update(self.proto_break_down())
96 return d
97
98
99ATTRS = {
100 '__bpf_fastcall': 'bpf_fastcall'
101}
102
103
104class HeaderParser(object):
105 """
106 An object used to parse a file in order to extract the documentation of a
107 list of eBPF helper functions. All the helpers that can be retrieved are
108 stored as Helper object, in the self.helpers() array.
109 @filename: name of file to parse, usually include/uapi/linux/bpf.h in the
110 kernel tree
111 """
112 def __init__(self, filename):
113 self.reader = open(filename, 'r')
114 self.line = ''
115 self.helpers = []
116 self.commands = []
117 self.desc_unique_helpers = set()
118 self.define_unique_helpers = []
119 self.helper_enum_vals = {}
120 self.helper_enum_pos = {}
121 self.desc_syscalls = []
122 self.enum_syscalls = []
123
124 def parse_element(self):
125 proto = self.parse_symbol()
126 desc = self.parse_desc(proto)
127 ret = self.parse_ret(proto)
128 return APIElement(proto=proto, desc=desc, ret=ret)
129
130 def parse_helper(self):
131 proto = self.parse_proto()
132 desc = self.parse_desc(proto)
133 ret = self.parse_ret(proto)
134 attrs = self.parse_attrs(proto)
135 return Helper(proto=proto, desc=desc, ret=ret, attrs=attrs)
136
137 def parse_symbol(self):
138 p = re.compile(r' \* ?(BPF\w+)$')
139 capture = p.match(self.line)
140 if not capture:
141 raise NoSyscallCommandFound
142 end_re = re.compile(r' \* ?NOTES$')
143 end = end_re.match(self.line)
144 if end:
145 raise NoSyscallCommandFound
146 self.line = self.reader.readline()
147 return capture.group(1)
148
149 def parse_proto(self):
150 # Argument can be of shape:
151 # - "void"
152 # - "type name"
153 # - "type *name"
154 # - Same as above, with "const" and/or "struct" in front of type
155 # - "..." (undefined number of arguments, for bpf_trace_printk())
156 # There is at least one term ("void"), and at most five arguments.
157 p = re.compile(r' \* ?((.+) \**\w+\((((const )?(struct )?(\w+|\.\.\.)( \**\w+)?)(, )?){1,5}\))$')
158 capture = p.match(self.line)
159 if not capture:
160 raise NoHelperFound
161 self.line = self.reader.readline()
162 return capture.group(1)
163
164 def parse_desc(self, proto):
165 p = re.compile(r' \* ?(?:\t| {5,8})Description$')
166 capture = p.match(self.line)
167 if not capture:
168 raise Exception("No description section found for " + proto)
169 # Description can be several lines, some of them possibly empty, and it
170 # stops when another subsection title is met.
171 desc = ''
172 desc_present = False
173 while True:
174 self.line = self.reader.readline()
175 if self.line == ' *\n':
176 desc += '\n'
177 else:
178 p = re.compile(r' \* ?(?:\t| {5,8})(?:\t| {8})(.*)')
179 capture = p.match(self.line)
180 if capture:
181 desc_present = True
182 desc += capture.group(1) + '\n'
183 else:
184 break
185
186 if not desc_present:
187 raise Exception("No description found for " + proto)
188 return desc
189
190 def parse_ret(self, proto):
191 p = re.compile(r' \* ?(?:\t| {5,8})Return$')
192 capture = p.match(self.line)
193 if not capture:
194 raise Exception("No return section found for " + proto)
195 # Return value description can be several lines, some of them possibly
196 # empty, and it stops when another subsection title is met.
197 ret = ''
198 ret_present = False
199 while True:
200 self.line = self.reader.readline()
201 if self.line == ' *\n':
202 ret += '\n'
203 else:
204 p = re.compile(r' \* ?(?:\t| {5,8})(?:\t| {8})(.*)')
205 capture = p.match(self.line)
206 if capture:
207 ret_present = True
208 ret += capture.group(1) + '\n'
209 else:
210 break
211
212 if not ret_present:
213 raise Exception("No return found for " + proto)
214 return ret
215
216 def parse_attrs(self, proto):
217 p = re.compile(r' \* ?(?:\t| {5,8})Attributes$')
218 capture = p.match(self.line)
219 if not capture:
220 return []
221 # Expect a single line with mnemonics for attributes separated by spaces
222 self.line = self.reader.readline()
223 p = re.compile(r' \* ?(?:\t| {5,8})(?:\t| {8})(.*)')
224 capture = p.match(self.line)
225 if not capture:
226 raise Exception("Incomplete 'Attributes' section for " + proto)
227 attrs = capture.group(1).split(' ')
228 for attr in attrs:
229 if attr not in ATTRS:
230 raise Exception("Unexpected attribute '" + attr + "' specified for " + proto)
231 self.line = self.reader.readline()
232 if self.line != ' *\n':
233 raise Exception("Expecting empty line after 'Attributes' section for " + proto)
234 # Prepare a line for next self.parse_* to consume
235 self.line = self.reader.readline()
236 return attrs
237
238 def seek_to(self, target, help_message, discard_lines = 1):
239 self.reader.seek(0)
240 offset = self.reader.read().find(target)
241 if offset == -1:
242 raise Exception(help_message)
243 self.reader.seek(offset)
244 self.reader.readline()
245 for _ in range(discard_lines):
246 self.reader.readline()
247 self.line = self.reader.readline()
248
249 def parse_desc_syscall(self):
250 self.seek_to('* DOC: eBPF Syscall Commands',
251 'Could not find start of eBPF syscall descriptions list')
252 while True:
253 try:
254 command = self.parse_element()
255 self.commands.append(command)
256 self.desc_syscalls.append(command.proto)
257
258 except NoSyscallCommandFound:
259 break
260
261 def parse_enum_syscall(self):
262 self.seek_to('enum bpf_cmd {',
263 'Could not find start of bpf_cmd enum', 0)
264 # Searches for either one or more BPF\w+ enums
265 bpf_p = re.compile(r'\s*(BPF\w+)+')
266 # Searches for an enum entry assigned to another entry,
267 # for e.g. BPF_PROG_RUN = BPF_PROG_TEST_RUN, which is
268 # not documented hence should be skipped in check to
269 # determine if the right number of syscalls are documented
270 assign_p = re.compile(r'\s*(BPF\w+)\s*=\s*(BPF\w+)')
271 bpf_cmd_str = ''
272 while True:
273 capture = assign_p.match(self.line)
274 if capture:
275 # Skip line if an enum entry is assigned to another entry
276 self.line = self.reader.readline()
277 continue
278 capture = bpf_p.match(self.line)
279 if capture:
280 bpf_cmd_str += self.line
281 else:
282 break
283 self.line = self.reader.readline()
284 # Find the number of occurences of BPF\w+
285 self.enum_syscalls = re.findall(r'(BPF\w+)+', bpf_cmd_str)
286
287 def parse_desc_helpers(self):
288 self.seek_to(helpersDocStart,
289 'Could not find start of eBPF helper descriptions list')
290 while True:
291 try:
292 helper = self.parse_helper()
293 self.helpers.append(helper)
294 proto = helper.proto_break_down()
295 self.desc_unique_helpers.add(proto['name'])
296 except NoHelperFound:
297 break
298
299 def parse_define_helpers(self):
300 # Parse FN(...) in #define ___BPF_FUNC_MAPPER to compare later with the
301 # number of unique function names present in description and use the
302 # correct enumeration value.
303 # Note: seek_to(..) discards the first line below the target search text,
304 # resulting in FN(unspec, 0, ##ctx) being skipped and not added to
305 # self.define_unique_helpers.
306 self.seek_to('#define ___BPF_FUNC_MAPPER(FN, ctx...)',
307 'Could not find start of eBPF helper definition list')
308 # Searches for one FN(\w+) define or a backslash for newline
309 p = re.compile(r'\s*FN\((\w+), (\d+), ##ctx\)|\\\\')
310 fn_defines_str = ''
311 i = 0
312 while True:
313 capture = p.match(self.line)
314 if capture:
315 fn_defines_str += self.line
316 helper_name = capture.expand(r'bpf_\1')
317 self.helper_enum_vals[helper_name] = int(capture.group(2))
318 self.helper_enum_pos[helper_name] = i
319 i += 1
320 else:
321 break
322 self.line = self.reader.readline()
323 # Find the number of occurences of FN(\w+)
324 self.define_unique_helpers = re.findall(r'FN\(\w+, \d+, ##ctx\)', fn_defines_str)
325
326 def validate_helpers(self):
327 last_helper = ''
328 seen_helpers = set()
329 seen_enum_vals = set()
330 i = 0
331 for helper in self.helpers:
332 proto = helper.proto_break_down()
333 name = proto['name']
334 try:
335 enum_val = self.helper_enum_vals[name]
336 enum_pos = self.helper_enum_pos[name]
337 except KeyError:
338 raise Exception("Helper %s is missing from enum bpf_func_id" % name)
339
340 if name in seen_helpers:
341 if last_helper != name:
342 raise Exception("Helper %s has multiple descriptions which are not grouped together" % name)
343 continue
344
345 # Enforce current practice of having the descriptions ordered
346 # by enum value.
347 if enum_pos != i:
348 raise Exception("Helper %s (ID %d) comment order (#%d) must be aligned with its position (#%d) in enum bpf_func_id" % (name, enum_val, i + 1, enum_pos + 1))
349 if enum_val in seen_enum_vals:
350 raise Exception("Helper %s has duplicated value %d" % (name, enum_val))
351
352 seen_helpers.add(name)
353 last_helper = name
354 seen_enum_vals.add(enum_val)
355
356 helper.enum_val = enum_val
357 i += 1
358
359 def run(self):
360 self.parse_desc_syscall()
361 self.parse_enum_syscall()
362 self.parse_desc_helpers()
363 self.parse_define_helpers()
364 self.validate_helpers()
365 self.reader.close()
366
367###############################################################################
368
369class Printer(object):
370 """
371 A generic class for printers. Printers should be created with an array of
372 Helper objects, and implement a way to print them in the desired fashion.
373 @parser: A HeaderParser with objects to print to standard output
374 """
375 def __init__(self, parser):
376 self.parser = parser
377 self.elements = []
378
379 def print_header(self):
380 pass
381
382 def print_footer(self):
383 pass
384
385 def print_one(self, helper):
386 pass
387
388 def print_all(self):
389 self.print_header()
390 for elem in self.elements:
391 self.print_one(elem)
392 self.print_footer()
393
394 def elem_number_check(self, desc_unique_elem, define_unique_elem, type, instance):
395 """
396 Checks the number of helpers/syscalls documented within the header file
397 description with those defined as part of enum/macro and raise an
398 Exception if they don't match.
399 """
400 nr_desc_unique_elem = len(desc_unique_elem)
401 nr_define_unique_elem = len(define_unique_elem)
402 if nr_desc_unique_elem != nr_define_unique_elem:
403 exception_msg = '''
404The number of unique %s in description (%d) doesn\'t match the number of unique %s defined in %s (%d)
405''' % (type, nr_desc_unique_elem, type, instance, nr_define_unique_elem)
406 if nr_desc_unique_elem < nr_define_unique_elem:
407 # Function description is parsed until no helper is found (which can be due to
408 # misformatting). Hence, only print the first missing/misformatted helper/enum.
409 exception_msg += '''
410The description for %s is not present or formatted correctly.
411''' % (define_unique_elem[nr_desc_unique_elem])
412 raise Exception(exception_msg)
413
414class PrinterRST(Printer):
415 """
416 A generic class for printers that print ReStructured Text. Printers should
417 be created with a HeaderParser object, and implement a way to print API
418 elements in the desired fashion.
419 @parser: A HeaderParser with objects to print to standard output
420 """
421 def __init__(self, parser):
422 self.parser = parser
423
424 def print_license(self):
425 license = '''\
426.. Copyright (C) All BPF authors and contributors from 2014 to present.
427.. See git log include/uapi/linux/bpf.h in kernel tree for details.
428..
429.. SPDX-License-Identifier: Linux-man-pages-copyleft
430..
431.. Please do not edit this file. It was generated from the documentation
432.. located in file include/uapi/linux/bpf.h of the Linux kernel sources
433.. (helpers description), and from scripts/bpf_doc.py in the same
434.. repository (header and footer).
435'''
436 print(license)
437
438 def print_elem(self, elem):
439 if (elem.desc):
440 print('\tDescription')
441 # Do not strip all newline characters: formatted code at the end of
442 # a section must be followed by a blank line.
443 for line in re.sub('\n$', '', elem.desc, count=1).split('\n'):
444 print('{}{}'.format('\t\t' if line else '', line))
445
446 if (elem.ret):
447 print('\tReturn')
448 for line in elem.ret.rstrip().split('\n'):
449 print('{}{}'.format('\t\t' if line else '', line))
450
451 print('')
452
453 def get_kernel_version(self):
454 try:
455 version = subprocess.run(['git', 'describe'], cwd=linuxRoot,
456 capture_output=True, check=True)
457 version = version.stdout.decode().rstrip()
458 except:
459 try:
460 version = subprocess.run(['make', '-s', '--no-print-directory', 'kernelversion'],
461 cwd=linuxRoot, capture_output=True, check=True)
462 version = version.stdout.decode().rstrip()
463 except:
464 return 'Linux'
465 return 'Linux {version}'.format(version=version)
466
467 def get_last_doc_update(self, delimiter):
468 try:
469 cmd = ['git', 'log', '-1', '--pretty=format:%cs', '--no-patch',
470 '-L',
471 '/{}/,/\\*\\//:include/uapi/linux/bpf.h'.format(delimiter)]
472 date = subprocess.run(cmd, cwd=linuxRoot,
473 capture_output=True, check=True)
474 return date.stdout.decode().rstrip()
475 except:
476 return ''
477
478class PrinterHelpersRST(PrinterRST):
479 """
480 A printer for dumping collected information about helpers as a ReStructured
481 Text page compatible with the rst2man program, which can be used to
482 generate a manual page for the helpers.
483 @parser: A HeaderParser with Helper objects to print to standard output
484 """
485 def __init__(self, parser):
486 self.elements = parser.helpers
487 self.elem_number_check(parser.desc_unique_helpers, parser.define_unique_helpers, 'helper', '___BPF_FUNC_MAPPER')
488
489 def print_header(self):
490 header = '''\
491===========
492BPF-HELPERS
493===========
494-------------------------------------------------------------------------------
495list of eBPF helper functions
496-------------------------------------------------------------------------------
497
498:Manual section: 7
499:Version: {version}
500{date_field}{date}
501
502DESCRIPTION
503===========
504
505The extended Berkeley Packet Filter (eBPF) subsystem consists in programs
506written in a pseudo-assembly language, then attached to one of the several
507kernel hooks and run in reaction of specific events. This framework differs
508from the older, "classic" BPF (or "cBPF") in several aspects, one of them being
509the ability to call special functions (or "helpers") from within a program.
510These functions are restricted to a white-list of helpers defined in the
511kernel.
512
513These helpers are used by eBPF programs to interact with the system, or with
514the context in which they work. For instance, they can be used to print
515debugging messages, to get the time since the system was booted, to interact
516with eBPF maps, or to manipulate network packets. Since there are several eBPF
517program types, and that they do not run in the same context, each program type
518can only call a subset of those helpers.
519
520Due to eBPF conventions, a helper can not have more than five arguments.
521
522Internally, eBPF programs call directly into the compiled helper functions
523without requiring any foreign-function interface. As a result, calling helpers
524introduces no overhead, thus offering excellent performance.
525
526This document is an attempt to list and document the helpers available to eBPF
527developers. They are sorted by chronological order (the oldest helpers in the
528kernel at the top).
529
530HELPERS
531=======
532'''
533 kernelVersion = self.get_kernel_version()
534 lastUpdate = self.get_last_doc_update(helpersDocStart)
535
536 PrinterRST.print_license(self)
537 print(header.format(version=kernelVersion,
538 date_field = ':Date: ' if lastUpdate else '',
539 date=lastUpdate))
540
541 def print_footer(self):
542 footer = '''
543EXAMPLES
544========
545
546Example usage for most of the eBPF helpers listed in this manual page are
547available within the Linux kernel sources, at the following locations:
548
549* *samples/bpf/*
550* *tools/testing/selftests/bpf/*
551
552LICENSE
553=======
554
555eBPF programs can have an associated license, passed along with the bytecode
556instructions to the kernel when the programs are loaded. The format for that
557string is identical to the one in use for kernel modules (Dual licenses, such
558as "Dual BSD/GPL", may be used). Some helper functions are only accessible to
559programs that are compatible with the GNU General Public License (GNU GPL).
560
561In order to use such helpers, the eBPF program must be loaded with the correct
562license string passed (via **attr**) to the **bpf**\\ () system call, and this
563generally translates into the C source code of the program containing a line
564similar to the following:
565
566::
567
568 char ____license[] __attribute__((section("license"), used)) = "GPL";
569
570IMPLEMENTATION
571==============
572
573This manual page is an effort to document the existing eBPF helper functions.
574But as of this writing, the BPF sub-system is under heavy development. New eBPF
575program or map types are added, along with new helper functions. Some helpers
576are occasionally made available for additional program types. So in spite of
577the efforts of the community, this page might not be up-to-date. If you want to
578check by yourself what helper functions exist in your kernel, or what types of
579programs they can support, here are some files among the kernel tree that you
580may be interested in:
581
582* *include/uapi/linux/bpf.h* is the main BPF header. It contains the full list
583 of all helper functions, as well as many other BPF definitions including most
584 of the flags, structs or constants used by the helpers.
585* *net/core/filter.c* contains the definition of most network-related helper
586 functions, and the list of program types from which they can be used.
587* *kernel/trace/bpf_trace.c* is the equivalent for most tracing program-related
588 helpers.
589* *kernel/bpf/verifier.c* contains the functions used to check that valid types
590 of eBPF maps are used with a given helper function.
591* *kernel/bpf/* directory contains other files in which additional helpers are
592 defined (for cgroups, sockmaps, etc.).
593* The bpftool utility can be used to probe the availability of helper functions
594 on the system (as well as supported program and map types, and a number of
595 other parameters). To do so, run **bpftool feature probe** (see
596 **bpftool-feature**\\ (8) for details). Add the **unprivileged** keyword to
597 list features available to unprivileged users.
598
599Compatibility between helper functions and program types can generally be found
600in the files where helper functions are defined. Look for the **struct
601bpf_func_proto** objects and for functions returning them: these functions
602contain a list of helpers that a given program type can call. Note that the
603**default:** label of the **switch ... case** used to filter helpers can call
604other functions, themselves allowing access to additional helpers. The
605requirement for GPL license is also in those **struct bpf_func_proto**.
606
607Compatibility between helper functions and map types can be found in the
608**check_map_func_compatibility**\\ () function in file *kernel/bpf/verifier.c*.
609
610Helper functions that invalidate the checks on **data** and **data_end**
611pointers for network processing are listed in function
612**bpf_helper_changes_pkt_data**\\ () in file *net/core/filter.c*.
613
614SEE ALSO
615========
616
617**bpf**\\ (2),
618**bpftool**\\ (8),
619**cgroups**\\ (7),
620**ip**\\ (8),
621**perf_event_open**\\ (2),
622**sendmsg**\\ (2),
623**socket**\\ (7),
624**tc-bpf**\\ (8)'''
625 print(footer)
626
627 def print_proto(self, helper):
628 """
629 Format function protocol with bold and italics markers. This makes RST
630 file less readable, but gives nice results in the manual page.
631 """
632 proto = helper.proto_break_down()
633
634 print('**%s %s%s(' % (proto['ret_type'],
635 proto['ret_star'].replace('*', '\\*'),
636 proto['name']),
637 end='')
638
639 comma = ''
640 for a in proto['args']:
641 one_arg = '{}{}'.format(comma, a['type'])
642 if a['name']:
643 if a['star']:
644 one_arg += ' {}**\\ '.format(a['star'].replace('*', '\\*'))
645 else:
646 one_arg += '** '
647 one_arg += '*{}*\\ **'.format(a['name'])
648 comma = ', '
649 print(one_arg, end='')
650
651 print(')**')
652
653 def print_one(self, helper):
654 self.print_proto(helper)
655 self.print_elem(helper)
656
657
658class PrinterSyscallRST(PrinterRST):
659 """
660 A printer for dumping collected information about the syscall API as a
661 ReStructured Text page compatible with the rst2man program, which can be
662 used to generate a manual page for the syscall.
663 @parser: A HeaderParser with APIElement objects to print to standard
664 output
665 """
666 def __init__(self, parser):
667 self.elements = parser.commands
668 self.elem_number_check(parser.desc_syscalls, parser.enum_syscalls, 'syscall', 'bpf_cmd')
669
670 def print_header(self):
671 header = '''\
672===
673bpf
674===
675-------------------------------------------------------------------------------
676Perform a command on an extended BPF object
677-------------------------------------------------------------------------------
678
679:Manual section: 2
680
681COMMANDS
682========
683'''
684 PrinterRST.print_license(self)
685 print(header)
686
687 def print_one(self, command):
688 print('**%s**' % (command.proto))
689 self.print_elem(command)
690
691
692class PrinterHelpersHeader(Printer):
693 """
694 A printer for dumping collected information about helpers as C header to
695 be included from BPF program.
696 @parser: A HeaderParser with Helper objects to print to standard output
697 """
698 def __init__(self, parser):
699 self.elements = parser.helpers
700 self.elem_number_check(parser.desc_unique_helpers, parser.define_unique_helpers, 'helper', '___BPF_FUNC_MAPPER')
701
702 type_fwds = [
703 'struct bpf_fib_lookup',
704 'struct bpf_sk_lookup',
705 'struct bpf_perf_event_data',
706 'struct bpf_perf_event_value',
707 'struct bpf_pidns_info',
708 'struct bpf_redir_neigh',
709 'struct bpf_sock',
710 'struct bpf_sock_addr',
711 'struct bpf_sock_ops',
712 'struct bpf_sock_tuple',
713 'struct bpf_spin_lock',
714 'struct bpf_sysctl',
715 'struct bpf_tcp_sock',
716 'struct bpf_tunnel_key',
717 'struct bpf_xfrm_state',
718 'struct linux_binprm',
719 'struct pt_regs',
720 'struct sk_reuseport_md',
721 'struct sockaddr',
722 'struct tcphdr',
723 'struct seq_file',
724 'struct tcp6_sock',
725 'struct tcp_sock',
726 'struct tcp_timewait_sock',
727 'struct tcp_request_sock',
728 'struct udp6_sock',
729 'struct unix_sock',
730 'struct task_struct',
731 'struct cgroup',
732
733 'struct __sk_buff',
734 'struct sk_msg_md',
735 'struct xdp_md',
736 'struct path',
737 'struct btf_ptr',
738 'struct inode',
739 'struct socket',
740 'struct file',
741 'struct bpf_timer',
742 'struct mptcp_sock',
743 'struct bpf_dynptr',
744 'struct iphdr',
745 'struct ipv6hdr',
746 ]
747 known_types = {
748 '...',
749 'void',
750 'const void',
751 'char',
752 'const char',
753 'int',
754 'long',
755 'unsigned long',
756
757 '__be16',
758 '__be32',
759 '__wsum',
760
761 'struct bpf_fib_lookup',
762 'struct bpf_perf_event_data',
763 'struct bpf_perf_event_value',
764 'struct bpf_pidns_info',
765 'struct bpf_redir_neigh',
766 'struct bpf_sk_lookup',
767 'struct bpf_sock',
768 'struct bpf_sock_addr',
769 'struct bpf_sock_ops',
770 'struct bpf_sock_tuple',
771 'struct bpf_spin_lock',
772 'struct bpf_sysctl',
773 'struct bpf_tcp_sock',
774 'struct bpf_tunnel_key',
775 'struct bpf_xfrm_state',
776 'struct linux_binprm',
777 'struct pt_regs',
778 'struct sk_reuseport_md',
779 'struct sockaddr',
780 'struct tcphdr',
781 'struct seq_file',
782 'struct tcp6_sock',
783 'struct tcp_sock',
784 'struct tcp_timewait_sock',
785 'struct tcp_request_sock',
786 'struct udp6_sock',
787 'struct unix_sock',
788 'struct task_struct',
789 'struct cgroup',
790 'struct path',
791 'const struct path',
792 'struct btf_ptr',
793 'struct inode',
794 'struct socket',
795 'struct file',
796 'struct bpf_timer',
797 'struct mptcp_sock',
798 'struct bpf_dynptr',
799 'const struct bpf_dynptr',
800 'struct iphdr',
801 'struct ipv6hdr',
802 }
803 mapped_types = {
804 'u8': '__u8',
805 'u16': '__u16',
806 'u32': '__u32',
807 'u64': '__u64',
808 's8': '__s8',
809 's16': '__s16',
810 's32': '__s32',
811 's64': '__s64',
812 'size_t': 'unsigned long',
813 'struct bpf_map': 'void',
814 'struct sk_buff': 'struct __sk_buff',
815 'const struct sk_buff': 'const struct __sk_buff',
816 'struct sk_msg_buff': 'struct sk_msg_md',
817 'struct xdp_buff': 'struct xdp_md',
818 }
819 # Helpers overloaded for different context types.
820 overloaded_helpers = [
821 'bpf_get_socket_cookie',
822 'bpf_sk_assign',
823 ]
824
825 def print_header(self):
826 header = '''\
827/* This is auto-generated file. See bpf_doc.py for details. */
828
829/* Forward declarations of BPF structs */'''
830
831 print(header)
832 for fwd in self.type_fwds:
833 print('%s;' % fwd)
834 print('')
835
836 used_attrs = set()
837 for helper in self.elements:
838 for attr in helper.attrs:
839 used_attrs.add(attr)
840 for attr in sorted(used_attrs):
841 print('#ifndef %s' % attr)
842 print('#if __has_attribute(%s)' % ATTRS[attr])
843 print('#define %s __attribute__((%s))' % (attr, ATTRS[attr]))
844 print('#else')
845 print('#define %s' % attr)
846 print('#endif')
847 print('#endif')
848 if used_attrs:
849 print('')
850
851 def print_footer(self):
852 footer = ''
853 print(footer)
854
855 def map_type(self, t):
856 if t in self.known_types:
857 return t
858 if t in self.mapped_types:
859 return self.mapped_types[t]
860 print("Unrecognized type '%s', please add it to known types!" % t,
861 file=sys.stderr)
862 sys.exit(1)
863
864 seen_helpers = set()
865
866 def print_one(self, helper):
867 proto = helper.proto_break_down()
868
869 if proto['name'] in self.seen_helpers:
870 return
871 self.seen_helpers.add(proto['name'])
872
873 print('/*')
874 print(" * %s" % proto['name'])
875 print(" *")
876 if (helper.desc):
877 # Do not strip all newline characters: formatted code at the end of
878 # a section must be followed by a blank line.
879 for line in re.sub('\n$', '', helper.desc, count=1).split('\n'):
880 print(' *{}{}'.format(' \t' if line else '', line))
881
882 if (helper.ret):
883 print(' *')
884 print(' * Returns')
885 for line in helper.ret.rstrip().split('\n'):
886 print(' *{}{}'.format(' \t' if line else '', line))
887
888 print(' */')
889 print('static ', end='')
890 if helper.attrs:
891 print('%s ' % (" ".join(helper.attrs)), end='')
892 print('%s %s(* const %s)(' % (self.map_type(proto['ret_type']),
893 proto['ret_star'], proto['name']), end='')
894 comma = ''
895 for i, a in enumerate(proto['args']):
896 t = a['type']
897 n = a['name']
898 if proto['name'] in self.overloaded_helpers and i == 0:
899 t = 'void'
900 n = 'ctx'
901 one_arg = '{}{}'.format(comma, self.map_type(t))
902 if n:
903 if a['star']:
904 one_arg += ' {}'.format(a['star'])
905 else:
906 one_arg += ' '
907 one_arg += '{}'.format(n)
908 comma = ', '
909 print(one_arg, end='')
910
911 print(') = (void *) %d;' % helper.enum_val)
912 print('')
913
914
915class PrinterHelpersJSON(Printer):
916 """
917 A printer for dumping collected information about helpers as a JSON file.
918 @parser: A HeaderParser with Helper objects
919 """
920
921 def __init__(self, parser):
922 self.elements = parser.helpers
923 self.elem_number_check(
924 parser.desc_unique_helpers,
925 parser.define_unique_helpers,
926 "helper",
927 "___BPF_FUNC_MAPPER",
928 )
929
930 def print_all(self):
931 helper_dicts = [helper.to_dict() for helper in self.elements]
932 out_dict = {'helpers': helper_dicts}
933 print(json.dumps(out_dict, indent=4))
934
935
936class PrinterSyscallJSON(Printer):
937 """
938 A printer for dumping collected syscall information as a JSON file.
939 @parser: A HeaderParser with APIElement objects
940 """
941
942 def __init__(self, parser):
943 self.elements = parser.commands
944 self.elem_number_check(parser.desc_syscalls, parser.enum_syscalls, 'syscall', 'bpf_cmd')
945
946 def print_all(self):
947 syscall_dicts = [syscall.to_dict() for syscall in self.elements]
948 out_dict = {'syscall': syscall_dicts}
949 print(json.dumps(out_dict, indent=4))
950
951###############################################################################
952
953# If script is launched from scripts/ from kernel tree and can access
954# ../include/uapi/linux/bpf.h, use it as a default name for the file to parse,
955# otherwise the --filename argument will be required from the command line.
956script = os.path.abspath(sys.argv[0])
957linuxRoot = os.path.dirname(os.path.dirname(script))
958bpfh = os.path.join(linuxRoot, 'include/uapi/linux/bpf.h')
959
960# target -> output format -> printer
961printers = {
962 'helpers': {
963 'rst': PrinterHelpersRST,
964 'json': PrinterHelpersJSON,
965 'header': PrinterHelpersHeader,
966 },
967 'syscall': {
968 'rst': PrinterSyscallRST,
969 'json': PrinterSyscallJSON
970 },
971}
972
973argParser = argparse.ArgumentParser(description="""
974Parse eBPF header file and generate documentation for the eBPF API.
975The RST-formatted output produced can be turned into a manual page with the
976rst2man utility.
977""")
978argParser.add_argument('--header', action='store_true',
979 help='generate C header file')
980argParser.add_argument('--json', action='store_true',
981 help='generate a JSON')
982if (os.path.isfile(bpfh)):
983 argParser.add_argument('--filename', help='path to include/uapi/linux/bpf.h',
984 default=bpfh)
985else:
986 argParser.add_argument('--filename', help='path to include/uapi/linux/bpf.h')
987argParser.add_argument('target', nargs='?', default='helpers',
988 choices=printers.keys(), help='eBPF API target')
989
990def error_die(message: str):
991 argParser.print_usage(file=sys.stderr)
992 print('Error: {}'.format(message), file=sys.stderr)
993 exit(1)
994
995def parse_and_dump():
996 args = argParser.parse_args()
997
998 # Parse file.
999 headerParser = HeaderParser(args.filename)
1000 headerParser.run()
1001
1002 if args.header and args.json:
1003 error_die('Use either --header or --json, not both')
1004
1005 output_format = 'rst'
1006 if args.header:
1007 output_format = 'header'
1008 elif args.json:
1009 output_format = 'json'
1010
1011 try:
1012 printer = printers[args.target][output_format](headerParser)
1013 # Print formatted output to standard output.
1014 printer.print_all()
1015 except KeyError:
1016 error_die('Unsupported target/format combination: "{}", "{}"'
1017 .format(args.target, output_format))
1018
1019if __name__ == "__main__":
1020 parse_and_dump()