aboutgitcodebugslistschat
path: root/tcp.c
diff options
context:
space:
mode:
authorStefano Brivio <sbrivio@redhat.com>2021-03-17 10:57:36 +0100
committerStefano Brivio <sbrivio@redhat.com>2021-03-17 10:57:36 +0100
commita41894683710b00d67015cd1683eef6de0ffd89b (patch)
tree19356a46614530fd359d238eea7f368bffecf495 /tcp.c
parent8bca388e8a771d069b2a2d4ac47589112f6f0af3 (diff)
downloadpasst-a41894683710b00d67015cd1683eef6de0ffd89b.tar
passt-a41894683710b00d67015cd1683eef6de0ffd89b.tar.gz
passt-a41894683710b00d67015cd1683eef6de0ffd89b.tar.bz2
passt-a41894683710b00d67015cd1683eef6de0ffd89b.tar.lz
passt-a41894683710b00d67015cd1683eef6de0ffd89b.tar.xz
passt-a41894683710b00d67015cd1683eef6de0ffd89b.tar.zst
passt-a41894683710b00d67015cd1683eef6de0ffd89b.zip
tcp: Add siphash implementation for initial sequence numbers
Implement siphash routines for initial TCP sequence numbers (12 bytes input for IPv4, 36 bytes input for IPv6), and while at it, also functions we'll use later on for hash table indices and TCP timestamp offsets (with 8, 20, 32 bytes of input). Use these to set the initial sequence number, according to RFC 6528, for connections originating either from the tap device or from sockets. Signed-off-by: Stefano Brivio <sbrivio@redhat.com>
Diffstat (limited to 'tcp.c')
-rw-r--r--tcp.c78
1 files changed, 71 insertions, 7 deletions
diff --git a/tcp.c b/tcp.c
index f1de9cf..6c6a6dd 100644
--- a/tcp.c
+++ b/tcp.c
@@ -304,8 +304,9 @@
#include <stddef.h>
#include <string.h>
#include <sys/epoll.h>
-#include <sys/types.h>
+#include <sys/random.h>
#include <sys/socket.h>
+#include <sys/types.h>
#include <unistd.h>
#include <linux/ip.h>
#include <linux/ipv6.h>
@@ -315,6 +316,7 @@
#include "passt.h"
#include "tap.h"
#include "util.h"
+#include "siphash.h"
/* Approximately maximum number of open descriptors per process */
#define MAX_CONNS (256 * 1024)
@@ -430,6 +432,7 @@ struct tcp_conn {
static char sock_buf[MAX_WINDOW];
static uint8_t tcp_act[MAX_CONNS / 8] = { 0 };
static struct tcp_conn tc[MAX_CONNS];
+static uint64_t hash_secret[2];
static int tcp_send_to_tap(struct ctx *c, int s, int flags, char *in, int len);
@@ -677,6 +680,60 @@ static void tcp_clamp_window(int s, struct tcphdr *th, int len, int init)
}
/**
+ * tcp_seq_init() - Calculate initial sequence number according to RFC 6528
+ * @c: Execution context
+ * @af: Address family, AF_INET or AF_INET6
+ * @addr: Remote address, pointer to sin_addr or sin6_addr
+ * @dstport: Destination port, connection-wise, network order
+ * @srcport: Source port, connection-wise, network order
+ *
+ * Return: initial TCP sequence
+ */
+static uint32_t tcp_seq_init(struct ctx *c, int af, void *addr,
+ in_port_t dstport, in_port_t srcport)
+{
+ struct timespec ts = { 0 };
+ uint32_t ns, seq;
+
+ clock_gettime(CLOCK_MONOTONIC, &ts);
+
+ if (af == AF_INET) {
+ struct {
+ struct in_addr src;
+ in_port_t srcport;
+ struct in_addr dst;
+ in_port_t dstport;
+ } __attribute__((__packed__)) in = {
+ .src = *(struct in_addr *)addr,
+ .srcport = srcport,
+ .dst = *(struct in_addr *)c->addr4,
+ .dstport = dstport,
+ };
+
+ seq = siphash_12b((uint8_t *)&in, hash_secret);
+ } else if (af == AF_INET6) {
+ struct {
+ struct in6_addr src;
+ in_port_t srcport;
+ struct in6_addr dst;
+ in_port_t dstport;
+ } __attribute__((__packed__)) in = {
+ .src = *(struct in6_addr *)addr,
+ .srcport = srcport,
+ .dst = c->addr6,
+ .dstport = dstport,
+ };
+
+ seq = siphash_36b((uint8_t *)&in, hash_secret);
+ }
+
+ ns = ts.tv_sec * 1E9;
+ ns += ts.tv_nsec >> 5; /* 32ns ticks, overflows 32 bits every 137s */
+
+ return seq + ns;
+}
+
+/**
* tcp_conn_from_tap() - Handle connection request (SYN segment) from tap
* @c: Execution context
* @af: Address family, AF_INET or AF_INET6
@@ -744,9 +801,8 @@ static void tcp_conn_from_tap(struct ctx *c, int af, void *addr,
tc[s].seq_from_tap = tc[s].seq_init_from_tap + 1;
tc[s].seq_ack_to_tap = tc[s].seq_from_tap;
- /* TODO: RFC 6528 with SipHash, worth it? */
- tc[s].seq_to_tap = 0;
- tc[s].seq_ack_from_tap = tc[s].seq_to_tap;
+ tc[s].seq_to_tap = tcp_seq_init(c, af, addr, th->dest, th->source);
+ tc[s].seq_ack_from_tap = tc[s].seq_to_tap + 1;
if (connect(s, sa, sl)) {
if (errno != EINPROGRESS) {
@@ -827,6 +883,10 @@ static void tcp_conn_from_sock(struct ctx *c, int fd)
tc[s].sock_port = sa4->sin_port;
tc[s].tap_port = ((struct sockaddr_in *)&sa_l)->sin_port;
+
+ tc[s].seq_to_tap = tcp_seq_init(c, AF_INET, &sa4->sin_addr,
+ tc[s].sock_port,
+ tc[s].tap_port);
} else if (sa_l.ss_family == AF_INET6) {
struct sockaddr_in6 *sa6 = (struct sockaddr_in6 *)&sa_r;
@@ -834,10 +894,12 @@ static void tcp_conn_from_sock(struct ctx *c, int fd)
tc[s].sock_port = sa6->sin6_port;
tc[s].tap_port = ((struct sockaddr_in6 *)&sa_l)->sin6_port;
+
+ tc[s].seq_to_tap = tcp_seq_init(c, AF_INET6, &sa6->sin6_addr,
+ tc[s].sock_port,
+ tc[s].tap_port);
}
- /* TODO: RFC 6528 with SipHash, worth it? */
- tc[s].seq_to_tap = 0;
tc[s].seq_ack_from_tap = tc[s].seq_to_tap + 1;
tc[s].tap_window = WINDOW_DEFAULT;
@@ -1230,7 +1292,7 @@ void tcp_sock_handler(struct ctx *c, int s, uint32_t events)
}
/**
- * tcp_sock_init() - Create and bind listening sockets for inbound connections
+ * tcp_sock_init() - Bind sockets for inbound connections, get key for sequence
* @c: Execution context
*
* Return: 0 on success, -1 on failure
@@ -1246,6 +1308,8 @@ int tcp_sock_init(struct ctx *c)
return -1;
}
+ getrandom(hash_secret, sizeof(hash_secret), GRND_RANDOM);
+
return 0;
}