Go

GoのnetパッケージにおけるIPアドレスの内部表現

More than 1 year has passed since last update.


GoのnetパッケージにおけるIPアドレスの内部表現

https://golang.org/src/net/ip.go

GoにおけるIPアドレスの内部表現については↑のファイルの冒頭に定義と解説がある。以下抜粋。


go/src/net/ip.go

// IP address manipulations                                                                                                                                   

//
// IPv4 addresses are 4 bytes; IPv6 addresses are 16 bytes.
// An IPv4 address can be converted to an IPv6 address by
// adding a canonical prefix (10 zeros, 2 0xFFs).
// This library accepts either size of byte slice but always
// returns 16-byte addresses.

package net

// IP address lengths (bytes).
const (
IPv4len = 4
IPv6len = 16
)

// An IP is a single IP address, a slice of bytes.
// Functions in this package accept either 4-byte (IPv4)
// or 16-byte (IPv6) slices as input.
//
// Note that in this documentation, referring to an
// IP address as an IPv4 address or an IPv6 address
// is a semantic property of the address, not just the
// length of the byte slice: a 16-byte slice can still
// be an IPv4 address.
type IP []byte

// An IP mask is an IP address.
type IPMask []byte

// An IPNet represents an IP network.
type IPNet struct {
IP IP // network number
Mask IPMask // network mask
}


GoではIPv4、IPv6アドレス共にIPという型で表現される。IPv4アドレスは4バイト、IPv6アドレスは16バイトだが、内部的にはIPv4アドレスであっても16バイトのバイト列で扱われる。

ip := net.ParseIP("8.8.8.8")

fmt.Println(len(ip)) // 16

net.ParseIP()はIPアドレスの文字列をIP型に変換する関数で、引数はIPv4、IPv6どちらのアドレスでも良い。GoのIPアドレス関連ユーティリティはこんな感じでIPv4とIPv6どちらのアドレスも一緒くたに扱うことができる。

なお、4バイトで表現されたIPv4アドレスが欲しい場合はTo4()関数を使う。

ip := net.ParseIP("8.8.8.8")

ipv4 := ip.To4()
fmt.Printf("%d.%d.%d.%d\n", ipv4[0], ipv4[1], ipv4[2], ipv4[3]) // 8.8.8.8
fmt.Println(len(ipv4)) // 4

また、16バイト表現によるIPv4アドレスはこんな感じで末尾の4バイトに実際の値が格納される。


go/src/net/ip.go

// IPv4 returns the IP address (in 16-byte form) of the                                                                                                       

// IPv4 address a.b.c.d.
func IPv4(a, b, c, d byte) IP {
p := make(IP, IPv6len)
copy(p, v4InV6Prefix)
p[12] = a
p[13] = b
p[14] = c
p[15] = d
return p
}

var v4InV6Prefix = []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff}



CIDRとサブネットマスク

10.0.0.0/24のようなCIDR表記のアドレス表現はIPNet、サブネットマスクはIPMaskで表現される。


go/src/net/ip.go

// An IP mask is an IP address.                                                                                                                               

type IPMask []byte

// An IPNet represents an IP network.
type IPNet struct {
IP IP // network number
Mask IPMask // network mask
}


CIDR表記の文字列をIPNet型に変換するにはnet.ParseCIDR()を使う。

ip, ipnet, err := net.ParseCIDR("10.0.0.0/24")

if err != nil {
// error handling
}
fmt.Println(ipnet.IP) // 10.0.0.0
fmt.Println(ipnet.Mask) // ffffff00

これは例えば特定のIPアドレスが同じネットワークに所属しているかを調べるのに利用出来る。

_, ipnet, err := net.ParseCIDR("10.0.0.0/24")

if err != nil {
// error handling
}
ip1 := net.ParseIP("10.0.0.1")
ip2 := net.ParseIP("10.1.0.1")
fmt.Println(ipnet.Contains(ip1)) // true
fmt.Println(ipnet.Contains(ip2)) // false