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

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

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

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

```go: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バイトのバイト列で扱われる。

```go
ip := net.ParseIP("8.8.8.8")
fmt.Println(len(ip)) // 16
```

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

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

```go
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: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: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()](https://golang.org/pkg/net/#ParseCIDR)を使う。

```go
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アドレスが同じネットワークに所属しているかを調べるのに利用出来る。

```go
_, 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
```