Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

[インデックス 18988] ファイルの概要

このコミットは、Go言語のネットワークパッケージにおいて、DragonFly BSDオペレーティングシステム上でのエフェメラルポート範囲の挙動を調整するものです。DragonFly BSDのデフォルトのエフェメラルポート範囲がIANAの推奨(RFC 6335)に準拠しておらず、非常に狭いという問題を解決するために、ソケットオプションを設定してより広い範囲を使用するように変更が加えられました。

コミット

commit 83ac901fb9298bf29059d52bc8b084c885586d1b
Author: Mikio Hara <mikioh.mikioh@gmail.com>
Date:   Sat Mar 29 13:04:25 2014 +0900

    net: tweak the ephemeral port range on dragonfly
    
    On DragonFly BSD, we adjust the ephemeral port range because
    unlike other BSD systems its default ephemeral port range
    doesn't conform to IANA recommendation as described in RFC 6355
    and is pretty narrow.
    
    On DragonFly BSD 3.6: default range [1024, 5000], high range [49152, 65535]
    On FreeBSD 10: default range [10000, 65535], high range [49152, 65535]
    On Linux 3.11: default range [32768, 61000]
    
    Fixes #7541.
    
    LGTM=iant
    R=jsing, gobot, iant
    CC=golang-codereviews
    https://golang.org/cl/80610044

GitHub上でのコミットページへのリンク

https://github.com/golang/go/commit/83ac901fb9298bf29059d52bc8b084c885586d1b

元コミット内容

commit 83ac901fb9298bf29059d52bc8b084c885586d1b
Author: Mikio Hara <mikioh.mikioh@gmail.com>
Date:   Sat Mar 29 13:04:25 2014 +0900

    net: tweak the ephemeral port range on dragonfly
    
    On DragonFly BSD, we adjust the ephemeral port range because
    unlike other BSD systems its default ephemeral port range
    doesn't conform to IANA recommendation as described in RFC 6355
    and is pretty narrow.
    
    On DragonFly BSD 3.6: default range [1024, 5000], high range [49152, 65535]
    On FreeBSD 10: default range [10000, 65535], high range [49152, 65535]
    On Linux 3.11: default range [32768, 61000]
    
    Fixes #7541.
    
    LGTM=iant
    R=jsing, gobot, iant
    CC=golang-codereviews
    https://golang.org/cl/80610044

変更の背景

この変更は、DragonFly BSDオペレーティングシステムにおけるエフェメラルポートの割り当てに関する問題に対処するために導入されました。エフェメラルポートとは、クライアントアプリケーションが一時的に使用するポート番号の範囲であり、通常、アウトバウンド接続の際にOSによって自動的に割り当てられます。

コミットメッセージに記載されているように、DragonFly BSD 3.6のデフォルトのエフェメラルポート範囲は [1024, 5000] と非常に狭く設定されていました。これは、他の主要なOS(FreeBSD 10の [10000, 65535] や Linux 3.11の [32768, 61000])と比較しても顕著です。

さらに重要なのは、DragonFly BSDのこのデフォルト範囲が、IANA (Internet Assigned Numbers Authority) がRFC 6335で推奨するエフェメラルポート範囲 [49152, 65535] に準拠していない点です。狭いポート範囲は、特に多数の同時接続を必要とするアプリケーションや、短期間に多くの接続を確立・切断するようなシナリオにおいて、ポート枯渇(Port Exhaustion)の問題を引き起こす可能性があります。ポートが枯渇すると、新しいアウトバウンド接続を確立できなくなり、アプリケーションの機能不全やパフォーマンス低下につながります。

Go言語のネットワークスタックがDragonFly BSD上で安定して動作し、IANAの推奨に沿った挙動をするように、このコミットではGoランタイムがソケット作成時にエフェメラルポート範囲を明示的に「高範囲 (high range)」に設定するよう調整されました。これにより、DragonFly BSD上でもGoアプリケーションがより多くのエフェメラルポートを利用できるようになり、ポート枯渇のリスクが軽減されます。

前提知識の解説

エフェメラルポート (Ephemeral Port)

エフェメラルポートは、TCP/IP通信において、クライアント側が一時的に使用するためにOSによって動的に割り当てられるポート番号です。サーバー側が特定のサービス(例: HTTPの80番、HTTPSの443番)のために固定ポートを使用するのに対し、クライアント側は通常、接続ごとに異なるエフェメラルポートを使用します。これにより、同じクライアントマシンから複数の接続が同時に行われる場合でも、それぞれの接続を一意に識別できます。エフェメラルポートは、接続が終了すると解放され、再利用可能になります。

IANA (Internet Assigned Numbers Authority)

IANAは、IPアドレス、AS番号、ドメイン名、プロトコルポート番号など、インターネットプロトコルに関するグローバルな調整を担当する組織です。IANAは、これらのリソースの一貫性とグローバルな相互運用性を確保するために、さまざまな推奨事項や標準を定めています。ポート番号に関しては、Well-Known Ports (0-1023)、Registered Ports (1024-49151)、そしてDynamic/Private Ports (49152-65535) の3つのカテゴリに分類し、それぞれの使用目的と推奨範囲を定義しています。

