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