Use Netlink to communicate with applications in the kernel
Some time ago, during the development of a driver, communication between the driver and the application was required. The driver notifies the application to handle a hardware interruption. To solve this problem, the driver provides several mechanisms: (1) using the copy_to_user/copy_from_user method, the disadvantage is that the communication response time is too long (2) using a letter number, but limited to character devices (3) use Netlink.
The Netlink mechanism was introduced after linux2.4. It will be one of the main methods for communication between Linux user States and kernel states. Netlink supports the interrupt process. That is to say, you can directly call Netlink functions in the interrupt program. When receiving user space data in the kernel space, it does not need to start a kernel thread on its own. Instead, it calls the receiving function specified in advance through another Soft Interrupt. The communication process of Netlink is as follows:
The following describes how to use Netlink in two parts: user space and kernel space.
1. user space program
Your applications use standard Socket socket to communicate with the kernel space. Functions of the standard socket API include socket (), BIND (), sendmsg (), recvmsg (), and close () it is easy to apply to netlink socket.
Program code:
# Define <include/Linux/Netlink. h>
Struct u_packet_info
{
Struct nlmsghdr HDR;
Struct packet_info p_info;
};
Struct u_packet_info Info;
/* Custom message header, which only contains the Netlink message header */
Struct msg_to_kernel
{
Struct nlmsghdr HDR;
};
Struct msg_to_kernel message;
Static int skfd, kpeerlen, rcvlen;
Struct sockaddr_nl local;
Struct sockaddr_nl kpeer;
Struct msg_to_kernel message;
Skfd = socket (af_netlink, sock_raw, netlink_generic );
If (skfd <0)
{
Printf ("can not create a netlink socket/N ");
Exit (0 );
}
Memset (& Local, 0, sizeof (local ));
Local. nl_family = af_netlink;
Local. nl_pid = getpid ();/* Set PID to your own PID value */
Local. nl_groups = 0;
/* Bind a socket */
If (BIND (skfd, (struct sockaddr *) & Local, sizeof (local ))! = 0)
{
Printf ("BIND () Error/N ");
Return-1;
}
Memset (& kpeer, 0, sizeof (kpeer ));
Kpeer. nl_family = af_netlink;
Kpeer. nl_pid = 0;
Kpeer. nl_groups = 0;
Memset (& message, 0, sizeof (Message ));
/* Calculate the message. Because only one request message is sent, there is no extra data. Therefore, the data length is 0 */
Message. HDR. nlmsg_len = nlmsg_length (0 );
Message. HDR. nlmsg_flags = 0;
Message. HDR. nlmsg_type = user_type;/* set the custom message type */
Message. HDR. nlmsg_pid = Local. nl_pid;/* set the PID of the sender */
/* Send a request */
Sendto (skfd, & message, message. HDR. nlmsg_len, 0, (struct sockaddr *) & kpeer, sizeof (kpeer ));
While (1)
{
Kpeerlen = sizeof (struct sockaddr_nl );
/* Receive the data returned by the kernel space */
Rcvlen = recvfrom (skfd, & info, sizeof (struct u_packet_info), 0, (struct sockaddr *) & kpeer, & kpeerlen );
/* Process received data */
......
}
As you can see, the application process is similar to the general socket program.
1) Call socket to create a socket.
The protocol cluster corresponding to Netlink is af_netlink. The second parameter must be sock_raw or sock_dgram. The third parameter specifies the Netlink protocol type, which can be a custom type, you can also use the predefined kernel types in the file include/Linux/Netlink. h.
# Define netlink_route 0/* routing/device hook */
# Define netlink_w1 1/* 1-wire subsystem */
# Define netlink_usersock 2/* Reserved for user mode socket protocols */
# Define netlink_firewall 3/* firewalling hook */
# Define netlink_inet_diag 4/* Inet socket monitoring */
# Define netlink_nflog 5/* Netfilter/iptables ulog */
# Define netlink_xfrm 6/* IPSec */
# Define netlink_selinux 7/* SELinux Event Notifications */
# Define netlink_iscsi 8/* Open-iSCSI */
# Define netlink_audit 9/* auditing */
# Define netlink_fib_lookup 10
# Define netlink_connector 11
# Define netlink_netfilter 12/* netfilter subsystem */
# Define netlink_ip6_fw 13
# Define netlink_dnrtmsg 14/* decnet routing messages */
# Define netlink_kobject_uevent 15/* kernel messages to userspace */
# Define netlink_generic 16
The custom protocol type can be added to this file.
2) Call bind and bind the Protocol address
The BIND function needs to bind the Protocol address. The Netlink socket address uses the struct sockaddr_nl structure description:
Struct sockaddr_nl
{
Sa_family_t nl_family;
Unsigned short nl_pad;
_ U32 nl_pid;
_ U32 nl_groups;
};
The member nl_family is the protocol cluster af_netlink, and the member nl_pad is not currently used. Therefore, always set it to 0, and the member nl_pid is the ID of the process that receives or sends messages, if you want the kernel to process messages or multicast messages, set this field to 0; otherwise, set it to the process ID for message processing. The member nl_groups is used to specify multicast groups. The BIND function is used to add the calling process to the multicast group specified by this field. If it is set to 0, the caller is not added to any multicast group.
3) Call sendto to send messages to the kernel
An important issue is the composition of messages sent by the kernel. When we send an IP network packet, the packet structure is "IP packet header + IP data". Similarly, the message structure of Netlink is "Netlink message header + data ". The Netlink message header is described using the struct nlmsghdr structure:
Struct nlmsghdr
{
_ U32 nlmsg_len;/* length of Message */
_ 2010nlmsg_type;/* Message Type */
_ 2010nlmsg_flags;/* Additional flags */
_ U32 nlmsg_seq;/* sequence number */
_ U32 nlmsg_pid;/* Sending process PID */
};
The nlmsg_len field specifies the total length of the message, including the length of the Data part that follows the structure and the size of the structure. Generally, we use the macro nlmsg_length provided by Netlink to calculate the length, you only need to provide the length of the data to be sent to the nlmsg_length macro. It automatically calculates the total length after Alignment:
/* Calculate the length of the datagram containing the header */
# Define nlmsg_length (LEN) + nlmsg_align (sizeof (struct nlmsghdr )))
/* Byte alignment */
# Define nlmsg_align (LEN) + NLMSG_ALIGNTO-1 )&~ (NLMSG_ALIGNTO-1 ))
We can see that Netlink provides many macros, which can be very convenient for us to compile Netlink macros.
The nlmsg_type field is used to define the message type within the application. It is transparent to the Netlink kernel. Therefore, in most cases, it is set to 0. The nlmsg_flags field is used to set the message flag, you can set it to 0, but only some advanced applications (such as netfilter and daemon require it to perform some complex operations). The fields nlmsg_seq and nlmsg_pid are used for application tracing, the former indicates the sequence number, and the latter indicates the ID of the message source process.
4) Call recvfrom
After sending the request, you can call the Recv function cluster to receive data from the kernel. the received data includes the Netlink message header and the data to be transmitted. The program defines the following data structure for communication:
/* The received data includes the Netlink message header and Custom Data Structure */
Struct u_packet_info
{
Struct nlmsghdr HDR;
Struct packet_info p_info;
};
2. Programs in the kernel space
With the application kernel, the kernel space also mainly completes three tasks:
1) create a netlink socket
The API function netlink_kernel_create is used to create a netlink socket and register a callback function to receive messages that process user space.
Struct sock * netlink_kernel_create (INT unit, void (* input) (struct sock * SK, int Len ));
The Unit parameter indicates the Netlink protocol type, such as netlink_generic. The input parameter is the Netlink message processing function defined by the kernel module. When a message arrives at the Netlink socket, the input function pointer will be referenced. The SK parameter of the function pointer input is actually the struct sock pointer returned by the function netlink_kernel_create. Sock is actually a socket kernel that represents the data structure, the socket created by the user-mode application also has a struct sock structure in the kernel.
2) receive and process the data sent by the user space
The input function registered with netlink_kernel_create is used to process data from the user space. The function input () is called when the sending process executes sendmsg (), so that messages are processed in a timely manner. However, if the message is too long, the system will call sendmsg () in this case, a kernel thread can be defined to be specifically responsible for message receiving, and the function input only wakes up the kernel thread, so that sendmsg will return soon.
3) send data to user space
In the kernel, the module calls the netlink_unicast function to send a unicast message:
Int netlink_unicast (struct sock * SK, struct sk_buff * SKB, u32 PID, int nonblock );
The SK parameter is the socket returned by the function netlink_kernel_create (). The SKB parameter stores the message. Its data field points to the structure of the Netlink message to be sent, and the SKB control block stores the Message address information, the PID parameter is the PID of the message receiving process. The nonblock parameter indicates whether the function is non-blocking. If it is set to 1, the function returns immediately when no cache is available. If it is set to 0, this function sleeps when no cache is available.
The kernel module or subsystem can also use the netlink_broadcast function to send broadcast messages:
Void netlink_broadcast (struct sock * SK, struct sk_buff * SKB, u32 PID, u32 group, int allocation );
Kernel Program code:
Static int _ init Init (void)
{
/* Create a netlink socket. The protocol type is custom netlink_generic, and kernel_reveive is the acceptance handler */
Nlfd = netlink_kernel_create (netlink_generic, kernel_receive );
If (! Nlfd)/* creation failed */
{
Printk ("can not create a netlink socket/N ");
Return-1;
}
}
/* The program releases Netlink sockets in the exit module */
Static void _ exit Fini (void)
{
If (nlfd)
Sock_release (nlfd-> socket);/* release netlink socket */
}
Declare_mutex (receive_sem);/* initialize the semaphore */
Static void kernel_receive (struct sock * SK, int Len)
{
Do
{
Struct sk_buff * SKB;
If (down_trylock (& receive_sem)/* obtain the semaphore */
Return;
/* Obtain the SKB from the receiving queue, and then perform some basic length validity verification */
While (SKB = skb_dequeue (& SK-> receive_queue ))! = NULL)
{
Struct nlmsghdr * NlH = NULL;
If (SKB-> Len> = sizeof (struct nlmsghdr ))
{
/* Obtain the header of the nlmsghdr structure in the Data */
NlH = (struct nlmsghdr *) SKB-> data;
If (NLH-> nlmsg_len> = sizeof (struct nlmsghdr ))
& (SKB-> Len> = NlH-> nlmsg_len ))
{
/* After the full-length method verification is completed, the system processes the custom message type of the application. It stores the user PID, that is, the kernel saves the message to whom the message is sent "*/
If (NLH-> nlmsg_type = user_type)/* Request */
{
Write_lock_bh (& user_proc.pid );
User_proc.pid = NlH-> nlmsg_pid;
Write_unlock_bh (& user_proc.pid );
}
}
}
Kfree_skb (SKB );
}
Up (& receive_sem);/* returns the semaphore */
} While (nlfd & nlfd-> receive_queue.qlen );
}
/* An interrupt handler sends data to the application */
Static irqreturn_t irq_handler (int irq, void * dev_id, struct pt_regs * regs)
{
Int ret;
Int size;
Unsigned char * old_tail;
Struct sk_buff * SKB;
Struct nlmsghdr * NlH;
Struct packet_info * packet;
/* Calculate the total message length: add data addition to the Message Header */
Size = nlmsg_space (sizeof (* info ));
/* Allocate a new socket cache */
SKB = alloc_skb (size, gfp_atomic );
Old_tail = SKB-> tail;
/* Initialize a Netlink message header */
NlH = nlmsg_put (SKB, 0, 0, 0, size-sizeof (* NLH ));
/* Skip the message header and point to the data zone */
Packet = nlmsg_data (NLH );
/* Initialize the data zone */
Memset (packet, 0, sizeof (struct packet_info ));
/* Fill in the data to be sent */
.......
/* Calculate the length difference between SKB and netlink */
NlH-> nlmsg_len = SKB-> tail-old_tail;
/* Set the control field */
Netlink_cb (SKB). dst_groups = 0;
Ret = netlink_unicast (nlfd, SKB, user_proc.pid, msg_dontwait );
Return irq_handled;
}