| .. 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. Only Generic Netlink is supported. |
| |
| The tool is located at ``tools/net/ynl/cli.py``. It accepts |
| a handul 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/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/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/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. |