最美情侣中文字幕电影,在线麻豆精品传媒,在线网站高清黄,久久黄色视频

歡迎光臨散文網(wǎng) 會員登陸 & 注冊

Linux內(nèi)核 | Netlink機(jī)制分析與使用

2022-07-31 12:25 作者:補(bǔ)給站Linux內(nèi)核  | 我要投稿

前面文章分析了sockfs文件系統(tǒng)、socket底層的原理:

淺析Linux sockfs文件系統(tǒng)

Linux內(nèi)核 |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)核的消息:




Linux內(nèi)核 | Netlink機(jī)制分析與使用的評論 (共 條)

分享到微博請遵守國家法律
米泉市| 和平县| 广宁县| 遵化市| 丰城市| 棋牌| 竹山县| 农安县| 牟定县| 句容市| 鄂州市| 邯郸县| 济南市| 安丘市| 内黄县| 尖扎县| 东乌珠穆沁旗| 雷山县| 广宗县| 拜城县| 富源县| 南安市| 镇赉县| 胶州市| 嫩江县| 涞水县| 天水市| 永年县| 繁昌县| 东光县| 盘山县| 嘉善县| 伊通| 陵川县| 偏关县| 南京市| 阿克陶县| 深水埗区| 东光县| 交口县| 游戏|