|  | /* SPDX-License-Identifier: GPL-2.0 */ | 
|  |  | 
|  | #ifndef __LWT_HELPERS_H | 
|  | #define __LWT_HELPERS_H | 
|  |  | 
|  | #include <time.h> | 
|  | #include <net/if.h> | 
|  | #include <linux/if_tun.h> | 
|  | #include <linux/icmp.h> | 
|  |  | 
|  | #include "test_progs.h" | 
|  |  | 
|  | #define log_err(MSG, ...) \ | 
|  | fprintf(stderr, "(%s:%d: errno: %s) " MSG "\n", \ | 
|  | __FILE__, __LINE__, strerror(errno), ##__VA_ARGS__) | 
|  |  | 
|  | #define RUN_TEST(name)                                                        \ | 
|  | ({                                                                    \ | 
|  | if (test__start_subtest(#name))                               \ | 
|  | if (ASSERT_OK(netns_create(), "netns_create")) {      \ | 
|  | struct nstoken *token = open_netns(NETNS);    \ | 
|  | if (ASSERT_OK_PTR(token, "setns")) {          \ | 
|  | test_ ## name();                      \ | 
|  | close_netns(token);                   \ | 
|  | }                                             \ | 
|  | netns_delete();                               \ | 
|  | }                                                     \ | 
|  | }) | 
|  |  | 
|  | static inline int netns_create(void) | 
|  | { | 
|  | return system("ip netns add " NETNS); | 
|  | } | 
|  |  | 
|  | static inline int netns_delete(void) | 
|  | { | 
|  | return system("ip netns del " NETNS ">/dev/null 2>&1"); | 
|  | } | 
|  |  | 
|  | static int open_tuntap(const char *dev_name, bool need_mac) | 
|  | { | 
|  | int err = 0; | 
|  | struct ifreq ifr; | 
|  | int fd = open("/dev/net/tun", O_RDWR); | 
|  |  | 
|  | if (!ASSERT_GT(fd, 0, "open(/dev/net/tun)")) | 
|  | return -1; | 
|  |  | 
|  | ifr.ifr_flags = IFF_NO_PI | (need_mac ? IFF_TAP : IFF_TUN); | 
|  | memcpy(ifr.ifr_name, dev_name, IFNAMSIZ); | 
|  |  | 
|  | err = ioctl(fd, TUNSETIFF, &ifr); | 
|  | if (!ASSERT_OK(err, "ioctl(TUNSETIFF)")) { | 
|  | close(fd); | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | err = fcntl(fd, F_SETFL, O_NONBLOCK); | 
|  | if (!ASSERT_OK(err, "fcntl(O_NONBLOCK)")) { | 
|  | close(fd); | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | return fd; | 
|  | } | 
|  |  | 
|  | #define ICMP_PAYLOAD_SIZE     100 | 
|  |  | 
|  | /* Match an ICMP packet with payload len ICMP_PAYLOAD_SIZE */ | 
|  | static int __expect_icmp_ipv4(char *buf, ssize_t len) | 
|  | { | 
|  | struct iphdr *ip = (struct iphdr *)buf; | 
|  | struct icmphdr *icmp = (struct icmphdr *)(ip + 1); | 
|  | ssize_t min_header_len = sizeof(*ip) + sizeof(*icmp); | 
|  |  | 
|  | if (len < min_header_len) | 
|  | return -1; | 
|  |  | 
|  | if (ip->protocol != IPPROTO_ICMP) | 
|  | return -1; | 
|  |  | 
|  | if (icmp->type != ICMP_ECHO) | 
|  | return -1; | 
|  |  | 
|  | return len == ICMP_PAYLOAD_SIZE + min_header_len; | 
|  | } | 
|  |  | 
|  | typedef int (*filter_t) (char *, ssize_t); | 
|  |  | 
|  | /* wait_for_packet - wait for a packet that matches the filter | 
|  | * | 
|  | * @fd: tun fd/packet socket to read packet | 
|  | * @filter: filter function, returning 1 if matches | 
|  | * @timeout: timeout to wait for the packet | 
|  | * | 
|  | * Returns 1 if a matching packet is read, 0 if timeout expired, -1 on error. | 
|  | */ | 
|  | static int wait_for_packet(int fd, filter_t filter, struct timeval *timeout) | 
|  | { | 
|  | char buf[4096]; | 
|  | int max_retry = 5; /* in case we read some spurious packets */ | 
|  | fd_set fds; | 
|  |  | 
|  | FD_ZERO(&fds); | 
|  | while (max_retry--) { | 
|  | /* Linux modifies timeout arg... So make a copy */ | 
|  | struct timeval copied_timeout = *timeout; | 
|  | ssize_t ret = -1; | 
|  |  | 
|  | FD_SET(fd, &fds); | 
|  |  | 
|  | ret = select(1 + fd, &fds, NULL, NULL, &copied_timeout); | 
|  | if (ret <= 0) { | 
|  | if (errno == EINTR) | 
|  | continue; | 
|  | else if (errno == EAGAIN || ret == 0) | 
|  | return 0; | 
|  |  | 
|  | log_err("select failed"); | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | ret = read(fd, buf, sizeof(buf)); | 
|  |  | 
|  | if (ret <= 0) { | 
|  | log_err("read(dev): %ld", ret); | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | if (filter && filter(buf, ret) > 0) | 
|  | return 1; | 
|  | } | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | #endif /* __LWT_HELPERS_H */ |