其他分享
首页 > 其他分享> > netlink

netlink

作者:互联网

 

1. netlink

Netlink套接字是用以实现用户进程与内核进程通信的一种特殊的进程间通信(IPC) ,也是网络应用程序与内核通信的最常用的接口。

在Linux 内核中,使用netlink 进行应用与内核通信的应用有很多,如
路由 daemon(NETLINK_ROUTE)
用户态 socket 协议(NETLINK_USERSOCK)
防火墙(NETLINK_FIREWALL)
netfilter 子系统(NETLINK_NETFILTER)
内核事件向用户态通知(NETLINK_KOBJECT_UEVENT)
通用netlink(NETLINK_GENERIC)

Netlink 是一种在内核与用户应用间进行双向数据传输的非常好的方式,用户态应用使用标准的 socket API 就可以使用 netlink 提供的强大功能,内核态需要使用专门的内核 API 来使用 netlink。一般来说用户空间和内核空间的通信方式有三种:/proc、ioctl、Netlink。而前两种都是单向的,而Netlink可以实现双工通信。

 

2.3 netlink实例 

对netlink的测试分为两部分,一部分是内核提供接收用户空间消息,并响应发送功能;另一部分是用户空间发送netlink到内核,并等待回复。

2.3.1 内核netlink测试代码

新建netlink_kernel.c文件:

 

#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 USER_PORT 100
int netlink_count = 0;
char netlink_kmsg[30];

struct sock *nlsk = NULL;
extern struct net init_net;

int send_usrmsg(char *pbuf, uint16_t len)
{
struct sk_buff *nl_skb;
struct nlmsghdr *nlh;

int ret;

//Create sk_buff using nlmsg_new().
nl_skb = nlmsg_new(len, GFP_ATOMIC);
if(!nl_skb)
{
printk("netlink alloc failure\n");
return -1;
}

//Set up nlmsghdr.
nlh = nlmsg_put(nl_skb, 0, 0, NETLINK_TEST, len, 0);
if(nlh == NULL)
{
printk("nlmsg_put failaure \n");
nlmsg_free(nl_skb); //If nlmsg_put() failed, nlmsg_free() will free sk_buff.
return -1;
}

//Copy pbuf to nlmsghdr payload.
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 = NULL;
char *umsg = NULL;
//char *kmsg = "hello users!!!";
char *kmsg;

if(skb->len >= nlmsg_total_size(0))
{
netlink_count++;
snprintf(netlink_kmsg, sizeof(netlink_kmsg), "hello users count=%d", netlink_count);
kmsg = netlink_kmsg;
nlh = nlmsg_hdr(skb); //Get nlmsghdr from sk_buff.
umsg = NLMSG_DATA(nlh); //Get payload from nlmsghdr.
if(umsg)
{
printk("kernel recv from user: %s\n", umsg);
send_usrmsg(kmsg, strlen(kmsg));
}
}
}

struct netlink_kernel_cfg cfg = {
.input = netlink_rcv_msg, /* set recv callback */
};

__init int netlink_test_init(void)
{
/* Create netlink socket */
nlsk = (struct sock *)netlink_kernel_create(&init_net, NETLINK_TEST, &cfg);
if(nlsk == NULL)
{
printk("netlink_kernel_create error !\n");
return -1;
}
printk("netlink_test_init\n");

return 0;
}

__exit void netlink_test_exit(void)
{
if (nlsk){
netlink_kernel_release(nlsk); /* release ..*/
nlsk = NULL;
}
printk("netlink_test_exit!\n");
}

module_init(netlink_test_init);
module_exit(netlink_test_exit);

MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("netlink test");

 

 

根据kbuild规范编写

Makefile如下:

obj-m := netlink_kernel.o

PWD := $(shell pwd)
KERNEL_DIR := "/usr/src/linux-headers-"$(shell uname -r)/

modules:
@$(MAKE) -C $(KERNEL_DIR) M=$(PWD) modules
clean:
@rm -rf *.ko *.o *.mod.c *symvers *order *cmd

 

 

 

 

insmod netlink_kernel.ko

dmesg

 

 

用户空间应用:

用户空间netlink测试代码

新建netlink_user.c文件:

 

#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <string.h>
#include <linux/netlink.h>
#include <stdint.h>
#include <unistd.h>
#include <errno.h>

#define NETLINK_TEST 30
#define USER_PORT 100
#define MAX_PLOAD 125
#define MSG_LEN 125

typedef struct _user_msg_info
{
struct nlmsghdr hdr;
char msg[MSG_LEN];
} user_msg_info;

