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

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

このコミットは、src/pkg/syscall/netlink_linux.go ファイルに対する変更です。このファイルは、Go言語のsyscallパッケージの一部であり、LinuxシステムにおけるNetlinkソケットとのインタラクションを扱うための低レベルなシステムコールを提供します。具体的には、Netlinkソケットを介してカーネルからルーティング情報ベース(RIB)を取得したり、Netlinkメッセージやルート属性をパースしたりする機能が含まれています。

コミット

syscall: simplify netlink sockets

R=dave, rsc
CC=golang-dev
https://golang.org/cl/7039044

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

https://github.com/golang/go/commit/a0509d85104f8e4397a1d882524278c24221b58d

元コミット内容

commit a0509d85104f8e4397a1d882524278c24221b58d
Author: Mikio Hara <mikioh.mikioh@gmail.com>
Date:   Mon Jan 14 19:29:03 2013 +0900

    syscall: simplify netlink sockets

    R=dave, rsc
    CC=golang-dev
    https://golang.org/cl/7039044
---
 src/pkg/syscall/netlink_linux.go | 170 +++++++++++++++------------------------
 1 file changed, 66 insertions(+), 104 deletions(-)

diff --git a/src/pkg/syscall/netlink_linux.go b/src/pkg/syscall/netlink_linux.go
index d535713069..49550ea2f0 100644
--- a/src/pkg/syscall/netlink_linux.go
+++ b/src/pkg/syscall/netlink_linux.go
@@ -6,9 +6,7 @@

 package syscall

