|
| 1 | +# iid |
| 2 | +[](http://godoc.org/github.com/c-robinson/iplib/iid) |
| 3 | +[](https://circleci.com/gh/c-robinson/iplib/tree/master) |
| 4 | +[](https://goreportcard.com/report/github.com/c-robinson/iplib) |
| 5 | +[](https://coveralls.io/github/c-robinson/iplib?branch=master) |
| 6 | + |
| 7 | +This package implements functions for generating and validating IPv6 Interface |
| 8 | +Identifiers (IID's) for use in link-local, global unicast and Stateless Address |
| 9 | +Autoconfiguration (SLAAC). For the purposes of this module an IID is an IPv6 |
| 10 | +address constructed, somehow, from information which uniquely identifies a |
| 11 | +given interface on a network, and is unique _within_ that network. |
| 12 | + |
| 13 | +## Installing |
| 14 | + |
| 15 | +```sh |
| 16 | +go get -u github.com/c-robinson/iplib/iid |
| 17 | +``` |
| 18 | + |
| 19 | +## Using IID |
| 20 | + |
| 21 | +This library contains functions for uniting `net.IP` and `net.HardwareAddr` |
| 22 | +addresses in order to generate globally unique IPv6 addresses. The simplest of |
| 23 | +which is the "Modified EUI-64 address" described in [RFC4291 section 2.5.1](https://tools.ietf.org/html/rfc4291#section-2.5.1) |
| 24 | + |
| 25 | +```go |
| 26 | +package main |
| 27 | + |
| 28 | +import ( |
| 29 | + "fmt" |
| 30 | + "net" |
| 31 | + |
| 32 | + "github.com/c-robinson/iplib/iid" |
| 33 | +) |
| 34 | + |
| 35 | +func main() { |
| 36 | + ip := net.ParseIP("2001:db8:1111:2222::") |
| 37 | + hw, _ := net.ParseMAC("99:88:77:66:55:44") |
| 38 | + myiid := iid.MakeEUI64Addr(ip, hw, iid.ScopeGlobal) |
| 39 | + fmt.Println(myiid) // will be "2001:db8:1111:2222:9b88:77ff:fe66:5544" |
| 40 | +} |
| 41 | +``` |
| 42 | + |
| 43 | +EUI64 is fine for a local subnet, but since it is tied to a hardware address |
| 44 | +and guessable by design it is a privacy nightmare as outlined in [RFC4941](https://tools.ietf.org/html/rfc4941). |
| 45 | + |
| 46 | +[RFC7217](https://tools.ietf.org/html/rfc7217) defines an algorithm to create |
| 47 | +"semantically opaque" IID's based on the local interface by hashing the address |
| 48 | +with a secret key, a counter, and some optional additional data. The resulting |
| 49 | +IID is pseudo-random (the same inputs will result in the same outputs) so care |
| 50 | +must be taken while generating it. This function has some requirements: |
| 51 | + |
| 52 | +- `secret` a `[]byte` that is a closely-held secret key |
| 53 | +- `counter` an `int64`, this is what provides the address its mutability. The |
| 54 | + RFC specifies that this counter should be incremented every time the same |
| 55 | + ipaddr/hwaddr pair is used as input and should be stored in non-volatile |
| 56 | + memory to preserve it |
| 57 | +- `netid` is an optional parameter to improve the privacy of the results, it |
| 58 | + is suggested that this be some other bit of information from the local |
| 59 | + network such as an 802.11 SSID. |
| 60 | + |
| 61 | +NOTE: it is possible, though very unlikely, that an address generated this way |
| 62 | +might collide with the [IANA Reserved Interface Identifier List](https://www.iana.org/assignments/ipv6-interface-ids/ipv6-interface-ids.xhtml), |
| 63 | +if this happens an `iid.ErrIIDAddressCollision` will be returned. If so |
| 64 | +`counter` should be incremented and the function re-run. |
| 65 | + |
| 66 | +```go |
| 67 | +package main |
| 68 | + |
| 69 | +import ( |
| 70 | + "fmt" |
| 71 | + "net" |
| 72 | + |
| 73 | + "github.com/c-robinson/iplib/iid" |
| 74 | +) |
| 75 | + |
| 76 | +func main() { |
| 77 | + ip := net.ParseIP("2001:db8::") |
| 78 | + hw, _ := net.ParseMAC("77:88:99:aa:bb:cc") |
| 79 | + counter := int64(1) |
| 80 | + netid := []byte("01234567") |
| 81 | + secret := []byte("secret") |
| 82 | + |
| 83 | + myiid, err := iid.MakeOpaqueAddr(ip, hw, counter, netid, secret) |
| 84 | + if err != nil { |
| 85 | + fmt.Println("a very unlikely collision occurred!") |
| 86 | + } |
| 87 | + fmt.Println(myiid) // will be "2001:db8::c6fa:ba02:41ab:282c" |
| 88 | +} |
| 89 | +``` |
| 90 | + |
| 91 | +`MakeOpaqueIID()` is an implementation of the RFC's specified function |
| 92 | +using its' preferred [SHA256](https://golang.org/pkg/crypto/sha256/) |
| 93 | +hashing algorithm and a `iid.ScopeGlobal` scope. If either of these is not to |
| 94 | +your liking you can roll your own by calling the underlying function. |
| 95 | + |
| 96 | +NOTE: if you use any hashing algorithm other than SHA224 or SHA256 you will |
| 97 | +need to import both `"crypto"` _and_ the crypto submodule with your specific |
| 98 | +implementation first (e.g. `_ "golang.org/x/crypto/blake2s"`. Also note that |
| 99 | +the RFC _specifically prohibits_ MD5 as being too insecure for use. Here's an |
| 100 | +example using [SHA512](https://golang.org/pkg/crypto/sha512/) |
| 101 | + |
| 102 | +```go |
| 103 | +package main |
| 104 | + |
| 105 | +import ( |
| 106 | + "crypto" |
| 107 | + _ "crypto/sha512" |
| 108 | + "fmt" |
| 109 | + "net" |
| 110 | + |
| 111 | + "github.com/c-robinson/iplib/iid" |
| 112 | +) |
| 113 | + |
| 114 | +func main() { |
| 115 | + ip := net.ParseIP("2001:db8::") |
| 116 | + hw, _ := net.ParseMAC("77:88:99:aa:bb:cc") |
| 117 | + counter := int64(1) |
| 118 | + netid := []byte("01234567") |
| 119 | + secret := []byte("secret") |
| 120 | + |
| 121 | + myiid, err := iid.GenerateRFC7217Addr(ip, hw, counter, netid, secret, crypto.SHA384, iid.ScopeGlobal) |
| 122 | + if err != nil { |
| 123 | + fmt.Println("a very unlikely collision occurred!") |
| 124 | + } |
| 125 | + fmt.Println(myiid) // will be "2001:db8::51b3:c6b0:4e14:3519" |
| 126 | +} |
| 127 | +``` |
| 128 | + |
| 129 | +Finally, to be entirely RFC7217-compliant a function _should_ check it's |
| 130 | +results to make sure they don't collide with the IANA Reserved Interface |
| 131 | +Identifier List. In the name of "using every part of the buffalo" the function |
| 132 | +is exposed for the extremely unlikely case where anyone needs it: |
| 133 | + |
| 134 | +```go |
| 135 | +package main |
| 136 | + |
| 137 | +import ( |
| 138 | + "fmt" |
| 139 | + "net" |
| 140 | + |
| 141 | + "github.com/c-robinson/iplib/iid" |
| 142 | +) |
| 143 | + |
| 144 | +func main() { |
| 145 | + ip := net.ParseIP("2001:db8::0200:5EFF:FE00:5211") |
| 146 | + res := iid.GetReservationsForIP(ip) |
| 147 | + fmt.Println(res.RFC) // will be "RFC4291" |
| 148 | + fmt.Println(res.Title) // "Reserved IPv6 Interface Identifiers corresponding to the IANA Ethernet Block" |
| 149 | +} |
| 150 | +``` |
0 commit comments