123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661 |
- /*
- * This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/.
- */
- // General functions for network address parsing and manipulation
- package netaddr
- import (
- "bytes"
- "fmt"
- "io"
- "math"
- "net"
- "strconv"
- )
- func byteOp(u, v []byte, f func(byte, byte) byte) []byte {
- w := make([]byte, len(u))
- for i := 0; i < len(u); i++ {
- w[i] = f(u[i], v[i])
- }
- return w
- }
- func byteOr(u, v []byte) []byte {
- return byteOp(u, v, func(ui, vi byte) byte {
- return ui | vi
- })
- }
- func byteXor(u, v []byte) []byte {
- return byteOp(u, v, func(ui, vi byte) byte {
- return ui ^ vi
- })
- }
- func applyMask(address, mask []byte) {
- for i := range address {
- address[i] &= mask[i]
- }
- return
- }
- var rfc1918 = []*NetAddr{
- &NetAddr{[]byte{10, 0, 0, 0}, uint8(8)},
- &NetAddr{[]byte{172, 16, 0, 0}, uint8(12)},
- &NetAddr{[]byte{192, 168, 0, 0}, uint8(16)},
- }
- func NewNetAddr(a []byte, l int) *NetAddr {
- addr := make([]byte, len(a))
- copy(addr, a)
- return &NetAddr{
- address: addr,
- length: uint8(clamp(l, 0, len(a)*8)),
- }
- }
- /*
- NetAddr stores an address of S bytes together with a prefix
- length L.
- A prefix length L < S induces a partition on the entire
- address space, containing 2^L equivalence classes, each with
- 2^(S-L) elements. The canonical element for a given
- equivalence class is always the least (or lowest) address.
- However, as any element of the equivalence class is
- representative of the rest, we allow the host bits to vary,
- regardless of the prefix length. As this is not proper CIDR,
- it is useful to think of this construction as an address
- with context.
- */
- type NetAddr struct {
- address []byte
- length uint8
- }
- /* Address returns an address without context (length). */
- func (na *NetAddr) Address() []byte {
- return na.address
- }
- /* Length returns the prefix length. */
- func (na *NetAddr) Length() int {
- return int(na.length)
- }
- /* AddressSize returns the size of the address in bytes. */
- func (na *NetAddr) AddressSize() int {
- return len(na.Address())
- }
- /*
- SetLength changes the length.
- The NetAddr pointer is returned for convenience.
- */
- func (na *NetAddr) SetLength(length int) *NetAddr {
- na.length = uint8(min(length, na.AddressSize()*8))
- return na
- }
- /*
- FirstAddress sets the address to the first (lowest)
- address in the prefix.
- The NetAddr pointer is returned for convenience.
- */
- func (na *NetAddr) FirstAddress() *NetAddr {
- bitSize := na.AddressSize() * 8
- mask := net.CIDRMask(na.Length(), bitSize)
- applyMask(na.address, mask)
- return na
- }
- /*
- LastAddress sets the address to the last (highest) address
- in the prefix.
- The NetAddr pointer is returned for convenience.
- */
- func (na *NetAddr) LastAddress() *NetAddr {
- bitSize := na.AddressSize() * 8
- mask := net.CIDRMask(na.Length(), bitSize)
- allOnes := net.CIDRMask(bitSize, bitSize)
- invMask := byteXor(mask, allOnes)
- na.address = byteOr(na.FirstAddress().address, invMask)
- return na
- }
- /*
- Clone returns a pointer to a new (deep) copy of the
- provided NetAddr.
- */
- func (na *NetAddr) Clone() *NetAddr {
- next := &NetAddr{
- address: make([]byte, len(na.address)),
- length: na.length,
- }
- copy(next.address, na.address)
- return next
- }
- /*
- Mask returns a dotted decimal subnet mask corresponding
- to the given NetAddr prefix length.
- */
- func (na *NetAddr) Mask() []byte {
- onesToMask :=
- [9]byte{0, 128, 192, 224, 240, 248, 252, 254, 255}
- ones := na.Length()
- mask := make([]byte, na.AddressSize())
- for i := range mask {
- olen := min(8, ones)
- ones -= olen
- mask[i] = onesToMask[int(olen)]
- }
- return mask
- }
- /*
- Integer returns an aton-style integer representation for the
- address of the given NetAddr.
- */
- func (na *NetAddr) Integer() (dec uint64) {
- for _, b := range na.address {
- dec <<= 8
- dec += uint64(b)
- }
- return
- }
- /*
- String returns an appopriate string representation for
- the given NetAddr.
- */
- func (na *NetAddr) String() string {
- addr := net.IP(na.address).String()
- length := fmt.Sprintf("%d", na.length)
- return addr + "/" + length
- }
- /*
- IsRFC1918 returns true when the given NetAddr is a subset of
- an RFC1918 reserved range.
- */
- func (na *NetAddr) IsRFC1918() bool {
- for _, e := range rfc1918 {
- if na.IsSubset(e) {
- return true
- }
- }
- return false
- }
- /*
- IsEqual returns true when the given NetAddrs have the same
- address and length.
- */
- func (na1 *NetAddr) IsEqual(na2 *NetAddr) bool {
- return NetAddrIsEqual(na1, na2)
- }
- /*
- Contains returns true when na1 contains, or is equal to,
- na2.
- */
- func (na1 *NetAddr) Contains(na2 *NetAddr) bool {
- return NetAddrContains(na1, na2)
- }
- /*
- IsSubset returns true when na2 contains, or is equal to,
- na1.
- */
- func (na1 *NetAddr) IsSubset(na2 *NetAddr) bool {
- return NetAddrIsSubset(na1, na2)
- }
- /*
- Overlaps returns true when na1 Contains na2 or na2 Contains
- na1.
- */
- func (na1 *NetAddr) Overlaps(na2 *NetAddr) bool {
- return NetAddrOverlaps(na1, na2)
- }
- /* Test against sheared isometry on byte prefixes. */
- func octetLessThan(o1, m1, o2, m2 byte) bool {
- /* We seek an isometry from a binary trie embedded in R²
- * to the integral number line, with ones bits branching to
- * the right and zeros bits branching to the left. When
- * considering only the ones bits of an n-bit binary
- * number, the usual decimal encoding suffices, as each ith
- * bit, for 0 <= i <= n-1, shifts a point at the origin to
- * the right by 2^i. However, the zeros bits have no
- * effect, in this sense, as leading zeros do not
- * contribute to the integer value, resulting in many
- * binary strings occupying the same point. This mapping is
- * well-formed and surjective (every integer has a binary
- * encoding), but it is not injective.
- *
- * -> R 0 00 000 0000
- * -> 0001
- * -> 001 0010
- * -> 0011
- * -> 01 010 0100
- * -> 0101
- * -> 011 0110
- * -> 0111
- * -> 1 10 100 1000
- * -> 1001
- * -> 101 1010
- * -> 1011
- * -> 11 110 1100
- * -> 1101
- * -> 111 1110
- * -> 1111
- *
- * Instead, by allowing each zero bit to provide a negative
- * contribution, we ensure that every path (binary string)
- * maps to a unique point on the integral line. To see that
- * this is an isometry, project the nodes of a binary trie
- * embedded in R², with branch width w = 2^(n-j-1) at rank
- * j and arbitrary branch height h, onto the integral line.
- *
- * Here, we express the above isometry by simply
- * subtracting the contributions of the place values of the
- * zeros bits, which we obtain by ones complement (XOR).
- * Masking ensures we map only the prefix.
- *
- * Finally, we note that projecting the usual geometry of a
- * binary trie onto the integral line places each ancestor
- * at the midpoint of its descendants. This creates a
- * disagreement between the partial and total orders, which
- * makes it difficult to recover the original partial order
- * generated by the trie.
- *
- * -> 0000
- * -> 000
- * -> 0001
- * -> 00
- * -> 0010
- * -> 001
- * -> 0011
- * -> 0
- * -> 0100
- * -> 010
- * -> 0101
- * -> 01
- * -> 0110
- * -> 011
- * -> 0111
- * 0/0
- * -> 1000
- * -> 100
- * -> 1001
- * -> 10
- * -> 1010
- * -> 101
- * -> 1011
- * -> 1
- * -> 1100
- * -> 110
- * -> 1101
- * -> 11
- * -> 1110
- * -> 111
- * -> 1111
- *
- * Instead, we require that the total order < given by the
- * proposed isometry have the property that it always agree
- * with the partial order ≪ given by the binary trie. That
- * is, given nodes a, b of a binary trie, if a ≪ b, then a
- * < b. Note that the converse does not hold, nor would it
- * be useful if it did. To accomplish this, we effectively
- * apply a horizontal shear to the right on the binary trie
- * such that the root of each subtree falls to the right of
- * its descendants.
- *
- * -> 0000
- * -> 0001
- * -> 000
- * -> 0010
- * -> 0011
- * -> 001
- * -> 00
- * -> 0100
- * -> 0101
- * -> 010
- * -> 0110
- * -> 0111
- * -> 011
- * -> 01
- * -> 0
- * -> 1000
- * -> 1001
- * -> 100
- * -> 1010
- * -> 1011
- * -> 101
- * -> 10
- * -> 1100
- * -> 1101
- * -> 110
- * -> 1110
- * -> 1111
- * -> 111
- * -> 11
- * -> 1
- * 0/0
- *
- * In the unsheared isometry, the nodes we wish to reorder
- * are specifically those which precede their descendants--
- * that is, whenever a < b and b ≪ a, we would like to
- * move a to the right of b so that b < a, thereby agreeing
- * with the partial order. Since b ≪ a, it is the case
- * that a represents a shorter prefix than b. Furthermore,
- * since b is a descendant of a, b's prefix is guaranteed
- * to share all of the bits of a. Conversely, if a and b
- * share a prefix, then that prefix represents the most
- * recent shared ancestor (least upper bound). And if the
- * prefix shared by a and b is a's prefix, then it must be
- * the case that a's prefix is shorter, and b is a
- * descendant of a. Thus, b ≪ a.
- *
- * Therefore, we conclude that a must be reordered
- * precisely when a < b and b shares a's prefix. Hence our
- * assertion `f1 < f2 && o1 != o2&m1`. To reorder, we
- * simply place prefixes with longer masks before shorter
- * ones, as given by `m1 > m2`.
- **/
- f1 := int(o1&m1) - int((o1^byte(255))&m1)
- f2 := int(o2&m2) - int((o2^byte(255))&m2)
- return f1 < f2 && o1 != o2&m1 || m1 > m2
- }
- /*
- LessThan imposes a total order < on NetAddrs, returning
- true precisely when na1 < na2.
- */
- func LessThan(na1 *NetAddr, na2 *NetAddr) (less bool) {
- if na1.AddressSize() != na2.AddressSize() {
- return
- }
- mask1 := na1.Mask()
- mask2 := na2.Mask()
- for i := 0; i < na1.AddressSize(); i++ {
- if octetLessThan(
- na1.address[i], mask1[i],
- na2.address[i], mask2[i],
- ) {
- less = true
- break
- }
- if na1.address[i] == na2.address[i] &&
- mask1[i] == mask2[i] {
- continue
- }
- break
- }
- return
- }
- /*
- FirstAddress is a non-mutating version of the FirstAddress
- method.
- */
- func FirstAddress(na *NetAddr) *NetAddr {
- return na.Clone().FirstAddress()
- }
- /*
- LastAddress is a non-mutating version of the LastAddress
- method.
- */
- func LastAddress(na *NetAddr) *NetAddr {
- return na.Clone().LastAddress()
- }
- /*
- NetAddrIsEqual is a function implementation of the IsEqual
- method.
- */
- func NetAddrIsEqual(na1, na2 *NetAddr) bool {
- return na1.length == na2.length &&
- bytes.Equal(na1.address, na2.address)
- }
- /*
- NetAddrContains is a function implementation of the
- Contains method.
- */
- func NetAddrContains(na1, na2 *NetAddr) bool {
- tmp := na2.Clone().SetLength(na1.Length())
- return na1.length <= na2.length &&
- FirstAddress(na1).IsEqual(FirstAddress(tmp))
- }
- /*
- NetAddrIsSubset is a function implementation of the IsSubset
- method.
- */
- func NetAddrIsSubset(na1, na2 *NetAddr) bool {
- return na2.Contains(na1)
- }
- /*
- NetAddrOverlaps is a function implementation of the Overlaps
- method.
- */
- func NetAddrOverlaps(na1, na2 *NetAddr) bool {
- return na1.Contains(na2) || na2.Contains(na1)
- }
- /*
- Contiguous tests whether na1 and na2 can be summarized by
- a single prefix containing only na1 and na2.
- */
- func Contiguous(na1, na2 *NetAddr) bool {
- if na1.AddressSize() != na2.AddressSize() ||
- na1.Length() != na2.Length() ||
- Join(na1, na2).Length() != na1.Length()-1 {
- return false
- }
- return true
- }
- /*
- LeftAdjacent tests whether na1 is left-adjacent to na2.
- LeftAdjacent returns true if, and only if, the first address
- in the prefix of na2 is immediately preceded by the last
- address in the prefix of na1. If na1 and na2 are contiguous,
- then either na1 is adjacent to na2 or na2 is adjacent to
- na1. However, neither converse holds, in general.
- */
- func LeftAdjacent(na1, na2 *NetAddr) bool {
- if na1.AddressSize() != na2.AddressSize() {
- return false
- }
- b1 := na1.LastAddress().Integer()
- a2 := na2.FirstAddress().Integer()
- return b1+1 == a2
- }
- /*
- Join returns the smallest summary containing both na1 and
- na2.
- */
- func Join(na1, na2 *NetAddr) *NetAddr {
- minLen := min(na1.Length(), na2.Length())
- addrSize := min(na1.AddressSize(), na2.AddressSize())
- bitSize := addrSize * 8
- fa1 := na1.FirstAddress()
- fa2 := na2.FirstAddress()
- var bits int
- for i := 0; i < addrSize; i++ {
- xor := fa1.address[i] ^ fa2.address[i]
- if xor != 0 {
- bits += int(math.Trunc(1.0 + math.Log2(float64(xor))))
- bits += bitSize - 8*(i+1)
- break
- }
- }
- return fa1.
- Clone().
- SetLength(min(minLen, bitSize-bits)).
- FirstAddress()
- }
- /*
- Split returns the two halves of a given NetAddr. The input
- is unchanged.
- If the given NetAddr represents a single address, then
- Split returns the input for both the left and right outputs.
- */
- func Split(na *NetAddr) (*NetAddr, *NetAddr) {
- if na.Length() == na.AddressSize()*8 {
- return na, na
- }
- nextLen := na.Length() + 1
- lower :=
- na.
- Clone().
- FirstAddress().
- SetLength(nextLen)
- upper :=
- na.
- Clone().
- LastAddress().
- SetLength(nextLen).
- FirstAddress()
- return lower, upper
- }
- func parseTruncatedIPv4(s string) (na *NetAddr, err error) {
- na = &NetAddr{address: make([]byte, 4)}
- r := bytes.NewBufferString(s)
- buf := new(bytes.Buffer)
- i := 0
- onLen := false
- for i < 4 {
- var b byte
- b, err = r.ReadByte()
- switch {
- case err == io.EOF:
- na.length = byte(8*i - 8)
- if onLen {
- var o int
- o, err = strconv.Atoi(buf.String())
- if err != nil {
- return
- }
- if o < 0 || 32 < o {
- err = fmt.Errorf("length out of range: %v", o)
- return
- }
- na.length = byte(o)
- return
- }
- err = nil
- return
- case err != nil:
- return
- case '0' <= b && b <= '9':
- buf.WriteByte(b)
- continue
- case b == '.' || b == '/':
- var o int
- o, err = strconv.Atoi(buf.String())
- if err != nil {
- return
- }
- if o < 0 || 255 < o {
- err = fmt.Errorf("octet out of range: %v", o)
- return
- }
- na.address[i] = byte(o)
- i++
- buf = new(bytes.Buffer)
- if b == '/' {
- onLen = true
- }
- }
- }
- err = fmt.Errorf("input is longer than expected")
- return
- }
- /*
- IP parses a string as IPv4 or IPv6, returning a NetAddr or
- an error.
- Address strings without lengths are always interpreted as
- single addresses with maximal length.
- Sample inputs:
- - 192.0.2.9/24
- - 2001:DB8::/32
- - 192.0.2.1
- - 2001:db8::
- - 192.0.2/24
- */
- func IP(s string) (na *NetAddr, err error) {
- var ip net.IP
- var ipNet *net.IPNet
- ip, ipNet, err = net.ParseCIDR(s)
- switch err.(type) {
- case nil:
- case *net.ParseError:
- ip = net.ParseIP(s)
- if ip == nil {
- na, err = parseTruncatedIPv4(s)
- if err != nil {
- err = fmt.Errorf("invalid ip address %v: %v", s, err)
- }
- return
- }
- if test := ip.To4(); test != nil {
- ipNet = &net.IPNet{
- IP: ip,
- Mask: net.IPMask([]byte{255, 255, 255, 255}),
- }
- } else if test := ip.To16(); test != nil {
- ipNet = &net.IPNet{
- IP: ip,
- Mask: net.IPMask(
- []byte{
- 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff,
- },
- ),
- }
- }
- err = nil
- default:
- return
- }
- var length, _ int = net.IPMask.Size(ipNet.Mask)
- var address []byte = ip.To4()
- if address == nil {
- address = ip.To16()
- }
- if address == nil {
- panic(fmt.Sprintf("unexpected failure parsing '%s'\n", s))
- }
- na = &NetAddr{address, uint8(length)}
- return
- }
|