forked from mirrors/linux
		
	bpf: add script and prepare bpf.h for new helpers documentation
Remove previous "overview" of eBPF helpers from user bpf.h header.
Replace it by a comment explaining how to process the new documentation
(to come in following patches) with a Python script to produce RST, then
man page documentation.
Also add the aforementioned Python script under scripts/. It is used to
process include/uapi/linux/bpf.h and to extract helper descriptions, to
turn it into a RST document that can further be processed with rst2man
to produce a man page. The script takes one "--filename <path/to/file>"
option. If the script is launched from scripts/ in the kernel root
directory, it should be able to find the location of the header to
parse, and "--filename <path/to/file>" is then optional. If it cannot
find the file, then the option becomes mandatory. RST-formatted
documentation is printed to standard output.
Typical workflow for producing the final man page would be:
    $ ./scripts/bpf_helpers_doc.py \
            --filename include/uapi/linux/bpf.h > /tmp/bpf-helpers.rst
    $ rst2man /tmp/bpf-helpers.rst > /tmp/bpf-helpers.7
    $ man /tmp/bpf-helpers.7
Note that the tool kernel-doc cannot be used to document eBPF helpers,
whose signatures are not available directly in the header files
(pre-processor directives are used to produce them at the beginning of
the compilation process).
v4:
- Also remove overviews for newly added bpf_xdp_adjust_tail() and
  bpf_skb_get_xfrm_state().
- Remove vague statement about what helpers are restricted to GPL
  programs in "LICENSE" section for man page footer.
- Replace license boilerplate with SPDX tag for Python script.
v3:
- Change license for man page.
- Remove "for safety reasons" from man page header text.
- Change "packets metadata" to "packets" in man page header text.
- Move and fix comment on helpers introducing no overhead.
- Remove "NOTES" section from man page footer.
- Add "LICENSE" section to man page footer.
- Edit description of file include/uapi/linux/bpf.h in man page footer.
Signed-off-by: Quentin Monnet <quentin.monnet@netronome.com>
Signed-off-by: Daniel Borkmann <daniel@iogearbox.net>
			
			
This commit is contained in:
		
							parent
							
								
									3f13de6d6f
								
							
						
					
					
						commit
						56a092c895
					
				
					 2 changed files with 434 additions and 403 deletions
				
			
		|  | @ -377,412 +377,22 @@ union bpf_attr { | |||
| 	}; | ||||
| } __attribute__((aligned(8))); | ||||
| 
 | ||||
