(This chapter is based on: linux-4.4.0-37)
There are many ways to communicate between the kernel and user space, NetLink is one of them, and the others are/proc, IOCTL, sockopt, shared memory, and so on. NetLink is characterized by asynchronous Full-duplex.
NetLink uses 32-bit port addressing, called PID (not related to the process number), where the kernel's PID address is 0. The main characteristics of NetLink are as follows:
1 supports Full-duplex, asynchronous communication (of course sync also supports)
2 User space can use the standard BSD socket interface (but NetLink does not mask the construction and parsing process of the Protocol package, recommend the use of LIBNL, etc. third third-party)
3 use a dedicated kernel API interface in kernel space
4 support for multicasting (hence support for "bus" communication, which enables message subscriptions)
5 at the kernel end can be used for process context and interrupt context
Basic data structure
struct MSGHDR {
void *msg_name; /* Optional Address * *
socklen_t Msg_namelen; /* Size of Address *
/struct Iovec *msg_iov; /* Scatter/gather Array
* /size_t Msg_iovlen; * * Elements in Msg_iov
/void *msg_control; /* Ancillary data, below * *
size_t msg_controllen;/* Ancillary Data buffer len * * /int msg_flags; * FLAGS (UNUSED) */
};
struct SOCKADDR_NL
{
sa_family_t nl_family;/* This field is always af_netlink
/unsigned short nl_pad;/* not currently used, fill to 0* /
__u32 nl_pid/* Process PID/
__u32 nl_groups;/* Multicast Groups Mask * *
;
struct SOCKADDR_NL is the NetLink mailing address, which is the same as the sockaddr_in function of our usual socket programming. The PID represents the communication port, and the groups represents the group, note that this is the mask that you want to add a multicast group number to, that is, support up to 32 groups.
struct NLMSGHDR
{
__u32 nlmsg_len;/* Length of message including header/__u16
nlmsg_type;/* Message Conte NT/
__u16 nlmsg_flags/* Additional flags/
__u32 NLMSG_SEQ/* Sequence number
/__u32 nlmsg_pid;/* Sen Ding process PID *
/};
The data area of the NetLink packet is composed of the message header and the message body, struct NLMSGHDR is the message header, and the message body is connected to the message header.
Kernel layer Operations
Create a socket
Static inline struct sock *
netlink_kernel_create (struct net *net, int unit, struct netlink_kernel_cfg);
NET: General Direct fill &init_net
Unit: protocol type, customizable, such as #define NETLINK_TEST 25
CFG: Configuration structure, type as follows:
/* Optional netlink Kernel configuration parameters
/struct netlink_kernel_cfg {unsigned
int groups;
unsigned int flags;
void (*input) (struct sk_buff *skb);
struct mutex *cb_mutex;
int (*bind) (struct net *net, int group);
void (*unbind) (struct net *net, int group);
BOOL (*compare) (struct net *net, struct sock *sk);
Groups: Group number;
Input: Receives a callback function, receives a SK_BUFF structure, the data contains a NLMSGHDR protocol header;
Return: Returns a sock structure that returns null to indicate that the creation failed;
Single-Broadcast delivery interface:
extern int Netlink_unicast (struct sock *ssk, struct Sk_buff *skb, __u32 portid, int nonblock);
(1) SSK: The socket returned for function netlink_kernel_create ().
(2) SKB: Holds the message, its data field points to the NetLink message structure to send, while the SKB control block holds the address information of the message, and the macro NETLINK_CB (SKB) is used to conveniently set the control block.
(3) Portid:pid port.
(4) Nonblock: Indicates whether the function is non-blocking, and if 1, the function returns immediately when no cache is available, and if 0, the function can use timed sleep without receiving caching.
Multi-Broadcast Delivery interface:
extern int Netlink_broadcast (struct sock *ssk, struct Sk_buff *skb, __u32 Portid,
__u32 Group, gfp_t allocation);
Group: A multicast group that receives messages, each of which represents a multicast group, so if sent to more than one multicast group;
Allocation: The memory allocation type, typically used for gfp_atomic or gfp_kernel,gfp_atomic in the context of the atom (that is, not sleep), and Gfp_kernel for the non-atomic context.
Release socket
extern void Netlink_kernel_release (struct sock *sk);
User Layer Actions
Common operations for NLMSGHDR structures:
Nlmsg_space (len): Adds Len to the length of the NLMSGHDR head and aligns to 4 bytes;
Nlmsg_data (NLH): Return to the first address of the data area;
Create Socke
int netlink_create_socket (void)
{
//create a socket return
socket (Af_netlink, Sock_raw, netlink_test);
Bind
int netlink_bind (int sock_fd)
{
struct sockaddr_nl addr;
memset (&addr, 0, sizeof (struct sockaddr_nl));
addr.nl_family = Af_netlink;
Addr.nl_pid = Test_pid;
addr.nl_groups = 0;
Return bind (SOCK_FD, (struct sockaddr *) &addr, sizeof (struct sockaddr_nl));
Send receive:
Send receive data using SENDMSG, recvmsg
ssize_t sendmsg (int sockfd, const struct MSGHDR *msg, int flags);
ssize_t recvmsg (int sockfd, struct MSGHDR *msg, int flags);
Send receive data using SendTo, Recvfrom
ssize_t sendto (int sockfd, const void *buf, size_t len, int flags,
const struct, sockaddr *dest_addr, socklen_t Addrlen );
ssize_t recvfrom (int sockfd, void *buf, size_t len, int flags,
struct sockaddr *src_addr, socklen_t *addrlen);
Example:
Description: The user layer PID is set to 100, the application layer sends a message to the kernel, the kernel replies the same information;
Kernel layer:
#include <linux/init.h> #include <linux/module.h> #include <linux/stat.h> #include <linux/kdev_ t.h> #include <linux/fs.h> #include <linux/device.h> #include <linux/cdev.h> #include <asm/
uaccess.h> #include <net/netlink.h> #include <net/sock.h> #define NETLINK_TEST () static dev_t devid;
static struct Class *cls = NULL;
struct sock *nl_sk = NULL;
static void Hello_cleanup (void) {netlink_kernel_release (NL_SK);
Device_destroy (CLS, devid);
Class_destroy (CLS);
Unregister_chrdev_region (Devid, 1);
static void Netlink_send (int pid, uint8_t *message, int len) {struct Sk_buff *skb_1;
struct NLMSGHDR *nlh;
if (!message | |!nl_sk) {return;
} skb_1 = Alloc_skb (Nlmsg_space (len), Gfp_kernel);
if (!skb_1) {PRINTK (kern_err "ALLOC_SKB error!\n");
} NLH = Nlmsg_put (skb_1, 0, 0, 0, len, 0); NetlinK_CB (skb_1). Portid = 0;
NETLINK_CB (skb_1). Dst_group = 0;
memcpy (Nlmsg_data (NLH), message, Len);
Netlink_unicast (Nl_sk, skb_1, PID, msg_dontwait);
} static void Netlink_input (struct sk_buff *__skb) {struct sk_buff;
Char str[100];
struct NLMSGHDR *nlh;
if (!__SKB) {return;
} SKB = Skb_get (__SKB);
if (Skb->len < Nlmsg_space (0)) {return;
} NLH = Nlmsg_hdr (SKB);
memset (str, 0, sizeof (str));
memcpy (str, nlmsg_data (NLH), sizeof (str));
PRINTK (kern_info "Receive Message" (pid:%d):%s\n ", Nlh->nlmsg_pid, str);
PRINTK (kern_info "space:%d\n", Nlmsg_space (0));
PRINTK (kern_info "size:%d\n", Nlh->nlmsg_len);
Netlink_send (Nlh->nlmsg_pid, Nlmsg_data (NLH), Nlh->nlmsg_len-nlmsg_space (0));
Return
static __init int Netlink_init (void) {int result; struct Netlink_kernel_cfg nkc
PRINTK (kern_warning "NetLink init start!\n"); Dynamic Registration Device number if (result = Alloc_chrdev_region (&devid, 0, 1, "Stone-alloc-dev"))!= 0) {PRINTK (
kern_warning "Register Dev ID error:%d\n", result);
Goto err;
else {PRINTK (kern_warning "Register dev ID success!\n");
///Dynamically create device Node CLS = class_create (This_module, "Stone-class");
if (Is_err (CLS)) {PRINTK (kern_warning "Create Class error!\n");
Goto err; } if (CLS, NULL, Devid, "", "hello%d", 0) = NULL) {PRINTK (kern_warning "Create Devi") (Device_create)
Ce error!\n ");
Goto err;
}//Initialize NetLink nkc.groups = 0;
nkc.flags = 0;
Nkc.input = Netlink_input;
Nkc.cb_mutex = NULL;
Nkc.bind = NULL;
Nkc.unbind = NULL;
Nkc.compare = NULL; Nl_sk = Netlink_kernel_create (&init_net, NETlink_test, &NKC);
if (!nl_sk) {PRINTK (kern_err "[NetLink] Create NetLink socket error!\n");
Goto err;
} PRINTK (Kern_alert "NetLink init success!\n");
return 0;
Err:hello_cleanup ();
return-1;
static __exit void Netlink_exit (void) {hello_cleanup ();
PRINTK (kern_warning "NetLink exit!\n");
} module_init (Netlink_init);
Module_exit (Netlink_exit);
Module_license ("GPL"); Module_author ("Stone");
Application Layer 1:
#include <sys/stat.h> #include <unistd.h> #include <stdio.h> #include <stdlib.h> #include < sys/socket.h> #include <string.h> #include <asm/types.h> #include <linux/netlink.h> #include < linux/socket.h> #include <errno.h> #define NETLINK_TEST #define MAX_PAYLOAD (1024) #define Test_pid (m) int netlink_create_socket (void) {//create a socket return socket (Af_netlink, Sock_raw, Netl
Ink_test);
int netlink_bind (int sock_fd) {struct SOCKADDR_NL addr;
memset (&addr, 0, sizeof (struct sockaddr_nl));
addr.nl_family = Af_netlink;
Addr.nl_pid = Test_pid;
addr.nl_groups = 0;
Return bind (SOCK_FD, (struct sockaddr *) &addr, sizeof (struct sockaddr_nl)); int netlink_send_message (int sock_fd, const unsigned char *message, int len, UN signed int PID, unsigned int group) {struct NLMSGHDR *nlh = NULL;
struct SOCKADDR_NL dest_addr;
struct Iovec Iov;
struct MSGHDR msg;
if (!message) {return-1;
}//create message Nlh = (struct NLMSGHDR *) malloc (Nlmsg_space (len));
if (!NLH) {perror ("malloc");
Return-2;
} Nlh->nlmsg_len = Nlmsg_space (len);
Nlh->nlmsg_pid = Test_pid;
nlh->nlmsg_flags = 0;
memcpy (Nlmsg_data (NLH), message, Len);
Iov.iov_base = (void *) NLH;
Iov.iov_len = nlh->nlmsg_len;
memset (&dest_addr, 0, sizeof (struct sockaddr_nl));
dest_addr.nl_family = Af_netlink;
Dest_addr.nl_pid = pid;
Dest_addr.nl_groups = Group;
memset (&msg, 0, sizeof (struct MSGHDR));
Msg.msg_name = (void *) &dest_addr;
Msg.msg_namelen = sizeof (struct SOCKADDR_NL);
Msg.msg_iov = &iov;
Msg.msg_iovlen = 1; Send Message if (SENdmsg (SOCK_FD, &msg, 0) < 0) {printf ("Send error!\n");
Free (NLH);
return-3;
Free (NLH);
return 0;
int netlink_recv_message (int sock_fd, unsigned char *message, int *len) {struct NLMSGHDR *nlh = NULL;
struct SOCKADDR_NL source_addr;
struct Iovec Iov;
struct MSGHDR msg;
if (!message | |!len) {return-1;
}//create message Nlh = (struct NLMSGHDR *) malloc (Nlmsg_space (max_payload));
if (!NLH) {perror ("malloc");
Return-2;
} iov.iov_base = (void *) NLH;
Iov.iov_len = Nlmsg_space (max_payload);
memset (&source_addr, 0, sizeof (struct sockaddr_nl));
memset (&msg, 0, sizeof (struct MSGHDR));
Msg.msg_name = (void *) &source_addr;
Msg.msg_namelen = sizeof (struct SOCKADDR_NL);
Msg.msg_iov = &iov; Msg. Msg_iovlen = 1;
if (Recvmsg (sock_fd, &msg, 0) < 0) {printf ("recvmsg error!\n");
return-3;
} *len = Nlh->nlmsg_len-nlmsg_space (0);
memcpy (message, (unsigned char *) nlmsg_data (NLH), *len);
Free (NLH);
return 0;
int main (int argc, char **argv) {int sock_fd;
Char Buf[max_payload];
int Len;
if (ARGC < 2) {printf ("Enter message!\n");
Exit (Exit_failure);
} SOCK_FD = Netlink_create_socket ();
if (sock_fd = = 1) {printf ("Socket error!\n");
return-1;
} if (Netlink_bind (SOCK_FD) < 0) {perror ("bind");
Close (SOCK_FD);
Exit (Exit_failure);
} netlink_send_message (SOCK_FD, argv[1], strlen (argv[1]) + 1, 0, 0); if (Netlink_recv_message (SOCK_FD, buf, &len) = 0) {priNTF ("recv:%s len:%d\n", buf, Len);
Close (SOCK_FD);
return 0; }
The above example is about using Sendmsg, recvmsg. In the application layer we can also use SendTo, Recvfrom send receive data, operating mode and UDP communication is very similar to the difference is to be able to receive data successfully, we also need to bind to the use of bind () the address, but for UDP this is not necessary;
Application Layer 2:
#include <sys/stat.h> #include <unistd.h> #include <stdio.h> #include <stdlib.h> #include < sys/socket.h> #include <string.h> #include <asm/types.h> #include <linux/netlink.h> #include < linux/socket.h> #include <errno.h> #define NETLINK_TEST #define MAX_PAYLOAD (1024) #define Test_pid (m) int netlink_create_socket (void) {//create a socket return socket (Af_netlink, Sock_raw, Netl
Ink_test);
int netlink_bind (int sock_fd) {struct SOCKADDR_NL addr;
memset (&addr, 0, sizeof (struct sockaddr_nl));
addr.nl_family = Af_netlink;
Addr.nl_pid = Test_pid;
addr.nl_groups = 0;
Return bind (SOCK_FD, (struct sockaddr *) &addr, sizeof (struct sockaddr_nl)); int netlink_send_message (int sock_fd, const unsigned char *message, int len, UN signed int PID, unsigned int group) {struct NLMSGHDR *nlh = NULL;
struct SOCKADDR_NL dest_addr;
if (!message) {return-1;
}//create message Nlh = (struct NLMSGHDR *) malloc (Nlmsg_space (len));
if (!NLH) {perror ("malloc");
Return-2;
} Nlh->nlmsg_len = Nlmsg_space (len);
Nlh->nlmsg_pid = Test_pid;
nlh->nlmsg_flags = 0;
memcpy (Nlmsg_data (NLH), message, Len);
memset (&dest_addr, 0, sizeof (struct sockaddr_nl));
dest_addr.nl_family = Af_netlink;
Dest_addr.nl_pid = pid;
Dest_addr.nl_groups = Group; Send Message if (SendTo (SOCK_FD, NLH, Nlh->nlmsg_len, 0, struct sockaddr *) &dest_addr, sizeof (struct SOC
KADDR_NL))!= Nlh->nlmsg_len) {printf ("Send error!\n");
Free (NLH);
return-3;
Free (NLH);
return 0; int netlink_recv_message (int sock_fd, unsigned char *message, int *leN) {struct NLMSGHDR *nlh = NULL;
struct SOCKADDR_NL src_addr;
socklen_t addrlen = sizeof (struct SOCKADDR_NL);
if (!message | |!len) {return-1;
}//create message Nlh = (struct NLMSGHDR *) malloc (Nlmsg_space (max_payload));
if (!NLH) {perror ("malloc");
Return-2;
memset (&src_addr, 0, sizeof (struct sockaddr_nl)); if (Recvfrom (SOCK_FD, NLH, Nlmsg_space (Max_payload), 0, (struct sockaddr *) &src_addr, (socklen_t *) &addrlen)
< 0) {printf ("recvmsg error!\n");
return-3;
} *len = Nlh->nlmsg_len-nlmsg_space (0);
memcpy (message, (unsigned char *) nlmsg_data (NLH), *len);
Free (NLH);
return 0;
int main (int argc, char **argv) {int sock_fd;
Char Buf[max_payload];
int Len; if (ARGC < 2) {printf ("Enter message!\n");
Exit (Exit_failure);
} SOCK_FD = Netlink_create_socket ();
if (sock_fd = = 1) {printf ("Socket error!\n");
return-1;
} if (Netlink_bind (SOCK_FD) < 0) {perror ("bind");
Close (SOCK_FD);
Exit (Exit_failure);
} netlink_send_message (SOCK_FD, argv[1], strlen (argv[1]) + 1, 0, 0);
if (Netlink_recv_message (SOCK_FD, buf, &len) = = 0) {printf ("recv:%s len:%d\n", buf, Len);
Close (SOCK_FD);
return 0;
}