netlink组播的使用

Linux的netlink机制是非常好的Linux内核与应用层进行双向交互数据的方式。其常用的单播方式可以实现内核为服务端,应用层为客户端的通信方式。如果需要实现应用层为服务端,内核为客户端的通信方式,则需要使用组播。这种场景一般是应用层守护进程需要实现获取内核的某些模块的状态信息。

内核中已经定义好的组有:

#define NETLINK_ROUTE           0       /* Routing/device hook                          */
#define NETLINK_W1 1 /* 1-wire subsystem */
#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

内核代码:

 1 #include <linux/module.h>
 2 #include <linux/kernel.h>
 3 #include <linux/init.h>
 4 #include <net/sock.h>
 5 #include <linux/socket.h>
 6 #include <linux/net.h>
 7 #include <asm/types.h>
 8 #include <linux/netlink.h>
 9 #include <linux/rtnetlink.h>
10 #include <linux/skbuff.h>
11 #include <linux/delay.h>
12 
13 #define NETLINK_USER 29  //User defined group, consistent in both kernel prog and user prog
14 #define MYGRP 2 //User defined group, consistent in both kernel prog and user prog
15 
16 struct sock *nl_sk = NULL;
17 
18 static void send_to_user(void);
19 
20 static void send_to_user(void)
21 {
22     struct sk_buff *skb_out;
23     struct nlmsghdr *nlh;
24     int msg_size;
25     char msg[20] = "Hello from kernel";
26     int res;
27 
28     printk(KERN_INFO "Entering: %s
", __FUNCTION__);
29     msg_size = strlen(msg);
30     printk(KERN_INFO "msg_size: %d
", msg_size);
31     //msg[msg_size - 1] = '';
32     skb_out = nlmsg_new(msg_size, 0);
33 
34     if (!skb_out) {
35         printk(KERN_ERR "Failed to allocate new skb
");
36         return;
37     }
38     nlh = nlmsg_put(skb_out, 0, 1, NLMSG_DONE, msg_size, 0);
39     //NETLINK_CB(skb_out).dst_group = 1; /* Multicast to group 1, 1<<0 */
40     strncpy(nlmsg_data(nlh), msg, msg_size);
41 
42     res = nlmsg_multicast(nl_sk, skb_out, 0, MYGRP, 0);
43     if (res < 0) {
44         printk(KERN_INFO "Error while sending bak to user, err id: %d
", res);
45     }
46 }
47 
48 static int __init
49 hello_init(void) {
50 
51     struct netlink_kernel_cfg cfg = {
52         .groups = MYGRP,
53     };
54     printk("Entering: %s
", __FUNCTION__);
55     nl_sk = netlink_kernel_create(&init_net, NETLINK_USER, &cfg);
56     if (!nl_sk) {
57         printk(KERN_ALERT "Error creating socket.
");
58         return -10;
59     }
60 
61     msleep(10000);
62     send_to_user();
63 
64     return 0;
65 }
66 
67 static void __exit
68 hello_exit(void) {
69 
70     printk(KERN_INFO "exiting hello module
");
71     netlink_kernel_release(nl_sk);
72 }
73 
74 module_init(hello_init);
75 module_exit(hello_exit);
76 MODULE_LICENSE("GPL");

应用层代码:

 1 #include<stdio.h>
 2 #include<string.h>
 3 #include<stdlib.h>
 4 #include<sys/socket.h>
 5 #include<linux/netlink.h>
 6 #include<sys/types.h>
 7 #include<unistd.h>
 8 
 9 #define MYPROTO 29 //User defined group, consistent in both kernel prog and user prog
10 #define MYMGRP 2 //User defined group, consistent in both kernel prog and user prog
11 
12 int open_netlink()
13 {
14     int sock = socket(AF_NETLINK,SOCK_RAW,MYPROTO);
15     struct sockaddr_nl addr;
16 
17     memset((void *)&addr, 0, sizeof(addr));
18 
19     if (sock<0)
20         return sock;
21     addr.nl_family = AF_NETLINK;
22     addr.nl_pid = getpid();
23     addr.nl_groups = MYMGRP;
24     if (bind(sock,(struct sockaddr *)&addr,sizeof(addr))<0)
25         return -1;
26     return sock;
27 }
28 
29 int read_event(int sock)
30 {
31     struct sockaddr_nl nladdr;
32     struct msghdr msg;
33     struct iovec iov[2];
34     struct nlmsghdr nlh;
35     char buffer[65536];
36     int ret;
37     iov[0].iov_base = (void *)&nlh;
38     iov[0].iov_len = sizeof(nlh);
39     iov[1].iov_base = (void *)buffer;
40     iov[1].iov_len = sizeof(buffer);
41     msg.msg_name = (void *)&(nladdr);
42     msg.msg_namelen = sizeof(nladdr);
43     msg.msg_iov = iov;
44     msg.msg_iovlen = sizeof(iov)/sizeof(iov[0]);
45     ret=recvmsg(sock, &msg, 0);
46     if (ret<0) {
47         return ret;
48     }
49     printf("Received message payload: %s
", NLMSG_DATA(&nlh));
50     char *a = NLMSG_DATA(&nlh);
51     int i;
52     printf("Recv Data:
");
53     for(i = 0; i < 10; i++) {
54         printf("%c", a[i]);
55     }
56     printf("
");
57 }
58 
59 int main(int argc, char *argv[])
60 {
61     int nls = open_netlink();
62     if (nls<0) {
63         err(1,"netlink");
64     }
65 
66     while (1)
67         read_event(nls);
68     return 0;
69 }

其中 协议类型与组号 内核要与应用层保持一致,且内核模块要先运行。

原文地址:https://www.cnblogs.com/sinaxyz/p/4525208.html