af_netlink_c-如何使用netlink套接字与内核模块通信?

我正在尝试编写一个使用netlink与用户进程通信的linux内核模块。 我使用netlink是因为我要交流的用户程序仅使用套接字进行通信,因此无法更改以添加netlink_unicast()或其他任何内容。

问题是我不知道该怎么做。 我已经用谷歌搜索过,但是我发现的所有示例都是像这样的旧示例,对于当前的内核版本不再有效。 我也看过这个SO问题,但是这里的示例使用libnl进行套接字操作,但是我想坚持使用标准套接字功能(由netlink_unicast()定义)。 因此,有人可以指导我在这里找到一些教程或指南,或者可以帮助我理解netlink的界面和用法的东西。 我将高度赞赏一个可行的示例,没有什么花哨的地方,只是一个非常基本的示例,该示例说明了如何建立从用户程序中的套接字到内核中的套接字的连接,然后将数据从用户进程发送到内核并从内核接收回来。

另外,请不要告诉我看内核代码。 我已经在做,但是要花很多时间,而且我没有很多了。

更新:经过大量的试验和错误后,我有以下代码将消息从用户程序发送到内核,但是消息从内核到用户程序,即使用netlink_unicast()无法正常工作。 它不仅不起作用,而且调用使系统挂起,然后我必须重新启动计算机。 可以请一个人看看并告诉我我在做什么错。 以下代码中注释了netlink_unicast()调用。 对于内核到用户程序的消息,应该不加注释。

用户程序

#include

#include

#define NETLINK_USER 31

#define MAX_PAYLOAD 1024 /* maximum payload size*/

struct sockaddr_nl src_addr, dest_addr;

struct nlmsghdr *nlh = NULL;

struct iovec iov;

int sock_fd;

struct msghdr msg;

void main()

{

sock_fd=socket(PF_NETLINK, SOCK_RAW, NETLINK_USER);

if(sock_fd<0)

return -1;

memset(&src_addr, 0, sizeof(src_addr));

src_addr.nl_family = AF_NETLINK;

src_addr.nl_pid = getpid(); /* self pid */

/* interested in group 1<<0 */

bind(sock_fd, (struct sockaddr*)&src_addr,

sizeof(src_addr));

memset(&dest_addr, 0, sizeof(dest_addr));

memset(&dest_addr, 0, sizeof(dest_addr));

dest_addr.nl_family = AF_NETLINK;

dest_addr.nl_pid = 0; /* For Linux Kernel */

dest_addr.nl_groups = 0; /* unicast */

nlh = (struct nlmsghdr *)malloc(

NLMSG_SPACE(MAX_PAYLOAD));

memset(nlh, 0, NLMSG_SPACE(MAX_PAYLOAD));

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 = nlh->nlmsg_len;

msg.msg_name = (void *)&dest_addr;

msg.msg_namelen = sizeof(dest_addr);

msg.msg_iov = &iov;

msg.msg_iovlen = 1;

printf("Sending message to kernel\n");

sendmsg(sock_fd,&msg,0);

printf("Waiting for message from kernel\n");

/* Read message from kernel */

recvmsg(sock_fd, &msg, 0);

printf(" Received message payload: %s\n",

NLMSG_DATA(nlh));

close(sock_fd);

}

内核代码

#include

#include

#include

#include

#include

#include

#include

#include

#include

#define NETLINK_USER 31

struct sock *nl_sk = NULL;

static void hello_nl_recv_msg(struct sk_buff *skb)

{

struct nlmsghdr *nlh;

int pid;

printk(KERN_INFO "Entering: %s\n", __FUNCTION__);

nlh=(struct nlmsghdr*)skb->data;

printk(KERN_INFO "Netlink received msg payload: %s\n",

(char*)NLMSG_DATA(nlh));

pid = nlh->nlmsg_pid; /*pid of sending process */

NETLINK_CB(skb).dst_group = 0; /* not in mcast group */

NETLINK_CB(skb).pid = 0; /* from kernel */

//NETLINK_CB(skb).groups = 0; /* not in mcast group */

//NETLINK_CB(skb).dst_pid = pid;

printk("About to send msg bak:\n");

//netlink_unicast(nl_sk,skb,pid,MSG_DONTWAIT);

}

