Reading TCP Options in Go
In Go http.Server
allows us to set ConnContext
, a custom function to modify the contetx used for a new connection. We can get the socket file descriptor from the connection through net.TCPConn.Control
.
server := http.Server{
ConnContext: func(ctx context.Context, c net.Conn) context.Context {
// In case the syscall fails, return early
sock, err := c.(*net.TCPConn).SyscallConn()
if err != nil {
return ctx
}
sock.Control(func(fd uintptr) {
ctx = context.WithValue(ctx, "info", netutils.Parse(fd))
})
return ctx
},
}
To then extract the tcp options given a socket file descriptor we will use getsocktopt
. Let’s first create a small wrapper around our C code.
package netutils
// #include "parse.h"
import "C"
type TCPOptions struct {
Mss int
Window int
Scale int
TTL int
}
func Parse(fd uintptr) Options {
var i C.struct_info_t = C.get_info(C.int(fd))
return Options{
Mss: uint32(i.tcp.tcpi_rcv_mss),
Window: uint32(i.tcp.tcpi_rcv_space),
Scale: uint32(i.tcp.tcpi_rcv_wscale),
TTL: int(i.ip.ttl),
}
}
And now the C code:
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/tcp.h>
struct tcp_info_t {
uint8_t tcpi_rcv_wscale;
uint32_t tcpi_rcv_mss;
uint32_t tcpi_rcv_space;
};
struct ip_info_t {
uint8_t ttl;
};
struct info_t {
struct tcp_info_t tcp;
struct ip_info_t ip;
};
static inline struct info_t get_info(int fd) {
struct info_t info;
// TCP option information
struct tcp_info ti;
socklen_t tisize = sizeof(ti);
getsockopt(fd, IPPROTO_TCP, TCP_INFO, &ti, &tisize);
info.tcp.tcpi_rcv_wscale = ti.tcpi_rcv_wscale;
info.tcp.tcpi_rcv_mss = ti.tcpi_rcv_mss;
info.tcp.tcpi_rcv_space = ti.tcpi_rcv_space;
// IP TTL
socklen_t optlen = sizeof(int);
getsockopt(fd, IPPROTO_IP, IP_TTL, &info.ip.ttl, &optlen);
return info;
}