- 完全に理解したカーブ的にもうちょいでちょっとできそうなところまできた
- 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のパケット構造がちょいおかしい気がしてきた
- AF_INET6だったら80バイト
共著のゆううきさんにもしっかり共有した
- 今日はぶつもりの海開きですね