static int __init hello_init(void)

{

printk("Entering: %s\n",__FUNCTION__);

nl_sk=netlink_kernel_create(&init_net, NETLINK_USER, 0,

hello_nl_recv_msg, NULL, THIS_MODULE);

if(!nl_sk)

{

printk(KERN_ALERT "Error creating socket.\n");

return -10;

}

return 0;

}

static void __exit hello_exit(void)

{

printk(KERN_INFO "exiting hello module\n");

netlink_kernel_release(nl_sk);

}

module_init(hello_init);

module_exit(hello_exit);

一、imp2源码 整个源码包含三个文件:imp2_k.c, imp2_u.c和imp2.h. 其中imp2_k.c为内核模块的源代码,imp2_u.c为应用程序,即测试代码,imp2.h为两个源文件都需要引用的头文件。其整体的功能是:注册一种新的netlink协议,并注册一个新的NF hook函数。当有ping包发往当前主机或者经过当前主机转发时,内核向用户发送ping包的源IP和目的IP。各个文件的简单分析见下文。 1. imp2.h 该文件主要是定义了一种新的Netlink协议类型NL_IMP2(31)。新的协议类型的选值不能和当前内核中已经定义的netlink协议类型重复。定义了基于该协议类型的消息类型,内核根据接收到消息的不同类型,进行不同的处理:IMP2_U_PID和IMP2_CLOSE分别为请求和关闭。IMP2_K_MSG代表内核空间发送的消息。 该头文件的源码如下: 2. imp2_k.c 该程序为内核模块程序。其完成的功能如下: (1)创建一种新的Netlink协议NL_IMP2,并注册该协议的回调函数kernel_receive。但用户空间通过建立且协议类型为NL_IMP2的socket套接字并调用sendto,sendmsg函数发送数据时,传送到内核空间的数据由kernel_receive进行处理。该函数主要是记录用户进程的ID,用于随后发送数据的时候指定目的。 (2)在Netfilter的hook点NF_IP_PRE_ROUTING注册hook函数get_icmp,对经过该hook点的ping包进行处理。get_icmp首先判断是否是ping包,如果不是,直接Accept。如果是,则记录该包的源IP和目的IP,然后调用send_to_user,将记录的信息发送给kernel_recieve函数中记录的用户进程ID。 该文件的源码如下: 3. imp2_u.c 该程序为用户空间的测试程序。该程序包括以下功能: (1)生成NL_IMP2协议的socket.然后通过调用sendto发送IMP2_U_PID类型的请求信息给内核。然后等待接受内核发回的信息。记住:仅当有ping包经过内核的NF时,内核才会向用户进程发送信息。 (2)当用户进程通过Ctrl+C来结束该程序时,调用信号处理函数sig_int,向内核发送IMP2_CLOSE的消息,结束socket。 该文件的源码如下: 二、编译和测试 1. 整个源文件编译的Makefile如下: all: gcc -O2 -DMODULE -D__KERNEL__ -W -Wstrict-prototypes -Wmissing-prototypes -isystem /lib/modules/`uname -r`/build/include -c -o imp2_k.o imp2_k.c gcc imp2_u.c -o imp2_u install: insmod imp2_k.o uninstall: rmmod imp2_k clean: rm -f imp2_k.o imp2_u 2. 加载内核模块,并执行测试程序。 #make install #./imp2_u 当没有ping包时,终端一直处于等待输出状态。通过另一台主机(192.168.1.100)向当前主机(192.168.1.101)发送ping包,则终端已经有输出: #./imp2_u [root@localhost imp2]# ./imp2_u src: 192.168.1.100, dest: 192.168.1.101 src: 192.168.1.100, dest: 192.168.1.101 src: 192.168.1.100, dest: 192.168.1.101 src: 192.168.1.100, dest: 192.168.1.101
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值