Network Security Internet Technology Development Database Servers Mobile Phone Android Software Apple Software Computer Software News IT Information

In addition to Weibo, there is also WeChat

Please pay attention

WeChat public account

Shulou

How to analyze the source code and examples of Netlink

2025-01-16 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Servers >

Share

Shulou(Shulou.com)05/31 Report--

This article is about how to carry out Netlink source code and example analysis, the editor feels very practical, so share with you to learn, I hope you can learn something after reading this article, do not say much, follow the editor to have a look.

Preface

I came across something about netlink when I was looking at the code related to ipvs these days, so I spent some time in the past two days to sort out the things about netlink again.

What is netlink?

A serious problem existing in linux kernel is the interaction between kernel mode and user mode. For this problem, kernel bosses have been studying various methods to make the interaction between kernel and user mode safe and efficient. Such as system calls, proc,sysfs and other memory file systems, but these methods are generally relatively simple, can only be polled in user space to access kernel changes, kernel changes can not be actively pushed out.

The emergence of netlink solves this problem well, and netlink has the following advantages:

Hongmeng official Strategic Cooperation to build HarmonyOS Technology Community

You can directly use the API of the socket socket to communicate between the kernel and the user mode, which is relatively simple to develop and use.

Using the kernel protocol stack to have buffer queue, it is a kind of asynchronous communication mechanism.

It can be a two-way communication between the kernel and the user mode, and the kernel can actively send messages to the user mode process. This method of communication does not have in the past.

For all user processes of the same protocol type, the kernel can broadcast messages to all processes, or you can specify the process pid to send messages.

At present, this mechanism of netlink is widely used in various scenarios, and there are many applications that use netlink to communicate with the kernel in the Linux kernel, including: routing daemon (NETLINK_ROUTE), user mode socket protocol (NETLINK_USERSOCK), firewall (NETLINK_FIREWALL), netfilter subsystem (NETLINK_NETFILTER), kernel event notification to user state (NETLINK_KOBJECT_UEVENT) and so on. For the specific types of support, you can check out the file include/uapi/linux/netlink.h.

Netlink kernel code walkthrough

Introduction of netlink Kernel related Files

The kernel code of netlink is in the net/netlink/ directory of the kernel code. I am currently looking at the kernel version 5.7.10. There are not many files related to netlink kernel, so it is relatively clear:

The helightxu@  ~ / open_code/linux-5.7.10  ls net/netlink config Makefile af_netlink.c af_netlink.h diag.c genetlink.c helightxu@  ~ / open_code/linux-5.7.10  file describes af_netlink.c and af_netlink.h: is the core file of netlink, which is also read in detail below. Diag.c monitors netlink sock and can insert or uninstall genetlink.c from the kernel, which can be seen as an upgraded version of netlink, or a high-level encapsulation.

Note:

Genetlink.c additional note: netlink supports more than 30 scenarios by default, but there is no specific definition for other scenarios. At this time, this general encapsulation has a great advantage. You can expand the application scenario without changing the kernel. You can see this wiki: https://wiki.linuxfoundation.org/networking/generic_netlink_howto.

There is also a header file in the include directory, as shown below, this header file is some auxiliary functions, macro definitions and related data structures, we must look at this file, it contains very detailed comments. These comments are very useful for understanding the message structure of netlink, and it is recommended that you take a closer look.

Helightxu@  ~ / open_code/linux-5.7.10  ls include/net/netlink.h

Af_netlink.c code read-out

At the bottom of the af_netlink.c file is a line of code:

Core_initcall (netlink_proto_init)

What does this code mean? By looking at the final implementation of this code, you can see that the compiler is told to put the netlink_proto_init function into the .init section of the final compiled binary file, and the kernel will execute the functions one by one from this side at startup. This means that netlink is directly supported by the kernel by default and is part of the native kernel (here you want to distinguish it from the dynamic plug and unplug module of the kernel).

The most critical line of code in the netlink_proto_init function is the last line below to register the protocol family of netlink with the network protocol stack.

