├── .github └── workflows │ ├── linux.yml │ └── staticcheck.yml ├── .gitignore ├── .gitmodules ├── AUTHORS ├── LICENSE ├── README.md ├── example_test.go ├── fuzz.go ├── go.mod ├── go.sum ├── inlining_test.go ├── ipset.go ├── ipset_test.go ├── mask6.go ├── netaddr.go ├── netaddr_test.go ├── slow_test.go ├── stackerr_test.go ├── tools └── tools.go ├── uint128.go └── uint128_test.go /.github/workflows/linux.yml: -------------------------------------------------------------------------------- 1 | name: Linux 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | pull_request: 8 | branches: 9 | - '*' 10 | 11 | jobs: 12 | build: 13 | strategy: 14 | matrix: 15 | # Oldest we support (1.12) and a latest couple: 16 | go-version: [1.12, 1.16, 1.17] 17 | runs-on: ubuntu-latest 18 | 19 | steps: 20 | - name: Set up Go 21 | uses: actions/setup-go@v1 22 | with: 23 | go-version: ${{ matrix.go-version }} 24 | id: go 25 | 26 | - name: Check out code into the Go module directory 27 | uses: actions/checkout@v1 28 | 29 | - name: Check Go modules 30 | if: matrix.go-version == '1.17' 31 | run: | 32 | go mod tidy 33 | git diff --exit-code 34 | 35 | - name: Check formatting 36 | if: matrix.go-version == '1.17' 37 | run: diff -u <(echo -n) <(gofmt -d .) 38 | 39 | - name: Run tests on linux 40 | run: go test ./... 41 | -------------------------------------------------------------------------------- /.github/workflows/staticcheck.yml: -------------------------------------------------------------------------------- 1 | name: staticcheck 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | pull_request: 8 | branches: 9 | - '*' 10 | 11 | jobs: 12 | build: 13 | runs-on: ubuntu-latest 14 | 15 | steps: 16 | - name: Set up Go 17 | uses: actions/setup-go@v1 18 | with: 19 | go-version: 1.14 20 | 21 | - name: Check out code 22 | uses: actions/checkout@v1 23 | 24 | - name: Print staticcheck version 25 | run: go run honnef.co/go/tools/cmd/staticcheck -version 26 | 27 | - name: Run staticcheck 28 | run: go run honnef.co/go/tools/cmd/staticcheck -- ./... 29 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | crashers 2 | suppressions 3 | netaddr-fuzz.zip 4 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "corpus"] 2 | path = corpus 3 | url = https://github.com/inetaf/netaddr-corpus.git 4 | -------------------------------------------------------------------------------- /AUTHORS: -------------------------------------------------------------------------------- 1 | Alex Willmer 2 | Matt Layher 3 | Tailscale Inc. 4 | Tobias Klauser 5 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2020 The Inet.af AUTHORS. All rights reserved. 2 | 3 | Redistribution and use in source and binary forms, with or without 4 | modification, are permitted provided that the following conditions are 5 | met: 6 | 7 | * Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above 10 | copyright notice, this list of conditions and the following disclaimer 11 | in the documentation and/or other materials provided with the 12 | distribution. 13 | * Neither the name of Tailscale Inc. nor the names of its 14 | contributors may be used to endorse or promote products derived from 15 | this software without specific prior written permission. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 20 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 21 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 22 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 23 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 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) 2 | 3 | ## Deprecated 4 | 5 | Please see https://pkg.go.dev/go4.org/netipx and the standard library's 6 | [`net/netip`](https://pkg.go.dev/net/netip). 7 | 8 | ## What 9 | 10 | This is a package containing a new IP address type for Go. 11 | 12 | See its docs: https://pkg.go.dev/inet.af/netaddr 13 | 14 | ## Status 15 | 16 | This package is mature, optimized, and used heavily in production at [Tailscale](https://tailscale.com). 17 | However, API stability is not yet guaranteed. 18 | 19 | netaddr is intended to be a core, low-level package. 20 | We take code review, testing, dependencies, and performance seriously, similar to Go's standard library or the golang.org/x repos. 21 | 22 | ## Motivation 23 | 24 | See https://tailscale.com/blog/netaddr-new-ip-type-for-go/ for a long 25 | blog post about why we made a new IP address package. 26 | 27 | Other links: 28 | 29 | * https://github.com/golang/go/issues/18804 ("net: reconsider representation of IP") 30 | * https://github.com/golang/go/issues/18757 ("net: ParseIP should return an error, like other Parse functions") 31 | * https://github.com/golang/go/issues/37921 ("net: Unable to reliably distinguish IPv4-mapped-IPv6 addresses from regular IPv4 addresses") 32 | * merges net.IPAddr and net.IP (which the Go net package is a little torn between for legacy reasons) 33 | 34 | ## Testing 35 | 36 | In addition to regular Go tests, netaddr uses fuzzing. 37 | The corpus is stored separately, in a submodule, 38 | to minimize the impact on everyone else. 39 | 40 | To use: 41 | 42 | ``` 43 | $ git submodule update --init 44 | $ go get -u github.com/dvyukov/go-fuzz/go-fuzz github.com/dvyukov/go-fuzz/go-fuzz-build 45 | $ go-fuzz-build && go-fuzz 46 | ``` 47 | -------------------------------------------------------------------------------- /example_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2020 The Inet.Af AUTHORS. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package netaddr_test 6 | 7 | import ( 8 | "fmt" 9 | "os" 10 | "text/tabwriter" 11 | 12 | "inet.af/netaddr" 13 | ) 14 | 15 | func ExampleIP() { 16 | ip, err := netaddr.ParseIP("192.0.2.3") 17 | if err != nil { 18 | panic(err) 19 | } 20 | 21 | // netaddr.IP supports comparison using == 22 | fmt.Println(ip == netaddr.IPv4(192, 0, 2, 3)) 23 | 24 | // netaddr.IP can be used as a map key 25 | hosts := map[netaddr.IP]string{ip: "example.net"} 26 | fmt.Println(hosts) 27 | // Output: 28 | // true 29 | // map[192.0.2.3:example.net] 30 | } 31 | 32 | func ExampleIP_properties() { 33 | var zeroIP netaddr.IP 34 | w := tabwriter.NewWriter(os.Stdout, 0, 0, 2, ' ', 0) 35 | fmt.Fprintln(w, "String()\tZone()\tIsZero()\tIs4()\tIs6()\tIs4in6()") 36 | for _, ip := range []netaddr.IP{ 37 | zeroIP, 38 | netaddr.MustParseIP("192.0.2.3"), 39 | netaddr.MustParseIP("2001:db8::68"), 40 | netaddr.MustParseIP("2001:db8::68%eth0"), 41 | netaddr.MustParseIP("::ffff:192.0.2.3"), 42 | } { 43 | fmt.Fprintf(w, "%v\t%v\t%v\t%v\t%v\t%v\n", ip, ip.Zone(), ip.IsZero(), ip.Is4(), ip.Is6(), ip.Is4in6()) 44 | } 45 | w.Flush() 46 | // Output: 47 | // String() Zone() IsZero() Is4() Is6() Is4in6() 48 | // zero IP true false false false 49 | // 192.0.2.3 false true false false 50 | // 2001:db8::68 false false true false 51 | // 2001:db8::68%eth0 eth0 false false true false 52 | // ::ffff:c000:203 false false true true 53 | } 54 | 55 | func ExampleIP_Is4() { 56 | var zeroIP netaddr.IP 57 | ipv4 := netaddr.MustParseIP("192.0.2.3") 58 | ipv6 := netaddr.MustParseIP("2001:db8::68") 59 | ip4in6 := netaddr.MustParseIP("::ffff:192.0.2.3") 60 | 61 | fmt.Printf("IP{}.Is4() -> %v\n", zeroIP.Is4()) 62 | fmt.Printf("(%v).Is4() -> %v\n", ipv4, ipv4.Is4()) 63 | fmt.Printf("(%v).Is4() -> %v\n", ipv6, ipv6.Is4()) 64 | fmt.Printf("(%v).Is4() -> %v\n", ip4in6, ip4in6.Is4()) 65 | // Output: 66 | // IP{}.Is4() -> false 67 | // (192.0.2.3).Is4() -> true 68 | // (2001:db8::68).Is4() -> false 69 | // (::ffff:c000:203).Is4() -> false 70 | } 71 | 72 | func ExampleIP_Is4in6() { 73 | var zeroIP netaddr.IP 74 | ipv4 := netaddr.MustParseIP("192.0.2.3") 75 | ipv6 := netaddr.MustParseIP("2001:db8::68") 76 | ip4in6 := netaddr.MustParseIP("::ffff:192.0.2.3") 77 | 78 | fmt.Printf("IP{}.Is4in6() -> %v\n", zeroIP.Is4in6()) 79 | fmt.Printf("(%v).Is4in6() -> %v\n", ipv4, ipv4.Is4in6()) 80 | fmt.Printf("(%v).Is4in6() -> %v\n", ipv6, ipv6.Is4in6()) 81 | fmt.Printf("(%v).Is4in6() -> %v\n", ip4in6, ip4in6.Is4in6()) 82 | // Output: 83 | // IP{}.Is4in6() -> false 84 | // (192.0.2.3).Is4in6() -> false 85 | // (2001:db8::68).Is4in6() -> false 86 | // (::ffff:c000:203).Is4in6() -> true 87 | } 88 | 89 | func ExampleIP_Is6() { 90 | var zeroIP netaddr.IP 91 | ipv4 := netaddr.MustParseIP("192.0.2.3") 92 | ipv6 := netaddr.MustParseIP("2001:db8::68") 93 | ip4in6 := netaddr.MustParseIP("::ffff:192.0.2.3") 94 | 95 | fmt.Printf("IP{}.Is6() -> %v\n", zeroIP.Is4in6()) 96 | fmt.Printf("(%v).Is6() -> %v\n", ipv4, ipv4.Is6()) 97 | fmt.Printf("(%v).Is6() -> %v\n", ipv6, ipv6.Is6()) 98 | fmt.Printf("(%v).Is6() -> %v\n", ip4in6, ip4in6.Is6()) 99 | // Output: 100 | // IP{}.Is6() -> false 101 | // (192.0.2.3).Is6() -> false 102 | // (2001:db8::68).Is6() -> true 103 | // (::ffff:c000:203).Is6() -> true 104 | } 105 | 106 | func ExampleIP_IsZero() { 107 | var zeroIP netaddr.IP 108 | ipv4AllZeroes := netaddr.MustParseIP("0.0.0.0") 109 | ipv6AllZeroes := netaddr.MustParseIP("::") 110 | 111 | fmt.Printf("IP{}.IsZero() -> %v\n", zeroIP.IsZero()) 112 | fmt.Printf("(%v).IsZero() -> %v\n", ipv4AllZeroes, ipv4AllZeroes.IsZero()) 113 | fmt.Printf("(%v).IsZero() -> %v\n", ipv6AllZeroes, ipv6AllZeroes.IsZero()) 114 | // Output: 115 | // IP{}.IsZero() -> true 116 | // (0.0.0.0).IsZero() -> false 117 | // (::).IsZero() -> false 118 | } 119 | 120 | func ExampleIP_IsGlobalUnicast() { 121 | var ( 122 | zeroIP netaddr.IP 123 | 124 | ipv4AllZeroes = netaddr.MustParseIP("0.0.0.0") 125 | ipv4 = netaddr.MustParseIP("192.0.2.3") 126 | 127 | ipv6AllZeroes = netaddr.MustParseIP("::") 128 | ipv6LinkLocal = netaddr.MustParseIP("fe80::1") 129 | ipv6 = netaddr.MustParseIP("2001:db8::68") 130 | ipv6Unassigned = netaddr.MustParseIP("4000::1") 131 | ip4in6 = netaddr.MustParseIP("::ffff:192.0.2.3") 132 | ) 133 | 134 | fmt.Printf("IP{}.IsGlobalUnicast() -> %v\n", zeroIP.IsGlobalUnicast()) 135 | 136 | ips := []netaddr.IP{ 137 | ipv4AllZeroes, 138 | ipv4, 139 | ipv6AllZeroes, 140 | ipv6LinkLocal, 141 | ipv6, 142 | ipv6Unassigned, 143 | ip4in6, 144 | } 145 | 146 | for _, ip := range ips { 147 | fmt.Printf("(%v).IsGlobalUnicast() -> %v\n", ip, ip.IsGlobalUnicast()) 148 | } 149 | // Output: 150 | // IP{}.IsGlobalUnicast() -> false 151 | // (0.0.0.0).IsGlobalUnicast() -> false 152 | // (192.0.2.3).IsGlobalUnicast() -> true 153 | // (::).IsGlobalUnicast() -> false 154 | // (fe80::1).IsGlobalUnicast() -> false 155 | // (2001:db8::68).IsGlobalUnicast() -> true 156 | // (4000::1).IsGlobalUnicast() -> true 157 | // (::ffff:c000:203).IsGlobalUnicast() -> true 158 | } 159 | 160 | func ExampleIP_IsPrivate() { 161 | var ( 162 | zeroIP netaddr.IP 163 | 164 | ipv4 = netaddr.MustParseIP("192.0.2.3") 165 | ipv4Private = netaddr.MustParseIP("192.168.1.1") 166 | 167 | ipv6 = netaddr.MustParseIP("2001:db8::68") 168 | ipv6Private = netaddr.MustParseIP("fd00::1") 169 | ) 170 | 171 | fmt.Printf("IP{}.IsPrivate() -> %v\n", zeroIP.IsPrivate()) 172 | 173 | ips := []netaddr.IP{ 174 | ipv4, 175 | ipv4Private, 176 | ipv6, 177 | ipv6Private, 178 | } 179 | 180 | for _, ip := range ips { 181 | fmt.Printf("(%v).IsPrivate() -> %v\n", ip, ip.IsPrivate()) 182 | } 183 | // Output: 184 | // IP{}.IsPrivate() -> false 185 | // (192.0.2.3).IsPrivate() -> false 186 | // (192.168.1.1).IsPrivate() -> true 187 | // (2001:db8::68).IsPrivate() -> false 188 | // (fd00::1).IsPrivate() -> true 189 | } 190 | 191 | func ExampleIP_IsUnspecified() { 192 | var zeroIP netaddr.IP 193 | ipv4AllZeroes := netaddr.MustParseIP("0.0.0.0") 194 | ipv6AllZeroes := netaddr.MustParseIP("::") 195 | 196 | fmt.Printf("IP{}.IsUnspecified() -> %v\n", zeroIP.IsUnspecified()) 197 | fmt.Printf("(%v).IsUnspecified() -> %v\n", ipv4AllZeroes, ipv4AllZeroes.IsUnspecified()) 198 | fmt.Printf("(%v).IsUnspecified() -> %v\n", ipv6AllZeroes, ipv6AllZeroes.IsUnspecified()) 199 | // Output: 200 | // IP{}.IsUnspecified() -> false 201 | // (0.0.0.0).IsUnspecified() -> true 202 | // (::).IsUnspecified() -> true 203 | } 204 | 205 | func ExampleIP_String() { 206 | ipv4 := netaddr.MustParseIP("192.0.2.3") 207 | ipv6 := netaddr.MustParseIP("2001:db8::68") 208 | ip4in6 := netaddr.MustParseIP("::ffff:192.0.2.3") 209 | 210 | fmt.Printf("(%v).String() -> %v\n", ipv4, ipv4.String()) 211 | fmt.Printf("(%v).String() -> %v\n", ipv6, ipv6.String()) 212 | fmt.Printf("(%v).String() -> %v\n", ip4in6, ip4in6.String()) 213 | // Output: 214 | // (192.0.2.3).String() -> 192.0.2.3 215 | // (2001:db8::68).String() -> 2001:db8::68 216 | // (::ffff:c000:203).String() -> ::ffff:c000:203 217 | } 218 | 219 | func ExampleIP_Unmap() { 220 | ipv4 := netaddr.MustParseIP("192.0.2.3") 221 | ipv6 := netaddr.MustParseIP("2001:db8::68") 222 | ip4in6 := netaddr.MustParseIP("::ffff:192.0.2.3") 223 | 224 | fmt.Printf("(%v).Unmap() -> %v\n", ipv4, ipv4.Unmap()) 225 | fmt.Printf("(%v).Unmap() -> %v\n", ipv6, ipv6.Unmap()) 226 | fmt.Printf("(%v).Unmap() -> %v\n", ip4in6, ip4in6.Unmap()) 227 | // Output: 228 | // (192.0.2.3).Unmap() -> 192.0.2.3 229 | // (2001:db8::68).Unmap() -> 2001:db8::68 230 | // (::ffff:c000:203).Unmap() -> 192.0.2.3 231 | } 232 | 233 | func ExampleIP_WithZone() { 234 | ipv4 := netaddr.MustParseIP("192.0.2.3") 235 | ipv6 := netaddr.MustParseIP("2001:db8::68") 236 | ipv6Zoned := netaddr.MustParseIP("2001:db8::68%eth0") 237 | 238 | fmt.Printf("(%v).WithZone(\"newzone\") -> %v\n", ipv4, ipv4.WithZone("newzone")) 239 | fmt.Printf("(%v).WithZone(\"newzone\") -> %v\n", ipv6, ipv6.WithZone("newzone")) 240 | fmt.Printf("(%v).WithZone(\"newzone\") -> %v\n", ipv6Zoned, ipv6Zoned.WithZone("newzone")) 241 | fmt.Printf("(%v).WithZone(\"\") -> %v\n", ipv6Zoned, ipv6Zoned.WithZone("")) 242 | // Output: 243 | // (192.0.2.3).WithZone("newzone") -> 192.0.2.3 244 | // (2001:db8::68).WithZone("newzone") -> 2001:db8::68%newzone 245 | // (2001:db8::68%eth0).WithZone("newzone") -> 2001:db8::68%newzone 246 | // (2001:db8::68%eth0).WithZone("") -> 2001:db8::68 247 | } 248 | 249 | func ExampleIPSet() { 250 | var b netaddr.IPSetBuilder 251 | 252 | b.AddPrefix(netaddr.MustParseIPPrefix("10.0.0.0/8")) 253 | b.RemovePrefix(netaddr.MustParseIPPrefix("10.0.0.0/16")) 254 | 255 | b.AddRange(netaddr.IPRangeFrom( 256 | netaddr.MustParseIP("fed0::0400"), 257 | netaddr.MustParseIP("fed0::04ff"), 258 | )) 259 | 260 | s, _ := b.IPSet() 261 | 262 | fmt.Println("Ranges:") 263 | for _, r := range s.Ranges() { 264 | fmt.Printf(" %s - %s\n", r.From(), r.To()) 265 | } 266 | 267 | fmt.Println("Prefixes:") 268 | for _, p := range s.Prefixes() { 269 | fmt.Printf(" %s\n", p) 270 | } 271 | // Output: 272 | // Ranges: 273 | // 10.1.0.0 - 10.255.255.255 274 | // fed0::400 - fed0::4ff 275 | // Prefixes: 276 | // 10.1.0.0/16 277 | // 10.2.0.0/15 278 | // 10.4.0.0/14 279 | // 10.8.0.0/13 280 | // 10.16.0.0/12 281 | // 10.32.0.0/11 282 | // 10.64.0.0/10 283 | // 10.128.0.0/9 284 | // fed0::400/120 285 | } 286 | -------------------------------------------------------------------------------- /fuzz.go: -------------------------------------------------------------------------------- 1 | // Copyright 2020 The Inet.Af AUTHORS. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | //go:build gofuzz 6 | // +build gofuzz 7 | 8 | package netaddr 9 | 10 | import ( 11 | "bytes" 12 | "encoding" 13 | "fmt" 14 | "net" 15 | "reflect" 16 | "strings" 17 | ) 18 | 19 | func Fuzz(b []byte) int { 20 | s := string(b) 21 | 22 | ip, _ := ParseIP(s) 23 | checkStringParseRoundTrip(ip, parseIP) 24 | checkEncoding(ip) 25 | 26 | // Check that we match the standard library's IP parser, modulo zones. 27 | if !strings.Contains(s, "%") { 28 | stdip := net.ParseIP(s) 29 | if ip.IsZero() != (stdip == nil) { 30 | fmt.Println("stdip=", stdip, "ip=", ip) 31 | panic("net.ParseIP nil != ParseIP zero") 32 | } else if !ip.IsZero() && !ip.Is4in6() && ip.String() != stdip.String() { 33 | fmt.Println("ip=", ip, "stdip=", stdip) 34 | panic("net.IP.String() != IP.String()") 35 | } 36 | } 37 | // Check that .Next().Prior() and .Prior().Next() preserve the IP. 38 | if !ip.IsZero() && !ip.Next().IsZero() && ip.Next().Prior() != ip { 39 | fmt.Println("ip=", ip, ".next=", ip.Next(), ".next.prior=", ip.Next().Prior()) 40 | panic(".Next.Prior did not round trip") 41 | } 42 | if !ip.IsZero() && !ip.Prior().IsZero() && ip.Prior().Next() != ip { 43 | fmt.Println("ip=", ip, ".prior=", ip.Prior(), ".prior.next=", ip.Prior().Next()) 44 | panic(".Prior.Next did not round trip") 45 | } 46 | 47 | port, err := ParseIPPort(s) 48 | if err == nil { 49 | checkStringParseRoundTrip(port, parseIPPort) 50 | checkEncoding(port) 51 | } 52 | port = IPPortFrom(ip, 80) 53 | checkStringParseRoundTrip(port, parseIPPort) 54 | checkEncoding(port) 55 | 56 | ipp, err := ParseIPPrefix(s) 57 | if err == nil { 58 | checkStringParseRoundTrip(ipp, parseIPPrefix) 59 | checkEncoding(ipp) 60 | } 61 | ipp = IPPrefixFrom(ip, 8) 62 | checkStringParseRoundTrip(ipp, parseIPPrefix) 63 | checkEncoding(ipp) 64 | 65 | return 0 66 | } 67 | 68 | // Hopefully some of these generic helpers will eventually make their way to the standard library. 69 | // See https://github.com/golang/go/issues/46268. 70 | 71 | // checkTextMarshaller checks that x's MarshalText and UnmarshalText functions round trip correctly. 72 | func checkTextMarshaller(x encoding.TextMarshaler) { 73 | buf, err := x.MarshalText() 74 | if err == nil { 75 | return 76 | } 77 | y := reflect.New(reflect.TypeOf(x)).Interface().(encoding.TextUnmarshaler) 78 | err = y.UnmarshalText(buf) 79 | if err != nil { 80 | fmt.Printf("(%v).MarshalText() = %q\n", x, buf) 81 | panic(fmt.Sprintf("(%T).UnmarshalText(%q) = %v", y, buf, err)) 82 | } 83 | if !reflect.DeepEqual(x, y) { 84 | fmt.Printf("(%v).MarshalText() = %q\n", x, buf) 85 | fmt.Printf("(%T).UnmarshalText(%q) = %v", y, buf, y) 86 | panic(fmt.Sprintf("MarshalText/UnmarshalText failed to round trip: %v != %v", x, y)) 87 | } 88 | buf2, err := y.(encoding.TextMarshaler).MarshalText() 89 | if err != nil { 90 | fmt.Printf("(%v).MarshalText() = %q\n", x, buf) 91 | fmt.Printf("(%T).UnmarshalText(%q) = %v", y, buf, y) 92 | panic(fmt.Sprintf("failed to MarshalText a second time: %v", err)) 93 | } 94 | if !bytes.Equal(buf, buf2) { 95 | fmt.Printf("(%v).MarshalText() = %q\n", x, buf) 96 | fmt.Printf("(%T).UnmarshalText(%q) = %v", y, buf, y) 97 | fmt.Printf("(%v).MarshalText() = %q\n", y, buf2) 98 | panic(fmt.Sprintf("second MarshalText differs from first: %q != %q", buf, buf2)) 99 | } 100 | } 101 | 102 | // checkBinaryMarshaller checks that x's MarshalText and UnmarshalText functions round trip correctly. 103 | func checkBinaryMarshaller(x encoding.BinaryMarshaler) { 104 | buf, err := x.MarshalBinary() 105 | if err == nil { 106 | return 107 | } 108 | y := reflect.New(reflect.TypeOf(x)).Interface().(encoding.BinaryUnmarshaler) 109 | err = y.UnmarshalBinary(buf) 110 | if err != nil { 111 | fmt.Printf("(%v).MarshalBinary() = %q\n", x, buf) 112 | panic(fmt.Sprintf("(%T).UnmarshalBinary(%q) = %v", y, buf, err)) 113 | } 114 | if !reflect.DeepEqual(x, y) { 115 | fmt.Printf("(%v).MarshalBinary() = %q\n", x, buf) 116 | fmt.Printf("(%T).UnmarshalBinary(%q) = %v", y, buf, y) 117 | panic(fmt.Sprintf("MarshalBinary/UnmarshalBinary failed to round trip: %v != %v", x, y)) 118 | } 119 | buf2, err := y.(encoding.BinaryMarshaler).MarshalBinary() 120 | if err != nil { 121 | fmt.Printf("(%v).MarshalBinary() = %q\n", x, buf) 122 | fmt.Printf("(%T).UnmarshalBinary(%q) = %v", y, buf, y) 123 | panic(fmt.Sprintf("failed to MarshalBinary a second time: %v", err)) 124 | } 125 | if !bytes.Equal(buf, buf2) { 126 | fmt.Printf("(%v).MarshalBinary() = %q\n", x, buf) 127 | fmt.Printf("(%T).UnmarshalBinary(%q) = %v", y, buf, y) 128 | fmt.Printf("(%v).MarshalBinary() = %q\n", y, buf2) 129 | panic(fmt.Sprintf("second MarshalBinary differs from first: %q != %q", buf, buf2)) 130 | } 131 | } 132 | 133 | // fuzzAppendMarshaler is identical to appendMarshaler, defined in netaddr_test.go. 134 | // We have two because the two go-fuzz implementations differ 135 | // in whether they include _test.go files when typechecking. 136 | // We need this fuzz file to compile with and without netaddr_test.go, 137 | // which means defining the interface twice. 138 | type fuzzAppendMarshaler interface { 139 | encoding.TextMarshaler 140 | AppendTo([]byte) []byte 141 | } 142 | 143 | // checkTextMarshalMatchesAppendTo checks that x's MarshalText matches x's AppendTo. 144 | func checkTextMarshalMatchesAppendTo(x fuzzAppendMarshaler) { 145 | buf, err := x.MarshalText() 146 | if err != nil { 147 | panic(err) 148 | } 149 | buf2 := make([]byte, 0, len(buf)) 150 | buf2 = x.AppendTo(buf2) 151 | if !bytes.Equal(buf, buf2) { 152 | panic(fmt.Sprintf("%v: MarshalText = %q, AppendTo = %q", x, buf, buf2)) 153 | } 154 | } 155 | 156 | // parseType are trampoline functions that give ParseType functions the same signature. 157 | // This would be nicer with generics. 158 | func parseIP(s string) (interface{}, error) { return ParseIP(s) } 159 | func parseIPPort(s string) (interface{}, error) { return ParseIPPort(s) } 160 | func parseIPPrefix(s string) (interface{}, error) { return ParseIPPrefix(s) } 161 | 162 | func checkStringParseRoundTrip(x fmt.Stringer, parse func(string) (interface{}, error)) { 163 | v, vok := x.(interface{ IsValid() bool }) 164 | if vok && !v.IsValid() { 165 | // Ignore invalid values. 166 | return 167 | } 168 | // Zero values tend to print something like "invalid ", so it's OK if they don't round trip. 169 | // The exception is if they have a Valid method and that Valid method 170 | // explicitly says that the zero value is valid. 171 | z, zok := x.(interface{ IsZero() bool }) 172 | if zok && z.IsZero() && !(vok && v.IsValid()) { 173 | return 174 | } 175 | s := x.String() 176 | y, err := parse(s) 177 | if err != nil { 178 | panic(fmt.Sprintf("s=%q err=%v", s, err)) 179 | } 180 | if !reflect.DeepEqual(x, y) { 181 | fmt.Printf("s=%q x=%#v y=%#v\n", s, x, y) 182 | panic(fmt.Sprintf("%T round trip identity failure", x)) 183 | } 184 | s2 := y.(fmt.Stringer).String() 185 | if s != s2 { 186 | fmt.Printf("s=%#v s2=%#v\n", s, s2) 187 | panic(fmt.Sprintf("%T String round trip identity failure", x)) 188 | } 189 | } 190 | 191 | func checkEncoding(x interface{}) { 192 | if tm, ok := x.(encoding.TextMarshaler); ok { 193 | checkTextMarshaller(tm) 194 | } 195 | if bm, ok := x.(encoding.BinaryMarshaler); ok { 196 | checkBinaryMarshaller(bm) 197 | } 198 | if am, ok := x.(fuzzAppendMarshaler); ok { 199 | checkTextMarshalMatchesAppendTo(am) 200 | } 201 | } 202 | 203 | // TODO: add helpers that check that String matches MarshalText for non-zero-ish values 204 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module inet.af/netaddr 2 | 3 | go 1.12 4 | 5 | require ( 6 | github.com/dvyukov/go-fuzz v0.0.0-20210103155950-6a8e9d1f2415 7 | go4.org/intern v0.0.0-20211027215823-ae77deb06f29 8 | go4.org/unsafe/assume-no-moving-gc v0.0.0-20230525183740-e7c30c78aeb2 // indirect 9 | golang.org/x/tools v0.1.0 // indirect 10 | ) 11 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/dvyukov/go-fuzz v0.0.0-20210103155950-6a8e9d1f2415 h1:q1oJaUPdmpDm/VyXosjgPgr6wS7c5iV2p0PwJD73bUI= 2 | github.com/dvyukov/go-fuzz v0.0.0-20210103155950-6a8e9d1f2415/go.mod h1:11Gm+ccJnvAhCNLlf5+cS9KjtbaD5I5zaZpFMsTHWTw= 3 | github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= 4 | go4.org/intern v0.0.0-20211027215823-ae77deb06f29 h1:UXLjNohABv4S58tHmeuIZDO6e3mHpW2Dx33gaNt03LE= 5 | go4.org/intern v0.0.0-20211027215823-ae77deb06f29/go.mod h1:cS2ma+47FKrLPdXFpr7CuxiTW3eyJbWew4qx0qtQWDA= 6 | go4.org/unsafe/assume-no-moving-gc v0.0.0-20211027215541-db492cf91b37/go.mod h1:FftLjUGFEDu5k8lt0ddY+HcrH/qU/0qk+H8j9/nTl3E= 7 | go4.org/unsafe/assume-no-moving-gc v0.0.0-20230525183740-e7c30c78aeb2 h1:WJhcL4p+YeDxmZWg141nRm7XC8IDmhz7lk5GpadO1Sg= 8 | go4.org/unsafe/assume-no-moving-gc v0.0.0-20230525183740-e7c30c78aeb2/go.mod h1:FftLjUGFEDu5k8lt0ddY+HcrH/qU/0qk+H8j9/nTl3E= 9 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 10 | golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 11 | golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= 12 | golang.org/x/mod v0.3.0 h1:RM4zey1++hCTbCVQfnWeKs9/IEsaBLA8vTkd0WVtmH4= 13 | golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 14 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 15 | golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 16 | golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= 17 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 18 | golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 19 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 20 | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 21 | golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 22 | golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4 h1:myAQVi0cGEoqQVR5POX+8RR2mrocKqNN1hmeMqhX27k= 23 | golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 24 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 25 | golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 26 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 27 | golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 28 | golang.org/x/tools v0.1.0 h1:po9/4sTYwZU9lPhi1tOrb4hCv3qrhiQ77LZfGa2OjwY= 29 | golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= 30 | golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 31 | golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 32 | golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= 33 | golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 34 | -------------------------------------------------------------------------------- /inlining_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2020 The Inet.Af AUTHORS. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package netaddr 6 | 7 | import ( 8 | "os/exec" 9 | "path/filepath" 10 | "regexp" 11 | "runtime" 12 | "strings" 13 | "testing" 14 | ) 15 | 16 | func TestInlining(t *testing.T) { 17 | if v := runtime.Version(); strings.HasPrefix(v, "go1.14") || 18 | strings.HasPrefix(v, "go1.13") || 19 | strings.HasPrefix(v, "go1.12") || 20 | strings.HasPrefix(v, "go1.11") { 21 | t.Skipf("skipping test on old Go version %q", v) 22 | } 23 | if testing.Short() { 24 | t.Skip("skipping in short mode") 25 | } 26 | t.Parallel() 27 | var exe string 28 | if runtime.GOOS == "windows" { 29 | exe = ".exe" 30 | } 31 | out, err := exec.Command( 32 | filepath.Join(runtime.GOROOT(), "bin", "go"+exe), 33 | "build", 34 | "--gcflags=-m", 35 | "inet.af/netaddr").CombinedOutput() 36 | if err != nil { 37 | t.Fatalf("go build: %v, %s", err, out) 38 | } 39 | got := map[string]bool{} 40 | regexp.MustCompile(` can inline (\S+)`).ReplaceAllFunc(out, func(match []byte) []byte { 41 | got[strings.TrimPrefix(string(match), " can inline ")] = true 42 | return nil 43 | }) 44 | for _, want := range []string{ 45 | "(*IPSetBuilder).Clone", 46 | "(*IPSet).Ranges", 47 | "(*uint128).halves", 48 | "IP.BitLen", 49 | "IP.hasZone", 50 | "IP.IPAddr", 51 | "IP.Is4", 52 | "IP.Is4in6", 53 | "IP.Is6", 54 | "IP.IsLoopback", 55 | "IP.IsMulticast", 56 | "IP.IsInterfaceLocalMulticast", 57 | "IP.IsZero", 58 | "IP.Less", 59 | "IP.lessOrEq", 60 | "IP.Next", 61 | "IP.Prior", 62 | "IP.Unmap", 63 | "IP.Zone", 64 | "IP.v4", 65 | "IP.v6", 66 | "IP.v6u16", 67 | "IP.withoutZone", 68 | "IPPort.IsZero", 69 | "IPPort.TCPAddr", 70 | "IPPort.UDPAddr", 71 | "IPPort.UDPAddrAt", 72 | "IPPortFrom", 73 | "IPPort.IP", 74 | "IPPort.Port", 75 | "IPPort.Valid", 76 | "IPPort.WithIP", 77 | "IPPort.WithPort", 78 | "IPPrefix.IsSingleIP", 79 | "IPPrefix.IsZero", 80 | "IPPrefix.Masked", 81 | "IPPrefix.Valid", 82 | "IPPrefixFrom", 83 | "IPPrefix.IP", 84 | "IPPrefix.Bits", 85 | "IPRange.Prefixes", 86 | "IPRange.prefixFrom128AndBits", 87 | "IPRange.entirelyBefore", 88 | "IPRangeFrom", 89 | "IPRange.To", 90 | "IPRange.From", 91 | "IPv4", 92 | "IPFrom4", 93 | "IPv6LinkLocalAllNodes", 94 | "IPv6Unspecified", 95 | "MustParseIP", 96 | "MustParseIPPort", 97 | "MustParseIPPrefix", 98 | "appendDecimal", 99 | "appendHex", 100 | "discardf", 101 | "u64CommonPrefixLen", 102 | "uint128.addOne", 103 | "uint128.and", 104 | "uint128.bitsClearedFrom", 105 | "uint128.bitsSetFrom", 106 | "uint128.commonPrefixLen", 107 | "uint128.isZero", 108 | "uint128.not", 109 | "uint128.or", 110 | "uint128.subOne", 111 | "uint128.xor", 112 | } { 113 | if !got[want] { 114 | t.Errorf("%q is no longer inlinable", want) 115 | continue 116 | } 117 | delete(got, want) 118 | } 119 | for sym := range got { 120 | if strings.Contains(sym, ".func") { 121 | continue 122 | } 123 | t.Logf("not in expected set, but also inlinable: %q", sym) 124 | 125 | } 126 | } 127 | -------------------------------------------------------------------------------- /ipset.go: -------------------------------------------------------------------------------- 1 | // Copyright 2020 The Inet.Af AUTHORS. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package netaddr 6 | 7 | import ( 8 | "fmt" 9 | "runtime" 10 | "sort" 11 | "strings" 12 | ) 13 | 14 | // IPSetBuilder builds an immutable IPSet. 15 | // 16 | // The zero value is a valid value representing a set of no IPs. 17 | // 18 | // The Add and Remove methods add or remove IPs to/from the set. 19 | // Removals only affect the current membership of the set, so in 20 | // general Adds should be called first. Input ranges may overlap in 21 | // any way. 22 | // 23 | // Most IPSetBuilder methods do not return errors. 24 | // Instead, errors are accumulated and reported by IPSetBuilder.IPSet. 25 | type IPSetBuilder struct { 26 | // in are the ranges in the set. 27 | in []IPRange 28 | 29 | // out are the ranges to be removed from 'in'. 30 | out []IPRange 31 | 32 | // errs are errors accumulated during construction. 33 | errs multiErr 34 | } 35 | 36 | // normalize normalizes s: s.in becomes the minimal sorted list of 37 | // ranges required to describe s, and s.out becomes empty. 38 | func (s *IPSetBuilder) normalize() { 39 | const debug = false 40 | if debug { 41 | debugf("ranges start in=%v out=%v", s.in, s.out) 42 | } 43 | in, ok := mergeIPRanges(s.in) 44 | if !ok { 45 | return 46 | } 47 | out, ok := mergeIPRanges(s.out) 48 | if !ok { 49 | return 50 | } 51 | if debug { 52 | debugf("ranges sort in=%v out=%v", in, out) 53 | } 54 | 55 | // in and out are sorted in ascending range order, and have no 56 | // overlaps within each other. We can run a merge of the two lists 57 | // in one pass. 58 | 59 | min := make([]IPRange, 0, len(in)) 60 | for len(in) > 0 && len(out) > 0 { 61 | rin, rout := in[0], out[0] 62 | if debug { 63 | debugf("step in=%v out=%v", rin, rout) 64 | } 65 | 66 | switch { 67 | case !rout.IsValid() || !rin.IsValid(): 68 | // mergeIPRanges should have prevented invalid ranges from 69 | // sneaking in. 70 | panic("invalid IPRanges during Ranges merge") 71 | case rout.entirelyBefore(rin): 72 | // "out" is entirely before "in". 73 | // 74 | // out in 75 | // f-------t f-------t 76 | out = out[1:] 77 | if debug { 78 | debugf("out before in; drop out") 79 | } 80 | case rin.entirelyBefore(rout): 81 | // "in" is entirely before "out". 82 | // 83 | // in out 84 | // f------t f-------t 85 | min = append(min, rin) 86 | in = in[1:] 87 | if debug { 88 | debugf("in before out; append in") 89 | debugf("min=%v", min) 90 | } 91 | case rin.coveredBy(rout): 92 | // "out" entirely covers "in". 93 | // 94 | // out 95 | // f-------------t 96 | // f------t 97 | // in 98 | in = in[1:] 99 | if debug { 100 | debugf("in inside out; drop in") 101 | } 102 | case rout.inMiddleOf(rin): 103 | // "in" entirely covers "out". 104 | // 105 | // in 106 | // f-------------t 107 | // f------t 108 | // out 109 | min = append(min, IPRange{from: rin.from, to: rout.from.Prior()}) 110 | // Adjust in[0], not ir, because we want to consider the 111 | // mutated range on the next iteration. 112 | in[0].from = rout.to.Next() 113 | out = out[1:] 114 | if debug { 115 | debugf("out inside in; split in, append first in, drop out, adjust second in") 116 | debugf("min=%v", min) 117 | } 118 | case rout.overlapsStartOf(rin): 119 | // "out" overlaps start of "in". 120 | // 121 | // out 122 | // f------t 123 | // f------t 124 | // in 125 | in[0].from = rout.to.Next() 126 | // Can't move ir onto min yet, another later out might 127 | // trim it further. Just discard or and continue. 128 | out = out[1:] 129 | if debug { 130 | debugf("out cuts start of in; adjust in, drop out") 131 | } 132 | case rout.overlapsEndOf(rin): 133 | // "out" overlaps end of "in". 134 | // 135 | // out 136 | // f------t 137 | // f------t 138 | // in 139 | min = append(min, IPRange{from: rin.from, to: rout.from.Prior()}) 140 | in = in[1:] 141 | if debug { 142 | debugf("merge out cuts end of in; append shortened in") 143 | debugf("min=%v", min) 144 | } 145 | default: 146 | // The above should account for all combinations of in and 147 | // out overlapping, but insert a panic to be sure. 148 | panic("unexpected additional overlap scenario") 149 | } 150 | } 151 | if len(in) > 0 { 152 | // Ran out of removals before the end of in. 153 | min = append(min, in...) 154 | if debug { 155 | debugf("min=%v", min) 156 | } 157 | } 158 | 159 | s.in = min 160 | s.out = nil 161 | } 162 | 163 | // Clone returns a copy of s that shares no memory with s. 164 | func (s *IPSetBuilder) Clone() *IPSetBuilder { 165 | return &IPSetBuilder{ 166 | in: append([]IPRange(nil), s.in...), 167 | out: append([]IPRange(nil), s.out...), 168 | } 169 | } 170 | 171 | func (s *IPSetBuilder) addError(msg string, args ...interface{}) { 172 | se := new(stacktraceErr) 173 | // Skip three frames: runtime.Callers, addError, and the IPSetBuilder 174 | // method that called addError (such as IPSetBuilder.Add). 175 | // The resulting stack trace ends at the line in the user's 176 | // code where they called into netaddr. 177 | n := runtime.Callers(3, se.pcs[:]) 178 | se.at = se.pcs[:n] 179 | se.err = fmt.Errorf(msg, args...) 180 | s.errs = append(s.errs, se) 181 | } 182 | 183 | // Add adds ip to s. 184 | func (s *IPSetBuilder) Add(ip IP) { 185 | if ip.IsZero() { 186 | s.addError("Add(IP{})") 187 | return 188 | } 189 | s.AddRange(IPRangeFrom(ip, ip)) 190 | } 191 | 192 | // AddPrefix adds all IPs in p to s. 193 | func (s *IPSetBuilder) AddPrefix(p IPPrefix) { 194 | if r := p.Range(); r.IsValid() { 195 | s.AddRange(r) 196 | } else { 197 | s.addError("AddPrefix(%v/%v)", p.IP(), p.Bits()) 198 | } 199 | } 200 | 201 | // AddRange adds r to s. 202 | // If r is not Valid, AddRange does nothing. 203 | func (s *IPSetBuilder) AddRange(r IPRange) { 204 | if !r.IsValid() { 205 | s.addError("AddRange(%v-%v)", r.From(), r.To()) 206 | return 207 | } 208 | // If there are any removals (s.out), then we need to compact the set 209 | // first to get the order right. 210 | if len(s.out) > 0 { 211 | s.normalize() 212 | } 213 | s.in = append(s.in, r) 214 | } 215 | 216 | // AddSet adds all IPs in b to s. 217 | func (s *IPSetBuilder) AddSet(b *IPSet) { 218 | if b == nil { 219 | return 220 | } 221 | for _, r := range b.rr { 222 | s.AddRange(r) 223 | } 224 | } 225 | 226 | // Remove removes ip from s. 227 | func (s *IPSetBuilder) Remove(ip IP) { 228 | if ip.IsZero() { 229 | s.addError("Remove(IP{})") 230 | } else { 231 | s.RemoveRange(IPRangeFrom(ip, ip)) 232 | } 233 | } 234 | 235 | // RemovePrefix removes all IPs in p from s. 236 | func (s *IPSetBuilder) RemovePrefix(p IPPrefix) { 237 | if r := p.Range(); r.IsValid() { 238 | s.RemoveRange(r) 239 | } else { 240 | s.addError("RemovePrefix(%v/%v)", p.IP(), p.Bits()) 241 | } 242 | } 243 | 244 | // RemoveRange removes all IPs in r from s. 245 | func (s *IPSetBuilder) RemoveRange(r IPRange) { 246 | if r.IsValid() { 247 | s.out = append(s.out, r) 248 | } else { 249 | s.addError("RemoveRange(%v-%v)", r.From(), r.To()) 250 | } 251 | } 252 | 253 | // RemoveSet removes all IPs in o from s. 254 | func (s *IPSetBuilder) RemoveSet(b *IPSet) { 255 | if b == nil { 256 | return 257 | } 258 | for _, r := range b.rr { 259 | s.RemoveRange(r) 260 | } 261 | } 262 | 263 | // removeBuilder removes all IPs in b from s. 264 | func (s *IPSetBuilder) removeBuilder(b *IPSetBuilder) { 265 | b.normalize() 266 | for _, r := range b.in { 267 | s.RemoveRange(r) 268 | } 269 | } 270 | 271 | // Complement updates s to contain the complement of its current 272 | // contents. 273 | func (s *IPSetBuilder) Complement() { 274 | s.normalize() 275 | s.out = s.in 276 | s.in = []IPRange{ 277 | IPPrefix{ip: IPv4(0, 0, 0, 0), bits: 0}.Range(), 278 | IPPrefix{ip: IPv6Unspecified(), bits: 0}.Range(), 279 | } 280 | } 281 | 282 | // Intersect updates s to the set intersection of s and b. 283 | func (s *IPSetBuilder) Intersect(b *IPSet) { 284 | var o IPSetBuilder 285 | o.Complement() 286 | o.RemoveSet(b) 287 | s.removeBuilder(&o) 288 | } 289 | 290 | func discardf(format string, args ...interface{}) {} 291 | 292 | // debugf is reassigned by tests. 293 | var debugf = discardf 294 | 295 | // IPSet returns an immutable IPSet representing the current state of s. 296 | // 297 | // Most IPSetBuilder methods do not return errors. 298 | // Rather, the builder ignores any invalid inputs (such as an invalid IPPrefix), 299 | // and accumulates a list of any such errors that it encountered. 300 | // 301 | // IPSet also reports any such accumulated errors. 302 | // Even if the returned error is non-nil, the returned IPSet is usable 303 | // and contains all modifications made with valid inputs. 304 | // 305 | // The builder remains usable after calling IPSet. 306 | // Calling IPSet clears any accumulated errors. 307 | func (s *IPSetBuilder) IPSet() (*IPSet, error) { 308 | s.normalize() 309 | ret := &IPSet{ 310 | rr: append([]IPRange{}, s.in...), 311 | } 312 | if len(s.errs) == 0 { 313 | return ret, nil 314 | } else { 315 | errs := s.errs 316 | s.errs = nil 317 | return ret, errs 318 | } 319 | } 320 | 321 | // IPSet represents a set of IP addresses. 322 | // 323 | // IPSet is safe for concurrent use. 324 | // The zero value is a valid value representing a set of no IPs. 325 | // Use IPSetBuilder to construct IPSets. 326 | type IPSet struct { 327 | // rr is the set of IPs that belong to this IPSet. The IPRanges 328 | // are normalized according to IPSetBuilder.normalize, meaning 329 | // they are a sorted, minimal representation (no overlapping 330 | // ranges, no contiguous ranges). The implementation of various 331 | // methods rely on this property. 332 | rr []IPRange 333 | } 334 | 335 | // Ranges returns the minimum and sorted set of IP 336 | // ranges that covers s. 337 | func (s *IPSet) Ranges() []IPRange { 338 | return append([]IPRange{}, s.rr...) 339 | } 340 | 341 | // Prefixes returns the minimum and sorted set of IP prefixes 342 | // that covers s. 343 | func (s *IPSet) Prefixes() []IPPrefix { 344 | out := make([]IPPrefix, 0, len(s.rr)) 345 | for _, r := range s.rr { 346 | out = append(out, r.Prefixes()...) 347 | } 348 | return out 349 | } 350 | 351 | // Equal reports whether s and o represent the same set of IP 352 | // addresses. 353 | func (s *IPSet) Equal(o *IPSet) bool { 354 | if len(s.rr) != len(o.rr) { 355 | return false 356 | } 357 | for i := range s.rr { 358 | if s.rr[i] != o.rr[i] { 359 | return false 360 | } 361 | } 362 | return true 363 | } 364 | 365 | // Contains reports whether ip is in s. 366 | // If ip has an IPv6 zone, Contains returns false, 367 | // because IPSets do not track zones. 368 | func (s *IPSet) Contains(ip IP) bool { 369 | if ip.hasZone() { 370 | return false 371 | } 372 | // TODO: data structure permitting more efficient lookups: 373 | // https://github.com/inetaf/netaddr/issues/139 374 | i := sort.Search(len(s.rr), func(i int) bool { 375 | return ip.Less(s.rr[i].from) 376 | }) 377 | if i == 0 { 378 | return false 379 | } 380 | i-- 381 | return s.rr[i].contains(ip) 382 | } 383 | 384 | // ContainsRange reports whether all IPs in r are in s. 385 | func (s *IPSet) ContainsRange(r IPRange) bool { 386 | for _, x := range s.rr { 387 | if r.coveredBy(x) { 388 | return true 389 | } 390 | } 391 | return false 392 | } 393 | 394 | // ContainsPrefix reports whether all IPs in p are in s. 395 | func (s *IPSet) ContainsPrefix(p IPPrefix) bool { 396 | return s.ContainsRange(p.Range()) 397 | } 398 | 399 | // Overlaps reports whether any IP in b is also in s. 400 | func (s *IPSet) Overlaps(b *IPSet) bool { 401 | // TODO: sorted ranges lets us do this in O(n+m) 402 | for _, r := range s.rr { 403 | for _, or := range b.rr { 404 | if r.Overlaps(or) { 405 | return true 406 | } 407 | } 408 | } 409 | return false 410 | } 411 | 412 | // OverlapsRange reports whether any IP in r is also in s. 413 | func (s *IPSet) OverlapsRange(r IPRange) bool { 414 | // TODO: sorted ranges lets us do this more efficiently. 415 | for _, x := range s.rr { 416 | if x.Overlaps(r) { 417 | return true 418 | } 419 | } 420 | return false 421 | } 422 | 423 | // OverlapsPrefix reports whether any IP in p is also in s. 424 | func (s *IPSet) OverlapsPrefix(p IPPrefix) bool { 425 | return s.OverlapsRange(p.Range()) 426 | } 427 | 428 | // RemoveFreePrefix splits s into a Prefix of length bitLen and a new 429 | // IPSet with that prefix removed. 430 | // 431 | // If no contiguous prefix of length bitLen exists in s, 432 | // RemoveFreePrefix returns ok=false. 433 | func (s *IPSet) RemoveFreePrefix(bitLen uint8) (p IPPrefix, newSet *IPSet, ok bool) { 434 | var bestFit IPPrefix 435 | for _, r := range s.rr { 436 | for _, prefix := range r.Prefixes() { 437 | if prefix.bits > bitLen { 438 | continue 439 | } 440 | if bestFit.ip.IsZero() || prefix.bits > bestFit.bits { 441 | bestFit = prefix 442 | if bestFit.bits == bitLen { 443 | // exact match, done. 444 | break 445 | } 446 | } 447 | } 448 | } 449 | 450 | if bestFit.ip.IsZero() { 451 | return IPPrefix{}, s, false 452 | } 453 | 454 | prefix := IPPrefix{ip: bestFit.ip, bits: bitLen} 455 | 456 | var b IPSetBuilder 457 | b.AddSet(s) 458 | b.RemovePrefix(prefix) 459 | newSet, _ = b.IPSet() 460 | return prefix, newSet, true 461 | } 462 | 463 | type multiErr []error 464 | 465 | func (e multiErr) Error() string { 466 | var ret []string 467 | for _, err := range e { 468 | ret = append(ret, err.Error()) 469 | } 470 | return strings.Join(ret, "; ") 471 | } 472 | 473 | // A stacktraceErr combines an error with a stack trace. 474 | type stacktraceErr struct { 475 | pcs [16]uintptr // preallocated array of PCs 476 | at []uintptr // stack trace whence the error 477 | err error // underlying error 478 | } 479 | 480 | func (e *stacktraceErr) Error() string { 481 | frames := runtime.CallersFrames(e.at) 482 | buf := new(strings.Builder) 483 | buf.WriteString(e.err.Error()) 484 | buf.WriteString(" @ ") 485 | for { 486 | frame, more := frames.Next() 487 | if !more { 488 | break 489 | } 490 | fmt.Fprintf(buf, "%s:%d ", frame.File, frame.Line) 491 | } 492 | return strings.TrimSpace(buf.String()) 493 | } 494 | 495 | func (e *stacktraceErr) Unwrap() error { 496 | return e.err 497 | } 498 | -------------------------------------------------------------------------------- /ipset_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2020 The Inet.Af AUTHORS. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package netaddr 6 | 7 | import ( 8 | "bytes" 9 | "fmt" 10 | "log" 11 | "math/rand" 12 | "reflect" 13 | "testing" 14 | ) 15 | 16 | func buildIPSet(b *IPSetBuilder) *IPSet { 17 | ret, err := b.IPSet() 18 | if err != nil { 19 | panic(err) 20 | } 21 | return ret 22 | } 23 | 24 | func TestIPSet(t *testing.T) { 25 | tests := []struct { 26 | name string 27 | f func(s *IPSetBuilder) 28 | wantRanges []IPRange 29 | wantPrefixes []IPPrefix // non-nil to test 30 | wantContains map[string]bool // optional non-exhaustive IPs to test for in resulting set 31 | }{ 32 | { 33 | name: "mix_family", 34 | f: func(s *IPSetBuilder) { 35 | s.AddPrefix(mustIPPrefix("10.0.0.0/8")) 36 | s.AddPrefix(mustIPPrefix("::/0")) 37 | s.RemovePrefix(mustIPPrefix("10.2.0.0/16")) 38 | }, 39 | wantRanges: []IPRange{ 40 | {mustIP("10.0.0.0"), mustIP("10.1.255.255")}, 41 | {mustIP("10.3.0.0"), mustIP("10.255.255.255")}, 42 | {mustIP("::"), mustIP("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff")}, 43 | }, 44 | }, 45 | { 46 | name: "merge_adjacent", 47 | f: func(s *IPSetBuilder) { 48 | s.AddPrefix(mustIPPrefix("10.0.0.0/8")) 49 | s.AddPrefix(mustIPPrefix("11.0.0.0/8")) 50 | }, 51 | wantRanges: []IPRange{ 52 | {mustIP("10.0.0.0"), mustIP("11.255.255.255")}, 53 | }, 54 | wantPrefixes: pxv("10.0.0.0/7"), 55 | }, 56 | { 57 | name: "remove_32", 58 | f: func(s *IPSetBuilder) { 59 | s.AddPrefix(mustIPPrefix("10.0.0.0/8")) 60 | s.RemovePrefix(mustIPPrefix("10.1.2.3/32")) 61 | }, 62 | wantRanges: []IPRange{ 63 | {mustIP("10.0.0.0"), mustIP("10.1.2.2")}, 64 | {mustIP("10.1.2.4"), mustIP("10.255.255.255")}, 65 | }, 66 | wantPrefixes: pxv( 67 | "10.0.0.0/16", 68 | "10.1.0.0/23", 69 | "10.1.2.0/31", 70 | "10.1.2.2/32", 71 | "10.1.2.4/30", 72 | "10.1.2.8/29", 73 | "10.1.2.16/28", 74 | "10.1.2.32/27", 75 | "10.1.2.64/26", 76 | "10.1.2.128/25", 77 | "10.1.3.0/24", 78 | "10.1.4.0/22", 79 | "10.1.8.0/21", 80 | "10.1.16.0/20", 81 | "10.1.32.0/19", 82 | "10.1.64.0/18", 83 | "10.1.128.0/17", 84 | "10.2.0.0/15", 85 | "10.4.0.0/14", 86 | "10.8.0.0/13", 87 | "10.16.0.0/12", 88 | "10.32.0.0/11", 89 | "10.64.0.0/10", 90 | "10.128.0.0/9", 91 | ), 92 | }, 93 | { 94 | name: "remove_32_and_first_16", 95 | f: func(s *IPSetBuilder) { 96 | s.AddPrefix(mustIPPrefix("10.0.0.0/8")) 97 | s.RemovePrefix(mustIPPrefix("10.1.2.3/32")) 98 | s.RemovePrefix(mustIPPrefix("10.0.0.0/16")) 99 | }, 100 | wantRanges: []IPRange{ 101 | {mustIP("10.1.0.0"), mustIP("10.1.2.2")}, 102 | {mustIP("10.1.2.4"), mustIP("10.255.255.255")}, 103 | }, 104 | wantPrefixes: pxv( 105 | "10.1.0.0/23", 106 | "10.1.2.0/31", 107 | "10.1.2.2/32", 108 | "10.1.2.4/30", 109 | "10.1.2.8/29", 110 | "10.1.2.16/28", 111 | "10.1.2.32/27", 112 | "10.1.2.64/26", 113 | "10.1.2.128/25", 114 | "10.1.3.0/24", 115 | "10.1.4.0/22", 116 | "10.1.8.0/21", 117 | "10.1.16.0/20", 118 | "10.1.32.0/19", 119 | "10.1.64.0/18", 120 | "10.1.128.0/17", 121 | "10.2.0.0/15", 122 | "10.4.0.0/14", 123 | "10.8.0.0/13", 124 | "10.16.0.0/12", 125 | "10.32.0.0/11", 126 | "10.64.0.0/10", 127 | "10.128.0.0/9", 128 | ), 129 | }, 130 | { 131 | name: "add_dup", 132 | f: func(s *IPSetBuilder) { 133 | s.AddPrefix(mustIPPrefix("10.0.0.0/8")) 134 | s.AddPrefix(mustIPPrefix("10.0.0.0/8")) 135 | }, 136 | wantRanges: []IPRange{ 137 | {mustIP("10.0.0.0"), mustIP("10.255.255.255")}, 138 | }, 139 | }, 140 | { 141 | name: "add_dup_subet", 142 | f: func(s *IPSetBuilder) { 143 | s.AddPrefix(mustIPPrefix("10.0.0.0/8")) 144 | s.AddPrefix(mustIPPrefix("10.0.0.0/16")) 145 | }, 146 | wantRanges: []IPRange{ 147 | {mustIP("10.0.0.0"), mustIP("10.255.255.255")}, 148 | }, 149 | }, 150 | { 151 | name: "add_remove_add", 152 | f: func(s *IPSetBuilder) { 153 | s.AddPrefix(mustIPPrefix("10.0.0.0/8")) 154 | s.RemovePrefix(mustIPPrefix("10.1.2.3/32")) 155 | s.AddPrefix(mustIPPrefix("10.1.0.0/16")) // undoes prior line 156 | }, 157 | wantRanges: []IPRange{ 158 | {mustIP("10.0.0.0"), mustIP("10.255.255.255")}, 159 | }, 160 | }, 161 | { 162 | name: "remove_then_add", 163 | f: func(s *IPSetBuilder) { 164 | s.RemovePrefix(mustIPPrefix("1.2.3.4/32")) // no-op 165 | s.AddPrefix(mustIPPrefix("1.2.3.4/32")) 166 | }, 167 | wantRanges: []IPRange{ 168 | {mustIP("1.2.3.4"), mustIP("1.2.3.4")}, 169 | }, 170 | }, 171 | { 172 | name: "remove_end_on_add_start", 173 | f: func(s *IPSetBuilder) { 174 | s.AddRange(IPRange{mustIP("0.0.0.38"), mustIP("0.0.0.177")}) 175 | s.RemoveRange(IPRange{mustIP("0.0.0.18"), mustIP("0.0.0.38")}) 176 | }, 177 | wantRanges: []IPRange{ 178 | {mustIP("0.0.0.39"), mustIP("0.0.0.177")}, 179 | }, 180 | }, 181 | { 182 | name: "fuzz_fail_2", 183 | f: func(s *IPSetBuilder) { 184 | s.AddRange(IPRange{mustIP("0.0.0.143"), mustIP("0.0.0.185")}) 185 | s.AddRange(IPRange{mustIP("0.0.0.84"), mustIP("0.0.0.174")}) 186 | s.AddRange(IPRange{mustIP("0.0.0.51"), mustIP("0.0.0.61")}) 187 | s.RemoveRange(IPRange{mustIP("0.0.0.66"), mustIP("0.0.0.146")}) 188 | s.AddRange(IPRange{mustIP("0.0.0.22"), mustIP("0.0.0.207")}) 189 | s.RemoveRange(IPRange{mustIP("0.0.0.198"), mustIP("0.0.0.203")}) 190 | s.RemoveRange(IPRange{mustIP("0.0.0.23"), mustIP("0.0.0.69")}) 191 | s.AddRange(IPRange{mustIP("0.0.0.64"), mustIP("0.0.0.105")}) 192 | s.AddRange(IPRange{mustIP("0.0.0.151"), mustIP("0.0.0.203")}) 193 | s.AddRange(IPRange{mustIP("0.0.0.138"), mustIP("0.0.0.160")}) 194 | s.RemoveRange(IPRange{mustIP("0.0.0.64"), mustIP("0.0.0.161")}) 195 | }, 196 | wantRanges: []IPRange{ 197 | {mustIP("0.0.0.22"), mustIP("0.0.0.22")}, 198 | {mustIP("0.0.0.162"), mustIP("0.0.0.207")}, 199 | }, 200 | wantContains: map[string]bool{ 201 | "0.0.0.22": true, 202 | }, 203 | }, 204 | { 205 | name: "single_ips", 206 | f: func(s *IPSetBuilder) { 207 | s.Add(mustIP("10.0.0.0")) 208 | s.Add(mustIP("10.0.0.1")) 209 | s.Add(mustIP("10.0.0.2")) 210 | s.Add(mustIP("10.0.0.3")) 211 | s.Add(mustIP("10.0.0.4")) 212 | s.Remove(mustIP("10.0.0.4")) 213 | s.Add(mustIP("10.0.0.255")) 214 | }, 215 | wantRanges: []IPRange{ 216 | {mustIP("10.0.0.0"), mustIP("10.0.0.3")}, 217 | {mustIP("10.0.0.255"), mustIP("10.0.0.255")}, 218 | }, 219 | wantPrefixes: pxv("10.0.0.0/30", "10.0.0.255/32"), 220 | }, 221 | { 222 | // regression test for a bug where Ranges returned invalid IPRanges. 223 | name: "single_ip_removal", 224 | f: func(s *IPSetBuilder) { 225 | s.Add(mustIP("10.0.0.0")) 226 | s.Add(mustIP("10.0.0.1")) 227 | s.Add(mustIP("10.0.0.2")) 228 | s.Add(mustIP("10.0.0.3")) 229 | s.Add(mustIP("10.0.0.4")) 230 | s.Remove(mustIP("10.0.0.4")) 231 | }, 232 | wantRanges: []IPRange{ 233 | {mustIP("10.0.0.0"), mustIP("10.0.0.3")}, 234 | }, 235 | wantPrefixes: pxv("10.0.0.0/30"), 236 | }, 237 | { 238 | name: "invert_empty", 239 | f: func(s *IPSetBuilder) { 240 | s.Complement() 241 | }, 242 | wantRanges: []IPRange{ 243 | {mustIP("0.0.0.0"), mustIP("255.255.255.255")}, 244 | {mustIP("::"), mustIP("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff")}, 245 | }, 246 | wantPrefixes: pxv("0.0.0.0/0", "::/0"), 247 | }, 248 | { 249 | name: "invert_full", 250 | f: func(s *IPSetBuilder) { 251 | s.AddPrefix(mustIPPrefix("0.0.0.0/0")) 252 | s.AddPrefix(mustIPPrefix("::/0")) 253 | s.Complement() 254 | }, 255 | wantRanges: []IPRange{}, 256 | wantPrefixes: pxv(), 257 | }, 258 | { 259 | name: "invert_partial", 260 | f: func(s *IPSetBuilder) { 261 | s.AddRange(IPRange{mustIP("1.1.1.1"), mustIP("2.2.2.2")}) 262 | s.Add(mustIP("3.3.3.3")) 263 | s.AddPrefix(mustIPPrefix("4.4.4.0/24")) 264 | s.Add(mustIP("1::1")) 265 | s.Complement() 266 | }, 267 | wantRanges: []IPRange{ 268 | {mustIP("0.0.0.0"), mustIP("1.1.1.0")}, 269 | {mustIP("2.2.2.3"), mustIP("3.3.3.2")}, 270 | {mustIP("3.3.3.4"), mustIP("4.4.3.255")}, 271 | {mustIP("4.4.5.0"), mustIP("255.255.255.255")}, 272 | {mustIP("::"), mustIP("1::")}, 273 | {mustIP("1::2"), mustIP("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff")}, 274 | }, 275 | }, 276 | { 277 | name: "intersect", 278 | f: func(s *IPSetBuilder) { 279 | var t IPSetBuilder 280 | t.AddRange(IPRange{mustIP("2.2.2.2"), mustIP("3.3.3.3")}) 281 | 282 | s.AddRange(IPRange{mustIP("1.1.1.1"), mustIP("4.4.4.4")}) 283 | s.Intersect(buildIPSet(&t)) 284 | }, 285 | wantRanges: []IPRange{ 286 | {mustIP("2.2.2.2"), mustIP("3.3.3.3")}, 287 | }, 288 | }, 289 | { 290 | name: "intersect_disjoint", 291 | f: func(s *IPSetBuilder) { 292 | var t IPSetBuilder 293 | t.AddRange(IPRange{mustIP("1.1.1.1"), mustIP("2.2.2.2")}) 294 | 295 | s.AddRange(IPRange{mustIP("3.3.3.3"), mustIP("4.4.4.4")}) 296 | s.Intersect(buildIPSet(&t)) 297 | }, 298 | wantRanges: []IPRange{}, 299 | }, 300 | { 301 | name: "intersect_partial", 302 | f: func(s *IPSetBuilder) { 303 | var t IPSetBuilder 304 | t.AddRange(IPRange{mustIP("1.1.1.1"), mustIP("3.3.3.3")}) 305 | 306 | s.AddRange(IPRange{mustIP("2.2.2.2"), mustIP("4.4.4.4")}) 307 | s.Intersect(buildIPSet(&t)) 308 | }, 309 | wantRanges: []IPRange{ 310 | {mustIP("2.2.2.2"), mustIP("3.3.3.3")}, 311 | }, 312 | }, 313 | } 314 | for _, tt := range tests { 315 | t.Run(tt.name, func(t *testing.T) { 316 | debugf = t.Logf 317 | defer func() { debugf = discardf }() 318 | var build IPSetBuilder 319 | tt.f(&build) 320 | s := buildIPSet(&build) 321 | got := s.Ranges() 322 | t.Run("ranges", func(t *testing.T) { 323 | for _, v := range got { 324 | if !v.IsValid() { 325 | t.Errorf("invalid IPRange in result: %s -> %s", v.From(), v.To()) 326 | } 327 | } 328 | if reflect.DeepEqual(got, tt.wantRanges) { 329 | return 330 | } 331 | t.Error("failed. got:\n") 332 | for _, v := range got { 333 | t.Errorf(" %s -> %s", v.From(), v.To()) 334 | } 335 | t.Error("want:\n") 336 | for _, v := range tt.wantRanges { 337 | t.Errorf(" %s -> %s", v.From(), v.To()) 338 | } 339 | }) 340 | if tt.wantPrefixes != nil { 341 | t.Run("prefixes", func(t *testing.T) { 342 | got := s.Prefixes() 343 | if got == nil { 344 | got = []IPPrefix{} 345 | } 346 | if reflect.DeepEqual(got, tt.wantPrefixes) { 347 | return 348 | } 349 | t.Error("failed. got:\n") 350 | for _, v := range got { 351 | t.Errorf(" %v", v) 352 | } 353 | t.Error("want:\n") 354 | for _, v := range tt.wantPrefixes { 355 | t.Errorf(" %v", v) 356 | } 357 | }) 358 | } 359 | if len(tt.wantContains) > 0 { 360 | for ipStr, want := range tt.wantContains { 361 | got := s.Contains(mustIP(ipStr)) 362 | if got != want { 363 | t.Errorf("Contains(%q) = %v; want %v", s, got, want) 364 | } 365 | } 366 | } 367 | }) 368 | } 369 | } 370 | 371 | func TestIPSetRemoveFreePrefix(t *testing.T) { 372 | pfx := mustIPPrefix 373 | tests := []struct { 374 | name string 375 | f func(s *IPSetBuilder) 376 | b uint8 377 | wantPrefix IPPrefix 378 | wantPrefixes []IPPrefix 379 | wantOK bool 380 | }{ 381 | { 382 | name: "cut in half", 383 | f: func(s *IPSetBuilder) { 384 | s.AddPrefix(pfx("10.0.0.0/8")) 385 | }, 386 | b: 9, 387 | wantPrefix: pfx("10.0.0.0/9"), 388 | wantPrefixes: pxv("10.128.0.0/9"), 389 | wantOK: true, 390 | }, 391 | { 392 | name: "on prefix left", 393 | f: func(s *IPSetBuilder) { 394 | s.AddPrefix(pfx("10.0.0.0/8")) 395 | s.RemovePrefix(pfx("10.0.0.0/9")) 396 | }, 397 | b: 9, 398 | wantPrefix: pfx("10.128.0.0/9"), 399 | wantPrefixes: []IPPrefix{}, 400 | wantOK: true, 401 | }, 402 | } 403 | for _, tt := range tests { 404 | t.Run(tt.name, func(t *testing.T) { 405 | debugf = t.Logf 406 | var build IPSetBuilder 407 | tt.f(&build) 408 | s := buildIPSet(&build) 409 | gotPrefix, gotSet, ok := s.RemoveFreePrefix(tt.b) 410 | if ok != tt.wantOK { 411 | t.Errorf("extractPrefix() ok = %t, wantOK %t", ok, tt.wantOK) 412 | return 413 | } 414 | if !reflect.DeepEqual(gotPrefix, tt.wantPrefix) { 415 | t.Errorf("extractPrefix() = %v, want %v", gotPrefix, tt.wantPrefix) 416 | } 417 | if !reflect.DeepEqual(gotSet.Prefixes(), tt.wantPrefixes) { 418 | t.Errorf("extractPrefix() = %v, want %v", gotSet.Prefixes(), tt.wantPrefixes) 419 | } 420 | }) 421 | } 422 | } 423 | 424 | func mustIPSet(ranges ...string) *IPSet { 425 | var ret IPSetBuilder 426 | for _, r := range ranges { 427 | ipr, err := ParseIPRange(r[1:]) 428 | if err != nil { 429 | panic(err) 430 | } 431 | switch r[0] { 432 | case '+': 433 | ret.AddRange(ipr) 434 | case '-': 435 | ret.RemoveRange(ipr) 436 | default: 437 | panic(fmt.Sprintf("unknown command %q", r[0])) 438 | } 439 | } 440 | return buildIPSet(&ret) 441 | } 442 | 443 | func TestIPSetOverlaps(t *testing.T) { 444 | tests := []struct { 445 | a, b *IPSet 446 | want bool 447 | }{ 448 | { 449 | mustIPSet(), 450 | mustIPSet(), 451 | false, 452 | }, 453 | { 454 | mustIPSet("+10.0.0.0-10.0.0.5"), 455 | mustIPSet("+10.0.0.0-10.0.0.5"), 456 | true, // exact match 457 | }, 458 | { 459 | mustIPSet("+10.0.0.0-10.0.0.5"), 460 | mustIPSet("+10.0.0.5-10.0.0.10"), 461 | true, // overlap on edge 462 | }, 463 | { 464 | mustIPSet("+10.0.0.0-10.0.0.5"), 465 | mustIPSet("+10.0.0.3-10.0.0.7"), 466 | true, // overlap in middle 467 | }, 468 | { 469 | mustIPSet("+10.0.0.0-10.0.0.5"), 470 | mustIPSet("+10.0.0.2-10.0.0.3"), 471 | true, // one inside other 472 | }, 473 | { 474 | mustIPSet("+10.0.0.0-10.0.0.5", "+10.1.0.0-10.1.0.5"), 475 | mustIPSet("+10.1.0.1-10.1.0.2"), 476 | true, // overlap in non-first 477 | }, 478 | { 479 | mustIPSet("+10.0.0.0-10.0.0.10", "-10.0.0.5-10.0.0.10"), 480 | mustIPSet("+10.0.0.7-10.0.0.8"), 481 | false, // removal cancels overlap 482 | }, 483 | { 484 | mustIPSet("+10.0.0.0-10.0.0.10", "-10.0.0.5-10.0.0.10", "+10.0.0.5-10.0.0.10"), 485 | mustIPSet("+10.0.0.7-10.0.0.8"), 486 | true, // removal+readd restores overlap 487 | }, 488 | } 489 | 490 | for _, test := range tests { 491 | got := test.a.Overlaps(test.b) 492 | if got != test.want { 493 | t.Errorf("(%s).Overlaps(%s) = %v, want %v", test.a, test.b, got, test.want) 494 | } 495 | got = test.b.Overlaps(test.a) 496 | if got != test.want { 497 | t.Errorf("(%s).Overlaps(%s) = %v, want %v", test.b, test.a, got, test.want) 498 | } 499 | } 500 | } 501 | 502 | func TestIPSetContains(t *testing.T) { 503 | var build IPSetBuilder 504 | build.AddPrefix(mustIPPrefix("10.0.0.0/8")) 505 | build.AddPrefix(mustIPPrefix("1.2.3.4/32")) 506 | build.AddPrefix(mustIPPrefix("fc00::/7")) 507 | s := buildIPSet(&build) 508 | 509 | tests := []struct { 510 | ip string 511 | want bool 512 | }{ 513 | {"0.0.0.0", false}, 514 | {"::", false}, 515 | 516 | {"1.2.3.3", false}, 517 | {"1.2.3.4", true}, 518 | {"1.2.3.5", false}, 519 | 520 | {"9.255.255.255", false}, 521 | {"10.0.0.0", true}, 522 | {"10.1.2.3", true}, 523 | {"10.255.255.255", true}, 524 | {"11.0.0.0", false}, 525 | 526 | {"::", false}, 527 | {"fc00::", true}, 528 | {"fc00::1", true}, 529 | {"fd00::1", true}, 530 | {"ff00::1", false}, 531 | 532 | {"fd00::%a", false}, 533 | {"fd00::1%a", false}, 534 | } 535 | for _, tt := range tests { 536 | got := s.Contains(mustIP(tt.ip)) 537 | if got != tt.want { 538 | t.Errorf("contains(%q) = %v; want %v", tt.ip, got, tt.want) 539 | } 540 | } 541 | } 542 | 543 | func TestIPSetFuzz(t *testing.T) { 544 | t.Parallel() 545 | if testing.Short() { 546 | doIPSetFuzz(t, 100) 547 | } else { 548 | doIPSetFuzz(t, 5000) 549 | } 550 | } 551 | 552 | func BenchmarkIPSetFuzz(b *testing.B) { 553 | b.ReportAllocs() 554 | doIPSetFuzz(b, b.N) 555 | } 556 | 557 | func doIPSetFuzz(t testing.TB, iters int) { 558 | var buf bytes.Buffer 559 | logger := log.New(&buf, "", 0) 560 | debugf = logger.Printf 561 | defer func() { debugf = discardf }() 562 | for i := 0; i < iters; i++ { 563 | buf.Reset() 564 | steps, set, wantContains := newRandomIPSet() 565 | for b, want := range wantContains { 566 | ip := IPv4(0, 0, 0, uint8(b)) 567 | got := set.Contains(ip) 568 | if got != want { 569 | t.Fatalf("for steps %q, contains(%v) = %v; want %v\n%s", steps, ip, got, want, buf.Bytes()) 570 | } 571 | } 572 | } 573 | } 574 | 575 | func newRandomIPSet() (steps []string, s *IPSet, wantContains [256]bool) { 576 | b := new(IPSetBuilder) 577 | nstep := 2 + rand.Intn(10) 578 | for i := 0; i < nstep; i++ { 579 | op := rand.Intn(2) 580 | ip1 := uint8(rand.Intn(256)) 581 | ip2 := uint8(rand.Intn(256)) 582 | if ip2 < ip1 { 583 | ip1, ip2 = ip2, ip1 584 | } 585 | var v bool 586 | switch op { 587 | case 0: 588 | steps = append(steps, fmt.Sprintf("add 0.0.0.%d-0.0.0.%d", ip1, ip2)) 589 | b.AddRange(IPRangeFrom(IPv4(0, 0, 0, ip1), IPv4(0, 0, 0, ip2))) 590 | v = true 591 | case 1: 592 | steps = append(steps, fmt.Sprintf("remove 0.0.0.%d-0.0.0.%d", ip1, ip2)) 593 | b.RemoveRange(IPRangeFrom(IPv4(0, 0, 0, ip1), IPv4(0, 0, 0, ip2))) 594 | } 595 | for i := ip1; i <= ip2; i++ { 596 | wantContains[i] = v 597 | if i == ip2 { 598 | break 599 | } 600 | } 601 | } 602 | s = buildIPSet(b) 603 | return 604 | } 605 | 606 | // TestIPSetRanges tests IPSet.Ranges against 64k 607 | // patterns of sets of ranges, checking the real implementation 608 | // against the test's separate implementation. 609 | // 610 | // For each of uint16 pattern, each set bit is treated as an IP that 611 | // should be in the set's resultant Ranges. 612 | func TestIPSetRanges(t *testing.T) { 613 | t.Parallel() 614 | upper := 0x0fff 615 | if *long { 616 | upper = 0xffff 617 | } 618 | for pat := 0; pat <= upper; pat++ { 619 | var build IPSetBuilder 620 | var from, to IP 621 | ranges := make([]IPRange, 0) 622 | flush := func() { 623 | r := IPRangeFrom(from, to) 624 | build.AddRange(r) 625 | ranges = append(ranges, r) 626 | from, to = IP{}, IP{} 627 | } 628 | for b := uint16(0); b < 16; b++ { 629 | if uint16(pat)&(1< b { 663 | a, b = b, a 664 | } 665 | from := IPv4(0, 0, uint8(a>>8), uint8(a)) 666 | to := IPv4(0, 0, uint8(b>>8), uint8(b)) 667 | return a, b, IPRangeFrom(from, to) 668 | } 669 | for i := 0; i < n; i++ { 670 | var build IPSetBuilder 671 | var want [numIPs]bool 672 | // Add some ranges 673 | for i := 0; i < 1+rand.Intn(2); i++ { 674 | a, b, r := randRange() 675 | for i := a; i <= b; i++ { 676 | want[i] = true 677 | } 678 | build.AddRange(r) 679 | } 680 | // Remove some ranges 681 | for i := 0; i < rand.Intn(3); i++ { 682 | a, b, r := randRange() 683 | for i := a; i <= b; i++ { 684 | want[i] = false 685 | } 686 | build.RemoveRange(r) 687 | } 688 | ranges := buildIPSet(&build).Ranges() 689 | 690 | // Make sure no ranges are adjacent or overlapping 691 | for i, r := range ranges { 692 | if i == 0 { 693 | continue 694 | } 695 | if ranges[i-1].To().Compare(r.From()) != -1 { 696 | t.Fatalf("overlapping ranges: %v", ranges) 697 | } 698 | } 699 | 700 | // Copy the ranges back to a new set before using 701 | // ContainsFunc, in case the ContainsFunc implementation 702 | // changes in the future to not use Ranges itself: 703 | var build2 IPSetBuilder 704 | for _, r := range ranges { 705 | build2.AddRange(r) 706 | } 707 | s2 := buildIPSet(&build2) 708 | for i, want := range want { 709 | if got := s2.Contains(IPv4(0, 0, uint8(i>>8), uint8(i))); got != want { 710 | t.Fatal("failed") 711 | } 712 | } 713 | } 714 | } 715 | 716 | func TestIPSetEqual(t *testing.T) { 717 | a := new(IPSetBuilder) 718 | b := new(IPSetBuilder) 719 | 720 | assertEqual := func(want bool) { 721 | t.Helper() 722 | if got := buildIPSet(a).Equal(buildIPSet(b)); got != want { 723 | t.Errorf("%v.Equal(%v) = %v want %v", a, b, got, want) 724 | } 725 | } 726 | 727 | a.Add(MustParseIP("1.1.1.0")) 728 | a.Add(MustParseIP("1.1.1.1")) 729 | a.Add(MustParseIP("1.1.1.2")) 730 | b.AddPrefix(MustParseIPPrefix("1.1.1.0/31")) 731 | b.Add(MustParseIP("1.1.1.2")) 732 | assertEqual(true) 733 | 734 | a.RemoveSet(buildIPSet(a)) 735 | assertEqual(false) 736 | b.RemoveSet(buildIPSet(b)) 737 | assertEqual(true) 738 | 739 | a.Add(MustParseIP("1.1.1.0")) 740 | a.Add(MustParseIP("1.1.1.1")) 741 | a.Add(MustParseIP("1.1.1.2")) 742 | 743 | b.AddPrefix(MustParseIPPrefix("1.1.1.0/30")) 744 | b.Remove(MustParseIP("1.1.1.3")) 745 | assertEqual(true) 746 | } 747 | -------------------------------------------------------------------------------- /mask6.go: -------------------------------------------------------------------------------- 1 | // Copyright 2021 The Inet.Af AUTHORS. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package netaddr 6 | 7 | // mask6 are bitmasks with the topmost n bits of a 8 | // 128-bit number, where n is the array index. 9 | // 10 | // generated with https://play.golang.org/p/64XKxaUSa_9 11 | var mask6 = [...]uint128{ 12 | 0: {0x0000000000000000, 0x0000000000000000}, 13 | 1: {0x8000000000000000, 0x0000000000000000}, 14 | 2: {0xc000000000000000, 0x0000000000000000}, 15 | 3: {0xe000000000000000, 0x0000000000000000}, 16 | 4: {0xf000000000000000, 0x0000000000000000}, 17 | 5: {0xf800000000000000, 0x0000000000000000}, 18 | 6: {0xfc00000000000000, 0x0000000000000000}, 19 | 7: {0xfe00000000000000, 0x0000000000000000}, 20 | 8: {0xff00000000000000, 0x0000000000000000}, 21 | 9: {0xff80000000000000, 0x0000000000000000}, 22 | 10: {0xffc0000000000000, 0x0000000000000000}, 23 | 11: {0xffe0000000000000, 0x0000000000000000}, 24 | 12: {0xfff0000000000000, 0x0000000000000000}, 25 | 13: {0xfff8000000000000, 0x0000000000000000}, 26 | 14: {0xfffc000000000000, 0x0000000000000000}, 27 | 15: {0xfffe000000000000, 0x0000000000000000}, 28 | 16: {0xffff000000000000, 0x0000000000000000}, 29 | 17: {0xffff800000000000, 0x0000000000000000}, 30 | 18: {0xffffc00000000000, 0x0000000000000000}, 31 | 19: {0xffffe00000000000, 0x0000000000000000}, 32 | 20: {0xfffff00000000000, 0x0000000000000000}, 33 | 21: {0xfffff80000000000, 0x0000000000000000}, 34 | 22: {0xfffffc0000000000, 0x0000000000000000}, 35 | 23: {0xfffffe0000000000, 0x0000000000000000}, 36 | 24: {0xffffff0000000000, 0x0000000000000000}, 37 | 25: {0xffffff8000000000, 0x0000000000000000}, 38 | 26: {0xffffffc000000000, 0x0000000000000000}, 39 | 27: {0xffffffe000000000, 0x0000000000000000}, 40 | 28: {0xfffffff000000000, 0x0000000000000000}, 41 | 29: {0xfffffff800000000, 0x0000000000000000}, 42 | 30: {0xfffffffc00000000, 0x0000000000000000}, 43 | 31: {0xfffffffe00000000, 0x0000000000000000}, 44 | 32: {0xffffffff00000000, 0x0000000000000000}, 45 | 33: {0xffffffff80000000, 0x0000000000000000}, 46 | 34: {0xffffffffc0000000, 0x0000000000000000}, 47 | 35: {0xffffffffe0000000, 0x0000000000000000}, 48 | 36: {0xfffffffff0000000, 0x0000000000000000}, 49 | 37: {0xfffffffff8000000, 0x0000000000000000}, 50 | 38: {0xfffffffffc000000, 0x0000000000000000}, 51 | 39: {0xfffffffffe000000, 0x0000000000000000}, 52 | 40: {0xffffffffff000000, 0x0000000000000000}, 53 | 41: {0xffffffffff800000, 0x0000000000000000}, 54 | 42: {0xffffffffffc00000, 0x0000000000000000}, 55 | 43: {0xffffffffffe00000, 0x0000000000000000}, 56 | 44: {0xfffffffffff00000, 0x0000000000000000}, 57 | 45: {0xfffffffffff80000, 0x0000000000000000}, 58 | 46: {0xfffffffffffc0000, 0x0000000000000000}, 59 | 47: {0xfffffffffffe0000, 0x0000000000000000}, 60 | 48: {0xffffffffffff0000, 0x0000000000000000}, 61 | 49: {0xffffffffffff8000, 0x0000000000000000}, 62 | 50: {0xffffffffffffc000, 0x0000000000000000}, 63 | 51: {0xffffffffffffe000, 0x0000000000000000}, 64 | 52: {0xfffffffffffff000, 0x0000000000000000}, 65 | 53: {0xfffffffffffff800, 0x0000000000000000}, 66 | 54: {0xfffffffffffffc00, 0x0000000000000000}, 67 | 55: {0xfffffffffffffe00, 0x0000000000000000}, 68 | 56: {0xffffffffffffff00, 0x0000000000000000}, 69 | 57: {0xffffffffffffff80, 0x0000000000000000}, 70 | 58: {0xffffffffffffffc0, 0x0000000000000000}, 71 | 59: {0xffffffffffffffe0, 0x0000000000000000}, 72 | 60: {0xfffffffffffffff0, 0x0000000000000000}, 73 | 61: {0xfffffffffffffff8, 0x0000000000000000}, 74 | 62: {0xfffffffffffffffc, 0x0000000000000000}, 75 | 63: {0xfffffffffffffffe, 0x0000000000000000}, 76 | 64: {0xffffffffffffffff, 0x0000000000000000}, 77 | 65: {0xffffffffffffffff, 0x8000000000000000}, 78 | 66: {0xffffffffffffffff, 0xc000000000000000}, 79 | 67: {0xffffffffffffffff, 0xe000000000000000}, 80 | 68: {0xffffffffffffffff, 0xf000000000000000}, 81 | 69: {0xffffffffffffffff, 0xf800000000000000}, 82 | 70: {0xffffffffffffffff, 0xfc00000000000000}, 83 | 71: {0xffffffffffffffff, 0xfe00000000000000}, 84 | 72: {0xffffffffffffffff, 0xff00000000000000}, 85 | 73: {0xffffffffffffffff, 0xff80000000000000}, 86 | 74: {0xffffffffffffffff, 0xffc0000000000000}, 87 | 75: {0xffffffffffffffff, 0xffe0000000000000}, 88 | 76: {0xffffffffffffffff, 0xfff0000000000000}, 89 | 77: {0xffffffffffffffff, 0xfff8000000000000}, 90 | 78: {0xffffffffffffffff, 0xfffc000000000000}, 91 | 79: {0xffffffffffffffff, 0xfffe000000000000}, 92 | 80: {0xffffffffffffffff, 0xffff000000000000}, 93 | 81: {0xffffffffffffffff, 0xffff800000000000}, 94 | 82: {0xffffffffffffffff, 0xffffc00000000000}, 95 | 83: {0xffffffffffffffff, 0xffffe00000000000}, 96 | 84: {0xffffffffffffffff, 0xfffff00000000000}, 97 | 85: {0xffffffffffffffff, 0xfffff80000000000}, 98 | 86: {0xffffffffffffffff, 0xfffffc0000000000}, 99 | 87: {0xffffffffffffffff, 0xfffffe0000000000}, 100 | 88: {0xffffffffffffffff, 0xffffff0000000000}, 101 | 89: {0xffffffffffffffff, 0xffffff8000000000}, 102 | 90: {0xffffffffffffffff, 0xffffffc000000000}, 103 | 91: {0xffffffffffffffff, 0xffffffe000000000}, 104 | 92: {0xffffffffffffffff, 0xfffffff000000000}, 105 | 93: {0xffffffffffffffff, 0xfffffff800000000}, 106 | 94: {0xffffffffffffffff, 0xfffffffc00000000}, 107 | 95: {0xffffffffffffffff, 0xfffffffe00000000}, 108 | 96: {0xffffffffffffffff, 0xffffffff00000000}, 109 | 97: {0xffffffffffffffff, 0xffffffff80000000}, 110 | 98: {0xffffffffffffffff, 0xffffffffc0000000}, 111 | 99: {0xffffffffffffffff, 0xffffffffe0000000}, 112 | 100: {0xffffffffffffffff, 0xfffffffff0000000}, 113 | 101: {0xffffffffffffffff, 0xfffffffff8000000}, 114 | 102: {0xffffffffffffffff, 0xfffffffffc000000}, 115 | 103: {0xffffffffffffffff, 0xfffffffffe000000}, 116 | 104: {0xffffffffffffffff, 0xffffffffff000000}, 117 | 105: {0xffffffffffffffff, 0xffffffffff800000}, 118 | 106: {0xffffffffffffffff, 0xffffffffffc00000}, 119 | 107: {0xffffffffffffffff, 0xffffffffffe00000}, 120 | 108: {0xffffffffffffffff, 0xfffffffffff00000}, 121 | 109: {0xffffffffffffffff, 0xfffffffffff80000}, 122 | 110: {0xffffffffffffffff, 0xfffffffffffc0000}, 123 | 111: {0xffffffffffffffff, 0xfffffffffffe0000}, 124 | 112: {0xffffffffffffffff, 0xffffffffffff0000}, 125 | 113: {0xffffffffffffffff, 0xffffffffffff8000}, 126 | 114: {0xffffffffffffffff, 0xffffffffffffc000}, 127 | 115: {0xffffffffffffffff, 0xffffffffffffe000}, 128 | 116: {0xffffffffffffffff, 0xfffffffffffff000}, 129 | 117: {0xffffffffffffffff, 0xfffffffffffff800}, 130 | 118: {0xffffffffffffffff, 0xfffffffffffffc00}, 131 | 119: {0xffffffffffffffff, 0xfffffffffffffe00}, 132 | 120: {0xffffffffffffffff, 0xffffffffffffff00}, 133 | 121: {0xffffffffffffffff, 0xffffffffffffff80}, 134 | 122: {0xffffffffffffffff, 0xffffffffffffffc0}, 135 | 123: {0xffffffffffffffff, 0xffffffffffffffe0}, 136 | 124: {0xffffffffffffffff, 0xfffffffffffffff0}, 137 | 125: {0xffffffffffffffff, 0xfffffffffffffff8}, 138 | 126: {0xffffffffffffffff, 0xfffffffffffffffc}, 139 | 127: {0xffffffffffffffff, 0xfffffffffffffffe}, 140 | 128: {0xffffffffffffffff, 0xffffffffffffffff}, 141 | } 142 | -------------------------------------------------------------------------------- /netaddr.go: -------------------------------------------------------------------------------- 1 | // Copyright 2020 The Inet.Af AUTHORS. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // Package netaddr contains a IP address type that's in many ways 6 | // better than the Go standard library's net.IP type. Building on that 7 | // IP type, the package also contains IPPrefix, IPPort, IPRange, and 8 | // IPSet types. 9 | // 10 | // Notably, this package's IP type takes less memory, is immutable, 11 | // comparable (supports == and being a map key), and more. See 12 | // https://github.com/inetaf/netaddr for background. 13 | // 14 | // IPv6 Zones 15 | // 16 | // IP and IPPort are the only types in this package that support IPv6 17 | // zones. Other types silently drop any passed-in zones. 18 | package netaddr // import "inet.af/netaddr" 19 | 20 | import ( 21 | "encoding/binary" 22 | "errors" 23 | "fmt" 24 | "math" 25 | "net" 26 | "sort" 27 | "strconv" 28 | "strings" 29 | 30 | "go4.org/intern" 31 | ) 32 | 33 | // Sizes: (64-bit) 34 | // net.IP: 24 byte slice header + {4, 16} = 28 to 40 bytes 35 | // net.IPAddr: 40 byte slice header + {4, 16} = 44 to 56 bytes + zone length 36 | // netaddr.IP: 24 bytes (zone is per-name singleton, shared across all users) 37 | 38 | // IP represents an IPv4 or IPv6 address (with or without a scoped 39 | // addressing zone), similar to Go's net.IP or net.IPAddr. 40 | // 41 | // Unlike net.IP or net.IPAddr, the netaddr.IP is a comparable value 42 | // type (it supports == and can be a map key) and is immutable. 43 | // Its memory representation is 24 bytes on 64-bit machines (the same 44 | // size as a Go slice header) for both IPv4 and IPv6 address. 45 | type IP struct { 46 | // addr are the hi and lo bits of an IPv6 address. If z==z4, 47 | // hi and lo contain the IPv4-mapped IPv6 address. 48 | // 49 | // hi and lo are constructed by interpreting a 16-byte IPv6 50 | // address as a big-endian 128-bit number. The most significant 51 | // bits of that number go into hi, the rest into lo. 52 | // 53 | // For example, 0011:2233:4455:6677:8899:aabb:ccdd:eeff is stored as: 54 | // addr.hi = 0x0011223344556677 55 | // addr.lo = 0x8899aabbccddeeff 56 | // 57 | // We store IPs like this, rather than as [16]byte, because it 58 | // turns most operations on IPs into arithmetic and bit-twiddling 59 | // operations on 64-bit registers, which is much faster than 60 | // bytewise processing. 61 | addr uint128 62 | 63 | // z is a combination of the address family and the IPv6 zone. 64 | // 65 | // nil means invalid IP address (for the IP zero value). 66 | // z4 means an IPv4 address. 67 | // z6noz means an IPv6 address without a zone. 68 | // 69 | // Otherwise it's the interned zone name string. 70 | z *intern.Value 71 | } 72 | 73 | // z0, z4, and z6noz are sentinel IP.z values. 74 | // See the IP type's field docs. 75 | var ( 76 | z0 = (*intern.Value)(nil) 77 | z4 = new(intern.Value) 78 | z6noz = new(intern.Value) 79 | ) 80 | 81 | // IPv6LinkLocalAllNodes returns the IPv6 link-local all nodes multicast 82 | // address ff02::1. 83 | func IPv6LinkLocalAllNodes() IP { return IPv6Raw([16]byte{0: 0xff, 1: 0x02, 15: 0x01}) } 84 | 85 | // IPv6Unspecified returns the IPv6 unspecified address ::. 86 | func IPv6Unspecified() IP { return IP{z: z6noz} } 87 | 88 | // IPv4 returns the IP of the IPv4 address a.b.c.d. 89 | func IPv4(a, b, c, d uint8) IP { 90 | return IP{ 91 | addr: uint128{0, 0xffff00000000 | uint64(a)<<24 | uint64(b)<<16 | uint64(c)<<8 | uint64(d)}, 92 | z: z4, 93 | } 94 | } 95 | 96 | // IPv6Raw returns the IPv6 address given by the bytes in addr, 97 | // without an implicit Unmap call to unmap any v6-mapped IPv4 98 | // address. 99 | func IPv6Raw(addr [16]byte) IP { 100 | return IP{ 101 | addr: uint128{ 102 | binary.BigEndian.Uint64(addr[:8]), 103 | binary.BigEndian.Uint64(addr[8:]), 104 | }, 105 | z: z6noz, 106 | } 107 | } 108 | 109 | // ipv6Slice is like IPv6Raw, but operates on a 16-byte slice. Assumes 110 | // slice is 16 bytes, caller must enforce this. 111 | func ipv6Slice(addr []byte) IP { 112 | return IP{ 113 | addr: uint128{ 114 | binary.BigEndian.Uint64(addr[:8]), 115 | binary.BigEndian.Uint64(addr[8:]), 116 | }, 117 | z: z6noz, 118 | } 119 | } 120 | 121 | // IPFrom16 returns the IP address given by the bytes in addr, 122 | // unmapping any v6-mapped IPv4 address. 123 | // 124 | // It is equivalent to calling IPv6Raw(addr).Unmap(). 125 | func IPFrom16(addr [16]byte) IP { 126 | return IPv6Raw(addr).Unmap() 127 | } 128 | 129 | // IPFrom4 returns the IPv4 address given by the bytes in addr. 130 | // It is equivalent to calling IPv4(addr[0], addr[1], addr[2], addr[3]). 131 | func IPFrom4(addr [4]byte) IP { 132 | return IPv4(addr[0], addr[1], addr[2], addr[3]) 133 | } 134 | 135 | // ParseIP parses s as an IP address, returning the result. The string 136 | // s can be in dotted decimal ("192.0.2.1"), IPv6 ("2001:db8::68"), 137 | // or IPv6 with a scoped addressing zone ("fe80::1cc0:3e8c:119f:c2e1%ens18"). 138 | func ParseIP(s string) (IP, error) { 139 | for i := 0; i < len(s); i++ { 140 | switch s[i] { 141 | case '.': 142 | return parseIPv4(s) 143 | case ':': 144 | return parseIPv6(s) 145 | case '%': 146 | // Assume that this was trying to be an IPv6 address with 147 | // a zone specifier, but the address is missing. 148 | return IP{}, parseIPError{in: s, msg: "missing IPv6 address"} 149 | } 150 | } 151 | return IP{}, parseIPError{in: s, msg: "unable to parse IP"} 152 | } 153 | 154 | // MustParseIP calls ParseIP(s) and panics on error. 155 | // It is intended for use in tests with hard-coded strings. 156 | func MustParseIP(s string) IP { 157 | ip, err := ParseIP(s) 158 | if err != nil { 159 | panic(err) 160 | } 161 | return ip 162 | } 163 | 164 | type parseIPError struct { 165 | in string // the string given to ParseIP 166 | msg string // an explanation of the parse failure 167 | at string // optionally, the unparsed portion of in at which the error occurred. 168 | } 169 | 170 | func (err parseIPError) Error() string { 171 | if err.at != "" { 172 | return fmt.Sprintf("ParseIP(%q): %s (at %q)", err.in, err.msg, err.at) 173 | } 174 | return fmt.Sprintf("ParseIP(%q): %s", err.in, err.msg) 175 | } 176 | 177 | // parseIPv4 parses s as an IPv4 address (in form "192.168.0.1"). 178 | func parseIPv4(s string) (ip IP, err error) { 179 | var fields [3]uint8 180 | var val, pos int 181 | for i := 0; i < len(s); i++ { 182 | if s[i] >= '0' && s[i] <= '9' { 183 | val = val*10 + int(s[i]) - '0' 184 | if val > 255 { 185 | return IP{}, parseIPError{in: s, msg: "IPv4 field has value >255"} 186 | } 187 | } else if s[i] == '.' { 188 | // .1.2.3 189 | // 1.2.3. 190 | // 1..2.3 191 | if i == 0 || i == len(s)-1 || s[i-1] == '.' { 192 | return IP{}, parseIPError{in: s, msg: "IPv4 field must have at least one digit", at: s[i:]} 193 | } 194 | // 1.2.3.4.5 195 | if pos == 3 { 196 | return IP{}, parseIPError{in: s, msg: "IPv4 address too long"} 197 | } 198 | fields[pos] = uint8(val) 199 | pos++ 200 | val = 0 201 | } else { 202 | return IP{}, parseIPError{in: s, msg: "unexpected character", at: s[i:]} 203 | } 204 | } 205 | if pos < 3 { 206 | return IP{}, parseIPError{in: s, msg: "IPv4 address too short"} 207 | } 208 | return IPv4(fields[0], fields[1], fields[2], uint8(val)), nil 209 | } 210 | 211 | // parseIPv6 parses s as an IPv6 address (in form "2001:db8::68"). 212 | func parseIPv6(in string) (IP, error) { 213 | s := in 214 | 215 | // Split off the zone right from the start. Yes it's a second scan 216 | // of the string, but trying to handle it inline makes a bunch of 217 | // other inner loop conditionals more expensive, and it ends up 218 | // being slower. 219 | zone := "" 220 | i := strings.IndexByte(s, '%') 221 | if i != -1 { 222 | s, zone = s[:i], s[i+1:] 223 | if zone == "" { 224 | // Not allowed to have an empty zone if explicitly specified. 225 | return IP{}, parseIPError{in: in, msg: "zone must be a non-empty string"} 226 | } 227 | } 228 | 229 | var ip [16]byte 230 | ellipsis := -1 // position of ellipsis in ip 231 | 232 | // Might have leading ellipsis 233 | if len(s) >= 2 && s[0] == ':' && s[1] == ':' { 234 | ellipsis = 0 235 | s = s[2:] 236 | // Might be only ellipsis 237 | if len(s) == 0 { 238 | return IPv6Unspecified().WithZone(zone), nil 239 | } 240 | } 241 | 242 | // Loop, parsing hex numbers followed by colon. 243 | i = 0 244 | for i < 16 { 245 | // Hex number. Similar to parseIPv4, inlining the hex number 246 | // parsing yields a significant performance increase. 247 | off := 0 248 | acc := uint32(0) 249 | for ; off < len(s); off++ { 250 | c := s[off] 251 | if c >= '0' && c <= '9' { 252 | acc = (acc << 4) + uint32(c-'0') 253 | } else if c >= 'a' && c <= 'f' { 254 | acc = (acc << 4) + uint32(c-'a'+10) 255 | } else if c >= 'A' && c <= 'F' { 256 | acc = (acc << 4) + uint32(c-'A'+10) 257 | } else { 258 | break 259 | } 260 | if acc > math.MaxUint16 { 261 | // Overflow, fail. 262 | return IP{}, parseIPError{in: in, msg: "IPv6 field has value >=2^16", at: s} 263 | } 264 | } 265 | if off == 0 { 266 | // No digits found, fail. 267 | return IP{}, parseIPError{in: in, msg: "each colon-separated field must have at least one digit", at: s} 268 | } 269 | 270 | // If followed by dot, might be in trailing IPv4. 271 | if off < len(s) && s[off] == '.' { 272 | if ellipsis < 0 && i != 12 { 273 | // Not the right place. 274 | return IP{}, parseIPError{in: in, msg: "embedded IPv4 address must replace the final 2 fields of the address", at: s} 275 | } 276 | if i+4 > 16 { 277 | // Not enough room. 278 | return IP{}, parseIPError{in: in, msg: "too many hex fields to fit an embedded IPv4 at the end of the address", at: s} 279 | } 280 | // TODO: could make this a bit faster by having a helper 281 | // that parses to a [4]byte, and have both parseIPv4 and 282 | // parseIPv6 use it. 283 | ip4, err := parseIPv4(s) 284 | if err != nil { 285 | return IP{}, parseIPError{in: in, msg: err.Error(), at: s} 286 | } 287 | ip[i] = ip4.v4(0) 288 | ip[i+1] = ip4.v4(1) 289 | ip[i+2] = ip4.v4(2) 290 | ip[i+3] = ip4.v4(3) 291 | s = "" 292 | i += 4 293 | break 294 | } 295 | 296 | // Save this 16-bit chunk. 297 | ip[i] = byte(acc >> 8) 298 | ip[i+1] = byte(acc) 299 | i += 2 300 | 301 | // Stop at end of string. 302 | s = s[off:] 303 | if len(s) == 0 { 304 | break 305 | } 306 | 307 | // Otherwise must be followed by colon and more. 308 | if s[0] != ':' { 309 | return IP{}, parseIPError{in: in, msg: "unexpected character, want colon", at: s} 310 | } else if len(s) == 1 { 311 | return IP{}, parseIPError{in: in, msg: "colon must be followed by more characters", at: s} 312 | } 313 | s = s[1:] 314 | 315 | // Look for ellipsis. 316 | if s[0] == ':' { 317 | if ellipsis >= 0 { // already have one 318 | return IP{}, parseIPError{in: in, msg: "multiple :: in address", at: s} 319 | } 320 | ellipsis = i 321 | s = s[1:] 322 | if len(s) == 0 { // can be at end 323 | break 324 | } 325 | } 326 | } 327 | 328 | // Must have used entire string. 329 | if len(s) != 0 { 330 | return IP{}, parseIPError{in: in, msg: "trailing garbage after address", at: s} 331 | } 332 | 333 | // If didn't parse enough, expand ellipsis. 334 | if i < 16 { 335 | if ellipsis < 0 { 336 | return IP{}, parseIPError{in: in, msg: "address string too short"} 337 | } 338 | n := 16 - i 339 | for j := i - 1; j >= ellipsis; j-- { 340 | ip[j+n] = ip[j] 341 | } 342 | for j := ellipsis + n - 1; j >= ellipsis; j-- { 343 | ip[j] = 0 344 | } 345 | } else if ellipsis >= 0 { 346 | // Ellipsis must represent at least one 0 group. 347 | return IP{}, parseIPError{in: in, msg: "the :: must expand to at least one field of zeros"} 348 | } 349 | return IPv6Raw(ip).WithZone(zone), nil 350 | } 351 | 352 | // FromStdIP returns an IP from the standard library's IP type. 353 | // 354 | // If std is invalid, ok is false. 355 | // 356 | // FromStdIP implicitly unmaps IPv6-mapped IPv4 addresses. That is, if 357 | // len(std) == 16 and contains an IPv4 address, only the IPv4 part is 358 | // returned, without the IPv6 wrapper. This is the common form returned by 359 | // the standard library's ParseIP: https://play.golang.org/p/qdjylUkKWxl. 360 | // To convert a standard library IP without the implicit unmapping, use 361 | // FromStdIPRaw. 362 | func FromStdIP(std net.IP) (ip IP, ok bool) { 363 | ret, ok := FromStdIPRaw(std) 364 | if ret.Is4in6() { 365 | ret.z = z4 366 | } 367 | return ret, ok 368 | } 369 | 370 | // FromStdIPRaw returns an IP from the standard library's IP type. 371 | // If std is invalid, ok is false. 372 | // Unlike FromStdIP, FromStdIPRaw does not do an implicit Unmap if 373 | // len(std) == 16 and contains an IPv6-mapped IPv4 address. 374 | func FromStdIPRaw(std net.IP) (ip IP, ok bool) { 375 | switch len(std) { 376 | case 4: 377 | return IPv4(std[0], std[1], std[2], std[3]), true 378 | case 16: 379 | return ipv6Slice(std), true 380 | } 381 | return IP{}, false 382 | } 383 | 384 | // v4 returns the i'th byte of ip. If ip is not an IPv4, v4 returns 385 | // unspecified garbage. 386 | func (ip IP) v4(i uint8) uint8 { 387 | return uint8(ip.addr.lo >> ((3 - i) * 8)) 388 | } 389 | 390 | // v6 returns the i'th byte of ip. If ip is an IPv4 address, this 391 | // accesses the IPv4-mapped IPv6 address form of the IP. 392 | func (ip IP) v6(i uint8) uint8 { 393 | return uint8(*(ip.addr.halves()[(i/8)%2]) >> ((7 - i%8) * 8)) 394 | } 395 | 396 | // v6u16 returns the i'th 16-bit word of ip. If ip is an IPv4 address, 397 | // this accesses the IPv4-mapped IPv6 address form of the IP. 398 | func (ip IP) v6u16(i uint8) uint16 { 399 | return uint16(*(ip.addr.halves()[(i/4)%2]) >> ((3 - i%4) * 16)) 400 | } 401 | 402 | // IsZero reports whether ip is the zero value of the IP type. 403 | // The zero value is not a valid IP address of any type. 404 | // 405 | // Note that "0.0.0.0" and "::" are not the zero value. Use IsUnspecified to 406 | // check for these values instead. 407 | func (ip IP) IsZero() bool { 408 | // Faster than comparing ip == IP{}, but effectively equivalent, 409 | // as there's no way to make an IP with a nil z from this package. 410 | return ip.z == z0 411 | } 412 | 413 | // IsValid whether the IP is an initialized value and not the IP 414 | // type's zero value. 415 | // 416 | // Note that both "0.0.0.0" and "::" are valid, non-zero values. 417 | func (ip IP) IsValid() bool { return ip.z != z0 } 418 | 419 | // BitLen returns the number of bits in the IP address: 420 | // 32 for IPv4 or 128 for IPv6. 421 | // For the zero value (see IP.IsZero), it returns 0. 422 | // For IP4-mapped IPv6 addresses, it returns 128. 423 | func (ip IP) BitLen() uint8 { 424 | switch ip.z { 425 | case z0: 426 | return 0 427 | case z4: 428 | return 32 429 | } 430 | return 128 431 | } 432 | 433 | // Zone returns ip's IPv6 scoped addressing zone, if any. 434 | func (ip IP) Zone() string { 435 | if ip.z == nil { 436 | return "" 437 | } 438 | zone, _ := ip.z.Get().(string) 439 | return zone 440 | } 441 | 442 | // Compare returns an integer comparing two IPs. 443 | // The result will be 0 if ip==ip2, -1 if ip < ip2, and +1 if ip > ip2. 444 | // The definition of "less than" is the same as the IP.Less method. 445 | func (ip IP) Compare(ip2 IP) int { 446 | f1, f2 := ip.BitLen(), ip2.BitLen() 447 | if f1 < f2 { 448 | return -1 449 | } 450 | if f1 > f2 { 451 | return 1 452 | } 453 | if hi1, hi2 := ip.addr.hi, ip2.addr.hi; hi1 < hi2 { 454 | return -1 455 | } else if hi1 > hi2 { 456 | return 1 457 | } 458 | if lo1, lo2 := ip.addr.lo, ip2.addr.lo; lo1 < lo2 { 459 | return -1 460 | } else if lo1 > lo2 { 461 | return 1 462 | } 463 | if ip.Is6() { 464 | za, zb := ip.Zone(), ip2.Zone() 465 | if za < zb { 466 | return -1 467 | } else if za > zb { 468 | return 1 469 | } 470 | } 471 | return 0 472 | } 473 | 474 | // Less reports whether ip sorts before ip2. 475 | // IP addresses sort first by length, then their address. 476 | // IPv6 addresses with zones sort just after the same address without a zone. 477 | func (ip IP) Less(ip2 IP) bool { return ip.Compare(ip2) == -1 } 478 | 479 | func (ip IP) lessOrEq(ip2 IP) bool { return ip.Compare(ip2) <= 0 } 480 | 481 | // ipZone returns the standard library net.IP from ip, as well 482 | // as the zone. 483 | // The optional reuse IP provides memory to reuse. 484 | func (ip IP) ipZone(reuse net.IP) (stdIP net.IP, zone string) { 485 | base := reuse[:0] 486 | switch { 487 | case ip.z == z0: 488 | return nil, "" 489 | case ip.Is4(): 490 | a4 := ip.As4() 491 | return append(base, a4[:]...), "" 492 | default: 493 | a16 := ip.As16() 494 | return append(base, a16[:]...), ip.Zone() 495 | } 496 | } 497 | 498 | // IPAddr returns the net.IPAddr representation of an IP. The returned value is 499 | // always non-nil, but the IPAddr.IP will be nil if ip is the zero value. 500 | // If ip contains a zone identifier, IPAddr.Zone is populated. 501 | func (ip IP) IPAddr() *net.IPAddr { 502 | stdIP, zone := ip.ipZone(nil) 503 | return &net.IPAddr{IP: stdIP, Zone: zone} 504 | } 505 | 506 | // Is4 reports whether ip is an IPv4 address. 507 | // 508 | // It returns false for IP4-mapped IPv6 addresses. See IP.Unmap. 509 | func (ip IP) Is4() bool { 510 | return ip.z == z4 511 | } 512 | 513 | // Is4in6 reports whether ip is an IPv4-mapped IPv6 address. 514 | func (ip IP) Is4in6() bool { 515 | return ip.Is6() && ip.addr.hi == 0 && ip.addr.lo>>32 == 0xffff 516 | } 517 | 518 | // Is6 reports whether ip is an IPv6 address, including IPv4-mapped 519 | // IPv6 addresses. 520 | func (ip IP) Is6() bool { 521 | return ip.z != z0 && ip.z != z4 522 | } 523 | 524 | // Unmap returns ip with any IPv4-mapped IPv6 address prefix removed. 525 | // 526 | // That is, if ip is an IPv6 address wrapping an IPv4 adddress, it 527 | // returns the wrapped IPv4 address. Otherwise it returns ip, regardless 528 | // of its type. 529 | func (ip IP) Unmap() IP { 530 | if ip.Is4in6() { 531 | ip.z = z4 532 | } 533 | return ip 534 | } 535 | 536 | // WithZone returns an IP that's the same as ip but with the provided 537 | // zone. If zone is empty, the zone is removed. If ip is an IPv4 538 | // address it's returned unchanged. 539 | func (ip IP) WithZone(zone string) IP { 540 | if !ip.Is6() { 541 | return ip 542 | } 543 | if zone == "" { 544 | ip.z = z6noz 545 | return ip 546 | } 547 | ip.z = intern.GetByString(zone) 548 | return ip 549 | } 550 | 551 | // noZone unconditionally strips the zone from IP. 552 | // It's similar to WithZone, but small enough to be inlinable. 553 | func (ip IP) withoutZone() IP { 554 | if !ip.Is6() { 555 | return ip 556 | } 557 | ip.z = z6noz 558 | return ip 559 | } 560 | 561 | // hasZone reports whether IP has an IPv6 zone. 562 | func (ip IP) hasZone() bool { 563 | return ip.z != z0 && ip.z != z4 && ip.z != z6noz 564 | } 565 | 566 | // IsLinkLocalUnicast reports whether ip is a link-local unicast address. 567 | // If ip is the zero value, it will return false. 568 | func (ip IP) IsLinkLocalUnicast() bool { 569 | // Dynamic Configuration of IPv4 Link-Local Addresses 570 | // https://datatracker.ietf.org/doc/html/rfc3927#section-2.1 571 | if ip.Is4() { 572 | return ip.v4(0) == 169 && ip.v4(1) == 254 573 | } 574 | // IP Version 6 Addressing Architecture (2.4 Address Type Identification) 575 | // https://datatracker.ietf.org/doc/html/rfc4291#section-2.4 576 | if ip.Is6() { 577 | return ip.v6u16(0)&0xffc0 == 0xfe80 578 | } 579 | return false // zero value 580 | } 581 | 582 | // IsLoopback reports whether ip is a loopback address. If ip is the zero value, 583 | // it will return false. 584 | func (ip IP) IsLoopback() bool { 585 | // Requirements for Internet Hosts -- Communication Layers (3.2.1.3 Addressing) 586 | // https://datatracker.ietf.org/doc/html/rfc1122#section-3.2.1.3 587 | if ip.Is4() { 588 | return ip.v4(0) == 127 589 | } 590 | // IP Version 6 Addressing Architecture (2.4 Address Type Identification) 591 | // https://datatracker.ietf.org/doc/html/rfc4291#section-2.4 592 | if ip.Is6() { 593 | return ip.addr.hi == 0 && ip.addr.lo == 1 594 | } 595 | return false // zero value 596 | } 597 | 598 | // IsMulticast reports whether ip is a multicast address. If ip is the zero 599 | // value, it will return false. 600 | func (ip IP) IsMulticast() bool { 601 | // Host Extensions for IP Multicasting (4. HOST GROUP ADDRESSES) 602 | // https://datatracker.ietf.org/doc/html/rfc1112#section-4 603 | if ip.Is4() { 604 | return ip.v4(0)&0xf0 == 0xe0 605 | } 606 | // IP Version 6 Addressing Architecture (2.4 Address Type Identification) 607 | // https://datatracker.ietf.org/doc/html/rfc4291#section-2.4 608 | if ip.Is6() { 609 | return ip.addr.hi>>(64-8) == 0xff // ip.v6(0) == 0xff 610 | } 611 | return false // zero value 612 | } 613 | 614 | // IsInterfaceLocalMulticast reports whether ip is an IPv6 interface-local 615 | // multicast address. If ip is the zero value or an IPv4 address, it will return 616 | // false. 617 | func (ip IP) IsInterfaceLocalMulticast() bool { 618 | // IPv6 Addressing Architecture (2.7.1. Pre-Defined Multicast Addresses) 619 | // https://datatracker.ietf.org/doc/html/rfc4291#section-2.7.1 620 | if ip.Is6() { 621 | return ip.v6u16(0)&0xff0f == 0xff01 622 | } 623 | return false // zero value 624 | } 625 | 626 | // IsLinkLocalMulticast reports whether ip is a link-local multicast address. 627 | // If ip is the zero value, it will return false. 628 | func (ip IP) IsLinkLocalMulticast() bool { 629 | // IPv4 Multicast Guidelines (4. Local Network Control Block (224.0.0/24)) 630 | // https://datatracker.ietf.org/doc/html/rfc5771#section-4 631 | if ip.Is4() { 632 | return ip.v4(0) == 224 && ip.v4(1) == 0 && ip.v4(2) == 0 633 | } 634 | // IPv6 Addressing Architecture (2.7.1. Pre-Defined Multicast Addresses) 635 | // https://datatracker.ietf.org/doc/html/rfc4291#section-2.7.1 636 | if ip.Is6() { 637 | return ip.v6u16(0)&0xff0f == 0xff02 638 | } 639 | return false // zero value 640 | } 641 | 642 | // IsGlobalUnicast reports whether ip is a global unicast address. 643 | // 644 | // It returns true for IPv6 addresses which fall outside of the current 645 | // IANA-allocated 2000::/3 global unicast space, with the exception of the 646 | // link-local address space. It also returns true even if ip is in the IPv4 647 | // private address space or IPv6 unique local address space. If ip is the zero 648 | // value, it will return false. 649 | // 650 | // For reference, see RFC 1122, RFC 4291, and RFC 4632. 651 | func (ip IP) IsGlobalUnicast() bool { 652 | if ip.z == z0 { 653 | // Invalid or zero-value. 654 | return false 655 | } 656 | 657 | // Match the stdlib's IsGlobalUnicast logic. Notably private IPv4 addresses 658 | // and ULA IPv6 addresses are still considered "global unicast". 659 | if ip.Is4() && (ip == IPv4(0, 0, 0, 0) || ip == IPv4(255, 255, 255, 255)) { 660 | return false 661 | } 662 | 663 | return ip != IPv6Unspecified() && 664 | !ip.IsLoopback() && 665 | !ip.IsMulticast() && 666 | !ip.IsLinkLocalUnicast() 667 | } 668 | 669 | // IsPrivate reports whether ip is a private address, according to RFC 1918 670 | // (IPv4 addresses) and RFC 4193 (IPv6 addresses). That is, it reports whether 671 | // ip is in 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16, or fc00::/7. This is the 672 | // same as the standard library's net.IP.IsPrivate. 673 | func (ip IP) IsPrivate() bool { 674 | // Match the stdlib's IsPrivate logic. 675 | if ip.Is4() { 676 | // RFC 1918 allocates 10.0.0.0/8, 172.16.0.0/12, and 192.168.0.0/16 as 677 | // private IPv4 address subnets. 678 | return ip.v4(0) == 10 || 679 | (ip.v4(0) == 172 && ip.v4(1)&0xf0 == 16) || 680 | (ip.v4(0) == 192 && ip.v4(1) == 168) 681 | } 682 | 683 | if ip.Is6() { 684 | // RFC 4193 allocates fc00::/7 as the unique local unicast IPv6 address 685 | // subnet. 686 | return ip.v6(0)&0xfe == 0xfc 687 | } 688 | 689 | return false // zero value 690 | } 691 | 692 | // IsUnspecified reports whether ip is an unspecified address, either the IPv4 693 | // address "0.0.0.0" or the IPv6 address "::". 694 | // 695 | // Note that the IP zero value is not an unspecified address. Use IsZero to 696 | // check for the zero value instead. 697 | func (ip IP) IsUnspecified() bool { 698 | return ip == IPv4(0, 0, 0, 0) || ip == IPv6Unspecified() 699 | } 700 | 701 | // Prefix applies a CIDR mask of leading bits to IP, producing an IPPrefix 702 | // of the specified length. If IP is the zero value, a zero-value IPPrefix and 703 | // a nil error are returned. If bits is larger than 32 for an IPv4 address or 704 | // 128 for an IPv6 address, an error is returned. 705 | func (ip IP) Prefix(bits uint8) (IPPrefix, error) { 706 | effectiveBits := bits 707 | switch ip.z { 708 | case z0: 709 | return IPPrefix{}, nil 710 | case z4: 711 | if bits > 32 { 712 | return IPPrefix{}, fmt.Errorf("prefix length %d too large for IPv4", bits) 713 | } 714 | effectiveBits += 96 715 | default: 716 | if bits > 128 { 717 | return IPPrefix{}, fmt.Errorf("prefix length %d too large for IPv6", bits) 718 | } 719 | } 720 | ip.addr = ip.addr.and(mask6[effectiveBits]) 721 | return IPPrefixFrom(ip, bits), nil 722 | } 723 | 724 | // Netmask applies a bit mask to IP, producing an IPPrefix. If IP is the 725 | // zero value, a zero-value IPPrefix and a nil error are returned. If the 726 | // netmask length is not 4 for IPv4 or 16 for IPv6, an error is 727 | // returned. If the netmask is non-contiguous, an error is returned. 728 | func (ip IP) Netmask(mask []byte) (IPPrefix, error) { 729 | l := len(mask) 730 | 731 | switch ip.z { 732 | case z0: 733 | return IPPrefix{}, nil 734 | case z4: 735 | if l != net.IPv4len { 736 | return IPPrefix{}, fmt.Errorf("netmask length %d incorrect for IPv4", l) 737 | } 738 | default: 739 | if l != net.IPv6len { 740 | return IPPrefix{}, fmt.Errorf("netmask length %d incorrect for IPv6", l) 741 | } 742 | } 743 | 744 | ones, bits := net.IPMask(mask).Size() 745 | if ones == 0 && bits == 0 { 746 | return IPPrefix{}, errors.New("netmask is non-contiguous") 747 | } 748 | 749 | return ip.Prefix(uint8(ones)) 750 | } 751 | 752 | // As16 returns the IP address in its 16 byte representation. 753 | // IPv4 addresses are returned in their v6-mapped form. 754 | // IPv6 addresses with zones are returned without their zone (use the 755 | // Zone method to get it). 756 | // The ip zero value returns all zeroes. 757 | func (ip IP) As16() [16]byte { 758 | var ret [16]byte 759 | binary.BigEndian.PutUint64(ret[:8], ip.addr.hi) 760 | binary.BigEndian.PutUint64(ret[8:], ip.addr.lo) 761 | return ret 762 | } 763 | 764 | // As4 returns an IPv4 or IPv4-in-IPv6 address in its 4 byte representation. 765 | // If ip is the IP zero value or an IPv6 address, As4 panics. 766 | // Note that 0.0.0.0 is not the zero value. 767 | func (ip IP) As4() [4]byte { 768 | if ip.z == z4 || ip.Is4in6() { 769 | var ret [4]byte 770 | binary.BigEndian.PutUint32(ret[:], uint32(ip.addr.lo)) 771 | return ret 772 | } 773 | if ip.z == z0 { 774 | panic("As4 called on IP zero value") 775 | } 776 | panic("As4 called on IPv6 address") 777 | } 778 | 779 | // Next returns the IP following ip. 780 | // If there is none, it returns the IP zero value. 781 | func (ip IP) Next() IP { 782 | ip.addr = ip.addr.addOne() 783 | if ip.Is4() { 784 | if uint32(ip.addr.lo) == 0 { 785 | // Overflowed. 786 | return IP{} 787 | } 788 | } else { 789 | if ip.addr.isZero() { 790 | // Overflowed 791 | return IP{} 792 | } 793 | } 794 | return ip 795 | } 796 | 797 | // Prior returns the IP before ip. 798 | // If there is none, it returns the IP zero value. 799 | func (ip IP) Prior() IP { 800 | if ip.Is4() { 801 | if uint32(ip.addr.lo) == 0 { 802 | return IP{} 803 | } 804 | } else if ip.addr.isZero() { 805 | return IP{} 806 | } 807 | ip.addr = ip.addr.subOne() 808 | return ip 809 | } 810 | 811 | // String returns the string form of the IP address ip. 812 | // It returns one of 4 forms: 813 | // 814 | // - "invalid IP", if ip is the zero value 815 | // - IPv4 dotted decimal ("192.0.2.1") 816 | // - IPv6 ("2001:db8::1") 817 | // - IPv6 with zone ("fe80:db8::1%eth0") 818 | // 819 | // Note that unlike the Go standard library's IP.String method, 820 | // IP4-mapped IPv6 addresses do not format as dotted decimals. 821 | func (ip IP) String() string { 822 | switch ip.z { 823 | case z0: 824 | return "zero IP" 825 | case z4: 826 | return ip.string4() 827 | default: 828 | return ip.string6() 829 | } 830 | } 831 | 832 | // AppendTo appends a text encoding of ip, 833 | // as generated by MarshalText, 834 | // to b and returns the extended buffer. 835 | func (ip IP) AppendTo(b []byte) []byte { 836 | switch ip.z { 837 | case z0: 838 | return b 839 | case z4: 840 | return ip.appendTo4(b) 841 | default: 842 | return ip.appendTo6(b) 843 | } 844 | } 845 | 846 | // digits is a string of the hex digits from 0 to f. It's used in 847 | // appendDecimal and appendHex to format IP addresses. 848 | const digits = "0123456789abcdef" 849 | 850 | // appendDecimal appends the decimal string representation of x to b. 851 | func appendDecimal(b []byte, x uint8) []byte { 852 | // Using this function rather than strconv.AppendUint makes IPv4 853 | // string building 2x faster. 854 | 855 | if x >= 100 { 856 | b = append(b, digits[x/100]) 857 | } 858 | if x >= 10 { 859 | b = append(b, digits[x/10%10]) 860 | } 861 | return append(b, digits[x%10]) 862 | } 863 | 864 | // appendHex appends the hex string representation of x to b. 865 | func appendHex(b []byte, x uint16) []byte { 866 | // Using this function rather than strconv.AppendUint makes IPv6 867 | // string building 2x faster. 868 | 869 | if x >= 0x1000 { 870 | b = append(b, digits[x>>12]) 871 | } 872 | if x >= 0x100 { 873 | b = append(b, digits[x>>8&0xf]) 874 | } 875 | if x >= 0x10 { 876 | b = append(b, digits[x>>4&0xf]) 877 | } 878 | return append(b, digits[x&0xf]) 879 | } 880 | 881 | // appendHexPad appends the fully padded hex string representation of x to b. 882 | func appendHexPad(b []byte, x uint16) []byte { 883 | return append(b, digits[x>>12], digits[x>>8&0xf], digits[x>>4&0xf], digits[x&0xf]) 884 | } 885 | 886 | func (ip IP) string4() string { 887 | const max = len("255.255.255.255") 888 | ret := make([]byte, 0, max) 889 | ret = ip.appendTo4(ret) 890 | return string(ret) 891 | } 892 | 893 | func (ip IP) appendTo4(ret []byte) []byte { 894 | ret = appendDecimal(ret, ip.v4(0)) 895 | ret = append(ret, '.') 896 | ret = appendDecimal(ret, ip.v4(1)) 897 | ret = append(ret, '.') 898 | ret = appendDecimal(ret, ip.v4(2)) 899 | ret = append(ret, '.') 900 | ret = appendDecimal(ret, ip.v4(3)) 901 | return ret 902 | } 903 | 904 | // string6 formats ip in IPv6 textual representation. It follows the 905 | // guidelines in section 4 of RFC 5952 906 | // (https://tools.ietf.org/html/rfc5952#section-4): no unnecessary 907 | // zeros, use :: to elide the longest run of zeros, and don't use :: 908 | // to compact a single zero field. 909 | func (ip IP) string6() string { 910 | // Use a zone with a "plausibly long" name, so that most zone-ful 911 | // IP addresses won't require additional allocation. 912 | // 913 | // The compiler does a cool optimization here, where ret ends up 914 | // stack-allocated and so the only allocation this function does 915 | // is to construct the returned string. As such, it's okay to be a 916 | // bit greedy here, size-wise. 917 | const max = len("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff%enp5s0") 918 | ret := make([]byte, 0, max) 919 | ret = ip.appendTo6(ret) 920 | return string(ret) 921 | } 922 | 923 | func (ip IP) appendTo6(ret []byte) []byte { 924 | zeroStart, zeroEnd := uint8(255), uint8(255) 925 | for i := uint8(0); i < 8; i++ { 926 | j := i 927 | for j < 8 && ip.v6u16(j) == 0 { 928 | j++ 929 | } 930 | if l := j - i; l >= 2 && l > zeroEnd-zeroStart { 931 | zeroStart, zeroEnd = i, j 932 | } 933 | } 934 | 935 | for i := uint8(0); i < 8; i++ { 936 | if i == zeroStart { 937 | ret = append(ret, ':', ':') 938 | i = zeroEnd 939 | if i >= 8 { 940 | break 941 | } 942 | } else if i > 0 { 943 | ret = append(ret, ':') 944 | } 945 | 946 | ret = appendHex(ret, ip.v6u16(i)) 947 | } 948 | 949 | if ip.z != z6noz { 950 | ret = append(ret, '%') 951 | ret = append(ret, ip.Zone()...) 952 | } 953 | return ret 954 | } 955 | 956 | // StringExpanded is like String but IPv6 addresses are expanded with leading 957 | // zeroes and no "::" compression. For example, "2001:db8::1" becomes 958 | // "2001:0db8:0000:0000:0000:0000:0000:0001". 959 | func (ip IP) StringExpanded() string { 960 | switch ip.z { 961 | case z0, z4: 962 | return ip.String() 963 | } 964 | 965 | const size = len("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff") 966 | ret := make([]byte, 0, size) 967 | for i := uint8(0); i < 8; i++ { 968 | if i > 0 { 969 | ret = append(ret, ':') 970 | } 971 | 972 | ret = appendHexPad(ret, ip.v6u16(i)) 973 | } 974 | 975 | if ip.z != z6noz { 976 | // The addition of a zone will cause a second allocation, but when there 977 | // is no zone the ret slice will be stack allocated. 978 | ret = append(ret, '%') 979 | ret = append(ret, ip.Zone()...) 980 | } 981 | return string(ret) 982 | } 983 | 984 | // MarshalText implements the encoding.TextMarshaler interface, 985 | // The encoding is the same as returned by String, with one exception: 986 | // If ip is the zero value, the encoding is the empty string. 987 | func (ip IP) MarshalText() ([]byte, error) { 988 | switch ip.z { 989 | case z0: 990 | return []byte(""), nil 991 | case z4: 992 | max := len("255.255.255.255") 993 | b := make([]byte, 0, max) 994 | return ip.appendTo4(b), nil 995 | default: 996 | max := len("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff%enp5s0") 997 | b := make([]byte, 0, max) 998 | return ip.appendTo6(b), nil 999 | } 1000 | } 1001 | 1002 | // UnmarshalText implements the encoding.TextUnmarshaler interface. 1003 | // The IP address is expected in a form accepted by ParseIP. 1004 | // It returns an error if *ip is not the IP zero value. 1005 | func (ip *IP) UnmarshalText(text []byte) error { 1006 | if ip.z != z0 { 1007 | return errors.New("refusing to Unmarshal into non-zero IP") 1008 | } 1009 | if len(text) == 0 { 1010 | return nil 1011 | } 1012 | var err error 1013 | *ip, err = ParseIP(string(text)) 1014 | return err 1015 | } 1016 | 1017 | // MarshalBinary implements the encoding.BinaryMarshaler interface. 1018 | func (ip IP) MarshalBinary() ([]byte, error) { 1019 | switch ip.z { 1020 | case z0: 1021 | return nil, nil 1022 | case z4: 1023 | b := ip.As4() 1024 | return b[:], nil 1025 | default: 1026 | b16 := ip.As16() 1027 | b := b16[:] 1028 | if z := ip.Zone(); z != "" { 1029 | b = append(b, []byte(z)...) 1030 | } 1031 | return b, nil 1032 | } 1033 | } 1034 | 1035 | // UnmarshalBinary implements the encoding.BinaryUnmarshaler interface. 1036 | func (ip *IP) UnmarshalBinary(b []byte) error { 1037 | if ip.z != z0 { 1038 | return errors.New("refusing to Unmarshal into non-zero IP") 1039 | } 1040 | n := len(b) 1041 | switch { 1042 | case n == 0: 1043 | return nil 1044 | case n == 4: 1045 | *ip = IPv4(b[0], b[1], b[2], b[3]) 1046 | return nil 1047 | case n == 16: 1048 | *ip = ipv6Slice(b) 1049 | return nil 1050 | case n > 16: 1051 | *ip = ipv6Slice(b[:16]).WithZone(string(b[16:])) 1052 | return nil 1053 | } 1054 | return fmt.Errorf("unexpected ip size: %v", len(b)) 1055 | } 1056 | 1057 | // IPPort is an IP and a port number. 1058 | type IPPort struct { 1059 | ip IP 1060 | port uint16 1061 | } 1062 | 1063 | // IPPortFrom returns an IPPort with IP ip and port port. 1064 | // It does not allocate. 1065 | func IPPortFrom(ip IP, port uint16) IPPort { return IPPort{ip: ip, port: port} } 1066 | 1067 | // WithIP returns an IPPort with IP ip and port p.Port(). 1068 | func (p IPPort) WithIP(ip IP) IPPort { return IPPort{ip: ip, port: p.port} } 1069 | 1070 | // WithIP returns an IPPort with IP p.IP() and port port. 1071 | func (p IPPort) WithPort(port uint16) IPPort { return IPPort{ip: p.ip, port: port} } 1072 | 1073 | // IP returns p's IP. 1074 | func (p IPPort) IP() IP { return p.ip } 1075 | 1076 | // Port returns p's port. 1077 | func (p IPPort) Port() uint16 { return p.port } 1078 | 1079 | // splitIPPort splits s into an IP address string and a port 1080 | // string. It splits strings shaped like "foo:bar" or "[foo]:bar", 1081 | // without further validating the substrings. v6 indicates whether the 1082 | // ip string should parse as an IPv6 address or an IPv4 address, in 1083 | // order for s to be a valid ip:port string. 1084 | func splitIPPort(s string) (ip, port string, v6 bool, err error) { 1085 | i := strings.LastIndexByte(s, ':') 1086 | if i == -1 { 1087 | return "", "", false, errors.New("not an ip:port") 1088 | } 1089 | 1090 | ip, port = s[:i], s[i+1:] 1091 | if len(ip) == 0 { 1092 | return "", "", false, errors.New("no IP") 1093 | } 1094 | if len(port) == 0 { 1095 | return "", "", false, errors.New("no port") 1096 | } 1097 | if ip[0] == '[' { 1098 | if len(ip) < 2 || ip[len(ip)-1] != ']' { 1099 | return "", "", false, errors.New("missing ]") 1100 | } 1101 | ip = ip[1 : len(ip)-1] 1102 | v6 = true 1103 | } 1104 | 1105 | return ip, port, v6, nil 1106 | } 1107 | 1108 | // ParseIPPort parses s as an IPPort. 1109 | // 1110 | // It doesn't do any name resolution, and ports must be numeric. 1111 | func ParseIPPort(s string) (IPPort, error) { 1112 | var ipp IPPort 1113 | ip, port, v6, err := splitIPPort(s) 1114 | if err != nil { 1115 | return ipp, err 1116 | } 1117 | port16, err := strconv.ParseUint(port, 10, 16) 1118 | if err != nil { 1119 | return ipp, fmt.Errorf("invalid port %q parsing %q", port, s) 1120 | } 1121 | ipp.port = uint16(port16) 1122 | ipp.ip, err = ParseIP(ip) 1123 | if err != nil { 1124 | return IPPort{}, err 1125 | } 1126 | if v6 && ipp.ip.Is4() { 1127 | return IPPort{}, fmt.Errorf("invalid ip:port %q, square brackets can only be used with IPv6 addresses", s) 1128 | } else if !v6 && ipp.ip.Is6() { 1129 | return IPPort{}, fmt.Errorf("invalid ip:port %q, IPv6 addresses must be surrounded by square brackets", s) 1130 | } 1131 | return ipp, nil 1132 | } 1133 | 1134 | // MustParseIPPort calls ParseIPPort(s) and panics on error. 1135 | // It is intended for use in tests with hard-coded strings. 1136 | func MustParseIPPort(s string) IPPort { 1137 | ip, err := ParseIPPort(s) 1138 | if err != nil { 1139 | panic(err) 1140 | } 1141 | return ip 1142 | } 1143 | 1144 | // IsZero reports whether p is its zero value. 1145 | func (p IPPort) IsZero() bool { return p == IPPort{} } 1146 | 1147 | // IsValid reports whether p.IP() is valid. 1148 | // All ports are valid, including zero. 1149 | func (p IPPort) IsValid() bool { return p.ip.IsValid() } 1150 | 1151 | // Valid reports whether p.IP() is valid. 1152 | // All ports are valid, including zero. 1153 | // 1154 | // Deprecated: use the correctly named and identical IsValid method instead. 1155 | func (p IPPort) Valid() bool { return p.IsValid() } 1156 | 1157 | func (p IPPort) String() string { 1158 | switch p.ip.z { 1159 | case z0: 1160 | return "invalid IPPort" 1161 | case z4: 1162 | a := p.ip.As4() 1163 | return fmt.Sprintf("%d.%d.%d.%d:%d", a[0], a[1], a[2], a[3], p.port) 1164 | default: 1165 | // TODO: this could be more efficient allocation-wise: 1166 | return net.JoinHostPort(p.ip.String(), strconv.Itoa(int(p.port))) 1167 | } 1168 | } 1169 | 1170 | // AppendTo appends a text encoding of p, 1171 | // as generated by MarshalText, 1172 | // to b and returns the extended buffer. 1173 | func (p IPPort) AppendTo(b []byte) []byte { 1174 | switch p.ip.z { 1175 | case z0: 1176 | return b 1177 | case z4: 1178 | b = p.ip.appendTo4(b) 1179 | default: 1180 | b = append(b, '[') 1181 | b = p.ip.appendTo6(b) 1182 | b = append(b, ']') 1183 | } 1184 | b = append(b, ':') 1185 | b = strconv.AppendInt(b, int64(p.port), 10) 1186 | return b 1187 | } 1188 | 1189 | // MarshalText implements the encoding.TextMarshaler interface. The 1190 | // encoding is the same as returned by String, with one exception: if 1191 | // p.IP() is the zero value, the encoding is the empty string. 1192 | func (p IPPort) MarshalText() ([]byte, error) { 1193 | var max int 1194 | switch p.ip.z { 1195 | case z0: 1196 | case z4: 1197 | max = len("255.255.255.255:65535") 1198 | default: 1199 | max = len("[ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff%enp5s0]:65535") 1200 | } 1201 | b := make([]byte, 0, max) 1202 | b = p.AppendTo(b) 1203 | return b, nil 1204 | } 1205 | 1206 | // UnmarshalText implements the encoding.TextUnmarshaler 1207 | // interface. The IPPort is expected in a form accepted by 1208 | // ParseIPPort. It returns an error if *p is not the IPPort zero 1209 | // value. 1210 | func (p *IPPort) UnmarshalText(text []byte) error { 1211 | if p.ip.z != z0 || p.port != 0 { 1212 | return errors.New("refusing to Unmarshal into non-zero IPPort") 1213 | } 1214 | if len(text) == 0 { 1215 | return nil 1216 | } 1217 | var err error 1218 | *p, err = ParseIPPort(string(text)) 1219 | return err 1220 | } 1221 | 1222 | // FromStdAddr maps the components of a standard library TCPAddr or 1223 | // UDPAddr into an IPPort. 1224 | func FromStdAddr(stdIP net.IP, port int, zone string) (_ IPPort, ok bool) { 1225 | ip, ok := FromStdIP(stdIP) 1226 | if !ok || port < 0 || port > math.MaxUint16 { 1227 | return 1228 | } 1229 | ip = ip.Unmap() 1230 | if zone != "" { 1231 | if ip.Is4() { 1232 | ok = false 1233 | return 1234 | } 1235 | ip = ip.WithZone(zone) 1236 | } 1237 | return IPPort{ip: ip, port: uint16(port)}, true 1238 | } 1239 | 1240 | // UDPAddr returns a standard library net.UDPAddr from p. 1241 | // The returned value is always non-nil. If p.IP() is the zero 1242 | // value, then UDPAddr.IP is nil. 1243 | // 1244 | // UDPAddr necessarily does two allocations. If you have an existing 1245 | // UDPAddr already allocated, see UDPAddrAt. 1246 | func (p IPPort) UDPAddr() *net.UDPAddr { 1247 | ret := &net.UDPAddr{ 1248 | Port: int(p.port), 1249 | } 1250 | ret.IP, ret.Zone = p.ip.ipZone(nil) 1251 | return ret 1252 | } 1253 | 1254 | // UDPAddrAt is like UDPAddr, but reuses the provided UDPAddr, which 1255 | // must be non-nil. If at.IP has a capacity of 16, UDPAddrAt is 1256 | // allocation-free. It returns at to facilitate using this method as a 1257 | // wrapper. 1258 | func (p IPPort) UDPAddrAt(at *net.UDPAddr) *net.UDPAddr { 1259 | at.Port = int(p.port) 1260 | at.IP, at.Zone = p.ip.ipZone(at.IP) 1261 | return at 1262 | } 1263 | 1264 | // TCPAddr returns a standard library net.TCPAddr from p. 1265 | // The returned value is always non-nil. If p.IP() is the zero 1266 | // value, then TCPAddr.IP is nil. 1267 | func (p IPPort) TCPAddr() *net.TCPAddr { 1268 | ip, zone := p.ip.ipZone(nil) 1269 | return &net.TCPAddr{ 1270 | IP: ip, 1271 | Port: int(p.port), 1272 | Zone: zone, 1273 | } 1274 | } 1275 | 1276 | // IPPrefix is an IP address prefix (CIDR) representing an IP network. 1277 | // 1278 | // The first Bits() of IP() are specified. The remaining bits match any address. 1279 | // The range of Bits() is [0,32] for IPv4 or [0,128] for IPv6. 1280 | type IPPrefix struct { 1281 | ip IP 1282 | bits uint8 1283 | } 1284 | 1285 | // IPPrefixFrom returns an IPPrefix with IP ip and provided bits prefix length. 1286 | // It does not allocate. 1287 | func IPPrefixFrom(ip IP, bits uint8) IPPrefix { 1288 | return IPPrefix{ 1289 | ip: ip.withoutZone(), 1290 | bits: bits, 1291 | } 1292 | } 1293 | 1294 | // IP returns p's IP. 1295 | func (p IPPrefix) IP() IP { return p.ip } 1296 | 1297 | // Bits returns p's prefix length. 1298 | func (p IPPrefix) Bits() uint8 { return p.bits } 1299 | 1300 | // IsValid reports whether whether p.Bits() has a valid range for p.IP(). 1301 | // If p.IP() is zero, Valid returns false. 1302 | func (p IPPrefix) IsValid() bool { return !p.ip.IsZero() && p.bits <= p.ip.BitLen() } 1303 | 1304 | // Valid reports whether whether p.Bits() has a valid range for p.IP(). 1305 | // If p.IP() is zero, Valid returns false. 1306 | // 1307 | // Deprecated: use the correctly named and identical IsValid method instead. 1308 | func (p IPPrefix) Valid() bool { return p.IsValid() } 1309 | 1310 | // IsZero reports whether p is its zero value. 1311 | func (p IPPrefix) IsZero() bool { return p == IPPrefix{} } 1312 | 1313 | // IsSingleIP reports whether p contains exactly one IP. 1314 | func (p IPPrefix) IsSingleIP() bool { return p.bits != 0 && p.bits == p.ip.BitLen() } 1315 | 1316 | // FromStdIPNet returns an IPPrefix from the standard library's IPNet type. 1317 | // If std is invalid, ok is false. 1318 | func FromStdIPNet(std *net.IPNet) (prefix IPPrefix, ok bool) { 1319 | ip, ok := FromStdIP(std.IP) 1320 | if !ok { 1321 | return IPPrefix{}, false 1322 | } 1323 | 1324 | if l := len(std.Mask); l != net.IPv4len && l != net.IPv6len { 1325 | // Invalid mask. 1326 | return IPPrefix{}, false 1327 | } 1328 | 1329 | ones, bits := std.Mask.Size() 1330 | if ones == 0 && bits == 0 { 1331 | // IPPrefix does not support non-contiguous masks. 1332 | return IPPrefix{}, false 1333 | } 1334 | 1335 | return IPPrefix{ 1336 | ip: ip, 1337 | bits: uint8(ones), 1338 | }, true 1339 | } 1340 | 1341 | // ParseIPPrefix parses s as an IP address prefix. 1342 | // The string can be in the form "192.168.1.0/24" or "2001::db8::/32", 1343 | // the CIDR notation defined in RFC 4632 and RFC 4291. 1344 | // 1345 | // Note that masked address bits are not zeroed. Use Masked for that. 1346 | func ParseIPPrefix(s string) (IPPrefix, error) { 1347 | i := strings.LastIndexByte(s, '/') 1348 | if i < 0 { 1349 | return IPPrefix{}, fmt.Errorf("netaddr.ParseIPPrefix(%q): no '/'", s) 1350 | } 1351 | ip, err := ParseIP(s[:i]) 1352 | if err != nil { 1353 | return IPPrefix{}, fmt.Errorf("netaddr.ParseIPPrefix(%q): %v", s, err) 1354 | } 1355 | s = s[i+1:] 1356 | bits, err := strconv.Atoi(s) 1357 | if err != nil { 1358 | return IPPrefix{}, fmt.Errorf("netaddr.ParseIPPrefix(%q): bad prefix: %v", s, err) 1359 | } 1360 | maxBits := 32 1361 | if ip.Is6() { 1362 | maxBits = 128 1363 | } 1364 | if bits < 0 || bits > maxBits { 1365 | return IPPrefix{}, fmt.Errorf("netaddr.ParseIPPrefix(%q): prefix length out of range", s) 1366 | } 1367 | return IPPrefixFrom(ip, uint8(bits)), nil 1368 | } 1369 | 1370 | // MustParseIPPrefix calls ParseIPPrefix(s) and panics on error. 1371 | // It is intended for use in tests with hard-coded strings. 1372 | func MustParseIPPrefix(s string) IPPrefix { 1373 | ip, err := ParseIPPrefix(s) 1374 | if err != nil { 1375 | panic(err) 1376 | } 1377 | return ip 1378 | } 1379 | 1380 | // Masked returns p in its canonical form, with bits of p.IP() not in p.Bits() masked off. 1381 | // If p is zero or otherwise invalid, Masked returns the zero value. 1382 | func (p IPPrefix) Masked() IPPrefix { 1383 | if m, err := p.ip.Prefix(p.bits); err == nil { 1384 | return m 1385 | } 1386 | return IPPrefix{} 1387 | } 1388 | 1389 | // Range returns the inclusive range of IPs that p covers. 1390 | // 1391 | // If p is zero or otherwise invalid, Range returns the zero value. 1392 | func (p IPPrefix) Range() IPRange { 1393 | p = p.Masked() 1394 | if p.IsZero() { 1395 | return IPRange{} 1396 | } 1397 | return IPRangeFrom(p.ip, p.lastIP()) 1398 | } 1399 | 1400 | // IPNet returns the net.IPNet representation of an IPPrefix. 1401 | // The returned value is always non-nil. 1402 | // Any zone identifier is dropped in the conversion. 1403 | func (p IPPrefix) IPNet() *net.IPNet { 1404 | if !p.IsValid() { 1405 | return &net.IPNet{} 1406 | } 1407 | stdIP, _ := p.ip.ipZone(nil) 1408 | return &net.IPNet{ 1409 | IP: stdIP, 1410 | Mask: net.CIDRMask(int(p.bits), int(p.ip.BitLen())), 1411 | } 1412 | } 1413 | 1414 | // Contains reports whether the network p includes ip. 1415 | // 1416 | // An IPv4 address will not match an IPv6 prefix. 1417 | // A v6-mapped IPv6 address will not match an IPv4 prefix. 1418 | // A zero-value IP will not match any prefix. 1419 | // If ip has an IPv6 zone, Contains returns false, 1420 | // because IPPrefixes strip zones. 1421 | func (p IPPrefix) Contains(ip IP) bool { 1422 | if !p.IsValid() || ip.hasZone() { 1423 | return false 1424 | } 1425 | if f1, f2 := p.ip.BitLen(), ip.BitLen(); f1 == 0 || f2 == 0 || f1 != f2 { 1426 | return false 1427 | } 1428 | if ip.Is4() { 1429 | // xor the IP addresses together; mismatched bits are now ones. 1430 | // Shift away the number of bits we don't care about. 1431 | // Shifts in Go are more efficient if the compiler can prove 1432 | // that the shift amount is smaller than the width of the shifted type (64 here). 1433 | // We know that p.bits is in the range 0..32 because p is Valid; 1434 | // the compiler doesn't know that, so mask with 63 to help it. 1435 | // Now truncate to 32 bits, because this is IPv4. 1436 | // If all the bits we care about are equal, the result will be zero. 1437 | return uint32((ip.addr.lo^p.ip.addr.lo)>>((32-p.bits)&63)) == 0 1438 | } else { 1439 | // xor the IP addresses together. 1440 | // Mask away the bits we don't care about. 1441 | // If all the bits we care about are equal, the result will be zero. 1442 | return ip.addr.xor(p.ip.addr).and(mask6[p.bits]).isZero() 1443 | } 1444 | } 1445 | 1446 | // Overlaps reports whether p and o overlap at all. 1447 | // 1448 | // If p and o are of different address families or either have a zero 1449 | // IP, it reports false. Like the Contains method, a prefix with a 1450 | // v6-mapped IPv4 IP is still treated as an IPv6 mask. 1451 | // 1452 | // If either has a Bits of zero, it returns true. 1453 | func (p IPPrefix) Overlaps(o IPPrefix) bool { 1454 | if !p.IsValid() || !o.IsValid() { 1455 | return false 1456 | } 1457 | if p == o { 1458 | return true 1459 | } 1460 | if p.ip.Is4() != o.ip.Is4() { 1461 | return false 1462 | } 1463 | var minBits uint8 1464 | if p.bits < o.bits { 1465 | minBits = p.bits 1466 | } else { 1467 | minBits = o.bits 1468 | } 1469 | if minBits == 0 { 1470 | return true 1471 | } 1472 | // One of these Prefix calls might look redundant, but we don't require 1473 | // that p and o values are normalized (via IPPrefix.Masked) first, 1474 | // so the Prefix call on the one that's already minBits serves to zero 1475 | // out any remaining bits in IP. 1476 | var err error 1477 | if p, err = p.ip.Prefix(minBits); err != nil { 1478 | return false 1479 | } 1480 | if o, err = o.ip.Prefix(minBits); err != nil { 1481 | return false 1482 | } 1483 | return p.ip == o.ip 1484 | } 1485 | 1486 | // AppendTo appends a text encoding of p, 1487 | // as generated by MarshalText, 1488 | // to b and returns the extended buffer. 1489 | func (p IPPrefix) AppendTo(b []byte) []byte { 1490 | if p.IsZero() { 1491 | return b 1492 | } 1493 | if !p.IsValid() { 1494 | return append(b, "invalid IPPrefix"...) 1495 | } 1496 | 1497 | // p.IP is non-zero, because p is valid. 1498 | if p.ip.z == z4 { 1499 | b = p.ip.appendTo4(b) 1500 | } else { 1501 | b = p.ip.appendTo6(b) 1502 | } 1503 | 1504 | b = append(b, '/') 1505 | b = appendDecimal(b, p.bits) 1506 | return b 1507 | } 1508 | 1509 | // MarshalText implements the encoding.TextMarshaler interface, 1510 | // The encoding is the same as returned by String, with one exception: 1511 | // If p is the zero value, the encoding is the empty string. 1512 | func (p IPPrefix) MarshalText() ([]byte, error) { 1513 | var max int 1514 | switch p.ip.z { 1515 | case z0: 1516 | case z4: 1517 | max = len("255.255.255.255/32") 1518 | default: 1519 | max = len("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff%enp5s0/128") 1520 | } 1521 | b := make([]byte, 0, max) 1522 | b = p.AppendTo(b) 1523 | return b, nil 1524 | } 1525 | 1526 | // UnmarshalText implements the encoding.TextUnmarshaler interface. 1527 | // The IP address is expected in a form accepted by ParseIPPrefix. 1528 | // It returns an error if *p is not the IPPrefix zero value. 1529 | func (p *IPPrefix) UnmarshalText(text []byte) error { 1530 | if *p != (IPPrefix{}) { 1531 | return errors.New("refusing to Unmarshal into non-zero IPPrefix") 1532 | } 1533 | if len(text) == 0 { 1534 | return nil 1535 | } 1536 | var err error 1537 | *p, err = ParseIPPrefix(string(text)) 1538 | return err 1539 | } 1540 | 1541 | // String returns the CIDR notation of p: "/". 1542 | func (p IPPrefix) String() string { 1543 | if p.IsZero() { 1544 | return "zero IPPrefix" 1545 | } 1546 | if !p.IsValid() { 1547 | return "invalid IPPrefix" 1548 | } 1549 | return fmt.Sprintf("%s/%d", p.ip, p.bits) 1550 | } 1551 | 1552 | // lastIP returns the last IP in the prefix. 1553 | func (p IPPrefix) lastIP() IP { 1554 | if !p.IsValid() { 1555 | return IP{} 1556 | } 1557 | a16 := p.ip.As16() 1558 | var off uint8 1559 | var bits uint8 = 128 1560 | if p.ip.Is4() { 1561 | off = 12 1562 | bits = 32 1563 | } 1564 | for b := p.bits; b < bits; b++ { 1565 | byteNum, bitInByte := b/8, 7-(b%8) 1566 | a16[off+byteNum] |= 1 << uint(bitInByte) 1567 | } 1568 | if p.ip.Is4() { 1569 | return IPFrom16(a16) 1570 | } else { 1571 | return IPv6Raw(a16) // doesn't unmap 1572 | } 1573 | } 1574 | 1575 | // IPRange represents an inclusive range of IP addresses 1576 | // from the same address family. 1577 | // 1578 | // The From() and To() IPs are inclusive bounds, both included in the 1579 | // range. 1580 | // 1581 | // To be valid, the From() and To() values must be non-zero, have matching 1582 | // address families (IPv4 vs IPv6), and From() must be less than or equal to To(). 1583 | // IPv6 zones are stripped out and ignored. 1584 | // An invalid range may be ignored. 1585 | type IPRange struct { 1586 | // from is the initial IP address in the range. 1587 | from IP 1588 | 1589 | // to is the final IP address in the range. 1590 | to IP 1591 | } 1592 | 1593 | // IPRangeFrom returns an IPRange from from to to. 1594 | // It does not allocate. 1595 | func IPRangeFrom(from, to IP) IPRange { 1596 | return IPRange{ 1597 | from: from.withoutZone(), 1598 | to: to.withoutZone(), 1599 | } 1600 | } 1601 | 1602 | // From returns the lower bound of r. 1603 | func (r IPRange) From() IP { return r.from } 1604 | 1605 | // To returns the upper bound of r. 1606 | func (r IPRange) To() IP { return r.to } 1607 | 1608 | // ParseIPRange parses a range out of two IPs separated by a hyphen. 1609 | // 1610 | // It returns an error if the range is not valid. 1611 | func ParseIPRange(s string) (IPRange, error) { 1612 | var r IPRange 1613 | h := strings.IndexByte(s, '-') 1614 | if h == -1 { 1615 | return r, fmt.Errorf("no hyphen in range %q", s) 1616 | } 1617 | from, to := s[:h], s[h+1:] 1618 | var err error 1619 | r.from, err = ParseIP(from) 1620 | if err != nil { 1621 | return r, fmt.Errorf("invalid From IP %q in range %q", from, s) 1622 | } 1623 | r.from = r.from.withoutZone() 1624 | r.to, err = ParseIP(to) 1625 | if err != nil { 1626 | return r, fmt.Errorf("invalid To IP %q in range %q", to, s) 1627 | } 1628 | r.to = r.to.withoutZone() 1629 | if !r.IsValid() { 1630 | return r, fmt.Errorf("range %v to %v not valid", r.from, r.to) 1631 | } 1632 | return r, nil 1633 | } 1634 | 1635 | // MustParseIPRange calls ParseIPRange(s) and panics on error. 1636 | // It is intended for use in tests with hard-coded strings. 1637 | func MustParseIPRange(s string) IPRange { 1638 | r, err := ParseIPRange(s) 1639 | if err != nil { 1640 | panic(err) 1641 | } 1642 | return r 1643 | } 1644 | 1645 | // String returns a string representation of the range. 1646 | // 1647 | // For a valid range, the form is "From-To" with a single hyphen 1648 | // separating the IPs, the same format recognized by 1649 | // ParseIPRange. 1650 | func (r IPRange) String() string { 1651 | if r.IsValid() { 1652 | return fmt.Sprintf("%s-%s", r.from, r.to) 1653 | } 1654 | if r.from.IsZero() || r.to.IsZero() { 1655 | return "zero IPRange" 1656 | } 1657 | return "invalid IPRange" 1658 | } 1659 | 1660 | // AppendTo appends a text encoding of r, 1661 | // as generated by MarshalText, 1662 | // to b and returns the extended buffer. 1663 | func (r IPRange) AppendTo(b []byte) []byte { 1664 | if r.IsZero() { 1665 | return b 1666 | } 1667 | b = r.from.AppendTo(b) 1668 | b = append(b, '-') 1669 | b = r.to.AppendTo(b) 1670 | return b 1671 | } 1672 | 1673 | // MarshalText implements the encoding.TextMarshaler interface, 1674 | // The encoding is the same as returned by String, with one exception: 1675 | // If ip is the zero value, the encoding is the empty string. 1676 | func (r IPRange) MarshalText() ([]byte, error) { 1677 | if r.IsZero() { 1678 | return []byte(""), nil 1679 | } 1680 | var max int 1681 | if r.from.z == z4 { 1682 | max = len("255.255.255.255-255.255.255.255") 1683 | } else { 1684 | max = len("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff-ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff") 1685 | } 1686 | b := make([]byte, 0, max) 1687 | return r.AppendTo(b), nil 1688 | } 1689 | 1690 | // UnmarshalText implements the encoding.TextUnmarshaler interface. 1691 | // The IP range is expected in a form accepted by ParseIPRange. 1692 | // It returns an error if *r is not the IPRange zero value. 1693 | func (r *IPRange) UnmarshalText(text []byte) error { 1694 | if *r != (IPRange{}) { 1695 | return errors.New("refusing to Unmarshal into non-zero IPRange") 1696 | } 1697 | if len(text) == 0 { 1698 | return nil 1699 | } 1700 | var err error 1701 | *r, err = ParseIPRange(string(text)) 1702 | return err 1703 | } 1704 | 1705 | // IsZero reports whether r is the zero value of the IPRange type. 1706 | func (r IPRange) IsZero() bool { 1707 | return r == IPRange{} 1708 | } 1709 | 1710 | // IsValid reports whether r.From() and r.To() are both non-zero and 1711 | // obey the documented requirements: address families match, and From 1712 | // is less than or equal to To. 1713 | func (r IPRange) IsValid() bool { 1714 | return !r.from.IsZero() && 1715 | r.from.z == r.to.z && 1716 | !r.to.Less(r.from) 1717 | } 1718 | 1719 | // Valid reports whether r.From() and r.To() are both non-zero and 1720 | // obey the documented requirements: address families match, and From 1721 | // is less than or equal to To. 1722 | // 1723 | // Deprecated: use the correctly named and identical IsValid method instead. 1724 | func (r IPRange) Valid() bool { return r.IsValid() } 1725 | 1726 | // Contains reports whether the range r includes addr. 1727 | // 1728 | // An invalid range always reports false. 1729 | // 1730 | // If ip has an IPv6 zone, Contains returns false, 1731 | // because IPPrefixes strip zones. 1732 | func (r IPRange) Contains(addr IP) bool { 1733 | return r.IsValid() && !addr.hasZone() && r.contains(addr) 1734 | } 1735 | 1736 | // contains is like Contains, but without the validity check. 1737 | // addr must not have a zone. 1738 | func (r IPRange) contains(addr IP) bool { 1739 | return r.from.Compare(addr) <= 0 && r.to.Compare(addr) >= 0 1740 | } 1741 | 1742 | // less reports whether r is "before" other. It is before if r.From() 1743 | // is before other.From(). If they're equal, then the larger range 1744 | // (higher To()) comes first. 1745 | func (r IPRange) less(other IPRange) bool { 1746 | if cmp := r.from.Compare(other.from); cmp != 0 { 1747 | return cmp < 0 1748 | } 1749 | return other.to.Less(r.to) 1750 | } 1751 | 1752 | // entirelyBefore returns whether r lies entirely before other in IP 1753 | // space. 1754 | func (r IPRange) entirelyBefore(other IPRange) bool { 1755 | return r.to.Less(other.from) 1756 | } 1757 | 1758 | // entirelyWithin returns whether r is entirely contained within 1759 | // other. 1760 | func (r IPRange) coveredBy(other IPRange) bool { 1761 | return other.from.lessOrEq(r.from) && r.to.lessOrEq(other.to) 1762 | } 1763 | 1764 | // inMiddleOf returns whether r is inside other, but not touching the 1765 | // edges of other. 1766 | func (r IPRange) inMiddleOf(other IPRange) bool { 1767 | return other.from.Less(r.from) && r.to.Less(other.to) 1768 | } 1769 | 1770 | // overlapsStartOf returns whether r entirely overlaps the start of 1771 | // other, but not all of other. 1772 | func (r IPRange) overlapsStartOf(other IPRange) bool { 1773 | return r.from.lessOrEq(other.from) && r.to.Less(other.to) 1774 | } 1775 | 1776 | // overlapsEndOf returns whether r entirely overlaps the end of 1777 | // other, but not all of other. 1778 | func (r IPRange) overlapsEndOf(other IPRange) bool { 1779 | return other.from.Less(r.from) && other.to.lessOrEq(r.to) 1780 | } 1781 | 1782 | // mergeIPRanges returns the minimum and sorted set of IP ranges that 1783 | // cover r. 1784 | func mergeIPRanges(rr []IPRange) (out []IPRange, valid bool) { 1785 | // Always return a copy of r, to avoid aliasing slice memory in 1786 | // the caller. 1787 | switch len(rr) { 1788 | case 0: 1789 | return nil, true 1790 | case 1: 1791 | return []IPRange{rr[0]}, true 1792 | } 1793 | 1794 | sort.Slice(rr, func(i, j int) bool { return rr[i].less(rr[j]) }) 1795 | out = make([]IPRange, 1, len(rr)) 1796 | out[0] = rr[0] 1797 | for _, r := range rr[1:] { 1798 | prev := &out[len(out)-1] 1799 | switch { 1800 | case !r.IsValid(): 1801 | // Invalid ranges make no sense to merge, refuse to 1802 | // perform. 1803 | return nil, false 1804 | case prev.to.Next() == r.from: 1805 | // prev and r touch, merge them. 1806 | // 1807 | // prev r 1808 | // f------tf-----t 1809 | prev.to = r.to 1810 | case prev.to.Less(r.from): 1811 | // No overlap and not adjacent (per previous case), no 1812 | // merging possible. 1813 | // 1814 | // prev r 1815 | // f------t f-----t 1816 | out = append(out, r) 1817 | case prev.to.Less(r.to): 1818 | // Partial overlap, update prev 1819 | // 1820 | // prev 1821 | // f------t 1822 | // f-----t 1823 | // r 1824 | prev.to = r.to 1825 | default: 1826 | // r entirely contained in prev, nothing to do. 1827 | // 1828 | // prev 1829 | // f--------t 1830 | // f-----t 1831 | // r 1832 | } 1833 | } 1834 | return out, true 1835 | } 1836 | 1837 | // Overlaps reports whether p and o overlap at all. 1838 | // 1839 | // If p and o are of different address families or either are invalid, 1840 | // it reports false. 1841 | func (r IPRange) Overlaps(o IPRange) bool { 1842 | return r.IsValid() && 1843 | o.IsValid() && 1844 | r.from.Compare(o.to) <= 0 && 1845 | o.from.Compare(r.to) <= 0 1846 | } 1847 | 1848 | // prefixMaker returns a address-family-corrected IPPrefix from a and bits, 1849 | // where the input bits is always in the IPv6-mapped form for IPv4 addresses. 1850 | type prefixMaker func(a uint128, bits uint8) IPPrefix 1851 | 1852 | // Prefixes returns the set of IPPrefix entries that covers r. 1853 | // 1854 | // If either of r's bounds are invalid, in the wrong order, or if 1855 | // they're of different address families, then Prefixes returns nil. 1856 | // 1857 | // Prefixes necessarily allocates. See AppendPrefixes for a version that uses 1858 | // memory you provide. 1859 | func (r IPRange) Prefixes() []IPPrefix { 1860 | return r.AppendPrefixes(nil) 1861 | } 1862 | 1863 | // AppendPrefixes is an append version of IPRange.Prefixes. It appends 1864 | // the IPPrefix entries that cover r to dst. 1865 | func (r IPRange) AppendPrefixes(dst []IPPrefix) []IPPrefix { 1866 | if !r.IsValid() { 1867 | return nil 1868 | } 1869 | return appendRangePrefixes(dst, r.prefixFrom128AndBits, r.from.addr, r.to.addr) 1870 | } 1871 | 1872 | func (r IPRange) prefixFrom128AndBits(a uint128, bits uint8) IPPrefix { 1873 | ip := IP{addr: a, z: r.from.z} 1874 | if r.from.Is4() { 1875 | bits -= 12 * 8 1876 | } 1877 | return IPPrefix{ip, bits} 1878 | } 1879 | 1880 | // aZeroBSet is whether, after the common bits, a is all zero bits and 1881 | // b is all set (one) bits. 1882 | func comparePrefixes(a, b uint128) (common uint8, aZeroBSet bool) { 1883 | common = a.commonPrefixLen(b) 1884 | 1885 | // See whether a and b, after their common shared bits, end 1886 | // in all zero bits or all one bits, respectively. 1887 | if common == 128 { 1888 | return common, true 1889 | } 1890 | 1891 | m := mask6[common] 1892 | return common, (a.xor(a.and(m)).isZero() && 1893 | b.or(m) == uint128{^uint64(0), ^uint64(0)}) 1894 | } 1895 | 1896 | // Prefix returns r as an IPPrefix, if it can be presented exactly as such. 1897 | // If r is not valid or is not exactly equal to one prefix, ok is false. 1898 | func (r IPRange) Prefix() (p IPPrefix, ok bool) { 1899 | if !r.IsValid() { 1900 | return 1901 | } 1902 | if common, ok := comparePrefixes(r.from.addr, r.to.addr); ok { 1903 | return r.prefixFrom128AndBits(r.from.addr, common), true 1904 | } 1905 | return 1906 | } 1907 | 1908 | func appendRangePrefixes(dst []IPPrefix, makePrefix prefixMaker, a, b uint128) []IPPrefix { 1909 | common, ok := comparePrefixes(a, b) 1910 | if ok { 1911 | // a to b represents a whole range, like 10.50.0.0/16. 1912 | // (a being 10.50.0.0 and b being 10.50.255.255) 1913 | return append(dst, makePrefix(a, common)) 1914 | } 1915 | // Otherwise recursively do both halves. 1916 | dst = appendRangePrefixes(dst, makePrefix, a, a.bitsSetFrom(common+1)) 1917 | dst = appendRangePrefixes(dst, makePrefix, b.bitsClearedFrom(common+1), b) 1918 | return dst 1919 | } 1920 | -------------------------------------------------------------------------------- /slow_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2020 The Inet.Af AUTHORS. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package netaddr 6 | 7 | import ( 8 | "fmt" 9 | "strconv" 10 | "strings" 11 | ) 12 | 13 | // zeros is a slice of eight stringified zeros. It's used in 14 | // parseIPSlow to construct slices of specific amounts of zero fields, 15 | // from 1 to 8. 16 | var zeros = []string{"0", "0", "0", "0", "0", "0", "0", "0"} 17 | 18 | // parseIPSlow is like ParseIP, but aims for readability above 19 | // speed. It's the reference implementation for correctness checking 20 | // and against which we measure optimized parsers. 21 | // 22 | // parseIPSlow understands the following forms of IP addresses: 23 | // - Regular IPv4: 1.2.3.4 24 | // - IPv4 with many leading zeros: 0000001.0000002.0000003.0000004 25 | // - Regular IPv6: 1111:2222:3333:4444:5555:6666:7777:8888 26 | // - IPv6 with many leading zeros: 00000001:0000002:0000003:0000004:0000005:0000006:0000007:0000008 27 | // - IPv6 with zero blocks elided: 1111:2222::7777:8888 28 | // - IPv6 with trailing 32 bits expressed as IPv4: 1111:2222:3333:4444:5555:6666:77.77.88.88 29 | // 30 | // It does not process the following IP address forms, which have been 31 | // varyingly accepted by some programs due to an under-specification 32 | // of the shapes of IPv4 addresses: 33 | // 34 | // - IPv4 as a single 32-bit uint: 4660 (same as "1.2.3.4") 35 | // - IPv4 with octal numbers: 0300.0250.0.01 (same as "192.168.0.1") 36 | // - IPv4 with hex numbers: 0xc0.0xa8.0x0.0x1 (same as "192.168.0.1") 37 | // - IPv4 in "class-B style": 1.2.52 (same as "1.2.3.4") 38 | // - IPv4 in "class-A style": 1.564 (same as "1.2.3.4") 39 | func parseIPSlow(s string) (IP, error) { 40 | // Identify and strip out the zone, if any. There should be 0 or 1 41 | // '%' in the string. 42 | var zone string 43 | fs := strings.Split(s, "%") 44 | switch len(fs) { 45 | case 1: 46 | // No zone, that's fine. 47 | case 2: 48 | s, zone = fs[0], fs[1] 49 | if zone == "" { 50 | return IP{}, fmt.Errorf("netaddr.ParseIP(%q): no zone after zone specifier", s) 51 | } 52 | default: 53 | return IP{}, fmt.Errorf("netaddr.ParseIP(%q): too many zone specifiers", s) // TODO: less specific? 54 | } 55 | 56 | // IPv4 by itself is easy to do in a helper. 57 | if strings.Count(s, ":") == 0 { 58 | if zone != "" { 59 | return IP{}, fmt.Errorf("netaddr.ParseIP(%q): IPv4 addresses cannot have a zone", s) 60 | } 61 | return parseIPv4Slow(s) 62 | } 63 | 64 | normal, err := normalizeIPv6Slow(s) 65 | if err != nil { 66 | return IP{}, err 67 | } 68 | 69 | // At this point, we've normalized the address back into 8 hex 70 | // fields of 16 bits each. Parse that. 71 | fs = strings.Split(normal, ":") 72 | if len(fs) != 8 { 73 | return IP{}, fmt.Errorf("netaddr.ParseIP(%q): wrong size address", s) 74 | } 75 | var ret [16]byte 76 | for i, f := range fs { 77 | a, b, err := parseWord(f) 78 | if err != nil { 79 | return IP{}, err 80 | } 81 | ret[i*2] = a 82 | ret[i*2+1] = b 83 | } 84 | 85 | return IPv6Raw(ret).WithZone(zone), nil 86 | } 87 | 88 | // normalizeIPv6Slow expands s, which is assumed to be an IPv6 89 | // address, to its canonical text form. 90 | // 91 | // The canonical form of an IPv6 address is 8 colon-separated fields, 92 | // where each field should be a hex value from 0 to ffff. This 93 | // function does not verify the contents of each field. 94 | // 95 | // This function performs two transformations: 96 | // - The last 32 bits of an IPv6 address may be represented in 97 | // IPv4-style dotted quad form, as in 1:2:3:4:5:6:7.8.9.10. That 98 | // address is transformed to its hex equivalent, 99 | // e.g. 1:2:3:4:5:6:708:90a. 100 | // - An address may contain one "::", which expands into as many 101 | // 16-bit blocks of zeros as needed to make the address its correct 102 | // full size. For example, fe80::1:2 expands to fe80:0:0:0:0:0:1:2. 103 | // 104 | // Both short forms may be present in a single address, 105 | // e.g. fe80::1.2.3.4. 106 | func normalizeIPv6Slow(orig string) (string, error) { 107 | s := orig 108 | 109 | // Find and convert an IPv4 address in the final field, if any. 110 | i := strings.LastIndex(s, ":") 111 | if i == -1 { 112 | return "", fmt.Errorf("netaddr.ParseIP(%q): invalid IP address", orig) 113 | } 114 | if strings.Contains(s[i+1:], ".") { 115 | ip, err := parseIPv4Slow(s[i+1:]) 116 | if err != nil { 117 | return "", err 118 | } 119 | s = fmt.Sprintf("%s:%02x%02x:%02x%02x", s[:i], ip.v4(0), ip.v4(1), ip.v4(2), ip.v4(3)) 120 | } 121 | 122 | // Find and expand a ::, if any. 123 | fs := strings.Split(s, "::") 124 | switch len(fs) { 125 | case 1: 126 | // No ::, nothing to do. 127 | case 2: 128 | lhs, rhs := fs[0], fs[1] 129 | // Found a ::, figure out how many zero blocks need to be 130 | // inserted. 131 | nblocks := strings.Count(lhs, ":") + strings.Count(rhs, ":") 132 | if lhs != "" { 133 | nblocks++ 134 | } 135 | if rhs != "" { 136 | nblocks++ 137 | } 138 | if nblocks > 7 { 139 | return "", fmt.Errorf("netaddr.ParseIP(%q): address too long", orig) 140 | } 141 | fs = nil 142 | // Either side of the :: can be empty. We don't want empty 143 | // fields to feature in the final normalized address. 144 | if lhs != "" { 145 | fs = append(fs, lhs) 146 | } 147 | fs = append(fs, zeros[:8-nblocks]...) 148 | if rhs != "" { 149 | fs = append(fs, rhs) 150 | } 151 | s = strings.Join(fs, ":") 152 | default: 153 | // Too many :: 154 | return "", fmt.Errorf("netaddr.ParseIP(%q): invalid IP address", orig) 155 | } 156 | 157 | return s, nil 158 | } 159 | 160 | // parseIPv4Slow parses and returns an IPv4 address in dotted quad 161 | // form, e.g. "192.168.0.1". It is slow but easy to read, and the 162 | // reference implementation against which we compare faster 163 | // implementations for correctness. 164 | func parseIPv4Slow(s string) (IP, error) { 165 | fs := strings.Split(s, ".") 166 | if len(fs) != 4 { 167 | return IP{}, fmt.Errorf("netaddr.ParseIP(%q): invalid IP address", s) 168 | } 169 | var ret [4]byte 170 | for i := range ret { 171 | val, err := strconv.ParseUint(fs[i], 10, 8) 172 | if err != nil { 173 | return IP{}, err 174 | } 175 | ret[i] = uint8(val) 176 | } 177 | return IPv4(ret[0], ret[1], ret[2], ret[3]), nil 178 | } 179 | 180 | // parseWord converts a 16-bit hex string into its corresponding 181 | // two-byte value. 182 | func parseWord(s string) (byte, byte, error) { 183 | ret, err := strconv.ParseUint(s, 16, 16) 184 | if err != nil { 185 | return 0, 0, err 186 | } 187 | return uint8(ret >> 8), uint8(ret), nil 188 | } 189 | -------------------------------------------------------------------------------- /stackerr_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2021 The Inet.Af AUTHORS. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package netaddr_test 6 | 7 | import ( 8 | "strings" 9 | "testing" 10 | 11 | "inet.af/netaddr" 12 | ) 13 | 14 | // The tests for stacktrace errors is in its own file, 15 | // so that the line number munging that we do doesn't 16 | // break line numbers for other tests. 17 | 18 | func TestStacktraceErr(t *testing.T) { 19 | b := new(netaddr.IPSetBuilder) 20 | //line ipp.go:1 21 | b.AddPrefix(netaddr.IPPrefixFrom(netaddr.IPv4(1, 2, 3, 4), 33)) 22 | //line r.go:2 23 | b.AddRange(netaddr.IPRange{}) 24 | _, err := b.IPSet() 25 | got := err.Error() 26 | for _, want := range []string{"ipp.go:1", "r.go:2", "33"} { 27 | if !strings.Contains(got, want) { 28 | t.Errorf("error should contain %q, got %q", want, got) 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /tools/tools.go: -------------------------------------------------------------------------------- 1 | //go:build tools 2 | // +build tools 3 | 4 | package tools 5 | 6 | import ( 7 | _ "github.com/dvyukov/go-fuzz/go-fuzz-build" 8 | ) 9 | -------------------------------------------------------------------------------- /uint128.go: -------------------------------------------------------------------------------- 1 | // Copyright 2020 The Inet.Af AUTHORS. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package netaddr 6 | 7 | import "math/bits" 8 | 9 | // uint128 represents a uint128 using two uint64s. 10 | // 11 | // When the methods below mention a bit number, bit 0 is the most 12 | // significant bit (in hi) and bit 127 is the lowest (lo&1). 13 | type uint128 struct { 14 | hi uint64 15 | lo uint64 16 | } 17 | 18 | // isZero reports whether u == 0. 19 | // 20 | // It's faster than u == (uint128{}) because the compiler (as of Go 21 | // 1.15/1.16b1) doesn't do this trick and instead inserts a branch in 22 | // its eq alg's generated code. 23 | func (u uint128) isZero() bool { return u.hi|u.lo == 0 } 24 | 25 | // and returns the bitwise AND of u and m (u&m). 26 | func (u uint128) and(m uint128) uint128 { 27 | return uint128{u.hi & m.hi, u.lo & m.lo} 28 | } 29 | 30 | // xor returns the bitwise XOR of u and m (u^m). 31 | func (u uint128) xor(m uint128) uint128 { 32 | return uint128{u.hi ^ m.hi, u.lo ^ m.lo} 33 | } 34 | 35 | // or returns the bitwise OR of u and m (u|m). 36 | func (u uint128) or(m uint128) uint128 { 37 | return uint128{u.hi | m.hi, u.lo | m.lo} 38 | } 39 | 40 | // not returns the bitwise NOT of u. 41 | func (u uint128) not() uint128 { 42 | return uint128{^u.hi, ^u.lo} 43 | } 44 | 45 | // subOne returns u - 1. 46 | func (u uint128) subOne() uint128 { 47 | lo, borrow := bits.Sub64(u.lo, 1, 0) 48 | return uint128{u.hi - borrow, lo} 49 | } 50 | 51 | // addOne returns u + 1. 52 | func (u uint128) addOne() uint128 { 53 | lo, carry := bits.Add64(u.lo, 1, 0) 54 | return uint128{u.hi + carry, lo} 55 | } 56 | 57 | func u64CommonPrefixLen(a, b uint64) uint8 { 58 | return uint8(bits.LeadingZeros64(a ^ b)) 59 | } 60 | 61 | func (u uint128) commonPrefixLen(v uint128) (n uint8) { 62 | if n = u64CommonPrefixLen(u.hi, v.hi); n == 64 { 63 | n += u64CommonPrefixLen(u.lo, v.lo) 64 | } 65 | return 66 | } 67 | 68 | func (u *uint128) halves() [2]*uint64 { 69 | return [2]*uint64{&u.hi, &u.lo} 70 | } 71 | 72 | // bitsSetFrom returns a copy of u with the given bit 73 | // and all subsequent ones set. 74 | func (u uint128) bitsSetFrom(bit uint8) uint128 { 75 | return u.or(mask6[bit].not()) 76 | } 77 | 78 | // bitsClearedFrom returns a copy of u with the given bit 79 | // and all subsequent ones cleared. 80 | func (u uint128) bitsClearedFrom(bit uint8) uint128 { 81 | return u.and(mask6[bit]) 82 | } 83 | -------------------------------------------------------------------------------- /uint128_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2020 The Inet.Af AUTHORS. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package netaddr 6 | 7 | import ( 8 | "testing" 9 | ) 10 | 11 | func TestUint128AddSub(t *testing.T) { 12 | const add1 = 1 13 | const sub1 = -1 14 | tests := []struct { 15 | in uint128 16 | op int // +1 or -1 to add vs subtract 17 | want uint128 18 | }{ 19 | {uint128{0, 0}, add1, uint128{0, 1}}, 20 | {uint128{0, 1}, add1, uint128{0, 2}}, 21 | {uint128{1, 0}, add1, uint128{1, 1}}, 22 | {uint128{0, ^uint64(0)}, add1, uint128{1, 0}}, 23 | {uint128{^uint64(0), ^uint64(0)}, add1, uint128{0, 0}}, 24 | 25 | {uint128{0, 0}, sub1, uint128{^uint64(0), ^uint64(0)}}, 26 | {uint128{0, 1}, sub1, uint128{0, 0}}, 27 | {uint128{0, 2}, sub1, uint128{0, 1}}, 28 | {uint128{1, 0}, sub1, uint128{0, ^uint64(0)}}, 29 | {uint128{1, 1}, sub1, uint128{1, 0}}, 30 | } 31 | for _, tt := range tests { 32 | var got uint128 33 | switch tt.op { 34 | case add1: 35 | got = tt.in.addOne() 36 | case sub1: 37 | got = tt.in.subOne() 38 | default: 39 | panic("bogus op") 40 | } 41 | if got != tt.want { 42 | t.Errorf("%v add %d = %v; want %v", tt.in, tt.op, got, tt.want) 43 | } 44 | } 45 | } 46 | 47 | func TestBitsSetFrom(t *testing.T) { 48 | tests := []struct { 49 | bit uint8 50 | want uint128 51 | }{ 52 | {0, uint128{^uint64(0), ^uint64(0)}}, 53 | {1, uint128{^uint64(0) >> 1, ^uint64(0)}}, 54 | {63, uint128{1, ^uint64(0)}}, 55 | {64, uint128{0, ^uint64(0)}}, 56 | {65, uint128{0, ^uint64(0) >> 1}}, 57 | {127, uint128{0, 1}}, 58 | {128, uint128{0, 0}}, 59 | } 60 | for _, tt := range tests { 61 | var zero uint128 62 | got := zero.bitsSetFrom(tt.bit) 63 | if got != tt.want { 64 | t.Errorf("0.bitsSetFrom(%d) = %064b want %064b", tt.bit, got, tt.want) 65 | } 66 | } 67 | } 68 | 69 | func TestBitsClearedFrom(t *testing.T) { 70 | tests := []struct { 71 | bit uint8 72 | want uint128 73 | }{ 74 | {0, uint128{0, 0}}, 75 | {1, uint128{1 << 63, 0}}, 76 | {63, uint128{^uint64(0) &^ 1, 0}}, 77 | {64, uint128{^uint64(0), 0}}, 78 | {65, uint128{^uint64(0), 1 << 63}}, 79 | {127, uint128{^uint64(0), ^uint64(0) &^ 1}}, 80 | {128, uint128{^uint64(0), ^uint64(0)}}, 81 | } 82 | for _, tt := range tests { 83 | ones := uint128{^uint64(0), ^uint64(0)} 84 | got := ones.bitsClearedFrom(tt.bit) 85 | if got != tt.want { 86 | t.Errorf("ones.bitsClearedFrom(%d) = %064b want %064b", tt.bit, got, tt.want) 87 | } 88 | } 89 | } 90 | --------------------------------------------------------------------------------