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; }