mirror of https://github.com/grafana/loki
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1919 lines
53 KiB
1919 lines
53 KiB
// Copyright 2020 The Inet.Af AUTHORS. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style
|
|
// license that can be found in the LICENSE file.
|
|
|
|
// Package netaddr contains a IP address type that's in many ways
|
|
// better than the Go standard library's net.IP type. Building on that
|
|
// IP type, the package also contains IPPrefix, IPPort, IPRange, and
|
|
// IPSet types.
|
|
//
|
|
// Notably, this package's IP type takes less memory, is immutable,
|
|
// comparable (supports == and being a map key), and more. See
|
|
// https://github.com/inetaf/netaddr for background.
|
|
//
|
|
// IPv6 Zones
|
|
//
|
|
// IP and IPPort are the only types in this package that support IPv6
|
|
// zones. Other types silently drop any passed-in zones.
|
|
package netaddr // import "inet.af/netaddr"
|
|
|
|
import (
|
|
"encoding/binary"
|
|
"errors"
|
|
"fmt"
|
|
"math"
|
|
"net"
|
|
"sort"
|
|
"strconv"
|
|
"strings"
|
|
|
|
"go4.org/intern"
|
|
)
|
|
|
|
// Sizes: (64-bit)
|
|
// net.IP: 24 byte slice header + {4, 16} = 28 to 40 bytes
|
|
// net.IPAddr: 40 byte slice header + {4, 16} = 44 to 56 bytes + zone length
|
|
// netaddr.IP: 24 bytes (zone is per-name singleton, shared across all users)
|
|
|
|
// IP represents an IPv4 or IPv6 address (with or without a scoped
|
|
// addressing zone), similar to Go's net.IP or net.IPAddr.
|
|
//
|
|
// Unlike net.IP or net.IPAddr, the netaddr.IP is a comparable value
|
|
// type (it supports == and can be a map key) and is immutable.
|
|
// Its memory representation is 24 bytes on 64-bit machines (the same
|
|
// size as a Go slice header) for both IPv4 and IPv6 address.
|
|
type IP struct {
|
|
// addr are the hi and lo bits of an IPv6 address. If z==z4,
|
|
// hi and lo contain the IPv4-mapped IPv6 address.
|
|
//
|
|
// hi and lo are constructed by interpreting a 16-byte IPv6
|
|
// address as a big-endian 128-bit number. The most significant
|
|
// bits of that number go into hi, the rest into lo.
|
|
//
|
|
// For example, 0011:2233:4455:6677:8899:aabb:ccdd:eeff is stored as:
|
|
// addr.hi = 0x0011223344556677
|
|
// addr.lo = 0x8899aabbccddeeff
|
|
//
|
|
// We store IPs like this, rather than as [16]byte, because it
|
|
// turns most operations on IPs into arithmetic and bit-twiddling
|
|
// operations on 64-bit registers, which is much faster than
|
|
// bytewise processing.
|
|
addr uint128
|
|
|
|
// z is a combination of the address family and the IPv6 zone.
|
|
//
|
|
// nil means invalid IP address (for the IP zero value).
|
|
// z4 means an IPv4 address.
|
|
// z6noz means an IPv6 address without a zone.
|
|
//
|
|
// Otherwise it's the interned zone name string.
|
|
z *intern.Value
|
|
}
|
|
|
|
// z0, z4, and z6noz are sentinel IP.z values.
|
|
// See the IP type's field docs.
|
|
var (
|
|
z0 = (*intern.Value)(nil)
|
|
z4 = new(intern.Value)
|
|
z6noz = new(intern.Value)
|
|
)
|
|
|
|
// IPv6LinkLocalAllNodes returns the IPv6 link-local all nodes multicast
|
|
// address ff02::1.
|
|
func IPv6LinkLocalAllNodes() IP { return IPv6Raw([16]byte{0: 0xff, 1: 0x02, 15: 0x01}) }
|
|
|
|
// IPv6Unspecified returns the IPv6 unspecified address ::.
|
|
func IPv6Unspecified() IP { return IP{z: z6noz} }
|
|
|
|
// IPv4 returns the IP of the IPv4 address a.b.c.d.
|
|
func IPv4(a, b, c, d uint8) IP {
|
|
return IP{
|
|
addr: uint128{0, 0xffff00000000 | uint64(a)<<24 | uint64(b)<<16 | uint64(c)<<8 | uint64(d)},
|
|
z: z4,
|
|
}
|
|
}
|
|
|
|
// IPv6Raw returns the IPv6 address given by the bytes in addr,
|
|
// without an implicit Unmap call to unmap any v6-mapped IPv4
|
|
// address.
|
|
func IPv6Raw(addr [16]byte) IP {
|
|
return IP{
|
|
addr: uint128{
|
|
binary.BigEndian.Uint64(addr[:8]),
|
|
binary.BigEndian.Uint64(addr[8:]),
|
|
},
|
|
z: z6noz,
|
|
}
|
|
}
|
|
|
|
// ipv6Slice is like IPv6Raw, but operates on a 16-byte slice. Assumes
|
|
// slice is 16 bytes, caller must enforce this.
|
|
func ipv6Slice(addr []byte) IP {
|
|
return IP{
|
|
addr: uint128{
|
|
binary.BigEndian.Uint64(addr[:8]),
|
|
binary.BigEndian.Uint64(addr[8:]),
|
|
},
|
|
z: z6noz,
|
|
}
|
|
}
|
|
|
|
// IPFrom16 returns the IP address given by the bytes in addr,
|
|
// unmapping any v6-mapped IPv4 address.
|
|
//
|
|
// It is equivalent to calling IPv6Raw(addr).Unmap().
|
|
func IPFrom16(addr [16]byte) IP {
|
|
return IPv6Raw(addr).Unmap()
|
|
}
|
|
|
|
// IPFrom4 returns the IPv4 address given by the bytes in addr.
|
|
// It is equivalent to calling IPv4(addr[0], addr[1], addr[2], addr[3]).
|
|
func IPFrom4(addr [4]byte) IP {
|
|
return IPv4(addr[0], addr[1], addr[2], addr[3])
|
|
}
|
|
|
|
// ParseIP parses s as an IP address, returning the result. The string
|
|
// s can be in dotted decimal ("192.0.2.1"), IPv6 ("2001:db8::68"),
|
|
// or IPv6 with a scoped addressing zone ("fe80::1cc0:3e8c:119f:c2e1%ens18").
|
|
func ParseIP(s string) (IP, error) {
|
|
for i := 0; i < len(s); i++ {
|
|
switch s[i] {
|
|
case '.':
|
|
return parseIPv4(s)
|
|
case ':':
|
|
return parseIPv6(s)
|
|
case '%':
|
|
// Assume that this was trying to be an IPv6 address with
|
|
// a zone specifier, but the address is missing.
|
|
return IP{}, parseIPError{in: s, msg: "missing IPv6 address"}
|
|
}
|
|
}
|
|
return IP{}, parseIPError{in: s, msg: "unable to parse IP"}
|
|
}
|
|
|
|
// MustParseIP calls ParseIP(s) and panics on error.
|
|
// It is intended for use in tests with hard-coded strings.
|
|
func MustParseIP(s string) IP {
|
|
ip, err := ParseIP(s)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
return ip
|
|
}
|
|
|
|
type parseIPError struct {
|
|
in string // the string given to ParseIP
|
|
msg string // an explanation of the parse failure
|
|
at string // optionally, the unparsed portion of in at which the error occurred.
|
|
}
|
|
|
|
func (err parseIPError) Error() string {
|
|
if err.at != "" {
|
|
return fmt.Sprintf("ParseIP(%q): %s (at %q)", err.in, err.msg, err.at)
|
|
}
|
|
return fmt.Sprintf("ParseIP(%q): %s", err.in, err.msg)
|
|
}
|
|
|
|
// parseIPv4 parses s as an IPv4 address (in form "192.168.0.1").
|
|
func parseIPv4(s string) (ip IP, err error) {
|
|
var fields [3]uint8
|
|
var val, pos int
|
|
for i := 0; i < len(s); i++ {
|
|
if s[i] >= '0' && s[i] <= '9' {
|
|
val = val*10 + int(s[i]) - '0'
|
|
if val > 255 {
|
|
return IP{}, parseIPError{in: s, msg: "IPv4 field has value >255"}
|
|
}
|
|
} else if s[i] == '.' {
|
|
// .1.2.3
|
|
// 1.2.3.
|
|
// 1..2.3
|
|
if i == 0 || i == len(s)-1 || s[i-1] == '.' {
|
|
return IP{}, parseIPError{in: s, msg: "IPv4 field must have at least one digit", at: s[i:]}
|
|
}
|
|
// 1.2.3.4.5
|
|
if pos == 3 {
|
|
return IP{}, parseIPError{in: s, msg: "IPv4 address too long"}
|
|
}
|
|
fields[pos] = uint8(val)
|
|
pos++
|
|
val = 0
|
|
} else {
|
|
return IP{}, parseIPError{in: s, msg: "unexpected character", at: s[i:]}
|
|
}
|
|
}
|
|
if pos < 3 {
|
|
return IP{}, parseIPError{in: s, msg: "IPv4 address too short"}
|
|
}
|
|
return IPv4(fields[0], fields[1], fields[2], uint8(val)), nil
|
|
}
|
|
|
|
// parseIPv6 parses s as an IPv6 address (in form "2001:db8::68").
|
|
func parseIPv6(in string) (IP, error) {
|
|
s := in
|
|
|
|
// Split off the zone right from the start. Yes it's a second scan
|
|
// of the string, but trying to handle it inline makes a bunch of
|
|
// other inner loop conditionals more expensive, and it ends up
|
|
// being slower.
|
|
zone := ""
|
|
i := strings.IndexByte(s, '%')
|
|
if i != -1 {
|
|
s, zone = s[:i], s[i+1:]
|
|
if zone == "" {
|
|
// Not allowed to have an empty zone if explicitly specified.
|
|
return IP{}, parseIPError{in: in, msg: "zone must be a non-empty string"}
|
|
}
|
|
}
|
|
|
|
var ip [16]byte
|
|
ellipsis := -1 // position of ellipsis in ip
|
|
|
|
// Might have leading ellipsis
|
|
if len(s) >= 2 && s[0] == ':' && s[1] == ':' {
|
|
ellipsis = 0
|
|
s = s[2:]
|
|
// Might be only ellipsis
|
|
if len(s) == 0 {
|
|
return IPv6Unspecified().WithZone(zone), nil
|
|
}
|
|
}
|
|
|
|
// Loop, parsing hex numbers followed by colon.
|
|
i = 0
|
|
for i < 16 {
|
|
// Hex number. Similar to parseIPv4, inlining the hex number
|
|
// parsing yields a significant performance increase.
|
|
off := 0
|
|
acc := uint32(0)
|
|
for ; off < len(s); off++ {
|
|
c := s[off]
|
|
if c >= '0' && c <= '9' {
|
|
acc = (acc << 4) + uint32(c-'0')
|
|
} else if c >= 'a' && c <= 'f' {
|
|
acc = (acc << 4) + uint32(c-'a'+10)
|
|
} else if c >= 'A' && c <= 'F' {
|
|
acc = (acc << 4) + uint32(c-'A'+10)
|
|
} else {
|
|
break
|
|
}
|
|
if acc > math.MaxUint16 {
|
|
// Overflow, fail.
|
|
return IP{}, parseIPError{in: in, msg: "IPv6 field has value >=2^16", at: s}
|
|
}
|
|
}
|
|
if off == 0 {
|
|
// No digits found, fail.
|
|
return IP{}, parseIPError{in: in, msg: "each colon-separated field must have at least one digit", at: s}
|
|
}
|
|
|
|
// If followed by dot, might be in trailing IPv4.
|
|
if off < len(s) && s[off] == '.' {
|
|
if ellipsis < 0 && i != 12 {
|
|
// Not the right place.
|
|
return IP{}, parseIPError{in: in, msg: "embedded IPv4 address must replace the final 2 fields of the address", at: s}
|
|
}
|
|
if i+4 > 16 {
|
|
// Not enough room.
|
|
return IP{}, parseIPError{in: in, msg: "too many hex fields to fit an embedded IPv4 at the end of the address", at: s}
|
|
}
|
|
// TODO: could make this a bit faster by having a helper
|
|
// that parses to a [4]byte, and have both parseIPv4 and
|
|
// parseIPv6 use it.
|
|
ip4, err := parseIPv4(s)
|
|
if err != nil {
|
|
return IP{}, parseIPError{in: in, msg: err.Error(), at: s}
|
|
}
|
|
ip[i] = ip4.v4(0)
|
|
ip[i+1] = ip4.v4(1)
|
|
ip[i+2] = ip4.v4(2)
|
|
ip[i+3] = ip4.v4(3)
|
|
s = ""
|
|
i += 4
|
|
break
|
|
}
|
|
|
|
// Save this 16-bit chunk.
|
|
ip[i] = byte(acc >> 8)
|
|
ip[i+1] = byte(acc)
|
|
i += 2
|
|
|
|
// Stop at end of string.
|
|
s = s[off:]
|
|
if len(s) == 0 {
|
|
break
|
|
}
|
|
|
|
// Otherwise must be followed by colon and more.
|
|
if s[0] != ':' {
|
|
return IP{}, parseIPError{in: in, msg: "unexpected character, want colon", at: s}
|
|
} else if len(s) == 1 {
|
|
return IP{}, parseIPError{in: in, msg: "colon must be followed by more characters", at: s}
|
|
}
|
|
s = s[1:]
|
|
|
|
// Look for ellipsis.
|
|
if s[0] == ':' {
|
|
if ellipsis >= 0 { // already have one
|
|
return IP{}, parseIPError{in: in, msg: "multiple :: in address", at: s}
|
|
}
|
|
ellipsis = i
|
|
s = s[1:]
|
|
if len(s) == 0 { // can be at end
|
|
break
|
|
}
|
|
}
|
|
}
|
|
|
|
// Must have used entire string.
|
|
if len(s) != 0 {
|
|
return IP{}, parseIPError{in: in, msg: "trailing garbage after address", at: s}
|
|
}
|
|
|
|
// If didn't parse enough, expand ellipsis.
|
|
if i < 16 {
|
|
if ellipsis < 0 {
|
|
return IP{}, parseIPError{in: in, msg: "address string too short"}
|
|
}
|
|
n := 16 - i
|
|
for j := i - 1; j >= ellipsis; j-- {
|
|
ip[j+n] = ip[j]
|
|
}
|
|
for j := ellipsis + n - 1; j >= ellipsis; j-- {
|
|
ip[j] = 0
|
|
}
|
|
} else if ellipsis >= 0 {
|
|
// Ellipsis must represent at least one 0 group.
|
|
return IP{}, parseIPError{in: in, msg: "the :: must expand to at least one field of zeros"}
|
|
}
|
|
return IPv6Raw(ip).WithZone(zone), nil
|
|
}
|
|
|
|
// FromStdIP returns an IP from the standard library's IP type.
|
|
//
|
|
// If std is invalid, ok is false.
|
|
//
|
|
// FromStdIP implicitly unmaps IPv6-mapped IPv4 addresses. That is, if
|
|
// len(std) == 16 and contains an IPv4 address, only the IPv4 part is
|
|
// returned, without the IPv6 wrapper. This is the common form returned by
|
|
// the standard library's ParseIP: https://play.golang.org/p/qdjylUkKWxl.
|
|
// To convert a standard library IP without the implicit unmapping, use
|
|
// FromStdIPRaw.
|
|
func FromStdIP(std net.IP) (ip IP, ok bool) {
|
|
ret, ok := FromStdIPRaw(std)
|
|
if ret.Is4in6() {
|
|
ret.z = z4
|
|
}
|
|
return ret, ok
|
|
}
|
|
|
|
// FromStdIPRaw returns an IP from the standard library's IP type.
|
|
// If std is invalid, ok is false.
|
|
// Unlike FromStdIP, FromStdIPRaw does not do an implicit Unmap if
|
|
// len(std) == 16 and contains an IPv6-mapped IPv4 address.
|
|
func FromStdIPRaw(std net.IP) (ip IP, ok bool) {
|
|
switch len(std) {
|
|
case 4:
|
|
return IPv4(std[0], std[1], std[2], std[3]), true
|
|
case 16:
|
|
return ipv6Slice(std), true
|
|
}
|
|
return IP{}, false
|
|
}
|
|
|
|
// v4 returns the i'th byte of ip. If ip is not an IPv4, v4 returns
|
|
// unspecified garbage.
|
|
func (ip IP) v4(i uint8) uint8 {
|
|
return uint8(ip.addr.lo >> ((3 - i) * 8))
|
|
}
|
|
|
|
// v6 returns the i'th byte of ip. If ip is an IPv4 address, this
|
|
// accesses the IPv4-mapped IPv6 address form of the IP.
|
|
func (ip IP) v6(i uint8) uint8 {
|
|
return uint8(*(ip.addr.halves()[(i/8)%2]) >> ((7 - i%8) * 8))
|
|
}
|
|
|
|
// v6u16 returns the i'th 16-bit word of ip. If ip is an IPv4 address,
|
|
// this accesses the IPv4-mapped IPv6 address form of the IP.
|
|
func (ip IP) v6u16(i uint8) uint16 {
|
|
return uint16(*(ip.addr.halves()[(i/4)%2]) >> ((3 - i%4) * 16))
|
|
}
|
|
|
|
// IsZero reports whether ip is the zero value of the IP type.
|
|
// The zero value is not a valid IP address of any type.
|
|
//
|
|
// Note that "0.0.0.0" and "::" are not the zero value. Use IsUnspecified to
|
|
// check for these values instead.
|
|
func (ip IP) IsZero() bool {
|
|
// Faster than comparing ip == IP{}, but effectively equivalent,
|
|
// as there's no way to make an IP with a nil z from this package.
|
|
return ip.z == z0
|
|
}
|
|
|
|
// IsValid whether the IP is an initialized value and not the IP
|
|
// type's zero value.
|
|
//
|
|
// Note that both "0.0.0.0" and "::" are valid, non-zero values.
|
|
func (ip IP) IsValid() bool { return ip.z != z0 }
|
|
|
|
// BitLen returns the number of bits in the IP address:
|
|
// 32 for IPv4 or 128 for IPv6.
|
|
// For the zero value (see IP.IsZero), it returns 0.
|
|
// For IP4-mapped IPv6 addresses, it returns 128.
|
|
func (ip IP) BitLen() uint8 {
|
|
switch ip.z {
|
|
case z0:
|
|
return 0
|
|
case z4:
|
|
return 32
|
|
}
|
|
return 128
|
|
}
|
|
|
|
// Zone returns ip's IPv6 scoped addressing zone, if any.
|
|
func (ip IP) Zone() string {
|
|
if ip.z == nil {
|
|
return ""
|
|
}
|
|
zone, _ := ip.z.Get().(string)
|
|
return zone
|
|
}
|
|
|
|
// Compare returns an integer comparing two IPs.
|
|
// The result will be 0 if ip==ip2, -1 if ip < ip2, and +1 if ip > ip2.
|
|
// The definition of "less than" is the same as the IP.Less method.
|
|
func (ip IP) Compare(ip2 IP) int {
|
|
f1, f2 := ip.BitLen(), ip2.BitLen()
|
|
if f1 < f2 {
|
|
return -1
|
|
}
|
|
if f1 > f2 {
|
|
return 1
|
|
}
|
|
if hi1, hi2 := ip.addr.hi, ip2.addr.hi; hi1 < hi2 {
|
|
return -1
|
|
} else if hi1 > hi2 {
|
|
return 1
|
|
}
|
|
if lo1, lo2 := ip.addr.lo, ip2.addr.lo; lo1 < lo2 {
|
|
return -1
|
|
} else if lo1 > lo2 {
|
|
return 1
|
|
}
|
|
if ip.Is6() {
|
|
za, zb := ip.Zone(), ip2.Zone()
|
|
if za < zb {
|
|
return -1
|
|
} else if za > zb {
|
|
return 1
|
|
}
|
|
}
|
|
return 0
|
|
}
|
|
|
|
// Less reports whether ip sorts before ip2.
|
|
// IP addresses sort first by length, then their address.
|
|
// IPv6 addresses with zones sort just after the same address without a zone.
|
|
func (ip IP) Less(ip2 IP) bool { return ip.Compare(ip2) == -1 }
|
|
|
|
func (ip IP) lessOrEq(ip2 IP) bool { return ip.Compare(ip2) <= 0 }
|
|
|
|
// ipZone returns the standard library net.IP from ip, as well
|
|
// as the zone.
|
|
// The optional reuse IP provides memory to reuse.
|
|
func (ip IP) ipZone(reuse net.IP) (stdIP net.IP, zone string) {
|
|
base := reuse[:0]
|
|
switch {
|
|
case ip.z == z0:
|
|
return nil, ""
|
|
case ip.Is4():
|
|
a4 := ip.As4()
|
|
return append(base, a4[:]...), ""
|
|
default:
|
|
a16 := ip.As16()
|
|
return append(base, a16[:]...), ip.Zone()
|
|
}
|
|
}
|
|
|
|
// IPAddr returns the net.IPAddr representation of an IP. The returned value is
|
|
// always non-nil, but the IPAddr.IP will be nil if ip is the zero value.
|
|
// If ip contains a zone identifier, IPAddr.Zone is populated.
|
|
func (ip IP) IPAddr() *net.IPAddr {
|
|
stdIP, zone := ip.ipZone(nil)
|
|
return &net.IPAddr{IP: stdIP, Zone: zone}
|
|
}
|
|
|
|
// Is4 reports whether ip is an IPv4 address.
|
|
//
|
|
// It returns false for IP4-mapped IPv6 addresses. See IP.Unmap.
|
|
func (ip IP) Is4() bool {
|
|
return ip.z == z4
|
|
}
|
|
|
|
// Is4in6 reports whether ip is an IPv4-mapped IPv6 address.
|
|
func (ip IP) Is4in6() bool {
|
|
return ip.Is6() && ip.addr.hi == 0 && ip.addr.lo>>32 == 0xffff
|
|
}
|
|
|
|
// Is6 reports whether ip is an IPv6 address, including IPv4-mapped
|
|
// IPv6 addresses.
|
|
func (ip IP) Is6() bool {
|
|
return ip.z != z0 && ip.z != z4
|
|
}
|
|
|
|
// Unmap returns ip with any IPv4-mapped IPv6 address prefix removed.
|
|
//
|
|
// That is, if ip is an IPv6 address wrapping an IPv4 adddress, it
|
|
// returns the wrapped IPv4 address. Otherwise it returns ip, regardless
|
|
// of its type.
|
|
func (ip IP) Unmap() IP {
|
|
if ip.Is4in6() {
|
|
ip.z = z4
|
|
}
|
|
return ip
|
|
}
|
|
|
|
// WithZone returns an IP that's the same as ip but with the provided
|
|
// zone. If zone is empty, the zone is removed. If ip is an IPv4
|
|
// address it's returned unchanged.
|
|
func (ip IP) WithZone(zone string) IP {
|
|
if !ip.Is6() {
|
|
return ip
|
|
}
|
|
if zone == "" {
|
|
ip.z = z6noz
|
|
return ip
|
|
}
|
|
ip.z = intern.GetByString(zone)
|
|
return ip
|
|
}
|
|
|
|
// noZone unconditionally strips the zone from IP.
|
|
// It's similar to WithZone, but small enough to be inlinable.
|
|
func (ip IP) withoutZone() IP {
|
|
if !ip.Is6() {
|
|
return ip
|
|
}
|
|
ip.z = z6noz
|
|
return ip
|
|
}
|
|
|
|
// hasZone reports whether IP has an IPv6 zone.
|
|
func (ip IP) hasZone() bool {
|
|
return ip.z != z0 && ip.z != z4 && ip.z != z6noz
|
|
}
|
|
|
|
// IsLinkLocalUnicast reports whether ip is a link-local unicast address.
|
|
// If ip is the zero value, it will return false.
|
|
func (ip IP) IsLinkLocalUnicast() bool {
|
|
// Dynamic Configuration of IPv4 Link-Local Addresses
|
|
// https://datatracker.ietf.org/doc/html/rfc3927#section-2.1
|
|
if ip.Is4() {
|
|
return ip.v4(0) == 169 && ip.v4(1) == 254
|
|
}
|
|
// IP Version 6 Addressing Architecture (2.4 Address Type Identification)
|
|
// https://datatracker.ietf.org/doc/html/rfc4291#section-2.4
|
|
if ip.Is6() {
|
|
return ip.v6u16(0)&0xffc0 == 0xfe80
|
|
}
|
|
return false // zero value
|
|
}
|
|
|
|
// IsLoopback reports whether ip is a loopback address. If ip is the zero value,
|
|
// it will return false.
|
|
func (ip IP) IsLoopback() bool {
|
|
// Requirements for Internet Hosts -- Communication Layers (3.2.1.3 Addressing)
|
|
// https://datatracker.ietf.org/doc/html/rfc1122#section-3.2.1.3
|
|
if ip.Is4() {
|
|
return ip.v4(0) == 127
|
|
}
|
|
// IP Version 6 Addressing Architecture (2.4 Address Type Identification)
|
|
// https://datatracker.ietf.org/doc/html/rfc4291#section-2.4
|
|
if ip.Is6() {
|
|
return ip.addr.hi == 0 && ip.addr.lo == 1
|
|
}
|
|
return false // zero value
|
|
}
|
|
|
|
// IsMulticast reports whether ip is a multicast address. If ip is the zero
|
|
// value, it will return false.
|
|
func (ip IP) IsMulticast() bool {
|
|
// Host Extensions for IP Multicasting (4. HOST GROUP ADDRESSES)
|
|
// https://datatracker.ietf.org/doc/html/rfc1112#section-4
|
|
if ip.Is4() {
|
|
return ip.v4(0)&0xf0 == 0xe0
|
|
}
|
|
// IP Version 6 Addressing Architecture (2.4 Address Type Identification)
|
|
// https://datatracker.ietf.org/doc/html/rfc4291#section-2.4
|
|
if ip.Is6() {
|
|
return ip.addr.hi>>(64-8) == 0xff // ip.v6(0) == 0xff
|
|
}
|
|
return false // zero value
|
|
}
|
|
|
|
// IsInterfaceLocalMulticast reports whether ip is an IPv6 interface-local
|
|
// multicast address. If ip is the zero value or an IPv4 address, it will return
|
|
// false.
|
|
func (ip IP) IsInterfaceLocalMulticast() bool {
|
|
// IPv6 Addressing Architecture (2.7.1. Pre-Defined Multicast Addresses)
|
|
// https://datatracker.ietf.org/doc/html/rfc4291#section-2.7.1
|
|
if ip.Is6() {
|
|
return ip.v6u16(0)&0xff0f == 0xff01
|
|
}
|
|
return false // zero value
|
|
}
|
|
|
|
// IsLinkLocalMulticast reports whether ip is a link-local multicast address.
|
|
// If ip is the zero value, it will return false.
|
|
func (ip IP) IsLinkLocalMulticast() bool {
|
|
// IPv4 Multicast Guidelines (4. Local Network Control Block (224.0.0/24))
|
|
// https://datatracker.ietf.org/doc/html/rfc5771#section-4
|
|
if ip.Is4() {
|
|
return ip.v4(0) == 224 && ip.v4(1) == 0 && ip.v4(2) == 0
|
|
}
|
|
// IPv6 Addressing Architecture (2.7.1. Pre-Defined Multicast Addresses)
|
|
// https://datatracker.ietf.org/doc/html/rfc4291#section-2.7.1
|
|
if ip.Is6() {
|
|
return ip.v6u16(0)&0xff0f == 0xff02
|
|
}
|
|
return false // zero value
|
|
}
|
|
|
|
// IsGlobalUnicast reports whether ip is a global unicast address.
|
|
//
|
|
// It returns true for IPv6 addresses which fall outside of the current
|
|
// IANA-allocated 2000::/3 global unicast space, with the exception of the
|
|
// link-local address space. It also returns true even if ip is in the IPv4
|
|
// private address space or IPv6 unique local address space. If ip is the zero
|
|
// value, it will return false.
|
|
//
|
|
// For reference, see RFC 1122, RFC 4291, and RFC 4632.
|
|
func (ip IP) IsGlobalUnicast() bool {
|
|
if ip.z == z0 {
|
|
// Invalid or zero-value.
|
|
return false
|
|
}
|
|
|
|
// Match the stdlib's IsGlobalUnicast logic. Notably private IPv4 addresses
|
|
// and ULA IPv6 addresses are still considered "global unicast".
|
|
if ip.Is4() && (ip == IPv4(0, 0, 0, 0) || ip == IPv4(255, 255, 255, 255)) {
|
|
return false
|
|
}
|
|
|
|
return ip != IPv6Unspecified() &&
|
|
!ip.IsLoopback() &&
|
|
!ip.IsMulticast() &&
|
|
!ip.IsLinkLocalUnicast()
|
|
}
|
|
|
|
// IsPrivate reports whether ip is a private address, according to RFC 1918
|
|
// (IPv4 addresses) and RFC 4193 (IPv6 addresses). That is, it reports whether
|
|
// ip is in 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16, or fc00::/7. This is the
|
|
// same as the standard library's net.IP.IsPrivate.
|
|
func (ip IP) IsPrivate() bool {
|
|
// Match the stdlib's IsPrivate logic.
|
|
if ip.Is4() {
|
|
// RFC 1918 allocates 10.0.0.0/8, 172.16.0.0/12, and 192.168.0.0/16 as
|
|
// private IPv4 address subnets.
|
|
return ip.v4(0) == 10 ||
|
|
(ip.v4(0) == 172 && ip.v4(1)&0xf0 == 16) ||
|
|
(ip.v4(0) == 192 && ip.v4(1) == 168)
|
|
}
|
|
|
|
if ip.Is6() {
|
|
// RFC 4193 allocates fc00::/7 as the unique local unicast IPv6 address
|
|
// subnet.
|
|
return ip.v6(0)&0xfe == 0xfc
|
|
}
|
|
|
|
return false // zero value
|
|
}
|
|
|
|
// IsUnspecified reports whether ip is an unspecified address, either the IPv4
|
|
// address "0.0.0.0" or the IPv6 address "::".
|
|
//
|
|
// Note that the IP zero value is not an unspecified address. Use IsZero to
|
|
// check for the zero value instead.
|
|
func (ip IP) IsUnspecified() bool {
|
|
return ip == IPv4(0, 0, 0, 0) || ip == IPv6Unspecified()
|
|
}
|
|
|
|
// Prefix applies a CIDR mask of leading bits to IP, producing an IPPrefix
|
|
// of the specified length. If IP is the zero value, a zero-value IPPrefix and
|
|
// a nil error are returned. If bits is larger than 32 for an IPv4 address or
|
|
// 128 for an IPv6 address, an error is returned.
|
|
func (ip IP) Prefix(bits uint8) (IPPrefix, error) {
|
|
effectiveBits := bits
|
|
switch ip.z {
|
|
case z0:
|
|
return IPPrefix{}, nil
|
|
case z4:
|
|
if bits > 32 {
|
|
return IPPrefix{}, fmt.Errorf("prefix length %d too large for IPv4", bits)
|
|
}
|
|
effectiveBits += 96
|
|
default:
|
|
if bits > 128 {
|
|
return IPPrefix{}, fmt.Errorf("prefix length %d too large for IPv6", bits)
|
|
}
|
|
}
|
|
ip.addr = ip.addr.and(mask6[effectiveBits])
|
|
return IPPrefixFrom(ip, bits), nil
|
|
}
|
|
|
|
// Netmask applies a bit mask to IP, producing an IPPrefix. If IP is the
|
|
// zero value, a zero-value IPPrefix and a nil error are returned. If the
|
|
// netmask length is not 4 for IPv4 or 16 for IPv6, an error is
|
|
// returned. If the netmask is non-contiguous, an error is returned.
|
|
func (ip IP) Netmask(mask []byte) (IPPrefix, error) {
|
|
l := len(mask)
|
|
|
|
switch ip.z {
|
|
case z0:
|
|
return IPPrefix{}, nil
|
|
case z4:
|
|
if l != net.IPv4len {
|
|
return IPPrefix{}, fmt.Errorf("netmask length %d incorrect for IPv4", l)
|
|
}
|
|
default:
|
|
if l != net.IPv6len {
|
|
return IPPrefix{}, fmt.Errorf("netmask length %d incorrect for IPv6", l)
|
|
}
|
|
}
|
|
|
|
ones, bits := net.IPMask(mask).Size()
|
|
if ones == 0 && bits == 0 {
|
|
return IPPrefix{}, errors.New("netmask is non-contiguous")
|
|
}
|
|
|
|
return ip.Prefix(uint8(ones))
|
|
}
|
|
|
|
// As16 returns the IP address in its 16 byte representation.
|
|
// IPv4 addresses are returned in their v6-mapped form.
|
|
// IPv6 addresses with zones are returned without their zone (use the
|
|
// Zone method to get it).
|
|
// The ip zero value returns all zeroes.
|
|
func (ip IP) As16() [16]byte {
|
|
var ret [16]byte
|
|
binary.BigEndian.PutUint64(ret[:8], ip.addr.hi)
|
|
binary.BigEndian.PutUint64(ret[8:], ip.addr.lo)
|
|
return ret
|
|
}
|
|
|
|
// As4 returns an IPv4 or IPv4-in-IPv6 address in its 4 byte representation.
|
|
// If ip is the IP zero value or an IPv6 address, As4 panics.
|
|
// Note that 0.0.0.0 is not the zero value.
|
|
func (ip IP) As4() [4]byte {
|
|
if ip.z == z4 || ip.Is4in6() {
|
|
var ret [4]byte
|
|
binary.BigEndian.PutUint32(ret[:], uint32(ip.addr.lo))
|
|
return ret
|
|
}
|
|
if ip.z == z0 {
|
|
panic("As4 called on IP zero value")
|
|
}
|
|
panic("As4 called on IPv6 address")
|
|
}
|
|
|
|
// Next returns the IP following ip.
|
|
// If there is none, it returns the IP zero value.
|
|
func (ip IP) Next() IP {
|
|
ip.addr = ip.addr.addOne()
|
|
if ip.Is4() {
|
|
if uint32(ip.addr.lo) == 0 {
|
|
// Overflowed.
|
|
return IP{}
|
|
}
|
|
} else {
|
|
if ip.addr.isZero() {
|
|
// Overflowed
|
|
return IP{}
|
|
}
|
|
}
|
|
return ip
|
|
}
|
|
|
|
// Prior returns the IP before ip.
|
|
// If there is none, it returns the IP zero value.
|
|
func (ip IP) Prior() IP {
|
|
if ip.Is4() {
|
|
if uint32(ip.addr.lo) == 0 {
|
|
return IP{}
|
|
}
|
|
} else if ip.addr.isZero() {
|
|
return IP{}
|
|
}
|
|
ip.addr = ip.addr.subOne()
|
|
return ip
|
|
}
|
|
|
|
// String returns the string form of the IP address ip.
|
|
// It returns one of 4 forms:
|
|
//
|
|
// - "invalid IP", if ip is the zero value
|
|
// - IPv4 dotted decimal ("192.0.2.1")
|
|
// - IPv6 ("2001:db8::1")
|
|
// - IPv6 with zone ("fe80:db8::1%eth0")
|
|
//
|
|
// Note that unlike the Go standard library's IP.String method,
|
|
// IP4-mapped IPv6 addresses do not format as dotted decimals.
|
|
func (ip IP) String() string {
|
|
switch ip.z {
|
|
case z0:
|
|
return "zero IP"
|
|
case z4:
|
|
return ip.string4()
|
|
default:
|
|
return ip.string6()
|
|
}
|
|
}
|
|
|
|
// AppendTo appends a text encoding of ip,
|
|
// as generated by MarshalText,
|
|
// to b and returns the extended buffer.
|
|
func (ip IP) AppendTo(b []byte) []byte {
|
|
switch ip.z {
|
|
case z0:
|
|
return b
|
|
case z4:
|
|
return ip.appendTo4(b)
|
|
default:
|
|
return ip.appendTo6(b)
|
|
}
|
|
}
|
|
|
|
// digits is a string of the hex digits from 0 to f. It's used in
|
|
// appendDecimal and appendHex to format IP addresses.
|
|
const digits = "0123456789abcdef"
|
|
|
|
// appendDecimal appends the decimal string representation of x to b.
|
|
func appendDecimal(b []byte, x uint8) []byte {
|
|
// Using this function rather than strconv.AppendUint makes IPv4
|
|
// string building 2x faster.
|
|
|
|
if x >= 100 {
|
|
b = append(b, digits[x/100])
|
|
}
|
|
if x >= 10 {
|
|
b = append(b, digits[x/10%10])
|
|
}
|
|
return append(b, digits[x%10])
|
|
}
|
|
|
|
// appendHex appends the hex string representation of x to b.
|
|
func appendHex(b []byte, x uint16) []byte {
|
|
// Using this function rather than strconv.AppendUint makes IPv6
|
|
// string building 2x faster.
|
|
|
|
if x >= 0x1000 {
|
|
b = append(b, digits[x>>12])
|
|
}
|
|
if x >= 0x100 {
|
|
b = append(b, digits[x>>8&0xf])
|
|
}
|
|
if x >= 0x10 {
|
|
b = append(b, digits[x>>4&0xf])
|
|
}
|
|
return append(b, digits[x&0xf])
|
|
}
|
|
|
|
// appendHexPad appends the fully padded hex string representation of x to b.
|
|
func appendHexPad(b []byte, x uint16) []byte {
|
|
return append(b, digits[x>>12], digits[x>>8&0xf], digits[x>>4&0xf], digits[x&0xf])
|
|
}
|
|
|
|
func (ip IP) string4() string {
|
|
const max = len("255.255.255.255")
|
|
ret := make([]byte, 0, max)
|
|
ret = ip.appendTo4(ret)
|
|
return string(ret)
|
|
}
|
|
|
|
func (ip IP) appendTo4(ret []byte) []byte {
|
|
ret = appendDecimal(ret, ip.v4(0))
|
|
ret = append(ret, '.')
|
|
ret = appendDecimal(ret, ip.v4(1))
|
|
ret = append(ret, '.')
|
|
ret = appendDecimal(ret, ip.v4(2))
|
|
ret = append(ret, '.')
|
|
ret = appendDecimal(ret, ip.v4(3))
|
|
return ret
|
|
}
|
|
|
|
// string6 formats ip in IPv6 textual representation. It follows the
|
|
// guidelines in section 4 of RFC 5952
|
|
// (https://tools.ietf.org/html/rfc5952#section-4): no unnecessary
|
|
// zeros, use :: to elide the longest run of zeros, and don't use ::
|
|
// to compact a single zero field.
|
|
func (ip IP) string6() string {
|
|
// Use a zone with a "plausibly long" name, so that most zone-ful
|
|
// IP addresses won't require additional allocation.
|
|
//
|
|
// The compiler does a cool optimization here, where ret ends up
|
|
// stack-allocated and so the only allocation this function does
|
|
// is to construct the returned string. As such, it's okay to be a
|
|
// bit greedy here, size-wise.
|
|
const max = len("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff%enp5s0")
|
|
ret := make([]byte, 0, max)
|
|
ret = ip.appendTo6(ret)
|
|
return string(ret)
|
|
}
|
|
|
|
func (ip IP) appendTo6(ret []byte) []byte {
|
|
zeroStart, zeroEnd := uint8(255), uint8(255)
|
|
for i := uint8(0); i < 8; i++ {
|
|
j := i
|
|
for j < 8 && ip.v6u16(j) == 0 {
|
|
j++
|
|
}
|
|
if l := j - i; l >= 2 && l > zeroEnd-zeroStart {
|
|
zeroStart, zeroEnd = i, j
|
|
}
|
|
}
|
|
|
|
for i := uint8(0); i < 8; i++ {
|
|
if i == zeroStart {
|
|
ret = append(ret, ':', ':')
|
|
i = zeroEnd
|
|
if i >= 8 {
|
|
break
|
|
}
|
|
} else if i > 0 {
|
|
ret = append(ret, ':')
|
|
}
|
|
|
|
ret = appendHex(ret, ip.v6u16(i))
|
|
}
|
|
|
|
if ip.z != z6noz {
|
|
ret = append(ret, '%')
|
|
ret = append(ret, ip.Zone()...)
|
|
}
|
|
return ret
|
|
}
|
|
|
|
// StringExpanded is like String but IPv6 addresses are expanded with leading
|
|
// zeroes and no "::" compression. For example, "2001:db8::1" becomes
|
|
// "2001:0db8:0000:0000:0000:0000:0000:0001".
|
|
func (ip IP) StringExpanded() string {
|
|
switch ip.z {
|
|
case z0, z4:
|
|
return ip.String()
|
|
}
|
|
|
|
const size = len("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff")
|
|
ret := make([]byte, 0, size)
|
|
for i := uint8(0); i < 8; i++ {
|
|
if i > 0 {
|
|
ret = append(ret, ':')
|
|
}
|
|
|
|
ret = appendHexPad(ret, ip.v6u16(i))
|
|
}
|
|
|
|
if ip.z != z6noz {
|
|
// The addition of a zone will cause a second allocation, but when there
|
|
// is no zone the ret slice will be stack allocated.
|
|
ret = append(ret, '%')
|
|
ret = append(ret, ip.Zone()...)
|
|
}
|
|
return string(ret)
|
|
}
|
|
|
|
// MarshalText implements the encoding.TextMarshaler interface,
|
|
// The encoding is the same as returned by String, with one exception:
|
|
// If ip is the zero value, the encoding is the empty string.
|
|
func (ip IP) MarshalText() ([]byte, error) {
|
|
switch ip.z {
|
|
case z0:
|
|
return []byte(""), nil
|
|
case z4:
|
|
max := len("255.255.255.255")
|
|
b := make([]byte, 0, max)
|
|
return ip.appendTo4(b), nil
|
|
default:
|
|
max := len("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff%enp5s0")
|
|
b := make([]byte, 0, max)
|
|
return ip.appendTo6(b), nil
|
|
}
|
|
}
|
|
|
|
// UnmarshalText implements the encoding.TextUnmarshaler interface.
|
|
// The IP address is expected in a form accepted by ParseIP.
|
|
// It returns an error if *ip is not the IP zero value.
|
|
func (ip *IP) UnmarshalText(text []byte) error {
|
|
if ip.z != z0 {
|
|
return errors.New("refusing to Unmarshal into non-zero IP")
|
|
}
|
|
if len(text) == 0 {
|
|
return nil
|
|
}
|
|
var err error
|
|
*ip, err = ParseIP(string(text))
|
|
return err
|
|
}
|
|
|
|
// MarshalBinary implements the encoding.BinaryMarshaler interface.
|
|
func (ip IP) MarshalBinary() ([]byte, error) {
|
|
switch ip.z {
|
|
case z0:
|
|
return nil, nil
|
|
case z4:
|
|
b := ip.As4()
|
|
return b[:], nil
|
|
default:
|
|
b16 := ip.As16()
|
|
b := b16[:]
|
|
if z := ip.Zone(); z != "" {
|
|
b = append(b, []byte(z)...)
|
|
}
|
|
return b, nil
|
|
}
|
|
}
|
|
|
|
// UnmarshalBinary implements the encoding.BinaryUnmarshaler interface.
|
|
func (ip *IP) UnmarshalBinary(b []byte) error {
|
|
if ip.z != z0 {
|
|
return errors.New("refusing to Unmarshal into non-zero IP")
|
|
}
|
|
n := len(b)
|
|
switch {
|
|
case n == 0:
|
|
return nil
|
|
case n == 4:
|
|
*ip = IPv4(b[0], b[1], b[2], b[3])
|
|
return nil
|
|
case n == 16:
|
|
*ip = ipv6Slice(b)
|
|
return nil
|
|
case n > 16:
|
|
*ip = ipv6Slice(b[:16]).WithZone(string(b[16:]))
|
|
return nil
|
|
}
|
|
return fmt.Errorf("unexpected ip size: %v", len(b))
|
|
}
|
|
|
|
// IPPort is an IP and a port number.
|
|
type IPPort struct {
|
|
ip IP
|
|
port uint16
|
|
}
|
|
|
|
// IPPortFrom returns an IPPort with IP ip and port port.
|
|
// It does not allocate.
|
|
func IPPortFrom(ip IP, port uint16) IPPort { return IPPort{ip: ip, port: port} }
|
|
|
|
// WithIP returns an IPPort with IP ip and port p.Port().
|
|
func (p IPPort) WithIP(ip IP) IPPort { return IPPort{ip: ip, port: p.port} }
|
|
|
|
// WithIP returns an IPPort with IP p.IP() and port port.
|
|
func (p IPPort) WithPort(port uint16) IPPort { return IPPort{ip: p.ip, port: port} }
|
|
|
|
// IP returns p's IP.
|
|
func (p IPPort) IP() IP { return p.ip }
|
|
|
|
// Port returns p's port.
|
|
func (p IPPort) Port() uint16 { return p.port }
|
|
|
|
// splitIPPort splits s into an IP address string and a port
|
|
// string. It splits strings shaped like "foo:bar" or "[foo]:bar",
|
|
// without further validating the substrings. v6 indicates whether the
|
|
// ip string should parse as an IPv6 address or an IPv4 address, in
|
|
// order for s to be a valid ip:port string.
|
|
func splitIPPort(s string) (ip, port string, v6 bool, err error) {
|
|
i := strings.LastIndexByte(s, ':')
|
|
if i == -1 {
|
|
return "", "", false, errors.New("not an ip:port")
|
|
}
|
|
|
|
ip, port = s[:i], s[i+1:]
|
|
if len(ip) == 0 {
|
|
return "", "", false, errors.New("no IP")
|
|
}
|
|
if len(port) == 0 {
|
|
return "", "", false, errors.New("no port")
|
|
}
|
|
if ip[0] == '[' {
|
|
if len(ip) < 2 || ip[len(ip)-1] != ']' {
|
|
return "", "", false, errors.New("missing ]")
|
|
}
|
|
ip = ip[1 : len(ip)-1]
|
|
v6 = true
|
|
}
|
|
|
|
return ip, port, v6, nil
|
|
}
|
|
|
|
// ParseIPPort parses s as an IPPort.
|
|
//
|
|
// It doesn't do any name resolution, and ports must be numeric.
|
|
func ParseIPPort(s string) (IPPort, error) {
|
|
var ipp IPPort
|
|
ip, port, v6, err := splitIPPort(s)
|
|
if err != nil {
|
|
return ipp, err
|
|
}
|
|
port16, err := strconv.ParseUint(port, 10, 16)
|
|
if err != nil {
|
|
return ipp, fmt.Errorf("invalid port %q parsing %q", port, s)
|
|
}
|
|
ipp.port = uint16(port16)
|
|
ipp.ip, err = ParseIP(ip)
|
|
if err != nil {
|
|
return IPPort{}, err
|
|
}
|
|
if v6 && ipp.ip.Is4() {
|
|
return IPPort{}, fmt.Errorf("invalid ip:port %q, square brackets can only be used with IPv6 addresses", s)
|
|
} else if !v6 && ipp.ip.Is6() {
|
|
return IPPort{}, fmt.Errorf("invalid ip:port %q, IPv6 addresses must be surrounded by square brackets", s)
|
|
}
|
|
return ipp, nil
|
|
}
|
|
|
|
// MustParseIPPort calls ParseIPPort(s) and panics on error.
|
|
// It is intended for use in tests with hard-coded strings.
|
|
func MustParseIPPort(s string) IPPort {
|
|
ip, err := ParseIPPort(s)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
return ip
|
|
}
|
|
|
|
// IsZero reports whether p is its zero value.
|
|
func (p IPPort) IsZero() bool { return p == IPPort{} }
|
|
|
|
// IsValid reports whether p.IP() is valid.
|
|
// All ports are valid, including zero.
|
|
func (p IPPort) IsValid() bool { return p.ip.IsValid() }
|
|
|
|
// Valid reports whether p.IP() is valid.
|
|
// All ports are valid, including zero.
|
|
//
|
|
// Deprecated: use the correctly named and identical IsValid method instead.
|
|
func (p IPPort) Valid() bool { return p.IsValid() }
|
|
|
|
func (p IPPort) String() string {
|
|
switch p.ip.z {
|
|
case z0:
|
|
return "invalid IPPort"
|
|
case z4:
|
|
a := p.ip.As4()
|
|
return fmt.Sprintf("%d.%d.%d.%d:%d", a[0], a[1], a[2], a[3], p.port)
|
|
default:
|
|
// TODO: this could be more efficient allocation-wise:
|
|
return net.JoinHostPort(p.ip.String(), strconv.Itoa(int(p.port)))
|
|
}
|
|
}
|
|
|
|
// AppendTo appends a text encoding of p,
|
|
// as generated by MarshalText,
|
|
// to b and returns the extended buffer.
|
|
func (p IPPort) AppendTo(b []byte) []byte {
|
|
switch p.ip.z {
|
|
case z0:
|
|
return b
|
|
case z4:
|
|
b = p.ip.appendTo4(b)
|
|
default:
|
|
b = append(b, '[')
|
|
b = p.ip.appendTo6(b)
|
|
b = append(b, ']')
|
|
}
|
|
b = append(b, ':')
|
|
b = strconv.AppendInt(b, int64(p.port), 10)
|
|
return b
|
|
}
|
|
|
|
// MarshalText implements the encoding.TextMarshaler interface. The
|
|
// encoding is the same as returned by String, with one exception: if
|
|
// p.IP() is the zero value, the encoding is the empty string.
|
|
func (p IPPort) MarshalText() ([]byte, error) {
|
|
var max int
|
|
switch p.ip.z {
|
|
case z0:
|
|
case z4:
|
|
max = len("255.255.255.255:65535")
|
|
default:
|
|
max = len("[ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff%enp5s0]:65535")
|
|
}
|
|
b := make([]byte, 0, max)
|
|
b = p.AppendTo(b)
|
|
return b, nil
|
|
}
|
|
|
|
// UnmarshalText implements the encoding.TextUnmarshaler
|
|
// interface. The IPPort is expected in a form accepted by
|
|
// ParseIPPort. It returns an error if *p is not the IPPort zero
|
|
// value.
|
|
func (p *IPPort) UnmarshalText(text []byte) error {
|
|
if p.ip.z != z0 || p.port != 0 {
|
|
return errors.New("refusing to Unmarshal into non-zero IPPort")
|
|
}
|
|
if len(text) == 0 {
|
|
return nil
|
|
}
|
|
var err error
|
|
*p, err = ParseIPPort(string(text))
|
|
return err
|
|
}
|
|
|
|
// FromStdAddr maps the components of a standard library TCPAddr or
|
|
// UDPAddr into an IPPort.
|
|
func FromStdAddr(stdIP net.IP, port int, zone string) (_ IPPort, ok bool) {
|
|
ip, ok := FromStdIP(stdIP)
|
|
if !ok || port < 0 || port > math.MaxUint16 {
|
|
return
|
|
}
|
|
ip = ip.Unmap()
|
|
if zone != "" {
|
|
if ip.Is4() {
|
|
ok = false
|
|
return
|
|
}
|
|
ip = ip.WithZone(zone)
|
|
}
|
|
return IPPort{ip: ip, port: uint16(port)}, true
|
|
}
|
|
|
|
// UDPAddr returns a standard library net.UDPAddr from p.
|
|
// The returned value is always non-nil. If p.IP() is the zero
|
|
// value, then UDPAddr.IP is nil.
|
|
//
|
|
// UDPAddr necessarily does two allocations. If you have an existing
|
|
// UDPAddr already allocated, see UDPAddrAt.
|
|
func (p IPPort) UDPAddr() *net.UDPAddr {
|
|
ret := &net.UDPAddr{
|
|
Port: int(p.port),
|
|
}
|
|
ret.IP, ret.Zone = p.ip.ipZone(nil)
|
|
return ret
|
|
}
|
|
|
|
// UDPAddrAt is like UDPAddr, but reuses the provided UDPAddr, which
|
|
// must be non-nil. If at.IP has a capacity of 16, UDPAddrAt is
|
|
// allocation-free. It returns at to facilitate using this method as a
|
|
// wrapper.
|
|
func (p IPPort) UDPAddrAt(at *net.UDPAddr) *net.UDPAddr {
|
|
at.Port = int(p.port)
|
|
at.IP, at.Zone = p.ip.ipZone(at.IP)
|
|
return at
|
|
}
|
|
|
|
// TCPAddr returns a standard library net.TCPAddr from p.
|
|
// The returned value is always non-nil. If p.IP() is the zero
|
|
// value, then TCPAddr.IP is nil.
|
|
func (p IPPort) TCPAddr() *net.TCPAddr {
|
|
ip, zone := p.ip.ipZone(nil)
|
|
return &net.TCPAddr{
|
|
IP: ip,
|
|
Port: int(p.port),
|
|
Zone: zone,
|
|
}
|
|
}
|
|
|
|
// IPPrefix is an IP address prefix (CIDR) representing an IP network.
|
|
//
|
|
// The first Bits() of IP() are specified. The remaining bits match any address.
|
|
// The range of Bits() is [0,32] for IPv4 or [0,128] for IPv6.
|
|
type IPPrefix struct {
|
|
ip IP
|
|
bits uint8
|
|
}
|
|
|
|
// IPPrefixFrom returns an IPPrefix with IP ip and provided bits prefix length.
|
|
// It does not allocate.
|
|
func IPPrefixFrom(ip IP, bits uint8) IPPrefix {
|
|
return IPPrefix{
|
|
ip: ip.withoutZone(),
|
|
bits: bits,
|
|
}
|
|
}
|
|
|
|
// IP returns p's IP.
|
|
func (p IPPrefix) IP() IP { return p.ip }
|
|
|
|
// Bits returns p's prefix length.
|
|
func (p IPPrefix) Bits() uint8 { return p.bits }
|
|
|
|
// IsValid reports whether whether p.Bits() has a valid range for p.IP().
|
|
// If p.IP() is zero, Valid returns false.
|
|
func (p IPPrefix) IsValid() bool { return !p.ip.IsZero() && p.bits <= p.ip.BitLen() }
|
|
|
|
// Valid reports whether whether p.Bits() has a valid range for p.IP().
|
|
// If p.IP() is zero, Valid returns false.
|
|
//
|
|
// Deprecated: use the correctly named and identical IsValid method instead.
|
|
func (p IPPrefix) Valid() bool { return p.IsValid() }
|
|
|
|
// IsZero reports whether p is its zero value.
|
|
func (p IPPrefix) IsZero() bool { return p == IPPrefix{} }
|
|
|
|
// IsSingleIP reports whether p contains exactly one IP.
|
|
func (p IPPrefix) IsSingleIP() bool { return p.bits != 0 && p.bits == p.ip.BitLen() }
|
|
|
|
// FromStdIPNet returns an IPPrefix from the standard library's IPNet type.
|
|
// If std is invalid, ok is false.
|
|
func FromStdIPNet(std *net.IPNet) (prefix IPPrefix, ok bool) {
|
|
ip, ok := FromStdIP(std.IP)
|
|
if !ok {
|
|
return IPPrefix{}, false
|
|
}
|
|
|
|
if l := len(std.Mask); l != net.IPv4len && l != net.IPv6len {
|
|
// Invalid mask.
|
|
return IPPrefix{}, false
|
|
}
|
|
|
|
ones, bits := std.Mask.Size()
|
|
if ones == 0 && bits == 0 {
|
|
// IPPrefix does not support non-contiguous masks.
|
|
return IPPrefix{}, false
|
|
}
|
|
|
|
return IPPrefix{
|
|
ip: ip,
|
|
bits: uint8(ones),
|
|
}, true
|
|
}
|
|
|
|
// ParseIPPrefix parses s as an IP address prefix.
|
|
// The string can be in the form "192.168.1.0/24" or "2001::db8::/32",
|
|
// the CIDR notation defined in RFC 4632 and RFC 4291.
|
|
//
|
|
// Note that masked address bits are not zeroed. Use Masked for that.
|
|
func ParseIPPrefix(s string) (IPPrefix, error) {
|
|
i := strings.LastIndexByte(s, '/')
|
|
if i < 0 {
|
|
return IPPrefix{}, fmt.Errorf("netaddr.ParseIPPrefix(%q): no '/'", s)
|
|
}
|
|
ip, err := ParseIP(s[:i])
|
|
if err != nil {
|
|
return IPPrefix{}, fmt.Errorf("netaddr.ParseIPPrefix(%q): %v", s, err)
|
|
}
|
|
s = s[i+1:]
|
|
bits, err := strconv.Atoi(s)
|
|
if err != nil {
|
|
return IPPrefix{}, fmt.Errorf("netaddr.ParseIPPrefix(%q): bad prefix: %v", s, err)
|
|
}
|
|
maxBits := 32
|
|
if ip.Is6() {
|
|
maxBits = 128
|
|
}
|
|
if bits < 0 || bits > maxBits {
|
|
return IPPrefix{}, fmt.Errorf("netaddr.ParseIPPrefix(%q): prefix length out of range", s)
|
|
}
|
|
return IPPrefixFrom(ip, uint8(bits)), nil
|
|
}
|
|
|
|
// MustParseIPPrefix calls ParseIPPrefix(s) and panics on error.
|
|
// It is intended for use in tests with hard-coded strings.
|
|
func MustParseIPPrefix(s string) IPPrefix {
|
|
ip, err := ParseIPPrefix(s)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
return ip
|
|
}
|
|
|
|
// Masked returns p in its canonical form, with bits of p.IP() not in p.Bits() masked off.
|
|
// If p is zero or otherwise invalid, Masked returns the zero value.
|
|
func (p IPPrefix) Masked() IPPrefix {
|
|
if m, err := p.ip.Prefix(p.bits); err == nil {
|
|
return m
|
|
}
|
|
return IPPrefix{}
|
|
}
|
|
|
|
// Range returns the inclusive range of IPs that p covers.
|
|
//
|
|
// If p is zero or otherwise invalid, Range returns the zero value.
|
|
func (p IPPrefix) Range() IPRange {
|
|
p = p.Masked()
|
|
if p.IsZero() {
|
|
return IPRange{}
|
|
}
|
|
return IPRangeFrom(p.ip, p.lastIP())
|
|
}
|
|
|
|
// IPNet returns the net.IPNet representation of an IPPrefix.
|
|
// The returned value is always non-nil.
|
|
// Any zone identifier is dropped in the conversion.
|
|
func (p IPPrefix) IPNet() *net.IPNet {
|
|
if !p.IsValid() {
|
|
return &net.IPNet{}
|
|
}
|
|
stdIP, _ := p.ip.ipZone(nil)
|
|
return &net.IPNet{
|
|
IP: stdIP,
|
|
Mask: net.CIDRMask(int(p.bits), int(p.ip.BitLen())),
|
|
}
|
|
}
|
|
|
|
// Contains reports whether the network p includes ip.
|
|
//
|
|
// An IPv4 address will not match an IPv6 prefix.
|
|
// A v6-mapped IPv6 address will not match an IPv4 prefix.
|
|
// A zero-value IP will not match any prefix.
|
|
// If ip has an IPv6 zone, Contains returns false,
|
|
// because IPPrefixes strip zones.
|
|
func (p IPPrefix) Contains(ip IP) bool {
|
|
if !p.IsValid() || ip.hasZone() {
|
|
return false
|
|
}
|
|
if f1, f2 := p.ip.BitLen(), ip.BitLen(); f1 == 0 || f2 == 0 || f1 != f2 {
|
|
return false
|
|
}
|
|
if ip.Is4() {
|
|
// xor the IP addresses together; mismatched bits are now ones.
|
|
// Shift away the number of bits we don't care about.
|
|
// Shifts in Go are more efficient if the compiler can prove
|
|
// that the shift amount is smaller than the width of the shifted type (64 here).
|
|
// We know that p.bits is in the range 0..32 because p is Valid;
|
|
// the compiler doesn't know that, so mask with 63 to help it.
|
|
// Now truncate to 32 bits, because this is IPv4.
|
|
// If all the bits we care about are equal, the result will be zero.
|
|
return uint32((ip.addr.lo^p.ip.addr.lo)>>((32-p.bits)&63)) == 0
|
|
} else {
|
|
// xor the IP addresses together.
|
|
// Mask away the bits we don't care about.
|
|
// If all the bits we care about are equal, the result will be zero.
|
|
return ip.addr.xor(p.ip.addr).and(mask6[p.bits]).isZero()
|
|
}
|
|
}
|
|
|
|
// Overlaps reports whether p and o overlap at all.
|
|
//
|
|
// If p and o are of different address families or either have a zero
|
|
// IP, it reports false. Like the Contains method, a prefix with a
|
|
// v6-mapped IPv4 IP is still treated as an IPv6 mask.
|
|
//
|
|
// If either has a Bits of zero, it returns true.
|
|
func (p IPPrefix) Overlaps(o IPPrefix) bool {
|
|
if !p.IsValid() || !o.IsValid() {
|
|
return false
|
|
}
|
|
if p == o {
|
|
return true
|
|
}
|
|
if p.ip.Is4() != o.ip.Is4() {
|
|
return false
|
|
}
|
|
var minBits uint8
|
|
if p.bits < o.bits {
|
|
minBits = p.bits
|
|
} else {
|
|
minBits = o.bits
|
|
}
|
|
if minBits == 0 {
|
|
return true
|
|
}
|
|
// One of these Prefix calls might look redundant, but we don't require
|
|
// that p and o values are normalized (via IPPrefix.Masked) first,
|
|
// so the Prefix call on the one that's already minBits serves to zero
|
|
// out any remaining bits in IP.
|
|
var err error
|
|
if p, err = p.ip.Prefix(minBits); err != nil {
|
|
return false
|
|
}
|
|
if o, err = o.ip.Prefix(minBits); err != nil {
|
|
return false
|
|
}
|
|
return p.ip == o.ip
|
|
}
|
|
|
|
// AppendTo appends a text encoding of p,
|
|
// as generated by MarshalText,
|
|
// to b and returns the extended buffer.
|
|
func (p IPPrefix) AppendTo(b []byte) []byte {
|
|
if p.IsZero() {
|
|
return b
|
|
}
|
|
if !p.IsValid() {
|
|
return append(b, "invalid IPPrefix"...)
|
|
}
|
|
|
|
// p.IP is non-zero, because p is valid.
|
|
if p.ip.z == z4 {
|
|
b = p.ip.appendTo4(b)
|
|
} else {
|
|
b = p.ip.appendTo6(b)
|
|
}
|
|
|
|
b = append(b, '/')
|
|
b = appendDecimal(b, p.bits)
|
|
return b
|
|
}
|
|
|
|
// MarshalText implements the encoding.TextMarshaler interface,
|
|
// The encoding is the same as returned by String, with one exception:
|
|
// If p is the zero value, the encoding is the empty string.
|
|
func (p IPPrefix) MarshalText() ([]byte, error) {
|
|
var max int
|
|
switch p.ip.z {
|
|
case z0:
|
|
case z4:
|
|
max = len("255.255.255.255/32")
|
|
default:
|
|
max = len("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff%enp5s0/128")
|
|
}
|
|
b := make([]byte, 0, max)
|
|
b = p.AppendTo(b)
|
|
return b, nil
|
|
}
|
|
|
|
// UnmarshalText implements the encoding.TextUnmarshaler interface.
|
|
// The IP address is expected in a form accepted by ParseIPPrefix.
|
|
// It returns an error if *p is not the IPPrefix zero value.
|
|
func (p *IPPrefix) UnmarshalText(text []byte) error {
|
|
if *p != (IPPrefix{}) {
|
|
return errors.New("refusing to Unmarshal into non-zero IPPrefix")
|
|
}
|
|
if len(text) == 0 {
|
|
return nil
|
|
}
|
|
var err error
|
|
*p, err = ParseIPPrefix(string(text))
|
|
return err
|
|
}
|
|
|
|
// String returns the CIDR notation of p: "<ip>/<bits>".
|
|
func (p IPPrefix) String() string {
|
|
if p.IsZero() {
|
|
return "zero IPPrefix"
|
|
}
|
|
if !p.IsValid() {
|
|
return "invalid IPPrefix"
|
|
}
|
|
return fmt.Sprintf("%s/%d", p.ip, p.bits)
|
|
}
|
|
|
|
// lastIP returns the last IP in the prefix.
|
|
func (p IPPrefix) lastIP() IP {
|
|
if !p.IsValid() {
|
|
return IP{}
|
|
}
|
|
a16 := p.ip.As16()
|
|
var off uint8
|
|
var bits uint8 = 128
|
|
if p.ip.Is4() {
|
|
off = 12
|
|
bits = 32
|
|
}
|
|
for b := p.bits; b < bits; b++ {
|
|
byteNum, bitInByte := b/8, 7-(b%8)
|
|
a16[off+byteNum] |= 1 << uint(bitInByte)
|
|
}
|
|
if p.ip.Is4() {
|
|
return IPFrom16(a16)
|
|
} else {
|
|
return IPv6Raw(a16) // doesn't unmap
|
|
}
|
|
}
|
|
|
|
// IPRange represents an inclusive range of IP addresses
|
|
// from the same address family.
|
|
//
|
|
// The From() and To() IPs are inclusive bounds, both included in the
|
|
// range.
|
|
//
|
|
// To be valid, the From() and To() values must be non-zero, have matching
|
|
// address families (IPv4 vs IPv6), and From() must be less than or equal to To().
|
|
// IPv6 zones are stripped out and ignored.
|
|
// An invalid range may be ignored.
|
|
type IPRange struct {
|
|
// from is the initial IP address in the range.
|
|
from IP
|
|
|
|
// to is the final IP address in the range.
|
|
to IP
|
|
}
|
|
|
|
// IPRangeFrom returns an IPRange from from to to.
|
|
// It does not allocate.
|
|
func IPRangeFrom(from, to IP) IPRange {
|
|
return IPRange{
|
|
from: from.withoutZone(),
|
|
to: to.withoutZone(),
|
|
}
|
|
}
|
|
|
|
// From returns the lower bound of r.
|
|
func (r IPRange) From() IP { return r.from }
|
|
|
|
// To returns the upper bound of r.
|
|
func (r IPRange) To() IP { return r.to }
|
|
|
|
// ParseIPRange parses a range out of two IPs separated by a hyphen.
|
|
//
|
|
// It returns an error if the range is not valid.
|
|
func ParseIPRange(s string) (IPRange, error) {
|
|
var r IPRange
|
|
h := strings.IndexByte(s, '-')
|
|
if h == -1 {
|
|
return r, fmt.Errorf("no hyphen in range %q", s)
|
|
}
|
|
from, to := s[:h], s[h+1:]
|
|
var err error
|
|
r.from, err = ParseIP(from)
|
|
if err != nil {
|
|
return r, fmt.Errorf("invalid From IP %q in range %q", from, s)
|
|
}
|
|
r.from = r.from.withoutZone()
|
|
r.to, err = ParseIP(to)
|
|
if err != nil {
|
|
return r, fmt.Errorf("invalid To IP %q in range %q", to, s)
|
|
}
|
|
r.to = r.to.withoutZone()
|
|
if !r.IsValid() {
|
|
return r, fmt.Errorf("range %v to %v not valid", r.from, r.to)
|
|
}
|
|
return r, nil
|
|
}
|
|
|
|
// MustParseIPRange calls ParseIPRange(s) and panics on error.
|
|
// It is intended for use in tests with hard-coded strings.
|
|
func MustParseIPRange(s string) IPRange {
|
|
r, err := ParseIPRange(s)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
return r
|
|
}
|
|
|
|
// String returns a string representation of the range.
|
|
//
|
|
// For a valid range, the form is "From-To" with a single hyphen
|
|
// separating the IPs, the same format recognized by
|
|
// ParseIPRange.
|
|
func (r IPRange) String() string {
|
|
if r.IsValid() {
|
|
return fmt.Sprintf("%s-%s", r.from, r.to)
|
|
}
|
|
if r.from.IsZero() || r.to.IsZero() {
|
|
return "zero IPRange"
|
|
}
|
|
return "invalid IPRange"
|
|
}
|
|
|
|
// AppendTo appends a text encoding of r,
|
|
// as generated by MarshalText,
|
|
// to b and returns the extended buffer.
|
|
func (r IPRange) AppendTo(b []byte) []byte {
|
|
if r.IsZero() {
|
|
return b
|
|
}
|
|
b = r.from.AppendTo(b)
|
|
b = append(b, '-')
|
|
b = r.to.AppendTo(b)
|
|
return b
|
|
}
|
|
|
|
// MarshalText implements the encoding.TextMarshaler interface,
|
|
// The encoding is the same as returned by String, with one exception:
|
|
// If ip is the zero value, the encoding is the empty string.
|
|
func (r IPRange) MarshalText() ([]byte, error) {
|
|
if r.IsZero() {
|
|
return []byte(""), nil
|
|
}
|
|
var max int
|
|
if r.from.z == z4 {
|
|
max = len("255.255.255.255-255.255.255.255")
|
|
} else {
|
|
max = len("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff-ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff")
|
|
}
|
|
b := make([]byte, 0, max)
|
|
return r.AppendTo(b), nil
|
|
}
|
|
|
|
// UnmarshalText implements the encoding.TextUnmarshaler interface.
|
|
// The IP range is expected in a form accepted by ParseIPRange.
|
|
// It returns an error if *r is not the IPRange zero value.
|
|
func (r *IPRange) UnmarshalText(text []byte) error {
|
|
if *r != (IPRange{}) {
|
|
return errors.New("refusing to Unmarshal into non-zero IPRange")
|
|
}
|
|
if len(text) == 0 {
|
|
return nil
|
|
}
|
|
var err error
|
|
*r, err = ParseIPRange(string(text))
|
|
return err
|
|
}
|
|
|
|
// IsZero reports whether r is the zero value of the IPRange type.
|
|
func (r IPRange) IsZero() bool {
|
|
return r == IPRange{}
|
|
}
|
|
|
|
// IsValid reports whether r.From() and r.To() are both non-zero and
|
|
// obey the documented requirements: address families match, and From
|
|
// is less than or equal to To.
|
|
func (r IPRange) IsValid() bool {
|
|
return !r.from.IsZero() &&
|
|
r.from.z == r.to.z &&
|
|
!r.to.Less(r.from)
|
|
}
|
|
|
|
// Valid reports whether r.From() and r.To() are both non-zero and
|
|
// obey the documented requirements: address families match, and From
|
|
// is less than or equal to To.
|
|
//
|
|
// Deprecated: use the correctly named and identical IsValid method instead.
|
|
func (r IPRange) Valid() bool { return r.IsValid() }
|
|
|
|
// Contains reports whether the range r includes addr.
|
|
//
|
|
// An invalid range always reports false.
|
|
//
|
|
// If ip has an IPv6 zone, Contains returns false,
|
|
// because IPPrefixes strip zones.
|
|
func (r IPRange) Contains(addr IP) bool {
|
|
return r.IsValid() && !addr.hasZone() && r.contains(addr)
|
|
}
|
|
|
|
// contains is like Contains, but without the validity check.
|
|
// addr must not have a zone.
|
|
func (r IPRange) contains(addr IP) bool {
|
|
return r.from.Compare(addr) <= 0 && r.to.Compare(addr) >= 0
|
|
}
|
|
|
|
// less reports whether r is "before" other. It is before if r.From()
|
|
// is before other.From(). If they're equal, then the larger range
|
|
// (higher To()) comes first.
|
|
func (r IPRange) less(other IPRange) bool {
|
|
if cmp := r.from.Compare(other.from); cmp != 0 {
|
|
return cmp < 0
|
|
}
|
|
return other.to.Less(r.to)
|
|
}
|
|
|
|
// entirelyBefore returns whether r lies entirely before other in IP
|
|
// space.
|
|
func (r IPRange) entirelyBefore(other IPRange) bool {
|
|
return r.to.Less(other.from)
|
|
}
|
|
|
|
// entirelyWithin returns whether r is entirely contained within
|
|
// other.
|
|
func (r IPRange) coveredBy(other IPRange) bool {
|
|
return other.from.lessOrEq(r.from) && r.to.lessOrEq(other.to)
|
|
}
|
|
|
|
// inMiddleOf returns whether r is inside other, but not touching the
|
|
// edges of other.
|
|
func (r IPRange) inMiddleOf(other IPRange) bool {
|
|
return other.from.Less(r.from) && r.to.Less(other.to)
|
|
}
|
|
|
|
// overlapsStartOf returns whether r entirely overlaps the start of
|
|
// other, but not all of other.
|
|
func (r IPRange) overlapsStartOf(other IPRange) bool {
|
|
return r.from.lessOrEq(other.from) && r.to.Less(other.to)
|
|
}
|
|
|
|
// overlapsEndOf returns whether r entirely overlaps the end of
|
|
// other, but not all of other.
|
|
func (r IPRange) overlapsEndOf(other IPRange) bool {
|
|
return other.from.Less(r.from) && other.to.lessOrEq(r.to)
|
|
}
|
|
|
|
// mergeIPRanges returns the minimum and sorted set of IP ranges that
|
|
// cover r.
|
|
func mergeIPRanges(rr []IPRange) (out []IPRange, valid bool) {
|
|
// Always return a copy of r, to avoid aliasing slice memory in
|
|
// the caller.
|
|
switch len(rr) {
|
|
case 0:
|
|
return nil, true
|
|
case 1:
|
|
return []IPRange{rr[0]}, true
|
|
}
|
|
|
|
sort.Slice(rr, func(i, j int) bool { return rr[i].less(rr[j]) })
|
|
out = make([]IPRange, 1, len(rr))
|
|
out[0] = rr[0]
|
|
for _, r := range rr[1:] {
|
|
prev := &out[len(out)-1]
|
|
switch {
|
|
case !r.IsValid():
|
|
// Invalid ranges make no sense to merge, refuse to
|
|
// perform.
|
|
return nil, false
|
|
case prev.to.Next() == r.from:
|
|
// prev and r touch, merge them.
|
|
//
|
|
// prev r
|
|
// f------tf-----t
|
|
prev.to = r.to
|
|
case prev.to.Less(r.from):
|
|
// No overlap and not adjacent (per previous case), no
|
|
// merging possible.
|
|
//
|
|
// prev r
|
|
// f------t f-----t
|
|
out = append(out, r)
|
|
case prev.to.Less(r.to):
|
|
// Partial overlap, update prev
|
|
//
|
|
// prev
|
|
// f------t
|
|
// f-----t
|
|
// r
|
|
prev.to = r.to
|
|
default:
|
|
// r entirely contained in prev, nothing to do.
|
|
//
|
|
// prev
|
|
// f--------t
|
|
// f-----t
|
|
// r
|
|
}
|
|
}
|
|
return out, true
|
|
}
|
|
|
|
// Overlaps reports whether p and o overlap at all.
|
|
//
|
|
// If p and o are of different address families or either are invalid,
|
|
// it reports false.
|
|
func (r IPRange) Overlaps(o IPRange) bool {
|
|
return r.IsValid() &&
|
|
o.IsValid() &&
|
|
r.from.Compare(o.to) <= 0 &&
|
|
o.from.Compare(r.to) <= 0
|
|
}
|
|
|
|
// prefixMaker returns a address-family-corrected IPPrefix from a and bits,
|
|
// where the input bits is always in the IPv6-mapped form for IPv4 addresses.
|
|
type prefixMaker func(a uint128, bits uint8) IPPrefix
|
|
|
|
// Prefixes returns the set of IPPrefix entries that covers r.
|
|
//
|
|
// If either of r's bounds are invalid, in the wrong order, or if
|
|
// they're of different address families, then Prefixes returns nil.
|
|
//
|
|
// Prefixes necessarily allocates. See AppendPrefixes for a version that uses
|
|
// memory you provide.
|
|
func (r IPRange) Prefixes() []IPPrefix {
|
|
return r.AppendPrefixes(nil)
|
|
}
|
|
|
|
// AppendPrefixes is an append version of IPRange.Prefixes. It appends
|
|
// the IPPrefix entries that cover r to dst.
|
|
func (r IPRange) AppendPrefixes(dst []IPPrefix) []IPPrefix {
|
|
if !r.IsValid() {
|
|
return nil
|
|
}
|
|
return appendRangePrefixes(dst, r.prefixFrom128AndBits, r.from.addr, r.to.addr)
|
|
}
|
|
|
|
func (r IPRange) prefixFrom128AndBits(a uint128, bits uint8) IPPrefix {
|
|
ip := IP{addr: a, z: r.from.z}
|
|
if r.from.Is4() {
|
|
bits -= 12 * 8
|
|
}
|
|
return IPPrefix{ip, bits}
|
|
}
|
|
|
|
// aZeroBSet is whether, after the common bits, a is all zero bits and
|
|
// b is all set (one) bits.
|
|
func comparePrefixes(a, b uint128) (common uint8, aZeroBSet bool) {
|
|
common = a.commonPrefixLen(b)
|
|
|
|
// See whether a and b, after their common shared bits, end
|
|
// in all zero bits or all one bits, respectively.
|
|
if common == 128 {
|
|
return common, true
|
|
}
|
|
|
|
m := mask6[common]
|
|
return common, (a.xor(a.and(m)).isZero() &&
|
|
b.or(m) == uint128{^uint64(0), ^uint64(0)})
|
|
}
|
|
|
|
// Prefix returns r as an IPPrefix, if it can be presented exactly as such.
|
|
// If r is not valid or is not exactly equal to one prefix, ok is false.
|
|
func (r IPRange) Prefix() (p IPPrefix, ok bool) {
|
|
if !r.IsValid() {
|
|
return
|
|
}
|
|
if common, ok := comparePrefixes(r.from.addr, r.to.addr); ok {
|
|
return r.prefixFrom128AndBits(r.from.addr, common), true
|
|
}
|
|
return
|
|
}
|
|
|
|
func appendRangePrefixes(dst []IPPrefix, makePrefix prefixMaker, a, b uint128) []IPPrefix {
|
|
common, ok := comparePrefixes(a, b)
|
|
if ok {
|
|
// a to b represents a whole range, like 10.50.0.0/16.
|
|
// (a being 10.50.0.0 and b being 10.50.255.255)
|
|
return append(dst, makePrefix(a, common))
|
|
}
|
|
// Otherwise recursively do both halves.
|
|
dst = appendRangePrefixes(dst, makePrefix, a, a.bitsSetFrom(common+1))
|
|
dst = appendRangePrefixes(dst, makePrefix, b.bitsClearedFrom(common+1), b)
|
|
return dst
|
|
}
|
|
|