#define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include /* ===> from passt's Makefile and code... */ #define RLIMIT_STACK_VAL 8192 #define NS_FN_STACK_SIZE (RLIMIT_STACK_VAL * 1024 / 8) int do_clone(int (*fn)(void *), char *stack_area, size_t stack_size, int flags, void *arg) { #ifdef __ia64__ return __clone2(fn, stack_area + stack_size / 2, stack_size / 2, flags, arg); #else return clone(fn, stack_area + stack_size / 2, flags, arg); #endif } static int nl_sock; static int nl_sock_init_do(void *arg) { struct sockaddr_nl addr = { .nl_family = AF_NETLINK, }; int *s = &nl_sock; #ifdef NETLINK_GET_STRICT_CHK int y = 1; #endif *s = socket(AF_NETLINK, SOCK_RAW | SOCK_CLOEXEC, NETLINK_ROUTE); if (*s < 0 || bind(*s, (struct sockaddr *)&addr, sizeof(addr))) { *s = -1; return 0; } return 0; } /** * nl_send() - Prepare and send netlink request * @s: Netlink socket * @req: Request (will fill netlink header) * @type: Request type * @flags: Extra request flags (NLM_F_REQUEST and NLM_F_ACK assumed) * @len: Request length * * Return: sequence number of request on success, terminates on error */ static uint32_t nl_send(int s, void *req, uint16_t type, uint16_t flags, ssize_t len) { struct nlmsghdr *nh; ssize_t n; nh = (struct nlmsghdr *)req; nh->nlmsg_type = type; nh->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK | flags; nh->nlmsg_len = len; nh->nlmsg_seq = 1; nh->nlmsg_pid = 0; n = send(s, req, len, 0); return nh->nlmsg_seq; } int nl_link_up(int s, unsigned int ifi, int mtu) { struct req_t { struct nlmsghdr nlh; struct ifinfomsg ifm; struct rtattr rta; unsigned int mtu; } req = { .ifm.ifi_family = AF_UNSPEC, .ifm.ifi_index = ifi, .ifm.ifi_flags = IFF_UP, .ifm.ifi_change = IFF_UP, .rta.rta_type = IFLA_MTU, .rta.rta_len = RTA_LENGTH(sizeof(unsigned int)), .mtu = mtu, }; ssize_t len = sizeof(req); if (!mtu) /* Shorten request to drop MTU attribute */ len = offsetof(struct req_t, rta); return nl_send(s, &req, RTM_NEWLINK, 0, len); /* was nl_do() */ } /* <=== ...until here */ static int tcp_probe_sockets(void *arg) { int *s = (int *)arg; nl_sock_init_do(NULL); nl_link_up(nl_sock, 1 /* lo */, 0); s[0] = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); s[1] = socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK, IPPROTO_TCP); return 0; } int main(int argc, char **argv) { char ns_fn_stack[NS_FN_STACK_SIZE], b; struct iovec iov[2] = { { NULL, 1 }, { &b, 1 }, }; struct sockaddr a = { AF_INET, htons(6666), }; struct msghdr msg = { iov, 2 }; int s[2], s_nl, s_recv; ssize_t len; char buf[10]; do_clone(tcp_probe_sockets, ns_fn_stack, sizeof(ns_fn_stack), CLONE_NEWNET | CLONE_NEWUSER | CLONE_VM | CLONE_VFORK | CLONE_FILES | SIGCHLD, (void *)s); bind(s[0], &a, sizeof(a)); getsockname(s[0], &a, &((int){ sizeof(a) })); listen(s[0], 0); connect(s[1], &a, sizeof(a)); s_recv = accept(s[0], NULL, NULL); send(s[1], (char *)("ab"), 2, 0); len = recvmsg(s_recv, &msg, MSG_PEEK); printf("MSG_PEEK with offset %ssupported\n", len == 1 ? "" : "not "); close(s_recv); close(s[1]); close(s[0]); return 0; }