| /* BPF helper function descriptions:
 | ||||
| /* The description below is an attempt at providing documentation to eBPF
 | ||||
|  * developers about the multiple available eBPF helper functions. It can be | ||||
|  * parsed and used to produce a manual page. The workflow is the following, | ||||
|  * and requires the rst2man utility: | ||||
|  * | ||||
|  * void *bpf_map_lookup_elem(&map, &key) | ||||
|  *     Return: Map value or NULL | ||||
|  *     $ ./scripts/bpf_helpers_doc.py \ | ||||
|  *             --filename include/uapi/linux/bpf.h > /tmp/bpf-helpers.rst | ||||
|  *     $ rst2man /tmp/bpf-helpers.rst > /tmp/bpf-helpers.7 | ||||
|  *     $ man /tmp/bpf-helpers.7 | ||||
|  * | ||||
|  * int bpf_map_update_elem(&map, &key, &value, flags) | ||||
|  *     Return: 0 on success or negative error | ||||
|  * Note that in order to produce this external documentation, some RST | ||||
|  * formatting is used in the descriptions to get "bold" and "italics" in | ||||
|  * manual pages. Also note that the few trailing white spaces are | ||||
|  * intentional, removing them would break paragraphs for rst2man. | ||||
|  * | ||||
|  * int bpf_map_delete_elem(&map, &key) | ||||
|  *     Return: 0 on success or negative error | ||||
|  * | ||||
|  * int bpf_probe_read(void *dst, int size, void *src) | ||||
|  *     Return: 0 on success or negative error | ||||
|  * | ||||
|  * u64 bpf_ktime_get_ns(void) | ||||
|  *     Return: current ktime | ||||
|  * | ||||
|  * int bpf_trace_printk(const char *fmt, int fmt_size, ...) | ||||
|  *     Return: length of buffer written or negative error | ||||
|  * | ||||
|  * u32 bpf_prandom_u32(void) | ||||
|  *     Return: random value | ||||
|  * | ||||
|  * u32 bpf_raw_smp_processor_id(void) | ||||
|  *     Return: SMP processor ID | ||||
|  * | ||||
|  * int bpf_skb_store_bytes(skb, offset, from, len, flags) | ||||
|  *     store bytes into packet | ||||
|  *     @skb: pointer to skb | ||||
|  *     @offset: offset within packet from skb->mac_header | ||||
|  *     @from: pointer where to copy bytes from | ||||
|  *     @len: number of bytes to store into packet | ||||
|  *     @flags: bit 0 - if true, recompute skb->csum | ||||
|  *             other bits - reserved | ||||
|  *     Return: 0 on success or negative error | ||||
|  * | ||||
|  * int bpf_l3_csum_replace(skb, offset, from, to, flags) | ||||
|  *     recompute IP checksum | ||||
|  *     @skb: pointer to skb | ||||
|  *     @offset: offset within packet where IP checksum is located | ||||
|  *     @from: old value of header field | ||||
|  *     @to: new value of header field | ||||
|  *     @flags: bits 0-3 - size of header field | ||||
|  *             other bits - reserved | ||||
|  *     Return: 0 on success or negative error | ||||
|  * | ||||
|  * int bpf_l4_csum_replace(skb, offset, from, to, flags) | ||||
|  *     recompute TCP/UDP checksum | ||||
|  *     @skb: pointer to skb | ||||
|  *     @offset: offset within packet where TCP/UDP checksum is located | ||||
|  *     @from: old value of header field | ||||
|  *     @to: new value of header field | ||||
|  *     @flags: bits 0-3 - size of header field | ||||
|  *             bit 4 - is pseudo header | ||||
|  *             other bits - reserved | ||||
|  *     Return: 0 on success or negative error | ||||
|  * | ||||
|  * int bpf_tail_call(ctx, prog_array_map, index) | ||||
|  *     jump into another BPF program | ||||
|  *     @ctx: context pointer passed to next program | ||||
|  *     @prog_array_map: pointer to map which type is BPF_MAP_TYPE_PROG_ARRAY | ||||
|  *     @index: 32-bit index inside array that selects specific program to run | ||||
|  *     Return: 0 on success or negative error | ||||
|  * | ||||
|  * int bpf_clone_redirect(skb, ifindex, flags) | ||||
|  *     redirect to another netdev | ||||
|  *     @skb: pointer to skb | ||||
|  *     @ifindex: ifindex of the net device | ||||
|  *     @flags: bit 0 - if set, redirect to ingress instead of egress | ||||
|  *             other bits - reserved | ||||
|  *     Return: 0 on success or negative error | ||||
|  * | ||||
|  * u64 bpf_get_current_pid_tgid(void) | ||||
|  *     Return: current->tgid << 32 | current->pid | ||||
|  * | ||||
|  * u64 bpf_get_current_uid_gid(void) | ||||
|  *     Return: current_gid << 32 | current_uid | ||||
|  * | ||||
|  * int bpf_get_current_comm(char *buf, int size_of_buf) | ||||
|  *     stores current->comm into buf | ||||
|  *     Return: 0 on success or negative error | ||||
|  * | ||||
|  * u32 bpf_get_cgroup_classid(skb) | ||||
|  *     retrieve a proc's classid | ||||
|  *     @skb: pointer to skb | ||||
|  *     Return: classid if != 0 | ||||
|  * | ||||
|  * int bpf_skb_vlan_push(skb, vlan_proto, vlan_tci) | ||||
|  *     Return: 0 on success or negative error | ||||
|  * | ||||
|  * int bpf_skb_vlan_pop(skb) | ||||
|  *     Return: 0 on success or negative error | ||||
|  * | ||||
|  * int bpf_skb_get_tunnel_key(skb, key, size, flags) | ||||
|  * int bpf_skb_set_tunnel_key(skb, key, size, flags) | ||||
|  *     retrieve or populate tunnel metadata | ||||
|  *     @skb: pointer to skb | ||||
|  *     @key: pointer to 'struct bpf_tunnel_key' | ||||
|  *     @size: size of 'struct bpf_tunnel_key' | ||||
|  *     @flags: room for future extensions | ||||
|  *     Return: 0 on success or negative error | ||||
|  * | ||||
|  * u64 bpf_perf_event_read(map, flags) | ||||
|  *     read perf event counter value | ||||
|  *     @map: pointer to perf_event_array map | ||||
|  *     @flags: index of event in the map or bitmask flags | ||||
|  *     Return: value of perf event counter read or error code | ||||
|  * | ||||
|  * int bpf_redirect(ifindex, flags) | ||||
|  *     redirect to another netdev | ||||
|  *     @ifindex: ifindex of the net device | ||||
|  *     @flags: | ||||
|  *	  cls_bpf: | ||||
|  *          bit 0 - if set, redirect to ingress instead of egress | ||||
|  *          other bits - reserved | ||||
|  *	  xdp_bpf: | ||||
|  *	    all bits - reserved | ||||
|  *     Return: cls_bpf: TC_ACT_REDIRECT on success or TC_ACT_SHOT on error | ||||
|  *	       xdp_bfp: XDP_REDIRECT on success or XDP_ABORT on error | ||||
|  * int bpf_redirect_map(map, key, flags) | ||||
|  *     redirect to endpoint in map | ||||
|  *     @map: pointer to dev map | ||||
|  *     @key: index in map to lookup | ||||
|  *     @flags: -- | ||||
|  *     Return: XDP_REDIRECT on success or XDP_ABORT on error | ||||
|  * | ||||
|  * u32 bpf_get_route_realm(skb) | ||||
|  *     retrieve a dst's tclassid | ||||
|  *     @skb: pointer to skb | ||||
|  *     Return: realm if != 0 | ||||
|  * | ||||
|  * int bpf_perf_event_output(ctx, map, flags, data, size) | ||||
|  *     output perf raw sample | ||||
|  *     @ctx: struct pt_regs* | ||||
|  *     @map: pointer to perf_event_array map | ||||
|  *     @flags: index of event in the map or bitmask flags | ||||
|  *     @data: data on stack to be output as raw data | ||||
|  *     @size: size of data | ||||
|  *     Return: 0 on success or negative error | ||||
|  * | ||||
|  * int bpf_get_stackid(ctx, map, flags) | ||||
|  *     walk user or kernel stack and return id | ||||
|  *     @ctx: struct pt_regs* | ||||
|  *     @map: pointer to stack_trace map | ||||
|  *     @flags: bits 0-7 - numer of stack frames to skip | ||||
|  *             bit 8 - collect user stack instead of kernel | ||||
|  *             bit 9 - compare stacks by hash only | ||||
|  *             bit 10 - if two different stacks hash into the same stackid | ||||
|  *                      discard old | ||||
|  *             other bits - reserved | ||||
|  *     Return: >= 0 stackid on success or negative error | ||||
|  * | ||||
|  * s64 bpf_csum_diff(from, from_size, to, to_size, seed) | ||||
|  *     calculate csum diff | ||||
|  *     @from: raw from buffer | ||||
|  *     @from_size: length of from buffer | ||||
|  *     @to: raw to buffer | ||||
|  *     @to_size: length of to buffer | ||||
|  *     @seed: optional seed | ||||
|  *     Return: csum result or negative error code | ||||
|  * | ||||
|  * int bpf_skb_get_tunnel_opt(skb, opt, size) | ||||
|  *     retrieve tunnel options metadata | ||||
|  *     @skb: pointer to skb | ||||
|  *     @opt: pointer to raw tunnel option data | ||||
|  *     @size: size of @opt | ||||
|  *     Return: option size | ||||
|  * | ||||
|  * int bpf_skb_set_tunnel_opt(skb, opt, size) | ||||
|  *     populate tunnel options metadata | ||||
|  *     @skb: pointer to skb | ||||
|  *     @opt: pointer to raw tunnel option data | ||||
|  *     @size: size of @opt | ||||
|  *     Return: 0 on success or negative error | ||||
|  * | ||||
|  * int bpf_skb_change_proto(skb, proto, flags) | ||||
|  *     Change protocol of the skb. Currently supported is v4 -> v6, | ||||
|  *     v6 -> v4 transitions. The helper will also resize the skb. eBPF | ||||
|  *     program is expected to fill the new headers via skb_store_bytes | ||||
|  *     and lX_csum_replace. | ||||
|  *     @skb: pointer to skb | ||||
|  *     @proto: new skb->protocol type | ||||
|  *     @flags: reserved | ||||
|  *     Return: 0 on success or negative error | ||||
|  * | ||||
|  * int bpf_skb_change_type(skb, type) | ||||
|  *     Change packet type of skb. | ||||
|  *     @skb: pointer to skb | ||||
|  *     @type: new skb->pkt_type type | ||||
|  *     Return: 0 on success or negative error | ||||
|  * | ||||
|  * int bpf_skb_under_cgroup(skb, map, index) | ||||
|  *     Check cgroup2 membership of skb | ||||
|  *     @skb: pointer to skb | ||||
|  *     @map: pointer to bpf_map in BPF_MAP_TYPE_CGROUP_ARRAY type | ||||
|  *     @index: index of the cgroup in the bpf_map | ||||
|  *     Return: | ||||
|  *       == 0 skb failed the cgroup2 descendant test | ||||
|  *       == 1 skb succeeded the cgroup2 descendant test | ||||
|  *        < 0 error | ||||
|  * | ||||
|  * u32 bpf_get_hash_recalc(skb) | ||||
|  *     Retrieve and possibly recalculate skb->hash. | ||||
|  *     @skb: pointer to skb | ||||
|  *     Return: hash | ||||
|  * | ||||
|  * u64 bpf_get_current_task(void) | ||||
|  *     Returns current task_struct | ||||
|  *     Return: current | ||||
|  * | ||||
|  * int bpf_probe_write_user(void *dst, void *src, int len) | ||||
|  *     safely attempt to write to a location | ||||
|  *     @dst: destination address in userspace | ||||
|  *     @src: source address on stack | ||||
|  *     @len: number of bytes to copy | ||||
|  *     Return: 0 on success or negative error | ||||
|  * | ||||
|  * int bpf_current_task_under_cgroup(map, index) | ||||
|  *     Check cgroup2 membership of current task | ||||
|  *     @map: pointer to bpf_map in BPF_MAP_TYPE_CGROUP_ARRAY type | ||||
|  *     @index: index of the cgroup in the bpf_map | ||||
|  *     Return: | ||||
|  *       == 0 current failed the cgroup2 descendant test | ||||
|  *       == 1 current succeeded the cgroup2 descendant test | ||||
|  *        < 0 error | ||||
|  * | ||||
|  * int bpf_skb_change_tail(skb, len, flags) | ||||
|  *     The helper will resize the skb to the given new size, to be used f.e. | ||||
|  *     with control messages. | ||||
|  *     @skb: pointer to skb | ||||
|  *     @len: new skb length | ||||
|  *     @flags: reserved | ||||
|  *     Return: 0 on success or negative error | ||||
|  * | ||||
|  * int bpf_skb_pull_data(skb, len) | ||||
|  *     The helper will pull in non-linear data in case the skb is non-linear | ||||
|  *     and not all of len are part of the linear section. Only needed for | ||||
|  *     read/write with direct packet access. | ||||
|  *     @skb: pointer to skb | ||||
|  *     @len: len to make read/writeable | ||||
|  *     Return: 0 on success or negative error | ||||
|  * | ||||
|  * s64 bpf_csum_update(skb, csum) | ||||
|  *     Adds csum into skb->csum in case of CHECKSUM_COMPLETE. | ||||
|  *     @skb: pointer to skb | ||||
|  *     @csum: csum to add | ||||
|  *     Return: csum on success or negative error | ||||
|  * | ||||
|  * void bpf_set_hash_invalid(skb) | ||||
|  *     Invalidate current skb->hash. | ||||
|  *     @skb: pointer to skb | ||||
|  * | ||||
|  * int bpf_get_numa_node_id() | ||||
|  *     Return: Id of current NUMA node. | ||||
|  * | ||||
|  * int bpf_skb_change_head() | ||||
|  *     Grows headroom of skb and adjusts MAC header offset accordingly. | ||||
|  *     Will extends/reallocae as required automatically. | ||||
|  *     May change skb data pointer and will thus invalidate any check | ||||
|  *     performed for direct packet access. | ||||
|  *     @skb: pointer to skb | ||||
|  *     @len: length of header to be pushed in front | ||||
|  *     @flags: Flags (unused for now) | ||||
|  *     Return: 0 on success or negative error | ||||
|  * | ||||
|  * int bpf_xdp_adjust_head(xdp_md, delta) | ||||
|  *     Adjust the xdp_md.data by delta | ||||
|  *     @xdp_md: pointer to xdp_md | ||||
|  *     @delta: An positive/negative integer to be added to xdp_md.data | ||||
|  *     Return: 0 on success or negative on error | ||||
|  * | ||||
|  * int bpf_probe_read_str(void *dst, int size, const void *unsafe_ptr) | ||||
|  *     Copy a NUL terminated string from unsafe address. In case the string | ||||
|  *     length is smaller than size, the target is not padded with further NUL | ||||
|  *     bytes. In case the string length is larger than size, just count-1 | ||||
|  *     bytes are copied and the last byte is set to NUL. | ||||
|  *     @dst: destination address | ||||
|  *     @size: maximum number of bytes to copy, including the trailing NUL | ||||
|  *     @unsafe_ptr: unsafe address | ||||
|  *     Return: | ||||
|  *       > 0 length of the string including the trailing NUL on success | ||||
|  *       < 0 error | ||||
|  * | ||||
|  * u64 bpf_get_socket_cookie(skb) | ||||
|  *     Get the cookie for the socket stored inside sk_buff. | ||||
|  *     @skb: pointer to skb | ||||
|  *     Return: 8 Bytes non-decreasing number on success or 0 if the socket | ||||
|  *     field is missing inside sk_buff | ||||
|  * | ||||
|  * u32 bpf_get_socket_uid(skb) | ||||
|  *     Get the owner uid of the socket stored inside sk_buff. | ||||
|  *     @skb: pointer to skb | ||||
|  *     Return: uid of the socket owner on success or overflowuid if failed. | ||||
|  * | ||||
|  * u32 bpf_set_hash(skb, hash) | ||||
|  *     Set full skb->hash. | ||||
|  *     @skb: pointer to skb | ||||
|  *     @hash: hash to set | ||||
|  * | ||||
|  * int bpf_setsockopt(bpf_socket, level, optname, optval, optlen) | ||||
|  *     Calls setsockopt. Not all opts are available, only those with | ||||
|  *     integer optvals plus TCP_CONGESTION. | ||||
|  *     Supported levels: SOL_SOCKET and IPPROTO_TCP | ||||
|  *     @bpf_socket: pointer to bpf_socket | ||||
|  *     @level: SOL_SOCKET or IPPROTO_TCP | ||||
|  *     @optname: option name | ||||
|  *     @optval: pointer to option value | ||||
|  *     @optlen: length of optval in bytes | ||||
|  *     Return: 0 or negative error | ||||
|  * | ||||
|  * int bpf_getsockopt(bpf_socket, level, optname, optval, optlen) | ||||
|  *     Calls getsockopt. Not all opts are available. | ||||
|  *     Supported levels: IPPROTO_TCP | ||||
|  *     @bpf_socket: pointer to bpf_socket | ||||
|  *     @level: IPPROTO_TCP | ||||
|  *     @optname: option name | ||||
|  *     @optval: pointer to option value | ||||
|  *     @optlen: length of optval in bytes | ||||
|  *     Return: 0 or negative error | ||||
|  * | ||||
|  * int bpf_sock_ops_cb_flags_set(bpf_sock_ops, flags) | ||||
|  *     Set callback flags for sock_ops | ||||
|  *     @bpf_sock_ops: pointer to bpf_sock_ops_kern struct | ||||
|  *     @flags: flags value | ||||
|  *     Return: 0 for no error | ||||
|  *             -EINVAL if there is no full tcp socket | ||||
|  *             bits in flags that are not supported by current kernel | ||||
|  * | ||||
|  * int bpf_skb_adjust_room(skb, len_diff, mode, flags) | ||||
|  *     Grow or shrink room in sk_buff. | ||||
|  *     @skb: pointer to skb | ||||
|  *     @len_diff: (signed) amount of room to grow/shrink | ||||
|  *     @mode: operation mode (enum bpf_adj_room_mode) | ||||
|  *     @flags: reserved for future use | ||||
|  *     Return: 0 on success or negative error code | ||||
|  * | ||||
|  * int bpf_sk_redirect_map(map, key, flags) | ||||
|  *     Redirect skb to a sock in map using key as a lookup key for the | ||||
|  *     sock in map. | ||||
|  *     @map: pointer to sockmap | ||||
|  *     @key: key to lookup sock in map | ||||
|  *     @flags: reserved for future use | ||||
|  *     Return: SK_PASS | ||||
|  * | ||||
|  * int bpf_sock_map_update(skops, map, key, flags) | ||||
|  *	@skops: pointer to bpf_sock_ops | ||||
|  *	@map: pointer to sockmap to update | ||||
|  *	@key: key to insert/update sock in map | ||||
|  *	@flags: same flags as map update elem | ||||
|  * | ||||
|  * int bpf_xdp_adjust_meta(xdp_md, delta) | ||||
|  *     Adjust the xdp_md.data_meta by delta | ||||
|  *     @xdp_md: pointer to xdp_md | ||||
|  *     @delta: An positive/negative integer to be added to xdp_md.data_meta | ||||
|  *     Return: 0 on success or negative on error | ||||
|  * | ||||
|  * int bpf_perf_event_read_value(map, flags, buf, buf_size) | ||||
|  *     read perf event counter value and perf event enabled/running time | ||||
|  *     @map: pointer to perf_event_array map | ||||
|  *     @flags: index of event in the map or bitmask flags | ||||
|  *     @buf: buf to fill | ||||
|  *     @buf_size: size of the buf | ||||
|  *     Return: 0 on success or negative error code | ||||
|  * | ||||
|  * int bpf_perf_prog_read_value(ctx, buf, buf_size) | ||||
|  *     read perf prog attached perf event counter and enabled/running time | ||||
|  *     @ctx: pointer to ctx | ||||
|  *     @buf: buf to fill | ||||
|  *     @buf_size: size of the buf | ||||
|  *     Return : 0 on success or negative error code | ||||
|  * | ||||
|  * int bpf_override_return(pt_regs, rc) | ||||
|  *	@pt_regs: pointer to struct pt_regs | ||||
|  *	@rc: the return value to set | ||||
|  * | ||||
|  * int bpf_msg_redirect_map(map, key, flags) | ||||
|  *     Redirect msg to a sock in map using key as a lookup key for the | ||||
|  *     sock in map. | ||||
|  *     @map: pointer to sockmap | ||||
|  *     @key: key to lookup sock in map | ||||
|  *     @flags: reserved for future use | ||||
|  *     Return: SK_PASS | ||||
|  * | ||||
|  * int bpf_bind(ctx, addr, addr_len) | ||||
|  *     Bind socket to address. Only binding to IP is supported, no port can be | ||||
|  *     set in addr. | ||||
|  *     @ctx: pointer to context of type bpf_sock_addr | ||||
|  *     @addr: pointer to struct sockaddr to bind socket to | ||||
|  *     @addr_len: length of sockaddr structure | ||||
|  *     Return: 0 on success or negative error code | ||||
|  * | ||||
|  * int bpf_xdp_adjust_tail(xdp_md, delta) | ||||
|  *     Adjust the xdp_md.data_end by delta. Only shrinking of packet's | ||||
|  *     size is supported. | ||||
|  *     @xdp_md: pointer to xdp_md | ||||
|  *     @delta: A negative integer to be added to xdp_md.data_end | ||||
|  *     Return: 0 on success or negative on error | ||||
|  * | ||||
|  * int bpf_skb_get_xfrm_state(skb, index, xfrm_state, size, flags) | ||||
|  *     retrieve XFRM state | ||||
|  *     @skb: pointer to skb | ||||
|  *     @index: index of the xfrm state in the secpath | ||||
|  *     @key: pointer to 'struct bpf_xfrm_state' | ||||
|  *     @size: size of 'struct bpf_xfrm_state' | ||||
|  *     @flags: room for future extensions | ||||
|  *     Return: 0 on success or negative error | ||||
|  * Start of BPF helper function descriptions: | ||||
|  */ | ||||
| #define __BPF_FUNC_MAPPER(FN)		\ | ||||
| 	FN(unspec),			\ | ||||
|  |  | |||
							
								
								
									
										421
									
								
								scripts/bpf_helpers_doc.py
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										421
									
								
								scripts/bpf_helpers_doc.py
									
									
									
									
									
										Executable file
									
								
							|  | @ -0,0 +1,421 @@ | |||
