<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Zdenko Vrabel</title>
    <description>The latest articles on DEV Community by Zdenko Vrabel (@sn3d).</description>
    <link>https://dev.to/sn3d</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F408950%2F0c6258d2-6fe5-47ee-8650-9634b13bc3dd.jpeg</url>
      <title>DEV Community: Zdenko Vrabel</title>
      <link>https://dev.to/sn3d</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/sn3d"/>
    <language>en</language>
    <item>
      <title>Introduction to Netlink with Go</title>
      <dc:creator>Zdenko Vrabel</dc:creator>
      <pubDate>Sun, 28 Apr 2024 11:44:30 +0000</pubDate>
      <link>https://dev.to/sn3d/introduction-to-netlink-with-go-b7f</link>
      <guid>https://dev.to/sn3d/introduction-to-netlink-with-go-b7f</guid>
      <description>&lt;p&gt;A few years back, I got really interested in understanding how Docker and containers work. One area that grabbed my attention was containers and networking. Docker handles networking pretty simply. It mainly relies on virtual interfaces, bridges, NAT routing, and other features provided by the Linux operating system. But I won't be focusing on Docker and networking in this article.&lt;/p&gt;

&lt;p&gt;As I delved deeper, I became curious about how Docker do all this networking. If you're like me—a developer who spends almost all their time in user space—you're probably familiar with interacting with the kernel through syscalls or the filesystem (like &lt;code&gt;/proc&lt;/code&gt;). At first, I had this naive idea that OS had some kind of magic syscall or something in &lt;code&gt;/proc&lt;/code&gt; that Docker was tapping into. But nope, I was way off. In Docker's code, I discovered another method: Netlink.&lt;/p&gt;

&lt;p&gt;So, what is Netlink? It's another way of exchanging information between user space and the kernel. But this time, the socket-like way. Think of Netlink as a direct socket connection into the kernel, allowing you to send and receive messages. This approach is quite interesting, because I can do asynchronous communication with the kernel or simply listening for messages from the kernel in user space.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8mvfknvofvi5lh0zkqup.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8mvfknvofvi5lh0zkqup.png" alt="netlink" width="800" height="546"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;With Netlink, I can communicate with various kernel subsystems. For example, I can receive events from SELinux, updates about routing or network links, and even modify routing tables and IP addresses.&lt;/p&gt;

&lt;p&gt;If you're comfortable with basic socket programming like me, handling Netlink could be easy to understand. All you need to do is open a socket to the kernel, address the subsystem, and send or receive binary messages.&lt;/p&gt;

&lt;h2&gt;
  
  
  Bring interface UP
&lt;/h2&gt;

&lt;p&gt;Let's get practical here. Let's start with something super basic—like a Netlink &lt;code&gt;hello world&lt;/code&gt;. One of the simplest examples I could think of is enabling an network interface. On my system, I've got this &lt;code&gt;veth0&lt;/code&gt; interface sitting there, in the &lt;code&gt;DOWN&lt;/code&gt; state:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;ip &lt;span class="nb"&gt;link&lt;/span&gt;
...
78: veth0@veth1: &amp;lt;BROADCAST,MULTICAST&amp;gt; mtu 1500 qdisc noqueue state DOWN mode DEFAULT group default qlen 1000
    &lt;span class="nb"&gt;link&lt;/span&gt;/ether de:8f:4e:7e:9c:cd brd ff:ff:ff:ff:ff:ff
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I'd like to write my own simple Go program to execute &lt;code&gt;ip link set veth1 up&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;One note before I start: Since I want to use syscalls and related data structures, I'll need to install the Go &lt;code&gt;golang.org/x/sys&lt;/code&gt; package.&lt;/p&gt;

&lt;p&gt;Let's begin by establishing the socket. The Netlink socket is created using &lt;code&gt;unix.Socket()&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="c"&gt;// open the Netlink socket &lt;/span&gt;
&lt;span class="n"&gt;sock&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;unix&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Socket&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;unix&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AF_NETLINK&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;unix&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SOCK_RAW&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;unix&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NETLINK_ROUTE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Error creating socket: %s&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;defer&lt;/span&gt; &lt;span class="n"&gt;unix&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Close&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sock&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Instead of the &lt;code&gt;AF_INET&lt;/code&gt; domain, which we typically use for TCP/IP communication, I'm using &lt;code&gt;AF_NETLINK&lt;/code&gt;. What's also interesting here is the last parameter. This value determines which subsystem I want to communicate with. It could be &lt;code&gt;NETLINK_NETFILTER&lt;/code&gt;, &lt;code&gt;NETLINK_SELINUX&lt;/code&gt;, and so on. I'm opting for &lt;code&gt;NETLINK_ROUTE&lt;/code&gt;, which is dedicated to interfaces, links, IP addresses, and such.&lt;/p&gt;

