Linux內(nèi)核 | Netlink機(jī)制分析與使用
前面文章分析了sockfs文件系統(tǒng)、socket底層的原理:
在兩章的基礎(chǔ)上分析Netlink機(jī)制,并編寫使用Netlink機(jī)制的案例
一、Netlink簡介
Linux中內(nèi)核與用戶空間數(shù)據(jù)交換有很多種方式,如系統(tǒng)調(diào)用、procfs、debugfs等,這些通信方式都是同步通信方式,由用戶態(tài)主動發(fā)起向內(nèi)核態(tài)的通信,內(nèi)核無法主動發(fā)起通信。而Netlink是一種異步全雙工的通信方式,支持由內(nèi)核態(tài)主動發(fā)起通信,內(nèi)核為Netlink通信提供了一組特殊的API接口,用戶態(tài)則基于socket API,內(nèi)核發(fā)送的數(shù)據(jù)會保存在接收進(jìn)程socket 的接收緩存中,由接收進(jìn)程處理。Netlink也是網(wǎng)絡(luò)應(yīng)用程序與內(nèi)核通信的最常用接口,如路由damon、防火墻(NETLINK_FIREWALL)等。Netlink機(jī)制作為一種內(nèi)核與用戶空間通信的機(jī)制,同時(shí)也是一套IP服務(wù)協(xié)議,代表著一種特殊的socket通信方式,對于Linux內(nèi)核與用戶空間進(jìn)行雙向數(shù)據(jù)傳輸是非常好的方式。
Netlink通信機(jī)制的簡易流程如下圖所示,本文****將從用戶態(tài)通過系統(tǒng)調(diào)用創(chuàng)建netlink socket、內(nèi)核態(tài)調(diào)用netlink_kernel_create創(chuàng)建內(nèi)核Netlink套接字進(jìn)行分析,并在文章最后編寫使用Netlink的案例。

二、用戶態(tài)創(chuàng)建Netlink套接字
1、用戶態(tài)創(chuàng)建Netlink socket 的相關(guān)分析
在Linux內(nèi)核 |socket底層的來龍去脈文章中分析了普通socket的創(chuàng)建過程,關(guān)于socket的創(chuàng)建過程的函數(shù)大概如下所示,不同的協(xié)議族最后調(diào)用不同的協(xié)議族的socket創(chuàng)建函數(shù),如AF_INET協(xié)議族調(diào)用inet_create函數(shù)(如下圖紅色部分 )。

由于用戶態(tài)也是采用socket API方式,所以用戶態(tài)創(chuàng)建socket的過程與普通的協(xié)議族是一樣的(Netlink的協(xié)議族是AF_NETLINK,對應(yīng)的創(chuàng)建函數(shù)是netlink_create,如下代碼所示),如下圖所示:
static const struct net_proto_family netlink_family_ops = {
.family = PF_NETLINK,
.create = netlink_create, ?//netlink socket創(chuàng)建函數(shù)
.owner = THIS_MODULE, /* for consistency 8) */
};

