| .. SPDX-License-Identifier: BSD-3-Clause | |
| ===================================== | |
| Using Netlink protocol specifications | |
| ===================================== | |
| This document is a quick starting guide for using Netlink protocol | |
| specifications. For more detailed description of the specs see :doc:`specs`. | |
| Simple CLI | |
| ========== | |
| Kernel comes with a simple CLI tool which should be useful when | |
| developing Netlink related code. The tool is implemented in Python | |
| and can use a YAML specification to issue Netlink requests | |
| to the kernel. | |
| The tool is located at ``tools/net/ynl/pyynl/cli.py``. It accepts | |
| a handful of arguments, the most important ones are: | |
| - ``--spec`` - point to the spec file | |
| - ``--do $name`` / ``--dump $name`` - issue request ``$name`` | |
| - ``--json $attrs`` - provide attributes for the request | |
| - ``--subscribe $group`` - receive notifications from ``$group`` | |
| YAML specs can be found under ``Documentation/netlink/specs/``. | |
| Example use:: | |
| $ ./tools/net/ynl/pyynl/cli.py --spec Documentation/netlink/specs/ethtool.yaml \ | |
| --do rings-get \ | |
| --json '{"header":{"dev-index": 18}}' | |
| {'header': {'dev-index': 18, 'dev-name': 'eni1np1'}, | |
| 'rx': 0, | |
| 'rx-jumbo': 0, | |
| 'rx-jumbo-max': 4096, | |
| 'rx-max': 4096, | |
| 'rx-mini': 0, | |
| 'rx-mini-max': 4096, | |
| 'tx': 0, | |
| 'tx-max': 4096, | |
| 'tx-push': 0} | |
| The input arguments are parsed as JSON, while the output is only | |
| Python-pretty-printed. This is because some Netlink types can't | |
| be expressed as JSON directly. If such attributes are needed in | |
| the input some hacking of the script will be necessary. | |
| The spec and Netlink internals are factored out as a standalone | |
| library - it should be easy to write Python tools / tests reusing | |
| code from ``cli.py``. | |
| Generating kernel code | |
| ====================== | |
| ``tools/net/ynl/ynl-regen.sh`` scans the kernel tree in search of | |
| auto-generated files which need to be updated. Using this tool is the easiest | |
| way to generate / update auto-generated code. | |
| By default code is re-generated only if spec is newer than the source, | |
| to force regeneration use ``-f``. | |
| ``ynl-regen.sh`` searches for ``YNL-GEN`` in the contents of files | |
| (note that it only scans files in the git index, that is only files | |
| tracked by git!) For instance the ``fou_nl.c`` kernel source contains:: | |
| /* Documentation/netlink/specs/fou.yaml */ | |
| /* YNL-GEN kernel source */ | |
| ``ynl-regen.sh`` will find this marker and replace the file with | |
| kernel source based on fou.yaml. | |
| The simplest way to generate a new file based on a spec is to add | |
| the two marker lines like above to a file, add that file to git, | |
| and run the regeneration tool. Grep the tree for ``YNL-GEN`` | |
| to see other examples. | |
| The code generation itself is performed by ``tools/net/ynl/pyynl/ynl_gen_c.py`` | |
| but it takes a few arguments so calling it directly for each file | |
| quickly becomes tedious. | |
| YNL lib | |
| ======= | |
| ``tools/net/ynl/lib/`` contains an implementation of a C library | |
| (based on libmnl) which integrates with code generated by | |
| ``tools/net/ynl/pyynl/ynl_gen_c.py`` to create easy to use netlink wrappers. | |
| YNL basics | |
| ---------- | |
| The YNL library consists of two parts - the generic code (functions | |
| prefix by ``ynl_``) and per-family auto-generated code (prefixed | |
| with the name of the family). | |
| To create a YNL socket call ynl_sock_create() passing the family | |
| struct (family structs are exported by the auto-generated code). | |
| ynl_sock_destroy() closes the socket. | |
| YNL requests | |
| ------------ | |
| Steps for issuing YNL requests are best explained on an example. | |
| All the functions and types in this example come from the auto-generated | |
| code (for the netdev family in this case): | |
| .. code-block:: c | |
| // 0. Request and response pointers | |
| struct netdev_dev_get_req *req; | |
| struct netdev_dev_get_rsp *d; | |
| // 1. Allocate a request | |
| req = netdev_dev_get_req_alloc(); | |
| // 2. Set request parameters (as needed) | |
| netdev_dev_get_req_set_ifindex(req, ifindex); | |
| // 3. Issues the request | |
| d = netdev_dev_get(ys, req); | |
| // 4. Free the request arguments | |
| netdev_dev_get_req_free(req); | |
| // 5. Error check (the return value from step 3) | |
| if (!d) { | |
| // 6. Print the YNL-generated error | |
| fprintf(stderr, "YNL: %s\n", ys->err.msg); | |
| return -1; | |
| } | |
| // ... do stuff with the response @d | |
| // 7. Free response | |
| netdev_dev_get_rsp_free(d); | |
| YNL dumps | |
| --------- | |
| Performing dumps follows similar pattern as requests. | |
| Dumps return a list of objects terminated by a special marker, | |
| or NULL on error. Use ``ynl_dump_foreach()`` to iterate over | |
| the result. | |
| YNL notifications | |
| ----------------- | |
| YNL lib supports using the same socket for notifications and | |
| requests. In case notifications arrive during processing of a request | |
| they are queued internally and can be retrieved at a later time. | |
| To subscribed to notifications use ``ynl_subscribe()``. | |
| The notifications have to be read out from the socket, | |
| ``ynl_socket_get_fd()`` returns the underlying socket fd which can | |
| be plugged into appropriate asynchronous IO API like ``poll``, | |
| or ``select``. | |
| Notifications can be retrieved using ``ynl_ntf_dequeue()`` and have | |
| to be freed using ``ynl_ntf_free()``. Since we don't know the notification | |
| type upfront the notifications are returned as ``struct ynl_ntf_base_type *`` | |
| and user is expected to cast them to the appropriate full type based | |
| on the ``cmd`` member. |