// SPDX-License-Identifier: MPL-2.0 #include #include #include #include "test.h" #define ETHER_NAME "eth0" #define LOOPBACK_NAME "lo" #define SUCC(expr) ((expr), 0) int find_lo_and_eth0_by_libc(struct if_nameindex *if_ni) { int found_links = 0; for (struct if_nameindex *i = if_ni; !(i->if_index == 0 && i->if_name == NULL); i++) { if (strcmp(i->if_name, LOOPBACK_NAME) == 0) { found_links++; } if (strcmp(i->if_name, ETHER_NAME) == 0) { found_links++; } } return found_links; } FN_TEST(if_nameindex) { struct if_nameindex *if_ni; CHECK_WITH(SUCC(if_ni = if_nameindex()), if_ni != NULL); TEST_RES(find_lo_and_eth0_by_libc(if_ni), _ret == 2); if_freenameindex(if_ni); } END_TEST() void find_lo_and_eth0_by_libnl(struct nl_object *obj, void *arg) { int *found_links = (int *)arg; struct rtnl_link *link = (struct rtnl_link *)obj; if (strcmp(rtnl_link_get_name(link), LOOPBACK_NAME) == 0) { *found_links += 1; } if (strcmp(rtnl_link_get_name(link), ETHER_NAME) == 0) { *found_links += 1; } } FN_TEST(get_link_by_libnl) { struct nl_sock *sock; struct nl_cache *link_cache; // 1. Create netlink socket and connect sock = nl_socket_alloc(); TEST_RES(nl_connect(sock, NETLINK_ROUTE), _ret >= 0); // 2. Allocate and retrieve link cache TEST_RES(rtnl_link_alloc_cache(sock, AF_UNSPEC, &link_cache), _ret >= 0); // 3. Iterate over all links to find lo and eth0 int found_links = 0; TEST_RES(SUCC(nl_cache_foreach(link_cache, find_lo_and_eth0_by_libnl, &found_links)), found_links == 2); // 4. Cleanup nl_cache_free(link_cache); nl_close(sock); nl_socket_free(sock); } END_TEST() void find_loopback_address(struct nl_object *obj, void *arg) { int *found_loopback = (int *)arg; struct rtnl_addr *addr = (struct rtnl_addr *)obj; struct nl_addr *local; char buf[INET_ADDRSTRLEN]; int family = rtnl_addr_get_family(addr); if (family != AF_INET) { return; } local = rtnl_addr_get_local(addr); if (local) { nl_addr2str(local, buf, sizeof(buf)); if (strcmp(buf, "127.0.0.1/8") == 0) { *found_loopback = 1; } } } FN_TEST(get_loopback_address) { struct nl_sock *sock; struct nl_cache *addr_cache; // 1. Create netlink socket and connect sock = nl_socket_alloc(); TEST_RES(nl_connect(sock, NETLINK_ROUTE), _ret >= 0); // 2. Allocate and retrieve address cache TEST_RES(rtnl_addr_alloc_cache(sock, &addr_cache), _ret >= 0); // 3. Iterate over all addresses to find loopback address int found_loopback = 0; TEST_RES(SUCC(nl_cache_foreach(addr_cache, find_loopback_address, &found_loopback)), found_loopback == 1); // 4. Cleanup nl_cache_free(addr_cache); nl_close(sock); nl_socket_free(sock); } END_TEST() int find_new_addr_until_done(char *buffer, size_t len, int *found_new_addr) { struct nlmsghdr *nlh = (struct nlmsghdr *)buffer; for (; NLMSG_OK(nlh, len); nlh = NLMSG_NEXT(nlh, len)) { if (nlh->nlmsg_type == NLMSG_DONE) { return *found_new_addr ? 1 : -1; } if (nlh->nlmsg_type == RTM_NEWADDR) { *found_new_addr += 1; } else { return -1; } } return 0; } FN_TEST(get_addr_error) { #define BUFFER_SIZE 8192 struct nl_req { struct nlmsghdr hdr; struct ifaddrmsg ifa; }; int sock_fd; struct sockaddr_nl sa; char buffer[BUFFER_SIZE]; sock_fd = TEST_SUCC(socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE)); memset(&sa, 0, sizeof(sa)); sa.nl_family = AF_NETLINK; TEST_SUCC(bind(sock_fd, (struct sockaddr *)&sa, sizeof(sa))); // 1. Without NLM_F_DUMP flag struct nl_req req; memset(&req, 0, sizeof(req)); req.hdr.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg)); req.hdr.nlmsg_type = RTM_GETADDR; req.hdr.nlmsg_flags = NLM_F_REQUEST; req.hdr.nlmsg_seq = 1; req.ifa.ifa_family = AF_UNSPEC; struct iovec iov = { &req, req.hdr.nlmsg_len }; struct msghdr msg = { &sa, sizeof(sa), &iov, 1, NULL, 0, 0 }; TEST_SUCC(sendmsg(sock_fd, &msg, 0)); TEST_RES(recv(sock_fd, buffer, BUFFER_SIZE, 0), ((struct nlmsghdr *)buffer)->nlmsg_type == NLMSG_ERROR && ((struct nlmsgerr *)NLMSG_DATA(buffer))->error == -EOPNOTSUPP); // 2. Invalid required index req.hdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP | NLM_F_ACK; req.ifa.ifa_index = 9999; TEST_SUCC(sendmsg(sock_fd, &msg, 0)); int found_new_addr = 0; while (1) { size_t recv_len = TEST_SUCC(recv(sock_fd, buffer, BUFFER_SIZE, 0)); int found_done = TEST_SUCC(find_new_addr_until_done( buffer, recv_len, &found_new_addr)); if (found_done != 0) { break; } } // 3. Invalid required family req.ifa.ifa_family = 255; req.ifa.ifa_index = 0; TEST_SUCC(sendmsg(sock_fd, &msg, 0)); found_new_addr = 0; while (1) { size_t recv_len = TEST_SUCC(recv(sock_fd, buffer, BUFFER_SIZE, 0)); int found_done = TEST_SUCC(find_new_addr_until_done( buffer, recv_len, &found_new_addr)); if (found_done != 0) { break; } } TEST_SUCC(close(sock_fd)); } END_TEST()