Static const struct net_proto_family netlink_family_ops = {.family = PF_NETLINK, .create = netlink_create, .owner = THIS_MODULE, / * for consistency 8) * /};. Sock_register & netlink_family_ops)

PF_NETLINK is the protocol family that represents netlink, which we will use later when we create netlink socket on the client side. As shown in the following code, the code comes from the client code in my test code https://github.com/helight/kernel_modules/tree/master/netlink_test. You can see that PF_NETLINK indicates that we are using netlink's protocol, and SOCK_RAW indicates that we are using the original protocol package, NETLINK_USER, a protocol field that we define ourselves. Netlink We mentioned earlier that there are more than 30 application scenarios, all of which are fixed in the kernel code, so when the client uses it, it will specify this field to represent the interaction with the function module of the application scenario in the kernel.

/ / int socket (int domain, int type, int protocol); sock_fd = socket (PF_NETLINK, SOCK_RAW, NETLINK_USER)

The main function of sock_register is to register the PF_NETLINK protocol type into the kernel, let the kernel know the protocol, and when establishing socket in the kernel network protocol, know which protocol to use to provide operational support for it.

Once registered, the kernel supports the netlink protocol, and the next step is to create a listening socket in the kernel and a link socket in the user mode.

Netlink user mode and kernel interaction process

Here I will briefly draw a diagram to show that there are two main operators for socket communication: the server side and the client side. How netlink works is as follows:

Object location-server-side kernel-client-side user-mode process-

Netlink key data structures and functions

Sockaddr_nl protocol socket

The address of netlink indicates that sockaddr_nl is in charge.

Struct sockaddr_nl {_ _ kernel_sa_family_t nl_family; / * AF_NETLINK * / unsigned short nl_pad; / * zero * / _ _ U32 nl_pid; / * port ID this is generally the process id * / _ _ U32 nl_groups; / * multicast groups mask * /}

Nl_family has developed a protocol family, and netlink has its own independent value: AF_NETLINK,nl_pid is generally taken as the process pid. Nl_groups is used for multicast, and this field is 0 when multicast is not needed.

Nlmsghdr message body

The netlink message is passed as the data part of the socket buffer sk_buff, and the message itself is divided into header and data. The head is:

Struct nlmsghdr {_ _ U32 nlmsg_len; / * Length of message including header * / _ _ U16 nlmsg_type; / * Message content * / _ _ U16 nlmsg_flags; / * Additional flags * / _ _ U32 nlmsg_seq; / * Sequence number * / _ _ U32 nlmsg_pid; / * Sending process port ID * /}

Nlmsg_len is the length of the message, including the header. Nlmsg_pid is the port ID of the sending process, which can be customized by the user, usually using the process pid.

Msghdr user state is the sending message body.

Use the sendmsg and recvmsg functions to send and receive messages, and the message body looks like this.

Struct iovec {/ * Scatter/gather array items * / void * iov_base; / * Starting address * / size_t iov_len; / * Number of bytes to transfer * /}; / * iov_base: iov_base points to the packet buffer, that is, the parameter buff,iov_len is the length of the buff. In msghdr, multiple buff can be passed at a time. When organized in msg_iov as an array, msg_iovlen records the length of the array (that is, how many buff there are) * / struct msghdr {void * msg_name; / * Socket name * / int msg_namelen; / * Length of name * / struct iovec * msg_iov / * Data blocks * / _ _ kernel_size_t msg_iovlen; / * Number of blocks * / void * msg_control; / * Per protocol magic (eg BSD file descriptor passing) * / _ _ kernel_size_t msg_controllen; / * Length of cmsg list * / unsigned int msg_flags;} / * msg_name: the destination address of the data. The network packet points to sockaddr_in, and netlink points to sockaddr_nl. Msg_namelen: address length represented by msg_name msg_iov: points to buffer array msg_iovlen: buffer array length msg_control: auxiliary data, control information (send any control information) msg_controllen: auxiliary information length msg_flags: message ID * /

The logical structure is as follows:

Socket is also a special file, which can also be used and managed through the interface of VFS. Socket itself needs to implement the corresponding interface of the file system and has its own set of operating methods.

Netlink common macros