RFC 6335

RFC 6335は「Internet Assigned Numbers Authority (IANA) Procedures for the Management of the Service Name and Transport Protocol Port Number Registry」というタイトルで、IANAがサービス名とトランスポートプロトコルポート番号のレジストリを管理するための手順を定義しています。このRFCの中で、エフェメラルポート(Dynamic and/or Private Ports)の推奨範囲が 49152-65535 であると明記されています。この範囲は、Well-Known PortsやRegistered Portsとの衝突を避け、アプリケーションが一時的な通信に十分な数のポートを利用できるようにするために設定されています。

DragonFly BSD

DragonFly BSDは、FreeBSD 4.8からフォークして開発されたオープンソースのUnix系オペレーティングシステムです。高性能とスケーラビリティを目標に、特にSMP (Symmetric Multi-Processing) 環境での効率的な動作に重点を置いています。ネットワークスタックやファイルシステム(HAMMER)など、FreeBSDとは異なる独自の進化を遂げている部分があります。このコミットが示すように、ネットワーク関連のデフォルト設定が他のBSD系OSやLinuxと異なる場合があります。

setsockopt システムコール

setsockopt は、ソケットのオプションを設定するためのシステムコールです。ソケットの挙動を細かく制御するために使用され、例えば、タイムアウト値の設定、バッファサイズの調整、再利用可能なアドレスの設定など、多岐にわたるオプションがあります。このコミットでは、エフェメラルポートの範囲を制御するオプションを設定するために使用されています。

IP_PORTRANGE / IPV6_PORTRANGE ソケットオプション

これらは、IP (IPv4) および IPv6 ソケットに対して、エフェメラルポートの割り当てポリシーを設定するためのソケットオプションです。これらのオプションは、通常、以下のいずれかの値を取ります。

  • IP_PORTRANGE_DEFAULT / IPV6_PORTRANGE_DEFAULT: OSのデフォルトのエフェメラルポート範囲を使用します。
  • IP_PORTRANGE_LOW / IPV6_PORTRANGE_LOW: 予約されたポートに近い、低い範囲のエフェメラルポートを使用します。これは通常、特定のレガシーアプリケーションや特殊な要件がある場合に使用されます。
  • IP_PORTRANGE_HIGH / IPV6_PORTRANGE_HIGH: IANAの推奨する 49152-65535 のような、高い範囲のエフェメラルポートを使用します。これは、ポート枯渇のリスクを減らすために推奨される設定です。

このコミットでは、DragonFly BSDにおいて IP_PORTRANGE_HIGH または IPV6_PORTRANGE_HIGH を設定することで、より広いエフェメラルポート範囲を利用するように強制しています。

技術的詳細

Go言語の標準ライブラリである net パッケージは、OSに依存しない形でネットワーク通信機能を提供しますが、内部的には各OSのシステムコールを呼び出して動作します。このコミットは、そのOS依存部分、特にBSD系のソケットオプション設定を扱う src/pkg/net/sockopt_bsd.go に変更を加えています。

setDefaultSockopts 関数は、新しいソケットが作成される際に、そのソケットにデフォルトのオプションを設定するために呼び出されます。この関数内で、Goランタイムは現在のOSを runtime.GOOS 変数で識別します。

変更の核心は、runtime.GOOS == "dragonfly" という条件分岐が追加された点です。これにより、コードはDragonFly BSD上で実行されている場合にのみ、特定のエフェメラルポート範囲調整ロジックを適用します。この条件分岐の内部では、ソケットのファミリー(IPv4: syscall.AF_INET または IPv6: syscall.AF_INET6)に応じて、適切なソケットオプション (syscall.IP_PORTRANGE または syscall.IPV6_PORTRANGE) を syscall.IP_PORTRANGE_HIGH または syscall.IPV6_PORTRANGE_HIGH に設定しています。

この設定は syscall.SetsockoptInt 関数を通じて行われます。この関数は、指定されたソケットディスクリプタ s に対して、指定されたレベル(例: syscall.IPPROTO_IPsyscall.IPPROTO_IPV6)、オプション名(例: syscall.IP_PORTRANGE)、そして整数値のオプション値(例: syscall.IP_PORTRANGE_HIGH)を設定します。

また、src/pkg/syscall/net_nacl.go ファイルには、これらのソケットオプション定数(IP_PORTRANGE, IP_PORTRANGE_DEFAULT, IP_PORTRANGE_LOW, IP_PORTRANGE_HIGH および対応するIPv6版)が追加されています。これは、Goの syscall パッケージがこれらの定数を認識し、Goプログラムから利用できるようにするためです。net_nacl.go はNaCl (Native Client) 環境向けのsyscall定義を含んでいますが、Goのクロスプラットフォームビルドシステムでは、他のOS固有のsyscall定義と組み合わせて使用されることがあります。この場合、これらの定数はDragonFly BSDのビルドターゲットで利用可能になるように追加されたと考えられます。

この変更により、GoアプリケーションがDragonFly BSD上でネットワーク接続を確立する際、デフォルトでIANA推奨の広いエフェメラルポート範囲が使用されるようになり、ポート枯渇による問題が回避されます。