| #!/usr/bin/python3 | ||||
| # SPDX-License-Identifier: GPL-2.0-only | ||||
| # | ||||
| # Copyright (C) 2018 Netronome Systems, Inc. | ||||
| 
 | ||||
| # In case user attempts to run with Python 2. | ||||
| from __future__ import print_function | ||||
| 
 | ||||
| import argparse | ||||
| import re | ||||
| import sys, os | ||||
| 
 | ||||
| class NoHelperFound(BaseException): | ||||
|     pass | ||||
| 
 | ||||
| class ParsingError(BaseException): | ||||
|     def __init__(self, line='<line not provided>', reader=None): | ||||
|         if reader: | ||||
|             BaseException.__init__(self, | ||||
|                                    'Error at file offset %d, parsing line: %s' % | ||||
|                                    (reader.tell(), line)) | ||||
|         else: | ||||
|             BaseException.__init__(self, 'Error parsing line: %s' % line) | ||||
| 
 | ||||
| class Helper(object): | ||||
|     """ | ||||
|     An object representing the description of an eBPF helper function. | ||||
|     @proto: function prototype of the helper function | ||||
|     @desc: textual description of the helper function | ||||
|     @ret: description of the return value of the helper function | ||||
|     """ | ||||
|     def __init__(self, proto='', desc='', ret=''): | ||||
|         self.proto = proto | ||||
|         self.desc = desc | ||||
|         self.ret = ret | ||||
| 
 | ||||