在AF_INET協(xié)議族中再根據(jù)不同的socket類型進(jìn)行賦值,如下inet_create函數(shù)中實(shí)現(xiàn)所示
static int inet_create(struct net *net, struct socket *sock, int protocol,
? ? ? ? int kern)
{
struct sock *sk;
struct inet_protosw *answer;
struct inet_sock *inet;
struct proto *answer_prot;
......
sock->ops = answer->ops; ?//根據(jù)不同的協(xié)議賦值不同的操作集
?......
}
其中struct inet_protosw answer有一個(gè)全局?jǐn)?shù)組,根據(jù)不同的協(xié)議進(jìn)行不同的封裝,TCP協(xié)議下struct inet_protosw answer結(jié)構(gòu)體中函數(shù)操作集就是封裝的inet_stream_ops,如下所示:
const struct proto_ops inet_stream_ops = {
.family ? ? = PF_INET,
.owner ? ? = THIS_MODULE,
.release ? ?= inet_release,
.bind ? ? = inet_bind,
.connect ? ?= inet_stream_connect,
.socketpair ? ?= sock_no_socketpair,
.accept ? ? = inet_accept,
.getname ? ?= inet_getname,
.poll ? ? = tcp_poll,
.ioctl ? ? = inet_ioctl,
.listen ? ? = inet_listen,
.shutdown ? ?= inet_shutdown,
.setsockopt ? ?= sock_common_setsockopt,
.getsockopt ? ?= sock_common_getsockopt,
.sendmsg ? ?= inet_sendmsg,
.recvmsg ? ?= inet_recvmsg,
.mmap ? ? = sock_no_mmap,
.sendpage ? ?= inet_sendpage,
.splice_read ? ?= tcp_splice_read,
.read_sock ? ?= tcp_read_sock,
.sendmsg_locked ? ?= tcp_sendmsg_locked,
.sendpage_locked ? = tcp_sendpage_locked,
.peek_len ? ?= tcp_peek_len,
#ifdef CONFIG_COMPAT
.compat_setsockopt = compat_sock_common_setsockopt,
.compat_getsockopt = compat_sock_common_getsockopt,
.compat_ioctl ? ?= inet_compat_ioctl,
#endif
};
netlink機(jī)制(AF_NETLINK協(xié)議族 )中,相關(guān)的函數(shù)集在__netlinkl_cteate函數(shù)中進(jìn)行賦值,如下所示:
static int __netlink_create(struct net *net, struct socket *sock,
? ? ? struct mutex *cb_mutex, int protocol,
? ? ? int kern)
{
struct sock *sk;
struct netlink_sock *nlk;
sock->ops = &netlink_ops;//設(shè)置AF_NETLINK協(xié)議族的函數(shù)集
?......
}
netlink_ops如下所示:
static const struct proto_ops netlink_ops = {
.family = PF_NETLINK,
.owner = THIS_MODULE,
.release = netlink_release,
.bind = ?netlink_bind,
.connect = netlink_connect,
.socketpair = sock_no_socketpair,
.accept = sock_no_accept,
.getname = netlink_getname,
.poll = ?datagram_poll,
.ioctl = netlink_ioctl,
.listen = sock_no_listen,
.shutdown = sock_no_shutdown,
.setsockopt = netlink_setsockopt,
.getsockopt = netlink_getsockopt,
.sendmsg = netlink_sendmsg,
.recvmsg = netlink_recvmsg,
.mmap = ?sock_no_mmap,
.sendpage = sock_no_sendpage,
};
【文章福利】小編推薦自己的Linux內(nèi)核技術(shù)交流群:【891587639】整理了一些個(gè)人覺得比較好的學(xué)習(xí)書籍、視頻資料共享在群文件里面,有需要的可以自行添加哦?。。。ê曨l教程、電子書、實(shí)戰(zhàn)項(xiàng)目及代碼)? ? ?


