一汁三菜

自分が楽しいと思うこと、マラソン、旅行、その他日々の記録をしたい。

TCP SYNパケットにオレオレオプションを付ける

Linuxを使っていて、ふいに「SYNフラグ付きのTCPパケットに、自分の好きなTCPオプションを埋め込みたい!」と思ってしまった時にどうすればいいかについてまとめます。

なお、当方Linux 2.6.24.3で実験しております。ちなみにIPv4限定です。IPv6は知りません。

前提知識

ソケット

TCPパケットの出力をする為のコードは、Linuxソースコード中の/net/ipv4/tcp_output.cにあります。ここを覗いてみれば色々と書いてあるわけですが、SYNパケットの送信はtcp_connect()に書いてあります。ここから呼ばれるtcp_connect_init()の中で、TCPソケットにTCPヘッダ長を書き込む箇所があるので、まずはこれをどうにかします。

static void tcp_connect_init(struct sock *sk)
{
        struct dst_entry *dst = __sk_dst_get(sk);
        struct tcp_sock *tp = tcp_sk(sk);
        __u8 rcv_wscale;

        /* We'll fix this up when we get a response from the other end.
         * See tcp_input.c:tcp_rcv_state_process case TCP_SYN_SENT.
         */
        tp->tcp_header_len = sizeof(struct tcphdr) +
                (sysctl_tcp_timestamps ? TCPOLEN_TSTAMP_ALIGNED : 0);

このtcp_header_lenの長さを必要な分だけ調節しておきましょう。

送信

tcp_connect()の後半で、tcp_transmit_skb()によってTCPパケットが送信されます。この途中でTCPヘッダーの構築が行われます。ここでもTCPヘッダ長の指定が必要です。今度はソケットではなく、パケットに対する指定になります。

        sysctl_flags = 0;
        if (unlikely(tcb->flags & TCPCB_FLAG_SYN)) {
                tcp_header_size = sizeof(struct tcphdr) + TCPOLEN_MSS;

実際にTCPオプションを生成するのは、この中から呼ばれるtcp_syn_build_options()です。SYN/ACKの送信の時にも呼ばれるので注意が必要です。

なお、TCPヘッダ長さは4の倍数である必要があるので、4の倍数ちょうどにならない時は、NOP(type 0x01)で埋めておきましょう。NOPとEOLは長さのフィールドが要らないので、1バイトずつ埋められます。