&lt;p&gt;However, just having the socket isn't enough. In TCP/IP communication, we associate a socket with a specific network address using &lt;code&gt;bind()&lt;/code&gt;. Similarly, in Netlink, I'll set what group and port ID (PID) I want to use. For simplicity, I'll use values of &lt;code&gt;0&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="c"&gt;// bind the socket to group and PID&lt;/span&gt;
&lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;unix&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Bind&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sock&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;unix&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SockaddrNetlink&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;Family&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;unix&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AF_NETLINK&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;Groups&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;Pid&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Error in binding socket: %s&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Create and send message
&lt;/h2&gt;

&lt;p&gt;The easy part is done. At this point, I've got everything ready for sending and receiving messages. Now comes the tricky part—building and parsing Netlink messages. Like in many binary protocols, the Netlink message consists of a header and payload.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fe2ke5n4sguqd7vckshdx.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fe2ke5n4sguqd7vckshdx.png" alt="Message" width="800" height="312"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let's start from the end—with the payload. The payload is all about what I want to do or get from Netlink. In my case, it's about enabling a network interface. Therefore, I'll use &lt;code&gt;IfInfomsg&lt;/code&gt;, where I'll set the &lt;code&gt;Change&lt;/code&gt; field to &lt;code&gt;IFF_UP&lt;/code&gt;. The good news is that the structure is available in the &lt;code&gt;golang.org/x/sys&lt;/code&gt; package, so I don't need to write it from scratch.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="n"&gt;payload&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;unix&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;IfInfomsg&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;Family&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;unix&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AF_UNSPEC&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;Change&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;unix&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;IFF_UP&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;Flags&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;  &lt;span class="n"&gt;unix&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;IFF_UP&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;Index&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;  &lt;span class="kt"&gt;int32&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ethIndex&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="c"&gt;// index of network interface I would like to enable (in my case it's 79 - veth1)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, I need to build a header. The header carries information like the type of the payload or the total length of the whole message. The structure for the header is &lt;code&gt;NlMsghdr&lt;/code&gt;. The type I need to set is &lt;code&gt;RTM_NEWLINK&lt;/code&gt;, which is related to the &lt;code&gt;IfInfomsg&lt;/code&gt; payload.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="c"&gt;// total length of message is size of header + size of payload&lt;/span&gt;
&lt;span class="n"&gt;length&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;unix&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SizeofNlMsghdr&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;unix&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SizeofIfInfomsg&lt;/span&gt;

&lt;span class="n"&gt;header&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;unix&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NlMsghdr&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;Len&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;   &lt;span class="kt"&gt;uint32&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;length&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;Type&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;  &lt;span class="kt"&gt;uint16&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;unix&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RTM_NEWLINK&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;Flags&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;uint16&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;unix&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NLM_F_REQUEST&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kt"&gt;uint16&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;unix&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NLM_F_ACK&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;Seq&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;   &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Alright, the message should be almost ready. I just need to put the header and payload into one message structure. I'll create an anonymous structure and fill it with the payload and header:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="n"&gt;msg&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="n"&gt;header&lt;/span&gt; &lt;span class="n"&gt;unix&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NlMsghdr&lt;/span&gt;
   &lt;span class="n"&gt;payload&lt;/span&gt; &lt;span class="n"&gt;unix&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;IfInfomsg&lt;/span&gt;
&lt;span class="p"&gt;}{&lt;/span&gt;
   &lt;span class="n"&gt;header&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;header&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
   &lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The message is ready, and I could write the message data into the socket. But before I call &lt;code&gt;Sendto()&lt;/code&gt;, I need to convert the message structure to an array (or slice) of bytes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="c"&gt;// first I need convert the `msg` to slice of bytes&lt;/span&gt;
&lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;asByteSlice&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="kt"&gt;byte&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;unix&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SizeofNlMsghdr&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;unix&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SizeofIfInfomsg&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="kt"&gt;byte&lt;/span&gt;&lt;span class="p"&gt;)(&lt;/span&gt;&lt;span class="n"&gt;unsafe&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Pointer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;)))[&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="c"&gt;// write the data to the socket&lt;/span&gt;
&lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;unix&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Sendto&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sock&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;asByteSlice&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;unix&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SockaddrNetlink&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;Family&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;unix&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AF_NETLINK&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Could not write message to socket:%s&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Receiving message
&lt;/h2&gt;

&lt;p&gt;At this point, if I compile and run my simple program with root privileges, the code will bring the &lt;code&gt;veth1&lt;/code&gt; interface up. Mission accomplished. Right? But what about receiving messages?&lt;/p&gt;

&lt;p&gt;Receiving messages might be complicated. The messages might be large, or the information might be broken into multiple pieces. There are various factors to consider. But I'll stick with my simple scenario. I just want to know if my &lt;code&gt;up&lt;/code&gt; operation failed or if it was successful.&lt;/p&gt;

&lt;p&gt;To receive the response, I'll use &lt;code&gt;unix.Recvfrom()&lt;/code&gt;, which will read all remaining data from the socket into the &lt;code&gt;buf&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;buf&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="m"&gt;1024&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="kt"&gt;byte&lt;/span&gt;
&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;unix&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Recvfrom&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sock&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;buf&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Could not read data from socket: %s&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The next step is parsing the received raw data. Here, I'll use &lt;code&gt;ParseNetlinkMessage()&lt;/code&gt; to do just that.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="c"&gt;// parse data to messages&lt;/span&gt;
&lt;span class="n"&gt;msgs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;syscall&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ParseNetlinkMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;buf&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Could not parse the response: %s&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The function will return parsed data as an array of &lt;code&gt;[]NetlinkMessage&lt;/code&gt;. The &lt;code&gt;NetlinkMessage&lt;/code&gt; is a simple structure with &lt;code&gt;Header&lt;/code&gt; and &lt;code&gt;Data&lt;/code&gt;. The &lt;code&gt;Header&lt;/code&gt; is &lt;code&gt;NlMsghdr&lt;/code&gt;, and &lt;code&gt;Data&lt;/code&gt; is an array of bytes. Based on the type in the &lt;code&gt;Header&lt;/code&gt;, I can cast the &lt;code&gt;Data&lt;/code&gt; to the proper type. In my case, the first response message will be &lt;code&gt;NLMSG_ERROR&lt;/code&gt;, so I'll cast &lt;code&gt;Data&lt;/code&gt; to &lt;code&gt;NlMsgerr&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="c"&gt;// the first received message must be `NLMSG_ERROR`&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;msgs&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Header&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Type&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="n"&gt;unix&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NLMSG_ERROR&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"The first received message is not NLMSG_ERROR&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;// cast the data to NlMsgerr payload&lt;/span&gt;
&lt;span class="n"&gt;errPayload&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;unix&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NlMsgerr&lt;/span&gt;&lt;span class="p"&gt;)(&lt;/span&gt;&lt;span class="n"&gt;unsafe&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Pointer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;resp&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]))&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;errPayload&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Error&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Error returned by Netlink&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Interface is UP&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The full code is available on &lt;a href="https://github.com/sn3d/netlink-example"&gt;github.com/sn3d/netlink-example&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Let's try...
&lt;/h2&gt;

&lt;p&gt;It's time to play with my program. As I mentioned above, I have &lt;code&gt;veth1&lt;/code&gt; present in my system which is &lt;code&gt;DOWN&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;ip &lt;span class="nb"&gt;link&lt;/span&gt;
...
78: veth0@veth1: &amp;lt;BROADCAST,MULTICAST&amp;gt; mtu 1500 qdisc noqueue state DOWN mode DEFAULT group default qlen 1000
    &lt;span class="nb"&gt;link&lt;/span&gt;/ether de:8f:4e:7e:9c:cd brd ff:ff:ff:ff:ff:ff

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you look closer, you might see the &lt;code&gt;veth0&lt;/code&gt; have index &lt;code&gt;78&lt;/code&gt;. I need this index pass to my program. Now when I run my program with this index, I should get information &lt;code&gt;Interface is UP&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;go run main.go 78
Interface is UP
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is not just a message from my program. If I will check the &lt;code&gt;veth0&lt;/code&gt;, I will notice the interface is &lt;code&gt;UP&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;ip &lt;span class="nb"&gt;link&lt;/span&gt;
...
78: veth0@veth1: &amp;lt;BROADCAST,MULTICAST,UP,LOWER_UP&amp;gt; mtu 1500 qdisc noqueue state UP mode DEFAULT group default qlen 1000
    &lt;span class="nb"&gt;link&lt;/span&gt;/ether de:8f:4e:7e:9c:cd brd ff:ff:ff:ff:ff:ff
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  How to debug messages
&lt;/h2&gt;

