2014年1月28日火曜日

somaxconnの上限値は65535

Linuxのネットワークパラメータの一つに、net.core.somaxconnというのがあります。これはlisten(2)の第二引数backlogの上限値となっています。このsomaxconnは一見intに見えますが、実はunsigned shortの範囲の数値しか受け付けません。それを超える数値を入れると黙って切り捨てられます。つまり

  • -1→65535
  • 0→0
  • 65535→65535
  • 65536→0
  • 65537→1

と同じような動作を内部的にします。なので、この値は絶対に0~65535の範囲を超えてはいけません。以下、詳しい説明です。

おことわり: この仕様はLinux 3.11以降変更されており、範囲外の数値を設定できないようになっています。ここに書いてある内容が再現するのは、Linux 3.10以前の古いカーネルのみです。

まずsysctlの定義ですが

net/core/sysctl_net_core.c

static struct ctl_table netns_core_table[] = {
 {
  .procname = "somaxconn",
  .data  = &init_net.core.sysctl_somaxconn,
  .maxlen  = sizeof(int),
  .mode  = 0644,
  .proc_handler = proc_dointvec
 },

include/net/netns/core.h

struct netns_core {
        /* core sysctls */
        struct ctl_table_header *sysctl_hdr;

        int     sysctl_somaxconn;

        struct prot_inuse __percpu *inuse;
};

以上のようにしっかりと"int"と書かれています。

さて、この値がどう使われているかというと、listen(2)でbacklogの最大値として利用された後inet_listenに引き渡されます。

net/ipv4/af_inet.c

int inet_listen(struct socket *sock, int backlog)
{
        struct sock *sk = sock->sk;
        unsigned char old_state;
        int err;
(途中省略)
        sk->sk_max_ack_backlog = backlog;
        err = 0;

out:
        release_sock(sk);
        return err;
}

という感じで、socket.sk_max_ack_backlogに渡されています。この値の宣言を見ると

include/net/sock.h

struct sock {
(途中省略)
        unsigned short          sk_ack_backlog;
        unsigned short          sk_max_ack_backlog;

unsigned shortとなっています。intをunsigned shortにそのまま代入してますね。なので、一番上に書いたようにオーバーフローします。黙ってオーバーフローします。net.core.somaxconnの値は正しくセットされるのが余計にたちが悪いですね。

ところでbacklog=0ってなんでしょうか?全くacceptできないように思えますが、実はキューの長さは

include/net/sock.h

static inline bool sk_acceptq_is_full(const struct sock *sk)
{
        return sk->sk_ack_backlog > sk->sk_max_ack_backlog;
}

という感じで不等号で比較されているので、最低一つはaccept待ちのソケットが使用できます。

これはTCPのコードですが、unix domain socketでも似たような事象が起こります。

あと何年かすれば役に立たなくなる感じの知識ではありますが、今のところsomaxconnは16bitです。

2014年1月22日水曜日

ssh で id_rsa と違うキーの id_rsa.pub が置いてあるとログイン出来ない

sshでログインする際にローカルホストで使用する鍵は.ssh/id_rsaですが、この時このid_rsaと異なる鍵の公開鍵がid_rsa.pubとして置かれていると、そちらを参照してしまいログインできないという状況が発生しました。

再現手順は以下のとおり。

  1. ssh-keygen -t rsa -b 2048 で適当に鍵を作る
  2. mv .ssh/id_rsa .ssh/id_rsa2 としてバックアップ
  3. ssh-keygen -t rsa -b 2048 でもう一つ鍵を作る
  4. mv .ssh/id_rsa2 .ssh/id_rsa として秘密鍵を戻す

これで正しい秘密鍵を見ているはずなのに、ログインできなくなります。デバッグ出力には

debug1: Next authentication method: publickey
debug1: Offering RSA public key: /home/yuryu/.ssh/id_rsa
debug1: Authentications that can continue: publickey,gssapi-keyex,gssapi-with-mic,password
debug1: Trying private key: /home/yuryu/.ssh/id_dsa
debug1: Trying private key: /home/yuryu/.ssh/id_ecdsa
debug1: Next authentication method: password

と出力されています。この「Offering RSA public key」が、.pubを見ているという出力です。 もし.pubが存在しないと

debug1: Next authentication method: publickey
debug1: Trying private key: /home/yuryu/.ssh/id_rsa
debug1: read PEM private key done: type RSA

といったログ出力になります。

この公開鍵がなぜ使われているのかまでは調べていませんが、おそらく公開鍵の計算をサボるために、公開鍵がすでに存在したら読み込むといった感じで使っているのではないかと思います。

ぐぐってもパーミッションの設定を間違えているケースばかりで、このハマり方をしている人はいませんでした。無駄に2時間ぐらい悩みました...

2014年1月15日水曜日

Linux Kernel Updates Vol.2013.12 の Kindle版を出しました

コミケ85で頒布した、Linux Kernel Updates Vol.2013.12 の Kindle版を出しました。

Linux Kernel Updates 2013.12の購入はこちらから!

以下は冊子版のプレビューです

目次

  • What's New in Linux 3.11?
  • What's New in Linux 3.12?
  • TCP/IPチューニング特集

TCP/IPチューニング特集はsysctlで設定できるパラメーターについて詳しく説明しています。紛らわしい設定、間違えやすい設定についてカーネルコードを参照しながら正確な説明を心がけました。

Linux Kernel Updates 2013.12の購入はこちらから!

2014年1月9日木曜日

VMware Fusionの中からPXE Bootする方法

VMware Fusionで仮想マシンを立ち上げた時に、外にあるサーバーからPXE Bootしたいと思いました。ところが普通にやってもfilenameやnext-serverが取れないので、起動できません。いろいろググったところ、下記のようにするのがよさそうでした。

dhcpd.confを編集

/Library/Preferences/VMware Fusion/vmnet8/dhcpd.conf を編集します。vmnet8というのは使っているネットワークのインタフェースになります。設定ファイルをのぞいてみて、IPのレンジが現在使っているものと一致すればいいと思います。私の環境にはvmnet1とvmnet8がありました。

dhcpd.confを開くと

###### VMNET DHCP Configuration. Start of "DO NOT MODIFY SECTION" #####

という行がありますから、その手前

allow booting;
filename "pxelinux.0";
next-server 192.168.123.45;

という3行を追加します。 next-server の右側はPXE用のサーバーのアドレスです。

dhcpdを再起動

一番簡単な方法はOSを再起動することです。もし何らかの理由で再起動できない時は、psすると

root              581   0.0  0.0  2467008    468   ??  Ss    7:36PM   0:00.00 /Applications/VMware Fusion.app/Contents/Library/vmnet-dhcpd -s 7 -cf /Library/Preferences/VMware Fusion/vmnet8/dhcpd.conf -lf /var/db/vmware/vmnet-dhcpd-vmnet8.leases -pf /var/run/vmnet-dhcpd-vmnet8.pid vmnet8

というようなプロセスがいるので、これを kill して同じパラメータで起動しなおせば動くと思います。

まとめ

以上の手順でVMware Fusion上の仮想マシンからPXE Bootすることができました。