-import (
-	"unsafe"
-)
+import "unsafe"

 // Round the length of a netlink message up to align it properly.
 func nlmAlignOf(msglen int) int {
@@ -21,8 +19,8 @@ func rtaAlignOf(attrlen int) int {
 	return (attrlen + RTA_ALIGNTO - 1) & ^(RTA_ALIGNTO - 1)
 }

-// NetlinkRouteRequest represents the request message to receive
-// routing and link states from the kernel.
+// NetlinkRouteRequest represents a request message to receive routing
+// and link states from the kernel.
 type NetlinkRouteRequest struct {
 	Header NlMsghdr
 	Data   RtGenmsg
@@ -49,167 +47,131 @@ func newNetlinkRouteRequest(proto, seq, family int) []byte {
 	return rr.toWireFormat()
 }

-// NetlinkRIB returns routing information base, as known as RIB,\n-// which consists of network facility information, states and\n-// parameters.\n+// NetlinkRIB returns routing information base, as known as RIB, which\n+// consists of network facility information, states and parameters.\n func NetlinkRIB(proto, family int) ([]byte, error) {
-\tvar (\n-\t\tlsanl SockaddrNetlink\n-\t\ttab   []byte\n-\t)\n-\n-\ts, e := Socket(AF_NETLINK, SOCK_RAW, 0)\n-\tif e != nil {\n-\t\treturn nil, e\n+\ts, err := Socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE)\n+\tif err != nil {\n+\t\treturn nil, err\n \t}\n \tdefer Close(s)\n-\n-\tlsanl.Family = AF_NETLINK\n-\te = Bind(s, &lsanl)\n-\tif e != nil {\n-\t\treturn nil, e\n+\tlsa := &SockaddrNetlink{Family: AF_NETLINK}\n+\tif err := Bind(s, lsa); err != nil {\n+\t\treturn nil, err\n \t}\n-\n-\tseq := 1\n-\twb := newNetlinkRouteRequest(proto, seq, family)\n-\te = Sendto(s, wb, 0, &lsanl)\n-\tif e != nil {\n-\t\treturn nil, e\n+\twb := newNetlinkRouteRequest(proto, 1, family)\n+\tif err := Sendto(s, wb, 0, lsa); err != nil {\n+\t\treturn nil, err\n \t}\n-\n+\tvar tab []byte\n+done:\n \tfor {\n-\t\tvar (\n-\t\t\trb  []byte\n-\t\t\tnr  int\n-\t\t\tlsa Sockaddr\n-\t\t)\n-\n-\t\trb = make([]byte, Getpagesize())\n-\t\tnr, _, e = Recvfrom(s, rb, 0)\n-\t\tif e != nil {\n-\t\t\treturn nil, e\n+\t\trb := make([]byte, Getpagesize())\n+\t\tnr, _, err := Recvfrom(s, rb, 0)\n+\t\tif err != nil {\n+\t\t\treturn nil, err\n \t\t}\n \t\tif nr < NLMSG_HDRLEN {\n \t\t\treturn nil, EINVAL\n \t\t}\n \t\trb = rb[:nr]\n \t\ttab = append(tab, rb...)\n-\n-\t\tmsgs, _ := ParseNetlinkMessage(rb)\n+\t\tmsgs, err := ParseNetlinkMessage(rb)\n+\t\tif err != nil {\n+\t\t\treturn nil, err\n+\t\t}\n \t\tfor _, m := range msgs {\n-\t\t\tif lsa, e = Getsockname(s); e != nil {\n-\t\t\t\treturn nil, e\n+\t\t\tlsa, err := Getsockname(s)\n+\t\t\tif err != nil {\n+\t\t\t\treturn nil, err\n \t\t\t}\n \t\t\tswitch v := lsa.(type) {\n \t\t\tcase *SockaddrNetlink:\n-\t\t\t\tif m.Header.Seq != uint32(seq) || m.Header.Pid != v.Pid {\n+\t\t\t\tif m.Header.Seq != 1 || m.Header.Pid != v.Pid {\n \t\t\t\t\treturn nil, EINVAL\n \t\t\t\t}\n \t\t\tdefault:\n \t\t\t\treturn nil, EINVAL\n \t\t\t}\n \t\t\tif m.Header.Type == NLMSG_DONE {\n-\t\t\t\tgoto done\n+\t\t\t\tbreak done\n \t\t\t}\n \t\t\tif m.Header.Type == NLMSG_ERROR {\n \t\t\t\treturn nil, EINVAL\n \t\t\t}\n \t\t}\n \t}\n-\n-done:\n \treturn tab, nil\n }\n \n-// NetlinkMessage represents the netlink message.\n+// NetlinkMessage represents a netlink message.\n type NetlinkMessage struct {\n 	Header NlMsghdr\n 	Data   []byte\n }\n \n-// ParseNetlinkMessage parses buf as netlink messages and returns\n-// the slice containing the NetlinkMessage structs.\n-func ParseNetlinkMessage(buf []byte) ([]NetlinkMessage, error) {\n-\tvar (\n-\t\th    *NlMsghdr\n-\t\tdbuf []byte\n-\t\tdlen int\n-\t\te    error\n-\t\tmsgs []NetlinkMessage\n-\t)\n-\n-\tfor len(buf) >= NLMSG_HDRLEN {\n-\t\th, dbuf, dlen, e = netlinkMessageHeaderAndData(buf)\n-\t\tif e != nil {\n-\t\t\tbreak\n+// ParseNetlinkMessage parses b as an array of netlink messages and\n+// returns the slice containing the NetlinkMessage structures.\n+func ParseNetlinkMessage(b []byte) ([]NetlinkMessage, error) {\n+\tvar msgs []NetlinkMessage\n+\tfor len(b) >= NLMSG_HDRLEN {\n+\t\th, dbuf, dlen, err := netlinkMessageHeaderAndData(b)\n+\t\tif err != nil {\n+\t\t\treturn nil, err\n \t\t}\n-\t\tm := NetlinkMessage{}\n-\t\tm.Header = *h\n-\t\tm.Data = dbuf[:int(h.Len)-NLMSG_HDRLEN]\n+\t\tm := NetlinkMessage{Header: *h, Data: dbuf[:int(h.Len)-NLMSG_HDRLEN]}\n \t\tmsgs = append(msgs, m)\n-\t\tbuf = buf[dlen:]\n+\t\tb = b[dlen:]\n \t}\n-\n-\treturn msgs, e\n+\treturn msgs, nil\n }\n \n-func netlinkMessageHeaderAndData(buf []byte) (*NlMsghdr, []byte, int, error) {\n-\th := (*NlMsghdr)(unsafe.Pointer(&buf[0]))\n-\tif int(h.Len) < NLMSG_HDRLEN || int(h.Len) > len(buf) {\n+func netlinkMessageHeaderAndData(b []byte) (*NlMsghdr, []byte, int, error) {\n+\th := (*NlMsghdr)(unsafe.Pointer(&b[0]))\n+\tif int(h.Len) < NLMSG_HDRLEN || int(h.Len) > len(b) {\n \t\treturn nil, nil, 0, EINVAL\n \t}\n-\treturn h, buf[NLMSG_HDRLEN:], nlmAlignOf(int(h.Len)), nil\n+\treturn h, b[NLMSG_HDRLEN:], nlmAlignOf(int(h.Len)), nil\n }\n \n-// NetlinkRouteAttr represents the netlink route attribute.\n+// NetlinkRouteAttr represents a netlink route attribute.\n type NetlinkRouteAttr struct {\n \tAttr  RtAttr\n \tValue []byte\n }\n \n-// ParseNetlinkRouteAttr parses msg\'s payload as netlink route\n-// attributes and returns the slice containing the NetlinkRouteAttr\n-// structs.\n-func ParseNetlinkRouteAttr(msg *NetlinkMessage) ([]NetlinkRouteAttr, error) {\n-\tvar (\n-\t\tbuf   []byte\n-\t\ta     *RtAttr\n-\t\talen  int\n-\t\tvbuf  []byte\n-\t\te     error\n-\t\tattrs []NetlinkRouteAttr\n-\t)\n-\n-\tswitch msg.Header.Type {\n+// ParseNetlinkRouteAttr parses m\'s payload as an array of netlink\n+// route attributes and returns the slice containing the\n+// NetlinkRouteAttr structures.\n+func ParseNetlinkRouteAttr(m *NetlinkMessage) ([]NetlinkRouteAttr, error) {\n+\tvar b []byte\n+\tswitch m.Header.Type {\n \tcase RTM_NEWLINK, RTM_DELLINK:\n-\t\tbuf = msg.Data[SizeofIfInfomsg:]\n+\t\tb = m.Data[SizeofIfInfomsg:]\n \tcase RTM_NEWADDR, RTM_DELADDR:\n-\t\tbuf = msg.Data[SizeofIfAddrmsg:]\n+\t\tb = m.Data[SizeofIfAddrmsg:]\n \tcase RTM_NEWROUTE, RTM_DELROUTE:\n-\t\tbuf = msg.Data[SizeofRtMsg:]\n+\t\tb = m.Data[SizeofRtMsg:]\n \tdefault:\n \t\treturn nil, EINVAL\n \t}\n-\n-\tfor len(buf) >= SizeofRtAttr {\n-\t\ta, vbuf, alen, e = netlinkRouteAttrAndValue(buf)\n-\t\tif e != nil {\n-\t\t\tbreak\n+\tvar attrs []NetlinkRouteAttr\n+\tfor len(b) >= SizeofRtAttr {\n+\t\ta, vbuf, alen, err := netlinkRouteAttrAndValue(b)\n+\t\tif err != nil {\n+\t\t\treturn nil, err\n \t\t}\n-\t\tra := NetlinkRouteAttr{}\n-\t\tra.Attr = *a\n-\t\tra.Value = vbuf[:int(a.Len)-SizeofRtAttr]\n+\t\tra := NetlinkRouteAttr{Attr: *a, Value: vbuf[:int(a.Len)-SizeofRtAttr]}\n \t\tattrs = append(attrs, ra)\n-\t\tbuf = buf[alen:]\n+\t\tb = b[alen:]\n \t}\n-\n \treturn attrs, nil\n }\n \n-func netlinkRouteAttrAndValue(buf []byte) (*RtAttr, []byte, int, error) {\n-\th := (*RtAttr)(unsafe.Pointer(&buf[0]))\n-\tif int(h.Len) < SizeofRtAttr || int(h.Len) > len(buf) {\n+func netlinkRouteAttrAndValue(b []byte) (*RtAttr, []byte, int, error) {\n+\ta := (*RtAttr)(unsafe.Pointer(&b[0]))\n+\tif int(a.Len) < SizeofRtAttr || int(a.Len) > len(b) {\n \t\treturn nil, nil, 0, EINVAL\n \t}\n-\treturn h, buf[SizeofRtAttr:], rtaAlignOf(int(h.Len)), nil\n+\treturn a, b[SizeofRtAttr:], rtaAlignOf(int(a.Len)), nil\n }\n```

## 変更の背景

このコミットの主な目的は、`src/pkg/syscall/netlink_linux.go` ファイル内のNetlinkソケット関連コードを「簡素化」することです。Go言語の初期段階では、コードのイディオムやベストプラクティスがまだ確立途上にありました。このコミットは、冗長な変数宣言の削減、エラーハンドリングの改善、そして特に`goto`ステートメントの使用を避けることで、コードの可読性、保守性、および堅牢性を向上させることを目指しています。

`goto`ステートメントは、プログラムの制御フローを任意のラベルにジャンプさせる機能であり、複雑なロジックやエラー処理で使われることがありますが、過度な使用は「スパゲッティコード」につながり、コードの追跡やデバッグを困難にする可能性があります。Go言語では、`defer`、`for-range`、`break`、`continue`などの構造化された制御フローを推奨しており、`goto`の使用は極力避けるべきとされています。このコミットは、このようなGo言語のイディオムに沿った改善の一環と見られます。

## 前提知識の解説

### Netlink Sockets

Netlinkは、Linuxカーネルとユーザー空間のプロセス間で通信を行うためのソケットファミリーです。TCP/IPソケット(AF_INETなど)がネットワーク通信に使用されるのに対し、Netlinkソケット(AF_NETLINK)は、主にカーネルのネットワーク関連機能(ルーティングテーブル、ネットワークインターフェース、ファイアウォールルールなど)の設定や情報の取得に使用されます。

Netlinkは、以下のような特徴を持ちます。
*   **非同期通信**: カーネルからのイベント通知(例: ネットワークインターフェースの状態変化)をユーザー空間で受け取ることができます。
*   **メッセージベース**: 固定長のヘッダと可変長のペイロードを持つNetlinkメッセージを交換します。
*   **複数のプロトコル**: ルーティング(NETLINK_ROUTE)、ファイアウォール(NETLINK_FIREWALL)、ソケット統計(NETLINK_SOCK_DIAG)など、様々なサブプロトコルが存在します。

### Go言語の`syscall`パッケージ

Go言語の`syscall`パッケージは、オペレーティングシステムが提供する低レベルなシステムコールへのアクセスを提供します。これにより、Goプログラムから直接OSの機能(ファイル操作、プロセス管理、ネットワーク通信など)を呼び出すことができます。`netlink_linux.go`ファイルは、Linux固有のNetlinkシステムコールをGoから利用するためのラッパー関数やデータ構造を定義しています。

### Go言語のエラーハンドリング

Go言語では、エラーハンドリングに例外機構(try-catchなど)を使用せず、関数の戻り値としてエラーを返すイディオムが採用されています。通常、関数は最後の戻り値として`error`型の値を返します。エラーが発生しなかった場合は`nil`を返し、エラーが発生した場合は`nil`ではない`error`オブジェクトを返します。呼び出し元は`if err != nil`という形式でエラーをチェックし、適切に処理します。

### `goto`ステートメントと`break`ラベル

*   **`goto`ステートメント**: `goto`は、プログラムの実行フローをコード内の指定されたラベルに無条件にジャンプさせる制御構造です。C言語などではよく使われますが、コードの可読性を損ない、デバッグを困難にする可能性があるため、現代のプログラミングではその使用は推奨されません。
*   **`break`ラベル**: Go言語では、`for`ループや`switch`、`select`ステートメントから抜け出すために`break`を使用します。ネストされたループから一度に抜け出す必要がある場合、`break`にラベルを付与することで、特定のラベルを持つ外側のループから直接抜け出すことができます。これは`goto`の代替として、より構造化された方法で制御フローを管理するために使用されます。

## 技術的詳細

このコミットは、主に以下の3つの関数におけるコードの簡素化と改善を行っています。

1.  **`NetlinkRIB`関数**:
    *   **冗長な変数宣言の削減**: 以前は`var ( lsanal SockaddrNetlink tab []byte )`のように複数の変数をまとめて宣言していましたが、変更後は必要な場所で`var tab []byte`のように個別に宣言するか、`s, err := Socket(...)`のように短い変数宣言(`:=`)を積極的に使用しています。これにより、変数のスコープが適切になり、コードが読みやすくなります。
    *   **エラーハンドリングの統一**: 以前は`s, e := Socket(...)`のようにエラー変数を`e`としていましたが、Goの慣習に従い`err`に統一されています。
    *   **`goto done`から`break done`への変更**: `NetlinkRIB`関数内のループで、`NLMSG_DONE`メッセージを受信した際にループを終了するために`goto done`が使用されていました。この変更では、`done:`というラベルを`for`ループの直前に配置し、`break done`を使用することで、構造化された方法でループを終了するように修正されています。これは、`goto`の使用を避けるというGoのイディオムに沿った改善です。
    *   **`SockaddrNetlink`の初期化の簡素化**: `lsanl.Family = AF_NETLINK`と個別に設定していた部分が、`lsa := &SockaddrNetlink{Family: AF_NETLINK}`のように構造体リテラルで直接初期化されるようになりました。

2.  **`ParseNetlinkMessage`関数**:
    *   **冗長な変数宣言の削減**: 以前は`var ( h *NlMsghdr dbuf []byte dlen int e error msgs []NetlinkMessage )`のように多くの変数を事前に宣言していましたが、変更後は`var msgs []NetlinkMessage`のみを宣言し、他の変数はループ内で`h, dbuf, dlen, err := netlinkMessageHeaderAndData(b)`のように短い変数宣言で初期化されています。
    *   **エラーハンドリングの改善**: ループ内でエラーが発生した場合、以前は`break`していましたが、変更後は`return nil, err`とすることで、エラーを即座に呼び出し元に伝播させるようになりました。これにより、エラー処理がより明確になります。
    *   **引数名の変更**: `buf`から`b`へ、より簡潔な引数名に変更されています。
    *   **`NetlinkMessage`の初期化の簡素化**: `m := NetlinkMessage{}; m.Header = *h; m.Data = dbuf[:int(h.Len)-NLMSG_HDRLEN]`と複数行に分かれていた初期化が、`m := NetlinkMessage{Header: *h, Data: dbuf[:int(h.Len)-NLMSG_HDRLEN]}`のように一行で構造体リテラルを使って初期化されるようになりました。

3.  **`ParseNetlinkRouteAttr`関数**:
    *   **冗長な変数宣言の削減**: `ParseNetlinkMessage`と同様に、不要な変数宣言が削除され、必要な場所で短い変数宣言が使用されています。
    *   **エラーハンドリングの改善**: ループ内でエラーが発生した場合の処理が`return nil, err`に統一され、エラーの即時伝播が図られています。
    *   **引数名の変更**: `msg`から`m`へ、`buf`から`b`へ、より簡潔な引数名に変更されています。
    *   **`NetlinkRouteAttr`の初期化の簡素化**: `ra := NetlinkRouteAttr{}; ra.Attr = *a; ra.Value = vbuf[:int(a.Len)-SizeofRtAttr]`と複数行に分かれていた初期化が、`ra := NetlinkRouteAttr{Attr: *a, Value: vbuf[:int(a.Len)-SizeofRtAttr]}`のように一行で構造体リテラルを使って初期化されるようになりました。

全体として、このコミットはGo言語のイディオムに沿ったコードのクリーンアップと改善であり、コードの品質と保守性を高めることに貢献しています。

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

### `src/pkg/syscall/netlink_linux.go`

#### `NetlinkRIB`関数

```diff
--- a/src/pkg/syscall/netlink_linux.go
+++ b/src/pkg/syscall/netlink_linux.go
@@ -49,167 +47,131 @@ func newNetlinkRouteRequest(proto, seq, family int) []byte {
 	return rr.toWireFormat()
 }

-// NetlinkRIB returns routing information base, as known as RIB,\n-// which consists of network facility information, states and\n-// parameters.\n+// NetlinkRIB returns routing information base, as known as RIB, which\n+// consists of network facility information, states and parameters.\n func NetlinkRIB(proto, family int) ([]byte, error) {
-\tvar (\n-\t\tlsanl SockaddrNetlink\n-\t\ttab   []byte\n-\t)\n-\n-\ts, e := Socket(AF_NETLINK, SOCK_RAW, 0)\n-\tif e != nil {\n-\t\treturn nil, e\n+\ts, err := Socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE)\n+\tif err != nil {\n+\t\treturn nil, err\n \t}\n \tdefer Close(s)\n-\n-\tlsanl.Family = AF_NETLINK\n-\te = Bind(s, &lsanl)\n-\tif e != nil {\n-\t\treturn nil, e\n+\tlsa := &SockaddrNetlink{Family: AF_NETLINK}\n+\tif err := Bind(s, lsa); err != nil {\n+\t\treturn nil, err\n \t}\n-\n-\tseq := 1\n-\twb := newNetlinkRouteRequest(proto, seq, family)\n-\te = Sendto(s, wb, 0, &lsanl)\n-\tif e != nil {\n-\t\treturn nil, e\n+\twb := newNetlinkRouteRequest(proto, 1, family)\n+\tif err := Sendto(s, wb, 0, lsa); err != nil {\n+\t\treturn nil, err\n \t}\n-\n+\tvar tab []byte\n+done:\n \tfor {\n-\t\tvar (\n-\t\t\trb  []byte\n-\t\t\tnr  int\n-\t\t\tlsa Sockaddr\n-\t\t)\n-\n-\t\trb = make([]byte, Getpagesize())\n-\t\tnr, _, e = Recvfrom(s, rb, 0)\n-\t\tif e != nil {\n-\t\t\treturn nil, e\n+\t\trb := make([]byte, Getpagesize())\n+\t\tnr, _, err := Recvfrom(s, rb, 0)\n+\t\tif err != nil {\n+\t\t\treturn nil, err\n \t\t}\n \t\tif nr < NLMSG_HDRLEN {\n \t\t\treturn nil, EINVAL\n \t\t}\n \t\trb = rb[:nr]\n \t\ttab = append(tab, rb...)\n-\n-\t\tmsgs, _ := ParseNetlinkMessage(rb)\n+\t\tmsgs, err := ParseNetlinkMessage(rb)\n+\t\tif err != nil {\n+\t\t\treturn nil, err\n+\t\t}\n \t\tfor _, m := range msgs {\n-\t\t\tif lsa, e = Getsockname(s); e != nil {\n-\t\t\t\treturn nil, e\n+\t\t\tlsa, err := Getsockname(s)\n+\t\t\tif err != nil {\n+\t\t\t\treturn nil, err\n \t\t\t}\n \t\t\tswitch v := lsa.(type) {\n \t\t\tcase *SockaddrNetlink:\n-\t\t\t\tif m.Header.Seq != uint32(seq) || m.Header.Pid != v.Pid {\n+\t\t\t\tif m.Header.Seq != 1 || m.Header.Pid != v.Pid {\n \t\t\t\t\treturn nil, EINVAL\n \t\t\t\t}\n \t\t\tdefault:\n \t\t\t\treturn nil, EINVAL\n \t\t\t}\n \t\t\tif m.Header.Type == NLMSG_DONE {\n-\t\t\t\tgoto done\n+\t\t\t\tbreak done\n \t\t\t}\n \t\t\tif m.Header.Type == NLMSG_ERROR {\n \t\t\t\treturn nil, EINVAL\n \t\t\t}\n \t\t}\n \t}\n-\n-done:\n \treturn tab, nil\n }

ParseNetlinkMessage関数

--- a/src/pkg/syscall/netlink_linux.go
+++ b/src/pkg/syscall/netlink_linux.go
@@ -119,30 +81,22 @@
 	Data   []byte
 }

-// ParseNetlinkMessage parses buf as netlink messages and returns
-// the slice containing the NetlinkMessage structs.\n-func ParseNetlinkMessage(buf []byte) ([]NetlinkMessage, error) {\n-\tvar (\n-\t\th    *NlMsghdr\n-\t\tdbuf []byte\n-\t\tdlen int\n-\t\te    error\n-\t\tmsgs []NetlinkMessage\n-\t)\n-\n-\tfor len(buf) >= NLMSG_HDRLEN {\n-\t\th, dbuf, dlen, e = netlinkMessageHeaderAndData(buf)\n-\t\tif e != nil {\n-\t\t\tbreak\n+// ParseNetlinkMessage parses b as an array of netlink messages and\n+// returns the slice containing the NetlinkMessage structures.\n+func ParseNetlinkMessage(b []byte) ([]NetlinkMessage, error) {\n+\tvar msgs []NetlinkMessage\n+\tfor len(b) >= NLMSG_HDRLEN {\n+\t\th, dbuf, dlen, err := netlinkMessageHeaderAndData(b)\n+\t\tif err != nil {\n+\t\t\treturn nil, err\n \t\t}\n-\t\tm := NetlinkMessage{}\n-\t\tm.Header = *h\n-\t\tm.Data = dbuf[:int(h.Len)-NLMSG_HDRLEN]\n+\t\tm := NetlinkMessage{Header: *h, Data: dbuf[:int(h.Len)-NLMSG_HDRLEN]}\n \t\tmsgs = append(msgs, m)\n-\t\tbuf = buf[dlen:]\n+\t\tb = b[dlen:]\n \t}\n-\n-\treturn msgs, e\n+\treturn msgs, nil
 }

-func netlinkMessageHeaderAndData(buf []byte) (*NlMsghdr, []byte, int, error) {\n-\th := (*NlMsghdr)(unsafe.Pointer(&buf[0]))\n-\tif int(h.Len) < NLMSG_HDRLEN || int(h.Len) > len(buf) {\n+func netlinkMessageHeaderAndData(b []byte) (*NlMsghdr, []byte, int, error) {\n+\th := (*NlMsghdr)(unsafe.Pointer(&b[0]))\n+\tif int(h.Len) < NLMSG_HDRLEN || int(h.Len) > len(b) {\n \t\treturn nil, nil, 0, EINVAL\n \t}\n-\treturn h, buf[NLMSG_HDRLEN:], nlmAlignOf(int(h.Len)), nil\n+\treturn h, b[NLMSG_HDRLEN:], nlmAlignOf(int(h.Len)), nil
 }

ParseNetlinkRouteAttr関数

--- a/src/pkg/syscall/netlink_linux.go
+++ b/src/pkg/syscall/netlink_linux.go
@@ -149,49 +123,37 @@
 	Value []byte
 }

-// ParseNetlinkRouteAttr parses msg\'s payload as netlink route\n-// attributes and returns the slice containing the NetlinkRouteAttr\n-// structs.\n-func ParseNetlinkRouteAttr(msg *NetlinkMessage) ([]NetlinkRouteAttr, error) {\n-\tvar (\n-\t\tbuf   []byte\n-\t\ta     *RtAttr\n-\t\talen  int\n-\t\tvbuf  []byte\n-\t\te     error\n-\t\tattrs []NetlinkRouteAttr\n-\t)\n-\n-\tswitch msg.Header.Type {\n+// ParseNetlinkRouteAttr parses m\'s payload as an array of netlink\n+// route attributes and returns the slice containing the\n+// NetlinkRouteAttr structures.\n+func ParseNetlinkRouteAttr(m *NetlinkMessage) ([]NetlinkRouteAttr, error) {\n+\tvar b []byte\n+\tswitch m.Header.Type {\n \tcase RTM_NEWLINK, RTM_DELLINK:\n-\t\tbuf = msg.Data[SizeofIfInfomsg:]\n+\t\tb = m.Data[SizeofIfInfomsg:]\n \tcase RTM_NEWADDR, RTM_DELADDR:\n-\t\tbuf = msg.Data[SizeofIfAddrmsg:]\n+\t\tb = m.Data[SizeofIfAddrmsg:]\n \tcase RTM_NEWROUTE, RTM_DELROUTE:\n-\t\tbuf = msg.Data[SizeofRtMsg:]\n+\t\tb = m.Data[SizeofRtMsg:]\n \tdefault:\n \t\treturn nil, EINVAL\n \t}\n-\n-\tfor len(buf) >= SizeofRtAttr {\n-\t\ta, vbuf, alen, e = netlinkRouteAttrAndValue(buf)\n-\t\tif e != nil {\n-\t\t\tbreak\n+\tvar attrs []NetlinkRouteAttr\n+\tfor len(b) >= SizeofRtAttr {\n+\t\ta, vbuf, alen, err := netlinkRouteAttrAndValue(b)\n+\t\tif err != nil {\n+\t\t\treturn nil, err\n \t\t}\n-\t\tra := NetlinkRouteAttr{}\n-\t\tra.Attr = *a\n-\t\tra.Value = vbuf[:int(a.Len)-SizeofRtAttr]\n+\t\tra := NetlinkRouteAttr{Attr: *a, Value: vbuf[:int(a.Len)-SizeofRtAttr]}\n \t\tattrs = append(attrs, ra)\n-\t\tbuf = buf[alen:]\n+\t\tb = b[alen:]\n \t}\n-\n \treturn attrs, nil
 }

-func netlinkRouteAttrAndValue(buf []byte) (*RtAttr, []byte, int, error) {\n-\th := (*RtAttr)(unsafe.Pointer(&buf[0]))\n-\tif int(h.Len) < SizeofRtAttr || int(h.Len) > len(buf) {\n+func netlinkRouteAttrAndValue(b []byte) (*RtAttr, []byte, int, error) {\n+\ta := (*RtAttr)(unsafe.Pointer(&b[0]))\n+\tif int(a.Len) < SizeofRtAttr || int(a.Len) > len(b) {\n \t\treturn nil, nil, 0, EINVAL\n \t}\n-\treturn h, buf[SizeofRtAttr:], rtaAlignOf(int(h.Len)), nil\n+\treturn a, b[SizeofRtAttr:], rtaAlignOf(int(a.Len)), nil
 }

コアとなるコードの解説

このコミットにおける主要な変更は、Go言語のコードスタイルとエラーハンドリングのベストプラクティスに沿ったものです。

  1. 変数宣言の簡素化とスコープの最適化:

    • 変更前は、関数の冒頭で複数の変数をまとめて宣言し、その後の処理で値を代入していました。これはC言語などのスタイルに似ていますが、Goでは変数を初めて使用する場所で:=演算子を使って宣言と初期化を同時に行うことが推奨されます。これにより、変数のスコープが最小限に抑えられ、コードの可読性が向上します。
    • 例: var ( lsanal SockaddrNetlink tab []byte ) が削除され、var tab []byteは残るものの、s, err := Socket(...)のように必要な場所で宣言されています。
  2. エラーハンドリングの統一と即時伝播:

    • Goのエラーハンドリングの慣習として、エラー変数はerrという名前を使用し、エラーが発生した場合は即座にreturn nil, errのように呼び出し元にエラーを伝播させることが一般的です。変更前はエラー変数がeであったり、エラー時にbreakしてループを抜けるだけの場合がありましたが、変更後はerrに統一され、エラー発生時には適切なエラー値を返して関数を終了するようになりました。これにより、エラーパスが明確になり、デバッグが容易になります。
  3. gotoからbreakラベルへの移行:

    • NetlinkRIB関数では、特定の条件(NLMSG_DONEメッセージの受信)でループを終了するためにgoto doneが使用されていました。gotoはコードのジャンプ先が分かりにくくなり、複雑な制御フローを生み出す可能性があるため、Goでは推奨されません。
    • この変更では、forループの直前にdone:というラベルを定義し、break doneを使用することで、外側のループを明示的に終了させています。これはgotoの代替として、より構造的で理解しやすい制御フローを実現します。
  4. 構造体リテラルによる初期化の活用:

    • NetlinkMessageNetlinkRouteAttrのインスタンスを生成する際、変更前はm := NetlinkMessage{}; m.Header = *h; m.Data = ...のように複数行に分けてフィールドを初期化していました。
    • 変更後は、m := NetlinkMessage{Header: *h, Data: dbuf[:int(h.Len)-NLMSG_HDRLEN]}のように、構造体リテラルを使って一行で全てのフィールドを初期化しています。これにより、コードがより簡潔になり、オブジェクトの初期状態が一目でわかるようになります。

これらの変更は、機能的な変更ではなく、コードの品質、可読性、保守性を向上させるためのリファクタリングです。Go言語の進化とともに、より良いコーディングスタイルが確立されていく過程を示しています。

関連リンク

参考にした情報源リンク

  • Go言語の公式ドキュメント
  • Go言語のコーディング規約やイディオムに関する一般的な情報源(例: Effective Go)
  • LinuxカーネルのNetlinkに関するドキュメント