{"id":18791803,"url":"https://github.com/phip1611/generic-netlink-user-kernel-rust","last_synced_at":"2025-08-02T01:10:28.109Z","repository":{"id":45222309,"uuid":"284773310","full_name":"phip1611/generic-netlink-user-kernel-rust","owner":"phip1611","description":"Example that communicates between userland program (Rust and C) and Linux kernel module (written in C) via Generic Netlink. A custom Netlink family is created and used for this IPC.","archived":false,"fork":false,"pushed_at":"2025-03-02T12:08:42.000Z","size":142,"stargazers_count":22,"open_issues_count":0,"forks_count":3,"subscribers_count":3,"default_branch":"main","last_synced_at":"2025-06-17T11:08:10.031Z","etag":null,"topics":["ipc","libnl","linux-kernel","netlink","netlink-protocol","rust"],"latest_commit_sha":null,"homepage":"","language":"C","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/phip1611.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null},"funding":{"github":"phip1611"}},"created_at":"2020-08-03T18:11:53.000Z","updated_at":"2025-05-18T06:33:13.000Z","dependencies_parsed_at":"2025-04-13T14:32:05.962Z","dependency_job_id":"c5387d55-691c-4148-81b7-cf3df1624d76","html_url":"https://github.com/phip1611/generic-netlink-user-kernel-rust","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/phip1611/generic-netlink-user-kernel-rust","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/phip1611%2Fgeneric-netlink-user-kernel-rust","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/phip1611%2Fgeneric-netlink-user-kernel-rust/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/phip1611%2Fgeneric-netlink-user-kernel-rust/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/phip1611%2Fgeneric-netlink-user-kernel-rust/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/phip1611","download_url":"https://codeload.github.com/phip1611/generic-netlink-user-kernel-rust/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/phip1611%2Fgeneric-netlink-user-kernel-rust/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":268322414,"owners_count":24231819,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-08-01T02:00:08.611Z","response_time":67,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["ipc","libnl","linux-kernel","netlink","netlink-protocol","rust"],"created_at":"2024-11-07T21:16:55.418Z","updated_at":"2025-08-02T01:10:28.081Z","avatar_url":"https://github.com/phip1611.png","language":"C","funding_links":["https://github.com/sponsors/phip1611"],"categories":[],"sub_categories":[],"readme":"# Generic Netlink data transfer between userland (C \u0026 Rust) and Linux kernel using a custom Netlink family\n\nThis is a stripped down example about how to implement and use a simple protocol on top of the generic part of \nNetlink, an IPC channel in Linux kernel. I'm using a custom Netlink family that I create with Generic Netlink \nduring runtime with a custom Linux kernel module/driver.\n\nWhen you use Generic Netlink it is best to imagine to implement a \nprotocol yourself on top of Netlink. The protocol describes operations you want to trigger on the \nreceiving side as well as payload to transfer. In this example a string is passed from userland to a Linux\nkernel module and echo'ed back via Netlink.\n\nThis repository consists of a Linux Kernel Module (**developed with Linux 5.4**), that is written in C, and three \n**independent** userland components, that all act as standalone binaries and talk to the kernel module via \nNetlink. This tutorial covers Netlink topics like **validation**, **error reporting** (`NMLSG_ERROR` responses),\ndump calls (with `NMLSG_DONE` responses) as well as **regular data transfer**. The code is well and intensively \ndocumented, especially the kernel module. You can use this code as a starter template for own kernel modules with \ncustom Generic Netlink families.\n\nThe (standalone, independent) userland components are:\n1) a Rust program using [neli](https://crates.io/crate/neli) as abstraction/library for (Generic) Netlink\n2) a C program using [libnl](https://www.infradead.org/~tgr/libnl/) as abstraction/library for (Generic) Netlink\n3) a pure C program using raw sockets and no library for abstraction _(originally not my work; see below - but I \n   adjusted some parts)_\n\n*The pure C program (3) code is inspired of the code on [electronicsfaq.com](http://www.electronicsfaq.com/2014/02/generic-netlink-sockets-example-code.html)\nby (I don't know the author but from the comments I guess) **Anurag Chugh** \n([Blogger](https://www.blogger.com/profile/15390575283968794206), [Website](http://www.lithiumhead.com/)).*\n\n## TL;DR; Where To Start?\nCheck out the code in `kernel-mod/gnl_foobar_xmpl.c`. Build it and load the kernel module. Afterwards you can run \nseveral userland components from this repository against it. \n* `$ cargo run --bin echo`\n* `$ cargo run --bin echo_with_dump_flag`\n* `$ cargo run --bin reply_with_error`\n\n## Netlink vs Generic Netlink\nNetlink knows about *families* and each family has an ID statically assigned in the Kernel code, \nsee `\u003clinux/netlink.h\u003e`. Generic Netlink is one of these families and it can be used to create new families\non the fly/during runtime. A new family ID gets assigned temporarily for the name and with this ID you can\nstart to send or listen for messages on the given ID (in Kernel and in userland). Therefore the ID is used for adressing packets in Netlink.\n\nGeneric Netlink (`\u003clinux/genetlink.h\u003e`) also helps you in the kernel code with some convenient functions for \nreceiving and replying, and it offers `struct genlmsghdr`. The struct `struct genlmsghdr` offers the two \nimportant properties `cmd` and `version`. `cmd` is used to trigger a specific action on the receiving side \nwhereas `version` can be used to cope with different versions (e.g. older code, newer versions of your app).\n\nA ***Generic Netlink*** message is a message that has the following structure:\n![Overview Generic Netlink message](Generic%20Netlink%20Message%20Overview.png \"Overview Generic Netlink message. I use operation and command as synonyms.\")\n\n(I use \"Operation\" and \"Command\" as synonyms.) In fact, in this demo, we are transferring Generic Netlink messages, \nwhich are Netlink messages with a Generic Netlink header.\n\n## Let's talk about the code\n\nIn this example we create a Netlink family on the fly called \n`gnl_foobar_xmpl` using Generic Netlink. The common Netlink properties shared by the C projects (Kernel and \nthe two C userland components) are specified in `include/gnl_foobar_xmpl.h`. The very first step is the \nkernel module which needs to be loaded and register the Netlink family using Generic Netlink. Afterwards \nthe userland components can talk to it.\n\n## How to run\n- this needs at least Linux 5.*\n- `$ sudo apt install build-essential`\n- `$ sudo apt install libnl-3 libnl-genl-3`: for C example with `libnl`\n- `$ sudo apt install linux-headers-$(uname -r)`: useful only for easier Kernel Module development; Clion IDE can find headers\n- make sure rustup/cargo is installed: for Rust userland component\n- `$ sh ./build_and_run.sh`: builds and starts everything. Creates an output as shown below.)\n\n#### Example output\n```\n[User-Rust]: Generic family number is 34\n[User-Rust]: Sending 'Some data that has `Nl` trait implemented, like \u0026str' via netlink\n[User-Rust]: Received from kernel: 'Some data that has `Nl` trait implemented, like \u0026str'\n\n[User-C-Pure] extracted family id is: 34\n[User-C-Pure] Sent to kernel: Hello World from C user program (using raw sockets)!\n[User-C-Pure] Kernel replied: Hello World from C user program (using raw sockets)!\n\n[User-C-libnl] Family-ID of generic netlink family 'gnl_foobar_xmpl' is: 34\n[User-C-libnl] Sent to kernel: 'Hello World from Userland with libnl \u0026 libnl-genl'\n[User-C-libnl] Kernel replied: Hello World from Userland with libnl \u0026 libnl-genl\n\noutput of kernel log:\n[10144.393103] Generic Netlink Example Module inserted.\n[10144.461217] generic-netlink-demo-km: gnl_foobar_xmpl_cb_echo() invoked\n[10144.461218] received: 'Some data that has `Nl` trait implemented, like \u0026str'\n[10144.542496] generic-netlink-demo-km: gnl_foobar_xmpl_cb_echo() invoked\n[10144.542497] received: 'Hello World from C user program (using raw sockets)!'\n[10144.543104] generic-netlink-demo-km: gnl_foobar_xmpl_cb_echo() invoked\n[10144.543105] received: 'Hello World from Userland with libnl \u0026 libnl-genl'\n\n```\n\n## Measurement and comparison the userland components (abstractions cost)\nI measured the average time in all three userland components for establishing a Netlink connection,\nbuilding the message (payload), sending the ECHO request to the kernel, and receiving the reply all together.\nI ran every user component in release mode (optimized) and took the measurements inside the code. These are the\naverage durations in microseconds. I did 100,000 iterations for each measurement.\n\n| User Rust (`neli`) | User C (Pure) | User C (`libnl`) |\n|------------------|---------------|----------------|\n|             12µs |           8µs |           13µs |\n\nAbstractions cost us a little bit of time :) Using strace we can find that the Rust program and C (with `libnl`) \ndoing much more system calls. Before I measured this, I removed the loop (100,000 iterations, as mentioned above)\nagain and just did a single run. I executed the following statements which results in the table shown below.\n\n- `$ strace user-rust/target/release/user-rust 2\u003e\u00261 \u003e/dev/null | wc -l`\n- `$ strace ./user-pure 2\u003e\u00261 \u003e/dev/null | wc -l`\n- `$ strace ./user-libnl 2\u003e\u00261 \u003e/dev/null | wc -l` \\\n\n\n| User Rust (`neli`) | User C (Pure) | User C (`libnl`) |\n|--------------------|---------------|----------------|\n|       108 syscalls |   45 syscalls |   89 syscalls |\n\nLook into the [measurements/](https://github.com/phip1611/generic-netlink-user-kernel-rust/tree/main/measurements) \ndirectory of this repository, you can find the traces there. I didn't dived in deeper but you can clearly see that \n`libnl` and `neli` results in a lot more syscalls which explains the slower result.\n\n## Trivia\nI had to figure this out for an uni project and it was quite tough in the beginning, so I'd like to\nshare my findings with the open source world! Netlink documentation and tutorial across the web are not good\nand not centralized, you have to look up many things by yourself ad different sources. Perhaps not all my \ninformation are 100% right, but I want to share what works for me. I hope I can help you :)\nIf so, let me know on Twitter ([@phip1611](https://twitter.com/phip1611)).\n\n## Additional resources\n- [Generic Netlink HOW-TO based on Jamal's original doc](https://lwn.net/Articles/208755/)\n- [Generic Netlink HOW-TO on wiki.linuxfoundation.org](https://wiki.linuxfoundation.org/networking/generic_netlink_howto)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fphip1611%2Fgeneric-netlink-user-kernel-rust","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fphip1611%2Fgeneric-netlink-user-kernel-rust","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fphip1611%2Fgeneric-netlink-user-kernel-rust/lists"}