Netlink通信機制

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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章