|     def proto_break_down(self): | ||||
|         """ | ||||
|         Break down helper function protocol into smaller chunks: return type, | ||||
|         name, distincts arguments. | ||||
|         """ | ||||
|         arg_re = re.compile('^((const )?(struct )?(\w+|...))( (\**)(\w+))?$') | ||||
|         res = {} | ||||
|         proto_re = re.compile('^(.+) (\**)(\w+)\(((([^,]+)(, )?){1,5})\)$') | ||||
| 
 | ||||
|         capture = proto_re.match(self.proto) | ||||
|         res['ret_type'] = capture.group(1) | ||||
|         res['ret_star'] = capture.group(2) | ||||
|         res['name']     = capture.group(3) | ||||
|         res['args'] = [] | ||||
| 
 | ||||
|         args    = capture.group(4).split(', ') | ||||
|         for a in args: | ||||
|             capture = arg_re.match(a) | ||||
|             res['args'].append({ | ||||
|                 'type' : capture.group(1), | ||||
|                 'star' : capture.group(6), | ||||
|                 'name' : capture.group(7) | ||||
|             }) | ||||
| 
 | ||||
|         return res | ||||
| 
 | ||||
| class HeaderParser(object): | ||||
|     """ | ||||
|     An object used to parse a file in order to extract the documentation of a | ||||
|     list of eBPF helper functions. All the helpers that can be retrieved are | ||||
|     stored as Helper object, in the self.helpers() array. | ||||
|     @filename: name of file to parse, usually include/uapi/linux/bpf.h in the | ||||
|                kernel tree | ||||
|     """ | ||||
|     def __init__(self, filename): | ||||
|         self.reader = open(filename, 'r') | ||||
|         self.line = '' | ||||
|         self.helpers = [] | ||||
| 
 | ||||
