Signed-off-by: Flavio Crisciani <flavio.crisciani@docker.com>
| ... | ... |
@@ -29,7 +29,9 @@ github.com/docker/go-events 18b43f1bc85d9cdd42c05a6cd2d444c7a200a894 |
| 29 | 29 |
github.com/armon/go-radix e39d623f12e8e41c7b5529e9a9dd67a1e2261f80 |
| 30 | 30 |
github.com/armon/go-metrics eb0af217e5e9747e41dd5303755356b62d28e3ec |
| 31 | 31 |
github.com/hashicorp/go-msgpack 71c2886f5a673a35f909803f38ece5810165097b |
| 32 |
-github.com/hashicorp/memberlist 88ac4de0d1a0ca6def284b571342db3b777a4c37 |
|
| 32 |
+github.com/hashicorp/memberlist v0.1.0 |
|
| 33 |
+github.com/sean-/seed e2103e2c35297fb7e17febb81e49b312087a2372 |
|
| 34 |
+github.com/hashicorp/go-sockaddr acd314c5781ea706c710d9ea70069fd2e110d61d |
|
| 33 | 35 |
github.com/hashicorp/go-multierror fcdddc395df1ddf4247c69bd436e84cfa0733f7e |
| 34 | 36 |
github.com/hashicorp/serf 598c54895cc5a7b1a24a398d635e8c0ea0959870 |
| 35 | 37 |
github.com/docker/libkv 1d8431073ae03cdaedb198a89722f3aab6d418ef |
| 36 | 38 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,373 @@ |
| 0 |
+Mozilla Public License Version 2.0 |
|
| 1 |
+================================== |
|
| 2 |
+ |
|
| 3 |
+1. Definitions |
|
| 4 |
+-------------- |
|
| 5 |
+ |
|
| 6 |
+1.1. "Contributor" |
|
| 7 |
+ means each individual or legal entity that creates, contributes to |
|
| 8 |
+ the creation of, or owns Covered Software. |
|
| 9 |
+ |
|
| 10 |
+1.2. "Contributor Version" |
|
| 11 |
+ means the combination of the Contributions of others (if any) used |
|
| 12 |
+ by a Contributor and that particular Contributor's Contribution. |
|
| 13 |
+ |
|
| 14 |
+1.3. "Contribution" |
|
| 15 |
+ means Covered Software of a particular Contributor. |
|
| 16 |
+ |
|
| 17 |
+1.4. "Covered Software" |
|
| 18 |
+ means Source Code Form to which the initial Contributor has attached |
|
| 19 |
+ the notice in Exhibit A, the Executable Form of such Source Code |
|
| 20 |
+ Form, and Modifications of such Source Code Form, in each case |
|
| 21 |
+ including portions thereof. |
|
| 22 |
+ |
|
| 23 |
+1.5. "Incompatible With Secondary Licenses" |
|
| 24 |
+ means |
|
| 25 |
+ |
|
| 26 |
+ (a) that the initial Contributor has attached the notice described |
|
| 27 |
+ in Exhibit B to the Covered Software; or |
|
| 28 |
+ |
|
| 29 |
+ (b) that the Covered Software was made available under the terms of |
|
| 30 |
+ version 1.1 or earlier of the License, but not also under the |
|
| 31 |
+ terms of a Secondary License. |
|
| 32 |
+ |
|
| 33 |
+1.6. "Executable Form" |
|
| 34 |
+ means any form of the work other than Source Code Form. |
|
| 35 |
+ |
|
| 36 |
+1.7. "Larger Work" |
|
| 37 |
+ means a work that combines Covered Software with other material, in |
|
| 38 |
+ a separate file or files, that is not Covered Software. |
|
| 39 |
+ |
|
| 40 |
+1.8. "License" |
|
| 41 |
+ means this document. |
|
| 42 |
+ |
|
| 43 |
+1.9. "Licensable" |
|
| 44 |
+ means having the right to grant, to the maximum extent possible, |
|
| 45 |
+ whether at the time of the initial grant or subsequently, any and |
|
| 46 |
+ all of the rights conveyed by this License. |
|
| 47 |
+ |
|
| 48 |
+1.10. "Modifications" |
|
| 49 |
+ means any of the following: |
|
| 50 |
+ |
|
| 51 |
+ (a) any file in Source Code Form that results from an addition to, |
|
| 52 |
+ deletion from, or modification of the contents of Covered |
|
| 53 |
+ Software; or |
|
| 54 |
+ |
|
| 55 |
+ (b) any new file in Source Code Form that contains any Covered |
|
| 56 |
+ Software. |
|
| 57 |
+ |
|
| 58 |
+1.11. "Patent Claims" of a Contributor |
|
| 59 |
+ means any patent claim(s), including without limitation, method, |
|
| 60 |
+ process, and apparatus claims, in any patent Licensable by such |
|
| 61 |
+ Contributor that would be infringed, but for the grant of the |
|
| 62 |
+ License, by the making, using, selling, offering for sale, having |
|
| 63 |
+ made, import, or transfer of either its Contributions or its |
|
| 64 |
+ Contributor Version. |
|
| 65 |
+ |
|
| 66 |
+1.12. "Secondary License" |
|
| 67 |
+ means either the GNU General Public License, Version 2.0, the GNU |
|
| 68 |
+ Lesser General Public License, Version 2.1, the GNU Affero General |
|
| 69 |
+ Public License, Version 3.0, or any later versions of those |
|
| 70 |
+ licenses. |
|
| 71 |
+ |
|
| 72 |
+1.13. "Source Code Form" |
|
| 73 |
+ means the form of the work preferred for making modifications. |
|
| 74 |
+ |
|
| 75 |
+1.14. "You" (or "Your") |
|
| 76 |
+ means an individual or a legal entity exercising rights under this |
|
| 77 |
+ License. For legal entities, "You" includes any entity that |
|
| 78 |
+ controls, is controlled by, or is under common control with You. For |
|
| 79 |
+ purposes of this definition, "control" means (a) the power, direct |
|
| 80 |
+ or indirect, to cause the direction or management of such entity, |
|
| 81 |
+ whether by contract or otherwise, or (b) ownership of more than |
|
| 82 |
+ fifty percent (50%) of the outstanding shares or beneficial |
|
| 83 |
+ ownership of such entity. |
|
| 84 |
+ |
|
| 85 |
+2. License Grants and Conditions |
|
| 86 |
+-------------------------------- |
|
| 87 |
+ |
|
| 88 |
+2.1. Grants |
|
| 89 |
+ |
|
| 90 |
+Each Contributor hereby grants You a world-wide, royalty-free, |
|
| 91 |
+non-exclusive license: |
|
| 92 |
+ |
|
| 93 |
+(a) under intellectual property rights (other than patent or trademark) |
|
| 94 |
+ Licensable by such Contributor to use, reproduce, make available, |
|
| 95 |
+ modify, display, perform, distribute, and otherwise exploit its |
|
| 96 |
+ Contributions, either on an unmodified basis, with Modifications, or |
|
| 97 |
+ as part of a Larger Work; and |
|
| 98 |
+ |
|
| 99 |
+(b) under Patent Claims of such Contributor to make, use, sell, offer |
|
| 100 |
+ for sale, have made, import, and otherwise transfer either its |
|
| 101 |
+ Contributions or its Contributor Version. |
|
| 102 |
+ |
|
| 103 |
+2.2. Effective Date |
|
| 104 |
+ |
|
| 105 |
+The licenses granted in Section 2.1 with respect to any Contribution |
|
| 106 |
+become effective for each Contribution on the date the Contributor first |
|
| 107 |
+distributes such Contribution. |
|
| 108 |
+ |
|
| 109 |
+2.3. Limitations on Grant Scope |
|
| 110 |
+ |
|
| 111 |
+The licenses granted in this Section 2 are the only rights granted under |
|
| 112 |
+this License. No additional rights or licenses will be implied from the |
|
| 113 |
+distribution or licensing of Covered Software under this License. |
|
| 114 |
+Notwithstanding Section 2.1(b) above, no patent license is granted by a |
|
| 115 |
+Contributor: |
|
| 116 |
+ |
|
| 117 |
+(a) for any code that a Contributor has removed from Covered Software; |
|
| 118 |
+ or |
|
| 119 |
+ |
|
| 120 |
+(b) for infringements caused by: (i) Your and any other third party's |
|
| 121 |
+ modifications of Covered Software, or (ii) the combination of its |
|
| 122 |
+ Contributions with other software (except as part of its Contributor |
|
| 123 |
+ Version); or |
|
| 124 |
+ |
|
| 125 |
+(c) under Patent Claims infringed by Covered Software in the absence of |
|
| 126 |
+ its Contributions. |
|
| 127 |
+ |
|
| 128 |
+This License does not grant any rights in the trademarks, service marks, |
|
| 129 |
+or logos of any Contributor (except as may be necessary to comply with |
|
| 130 |
+the notice requirements in Section 3.4). |
|
| 131 |
+ |
|
| 132 |
+2.4. Subsequent Licenses |
|
| 133 |
+ |
|
| 134 |
+No Contributor makes additional grants as a result of Your choice to |
|
| 135 |
+distribute the Covered Software under a subsequent version of this |
|
| 136 |
+License (see Section 10.2) or under the terms of a Secondary License (if |
|
| 137 |
+permitted under the terms of Section 3.3). |
|
| 138 |
+ |
|
| 139 |
+2.5. Representation |
|
| 140 |
+ |
|
| 141 |
+Each Contributor represents that the Contributor believes its |
|
| 142 |
+Contributions are its original creation(s) or it has sufficient rights |
|
| 143 |
+to grant the rights to its Contributions conveyed by this License. |
|
| 144 |
+ |
|
| 145 |
+2.6. Fair Use |
|
| 146 |
+ |
|
| 147 |
+This License is not intended to limit any rights You have under |
|
| 148 |
+applicable copyright doctrines of fair use, fair dealing, or other |
|
| 149 |
+equivalents. |
|
| 150 |
+ |
|
| 151 |
+2.7. Conditions |
|
| 152 |
+ |
|
| 153 |
+Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted |
|
| 154 |
+in Section 2.1. |
|
| 155 |
+ |
|
| 156 |
+3. Responsibilities |
|
| 157 |
+------------------- |
|
| 158 |
+ |
|
| 159 |
+3.1. Distribution of Source Form |
|
| 160 |
+ |
|
| 161 |
+All distribution of Covered Software in Source Code Form, including any |
|
| 162 |
+Modifications that You create or to which You contribute, must be under |
|
| 163 |
+the terms of this License. You must inform recipients that the Source |
|
| 164 |
+Code Form of the Covered Software is governed by the terms of this |
|
| 165 |
+License, and how they can obtain a copy of this License. You may not |
|
| 166 |
+attempt to alter or restrict the recipients' rights in the Source Code |
|
| 167 |
+Form. |
|
| 168 |
+ |
|
| 169 |
+3.2. Distribution of Executable Form |
|
| 170 |
+ |
|
| 171 |
+If You distribute Covered Software in Executable Form then: |
|
| 172 |
+ |
|
| 173 |
+(a) such Covered Software must also be made available in Source Code |
|
| 174 |
+ Form, as described in Section 3.1, and You must inform recipients of |
|
| 175 |
+ the Executable Form how they can obtain a copy of such Source Code |
|
| 176 |
+ Form by reasonable means in a timely manner, at a charge no more |
|
| 177 |
+ than the cost of distribution to the recipient; and |
|
| 178 |
+ |
|
| 179 |
+(b) You may distribute such Executable Form under the terms of this |
|
| 180 |
+ License, or sublicense it under different terms, provided that the |
|
| 181 |
+ license for the Executable Form does not attempt to limit or alter |
|
| 182 |
+ the recipients' rights in the Source Code Form under this License. |
|
| 183 |
+ |
|
| 184 |
+3.3. Distribution of a Larger Work |
|
| 185 |
+ |
|
| 186 |
+You may create and distribute a Larger Work under terms of Your choice, |
|
| 187 |
+provided that You also comply with the requirements of this License for |
|
| 188 |
+the Covered Software. If the Larger Work is a combination of Covered |
|
| 189 |
+Software with a work governed by one or more Secondary Licenses, and the |
|
| 190 |
+Covered Software is not Incompatible With Secondary Licenses, this |
|
| 191 |
+License permits You to additionally distribute such Covered Software |
|
| 192 |
+under the terms of such Secondary License(s), so that the recipient of |
|
| 193 |
+the Larger Work may, at their option, further distribute the Covered |
|
| 194 |
+Software under the terms of either this License or such Secondary |
|
| 195 |
+License(s). |
|
| 196 |
+ |
|
| 197 |
+3.4. Notices |
|
| 198 |
+ |
|
| 199 |
+You may not remove or alter the substance of any license notices |
|
| 200 |
+(including copyright notices, patent notices, disclaimers of warranty, |
|
| 201 |
+or limitations of liability) contained within the Source Code Form of |
|
| 202 |
+the Covered Software, except that You may alter any license notices to |
|
| 203 |
+the extent required to remedy known factual inaccuracies. |
|
| 204 |
+ |
|
| 205 |
+3.5. Application of Additional Terms |
|
| 206 |
+ |
|
| 207 |
+You may choose to offer, and to charge a fee for, warranty, support, |
|
| 208 |
+indemnity or liability obligations to one or more recipients of Covered |
|
| 209 |
+Software. However, You may do so only on Your own behalf, and not on |
|
| 210 |
+behalf of any Contributor. You must make it absolutely clear that any |
|
| 211 |
+such warranty, support, indemnity, or liability obligation is offered by |
|
| 212 |
+You alone, and You hereby agree to indemnify every Contributor for any |
|
| 213 |
+liability incurred by such Contributor as a result of warranty, support, |
|
| 214 |
+indemnity or liability terms You offer. You may include additional |
|
| 215 |
+disclaimers of warranty and limitations of liability specific to any |
|
| 216 |
+jurisdiction. |
|
| 217 |
+ |
|
| 218 |
+4. Inability to Comply Due to Statute or Regulation |
|
| 219 |
+--------------------------------------------------- |
|
| 220 |
+ |
|
| 221 |
+If it is impossible for You to comply with any of the terms of this |
|
| 222 |
+License with respect to some or all of the Covered Software due to |
|
| 223 |
+statute, judicial order, or regulation then You must: (a) comply with |
|
| 224 |
+the terms of this License to the maximum extent possible; and (b) |
|
| 225 |
+describe the limitations and the code they affect. Such description must |
|
| 226 |
+be placed in a text file included with all distributions of the Covered |
|
| 227 |
+Software under this License. Except to the extent prohibited by statute |
|
| 228 |
+or regulation, such description must be sufficiently detailed for a |
|
| 229 |
+recipient of ordinary skill to be able to understand it. |
|
| 230 |
+ |
|
| 231 |
+5. Termination |
|
| 232 |
+-------------- |
|
| 233 |
+ |
|
| 234 |
+5.1. The rights granted under this License will terminate automatically |
|
| 235 |
+if You fail to comply with any of its terms. However, if You become |
|
| 236 |
+compliant, then the rights granted under this License from a particular |
|
| 237 |
+Contributor are reinstated (a) provisionally, unless and until such |
|
| 238 |
+Contributor explicitly and finally terminates Your grants, and (b) on an |
|
| 239 |
+ongoing basis, if such Contributor fails to notify You of the |
|
| 240 |
+non-compliance by some reasonable means prior to 60 days after You have |
|
| 241 |
+come back into compliance. Moreover, Your grants from a particular |
|
| 242 |
+Contributor are reinstated on an ongoing basis if such Contributor |
|
| 243 |
+notifies You of the non-compliance by some reasonable means, this is the |
|
| 244 |
+first time You have received notice of non-compliance with this License |
|
| 245 |
+from such Contributor, and You become compliant prior to 30 days after |
|
| 246 |
+Your receipt of the notice. |
|
| 247 |
+ |
|
| 248 |
+5.2. If You initiate litigation against any entity by asserting a patent |
|
| 249 |
+infringement claim (excluding declaratory judgment actions, |
|
| 250 |
+counter-claims, and cross-claims) alleging that a Contributor Version |
|
| 251 |
+directly or indirectly infringes any patent, then the rights granted to |
|
| 252 |
+You by any and all Contributors for the Covered Software under Section |
|
| 253 |
+2.1 of this License shall terminate. |
|
| 254 |
+ |
|
| 255 |
+5.3. In the event of termination under Sections 5.1 or 5.2 above, all |
|
| 256 |
+end user license agreements (excluding distributors and resellers) which |
|
| 257 |
+have been validly granted by You or Your distributors under this License |
|
| 258 |
+prior to termination shall survive termination. |
|
| 259 |
+ |
|
| 260 |
+************************************************************************ |
|
| 261 |
+* * |
|
| 262 |
+* 6. Disclaimer of Warranty * |
|
| 263 |
+* ------------------------- * |
|
| 264 |
+* * |
|
| 265 |
+* Covered Software is provided under this License on an "as is" * |
|
| 266 |
+* basis, without warranty of any kind, either expressed, implied, or * |
|
| 267 |
+* statutory, including, without limitation, warranties that the * |
|
| 268 |
+* Covered Software is free of defects, merchantable, fit for a * |
|
| 269 |
+* particular purpose or non-infringing. The entire risk as to the * |
|
| 270 |
+* quality and performance of the Covered Software is with You. * |
|
| 271 |
+* Should any Covered Software prove defective in any respect, You * |
|
| 272 |
+* (not any Contributor) assume the cost of any necessary servicing, * |
|
| 273 |
+* repair, or correction. This disclaimer of warranty constitutes an * |
|
| 274 |
+* essential part of this License. No use of any Covered Software is * |
|
| 275 |
+* authorized under this License except under this disclaimer. * |
|
| 276 |
+* * |
|
| 277 |
+************************************************************************ |
|
| 278 |
+ |
|
| 279 |
+************************************************************************ |
|
| 280 |
+* * |
|
| 281 |
+* 7. Limitation of Liability * |
|
| 282 |
+* -------------------------- * |
|
| 283 |
+* * |
|
| 284 |
+* Under no circumstances and under no legal theory, whether tort * |
|
| 285 |
+* (including negligence), contract, or otherwise, shall any * |
|
| 286 |
+* Contributor, or anyone who distributes Covered Software as * |
|
| 287 |
+* permitted above, be liable to You for any direct, indirect, * |
|
| 288 |
+* special, incidental, or consequential damages of any character * |
|
| 289 |
+* including, without limitation, damages for lost profits, loss of * |
|
| 290 |
+* goodwill, work stoppage, computer failure or malfunction, or any * |
|
| 291 |
+* and all other commercial damages or losses, even if such party * |
|
| 292 |
+* shall have been informed of the possibility of such damages. This * |
|
| 293 |
+* limitation of liability shall not apply to liability for death or * |
|
| 294 |
+* personal injury resulting from such party's negligence to the * |
|
| 295 |
+* extent applicable law prohibits such limitation. Some * |
|
| 296 |
+* jurisdictions do not allow the exclusion or limitation of * |
|
| 297 |
+* incidental or consequential damages, so this exclusion and * |
|
| 298 |
+* limitation may not apply to You. * |
|
| 299 |
+* * |
|
| 300 |
+************************************************************************ |
|
| 301 |
+ |
|
| 302 |
+8. Litigation |
|
| 303 |
+------------- |
|
| 304 |
+ |
|
| 305 |
+Any litigation relating to this License may be brought only in the |
|
| 306 |
+courts of a jurisdiction where the defendant maintains its principal |
|
| 307 |
+place of business and such litigation shall be governed by laws of that |
|
| 308 |
+jurisdiction, without reference to its conflict-of-law provisions. |
|
| 309 |
+Nothing in this Section shall prevent a party's ability to bring |
|
| 310 |
+cross-claims or counter-claims. |
|
| 311 |
+ |
|
| 312 |
+9. Miscellaneous |
|
| 313 |
+---------------- |
|
| 314 |
+ |
|
| 315 |
+This License represents the complete agreement concerning the subject |
|
| 316 |
+matter hereof. If any provision of this License is held to be |
|
| 317 |
+unenforceable, such provision shall be reformed only to the extent |
|
| 318 |
+necessary to make it enforceable. Any law or regulation which provides |
|
| 319 |
+that the language of a contract shall be construed against the drafter |
|
| 320 |
+shall not be used to construe this License against a Contributor. |
|
| 321 |
+ |
|
| 322 |
+10. Versions of the License |
|
| 323 |
+--------------------------- |
|
| 324 |
+ |
|
| 325 |
+10.1. New Versions |
|
| 326 |
+ |
|
| 327 |
+Mozilla Foundation is the license steward. Except as provided in Section |
|
| 328 |
+10.3, no one other than the license steward has the right to modify or |
|
| 329 |
+publish new versions of this License. Each version will be given a |
|
| 330 |
+distinguishing version number. |
|
| 331 |
+ |
|
| 332 |
+10.2. Effect of New Versions |
|
| 333 |
+ |
|
| 334 |
+You may distribute the Covered Software under the terms of the version |
|
| 335 |
+of the License under which You originally received the Covered Software, |
|
| 336 |
+or under the terms of any subsequent version published by the license |
|
| 337 |
+steward. |
|
| 338 |
+ |
|
| 339 |
+10.3. Modified Versions |
|
| 340 |
+ |
|
| 341 |
+If you create software not governed by this License, and you want to |
|
| 342 |
+create a new license for such software, you may create and use a |
|
| 343 |
+modified version of this License if you rename the license and remove |
|
| 344 |
+any references to the name of the license steward (except to note that |
|
| 345 |
+such modified license differs from this License). |
|
| 346 |
+ |
|
| 347 |
+10.4. Distributing Source Code Form that is Incompatible With Secondary |
|
| 348 |
+Licenses |
|
| 349 |
+ |
|
| 350 |
+If You choose to distribute Source Code Form that is Incompatible With |
|
| 351 |
+Secondary Licenses under the terms of this version of the License, the |
|
| 352 |
+notice described in Exhibit B of this License must be attached. |
|
| 353 |
+ |
|
| 354 |
+Exhibit A - Source Code Form License Notice |
|
| 355 |
+------------------------------------------- |
|
| 356 |
+ |
|
| 357 |
+ This Source Code Form is subject to the terms of the Mozilla Public |
|
| 358 |
+ License, v. 2.0. If a copy of the MPL was not distributed with this |
|
| 359 |
+ file, You can obtain one at http://mozilla.org/MPL/2.0/. |
|
| 360 |
+ |
|
| 361 |
+If it is not possible or desirable to put the notice in a particular |
|
| 362 |
+file, then You may include the notice in a location (such as a LICENSE |
|
| 363 |
+file in a relevant directory) where a recipient would be likely to look |
|
| 364 |
+for such a notice. |
|
| 365 |
+ |
|
| 366 |
+You may add additional accurate notices of copyright ownership. |
|
| 367 |
+ |
|
| 368 |
+Exhibit B - "Incompatible With Secondary Licenses" Notice |
|
| 369 |
+--------------------------------------------------------- |
|
| 370 |
+ |
|
| 371 |
+ This Source Code Form is "Incompatible With Secondary Licenses", as |
|
| 372 |
+ defined by the Mozilla Public License, v. 2.0. |
| 0 | 373 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,118 @@ |
| 0 |
+# go-sockaddr |
|
| 1 |
+ |
|
| 2 |
+## `sockaddr` Library |
|
| 3 |
+ |
|
| 4 |
+Socket address convenience functions for Go. `go-sockaddr` is a convenience |
|
| 5 |
+library that makes doing the right thing with IP addresses easy. `go-sockaddr` |
|
| 6 |
+is loosely modeled after the UNIX `sockaddr_t` and creates a union of the family |
|
| 7 |
+of `sockaddr_t` types (see below for an ascii diagram). Library documentation |
|
| 8 |
+is available |
|
| 9 |
+at |
|
| 10 |
+[https://godoc.org/github.com/hashicorp/go-sockaddr](https://godoc.org/github.com/hashicorp/go-sockaddr). |
|
| 11 |
+The primary intent of the library was to make it possible to define heuristics |
|
| 12 |
+for selecting the correct IP addresses when a configuration is evaluated at |
|
| 13 |
+runtime. See |
|
| 14 |
+the |
|
| 15 |
+[docs](https://godoc.org/github.com/hashicorp/go-sockaddr), |
|
| 16 |
+[`template` package](https://godoc.org/github.com/hashicorp/go-sockaddr/template), |
|
| 17 |
+tests, |
|
| 18 |
+and |
|
| 19 |
+[CLI utility](https://github.com/hashicorp/go-sockaddr/tree/master/cmd/sockaddr) |
|
| 20 |
+for details and hints as to how to use this library. |
|
| 21 |
+ |
|
| 22 |
+For example, with this library it is possible to find an IP address that: |
|
| 23 |
+ |
|
| 24 |
+* is attached to a default route |
|
| 25 |
+ ([`GetDefaultInterfaces()`](https://godoc.org/github.com/hashicorp/go-sockaddr#GetDefaultInterfaces)) |
|
| 26 |
+* is contained within a CIDR block ([`IfByNetwork()`](https://godoc.org/github.com/hashicorp/go-sockaddr#IfByNetwork)) |
|
| 27 |
+* is an RFC1918 address |
|
| 28 |
+ ([`IfByRFC("1918")`](https://godoc.org/github.com/hashicorp/go-sockaddr#IfByRFC))
|
|
| 29 |
+* is ordered |
|
| 30 |
+ ([`OrderedIfAddrBy(args)`](https://godoc.org/github.com/hashicorp/go-sockaddr#OrderedIfAddrBy) where |
|
| 31 |
+ `args` includes, but is not limited |
|
| 32 |
+ to, |
|
| 33 |
+ [`AscIfType`](https://godoc.org/github.com/hashicorp/go-sockaddr#AscIfType), |
|
| 34 |
+ [`AscNetworkSize`](https://godoc.org/github.com/hashicorp/go-sockaddr#AscNetworkSize)) |
|
| 35 |
+* excludes all IPv6 addresses |
|
| 36 |
+ ([`IfByType("^(IPv4)$")`](https://godoc.org/github.com/hashicorp/go-sockaddr#IfByType))
|
|
| 37 |
+* is larger than a `/32` |
|
| 38 |
+ ([`IfByMaskSize(32)`](https://godoc.org/github.com/hashicorp/go-sockaddr#IfByMaskSize)) |
|
| 39 |
+* is not on a `down` interface |
|
| 40 |
+ ([`ExcludeIfs("flags", "down")`](https://godoc.org/github.com/hashicorp/go-sockaddr#ExcludeIfs))
|
|
| 41 |
+* preferences an IPv6 address over an IPv4 address |
|
| 42 |
+ ([`SortIfByType()`](https://godoc.org/github.com/hashicorp/go-sockaddr#SortIfByType) + |
|
| 43 |
+ [`ReverseIfAddrs()`](https://godoc.org/github.com/hashicorp/go-sockaddr#ReverseIfAddrs)); and |
|
| 44 |
+* excludes any IP in RFC6890 address |
|
| 45 |
+ ([`IfByRFC("6890")`](https://godoc.org/github.com/hashicorp/go-sockaddr#IfByRFC))
|
|
| 46 |
+ |
|
| 47 |
+Or any combination or variation therein. |
|
| 48 |
+ |
|
| 49 |
+There are also a few simple helper functions such as `GetPublicIP` and |
|
| 50 |
+`GetPrivateIP` which both return strings and select the first public or private |
|
| 51 |
+IP address on the default interface, respectively. Similarly, there is also a |
|
| 52 |
+helper function called `GetInterfaceIP` which returns the first usable IP |
|
| 53 |
+address on the named interface. |
|
| 54 |
+ |
|
| 55 |
+## `sockaddr` CLI |
|
| 56 |
+ |
|
| 57 |
+Given the possible complexity of the `sockaddr` library, there is a CLI utility |
|
| 58 |
+that accompanies the library, also |
|
| 59 |
+called |
|
| 60 |
+[`sockaddr`](https://github.com/hashicorp/go-sockaddr/tree/master/cmd/sockaddr). |
|
| 61 |
+The |
|
| 62 |
+[`sockaddr`](https://github.com/hashicorp/go-sockaddr/tree/master/cmd/sockaddr) |
|
| 63 |
+utility exposes nearly all of the functionality of the library and can be used |
|
| 64 |
+either as an administrative tool or testing tool. To install |
|
| 65 |
+the |
|
| 66 |
+[`sockaddr`](https://github.com/hashicorp/go-sockaddr/tree/master/cmd/sockaddr), |
|
| 67 |
+run: |
|
| 68 |
+ |
|
| 69 |
+```text |
|
| 70 |
+$ go get -u github.com/hashicorp/go-sockaddr/cmd/sockaddr |
|
| 71 |
+``` |
|
| 72 |
+ |
|
| 73 |
+If you're familiar with UNIX's `sockaddr` struct's, the following diagram |
|
| 74 |
+mapping the C `sockaddr` (top) to `go-sockaddr` structs (bottom) and |
|
| 75 |
+interfaces will be helpful: |
|
| 76 |
+ |
|
| 77 |
+``` |
|
| 78 |
++-------------------------------------------------------+ |
|
| 79 |
+| | |
|
| 80 |
+| sockaddr | |
|
| 81 |
+| SockAddr | |
|
| 82 |
+| | |
|
| 83 |
+| +--------------+ +----------------------------------+ | |
|
| 84 |
+| | sockaddr_un | | | | |
|
| 85 |
+| | SockAddrUnix | | sockaddr_in{,6} | |
|
|
| 86 |
+| +--------------+ | IPAddr | | |
|
| 87 |
+| | | | |
|
| 88 |
+| | +-------------+ +--------------+ | | |
|
| 89 |
+| | | sockaddr_in | | sockaddr_in6 | | | |
|
| 90 |
+| | | IPv4Addr | | IPv6Addr | | | |
|
| 91 |
+| | +-------------+ +--------------+ | | |
|
| 92 |
+| | | | |
|
| 93 |
+| +----------------------------------+ | |
|
| 94 |
+| | |
|
| 95 |
++-------------------------------------------------------+ |
|
| 96 |
+``` |
|
| 97 |
+ |
|
| 98 |
+## Inspiration and Design |
|
| 99 |
+ |
|
| 100 |
+There were many subtle inspirations that led to this design, but the most direct |
|
| 101 |
+inspiration for the filtering syntax was |
|
| 102 |
+OpenBSD's |
|
| 103 |
+[`pf.conf(5)`](https://www.freebsd.org/cgi/man.cgi?query=pf.conf&apropos=0&sektion=0&arch=default&format=html#PARAMETERS) firewall |
|
| 104 |
+syntax that lets you select the first IP address on a given named interface. |
|
| 105 |
+The original problem stemmed from: |
|
| 106 |
+ |
|
| 107 |
+* needing to create immutable images using [Packer](https://www.packer.io) that |
|
| 108 |
+ ran the [Consul](https://www.consul.io) process (Consul can only use one IP |
|
| 109 |
+ address at a time); |
|
| 110 |
+* images that may or may not have multiple interfaces or IP addresses at |
|
| 111 |
+ runtime; and |
|
| 112 |
+* we didn't want to rely on configuration management to render out the correct |
|
| 113 |
+ IP address if the VM image was being used in an auto-scaling group. |
|
| 114 |
+ |
|
| 115 |
+Instead we needed some way to codify a heuristic that would correctly select the |
|
| 116 |
+right IP address but the input parameters were not known when the image was |
|
| 117 |
+created. |
| 0 | 5 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,126 @@ |
| 0 |
+package sockaddr |
|
| 1 |
+ |
|
| 2 |
+// ifAddrAttrMap is a map of the IfAddr type-specific attributes. |
|
| 3 |
+var ifAddrAttrMap map[AttrName]func(IfAddr) string |
|
| 4 |
+var ifAddrAttrs []AttrName |
|
| 5 |
+ |
|
| 6 |
+func init() {
|
|
| 7 |
+ ifAddrAttrInit() |
|
| 8 |
+} |
|
| 9 |
+ |
|
| 10 |
+// GetPrivateIP returns a string with a single IP address that is part of RFC |
|
| 11 |
+// 6890 and has a default route. If the system can't determine its IP address |
|
| 12 |
+// or find an RFC 6890 IP address, an empty string will be returned instead. |
|
| 13 |
+// This function is the `eval` equivalent of: |
|
| 14 |
+// |
|
| 15 |
+// ``` |
|
| 16 |
+// $ sockaddr eval -r '{{GetPrivateInterfaces | attr "address"}}'
|
|
| 17 |
+/// ``` |
|
| 18 |
+func GetPrivateIP() (string, error) {
|
|
| 19 |
+ privateIfs, err := GetPrivateInterfaces() |
|
| 20 |
+ if err != nil {
|
|
| 21 |
+ return "", err |
|
| 22 |
+ } |
|
| 23 |
+ if len(privateIfs) < 1 {
|
|
| 24 |
+ return "", nil |
|
| 25 |
+ } |
|
| 26 |
+ |
|
| 27 |
+ ifAddr := privateIfs[0] |
|
| 28 |
+ ip := *ToIPAddr(ifAddr.SockAddr) |
|
| 29 |
+ return ip.NetIP().String(), nil |
|
| 30 |
+} |
|
| 31 |
+ |
|
| 32 |
+// GetPublicIP returns a string with a single IP address that is NOT part of RFC |
|
| 33 |
+// 6890 and has a default route. If the system can't determine its IP address |
|
| 34 |
+// or find a non RFC 6890 IP address, an empty string will be returned instead. |
|
| 35 |
+// This function is the `eval` equivalent of: |
|
| 36 |
+// |
|
| 37 |
+// ``` |
|
| 38 |
+// $ sockaddr eval -r '{{GetPublicInterfaces | attr "address"}}'
|
|
| 39 |
+/// ``` |
|
| 40 |
+func GetPublicIP() (string, error) {
|
|
| 41 |
+ publicIfs, err := GetPublicInterfaces() |
|
| 42 |
+ if err != nil {
|
|
| 43 |
+ return "", err |
|
| 44 |
+ } else if len(publicIfs) < 1 {
|
|
| 45 |
+ return "", nil |
|
| 46 |
+ } |
|
| 47 |
+ |
|
| 48 |
+ ifAddr := publicIfs[0] |
|
| 49 |
+ ip := *ToIPAddr(ifAddr.SockAddr) |
|
| 50 |
+ return ip.NetIP().String(), nil |
|
| 51 |
+} |
|
| 52 |
+ |
|
| 53 |
+// GetInterfaceIP returns a string with a single IP address sorted by the size |
|
| 54 |
+// of the network (i.e. IP addresses with a smaller netmask, larger network |
|
| 55 |
+// size, are sorted first). This function is the `eval` equivalent of: |
|
| 56 |
+// |
|
| 57 |
+// ``` |
|
| 58 |
+// $ sockaddr eval -r '{{GetAllInterfaces | include "name" <<ARG>> | sort "type,size" | include "flag" "forwardable" | attr "address" }}'
|
|
| 59 |
+/// ``` |
|
| 60 |
+func GetInterfaceIP(namedIfRE string) (string, error) {
|
|
| 61 |
+ ifAddrs, err := GetAllInterfaces() |
|
| 62 |
+ if err != nil {
|
|
| 63 |
+ return "", err |
|
| 64 |
+ } |
|
| 65 |
+ |
|
| 66 |
+ ifAddrs, _, err = IfByName(namedIfRE, ifAddrs) |
|
| 67 |
+ if err != nil {
|
|
| 68 |
+ return "", err |
|
| 69 |
+ } |
|
| 70 |
+ |
|
| 71 |
+ ifAddrs, _, err = IfByFlag("forwardable", ifAddrs)
|
|
| 72 |
+ if err != nil {
|
|
| 73 |
+ return "", err |
|
| 74 |
+ } |
|
| 75 |
+ |
|
| 76 |
+ ifAddrs, err = SortIfBy("+type,+size", ifAddrs)
|
|
| 77 |
+ if err != nil {
|
|
| 78 |
+ return "", err |
|
| 79 |
+ } |
|
| 80 |
+ |
|
| 81 |
+ if len(ifAddrs) == 0 {
|
|
| 82 |
+ return "", err |
|
| 83 |
+ } |
|
| 84 |
+ |
|
| 85 |
+ ip := ToIPAddr(ifAddrs[0].SockAddr) |
|
| 86 |
+ if ip == nil {
|
|
| 87 |
+ return "", err |
|
| 88 |
+ } |
|
| 89 |
+ |
|
| 90 |
+ return IPAddrAttr(*ip, "address"), nil |
|
| 91 |
+} |
|
| 92 |
+ |
|
| 93 |
+// IfAddrAttrs returns a list of attributes supported by the IfAddr type |
|
| 94 |
+func IfAddrAttrs() []AttrName {
|
|
| 95 |
+ return ifAddrAttrs |
|
| 96 |
+} |
|
| 97 |
+ |
|
| 98 |
+// IfAddrAttr returns a string representation of an attribute for the given |
|
| 99 |
+// IfAddr. |
|
| 100 |
+func IfAddrAttr(ifAddr IfAddr, attrName AttrName) string {
|
|
| 101 |
+ fn, found := ifAddrAttrMap[attrName] |
|
| 102 |
+ if !found {
|
|
| 103 |
+ return "" |
|
| 104 |
+ } |
|
| 105 |
+ |
|
| 106 |
+ return fn(ifAddr) |
|
| 107 |
+} |
|
| 108 |
+ |
|
| 109 |
+// ifAddrAttrInit is called once at init() |
|
| 110 |
+func ifAddrAttrInit() {
|
|
| 111 |
+ // Sorted for human readability |
|
| 112 |
+ ifAddrAttrs = []AttrName{
|
|
| 113 |
+ "flags", |
|
| 114 |
+ "name", |
|
| 115 |
+ } |
|
| 116 |
+ |
|
| 117 |
+ ifAddrAttrMap = map[AttrName]func(ifAddr IfAddr) string{
|
|
| 118 |
+ "flags": func(ifAddr IfAddr) string {
|
|
| 119 |
+ return ifAddr.Interface.Flags.String() |
|
| 120 |
+ }, |
|
| 121 |
+ "name": func(ifAddr IfAddr) string {
|
|
| 122 |
+ return ifAddr.Interface.Name |
|
| 123 |
+ }, |
|
| 124 |
+ } |
|
| 125 |
+} |
| 0 | 126 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,969 @@ |
| 0 |
+package sockaddr |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "errors" |
|
| 4 |
+ "fmt" |
|
| 5 |
+ "net" |
|
| 6 |
+ "regexp" |
|
| 7 |
+ "sort" |
|
| 8 |
+ "strconv" |
|
| 9 |
+ "strings" |
|
| 10 |
+) |
|
| 11 |
+ |
|
| 12 |
+// IfAddrs is a slice of IfAddr |
|
| 13 |
+type IfAddrs []IfAddr |
|
| 14 |
+ |
|
| 15 |
+func (ifs IfAddrs) Len() int { return len(ifs) }
|
|
| 16 |
+ |
|
| 17 |
+// CmpIfFunc is the function signature that must be met to be used in the |
|
| 18 |
+// OrderedIfAddrBy multiIfAddrSorter |
|
| 19 |
+type CmpIfAddrFunc func(p1, p2 *IfAddr) int |
|
| 20 |
+ |
|
| 21 |
+// multiIfAddrSorter implements the Sort interface, sorting the IfAddrs within. |
|
| 22 |
+type multiIfAddrSorter struct {
|
|
| 23 |
+ ifAddrs IfAddrs |
|
| 24 |
+ cmp []CmpIfAddrFunc |
|
| 25 |
+} |
|
| 26 |
+ |
|
| 27 |
+// Sort sorts the argument slice according to the Cmp functions passed to |
|
| 28 |
+// OrderedIfAddrBy. |
|
| 29 |
+func (ms *multiIfAddrSorter) Sort(ifAddrs IfAddrs) {
|
|
| 30 |
+ ms.ifAddrs = ifAddrs |
|
| 31 |
+ sort.Sort(ms) |
|
| 32 |
+} |
|
| 33 |
+ |
|
| 34 |
+// OrderedIfAddrBy sorts SockAddr by the list of sort function pointers. |
|
| 35 |
+func OrderedIfAddrBy(cmpFuncs ...CmpIfAddrFunc) *multiIfAddrSorter {
|
|
| 36 |
+ return &multiIfAddrSorter{
|
|
| 37 |
+ cmp: cmpFuncs, |
|
| 38 |
+ } |
|
| 39 |
+} |
|
| 40 |
+ |
|
| 41 |
+// Len is part of sort.Interface. |
|
| 42 |
+func (ms *multiIfAddrSorter) Len() int {
|
|
| 43 |
+ return len(ms.ifAddrs) |
|
| 44 |
+} |
|
| 45 |
+ |
|
| 46 |
+// Less is part of sort.Interface. It is implemented by looping along the Cmp() |
|
| 47 |
+// functions until it finds a comparison that is either less than or greater |
|
| 48 |
+// than. A return value of 0 defers sorting to the next function in the |
|
| 49 |
+// multisorter (which means the results of sorting may leave the resutls in a |
|
| 50 |
+// non-deterministic order). |
|
| 51 |
+func (ms *multiIfAddrSorter) Less(i, j int) bool {
|
|
| 52 |
+ p, q := &ms.ifAddrs[i], &ms.ifAddrs[j] |
|
| 53 |
+ // Try all but the last comparison. |
|
| 54 |
+ var k int |
|
| 55 |
+ for k = 0; k < len(ms.cmp)-1; k++ {
|
|
| 56 |
+ cmp := ms.cmp[k] |
|
| 57 |
+ x := cmp(p, q) |
|
| 58 |
+ switch x {
|
|
| 59 |
+ case -1: |
|
| 60 |
+ // p < q, so we have a decision. |
|
| 61 |
+ return true |
|
| 62 |
+ case 1: |
|
| 63 |
+ // p > q, so we have a decision. |
|
| 64 |
+ return false |
|
| 65 |
+ } |
|
| 66 |
+ // p == q; try the next comparison. |
|
| 67 |
+ } |
|
| 68 |
+ // All comparisons to here said "equal", so just return whatever the |
|
| 69 |
+ // final comparison reports. |
|
| 70 |
+ switch ms.cmp[k](p, q) {
|
|
| 71 |
+ case -1: |
|
| 72 |
+ return true |
|
| 73 |
+ case 1: |
|
| 74 |
+ return false |
|
| 75 |
+ default: |
|
| 76 |
+ // Still a tie! Now what? |
|
| 77 |
+ return false |
|
| 78 |
+ panic("undefined sort order for remaining items in the list")
|
|
| 79 |
+ } |
|
| 80 |
+} |
|
| 81 |
+ |
|
| 82 |
+// Swap is part of sort.Interface. |
|
| 83 |
+func (ms *multiIfAddrSorter) Swap(i, j int) {
|
|
| 84 |
+ ms.ifAddrs[i], ms.ifAddrs[j] = ms.ifAddrs[j], ms.ifAddrs[i] |
|
| 85 |
+} |
|
| 86 |
+ |
|
| 87 |
+// AscIfAddress is a sorting function to sort IfAddrs by their respective |
|
| 88 |
+// address type. Non-equal types are deferred in the sort. |
|
| 89 |
+func AscIfAddress(p1Ptr, p2Ptr *IfAddr) int {
|
|
| 90 |
+ return AscAddress(&p1Ptr.SockAddr, &p2Ptr.SockAddr) |
|
| 91 |
+} |
|
| 92 |
+ |
|
| 93 |
+// AscIfName is a sorting function to sort IfAddrs by their interface names. |
|
| 94 |
+func AscIfName(p1Ptr, p2Ptr *IfAddr) int {
|
|
| 95 |
+ return strings.Compare(p1Ptr.Name, p2Ptr.Name) |
|
| 96 |
+} |
|
| 97 |
+ |
|
| 98 |
+// AscIfNetworkSize is a sorting function to sort IfAddrs by their respective |
|
| 99 |
+// network mask size. |
|
| 100 |
+func AscIfNetworkSize(p1Ptr, p2Ptr *IfAddr) int {
|
|
| 101 |
+ return AscNetworkSize(&p1Ptr.SockAddr, &p2Ptr.SockAddr) |
|
| 102 |
+} |
|
| 103 |
+ |
|
| 104 |
+// AscIfPort is a sorting function to sort IfAddrs by their respective |
|
| 105 |
+// port type. Non-equal types are deferred in the sort. |
|
| 106 |
+func AscIfPort(p1Ptr, p2Ptr *IfAddr) int {
|
|
| 107 |
+ return AscPort(&p1Ptr.SockAddr, &p2Ptr.SockAddr) |
|
| 108 |
+} |
|
| 109 |
+ |
|
| 110 |
+// AscIfPrivate is a sorting function to sort IfAddrs by "private" values before |
|
| 111 |
+// "public" values. Both IPv4 and IPv6 are compared against RFC6890 (RFC6890 |
|
| 112 |
+// includes, and is not limited to, RFC1918 and RFC6598 for IPv4, and IPv6 |
|
| 113 |
+// includes RFC4193). |
|
| 114 |
+func AscIfPrivate(p1Ptr, p2Ptr *IfAddr) int {
|
|
| 115 |
+ return AscPrivate(&p1Ptr.SockAddr, &p2Ptr.SockAddr) |
|
| 116 |
+} |
|
| 117 |
+ |
|
| 118 |
+// AscIfType is a sorting function to sort IfAddrs by their respective address |
|
| 119 |
+// type. Non-equal types are deferred in the sort. |
|
| 120 |
+func AscIfType(p1Ptr, p2Ptr *IfAddr) int {
|
|
| 121 |
+ return AscType(&p1Ptr.SockAddr, &p2Ptr.SockAddr) |
|
| 122 |
+} |
|
| 123 |
+ |
|
| 124 |
+// DescIfAddress is identical to AscIfAddress but reverse ordered. |
|
| 125 |
+func DescIfAddress(p1Ptr, p2Ptr *IfAddr) int {
|
|
| 126 |
+ return -1 * AscAddress(&p1Ptr.SockAddr, &p2Ptr.SockAddr) |
|
| 127 |
+} |
|
| 128 |
+ |
|
| 129 |
+// DescIfName is identical to AscIfName but reverse ordered. |
|
| 130 |
+func DescIfName(p1Ptr, p2Ptr *IfAddr) int {
|
|
| 131 |
+ return -1 * strings.Compare(p1Ptr.Name, p2Ptr.Name) |
|
| 132 |
+} |
|
| 133 |
+ |
|
| 134 |
+// DescIfNetworkSize is identical to AscIfNetworkSize but reverse ordered. |
|
| 135 |
+func DescIfNetworkSize(p1Ptr, p2Ptr *IfAddr) int {
|
|
| 136 |
+ return -1 * AscNetworkSize(&p1Ptr.SockAddr, &p2Ptr.SockAddr) |
|
| 137 |
+} |
|
| 138 |
+ |
|
| 139 |
+// DescIfPort is identical to AscIfPort but reverse ordered. |
|
| 140 |
+func DescIfPort(p1Ptr, p2Ptr *IfAddr) int {
|
|
| 141 |
+ return -1 * AscPort(&p1Ptr.SockAddr, &p2Ptr.SockAddr) |
|
| 142 |
+} |
|
| 143 |
+ |
|
| 144 |
+// DescIfPrivate is identical to AscIfPrivate but reverse ordered. |
|
| 145 |
+func DescIfPrivate(p1Ptr, p2Ptr *IfAddr) int {
|
|
| 146 |
+ return -1 * AscPrivate(&p1Ptr.SockAddr, &p2Ptr.SockAddr) |
|
| 147 |
+} |
|
| 148 |
+ |
|
| 149 |
+// DescIfType is identical to AscIfType but reverse ordered. |
|
| 150 |
+func DescIfType(p1Ptr, p2Ptr *IfAddr) int {
|
|
| 151 |
+ return -1 * AscType(&p1Ptr.SockAddr, &p2Ptr.SockAddr) |
|
| 152 |
+} |
|
| 153 |
+ |
|
| 154 |
+// FilterIfByType filters IfAddrs and returns a list of the matching type |
|
| 155 |
+func FilterIfByType(ifAddrs IfAddrs, type_ SockAddrType) (matchedIfs, excludedIfs IfAddrs) {
|
|
| 156 |
+ excludedIfs = make(IfAddrs, 0, len(ifAddrs)) |
|
| 157 |
+ matchedIfs = make(IfAddrs, 0, len(ifAddrs)) |
|
| 158 |
+ |
|
| 159 |
+ for _, ifAddr := range ifAddrs {
|
|
| 160 |
+ if ifAddr.SockAddr.Type()&type_ != 0 {
|
|
| 161 |
+ matchedIfs = append(matchedIfs, ifAddr) |
|
| 162 |
+ } else {
|
|
| 163 |
+ excludedIfs = append(excludedIfs, ifAddr) |
|
| 164 |
+ } |
|
| 165 |
+ } |
|
| 166 |
+ return matchedIfs, excludedIfs |
|
| 167 |
+} |
|
| 168 |
+ |
|
| 169 |
+// IfAttr forwards the selector to IfAttr.Attr() for resolution. If there is |
|
| 170 |
+// more than one IfAddr, only the first IfAddr is used. |
|
| 171 |
+func IfAttr(selectorName string, ifAddrs IfAddrs) (string, error) {
|
|
| 172 |
+ if len(ifAddrs) == 0 {
|
|
| 173 |
+ return "", nil |
|
| 174 |
+ } |
|
| 175 |
+ |
|
| 176 |
+ attrName := AttrName(strings.ToLower(selectorName)) |
|
| 177 |
+ attrVal, err := ifAddrs[0].Attr(attrName) |
|
| 178 |
+ return attrVal, err |
|
| 179 |
+} |
|
| 180 |
+ |
|
| 181 |
+// GetAllInterfaces iterates over all available network interfaces and finds all |
|
| 182 |
+// available IP addresses on each interface and converts them to |
|
| 183 |
+// sockaddr.IPAddrs, and returning the result as an array of IfAddr. |
|
| 184 |
+func GetAllInterfaces() (IfAddrs, error) {
|
|
| 185 |
+ ifs, err := net.Interfaces() |
|
| 186 |
+ if err != nil {
|
|
| 187 |
+ return nil, err |
|
| 188 |
+ } |
|
| 189 |
+ |
|
| 190 |
+ ifAddrs := make(IfAddrs, 0, len(ifs)) |
|
| 191 |
+ for _, intf := range ifs {
|
|
| 192 |
+ addrs, err := intf.Addrs() |
|
| 193 |
+ if err != nil {
|
|
| 194 |
+ return nil, err |
|
| 195 |
+ } |
|
| 196 |
+ |
|
| 197 |
+ for _, addr := range addrs {
|
|
| 198 |
+ var ipAddr IPAddr |
|
| 199 |
+ ipAddr, err = NewIPAddr(addr.String()) |
|
| 200 |
+ if err != nil {
|
|
| 201 |
+ return IfAddrs{}, fmt.Errorf("unable to create an IP address from %q", addr.String())
|
|
| 202 |
+ } |
|
| 203 |
+ |
|
| 204 |
+ ifAddr := IfAddr{
|
|
| 205 |
+ SockAddr: ipAddr, |
|
| 206 |
+ Interface: intf, |
|
| 207 |
+ } |
|
| 208 |
+ ifAddrs = append(ifAddrs, ifAddr) |
|
| 209 |
+ } |
|
| 210 |
+ } |
|
| 211 |
+ |
|
| 212 |
+ return ifAddrs, nil |
|
| 213 |
+} |
|
| 214 |
+ |
|
| 215 |
+// GetDefaultInterfaces returns IfAddrs of the addresses attached to the default |
|
| 216 |
+// route. |
|
| 217 |
+func GetDefaultInterfaces() (IfAddrs, error) {
|
|
| 218 |
+ ri, err := NewRouteInfo() |
|
| 219 |
+ if err != nil {
|
|
| 220 |
+ return nil, err |
|
| 221 |
+ } |
|
| 222 |
+ |
|
| 223 |
+ defaultIfName, err := ri.GetDefaultInterfaceName() |
|
| 224 |
+ if err != nil {
|
|
| 225 |
+ return nil, err |
|
| 226 |
+ } |
|
| 227 |
+ |
|
| 228 |
+ var defaultIfs, ifAddrs IfAddrs |
|
| 229 |
+ ifAddrs, err = GetAllInterfaces() |
|
| 230 |
+ for _, ifAddr := range ifAddrs {
|
|
| 231 |
+ if ifAddr.Name == defaultIfName {
|
|
| 232 |
+ defaultIfs = append(defaultIfs, ifAddr) |
|
| 233 |
+ } |
|
| 234 |
+ } |
|
| 235 |
+ |
|
| 236 |
+ return defaultIfs, nil |
|
| 237 |
+} |
|
| 238 |
+ |
|
| 239 |
+// GetPrivateInterfaces returns an IfAddrs that are part of RFC 6890 and have a |
|
| 240 |
+// default route. If the system can't determine its IP address or find an RFC |
|
| 241 |
+// 6890 IP address, an empty IfAddrs will be returned instead. This function is |
|
| 242 |
+// the `eval` equivalent of: |
|
| 243 |
+// |
|
| 244 |
+// ``` |
|
| 245 |
+// $ sockaddr eval -r '{{GetDefaultInterfaces | include "type" "ip" | include "flags" "forwardable|up" | sort "type,size" | include "RFC" "6890" }}'
|
|
| 246 |
+/// ``` |
|
| 247 |
+func GetPrivateInterfaces() (IfAddrs, error) {
|
|
| 248 |
+ privateIfs, err := GetDefaultInterfaces() |
|
| 249 |
+ if err != nil {
|
|
| 250 |
+ return IfAddrs{}, err
|
|
| 251 |
+ } |
|
| 252 |
+ if len(privateIfs) == 0 {
|
|
| 253 |
+ return IfAddrs{}, nil
|
|
| 254 |
+ } |
|
| 255 |
+ |
|
| 256 |
+ privateIfs, _ = FilterIfByType(privateIfs, TypeIP) |
|
| 257 |
+ if len(privateIfs) == 0 {
|
|
| 258 |
+ return IfAddrs{}, nil
|
|
| 259 |
+ } |
|
| 260 |
+ |
|
| 261 |
+ privateIfs, _, err = IfByFlag("forwardable|up", privateIfs)
|
|
| 262 |
+ if err != nil {
|
|
| 263 |
+ return IfAddrs{}, err
|
|
| 264 |
+ } |
|
| 265 |
+ if len(privateIfs) == 0 {
|
|
| 266 |
+ return IfAddrs{}, nil
|
|
| 267 |
+ } |
|
| 268 |
+ |
|
| 269 |
+ OrderedIfAddrBy(AscIfType, AscIfNetworkSize).Sort(privateIfs) |
|
| 270 |
+ |
|
| 271 |
+ privateIfs, _, err = IfByRFC("6890", privateIfs)
|
|
| 272 |
+ if err != nil {
|
|
| 273 |
+ return IfAddrs{}, err
|
|
| 274 |
+ } else if len(privateIfs) == 0 {
|
|
| 275 |
+ return IfAddrs{}, nil
|
|
| 276 |
+ } |
|
| 277 |
+ |
|
| 278 |
+ return privateIfs, nil |
|
| 279 |
+} |
|
| 280 |
+ |
|
| 281 |
+// GetPublicInterfaces returns an IfAddrs that are NOT part of RFC 6890 and has a |
|
| 282 |
+// default route. If the system can't determine its IP address or find a non |
|
| 283 |
+// RFC 6890 IP address, an empty IfAddrs will be returned instead. This |
|
| 284 |
+// function is the `eval` equivalent of: |
|
| 285 |
+// |
|
| 286 |
+// ``` |
|
| 287 |
+// $ sockaddr eval -r '{{GetDefaultInterfaces | include "type" "ip" | include "flags" "forwardable|up" | sort "type,size" | exclude "RFC" "6890" }}'
|
|
| 288 |
+/// ``` |
|
| 289 |
+func GetPublicInterfaces() (IfAddrs, error) {
|
|
| 290 |
+ publicIfs, err := GetDefaultInterfaces() |
|
| 291 |
+ if err != nil {
|
|
| 292 |
+ return IfAddrs{}, err
|
|
| 293 |
+ } |
|
| 294 |
+ if len(publicIfs) == 0 {
|
|
| 295 |
+ return IfAddrs{}, nil
|
|
| 296 |
+ } |
|
| 297 |
+ |
|
| 298 |
+ publicIfs, _ = FilterIfByType(publicIfs, TypeIP) |
|
| 299 |
+ if len(publicIfs) == 0 {
|
|
| 300 |
+ return IfAddrs{}, nil
|
|
| 301 |
+ } |
|
| 302 |
+ |
|
| 303 |
+ publicIfs, _, err = IfByFlag("forwardable|up", publicIfs)
|
|
| 304 |
+ if err != nil {
|
|
| 305 |
+ return IfAddrs{}, err
|
|
| 306 |
+ } |
|
| 307 |
+ if len(publicIfs) == 0 {
|
|
| 308 |
+ return IfAddrs{}, nil
|
|
| 309 |
+ } |
|
| 310 |
+ |
|
| 311 |
+ OrderedIfAddrBy(AscIfType, AscIfNetworkSize).Sort(publicIfs) |
|
| 312 |
+ |
|
| 313 |
+ _, publicIfs, err = IfByRFC("6890", publicIfs)
|
|
| 314 |
+ if err != nil {
|
|
| 315 |
+ return IfAddrs{}, err
|
|
| 316 |
+ } else if len(publicIfs) == 0 {
|
|
| 317 |
+ return IfAddrs{}, nil
|
|
| 318 |
+ } |
|
| 319 |
+ |
|
| 320 |
+ return publicIfs, nil |
|
| 321 |
+} |
|
| 322 |
+ |
|
| 323 |
+// IfByAddress returns a list of matched and non-matched IfAddrs, or an error if |
|
| 324 |
+// the regexp fails to compile. |
|
| 325 |
+func IfByAddress(inputRe string, ifAddrs IfAddrs) (matched, remainder IfAddrs, err error) {
|
|
| 326 |
+ re, err := regexp.Compile(inputRe) |
|
| 327 |
+ if err != nil {
|
|
| 328 |
+ return nil, nil, fmt.Errorf("Unable to compile address regexp %+q: %v", inputRe, err)
|
|
| 329 |
+ } |
|
| 330 |
+ |
|
| 331 |
+ matchedAddrs := make(IfAddrs, 0, len(ifAddrs)) |
|
| 332 |
+ excludedAddrs := make(IfAddrs, 0, len(ifAddrs)) |
|
| 333 |
+ for _, addr := range ifAddrs {
|
|
| 334 |
+ if re.MatchString(addr.SockAddr.String()) {
|
|
| 335 |
+ matchedAddrs = append(matchedAddrs, addr) |
|
| 336 |
+ } else {
|
|
| 337 |
+ excludedAddrs = append(excludedAddrs, addr) |
|
| 338 |
+ } |
|
| 339 |
+ } |
|
| 340 |
+ |
|
| 341 |
+ return matchedAddrs, excludedAddrs, nil |
|
| 342 |
+} |
|
| 343 |
+ |
|
| 344 |
+// IfByName returns a list of matched and non-matched IfAddrs, or an error if |
|
| 345 |
+// the regexp fails to compile. |
|
| 346 |
+func IfByName(inputRe string, ifAddrs IfAddrs) (matched, remainder IfAddrs, err error) {
|
|
| 347 |
+ re, err := regexp.Compile(inputRe) |
|
| 348 |
+ if err != nil {
|
|
| 349 |
+ return nil, nil, fmt.Errorf("Unable to compile name regexp %+q: %v", inputRe, err)
|
|
| 350 |
+ } |
|
| 351 |
+ |
|
| 352 |
+ matchedAddrs := make(IfAddrs, 0, len(ifAddrs)) |
|
| 353 |
+ excludedAddrs := make(IfAddrs, 0, len(ifAddrs)) |
|
| 354 |
+ for _, addr := range ifAddrs {
|
|
| 355 |
+ if re.MatchString(addr.Name) {
|
|
| 356 |
+ matchedAddrs = append(matchedAddrs, addr) |
|
| 357 |
+ } else {
|
|
| 358 |
+ excludedAddrs = append(excludedAddrs, addr) |
|
| 359 |
+ } |
|
| 360 |
+ } |
|
| 361 |
+ |
|
| 362 |
+ return matchedAddrs, excludedAddrs, nil |
|
| 363 |
+} |
|
| 364 |
+ |
|
| 365 |
+// IfByPort returns a list of matched and non-matched IfAddrs, or an error if |
|
| 366 |
+// the regexp fails to compile. |
|
| 367 |
+func IfByPort(inputRe string, ifAddrs IfAddrs) (matchedIfs, excludedIfs IfAddrs, err error) {
|
|
| 368 |
+ re, err := regexp.Compile(inputRe) |
|
| 369 |
+ if err != nil {
|
|
| 370 |
+ return nil, nil, fmt.Errorf("Unable to compile port regexp %+q: %v", inputRe, err)
|
|
| 371 |
+ } |
|
| 372 |
+ |
|
| 373 |
+ ipIfs, nonIfs := FilterIfByType(ifAddrs, TypeIP) |
|
| 374 |
+ matchedIfs = make(IfAddrs, 0, len(ipIfs)) |
|
| 375 |
+ excludedIfs = append(IfAddrs(nil), nonIfs...) |
|
| 376 |
+ for _, addr := range ipIfs {
|
|
| 377 |
+ ipAddr := ToIPAddr(addr.SockAddr) |
|
| 378 |
+ if ipAddr == nil {
|
|
| 379 |
+ continue |
|
| 380 |
+ } |
|
| 381 |
+ |
|
| 382 |
+ port := strconv.FormatInt(int64((*ipAddr).IPPort()), 10) |
|
| 383 |
+ if re.MatchString(port) {
|
|
| 384 |
+ matchedIfs = append(matchedIfs, addr) |
|
| 385 |
+ } else {
|
|
| 386 |
+ excludedIfs = append(excludedIfs, addr) |
|
| 387 |
+ } |
|
| 388 |
+ } |
|
| 389 |
+ |
|
| 390 |
+ return matchedIfs, excludedIfs, nil |
|
| 391 |
+} |
|
| 392 |
+ |
|
| 393 |
+// IfByRFC returns a list of matched and non-matched IfAddrs that contain the |
|
| 394 |
+// relevant RFC-specified traits. |
|
| 395 |
+func IfByRFC(selectorParam string, ifAddrs IfAddrs) (matched, remainder IfAddrs, err error) {
|
|
| 396 |
+ inputRFC, err := strconv.ParseUint(selectorParam, 10, 64) |
|
| 397 |
+ if err != nil {
|
|
| 398 |
+ return IfAddrs{}, IfAddrs{}, fmt.Errorf("unable to parse RFC number %q: %v", selectorParam, err)
|
|
| 399 |
+ } |
|
| 400 |
+ |
|
| 401 |
+ matchedIfAddrs := make(IfAddrs, 0, len(ifAddrs)) |
|
| 402 |
+ remainingIfAddrs := make(IfAddrs, 0, len(ifAddrs)) |
|
| 403 |
+ |
|
| 404 |
+ rfcNetMap := KnownRFCs() |
|
| 405 |
+ rfcNets, ok := rfcNetMap[uint(inputRFC)] |
|
| 406 |
+ if !ok {
|
|
| 407 |
+ return nil, nil, fmt.Errorf("unsupported RFC %d", inputRFC)
|
|
| 408 |
+ } |
|
| 409 |
+ |
|
| 410 |
+ for _, ifAddr := range ifAddrs {
|
|
| 411 |
+ var contained bool |
|
| 412 |
+ for _, rfcNet := range rfcNets {
|
|
| 413 |
+ if rfcNet.Contains(ifAddr.SockAddr) {
|
|
| 414 |
+ matchedIfAddrs = append(matchedIfAddrs, ifAddr) |
|
| 415 |
+ contained = true |
|
| 416 |
+ break |
|
| 417 |
+ } |
|
| 418 |
+ } |
|
| 419 |
+ if !contained {
|
|
| 420 |
+ remainingIfAddrs = append(remainingIfAddrs, ifAddr) |
|
| 421 |
+ } |
|
| 422 |
+ } |
|
| 423 |
+ |
|
| 424 |
+ return matchedIfAddrs, remainingIfAddrs, nil |
|
| 425 |
+} |
|
| 426 |
+ |
|
| 427 |
+// IfByRFCs returns a list of matched and non-matched IfAddrs that contain the |
|
| 428 |
+// relevant RFC-specified traits. Multiple RFCs can be specified and separated |
|
| 429 |
+// by the `|` symbol. No protection is taken to ensure an IfAddr does not end |
|
| 430 |
+// up in both the included and excluded list. |
|
| 431 |
+func IfByRFCs(selectorParam string, ifAddrs IfAddrs) (matched, remainder IfAddrs, err error) {
|
|
| 432 |
+ var includedIfs, excludedIfs IfAddrs |
|
| 433 |
+ for _, rfcStr := range strings.Split(selectorParam, "|") {
|
|
| 434 |
+ includedRFCIfs, excludedRFCIfs, err := IfByRFC(rfcStr, ifAddrs) |
|
| 435 |
+ if err != nil {
|
|
| 436 |
+ return IfAddrs{}, IfAddrs{}, fmt.Errorf("unable to lookup RFC number %q: %v", rfcStr, err)
|
|
| 437 |
+ } |
|
| 438 |
+ includedIfs = append(includedIfs, includedRFCIfs...) |
|
| 439 |
+ excludedIfs = append(excludedIfs, excludedRFCIfs...) |
|
| 440 |
+ } |
|
| 441 |
+ |
|
| 442 |
+ return includedIfs, excludedIfs, nil |
|
| 443 |
+} |
|
| 444 |
+ |
|
| 445 |
+// IfByMaskSize returns a list of matched and non-matched IfAddrs that have the |
|
| 446 |
+// matching mask size. |
|
| 447 |
+func IfByMaskSize(selectorParam string, ifAddrs IfAddrs) (matchedIfs, excludedIfs IfAddrs, err error) {
|
|
| 448 |
+ maskSize, err := strconv.ParseUint(selectorParam, 10, 64) |
|
| 449 |
+ if err != nil {
|
|
| 450 |
+ return IfAddrs{}, IfAddrs{}, fmt.Errorf("invalid exclude size argument (%q): %v", selectorParam, err)
|
|
| 451 |
+ } |
|
| 452 |
+ |
|
| 453 |
+ ipIfs, nonIfs := FilterIfByType(ifAddrs, TypeIP) |
|
| 454 |
+ matchedIfs = make(IfAddrs, 0, len(ipIfs)) |
|
| 455 |
+ excludedIfs = append(IfAddrs(nil), nonIfs...) |
|
| 456 |
+ for _, addr := range ipIfs {
|
|
| 457 |
+ ipAddr := ToIPAddr(addr.SockAddr) |
|
| 458 |
+ if ipAddr == nil {
|
|
| 459 |
+ return IfAddrs{}, IfAddrs{}, fmt.Errorf("unable to filter mask sizes on non-IP type %s: %v", addr.SockAddr.Type().String(), addr.SockAddr.String())
|
|
| 460 |
+ } |
|
| 461 |
+ |
|
| 462 |
+ switch {
|
|
| 463 |
+ case (*ipAddr).Type()&TypeIPv4 != 0 && maskSize > 32: |
|
| 464 |
+ return IfAddrs{}, IfAddrs{}, fmt.Errorf("mask size out of bounds for IPv4 address: %d", maskSize)
|
|
| 465 |
+ case (*ipAddr).Type()&TypeIPv6 != 0 && maskSize > 128: |
|
| 466 |
+ return IfAddrs{}, IfAddrs{}, fmt.Errorf("mask size out of bounds for IPv6 address: %d", maskSize)
|
|
| 467 |
+ } |
|
| 468 |
+ |
|
| 469 |
+ if (*ipAddr).Maskbits() == int(maskSize) {
|
|
| 470 |
+ matchedIfs = append(matchedIfs, addr) |
|
| 471 |
+ } else {
|
|
| 472 |
+ excludedIfs = append(excludedIfs, addr) |
|
| 473 |
+ } |
|
| 474 |
+ } |
|
| 475 |
+ |
|
| 476 |
+ return matchedIfs, excludedIfs, nil |
|
| 477 |
+} |
|
| 478 |
+ |
|
| 479 |
+// IfByType returns a list of matching and non-matching IfAddr that match the |
|
| 480 |
+// specified type. For instance: |
|
| 481 |
+// |
|
| 482 |
+// include "type" "IPv4,IPv6" |
|
| 483 |
+// |
|
| 484 |
+// will include any IfAddrs that is either an IPv4 or IPv6 address. Any |
|
| 485 |
+// addresses on those interfaces that don't match will be included in the |
|
| 486 |
+// remainder results. |
|
| 487 |
+func IfByType(inputTypes string, ifAddrs IfAddrs) (matched, remainder IfAddrs, err error) {
|
|
| 488 |
+ matchingIfAddrs := make(IfAddrs, 0, len(ifAddrs)) |
|
| 489 |
+ remainingIfAddrs := make(IfAddrs, 0, len(ifAddrs)) |
|
| 490 |
+ |
|
| 491 |
+ ifTypes := strings.Split(strings.ToLower(inputTypes), "|") |
|
| 492 |
+ for _, ifType := range ifTypes {
|
|
| 493 |
+ switch ifType {
|
|
| 494 |
+ case "ip", "ipv4", "ipv6", "unix": |
|
| 495 |
+ // Valid types |
|
| 496 |
+ default: |
|
| 497 |
+ return nil, nil, fmt.Errorf("unsupported type %q %q", ifType, inputTypes)
|
|
| 498 |
+ } |
|
| 499 |
+ } |
|
| 500 |
+ |
|
| 501 |
+ for _, ifAddr := range ifAddrs {
|
|
| 502 |
+ for _, ifType := range ifTypes {
|
|
| 503 |
+ var matched bool |
|
| 504 |
+ switch {
|
|
| 505 |
+ case ifType == "ip" && ifAddr.SockAddr.Type()&TypeIP != 0: |
|
| 506 |
+ matched = true |
|
| 507 |
+ case ifType == "ipv4" && ifAddr.SockAddr.Type()&TypeIPv4 != 0: |
|
| 508 |
+ matched = true |
|
| 509 |
+ case ifType == "ipv6" && ifAddr.SockAddr.Type()&TypeIPv6 != 0: |
|
| 510 |
+ matched = true |
|
| 511 |
+ case ifType == "unix" && ifAddr.SockAddr.Type()&TypeUnix != 0: |
|
| 512 |
+ matched = true |
|
| 513 |
+ } |
|
| 514 |
+ |
|
| 515 |
+ if matched {
|
|
| 516 |
+ matchingIfAddrs = append(matchingIfAddrs, ifAddr) |
|
| 517 |
+ } else {
|
|
| 518 |
+ remainingIfAddrs = append(remainingIfAddrs, ifAddr) |
|
| 519 |
+ } |
|
| 520 |
+ } |
|
| 521 |
+ } |
|
| 522 |
+ |
|
| 523 |
+ return matchingIfAddrs, remainingIfAddrs, nil |
|
| 524 |
+} |
|
| 525 |
+ |
|
| 526 |
+// IfByFlag returns a list of matching and non-matching IfAddrs that match the |
|
| 527 |
+// specified type. For instance: |
|
| 528 |
+// |
|
| 529 |
+// include "flag" "up,broadcast" |
|
| 530 |
+// |
|
| 531 |
+// will include any IfAddrs that have both the "up" and "broadcast" flags set. |
|
| 532 |
+// Any addresses on those interfaces that don't match will be omitted from the |
|
| 533 |
+// results. |
|
| 534 |
+func IfByFlag(inputFlags string, ifAddrs IfAddrs) (matched, remainder IfAddrs, err error) {
|
|
| 535 |
+ matchedAddrs := make(IfAddrs, 0, len(ifAddrs)) |
|
| 536 |
+ excludedAddrs := make(IfAddrs, 0, len(ifAddrs)) |
|
| 537 |
+ |
|
| 538 |
+ var wantForwardable, |
|
| 539 |
+ wantGlobalUnicast, |
|
| 540 |
+ wantInterfaceLocalMulticast, |
|
| 541 |
+ wantLinkLocalMulticast, |
|
| 542 |
+ wantLinkLocalUnicast, |
|
| 543 |
+ wantLoopback, |
|
| 544 |
+ wantMulticast, |
|
| 545 |
+ wantUnspecified bool |
|
| 546 |
+ var ifFlags net.Flags |
|
| 547 |
+ var checkFlags, checkAttrs bool |
|
| 548 |
+ for _, flagName := range strings.Split(strings.ToLower(inputFlags), "|") {
|
|
| 549 |
+ switch flagName {
|
|
| 550 |
+ case "broadcast": |
|
| 551 |
+ checkFlags = true |
|
| 552 |
+ ifFlags = ifFlags | net.FlagBroadcast |
|
| 553 |
+ case "down": |
|
| 554 |
+ checkFlags = true |
|
| 555 |
+ ifFlags = (ifFlags &^ net.FlagUp) |
|
| 556 |
+ case "forwardable": |
|
| 557 |
+ checkAttrs = true |
|
| 558 |
+ wantForwardable = true |
|
| 559 |
+ case "global unicast": |
|
| 560 |
+ checkAttrs = true |
|
| 561 |
+ wantGlobalUnicast = true |
|
| 562 |
+ case "interface-local multicast": |
|
| 563 |
+ checkAttrs = true |
|
| 564 |
+ wantInterfaceLocalMulticast = true |
|
| 565 |
+ case "link-local multicast": |
|
| 566 |
+ checkAttrs = true |
|
| 567 |
+ wantLinkLocalMulticast = true |
|
| 568 |
+ case "link-local unicast": |
|
| 569 |
+ checkAttrs = true |
|
| 570 |
+ wantLinkLocalUnicast = true |
|
| 571 |
+ case "loopback": |
|
| 572 |
+ checkAttrs = true |
|
| 573 |
+ checkFlags = true |
|
| 574 |
+ ifFlags = ifFlags | net.FlagLoopback |
|
| 575 |
+ wantLoopback = true |
|
| 576 |
+ case "multicast": |
|
| 577 |
+ checkAttrs = true |
|
| 578 |
+ checkFlags = true |
|
| 579 |
+ ifFlags = ifFlags | net.FlagMulticast |
|
| 580 |
+ wantMulticast = true |
|
| 581 |
+ case "point-to-point": |
|
| 582 |
+ checkFlags = true |
|
| 583 |
+ ifFlags = ifFlags | net.FlagPointToPoint |
|
| 584 |
+ case "unspecified": |
|
| 585 |
+ checkAttrs = true |
|
| 586 |
+ wantUnspecified = true |
|
| 587 |
+ case "up": |
|
| 588 |
+ checkFlags = true |
|
| 589 |
+ ifFlags = ifFlags | net.FlagUp |
|
| 590 |
+ default: |
|
| 591 |
+ return nil, nil, fmt.Errorf("Unknown interface flag: %+q", flagName)
|
|
| 592 |
+ } |
|
| 593 |
+ } |
|
| 594 |
+ |
|
| 595 |
+ for _, ifAddr := range ifAddrs {
|
|
| 596 |
+ var matched bool |
|
| 597 |
+ if checkFlags && ifAddr.Interface.Flags&ifFlags == ifFlags {
|
|
| 598 |
+ matched = true |
|
| 599 |
+ } |
|
| 600 |
+ if checkAttrs {
|
|
| 601 |
+ if ip := ToIPAddr(ifAddr.SockAddr); ip != nil {
|
|
| 602 |
+ netIP := (*ip).NetIP() |
|
| 603 |
+ switch {
|
|
| 604 |
+ case wantGlobalUnicast && netIP.IsGlobalUnicast(): |
|
| 605 |
+ matched = true |
|
| 606 |
+ case wantInterfaceLocalMulticast && netIP.IsInterfaceLocalMulticast(): |
|
| 607 |
+ matched = true |
|
| 608 |
+ case wantLinkLocalMulticast && netIP.IsLinkLocalMulticast(): |
|
| 609 |
+ matched = true |
|
| 610 |
+ case wantLinkLocalUnicast && netIP.IsLinkLocalUnicast(): |
|
| 611 |
+ matched = true |
|
| 612 |
+ case wantLoopback && netIP.IsLoopback(): |
|
| 613 |
+ matched = true |
|
| 614 |
+ case wantMulticast && netIP.IsMulticast(): |
|
| 615 |
+ matched = true |
|
| 616 |
+ case wantUnspecified && netIP.IsUnspecified(): |
|
| 617 |
+ matched = true |
|
| 618 |
+ case wantForwardable && !IsRFC(ForwardingBlacklist, ifAddr.SockAddr): |
|
| 619 |
+ matched = true |
|
| 620 |
+ } |
|
| 621 |
+ } |
|
| 622 |
+ } |
|
| 623 |
+ if matched {
|
|
| 624 |
+ matchedAddrs = append(matchedAddrs, ifAddr) |
|
| 625 |
+ } else {
|
|
| 626 |
+ excludedAddrs = append(excludedAddrs, ifAddr) |
|
| 627 |
+ } |
|
| 628 |
+ } |
|
| 629 |
+ return matchedAddrs, excludedAddrs, nil |
|
| 630 |
+} |
|
| 631 |
+ |
|
| 632 |
+// IfByNetwork returns an IfAddrs that are equal to or included within the |
|
| 633 |
+// network passed in by selector. |
|
| 634 |
+func IfByNetwork(selectorParam string, inputIfAddrs IfAddrs) (IfAddrs, IfAddrs, error) {
|
|
| 635 |
+ var includedIfs, excludedIfs IfAddrs |
|
| 636 |
+ for _, netStr := range strings.Split(selectorParam, "|") {
|
|
| 637 |
+ netAddr, err := NewIPAddr(netStr) |
|
| 638 |
+ if err != nil {
|
|
| 639 |
+ return nil, nil, fmt.Errorf("unable to create an IP address from %+q: %v", netStr, err)
|
|
| 640 |
+ } |
|
| 641 |
+ |
|
| 642 |
+ for _, ifAddr := range inputIfAddrs {
|
|
| 643 |
+ if netAddr.Contains(ifAddr.SockAddr) {
|
|
| 644 |
+ includedIfs = append(includedIfs, ifAddr) |
|
| 645 |
+ } else {
|
|
| 646 |
+ excludedIfs = append(excludedIfs, ifAddr) |
|
| 647 |
+ } |
|
| 648 |
+ } |
|
| 649 |
+ } |
|
| 650 |
+ |
|
| 651 |
+ return includedIfs, excludedIfs, nil |
|
| 652 |
+} |
|
| 653 |
+ |
|
| 654 |
+// IncludeIfs returns an IfAddrs based on the passed in selector. |
|
| 655 |
+func IncludeIfs(selectorName, selectorParam string, inputIfAddrs IfAddrs) (IfAddrs, error) {
|
|
| 656 |
+ var includedIfs IfAddrs |
|
| 657 |
+ var err error |
|
| 658 |
+ |
|
| 659 |
+ switch strings.ToLower(selectorName) {
|
|
| 660 |
+ case "address": |
|
| 661 |
+ includedIfs, _, err = IfByAddress(selectorParam, inputIfAddrs) |
|
| 662 |
+ case "flag", "flags": |
|
| 663 |
+ includedIfs, _, err = IfByFlag(selectorParam, inputIfAddrs) |
|
| 664 |
+ case "name": |
|
| 665 |
+ includedIfs, _, err = IfByName(selectorParam, inputIfAddrs) |
|
| 666 |
+ case "network": |
|
| 667 |
+ includedIfs, _, err = IfByNetwork(selectorParam, inputIfAddrs) |
|
| 668 |
+ case "port": |
|
| 669 |
+ includedIfs, _, err = IfByPort(selectorParam, inputIfAddrs) |
|
| 670 |
+ case "rfc", "rfcs": |
|
| 671 |
+ includedIfs, _, err = IfByRFCs(selectorParam, inputIfAddrs) |
|
| 672 |
+ case "size": |
|
| 673 |
+ includedIfs, _, err = IfByMaskSize(selectorParam, inputIfAddrs) |
|
| 674 |
+ case "type": |
|
| 675 |
+ includedIfs, _, err = IfByType(selectorParam, inputIfAddrs) |
|
| 676 |
+ default: |
|
| 677 |
+ return IfAddrs{}, fmt.Errorf("invalid include selector %q", selectorName)
|
|
| 678 |
+ } |
|
| 679 |
+ |
|
| 680 |
+ if err != nil {
|
|
| 681 |
+ return IfAddrs{}, err
|
|
| 682 |
+ } |
|
| 683 |
+ |
|
| 684 |
+ return includedIfs, nil |
|
| 685 |
+} |
|
| 686 |
+ |
|
| 687 |
+// ExcludeIfs returns an IfAddrs based on the passed in selector. |
|
| 688 |
+func ExcludeIfs(selectorName, selectorParam string, inputIfAddrs IfAddrs) (IfAddrs, error) {
|
|
| 689 |
+ var excludedIfs IfAddrs |
|
| 690 |
+ var err error |
|
| 691 |
+ |
|
| 692 |
+ switch strings.ToLower(selectorName) {
|
|
| 693 |
+ case "address": |
|
| 694 |
+ _, excludedIfs, err = IfByAddress(selectorParam, inputIfAddrs) |
|
| 695 |
+ case "flag", "flags": |
|
| 696 |
+ _, excludedIfs, err = IfByFlag(selectorParam, inputIfAddrs) |
|
| 697 |
+ case "name": |
|
| 698 |
+ _, excludedIfs, err = IfByName(selectorParam, inputIfAddrs) |
|
| 699 |
+ case "network": |
|
| 700 |
+ _, excludedIfs, err = IfByNetwork(selectorParam, inputIfAddrs) |
|
| 701 |
+ case "port": |
|
| 702 |
+ _, excludedIfs, err = IfByPort(selectorParam, inputIfAddrs) |
|
| 703 |
+ case "rfc", "rfcs": |
|
| 704 |
+ _, excludedIfs, err = IfByRFCs(selectorParam, inputIfAddrs) |
|
| 705 |
+ case "size": |
|
| 706 |
+ _, excludedIfs, err = IfByMaskSize(selectorParam, inputIfAddrs) |
|
| 707 |
+ case "type": |
|
| 708 |
+ _, excludedIfs, err = IfByType(selectorParam, inputIfAddrs) |
|
| 709 |
+ default: |
|
| 710 |
+ return IfAddrs{}, fmt.Errorf("invalid exclude selector %q", selectorName)
|
|
| 711 |
+ } |
|
| 712 |
+ |
|
| 713 |
+ if err != nil {
|
|
| 714 |
+ return IfAddrs{}, err
|
|
| 715 |
+ } |
|
| 716 |
+ |
|
| 717 |
+ return excludedIfs, nil |
|
| 718 |
+} |
|
| 719 |
+ |
|
| 720 |
+// SortIfBy returns an IfAddrs sorted based on the passed in selector. Multiple |
|
| 721 |
+// sort clauses can be passed in as a comma delimited list without whitespace. |
|
| 722 |
+func SortIfBy(selectorParam string, inputIfAddrs IfAddrs) (IfAddrs, error) {
|
|
| 723 |
+ sortedIfs := append(IfAddrs(nil), inputIfAddrs...) |
|
| 724 |
+ |
|
| 725 |
+ clauses := strings.Split(selectorParam, ",") |
|
| 726 |
+ sortFuncs := make([]CmpIfAddrFunc, len(clauses)) |
|
| 727 |
+ |
|
| 728 |
+ for i, clause := range clauses {
|
|
| 729 |
+ switch strings.TrimSpace(strings.ToLower(clause)) {
|
|
| 730 |
+ case "+address", "address": |
|
| 731 |
+ // The "address" selector returns an array of IfAddrs |
|
| 732 |
+ // ordered by the network address. IfAddrs that are not |
|
| 733 |
+ // comparable will be at the end of the list and in a |
|
| 734 |
+ // non-deterministic order. |
|
| 735 |
+ sortFuncs[i] = AscIfAddress |
|
| 736 |
+ case "-address": |
|
| 737 |
+ sortFuncs[i] = DescIfAddress |
|
| 738 |
+ case "+name", "name": |
|
| 739 |
+ // The "name" selector returns an array of IfAddrs |
|
| 740 |
+ // ordered by the interface name. |
|
| 741 |
+ sortFuncs[i] = AscIfName |
|
| 742 |
+ case "-name": |
|
| 743 |
+ sortFuncs[i] = DescIfName |
|
| 744 |
+ case "+port", "port": |
|
| 745 |
+ // The "port" selector returns an array of IfAddrs |
|
| 746 |
+ // ordered by the port, if included in the IfAddr. |
|
| 747 |
+ // IfAddrs that are not comparable will be at the end of |
|
| 748 |
+ // the list and in a non-deterministic order. |
|
| 749 |
+ sortFuncs[i] = AscIfPort |
|
| 750 |
+ case "-port": |
|
| 751 |
+ sortFuncs[i] = DescIfPort |
|
| 752 |
+ case "+private", "private": |
|
| 753 |
+ // The "private" selector returns an array of IfAddrs |
|
| 754 |
+ // ordered by private addresses first. IfAddrs that are |
|
| 755 |
+ // not comparable will be at the end of the list and in |
|
| 756 |
+ // a non-deterministic order. |
|
| 757 |
+ sortFuncs[i] = AscIfPrivate |
|
| 758 |
+ case "-private": |
|
| 759 |
+ sortFuncs[i] = DescIfPrivate |
|
| 760 |
+ case "+size", "size": |
|
| 761 |
+ // The "size" selector returns an array of IfAddrs |
|
| 762 |
+ // ordered by the size of the network mask, smaller mask |
|
| 763 |
+ // (larger number of hosts per network) to largest |
|
| 764 |
+ // (e.g. a /24 sorts before a /32). |
|
| 765 |
+ sortFuncs[i] = AscIfNetworkSize |
|
| 766 |
+ case "-size": |
|
| 767 |
+ sortFuncs[i] = DescIfNetworkSize |
|
| 768 |
+ case "+type", "type": |
|
| 769 |
+ // The "type" selector returns an array of IfAddrs |
|
| 770 |
+ // ordered by the type of the IfAddr. The sort order is |
|
| 771 |
+ // Unix, IPv4, then IPv6. |
|
| 772 |
+ sortFuncs[i] = AscIfType |
|
| 773 |
+ case "-type": |
|
| 774 |
+ sortFuncs[i] = DescIfType |
|
| 775 |
+ default: |
|
| 776 |
+ // Return an empty list for invalid sort types. |
|
| 777 |
+ return IfAddrs{}, fmt.Errorf("unknown sort type: %q", clause)
|
|
| 778 |
+ } |
|
| 779 |
+ } |
|
| 780 |
+ |
|
| 781 |
+ OrderedIfAddrBy(sortFuncs...).Sort(sortedIfs) |
|
| 782 |
+ |
|
| 783 |
+ return sortedIfs, nil |
|
| 784 |
+} |
|
| 785 |
+ |
|
| 786 |
+// UniqueIfAddrsBy creates a unique set of IfAddrs based on the matching |
|
| 787 |
+// selector. UniqueIfAddrsBy assumes the input has already been sorted. |
|
| 788 |
+func UniqueIfAddrsBy(selectorName string, inputIfAddrs IfAddrs) (IfAddrs, error) {
|
|
| 789 |
+ attrName := strings.ToLower(selectorName) |
|
| 790 |
+ |
|
| 791 |
+ ifs := make(IfAddrs, 0, len(inputIfAddrs)) |
|
| 792 |
+ var lastMatch string |
|
| 793 |
+ for _, ifAddr := range inputIfAddrs {
|
|
| 794 |
+ var out string |
|
| 795 |
+ switch attrName {
|
|
| 796 |
+ case "address": |
|
| 797 |
+ out = ifAddr.SockAddr.String() |
|
| 798 |
+ case "name": |
|
| 799 |
+ out = ifAddr.Name |
|
| 800 |
+ default: |
|
| 801 |
+ return nil, fmt.Errorf("unsupported unique constraint %+q", selectorName)
|
|
| 802 |
+ } |
|
| 803 |
+ |
|
| 804 |
+ switch {
|
|
| 805 |
+ case lastMatch == "", lastMatch != out: |
|
| 806 |
+ lastMatch = out |
|
| 807 |
+ ifs = append(ifs, ifAddr) |
|
| 808 |
+ case lastMatch == out: |
|
| 809 |
+ continue |
|
| 810 |
+ } |
|
| 811 |
+ } |
|
| 812 |
+ |
|
| 813 |
+ return ifs, nil |
|
| 814 |
+} |
|
| 815 |
+ |
|
| 816 |
+// JoinIfAddrs joins an IfAddrs and returns a string |
|
| 817 |
+func JoinIfAddrs(selectorName string, joinStr string, inputIfAddrs IfAddrs) (string, error) {
|
|
| 818 |
+ outputs := make([]string, 0, len(inputIfAddrs)) |
|
| 819 |
+ attrName := AttrName(strings.ToLower(selectorName)) |
|
| 820 |
+ |
|
| 821 |
+ for _, ifAddr := range inputIfAddrs {
|
|
| 822 |
+ var attrVal string |
|
| 823 |
+ var err error |
|
| 824 |
+ attrVal, err = ifAddr.Attr(attrName) |
|
| 825 |
+ if err != nil {
|
|
| 826 |
+ return "", err |
|
| 827 |
+ } |
|
| 828 |
+ outputs = append(outputs, attrVal) |
|
| 829 |
+ } |
|
| 830 |
+ return strings.Join(outputs, joinStr), nil |
|
| 831 |
+} |
|
| 832 |
+ |
|
| 833 |
+// LimitIfAddrs returns a slice of IfAddrs based on the specified limit. |
|
| 834 |
+func LimitIfAddrs(lim uint, in IfAddrs) (IfAddrs, error) {
|
|
| 835 |
+ // Clamp the limit to the length of the array |
|
| 836 |
+ if int(lim) > len(in) {
|
|
| 837 |
+ lim = uint(len(in)) |
|
| 838 |
+ } |
|
| 839 |
+ |
|
| 840 |
+ return in[0:lim], nil |
|
| 841 |
+} |
|
| 842 |
+ |
|
| 843 |
+// OffsetIfAddrs returns a slice of IfAddrs based on the specified offset. |
|
| 844 |
+func OffsetIfAddrs(off int, in IfAddrs) (IfAddrs, error) {
|
|
| 845 |
+ var end bool |
|
| 846 |
+ if off < 0 {
|
|
| 847 |
+ end = true |
|
| 848 |
+ off = off * -1 |
|
| 849 |
+ } |
|
| 850 |
+ |
|
| 851 |
+ if off > len(in) {
|
|
| 852 |
+ 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))
|
|
| 853 |
+ } |
|
| 854 |
+ |
|
| 855 |
+ if end {
|
|
| 856 |
+ return in[len(in)-off:], nil |
|
| 857 |
+ } |
|
| 858 |
+ return in[off:], nil |
|
| 859 |
+} |
|
| 860 |
+ |
|
| 861 |
+func (ifAddr IfAddr) String() string {
|
|
| 862 |
+ return fmt.Sprintf("%s %v", ifAddr.SockAddr, ifAddr.Interface)
|
|
| 863 |
+} |
|
| 864 |
+ |
|
| 865 |
+// parseDefaultIfNameFromRoute parses standard route(8)'s output for the *BSDs |
|
| 866 |
+// and Solaris. |
|
| 867 |
+func parseDefaultIfNameFromRoute(routeOut string) (string, error) {
|
|
| 868 |
+ lines := strings.Split(routeOut, "\n") |
|
| 869 |
+ for _, line := range lines {
|
|
| 870 |
+ kvs := strings.SplitN(line, ":", 2) |
|
| 871 |
+ if len(kvs) != 2 {
|
|
| 872 |
+ continue |
|
| 873 |
+ } |
|
| 874 |
+ |
|
| 875 |
+ if strings.TrimSpace(kvs[0]) == "interface" {
|
|
| 876 |
+ ifName := strings.TrimSpace(kvs[1]) |
|
| 877 |
+ return ifName, nil |
|
| 878 |
+ } |
|
| 879 |
+ } |
|
| 880 |
+ |
|
| 881 |
+ return "", errors.New("No default interface found")
|
|
| 882 |
+} |
|
| 883 |
+ |
|
| 884 |
+// parseDefaultIfNameFromIPCmd parses the default interface from ip(8) for |
|
| 885 |
+// Linux. |
|
| 886 |
+func parseDefaultIfNameFromIPCmd(routeOut string) (string, error) {
|
|
| 887 |
+ lines := strings.Split(routeOut, "\n") |
|
| 888 |
+ re := regexp.MustCompile(`[\s]+`) |
|
| 889 |
+ for _, line := range lines {
|
|
| 890 |
+ kvs := re.Split(line, -1) |
|
| 891 |
+ if len(kvs) < 5 {
|
|
| 892 |
+ continue |
|
| 893 |
+ } |
|
| 894 |
+ |
|
| 895 |
+ if kvs[0] == "default" && |
|
| 896 |
+ kvs[1] == "via" && |
|
| 897 |
+ kvs[3] == "dev" {
|
|
| 898 |
+ ifName := strings.TrimSpace(kvs[4]) |
|
| 899 |
+ return ifName, nil |
|
| 900 |
+ } |
|
| 901 |
+ } |
|
| 902 |
+ |
|
| 903 |
+ return "", errors.New("No default interface found")
|
|
| 904 |
+} |
|
| 905 |
+ |
|
| 906 |
+// parseDefaultIfNameWindows parses the default interface from `netstat -rn` and |
|
| 907 |
+// `ipconfig` on Windows. |
|
| 908 |
+func parseDefaultIfNameWindows(routeOut, ipconfigOut string) (string, error) {
|
|
| 909 |
+ defaultIPAddr, err := parseDefaultIPAddrWindowsRoute(routeOut) |
|
| 910 |
+ if err != nil {
|
|
| 911 |
+ return "", err |
|
| 912 |
+ } |
|
| 913 |
+ |
|
| 914 |
+ ifName, err := parseDefaultIfNameWindowsIPConfig(defaultIPAddr, ipconfigOut) |
|
| 915 |
+ if err != nil {
|
|
| 916 |
+ return "", err |
|
| 917 |
+ } |
|
| 918 |
+ |
|
| 919 |
+ return ifName, nil |
|
| 920 |
+} |
|
| 921 |
+ |
|
| 922 |
+// parseDefaultIPAddrWindowsRoute parses the IP address on the default interface |
|
| 923 |
+// `netstat -rn`. |
|
| 924 |
+// |
|
| 925 |
+// NOTES(sean): Only IPv4 addresses are parsed at this time. If you have an |
|
| 926 |
+// IPv6 connected host, submit an issue on github.com/hashicorp/go-sockaddr with |
|
| 927 |
+// the output from `netstat -rn`, `ipconfig`, and version of Windows to see IPv6 |
|
| 928 |
+// support added. |
|
| 929 |
+func parseDefaultIPAddrWindowsRoute(routeOut string) (string, error) {
|
|
| 930 |
+ lines := strings.Split(routeOut, "\n") |
|
| 931 |
+ re := regexp.MustCompile(`[\s]+`) |
|
| 932 |
+ for _, line := range lines {
|
|
| 933 |
+ kvs := re.Split(strings.TrimSpace(line), -1) |
|
| 934 |
+ if len(kvs) < 3 {
|
|
| 935 |
+ continue |
|
| 936 |
+ } |
|
| 937 |
+ |
|
| 938 |
+ if kvs[0] == "0.0.0.0" && kvs[1] == "0.0.0.0" {
|
|
| 939 |
+ defaultIPAddr := strings.TrimSpace(kvs[3]) |
|
| 940 |
+ return defaultIPAddr, nil |
|
| 941 |
+ } |
|
| 942 |
+ } |
|
| 943 |
+ |
|
| 944 |
+ return "", errors.New("No IP on default interface found")
|
|
| 945 |
+} |
|
| 946 |
+ |
|
| 947 |
+// parseDefaultIfNameWindowsIPConfig parses the output of `ipconfig` to find the |
|
| 948 |
+// interface name forwarding traffic to the default gateway. |
|
| 949 |
+func parseDefaultIfNameWindowsIPConfig(defaultIPAddr, routeOut string) (string, error) {
|
|
| 950 |
+ lines := strings.Split(routeOut, "\n") |
|
| 951 |
+ ifNameRE := regexp.MustCompile(`^Ethernet adapter ([^\s:]+):`) |
|
| 952 |
+ ipAddrRE := regexp.MustCompile(`^ IPv[46] Address\. \. \. \. \. \. \. \. \. \. \. : ([^\s]+)`) |
|
| 953 |
+ var ifName string |
|
| 954 |
+ for _, line := range lines {
|
|
| 955 |
+ switch ifNameMatches := ifNameRE.FindStringSubmatch(line); {
|
|
| 956 |
+ case len(ifNameMatches) > 1: |
|
| 957 |
+ ifName = ifNameMatches[1] |
|
| 958 |
+ continue |
|
| 959 |
+ } |
|
| 960 |
+ |
|
| 961 |
+ switch ipAddrMatches := ipAddrRE.FindStringSubmatch(line); {
|
|
| 962 |
+ case len(ipAddrMatches) > 1 && ipAddrMatches[1] == defaultIPAddr: |
|
| 963 |
+ return ifName, nil |
|
| 964 |
+ } |
|
| 965 |
+ } |
|
| 966 |
+ |
|
| 967 |
+ return "", errors.New("No default interface found with matching IP")
|
|
| 968 |
+} |
| 0 | 969 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,65 @@ |
| 0 |
+package sockaddr |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "fmt" |
|
| 4 |
+ "net" |
|
| 5 |
+) |
|
| 6 |
+ |
|
| 7 |
+// IfAddr is a union of a SockAddr and a net.Interface. |
|
| 8 |
+type IfAddr struct {
|
|
| 9 |
+ SockAddr |
|
| 10 |
+ net.Interface |
|
| 11 |
+} |
|
| 12 |
+ |
|
| 13 |
+// Attr returns the named attribute as a string |
|
| 14 |
+func (ifAddr IfAddr) Attr(attrName AttrName) (string, error) {
|
|
| 15 |
+ val := IfAddrAttr(ifAddr, attrName) |
|
| 16 |
+ if val != "" {
|
|
| 17 |
+ return val, nil |
|
| 18 |
+ } |
|
| 19 |
+ |
|
| 20 |
+ return Attr(ifAddr.SockAddr, attrName) |
|
| 21 |
+} |
|
| 22 |
+ |
|
| 23 |
+// Attr returns the named attribute as a string |
|
| 24 |
+func Attr(sa SockAddr, attrName AttrName) (string, error) {
|
|
| 25 |
+ switch sockType := sa.Type(); {
|
|
| 26 |
+ case sockType&TypeIP != 0: |
|
| 27 |
+ ip := *ToIPAddr(sa) |
|
| 28 |
+ attrVal := IPAddrAttr(ip, attrName) |
|
| 29 |
+ if attrVal != "" {
|
|
| 30 |
+ return attrVal, nil |
|
| 31 |
+ } |
|
| 32 |
+ |
|
| 33 |
+ if sockType == TypeIPv4 {
|
|
| 34 |
+ ipv4 := *ToIPv4Addr(sa) |
|
| 35 |
+ attrVal := IPv4AddrAttr(ipv4, attrName) |
|
| 36 |
+ if attrVal != "" {
|
|
| 37 |
+ return attrVal, nil |
|
| 38 |
+ } |
|
| 39 |
+ } else if sockType == TypeIPv6 {
|
|
| 40 |
+ ipv6 := *ToIPv6Addr(sa) |
|
| 41 |
+ attrVal := IPv6AddrAttr(ipv6, attrName) |
|
| 42 |
+ if attrVal != "" {
|
|
| 43 |
+ return attrVal, nil |
|
| 44 |
+ } |
|
| 45 |
+ } |
|
| 46 |
+ |
|
| 47 |
+ case sockType == TypeUnix: |
|
| 48 |
+ us := *ToUnixSock(sa) |
|
| 49 |
+ attrVal := UnixSockAttr(us, attrName) |
|
| 50 |
+ if attrVal != "" {
|
|
| 51 |
+ return attrVal, nil |
|
| 52 |
+ } |
|
| 53 |
+ } |
|
| 54 |
+ |
|
| 55 |
+ // Non type-specific attributes |
|
| 56 |
+ switch attrName {
|
|
| 57 |
+ case "string": |
|
| 58 |
+ return sa.String(), nil |
|
| 59 |
+ case "type": |
|
| 60 |
+ return sa.Type().String(), nil |
|
| 61 |
+ } |
|
| 62 |
+ |
|
| 63 |
+ return "", fmt.Errorf("unsupported attribute name %q", attrName)
|
|
| 64 |
+} |
| 0 | 65 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,169 @@ |
| 0 |
+package sockaddr |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "fmt" |
|
| 4 |
+ "math/big" |
|
| 5 |
+ "net" |
|
| 6 |
+ "strings" |
|
| 7 |
+) |
|
| 8 |
+ |
|
| 9 |
+// Constants for the sizes of IPv3, IPv4, and IPv6 address types. |
|
| 10 |
+const ( |
|
| 11 |
+ IPv3len = 6 |
|
| 12 |
+ IPv4len = 4 |
|
| 13 |
+ IPv6len = 16 |
|
| 14 |
+) |
|
| 15 |
+ |
|
| 16 |
+// IPAddr is a generic IP address interface for IPv4 and IPv6 addresses, |
|
| 17 |
+// networks, and socket endpoints. |
|
| 18 |
+type IPAddr interface {
|
|
| 19 |
+ SockAddr |
|
| 20 |
+ AddressBinString() string |
|
| 21 |
+ AddressHexString() string |
|
| 22 |
+ Cmp(SockAddr) int |
|
| 23 |
+ CmpAddress(SockAddr) int |
|
| 24 |
+ CmpPort(SockAddr) int |
|
| 25 |
+ FirstUsable() IPAddr |
|
| 26 |
+ Host() IPAddr |
|
| 27 |
+ IPPort() IPPort |
|
| 28 |
+ LastUsable() IPAddr |
|
| 29 |
+ Maskbits() int |
|
| 30 |
+ NetIP() *net.IP |
|
| 31 |
+ NetIPMask() *net.IPMask |
|
| 32 |
+ NetIPNet() *net.IPNet |
|
| 33 |
+ Network() IPAddr |
|
| 34 |
+ Octets() []int |
|
| 35 |
+} |
|
| 36 |
+ |
|
| 37 |
+// IPPort is the type for an IP port number for the TCP and UDP IP transports. |
|
| 38 |
+type IPPort uint16 |
|
| 39 |
+ |
|
| 40 |
+// IPPrefixLen is a typed integer representing the prefix length for a given |
|
| 41 |
+// IPAddr. |
|
| 42 |
+type IPPrefixLen byte |
|
| 43 |
+ |
|
| 44 |
+// ipAddrAttrMap is a map of the IPAddr type-specific attributes. |
|
| 45 |
+var ipAddrAttrMap map[AttrName]func(IPAddr) string |
|
| 46 |
+var ipAddrAttrs []AttrName |
|
| 47 |
+ |
|
| 48 |
+func init() {
|
|
| 49 |
+ ipAddrInit() |
|
| 50 |
+} |
|
| 51 |
+ |
|
| 52 |
+// NewIPAddr creates a new IPAddr from a string. Returns nil if the string is |
|
| 53 |
+// not an IPv4 or an IPv6 address. |
|
| 54 |
+func NewIPAddr(addr string) (IPAddr, error) {
|
|
| 55 |
+ ipv4Addr, err := NewIPv4Addr(addr) |
|
| 56 |
+ if err == nil {
|
|
| 57 |
+ return ipv4Addr, nil |
|
| 58 |
+ } |
|
| 59 |
+ |
|
| 60 |
+ ipv6Addr, err := NewIPv6Addr(addr) |
|
| 61 |
+ if err == nil {
|
|
| 62 |
+ return ipv6Addr, nil |
|
| 63 |
+ } |
|
| 64 |
+ |
|
| 65 |
+ return nil, fmt.Errorf("invalid IPAddr %v", addr)
|
|
| 66 |
+} |
|
| 67 |
+ |
|
| 68 |
+// IPAddrAttr returns a string representation of an attribute for the given |
|
| 69 |
+// IPAddr. |
|
| 70 |
+func IPAddrAttr(ip IPAddr, selector AttrName) string {
|
|
| 71 |
+ fn, found := ipAddrAttrMap[selector] |
|
| 72 |
+ if !found {
|
|
| 73 |
+ return "" |
|
| 74 |
+ } |
|
| 75 |
+ |
|
| 76 |
+ return fn(ip) |
|
| 77 |
+} |
|
| 78 |
+ |
|
| 79 |
+// IPAttrs returns a list of attributes supported by the IPAddr type |
|
| 80 |
+func IPAttrs() []AttrName {
|
|
| 81 |
+ return ipAddrAttrs |
|
| 82 |
+} |
|
| 83 |
+ |
|
| 84 |
+// MustIPAddr is a helper method that must return an IPAddr or panic on invalid |
|
| 85 |
+// input. |
|
| 86 |
+func MustIPAddr(addr string) IPAddr {
|
|
| 87 |
+ ip, err := NewIPAddr(addr) |
|
| 88 |
+ if err != nil {
|
|
| 89 |
+ panic(fmt.Sprintf("Unable to create an IPAddr from %+q: %v", addr, err))
|
|
| 90 |
+ } |
|
| 91 |
+ return ip |
|
| 92 |
+} |
|
| 93 |
+ |
|
| 94 |
+// ipAddrInit is called once at init() |
|
| 95 |
+func ipAddrInit() {
|
|
| 96 |
+ // Sorted for human readability |
|
| 97 |
+ ipAddrAttrs = []AttrName{
|
|
| 98 |
+ "host", |
|
| 99 |
+ "address", |
|
| 100 |
+ "port", |
|
| 101 |
+ "netmask", |
|
| 102 |
+ "network", |
|
| 103 |
+ "mask_bits", |
|
| 104 |
+ "binary", |
|
| 105 |
+ "hex", |
|
| 106 |
+ "first_usable", |
|
| 107 |
+ "last_usable", |
|
| 108 |
+ "octets", |
|
| 109 |
+ } |
|
| 110 |
+ |
|
| 111 |
+ ipAddrAttrMap = map[AttrName]func(ip IPAddr) string{
|
|
| 112 |
+ "address": func(ip IPAddr) string {
|
|
| 113 |
+ return ip.NetIP().String() |
|
| 114 |
+ }, |
|
| 115 |
+ "binary": func(ip IPAddr) string {
|
|
| 116 |
+ return ip.AddressBinString() |
|
| 117 |
+ }, |
|
| 118 |
+ "first_usable": func(ip IPAddr) string {
|
|
| 119 |
+ return ip.FirstUsable().String() |
|
| 120 |
+ }, |
|
| 121 |
+ "hex": func(ip IPAddr) string {
|
|
| 122 |
+ return ip.AddressHexString() |
|
| 123 |
+ }, |
|
| 124 |
+ "host": func(ip IPAddr) string {
|
|
| 125 |
+ return ip.Host().String() |
|
| 126 |
+ }, |
|
| 127 |
+ "last_usable": func(ip IPAddr) string {
|
|
| 128 |
+ return ip.LastUsable().String() |
|
| 129 |
+ }, |
|
| 130 |
+ "mask_bits": func(ip IPAddr) string {
|
|
| 131 |
+ return fmt.Sprintf("%d", ip.Maskbits())
|
|
| 132 |
+ }, |
|
| 133 |
+ "netmask": func(ip IPAddr) string {
|
|
| 134 |
+ switch v := ip.(type) {
|
|
| 135 |
+ case IPv4Addr: |
|
| 136 |
+ ipv4Mask := IPv4Addr{
|
|
| 137 |
+ Address: IPv4Address(v.Mask), |
|
| 138 |
+ Mask: IPv4HostMask, |
|
| 139 |
+ } |
|
| 140 |
+ return ipv4Mask.String() |
|
| 141 |
+ case IPv6Addr: |
|
| 142 |
+ ipv6Mask := new(big.Int) |
|
| 143 |
+ ipv6Mask.Set(v.Mask) |
|
| 144 |
+ ipv6MaskAddr := IPv6Addr{
|
|
| 145 |
+ Address: IPv6Address(ipv6Mask), |
|
| 146 |
+ Mask: ipv6HostMask, |
|
| 147 |
+ } |
|
| 148 |
+ return ipv6MaskAddr.String() |
|
| 149 |
+ default: |
|
| 150 |
+ return fmt.Sprintf("<unsupported type: %T>", ip)
|
|
| 151 |
+ } |
|
| 152 |
+ }, |
|
| 153 |
+ "network": func(ip IPAddr) string {
|
|
| 154 |
+ return ip.Network().NetIP().String() |
|
| 155 |
+ }, |
|
| 156 |
+ "octets": func(ip IPAddr) string {
|
|
| 157 |
+ octets := ip.Octets() |
|
| 158 |
+ octetStrs := make([]string, 0, len(octets)) |
|
| 159 |
+ for _, octet := range octets {
|
|
| 160 |
+ octetStrs = append(octetStrs, fmt.Sprintf("%d", octet))
|
|
| 161 |
+ } |
|
| 162 |
+ return strings.Join(octetStrs, " ") |
|
| 163 |
+ }, |
|
| 164 |
+ "port": func(ip IPAddr) string {
|
|
| 165 |
+ return fmt.Sprintf("%d", ip.IPPort())
|
|
| 166 |
+ }, |
|
| 167 |
+ } |
|
| 168 |
+} |
| 0 | 169 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,98 @@ |
| 0 |
+package sockaddr |
|
| 1 |
+ |
|
| 2 |
+import "bytes" |
|
| 3 |
+ |
|
| 4 |
+type IPAddrs []IPAddr |
|
| 5 |
+ |
|
| 6 |
+func (s IPAddrs) Len() int { return len(s) }
|
|
| 7 |
+func (s IPAddrs) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
|
|
| 8 |
+ |
|
| 9 |
+// // SortIPAddrsByCmp is a type that satisfies sort.Interface and can be used |
|
| 10 |
+// // by the routines in this package. The SortIPAddrsByCmp type is used to |
|
| 11 |
+// // sort IPAddrs by Cmp() |
|
| 12 |
+// type SortIPAddrsByCmp struct{ IPAddrs }
|
|
| 13 |
+ |
|
| 14 |
+// // Less reports whether the element with index i should sort before the |
|
| 15 |
+// // element with index j. |
|
| 16 |
+// func (s SortIPAddrsByCmp) Less(i, j int) bool {
|
|
| 17 |
+// // Sort by Type, then address, then port number. |
|
| 18 |
+// return Less(s.IPAddrs[i], s.IPAddrs[j]) |
|
| 19 |
+// } |
|
| 20 |
+ |
|
| 21 |
+// SortIPAddrsBySpecificMaskLen is a type that satisfies sort.Interface and |
|
| 22 |
+// can be used by the routines in this package. The |
|
| 23 |
+// SortIPAddrsBySpecificMaskLen type is used to sort IPAddrs by smallest |
|
| 24 |
+// network (most specific to largest network). |
|
| 25 |
+type SortIPAddrsByNetworkSize struct{ IPAddrs }
|
|
| 26 |
+ |
|
| 27 |
+// Less reports whether the element with index i should sort before the |
|
| 28 |
+// element with index j. |
|
| 29 |
+func (s SortIPAddrsByNetworkSize) Less(i, j int) bool {
|
|
| 30 |
+ // Sort masks with a larger binary value (i.e. fewer hosts per network |
|
| 31 |
+ // prefix) after masks with a smaller value (larger number of hosts per |
|
| 32 |
+ // prefix). |
|
| 33 |
+ switch bytes.Compare([]byte(*s.IPAddrs[i].NetIPMask()), []byte(*s.IPAddrs[j].NetIPMask())) {
|
|
| 34 |
+ case 0: |
|
| 35 |
+ // Fall through to the second test if the net.IPMasks are the |
|
| 36 |
+ // same. |
|
| 37 |
+ break |
|
| 38 |
+ case 1: |
|
| 39 |
+ return true |
|
| 40 |
+ case -1: |
|
| 41 |
+ return false |
|
| 42 |
+ default: |
|
| 43 |
+ panic("bad, m'kay?")
|
|
| 44 |
+ } |
|
| 45 |
+ |
|
| 46 |
+ // Sort IPs based on the length (i.e. prefer IPv4 over IPv6). |
|
| 47 |
+ iLen := len(*s.IPAddrs[i].NetIP()) |
|
| 48 |
+ jLen := len(*s.IPAddrs[j].NetIP()) |
|
| 49 |
+ if iLen != jLen {
|
|
| 50 |
+ return iLen > jLen |
|
| 51 |
+ } |
|
| 52 |
+ |
|
| 53 |
+ // Sort IPs based on their network address from lowest to highest. |
|
| 54 |
+ switch bytes.Compare(s.IPAddrs[i].NetIPNet().IP, s.IPAddrs[j].NetIPNet().IP) {
|
|
| 55 |
+ case 0: |
|
| 56 |
+ break |
|
| 57 |
+ case 1: |
|
| 58 |
+ return false |
|
| 59 |
+ case -1: |
|
| 60 |
+ return true |
|
| 61 |
+ default: |
|
| 62 |
+ panic("lol wut?")
|
|
| 63 |
+ } |
|
| 64 |
+ |
|
| 65 |
+ // If a host does not have a port set, it always sorts after hosts |
|
| 66 |
+ // that have a port (e.g. a host with a /32 and port number is more |
|
| 67 |
+ // specific and should sort first over a host with a /32 but no port |
|
| 68 |
+ // set). |
|
| 69 |
+ if s.IPAddrs[i].IPPort() == 0 || s.IPAddrs[j].IPPort() == 0 {
|
|
| 70 |
+ return false |
|
| 71 |
+ } |
|
| 72 |
+ return s.IPAddrs[i].IPPort() < s.IPAddrs[j].IPPort() |
|
| 73 |
+} |
|
| 74 |
+ |
|
| 75 |
+// SortIPAddrsBySpecificMaskLen is a type that satisfies sort.Interface and |
|
| 76 |
+// can be used by the routines in this package. The |
|
| 77 |
+// SortIPAddrsBySpecificMaskLen type is used to sort IPAddrs by smallest |
|
| 78 |
+// network (most specific to largest network). |
|
| 79 |
+type SortIPAddrsBySpecificMaskLen struct{ IPAddrs }
|
|
| 80 |
+ |
|
| 81 |
+// Less reports whether the element with index i should sort before the |
|
| 82 |
+// element with index j. |
|
| 83 |
+func (s SortIPAddrsBySpecificMaskLen) Less(i, j int) bool {
|
|
| 84 |
+ return s.IPAddrs[i].Maskbits() > s.IPAddrs[j].Maskbits() |
|
| 85 |
+} |
|
| 86 |
+ |
|
| 87 |
+// SortIPAddrsByBroadMaskLen is a type that satisfies sort.Interface and can |
|
| 88 |
+// be used by the routines in this package. The SortIPAddrsByBroadMaskLen |
|
| 89 |
+// type is used to sort IPAddrs by largest network (i.e. largest subnets |
|
| 90 |
+// first). |
|
| 91 |
+type SortIPAddrsByBroadMaskLen struct{ IPAddrs }
|
|
| 92 |
+ |
|
| 93 |
+// Less reports whether the element with index i should sort before the |
|
| 94 |
+// element with index j. |
|
| 95 |
+func (s SortIPAddrsByBroadMaskLen) Less(i, j int) bool {
|
|
| 96 |
+ return s.IPAddrs[i].Maskbits() < s.IPAddrs[j].Maskbits() |
|
| 97 |
+} |
| 0 | 98 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,515 @@ |
| 0 |
+package sockaddr |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "encoding/binary" |
|
| 4 |
+ "fmt" |
|
| 5 |
+ "net" |
|
| 6 |
+ "regexp" |
|
| 7 |
+ "strconv" |
|
| 8 |
+ "strings" |
|
| 9 |
+) |
|
| 10 |
+ |
|
| 11 |
+type ( |
|
| 12 |
+ // IPv4Address is a named type representing an IPv4 address. |
|
| 13 |
+ IPv4Address uint32 |
|
| 14 |
+ |
|
| 15 |
+ // IPv4Network is a named type representing an IPv4 network. |
|
| 16 |
+ IPv4Network uint32 |
|
| 17 |
+ |
|
| 18 |
+ // IPv4Mask is a named type representing an IPv4 network mask. |
|
| 19 |
+ IPv4Mask uint32 |
|
| 20 |
+) |
|
| 21 |
+ |
|
| 22 |
+// IPv4HostMask is a constant represents a /32 IPv4 Address |
|
| 23 |
+// (i.e. 255.255.255.255). |
|
| 24 |
+const IPv4HostMask = IPv4Mask(0xffffffff) |
|
| 25 |
+ |
|
| 26 |
+// ipv4AddrAttrMap is a map of the IPv4Addr type-specific attributes. |
|
| 27 |
+var ipv4AddrAttrMap map[AttrName]func(IPv4Addr) string |
|
| 28 |
+var ipv4AddrAttrs []AttrName |
|
| 29 |
+var trailingHexNetmaskRE *regexp.Regexp |
|
| 30 |
+ |
|
| 31 |
+// IPv4Addr implements a convenience wrapper around the union of Go's |
|
| 32 |
+// built-in net.IP and net.IPNet types. In UNIX-speak, IPv4Addr implements |
|
| 33 |
+// `sockaddr` when the the address family is set to AF_INET |
|
| 34 |
+// (i.e. `sockaddr_in`). |
|
| 35 |
+type IPv4Addr struct {
|
|
| 36 |
+ IPAddr |
|
| 37 |
+ Address IPv4Address |
|
| 38 |
+ Mask IPv4Mask |
|
| 39 |
+ Port IPPort |
|
| 40 |
+} |
|
| 41 |
+ |
|
| 42 |
+func init() {
|
|
| 43 |
+ ipv4AddrInit() |
|
| 44 |
+ trailingHexNetmaskRE = regexp.MustCompile(`/([0f]{8})$`)
|
|
| 45 |
+} |
|
| 46 |
+ |
|
| 47 |
+// NewIPv4Addr creates an IPv4Addr from a string. String can be in the form |
|
| 48 |
+// of either an IPv4:port (e.g. `1.2.3.4:80`, in which case the mask is |
|
| 49 |
+// assumed to be a `/32`), an IPv4 address (e.g. `1.2.3.4`, also with a `/32` |
|
| 50 |
+// mask), or an IPv4 CIDR (e.g. `1.2.3.4/24`, which has its IP port |
|
| 51 |
+// initialized to zero). ipv4Str can not be a hostname. |
|
| 52 |
+// |
|
| 53 |
+// NOTE: Many net.*() routines will initialize and return an IPv6 address. |
|
| 54 |
+// To create uint32 values from net.IP, always test to make sure the address |
|
| 55 |
+// returned can be converted to a 4 byte array using To4(). |
|
| 56 |
+func NewIPv4Addr(ipv4Str string) (IPv4Addr, error) {
|
|
| 57 |
+ // Strip off any bogus hex-encoded netmasks that will be mis-parsed by Go. In |
|
| 58 |
+ // particular, clients with the Barracuda VPN client will see something like: |
|
| 59 |
+ // `192.168.3.51/00ffffff` as their IP address. |
|
| 60 |
+ if match := trailingHexNetmaskRE.FindStringIndex(ipv4Str); match != nil {
|
|
| 61 |
+ ipv4Str = ipv4Str[:match[0]] |
|
| 62 |
+ } |
|
| 63 |
+ |
|
| 64 |
+ // Parse as an IPv4 CIDR |
|
| 65 |
+ ipAddr, network, err := net.ParseCIDR(ipv4Str) |
|
| 66 |
+ if err == nil {
|
|
| 67 |
+ ipv4 := ipAddr.To4() |
|
| 68 |
+ if ipv4 == nil {
|
|
| 69 |
+ return IPv4Addr{}, fmt.Errorf("Unable to convert %s to an IPv4 address", ipv4Str)
|
|
| 70 |
+ } |
|
| 71 |
+ |
|
| 72 |
+ // If we see an IPv6 netmask, convert it to an IPv4 mask. |
|
| 73 |
+ netmaskSepPos := strings.LastIndexByte(ipv4Str, '/') |
|
| 74 |
+ if netmaskSepPos != -1 && netmaskSepPos+1 < len(ipv4Str) {
|
|
| 75 |
+ netMask, err := strconv.ParseUint(ipv4Str[netmaskSepPos+1:], 10, 8) |
|
| 76 |
+ if err != nil {
|
|
| 77 |
+ return IPv4Addr{}, fmt.Errorf("Unable to convert %s to an IPv4 address: unable to parse CIDR netmask: %v", ipv4Str, err)
|
|
| 78 |
+ } else if netMask > 128 {
|
|
| 79 |
+ return IPv4Addr{}, fmt.Errorf("Unable to convert %s to an IPv4 address: invalid CIDR netmask", ipv4Str)
|
|
| 80 |
+ } |
|
| 81 |
+ |
|
| 82 |
+ if netMask >= 96 {
|
|
| 83 |
+ // Convert the IPv6 netmask to an IPv4 netmask |
|
| 84 |
+ network.Mask = net.CIDRMask(int(netMask-96), IPv4len*8) |
|
| 85 |
+ } |
|
| 86 |
+ } |
|
| 87 |
+ ipv4Addr := IPv4Addr{
|
|
| 88 |
+ Address: IPv4Address(binary.BigEndian.Uint32(ipv4)), |
|
| 89 |
+ Mask: IPv4Mask(binary.BigEndian.Uint32(network.Mask)), |
|
| 90 |
+ } |
|
| 91 |
+ return ipv4Addr, nil |
|
| 92 |
+ } |
|
| 93 |
+ |
|
| 94 |
+ // Attempt to parse ipv4Str as a /32 host with a port number. |
|
| 95 |
+ tcpAddr, err := net.ResolveTCPAddr("tcp4", ipv4Str)
|
|
| 96 |
+ if err == nil {
|
|
| 97 |
+ ipv4 := tcpAddr.IP.To4() |
|
| 98 |
+ if ipv4 == nil {
|
|
| 99 |
+ return IPv4Addr{}, fmt.Errorf("Unable to resolve %+q as an IPv4 address", ipv4Str)
|
|
| 100 |
+ } |
|
| 101 |
+ |
|
| 102 |
+ ipv4Uint32 := binary.BigEndian.Uint32(ipv4) |
|
| 103 |
+ ipv4Addr := IPv4Addr{
|
|
| 104 |
+ Address: IPv4Address(ipv4Uint32), |
|
| 105 |
+ Mask: IPv4HostMask, |
|
| 106 |
+ Port: IPPort(tcpAddr.Port), |
|
| 107 |
+ } |
|
| 108 |
+ |
|
| 109 |
+ return ipv4Addr, nil |
|
| 110 |
+ } |
|
| 111 |
+ |
|
| 112 |
+ // Parse as a naked IPv4 address |
|
| 113 |
+ ip := net.ParseIP(ipv4Str) |
|
| 114 |
+ if ip != nil {
|
|
| 115 |
+ ipv4 := ip.To4() |
|
| 116 |
+ if ipv4 == nil {
|
|
| 117 |
+ return IPv4Addr{}, fmt.Errorf("Unable to string convert %+q to an IPv4 address", ipv4Str)
|
|
| 118 |
+ } |
|
| 119 |
+ |
|
| 120 |
+ ipv4Uint32 := binary.BigEndian.Uint32(ipv4) |
|
| 121 |
+ ipv4Addr := IPv4Addr{
|
|
| 122 |
+ Address: IPv4Address(ipv4Uint32), |
|
| 123 |
+ Mask: IPv4HostMask, |
|
| 124 |
+ } |
|
| 125 |
+ return ipv4Addr, nil |
|
| 126 |
+ } |
|
| 127 |
+ |
|
| 128 |
+ return IPv4Addr{}, fmt.Errorf("Unable to parse %+q to an IPv4 address: %v", ipv4Str, err)
|
|
| 129 |
+} |
|
| 130 |
+ |
|
| 131 |
+// AddressBinString returns a string with the IPv4Addr's Address represented |
|
| 132 |
+// as a sequence of '0' and '1' characters. This method is useful for |
|
| 133 |
+// debugging or by operators who want to inspect an address. |
|
| 134 |
+func (ipv4 IPv4Addr) AddressBinString() string {
|
|
| 135 |
+ return fmt.Sprintf("%032s", strconv.FormatUint(uint64(ipv4.Address), 2))
|
|
| 136 |
+} |
|
| 137 |
+ |
|
| 138 |
+// AddressHexString returns a string with the IPv4Addr address represented as |
|
| 139 |
+// a sequence of hex characters. This method is useful for debugging or by |
|
| 140 |
+// operators who want to inspect an address. |
|
| 141 |
+func (ipv4 IPv4Addr) AddressHexString() string {
|
|
| 142 |
+ return fmt.Sprintf("%08s", strconv.FormatUint(uint64(ipv4.Address), 16))
|
|
| 143 |
+} |
|
| 144 |
+ |
|
| 145 |
+// Broadcast is an IPv4Addr-only method that returns the broadcast address of |
|
| 146 |
+// the network. |
|
| 147 |
+// |
|
| 148 |
+// NOTE: IPv6 only supports multicast, so this method only exists for |
|
| 149 |
+// IPv4Addr. |
|
| 150 |
+func (ipv4 IPv4Addr) Broadcast() IPAddr {
|
|
| 151 |
+ // Nothing should listen on a broadcast address. |
|
| 152 |
+ return IPv4Addr{
|
|
| 153 |
+ Address: IPv4Address(ipv4.BroadcastAddress()), |
|
| 154 |
+ Mask: IPv4HostMask, |
|
| 155 |
+ } |
|
| 156 |
+} |
|
| 157 |
+ |
|
| 158 |
+// BroadcastAddress returns a IPv4Network of the IPv4Addr's broadcast |
|
| 159 |
+// address. |
|
| 160 |
+func (ipv4 IPv4Addr) BroadcastAddress() IPv4Network {
|
|
| 161 |
+ return IPv4Network(uint32(ipv4.Address)&uint32(ipv4.Mask) | ^uint32(ipv4.Mask)) |
|
| 162 |
+} |
|
| 163 |
+ |
|
| 164 |
+// CmpAddress follows the Cmp() standard protocol and returns: |
|
| 165 |
+// |
|
| 166 |
+// - -1 If the receiver should sort first because its address is lower than arg |
|
| 167 |
+// - 0 if the SockAddr arg is equal to the receiving IPv4Addr or the argument is |
|
| 168 |
+// of a different type. |
|
| 169 |
+// - 1 If the argument should sort first. |
|
| 170 |
+func (ipv4 IPv4Addr) CmpAddress(sa SockAddr) int {
|
|
| 171 |
+ ipv4b, ok := sa.(IPv4Addr) |
|
| 172 |
+ if !ok {
|
|
| 173 |
+ return sortDeferDecision |
|
| 174 |
+ } |
|
| 175 |
+ |
|
| 176 |
+ switch {
|
|
| 177 |
+ case ipv4.Address == ipv4b.Address: |
|
| 178 |
+ return sortDeferDecision |
|
| 179 |
+ case ipv4.Address < ipv4b.Address: |
|
| 180 |
+ return sortReceiverBeforeArg |
|
| 181 |
+ default: |
|
| 182 |
+ return sortArgBeforeReceiver |
|
| 183 |
+ } |
|
| 184 |
+} |
|
| 185 |
+ |
|
| 186 |
+// CmpPort follows the Cmp() standard protocol and returns: |
|
| 187 |
+// |
|
| 188 |
+// - -1 If the receiver should sort first because its port is lower than arg |
|
| 189 |
+// - 0 if the SockAddr arg's port number is equal to the receiving IPv4Addr, |
|
| 190 |
+// regardless of type. |
|
| 191 |
+// - 1 If the argument should sort first. |
|
| 192 |
+func (ipv4 IPv4Addr) CmpPort(sa SockAddr) int {
|
|
| 193 |
+ var saPort IPPort |
|
| 194 |
+ switch v := sa.(type) {
|
|
| 195 |
+ case IPv4Addr: |
|
| 196 |
+ saPort = v.Port |
|
| 197 |
+ case IPv6Addr: |
|
| 198 |
+ saPort = v.Port |
|
| 199 |
+ default: |
|
| 200 |
+ return sortDeferDecision |
|
| 201 |
+ } |
|
| 202 |
+ |
|
| 203 |
+ switch {
|
|
| 204 |
+ case ipv4.Port == saPort: |
|
| 205 |
+ return sortDeferDecision |
|
| 206 |
+ case ipv4.Port < saPort: |
|
| 207 |
+ return sortReceiverBeforeArg |
|
| 208 |
+ default: |
|
| 209 |
+ return sortArgBeforeReceiver |
|
| 210 |
+ } |
|
| 211 |
+} |
|
| 212 |
+ |
|
| 213 |
+// CmpRFC follows the Cmp() standard protocol and returns: |
|
| 214 |
+// |
|
| 215 |
+// - -1 If the receiver should sort first because it belongs to the RFC and its |
|
| 216 |
+// arg does not |
|
| 217 |
+// - 0 if the receiver and arg both belong to the same RFC or neither do. |
|
| 218 |
+// - 1 If the arg belongs to the RFC but receiver does not. |
|
| 219 |
+func (ipv4 IPv4Addr) CmpRFC(rfcNum uint, sa SockAddr) int {
|
|
| 220 |
+ recvInRFC := IsRFC(rfcNum, ipv4) |
|
| 221 |
+ ipv4b, ok := sa.(IPv4Addr) |
|
| 222 |
+ if !ok {
|
|
| 223 |
+ // If the receiver is part of the desired RFC and the SockAddr |
|
| 224 |
+ // argument is not, return -1 so that the receiver sorts before |
|
| 225 |
+ // the non-IPv4 SockAddr. Conversely, if the receiver is not |
|
| 226 |
+ // part of the RFC, punt on sorting and leave it for the next |
|
| 227 |
+ // sorter. |
|
| 228 |
+ if recvInRFC {
|
|
| 229 |
+ return sortReceiverBeforeArg |
|
| 230 |
+ } else {
|
|
| 231 |
+ return sortDeferDecision |
|
| 232 |
+ } |
|
| 233 |
+ } |
|
| 234 |
+ |
|
| 235 |
+ argInRFC := IsRFC(rfcNum, ipv4b) |
|
| 236 |
+ switch {
|
|
| 237 |
+ case (recvInRFC && argInRFC), (!recvInRFC && !argInRFC): |
|
| 238 |
+ // If a and b both belong to the RFC, or neither belong to |
|
| 239 |
+ // rfcNum, defer sorting to the next sorter. |
|
| 240 |
+ return sortDeferDecision |
|
| 241 |
+ case recvInRFC && !argInRFC: |
|
| 242 |
+ return sortReceiverBeforeArg |
|
| 243 |
+ default: |
|
| 244 |
+ return sortArgBeforeReceiver |
|
| 245 |
+ } |
|
| 246 |
+} |
|
| 247 |
+ |
|
| 248 |
+// Contains returns true if the SockAddr is contained within the receiver. |
|
| 249 |
+func (ipv4 IPv4Addr) Contains(sa SockAddr) bool {
|
|
| 250 |
+ ipv4b, ok := sa.(IPv4Addr) |
|
| 251 |
+ if !ok {
|
|
| 252 |
+ return false |
|
| 253 |
+ } |
|
| 254 |
+ |
|
| 255 |
+ return ipv4.ContainsNetwork(ipv4b) |
|
| 256 |
+} |
|
| 257 |
+ |
|
| 258 |
+// ContainsAddress returns true if the IPv4Address is contained within the |
|
| 259 |
+// receiver. |
|
| 260 |
+func (ipv4 IPv4Addr) ContainsAddress(x IPv4Address) bool {
|
|
| 261 |
+ return IPv4Address(ipv4.NetworkAddress()) <= x && |
|
| 262 |
+ IPv4Address(ipv4.BroadcastAddress()) >= x |
|
| 263 |
+} |
|
| 264 |
+ |
|
| 265 |
+// ContainsNetwork returns true if the network from IPv4Addr is contained |
|
| 266 |
+// within the receiver. |
|
| 267 |
+func (ipv4 IPv4Addr) ContainsNetwork(x IPv4Addr) bool {
|
|
| 268 |
+ return ipv4.NetworkAddress() <= x.NetworkAddress() && |
|
| 269 |
+ ipv4.BroadcastAddress() >= x.BroadcastAddress() |
|
| 270 |
+} |
|
| 271 |
+ |
|
| 272 |
+// DialPacketArgs returns the arguments required to be passed to |
|
| 273 |
+// net.DialUDP(). If the Mask of ipv4 is not a /32 or the Port is 0, |
|
| 274 |
+// DialPacketArgs() will fail. See Host() to create an IPv4Addr with its |
|
| 275 |
+// mask set to /32. |
|
| 276 |
+func (ipv4 IPv4Addr) DialPacketArgs() (network, dialArgs string) {
|
|
| 277 |
+ if ipv4.Mask != IPv4HostMask || ipv4.Port == 0 {
|
|
| 278 |
+ return "udp4", "" |
|
| 279 |
+ } |
|
| 280 |
+ return "udp4", fmt.Sprintf("%s:%d", ipv4.NetIP().String(), ipv4.Port)
|
|
| 281 |
+} |
|
| 282 |
+ |
|
| 283 |
+// DialStreamArgs returns the arguments required to be passed to |
|
| 284 |
+// net.DialTCP(). If the Mask of ipv4 is not a /32 or the Port is 0, |
|
| 285 |
+// DialStreamArgs() will fail. See Host() to create an IPv4Addr with its |
|
| 286 |
+// mask set to /32. |
|
| 287 |
+func (ipv4 IPv4Addr) DialStreamArgs() (network, dialArgs string) {
|
|
| 288 |
+ if ipv4.Mask != IPv4HostMask || ipv4.Port == 0 {
|
|
| 289 |
+ return "tcp4", "" |
|
| 290 |
+ } |
|
| 291 |
+ return "tcp4", fmt.Sprintf("%s:%d", ipv4.NetIP().String(), ipv4.Port)
|
|
| 292 |
+} |
|
| 293 |
+ |
|
| 294 |
+// Equal returns true if a SockAddr is equal to the receiving IPv4Addr. |
|
| 295 |
+func (ipv4 IPv4Addr) Equal(sa SockAddr) bool {
|
|
| 296 |
+ ipv4b, ok := sa.(IPv4Addr) |
|
| 297 |
+ if !ok {
|
|
| 298 |
+ return false |
|
| 299 |
+ } |
|
| 300 |
+ |
|
| 301 |
+ if ipv4.Port != ipv4b.Port {
|
|
| 302 |
+ return false |
|
| 303 |
+ } |
|
| 304 |
+ |
|
| 305 |
+ if ipv4.Address != ipv4b.Address {
|
|
| 306 |
+ return false |
|
| 307 |
+ } |
|
| 308 |
+ |
|
| 309 |
+ if ipv4.NetIPNet().String() != ipv4b.NetIPNet().String() {
|
|
| 310 |
+ return false |
|
| 311 |
+ } |
|
| 312 |
+ |
|
| 313 |
+ return true |
|
| 314 |
+} |
|
| 315 |
+ |
|
| 316 |
+// FirstUsable returns an IPv4Addr set to the first address following the |
|
| 317 |
+// network prefix. The first usable address in a network is normally the |
|
| 318 |
+// gateway and should not be used except by devices forwarding packets |
|
| 319 |
+// between two administratively distinct networks (i.e. a router). This |
|
| 320 |
+// function does not discriminate against first usable vs "first address that |
|
| 321 |
+// should be used." For example, FirstUsable() on "192.168.1.10/24" would |
|
| 322 |
+// return the address "192.168.1.1/24". |
|
| 323 |
+func (ipv4 IPv4Addr) FirstUsable() IPAddr {
|
|
| 324 |
+ addr := ipv4.NetworkAddress() |
|
| 325 |
+ |
|
| 326 |
+ // If /32, return the address itself. If /31 assume a point-to-point |
|
| 327 |
+ // link and return the lower address. |
|
| 328 |
+ if ipv4.Maskbits() < 31 {
|
|
| 329 |
+ addr++ |
|
| 330 |
+ } |
|
| 331 |
+ |
|
| 332 |
+ return IPv4Addr{
|
|
| 333 |
+ Address: IPv4Address(addr), |
|
| 334 |
+ Mask: IPv4HostMask, |
|
| 335 |
+ } |
|
| 336 |
+} |
|
| 337 |
+ |
|
| 338 |
+// Host returns a copy of ipv4 with its mask set to /32 so that it can be |
|
| 339 |
+// used by DialPacketArgs(), DialStreamArgs(), ListenPacketArgs(), or |
|
| 340 |
+// ListenStreamArgs(). |
|
| 341 |
+func (ipv4 IPv4Addr) Host() IPAddr {
|
|
| 342 |
+ // Nothing should listen on a broadcast address. |
|
| 343 |
+ return IPv4Addr{
|
|
| 344 |
+ Address: ipv4.Address, |
|
| 345 |
+ Mask: IPv4HostMask, |
|
| 346 |
+ Port: ipv4.Port, |
|
| 347 |
+ } |
|
| 348 |
+} |
|
| 349 |
+ |
|
| 350 |
+// IPPort returns the Port number attached to the IPv4Addr |
|
| 351 |
+func (ipv4 IPv4Addr) IPPort() IPPort {
|
|
| 352 |
+ return ipv4.Port |
|
| 353 |
+} |
|
| 354 |
+ |
|
| 355 |
+// LastUsable returns the last address before the broadcast address in a |
|
| 356 |
+// given network. |
|
| 357 |
+func (ipv4 IPv4Addr) LastUsable() IPAddr {
|
|
| 358 |
+ addr := ipv4.BroadcastAddress() |
|
| 359 |
+ |
|
| 360 |
+ // If /32, return the address itself. If /31 assume a point-to-point |
|
| 361 |
+ // link and return the upper address. |
|
| 362 |
+ if ipv4.Maskbits() < 31 {
|
|
| 363 |
+ addr-- |
|
| 364 |
+ } |
|
| 365 |
+ |
|
| 366 |
+ return IPv4Addr{
|
|
| 367 |
+ Address: IPv4Address(addr), |
|
| 368 |
+ Mask: IPv4HostMask, |
|
| 369 |
+ } |
|
| 370 |
+} |
|
| 371 |
+ |
|
| 372 |
+// ListenPacketArgs returns the arguments required to be passed to |
|
| 373 |
+// net.ListenUDP(). If the Mask of ipv4 is not a /32, ListenPacketArgs() |
|
| 374 |
+// will fail. See Host() to create an IPv4Addr with its mask set to /32. |
|
| 375 |
+func (ipv4 IPv4Addr) ListenPacketArgs() (network, listenArgs string) {
|
|
| 376 |
+ if ipv4.Mask != IPv4HostMask {
|
|
| 377 |
+ return "udp4", "" |
|
| 378 |
+ } |
|
| 379 |
+ return "udp4", fmt.Sprintf("%s:%d", ipv4.NetIP().String(), ipv4.Port)
|
|
| 380 |
+} |
|
| 381 |
+ |
|
| 382 |
+// ListenStreamArgs returns the arguments required to be passed to |
|
| 383 |
+// net.ListenTCP(). If the Mask of ipv4 is not a /32, ListenStreamArgs() |
|
| 384 |
+// will fail. See Host() to create an IPv4Addr with its mask set to /32. |
|
| 385 |
+func (ipv4 IPv4Addr) ListenStreamArgs() (network, listenArgs string) {
|
|
| 386 |
+ if ipv4.Mask != IPv4HostMask {
|
|
| 387 |
+ return "tcp4", "" |
|
| 388 |
+ } |
|
| 389 |
+ return "tcp4", fmt.Sprintf("%s:%d", ipv4.NetIP().String(), ipv4.Port)
|
|
| 390 |
+} |
|
| 391 |
+ |
|
| 392 |
+// Maskbits returns the number of network mask bits in a given IPv4Addr. For |
|
| 393 |
+// example, the Maskbits() of "192.168.1.1/24" would return 24. |
|
| 394 |
+func (ipv4 IPv4Addr) Maskbits() int {
|
|
| 395 |
+ mask := make(net.IPMask, IPv4len) |
|
| 396 |
+ binary.BigEndian.PutUint32(mask, uint32(ipv4.Mask)) |
|
| 397 |
+ maskOnes, _ := mask.Size() |
|
| 398 |
+ return maskOnes |
|
| 399 |
+} |
|
| 400 |
+ |
|
| 401 |
+// MustIPv4Addr is a helper method that must return an IPv4Addr or panic on |
|
| 402 |
+// invalid input. |
|
| 403 |
+func MustIPv4Addr(addr string) IPv4Addr {
|
|
| 404 |
+ ipv4, err := NewIPv4Addr(addr) |
|
| 405 |
+ if err != nil {
|
|
| 406 |
+ panic(fmt.Sprintf("Unable to create an IPv4Addr from %+q: %v", addr, err))
|
|
| 407 |
+ } |
|
| 408 |
+ return ipv4 |
|
| 409 |
+} |
|
| 410 |
+ |
|
| 411 |
+// NetIP returns the address as a net.IP (address is always presized to |
|
| 412 |
+// IPv4). |
|
| 413 |
+func (ipv4 IPv4Addr) NetIP() *net.IP {
|
|
| 414 |
+ x := make(net.IP, IPv4len) |
|
| 415 |
+ binary.BigEndian.PutUint32(x, uint32(ipv4.Address)) |
|
| 416 |
+ return &x |
|
| 417 |
+} |
|
| 418 |
+ |
|
| 419 |
+// NetIPMask create a new net.IPMask from the IPv4Addr. |
|
| 420 |
+func (ipv4 IPv4Addr) NetIPMask() *net.IPMask {
|
|
| 421 |
+ ipv4Mask := net.IPMask{}
|
|
| 422 |
+ ipv4Mask = make(net.IPMask, IPv4len) |
|
| 423 |
+ binary.BigEndian.PutUint32(ipv4Mask, uint32(ipv4.Mask)) |
|
| 424 |
+ return &ipv4Mask |
|
| 425 |
+} |
|
| 426 |
+ |
|
| 427 |
+// NetIPNet create a new net.IPNet from the IPv4Addr. |
|
| 428 |
+func (ipv4 IPv4Addr) NetIPNet() *net.IPNet {
|
|
| 429 |
+ ipv4net := &net.IPNet{}
|
|
| 430 |
+ ipv4net.IP = make(net.IP, IPv4len) |
|
| 431 |
+ binary.BigEndian.PutUint32(ipv4net.IP, uint32(ipv4.NetworkAddress())) |
|
| 432 |
+ ipv4net.Mask = *ipv4.NetIPMask() |
|
| 433 |
+ return ipv4net |
|
| 434 |
+} |
|
| 435 |
+ |
|
| 436 |
+// Network returns the network prefix or network address for a given network. |
|
| 437 |
+func (ipv4 IPv4Addr) Network() IPAddr {
|
|
| 438 |
+ return IPv4Addr{
|
|
| 439 |
+ Address: IPv4Address(ipv4.NetworkAddress()), |
|
| 440 |
+ Mask: ipv4.Mask, |
|
| 441 |
+ } |
|
| 442 |
+} |
|
| 443 |
+ |
|
| 444 |
+// NetworkAddress returns an IPv4Network of the IPv4Addr's network address. |
|
| 445 |
+func (ipv4 IPv4Addr) NetworkAddress() IPv4Network {
|
|
| 446 |
+ return IPv4Network(uint32(ipv4.Address) & uint32(ipv4.Mask)) |
|
| 447 |
+} |
|
| 448 |
+ |
|
| 449 |
+// Octets returns a slice of the four octets in an IPv4Addr's Address. The |
|
| 450 |
+// order of the bytes is big endian. |
|
| 451 |
+func (ipv4 IPv4Addr) Octets() []int {
|
|
| 452 |
+ return []int{
|
|
| 453 |
+ int(ipv4.Address >> 24), |
|
| 454 |
+ int((ipv4.Address >> 16) & 0xff), |
|
| 455 |
+ int((ipv4.Address >> 8) & 0xff), |
|
| 456 |
+ int(ipv4.Address & 0xff), |
|
| 457 |
+ } |
|
| 458 |
+} |
|
| 459 |
+ |
|
| 460 |
+// String returns a string representation of the IPv4Addr |
|
| 461 |
+func (ipv4 IPv4Addr) String() string {
|
|
| 462 |
+ if ipv4.Port != 0 {
|
|
| 463 |
+ return fmt.Sprintf("%s:%d", ipv4.NetIP().String(), ipv4.Port)
|
|
| 464 |
+ } |
|
| 465 |
+ |
|
| 466 |
+ if ipv4.Maskbits() == 32 {
|
|
| 467 |
+ return ipv4.NetIP().String() |
|
| 468 |
+ } |
|
| 469 |
+ |
|
| 470 |
+ return fmt.Sprintf("%s/%d", ipv4.NetIP().String(), ipv4.Maskbits())
|
|
| 471 |
+} |
|
| 472 |
+ |
|
| 473 |
+// Type is used as a type switch and returns TypeIPv4 |
|
| 474 |
+func (IPv4Addr) Type() SockAddrType {
|
|
| 475 |
+ return TypeIPv4 |
|
| 476 |
+} |
|
| 477 |
+ |
|
| 478 |
+// IPv4AddrAttr returns a string representation of an attribute for the given |
|
| 479 |
+// IPv4Addr. |
|
| 480 |
+func IPv4AddrAttr(ipv4 IPv4Addr, selector AttrName) string {
|
|
| 481 |
+ fn, found := ipv4AddrAttrMap[selector] |
|
| 482 |
+ if !found {
|
|
| 483 |
+ return "" |
|
| 484 |
+ } |
|
| 485 |
+ |
|
| 486 |
+ return fn(ipv4) |
|
| 487 |
+} |
|
| 488 |
+ |
|
| 489 |
+// IPv4Attrs returns a list of attributes supported by the IPv4Addr type |
|
| 490 |
+func IPv4Attrs() []AttrName {
|
|
| 491 |
+ return ipv4AddrAttrs |
|
| 492 |
+} |
|
| 493 |
+ |
|
| 494 |
+// ipv4AddrInit is called once at init() |
|
| 495 |
+func ipv4AddrInit() {
|
|
| 496 |
+ // Sorted for human readability |
|
| 497 |
+ ipv4AddrAttrs = []AttrName{
|
|
| 498 |
+ "size", // Same position as in IPv6 for output consistency |
|
| 499 |
+ "broadcast", |
|
| 500 |
+ "uint32", |
|
| 501 |
+ } |
|
| 502 |
+ |
|
| 503 |
+ ipv4AddrAttrMap = map[AttrName]func(ipv4 IPv4Addr) string{
|
|
| 504 |
+ "broadcast": func(ipv4 IPv4Addr) string {
|
|
| 505 |
+ return ipv4.Broadcast().String() |
|
| 506 |
+ }, |
|
| 507 |
+ "size": func(ipv4 IPv4Addr) string {
|
|
| 508 |
+ return fmt.Sprintf("%d", 1<<uint(IPv4len*8-ipv4.Maskbits()))
|
|
| 509 |
+ }, |
|
| 510 |
+ "uint32": func(ipv4 IPv4Addr) string {
|
|
| 511 |
+ return fmt.Sprintf("%d", uint32(ipv4.Address))
|
|
| 512 |
+ }, |
|
| 513 |
+ } |
|
| 514 |
+} |
| 0 | 515 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,591 @@ |
| 0 |
+package sockaddr |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "bytes" |
|
| 4 |
+ "encoding/binary" |
|
| 5 |
+ "fmt" |
|
| 6 |
+ "math/big" |
|
| 7 |
+ "net" |
|
| 8 |
+) |
|
| 9 |
+ |
|
| 10 |
+type ( |
|
| 11 |
+ // IPv6Address is a named type representing an IPv6 address. |
|
| 12 |
+ IPv6Address *big.Int |
|
| 13 |
+ |
|
| 14 |
+ // IPv6Network is a named type representing an IPv6 network. |
|
| 15 |
+ IPv6Network *big.Int |
|
| 16 |
+ |
|
| 17 |
+ // IPv6Mask is a named type representing an IPv6 network mask. |
|
| 18 |
+ IPv6Mask *big.Int |
|
| 19 |
+) |
|
| 20 |
+ |
|
| 21 |
+// IPv6HostPrefix is a constant represents a /128 IPv6 Prefix. |
|
| 22 |
+const IPv6HostPrefix = IPPrefixLen(128) |
|
| 23 |
+ |
|
| 24 |
+// ipv6HostMask is an unexported big.Int representing a /128 IPv6 address. |
|
| 25 |
+// This value must be a constant and always set to all ones. |
|
| 26 |
+var ipv6HostMask IPv6Mask |
|
| 27 |
+ |
|
| 28 |
+// ipv6AddrAttrMap is a map of the IPv6Addr type-specific attributes. |
|
| 29 |
+var ipv6AddrAttrMap map[AttrName]func(IPv6Addr) string |
|
| 30 |
+var ipv6AddrAttrs []AttrName |
|
| 31 |
+ |
|
| 32 |
+func init() {
|
|
| 33 |
+ biMask := new(big.Int) |
|
| 34 |
+ biMask.SetBytes([]byte{
|
|
| 35 |
+ 0xff, 0xff, |
|
| 36 |
+ 0xff, 0xff, |
|
| 37 |
+ 0xff, 0xff, |
|
| 38 |
+ 0xff, 0xff, |
|
| 39 |
+ 0xff, 0xff, |
|
| 40 |
+ 0xff, 0xff, |
|
| 41 |
+ 0xff, 0xff, |
|
| 42 |
+ 0xff, 0xff, |
|
| 43 |
+ }, |
|
| 44 |
+ ) |
|
| 45 |
+ ipv6HostMask = IPv6Mask(biMask) |
|
| 46 |
+ |
|
| 47 |
+ ipv6AddrInit() |
|
| 48 |
+} |
|
| 49 |
+ |
|
| 50 |
+// IPv6Addr implements a convenience wrapper around the union of Go's |
|
| 51 |
+// built-in net.IP and net.IPNet types. In UNIX-speak, IPv6Addr implements |
|
| 52 |
+// `sockaddr` when the the address family is set to AF_INET6 |
|
| 53 |
+// (i.e. `sockaddr_in6`). |
|
| 54 |
+type IPv6Addr struct {
|
|
| 55 |
+ IPAddr |
|
| 56 |
+ Address IPv6Address |
|
| 57 |
+ Mask IPv6Mask |
|
| 58 |
+ Port IPPort |
|
| 59 |
+} |
|
| 60 |
+ |
|
| 61 |
+// NewIPv6Addr creates an IPv6Addr from a string. String can be in the form of |
|
| 62 |
+// an an IPv6:port (e.g. `[2001:4860:0:2001::68]:80`, in which case the mask is |
|
| 63 |
+// assumed to be a /128), an IPv6 address (e.g. `2001:4860:0:2001::68`, also |
|
| 64 |
+// with a `/128` mask), an IPv6 CIDR (e.g. `2001:4860:0:2001::68/64`, which has |
|
| 65 |
+// its IP port initialized to zero). ipv6Str can not be a hostname. |
|
| 66 |
+// |
|
| 67 |
+// NOTE: Many net.*() routines will initialize and return an IPv4 address. |
|
| 68 |
+// Always test to make sure the address returned cannot be converted to a 4 byte |
|
| 69 |
+// array using To4(). |
|
| 70 |
+func NewIPv6Addr(ipv6Str string) (IPv6Addr, error) {
|
|
| 71 |
+ v6Addr := false |
|
| 72 |
+LOOP: |
|
| 73 |
+ for i := 0; i < len(ipv6Str); i++ {
|
|
| 74 |
+ switch ipv6Str[i] {
|
|
| 75 |
+ case '.': |
|
| 76 |
+ break LOOP |
|
| 77 |
+ case ':': |
|
| 78 |
+ v6Addr = true |
|
| 79 |
+ break LOOP |
|
| 80 |
+ } |
|
| 81 |
+ } |
|
| 82 |
+ |
|
| 83 |
+ if !v6Addr {
|
|
| 84 |
+ return IPv6Addr{}, fmt.Errorf("Unable to resolve %+q as an IPv6 address, appears to be an IPv4 address", ipv6Str)
|
|
| 85 |
+ } |
|
| 86 |
+ |
|
| 87 |
+ // Attempt to parse ipv6Str as a /128 host with a port number. |
|
| 88 |
+ tcpAddr, err := net.ResolveTCPAddr("tcp6", ipv6Str)
|
|
| 89 |
+ if err == nil {
|
|
| 90 |
+ ipv6 := tcpAddr.IP.To16() |
|
| 91 |
+ if ipv6 == nil {
|
|
| 92 |
+ return IPv6Addr{}, fmt.Errorf("Unable to resolve %+q as a 16byte IPv6 address", ipv6Str)
|
|
| 93 |
+ } |
|
| 94 |
+ |
|
| 95 |
+ ipv6BigIntAddr := new(big.Int) |
|
| 96 |
+ ipv6BigIntAddr.SetBytes(ipv6) |
|
| 97 |
+ |
|
| 98 |
+ ipv6BigIntMask := new(big.Int) |
|
| 99 |
+ ipv6BigIntMask.Set(ipv6HostMask) |
|
| 100 |
+ |
|
| 101 |
+ ipv6Addr := IPv6Addr{
|
|
| 102 |
+ Address: IPv6Address(ipv6BigIntAddr), |
|
| 103 |
+ Mask: IPv6Mask(ipv6BigIntMask), |
|
| 104 |
+ Port: IPPort(tcpAddr.Port), |
|
| 105 |
+ } |
|
| 106 |
+ |
|
| 107 |
+ return ipv6Addr, nil |
|
| 108 |
+ } |
|
| 109 |
+ |
|
| 110 |
+ // Parse as a naked IPv6 address. Trim square brackets if present. |
|
| 111 |
+ if len(ipv6Str) > 2 && ipv6Str[0] == '[' && ipv6Str[len(ipv6Str)-1] == ']' {
|
|
| 112 |
+ ipv6Str = ipv6Str[1 : len(ipv6Str)-1] |
|
| 113 |
+ } |
|
| 114 |
+ ip := net.ParseIP(ipv6Str) |
|
| 115 |
+ if ip != nil {
|
|
| 116 |
+ ipv6 := ip.To16() |
|
| 117 |
+ if ipv6 == nil {
|
|
| 118 |
+ return IPv6Addr{}, fmt.Errorf("Unable to string convert %+q to a 16byte IPv6 address", ipv6Str)
|
|
| 119 |
+ } |
|
| 120 |
+ |
|
| 121 |
+ ipv6BigIntAddr := new(big.Int) |
|
| 122 |
+ ipv6BigIntAddr.SetBytes(ipv6) |
|
| 123 |
+ |
|
| 124 |
+ ipv6BigIntMask := new(big.Int) |
|
| 125 |
+ ipv6BigIntMask.Set(ipv6HostMask) |
|
| 126 |
+ |
|
| 127 |
+ return IPv6Addr{
|
|
| 128 |
+ Address: IPv6Address(ipv6BigIntAddr), |
|
| 129 |
+ Mask: IPv6Mask(ipv6BigIntMask), |
|
| 130 |
+ }, nil |
|
| 131 |
+ } |
|
| 132 |
+ |
|
| 133 |
+ // Parse as an IPv6 CIDR |
|
| 134 |
+ ipAddr, network, err := net.ParseCIDR(ipv6Str) |
|
| 135 |
+ if err == nil {
|
|
| 136 |
+ ipv6 := ipAddr.To16() |
|
| 137 |
+ if ipv6 == nil {
|
|
| 138 |
+ return IPv6Addr{}, fmt.Errorf("Unable to convert %+q to a 16byte IPv6 address", ipv6Str)
|
|
| 139 |
+ } |
|
| 140 |
+ |
|
| 141 |
+ ipv6BigIntAddr := new(big.Int) |
|
| 142 |
+ ipv6BigIntAddr.SetBytes(ipv6) |
|
| 143 |
+ |
|
| 144 |
+ ipv6BigIntMask := new(big.Int) |
|
| 145 |
+ ipv6BigIntMask.SetBytes(network.Mask) |
|
| 146 |
+ |
|
| 147 |
+ ipv6Addr := IPv6Addr{
|
|
| 148 |
+ Address: IPv6Address(ipv6BigIntAddr), |
|
| 149 |
+ Mask: IPv6Mask(ipv6BigIntMask), |
|
| 150 |
+ } |
|
| 151 |
+ return ipv6Addr, nil |
|
| 152 |
+ } |
|
| 153 |
+ |
|
| 154 |
+ return IPv6Addr{}, fmt.Errorf("Unable to parse %+q to an IPv6 address: %v", ipv6Str, err)
|
|
| 155 |
+} |
|
| 156 |
+ |
|
| 157 |
+// AddressBinString returns a string with the IPv6Addr's Address represented |
|
| 158 |
+// as a sequence of '0' and '1' characters. This method is useful for |
|
| 159 |
+// debugging or by operators who want to inspect an address. |
|
| 160 |
+func (ipv6 IPv6Addr) AddressBinString() string {
|
|
| 161 |
+ bi := big.Int(*ipv6.Address) |
|
| 162 |
+ return fmt.Sprintf("%0128s", bi.Text(2))
|
|
| 163 |
+} |
|
| 164 |
+ |
|
| 165 |
+// AddressHexString returns a string with the IPv6Addr address represented as |
|
| 166 |
+// a sequence of hex characters. This method is useful for debugging or by |
|
| 167 |
+// operators who want to inspect an address. |
|
| 168 |
+func (ipv6 IPv6Addr) AddressHexString() string {
|
|
| 169 |
+ bi := big.Int(*ipv6.Address) |
|
| 170 |
+ return fmt.Sprintf("%032s", bi.Text(16))
|
|
| 171 |
+} |
|
| 172 |
+ |
|
| 173 |
+// CmpAddress follows the Cmp() standard protocol and returns: |
|
| 174 |
+// |
|
| 175 |
+// - -1 If the receiver should sort first because its address is lower than arg |
|
| 176 |
+// - 0 if the SockAddr arg equal to the receiving IPv6Addr or the argument is of a |
|
| 177 |
+// different type. |
|
| 178 |
+// - 1 If the argument should sort first. |
|
| 179 |
+func (ipv6 IPv6Addr) CmpAddress(sa SockAddr) int {
|
|
| 180 |
+ ipv6b, ok := sa.(IPv6Addr) |
|
| 181 |
+ if !ok {
|
|
| 182 |
+ return sortDeferDecision |
|
| 183 |
+ } |
|
| 184 |
+ |
|
| 185 |
+ ipv6aBigInt := new(big.Int) |
|
| 186 |
+ ipv6aBigInt.Set(ipv6.Address) |
|
| 187 |
+ ipv6bBigInt := new(big.Int) |
|
| 188 |
+ ipv6bBigInt.Set(ipv6b.Address) |
|
| 189 |
+ |
|
| 190 |
+ return ipv6aBigInt.Cmp(ipv6bBigInt) |
|
| 191 |
+} |
|
| 192 |
+ |
|
| 193 |
+// CmpPort follows the Cmp() standard protocol and returns: |
|
| 194 |
+// |
|
| 195 |
+// - -1 If the receiver should sort first because its port is lower than arg |
|
| 196 |
+// - 0 if the SockAddr arg's port number is equal to the receiving IPv6Addr, |
|
| 197 |
+// regardless of type. |
|
| 198 |
+// - 1 If the argument should sort first. |
|
| 199 |
+func (ipv6 IPv6Addr) CmpPort(sa SockAddr) int {
|
|
| 200 |
+ var saPort IPPort |
|
| 201 |
+ switch v := sa.(type) {
|
|
| 202 |
+ case IPv4Addr: |
|
| 203 |
+ saPort = v.Port |
|
| 204 |
+ case IPv6Addr: |
|
| 205 |
+ saPort = v.Port |
|
| 206 |
+ default: |
|
| 207 |
+ return sortDeferDecision |
|
| 208 |
+ } |
|
| 209 |
+ |
|
| 210 |
+ switch {
|
|
| 211 |
+ case ipv6.Port == saPort: |
|
| 212 |
+ return sortDeferDecision |
|
| 213 |
+ case ipv6.Port < saPort: |
|
| 214 |
+ return sortReceiverBeforeArg |
|
| 215 |
+ default: |
|
| 216 |
+ return sortArgBeforeReceiver |
|
| 217 |
+ } |
|
| 218 |
+} |
|
| 219 |
+ |
|
| 220 |
+// CmpRFC follows the Cmp() standard protocol and returns: |
|
| 221 |
+// |
|
| 222 |
+// - -1 If the receiver should sort first because it belongs to the RFC and its |
|
| 223 |
+// arg does not |
|
| 224 |
+// - 0 if the receiver and arg both belong to the same RFC or neither do. |
|
| 225 |
+// - 1 If the arg belongs to the RFC but receiver does not. |
|
| 226 |
+func (ipv6 IPv6Addr) CmpRFC(rfcNum uint, sa SockAddr) int {
|
|
| 227 |
+ recvInRFC := IsRFC(rfcNum, ipv6) |
|
| 228 |
+ ipv6b, ok := sa.(IPv6Addr) |
|
| 229 |
+ if !ok {
|
|
| 230 |
+ // If the receiver is part of the desired RFC and the SockAddr |
|
| 231 |
+ // argument is not, sort receiver before the non-IPv6 SockAddr. |
|
| 232 |
+ // Conversely, if the receiver is not part of the RFC, punt on |
|
| 233 |
+ // sorting and leave it for the next sorter. |
|
| 234 |
+ if recvInRFC {
|
|
| 235 |
+ return sortReceiverBeforeArg |
|
| 236 |
+ } else {
|
|
| 237 |
+ return sortDeferDecision |
|
| 238 |
+ } |
|
| 239 |
+ } |
|
| 240 |
+ |
|
| 241 |
+ argInRFC := IsRFC(rfcNum, ipv6b) |
|
| 242 |
+ switch {
|
|
| 243 |
+ case (recvInRFC && argInRFC), (!recvInRFC && !argInRFC): |
|
| 244 |
+ // If a and b both belong to the RFC, or neither belong to |
|
| 245 |
+ // rfcNum, defer sorting to the next sorter. |
|
| 246 |
+ return sortDeferDecision |
|
| 247 |
+ case recvInRFC && !argInRFC: |
|
| 248 |
+ return sortReceiverBeforeArg |
|
| 249 |
+ default: |
|
| 250 |
+ return sortArgBeforeReceiver |
|
| 251 |
+ } |
|
| 252 |
+} |
|
| 253 |
+ |
|
| 254 |
+// Contains returns true if the SockAddr is contained within the receiver. |
|
| 255 |
+func (ipv6 IPv6Addr) Contains(sa SockAddr) bool {
|
|
| 256 |
+ ipv6b, ok := sa.(IPv6Addr) |
|
| 257 |
+ if !ok {
|
|
| 258 |
+ return false |
|
| 259 |
+ } |
|
| 260 |
+ |
|
| 261 |
+ return ipv6.ContainsNetwork(ipv6b) |
|
| 262 |
+} |
|
| 263 |
+ |
|
| 264 |
+// ContainsAddress returns true if the IPv6Address is contained within the |
|
| 265 |
+// receiver. |
|
| 266 |
+func (ipv6 IPv6Addr) ContainsAddress(x IPv6Address) bool {
|
|
| 267 |
+ xAddr := IPv6Addr{
|
|
| 268 |
+ Address: x, |
|
| 269 |
+ Mask: ipv6HostMask, |
|
| 270 |
+ } |
|
| 271 |
+ |
|
| 272 |
+ {
|
|
| 273 |
+ xIPv6 := xAddr.FirstUsable().(IPv6Addr) |
|
| 274 |
+ yIPv6 := ipv6.FirstUsable().(IPv6Addr) |
|
| 275 |
+ if xIPv6.CmpAddress(yIPv6) >= 1 {
|
|
| 276 |
+ return false |
|
| 277 |
+ } |
|
| 278 |
+ } |
|
| 279 |
+ |
|
| 280 |
+ {
|
|
| 281 |
+ xIPv6 := xAddr.LastUsable().(IPv6Addr) |
|
| 282 |
+ yIPv6 := ipv6.LastUsable().(IPv6Addr) |
|
| 283 |
+ if xIPv6.CmpAddress(yIPv6) <= -1 {
|
|
| 284 |
+ return false |
|
| 285 |
+ } |
|
| 286 |
+ } |
|
| 287 |
+ return true |
|
| 288 |
+} |
|
| 289 |
+ |
|
| 290 |
+// ContainsNetwork returns true if the network from IPv6Addr is contained within |
|
| 291 |
+// the receiver. |
|
| 292 |
+func (x IPv6Addr) ContainsNetwork(y IPv6Addr) bool {
|
|
| 293 |
+ {
|
|
| 294 |
+ xIPv6 := x.FirstUsable().(IPv6Addr) |
|
| 295 |
+ yIPv6 := y.FirstUsable().(IPv6Addr) |
|
| 296 |
+ if ret := xIPv6.CmpAddress(yIPv6); ret >= 1 {
|
|
| 297 |
+ return false |
|
| 298 |
+ } |
|
| 299 |
+ } |
|
| 300 |
+ |
|
| 301 |
+ {
|
|
| 302 |
+ xIPv6 := x.LastUsable().(IPv6Addr) |
|
| 303 |
+ yIPv6 := y.LastUsable().(IPv6Addr) |
|
| 304 |
+ if ret := xIPv6.CmpAddress(yIPv6); ret <= -1 {
|
|
| 305 |
+ return false |
|
| 306 |
+ } |
|
| 307 |
+ } |
|
| 308 |
+ return true |
|
| 309 |
+} |
|
| 310 |
+ |
|
| 311 |
+// DialPacketArgs returns the arguments required to be passed to |
|
| 312 |
+// net.DialUDP(). If the Mask of ipv6 is not a /128 or the Port is 0, |
|
| 313 |
+// DialPacketArgs() will fail. See Host() to create an IPv6Addr with its |
|
| 314 |
+// mask set to /128. |
|
| 315 |
+func (ipv6 IPv6Addr) DialPacketArgs() (network, dialArgs string) {
|
|
| 316 |
+ ipv6Mask := big.Int(*ipv6.Mask) |
|
| 317 |
+ if ipv6Mask.Cmp(ipv6HostMask) != 0 || ipv6.Port == 0 {
|
|
| 318 |
+ return "udp6", "" |
|
| 319 |
+ } |
|
| 320 |
+ return "udp6", fmt.Sprintf("[%s]:%d", ipv6.NetIP().String(), ipv6.Port)
|
|
| 321 |
+} |
|
| 322 |
+ |
|
| 323 |
+// DialStreamArgs returns the arguments required to be passed to |
|
| 324 |
+// net.DialTCP(). If the Mask of ipv6 is not a /128 or the Port is 0, |
|
| 325 |
+// DialStreamArgs() will fail. See Host() to create an IPv6Addr with its |
|
| 326 |
+// mask set to /128. |
|
| 327 |
+func (ipv6 IPv6Addr) DialStreamArgs() (network, dialArgs string) {
|
|
| 328 |
+ ipv6Mask := big.Int(*ipv6.Mask) |
|
| 329 |
+ if ipv6Mask.Cmp(ipv6HostMask) != 0 || ipv6.Port == 0 {
|
|
| 330 |
+ return "tcp6", "" |
|
| 331 |
+ } |
|
| 332 |
+ return "tcp6", fmt.Sprintf("[%s]:%d", ipv6.NetIP().String(), ipv6.Port)
|
|
| 333 |
+} |
|
| 334 |
+ |
|
| 335 |
+// Equal returns true if a SockAddr is equal to the receiving IPv4Addr. |
|
| 336 |
+func (ipv6a IPv6Addr) Equal(sa SockAddr) bool {
|
|
| 337 |
+ ipv6b, ok := sa.(IPv6Addr) |
|
| 338 |
+ if !ok {
|
|
| 339 |
+ return false |
|
| 340 |
+ } |
|
| 341 |
+ |
|
| 342 |
+ if ipv6a.NetIP().String() != ipv6b.NetIP().String() {
|
|
| 343 |
+ return false |
|
| 344 |
+ } |
|
| 345 |
+ |
|
| 346 |
+ if ipv6a.NetIPNet().String() != ipv6b.NetIPNet().String() {
|
|
| 347 |
+ return false |
|
| 348 |
+ } |
|
| 349 |
+ |
|
| 350 |
+ if ipv6a.Port != ipv6b.Port {
|
|
| 351 |
+ return false |
|
| 352 |
+ } |
|
| 353 |
+ |
|
| 354 |
+ return true |
|
| 355 |
+} |
|
| 356 |
+ |
|
| 357 |
+// FirstUsable returns an IPv6Addr set to the first address following the |
|
| 358 |
+// network prefix. The first usable address in a network is normally the |
|
| 359 |
+// gateway and should not be used except by devices forwarding packets |
|
| 360 |
+// between two administratively distinct networks (i.e. a router). This |
|
| 361 |
+// function does not discriminate against first usable vs "first address that |
|
| 362 |
+// should be used." For example, FirstUsable() on "2001:0db8::0003/64" would |
|
| 363 |
+// return "2001:0db8::00011". |
|
| 364 |
+func (ipv6 IPv6Addr) FirstUsable() IPAddr {
|
|
| 365 |
+ return IPv6Addr{
|
|
| 366 |
+ Address: IPv6Address(ipv6.NetworkAddress()), |
|
| 367 |
+ Mask: ipv6HostMask, |
|
| 368 |
+ } |
|
| 369 |
+} |
|
| 370 |
+ |
|
| 371 |
+// Host returns a copy of ipv6 with its mask set to /128 so that it can be |
|
| 372 |
+// used by DialPacketArgs(), DialStreamArgs(), ListenPacketArgs(), or |
|
| 373 |
+// ListenStreamArgs(). |
|
| 374 |
+func (ipv6 IPv6Addr) Host() IPAddr {
|
|
| 375 |
+ // Nothing should listen on a broadcast address. |
|
| 376 |
+ return IPv6Addr{
|
|
| 377 |
+ Address: ipv6.Address, |
|
| 378 |
+ Mask: ipv6HostMask, |
|
| 379 |
+ Port: ipv6.Port, |
|
| 380 |
+ } |
|
| 381 |
+} |
|
| 382 |
+ |
|
| 383 |
+// IPPort returns the Port number attached to the IPv6Addr |
|
| 384 |
+func (ipv6 IPv6Addr) IPPort() IPPort {
|
|
| 385 |
+ return ipv6.Port |
|
| 386 |
+} |
|
| 387 |
+ |
|
| 388 |
+// LastUsable returns the last address in a given network. |
|
| 389 |
+func (ipv6 IPv6Addr) LastUsable() IPAddr {
|
|
| 390 |
+ addr := new(big.Int) |
|
| 391 |
+ addr.Set(ipv6.Address) |
|
| 392 |
+ |
|
| 393 |
+ mask := new(big.Int) |
|
| 394 |
+ mask.Set(ipv6.Mask) |
|
| 395 |
+ |
|
| 396 |
+ negMask := new(big.Int) |
|
| 397 |
+ negMask.Xor(ipv6HostMask, mask) |
|
| 398 |
+ |
|
| 399 |
+ lastAddr := new(big.Int) |
|
| 400 |
+ lastAddr.And(addr, mask) |
|
| 401 |
+ lastAddr.Or(lastAddr, negMask) |
|
| 402 |
+ |
|
| 403 |
+ return IPv6Addr{
|
|
| 404 |
+ Address: IPv6Address(lastAddr), |
|
| 405 |
+ Mask: ipv6HostMask, |
|
| 406 |
+ } |
|
| 407 |
+} |
|
| 408 |
+ |
|
| 409 |
+// ListenPacketArgs returns the arguments required to be passed to |
|
| 410 |
+// net.ListenUDP(). If the Mask of ipv6 is not a /128, ListenPacketArgs() |
|
| 411 |
+// will fail. See Host() to create an IPv6Addr with its mask set to /128. |
|
| 412 |
+func (ipv6 IPv6Addr) ListenPacketArgs() (network, listenArgs string) {
|
|
| 413 |
+ ipv6Mask := big.Int(*ipv6.Mask) |
|
| 414 |
+ if ipv6Mask.Cmp(ipv6HostMask) != 0 {
|
|
| 415 |
+ return "udp6", "" |
|
| 416 |
+ } |
|
| 417 |
+ return "udp6", fmt.Sprintf("[%s]:%d", ipv6.NetIP().String(), ipv6.Port)
|
|
| 418 |
+} |
|
| 419 |
+ |
|
| 420 |
+// ListenStreamArgs returns the arguments required to be passed to |
|
| 421 |
+// net.ListenTCP(). If the Mask of ipv6 is not a /128, ListenStreamArgs() |
|
| 422 |
+// will fail. See Host() to create an IPv6Addr with its mask set to /128. |
|
| 423 |
+func (ipv6 IPv6Addr) ListenStreamArgs() (network, listenArgs string) {
|
|
| 424 |
+ ipv6Mask := big.Int(*ipv6.Mask) |
|
| 425 |
+ if ipv6Mask.Cmp(ipv6HostMask) != 0 {
|
|
| 426 |
+ return "tcp6", "" |
|
| 427 |
+ } |
|
| 428 |
+ return "tcp6", fmt.Sprintf("[%s]:%d", ipv6.NetIP().String(), ipv6.Port)
|
|
| 429 |
+} |
|
| 430 |
+ |
|
| 431 |
+// Maskbits returns the number of network mask bits in a given IPv6Addr. For |
|
| 432 |
+// example, the Maskbits() of "2001:0db8::0003/64" would return 64. |
|
| 433 |
+func (ipv6 IPv6Addr) Maskbits() int {
|
|
| 434 |
+ maskOnes, _ := ipv6.NetIPNet().Mask.Size() |
|
| 435 |
+ |
|
| 436 |
+ return maskOnes |
|
| 437 |
+} |
|
| 438 |
+ |
|
| 439 |
+// MustIPv6Addr is a helper method that must return an IPv6Addr or panic on |
|
| 440 |
+// invalid input. |
|
| 441 |
+func MustIPv6Addr(addr string) IPv6Addr {
|
|
| 442 |
+ ipv6, err := NewIPv6Addr(addr) |
|
| 443 |
+ if err != nil {
|
|
| 444 |
+ panic(fmt.Sprintf("Unable to create an IPv6Addr from %+q: %v", addr, err))
|
|
| 445 |
+ } |
|
| 446 |
+ return ipv6 |
|
| 447 |
+} |
|
| 448 |
+ |
|
| 449 |
+// NetIP returns the address as a net.IP. |
|
| 450 |
+func (ipv6 IPv6Addr) NetIP() *net.IP {
|
|
| 451 |
+ return bigIntToNetIPv6(ipv6.Address) |
|
| 452 |
+} |
|
| 453 |
+ |
|
| 454 |
+// NetIPMask create a new net.IPMask from the IPv6Addr. |
|
| 455 |
+func (ipv6 IPv6Addr) NetIPMask() *net.IPMask {
|
|
| 456 |
+ ipv6Mask := make(net.IPMask, IPv6len) |
|
| 457 |
+ m := big.Int(*ipv6.Mask) |
|
| 458 |
+ copy(ipv6Mask, m.Bytes()) |
|
| 459 |
+ return &ipv6Mask |
|
| 460 |
+} |
|
| 461 |
+ |
|
| 462 |
+// Network returns a pointer to the net.IPNet within IPv4Addr receiver. |
|
| 463 |
+func (ipv6 IPv6Addr) NetIPNet() *net.IPNet {
|
|
| 464 |
+ ipv6net := &net.IPNet{}
|
|
| 465 |
+ ipv6net.IP = make(net.IP, IPv6len) |
|
| 466 |
+ copy(ipv6net.IP, *ipv6.NetIP()) |
|
| 467 |
+ ipv6net.Mask = *ipv6.NetIPMask() |
|
| 468 |
+ return ipv6net |
|
| 469 |
+} |
|
| 470 |
+ |
|
| 471 |
+// Network returns the network prefix or network address for a given network. |
|
| 472 |
+func (ipv6 IPv6Addr) Network() IPAddr {
|
|
| 473 |
+ return IPv6Addr{
|
|
| 474 |
+ Address: IPv6Address(ipv6.NetworkAddress()), |
|
| 475 |
+ Mask: ipv6.Mask, |
|
| 476 |
+ } |
|
| 477 |
+} |
|
| 478 |
+ |
|
| 479 |
+// NetworkAddress returns an IPv6Network of the IPv6Addr's network address. |
|
| 480 |
+func (ipv6 IPv6Addr) NetworkAddress() IPv6Network {
|
|
| 481 |
+ addr := new(big.Int) |
|
| 482 |
+ addr.SetBytes((*ipv6.Address).Bytes()) |
|
| 483 |
+ |
|
| 484 |
+ mask := new(big.Int) |
|
| 485 |
+ mask.SetBytes(*ipv6.NetIPMask()) |
|
| 486 |
+ |
|
| 487 |
+ netAddr := new(big.Int) |
|
| 488 |
+ netAddr.And(addr, mask) |
|
| 489 |
+ |
|
| 490 |
+ return IPv6Network(netAddr) |
|
| 491 |
+} |
|
| 492 |
+ |
|
| 493 |
+// Octets returns a slice of the 16 octets in an IPv6Addr's Address. The |
|
| 494 |
+// order of the bytes is big endian. |
|
| 495 |
+func (ipv6 IPv6Addr) Octets() []int {
|
|
| 496 |
+ x := make([]int, IPv6len) |
|
| 497 |
+ for i, b := range *bigIntToNetIPv6(ipv6.Address) {
|
|
| 498 |
+ x[i] = int(b) |
|
| 499 |
+ } |
|
| 500 |
+ |
|
| 501 |
+ return x |
|
| 502 |
+} |
|
| 503 |
+ |
|
| 504 |
+// String returns a string representation of the IPv6Addr |
|
| 505 |
+func (ipv6 IPv6Addr) String() string {
|
|
| 506 |
+ if ipv6.Port != 0 {
|
|
| 507 |
+ return fmt.Sprintf("[%s]:%d", ipv6.NetIP().String(), ipv6.Port)
|
|
| 508 |
+ } |
|
| 509 |
+ |
|
| 510 |
+ if ipv6.Maskbits() == 128 {
|
|
| 511 |
+ return ipv6.NetIP().String() |
|
| 512 |
+ } |
|
| 513 |
+ |
|
| 514 |
+ return fmt.Sprintf("%s/%d", ipv6.NetIP().String(), ipv6.Maskbits())
|
|
| 515 |
+} |
|
| 516 |
+ |
|
| 517 |
+// Type is used as a type switch and returns TypeIPv6 |
|
| 518 |
+func (IPv6Addr) Type() SockAddrType {
|
|
| 519 |
+ return TypeIPv6 |
|
| 520 |
+} |
|
| 521 |
+ |
|
| 522 |
+// IPv6Attrs returns a list of attributes supported by the IPv6Addr type |
|
| 523 |
+func IPv6Attrs() []AttrName {
|
|
| 524 |
+ return ipv6AddrAttrs |
|
| 525 |
+} |
|
| 526 |
+ |
|
| 527 |
+// IPv6AddrAttr returns a string representation of an attribute for the given |
|
| 528 |
+// IPv6Addr. |
|
| 529 |
+func IPv6AddrAttr(ipv6 IPv6Addr, selector AttrName) string {
|
|
| 530 |
+ fn, found := ipv6AddrAttrMap[selector] |
|
| 531 |
+ if !found {
|
|
| 532 |
+ return "" |
|
| 533 |
+ } |
|
| 534 |
+ |
|
| 535 |
+ return fn(ipv6) |
|
| 536 |
+} |
|
| 537 |
+ |
|
| 538 |
+// ipv6AddrInit is called once at init() |
|
| 539 |
+func ipv6AddrInit() {
|
|
| 540 |
+ // Sorted for human readability |
|
| 541 |
+ ipv6AddrAttrs = []AttrName{
|
|
| 542 |
+ "size", // Same position as in IPv6 for output consistency |
|
| 543 |
+ "uint128", |
|
| 544 |
+ } |
|
| 545 |
+ |
|
| 546 |
+ ipv6AddrAttrMap = map[AttrName]func(ipv6 IPv6Addr) string{
|
|
| 547 |
+ "size": func(ipv6 IPv6Addr) string {
|
|
| 548 |
+ netSize := big.NewInt(1) |
|
| 549 |
+ netSize = netSize.Lsh(netSize, uint(IPv6len*8-ipv6.Maskbits())) |
|
| 550 |
+ return netSize.Text(10) |
|
| 551 |
+ }, |
|
| 552 |
+ "uint128": func(ipv6 IPv6Addr) string {
|
|
| 553 |
+ b := big.Int(*ipv6.Address) |
|
| 554 |
+ return b.Text(10) |
|
| 555 |
+ }, |
|
| 556 |
+ } |
|
| 557 |
+} |
|
| 558 |
+ |
|
| 559 |
+// bigIntToNetIPv6 is a helper function that correctly returns a net.IP with the |
|
| 560 |
+// correctly padded values. |
|
| 561 |
+func bigIntToNetIPv6(bi *big.Int) *net.IP {
|
|
| 562 |
+ x := make(net.IP, IPv6len) |
|
| 563 |
+ ipv6Bytes := bi.Bytes() |
|
| 564 |
+ |
|
| 565 |
+ // It's possibe for ipv6Bytes to be less than IPv6len bytes in size. If |
|
| 566 |
+ // they are different sizes we to pad the size of response. |
|
| 567 |
+ if len(ipv6Bytes) < IPv6len {
|
|
| 568 |
+ buf := new(bytes.Buffer) |
|
| 569 |
+ buf.Grow(IPv6len) |
|
| 570 |
+ |
|
| 571 |
+ for i := len(ipv6Bytes); i < IPv6len; i++ {
|
|
| 572 |
+ if err := binary.Write(buf, binary.BigEndian, byte(0)); err != nil {
|
|
| 573 |
+ panic(fmt.Sprintf("Unable to pad byte %d of input %v: %v", i, bi, err))
|
|
| 574 |
+ } |
|
| 575 |
+ } |
|
| 576 |
+ |
|
| 577 |
+ for _, b := range ipv6Bytes {
|
|
| 578 |
+ if err := binary.Write(buf, binary.BigEndian, b); err != nil {
|
|
| 579 |
+ panic(fmt.Sprintf("Unable to preserve endianness of input %v: %v", bi, err))
|
|
| 580 |
+ } |
|
| 581 |
+ } |
|
| 582 |
+ |
|
| 583 |
+ ipv6Bytes = buf.Bytes() |
|
| 584 |
+ } |
|
| 585 |
+ i := copy(x, ipv6Bytes) |
|
| 586 |
+ if i != IPv6len {
|
|
| 587 |
+ panic("IPv6 wrong size")
|
|
| 588 |
+ } |
|
| 589 |
+ return &x |
|
| 590 |
+} |
| 0 | 591 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,947 @@ |
| 0 |
+package sockaddr |
|
| 1 |
+ |
|
| 2 |
+// ForwardingBlacklist is a faux RFC that includes a list of non-forwardable IP |
|
| 3 |
+// blocks. |
|
| 4 |
+const ForwardingBlacklist = 4294967295 |
|
| 5 |
+ |
|
| 6 |
+// IsRFC tests to see if an SockAddr matches the specified RFC |
|
| 7 |
+func IsRFC(rfcNum uint, sa SockAddr) bool {
|
|
| 8 |
+ rfcNetMap := KnownRFCs() |
|
| 9 |
+ rfcNets, ok := rfcNetMap[rfcNum] |
|
| 10 |
+ if !ok {
|
|
| 11 |
+ return false |
|
| 12 |
+ } |
|
| 13 |
+ |
|
| 14 |
+ var contained bool |
|
| 15 |
+ for _, rfcNet := range rfcNets {
|
|
| 16 |
+ if rfcNet.Contains(sa) {
|
|
| 17 |
+ contained = true |
|
| 18 |
+ break |
|
| 19 |
+ } |
|
| 20 |
+ } |
|
| 21 |
+ return contained |
|
| 22 |
+} |
|
| 23 |
+ |
|
| 24 |
+// KnownRFCs returns an initial set of known RFCs. |
|
| 25 |
+// |
|
| 26 |
+// NOTE (sean@): As this list evolves over time, please submit patches to keep |
|
| 27 |
+// this list current. If something isn't right, inquire, as it may just be a |
|
| 28 |
+// bug on my part. Some of the inclusions were based on my judgement as to what |
|
| 29 |
+// would be a useful value (e.g. RFC3330). |
|
| 30 |
+// |
|
| 31 |
+// Useful resources: |
|
| 32 |
+// |
|
| 33 |
+// * https://www.iana.org/assignments/ipv6-address-space/ipv6-address-space.xhtml |
|
| 34 |
+// * https://www.iana.org/assignments/ipv6-unicast-address-assignments/ipv6-unicast-address-assignments.xhtml |
|
| 35 |
+// * https://www.iana.org/assignments/ipv6-address-space/ipv6-address-space.xhtml |
|
| 36 |
+func KnownRFCs() map[uint]SockAddrs {
|
|
| 37 |
+ // NOTE(sean@): Multiple SockAddrs per RFC lend themselves well to a |
|
| 38 |
+ // RADIX tree, but `ENOTIME`. Patches welcome. |
|
| 39 |
+ return map[uint]SockAddrs{
|
|
| 40 |
+ 919: {
|
|
| 41 |
+ // [RFC919] Broadcasting Internet Datagrams |
|
| 42 |
+ MustIPv4Addr("255.255.255.255/32"), // [RFC1122], §7 Broadcast IP Addressing - Proposed Standards
|
|
| 43 |
+ }, |
|
| 44 |
+ 1122: {
|
|
| 45 |
+ // [RFC1122] Requirements for Internet Hosts -- Communication Layers |
|
| 46 |
+ MustIPv4Addr("0.0.0.0/8"), // [RFC1122], §3.2.1.3
|
|
| 47 |
+ MustIPv4Addr("127.0.0.0/8"), // [RFC1122], §3.2.1.3
|
|
| 48 |
+ }, |
|
| 49 |
+ 1112: {
|
|
| 50 |
+ // [RFC1112] Host Extensions for IP Multicasting |
|
| 51 |
+ MustIPv4Addr("224.0.0.0/4"), // [RFC1112], §4 Host Group Addresses
|
|
| 52 |
+ }, |
|
| 53 |
+ 1918: {
|
|
| 54 |
+ // [RFC1918] Address Allocation for Private Internets |
|
| 55 |
+ MustIPv4Addr("10.0.0.0/8"),
|
|
| 56 |
+ MustIPv4Addr("172.16.0.0/12"),
|
|
| 57 |
+ MustIPv4Addr("192.168.0.0/16"),
|
|
| 58 |
+ }, |
|
| 59 |
+ 2544: {
|
|
| 60 |
+ // [RFC2544] Benchmarking Methodology for Network |
|
| 61 |
+ // Interconnect Devices |
|
| 62 |
+ MustIPv4Addr("198.18.0.0/15"),
|
|
| 63 |
+ }, |
|
| 64 |
+ 2765: {
|
|
| 65 |
+ // [RFC2765] Stateless IP/ICMP Translation Algorithm |
|
| 66 |
+ // (SIIT) (obsoleted by RFCs 6145, which itself was |
|
| 67 |
+ // later obsoleted by 7915). |
|
| 68 |
+ |
|
| 69 |
+ // [RFC2765], §2.1 Addresses |
|
| 70 |
+ MustIPv6Addr("0:0:0:0:0:ffff:0:0/96"),
|
|
| 71 |
+ }, |
|
| 72 |
+ 2928: {
|
|
| 73 |
+ // [RFC2928] Initial IPv6 Sub-TLA ID Assignments |
|
| 74 |
+ MustIPv6Addr("2001::/16"), // Superblock
|
|
| 75 |
+ //MustIPv6Addr("2001:0000::/23"), // IANA
|
|
| 76 |
+ //MustIPv6Addr("2001:0200::/23"), // APNIC
|
|
| 77 |
+ //MustIPv6Addr("2001:0400::/23"), // ARIN
|
|
| 78 |
+ //MustIPv6Addr("2001:0600::/23"), // RIPE NCC
|
|
| 79 |
+ //MustIPv6Addr("2001:0800::/23"), // (future assignment)
|
|
| 80 |
+ // ... |
|
| 81 |
+ //MustIPv6Addr("2001:FE00::/23"), // (future assignment)
|
|
| 82 |
+ }, |
|
| 83 |
+ 3056: { // 6to4 address
|
|
| 84 |
+ // [RFC3056] Connection of IPv6 Domains via IPv4 Clouds |
|
| 85 |
+ |
|
| 86 |
+ // [RFC3056], §2 IPv6 Prefix Allocation |
|
| 87 |
+ MustIPv6Addr("2002::/16"),
|
|
| 88 |
+ }, |
|
| 89 |
+ 3068: {
|
|
| 90 |
+ // [RFC3068] An Anycast Prefix for 6to4 Relay Routers |
|
| 91 |
+ // (obsolete by RFC7526) |
|
| 92 |
+ |
|
| 93 |
+ // [RFC3068], § 6to4 Relay anycast address |
|
| 94 |
+ MustIPv4Addr("192.88.99.0/24"),
|
|
| 95 |
+ |
|
| 96 |
+ // [RFC3068], §2.5 6to4 IPv6 relay anycast address |
|
| 97 |
+ // |
|
| 98 |
+ // NOTE: /120 == 128-(32-24) |
|
| 99 |
+ MustIPv6Addr("2002:c058:6301::/120"),
|
|
| 100 |
+ }, |
|
| 101 |
+ 3171: {
|
|
| 102 |
+ // [RFC3171] IANA Guidelines for IPv4 Multicast Address Assignments |
|
| 103 |
+ MustIPv4Addr("224.0.0.0/4"),
|
|
| 104 |
+ }, |
|
| 105 |
+ 3330: {
|
|
| 106 |
+ // [RFC3330] Special-Use IPv4 Addresses |
|
| 107 |
+ |
|
| 108 |
+ // Addresses in this block refer to source hosts on |
|
| 109 |
+ // "this" network. Address 0.0.0.0/32 may be used as a |
|
| 110 |
+ // source address for this host on this network; other |
|
| 111 |
+ // addresses within 0.0.0.0/8 may be used to refer to |
|
| 112 |
+ // specified hosts on this network [RFC1700, page 4]. |
|
| 113 |
+ MustIPv4Addr("0.0.0.0/8"),
|
|
| 114 |
+ |
|
| 115 |
+ // 10.0.0.0/8 - This block is set aside for use in |
|
| 116 |
+ // private networks. Its intended use is documented in |
|
| 117 |
+ // [RFC1918]. Addresses within this block should not |
|
| 118 |
+ // appear on the public Internet. |
|
| 119 |
+ MustIPv4Addr("10.0.0.0/8"),
|
|
| 120 |
+ |
|
| 121 |
+ // 14.0.0.0/8 - This block is set aside for assignments |
|
| 122 |
+ // to the international system of Public Data Networks |
|
| 123 |
+ // [RFC1700, page 181]. The registry of assignments |
|
| 124 |
+ // within this block can be accessed from the "Public |
|
| 125 |
+ // Data Network Numbers" link on the web page at |
|
| 126 |
+ // http://www.iana.org/numbers.html. Addresses within |
|
| 127 |
+ // this block are assigned to users and should be |
|
| 128 |
+ // treated as such. |
|
| 129 |
+ |
|
| 130 |
+ // 24.0.0.0/8 - This block was allocated in early 1996 |
|
| 131 |
+ // for use in provisioning IP service over cable |
|
| 132 |
+ // television systems. Although the IANA initially was |
|
| 133 |
+ // involved in making assignments to cable operators, |
|
| 134 |
+ // this responsibility was transferred to American |
|
| 135 |
+ // Registry for Internet Numbers (ARIN) in May 2001. |
|
| 136 |
+ // Addresses within this block are assigned in the |
|
| 137 |
+ // normal manner and should be treated as such. |
|
| 138 |
+ |
|
| 139 |
+ // 39.0.0.0/8 - This block was used in the "Class A |
|
| 140 |
+ // Subnet Experiment" that commenced in May 1995, as |
|
| 141 |
+ // documented in [RFC1797]. The experiment has been |
|
| 142 |
+ // completed and this block has been returned to the |
|
| 143 |
+ // pool of addresses reserved for future allocation or |
|
| 144 |
+ // assignment. This block therefore no longer has a |
|
| 145 |
+ // special use and is subject to allocation to a |
|
| 146 |
+ // Regional Internet Registry for assignment in the |
|
| 147 |
+ // normal manner. |
|
| 148 |
+ |
|
| 149 |
+ // 127.0.0.0/8 - This block is assigned for use as the Internet host |
|
| 150 |
+ // loopback address. A datagram sent by a higher level protocol to an |
|
| 151 |
+ // address anywhere within this block should loop back inside the host. |
|
| 152 |
+ // This is ordinarily implemented using only 127.0.0.1/32 for loopback, |
|
| 153 |
+ // but no addresses within this block should ever appear on any network |
|
| 154 |
+ // anywhere [RFC1700, page 5]. |
|
| 155 |
+ MustIPv4Addr("127.0.0.0/8"),
|
|
| 156 |
+ |
|
| 157 |
+ // 128.0.0.0/16 - This block, corresponding to the |
|
| 158 |
+ // numerically lowest of the former Class B addresses, |
|
| 159 |
+ // was initially and is still reserved by the IANA. |
|
| 160 |
+ // Given the present classless nature of the IP address |
|
| 161 |
+ // space, the basis for the reservation no longer |
|
| 162 |
+ // applies and addresses in this block are subject to |
|
| 163 |
+ // future allocation to a Regional Internet Registry for |
|
| 164 |
+ // assignment in the normal manner. |
|
| 165 |
+ |
|
| 166 |
+ // 169.254.0.0/16 - This is the "link local" block. It |
|
| 167 |
+ // is allocated for communication between hosts on a |
|
| 168 |
+ // single link. Hosts obtain these addresses by |
|
| 169 |
+ // auto-configuration, such as when a DHCP server may |
|
| 170 |
+ // not be found. |
|
| 171 |
+ MustIPv4Addr("169.254.0.0/16"),
|
|
| 172 |
+ |
|
| 173 |
+ // 172.16.0.0/12 - This block is set aside for use in |
|
| 174 |
+ // private networks. Its intended use is documented in |
|
| 175 |
+ // [RFC1918]. Addresses within this block should not |
|
| 176 |
+ // appear on the public Internet. |
|
| 177 |
+ MustIPv4Addr("172.16.0.0/12"),
|
|
| 178 |
+ |
|
| 179 |
+ // 191.255.0.0/16 - This block, corresponding to the numerically highest |
|
| 180 |
+ // to the former Class B addresses, was initially and is still reserved |
|
| 181 |
+ // by the IANA. Given the present classless nature of the IP address |
|
| 182 |
+ // space, the basis for the reservation no longer applies and addresses |
|
| 183 |
+ // in this block are subject to future allocation to a Regional Internet |
|
| 184 |
+ // Registry for assignment in the normal manner. |
|
| 185 |
+ |
|
| 186 |
+ // 192.0.0.0/24 - This block, corresponding to the |
|
| 187 |
+ // numerically lowest of the former Class C addresses, |
|
| 188 |
+ // was initially and is still reserved by the IANA. |
|
| 189 |
+ // Given the present classless nature of the IP address |
|
| 190 |
+ // space, the basis for the reservation no longer |
|
| 191 |
+ // applies and addresses in this block are subject to |
|
| 192 |
+ // future allocation to a Regional Internet Registry for |
|
| 193 |
+ // assignment in the normal manner. |
|
| 194 |
+ |
|
| 195 |
+ // 192.0.2.0/24 - This block is assigned as "TEST-NET" for use in |
|
| 196 |
+ // documentation and example code. It is often used in conjunction with |
|
| 197 |
+ // domain names example.com or example.net in vendor and protocol |
|
| 198 |
+ // documentation. Addresses within this block should not appear on the |
|
| 199 |
+ // public Internet. |
|
| 200 |
+ MustIPv4Addr("192.0.2.0/24"),
|
|
| 201 |
+ |
|
| 202 |
+ // 192.88.99.0/24 - This block is allocated for use as 6to4 relay |
|
| 203 |
+ // anycast addresses, according to [RFC3068]. |
|
| 204 |
+ MustIPv4Addr("192.88.99.0/24"),
|
|
| 205 |
+ |
|
| 206 |
+ // 192.168.0.0/16 - This block is set aside for use in private networks. |
|
| 207 |
+ // Its intended use is documented in [RFC1918]. Addresses within this |
|
| 208 |
+ // block should not appear on the public Internet. |
|
| 209 |
+ MustIPv4Addr("192.168.0.0/16"),
|
|
| 210 |
+ |
|
| 211 |
+ // 198.18.0.0/15 - This block has been allocated for use |
|
| 212 |
+ // in benchmark tests of network interconnect devices. |
|
| 213 |
+ // Its use is documented in [RFC2544]. |
|
| 214 |
+ MustIPv4Addr("198.18.0.0/15"),
|
|
| 215 |
+ |
|
| 216 |
+ // 223.255.255.0/24 - This block, corresponding to the |
|
| 217 |
+ // numerically highest of the former Class C addresses, |
|
| 218 |
+ // was initially and is still reserved by the IANA. |
|
| 219 |
+ // Given the present classless nature of the IP address |
|
| 220 |
+ // space, the basis for the reservation no longer |
|
| 221 |
+ // applies and addresses in this block are subject to |
|
| 222 |
+ // future allocation to a Regional Internet Registry for |
|
| 223 |
+ // assignment in the normal manner. |
|
| 224 |
+ |
|
| 225 |
+ // 224.0.0.0/4 - This block, formerly known as the Class |
|
| 226 |
+ // D address space, is allocated for use in IPv4 |
|
| 227 |
+ // multicast address assignments. The IANA guidelines |
|
| 228 |
+ // for assignments from this space are described in |
|
| 229 |
+ // [RFC3171]. |
|
| 230 |
+ MustIPv4Addr("224.0.0.0/4"),
|
|
| 231 |
+ |
|
| 232 |
+ // 240.0.0.0/4 - This block, formerly known as the Class E address |
|
| 233 |
+ // space, is reserved. The "limited broadcast" destination address |
|
| 234 |
+ // 255.255.255.255 should never be forwarded outside the (sub-)net of |
|
| 235 |
+ // the source. The remainder of this space is reserved |
|
| 236 |
+ // for future use. [RFC1700, page 4] |
|
| 237 |
+ MustIPv4Addr("240.0.0.0/4"),
|
|
| 238 |
+ }, |
|
| 239 |
+ 3849: {
|
|
| 240 |
+ // [RFC3849] IPv6 Address Prefix Reserved for Documentation |
|
| 241 |
+ MustIPv6Addr("2001:db8::/32"), // [RFC3849], §4 IANA Considerations
|
|
| 242 |
+ }, |
|
| 243 |
+ 3927: {
|
|
| 244 |
+ // [RFC3927] Dynamic Configuration of IPv4 Link-Local Addresses |
|
| 245 |
+ MustIPv4Addr("169.254.0.0/16"), // [RFC3927], §2.1 Link-Local Address Selection
|
|
| 246 |
+ }, |
|
| 247 |
+ 4038: {
|
|
| 248 |
+ // [RFC4038] Application Aspects of IPv6 Transition |
|
| 249 |
+ |
|
| 250 |
+ // [RFC4038], §4.2. IPv6 Applications in a Dual-Stack Node |
|
| 251 |
+ MustIPv6Addr("0:0:0:0:0:ffff::/96"),
|
|
| 252 |
+ }, |
|
| 253 |
+ 4193: {
|
|
| 254 |
+ // [RFC4193] Unique Local IPv6 Unicast Addresses |
|
| 255 |
+ MustIPv6Addr("fc00::/7"),
|
|
| 256 |
+ }, |
|
| 257 |
+ 4291: {
|
|
| 258 |
+ // [RFC4291] IP Version 6 Addressing Architecture |
|
| 259 |
+ |
|
| 260 |
+ // [RFC4291], §2.5.2 The Unspecified Address |
|
| 261 |
+ MustIPv6Addr("::/128"),
|
|
| 262 |
+ |
|
| 263 |
+ // [RFC4291], §2.5.3 The Loopback Address |
|
| 264 |
+ MustIPv6Addr("::1/128"),
|
|
| 265 |
+ |
|
| 266 |
+ // [RFC4291], §2.5.5.1. IPv4-Compatible IPv6 Address |
|
| 267 |
+ MustIPv6Addr("::/96"),
|
|
| 268 |
+ |
|
| 269 |
+ // [RFC4291], §2.5.5.2. IPv4-Mapped IPv6 Address |
|
| 270 |
+ MustIPv6Addr("::ffff:0:0/96"),
|
|
| 271 |
+ |
|
| 272 |
+ // [RFC4291], §2.5.6 Link-Local IPv6 Unicast Addresses |
|
| 273 |
+ MustIPv6Addr("fe80::/10"),
|
|
| 274 |
+ |
|
| 275 |
+ // [RFC4291], §2.5.7 Site-Local IPv6 Unicast Addresses |
|
| 276 |
+ // (depreciated) |
|
| 277 |
+ MustIPv6Addr("fec0::/10"),
|
|
| 278 |
+ |
|
| 279 |
+ // [RFC4291], §2.7 Multicast Addresses |
|
| 280 |
+ MustIPv6Addr("ff00::/8"),
|
|
| 281 |
+ |
|
| 282 |
+ // IPv6 Multicast Information. |
|
| 283 |
+ // |
|
| 284 |
+ // In the following "table" below, `ff0x` is replaced |
|
| 285 |
+ // with the following values depending on the scope of |
|
| 286 |
+ // the query: |
|
| 287 |
+ // |
|
| 288 |
+ // IPv6 Multicast Scopes: |
|
| 289 |
+ // * ff00/9 // reserved |
|
| 290 |
+ // * ff01/9 // interface-local |
|
| 291 |
+ // * ff02/9 // link-local |
|
| 292 |
+ // * ff03/9 // realm-local |
|
| 293 |
+ // * ff04/9 // admin-local |
|
| 294 |
+ // * ff05/9 // site-local |
|
| 295 |
+ // * ff08/9 // organization-local |
|
| 296 |
+ // * ff0e/9 // global |
|
| 297 |
+ // * ff0f/9 // reserved |
|
| 298 |
+ // |
|
| 299 |
+ // IPv6 Multicast Addresses: |
|
| 300 |
+ // * ff0x::2 // All routers |
|
| 301 |
+ // * ff02::5 // OSPFIGP |
|
| 302 |
+ // * ff02::6 // OSPFIGP Designated Routers |
|
| 303 |
+ // * ff02::9 // RIP Routers |
|
| 304 |
+ // * ff02::a // EIGRP Routers |
|
| 305 |
+ // * ff02::d // All PIM Routers |
|
| 306 |
+ // * ff02::1a // All RPL Routers |
|
| 307 |
+ // * ff0x::fb // mDNSv6 |
|
| 308 |
+ // * ff0x::101 // All Network Time Protocol (NTP) servers |
|
| 309 |
+ // * ff02::1:1 // Link Name |
|
| 310 |
+ // * ff02::1:2 // All-dhcp-agents |
|
| 311 |
+ // * ff02::1:3 // Link-local Multicast Name Resolution |
|
| 312 |
+ // * ff05::1:3 // All-dhcp-servers |
|
| 313 |
+ // * ff02::1:ff00:0/104 // Solicited-node multicast address. |
|
| 314 |
+ // * ff02::2:ff00:0/104 // Node Information Queries |
|
| 315 |
+ }, |
|
| 316 |
+ 4380: {
|
|
| 317 |
+ // [RFC4380] Teredo: Tunneling IPv6 over UDP through |
|
| 318 |
+ // Network Address Translations (NATs) |
|
| 319 |
+ |
|
| 320 |
+ // [RFC4380], §2.6 Global Teredo IPv6 Service Prefix |
|
| 321 |
+ MustIPv6Addr("2001:0000::/32"),
|
|
| 322 |
+ }, |
|
| 323 |
+ 4773: {
|
|
| 324 |
+ // [RFC4773] Administration of the IANA Special Purpose IPv6 Address Block |
|
| 325 |
+ MustIPv6Addr("2001:0000::/23"), // IANA
|
|
| 326 |
+ }, |
|
| 327 |
+ 4843: {
|
|
| 328 |
+ // [RFC4843] An IPv6 Prefix for Overlay Routable Cryptographic Hash Identifiers (ORCHID) |
|
| 329 |
+ MustIPv6Addr("2001:10::/28"), // [RFC4843], §7 IANA Considerations
|
|
| 330 |
+ }, |
|
| 331 |
+ 5180: {
|
|
| 332 |
+ // [RFC5180] IPv6 Benchmarking Methodology for Network Interconnect Devices |
|
| 333 |
+ MustIPv6Addr("2001:0200::/48"), // [RFC5180], §8 IANA Considerations
|
|
| 334 |
+ }, |
|
| 335 |
+ 5735: {
|
|
| 336 |
+ // [RFC5735] Special Use IPv4 Addresses |
|
| 337 |
+ MustIPv4Addr("192.0.2.0/24"), // TEST-NET-1
|
|
| 338 |
+ MustIPv4Addr("198.51.100.0/24"), // TEST-NET-2
|
|
| 339 |
+ MustIPv4Addr("203.0.113.0/24"), // TEST-NET-3
|
|
| 340 |
+ MustIPv4Addr("198.18.0.0/15"), // Benchmarks
|
|
| 341 |
+ }, |
|
| 342 |
+ 5737: {
|
|
| 343 |
+ // [RFC5737] IPv4 Address Blocks Reserved for Documentation |
|
| 344 |
+ MustIPv4Addr("192.0.2.0/24"), // TEST-NET-1
|
|
| 345 |
+ MustIPv4Addr("198.51.100.0/24"), // TEST-NET-2
|
|
| 346 |
+ MustIPv4Addr("203.0.113.0/24"), // TEST-NET-3
|
|
| 347 |
+ }, |
|
| 348 |
+ 6052: {
|
|
| 349 |
+ // [RFC6052] IPv6 Addressing of IPv4/IPv6 Translators |
|
| 350 |
+ MustIPv6Addr("64:ff9b::/96"), // [RFC6052], §2.1. Well-Known Prefix
|
|
| 351 |
+ }, |
|
| 352 |
+ 6333: {
|
|
| 353 |
+ // [RFC6333] Dual-Stack Lite Broadband Deployments Following IPv4 Exhaustion |
|
| 354 |
+ MustIPv4Addr("192.0.0.0/29"), // [RFC6333], §5.7 Well-Known IPv4 Address
|
|
| 355 |
+ }, |
|
| 356 |
+ 6598: {
|
|
| 357 |
+ // [RFC6598] IANA-Reserved IPv4 Prefix for Shared Address Space |
|
| 358 |
+ MustIPv4Addr("100.64.0.0/10"),
|
|
| 359 |
+ }, |
|
| 360 |
+ 6666: {
|
|
| 361 |
+ // [RFC6666] A Discard Prefix for IPv6 |
|
| 362 |
+ MustIPv6Addr("0100::/64"),
|
|
| 363 |
+ }, |
|
| 364 |
+ 6890: {
|
|
| 365 |
+ // [RFC6890] Special-Purpose IP Address Registries |
|
| 366 |
+ |
|
| 367 |
+ // From "RFC6890 §2.2.1 Information Requirements": |
|
| 368 |
+ /* |
|
| 369 |
+ The IPv4 and IPv6 Special-Purpose Address Registries maintain the |
|
| 370 |
+ following information regarding each entry: |
|
| 371 |
+ |
|
| 372 |
+ o Address Block - A block of IPv4 or IPv6 addresses that has been |
|
| 373 |
+ registered for a special purpose. |
|
| 374 |
+ |
|
| 375 |
+ o Name - A descriptive name for the special-purpose address block. |
|
| 376 |
+ |
|
| 377 |
+ o RFC - The RFC through which the special-purpose address block was |
|
| 378 |
+ requested. |
|
| 379 |
+ |
|
| 380 |
+ o Allocation Date - The date upon which the special-purpose address |
|
| 381 |
+ block was allocated. |
|
| 382 |
+ |
|
| 383 |
+ o Termination Date - The date upon which the allocation is to be |
|
| 384 |
+ terminated. This field is applicable for limited-use allocations |
|
| 385 |
+ only. |
|
| 386 |
+ |
|
| 387 |
+ o Source - A boolean value indicating whether an address from the |
|
| 388 |
+ allocated special-purpose address block is valid when used as the |
|
| 389 |
+ source address of an IP datagram that transits two devices. |
|
| 390 |
+ |
|
| 391 |
+ o Destination - A boolean value indicating whether an address from |
|
| 392 |
+ the allocated special-purpose address block is valid when used as |
|
| 393 |
+ the destination address of an IP datagram that transits two |
|
| 394 |
+ devices. |
|
| 395 |
+ |
|
| 396 |
+ o Forwardable - A boolean value indicating whether a router may |
|
| 397 |
+ forward an IP datagram whose destination address is drawn from the |
|
| 398 |
+ allocated special-purpose address block between external |
|
| 399 |
+ interfaces. |
|
| 400 |
+ |
|
| 401 |
+ o Global - A boolean value indicating whether an IP datagram whose |
|
| 402 |
+ destination address is drawn from the allocated special-purpose |
|
| 403 |
+ address block is forwardable beyond a specified administrative |
|
| 404 |
+ domain. |
|
| 405 |
+ |
|
| 406 |
+ o Reserved-by-Protocol - A boolean value indicating whether the |
|
| 407 |
+ special-purpose address block is reserved by IP, itself. This |
|
| 408 |
+ value is "TRUE" if the RFC that created the special-purpose |
|
| 409 |
+ address block requires all compliant IP implementations to behave |
|
| 410 |
+ in a special way when processing packets either to or from |
|
| 411 |
+ addresses contained by the address block. |
|
| 412 |
+ |
|
| 413 |
+ If the value of "Destination" is FALSE, the values of "Forwardable" |
|
| 414 |
+ and "Global" must also be false. |
|
| 415 |
+ */ |
|
| 416 |
+ |
|
| 417 |
+ /*+----------------------+----------------------------+ |
|
| 418 |
+ * | Attribute | Value | |
|
| 419 |
+ * +----------------------+----------------------------+ |
|
| 420 |
+ * | Address Block | 0.0.0.0/8 | |
|
| 421 |
+ * | Name | "This host on this network"| |
|
| 422 |
+ * | RFC | [RFC1122], Section 3.2.1.3 | |
|
| 423 |
+ * | Allocation Date | September 1981 | |
|
| 424 |
+ * | Termination Date | N/A | |
|
| 425 |
+ * | Source | True | |
|
| 426 |
+ * | Destination | False | |
|
| 427 |
+ * | Forwardable | False | |
|
| 428 |
+ * | Global | False | |
|
| 429 |
+ * | Reserved-by-Protocol | True | |
|
| 430 |
+ * +----------------------+----------------------------+*/ |
|
| 431 |
+ MustIPv4Addr("0.0.0.0/8"),
|
|
| 432 |
+ |
|
| 433 |
+ /*+----------------------+---------------+ |
|
| 434 |
+ * | Attribute | Value | |
|
| 435 |
+ * +----------------------+---------------+ |
|
| 436 |
+ * | Address Block | 10.0.0.0/8 | |
|
| 437 |
+ * | Name | Private-Use | |
|
| 438 |
+ * | RFC | [RFC1918] | |
|
| 439 |
+ * | Allocation Date | February 1996 | |
|
| 440 |
+ * | Termination Date | N/A | |
|
| 441 |
+ * | Source | True | |
|
| 442 |
+ * | Destination | True | |
|
| 443 |
+ * | Forwardable | True | |
|
| 444 |
+ * | Global | False | |
|
| 445 |
+ * | Reserved-by-Protocol | False | |
|
| 446 |
+ * +----------------------+---------------+ */ |
|
| 447 |
+ MustIPv4Addr("10.0.0.0/8"),
|
|
| 448 |
+ |
|
| 449 |
+ /*+----------------------+----------------------+ |
|
| 450 |
+ | Attribute | Value | |
|
| 451 |
+ +----------------------+----------------------+ |
|
| 452 |
+ | Address Block | 100.64.0.0/10 | |
|
| 453 |
+ | Name | Shared Address Space | |
|
| 454 |
+ | RFC | [RFC6598] | |
|
| 455 |
+ | Allocation Date | April 2012 | |
|
| 456 |
+ | Termination Date | N/A | |
|
| 457 |
+ | Source | True | |
|
| 458 |
+ | Destination | True | |
|
| 459 |
+ | Forwardable | True | |
|
| 460 |
+ | Global | False | |
|
| 461 |
+ | Reserved-by-Protocol | False | |
|
| 462 |
+ +----------------------+----------------------+*/ |
|
| 463 |
+ MustIPv4Addr("100.64.0.0/10"),
|
|
| 464 |
+ |
|
| 465 |
+ /*+----------------------+----------------------------+ |
|
| 466 |
+ | Attribute | Value | |
|
| 467 |
+ +----------------------+----------------------------+ |
|
| 468 |
+ | Address Block | 127.0.0.0/8 | |
|
| 469 |
+ | Name | Loopback | |
|
| 470 |
+ | RFC | [RFC1122], Section 3.2.1.3 | |
|
| 471 |
+ | Allocation Date | September 1981 | |
|
| 472 |
+ | Termination Date | N/A | |
|
| 473 |
+ | Source | False [1] | |
|
| 474 |
+ | Destination | False [1] | |
|
| 475 |
+ | Forwardable | False [1] | |
|
| 476 |
+ | Global | False [1] | |
|
| 477 |
+ | Reserved-by-Protocol | True | |
|
| 478 |
+ +----------------------+----------------------------+*/ |
|
| 479 |
+ // [1] Several protocols have been granted exceptions to |
|
| 480 |
+ // this rule. For examples, see [RFC4379] and |
|
| 481 |
+ // [RFC5884]. |
|
| 482 |
+ MustIPv4Addr("127.0.0.0/8"),
|
|
| 483 |
+ |
|
| 484 |
+ /*+----------------------+----------------+ |
|
| 485 |
+ | Attribute | Value | |
|
| 486 |
+ +----------------------+----------------+ |
|
| 487 |
+ | Address Block | 169.254.0.0/16 | |
|
| 488 |
+ | Name | Link Local | |
|
| 489 |
+ | RFC | [RFC3927] | |
|
| 490 |
+ | Allocation Date | May 2005 | |
|
| 491 |
+ | Termination Date | N/A | |
|
| 492 |
+ | Source | True | |
|
| 493 |
+ | Destination | True | |
|
| 494 |
+ | Forwardable | False | |
|
| 495 |
+ | Global | False | |
|
| 496 |
+ | Reserved-by-Protocol | True | |
|
| 497 |
+ +----------------------+----------------+*/ |
|
| 498 |
+ MustIPv4Addr("169.254.0.0/16"),
|
|
| 499 |
+ |
|
| 500 |
+ /*+----------------------+---------------+ |
|
| 501 |
+ | Attribute | Value | |
|
| 502 |
+ +----------------------+---------------+ |
|
| 503 |
+ | Address Block | 172.16.0.0/12 | |
|
| 504 |
+ | Name | Private-Use | |
|
| 505 |
+ | RFC | [RFC1918] | |
|
| 506 |
+ | Allocation Date | February 1996 | |
|
| 507 |
+ | Termination Date | N/A | |
|
| 508 |
+ | Source | True | |
|
| 509 |
+ | Destination | True | |
|
| 510 |
+ | Forwardable | True | |
|
| 511 |
+ | Global | False | |
|
| 512 |
+ | Reserved-by-Protocol | False | |
|
| 513 |
+ +----------------------+---------------+*/ |
|
| 514 |
+ MustIPv4Addr("172.16.0.0/12"),
|
|
| 515 |
+ |
|
| 516 |
+ /*+----------------------+---------------------------------+ |
|
| 517 |
+ | Attribute | Value | |
|
| 518 |
+ +----------------------+---------------------------------+ |
|
| 519 |
+ | Address Block | 192.0.0.0/24 [2] | |
|
| 520 |
+ | Name | IETF Protocol Assignments | |
|
| 521 |
+ | RFC | Section 2.1 of this document | |
|
| 522 |
+ | Allocation Date | January 2010 | |
|
| 523 |
+ | Termination Date | N/A | |
|
| 524 |
+ | Source | False | |
|
| 525 |
+ | Destination | False | |
|
| 526 |
+ | Forwardable | False | |
|
| 527 |
+ | Global | False | |
|
| 528 |
+ | Reserved-by-Protocol | False | |
|
| 529 |
+ +----------------------+---------------------------------+*/ |
|
| 530 |
+ // [2] Not usable unless by virtue of a more specific |
|
| 531 |
+ // reservation. |
|
| 532 |
+ MustIPv4Addr("192.0.0.0/24"),
|
|
| 533 |
+ |
|
| 534 |
+ /*+----------------------+--------------------------------+ |
|
| 535 |
+ | Attribute | Value | |
|
| 536 |
+ +----------------------+--------------------------------+ |
|
| 537 |
+ | Address Block | 192.0.0.0/29 | |
|
| 538 |
+ | Name | IPv4 Service Continuity Prefix | |
|
| 539 |
+ | RFC | [RFC6333], [RFC7335] | |
|
| 540 |
+ | Allocation Date | June 2011 | |
|
| 541 |
+ | Termination Date | N/A | |
|
| 542 |
+ | Source | True | |
|
| 543 |
+ | Destination | True | |
|
| 544 |
+ | Forwardable | True | |
|
| 545 |
+ | Global | False | |
|
| 546 |
+ | Reserved-by-Protocol | False | |
|
| 547 |
+ +----------------------+--------------------------------+*/ |
|
| 548 |
+ MustIPv4Addr("192.0.0.0/29"),
|
|
| 549 |
+ |
|
| 550 |
+ /*+----------------------+----------------------------+ |
|
| 551 |
+ | Attribute | Value | |
|
| 552 |
+ +----------------------+----------------------------+ |
|
| 553 |
+ | Address Block | 192.0.2.0/24 | |
|
| 554 |
+ | Name | Documentation (TEST-NET-1) | |
|
| 555 |
+ | RFC | [RFC5737] | |
|
| 556 |
+ | Allocation Date | January 2010 | |
|
| 557 |
+ | Termination Date | N/A | |
|
| 558 |
+ | Source | False | |
|
| 559 |
+ | Destination | False | |
|
| 560 |
+ | Forwardable | False | |
|
| 561 |
+ | Global | False | |
|
| 562 |
+ | Reserved-by-Protocol | False | |
|
| 563 |
+ +----------------------+----------------------------+*/ |
|
| 564 |
+ MustIPv4Addr("192.0.2.0/24"),
|
|
| 565 |
+ |
|
| 566 |
+ /*+----------------------+--------------------+ |
|
| 567 |
+ | Attribute | Value | |
|
| 568 |
+ +----------------------+--------------------+ |
|
| 569 |
+ | Address Block | 192.88.99.0/24 | |
|
| 570 |
+ | Name | 6to4 Relay Anycast | |
|
| 571 |
+ | RFC | [RFC3068] | |
|
| 572 |
+ | Allocation Date | June 2001 | |
|
| 573 |
+ | Termination Date | N/A | |
|
| 574 |
+ | Source | True | |
|
| 575 |
+ | Destination | True | |
|
| 576 |
+ | Forwardable | True | |
|
| 577 |
+ | Global | True | |
|
| 578 |
+ | Reserved-by-Protocol | False | |
|
| 579 |
+ +----------------------+--------------------+*/ |
|
| 580 |
+ MustIPv4Addr("192.88.99.0/24"),
|
|
| 581 |
+ |
|
| 582 |
+ /*+----------------------+----------------+ |
|
| 583 |
+ | Attribute | Value | |
|
| 584 |
+ +----------------------+----------------+ |
|
| 585 |
+ | Address Block | 192.168.0.0/16 | |
|
| 586 |
+ | Name | Private-Use | |
|
| 587 |
+ | RFC | [RFC1918] | |
|
| 588 |
+ | Allocation Date | February 1996 | |
|
| 589 |
+ | Termination Date | N/A | |
|
| 590 |
+ | Source | True | |
|
| 591 |
+ | Destination | True | |
|
| 592 |
+ | Forwardable | True | |
|
| 593 |
+ | Global | False | |
|
| 594 |
+ | Reserved-by-Protocol | False | |
|
| 595 |
+ +----------------------+----------------+*/ |
|
| 596 |
+ MustIPv4Addr("192.168.0.0/16"),
|
|
| 597 |
+ |
|
| 598 |
+ /*+----------------------+---------------+ |
|
| 599 |
+ | Attribute | Value | |
|
| 600 |
+ +----------------------+---------------+ |
|
| 601 |
+ | Address Block | 198.18.0.0/15 | |
|
| 602 |
+ | Name | Benchmarking | |
|
| 603 |
+ | RFC | [RFC2544] | |
|
| 604 |
+ | Allocation Date | March 1999 | |
|
| 605 |
+ | Termination Date | N/A | |
|
| 606 |
+ | Source | True | |
|
| 607 |
+ | Destination | True | |
|
| 608 |
+ | Forwardable | True | |
|
| 609 |
+ | Global | False | |
|
| 610 |
+ | Reserved-by-Protocol | False | |
|
| 611 |
+ +----------------------+---------------+*/ |
|
| 612 |
+ MustIPv4Addr("198.18.0.0/15"),
|
|
| 613 |
+ |
|
| 614 |
+ /*+----------------------+----------------------------+ |
|
| 615 |
+ | Attribute | Value | |
|
| 616 |
+ +----------------------+----------------------------+ |
|
| 617 |
+ | Address Block | 198.51.100.0/24 | |
|
| 618 |
+ | Name | Documentation (TEST-NET-2) | |
|
| 619 |
+ | RFC | [RFC5737] | |
|
| 620 |
+ | Allocation Date | January 2010 | |
|
| 621 |
+ | Termination Date | N/A | |
|
| 622 |
+ | Source | False | |
|
| 623 |
+ | Destination | False | |
|
| 624 |
+ | Forwardable | False | |
|
| 625 |
+ | Global | False | |
|
| 626 |
+ | Reserved-by-Protocol | False | |
|
| 627 |
+ +----------------------+----------------------------+*/ |
|
| 628 |
+ MustIPv4Addr("198.51.100.0/24"),
|
|
| 629 |
+ |
|
| 630 |
+ /*+----------------------+----------------------------+ |
|
| 631 |
+ | Attribute | Value | |
|
| 632 |
+ +----------------------+----------------------------+ |
|
| 633 |
+ | Address Block | 203.0.113.0/24 | |
|
| 634 |
+ | Name | Documentation (TEST-NET-3) | |
|
| 635 |
+ | RFC | [RFC5737] | |
|
| 636 |
+ | Allocation Date | January 2010 | |
|
| 637 |
+ | Termination Date | N/A | |
|
| 638 |
+ | Source | False | |
|
| 639 |
+ | Destination | False | |
|
| 640 |
+ | Forwardable | False | |
|
| 641 |
+ | Global | False | |
|
| 642 |
+ | Reserved-by-Protocol | False | |
|
| 643 |
+ +----------------------+----------------------------+*/ |
|
| 644 |
+ MustIPv4Addr("203.0.113.0/24"),
|
|
| 645 |
+ |
|
| 646 |
+ /*+----------------------+----------------------+ |
|
| 647 |
+ | Attribute | Value | |
|
| 648 |
+ +----------------------+----------------------+ |
|
| 649 |
+ | Address Block | 240.0.0.0/4 | |
|
| 650 |
+ | Name | Reserved | |
|
| 651 |
+ | RFC | [RFC1112], Section 4 | |
|
| 652 |
+ | Allocation Date | August 1989 | |
|
| 653 |
+ | Termination Date | N/A | |
|
| 654 |
+ | Source | False | |
|
| 655 |
+ | Destination | False | |
|
| 656 |
+ | Forwardable | False | |
|
| 657 |
+ | Global | False | |
|
| 658 |
+ | Reserved-by-Protocol | True | |
|
| 659 |
+ +----------------------+----------------------+*/ |
|
| 660 |
+ MustIPv4Addr("240.0.0.0/4"),
|
|
| 661 |
+ |
|
| 662 |
+ /*+----------------------+----------------------+ |
|
| 663 |
+ | Attribute | Value | |
|
| 664 |
+ +----------------------+----------------------+ |
|
| 665 |
+ | Address Block | 255.255.255.255/32 | |
|
| 666 |
+ | Name | Limited Broadcast | |
|
| 667 |
+ | RFC | [RFC0919], Section 7 | |
|
| 668 |
+ | Allocation Date | October 1984 | |
|
| 669 |
+ | Termination Date | N/A | |
|
| 670 |
+ | Source | False | |
|
| 671 |
+ | Destination | True | |
|
| 672 |
+ | Forwardable | False | |
|
| 673 |
+ | Global | False | |
|
| 674 |
+ | Reserved-by-Protocol | False | |
|
| 675 |
+ +----------------------+----------------------+*/ |
|
| 676 |
+ MustIPv4Addr("255.255.255.255/32"),
|
|
| 677 |
+ |
|
| 678 |
+ /*+----------------------+------------------+ |
|
| 679 |
+ | Attribute | Value | |
|
| 680 |
+ +----------------------+------------------+ |
|
| 681 |
+ | Address Block | ::1/128 | |
|
| 682 |
+ | Name | Loopback Address | |
|
| 683 |
+ | RFC | [RFC4291] | |
|
| 684 |
+ | Allocation Date | February 2006 | |
|
| 685 |
+ | Termination Date | N/A | |
|
| 686 |
+ | Source | False | |
|
| 687 |
+ | Destination | False | |
|
| 688 |
+ | Forwardable | False | |
|
| 689 |
+ | Global | False | |
|
| 690 |
+ | Reserved-by-Protocol | True | |
|
| 691 |
+ +----------------------+------------------+*/ |
|
| 692 |
+ MustIPv6Addr("::1/128"),
|
|
| 693 |
+ |
|
| 694 |
+ /*+----------------------+---------------------+ |
|
| 695 |
+ | Attribute | Value | |
|
| 696 |
+ +----------------------+---------------------+ |
|
| 697 |
+ | Address Block | ::/128 | |
|
| 698 |
+ | Name | Unspecified Address | |
|
| 699 |
+ | RFC | [RFC4291] | |
|
| 700 |
+ | Allocation Date | February 2006 | |
|
| 701 |
+ | Termination Date | N/A | |
|
| 702 |
+ | Source | True | |
|
| 703 |
+ | Destination | False | |
|
| 704 |
+ | Forwardable | False | |
|
| 705 |
+ | Global | False | |
|
| 706 |
+ | Reserved-by-Protocol | True | |
|
| 707 |
+ +----------------------+---------------------+*/ |
|
| 708 |
+ MustIPv6Addr("::/128"),
|
|
| 709 |
+ |
|
| 710 |
+ /*+----------------------+---------------------+ |
|
| 711 |
+ | Attribute | Value | |
|
| 712 |
+ +----------------------+---------------------+ |
|
| 713 |
+ | Address Block | 64:ff9b::/96 | |
|
| 714 |
+ | Name | IPv4-IPv6 Translat. | |
|
| 715 |
+ | RFC | [RFC6052] | |
|
| 716 |
+ | Allocation Date | October 2010 | |
|
| 717 |
+ | Termination Date | N/A | |
|
| 718 |
+ | Source | True | |
|
| 719 |
+ | Destination | True | |
|
| 720 |
+ | Forwardable | True | |
|
| 721 |
+ | Global | True | |
|
| 722 |
+ | Reserved-by-Protocol | False | |
|
| 723 |
+ +----------------------+---------------------+*/ |
|
| 724 |
+ MustIPv6Addr("64:ff9b::/96"),
|
|
| 725 |
+ |
|
| 726 |
+ /*+----------------------+---------------------+ |
|
| 727 |
+ | Attribute | Value | |
|
| 728 |
+ +----------------------+---------------------+ |
|
| 729 |
+ | Address Block | ::ffff:0:0/96 | |
|
| 730 |
+ | Name | IPv4-mapped Address | |
|
| 731 |
+ | RFC | [RFC4291] | |
|
| 732 |
+ | Allocation Date | February 2006 | |
|
| 733 |
+ | Termination Date | N/A | |
|
| 734 |
+ | Source | False | |
|
| 735 |
+ | Destination | False | |
|
| 736 |
+ | Forwardable | False | |
|
| 737 |
+ | Global | False | |
|
| 738 |
+ | Reserved-by-Protocol | True | |
|
| 739 |
+ +----------------------+---------------------+*/ |
|
| 740 |
+ MustIPv6Addr("::ffff:0:0/96"),
|
|
| 741 |
+ |
|
| 742 |
+ /*+----------------------+----------------------------+ |
|
| 743 |
+ | Attribute | Value | |
|
| 744 |
+ +----------------------+----------------------------+ |
|
| 745 |
+ | Address Block | 100::/64 | |
|
| 746 |
+ | Name | Discard-Only Address Block | |
|
| 747 |
+ | RFC | [RFC6666] | |
|
| 748 |
+ | Allocation Date | June 2012 | |
|
| 749 |
+ | Termination Date | N/A | |
|
| 750 |
+ | Source | True | |
|
| 751 |
+ | Destination | True | |
|
| 752 |
+ | Forwardable | True | |
|
| 753 |
+ | Global | False | |
|
| 754 |
+ | Reserved-by-Protocol | False | |
|
| 755 |
+ +----------------------+----------------------------+*/ |
|
| 756 |
+ MustIPv6Addr("100::/64"),
|
|
| 757 |
+ |
|
| 758 |
+ /*+----------------------+---------------------------+ |
|
| 759 |
+ | Attribute | Value | |
|
| 760 |
+ +----------------------+---------------------------+ |
|
| 761 |
+ | Address Block | 2001::/23 | |
|
| 762 |
+ | Name | IETF Protocol Assignments | |
|
| 763 |
+ | RFC | [RFC2928] | |
|
| 764 |
+ | Allocation Date | September 2000 | |
|
| 765 |
+ | Termination Date | N/A | |
|
| 766 |
+ | Source | False[1] | |
|
| 767 |
+ | Destination | False[1] | |
|
| 768 |
+ | Forwardable | False[1] | |
|
| 769 |
+ | Global | False[1] | |
|
| 770 |
+ | Reserved-by-Protocol | False | |
|
| 771 |
+ +----------------------+---------------------------+*/ |
|
| 772 |
+ // [1] Unless allowed by a more specific allocation. |
|
| 773 |
+ MustIPv6Addr("2001::/16"),
|
|
| 774 |
+ |
|
| 775 |
+ /*+----------------------+----------------+ |
|
| 776 |
+ | Attribute | Value | |
|
| 777 |
+ +----------------------+----------------+ |
|
| 778 |
+ | Address Block | 2001::/32 | |
|
| 779 |
+ | Name | TEREDO | |
|
| 780 |
+ | RFC | [RFC4380] | |
|
| 781 |
+ | Allocation Date | January 2006 | |
|
| 782 |
+ | Termination Date | N/A | |
|
| 783 |
+ | Source | True | |
|
| 784 |
+ | Destination | True | |
|
| 785 |
+ | Forwardable | True | |
|
| 786 |
+ | Global | False | |
|
| 787 |
+ | Reserved-by-Protocol | False | |
|
| 788 |
+ +----------------------+----------------+*/ |
|
| 789 |
+ // Covered by previous entry, included for completeness. |
|
| 790 |
+ // |
|
| 791 |
+ // MustIPv6Addr("2001::/16"),
|
|
| 792 |
+ |
|
| 793 |
+ /*+----------------------+----------------+ |
|
| 794 |
+ | Attribute | Value | |
|
| 795 |
+ +----------------------+----------------+ |
|
| 796 |
+ | Address Block | 2001:2::/48 | |
|
| 797 |
+ | Name | Benchmarking | |
|
| 798 |
+ | RFC | [RFC5180] | |
|
| 799 |
+ | Allocation Date | April 2008 | |
|
| 800 |
+ | Termination Date | N/A | |
|
| 801 |
+ | Source | True | |
|
| 802 |
+ | Destination | True | |
|
| 803 |
+ | Forwardable | True | |
|
| 804 |
+ | Global | False | |
|
| 805 |
+ | Reserved-by-Protocol | False | |
|
| 806 |
+ +----------------------+----------------+*/ |
|
| 807 |
+ // Covered by previous entry, included for completeness. |
|
| 808 |
+ // |
|
| 809 |
+ // MustIPv6Addr("2001:2::/48"),
|
|
| 810 |
+ |
|
| 811 |
+ /*+----------------------+---------------+ |
|
| 812 |
+ | Attribute | Value | |
|
| 813 |
+ +----------------------+---------------+ |
|
| 814 |
+ | Address Block | 2001:db8::/32 | |
|
| 815 |
+ | Name | Documentation | |
|
| 816 |
+ | RFC | [RFC3849] | |
|
| 817 |
+ | Allocation Date | July 2004 | |
|
| 818 |
+ | Termination Date | N/A | |
|
| 819 |
+ | Source | False | |
|
| 820 |
+ | Destination | False | |
|
| 821 |
+ | Forwardable | False | |
|
| 822 |
+ | Global | False | |
|
| 823 |
+ | Reserved-by-Protocol | False | |
|
| 824 |
+ +----------------------+---------------+*/ |
|
| 825 |
+ // Covered by previous entry, included for completeness. |
|
| 826 |
+ // |
|
| 827 |
+ // MustIPv6Addr("2001:db8::/32"),
|
|
| 828 |
+ |
|
| 829 |
+ /*+----------------------+--------------+ |
|
| 830 |
+ | Attribute | Value | |
|
| 831 |
+ +----------------------+--------------+ |
|
| 832 |
+ | Address Block | 2001:10::/28 | |
|
| 833 |
+ | Name | ORCHID | |
|
| 834 |
+ | RFC | [RFC4843] | |
|
| 835 |
+ | Allocation Date | March 2007 | |
|
| 836 |
+ | Termination Date | March 2014 | |
|
| 837 |
+ | Source | False | |
|
| 838 |
+ | Destination | False | |
|
| 839 |
+ | Forwardable | False | |
|
| 840 |
+ | Global | False | |
|
| 841 |
+ | Reserved-by-Protocol | False | |
|
| 842 |
+ +----------------------+--------------+*/ |
|
| 843 |
+ // Covered by previous entry, included for completeness. |
|
| 844 |
+ // |
|
| 845 |
+ // MustIPv6Addr("2001:10::/28"),
|
|
| 846 |
+ |
|
| 847 |
+ /*+----------------------+---------------+ |
|
| 848 |
+ | Attribute | Value | |
|
| 849 |
+ +----------------------+---------------+ |
|
| 850 |
+ | Address Block | 2002::/16 [2] | |
|
| 851 |
+ | Name | 6to4 | |
|
| 852 |
+ | RFC | [RFC3056] | |
|
| 853 |
+ | Allocation Date | February 2001 | |
|
| 854 |
+ | Termination Date | N/A | |
|
| 855 |
+ | Source | True | |
|
| 856 |
+ | Destination | True | |
|
| 857 |
+ | Forwardable | True | |
|
| 858 |
+ | Global | N/A [2] | |
|
| 859 |
+ | Reserved-by-Protocol | False | |
|
| 860 |
+ +----------------------+---------------+*/ |
|
| 861 |
+ // [2] See [RFC3056] for details. |
|
| 862 |
+ MustIPv6Addr("2002::/16"),
|
|
| 863 |
+ |
|
| 864 |
+ /*+----------------------+--------------+ |
|
| 865 |
+ | Attribute | Value | |
|
| 866 |
+ +----------------------+--------------+ |
|
| 867 |
+ | Address Block | fc00::/7 | |
|
| 868 |
+ | Name | Unique-Local | |
|
| 869 |
+ | RFC | [RFC4193] | |
|
| 870 |
+ | Allocation Date | October 2005 | |
|
| 871 |
+ | Termination Date | N/A | |
|
| 872 |
+ | Source | True | |
|
| 873 |
+ | Destination | True | |
|
| 874 |
+ | Forwardable | True | |
|
| 875 |
+ | Global | False | |
|
| 876 |
+ | Reserved-by-Protocol | False | |
|
| 877 |
+ +----------------------+--------------+*/ |
|
| 878 |
+ MustIPv6Addr("fc00::/7"),
|
|
| 879 |
+ |
|
| 880 |
+ /*+----------------------+-----------------------+ |
|
| 881 |
+ | Attribute | Value | |
|
| 882 |
+ +----------------------+-----------------------+ |
|
| 883 |
+ | Address Block | fe80::/10 | |
|
| 884 |
+ | Name | Linked-Scoped Unicast | |
|
| 885 |
+ | RFC | [RFC4291] | |
|
| 886 |
+ | Allocation Date | February 2006 | |
|
| 887 |
+ | Termination Date | N/A | |
|
| 888 |
+ | Source | True | |
|
| 889 |
+ | Destination | True | |
|
| 890 |
+ | Forwardable | False | |
|
| 891 |
+ | Global | False | |
|
| 892 |
+ | Reserved-by-Protocol | True | |
|
| 893 |
+ +----------------------+-----------------------+*/ |
|
| 894 |
+ MustIPv6Addr("fe80::/10"),
|
|
| 895 |
+ }, |
|
| 896 |
+ 7335: {
|
|
| 897 |
+ // [RFC7335] IPv4 Service Continuity Prefix |
|
| 898 |
+ MustIPv4Addr("192.0.0.0/29"), // [RFC7335], §6 IANA Considerations
|
|
| 899 |
+ }, |
|
| 900 |
+ ForwardingBlacklist: { // Pseudo-RFC
|
|
| 901 |
+ // Blacklist of non-forwardable IP blocks taken from RFC6890 |
|
| 902 |
+ // |
|
| 903 |
+ // TODO: the attributes for forwardable should be |
|
| 904 |
+ // searcahble and embedded in the main list of RFCs |
|
| 905 |
+ // above. |
|
| 906 |
+ MustIPv4Addr("0.0.0.0/8"),
|
|
| 907 |
+ MustIPv4Addr("127.0.0.0/8"),
|
|
| 908 |
+ MustIPv4Addr("169.254.0.0/16"),
|
|
| 909 |
+ MustIPv4Addr("192.0.0.0/24"),
|
|
| 910 |
+ MustIPv4Addr("192.0.2.0/24"),
|
|
| 911 |
+ MustIPv4Addr("198.51.100.0/24"),
|
|
| 912 |
+ MustIPv4Addr("203.0.113.0/24"),
|
|
| 913 |
+ MustIPv4Addr("240.0.0.0/4"),
|
|
| 914 |
+ MustIPv4Addr("255.255.255.255/32"),
|
|
| 915 |
+ MustIPv6Addr("::1/128"),
|
|
| 916 |
+ MustIPv6Addr("::/128"),
|
|
| 917 |
+ MustIPv6Addr("::ffff:0:0/96"),
|
|
| 918 |
+ |
|
| 919 |
+ // There is no way of expressing a whitelist per RFC2928 |
|
| 920 |
+ // atm without creating a negative mask, which I don't |
|
| 921 |
+ // want to do atm. |
|
| 922 |
+ //MustIPv6Addr("2001::/23"),
|
|
| 923 |
+ |
|
| 924 |
+ MustIPv6Addr("2001:db8::/32"),
|
|
| 925 |
+ MustIPv6Addr("2001:10::/28"),
|
|
| 926 |
+ MustIPv6Addr("fe80::/10"),
|
|
| 927 |
+ }, |
|
| 928 |
+ } |
|
| 929 |
+} |
|
| 930 |
+ |
|
| 931 |
+// VisitAllRFCs iterates over all known RFCs and calls the visitor |
|
| 932 |
+func VisitAllRFCs(fn func(rfcNum uint, sockaddrs SockAddrs)) {
|
|
| 933 |
+ rfcNetMap := KnownRFCs() |
|
| 934 |
+ |
|
| 935 |
+ // Blacklist of faux-RFCs. Don't show the world that we're abusing the |
|
| 936 |
+ // RFC system in this library. |
|
| 937 |
+ rfcBlacklist := map[uint]struct{}{
|
|
| 938 |
+ ForwardingBlacklist: {},
|
|
| 939 |
+ } |
|
| 940 |
+ |
|
| 941 |
+ for rfcNum, sas := range rfcNetMap {
|
|
| 942 |
+ if _, found := rfcBlacklist[rfcNum]; !found {
|
|
| 943 |
+ fn(rfcNum, sas) |
|
| 944 |
+ } |
|
| 945 |
+ } |
|
| 946 |
+} |
| 0 | 947 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,19 @@ |
| 0 |
+package sockaddr |
|
| 1 |
+ |
|
| 2 |
+// RouteInterface specifies an interface for obtaining memoized route table and |
|
| 3 |
+// network information from a given OS. |
|
| 4 |
+type RouteInterface interface {
|
|
| 5 |
+ // GetDefaultInterfaceName returns the name of the interface that has a |
|
| 6 |
+ // default route or an error and an empty string if a problem was |
|
| 7 |
+ // encountered. |
|
| 8 |
+ GetDefaultInterfaceName() (string, error) |
|
| 9 |
+} |
|
| 10 |
+ |
|
| 11 |
+// VisitCommands visits each command used by the platform-specific RouteInfo |
|
| 12 |
+// implementation. |
|
| 13 |
+func (ri routeInfo) VisitCommands(fn func(name string, cmd []string)) {
|
|
| 14 |
+ for k, v := range ri.cmds {
|
|
| 15 |
+ cmds := append([]string(nil), v...) |
|
| 16 |
+ fn(k, cmds) |
|
| 17 |
+ } |
|
| 18 |
+} |
| 0 | 19 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,36 @@ |
| 0 |
+// +build darwin dragonfly freebsd netbsd openbsd |
|
| 1 |
+ |
|
| 2 |
+package sockaddr |
|
| 3 |
+ |
|
| 4 |
+import "os/exec" |
|
| 5 |
+ |
|
| 6 |
+var cmds map[string][]string = map[string][]string{
|
|
| 7 |
+ "route": {"/sbin/route", "-n", "get", "default"},
|
|
| 8 |
+} |
|
| 9 |
+ |
|
| 10 |
+type routeInfo struct {
|
|
| 11 |
+ cmds map[string][]string |
|
| 12 |
+} |
|
| 13 |
+ |
|
| 14 |
+// NewRouteInfo returns a BSD-specific implementation of the RouteInfo |
|
| 15 |
+// interface. |
|
| 16 |
+func NewRouteInfo() (routeInfo, error) {
|
|
| 17 |
+ return routeInfo{
|
|
| 18 |
+ cmds: cmds, |
|
| 19 |
+ }, nil |
|
| 20 |
+} |
|
| 21 |
+ |
|
| 22 |
+// GetDefaultInterfaceName returns the interface name attached to the default |
|
| 23 |
+// route on the default interface. |
|
| 24 |
+func (ri routeInfo) GetDefaultInterfaceName() (string, error) {
|
|
| 25 |
+ out, err := exec.Command(cmds["route"][0], cmds["route"][1:]...).Output() |
|
| 26 |
+ if err != nil {
|
|
| 27 |
+ return "", err |
|
| 28 |
+ } |
|
| 29 |
+ |
|
| 30 |
+ var ifName string |
|
| 31 |
+ if ifName, err = parseDefaultIfNameFromRoute(string(out)); err != nil {
|
|
| 32 |
+ return "", err |
|
| 33 |
+ } |
|
| 34 |
+ return ifName, nil |
|
| 35 |
+} |
| 0 | 36 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,10 @@ |
| 0 |
+// +build android nacl plan9 |
|
| 1 |
+ |
|
| 2 |
+package sockaddr |
|
| 3 |
+ |
|
| 4 |
+import "errors" |
|
| 5 |
+ |
|
| 6 |
+// getDefaultIfName is the default interface function for unsupported platforms. |
|
| 7 |
+func getDefaultIfName() (string, error) {
|
|
| 8 |
+ return "", errors.New("No default interface found (unsupported platform)")
|
|
| 9 |
+} |
| 0 | 10 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,37 @@ |
| 0 |
+package sockaddr |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "errors" |
|
| 4 |
+ "os/exec" |
|
| 5 |
+) |
|
| 6 |
+ |
|
| 7 |
+var cmds map[string][]string = map[string][]string{
|
|
| 8 |
+ "ip": {"/sbin/ip", "route"},
|
|
| 9 |
+} |
|
| 10 |
+ |
|
| 11 |
+type routeInfo struct {
|
|
| 12 |
+ cmds map[string][]string |
|
| 13 |
+} |
|
| 14 |
+ |
|
| 15 |
+// NewRouteInfo returns a Linux-specific implementation of the RouteInfo |
|
| 16 |
+// interface. |
|
| 17 |
+func NewRouteInfo() (routeInfo, error) {
|
|
| 18 |
+ return routeInfo{
|
|
| 19 |
+ cmds: cmds, |
|
| 20 |
+ }, nil |
|
| 21 |
+} |
|
| 22 |
+ |
|
| 23 |
+// GetDefaultInterfaceName returns the interface name attached to the default |
|
| 24 |
+// route on the default interface. |
|
| 25 |
+func (ri routeInfo) GetDefaultInterfaceName() (string, error) {
|
|
| 26 |
+ out, err := exec.Command(cmds["ip"][0], cmds["ip"][1:]...).Output() |
|
| 27 |
+ if err != nil {
|
|
| 28 |
+ return "", err |
|
| 29 |
+ } |
|
| 30 |
+ |
|
| 31 |
+ var ifName string |
|
| 32 |
+ if ifName, err = parseDefaultIfNameFromIPCmd(string(out)); err != nil {
|
|
| 33 |
+ return "", errors.New("No default interface found")
|
|
| 34 |
+ } |
|
| 35 |
+ return ifName, nil |
|
| 36 |
+} |
| 0 | 37 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,37 @@ |
| 0 |
+package sockaddr |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "errors" |
|
| 4 |
+ "os/exec" |
|
| 5 |
+) |
|
| 6 |
+ |
|
| 7 |
+var cmds map[string][]string = map[string][]string{
|
|
| 8 |
+ "route": {"/usr/sbin/route", "-n", "get", "default"},
|
|
| 9 |
+} |
|
| 10 |
+ |
|
| 11 |
+type routeInfo struct {
|
|
| 12 |
+ cmds map[string][]string |
|
| 13 |
+} |
|
| 14 |
+ |
|
| 15 |
+// NewRouteInfo returns a BSD-specific implementation of the RouteInfo |
|
| 16 |
+// interface. |
|
| 17 |
+func NewRouteInfo() (routeInfo, error) {
|
|
| 18 |
+ return routeInfo{
|
|
| 19 |
+ cmds: cmds, |
|
| 20 |
+ }, nil |
|
| 21 |
+} |
|
| 22 |
+ |
|
| 23 |
+// GetDefaultInterfaceName returns the interface name attached to the default |
|
| 24 |
+// route on the default interface. |
|
| 25 |
+func (ri routeInfo) GetDefaultInterfaceName() (string, error) {
|
|
| 26 |
+ out, err := exec.Command(cmds["route"][0], cmds["route"][1:]...).Output() |
|
| 27 |
+ if err != nil {
|
|
| 28 |
+ return "", err |
|
| 29 |
+ } |
|
| 30 |
+ |
|
| 31 |
+ var ifName string |
|
| 32 |
+ if ifName, err = parseDefaultIfNameFromRoute(string(out)); err != nil {
|
|
| 33 |
+ return "", errors.New("No default interface found")
|
|
| 34 |
+ } |
|
| 35 |
+ return ifName, nil |
|
| 36 |
+} |
| 0 | 37 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,41 @@ |
| 0 |
+package sockaddr |
|
| 1 |
+ |
|
| 2 |
+import "os/exec" |
|
| 3 |
+ |
|
| 4 |
+var cmds map[string][]string = map[string][]string{
|
|
| 5 |
+ "netstat": {"netstat", "-rn"},
|
|
| 6 |
+ "ipconfig": {"ipconfig"},
|
|
| 7 |
+} |
|
| 8 |
+ |
|
| 9 |
+type routeInfo struct {
|
|
| 10 |
+ cmds map[string][]string |
|
| 11 |
+} |
|
| 12 |
+ |
|
| 13 |
+// NewRouteInfo returns a BSD-specific implementation of the RouteInfo |
|
| 14 |
+// interface. |
|
| 15 |
+func NewRouteInfo() (routeInfo, error) {
|
|
| 16 |
+ return routeInfo{
|
|
| 17 |
+ cmds: cmds, |
|
| 18 |
+ }, nil |
|
| 19 |
+} |
|
| 20 |
+ |
|
| 21 |
+// GetDefaultInterfaceName returns the interface name attached to the default |
|
| 22 |
+// route on the default interface. |
|
| 23 |
+func (ri routeInfo) GetDefaultInterfaceName() (string, error) {
|
|
| 24 |
+ ifNameOut, err := exec.Command(cmds["netstat"][0], cmds["netstat"][1:]...).Output() |
|
| 25 |
+ if err != nil {
|
|
| 26 |
+ return "", err |
|
| 27 |
+ } |
|
| 28 |
+ |
|
| 29 |
+ ipconfigOut, err := exec.Command(cmds["ipconfig"][0], cmds["ipconfig"][1:]...).Output() |
|
| 30 |
+ if err != nil {
|
|
| 31 |
+ return "", err |
|
| 32 |
+ } |
|
| 33 |
+ |
|
| 34 |
+ ifName, err := parseDefaultIfNameWindows(string(ifNameOut), string(ipconfigOut)) |
|
| 35 |
+ if err != nil {
|
|
| 36 |
+ return "", err |
|
| 37 |
+ } |
|
| 38 |
+ |
|
| 39 |
+ return ifName, nil |
|
| 40 |
+} |
| 0 | 41 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,178 @@ |
| 0 |
+package sockaddr |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "fmt" |
|
| 4 |
+ "strings" |
|
| 5 |
+) |
|
| 6 |
+ |
|
| 7 |
+type SockAddrType int |
|
| 8 |
+type AttrName string |
|
| 9 |
+ |
|
| 10 |
+const ( |
|
| 11 |
+ TypeUnknown SockAddrType = 0x0 |
|
| 12 |
+ TypeUnix = 0x1 |
|
| 13 |
+ TypeIPv4 = 0x2 |
|
| 14 |
+ TypeIPv6 = 0x4 |
|
| 15 |
+ |
|
| 16 |
+ // TypeIP is the union of TypeIPv4 and TypeIPv6 |
|
| 17 |
+ TypeIP = 0x6 |
|
| 18 |
+) |
|
| 19 |
+ |
|
| 20 |
+type SockAddr interface {
|
|
| 21 |
+ // CmpRFC returns 0 if SockAddr exactly matches one of the matched RFC |
|
| 22 |
+ // networks, -1 if the receiver is contained within the RFC network, or |
|
| 23 |
+ // 1 if the address is not contained within the RFC. |
|
| 24 |
+ CmpRFC(rfcNum uint, sa SockAddr) int |
|
| 25 |
+ |
|
| 26 |
+ // Contains returns true if the SockAddr arg is contained within the |
|
| 27 |
+ // receiver |
|
| 28 |
+ Contains(SockAddr) bool |
|
| 29 |
+ |
|
| 30 |
+ // Equal allows for the comparison of two SockAddrs |
|
| 31 |
+ Equal(SockAddr) bool |
|
| 32 |
+ |
|
| 33 |
+ DialPacketArgs() (string, string) |
|
| 34 |
+ DialStreamArgs() (string, string) |
|
| 35 |
+ ListenPacketArgs() (string, string) |
|
| 36 |
+ ListenStreamArgs() (string, string) |
|
| 37 |
+ |
|
| 38 |
+ // String returns the string representation of SockAddr |
|
| 39 |
+ String() string |
|
| 40 |
+ |
|
| 41 |
+ // Type returns the SockAddrType |
|
| 42 |
+ Type() SockAddrType |
|
| 43 |
+} |
|
| 44 |
+ |
|
| 45 |
+// sockAddrAttrMap is a map of the SockAddr type-specific attributes. |
|
| 46 |
+var sockAddrAttrMap map[AttrName]func(SockAddr) string |
|
| 47 |
+var sockAddrAttrs []AttrName |
|
| 48 |
+ |
|
| 49 |
+func init() {
|
|
| 50 |
+ sockAddrInit() |
|
| 51 |
+} |
|
| 52 |
+ |
|
| 53 |
+// New creates a new SockAddr from the string. The order in which New() |
|
| 54 |
+// attempts to construct a SockAddr is: IPv4Addr, IPv6Addr, SockAddrUnix. |
|
| 55 |
+// |
|
| 56 |
+// NOTE: New() relies on the heuristic wherein if the path begins with either a |
|
| 57 |
+// '.' or '/' character before creating a new UnixSock. For UNIX sockets that |
|
| 58 |
+// are absolute paths or are nested within a sub-directory, this works as |
|
| 59 |
+// expected, however if the UNIX socket is contained in the current working |
|
| 60 |
+// directory, this will fail unless the path begins with "./" |
|
| 61 |
+// (e.g. "./my-local-socket"). Calls directly to NewUnixSock() do not suffer |
|
| 62 |
+// this limitation. Invalid IP addresses such as "256.0.0.0/-1" will run afoul |
|
| 63 |
+// of this heuristic and be assumed to be a valid UNIX socket path (which they |
|
| 64 |
+// are, but it is probably not what you want and you won't realize it until you |
|
| 65 |
+// stat(2) the file system to discover it doesn't exist). |
|
| 66 |
+func NewSockAddr(s string) (SockAddr, error) {
|
|
| 67 |
+ ipv4Addr, err := NewIPv4Addr(s) |
|
| 68 |
+ if err == nil {
|
|
| 69 |
+ return ipv4Addr, nil |
|
| 70 |
+ } |
|
| 71 |
+ |
|
| 72 |
+ ipv6Addr, err := NewIPv6Addr(s) |
|
| 73 |
+ if err == nil {
|
|
| 74 |
+ return ipv6Addr, nil |
|
| 75 |
+ } |
|
| 76 |
+ |
|
| 77 |
+ // Check to make sure the string begins with either a '.' or '/', or |
|
| 78 |
+ // contains a '/'. |
|
| 79 |
+ if len(s) > 1 && (strings.IndexAny(s[0:1], "./") != -1 || strings.IndexByte(s, '/') != -1) {
|
|
| 80 |
+ unixSock, err := NewUnixSock(s) |
|
| 81 |
+ if err == nil {
|
|
| 82 |
+ return unixSock, nil |
|
| 83 |
+ } |
|
| 84 |
+ } |
|
| 85 |
+ |
|
| 86 |
+ return nil, fmt.Errorf("Unable to convert %q to an IPv4 or IPv6 address, or a UNIX Socket", s)
|
|
| 87 |
+} |
|
| 88 |
+ |
|
| 89 |
+// ToIPAddr returns an IPAddr type or nil if the type conversion fails. |
|
| 90 |
+func ToIPAddr(sa SockAddr) *IPAddr {
|
|
| 91 |
+ ipa, ok := sa.(IPAddr) |
|
| 92 |
+ if !ok {
|
|
| 93 |
+ return nil |
|
| 94 |
+ } |
|
| 95 |
+ return &ipa |
|
| 96 |
+} |
|
| 97 |
+ |
|
| 98 |
+// ToIPv4Addr returns an IPv4Addr type or nil if the type conversion fails. |
|
| 99 |
+func ToIPv4Addr(sa SockAddr) *IPv4Addr {
|
|
| 100 |
+ switch v := sa.(type) {
|
|
| 101 |
+ case IPv4Addr: |
|
| 102 |
+ return &v |
|
| 103 |
+ default: |
|
| 104 |
+ return nil |
|
| 105 |
+ } |
|
| 106 |
+} |
|
| 107 |
+ |
|
| 108 |
+// ToIPv6Addr returns an IPv6Addr type or nil if the type conversion fails. |
|
| 109 |
+func ToIPv6Addr(sa SockAddr) *IPv6Addr {
|
|
| 110 |
+ switch v := sa.(type) {
|
|
| 111 |
+ case IPv6Addr: |
|
| 112 |
+ return &v |
|
| 113 |
+ default: |
|
| 114 |
+ return nil |
|
| 115 |
+ } |
|
| 116 |
+} |
|
| 117 |
+ |
|
| 118 |
+// ToUnixSock returns a UnixSock type or nil if the type conversion fails. |
|
| 119 |
+func ToUnixSock(sa SockAddr) *UnixSock {
|
|
| 120 |
+ switch v := sa.(type) {
|
|
| 121 |
+ case UnixSock: |
|
| 122 |
+ return &v |
|
| 123 |
+ default: |
|
| 124 |
+ return nil |
|
| 125 |
+ } |
|
| 126 |
+} |
|
| 127 |
+ |
|
| 128 |
+// SockAddrAttr returns a string representation of an attribute for the given |
|
| 129 |
+// SockAddr. |
|
| 130 |
+func SockAddrAttr(sa SockAddr, selector AttrName) string {
|
|
| 131 |
+ fn, found := sockAddrAttrMap[selector] |
|
| 132 |
+ if !found {
|
|
| 133 |
+ return "" |
|
| 134 |
+ } |
|
| 135 |
+ |
|
| 136 |
+ return fn(sa) |
|
| 137 |
+} |
|
| 138 |
+ |
|
| 139 |
+// String() for SockAddrType returns a string representation of the |
|
| 140 |
+// SockAddrType (e.g. "IPv4", "IPv6", "UNIX", "IP", or "unknown"). |
|
| 141 |
+func (sat SockAddrType) String() string {
|
|
| 142 |
+ switch sat {
|
|
| 143 |
+ case TypeIPv4: |
|
| 144 |
+ return "IPv4" |
|
| 145 |
+ case TypeIPv6: |
|
| 146 |
+ return "IPv6" |
|
| 147 |
+ // There is no concrete "IP" type. Leaving here as a reminder. |
|
| 148 |
+ // case TypeIP: |
|
| 149 |
+ // return "IP" |
|
| 150 |
+ case TypeUnix: |
|
| 151 |
+ return "UNIX" |
|
| 152 |
+ default: |
|
| 153 |
+ panic("unsupported type")
|
|
| 154 |
+ } |
|
| 155 |
+} |
|
| 156 |
+ |
|
| 157 |
+// sockAddrInit is called once at init() |
|
| 158 |
+func sockAddrInit() {
|
|
| 159 |
+ sockAddrAttrs = []AttrName{
|
|
| 160 |
+ "type", // type should be first |
|
| 161 |
+ "string", |
|
| 162 |
+ } |
|
| 163 |
+ |
|
| 164 |
+ sockAddrAttrMap = map[AttrName]func(sa SockAddr) string{
|
|
| 165 |
+ "string": func(sa SockAddr) string {
|
|
| 166 |
+ return sa.String() |
|
| 167 |
+ }, |
|
| 168 |
+ "type": func(sa SockAddr) string {
|
|
| 169 |
+ return sa.Type().String() |
|
| 170 |
+ }, |
|
| 171 |
+ } |
|
| 172 |
+} |
|
| 173 |
+ |
|
| 174 |
+// UnixSockAttrs returns a list of attributes supported by the UnixSock type |
|
| 175 |
+func SockAddrAttrs() []AttrName {
|
|
| 176 |
+ return sockAddrAttrs |
|
| 177 |
+} |
| 0 | 178 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,193 @@ |
| 0 |
+package sockaddr |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "bytes" |
|
| 4 |
+ "sort" |
|
| 5 |
+) |
|
| 6 |
+ |
|
| 7 |
+// SockAddrs is a slice of SockAddrs |
|
| 8 |
+type SockAddrs []SockAddr |
|
| 9 |
+ |
|
| 10 |
+func (s SockAddrs) Len() int { return len(s) }
|
|
| 11 |
+func (s SockAddrs) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
|
|
| 12 |
+ |
|
| 13 |
+// CmpAddrFunc is the function signature that must be met to be used in the |
|
| 14 |
+// OrderedAddrBy multiAddrSorter |
|
| 15 |
+type CmpAddrFunc func(p1, p2 *SockAddr) int |
|
| 16 |
+ |
|
| 17 |
+// multiAddrSorter implements the Sort interface, sorting the SockAddrs within. |
|
| 18 |
+type multiAddrSorter struct {
|
|
| 19 |
+ addrs SockAddrs |
|
| 20 |
+ cmp []CmpAddrFunc |
|
| 21 |
+} |
|
| 22 |
+ |
|
| 23 |
+// Sort sorts the argument slice according to the Cmp functions passed to |
|
| 24 |
+// OrderedAddrBy. |
|
| 25 |
+func (ms *multiAddrSorter) Sort(sockAddrs SockAddrs) {
|
|
| 26 |
+ ms.addrs = sockAddrs |
|
| 27 |
+ sort.Sort(ms) |
|
| 28 |
+} |
|
| 29 |
+ |
|
| 30 |
+// OrderedAddrBy sorts SockAddr by the list of sort function pointers. |
|
| 31 |
+func OrderedAddrBy(cmpFuncs ...CmpAddrFunc) *multiAddrSorter {
|
|
| 32 |
+ return &multiAddrSorter{
|
|
| 33 |
+ cmp: cmpFuncs, |
|
| 34 |
+ } |
|
| 35 |
+} |
|
| 36 |
+ |
|
| 37 |
+// Len is part of sort.Interface. |
|
| 38 |
+func (ms *multiAddrSorter) Len() int {
|
|
| 39 |
+ return len(ms.addrs) |
|
| 40 |
+} |
|
| 41 |
+ |
|
| 42 |
+// Less is part of sort.Interface. It is implemented by looping along the |
|
| 43 |
+// Cmp() functions until it finds a comparison that is either less than, |
|
| 44 |
+// equal to, or greater than. |
|
| 45 |
+func (ms *multiAddrSorter) Less(i, j int) bool {
|
|
| 46 |
+ p, q := &ms.addrs[i], &ms.addrs[j] |
|
| 47 |
+ // Try all but the last comparison. |
|
| 48 |
+ var k int |
|
| 49 |
+ for k = 0; k < len(ms.cmp)-1; k++ {
|
|
| 50 |
+ cmp := ms.cmp[k] |
|
| 51 |
+ x := cmp(p, q) |
|
| 52 |
+ switch x {
|
|
| 53 |
+ case -1: |
|
| 54 |
+ // p < q, so we have a decision. |
|
| 55 |
+ return true |
|
| 56 |
+ case 1: |
|
| 57 |
+ // p > q, so we have a decision. |
|
| 58 |
+ return false |
|
| 59 |
+ } |
|
| 60 |
+ // p == q; try the next comparison. |
|
| 61 |
+ } |
|
| 62 |
+ // All comparisons to here said "equal", so just return whatever the |
|
| 63 |
+ // final comparison reports. |
|
| 64 |
+ switch ms.cmp[k](p, q) {
|
|
| 65 |
+ case -1: |
|
| 66 |
+ return true |
|
| 67 |
+ case 1: |
|
| 68 |
+ return false |
|
| 69 |
+ default: |
|
| 70 |
+ // Still a tie! Now what? |
|
| 71 |
+ return false |
|
| 72 |
+ } |
|
| 73 |
+} |
|
| 74 |
+ |
|
| 75 |
+// Swap is part of sort.Interface. |
|
| 76 |
+func (ms *multiAddrSorter) Swap(i, j int) {
|
|
| 77 |
+ ms.addrs[i], ms.addrs[j] = ms.addrs[j], ms.addrs[i] |
|
| 78 |
+} |
|
| 79 |
+ |
|
| 80 |
+const ( |
|
| 81 |
+ // NOTE (sean@): These constants are here for code readability only and |
|
| 82 |
+ // are sprucing up the code for readability purposes. Some of the |
|
| 83 |
+ // Cmp*() variants have confusing logic (especially when dealing with |
|
| 84 |
+ // mixed-type comparisons) and this, I think, has made it easier to grok |
|
| 85 |
+ // the code faster. |
|
| 86 |
+ sortReceiverBeforeArg = -1 |
|
| 87 |
+ sortDeferDecision = 0 |
|
| 88 |
+ sortArgBeforeReceiver = 1 |
|
| 89 |
+) |
|
| 90 |
+ |
|
| 91 |
+// AscAddress is a sorting function to sort SockAddrs by their respective |
|
| 92 |
+// address type. Non-equal types are deferred in the sort. |
|
| 93 |
+func AscAddress(p1Ptr, p2Ptr *SockAddr) int {
|
|
| 94 |
+ p1 := *p1Ptr |
|
| 95 |
+ p2 := *p2Ptr |
|
| 96 |
+ |
|
| 97 |
+ switch v := p1.(type) {
|
|
| 98 |
+ case IPv4Addr: |
|
| 99 |
+ return v.CmpAddress(p2) |
|
| 100 |
+ case IPv6Addr: |
|
| 101 |
+ return v.CmpAddress(p2) |
|
| 102 |
+ case UnixSock: |
|
| 103 |
+ return v.CmpAddress(p2) |
|
| 104 |
+ default: |
|
| 105 |
+ return sortDeferDecision |
|
| 106 |
+ } |
|
| 107 |
+} |
|
| 108 |
+ |
|
| 109 |
+// AscPort is a sorting function to sort SockAddrs by their respective address |
|
| 110 |
+// type. Non-equal types are deferred in the sort. |
|
| 111 |
+func AscPort(p1Ptr, p2Ptr *SockAddr) int {
|
|
| 112 |
+ p1 := *p1Ptr |
|
| 113 |
+ p2 := *p2Ptr |
|
| 114 |
+ |
|
| 115 |
+ switch v := p1.(type) {
|
|
| 116 |
+ case IPv4Addr: |
|
| 117 |
+ return v.CmpPort(p2) |
|
| 118 |
+ case IPv6Addr: |
|
| 119 |
+ return v.CmpPort(p2) |
|
| 120 |
+ default: |
|
| 121 |
+ return sortDeferDecision |
|
| 122 |
+ } |
|
| 123 |
+} |
|
| 124 |
+ |
|
| 125 |
+// AscPrivate is a sorting function to sort "more secure" private values before |
|
| 126 |
+// "more public" values. Both IPv4 and IPv6 are compared against RFC6890 |
|
| 127 |
+// (RFC6890 includes, and is not limited to, RFC1918 and RFC6598 for IPv4, and |
|
| 128 |
+// IPv6 includes RFC4193). |
|
| 129 |
+func AscPrivate(p1Ptr, p2Ptr *SockAddr) int {
|
|
| 130 |
+ p1 := *p1Ptr |
|
| 131 |
+ p2 := *p2Ptr |
|
| 132 |
+ |
|
| 133 |
+ switch v := p1.(type) {
|
|
| 134 |
+ case IPv4Addr, IPv6Addr: |
|
| 135 |
+ return v.CmpRFC(6890, p2) |
|
| 136 |
+ default: |
|
| 137 |
+ return sortDeferDecision |
|
| 138 |
+ } |
|
| 139 |
+} |
|
| 140 |
+ |
|
| 141 |
+// AscNetworkSize is a sorting function to sort SockAddrs based on their network |
|
| 142 |
+// size. Non-equal types are deferred in the sort. |
|
| 143 |
+func AscNetworkSize(p1Ptr, p2Ptr *SockAddr) int {
|
|
| 144 |
+ p1 := *p1Ptr |
|
| 145 |
+ p2 := *p2Ptr |
|
| 146 |
+ p1Type := p1.Type() |
|
| 147 |
+ p2Type := p2.Type() |
|
| 148 |
+ |
|
| 149 |
+ // Network size operations on non-IP types make no sense |
|
| 150 |
+ if p1Type != p2Type && p1Type != TypeIP {
|
|
| 151 |
+ return sortDeferDecision |
|
| 152 |
+ } |
|
| 153 |
+ |
|
| 154 |
+ ipA := p1.(IPAddr) |
|
| 155 |
+ ipB := p2.(IPAddr) |
|
| 156 |
+ |
|
| 157 |
+ return bytes.Compare([]byte(*ipA.NetIPMask()), []byte(*ipB.NetIPMask())) |
|
| 158 |
+} |
|
| 159 |
+ |
|
| 160 |
+// AscType is a sorting function to sort "more secure" types before |
|
| 161 |
+// "less-secure" types. |
|
| 162 |
+func AscType(p1Ptr, p2Ptr *SockAddr) int {
|
|
| 163 |
+ p1 := *p1Ptr |
|
| 164 |
+ p2 := *p2Ptr |
|
| 165 |
+ p1Type := p1.Type() |
|
| 166 |
+ p2Type := p2.Type() |
|
| 167 |
+ switch {
|
|
| 168 |
+ case p1Type < p2Type: |
|
| 169 |
+ return sortReceiverBeforeArg |
|
| 170 |
+ case p1Type == p2Type: |
|
| 171 |
+ return sortDeferDecision |
|
| 172 |
+ case p1Type > p2Type: |
|
| 173 |
+ return sortArgBeforeReceiver |
|
| 174 |
+ default: |
|
| 175 |
+ return sortDeferDecision |
|
| 176 |
+ } |
|
| 177 |
+} |
|
| 178 |
+ |
|
| 179 |
+// FilterByType returns two lists: a list of matched and unmatched SockAddrs |
|
| 180 |
+func (sas SockAddrs) FilterByType(type_ SockAddrType) (matched, excluded SockAddrs) {
|
|
| 181 |
+ matched = make(SockAddrs, 0, len(sas)) |
|
| 182 |
+ excluded = make(SockAddrs, 0, len(sas)) |
|
| 183 |
+ |
|
| 184 |
+ for _, sa := range sas {
|
|
| 185 |
+ if sa.Type()&type_ != 0 {
|
|
| 186 |
+ matched = append(matched, sa) |
|
| 187 |
+ } else {
|
|
| 188 |
+ excluded = append(excluded, sa) |
|
| 189 |
+ } |
|
| 190 |
+ } |
|
| 191 |
+ return matched, excluded |
|
| 192 |
+} |
| 0 | 193 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,135 @@ |
| 0 |
+package sockaddr |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "fmt" |
|
| 4 |
+ "strings" |
|
| 5 |
+) |
|
| 6 |
+ |
|
| 7 |
+type UnixSock struct {
|
|
| 8 |
+ SockAddr |
|
| 9 |
+ path string |
|
| 10 |
+} |
|
| 11 |
+type UnixSocks []*UnixSock |
|
| 12 |
+ |
|
| 13 |
+// unixAttrMap is a map of the UnixSockAddr type-specific attributes. |
|
| 14 |
+var unixAttrMap map[AttrName]func(UnixSock) string |
|
| 15 |
+var unixAttrs []AttrName |
|
| 16 |
+ |
|
| 17 |
+func init() {
|
|
| 18 |
+ unixAttrInit() |
|
| 19 |
+} |
|
| 20 |
+ |
|
| 21 |
+// NewUnixSock creates an UnixSock from a string path. String can be in the |
|
| 22 |
+// form of either URI-based string (e.g. `file:///etc/passwd`), an absolute |
|
| 23 |
+// path (e.g. `/etc/passwd`), or a relative path (e.g. `./foo`). |
|
| 24 |
+func NewUnixSock(s string) (ret UnixSock, err error) {
|
|
| 25 |
+ ret.path = s |
|
| 26 |
+ return ret, nil |
|
| 27 |
+} |
|
| 28 |
+ |
|
| 29 |
+// CmpAddress follows the Cmp() standard protocol and returns: |
|
| 30 |
+// |
|
| 31 |
+// - -1 If the receiver should sort first because its name lexically sorts before arg |
|
| 32 |
+// - 0 if the SockAddr arg is not a UnixSock, or is a UnixSock with the same path. |
|
| 33 |
+// - 1 If the argument should sort first. |
|
| 34 |
+func (us UnixSock) CmpAddress(sa SockAddr) int {
|
|
| 35 |
+ usb, ok := sa.(UnixSock) |
|
| 36 |
+ if !ok {
|
|
| 37 |
+ return sortDeferDecision |
|
| 38 |
+ } |
|
| 39 |
+ |
|
| 40 |
+ return strings.Compare(us.Path(), usb.Path()) |
|
| 41 |
+} |
|
| 42 |
+ |
|
| 43 |
+// DialPacketArgs returns the arguments required to be passed to net.DialUnix() |
|
| 44 |
+// with the `unixgram` network type. |
|
| 45 |
+func (us UnixSock) DialPacketArgs() (network, dialArgs string) {
|
|
| 46 |
+ return "unixgram", us.path |
|
| 47 |
+} |
|
| 48 |
+ |
|
| 49 |
+// DialStreamArgs returns the arguments required to be passed to net.DialUnix() |
|
| 50 |
+// with the `unix` network type. |
|
| 51 |
+func (us UnixSock) DialStreamArgs() (network, dialArgs string) {
|
|
| 52 |
+ return "unix", us.path |
|
| 53 |
+} |
|
| 54 |
+ |
|
| 55 |
+// Equal returns true if a SockAddr is equal to the receiving UnixSock. |
|
| 56 |
+func (us UnixSock) Equal(sa SockAddr) bool {
|
|
| 57 |
+ usb, ok := sa.(UnixSock) |
|
| 58 |
+ if !ok {
|
|
| 59 |
+ return false |
|
| 60 |
+ } |
|
| 61 |
+ |
|
| 62 |
+ if us.Path() != usb.Path() {
|
|
| 63 |
+ return false |
|
| 64 |
+ } |
|
| 65 |
+ |
|
| 66 |
+ return true |
|
| 67 |
+} |
|
| 68 |
+ |
|
| 69 |
+// ListenPacketArgs returns the arguments required to be passed to |
|
| 70 |
+// net.ListenUnixgram() with the `unixgram` network type. |
|
| 71 |
+func (us UnixSock) ListenPacketArgs() (network, dialArgs string) {
|
|
| 72 |
+ return "unixgram", us.path |
|
| 73 |
+} |
|
| 74 |
+ |
|
| 75 |
+// ListenStreamArgs returns the arguments required to be passed to |
|
| 76 |
+// net.ListenUnix() with the `unix` network type. |
|
| 77 |
+func (us UnixSock) ListenStreamArgs() (network, dialArgs string) {
|
|
| 78 |
+ return "unix", us.path |
|
| 79 |
+} |
|
| 80 |
+ |
|
| 81 |
+// MustUnixSock is a helper method that must return an UnixSock or panic on |
|
| 82 |
+// invalid input. |
|
| 83 |
+func MustUnixSock(addr string) UnixSock {
|
|
| 84 |
+ us, err := NewUnixSock(addr) |
|
| 85 |
+ if err != nil {
|
|
| 86 |
+ panic(fmt.Sprintf("Unable to create a UnixSock from %+q: %v", addr, err))
|
|
| 87 |
+ } |
|
| 88 |
+ return us |
|
| 89 |
+} |
|
| 90 |
+ |
|
| 91 |
+// Path returns the given path of the UnixSock |
|
| 92 |
+func (us UnixSock) Path() string {
|
|
| 93 |
+ return us.path |
|
| 94 |
+} |
|
| 95 |
+ |
|
| 96 |
+// String returns the path of the UnixSock |
|
| 97 |
+func (us UnixSock) String() string {
|
|
| 98 |
+ return fmt.Sprintf("%+q", us.path)
|
|
| 99 |
+} |
|
| 100 |
+ |
|
| 101 |
+// Type is used as a type switch and returns TypeUnix |
|
| 102 |
+func (UnixSock) Type() SockAddrType {
|
|
| 103 |
+ return TypeUnix |
|
| 104 |
+} |
|
| 105 |
+ |
|
| 106 |
+// UnixSockAttrs returns a list of attributes supported by the UnixSockAddr type |
|
| 107 |
+func UnixSockAttrs() []AttrName {
|
|
| 108 |
+ return unixAttrs |
|
| 109 |
+} |
|
| 110 |
+ |
|
| 111 |
+// UnixSockAttr returns a string representation of an attribute for the given |
|
| 112 |
+// UnixSock. |
|
| 113 |
+func UnixSockAttr(us UnixSock, attrName AttrName) string {
|
|
| 114 |
+ fn, found := unixAttrMap[attrName] |
|
| 115 |
+ if !found {
|
|
| 116 |
+ return "" |
|
| 117 |
+ } |
|
| 118 |
+ |
|
| 119 |
+ return fn(us) |
|
| 120 |
+} |
|
| 121 |
+ |
|
| 122 |
+// unixAttrInit is called once at init() |
|
| 123 |
+func unixAttrInit() {
|
|
| 124 |
+ // Sorted for human readability |
|
| 125 |
+ unixAttrs = []AttrName{
|
|
| 126 |
+ "path", |
|
| 127 |
+ } |
|
| 128 |
+ |
|
| 129 |
+ unixAttrMap = map[AttrName]func(us UnixSock) string{
|
|
| 130 |
+ "path": func(us UnixSock) string {
|
|
| 131 |
+ return us.Path() |
|
| 132 |
+ }, |
|
| 133 |
+ } |
|
| 134 |
+} |
| ... | ... |
@@ -82,7 +82,7 @@ least one existing member in order to join the cluster. The new member |
| 82 | 82 |
does a full state sync with the existing member over TCP and begins gossiping its |
| 83 | 83 |
existence to the cluster. |
| 84 | 84 |
|
| 85 |
-Gossip is done over UDP to a with a configurable but fixed fanout and interval. |
|
| 85 |
+Gossip is done over UDP with a configurable but fixed fanout and interval. |
|
| 86 | 86 |
This ensures that network usage is constant with regards to number of nodes, as opposed to |
| 87 | 87 |
exponential growth that can occur with traditional heartbeat mechanisms. |
| 88 | 88 |
Complete state exchanges with a random node are done periodically over |
| 89 | 89 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,69 @@ |
| 0 |
+package memberlist |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "sync" |
|
| 4 |
+ "time" |
|
| 5 |
+ |
|
| 6 |
+ "github.com/armon/go-metrics" |
|
| 7 |
+) |
|
| 8 |
+ |
|
| 9 |
+// awareness manages a simple metric for tracking the estimated health of the |
|
| 10 |
+// local node. Health is primary the node's ability to respond in the soft |
|
| 11 |
+// real-time manner required for correct health checking of other nodes in the |
|
| 12 |
+// cluster. |
|
| 13 |
+type awareness struct {
|
|
| 14 |
+ sync.RWMutex |
|
| 15 |
+ |
|
| 16 |
+ // max is the upper threshold for the timeout scale (the score will be |
|
| 17 |
+ // constrained to be from 0 <= score < max). |
|
| 18 |
+ max int |
|
| 19 |
+ |
|
| 20 |
+ // score is the current awareness score. Lower values are healthier and |
|
| 21 |
+ // zero is the minimum value. |
|
| 22 |
+ score int |
|
| 23 |
+} |
|
| 24 |
+ |
|
| 25 |
+// newAwareness returns a new awareness object. |
|
| 26 |
+func newAwareness(max int) *awareness {
|
|
| 27 |
+ return &awareness{
|
|
| 28 |
+ max: max, |
|
| 29 |
+ score: 0, |
|
| 30 |
+ } |
|
| 31 |
+} |
|
| 32 |
+ |
|
| 33 |
+// ApplyDelta takes the given delta and applies it to the score in a thread-safe |
|
| 34 |
+// manner. It also enforces a floor of zero and a max of max, so deltas may not |
|
| 35 |
+// change the overall score if it's railed at one of the extremes. |
|
| 36 |
+func (a *awareness) ApplyDelta(delta int) {
|
|
| 37 |
+ a.Lock() |
|
| 38 |
+ initial := a.score |
|
| 39 |
+ a.score += delta |
|
| 40 |
+ if a.score < 0 {
|
|
| 41 |
+ a.score = 0 |
|
| 42 |
+ } else if a.score > (a.max - 1) {
|
|
| 43 |
+ a.score = (a.max - 1) |
|
| 44 |
+ } |
|
| 45 |
+ final := a.score |
|
| 46 |
+ a.Unlock() |
|
| 47 |
+ |
|
| 48 |
+ if initial != final {
|
|
| 49 |
+ metrics.SetGauge([]string{"memberlist", "health", "score"}, float32(final))
|
|
| 50 |
+ } |
|
| 51 |
+} |
|
| 52 |
+ |
|
| 53 |
+// GetHealthScore returns the raw health score. |
|
| 54 |
+func (a *awareness) GetHealthScore() int {
|
|
| 55 |
+ a.RLock() |
|
| 56 |
+ score := a.score |
|
| 57 |
+ a.RUnlock() |
|
| 58 |
+ return score |
|
| 59 |
+} |
|
| 60 |
+ |
|
| 61 |
+// ScaleTimeout takes the given duration and scales it based on the current |
|
| 62 |
+// score. Less healthyness will lead to longer timeouts. |
|
| 63 |
+func (a *awareness) ScaleTimeout(timeout time.Duration) time.Duration {
|
|
| 64 |
+ a.RLock() |
|
| 65 |
+ score := a.score |
|
| 66 |
+ a.RUnlock() |
|
| 67 |
+ return timeout * (time.Duration(score) + 1) |
|
| 68 |
+} |
| ... | ... |
@@ -11,10 +11,15 @@ type Config struct {
|
| 11 | 11 |
// The name of this node. This must be unique in the cluster. |
| 12 | 12 |
Name string |
| 13 | 13 |
|
| 14 |
+ // Transport is a hook for providing custom code to communicate with |
|
| 15 |
+ // other nodes. If this is left nil, then memberlist will by default |
|
| 16 |
+ // make a NetTransport using BindAddr and BindPort from this structure. |
|
| 17 |
+ Transport Transport |
|
| 18 |
+ |
|
| 14 | 19 |
// Configuration related to what address to bind to and ports to |
| 15 |
- // listen on. The port is used for both UDP and TCP gossip. |
|
| 16 |
- // It is assumed other nodes are running on this port, but they |
|
| 17 |
- // do not need to. |
|
| 20 |
+ // listen on. The port is used for both UDP and TCP gossip. It is |
|
| 21 |
+ // assumed other nodes are running on this port, but they do not need |
|
| 22 |
+ // to. |
|
| 18 | 23 |
BindAddr string |
| 19 | 24 |
BindPort int |
| 20 | 25 |
|
| ... | ... |
@@ -28,8 +33,11 @@ type Config struct {
|
| 28 | 28 |
// ProtocolVersionMax. |
| 29 | 29 |
ProtocolVersion uint8 |
| 30 | 30 |
|
| 31 |
- // TCPTimeout is the timeout for establishing a TCP connection with |
|
| 32 |
- // a remote node for a full state sync. |
|
| 31 |
+ // TCPTimeout is the timeout for establishing a stream connection with |
|
| 32 |
+ // a remote node for a full state sync, and for stream read and write |
|
| 33 |
+ // operations. This is a legacy name for backwards compatibility, but |
|
| 34 |
+ // should really be called StreamTimeout now that we have generalized |
|
| 35 |
+ // the transport. |
|
| 33 | 36 |
TCPTimeout time.Duration |
| 34 | 37 |
|
| 35 | 38 |
// IndirectChecks is the number of nodes that will be asked to perform |
| ... | ... |
@@ -63,6 +71,23 @@ type Config struct {
|
| 63 | 63 |
// still alive. |
| 64 | 64 |
SuspicionMult int |
| 65 | 65 |
|
| 66 |
+ // SuspicionMaxTimeoutMult is the multiplier applied to the |
|
| 67 |
+ // SuspicionTimeout used as an upper bound on detection time. This max |
|
| 68 |
+ // timeout is calculated using the formula: |
|
| 69 |
+ // |
|
| 70 |
+ // SuspicionMaxTimeout = SuspicionMaxTimeoutMult * SuspicionTimeout |
|
| 71 |
+ // |
|
| 72 |
+ // If everything is working properly, confirmations from other nodes will |
|
| 73 |
+ // accelerate suspicion timers in a manner which will cause the timeout |
|
| 74 |
+ // to reach the base SuspicionTimeout before that elapses, so this value |
|
| 75 |
+ // will typically only come into play if a node is experiencing issues |
|
| 76 |
+ // communicating with other nodes. It should be set to a something fairly |
|
| 77 |
+ // large so that a node having problems will have a lot of chances to |
|
| 78 |
+ // recover before falsely declaring other nodes as failed, but short |
|
| 79 |
+ // enough for a legitimately isolated node to still make progress marking |
|
| 80 |
+ // nodes failed in a reasonable amount of time. |
|
| 81 |
+ SuspicionMaxTimeoutMult int |
|
| 82 |
+ |
|
| 66 | 83 |
// PushPullInterval is the interval between complete state syncs. |
| 67 | 84 |
// Complete state syncs are done with a single node over TCP and are |
| 68 | 85 |
// quite expensive relative to standard gossiped messages. Setting this |
| ... | ... |
@@ -91,6 +116,11 @@ type Config struct {
|
| 91 | 91 |
// indirect UDP pings. |
| 92 | 92 |
DisableTcpPings bool |
| 93 | 93 |
|
| 94 |
+ // AwarenessMaxMultiplier will increase the probe interval if the node |
|
| 95 |
+ // becomes aware that it might be degraded and not meeting the soft real |
|
| 96 |
+ // time requirements to reliably probe other nodes. |
|
| 97 |
+ AwarenessMaxMultiplier int |
|
| 98 |
+ |
|
| 94 | 99 |
// GossipInterval and GossipNodes are used to configure the gossip |
| 95 | 100 |
// behavior of memberlist. |
| 96 | 101 |
// |
| ... | ... |
@@ -104,8 +134,12 @@ type Config struct {
|
| 104 | 104 |
// per GossipInterval. Increasing this number causes the gossip messages |
| 105 | 105 |
// to propagate across the cluster more quickly at the expense of |
| 106 | 106 |
// increased bandwidth. |
| 107 |
- GossipInterval time.Duration |
|
| 108 |
- GossipNodes int |
|
| 107 |
+ // |
|
| 108 |
+ // GossipToTheDeadTime is the interval after which a node has died that |
|
| 109 |
+ // we will still try to gossip to it. This gives it a chance to refute. |
|
| 110 |
+ GossipInterval time.Duration |
|
| 111 |
+ GossipNodes int |
|
| 112 |
+ GossipToTheDeadTime time.Duration |
|
| 109 | 113 |
|
| 110 | 114 |
// EnableCompression is used to control message compression. This can |
| 111 | 115 |
// be used to reduce bandwidth usage at the cost of slightly more CPU |
| ... | ... |
@@ -157,6 +191,20 @@ type Config struct {
|
| 157 | 157 |
// behavior for using LogOutput. You cannot specify both LogOutput and Logger |
| 158 | 158 |
// at the same time. |
| 159 | 159 |
Logger *log.Logger |
| 160 |
+ |
|
| 161 |
+ // Size of Memberlist's internal channel which handles UDP messages. The |
|
| 162 |
+ // size of this determines the size of the queue which Memberlist will keep |
|
| 163 |
+ // while UDP messages are handled. |
|
| 164 |
+ HandoffQueueDepth int |
|
| 165 |
+ |
|
| 166 |
+ // Maximum number of bytes that memberlist will put in a packet (this |
|
| 167 |
+ // will be for UDP packets by default with a NetTransport). A safe value |
|
| 168 |
+ // for this is typically 1400 bytes (which is the default). However, |
|
| 169 |
+ // depending on your network's MTU (Maximum Transmission Unit) you may |
|
| 170 |
+ // be able to increase this to get more content into each gossip packet. |
|
| 171 |
+ // This is a legacy name for backward compatibility but should really be |
|
| 172 |
+ // called PacketBufferSize now that we have generalized the transport. |
|
| 173 |
+ UDPBufferSize int |
|
| 160 | 174 |
} |
| 161 | 175 |
|
| 162 | 176 |
// DefaultLANConfig returns a sane set of configurations for Memberlist. |
| ... | ... |
@@ -168,23 +216,26 @@ type Config struct {
|
| 168 | 168 |
func DefaultLANConfig() *Config {
|
| 169 | 169 |
hostname, _ := os.Hostname() |
| 170 | 170 |
return &Config{
|
| 171 |
- Name: hostname, |
|
| 172 |
- BindAddr: "0.0.0.0", |
|
| 173 |
- BindPort: 7946, |
|
| 174 |
- AdvertiseAddr: "", |
|
| 175 |
- AdvertisePort: 7946, |
|
| 176 |
- ProtocolVersion: ProtocolVersion2Compatible, |
|
| 177 |
- TCPTimeout: 10 * time.Second, // Timeout after 10 seconds |
|
| 178 |
- IndirectChecks: 3, // Use 3 nodes for the indirect ping |
|
| 179 |
- RetransmitMult: 4, // Retransmit a message 4 * log(N+1) nodes |
|
| 180 |
- SuspicionMult: 5, // Suspect a node for 5 * log(N+1) * Interval |
|
| 181 |
- PushPullInterval: 30 * time.Second, // Low frequency |
|
| 182 |
- ProbeTimeout: 500 * time.Millisecond, // Reasonable RTT time for LAN |
|
| 183 |
- ProbeInterval: 1 * time.Second, // Failure check every second |
|
| 184 |
- DisableTcpPings: false, // TCP pings are safe, even with mixed versions |
|
| 185 |
- |
|
| 186 |
- GossipNodes: 3, // Gossip to 3 nodes |
|
| 187 |
- GossipInterval: 200 * time.Millisecond, // Gossip more rapidly |
|
| 171 |
+ Name: hostname, |
|
| 172 |
+ BindAddr: "0.0.0.0", |
|
| 173 |
+ BindPort: 7946, |
|
| 174 |
+ AdvertiseAddr: "", |
|
| 175 |
+ AdvertisePort: 7946, |
|
| 176 |
+ ProtocolVersion: ProtocolVersion2Compatible, |
|
| 177 |
+ TCPTimeout: 10 * time.Second, // Timeout after 10 seconds |
|
| 178 |
+ IndirectChecks: 3, // Use 3 nodes for the indirect ping |
|
| 179 |
+ RetransmitMult: 4, // Retransmit a message 4 * log(N+1) nodes |
|
| 180 |
+ SuspicionMult: 5, // Suspect a node for 5 * log(N+1) * Interval |
|
| 181 |
+ SuspicionMaxTimeoutMult: 6, // For 10k nodes this will give a max timeout of 120 seconds |
|
| 182 |
+ PushPullInterval: 30 * time.Second, // Low frequency |
|
| 183 |
+ ProbeTimeout: 500 * time.Millisecond, // Reasonable RTT time for LAN |
|
| 184 |
+ ProbeInterval: 1 * time.Second, // Failure check every second |
|
| 185 |
+ DisableTcpPings: false, // TCP pings are safe, even with mixed versions |
|
| 186 |
+ AwarenessMaxMultiplier: 8, // Probe interval backs off to 8 seconds |
|
| 187 |
+ |
|
| 188 |
+ GossipNodes: 3, // Gossip to 3 nodes |
|
| 189 |
+ GossipInterval: 200 * time.Millisecond, // Gossip more rapidly |
|
| 190 |
+ GossipToTheDeadTime: 30 * time.Second, // Same as push/pull |
|
| 188 | 191 |
|
| 189 | 192 |
EnableCompression: true, // Enable compression by default |
| 190 | 193 |
|
| ... | ... |
@@ -192,6 +243,9 @@ func DefaultLANConfig() *Config {
|
| 192 | 192 |
Keyring: nil, |
| 193 | 193 |
|
| 194 | 194 |
DNSConfigPath: "/etc/resolv.conf", |
| 195 |
+ |
|
| 196 |
+ HandoffQueueDepth: 1024, |
|
| 197 |
+ UDPBufferSize: 1400, |
|
| 195 | 198 |
} |
| 196 | 199 |
} |
| 197 | 200 |
|
| ... | ... |
@@ -207,6 +261,7 @@ func DefaultWANConfig() *Config {
|
| 207 | 207 |
conf.ProbeInterval = 5 * time.Second |
| 208 | 208 |
conf.GossipNodes = 4 // Gossip less frequently, but to an additional node |
| 209 | 209 |
conf.GossipInterval = 500 * time.Millisecond |
| 210 |
+ conf.GossipToTheDeadTime = 60 * time.Second |
|
| 210 | 211 |
return conf |
| 211 | 212 |
} |
| 212 | 213 |
|
| ... | ... |
@@ -223,6 +278,7 @@ func DefaultLocalConfig() *Config {
|
| 223 | 223 |
conf.ProbeTimeout = 200 * time.Millisecond |
| 224 | 224 |
conf.ProbeInterval = time.Second |
| 225 | 225 |
conf.GossipInterval = 100 * time.Millisecond |
| 226 |
+ conf.GossipToTheDeadTime = 15 * time.Second |
|
| 226 | 227 |
return conf |
| 227 | 228 |
} |
| 228 | 229 |
|
| ... | ... |
@@ -12,7 +12,7 @@ type Delegate interface {
|
| 12 | 12 |
// NotifyMsg is called when a user-data message is received. |
| 13 | 13 |
// Care should be taken that this method does not block, since doing |
| 14 | 14 |
// so would block the entire UDP packet receive loop. Additionally, the byte |
| 15 |
- // slice may be modified after the call returns, so it should be copied if needed. |
|
| 15 |
+ // slice may be modified after the call returns, so it should be copied if needed |
|
| 16 | 16 |
NotifyMsg([]byte) |
| 17 | 17 |
|
| 18 | 18 |
// GetBroadcasts is called when user data messages can be broadcast. |
| ... | ... |
@@ -58,6 +58,17 @@ func NewKeyring(keys [][]byte, primaryKey []byte) (*Keyring, error) {
|
| 58 | 58 |
return keyring, nil |
| 59 | 59 |
} |
| 60 | 60 |
|
| 61 |
+// ValidateKey will check to see if the key is valid and returns an error if not. |
|
| 62 |
+// |
|
| 63 |
+// key should be either 16, 24, or 32 bytes to select AES-128, |
|
| 64 |
+// AES-192, or AES-256. |
|
| 65 |
+func ValidateKey(key []byte) error {
|
|
| 66 |
+ if l := len(key); l != 16 && l != 24 && l != 32 {
|
|
| 67 |
+ return fmt.Errorf("key size must be 16, 24 or 32 bytes")
|
|
| 68 |
+ } |
|
| 69 |
+ return nil |
|
| 70 |
+} |
|
| 71 |
+ |
|
| 61 | 72 |
// AddKey will install a new key on the ring. Adding a key to the ring will make |
| 62 | 73 |
// it available for use in decryption. If the key already exists on the ring, |
| 63 | 74 |
// this function will just return noop. |
| ... | ... |
@@ -65,8 +76,8 @@ func NewKeyring(keys [][]byte, primaryKey []byte) (*Keyring, error) {
|
| 65 | 65 |
// key should be either 16, 24, or 32 bytes to select AES-128, |
| 66 | 66 |
// AES-192, or AES-256. |
| 67 | 67 |
func (k *Keyring) AddKey(key []byte) error {
|
| 68 |
- if l := len(key); l != 16 && l != 24 && l != 32 {
|
|
| 69 |
- return fmt.Errorf("key size must be 16, 24 or 32 bytes")
|
|
| 68 |
+ if err := ValidateKey(key); err != nil {
|
|
| 69 |
+ return err |
|
| 70 | 70 |
} |
| 71 | 71 |
|
| 72 | 72 |
// No-op if key is already installed |
| ... | ... |
@@ -25,6 +25,7 @@ import ( |
| 25 | 25 |
"time" |
| 26 | 26 |
|
| 27 | 27 |
"github.com/hashicorp/go-multierror" |
| 28 |
+ sockaddr "github.com/hashicorp/go-sockaddr" |
|
| 28 | 29 |
"github.com/miekg/dns" |
| 29 | 30 |
) |
| 30 | 31 |
|
| ... | ... |
@@ -39,13 +40,14 @@ type Memberlist struct {
|
| 39 | 39 |
leave bool |
| 40 | 40 |
leaveBroadcast chan struct{}
|
| 41 | 41 |
|
| 42 |
- udpListener *net.UDPConn |
|
| 43 |
- tcpListener *net.TCPListener |
|
| 44 |
- handoff chan msgHandoff |
|
| 42 |
+ transport Transport |
|
| 43 |
+ handoff chan msgHandoff |
|
| 45 | 44 |
|
| 46 |
- nodeLock sync.RWMutex |
|
| 47 |
- nodes []*nodeState // Known nodes |
|
| 48 |
- nodeMap map[string]*nodeState // Maps Addr.String() -> NodeState |
|
| 45 |
+ nodeLock sync.RWMutex |
|
| 46 |
+ nodes []*nodeState // Known nodes |
|
| 47 |
+ nodeMap map[string]*nodeState // Maps Addr.String() -> NodeState |
|
| 48 |
+ nodeTimers map[string]*suspicion // Maps Addr.String() -> suspicion timer |
|
| 49 |
+ awareness *awareness |
|
| 49 | 50 |
|
| 50 | 51 |
tickerLock sync.Mutex |
| 51 | 52 |
tickers []*time.Ticker |
| ... | ... |
@@ -61,7 +63,7 @@ type Memberlist struct {
|
| 61 | 61 |
} |
| 62 | 62 |
|
| 63 | 63 |
// newMemberlist creates the network listeners. |
| 64 |
-// Does not schedule execution of background maintenence. |
|
| 64 |
+// Does not schedule execution of background maintenance. |
|
| 65 | 65 |
func newMemberlist(conf *Config) (*Memberlist, error) {
|
| 66 | 66 |
if conf.ProtocolVersion < ProtocolVersionMin {
|
| 67 | 67 |
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) {
|
| 88 | 88 |
} |
| 89 | 89 |
} |
| 90 | 90 |
|
| 91 |
- tcpAddr := &net.TCPAddr{IP: net.ParseIP(conf.BindAddr), Port: conf.BindPort}
|
|
| 92 |
- tcpLn, err := net.ListenTCP("tcp", tcpAddr)
|
|
| 93 |
- if err != nil {
|
|
| 94 |
- return nil, fmt.Errorf("Failed to start TCP listener. Err: %s", err)
|
|
| 95 |
- } |
|
| 96 |
- if conf.BindPort == 0 {
|
|
| 97 |
- conf.BindPort = tcpLn.Addr().(*net.TCPAddr).Port |
|
| 98 |
- } |
|
| 99 |
- |
|
| 100 |
- udpAddr := &net.UDPAddr{IP: net.ParseIP(conf.BindAddr), Port: conf.BindPort}
|
|
| 101 |
- udpLn, err := net.ListenUDP("udp", udpAddr)
|
|
| 102 |
- if err != nil {
|
|
| 103 |
- tcpLn.Close() |
|
| 104 |
- return nil, fmt.Errorf("Failed to start UDP listener. Err: %s", err)
|
|
| 105 |
- } |
|
| 106 |
- |
|
| 107 |
- // Set the UDP receive window size |
|
| 108 |
- setUDPRecvBuf(udpLn) |
|
| 109 |
- |
|
| 110 | 91 |
if conf.LogOutput != nil && conf.Logger != nil {
|
| 111 | 92 |
return nil, fmt.Errorf("Cannot specify both LogOutput and Logger. Please choose a single log configuration setting.")
|
| 112 | 93 |
} |
| ... | ... |
@@ -121,14 +104,37 @@ func newMemberlist(conf *Config) (*Memberlist, error) {
|
| 121 | 121 |
logger = log.New(logDest, "", log.LstdFlags) |
| 122 | 122 |
} |
| 123 | 123 |
|
| 124 |
+ // Set up a network transport by default if a custom one wasn't given |
|
| 125 |
+ // by the config. |
|
| 126 |
+ transport := conf.Transport |
|
| 127 |
+ if transport == nil {
|
|
| 128 |
+ nc := &NetTransportConfig{
|
|
| 129 |
+ BindAddrs: []string{conf.BindAddr},
|
|
| 130 |
+ BindPort: conf.BindPort, |
|
| 131 |
+ Logger: logger, |
|
| 132 |
+ } |
|
| 133 |
+ nt, err := NewNetTransport(nc) |
|
| 134 |
+ if err != nil {
|
|
| 135 |
+ return nil, fmt.Errorf("Could not set up network transport: %v", err)
|
|
| 136 |
+ } |
|
| 137 |
+ |
|
| 138 |
+ if conf.BindPort == 0 {
|
|
| 139 |
+ port := nt.GetAutoBindPort() |
|
| 140 |
+ conf.BindPort = port |
|
| 141 |
+ logger.Printf("[DEBUG] Using dynamic bind port %d", port)
|
|
| 142 |
+ } |
|
| 143 |
+ transport = nt |
|
| 144 |
+ } |
|
| 145 |
+ |
|
| 124 | 146 |
m := &Memberlist{
|
| 125 | 147 |
config: conf, |
| 126 | 148 |
shutdownCh: make(chan struct{}),
|
| 127 | 149 |
leaveBroadcast: make(chan struct{}, 1),
|
| 128 |
- udpListener: udpLn, |
|
| 129 |
- tcpListener: tcpLn, |
|
| 130 |
- handoff: make(chan msgHandoff, 1024), |
|
| 150 |
+ transport: transport, |
|
| 151 |
+ handoff: make(chan msgHandoff, conf.HandoffQueueDepth), |
|
| 131 | 152 |
nodeMap: make(map[string]*nodeState), |
| 153 |
+ nodeTimers: make(map[string]*suspicion), |
|
| 154 |
+ awareness: newAwareness(conf.AwarenessMaxMultiplier), |
|
| 132 | 155 |
ackHandlers: make(map[uint32]*ackHandler), |
| 133 | 156 |
broadcasts: &TransmitLimitedQueue{RetransmitMult: conf.RetransmitMult},
|
| 134 | 157 |
logger: logger, |
| ... | ... |
@@ -136,9 +142,9 @@ func newMemberlist(conf *Config) (*Memberlist, error) {
|
| 136 | 136 |
m.broadcasts.NumNodes = func() int {
|
| 137 | 137 |
return m.estNumNodes() |
| 138 | 138 |
} |
| 139 |
- go m.tcpListen() |
|
| 140 |
- go m.udpListen() |
|
| 141 |
- go m.udpHandler() |
|
| 139 |
+ go m.streamListen() |
|
| 140 |
+ go m.packetListen() |
|
| 141 |
+ go m.packetHandler() |
|
| 142 | 142 |
return m, nil |
| 143 | 143 |
} |
| 144 | 144 |
|
| ... | ... |
@@ -182,7 +188,8 @@ func (m *Memberlist) Join(existing []string) (int, error) {
|
| 182 | 182 |
} |
| 183 | 183 |
|
| 184 | 184 |
for _, addr := range addrs {
|
| 185 |
- if err := m.pushPullNode(addr.ip, addr.port, true); err != nil {
|
|
| 185 |
+ hp := joinHostPort(addr.ip.String(), addr.port) |
|
| 186 |
+ if err := m.pushPullNode(hp, true); err != nil {
|
|
| 186 | 187 |
err = fmt.Errorf("Failed to join %s: %v", addr.ip, err)
|
| 187 | 188 |
errs = multierror.Append(errs, err) |
| 188 | 189 |
m.logger.Printf("[DEBUG] memberlist: %v", err)
|
| ... | ... |
@@ -322,78 +329,30 @@ func (m *Memberlist) resolveAddr(hostStr string) ([]ipPort, error) {
|
| 322 | 322 |
// as if we received an alive notification our own network channel for |
| 323 | 323 |
// ourself. |
| 324 | 324 |
func (m *Memberlist) setAlive() error {
|
| 325 |
- var advertiseAddr []byte |
|
| 326 |
- var advertisePort int |
|
| 327 |
- if m.config.AdvertiseAddr != "" {
|
|
| 328 |
- // If AdvertiseAddr is not empty, then advertise |
|
| 329 |
- // the given address and port. |
|
| 330 |
- ip := net.ParseIP(m.config.AdvertiseAddr) |
|
| 331 |
- if ip == nil {
|
|
| 332 |
- return fmt.Errorf("Failed to parse advertise address!")
|
|
| 333 |
- } |
|
| 334 |
- |
|
| 335 |
- // Ensure IPv4 conversion if necessary |
|
| 336 |
- if ip4 := ip.To4(); ip4 != nil {
|
|
| 337 |
- ip = ip4 |
|
| 338 |
- } |
|
| 339 |
- |
|
| 340 |
- advertiseAddr = ip |
|
| 341 |
- advertisePort = m.config.AdvertisePort |
|
| 342 |
- } else {
|
|
| 343 |
- if m.config.BindAddr == "0.0.0.0" {
|
|
| 344 |
- // Otherwise, if we're not bound to a specific IP, |
|
| 345 |
- //let's list the interfaces on this machine and use |
|
| 346 |
- // the first private IP we find. |
|
| 347 |
- addresses, err := net.InterfaceAddrs() |
|
| 348 |
- if err != nil {
|
|
| 349 |
- return fmt.Errorf("Failed to get interface addresses! Err: %v", err)
|
|
| 350 |
- } |
|
| 351 |
- |
|
| 352 |
- // Find private IPv4 address |
|
| 353 |
- for _, rawAddr := range addresses {
|
|
| 354 |
- var ip net.IP |
|
| 355 |
- switch addr := rawAddr.(type) {
|
|
| 356 |
- case *net.IPAddr: |
|
| 357 |
- ip = addr.IP |
|
| 358 |
- case *net.IPNet: |
|
| 359 |
- ip = addr.IP |
|
| 360 |
- default: |
|
| 361 |
- continue |
|
| 362 |
- } |
|
| 363 |
- |
|
| 364 |
- if ip.To4() == nil {
|
|
| 365 |
- continue |
|
| 366 |
- } |
|
| 367 |
- if !IsPrivateIP(ip.String()) {
|
|
| 368 |
- continue |
|
| 369 |
- } |
|
| 370 |
- |
|
| 371 |
- advertiseAddr = ip |
|
| 372 |
- break |
|
| 373 |
- } |
|
| 374 |
- |
|
| 375 |
- // Failed to find private IP, error |
|
| 376 |
- if advertiseAddr == nil {
|
|
| 377 |
- return fmt.Errorf("No private IP address found, and explicit IP not provided")
|
|
| 378 |
- } |
|
| 379 |
- |
|
| 380 |
- } else {
|
|
| 381 |
- // Use the IP that we're bound to. |
|
| 382 |
- addr := m.tcpListener.Addr().(*net.TCPAddr) |
|
| 383 |
- advertiseAddr = addr.IP |
|
| 384 |
- } |
|
| 385 |
- |
|
| 386 |
- // Use the port we are bound to. |
|
| 387 |
- advertisePort = m.tcpListener.Addr().(*net.TCPAddr).Port |
|
| 325 |
+ // Get the final advertise address from the transport, which may need |
|
| 326 |
+ // to see which address we bound to. |
|
| 327 |
+ addr, port, err := m.transport.FinalAdvertiseAddr( |
|
| 328 |
+ m.config.AdvertiseAddr, m.config.AdvertisePort) |
|
| 329 |
+ if err != nil {
|
|
| 330 |
+ return fmt.Errorf("Failed to get final advertise address: %v", err)
|
|
| 388 | 331 |
} |
| 389 | 332 |
|
| 390 | 333 |
// Check if this is a public address without encryption |
| 391 |
- addrStr := net.IP(advertiseAddr).String() |
|
| 392 |
- if !IsPrivateIP(addrStr) && !isLoopbackIP(addrStr) && !m.config.EncryptionEnabled() {
|
|
| 334 |
+ ipAddr, err := sockaddr.NewIPAddr(addr.String()) |
|
| 335 |
+ if err != nil {
|
|
| 336 |
+ return fmt.Errorf("Failed to parse interface addresses: %v", err)
|
|
| 337 |
+ } |
|
| 338 |
+ ifAddrs := []sockaddr.IfAddr{
|
|
| 339 |
+ sockaddr.IfAddr{
|
|
| 340 |
+ SockAddr: ipAddr, |
|
| 341 |
+ }, |
|
| 342 |
+ } |
|
| 343 |
+ _, publicIfs, err := sockaddr.IfByRFC("6890", ifAddrs)
|
|
| 344 |
+ if len(publicIfs) > 0 && !m.config.EncryptionEnabled() {
|
|
| 393 | 345 |
m.logger.Printf("[WARN] memberlist: Binding to public address without encryption!")
|
| 394 | 346 |
} |
| 395 | 347 |
|
| 396 |
- // Get the node meta data |
|
| 348 |
+ // Set any metadata from the delegate. |
|
| 397 | 349 |
var meta []byte |
| 398 | 350 |
if m.config.Delegate != nil {
|
| 399 | 351 |
meta = m.config.Delegate.NodeMeta(MetaMaxSize) |
| ... | ... |
@@ -405,8 +364,8 @@ func (m *Memberlist) setAlive() error {
|
| 405 | 405 |
a := alive{
|
| 406 | 406 |
Incarnation: m.nextIncarnation(), |
| 407 | 407 |
Node: m.config.Name, |
| 408 |
- Addr: advertiseAddr, |
|
| 409 |
- Port: uint16(advertisePort), |
|
| 408 |
+ Addr: addr, |
|
| 409 |
+ Port: uint16(port), |
|
| 410 | 410 |
Meta: meta, |
| 411 | 411 |
Vsn: []uint8{
|
| 412 | 412 |
ProtocolVersionMin, ProtocolVersionMax, m.config.ProtocolVersion, |
| ... | ... |
@@ -415,7 +374,6 @@ func (m *Memberlist) setAlive() error {
|
| 415 | 415 |
}, |
| 416 | 416 |
} |
| 417 | 417 |
m.aliveNode(&a, nil, true) |
| 418 |
- |
|
| 419 | 418 |
return nil |
| 420 | 419 |
} |
| 421 | 420 |
|
| ... | ... |
@@ -478,13 +436,8 @@ func (m *Memberlist) UpdateNode(timeout time.Duration) error {
|
| 478 | 478 |
return nil |
| 479 | 479 |
} |
| 480 | 480 |
|
| 481 |
-// SendTo is used to directly send a message to another node, without |
|
| 482 |
-// the use of the gossip mechanism. This will encode the message as a |
|
| 483 |
-// user-data message, which a delegate will receive through NotifyMsg |
|
| 484 |
-// The actual data is transmitted over UDP, which means this is a |
|
| 485 |
-// best-effort transmission mechanism, and the maximum size of the |
|
| 486 |
-// message is the size of a single UDP datagram, after compression. |
|
| 487 |
-// This method is DEPRECATED in favor or SendToUDP |
|
| 481 |
+// SendTo is deprecated in favor of SendBestEffort, which requires a node to |
|
| 482 |
+// target. |
|
| 488 | 483 |
func (m *Memberlist) SendTo(to net.Addr, msg []byte) error {
|
| 489 | 484 |
// Encode as a user message |
| 490 | 485 |
buf := make([]byte, 1, len(msg)+1) |
| ... | ... |
@@ -492,36 +445,39 @@ func (m *Memberlist) SendTo(to net.Addr, msg []byte) error {
|
| 492 | 492 |
buf = append(buf, msg...) |
| 493 | 493 |
|
| 494 | 494 |
// Send the message |
| 495 |
- return m.rawSendMsgUDP(to, buf) |
|
| 495 |
+ return m.rawSendMsgPacket(to.String(), nil, buf) |
|
| 496 | 496 |
} |
| 497 | 497 |
|
| 498 |
-// SendToUDP is used to directly send a message to another node, without |
|
| 499 |
-// the use of the gossip mechanism. This will encode the message as a |
|
| 500 |
-// user-data message, which a delegate will receive through NotifyMsg |
|
| 501 |
-// The actual data is transmitted over UDP, which means this is a |
|
| 502 |
-// best-effort transmission mechanism, and the maximum size of the |
|
| 503 |
-// message is the size of a single UDP datagram, after compression |
|
| 498 |
+// SendToUDP is deprecated in favor of SendBestEffort. |
|
| 504 | 499 |
func (m *Memberlist) SendToUDP(to *Node, msg []byte) error {
|
| 500 |
+ return m.SendBestEffort(to, msg) |
|
| 501 |
+} |
|
| 502 |
+ |
|
| 503 |
+// SendToTCP is deprecated in favor of SendReliable. |
|
| 504 |
+func (m *Memberlist) SendToTCP(to *Node, msg []byte) error {
|
|
| 505 |
+ return m.SendReliable(to, msg) |
|
| 506 |
+} |
|
| 507 |
+ |
|
| 508 |
+// SendBestEffort uses the unreliable packet-oriented interface of the transport |
|
| 509 |
+// to target a user message at the given node (this does not use the gossip |
|
| 510 |
+// mechanism). The maximum size of the message depends on the configured |
|
| 511 |
+// UDPBufferSize for this memberlist instance. |
|
| 512 |
+func (m *Memberlist) SendBestEffort(to *Node, msg []byte) error {
|
|
| 505 | 513 |
// Encode as a user message |
| 506 | 514 |
buf := make([]byte, 1, len(msg)+1) |
| 507 | 515 |
buf[0] = byte(userMsg) |
| 508 | 516 |
buf = append(buf, msg...) |
| 509 | 517 |
|
| 510 | 518 |
// Send the message |
| 511 |
- destAddr := &net.UDPAddr{IP: to.Addr, Port: int(to.Port)}
|
|
| 512 |
- return m.rawSendMsgUDP(destAddr, buf) |
|
| 519 |
+ return m.rawSendMsgPacket(to.Address(), to, buf) |
|
| 513 | 520 |
} |
| 514 | 521 |
|
| 515 |
-// SendToTCP is used to directly send a message to another node, without |
|
| 516 |
-// the use of the gossip mechanism. This will encode the message as a |
|
| 517 |
-// user-data message, which a delegate will receive through NotifyMsg |
|
| 518 |
-// The actual data is transmitted over TCP, which means delivery |
|
| 519 |
-// is guaranteed if no error is returned. There is no limit |
|
| 520 |
-// to the size of the message |
|
| 521 |
-func (m *Memberlist) SendToTCP(to *Node, msg []byte) error {
|
|
| 522 |
- // Send the message |
|
| 523 |
- destAddr := &net.TCPAddr{IP: to.Addr, Port: int(to.Port)}
|
|
| 524 |
- return m.sendTCPUserMsg(destAddr, msg) |
|
| 522 |
+// SendReliable uses the reliable stream-oriented interface of the transport to |
|
| 523 |
+// target a user message at the given node (this does not use the gossip |
|
| 524 |
+// mechanism). Delivery is guaranteed if no error is returned, and there is no |
|
| 525 |
+// limit on the size of the message. |
|
| 526 |
+func (m *Memberlist) SendReliable(to *Node, msg []byte) error {
|
|
| 527 |
+ return m.sendUserMsg(to.Address(), msg) |
|
| 525 | 528 |
} |
| 526 | 529 |
|
| 527 | 530 |
// Members returns a list of all known live nodes. The node structures |
| ... | ... |
@@ -625,6 +581,13 @@ func (m *Memberlist) anyAlive() bool {
|
| 625 | 625 |
return false |
| 626 | 626 |
} |
| 627 | 627 |
|
| 628 |
+// GetHealthScore gives this instance's idea of how well it is meeting the soft |
|
| 629 |
+// real-time requirements of the protocol. Lower numbers are better, and zero |
|
| 630 |
+// means "totally healthy". |
|
| 631 |
+func (m *Memberlist) GetHealthScore() int {
|
|
| 632 |
+ return m.awareness.GetHealthScore() |
|
| 633 |
+} |
|
| 634 |
+ |
|
| 628 | 635 |
// ProtocolVersion returns the protocol version currently in use by |
| 629 | 636 |
// this memberlist. |
| 630 | 637 |
func (m *Memberlist) ProtocolVersion() uint8 {
|
| ... | ... |
@@ -649,10 +612,14 @@ func (m *Memberlist) Shutdown() error {
|
| 649 | 649 |
return nil |
| 650 | 650 |
} |
| 651 | 651 |
|
| 652 |
+ // Shut down the transport first, which should block until it's |
|
| 653 |
+ // completely torn down. If we kill the memberlist-side handlers |
|
| 654 |
+ // those I/O handlers might get stuck. |
|
| 655 |
+ m.transport.Shutdown() |
|
| 656 |
+ |
|
| 657 |
+ // Now tear down everything else. |
|
| 652 | 658 |
m.shutdown = true |
| 653 | 659 |
close(m.shutdownCh) |
| 654 | 660 |
m.deschedule() |
| 655 |
- m.udpListener.Close() |
|
| 656 |
- m.tcpListener.Close() |
|
| 657 | 661 |
return nil |
| 658 | 662 |
} |
| 659 | 663 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,121 @@ |
| 0 |
+package memberlist |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "fmt" |
|
| 4 |
+ "net" |
|
| 5 |
+ "strconv" |
|
| 6 |
+ "time" |
|
| 7 |
+) |
|
| 8 |
+ |
|
| 9 |
+// MockNetwork is used as a factory that produces MockTransport instances which |
|
| 10 |
+// are uniquely addressed and wired up to talk to each other. |
|
| 11 |
+type MockNetwork struct {
|
|
| 12 |
+ transports map[string]*MockTransport |
|
| 13 |
+ port int |
|
| 14 |
+} |
|
| 15 |
+ |
|
| 16 |
+// NewTransport returns a new MockTransport with a unique address, wired up to |
|
| 17 |
+// talk to the other transports in the MockNetwork. |
|
| 18 |
+func (n *MockNetwork) NewTransport() *MockTransport {
|
|
| 19 |
+ n.port += 1 |
|
| 20 |
+ addr := fmt.Sprintf("127.0.0.1:%d", n.port)
|
|
| 21 |
+ transport := &MockTransport{
|
|
| 22 |
+ net: n, |
|
| 23 |
+ addr: &MockAddress{addr},
|
|
| 24 |
+ packetCh: make(chan *Packet), |
|
| 25 |
+ streamCh: make(chan net.Conn), |
|
| 26 |
+ } |
|
| 27 |
+ |
|
| 28 |
+ if n.transports == nil {
|
|
| 29 |
+ n.transports = make(map[string]*MockTransport) |
|
| 30 |
+ } |
|
| 31 |
+ n.transports[addr] = transport |
|
| 32 |
+ return transport |
|
| 33 |
+} |
|
| 34 |
+ |
|
| 35 |
+// MockAddress is a wrapper which adds the net.Addr interface to our mock |
|
| 36 |
+// address scheme. |
|
| 37 |
+type MockAddress struct {
|
|
| 38 |
+ addr string |
|
| 39 |
+} |
|
| 40 |
+ |
|
| 41 |
+// See net.Addr. |
|
| 42 |
+func (a *MockAddress) Network() string {
|
|
| 43 |
+ return "mock" |
|
| 44 |
+} |
|
| 45 |
+ |
|
| 46 |
+// See net.Addr. |
|
| 47 |
+func (a *MockAddress) String() string {
|
|
| 48 |
+ return a.addr |
|
| 49 |
+} |
|
| 50 |
+ |
|
| 51 |
+// MockTransport directly plumbs messages to other transports its MockNetwork. |
|
| 52 |
+type MockTransport struct {
|
|
| 53 |
+ net *MockNetwork |
|
| 54 |
+ addr *MockAddress |
|
| 55 |
+ packetCh chan *Packet |
|
| 56 |
+ streamCh chan net.Conn |
|
| 57 |
+} |
|
| 58 |
+ |
|
| 59 |
+// See Transport. |
|
| 60 |
+func (t *MockTransport) FinalAdvertiseAddr(string, int) (net.IP, int, error) {
|
|
| 61 |
+ host, portStr, err := net.SplitHostPort(t.addr.String()) |
|
| 62 |
+ if err != nil {
|
|
| 63 |
+ return nil, 0, err |
|
| 64 |
+ } |
|
| 65 |
+ |
|
| 66 |
+ ip := net.ParseIP(host) |
|
| 67 |
+ if ip == nil {
|
|
| 68 |
+ return nil, 0, fmt.Errorf("Failed to parse IP %q", host)
|
|
| 69 |
+ } |
|
| 70 |
+ |
|
| 71 |
+ port, err := strconv.ParseInt(portStr, 10, 16) |
|
| 72 |
+ if err != nil {
|
|
| 73 |
+ return nil, 0, err |
|
| 74 |
+ } |
|
| 75 |
+ |
|
| 76 |
+ return ip, int(port), nil |
|
| 77 |
+} |
|
| 78 |
+ |
|
| 79 |
+// See Transport. |
|
| 80 |
+func (t *MockTransport) WriteTo(b []byte, addr string) (time.Time, error) {
|
|
| 81 |
+ dest, ok := t.net.transports[addr] |
|
| 82 |
+ if !ok {
|
|
| 83 |
+ return time.Time{}, fmt.Errorf("No route to %q", addr)
|
|
| 84 |
+ } |
|
| 85 |
+ |
|
| 86 |
+ now := time.Now() |
|
| 87 |
+ dest.packetCh <- &Packet{
|
|
| 88 |
+ Buf: b, |
|
| 89 |
+ From: t.addr, |
|
| 90 |
+ Timestamp: now, |
|
| 91 |
+ } |
|
| 92 |
+ return now, nil |
|
| 93 |
+} |
|
| 94 |
+ |
|
| 95 |
+// See Transport. |
|
| 96 |
+func (t *MockTransport) PacketCh() <-chan *Packet {
|
|
| 97 |
+ return t.packetCh |
|
| 98 |
+} |
|
| 99 |
+ |
|
| 100 |
+// See Transport. |
|
| 101 |
+func (t *MockTransport) DialTimeout(addr string, timeout time.Duration) (net.Conn, error) {
|
|
| 102 |
+ dest, ok := t.net.transports[addr] |
|
| 103 |
+ if !ok {
|
|
| 104 |
+ return nil, fmt.Errorf("No route to %q", addr)
|
|
| 105 |
+ } |
|
| 106 |
+ |
|
| 107 |
+ p1, p2 := net.Pipe() |
|
| 108 |
+ dest.streamCh <- p1 |
|
| 109 |
+ return p2, nil |
|
| 110 |
+} |
|
| 111 |
+ |
|
| 112 |
+// See Transport. |
|
| 113 |
+func (t *MockTransport) StreamCh() <-chan net.Conn {
|
|
| 114 |
+ return t.streamCh |
|
| 115 |
+} |
|
| 116 |
+ |
|
| 117 |
+// See Transport. |
|
| 118 |
+func (t *MockTransport) Shutdown() error {
|
|
| 119 |
+ return nil |
|
| 120 |
+} |
| ... | ... |
@@ -5,6 +5,7 @@ import ( |
| 5 | 5 |
"bytes" |
| 6 | 6 |
"encoding/binary" |
| 7 | 7 |
"fmt" |
| 8 |
+ "hash/crc32" |
|
| 8 | 9 |
"io" |
| 9 | 10 |
"net" |
| 10 | 11 |
"time" |
| ... | ... |
@@ -24,9 +25,15 @@ const ( |
| 24 | 24 |
// A memberlist speaking version 2 of the protocol will attempt |
| 25 | 25 |
// to TCP ping another memberlist who understands version 3 or |
| 26 | 26 |
// greater. |
| 27 |
+ // |
|
| 28 |
+ // Version 4 added support for nacks as part of indirect probes. |
|
| 29 |
+ // A memberlist speaking version 2 of the protocol will expect |
|
| 30 |
+ // nacks from another memberlist who understands version 4 or |
|
| 31 |
+ // greater, and likewise nacks will be sent to memberlists who |
|
| 32 |
+ // understand version 4 or greater. |
|
| 27 | 33 |
ProtocolVersion2Compatible = 2 |
| 28 | 34 |
|
| 29 |
- ProtocolVersionMax = 3 |
|
| 35 |
+ ProtocolVersionMax = 5 |
|
| 30 | 36 |
) |
| 31 | 37 |
|
| 32 | 38 |
// messageType is an integer ID of a type of message that can be received |
| ... | ... |
@@ -46,6 +53,8 @@ const ( |
| 46 | 46 |
userMsg // User mesg, not handled by us |
| 47 | 47 |
compressMsg |
| 48 | 48 |
encryptMsg |
| 49 |
+ nackRespMsg |
|
| 50 |
+ hasCrcMsg |
|
| 49 | 51 |
) |
| 50 | 52 |
|
| 51 | 53 |
// compressionType is used to specify the compression algorithm |
| ... | ... |
@@ -59,9 +68,6 @@ const ( |
| 59 | 59 |
MetaMaxSize = 512 // Maximum size for node meta data |
| 60 | 60 |
compoundHeaderOverhead = 2 // Assumed header overhead |
| 61 | 61 |
compoundOverhead = 2 // Assumed overhead per entry in compoundHeader |
| 62 |
- udpBufSize = 65536 |
|
| 63 |
- udpRecvBuf = 2 * 1024 * 1024 |
|
| 64 |
- udpSendBuf = 1400 |
|
| 65 | 62 |
userMsgOverhead = 1 |
| 66 | 63 |
blockingWarning = 10 * time.Millisecond // Warn if a UDP packet takes this long to process |
| 67 | 64 |
maxPushStateBytes = 10 * 1024 * 1024 |
| ... | ... |
@@ -83,6 +89,7 @@ type indirectPingReq struct {
|
| 83 | 83 |
Target []byte |
| 84 | 84 |
Port uint16 |
| 85 | 85 |
Node string |
| 86 |
+ Nack bool // true if we'd like a nack back |
|
| 86 | 87 |
} |
| 87 | 88 |
|
| 88 | 89 |
// ack response is sent for a ping |
| ... | ... |
@@ -91,6 +98,13 @@ type ackResp struct {
|
| 91 | 91 |
Payload []byte |
| 92 | 92 |
} |
| 93 | 93 |
|
| 94 |
+// nack response is sent for an indirect ping when the pinger doesn't hear from |
|
| 95 |
+// the ping-ee within the configured timeout. This lets the original node know |
|
| 96 |
+// that the indirect ping attempt happened but didn't succeed. |
|
| 97 |
+type nackResp struct {
|
|
| 98 |
+ SeqNo uint32 |
|
| 99 |
+} |
|
| 100 |
+ |
|
| 94 | 101 |
// suspect is broadcast when we suspect a node is dead |
| 95 | 102 |
type suspect struct {
|
| 96 | 103 |
Incarnation uint32 |
| ... | ... |
@@ -121,7 +135,7 @@ type dead struct {
|
| 121 | 121 |
} |
| 122 | 122 |
|
| 123 | 123 |
// pushPullHeader is used to inform the |
| 124 |
-// otherside how many states we are transfering |
|
| 124 |
+// otherside how many states we are transferring |
|
| 125 | 125 |
type pushPullHeader struct {
|
| 126 | 126 |
Nodes int |
| 127 | 127 |
UserStateLen int // Encodes the byte lengh of user state |
| ... | ... |
@@ -134,7 +148,7 @@ type userMsgHeader struct {
|
| 134 | 134 |
} |
| 135 | 135 |
|
| 136 | 136 |
// pushNodeState is used for pushPullReq when we are |
| 137 |
-// transfering out node states |
|
| 137 |
+// transferring out node states |
|
| 138 | 138 |
type pushNodeState struct {
|
| 139 | 139 |
Name string |
| 140 | 140 |
Addr []byte |
| ... | ... |
@@ -169,45 +183,33 @@ func (m *Memberlist) encryptionVersion() encryptionVersion {
|
| 169 | 169 |
} |
| 170 | 170 |
} |
| 171 | 171 |
|
| 172 |
-// setUDPRecvBuf is used to resize the UDP receive window. The function |
|
| 173 |
-// attempts to set the read buffer to `udpRecvBuf` but backs off until |
|
| 174 |
-// the read buffer can be set. |
|
| 175 |
-func setUDPRecvBuf(c *net.UDPConn) {
|
|
| 176 |
- size := udpRecvBuf |
|
| 172 |
+// streamListen is a long running goroutine that pulls incoming streams from the |
|
| 173 |
+// transport and hands them off for processing. |
|
| 174 |
+func (m *Memberlist) streamListen() {
|
|
| 177 | 175 |
for {
|
| 178 |
- if err := c.SetReadBuffer(size); err == nil {
|
|
| 179 |
- break |
|
| 180 |
- } |
|
| 181 |
- size = size / 2 |
|
| 182 |
- } |
|
| 183 |
-} |
|
| 176 |
+ select {
|
|
| 177 |
+ case conn := <-m.transport.StreamCh(): |
|
| 178 |
+ go m.handleConn(conn) |
|
| 184 | 179 |
|
| 185 |
-// tcpListen listens for and handles incoming connections |
|
| 186 |
-func (m *Memberlist) tcpListen() {
|
|
| 187 |
- for {
|
|
| 188 |
- conn, err := m.tcpListener.AcceptTCP() |
|
| 189 |
- if err != nil {
|
|
| 190 |
- if m.shutdown {
|
|
| 191 |
- break |
|
| 192 |
- } |
|
| 193 |
- m.logger.Printf("[ERR] memberlist: Error accepting TCP connection: %s", err)
|
|
| 194 |
- continue |
|
| 180 |
+ case <-m.shutdownCh: |
|
| 181 |
+ return |
|
| 195 | 182 |
} |
| 196 |
- go m.handleConn(conn) |
|
| 197 | 183 |
} |
| 198 | 184 |
} |
| 199 | 185 |
|
| 200 |
-// handleConn handles a single incoming TCP connection |
|
| 201 |
-func (m *Memberlist) handleConn(conn *net.TCPConn) {
|
|
| 202 |
- m.logger.Printf("[DEBUG] memberlist: TCP connection %s", LogConn(conn))
|
|
| 186 |
+// handleConn handles a single incoming stream connection from the transport. |
|
| 187 |
+func (m *Memberlist) handleConn(conn net.Conn) {
|
|
| 188 |
+ m.logger.Printf("[DEBUG] memberlist: Stream connection %s", LogConn(conn))
|
|
| 203 | 189 |
|
| 204 | 190 |
defer conn.Close() |
| 205 | 191 |
metrics.IncrCounter([]string{"memberlist", "tcp", "accept"}, 1)
|
| 206 | 192 |
|
| 207 | 193 |
conn.SetDeadline(time.Now().Add(m.config.TCPTimeout)) |
| 208 |
- msgType, bufConn, dec, err := m.readTCP(conn) |
|
| 194 |
+ msgType, bufConn, dec, err := m.readStream(conn) |
|
| 209 | 195 |
if err != nil {
|
| 210 |
- m.logger.Printf("[ERR] memberlist: failed to receive: %s %s", err, LogConn(conn))
|
|
| 196 |
+ if err != io.EOF {
|
|
| 197 |
+ m.logger.Printf("[ERR] memberlist: failed to receive: %s %s", err, LogConn(conn))
|
|
| 198 |
+ } |
|
| 211 | 199 |
return |
| 212 | 200 |
} |
| 213 | 201 |
|
| ... | ... |
@@ -235,7 +237,7 @@ func (m *Memberlist) handleConn(conn *net.TCPConn) {
|
| 235 | 235 |
case pingMsg: |
| 236 | 236 |
var p ping |
| 237 | 237 |
if err := dec.Decode(&p); err != nil {
|
| 238 |
- m.logger.Printf("[ERR] memberlist: Failed to decode TCP ping: %s %s", err, LogConn(conn))
|
|
| 238 |
+ m.logger.Printf("[ERR] memberlist: Failed to decode ping: %s %s", err, LogConn(conn))
|
|
| 239 | 239 |
return |
| 240 | 240 |
} |
| 241 | 241 |
|
| ... | ... |
@@ -247,13 +249,13 @@ func (m *Memberlist) handleConn(conn *net.TCPConn) {
|
| 247 | 247 |
ack := ackResp{p.SeqNo, nil}
|
| 248 | 248 |
out, err := encode(ackRespMsg, &ack) |
| 249 | 249 |
if err != nil {
|
| 250 |
- m.logger.Printf("[ERR] memberlist: Failed to encode TCP ack: %s", err)
|
|
| 250 |
+ m.logger.Printf("[ERR] memberlist: Failed to encode ack: %s", err)
|
|
| 251 | 251 |
return |
| 252 | 252 |
} |
| 253 | 253 |
|
| 254 |
- err = m.rawSendMsgTCP(conn, out.Bytes()) |
|
| 254 |
+ err = m.rawSendMsgStream(conn, out.Bytes()) |
|
| 255 | 255 |
if err != nil {
|
| 256 |
- m.logger.Printf("[ERR] memberlist: Failed to send TCP ack: %s %s", err, LogConn(conn))
|
|
| 256 |
+ m.logger.Printf("[ERR] memberlist: Failed to send ack: %s %s", err, LogConn(conn))
|
|
| 257 | 257 |
return |
| 258 | 258 |
} |
| 259 | 259 |
default: |
| ... | ... |
@@ -261,49 +263,17 @@ func (m *Memberlist) handleConn(conn *net.TCPConn) {
|
| 261 | 261 |
} |
| 262 | 262 |
} |
| 263 | 263 |
|
| 264 |
-// udpListen listens for and handles incoming UDP packets |
|
| 265 |
-func (m *Memberlist) udpListen() {
|
|
| 266 |
- var n int |
|
| 267 |
- var addr net.Addr |
|
| 268 |
- var err error |
|
| 269 |
- var lastPacket time.Time |
|
| 264 |
+// packetListen is a long running goroutine that pulls packets out of the |
|
| 265 |
+// transport and hands them off for processing. |
|
| 266 |
+func (m *Memberlist) packetListen() {
|
|
| 270 | 267 |
for {
|
| 271 |
- // Do a check for potentially blocking operations |
|
| 272 |
- if !lastPacket.IsZero() && time.Now().Sub(lastPacket) > blockingWarning {
|
|
| 273 |
- diff := time.Now().Sub(lastPacket) |
|
| 274 |
- m.logger.Printf( |
|
| 275 |
- "[DEBUG] memberlist: Potential blocking operation. Last command took %v", |
|
| 276 |
- diff) |
|
| 277 |
- } |
|
| 278 |
- |
|
| 279 |
- // Create a new buffer |
|
| 280 |
- // TODO: Use Sync.Pool eventually |
|
| 281 |
- buf := make([]byte, udpBufSize) |
|
| 282 |
- |
|
| 283 |
- // Read a packet |
|
| 284 |
- n, addr, err = m.udpListener.ReadFrom(buf) |
|
| 285 |
- if err != nil {
|
|
| 286 |
- if m.shutdown {
|
|
| 287 |
- break |
|
| 288 |
- } |
|
| 289 |
- m.logger.Printf("[ERR] memberlist: Error reading UDP packet: %s", err)
|
|
| 290 |
- continue |
|
| 291 |
- } |
|
| 292 |
- |
|
| 293 |
- // Capture the reception time of the packet as close to the |
|
| 294 |
- // system calls as possible. |
|
| 295 |
- lastPacket = time.Now() |
|
| 268 |
+ select {
|
|
| 269 |
+ case packet := <-m.transport.PacketCh(): |
|
| 270 |
+ m.ingestPacket(packet.Buf, packet.From, packet.Timestamp) |
|
| 296 | 271 |
|
| 297 |
- // Check the length |
|
| 298 |
- if n < 1 {
|
|
| 299 |
- m.logger.Printf("[ERR] memberlist: UDP packet too short (%d bytes) %s",
|
|
| 300 |
- len(buf), LogAddress(addr)) |
|
| 301 |
- continue |
|
| 272 |
+ case <-m.shutdownCh: |
|
| 273 |
+ return |
|
| 302 | 274 |
} |
| 303 |
- |
|
| 304 |
- // Ingest this packet |
|
| 305 |
- metrics.IncrCounter([]string{"memberlist", "udp", "received"}, float32(n))
|
|
| 306 |
- m.ingestPacket(buf[:n], addr, lastPacket) |
|
| 307 | 275 |
} |
| 308 | 276 |
} |
| 309 | 277 |
|
| ... | ... |
@@ -321,8 +291,18 @@ func (m *Memberlist) ingestPacket(buf []byte, from net.Addr, timestamp time.Time |
| 321 | 321 |
buf = plain |
| 322 | 322 |
} |
| 323 | 323 |
|
| 324 |
- // Handle the command |
|
| 325 |
- m.handleCommand(buf, from, timestamp) |
|
| 324 |
+ // See if there's a checksum included to verify the contents of the message |
|
| 325 |
+ if len(buf) >= 5 && messageType(buf[0]) == hasCrcMsg {
|
|
| 326 |
+ crc := crc32.ChecksumIEEE(buf[5:]) |
|
| 327 |
+ expected := binary.BigEndian.Uint32(buf[1:5]) |
|
| 328 |
+ if crc != expected {
|
|
| 329 |
+ m.logger.Printf("[WARN] memberlist: Got invalid checksum for UDP packet: %x, %x", crc, expected)
|
|
| 330 |
+ return |
|
| 331 |
+ } |
|
| 332 |
+ m.handleCommand(buf[5:], from, timestamp) |
|
| 333 |
+ } else {
|
|
| 334 |
+ m.handleCommand(buf, from, timestamp) |
|
| 335 |
+ } |
|
| 326 | 336 |
} |
| 327 | 337 |
|
| 328 | 338 |
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 |
| 343 | 343 |
m.handleIndirectPing(buf, from) |
| 344 | 344 |
case ackRespMsg: |
| 345 | 345 |
m.handleAck(buf, from, timestamp) |
| 346 |
+ case nackRespMsg: |
|
| 347 |
+ m.handleNack(buf, from) |
|
| 346 | 348 |
|
| 347 | 349 |
case suspectMsg: |
| 348 | 350 |
fallthrough |
| ... | ... |
@@ -354,18 +336,18 @@ func (m *Memberlist) handleCommand(buf []byte, from net.Addr, timestamp time.Tim |
| 354 | 354 |
select {
|
| 355 | 355 |
case m.handoff <- msgHandoff{msgType, buf, from}:
|
| 356 | 356 |
default: |
| 357 |
- m.logger.Printf("[WARN] memberlist: UDP handler queue full, dropping message (%d) %s", msgType, LogAddress(from))
|
|
| 357 |
+ m.logger.Printf("[WARN] memberlist: handler queue full, dropping message (%d) %s", msgType, LogAddress(from))
|
|
| 358 | 358 |
} |
| 359 | 359 |
|
| 360 | 360 |
default: |
| 361 |
- m.logger.Printf("[ERR] memberlist: UDP msg type (%d) not supported %s", msgType, LogAddress(from))
|
|
| 361 |
+ m.logger.Printf("[ERR] memberlist: msg type (%d) not supported %s", msgType, LogAddress(from))
|
|
| 362 | 362 |
} |
| 363 | 363 |
} |
| 364 | 364 |
|
| 365 |
-// udpHandler processes messages received over UDP, but is decoupled |
|
| 366 |
-// from the listener to avoid blocking the listener which may cause |
|
| 367 |
-// ping/ack messages to be delayed. |
|
| 368 |
-func (m *Memberlist) udpHandler() {
|
|
| 365 |
+// packetHandler is a long running goroutine that processes messages received |
|
| 366 |
+// over the packet interface, but is decoupled from the listener to avoid |
|
| 367 |
+// blocking the listener which may cause ping/ack messages to be delayed. |
|
| 368 |
+func (m *Memberlist) packetHandler() {
|
|
| 369 | 369 |
for {
|
| 370 | 370 |
select {
|
| 371 | 371 |
case msg := <-m.handoff: |
| ... | ... |
@@ -383,7 +365,7 @@ func (m *Memberlist) udpHandler() {
|
| 383 | 383 |
case userMsg: |
| 384 | 384 |
m.handleUser(buf, from) |
| 385 | 385 |
default: |
| 386 |
- m.logger.Printf("[ERR] memberlist: UDP msg type (%d) not supported %s (handler)", msgType, LogAddress(from))
|
|
| 386 |
+ m.logger.Printf("[ERR] memberlist: Message type (%d) not supported %s (packet handler)", msgType, LogAddress(from))
|
|
| 387 | 387 |
} |
| 388 | 388 |
|
| 389 | 389 |
case <-m.shutdownCh: |
| ... | ... |
@@ -427,7 +409,7 @@ func (m *Memberlist) handlePing(buf []byte, from net.Addr) {
|
| 427 | 427 |
if m.config.Ping != nil {
|
| 428 | 428 |
ack.Payload = m.config.Ping.AckPayload() |
| 429 | 429 |
} |
| 430 |
- if err := m.encodeAndSendMsg(from, ackRespMsg, &ack); err != nil {
|
|
| 430 |
+ if err := m.encodeAndSendMsg(from.String(), ackRespMsg, &ack); err != nil {
|
|
| 431 | 431 |
m.logger.Printf("[ERR] memberlist: Failed to send ack: %s %s", err, LogAddress(from))
|
| 432 | 432 |
} |
| 433 | 433 |
} |
| ... | ... |
@@ -440,29 +422,49 @@ func (m *Memberlist) handleIndirectPing(buf []byte, from net.Addr) {
|
| 440 | 440 |
} |
| 441 | 441 |
|
| 442 | 442 |
// For proto versions < 2, there is no port provided. Mask old |
| 443 |
- // behavior by using the configured port |
|
| 443 |
+ // behavior by using the configured port. |
|
| 444 | 444 |
if m.ProtocolVersion() < 2 || ind.Port == 0 {
|
| 445 | 445 |
ind.Port = uint16(m.config.BindPort) |
| 446 | 446 |
} |
| 447 | 447 |
|
| 448 |
- // Send a ping to the correct host |
|
| 448 |
+ // Send a ping to the correct host. |
|
| 449 | 449 |
localSeqNo := m.nextSeqNo() |
| 450 | 450 |
ping := ping{SeqNo: localSeqNo, Node: ind.Node}
|
| 451 |
- destAddr := &net.UDPAddr{IP: ind.Target, Port: int(ind.Port)}
|
|
| 452 | 451 |
|
| 453 | 452 |
// Setup a response handler to relay the ack |
| 453 |
+ cancelCh := make(chan struct{})
|
|
| 454 | 454 |
respHandler := func(payload []byte, timestamp time.Time) {
|
| 455 |
+ // Try to prevent the nack if we've caught it in time. |
|
| 456 |
+ close(cancelCh) |
|
| 457 |
+ |
|
| 458 |
+ // Forward the ack back to the requestor. |
|
| 455 | 459 |
ack := ackResp{ind.SeqNo, nil}
|
| 456 |
- if err := m.encodeAndSendMsg(from, ackRespMsg, &ack); err != nil {
|
|
| 460 |
+ if err := m.encodeAndSendMsg(from.String(), ackRespMsg, &ack); err != nil {
|
|
| 457 | 461 |
m.logger.Printf("[ERR] memberlist: Failed to forward ack: %s %s", err, LogAddress(from))
|
| 458 | 462 |
} |
| 459 | 463 |
} |
| 460 | 464 |
m.setAckHandler(localSeqNo, respHandler, m.config.ProbeTimeout) |
| 461 | 465 |
|
| 462 |
- // Send the ping |
|
| 463 |
- if err := m.encodeAndSendMsg(destAddr, pingMsg, &ping); err != nil {
|
|
| 466 |
+ // Send the ping. |
|
| 467 |
+ addr := joinHostPort(net.IP(ind.Target).String(), ind.Port) |
|
| 468 |
+ if err := m.encodeAndSendMsg(addr, pingMsg, &ping); err != nil {
|
|
| 464 | 469 |
m.logger.Printf("[ERR] memberlist: Failed to send ping: %s %s", err, LogAddress(from))
|
| 465 | 470 |
} |
| 471 |
+ |
|
| 472 |
+ // Setup a timer to fire off a nack if no ack is seen in time. |
|
| 473 |
+ if ind.Nack {
|
|
| 474 |
+ go func() {
|
|
| 475 |
+ select {
|
|
| 476 |
+ case <-cancelCh: |
|
| 477 |
+ return |
|
| 478 |
+ case <-time.After(m.config.ProbeTimeout): |
|
| 479 |
+ nack := nackResp{ind.SeqNo}
|
|
| 480 |
+ if err := m.encodeAndSendMsg(from.String(), nackRespMsg, &nack); err != nil {
|
|
| 481 |
+ m.logger.Printf("[ERR] memberlist: Failed to send nack: %s %s", err, LogAddress(from))
|
|
| 482 |
+ } |
|
| 483 |
+ } |
|
| 484 |
+ }() |
|
| 485 |
+ } |
|
| 466 | 486 |
} |
| 467 | 487 |
|
| 468 | 488 |
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) {
|
| 474 | 474 |
m.invokeAckHandler(ack, timestamp) |
| 475 | 475 |
} |
| 476 | 476 |
|
| 477 |
+func (m *Memberlist) handleNack(buf []byte, from net.Addr) {
|
|
| 478 |
+ var nack nackResp |
|
| 479 |
+ if err := decode(buf, &nack); err != nil {
|
|
| 480 |
+ m.logger.Printf("[ERR] memberlist: Failed to decode nack response: %s %s", err, LogAddress(from))
|
|
| 481 |
+ return |
|
| 482 |
+ } |
|
| 483 |
+ m.invokeNackHandler(nack) |
|
| 484 |
+} |
|
| 485 |
+ |
|
| 477 | 486 |
func (m *Memberlist) handleSuspect(buf []byte, from net.Addr) {
|
| 478 | 487 |
var sus suspect |
| 479 | 488 |
if err := decode(buf, &sus); err != nil {
|
| ... | ... |
@@ -530,22 +541,22 @@ func (m *Memberlist) handleCompressed(buf []byte, from net.Addr, timestamp time. |
| 530 | 530 |
} |
| 531 | 531 |
|
| 532 | 532 |
// encodeAndSendMsg is used to combine the encoding and sending steps |
| 533 |
-func (m *Memberlist) encodeAndSendMsg(to net.Addr, msgType messageType, msg interface{}) error {
|
|
| 533 |
+func (m *Memberlist) encodeAndSendMsg(addr string, msgType messageType, msg interface{}) error {
|
|
| 534 | 534 |
out, err := encode(msgType, msg) |
| 535 | 535 |
if err != nil {
|
| 536 | 536 |
return err |
| 537 | 537 |
} |
| 538 |
- if err := m.sendMsg(to, out.Bytes()); err != nil {
|
|
| 538 |
+ if err := m.sendMsg(addr, out.Bytes()); err != nil {
|
|
| 539 | 539 |
return err |
| 540 | 540 |
} |
| 541 | 541 |
return nil |
| 542 | 542 |
} |
| 543 | 543 |
|
| 544 |
-// sendMsg is used to send a UDP message to another host. It will opportunistically |
|
| 545 |
-// create a compoundMsg and piggy back other broadcasts |
|
| 546 |
-func (m *Memberlist) sendMsg(to net.Addr, msg []byte) error {
|
|
| 544 |
+// sendMsg is used to send a message via packet to another host. It will |
|
| 545 |
+// opportunistically create a compoundMsg and piggy back other broadcasts. |
|
| 546 |
+func (m *Memberlist) sendMsg(addr string, msg []byte) error {
|
|
| 547 | 547 |
// Check if we can piggy back any messages |
| 548 |
- bytesAvail := udpSendBuf - len(msg) - compoundHeaderOverhead |
|
| 548 |
+ bytesAvail := m.config.UDPBufferSize - len(msg) - compoundHeaderOverhead |
|
| 549 | 549 |
if m.config.EncryptionEnabled() {
|
| 550 | 550 |
bytesAvail -= encryptOverhead(m.encryptionVersion()) |
| 551 | 551 |
} |
| ... | ... |
@@ -553,7 +564,7 @@ func (m *Memberlist) sendMsg(to net.Addr, msg []byte) error {
|
| 553 | 553 |
|
| 554 | 554 |
// Fast path if nothing to piggypack |
| 555 | 555 |
if len(extra) == 0 {
|
| 556 |
- return m.rawSendMsgUDP(to, msg) |
|
| 556 |
+ return m.rawSendMsgPacket(addr, nil, msg) |
|
| 557 | 557 |
} |
| 558 | 558 |
|
| 559 | 559 |
// Join all the messages |
| ... | ... |
@@ -565,11 +576,12 @@ func (m *Memberlist) sendMsg(to net.Addr, msg []byte) error {
|
| 565 | 565 |
compound := makeCompoundMessage(msgs) |
| 566 | 566 |
|
| 567 | 567 |
// Send the message |
| 568 |
- return m.rawSendMsgUDP(to, compound.Bytes()) |
|
| 568 |
+ return m.rawSendMsgPacket(addr, nil, compound.Bytes()) |
|
| 569 | 569 |
} |
| 570 | 570 |
|
| 571 |
-// rawSendMsgUDP is used to send a UDP message to another host without modification |
|
| 572 |
-func (m *Memberlist) rawSendMsgUDP(to net.Addr, msg []byte) error {
|
|
| 571 |
+// rawSendMsgPacket is used to send message via packet to another host without |
|
| 572 |
+// modification, other than compression or encryption if enabled. |
|
| 573 |
+func (m *Memberlist) rawSendMsgPacket(addr string, node *Node, msg []byte) error {
|
|
| 573 | 574 |
// Check if we have compression enabled |
| 574 | 575 |
if m.config.EnableCompression {
|
| 575 | 576 |
buf, err := compressPayload(msg) |
| ... | ... |
@@ -583,6 +595,31 @@ func (m *Memberlist) rawSendMsgUDP(to net.Addr, msg []byte) error {
|
| 583 | 583 |
} |
| 584 | 584 |
} |
| 585 | 585 |
|
| 586 |
+ // Try to look up the destination node |
|
| 587 |
+ if node == nil {
|
|
| 588 |
+ toAddr, _, err := net.SplitHostPort(addr) |
|
| 589 |
+ if err != nil {
|
|
| 590 |
+ m.logger.Printf("[ERR] memberlist: Failed to parse address %q: %v", addr, err)
|
|
| 591 |
+ return err |
|
| 592 |
+ } |
|
| 593 |
+ m.nodeLock.RLock() |
|
| 594 |
+ nodeState, ok := m.nodeMap[toAddr] |
|
| 595 |
+ m.nodeLock.RUnlock() |
|
| 596 |
+ if ok {
|
|
| 597 |
+ node = &nodeState.Node |
|
| 598 |
+ } |
|
| 599 |
+ } |
|
| 600 |
+ |
|
| 601 |
+ // Add a CRC to the end of the payload if the recipient understands |
|
| 602 |
+ // ProtocolVersion >= 5 |
|
| 603 |
+ if node != nil && node.PMax >= 5 {
|
|
| 604 |
+ crc := crc32.ChecksumIEEE(msg) |
|
| 605 |
+ header := make([]byte, 5, 5+len(msg)) |
|
| 606 |
+ header[0] = byte(hasCrcMsg) |
|
| 607 |
+ binary.BigEndian.PutUint32(header[1:], crc) |
|
| 608 |
+ msg = append(header, msg...) |
|
| 609 |
+ } |
|
| 610 |
+ |
|
| 586 | 611 |
// Check if we have encryption enabled |
| 587 | 612 |
if m.config.EncryptionEnabled() {
|
| 588 | 613 |
// Encrypt the payload |
| ... | ... |
@@ -597,12 +634,13 @@ func (m *Memberlist) rawSendMsgUDP(to net.Addr, msg []byte) error {
|
| 597 | 597 |
} |
| 598 | 598 |
|
| 599 | 599 |
metrics.IncrCounter([]string{"memberlist", "udp", "sent"}, float32(len(msg)))
|
| 600 |
- _, err := m.udpListener.WriteTo(msg, to) |
|
| 600 |
+ _, err := m.transport.WriteTo(msg, addr) |
|
| 601 | 601 |
return err |
| 602 | 602 |
} |
| 603 | 603 |
|
| 604 |
-// rawSendMsgTCP is used to send a TCP message to another host without modification |
|
| 605 |
-func (m *Memberlist) rawSendMsgTCP(conn net.Conn, sendBuf []byte) error {
|
|
| 604 |
+// rawSendMsgStream is used to stream a message to another host without |
|
| 605 |
+// modification, other than applying compression and encryption if enabled. |
|
| 606 |
+func (m *Memberlist) rawSendMsgStream(conn net.Conn, sendBuf []byte) error {
|
|
| 606 | 607 |
// Check if compresion is enabled |
| 607 | 608 |
if m.config.EnableCompression {
|
| 608 | 609 |
compBuf, err := compressPayload(sendBuf) |
| ... | ... |
@@ -635,43 +673,36 @@ func (m *Memberlist) rawSendMsgTCP(conn net.Conn, sendBuf []byte) error {
|
| 635 | 635 |
return nil |
| 636 | 636 |
} |
| 637 | 637 |
|
| 638 |
-// sendTCPUserMsg is used to send a TCP userMsg to another host |
|
| 639 |
-func (m *Memberlist) sendTCPUserMsg(to net.Addr, sendBuf []byte) error {
|
|
| 640 |
- dialer := net.Dialer{Timeout: m.config.TCPTimeout}
|
|
| 641 |
- conn, err := dialer.Dial("tcp", to.String())
|
|
| 638 |
+// sendUserMsg is used to stream a user message to another host. |
|
| 639 |
+func (m *Memberlist) sendUserMsg(addr string, sendBuf []byte) error {
|
|
| 640 |
+ conn, err := m.transport.DialTimeout(addr, m.config.TCPTimeout) |
|
| 642 | 641 |
if err != nil {
|
| 643 | 642 |
return err |
| 644 | 643 |
} |
| 645 | 644 |
defer conn.Close() |
| 646 | 645 |
|
| 647 | 646 |
bufConn := bytes.NewBuffer(nil) |
| 648 |
- |
|
| 649 | 647 |
if err := bufConn.WriteByte(byte(userMsg)); err != nil {
|
| 650 | 648 |
return err |
| 651 | 649 |
} |
| 652 | 650 |
|
| 653 |
- // Send our node state |
|
| 654 | 651 |
header := userMsgHeader{UserMsgLen: len(sendBuf)}
|
| 655 | 652 |
hd := codec.MsgpackHandle{}
|
| 656 | 653 |
enc := codec.NewEncoder(bufConn, &hd) |
| 657 |
- |
|
| 658 | 654 |
if err := enc.Encode(&header); err != nil {
|
| 659 | 655 |
return err |
| 660 | 656 |
} |
| 661 |
- |
|
| 662 | 657 |
if _, err := bufConn.Write(sendBuf); err != nil {
|
| 663 | 658 |
return err |
| 664 | 659 |
} |
| 665 |
- |
|
| 666 |
- return m.rawSendMsgTCP(conn, bufConn.Bytes()) |
|
| 660 |
+ return m.rawSendMsgStream(conn, bufConn.Bytes()) |
|
| 667 | 661 |
} |
| 668 | 662 |
|
| 669 |
-// sendAndReceiveState is used to initiate a push/pull over TCP with a remote node |
|
| 670 |
-func (m *Memberlist) sendAndReceiveState(addr []byte, port uint16, join bool) ([]pushNodeState, []byte, error) {
|
|
| 663 |
+// sendAndReceiveState is used to initiate a push/pull over a stream with a |
|
| 664 |
+// remote host. |
|
| 665 |
+func (m *Memberlist) sendAndReceiveState(addr string, join bool) ([]pushNodeState, []byte, error) {
|
|
| 671 | 666 |
// Attempt to connect |
| 672 |
- dialer := net.Dialer{Timeout: m.config.TCPTimeout}
|
|
| 673 |
- dest := net.TCPAddr{IP: addr, Port: int(port)}
|
|
| 674 |
- conn, err := dialer.Dial("tcp", dest.String())
|
|
| 667 |
+ conn, err := m.transport.DialTimeout(addr, m.config.TCPTimeout) |
|
| 675 | 668 |
if err != nil {
|
| 676 | 669 |
return nil, nil, err |
| 677 | 670 |
} |
| ... | ... |
@@ -685,7 +716,7 @@ func (m *Memberlist) sendAndReceiveState(addr []byte, port uint16, join bool) ([ |
| 685 | 685 |
} |
| 686 | 686 |
|
| 687 | 687 |
conn.SetDeadline(time.Now().Add(m.config.TCPTimeout)) |
| 688 |
- msgType, bufConn, dec, err := m.readTCP(conn) |
|
| 688 |
+ msgType, bufConn, dec, err := m.readStream(conn) |
|
| 689 | 689 |
if err != nil {
|
| 690 | 690 |
return nil, nil, err |
| 691 | 691 |
} |
| ... | ... |
@@ -701,7 +732,7 @@ func (m *Memberlist) sendAndReceiveState(addr []byte, port uint16, join bool) ([ |
| 701 | 701 |
return remoteNodes, userState, err |
| 702 | 702 |
} |
| 703 | 703 |
|
| 704 |
-// sendLocalState is invoked to send our local state over a tcp connection |
|
| 704 |
+// sendLocalState is invoked to send our local state over a stream connection. |
|
| 705 | 705 |
func (m *Memberlist) sendLocalState(conn net.Conn, join bool) error {
|
| 706 | 706 |
// Setup a deadline |
| 707 | 707 |
conn.SetDeadline(time.Now().Add(m.config.TCPTimeout)) |
| ... | ... |
@@ -759,7 +790,7 @@ func (m *Memberlist) sendLocalState(conn net.Conn, join bool) error {
|
| 759 | 759 |
} |
| 760 | 760 |
|
| 761 | 761 |
// Get the send buffer |
| 762 |
- return m.rawSendMsgTCP(conn, bufConn.Bytes()) |
|
| 762 |
+ return m.rawSendMsgStream(conn, bufConn.Bytes()) |
|
| 763 | 763 |
} |
| 764 | 764 |
|
| 765 | 765 |
// encryptLocalState is used to help encrypt local state before sending |
| ... | ... |
@@ -817,9 +848,9 @@ func (m *Memberlist) decryptRemoteState(bufConn io.Reader) ([]byte, error) {
|
| 817 | 817 |
return decryptPayload(keys, cipherBytes, dataBytes) |
| 818 | 818 |
} |
| 819 | 819 |
|
| 820 |
-// readTCP is used to read the start of a TCP stream. |
|
| 821 |
-// it decrypts and decompresses the stream if necessary |
|
| 822 |
-func (m *Memberlist) readTCP(conn net.Conn) (messageType, io.Reader, *codec.Decoder, error) {
|
|
| 820 |
+// readStream is used to read from a stream connection, decrypting and |
|
| 821 |
+// decompressing the stream if necessary. |
|
| 822 |
+func (m *Memberlist) readStream(conn net.Conn) (messageType, io.Reader, *codec.Decoder, error) {
|
|
| 823 | 823 |
// Created a buffered reader |
| 824 | 824 |
var bufConn io.Reader = bufio.NewReader(conn) |
| 825 | 825 |
|
| ... | ... |
@@ -960,7 +991,7 @@ func (m *Memberlist) mergeRemoteState(join bool, remoteNodes []pushNodeState, us |
| 960 | 960 |
return nil |
| 961 | 961 |
} |
| 962 | 962 |
|
| 963 |
-// readUserMsg is used to decode a userMsg from a TCP stream |
|
| 963 |
+// readUserMsg is used to decode a userMsg from a stream. |
|
| 964 | 964 |
func (m *Memberlist) readUserMsg(bufConn io.Reader, dec *codec.Decoder) error {
|
| 965 | 965 |
// Read the user message header |
| 966 | 966 |
var header userMsgHeader |
| ... | ... |
@@ -991,13 +1022,12 @@ func (m *Memberlist) readUserMsg(bufConn io.Reader, dec *codec.Decoder) error {
|
| 991 | 991 |
return nil |
| 992 | 992 |
} |
| 993 | 993 |
|
| 994 |
-// sendPingAndWaitForAck makes a TCP connection to the given address, sends |
|
| 994 |
+// sendPingAndWaitForAck makes a stream connection to the given address, sends |
|
| 995 | 995 |
// a ping, and waits for an ack. All of this is done as a series of blocking |
| 996 | 996 |
// operations, given the deadline. The bool return parameter is true if we |
| 997 | 997 |
// we able to round trip a ping to the other node. |
| 998 |
-func (m *Memberlist) sendPingAndWaitForAck(destAddr net.Addr, ping ping, deadline time.Time) (bool, error) {
|
|
| 999 |
- dialer := net.Dialer{Deadline: deadline}
|
|
| 1000 |
- conn, err := dialer.Dial("tcp", destAddr.String())
|
|
| 998 |
+func (m *Memberlist) sendPingAndWaitForAck(addr string, ping ping, deadline time.Time) (bool, error) {
|
|
| 999 |
+ conn, err := m.transport.DialTimeout(addr, m.config.TCPTimeout) |
|
| 1001 | 1000 |
if err != nil {
|
| 1002 | 1001 |
// If the node is actually dead we expect this to fail, so we |
| 1003 | 1002 |
// 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 |
| 1013 | 1013 |
return false, err |
| 1014 | 1014 |
} |
| 1015 | 1015 |
|
| 1016 |
- if err = m.rawSendMsgTCP(conn, out.Bytes()); err != nil {
|
|
| 1016 |
+ if err = m.rawSendMsgStream(conn, out.Bytes()); err != nil {
|
|
| 1017 | 1017 |
return false, err |
| 1018 | 1018 |
} |
| 1019 | 1019 |
|
| 1020 |
- msgType, _, dec, err := m.readTCP(conn) |
|
| 1020 |
+ msgType, _, dec, err := m.readStream(conn) |
|
| 1021 | 1021 |
if err != nil {
|
| 1022 | 1022 |
return false, err |
| 1023 | 1023 |
} |
| 1024 | 1024 |
|
| 1025 | 1025 |
if msgType != ackRespMsg {
|
| 1026 |
- return false, fmt.Errorf("Unexpected msgType (%d) from TCP ping %s", msgType, LogConn(conn))
|
|
| 1026 |
+ return false, fmt.Errorf("Unexpected msgType (%d) from ping %s", msgType, LogConn(conn))
|
|
| 1027 | 1027 |
} |
| 1028 | 1028 |
|
| 1029 | 1029 |
var ack ackResp |
| ... | ... |
@@ -1032,7 +1062,7 @@ func (m *Memberlist) sendPingAndWaitForAck(destAddr net.Addr, ping ping, deadlin |
| 1032 | 1032 |
} |
| 1033 | 1033 |
|
| 1034 | 1034 |
if ack.SeqNo != ping.SeqNo {
|
| 1035 |
- return false, fmt.Errorf("Sequence number from ack (%d) doesn't match ping (%d) from TCP ping %s", ack.SeqNo, ping.SeqNo, LogConn(conn))
|
|
| 1035 |
+ return false, fmt.Errorf("Sequence number from ack (%d) doesn't match ping (%d)", ack.SeqNo, ping.SeqNo, LogConn(conn))
|
|
| 1036 | 1036 |
} |
| 1037 | 1037 |
|
| 1038 | 1038 |
return true, nil |
| 1039 | 1039 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,289 @@ |
| 0 |
+package memberlist |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "fmt" |
|
| 4 |
+ "log" |
|
| 5 |
+ "net" |
|
| 6 |
+ "sync" |
|
| 7 |
+ "sync/atomic" |
|
| 8 |
+ "time" |
|
| 9 |
+ |
|
| 10 |
+ "github.com/armon/go-metrics" |
|
| 11 |
+ sockaddr "github.com/hashicorp/go-sockaddr" |
|
| 12 |
+) |
|
| 13 |
+ |
|
| 14 |
+const ( |
|
| 15 |
+ // udpPacketBufSize is used to buffer incoming packets during read |
|
| 16 |
+ // operations. |
|
| 17 |
+ udpPacketBufSize = 65536 |
|
| 18 |
+ |
|
| 19 |
+ // udpRecvBufSize is a large buffer size that we attempt to set UDP |
|
| 20 |
+ // sockets to in order to handle a large volume of messages. |
|
| 21 |
+ udpRecvBufSize = 2 * 1024 * 1024 |
|
| 22 |
+) |
|
| 23 |
+ |
|
| 24 |
+// NetTransportConfig is used to configure a net transport. |
|
| 25 |
+type NetTransportConfig struct {
|
|
| 26 |
+ // BindAddrs is a list of addresses to bind to for both TCP and UDP |
|
| 27 |
+ // communications. |
|
| 28 |
+ BindAddrs []string |
|
| 29 |
+ |
|
| 30 |
+ // BindPort is the port to listen on, for each address above. |
|
| 31 |
+ BindPort int |
|
| 32 |
+ |
|
| 33 |
+ // Logger is a logger for operator messages. |
|
| 34 |
+ Logger *log.Logger |
|
| 35 |
+} |
|
| 36 |
+ |
|
| 37 |
+// NetTransport is a Transport implementation that uses connectionless UDP for |
|
| 38 |
+// packet operations, and ad-hoc TCP connections for stream operations. |
|
| 39 |
+type NetTransport struct {
|
|
| 40 |
+ config *NetTransportConfig |
|
| 41 |
+ packetCh chan *Packet |
|
| 42 |
+ streamCh chan net.Conn |
|
| 43 |
+ logger *log.Logger |
|
| 44 |
+ wg sync.WaitGroup |
|
| 45 |
+ tcpListeners []*net.TCPListener |
|
| 46 |
+ udpListeners []*net.UDPConn |
|
| 47 |
+ shutdown int32 |
|
| 48 |
+} |
|
| 49 |
+ |
|
| 50 |
+// NewNetTransport returns a net transport with the given configuration. On |
|
| 51 |
+// success all the network listeners will be created and listening. |
|
| 52 |
+func NewNetTransport(config *NetTransportConfig) (*NetTransport, error) {
|
|
| 53 |
+ // If we reject the empty list outright we can assume that there's at |
|
| 54 |
+ // least one listener of each type later during operation. |
|
| 55 |
+ if len(config.BindAddrs) == 0 {
|
|
| 56 |
+ return nil, fmt.Errorf("At least one bind address is required")
|
|
| 57 |
+ } |
|
| 58 |
+ |
|
| 59 |
+ // Build out the new transport. |
|
| 60 |
+ var ok bool |
|
| 61 |
+ t := NetTransport{
|
|
| 62 |
+ config: config, |
|
| 63 |
+ packetCh: make(chan *Packet), |
|
| 64 |
+ streamCh: make(chan net.Conn), |
|
| 65 |
+ logger: config.Logger, |
|
| 66 |
+ } |
|
| 67 |
+ |
|
| 68 |
+ // Clean up listeners if there's an error. |
|
| 69 |
+ defer func() {
|
|
| 70 |
+ if !ok {
|
|
| 71 |
+ t.Shutdown() |
|
| 72 |
+ } |
|
| 73 |
+ }() |
|
| 74 |
+ |
|
| 75 |
+ // Build all the TCP and UDP listeners. |
|
| 76 |
+ port := config.BindPort |
|
| 77 |
+ for _, addr := range config.BindAddrs {
|
|
| 78 |
+ ip := net.ParseIP(addr) |
|
| 79 |
+ |
|
| 80 |
+ tcpAddr := &net.TCPAddr{IP: ip, Port: port}
|
|
| 81 |
+ tcpLn, err := net.ListenTCP("tcp", tcpAddr)
|
|
| 82 |
+ if err != nil {
|
|
| 83 |
+ return nil, fmt.Errorf("Failed to start TCP listener on %q port %d: %v", addr, port, err)
|
|
| 84 |
+ } |
|
| 85 |
+ t.tcpListeners = append(t.tcpListeners, tcpLn) |
|
| 86 |
+ |
|
| 87 |
+ // If the config port given was zero, use the first TCP listener |
|
| 88 |
+ // to pick an available port and then apply that to everything |
|
| 89 |
+ // else. |
|
| 90 |
+ if port == 0 {
|
|
| 91 |
+ port = tcpLn.Addr().(*net.TCPAddr).Port |
|
| 92 |
+ } |
|
| 93 |
+ |
|
| 94 |
+ udpAddr := &net.UDPAddr{IP: ip, Port: port}
|
|
| 95 |
+ udpLn, err := net.ListenUDP("udp", udpAddr)
|
|
| 96 |
+ if err != nil {
|
|
| 97 |
+ return nil, fmt.Errorf("Failed to start UDP listener on %q port %d: %v", addr, port, err)
|
|
| 98 |
+ } |
|
| 99 |
+ if err := setUDPRecvBuf(udpLn); err != nil {
|
|
| 100 |
+ return nil, fmt.Errorf("Failed to resize UDP buffer: %v", err)
|
|
| 101 |
+ } |
|
| 102 |
+ t.udpListeners = append(t.udpListeners, udpLn) |
|
| 103 |
+ } |
|
| 104 |
+ |
|
| 105 |
+ // Fire them up now that we've been able to create them all. |
|
| 106 |
+ for i := 0; i < len(config.BindAddrs); i++ {
|
|
| 107 |
+ t.wg.Add(2) |
|
| 108 |
+ go t.tcpListen(t.tcpListeners[i]) |
|
| 109 |
+ go t.udpListen(t.udpListeners[i]) |
|
| 110 |
+ } |
|
| 111 |
+ |
|
| 112 |
+ ok = true |
|
| 113 |
+ return &t, nil |
|
| 114 |
+} |
|
| 115 |
+ |
|
| 116 |
+// GetAutoBindPort returns the bind port that was automatically given by the |
|
| 117 |
+// kernel, if a bind port of 0 was given. |
|
| 118 |
+func (t *NetTransport) GetAutoBindPort() int {
|
|
| 119 |
+ // We made sure there's at least one TCP listener, and that one's |
|
| 120 |
+ // port was applied to all the others for the dynamic bind case. |
|
| 121 |
+ return t.tcpListeners[0].Addr().(*net.TCPAddr).Port |
|
| 122 |
+} |
|
| 123 |
+ |
|
| 124 |
+// See Transport. |
|
| 125 |
+func (t *NetTransport) FinalAdvertiseAddr(ip string, port int) (net.IP, int, error) {
|
|
| 126 |
+ var advertiseAddr net.IP |
|
| 127 |
+ var advertisePort int |
|
| 128 |
+ if ip != "" {
|
|
| 129 |
+ // If they've supplied an address, use that. |
|
| 130 |
+ advertiseAddr = net.ParseIP(ip) |
|
| 131 |
+ if advertiseAddr == nil {
|
|
| 132 |
+ return nil, 0, fmt.Errorf("Failed to parse advertise address %q", ip)
|
|
| 133 |
+ } |
|
| 134 |
+ |
|
| 135 |
+ // Ensure IPv4 conversion if necessary. |
|
| 136 |
+ if ip4 := advertiseAddr.To4(); ip4 != nil {
|
|
| 137 |
+ advertiseAddr = ip4 |
|
| 138 |
+ } |
|
| 139 |
+ advertisePort = port |
|
| 140 |
+ } else {
|
|
| 141 |
+ if t.config.BindAddrs[0] == "0.0.0.0" {
|
|
| 142 |
+ // Otherwise, if we're not bound to a specific IP, let's |
|
| 143 |
+ // use a suitable private IP address. |
|
| 144 |
+ var err error |
|
| 145 |
+ ip, err = sockaddr.GetPrivateIP() |
|
| 146 |
+ if err != nil {
|
|
| 147 |
+ return nil, 0, fmt.Errorf("Failed to get interface addresses: %v", err)
|
|
| 148 |
+ } |
|
| 149 |
+ if ip == "" {
|
|
| 150 |
+ return nil, 0, fmt.Errorf("No private IP address found, and explicit IP not provided")
|
|
| 151 |
+ } |
|
| 152 |
+ |
|
| 153 |
+ advertiseAddr = net.ParseIP(ip) |
|
| 154 |
+ if advertiseAddr == nil {
|
|
| 155 |
+ return nil, 0, fmt.Errorf("Failed to parse advertise address: %q", ip)
|
|
| 156 |
+ } |
|
| 157 |
+ } else {
|
|
| 158 |
+ // Use the IP that we're bound to, based on the first |
|
| 159 |
+ // TCP listener, which we already ensure is there. |
|
| 160 |
+ advertiseAddr = t.tcpListeners[0].Addr().(*net.TCPAddr).IP |
|
| 161 |
+ } |
|
| 162 |
+ |
|
| 163 |
+ // Use the port we are bound to. |
|
| 164 |
+ advertisePort = t.GetAutoBindPort() |
|
| 165 |
+ } |
|
| 166 |
+ |
|
| 167 |
+ return advertiseAddr, advertisePort, nil |
|
| 168 |
+} |
|
| 169 |
+ |
|
| 170 |
+// See Transport. |
|
| 171 |
+func (t *NetTransport) WriteTo(b []byte, addr string) (time.Time, error) {
|
|
| 172 |
+ udpAddr, err := net.ResolveUDPAddr("udp", addr)
|
|
| 173 |
+ if err != nil {
|
|
| 174 |
+ return time.Time{}, err
|
|
| 175 |
+ } |
|
| 176 |
+ |
|
| 177 |
+ // We made sure there's at least one UDP listener, so just use the |
|
| 178 |
+ // packet sending interface on the first one. Take the time after the |
|
| 179 |
+ // write call comes back, which will underestimate the time a little, |
|
| 180 |
+ // but help account for any delays before the write occurs. |
|
| 181 |
+ _, err = t.udpListeners[0].WriteTo(b, udpAddr) |
|
| 182 |
+ return time.Now(), err |
|
| 183 |
+} |
|
| 184 |
+ |
|
| 185 |
+// See Transport. |
|
| 186 |
+func (t *NetTransport) PacketCh() <-chan *Packet {
|
|
| 187 |
+ return t.packetCh |
|
| 188 |
+} |
|
| 189 |
+ |
|
| 190 |
+// See Transport. |
|
| 191 |
+func (t *NetTransport) DialTimeout(addr string, timeout time.Duration) (net.Conn, error) {
|
|
| 192 |
+ dialer := net.Dialer{Timeout: timeout}
|
|
| 193 |
+ return dialer.Dial("tcp", addr)
|
|
| 194 |
+} |
|
| 195 |
+ |
|
| 196 |
+// See Transport. |
|
| 197 |
+func (t *NetTransport) StreamCh() <-chan net.Conn {
|
|
| 198 |
+ return t.streamCh |
|
| 199 |
+} |
|
| 200 |
+ |
|
| 201 |
+// See Transport. |
|
| 202 |
+func (t *NetTransport) Shutdown() error {
|
|
| 203 |
+ // This will avoid log spam about errors when we shut down. |
|
| 204 |
+ atomic.StoreInt32(&t.shutdown, 1) |
|
| 205 |
+ |
|
| 206 |
+ // Rip through all the connections and shut them down. |
|
| 207 |
+ for _, conn := range t.tcpListeners {
|
|
| 208 |
+ conn.Close() |
|
| 209 |
+ } |
|
| 210 |
+ for _, conn := range t.udpListeners {
|
|
| 211 |
+ conn.Close() |
|
| 212 |
+ } |
|
| 213 |
+ |
|
| 214 |
+ // Block until all the listener threads have died. |
|
| 215 |
+ t.wg.Wait() |
|
| 216 |
+ return nil |
|
| 217 |
+} |
|
| 218 |
+ |
|
| 219 |
+// tcpListen is a long running goroutine that accepts incoming TCP connections |
|
| 220 |
+// and hands them off to the stream channel. |
|
| 221 |
+func (t *NetTransport) tcpListen(tcpLn *net.TCPListener) {
|
|
| 222 |
+ defer t.wg.Done() |
|
| 223 |
+ for {
|
|
| 224 |
+ conn, err := tcpLn.AcceptTCP() |
|
| 225 |
+ if err != nil {
|
|
| 226 |
+ if s := atomic.LoadInt32(&t.shutdown); s == 1 {
|
|
| 227 |
+ break |
|
| 228 |
+ } |
|
| 229 |
+ |
|
| 230 |
+ t.logger.Printf("[ERR] memberlist: Error accepting TCP connection: %v", err)
|
|
| 231 |
+ continue |
|
| 232 |
+ } |
|
| 233 |
+ |
|
| 234 |
+ t.streamCh <- conn |
|
| 235 |
+ } |
|
| 236 |
+} |
|
| 237 |
+ |
|
| 238 |
+// udpListen is a long running goroutine that accepts incoming UDP packets and |
|
| 239 |
+// hands them off to the packet channel. |
|
| 240 |
+func (t *NetTransport) udpListen(udpLn *net.UDPConn) {
|
|
| 241 |
+ defer t.wg.Done() |
|
| 242 |
+ for {
|
|
| 243 |
+ // Do a blocking read into a fresh buffer. Grab a time stamp as |
|
| 244 |
+ // close as possible to the I/O. |
|
| 245 |
+ buf := make([]byte, udpPacketBufSize) |
|
| 246 |
+ n, addr, err := udpLn.ReadFrom(buf) |
|
| 247 |
+ ts := time.Now() |
|
| 248 |
+ if err != nil {
|
|
| 249 |
+ if s := atomic.LoadInt32(&t.shutdown); s == 1 {
|
|
| 250 |
+ break |
|
| 251 |
+ } |
|
| 252 |
+ |
|
| 253 |
+ t.logger.Printf("[ERR] memberlist: Error reading UDP packet: %v", err)
|
|
| 254 |
+ continue |
|
| 255 |
+ } |
|
| 256 |
+ |
|
| 257 |
+ // Check the length - it needs to have at least one byte to be a |
|
| 258 |
+ // proper message. |
|
| 259 |
+ if n < 1 {
|
|
| 260 |
+ t.logger.Printf("[ERR] memberlist: UDP packet too short (%d bytes) %s",
|
|
| 261 |
+ len(buf), LogAddress(addr)) |
|
| 262 |
+ continue |
|
| 263 |
+ } |
|
| 264 |
+ |
|
| 265 |
+ // Ingest the packet. |
|
| 266 |
+ metrics.IncrCounter([]string{"memberlist", "udp", "received"}, float32(n))
|
|
| 267 |
+ t.packetCh <- &Packet{
|
|
| 268 |
+ Buf: buf[:n], |
|
| 269 |
+ From: addr, |
|
| 270 |
+ Timestamp: ts, |
|
| 271 |
+ } |
|
| 272 |
+ } |
|
| 273 |
+} |
|
| 274 |
+ |
|
| 275 |
+// setUDPRecvBuf is used to resize the UDP receive window. The function |
|
| 276 |
+// attempts to set the read buffer to `udpRecvBuf` but backs off until |
|
| 277 |
+// the read buffer can be set. |
|
| 278 |
+func setUDPRecvBuf(c *net.UDPConn) error {
|
|
| 279 |
+ size := udpRecvBufSize |
|
| 280 |
+ var err error |
|
| 281 |
+ for size > 0 {
|
|
| 282 |
+ if err = c.SetReadBuffer(size); err == nil {
|
|
| 283 |
+ return nil |
|
| 284 |
+ } |
|
| 285 |
+ size = size / 2 |
|
| 286 |
+ } |
|
| 287 |
+ return err |
|
| 288 |
+} |
| ... | ... |
@@ -34,6 +34,12 @@ type Node struct {
|
| 34 | 34 |
DCur uint8 // Current version delegate is speaking |
| 35 | 35 |
} |
| 36 | 36 |
|
| 37 |
+// Address returns the host:port form of a node's address, suitable for use |
|
| 38 |
+// with a transport. |
|
| 39 |
+func (n *Node) Address() string {
|
|
| 40 |
+ return joinHostPort(n.Addr.String(), n.Port) |
|
| 41 |
+} |
|
| 42 |
+ |
|
| 37 | 43 |
// NodeState is used to manage our state view of another node |
| 38 | 44 |
type nodeState struct {
|
| 39 | 45 |
Node |
| ... | ... |
@@ -42,10 +48,17 @@ type nodeState struct {
|
| 42 | 42 |
StateChange time.Time // Time last state change happened |
| 43 | 43 |
} |
| 44 | 44 |
|
| 45 |
-// ackHandler is used to register handlers for incoming acks |
|
| 45 |
+// Address returns the host:port form of a node's address, suitable for use |
|
| 46 |
+// with a transport. |
|
| 47 |
+func (n *nodeState) Address() string {
|
|
| 48 |
+ return n.Node.Address() |
|
| 49 |
+} |
|
| 50 |
+ |
|
| 51 |
+// ackHandler is used to register handlers for incoming acks and nacks. |
|
| 46 | 52 |
type ackHandler struct {
|
| 47 |
- handler func([]byte, time.Time) |
|
| 48 |
- timer *time.Timer |
|
| 53 |
+ ackFn func([]byte, time.Time) |
|
| 54 |
+ nackFn func() |
|
| 55 |
+ timer *time.Timer |
|
| 49 | 56 |
} |
| 50 | 57 |
|
| 51 | 58 |
// NoPingResponseError is used to indicate a 'ping' packet was |
| ... | ... |
@@ -148,7 +161,7 @@ func (m *Memberlist) pushPullTrigger(stop <-chan struct{}) {
|
| 148 | 148 |
} |
| 149 | 149 |
} |
| 150 | 150 |
|
| 151 |
-// Deschedule is used to stop the background maintenence. This is safe |
|
| 151 |
+// Deschedule is used to stop the background maintenance. This is safe |
|
| 152 | 152 |
// to call multiple times. |
| 153 | 153 |
func (m *Memberlist) deschedule() {
|
| 154 | 154 |
m.tickerLock.Lock() |
| ... | ... |
@@ -219,17 +232,51 @@ START: |
| 219 | 219 |
func (m *Memberlist) probeNode(node *nodeState) {
|
| 220 | 220 |
defer metrics.MeasureSince([]string{"memberlist", "probeNode"}, time.Now())
|
| 221 | 221 |
|
| 222 |
+ // We use our health awareness to scale the overall probe interval, so we |
|
| 223 |
+ // slow down if we detect problems. The ticker that calls us can handle |
|
| 224 |
+ // us running over the base interval, and will skip missed ticks. |
|
| 225 |
+ probeInterval := m.awareness.ScaleTimeout(m.config.ProbeInterval) |
|
| 226 |
+ if probeInterval > m.config.ProbeInterval {
|
|
| 227 |
+ metrics.IncrCounter([]string{"memberlist", "degraded", "probe"}, 1)
|
|
| 228 |
+ } |
|
| 229 |
+ |
|
| 222 | 230 |
// Prepare a ping message and setup an ack handler. |
| 223 | 231 |
ping := ping{SeqNo: m.nextSeqNo(), Node: node.Name}
|
| 224 | 232 |
ackCh := make(chan ackMessage, m.config.IndirectChecks+1) |
| 225 |
- m.setAckChannel(ping.SeqNo, ackCh, m.config.ProbeInterval) |
|
| 233 |
+ nackCh := make(chan struct{}, m.config.IndirectChecks+1)
|
|
| 234 |
+ m.setProbeChannels(ping.SeqNo, ackCh, nackCh, probeInterval) |
|
| 235 |
+ |
|
| 236 |
+ // Send a ping to the node. If this node looks like it's suspect or dead, |
|
| 237 |
+ // also tack on a suspect message so that it has a chance to refute as |
|
| 238 |
+ // soon as possible. |
|
| 239 |
+ deadline := time.Now().Add(probeInterval) |
|
| 240 |
+ addr := node.Address() |
|
| 241 |
+ if node.State == stateAlive {
|
|
| 242 |
+ if err := m.encodeAndSendMsg(addr, pingMsg, &ping); err != nil {
|
|
| 243 |
+ m.logger.Printf("[ERR] memberlist: Failed to send ping: %s", err)
|
|
| 244 |
+ return |
|
| 245 |
+ } |
|
| 246 |
+ } else {
|
|
| 247 |
+ var msgs [][]byte |
|
| 248 |
+ if buf, err := encode(pingMsg, &ping); err != nil {
|
|
| 249 |
+ m.logger.Printf("[ERR] memberlist: Failed to encode ping message: %s", err)
|
|
| 250 |
+ return |
|
| 251 |
+ } else {
|
|
| 252 |
+ msgs = append(msgs, buf.Bytes()) |
|
| 253 |
+ } |
|
| 254 |
+ s := suspect{Incarnation: node.Incarnation, Node: node.Name, From: m.config.Name}
|
|
| 255 |
+ if buf, err := encode(suspectMsg, &s); err != nil {
|
|
| 256 |
+ m.logger.Printf("[ERR] memberlist: Failed to encode suspect message: %s", err)
|
|
| 257 |
+ return |
|
| 258 |
+ } else {
|
|
| 259 |
+ msgs = append(msgs, buf.Bytes()) |
|
| 260 |
+ } |
|
| 226 | 261 |
|
| 227 |
- // Send a ping to the node. |
|
| 228 |
- deadline := time.Now().Add(m.config.ProbeInterval) |
|
| 229 |
- destAddr := &net.UDPAddr{IP: node.Addr, Port: int(node.Port)}
|
|
| 230 |
- if err := m.encodeAndSendMsg(destAddr, pingMsg, &ping); err != nil {
|
|
| 231 |
- m.logger.Printf("[ERR] memberlist: Failed to send ping: %s", err)
|
|
| 232 |
- return |
|
| 262 |
+ compound := makeCompoundMessage(msgs) |
|
| 263 |
+ if err := m.rawSendMsgPacket(addr, &node.Node, compound.Bytes()); err != nil {
|
|
| 264 |
+ m.logger.Printf("[ERR] memberlist: Failed to send compound ping and suspect message to %s: %s", addr, err)
|
|
| 265 |
+ return |
|
| 266 |
+ } |
|
| 233 | 267 |
} |
| 234 | 268 |
|
| 235 | 269 |
// Mark the sent time here, which should be after any pre-processing and |
| ... | ... |
@@ -237,6 +284,16 @@ func (m *Memberlist) probeNode(node *nodeState) {
|
| 237 | 237 |
// but it's the best we can do. |
| 238 | 238 |
sent := time.Now() |
| 239 | 239 |
|
| 240 |
+ // Arrange for our self-awareness to get updated. At this point we've |
|
| 241 |
+ // sent the ping, so any return statement means the probe succeeded |
|
| 242 |
+ // which will improve our health until we get to the failure scenarios |
|
| 243 |
+ // at the end of this function, which will alter this delta variable |
|
| 244 |
+ // accordingly. |
|
| 245 |
+ awarenessDelta := -1 |
|
| 246 |
+ defer func() {
|
|
| 247 |
+ m.awareness.ApplyDelta(awarenessDelta) |
|
| 248 |
+ }() |
|
| 249 |
+ |
|
| 240 | 250 |
// Wait for response or round-trip-time. |
| 241 | 251 |
select {
|
| 242 | 252 |
case v := <-ackCh: |
| ... | ... |
@@ -254,20 +311,35 @@ func (m *Memberlist) probeNode(node *nodeState) {
|
| 254 | 254 |
ackCh <- v |
| 255 | 255 |
} |
| 256 | 256 |
case <-time.After(m.config.ProbeTimeout): |
| 257 |
- m.logger.Printf("[DEBUG] memberlist: Failed UDP ping: %v (timeout reached)", node.Name)
|
|
| 257 |
+ // Note that we don't scale this timeout based on awareness and |
|
| 258 |
+ // the health score. That's because we don't really expect waiting |
|
| 259 |
+ // longer to help get UDP through. Since health does extend the |
|
| 260 |
+ // probe interval it will give the TCP fallback more time, which |
|
| 261 |
+ // is more active in dealing with lost packets, and it gives more |
|
| 262 |
+ // time to wait for indirect acks/nacks. |
|
| 263 |
+ m.logger.Printf("[DEBUG] memberlist: Failed ping: %v (timeout reached)", node.Name)
|
|
| 258 | 264 |
} |
| 259 | 265 |
|
| 260 | 266 |
// Get some random live nodes. |
| 261 | 267 |
m.nodeLock.RLock() |
| 262 |
- excludes := []string{m.config.Name, node.Name}
|
|
| 263 |
- kNodes := kRandomNodes(m.config.IndirectChecks, excludes, m.nodes) |
|
| 268 |
+ kNodes := kRandomNodes(m.config.IndirectChecks, m.nodes, func(n *nodeState) bool {
|
|
| 269 |
+ return n.Name == m.config.Name || |
|
| 270 |
+ n.Name == node.Name || |
|
| 271 |
+ n.State != stateAlive |
|
| 272 |
+ }) |
|
| 264 | 273 |
m.nodeLock.RUnlock() |
| 265 | 274 |
|
| 266 | 275 |
// Attempt an indirect ping. |
| 276 |
+ expectedNacks := 0 |
|
| 267 | 277 |
ind := indirectPingReq{SeqNo: ping.SeqNo, Target: node.Addr, Port: node.Port, Node: node.Name}
|
| 268 | 278 |
for _, peer := range kNodes {
|
| 269 |
- destAddr := &net.UDPAddr{IP: peer.Addr, Port: int(peer.Port)}
|
|
| 270 |
- if err := m.encodeAndSendMsg(destAddr, indirectPingMsg, &ind); err != nil {
|
|
| 279 |
+ // We only expect nack to be sent from peers who understand |
|
| 280 |
+ // version 4 of the protocol. |
|
| 281 |
+ if ind.Nack = peer.PMax >= 4; ind.Nack {
|
|
| 282 |
+ expectedNacks++ |
|
| 283 |
+ } |
|
| 284 |
+ |
|
| 285 |
+ if err := m.encodeAndSendMsg(peer.Address(), indirectPingMsg, &ind); err != nil {
|
|
| 271 | 286 |
m.logger.Printf("[ERR] memberlist: Failed to send indirect ping: %s", err)
|
| 272 | 287 |
} |
| 273 | 288 |
} |
| ... | ... |
@@ -284,12 +356,11 @@ func (m *Memberlist) probeNode(node *nodeState) {
|
| 284 | 284 |
// config option to turn this off if desired. |
| 285 | 285 |
fallbackCh := make(chan bool, 1) |
| 286 | 286 |
if (!m.config.DisableTcpPings) && (node.PMax >= 3) {
|
| 287 |
- destAddr := &net.TCPAddr{IP: node.Addr, Port: int(node.Port)}
|
|
| 288 | 287 |
go func() {
|
| 289 | 288 |
defer close(fallbackCh) |
| 290 |
- didContact, err := m.sendPingAndWaitForAck(destAddr, ping, deadline) |
|
| 289 |
+ didContact, err := m.sendPingAndWaitForAck(node.Address(), ping, deadline) |
|
| 291 | 290 |
if err != nil {
|
| 292 |
- m.logger.Printf("[ERR] memberlist: Failed TCP fallback ping: %s", err)
|
|
| 291 |
+ m.logger.Printf("[ERR] memberlist: Failed fallback ping: %s", err)
|
|
| 293 | 292 |
} else {
|
| 294 | 293 |
fallbackCh <- didContact |
| 295 | 294 |
} |
| ... | ... |
@@ -314,12 +385,28 @@ func (m *Memberlist) probeNode(node *nodeState) {
|
| 314 | 314 |
// any additional time here. |
| 315 | 315 |
for didContact := range fallbackCh {
|
| 316 | 316 |
if didContact {
|
| 317 |
- 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)
|
|
| 317 |
+ m.logger.Printf("[WARN] memberlist: Was able to connect to %s but other probes failed, network may be misconfigured", node.Name)
|
|
| 318 | 318 |
return |
| 319 | 319 |
} |
| 320 | 320 |
} |
| 321 | 321 |
|
| 322 |
- // No acks received from target, suspect |
|
| 322 |
+ // Update our self-awareness based on the results of this failed probe. |
|
| 323 |
+ // If we don't have peers who will send nacks then we penalize for any |
|
| 324 |
+ // failed probe as a simple health metric. If we do have peers to nack |
|
| 325 |
+ // verify, then we can use that as a more sophisticated measure of self- |
|
| 326 |
+ // health because we assume them to be working, and they can help us |
|
| 327 |
+ // decide if the probed node was really dead or if it was something wrong |
|
| 328 |
+ // with ourselves. |
|
| 329 |
+ awarenessDelta = 0 |
|
| 330 |
+ if expectedNacks > 0 {
|
|
| 331 |
+ if nackCount := len(nackCh); nackCount < expectedNacks {
|
|
| 332 |
+ awarenessDelta += (expectedNacks - nackCount) |
|
| 333 |
+ } |
|
| 334 |
+ } else {
|
|
| 335 |
+ awarenessDelta += 1 |
|
| 336 |
+ } |
|
| 337 |
+ |
|
| 338 |
+ // No acks received from target, suspect it as failed. |
|
| 323 | 339 |
m.logger.Printf("[INFO] memberlist: Suspect %s has failed, no acks received", node.Name)
|
| 324 | 340 |
s := suspect{Incarnation: node.Incarnation, Node: node.Name, From: m.config.Name}
|
| 325 | 341 |
m.suspectNode(&s) |
| ... | ... |
@@ -330,10 +417,10 @@ func (m *Memberlist) Ping(node string, addr net.Addr) (time.Duration, error) {
|
| 330 | 330 |
// Prepare a ping message and setup an ack handler. |
| 331 | 331 |
ping := ping{SeqNo: m.nextSeqNo(), Node: node}
|
| 332 | 332 |
ackCh := make(chan ackMessage, m.config.IndirectChecks+1) |
| 333 |
- m.setAckChannel(ping.SeqNo, ackCh, m.config.ProbeInterval) |
|
| 333 |
+ m.setProbeChannels(ping.SeqNo, ackCh, nil, m.config.ProbeInterval) |
|
| 334 | 334 |
|
| 335 | 335 |
// Send a ping to the node. |
| 336 |
- if err := m.encodeAndSendMsg(addr, pingMsg, &ping); err != nil {
|
|
| 336 |
+ if err := m.encodeAndSendMsg(addr.String(), pingMsg, &ping); err != nil {
|
|
| 337 | 337 |
return 0, err |
| 338 | 338 |
} |
| 339 | 339 |
|
| ... | ... |
@@ -362,8 +449,8 @@ func (m *Memberlist) resetNodes() {
|
| 362 | 362 |
m.nodeLock.Lock() |
| 363 | 363 |
defer m.nodeLock.Unlock() |
| 364 | 364 |
|
| 365 |
- // Move the dead nodes |
|
| 366 |
- deadIdx := moveDeadNodes(m.nodes) |
|
| 365 |
+ // Move dead nodes, but respect gossip to the dead interval |
|
| 366 |
+ deadIdx := moveDeadNodes(m.nodes, m.config.GossipToTheDeadTime) |
|
| 367 | 367 |
|
| 368 | 368 |
// Deregister the dead nodes |
| 369 | 369 |
for i := deadIdx; i < len(m.nodes); i++ {
|
| ... | ... |
@@ -386,14 +473,28 @@ func (m *Memberlist) resetNodes() {
|
| 386 | 386 |
func (m *Memberlist) gossip() {
|
| 387 | 387 |
defer metrics.MeasureSince([]string{"memberlist", "gossip"}, time.Now())
|
| 388 | 388 |
|
| 389 |
- // Get some random live nodes |
|
| 389 |
+ // Get some random live, suspect, or recently dead nodes |
|
| 390 | 390 |
m.nodeLock.RLock() |
| 391 |
- excludes := []string{m.config.Name}
|
|
| 392 |
- kNodes := kRandomNodes(m.config.GossipNodes, excludes, m.nodes) |
|
| 391 |
+ kNodes := kRandomNodes(m.config.GossipNodes, m.nodes, func(n *nodeState) bool {
|
|
| 392 |
+ if n.Name == m.config.Name {
|
|
| 393 |
+ return true |
|
| 394 |
+ } |
|
| 395 |
+ |
|
| 396 |
+ switch n.State {
|
|
| 397 |
+ case stateAlive, stateSuspect: |
|
| 398 |
+ return false |
|
| 399 |
+ |
|
| 400 |
+ case stateDead: |
|
| 401 |
+ return time.Since(n.StateChange) > m.config.GossipToTheDeadTime |
|
| 402 |
+ |
|
| 403 |
+ default: |
|
| 404 |
+ return true |
|
| 405 |
+ } |
|
| 406 |
+ }) |
|
| 393 | 407 |
m.nodeLock.RUnlock() |
| 394 | 408 |
|
| 395 | 409 |
// Compute the bytes available |
| 396 |
- bytesAvail := udpSendBuf - compoundHeaderOverhead |
|
| 410 |
+ bytesAvail := m.config.UDPBufferSize - compoundHeaderOverhead |
|
| 397 | 411 |
if m.config.EncryptionEnabled() {
|
| 398 | 412 |
bytesAvail -= encryptOverhead(m.encryptionVersion()) |
| 399 | 413 |
} |
| ... | ... |
@@ -405,13 +506,18 @@ func (m *Memberlist) gossip() {
|
| 405 | 405 |
return |
| 406 | 406 |
} |
| 407 | 407 |
|
| 408 |
- // Create a compound message |
|
| 409 |
- compound := makeCompoundMessage(msgs) |
|
| 410 |
- |
|
| 411 |
- // Send the compound message |
|
| 412 |
- destAddr := &net.UDPAddr{IP: node.Addr, Port: int(node.Port)}
|
|
| 413 |
- if err := m.rawSendMsgUDP(destAddr, compound.Bytes()); err != nil {
|
|
| 414 |
- m.logger.Printf("[ERR] memberlist: Failed to send gossip to %s: %s", destAddr, err)
|
|
| 408 |
+ addr := node.Address() |
|
| 409 |
+ if len(msgs) == 1 {
|
|
| 410 |
+ // Send single message as is |
|
| 411 |
+ if err := m.rawSendMsgPacket(addr, &node.Node, msgs[0]); err != nil {
|
|
| 412 |
+ m.logger.Printf("[ERR] memberlist: Failed to send gossip to %s: %s", addr, err)
|
|
| 413 |
+ } |
|
| 414 |
+ } else {
|
|
| 415 |
+ // Otherwise create and send a compound message |
|
| 416 |
+ compound := makeCompoundMessage(msgs) |
|
| 417 |
+ if err := m.rawSendMsgPacket(addr, &node.Node, compound.Bytes()); err != nil {
|
|
| 418 |
+ m.logger.Printf("[ERR] memberlist: Failed to send gossip to %s: %s", addr, err)
|
|
| 419 |
+ } |
|
| 415 | 420 |
} |
| 416 | 421 |
} |
| 417 | 422 |
} |
| ... | ... |
@@ -423,8 +529,10 @@ func (m *Memberlist) gossip() {
|
| 423 | 423 |
func (m *Memberlist) pushPull() {
|
| 424 | 424 |
// Get a random live node |
| 425 | 425 |
m.nodeLock.RLock() |
| 426 |
- excludes := []string{m.config.Name}
|
|
| 427 |
- nodes := kRandomNodes(1, excludes, m.nodes) |
|
| 426 |
+ nodes := kRandomNodes(1, m.nodes, func(n *nodeState) bool {
|
|
| 427 |
+ return n.Name == m.config.Name || |
|
| 428 |
+ n.State != stateAlive |
|
| 429 |
+ }) |
|
| 428 | 430 |
m.nodeLock.RUnlock() |
| 429 | 431 |
|
| 430 | 432 |
// If no nodes, bail |
| ... | ... |
@@ -434,17 +542,17 @@ func (m *Memberlist) pushPull() {
|
| 434 | 434 |
node := nodes[0] |
| 435 | 435 |
|
| 436 | 436 |
// Attempt a push pull |
| 437 |
- if err := m.pushPullNode(node.Addr, node.Port, false); err != nil {
|
|
| 437 |
+ if err := m.pushPullNode(node.Address(), false); err != nil {
|
|
| 438 | 438 |
m.logger.Printf("[ERR] memberlist: Push/Pull with %s failed: %s", node.Name, err)
|
| 439 | 439 |
} |
| 440 | 440 |
} |
| 441 | 441 |
|
| 442 | 442 |
// pushPullNode does a complete state exchange with a specific node. |
| 443 |
-func (m *Memberlist) pushPullNode(addr []byte, port uint16, join bool) error {
|
|
| 443 |
+func (m *Memberlist) pushPullNode(addr string, join bool) error {
|
|
| 444 | 444 |
defer metrics.MeasureSince([]string{"memberlist", "pushPullNode"}, time.Now())
|
| 445 | 445 |
|
| 446 | 446 |
// Attempt to send and receive with the node |
| 447 |
- remote, userState, err := m.sendAndReceiveState(addr, port, join) |
|
| 447 |
+ remote, userState, err := m.sendAndReceiveState(addr, join) |
|
| 448 | 448 |
if err != nil {
|
| 449 | 449 |
return err |
| 450 | 450 |
} |
| ... | ... |
@@ -584,6 +692,11 @@ func (m *Memberlist) nextIncarnation() uint32 {
|
| 584 | 584 |
return atomic.AddUint32(&m.incarnation, 1) |
| 585 | 585 |
} |
| 586 | 586 |
|
| 587 |
+// skipIncarnation adds the positive offset to the incarnation number. |
|
| 588 |
+func (m *Memberlist) skipIncarnation(offset uint32) uint32 {
|
|
| 589 |
+ return atomic.AddUint32(&m.incarnation, offset) |
|
| 590 |
+} |
|
| 591 |
+ |
|
| 587 | 592 |
// estNumNodes is used to get the current estimate of the number of nodes |
| 588 | 593 |
func (m *Memberlist) estNumNodes() int {
|
| 589 | 594 |
return int(atomic.LoadUint32(&m.numNodes)) |
| ... | ... |
@@ -595,19 +708,27 @@ type ackMessage struct {
|
| 595 | 595 |
Timestamp time.Time |
| 596 | 596 |
} |
| 597 | 597 |
|
| 598 |
-// setAckChannel is used to attach a channel to receive a message when an ack with a given |
|
| 599 |
-// sequence number is received. The `complete` field of the message will be false on timeout |
|
| 600 |
-func (m *Memberlist) setAckChannel(seqNo uint32, ch chan ackMessage, timeout time.Duration) {
|
|
| 601 |
- // Create a handler function |
|
| 602 |
- handler := func(payload []byte, timestamp time.Time) {
|
|
| 598 |
+// setProbeChannels is used to attach the ackCh to receive a message when an ack |
|
| 599 |
+// with a given sequence number is received. The `complete` field of the message |
|
| 600 |
+// will be false on timeout. Any nack messages will cause an empty struct to be |
|
| 601 |
+// passed to the nackCh, which can be nil if not needed. |
|
| 602 |
+func (m *Memberlist) setProbeChannels(seqNo uint32, ackCh chan ackMessage, nackCh chan struct{}, timeout time.Duration) {
|
|
| 603 |
+ // Create handler functions for acks and nacks |
|
| 604 |
+ ackFn := func(payload []byte, timestamp time.Time) {
|
|
| 605 |
+ select {
|
|
| 606 |
+ case ackCh <- ackMessage{true, payload, timestamp}:
|
|
| 607 |
+ default: |
|
| 608 |
+ } |
|
| 609 |
+ } |
|
| 610 |
+ nackFn := func() {
|
|
| 603 | 611 |
select {
|
| 604 |
- case ch <- ackMessage{true, payload, timestamp}:
|
|
| 612 |
+ case nackCh <- struct{}{}:
|
|
| 605 | 613 |
default: |
| 606 | 614 |
} |
| 607 | 615 |
} |
| 608 | 616 |
|
| 609 |
- // Add the handler |
|
| 610 |
- ah := &ackHandler{handler, nil}
|
|
| 617 |
+ // Add the handlers |
|
| 618 |
+ ah := &ackHandler{ackFn, nackFn, nil}
|
|
| 611 | 619 |
m.ackLock.Lock() |
| 612 | 620 |
m.ackHandlers[seqNo] = ah |
| 613 | 621 |
m.ackLock.Unlock() |
| ... | ... |
@@ -618,18 +739,19 @@ func (m *Memberlist) setAckChannel(seqNo uint32, ch chan ackMessage, timeout tim |
| 618 | 618 |
delete(m.ackHandlers, seqNo) |
| 619 | 619 |
m.ackLock.Unlock() |
| 620 | 620 |
select {
|
| 621 |
- case ch <- ackMessage{false, nil, time.Now()}:
|
|
| 621 |
+ case ackCh <- ackMessage{false, nil, time.Now()}:
|
|
| 622 | 622 |
default: |
| 623 | 623 |
} |
| 624 | 624 |
}) |
| 625 | 625 |
} |
| 626 | 626 |
|
| 627 |
-// setAckHandler is used to attach a handler to be invoked when an |
|
| 628 |
-// ack with a given sequence number is received. If a timeout is reached, |
|
| 629 |
-// the handler is deleted |
|
| 630 |
-func (m *Memberlist) setAckHandler(seqNo uint32, handler func([]byte, time.Time), timeout time.Duration) {
|
|
| 627 |
+// setAckHandler is used to attach a handler to be invoked when an ack with a |
|
| 628 |
+// given sequence number is received. If a timeout is reached, the handler is |
|
| 629 |
+// deleted. This is used for indirect pings so does not configure a function |
|
| 630 |
+// for nacks. |
|
| 631 |
+func (m *Memberlist) setAckHandler(seqNo uint32, ackFn func([]byte, time.Time), timeout time.Duration) {
|
|
| 631 | 632 |
// Add the handler |
| 632 |
- ah := &ackHandler{handler, nil}
|
|
| 633 |
+ ah := &ackHandler{ackFn, nil, nil}
|
|
| 633 | 634 |
m.ackLock.Lock() |
| 634 | 635 |
m.ackHandlers[seqNo] = ah |
| 635 | 636 |
m.ackLock.Unlock() |
| ... | ... |
@@ -642,7 +764,7 @@ func (m *Memberlist) setAckHandler(seqNo uint32, handler func([]byte, time.Time) |
| 642 | 642 |
}) |
| 643 | 643 |
} |
| 644 | 644 |
|
| 645 |
-// Invokes an Ack handler if any is associated, and reaps the handler immediately |
|
| 645 |
+// Invokes an ack handler if any is associated, and reaps the handler immediately |
|
| 646 | 646 |
func (m *Memberlist) invokeAckHandler(ack ackResp, timestamp time.Time) {
|
| 647 | 647 |
m.ackLock.Lock() |
| 648 | 648 |
ah, ok := m.ackHandlers[ack.SeqNo] |
| ... | ... |
@@ -652,7 +774,49 @@ func (m *Memberlist) invokeAckHandler(ack ackResp, timestamp time.Time) {
|
| 652 | 652 |
return |
| 653 | 653 |
} |
| 654 | 654 |
ah.timer.Stop() |
| 655 |
- ah.handler(ack.Payload, timestamp) |
|
| 655 |
+ ah.ackFn(ack.Payload, timestamp) |
|
| 656 |
+} |
|
| 657 |
+ |
|
| 658 |
+// Invokes nack handler if any is associated. |
|
| 659 |
+func (m *Memberlist) invokeNackHandler(nack nackResp) {
|
|
| 660 |
+ m.ackLock.Lock() |
|
| 661 |
+ ah, ok := m.ackHandlers[nack.SeqNo] |
|
| 662 |
+ m.ackLock.Unlock() |
|
| 663 |
+ if !ok || ah.nackFn == nil {
|
|
| 664 |
+ return |
|
| 665 |
+ } |
|
| 666 |
+ ah.nackFn() |
|
| 667 |
+} |
|
| 668 |
+ |
|
| 669 |
+// refute gossips an alive message in response to incoming information that we |
|
| 670 |
+// are suspect or dead. It will make sure the incarnation number beats the given |
|
| 671 |
+// accusedInc value, or you can supply 0 to just get the next incarnation number. |
|
| 672 |
+// This alters the node state that's passed in so this MUST be called while the |
|
| 673 |
+// nodeLock is held. |
|
| 674 |
+func (m *Memberlist) refute(me *nodeState, accusedInc uint32) {
|
|
| 675 |
+ // Make sure the incarnation number beats the accusation. |
|
| 676 |
+ inc := m.nextIncarnation() |
|
| 677 |
+ if accusedInc >= inc {
|
|
| 678 |
+ inc = m.skipIncarnation(accusedInc - inc + 1) |
|
| 679 |
+ } |
|
| 680 |
+ me.Incarnation = inc |
|
| 681 |
+ |
|
| 682 |
+ // Decrease our health because we are being asked to refute a problem. |
|
| 683 |
+ m.awareness.ApplyDelta(1) |
|
| 684 |
+ |
|
| 685 |
+ // Format and broadcast an alive message. |
|
| 686 |
+ a := alive{
|
|
| 687 |
+ Incarnation: inc, |
|
| 688 |
+ Node: me.Name, |
|
| 689 |
+ Addr: me.Addr, |
|
| 690 |
+ Port: me.Port, |
|
| 691 |
+ Meta: me.Meta, |
|
| 692 |
+ Vsn: []uint8{
|
|
| 693 |
+ me.PMin, me.PMax, me.PCur, |
|
| 694 |
+ me.DMin, me.DMax, me.DCur, |
|
| 695 |
+ }, |
|
| 696 |
+ } |
|
| 697 |
+ m.encodeAndBroadcast(me.Addr.String(), aliveMsg, a) |
|
| 656 | 698 |
} |
| 657 | 699 |
|
| 658 | 700 |
// 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) {
|
| 754 | 754 |
return |
| 755 | 755 |
} |
| 756 | 756 |
|
| 757 |
+ // Clear out any suspicion timer that may be in effect. |
|
| 758 |
+ delete(m.nodeTimers, a.Node) |
|
| 759 |
+ |
|
| 757 | 760 |
// Store the old state and meta data |
| 758 | 761 |
oldState := state.State |
| 759 | 762 |
oldMeta := state.Meta |
| ... | ... |
@@ -783,21 +950,7 @@ func (m *Memberlist) aliveNode(a *alive, notify chan struct{}, bootstrap bool) {
|
| 783 | 783 |
return |
| 784 | 784 |
} |
| 785 | 785 |
|
| 786 |
- inc := m.nextIncarnation() |
|
| 787 |
- for a.Incarnation >= inc {
|
|
| 788 |
- inc = m.nextIncarnation() |
|
| 789 |
- } |
|
| 790 |
- state.Incarnation = inc |
|
| 791 |
- |
|
| 792 |
- a := alive{
|
|
| 793 |
- Incarnation: inc, |
|
| 794 |
- Node: state.Name, |
|
| 795 |
- Addr: state.Addr, |
|
| 796 |
- Port: state.Port, |
|
| 797 |
- Meta: state.Meta, |
|
| 798 |
- Vsn: versions, |
|
| 799 |
- } |
|
| 800 |
- m.encodeBroadcastNotify(a.Node, aliveMsg, a, notify) |
|
| 786 |
+ m.refute(state, a.Incarnation) |
|
| 801 | 787 |
m.logger.Printf("[WARN] memberlist: Refuting an alive message")
|
| 802 | 788 |
} else {
|
| 803 | 789 |
m.encodeBroadcastNotify(a.Node, aliveMsg, a, notify) |
| ... | ... |
@@ -854,6 +1007,17 @@ func (m *Memberlist) suspectNode(s *suspect) {
|
| 854 | 854 |
return |
| 855 | 855 |
} |
| 856 | 856 |
|
| 857 |
+ // See if there's a suspicion timer we can confirm. If the info is new |
|
| 858 |
+ // to us we will go ahead and re-gossip it. This allows for multiple |
|
| 859 |
+ // independent confirmations to flow even when a node probes a node |
|
| 860 |
+ // that's already suspect. |
|
| 861 |
+ if timer, ok := m.nodeTimers[s.Node]; ok {
|
|
| 862 |
+ if timer.Confirm(s.From) {
|
|
| 863 |
+ m.encodeAndBroadcast(s.Node, suspectMsg, s) |
|
| 864 |
+ } |
|
| 865 |
+ return |
|
| 866 |
+ } |
|
| 867 |
+ |
|
| 857 | 868 |
// Ignore non-alive nodes |
| 858 | 869 |
if state.State != stateAlive {
|
| 859 | 870 |
return |
| ... | ... |
@@ -861,24 +1025,7 @@ func (m *Memberlist) suspectNode(s *suspect) {
|
| 861 | 861 |
|
| 862 | 862 |
// If this is us we need to refute, otherwise re-broadcast |
| 863 | 863 |
if state.Name == m.config.Name {
|
| 864 |
- inc := m.nextIncarnation() |
|
| 865 |
- for s.Incarnation >= inc {
|
|
| 866 |
- inc = m.nextIncarnation() |
|
| 867 |
- } |
|
| 868 |
- state.Incarnation = inc |
|
| 869 |
- |
|
| 870 |
- a := alive{
|
|
| 871 |
- Incarnation: inc, |
|
| 872 |
- Node: state.Name, |
|
| 873 |
- Addr: state.Addr, |
|
| 874 |
- Port: state.Port, |
|
| 875 |
- Meta: state.Meta, |
|
| 876 |
- Vsn: []uint8{
|
|
| 877 |
- state.PMin, state.PMax, state.PCur, |
|
| 878 |
- state.DMin, state.DMax, state.DCur, |
|
| 879 |
- }, |
|
| 880 |
- } |
|
| 881 |
- m.encodeAndBroadcast(s.Node, aliveMsg, a) |
|
| 864 |
+ m.refute(state, s.Incarnation) |
|
| 882 | 865 |
m.logger.Printf("[WARN] memberlist: Refuting a suspect message (from: %s)", s.From)
|
| 883 | 866 |
return // Do not mark ourself suspect |
| 884 | 867 |
} else {
|
| ... | ... |
@@ -894,26 +1041,41 @@ func (m *Memberlist) suspectNode(s *suspect) {
|
| 894 | 894 |
changeTime := time.Now() |
| 895 | 895 |
state.StateChange = changeTime |
| 896 | 896 |
|
| 897 |
- // Setup a timeout for this |
|
| 898 |
- timeout := suspicionTimeout(m.config.SuspicionMult, m.estNumNodes(), m.config.ProbeInterval) |
|
| 899 |
- time.AfterFunc(timeout, func() {
|
|
| 897 |
+ // Setup a suspicion timer. Given that we don't have any known phase |
|
| 898 |
+ // relationship with our peers, we set up k such that we hit the nominal |
|
| 899 |
+ // timeout two probe intervals short of what we expect given the suspicion |
|
| 900 |
+ // multiplier. |
|
| 901 |
+ k := m.config.SuspicionMult - 2 |
|
| 902 |
+ |
|
| 903 |
+ // If there aren't enough nodes to give the expected confirmations, just |
|
| 904 |
+ // set k to 0 to say that we don't expect any. Note we subtract 2 from n |
|
| 905 |
+ // here to take out ourselves and the node being probed. |
|
| 906 |
+ n := m.estNumNodes() |
|
| 907 |
+ if n-2 < k {
|
|
| 908 |
+ k = 0 |
|
| 909 |
+ } |
|
| 910 |
+ |
|
| 911 |
+ // Compute the timeouts based on the size of the cluster. |
|
| 912 |
+ min := suspicionTimeout(m.config.SuspicionMult, n, m.config.ProbeInterval) |
|
| 913 |
+ max := time.Duration(m.config.SuspicionMaxTimeoutMult) * min |
|
| 914 |
+ fn := func(numConfirmations int) {
|
|
| 900 | 915 |
m.nodeLock.Lock() |
| 901 | 916 |
state, ok := m.nodeMap[s.Node] |
| 902 | 917 |
timeout := ok && state.State == stateSuspect && state.StateChange == changeTime |
| 903 | 918 |
m.nodeLock.Unlock() |
| 904 | 919 |
|
| 905 | 920 |
if timeout {
|
| 906 |
- m.suspectTimeout(state) |
|
| 907 |
- } |
|
| 908 |
- }) |
|
| 909 |
-} |
|
| 921 |
+ if k > 0 && numConfirmations < k {
|
|
| 922 |
+ metrics.IncrCounter([]string{"memberlist", "degraded", "timeout"}, 1)
|
|
| 923 |
+ } |
|
| 910 | 924 |
|
| 911 |
-// suspectTimeout is invoked when a suspect timeout has occurred |
|
| 912 |
-func (m *Memberlist) suspectTimeout(n *nodeState) {
|
|
| 913 |
- // Construct a dead message |
|
| 914 |
- m.logger.Printf("[INFO] memberlist: Marking %s as failed, suspect timeout reached", n.Name)
|
|
| 915 |
- d := dead{Incarnation: n.Incarnation, Node: n.Name, From: m.config.Name}
|
|
| 916 |
- m.deadNode(&d) |
|
| 925 |
+ m.logger.Printf("[INFO] memberlist: Marking %s as failed, suspect timeout reached (%d peer confirmations)",
|
|
| 926 |
+ state.Name, numConfirmations) |
|
| 927 |
+ d := dead{Incarnation: state.Incarnation, Node: state.Name, From: m.config.Name}
|
|
| 928 |
+ m.deadNode(&d) |
|
| 929 |
+ } |
|
| 930 |
+ } |
|
| 931 |
+ m.nodeTimers[s.Node] = newSuspicion(s.From, k, min, max, fn) |
|
| 917 | 932 |
} |
| 918 | 933 |
|
| 919 | 934 |
// deadNode is invoked by the network layer when we get a message |
| ... | ... |
@@ -933,6 +1095,9 @@ func (m *Memberlist) deadNode(d *dead) {
|
| 933 | 933 |
return |
| 934 | 934 |
} |
| 935 | 935 |
|
| 936 |
+ // Clear out any suspicion timer that may be in effect. |
|
| 937 |
+ delete(m.nodeTimers, d.Node) |
|
| 938 |
+ |
|
| 936 | 939 |
// Ignore if node is already dead |
| 937 | 940 |
if state.State == stateDead {
|
| 938 | 941 |
return |
| ... | ... |
@@ -942,24 +1107,7 @@ func (m *Memberlist) deadNode(d *dead) {
|
| 942 | 942 |
if state.Name == m.config.Name {
|
| 943 | 943 |
// If we are not leaving we need to refute |
| 944 | 944 |
if !m.leave {
|
| 945 |
- inc := m.nextIncarnation() |
|
| 946 |
- for d.Incarnation >= inc {
|
|
| 947 |
- inc = m.nextIncarnation() |
|
| 948 |
- } |
|
| 949 |
- state.Incarnation = inc |
|
| 950 |
- |
|
| 951 |
- a := alive{
|
|
| 952 |
- Incarnation: inc, |
|
| 953 |
- Node: state.Name, |
|
| 954 |
- Addr: state.Addr, |
|
| 955 |
- Port: state.Port, |
|
| 956 |
- Meta: state.Meta, |
|
| 957 |
- Vsn: []uint8{
|
|
| 958 |
- state.PMin, state.PMax, state.PCur, |
|
| 959 |
- state.DMin, state.DMax, state.DCur, |
|
| 960 |
- }, |
|
| 961 |
- } |
|
| 962 |
- m.encodeAndBroadcast(d.Node, aliveMsg, a) |
|
| 945 |
+ m.refute(state, d.Incarnation) |
|
| 963 | 946 |
m.logger.Printf("[WARN] memberlist: Refuting a dead message (from: %s)", d.From)
|
| 964 | 947 |
return // Do not mark ourself dead |
| 965 | 948 |
} |
| ... | ... |
@@ -1001,7 +1149,7 @@ func (m *Memberlist) mergeState(remote []pushNodeState) {
|
| 1001 | 1001 |
m.aliveNode(&a, nil, false) |
| 1002 | 1002 |
|
| 1003 | 1003 |
case stateDead: |
| 1004 |
- // If the remote node belives a node is dead, we prefer to |
|
| 1004 |
+ // If the remote node believes a node is dead, we prefer to |
|
| 1005 | 1005 |
// suspect that node instead of declaring it dead instantly |
| 1006 | 1006 |
fallthrough |
| 1007 | 1007 |
case stateSuspect: |
| 1008 | 1008 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,130 @@ |
| 0 |
+package memberlist |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "math" |
|
| 4 |
+ "sync/atomic" |
|
| 5 |
+ "time" |
|
| 6 |
+) |
|
| 7 |
+ |
|
| 8 |
+// suspicion manages the suspect timer for a node and provides an interface |
|
| 9 |
+// to accelerate the timeout as we get more independent confirmations that |
|
| 10 |
+// a node is suspect. |
|
| 11 |
+type suspicion struct {
|
|
| 12 |
+ // n is the number of independent confirmations we've seen. This must |
|
| 13 |
+ // be updated using atomic instructions to prevent contention with the |
|
| 14 |
+ // timer callback. |
|
| 15 |
+ n int32 |
|
| 16 |
+ |
|
| 17 |
+ // k is the number of independent confirmations we'd like to see in |
|
| 18 |
+ // order to drive the timer to its minimum value. |
|
| 19 |
+ k int32 |
|
| 20 |
+ |
|
| 21 |
+ // min is the minimum timer value. |
|
| 22 |
+ min time.Duration |
|
| 23 |
+ |
|
| 24 |
+ // max is the maximum timer value. |
|
| 25 |
+ max time.Duration |
|
| 26 |
+ |
|
| 27 |
+ // start captures the timestamp when we began the timer. This is used |
|
| 28 |
+ // so we can calculate durations to feed the timer during updates in |
|
| 29 |
+ // a way the achieves the overall time we'd like. |
|
| 30 |
+ start time.Time |
|
| 31 |
+ |
|
| 32 |
+ // timer is the underlying timer that implements the timeout. |
|
| 33 |
+ timer *time.Timer |
|
| 34 |
+ |
|
| 35 |
+ // f is the function to call when the timer expires. We hold on to this |
|
| 36 |
+ // because there are cases where we call it directly. |
|
| 37 |
+ timeoutFn func() |
|
| 38 |
+ |
|
| 39 |
+ // confirmations is a map of "from" nodes that have confirmed a given |
|
| 40 |
+ // node is suspect. This prevents double counting. |
|
| 41 |
+ confirmations map[string]struct{}
|
|
| 42 |
+} |
|
| 43 |
+ |
|
| 44 |
+// newSuspicion returns a timer started with the max time, and that will drive |
|
| 45 |
+// to the min time after seeing k or more confirmations. The from node will be |
|
| 46 |
+// excluded from confirmations since we might get our own suspicion message |
|
| 47 |
+// gossiped back to us. The minimum time will be used if no confirmations are |
|
| 48 |
+// called for (k <= 0). |
|
| 49 |
+func newSuspicion(from string, k int, min time.Duration, max time.Duration, fn func(int)) *suspicion {
|
|
| 50 |
+ s := &suspicion{
|
|
| 51 |
+ k: int32(k), |
|
| 52 |
+ min: min, |
|
| 53 |
+ max: max, |
|
| 54 |
+ confirmations: make(map[string]struct{}),
|
|
| 55 |
+ } |
|
| 56 |
+ |
|
| 57 |
+ // Exclude the from node from any confirmations. |
|
| 58 |
+ s.confirmations[from] = struct{}{}
|
|
| 59 |
+ |
|
| 60 |
+ // Pass the number of confirmations into the timeout function for |
|
| 61 |
+ // easy telemetry. |
|
| 62 |
+ s.timeoutFn = func() {
|
|
| 63 |
+ fn(int(atomic.LoadInt32(&s.n))) |
|
| 64 |
+ } |
|
| 65 |
+ |
|
| 66 |
+ // If there aren't any confirmations to be made then take the min |
|
| 67 |
+ // time from the start. |
|
| 68 |
+ timeout := max |
|
| 69 |
+ if k < 1 {
|
|
| 70 |
+ timeout = min |
|
| 71 |
+ } |
|
| 72 |
+ s.timer = time.AfterFunc(timeout, s.timeoutFn) |
|
| 73 |
+ |
|
| 74 |
+ // Capture the start time right after starting the timer above so |
|
| 75 |
+ // we should always err on the side of a little longer timeout if |
|
| 76 |
+ // there's any preemption that separates this and the step above. |
|
| 77 |
+ s.start = time.Now() |
|
| 78 |
+ return s |
|
| 79 |
+} |
|
| 80 |
+ |
|
| 81 |
+// remainingSuspicionTime takes the state variables of the suspicion timer and |
|
| 82 |
+// calculates the remaining time to wait before considering a node dead. The |
|
| 83 |
+// return value can be negative, so be prepared to fire the timer immediately in |
|
| 84 |
+// that case. |
|
| 85 |
+func remainingSuspicionTime(n, k int32, elapsed time.Duration, min, max time.Duration) time.Duration {
|
|
| 86 |
+ frac := math.Log(float64(n)+1.0) / math.Log(float64(k)+1.0) |
|
| 87 |
+ raw := max.Seconds() - frac*(max.Seconds()-min.Seconds()) |
|
| 88 |
+ timeout := time.Duration(math.Floor(1000.0*raw)) * time.Millisecond |
|
| 89 |
+ if timeout < min {
|
|
| 90 |
+ timeout = min |
|
| 91 |
+ } |
|
| 92 |
+ |
|
| 93 |
+ // We have to take into account the amount of time that has passed so |
|
| 94 |
+ // far, so we get the right overall timeout. |
|
| 95 |
+ return timeout - elapsed |
|
| 96 |
+} |
|
| 97 |
+ |
|
| 98 |
+// Confirm registers that a possibly new peer has also determined the given |
|
| 99 |
+// node is suspect. This returns true if this was new information, and false |
|
| 100 |
+// if it was a duplicate confirmation, or if we've got enough confirmations to |
|
| 101 |
+// hit the minimum. |
|
| 102 |
+func (s *suspicion) Confirm(from string) bool {
|
|
| 103 |
+ // If we've got enough confirmations then stop accepting them. |
|
| 104 |
+ if atomic.LoadInt32(&s.n) >= s.k {
|
|
| 105 |
+ return false |
|
| 106 |
+ } |
|
| 107 |
+ |
|
| 108 |
+ // Only allow one confirmation from each possible peer. |
|
| 109 |
+ if _, ok := s.confirmations[from]; ok {
|
|
| 110 |
+ return false |
|
| 111 |
+ } |
|
| 112 |
+ s.confirmations[from] = struct{}{}
|
|
| 113 |
+ |
|
| 114 |
+ // Compute the new timeout given the current number of confirmations and |
|
| 115 |
+ // adjust the timer. If the timeout becomes negative *and* we can cleanly |
|
| 116 |
+ // stop the timer then we will call the timeout function directly from |
|
| 117 |
+ // here. |
|
| 118 |
+ n := atomic.AddInt32(&s.n, 1) |
|
| 119 |
+ elapsed := time.Now().Sub(s.start) |
|
| 120 |
+ remaining := remainingSuspicionTime(n, s.k, elapsed, s.min, s.max) |
|
| 121 |
+ if s.timer.Stop() {
|
|
| 122 |
+ if remaining > 0 {
|
|
| 123 |
+ s.timer.Reset(remaining) |
|
| 124 |
+ } else {
|
|
| 125 |
+ go s.timeoutFn() |
|
| 126 |
+ } |
|
| 127 |
+ } |
|
| 128 |
+ return true |
|
| 129 |
+} |
| 0 | 130 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,65 @@ |
| 0 |
+package memberlist |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "net" |
|
| 4 |
+ "time" |
|
| 5 |
+) |
|
| 6 |
+ |
|
| 7 |
+// Packet is used to provide some metadata about incoming packets from peers |
|
| 8 |
+// over a packet connection, as well as the packet payload. |
|
| 9 |
+type Packet struct {
|
|
| 10 |
+ // Buf has the raw contents of the packet. |
|
| 11 |
+ Buf []byte |
|
| 12 |
+ |
|
| 13 |
+ // From has the address of the peer. This is an actual net.Addr so we |
|
| 14 |
+ // can expose some concrete details about incoming packets. |
|
| 15 |
+ From net.Addr |
|
| 16 |
+ |
|
| 17 |
+ // Timestamp is the time when the packet was received. This should be |
|
| 18 |
+ // taken as close as possible to the actual receipt time to help make an |
|
| 19 |
+ // accurate RTT measurements during probes. |
|
| 20 |
+ Timestamp time.Time |
|
| 21 |
+} |
|
| 22 |
+ |
|
| 23 |
+// Transport is used to abstract over communicating with other peers. The packet |
|
| 24 |
+// interface is assumed to be best-effort and the stream interface is assumed to |
|
| 25 |
+// be reliable. |
|
| 26 |
+type Transport interface {
|
|
| 27 |
+ // FinalAdvertiseAddr is given the user's configured values (which |
|
| 28 |
+ // might be empty) and returns the desired IP and port to advertise to |
|
| 29 |
+ // the rest of the cluster. |
|
| 30 |
+ FinalAdvertiseAddr(ip string, port int) (net.IP, int, error) |
|
| 31 |
+ |
|
| 32 |
+ // WriteTo is a packet-oriented interface that fires off the given |
|
| 33 |
+ // payload to the given address in a connectionless fashion. This should |
|
| 34 |
+ // return a time stamp that's as close as possible to when the packet |
|
| 35 |
+ // was transmitted to help make accurate RTT measurements during probes. |
|
| 36 |
+ // |
|
| 37 |
+ // This is similar to net.PacketConn, though we didn't want to expose |
|
| 38 |
+ // that full set of required methods to keep assumptions about the |
|
| 39 |
+ // underlying plumbing to a minimum. We also treat the address here as a |
|
| 40 |
+ // string, similar to Dial, so it's network neutral, so this usually is |
|
| 41 |
+ // in the form of "host:port". |
|
| 42 |
+ WriteTo(b []byte, addr string) (time.Time, error) |
|
| 43 |
+ |
|
| 44 |
+ // PacketCh returns a channel that can be read to receive incoming |
|
| 45 |
+ // packets from other peers. How this is set up for listening is left as |
|
| 46 |
+ // an exercise for the concrete transport implementations. |
|
| 47 |
+ PacketCh() <-chan *Packet |
|
| 48 |
+ |
|
| 49 |
+ // DialTimeout is used to create a connection that allows us to perform |
|
| 50 |
+ // two-way communication with a peer. This is generally more expensive |
|
| 51 |
+ // than packet connections so is used for more infrequent operations |
|
| 52 |
+ // such as anti-entropy or fallback probes if the packet-oriented probe |
|
| 53 |
+ // failed. |
|
| 54 |
+ DialTimeout(addr string, timeout time.Duration) (net.Conn, error) |
|
| 55 |
+ |
|
| 56 |
+ // StreamCh returns a channel that can be read to handle incoming stream |
|
| 57 |
+ // connections from other peers. How this is set up for listening is |
|
| 58 |
+ // left as an exercise for the concrete transport implementations. |
|
| 59 |
+ StreamCh() <-chan net.Conn |
|
| 60 |
+ |
|
| 61 |
+ // Shutdown is called when memberlist is shutting down; this gives the |
|
| 62 |
+ // transport a chance to clean up any listeners. |
|
| 63 |
+ Shutdown() error |
|
| 64 |
+} |
| ... | ... |
@@ -9,10 +9,12 @@ import ( |
| 9 | 9 |
"math" |
| 10 | 10 |
"math/rand" |
| 11 | 11 |
"net" |
| 12 |
+ "strconv" |
|
| 12 | 13 |
"strings" |
| 13 | 14 |
"time" |
| 14 | 15 |
|
| 15 | 16 |
"github.com/hashicorp/go-msgpack/codec" |
| 17 |
+ "github.com/sean-/seed" |
|
| 16 | 18 |
) |
| 17 | 19 |
|
| 18 | 20 |
// pushPullScale is the minimum number of nodes |
| ... | ... |
@@ -22,72 +24,13 @@ import ( |
| 22 | 22 |
// while the 65th will triple it. |
| 23 | 23 |
const pushPullScaleThreshold = 32 |
| 24 | 24 |
|
| 25 |
-/* |
|
| 26 |
- * Contains an entry for each private block: |
|
| 27 |
- * 10.0.0.0/8 |
|
| 28 |
- * 100.64.0.0/10 |
|
| 29 |
- * 127.0.0.0/8 |
|
| 30 |
- * 169.254.0.0/16 |
|
| 31 |
- * 172.16.0.0/12 |
|
| 32 |
- * 192.168.0.0/16 |
|
| 33 |
- */ |
|
| 34 |
-var privateBlocks []*net.IPNet |
|
| 35 |
- |
|
| 36 |
-var loopbackBlock *net.IPNet |
|
| 37 |
- |
|
| 38 | 25 |
const ( |
| 39 | 26 |
// Constant litWidth 2-8 |
| 40 | 27 |
lzwLitWidth = 8 |
| 41 | 28 |
) |
| 42 | 29 |
|
| 43 | 30 |
func init() {
|
| 44 |
- // Seed the random number generator |
|
| 45 |
- rand.Seed(time.Now().UnixNano()) |
|
| 46 |
- |
|
| 47 |
- // Add each private block |
|
| 48 |
- privateBlocks = make([]*net.IPNet, 6) |
|
| 49 |
- |
|
| 50 |
- _, block, err := net.ParseCIDR("10.0.0.0/8")
|
|
| 51 |
- if err != nil {
|
|
| 52 |
- panic(fmt.Sprintf("Bad cidr. Got %v", err))
|
|
| 53 |
- } |
|
| 54 |
- privateBlocks[0] = block |
|
| 55 |
- |
|
| 56 |
- _, block, err = net.ParseCIDR("100.64.0.0/10")
|
|
| 57 |
- if err != nil {
|
|
| 58 |
- panic(fmt.Sprintf("Bad cidr. Got %v", err))
|
|
| 59 |
- } |
|
| 60 |
- privateBlocks[1] = block |
|
| 61 |
- |
|
| 62 |
- _, block, err = net.ParseCIDR("127.0.0.0/8")
|
|
| 63 |
- if err != nil {
|
|
| 64 |
- panic(fmt.Sprintf("Bad cidr. Got %v", err))
|
|
| 65 |
- } |
|
| 66 |
- privateBlocks[2] = block |
|
| 67 |
- |
|
| 68 |
- _, block, err = net.ParseCIDR("169.254.0.0/16")
|
|
| 69 |
- if err != nil {
|
|
| 70 |
- panic(fmt.Sprintf("Bad cidr. Got %v", err))
|
|
| 71 |
- } |
|
| 72 |
- privateBlocks[3] = block |
|
| 73 |
- |
|
| 74 |
- _, block, err = net.ParseCIDR("172.16.0.0/12")
|
|
| 75 |
- if err != nil {
|
|
| 76 |
- panic(fmt.Sprintf("Bad cidr. Got %v", err))
|
|
| 77 |
- } |
|
| 78 |
- privateBlocks[4] = block |
|
| 79 |
- |
|
| 80 |
- _, block, err = net.ParseCIDR("192.168.0.0/16")
|
|
| 81 |
- if err != nil {
|
|
| 82 |
- panic(fmt.Sprintf("Bad cidr. Got %v", err))
|
|
| 83 |
- } |
|
| 84 |
- privateBlocks[5] = block |
|
| 85 |
- |
|
| 86 |
- _, block, err = net.ParseCIDR("127.0.0.0/8")
|
|
| 87 |
- if err != nil {
|
|
| 88 |
- panic(fmt.Sprintf("Bad cidr. Got %v", err))
|
|
| 89 |
- } |
|
| 90 |
- loopbackBlock = block |
|
| 31 |
+ seed.Init() |
|
| 91 | 32 |
} |
| 92 | 33 |
|
| 93 | 34 |
// Decode reverses the encode operation on a byte slice input |
| ... | ... |
@@ -108,42 +51,6 @@ func encode(msgType messageType, in interface{}) (*bytes.Buffer, error) {
|
| 108 | 108 |
return buf, err |
| 109 | 109 |
} |
| 110 | 110 |
|
| 111 |
-// GetPrivateIP returns the first private IP address found in a list of |
|
| 112 |
-// addresses. |
|
| 113 |
-func GetPrivateIP(addresses []net.Addr) (net.IP, error) {
|
|
| 114 |
- var candidates []net.IP |
|
| 115 |
- |
|
| 116 |
- // Find private IPv4 address |
|
| 117 |
- for _, rawAddr := range addresses {
|
|
| 118 |
- var ip net.IP |
|
| 119 |
- switch addr := rawAddr.(type) {
|
|
| 120 |
- case *net.IPAddr: |
|
| 121 |
- ip = addr.IP |
|
| 122 |
- case *net.IPNet: |
|
| 123 |
- ip = addr.IP |
|
| 124 |
- default: |
|
| 125 |
- continue |
|
| 126 |
- } |
|
| 127 |
- |
|
| 128 |
- if ip.To4() == nil {
|
|
| 129 |
- continue |
|
| 130 |
- } |
|
| 131 |
- if !IsPrivateIP(ip.String()) {
|
|
| 132 |
- continue |
|
| 133 |
- } |
|
| 134 |
- candidates = append(candidates, ip) |
|
| 135 |
- } |
|
| 136 |
- numIps := len(candidates) |
|
| 137 |
- switch numIps {
|
|
| 138 |
- case 0: |
|
| 139 |
- return nil, fmt.Errorf("No private IP address found")
|
|
| 140 |
- case 1: |
|
| 141 |
- return candidates[0], nil |
|
| 142 |
- default: |
|
| 143 |
- return nil, fmt.Errorf("Multiple private IPs found. Please configure one.")
|
|
| 144 |
- } |
|
| 145 |
-} |
|
| 146 |
- |
|
| 147 | 111 |
// Returns a random offset between 0 and n |
| 148 | 112 |
func randomOffset(n int) int {
|
| 149 | 113 |
if n == 0 {
|
| ... | ... |
@@ -155,8 +62,9 @@ func randomOffset(n int) int {
|
| 155 | 155 |
// suspicionTimeout computes the timeout that should be used when |
| 156 | 156 |
// a node is suspected |
| 157 | 157 |
func suspicionTimeout(suspicionMult, n int, interval time.Duration) time.Duration {
|
| 158 |
- nodeScale := math.Ceil(math.Log10(float64(n + 1))) |
|
| 159 |
- timeout := time.Duration(suspicionMult) * time.Duration(nodeScale) * interval |
|
| 158 |
+ nodeScale := math.Max(1.0, math.Log10(math.Max(1.0, float64(n)))) |
|
| 159 |
+ // multiply by 1000 to keep some precision because time.Duration is an int64 type |
|
| 160 |
+ timeout := time.Duration(suspicionMult) * time.Duration(nodeScale*1000) * interval / 1000 |
|
| 160 | 161 |
return timeout |
| 161 | 162 |
} |
| 162 | 163 |
|
| ... | ... |
@@ -189,9 +97,9 @@ func pushPullScale(interval time.Duration, n int) time.Duration {
|
| 189 | 189 |
return time.Duration(multiplier) * interval |
| 190 | 190 |
} |
| 191 | 191 |
|
| 192 |
-// moveDeadNodes moves all the nodes in the dead state |
|
| 193 |
-// to the end of the slice and returns the index of the first dead node. |
|
| 194 |
-func moveDeadNodes(nodes []*nodeState) int {
|
|
| 192 |
+// moveDeadNodes moves nodes that are dead and beyond the gossip to the dead interval |
|
| 193 |
+// to the end of the slice and returns the index of the first moved node. |
|
| 194 |
+func moveDeadNodes(nodes []*nodeState, gossipToTheDeadTime time.Duration) int {
|
|
| 195 | 195 |
numDead := 0 |
| 196 | 196 |
n := len(nodes) |
| 197 | 197 |
for i := 0; i < n-numDead; i++ {
|
| ... | ... |
@@ -199,6 +107,11 @@ func moveDeadNodes(nodes []*nodeState) int {
|
| 199 | 199 |
continue |
| 200 | 200 |
} |
| 201 | 201 |
|
| 202 |
+ // Respect the gossip to the dead interval |
|
| 203 |
+ if time.Since(nodes[i].StateChange) <= gossipToTheDeadTime {
|
|
| 204 |
+ continue |
|
| 205 |
+ } |
|
| 206 |
+ |
|
| 202 | 207 |
// Move this node to the end |
| 203 | 208 |
nodes[i], nodes[n-numDead-1] = nodes[n-numDead-1], nodes[i] |
| 204 | 209 |
numDead++ |
| ... | ... |
@@ -207,9 +120,10 @@ func moveDeadNodes(nodes []*nodeState) int {
|
| 207 | 207 |
return n - numDead |
| 208 | 208 |
} |
| 209 | 209 |
|
| 210 |
-// kRandomNodes is used to select up to k random nodes, excluding a given |
|
| 211 |
-// node and any non-alive nodes. It is possible that less than k nodes are returned. |
|
| 212 |
-func kRandomNodes(k int, excludes []string, nodes []*nodeState) []*nodeState {
|
|
| 210 |
+// kRandomNodes is used to select up to k random nodes, excluding any nodes where |
|
| 211 |
+// the filter function returns true. It is possible that less than k nodes are |
|
| 212 |
+// returned. |
|
| 213 |
+func kRandomNodes(k int, nodes []*nodeState, filterFn func(*nodeState) bool) []*nodeState {
|
|
| 213 | 214 |
n := len(nodes) |
| 214 | 215 |
kNodes := make([]*nodeState, 0, k) |
| 215 | 216 |
OUTER: |
| ... | ... |
@@ -221,16 +135,9 @@ OUTER: |
| 221 | 221 |
idx := randomOffset(n) |
| 222 | 222 |
node := nodes[idx] |
| 223 | 223 |
|
| 224 |
- // Exclude node if match |
|
| 225 |
- for _, exclude := range excludes {
|
|
| 226 |
- if node.Name == exclude {
|
|
| 227 |
- continue OUTER |
|
| 228 |
- } |
|
| 229 |
- } |
|
| 230 |
- |
|
| 231 |
- // Exclude if not alive |
|
| 232 |
- if node.State != stateAlive {
|
|
| 233 |
- continue |
|
| 224 |
+ // Give the filter a shot at it. |
|
| 225 |
+ if filterFn != nil && filterFn(node) {
|
|
| 226 |
+ continue OUTER |
|
| 234 | 227 |
} |
| 235 | 228 |
|
| 236 | 229 |
// Check if we have this node already |
| ... | ... |
@@ -310,27 +217,18 @@ func decodeCompoundMessage(buf []byte) (trunc int, parts [][]byte, err error) {
|
| 310 | 310 |
return |
| 311 | 311 |
} |
| 312 | 312 |
|
| 313 |
-// Returns if the given IP is in a private block |
|
| 314 |
-func IsPrivateIP(ip_str string) bool {
|
|
| 315 |
- ip := net.ParseIP(ip_str) |
|
| 316 |
- for _, priv := range privateBlocks {
|
|
| 317 |
- if priv.Contains(ip) {
|
|
| 318 |
- return true |
|
| 319 |
- } |
|
| 320 |
- } |
|
| 321 |
- return false |
|
| 322 |
-} |
|
| 323 |
- |
|
| 324 |
-// Returns if the given IP is in a loopback block |
|
| 325 |
-func isLoopbackIP(ip_str string) bool {
|
|
| 326 |
- ip := net.ParseIP(ip_str) |
|
| 327 |
- return loopbackBlock.Contains(ip) |
|
| 328 |
-} |
|
| 329 |
- |
|
| 330 |
-// Given a string of the form "host", "host:port", or "[ipv6::address]:port", |
|
| 313 |
+// Given a string of the form "host", "host:port", |
|
| 314 |
+// "ipv6::addr" or "[ipv6::address]:port", |
|
| 331 | 315 |
// return true if the string includes a port. |
| 332 | 316 |
func hasPort(s string) bool {
|
| 333 |
- return strings.LastIndex(s, ":") > strings.LastIndex(s, "]") |
|
| 317 |
+ last := strings.LastIndex(s, ":") |
|
| 318 |
+ if last == -1 {
|
|
| 319 |
+ return false |
|
| 320 |
+ } |
|
| 321 |
+ if s[0] == '[' {
|
|
| 322 |
+ return s[last-1] == ']' |
|
| 323 |
+ } |
|
| 324 |
+ return strings.Index(s, ":") == last |
|
| 334 | 325 |
} |
| 335 | 326 |
|
| 336 | 327 |
// compressPayload takes an opaque input buffer, compresses it |
| ... | ... |
@@ -390,3 +288,9 @@ func decompressBuffer(c *compress) ([]byte, error) {
|
| 390 | 390 |
// Return the uncompressed bytes |
| 391 | 391 |
return b.Bytes(), nil |
| 392 | 392 |
} |
| 393 |
+ |
|
| 394 |
+// joinHostPort returns the host:port form of an address, for use with a |
|
| 395 |
+// transport. |
|
| 396 |
+func joinHostPort(host string, port uint16) string {
|
|
| 397 |
+ return net.JoinHostPort(host, strconv.Itoa(int(port))) |
|
| 398 |
+} |
| 393 | 399 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,54 @@ |
| 0 |
+MIT License |
|
| 1 |
+ |
|
| 2 |
+Copyright (c) 2017 Sean Chittenden |
|
| 3 |
+Copyright (c) 2016 Alex Dadgar |
|
| 4 |
+ |
|
| 5 |
+Permission is hereby granted, free of charge, to any person obtaining a copy |
|
| 6 |
+of this software and associated documentation files (the "Software"), to deal |
|
| 7 |
+in the Software without restriction, including without limitation the rights |
|
| 8 |
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
|
| 9 |
+copies of the Software, and to permit persons to whom the Software is |
|
| 10 |
+furnished to do so, subject to the following conditions: |
|
| 11 |
+ |
|
| 12 |
+The above copyright notice and this permission notice shall be included in all |
|
| 13 |
+copies or substantial portions of the Software. |
|
| 14 |
+ |
|
| 15 |
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
|
| 16 |
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
|
| 17 |
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
|
| 18 |
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
|
| 19 |
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
|
| 20 |
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
|
| 21 |
+SOFTWARE. |
|
| 22 |
+ |
|
| 23 |
+===== |
|
| 24 |
+ |
|
| 25 |
+Bits of Go-lang's `once.Do()` were cribbed and reused here, too. |
|
| 26 |
+ |
|
| 27 |
+Copyright (c) 2009 The Go Authors. All rights reserved. |
|
| 28 |
+ |
|
| 29 |
+Redistribution and use in source and binary forms, with or without |
|
| 30 |
+modification, are permitted provided that the following conditions are |
|
| 31 |
+met: |
|
| 32 |
+ |
|
| 33 |
+ * Redistributions of source code must retain the above copyright |
|
| 34 |
+notice, this list of conditions and the following disclaimer. |
|
| 35 |
+ * Redistributions in binary form must reproduce the above |
|
| 36 |
+copyright notice, this list of conditions and the following disclaimer |
|
| 37 |
+in the documentation and/or other materials provided with the |
|
| 38 |
+distribution. |
|
| 39 |
+ * Neither the name of Google Inc. nor the names of its |
|
| 40 |
+contributors may be used to endorse or promote products derived from |
|
| 41 |
+this software without specific prior written permission. |
|
| 42 |
+ |
|
| 43 |
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
|
| 44 |
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
|
| 45 |
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
|
| 46 |
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
|
| 47 |
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
|
| 48 |
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
|
| 49 |
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
|
| 50 |
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
|
| 51 |
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
|
| 52 |
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
|
| 53 |
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| 0 | 54 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,44 @@ |
| 0 |
+# `seed` - Quickly Seed Go's Random Number Generator |
|
| 1 |
+ |
|
| 2 |
+Boiler-plate to securely [seed](https://en.wikipedia.org/wiki/Random_seed) Go's |
|
| 3 |
+random number generator (if possible). This library isn't anything fancy, it's |
|
| 4 |
+just a canonical way of seeding Go's random number generator. Cribbed from |
|
| 5 |
+[`Nomad`](https://github.com/hashicorp/nomad/commit/f89a993ec6b91636a3384dd568898245fbc273a1) |
|
| 6 |
+before it was moved into |
|
| 7 |
+[`Consul`](https://github.com/hashicorp/consul/commit/d695bcaae6e31ee307c11fdf55bb0bf46ea9fcf4) |
|
| 8 |
+and made into a helper function, and now further modularized to be a super |
|
| 9 |
+lightweight and reusable library. |
|
| 10 |
+ |
|
| 11 |
+Time is better than |
|
| 12 |
+[Go's default seed of `1`](https://golang.org/pkg/math/rand/#Seed), but friends |
|
| 13 |
+don't let friends use time as a seed to a random number generator. Use |
|
| 14 |
+`seed.MustInit()` instead. |
|
| 15 |
+ |
|
| 16 |
+`seed.Init()` is an idempotent and reentrant call that will return an error if |
|
| 17 |
+it can't seed the value the first time it is called. `Init()` is reentrant. |
|
| 18 |
+ |
|
| 19 |
+`seed.MustInit()` is idempotent and reentrant call that will `panic()` if it |
|
| 20 |
+can't seed the value the first time it is called. `MustInit()` is reentrant. |
|
| 21 |
+ |
|
| 22 |
+## Usage |
|
| 23 |
+ |
|
| 24 |
+``` |
|
| 25 |
+package mypackage |
|
| 26 |
+ |
|
| 27 |
+import ( |
|
| 28 |
+ "github.com/sean-/seed" |
|
| 29 |
+) |
|
| 30 |
+ |
|
| 31 |
+// MustInit will panic() if it is unable to set a high-entropy random seed: |
|
| 32 |
+func init() {
|
|
| 33 |
+ seed.MustInit() |
|
| 34 |
+} |
|
| 35 |
+ |
|
| 36 |
+// Or if you want to not panic() and can actually handle this error: |
|
| 37 |
+func init() {
|
|
| 38 |
+ if secure, err := !seed.Init(); !secure {
|
|
| 39 |
+ // Handle the error |
|
| 40 |
+ //panic(fmt.Sprintf("Unable to securely seed Go's RNG: %v", err))
|
|
| 41 |
+ } |
|
| 42 |
+} |
|
| 43 |
+``` |
| 0 | 44 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,84 @@ |
| 0 |
+package seed |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ crand "crypto/rand" |
|
| 4 |
+ "fmt" |
|
| 5 |
+ "math" |
|
| 6 |
+ "math/big" |
|
| 7 |
+ "math/rand" |
|
| 8 |
+ "sync" |
|
| 9 |
+ "sync/atomic" |
|
| 10 |
+ "time" |
|
| 11 |
+) |
|
| 12 |
+ |
|
| 13 |
+var ( |
|
| 14 |
+ m sync.Mutex |
|
| 15 |
+ secure int32 |
|
| 16 |
+ seeded int32 |
|
| 17 |
+) |
|
| 18 |
+ |
|
| 19 |
+func cryptoSeed() error {
|
|
| 20 |
+ defer atomic.StoreInt32(&seeded, 1) |
|
| 21 |
+ |
|
| 22 |
+ var err error |
|
| 23 |
+ var n *big.Int |
|
| 24 |
+ n, err = crand.Int(crand.Reader, big.NewInt(math.MaxInt64)) |
|
| 25 |
+ if err != nil {
|
|
| 26 |
+ rand.Seed(time.Now().UTC().UnixNano()) |
|
| 27 |
+ return err |
|
| 28 |
+ } |
|
| 29 |
+ rand.Seed(n.Int64()) |
|
| 30 |
+ atomic.StoreInt32(&secure, 1) |
|
| 31 |
+ return nil |
|
| 32 |
+} |
|
| 33 |
+ |
|
| 34 |
+// Init provides best-effort seeding (which is better than running with Go's |
|
| 35 |
+// default seed of 1). If `/dev/urandom` is available, Init() will seed Go's |
|
| 36 |
+// runtime with entropy from `/dev/urandom` and return true because the runtime |
|
| 37 |
+// was securely seeded. If Init() has already initialized the random number or |
|
| 38 |
+// it had failed to securely initialize the random number generation, Init() |
|
| 39 |
+// will return false. See MustInit(). |
|
| 40 |
+func Init() (seededSecurely bool, err error) {
|
|
| 41 |
+ if atomic.LoadInt32(&seeded) == 1 {
|
|
| 42 |
+ return false, nil |
|
| 43 |
+ } |
|
| 44 |
+ |
|
| 45 |
+ // Slow-path |
|
| 46 |
+ m.Lock() |
|
| 47 |
+ defer m.Unlock() |
|
| 48 |
+ |
|
| 49 |
+ if err := cryptoSeed(); err != nil {
|
|
| 50 |
+ return false, err |
|
| 51 |
+ } |
|
| 52 |
+ |
|
| 53 |
+ return true, nil |
|
| 54 |
+} |
|
| 55 |
+ |
|
| 56 |
+// MustInit provides guaranteed secure seeding. If `/dev/urandom` is not |
|
| 57 |
+// available, MustInit will panic() with an error indicating why reading from |
|
| 58 |
+// `/dev/urandom` failed. MustInit() will upgrade the seed if for some reason a |
|
| 59 |
+// call to Init() failed in the past. |
|
| 60 |
+func MustInit() {
|
|
| 61 |
+ if atomic.LoadInt32(&secure) == 1 {
|
|
| 62 |
+ return |
|
| 63 |
+ } |
|
| 64 |
+ |
|
| 65 |
+ // Slow-path |
|
| 66 |
+ m.Lock() |
|
| 67 |
+ defer m.Unlock() |
|
| 68 |
+ |
|
| 69 |
+ if err := cryptoSeed(); err != nil {
|
|
| 70 |
+ panic(fmt.Sprintf("Unable to seed the random number generator: %v", err))
|
|
| 71 |
+ } |
|
| 72 |
+} |
|
| 73 |
+ |
|
| 74 |
+// Secure returns true if a cryptographically secure seed was used to |
|
| 75 |
+// initialize rand. |
|
| 76 |
+func Secure() bool {
|
|
| 77 |
+ return atomic.LoadInt32(&secure) == 1 |
|
| 78 |
+} |
|
| 79 |
+ |
|
| 80 |
+// Seeded returns true if Init has seeded the random number generator. |
|
| 81 |
+func Seeded() bool {
|
|
| 82 |
+ return atomic.LoadInt32(&seeded) == 1 |
|
| 83 |
+} |