/* * Test key rotation for TFO. * New keys are 'rotated' in two steps: * 1) Add new key as the 'backup' key 'behind' the primary key * 2) Make new key the primary by swapping the backup and primary keys * * The rotation is done in stages using multiple sockets bound * to the same port via SO_REUSEPORT. This simulates key rotation * behind say a load balancer. We verify that across the rotation * there are no cases in which a cookie is not accepted by verifying * that TcpExtTCPFastOpenPassiveFail remains 0.
*/ #define _GNU_SOURCE #include <arpa/inet.h> #include <errno.h> #include <error.h> #include <stdbool.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/epoll.h> #include <unistd.h> #include <netinet/tcp.h> #include <fcntl.h> #include <time.h>
if (do_sockopt) { if (setsockopt(fd, SOL_TCP, TCP_FASTOPEN_KEY, keys,
key_len))
error(1, errno, "Unable to set key"); return;
} if (do_rotate)
snprintf(buf, 128, "%08x-%08x-%08x-%08x,%08x-%08x-%08x-%08x",
keys[0], keys[1], keys[2], keys[3], keys[4], keys[5],
keys[6], keys[7]); else
snprintf(buf, 128, "%08x-%08x-%08x-%08x",
keys[0], keys[1], keys[2], keys[3]);
lseek(proc_fd, 0, SEEK_SET); if (write(proc_fd, buf, sizeof(buf)) <= 0)
error(1, errno, "Unable to write %s", PROC_FASTOPEN_KEY);
}
staticvoid build_rcv_fd(int family, int proto, int *rcv_fds)
{ struct sockaddr_in addr4 = {0}; struct sockaddr_in6 addr6 = {0}; struct sockaddr *addr; int opt = 1, i, sz; int qlen = 100;
uint32_t keys[8];
switch (family) { case AF_INET:
addr4.sin_family = family;
addr4.sin_addr.s_addr = htonl(INADDR_ANY);
addr4.sin_port = htons(PORT);
sz = sizeof(addr4);
addr = (struct sockaddr *)&addr4; break; case AF_INET6:
addr6.sin6_family = AF_INET6;
addr6.sin6_addr = in6addr_any;
addr6.sin6_port = htons(PORT);
sz = sizeof(addr6);
addr = (struct sockaddr *)&addr6; break; default:
error(1, 0, "Unsupported family %d", family); /* clang does not recognize error() above as terminating * the program, so it complains that saddr, sz are * not initialized when this code path is taken. Silence it.
*/ return;
} for (i = 0; i < ARRAY_SIZE(keys); i++)
keys[i] = rand(); for (i = 0; i < N_LISTEN; i++) {
rcv_fds[i] = socket(family, proto, 0); if (rcv_fds[i] < 0)
error(1, errno, "failed to create receive socket"); if (setsockopt(rcv_fds[i], SOL_SOCKET, SO_REUSEPORT, &opt, sizeof(opt)))
error(1, errno, "failed to set SO_REUSEPORT"); if (bind(rcv_fds[i], addr, sz))
error(1, errno, "failed to bind receive socket"); if (setsockopt(rcv_fds[i], SOL_TCP, TCP_FASTOPEN, &qlen, sizeof(qlen)))
error(1, errno, "failed to set TCP_FASTOPEN");
set_keys(rcv_fds[i], keys); if (proto == SOCK_STREAM && listen(rcv_fds[i], 10))
error(1, errno, "failed to listen on receive port");
}
}
sz = sizeof(saddr6);
saddr = (struct sockaddr *)&saddr6;
daddr = (struct sockaddr *)&daddr6; break; default:
error(1, 0, "Unsupported family %d", family); /* clang does not recognize error() above as terminating * the program, so it complains that saddr, daddr, sz are * not initialized when this code path is taken. Silence it.
*/ return -1;
}
fd = socket(family, proto, 0); if (fd < 0)
error(1, errno, "failed to create send socket"); if (bind(fd, saddr, sz))
error(1, errno, "failed to bind send socket");
data[0] = 'a';
ret = sendto(fd, data, 1, MSG_FASTOPEN, daddr, sz); if (ret != 1)
error(1, errno, "failed to sendto");
return fd;
}
staticbool is_listen_fd(int fd)
{ int i;
for (i = 0; i < N_LISTEN; i++) { if (rcv_fds[i] == fd) returntrue;
} returnfalse;
}
if (iter < N_LISTEN) { /* first set new key as backups */ if (iter == 0) { for (i = 0; i < ARRAY_SIZE(new_key); i++)
new_key[i] = rand();
}
get_keys(fd, keys);
memcpy(keys + 4, new_key, KEY_LENGTH);
set_keys(fd, keys);
} else { /* swap the keys */
get_keys(fd, keys);
memcpy(tmp_key, keys + 4, KEY_LENGTH);
memcpy(keys + 4, keys, KEY_LENGTH);
memcpy(keys, tmp_key, KEY_LENGTH);
set_keys(fd, keys);
} if (++iter >= (N_LISTEN * 2))
iter = 0;
}
staticvoid run_one_test(int family)
{ struct epoll_event ev; int i, send_fd; int n_loops = 10000; int rotate_key_fd = 0; int key_rotate_interval = 50; int fd, epfd; char buf[1];
build_rcv_fd(family, SOCK_STREAM, rcv_fds);
epfd = epoll_create(1); if (epfd < 0)
error(1, errno, "failed to create epoll");
ev.events = EPOLLIN; for (i = 0; i < N_LISTEN; i++) {
ev.data.fd = rcv_fds[i]; if (epoll_ctl(epfd, EPOLL_CTL_ADD, rcv_fds[i], &ev))
error(1, errno, "failed to register sock epoll");
} while (n_loops--) {
send_fd = connect_and_send(family, SOCK_STREAM); if (do_rotate && ((n_loops % key_rotate_interval) == 0)) {
rotate_key(rcv_fds[rotate_key_fd]); if (++rotate_key_fd >= N_LISTEN)
rotate_key_fd = 0;
} while (1) {
i = epoll_wait(epfd, &ev, 1, -1); if (i < 0)
error(1, errno, "epoll_wait failed"); if (is_listen_fd(ev.data.fd)) {
fd = accept(ev.data.fd, NULL, NULL); if (fd < 0)
error(1, errno, "failed to accept");
ev.data.fd = fd; if (epoll_ctl(epfd, EPOLL_CTL_ADD, fd, &ev))
error(1, errno, "failed epoll add"); continue;
}
i = recv(ev.data.fd, buf, sizeof(buf), 0); if (i != 1)
error(1, errno, "failed recv data"); if (epoll_ctl(epfd, EPOLL_CTL_DEL, ev.data.fd, NULL))
error(1, errno, "failed epoll del");
close(ev.data.fd); break;
}
close(send_fd);
} for (i = 0; i < N_LISTEN; i++)
close(rcv_fds[i]);
}
staticvoid parse_opts(int argc, char **argv)
{ int c;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.