# define NLMSG_ALIGNTO 4U NLMSG_ALIGN * Macro NLMSG_ALIGN (len) is used to get the minimum value of not less than len and byte alignment * / # define NLMSG_ALIGN (len) (len) + NLMSG_ALIGNTO-1) & ~ (NLMSG_ALIGNTO-1)) / * Netlink header length * / # define NLMSG_HDRLEN ((int) NLMSG_ALIGN (sizeof (struct nlmsghdr) / * calculate the truth of message data len Real message length (message body + header) * / # define NLMSG_LENGTH (len) ((len) + NLMSG_HDRLEN) / * Macro NLMSG_SPACE (len) returns a minimum value of not less than NLMSG_LENGTH (len) and byte alignment * / # define NLMSG_SPACE (len) NLMSG_ALIGN (NLMSG_LENGTH (len)) / * Macro NLMSG_DATA (nlh) is used to obtain the first address of the data part of the message You need to use this macro * / # define NLMSG_DATA (nlh) ((void*) ((char*) nlh) + NLMSG_LENGTH (0)) / * macro NLMSG_NEXT (nlh,len) to get the first address of the next message when setting up and reading the message data part, and len becomes the length of the remaining message * / # define NLMSG_NEXT (nlh,len) ((len)-= NLMSG_ALIGN ((nlh)-> nlmsg_len)) \ (struct nlmsghdr*) (char*) (nlh)) + NLMSG_ALIGN ((nlh)-> nlmsg_len)) / * determine whether the message is > len * / # define NLMSG_OK (nlh) Len) ((len) > = (int) sizeof (struct nlmsghdr) & &\ (nlh)-> nlmsg_len > = sizeof (struct nlmsghdr) & &\ (nlh)-> nlmsg_len nlmsg_len-NLMSG_SPACE ((len)

Common functions in netlink kernel

Netlink_kernel_create

This kernel function is used to create a kernel socket to provide communication with the user mode

Static inline struct sock * netlink_kernel_create (struct net * net, int unit, struct netlink_kernel_cfg * cfg) / * net: point to the network namespace in which it resides. By default, & init_net is passed (no definition is required); defined in net_namespace.c (extern struct net init_net) Unit: netlink protocol type cfg: cfg stores netlink kernel configuration parameters (below) * / * optional Netlink kernel configuration parameters * / struct netlink_kernel_cfg {unsigned int groups; unsigned int flags; void (* input) (struct sk_buff * skb); / * input callback function * / struct mutex * cb_mutex; void (* bind) (int group) Bool (* compare) (struct net * net, struct sock * sk);}

Unicast function netlink_unicast () and Multicast function netlink_broadcast ()

/ * to send unicast messages * / extern int netlink_unicast (struct sock * ssk, struct sk_buff * skb, _ _ U32 portid, int nonblock) / * ssk: netlink socket skb: skb buff pointer portid: the port number of the communication nonblock: indicates whether the function is non-blocking. If 1, the function will return immediately when no receive cache is available, and if 0 This function can use timed sleep * / / * to send multicast messages * / extern int netlink_broadcast (struct sock * ssk, struct sk_buff * skb, _ _ U32 portid, _ _ U32 group, gfp_t allocation) without receiving cache. / * ssk: same as above (corresponding to the returned value of netlink_kernel_create), skb: kernel skb buff portid: Port id group: operate on the "OR" of all destination multicast groups corresponding to the mask. Allocation: specifies how kernel memory is allocated. GFP_ATOMIC is usually used for interrupt context, while GFP_KERNEL is used for other situations. This parameter exists because the API may need to allocate one or more buffers to clone multicast messages. , /

Test the sample code

The process of establishing socket in netlink kernel

The kernel code is very simple, the core code is given here, and that's all, the received message is printed directly in the receiver function.

