// 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 netipimport ()// 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 ::.typeAddrstruct {// 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// Details about the address, wrapped up together and canonicalized. z unique.Handle[addrDetail]}// addrDetail represents the details of an Addr, like address family and IPv6 zone.type addrDetail struct { isV6 bool// IPv4 is false, IPv6 is true. zoneV6 string// != "" only if IsV6 is true.}// z0, z4, and z6noz are sentinel Addr.z values.// See the Addr type's field docs.var ( z0 unique.Handle[addrDetail] z4 = unique.Make(addrDetail{}) z6noz = unique.Make(addrDetail{isV6: true}))// IPv6LinkLocalAllNodes returns the IPv6 link-local all nodes multicast// address ff02::1.func () Addr { returnAddrFrom16([16]byte{0: 0xff, 1: 0x02, 15: 0x01}) }// IPv6LinkLocalAllRouters returns the IPv6 link-local all routers multicast// address ff02::2.func () Addr { returnAddrFrom16([16]byte{0: 0xff, 1: 0x02, 15: 0x02}) }// IPv6Loopback returns the IPv6 loopback address ::1.func () Addr { returnAddrFrom16([16]byte{15: 0x01}) }// IPv6Unspecified returns the IPv6 unspecified address "::".func () Addr { returnAddr{z: z6noz} }// IPv4Unspecified returns the IPv4 unspecified address "0.0.0.0".func () Addr { returnAddrFrom4([4]byte{}) }// AddrFrom4 returns the address of the IPv4 address given by the bytes in addr.func ( [4]byte) Addr {returnAddr{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 {returnAddr{addr: uint128{byteorder.BeUint64([:8]),byteorder.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'.':returnparseIPv4()case':':returnparseIPv6()case'%':// Assume that this was trying to be an IPv6 address with // a zone specifier, but the address is missing.returnAddr{}, parseAddrError{in: , msg: "missing IPv6 address"} } }returnAddr{}, 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.Quoteif .at != "" {return"ParseAddr(" + (.in) + "): " + .msg + " (at " + (.at) + ")" }return"ParseAddr(" + (.in) + "): " + .msg}func parseIPv4Fields( string, , int, []uint8) error {var , intvarint// number of digits in current octet := [:]for := 0; < len(); ++ {if [] >= '0' && [] <= '9' {if == 1 && == 0 {returnparseAddrError{in: , msg: "IPv4 field has octet with leading zero"} } = *10 + int([]) - '0' ++if > 255 {returnparseAddrError{in: , msg: "IPv4 field has value >255"} } } elseif [] == '.' {// .1.2.3 // 1.2.3. // 1..2.3if == 0 || == len()-1 || [-1] == '.' {returnparseAddrError{in: , msg: "IPv4 field must have at least one digit", at: [:]} }// 1.2.3.4.5if == 3 {returnparseAddrError{in: , msg: "IPv4 address too long"} } [] = uint8() ++ = 0 = 0 } else {returnparseAddrError{in: , msg: "unexpected character", at: [:]} } }if < 3 {returnparseAddrError{in: , msg: "IPv4 address too short"} } [3] = uint8()returnnil}// parseIPv4 parses s as an IPv4 address (in form "192.168.0.1").func parseIPv4( string) ( Addr, error) {var [4]uint8 = parseIPv4Fields(, 0, len(), [:])if != nil {returnAddr{}, }returnAddrFrom4(), 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.returnAddr{}, parseAddrError{in: , msg: "zone must be a non-empty string"} } }var [16]byte := -1// position of ellipsis in ip// Might have leading ellipsisiflen() >= 2 && [0] == ':' && [1] == ':' { = 0 = [2:]// Might be only ellipsisiflen() == 0 {returnIPv6Unspecified().WithZone(), nil } }// Loop, parsing hex numbers followed by colon. = 0for < 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') } elseif >= 'a' && <= 'f' { = ( << 4) + uint32(-'a'+10) } elseif >= 'A' && <= 'F' { = ( << 4) + uint32(-'A'+10) } else {break }if > 3 {//more than 4 digits in group, fail.returnAddr{}, parseAddrError{in: , msg: "each group must have 4 or less digits", at: } }if > math.MaxUint16 {// Overflow, fail.returnAddr{}, parseAddrError{in: , msg: "IPv6 field has value >=2^16", at: } } }if == 0 {// No digits found, fail.returnAddr{}, 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.returnAddr{}, parseAddrError{in: , msg: "embedded IPv4 address must replace the final 2 fields of the address", at: } }if +4 > 16 {// Not enough room.returnAddr{}, parseAddrError{in: , msg: "too many hex fields to fit an embedded IPv4 at the end of the address", at: } } := len()iflen() > 0 { -= len() + 1 } := parseIPv4Fields(, -len(), , [:+4])if != nil {returnAddr{}, } = "" += 4break }// Save this 16-bit chunk. [] = byte( >> 8) [+1] = byte() += 2// Stop at end of string. = [:]iflen() == 0 {break }// Otherwise must be followed by colon and more.if [0] != ':' {returnAddr{}, parseAddrError{in: , msg: "unexpected character, want colon", at: } } elseiflen() == 1 {returnAddr{}, parseAddrError{in: , msg: "colon must be followed by more characters", at: } } = [1:]// Look for ellipsis.if [0] == ':' {if >= 0 { // already have onereturnAddr{}, parseAddrError{in: , msg: "multiple :: in address", at: } } = = [1:]iflen() == 0 { // can be at endbreak } } }// Must have used entire string.iflen() != 0 {returnAddr{}, parseAddrError{in: , msg: "trailing garbage after address", at: } }// If didn't parse enough, expand ellipsis.if < 16 {if < 0 {returnAddr{}, parseAddrError{in: , msg: "address string too short"} } := 16 - for := - 1; >= ; -- { [+] = [] }clear([ : +]) } elseif >= 0 {// Ellipsis must represent at least one 0 group.returnAddr{}, parseAddrError{in: , msg: "the :: must expand to at least one field of zeros"} }returnAddrFrom16().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) {switchlen() {case4:returnAddrFrom4([4]byte()), truecase16:returnAddrFrom16([16]byte()), true }returnAddr{}, false}// v4 returns the i'th byte of ip. If ip is not an IPv4, v4 returns// unspecified garbage.func ( Addr) ( uint8) uint8 {returnuint8(.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 {returnuint8(*(.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 {returnuint16(*(.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 {casez0:return0casez4:return32 }return128}// Zone returns ip's IPv6 scoped addressing zone, if any.func ( Addr) () string {if .z == z0 {return"" }return .z.Value().zoneV6}// 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 > {return1 } , := .addr.hi, .addr.hiif < {return -1 }if > {return1 } , := .addr.lo, .addr.loif < {return -1 }if > {return1 }if .Is6() { , := .Zone(), .Zone()if < {return -1 }if > {return1 } }return0}// 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 = z6nozreturn } .z = unique.Make(addrDetail{isV6: true, zoneV6: })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 = z6nozreturn}// 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 {if .Is4In6() { = .Unmap() }// Dynamic Configuration of IPv4 Link-Local Addresses // https://datatracker.ietf.org/doc/html/rfc3927#section-2.1if .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.4if .Is6() {return .v6u16(0)&0xffc0 == 0xfe80 }returnfalse// zero value}// IsLoopback reports whether ip is a loopback address.func ( Addr) () bool {if .Is4In6() { = .Unmap() }// Requirements for Internet Hosts -- Communication Layers (3.2.1.3 Addressing) // https://datatracker.ietf.org/doc/html/rfc1122#section-3.2.1.3if .Is4() {return .v4(0) == 127 }// IP Version 6 Addressing Architecture (2.4 Address Type Identification) // https://datatracker.ietf.org/doc/html/rfc4291#section-2.4if .Is6() {return .addr.hi == 0 && .addr.lo == 1 }returnfalse// zero value}// IsMulticast reports whether ip is a multicast address.func ( Addr) () bool {if .Is4In6() { = .Unmap() }// Host Extensions for IP Multicasting (4. HOST GROUP ADDRESSES) // https://datatracker.ietf.org/doc/html/rfc1112#section-4if .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.4if .Is6() {return .addr.hi>>(64-8) == 0xff// ip.v6(0) == 0xff }returnfalse// 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.1if .Is6() && !.Is4In6() {return .v6u16(0)&0xff0f == 0xff01 }returnfalse// zero value}// IsLinkLocalMulticast reports whether ip is a link-local multicast address.func ( Addr) () bool {if .Is4In6() { = .Unmap() }// IPv4 Multicast Guidelines (4. Local Network Control Block (224.0.0/24)) // https://datatracker.ietf.org/doc/html/rfc5771#section-4if .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.1if .Is6() {return .v6u16(0)&0xff0f == 0xff02 }returnfalse// 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.returnfalse }if .Is4In6() { = .Unmap() }// 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})) {returnfalse }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 {if .Is4In6() { = .Unmap() }// 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 }returnfalse// 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 {returnPrefix{}, errors.New("negative Prefix bits") } := switch .z {casez0:returnPrefix{}, nilcasez4:if > 32 {returnPrefix{}, errors.New("prefix length " + itoa.Itoa() + " too large for IPv4") } += 96default:if > 128 {returnPrefix{}, errors.New("prefix length " + itoa.Itoa() + " too large for IPv6") } } .addr = .addr.and(mask6())returnPrefixFrom(, ), 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) {byteorder.BePutUint64([:8], .addr.hi)byteorder.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() {byteorder.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 {casez0:returnnilcasez4:var [4]bytebyteorder.BePutUint32([:], uint32(.addr.lo))return [:]default:var [16]bytebyteorder.BePutUint64([:8], .addr.hi)byteorder.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() {ifuint32(.addr.lo) == 0 {// Overflowed.returnAddr{} } } else {if .addr.isZero() {// OverflowedreturnAddr{} } }return}// Prev returns the IP before ip.// If there is none, it returns the IP zero value.func ( Addr) () Addr {if .Is4() {ifuint32(.addr.lo) == 0 {returnAddr{} } } elseif .addr.isZero() {returnAddr{} } .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 {casez0:return"invalid IP"casez4:return .string4()default:if .Is4In6() {return .string4In6() }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 {casez0:returncasez4:return .appendTo4()default:if .Is4In6() {return .appendTo4In6() }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]) }returnappend(, 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]) }returnappend(, digits[&0xf])}// appendHexPad appends the fully padded hex string representation of x to b.func appendHexPad( []byte, uint16) []byte {returnappend(, digits[>>12], digits[>>8&0xf], digits[>>4&0xf], digits[&0xf])}func ( Addr) () string {const = len("255.255.255.255") := make([]byte, 0, ) = .appendTo4()returnstring()}func ( Addr) ( []byte) []byte { = appendDecimal(, .v4(0)) = append(, '.') = appendDecimal(, .v4(1)) = append(, '.') = appendDecimal(, .v4(2)) = append(, '.') = appendDecimal(, .v4(3))return}func ( Addr) () string {const = len("::ffff:255.255.255.255%enp5s0") := make([]byte, 0, ) = .appendTo4In6()returnstring()}func ( Addr) ( []byte) []byte { = append(, "::ffff:"...) = .Unmap().appendTo4()if .z != z6noz { = append(, '%') = append(, .Zone()...) }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()returnstring()}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 } } elseif > 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 {casez0, 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()...) }returnstring()}// 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 {casez0:return []byte(""), nilcasez4: := len("255.255.255.255") := make([]byte, 0, )return .appendTo4(), nildefault:if .Is4In6() { := len("::ffff:255.255.255.255%enp5s0") := make([]byte, 0, )return .appendTo4In6(), nil } := len("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff%enp5s0") := make([]byte, 0, )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 {iflen() == 0 { * = Addr{}returnnil }varerror *, = ParseAddr(string())return}func ( Addr) ( int) []byte {var []byteswitch .z {casez0: = make([]byte, )casez4: = make([]byte, 4+)byteorder.BePutUint32(, uint32(.addr.lo))default: := .Zone() = make([]byte, 16+len()+)byteorder.BePutUint64([:8], .addr.hi)byteorder.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{}returnnilcase == 4: * = AddrFrom4([4]byte())returnnilcase == 16: * = AddrFrom16([16]byte())returnnilcase > 16: * = AddrFrom16([16]byte([:16])).WithZone(string([16:]))returnnil }returnerrors.New("unexpected slice size")}// AddrPort is an IP and a port number.typeAddrPortstruct { ip Addr port uint16}// AddrPortFrom returns an [AddrPort] with the provided IP and port.// It does not allocate.func ( Addr, uint16) AddrPort { returnAddrPort{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:]iflen() == 0 {return"", "", false, errors.New("no IP") }iflen() == 0 {return"", "", false, errors.New("no port") }if [0] == '[' {iflen() < 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) {varAddrPort , , , := 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 {returnAddrPort{}, }if && .ip.Is4() {returnAddrPort{}, errors.New("invalid ip:port " + strconv.Quote() + ", square brackets can only be used with IPv6 addresses") } elseif ! && .ip.Is6() {returnAddrPort{}, 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 }returncmp.Compare(.Port(), .Port())}func ( AddrPort) () string {var []byteswitch .ip.z {casez0:return"invalid AddrPort"casez4:const = len("255.255.255.255:65535") = make([]byte, 0, ) = .ip.appendTo4()default:if .ip.Is4In6() {const = len("[::ffff:255.255.255.255%enp5s0]:65535") = make([]byte, 0, ) = append(, '[') = .ip.appendTo4In6() } else {const = len("[ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff%enp5s0]:65535") = make([]byte, 0, ) = append(, '[') = .ip.appendTo6() } = append(, ']') } = append(, ':') = strconv.AppendUint(, uint64(.port), 10)returnstring()}// 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 {casez0:returncasez4: = .ip.appendTo4()default: = append(, '[')if .ip.Is4In6() { = .ip.appendTo4In6() } else { = .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) {varintswitch .ip.z {casez0:casez4: = 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 {iflen() == 0 { * = AddrPort{}returnnil }varerror *, = 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)byteorder.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 {iflen() < 2 {returnerrors.New("unexpected slice size") }varAddr := .UnmarshalBinary([:len()-2])if != nil {return } * = AddrPortFrom(, byteorder.LeUint16([len()-2:]))returnnil}// 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.typePrefixstruct { 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 {varuint8if !.isZero() && >= 0 && <= .BitLen() { = uint8() + 1 }returnPrefix{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 { returnint(.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())}type parsePrefixError struct { in string// the string given to ParsePrefix msg string// an explanation of the parse failure}func ( parsePrefixError) () string {return"netip.ParsePrefix(" + strconv.Quote(.in) + "): " + .msg}// 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 {returnPrefix{}, parsePrefixError{in: , msg: "no '/'"} } , := ParseAddr([:])if != nil {returnPrefix{}, parsePrefixError{in: , msg: .Error()} }// IPv6 zones are not allowed: https://go.dev/issue/51899if .Is6() && .z != z6noz {returnPrefix{}, parsePrefixError{in: , msg: "IPv6 zones cannot be present in a prefix"} } := [+1:]// strconv.Atoi accepts a leading sign and leading zeroes, but we don't want that.iflen() > 1 && ([0] < '1' || [0] > '9') {returnPrefix{}, parsePrefixError{in: , msg: "bad bits after slash: " + strconv.Quote()} } , := strconv.Atoi()if != nil {returnPrefix{}, parsePrefixError{in: , msg: "bad bits after slash: " + strconv.Quote()} } := 32if .Is6() { = 128 }if < 0 || > {returnPrefix{}, parsePrefixError{in: , msg: "prefix length out of range"} }returnPrefixFrom(, ), 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() {returnfalse }if , := .ip.BitLen(), .BitLen(); == 0 || == 0 || != {returnfalse }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.returnuint32((.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() {returnfalse }if == {returntrue }if .ip.Is4() != .ip.Is4() {returnfalse }varintif , := .Bits(), .Bits(); < { = } else { = }if == 0 {returntrue }// 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.varerrorif , = .ip.Prefix(); != nil {returnfalse }if , = .ip.Prefix(); != nil {returnfalse }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() {returnappend(, "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) {varintswitch .ip.z {casez0:casez4: = 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 {iflen() == 0 { * = Prefix{}returnnil }varerror *, = 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 {iflen() < 1 {returnerrors.New("unexpected slice size") }varAddr := .UnmarshalBinary([:len()-1])if != nil {return } * = PrefixFrom(, int([len()-1]))returnnil}// String returns the CIDR notation of p: "<ip>/<bits>".func ( Prefix) () string {if !.IsValid() {return"invalid Prefix" }return .ip.String() + "/" + itoa.Itoa(.Bits())}
The pages are generated with Goldsv0.7.0-preview. (GOOS=linux GOARCH=amd64)
Golds is a Go 101 project developed by Tapir Liu.
PR and bug reports are welcome and can be submitted to the issue list.
Please follow @zigo_101 (reachable from the left QR code) to get the latest news of Golds.