|     def parse_helper(self): | ||||
|         proto    = self.parse_proto() | ||||
|         desc     = self.parse_desc() | ||||
|         ret      = self.parse_ret() | ||||
|         return Helper(proto=proto, desc=desc, ret=ret) | ||||
| 
 | ||||
|     def parse_proto(self): | ||||
|         # Argument can be of shape: | ||||
|         #   - "void" | ||||
|         #   - "type  name" | ||||
|         #   - "type *name" | ||||
|         #   - Same as above, with "const" and/or "struct" in front of type | ||||
|         #   - "..." (undefined number of arguments, for bpf_trace_printk()) | ||||
|         # There is at least one term ("void"), and at most five arguments. | ||||
|         p = re.compile('^ \* ((.+) \**\w+\((((const )?(struct )?(\w+|\.\.\.)( \**\w+)?)(, )?){1,5}\))$') | ||||
|         capture = p.match(self.line) | ||||
|         if not capture: | ||||
|             raise NoHelperFound | ||||
|         self.line = self.reader.readline() | ||||
|         return capture.group(1) | ||||
| 
 | ||||
|     def parse_desc(self): | ||||
|         p = re.compile('^ \* \tDescription$') | ||||
|         capture = p.match(self.line) | ||||
|         if not capture: | ||||
|             # Helper can have empty description and we might be parsing another | ||||
|             # attribute: return but do not consume. | ||||
|             return '' | ||||
|         # Description can be several lines, some of them possibly empty, and it | ||||
|         # stops when another subsection title is met. | ||||
|         desc = '' | ||||
|         while True: | ||||
|             self.line = self.reader.readline() | ||||
|             if self.line == ' *\n': | ||||
|                 desc += '\n' | ||||
|             else: | ||||
|                 p = re.compile('^ \* \t\t(.*)') | ||||
|                 capture = p.match(self.line) | ||||
|                 if capture: | ||||
|                     desc += capture.group(1) + '\n' | ||||
|                 else: | ||||
|                     break | ||||
|         return desc | ||||
| 
 | ||||