# include # define NETLINK_XUX 31 / * testing * / static struct sock * xux_sock = NULL; / / callback function to receive messages. The receive parameter is sk_buff static void recv_netlink (struct sk_buff * skb) {struct nlmsghdr * nlh; nlh = nlmsg_hdr (skb) / / get the message body printk ("receive data from user process:% s", (char *) NLMSG_DATA (nlh)); / / print the received data content.} int _ init init_link (void) {struct netlink_kernel_cfg cfg = {.input = recv_netlink,}; xux_sock = netlink_kernel_create (& init_net, NETLINK_XUX, & cfg) / / create kernel socket if (! xux_sock) {printk ("cannot initialize netlink socket"); return-1;} printk ("Init OK!\ n"); return 0;}

Establish links and send and receive messages in netlink user mode

. / / save # define NETLINK_USER 31 / / self defined # define MAX_PAYLOAD 1024 / * maximum payload size*/ struct sockaddr_nl src_addr, dest_addr; struct nlmsghdr * nlh = NULL; struct msghdr msg; struct iovec iov; int sock_fd; int main (int args, char * argv []) {sock_fd = socket (PF_NETLINK, SOCK_RAW, NETLINK_USER); / / create socket if (sock_fd)

< 0) return -1; memset(&src_addr, 0, sizeof(src_addr)); src_addr.nl_family = AF_NETLINK; src_addr.nl_pid = getpid(); /* 当前进程的 pid */ if(bind(sock_fd, (struct sockaddr*)&src_addr, sizeof(src_addr))){ // 和指定协议进行 socket 绑定 perror("bind() error\n"); close(skfd); return -1; } memset(&dest_addr, 0, sizeof(dest_addr)); dest_addr.nl_family = AF_NETLINK; dest_addr.nl_pid = 0; /* For Linux Kernel */ dest_addr.nl_groups = 0; /* unicast */ nlh = (struct nlmsghdr *)malloc(NLMSG_SPACE(MAX_PAYLOAD)); memset(nlh, 0, NLMSG_SPACE(MAX_PAYLOAD)); nlh->

Nlmsg_len = NLMSG_SPACE (MAX_PAYLOAD); nlh- > nlmsg_pid = getpid (); / / self pid nlh- > nlmsg_flags = 0; / / copy the information to the sending buffer strcpy (NLMSG_DATA (nlh), "Hello this is a msg from userspace"); / / construct the sending message body iov.iov_base = (void *) nlh; / / iov-> nlh iov.iov_len = nlh- > nlmsg_len Msg.msg_name = (void *) & dest_addr; msg.msg_namelen = sizeof (dest_addr); msg.msg_iov = & iov; / / iov stores netlink headers and message data msg.msg_iovlen = 1; printf ("Sending message to kernel\ n"); int ret = sendmsg (sock_fd, & msg, 0); / / send messages to kernel printf ("send ret:% d\ n", ret) Printf ("Waiting for message from kernel\ n"); / * receive messages from the kernel * / recvmsg (sock_fd, & msg, 0); printf ("Received message payload:% s\ n", NLMSG_DATA (nlh)); / / print received messages close (sock_fd); return 0;}

Netlink currently feels like an easy way to interact between kernel and user space, but it also has its own usage scenario, which is suitable for active interaction between user space and kernel space.

But in the stand-alone scenario, most of the initiative lies in the user process, the user process writes data to the kernel, and the user process actively reads the kernel data. These two scenarios cover most of the kernel scenarios.

Netlink is more suitable in scenarios where the kernel needs to be active. All I can think of is kernel data auditing, security triggering, and so on. In such scenarios, the kernel can inform the user in real time what is happening to the process kernel.

When I was looking at the code of ipvs, I saw the use of netlink in it. I found that early iptables used netlink to issue configuration instructions. There is also this part of the code in netfilter and iptables in the kernel. I also downloaded it today and roughly read it. You can search for the keyword NETLINK. But iptables later code did not use this approach, but used a library called iptc, whose core idea is to use setsockops, and ultimately copy_from_user. However, this method is very practical for scenarios like iptables.

The above is how to analyze the source code and examples of Netlink. The editor believes that there are some knowledge points that we may see or use in our daily work. I hope you can learn more from this article. For more details, please follow the industry information channel.

Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.

Views: 0

*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.

Share To

Servers

Wechat

© 2024 shulou.com SLNews company. All rights reserved.

12
Report