&lt;p&gt;As you noticed, creating a Netlink connection, reading from, and writing to the socket is the easy part. Maybe reading bigger chunks of data or data that's broken into smaller chunks is more complicated, but it's something we're familiar with from socket programming.&lt;/p&gt;

&lt;p&gt;The tricky part for me was creating a proper Netlink message. But there's a pretty useful way to debug and observe Netlink messages - using &lt;code&gt;strace&lt;/code&gt;. Modern &lt;code&gt;strace&lt;/code&gt; has a great feature - it can parse and understand Netlink messages.&lt;/p&gt;

&lt;p&gt;If you try to execute &lt;code&gt;ip link set veth0 up&lt;/code&gt; with &lt;code&gt;strace&lt;/code&gt;, you might see &lt;code&gt;sendmsg&lt;/code&gt; with a parsed Netlink message:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;strace &lt;span class="nt"&gt;-Tfe&lt;/span&gt; &lt;span class="nv"&gt;trace&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;sendmsg ip &lt;span class="nb"&gt;link set &lt;/span&gt;veth0 up
endmsg&lt;span class="o"&gt;(&lt;/span&gt;4, &lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="nv"&gt;msg_name&lt;/span&gt;&lt;span class="o"&gt;={&lt;/span&gt;&lt;span class="nv"&gt;sa_family&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;AF_NETLINK, &lt;span class="nv"&gt;nl_pid&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0, &lt;span class="nv"&gt;nl_groups&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;00000000&lt;span class="o"&gt;}&lt;/span&gt;, &lt;span class="nv"&gt;msg_namelen&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;12, &lt;span class="nv"&gt;msg_iov&lt;/span&gt;&lt;span class="o"&gt;=[{&lt;/span&gt;&lt;span class="nv"&gt;iov_base&lt;/span&gt;&lt;span class="o"&gt;=[{&lt;/span&gt;&lt;span class="nv"&gt;nlmsg_len&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;52, &lt;span class="nv"&gt;nlmsg_type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;RTM_GETLINK, &lt;span class="nv"&gt;nlmsg_flags&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;NLM_F_REQUEST, &lt;span class="nv"&gt;nlmsg_seq&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1714051690, &lt;span class="nv"&gt;nlmsg_pid&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0&lt;span class="o"&gt;}&lt;/span&gt;, &lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="nv"&gt;ifi_family&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;AF_UNSPEC, &lt;span class="nv"&gt;ifi_type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;ARPHRD_NETROM, &lt;span class="nv"&gt;ifi_index&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0, &lt;span class="nv"&gt;ifi_flags&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0, &lt;span class="nv"&gt;ifi_change&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0&lt;span class="o"&gt;}&lt;/span&gt;, &lt;span class="o"&gt;[[{&lt;/span&gt;&lt;span class="nv"&gt;nla_len&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;8, &lt;span class="nv"&gt;nla_type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;IFLA_EXT_MASK&lt;span class="o"&gt;}&lt;/span&gt;, RTEXT_FILTER_VF|RTEXT_FILTER_SKIP_STATS], &lt;span class="o"&gt;[{&lt;/span&gt;&lt;span class="nv"&gt;nla_len&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;10, &lt;span class="nv"&gt;nla_type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;IFLA_IFNAME&lt;span class="o"&gt;}&lt;/span&gt;, &lt;span class="s2"&gt;"veth0"&lt;/span&gt;&lt;span class="o"&gt;]]]&lt;/span&gt;, &lt;span class="nv"&gt;iov_len&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;52&lt;span class="o"&gt;}]&lt;/span&gt;, &lt;span class="nv"&gt;msg_iovlen&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1, &lt;span class="nv"&gt;msg_controllen&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0, &lt;span class="nv"&gt;msg_flags&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0&lt;span class="o"&gt;}&lt;/span&gt;, 0&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; 52 &amp;lt;0.000094&amp;gt;
sendmsg&lt;span class="o"&gt;(&lt;/span&gt;3, &lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="nv"&gt;msg_name&lt;/span&gt;&lt;span class="o"&gt;={&lt;/span&gt;&lt;span class="nv"&gt;sa_family&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;AF_NETLINK, &lt;span class="nv"&gt;nl_pid&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0, &lt;span class="nv"&gt;nl_groups&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;00000000&lt;span class="o"&gt;}&lt;/span&gt;, &lt;span class="nv"&gt;msg_namelen&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;12, &lt;span class="nv"&gt;msg_iov&lt;/span&gt;&lt;span class="o"&gt;=[{&lt;/span&gt;&lt;span class="nv"&gt;iov_base&lt;/span&gt;&lt;span class="o"&gt;=[{&lt;/span&gt;&lt;span class="nv"&gt;nlmsg_len&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;32, &lt;span class="nv"&gt;nlmsg_type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;RTM_NEWLINK, &lt;span class="nv"&gt;nlmsg_flags&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;NLM_F_REQUEST|NLM_F_ACK, &lt;span class="nv"&gt;nlmsg_seq&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1714051690, &lt;span class="nv"&gt;nlmsg_pid&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0&lt;span class="o"&gt;}&lt;/span&gt;, &lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="nv"&gt;ifi_family&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;AF_UNSPEC, &lt;span class="nv"&gt;ifi_type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;ARPHRD_NETROM, &lt;span class="nv"&gt;ifi_index&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;if_nametoindex&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"veth0"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;, &lt;span class="nv"&gt;ifi_flags&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;IFF_UP, &lt;span class="nv"&gt;ifi_change&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0x1&lt;span class="o"&gt;}]&lt;/span&gt;, &lt;span class="nv"&gt;iov_len&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;32&lt;span class="o"&gt;}]&lt;/span&gt;, &lt;span class="nv"&gt;msg_iovlen&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1, &lt;span class="nv"&gt;msg_controllen&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0, &lt;span class="nv"&gt;msg_flags&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0&lt;span class="o"&gt;}&lt;/span&gt;, 0&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; 32 &amp;lt;0.000064&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It's a bit messy output, but you could notice 2 &lt;code&gt;sendmsg&lt;/code&gt; calls. One is for &lt;code&gt;RTM_GETLINK&lt;/code&gt;, and the second is for &lt;code&gt;RTM_NEWLINK&lt;/code&gt;. In this output, you might see the header and payload. For instance, the header of the second &lt;code&gt;RTM_NEWLINK&lt;/code&gt; is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{nlmsg_len=32, nlmsg_type=RTM_NEWLINK, nlmsg_flags=NLM_F_REQUEST|NLM_F_ACK, nlmsg_seq=1714051690, nlmsg_pid=0}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And the payload is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{ifi_family=AF_UNSPEC, ifi_type=ARPHRD_NETROM, ifi_index=if_nametoindex("veth0"), ifi_flags=IFF_UP, ifi_change=0x1}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With &lt;code&gt;strace&lt;/code&gt;, we could study requests like creating a bridge, etc., and reproduce the messages from our code.&lt;/p&gt;