|     def parse_ret(self): | ||||
|         p = re.compile('^ \* \tReturn$') | ||||
|         capture = p.match(self.line) | ||||
|         if not capture: | ||||
|             # Helper can have empty retval and we might be parsing another | ||||
|             # attribute: return but do not consume. | ||||
|             return '' | ||||
|         # Return value description can be several lines, some of them possibly | ||||
|         # empty, and it stops when another subsection title is met. | ||||
|         ret = '' | ||||
|         while True: | ||||
|             self.line = self.reader.readline() | ||||
|             if self.line == ' *\n': | ||||
|                 ret += '\n' | ||||
|             else: | ||||
|                 p = re.compile('^ \* \t\t(.*)') | ||||
|                 capture = p.match(self.line) | ||||
|                 if capture: | ||||
|                     ret += capture.group(1) + '\n' | ||||
|                 else: | ||||
|                     break | ||||
|         return ret | ||||
| 
 | ||||
|     def run(self): | ||||
|         # Advance to start of helper function descriptions. | ||||
|         offset = self.reader.read().find('* Start of BPF helper function descriptions:') | ||||
|         if offset == -1: | ||||
|             raise Exception('Could not find start of eBPF helper descriptions list') | ||||
|         self.reader.seek(offset) | ||||
|         self.reader.readline() | ||||
|         self.reader.readline() | ||||
|         self.line = self.reader.readline() | ||||
| 
 | ||||
|         while True: | ||||
|             try: | ||||
|                 helper = self.parse_helper() | ||||
|                 self.helpers.append(helper) | ||||
|             except NoHelperFound: | ||||
|                 break | ||||
| 
 | ||||
|         self.reader.close() | ||||
|         print('Parsed description of %d helper function(s)' % len(self.helpers), | ||||
|               file=sys.stderr) | ||||
| 
 | ||||
| ############################################################################### | ||||
| 
 | ||||
| class Printer(object): | ||||
|     """ | ||||
|     A generic class for printers. Printers should be created with an array of | ||||
|     Helper objects, and implement a way to print them in the desired fashion. | ||||
|     @helpers: array of Helper objects to print to standard output | ||||
|     """ | ||||
|     def __init__(self, helpers): | ||||
|         self.helpers = helpers | ||||
| 
 | ||||
|     def print_header(self): | ||||
|         pass | ||||
| 
 | ||||
|     def print_footer(self): | ||||
|         pass | ||||
| 
 | ||||
|     def print_one(self, helper): | ||||
|         pass | ||||
| 
 | ||||
|     def print_all(self): | ||||
|         self.print_header() | ||||
|         for helper in self.helpers: | ||||
|             self.print_one(helper) | ||||
|         self.print_footer() | ||||
| 
 | ||||