在AF_INET協(xié)議族中,TCP、UDP核心的結(jié)構(gòu)體:struct tcp_sock、struct udp_sock,它們代表TCP、UDP協(xié)議中socket底層的使用,在Netlink機(jī)制(AF_NETLINK協(xié)議族)中,netlink_sock作為底層socket的使用,如下所示**:**
struct netlink_sock {
/* 結(jié)構(gòu)體sock必須處于最前端,這樣可以通過sock指針找到netlink_sock*/
struct sock ?sk;
u32 ? portid; ?//表示本套接字自己綁定的id號,對于內(nèi)核來說它就是0
u32 ? dst_portid; //表示目的id號
u32 ? dst_group;
u32 ? flags;
u32 ? subscriptions;
u32 ? ngroups; //表示協(xié)議支持多播組數(shù)量
unsigned long ?*groups; //保存組位掩碼
unsigned long ?state;
size_t ? max_recvmsg_len;
wait_queue_head_t wait;
bool ? bound;
bool ? cb_running;
int ? dump_done_errno;
struct netlink_callback cb;
struct mutex ?*cb_mutex;//互斥鎖
struct mutex ?cb_def_mutex;//默認(rèn)的互斥鎖
void ? (*netlink_rcv)(struct sk_buff *skb);//保存接收用戶態(tài)數(shù)據(jù)后的處理函數(shù)
int ? (*netlink_bind)(struct net *net, int group);
void ? (*netlink_unbind)(struct net *net, int group);
struct module ?*module;
struct rhash_head node;
struct rcu_head ?rcu;
struct work_struct work;
};
由于struct sock 結(jié)構(gòu)體位于netlink_sock結(jié)構(gòu)體成員的最前端,所以在分配完struct sock后可以直接找到netlink_sock首地址(struct sock結(jié)構(gòu)體地址與netlink_sock結(jié)構(gòu)體首地址一樣 ),之后對netlink_sock進(jìn)行初始化,如下在netlinkl_cteate中的實(shí)現(xiàn)源碼,其中nlk_sk函數(shù)獲取到netlink_sock的首地址,也就是struct sock的首地址。
static int netlink_create(struct net *net, struct socket *sock, int protocol,
? ? int kern)
{
?struct netlink_sock *nlk;
......
nlk = nlk_sk(sock->sk);
nlk->module = module;
nlk->netlink_bind = bind;
nlk->netlink_unbind = unbind;
......
}
2、關(guān)于Netlink socket通信地址
以上將用戶態(tài)創(chuàng)建Netlink socket的過程進(jìn)行了淺析,在創(chuàng)建完socket后,用戶態(tài)便可進(jìn)行一系列的bind、send等操作。由于netlink機(jī)制是Linux內(nèi)核與用戶空間進(jìn)行雙向數(shù)據(jù)傳輸?shù)臋C(jī)制,那么與普通的socket通信有一些不同,如下圖在用戶態(tài) 通信地址上的不同:

用戶態(tài)通信地址結(jié)構(gòu):struct sockaddr_nl如下所示
struct sockaddr_nl {
__kernel_sa_family_t nl_family; //nl_family始終為AF_NETLINK
unsigned short nl_pad; ?//nl_pad始終為0
__u32 ?nl_pid; ?//nl_pid為netlink套接字的單播地址,在發(fā)送消息時(shí)用于表示目的套接字的地址
? ? ? ?__u32 ?nl_groups; //nl_groups表示組播組
};
nl_pid為netlink套接字的單播地址,在發(fā)送消息時(shí)用于表示目的套接字的地址,在用戶空間綁定時(shí)可以指定為當(dāng)前進(jìn)程的PID號。nl_groups表示組播組。在發(fā)送消息時(shí)用于表示目的多播組,在綁定地址時(shí)用于表示加入的多播組。這里nl_groups為一個(gè)32位無符號數(shù),其中的每一位表示一個(gè) 多播組,一個(gè)netlink套接字可以加入多個(gè)多播組用以接收多個(gè)多播組的多播消息(最多支持32個(gè))。
三、內(nèi)核Netlink套接字創(chuàng)建分析
N etlink預(yù)定義的協(xié)議類型: 如下所示,這些協(xié)議已經(jīng)為不同的系統(tǒng)應(yīng)用所使用,每種不同的應(yīng)用都有特有的傳輸數(shù)據(jù)的格式,因此如果用戶不使用這些協(xié)議,需要加入自己定義的協(xié)議號。
#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 /* Unused number, formerly ip_queue ?*/
#define NETLINK_SOCK_DIAG 4 /* 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
#define NETLINK_RDMA ?20
#define NETLINK_CRYPTO ?21 /* Crypto layer */
#define NETLINK_SMC ?22 /* SMC monitoring */
#define NETLINK_INET_DIAG NETLINK_SOCK_DIAG
#define MAX_LINKS 32
內(nèi)核netlink配置結(jié)構(gòu):
struct netlink_kernel_cfg,如下所示該結(jié)構(gòu)體包含了內(nèi)核netlink的可選參數(shù),其中g(shù)roups用于指定最大的多播組;flags成員可以為NL_CFG_F_NONROOT_RECV或NL_CFG_F_NONROOT_SEND,這兩個(gè)符號前者用來限定非超級用戶是否可以綁定到多播組,后者用來限定非超級用戶是否可以發(fā)送組播;input指針用于指定回調(diào)函數(shù),該回調(diào)函數(shù)用于接收和處理來自用戶空間的消息(若無需接收來自用戶空間的消息可不指定) ,最后的三個(gè)函數(shù)指針實(shí)現(xiàn)sock的綁定和解綁定等操作,會添加到nl_table對應(yīng)的項(xiàng)中去。
/* 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);
}; ?
netlink屬性頭: struct nlattr,如下所示,netlink的消息頭后面跟著的是消息的有效載荷部分,它采用的是格式為“類型——長度——值”,簡寫TLV。其中類型和長度使用屬性頭nlattr來表示。
struct nlattr {
__u16 ? ? ? ? ? nla_len;
__u16 ? ? ? ? ? nla_type;
};
netlink套接字結(jié)構(gòu)體: netlink_sock結(jié)構(gòu)體,其在用戶態(tài)系統(tǒng)調(diào)用創(chuàng)建的socket和內(nèi)核socket是一樣的,如下所示(在二、中介紹):
struct netlink_sock {
/* 結(jié)構(gòu)體sock必須處于最前端,這樣可以通過sock指針找到netlink_sock*/
struct sock ?sk;
u32 ? portid; ?//表示本套接字自己綁定的id號,對于內(nèi)核來說它就是0
u32 ? dst_portid; //表示目的id號
u32 ? dst_group;
u32 ? flags;
u32 ? subscriptions;
u32 ? ngroups; //表示協(xié)議支持多播組數(shù)量
unsigned long ?*groups; //保存組位掩碼
unsigned long ?state;
size_t ? max_recvmsg_len;
wait_queue_head_t wait;
bool ? bound;
bool ? cb_running;
int ? dump_done_errno;
struct netlink_callback cb;
struct mutex ?*cb_mutex;//互斥鎖
struct mutex ?cb_def_mutex;//默認(rèn)的互斥鎖
void ? (*netlink_rcv)(struct sk_buff *skb);//保存接收用戶態(tài)數(shù)據(jù)后的處理函數(shù)
int ? (*netlink_bind)(struct net *net, int group);
void ? (*netlink_unbind)(struct net *net, int group);
struct module ?*module;
struct rhash_head node;
struct rcu_head ?rcu;
struct work_struct work;
};
內(nèi)核Netlink套接字創(chuàng)建主流程如下所示,最終也會和用戶態(tài)通過系統(tǒng)調(diào)用創(chuàng)建socket的調(diào)用函數(shù)一樣:__netlink_create。

