1、Netlink 機制簡介
爲了完成內核空間與用戶空間通信,Linux提供了基於socket的Netlink通信機制,可以實現內核與用戶空間數據的及時交換。Netlink是基於socket的通信機制,由於socket本身的雙共性、突發性、不阻塞特點,因此能夠很好的滿足內核與用戶空間小量數據的及時交互,因此在Linux 2.6內核中廣泛使用,例如SELinux,Linux系統的防火牆分爲內核態的netfilter和用戶態的iptables,netfilter與iptables的數據交換就是通過Netlink機制完成。
netlink作爲一種用戶空間和內核空間通信的機制,它不光爲了內核和用戶通信,還可以作爲IPC機制進行進程間通信。它也用到了sk_buff結構體,和網絡套接字一樣,更好的事情是它並沒 有觸及sk_buff裏面的標準字段,而僅僅用了一個擴展的cb字段,cb在 sk_buff裏面的定義是char cb[40];在netlink模塊裏面NETLINK_CB宏就是取cb字段的,也就是netlink所用的私有字段,這樣的話你就可以用 netlink向任何執行實體傳輸任何數據了,不限於本機。
Linux操作系統中當CPU處於內核狀態時,可以分爲有用戶上下文的狀態和執行硬件、軟件中斷兩種。其中當處於有用戶上下文時,由於內核態和用戶態的內 存映射機制不同,不可直接將本地變量傳給用戶態的內存區;處於硬件、軟件中斷時,無法直接向用戶內存區傳遞數據,代碼執行不可中斷。針對傳統的進程間通信 機制,他們均無法直接在內核態和用戶態之間使用,原因如下:
通信方法 無法介於內核態與用戶態的原因
管道(不包括命名管道) 侷限於父子進程間的通信。
消息隊列 在硬、軟中斷中無法無阻塞地接收數據。
信號量 無法介於內核態和用戶態使用。
內存共享 需要信號量輔助,而信號量又無法使用。
套接字 在硬、軟中斷中無法無阻塞地接收數據。
Netlink相對於其他的通信機制具有以下優點:
(1)使用Netlink通過自定義一種新的協議並加入協議族即可通過socket API使用Netlink協議完成數據交換,而ioctl和proc文件系統均需要通過程序加入相應的設備或文件。
(2)Netlink使用socket緩存隊列,是一種異步通信機制,而ioctl是同步通信機制,如果傳輸的數據量較大,會影響系統性能。
(3)Netlink支持多播,屬於一個Netlink組的模塊和進程都能獲得該多播消息。
(4)Netlink允許內核發起會話,而ioctl和系統調用只能由用戶空間進程發起。
2、源碼分析
2.1 linux\netlink.h
2.1.1 netlink 協議類型
#define NETLINK_ROUTE 0 /* Routing/device hook */ #define NETLINK_UNUSED 1 /* Unused number */ #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 /* leave room for NETLINK_DM (DM Events) */ #define NETLINK_SCSITRANSPORT 18 /* SCSI Transports */ #define NETLINK_ECRYPTFS 19可以根據自己的應用,創建新的通信協議。新協議的定義不能和內核已經定義的衝突,同時不能超過MAX_LINKS這個宏的限定,MAX_LINKS = 32。
2.1.2 netlink 地址格式
struct sockaddr_nl { sa_family_t nl_family; /* AF_NETLINK */ unsigned short nl_pad; /* zero */ __u32 nl_pid; /* port ID */ __u32 nl_groups; /* multicast groups mask */ };nl_pad 當前沒有使用,因此要總是設置爲 0,
nl_pid就是一個約定的通信端口,用戶態使用的時候需要用一個非0的數字,一般來說可以直接採用上層應用的進程ID(未必是進程 ID,它只是用於區分不同的接收者或發送者的一個標識,用戶可以根據自己需要設置該字段,只要是系統中不衝突的一個數字即可)。對於內核的地址,該值必須用0,即上層通過sendto向內核發送netlink消息,peer addr中nl_pid必須填寫0。
nl_groups用於一個消息同時分發給不同的接收者,是一種組播應用。綁定時用於指定綁定者所要加入的多播組,這樣綁定者就可以接收多播消息,發送 消息時可以用於指定多播組,這樣就可以將消息發給多個接收者。這裏nl_groups 爲32位的無符號整形,所以可以指定32個多播組,每個進程可以加入多個多播組, 因爲多播組是通過“或”操作,如果設置爲 0,表示調用者不加入任何多播組。
本質上,nl_pid就是netlink的通信地址。除了通信地址,netlink還提供“協議”來標示通信實體,在創建socket的時候,需要指定netlink的通信協議號。每個協議號代表一種“應用”,上層可以用內核已經定義的協議和內核進行通信,獲得內核已經提供的信息。netlink採用“協議號 + 通信端口”的方式構建自己的地址體系。
2.1.3 netlink 頭部信息
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 指定消息的總長度,包括緊跟該結構的數據部分長度以及該結構的大小。
nlmsg_type 用於應用內部定義消息的類型,它對netlink 內核實現是透明的,因此大部分情況下設置爲 0。
nlmsg_seq 和 nlmsg_pid 用於應用追蹤消息,前者表示順序號,後者爲消息來源進程 ID。其中pid是Netlink分配的一個ID,不同的值代表不同的socket通道,默認的值是進程PID。在某些情況下,這個值被設置爲0,比如消息來自內核空間,或者想要Netlink來設置這個值。
nlmsg_flags 用於設置消息標誌,可用的標誌包括:
/* Flags values */ #define NLM_F_REQUEST 1 /* It is request message. */ #define NLM_F_MULTI 2 /* Multipart message, terminated by NLMSG_DONE */ #define NLM_F_ACK 4 /* Reply with ack, with zero or error code */ #define NLM_F_ECHO 8 /* Echo this request */ /* Modifiers to GET request */ #define NLM_F_ROOT 0x100 /* specify tree root */ #define NLM_F_MATCH 0x200 /* return all matching */ #define NLM_F_ATOMIC 0x400 /* atomic GET */ #define NLM_F_DUMP (NLM_F_ROOT|NLM_F_MATCH) /* Modifiers to NEW request */ #define NLM_F_REPLACE 0x100 /* Override existing */ #define NLM_F_EXCL 0x200 /* Do not touch, if it exists */ #define NLM_F_CREATE 0x400 /* Create, if it does not exist */ #define NLM_F_APPEND 0x800 /* Add to end of list */
2.1.4 netlink 在skb_buff中的對應信息
struct netlink_skb_parms { struct ucred creds; /* Skb credentials */ __u32 pid; __u32 dst_group; kernel_cap_t eff_cap; __u32 loginuid; /* Login (audit) uid */ __u32 sessionid; /* Session id (audit) */ __u32 sid; /* SELinux security id */ };
內核態發送數據的時候,在skb_buff存儲對應的發送地址等信息,供用戶態需要時使用。
2.1.5 netlink 相關的宏
#define NLMSG_ALIGNTO 4 #define NLMSG_ALIGN(len) ( ((len)+NLMSG_ALIGNTO-1) & ~(NLMSG_ALIGNTO-1) ) #define NLMSG_HDRLEN ((int) NLMSG_ALIGN(sizeof(struct nlmsghdr))) #define NLMSG_LENGTH(len) ((len)+NLMSG_ALIGN(NLMSG_HDRLEN)) #define NLMSG_SPACE(len) NLMSG_ALIGN(NLMSG_LENGTH(len)) #define NLMSG_DATA(nlh) ((void*)(((char*)nlh) + NLMSG_LENGTH(0))) //獲取nlmsghdr頭部之後的數據 #define NLMSG_NEXT(nlh,len) ((len) -= NLMSG_ALIGN((nlh)->nlmsg_len), \ (struct nlmsghdr*)(((char*)(nlh)) + NLMSG_ALIGN((nlh)->nlmsg_len))) #define NLMSG_OK(nlh,len) ((len) >= (int)sizeof(struct nlmsghdr) && \ (nlh)->nlmsg_len >= sizeof(struct nlmsghdr) && \ (nlh)->nlmsg_len <= (len)) #define NLMSG_PAYLOAD(nlh,len) ((nlh)->nlmsg_len - NLMSG_SPACE((len)))2.2 net\netlink\af_netlink.c
netlink的實現方法主要在 /net/netlink/af_netlink.c文件中。
2.2.1 netlink 套接字結構
struct netlink_sock { /* struct sock has to be the first member of netlink_sock */ struct sock sk; u32 pid; //內核態pid u32 dst_pid; u32 dst_group; u32 flags; u32 subscriptions; u32 ngroups; //組數 unsigned long *groups; //組號 unsigned long state; wait_queue_head_t wait; //等待隊列 struct netlink_callback *cb; struct mutex *cb_mutex; struct mutex cb_def_mutex; void (*netlink_rcv)(struct sk_buff *skb); //內核態接收用戶態信息後的處理函數 struct module *module; };Groups : 對於每一個netlink協議類型,可以使用多播的概念,最多可以有 32個多播組,每一個多播組用一個位表示,netlink 的多播特性使得發送消息給同一個組僅需要一次系統調用,因而對於需要多播消息的應用而言,大大地降低了系統調用的次數。
注意:用戶態可以使用標準的socket APIs, socket(), bind(), sendmsg(), recvmsg() 和 close() 等函數就能很容易地使用 netlink socket,在用戶空間可以直接通過socket函數來使用Netlink通信,區別是:
(1)netlink有自己的地址;
創建socket,socket(PF_NETLINK, SOCK_DGRAM, NETLINK_XXX);第一個參數必須是PF_NETLINK或者AF_NETLINK(Linux中實際爲一個東西,它表示要使用netlink),第二個參數用SOCK_DGRAM和SOCK_RAW都沒問題,第三個參數就是netlink的協議號。
(2)netlink接收到的消息帶一個netlink自己的消息頭;
用戶態發送、接收的時候,需要在自己附帶的消息前面要加上一個netlink的消息頭:
struct tag_rcv_buf { struct nlmsghdr hdr; //netlink的消息頭 netlink_notify_s my_msg; //通信實體消息 }st_snd_buf;2.2.2 創建netlink socket
struct sock * netlink_kernel_create(struct net *net, int unit, unsigned int groups, void (*input)(struct sk_buff *skb), struct mutex *cb_mutex, struct module *module) { struct socket *sock; struct sock *sk; struct netlink_sock *nlk; unsigned long *listeners = NULL; BUG_ON(!nl_table); if (unit < 0 || unit >= MAX_LINKS) return NULL; if (sock_create_lite(PF_NETLINK, SOCK_DGRAM, unit, &sock)) //創建socket 結構 return NULL; /* * We have to just have a reference on the net from sk, but don't * get_net it. Besides, we cannot get and then put the net here. * So we create one inside init_net and the move it to net. */ if (__netlink_create(&init_net, sock, cb_mutex, unit) < 0) //創建sock 結構 goto out_sock_release_nosk; sk = sock->sk; sk_change_net(sk, net); if (groups < 32) groups = 32; listeners = kzalloc(NLGRPSZ(groups) + sizeof(struct listeners_rcu_head), GFP_KERNEL); if (!listeners) goto out_sock_release; sk->sk_data_ready = netlink_data_ready; if (input) nlk_sk(sk)->netlink_rcv = input; //設置內核接收Netlink消息的函數 if (netlink_insert(sk, net, 0)) goto out_sock_release; nlk = nlk_sk(sk); //取得sock嵌入的netlink_sock結構體 nlk->flags |= NETLINK_KERNEL_SOCKET; netlink_table_grab(); if (!nl_table[unit].registered) { nl_table[unit].groups = groups; nl_table[unit].listeners = listeners; nl_table[unit].cb_mutex = cb_mutex; nl_table[unit].module = module; nl_table[unit].registered = 1; //更新netlink_table結構體信息,每種協議對應一個netlink_table結構 } else { kfree(listeners); nl_table[unit].registered++; } netlink_table_ungrab(); return sk; out_sock_release: kfree(listeners); netlink_kernel_release(sk); return NULL; out_sock_release_nosk: sock_release(sock); return NULL; }內核態創建netlink socket的時候,調用 netlink_kernel_create() 函數。
2.2.3 內核態發送數據
內核態向用戶態發送數據的時候,調用netlink_unicast() 函數。
int netlink_unicast(struct sock *ssk, struct sk_buff *skb, u32 pid, int nonblock) { struct sock *sk; int err; long timeo; skb = netlink_trim(skb, gfp_any()); timeo = sock_sndtimeo(ssk, nonblock); retry: sk = netlink_getsockbypid(ssk, pid); //找到內核中對應該pid和SOCK_DGRAM協議的sock結構,掛載在哈希表中 if (IS_ERR(sk)) { kfree_skb(skb); return PTR_ERR(sk); } if (netlink_is_kernel(sk)) //若另一端也爲內核端,調用其netlink_sock的input函數接受 return netlink_unicast_kernel(sk, skb); if (sk_filter(sk, skb)) { err = skb->len; kfree_skb(skb); sock_put(sk); return err; } err = netlink_attachskb(sk, skb, &timeo, ssk); if (err == 1) goto retry; if (err) return err; return netlink_sendskb(sk, skb); //掛載到用戶態sock結構的receive_queue隊列中, 然後調用sk_data_ready函數,喚醒用戶態睡眠函數 }2.2.4 內核態接收數據
在netlink_kernel_create函數中,可以設置接收函數。
2.2.5 用戶態發送數據
static int netlink_sendmsg(struct kiocb *kiocb, struct socket *sock, struct msghdr *msg, size_t len) { struct sock_iocb *siocb = kiocb_to_siocb(kiocb); struct sock *sk = sock->sk; struct netlink_sock *nlk = nlk_sk(sk); struct sockaddr_nl *addr = msg->msg_name; //目的地址信息 u32 dst_pid; u32 dst_group; struct sk_buff *skb; int err; struct scm_cookie scm; if (msg->msg_flags&MSG_OOB) return -EOPNOTSUPP; if (NULL == siocb->scm) siocb->scm = &scm; err = scm_send(sock, msg, siocb->scm); if (err < 0) return err; if (msg->msg_namelen) { if (addr->nl_family != AF_NETLINK) return -EINVAL; dst_pid = addr->nl_pid; dst_group = ffs(addr->nl_groups); if ((dst_group || dst_pid) && !netlink_capable(sock, NL_NONROOT_SEND)) return -EPERM; } else { dst_pid = nlk->dst_pid; dst_group = nlk->dst_group; } if (!nlk->pid) { err = netlink_autobind(sock); if (err) goto out; } err = -EMSGSIZE; if (len > sk->sk_sndbuf - 32) goto out; err = -ENOBUFS; skb = alloc_skb(len, GFP_KERNEL); //分配一個sk_buff結構,目的是將msghdr結構轉化爲sk_buff結構 if (skb == NULL) goto out; NETLINK_CB(skb).pid = nlk->pid; //本地的pid信息 NETLINK_CB(skb).dst_group = dst_group; NETLINK_CB(skb).loginuid = audit_get_loginuid(current); NETLINK_CB(skb).sessionid = audit_get_sessionid(current); security_task_getsecid(current, &(NETLINK_CB(skb).sid)); memcpy(NETLINK_CREDS(skb), &siocb->scm->creds, sizeof(struct ucred)); /* What can I do? Netlink is asynchronous, so that we will have to save current capabilities to check them, when this message will be delivered to corresponding kernel module. --ANK (980802) */ err = -EFAULT; if (memcpy_fromiovec(skb_put(skb, len), msg->msg_iov, len)) { //將數據拷貝到sk_buff中 kfree_skb(skb); goto out; } err = security_netlink_send(sk, skb); if (err) { kfree_skb(skb); goto out; } if (dst_group) { atomic_inc(&skb->users); netlink_broadcast(sk, skb, dst_pid, dst_group, GFP_KERNEL); } err = netlink_unicast(sk, skb, dst_pid, msg->msg_flags&MSG_DONTWAIT); out: return err; }2.2.6 用戶態接收數據
static int netlink_recvmsg(struct kiocb *kiocb, struct socket *sock, struct msghdr *msg, size_t len, int flags) { struct sock_iocb *siocb = kiocb_to_siocb(kiocb); struct scm_cookie scm; struct sock *sk = sock->sk; struct netlink_sock *nlk = nlk_sk(sk); int noblock = flags&MSG_DONTWAIT; size_t copied; struct sk_buff *skb, *data_skb; int err; if (flags&MSG_OOB) return -EOPNOTSUPP; copied = 0; skb = skb_recv_datagram(sk, flags, noblock, &err); //從等待隊列中接受一個數據包 if (skb == NULL) goto out; data_skb = skb; #ifdef CONFIG_COMPAT_NETLINK_MESSAGES if (unlikely(skb_shinfo(skb)->frag_list)) { /* * If this skb has a frag_list, then here that means that we * will have to use the frag_list skb's data for compat tasks * and the regular skb's data for normal (non-compat) tasks. * * If we need to send the compat skb, assign it to the * 'data_skb' variable so that it will be used below for data * copying. We keep 'skb' for everything else, including * freeing both later. */ if (flags & MSG_CMSG_COMPAT) data_skb = skb_shinfo(skb)->frag_list; } #endif copied = data_skb->len; //接收的數據包長度 if (len < copied) { msg->msg_flags |= MSG_TRUNC; copied = len; } skb_reset_transport_header(data_skb); //將數據包從sk_buff中拷貝到msghdr結構中 err = skb_copy_datagram_iovec(data_skb, 0, msg->msg_iov, copied); if (msg->msg_name) { //如果要求得到kernel的struct netlink_nl結構 struct sockaddr_nl *addr = (struct sockaddr_nl *)msg->msg_name; addr->nl_family = AF_NETLINK; addr->nl_pad = 0; addr->nl_pid = NETLINK_CB(skb).pid; //內核態pid addr->nl_groups = netlink_group_mask(NETLINK_CB(skb).dst_group); //發送分組 msg->msg_namelen = sizeof(*addr); } if (nlk->flags & NETLINK_RECV_PKTINFO) netlink_cmsg_recv_pktinfo(msg, skb); if (NULL == siocb->scm) { memset(&scm, 0, sizeof(scm)); siocb->scm = &scm; } siocb->scm->creds = *NETLINK_CREDS(skb); if (flags & MSG_TRUNC) copied = data_skb->len; skb_free_datagram(sk, skb); //釋放sk_buff數據包信息 if (nlk->cb && atomic_read(&sk->sk_rmem_alloc) <= sk->sk_rcvbuf / 2) netlink_dump(sk); scm_recv(sock, msg, siocb->scm, flags); out: netlink_rcv_wake(sk); //接受喚醒 return err ? : copied; }內核態將數據發送到用戶態對應的sock的sk_receive_queue中,並且喚醒睡眠的進程。
3、測試
3.1 用戶態與內核態通信
3.2.1 內核代碼實現
#include <linux/init.h> #include <linux/module.h> #include <linux/timer.h> #include <linux/time.h> #include <linux/types.h> #include <net/sock.h> #include <net/netlink.h> #define NETLINK_TEST 25 #define MAX_MSGSIZE 1024 int stringlength(char *s); void sendnlmsg(char * message); int pid; int err; struct sock *nl_sk = NULL; int flag = 0; void sendnlmsg(char *message) { struct sk_buff *skb_1; struct nlmsghdr *nlh; int len = NLMSG_SPACE(MAX_MSGSIZE); int slen = 0; if(!message || !nl_sk) { return ; } skb_1 = alloc_skb(len,GFP_KERNEL); if(!skb_1) { printk(KERN_ERR "my_net_link:alloc_skb_1 error\n"); } slen = stringlength(message); nlh = nlmsg_put(skb_1,0,0,0,MAX_MSGSIZE,0); NETLINK_CB(skb_1).pid = 0; NETLINK_CB(skb_1).dst_group = 0; message[slen]= '\0'; memcpy(NLMSG_DATA(nlh),message,slen+1); printk("my_net_link:send message '%s'.\n",(char *)NLMSG_DATA(nlh)); netlink_unicast(nl_sk,skb_1,pid,MSG_DONTWAIT); } int stringlength(char *s) { int slen = 0; for(; *s; s++){ slen++; } return slen; } void nl_data_ready(struct sk_buff *__skb) { struct sk_buff *skb; struct nlmsghdr *nlh; char str[100]; struct completion cmpl; int i=10; skb = skb_get (__skb); if(skb->len >= NLMSG_SPACE(0)) { nlh = nlmsg_hdr(skb); memcpy(str, NLMSG_DATA(nlh), sizeof(str)); printk("Message received:%s\n",str) ; pid = nlh->nlmsg_pid; while(i--) { init_completion(&cmpl); wait_for_completion_timeout(&cmpl,3 * HZ); sendnlmsg("I am from kernel!"); } flag = 1; kfree_skb(skb); } } int netlink_init(void) { nl_sk = netlink_kernel_create(&init_net, NETLINK_TEST, 0, nl_data_ready, NULL, THIS_MODULE); if(!nl_sk){ printk(KERN_ERR "my_net_link: create netlink socket error.\n"); return 1; } printk("my_net_link: create netlink socket ok.\n"); return 0; } static void netlink_exit(void) { if(nl_sk != NULL){ sock_release(nl_sk->sk_socket); } printk("my_net_link: module exited.\n"); } module_init(netlink_init); module_exit(netlink_exit); MODULE_AUTHOR("LRQ"); MODULE_LICENSE("GPL");3.2.2 用戶態代碼實現
#include <sys/stat.h> #include <unistd.h> #include <stdio.h> #include <stdlib.h> #include <sys/socket.h> #include <sys/types.h> #include <string.h> #include <asm/types.h> #include <linux/netlink.h> #include <linux/socket.h> #include <errno.h> #define NETLINK_TEST 25 #define MAX_PAYLOAD 1024 // maximum payload size int main(int argc, char* argv[]) { int state; struct sockaddr_nl src_addr, dest_addr; struct nlmsghdr *nlh = NULL; struct iovec iov; struct msghdr msg; int sock_fd, retval; int state_smg = 0; // Create a socket sock_fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_TEST); if(sock_fd == -1){ printf("error getting socket: %s", strerror(errno)); return -1; } // To prepare binding memset(&msg,0,sizeof(msg)); memset(&src_addr, 0, sizeof(src_addr)); src_addr.nl_family = AF_NETLINK; src_addr.nl_pid = getpid(); // self pid src_addr.nl_groups = 0; // multi cast retval = bind(sock_fd, (struct sockaddr*)&src_addr, sizeof(src_addr)); if(retval < 0){ printf("bind failed: %s", strerror(errno)); close(sock_fd); return -1; } // To prepare recvmsg nlh = (struct nlmsghdr *)malloc(NLMSG_SPACE(MAX_PAYLOAD)); if(!nlh){ printf("malloc nlmsghdr error!\n"); close(sock_fd); return -1; } memset(&dest_addr,0,sizeof(dest_addr)); dest_addr.nl_family = AF_NETLINK; dest_addr.nl_pid = 0; dest_addr.nl_groups = 0; nlh->nlmsg_len = NLMSG_SPACE(MAX_PAYLOAD); nlh->nlmsg_pid = getpid(); nlh->nlmsg_flags = 0; strcpy(NLMSG_DATA(nlh),"Hello!"); iov.iov_base = (void *)nlh; iov.iov_len = NLMSG_SPACE(MAX_PAYLOAD); // iov.iov_len = nlh->nlmsg_len; memset(&msg, 0, sizeof(msg)); msg.msg_name = (void *)&dest_addr; msg.msg_namelen = sizeof(dest_addr); msg.msg_iov = &iov; msg.msg_iovlen = 1; printf("state_smg\n"); state_smg = sendmsg(sock_fd,&msg,0); if(state_smg == -1) { printf("get error sendmsg = %s\n",strerror(errno)); } memset(nlh,0,NLMSG_SPACE(MAX_PAYLOAD)); printf("waiting received!\n"); // Read message from kernel while(1){ printf("In while recvmsg\n"); state = recvmsg(sock_fd, &msg, 0); if(state<0) { printf("state<1"); } printf("In while\n"); printf("Received message: %s\n",(char *) NLMSG_DATA(nlh)); } close(sock_fd); return 0; }