int main(int argc, char **argv)
{
int skfd;
int ret;
user_msg_info u_info;
socklen_t len;
struct nlmsghdr *nlh = NULL;
struct sockaddr_nl saddr, daddr;
char *umsg = "hello netlink!!";
int loop_count = 0;

/*Create netlink socket*/
skfd = socket(AF_NETLINK, SOCK_RAW, NETLINK_TEST); //Create a socket using user defined protocol NETLINK_TEST.
if(skfd == -1)
{
perror("create socket error\n");
return -1;
}

//Source address.
memset(&saddr, 0, sizeof(saddr));
saddr.nl_family = AF_NETLINK; //AF_NETLINK
saddr.nl_pid = USER_PORT; //netlink portid, same as kernel.
saddr.nl_groups = 0;
if(bind(skfd, (struct sockaddr *)&saddr, sizeof(saddr)) != 0) //bind to skfd with saddr.
{
perror("bind() error\n");
close(skfd);
return -1;
}

//Destination address.
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

memcpy(NLMSG_DATA(nlh), umsg, strlen(umsg));
while(loop_count < 11) {
printf("sendto kernel:%s\n", umsg);
ret = sendto(skfd, nlh, nlh->nlmsg_len, 0, (struct sockaddr *)&daddr, sizeof(struct sockaddr_nl));
if(!ret)
{
perror("sendto error\n");
close(skfd);
exit(-1);
}

//Receive netlink message from kernel.
memset(&u_info, 0, sizeof(u_info));
len = sizeof(struct sockaddr_nl);
ret = recvfrom(skfd, &u_info, sizeof(user_msg_info), 0, (struct sockaddr *)&daddr, &len);
if(!ret)
{
perror("recv form kernel error\n");
close(skfd);
exit(-1);
}

printf("from kernel:%s\n", u_info.msg);
//usleep(1000);
loop_count++;
}
close(skfd);

free((void *)nlh);
return 0;
}

 

 

然后通过gcc netlink_user.c -o netlink_user编译。

 

2.3.3 测试结果

将编译的内核module通过sudo insmod netlink_kernel.kl加载到内核。

然后执行./netlink_user,得到如下结果。

 

sendto kernel:hello netlink!!
from kernel:hello users count=1
sendto kernel:hello netlink!!
from kernel:hello users count=2
sendto kernel:hello netlink!!
from kernel:hello users count=3
sendto kernel:hello netlink!!
from kernel:hello users count=4
sendto kernel:hello netlink!!
from kernel:hello users count=5
sendto kernel:hello netlink!!
from kernel:hello users count=6
sendto kernel:hello netlink!!
from kernel:hello users count=7
sendto kernel:hello netlink!!
from kernel:hello users count=8
sendto kernel:hello netlink!!
from kernel:hello users count=9
sendto kernel:hello netlink!!
from kernel:hello users count=10
sendto kernel:hello netlink!!
from kernel:hello users count=11

 

dmesg

 

 

 

 

 

 

 

 

 编写内核模块:

 

写一个hello.c的测试文件:

 

#include <linux/kernel.h> /*Needed by all modules*/
#include <linux/module.h> /*Needed for KERN_* */
#include <linux/init.h> /* Needed for the macros */

MODULE_LICENSE("GPL");

static int year=2014;

static int hello_init(void)
{
printk(KERN_WARNING "Hello kernel, it's %d!\n",year);
return 0;
}


static void hello_exit(void)
{
printk("Bye, kernel!\n");
}

/* main module function*/
module_init(hello_init);
module_exit(hello_exit);

 

 

 然后用kbuild标准写一个Makefile文件:

obj-m := hello.o

all :
$(MAKE) -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules

clean:
$(MAKE) -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean

 

obj-m := name.o表示会编译一个模块(-m),生成的name.o文件来自于name.c文件。

uname -r 命令返回的实际就是你现在使用的内核版本

 

所以-C选项后面完整的路径是:/lib/modules/3.13.0-19-generic/build,但是不建议给出具体的路径,而是用$(shell uname -r)这种可变的路径,这样,当你在低版本内核中编译过,拿到高版本内核中再次编译的时候也能正常通过。

编译完成之后能够看到模块文件:
$sudo insmod ./hello.ko #加载
$sudo rmmod hello #删除 或者 sudo rmmod ./hello.ko

 

 

 

 

 

参考:

https://www.cnblogs.com/arnoldlu/p/9532254.html

https://www.cnblogs.com/nerohwang/p/3621316.html

 

标签:count,kernel,netlink,struct,nlh,hello
来源: https://www.cnblogs.com/rebrobot/p/16450803.html