欢迎您访问 最编程 本站为您分享编程语言代码,编程技术文章!
您现在的位置是: 首页

Linux:网联简介 - 2.网联示例

最编程 2024-04-28 07:23:17
...

Netlink 通信包括 内核空间用户空间 两部分,具体来讲,是两个分别位于 内核空间用户空间AF_NETLINK 协议簇套接字。来看一个具体的例子,先看 内核空间 部分 netlink_kern_test.c

#include <linux/init.h>
#include <linux/module.h>
#include <linux/types.h>
#include <net/sock.h>
#include <linux/netlink.h>

extern struct net init_net;

#define NETLINK_TEST	30
#define MSG_LEN			100
#define USER_PORT		66

static struct sock *test_nlsk;

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

	nl_skb = nlmsg_new(len, GFP_ATOMIC);
    if (!nl_skb) {
        pr_err("netlink alloc failure\n");
        return -1;
    }

	nlh = nlmsg_put(nl_skb, 0, 0, NETLINK_TEST, len, 0);
    if (nlh == NULL) {
        pr_err("nlmsg_put failure\n");
        nlmsg_free(nl_skb);
        return -1;
    }

	memcpy(nlmsg_data(nlh), buf, len);

	return netlink_unicast(test_nlsk, nl_skb, USER_PORT, MSG_DONTWAIT);
}

static void netlink_rcv_msg(struct sk_buff *skb)
{   
    struct nlmsghdr *nlh;
    char *umsg = NULL;
    char *kmsg = "Hello user process!";
	
	nlh = nlmsg_hdr(skb);
    umsg = NLMSG_DATA(nlh);
    if (umsg) {
        printk("kernel recv msg from user: %s\n", umsg);
        printk("port id: %d\n", NETLINK_CB(skb).portid);
        send_usrmsg(kmsg, strlen(kmsg));
    }
}

static int __init test_netlink_init(void)
{
	struct netlink_kernel_cfg cfg = {
		.input = netlink_rcv_msg,
	};
	
	 test_nlsk = (struct sock *)netlink_kernel_create(&init_net, NETLINK_TEST, &cfg);

	return test_nlsk ? 0 : -1;
}

static void __exit test_netlink_exit(void)
{
    netlink_kernel_release(test_nlsk);
}

module_init(test_netlink_init);
module_exit(test_netlink_exit);

MODULE_LICENSE("GPL");

内核 Netlink 示例模块的逻辑很简单,调用接口 netlink_kernel_create() 创建一个 Netlink 内核空间套接字,且其协议类型为自定的 NETLINK_TEST,其数据接收接口为 netlink_rcv_msg() 。当其它 Netlink 套接字向它发送数据时,内核调用 netlink_rcv_msg() 处理数据接收,此时可通过调用 netlink_unicast() 向数据发送套接字回送数据。
接下来构建一个 Makefile 来编译这个内核模块,并安装到系统。Makefile 内容如下:

ifneq ($(KERNELRELEASE),)

obj-m	:= netlink_kern_test.o

else

KERNELDIR ?= /lib/modules/$(shell uname -r)/build
PWD       := $(shell pwd)

modules:
	$(MAKE) -C $(KERNELDIR) M=$(PWD) modules

endif

clean:
	rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions .cache.mk modules.order Module.symvers
$ make
$ sudo insmod netlink_kern_test.ko

再看 Netlink 示例的用户控件部分 netlink_user_test.c

#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 USER_PORT 66

#define MSG_LEN 100
#define MAX_PLOAD 200

struct netlink_user_msg {
    struct nlmsghdr hdr;
    char msg[MSG_LEN];
};

int main(int argc,char **argv)
{
    int sockfd;
    struct sockaddr_nl saddr, daddr;
    struct nlmsghdr *nlh;
    struct netlink_user_msg u_info;
    char *msg = "Hello kernel, I am a user process!";
    socklen_t len;

	// 创建 Netlink 用户空间套接字
	sockfd = socket(AF_NETLINK, SOCK_RAW, NETLINK_TEST);
	if (sockfd < 0) {
		perror("socket");
		return -1;
	}

	// 数据 源 Netlink 套接字 地址
	memset(&saddr, 0, sizeof(saddr));
    saddr.nl_family = AF_NETLINK;
    saddr.nl_pad = 0;
    saddr.nl_pid = USER_PORT; // 端口号
    saddr.nl_groups = 0;
    if (bind(sockfd, (struct sockaddr *)&saddr, sizeof(saddr)) < 0) {
		perror("socket");
		return -1;
	}

	// Netlink 消息数据
	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;
    memcpy(NLMSG_DATA(nlh), msg, strlen(msg));

	// 数据 目的 Netlink 套接字 地址
	memset(&daddr, 0, sizeof(daddr));
    daddr.nl_family = AF_NETLINK;
    daddr.nl_pid = 0; /* 0 表示数据发往 内核空间 Netlink 套接字 */
    daddr.nl_groups = 0;
    sendto(sockfd, nlh, nlh->nlmsg_len, 0, 
			(struct sockaddr *)&daddr, sizeof(struct sockaddr_nl));
    printf("send kernel: %s", msg);

	// 从内核 Netlink 套接字接收数据
	memset(&daddr, 0, sizeof(daddr));
    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(%u): %s\n", daddr.nl_pid, u_info.msg);

	close(sockfd);

	return 0;
}

编译执行用户空间测试程序 netlink_user_test

$ make netlink_user_test
$ ./netlink_user_test

本文示例是 内核空间用户空间 Netlink 套接字通信,事实上,内核空间的 Netlink 相互之间也可以通信,而且 Netlink 不仅支持单播通信,还支持广播通信,本文对此不做展开。

推荐阅读