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 'struct btf_ptr', 792 'struct inode', 793 'struct socket', 794 'struct file', 795 'struct bpf_timer', 796 'struct mptcp_sock', 797 'struct bpf_dynptr', 798 'const struct bpf_dynptr', 799 'struct iphdr', 800 'struct ipv6hdr', 801 } 802 mapped_types = { 803 'u8': '__u8', 804 'u16': '__u16', 805 'u32': '__u32', 806 'u64': '__u64', 807 's8': '__s8', 808 's16': '__s16', 809 's32': '__s32', 810 's64': '__s64', 811 'size_t': 'unsigned long', 812 'struct bpf_map': 'void', 813 'struct sk_buff': 'struct __sk_buff', 814 'const struct sk_buff': 'const struct __sk_buff', 815 'struct sk_msg_buff': 'struct sk_msg_md', 816 'struct xdp_buff': 'struct xdp_md', 817 } 818 # Helpers overloaded for different context types. 819 overloaded_helpers = [ 820 'bpf_get_socket_cookie', 821 'bpf_sk_assign', 822 ] 823 824 def print_header(self): 825 header = '''\ 826/* This is auto-generated file. See bpf_doc.py for details. */ 827 828/* Forward declarations of BPF structs */''' 829 830 print(header) 831 for fwd in self.type_fwds: 832 print('%s;' % fwd) 833 print('') 834 835 used_attrs = set() 836 for helper in self.elements: 837 for attr in helper.attrs: 838 used_attrs.add(attr) 839 for attr in sorted(used_attrs): 840 print('#ifndef %s' % attr) 841 print('#if __has_attribute(%s)' % ATTRS[attr]) 842 print('#define %s __attribute__((%s))' % (attr, ATTRS[attr])) 843 print('#else') 844 print('#define %s' % attr) 845 print('#endif') 846 print('#endif') 847 if used_attrs: 848 print('') 849 850 def print_footer(self): 851 footer = '' 852 print(footer) 853 854 def map_type(self, t): 855 if t in self.known_types: 856 return t 857 if t in self.mapped_types: 858 return self.mapped_types[t] 859 print("Unrecognized type '%s', please add it to known types!" % t, 860 file=sys.stderr) 861 sys.exit(1) 862 863 seen_helpers = set() 864 865 def print_one(self, helper): 866 proto = helper.proto_break_down() 867 868 if proto['name'] in self.seen_helpers: 869 return 870 self.seen_helpers.add(proto['name']) 871 872 print('/*') 873 print(" * %s" % proto['name']) 874 print(" *") 875 if (helper.desc): 876 # Do not strip all newline characters: formatted code at the end of 877 # a section must be followed by a blank line. 878 for line in re.sub('\n$', '', helper.desc, count=1).split('\n'): 879 print(' *{}{}'.format(' \t' if line else '', line)) 880 881 if (helper.ret): 882 print(' *') 883 print(' * Returns') 884 for line in helper.ret.rstrip().split('\n'): 885 print(' *{}{}'.format(' \t' if line else '', line)) 886 887 print(' */') 888 print('static ', end='') 889 if helper.attrs: 890 print('%s ' % (" ".join(helper.attrs)), end='') 891 print('%s %s(* const %s)(' % (self.map_type(proto['ret_type']), 892 proto['ret_star'], proto['name']), end='') 893 comma = '' 894 for i, a in enumerate(proto['args']): 895 t = a['type'] 896 n = a['name'] 897 if proto['name'] in self.overloaded_helpers and i == 0: 898 t = 'void' 899 n = 'ctx' 900 one_arg = '{}{}'.format(comma, self.map_type(t)) 901 if n: 902 if a['star']: 903 one_arg += ' {}'.format(a['star']) 904 else: 905 one_arg += ' ' 906 one_arg += '{}'.format(n) 907 comma = ', ' 908 print(one_arg, end='') 909 910 print(') = (void *) %d;' % helper.enum_val) 911 print('') 912 913 914class PrinterHelpersJSON(Printer): 915 """ 916 A printer for dumping collected information about helpers as a JSON file. 917 @parser: A HeaderParser with Helper objects 918 """ 919 920 def __init__(self, parser): 921 self.elements = parser.helpers 922 self.elem_number_check( 923 parser.desc_unique_helpers, 924 parser.define_unique_helpers, 925 "helper", 926 "___BPF_FUNC_MAPPER", 927 ) 928 929 def print_all(self): 930 helper_dicts = [helper.to_dict() for helper in self.elements] 931 out_dict = {'helpers': helper_dicts} 932 print(json.dumps(out_dict, indent=4)) 933 934 935class PrinterSyscallJSON(Printer): 936 """ 937 A printer for dumping collected syscall information as a JSON file. 938 @parser: A HeaderParser with APIElement objects 939 """ 940 941 def __init__(self, parser): 942 self.elements = parser.commands 943 self.elem_number_check(parser.desc_syscalls, parser.enum_syscalls, 'syscall', 'bpf_cmd') 944 945 def print_all(self): 946 syscall_dicts = [syscall.to_dict() for syscall in self.elements] 947 out_dict = {'syscall': syscall_dicts} 948 print(json.dumps(out_dict, indent=4)) 949 950############################################################################### 951 952# If script is launched from scripts/ from kernel tree and can access 953# ../include/uapi/linux/bpf.h, use it as a default name for the file to parse, 954# otherwise the --filename argument will be required from the command line. 955script = os.path.abspath(sys.argv[0]) 956linuxRoot = os.path.dirname(os.path.dirname(script)) 957bpfh = os.path.join(linuxRoot, 'include/uapi/linux/bpf.h') 958 959# target -> output format -> printer 960printers = { 961 'helpers': { 962 'rst': PrinterHelpersRST, 963 'json': PrinterHelpersJSON, 964 'header': PrinterHelpersHeader, 965 }, 966 'syscall': { 967 'rst': PrinterSyscallRST, 968 'json': PrinterSyscallJSON 969 }, 970} 971 972argParser = argparse.ArgumentParser(description=""" 973Parse eBPF header file and generate documentation for the eBPF API. 974The RST-formatted output produced can be turned into a manual page with the 975rst2man utility. 976""") 977argParser.add_argument('--header', action='store_true', 978 help='generate C header file') 979argParser.add_argument('--json', action='store_true', 980 help='generate a JSON') 981if (os.path.isfile(bpfh)): 982 argParser.add_argument('--filename', help='path to include/uapi/linux/bpf.h', 983 default=bpfh) 984else: 985 argParser.add_argument('--filename', help='path to include/uapi/linux/bpf.h') 986argParser.add_argument('target', nargs='?', default='helpers', 987 choices=printers.keys(), help='eBPF API target') 988 989def error_die(message: str): 990 argParser.print_usage(file=sys.stderr) 991 print('Error: {}'.format(message), file=sys.stderr) 992 exit(1) 993 994def parse_and_dump(): 995 args = argParser.parse_args() 996 997 # Parse file. 998 headerParser = HeaderParser(args.filename) 999 headerParser.run() 1000 1001 if args.header and args.json: 1002 error_die('Use either --header or --json, not both') 1003 1004 output_format = 'rst' 1005 if args.header: 1006 output_format = 'header' 1007 elif args.json: 1008 output_format = 'json' 1009 1010 try: 1011 printer = printers[args.target][output_format](headerParser) 1012 # Print formatted output to standard output. 1013 printer.print_all() 1014 except KeyError: 1015 error_die('Unsupported target/format combination: "{}", "{}"' 1016 .format(args.target, output_format)) 1017 1018if __name__ == "__main__": 1019 parse_and_dump() 1020