socket(AF_NETLINK, SOCK_RAW, 0)原理
时间: 2024-04-05 09:31:48 浏览: 27
socket(AF_NETLINK, SOCK_RAW, 0)是在Linux系统中创建一个Netlink套接字,用于与内核之间进行通信。该函数中的参数含义如下:
- AF_NETLINK:表示使用Netlink协议,该协议用于内核与用户空间进行通信。
- SOCK_RAW:表示使用原始套接字,即数据报文不会被内核进行任何处理。
- 0:表示使用默认的协议,即NETLINK_ROUTE协议。
Netlink协议是Linux内核与用户空间之间通信的一种机制,它定义了一组消息类型,用于在内核和用户空间之间进行交互。通过Netlink套接字,用户空间可以向内核发送消息,也可以接收内核发送的消息。
在Linux系统中,Netlink协议主要用于以下几个方面:
1. 系统配置:可以使用Netlink协议配置网络接口、路由、防火墙等系统参数。
2. 系统监控:可以使用Netlink协议监控网络状态、进程状态等系统信息。
3. 内核扩展:可以使用Netlink协议在内核中实现新的模块或功能。
需要注意的是,使用Netlink套接字需要了解Netlink协议的消息格式和使用方法。同时,Netlink协议是一种异步通信机制,在发送和接收消息时需要使用异步IO或者多线程等机制来处理消息。
相关问题
linux如何创建PF_NETLINK协议通信,写出服务端和客户端
PF_NETLINK是Linux内核提供的一种用于内核与用户空间进程之间通信的协议。下面是一个简单的PF_NETLINK服务端和客户端的示例代码:
服务端代码:
```
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <linux/netlink.h>
#define NETLINK_TEST 25
#define MSG_LEN 100
int main(int argc, char *argv[]) {
int skfd;
struct sockaddr_nl saddr, daddr;
struct nlmsghdr *nlh = NULL;
struct iovec iov;
struct msghdr msg;
char msg_buf[MSG_LEN];
// 创建socket
skfd = socket(AF_NETLINK, SOCK_RAW, NETLINK_TEST);
if (skfd == -1) {
perror("socket");
exit(EXIT_FAILURE);
}
// 绑定源地址
memset(&saddr, 0, sizeof(saddr));
saddr.nl_family = AF_NETLINK;
saddr.nl_pid = getpid();
saddr.nl_groups = 0;
if (bind(skfd, (struct sockaddr *)&saddr, sizeof(saddr)) != 0) {
perror("bind");
close(skfd);
exit(EXIT_FAILURE);
}
// 初始化目标地址
memset(&daddr, 0, sizeof(daddr));
daddr.nl_family = AF_NETLINK;
daddr.nl_pid = 0; // 目标地址为内核
daddr.nl_groups = 0;
// 构造netlink消息
nlh = (struct nlmsghdr *)msg_buf;
nlh->nlmsg_len = NLMSG_LENGTH(strlen("Hello from kernel!"));
nlh->nlmsg_pid = getpid();
nlh->nlmsg_flags = 0;
strcpy(NLMSG_DATA(nlh), "Hello from kernel!");
// 设置iov
iov.iov_base = (void *)nlh;
iov.iov_len = nlh->nlmsg_len;
// 设置msghdr
memset(&msg, 0, sizeof(msg));
msg.msg_name = (void *)&daddr;
msg.msg_namelen = sizeof(daddr);
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
// 发送消息
if (sendmsg(skfd, &msg, 0) == -1) {
perror("sendmsg");
close(skfd);
exit(EXIT_FAILURE);
}
printf("Kernel message sent.\n");
close(skfd);
return 0;
}
```
客户端代码:
```
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <linux/netlink.h>
#define NETLINK_TEST 25
#define MSG_LEN 100
int main(int argc, char *argv[]) {
int skfd;
struct sockaddr_nl saddr, daddr;
struct nlmsghdr *nlh = NULL;
struct iovec iov;
struct msghdr msg;
char msg_buf[MSG_LEN];
// 创建socket
skfd = socket(AF_NETLINK, SOCK_RAW, NETLINK_TEST);
if (skfd == -1) {
perror("socket");
exit(EXIT_FAILURE);
}
// 初始化源地址
memset(&saddr, 0, sizeof(saddr));
saddr.nl_family = AF_NETLINK;
saddr.nl_pid = getpid();
saddr.nl_groups = 0;
// 绑定源地址
if (bind(skfd, (struct sockaddr *)&saddr, sizeof(saddr)) != 0) {
perror("bind");
close(skfd);
exit(EXIT_FAILURE);
}
// 初始化目标地址
memset(&daddr, 0, sizeof(daddr));
daddr.nl_family = AF_NETLINK;
daddr.nl_pid = 0; // 目标地址为内核
daddr.nl_groups = 0;
// 构造netlink消息
nlh = (struct nlmsghdr *)msg_buf;
nlh->nlmsg_len = NLMSG_SPACE(MSG_LEN);
nlh->nlmsg_pid = getpid();
nlh->nlmsg_flags = 0;
strcpy(NLMSG_DATA(nlh), "Hello from user space!");
// 设置iov
iov.iov_base = (void *)nlh;
iov.iov_len = nlh->nlmsg_len;
// 设置msghdr
memset(&msg, 0, sizeof(msg));
msg.msg_name = (void *)&daddr;
msg.msg_namelen = sizeof(daddr);
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
// 发送消息
if (sendmsg(skfd, &msg, 0) == -1) {
perror("sendmsg");
close(skfd);
exit(EXIT_FAILURE);
}
printf("User message sent.\n");
// 接收消息
memset(&msg, 0, sizeof(msg));
msg.msg_name = (void *)&saddr;
msg.msg_namelen = sizeof(saddr);
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
if (recvmsg(skfd, &msg, 0) == -1) {
perror("recvmsg");
close(skfd);
exit(EXIT_FAILURE);
}
printf("Received message from kernel: %s\n", (char *)NLMSG_DATA(nlh));
close(skfd);
return 0;
}
```
运行上述服务端和客户端代码即可进行PF_NETLINK通信。注意需要以root权限运行程序。
#include "tst_test.h" #include "tst_safe_macros.h" #include "lapi/sched.h" #define MAX_TRIES 1000 static void child_func(void) { int fd, len, event_found, tries; struct sockaddr_nl sa; char buffer[4096]; struct nlmsghdr *nlh; /* child will listen to a network interface create/delete/up/down events */ memset(&sa, 0, sizeof(sa)); sa.nl_family = AF_NETLINK; sa.nl_groups = RTMGRP_LINK; fd = SAFE_SOCKET(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); SAFE_BIND(fd, (struct sockaddr *) &sa, sizeof(sa)); /* waits for parent to create an interface */ TST_CHECKPOINT_WAKE_AND_WAIT(0); /* * To get rid of "resource temporarily unavailable" errors * when testing with -i option */ tries = 0; event_found = 0; nlh = (struct nlmsghdr *) buffer; while (tries < MAX_TRIES) { len = recv(fd, nlh, sizeof(buffer), MSG_DONTWAIT); if (len > 0) { /* stop receiving only on interface create/delete event */ if (nlh->nlmsg_type == RTM_NEWLINK || nlh->nlmsg_type == RTM_DELLINK) { event_found++; break; } } usleep(10000); tries++; } SAFE_CLOSE(fd); if (event_found) tst_res(TPASS, "interface changes detected"); else tst_res(TFAIL, "failed to detect interface changes"); exit(0); } static void test_netns_netlink(void) { /* unshares the network namespace */ SAFE_UNSHARE(CLONE_NEWNET); if (SAFE_FORK() == 0) child_func(); /* wait until child opens netlink socket */ TST_CHECKPOINT_WAIT(0); /* creates TAP network interface dummy0 */ if (WEXITSTATUS(system("ip tuntap add dev dummy0 mode tap"))) tst_brk(TBROK, "adding interface failed"); /* removes previously created dummy0 device */ if (WEXITSTATUS(system("ip tuntap del mode tap dummy0"))) tst_brk(TBROK, "removing interface failed"); /* allow child to continue */ TST_CHECKPOINT_WAKE(0); tst_reap_children(); } static struct tst_test test = { .test_all = test_netns_netlink, .needs_checkpoints = 1, .needs_root = 1, .forks_child = 1, .needs_kconfigs = (const char *[]) { "CONFIG_NET_NS=y", "CONFIG_TUN", NULL }, };
这段代码是一个测试用例,用于测试网络命名空间(netns)中的网络接口事件。它包含了一些头文件和相关的函数调用。
在`child_func`函数中,首先创建一个网络套接字,并绑定到一个特定的地址和端口上。然后使用`tst_checkpoint_wake_and_wait`函数等待父进程创建一个网络接口。接着使用循环来接收网络事件消息,如果接收到创建或删除网络接口的消息,则跳出循环。最后关闭套接字,并根据是否接收到事件消息来输出相应的测试结果。
在`test_netns_netlink`函数中,首先使用`safe_unshare`函数创建一个新的网络命名空间。然后使用`safe_fork`函数创建一个子进程,在子进程中调用`child_func`函数。接着使用`tst_checkpoint_wait`函数等待子进程打开网络套接字。然后使用系统命令创建一个TAP网络接口(dummy0),并使用系统命令删除之前创建的dummy0设备。最后使用`tst_checkpoint_wake`函数允许子进程继续执行。
最后定义了一个`tst_test`结构体,指定了测试运行的函数为`test_netns_netlink`,需要使用检查点功能(needs_checkpoints),需要以root权限运行,需要在内核配置中启用网络命名空间(CONFIG_NET_NS)和TUN设备驱动(CONFIG_TUN)。
这段代码的目的是测试在网络命名空间中创建和删除网络接口时是否能够正确检测到事件。