static inline struct sock *
netlink_kernel_create(struct net *net, int unit, struct netlink_kernel_cfg *cfg)
{
return __netlink_kernel_create(net, unit, THIS_MODULE, cfg);
}
netlink_kernel_create是對__netlink_kernel_create函數(shù)的封裝:
struct sock *
__netlink_kernel_create(struct net *net, int unit, struct module *module,
? struct netlink_kernel_cfg *cfg)
{
struct socket *sock;
struct sock *sk;
struct netlink_sock *nlk;//用于netlink中socket的使用
struct listeners *listeners = NULL;
struct mutex *cb_mutex = cfg ? cfg->cb_mutex : NULL;
......
if (sock_create_lite(PF_NETLINK, SOCK_DGRAM, unit, &sock))//分配socket結(jié)構(gòu)體,并初始化,將socket的type類型指定為SOCK_DGRAM
?return NULL;
if (__netlink_create(net, sock, cb_mutex, unit, 1) < 0)//分配struct sock、struct netlink_sock等,并初始化
?goto out_sock_release_nosk;
sk = sock->sk;
?......
sk->sk_data_ready = netlink_data_ready;
if (cfg && cfg->input)//設(shè)置接收數(shù)據(jù)包的回調(diào)函數(shù)函數(shù)
?nlk_sk(sk)->netlink_rcv = cfg->input;
?......
}
其中sock_create_lite函數(shù)分配socket結(jié)構(gòu)體并初始化:
int sock_create_lite(int family, int type, int protocol, struct socket **res)
{
int err;
struct socket *sock = NULL;
err = security_socket_create(family, type, protocol, 1);
if (err)
?goto out;
sock = sock_alloc(); ?//分配socket結(jié)構(gòu)體
if (!sock) {
?err = -ENOMEM;
?goto out;
}
sock->type = type; //設(shè)置socket類型
err = security_socket_post_create(sock, family, type, protocol, 1);
......
}
__netlink_create函數(shù)如下,創(chuàng)建struct sock,根據(jù)首地址獲取netlink_sock,并進(jìn)行初始化
static int __netlink_create(struct net *net, struct socket *sock,
? ? ? struct mutex *cb_mutex, int protocol,
? ? ? int kern)
{
struct sock *sk;
struct netlink_sock *nlk;
sock->ops = &netlink_ops;//設(shè)置函數(shù)操作集
sk = sk_alloc(net, PF_NETLINK, GFP_KERNEL, &netlink_proto, kern);
if (!sk)
?return -ENOMEM;
sock_init_data(sock, sk);
nlk = nlk_sk(sk); //從struct sk獲取netlink_sock首地址
if (cb_mutex) {
?lockdep_set_class_and_name(nlk->cb_mutex,
? ? ? ?nlk_cb_mutex_key
?nlk->cb_mutex = cb_mutex;
} else {
?nlk->cb_mutex = &nlk->cb_def_mutex;
?mutex_init(nlk->cb_mutex);s + protocol,
? ? ? ?nlk_cb_mutex_key_strings[protocol]);
}
init_waitqueue_head(&nlk->wait);
sk->sk_destruct = netlink_sock_destruct;
sk->sk_protocol = protocol;
return 0;
}
netlink_kernel_create函數(shù)最后將設(shè)置數(shù)據(jù)包接收函數(shù),如下所示:
if (cfg && cfg->input)
?nlk_sk(sk)->netlink_rcv = cfg->input;
內(nèi)核中netlink相關(guān)宏定義:
#define NLMSG_ALIGNTO ? 4U/* 宏 NLMSG_ALIGN(len) 用于得到不小于len且字節(jié)對齊的最小數(shù)值 */
#define NLMSG_ALIGN(len) ( ((len)+NLMSG_ALIGNTO-1) & ~(NLMSG_ALIGNTO-1) )
/* Netlink 頭部長度 */
#define NLMSG_HDRLEN ? ? ((int) NLMSG_ALIGN(sizeof(struct nlmsghdr)))
/* 計(jì)算消息數(shù)據(jù) len 的真實(shí)消息長度(消息體 + 消息頭)*/
#define NLMSG_LENGTH(len) ((len) + NLMSG_HDRLEN)
/* 宏 NLMSG_SPACE(len) 返回不小于 NLMSG_LENGTH(len) 且字節(jié)對齊的最小數(shù)值 */
#define NLMSG_SPACE(len) NLMSG_ALIGN(NLMSG_LENGTH(len))
/* 宏 NLMSG_DATA(nlh) 用于取得消息的數(shù)據(jù)部分的首地址,設(shè)置和讀取消息數(shù)據(jù)部分時(shí)需要使用該宏 */
#define NLMSG_DATA(nlh) ?((void*)(((char*)nlh) + NLMSG_LENGTH(0)))
/* 宏 NLMSG_NEXT(nlh,len) 用于得到下一個(gè)消息的首地址, 同時(shí) len 變?yōu)槭S嘞⒌拈L度 */
#define NLMSG_NEXT(nlh,len) ?((len) -= NLMSG_ALIGN((nlh)->nlmsg_len), \ ? ? ? ? ? ? ? ? ?(struct nlmsghdr*)(((char*)(nlh)) + NLMSG_ALIGN((nlh)->nlmsg_len)))
/* 判斷消息是否 >len */
#define NLMSG_OK(nlh,len) ((len) >= (int)sizeof(struct nlmsghdr) && \ ? ? ? ? ? ? ? (nlh)->nlmsg_len >= sizeof(struct nlmsghdr) && \ ? ? ? ? ? ? ? (nlh)->nlmsg_len <= (len))
/* NLMSG_PAYLOAD(nlh,len) 用于返回 payload 的長度*/
#define NLMSG_PAYLOAD(nlh,len) ((nlh)->nlmsg_len - NLMSG_SPACE((len)))
四、使用Netlink機(jī)制
內(nèi)核態(tài) netlink.c: 定義NETLINK_TEST協(xié)議,并實(shí)現(xiàn)input回調(diào)函數(shù)接口:net_rcv_msg,收到用戶態(tài)數(shù)據(jù)后向用戶態(tài)發(fā)送數(shù)據(jù)
#include <linux/init.h>
#include <linux/module.h>
#include <linux/types.h>
#include <net/sock.h>
#include <linux/netlink.h>
#define NETLINK_TEST ? ? 30
#define MSG_LEN ? ? ? ? ? ?100
#define USER_PORT ? ? ? ?66
struct sock *nlsk;
extern struct net init_net;
int send_usrmsg(char *pbuf, uint16_t len)
{
? ?struct sk_buff *nl_skb;
? ?struct nlmsghdr *nlh; ? //消息頭部
? ?int ret;
? ?//創(chuàng)建sk_buff
? ?nl_skb = nlmsg_new(len, GFP_ATOMIC);
? ?if(!nl_skb)
? ?{
? ? ? ?printk("netlink alloc failure\n");
? ? ? ?return -1;
? ?}
? ?
? ?/* 設(shè)置netlink消息頭部 */
? ?nlh = nlmsg_put(nl_skb, 0, 0, NETLINK_TEST, len, 0);
? ?if(nlh == NULL)
? ?{
? ? ? ?printk("nlmsg_put failaure \n");
? ? ? ?nlmsg_free(nl_skb);
? ? ? ?return -1;
? ?}
? ?/* 拷貝數(shù)據(jù)發(fā)送 */
? ?memcpy(nlmsg_data(nlh), pbuf, len);
? ?ret = netlink_unicast(nlsk, nl_skb, USER_PORT, MSG_DONTWAIT);
? ?return ret;
}
static void netlink_rcv_msg(struct sk_buff *skb)
{ ?
? ?struct nlmsghdr *nlh;
? ?char *umsg = NULL;
? ?char *kmsg = "hello users";
? ?//從skb中獲取data字段,并轉(zhuǎn)換成nlh進(jìn)行讀取
? ?nlh = nlmsg_hdr(skb);
? ?//讀取nlh后面的數(shù)據(jù)部分
? ?umsg = NLMSG_DATA(nlh);
? ?if(umsg){
? ? ? ?printk("kernel recv from user: %s\n", umsg);
? ? ? ?printk("port id :%d\n",NETLINK_CB(skb).portid);
? ? ? ?send_usrmsg(kmsg, strlen(kmsg)); ? ?//給用戶態(tài)發(fā)消息
? ?}
? ?
}
struct netlink_kernel_cfg cfg = {
? ?.input = netlink_rcv_msg,
};
static int __init test_netlink_init(void)
{
? ?nlsk ?= (struct sock *)netlink_kernel_create(&init_net,NETLINK_TEST,&cfg);
? ?return 0;
}
static void __exit test_netlink_exit(void)
{
? ?printk("exit......\n");
}
MODULE_LICENSE("GPL");
module_init(test_netlink_init);
module_exit(test_netlink_exit);
Makefile:
obj-m:=netlink.o ? ? ?
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?
CURRENT_PATH:=$(shell pwd) ? ?
LINUX_KERNEL:=$(shell uname -r) ?
LINUX_KERNEL_PATH:=/usr/src/linux-headers-$(LINUX_KERNEL) ?
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?
all:
make -C $(LINUX_KERNEL_PATH) M=$(CURRENT_PATH) modules ?#編譯模塊
clean:
make -C $(LINUX_KERNEL_PATH) M=$(CURRENT_PATH) clean ? #清理
用戶態(tài) user.c:給內(nèi)核發(fā)消息,并接收內(nèi)核發(fā)來的數(shù)據(jù)
#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <string.h>
#include <linux/netlink.h>
#include <stdint.h>
#include <unistd.h>
#define NETLINK_TEST 30
#define MSG_LEN 100
#define MAX_PLOAD 200
typedef struct _user_msg_info
{
? ?struct nlmsghdr hdr;
? ?char msg[MSG_LEN];
} user_msg_info;
int main(int argc,char **argv)
{
? ?int sockfd;
? ?/*
? ?struct sockaddr_nl {
? ? __kernel_sa_family_t ? ?nl_family; ?// AF_NETLINK (跟AF_INET對應(yīng))
? ? unsigned short ?nl_pad; ? ? // zero
? ? __u32 ? ? ? nl_pid; ? ? // port ID ?(通信端口號)
? ? __u32 ? ? ? nl_groups; ?//multicast groups mask
};
? ?*/
? ?struct sockaddr_nl saddr, daddr;
? /* struct nlmsghd 是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
};
*/
? ?struct nlmsghdr *nlh;
? ?user_msg_info u_info;
? ?char *msg = "hell kernel, I am user process!";
? ?socklen_t len;
? ?//創(chuàng)建socket
? ?sockfd = socket(AF_NETLINK,SOCK_RAW, NETLINK_TEST);
? ?//初始化地址
? ?memset(&saddr,0,sizeof(saddr));
? ?
? ?//地址賦值
? ?saddr.nl_family = AF_NETLINK;
? ?saddr.nl_pad = 0;
? ?saddr.nl_pid = 66;
? ?saddr.nl_groups = 0;
? ?//地址與sockt綁定-bind
? ?bind(sockfd,(struct sockaddr *)&saddr,sizeof(saddr));
? ?
? ?//初始化目的地址
? ?memset(&daddr,0,sizeof(daddr));
? ?memset(&daddr, 0, sizeof(daddr));
? ?daddr.nl_family = AF_NETLINK;
? ?daddr.nl_pid = 0; // to kernel
? ?daddr.nl_groups = 0;
? ?
? ?//初始化消息頭
? ?nlh = (struct nlmsghdr *)malloc(NLMSG_SPACE(MAX_PLOAD));
? ?memset(nlh, 0, sizeof(struct nlmsghdr));
? ?nlh->nlmsg_len = NLMSG_SPACE(MAX_PLOAD);
? ?nlh->nlmsg_flags = 0;
? ?nlh->nlmsg_type = 0;
? ?nlh->nlmsg_seq = 0;
? ?nlh->nlmsg_pid = saddr.nl_pid; //self port
? ?//設(shè)置消息內(nèi)容
? ?memcpy(NLMSG_DATA(nlh),msg,strlen(msg));
? ?//發(fā)送消息
? ?sendto(sockfd,nlh,nlh->nlmsg_len,0,(struct sockaddr *)&daddr,sizeof(struct sockaddr_nl));
? ?printf("send kernel :%s",msg);
? ?
? ?memset(&u_info, 0, sizeof(u_info));
? ?len = sizeof(struct sockaddr_nl);
? ?//接收消息
? ?recvfrom(sockfd,&u_info,sizeof(user_msg_info),0,(struct sockaddr *)&daddr,&len);
? ?printf("\n");
? ?printf("from kernel:%s\n",u_info.msg);
? ?close(sockfd);
? ?return 0;
}
編譯、插入內(nèi)核:創(chuàng)建內(nèi)核Netlink socket:

運(yùn)行用戶態(tài)程序:向內(nèi)核發(fā)送消息,并接收來自內(nèi)核的消息:

