diff --git a/go-controller/go.mod b/go-controller/go.mod index 04b9a678ec..23da240159 100644 --- a/go-controller/go.mod +++ b/go-controller/go.mod @@ -58,6 +58,7 @@ require ( gopkg.in/gcfg.v1 v1.2.3 gopkg.in/k8snetworkplumbingwg/multus-cni.v4 v4.0.2 gopkg.in/natefinch/lumberjack.v2 v2.2.1 + inet.af/netaddr v0.0.0-20230525184311-b8eac61e914a k8s.io/api v0.33.3 k8s.io/apimachinery v0.33.3 k8s.io/client-go v0.33.3 @@ -131,6 +132,8 @@ require ( go.opencensus.io v0.24.0 // indirect go.opentelemetry.io/otel v1.33.0 // indirect go.opentelemetry.io/otel/trace v1.33.0 // indirect + go4.org/intern v0.0.0-20211027215823-ae77deb06f29 // indirect + go4.org/unsafe/assume-no-moving-gc v0.0.0-20230525183740-e7c30c78aeb2 // indirect golang.org/x/crypto v0.36.0 // indirect golang.org/x/oauth2 v0.27.0 // indirect golang.org/x/term v0.30.0 // indirect diff --git a/go-controller/go.sum b/go-controller/go.sum index df20a066c9..8e07021c76 100644 --- a/go-controller/go.sum +++ b/go-controller/go.sum @@ -249,6 +249,7 @@ github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZ github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE= github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= +github.com/dvyukov/go-fuzz v0.0.0-20210103155950-6a8e9d1f2415/go.mod h1:11Gm+ccJnvAhCNLlf5+cS9KjtbaD5I5zaZpFMsTHWTw= github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc= github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= github.com/emicklei/go-restful v2.9.5+incompatible/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= @@ -879,6 +880,11 @@ go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo= go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= +go4.org/intern v0.0.0-20211027215823-ae77deb06f29 h1:UXLjNohABv4S58tHmeuIZDO6e3mHpW2Dx33gaNt03LE= +go4.org/intern v0.0.0-20211027215823-ae77deb06f29/go.mod h1:cS2ma+47FKrLPdXFpr7CuxiTW3eyJbWew4qx0qtQWDA= +go4.org/unsafe/assume-no-moving-gc v0.0.0-20211027215541-db492cf91b37/go.mod h1:FftLjUGFEDu5k8lt0ddY+HcrH/qU/0qk+H8j9/nTl3E= +go4.org/unsafe/assume-no-moving-gc v0.0.0-20230525183740-e7c30c78aeb2 h1:WJhcL4p+YeDxmZWg141nRm7XC8IDmhz7lk5GpadO1Sg= +go4.org/unsafe/assume-no-moving-gc v0.0.0-20230525183740-e7c30c78aeb2/go.mod h1:FftLjUGFEDu5k8lt0ddY+HcrH/qU/0qk+H8j9/nTl3E= golang.org/x/crypto v0.0.0-20171113213409-9f005a07e0d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20180214000028-650f4a345ab4/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= @@ -1063,6 +1069,7 @@ golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201202213521-69691e467435/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210324051608-47abb6519492/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -1148,6 +1155,7 @@ golang.org/x/tools v0.0.0-20200505023115-26f46d2f7ef8/go.mod h1:EkVYQZoAsY45+roY golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.6-0.20210820212750-d4cc65f0b2ff/go.mod h1:YD9qOF0M9xpSpdWTBbzEl5e/RnCefISl8E5Noe10jFM= @@ -1306,6 +1314,8 @@ honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWh honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= +inet.af/netaddr v0.0.0-20230525184311-b8eac61e914a h1:1XCVEdxrvL6c0TGOhecLuB7U9zYNdxZEjvOqJreKZiM= +inet.af/netaddr v0.0.0-20230525184311-b8eac61e914a/go.mod h1:e83i32mAQOW1LAqEIweALsuK2Uw4mhQadA5r7b0Wobo= k8s.io/api v0.20.1/go.mod h1:KqwcCVogGxQY3nBlRpwt+wpAMF/KjaCc7RpywacvqUo= k8s.io/api v0.20.4/go.mod h1:++lNL1AJMkDymriNniQsWRkMDzRaX2Y/POTUi8yvqYQ= k8s.io/api v0.20.6/go.mod h1:X9e8Qag6JV/bL5G6bU8sdVRltWKmdHsFUGS3eVndqE8= diff --git a/go-controller/pkg/ovn/gress_policy.go b/go-controller/pkg/ovn/gress_policy.go index ad20fadfb3..961baf97ee 100644 --- a/go-controller/pkg/ovn/gress_policy.go +++ b/go-controller/pkg/ovn/gress_policy.go @@ -7,8 +7,10 @@ import ( "strings" "sync" + "inet.af/netaddr" corev1 "k8s.io/api/core/v1" knet "k8s.io/api/networking/v1" + "k8s.io/klog/v2" utilnet "k8s.io/utils/net" libovsdbops "github.com/ovn-org/ovn-kubernetes/go-controller/pkg/libovsdb/ops" @@ -167,35 +169,105 @@ func (gp *gressPolicy) allIPsMatch() string { } } -func (gp *gressPolicy) getMatchFromIPBlock(lportMatch, l4Match string) []string { - var direction string +func (gp *gressPolicy) getMatchFromIPBlock(lportMatch, l4Match string) ([]string, error) { + direction := "dst" if gp.policyType == knet.PolicyTypeIngress { direction = "src" - } else { - direction = "dst" } - var matchStrings []string - var matchStr, ipVersion string + + var ( + ipv4Builder netaddr.IPSetBuilder + ipv6Builder netaddr.IPSetBuilder + ) + + // Precompute which blocks are v4/v6 and build sets in a single pass for _, ipBlock := range gp.ipBlocks { - if utilnet.IsIPv6CIDRString(ipBlock.CIDR) { - ipVersion = "ip6" + cidrPrefix := netaddr.MustParseIPPrefix(ipBlock.CIDR) + var exceptSet *netaddr.IPSet + var err error + + if len(ipBlock.Except) > 0 { + var exceptBuilder netaddr.IPSetBuilder + for _, except := range ipBlock.Except { + exceptBuilder.AddPrefix(netaddr.MustParseIPPrefix(except)) + } + exceptSet, err = exceptBuilder.IPSet() + if err != nil { + return nil, fmt.Errorf("failed to build IPSet from except %v: %v", ipBlock.Except, err) + } + } + + var blockBuilder netaddr.IPSetBuilder + blockBuilder.AddPrefix(cidrPrefix) + cidrSet, err := blockBuilder.IPSet() + if err != nil { + return nil, fmt.Errorf("failed to build IPSet from CIDR %s: %v", ipBlock.CIDR, err) + } + + var finalSet *netaddr.IPSet + if exceptSet != nil { + var diffBuilder netaddr.IPSetBuilder + diffBuilder.AddSet(cidrSet) + diffBuilder.RemoveSet(exceptSet) + finalSet, err = diffBuilder.IPSet() + if err != nil { + return nil, fmt.Errorf("failed to build IPSet from diff %s: %v", ipBlock.CIDR, err) + } } else { - ipVersion = "ip4" + finalSet = cidrSet } - if len(ipBlock.Except) == 0 { - matchStr = fmt.Sprintf("%s.%s == %s", ipVersion, direction, ipBlock.CIDR) + + if utilnet.IsIPv6CIDRString(ipBlock.CIDR) { + ipv6Builder.AddSet(finalSet) } else { - matchStr = fmt.Sprintf("%s.%s == %s && %s.%s != {%s}", ipVersion, direction, ipBlock.CIDR, - ipVersion, direction, strings.Join(ipBlock.Except, ", ")) + ipv4Builder.AddSet(finalSet) } + } + + var matchStrings []string + + // Helper to build match string for a given IP version + buildMatch := func(ipVer, direction string, prefixes []netaddr.IPPrefix) string { + if len(prefixes) == 0 { + return "" + } + parts := make([]string, len(prefixes)) + for i, p := range prefixes { + parts[i] = p.String() + } + match := fmt.Sprintf("%s.%s == {%s}", ipVer, direction, strings.Join(parts, ", ")) if l4Match == libovsdbutil.UnspecifiedL4Match { - matchStr = fmt.Sprintf("%s && %s", matchStr, lportMatch) - } else { - matchStr = fmt.Sprintf("%s && %s && %s", matchStr, l4Match, lportMatch) + return fmt.Sprintf("%s && %s", match, lportMatch) } - matchStrings = append(matchStrings, matchStr) + return fmt.Sprintf("%s && %s && %s", match, l4Match, lportMatch) } - return matchStrings + + // Only build match strings if there are prefixes + if ipv4Set, err := ipv4Builder.IPSet(); err != nil { + return nil, fmt.Errorf("failed to build IPSet from final IPv4 IPBlock: %v", err) + } else { + v4Prefixes := ipv4Set.Prefixes() + if len(v4Prefixes) > 0 { + match := buildMatch("ip4", direction, v4Prefixes) + if match != "" { + matchStrings = append(matchStrings, match) + } + } + } + + if ipv6Set, err := ipv6Builder.IPSet(); err != nil { + return nil, fmt.Errorf("failed to build IPSet from final IPv6 IPBlock: %v", err) + } else { + v6Prefixes := ipv6Set.Prefixes() + if len(v6Prefixes) > 0 { + match := buildMatch("ip6", direction, v6Prefixes) + if match != "" { + matchStrings = append(matchStrings, match) + } + } + } + + return matchStrings, nil } // addNamespaceAddressSet adds a namespace address set to the gress policy. @@ -285,7 +357,11 @@ func (gp *gressPolicy) buildLocalPodACLs(portGroupName string, aclLogging *libov for protocol, l4Match := range libovsdbutil.GetL4MatchesFromNetworkPolicyPorts(gp.portPolicies) { if len(gp.ipBlocks) > 0 { // Add ACL allow rule for IPBlock CIDR - ipBlockMatches := gp.getMatchFromIPBlock(lportMatch, l4Match) + ipBlockMatches, err := gp.getMatchFromIPBlock(lportMatch, l4Match) + if err != nil { + klog.Errorf("failed to get match from IPBlock: %v", err) + continue + } for ipBlockIdx, ipBlockMatch := range ipBlockMatches { aclIDs := gp.getNetpolACLDbIDs(ipBlockIdx, protocol) acl := libovsdbutil.BuildACLWithDefaultTier(aclIDs, types.DefaultAllowPriority, ipBlockMatch, action, diff --git a/go-controller/vendor/go4.org/intern/LICENSE b/go-controller/vendor/go4.org/intern/LICENSE new file mode 100644 index 0000000000..b0ab8921dc --- /dev/null +++ b/go-controller/vendor/go4.org/intern/LICENSE @@ -0,0 +1,29 @@ +BSD 3-Clause License + +Copyright (c) 2020, Brad Fitzpatrick +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/go-controller/vendor/go4.org/intern/README.md b/go-controller/vendor/go4.org/intern/README.md new file mode 100644 index 0000000000..1db456b735 --- /dev/null +++ b/go-controller/vendor/go4.org/intern/README.md @@ -0,0 +1,4 @@ +# go4.org/intern + +See https://godoc.org/go4.org/intern + diff --git a/go-controller/vendor/go4.org/intern/intern.go b/go-controller/vendor/go4.org/intern/intern.go new file mode 100644 index 0000000000..536014cd35 --- /dev/null +++ b/go-controller/vendor/go4.org/intern/intern.go @@ -0,0 +1,183 @@ +// Copyright 2020 Brad Fitzpatrick. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package intern lets you make smaller comparable values by boxing +// a larger comparable value (such as a 16 byte string header) down +// into a globally unique 8 byte pointer. +// +// The globally unique pointers are garbage collected with weak +// references and finalizers. This package hides that. +// +// The GitHub repo is https://github.com/go4org/intern +package intern // import "go4.org/intern" + +import ( + "os" + "runtime" + "strconv" + "sync" + "unsafe" + + _ "go4.org/unsafe/assume-no-moving-gc" +) + +// A Value pointer is the handle to an underlying comparable value. +// See func Get for how Value pointers may be used. +type Value struct { + _ [0]func() // prevent people from accidentally using value type as comparable + cmpVal interface{} + // resurrected is guarded by mu (for all instances of Value). + // It is set true whenever v is synthesized from a uintptr. + resurrected bool +} + +// Get returns the comparable value passed to the Get func +// that returned v. +func (v *Value) Get() interface{} { return v.cmpVal } + +// key is a key in our global value map. +// It contains type-specialized fields to avoid allocations +// when converting common types to empty interfaces. +type key struct { + s string + cmpVal interface{} + // isString reports whether key contains a string. + // Without it, the zero value of key is ambiguous. + isString bool +} + +// keyFor returns a key to use with cmpVal. +func keyFor(cmpVal interface{}) key { + if s, ok := cmpVal.(string); ok { + return key{s: s, isString: true} + } + return key{cmpVal: cmpVal} +} + +// Value returns a *Value built from k. +func (k key) Value() *Value { + if k.isString { + return &Value{cmpVal: k.s} + } + return &Value{cmpVal: k.cmpVal} +} + +var ( + // mu guards valMap, a weakref map of *Value by underlying value. + // It also guards the resurrected field of all *Values. + mu sync.Mutex + valMap = map[key]uintptr{} // to uintptr(*Value) + valSafe = safeMap() // non-nil in safe+leaky mode +) + +// safeMap returns a non-nil map if we're in safe-but-leaky mode, +// as controlled by GO4_INTERN_SAFE_BUT_LEAKY. +func safeMap() map[key]*Value { + if v, _ := strconv.ParseBool(os.Getenv("GO4_INTERN_SAFE_BUT_LEAKY")); v { + return map[key]*Value{} + } + return nil +} + +// Get returns a pointer representing the comparable value cmpVal. +// +// The returned pointer will be the same for Get(v) and Get(v2) +// if and only if v == v2, and can be used as a map key. +func Get(cmpVal interface{}) *Value { + return get(keyFor(cmpVal)) +} + +// GetByString is identical to Get, except that it is specialized for strings. +// This avoids an allocation from putting a string into an interface{} +// to pass as an argument to Get. +func GetByString(s string) *Value { + return get(key{s: s, isString: true}) +} + +// We play unsafe games that violate Go's rules (and assume a non-moving +// collector). So we quiet Go here. +// See the comment below Get for more implementation details. +//go:nocheckptr +func get(k key) *Value { + mu.Lock() + defer mu.Unlock() + + var v *Value + if valSafe != nil { + v = valSafe[k] + } else if addr, ok := valMap[k]; ok { + v = (*Value)((unsafe.Pointer)(addr)) + v.resurrected = true + } + if v != nil { + return v + } + v = k.Value() + if valSafe != nil { + valSafe[k] = v + } else { + // SetFinalizer before uintptr conversion (theoretical concern; + // see https://github.com/go4org/intern/issues/13) + runtime.SetFinalizer(v, finalize) + valMap[k] = uintptr(unsafe.Pointer(v)) + } + return v +} + +func finalize(v *Value) { + mu.Lock() + defer mu.Unlock() + if v.resurrected { + // We lost the race. Somebody resurrected it while we + // were about to finalize it. Try again next round. + v.resurrected = false + runtime.SetFinalizer(v, finalize) + return + } + delete(valMap, keyFor(v.cmpVal)) +} + +// Interning is simple if you don't require that unused values be +// garbage collectable. But we do require that; we don't want to be +// DOS vector. We do this by using a uintptr to hide the pointer from +// the garbage collector, and using a finalizer to eliminate the +// pointer when no other code is using it. +// +// The obvious implementation of this is to use a +// map[interface{}]uintptr-of-*interface{}, and set up a finalizer to +// delete from the map. Unfortunately, this is racy. Because pointers +// are being created in violation of Go's unsafety rules, it's +// possible to create a pointer to a value concurrently with the GC +// concluding that the value can be collected. There are other races +// that break the equality invariant as well, but the use-after-free +// will cause a runtime crash. +// +// To make this work, the finalizer needs to know that no references +// have been unsafely created since the finalizer was set up. To do +// this, values carry a "resurrected" sentinel, which gets set +// whenever a pointer is unsafely created. If the finalizer encounters +// the sentinel, it clears the sentinel and delays collection for one +// additional GC cycle, by re-installing itself as finalizer. This +// ensures that the unsafely created pointer is visible to the GC, and +// will correctly prevent collection. +// +// This technique does mean that interned values that get reused take +// at least 3 GC cycles to fully collect (1 to clear the sentinel, 1 +// to clean up the unsafe map, 1 to be actually deleted). +// +// @ianlancetaylor commented in +// https://github.com/golang/go/issues/41303#issuecomment-717401656 +// that it is possible to implement weak references in terms of +// finalizers without unsafe. Unfortunately, the approach he outlined +// does not work here, for two reasons. First, there is no way to +// construct a strong pointer out of a weak pointer; our map stores +// weak pointers, but we must return strong pointers to callers. +// Second, and more fundamentally, we must return not just _a_ strong +// pointer to callers, but _the same_ strong pointer to callers. In +// order to return _the same_ strong pointer to callers, we must track +// it, which is exactly what we cannot do with strong pointers. +// +// See https://github.com/inetaf/netaddr/issues/53 for more +// discussion, and https://github.com/go4org/intern/issues/2 for an +// illustration of the subtleties at play. diff --git a/go-controller/vendor/go4.org/unsafe/assume-no-moving-gc/LICENSE b/go-controller/vendor/go4.org/unsafe/assume-no-moving-gc/LICENSE new file mode 100644 index 0000000000..b0ab8921dc --- /dev/null +++ b/go-controller/vendor/go4.org/unsafe/assume-no-moving-gc/LICENSE @@ -0,0 +1,29 @@ +BSD 3-Clause License + +Copyright (c) 2020, Brad Fitzpatrick +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/go-controller/vendor/go4.org/unsafe/assume-no-moving-gc/README.md b/go-controller/vendor/go4.org/unsafe/assume-no-moving-gc/README.md new file mode 100644 index 0000000000..920fc9ddab --- /dev/null +++ b/go-controller/vendor/go4.org/unsafe/assume-no-moving-gc/README.md @@ -0,0 +1,13 @@ +# go4.org/unsafe/assume-no-moving-gc + +If your Go package wants to declare that it plays `unsafe` games that only +work if the Go runtime's garbage collector is not a moving collector, then add: + +```go +import _ "go4.org/unsafe/assume-no-moving-gc" +``` + +Then your program will explode if that's no longer the case. (Users can override +the explosion with a scary sounding environment variable.) + +This also gives us a way to find all the really gross unsafe packages. diff --git a/go-controller/vendor/go4.org/unsafe/assume-no-moving-gc/assume-no-moving-gc.go b/go-controller/vendor/go4.org/unsafe/assume-no-moving-gc/assume-no-moving-gc.go new file mode 100644 index 0000000000..60561d05d4 --- /dev/null +++ b/go-controller/vendor/go4.org/unsafe/assume-no-moving-gc/assume-no-moving-gc.go @@ -0,0 +1,32 @@ +// Copyright 2020 Brad Fitzpatrick. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package go4.org/unsafe/assume-no-moving-gc exists so you can depend +// on it from unsafe code that wants to declare that it assumes that +// the Go runtime does not using a moving garbage collector. Specifically, +// it asserts that the caller is playing stupid games with the addresses +// of heap-allocated values. It says nothing about values that Go's escape +// analysis keeps on the stack. Ensuring things aren't stack-allocated +// is the caller's responsibility. +// +// This package is then updated as needed for new Go versions when +// that is still the case and explodes at runtime with a failure +// otherwise, with the idea that it's better to not start at all than +// to silently corrupt your data at runtime. +// +// To use: +// +// import _ "go4.org/unsafe/assume-no-moving-gc" +// +// There is no API. +// +// As of Go 1.21, this package asks the Go runtime whether it can move +// heap objects around. If you get an error on versions prior to that, +// go get go4.org/unsafe/assume-no-moving-gc@latest and things will +// work. +// +// The GitHub repo is at https://github.com/go4org/unsafe-assume-no-moving-gc +package assume_no_moving_gc + +const env = "ASSUME_NO_MOVING_GC_UNSAFE" diff --git a/go-controller/vendor/go4.org/unsafe/assume-no-moving-gc/check.go b/go-controller/vendor/go4.org/unsafe/assume-no-moving-gc/check.go new file mode 100644 index 0000000000..a2fbf46507 --- /dev/null +++ b/go-controller/vendor/go4.org/unsafe/assume-no-moving-gc/check.go @@ -0,0 +1,36 @@ +// Copyright 2020 Brad Fitzpatrick. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build go1.21 +// +build go1.21 + +package assume_no_moving_gc + +import ( + "os" + _ "unsafe" +) + +//go:linkname heapObjectsCanMove runtime.heapObjectsCanMove +func heapObjectsCanMove() bool + +func init() { + if !heapObjectsCanMove() { + // The unsafe assumptions made by the package + // importing this package still hold. All's good. (at + // least unless they made other assumption this + // package doesn't concern itself with) + return + } + if os.Getenv(env) == "play-with-fire" { + return + } + panic(` +Something in this program imports go4.org/unsafe/assume-no-moving-gc to +declare that it assumes a non-moving garbage collector, but the version +of Go you're using declares that its heap objects can now move around. +This program is no longer safe. You should update your packages which import +go4.org/unsafe/assume-no-moving-gc. To risk it and bypass this check, set +ASSUME_NO_MOVING_GC_UNSAFE=play-with-fire and cross your fingers.`) +} diff --git a/go-controller/vendor/inet.af/netaddr/.gitignore b/go-controller/vendor/inet.af/netaddr/.gitignore new file mode 100644 index 0000000000..c60fe1e0c2 --- /dev/null +++ b/go-controller/vendor/inet.af/netaddr/.gitignore @@ -0,0 +1,3 @@ +crashers +suppressions +netaddr-fuzz.zip diff --git a/go-controller/vendor/inet.af/netaddr/.gitmodules b/go-controller/vendor/inet.af/netaddr/.gitmodules new file mode 100644 index 0000000000..e1ebefa586 --- /dev/null +++ b/go-controller/vendor/inet.af/netaddr/.gitmodules @@ -0,0 +1,3 @@ +[submodule "corpus"] + path = corpus + url = https://github.com/inetaf/netaddr-corpus.git diff --git a/go-controller/vendor/inet.af/netaddr/AUTHORS b/go-controller/vendor/inet.af/netaddr/AUTHORS new file mode 100644 index 0000000000..ac0d1591b3 --- /dev/null +++ b/go-controller/vendor/inet.af/netaddr/AUTHORS @@ -0,0 +1,4 @@ +Alex Willmer +Matt Layher +Tailscale Inc. +Tobias Klauser diff --git a/go-controller/vendor/inet.af/netaddr/LICENSE b/go-controller/vendor/inet.af/netaddr/LICENSE new file mode 100644 index 0000000000..c47d4315ae --- /dev/null +++ b/go-controller/vendor/inet.af/netaddr/LICENSE @@ -0,0 +1,27 @@ +Copyright (c) 2020 The Inet.af AUTHORS. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Tailscale Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/go-controller/vendor/inet.af/netaddr/README.md b/go-controller/vendor/inet.af/netaddr/README.md new file mode 100644 index 0000000000..1fdaee5f5a --- /dev/null +++ b/go-controller/vendor/inet.af/netaddr/README.md @@ -0,0 +1,46 @@ +# netaddr [![Test Status](https://github.com/inetaf/netaddr/workflows/Linux/badge.svg)](https://github.com/inetaf/netaddr/actions) [![Go Reference](https://pkg.go.dev/badge/inet.af/netaddr.svg)](https://pkg.go.dev/inet.af/netaddr) + +## Deprecated + +Please see https://pkg.go.dev/go4.org/netipx and the standard library's +[`net/netip`](https://pkg.go.dev/net/netip). + +## What + +This is a package containing a new IP address type for Go. + +See its docs: https://pkg.go.dev/inet.af/netaddr + +## Status + +This package is mature, optimized, and used heavily in production at [Tailscale](https://tailscale.com). +However, API stability is not yet guaranteed. + +netaddr is intended to be a core, low-level package. +We take code review, testing, dependencies, and performance seriously, similar to Go's standard library or the golang.org/x repos. + +## Motivation + +See https://tailscale.com/blog/netaddr-new-ip-type-for-go/ for a long +blog post about why we made a new IP address package. + +Other links: + +* https://github.com/golang/go/issues/18804 ("net: reconsider representation of IP") +* https://github.com/golang/go/issues/18757 ("net: ParseIP should return an error, like other Parse functions") +* https://github.com/golang/go/issues/37921 ("net: Unable to reliably distinguish IPv4-mapped-IPv6 addresses from regular IPv4 addresses") +* merges net.IPAddr and net.IP (which the Go net package is a little torn between for legacy reasons) + +## Testing + +In addition to regular Go tests, netaddr uses fuzzing. +The corpus is stored separately, in a submodule, +to minimize the impact on everyone else. + +To use: + +``` +$ git submodule update --init +$ go get -u github.com/dvyukov/go-fuzz/go-fuzz github.com/dvyukov/go-fuzz/go-fuzz-build +$ go-fuzz-build && go-fuzz +``` diff --git a/go-controller/vendor/inet.af/netaddr/fuzz.go b/go-controller/vendor/inet.af/netaddr/fuzz.go new file mode 100644 index 0000000000..cf1836dc06 --- /dev/null +++ b/go-controller/vendor/inet.af/netaddr/fuzz.go @@ -0,0 +1,203 @@ +// 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. + +//go:build gofuzz +// +build gofuzz + +package netaddr + +import ( + "bytes" + "encoding" + "fmt" + "net" + "reflect" + "strings" +) + +func Fuzz(b []byte) int { + s := string(b) + + ip, _ := ParseIP(s) + checkStringParseRoundTrip(ip, parseIP) + checkEncoding(ip) + + // Check that we match the standard library's IP parser, modulo zones. + if !strings.Contains(s, "%") { + stdip := net.ParseIP(s) + if ip.IsZero() != (stdip == nil) { + fmt.Println("stdip=", stdip, "ip=", ip) + panic("net.ParseIP nil != ParseIP zero") + } else if !ip.IsZero() && !ip.Is4in6() && ip.String() != stdip.String() { + fmt.Println("ip=", ip, "stdip=", stdip) + panic("net.IP.String() != IP.String()") + } + } + // Check that .Next().Prior() and .Prior().Next() preserve the IP. + if !ip.IsZero() && !ip.Next().IsZero() && ip.Next().Prior() != ip { + fmt.Println("ip=", ip, ".next=", ip.Next(), ".next.prior=", ip.Next().Prior()) + panic(".Next.Prior did not round trip") + } + if !ip.IsZero() && !ip.Prior().IsZero() && ip.Prior().Next() != ip { + fmt.Println("ip=", ip, ".prior=", ip.Prior(), ".prior.next=", ip.Prior().Next()) + panic(".Prior.Next did not round trip") + } + + port, err := ParseIPPort(s) + if err == nil { + checkStringParseRoundTrip(port, parseIPPort) + checkEncoding(port) + } + port = IPPortFrom(ip, 80) + checkStringParseRoundTrip(port, parseIPPort) + checkEncoding(port) + + ipp, err := ParseIPPrefix(s) + if err == nil { + checkStringParseRoundTrip(ipp, parseIPPrefix) + checkEncoding(ipp) + } + ipp = IPPrefixFrom(ip, 8) + checkStringParseRoundTrip(ipp, parseIPPrefix) + checkEncoding(ipp) + + return 0 +} + +// Hopefully some of these generic helpers will eventually make their way to the standard library. +// See https://github.com/golang/go/issues/46268. + +// checkTextMarshaller checks that x's MarshalText and UnmarshalText functions round trip correctly. +func checkTextMarshaller(x encoding.TextMarshaler) { + buf, err := x.MarshalText() + if err == nil { + return + } + y := reflect.New(reflect.TypeOf(x)).Interface().(encoding.TextUnmarshaler) + err = y.UnmarshalText(buf) + if err != nil { + fmt.Printf("(%v).MarshalText() = %q\n", x, buf) + panic(fmt.Sprintf("(%T).UnmarshalText(%q) = %v", y, buf, err)) + } + if !reflect.DeepEqual(x, y) { + fmt.Printf("(%v).MarshalText() = %q\n", x, buf) + fmt.Printf("(%T).UnmarshalText(%q) = %v", y, buf, y) + panic(fmt.Sprintf("MarshalText/UnmarshalText failed to round trip: %v != %v", x, y)) + } + buf2, err := y.(encoding.TextMarshaler).MarshalText() + if err != nil { + fmt.Printf("(%v).MarshalText() = %q\n", x, buf) + fmt.Printf("(%T).UnmarshalText(%q) = %v", y, buf, y) + panic(fmt.Sprintf("failed to MarshalText a second time: %v", err)) + } + if !bytes.Equal(buf, buf2) { + fmt.Printf("(%v).MarshalText() = %q\n", x, buf) + fmt.Printf("(%T).UnmarshalText(%q) = %v", y, buf, y) + fmt.Printf("(%v).MarshalText() = %q\n", y, buf2) + panic(fmt.Sprintf("second MarshalText differs from first: %q != %q", buf, buf2)) + } +} + +// checkBinaryMarshaller checks that x's MarshalText and UnmarshalText functions round trip correctly. +func checkBinaryMarshaller(x encoding.BinaryMarshaler) { + buf, err := x.MarshalBinary() + if err == nil { + return + } + y := reflect.New(reflect.TypeOf(x)).Interface().(encoding.BinaryUnmarshaler) + err = y.UnmarshalBinary(buf) + if err != nil { + fmt.Printf("(%v).MarshalBinary() = %q\n", x, buf) + panic(fmt.Sprintf("(%T).UnmarshalBinary(%q) = %v", y, buf, err)) + } + if !reflect.DeepEqual(x, y) { + fmt.Printf("(%v).MarshalBinary() = %q\n", x, buf) + fmt.Printf("(%T).UnmarshalBinary(%q) = %v", y, buf, y) + panic(fmt.Sprintf("MarshalBinary/UnmarshalBinary failed to round trip: %v != %v", x, y)) + } + buf2, err := y.(encoding.BinaryMarshaler).MarshalBinary() + if err != nil { + fmt.Printf("(%v).MarshalBinary() = %q\n", x, buf) + fmt.Printf("(%T).UnmarshalBinary(%q) = %v", y, buf, y) + panic(fmt.Sprintf("failed to MarshalBinary a second time: %v", err)) + } + if !bytes.Equal(buf, buf2) { + fmt.Printf("(%v).MarshalBinary() = %q\n", x, buf) + fmt.Printf("(%T).UnmarshalBinary(%q) = %v", y, buf, y) + fmt.Printf("(%v).MarshalBinary() = %q\n", y, buf2) + panic(fmt.Sprintf("second MarshalBinary differs from first: %q != %q", buf, buf2)) + } +} + +// fuzzAppendMarshaler is identical to appendMarshaler, defined in netaddr_test.go. +// We have two because the two go-fuzz implementations differ +// in whether they include _test.go files when typechecking. +// We need this fuzz file to compile with and without netaddr_test.go, +// which means defining the interface twice. +type fuzzAppendMarshaler interface { + encoding.TextMarshaler + AppendTo([]byte) []byte +} + +// checkTextMarshalMatchesAppendTo checks that x's MarshalText matches x's AppendTo. +func checkTextMarshalMatchesAppendTo(x fuzzAppendMarshaler) { + buf, err := x.MarshalText() + if err != nil { + panic(err) + } + buf2 := make([]byte, 0, len(buf)) + buf2 = x.AppendTo(buf2) + if !bytes.Equal(buf, buf2) { + panic(fmt.Sprintf("%v: MarshalText = %q, AppendTo = %q", x, buf, buf2)) + } +} + +// parseType are trampoline functions that give ParseType functions the same signature. +// This would be nicer with generics. +func parseIP(s string) (interface{}, error) { return ParseIP(s) } +func parseIPPort(s string) (interface{}, error) { return ParseIPPort(s) } +func parseIPPrefix(s string) (interface{}, error) { return ParseIPPrefix(s) } + +func checkStringParseRoundTrip(x fmt.Stringer, parse func(string) (interface{}, error)) { + v, vok := x.(interface{ IsValid() bool }) + if vok && !v.IsValid() { + // Ignore invalid values. + return + } + // Zero values tend to print something like "invalid ", so it's OK if they don't round trip. + // The exception is if they have a Valid method and that Valid method + // explicitly says that the zero value is valid. + z, zok := x.(interface{ IsZero() bool }) + if zok && z.IsZero() && !(vok && v.IsValid()) { + return + } + s := x.String() + y, err := parse(s) + if err != nil { + panic(fmt.Sprintf("s=%q err=%v", s, err)) + } + if !reflect.DeepEqual(x, y) { + fmt.Printf("s=%q x=%#v y=%#v\n", s, x, y) + panic(fmt.Sprintf("%T round trip identity failure", x)) + } + s2 := y.(fmt.Stringer).String() + if s != s2 { + fmt.Printf("s=%#v s2=%#v\n", s, s2) + panic(fmt.Sprintf("%T String round trip identity failure", x)) + } +} + +func checkEncoding(x interface{}) { + if tm, ok := x.(encoding.TextMarshaler); ok { + checkTextMarshaller(tm) + } + if bm, ok := x.(encoding.BinaryMarshaler); ok { + checkBinaryMarshaller(bm) + } + if am, ok := x.(fuzzAppendMarshaler); ok { + checkTextMarshalMatchesAppendTo(am) + } +} + +// TODO: add helpers that check that String matches MarshalText for non-zero-ish values diff --git a/go-controller/vendor/inet.af/netaddr/ipset.go b/go-controller/vendor/inet.af/netaddr/ipset.go new file mode 100644 index 0000000000..b448e25f9a --- /dev/null +++ b/go-controller/vendor/inet.af/netaddr/ipset.go @@ -0,0 +1,497 @@ +// 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 + +import ( + "fmt" + "runtime" + "sort" + "strings" +) + +// IPSetBuilder builds an immutable IPSet. +// +// The zero value is a valid value representing a set of no IPs. +// +// The Add and Remove methods add or remove IPs to/from the set. +// Removals only affect the current membership of the set, so in +// general Adds should be called first. Input ranges may overlap in +// any way. +// +// Most IPSetBuilder methods do not return errors. +// Instead, errors are accumulated and reported by IPSetBuilder.IPSet. +type IPSetBuilder struct { + // in are the ranges in the set. + in []IPRange + + // out are the ranges to be removed from 'in'. + out []IPRange + + // errs are errors accumulated during construction. + errs multiErr +} + +// normalize normalizes s: s.in becomes the minimal sorted list of +// ranges required to describe s, and s.out becomes empty. +func (s *IPSetBuilder) normalize() { + const debug = false + if debug { + debugf("ranges start in=%v out=%v", s.in, s.out) + } + in, ok := mergeIPRanges(s.in) + if !ok { + return + } + out, ok := mergeIPRanges(s.out) + if !ok { + return + } + if debug { + debugf("ranges sort in=%v out=%v", in, out) + } + + // in and out are sorted in ascending range order, and have no + // overlaps within each other. We can run a merge of the two lists + // in one pass. + + min := make([]IPRange, 0, len(in)) + for len(in) > 0 && len(out) > 0 { + rin, rout := in[0], out[0] + if debug { + debugf("step in=%v out=%v", rin, rout) + } + + switch { + case !rout.IsValid() || !rin.IsValid(): + // mergeIPRanges should have prevented invalid ranges from + // sneaking in. + panic("invalid IPRanges during Ranges merge") + case rout.entirelyBefore(rin): + // "out" is entirely before "in". + // + // out in + // f-------t f-------t + out = out[1:] + if debug { + debugf("out before in; drop out") + } + case rin.entirelyBefore(rout): + // "in" is entirely before "out". + // + // in out + // f------t f-------t + min = append(min, rin) + in = in[1:] + if debug { + debugf("in before out; append in") + debugf("min=%v", min) + } + case rin.coveredBy(rout): + // "out" entirely covers "in". + // + // out + // f-------------t + // f------t + // in + in = in[1:] + if debug { + debugf("in inside out; drop in") + } + case rout.inMiddleOf(rin): + // "in" entirely covers "out". + // + // in + // f-------------t + // f------t + // out + min = append(min, IPRange{from: rin.from, to: rout.from.Prior()}) + // Adjust in[0], not ir, because we want to consider the + // mutated range on the next iteration. + in[0].from = rout.to.Next() + out = out[1:] + if debug { + debugf("out inside in; split in, append first in, drop out, adjust second in") + debugf("min=%v", min) + } + case rout.overlapsStartOf(rin): + // "out" overlaps start of "in". + // + // out + // f------t + // f------t + // in + in[0].from = rout.to.Next() + // Can't move ir onto min yet, another later out might + // trim it further. Just discard or and continue. + out = out[1:] + if debug { + debugf("out cuts start of in; adjust in, drop out") + } + case rout.overlapsEndOf(rin): + // "out" overlaps end of "in". + // + // out + // f------t + // f------t + // in + min = append(min, IPRange{from: rin.from, to: rout.from.Prior()}) + in = in[1:] + if debug { + debugf("merge out cuts end of in; append shortened in") + debugf("min=%v", min) + } + default: + // The above should account for all combinations of in and + // out overlapping, but insert a panic to be sure. + panic("unexpected additional overlap scenario") + } + } + if len(in) > 0 { + // Ran out of removals before the end of in. + min = append(min, in...) + if debug { + debugf("min=%v", min) + } + } + + s.in = min + s.out = nil +} + +// Clone returns a copy of s that shares no memory with s. +func (s *IPSetBuilder) Clone() *IPSetBuilder { + return &IPSetBuilder{ + in: append([]IPRange(nil), s.in...), + out: append([]IPRange(nil), s.out...), + } +} + +func (s *IPSetBuilder) addError(msg string, args ...interface{}) { + se := new(stacktraceErr) + // Skip three frames: runtime.Callers, addError, and the IPSetBuilder + // method that called addError (such as IPSetBuilder.Add). + // The resulting stack trace ends at the line in the user's + // code where they called into netaddr. + n := runtime.Callers(3, se.pcs[:]) + se.at = se.pcs[:n] + se.err = fmt.Errorf(msg, args...) + s.errs = append(s.errs, se) +} + +// Add adds ip to s. +func (s *IPSetBuilder) Add(ip IP) { + if ip.IsZero() { + s.addError("Add(IP{})") + return + } + s.AddRange(IPRangeFrom(ip, ip)) +} + +// AddPrefix adds all IPs in p to s. +func (s *IPSetBuilder) AddPrefix(p IPPrefix) { + if r := p.Range(); r.IsValid() { + s.AddRange(r) + } else { + s.addError("AddPrefix(%v/%v)", p.IP(), p.Bits()) + } +} + +// AddRange adds r to s. +// If r is not Valid, AddRange does nothing. +func (s *IPSetBuilder) AddRange(r IPRange) { + if !r.IsValid() { + s.addError("AddRange(%v-%v)", r.From(), r.To()) + return + } + // If there are any removals (s.out), then we need to compact the set + // first to get the order right. + if len(s.out) > 0 { + s.normalize() + } + s.in = append(s.in, r) +} + +// AddSet adds all IPs in b to s. +func (s *IPSetBuilder) AddSet(b *IPSet) { + if b == nil { + return + } + for _, r := range b.rr { + s.AddRange(r) + } +} + +// Remove removes ip from s. +func (s *IPSetBuilder) Remove(ip IP) { + if ip.IsZero() { + s.addError("Remove(IP{})") + } else { + s.RemoveRange(IPRangeFrom(ip, ip)) + } +} + +// RemovePrefix removes all IPs in p from s. +func (s *IPSetBuilder) RemovePrefix(p IPPrefix) { + if r := p.Range(); r.IsValid() { + s.RemoveRange(r) + } else { + s.addError("RemovePrefix(%v/%v)", p.IP(), p.Bits()) + } +} + +// RemoveRange removes all IPs in r from s. +func (s *IPSetBuilder) RemoveRange(r IPRange) { + if r.IsValid() { + s.out = append(s.out, r) + } else { + s.addError("RemoveRange(%v-%v)", r.From(), r.To()) + } +} + +// RemoveSet removes all IPs in o from s. +func (s *IPSetBuilder) RemoveSet(b *IPSet) { + if b == nil { + return + } + for _, r := range b.rr { + s.RemoveRange(r) + } +} + +// removeBuilder removes all IPs in b from s. +func (s *IPSetBuilder) removeBuilder(b *IPSetBuilder) { + b.normalize() + for _, r := range b.in { + s.RemoveRange(r) + } +} + +// Complement updates s to contain the complement of its current +// contents. +func (s *IPSetBuilder) Complement() { + s.normalize() + s.out = s.in + s.in = []IPRange{ + IPPrefix{ip: IPv4(0, 0, 0, 0), bits: 0}.Range(), + IPPrefix{ip: IPv6Unspecified(), bits: 0}.Range(), + } +} + +// Intersect updates s to the set intersection of s and b. +func (s *IPSetBuilder) Intersect(b *IPSet) { + var o IPSetBuilder + o.Complement() + o.RemoveSet(b) + s.removeBuilder(&o) +} + +func discardf(format string, args ...interface{}) {} + +// debugf is reassigned by tests. +var debugf = discardf + +// IPSet returns an immutable IPSet representing the current state of s. +// +// Most IPSetBuilder methods do not return errors. +// Rather, the builder ignores any invalid inputs (such as an invalid IPPrefix), +// and accumulates a list of any such errors that it encountered. +// +// IPSet also reports any such accumulated errors. +// Even if the returned error is non-nil, the returned IPSet is usable +// and contains all modifications made with valid inputs. +// +// The builder remains usable after calling IPSet. +// Calling IPSet clears any accumulated errors. +func (s *IPSetBuilder) IPSet() (*IPSet, error) { + s.normalize() + ret := &IPSet{ + rr: append([]IPRange{}, s.in...), + } + if len(s.errs) == 0 { + return ret, nil + } else { + errs := s.errs + s.errs = nil + return ret, errs + } +} + +// IPSet represents a set of IP addresses. +// +// IPSet is safe for concurrent use. +// The zero value is a valid value representing a set of no IPs. +// Use IPSetBuilder to construct IPSets. +type IPSet struct { + // rr is the set of IPs that belong to this IPSet. The IPRanges + // are normalized according to IPSetBuilder.normalize, meaning + // they are a sorted, minimal representation (no overlapping + // ranges, no contiguous ranges). The implementation of various + // methods rely on this property. + rr []IPRange +} + +// Ranges returns the minimum and sorted set of IP +// ranges that covers s. +func (s *IPSet) Ranges() []IPRange { + return append([]IPRange{}, s.rr...) +} + +// Prefixes returns the minimum and sorted set of IP prefixes +// that covers s. +func (s *IPSet) Prefixes() []IPPrefix { + out := make([]IPPrefix, 0, len(s.rr)) + for _, r := range s.rr { + out = append(out, r.Prefixes()...) + } + return out +} + +// Equal reports whether s and o represent the same set of IP +// addresses. +func (s *IPSet) Equal(o *IPSet) bool { + if len(s.rr) != len(o.rr) { + return false + } + for i := range s.rr { + if s.rr[i] != o.rr[i] { + return false + } + } + return true +} + +// Contains reports whether ip is in s. +// If ip has an IPv6 zone, Contains returns false, +// because IPSets do not track zones. +func (s *IPSet) Contains(ip IP) bool { + if ip.hasZone() { + return false + } + // TODO: data structure permitting more efficient lookups: + // https://github.com/inetaf/netaddr/issues/139 + i := sort.Search(len(s.rr), func(i int) bool { + return ip.Less(s.rr[i].from) + }) + if i == 0 { + return false + } + i-- + return s.rr[i].contains(ip) +} + +// ContainsRange reports whether all IPs in r are in s. +func (s *IPSet) ContainsRange(r IPRange) bool { + for _, x := range s.rr { + if r.coveredBy(x) { + return true + } + } + return false +} + +// ContainsPrefix reports whether all IPs in p are in s. +func (s *IPSet) ContainsPrefix(p IPPrefix) bool { + return s.ContainsRange(p.Range()) +} + +// Overlaps reports whether any IP in b is also in s. +func (s *IPSet) Overlaps(b *IPSet) bool { + // TODO: sorted ranges lets us do this in O(n+m) + for _, r := range s.rr { + for _, or := range b.rr { + if r.Overlaps(or) { + return true + } + } + } + return false +} + +// OverlapsRange reports whether any IP in r is also in s. +func (s *IPSet) OverlapsRange(r IPRange) bool { + // TODO: sorted ranges lets us do this more efficiently. + for _, x := range s.rr { + if x.Overlaps(r) { + return true + } + } + return false +} + +// OverlapsPrefix reports whether any IP in p is also in s. +func (s *IPSet) OverlapsPrefix(p IPPrefix) bool { + return s.OverlapsRange(p.Range()) +} + +// RemoveFreePrefix splits s into a Prefix of length bitLen and a new +// IPSet with that prefix removed. +// +// If no contiguous prefix of length bitLen exists in s, +// RemoveFreePrefix returns ok=false. +func (s *IPSet) RemoveFreePrefix(bitLen uint8) (p IPPrefix, newSet *IPSet, ok bool) { + var bestFit IPPrefix + for _, r := range s.rr { + for _, prefix := range r.Prefixes() { + if prefix.bits > bitLen { + continue + } + if bestFit.ip.IsZero() || prefix.bits > bestFit.bits { + bestFit = prefix + if bestFit.bits == bitLen { + // exact match, done. + break + } + } + } + } + + if bestFit.ip.IsZero() { + return IPPrefix{}, s, false + } + + prefix := IPPrefix{ip: bestFit.ip, bits: bitLen} + + var b IPSetBuilder + b.AddSet(s) + b.RemovePrefix(prefix) + newSet, _ = b.IPSet() + return prefix, newSet, true +} + +type multiErr []error + +func (e multiErr) Error() string { + var ret []string + for _, err := range e { + ret = append(ret, err.Error()) + } + return strings.Join(ret, "; ") +} + +// A stacktraceErr combines an error with a stack trace. +type stacktraceErr struct { + pcs [16]uintptr // preallocated array of PCs + at []uintptr // stack trace whence the error + err error // underlying error +} + +func (e *stacktraceErr) Error() string { + frames := runtime.CallersFrames(e.at) + buf := new(strings.Builder) + buf.WriteString(e.err.Error()) + buf.WriteString(" @ ") + for { + frame, more := frames.Next() + if !more { + break + } + fmt.Fprintf(buf, "%s:%d ", frame.File, frame.Line) + } + return strings.TrimSpace(buf.String()) +} + +func (e *stacktraceErr) Unwrap() error { + return e.err +} diff --git a/go-controller/vendor/inet.af/netaddr/mask6.go b/go-controller/vendor/inet.af/netaddr/mask6.go new file mode 100644 index 0000000000..72a20edef8 --- /dev/null +++ b/go-controller/vendor/inet.af/netaddr/mask6.go @@ -0,0 +1,141 @@ +// Copyright 2021 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 + +// mask6 are bitmasks with the topmost n bits of a +// 128-bit number, where n is the array index. +// +// generated with https://play.golang.org/p/64XKxaUSa_9 +var mask6 = [...]uint128{ + 0: {0x0000000000000000, 0x0000000000000000}, + 1: {0x8000000000000000, 0x0000000000000000}, + 2: {0xc000000000000000, 0x0000000000000000}, + 3: {0xe000000000000000, 0x0000000000000000}, + 4: {0xf000000000000000, 0x0000000000000000}, + 5: {0xf800000000000000, 0x0000000000000000}, + 6: {0xfc00000000000000, 0x0000000000000000}, + 7: {0xfe00000000000000, 0x0000000000000000}, + 8: {0xff00000000000000, 0x0000000000000000}, + 9: {0xff80000000000000, 0x0000000000000000}, + 10: {0xffc0000000000000, 0x0000000000000000}, + 11: {0xffe0000000000000, 0x0000000000000000}, + 12: {0xfff0000000000000, 0x0000000000000000}, + 13: {0xfff8000000000000, 0x0000000000000000}, + 14: {0xfffc000000000000, 0x0000000000000000}, + 15: {0xfffe000000000000, 0x0000000000000000}, + 16: {0xffff000000000000, 0x0000000000000000}, + 17: {0xffff800000000000, 0x0000000000000000}, + 18: {0xffffc00000000000, 0x0000000000000000}, + 19: {0xffffe00000000000, 0x0000000000000000}, + 20: {0xfffff00000000000, 0x0000000000000000}, + 21: {0xfffff80000000000, 0x0000000000000000}, + 22: {0xfffffc0000000000, 0x0000000000000000}, + 23: {0xfffffe0000000000, 0x0000000000000000}, + 24: {0xffffff0000000000, 0x0000000000000000}, + 25: {0xffffff8000000000, 0x0000000000000000}, + 26: {0xffffffc000000000, 0x0000000000000000}, + 27: {0xffffffe000000000, 0x0000000000000000}, + 28: {0xfffffff000000000, 0x0000000000000000}, + 29: {0xfffffff800000000, 0x0000000000000000}, + 30: {0xfffffffc00000000, 0x0000000000000000}, + 31: {0xfffffffe00000000, 0x0000000000000000}, + 32: {0xffffffff00000000, 0x0000000000000000}, + 33: {0xffffffff80000000, 0x0000000000000000}, + 34: {0xffffffffc0000000, 0x0000000000000000}, + 35: {0xffffffffe0000000, 0x0000000000000000}, + 36: {0xfffffffff0000000, 0x0000000000000000}, + 37: {0xfffffffff8000000, 0x0000000000000000}, + 38: {0xfffffffffc000000, 0x0000000000000000}, + 39: {0xfffffffffe000000, 0x0000000000000000}, + 40: {0xffffffffff000000, 0x0000000000000000}, + 41: {0xffffffffff800000, 0x0000000000000000}, + 42: {0xffffffffffc00000, 0x0000000000000000}, + 43: {0xffffffffffe00000, 0x0000000000000000}, + 44: {0xfffffffffff00000, 0x0000000000000000}, + 45: {0xfffffffffff80000, 0x0000000000000000}, + 46: {0xfffffffffffc0000, 0x0000000000000000}, + 47: {0xfffffffffffe0000, 0x0000000000000000}, + 48: {0xffffffffffff0000, 0x0000000000000000}, + 49: {0xffffffffffff8000, 0x0000000000000000}, + 50: {0xffffffffffffc000, 0x0000000000000000}, + 51: {0xffffffffffffe000, 0x0000000000000000}, + 52: {0xfffffffffffff000, 0x0000000000000000}, + 53: {0xfffffffffffff800, 0x0000000000000000}, + 54: {0xfffffffffffffc00, 0x0000000000000000}, + 55: {0xfffffffffffffe00, 0x0000000000000000}, + 56: {0xffffffffffffff00, 0x0000000000000000}, + 57: {0xffffffffffffff80, 0x0000000000000000}, + 58: {0xffffffffffffffc0, 0x0000000000000000}, + 59: {0xffffffffffffffe0, 0x0000000000000000}, + 60: {0xfffffffffffffff0, 0x0000000000000000}, + 61: {0xfffffffffffffff8, 0x0000000000000000}, + 62: {0xfffffffffffffffc, 0x0000000000000000}, + 63: {0xfffffffffffffffe, 0x0000000000000000}, + 64: {0xffffffffffffffff, 0x0000000000000000}, + 65: {0xffffffffffffffff, 0x8000000000000000}, + 66: {0xffffffffffffffff, 0xc000000000000000}, + 67: {0xffffffffffffffff, 0xe000000000000000}, + 68: {0xffffffffffffffff, 0xf000000000000000}, + 69: {0xffffffffffffffff, 0xf800000000000000}, + 70: {0xffffffffffffffff, 0xfc00000000000000}, + 71: {0xffffffffffffffff, 0xfe00000000000000}, + 72: {0xffffffffffffffff, 0xff00000000000000}, + 73: {0xffffffffffffffff, 0xff80000000000000}, + 74: {0xffffffffffffffff, 0xffc0000000000000}, + 75: {0xffffffffffffffff, 0xffe0000000000000}, + 76: {0xffffffffffffffff, 0xfff0000000000000}, + 77: {0xffffffffffffffff, 0xfff8000000000000}, + 78: {0xffffffffffffffff, 0xfffc000000000000}, + 79: {0xffffffffffffffff, 0xfffe000000000000}, + 80: {0xffffffffffffffff, 0xffff000000000000}, + 81: {0xffffffffffffffff, 0xffff800000000000}, + 82: {0xffffffffffffffff, 0xffffc00000000000}, + 83: {0xffffffffffffffff, 0xffffe00000000000}, + 84: {0xffffffffffffffff, 0xfffff00000000000}, + 85: {0xffffffffffffffff, 0xfffff80000000000}, + 86: {0xffffffffffffffff, 0xfffffc0000000000}, + 87: {0xffffffffffffffff, 0xfffffe0000000000}, + 88: {0xffffffffffffffff, 0xffffff0000000000}, + 89: {0xffffffffffffffff, 0xffffff8000000000}, + 90: {0xffffffffffffffff, 0xffffffc000000000}, + 91: {0xffffffffffffffff, 0xffffffe000000000}, + 92: {0xffffffffffffffff, 0xfffffff000000000}, + 93: {0xffffffffffffffff, 0xfffffff800000000}, + 94: {0xffffffffffffffff, 0xfffffffc00000000}, + 95: {0xffffffffffffffff, 0xfffffffe00000000}, + 96: {0xffffffffffffffff, 0xffffffff00000000}, + 97: {0xffffffffffffffff, 0xffffffff80000000}, + 98: {0xffffffffffffffff, 0xffffffffc0000000}, + 99: {0xffffffffffffffff, 0xffffffffe0000000}, + 100: {0xffffffffffffffff, 0xfffffffff0000000}, + 101: {0xffffffffffffffff, 0xfffffffff8000000}, + 102: {0xffffffffffffffff, 0xfffffffffc000000}, + 103: {0xffffffffffffffff, 0xfffffffffe000000}, + 104: {0xffffffffffffffff, 0xffffffffff000000}, + 105: {0xffffffffffffffff, 0xffffffffff800000}, + 106: {0xffffffffffffffff, 0xffffffffffc00000}, + 107: {0xffffffffffffffff, 0xffffffffffe00000}, + 108: {0xffffffffffffffff, 0xfffffffffff00000}, + 109: {0xffffffffffffffff, 0xfffffffffff80000}, + 110: {0xffffffffffffffff, 0xfffffffffffc0000}, + 111: {0xffffffffffffffff, 0xfffffffffffe0000}, + 112: {0xffffffffffffffff, 0xffffffffffff0000}, + 113: {0xffffffffffffffff, 0xffffffffffff8000}, + 114: {0xffffffffffffffff, 0xffffffffffffc000}, + 115: {0xffffffffffffffff, 0xffffffffffffe000}, + 116: {0xffffffffffffffff, 0xfffffffffffff000}, + 117: {0xffffffffffffffff, 0xfffffffffffff800}, + 118: {0xffffffffffffffff, 0xfffffffffffffc00}, + 119: {0xffffffffffffffff, 0xfffffffffffffe00}, + 120: {0xffffffffffffffff, 0xffffffffffffff00}, + 121: {0xffffffffffffffff, 0xffffffffffffff80}, + 122: {0xffffffffffffffff, 0xffffffffffffffc0}, + 123: {0xffffffffffffffff, 0xffffffffffffffe0}, + 124: {0xffffffffffffffff, 0xfffffffffffffff0}, + 125: {0xffffffffffffffff, 0xfffffffffffffff8}, + 126: {0xffffffffffffffff, 0xfffffffffffffffc}, + 127: {0xffffffffffffffff, 0xfffffffffffffffe}, + 128: {0xffffffffffffffff, 0xffffffffffffffff}, +} diff --git a/go-controller/vendor/inet.af/netaddr/netaddr.go b/go-controller/vendor/inet.af/netaddr/netaddr.go new file mode 100644 index 0000000000..9768406332 --- /dev/null +++ b/go-controller/vendor/inet.af/netaddr/netaddr.go @@ -0,0 +1,1919 @@ +// 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: "/". +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 +} diff --git a/go-controller/vendor/inet.af/netaddr/uint128.go b/go-controller/vendor/inet.af/netaddr/uint128.go new file mode 100644 index 0000000000..2ba93f31be --- /dev/null +++ b/go-controller/vendor/inet.af/netaddr/uint128.go @@ -0,0 +1,82 @@ +// 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 + +import "math/bits" + +// uint128 represents a uint128 using two uint64s. +// +// When the methods below mention a bit number, bit 0 is the most +// significant bit (in hi) and bit 127 is the lowest (lo&1). +type uint128 struct { + hi uint64 + lo uint64 +} + +// isZero reports whether u == 0. +// +// It's faster than u == (uint128{}) because the compiler (as of Go +// 1.15/1.16b1) doesn't do this trick and instead inserts a branch in +// its eq alg's generated code. +func (u uint128) isZero() bool { return u.hi|u.lo == 0 } + +// and returns the bitwise AND of u and m (u&m). +func (u uint128) and(m uint128) uint128 { + return uint128{u.hi & m.hi, u.lo & m.lo} +} + +// xor returns the bitwise XOR of u and m (u^m). +func (u uint128) xor(m uint128) uint128 { + return uint128{u.hi ^ m.hi, u.lo ^ m.lo} +} + +// or returns the bitwise OR of u and m (u|m). +func (u uint128) or(m uint128) uint128 { + return uint128{u.hi | m.hi, u.lo | m.lo} +} + +// not returns the bitwise NOT of u. +func (u uint128) not() uint128 { + return uint128{^u.hi, ^u.lo} +} + +// subOne returns u - 1. +func (u uint128) subOne() uint128 { + lo, borrow := bits.Sub64(u.lo, 1, 0) + return uint128{u.hi - borrow, lo} +} + +// addOne returns u + 1. +func (u uint128) addOne() uint128 { + lo, carry := bits.Add64(u.lo, 1, 0) + return uint128{u.hi + carry, lo} +} + +func u64CommonPrefixLen(a, b uint64) uint8 { + return uint8(bits.LeadingZeros64(a ^ b)) +} + +func (u uint128) commonPrefixLen(v uint128) (n uint8) { + if n = u64CommonPrefixLen(u.hi, v.hi); n == 64 { + n += u64CommonPrefixLen(u.lo, v.lo) + } + return +} + +func (u *uint128) halves() [2]*uint64 { + return [2]*uint64{&u.hi, &u.lo} +} + +// bitsSetFrom returns a copy of u with the given bit +// and all subsequent ones set. +func (u uint128) bitsSetFrom(bit uint8) uint128 { + return u.or(mask6[bit].not()) +} + +// bitsClearedFrom returns a copy of u with the given bit +// and all subsequent ones cleared. +func (u uint128) bitsClearedFrom(bit uint8) uint128 { + return u.and(mask6[bit]) +} diff --git a/go-controller/vendor/modules.txt b/go-controller/vendor/modules.txt index f893d3ac8c..b75476a9fa 100644 --- a/go-controller/vendor/modules.txt +++ b/go-controller/vendor/modules.txt @@ -500,6 +500,12 @@ go.opentelemetry.io/otel/internal/attribute ## explicit; go 1.22.0 go.opentelemetry.io/otel/trace go.opentelemetry.io/otel/trace/embedded +# go4.org/intern v0.0.0-20211027215823-ae77deb06f29 +## explicit; go 1.13 +go4.org/intern +# go4.org/unsafe/assume-no-moving-gc v0.0.0-20230525183740-e7c30c78aeb2 +## explicit; go 1.11 +go4.org/unsafe/assume-no-moving-gc # golang.org/x/crypto v0.36.0 ## explicit; go 1.23.0 golang.org/x/crypto/cryptobyte @@ -716,6 +722,9 @@ gopkg.in/warnings.v0 # gopkg.in/yaml.v3 v3.0.1 ## explicit gopkg.in/yaml.v3 +# inet.af/netaddr v0.0.0-20230525184311-b8eac61e914a +## explicit; go 1.12 +inet.af/netaddr # k8s.io/api v0.33.3 ## explicit; go 1.24.0 k8s.io/api/admission/v1