| class PrinterRST(Printer): | ||||
|     """ | ||||
|     A printer for dumping collected information about helpers as a ReStructured | ||||
|     Text page compatible with the rst2man program, which can be used to | ||||
|     generate a manual page for the helpers. | ||||
|     @helpers: array of Helper objects to print to standard output | ||||
|     """ | ||||
|     def print_header(self): | ||||
|         header = '''\ | ||||
| .. Copyright (C) All BPF authors and contributors from 2014 to present. | ||||
| .. See git log include/uapi/linux/bpf.h in kernel tree for details. | ||||
| ..  | ||||
| .. %%%LICENSE_START(VERBATIM) | ||||
| .. Permission is granted to make and distribute verbatim copies of this | ||||
| .. manual provided the copyright notice and this permission notice are | ||||
| .. preserved on all copies. | ||||
| ..  | ||||
| .. Permission is granted to copy and distribute modified versions of this | ||||
| .. manual under the conditions for verbatim copying, provided that the | ||||
| .. entire resulting derived work is distributed under the terms of a | ||||
| .. permission notice identical to this one. | ||||
| ..  | ||||
| .. Since the Linux kernel and libraries are constantly changing, this | ||||
| .. manual page may be incorrect or out-of-date.  The author(s) assume no | ||||
| .. responsibility for errors or omissions, or for damages resulting from | ||||
| .. the use of the information contained herein.  The author(s) may not | ||||
| .. have taken the same level of care in the production of this manual, | ||||
| .. which is licensed free of charge, as they might when working | ||||
| .. professionally. | ||||
| ..  | ||||
| .. Formatted or processed versions of this manual, if unaccompanied by | ||||
| .. the source, must acknowledge the copyright and authors of this work. | ||||
| .. %%%LICENSE_END | ||||
| ..  | ||||
| .. Please do not edit this file. It was generated from the documentation | ||||
| .. located in file include/uapi/linux/bpf.h of the Linux kernel sources | ||||
| .. (helpers description), and from scripts/bpf_helpers_doc.py in the same | ||||
| .. repository (header and footer). | ||||
| 
 | ||||
| =========== | ||||
| BPF-HELPERS | ||||
| =========== | ||||
| ------------------------------------------------------------------------------- | ||||
| list of eBPF helper functions | ||||
| ------------------------------------------------------------------------------- | ||||
| 
 | ||||
| :Manual section: 7 | ||||
| 
 | ||||
| DESCRIPTION | ||||
| =========== | ||||
| 
 | ||||
| The extended Berkeley Packet Filter (eBPF) subsystem consists in programs | ||||
| written in a pseudo-assembly language, then attached to one of the several | ||||
| kernel hooks and run in reaction of specific events. This framework differs | ||||
| from the older, "classic" BPF (or "cBPF") in several aspects, one of them being | ||||
| the ability to call special functions (or "helpers") from within a program. | ||||
| These functions are restricted to a white-list of helpers defined in the | ||||
| kernel. | ||||
| 
 | ||||
| These helpers are used by eBPF programs to interact with the system, or with | ||||
| the context in which they work. For instance, they can be used to print | ||||
| debugging messages, to get the time since the system was booted, to interact | ||||
| with eBPF maps, or to manipulate network packets. Since there are several eBPF | ||||
| program types, and that they do not run in the same context, each program type | ||||
| can only call a subset of those helpers. | ||||
| 
 | ||||
| Due to eBPF conventions, a helper can not have more than five arguments. | ||||
| 
 | ||||
| Internally, eBPF programs call directly into the compiled helper functions | ||||
| without requiring any foreign-function interface. As a result, calling helpers | ||||
| introduces no overhead, thus offering excellent performance. | ||||
| 
 | ||||
| This document is an attempt to list and document the helpers available to eBPF | ||||
| developers. They are sorted by chronological order (the oldest helpers in the | ||||
| kernel at the top). | ||||
| 
 | ||||
| HELPERS | ||||
| ======= | ||||
| ''' | ||||
|         print(header) | ||||
| 
 | ||||
