mirror of
https://github.com/moby/moby.git
synced 2022-11-09 12:21:53 -05:00
Vendoring memberlist tag 0.1.0
Signed-off-by: Flavio Crisciani <flavio.crisciani@docker.com>
This commit is contained in:
parent
cfae6278be
commit
01fd4e49c3
37 changed files with 6187 additions and 551 deletions
|
@ -29,7 +29,9 @@ github.com/docker/go-events 18b43f1bc85d9cdd42c05a6cd2d444c7a200a894
|
||||||
github.com/armon/go-radix e39d623f12e8e41c7b5529e9a9dd67a1e2261f80
|
github.com/armon/go-radix e39d623f12e8e41c7b5529e9a9dd67a1e2261f80
|
||||||
github.com/armon/go-metrics eb0af217e5e9747e41dd5303755356b62d28e3ec
|
github.com/armon/go-metrics eb0af217e5e9747e41dd5303755356b62d28e3ec
|
||||||
github.com/hashicorp/go-msgpack 71c2886f5a673a35f909803f38ece5810165097b
|
github.com/hashicorp/go-msgpack 71c2886f5a673a35f909803f38ece5810165097b
|
||||||
github.com/hashicorp/memberlist 88ac4de0d1a0ca6def284b571342db3b777a4c37
|
github.com/hashicorp/memberlist v0.1.0
|
||||||
|
github.com/sean-/seed e2103e2c35297fb7e17febb81e49b312087a2372
|
||||||
|
github.com/hashicorp/go-sockaddr acd314c5781ea706c710d9ea70069fd2e110d61d
|
||||||
github.com/hashicorp/go-multierror fcdddc395df1ddf4247c69bd436e84cfa0733f7e
|
github.com/hashicorp/go-multierror fcdddc395df1ddf4247c69bd436e84cfa0733f7e
|
||||||
github.com/hashicorp/serf 598c54895cc5a7b1a24a398d635e8c0ea0959870
|
github.com/hashicorp/serf 598c54895cc5a7b1a24a398d635e8c0ea0959870
|
||||||
github.com/docker/libkv 1d8431073ae03cdaedb198a89722f3aab6d418ef
|
github.com/docker/libkv 1d8431073ae03cdaedb198a89722f3aab6d418ef
|
||||||
|
|
373
vendor/github.com/hashicorp/go-sockaddr/LICENSE
generated
vendored
Normal file
373
vendor/github.com/hashicorp/go-sockaddr/LICENSE
generated
vendored
Normal file
|
@ -0,0 +1,373 @@
|
||||||
|
Mozilla Public License Version 2.0
|
||||||
|
==================================
|
||||||
|
|
||||||
|
1. Definitions
|
||||||
|
--------------
|
||||||
|
|
||||||
|
1.1. "Contributor"
|
||||||
|
means each individual or legal entity that creates, contributes to
|
||||||
|
the creation of, or owns Covered Software.
|
||||||
|
|
||||||
|
1.2. "Contributor Version"
|
||||||
|
means the combination of the Contributions of others (if any) used
|
||||||
|
by a Contributor and that particular Contributor's Contribution.
|
||||||
|
|
||||||
|
1.3. "Contribution"
|
||||||
|
means Covered Software of a particular Contributor.
|
||||||
|
|
||||||
|
1.4. "Covered Software"
|
||||||
|
means Source Code Form to which the initial Contributor has attached
|
||||||
|
the notice in Exhibit A, the Executable Form of such Source Code
|
||||||
|
Form, and Modifications of such Source Code Form, in each case
|
||||||
|
including portions thereof.
|
||||||
|
|
||||||
|
1.5. "Incompatible With Secondary Licenses"
|
||||||
|
means
|
||||||
|
|
||||||
|
(a) that the initial Contributor has attached the notice described
|
||||||
|
in Exhibit B to the Covered Software; or
|
||||||
|
|
||||||
|
(b) that the Covered Software was made available under the terms of
|
||||||
|
version 1.1 or earlier of the License, but not also under the
|
||||||
|
terms of a Secondary License.
|
||||||
|
|
||||||
|
1.6. "Executable Form"
|
||||||
|
means any form of the work other than Source Code Form.
|
||||||
|
|
||||||
|
1.7. "Larger Work"
|
||||||
|
means a work that combines Covered Software with other material, in
|
||||||
|
a separate file or files, that is not Covered Software.
|
||||||
|
|
||||||
|
1.8. "License"
|
||||||
|
means this document.
|
||||||
|
|
||||||
|
1.9. "Licensable"
|
||||||
|
means having the right to grant, to the maximum extent possible,
|
||||||
|
whether at the time of the initial grant or subsequently, any and
|
||||||
|
all of the rights conveyed by this License.
|
||||||
|
|
||||||
|
1.10. "Modifications"
|
||||||
|
means any of the following:
|
||||||
|
|
||||||
|
(a) any file in Source Code Form that results from an addition to,
|
||||||
|
deletion from, or modification of the contents of Covered
|
||||||
|
Software; or
|
||||||
|
|
||||||
|
(b) any new file in Source Code Form that contains any Covered
|
||||||
|
Software.
|
||||||
|
|
||||||
|
1.11. "Patent Claims" of a Contributor
|
||||||
|
means any patent claim(s), including without limitation, method,
|
||||||
|
process, and apparatus claims, in any patent Licensable by such
|
||||||
|
Contributor that would be infringed, but for the grant of the
|
||||||
|
License, by the making, using, selling, offering for sale, having
|
||||||
|
made, import, or transfer of either its Contributions or its
|
||||||
|
Contributor Version.
|
||||||
|
|
||||||
|
1.12. "Secondary License"
|
||||||
|
means either the GNU General Public License, Version 2.0, the GNU
|
||||||
|
Lesser General Public License, Version 2.1, the GNU Affero General
|
||||||
|
Public License, Version 3.0, or any later versions of those
|
||||||
|
licenses.
|
||||||
|
|
||||||
|
1.13. "Source Code Form"
|
||||||
|
means the form of the work preferred for making modifications.
|
||||||
|
|
||||||
|
1.14. "You" (or "Your")
|
||||||
|
means an individual or a legal entity exercising rights under this
|
||||||
|
License. For legal entities, "You" includes any entity that
|
||||||
|
controls, is controlled by, or is under common control with You. For
|
||||||
|
purposes of this definition, "control" means (a) the power, direct
|
||||||
|
or indirect, to cause the direction or management of such entity,
|
||||||
|
whether by contract or otherwise, or (b) ownership of more than
|
||||||
|
fifty percent (50%) of the outstanding shares or beneficial
|
||||||
|
ownership of such entity.
|
||||||
|
|
||||||
|
2. License Grants and Conditions
|
||||||
|
--------------------------------
|
||||||
|
|
||||||
|
2.1. Grants
|
||||||
|
|
||||||
|
Each Contributor hereby grants You a world-wide, royalty-free,
|
||||||
|
non-exclusive license:
|
||||||
|
|
||||||
|
(a) under intellectual property rights (other than patent or trademark)
|
||||||
|
Licensable by such Contributor to use, reproduce, make available,
|
||||||
|
modify, display, perform, distribute, and otherwise exploit its
|
||||||
|
Contributions, either on an unmodified basis, with Modifications, or
|
||||||
|
as part of a Larger Work; and
|
||||||
|
|
||||||
|
(b) under Patent Claims of such Contributor to make, use, sell, offer
|
||||||
|
for sale, have made, import, and otherwise transfer either its
|
||||||
|
Contributions or its Contributor Version.
|
||||||
|
|
||||||
|
2.2. Effective Date
|
||||||
|
|
||||||
|
The licenses granted in Section 2.1 with respect to any Contribution
|
||||||
|
become effective for each Contribution on the date the Contributor first
|
||||||
|
distributes such Contribution.
|
||||||
|
|
||||||
|
2.3. Limitations on Grant Scope
|
||||||
|
|
||||||
|
The licenses granted in this Section 2 are the only rights granted under
|
||||||
|
this License. No additional rights or licenses will be implied from the
|
||||||
|
distribution or licensing of Covered Software under this License.
|
||||||
|
Notwithstanding Section 2.1(b) above, no patent license is granted by a
|
||||||
|
Contributor:
|
||||||
|
|
||||||
|
(a) for any code that a Contributor has removed from Covered Software;
|
||||||
|
or
|
||||||
|
|
||||||
|
(b) for infringements caused by: (i) Your and any other third party's
|
||||||
|
modifications of Covered Software, or (ii) the combination of its
|
||||||
|
Contributions with other software (except as part of its Contributor
|
||||||
|
Version); or
|
||||||
|
|
||||||
|
(c) under Patent Claims infringed by Covered Software in the absence of
|
||||||
|
its Contributions.
|
||||||
|
|
||||||
|
This License does not grant any rights in the trademarks, service marks,
|
||||||
|
or logos of any Contributor (except as may be necessary to comply with
|
||||||
|
the notice requirements in Section 3.4).
|
||||||
|
|
||||||
|
2.4. Subsequent Licenses
|
||||||
|
|
||||||
|
No Contributor makes additional grants as a result of Your choice to
|
||||||
|
distribute the Covered Software under a subsequent version of this
|
||||||
|
License (see Section 10.2) or under the terms of a Secondary License (if
|
||||||
|
permitted under the terms of Section 3.3).
|
||||||
|
|
||||||
|
2.5. Representation
|
||||||
|
|
||||||
|
Each Contributor represents that the Contributor believes its
|
||||||
|
Contributions are its original creation(s) or it has sufficient rights
|
||||||
|
to grant the rights to its Contributions conveyed by this License.
|
||||||
|
|
||||||
|
2.6. Fair Use
|
||||||
|
|
||||||
|
This License is not intended to limit any rights You have under
|
||||||
|
applicable copyright doctrines of fair use, fair dealing, or other
|
||||||
|
equivalents.
|
||||||
|
|
||||||
|
2.7. Conditions
|
||||||
|
|
||||||
|
Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted
|
||||||
|
in Section 2.1.
|
||||||
|
|
||||||
|
3. Responsibilities
|
||||||
|
-------------------
|
||||||
|
|
||||||
|
3.1. Distribution of Source Form
|
||||||
|
|
||||||
|
All distribution of Covered Software in Source Code Form, including any
|
||||||
|
Modifications that You create or to which You contribute, must be under
|
||||||
|
the terms of this License. You must inform recipients that the Source
|
||||||
|
Code Form of the Covered Software is governed by the terms of this
|
||||||
|
License, and how they can obtain a copy of this License. You may not
|
||||||
|
attempt to alter or restrict the recipients' rights in the Source Code
|
||||||
|
Form.
|
||||||
|
|
||||||
|
3.2. Distribution of Executable Form
|
||||||
|
|
||||||
|
If You distribute Covered Software in Executable Form then:
|
||||||
|
|
||||||
|
(a) such Covered Software must also be made available in Source Code
|
||||||
|
Form, as described in Section 3.1, and You must inform recipients of
|
||||||
|
the Executable Form how they can obtain a copy of such Source Code
|
||||||
|
Form by reasonable means in a timely manner, at a charge no more
|
||||||
|
than the cost of distribution to the recipient; and
|
||||||
|
|
||||||
|
(b) You may distribute such Executable Form under the terms of this
|
||||||
|
License, or sublicense it under different terms, provided that the
|
||||||
|
license for the Executable Form does not attempt to limit or alter
|
||||||
|
the recipients' rights in the Source Code Form under this License.
|
||||||
|
|
||||||
|
3.3. Distribution of a Larger Work
|
||||||
|
|
||||||
|
You may create and distribute a Larger Work under terms of Your choice,
|
||||||
|
provided that You also comply with the requirements of this License for
|
||||||
|
the Covered Software. If the Larger Work is a combination of Covered
|
||||||
|
Software with a work governed by one or more Secondary Licenses, and the
|
||||||
|
Covered Software is not Incompatible With Secondary Licenses, this
|
||||||
|
License permits You to additionally distribute such Covered Software
|
||||||
|
under the terms of such Secondary License(s), so that the recipient of
|
||||||
|
the Larger Work may, at their option, further distribute the Covered
|
||||||
|
Software under the terms of either this License or such Secondary
|
||||||
|
License(s).
|
||||||
|
|
||||||
|
3.4. Notices
|
||||||
|
|
||||||
|
You may not remove or alter the substance of any license notices
|
||||||
|
(including copyright notices, patent notices, disclaimers of warranty,
|
||||||
|
or limitations of liability) contained within the Source Code Form of
|
||||||
|
the Covered Software, except that You may alter any license notices to
|
||||||
|
the extent required to remedy known factual inaccuracies.
|
||||||
|
|
||||||
|
3.5. Application of Additional Terms
|
||||||
|
|
||||||
|
You may choose to offer, and to charge a fee for, warranty, support,
|
||||||
|
indemnity or liability obligations to one or more recipients of Covered
|
||||||
|
Software. However, You may do so only on Your own behalf, and not on
|
||||||
|
behalf of any Contributor. You must make it absolutely clear that any
|
||||||
|
such warranty, support, indemnity, or liability obligation is offered by
|
||||||
|
You alone, and You hereby agree to indemnify every Contributor for any
|
||||||
|
liability incurred by such Contributor as a result of warranty, support,
|
||||||
|
indemnity or liability terms You offer. You may include additional
|
||||||
|
disclaimers of warranty and limitations of liability specific to any
|
||||||
|
jurisdiction.
|
||||||
|
|
||||||
|
4. Inability to Comply Due to Statute or Regulation
|
||||||
|
---------------------------------------------------
|
||||||
|
|
||||||
|
If it is impossible for You to comply with any of the terms of this
|
||||||
|
License with respect to some or all of the Covered Software due to
|
||||||
|
statute, judicial order, or regulation then You must: (a) comply with
|
||||||
|
the terms of this License to the maximum extent possible; and (b)
|
||||||
|
describe the limitations and the code they affect. Such description must
|
||||||
|
be placed in a text file included with all distributions of the Covered
|
||||||
|
Software under this License. Except to the extent prohibited by statute
|
||||||
|
or regulation, such description must be sufficiently detailed for a
|
||||||
|
recipient of ordinary skill to be able to understand it.
|
||||||
|
|
||||||
|
5. Termination
|
||||||
|
--------------
|
||||||
|
|
||||||
|
5.1. The rights granted under this License will terminate automatically
|
||||||
|
if You fail to comply with any of its terms. However, if You become
|
||||||
|
compliant, then the rights granted under this License from a particular
|
||||||
|
Contributor are reinstated (a) provisionally, unless and until such
|
||||||
|
Contributor explicitly and finally terminates Your grants, and (b) on an
|
||||||
|
ongoing basis, if such Contributor fails to notify You of the
|
||||||
|
non-compliance by some reasonable means prior to 60 days after You have
|
||||||
|
come back into compliance. Moreover, Your grants from a particular
|
||||||
|
Contributor are reinstated on an ongoing basis if such Contributor
|
||||||
|
notifies You of the non-compliance by some reasonable means, this is the
|
||||||
|
first time You have received notice of non-compliance with this License
|
||||||
|
from such Contributor, and You become compliant prior to 30 days after
|
||||||
|
Your receipt of the notice.
|
||||||
|
|
||||||
|
5.2. If You initiate litigation against any entity by asserting a patent
|
||||||
|
infringement claim (excluding declaratory judgment actions,
|
||||||
|
counter-claims, and cross-claims) alleging that a Contributor Version
|
||||||
|
directly or indirectly infringes any patent, then the rights granted to
|
||||||
|
You by any and all Contributors for the Covered Software under Section
|
||||||
|
2.1 of this License shall terminate.
|
||||||
|
|
||||||
|
5.3. In the event of termination under Sections 5.1 or 5.2 above, all
|
||||||
|
end user license agreements (excluding distributors and resellers) which
|
||||||
|
have been validly granted by You or Your distributors under this License
|
||||||
|
prior to termination shall survive termination.
|
||||||
|
|
||||||
|
************************************************************************
|
||||||
|
* *
|
||||||
|
* 6. Disclaimer of Warranty *
|
||||||
|
* ------------------------- *
|
||||||
|
* *
|
||||||
|
* Covered Software is provided under this License on an "as is" *
|
||||||
|
* basis, without warranty of any kind, either expressed, implied, or *
|
||||||
|
* statutory, including, without limitation, warranties that the *
|
||||||
|
* Covered Software is free of defects, merchantable, fit for a *
|
||||||
|
* particular purpose or non-infringing. The entire risk as to the *
|
||||||
|
* quality and performance of the Covered Software is with You. *
|
||||||
|
* Should any Covered Software prove defective in any respect, You *
|
||||||
|
* (not any Contributor) assume the cost of any necessary servicing, *
|
||||||
|
* repair, or correction. This disclaimer of warranty constitutes an *
|
||||||
|
* essential part of this License. No use of any Covered Software is *
|
||||||
|
* authorized under this License except under this disclaimer. *
|
||||||
|
* *
|
||||||
|
************************************************************************
|
||||||
|
|
||||||
|
************************************************************************
|
||||||
|
* *
|
||||||
|
* 7. Limitation of Liability *
|
||||||
|
* -------------------------- *
|
||||||
|
* *
|
||||||
|
* Under no circumstances and under no legal theory, whether tort *
|
||||||
|
* (including negligence), contract, or otherwise, shall any *
|
||||||
|
* Contributor, or anyone who distributes Covered Software as *
|
||||||
|
* permitted above, be liable to You for any direct, indirect, *
|
||||||
|
* special, incidental, or consequential damages of any character *
|
||||||
|
* including, without limitation, damages for lost profits, loss of *
|
||||||
|
* goodwill, work stoppage, computer failure or malfunction, or any *
|
||||||
|
* and all other commercial damages or losses, even if such party *
|
||||||
|
* shall have been informed of the possibility of such damages. This *
|
||||||
|
* limitation of liability shall not apply to liability for death or *
|
||||||
|
* personal injury resulting from such party's negligence to the *
|
||||||
|
* extent applicable law prohibits such limitation. Some *
|
||||||
|
* jurisdictions do not allow the exclusion or limitation of *
|
||||||
|
* incidental or consequential damages, so this exclusion and *
|
||||||
|
* limitation may not apply to You. *
|
||||||
|
* *
|
||||||
|
************************************************************************
|
||||||
|
|
||||||
|
8. Litigation
|
||||||
|
-------------
|
||||||
|
|
||||||
|
Any litigation relating to this License may be brought only in the
|
||||||
|
courts of a jurisdiction where the defendant maintains its principal
|
||||||
|
place of business and such litigation shall be governed by laws of that
|
||||||
|
jurisdiction, without reference to its conflict-of-law provisions.
|
||||||
|
Nothing in this Section shall prevent a party's ability to bring
|
||||||
|
cross-claims or counter-claims.
|
||||||
|
|
||||||
|
9. Miscellaneous
|
||||||
|
----------------
|
||||||
|
|
||||||
|
This License represents the complete agreement concerning the subject
|
||||||
|
matter hereof. If any provision of this License is held to be
|
||||||
|
unenforceable, such provision shall be reformed only to the extent
|
||||||
|
necessary to make it enforceable. Any law or regulation which provides
|
||||||
|
that the language of a contract shall be construed against the drafter
|
||||||
|
shall not be used to construe this License against a Contributor.
|
||||||
|
|
||||||
|
10. Versions of the License
|
||||||
|
---------------------------
|
||||||
|
|
||||||
|
10.1. New Versions
|
||||||
|
|
||||||
|
Mozilla Foundation is the license steward. Except as provided in Section
|
||||||
|
10.3, no one other than the license steward has the right to modify or
|
||||||
|
publish new versions of this License. Each version will be given a
|
||||||
|
distinguishing version number.
|
||||||
|
|
||||||
|
10.2. Effect of New Versions
|
||||||
|
|
||||||
|
You may distribute the Covered Software under the terms of the version
|
||||||
|
of the License under which You originally received the Covered Software,
|
||||||
|
or under the terms of any subsequent version published by the license
|
||||||
|
steward.
|
||||||
|
|
||||||
|
10.3. Modified Versions
|
||||||
|
|
||||||
|
If you create software not governed by this License, and you want to
|
||||||
|
create a new license for such software, you may create and use a
|
||||||
|
modified version of this License if you rename the license and remove
|
||||||
|
any references to the name of the license steward (except to note that
|
||||||
|
such modified license differs from this License).
|
||||||
|
|
||||||
|
10.4. Distributing Source Code Form that is Incompatible With Secondary
|
||||||
|
Licenses
|
||||||
|
|
||||||
|
If You choose to distribute Source Code Form that is Incompatible With
|
||||||
|
Secondary Licenses under the terms of this version of the License, the
|
||||||
|
notice described in Exhibit B of this License must be attached.
|
||||||
|
|
||||||
|
Exhibit A - Source Code Form License Notice
|
||||||
|
-------------------------------------------
|
||||||
|
|
||||||
|
This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
|
If it is not possible or desirable to put the notice in a particular
|
||||||
|
file, then You may include the notice in a location (such as a LICENSE
|
||||||
|
file in a relevant directory) where a recipient would be likely to look
|
||||||
|
for such a notice.
|
||||||
|
|
||||||
|
You may add additional accurate notices of copyright ownership.
|
||||||
|
|
||||||
|
Exhibit B - "Incompatible With Secondary Licenses" Notice
|
||||||
|
---------------------------------------------------------
|
||||||
|
|
||||||
|
This Source Code Form is "Incompatible With Secondary Licenses", as
|
||||||
|
defined by the Mozilla Public License, v. 2.0.
|
118
vendor/github.com/hashicorp/go-sockaddr/README.md
generated
vendored
Normal file
118
vendor/github.com/hashicorp/go-sockaddr/README.md
generated
vendored
Normal file
|
@ -0,0 +1,118 @@
|
||||||
|
# go-sockaddr
|
||||||
|
|
||||||
|
## `sockaddr` Library
|
||||||
|
|
||||||
|
Socket address convenience functions for Go. `go-sockaddr` is a convenience
|
||||||
|
library that makes doing the right thing with IP addresses easy. `go-sockaddr`
|
||||||
|
is loosely modeled after the UNIX `sockaddr_t` and creates a union of the family
|
||||||
|
of `sockaddr_t` types (see below for an ascii diagram). Library documentation
|
||||||
|
is available
|
||||||
|
at
|
||||||
|
[https://godoc.org/github.com/hashicorp/go-sockaddr](https://godoc.org/github.com/hashicorp/go-sockaddr).
|
||||||
|
The primary intent of the library was to make it possible to define heuristics
|
||||||
|
for selecting the correct IP addresses when a configuration is evaluated at
|
||||||
|
runtime. See
|
||||||
|
the
|
||||||
|
[docs](https://godoc.org/github.com/hashicorp/go-sockaddr),
|
||||||
|
[`template` package](https://godoc.org/github.com/hashicorp/go-sockaddr/template),
|
||||||
|
tests,
|
||||||
|
and
|
||||||
|
[CLI utility](https://github.com/hashicorp/go-sockaddr/tree/master/cmd/sockaddr)
|
||||||
|
for details and hints as to how to use this library.
|
||||||
|
|
||||||
|
For example, with this library it is possible to find an IP address that:
|
||||||
|
|
||||||
|
* is attached to a default route
|
||||||
|
([`GetDefaultInterfaces()`](https://godoc.org/github.com/hashicorp/go-sockaddr#GetDefaultInterfaces))
|
||||||
|
* is contained within a CIDR block ([`IfByNetwork()`](https://godoc.org/github.com/hashicorp/go-sockaddr#IfByNetwork))
|
||||||
|
* is an RFC1918 address
|
||||||
|
([`IfByRFC("1918")`](https://godoc.org/github.com/hashicorp/go-sockaddr#IfByRFC))
|
||||||
|
* is ordered
|
||||||
|
([`OrderedIfAddrBy(args)`](https://godoc.org/github.com/hashicorp/go-sockaddr#OrderedIfAddrBy) where
|
||||||
|
`args` includes, but is not limited
|
||||||
|
to,
|
||||||
|
[`AscIfType`](https://godoc.org/github.com/hashicorp/go-sockaddr#AscIfType),
|
||||||
|
[`AscNetworkSize`](https://godoc.org/github.com/hashicorp/go-sockaddr#AscNetworkSize))
|
||||||
|
* excludes all IPv6 addresses
|
||||||
|
([`IfByType("^(IPv4)$")`](https://godoc.org/github.com/hashicorp/go-sockaddr#IfByType))
|
||||||
|
* is larger than a `/32`
|
||||||
|
([`IfByMaskSize(32)`](https://godoc.org/github.com/hashicorp/go-sockaddr#IfByMaskSize))
|
||||||
|
* is not on a `down` interface
|
||||||
|
([`ExcludeIfs("flags", "down")`](https://godoc.org/github.com/hashicorp/go-sockaddr#ExcludeIfs))
|
||||||
|
* preferences an IPv6 address over an IPv4 address
|
||||||
|
([`SortIfByType()`](https://godoc.org/github.com/hashicorp/go-sockaddr#SortIfByType) +
|
||||||
|
[`ReverseIfAddrs()`](https://godoc.org/github.com/hashicorp/go-sockaddr#ReverseIfAddrs)); and
|
||||||
|
* excludes any IP in RFC6890 address
|
||||||
|
([`IfByRFC("6890")`](https://godoc.org/github.com/hashicorp/go-sockaddr#IfByRFC))
|
||||||
|
|
||||||
|
Or any combination or variation therein.
|
||||||
|
|
||||||
|
There are also a few simple helper functions such as `GetPublicIP` and
|
||||||
|
`GetPrivateIP` which both return strings and select the first public or private
|
||||||
|
IP address on the default interface, respectively. Similarly, there is also a
|
||||||
|
helper function called `GetInterfaceIP` which returns the first usable IP
|
||||||
|
address on the named interface.
|
||||||
|
|
||||||
|
## `sockaddr` CLI
|
||||||
|
|
||||||
|
Given the possible complexity of the `sockaddr` library, there is a CLI utility
|
||||||
|
that accompanies the library, also
|
||||||
|
called
|
||||||
|
[`sockaddr`](https://github.com/hashicorp/go-sockaddr/tree/master/cmd/sockaddr).
|
||||||
|
The
|
||||||
|
[`sockaddr`](https://github.com/hashicorp/go-sockaddr/tree/master/cmd/sockaddr)
|
||||||
|
utility exposes nearly all of the functionality of the library and can be used
|
||||||
|
either as an administrative tool or testing tool. To install
|
||||||
|
the
|
||||||
|
[`sockaddr`](https://github.com/hashicorp/go-sockaddr/tree/master/cmd/sockaddr),
|
||||||
|
run:
|
||||||
|
|
||||||
|
```text
|
||||||
|
$ go get -u github.com/hashicorp/go-sockaddr/cmd/sockaddr
|
||||||
|
```
|
||||||
|
|
||||||
|
If you're familiar with UNIX's `sockaddr` struct's, the following diagram
|
||||||
|
mapping the C `sockaddr` (top) to `go-sockaddr` structs (bottom) and
|
||||||
|
interfaces will be helpful:
|
||||||
|
|
||||||
|
```
|
||||||
|
+-------------------------------------------------------+
|
||||||
|
| |
|
||||||
|
| sockaddr |
|
||||||
|
| SockAddr |
|
||||||
|
| |
|
||||||
|
| +--------------+ +----------------------------------+ |
|
||||||
|
| | sockaddr_un | | | |
|
||||||
|
| | SockAddrUnix | | sockaddr_in{,6} | |
|
||||||
|
| +--------------+ | IPAddr | |
|
||||||
|
| | | |
|
||||||
|
| | +-------------+ +--------------+ | |
|
||||||
|
| | | sockaddr_in | | sockaddr_in6 | | |
|
||||||
|
| | | IPv4Addr | | IPv6Addr | | |
|
||||||
|
| | +-------------+ +--------------+ | |
|
||||||
|
| | | |
|
||||||
|
| +----------------------------------+ |
|
||||||
|
| |
|
||||||
|
+-------------------------------------------------------+
|
||||||
|
```
|
||||||
|
|
||||||
|
## Inspiration and Design
|
||||||
|
|
||||||
|
There were many subtle inspirations that led to this design, but the most direct
|
||||||
|
inspiration for the filtering syntax was
|
||||||
|
OpenBSD's
|
||||||
|
[`pf.conf(5)`](https://www.freebsd.org/cgi/man.cgi?query=pf.conf&apropos=0&sektion=0&arch=default&format=html#PARAMETERS) firewall
|
||||||
|
syntax that lets you select the first IP address on a given named interface.
|
||||||
|
The original problem stemmed from:
|
||||||
|
|
||||||
|
* needing to create immutable images using [Packer](https://www.packer.io) that
|
||||||
|
ran the [Consul](https://www.consul.io) process (Consul can only use one IP
|
||||||
|
address at a time);
|
||||||
|
* images that may or may not have multiple interfaces or IP addresses at
|
||||||
|
runtime; and
|
||||||
|
* we didn't want to rely on configuration management to render out the correct
|
||||||
|
IP address if the VM image was being used in an auto-scaling group.
|
||||||
|
|
||||||
|
Instead we needed some way to codify a heuristic that would correctly select the
|
||||||
|
right IP address but the input parameters were not known when the image was
|
||||||
|
created.
|
5
vendor/github.com/hashicorp/go-sockaddr/doc.go
generated
vendored
Normal file
5
vendor/github.com/hashicorp/go-sockaddr/doc.go
generated
vendored
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
/*
|
||||||
|
Package sockaddr is a Go implementation of the UNIX socket family data types and
|
||||||
|
related helper functions.
|
||||||
|
*/
|
||||||
|
package sockaddr
|
126
vendor/github.com/hashicorp/go-sockaddr/ifaddr.go
generated
vendored
Normal file
126
vendor/github.com/hashicorp/go-sockaddr/ifaddr.go
generated
vendored
Normal file
|
@ -0,0 +1,126 @@
|
||||||
|
package sockaddr
|
||||||
|
|
||||||
|
// ifAddrAttrMap is a map of the IfAddr type-specific attributes.
|
||||||
|
var ifAddrAttrMap map[AttrName]func(IfAddr) string
|
||||||
|
var ifAddrAttrs []AttrName
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
ifAddrAttrInit()
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetPrivateIP returns a string with a single IP address that is part of RFC
|
||||||
|
// 6890 and has a default route. If the system can't determine its IP address
|
||||||
|
// or find an RFC 6890 IP address, an empty string will be returned instead.
|
||||||
|
// This function is the `eval` equivalent of:
|
||||||
|
//
|
||||||
|
// ```
|
||||||
|
// $ sockaddr eval -r '{{GetPrivateInterfaces | attr "address"}}'
|
||||||
|
/// ```
|
||||||
|
func GetPrivateIP() (string, error) {
|
||||||
|
privateIfs, err := GetPrivateInterfaces()
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
if len(privateIfs) < 1 {
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
|
||||||
|
ifAddr := privateIfs[0]
|
||||||
|
ip := *ToIPAddr(ifAddr.SockAddr)
|
||||||
|
return ip.NetIP().String(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetPublicIP returns a string with a single IP address that is NOT part of RFC
|
||||||
|
// 6890 and has a default route. If the system can't determine its IP address
|
||||||
|
// or find a non RFC 6890 IP address, an empty string will be returned instead.
|
||||||
|
// This function is the `eval` equivalent of:
|
||||||
|
//
|
||||||
|
// ```
|
||||||
|
// $ sockaddr eval -r '{{GetPublicInterfaces | attr "address"}}'
|
||||||
|
/// ```
|
||||||
|
func GetPublicIP() (string, error) {
|
||||||
|
publicIfs, err := GetPublicInterfaces()
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
} else if len(publicIfs) < 1 {
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
|
||||||
|
ifAddr := publicIfs[0]
|
||||||
|
ip := *ToIPAddr(ifAddr.SockAddr)
|
||||||
|
return ip.NetIP().String(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetInterfaceIP returns a string with a single IP address sorted by the size
|
||||||
|
// of the network (i.e. IP addresses with a smaller netmask, larger network
|
||||||
|
// size, are sorted first). This function is the `eval` equivalent of:
|
||||||
|
//
|
||||||
|
// ```
|
||||||
|
// $ sockaddr eval -r '{{GetAllInterfaces | include "name" <<ARG>> | sort "type,size" | include "flag" "forwardable" | attr "address" }}'
|
||||||
|
/// ```
|
||||||
|
func GetInterfaceIP(namedIfRE string) (string, error) {
|
||||||
|
ifAddrs, err := GetAllInterfaces()
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
ifAddrs, _, err = IfByName(namedIfRE, ifAddrs)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
ifAddrs, _, err = IfByFlag("forwardable", ifAddrs)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
ifAddrs, err = SortIfBy("+type,+size", ifAddrs)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(ifAddrs) == 0 {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
ip := ToIPAddr(ifAddrs[0].SockAddr)
|
||||||
|
if ip == nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
return IPAddrAttr(*ip, "address"), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// IfAddrAttrs returns a list of attributes supported by the IfAddr type
|
||||||
|
func IfAddrAttrs() []AttrName {
|
||||||
|
return ifAddrAttrs
|
||||||
|
}
|
||||||
|
|
||||||
|
// IfAddrAttr returns a string representation of an attribute for the given
|
||||||
|
// IfAddr.
|
||||||
|
func IfAddrAttr(ifAddr IfAddr, attrName AttrName) string {
|
||||||
|
fn, found := ifAddrAttrMap[attrName]
|
||||||
|
if !found {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
return fn(ifAddr)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ifAddrAttrInit is called once at init()
|
||||||
|
func ifAddrAttrInit() {
|
||||||
|
// Sorted for human readability
|
||||||
|
ifAddrAttrs = []AttrName{
|
||||||
|
"flags",
|
||||||
|
"name",
|
||||||
|
}
|
||||||
|
|
||||||
|
ifAddrAttrMap = map[AttrName]func(ifAddr IfAddr) string{
|
||||||
|
"flags": func(ifAddr IfAddr) string {
|
||||||
|
return ifAddr.Interface.Flags.String()
|
||||||
|
},
|
||||||
|
"name": func(ifAddr IfAddr) string {
|
||||||
|
return ifAddr.Interface.Name
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
969
vendor/github.com/hashicorp/go-sockaddr/ifaddrs.go
generated
vendored
Normal file
969
vendor/github.com/hashicorp/go-sockaddr/ifaddrs.go
generated
vendored
Normal file
|
@ -0,0 +1,969 @@
|
||||||
|
package sockaddr
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
"regexp"
|
||||||
|
"sort"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// IfAddrs is a slice of IfAddr
|
||||||
|
type IfAddrs []IfAddr
|
||||||
|
|
||||||
|
func (ifs IfAddrs) Len() int { return len(ifs) }
|
||||||
|
|
||||||
|
// CmpIfFunc is the function signature that must be met to be used in the
|
||||||
|
// OrderedIfAddrBy multiIfAddrSorter
|
||||||
|
type CmpIfAddrFunc func(p1, p2 *IfAddr) int
|
||||||
|
|
||||||
|
// multiIfAddrSorter implements the Sort interface, sorting the IfAddrs within.
|
||||||
|
type multiIfAddrSorter struct {
|
||||||
|
ifAddrs IfAddrs
|
||||||
|
cmp []CmpIfAddrFunc
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sort sorts the argument slice according to the Cmp functions passed to
|
||||||
|
// OrderedIfAddrBy.
|
||||||
|
func (ms *multiIfAddrSorter) Sort(ifAddrs IfAddrs) {
|
||||||
|
ms.ifAddrs = ifAddrs
|
||||||
|
sort.Sort(ms)
|
||||||
|
}
|
||||||
|
|
||||||
|
// OrderedIfAddrBy sorts SockAddr by the list of sort function pointers.
|
||||||
|
func OrderedIfAddrBy(cmpFuncs ...CmpIfAddrFunc) *multiIfAddrSorter {
|
||||||
|
return &multiIfAddrSorter{
|
||||||
|
cmp: cmpFuncs,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Len is part of sort.Interface.
|
||||||
|
func (ms *multiIfAddrSorter) Len() int {
|
||||||
|
return len(ms.ifAddrs)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Less is part of sort.Interface. It is implemented by looping along the Cmp()
|
||||||
|
// functions until it finds a comparison that is either less than or greater
|
||||||
|
// than. A return value of 0 defers sorting to the next function in the
|
||||||
|
// multisorter (which means the results of sorting may leave the resutls in a
|
||||||
|
// non-deterministic order).
|
||||||
|
func (ms *multiIfAddrSorter) Less(i, j int) bool {
|
||||||
|
p, q := &ms.ifAddrs[i], &ms.ifAddrs[j]
|
||||||
|
// Try all but the last comparison.
|
||||||
|
var k int
|
||||||
|
for k = 0; k < len(ms.cmp)-1; k++ {
|
||||||
|
cmp := ms.cmp[k]
|
||||||
|
x := cmp(p, q)
|
||||||
|
switch x {
|
||||||
|
case -1:
|
||||||
|
// p < q, so we have a decision.
|
||||||
|
return true
|
||||||
|
case 1:
|
||||||
|
// p > q, so we have a decision.
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
// p == q; try the next comparison.
|
||||||
|
}
|
||||||
|
// All comparisons to here said "equal", so just return whatever the
|
||||||
|
// final comparison reports.
|
||||||
|
switch ms.cmp[k](p, q) {
|
||||||
|
case -1:
|
||||||
|
return true
|
||||||
|
case 1:
|
||||||
|
return false
|
||||||
|
default:
|
||||||
|
// Still a tie! Now what?
|
||||||
|
return false
|
||||||
|
panic("undefined sort order for remaining items in the list")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Swap is part of sort.Interface.
|
||||||
|
func (ms *multiIfAddrSorter) Swap(i, j int) {
|
||||||
|
ms.ifAddrs[i], ms.ifAddrs[j] = ms.ifAddrs[j], ms.ifAddrs[i]
|
||||||
|
}
|
||||||
|
|
||||||
|
// AscIfAddress is a sorting function to sort IfAddrs by their respective
|
||||||
|
// address type. Non-equal types are deferred in the sort.
|
||||||
|
func AscIfAddress(p1Ptr, p2Ptr *IfAddr) int {
|
||||||
|
return AscAddress(&p1Ptr.SockAddr, &p2Ptr.SockAddr)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AscIfName is a sorting function to sort IfAddrs by their interface names.
|
||||||
|
func AscIfName(p1Ptr, p2Ptr *IfAddr) int {
|
||||||
|
return strings.Compare(p1Ptr.Name, p2Ptr.Name)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AscIfNetworkSize is a sorting function to sort IfAddrs by their respective
|
||||||
|
// network mask size.
|
||||||
|
func AscIfNetworkSize(p1Ptr, p2Ptr *IfAddr) int {
|
||||||
|
return AscNetworkSize(&p1Ptr.SockAddr, &p2Ptr.SockAddr)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AscIfPort is a sorting function to sort IfAddrs by their respective
|
||||||
|
// port type. Non-equal types are deferred in the sort.
|
||||||
|
func AscIfPort(p1Ptr, p2Ptr *IfAddr) int {
|
||||||
|
return AscPort(&p1Ptr.SockAddr, &p2Ptr.SockAddr)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AscIfPrivate is a sorting function to sort IfAddrs by "private" values before
|
||||||
|
// "public" values. Both IPv4 and IPv6 are compared against RFC6890 (RFC6890
|
||||||
|
// includes, and is not limited to, RFC1918 and RFC6598 for IPv4, and IPv6
|
||||||
|
// includes RFC4193).
|
||||||
|
func AscIfPrivate(p1Ptr, p2Ptr *IfAddr) int {
|
||||||
|
return AscPrivate(&p1Ptr.SockAddr, &p2Ptr.SockAddr)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AscIfType is a sorting function to sort IfAddrs by their respective address
|
||||||
|
// type. Non-equal types are deferred in the sort.
|
||||||
|
func AscIfType(p1Ptr, p2Ptr *IfAddr) int {
|
||||||
|
return AscType(&p1Ptr.SockAddr, &p2Ptr.SockAddr)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DescIfAddress is identical to AscIfAddress but reverse ordered.
|
||||||
|
func DescIfAddress(p1Ptr, p2Ptr *IfAddr) int {
|
||||||
|
return -1 * AscAddress(&p1Ptr.SockAddr, &p2Ptr.SockAddr)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DescIfName is identical to AscIfName but reverse ordered.
|
||||||
|
func DescIfName(p1Ptr, p2Ptr *IfAddr) int {
|
||||||
|
return -1 * strings.Compare(p1Ptr.Name, p2Ptr.Name)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DescIfNetworkSize is identical to AscIfNetworkSize but reverse ordered.
|
||||||
|
func DescIfNetworkSize(p1Ptr, p2Ptr *IfAddr) int {
|
||||||
|
return -1 * AscNetworkSize(&p1Ptr.SockAddr, &p2Ptr.SockAddr)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DescIfPort is identical to AscIfPort but reverse ordered.
|
||||||
|
func DescIfPort(p1Ptr, p2Ptr *IfAddr) int {
|
||||||
|
return -1 * AscPort(&p1Ptr.SockAddr, &p2Ptr.SockAddr)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DescIfPrivate is identical to AscIfPrivate but reverse ordered.
|
||||||
|
func DescIfPrivate(p1Ptr, p2Ptr *IfAddr) int {
|
||||||
|
return -1 * AscPrivate(&p1Ptr.SockAddr, &p2Ptr.SockAddr)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DescIfType is identical to AscIfType but reverse ordered.
|
||||||
|
func DescIfType(p1Ptr, p2Ptr *IfAddr) int {
|
||||||
|
return -1 * AscType(&p1Ptr.SockAddr, &p2Ptr.SockAddr)
|
||||||
|
}
|
||||||
|
|
||||||
|
// FilterIfByType filters IfAddrs and returns a list of the matching type
|
||||||
|
func FilterIfByType(ifAddrs IfAddrs, type_ SockAddrType) (matchedIfs, excludedIfs IfAddrs) {
|
||||||
|
excludedIfs = make(IfAddrs, 0, len(ifAddrs))
|
||||||
|
matchedIfs = make(IfAddrs, 0, len(ifAddrs))
|
||||||
|
|
||||||
|
for _, ifAddr := range ifAddrs {
|
||||||
|
if ifAddr.SockAddr.Type()&type_ != 0 {
|
||||||
|
matchedIfs = append(matchedIfs, ifAddr)
|
||||||
|
} else {
|
||||||
|
excludedIfs = append(excludedIfs, ifAddr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return matchedIfs, excludedIfs
|
||||||
|
}
|
||||||
|
|
||||||
|
// IfAttr forwards the selector to IfAttr.Attr() for resolution. If there is
|
||||||
|
// more than one IfAddr, only the first IfAddr is used.
|
||||||
|
func IfAttr(selectorName string, ifAddrs IfAddrs) (string, error) {
|
||||||
|
if len(ifAddrs) == 0 {
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
|
||||||
|
attrName := AttrName(strings.ToLower(selectorName))
|
||||||
|
attrVal, err := ifAddrs[0].Attr(attrName)
|
||||||
|
return attrVal, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetAllInterfaces iterates over all available network interfaces and finds all
|
||||||
|
// available IP addresses on each interface and converts them to
|
||||||
|
// sockaddr.IPAddrs, and returning the result as an array of IfAddr.
|
||||||
|
func GetAllInterfaces() (IfAddrs, error) {
|
||||||
|
ifs, err := net.Interfaces()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
ifAddrs := make(IfAddrs, 0, len(ifs))
|
||||||
|
for _, intf := range ifs {
|
||||||
|
addrs, err := intf.Addrs()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, addr := range addrs {
|
||||||
|
var ipAddr IPAddr
|
||||||
|
ipAddr, err = NewIPAddr(addr.String())
|
||||||
|
if err != nil {
|
||||||
|
return IfAddrs{}, fmt.Errorf("unable to create an IP address from %q", addr.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
ifAddr := IfAddr{
|
||||||
|
SockAddr: ipAddr,
|
||||||
|
Interface: intf,
|
||||||
|
}
|
||||||
|
ifAddrs = append(ifAddrs, ifAddr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ifAddrs, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetDefaultInterfaces returns IfAddrs of the addresses attached to the default
|
||||||
|
// route.
|
||||||
|
func GetDefaultInterfaces() (IfAddrs, error) {
|
||||||
|
ri, err := NewRouteInfo()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
defaultIfName, err := ri.GetDefaultInterfaceName()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var defaultIfs, ifAddrs IfAddrs
|
||||||
|
ifAddrs, err = GetAllInterfaces()
|
||||||
|
for _, ifAddr := range ifAddrs {
|
||||||
|
if ifAddr.Name == defaultIfName {
|
||||||
|
defaultIfs = append(defaultIfs, ifAddr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return defaultIfs, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetPrivateInterfaces returns an IfAddrs that are part of RFC 6890 and have a
|
||||||
|
// default route. If the system can't determine its IP address or find an RFC
|
||||||
|
// 6890 IP address, an empty IfAddrs will be returned instead. This function is
|
||||||
|
// the `eval` equivalent of:
|
||||||
|
//
|
||||||
|
// ```
|
||||||
|
// $ sockaddr eval -r '{{GetDefaultInterfaces | include "type" "ip" | include "flags" "forwardable|up" | sort "type,size" | include "RFC" "6890" }}'
|
||||||
|
/// ```
|
||||||
|
func GetPrivateInterfaces() (IfAddrs, error) {
|
||||||
|
privateIfs, err := GetDefaultInterfaces()
|
||||||
|
if err != nil {
|
||||||
|
return IfAddrs{}, err
|
||||||
|
}
|
||||||
|
if len(privateIfs) == 0 {
|
||||||
|
return IfAddrs{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
privateIfs, _ = FilterIfByType(privateIfs, TypeIP)
|
||||||
|
if len(privateIfs) == 0 {
|
||||||
|
return IfAddrs{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
privateIfs, _, err = IfByFlag("forwardable|up", privateIfs)
|
||||||
|
if err != nil {
|
||||||
|
return IfAddrs{}, err
|
||||||
|
}
|
||||||
|
if len(privateIfs) == 0 {
|
||||||
|
return IfAddrs{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
OrderedIfAddrBy(AscIfType, AscIfNetworkSize).Sort(privateIfs)
|
||||||
|
|
||||||
|
privateIfs, _, err = IfByRFC("6890", privateIfs)
|
||||||
|
if err != nil {
|
||||||
|
return IfAddrs{}, err
|
||||||
|
} else if len(privateIfs) == 0 {
|
||||||
|
return IfAddrs{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return privateIfs, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetPublicInterfaces returns an IfAddrs that are NOT part of RFC 6890 and has a
|
||||||
|
// default route. If the system can't determine its IP address or find a non
|
||||||
|
// RFC 6890 IP address, an empty IfAddrs will be returned instead. This
|
||||||
|
// function is the `eval` equivalent of:
|
||||||
|
//
|
||||||
|
// ```
|
||||||
|
// $ sockaddr eval -r '{{GetDefaultInterfaces | include "type" "ip" | include "flags" "forwardable|up" | sort "type,size" | exclude "RFC" "6890" }}'
|
||||||
|
/// ```
|
||||||
|
func GetPublicInterfaces() (IfAddrs, error) {
|
||||||
|
publicIfs, err := GetDefaultInterfaces()
|
||||||
|
if err != nil {
|
||||||
|
return IfAddrs{}, err
|
||||||
|
}
|
||||||
|
if len(publicIfs) == 0 {
|
||||||
|
return IfAddrs{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
publicIfs, _ = FilterIfByType(publicIfs, TypeIP)
|
||||||
|
if len(publicIfs) == 0 {
|
||||||
|
return IfAddrs{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
publicIfs, _, err = IfByFlag("forwardable|up", publicIfs)
|
||||||
|
if err != nil {
|
||||||
|
return IfAddrs{}, err
|
||||||
|
}
|
||||||
|
if len(publicIfs) == 0 {
|
||||||
|
return IfAddrs{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
OrderedIfAddrBy(AscIfType, AscIfNetworkSize).Sort(publicIfs)
|
||||||
|
|
||||||
|
_, publicIfs, err = IfByRFC("6890", publicIfs)
|
||||||
|
if err != nil {
|
||||||
|
return IfAddrs{}, err
|
||||||
|
} else if len(publicIfs) == 0 {
|
||||||
|
return IfAddrs{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return publicIfs, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// IfByAddress returns a list of matched and non-matched IfAddrs, or an error if
|
||||||
|
// the regexp fails to compile.
|
||||||
|
func IfByAddress(inputRe string, ifAddrs IfAddrs) (matched, remainder IfAddrs, err error) {
|
||||||
|
re, err := regexp.Compile(inputRe)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, fmt.Errorf("Unable to compile address regexp %+q: %v", inputRe, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
matchedAddrs := make(IfAddrs, 0, len(ifAddrs))
|
||||||
|
excludedAddrs := make(IfAddrs, 0, len(ifAddrs))
|
||||||
|
for _, addr := range ifAddrs {
|
||||||
|
if re.MatchString(addr.SockAddr.String()) {
|
||||||
|
matchedAddrs = append(matchedAddrs, addr)
|
||||||
|
} else {
|
||||||
|
excludedAddrs = append(excludedAddrs, addr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return matchedAddrs, excludedAddrs, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// IfByName returns a list of matched and non-matched IfAddrs, or an error if
|
||||||
|
// the regexp fails to compile.
|
||||||
|
func IfByName(inputRe string, ifAddrs IfAddrs) (matched, remainder IfAddrs, err error) {
|
||||||
|
re, err := regexp.Compile(inputRe)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, fmt.Errorf("Unable to compile name regexp %+q: %v", inputRe, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
matchedAddrs := make(IfAddrs, 0, len(ifAddrs))
|
||||||
|
excludedAddrs := make(IfAddrs, 0, len(ifAddrs))
|
||||||
|
for _, addr := range ifAddrs {
|
||||||
|
if re.MatchString(addr.Name) {
|
||||||
|
matchedAddrs = append(matchedAddrs, addr)
|
||||||
|
} else {
|
||||||
|
excludedAddrs = append(excludedAddrs, addr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return matchedAddrs, excludedAddrs, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// IfByPort returns a list of matched and non-matched IfAddrs, or an error if
|
||||||
|
// the regexp fails to compile.
|
||||||
|
func IfByPort(inputRe string, ifAddrs IfAddrs) (matchedIfs, excludedIfs IfAddrs, err error) {
|
||||||
|
re, err := regexp.Compile(inputRe)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, fmt.Errorf("Unable to compile port regexp %+q: %v", inputRe, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
ipIfs, nonIfs := FilterIfByType(ifAddrs, TypeIP)
|
||||||
|
matchedIfs = make(IfAddrs, 0, len(ipIfs))
|
||||||
|
excludedIfs = append(IfAddrs(nil), nonIfs...)
|
||||||
|
for _, addr := range ipIfs {
|
||||||
|
ipAddr := ToIPAddr(addr.SockAddr)
|
||||||
|
if ipAddr == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
port := strconv.FormatInt(int64((*ipAddr).IPPort()), 10)
|
||||||
|
if re.MatchString(port) {
|
||||||
|
matchedIfs = append(matchedIfs, addr)
|
||||||
|
} else {
|
||||||
|
excludedIfs = append(excludedIfs, addr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return matchedIfs, excludedIfs, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// IfByRFC returns a list of matched and non-matched IfAddrs that contain the
|
||||||
|
// relevant RFC-specified traits.
|
||||||
|
func IfByRFC(selectorParam string, ifAddrs IfAddrs) (matched, remainder IfAddrs, err error) {
|
||||||
|
inputRFC, err := strconv.ParseUint(selectorParam, 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return IfAddrs{}, IfAddrs{}, fmt.Errorf("unable to parse RFC number %q: %v", selectorParam, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
matchedIfAddrs := make(IfAddrs, 0, len(ifAddrs))
|
||||||
|
remainingIfAddrs := make(IfAddrs, 0, len(ifAddrs))
|
||||||
|
|
||||||
|
rfcNetMap := KnownRFCs()
|
||||||
|
rfcNets, ok := rfcNetMap[uint(inputRFC)]
|
||||||
|
if !ok {
|
||||||
|
return nil, nil, fmt.Errorf("unsupported RFC %d", inputRFC)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, ifAddr := range ifAddrs {
|
||||||
|
var contained bool
|
||||||
|
for _, rfcNet := range rfcNets {
|
||||||
|
if rfcNet.Contains(ifAddr.SockAddr) {
|
||||||
|
matchedIfAddrs = append(matchedIfAddrs, ifAddr)
|
||||||
|
contained = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !contained {
|
||||||
|
remainingIfAddrs = append(remainingIfAddrs, ifAddr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return matchedIfAddrs, remainingIfAddrs, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// IfByRFCs returns a list of matched and non-matched IfAddrs that contain the
|
||||||
|
// relevant RFC-specified traits. Multiple RFCs can be specified and separated
|
||||||
|
// by the `|` symbol. No protection is taken to ensure an IfAddr does not end
|
||||||
|
// up in both the included and excluded list.
|
||||||
|
func IfByRFCs(selectorParam string, ifAddrs IfAddrs) (matched, remainder IfAddrs, err error) {
|
||||||
|
var includedIfs, excludedIfs IfAddrs
|
||||||
|
for _, rfcStr := range strings.Split(selectorParam, "|") {
|
||||||
|
includedRFCIfs, excludedRFCIfs, err := IfByRFC(rfcStr, ifAddrs)
|
||||||
|
if err != nil {
|
||||||
|
return IfAddrs{}, IfAddrs{}, fmt.Errorf("unable to lookup RFC number %q: %v", rfcStr, err)
|
||||||
|
}
|
||||||
|
includedIfs = append(includedIfs, includedRFCIfs...)
|
||||||
|
excludedIfs = append(excludedIfs, excludedRFCIfs...)
|
||||||
|
}
|
||||||
|
|
||||||
|
return includedIfs, excludedIfs, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// IfByMaskSize returns a list of matched and non-matched IfAddrs that have the
|
||||||
|
// matching mask size.
|
||||||
|
func IfByMaskSize(selectorParam string, ifAddrs IfAddrs) (matchedIfs, excludedIfs IfAddrs, err error) {
|
||||||
|
maskSize, err := strconv.ParseUint(selectorParam, 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return IfAddrs{}, IfAddrs{}, fmt.Errorf("invalid exclude size argument (%q): %v", selectorParam, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
ipIfs, nonIfs := FilterIfByType(ifAddrs, TypeIP)
|
||||||
|
matchedIfs = make(IfAddrs, 0, len(ipIfs))
|
||||||
|
excludedIfs = append(IfAddrs(nil), nonIfs...)
|
||||||
|
for _, addr := range ipIfs {
|
||||||
|
ipAddr := ToIPAddr(addr.SockAddr)
|
||||||
|
if ipAddr == nil {
|
||||||
|
return IfAddrs{}, IfAddrs{}, fmt.Errorf("unable to filter mask sizes on non-IP type %s: %v", addr.SockAddr.Type().String(), addr.SockAddr.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
switch {
|
||||||
|
case (*ipAddr).Type()&TypeIPv4 != 0 && maskSize > 32:
|
||||||
|
return IfAddrs{}, IfAddrs{}, fmt.Errorf("mask size out of bounds for IPv4 address: %d", maskSize)
|
||||||
|
case (*ipAddr).Type()&TypeIPv6 != 0 && maskSize > 128:
|
||||||
|
return IfAddrs{}, IfAddrs{}, fmt.Errorf("mask size out of bounds for IPv6 address: %d", maskSize)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (*ipAddr).Maskbits() == int(maskSize) {
|
||||||
|
matchedIfs = append(matchedIfs, addr)
|
||||||
|
} else {
|
||||||
|
excludedIfs = append(excludedIfs, addr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return matchedIfs, excludedIfs, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// IfByType returns a list of matching and non-matching IfAddr that match the
|
||||||
|
// specified type. For instance:
|
||||||
|
//
|
||||||
|
// include "type" "IPv4,IPv6"
|
||||||
|
//
|
||||||
|
// will include any IfAddrs that is either an IPv4 or IPv6 address. Any
|
||||||
|
// addresses on those interfaces that don't match will be included in the
|
||||||
|
// remainder results.
|
||||||
|
func IfByType(inputTypes string, ifAddrs IfAddrs) (matched, remainder IfAddrs, err error) {
|
||||||
|
matchingIfAddrs := make(IfAddrs, 0, len(ifAddrs))
|
||||||
|
remainingIfAddrs := make(IfAddrs, 0, len(ifAddrs))
|
||||||
|
|
||||||
|
ifTypes := strings.Split(strings.ToLower(inputTypes), "|")
|
||||||
|
for _, ifType := range ifTypes {
|
||||||
|
switch ifType {
|
||||||
|
case "ip", "ipv4", "ipv6", "unix":
|
||||||
|
// Valid types
|
||||||
|
default:
|
||||||
|
return nil, nil, fmt.Errorf("unsupported type %q %q", ifType, inputTypes)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, ifAddr := range ifAddrs {
|
||||||
|
for _, ifType := range ifTypes {
|
||||||
|
var matched bool
|
||||||
|
switch {
|
||||||
|
case ifType == "ip" && ifAddr.SockAddr.Type()&TypeIP != 0:
|
||||||
|
matched = true
|
||||||
|
case ifType == "ipv4" && ifAddr.SockAddr.Type()&TypeIPv4 != 0:
|
||||||
|
matched = true
|
||||||
|
case ifType == "ipv6" && ifAddr.SockAddr.Type()&TypeIPv6 != 0:
|
||||||
|
matched = true
|
||||||
|
case ifType == "unix" && ifAddr.SockAddr.Type()&TypeUnix != 0:
|
||||||
|
matched = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if matched {
|
||||||
|
matchingIfAddrs = append(matchingIfAddrs, ifAddr)
|
||||||
|
} else {
|
||||||
|
remainingIfAddrs = append(remainingIfAddrs, ifAddr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return matchingIfAddrs, remainingIfAddrs, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// IfByFlag returns a list of matching and non-matching IfAddrs that match the
|
||||||
|
// specified type. For instance:
|
||||||
|
//
|
||||||
|
// include "flag" "up,broadcast"
|
||||||
|
//
|
||||||
|
// will include any IfAddrs that have both the "up" and "broadcast" flags set.
|
||||||
|
// Any addresses on those interfaces that don't match will be omitted from the
|
||||||
|
// results.
|
||||||
|
func IfByFlag(inputFlags string, ifAddrs IfAddrs) (matched, remainder IfAddrs, err error) {
|
||||||
|
matchedAddrs := make(IfAddrs, 0, len(ifAddrs))
|
||||||
|
excludedAddrs := make(IfAddrs, 0, len(ifAddrs))
|
||||||
|
|
||||||
|
var wantForwardable,
|
||||||
|
wantGlobalUnicast,
|
||||||
|
wantInterfaceLocalMulticast,
|
||||||
|
wantLinkLocalMulticast,
|
||||||
|
wantLinkLocalUnicast,
|
||||||
|
wantLoopback,
|
||||||
|
wantMulticast,
|
||||||
|
wantUnspecified bool
|
||||||
|
var ifFlags net.Flags
|
||||||
|
var checkFlags, checkAttrs bool
|
||||||
|
for _, flagName := range strings.Split(strings.ToLower(inputFlags), "|") {
|
||||||
|
switch flagName {
|
||||||
|
case "broadcast":
|
||||||
|
checkFlags = true
|
||||||
|
ifFlags = ifFlags | net.FlagBroadcast
|
||||||
|
case "down":
|
||||||
|
checkFlags = true
|
||||||
|
ifFlags = (ifFlags &^ net.FlagUp)
|
||||||
|
case "forwardable":
|
||||||
|
checkAttrs = true
|
||||||
|
wantForwardable = true
|
||||||
|
case "global unicast":
|
||||||
|
checkAttrs = true
|
||||||
|
wantGlobalUnicast = true
|
||||||
|
case "interface-local multicast":
|
||||||
|
checkAttrs = true
|
||||||
|
wantInterfaceLocalMulticast = true
|
||||||
|
case "link-local multicast":
|
||||||
|
checkAttrs = true
|
||||||
|
wantLinkLocalMulticast = true
|
||||||
|
case "link-local unicast":
|
||||||
|
checkAttrs = true
|
||||||
|
wantLinkLocalUnicast = true
|
||||||
|
case "loopback":
|
||||||
|
checkAttrs = true
|
||||||
|
checkFlags = true
|
||||||
|
ifFlags = ifFlags | net.FlagLoopback
|
||||||
|
wantLoopback = true
|
||||||
|
case "multicast":
|
||||||
|
checkAttrs = true
|
||||||
|
checkFlags = true
|
||||||
|
ifFlags = ifFlags | net.FlagMulticast
|
||||||
|
wantMulticast = true
|
||||||
|
case "point-to-point":
|
||||||
|
checkFlags = true
|
||||||
|
ifFlags = ifFlags | net.FlagPointToPoint
|
||||||
|
case "unspecified":
|
||||||
|
checkAttrs = true
|
||||||
|
wantUnspecified = true
|
||||||
|
case "up":
|
||||||
|
checkFlags = true
|
||||||
|
ifFlags = ifFlags | net.FlagUp
|
||||||
|
default:
|
||||||
|
return nil, nil, fmt.Errorf("Unknown interface flag: %+q", flagName)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, ifAddr := range ifAddrs {
|
||||||
|
var matched bool
|
||||||
|
if checkFlags && ifAddr.Interface.Flags&ifFlags == ifFlags {
|
||||||
|
matched = true
|
||||||
|
}
|
||||||
|
if checkAttrs {
|
||||||
|
if ip := ToIPAddr(ifAddr.SockAddr); ip != nil {
|
||||||
|
netIP := (*ip).NetIP()
|
||||||
|
switch {
|
||||||
|
case wantGlobalUnicast && netIP.IsGlobalUnicast():
|
||||||
|
matched = true
|
||||||
|
case wantInterfaceLocalMulticast && netIP.IsInterfaceLocalMulticast():
|
||||||
|
matched = true
|
||||||
|
case wantLinkLocalMulticast && netIP.IsLinkLocalMulticast():
|
||||||
|
matched = true
|
||||||
|
case wantLinkLocalUnicast && netIP.IsLinkLocalUnicast():
|
||||||
|
matched = true
|
||||||
|
case wantLoopback && netIP.IsLoopback():
|
||||||
|
matched = true
|
||||||
|
case wantMulticast && netIP.IsMulticast():
|
||||||
|
matched = true
|
||||||
|
case wantUnspecified && netIP.IsUnspecified():
|
||||||
|
matched = true
|
||||||
|
case wantForwardable && !IsRFC(ForwardingBlacklist, ifAddr.SockAddr):
|
||||||
|
matched = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if matched {
|
||||||
|
matchedAddrs = append(matchedAddrs, ifAddr)
|
||||||
|
} else {
|
||||||
|
excludedAddrs = append(excludedAddrs, ifAddr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return matchedAddrs, excludedAddrs, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// IfByNetwork returns an IfAddrs that are equal to or included within the
|
||||||
|
// network passed in by selector.
|
||||||
|
func IfByNetwork(selectorParam string, inputIfAddrs IfAddrs) (IfAddrs, IfAddrs, error) {
|
||||||
|
var includedIfs, excludedIfs IfAddrs
|
||||||
|
for _, netStr := range strings.Split(selectorParam, "|") {
|
||||||
|
netAddr, err := NewIPAddr(netStr)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, fmt.Errorf("unable to create an IP address from %+q: %v", netStr, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, ifAddr := range inputIfAddrs {
|
||||||
|
if netAddr.Contains(ifAddr.SockAddr) {
|
||||||
|
includedIfs = append(includedIfs, ifAddr)
|
||||||
|
} else {
|
||||||
|
excludedIfs = append(excludedIfs, ifAddr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return includedIfs, excludedIfs, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// IncludeIfs returns an IfAddrs based on the passed in selector.
|
||||||
|
func IncludeIfs(selectorName, selectorParam string, inputIfAddrs IfAddrs) (IfAddrs, error) {
|
||||||
|
var includedIfs IfAddrs
|
||||||
|
var err error
|
||||||
|
|
||||||
|
switch strings.ToLower(selectorName) {
|
||||||
|
case "address":
|
||||||
|
includedIfs, _, err = IfByAddress(selectorParam, inputIfAddrs)
|
||||||
|
case "flag", "flags":
|
||||||
|
includedIfs, _, err = IfByFlag(selectorParam, inputIfAddrs)
|
||||||
|
case "name":
|
||||||
|
includedIfs, _, err = IfByName(selectorParam, inputIfAddrs)
|
||||||
|
case "network":
|
||||||
|
includedIfs, _, err = IfByNetwork(selectorParam, inputIfAddrs)
|
||||||
|
case "port":
|
||||||
|
includedIfs, _, err = IfByPort(selectorParam, inputIfAddrs)
|
||||||
|
case "rfc", "rfcs":
|
||||||
|
includedIfs, _, err = IfByRFCs(selectorParam, inputIfAddrs)
|
||||||
|
case "size":
|
||||||
|
includedIfs, _, err = IfByMaskSize(selectorParam, inputIfAddrs)
|
||||||
|
case "type":
|
||||||
|
includedIfs, _, err = IfByType(selectorParam, inputIfAddrs)
|
||||||
|
default:
|
||||||
|
return IfAddrs{}, fmt.Errorf("invalid include selector %q", selectorName)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return IfAddrs{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return includedIfs, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExcludeIfs returns an IfAddrs based on the passed in selector.
|
||||||
|
func ExcludeIfs(selectorName, selectorParam string, inputIfAddrs IfAddrs) (IfAddrs, error) {
|
||||||
|
var excludedIfs IfAddrs
|
||||||
|
var err error
|
||||||
|
|
||||||
|
switch strings.ToLower(selectorName) {
|
||||||
|
case "address":
|
||||||
|
_, excludedIfs, err = IfByAddress(selectorParam, inputIfAddrs)
|
||||||
|
case "flag", "flags":
|
||||||
|
_, excludedIfs, err = IfByFlag(selectorParam, inputIfAddrs)
|
||||||
|
case "name":
|
||||||
|
_, excludedIfs, err = IfByName(selectorParam, inputIfAddrs)
|
||||||
|
case "network":
|
||||||
|
_, excludedIfs, err = IfByNetwork(selectorParam, inputIfAddrs)
|
||||||
|
case "port":
|
||||||
|
_, excludedIfs, err = IfByPort(selectorParam, inputIfAddrs)
|
||||||
|
case "rfc", "rfcs":
|
||||||
|
_, excludedIfs, err = IfByRFCs(selectorParam, inputIfAddrs)
|
||||||
|
case "size":
|
||||||
|
_, excludedIfs, err = IfByMaskSize(selectorParam, inputIfAddrs)
|
||||||
|
case "type":
|
||||||
|
_, excludedIfs, err = IfByType(selectorParam, inputIfAddrs)
|
||||||
|
default:
|
||||||
|
return IfAddrs{}, fmt.Errorf("invalid exclude selector %q", selectorName)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return IfAddrs{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return excludedIfs, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SortIfBy returns an IfAddrs sorted based on the passed in selector. Multiple
|
||||||
|
// sort clauses can be passed in as a comma delimited list without whitespace.
|
||||||
|
func SortIfBy(selectorParam string, inputIfAddrs IfAddrs) (IfAddrs, error) {
|
||||||
|
sortedIfs := append(IfAddrs(nil), inputIfAddrs...)
|
||||||
|
|
||||||
|
clauses := strings.Split(selectorParam, ",")
|
||||||
|
sortFuncs := make([]CmpIfAddrFunc, len(clauses))
|
||||||
|
|
||||||
|
for i, clause := range clauses {
|
||||||
|
switch strings.TrimSpace(strings.ToLower(clause)) {
|
||||||
|
case "+address", "address":
|
||||||
|
// The "address" selector returns an array of IfAddrs
|
||||||
|
// ordered by the network address. IfAddrs that are not
|
||||||
|
// comparable will be at the end of the list and in a
|
||||||
|
// non-deterministic order.
|
||||||
|
sortFuncs[i] = AscIfAddress
|
||||||
|
case "-address":
|
||||||
|
sortFuncs[i] = DescIfAddress
|
||||||
|
case "+name", "name":
|
||||||
|
// The "name" selector returns an array of IfAddrs
|
||||||
|
// ordered by the interface name.
|
||||||
|
sortFuncs[i] = AscIfName
|
||||||
|
case "-name":
|
||||||
|
sortFuncs[i] = DescIfName
|
||||||
|
case "+port", "port":
|
||||||
|
// The "port" selector returns an array of IfAddrs
|
||||||
|
// ordered by the port, if included in the IfAddr.
|
||||||
|
// IfAddrs that are not comparable will be at the end of
|
||||||
|
// the list and in a non-deterministic order.
|
||||||
|
sortFuncs[i] = AscIfPort
|
||||||
|
case "-port":
|
||||||
|
sortFuncs[i] = DescIfPort
|
||||||
|
case "+private", "private":
|
||||||
|
// The "private" selector returns an array of IfAddrs
|
||||||
|
// ordered by private addresses first. IfAddrs that are
|
||||||
|
// not comparable will be at the end of the list and in
|
||||||
|
// a non-deterministic order.
|
||||||
|
sortFuncs[i] = AscIfPrivate
|
||||||
|
case "-private":
|
||||||
|
sortFuncs[i] = DescIfPrivate
|
||||||
|
case "+size", "size":
|
||||||
|
// The "size" selector returns an array of IfAddrs
|
||||||
|
// ordered by the size of the network mask, smaller mask
|
||||||
|
// (larger number of hosts per network) to largest
|
||||||
|
// (e.g. a /24 sorts before a /32).
|
||||||
|
sortFuncs[i] = AscIfNetworkSize
|
||||||
|
case "-size":
|
||||||
|
sortFuncs[i] = DescIfNetworkSize
|
||||||
|
case "+type", "type":
|
||||||
|
// The "type" selector returns an array of IfAddrs
|
||||||
|
// ordered by the type of the IfAddr. The sort order is
|
||||||
|
// Unix, IPv4, then IPv6.
|
||||||
|
sortFuncs[i] = AscIfType
|
||||||
|
case "-type":
|
||||||
|
sortFuncs[i] = DescIfType
|
||||||
|
default:
|
||||||
|
// Return an empty list for invalid sort types.
|
||||||
|
return IfAddrs{}, fmt.Errorf("unknown sort type: %q", clause)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
OrderedIfAddrBy(sortFuncs...).Sort(sortedIfs)
|
||||||
|
|
||||||
|
return sortedIfs, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// UniqueIfAddrsBy creates a unique set of IfAddrs based on the matching
|
||||||
|
// selector. UniqueIfAddrsBy assumes the input has already been sorted.
|
||||||
|
func UniqueIfAddrsBy(selectorName string, inputIfAddrs IfAddrs) (IfAddrs, error) {
|
||||||
|
attrName := strings.ToLower(selectorName)
|
||||||
|
|
||||||
|
ifs := make(IfAddrs, 0, len(inputIfAddrs))
|
||||||
|
var lastMatch string
|
||||||
|
for _, ifAddr := range inputIfAddrs {
|
||||||
|
var out string
|
||||||
|
switch attrName {
|
||||||
|
case "address":
|
||||||
|
out = ifAddr.SockAddr.String()
|
||||||
|
case "name":
|
||||||
|
out = ifAddr.Name
|
||||||
|
default:
|
||||||
|
return nil, fmt.Errorf("unsupported unique constraint %+q", selectorName)
|
||||||
|
}
|
||||||
|
|
||||||
|
switch {
|
||||||
|
case lastMatch == "", lastMatch != out:
|
||||||
|
lastMatch = out
|
||||||
|
ifs = append(ifs, ifAddr)
|
||||||
|
case lastMatch == out:
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ifs, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// JoinIfAddrs joins an IfAddrs and returns a string
|
||||||
|
func JoinIfAddrs(selectorName string, joinStr string, inputIfAddrs IfAddrs) (string, error) {
|
||||||
|
outputs := make([]string, 0, len(inputIfAddrs))
|
||||||
|
attrName := AttrName(strings.ToLower(selectorName))
|
||||||
|
|
||||||
|
for _, ifAddr := range inputIfAddrs {
|
||||||
|
var attrVal string
|
||||||
|
var err error
|
||||||
|
attrVal, err = ifAddr.Attr(attrName)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
outputs = append(outputs, attrVal)
|
||||||
|
}
|
||||||
|
return strings.Join(outputs, joinStr), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// LimitIfAddrs returns a slice of IfAddrs based on the specified limit.
|
||||||
|
func LimitIfAddrs(lim uint, in IfAddrs) (IfAddrs, error) {
|
||||||
|
// Clamp the limit to the length of the array
|
||||||
|
if int(lim) > len(in) {
|
||||||
|
lim = uint(len(in))
|
||||||
|
}
|
||||||
|
|
||||||
|
return in[0:lim], nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// OffsetIfAddrs returns a slice of IfAddrs based on the specified offset.
|
||||||
|
func OffsetIfAddrs(off int, in IfAddrs) (IfAddrs, error) {
|
||||||
|
var end bool
|
||||||
|
if off < 0 {
|
||||||
|
end = true
|
||||||
|
off = off * -1
|
||||||
|
}
|
||||||
|
|
||||||
|
if off > len(in) {
|
||||||
|
return IfAddrs{}, fmt.Errorf("unable to seek past the end of the interface array: offset (%d) exceeds the number of interfaces (%d)", off, len(in))
|
||||||
|
}
|
||||||
|
|
||||||
|
if end {
|
||||||
|
return in[len(in)-off:], nil
|
||||||
|
}
|
||||||
|
return in[off:], nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ifAddr IfAddr) String() string {
|
||||||
|
return fmt.Sprintf("%s %v", ifAddr.SockAddr, ifAddr.Interface)
|
||||||
|
}
|
||||||
|
|
||||||
|
// parseDefaultIfNameFromRoute parses standard route(8)'s output for the *BSDs
|
||||||
|
// and Solaris.
|
||||||
|
func parseDefaultIfNameFromRoute(routeOut string) (string, error) {
|
||||||
|
lines := strings.Split(routeOut, "\n")
|
||||||
|
for _, line := range lines {
|
||||||
|
kvs := strings.SplitN(line, ":", 2)
|
||||||
|
if len(kvs) != 2 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if strings.TrimSpace(kvs[0]) == "interface" {
|
||||||
|
ifName := strings.TrimSpace(kvs[1])
|
||||||
|
return ifName, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return "", errors.New("No default interface found")
|
||||||
|
}
|
||||||
|
|
||||||
|
// parseDefaultIfNameFromIPCmd parses the default interface from ip(8) for
|
||||||
|
// Linux.
|
||||||
|
func parseDefaultIfNameFromIPCmd(routeOut string) (string, error) {
|
||||||
|
lines := strings.Split(routeOut, "\n")
|
||||||
|
re := regexp.MustCompile(`[\s]+`)
|
||||||
|
for _, line := range lines {
|
||||||
|
kvs := re.Split(line, -1)
|
||||||
|
if len(kvs) < 5 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if kvs[0] == "default" &&
|
||||||
|
kvs[1] == "via" &&
|
||||||
|
kvs[3] == "dev" {
|
||||||
|
ifName := strings.TrimSpace(kvs[4])
|
||||||
|
return ifName, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return "", errors.New("No default interface found")
|
||||||
|
}
|
||||||
|
|
||||||
|
// parseDefaultIfNameWindows parses the default interface from `netstat -rn` and
|
||||||
|
// `ipconfig` on Windows.
|
||||||
|
func parseDefaultIfNameWindows(routeOut, ipconfigOut string) (string, error) {
|
||||||
|
defaultIPAddr, err := parseDefaultIPAddrWindowsRoute(routeOut)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
ifName, err := parseDefaultIfNameWindowsIPConfig(defaultIPAddr, ipconfigOut)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
return ifName, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// parseDefaultIPAddrWindowsRoute parses the IP address on the default interface
|
||||||
|
// `netstat -rn`.
|
||||||
|
//
|
||||||
|
// NOTES(sean): Only IPv4 addresses are parsed at this time. If you have an
|
||||||
|
// IPv6 connected host, submit an issue on github.com/hashicorp/go-sockaddr with
|
||||||
|
// the output from `netstat -rn`, `ipconfig`, and version of Windows to see IPv6
|
||||||
|
// support added.
|
||||||
|
func parseDefaultIPAddrWindowsRoute(routeOut string) (string, error) {
|
||||||
|
lines := strings.Split(routeOut, "\n")
|
||||||
|
re := regexp.MustCompile(`[\s]+`)
|
||||||
|
for _, line := range lines {
|
||||||
|
kvs := re.Split(strings.TrimSpace(line), -1)
|
||||||
|
if len(kvs) < 3 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if kvs[0] == "0.0.0.0" && kvs[1] == "0.0.0.0" {
|
||||||
|
defaultIPAddr := strings.TrimSpace(kvs[3])
|
||||||
|
return defaultIPAddr, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return "", errors.New("No IP on default interface found")
|
||||||
|
}
|
||||||
|
|
||||||
|
// parseDefaultIfNameWindowsIPConfig parses the output of `ipconfig` to find the
|
||||||
|
// interface name forwarding traffic to the default gateway.
|
||||||
|
func parseDefaultIfNameWindowsIPConfig(defaultIPAddr, routeOut string) (string, error) {
|
||||||
|
lines := strings.Split(routeOut, "\n")
|
||||||
|
ifNameRE := regexp.MustCompile(`^Ethernet adapter ([^\s:]+):`)
|
||||||
|
ipAddrRE := regexp.MustCompile(`^ IPv[46] Address\. \. \. \. \. \. \. \. \. \. \. : ([^\s]+)`)
|
||||||
|
var ifName string
|
||||||
|
for _, line := range lines {
|
||||||
|
switch ifNameMatches := ifNameRE.FindStringSubmatch(line); {
|
||||||
|
case len(ifNameMatches) > 1:
|
||||||
|
ifName = ifNameMatches[1]
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
switch ipAddrMatches := ipAddrRE.FindStringSubmatch(line); {
|
||||||
|
case len(ipAddrMatches) > 1 && ipAddrMatches[1] == defaultIPAddr:
|
||||||
|
return ifName, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return "", errors.New("No default interface found with matching IP")
|
||||||
|
}
|
65
vendor/github.com/hashicorp/go-sockaddr/ifattr.go
generated
vendored
Normal file
65
vendor/github.com/hashicorp/go-sockaddr/ifattr.go
generated
vendored
Normal file
|
@ -0,0 +1,65 @@
|
||||||
|
package sockaddr
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
)
|
||||||
|
|
||||||
|
// IfAddr is a union of a SockAddr and a net.Interface.
|
||||||
|
type IfAddr struct {
|
||||||
|
SockAddr
|
||||||
|
net.Interface
|
||||||
|
}
|
||||||
|
|
||||||
|
// Attr returns the named attribute as a string
|
||||||
|
func (ifAddr IfAddr) Attr(attrName AttrName) (string, error) {
|
||||||
|
val := IfAddrAttr(ifAddr, attrName)
|
||||||
|
if val != "" {
|
||||||
|
return val, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return Attr(ifAddr.SockAddr, attrName)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Attr returns the named attribute as a string
|
||||||
|
func Attr(sa SockAddr, attrName AttrName) (string, error) {
|
||||||
|
switch sockType := sa.Type(); {
|
||||||
|
case sockType&TypeIP != 0:
|
||||||
|
ip := *ToIPAddr(sa)
|
||||||
|
attrVal := IPAddrAttr(ip, attrName)
|
||||||
|
if attrVal != "" {
|
||||||
|
return attrVal, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if sockType == TypeIPv4 {
|
||||||
|
ipv4 := *ToIPv4Addr(sa)
|
||||||
|
attrVal := IPv4AddrAttr(ipv4, attrName)
|
||||||
|
if attrVal != "" {
|
||||||
|
return attrVal, nil
|
||||||
|
}
|
||||||
|
} else if sockType == TypeIPv6 {
|
||||||
|
ipv6 := *ToIPv6Addr(sa)
|
||||||
|
attrVal := IPv6AddrAttr(ipv6, attrName)
|
||||||
|
if attrVal != "" {
|
||||||
|
return attrVal, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
case sockType == TypeUnix:
|
||||||
|
us := *ToUnixSock(sa)
|
||||||
|
attrVal := UnixSockAttr(us, attrName)
|
||||||
|
if attrVal != "" {
|
||||||
|
return attrVal, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Non type-specific attributes
|
||||||
|
switch attrName {
|
||||||
|
case "string":
|
||||||
|
return sa.String(), nil
|
||||||
|
case "type":
|
||||||
|
return sa.Type().String(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return "", fmt.Errorf("unsupported attribute name %q", attrName)
|
||||||
|
}
|
169
vendor/github.com/hashicorp/go-sockaddr/ipaddr.go
generated
vendored
Normal file
169
vendor/github.com/hashicorp/go-sockaddr/ipaddr.go
generated
vendored
Normal file
|
@ -0,0 +1,169 @@
|
||||||
|
package sockaddr
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"math/big"
|
||||||
|
"net"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Constants for the sizes of IPv3, IPv4, and IPv6 address types.
|
||||||
|
const (
|
||||||
|
IPv3len = 6
|
||||||
|
IPv4len = 4
|
||||||
|
IPv6len = 16
|
||||||
|
)
|
||||||
|
|
||||||
|
// IPAddr is a generic IP address interface for IPv4 and IPv6 addresses,
|
||||||
|
// networks, and socket endpoints.
|
||||||
|
type IPAddr interface {
|
||||||
|
SockAddr
|
||||||
|
AddressBinString() string
|
||||||
|
AddressHexString() string
|
||||||
|
Cmp(SockAddr) int
|
||||||
|
CmpAddress(SockAddr) int
|
||||||
|
CmpPort(SockAddr) int
|
||||||
|
FirstUsable() IPAddr
|
||||||
|
Host() IPAddr
|
||||||
|
IPPort() IPPort
|
||||||
|
LastUsable() IPAddr
|
||||||
|
Maskbits() int
|
||||||
|
NetIP() *net.IP
|
||||||
|
NetIPMask() *net.IPMask
|
||||||
|
NetIPNet() *net.IPNet
|
||||||
|
Network() IPAddr
|
||||||
|
Octets() []int
|
||||||
|
}
|
||||||
|
|
||||||
|
// IPPort is the type for an IP port number for the TCP and UDP IP transports.
|
||||||
|
type IPPort uint16
|
||||||
|
|
||||||
|
// IPPrefixLen is a typed integer representing the prefix length for a given
|
||||||
|
// IPAddr.
|
||||||
|
type IPPrefixLen byte
|
||||||
|
|
||||||
|
// ipAddrAttrMap is a map of the IPAddr type-specific attributes.
|
||||||
|
var ipAddrAttrMap map[AttrName]func(IPAddr) string
|
||||||
|
var ipAddrAttrs []AttrName
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
ipAddrInit()
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewIPAddr creates a new IPAddr from a string. Returns nil if the string is
|
||||||
|
// not an IPv4 or an IPv6 address.
|
||||||
|
func NewIPAddr(addr string) (IPAddr, error) {
|
||||||
|
ipv4Addr, err := NewIPv4Addr(addr)
|
||||||
|
if err == nil {
|
||||||
|
return ipv4Addr, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
ipv6Addr, err := NewIPv6Addr(addr)
|
||||||
|
if err == nil {
|
||||||
|
return ipv6Addr, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, fmt.Errorf("invalid IPAddr %v", addr)
|
||||||
|
}
|
||||||
|
|
||||||
|
// IPAddrAttr returns a string representation of an attribute for the given
|
||||||
|
// IPAddr.
|
||||||
|
func IPAddrAttr(ip IPAddr, selector AttrName) string {
|
||||||
|
fn, found := ipAddrAttrMap[selector]
|
||||||
|
if !found {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
return fn(ip)
|
||||||
|
}
|
||||||
|
|
||||||
|
// IPAttrs returns a list of attributes supported by the IPAddr type
|
||||||
|
func IPAttrs() []AttrName {
|
||||||
|
return ipAddrAttrs
|
||||||
|
}
|
||||||
|
|
||||||
|
// MustIPAddr is a helper method that must return an IPAddr or panic on invalid
|
||||||
|
// input.
|
||||||
|
func MustIPAddr(addr string) IPAddr {
|
||||||
|
ip, err := NewIPAddr(addr)
|
||||||
|
if err != nil {
|
||||||
|
panic(fmt.Sprintf("Unable to create an IPAddr from %+q: %v", addr, err))
|
||||||
|
}
|
||||||
|
return ip
|
||||||
|
}
|
||||||
|
|
||||||
|
// ipAddrInit is called once at init()
|
||||||
|
func ipAddrInit() {
|
||||||
|
// Sorted for human readability
|
||||||
|
ipAddrAttrs = []AttrName{
|
||||||
|
"host",
|
||||||
|
"address",
|
||||||
|
"port",
|
||||||
|
"netmask",
|
||||||
|
"network",
|
||||||
|
"mask_bits",
|
||||||
|
"binary",
|
||||||
|
"hex",
|
||||||
|
"first_usable",
|
||||||
|
"last_usable",
|
||||||
|
"octets",
|
||||||
|
}
|
||||||
|
|
||||||
|
ipAddrAttrMap = map[AttrName]func(ip IPAddr) string{
|
||||||
|
"address": func(ip IPAddr) string {
|
||||||
|
return ip.NetIP().String()
|
||||||
|
},
|
||||||
|
"binary": func(ip IPAddr) string {
|
||||||
|
return ip.AddressBinString()
|
||||||
|
},
|
||||||
|
"first_usable": func(ip IPAddr) string {
|
||||||
|
return ip.FirstUsable().String()
|
||||||
|
},
|
||||||
|
"hex": func(ip IPAddr) string {
|
||||||
|
return ip.AddressHexString()
|
||||||
|
},
|
||||||
|
"host": func(ip IPAddr) string {
|
||||||
|
return ip.Host().String()
|
||||||
|
},
|
||||||
|
"last_usable": func(ip IPAddr) string {
|
||||||
|
return ip.LastUsable().String()
|
||||||
|
},
|
||||||
|
"mask_bits": func(ip IPAddr) string {
|
||||||
|
return fmt.Sprintf("%d", ip.Maskbits())
|
||||||
|
},
|
||||||
|
"netmask": func(ip IPAddr) string {
|
||||||
|
switch v := ip.(type) {
|
||||||
|
case IPv4Addr:
|
||||||
|
ipv4Mask := IPv4Addr{
|
||||||
|
Address: IPv4Address(v.Mask),
|
||||||
|
Mask: IPv4HostMask,
|
||||||
|
}
|
||||||
|
return ipv4Mask.String()
|
||||||
|
case IPv6Addr:
|
||||||
|
ipv6Mask := new(big.Int)
|
||||||
|
ipv6Mask.Set(v.Mask)
|
||||||
|
ipv6MaskAddr := IPv6Addr{
|
||||||
|
Address: IPv6Address(ipv6Mask),
|
||||||
|
Mask: ipv6HostMask,
|
||||||
|
}
|
||||||
|
return ipv6MaskAddr.String()
|
||||||
|
default:
|
||||||
|
return fmt.Sprintf("<unsupported type: %T>", ip)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"network": func(ip IPAddr) string {
|
||||||
|
return ip.Network().NetIP().String()
|
||||||
|
},
|
||||||
|
"octets": func(ip IPAddr) string {
|
||||||
|
octets := ip.Octets()
|
||||||
|
octetStrs := make([]string, 0, len(octets))
|
||||||
|
for _, octet := range octets {
|
||||||
|
octetStrs = append(octetStrs, fmt.Sprintf("%d", octet))
|
||||||
|
}
|
||||||
|
return strings.Join(octetStrs, " ")
|
||||||
|
},
|
||||||
|
"port": func(ip IPAddr) string {
|
||||||
|
return fmt.Sprintf("%d", ip.IPPort())
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
98
vendor/github.com/hashicorp/go-sockaddr/ipaddrs.go
generated
vendored
Normal file
98
vendor/github.com/hashicorp/go-sockaddr/ipaddrs.go
generated
vendored
Normal file
|
@ -0,0 +1,98 @@
|
||||||
|
package sockaddr
|
||||||
|
|
||||||
|
import "bytes"
|
||||||
|
|
||||||
|
type IPAddrs []IPAddr
|
||||||
|
|
||||||
|
func (s IPAddrs) Len() int { return len(s) }
|
||||||
|
func (s IPAddrs) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
|
||||||
|
|
||||||
|
// // SortIPAddrsByCmp is a type that satisfies sort.Interface and can be used
|
||||||
|
// // by the routines in this package. The SortIPAddrsByCmp type is used to
|
||||||
|
// // sort IPAddrs by Cmp()
|
||||||
|
// type SortIPAddrsByCmp struct{ IPAddrs }
|
||||||
|
|
||||||
|
// // Less reports whether the element with index i should sort before the
|
||||||
|
// // element with index j.
|
||||||
|
// func (s SortIPAddrsByCmp) Less(i, j int) bool {
|
||||||
|
// // Sort by Type, then address, then port number.
|
||||||
|
// return Less(s.IPAddrs[i], s.IPAddrs[j])
|
||||||
|
// }
|
||||||
|
|
||||||
|
// SortIPAddrsBySpecificMaskLen is a type that satisfies sort.Interface and
|
||||||
|
// can be used by the routines in this package. The
|
||||||
|
// SortIPAddrsBySpecificMaskLen type is used to sort IPAddrs by smallest
|
||||||
|
// network (most specific to largest network).
|
||||||
|
type SortIPAddrsByNetworkSize struct{ IPAddrs }
|
||||||
|
|
||||||
|
// Less reports whether the element with index i should sort before the
|
||||||
|
// element with index j.
|
||||||
|
func (s SortIPAddrsByNetworkSize) Less(i, j int) bool {
|
||||||
|
// Sort masks with a larger binary value (i.e. fewer hosts per network
|
||||||
|
// prefix) after masks with a smaller value (larger number of hosts per
|
||||||
|
// prefix).
|
||||||
|
switch bytes.Compare([]byte(*s.IPAddrs[i].NetIPMask()), []byte(*s.IPAddrs[j].NetIPMask())) {
|
||||||
|
case 0:
|
||||||
|
// Fall through to the second test if the net.IPMasks are the
|
||||||
|
// same.
|
||||||
|
break
|
||||||
|
case 1:
|
||||||
|
return true
|
||||||
|
case -1:
|
||||||
|
return false
|
||||||
|
default:
|
||||||
|
panic("bad, m'kay?")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sort IPs based on the length (i.e. prefer IPv4 over IPv6).
|
||||||
|
iLen := len(*s.IPAddrs[i].NetIP())
|
||||||
|
jLen := len(*s.IPAddrs[j].NetIP())
|
||||||
|
if iLen != jLen {
|
||||||
|
return iLen > jLen
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sort IPs based on their network address from lowest to highest.
|
||||||
|
switch bytes.Compare(s.IPAddrs[i].NetIPNet().IP, s.IPAddrs[j].NetIPNet().IP) {
|
||||||
|
case 0:
|
||||||
|
break
|
||||||
|
case 1:
|
||||||
|
return false
|
||||||
|
case -1:
|
||||||
|
return true
|
||||||
|
default:
|
||||||
|
panic("lol wut?")
|
||||||
|
}
|
||||||
|
|
||||||
|
// If a host does not have a port set, it always sorts after hosts
|
||||||
|
// that have a port (e.g. a host with a /32 and port number is more
|
||||||
|
// specific and should sort first over a host with a /32 but no port
|
||||||
|
// set).
|
||||||
|
if s.IPAddrs[i].IPPort() == 0 || s.IPAddrs[j].IPPort() == 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return s.IPAddrs[i].IPPort() < s.IPAddrs[j].IPPort()
|
||||||
|
}
|
||||||
|
|
||||||
|
// SortIPAddrsBySpecificMaskLen is a type that satisfies sort.Interface and
|
||||||
|
// can be used by the routines in this package. The
|
||||||
|
// SortIPAddrsBySpecificMaskLen type is used to sort IPAddrs by smallest
|
||||||
|
// network (most specific to largest network).
|
||||||
|
type SortIPAddrsBySpecificMaskLen struct{ IPAddrs }
|
||||||
|
|
||||||
|
// Less reports whether the element with index i should sort before the
|
||||||
|
// element with index j.
|
||||||
|
func (s SortIPAddrsBySpecificMaskLen) Less(i, j int) bool {
|
||||||
|
return s.IPAddrs[i].Maskbits() > s.IPAddrs[j].Maskbits()
|
||||||
|
}
|
||||||
|
|
||||||
|
// SortIPAddrsByBroadMaskLen is a type that satisfies sort.Interface and can
|
||||||
|
// be used by the routines in this package. The SortIPAddrsByBroadMaskLen
|
||||||
|
// type is used to sort IPAddrs by largest network (i.e. largest subnets
|
||||||
|
// first).
|
||||||
|
type SortIPAddrsByBroadMaskLen struct{ IPAddrs }
|
||||||
|
|
||||||
|
// Less reports whether the element with index i should sort before the
|
||||||
|
// element with index j.
|
||||||
|
func (s SortIPAddrsByBroadMaskLen) Less(i, j int) bool {
|
||||||
|
return s.IPAddrs[i].Maskbits() < s.IPAddrs[j].Maskbits()
|
||||||
|
}
|
515
vendor/github.com/hashicorp/go-sockaddr/ipv4addr.go
generated
vendored
Normal file
515
vendor/github.com/hashicorp/go-sockaddr/ipv4addr.go
generated
vendored
Normal file
|
@ -0,0 +1,515 @@
|
||||||
|
package sockaddr
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/binary"
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
"regexp"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
type (
|
||||||
|
// IPv4Address is a named type representing an IPv4 address.
|
||||||
|
IPv4Address uint32
|
||||||
|
|
||||||
|
// IPv4Network is a named type representing an IPv4 network.
|
||||||
|
IPv4Network uint32
|
||||||
|
|
||||||
|
// IPv4Mask is a named type representing an IPv4 network mask.
|
||||||
|
IPv4Mask uint32
|
||||||
|
)
|
||||||
|
|
||||||
|
// IPv4HostMask is a constant represents a /32 IPv4 Address
|
||||||
|
// (i.e. 255.255.255.255).
|
||||||
|
const IPv4HostMask = IPv4Mask(0xffffffff)
|
||||||
|
|
||||||
|
// ipv4AddrAttrMap is a map of the IPv4Addr type-specific attributes.
|
||||||
|
var ipv4AddrAttrMap map[AttrName]func(IPv4Addr) string
|
||||||
|
var ipv4AddrAttrs []AttrName
|
||||||
|
var trailingHexNetmaskRE *regexp.Regexp
|
||||||
|
|
||||||
|
// IPv4Addr implements a convenience wrapper around the union of Go's
|
||||||
|
// built-in net.IP and net.IPNet types. In UNIX-speak, IPv4Addr implements
|
||||||
|
// `sockaddr` when the the address family is set to AF_INET
|
||||||
|
// (i.e. `sockaddr_in`).
|
||||||
|
type IPv4Addr struct {
|
||||||
|
IPAddr
|
||||||
|
Address IPv4Address
|
||||||
|
Mask IPv4Mask
|
||||||
|
Port IPPort
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
ipv4AddrInit()
|
||||||
|
trailingHexNetmaskRE = regexp.MustCompile(`/([0f]{8})$`)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewIPv4Addr creates an IPv4Addr from a string. String can be in the form
|
||||||
|
// of either an IPv4:port (e.g. `1.2.3.4:80`, in which case the mask is
|
||||||
|
// assumed to be a `/32`), an IPv4 address (e.g. `1.2.3.4`, also with a `/32`
|
||||||
|
// mask), or an IPv4 CIDR (e.g. `1.2.3.4/24`, which has its IP port
|
||||||
|
// initialized to zero). ipv4Str can not be a hostname.
|
||||||
|
//
|
||||||
|
// NOTE: Many net.*() routines will initialize and return an IPv6 address.
|
||||||
|
// To create uint32 values from net.IP, always test to make sure the address
|
||||||
|
// returned can be converted to a 4 byte array using To4().
|
||||||
|
func NewIPv4Addr(ipv4Str string) (IPv4Addr, error) {
|
||||||
|
// Strip off any bogus hex-encoded netmasks that will be mis-parsed by Go. In
|
||||||
|
// particular, clients with the Barracuda VPN client will see something like:
|
||||||
|
// `192.168.3.51/00ffffff` as their IP address.
|
||||||
|
if match := trailingHexNetmaskRE.FindStringIndex(ipv4Str); match != nil {
|
||||||
|
ipv4Str = ipv4Str[:match[0]]
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse as an IPv4 CIDR
|
||||||
|
ipAddr, network, err := net.ParseCIDR(ipv4Str)
|
||||||
|
if err == nil {
|
||||||
|
ipv4 := ipAddr.To4()
|
||||||
|
if ipv4 == nil {
|
||||||
|
return IPv4Addr{}, fmt.Errorf("Unable to convert %s to an IPv4 address", ipv4Str)
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we see an IPv6 netmask, convert it to an IPv4 mask.
|
||||||
|
netmaskSepPos := strings.LastIndexByte(ipv4Str, '/')
|
||||||
|
if netmaskSepPos != -1 && netmaskSepPos+1 < len(ipv4Str) {
|
||||||
|
netMask, err := strconv.ParseUint(ipv4Str[netmaskSepPos+1:], 10, 8)
|
||||||
|
if err != nil {
|
||||||
|
return IPv4Addr{}, fmt.Errorf("Unable to convert %s to an IPv4 address: unable to parse CIDR netmask: %v", ipv4Str, err)
|
||||||
|
} else if netMask > 128 {
|
||||||
|
return IPv4Addr{}, fmt.Errorf("Unable to convert %s to an IPv4 address: invalid CIDR netmask", ipv4Str)
|
||||||
|
}
|
||||||
|
|
||||||
|
if netMask >= 96 {
|
||||||
|
// Convert the IPv6 netmask to an IPv4 netmask
|
||||||
|
network.Mask = net.CIDRMask(int(netMask-96), IPv4len*8)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ipv4Addr := IPv4Addr{
|
||||||
|
Address: IPv4Address(binary.BigEndian.Uint32(ipv4)),
|
||||||
|
Mask: IPv4Mask(binary.BigEndian.Uint32(network.Mask)),
|
||||||
|
}
|
||||||
|
return ipv4Addr, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Attempt to parse ipv4Str as a /32 host with a port number.
|
||||||
|
tcpAddr, err := net.ResolveTCPAddr("tcp4", ipv4Str)
|
||||||
|
if err == nil {
|
||||||
|
ipv4 := tcpAddr.IP.To4()
|
||||||
|
if ipv4 == nil {
|
||||||
|
return IPv4Addr{}, fmt.Errorf("Unable to resolve %+q as an IPv4 address", ipv4Str)
|
||||||
|
}
|
||||||
|
|
||||||
|
ipv4Uint32 := binary.BigEndian.Uint32(ipv4)
|
||||||
|
ipv4Addr := IPv4Addr{
|
||||||
|
Address: IPv4Address(ipv4Uint32),
|
||||||
|
Mask: IPv4HostMask,
|
||||||
|
Port: IPPort(tcpAddr.Port),
|
||||||
|
}
|
||||||
|
|
||||||
|
return ipv4Addr, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse as a naked IPv4 address
|
||||||
|
ip := net.ParseIP(ipv4Str)
|
||||||
|
if ip != nil {
|
||||||
|
ipv4 := ip.To4()
|
||||||
|
if ipv4 == nil {
|
||||||
|
return IPv4Addr{}, fmt.Errorf("Unable to string convert %+q to an IPv4 address", ipv4Str)
|
||||||
|
}
|
||||||
|
|
||||||
|
ipv4Uint32 := binary.BigEndian.Uint32(ipv4)
|
||||||
|
ipv4Addr := IPv4Addr{
|
||||||
|
Address: IPv4Address(ipv4Uint32),
|
||||||
|
Mask: IPv4HostMask,
|
||||||
|
}
|
||||||
|
return ipv4Addr, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return IPv4Addr{}, fmt.Errorf("Unable to parse %+q to an IPv4 address: %v", ipv4Str, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddressBinString returns a string with the IPv4Addr's Address represented
|
||||||
|
// as a sequence of '0' and '1' characters. This method is useful for
|
||||||
|
// debugging or by operators who want to inspect an address.
|
||||||
|
func (ipv4 IPv4Addr) AddressBinString() string {
|
||||||
|
return fmt.Sprintf("%032s", strconv.FormatUint(uint64(ipv4.Address), 2))
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddressHexString returns a string with the IPv4Addr address represented as
|
||||||
|
// a sequence of hex characters. This method is useful for debugging or by
|
||||||
|
// operators who want to inspect an address.
|
||||||
|
func (ipv4 IPv4Addr) AddressHexString() string {
|
||||||
|
return fmt.Sprintf("%08s", strconv.FormatUint(uint64(ipv4.Address), 16))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Broadcast is an IPv4Addr-only method that returns the broadcast address of
|
||||||
|
// the network.
|
||||||
|
//
|
||||||
|
// NOTE: IPv6 only supports multicast, so this method only exists for
|
||||||
|
// IPv4Addr.
|
||||||
|
func (ipv4 IPv4Addr) Broadcast() IPAddr {
|
||||||
|
// Nothing should listen on a broadcast address.
|
||||||
|
return IPv4Addr{
|
||||||
|
Address: IPv4Address(ipv4.BroadcastAddress()),
|
||||||
|
Mask: IPv4HostMask,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// BroadcastAddress returns a IPv4Network of the IPv4Addr's broadcast
|
||||||
|
// address.
|
||||||
|
func (ipv4 IPv4Addr) BroadcastAddress() IPv4Network {
|
||||||
|
return IPv4Network(uint32(ipv4.Address)&uint32(ipv4.Mask) | ^uint32(ipv4.Mask))
|
||||||
|
}
|
||||||
|
|
||||||
|
// CmpAddress follows the Cmp() standard protocol and returns:
|
||||||
|
//
|
||||||
|
// - -1 If the receiver should sort first because its address is lower than arg
|
||||||
|
// - 0 if the SockAddr arg is equal to the receiving IPv4Addr or the argument is
|
||||||
|
// of a different type.
|
||||||
|
// - 1 If the argument should sort first.
|
||||||
|
func (ipv4 IPv4Addr) CmpAddress(sa SockAddr) int {
|
||||||
|
ipv4b, ok := sa.(IPv4Addr)
|
||||||
|
if !ok {
|
||||||
|
return sortDeferDecision
|
||||||
|
}
|
||||||
|
|
||||||
|
switch {
|
||||||
|
case ipv4.Address == ipv4b.Address:
|
||||||
|
return sortDeferDecision
|
||||||
|
case ipv4.Address < ipv4b.Address:
|
||||||
|
return sortReceiverBeforeArg
|
||||||
|
default:
|
||||||
|
return sortArgBeforeReceiver
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// CmpPort follows the Cmp() standard protocol and returns:
|
||||||
|
//
|
||||||
|
// - -1 If the receiver should sort first because its port is lower than arg
|
||||||
|
// - 0 if the SockAddr arg's port number is equal to the receiving IPv4Addr,
|
||||||
|
// regardless of type.
|
||||||
|
// - 1 If the argument should sort first.
|
||||||
|
func (ipv4 IPv4Addr) CmpPort(sa SockAddr) int {
|
||||||
|
var saPort IPPort
|
||||||
|
switch v := sa.(type) {
|
||||||
|
case IPv4Addr:
|
||||||
|
saPort = v.Port
|
||||||
|
case IPv6Addr:
|
||||||
|
saPort = v.Port
|
||||||
|
default:
|
||||||
|
return sortDeferDecision
|
||||||
|
}
|
||||||
|
|
||||||
|
switch {
|
||||||
|
case ipv4.Port == saPort:
|
||||||
|
return sortDeferDecision
|
||||||
|
case ipv4.Port < saPort:
|
||||||
|
return sortReceiverBeforeArg
|
||||||
|
default:
|
||||||
|
return sortArgBeforeReceiver
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// CmpRFC follows the Cmp() standard protocol and returns:
|
||||||
|
//
|
||||||
|
// - -1 If the receiver should sort first because it belongs to the RFC and its
|
||||||
|
// arg does not
|
||||||
|
// - 0 if the receiver and arg both belong to the same RFC or neither do.
|
||||||
|
// - 1 If the arg belongs to the RFC but receiver does not.
|
||||||
|
func (ipv4 IPv4Addr) CmpRFC(rfcNum uint, sa SockAddr) int {
|
||||||
|
recvInRFC := IsRFC(rfcNum, ipv4)
|
||||||
|
ipv4b, ok := sa.(IPv4Addr)
|
||||||
|
if !ok {
|
||||||
|
// If the receiver is part of the desired RFC and the SockAddr
|
||||||
|
// argument is not, return -1 so that the receiver sorts before
|
||||||
|
// the non-IPv4 SockAddr. Conversely, if the receiver is not
|
||||||
|
// part of the RFC, punt on sorting and leave it for the next
|
||||||
|
// sorter.
|
||||||
|
if recvInRFC {
|
||||||
|
return sortReceiverBeforeArg
|
||||||
|
} else {
|
||||||
|
return sortDeferDecision
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
argInRFC := IsRFC(rfcNum, ipv4b)
|
||||||
|
switch {
|
||||||
|
case (recvInRFC && argInRFC), (!recvInRFC && !argInRFC):
|
||||||
|
// If a and b both belong to the RFC, or neither belong to
|
||||||
|
// rfcNum, defer sorting to the next sorter.
|
||||||
|
return sortDeferDecision
|
||||||
|
case recvInRFC && !argInRFC:
|
||||||
|
return sortReceiverBeforeArg
|
||||||
|
default:
|
||||||
|
return sortArgBeforeReceiver
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Contains returns true if the SockAddr is contained within the receiver.
|
||||||
|
func (ipv4 IPv4Addr) Contains(sa SockAddr) bool {
|
||||||
|
ipv4b, ok := sa.(IPv4Addr)
|
||||||
|
if !ok {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return ipv4.ContainsNetwork(ipv4b)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ContainsAddress returns true if the IPv4Address is contained within the
|
||||||
|
// receiver.
|
||||||
|
func (ipv4 IPv4Addr) ContainsAddress(x IPv4Address) bool {
|
||||||
|
return IPv4Address(ipv4.NetworkAddress()) <= x &&
|
||||||
|
IPv4Address(ipv4.BroadcastAddress()) >= x
|
||||||
|
}
|
||||||
|
|
||||||
|
// ContainsNetwork returns true if the network from IPv4Addr is contained
|
||||||
|
// within the receiver.
|
||||||
|
func (ipv4 IPv4Addr) ContainsNetwork(x IPv4Addr) bool {
|
||||||
|
return ipv4.NetworkAddress() <= x.NetworkAddress() &&
|
||||||
|
ipv4.BroadcastAddress() >= x.BroadcastAddress()
|
||||||
|
}
|
||||||
|
|
||||||
|
// DialPacketArgs returns the arguments required to be passed to
|
||||||
|
// net.DialUDP(). If the Mask of ipv4 is not a /32 or the Port is 0,
|
||||||
|
// DialPacketArgs() will fail. See Host() to create an IPv4Addr with its
|
||||||
|
// mask set to /32.
|
||||||
|
func (ipv4 IPv4Addr) DialPacketArgs() (network, dialArgs string) {
|
||||||
|
if ipv4.Mask != IPv4HostMask || ipv4.Port == 0 {
|
||||||
|
return "udp4", ""
|
||||||
|
}
|
||||||
|
return "udp4", fmt.Sprintf("%s:%d", ipv4.NetIP().String(), ipv4.Port)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DialStreamArgs returns the arguments required to be passed to
|
||||||
|
// net.DialTCP(). If the Mask of ipv4 is not a /32 or the Port is 0,
|
||||||
|
// DialStreamArgs() will fail. See Host() to create an IPv4Addr with its
|
||||||
|
// mask set to /32.
|
||||||
|
func (ipv4 IPv4Addr) DialStreamArgs() (network, dialArgs string) {
|
||||||
|
if ipv4.Mask != IPv4HostMask || ipv4.Port == 0 {
|
||||||
|
return "tcp4", ""
|
||||||
|
}
|
||||||
|
return "tcp4", fmt.Sprintf("%s:%d", ipv4.NetIP().String(), ipv4.Port)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Equal returns true if a SockAddr is equal to the receiving IPv4Addr.
|
||||||
|
func (ipv4 IPv4Addr) Equal(sa SockAddr) bool {
|
||||||
|
ipv4b, ok := sa.(IPv4Addr)
|
||||||
|
if !ok {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if ipv4.Port != ipv4b.Port {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if ipv4.Address != ipv4b.Address {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if ipv4.NetIPNet().String() != ipv4b.NetIPNet().String() {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// FirstUsable returns an IPv4Addr set to the first address following the
|
||||||
|
// network prefix. The first usable address in a network is normally the
|
||||||
|
// gateway and should not be used except by devices forwarding packets
|
||||||
|
// between two administratively distinct networks (i.e. a router). This
|
||||||
|
// function does not discriminate against first usable vs "first address that
|
||||||
|
// should be used." For example, FirstUsable() on "192.168.1.10/24" would
|
||||||
|
// return the address "192.168.1.1/24".
|
||||||
|
func (ipv4 IPv4Addr) FirstUsable() IPAddr {
|
||||||
|
addr := ipv4.NetworkAddress()
|
||||||
|
|
||||||
|
// If /32, return the address itself. If /31 assume a point-to-point
|
||||||
|
// link and return the lower address.
|
||||||
|
if ipv4.Maskbits() < 31 {
|
||||||
|
addr++
|
||||||
|
}
|
||||||
|
|
||||||
|
return IPv4Addr{
|
||||||
|
Address: IPv4Address(addr),
|
||||||
|
Mask: IPv4HostMask,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Host returns a copy of ipv4 with its mask set to /32 so that it can be
|
||||||
|
// used by DialPacketArgs(), DialStreamArgs(), ListenPacketArgs(), or
|
||||||
|
// ListenStreamArgs().
|
||||||
|
func (ipv4 IPv4Addr) Host() IPAddr {
|
||||||
|
// Nothing should listen on a broadcast address.
|
||||||
|
return IPv4Addr{
|
||||||
|
Address: ipv4.Address,
|
||||||
|
Mask: IPv4HostMask,
|
||||||
|
Port: ipv4.Port,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// IPPort returns the Port number attached to the IPv4Addr
|
||||||
|
func (ipv4 IPv4Addr) IPPort() IPPort {
|
||||||
|
return ipv4.Port
|
||||||
|
}
|
||||||
|
|
||||||
|
// LastUsable returns the last address before the broadcast address in a
|
||||||
|
// given network.
|
||||||
|
func (ipv4 IPv4Addr) LastUsable() IPAddr {
|
||||||
|
addr := ipv4.BroadcastAddress()
|
||||||
|
|
||||||
|
// If /32, return the address itself. If /31 assume a point-to-point
|
||||||
|
// link and return the upper address.
|
||||||
|
if ipv4.Maskbits() < 31 {
|
||||||
|
addr--
|
||||||
|
}
|
||||||
|
|
||||||
|
return IPv4Addr{
|
||||||
|
Address: IPv4Address(addr),
|
||||||
|
Mask: IPv4HostMask,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListenPacketArgs returns the arguments required to be passed to
|
||||||
|
// net.ListenUDP(). If the Mask of ipv4 is not a /32, ListenPacketArgs()
|
||||||
|
// will fail. See Host() to create an IPv4Addr with its mask set to /32.
|
||||||
|
func (ipv4 IPv4Addr) ListenPacketArgs() (network, listenArgs string) {
|
||||||
|
if ipv4.Mask != IPv4HostMask {
|
||||||
|
return "udp4", ""
|
||||||
|
}
|
||||||
|
return "udp4", fmt.Sprintf("%s:%d", ipv4.NetIP().String(), ipv4.Port)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListenStreamArgs returns the arguments required to be passed to
|
||||||
|
// net.ListenTCP(). If the Mask of ipv4 is not a /32, ListenStreamArgs()
|
||||||
|
// will fail. See Host() to create an IPv4Addr with its mask set to /32.
|
||||||
|
func (ipv4 IPv4Addr) ListenStreamArgs() (network, listenArgs string) {
|
||||||
|
if ipv4.Mask != IPv4HostMask {
|
||||||
|
return "tcp4", ""
|
||||||
|
}
|
||||||
|
return "tcp4", fmt.Sprintf("%s:%d", ipv4.NetIP().String(), ipv4.Port)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Maskbits returns the number of network mask bits in a given IPv4Addr. For
|
||||||
|
// example, the Maskbits() of "192.168.1.1/24" would return 24.
|
||||||
|
func (ipv4 IPv4Addr) Maskbits() int {
|
||||||
|
mask := make(net.IPMask, IPv4len)
|
||||||
|
binary.BigEndian.PutUint32(mask, uint32(ipv4.Mask))
|
||||||
|
maskOnes, _ := mask.Size()
|
||||||
|
return maskOnes
|
||||||
|
}
|
||||||
|
|
||||||
|
// MustIPv4Addr is a helper method that must return an IPv4Addr or panic on
|
||||||
|
// invalid input.
|
||||||
|
func MustIPv4Addr(addr string) IPv4Addr {
|
||||||
|
ipv4, err := NewIPv4Addr(addr)
|
||||||
|
if err != nil {
|
||||||
|
panic(fmt.Sprintf("Unable to create an IPv4Addr from %+q: %v", addr, err))
|
||||||
|
}
|
||||||
|
return ipv4
|
||||||
|
}
|
||||||
|
|
||||||
|
// NetIP returns the address as a net.IP (address is always presized to
|
||||||
|
// IPv4).
|
||||||
|
func (ipv4 IPv4Addr) NetIP() *net.IP {
|
||||||
|
x := make(net.IP, IPv4len)
|
||||||
|
binary.BigEndian.PutUint32(x, uint32(ipv4.Address))
|
||||||
|
return &x
|
||||||
|
}
|
||||||
|
|
||||||
|
// NetIPMask create a new net.IPMask from the IPv4Addr.
|
||||||
|
func (ipv4 IPv4Addr) NetIPMask() *net.IPMask {
|
||||||
|
ipv4Mask := net.IPMask{}
|
||||||
|
ipv4Mask = make(net.IPMask, IPv4len)
|
||||||
|
binary.BigEndian.PutUint32(ipv4Mask, uint32(ipv4.Mask))
|
||||||
|
return &ipv4Mask
|
||||||
|
}
|
||||||
|
|
||||||
|
// NetIPNet create a new net.IPNet from the IPv4Addr.
|
||||||
|
func (ipv4 IPv4Addr) NetIPNet() *net.IPNet {
|
||||||
|
ipv4net := &net.IPNet{}
|
||||||
|
ipv4net.IP = make(net.IP, IPv4len)
|
||||||
|
binary.BigEndian.PutUint32(ipv4net.IP, uint32(ipv4.NetworkAddress()))
|
||||||
|
ipv4net.Mask = *ipv4.NetIPMask()
|
||||||
|
return ipv4net
|
||||||
|
}
|
||||||
|
|
||||||
|
// Network returns the network prefix or network address for a given network.
|
||||||
|
func (ipv4 IPv4Addr) Network() IPAddr {
|
||||||
|
return IPv4Addr{
|
||||||
|
Address: IPv4Address(ipv4.NetworkAddress()),
|
||||||
|
Mask: ipv4.Mask,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NetworkAddress returns an IPv4Network of the IPv4Addr's network address.
|
||||||
|
func (ipv4 IPv4Addr) NetworkAddress() IPv4Network {
|
||||||
|
return IPv4Network(uint32(ipv4.Address) & uint32(ipv4.Mask))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Octets returns a slice of the four octets in an IPv4Addr's Address. The
|
||||||
|
// order of the bytes is big endian.
|
||||||
|
func (ipv4 IPv4Addr) Octets() []int {
|
||||||
|
return []int{
|
||||||
|
int(ipv4.Address >> 24),
|
||||||
|
int((ipv4.Address >> 16) & 0xff),
|
||||||
|
int((ipv4.Address >> 8) & 0xff),
|
||||||
|
int(ipv4.Address & 0xff),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// String returns a string representation of the IPv4Addr
|
||||||
|
func (ipv4 IPv4Addr) String() string {
|
||||||
|
if ipv4.Port != 0 {
|
||||||
|
return fmt.Sprintf("%s:%d", ipv4.NetIP().String(), ipv4.Port)
|
||||||
|
}
|
||||||
|
|
||||||
|
if ipv4.Maskbits() == 32 {
|
||||||
|
return ipv4.NetIP().String()
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt.Sprintf("%s/%d", ipv4.NetIP().String(), ipv4.Maskbits())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Type is used as a type switch and returns TypeIPv4
|
||||||
|
func (IPv4Addr) Type() SockAddrType {
|
||||||
|
return TypeIPv4
|
||||||
|
}
|
||||||
|
|
||||||
|
// IPv4AddrAttr returns a string representation of an attribute for the given
|
||||||
|
// IPv4Addr.
|
||||||
|
func IPv4AddrAttr(ipv4 IPv4Addr, selector AttrName) string {
|
||||||
|
fn, found := ipv4AddrAttrMap[selector]
|
||||||
|
if !found {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
return fn(ipv4)
|
||||||
|
}
|
||||||
|
|
||||||
|
// IPv4Attrs returns a list of attributes supported by the IPv4Addr type
|
||||||
|
func IPv4Attrs() []AttrName {
|
||||||
|
return ipv4AddrAttrs
|
||||||
|
}
|
||||||
|
|
||||||
|
// ipv4AddrInit is called once at init()
|
||||||
|
func ipv4AddrInit() {
|
||||||
|
// Sorted for human readability
|
||||||
|
ipv4AddrAttrs = []AttrName{
|
||||||
|
"size", // Same position as in IPv6 for output consistency
|
||||||
|
"broadcast",
|
||||||
|
"uint32",
|
||||||
|
}
|
||||||
|
|
||||||
|
ipv4AddrAttrMap = map[AttrName]func(ipv4 IPv4Addr) string{
|
||||||
|
"broadcast": func(ipv4 IPv4Addr) string {
|
||||||
|
return ipv4.Broadcast().String()
|
||||||
|
},
|
||||||
|
"size": func(ipv4 IPv4Addr) string {
|
||||||
|
return fmt.Sprintf("%d", 1<<uint(IPv4len*8-ipv4.Maskbits()))
|
||||||
|
},
|
||||||
|
"uint32": func(ipv4 IPv4Addr) string {
|
||||||
|
return fmt.Sprintf("%d", uint32(ipv4.Address))
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
591
vendor/github.com/hashicorp/go-sockaddr/ipv6addr.go
generated
vendored
Normal file
591
vendor/github.com/hashicorp/go-sockaddr/ipv6addr.go
generated
vendored
Normal file
|
@ -0,0 +1,591 @@
|
||||||
|
package sockaddr
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/binary"
|
||||||
|
"fmt"
|
||||||
|
"math/big"
|
||||||
|
"net"
|
||||||
|
)
|
||||||
|
|
||||||
|
type (
|
||||||
|
// IPv6Address is a named type representing an IPv6 address.
|
||||||
|
IPv6Address *big.Int
|
||||||
|
|
||||||
|
// IPv6Network is a named type representing an IPv6 network.
|
||||||
|
IPv6Network *big.Int
|
||||||
|
|
||||||
|
// IPv6Mask is a named type representing an IPv6 network mask.
|
||||||
|
IPv6Mask *big.Int
|
||||||
|
)
|
||||||
|
|
||||||
|
// IPv6HostPrefix is a constant represents a /128 IPv6 Prefix.
|
||||||
|
const IPv6HostPrefix = IPPrefixLen(128)
|
||||||
|
|
||||||
|
// ipv6HostMask is an unexported big.Int representing a /128 IPv6 address.
|
||||||
|
// This value must be a constant and always set to all ones.
|
||||||
|
var ipv6HostMask IPv6Mask
|
||||||
|
|
||||||
|
// ipv6AddrAttrMap is a map of the IPv6Addr type-specific attributes.
|
||||||
|
var ipv6AddrAttrMap map[AttrName]func(IPv6Addr) string
|
||||||
|
var ipv6AddrAttrs []AttrName
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
biMask := new(big.Int)
|
||||||
|
biMask.SetBytes([]byte{
|
||||||
|
0xff, 0xff,
|
||||||
|
0xff, 0xff,
|
||||||
|
0xff, 0xff,
|
||||||
|
0xff, 0xff,
|
||||||
|
0xff, 0xff,
|
||||||
|
0xff, 0xff,
|
||||||
|
0xff, 0xff,
|
||||||
|
0xff, 0xff,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
ipv6HostMask = IPv6Mask(biMask)
|
||||||
|
|
||||||
|
ipv6AddrInit()
|
||||||
|
}
|
||||||
|
|
||||||
|
// IPv6Addr implements a convenience wrapper around the union of Go's
|
||||||
|
// built-in net.IP and net.IPNet types. In UNIX-speak, IPv6Addr implements
|
||||||
|
// `sockaddr` when the the address family is set to AF_INET6
|
||||||
|
// (i.e. `sockaddr_in6`).
|
||||||
|
type IPv6Addr struct {
|
||||||
|
IPAddr
|
||||||
|
Address IPv6Address
|
||||||
|
Mask IPv6Mask
|
||||||
|
Port IPPort
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewIPv6Addr creates an IPv6Addr from a string. String can be in the form of
|
||||||
|
// an an IPv6:port (e.g. `[2001:4860:0:2001::68]:80`, in which case the mask is
|
||||||
|
// assumed to be a /128), an IPv6 address (e.g. `2001:4860:0:2001::68`, also
|
||||||
|
// with a `/128` mask), an IPv6 CIDR (e.g. `2001:4860:0:2001::68/64`, which has
|
||||||
|
// its IP port initialized to zero). ipv6Str can not be a hostname.
|
||||||
|
//
|
||||||
|
// NOTE: Many net.*() routines will initialize and return an IPv4 address.
|
||||||
|
// Always test to make sure the address returned cannot be converted to a 4 byte
|
||||||
|
// array using To4().
|
||||||
|
func NewIPv6Addr(ipv6Str string) (IPv6Addr, error) {
|
||||||
|
v6Addr := false
|
||||||
|
LOOP:
|
||||||
|
for i := 0; i < len(ipv6Str); i++ {
|
||||||
|
switch ipv6Str[i] {
|
||||||
|
case '.':
|
||||||
|
break LOOP
|
||||||
|
case ':':
|
||||||
|
v6Addr = true
|
||||||
|
break LOOP
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !v6Addr {
|
||||||
|
return IPv6Addr{}, fmt.Errorf("Unable to resolve %+q as an IPv6 address, appears to be an IPv4 address", ipv6Str)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Attempt to parse ipv6Str as a /128 host with a port number.
|
||||||
|
tcpAddr, err := net.ResolveTCPAddr("tcp6", ipv6Str)
|
||||||
|
if err == nil {
|
||||||
|
ipv6 := tcpAddr.IP.To16()
|
||||||
|
if ipv6 == nil {
|
||||||
|
return IPv6Addr{}, fmt.Errorf("Unable to resolve %+q as a 16byte IPv6 address", ipv6Str)
|
||||||
|
}
|
||||||
|
|
||||||
|
ipv6BigIntAddr := new(big.Int)
|
||||||
|
ipv6BigIntAddr.SetBytes(ipv6)
|
||||||
|
|
||||||
|
ipv6BigIntMask := new(big.Int)
|
||||||
|
ipv6BigIntMask.Set(ipv6HostMask)
|
||||||
|
|
||||||
|
ipv6Addr := IPv6Addr{
|
||||||
|
Address: IPv6Address(ipv6BigIntAddr),
|
||||||
|
Mask: IPv6Mask(ipv6BigIntMask),
|
||||||
|
Port: IPPort(tcpAddr.Port),
|
||||||
|
}
|
||||||
|
|
||||||
|
return ipv6Addr, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse as a naked IPv6 address. Trim square brackets if present.
|
||||||
|
if len(ipv6Str) > 2 && ipv6Str[0] == '[' && ipv6Str[len(ipv6Str)-1] == ']' {
|
||||||
|
ipv6Str = ipv6Str[1 : len(ipv6Str)-1]
|
||||||
|
}
|
||||||
|
ip := net.ParseIP(ipv6Str)
|
||||||
|
if ip != nil {
|
||||||
|
ipv6 := ip.To16()
|
||||||
|
if ipv6 == nil {
|
||||||
|
return IPv6Addr{}, fmt.Errorf("Unable to string convert %+q to a 16byte IPv6 address", ipv6Str)
|
||||||
|
}
|
||||||
|
|
||||||
|
ipv6BigIntAddr := new(big.Int)
|
||||||
|
ipv6BigIntAddr.SetBytes(ipv6)
|
||||||
|
|
||||||
|
ipv6BigIntMask := new(big.Int)
|
||||||
|
ipv6BigIntMask.Set(ipv6HostMask)
|
||||||
|
|
||||||
|
return IPv6Addr{
|
||||||
|
Address: IPv6Address(ipv6BigIntAddr),
|
||||||
|
Mask: IPv6Mask(ipv6BigIntMask),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse as an IPv6 CIDR
|
||||||
|
ipAddr, network, err := net.ParseCIDR(ipv6Str)
|
||||||
|
if err == nil {
|
||||||
|
ipv6 := ipAddr.To16()
|
||||||
|
if ipv6 == nil {
|
||||||
|
return IPv6Addr{}, fmt.Errorf("Unable to convert %+q to a 16byte IPv6 address", ipv6Str)
|
||||||
|
}
|
||||||
|
|
||||||
|
ipv6BigIntAddr := new(big.Int)
|
||||||
|
ipv6BigIntAddr.SetBytes(ipv6)
|
||||||
|
|
||||||
|
ipv6BigIntMask := new(big.Int)
|
||||||
|
ipv6BigIntMask.SetBytes(network.Mask)
|
||||||
|
|
||||||
|
ipv6Addr := IPv6Addr{
|
||||||
|
Address: IPv6Address(ipv6BigIntAddr),
|
||||||
|
Mask: IPv6Mask(ipv6BigIntMask),
|
||||||
|
}
|
||||||
|
return ipv6Addr, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return IPv6Addr{}, fmt.Errorf("Unable to parse %+q to an IPv6 address: %v", ipv6Str, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddressBinString returns a string with the IPv6Addr's Address represented
|
||||||
|
// as a sequence of '0' and '1' characters. This method is useful for
|
||||||
|
// debugging or by operators who want to inspect an address.
|
||||||
|
func (ipv6 IPv6Addr) AddressBinString() string {
|
||||||
|
bi := big.Int(*ipv6.Address)
|
||||||
|
return fmt.Sprintf("%0128s", bi.Text(2))
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddressHexString returns a string with the IPv6Addr address represented as
|
||||||
|
// a sequence of hex characters. This method is useful for debugging or by
|
||||||
|
// operators who want to inspect an address.
|
||||||
|
func (ipv6 IPv6Addr) AddressHexString() string {
|
||||||
|
bi := big.Int(*ipv6.Address)
|
||||||
|
return fmt.Sprintf("%032s", bi.Text(16))
|
||||||
|
}
|
||||||
|
|
||||||
|
// CmpAddress follows the Cmp() standard protocol and returns:
|
||||||
|
//
|
||||||
|
// - -1 If the receiver should sort first because its address is lower than arg
|
||||||
|
// - 0 if the SockAddr arg equal to the receiving IPv6Addr or the argument is of a
|
||||||
|
// different type.
|
||||||
|
// - 1 If the argument should sort first.
|
||||||
|
func (ipv6 IPv6Addr) CmpAddress(sa SockAddr) int {
|
||||||
|
ipv6b, ok := sa.(IPv6Addr)
|
||||||
|
if !ok {
|
||||||
|
return sortDeferDecision
|
||||||
|
}
|
||||||
|
|
||||||
|
ipv6aBigInt := new(big.Int)
|
||||||
|
ipv6aBigInt.Set(ipv6.Address)
|
||||||
|
ipv6bBigInt := new(big.Int)
|
||||||
|
ipv6bBigInt.Set(ipv6b.Address)
|
||||||
|
|
||||||
|
return ipv6aBigInt.Cmp(ipv6bBigInt)
|
||||||
|
}
|
||||||
|
|
||||||
|
// CmpPort follows the Cmp() standard protocol and returns:
|
||||||
|
//
|
||||||
|
// - -1 If the receiver should sort first because its port is lower than arg
|
||||||
|
// - 0 if the SockAddr arg's port number is equal to the receiving IPv6Addr,
|
||||||
|
// regardless of type.
|
||||||
|
// - 1 If the argument should sort first.
|
||||||
|
func (ipv6 IPv6Addr) CmpPort(sa SockAddr) int {
|
||||||
|
var saPort IPPort
|
||||||
|
switch v := sa.(type) {
|
||||||
|
case IPv4Addr:
|
||||||
|
saPort = v.Port
|
||||||
|
case IPv6Addr:
|
||||||
|
saPort = v.Port
|
||||||
|
default:
|
||||||
|
return sortDeferDecision
|
||||||
|
}
|
||||||
|
|
||||||
|
switch {
|
||||||
|
case ipv6.Port == saPort:
|
||||||
|
return sortDeferDecision
|
||||||
|
case ipv6.Port < saPort:
|
||||||
|
return sortReceiverBeforeArg
|
||||||
|
default:
|
||||||
|
return sortArgBeforeReceiver
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// CmpRFC follows the Cmp() standard protocol and returns:
|
||||||
|
//
|
||||||
|
// - -1 If the receiver should sort first because it belongs to the RFC and its
|
||||||
|
// arg does not
|
||||||
|
// - 0 if the receiver and arg both belong to the same RFC or neither do.
|
||||||
|
// - 1 If the arg belongs to the RFC but receiver does not.
|
||||||
|
func (ipv6 IPv6Addr) CmpRFC(rfcNum uint, sa SockAddr) int {
|
||||||
|
recvInRFC := IsRFC(rfcNum, ipv6)
|
||||||
|
ipv6b, ok := sa.(IPv6Addr)
|
||||||
|
if !ok {
|
||||||
|
// If the receiver is part of the desired RFC and the SockAddr
|
||||||
|
// argument is not, sort receiver before the non-IPv6 SockAddr.
|
||||||
|
// Conversely, if the receiver is not part of the RFC, punt on
|
||||||
|
// sorting and leave it for the next sorter.
|
||||||
|
if recvInRFC {
|
||||||
|
return sortReceiverBeforeArg
|
||||||
|
} else {
|
||||||
|
return sortDeferDecision
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
argInRFC := IsRFC(rfcNum, ipv6b)
|
||||||
|
switch {
|
||||||
|
case (recvInRFC && argInRFC), (!recvInRFC && !argInRFC):
|
||||||
|
// If a and b both belong to the RFC, or neither belong to
|
||||||
|
// rfcNum, defer sorting to the next sorter.
|
||||||
|
return sortDeferDecision
|
||||||
|
case recvInRFC && !argInRFC:
|
||||||
|
return sortReceiverBeforeArg
|
||||||
|
default:
|
||||||
|
return sortArgBeforeReceiver
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Contains returns true if the SockAddr is contained within the receiver.
|
||||||
|
func (ipv6 IPv6Addr) Contains(sa SockAddr) bool {
|
||||||
|
ipv6b, ok := sa.(IPv6Addr)
|
||||||
|
if !ok {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return ipv6.ContainsNetwork(ipv6b)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ContainsAddress returns true if the IPv6Address is contained within the
|
||||||
|
// receiver.
|
||||||
|
func (ipv6 IPv6Addr) ContainsAddress(x IPv6Address) bool {
|
||||||
|
xAddr := IPv6Addr{
|
||||||
|
Address: x,
|
||||||
|
Mask: ipv6HostMask,
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
xIPv6 := xAddr.FirstUsable().(IPv6Addr)
|
||||||
|
yIPv6 := ipv6.FirstUsable().(IPv6Addr)
|
||||||
|
if xIPv6.CmpAddress(yIPv6) >= 1 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
xIPv6 := xAddr.LastUsable().(IPv6Addr)
|
||||||
|
yIPv6 := ipv6.LastUsable().(IPv6Addr)
|
||||||
|
if xIPv6.CmpAddress(yIPv6) <= -1 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// ContainsNetwork returns true if the network from IPv6Addr is contained within
|
||||||
|
// the receiver.
|
||||||
|
func (x IPv6Addr) ContainsNetwork(y IPv6Addr) bool {
|
||||||
|
{
|
||||||
|
xIPv6 := x.FirstUsable().(IPv6Addr)
|
||||||
|
yIPv6 := y.FirstUsable().(IPv6Addr)
|
||||||
|
if ret := xIPv6.CmpAddress(yIPv6); ret >= 1 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
xIPv6 := x.LastUsable().(IPv6Addr)
|
||||||
|
yIPv6 := y.LastUsable().(IPv6Addr)
|
||||||
|
if ret := xIPv6.CmpAddress(yIPv6); ret <= -1 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// DialPacketArgs returns the arguments required to be passed to
|
||||||
|
// net.DialUDP(). If the Mask of ipv6 is not a /128 or the Port is 0,
|
||||||
|
// DialPacketArgs() will fail. See Host() to create an IPv6Addr with its
|
||||||
|
// mask set to /128.
|
||||||
|
func (ipv6 IPv6Addr) DialPacketArgs() (network, dialArgs string) {
|
||||||
|
ipv6Mask := big.Int(*ipv6.Mask)
|
||||||
|
if ipv6Mask.Cmp(ipv6HostMask) != 0 || ipv6.Port == 0 {
|
||||||
|
return "udp6", ""
|
||||||
|
}
|
||||||
|
return "udp6", fmt.Sprintf("[%s]:%d", ipv6.NetIP().String(), ipv6.Port)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DialStreamArgs returns the arguments required to be passed to
|
||||||
|
// net.DialTCP(). If the Mask of ipv6 is not a /128 or the Port is 0,
|
||||||
|
// DialStreamArgs() will fail. See Host() to create an IPv6Addr with its
|
||||||
|
// mask set to /128.
|
||||||
|
func (ipv6 IPv6Addr) DialStreamArgs() (network, dialArgs string) {
|
||||||
|
ipv6Mask := big.Int(*ipv6.Mask)
|
||||||
|
if ipv6Mask.Cmp(ipv6HostMask) != 0 || ipv6.Port == 0 {
|
||||||
|
return "tcp6", ""
|
||||||
|
}
|
||||||
|
return "tcp6", fmt.Sprintf("[%s]:%d", ipv6.NetIP().String(), ipv6.Port)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Equal returns true if a SockAddr is equal to the receiving IPv4Addr.
|
||||||
|
func (ipv6a IPv6Addr) Equal(sa SockAddr) bool {
|
||||||
|
ipv6b, ok := sa.(IPv6Addr)
|
||||||
|
if !ok {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if ipv6a.NetIP().String() != ipv6b.NetIP().String() {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if ipv6a.NetIPNet().String() != ipv6b.NetIPNet().String() {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if ipv6a.Port != ipv6b.Port {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// FirstUsable returns an IPv6Addr set to the first address following the
|
||||||
|
// network prefix. The first usable address in a network is normally the
|
||||||
|
// gateway and should not be used except by devices forwarding packets
|
||||||
|
// between two administratively distinct networks (i.e. a router). This
|
||||||
|
// function does not discriminate against first usable vs "first address that
|
||||||
|
// should be used." For example, FirstUsable() on "2001:0db8::0003/64" would
|
||||||
|
// return "2001:0db8::00011".
|
||||||
|
func (ipv6 IPv6Addr) FirstUsable() IPAddr {
|
||||||
|
return IPv6Addr{
|
||||||
|
Address: IPv6Address(ipv6.NetworkAddress()),
|
||||||
|
Mask: ipv6HostMask,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Host returns a copy of ipv6 with its mask set to /128 so that it can be
|
||||||
|
// used by DialPacketArgs(), DialStreamArgs(), ListenPacketArgs(), or
|
||||||
|
// ListenStreamArgs().
|
||||||
|
func (ipv6 IPv6Addr) Host() IPAddr {
|
||||||
|
// Nothing should listen on a broadcast address.
|
||||||
|
return IPv6Addr{
|
||||||
|
Address: ipv6.Address,
|
||||||
|
Mask: ipv6HostMask,
|
||||||
|
Port: ipv6.Port,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// IPPort returns the Port number attached to the IPv6Addr
|
||||||
|
func (ipv6 IPv6Addr) IPPort() IPPort {
|
||||||
|
return ipv6.Port
|
||||||
|
}
|
||||||
|
|
||||||
|
// LastUsable returns the last address in a given network.
|
||||||
|
func (ipv6 IPv6Addr) LastUsable() IPAddr {
|
||||||
|
addr := new(big.Int)
|
||||||
|
addr.Set(ipv6.Address)
|
||||||
|
|
||||||
|
mask := new(big.Int)
|
||||||
|
mask.Set(ipv6.Mask)
|
||||||
|
|
||||||
|
negMask := new(big.Int)
|
||||||
|
negMask.Xor(ipv6HostMask, mask)
|
||||||
|
|
||||||
|
lastAddr := new(big.Int)
|
||||||
|
lastAddr.And(addr, mask)
|
||||||
|
lastAddr.Or(lastAddr, negMask)
|
||||||
|
|
||||||
|
return IPv6Addr{
|
||||||
|
Address: IPv6Address(lastAddr),
|
||||||
|
Mask: ipv6HostMask,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListenPacketArgs returns the arguments required to be passed to
|
||||||
|
// net.ListenUDP(). If the Mask of ipv6 is not a /128, ListenPacketArgs()
|
||||||
|
// will fail. See Host() to create an IPv6Addr with its mask set to /128.
|
||||||
|
func (ipv6 IPv6Addr) ListenPacketArgs() (network, listenArgs string) {
|
||||||
|
ipv6Mask := big.Int(*ipv6.Mask)
|
||||||
|
if ipv6Mask.Cmp(ipv6HostMask) != 0 {
|
||||||
|
return "udp6", ""
|
||||||
|
}
|
||||||
|
return "udp6", fmt.Sprintf("[%s]:%d", ipv6.NetIP().String(), ipv6.Port)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListenStreamArgs returns the arguments required to be passed to
|
||||||
|
// net.ListenTCP(). If the Mask of ipv6 is not a /128, ListenStreamArgs()
|
||||||
|
// will fail. See Host() to create an IPv6Addr with its mask set to /128.
|
||||||
|
func (ipv6 IPv6Addr) ListenStreamArgs() (network, listenArgs string) {
|
||||||
|
ipv6Mask := big.Int(*ipv6.Mask)
|
||||||
|
if ipv6Mask.Cmp(ipv6HostMask) != 0 {
|
||||||
|
return "tcp6", ""
|
||||||
|
}
|
||||||
|
return "tcp6", fmt.Sprintf("[%s]:%d", ipv6.NetIP().String(), ipv6.Port)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Maskbits returns the number of network mask bits in a given IPv6Addr. For
|
||||||
|
// example, the Maskbits() of "2001:0db8::0003/64" would return 64.
|
||||||
|
func (ipv6 IPv6Addr) Maskbits() int {
|
||||||
|
maskOnes, _ := ipv6.NetIPNet().Mask.Size()
|
||||||
|
|
||||||
|
return maskOnes
|
||||||
|
}
|
||||||
|
|
||||||
|
// MustIPv6Addr is a helper method that must return an IPv6Addr or panic on
|
||||||
|
// invalid input.
|
||||||
|
func MustIPv6Addr(addr string) IPv6Addr {
|
||||||
|
ipv6, err := NewIPv6Addr(addr)
|
||||||
|
if err != nil {
|
||||||
|
panic(fmt.Sprintf("Unable to create an IPv6Addr from %+q: %v", addr, err))
|
||||||
|
}
|
||||||
|
return ipv6
|
||||||
|
}
|
||||||
|
|
||||||
|
// NetIP returns the address as a net.IP.
|
||||||
|
func (ipv6 IPv6Addr) NetIP() *net.IP {
|
||||||
|
return bigIntToNetIPv6(ipv6.Address)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NetIPMask create a new net.IPMask from the IPv6Addr.
|
||||||
|
func (ipv6 IPv6Addr) NetIPMask() *net.IPMask {
|
||||||
|
ipv6Mask := make(net.IPMask, IPv6len)
|
||||||
|
m := big.Int(*ipv6.Mask)
|
||||||
|
copy(ipv6Mask, m.Bytes())
|
||||||
|
return &ipv6Mask
|
||||||
|
}
|
||||||
|
|
||||||
|
// Network returns a pointer to the net.IPNet within IPv4Addr receiver.
|
||||||
|
func (ipv6 IPv6Addr) NetIPNet() *net.IPNet {
|
||||||
|
ipv6net := &net.IPNet{}
|
||||||
|
ipv6net.IP = make(net.IP, IPv6len)
|
||||||
|
copy(ipv6net.IP, *ipv6.NetIP())
|
||||||
|
ipv6net.Mask = *ipv6.NetIPMask()
|
||||||
|
return ipv6net
|
||||||
|
}
|
||||||
|
|
||||||
|
// Network returns the network prefix or network address for a given network.
|
||||||
|
func (ipv6 IPv6Addr) Network() IPAddr {
|
||||||
|
return IPv6Addr{
|
||||||
|
Address: IPv6Address(ipv6.NetworkAddress()),
|
||||||
|
Mask: ipv6.Mask,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NetworkAddress returns an IPv6Network of the IPv6Addr's network address.
|
||||||
|
func (ipv6 IPv6Addr) NetworkAddress() IPv6Network {
|
||||||
|
addr := new(big.Int)
|
||||||
|
addr.SetBytes((*ipv6.Address).Bytes())
|
||||||
|
|
||||||
|
mask := new(big.Int)
|
||||||
|
mask.SetBytes(*ipv6.NetIPMask())
|
||||||
|
|
||||||
|
netAddr := new(big.Int)
|
||||||
|
netAddr.And(addr, mask)
|
||||||
|
|
||||||
|
return IPv6Network(netAddr)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Octets returns a slice of the 16 octets in an IPv6Addr's Address. The
|
||||||
|
// order of the bytes is big endian.
|
||||||
|
func (ipv6 IPv6Addr) Octets() []int {
|
||||||
|
x := make([]int, IPv6len)
|
||||||
|
for i, b := range *bigIntToNetIPv6(ipv6.Address) {
|
||||||
|
x[i] = int(b)
|
||||||
|
}
|
||||||
|
|
||||||
|
return x
|
||||||
|
}
|
||||||
|
|
||||||
|
// String returns a string representation of the IPv6Addr
|
||||||
|
func (ipv6 IPv6Addr) String() string {
|
||||||
|
if ipv6.Port != 0 {
|
||||||
|
return fmt.Sprintf("[%s]:%d", ipv6.NetIP().String(), ipv6.Port)
|
||||||
|
}
|
||||||
|
|
||||||
|
if ipv6.Maskbits() == 128 {
|
||||||
|
return ipv6.NetIP().String()
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt.Sprintf("%s/%d", ipv6.NetIP().String(), ipv6.Maskbits())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Type is used as a type switch and returns TypeIPv6
|
||||||
|
func (IPv6Addr) Type() SockAddrType {
|
||||||
|
return TypeIPv6
|
||||||
|
}
|
||||||
|
|
||||||
|
// IPv6Attrs returns a list of attributes supported by the IPv6Addr type
|
||||||
|
func IPv6Attrs() []AttrName {
|
||||||
|
return ipv6AddrAttrs
|
||||||
|
}
|
||||||
|
|
||||||
|
// IPv6AddrAttr returns a string representation of an attribute for the given
|
||||||
|
// IPv6Addr.
|
||||||
|
func IPv6AddrAttr(ipv6 IPv6Addr, selector AttrName) string {
|
||||||
|
fn, found := ipv6AddrAttrMap[selector]
|
||||||
|
if !found {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
return fn(ipv6)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ipv6AddrInit is called once at init()
|
||||||
|
func ipv6AddrInit() {
|
||||||
|
// Sorted for human readability
|
||||||
|
ipv6AddrAttrs = []AttrName{
|
||||||
|
"size", // Same position as in IPv6 for output consistency
|
||||||
|
"uint128",
|
||||||
|
}
|
||||||
|
|
||||||
|
ipv6AddrAttrMap = map[AttrName]func(ipv6 IPv6Addr) string{
|
||||||
|
"size": func(ipv6 IPv6Addr) string {
|
||||||
|
netSize := big.NewInt(1)
|
||||||
|
netSize = netSize.Lsh(netSize, uint(IPv6len*8-ipv6.Maskbits()))
|
||||||
|
return netSize.Text(10)
|
||||||
|
},
|
||||||
|
"uint128": func(ipv6 IPv6Addr) string {
|
||||||
|
b := big.Int(*ipv6.Address)
|
||||||
|
return b.Text(10)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// bigIntToNetIPv6 is a helper function that correctly returns a net.IP with the
|
||||||
|
// correctly padded values.
|
||||||
|
func bigIntToNetIPv6(bi *big.Int) *net.IP {
|
||||||
|
x := make(net.IP, IPv6len)
|
||||||
|
ipv6Bytes := bi.Bytes()
|
||||||
|
|
||||||
|
// It's possibe for ipv6Bytes to be less than IPv6len bytes in size. If
|
||||||
|
// they are different sizes we to pad the size of response.
|
||||||
|
if len(ipv6Bytes) < IPv6len {
|
||||||
|
buf := new(bytes.Buffer)
|
||||||
|
buf.Grow(IPv6len)
|
||||||
|
|
||||||
|
for i := len(ipv6Bytes); i < IPv6len; i++ {
|
||||||
|
if err := binary.Write(buf, binary.BigEndian, byte(0)); err != nil {
|
||||||
|
panic(fmt.Sprintf("Unable to pad byte %d of input %v: %v", i, bi, err))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, b := range ipv6Bytes {
|
||||||
|
if err := binary.Write(buf, binary.BigEndian, b); err != nil {
|
||||||
|
panic(fmt.Sprintf("Unable to preserve endianness of input %v: %v", bi, err))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ipv6Bytes = buf.Bytes()
|
||||||
|
}
|
||||||
|
i := copy(x, ipv6Bytes)
|
||||||
|
if i != IPv6len {
|
||||||
|
panic("IPv6 wrong size")
|
||||||
|
}
|
||||||
|
return &x
|
||||||
|
}
|
947
vendor/github.com/hashicorp/go-sockaddr/rfc.go
generated
vendored
Normal file
947
vendor/github.com/hashicorp/go-sockaddr/rfc.go
generated
vendored
Normal file
|
@ -0,0 +1,947 @@
|
||||||
|
package sockaddr
|
||||||
|
|
||||||
|
// ForwardingBlacklist is a faux RFC that includes a list of non-forwardable IP
|
||||||
|
// blocks.
|
||||||
|
const ForwardingBlacklist = 4294967295
|
||||||
|
|
||||||
|
// IsRFC tests to see if an SockAddr matches the specified RFC
|
||||||
|
func IsRFC(rfcNum uint, sa SockAddr) bool {
|
||||||
|
rfcNetMap := KnownRFCs()
|
||||||
|
rfcNets, ok := rfcNetMap[rfcNum]
|
||||||
|
if !ok {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
var contained bool
|
||||||
|
for _, rfcNet := range rfcNets {
|
||||||
|
if rfcNet.Contains(sa) {
|
||||||
|
contained = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return contained
|
||||||
|
}
|
||||||
|
|
||||||
|
// KnownRFCs returns an initial set of known RFCs.
|
||||||
|
//
|
||||||
|
// NOTE (sean@): As this list evolves over time, please submit patches to keep
|
||||||
|
// this list current. If something isn't right, inquire, as it may just be a
|
||||||
|
// bug on my part. Some of the inclusions were based on my judgement as to what
|
||||||
|
// would be a useful value (e.g. RFC3330).
|
||||||
|
//
|
||||||
|
// Useful resources:
|
||||||
|
//
|
||||||
|
// * https://www.iana.org/assignments/ipv6-address-space/ipv6-address-space.xhtml
|
||||||
|
// * https://www.iana.org/assignments/ipv6-unicast-address-assignments/ipv6-unicast-address-assignments.xhtml
|
||||||
|
// * https://www.iana.org/assignments/ipv6-address-space/ipv6-address-space.xhtml
|
||||||
|
func KnownRFCs() map[uint]SockAddrs {
|
||||||
|
// NOTE(sean@): Multiple SockAddrs per RFC lend themselves well to a
|
||||||
|
// RADIX tree, but `ENOTIME`. Patches welcome.
|
||||||
|
return map[uint]SockAddrs{
|
||||||
|
919: {
|
||||||
|
// [RFC919] Broadcasting Internet Datagrams
|
||||||
|
MustIPv4Addr("255.255.255.255/32"), // [RFC1122], §7 Broadcast IP Addressing - Proposed Standards
|
||||||
|
},
|
||||||
|
1122: {
|
||||||
|
// [RFC1122] Requirements for Internet Hosts -- Communication Layers
|
||||||
|
MustIPv4Addr("0.0.0.0/8"), // [RFC1122], §3.2.1.3
|
||||||
|
MustIPv4Addr("127.0.0.0/8"), // [RFC1122], §3.2.1.3
|
||||||
|
},
|
||||||
|
1112: {
|
||||||
|
// [RFC1112] Host Extensions for IP Multicasting
|
||||||
|
MustIPv4Addr("224.0.0.0/4"), // [RFC1112], §4 Host Group Addresses
|
||||||
|
},
|
||||||
|
1918: {
|
||||||
|
// [RFC1918] Address Allocation for Private Internets
|
||||||
|
MustIPv4Addr("10.0.0.0/8"),
|
||||||
|
MustIPv4Addr("172.16.0.0/12"),
|
||||||
|
MustIPv4Addr("192.168.0.0/16"),
|
||||||
|
},
|
||||||
|
2544: {
|
||||||
|
// [RFC2544] Benchmarking Methodology for Network
|
||||||
|
// Interconnect Devices
|
||||||
|
MustIPv4Addr("198.18.0.0/15"),
|
||||||
|
},
|
||||||
|
2765: {
|
||||||
|
// [RFC2765] Stateless IP/ICMP Translation Algorithm
|
||||||
|
// (SIIT) (obsoleted by RFCs 6145, which itself was
|
||||||
|
// later obsoleted by 7915).
|
||||||
|
|
||||||
|
// [RFC2765], §2.1 Addresses
|
||||||
|
MustIPv6Addr("0:0:0:0:0:ffff:0:0/96"),
|
||||||
|
},
|
||||||
|
2928: {
|
||||||
|
// [RFC2928] Initial IPv6 Sub-TLA ID Assignments
|
||||||
|
MustIPv6Addr("2001::/16"), // Superblock
|
||||||
|
//MustIPv6Addr("2001:0000::/23"), // IANA
|
||||||
|
//MustIPv6Addr("2001:0200::/23"), // APNIC
|
||||||
|
//MustIPv6Addr("2001:0400::/23"), // ARIN
|
||||||
|
//MustIPv6Addr("2001:0600::/23"), // RIPE NCC
|
||||||
|
//MustIPv6Addr("2001:0800::/23"), // (future assignment)
|
||||||
|
// ...
|
||||||
|
//MustIPv6Addr("2001:FE00::/23"), // (future assignment)
|
||||||
|
},
|
||||||
|
3056: { // 6to4 address
|
||||||
|
// [RFC3056] Connection of IPv6 Domains via IPv4 Clouds
|
||||||
|
|
||||||
|
// [RFC3056], §2 IPv6 Prefix Allocation
|
||||||
|
MustIPv6Addr("2002::/16"),
|
||||||
|
},
|
||||||
|
3068: {
|
||||||
|
// [RFC3068] An Anycast Prefix for 6to4 Relay Routers
|
||||||
|
// (obsolete by RFC7526)
|
||||||
|
|
||||||
|
// [RFC3068], § 6to4 Relay anycast address
|
||||||
|
MustIPv4Addr("192.88.99.0/24"),
|
||||||
|
|
||||||
|
// [RFC3068], §2.5 6to4 IPv6 relay anycast address
|
||||||
|
//
|
||||||
|
// NOTE: /120 == 128-(32-24)
|
||||||
|
MustIPv6Addr("2002:c058:6301::/120"),
|
||||||
|
},
|
||||||
|
3171: {
|
||||||
|
// [RFC3171] IANA Guidelines for IPv4 Multicast Address Assignments
|
||||||
|
MustIPv4Addr("224.0.0.0/4"),
|
||||||
|
},
|
||||||
|
3330: {
|
||||||
|
// [RFC3330] Special-Use IPv4 Addresses
|
||||||
|
|
||||||
|
// Addresses in this block refer to source hosts on
|
||||||
|
// "this" network. Address 0.0.0.0/32 may be used as a
|
||||||
|
// source address for this host on this network; other
|
||||||
|
// addresses within 0.0.0.0/8 may be used to refer to
|
||||||
|
// specified hosts on this network [RFC1700, page 4].
|
||||||
|
MustIPv4Addr("0.0.0.0/8"),
|
||||||
|
|
||||||
|
// 10.0.0.0/8 - This block is set aside for use in
|
||||||
|
// private networks. Its intended use is documented in
|
||||||
|
// [RFC1918]. Addresses within this block should not
|
||||||
|
// appear on the public Internet.
|
||||||
|
MustIPv4Addr("10.0.0.0/8"),
|
||||||
|
|
||||||
|
// 14.0.0.0/8 - This block is set aside for assignments
|
||||||
|
// to the international system of Public Data Networks
|
||||||
|
// [RFC1700, page 181]. The registry of assignments
|
||||||
|
// within this block can be accessed from the "Public
|
||||||
|
// Data Network Numbers" link on the web page at
|
||||||
|
// http://www.iana.org/numbers.html. Addresses within
|
||||||
|
// this block are assigned to users and should be
|
||||||
|
// treated as such.
|
||||||
|
|
||||||
|
// 24.0.0.0/8 - This block was allocated in early 1996
|
||||||
|
// for use in provisioning IP service over cable
|
||||||
|
// television systems. Although the IANA initially was
|
||||||
|
// involved in making assignments to cable operators,
|
||||||
|
// this responsibility was transferred to American
|
||||||
|
// Registry for Internet Numbers (ARIN) in May 2001.
|
||||||
|
// Addresses within this block are assigned in the
|
||||||
|
// normal manner and should be treated as such.
|
||||||
|
|
||||||
|
// 39.0.0.0/8 - This block was used in the "Class A
|
||||||
|
// Subnet Experiment" that commenced in May 1995, as
|
||||||
|
// documented in [RFC1797]. The experiment has been
|
||||||
|
// completed and this block has been returned to the
|
||||||
|
// pool of addresses reserved for future allocation or
|
||||||
|
// assignment. This block therefore no longer has a
|
||||||
|
// special use and is subject to allocation to a
|
||||||
|
// Regional Internet Registry for assignment in the
|
||||||
|
// normal manner.
|
||||||
|
|
||||||
|
// 127.0.0.0/8 - This block is assigned for use as the Internet host
|
||||||
|
// loopback address. A datagram sent by a higher level protocol to an
|
||||||
|
// address anywhere within this block should loop back inside the host.
|
||||||
|
// This is ordinarily implemented using only 127.0.0.1/32 for loopback,
|
||||||
|
// but no addresses within this block should ever appear on any network
|
||||||
|
// anywhere [RFC1700, page 5].
|
||||||
|
MustIPv4Addr("127.0.0.0/8"),
|
||||||
|
|
||||||
|
// 128.0.0.0/16 - This block, corresponding to the
|
||||||
|
// numerically lowest of the former Class B addresses,
|
||||||
|
// was initially and is still reserved by the IANA.
|
||||||
|
// Given the present classless nature of the IP address
|
||||||
|
// space, the basis for the reservation no longer
|
||||||
|
// applies and addresses in this block are subject to
|
||||||
|
// future allocation to a Regional Internet Registry for
|
||||||
|
// assignment in the normal manner.
|
||||||
|
|
||||||
|
// 169.254.0.0/16 - This is the "link local" block. It
|
||||||
|
// is allocated for communication between hosts on a
|
||||||
|
// single link. Hosts obtain these addresses by
|
||||||
|
// auto-configuration, such as when a DHCP server may
|
||||||
|
// not be found.
|
||||||
|
MustIPv4Addr("169.254.0.0/16"),
|
||||||
|
|
||||||
|
// 172.16.0.0/12 - This block is set aside for use in
|
||||||
|
// private networks. Its intended use is documented in
|
||||||
|
// [RFC1918]. Addresses within this block should not
|
||||||
|
// appear on the public Internet.
|
||||||
|
MustIPv4Addr("172.16.0.0/12"),
|
||||||
|
|
||||||
|
// 191.255.0.0/16 - This block, corresponding to the numerically highest
|
||||||
|
// to the former Class B addresses, was initially and is still reserved
|
||||||
|
// by the IANA. Given the present classless nature of the IP address
|
||||||
|
// space, the basis for the reservation no longer applies and addresses
|
||||||
|
// in this block are subject to future allocation to a Regional Internet
|
||||||
|
// Registry for assignment in the normal manner.
|
||||||
|
|
||||||
|
// 192.0.0.0/24 - This block, corresponding to the
|
||||||
|
// numerically lowest of the former Class C addresses,
|
||||||
|
// was initially and is still reserved by the IANA.
|
||||||
|
// Given the present classless nature of the IP address
|
||||||
|
// space, the basis for the reservation no longer
|
||||||
|
// applies and addresses in this block are subject to
|
||||||
|
// future allocation to a Regional Internet Registry for
|
||||||
|
// assignment in the normal manner.
|
||||||
|
|
||||||
|
// 192.0.2.0/24 - This block is assigned as "TEST-NET" for use in
|
||||||
|
// documentation and example code. It is often used in conjunction with
|
||||||
|
// domain names example.com or example.net in vendor and protocol
|
||||||
|
// documentation. Addresses within this block should not appear on the
|
||||||
|
// public Internet.
|
||||||
|
MustIPv4Addr("192.0.2.0/24"),
|
||||||
|
|
||||||
|
// 192.88.99.0/24 - This block is allocated for use as 6to4 relay
|
||||||
|
// anycast addresses, according to [RFC3068].
|
||||||
|
MustIPv4Addr("192.88.99.0/24"),
|
||||||
|
|
||||||
|
// 192.168.0.0/16 - This block is set aside for use in private networks.
|
||||||
|
// Its intended use is documented in [RFC1918]. Addresses within this
|
||||||
|
// block should not appear on the public Internet.
|
||||||
|
MustIPv4Addr("192.168.0.0/16"),
|
||||||
|
|
||||||
|
// 198.18.0.0/15 - This block has been allocated for use
|
||||||
|
// in benchmark tests of network interconnect devices.
|
||||||
|
// Its use is documented in [RFC2544].
|
||||||
|
MustIPv4Addr("198.18.0.0/15"),
|
||||||
|
|
||||||
|
// 223.255.255.0/24 - This block, corresponding to the
|
||||||
|
// numerically highest of the former Class C addresses,
|
||||||
|
// was initially and is still reserved by the IANA.
|
||||||
|
// Given the present classless nature of the IP address
|
||||||
|
// space, the basis for the reservation no longer
|
||||||
|
// applies and addresses in this block are subject to
|
||||||
|
// future allocation to a Regional Internet Registry for
|
||||||
|
// assignment in the normal manner.
|
||||||
|
|
||||||
|
// 224.0.0.0/4 - This block, formerly known as the Class
|
||||||
|
// D address space, is allocated for use in IPv4
|
||||||
|
// multicast address assignments. The IANA guidelines
|
||||||
|
// for assignments from this space are described in
|
||||||
|
// [RFC3171].
|
||||||
|
MustIPv4Addr("224.0.0.0/4"),
|
||||||
|
|
||||||
|
// 240.0.0.0/4 - This block, formerly known as the Class E address
|
||||||
|
// space, is reserved. The "limited broadcast" destination address
|
||||||
|
// 255.255.255.255 should never be forwarded outside the (sub-)net of
|
||||||
|
// the source. The remainder of this space is reserved
|
||||||
|
// for future use. [RFC1700, page 4]
|
||||||
|
MustIPv4Addr("240.0.0.0/4"),
|
||||||
|
},
|
||||||
|
3849: {
|
||||||
|
// [RFC3849] IPv6 Address Prefix Reserved for Documentation
|
||||||
|
MustIPv6Addr("2001:db8::/32"), // [RFC3849], §4 IANA Considerations
|
||||||
|
},
|
||||||
|
3927: {
|
||||||
|
// [RFC3927] Dynamic Configuration of IPv4 Link-Local Addresses
|
||||||
|
MustIPv4Addr("169.254.0.0/16"), // [RFC3927], §2.1 Link-Local Address Selection
|
||||||
|
},
|
||||||
|
4038: {
|
||||||
|
// [RFC4038] Application Aspects of IPv6 Transition
|
||||||
|
|
||||||
|
// [RFC4038], §4.2. IPv6 Applications in a Dual-Stack Node
|
||||||
|
MustIPv6Addr("0:0:0:0:0:ffff::/96"),
|
||||||
|
},
|
||||||
|
4193: {
|
||||||
|
// [RFC4193] Unique Local IPv6 Unicast Addresses
|
||||||
|
MustIPv6Addr("fc00::/7"),
|
||||||
|
},
|
||||||
|
4291: {
|
||||||
|
// [RFC4291] IP Version 6 Addressing Architecture
|
||||||
|
|
||||||
|
// [RFC4291], §2.5.2 The Unspecified Address
|
||||||
|
MustIPv6Addr("::/128"),
|
||||||
|
|
||||||
|
// [RFC4291], §2.5.3 The Loopback Address
|
||||||
|
MustIPv6Addr("::1/128"),
|
||||||
|
|
||||||
|
// [RFC4291], §2.5.5.1. IPv4-Compatible IPv6 Address
|
||||||
|
MustIPv6Addr("::/96"),
|
||||||
|
|
||||||
|
// [RFC4291], §2.5.5.2. IPv4-Mapped IPv6 Address
|
||||||
|
MustIPv6Addr("::ffff:0:0/96"),
|
||||||
|
|
||||||
|
// [RFC4291], §2.5.6 Link-Local IPv6 Unicast Addresses
|
||||||
|
MustIPv6Addr("fe80::/10"),
|
||||||
|
|
||||||
|
// [RFC4291], §2.5.7 Site-Local IPv6 Unicast Addresses
|
||||||
|
// (depreciated)
|
||||||
|
MustIPv6Addr("fec0::/10"),
|
||||||
|
|
||||||
|
// [RFC4291], §2.7 Multicast Addresses
|
||||||
|
MustIPv6Addr("ff00::/8"),
|
||||||
|
|
||||||
|
// IPv6 Multicast Information.
|
||||||
|
//
|
||||||
|
// In the following "table" below, `ff0x` is replaced
|
||||||
|
// with the following values depending on the scope of
|
||||||
|
// the query:
|
||||||
|
//
|
||||||
|
// IPv6 Multicast Scopes:
|
||||||
|
// * ff00/9 // reserved
|
||||||
|
// * ff01/9 // interface-local
|
||||||
|
// * ff02/9 // link-local
|
||||||
|
// * ff03/9 // realm-local
|
||||||
|
// * ff04/9 // admin-local
|
||||||
|
// * ff05/9 // site-local
|
||||||
|
// * ff08/9 // organization-local
|
||||||
|
// * ff0e/9 // global
|
||||||
|
// * ff0f/9 // reserved
|
||||||
|
//
|
||||||
|
// IPv6 Multicast Addresses:
|
||||||
|
// * ff0x::2 // All routers
|
||||||
|
// * ff02::5 // OSPFIGP
|
||||||
|
// * ff02::6 // OSPFIGP Designated Routers
|
||||||
|
// * ff02::9 // RIP Routers
|
||||||
|
// * ff02::a // EIGRP Routers
|
||||||
|
// * ff02::d // All PIM Routers
|
||||||
|
// * ff02::1a // All RPL Routers
|
||||||
|
// * ff0x::fb // mDNSv6
|
||||||
|
// * ff0x::101 // All Network Time Protocol (NTP) servers
|
||||||
|
// * ff02::1:1 // Link Name
|
||||||
|
// * ff02::1:2 // All-dhcp-agents
|
||||||
|
// * ff02::1:3 // Link-local Multicast Name Resolution
|
||||||
|
// * ff05::1:3 // All-dhcp-servers
|
||||||
|
// * ff02::1:ff00:0/104 // Solicited-node multicast address.
|
||||||
|
// * ff02::2:ff00:0/104 // Node Information Queries
|
||||||
|
},
|
||||||
|
4380: {
|
||||||
|
// [RFC4380] Teredo: Tunneling IPv6 over UDP through
|
||||||
|
// Network Address Translations (NATs)
|
||||||
|
|
||||||
|
// [RFC4380], §2.6 Global Teredo IPv6 Service Prefix
|
||||||
|
MustIPv6Addr("2001:0000::/32"),
|
||||||
|
},
|
||||||
|
4773: {
|
||||||
|
// [RFC4773] Administration of the IANA Special Purpose IPv6 Address Block
|
||||||
|
MustIPv6Addr("2001:0000::/23"), // IANA
|
||||||
|
},
|
||||||
|
4843: {
|
||||||
|
// [RFC4843] An IPv6 Prefix for Overlay Routable Cryptographic Hash Identifiers (ORCHID)
|
||||||
|
MustIPv6Addr("2001:10::/28"), // [RFC4843], §7 IANA Considerations
|
||||||
|
},
|
||||||
|
5180: {
|
||||||
|
// [RFC5180] IPv6 Benchmarking Methodology for Network Interconnect Devices
|
||||||
|
MustIPv6Addr("2001:0200::/48"), // [RFC5180], §8 IANA Considerations
|
||||||
|
},
|
||||||
|
5735: {
|
||||||
|
// [RFC5735] Special Use IPv4 Addresses
|
||||||
|
MustIPv4Addr("192.0.2.0/24"), // TEST-NET-1
|
||||||
|
MustIPv4Addr("198.51.100.0/24"), // TEST-NET-2
|
||||||
|
MustIPv4Addr("203.0.113.0/24"), // TEST-NET-3
|
||||||
|
MustIPv4Addr("198.18.0.0/15"), // Benchmarks
|
||||||
|
},
|
||||||
|
5737: {
|
||||||
|
// [RFC5737] IPv4 Address Blocks Reserved for Documentation
|
||||||
|
MustIPv4Addr("192.0.2.0/24"), // TEST-NET-1
|
||||||
|
MustIPv4Addr("198.51.100.0/24"), // TEST-NET-2
|
||||||
|
MustIPv4Addr("203.0.113.0/24"), // TEST-NET-3
|
||||||
|
},
|
||||||
|
6052: {
|
||||||
|
// [RFC6052] IPv6 Addressing of IPv4/IPv6 Translators
|
||||||
|
MustIPv6Addr("64:ff9b::/96"), // [RFC6052], §2.1. Well-Known Prefix
|
||||||
|
},
|
||||||
|
6333: {
|
||||||
|
// [RFC6333] Dual-Stack Lite Broadband Deployments Following IPv4 Exhaustion
|
||||||
|
MustIPv4Addr("192.0.0.0/29"), // [RFC6333], §5.7 Well-Known IPv4 Address
|
||||||
|
},
|
||||||
|
6598: {
|
||||||
|
// [RFC6598] IANA-Reserved IPv4 Prefix for Shared Address Space
|
||||||
|
MustIPv4Addr("100.64.0.0/10"),
|
||||||
|
},
|
||||||
|
6666: {
|
||||||
|
// [RFC6666] A Discard Prefix for IPv6
|
||||||
|
MustIPv6Addr("0100::/64"),
|
||||||
|
},
|
||||||
|
6890: {
|
||||||
|
// [RFC6890] Special-Purpose IP Address Registries
|
||||||
|
|
||||||
|
// From "RFC6890 §2.2.1 Information Requirements":
|
||||||
|
/*
|
||||||
|
The IPv4 and IPv6 Special-Purpose Address Registries maintain the
|
||||||
|
following information regarding each entry:
|
||||||
|
|
||||||
|
o Address Block - A block of IPv4 or IPv6 addresses that has been
|
||||||
|
registered for a special purpose.
|
||||||
|
|
||||||
|
o Name - A descriptive name for the special-purpose address block.
|
||||||
|
|
||||||
|
o RFC - The RFC through which the special-purpose address block was
|
||||||
|
requested.
|
||||||
|
|
||||||
|
o Allocation Date - The date upon which the special-purpose address
|
||||||
|
block was allocated.
|
||||||
|
|
||||||
|
o Termination Date - The date upon which the allocation is to be
|
||||||
|
terminated. This field is applicable for limited-use allocations
|
||||||
|
only.
|
||||||
|
|
||||||
|
o Source - A boolean value indicating whether an address from the
|
||||||
|
allocated special-purpose address block is valid when used as the
|
||||||
|
source address of an IP datagram that transits two devices.
|
||||||
|
|
||||||
|
o Destination - A boolean value indicating whether an address from
|
||||||
|
the allocated special-purpose address block is valid when used as
|
||||||
|
the destination address of an IP datagram that transits two
|
||||||
|
devices.
|
||||||
|
|
||||||
|
o Forwardable - A boolean value indicating whether a router may
|
||||||
|
forward an IP datagram whose destination address is drawn from the
|
||||||
|
allocated special-purpose address block between external
|
||||||
|
interfaces.
|
||||||
|
|
||||||
|
o Global - A boolean value indicating whether an IP datagram whose
|
||||||
|
destination address is drawn from the allocated special-purpose
|
||||||
|
address block is forwardable beyond a specified administrative
|
||||||
|
domain.
|
||||||
|
|
||||||
|
o Reserved-by-Protocol - A boolean value indicating whether the
|
||||||
|
special-purpose address block is reserved by IP, itself. This
|
||||||
|
value is "TRUE" if the RFC that created the special-purpose
|
||||||
|
address block requires all compliant IP implementations to behave
|
||||||
|
in a special way when processing packets either to or from
|
||||||
|
addresses contained by the address block.
|
||||||
|
|
||||||
|
If the value of "Destination" is FALSE, the values of "Forwardable"
|
||||||
|
and "Global" must also be false.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*+----------------------+----------------------------+
|
||||||
|
* | Attribute | Value |
|
||||||
|
* +----------------------+----------------------------+
|
||||||
|
* | Address Block | 0.0.0.0/8 |
|
||||||
|
* | Name | "This host on this network"|
|
||||||
|
* | RFC | [RFC1122], Section 3.2.1.3 |
|
||||||
|
* | Allocation Date | September 1981 |
|
||||||
|
* | Termination Date | N/A |
|
||||||
|
* | Source | True |
|
||||||
|
* | Destination | False |
|
||||||
|
* | Forwardable | False |
|
||||||
|
* | Global | False |
|
||||||
|
* | Reserved-by-Protocol | True |
|
||||||
|
* +----------------------+----------------------------+*/
|
||||||
|
MustIPv4Addr("0.0.0.0/8"),
|
||||||
|
|
||||||
|
/*+----------------------+---------------+
|
||||||
|
* | Attribute | Value |
|
||||||
|
* +----------------------+---------------+
|
||||||
|
* | Address Block | 10.0.0.0/8 |
|
||||||
|
* | Name | Private-Use |
|
||||||
|
* | RFC | [RFC1918] |
|
||||||
|
* | Allocation Date | February 1996 |
|
||||||
|
* | Termination Date | N/A |
|
||||||
|
* | Source | True |
|
||||||
|
* | Destination | True |
|
||||||
|
* | Forwardable | True |
|
||||||
|
* | Global | False |
|
||||||
|
* | Reserved-by-Protocol | False |
|
||||||
|
* +----------------------+---------------+ */
|
||||||
|
MustIPv4Addr("10.0.0.0/8"),
|
||||||
|
|
||||||
|
/*+----------------------+----------------------+
|
||||||
|
| Attribute | Value |
|
||||||
|
+----------------------+----------------------+
|
||||||
|
| Address Block | 100.64.0.0/10 |
|
||||||
|
| Name | Shared Address Space |
|
||||||
|
| RFC | [RFC6598] |
|
||||||
|
| Allocation Date | April 2012 |
|
||||||
|
| Termination Date | N/A |
|
||||||
|
| Source | True |
|
||||||
|
| Destination | True |
|
||||||
|
| Forwardable | True |
|
||||||
|
| Global | False |
|
||||||
|
| Reserved-by-Protocol | False |
|
||||||
|
+----------------------+----------------------+*/
|
||||||
|
MustIPv4Addr("100.64.0.0/10"),
|
||||||
|
|
||||||
|
/*+----------------------+----------------------------+
|
||||||
|
| Attribute | Value |
|
||||||
|
+----------------------+----------------------------+
|
||||||
|
| Address Block | 127.0.0.0/8 |
|
||||||
|
| Name | Loopback |
|
||||||
|
| RFC | [RFC1122], Section 3.2.1.3 |
|
||||||
|
| Allocation Date | September 1981 |
|
||||||
|
| Termination Date | N/A |
|
||||||
|
| Source | False [1] |
|
||||||
|
| Destination | False [1] |
|
||||||
|
| Forwardable | False [1] |
|
||||||
|
| Global | False [1] |
|
||||||
|
| Reserved-by-Protocol | True |
|
||||||
|
+----------------------+----------------------------+*/
|
||||||
|
// [1] Several protocols have been granted exceptions to
|
||||||
|
// this rule. For examples, see [RFC4379] and
|
||||||
|
// [RFC5884].
|
||||||
|
MustIPv4Addr("127.0.0.0/8"),
|
||||||
|
|
||||||
|
/*+----------------------+----------------+
|
||||||
|
| Attribute | Value |
|
||||||
|
+----------------------+----------------+
|
||||||
|
| Address Block | 169.254.0.0/16 |
|
||||||
|
| Name | Link Local |
|
||||||
|
| RFC | [RFC3927] |
|
||||||
|
| Allocation Date | May 2005 |
|
||||||
|
| Termination Date | N/A |
|
||||||
|
| Source | True |
|
||||||
|
| Destination | True |
|
||||||
|
| Forwardable | False |
|
||||||
|
| Global | False |
|
||||||
|
| Reserved-by-Protocol | True |
|
||||||
|
+----------------------+----------------+*/
|
||||||
|
MustIPv4Addr("169.254.0.0/16"),
|
||||||
|
|
||||||
|
/*+----------------------+---------------+
|
||||||
|
| Attribute | Value |
|
||||||
|
+----------------------+---------------+
|
||||||
|
| Address Block | 172.16.0.0/12 |
|
||||||
|
| Name | Private-Use |
|
||||||
|
| RFC | [RFC1918] |
|
||||||
|
| Allocation Date | February 1996 |
|
||||||
|
| Termination Date | N/A |
|
||||||
|
| Source | True |
|
||||||
|
| Destination | True |
|
||||||
|
| Forwardable | True |
|
||||||
|
| Global | False |
|
||||||
|
| Reserved-by-Protocol | False |
|
||||||
|
+----------------------+---------------+*/
|
||||||
|
MustIPv4Addr("172.16.0.0/12"),
|
||||||
|
|
||||||
|
/*+----------------------+---------------------------------+
|
||||||
|
| Attribute | Value |
|
||||||
|
+----------------------+---------------------------------+
|
||||||
|
| Address Block | 192.0.0.0/24 [2] |
|
||||||
|
| Name | IETF Protocol Assignments |
|
||||||
|
| RFC | Section 2.1 of this document |
|
||||||
|
| Allocation Date | January 2010 |
|
||||||
|
| Termination Date | N/A |
|
||||||
|
| Source | False |
|
||||||
|
| Destination | False |
|
||||||
|
| Forwardable | False |
|
||||||
|
| Global | False |
|
||||||
|
| Reserved-by-Protocol | False |
|
||||||
|
+----------------------+---------------------------------+*/
|
||||||
|
// [2] Not usable unless by virtue of a more specific
|
||||||
|
// reservation.
|
||||||
|
MustIPv4Addr("192.0.0.0/24"),
|
||||||
|
|
||||||
|
/*+----------------------+--------------------------------+
|
||||||
|
| Attribute | Value |
|
||||||
|
+----------------------+--------------------------------+
|
||||||
|
| Address Block | 192.0.0.0/29 |
|
||||||
|
| Name | IPv4 Service Continuity Prefix |
|
||||||
|
| RFC | [RFC6333], [RFC7335] |
|
||||||
|
| Allocation Date | June 2011 |
|
||||||
|
| Termination Date | N/A |
|
||||||
|
| Source | True |
|
||||||
|
| Destination | True |
|
||||||
|
| Forwardable | True |
|
||||||
|
| Global | False |
|
||||||
|
| Reserved-by-Protocol | False |
|
||||||
|
+----------------------+--------------------------------+*/
|
||||||
|
MustIPv4Addr("192.0.0.0/29"),
|
||||||
|
|
||||||
|
/*+----------------------+----------------------------+
|
||||||
|
| Attribute | Value |
|
||||||
|
+----------------------+----------------------------+
|
||||||
|
| Address Block | 192.0.2.0/24 |
|
||||||
|
| Name | Documentation (TEST-NET-1) |
|
||||||
|
| RFC | [RFC5737] |
|
||||||
|
| Allocation Date | January 2010 |
|
||||||
|
| Termination Date | N/A |
|
||||||
|
| Source | False |
|
||||||
|
| Destination | False |
|
||||||
|
| Forwardable | False |
|
||||||
|
| Global | False |
|
||||||
|
| Reserved-by-Protocol | False |
|
||||||
|
+----------------------+----------------------------+*/
|
||||||
|
MustIPv4Addr("192.0.2.0/24"),
|
||||||
|
|
||||||
|
/*+----------------------+--------------------+
|
||||||
|
| Attribute | Value |
|
||||||
|
+----------------------+--------------------+
|
||||||
|
| Address Block | 192.88.99.0/24 |
|
||||||
|
| Name | 6to4 Relay Anycast |
|
||||||
|
| RFC | [RFC3068] |
|
||||||
|
| Allocation Date | June 2001 |
|
||||||
|
| Termination Date | N/A |
|
||||||
|
| Source | True |
|
||||||
|
| Destination | True |
|
||||||
|
| Forwardable | True |
|
||||||
|
| Global | True |
|
||||||
|
| Reserved-by-Protocol | False |
|
||||||
|
+----------------------+--------------------+*/
|
||||||
|
MustIPv4Addr("192.88.99.0/24"),
|
||||||
|
|
||||||
|
/*+----------------------+----------------+
|
||||||
|
| Attribute | Value |
|
||||||
|
+----------------------+----------------+
|
||||||
|
| Address Block | 192.168.0.0/16 |
|
||||||
|
| Name | Private-Use |
|
||||||
|
| RFC | [RFC1918] |
|
||||||
|
| Allocation Date | February 1996 |
|
||||||
|
| Termination Date | N/A |
|
||||||
|
| Source | True |
|
||||||
|
| Destination | True |
|
||||||
|
| Forwardable | True |
|
||||||
|
| Global | False |
|
||||||
|
| Reserved-by-Protocol | False |
|
||||||
|
+----------------------+----------------+*/
|
||||||
|
MustIPv4Addr("192.168.0.0/16"),
|
||||||
|
|
||||||
|
/*+----------------------+---------------+
|
||||||
|
| Attribute | Value |
|
||||||
|
+----------------------+---------------+
|
||||||
|
| Address Block | 198.18.0.0/15 |
|
||||||
|
| Name | Benchmarking |
|
||||||
|
| RFC | [RFC2544] |
|
||||||
|
| Allocation Date | March 1999 |
|
||||||
|
| Termination Date | N/A |
|
||||||
|
| Source | True |
|
||||||
|
| Destination | True |
|
||||||
|
| Forwardable | True |
|
||||||
|
| Global | False |
|
||||||
|
| Reserved-by-Protocol | False |
|
||||||
|
+----------------------+---------------+*/
|
||||||
|
MustIPv4Addr("198.18.0.0/15"),
|
||||||
|
|
||||||
|
/*+----------------------+----------------------------+
|
||||||
|
| Attribute | Value |
|
||||||
|
+----------------------+----------------------------+
|
||||||
|
| Address Block | 198.51.100.0/24 |
|
||||||
|
| Name | Documentation (TEST-NET-2) |
|
||||||
|
| RFC | [RFC5737] |
|
||||||
|
| Allocation Date | January 2010 |
|
||||||
|
| Termination Date | N/A |
|
||||||
|
| Source | False |
|
||||||
|
| Destination | False |
|
||||||
|
| Forwardable | False |
|
||||||
|
| Global | False |
|
||||||
|
| Reserved-by-Protocol | False |
|
||||||
|
+----------------------+----------------------------+*/
|
||||||
|
MustIPv4Addr("198.51.100.0/24"),
|
||||||
|
|
||||||
|
/*+----------------------+----------------------------+
|
||||||
|
| Attribute | Value |
|
||||||
|
+----------------------+----------------------------+
|
||||||
|
| Address Block | 203.0.113.0/24 |
|
||||||
|
| Name | Documentation (TEST-NET-3) |
|
||||||
|
| RFC | [RFC5737] |
|
||||||
|
| Allocation Date | January 2010 |
|
||||||
|
| Termination Date | N/A |
|
||||||
|
| Source | False |
|
||||||
|
| Destination | False |
|
||||||
|
| Forwardable | False |
|
||||||
|
| Global | False |
|
||||||
|
| Reserved-by-Protocol | False |
|
||||||
|
+----------------------+----------------------------+*/
|
||||||
|
MustIPv4Addr("203.0.113.0/24"),
|
||||||
|
|
||||||
|
/*+----------------------+----------------------+
|
||||||
|
| Attribute | Value |
|
||||||
|
+----------------------+----------------------+
|
||||||
|
| Address Block | 240.0.0.0/4 |
|
||||||
|
| Name | Reserved |
|
||||||
|
| RFC | [RFC1112], Section 4 |
|
||||||
|
| Allocation Date | August 1989 |
|
||||||
|
| Termination Date | N/A |
|
||||||
|
| Source | False |
|
||||||
|
| Destination | False |
|
||||||
|
| Forwardable | False |
|
||||||
|
| Global | False |
|
||||||
|
| Reserved-by-Protocol | True |
|
||||||
|
+----------------------+----------------------+*/
|
||||||
|
MustIPv4Addr("240.0.0.0/4"),
|
||||||
|
|
||||||
|
/*+----------------------+----------------------+
|
||||||
|
| Attribute | Value |
|
||||||
|
+----------------------+----------------------+
|
||||||
|
| Address Block | 255.255.255.255/32 |
|
||||||
|
| Name | Limited Broadcast |
|
||||||
|
| RFC | [RFC0919], Section 7 |
|
||||||
|
| Allocation Date | October 1984 |
|
||||||
|
| Termination Date | N/A |
|
||||||
|
| Source | False |
|
||||||
|
| Destination | True |
|
||||||
|
| Forwardable | False |
|
||||||
|
| Global | False |
|
||||||
|
| Reserved-by-Protocol | False |
|
||||||
|
+----------------------+----------------------+*/
|
||||||
|
MustIPv4Addr("255.255.255.255/32"),
|
||||||
|
|
||||||
|
/*+----------------------+------------------+
|
||||||
|
| Attribute | Value |
|
||||||
|
+----------------------+------------------+
|
||||||
|
| Address Block | ::1/128 |
|
||||||
|
| Name | Loopback Address |
|
||||||
|
| RFC | [RFC4291] |
|
||||||
|
| Allocation Date | February 2006 |
|
||||||
|
| Termination Date | N/A |
|
||||||
|
| Source | False |
|
||||||
|
| Destination | False |
|
||||||
|
| Forwardable | False |
|
||||||
|
| Global | False |
|
||||||
|
| Reserved-by-Protocol | True |
|
||||||
|
+----------------------+------------------+*/
|
||||||
|
MustIPv6Addr("::1/128"),
|
||||||
|
|
||||||
|
/*+----------------------+---------------------+
|
||||||
|
| Attribute | Value |
|
||||||
|
+----------------------+---------------------+
|
||||||
|
| Address Block | ::/128 |
|
||||||
|
| Name | Unspecified Address |
|
||||||
|
| RFC | [RFC4291] |
|
||||||
|
| Allocation Date | February 2006 |
|
||||||
|
| Termination Date | N/A |
|
||||||
|
| Source | True |
|
||||||
|
| Destination | False |
|
||||||
|
| Forwardable | False |
|
||||||
|
| Global | False |
|
||||||
|
| Reserved-by-Protocol | True |
|
||||||
|
+----------------------+---------------------+*/
|
||||||
|
MustIPv6Addr("::/128"),
|
||||||
|
|
||||||
|
/*+----------------------+---------------------+
|
||||||
|
| Attribute | Value |
|
||||||
|
+----------------------+---------------------+
|
||||||
|
| Address Block | 64:ff9b::/96 |
|
||||||
|
| Name | IPv4-IPv6 Translat. |
|
||||||
|
| RFC | [RFC6052] |
|
||||||
|
| Allocation Date | October 2010 |
|
||||||
|
| Termination Date | N/A |
|
||||||
|
| Source | True |
|
||||||
|
| Destination | True |
|
||||||
|
| Forwardable | True |
|
||||||
|
| Global | True |
|
||||||
|
| Reserved-by-Protocol | False |
|
||||||
|
+----------------------+---------------------+*/
|
||||||
|
MustIPv6Addr("64:ff9b::/96"),
|
||||||
|
|
||||||
|
/*+----------------------+---------------------+
|
||||||
|
| Attribute | Value |
|
||||||
|
+----------------------+---------------------+
|
||||||
|
| Address Block | ::ffff:0:0/96 |
|
||||||
|
| Name | IPv4-mapped Address |
|
||||||
|
| RFC | [RFC4291] |
|
||||||
|
| Allocation Date | February 2006 |
|
||||||
|
| Termination Date | N/A |
|
||||||
|
| Source | False |
|
||||||
|
| Destination | False |
|
||||||
|
| Forwardable | False |
|
||||||
|
| Global | False |
|
||||||
|
| Reserved-by-Protocol | True |
|
||||||
|
+----------------------+---------------------+*/
|
||||||
|
MustIPv6Addr("::ffff:0:0/96"),
|
||||||
|
|
||||||
|
/*+----------------------+----------------------------+
|
||||||
|
| Attribute | Value |
|
||||||
|
+----------------------+----------------------------+
|
||||||
|
| Address Block | 100::/64 |
|
||||||
|
| Name | Discard-Only Address Block |
|
||||||
|
| RFC | [RFC6666] |
|
||||||
|
| Allocation Date | June 2012 |
|
||||||
|
| Termination Date | N/A |
|
||||||
|
| Source | True |
|
||||||
|
| Destination | True |
|
||||||
|
| Forwardable | True |
|
||||||
|
| Global | False |
|
||||||
|
| Reserved-by-Protocol | False |
|
||||||
|
+----------------------+----------------------------+*/
|
||||||
|
MustIPv6Addr("100::/64"),
|
||||||
|
|
||||||
|
/*+----------------------+---------------------------+
|
||||||
|
| Attribute | Value |
|
||||||
|
+----------------------+---------------------------+
|
||||||
|
| Address Block | 2001::/23 |
|
||||||
|
| Name | IETF Protocol Assignments |
|
||||||
|
| RFC | [RFC2928] |
|
||||||
|
| Allocation Date | September 2000 |
|
||||||
|
| Termination Date | N/A |
|
||||||
|
| Source | False[1] |
|
||||||
|
| Destination | False[1] |
|
||||||
|
| Forwardable | False[1] |
|
||||||
|
| Global | False[1] |
|
||||||
|
| Reserved-by-Protocol | False |
|
||||||
|
+----------------------+---------------------------+*/
|
||||||
|
// [1] Unless allowed by a more specific allocation.
|
||||||
|
MustIPv6Addr("2001::/16"),
|
||||||
|
|
||||||
|
/*+----------------------+----------------+
|
||||||
|
| Attribute | Value |
|
||||||
|
+----------------------+----------------+
|
||||||
|
| Address Block | 2001::/32 |
|
||||||
|
| Name | TEREDO |
|
||||||
|
| RFC | [RFC4380] |
|
||||||
|
| Allocation Date | January 2006 |
|
||||||
|
| Termination Date | N/A |
|
||||||
|
| Source | True |
|
||||||
|
| Destination | True |
|
||||||
|
| Forwardable | True |
|
||||||
|
| Global | False |
|
||||||
|
| Reserved-by-Protocol | False |
|
||||||
|
+----------------------+----------------+*/
|
||||||
|
// Covered by previous entry, included for completeness.
|
||||||
|
//
|
||||||
|
// MustIPv6Addr("2001::/16"),
|
||||||
|
|
||||||
|
/*+----------------------+----------------+
|
||||||
|
| Attribute | Value |
|
||||||
|
+----------------------+----------------+
|
||||||
|
| Address Block | 2001:2::/48 |
|
||||||
|
| Name | Benchmarking |
|
||||||
|
| RFC | [RFC5180] |
|
||||||
|
| Allocation Date | April 2008 |
|
||||||
|
| Termination Date | N/A |
|
||||||
|
| Source | True |
|
||||||
|
| Destination | True |
|
||||||
|
| Forwardable | True |
|
||||||
|
| Global | False |
|
||||||
|
| Reserved-by-Protocol | False |
|
||||||
|
+----------------------+----------------+*/
|
||||||
|
// Covered by previous entry, included for completeness.
|
||||||
|
//
|
||||||
|
// MustIPv6Addr("2001:2::/48"),
|
||||||
|
|
||||||
|
/*+----------------------+---------------+
|
||||||
|
| Attribute | Value |
|
||||||
|
+----------------------+---------------+
|
||||||
|
| Address Block | 2001:db8::/32 |
|
||||||
|
| Name | Documentation |
|
||||||
|
| RFC | [RFC3849] |
|
||||||
|
| Allocation Date | July 2004 |
|
||||||
|
| Termination Date | N/A |
|
||||||
|
| Source | False |
|
||||||
|
| Destination | False |
|
||||||
|
| Forwardable | False |
|
||||||
|
| Global | False |
|
||||||
|
| Reserved-by-Protocol | False |
|
||||||
|
+----------------------+---------------+*/
|
||||||
|
// Covered by previous entry, included for completeness.
|
||||||
|
//
|
||||||
|
// MustIPv6Addr("2001:db8::/32"),
|
||||||
|
|
||||||
|
/*+----------------------+--------------+
|
||||||
|
| Attribute | Value |
|
||||||
|
+----------------------+--------------+
|
||||||
|
| Address Block | 2001:10::/28 |
|
||||||
|
| Name | ORCHID |
|
||||||
|
| RFC | [RFC4843] |
|
||||||
|
| Allocation Date | March 2007 |
|
||||||
|
| Termination Date | March 2014 |
|
||||||
|
| Source | False |
|
||||||
|
| Destination | False |
|
||||||
|
| Forwardable | False |
|
||||||
|
| Global | False |
|
||||||
|
| Reserved-by-Protocol | False |
|
||||||
|
+----------------------+--------------+*/
|
||||||
|
// Covered by previous entry, included for completeness.
|
||||||
|
//
|
||||||
|
// MustIPv6Addr("2001:10::/28"),
|
||||||
|
|
||||||
|
/*+----------------------+---------------+
|
||||||
|
| Attribute | Value |
|
||||||
|
+----------------------+---------------+
|
||||||
|
| Address Block | 2002::/16 [2] |
|
||||||
|
| Name | 6to4 |
|
||||||
|
| RFC | [RFC3056] |
|
||||||
|
| Allocation Date | February 2001 |
|
||||||
|
| Termination Date | N/A |
|
||||||
|
| Source | True |
|
||||||
|
| Destination | True |
|
||||||
|
| Forwardable | True |
|
||||||
|
| Global | N/A [2] |
|
||||||
|
| Reserved-by-Protocol | False |
|
||||||
|
+----------------------+---------------+*/
|
||||||
|
// [2] See [RFC3056] for details.
|
||||||
|
MustIPv6Addr("2002::/16"),
|
||||||
|
|
||||||
|
/*+----------------------+--------------+
|
||||||
|
| Attribute | Value |
|
||||||
|
+----------------------+--------------+
|
||||||
|
| Address Block | fc00::/7 |
|
||||||
|
| Name | Unique-Local |
|
||||||
|
| RFC | [RFC4193] |
|
||||||
|
| Allocation Date | October 2005 |
|
||||||
|
| Termination Date | N/A |
|
||||||
|
| Source | True |
|
||||||
|
| Destination | True |
|
||||||
|
| Forwardable | True |
|
||||||
|
| Global | False |
|
||||||
|
| Reserved-by-Protocol | False |
|
||||||
|
+----------------------+--------------+*/
|
||||||
|
MustIPv6Addr("fc00::/7"),
|
||||||
|
|
||||||
|
/*+----------------------+-----------------------+
|
||||||
|
| Attribute | Value |
|
||||||
|
+----------------------+-----------------------+
|
||||||
|
| Address Block | fe80::/10 |
|
||||||
|
| Name | Linked-Scoped Unicast |
|
||||||
|
| RFC | [RFC4291] |
|
||||||
|
| Allocation Date | February 2006 |
|
||||||
|
| Termination Date | N/A |
|
||||||
|
| Source | True |
|
||||||
|
| Destination | True |
|
||||||
|
| Forwardable | False |
|
||||||
|
| Global | False |
|
||||||
|
| Reserved-by-Protocol | True |
|
||||||
|
+----------------------+-----------------------+*/
|
||||||
|
MustIPv6Addr("fe80::/10"),
|
||||||
|
},
|
||||||
|
7335: {
|
||||||
|
// [RFC7335] IPv4 Service Continuity Prefix
|
||||||
|
MustIPv4Addr("192.0.0.0/29"), // [RFC7335], §6 IANA Considerations
|
||||||
|
},
|
||||||
|
ForwardingBlacklist: { // Pseudo-RFC
|
||||||
|
// Blacklist of non-forwardable IP blocks taken from RFC6890
|
||||||
|
//
|
||||||
|
// TODO: the attributes for forwardable should be
|
||||||
|
// searcahble and embedded in the main list of RFCs
|
||||||
|
// above.
|
||||||
|
MustIPv4Addr("0.0.0.0/8"),
|
||||||
|
MustIPv4Addr("127.0.0.0/8"),
|
||||||
|
MustIPv4Addr("169.254.0.0/16"),
|
||||||
|
MustIPv4Addr("192.0.0.0/24"),
|
||||||
|
MustIPv4Addr("192.0.2.0/24"),
|
||||||
|
MustIPv4Addr("198.51.100.0/24"),
|
||||||
|
MustIPv4Addr("203.0.113.0/24"),
|
||||||
|
MustIPv4Addr("240.0.0.0/4"),
|
||||||
|
MustIPv4Addr("255.255.255.255/32"),
|
||||||
|
MustIPv6Addr("::1/128"),
|
||||||
|
MustIPv6Addr("::/128"),
|
||||||
|
MustIPv6Addr("::ffff:0:0/96"),
|
||||||
|
|
||||||
|
// There is no way of expressing a whitelist per RFC2928
|
||||||
|
// atm without creating a negative mask, which I don't
|
||||||
|
// want to do atm.
|
||||||
|
//MustIPv6Addr("2001::/23"),
|
||||||
|
|
||||||
|
MustIPv6Addr("2001:db8::/32"),
|
||||||
|
MustIPv6Addr("2001:10::/28"),
|
||||||
|
MustIPv6Addr("fe80::/10"),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// VisitAllRFCs iterates over all known RFCs and calls the visitor
|
||||||
|
func VisitAllRFCs(fn func(rfcNum uint, sockaddrs SockAddrs)) {
|
||||||
|
rfcNetMap := KnownRFCs()
|
||||||
|
|
||||||
|
// Blacklist of faux-RFCs. Don't show the world that we're abusing the
|
||||||
|
// RFC system in this library.
|
||||||
|
rfcBlacklist := map[uint]struct{}{
|
||||||
|
ForwardingBlacklist: {},
|
||||||
|
}
|
||||||
|
|
||||||
|
for rfcNum, sas := range rfcNetMap {
|
||||||
|
if _, found := rfcBlacklist[rfcNum]; !found {
|
||||||
|
fn(rfcNum, sas)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
19
vendor/github.com/hashicorp/go-sockaddr/route_info.go
generated
vendored
Normal file
19
vendor/github.com/hashicorp/go-sockaddr/route_info.go
generated
vendored
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
package sockaddr
|
||||||
|
|
||||||
|
// RouteInterface specifies an interface for obtaining memoized route table and
|
||||||
|
// network information from a given OS.
|
||||||
|
type RouteInterface interface {
|
||||||
|
// GetDefaultInterfaceName returns the name of the interface that has a
|
||||||
|
// default route or an error and an empty string if a problem was
|
||||||
|
// encountered.
|
||||||
|
GetDefaultInterfaceName() (string, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// VisitCommands visits each command used by the platform-specific RouteInfo
|
||||||
|
// implementation.
|
||||||
|
func (ri routeInfo) VisitCommands(fn func(name string, cmd []string)) {
|
||||||
|
for k, v := range ri.cmds {
|
||||||
|
cmds := append([]string(nil), v...)
|
||||||
|
fn(k, cmds)
|
||||||
|
}
|
||||||
|
}
|
36
vendor/github.com/hashicorp/go-sockaddr/route_info_bsd.go
generated
vendored
Normal file
36
vendor/github.com/hashicorp/go-sockaddr/route_info_bsd.go
generated
vendored
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
// +build darwin dragonfly freebsd netbsd openbsd
|
||||||
|
|
||||||
|
package sockaddr
|
||||||
|
|
||||||
|
import "os/exec"
|
||||||
|
|
||||||
|
var cmds map[string][]string = map[string][]string{
|
||||||
|
"route": {"/sbin/route", "-n", "get", "default"},
|
||||||
|
}
|
||||||
|
|
||||||
|
type routeInfo struct {
|
||||||
|
cmds map[string][]string
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewRouteInfo returns a BSD-specific implementation of the RouteInfo
|
||||||
|
// interface.
|
||||||
|
func NewRouteInfo() (routeInfo, error) {
|
||||||
|
return routeInfo{
|
||||||
|
cmds: cmds,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetDefaultInterfaceName returns the interface name attached to the default
|
||||||
|
// route on the default interface.
|
||||||
|
func (ri routeInfo) GetDefaultInterfaceName() (string, error) {
|
||||||
|
out, err := exec.Command(cmds["route"][0], cmds["route"][1:]...).Output()
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
var ifName string
|
||||||
|
if ifName, err = parseDefaultIfNameFromRoute(string(out)); err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return ifName, nil
|
||||||
|
}
|
10
vendor/github.com/hashicorp/go-sockaddr/route_info_default.go
generated
vendored
Normal file
10
vendor/github.com/hashicorp/go-sockaddr/route_info_default.go
generated
vendored
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
// +build android nacl plan9
|
||||||
|
|
||||||
|
package sockaddr
|
||||||
|
|
||||||
|
import "errors"
|
||||||
|
|
||||||
|
// getDefaultIfName is the default interface function for unsupported platforms.
|
||||||
|
func getDefaultIfName() (string, error) {
|
||||||
|
return "", errors.New("No default interface found (unsupported platform)")
|
||||||
|
}
|
37
vendor/github.com/hashicorp/go-sockaddr/route_info_linux.go
generated
vendored
Normal file
37
vendor/github.com/hashicorp/go-sockaddr/route_info_linux.go
generated
vendored
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
package sockaddr
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"os/exec"
|
||||||
|
)
|
||||||
|
|
||||||
|
var cmds map[string][]string = map[string][]string{
|
||||||
|
"ip": {"/sbin/ip", "route"},
|
||||||
|
}
|
||||||
|
|
||||||
|
type routeInfo struct {
|
||||||
|
cmds map[string][]string
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewRouteInfo returns a Linux-specific implementation of the RouteInfo
|
||||||
|
// interface.
|
||||||
|
func NewRouteInfo() (routeInfo, error) {
|
||||||
|
return routeInfo{
|
||||||
|
cmds: cmds,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetDefaultInterfaceName returns the interface name attached to the default
|
||||||
|
// route on the default interface.
|
||||||
|
func (ri routeInfo) GetDefaultInterfaceName() (string, error) {
|
||||||
|
out, err := exec.Command(cmds["ip"][0], cmds["ip"][1:]...).Output()
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
var ifName string
|
||||||
|
if ifName, err = parseDefaultIfNameFromIPCmd(string(out)); err != nil {
|
||||||
|
return "", errors.New("No default interface found")
|
||||||
|
}
|
||||||
|
return ifName, nil
|
||||||
|
}
|
37
vendor/github.com/hashicorp/go-sockaddr/route_info_solaris.go
generated
vendored
Normal file
37
vendor/github.com/hashicorp/go-sockaddr/route_info_solaris.go
generated
vendored
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
package sockaddr
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"os/exec"
|
||||||
|
)
|
||||||
|
|
||||||
|
var cmds map[string][]string = map[string][]string{
|
||||||
|
"route": {"/usr/sbin/route", "-n", "get", "default"},
|
||||||
|
}
|
||||||
|
|
||||||
|
type routeInfo struct {
|
||||||
|
cmds map[string][]string
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewRouteInfo returns a BSD-specific implementation of the RouteInfo
|
||||||
|
// interface.
|
||||||
|
func NewRouteInfo() (routeInfo, error) {
|
||||||
|
return routeInfo{
|
||||||
|
cmds: cmds,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetDefaultInterfaceName returns the interface name attached to the default
|
||||||
|
// route on the default interface.
|
||||||
|
func (ri routeInfo) GetDefaultInterfaceName() (string, error) {
|
||||||
|
out, err := exec.Command(cmds["route"][0], cmds["route"][1:]...).Output()
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
var ifName string
|
||||||
|
if ifName, err = parseDefaultIfNameFromRoute(string(out)); err != nil {
|
||||||
|
return "", errors.New("No default interface found")
|
||||||
|
}
|
||||||
|
return ifName, nil
|
||||||
|
}
|
41
vendor/github.com/hashicorp/go-sockaddr/route_info_windows.go
generated
vendored
Normal file
41
vendor/github.com/hashicorp/go-sockaddr/route_info_windows.go
generated
vendored
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
package sockaddr
|
||||||
|
|
||||||
|
import "os/exec"
|
||||||
|
|
||||||
|
var cmds map[string][]string = map[string][]string{
|
||||||
|
"netstat": {"netstat", "-rn"},
|
||||||
|
"ipconfig": {"ipconfig"},
|
||||||
|
}
|
||||||
|
|
||||||
|
type routeInfo struct {
|
||||||
|
cmds map[string][]string
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewRouteInfo returns a BSD-specific implementation of the RouteInfo
|
||||||
|
// interface.
|
||||||
|
func NewRouteInfo() (routeInfo, error) {
|
||||||
|
return routeInfo{
|
||||||
|
cmds: cmds,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetDefaultInterfaceName returns the interface name attached to the default
|
||||||
|
// route on the default interface.
|
||||||
|
func (ri routeInfo) GetDefaultInterfaceName() (string, error) {
|
||||||
|
ifNameOut, err := exec.Command(cmds["netstat"][0], cmds["netstat"][1:]...).Output()
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
ipconfigOut, err := exec.Command(cmds["ipconfig"][0], cmds["ipconfig"][1:]...).Output()
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
ifName, err := parseDefaultIfNameWindows(string(ifNameOut), string(ipconfigOut))
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
return ifName, nil
|
||||||
|
}
|
178
vendor/github.com/hashicorp/go-sockaddr/sockaddr.go
generated
vendored
Normal file
178
vendor/github.com/hashicorp/go-sockaddr/sockaddr.go
generated
vendored
Normal file
|
@ -0,0 +1,178 @@
|
||||||
|
package sockaddr
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
type SockAddrType int
|
||||||
|
type AttrName string
|
||||||
|
|
||||||
|
const (
|
||||||
|
TypeUnknown SockAddrType = 0x0
|
||||||
|
TypeUnix = 0x1
|
||||||
|
TypeIPv4 = 0x2
|
||||||
|
TypeIPv6 = 0x4
|
||||||
|
|
||||||
|
// TypeIP is the union of TypeIPv4 and TypeIPv6
|
||||||
|
TypeIP = 0x6
|
||||||
|
)
|
||||||
|
|
||||||
|
type SockAddr interface {
|
||||||
|
// CmpRFC returns 0 if SockAddr exactly matches one of the matched RFC
|
||||||
|
// networks, -1 if the receiver is contained within the RFC network, or
|
||||||
|
// 1 if the address is not contained within the RFC.
|
||||||
|
CmpRFC(rfcNum uint, sa SockAddr) int
|
||||||
|
|
||||||
|
// Contains returns true if the SockAddr arg is contained within the
|
||||||
|
// receiver
|
||||||
|
Contains(SockAddr) bool
|
||||||
|
|
||||||
|
// Equal allows for the comparison of two SockAddrs
|
||||||
|
Equal(SockAddr) bool
|
||||||
|
|
||||||
|
DialPacketArgs() (string, string)
|
||||||
|
DialStreamArgs() (string, string)
|
||||||
|
ListenPacketArgs() (string, string)
|
||||||
|
ListenStreamArgs() (string, string)
|
||||||
|
|
||||||
|
// String returns the string representation of SockAddr
|
||||||
|
String() string
|
||||||
|
|
||||||
|
// Type returns the SockAddrType
|
||||||
|
Type() SockAddrType
|
||||||
|
}
|
||||||
|
|
||||||
|
// sockAddrAttrMap is a map of the SockAddr type-specific attributes.
|
||||||
|
var sockAddrAttrMap map[AttrName]func(SockAddr) string
|
||||||
|
var sockAddrAttrs []AttrName
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
sockAddrInit()
|
||||||
|
}
|
||||||
|
|
||||||
|
// New creates a new SockAddr from the string. The order in which New()
|
||||||
|
// attempts to construct a SockAddr is: IPv4Addr, IPv6Addr, SockAddrUnix.
|
||||||
|
//
|
||||||
|
// NOTE: New() relies on the heuristic wherein if the path begins with either a
|
||||||
|
// '.' or '/' character before creating a new UnixSock. For UNIX sockets that
|
||||||
|
// are absolute paths or are nested within a sub-directory, this works as
|
||||||
|
// expected, however if the UNIX socket is contained in the current working
|
||||||
|
// directory, this will fail unless the path begins with "./"
|
||||||
|
// (e.g. "./my-local-socket"). Calls directly to NewUnixSock() do not suffer
|
||||||
|
// this limitation. Invalid IP addresses such as "256.0.0.0/-1" will run afoul
|
||||||
|
// of this heuristic and be assumed to be a valid UNIX socket path (which they
|
||||||
|
// are, but it is probably not what you want and you won't realize it until you
|
||||||
|
// stat(2) the file system to discover it doesn't exist).
|
||||||
|
func NewSockAddr(s string) (SockAddr, error) {
|
||||||
|
ipv4Addr, err := NewIPv4Addr(s)
|
||||||
|
if err == nil {
|
||||||
|
return ipv4Addr, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
ipv6Addr, err := NewIPv6Addr(s)
|
||||||
|
if err == nil {
|
||||||
|
return ipv6Addr, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check to make sure the string begins with either a '.' or '/', or
|
||||||
|
// contains a '/'.
|
||||||
|
if len(s) > 1 && (strings.IndexAny(s[0:1], "./") != -1 || strings.IndexByte(s, '/') != -1) {
|
||||||
|
unixSock, err := NewUnixSock(s)
|
||||||
|
if err == nil {
|
||||||
|
return unixSock, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, fmt.Errorf("Unable to convert %q to an IPv4 or IPv6 address, or a UNIX Socket", s)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToIPAddr returns an IPAddr type or nil if the type conversion fails.
|
||||||
|
func ToIPAddr(sa SockAddr) *IPAddr {
|
||||||
|
ipa, ok := sa.(IPAddr)
|
||||||
|
if !ok {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return &ipa
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToIPv4Addr returns an IPv4Addr type or nil if the type conversion fails.
|
||||||
|
func ToIPv4Addr(sa SockAddr) *IPv4Addr {
|
||||||
|
switch v := sa.(type) {
|
||||||
|
case IPv4Addr:
|
||||||
|
return &v
|
||||||
|
default:
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToIPv6Addr returns an IPv6Addr type or nil if the type conversion fails.
|
||||||
|
func ToIPv6Addr(sa SockAddr) *IPv6Addr {
|
||||||
|
switch v := sa.(type) {
|
||||||
|
case IPv6Addr:
|
||||||
|
return &v
|
||||||
|
default:
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToUnixSock returns a UnixSock type or nil if the type conversion fails.
|
||||||
|
func ToUnixSock(sa SockAddr) *UnixSock {
|
||||||
|
switch v := sa.(type) {
|
||||||
|
case UnixSock:
|
||||||
|
return &v
|
||||||
|
default:
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SockAddrAttr returns a string representation of an attribute for the given
|
||||||
|
// SockAddr.
|
||||||
|
func SockAddrAttr(sa SockAddr, selector AttrName) string {
|
||||||
|
fn, found := sockAddrAttrMap[selector]
|
||||||
|
if !found {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
return fn(sa)
|
||||||
|
}
|
||||||
|
|
||||||
|
// String() for SockAddrType returns a string representation of the
|
||||||
|
// SockAddrType (e.g. "IPv4", "IPv6", "UNIX", "IP", or "unknown").
|
||||||
|
func (sat SockAddrType) String() string {
|
||||||
|
switch sat {
|
||||||
|
case TypeIPv4:
|
||||||
|
return "IPv4"
|
||||||
|
case TypeIPv6:
|
||||||
|
return "IPv6"
|
||||||
|
// There is no concrete "IP" type. Leaving here as a reminder.
|
||||||
|
// case TypeIP:
|
||||||
|
// return "IP"
|
||||||
|
case TypeUnix:
|
||||||
|
return "UNIX"
|
||||||
|
default:
|
||||||
|
panic("unsupported type")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// sockAddrInit is called once at init()
|
||||||
|
func sockAddrInit() {
|
||||||
|
sockAddrAttrs = []AttrName{
|
||||||
|
"type", // type should be first
|
||||||
|
"string",
|
||||||
|
}
|
||||||
|
|
||||||
|
sockAddrAttrMap = map[AttrName]func(sa SockAddr) string{
|
||||||
|
"string": func(sa SockAddr) string {
|
||||||
|
return sa.String()
|
||||||
|
},
|
||||||
|
"type": func(sa SockAddr) string {
|
||||||
|
return sa.Type().String()
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnixSockAttrs returns a list of attributes supported by the UnixSock type
|
||||||
|
func SockAddrAttrs() []AttrName {
|
||||||
|
return sockAddrAttrs
|
||||||
|
}
|
193
vendor/github.com/hashicorp/go-sockaddr/sockaddrs.go
generated
vendored
Normal file
193
vendor/github.com/hashicorp/go-sockaddr/sockaddrs.go
generated
vendored
Normal file
|
@ -0,0 +1,193 @@
|
||||||
|
package sockaddr
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"sort"
|
||||||
|
)
|
||||||
|
|
||||||
|
// SockAddrs is a slice of SockAddrs
|
||||||
|
type SockAddrs []SockAddr
|
||||||
|
|
||||||
|
func (s SockAddrs) Len() int { return len(s) }
|
||||||
|
func (s SockAddrs) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
|
||||||
|
|
||||||
|
// CmpAddrFunc is the function signature that must be met to be used in the
|
||||||
|
// OrderedAddrBy multiAddrSorter
|
||||||
|
type CmpAddrFunc func(p1, p2 *SockAddr) int
|
||||||
|
|
||||||
|
// multiAddrSorter implements the Sort interface, sorting the SockAddrs within.
|
||||||
|
type multiAddrSorter struct {
|
||||||
|
addrs SockAddrs
|
||||||
|
cmp []CmpAddrFunc
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sort sorts the argument slice according to the Cmp functions passed to
|
||||||
|
// OrderedAddrBy.
|
||||||
|
func (ms *multiAddrSorter) Sort(sockAddrs SockAddrs) {
|
||||||
|
ms.addrs = sockAddrs
|
||||||
|
sort.Sort(ms)
|
||||||
|
}
|
||||||
|
|
||||||
|
// OrderedAddrBy sorts SockAddr by the list of sort function pointers.
|
||||||
|
func OrderedAddrBy(cmpFuncs ...CmpAddrFunc) *multiAddrSorter {
|
||||||
|
return &multiAddrSorter{
|
||||||
|
cmp: cmpFuncs,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Len is part of sort.Interface.
|
||||||
|
func (ms *multiAddrSorter) Len() int {
|
||||||
|
return len(ms.addrs)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Less is part of sort.Interface. It is implemented by looping along the
|
||||||
|
// Cmp() functions until it finds a comparison that is either less than,
|
||||||
|
// equal to, or greater than.
|
||||||
|
func (ms *multiAddrSorter) Less(i, j int) bool {
|
||||||
|
p, q := &ms.addrs[i], &ms.addrs[j]
|
||||||
|
// Try all but the last comparison.
|
||||||
|
var k int
|
||||||
|
for k = 0; k < len(ms.cmp)-1; k++ {
|
||||||
|
cmp := ms.cmp[k]
|
||||||
|
x := cmp(p, q)
|
||||||
|
switch x {
|
||||||
|
case -1:
|
||||||
|
// p < q, so we have a decision.
|
||||||
|
return true
|
||||||
|
case 1:
|
||||||
|
// p > q, so we have a decision.
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
// p == q; try the next comparison.
|
||||||
|
}
|
||||||
|
// All comparisons to here said "equal", so just return whatever the
|
||||||
|
// final comparison reports.
|
||||||
|
switch ms.cmp[k](p, q) {
|
||||||
|
case -1:
|
||||||
|
return true
|
||||||
|
case 1:
|
||||||
|
return false
|
||||||
|
default:
|
||||||
|
// Still a tie! Now what?
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Swap is part of sort.Interface.
|
||||||
|
func (ms *multiAddrSorter) Swap(i, j int) {
|
||||||
|
ms.addrs[i], ms.addrs[j] = ms.addrs[j], ms.addrs[i]
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
// NOTE (sean@): These constants are here for code readability only and
|
||||||
|
// are sprucing up the code for readability purposes. Some of the
|
||||||
|
// Cmp*() variants have confusing logic (especially when dealing with
|
||||||
|
// mixed-type comparisons) and this, I think, has made it easier to grok
|
||||||
|
// the code faster.
|
||||||
|
sortReceiverBeforeArg = -1
|
||||||
|
sortDeferDecision = 0
|
||||||
|
sortArgBeforeReceiver = 1
|
||||||
|
)
|
||||||
|
|
||||||
|
// AscAddress is a sorting function to sort SockAddrs by their respective
|
||||||
|
// address type. Non-equal types are deferred in the sort.
|
||||||
|
func AscAddress(p1Ptr, p2Ptr *SockAddr) int {
|
||||||
|
p1 := *p1Ptr
|
||||||
|
p2 := *p2Ptr
|
||||||
|
|
||||||
|
switch v := p1.(type) {
|
||||||
|
case IPv4Addr:
|
||||||
|
return v.CmpAddress(p2)
|
||||||
|
case IPv6Addr:
|
||||||
|
return v.CmpAddress(p2)
|
||||||
|
case UnixSock:
|
||||||
|
return v.CmpAddress(p2)
|
||||||
|
default:
|
||||||
|
return sortDeferDecision
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// AscPort is a sorting function to sort SockAddrs by their respective address
|
||||||
|
// type. Non-equal types are deferred in the sort.
|
||||||
|
func AscPort(p1Ptr, p2Ptr *SockAddr) int {
|
||||||
|
p1 := *p1Ptr
|
||||||
|
p2 := *p2Ptr
|
||||||
|
|
||||||
|
switch v := p1.(type) {
|
||||||
|
case IPv4Addr:
|
||||||
|
return v.CmpPort(p2)
|
||||||
|
case IPv6Addr:
|
||||||
|
return v.CmpPort(p2)
|
||||||
|
default:
|
||||||
|
return sortDeferDecision
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// AscPrivate is a sorting function to sort "more secure" private values before
|
||||||
|
// "more public" values. Both IPv4 and IPv6 are compared against RFC6890
|
||||||
|
// (RFC6890 includes, and is not limited to, RFC1918 and RFC6598 for IPv4, and
|
||||||
|
// IPv6 includes RFC4193).
|
||||||
|
func AscPrivate(p1Ptr, p2Ptr *SockAddr) int {
|
||||||
|
p1 := *p1Ptr
|
||||||
|
p2 := *p2Ptr
|
||||||
|
|
||||||
|
switch v := p1.(type) {
|
||||||
|
case IPv4Addr, IPv6Addr:
|
||||||
|
return v.CmpRFC(6890, p2)
|
||||||
|
default:
|
||||||
|
return sortDeferDecision
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// AscNetworkSize is a sorting function to sort SockAddrs based on their network
|
||||||
|
// size. Non-equal types are deferred in the sort.
|
||||||
|
func AscNetworkSize(p1Ptr, p2Ptr *SockAddr) int {
|
||||||
|
p1 := *p1Ptr
|
||||||
|
p2 := *p2Ptr
|
||||||
|
p1Type := p1.Type()
|
||||||
|
p2Type := p2.Type()
|
||||||
|
|
||||||
|
// Network size operations on non-IP types make no sense
|
||||||
|
if p1Type != p2Type && p1Type != TypeIP {
|
||||||
|
return sortDeferDecision
|
||||||
|
}
|
||||||
|
|
||||||
|
ipA := p1.(IPAddr)
|
||||||
|
ipB := p2.(IPAddr)
|
||||||
|
|
||||||
|
return bytes.Compare([]byte(*ipA.NetIPMask()), []byte(*ipB.NetIPMask()))
|
||||||
|
}
|
||||||
|
|
||||||
|
// AscType is a sorting function to sort "more secure" types before
|
||||||
|
// "less-secure" types.
|
||||||
|
func AscType(p1Ptr, p2Ptr *SockAddr) int {
|
||||||
|
p1 := *p1Ptr
|
||||||
|
p2 := *p2Ptr
|
||||||
|
p1Type := p1.Type()
|
||||||
|
p2Type := p2.Type()
|
||||||
|
switch {
|
||||||
|
case p1Type < p2Type:
|
||||||
|
return sortReceiverBeforeArg
|
||||||
|
case p1Type == p2Type:
|
||||||
|
return sortDeferDecision
|
||||||
|
case p1Type > p2Type:
|
||||||
|
return sortArgBeforeReceiver
|
||||||
|
default:
|
||||||
|
return sortDeferDecision
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// FilterByType returns two lists: a list of matched and unmatched SockAddrs
|
||||||
|
func (sas SockAddrs) FilterByType(type_ SockAddrType) (matched, excluded SockAddrs) {
|
||||||
|
matched = make(SockAddrs, 0, len(sas))
|
||||||
|
excluded = make(SockAddrs, 0, len(sas))
|
||||||
|
|
||||||
|
for _, sa := range sas {
|
||||||
|
if sa.Type()&type_ != 0 {
|
||||||
|
matched = append(matched, sa)
|
||||||
|
} else {
|
||||||
|
excluded = append(excluded, sa)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return matched, excluded
|
||||||
|
}
|
135
vendor/github.com/hashicorp/go-sockaddr/unixsock.go
generated
vendored
Normal file
135
vendor/github.com/hashicorp/go-sockaddr/unixsock.go
generated
vendored
Normal file
|
@ -0,0 +1,135 @@
|
||||||
|
package sockaddr
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
type UnixSock struct {
|
||||||
|
SockAddr
|
||||||
|
path string
|
||||||
|
}
|
||||||
|
type UnixSocks []*UnixSock
|
||||||
|
|
||||||
|
// unixAttrMap is a map of the UnixSockAddr type-specific attributes.
|
||||||
|
var unixAttrMap map[AttrName]func(UnixSock) string
|
||||||
|
var unixAttrs []AttrName
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
unixAttrInit()
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewUnixSock creates an UnixSock from a string path. String can be in the
|
||||||
|
// form of either URI-based string (e.g. `file:///etc/passwd`), an absolute
|
||||||
|
// path (e.g. `/etc/passwd`), or a relative path (e.g. `./foo`).
|
||||||
|
func NewUnixSock(s string) (ret UnixSock, err error) {
|
||||||
|
ret.path = s
|
||||||
|
return ret, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// CmpAddress follows the Cmp() standard protocol and returns:
|
||||||
|
//
|
||||||
|
// - -1 If the receiver should sort first because its name lexically sorts before arg
|
||||||
|
// - 0 if the SockAddr arg is not a UnixSock, or is a UnixSock with the same path.
|
||||||
|
// - 1 If the argument should sort first.
|
||||||
|
func (us UnixSock) CmpAddress(sa SockAddr) int {
|
||||||
|
usb, ok := sa.(UnixSock)
|
||||||
|
if !ok {
|
||||||
|
return sortDeferDecision
|
||||||
|
}
|
||||||
|
|
||||||
|
return strings.Compare(us.Path(), usb.Path())
|
||||||
|
}
|
||||||
|
|
||||||
|
// DialPacketArgs returns the arguments required to be passed to net.DialUnix()
|
||||||
|
// with the `unixgram` network type.
|
||||||
|
func (us UnixSock) DialPacketArgs() (network, dialArgs string) {
|
||||||
|
return "unixgram", us.path
|
||||||
|
}
|
||||||
|
|
||||||
|
// DialStreamArgs returns the arguments required to be passed to net.DialUnix()
|
||||||
|
// with the `unix` network type.
|
||||||
|
func (us UnixSock) DialStreamArgs() (network, dialArgs string) {
|
||||||
|
return "unix", us.path
|
||||||
|
}
|
||||||
|
|
||||||
|
// Equal returns true if a SockAddr is equal to the receiving UnixSock.
|
||||||
|
func (us UnixSock) Equal(sa SockAddr) bool {
|
||||||
|
usb, ok := sa.(UnixSock)
|
||||||
|
if !ok {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if us.Path() != usb.Path() {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListenPacketArgs returns the arguments required to be passed to
|
||||||
|
// net.ListenUnixgram() with the `unixgram` network type.
|
||||||
|
func (us UnixSock) ListenPacketArgs() (network, dialArgs string) {
|
||||||
|
return "unixgram", us.path
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListenStreamArgs returns the arguments required to be passed to
|
||||||
|
// net.ListenUnix() with the `unix` network type.
|
||||||
|
func (us UnixSock) ListenStreamArgs() (network, dialArgs string) {
|
||||||
|
return "unix", us.path
|
||||||
|
}
|
||||||
|
|
||||||
|
// MustUnixSock is a helper method that must return an UnixSock or panic on
|
||||||
|
// invalid input.
|
||||||
|
func MustUnixSock(addr string) UnixSock {
|
||||||
|
us, err := NewUnixSock(addr)
|
||||||
|
if err != nil {
|
||||||
|
panic(fmt.Sprintf("Unable to create a UnixSock from %+q: %v", addr, err))
|
||||||
|
}
|
||||||
|
return us
|
||||||
|
}
|
||||||
|
|
||||||
|
// Path returns the given path of the UnixSock
|
||||||
|
func (us UnixSock) Path() string {
|
||||||
|
return us.path
|
||||||
|
}
|
||||||
|
|
||||||
|
// String returns the path of the UnixSock
|
||||||
|
func (us UnixSock) String() string {
|
||||||
|
return fmt.Sprintf("%+q", us.path)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Type is used as a type switch and returns TypeUnix
|
||||||
|
func (UnixSock) Type() SockAddrType {
|
||||||
|
return TypeUnix
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnixSockAttrs returns a list of attributes supported by the UnixSockAddr type
|
||||||
|
func UnixSockAttrs() []AttrName {
|
||||||
|
return unixAttrs
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnixSockAttr returns a string representation of an attribute for the given
|
||||||
|
// UnixSock.
|
||||||
|
func UnixSockAttr(us UnixSock, attrName AttrName) string {
|
||||||
|
fn, found := unixAttrMap[attrName]
|
||||||
|
if !found {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
return fn(us)
|
||||||
|
}
|
||||||
|
|
||||||
|
// unixAttrInit is called once at init()
|
||||||
|
func unixAttrInit() {
|
||||||
|
// Sorted for human readability
|
||||||
|
unixAttrs = []AttrName{
|
||||||
|
"path",
|
||||||
|
}
|
||||||
|
|
||||||
|
unixAttrMap = map[AttrName]func(us UnixSock) string{
|
||||||
|
"path": func(us UnixSock) string {
|
||||||
|
return us.Path()
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
2
vendor/github.com/hashicorp/memberlist/README.md
generated
vendored
2
vendor/github.com/hashicorp/memberlist/README.md
generated
vendored
|
@ -82,7 +82,7 @@ least one existing member in order to join the cluster. The new member
|
||||||
does a full state sync with the existing member over TCP and begins gossiping its
|
does a full state sync with the existing member over TCP and begins gossiping its
|
||||||
existence to the cluster.
|
existence to the cluster.
|
||||||
|
|
||||||
Gossip is done over UDP to a with a configurable but fixed fanout and interval.
|
Gossip is done over UDP with a configurable but fixed fanout and interval.
|
||||||
This ensures that network usage is constant with regards to number of nodes, as opposed to
|
This ensures that network usage is constant with regards to number of nodes, as opposed to
|
||||||
exponential growth that can occur with traditional heartbeat mechanisms.
|
exponential growth that can occur with traditional heartbeat mechanisms.
|
||||||
Complete state exchanges with a random node are done periodically over
|
Complete state exchanges with a random node are done periodically over
|
||||||
|
|
69
vendor/github.com/hashicorp/memberlist/awareness.go
generated
vendored
Normal file
69
vendor/github.com/hashicorp/memberlist/awareness.go
generated
vendored
Normal file
|
@ -0,0 +1,69 @@
|
||||||
|
package memberlist
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/armon/go-metrics"
|
||||||
|
)
|
||||||
|
|
||||||
|
// awareness manages a simple metric for tracking the estimated health of the
|
||||||
|
// local node. Health is primary the node's ability to respond in the soft
|
||||||
|
// real-time manner required for correct health checking of other nodes in the
|
||||||
|
// cluster.
|
||||||
|
type awareness struct {
|
||||||
|
sync.RWMutex
|
||||||
|
|
||||||
|
// max is the upper threshold for the timeout scale (the score will be
|
||||||
|
// constrained to be from 0 <= score < max).
|
||||||
|
max int
|
||||||
|
|
||||||
|
// score is the current awareness score. Lower values are healthier and
|
||||||
|
// zero is the minimum value.
|
||||||
|
score int
|
||||||
|
}
|
||||||
|
|
||||||
|
// newAwareness returns a new awareness object.
|
||||||
|
func newAwareness(max int) *awareness {
|
||||||
|
return &awareness{
|
||||||
|
max: max,
|
||||||
|
score: 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ApplyDelta takes the given delta and applies it to the score in a thread-safe
|
||||||
|
// manner. It also enforces a floor of zero and a max of max, so deltas may not
|
||||||
|
// change the overall score if it's railed at one of the extremes.
|
||||||
|
func (a *awareness) ApplyDelta(delta int) {
|
||||||
|
a.Lock()
|
||||||
|
initial := a.score
|
||||||
|
a.score += delta
|
||||||
|
if a.score < 0 {
|
||||||
|
a.score = 0
|
||||||
|
} else if a.score > (a.max - 1) {
|
||||||
|
a.score = (a.max - 1)
|
||||||
|
}
|
||||||
|
final := a.score
|
||||||
|
a.Unlock()
|
||||||
|
|
||||||
|
if initial != final {
|
||||||
|
metrics.SetGauge([]string{"memberlist", "health", "score"}, float32(final))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetHealthScore returns the raw health score.
|
||||||
|
func (a *awareness) GetHealthScore() int {
|
||||||
|
a.RLock()
|
||||||
|
score := a.score
|
||||||
|
a.RUnlock()
|
||||||
|
return score
|
||||||
|
}
|
||||||
|
|
||||||
|
// ScaleTimeout takes the given duration and scales it based on the current
|
||||||
|
// score. Less healthyness will lead to longer timeouts.
|
||||||
|
func (a *awareness) ScaleTimeout(timeout time.Duration) time.Duration {
|
||||||
|
a.RLock()
|
||||||
|
score := a.score
|
||||||
|
a.RUnlock()
|
||||||
|
return timeout * (time.Duration(score) + 1)
|
||||||
|
}
|
102
vendor/github.com/hashicorp/memberlist/config.go
generated
vendored
102
vendor/github.com/hashicorp/memberlist/config.go
generated
vendored
|
@ -11,10 +11,15 @@ type Config struct {
|
||||||
// The name of this node. This must be unique in the cluster.
|
// The name of this node. This must be unique in the cluster.
|
||||||
Name string
|
Name string
|
||||||
|
|
||||||
|
// Transport is a hook for providing custom code to communicate with
|
||||||
|
// other nodes. If this is left nil, then memberlist will by default
|
||||||
|
// make a NetTransport using BindAddr and BindPort from this structure.
|
||||||
|
Transport Transport
|
||||||
|
|
||||||
// Configuration related to what address to bind to and ports to
|
// Configuration related to what address to bind to and ports to
|
||||||
// listen on. The port is used for both UDP and TCP gossip.
|
// listen on. The port is used for both UDP and TCP gossip. It is
|
||||||
// It is assumed other nodes are running on this port, but they
|
// assumed other nodes are running on this port, but they do not need
|
||||||
// do not need to.
|
// to.
|
||||||
BindAddr string
|
BindAddr string
|
||||||
BindPort int
|
BindPort int
|
||||||
|
|
||||||
|
@ -28,8 +33,11 @@ type Config struct {
|
||||||
// ProtocolVersionMax.
|
// ProtocolVersionMax.
|
||||||
ProtocolVersion uint8
|
ProtocolVersion uint8
|
||||||
|
|
||||||
// TCPTimeout is the timeout for establishing a TCP connection with
|
// TCPTimeout is the timeout for establishing a stream connection with
|
||||||
// a remote node for a full state sync.
|
// a remote node for a full state sync, and for stream read and write
|
||||||
|
// operations. This is a legacy name for backwards compatibility, but
|
||||||
|
// should really be called StreamTimeout now that we have generalized
|
||||||
|
// the transport.
|
||||||
TCPTimeout time.Duration
|
TCPTimeout time.Duration
|
||||||
|
|
||||||
// IndirectChecks is the number of nodes that will be asked to perform
|
// IndirectChecks is the number of nodes that will be asked to perform
|
||||||
|
@ -63,6 +71,23 @@ type Config struct {
|
||||||
// still alive.
|
// still alive.
|
||||||
SuspicionMult int
|
SuspicionMult int
|
||||||
|
|
||||||
|
// SuspicionMaxTimeoutMult is the multiplier applied to the
|
||||||
|
// SuspicionTimeout used as an upper bound on detection time. This max
|
||||||
|
// timeout is calculated using the formula:
|
||||||
|
//
|
||||||
|
// SuspicionMaxTimeout = SuspicionMaxTimeoutMult * SuspicionTimeout
|
||||||
|
//
|
||||||
|
// If everything is working properly, confirmations from other nodes will
|
||||||
|
// accelerate suspicion timers in a manner which will cause the timeout
|
||||||
|
// to reach the base SuspicionTimeout before that elapses, so this value
|
||||||
|
// will typically only come into play if a node is experiencing issues
|
||||||
|
// communicating with other nodes. It should be set to a something fairly
|
||||||
|
// large so that a node having problems will have a lot of chances to
|
||||||
|
// recover before falsely declaring other nodes as failed, but short
|
||||||
|
// enough for a legitimately isolated node to still make progress marking
|
||||||
|
// nodes failed in a reasonable amount of time.
|
||||||
|
SuspicionMaxTimeoutMult int
|
||||||
|
|
||||||
// PushPullInterval is the interval between complete state syncs.
|
// PushPullInterval is the interval between complete state syncs.
|
||||||
// Complete state syncs are done with a single node over TCP and are
|
// Complete state syncs are done with a single node over TCP and are
|
||||||
// quite expensive relative to standard gossiped messages. Setting this
|
// quite expensive relative to standard gossiped messages. Setting this
|
||||||
|
@ -91,6 +116,11 @@ type Config struct {
|
||||||
// indirect UDP pings.
|
// indirect UDP pings.
|
||||||
DisableTcpPings bool
|
DisableTcpPings bool
|
||||||
|
|
||||||
|
// AwarenessMaxMultiplier will increase the probe interval if the node
|
||||||
|
// becomes aware that it might be degraded and not meeting the soft real
|
||||||
|
// time requirements to reliably probe other nodes.
|
||||||
|
AwarenessMaxMultiplier int
|
||||||
|
|
||||||
// GossipInterval and GossipNodes are used to configure the gossip
|
// GossipInterval and GossipNodes are used to configure the gossip
|
||||||
// behavior of memberlist.
|
// behavior of memberlist.
|
||||||
//
|
//
|
||||||
|
@ -104,8 +134,12 @@ type Config struct {
|
||||||
// per GossipInterval. Increasing this number causes the gossip messages
|
// per GossipInterval. Increasing this number causes the gossip messages
|
||||||
// to propagate across the cluster more quickly at the expense of
|
// to propagate across the cluster more quickly at the expense of
|
||||||
// increased bandwidth.
|
// increased bandwidth.
|
||||||
GossipInterval time.Duration
|
//
|
||||||
GossipNodes int
|
// GossipToTheDeadTime is the interval after which a node has died that
|
||||||
|
// we will still try to gossip to it. This gives it a chance to refute.
|
||||||
|
GossipInterval time.Duration
|
||||||
|
GossipNodes int
|
||||||
|
GossipToTheDeadTime time.Duration
|
||||||
|
|
||||||
// EnableCompression is used to control message compression. This can
|
// EnableCompression is used to control message compression. This can
|
||||||
// be used to reduce bandwidth usage at the cost of slightly more CPU
|
// be used to reduce bandwidth usage at the cost of slightly more CPU
|
||||||
|
@ -157,6 +191,20 @@ type Config struct {
|
||||||
// behavior for using LogOutput. You cannot specify both LogOutput and Logger
|
// behavior for using LogOutput. You cannot specify both LogOutput and Logger
|
||||||
// at the same time.
|
// at the same time.
|
||||||
Logger *log.Logger
|
Logger *log.Logger
|
||||||
|
|
||||||
|
// Size of Memberlist's internal channel which handles UDP messages. The
|
||||||
|
// size of this determines the size of the queue which Memberlist will keep
|
||||||
|
// while UDP messages are handled.
|
||||||
|
HandoffQueueDepth int
|
||||||
|
|
||||||
|
// Maximum number of bytes that memberlist will put in a packet (this
|
||||||
|
// will be for UDP packets by default with a NetTransport). A safe value
|
||||||
|
// for this is typically 1400 bytes (which is the default). However,
|
||||||
|
// depending on your network's MTU (Maximum Transmission Unit) you may
|
||||||
|
// be able to increase this to get more content into each gossip packet.
|
||||||
|
// This is a legacy name for backward compatibility but should really be
|
||||||
|
// called PacketBufferSize now that we have generalized the transport.
|
||||||
|
UDPBufferSize int
|
||||||
}
|
}
|
||||||
|
|
||||||
// DefaultLANConfig returns a sane set of configurations for Memberlist.
|
// DefaultLANConfig returns a sane set of configurations for Memberlist.
|
||||||
|
@ -168,23 +216,26 @@ type Config struct {
|
||||||
func DefaultLANConfig() *Config {
|
func DefaultLANConfig() *Config {
|
||||||
hostname, _ := os.Hostname()
|
hostname, _ := os.Hostname()
|
||||||
return &Config{
|
return &Config{
|
||||||
Name: hostname,
|
Name: hostname,
|
||||||
BindAddr: "0.0.0.0",
|
BindAddr: "0.0.0.0",
|
||||||
BindPort: 7946,
|
BindPort: 7946,
|
||||||
AdvertiseAddr: "",
|
AdvertiseAddr: "",
|
||||||
AdvertisePort: 7946,
|
AdvertisePort: 7946,
|
||||||
ProtocolVersion: ProtocolVersion2Compatible,
|
ProtocolVersion: ProtocolVersion2Compatible,
|
||||||
TCPTimeout: 10 * time.Second, // Timeout after 10 seconds
|
TCPTimeout: 10 * time.Second, // Timeout after 10 seconds
|
||||||
IndirectChecks: 3, // Use 3 nodes for the indirect ping
|
IndirectChecks: 3, // Use 3 nodes for the indirect ping
|
||||||
RetransmitMult: 4, // Retransmit a message 4 * log(N+1) nodes
|
RetransmitMult: 4, // Retransmit a message 4 * log(N+1) nodes
|
||||||
SuspicionMult: 5, // Suspect a node for 5 * log(N+1) * Interval
|
SuspicionMult: 5, // Suspect a node for 5 * log(N+1) * Interval
|
||||||
PushPullInterval: 30 * time.Second, // Low frequency
|
SuspicionMaxTimeoutMult: 6, // For 10k nodes this will give a max timeout of 120 seconds
|
||||||
ProbeTimeout: 500 * time.Millisecond, // Reasonable RTT time for LAN
|
PushPullInterval: 30 * time.Second, // Low frequency
|
||||||
ProbeInterval: 1 * time.Second, // Failure check every second
|
ProbeTimeout: 500 * time.Millisecond, // Reasonable RTT time for LAN
|
||||||
DisableTcpPings: false, // TCP pings are safe, even with mixed versions
|
ProbeInterval: 1 * time.Second, // Failure check every second
|
||||||
|
DisableTcpPings: false, // TCP pings are safe, even with mixed versions
|
||||||
|
AwarenessMaxMultiplier: 8, // Probe interval backs off to 8 seconds
|
||||||
|
|
||||||
GossipNodes: 3, // Gossip to 3 nodes
|
GossipNodes: 3, // Gossip to 3 nodes
|
||||||
GossipInterval: 200 * time.Millisecond, // Gossip more rapidly
|
GossipInterval: 200 * time.Millisecond, // Gossip more rapidly
|
||||||
|
GossipToTheDeadTime: 30 * time.Second, // Same as push/pull
|
||||||
|
|
||||||
EnableCompression: true, // Enable compression by default
|
EnableCompression: true, // Enable compression by default
|
||||||
|
|
||||||
|
@ -192,6 +243,9 @@ func DefaultLANConfig() *Config {
|
||||||
Keyring: nil,
|
Keyring: nil,
|
||||||
|
|
||||||
DNSConfigPath: "/etc/resolv.conf",
|
DNSConfigPath: "/etc/resolv.conf",
|
||||||
|
|
||||||
|
HandoffQueueDepth: 1024,
|
||||||
|
UDPBufferSize: 1400,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -207,6 +261,7 @@ func DefaultWANConfig() *Config {
|
||||||
conf.ProbeInterval = 5 * time.Second
|
conf.ProbeInterval = 5 * time.Second
|
||||||
conf.GossipNodes = 4 // Gossip less frequently, but to an additional node
|
conf.GossipNodes = 4 // Gossip less frequently, but to an additional node
|
||||||
conf.GossipInterval = 500 * time.Millisecond
|
conf.GossipInterval = 500 * time.Millisecond
|
||||||
|
conf.GossipToTheDeadTime = 60 * time.Second
|
||||||
return conf
|
return conf
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -223,6 +278,7 @@ func DefaultLocalConfig() *Config {
|
||||||
conf.ProbeTimeout = 200 * time.Millisecond
|
conf.ProbeTimeout = 200 * time.Millisecond
|
||||||
conf.ProbeInterval = time.Second
|
conf.ProbeInterval = time.Second
|
||||||
conf.GossipInterval = 100 * time.Millisecond
|
conf.GossipInterval = 100 * time.Millisecond
|
||||||
|
conf.GossipToTheDeadTime = 15 * time.Second
|
||||||
return conf
|
return conf
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
2
vendor/github.com/hashicorp/memberlist/delegate.go
generated
vendored
2
vendor/github.com/hashicorp/memberlist/delegate.go
generated
vendored
|
@ -12,7 +12,7 @@ type Delegate interface {
|
||||||
// NotifyMsg is called when a user-data message is received.
|
// NotifyMsg is called when a user-data message is received.
|
||||||
// Care should be taken that this method does not block, since doing
|
// Care should be taken that this method does not block, since doing
|
||||||
// so would block the entire UDP packet receive loop. Additionally, the byte
|
// so would block the entire UDP packet receive loop. Additionally, the byte
|
||||||
// slice may be modified after the call returns, so it should be copied if needed.
|
// slice may be modified after the call returns, so it should be copied if needed
|
||||||
NotifyMsg([]byte)
|
NotifyMsg([]byte)
|
||||||
|
|
||||||
// GetBroadcasts is called when user data messages can be broadcast.
|
// GetBroadcasts is called when user data messages can be broadcast.
|
||||||
|
|
15
vendor/github.com/hashicorp/memberlist/keyring.go
generated
vendored
15
vendor/github.com/hashicorp/memberlist/keyring.go
generated
vendored
|
@ -58,6 +58,17 @@ func NewKeyring(keys [][]byte, primaryKey []byte) (*Keyring, error) {
|
||||||
return keyring, nil
|
return keyring, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ValidateKey will check to see if the key is valid and returns an error if not.
|
||||||
|
//
|
||||||
|
// key should be either 16, 24, or 32 bytes to select AES-128,
|
||||||
|
// AES-192, or AES-256.
|
||||||
|
func ValidateKey(key []byte) error {
|
||||||
|
if l := len(key); l != 16 && l != 24 && l != 32 {
|
||||||
|
return fmt.Errorf("key size must be 16, 24 or 32 bytes")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// AddKey will install a new key on the ring. Adding a key to the ring will make
|
// AddKey will install a new key on the ring. Adding a key to the ring will make
|
||||||
// it available for use in decryption. If the key already exists on the ring,
|
// it available for use in decryption. If the key already exists on the ring,
|
||||||
// this function will just return noop.
|
// this function will just return noop.
|
||||||
|
@ -65,8 +76,8 @@ func NewKeyring(keys [][]byte, primaryKey []byte) (*Keyring, error) {
|
||||||
// key should be either 16, 24, or 32 bytes to select AES-128,
|
// key should be either 16, 24, or 32 bytes to select AES-128,
|
||||||
// AES-192, or AES-256.
|
// AES-192, or AES-256.
|
||||||
func (k *Keyring) AddKey(key []byte) error {
|
func (k *Keyring) AddKey(key []byte) error {
|
||||||
if l := len(key); l != 16 && l != 24 && l != 32 {
|
if err := ValidateKey(key); err != nil {
|
||||||
return fmt.Errorf("key size must be 16, 24 or 32 bytes")
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// No-op if key is already installed
|
// No-op if key is already installed
|
||||||
|
|
227
vendor/github.com/hashicorp/memberlist/memberlist.go
generated
vendored
227
vendor/github.com/hashicorp/memberlist/memberlist.go
generated
vendored
|
@ -25,6 +25,7 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/hashicorp/go-multierror"
|
"github.com/hashicorp/go-multierror"
|
||||||
|
sockaddr "github.com/hashicorp/go-sockaddr"
|
||||||
"github.com/miekg/dns"
|
"github.com/miekg/dns"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -39,13 +40,14 @@ type Memberlist struct {
|
||||||
leave bool
|
leave bool
|
||||||
leaveBroadcast chan struct{}
|
leaveBroadcast chan struct{}
|
||||||
|
|
||||||
udpListener *net.UDPConn
|
transport Transport
|
||||||
tcpListener *net.TCPListener
|
handoff chan msgHandoff
|
||||||
handoff chan msgHandoff
|
|
||||||
|
|
||||||
nodeLock sync.RWMutex
|
nodeLock sync.RWMutex
|
||||||
nodes []*nodeState // Known nodes
|
nodes []*nodeState // Known nodes
|
||||||
nodeMap map[string]*nodeState // Maps Addr.String() -> NodeState
|
nodeMap map[string]*nodeState // Maps Addr.String() -> NodeState
|
||||||
|
nodeTimers map[string]*suspicion // Maps Addr.String() -> suspicion timer
|
||||||
|
awareness *awareness
|
||||||
|
|
||||||
tickerLock sync.Mutex
|
tickerLock sync.Mutex
|
||||||
tickers []*time.Ticker
|
tickers []*time.Ticker
|
||||||
|
@ -61,7 +63,7 @@ type Memberlist struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// newMemberlist creates the network listeners.
|
// newMemberlist creates the network listeners.
|
||||||
// Does not schedule execution of background maintenence.
|
// Does not schedule execution of background maintenance.
|
||||||
func newMemberlist(conf *Config) (*Memberlist, error) {
|
func newMemberlist(conf *Config) (*Memberlist, error) {
|
||||||
if conf.ProtocolVersion < ProtocolVersionMin {
|
if conf.ProtocolVersion < ProtocolVersionMin {
|
||||||
return nil, fmt.Errorf("Protocol version '%d' too low. Must be in range: [%d, %d]",
|
return nil, fmt.Errorf("Protocol version '%d' too low. Must be in range: [%d, %d]",
|
||||||
|
@ -88,25 +90,6 @@ func newMemberlist(conf *Config) (*Memberlist, error) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
tcpAddr := &net.TCPAddr{IP: net.ParseIP(conf.BindAddr), Port: conf.BindPort}
|
|
||||||
tcpLn, err := net.ListenTCP("tcp", tcpAddr)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("Failed to start TCP listener. Err: %s", err)
|
|
||||||
}
|
|
||||||
if conf.BindPort == 0 {
|
|
||||||
conf.BindPort = tcpLn.Addr().(*net.TCPAddr).Port
|
|
||||||
}
|
|
||||||
|
|
||||||
udpAddr := &net.UDPAddr{IP: net.ParseIP(conf.BindAddr), Port: conf.BindPort}
|
|
||||||
udpLn, err := net.ListenUDP("udp", udpAddr)
|
|
||||||
if err != nil {
|
|
||||||
tcpLn.Close()
|
|
||||||
return nil, fmt.Errorf("Failed to start UDP listener. Err: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set the UDP receive window size
|
|
||||||
setUDPRecvBuf(udpLn)
|
|
||||||
|
|
||||||
if conf.LogOutput != nil && conf.Logger != nil {
|
if conf.LogOutput != nil && conf.Logger != nil {
|
||||||
return nil, fmt.Errorf("Cannot specify both LogOutput and Logger. Please choose a single log configuration setting.")
|
return nil, fmt.Errorf("Cannot specify both LogOutput and Logger. Please choose a single log configuration setting.")
|
||||||
}
|
}
|
||||||
|
@ -121,14 +104,37 @@ func newMemberlist(conf *Config) (*Memberlist, error) {
|
||||||
logger = log.New(logDest, "", log.LstdFlags)
|
logger = log.New(logDest, "", log.LstdFlags)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Set up a network transport by default if a custom one wasn't given
|
||||||
|
// by the config.
|
||||||
|
transport := conf.Transport
|
||||||
|
if transport == nil {
|
||||||
|
nc := &NetTransportConfig{
|
||||||
|
BindAddrs: []string{conf.BindAddr},
|
||||||
|
BindPort: conf.BindPort,
|
||||||
|
Logger: logger,
|
||||||
|
}
|
||||||
|
nt, err := NewNetTransport(nc)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("Could not set up network transport: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if conf.BindPort == 0 {
|
||||||
|
port := nt.GetAutoBindPort()
|
||||||
|
conf.BindPort = port
|
||||||
|
logger.Printf("[DEBUG] Using dynamic bind port %d", port)
|
||||||
|
}
|
||||||
|
transport = nt
|
||||||
|
}
|
||||||
|
|
||||||
m := &Memberlist{
|
m := &Memberlist{
|
||||||
config: conf,
|
config: conf,
|
||||||
shutdownCh: make(chan struct{}),
|
shutdownCh: make(chan struct{}),
|
||||||
leaveBroadcast: make(chan struct{}, 1),
|
leaveBroadcast: make(chan struct{}, 1),
|
||||||
udpListener: udpLn,
|
transport: transport,
|
||||||
tcpListener: tcpLn,
|
handoff: make(chan msgHandoff, conf.HandoffQueueDepth),
|
||||||
handoff: make(chan msgHandoff, 1024),
|
|
||||||
nodeMap: make(map[string]*nodeState),
|
nodeMap: make(map[string]*nodeState),
|
||||||
|
nodeTimers: make(map[string]*suspicion),
|
||||||
|
awareness: newAwareness(conf.AwarenessMaxMultiplier),
|
||||||
ackHandlers: make(map[uint32]*ackHandler),
|
ackHandlers: make(map[uint32]*ackHandler),
|
||||||
broadcasts: &TransmitLimitedQueue{RetransmitMult: conf.RetransmitMult},
|
broadcasts: &TransmitLimitedQueue{RetransmitMult: conf.RetransmitMult},
|
||||||
logger: logger,
|
logger: logger,
|
||||||
|
@ -136,9 +142,9 @@ func newMemberlist(conf *Config) (*Memberlist, error) {
|
||||||
m.broadcasts.NumNodes = func() int {
|
m.broadcasts.NumNodes = func() int {
|
||||||
return m.estNumNodes()
|
return m.estNumNodes()
|
||||||
}
|
}
|
||||||
go m.tcpListen()
|
go m.streamListen()
|
||||||
go m.udpListen()
|
go m.packetListen()
|
||||||
go m.udpHandler()
|
go m.packetHandler()
|
||||||
return m, nil
|
return m, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -182,7 +188,8 @@ func (m *Memberlist) Join(existing []string) (int, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, addr := range addrs {
|
for _, addr := range addrs {
|
||||||
if err := m.pushPullNode(addr.ip, addr.port, true); err != nil {
|
hp := joinHostPort(addr.ip.String(), addr.port)
|
||||||
|
if err := m.pushPullNode(hp, true); err != nil {
|
||||||
err = fmt.Errorf("Failed to join %s: %v", addr.ip, err)
|
err = fmt.Errorf("Failed to join %s: %v", addr.ip, err)
|
||||||
errs = multierror.Append(errs, err)
|
errs = multierror.Append(errs, err)
|
||||||
m.logger.Printf("[DEBUG] memberlist: %v", err)
|
m.logger.Printf("[DEBUG] memberlist: %v", err)
|
||||||
|
@ -322,78 +329,30 @@ func (m *Memberlist) resolveAddr(hostStr string) ([]ipPort, error) {
|
||||||
// as if we received an alive notification our own network channel for
|
// as if we received an alive notification our own network channel for
|
||||||
// ourself.
|
// ourself.
|
||||||
func (m *Memberlist) setAlive() error {
|
func (m *Memberlist) setAlive() error {
|
||||||
var advertiseAddr []byte
|
// Get the final advertise address from the transport, which may need
|
||||||
var advertisePort int
|
// to see which address we bound to.
|
||||||
if m.config.AdvertiseAddr != "" {
|
addr, port, err := m.transport.FinalAdvertiseAddr(
|
||||||
// If AdvertiseAddr is not empty, then advertise
|
m.config.AdvertiseAddr, m.config.AdvertisePort)
|
||||||
// the given address and port.
|
if err != nil {
|
||||||
ip := net.ParseIP(m.config.AdvertiseAddr)
|
return fmt.Errorf("Failed to get final advertise address: %v", err)
|
||||||
if ip == nil {
|
|
||||||
return fmt.Errorf("Failed to parse advertise address!")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ensure IPv4 conversion if necessary
|
|
||||||
if ip4 := ip.To4(); ip4 != nil {
|
|
||||||
ip = ip4
|
|
||||||
}
|
|
||||||
|
|
||||||
advertiseAddr = ip
|
|
||||||
advertisePort = m.config.AdvertisePort
|
|
||||||
} else {
|
|
||||||
if m.config.BindAddr == "0.0.0.0" {
|
|
||||||
// Otherwise, if we're not bound to a specific IP,
|
|
||||||
//let's list the interfaces on this machine and use
|
|
||||||
// the first private IP we find.
|
|
||||||
addresses, err := net.InterfaceAddrs()
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("Failed to get interface addresses! Err: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Find private IPv4 address
|
|
||||||
for _, rawAddr := range addresses {
|
|
||||||
var ip net.IP
|
|
||||||
switch addr := rawAddr.(type) {
|
|
||||||
case *net.IPAddr:
|
|
||||||
ip = addr.IP
|
|
||||||
case *net.IPNet:
|
|
||||||
ip = addr.IP
|
|
||||||
default:
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if ip.To4() == nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if !IsPrivateIP(ip.String()) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
advertiseAddr = ip
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
// Failed to find private IP, error
|
|
||||||
if advertiseAddr == nil {
|
|
||||||
return fmt.Errorf("No private IP address found, and explicit IP not provided")
|
|
||||||
}
|
|
||||||
|
|
||||||
} else {
|
|
||||||
// Use the IP that we're bound to.
|
|
||||||
addr := m.tcpListener.Addr().(*net.TCPAddr)
|
|
||||||
advertiseAddr = addr.IP
|
|
||||||
}
|
|
||||||
|
|
||||||
// Use the port we are bound to.
|
|
||||||
advertisePort = m.tcpListener.Addr().(*net.TCPAddr).Port
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if this is a public address without encryption
|
// Check if this is a public address without encryption
|
||||||
addrStr := net.IP(advertiseAddr).String()
|
ipAddr, err := sockaddr.NewIPAddr(addr.String())
|
||||||
if !IsPrivateIP(addrStr) && !isLoopbackIP(addrStr) && !m.config.EncryptionEnabled() {
|
if err != nil {
|
||||||
|
return fmt.Errorf("Failed to parse interface addresses: %v", err)
|
||||||
|
}
|
||||||
|
ifAddrs := []sockaddr.IfAddr{
|
||||||
|
sockaddr.IfAddr{
|
||||||
|
SockAddr: ipAddr,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
_, publicIfs, err := sockaddr.IfByRFC("6890", ifAddrs)
|
||||||
|
if len(publicIfs) > 0 && !m.config.EncryptionEnabled() {
|
||||||
m.logger.Printf("[WARN] memberlist: Binding to public address without encryption!")
|
m.logger.Printf("[WARN] memberlist: Binding to public address without encryption!")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the node meta data
|
// Set any metadata from the delegate.
|
||||||
var meta []byte
|
var meta []byte
|
||||||
if m.config.Delegate != nil {
|
if m.config.Delegate != nil {
|
||||||
meta = m.config.Delegate.NodeMeta(MetaMaxSize)
|
meta = m.config.Delegate.NodeMeta(MetaMaxSize)
|
||||||
|
@ -405,8 +364,8 @@ func (m *Memberlist) setAlive() error {
|
||||||
a := alive{
|
a := alive{
|
||||||
Incarnation: m.nextIncarnation(),
|
Incarnation: m.nextIncarnation(),
|
||||||
Node: m.config.Name,
|
Node: m.config.Name,
|
||||||
Addr: advertiseAddr,
|
Addr: addr,
|
||||||
Port: uint16(advertisePort),
|
Port: uint16(port),
|
||||||
Meta: meta,
|
Meta: meta,
|
||||||
Vsn: []uint8{
|
Vsn: []uint8{
|
||||||
ProtocolVersionMin, ProtocolVersionMax, m.config.ProtocolVersion,
|
ProtocolVersionMin, ProtocolVersionMax, m.config.ProtocolVersion,
|
||||||
|
@ -415,7 +374,6 @@ func (m *Memberlist) setAlive() error {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
m.aliveNode(&a, nil, true)
|
m.aliveNode(&a, nil, true)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -478,13 +436,8 @@ func (m *Memberlist) UpdateNode(timeout time.Duration) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// SendTo is used to directly send a message to another node, without
|
// SendTo is deprecated in favor of SendBestEffort, which requires a node to
|
||||||
// the use of the gossip mechanism. This will encode the message as a
|
// target.
|
||||||
// user-data message, which a delegate will receive through NotifyMsg
|
|
||||||
// The actual data is transmitted over UDP, which means this is a
|
|
||||||
// best-effort transmission mechanism, and the maximum size of the
|
|
||||||
// message is the size of a single UDP datagram, after compression.
|
|
||||||
// This method is DEPRECATED in favor or SendToUDP
|
|
||||||
func (m *Memberlist) SendTo(to net.Addr, msg []byte) error {
|
func (m *Memberlist) SendTo(to net.Addr, msg []byte) error {
|
||||||
// Encode as a user message
|
// Encode as a user message
|
||||||
buf := make([]byte, 1, len(msg)+1)
|
buf := make([]byte, 1, len(msg)+1)
|
||||||
|
@ -492,36 +445,39 @@ func (m *Memberlist) SendTo(to net.Addr, msg []byte) error {
|
||||||
buf = append(buf, msg...)
|
buf = append(buf, msg...)
|
||||||
|
|
||||||
// Send the message
|
// Send the message
|
||||||
return m.rawSendMsgUDP(to, buf)
|
return m.rawSendMsgPacket(to.String(), nil, buf)
|
||||||
}
|
}
|
||||||
|
|
||||||
// SendToUDP is used to directly send a message to another node, without
|
// SendToUDP is deprecated in favor of SendBestEffort.
|
||||||
// the use of the gossip mechanism. This will encode the message as a
|
|
||||||
// user-data message, which a delegate will receive through NotifyMsg
|
|
||||||
// The actual data is transmitted over UDP, which means this is a
|
|
||||||
// best-effort transmission mechanism, and the maximum size of the
|
|
||||||
// message is the size of a single UDP datagram, after compression
|
|
||||||
func (m *Memberlist) SendToUDP(to *Node, msg []byte) error {
|
func (m *Memberlist) SendToUDP(to *Node, msg []byte) error {
|
||||||
|
return m.SendBestEffort(to, msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SendToTCP is deprecated in favor of SendReliable.
|
||||||
|
func (m *Memberlist) SendToTCP(to *Node, msg []byte) error {
|
||||||
|
return m.SendReliable(to, msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SendBestEffort uses the unreliable packet-oriented interface of the transport
|
||||||
|
// to target a user message at the given node (this does not use the gossip
|
||||||
|
// mechanism). The maximum size of the message depends on the configured
|
||||||
|
// UDPBufferSize for this memberlist instance.
|
||||||
|
func (m *Memberlist) SendBestEffort(to *Node, msg []byte) error {
|
||||||
// Encode as a user message
|
// Encode as a user message
|
||||||
buf := make([]byte, 1, len(msg)+1)
|
buf := make([]byte, 1, len(msg)+1)
|
||||||
buf[0] = byte(userMsg)
|
buf[0] = byte(userMsg)
|
||||||
buf = append(buf, msg...)
|
buf = append(buf, msg...)
|
||||||
|
|
||||||
// Send the message
|
// Send the message
|
||||||
destAddr := &net.UDPAddr{IP: to.Addr, Port: int(to.Port)}
|
return m.rawSendMsgPacket(to.Address(), to, buf)
|
||||||
return m.rawSendMsgUDP(destAddr, buf)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// SendToTCP is used to directly send a message to another node, without
|
// SendReliable uses the reliable stream-oriented interface of the transport to
|
||||||
// the use of the gossip mechanism. This will encode the message as a
|
// target a user message at the given node (this does not use the gossip
|
||||||
// user-data message, which a delegate will receive through NotifyMsg
|
// mechanism). Delivery is guaranteed if no error is returned, and there is no
|
||||||
// The actual data is transmitted over TCP, which means delivery
|
// limit on the size of the message.
|
||||||
// is guaranteed if no error is returned. There is no limit
|
func (m *Memberlist) SendReliable(to *Node, msg []byte) error {
|
||||||
// to the size of the message
|
return m.sendUserMsg(to.Address(), msg)
|
||||||
func (m *Memberlist) SendToTCP(to *Node, msg []byte) error {
|
|
||||||
// Send the message
|
|
||||||
destAddr := &net.TCPAddr{IP: to.Addr, Port: int(to.Port)}
|
|
||||||
return m.sendTCPUserMsg(destAddr, msg)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Members returns a list of all known live nodes. The node structures
|
// Members returns a list of all known live nodes. The node structures
|
||||||
|
@ -625,6 +581,13 @@ func (m *Memberlist) anyAlive() bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetHealthScore gives this instance's idea of how well it is meeting the soft
|
||||||
|
// real-time requirements of the protocol. Lower numbers are better, and zero
|
||||||
|
// means "totally healthy".
|
||||||
|
func (m *Memberlist) GetHealthScore() int {
|
||||||
|
return m.awareness.GetHealthScore()
|
||||||
|
}
|
||||||
|
|
||||||
// ProtocolVersion returns the protocol version currently in use by
|
// ProtocolVersion returns the protocol version currently in use by
|
||||||
// this memberlist.
|
// this memberlist.
|
||||||
func (m *Memberlist) ProtocolVersion() uint8 {
|
func (m *Memberlist) ProtocolVersion() uint8 {
|
||||||
|
@ -649,10 +612,14 @@ func (m *Memberlist) Shutdown() error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Shut down the transport first, which should block until it's
|
||||||
|
// completely torn down. If we kill the memberlist-side handlers
|
||||||
|
// those I/O handlers might get stuck.
|
||||||
|
m.transport.Shutdown()
|
||||||
|
|
||||||
|
// Now tear down everything else.
|
||||||
m.shutdown = true
|
m.shutdown = true
|
||||||
close(m.shutdownCh)
|
close(m.shutdownCh)
|
||||||
m.deschedule()
|
m.deschedule()
|
||||||
m.udpListener.Close()
|
|
||||||
m.tcpListener.Close()
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
121
vendor/github.com/hashicorp/memberlist/mock_transport.go
generated
vendored
Normal file
121
vendor/github.com/hashicorp/memberlist/mock_transport.go
generated
vendored
Normal file
|
@ -0,0 +1,121 @@
|
||||||
|
package memberlist
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
"strconv"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// MockNetwork is used as a factory that produces MockTransport instances which
|
||||||
|
// are uniquely addressed and wired up to talk to each other.
|
||||||
|
type MockNetwork struct {
|
||||||
|
transports map[string]*MockTransport
|
||||||
|
port int
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewTransport returns a new MockTransport with a unique address, wired up to
|
||||||
|
// talk to the other transports in the MockNetwork.
|
||||||
|
func (n *MockNetwork) NewTransport() *MockTransport {
|
||||||
|
n.port += 1
|
||||||
|
addr := fmt.Sprintf("127.0.0.1:%d", n.port)
|
||||||
|
transport := &MockTransport{
|
||||||
|
net: n,
|
||||||
|
addr: &MockAddress{addr},
|
||||||
|
packetCh: make(chan *Packet),
|
||||||
|
streamCh: make(chan net.Conn),
|
||||||
|
}
|
||||||
|
|
||||||
|
if n.transports == nil {
|
||||||
|
n.transports = make(map[string]*MockTransport)
|
||||||
|
}
|
||||||
|
n.transports[addr] = transport
|
||||||
|
return transport
|
||||||
|
}
|
||||||
|
|
||||||
|
// MockAddress is a wrapper which adds the net.Addr interface to our mock
|
||||||
|
// address scheme.
|
||||||
|
type MockAddress struct {
|
||||||
|
addr string
|
||||||
|
}
|
||||||
|
|
||||||
|
// See net.Addr.
|
||||||
|
func (a *MockAddress) Network() string {
|
||||||
|
return "mock"
|
||||||
|
}
|
||||||
|
|
||||||
|
// See net.Addr.
|
||||||
|
func (a *MockAddress) String() string {
|
||||||
|
return a.addr
|
||||||
|
}
|
||||||
|
|
||||||
|
// MockTransport directly plumbs messages to other transports its MockNetwork.
|
||||||
|
type MockTransport struct {
|
||||||
|
net *MockNetwork
|
||||||
|
addr *MockAddress
|
||||||
|
packetCh chan *Packet
|
||||||
|
streamCh chan net.Conn
|
||||||
|
}
|
||||||
|
|
||||||
|
// See Transport.
|
||||||
|
func (t *MockTransport) FinalAdvertiseAddr(string, int) (net.IP, int, error) {
|
||||||
|
host, portStr, err := net.SplitHostPort(t.addr.String())
|
||||||
|
if err != nil {
|
||||||
|
return nil, 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
ip := net.ParseIP(host)
|
||||||
|
if ip == nil {
|
||||||
|
return nil, 0, fmt.Errorf("Failed to parse IP %q", host)
|
||||||
|
}
|
||||||
|
|
||||||
|
port, err := strconv.ParseInt(portStr, 10, 16)
|
||||||
|
if err != nil {
|
||||||
|
return nil, 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return ip, int(port), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// See Transport.
|
||||||
|
func (t *MockTransport) WriteTo(b []byte, addr string) (time.Time, error) {
|
||||||
|
dest, ok := t.net.transports[addr]
|
||||||
|
if !ok {
|
||||||
|
return time.Time{}, fmt.Errorf("No route to %q", addr)
|
||||||
|
}
|
||||||
|
|
||||||
|
now := time.Now()
|
||||||
|
dest.packetCh <- &Packet{
|
||||||
|
Buf: b,
|
||||||
|
From: t.addr,
|
||||||
|
Timestamp: now,
|
||||||
|
}
|
||||||
|
return now, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// See Transport.
|
||||||
|
func (t *MockTransport) PacketCh() <-chan *Packet {
|
||||||
|
return t.packetCh
|
||||||
|
}
|
||||||
|
|
||||||
|
// See Transport.
|
||||||
|
func (t *MockTransport) DialTimeout(addr string, timeout time.Duration) (net.Conn, error) {
|
||||||
|
dest, ok := t.net.transports[addr]
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf("No route to %q", addr)
|
||||||
|
}
|
||||||
|
|
||||||
|
p1, p2 := net.Pipe()
|
||||||
|
dest.streamCh <- p1
|
||||||
|
return p2, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// See Transport.
|
||||||
|
func (t *MockTransport) StreamCh() <-chan net.Conn {
|
||||||
|
return t.streamCh
|
||||||
|
}
|
||||||
|
|
||||||
|
// See Transport.
|
||||||
|
func (t *MockTransport) Shutdown() error {
|
||||||
|
return nil
|
||||||
|
}
|
306
vendor/github.com/hashicorp/memberlist/net.go
generated
vendored
306
vendor/github.com/hashicorp/memberlist/net.go
generated
vendored
|
@ -5,6 +5,7 @@ import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"hash/crc32"
|
||||||
"io"
|
"io"
|
||||||
"net"
|
"net"
|
||||||
"time"
|
"time"
|
||||||
|
@ -24,9 +25,15 @@ const (
|
||||||
// A memberlist speaking version 2 of the protocol will attempt
|
// A memberlist speaking version 2 of the protocol will attempt
|
||||||
// to TCP ping another memberlist who understands version 3 or
|
// to TCP ping another memberlist who understands version 3 or
|
||||||
// greater.
|
// greater.
|
||||||
|
//
|
||||||
|
// Version 4 added support for nacks as part of indirect probes.
|
||||||
|
// A memberlist speaking version 2 of the protocol will expect
|
||||||
|
// nacks from another memberlist who understands version 4 or
|
||||||
|
// greater, and likewise nacks will be sent to memberlists who
|
||||||
|
// understand version 4 or greater.
|
||||||
ProtocolVersion2Compatible = 2
|
ProtocolVersion2Compatible = 2
|
||||||
|
|
||||||
ProtocolVersionMax = 3
|
ProtocolVersionMax = 5
|
||||||
)
|
)
|
||||||
|
|
||||||
// messageType is an integer ID of a type of message that can be received
|
// messageType is an integer ID of a type of message that can be received
|
||||||
|
@ -46,6 +53,8 @@ const (
|
||||||
userMsg // User mesg, not handled by us
|
userMsg // User mesg, not handled by us
|
||||||
compressMsg
|
compressMsg
|
||||||
encryptMsg
|
encryptMsg
|
||||||
|
nackRespMsg
|
||||||
|
hasCrcMsg
|
||||||
)
|
)
|
||||||
|
|
||||||
// compressionType is used to specify the compression algorithm
|
// compressionType is used to specify the compression algorithm
|
||||||
|
@ -59,9 +68,6 @@ const (
|
||||||
MetaMaxSize = 512 // Maximum size for node meta data
|
MetaMaxSize = 512 // Maximum size for node meta data
|
||||||
compoundHeaderOverhead = 2 // Assumed header overhead
|
compoundHeaderOverhead = 2 // Assumed header overhead
|
||||||
compoundOverhead = 2 // Assumed overhead per entry in compoundHeader
|
compoundOverhead = 2 // Assumed overhead per entry in compoundHeader
|
||||||
udpBufSize = 65536
|
|
||||||
udpRecvBuf = 2 * 1024 * 1024
|
|
||||||
udpSendBuf = 1400
|
|
||||||
userMsgOverhead = 1
|
userMsgOverhead = 1
|
||||||
blockingWarning = 10 * time.Millisecond // Warn if a UDP packet takes this long to process
|
blockingWarning = 10 * time.Millisecond // Warn if a UDP packet takes this long to process
|
||||||
maxPushStateBytes = 10 * 1024 * 1024
|
maxPushStateBytes = 10 * 1024 * 1024
|
||||||
|
@ -83,6 +89,7 @@ type indirectPingReq struct {
|
||||||
Target []byte
|
Target []byte
|
||||||
Port uint16
|
Port uint16
|
||||||
Node string
|
Node string
|
||||||
|
Nack bool // true if we'd like a nack back
|
||||||
}
|
}
|
||||||
|
|
||||||
// ack response is sent for a ping
|
// ack response is sent for a ping
|
||||||
|
@ -91,6 +98,13 @@ type ackResp struct {
|
||||||
Payload []byte
|
Payload []byte
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// nack response is sent for an indirect ping when the pinger doesn't hear from
|
||||||
|
// the ping-ee within the configured timeout. This lets the original node know
|
||||||
|
// that the indirect ping attempt happened but didn't succeed.
|
||||||
|
type nackResp struct {
|
||||||
|
SeqNo uint32
|
||||||
|
}
|
||||||
|
|
||||||
// suspect is broadcast when we suspect a node is dead
|
// suspect is broadcast when we suspect a node is dead
|
||||||
type suspect struct {
|
type suspect struct {
|
||||||
Incarnation uint32
|
Incarnation uint32
|
||||||
|
@ -121,7 +135,7 @@ type dead struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// pushPullHeader is used to inform the
|
// pushPullHeader is used to inform the
|
||||||
// otherside how many states we are transfering
|
// otherside how many states we are transferring
|
||||||
type pushPullHeader struct {
|
type pushPullHeader struct {
|
||||||
Nodes int
|
Nodes int
|
||||||
UserStateLen int // Encodes the byte lengh of user state
|
UserStateLen int // Encodes the byte lengh of user state
|
||||||
|
@ -134,7 +148,7 @@ type userMsgHeader struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// pushNodeState is used for pushPullReq when we are
|
// pushNodeState is used for pushPullReq when we are
|
||||||
// transfering out node states
|
// transferring out node states
|
||||||
type pushNodeState struct {
|
type pushNodeState struct {
|
||||||
Name string
|
Name string
|
||||||
Addr []byte
|
Addr []byte
|
||||||
|
@ -169,45 +183,33 @@ func (m *Memberlist) encryptionVersion() encryptionVersion {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// setUDPRecvBuf is used to resize the UDP receive window. The function
|
// streamListen is a long running goroutine that pulls incoming streams from the
|
||||||
// attempts to set the read buffer to `udpRecvBuf` but backs off until
|
// transport and hands them off for processing.
|
||||||
// the read buffer can be set.
|
func (m *Memberlist) streamListen() {
|
||||||
func setUDPRecvBuf(c *net.UDPConn) {
|
|
||||||
size := udpRecvBuf
|
|
||||||
for {
|
for {
|
||||||
if err := c.SetReadBuffer(size); err == nil {
|
select {
|
||||||
break
|
case conn := <-m.transport.StreamCh():
|
||||||
|
go m.handleConn(conn)
|
||||||
|
|
||||||
|
case <-m.shutdownCh:
|
||||||
|
return
|
||||||
}
|
}
|
||||||
size = size / 2
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// tcpListen listens for and handles incoming connections
|
// handleConn handles a single incoming stream connection from the transport.
|
||||||
func (m *Memberlist) tcpListen() {
|
func (m *Memberlist) handleConn(conn net.Conn) {
|
||||||
for {
|
m.logger.Printf("[DEBUG] memberlist: Stream connection %s", LogConn(conn))
|
||||||
conn, err := m.tcpListener.AcceptTCP()
|
|
||||||
if err != nil {
|
|
||||||
if m.shutdown {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
m.logger.Printf("[ERR] memberlist: Error accepting TCP connection: %s", err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
go m.handleConn(conn)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// handleConn handles a single incoming TCP connection
|
|
||||||
func (m *Memberlist) handleConn(conn *net.TCPConn) {
|
|
||||||
m.logger.Printf("[DEBUG] memberlist: TCP connection %s", LogConn(conn))
|
|
||||||
|
|
||||||
defer conn.Close()
|
defer conn.Close()
|
||||||
metrics.IncrCounter([]string{"memberlist", "tcp", "accept"}, 1)
|
metrics.IncrCounter([]string{"memberlist", "tcp", "accept"}, 1)
|
||||||
|
|
||||||
conn.SetDeadline(time.Now().Add(m.config.TCPTimeout))
|
conn.SetDeadline(time.Now().Add(m.config.TCPTimeout))
|
||||||
msgType, bufConn, dec, err := m.readTCP(conn)
|
msgType, bufConn, dec, err := m.readStream(conn)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
m.logger.Printf("[ERR] memberlist: failed to receive: %s %s", err, LogConn(conn))
|
if err != io.EOF {
|
||||||
|
m.logger.Printf("[ERR] memberlist: failed to receive: %s %s", err, LogConn(conn))
|
||||||
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -235,7 +237,7 @@ func (m *Memberlist) handleConn(conn *net.TCPConn) {
|
||||||
case pingMsg:
|
case pingMsg:
|
||||||
var p ping
|
var p ping
|
||||||
if err := dec.Decode(&p); err != nil {
|
if err := dec.Decode(&p); err != nil {
|
||||||
m.logger.Printf("[ERR] memberlist: Failed to decode TCP ping: %s %s", err, LogConn(conn))
|
m.logger.Printf("[ERR] memberlist: Failed to decode ping: %s %s", err, LogConn(conn))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -247,13 +249,13 @@ func (m *Memberlist) handleConn(conn *net.TCPConn) {
|
||||||
ack := ackResp{p.SeqNo, nil}
|
ack := ackResp{p.SeqNo, nil}
|
||||||
out, err := encode(ackRespMsg, &ack)
|
out, err := encode(ackRespMsg, &ack)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
m.logger.Printf("[ERR] memberlist: Failed to encode TCP ack: %s", err)
|
m.logger.Printf("[ERR] memberlist: Failed to encode ack: %s", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
err = m.rawSendMsgTCP(conn, out.Bytes())
|
err = m.rawSendMsgStream(conn, out.Bytes())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
m.logger.Printf("[ERR] memberlist: Failed to send TCP ack: %s %s", err, LogConn(conn))
|
m.logger.Printf("[ERR] memberlist: Failed to send ack: %s %s", err, LogConn(conn))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
|
@ -261,49 +263,17 @@ func (m *Memberlist) handleConn(conn *net.TCPConn) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// udpListen listens for and handles incoming UDP packets
|
// packetListen is a long running goroutine that pulls packets out of the
|
||||||
func (m *Memberlist) udpListen() {
|
// transport and hands them off for processing.
|
||||||
var n int
|
func (m *Memberlist) packetListen() {
|
||||||
var addr net.Addr
|
|
||||||
var err error
|
|
||||||
var lastPacket time.Time
|
|
||||||
for {
|
for {
|
||||||
// Do a check for potentially blocking operations
|
select {
|
||||||
if !lastPacket.IsZero() && time.Now().Sub(lastPacket) > blockingWarning {
|
case packet := <-m.transport.PacketCh():
|
||||||
diff := time.Now().Sub(lastPacket)
|
m.ingestPacket(packet.Buf, packet.From, packet.Timestamp)
|
||||||
m.logger.Printf(
|
|
||||||
"[DEBUG] memberlist: Potential blocking operation. Last command took %v",
|
case <-m.shutdownCh:
|
||||||
diff)
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create a new buffer
|
|
||||||
// TODO: Use Sync.Pool eventually
|
|
||||||
buf := make([]byte, udpBufSize)
|
|
||||||
|
|
||||||
// Read a packet
|
|
||||||
n, addr, err = m.udpListener.ReadFrom(buf)
|
|
||||||
if err != nil {
|
|
||||||
if m.shutdown {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
m.logger.Printf("[ERR] memberlist: Error reading UDP packet: %s", err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
// Capture the reception time of the packet as close to the
|
|
||||||
// system calls as possible.
|
|
||||||
lastPacket = time.Now()
|
|
||||||
|
|
||||||
// Check the length
|
|
||||||
if n < 1 {
|
|
||||||
m.logger.Printf("[ERR] memberlist: UDP packet too short (%d bytes) %s",
|
|
||||||
len(buf), LogAddress(addr))
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ingest this packet
|
|
||||||
metrics.IncrCounter([]string{"memberlist", "udp", "received"}, float32(n))
|
|
||||||
m.ingestPacket(buf[:n], addr, lastPacket)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -321,8 +291,18 @@ func (m *Memberlist) ingestPacket(buf []byte, from net.Addr, timestamp time.Time
|
||||||
buf = plain
|
buf = plain
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle the command
|
// See if there's a checksum included to verify the contents of the message
|
||||||
m.handleCommand(buf, from, timestamp)
|
if len(buf) >= 5 && messageType(buf[0]) == hasCrcMsg {
|
||||||
|
crc := crc32.ChecksumIEEE(buf[5:])
|
||||||
|
expected := binary.BigEndian.Uint32(buf[1:5])
|
||||||
|
if crc != expected {
|
||||||
|
m.logger.Printf("[WARN] memberlist: Got invalid checksum for UDP packet: %x, %x", crc, expected)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
m.handleCommand(buf[5:], from, timestamp)
|
||||||
|
} else {
|
||||||
|
m.handleCommand(buf, from, timestamp)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Memberlist) handleCommand(buf []byte, from net.Addr, timestamp time.Time) {
|
func (m *Memberlist) handleCommand(buf []byte, from net.Addr, timestamp time.Time) {
|
||||||
|
@ -343,6 +323,8 @@ func (m *Memberlist) handleCommand(buf []byte, from net.Addr, timestamp time.Tim
|
||||||
m.handleIndirectPing(buf, from)
|
m.handleIndirectPing(buf, from)
|
||||||
case ackRespMsg:
|
case ackRespMsg:
|
||||||
m.handleAck(buf, from, timestamp)
|
m.handleAck(buf, from, timestamp)
|
||||||
|
case nackRespMsg:
|
||||||
|
m.handleNack(buf, from)
|
||||||
|
|
||||||
case suspectMsg:
|
case suspectMsg:
|
||||||
fallthrough
|
fallthrough
|
||||||
|
@ -354,18 +336,18 @@ func (m *Memberlist) handleCommand(buf []byte, from net.Addr, timestamp time.Tim
|
||||||
select {
|
select {
|
||||||
case m.handoff <- msgHandoff{msgType, buf, from}:
|
case m.handoff <- msgHandoff{msgType, buf, from}:
|
||||||
default:
|
default:
|
||||||
m.logger.Printf("[WARN] memberlist: UDP handler queue full, dropping message (%d) %s", msgType, LogAddress(from))
|
m.logger.Printf("[WARN] memberlist: handler queue full, dropping message (%d) %s", msgType, LogAddress(from))
|
||||||
}
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
m.logger.Printf("[ERR] memberlist: UDP msg type (%d) not supported %s", msgType, LogAddress(from))
|
m.logger.Printf("[ERR] memberlist: msg type (%d) not supported %s", msgType, LogAddress(from))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// udpHandler processes messages received over UDP, but is decoupled
|
// packetHandler is a long running goroutine that processes messages received
|
||||||
// from the listener to avoid blocking the listener which may cause
|
// over the packet interface, but is decoupled from the listener to avoid
|
||||||
// ping/ack messages to be delayed.
|
// blocking the listener which may cause ping/ack messages to be delayed.
|
||||||
func (m *Memberlist) udpHandler() {
|
func (m *Memberlist) packetHandler() {
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case msg := <-m.handoff:
|
case msg := <-m.handoff:
|
||||||
|
@ -383,7 +365,7 @@ func (m *Memberlist) udpHandler() {
|
||||||
case userMsg:
|
case userMsg:
|
||||||
m.handleUser(buf, from)
|
m.handleUser(buf, from)
|
||||||
default:
|
default:
|
||||||
m.logger.Printf("[ERR] memberlist: UDP msg type (%d) not supported %s (handler)", msgType, LogAddress(from))
|
m.logger.Printf("[ERR] memberlist: Message type (%d) not supported %s (packet handler)", msgType, LogAddress(from))
|
||||||
}
|
}
|
||||||
|
|
||||||
case <-m.shutdownCh:
|
case <-m.shutdownCh:
|
||||||
|
@ -427,7 +409,7 @@ func (m *Memberlist) handlePing(buf []byte, from net.Addr) {
|
||||||
if m.config.Ping != nil {
|
if m.config.Ping != nil {
|
||||||
ack.Payload = m.config.Ping.AckPayload()
|
ack.Payload = m.config.Ping.AckPayload()
|
||||||
}
|
}
|
||||||
if err := m.encodeAndSendMsg(from, ackRespMsg, &ack); err != nil {
|
if err := m.encodeAndSendMsg(from.String(), ackRespMsg, &ack); err != nil {
|
||||||
m.logger.Printf("[ERR] memberlist: Failed to send ack: %s %s", err, LogAddress(from))
|
m.logger.Printf("[ERR] memberlist: Failed to send ack: %s %s", err, LogAddress(from))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -440,29 +422,49 @@ func (m *Memberlist) handleIndirectPing(buf []byte, from net.Addr) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// For proto versions < 2, there is no port provided. Mask old
|
// For proto versions < 2, there is no port provided. Mask old
|
||||||
// behavior by using the configured port
|
// behavior by using the configured port.
|
||||||
if m.ProtocolVersion() < 2 || ind.Port == 0 {
|
if m.ProtocolVersion() < 2 || ind.Port == 0 {
|
||||||
ind.Port = uint16(m.config.BindPort)
|
ind.Port = uint16(m.config.BindPort)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Send a ping to the correct host
|
// Send a ping to the correct host.
|
||||||
localSeqNo := m.nextSeqNo()
|
localSeqNo := m.nextSeqNo()
|
||||||
ping := ping{SeqNo: localSeqNo, Node: ind.Node}
|
ping := ping{SeqNo: localSeqNo, Node: ind.Node}
|
||||||
destAddr := &net.UDPAddr{IP: ind.Target, Port: int(ind.Port)}
|
|
||||||
|
|
||||||
// Setup a response handler to relay the ack
|
// Setup a response handler to relay the ack
|
||||||
|
cancelCh := make(chan struct{})
|
||||||
respHandler := func(payload []byte, timestamp time.Time) {
|
respHandler := func(payload []byte, timestamp time.Time) {
|
||||||
|
// Try to prevent the nack if we've caught it in time.
|
||||||
|
close(cancelCh)
|
||||||
|
|
||||||
|
// Forward the ack back to the requestor.
|
||||||
ack := ackResp{ind.SeqNo, nil}
|
ack := ackResp{ind.SeqNo, nil}
|
||||||
if err := m.encodeAndSendMsg(from, ackRespMsg, &ack); err != nil {
|
if err := m.encodeAndSendMsg(from.String(), ackRespMsg, &ack); err != nil {
|
||||||
m.logger.Printf("[ERR] memberlist: Failed to forward ack: %s %s", err, LogAddress(from))
|
m.logger.Printf("[ERR] memberlist: Failed to forward ack: %s %s", err, LogAddress(from))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
m.setAckHandler(localSeqNo, respHandler, m.config.ProbeTimeout)
|
m.setAckHandler(localSeqNo, respHandler, m.config.ProbeTimeout)
|
||||||
|
|
||||||
// Send the ping
|
// Send the ping.
|
||||||
if err := m.encodeAndSendMsg(destAddr, pingMsg, &ping); err != nil {
|
addr := joinHostPort(net.IP(ind.Target).String(), ind.Port)
|
||||||
|
if err := m.encodeAndSendMsg(addr, pingMsg, &ping); err != nil {
|
||||||
m.logger.Printf("[ERR] memberlist: Failed to send ping: %s %s", err, LogAddress(from))
|
m.logger.Printf("[ERR] memberlist: Failed to send ping: %s %s", err, LogAddress(from))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Setup a timer to fire off a nack if no ack is seen in time.
|
||||||
|
if ind.Nack {
|
||||||
|
go func() {
|
||||||
|
select {
|
||||||
|
case <-cancelCh:
|
||||||
|
return
|
||||||
|
case <-time.After(m.config.ProbeTimeout):
|
||||||
|
nack := nackResp{ind.SeqNo}
|
||||||
|
if err := m.encodeAndSendMsg(from.String(), nackRespMsg, &nack); err != nil {
|
||||||
|
m.logger.Printf("[ERR] memberlist: Failed to send nack: %s %s", err, LogAddress(from))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Memberlist) handleAck(buf []byte, from net.Addr, timestamp time.Time) {
|
func (m *Memberlist) handleAck(buf []byte, from net.Addr, timestamp time.Time) {
|
||||||
|
@ -474,6 +476,15 @@ func (m *Memberlist) handleAck(buf []byte, from net.Addr, timestamp time.Time) {
|
||||||
m.invokeAckHandler(ack, timestamp)
|
m.invokeAckHandler(ack, timestamp)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (m *Memberlist) handleNack(buf []byte, from net.Addr) {
|
||||||
|
var nack nackResp
|
||||||
|
if err := decode(buf, &nack); err != nil {
|
||||||
|
m.logger.Printf("[ERR] memberlist: Failed to decode nack response: %s %s", err, LogAddress(from))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
m.invokeNackHandler(nack)
|
||||||
|
}
|
||||||
|
|
||||||
func (m *Memberlist) handleSuspect(buf []byte, from net.Addr) {
|
func (m *Memberlist) handleSuspect(buf []byte, from net.Addr) {
|
||||||
var sus suspect
|
var sus suspect
|
||||||
if err := decode(buf, &sus); err != nil {
|
if err := decode(buf, &sus); err != nil {
|
||||||
|
@ -530,22 +541,22 @@ func (m *Memberlist) handleCompressed(buf []byte, from net.Addr, timestamp time.
|
||||||
}
|
}
|
||||||
|
|
||||||
// encodeAndSendMsg is used to combine the encoding and sending steps
|
// encodeAndSendMsg is used to combine the encoding and sending steps
|
||||||
func (m *Memberlist) encodeAndSendMsg(to net.Addr, msgType messageType, msg interface{}) error {
|
func (m *Memberlist) encodeAndSendMsg(addr string, msgType messageType, msg interface{}) error {
|
||||||
out, err := encode(msgType, msg)
|
out, err := encode(msgType, msg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := m.sendMsg(to, out.Bytes()); err != nil {
|
if err := m.sendMsg(addr, out.Bytes()); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// sendMsg is used to send a UDP message to another host. It will opportunistically
|
// sendMsg is used to send a message via packet to another host. It will
|
||||||
// create a compoundMsg and piggy back other broadcasts
|
// opportunistically create a compoundMsg and piggy back other broadcasts.
|
||||||
func (m *Memberlist) sendMsg(to net.Addr, msg []byte) error {
|
func (m *Memberlist) sendMsg(addr string, msg []byte) error {
|
||||||
// Check if we can piggy back any messages
|
// Check if we can piggy back any messages
|
||||||
bytesAvail := udpSendBuf - len(msg) - compoundHeaderOverhead
|
bytesAvail := m.config.UDPBufferSize - len(msg) - compoundHeaderOverhead
|
||||||
if m.config.EncryptionEnabled() {
|
if m.config.EncryptionEnabled() {
|
||||||
bytesAvail -= encryptOverhead(m.encryptionVersion())
|
bytesAvail -= encryptOverhead(m.encryptionVersion())
|
||||||
}
|
}
|
||||||
|
@ -553,7 +564,7 @@ func (m *Memberlist) sendMsg(to net.Addr, msg []byte) error {
|
||||||
|
|
||||||
// Fast path if nothing to piggypack
|
// Fast path if nothing to piggypack
|
||||||
if len(extra) == 0 {
|
if len(extra) == 0 {
|
||||||
return m.rawSendMsgUDP(to, msg)
|
return m.rawSendMsgPacket(addr, nil, msg)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Join all the messages
|
// Join all the messages
|
||||||
|
@ -565,11 +576,12 @@ func (m *Memberlist) sendMsg(to net.Addr, msg []byte) error {
|
||||||
compound := makeCompoundMessage(msgs)
|
compound := makeCompoundMessage(msgs)
|
||||||
|
|
||||||
// Send the message
|
// Send the message
|
||||||
return m.rawSendMsgUDP(to, compound.Bytes())
|
return m.rawSendMsgPacket(addr, nil, compound.Bytes())
|
||||||
}
|
}
|
||||||
|
|
||||||
// rawSendMsgUDP is used to send a UDP message to another host without modification
|
// rawSendMsgPacket is used to send message via packet to another host without
|
||||||
func (m *Memberlist) rawSendMsgUDP(to net.Addr, msg []byte) error {
|
// modification, other than compression or encryption if enabled.
|
||||||
|
func (m *Memberlist) rawSendMsgPacket(addr string, node *Node, msg []byte) error {
|
||||||
// Check if we have compression enabled
|
// Check if we have compression enabled
|
||||||
if m.config.EnableCompression {
|
if m.config.EnableCompression {
|
||||||
buf, err := compressPayload(msg)
|
buf, err := compressPayload(msg)
|
||||||
|
@ -583,6 +595,31 @@ func (m *Memberlist) rawSendMsgUDP(to net.Addr, msg []byte) error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Try to look up the destination node
|
||||||
|
if node == nil {
|
||||||
|
toAddr, _, err := net.SplitHostPort(addr)
|
||||||
|
if err != nil {
|
||||||
|
m.logger.Printf("[ERR] memberlist: Failed to parse address %q: %v", addr, err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
m.nodeLock.RLock()
|
||||||
|
nodeState, ok := m.nodeMap[toAddr]
|
||||||
|
m.nodeLock.RUnlock()
|
||||||
|
if ok {
|
||||||
|
node = &nodeState.Node
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add a CRC to the end of the payload if the recipient understands
|
||||||
|
// ProtocolVersion >= 5
|
||||||
|
if node != nil && node.PMax >= 5 {
|
||||||
|
crc := crc32.ChecksumIEEE(msg)
|
||||||
|
header := make([]byte, 5, 5+len(msg))
|
||||||
|
header[0] = byte(hasCrcMsg)
|
||||||
|
binary.BigEndian.PutUint32(header[1:], crc)
|
||||||
|
msg = append(header, msg...)
|
||||||
|
}
|
||||||
|
|
||||||
// Check if we have encryption enabled
|
// Check if we have encryption enabled
|
||||||
if m.config.EncryptionEnabled() {
|
if m.config.EncryptionEnabled() {
|
||||||
// Encrypt the payload
|
// Encrypt the payload
|
||||||
|
@ -597,12 +634,13 @@ func (m *Memberlist) rawSendMsgUDP(to net.Addr, msg []byte) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
metrics.IncrCounter([]string{"memberlist", "udp", "sent"}, float32(len(msg)))
|
metrics.IncrCounter([]string{"memberlist", "udp", "sent"}, float32(len(msg)))
|
||||||
_, err := m.udpListener.WriteTo(msg, to)
|
_, err := m.transport.WriteTo(msg, addr)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// rawSendMsgTCP is used to send a TCP message to another host without modification
|
// rawSendMsgStream is used to stream a message to another host without
|
||||||
func (m *Memberlist) rawSendMsgTCP(conn net.Conn, sendBuf []byte) error {
|
// modification, other than applying compression and encryption if enabled.
|
||||||
|
func (m *Memberlist) rawSendMsgStream(conn net.Conn, sendBuf []byte) error {
|
||||||
// Check if compresion is enabled
|
// Check if compresion is enabled
|
||||||
if m.config.EnableCompression {
|
if m.config.EnableCompression {
|
||||||
compBuf, err := compressPayload(sendBuf)
|
compBuf, err := compressPayload(sendBuf)
|
||||||
|
@ -635,43 +673,36 @@ func (m *Memberlist) rawSendMsgTCP(conn net.Conn, sendBuf []byte) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// sendTCPUserMsg is used to send a TCP userMsg to another host
|
// sendUserMsg is used to stream a user message to another host.
|
||||||
func (m *Memberlist) sendTCPUserMsg(to net.Addr, sendBuf []byte) error {
|
func (m *Memberlist) sendUserMsg(addr string, sendBuf []byte) error {
|
||||||
dialer := net.Dialer{Timeout: m.config.TCPTimeout}
|
conn, err := m.transport.DialTimeout(addr, m.config.TCPTimeout)
|
||||||
conn, err := dialer.Dial("tcp", to.String())
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
defer conn.Close()
|
defer conn.Close()
|
||||||
|
|
||||||
bufConn := bytes.NewBuffer(nil)
|
bufConn := bytes.NewBuffer(nil)
|
||||||
|
|
||||||
if err := bufConn.WriteByte(byte(userMsg)); err != nil {
|
if err := bufConn.WriteByte(byte(userMsg)); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Send our node state
|
|
||||||
header := userMsgHeader{UserMsgLen: len(sendBuf)}
|
header := userMsgHeader{UserMsgLen: len(sendBuf)}
|
||||||
hd := codec.MsgpackHandle{}
|
hd := codec.MsgpackHandle{}
|
||||||
enc := codec.NewEncoder(bufConn, &hd)
|
enc := codec.NewEncoder(bufConn, &hd)
|
||||||
|
|
||||||
if err := enc.Encode(&header); err != nil {
|
if err := enc.Encode(&header); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err := bufConn.Write(sendBuf); err != nil {
|
if _, err := bufConn.Write(sendBuf); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
return m.rawSendMsgStream(conn, bufConn.Bytes())
|
||||||
return m.rawSendMsgTCP(conn, bufConn.Bytes())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// sendAndReceiveState is used to initiate a push/pull over TCP with a remote node
|
// sendAndReceiveState is used to initiate a push/pull over a stream with a
|
||||||
func (m *Memberlist) sendAndReceiveState(addr []byte, port uint16, join bool) ([]pushNodeState, []byte, error) {
|
// remote host.
|
||||||
|
func (m *Memberlist) sendAndReceiveState(addr string, join bool) ([]pushNodeState, []byte, error) {
|
||||||
// Attempt to connect
|
// Attempt to connect
|
||||||
dialer := net.Dialer{Timeout: m.config.TCPTimeout}
|
conn, err := m.transport.DialTimeout(addr, m.config.TCPTimeout)
|
||||||
dest := net.TCPAddr{IP: addr, Port: int(port)}
|
|
||||||
conn, err := dialer.Dial("tcp", dest.String())
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
@ -685,7 +716,7 @@ func (m *Memberlist) sendAndReceiveState(addr []byte, port uint16, join bool) ([
|
||||||
}
|
}
|
||||||
|
|
||||||
conn.SetDeadline(time.Now().Add(m.config.TCPTimeout))
|
conn.SetDeadline(time.Now().Add(m.config.TCPTimeout))
|
||||||
msgType, bufConn, dec, err := m.readTCP(conn)
|
msgType, bufConn, dec, err := m.readStream(conn)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
@ -701,7 +732,7 @@ func (m *Memberlist) sendAndReceiveState(addr []byte, port uint16, join bool) ([
|
||||||
return remoteNodes, userState, err
|
return remoteNodes, userState, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// sendLocalState is invoked to send our local state over a tcp connection
|
// sendLocalState is invoked to send our local state over a stream connection.
|
||||||
func (m *Memberlist) sendLocalState(conn net.Conn, join bool) error {
|
func (m *Memberlist) sendLocalState(conn net.Conn, join bool) error {
|
||||||
// Setup a deadline
|
// Setup a deadline
|
||||||
conn.SetDeadline(time.Now().Add(m.config.TCPTimeout))
|
conn.SetDeadline(time.Now().Add(m.config.TCPTimeout))
|
||||||
|
@ -759,7 +790,7 @@ func (m *Memberlist) sendLocalState(conn net.Conn, join bool) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the send buffer
|
// Get the send buffer
|
||||||
return m.rawSendMsgTCP(conn, bufConn.Bytes())
|
return m.rawSendMsgStream(conn, bufConn.Bytes())
|
||||||
}
|
}
|
||||||
|
|
||||||
// encryptLocalState is used to help encrypt local state before sending
|
// encryptLocalState is used to help encrypt local state before sending
|
||||||
|
@ -817,9 +848,9 @@ func (m *Memberlist) decryptRemoteState(bufConn io.Reader) ([]byte, error) {
|
||||||
return decryptPayload(keys, cipherBytes, dataBytes)
|
return decryptPayload(keys, cipherBytes, dataBytes)
|
||||||
}
|
}
|
||||||
|
|
||||||
// readTCP is used to read the start of a TCP stream.
|
// readStream is used to read from a stream connection, decrypting and
|
||||||
// it decrypts and decompresses the stream if necessary
|
// decompressing the stream if necessary.
|
||||||
func (m *Memberlist) readTCP(conn net.Conn) (messageType, io.Reader, *codec.Decoder, error) {
|
func (m *Memberlist) readStream(conn net.Conn) (messageType, io.Reader, *codec.Decoder, error) {
|
||||||
// Created a buffered reader
|
// Created a buffered reader
|
||||||
var bufConn io.Reader = bufio.NewReader(conn)
|
var bufConn io.Reader = bufio.NewReader(conn)
|
||||||
|
|
||||||
|
@ -960,7 +991,7 @@ func (m *Memberlist) mergeRemoteState(join bool, remoteNodes []pushNodeState, us
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// readUserMsg is used to decode a userMsg from a TCP stream
|
// readUserMsg is used to decode a userMsg from a stream.
|
||||||
func (m *Memberlist) readUserMsg(bufConn io.Reader, dec *codec.Decoder) error {
|
func (m *Memberlist) readUserMsg(bufConn io.Reader, dec *codec.Decoder) error {
|
||||||
// Read the user message header
|
// Read the user message header
|
||||||
var header userMsgHeader
|
var header userMsgHeader
|
||||||
|
@ -991,13 +1022,12 @@ func (m *Memberlist) readUserMsg(bufConn io.Reader, dec *codec.Decoder) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// sendPingAndWaitForAck makes a TCP connection to the given address, sends
|
// sendPingAndWaitForAck makes a stream connection to the given address, sends
|
||||||
// a ping, and waits for an ack. All of this is done as a series of blocking
|
// a ping, and waits for an ack. All of this is done as a series of blocking
|
||||||
// operations, given the deadline. The bool return parameter is true if we
|
// operations, given the deadline. The bool return parameter is true if we
|
||||||
// we able to round trip a ping to the other node.
|
// we able to round trip a ping to the other node.
|
||||||
func (m *Memberlist) sendPingAndWaitForAck(destAddr net.Addr, ping ping, deadline time.Time) (bool, error) {
|
func (m *Memberlist) sendPingAndWaitForAck(addr string, ping ping, deadline time.Time) (bool, error) {
|
||||||
dialer := net.Dialer{Deadline: deadline}
|
conn, err := m.transport.DialTimeout(addr, m.config.TCPTimeout)
|
||||||
conn, err := dialer.Dial("tcp", destAddr.String())
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// If the node is actually dead we expect this to fail, so we
|
// If the node is actually dead we expect this to fail, so we
|
||||||
// shouldn't spam the logs with it. After this point, errors
|
// shouldn't spam the logs with it. After this point, errors
|
||||||
|
@ -1013,17 +1043,17 @@ func (m *Memberlist) sendPingAndWaitForAck(destAddr net.Addr, ping ping, deadlin
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = m.rawSendMsgTCP(conn, out.Bytes()); err != nil {
|
if err = m.rawSendMsgStream(conn, out.Bytes()); err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
|
|
||||||
msgType, _, dec, err := m.readTCP(conn)
|
msgType, _, dec, err := m.readStream(conn)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if msgType != ackRespMsg {
|
if msgType != ackRespMsg {
|
||||||
return false, fmt.Errorf("Unexpected msgType (%d) from TCP ping %s", msgType, LogConn(conn))
|
return false, fmt.Errorf("Unexpected msgType (%d) from ping %s", msgType, LogConn(conn))
|
||||||
}
|
}
|
||||||
|
|
||||||
var ack ackResp
|
var ack ackResp
|
||||||
|
@ -1032,7 +1062,7 @@ func (m *Memberlist) sendPingAndWaitForAck(destAddr net.Addr, ping ping, deadlin
|
||||||
}
|
}
|
||||||
|
|
||||||
if ack.SeqNo != ping.SeqNo {
|
if ack.SeqNo != ping.SeqNo {
|
||||||
return false, fmt.Errorf("Sequence number from ack (%d) doesn't match ping (%d) from TCP ping %s", ack.SeqNo, ping.SeqNo, LogConn(conn))
|
return false, fmt.Errorf("Sequence number from ack (%d) doesn't match ping (%d)", ack.SeqNo, ping.SeqNo, LogConn(conn))
|
||||||
}
|
}
|
||||||
|
|
||||||
return true, nil
|
return true, nil
|
||||||
|
|
289
vendor/github.com/hashicorp/memberlist/net_transport.go
generated
vendored
Normal file
289
vendor/github.com/hashicorp/memberlist/net_transport.go
generated
vendored
Normal file
|
@ -0,0 +1,289 @@
|
||||||
|
package memberlist
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"net"
|
||||||
|
"sync"
|
||||||
|
"sync/atomic"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/armon/go-metrics"
|
||||||
|
sockaddr "github.com/hashicorp/go-sockaddr"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// udpPacketBufSize is used to buffer incoming packets during read
|
||||||
|
// operations.
|
||||||
|
udpPacketBufSize = 65536
|
||||||
|
|
||||||
|
// udpRecvBufSize is a large buffer size that we attempt to set UDP
|
||||||
|
// sockets to in order to handle a large volume of messages.
|
||||||
|
udpRecvBufSize = 2 * 1024 * 1024
|
||||||
|
)
|
||||||
|
|
||||||
|
// NetTransportConfig is used to configure a net transport.
|
||||||
|
type NetTransportConfig struct {
|
||||||
|
// BindAddrs is a list of addresses to bind to for both TCP and UDP
|
||||||
|
// communications.
|
||||||
|
BindAddrs []string
|
||||||
|
|
||||||
|
// BindPort is the port to listen on, for each address above.
|
||||||
|
BindPort int
|
||||||
|
|
||||||
|
// Logger is a logger for operator messages.
|
||||||
|
Logger *log.Logger
|
||||||
|
}
|
||||||
|
|
||||||
|
// NetTransport is a Transport implementation that uses connectionless UDP for
|
||||||
|
// packet operations, and ad-hoc TCP connections for stream operations.
|
||||||
|
type NetTransport struct {
|
||||||
|
config *NetTransportConfig
|
||||||
|
packetCh chan *Packet
|
||||||
|
streamCh chan net.Conn
|
||||||
|
logger *log.Logger
|
||||||
|
wg sync.WaitGroup
|
||||||
|
tcpListeners []*net.TCPListener
|
||||||
|
udpListeners []*net.UDPConn
|
||||||
|
shutdown int32
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewNetTransport returns a net transport with the given configuration. On
|
||||||
|
// success all the network listeners will be created and listening.
|
||||||
|
func NewNetTransport(config *NetTransportConfig) (*NetTransport, error) {
|
||||||
|
// If we reject the empty list outright we can assume that there's at
|
||||||
|
// least one listener of each type later during operation.
|
||||||
|
if len(config.BindAddrs) == 0 {
|
||||||
|
return nil, fmt.Errorf("At least one bind address is required")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build out the new transport.
|
||||||
|
var ok bool
|
||||||
|
t := NetTransport{
|
||||||
|
config: config,
|
||||||
|
packetCh: make(chan *Packet),
|
||||||
|
streamCh: make(chan net.Conn),
|
||||||
|
logger: config.Logger,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clean up listeners if there's an error.
|
||||||
|
defer func() {
|
||||||
|
if !ok {
|
||||||
|
t.Shutdown()
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
// Build all the TCP and UDP listeners.
|
||||||
|
port := config.BindPort
|
||||||
|
for _, addr := range config.BindAddrs {
|
||||||
|
ip := net.ParseIP(addr)
|
||||||
|
|
||||||
|
tcpAddr := &net.TCPAddr{IP: ip, Port: port}
|
||||||
|
tcpLn, err := net.ListenTCP("tcp", tcpAddr)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("Failed to start TCP listener on %q port %d: %v", addr, port, err)
|
||||||
|
}
|
||||||
|
t.tcpListeners = append(t.tcpListeners, tcpLn)
|
||||||
|
|
||||||
|
// If the config port given was zero, use the first TCP listener
|
||||||
|
// to pick an available port and then apply that to everything
|
||||||
|
// else.
|
||||||
|
if port == 0 {
|
||||||
|
port = tcpLn.Addr().(*net.TCPAddr).Port
|
||||||
|
}
|
||||||
|
|
||||||
|
udpAddr := &net.UDPAddr{IP: ip, Port: port}
|
||||||
|
udpLn, err := net.ListenUDP("udp", udpAddr)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("Failed to start UDP listener on %q port %d: %v", addr, port, err)
|
||||||
|
}
|
||||||
|
if err := setUDPRecvBuf(udpLn); err != nil {
|
||||||
|
return nil, fmt.Errorf("Failed to resize UDP buffer: %v", err)
|
||||||
|
}
|
||||||
|
t.udpListeners = append(t.udpListeners, udpLn)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fire them up now that we've been able to create them all.
|
||||||
|
for i := 0; i < len(config.BindAddrs); i++ {
|
||||||
|
t.wg.Add(2)
|
||||||
|
go t.tcpListen(t.tcpListeners[i])
|
||||||
|
go t.udpListen(t.udpListeners[i])
|
||||||
|
}
|
||||||
|
|
||||||
|
ok = true
|
||||||
|
return &t, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetAutoBindPort returns the bind port that was automatically given by the
|
||||||
|
// kernel, if a bind port of 0 was given.
|
||||||
|
func (t *NetTransport) GetAutoBindPort() int {
|
||||||
|
// We made sure there's at least one TCP listener, and that one's
|
||||||
|
// port was applied to all the others for the dynamic bind case.
|
||||||
|
return t.tcpListeners[0].Addr().(*net.TCPAddr).Port
|
||||||
|
}
|
||||||
|
|
||||||
|
// See Transport.
|
||||||
|
func (t *NetTransport) FinalAdvertiseAddr(ip string, port int) (net.IP, int, error) {
|
||||||
|
var advertiseAddr net.IP
|
||||||
|
var advertisePort int
|
||||||
|
if ip != "" {
|
||||||
|
// If they've supplied an address, use that.
|
||||||
|
advertiseAddr = net.ParseIP(ip)
|
||||||
|
if advertiseAddr == nil {
|
||||||
|
return nil, 0, fmt.Errorf("Failed to parse advertise address %q", ip)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure IPv4 conversion if necessary.
|
||||||
|
if ip4 := advertiseAddr.To4(); ip4 != nil {
|
||||||
|
advertiseAddr = ip4
|
||||||
|
}
|
||||||
|
advertisePort = port
|
||||||
|
} else {
|
||||||
|
if t.config.BindAddrs[0] == "0.0.0.0" {
|
||||||
|
// Otherwise, if we're not bound to a specific IP, let's
|
||||||
|
// use a suitable private IP address.
|
||||||
|
var err error
|
||||||
|
ip, err = sockaddr.GetPrivateIP()
|
||||||
|
if err != nil {
|
||||||
|
return nil, 0, fmt.Errorf("Failed to get interface addresses: %v", err)
|
||||||
|
}
|
||||||
|
if ip == "" {
|
||||||
|
return nil, 0, fmt.Errorf("No private IP address found, and explicit IP not provided")
|
||||||
|
}
|
||||||
|
|
||||||
|
advertiseAddr = net.ParseIP(ip)
|
||||||
|
if advertiseAddr == nil {
|
||||||
|
return nil, 0, fmt.Errorf("Failed to parse advertise address: %q", ip)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Use the IP that we're bound to, based on the first
|
||||||
|
// TCP listener, which we already ensure is there.
|
||||||
|
advertiseAddr = t.tcpListeners[0].Addr().(*net.TCPAddr).IP
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use the port we are bound to.
|
||||||
|
advertisePort = t.GetAutoBindPort()
|
||||||
|
}
|
||||||
|
|
||||||
|
return advertiseAddr, advertisePort, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// See Transport.
|
||||||
|
func (t *NetTransport) WriteTo(b []byte, addr string) (time.Time, error) {
|
||||||
|
udpAddr, err := net.ResolveUDPAddr("udp", addr)
|
||||||
|
if err != nil {
|
||||||
|
return time.Time{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// We made sure there's at least one UDP listener, so just use the
|
||||||
|
// packet sending interface on the first one. Take the time after the
|
||||||
|
// write call comes back, which will underestimate the time a little,
|
||||||
|
// but help account for any delays before the write occurs.
|
||||||
|
_, err = t.udpListeners[0].WriteTo(b, udpAddr)
|
||||||
|
return time.Now(), err
|
||||||
|
}
|
||||||
|
|
||||||
|
// See Transport.
|
||||||
|
func (t *NetTransport) PacketCh() <-chan *Packet {
|
||||||
|
return t.packetCh
|
||||||
|
}
|
||||||
|
|
||||||
|
// See Transport.
|
||||||
|
func (t *NetTransport) DialTimeout(addr string, timeout time.Duration) (net.Conn, error) {
|
||||||
|
dialer := net.Dialer{Timeout: timeout}
|
||||||
|
return dialer.Dial("tcp", addr)
|
||||||
|
}
|
||||||
|
|
||||||
|
// See Transport.
|
||||||
|
func (t *NetTransport) StreamCh() <-chan net.Conn {
|
||||||
|
return t.streamCh
|
||||||
|
}
|
||||||
|
|
||||||
|
// See Transport.
|
||||||
|
func (t *NetTransport) Shutdown() error {
|
||||||
|
// This will avoid log spam about errors when we shut down.
|
||||||
|
atomic.StoreInt32(&t.shutdown, 1)
|
||||||
|
|
||||||
|
// Rip through all the connections and shut them down.
|
||||||
|
for _, conn := range t.tcpListeners {
|
||||||
|
conn.Close()
|
||||||
|
}
|
||||||
|
for _, conn := range t.udpListeners {
|
||||||
|
conn.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Block until all the listener threads have died.
|
||||||
|
t.wg.Wait()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// tcpListen is a long running goroutine that accepts incoming TCP connections
|
||||||
|
// and hands them off to the stream channel.
|
||||||
|
func (t *NetTransport) tcpListen(tcpLn *net.TCPListener) {
|
||||||
|
defer t.wg.Done()
|
||||||
|
for {
|
||||||
|
conn, err := tcpLn.AcceptTCP()
|
||||||
|
if err != nil {
|
||||||
|
if s := atomic.LoadInt32(&t.shutdown); s == 1 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
t.logger.Printf("[ERR] memberlist: Error accepting TCP connection: %v", err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
t.streamCh <- conn
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// udpListen is a long running goroutine that accepts incoming UDP packets and
|
||||||
|
// hands them off to the packet channel.
|
||||||
|
func (t *NetTransport) udpListen(udpLn *net.UDPConn) {
|
||||||
|
defer t.wg.Done()
|
||||||
|
for {
|
||||||
|
// Do a blocking read into a fresh buffer. Grab a time stamp as
|
||||||
|
// close as possible to the I/O.
|
||||||
|
buf := make([]byte, udpPacketBufSize)
|
||||||
|
n, addr, err := udpLn.ReadFrom(buf)
|
||||||
|
ts := time.Now()
|
||||||
|
if err != nil {
|
||||||
|
if s := atomic.LoadInt32(&t.shutdown); s == 1 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
t.logger.Printf("[ERR] memberlist: Error reading UDP packet: %v", err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check the length - it needs to have at least one byte to be a
|
||||||
|
// proper message.
|
||||||
|
if n < 1 {
|
||||||
|
t.logger.Printf("[ERR] memberlist: UDP packet too short (%d bytes) %s",
|
||||||
|
len(buf), LogAddress(addr))
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ingest the packet.
|
||||||
|
metrics.IncrCounter([]string{"memberlist", "udp", "received"}, float32(n))
|
||||||
|
t.packetCh <- &Packet{
|
||||||
|
Buf: buf[:n],
|
||||||
|
From: addr,
|
||||||
|
Timestamp: ts,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// setUDPRecvBuf is used to resize the UDP receive window. The function
|
||||||
|
// attempts to set the read buffer to `udpRecvBuf` but backs off until
|
||||||
|
// the read buffer can be set.
|
||||||
|
func setUDPRecvBuf(c *net.UDPConn) error {
|
||||||
|
size := udpRecvBufSize
|
||||||
|
var err error
|
||||||
|
for size > 0 {
|
||||||
|
if err = c.SetReadBuffer(size); err == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
size = size / 2
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
392
vendor/github.com/hashicorp/memberlist/state.go
generated
vendored
392
vendor/github.com/hashicorp/memberlist/state.go
generated
vendored
|
@ -34,6 +34,12 @@ type Node struct {
|
||||||
DCur uint8 // Current version delegate is speaking
|
DCur uint8 // Current version delegate is speaking
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Address returns the host:port form of a node's address, suitable for use
|
||||||
|
// with a transport.
|
||||||
|
func (n *Node) Address() string {
|
||||||
|
return joinHostPort(n.Addr.String(), n.Port)
|
||||||
|
}
|
||||||
|
|
||||||
// NodeState is used to manage our state view of another node
|
// NodeState is used to manage our state view of another node
|
||||||
type nodeState struct {
|
type nodeState struct {
|
||||||
Node
|
Node
|
||||||
|
@ -42,10 +48,17 @@ type nodeState struct {
|
||||||
StateChange time.Time // Time last state change happened
|
StateChange time.Time // Time last state change happened
|
||||||
}
|
}
|
||||||
|
|
||||||
// ackHandler is used to register handlers for incoming acks
|
// Address returns the host:port form of a node's address, suitable for use
|
||||||
|
// with a transport.
|
||||||
|
func (n *nodeState) Address() string {
|
||||||
|
return n.Node.Address()
|
||||||
|
}
|
||||||
|
|
||||||
|
// ackHandler is used to register handlers for incoming acks and nacks.
|
||||||
type ackHandler struct {
|
type ackHandler struct {
|
||||||
handler func([]byte, time.Time)
|
ackFn func([]byte, time.Time)
|
||||||
timer *time.Timer
|
nackFn func()
|
||||||
|
timer *time.Timer
|
||||||
}
|
}
|
||||||
|
|
||||||
// NoPingResponseError is used to indicate a 'ping' packet was
|
// NoPingResponseError is used to indicate a 'ping' packet was
|
||||||
|
@ -148,7 +161,7 @@ func (m *Memberlist) pushPullTrigger(stop <-chan struct{}) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Deschedule is used to stop the background maintenence. This is safe
|
// Deschedule is used to stop the background maintenance. This is safe
|
||||||
// to call multiple times.
|
// to call multiple times.
|
||||||
func (m *Memberlist) deschedule() {
|
func (m *Memberlist) deschedule() {
|
||||||
m.tickerLock.Lock()
|
m.tickerLock.Lock()
|
||||||
|
@ -219,17 +232,51 @@ START:
|
||||||
func (m *Memberlist) probeNode(node *nodeState) {
|
func (m *Memberlist) probeNode(node *nodeState) {
|
||||||
defer metrics.MeasureSince([]string{"memberlist", "probeNode"}, time.Now())
|
defer metrics.MeasureSince([]string{"memberlist", "probeNode"}, time.Now())
|
||||||
|
|
||||||
|
// We use our health awareness to scale the overall probe interval, so we
|
||||||
|
// slow down if we detect problems. The ticker that calls us can handle
|
||||||
|
// us running over the base interval, and will skip missed ticks.
|
||||||
|
probeInterval := m.awareness.ScaleTimeout(m.config.ProbeInterval)
|
||||||
|
if probeInterval > m.config.ProbeInterval {
|
||||||
|
metrics.IncrCounter([]string{"memberlist", "degraded", "probe"}, 1)
|
||||||
|
}
|
||||||
|
|
||||||
// Prepare a ping message and setup an ack handler.
|
// Prepare a ping message and setup an ack handler.
|
||||||
ping := ping{SeqNo: m.nextSeqNo(), Node: node.Name}
|
ping := ping{SeqNo: m.nextSeqNo(), Node: node.Name}
|
||||||
ackCh := make(chan ackMessage, m.config.IndirectChecks+1)
|
ackCh := make(chan ackMessage, m.config.IndirectChecks+1)
|
||||||
m.setAckChannel(ping.SeqNo, ackCh, m.config.ProbeInterval)
|
nackCh := make(chan struct{}, m.config.IndirectChecks+1)
|
||||||
|
m.setProbeChannels(ping.SeqNo, ackCh, nackCh, probeInterval)
|
||||||
|
|
||||||
// Send a ping to the node.
|
// Send a ping to the node. If this node looks like it's suspect or dead,
|
||||||
deadline := time.Now().Add(m.config.ProbeInterval)
|
// also tack on a suspect message so that it has a chance to refute as
|
||||||
destAddr := &net.UDPAddr{IP: node.Addr, Port: int(node.Port)}
|
// soon as possible.
|
||||||
if err := m.encodeAndSendMsg(destAddr, pingMsg, &ping); err != nil {
|
deadline := time.Now().Add(probeInterval)
|
||||||
m.logger.Printf("[ERR] memberlist: Failed to send ping: %s", err)
|
addr := node.Address()
|
||||||
return
|
if node.State == stateAlive {
|
||||||
|
if err := m.encodeAndSendMsg(addr, pingMsg, &ping); err != nil {
|
||||||
|
m.logger.Printf("[ERR] memberlist: Failed to send ping: %s", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
var msgs [][]byte
|
||||||
|
if buf, err := encode(pingMsg, &ping); err != nil {
|
||||||
|
m.logger.Printf("[ERR] memberlist: Failed to encode ping message: %s", err)
|
||||||
|
return
|
||||||
|
} else {
|
||||||
|
msgs = append(msgs, buf.Bytes())
|
||||||
|
}
|
||||||
|
s := suspect{Incarnation: node.Incarnation, Node: node.Name, From: m.config.Name}
|
||||||
|
if buf, err := encode(suspectMsg, &s); err != nil {
|
||||||
|
m.logger.Printf("[ERR] memberlist: Failed to encode suspect message: %s", err)
|
||||||
|
return
|
||||||
|
} else {
|
||||||
|
msgs = append(msgs, buf.Bytes())
|
||||||
|
}
|
||||||
|
|
||||||
|
compound := makeCompoundMessage(msgs)
|
||||||
|
if err := m.rawSendMsgPacket(addr, &node.Node, compound.Bytes()); err != nil {
|
||||||
|
m.logger.Printf("[ERR] memberlist: Failed to send compound ping and suspect message to %s: %s", addr, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Mark the sent time here, which should be after any pre-processing and
|
// Mark the sent time here, which should be after any pre-processing and
|
||||||
|
@ -237,6 +284,16 @@ func (m *Memberlist) probeNode(node *nodeState) {
|
||||||
// but it's the best we can do.
|
// but it's the best we can do.
|
||||||
sent := time.Now()
|
sent := time.Now()
|
||||||
|
|
||||||
|
// Arrange for our self-awareness to get updated. At this point we've
|
||||||
|
// sent the ping, so any return statement means the probe succeeded
|
||||||
|
// which will improve our health until we get to the failure scenarios
|
||||||
|
// at the end of this function, which will alter this delta variable
|
||||||
|
// accordingly.
|
||||||
|
awarenessDelta := -1
|
||||||
|
defer func() {
|
||||||
|
m.awareness.ApplyDelta(awarenessDelta)
|
||||||
|
}()
|
||||||
|
|
||||||
// Wait for response or round-trip-time.
|
// Wait for response or round-trip-time.
|
||||||
select {
|
select {
|
||||||
case v := <-ackCh:
|
case v := <-ackCh:
|
||||||
|
@ -254,20 +311,35 @@ func (m *Memberlist) probeNode(node *nodeState) {
|
||||||
ackCh <- v
|
ackCh <- v
|
||||||
}
|
}
|
||||||
case <-time.After(m.config.ProbeTimeout):
|
case <-time.After(m.config.ProbeTimeout):
|
||||||
m.logger.Printf("[DEBUG] memberlist: Failed UDP ping: %v (timeout reached)", node.Name)
|
// Note that we don't scale this timeout based on awareness and
|
||||||
|
// the health score. That's because we don't really expect waiting
|
||||||
|
// longer to help get UDP through. Since health does extend the
|
||||||
|
// probe interval it will give the TCP fallback more time, which
|
||||||
|
// is more active in dealing with lost packets, and it gives more
|
||||||
|
// time to wait for indirect acks/nacks.
|
||||||
|
m.logger.Printf("[DEBUG] memberlist: Failed ping: %v (timeout reached)", node.Name)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get some random live nodes.
|
// Get some random live nodes.
|
||||||
m.nodeLock.RLock()
|
m.nodeLock.RLock()
|
||||||
excludes := []string{m.config.Name, node.Name}
|
kNodes := kRandomNodes(m.config.IndirectChecks, m.nodes, func(n *nodeState) bool {
|
||||||
kNodes := kRandomNodes(m.config.IndirectChecks, excludes, m.nodes)
|
return n.Name == m.config.Name ||
|
||||||
|
n.Name == node.Name ||
|
||||||
|
n.State != stateAlive
|
||||||
|
})
|
||||||
m.nodeLock.RUnlock()
|
m.nodeLock.RUnlock()
|
||||||
|
|
||||||
// Attempt an indirect ping.
|
// Attempt an indirect ping.
|
||||||
|
expectedNacks := 0
|
||||||
ind := indirectPingReq{SeqNo: ping.SeqNo, Target: node.Addr, Port: node.Port, Node: node.Name}
|
ind := indirectPingReq{SeqNo: ping.SeqNo, Target: node.Addr, Port: node.Port, Node: node.Name}
|
||||||
for _, peer := range kNodes {
|
for _, peer := range kNodes {
|
||||||
destAddr := &net.UDPAddr{IP: peer.Addr, Port: int(peer.Port)}
|
// We only expect nack to be sent from peers who understand
|
||||||
if err := m.encodeAndSendMsg(destAddr, indirectPingMsg, &ind); err != nil {
|
// version 4 of the protocol.
|
||||||
|
if ind.Nack = peer.PMax >= 4; ind.Nack {
|
||||||
|
expectedNacks++
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := m.encodeAndSendMsg(peer.Address(), indirectPingMsg, &ind); err != nil {
|
||||||
m.logger.Printf("[ERR] memberlist: Failed to send indirect ping: %s", err)
|
m.logger.Printf("[ERR] memberlist: Failed to send indirect ping: %s", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -284,12 +356,11 @@ func (m *Memberlist) probeNode(node *nodeState) {
|
||||||
// config option to turn this off if desired.
|
// config option to turn this off if desired.
|
||||||
fallbackCh := make(chan bool, 1)
|
fallbackCh := make(chan bool, 1)
|
||||||
if (!m.config.DisableTcpPings) && (node.PMax >= 3) {
|
if (!m.config.DisableTcpPings) && (node.PMax >= 3) {
|
||||||
destAddr := &net.TCPAddr{IP: node.Addr, Port: int(node.Port)}
|
|
||||||
go func() {
|
go func() {
|
||||||
defer close(fallbackCh)
|
defer close(fallbackCh)
|
||||||
didContact, err := m.sendPingAndWaitForAck(destAddr, ping, deadline)
|
didContact, err := m.sendPingAndWaitForAck(node.Address(), ping, deadline)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
m.logger.Printf("[ERR] memberlist: Failed TCP fallback ping: %s", err)
|
m.logger.Printf("[ERR] memberlist: Failed fallback ping: %s", err)
|
||||||
} else {
|
} else {
|
||||||
fallbackCh <- didContact
|
fallbackCh <- didContact
|
||||||
}
|
}
|
||||||
|
@ -314,12 +385,28 @@ func (m *Memberlist) probeNode(node *nodeState) {
|
||||||
// any additional time here.
|
// any additional time here.
|
||||||
for didContact := range fallbackCh {
|
for didContact := range fallbackCh {
|
||||||
if didContact {
|
if didContact {
|
||||||
m.logger.Printf("[WARN] memberlist: Was able to reach %s via TCP but not UDP, network may be misconfigured and not allowing bidirectional UDP", node.Name)
|
m.logger.Printf("[WARN] memberlist: Was able to connect to %s but other probes failed, network may be misconfigured", node.Name)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// No acks received from target, suspect
|
// Update our self-awareness based on the results of this failed probe.
|
||||||
|
// If we don't have peers who will send nacks then we penalize for any
|
||||||
|
// failed probe as a simple health metric. If we do have peers to nack
|
||||||
|
// verify, then we can use that as a more sophisticated measure of self-
|
||||||
|
// health because we assume them to be working, and they can help us
|
||||||
|
// decide if the probed node was really dead or if it was something wrong
|
||||||
|
// with ourselves.
|
||||||
|
awarenessDelta = 0
|
||||||
|
if expectedNacks > 0 {
|
||||||
|
if nackCount := len(nackCh); nackCount < expectedNacks {
|
||||||
|
awarenessDelta += (expectedNacks - nackCount)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
awarenessDelta += 1
|
||||||
|
}
|
||||||
|
|
||||||
|
// No acks received from target, suspect it as failed.
|
||||||
m.logger.Printf("[INFO] memberlist: Suspect %s has failed, no acks received", node.Name)
|
m.logger.Printf("[INFO] memberlist: Suspect %s has failed, no acks received", node.Name)
|
||||||
s := suspect{Incarnation: node.Incarnation, Node: node.Name, From: m.config.Name}
|
s := suspect{Incarnation: node.Incarnation, Node: node.Name, From: m.config.Name}
|
||||||
m.suspectNode(&s)
|
m.suspectNode(&s)
|
||||||
|
@ -330,10 +417,10 @@ func (m *Memberlist) Ping(node string, addr net.Addr) (time.Duration, error) {
|
||||||
// Prepare a ping message and setup an ack handler.
|
// Prepare a ping message and setup an ack handler.
|
||||||
ping := ping{SeqNo: m.nextSeqNo(), Node: node}
|
ping := ping{SeqNo: m.nextSeqNo(), Node: node}
|
||||||
ackCh := make(chan ackMessage, m.config.IndirectChecks+1)
|
ackCh := make(chan ackMessage, m.config.IndirectChecks+1)
|
||||||
m.setAckChannel(ping.SeqNo, ackCh, m.config.ProbeInterval)
|
m.setProbeChannels(ping.SeqNo, ackCh, nil, m.config.ProbeInterval)
|
||||||
|
|
||||||
// Send a ping to the node.
|
// Send a ping to the node.
|
||||||
if err := m.encodeAndSendMsg(addr, pingMsg, &ping); err != nil {
|
if err := m.encodeAndSendMsg(addr.String(), pingMsg, &ping); err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -362,8 +449,8 @@ func (m *Memberlist) resetNodes() {
|
||||||
m.nodeLock.Lock()
|
m.nodeLock.Lock()
|
||||||
defer m.nodeLock.Unlock()
|
defer m.nodeLock.Unlock()
|
||||||
|
|
||||||
// Move the dead nodes
|
// Move dead nodes, but respect gossip to the dead interval
|
||||||
deadIdx := moveDeadNodes(m.nodes)
|
deadIdx := moveDeadNodes(m.nodes, m.config.GossipToTheDeadTime)
|
||||||
|
|
||||||
// Deregister the dead nodes
|
// Deregister the dead nodes
|
||||||
for i := deadIdx; i < len(m.nodes); i++ {
|
for i := deadIdx; i < len(m.nodes); i++ {
|
||||||
|
@ -386,14 +473,28 @@ func (m *Memberlist) resetNodes() {
|
||||||
func (m *Memberlist) gossip() {
|
func (m *Memberlist) gossip() {
|
||||||
defer metrics.MeasureSince([]string{"memberlist", "gossip"}, time.Now())
|
defer metrics.MeasureSince([]string{"memberlist", "gossip"}, time.Now())
|
||||||
|
|
||||||
// Get some random live nodes
|
// Get some random live, suspect, or recently dead nodes
|
||||||
m.nodeLock.RLock()
|
m.nodeLock.RLock()
|
||||||
excludes := []string{m.config.Name}
|
kNodes := kRandomNodes(m.config.GossipNodes, m.nodes, func(n *nodeState) bool {
|
||||||
kNodes := kRandomNodes(m.config.GossipNodes, excludes, m.nodes)
|
if n.Name == m.config.Name {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
switch n.State {
|
||||||
|
case stateAlive, stateSuspect:
|
||||||
|
return false
|
||||||
|
|
||||||
|
case stateDead:
|
||||||
|
return time.Since(n.StateChange) > m.config.GossipToTheDeadTime
|
||||||
|
|
||||||
|
default:
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
})
|
||||||
m.nodeLock.RUnlock()
|
m.nodeLock.RUnlock()
|
||||||
|
|
||||||
// Compute the bytes available
|
// Compute the bytes available
|
||||||
bytesAvail := udpSendBuf - compoundHeaderOverhead
|
bytesAvail := m.config.UDPBufferSize - compoundHeaderOverhead
|
||||||
if m.config.EncryptionEnabled() {
|
if m.config.EncryptionEnabled() {
|
||||||
bytesAvail -= encryptOverhead(m.encryptionVersion())
|
bytesAvail -= encryptOverhead(m.encryptionVersion())
|
||||||
}
|
}
|
||||||
|
@ -405,13 +506,18 @@ func (m *Memberlist) gossip() {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create a compound message
|
addr := node.Address()
|
||||||
compound := makeCompoundMessage(msgs)
|
if len(msgs) == 1 {
|
||||||
|
// Send single message as is
|
||||||
// Send the compound message
|
if err := m.rawSendMsgPacket(addr, &node.Node, msgs[0]); err != nil {
|
||||||
destAddr := &net.UDPAddr{IP: node.Addr, Port: int(node.Port)}
|
m.logger.Printf("[ERR] memberlist: Failed to send gossip to %s: %s", addr, err)
|
||||||
if err := m.rawSendMsgUDP(destAddr, compound.Bytes()); err != nil {
|
}
|
||||||
m.logger.Printf("[ERR] memberlist: Failed to send gossip to %s: %s", destAddr, err)
|
} else {
|
||||||
|
// Otherwise create and send a compound message
|
||||||
|
compound := makeCompoundMessage(msgs)
|
||||||
|
if err := m.rawSendMsgPacket(addr, &node.Node, compound.Bytes()); err != nil {
|
||||||
|
m.logger.Printf("[ERR] memberlist: Failed to send gossip to %s: %s", addr, err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -423,8 +529,10 @@ func (m *Memberlist) gossip() {
|
||||||
func (m *Memberlist) pushPull() {
|
func (m *Memberlist) pushPull() {
|
||||||
// Get a random live node
|
// Get a random live node
|
||||||
m.nodeLock.RLock()
|
m.nodeLock.RLock()
|
||||||
excludes := []string{m.config.Name}
|
nodes := kRandomNodes(1, m.nodes, func(n *nodeState) bool {
|
||||||
nodes := kRandomNodes(1, excludes, m.nodes)
|
return n.Name == m.config.Name ||
|
||||||
|
n.State != stateAlive
|
||||||
|
})
|
||||||
m.nodeLock.RUnlock()
|
m.nodeLock.RUnlock()
|
||||||
|
|
||||||
// If no nodes, bail
|
// If no nodes, bail
|
||||||
|
@ -434,17 +542,17 @@ func (m *Memberlist) pushPull() {
|
||||||
node := nodes[0]
|
node := nodes[0]
|
||||||
|
|
||||||
// Attempt a push pull
|
// Attempt a push pull
|
||||||
if err := m.pushPullNode(node.Addr, node.Port, false); err != nil {
|
if err := m.pushPullNode(node.Address(), false); err != nil {
|
||||||
m.logger.Printf("[ERR] memberlist: Push/Pull with %s failed: %s", node.Name, err)
|
m.logger.Printf("[ERR] memberlist: Push/Pull with %s failed: %s", node.Name, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// pushPullNode does a complete state exchange with a specific node.
|
// pushPullNode does a complete state exchange with a specific node.
|
||||||
func (m *Memberlist) pushPullNode(addr []byte, port uint16, join bool) error {
|
func (m *Memberlist) pushPullNode(addr string, join bool) error {
|
||||||
defer metrics.MeasureSince([]string{"memberlist", "pushPullNode"}, time.Now())
|
defer metrics.MeasureSince([]string{"memberlist", "pushPullNode"}, time.Now())
|
||||||
|
|
||||||
// Attempt to send and receive with the node
|
// Attempt to send and receive with the node
|
||||||
remote, userState, err := m.sendAndReceiveState(addr, port, join)
|
remote, userState, err := m.sendAndReceiveState(addr, join)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -584,6 +692,11 @@ func (m *Memberlist) nextIncarnation() uint32 {
|
||||||
return atomic.AddUint32(&m.incarnation, 1)
|
return atomic.AddUint32(&m.incarnation, 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// skipIncarnation adds the positive offset to the incarnation number.
|
||||||
|
func (m *Memberlist) skipIncarnation(offset uint32) uint32 {
|
||||||
|
return atomic.AddUint32(&m.incarnation, offset)
|
||||||
|
}
|
||||||
|
|
||||||
// estNumNodes is used to get the current estimate of the number of nodes
|
// estNumNodes is used to get the current estimate of the number of nodes
|
||||||
func (m *Memberlist) estNumNodes() int {
|
func (m *Memberlist) estNumNodes() int {
|
||||||
return int(atomic.LoadUint32(&m.numNodes))
|
return int(atomic.LoadUint32(&m.numNodes))
|
||||||
|
@ -595,19 +708,27 @@ type ackMessage struct {
|
||||||
Timestamp time.Time
|
Timestamp time.Time
|
||||||
}
|
}
|
||||||
|
|
||||||
// setAckChannel is used to attach a channel to receive a message when an ack with a given
|
// setProbeChannels is used to attach the ackCh to receive a message when an ack
|
||||||
// sequence number is received. The `complete` field of the message will be false on timeout
|
// with a given sequence number is received. The `complete` field of the message
|
||||||
func (m *Memberlist) setAckChannel(seqNo uint32, ch chan ackMessage, timeout time.Duration) {
|
// will be false on timeout. Any nack messages will cause an empty struct to be
|
||||||
// Create a handler function
|
// passed to the nackCh, which can be nil if not needed.
|
||||||
handler := func(payload []byte, timestamp time.Time) {
|
func (m *Memberlist) setProbeChannels(seqNo uint32, ackCh chan ackMessage, nackCh chan struct{}, timeout time.Duration) {
|
||||||
|
// Create handler functions for acks and nacks
|
||||||
|
ackFn := func(payload []byte, timestamp time.Time) {
|
||||||
select {
|
select {
|
||||||
case ch <- ackMessage{true, payload, timestamp}:
|
case ackCh <- ackMessage{true, payload, timestamp}:
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
}
|
||||||
|
nackFn := func() {
|
||||||
|
select {
|
||||||
|
case nackCh <- struct{}{}:
|
||||||
default:
|
default:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add the handler
|
// Add the handlers
|
||||||
ah := &ackHandler{handler, nil}
|
ah := &ackHandler{ackFn, nackFn, nil}
|
||||||
m.ackLock.Lock()
|
m.ackLock.Lock()
|
||||||
m.ackHandlers[seqNo] = ah
|
m.ackHandlers[seqNo] = ah
|
||||||
m.ackLock.Unlock()
|
m.ackLock.Unlock()
|
||||||
|
@ -618,18 +739,19 @@ func (m *Memberlist) setAckChannel(seqNo uint32, ch chan ackMessage, timeout tim
|
||||||
delete(m.ackHandlers, seqNo)
|
delete(m.ackHandlers, seqNo)
|
||||||
m.ackLock.Unlock()
|
m.ackLock.Unlock()
|
||||||
select {
|
select {
|
||||||
case ch <- ackMessage{false, nil, time.Now()}:
|
case ackCh <- ackMessage{false, nil, time.Now()}:
|
||||||
default:
|
default:
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// setAckHandler is used to attach a handler to be invoked when an
|
// setAckHandler is used to attach a handler to be invoked when an ack with a
|
||||||
// ack with a given sequence number is received. If a timeout is reached,
|
// given sequence number is received. If a timeout is reached, the handler is
|
||||||
// the handler is deleted
|
// deleted. This is used for indirect pings so does not configure a function
|
||||||
func (m *Memberlist) setAckHandler(seqNo uint32, handler func([]byte, time.Time), timeout time.Duration) {
|
// for nacks.
|
||||||
|
func (m *Memberlist) setAckHandler(seqNo uint32, ackFn func([]byte, time.Time), timeout time.Duration) {
|
||||||
// Add the handler
|
// Add the handler
|
||||||
ah := &ackHandler{handler, nil}
|
ah := &ackHandler{ackFn, nil, nil}
|
||||||
m.ackLock.Lock()
|
m.ackLock.Lock()
|
||||||
m.ackHandlers[seqNo] = ah
|
m.ackHandlers[seqNo] = ah
|
||||||
m.ackLock.Unlock()
|
m.ackLock.Unlock()
|
||||||
|
@ -642,7 +764,7 @@ func (m *Memberlist) setAckHandler(seqNo uint32, handler func([]byte, time.Time)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// Invokes an Ack handler if any is associated, and reaps the handler immediately
|
// Invokes an ack handler if any is associated, and reaps the handler immediately
|
||||||
func (m *Memberlist) invokeAckHandler(ack ackResp, timestamp time.Time) {
|
func (m *Memberlist) invokeAckHandler(ack ackResp, timestamp time.Time) {
|
||||||
m.ackLock.Lock()
|
m.ackLock.Lock()
|
||||||
ah, ok := m.ackHandlers[ack.SeqNo]
|
ah, ok := m.ackHandlers[ack.SeqNo]
|
||||||
|
@ -652,7 +774,49 @@ func (m *Memberlist) invokeAckHandler(ack ackResp, timestamp time.Time) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
ah.timer.Stop()
|
ah.timer.Stop()
|
||||||
ah.handler(ack.Payload, timestamp)
|
ah.ackFn(ack.Payload, timestamp)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Invokes nack handler if any is associated.
|
||||||
|
func (m *Memberlist) invokeNackHandler(nack nackResp) {
|
||||||
|
m.ackLock.Lock()
|
||||||
|
ah, ok := m.ackHandlers[nack.SeqNo]
|
||||||
|
m.ackLock.Unlock()
|
||||||
|
if !ok || ah.nackFn == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ah.nackFn()
|
||||||
|
}
|
||||||
|
|
||||||
|
// refute gossips an alive message in response to incoming information that we
|
||||||
|
// are suspect or dead. It will make sure the incarnation number beats the given
|
||||||
|
// accusedInc value, or you can supply 0 to just get the next incarnation number.
|
||||||
|
// This alters the node state that's passed in so this MUST be called while the
|
||||||
|
// nodeLock is held.
|
||||||
|
func (m *Memberlist) refute(me *nodeState, accusedInc uint32) {
|
||||||
|
// Make sure the incarnation number beats the accusation.
|
||||||
|
inc := m.nextIncarnation()
|
||||||
|
if accusedInc >= inc {
|
||||||
|
inc = m.skipIncarnation(accusedInc - inc + 1)
|
||||||
|
}
|
||||||
|
me.Incarnation = inc
|
||||||
|
|
||||||
|
// Decrease our health because we are being asked to refute a problem.
|
||||||
|
m.awareness.ApplyDelta(1)
|
||||||
|
|
||||||
|
// Format and broadcast an alive message.
|
||||||
|
a := alive{
|
||||||
|
Incarnation: inc,
|
||||||
|
Node: me.Name,
|
||||||
|
Addr: me.Addr,
|
||||||
|
Port: me.Port,
|
||||||
|
Meta: me.Meta,
|
||||||
|
Vsn: []uint8{
|
||||||
|
me.PMin, me.PMax, me.PCur,
|
||||||
|
me.DMin, me.DMax, me.DCur,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
m.encodeAndBroadcast(me.Addr.String(), aliveMsg, a)
|
||||||
}
|
}
|
||||||
|
|
||||||
// aliveNode is invoked by the network layer when we get a message about a
|
// aliveNode is invoked by the network layer when we get a message about a
|
||||||
|
@ -754,6 +918,9 @@ func (m *Memberlist) aliveNode(a *alive, notify chan struct{}, bootstrap bool) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Clear out any suspicion timer that may be in effect.
|
||||||
|
delete(m.nodeTimers, a.Node)
|
||||||
|
|
||||||
// Store the old state and meta data
|
// Store the old state and meta data
|
||||||
oldState := state.State
|
oldState := state.State
|
||||||
oldMeta := state.Meta
|
oldMeta := state.Meta
|
||||||
|
@ -783,21 +950,7 @@ func (m *Memberlist) aliveNode(a *alive, notify chan struct{}, bootstrap bool) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
inc := m.nextIncarnation()
|
m.refute(state, a.Incarnation)
|
||||||
for a.Incarnation >= inc {
|
|
||||||
inc = m.nextIncarnation()
|
|
||||||
}
|
|
||||||
state.Incarnation = inc
|
|
||||||
|
|
||||||
a := alive{
|
|
||||||
Incarnation: inc,
|
|
||||||
Node: state.Name,
|
|
||||||
Addr: state.Addr,
|
|
||||||
Port: state.Port,
|
|
||||||
Meta: state.Meta,
|
|
||||||
Vsn: versions,
|
|
||||||
}
|
|
||||||
m.encodeBroadcastNotify(a.Node, aliveMsg, a, notify)
|
|
||||||
m.logger.Printf("[WARN] memberlist: Refuting an alive message")
|
m.logger.Printf("[WARN] memberlist: Refuting an alive message")
|
||||||
} else {
|
} else {
|
||||||
m.encodeBroadcastNotify(a.Node, aliveMsg, a, notify)
|
m.encodeBroadcastNotify(a.Node, aliveMsg, a, notify)
|
||||||
|
@ -854,6 +1007,17 @@ func (m *Memberlist) suspectNode(s *suspect) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// See if there's a suspicion timer we can confirm. If the info is new
|
||||||
|
// to us we will go ahead and re-gossip it. This allows for multiple
|
||||||
|
// independent confirmations to flow even when a node probes a node
|
||||||
|
// that's already suspect.
|
||||||
|
if timer, ok := m.nodeTimers[s.Node]; ok {
|
||||||
|
if timer.Confirm(s.From) {
|
||||||
|
m.encodeAndBroadcast(s.Node, suspectMsg, s)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// Ignore non-alive nodes
|
// Ignore non-alive nodes
|
||||||
if state.State != stateAlive {
|
if state.State != stateAlive {
|
||||||
return
|
return
|
||||||
|
@ -861,24 +1025,7 @@ func (m *Memberlist) suspectNode(s *suspect) {
|
||||||
|
|
||||||
// If this is us we need to refute, otherwise re-broadcast
|
// If this is us we need to refute, otherwise re-broadcast
|
||||||
if state.Name == m.config.Name {
|
if state.Name == m.config.Name {
|
||||||
inc := m.nextIncarnation()
|
m.refute(state, s.Incarnation)
|
||||||
for s.Incarnation >= inc {
|
|
||||||
inc = m.nextIncarnation()
|
|
||||||
}
|
|
||||||
state.Incarnation = inc
|
|
||||||
|
|
||||||
a := alive{
|
|
||||||
Incarnation: inc,
|
|
||||||
Node: state.Name,
|
|
||||||
Addr: state.Addr,
|
|
||||||
Port: state.Port,
|
|
||||||
Meta: state.Meta,
|
|
||||||
Vsn: []uint8{
|
|
||||||
state.PMin, state.PMax, state.PCur,
|
|
||||||
state.DMin, state.DMax, state.DCur,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
m.encodeAndBroadcast(s.Node, aliveMsg, a)
|
|
||||||
m.logger.Printf("[WARN] memberlist: Refuting a suspect message (from: %s)", s.From)
|
m.logger.Printf("[WARN] memberlist: Refuting a suspect message (from: %s)", s.From)
|
||||||
return // Do not mark ourself suspect
|
return // Do not mark ourself suspect
|
||||||
} else {
|
} else {
|
||||||
|
@ -894,26 +1041,41 @@ func (m *Memberlist) suspectNode(s *suspect) {
|
||||||
changeTime := time.Now()
|
changeTime := time.Now()
|
||||||
state.StateChange = changeTime
|
state.StateChange = changeTime
|
||||||
|
|
||||||
// Setup a timeout for this
|
// Setup a suspicion timer. Given that we don't have any known phase
|
||||||
timeout := suspicionTimeout(m.config.SuspicionMult, m.estNumNodes(), m.config.ProbeInterval)
|
// relationship with our peers, we set up k such that we hit the nominal
|
||||||
time.AfterFunc(timeout, func() {
|
// timeout two probe intervals short of what we expect given the suspicion
|
||||||
|
// multiplier.
|
||||||
|
k := m.config.SuspicionMult - 2
|
||||||
|
|
||||||
|
// If there aren't enough nodes to give the expected confirmations, just
|
||||||
|
// set k to 0 to say that we don't expect any. Note we subtract 2 from n
|
||||||
|
// here to take out ourselves and the node being probed.
|
||||||
|
n := m.estNumNodes()
|
||||||
|
if n-2 < k {
|
||||||
|
k = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compute the timeouts based on the size of the cluster.
|
||||||
|
min := suspicionTimeout(m.config.SuspicionMult, n, m.config.ProbeInterval)
|
||||||
|
max := time.Duration(m.config.SuspicionMaxTimeoutMult) * min
|
||||||
|
fn := func(numConfirmations int) {
|
||||||
m.nodeLock.Lock()
|
m.nodeLock.Lock()
|
||||||
state, ok := m.nodeMap[s.Node]
|
state, ok := m.nodeMap[s.Node]
|
||||||
timeout := ok && state.State == stateSuspect && state.StateChange == changeTime
|
timeout := ok && state.State == stateSuspect && state.StateChange == changeTime
|
||||||
m.nodeLock.Unlock()
|
m.nodeLock.Unlock()
|
||||||
|
|
||||||
if timeout {
|
if timeout {
|
||||||
m.suspectTimeout(state)
|
if k > 0 && numConfirmations < k {
|
||||||
}
|
metrics.IncrCounter([]string{"memberlist", "degraded", "timeout"}, 1)
|
||||||
})
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// suspectTimeout is invoked when a suspect timeout has occurred
|
m.logger.Printf("[INFO] memberlist: Marking %s as failed, suspect timeout reached (%d peer confirmations)",
|
||||||
func (m *Memberlist) suspectTimeout(n *nodeState) {
|
state.Name, numConfirmations)
|
||||||
// Construct a dead message
|
d := dead{Incarnation: state.Incarnation, Node: state.Name, From: m.config.Name}
|
||||||
m.logger.Printf("[INFO] memberlist: Marking %s as failed, suspect timeout reached", n.Name)
|
m.deadNode(&d)
|
||||||
d := dead{Incarnation: n.Incarnation, Node: n.Name, From: m.config.Name}
|
}
|
||||||
m.deadNode(&d)
|
}
|
||||||
|
m.nodeTimers[s.Node] = newSuspicion(s.From, k, min, max, fn)
|
||||||
}
|
}
|
||||||
|
|
||||||
// deadNode is invoked by the network layer when we get a message
|
// deadNode is invoked by the network layer when we get a message
|
||||||
|
@ -933,6 +1095,9 @@ func (m *Memberlist) deadNode(d *dead) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Clear out any suspicion timer that may be in effect.
|
||||||
|
delete(m.nodeTimers, d.Node)
|
||||||
|
|
||||||
// Ignore if node is already dead
|
// Ignore if node is already dead
|
||||||
if state.State == stateDead {
|
if state.State == stateDead {
|
||||||
return
|
return
|
||||||
|
@ -942,24 +1107,7 @@ func (m *Memberlist) deadNode(d *dead) {
|
||||||
if state.Name == m.config.Name {
|
if state.Name == m.config.Name {
|
||||||
// If we are not leaving we need to refute
|
// If we are not leaving we need to refute
|
||||||
if !m.leave {
|
if !m.leave {
|
||||||
inc := m.nextIncarnation()
|
m.refute(state, d.Incarnation)
|
||||||
for d.Incarnation >= inc {
|
|
||||||
inc = m.nextIncarnation()
|
|
||||||
}
|
|
||||||
state.Incarnation = inc
|
|
||||||
|
|
||||||
a := alive{
|
|
||||||
Incarnation: inc,
|
|
||||||
Node: state.Name,
|
|
||||||
Addr: state.Addr,
|
|
||||||
Port: state.Port,
|
|
||||||
Meta: state.Meta,
|
|
||||||
Vsn: []uint8{
|
|
||||||
state.PMin, state.PMax, state.PCur,
|
|
||||||
state.DMin, state.DMax, state.DCur,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
m.encodeAndBroadcast(d.Node, aliveMsg, a)
|
|
||||||
m.logger.Printf("[WARN] memberlist: Refuting a dead message (from: %s)", d.From)
|
m.logger.Printf("[WARN] memberlist: Refuting a dead message (from: %s)", d.From)
|
||||||
return // Do not mark ourself dead
|
return // Do not mark ourself dead
|
||||||
}
|
}
|
||||||
|
@ -1001,7 +1149,7 @@ func (m *Memberlist) mergeState(remote []pushNodeState) {
|
||||||
m.aliveNode(&a, nil, false)
|
m.aliveNode(&a, nil, false)
|
||||||
|
|
||||||
case stateDead:
|
case stateDead:
|
||||||
// If the remote node belives a node is dead, we prefer to
|
// If the remote node believes a node is dead, we prefer to
|
||||||
// suspect that node instead of declaring it dead instantly
|
// suspect that node instead of declaring it dead instantly
|
||||||
fallthrough
|
fallthrough
|
||||||
case stateSuspect:
|
case stateSuspect:
|
||||||
|
|
130
vendor/github.com/hashicorp/memberlist/suspicion.go
generated
vendored
Normal file
130
vendor/github.com/hashicorp/memberlist/suspicion.go
generated
vendored
Normal file
|
@ -0,0 +1,130 @@
|
||||||
|
package memberlist
|
||||||
|
|
||||||
|
import (
|
||||||
|
"math"
|
||||||
|
"sync/atomic"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// suspicion manages the suspect timer for a node and provides an interface
|
||||||
|
// to accelerate the timeout as we get more independent confirmations that
|
||||||
|
// a node is suspect.
|
||||||
|
type suspicion struct {
|
||||||
|
// n is the number of independent confirmations we've seen. This must
|
||||||
|
// be updated using atomic instructions to prevent contention with the
|
||||||
|
// timer callback.
|
||||||
|
n int32
|
||||||
|
|
||||||
|
// k is the number of independent confirmations we'd like to see in
|
||||||
|
// order to drive the timer to its minimum value.
|
||||||
|
k int32
|
||||||
|
|
||||||
|
// min is the minimum timer value.
|
||||||
|
min time.Duration
|
||||||
|
|
||||||
|
// max is the maximum timer value.
|
||||||
|
max time.Duration
|
||||||
|
|
||||||
|
// start captures the timestamp when we began the timer. This is used
|
||||||
|
// so we can calculate durations to feed the timer during updates in
|
||||||
|
// a way the achieves the overall time we'd like.
|
||||||
|
start time.Time
|
||||||
|
|
||||||
|
// timer is the underlying timer that implements the timeout.
|
||||||
|
timer *time.Timer
|
||||||
|
|
||||||
|
// f is the function to call when the timer expires. We hold on to this
|
||||||
|
// because there are cases where we call it directly.
|
||||||
|
timeoutFn func()
|
||||||
|
|
||||||
|
// confirmations is a map of "from" nodes that have confirmed a given
|
||||||
|
// node is suspect. This prevents double counting.
|
||||||
|
confirmations map[string]struct{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// newSuspicion returns a timer started with the max time, and that will drive
|
||||||
|
// to the min time after seeing k or more confirmations. The from node will be
|
||||||
|
// excluded from confirmations since we might get our own suspicion message
|
||||||
|
// gossiped back to us. The minimum time will be used if no confirmations are
|
||||||
|
// called for (k <= 0).
|
||||||
|
func newSuspicion(from string, k int, min time.Duration, max time.Duration, fn func(int)) *suspicion {
|
||||||
|
s := &suspicion{
|
||||||
|
k: int32(k),
|
||||||
|
min: min,
|
||||||
|
max: max,
|
||||||
|
confirmations: make(map[string]struct{}),
|
||||||
|
}
|
||||||
|
|
||||||
|
// Exclude the from node from any confirmations.
|
||||||
|
s.confirmations[from] = struct{}{}
|
||||||
|
|
||||||
|
// Pass the number of confirmations into the timeout function for
|
||||||
|
// easy telemetry.
|
||||||
|
s.timeoutFn = func() {
|
||||||
|
fn(int(atomic.LoadInt32(&s.n)))
|
||||||
|
}
|
||||||
|
|
||||||
|
// If there aren't any confirmations to be made then take the min
|
||||||
|
// time from the start.
|
||||||
|
timeout := max
|
||||||
|
if k < 1 {
|
||||||
|
timeout = min
|
||||||
|
}
|
||||||
|
s.timer = time.AfterFunc(timeout, s.timeoutFn)
|
||||||
|
|
||||||
|
// Capture the start time right after starting the timer above so
|
||||||
|
// we should always err on the side of a little longer timeout if
|
||||||
|
// there's any preemption that separates this and the step above.
|
||||||
|
s.start = time.Now()
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
// remainingSuspicionTime takes the state variables of the suspicion timer and
|
||||||
|
// calculates the remaining time to wait before considering a node dead. The
|
||||||
|
// return value can be negative, so be prepared to fire the timer immediately in
|
||||||
|
// that case.
|
||||||
|
func remainingSuspicionTime(n, k int32, elapsed time.Duration, min, max time.Duration) time.Duration {
|
||||||
|
frac := math.Log(float64(n)+1.0) / math.Log(float64(k)+1.0)
|
||||||
|
raw := max.Seconds() - frac*(max.Seconds()-min.Seconds())
|
||||||
|
timeout := time.Duration(math.Floor(1000.0*raw)) * time.Millisecond
|
||||||
|
if timeout < min {
|
||||||
|
timeout = min
|
||||||
|
}
|
||||||
|
|
||||||
|
// We have to take into account the amount of time that has passed so
|
||||||
|
// far, so we get the right overall timeout.
|
||||||
|
return timeout - elapsed
|
||||||
|
}
|
||||||
|
|
||||||
|
// Confirm registers that a possibly new peer has also determined the given
|
||||||
|
// node is suspect. This returns true if this was new information, and false
|
||||||
|
// if it was a duplicate confirmation, or if we've got enough confirmations to
|
||||||
|
// hit the minimum.
|
||||||
|
func (s *suspicion) Confirm(from string) bool {
|
||||||
|
// If we've got enough confirmations then stop accepting them.
|
||||||
|
if atomic.LoadInt32(&s.n) >= s.k {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Only allow one confirmation from each possible peer.
|
||||||
|
if _, ok := s.confirmations[from]; ok {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
s.confirmations[from] = struct{}{}
|
||||||
|
|
||||||
|
// Compute the new timeout given the current number of confirmations and
|
||||||
|
// adjust the timer. If the timeout becomes negative *and* we can cleanly
|
||||||
|
// stop the timer then we will call the timeout function directly from
|
||||||
|
// here.
|
||||||
|
n := atomic.AddInt32(&s.n, 1)
|
||||||
|
elapsed := time.Now().Sub(s.start)
|
||||||
|
remaining := remainingSuspicionTime(n, s.k, elapsed, s.min, s.max)
|
||||||
|
if s.timer.Stop() {
|
||||||
|
if remaining > 0 {
|
||||||
|
s.timer.Reset(remaining)
|
||||||
|
} else {
|
||||||
|
go s.timeoutFn()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
65
vendor/github.com/hashicorp/memberlist/transport.go
generated
vendored
Normal file
65
vendor/github.com/hashicorp/memberlist/transport.go
generated
vendored
Normal file
|
@ -0,0 +1,65 @@
|
||||||
|
package memberlist
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Packet is used to provide some metadata about incoming packets from peers
|
||||||
|
// over a packet connection, as well as the packet payload.
|
||||||
|
type Packet struct {
|
||||||
|
// Buf has the raw contents of the packet.
|
||||||
|
Buf []byte
|
||||||
|
|
||||||
|
// From has the address of the peer. This is an actual net.Addr so we
|
||||||
|
// can expose some concrete details about incoming packets.
|
||||||
|
From net.Addr
|
||||||
|
|
||||||
|
// Timestamp is the time when the packet was received. This should be
|
||||||
|
// taken as close as possible to the actual receipt time to help make an
|
||||||
|
// accurate RTT measurements during probes.
|
||||||
|
Timestamp time.Time
|
||||||
|
}
|
||||||
|
|
||||||
|
// Transport is used to abstract over communicating with other peers. The packet
|
||||||
|
// interface is assumed to be best-effort and the stream interface is assumed to
|
||||||
|
// be reliable.
|
||||||
|
type Transport interface {
|
||||||
|
// FinalAdvertiseAddr is given the user's configured values (which
|
||||||
|
// might be empty) and returns the desired IP and port to advertise to
|
||||||
|
// the rest of the cluster.
|
||||||
|
FinalAdvertiseAddr(ip string, port int) (net.IP, int, error)
|
||||||
|
|
||||||
|
// WriteTo is a packet-oriented interface that fires off the given
|
||||||
|
// payload to the given address in a connectionless fashion. This should
|
||||||
|
// return a time stamp that's as close as possible to when the packet
|
||||||
|
// was transmitted to help make accurate RTT measurements during probes.
|
||||||
|
//
|
||||||
|
// This is similar to net.PacketConn, though we didn't want to expose
|
||||||
|
// that full set of required methods to keep assumptions about the
|
||||||
|
// underlying plumbing to a minimum. We also treat the address here as a
|
||||||
|
// string, similar to Dial, so it's network neutral, so this usually is
|
||||||
|
// in the form of "host:port".
|
||||||
|
WriteTo(b []byte, addr string) (time.Time, error)
|
||||||
|
|
||||||
|
// PacketCh returns a channel that can be read to receive incoming
|
||||||
|
// packets from other peers. How this is set up for listening is left as
|
||||||
|
// an exercise for the concrete transport implementations.
|
||||||
|
PacketCh() <-chan *Packet
|
||||||
|
|
||||||
|
// DialTimeout is used to create a connection that allows us to perform
|
||||||
|
// two-way communication with a peer. This is generally more expensive
|
||||||
|
// than packet connections so is used for more infrequent operations
|
||||||
|
// such as anti-entropy or fallback probes if the packet-oriented probe
|
||||||
|
// failed.
|
||||||
|
DialTimeout(addr string, timeout time.Duration) (net.Conn, error)
|
||||||
|
|
||||||
|
// StreamCh returns a channel that can be read to handle incoming stream
|
||||||
|
// connections from other peers. How this is set up for listening is
|
||||||
|
// left as an exercise for the concrete transport implementations.
|
||||||
|
StreamCh() <-chan net.Conn
|
||||||
|
|
||||||
|
// Shutdown is called when memberlist is shutting down; this gives the
|
||||||
|
// transport a chance to clean up any listeners.
|
||||||
|
Shutdown() error
|
||||||
|
}
|
170
vendor/github.com/hashicorp/memberlist/util.go
generated
vendored
170
vendor/github.com/hashicorp/memberlist/util.go
generated
vendored
|
@ -9,10 +9,12 @@ import (
|
||||||
"math"
|
"math"
|
||||||
"math/rand"
|
"math/rand"
|
||||||
"net"
|
"net"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/hashicorp/go-msgpack/codec"
|
"github.com/hashicorp/go-msgpack/codec"
|
||||||
|
"github.com/sean-/seed"
|
||||||
)
|
)
|
||||||
|
|
||||||
// pushPullScale is the minimum number of nodes
|
// pushPullScale is the minimum number of nodes
|
||||||
|
@ -22,72 +24,13 @@ import (
|
||||||
// while the 65th will triple it.
|
// while the 65th will triple it.
|
||||||
const pushPullScaleThreshold = 32
|
const pushPullScaleThreshold = 32
|
||||||
|
|
||||||
/*
|
|
||||||
* Contains an entry for each private block:
|
|
||||||
* 10.0.0.0/8
|
|
||||||
* 100.64.0.0/10
|
|
||||||
* 127.0.0.0/8
|
|
||||||
* 169.254.0.0/16
|
|
||||||
* 172.16.0.0/12
|
|
||||||
* 192.168.0.0/16
|
|
||||||
*/
|
|
||||||
var privateBlocks []*net.IPNet
|
|
||||||
|
|
||||||
var loopbackBlock *net.IPNet
|
|
||||||
|
|
||||||
const (
|
const (
|
||||||
// Constant litWidth 2-8
|
// Constant litWidth 2-8
|
||||||
lzwLitWidth = 8
|
lzwLitWidth = 8
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
// Seed the random number generator
|
seed.Init()
|
||||||
rand.Seed(time.Now().UnixNano())
|
|
||||||
|
|
||||||
// Add each private block
|
|
||||||
privateBlocks = make([]*net.IPNet, 6)
|
|
||||||
|
|
||||||
_, block, err := net.ParseCIDR("10.0.0.0/8")
|
|
||||||
if err != nil {
|
|
||||||
panic(fmt.Sprintf("Bad cidr. Got %v", err))
|
|
||||||
}
|
|
||||||
privateBlocks[0] = block
|
|
||||||
|
|
||||||
_, block, err = net.ParseCIDR("100.64.0.0/10")
|
|
||||||
if err != nil {
|
|
||||||
panic(fmt.Sprintf("Bad cidr. Got %v", err))
|
|
||||||
}
|
|
||||||
privateBlocks[1] = block
|
|
||||||
|
|
||||||
_, block, err = net.ParseCIDR("127.0.0.0/8")
|
|
||||||
if err != nil {
|
|
||||||
panic(fmt.Sprintf("Bad cidr. Got %v", err))
|
|
||||||
}
|
|
||||||
privateBlocks[2] = block
|
|
||||||
|
|
||||||
_, block, err = net.ParseCIDR("169.254.0.0/16")
|
|
||||||
if err != nil {
|
|
||||||
panic(fmt.Sprintf("Bad cidr. Got %v", err))
|
|
||||||
}
|
|
||||||
privateBlocks[3] = block
|
|
||||||
|
|
||||||
_, block, err = net.ParseCIDR("172.16.0.0/12")
|
|
||||||
if err != nil {
|
|
||||||
panic(fmt.Sprintf("Bad cidr. Got %v", err))
|
|
||||||
}
|
|
||||||
privateBlocks[4] = block
|
|
||||||
|
|
||||||
_, block, err = net.ParseCIDR("192.168.0.0/16")
|
|
||||||
if err != nil {
|
|
||||||
panic(fmt.Sprintf("Bad cidr. Got %v", err))
|
|
||||||
}
|
|
||||||
privateBlocks[5] = block
|
|
||||||
|
|
||||||
_, block, err = net.ParseCIDR("127.0.0.0/8")
|
|
||||||
if err != nil {
|
|
||||||
panic(fmt.Sprintf("Bad cidr. Got %v", err))
|
|
||||||
}
|
|
||||||
loopbackBlock = block
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Decode reverses the encode operation on a byte slice input
|
// Decode reverses the encode operation on a byte slice input
|
||||||
|
@ -108,42 +51,6 @@ func encode(msgType messageType, in interface{}) (*bytes.Buffer, error) {
|
||||||
return buf, err
|
return buf, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetPrivateIP returns the first private IP address found in a list of
|
|
||||||
// addresses.
|
|
||||||
func GetPrivateIP(addresses []net.Addr) (net.IP, error) {
|
|
||||||
var candidates []net.IP
|
|
||||||
|
|
||||||
// Find private IPv4 address
|
|
||||||
for _, rawAddr := range addresses {
|
|
||||||
var ip net.IP
|
|
||||||
switch addr := rawAddr.(type) {
|
|
||||||
case *net.IPAddr:
|
|
||||||
ip = addr.IP
|
|
||||||
case *net.IPNet:
|
|
||||||
ip = addr.IP
|
|
||||||
default:
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if ip.To4() == nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if !IsPrivateIP(ip.String()) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
candidates = append(candidates, ip)
|
|
||||||
}
|
|
||||||
numIps := len(candidates)
|
|
||||||
switch numIps {
|
|
||||||
case 0:
|
|
||||||
return nil, fmt.Errorf("No private IP address found")
|
|
||||||
case 1:
|
|
||||||
return candidates[0], nil
|
|
||||||
default:
|
|
||||||
return nil, fmt.Errorf("Multiple private IPs found. Please configure one.")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Returns a random offset between 0 and n
|
// Returns a random offset between 0 and n
|
||||||
func randomOffset(n int) int {
|
func randomOffset(n int) int {
|
||||||
if n == 0 {
|
if n == 0 {
|
||||||
|
@ -155,8 +62,9 @@ func randomOffset(n int) int {
|
||||||
// suspicionTimeout computes the timeout that should be used when
|
// suspicionTimeout computes the timeout that should be used when
|
||||||
// a node is suspected
|
// a node is suspected
|
||||||
func suspicionTimeout(suspicionMult, n int, interval time.Duration) time.Duration {
|
func suspicionTimeout(suspicionMult, n int, interval time.Duration) time.Duration {
|
||||||
nodeScale := math.Ceil(math.Log10(float64(n + 1)))
|
nodeScale := math.Max(1.0, math.Log10(math.Max(1.0, float64(n))))
|
||||||
timeout := time.Duration(suspicionMult) * time.Duration(nodeScale) * interval
|
// multiply by 1000 to keep some precision because time.Duration is an int64 type
|
||||||
|
timeout := time.Duration(suspicionMult) * time.Duration(nodeScale*1000) * interval / 1000
|
||||||
return timeout
|
return timeout
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -189,9 +97,9 @@ func pushPullScale(interval time.Duration, n int) time.Duration {
|
||||||
return time.Duration(multiplier) * interval
|
return time.Duration(multiplier) * interval
|
||||||
}
|
}
|
||||||
|
|
||||||
// moveDeadNodes moves all the nodes in the dead state
|
// moveDeadNodes moves nodes that are dead and beyond the gossip to the dead interval
|
||||||
// to the end of the slice and returns the index of the first dead node.
|
// to the end of the slice and returns the index of the first moved node.
|
||||||
func moveDeadNodes(nodes []*nodeState) int {
|
func moveDeadNodes(nodes []*nodeState, gossipToTheDeadTime time.Duration) int {
|
||||||
numDead := 0
|
numDead := 0
|
||||||
n := len(nodes)
|
n := len(nodes)
|
||||||
for i := 0; i < n-numDead; i++ {
|
for i := 0; i < n-numDead; i++ {
|
||||||
|
@ -199,6 +107,11 @@ func moveDeadNodes(nodes []*nodeState) int {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Respect the gossip to the dead interval
|
||||||
|
if time.Since(nodes[i].StateChange) <= gossipToTheDeadTime {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
// Move this node to the end
|
// Move this node to the end
|
||||||
nodes[i], nodes[n-numDead-1] = nodes[n-numDead-1], nodes[i]
|
nodes[i], nodes[n-numDead-1] = nodes[n-numDead-1], nodes[i]
|
||||||
numDead++
|
numDead++
|
||||||
|
@ -207,9 +120,10 @@ func moveDeadNodes(nodes []*nodeState) int {
|
||||||
return n - numDead
|
return n - numDead
|
||||||
}
|
}
|
||||||
|
|
||||||
// kRandomNodes is used to select up to k random nodes, excluding a given
|
// kRandomNodes is used to select up to k random nodes, excluding any nodes where
|
||||||
// node and any non-alive nodes. It is possible that less than k nodes are returned.
|
// the filter function returns true. It is possible that less than k nodes are
|
||||||
func kRandomNodes(k int, excludes []string, nodes []*nodeState) []*nodeState {
|
// returned.
|
||||||
|
func kRandomNodes(k int, nodes []*nodeState, filterFn func(*nodeState) bool) []*nodeState {
|
||||||
n := len(nodes)
|
n := len(nodes)
|
||||||
kNodes := make([]*nodeState, 0, k)
|
kNodes := make([]*nodeState, 0, k)
|
||||||
OUTER:
|
OUTER:
|
||||||
|
@ -221,16 +135,9 @@ OUTER:
|
||||||
idx := randomOffset(n)
|
idx := randomOffset(n)
|
||||||
node := nodes[idx]
|
node := nodes[idx]
|
||||||
|
|
||||||
// Exclude node if match
|
// Give the filter a shot at it.
|
||||||
for _, exclude := range excludes {
|
if filterFn != nil && filterFn(node) {
|
||||||
if node.Name == exclude {
|
continue OUTER
|
||||||
continue OUTER
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Exclude if not alive
|
|
||||||
if node.State != stateAlive {
|
|
||||||
continue
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if we have this node already
|
// Check if we have this node already
|
||||||
|
@ -310,27 +217,18 @@ func decodeCompoundMessage(buf []byte) (trunc int, parts [][]byte, err error) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns if the given IP is in a private block
|
// Given a string of the form "host", "host:port",
|
||||||
func IsPrivateIP(ip_str string) bool {
|
// "ipv6::addr" or "[ipv6::address]:port",
|
||||||
ip := net.ParseIP(ip_str)
|
|
||||||
for _, priv := range privateBlocks {
|
|
||||||
if priv.Contains(ip) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// Returns if the given IP is in a loopback block
|
|
||||||
func isLoopbackIP(ip_str string) bool {
|
|
||||||
ip := net.ParseIP(ip_str)
|
|
||||||
return loopbackBlock.Contains(ip)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Given a string of the form "host", "host:port", or "[ipv6::address]:port",
|
|
||||||
// return true if the string includes a port.
|
// return true if the string includes a port.
|
||||||
func hasPort(s string) bool {
|
func hasPort(s string) bool {
|
||||||
return strings.LastIndex(s, ":") > strings.LastIndex(s, "]")
|
last := strings.LastIndex(s, ":")
|
||||||
|
if last == -1 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if s[0] == '[' {
|
||||||
|
return s[last-1] == ']'
|
||||||
|
}
|
||||||
|
return strings.Index(s, ":") == last
|
||||||
}
|
}
|
||||||
|
|
||||||
// compressPayload takes an opaque input buffer, compresses it
|
// compressPayload takes an opaque input buffer, compresses it
|
||||||
|
@ -390,3 +288,9 @@ func decompressBuffer(c *compress) ([]byte, error) {
|
||||||
// Return the uncompressed bytes
|
// Return the uncompressed bytes
|
||||||
return b.Bytes(), nil
|
return b.Bytes(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// joinHostPort returns the host:port form of an address, for use with a
|
||||||
|
// transport.
|
||||||
|
func joinHostPort(host string, port uint16) string {
|
||||||
|
return net.JoinHostPort(host, strconv.Itoa(int(port)))
|
||||||
|
}
|
||||||
|
|
54
vendor/github.com/sean-/seed/LICENSE
generated
vendored
Normal file
54
vendor/github.com/sean-/seed/LICENSE
generated
vendored
Normal file
|
@ -0,0 +1,54 @@
|
||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2017 Sean Chittenden
|
||||||
|
Copyright (c) 2016 Alex Dadgar
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
|
|
||||||
|
=====
|
||||||
|
|
||||||
|
Bits of Go-lang's `once.Do()` were cribbed and reused here, too.
|
||||||
|
|
||||||
|
Copyright (c) 2009 The Go Authors. All rights reserved.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
modification, are permitted provided that the following conditions are
|
||||||
|
met:
|
||||||
|
|
||||||
|
* Redistributions of source code must retain the above copyright
|
||||||
|
notice, this list of conditions and the following disclaimer.
|
||||||
|
* Redistributions in binary form must reproduce the above
|
||||||
|
copyright notice, this list of conditions and the following disclaimer
|
||||||
|
in the documentation and/or other materials provided with the
|
||||||
|
distribution.
|
||||||
|
* Neither the name of Google Inc. nor the names of its
|
||||||
|
contributors may be used to endorse or promote products derived from
|
||||||
|
this software without specific prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
44
vendor/github.com/sean-/seed/README.md
generated
vendored
Normal file
44
vendor/github.com/sean-/seed/README.md
generated
vendored
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
# `seed` - Quickly Seed Go's Random Number Generator
|
||||||
|
|
||||||
|
Boiler-plate to securely [seed](https://en.wikipedia.org/wiki/Random_seed) Go's
|
||||||
|
random number generator (if possible). This library isn't anything fancy, it's
|
||||||
|
just a canonical way of seeding Go's random number generator. Cribbed from
|
||||||
|
[`Nomad`](https://github.com/hashicorp/nomad/commit/f89a993ec6b91636a3384dd568898245fbc273a1)
|
||||||
|
before it was moved into
|
||||||
|
[`Consul`](https://github.com/hashicorp/consul/commit/d695bcaae6e31ee307c11fdf55bb0bf46ea9fcf4)
|
||||||
|
and made into a helper function, and now further modularized to be a super
|
||||||
|
lightweight and reusable library.
|
||||||
|
|
||||||
|
Time is better than
|
||||||
|
[Go's default seed of `1`](https://golang.org/pkg/math/rand/#Seed), but friends
|
||||||
|
don't let friends use time as a seed to a random number generator. Use
|
||||||
|
`seed.MustInit()` instead.
|
||||||
|
|
||||||
|
`seed.Init()` is an idempotent and reentrant call that will return an error if
|
||||||
|
it can't seed the value the first time it is called. `Init()` is reentrant.
|
||||||
|
|
||||||
|
`seed.MustInit()` is idempotent and reentrant call that will `panic()` if it
|
||||||
|
can't seed the value the first time it is called. `MustInit()` is reentrant.
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
```
|
||||||
|
package mypackage
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/sean-/seed"
|
||||||
|
)
|
||||||
|
|
||||||
|
// MustInit will panic() if it is unable to set a high-entropy random seed:
|
||||||
|
func init() {
|
||||||
|
seed.MustInit()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Or if you want to not panic() and can actually handle this error:
|
||||||
|
func init() {
|
||||||
|
if secure, err := !seed.Init(); !secure {
|
||||||
|
// Handle the error
|
||||||
|
//panic(fmt.Sprintf("Unable to securely seed Go's RNG: %v", err))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
84
vendor/github.com/sean-/seed/init.go
generated
vendored
Normal file
84
vendor/github.com/sean-/seed/init.go
generated
vendored
Normal file
|
@ -0,0 +1,84 @@
|
||||||
|
package seed
|
||||||
|
|
||||||
|
import (
|
||||||
|
crand "crypto/rand"
|
||||||
|
"fmt"
|
||||||
|
"math"
|
||||||
|
"math/big"
|
||||||
|
"math/rand"
|
||||||
|
"sync"
|
||||||
|
"sync/atomic"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
m sync.Mutex
|
||||||
|
secure int32
|
||||||
|
seeded int32
|
||||||
|
)
|
||||||
|
|
||||||
|
func cryptoSeed() error {
|
||||||
|
defer atomic.StoreInt32(&seeded, 1)
|
||||||
|
|
||||||
|
var err error
|
||||||
|
var n *big.Int
|
||||||
|
n, err = crand.Int(crand.Reader, big.NewInt(math.MaxInt64))
|
||||||
|
if err != nil {
|
||||||
|
rand.Seed(time.Now().UTC().UnixNano())
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
rand.Seed(n.Int64())
|
||||||
|
atomic.StoreInt32(&secure, 1)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Init provides best-effort seeding (which is better than running with Go's
|
||||||
|
// default seed of 1). If `/dev/urandom` is available, Init() will seed Go's
|
||||||
|
// runtime with entropy from `/dev/urandom` and return true because the runtime
|
||||||
|
// was securely seeded. If Init() has already initialized the random number or
|
||||||
|
// it had failed to securely initialize the random number generation, Init()
|
||||||
|
// will return false. See MustInit().
|
||||||
|
func Init() (seededSecurely bool, err error) {
|
||||||
|
if atomic.LoadInt32(&seeded) == 1 {
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Slow-path
|
||||||
|
m.Lock()
|
||||||
|
defer m.Unlock()
|
||||||
|
|
||||||
|
if err := cryptoSeed(); err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// MustInit provides guaranteed secure seeding. If `/dev/urandom` is not
|
||||||
|
// available, MustInit will panic() with an error indicating why reading from
|
||||||
|
// `/dev/urandom` failed. MustInit() will upgrade the seed if for some reason a
|
||||||
|
// call to Init() failed in the past.
|
||||||
|
func MustInit() {
|
||||||
|
if atomic.LoadInt32(&secure) == 1 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Slow-path
|
||||||
|
m.Lock()
|
||||||
|
defer m.Unlock()
|
||||||
|
|
||||||
|
if err := cryptoSeed(); err != nil {
|
||||||
|
panic(fmt.Sprintf("Unable to seed the random number generator: %v", err))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Secure returns true if a cryptographically secure seed was used to
|
||||||
|
// initialize rand.
|
||||||
|
func Secure() bool {
|
||||||
|
return atomic.LoadInt32(&secure) == 1
|
||||||
|
}
|
||||||
|
|
||||||
|
// Seeded returns true if Init has seeded the random number generator.
|
||||||
|
func Seeded() bool {
|
||||||
|
return atomic.LoadInt32(&seeded) == 1
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue