[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <1430747599.3711.170.camel@edumazet-glaptop2.roam.corp.google.com>
Date: Mon, 04 May 2015 06:53:19 -0700
From: Eric Dumazet <eric.dumazet@...il.com>
To: mtk.manpages@...il.com
Cc: Eric B Munson <emunson@...mai.com>,
Tom Herbert <tom@...bertland.com>,
"David S. Miller" <davem@...emloft.net>,
Linux API <linux-api@...r.kernel.org>,
netdev <netdev@...r.kernel.org>
Subject: Re: [PATCH net-next] tcp: provide SYN headers for passive
connections
On Mon, 2015-05-04 at 08:47 +0200, Michael Kerrisk (man-pages) wrote:
> Eric,
>
> On 4 May 2015 at 06:34, Eric Dumazet <eric.dumazet@...il.com> wrote:
> > From: Eric Dumazet <edumazet@...gle.com>
> >
> > This patch allows a server application to get the TCP SYN headers for
> > its passive connections. This is useful if the server is doing
> > fingerprinting of clients based on SYN packet contents.
> >
> > Two socket options are added: TCP_SAVE_SYN and TCP_SAVED_SYN.
> >
> > The first is used on a socket to enable saving the SYN headers
> > for child connections. This can be set before or after the listen()
> > call.
> >
> > The latter is used to retrieve the SYN headers for passive connections,
> > if the parent listener has enabled TCP_SAVE_SYN.
> >
> > TCP_SAVED_SYN is read once, it frees the saved SYN headers.
> >
> > The data returned in TCP_SAVED_SYN are network (IPv4/IPv6) and TCP
> > headers.
>
> This description is a little thin, so I'm unclear on one or two
> points. TCP_SAVE_SYN is clearly applied to the listening socket. But
> what about TCP_SAVED_SYN? Is that applied to the connected socket
> returned by accept()?
>
> The highly similar naming of these two seems unfortunate. At the very
> least, it makes for easy confusion in conversations about the two
> options. It would be better to have names that were more distinct.
> Perhaps the latter could be TCP_CONN_SYN or TCP_CONN_SAVED_SYN, for
> example?
>
> Thanks,
TCP_CONN_SYN is rather confusing, because of the analogy with connect().
Maybe I can send the test Neal wrote 3 years ago, part of our automated
non regression tests we run here.
Let me know if you need more information, thanks !
/*
* Copyright 2012 Google Inc. All Rights Reserved.
* Author: ncardwell@...gle.com (Neal Cardwell)
*
* A basic test for the TCP_SAVE_SYN and TCP_SAVED_SYN socket options.
*/
#include <assert.h>
#include <errno.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#ifndef TCP_SAVE_SYN
#define TCP_SAVE_SYN 27
#endif
#ifndef TCP_SAVED_SYN
#define TCP_SAVED_SYN 28
#endif
typedef char bool;
typedef enum bool_t {
false = 0,
true = 1,
} bool_t;
static void fail(const char *msg)
{
fprintf(stderr, "%s\n", msg);
exit(1);
}
static void fail_perror(const char *msg)
{
perror(msg);
exit(1);
}
/*
* Once every ~5000 connections, connect fails with ENOBUFS. This is
* not even in the manpage, and seem to be transient. For now, just retry.
*/
static void connect_reliably(int fd, struct sockaddr *daddr, int dlen)
{
int ret, max_runs = 5;
do {
ret = connect(fd, daddr, dlen);
} while (ret == -1 && errno == ENOBUFS && --max_runs);
if (ret)
fail_perror("reliable connect");
}
/* Get and validate the saved SYN. */
static void read_saved_syn(int fd, int address_family)
{
unsigned char syn[500];
socklen_t syn_len = sizeof(syn);
memset(syn, 0, sizeof(syn));
/* Read the saved SYN. */
if (getsockopt(fd, IPPROTO_TCP, TCP_SAVED_SYN, syn, &syn_len) != 0)
fail_perror("first getsockopt TCP_SAVED_SYN failed");
/* Check the length and first byte of the SYN. */
if (address_family == AF_INET) {
assert(syn_len == 60);
assert(syn[0] >> 4 == 0x4); /* IPv4 */
} else if (address_family == AF_INET6) {
assert(syn_len == 80);
assert(syn[0] >> 4 == 0x6); /* IPv6 */
} else {
assert(!"bad address family");
}
/* Check the last few bytes of the SYN, which will be TCP options. */
assert(syn[syn_len-4] == 0x01); /* TCP option: kind = NOP */
assert(syn[syn_len-3] == 0x03); /* TCP option: kind = window scale */
assert(syn[syn_len-2] == 0x03); /* TCP option: length = 3 */
assert(syn[syn_len-1] == 0x06 || syn[syn_len-1] == 0x07); /* TCP option: window scale = 6 or 7 */
/* If we try TCP_SAVED_SYN again it should succeed with 0 length. */
if (getsockopt(fd, IPPROTO_TCP, TCP_SAVED_SYN, syn, &syn_len) != 0)
fail("repeated getsockopt TCP_SAVED_SYN failed");
assert(syn_len == 0);
}
/* Open server and client socket and test TCP_SAVE_SYN and TCP_SAVED_SYN. */
static void do_test(struct sockaddr *srv_addr, uint16_t *srv_port, int srv_len,
struct sockaddr *cli_addr, uint16_t *cli_port, int cli_len,
bool get_saved_syn)
{
int fd_listen = -1, fd_accept = -1, fd_connect = -1;
int one = 1;
fd_listen = socket(srv_addr->sa_family, SOCK_STREAM, 0);
if (fd_listen == -1)
fail_perror("open fd_listen");
if (setsockopt(fd_listen, SOL_SOCKET, SO_REUSEADDR,
&one, sizeof(one)) < 0)
fail_perror("setsockopt SO_REUSEADDR");
if (bind(fd_listen, srv_addr, srv_len))
fail_perror("bind fd_listen");
if (getsockname(fd_listen, srv_addr, (socklen_t *)&srv_len))
fail_perror("getsockname fd_listen");
*cli_port = *srv_port;
if (setsockopt(fd_listen, IPPROTO_TCP, TCP_SAVE_SYN,
&one, sizeof(one)) < 0)
fail_perror("setsockopt TCP_SAVE_SYN");
if (listen(fd_listen, 1))
fail_perror("listen fd_listen");
fd_connect = socket(cli_addr->sa_family, SOCK_STREAM, 0);
if (fd_connect == -1)
fail_perror("open fd_connect");
connect_reliably(fd_connect, cli_addr, cli_len);
fd_accept = accept(fd_listen, NULL, 0);
if (fd_accept == -1)
fail_perror("accept fd_listen");
if (get_saved_syn) {
read_saved_syn(fd_accept, cli_addr->sa_family);
}
if (close(fd_listen))
fail_perror("close fd_listen");
if (close(fd_accept))
fail_perror("close fd_accept");
if (close(fd_connect))
fail_perror("close fd_connect");
}
static void test_ipv4(bool get_saved_syn)
{
struct sockaddr_in srv_addr = {
.sin_family = AF_INET,
.sin_addr.s_addr = INADDR_ANY,
.sin_port = 0,
};
struct sockaddr_in cli_addr = {
.sin_family = AF_INET,
.sin_addr.s_addr = htonl(INADDR_LOOPBACK),
};
printf(" testing IPv4 ...\n");
do_test((struct sockaddr *)&srv_addr, &srv_addr.sin_port,
sizeof(srv_addr),
(struct sockaddr *)&cli_addr, &cli_addr.sin_port,
sizeof(cli_addr),
get_saved_syn);
}
static void test_ipv6(bool get_saved_syn)
{
struct sockaddr_in6 srv_addr = {
.sin6_family = AF_INET6,
.sin6_addr = IN6ADDR_ANY_INIT,
.sin6_port = 0,
};
struct sockaddr_in6 cli_addr = {
.sin6_family = AF_INET6,
.sin6_addr = IN6ADDR_LOOPBACK_INIT,
};
printf(" testing IPv6 ...\n");
do_test((struct sockaddr *)&srv_addr, &srv_addr.sin6_port,
sizeof(srv_addr),
(struct sockaddr *)&cli_addr, &cli_addr.sin6_port,
sizeof(cli_addr),
get_saved_syn);
}
static void test_ipv4_mapped_ipv6(bool get_saved_syn)
{
struct sockaddr_in6 srv_addr = {
.sin6_family = AF_INET6,
.sin6_addr = IN6ADDR_ANY_INIT,
.sin6_port = 0,
};
struct sockaddr_in cli_addr = {
.sin_family = AF_INET,
.sin_addr.s_addr = htonl(INADDR_LOOPBACK),
};
printf(" testing IPv4-mapped-IPv6 (srv=AF_INET6, cli=AF_INET)...\n");
do_test((struct sockaddr *)&srv_addr, &srv_addr.sin6_port,
sizeof(srv_addr),
(struct sockaddr *)&cli_addr, &cli_addr.sin_port,
sizeof(cli_addr),
get_saved_syn);
}
static void test_all_address_families(bool get_saved_syn)
{
test_ipv4(get_saved_syn);
test_ipv6(get_saved_syn);
test_ipv4_mapped_ipv6(get_saved_syn);
}
static void run_all_tests(void)
{
bool get_saved_syn;
/* Test normal behavior, when we ask the kernel to record the
* SYN and then read it using the TCP_SAVED_SYN getsockopt().
*/
printf("test: reading the saved SYN...\n");
get_saved_syn = true;
test_all_address_families(get_saved_syn);
/* Test behavior when we ask the kernel to record the SYN and
* then never actually use the TCP_SAVED_SYN getsockopt() to
* extract the saved SYN.
*/
printf("test: not reading the saved SYN...\n");
get_saved_syn = false;
test_all_address_families(get_saved_syn);
}
static int usage(const char *executable_path)
{
fprintf(stderr, "usage: %s\n", executable_path);
return 1;
}
int main(int argc, char **argv)
{
if (getuid() != 0 || getgid() != 0)
fail("must run as root\n");
if (argc != 1)
return usage(argv[0]);
system("sysctl net.ipv4.tcp_timestamps=1");
system("sysctl net.ipv4.tcp_sack=1");
system("sysctl net.ipv4.tcp_window_scaling=1");
run_all_tests();
printf("OK. All tests passed.\n");
return 0;
}
--
To unsubscribe from this list: send the line "unsubscribe netdev" in
the body of a message to majordomo@...r.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Powered by blists - more mailing lists