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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 literal 0 Hc-jL100001 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; +} -- 2.47.2