&lt;h2&gt;
  
  
  Netlink library
&lt;/h2&gt;

&lt;p&gt;Working with sockets, building, and parsing our own messages require quite a lot of work. Thanks to Vish Abrams, we can use in our project the package &lt;code&gt;github.com/vishvananda/netlink&lt;/code&gt;, which provides a lot of Netlink functionalities without having to build our own message structures from scratch. It's well-maintained and used by many projects like Docker, Cilium, Flannel, Istio, etc.&lt;/p&gt;

&lt;p&gt;Thanks to this library, adding a new bridge to the system is a matter of a few lines:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="n"&gt;la&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;netlink&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewLinkAttrs&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;la&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"docker0"&lt;/span&gt;
&lt;span class="n"&gt;dockerBridge&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;netlink&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Bridge&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;LinkAttrs&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;la&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;netlink&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;LinkAdd&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dockerBridge&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  A few words in conclusion...
&lt;/h2&gt;

&lt;p&gt;One important aspect I didn't mention earlier is that the byte order in messages depends on the host's CPU architecture. This means we don't need to worry about converting between little and big-endian for integers. Additionally, it's crucial that our messages follow a four-byte padding rule. For example, if a message is 33 bytes long, we'll need to send 36 bytes.&lt;/p&gt;

&lt;p&gt;I wrote about Netlink almost three years ago, but it was in Slovak. I decided to write this English version because, even after three years, I still find Netlink interesting and worth studying and trying.&lt;/p&gt;

</description>
      <category>linux</category>
      <category>containers</category>
      <category>networking</category>
      <category>go</category>
    </item>
  </channel>
</rss>
