test_netlink.c 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246
  1. #define _GNU_SOURCE
  2. #include <sched.h>
  3. #include <stdio.h>
  4. #include <stdlib.h>
  5. #include <string.h>
  6. #include <unistd.h>
  7. #include <arpa/inet.h>
  8. #include <linux/rtnetlink.h>
  9. #include <sys/socket.h>
  10. // 定义一个足够大的缓冲区来接收Netlink消息
  11. #define NL_BUFSIZE 8192
  12. // 结构体,用于将请求消息封装起来
  13. struct nl_req_t {
  14. struct nlmsghdr nlh;
  15. struct ifaddrmsg ifa;
  16. };
  17. void parse_rtattr(struct rtattr *tb[], int max, struct rtattr *rta, int len) {
  18. memset(tb, 0, sizeof(struct rtattr *) * (max + 1));
  19. while (RTA_OK(rta, len)) {
  20. if (rta->rta_type <= max) {
  21. tb[rta->rta_type] = rta;
  22. }
  23. rta = RTA_NEXT(rta, len);
  24. }
  25. }
  26. int run_netlink_test() {
  27. int sock_fd;
  28. struct sockaddr_nl sa_nl;
  29. struct nl_req_t req;
  30. // struct iovec iov;
  31. // struct msghdr msg;
  32. // struct sockaddr_nl src_addr; // 用于接收发送方的地址
  33. char buf[NL_BUFSIZE];
  34. ssize_t len;
  35. struct nlmsghdr *nlh;
  36. // 创建Netlink套接字
  37. sock_fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
  38. if (sock_fd < 0) {
  39. perror("socket creation failed");
  40. return EXIT_FAILURE;
  41. }
  42. // 设置Netlink地址
  43. memset(&sa_nl, 0, sizeof(sa_nl));
  44. sa_nl.nl_family = AF_NETLINK;
  45. sa_nl.nl_pid = getpid(); // 使用进程ID作为地址
  46. // 绑定Netlink套接字
  47. if (bind(sock_fd, (struct sockaddr *)&sa_nl, sizeof(sa_nl)) < 0) {
  48. perror("socket bind failed");
  49. close(sock_fd);
  50. return EXIT_FAILURE;
  51. }
  52. // 构建RTM_GETADDR请求消息
  53. memset(&req, 0, sizeof(req));
  54. req.nlh.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg));
  55. // 这是关键:设置DUMP标志以获取所有地址
  56. req.nlh.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
  57. req.nlh.nlmsg_type = RTM_GETADDR;
  58. req.nlh.nlmsg_seq = 1; // 序列号,用于匹配请求和响应
  59. req.nlh.nlmsg_pid = getpid();
  60. req.ifa.ifa_family =
  61. AF_INET; // 只请求 IPv4 地址 (也可以用 AF_UNSPEC 获取所有)
  62. // 内核的目标地址
  63. struct sockaddr_nl dest_addr;
  64. memset(&dest_addr, 0, sizeof(dest_addr));
  65. dest_addr.nl_family = AF_NETLINK;
  66. dest_addr.nl_pid = 0; // 0 for the kernel
  67. dest_addr.nl_groups = 0; // Unicast
  68. // 发送请求到内核
  69. if (sendto(sock_fd,
  70. &req,
  71. req.nlh.nlmsg_len,
  72. 0,
  73. (struct sockaddr *)&dest_addr,
  74. sizeof(dest_addr)) < 0) {
  75. perror("send failed");
  76. close(sock_fd);
  77. return EXIT_FAILURE;
  78. }
  79. printf("Sent RTM_GETADDR request with DUMP flag.\n\n");
  80. // // 准备 recvmsg 所需的结构体
  81. // // iovec 指向我们的主数据缓冲区
  82. // iov.iov_base = buf;
  83. // iov.iov_len = sizeof(buf);
  84. // // msghdr 将所有部分组合在一起
  85. // msg.msg_name = &src_addr; // 填充发送方地址
  86. // msg.msg_namelen = sizeof(src_addr);
  87. // msg.msg_iov = &iov; // 指向数据缓冲区
  88. // msg.msg_iovlen = 1;
  89. // msg.msg_control = NULL; // 我们暂时不处理控制消息
  90. // msg.msg_controllen = 0;
  91. int received_messages = 0;
  92. // 循环接收响应
  93. while ((len = recv(sock_fd, buf, sizeof(buf), 0)) > 0) {
  94. // ssize_t len = recvmsg(sock_fd, &msg, 0);
  95. // if (len < 0) {
  96. // perror("recvmsg failed");
  97. // break;
  98. // }
  99. // if (len == 0) {
  100. // printf("EOF on netlink socket\n");
  101. // break;
  102. // }
  103. // if (msg.msg_flags & MSG_TRUNC) {
  104. // fprintf(
  105. // stderr,
  106. // "Warning: Message was truncated. Buffer may be too small.\n");
  107. // }
  108. // 使用 NLMSG_OK 遍历缓冲区中可能存在的多条消息
  109. for (nlh = (struct nlmsghdr *)buf; NLMSG_OK(nlh, len);
  110. nlh = NLMSG_NEXT(nlh, len)) {
  111. // 如果是 DUMP 结束的标志,则退出循环
  112. if (nlh->nlmsg_type == NLMSG_DONE) {
  113. printf("--- End of DUMP ---\n");
  114. if (!received_messages) {
  115. printf("(Received an empty list as expected)\n");
  116. }
  117. close(sock_fd);
  118. return EXIT_SUCCESS;
  119. }
  120. // 如果是错误消息
  121. if (nlh->nlmsg_type == NLMSG_ERROR) {
  122. struct nlmsgerr *err = (struct nlmsgerr *)NLMSG_DATA(nlh);
  123. fprintf(stderr,
  124. "Netlink error received: %s\n",
  125. strerror(-err->error));
  126. close(sock_fd);
  127. return EXIT_FAILURE;
  128. }
  129. // 只处理我们期望的 RTM_NEWADDR 消息
  130. if (nlh->nlmsg_type != RTM_NEWADDR) {
  131. printf("Received unexpected message type: %d\n",
  132. nlh->nlmsg_type);
  133. continue;
  134. }
  135. // printf("Received message from PID: %u\n", src_addr.nl_pid);
  136. // 表明我们至少接收到了一条信息
  137. received_messages = 1;
  138. struct ifaddrmsg *ifa = (struct ifaddrmsg *)NLMSG_DATA(nlh);
  139. struct rtattr *rta_tb[IFA_MAX + 1];
  140. // 解析消息中的路由属性
  141. int rta_len = nlh->nlmsg_len - NLMSG_LENGTH(sizeof(*ifa));
  142. parse_rtattr(rta_tb, IFA_MAX, IFA_RTA(ifa), rta_len);
  143. printf("Interface Index: %d, PrefixLen: %d, Scope: %d\n",
  144. ifa->ifa_index,
  145. ifa->ifa_prefixlen,
  146. ifa->ifa_scope);
  147. char ip_addr_str[INET6_ADDRSTRLEN];
  148. // 打印 IFA_LABEL (对应你的 AddrAttr::Label)
  149. if (rta_tb[IFA_LABEL]) {
  150. printf("\tLabel: %s\n", (char *)RTA_DATA(rta_tb[IFA_LABEL]));
  151. }
  152. // 打印 IFA_ADDRESS (对应你的 AddrAttr::Address)
  153. if (rta_tb[IFA_ADDRESS]) {
  154. inet_ntop(ifa->ifa_family,
  155. RTA_DATA(rta_tb[IFA_ADDRESS]),
  156. ip_addr_str,
  157. sizeof(ip_addr_str));
  158. printf("\tAddress: %s\n", ip_addr_str);
  159. }
  160. // 打印 IFA_LOCAL (对应你的 AddrAttr::Local)
  161. if (rta_tb[IFA_LOCAL]) {
  162. inet_ntop(ifa->ifa_family,
  163. RTA_DATA(rta_tb[IFA_LOCAL]),
  164. ip_addr_str,
  165. sizeof(ip_addr_str));
  166. printf("\tLocal: %s\n", ip_addr_str);
  167. }
  168. printf("----------------------------------------\n");
  169. }
  170. }
  171. if (len < 0) {
  172. perror("recv failed");
  173. }
  174. close(sock_fd);
  175. return EXIT_SUCCESS;
  176. }
  177. int main(int argc, char *argv[]) {
  178. printf("=========== STAGE 1: Testing in Default Network Namespace "
  179. "===========\n");
  180. if (run_netlink_test() != 0) {
  181. fprintf(stderr, "Test failed in the default namespace.\n");
  182. return EXIT_FAILURE;
  183. }
  184. printf("\n\n=========== STAGE 2: Creating and Testing in a New Network "
  185. "Namespace ===========\n");
  186. // ** 关键步骤:创建新的网络命名空间 **
  187. // 这个调用会将当前进程移入一个新的、隔离的网络栈中
  188. if (unshare(CLONE_NEWNET) == -1) {
  189. perror("unshare(CLONE_NEWNET) failed");
  190. fprintf(stderr,
  191. "This test requires root privileges (e.g., 'sudo "
  192. "./your_program').\n");
  193. return EXIT_FAILURE;
  194. }
  195. printf("Successfully created and entered a new network namespace.\n");
  196. // 在新的命名空间中再次运行同样的测试
  197. if (run_netlink_test() != 0) {
  198. fprintf(stderr, "Test failed in the new namespace.\n");
  199. return EXIT_FAILURE;
  200. }
  201. printf("\nAll tests completed successfully.\n");
  202. return EXIT_SUCCESS;
  203. }