hana_shinのLinux技術ブログ

Linuxの技術情報を掲載しています。特にネットワークをメインに掲載していきます。

カーネルパラメータの使い方(ip_local_port_range編)



1 ip_local_port_rangeとは?

プロセスがパケットの到着を待つポート番号は、bindシステムコールを使って指定します。パケットの到着を待つポート番号は、bindシステムコールに指定するポート番号によって、以下のようになります。

bindに指定するポート番号 意味
0を指定した場合 ip_local_port_rangが示す範囲の中から、OSが任意のポート番号を割り当てる
0以外を指定した場合 bindシステムコールで指定したポート番号が割り当てられる

つまり、ip_local_port_rangeは、bindシステムコールに指定するポート番号に0を指定した場合、OSがプロセスに割り当てるポート番号の範囲を示します。

なお、私の環境では、ip_local_port_rangeはデフォルトで以下の設定になっていました。

[root@server ~]# sysctl -n net.ipv4.ip_local_port_range
32768   60999

2 検証環境

CentOS版数は以下のとおりです。

[root@server ~]# cat /etc/redhat-release
CentOS Linux release 7.9.2009 (Core)

カーネル版数は以下のとおりです。

[root@server ~]# uname -r
3.10.0-1160.el7.x86_64

3 事前準備

3.1 テストプログラムの作成

任意のポートでListenするテストプログラムを作成します。テストプログラムの第1引数にポート番号を指定して実行します。

[root@server ~]# cat sv.c
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <stdlib.h>

int main(int argc, char *argv[])
{
    int lfd, cfd, optval=1;
    socklen_t len;
    struct sockaddr_in sv, cl;
    char buf[32];
    ssize_t n;
    u_short port;

    lfd = socket(AF_INET, SOCK_STREAM, 0);

    port = atoi(argv[1]);
    sv.sin_family = AF_INET;
    sv.sin_port = htons(port);
    sv.sin_addr.s_addr = INADDR_ANY;

    setsockopt(lfd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval));
    bind(lfd, (struct sockaddr *)&sv, sizeof(sv));
    listen(lfd, 5);

    len = sizeof(cl);
    cfd = accept(lfd, (struct sockaddr *)&cl, &len);

    while (1) {
        memset(buf, 0, sizeof(buf));
        n = read(cfd, buf, sizeof(buf));
        if(n > 0)
            fprintf(stderr,"%zd bytes received:%s", n, buf);
        else if(n == 0){
            fprintf(stderr,"EOF recieved. I'm going to sleep 300s.\n");
            exit(0);
        }
        else {
            perror("read");
            exit(1);
        }
    }
    close(lfd);
    exit(0);
}

3.2 コンパイル

テストプログラムをコンパイルします。

[root@server ~]# gcc -Wall -o sv sv.c

実行ファイルを確認します。

[root@server ~]# ls -l sv*
-rwxr-xr-x. 1 root root 9064  2月 25 19:44 sv
-rw-r--r--. 1 root root 1116  2月 25 19:36 sv.c

4 実験

4.1 ip_local_port_rangeをデフォルトで使用した場合

ip_local_port_rangeの初期値を確認します。

[root@server ~]# sysctl -n net.ipv4.ip_local_port_range
32768   60999

テストプログラムを実行します。このとき、テストプログラムの第1引数に、パケットの到着を待ち受けるポート番号を明に指定します。

[root@server ~]# ./sv 11111 &
[1] 1651

lsofコマンドを実行します。テストプログラムの引数に指定したポート番号でListenしていることがわかります。なお、lsofコマンドの使い方は、lsofコマンドの使い方 - hana_shinのLinux技術ブログを参照してください。

[root@server ~]# lsof -c sv -a -i -a -nP
COMMAND  PID USER   FD   TYPE DEVICE SIZE/OFF NODE NAME
sv      1651 root    3u  IPv4  32048      0t0  TCP *:11111 (LISTEN)

次に、引数に0を指定してテストプログラムを実行します。

[root@server ~]# ./sv 0 &
[1] 1636

lsofコマンドを実行します。ip_local_port_rangeが示す範囲の中からポート番号が選択されていることがわかります。

[root@server ~]# lsof -c sv -a -i -a -nP
COMMAND  PID USER   FD   TYPE DEVICE SIZE/OFF NODE NAME
sv      1636 root    3u  IPv4  28848      0t0  TCP *:33246 (LISTEN)

引数に0を指定してテストプログラムを実行します。

[root@server ~]# ./sv 0 &
[2] 1639

lsofコマンドを実行します。ip_local_port_rangeが示す範囲の中からポート番号が選択されていることがわかります。

[root@server ~]# lsof -c sv -a -i -a -nP
COMMAND  PID USER   FD   TYPE DEVICE SIZE/OFF NODE NAME
sv      1636 root    3u  IPv4  28848      0t0  TCP *:33246 (LISTEN)
sv      1639 root    3u  IPv4  31522      0t0  TCP *:38166 (LISTEN)

引数に0を指定してテストプログラムを実行します。

[root@server ~]# ./sv 0 &
[3] 1642

lsofコマンドを実行します。ip_local_port_rangeが示す範囲の中からポート番号が選択されていることがわかります。

[root@server ~]# lsof -c sv -a -i -a -nP
COMMAND  PID USER   FD   TYPE DEVICE SIZE/OFF NODE NAME
sv      1636 root    3u  IPv4  28848      0t0  TCP *:33246 (LISTEN)
sv      1639 root    3u  IPv4  31522      0t0  TCP *:38166 (LISTEN)
sv      1642 root    3u  IPv4  31532      0t0  TCP *:34320 (LISTEN)

4.2 ip_local_port_rangeをカスタマイズした場合

ip_local_port_rangeが示す範囲を50001から50003に変更します。

[root@server ~]# sysctl -w net.ipv4.ip_local_port_range="50001 50003"
net.ipv4.ip_local_port_range = 50001 50003

ip_local_port_rangeが示す範囲を確認します。

[root@server ~]# sysctl -n net.ipv4.ip_local_port_range
50001   50003

引数に0を指定して、テストプログラムを実行します。

[root@server ~]# ./sv 0 &
[1] 1689

lsofコマンドを実行します。ip_local_port_rangeが示す範囲の中からポート番号が選択されていることがわかります。

[root@server ~]# lsof -c sv -a -i -a -nP
COMMAND  PID USER   FD   TYPE DEVICE SIZE/OFF NODE NAME
sv      1689 root    3u  IPv4  33904      0t0  TCP *:50002 (LISTEN)

引数に0を指定して、テストプログラムを実行します。

[root@server ~]# ./sv 0 &
[2] 1692

lsofコマンドを実行します。ip_local_port_rangeが示す範囲の中からポート番号が選択されていることがわかります。

[root@server ~]# lsof -c sv -a -i -a -nP
COMMAND  PID USER   FD   TYPE DEVICE SIZE/OFF NODE NAME
sv      1689 root    3u  IPv4  33904      0t0  TCP *:50002 (LISTEN)
sv      1692 root    3u  IPv4  37000      0t0  TCP *:50003 (LISTEN)

引数に0を指定して、テストプログラムを実行します。

[root@server ~]# ./sv 0 &
[3] 1695

lsofコマンドを実行します。ip_local_port_rangeが示す範囲の中からポート番号が選択されていることがわかります。

[root@server ~]# lsof -c sv -a -i -a -nP
COMMAND  PID USER   FD   TYPE DEVICE SIZE/OFF NODE NAME
sv      1689 root    3u  IPv4  33904      0t0  TCP *:50002 (LISTEN)
sv      1692 root    3u  IPv4  37000      0t0  TCP *:50003 (LISTEN)
sv      1695 root    3u  IPv4  28963      0t0  TCP *:50001 (LISTEN)

引数に0を指定して、テストプログラムを実行します。しかし、ip_local_port_rangeが示す範囲のポート番号は全て使われているので、テストプログラムが実行できないことがわかります。

[root@server ~]# ./sv 0 &
[4] 1698
[root@server ~]# read: Bad file descriptor

テストプログラムを終了します。

[root@server ~]# pkill sv
[1]   Terminated              ./sv 0
[2]-  Terminated              ./sv 0
[3]+  Terminated              ./sv 0

Z 参考情報

私が業務や記事執筆で参考にした書籍を以下のページに記載します。
Linux技術のスキルアップをしよう! - hana_shinのLinux技術ブログ