| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246 |
- #define _GNU_SOURCE
- #include <sched.h>
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
- #include <unistd.h>
- #include <arpa/inet.h>
- #include <linux/rtnetlink.h>
- #include <sys/socket.h>
- // 定义一个足够大的缓冲区来接收Netlink消息
- #define NL_BUFSIZE 8192
- // 结构体,用于将请求消息封装起来
- struct nl_req_t {
- struct nlmsghdr nlh;
- struct ifaddrmsg ifa;
- };
- void parse_rtattr(struct rtattr *tb[], int max, struct rtattr *rta, int len) {
- memset(tb, 0, sizeof(struct rtattr *) * (max + 1));
- while (RTA_OK(rta, len)) {
- if (rta->rta_type <= max) {
- tb[rta->rta_type] = rta;
- }
- rta = RTA_NEXT(rta, len);
- }
- }
- int run_netlink_test() {
- int sock_fd;
- struct sockaddr_nl sa_nl;
- struct nl_req_t req;
- // struct iovec iov;
- // struct msghdr msg;
- // struct sockaddr_nl src_addr; // 用于接收发送方的地址
- char buf[NL_BUFSIZE];
- ssize_t len;
- struct nlmsghdr *nlh;
- // 创建Netlink套接字
- sock_fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
- if (sock_fd < 0) {
- perror("socket creation failed");
- return EXIT_FAILURE;
- }
- // 设置Netlink地址
- memset(&sa_nl, 0, sizeof(sa_nl));
- sa_nl.nl_family = AF_NETLINK;
- sa_nl.nl_pid = getpid(); // 使用进程ID作为地址
- // 绑定Netlink套接字
- if (bind(sock_fd, (struct sockaddr *)&sa_nl, sizeof(sa_nl)) < 0) {
- perror("socket bind failed");
- close(sock_fd);
- return EXIT_FAILURE;
- }
- // 构建RTM_GETADDR请求消息
- memset(&req, 0, sizeof(req));
- req.nlh.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg));
- // 这是关键:设置DUMP标志以获取所有地址
- req.nlh.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
- req.nlh.nlmsg_type = RTM_GETADDR;
- req.nlh.nlmsg_seq = 1; // 序列号,用于匹配请求和响应
- req.nlh.nlmsg_pid = getpid();
- req.ifa.ifa_family =
- AF_INET; // 只请求 IPv4 地址 (也可以用 AF_UNSPEC 获取所有)
- // 内核的目标地址
- struct sockaddr_nl dest_addr;
- memset(&dest_addr, 0, sizeof(dest_addr));
- dest_addr.nl_family = AF_NETLINK;
- dest_addr.nl_pid = 0; // 0 for the kernel
- dest_addr.nl_groups = 0; // Unicast
- // 发送请求到内核
- if (sendto(sock_fd,
- &req,
- req.nlh.nlmsg_len,
- 0,
- (struct sockaddr *)&dest_addr,
- sizeof(dest_addr)) < 0) {
- perror("send failed");
- close(sock_fd);
- return EXIT_FAILURE;
- }
- printf("Sent RTM_GETADDR request with DUMP flag.\n\n");
- // // 准备 recvmsg 所需的结构体
- // // iovec 指向我们的主数据缓冲区
- // iov.iov_base = buf;
- // iov.iov_len = sizeof(buf);
- // // msghdr 将所有部分组合在一起
- // msg.msg_name = &src_addr; // 填充发送方地址
- // msg.msg_namelen = sizeof(src_addr);
- // msg.msg_iov = &iov; // 指向数据缓冲区
- // msg.msg_iovlen = 1;
- // msg.msg_control = NULL; // 我们暂时不处理控制消息
- // msg.msg_controllen = 0;
- int received_messages = 0;
- // 循环接收响应
- while ((len = recv(sock_fd, buf, sizeof(buf), 0)) > 0) {
- // ssize_t len = recvmsg(sock_fd, &msg, 0);
- // if (len < 0) {
- // perror("recvmsg failed");
- // break;
- // }
- // if (len == 0) {
- // printf("EOF on netlink socket\n");
- // break;
- // }
- // if (msg.msg_flags & MSG_TRUNC) {
- // fprintf(
- // stderr,
- // "Warning: Message was truncated. Buffer may be too small.\n");
- // }
- // 使用 NLMSG_OK 遍历缓冲区中可能存在的多条消息
- for (nlh = (struct nlmsghdr *)buf; NLMSG_OK(nlh, len);
- nlh = NLMSG_NEXT(nlh, len)) {
- // 如果是 DUMP 结束的标志,则退出循环
- if (nlh->nlmsg_type == NLMSG_DONE) {
- printf("--- End of DUMP ---\n");
- if (!received_messages) {
- printf("(Received an empty list as expected)\n");
- }
- close(sock_fd);
- return EXIT_SUCCESS;
- }
- // 如果是错误消息
- if (nlh->nlmsg_type == NLMSG_ERROR) {
- struct nlmsgerr *err = (struct nlmsgerr *)NLMSG_DATA(nlh);
- fprintf(stderr,
- "Netlink error received: %s\n",
- strerror(-err->error));
- close(sock_fd);
- return EXIT_FAILURE;
- }
- // 只处理我们期望的 RTM_NEWADDR 消息
- if (nlh->nlmsg_type != RTM_NEWADDR) {
- printf("Received unexpected message type: %d\n",
- nlh->nlmsg_type);
- continue;
- }
- // printf("Received message from PID: %u\n", src_addr.nl_pid);
- // 表明我们至少接收到了一条信息
- received_messages = 1;
- struct ifaddrmsg *ifa = (struct ifaddrmsg *)NLMSG_DATA(nlh);
- struct rtattr *rta_tb[IFA_MAX + 1];
- // 解析消息中的路由属性
- int rta_len = nlh->nlmsg_len - NLMSG_LENGTH(sizeof(*ifa));
- parse_rtattr(rta_tb, IFA_MAX, IFA_RTA(ifa), rta_len);
- printf("Interface Index: %d, PrefixLen: %d, Scope: %d\n",
- ifa->ifa_index,
- ifa->ifa_prefixlen,
- ifa->ifa_scope);
- char ip_addr_str[INET6_ADDRSTRLEN];
- // 打印 IFA_LABEL (对应你的 AddrAttr::Label)
- if (rta_tb[IFA_LABEL]) {
- printf("\tLabel: %s\n", (char *)RTA_DATA(rta_tb[IFA_LABEL]));
- }
- // 打印 IFA_ADDRESS (对应你的 AddrAttr::Address)
- if (rta_tb[IFA_ADDRESS]) {
- inet_ntop(ifa->ifa_family,
- RTA_DATA(rta_tb[IFA_ADDRESS]),
- ip_addr_str,
- sizeof(ip_addr_str));
- printf("\tAddress: %s\n", ip_addr_str);
- }
- // 打印 IFA_LOCAL (对应你的 AddrAttr::Local)
- if (rta_tb[IFA_LOCAL]) {
- inet_ntop(ifa->ifa_family,
- RTA_DATA(rta_tb[IFA_LOCAL]),
- ip_addr_str,
- sizeof(ip_addr_str));
- printf("\tLocal: %s\n", ip_addr_str);
- }
- printf("----------------------------------------\n");
- }
- }
- if (len < 0) {
- perror("recv failed");
- }
- close(sock_fd);
- return EXIT_SUCCESS;
- }
- int main(int argc, char *argv[]) {
- printf("=========== STAGE 1: Testing in Default Network Namespace "
- "===========\n");
- if (run_netlink_test() != 0) {
- fprintf(stderr, "Test failed in the default namespace.\n");
- return EXIT_FAILURE;
- }
- printf("\n\n=========== STAGE 2: Creating and Testing in a New Network "
- "Namespace ===========\n");
- // ** 关键步骤:创建新的网络命名空间 **
- // 这个调用会将当前进程移入一个新的、隔离的网络栈中
- if (unshare(CLONE_NEWNET) == -1) {
- perror("unshare(CLONE_NEWNET) failed");
- fprintf(stderr,
- "This test requires root privileges (e.g., 'sudo "
- "./your_program').\n");
- return EXIT_FAILURE;
- }
- printf("Successfully created and entered a new network namespace.\n");
- // 在新的命名空间中再次运行同样的测试
- if (run_netlink_test() != 0) {
- fprintf(stderr, "Test failed in the new namespace.\n");
- return EXIT_FAILURE;
- }
- printf("\nAll tests completed successfully.\n");
- return EXIT_SUCCESS;
- }
|