GoのnetパッケージにおけるIPアドレスの内部表現
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