[インデックス 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_IP
や syscall.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のエフェメラルポート範囲を制御するための基盤が整いました。
関連リンク
- Go言語のコミット: https://github.com/golang/go/commit/83ac901fb9298bf29059d52bc8b084c885586d1b
- Go CL (Code Review): https://golang.org/cl/80610044
- Go Issue #7541: https://github.com/golang/go/issues/7541
参考にした情報源リンク
- RFC 6335: Internet Assigned Numbers Authority (IANA) Procedures for the Management of the Service Name and Transport Protocol Port Number Registry: https://datatracker.ietf.org/doc/html/rfc6335
- IANA Service Name and Transport Protocol Port Number Registry: https://www.iana.org/assignments/service-names-port-numbers/service-names-port-numbers.xhtml
- DragonFly BSD 公式サイト: https://www.dragonflybsd.org/
setsockopt
man page (一般的な情報): https://man7.org/linux/man-pages/man2/setsockopt.2.html (Linuxの例ですが、概念は共通)