2020-07-03 TCP_SAVED_SYNとかTCP_SAVE_SYNを完全に理解した

  • 完全に理解したカーブ的にもうちょいでちょっとできそうなところまできた
  • TCP_SAVED_SYN系はubuntu20.04のカーネル5.4系でユーザランドにそれを扱うヘッダファイルが用意されていないけど、カーネルにはちゃんと実装されているので、カーネルで定義されている各種フラフの値をユーザランドで定義してやれば普通に使える(do_setsockoptとかそのへんみると良い)
    • その上で、サーバ側でsetsockoptによってTCP_SAVE_SYNをセットしてからlistenし、クライアントからのセッションをacceptしたあとに、getsockootでTCP_SAVED_SYNをセットしてデータをとってきたらTCPオプションヘッダが60バイト分持ってこれる
    • 大体サーバ側でこんな感じの実装すればよい
    • たった1度だけとれる(一度とるともう得られない)
  bind(srv, (struct sockaddr *)&srvaddr, sizeof(srvaddr));

  if (setsockopt(srv, IPPROTO_TCP, TCP_SAVE_SYN, &one, sizeof(one)) < 0)
    fail_perror("setsockopt TCP_SAVE_SYN");

  listen(srv, 1);

  cli = accept(srv, (struct sockaddr *)&cliaddr, &cliaddrsize);

  if (getsockopt(cli, IPPROTO_TCP, TCP_SAVED_SYN, syn, &syn_len) != 0)
    fail_perror("first getsockopt TCP_SAVED_SYN failed");

  if (address_family == AF_INET)
    printf("syn_len: %d\n", syn_len); // => 60
  • tcprivの文脈でいくとクライアント側でのカーネル実装だけで良いことがわかった

    • AF_INET6だったら80バイト
      • あとはその生データ(上記コードのsyn)をパースしてやればヘッダの情報を扱える
      • なんで、tcprivでclient側でオプションヘッダにデータを書いておけば、サーバ側ではカーネルでパースしてprocに吐き出すみたいなことをせずに、いい感じユーザランドからデータを取れることがわかった
      • なにこれ最高、googleさんきっと似たようなクライアントプロセスベースの認証をやっている気がするなぁ
    • 完全に満足したので、明日はこれを取り出す実装と、tcprivのリファクタリング(いらない実装が増えた)、いい感じで扱うユーザランドのライブラリでも作っていこうかな
    • なんかparseして見ていると、tcprivのパケット構造がちょいおかしい気がしてきた
  • 共著のゆううきさんにもしっかり共有した


  • 今日はぶつもりの海開きですね