From 6c7d67acfe953849622a64771fa5008730517a75 Mon Sep 17 00:00:00 2001 From: "Jason A. Donenfeld" Date: Tue, 19 Sep 2017 23:17:56 +0200 Subject: [PATCH] contrib: add sticky sockets example code Signed-off-by: Jason A. Donenfeld --- contrib/sticky-sockets/README | 5 + contrib/sticky-sockets/a.out | Bin 0 -> 13232 bytes contrib/sticky-sockets/sticky-sockets.c | 337 ++++++++++++++++++++++++ 3 files changed, 342 insertions(+) create mode 100644 contrib/sticky-sockets/README create mode 100755 contrib/sticky-sockets/a.out create mode 100644 contrib/sticky-sockets/sticky-sockets.c diff --git a/contrib/sticky-sockets/README b/contrib/sticky-sockets/README new file mode 100644 index 0000000..cc14570 --- /dev/null +++ b/contrib/sticky-sockets/README @@ -0,0 +1,5 @@ +Sticky Sockets +============== + +This is a small userspace mini-library that implements as close to +possible how the kernel does its sticky src address sending. diff --git a/contrib/sticky-sockets/a.out b/contrib/sticky-sockets/a.out new file mode 100755 index 0000000000000000000000000000000000000000..844d08e5e129616393059feaa4913d2e407512c9 GIT binary patch literal 13232 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 N!sSybD5z8aKLL;15>NmD literal 0 HcmV?d00001 diff --git a/contrib/sticky-sockets/sticky-sockets.c b/contrib/sticky-sockets/sticky-sockets.c new file mode 100644 index 0000000..1a910b2 --- /dev/null +++ b/contrib/sticky-sockets/sticky-sockets.c @@ -0,0 +1,337 @@ +/* Copyright 2017 Jason A. Donenfeld . All Rights Reserved. + * + * This implements userspace semantics of "sticky sockets", modeled after + * WireGuard's kernelspace implementation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct magic_endpoint { + union { + struct sockaddr addr; + struct sockaddr_in addr4; + struct sockaddr_in6 addr6; + }; + union { + struct { + struct in_addr src4; + int src_if4; /* Essentially the same as addr6->scope_id */ + }; + struct in6_addr src6; + }; +}; + +ssize_t magic_send4(int sock, struct magic_endpoint *endpoint, void *buffer, size_t len) +{ + ssize_t ret; + struct iovec iovec = { + .iov_base = buffer, + .iov_len = len + }; + struct { + struct cmsghdr cmsghdr; + struct in_pktinfo pktinfo; + } cmsg = { + .cmsghdr.cmsg_level = IPPROTO_IP, + .cmsghdr.cmsg_type = IP_PKTINFO, + .cmsghdr.cmsg_len = CMSG_LEN(sizeof(cmsg.pktinfo)), + .pktinfo.ipi_spec_dst = endpoint->src4, + .pktinfo.ipi_ifindex = endpoint->src_if4 + }; + struct msghdr msghdr = { + .msg_iov = &iovec, + .msg_iovlen = 1, + .msg_name = &endpoint->addr4, + .msg_namelen = sizeof(endpoint->addr4), + .msg_control = &cmsg, + .msg_controllen = sizeof(cmsg) + }; + ret = sendmsg(sock, &msghdr, 0); + if (ret < 0 && errno == EINVAL) { + memset(&cmsg.pktinfo, 0, sizeof(cmsg.pktinfo)); + endpoint->src4.s_addr = endpoint->src_if4 = 0; + return sendmsg(sock, &msghdr, 0); + } + return ret; +} + +ssize_t magic_send6(int sock, struct magic_endpoint *endpoint, void *buffer, size_t len) +{ + ssize_t ret; + struct iovec iovec = { + .iov_base = buffer, + .iov_len = len + }; + struct { + struct cmsghdr cmsghdr; + struct in6_pktinfo pktinfo; + } cmsg = { + .cmsghdr.cmsg_level = IPPROTO_IPV6, + .cmsghdr.cmsg_type = IPV6_PKTINFO, + .cmsghdr.cmsg_len = CMSG_LEN(sizeof(cmsg.pktinfo)), + .pktinfo.ipi6_addr = endpoint->src6, + .pktinfo.ipi6_ifindex = memcmp(&in6addr_any, &endpoint->src6, sizeof(endpoint->src6)) ? endpoint->addr6.sin6_scope_id : 0 + }; + struct msghdr msghdr = { + .msg_iov = &iovec, + .msg_iovlen = 1, + .msg_name = &endpoint->addr6, + .msg_namelen = sizeof(endpoint->addr6), + .msg_control = &cmsg, + .msg_controllen = sizeof(cmsg) + }; + + ret = sendmsg(sock, &msghdr, 0); + if (ret < 0 && errno == EINVAL) { + memset(&cmsg.pktinfo, 0, sizeof(cmsg.pktinfo)); + memset(&endpoint->src6, 0, sizeof(endpoint->src6)); + return sendmsg(sock, &msghdr, 0); + } + return ret; +} + +ssize_t magic_receive4(int sock, struct magic_endpoint *endpoint, void *buffer, size_t len) +{ + ssize_t ret; + struct iovec iovec = { + .iov_base = buffer, + .iov_len = len + }; + struct { + struct cmsghdr cmsghdr; + struct in_pktinfo pktinfo; + } cmsg; + struct msghdr msghdr = { + .msg_iov = &iovec, + .msg_iovlen = 1, + .msg_name = &endpoint->addr4, + .msg_namelen = sizeof(endpoint->addr4), + .msg_control = &cmsg, + .msg_controllen = sizeof(cmsg) + }; + + ret = recvmsg(sock, &msghdr, 0); + if (ret < 0) + return ret; + if (cmsg.cmsghdr.cmsg_level == IPPROTO_IP && cmsg.cmsghdr.cmsg_type == IP_PKTINFO && cmsg.cmsghdr.cmsg_len >= CMSG_LEN(sizeof(cmsg.pktinfo))) { + endpoint->src4 = cmsg.pktinfo.ipi_spec_dst; + endpoint->src_if4 = cmsg.pktinfo.ipi_ifindex; + } + return ret; +} + +ssize_t magic_receive6(int sock, struct magic_endpoint *endpoint, void *buffer, size_t len) +{ + ssize_t ret; + struct iovec iovec = { + .iov_base = buffer, + .iov_len = len + }; + struct { + struct cmsghdr cmsghdr; + struct in6_pktinfo pktinfo; + } cmsg; + struct msghdr msghdr = { + .msg_iov = &iovec, + .msg_iovlen = 1, + .msg_name = &endpoint->addr6, + .msg_namelen = sizeof(endpoint->addr6), + .msg_control = &cmsg, + .msg_controllen = sizeof(cmsg) + }; + + ret = recvmsg(sock, &msghdr, 0); + if (ret < 0) + return ret; + if (cmsg.cmsghdr.cmsg_level == IPPROTO_IPV6 && cmsg.cmsghdr.cmsg_type == IPV6_PKTINFO && cmsg.cmsghdr.cmsg_len >= CMSG_LEN(sizeof(cmsg.pktinfo))) { + endpoint->src6 = cmsg.pktinfo.ipi6_addr; + endpoint->addr6.sin6_scope_id = cmsg.pktinfo.ipi6_ifindex; + } + return ret; +} + +void magic_endpoint_clearsrc(struct magic_endpoint *endpoint) +{ + if (endpoint->addr.sa_family == AF_INET) + endpoint->src4.s_addr = endpoint->src_if4 = 0; + else if (endpoint->addr.sa_family == AF_INET6) + memset(&endpoint->src6, 0, sizeof(endpoint->src6)); + else + memset(endpoint, 0, sizeof(*endpoint)); +} + +void magic_endpoint_set(struct magic_endpoint *endpoint, const struct sockaddr *addr) +{ + if (addr->sa_family == AF_INET) + endpoint->addr4 = *(struct sockaddr_in *)addr; + else if (addr->sa_family == AF_INET6) + endpoint->addr6 = *(struct sockaddr_in6 *)addr; + magic_endpoint_clearsrc(endpoint); +} + +int magic_create_sock4(uint16_t listen_port) +{ + static const int on = 1; + struct sockaddr_in listen_addr = { + .sin_family = AF_INET, + .sin_port = htons(listen_port), + .sin_addr = INADDR_ANY + }; + int fd, ret; + + fd = socket(AF_INET, SOCK_DGRAM, 0); + if (fd < 0) + return fd; + + ret = setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); + if (ret < 0) + goto err; + + ret = setsockopt(fd, IPPROTO_IP, IP_PKTINFO, &on, sizeof(on)); + if (ret < 0) + goto err; + + ret = bind(fd, (struct sockaddr *)&listen_addr, sizeof(listen_addr)); + if (ret < 0) + goto err; + + return fd; + +err: + close(fd); + return ret; +} + +int magic_create_sock6(uint16_t listen_port) +{ + static const int on = 1; + struct sockaddr_in6 listen_addr = { + .sin6_family = AF_INET6, + .sin6_port = htons(listen_port), + .sin6_addr = IN6ADDR_ANY_INIT + }; + int fd, ret; + + fd = socket(AF_INET6, SOCK_DGRAM, 0); + if (fd < 0) + return fd; + + ret = setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); + if (ret < 0) + goto err; + + ret = setsockopt(fd, IPPROTO_IPV6, IPV6_RECVPKTINFO, &on, sizeof(on)); + if (ret < 0) + goto err; + + ret = setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof(on)); + if (ret < 0) + goto err; + + ret = bind(fd, (struct sockaddr *)&listen_addr, sizeof(listen_addr)); + if (ret < 0) + goto err; + + return fd; + +err: + close(fd); + return ret; +} + +int main(int argc, char *argv[]) +{ + struct magic_endpoint endpoint = { 0 }; + int sock; + ssize_t ret; + uint8_t buffer[1024] = { 0 }; + char srcaddr[40], dstaddr[40]; + + if (argc == 2 && !strcmp(argv[1], "-4")) + goto v4; + if (argc == 2 && !strcmp(argv[1], "-6")) + goto v6; + return 1; + +v6: + sock = magic_create_sock6(51820); + if (sock < 0) { + perror("magic_create_sock6"); + return 1; + } + + ret = magic_receive6(sock, &endpoint, buffer, sizeof(buffer)); + if (ret < 0) { + perror("magic_receive6"); + return 1; + } + + if (!inet_ntop(AF_INET6, &endpoint.src6, srcaddr, sizeof(srcaddr))) { + perror("inet_ntop"); + return 1; + } + + if (!inet_ntop(AF_INET6, &endpoint.addr6.sin6_addr, dstaddr, sizeof(dstaddr))) { + perror("inet_ntop"); + return 1; + } + + printf("if:%d src:%s dst:%s\n", endpoint.addr6.sin6_scope_id, srcaddr, dstaddr); + printf("Received a packet. Sleeping for 10 seconds before replying, so you have time to mess with your networking setup.\n"); + sleep(10); + + ret = magic_send6(sock, &endpoint, buffer, sizeof(buffer)); + if (ret < 0) { + perror("magic_send6"); + return 1; + } + + close(sock); + return 0; + +v4: + sock = magic_create_sock4(51820); + if (sock < 0) { + perror("magic_create_sock4"); + return 1; + } + + ret = magic_receive4(sock, &endpoint, buffer, sizeof(buffer)); + if (ret < 0) { + perror("magic_receive4"); + return 1; + } + + if (!inet_ntop(AF_INET, &endpoint.src4, srcaddr, sizeof(srcaddr))) { + perror("inet_ntop"); + return 1; + } + + if (!inet_ntop(AF_INET, &endpoint.addr4.sin_addr, dstaddr, sizeof(dstaddr))) { + perror("inet_ntop"); + return 1; + } + + printf("if:%d src:%s dst:%s\n", endpoint.src_if4, srcaddr, dstaddr); + printf("Received a packet. Sleeping for 10 seconds before replying, so you have time to mess with your networking setup.\n"); + sleep(10); + + ret = magic_send4(sock, &endpoint, buffer, sizeof(buffer)); + if (ret < 0) { + perror("magic_send4"); + return 1; + } + + close(sock); + return 0; +}