From: bluhm Date: Thu, 7 Dec 2023 23:47:48 +0000 (+0000) Subject: Run bind(2) and connect(2) stress test also with IPv6. X-Git-Url: http://artulab.com/gitweb/?a=commitdiff_plain;h=dd1c5868edaa80b7ad9df54e8b3eae1c49c42319;p=openbsd Run bind(2) and connect(2) stress test also with IPv6. --- diff --git a/regress/sys/netinet/bindconnect/Makefile b/regress/sys/netinet/bindconnect/Makefile index 258a6a3f8c7..e2cb3295ef0 100644 --- a/regress/sys/netinet/bindconnect/Makefile +++ b/regress/sys/netinet/bindconnect/Makefile @@ -1,4 +1,4 @@ -# $OpenBSD: Makefile,v 1.2 2023/12/06 22:57:14 bluhm Exp $ +# $OpenBSD: Makefile,v 1.3 2023/12/07 23:47:48 bluhm Exp $ PROG= bindconnect LDADD= -lpthread @@ -8,6 +8,7 @@ WARNINGS= yes CLEANFILES= ktrace.out LOCAL_NET ?= +LOCAL_NET6 ?= REGRESS_ROOT_TARGETS += setup-maxfiles run-100000 run-localnet-connect-delete @@ -18,52 +19,68 @@ setup-maxfiles: REGRESS_SETUP += ${PROG} -.if ! empty(LOCAL_NET) -REGRESS_CLEANUP += cleanup-delete -.endif -cleanup-delete: - -${SUDO} time ./${PROG} -s 0 -o 0 -b 0 -c 0 -d 1 -N ${LOCAL_NET} -t 1 - REGRESS_TARGETS += run-default run-default: time ${KTRACE} ./${PROG} -REGRESS_TARGETS += run-bind -run-bind: - time ${KTRACE} ./${PROG} -n 16 -s 2 -o 1 -b 5 -c 0 - -REGRESS_TARGETS += run-connect -run-connect: - time ${KTRACE} ./${PROG} -n 16 -s 2 -o 1 -b 0 -c 5 - -REGRESS_TARGETS += run-bind-connect -run-bind-connect: - time ${KTRACE} ./${PROG} -n 16 -s 2 -o 1 -b 3 -c 3 +NET_inet = ${LOCAL_NET} +NET_inet6 = ${LOCAL_NET6} -REGRESS_TARGETS += run-100000 -run-100000: - ${SUDO} time ${KTRACE} ./${PROG} -n 100000 -s 2 -o 1 -b 3 -c 3 +.for af in inet inet6 -REGRESS_TARGETS += run-reuseport -run-reuseport: - time ${KTRACE} ./${PROG} -n 16 -s 2 -o 1 -b 3 -c 3 -r - -.if empty(LOCAL_NET) -REGRESS_SKIP_TARGETS += run-localnet-connect run-localnet-bind-connect \ - run-localnet-connect-delete +.if ! empty(NET_${af}) +REGRESS_CLEANUP += cleanup-${af}-delete +.endif +cleanup-${af}-delete: + -${SUDO} time ./${PROG} \ + -f ${af} -s 0 -o 0 -b 0 -c 0 -d 1 -N ${NET_${af}} -t 1 + +REGRESS_TARGETS += run-${af}-bind +run-${af}-bind: + time ${KTRACE} ./${PROG} \ + -f ${af} -n 16 -s 2 -o 1 -b 6 -c 0 + +REGRESS_TARGETS += run-${af}-connect +run-${af}-connect: + time ${KTRACE} ./${PROG} \ + -f ${af} -n 16 -s 2 -o 1 -b 0 -c 6 + +REGRESS_TARGETS += run-${af}-bind-connect +run-${af}-bind-connect: + time ${KTRACE} ./${PROG} \ + -f ${af} -n 16 -s 2 -o 1 -b 3 -c 3 + +REGRESS_TARGETS += run-${af}-100000 +run-${af}-100000: + ${SUDO} time ${KTRACE} ./${PROG} \ + -f ${af} -n 100000 -s 2 -o 1 -b 3 -c 3 + +REGRESS_TARGETS += run-${af}-reuseport +run-${af}-reuseport: + time ${KTRACE} ./${PROG} \ + -f ${af} -n 16 -s 2 -o 1 -b 3 -c 3 -r + +.if empty(NET_${af}) +REGRESS_SKIP_TARGETS += run-${af}-localnet-connect \ + run-${af}-localnet-bind-connect \ + run-${af}-localnet-connect-delete .endif -REGRESS_TARGETS += run-localnet-connect -run-localnet-connect: - time ${KTRACE} ./${PROG} -n 16 -s 2 -o 1 -c 5 -N ${LOCAL_NET} +REGRESS_TARGETS += run-${af}-localnet-connect +run-${af}-localnet-connect: + time ${KTRACE} ./${PROG} \ + -f ${af} -n 16 -s 2 -o 1 -c 6 -N ${NET_${af}} + +REGRESS_TARGETS += run-${af}-localnet-bind-connect +run-${af}-localnet-bind-connect: + time ${KTRACE} ./${PROG} \ + -f ${af} -n 16 -s 2 -o 1 -b 3 -c 3 -N ${NET_${af}} -REGRESS_TARGETS += run-localnet-bind-connect -run-localnet-bind-connect: - time ${KTRACE} ./${PROG} -n 16 -s 2 -o 1 -b 3 -c 3 -N ${LOCAL_NET} +REGRESS_TARGETS += run-${af}-localnet-connect-delete +run-${af}-localnet-connect-delete: + ${SUDO} time ${KTRACE} ./${PROG} \ + -f ${af} -n 16 -s 2 -o 1 -b 0 -c 6 -d 3 -N ${NET_${af}} -REGRESS_TARGETS += run-localnet-connect-delete -run-localnet-connect-delete: - ${SUDO} time ${KTRACE} \ - ./${PROG} -n 16 -s 2 -o 1 -b 0 -c 5 -d 3 -N ${LOCAL_NET} +.endfor .include diff --git a/regress/sys/netinet/bindconnect/README b/regress/sys/netinet/bindconnect/README index 0c168e63e10..f4259087738 100644 --- a/regress/sys/netinet/bindconnect/README +++ b/regress/sys/netinet/bindconnect/README @@ -5,6 +5,7 @@ bindconnect [-r] [-b bind] [-c connect] [-d delroute] -b bind threads binding sockets, default 1 -c connect threads connecting sockets, default 1 -d delroute threads deleting cloned routes, default 0 + -f family address family inet or inet6, default inet -N addr/net connect to any address within network -n num number of file descriptors, default 128 -o close threads closing sockets, default 1 @@ -19,9 +20,9 @@ system calls operate on random file descriptors. By setting the number of threads for each system call and the number of available file descriptors, the focus for the stress test can be changed. -Currently only IPv4 UDP sockets are supported. Per default the -address to bind and connect is 127.0.0.1. LOCAL_NET environment -variable allows to bind on a local address and connect to all -directly attached hosts. This triggers creation of cloned routes -during source address selection. To stress test routing table, -these routes can be deleted in another thread. +Currently IPv4 and IPv6 UDP sockets are supported. Per default the +address to bind and connect is 127.0.0.1 or ::1. LOCAL_NET or +LOCAL_NET6 environment variable allows to bind on a local address +and connect to all directly attached hosts. This triggers creation +of cloned routes during source address selection. To stress test +routing table, these routes can be deleted in another thread. diff --git a/regress/sys/netinet/bindconnect/bindconnect.c b/regress/sys/netinet/bindconnect/bindconnect.c index 19966d9bbb0..7ed5eaf1297 100644 --- a/regress/sys/netinet/bindconnect/bindconnect.c +++ b/regress/sys/netinet/bindconnect/bindconnect.c @@ -1,4 +1,4 @@ -/* $OpenBSD: bindconnect.c,v 1.2 2023/12/06 22:57:14 bluhm Exp $ */ +/* $OpenBSD: bindconnect.c,v 1.3 2023/12/07 23:47:48 bluhm Exp $ */ /* * Copyright (c) 2023 Alexander Bluhm @@ -33,14 +33,30 @@ #define MAX(a, b) ((a) > (b) ? (a) : (b)) +#define s6_addr8 __u6_addr.__u6_addr8 +#define s6_addr16 __u6_addr.__u6_addr16 +#define s6_addr32 __u6_addr.__u6_addr32 + +union sockaddr_union { + struct sockaddr su_sa; + struct sockaddr_in su_sin; + struct sockaddr_in6 su_sin6; +}; + +union inaddr_union { + struct in_addr au_inaddr; + struct in6_addr au_in6addr; +}; + int fd_base; unsigned int fd_num = 128; unsigned int run_time = 10; unsigned int socket_num = 1, close_num = 1, bind_num = 1, connect_num = 1, delroute_num = 0; int reuse_port = 0; -struct in_addr addr, mask; -int prefix = -1, route_sock = -1; +const char *family = "inet"; +union inaddr_union addr, mask; +int af, prefix = -1, route_sock = -1; static void __dead usage(void) @@ -51,23 +67,18 @@ usage(void) " -b bind threads binding sockets, default %u\n" " -c connect threads connecting sockets, default %u\n" " -d delroute threads deleting cloned routes, default %u\n" + " -f family address family inet or inet6, default %s\n" " -N addr/net connect to any address within network\n" " -n num number of file descriptors, default %u\n" " -o close threads closing sockets, default %u\n" " -r set reuse port socket option\n" " -s socket threads creating sockets, default %u\n" " -t time run time in seconds, default %u\n", - bind_num, connect_num, delroute_num, fd_num, close_num, socket_num, - run_time); + bind_num, connect_num, delroute_num, family, fd_num, close_num, + socket_num, run_time); exit(2); } -static inline struct sockaddr * -sintosa(struct sockaddr_in *sin) -{ - return ((struct sockaddr *)(sin)); -} - static void in_prefixlen2mask(struct in_addr *maskp, int plen) { @@ -77,6 +88,72 @@ in_prefixlen2mask(struct in_addr *maskp, int plen) maskp->s_addr = htonl(0xffffffff << (32 - plen)); } +static void +in6_prefixlen2mask(struct in6_addr *maskp, int len) +{ + u_char maskarray[8] = {0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe, 0xff}; + int bytelen, bitlen, i; + + bzero(maskp, sizeof(*maskp)); + bytelen = len / 8; + bitlen = len % 8; + for (i = 0; i < bytelen; i++) + maskp->s6_addr[i] = 0xff; + /* len == 128 is ok because bitlen == 0 then */ + if (bitlen) + maskp->s6_addr[bytelen] = maskarray[bitlen - 1]; +} + +static void +fill_sockaddr(union sockaddr_union *su) +{ + memset(su, 0, sizeof(*su)); + su->su_sa.sa_family = af; + if (af == AF_INET) { + su->su_sin.sin_len = sizeof(su->su_sin); + if (prefix >= 0) + su->su_sin.sin_addr = addr.au_inaddr; + else + su->su_sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + } + if (af == AF_INET6) { + su->su_sin6.sin6_len = sizeof(su->su_sin6); + if (prefix >= 0) + su->su_sin6.sin6_addr = addr.au_in6addr; + else + su->su_sin6.sin6_addr = in6addr_loopback; + } +} + +static void +mask_sockaddr(union sockaddr_union *su) +{ + if (af == AF_INET) { + if (prefix >=0 && prefix != 32) { + su->su_sin.sin_addr.s_addr &= + mask.au_inaddr.s_addr; + /* do only 8 bits variation, routes should be reused */ + su->su_sin.sin_addr.s_addr |= htonl(255) & + ~mask.au_inaddr.s_addr & arc4random(); + } + } + if (af == AF_INET6) { + if (prefix >=0 && prefix != 128) { + su->su_sin6.sin6_addr.s6_addr32[0] &= + mask.au_in6addr.s6_addr32[0]; + su->su_sin6.sin6_addr.s6_addr32[1] &= + mask.au_in6addr.s6_addr32[1]; + su->su_sin6.sin6_addr.s6_addr32[2] &= + mask.au_in6addr.s6_addr32[2]; + su->su_sin6.sin6_addr.s6_addr32[3] &= + mask.au_in6addr.s6_addr32[3]; + /* do only 8 bits variation, routes should be reused */ + su->su_sin6.sin6_addr.s6_addr32[3] |= htonl(255) & + ~mask.au_in6addr.s6_addr32[3] & arc4random(); + } + } +} + static void * thread_socket(void *arg) { @@ -87,7 +164,7 @@ thread_socket(void *arg) for (count = 0; *run; count++) { int opt; - fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + fd = socket(af, SOCK_DGRAM, IPPROTO_UDP); if (fd < 0 || !reuse_port) continue; opt = 1; @@ -118,19 +195,13 @@ thread_bind(void *arg) volatile int *run = arg; unsigned long count; int fd; - struct sockaddr_in sin; - - memset(&sin, 0, sizeof(sin)); - sin.sin_len = sizeof(sin); - sin.sin_family = AF_INET; - sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + union sockaddr_union su; - if (prefix >= 0) - sin.sin_addr = addr; + fill_sockaddr(&su); for (count = 0; *run; count++) { fd = fd_base + arc4random_uniform(fd_num); - bind(fd, sintosa(&sin), sizeof(sin)); + bind(fd, &su.su_sa, su.su_sa.sa_len); } return (void *)count; @@ -142,24 +213,18 @@ thread_connect(void *arg) volatile int *run = arg; unsigned long count; int fd; - struct sockaddr_in sin; + union sockaddr_union su; - memset(&sin, 0, sizeof(sin)); - sin.sin_len = sizeof(sin); - sin.sin_family = AF_INET; - sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK); - - if (prefix >= 0) - sin.sin_addr = addr; + fill_sockaddr(&su); for (count = 0; *run; count++) { fd = fd_base + arc4random_uniform(fd_num); - if (prefix >=0 && prefix != 32) { - sin.sin_addr.s_addr &= mask.s_addr; - sin.sin_addr.s_addr |= ~mask.s_addr & arc4random(); - } - sin.sin_port = arc4random(); - connect(fd, sintosa(&sin), sizeof(sin)); + mask_sockaddr(&su); + if (af == AF_INET) + su.su_sin.sin_port = arc4random(); + if (af == AF_INET6) + su.su_sin6.sin6_port = arc4random(); + connect(fd, &su.su_sa, su.su_sa.sa_len); } return (void *)count; @@ -175,7 +240,7 @@ thread_delroute(void *arg) struct rt_msghdr m_rtm; char m_space[512]; } m_rtmsg; - struct sockaddr_in sin; + union sockaddr_union su; #define rtm \ m_rtmsg.m_rtm @@ -185,8 +250,8 @@ thread_delroute(void *arg) (x += ROUNDUP((n)->sa_len)) #define NEXTADDR(w, sa) \ if (rtm.rtm_addrs & (w)) { \ - int l = ROUNDUP(sa->sa_len); \ - memcpy(cp, sa, l); \ + int l = ROUNDUP((sa)->sa_len); \ + memcpy(cp, (sa), l); \ cp += l; \ } @@ -195,21 +260,16 @@ thread_delroute(void *arg) rtm.rtm_flags = RTF_HOST; rtm.rtm_version = RTM_VERSION; rtm.rtm_addrs = RTA_DST; - rtm.rtm_priority = 0; /* XXX */ rtm.rtm_hdrlen = sizeof(rtm); - memset(&sin, 0, sizeof(sin)); - sin.sin_len = sizeof(sin); - sin.sin_family = AF_INET; - sin.sin_addr = addr; + fill_sockaddr(&su); for (count = 0; *run; count++) { char *cp = m_rtmsg.m_space; rtm.rtm_seq = ++seq; - sin.sin_addr.s_addr &= mask.s_addr; - sin.sin_addr.s_addr |= ~mask.s_addr & arc4random(); - NEXTADDR(RTA_DST, sintosa(&sin)); + mask_sockaddr(&su); + NEXTADDR(RTA_DST, &su.su_sa); rtm.rtm_msglen = cp - (char *)&m_rtmsg; write(route_sock, &m_rtmsg, rtm.rtm_msglen); } @@ -228,12 +288,14 @@ main(int argc, char *argv[]) struct rlimit rlim; pthread_t *tsocket, *tclose, *tbind, *tconnect, *tdelroute; const char *errstr, *addr_net = NULL; + char buf[128], *p; int ch, run; unsigned int n; unsigned long socket_count, close_count, bind_count, connect_count, delroute_count; + union sockaddr_union su; - while ((ch = getopt(argc, argv, "b:c:d:N:n:o:rs:t:")) != -1) { + while ((ch = getopt(argc, argv, "b:c:d:f:N:n:o:rs:t:")) != -1) { switch (ch) { case 'b': bind_num = strtonum(optarg, 0, UINT_MAX, &errstr); @@ -250,6 +312,9 @@ main(int argc, char *argv[]) if (errstr != NULL) errx(1, "delroute is %s: %s", errstr, optarg); break; + case 'f': + family = optarg; + break; case 'N': addr_net = optarg; break; @@ -285,27 +350,58 @@ main(int argc, char *argv[]) if (argc > 0) usage(); + if (strcmp(family, "inet") == 0) + af = AF_INET; + else if (strcmp(family, "inet6") == 0) + af = AF_INET6; + else + errx(1, "bad address family %s", family); + + /* split addr/net into addr, mask, prefix */ if (addr_net != NULL) { - prefix = inet_net_pton(AF_INET, addr_net, &addr, sizeof(addr)); + prefix = inet_net_pton(af, addr_net, &addr, sizeof(addr)); if (prefix < 0) err(1, "inet_net_pton %s", addr_net); - in_prefixlen2mask(&mask, prefix); + if (af == AF_INET6) { + /* + * Man page says inet_net_pton() preserves lower + * bits. That is not true, call inet_pton() again. + */ + if (strlcpy(buf, addr_net, sizeof(buf)) >= sizeof(buf)) + err(1, "strlcpy %s", addr_net); + p = strchr(buf, '/'); + if (p != NULL ) { + *p = '\0'; + if (inet_pton(af, buf, &addr) < 0) + err(1, "inet_pton %s", buf); + } + } + if (af == AF_INET) + in_prefixlen2mask(&mask.au_inaddr, prefix); + if (af == AF_INET6) + in6_prefixlen2mask(&mask.au_in6addr, prefix); } + + /* preopen route socket before file descriptor limits are set */ if (delroute_num > 0) { if (prefix < 0 || prefix == 32) errx(1, "delroute %u needs addr/net", delroute_num); - route_sock = socket(AF_ROUTE, SOCK_RAW, AF_INET); + route_sock = socket(AF_ROUTE, SOCK_RAW, af); if (route_sock < 0) err(1, "socket route"); if (shutdown(route_sock, SHUT_RD) < 0) err(1, "shutdown read route"); } - fd_base = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + /* detect lowest file desciptor, test bind, close everything above */ + fd_base = socket(af, SOCK_DGRAM, IPPROTO_UDP); if (fd_base < 0) err(1, "socket fd_base"); if (fd_base > INT_MAX - (int)fd_num) err(1, "fd base %d and num %u overflow", fd_base, fd_num); + fill_sockaddr(&su); + if (bind(fd_base, &su.su_sa, su.su_sa.sa_len) < 0) + err(1, "bind %s", inet_ntop(af, &addr, buf, sizeof(buf))); if (closefrom(fd_base) < 0) err(1, "closefrom %d", fd_base);