// Copyright 2020 The Go 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 netip defines an IP address type that's a small value type. // Building on that [Addr] type, the package also defines [AddrPort] (an // IP address and a port) and [Prefix] (an IP address and a bit length // prefix). // // Compared to the [net.IP] type, [Addr] type takes less memory, is immutable, // and is comparable (supports == and being a map key).
package netip import ( ) // 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 // netip.Addr: 24 bytes (zone is per-name singleton, shared across all users) // Addr represents an IPv4 or IPv6 address (with or without a scoped // addressing zone), similar to [net.IP] or [net.IPAddr]. // // Unlike [net.IP] or [net.IPAddr], Addr is a comparable value // type (it supports == and can be a map key) and is immutable. // // The zero Addr is not a valid IP address. // Addr{} is distinct from both 0.0.0.0 and ::. type Addr struct { // addr is 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 a zero Addr). // 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 Addr.z values. // See the Addr 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 () Addr { return AddrFrom16([16]byte{0: 0xff, 1: 0x02, 15: 0x01}) } // IPv6LinkLocalAllRouters returns the IPv6 link-local all routers multicast // address ff02::2. func () Addr { return AddrFrom16([16]byte{0: 0xff, 1: 0x02, 15: 0x02}) } // IPv6Loopback returns the IPv6 loopback address ::1. func () Addr { return AddrFrom16([16]byte{15: 0x01}) } // IPv6Unspecified returns the IPv6 unspecified address "::". func () Addr { return Addr{z: z6noz} } // IPv4Unspecified returns the IPv4 unspecified address "0.0.0.0". func () Addr { return AddrFrom4([4]byte{}) } // AddrFrom4 returns the address of the IPv4 address given by the bytes in addr. func ( [4]byte) Addr { return Addr{ addr: uint128{0, 0xffff00000000 | uint64([0])<<24 | uint64([1])<<16 | uint64([2])<<8 | uint64([3])}, z: z4, } } // AddrFrom16 returns the IPv6 address given by the bytes in addr. // An IPv4-mapped IPv6 address is left as an IPv6 address. // (Use Unmap to convert them if needed.) func ( [16]byte) Addr { return Addr{ addr: uint128{ beUint64([:8]), beUint64([8:]), }, z: z6noz, } } // ParseAddr 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 ( string) (Addr, error) { for := 0; < len(); ++ { switch [] { case '.': return parseIPv4() case ':': return parseIPv6() case '%': // Assume that this was trying to be an IPv6 address with // a zone specifier, but the address is missing. return Addr{}, parseAddrError{in: , msg: "missing IPv6 address"} } } return Addr{}, parseAddrError{in: , msg: "unable to parse IP"} } // MustParseAddr calls [ParseAddr](s) and panics on error. // It is intended for use in tests with hard-coded strings. func ( string) Addr { , := ParseAddr() if != nil { panic() } return } type parseAddrError struct { in string // the string given to ParseAddr msg string // an explanation of the parse failure at string // optionally, the unparsed portion of in at which the error occurred. } func ( parseAddrError) () string { := strconv.Quote if .at != "" { return "ParseAddr(" + (.in) + "): " + .msg + " (at " + (.at) + ")" } return "ParseAddr(" + (.in) + "): " + .msg } // parseIPv4 parses s as an IPv4 address (in form "192.168.0.1"). func parseIPv4( string) ( Addr, error) { var [4]uint8 var , int var int // number of digits in current octet for := 0; < len(); ++ { if [] >= '0' && [] <= '9' { if == 1 && == 0 { return Addr{}, parseAddrError{in: , msg: "IPv4 field has octet with leading zero"} } = *10 + int([]) - '0' ++ if > 255 { return Addr{}, parseAddrError{in: , msg: "IPv4 field has value >255"} } } else if [] == '.' { // .1.2.3 // 1.2.3. // 1..2.3 if == 0 || == len()-1 || [-1] == '.' { return Addr{}, parseAddrError{in: , msg: "IPv4 field must have at least one digit", at: [:]} } // 1.2.3.4.5 if == 3 { return Addr{}, parseAddrError{in: , msg: "IPv4 address too long"} } [] = uint8() ++ = 0 = 0 } else { return Addr{}, parseAddrError{in: , msg: "unexpected character", at: [:]} } } if < 3 { return Addr{}, parseAddrError{in: , msg: "IPv4 address too short"} } [3] = uint8() return AddrFrom4(), nil } // parseIPv6 parses s as an IPv6 address (in form "2001:db8::68"). func parseIPv6( string) (Addr, error) { := // 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. := "" := bytealg.IndexByteString(, '%') if != -1 { , = [:], [+1:] if == "" { // Not allowed to have an empty zone if explicitly specified. return Addr{}, parseAddrError{in: , msg: "zone must be a non-empty string"} } } var [16]byte := -1 // position of ellipsis in ip // Might have leading ellipsis if len() >= 2 && [0] == ':' && [1] == ':' { = 0 = [2:] // Might be only ellipsis if len() == 0 { return IPv6Unspecified().WithZone(), nil } } // Loop, parsing hex numbers followed by colon. = 0 for < 16 { // Hex number. Similar to parseIPv4, inlining the hex number // parsing yields a significant performance increase. := 0 := uint32(0) for ; < len(); ++ { := [] if >= '0' && <= '9' { = ( << 4) + uint32(-'0') } else if >= 'a' && <= 'f' { = ( << 4) + uint32(-'a'+10) } else if >= 'A' && <= 'F' { = ( << 4) + uint32(-'A'+10) } else { break } if > math.MaxUint16 { // Overflow, fail. return Addr{}, parseAddrError{in: , msg: "IPv6 field has value >=2^16", at: } } } if == 0 { // No digits found, fail. return Addr{}, parseAddrError{in: , msg: "each colon-separated field must have at least one digit", at: } } // If followed by dot, might be in trailing IPv4. if < len() && [] == '.' { if < 0 && != 12 { // Not the right place. return Addr{}, parseAddrError{in: , msg: "embedded IPv4 address must replace the final 2 fields of the address", at: } } if +4 > 16 { // Not enough room. return Addr{}, parseAddrError{in: , msg: "too many hex fields to fit an embedded IPv4 at the end of the address", at: } } // 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. , := parseIPv4() if != nil { return Addr{}, parseAddrError{in: , msg: .Error(), at: } } [] = .v4(0) [+1] = .v4(1) [+2] = .v4(2) [+3] = .v4(3) = "" += 4 break } // Save this 16-bit chunk. [] = byte( >> 8) [+1] = byte() += 2 // Stop at end of string. = [:] if len() == 0 { break } // Otherwise must be followed by colon and more. if [0] != ':' { return Addr{}, parseAddrError{in: , msg: "unexpected character, want colon", at: } } else if len() == 1 { return Addr{}, parseAddrError{in: , msg: "colon must be followed by more characters", at: } } = [1:] // Look for ellipsis. if [0] == ':' { if >= 0 { // already have one return Addr{}, parseAddrError{in: , msg: "multiple :: in address", at: } } = = [1:] if len() == 0 { // can be at end break } } } // Must have used entire string. if len() != 0 { return Addr{}, parseAddrError{in: , msg: "trailing garbage after address", at: } } // If didn't parse enough, expand ellipsis. if < 16 { if < 0 { return Addr{}, parseAddrError{in: , msg: "address string too short"} } := 16 - for := - 1; >= ; -- { [+] = [] } for := + - 1; >= ; -- { [] = 0 } } else if >= 0 { // Ellipsis must represent at least one 0 group. return Addr{}, parseAddrError{in: , msg: "the :: must expand to at least one field of zeros"} } return AddrFrom16().WithZone(), nil } // AddrFromSlice parses the 4- or 16-byte byte slice as an IPv4 or IPv6 address. // Note that a [net.IP] can be passed directly as the []byte argument. // If slice's length is not 4 or 16, AddrFromSlice returns [Addr]{}, false. func ( []byte) ( Addr, bool) { switch len() { case 4: return AddrFrom4([4]byte()), true case 16: return AddrFrom16([16]byte()), true } return Addr{}, false } // v4 returns the i'th byte of ip. If ip is not an IPv4, v4 returns // unspecified garbage. func ( Addr) ( uint8) uint8 { return uint8(.addr.lo >> ((3 - ) * 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 ( Addr) ( uint8) uint8 { return uint8(*(.addr.halves()[(/8)%2]) >> ((7 - %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 ( Addr) ( uint8) uint16 { return uint16(*(.addr.halves()[(/4)%2]) >> ((3 - %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 ( Addr) () bool { // Faster than comparing ip == Addr{}, but effectively equivalent, // as there's no way to make an IP with a nil z from this package. return .z == z0 } // IsValid reports whether the [Addr] is an initialized address (not the zero Addr). // // Note that "0.0.0.0" and "::" are both valid values. func ( Addr) () bool { return .z != z0 } // BitLen returns the number of bits in the IP address: // 128 for IPv6, 32 for IPv4, and 0 for the zero [Addr]. // // Note that IPv4-mapped IPv6 addresses are considered IPv6 addresses // and therefore have bit length 128. func ( Addr) () int { switch .z { case z0: return 0 case z4: return 32 } return 128 } // Zone returns ip's IPv6 scoped addressing zone, if any. func ( Addr) () string { if .z == nil { return "" } , := .z.Get().(string) return } // 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 [Addr.Less] method. func ( Addr) ( Addr) int { , := .BitLen(), .BitLen() if < { return -1 } if > { return 1 } , := .addr.hi, .addr.hi if < { return -1 } if > { return 1 } , := .addr.lo, .addr.lo if < { return -1 } if > { return 1 } if .Is6() { , := .Zone(), .Zone() if < { return -1 } if > { 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 ( Addr) ( Addr) bool { return .Compare() == -1 } // Is4 reports whether ip is an IPv4 address. // // It returns false for IPv4-mapped IPv6 addresses. See [Addr.Unmap]. func ( Addr) () bool { return .z == z4 } // Is4In6 reports whether ip is an IPv4-mapped IPv6 address. func ( Addr) () bool { return .Is6() && .addr.hi == 0 && .addr.lo>>32 == 0xffff } // Is6 reports whether ip is an IPv6 address, including IPv4-mapped // IPv6 addresses. func ( Addr) () bool { return .z != z0 && .z != z4 } // Unmap returns ip with any IPv4-mapped IPv6 address prefix removed. // // That is, if ip is an IPv6 address wrapping an IPv4 address, it // returns the wrapped IPv4 address. Otherwise it returns ip unmodified. func ( Addr) () Addr { if .Is4In6() { .z = z4 } return } // 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, WithZone is a no-op and returns ip unchanged. func ( Addr) ( string) Addr { if !.Is6() { return } if == "" { .z = z6noz return } .z = intern.GetByString() return } // withoutZone unconditionally strips the zone from ip. // It's similar to WithZone, but small enough to be inlinable. func ( Addr) () Addr { if !.Is6() { return } .z = z6noz return } // hasZone reports whether ip has an IPv6 zone. func ( Addr) () bool { return .z != z0 && .z != z4 && .z != z6noz } // IsLinkLocalUnicast reports whether ip is a link-local unicast address. func ( Addr) () bool { // Dynamic Configuration of IPv4 Link-Local Addresses // https://datatracker.ietf.org/doc/html/rfc3927#section-2.1 if .Is4() { return .v4(0) == 169 && .v4(1) == 254 } // IP Version 6 Addressing Architecture (2.4 Address Type Identification) // https://datatracker.ietf.org/doc/html/rfc4291#section-2.4 if .Is6() { return .v6u16(0)&0xffc0 == 0xfe80 } return false // zero value } // IsLoopback reports whether ip is a loopback address. func ( Addr) () 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 .Is4() { return .v4(0) == 127 } // IP Version 6 Addressing Architecture (2.4 Address Type Identification) // https://datatracker.ietf.org/doc/html/rfc4291#section-2.4 if .Is6() { return .addr.hi == 0 && .addr.lo == 1 } return false // zero value } // IsMulticast reports whether ip is a multicast address. func ( Addr) () bool { // Host Extensions for IP Multicasting (4. HOST GROUP ADDRESSES) // https://datatracker.ietf.org/doc/html/rfc1112#section-4 if .Is4() { return .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 .Is6() { return .addr.hi>>(64-8) == 0xff // ip.v6(0) == 0xff } return false // zero value } // IsInterfaceLocalMulticast reports whether ip is an IPv6 interface-local // multicast address. func ( Addr) () bool { // IPv6 Addressing Architecture (2.7.1. Pre-Defined Multicast Addresses) // https://datatracker.ietf.org/doc/html/rfc4291#section-2.7.1 if .Is6() { return .v6u16(0)&0xff0f == 0xff01 } return false // zero value } // IsLinkLocalMulticast reports whether ip is a link-local multicast address. func ( Addr) () bool { // IPv4 Multicast Guidelines (4. Local Network Control Block (224.0.0/24)) // https://datatracker.ietf.org/doc/html/rfc5771#section-4 if .Is4() { return .v4(0) == 224 && .v4(1) == 0 && .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 .Is6() { return .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. // It returns false for the zero [Addr]. // // For reference, see RFC 1122, RFC 4291, and RFC 4632. func ( Addr) () bool { if .z == z0 { // Invalid or zero-value. return false } // Match package net's IsGlobalUnicast logic. Notably private IPv4 addresses // and ULA IPv6 addresses are still considered "global unicast". if .Is4() && ( == IPv4Unspecified() || == AddrFrom4([4]byte{255, 255, 255, 255})) { return false } return != IPv6Unspecified() && !.IsLoopback() && !.IsMulticast() && !.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 [net.IP.IsPrivate]. func ( Addr) () bool { // Match the stdlib's IsPrivate logic. if .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 .v4(0) == 10 || (.v4(0) == 172 && .v4(1)&0xf0 == 16) || (.v4(0) == 192 && .v4(1) == 168) } if .Is6() { // RFC 4193 allocates fc00::/7 as the unique local unicast IPv6 address // subnet. return .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 zero [Addr] is not an unspecified address. func ( Addr) () bool { return == IPv4Unspecified() || == IPv6Unspecified() } // Prefix keeps only the top b bits of IP, producing a Prefix // of the specified length. // If ip is a zero [Addr], Prefix always returns a zero Prefix and a nil error. // Otherwise, if bits is less than zero or greater than ip.BitLen(), // Prefix returns an error. func ( Addr) ( int) (Prefix, error) { if < 0 { return Prefix{}, errors.New("negative Prefix bits") } := switch .z { case z0: return Prefix{}, nil case z4: if > 32 { return Prefix{}, errors.New("prefix length " + itoa.Itoa() + " too large for IPv4") } += 96 default: if > 128 { return Prefix{}, errors.New("prefix length " + itoa.Itoa() + " too large for IPv6") } } .addr = .addr.and(mask6()) return PrefixFrom(, ), nil } // As16 returns the IP address in its 16-byte representation. // IPv4 addresses are returned as IPv4-mapped IPv6 addresses. // IPv6 addresses with zones are returned without their zone (use the // [Addr.Zone] method to get it). // The ip zero value returns all zeroes. func ( Addr) () ( [16]byte) { bePutUint64([:8], .addr.hi) bePutUint64([8:], .addr.lo) return } // As4 returns an IPv4 or IPv4-in-IPv6 address in its 4-byte representation. // If ip is the zero [Addr] or an IPv6 address, As4 panics. // Note that 0.0.0.0 is not the zero Addr. func ( Addr) () ( [4]byte) { if .z == z4 || .Is4In6() { bePutUint32([:], uint32(.addr.lo)) return } if .z == z0 { panic("As4 called on IP zero value") } panic("As4 called on IPv6 address") } // AsSlice returns an IPv4 or IPv6 address in its respective 4-byte or 16-byte representation. func ( Addr) () []byte { switch .z { case z0: return nil case z4: var [4]byte bePutUint32([:], uint32(.addr.lo)) return [:] default: var [16]byte bePutUint64([:8], .addr.hi) bePutUint64([8:], .addr.lo) return [:] } } // Next returns the address following ip. // If there is none, it returns the zero [Addr]. func ( Addr) () Addr { .addr = .addr.addOne() if .Is4() { if uint32(.addr.lo) == 0 { // Overflowed. return Addr{} } } else { if .addr.isZero() { // Overflowed return Addr{} } } return } // Prev returns the IP before ip. // If there is none, it returns the IP zero value. func ( Addr) () Addr { if .Is4() { if uint32(.addr.lo) == 0 { return Addr{} } } else if .addr.isZero() { return Addr{} } .addr = .addr.subOne() return } // String returns the string form of the IP address ip. // It returns one of 5 forms: // // - "invalid IP", if ip is the zero [Addr] // - IPv4 dotted decimal ("192.0.2.1") // - IPv6 ("2001:db8::1") // - "::ffff:1.2.3.4" (if [Addr.Is4In6]) // - IPv6 with zone ("fe80:db8::1%eth0") // // Note that unlike package net's IP.String method, // IPv4-mapped IPv6 addresses format with a "::ffff:" // prefix before the dotted quad. func ( Addr) () string { switch .z { case z0: return "invalid IP" case z4: return .string4() default: if .Is4In6() { if := .Zone(); != "" { return "::ffff:" + .Unmap().string4() + "%" + } else { return "::ffff:" + .Unmap().string4() } } return .string6() } } // AppendTo appends a text encoding of ip, // as generated by [Addr.MarshalText], // to b and returns the extended buffer. func ( Addr) ( []byte) []byte { switch .z { case z0: return case z4: return .appendTo4() default: if .Is4In6() { = append(, "::ffff:"...) = .Unmap().appendTo4() if := .Zone(); != "" { = append(, '%') = append(, ...) } return } return .appendTo6() } } // 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( []byte, uint8) []byte { // Using this function rather than strconv.AppendUint makes IPv4 // string building 2x faster. if >= 100 { = append(, digits[/100]) } if >= 10 { = append(, digits[/10%10]) } return append(, digits[%10]) } // appendHex appends the hex string representation of x to b. func appendHex( []byte, uint16) []byte { // Using this function rather than strconv.AppendUint makes IPv6 // string building 2x faster. if >= 0x1000 { = append(, digits[>>12]) } if >= 0x100 { = append(, digits[>>8&0xf]) } if >= 0x10 { = append(, digits[>>4&0xf]) } return append(, digits[&0xf]) } // appendHexPad appends the fully padded hex string representation of x to b. func appendHexPad( []byte, uint16) []byte { return append(, digits[>>12], digits[>>8&0xf], digits[>>4&0xf], digits[&0xf]) } func ( Addr) () string { const = len("255.255.255.255") := make([]byte, 0, ) = .appendTo4() return string() } func ( Addr) ( []byte) []byte { = appendDecimal(, .v4(0)) = append(, '.') = appendDecimal(, .v4(1)) = append(, '.') = appendDecimal(, .v4(2)) = append(, '.') = appendDecimal(, .v4(3)) return } // 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 ( Addr) () 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 = len("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff%enp5s0") := make([]byte, 0, ) = .appendTo6() return string() } func ( Addr) ( []byte) []byte { , := uint8(255), uint8(255) for := uint8(0); < 8; ++ { := for < 8 && .v6u16() == 0 { ++ } if := - ; >= 2 && > - { , = , } } for := uint8(0); < 8; ++ { if == { = append(, ':', ':') = if >= 8 { break } } else if > 0 { = append(, ':') } = appendHex(, .v6u16()) } if .z != z6noz { = append(, '%') = append(, .Zone()...) } return } // StringExpanded is like [Addr.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 ( Addr) () string { switch .z { case z0, z4: return .String() } const = len("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff") := make([]byte, 0, ) for := uint8(0); < 8; ++ { if > 0 { = append(, ':') } = appendHexPad(, .v6u16()) } if .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. = append(, '%') = append(, .Zone()...) } return string() } // MarshalText implements the [encoding.TextMarshaler] interface, // The encoding is the same as returned by [Addr.String], with one exception: // If ip is the zero [Addr], the encoding is the empty string. func ( Addr) () ([]byte, error) { switch .z { case z0: return []byte(""), nil case z4: := len("255.255.255.255") := make([]byte, 0, ) return .appendTo4(), nil default: := len("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff%enp5s0") := make([]byte, 0, ) if .Is4In6() { = append(, "::ffff:"...) = .Unmap().appendTo4() if := .Zone(); != "" { = append(, '%') = append(, ...) } return , nil } return .appendTo6(), nil } } // UnmarshalText implements the encoding.TextUnmarshaler interface. // The IP address is expected in a form accepted by [ParseAddr]. // // If text is empty, UnmarshalText sets *ip to the zero [Addr] and // returns no error. func ( *Addr) ( []byte) error { if len() == 0 { * = Addr{} return nil } var error *, = ParseAddr(string()) return } func ( Addr) ( int) []byte { var []byte switch .z { case z0: = make([]byte, ) case z4: = make([]byte, 4+) bePutUint32(, uint32(.addr.lo)) default: := .Zone() = make([]byte, 16+len()+) bePutUint64([:8], .addr.hi) bePutUint64([8:], .addr.lo) copy([16:], ) } return } // MarshalBinary implements the [encoding.BinaryMarshaler] interface. // It returns a zero-length slice for the zero [Addr], // the 4-byte form for an IPv4 address, // and the 16-byte form with zone appended for an IPv6 address. func ( Addr) () ([]byte, error) { return .marshalBinaryWithTrailingBytes(0), nil } // UnmarshalBinary implements the [encoding.BinaryUnmarshaler] interface. // It expects data in the form generated by MarshalBinary. func ( *Addr) ( []byte) error { := len() switch { case == 0: * = Addr{} return nil case == 4: * = AddrFrom4([4]byte()) return nil case == 16: * = AddrFrom16([16]byte()) return nil case > 16: * = AddrFrom16([16]byte([:16])).WithZone(string([16:])) return nil } return errors.New("unexpected slice size") } // AddrPort is an IP and a port number. type AddrPort struct { ip Addr port uint16 } // AddrPortFrom returns an [AddrPort] with the provided IP and port. // It does not allocate. func ( Addr, uint16) AddrPort { return AddrPort{ip: , port: } } // Addr returns p's IP address. func ( AddrPort) () Addr { return .ip } // Port returns p's port. func ( AddrPort) () uint16 { return .port } // splitAddrPort 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 splitAddrPort( string) (, string, bool, error) { := bytealg.LastIndexByteString(, ':') if == -1 { return "", "", false, errors.New("not an ip:port") } , = [:], [+1:] if len() == 0 { return "", "", false, errors.New("no IP") } if len() == 0 { return "", "", false, errors.New("no port") } if [0] == '[' { if len() < 2 || [len()-1] != ']' { return "", "", false, errors.New("missing ]") } = [1 : len()-1] = true } return , , , nil } // ParseAddrPort parses s as an [AddrPort]. // // It doesn't do any name resolution: both the address and the port // must be numeric. func ( string) (AddrPort, error) { var AddrPort , , , := splitAddrPort() if != nil { return , } , := strconv.ParseUint(, 10, 16) if != nil { return , errors.New("invalid port " + strconv.Quote() + " parsing " + strconv.Quote()) } .port = uint16() .ip, = ParseAddr() if != nil { return AddrPort{}, } if && .ip.Is4() { return AddrPort{}, errors.New("invalid ip:port " + strconv.Quote() + ", square brackets can only be used with IPv6 addresses") } else if ! && .ip.Is6() { return AddrPort{}, errors.New("invalid ip:port " + strconv.Quote() + ", IPv6 addresses must be surrounded by square brackets") } return , nil } // MustParseAddrPort calls [ParseAddrPort](s) and panics on error. // It is intended for use in tests with hard-coded strings. func ( string) AddrPort { , := ParseAddrPort() if != nil { panic() } return } // IsValid reports whether p.Addr() is valid. // All ports are valid, including zero. func ( AddrPort) () bool { return .ip.IsValid() } // Compare returns an integer comparing two AddrPorts. // The result will be 0 if p == p2, -1 if p < p2, and +1 if p > p2. // AddrPorts sort first by IP address, then port. func ( AddrPort) ( AddrPort) int { if := .Addr().Compare(.Addr()); != 0 { return } return cmp.Compare(.Port(), .Port()) } func ( AddrPort) () string { switch .ip.z { case z0: return "invalid AddrPort" case z4: const = len("255.255.255.255:65535") := make([]byte, 0, ) = .ip.appendTo4() = append(, ':') = strconv.AppendUint(, uint64(.port), 10) return string() default: // TODO: this could be more efficient allocation-wise: return "[" + .ip.String() + "]:" + itoa.Uitoa(uint(.port)) } } // AppendTo appends a text encoding of p, // as generated by [AddrPort.MarshalText], // to b and returns the extended buffer. func ( AddrPort) ( []byte) []byte { switch .ip.z { case z0: return case z4: = .ip.appendTo4() default: if .ip.Is4In6() { = append(, "[::ffff:"...) = .ip.Unmap().appendTo4() if := .ip.Zone(); != "" { = append(, '%') = append(, ...) } } else { = append(, '[') = .ip.appendTo6() } = append(, ']') } = append(, ':') = strconv.AppendUint(, uint64(.port), 10) return } // MarshalText implements the [encoding.TextMarshaler] interface. The // encoding is the same as returned by [AddrPort.String], with one exception: if // p.Addr() is the zero [Addr], the encoding is the empty string. func ( AddrPort) () ([]byte, error) { var int switch .ip.z { case z0: case z4: = len("255.255.255.255:65535") default: = len("[ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff%enp5s0]:65535") } := make([]byte, 0, ) = .AppendTo() return , nil } // UnmarshalText implements the encoding.TextUnmarshaler // interface. The [AddrPort] is expected in a form // generated by [AddrPort.MarshalText] or accepted by [ParseAddrPort]. func ( *AddrPort) ( []byte) error { if len() == 0 { * = AddrPort{} return nil } var error *, = ParseAddrPort(string()) return } // MarshalBinary implements the [encoding.BinaryMarshaler] interface. // It returns [Addr.MarshalBinary] with an additional two bytes appended // containing the port in little-endian. func ( AddrPort) () ([]byte, error) { := .Addr().marshalBinaryWithTrailingBytes(2) lePutUint16([len()-2:], .Port()) return , nil } // UnmarshalBinary implements the [encoding.BinaryUnmarshaler] interface. // It expects data in the form generated by [AddrPort.MarshalBinary]. func ( *AddrPort) ( []byte) error { if len() < 2 { return errors.New("unexpected slice size") } var Addr := .UnmarshalBinary([:len()-2]) if != nil { return } * = AddrPortFrom(, leUint16([len()-2:])) return nil } // Prefix is an IP address prefix (CIDR) representing an IP network. // // The first [Prefix.Bits]() of [Addr]() are specified. The remaining bits match any address. // The range of Bits() is [0,32] for IPv4 or [0,128] for IPv6. type Prefix struct { ip Addr // bitsPlusOne stores the prefix bit length plus one. // A Prefix is valid if and only if bitsPlusOne is non-zero. bitsPlusOne uint8 } // PrefixFrom returns a [Prefix] with the provided IP address and bit // prefix length. // // It does not allocate. Unlike [Addr.Prefix], [PrefixFrom] does not mask // off the host bits of ip. // // If bits is less than zero or greater than ip.BitLen, [Prefix.Bits] // will return an invalid value -1. func ( Addr, int) Prefix { var uint8 if !.isZero() && >= 0 && <= .BitLen() { = uint8() + 1 } return Prefix{ ip: .withoutZone(), bitsPlusOne: , } } // Addr returns p's IP address. func ( Prefix) () Addr { return .ip } // Bits returns p's prefix length. // // It reports -1 if invalid. func ( Prefix) () int { return int(.bitsPlusOne) - 1 } // IsValid reports whether p.Bits() has a valid range for p.Addr(). // If p.Addr() is the zero [Addr], IsValid returns false. // Note that if p is the zero [Prefix], then p.IsValid() == false. func ( Prefix) () bool { return .bitsPlusOne > 0 } func ( Prefix) () bool { return == Prefix{} } // IsSingleIP reports whether p contains exactly one IP. func ( Prefix) () bool { return .IsValid() && .Bits() == .ip.BitLen() } // compare returns an integer comparing two prefixes. // The result will be 0 if p == p2, -1 if p < p2, and +1 if p > p2. // Prefixes sort first by validity (invalid before valid), then // address family (IPv4 before IPv6), then prefix length, then // address. // // Unexported for Go 1.22 because we may want to compare by p.Addr first. // See post-acceptance discussion on go.dev/issue/61642. func ( Prefix) ( Prefix) int { if := cmp.Compare(.Addr().BitLen(), .Addr().BitLen()); != 0 { return } if := cmp.Compare(.Bits(), .Bits()); != 0 { return } return .Addr().Compare(.Addr()) } // ParsePrefix 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. // IPv6 zones are not permitted in prefixes, and an error will be returned if a // zone is present. // // Note that masked address bits are not zeroed. Use Masked for that. func ( string) (Prefix, error) { := bytealg.LastIndexByteString(, '/') if < 0 { return Prefix{}, errors.New("netip.ParsePrefix(" + strconv.Quote() + "): no '/'") } , := ParseAddr([:]) if != nil { return Prefix{}, errors.New("netip.ParsePrefix(" + strconv.Quote() + "): " + .Error()) } // IPv6 zones are not allowed: https://go.dev/issue/51899 if .Is6() && .z != z6noz { return Prefix{}, errors.New("netip.ParsePrefix(" + strconv.Quote() + "): IPv6 zones cannot be present in a prefix") } := [+1:] // strconv.Atoi accepts a leading sign and leading zeroes, but we don't want that. if len() > 1 && ([0] < '1' || [0] > '9') { return Prefix{}, errors.New("netip.ParsePrefix(" + strconv.Quote() + "): bad bits after slash: " + strconv.Quote()) } , := strconv.Atoi() if != nil { return Prefix{}, errors.New("netip.ParsePrefix(" + strconv.Quote() + "): bad bits after slash: " + strconv.Quote()) } := 32 if .Is6() { = 128 } if < 0 || > { return Prefix{}, errors.New("netip.ParsePrefix(" + strconv.Quote() + "): prefix length out of range") } return PrefixFrom(, ), nil } // MustParsePrefix calls [ParsePrefix](s) and panics on error. // It is intended for use in tests with hard-coded strings. func ( string) Prefix { , := ParsePrefix() if != nil { panic() } return } // Masked returns p in its canonical form, with all but the high // p.Bits() bits of p.Addr() masked off. // // If p is zero or otherwise invalid, Masked returns the zero [Prefix]. func ( Prefix) () Prefix { , := .ip.Prefix(.Bits()) return } // Contains reports whether the network p includes ip. // // An IPv4 address will not match an IPv6 prefix. // An IPv4-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 Prefixes strip zones. func ( Prefix) ( Addr) bool { if !.IsValid() || .hasZone() { return false } if , := .ip.BitLen(), .BitLen(); == 0 || == 0 || != { return false } if .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((.addr.lo^.ip.addr.lo)>>((32-.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 .addr.xor(.ip.addr).and(mask6(.Bits())).isZero() } } // Overlaps reports whether p and o contain any IP addresses in common. // // 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 an // IPv4-mapped IPv6 address is still treated as an IPv6 mask. func ( Prefix) ( Prefix) bool { if !.IsValid() || !.IsValid() { return false } if == { return true } if .ip.Is4() != .ip.Is4() { return false } var int if , := .Bits(), .Bits(); < { = } else { = } if == 0 { return true } // One of these Prefix calls might look redundant, but we don't require // that p and o values are normalized (via Prefix.Masked) first, // so the Prefix call on the one that's already minBits serves to zero // out any remaining bits in IP. var error if , = .ip.Prefix(); != nil { return false } if , = .ip.Prefix(); != nil { return false } return .ip == .ip } // AppendTo appends a text encoding of p, // as generated by [Prefix.MarshalText], // to b and returns the extended buffer. func ( Prefix) ( []byte) []byte { if .isZero() { return } if !.IsValid() { return append(, "invalid Prefix"...) } // p.ip is non-nil, because p is valid. if .ip.z == z4 { = .ip.appendTo4() } else { if .ip.Is4In6() { = append(, "::ffff:"...) = .ip.Unmap().appendTo4() } else { = .ip.appendTo6() } } = append(, '/') = appendDecimal(, uint8(.Bits())) return } // MarshalText implements the [encoding.TextMarshaler] interface, // The encoding is the same as returned by [Prefix.String], with one exception: // If p is the zero value, the encoding is the empty string. func ( Prefix) () ([]byte, error) { var int switch .ip.z { case z0: case z4: = len("255.255.255.255/32") default: = len("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff%enp5s0/128") } := make([]byte, 0, ) = .AppendTo() return , nil } // UnmarshalText implements the encoding.TextUnmarshaler interface. // The IP address is expected in a form accepted by [ParsePrefix] // or generated by [Prefix.MarshalText]. func ( *Prefix) ( []byte) error { if len() == 0 { * = Prefix{} return nil } var error *, = ParsePrefix(string()) return } // MarshalBinary implements the [encoding.BinaryMarshaler] interface. // It returns [Addr.MarshalBinary] with an additional byte appended // containing the prefix bits. func ( Prefix) () ([]byte, error) { := .Addr().withoutZone().marshalBinaryWithTrailingBytes(1) [len()-1] = uint8(.Bits()) return , nil } // UnmarshalBinary implements the [encoding.BinaryUnmarshaler] interface. // It expects data in the form generated by [Prefix.MarshalBinary]. func ( *Prefix) ( []byte) error { if len() < 1 { return errors.New("unexpected slice size") } var Addr := .UnmarshalBinary([:len()-1]) if != nil { return } * = PrefixFrom(, int([len()-1])) return nil } // String returns the CIDR notation of p: "<ip>/<bits>". func ( Prefix) () string { if !.IsValid() { return "invalid Prefix" } return .ip.String() + "/" + itoa.Itoa(.Bits()) }