コアとなるコードの変更箇所

src/pkg/net/sockopt_bsd.go

--- a/src/pkg/net/sockopt_bsd.go
+++ b/src/pkg/net/sockopt_bsd.go
@@ -8,10 +8,23 @@ package net
 
 import (
 	"os"
+	"runtime"
 	"syscall"
 )
 
 func setDefaultSockopts(s, family, sotype int, ipv6only bool) error {
+\tif runtime.GOOS == "dragonfly" && sotype != syscall.SOCK_RAW {
+\t\t// On DragonFly BSD, we adjust the ephemeral port
+\t\t// range because unlike other BSD systems its default
+\t\t// port range doesn't conform to IANA recommendation
+\t\t// as described in RFC 6355 and is pretty narrow.
+\t\tswitch family {
+\t\tcase syscall.AF_INET:
+\t\t\tsyscall.SetsockoptInt(s, syscall.IPPROTO_IP, syscall.IP_PORTRANGE, syscall.IP_PORTRANGE_HIGH)
+\t\tcase syscall.AF_INET6:
+\t\t\tsyscall.SetsockoptInt(s, syscall.IPPROTO_IPV6, syscall.IPV6_PORTRANGE, syscall.IPV6_PORTRANGE_HIGH)
+\t\t}\n+\t}\n \tif family == syscall.AF_INET6 && sotype != syscall.SOCK_RAW {
 \t\t// Allow both IP versions even if the OS default
 \t\t// is otherwise.  Note that some operating systems

src/pkg/syscall/net_nacl.go

--- a/src/pkg/syscall/net_nacl.go
+++ b/src/pkg/syscall/net_nacl.go
@@ -113,9 +113,17 @@ const (
 	SO_KEEPALIVE
 	SO_LINGER
 	SO_ERROR
+	IP_PORTRANGE
+	IP_PORTRANGE_DEFAULT
+	IP_PORTRANGE_LOW
+	IP_PORTRANGE_HIGH
 	IP_MULTICAST_IF
 	IP_MULTICAST_LOOP
 	IP_ADD_MEMBERSHIP
+	IPV6_PORTRANGE
+	IPV6_PORTRANGE_DEFAULT
+	IPV6_PORTRANGE_LOW
+	IPV6_PORTRANGE_HIGH
 	IPV6_MULTICAST_IF
 	IPV6_MULTICAST_LOOP
 	IPV6_JOIN_GROUP

コアとなるコードの解説

src/pkg/net/sockopt_bsd.go の変更

  • import "runtime" の追加: runtime.GOOS を使用して現在のOSを判別するために、runtime パッケージがインポートされました。
  • DragonFly BSD固有のロジック:
    • if runtime.GOOS == "dragonfly" && sotype != syscall.SOCK_RAW という条件が追加されました。これは、コードがDragonFly BSD上で実行されており、かつソケットタイプがRAWソケット(通常のエフェメラルポート割り当ての対象外)でない場合にのみ、以下の処理を実行することを示します。
    • コメントで、DragonFly BSDのデフォルトポート範囲がIANA推奨に準拠しておらず、狭いことが説明されています。
    • switch family ブロック内で、ソケットのファミリー(IPv4またはIPv6)に応じて処理が分岐します。
      • case syscall.AF_INET: IPv4ソケットの場合、syscall.SetsockoptInt(s, syscall.IPPROTO_IP, syscall.IP_PORTRANGE, syscall.IP_PORTRANGE_HIGH) が呼び出されます。これは、ソケット s に対して、IPプロトコルレベルで IP_PORTRANGE オプションを IP_PORTRANGE_HIGH に設定することを意味します。これにより、IPv4のエフェメラルポート範囲がIANA推奨の「高範囲」に設定されます。
      • case syscall.AF_INET6: IPv6ソケットの場合、syscall.SetsockoptInt(s, syscall.IPPROTO_IPV6, syscall.IPV6_PORTRANGE, syscall.IPV6_PORTRANGE_HIGH) が呼び出されます。同様に、IPv6のエフェメラルポート範囲が「高範囲」に設定されます。

この変更により、GoアプリケーションがDragonFly BSD上でTCP/UDPソケットを作成する際、自動的にエフェメラルポートの割り当てがIANA推奨の広い範囲で行われるようになります。

src/pkg/syscall/net_nacl.go の変更

  • 新しい定数の追加:
    • IP_PORTRANGE, IP_PORTRANGE_DEFAULT, IP_PORTRANGE_LOW, IP_PORTRANGE_HIGH
    • IPV6_PORTRANGE, IPV6_PORTRANGE_DEFAULT, IPV6_PORTRANGE_LOW, IPV6_PORTRANGE_HIGH これらの定数は、Goの syscall パッケージがOS固有のソケットオプションを表現するために使用するものです。これらの定数が追加されたことで、Goプログラムはこれらのソケットオプションを型安全な形で参照し、setsockopt システムコールを通じて設定できるようになります。これにより、GoランタイムがDragonFly BSDのエフェメラルポート範囲を制御するための基盤が整いました。

関連リンク

参考にした情報源リンク