|     def print_footer(self): | ||||
|         footer = ''' | ||||
| EXAMPLES | ||||
| ======== | ||||
| 
 | ||||
| Example usage for most of the eBPF helpers listed in this manual page are | ||||
| available within the Linux kernel sources, at the following locations: | ||||
| 
 | ||||
| * *samples/bpf/* | ||||
| * *tools/testing/selftests/bpf/* | ||||
| 
 | ||||
| LICENSE | ||||
| ======= | ||||
| 
 | ||||
| eBPF programs can have an associated license, passed along with the bytecode | ||||
| instructions to the kernel when the programs are loaded. The format for that | ||||
| string is identical to the one in use for kernel modules (Dual licenses, such | ||||
| as "Dual BSD/GPL", may be used). Some helper functions are only accessible to | ||||
| programs that are compatible with the GNU Privacy License (GPL). | ||||
| 
 | ||||
| In order to use such helpers, the eBPF program must be loaded with the correct | ||||
| license string passed (via **attr**) to the **bpf**\ () system call, and this | ||||
| generally translates into the C source code of the program containing a line | ||||
| similar to the following: | ||||
| 
 | ||||
| :: | ||||
| 
 | ||||
| 	char ____license[] __attribute__((section("license"), used)) = "GPL"; | ||||
| 
 | ||||
| IMPLEMENTATION | ||||
| ============== | ||||
| 
 | ||||
| This manual page is an effort to document the existing eBPF helper functions. | ||||
| But as of this writing, the BPF sub-system is under heavy development. New eBPF | ||||
| program or map types are added, along with new helper functions. Some helpers | ||||
| are occasionally made available for additional program types. So in spite of | ||||
| the efforts of the community, this page might not be up-to-date. If you want to | ||||
| check by yourself what helper functions exist in your kernel, or what types of | ||||
| programs they can support, here are some files among the kernel tree that you | ||||
| may be interested in: | ||||
| 
 | ||||
| * *include/uapi/linux/bpf.h* is the main BPF header. It contains the full list | ||||
|   of all helper functions, as well as many other BPF definitions including most | ||||
|   of the flags, structs or constants used by the helpers. | ||||
| * *net/core/filter.c* contains the definition of most network-related helper | ||||
|   functions, and the list of program types from which they can be used. | ||||
| * *kernel/trace/bpf_trace.c* is the equivalent for most tracing program-related | ||||
|   helpers. | ||||
| * *kernel/bpf/verifier.c* contains the functions used to check that valid types | ||||
|   of eBPF maps are used with a given helper function. | ||||
| * *kernel/bpf/* directory contains other files in which additional helpers are | ||||
|   defined (for cgroups, sockmaps, etc.). | ||||
| 
 | ||||
| Compatibility between helper functions and program types can generally be found | ||||
| in the files where helper functions are defined. Look for the **struct | ||||
| bpf_func_proto** objects and for functions returning them: these functions | ||||
| contain a list of helpers that a given program type can call. Note that the | ||||
| **default:** label of the **switch ... case** used to filter helpers can call | ||||
| other functions, themselves allowing access to additional helpers. The | ||||
| requirement for GPL license is also in those **struct bpf_func_proto**. | ||||
| 
 | ||||
| Compatibility between helper functions and map types can be found in the | ||||
| **check_map_func_compatibility**\ () function in file *kernel/bpf/verifier.c*. | ||||
| 
 | ||||
| Helper functions that invalidate the checks on **data** and **data_end** | ||||
| pointers for network processing are listed in function | ||||
| **bpf_helper_changes_pkt_data**\ () in file *net/core/filter.c*. | ||||
| 
 | ||||
| SEE ALSO | ||||
| ======== | ||||
| 
 | ||||
| **bpf**\ (2), | ||||
| **cgroups**\ (7), | ||||
| **ip**\ (8), | ||||
| **perf_event_open**\ (2), | ||||
| **sendmsg**\ (2), | ||||
| **socket**\ (7), | ||||
| **tc-bpf**\ (8)''' | ||||
|         print(footer) | ||||
| 
 | ||||
|     def print_proto(self, helper): | ||||
|         """ | ||||
|         Format function protocol with bold and italics markers. This makes RST | ||||
|         file less readable, but gives nice results in the manual page. | ||||
|         """ | ||||
|         proto = helper.proto_break_down() | ||||
| 
 | ||||
|         print('**%s %s%s(' % (proto['ret_type'], | ||||
|                               proto['ret_star'].replace('*', '\\*'), | ||||
|                               proto['name']), | ||||
|               end='') | ||||
| 
 | ||||
|         comma = '' | ||||
|         for a in proto['args']: | ||||
|             one_arg = '{}{}'.format(comma, a['type']) | ||||
|             if a['name']: | ||||
|                 if a['star']: | ||||
|                     one_arg += ' {}**\ '.format(a['star'].replace('*', '\\*')) | ||||
|                 else: | ||||
|                     one_arg += '** ' | ||||
|                 one_arg += '*{}*\\ **'.format(a['name']) | ||||
|             comma = ', ' | ||||
|             print(one_arg, end='') | ||||
| 
 | ||||
|         print(')**') | ||||
| 
 | ||||
|     def print_one(self, helper): | ||||
|         self.print_proto(helper) | ||||
| 
 | ||||
|         if (helper.desc): | ||||
|             print('\tDescription') | ||||
|             # Do not strip all newline characters: formatted code at the end of | ||||
|             # a section must be followed by a blank line. | ||||
|             for line in re.sub('\n$', '', helper.desc, count=1).split('\n'): | ||||
|                 print('{}{}'.format('\t\t' if line else '', line)) | ||||
| 
 | ||||
|         if (helper.ret): | ||||
|             print('\tReturn') | ||||
|             for line in helper.ret.rstrip().split('\n'): | ||||
|                 print('{}{}'.format('\t\t' if line else '', line)) | ||||
| 
 | ||||
|         print('') | ||||
| 
 | ||||
| ############################################################################### | ||||
| 
 | ||||
| # If script is launched from scripts/ from kernel tree and can access | ||||
| # ../include/uapi/linux/bpf.h, use it as a default name for the file to parse, | ||||
| # otherwise the --filename argument will be required from the command line. | ||||
| script = os.path.abspath(sys.argv[0]) | ||||
| linuxRoot = os.path.dirname(os.path.dirname(script)) | ||||
| bpfh = os.path.join(linuxRoot, 'include/uapi/linux/bpf.h') | ||||
| 
 | ||||
| argParser = argparse.ArgumentParser(description=""" | ||||
| Parse eBPF header file and generate documentation for eBPF helper functions. | ||||
| The RST-formatted output produced can be turned into a manual page with the | ||||
| rst2man utility. | ||||
| """) | ||||
| if (os.path.isfile(bpfh)): | ||||
|     argParser.add_argument('--filename', help='path to include/uapi/linux/bpf.h', | ||||
|                            default=bpfh) | ||||
| else: | ||||
|     argParser.add_argument('--filename', help='path to include/uapi/linux/bpf.h') | ||||
| args = argParser.parse_args() | ||||
| 
 | ||||
| # Parse file. | ||||
| headerParser = HeaderParser(args.filename) | ||||
| headerParser.run() | ||||
| 
 | ||||
| # Print formatted output to standard output. | ||||
| printer = PrinterRST(headerParser.helpers) | ||||
| printer.print_all() | ||||
		Loading…
	
		Reference in a new issue
	
	 Quentin Monnet
						Quentin Monnet