Compare commits
226 Commits
0.0.201805
...
0.0.201904
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
18fa270472 | ||
|
|
f156a53ff4 | ||
|
|
e680008700 | ||
|
|
767c86f8cb | ||
|
|
421c1f9143 | ||
|
|
ac25702eaf | ||
|
|
92f8474832 | ||
|
|
2e0ed4614a | ||
|
|
2fa80c0cb7 | ||
|
|
52ec440d79 | ||
|
|
2faf2dcf90 | ||
|
|
41c30a7279 | ||
|
|
4b1db1d39b | ||
|
|
a80db5e65e | ||
|
|
9748a52073 | ||
|
|
317d716d66 | ||
|
|
6440f010ee | ||
|
|
49ea0c9b1a | ||
|
|
ca59b60aa7 | ||
|
|
c050c6e60f | ||
|
|
91b4e909bb | ||
|
|
2c51d6af48 | ||
|
|
03f2e2614a | ||
|
|
b0e0ab308d | ||
|
|
66fb5caf02 | ||
|
|
3dd9a0535f | ||
|
|
c2a2b8d739 | ||
|
|
70449f1a97 | ||
|
|
33c3528430 | ||
|
|
30ab07e354 | ||
|
|
a6d5ef82f4 | ||
|
|
5c7cc256e3 | ||
|
|
368dea72fe | ||
|
|
9b22255cad | ||
|
|
11f5780250 | ||
|
|
26af6c4651 | ||
|
|
92f72f5aa6 | ||
|
|
1fdf7b19a3 | ||
|
|
a1aabb21ae | ||
|
|
9041d38e2d | ||
|
|
cddfd9a0d8 | ||
|
|
68f0721c6a | ||
|
|
b8e85267cf | ||
|
|
69f0fe67b6 | ||
|
|
d435be35ca | ||
|
|
967d1a0f3d | ||
|
|
88ff67fb6f | ||
|
|
971be13e77 | ||
|
|
366cbd11a4 | ||
|
|
ab0f442daf | ||
|
|
66524c1f7e | ||
|
|
6e4460ae65 | ||
|
|
d002eff155 | ||
|
|
e06a8f8f9f | ||
|
|
ac4944a708 | ||
|
|
2491f9d454 | ||
|
|
8091c6474a | ||
|
|
040da43889 | ||
|
|
b7025b5627 | ||
|
|
6581cfb885 | ||
|
|
4863089120 | ||
|
|
42c6d0e261 | ||
|
|
f7170e5de2 | ||
|
|
b719a09a26 | ||
|
|
f05f52637f | ||
|
|
713477cfb1 | ||
|
|
5981d5cacf | ||
|
|
b13739ada2 | ||
|
|
c4988999ac | ||
|
|
b662896cf4 | ||
|
|
0525f6b112 | ||
|
|
9d830826c5 | ||
|
|
bd963497da | ||
|
|
05d25fd1b7 | ||
|
|
6d2729dccc | ||
|
|
d87cbeeb2f | ||
|
|
043b7e8013 | ||
|
|
ef48d4fa95 | ||
|
|
f7276ed522 | ||
|
|
c4b43e35a7 | ||
|
|
2efafecab5 | ||
|
|
fac1fbcd72 | ||
|
|
52aa00f3ba | ||
|
|
ea59177f1c | ||
|
|
306d08e692 | ||
|
|
3b7a4fa3ef | ||
|
|
223685875f | ||
|
|
652158ec3c | ||
|
|
cb2bc4b34c | ||
|
|
46279ad0f9 | ||
|
|
73df1c0871 | ||
|
|
069016bbc4 | ||
|
|
3c29434a79 | ||
|
|
c599bf9497 | ||
|
|
f7f63765d1 | ||
|
|
3e8f2e3fa5 | ||
|
|
7b636380e5 | ||
|
|
99a3b628e9 | ||
|
|
e7ffce0d21 | ||
|
|
35f72239ac | ||
|
|
c15cbefc12 | ||
|
|
dd998ca86a | ||
|
|
024a4916c2 | ||
|
|
963be8e993 | ||
|
|
e821cdabd2 | ||
|
|
38c7acd70f | ||
|
|
20f1512b7c | ||
|
|
348b4e9f7c | ||
|
|
f81882ee8b | ||
|
|
3e0e61dd26 | ||
|
|
9635a0b3a6 | ||
|
|
90b6938ca0 | ||
|
|
269944002f | ||
|
|
a5a1ece32f | ||
|
|
f1d5db6547 | ||
|
|
dce5192d86 | ||
|
|
955d8dfe04 | ||
|
|
25e18d01e6 | ||
|
|
45959c116a | ||
|
|
d41bc015cc | ||
|
|
31949136df | ||
|
|
6f76edd045 | ||
|
|
3af9aa88a3 | ||
|
|
a5ca02d79a | ||
|
|
2b7562abbb | ||
|
|
89d2c5ed7a | ||
|
|
dff424baf8 | ||
|
|
6e61c369e8 | ||
|
|
8fde8334dc | ||
|
|
a8326ae753 | ||
|
|
05cc0c8298 | ||
|
|
c967f15e44 | ||
|
|
5ace0fdfe2 | ||
|
|
849fa400e9 | ||
|
|
651744561e | ||
|
|
4fd55daafe | ||
|
|
276bf973e8 | ||
|
|
c37c4ece9e | ||
|
|
b803276061 | ||
|
|
8be1fc9c00 | ||
|
|
738d027f0b | ||
|
|
60848b9c72 | ||
|
|
2e772194cf | ||
|
|
85b2378a07 | ||
|
|
fddb949002 | ||
|
|
5d6083df7e | ||
|
|
b41922e5c8 | ||
|
|
dbb72402f2 | ||
|
|
7c971d7ef4 | ||
|
|
70bcf9ecb8 | ||
|
|
ebc7541953 | ||
|
|
833597b585 | ||
|
|
cf81a28dd3 | ||
|
|
942abf948a | ||
|
|
47d1140361 | ||
|
|
39d6e4f2f1 | ||
|
|
1c02557013 | ||
|
|
32d2148835 | ||
|
|
5be541d147 | ||
|
|
063becdc73 | ||
|
|
15da869b31 | ||
|
|
3ad3e83c7a | ||
|
|
2e13b7b0fb | ||
|
|
6b3b1c3b91 | ||
|
|
6a5d0e2bcd | ||
|
|
0ba551807f | ||
|
|
99d5aeeb27 | ||
|
|
a050431f26 | ||
|
|
0c976003c8 | ||
|
|
955e89839f | ||
|
|
a4cd0216c0 | ||
|
|
1d7845a600 | ||
|
|
5079298ce2 | ||
|
|
fc3a7635e5 | ||
|
|
2496cdd8e6 | ||
|
|
4365b4583f | ||
|
|
bbf320c477 | ||
|
|
625d59da14 | ||
|
|
2f2eca8947 | ||
|
|
66f6ca3e4a | ||
|
|
e6657638fc | ||
|
|
4a9de3218e | ||
|
|
28a167e828 | ||
|
|
99c6513d60 | ||
|
|
8a92a9109a | ||
|
|
0b647d1ca7 | ||
|
|
588b9f01ae | ||
|
|
f70bd1fab3 | ||
|
|
40d5ff0c70 | ||
|
|
5a2228a5c9 | ||
|
|
0a63188afa | ||
|
|
65a74f3175 | ||
|
|
b4cef2524f | ||
|
|
7038de95e1 | ||
|
|
82d12e85bb | ||
|
|
d6b694e161 | ||
|
|
794e494802 | ||
|
|
dd663a7ba4 | ||
|
|
8462c08cf2 | ||
|
|
b8c9e13c6e | ||
|
|
bc05eb1c3c | ||
|
|
7a527f7c89 | ||
|
|
84f52ce0d6 | ||
|
|
7bdc5eb54e | ||
|
|
1c666576d5 | ||
|
|
2ae22ac65d | ||
|
|
ff3f2455e5 | ||
|
|
b962d7d791 | ||
|
|
837a12c841 | ||
|
|
7472930d4e | ||
|
|
6307bfcdf4 | ||
|
|
e28d70f5b2 | ||
|
|
84c5357cf3 | ||
|
|
acb5481246 | ||
|
|
18f43705ec | ||
|
|
058cedcf66 | ||
|
|
c5fa3de24c | ||
|
|
1068d6b92b | ||
|
|
5e924e5407 | ||
|
|
b290cf05e3 | ||
|
|
b95a4c61a5 | ||
|
|
a5b3340e5b | ||
|
|
7c21a3de0a | ||
|
|
0a68c1ab17 | ||
|
|
e04f9543c0 | ||
|
|
fa003b6933 |
2
.gitignore
vendored
2
.gitignore
vendored
@@ -1,2 +1,4 @@
|
||||
wireguard-go
|
||||
vendor
|
||||
.gopath
|
||||
ireallywantobuildon_linux.go
|
||||
|
||||
351
COPYING
351
COPYING
@@ -1,338 +1,17 @@
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 2, June 1991
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
of the Software, and to permit persons to whom the Software is furnished to do
|
||||
so, subject to the following conditions:
|
||||
|
||||
Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
Preamble
|
||||
|
||||
The licenses for most software are designed to take away your
|
||||
freedom to share and change it. By contrast, the GNU General Public
|
||||
License is intended to guarantee your freedom to share and change free
|
||||
software--to make sure the software is free for all its users. This
|
||||
General Public License applies to most of the Free Software
|
||||
Foundation's software and to any other program whose authors commit to
|
||||
using it. (Some other Free Software Foundation software is covered by
|
||||
the GNU Lesser General Public License instead.) You can apply it to
|
||||
your programs, too.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
this service if you wish), that you receive source code or can get it
|
||||
if you want it, that you can change the software or use pieces of it
|
||||
in new free programs; and that you know you can do these things.
|
||||
|
||||
To protect your rights, we need to make restrictions that forbid
|
||||
anyone to deny you these rights or to ask you to surrender the rights.
|
||||
These restrictions translate to certain responsibilities for you if you
|
||||
distribute copies of the software, or if you modify it.
|
||||
|
||||
For example, if you distribute copies of such a program, whether
|
||||
gratis or for a fee, you must give the recipients all the rights that
|
||||
you have. You must make sure that they, too, receive or can get the
|
||||
source code. And you must show them these terms so they know their
|
||||
rights.
|
||||
|
||||
We protect your rights with two steps: (1) copyright the software, and
|
||||
(2) offer you this license which gives you legal permission to copy,
|
||||
distribute and/or modify the software.
|
||||
|
||||
Also, for each author's protection and ours, we want to make certain
|
||||
that everyone understands that there is no warranty for this free
|
||||
software. If the software is modified by someone else and passed on, we
|
||||
want its recipients to know that what they have is not the original, so
|
||||
that any problems introduced by others will not reflect on the original
|
||||
authors' reputations.
|
||||
|
||||
Finally, any free program is threatened constantly by software
|
||||
patents. We wish to avoid the danger that redistributors of a free
|
||||
program will individually obtain patent licenses, in effect making the
|
||||
program proprietary. To prevent this, we have made it clear that any
|
||||
patent must be licensed for everyone's free use or not licensed at all.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
|
||||
0. This License applies to any program or other work which contains
|
||||
a notice placed by the copyright holder saying it may be distributed
|
||||
under the terms of this General Public License. The "Program", below,
|
||||
refers to any such program or work, and a "work based on the Program"
|
||||
means either the Program or any derivative work under copyright law:
|
||||
that is to say, a work containing the Program or a portion of it,
|
||||
either verbatim or with modifications and/or translated into another
|
||||
language. (Hereinafter, translation is included without limitation in
|
||||
the term "modification".) Each licensee is addressed as "you".
|
||||
|
||||
Activities other than copying, distribution and modification are not
|
||||
covered by this License; they are outside its scope. The act of
|
||||
running the Program is not restricted, and the output from the Program
|
||||
is covered only if its contents constitute a work based on the
|
||||
Program (independent of having been made by running the Program).
|
||||
Whether that is true depends on what the Program does.
|
||||
|
||||
1. You may copy and distribute verbatim copies of the Program's
|
||||
source code as you receive it, in any medium, provided that you
|
||||
conspicuously and appropriately publish on each copy an appropriate
|
||||
copyright notice and disclaimer of warranty; keep intact all the
|
||||
notices that refer to this License and to the absence of any warranty;
|
||||
and give any other recipients of the Program a copy of this License
|
||||
along with the Program.
|
||||
|
||||
You may charge a fee for the physical act of transferring a copy, and
|
||||
you may at your option offer warranty protection in exchange for a fee.
|
||||
|
||||
2. You may modify your copy or copies of the Program or any portion
|
||||
of it, thus forming a work based on the Program, and copy and
|
||||
distribute such modifications or work under the terms of Section 1
|
||||
above, provided that you also meet all of these conditions:
|
||||
|
||||
a) You must cause the modified files to carry prominent notices
|
||||
stating that you changed the files and the date of any change.
|
||||
|
||||
b) You must cause any work that you distribute or publish, that in
|
||||
whole or in part contains or is derived from the Program or any
|
||||
part thereof, to be licensed as a whole at no charge to all third
|
||||
parties under the terms of this License.
|
||||
|
||||
c) If the modified program normally reads commands interactively
|
||||
when run, you must cause it, when started running for such
|
||||
interactive use in the most ordinary way, to print or display an
|
||||
announcement including an appropriate copyright notice and a
|
||||
notice that there is no warranty (or else, saying that you provide
|
||||
a warranty) and that users may redistribute the program under
|
||||
these conditions, and telling the user how to view a copy of this
|
||||
License. (Exception: if the Program itself is interactive but
|
||||
does not normally print such an announcement, your work based on
|
||||
the Program is not required to print an announcement.)
|
||||
|
||||
These requirements apply to the modified work as a whole. If
|
||||
identifiable sections of that work are not derived from the Program,
|
||||
and can be reasonably considered independent and separate works in
|
||||
themselves, then this License, and its terms, do not apply to those
|
||||
sections when you distribute them as separate works. But when you
|
||||
distribute the same sections as part of a whole which is a work based
|
||||
on the Program, the distribution of the whole must be on the terms of
|
||||
this License, whose permissions for other licensees extend to the
|
||||
entire whole, and thus to each and every part regardless of who wrote it.
|
||||
|
||||
Thus, it is not the intent of this section to claim rights or contest
|
||||
your rights to work written entirely by you; rather, the intent is to
|
||||
exercise the right to control the distribution of derivative or
|
||||
collective works based on the Program.
|
||||
|
||||
In addition, mere aggregation of another work not based on the Program
|
||||
with the Program (or with a work based on the Program) on a volume of
|
||||
a storage or distribution medium does not bring the other work under
|
||||
the scope of this License.
|
||||
|
||||
3. You may copy and distribute the Program (or a work based on it,
|
||||
under Section 2) in object code or executable form under the terms of
|
||||
Sections 1 and 2 above provided that you also do one of the following:
|
||||
|
||||
a) Accompany it with the complete corresponding machine-readable
|
||||
source code, which must be distributed under the terms of Sections
|
||||
1 and 2 above on a medium customarily used for software interchange; or,
|
||||
|
||||
b) Accompany it with a written offer, valid for at least three
|
||||
years, to give any third party, for a charge no more than your
|
||||
cost of physically performing source distribution, a complete
|
||||
machine-readable copy of the corresponding source code, to be
|
||||
distributed under the terms of Sections 1 and 2 above on a medium
|
||||
customarily used for software interchange; or,
|
||||
|
||||
c) Accompany it with the information you received as to the offer
|
||||
to distribute corresponding source code. (This alternative is
|
||||
allowed only for noncommercial distribution and only if you
|
||||
received the program in object code or executable form with such
|
||||
an offer, in accord with Subsection b above.)
|
||||
|
||||
The source code for a work means the preferred form of the work for
|
||||
making modifications to it. For an executable work, complete source
|
||||
code means all the source code for all modules it contains, plus any
|
||||
associated interface definition files, plus the scripts used to
|
||||
control compilation and installation of the executable. However, as a
|
||||
special exception, the source code distributed need not include
|
||||
anything that is normally distributed (in either source or binary
|
||||
form) with the major components (compiler, kernel, and so on) of the
|
||||
operating system on which the executable runs, unless that component
|
||||
itself accompanies the executable.
|
||||
|
||||
If distribution of executable or object code is made by offering
|
||||
access to copy from a designated place, then offering equivalent
|
||||
access to copy the source code from the same place counts as
|
||||
distribution of the source code, even though third parties are not
|
||||
compelled to copy the source along with the object code.
|
||||
|
||||
4. You may not copy, modify, sublicense, or distribute the Program
|
||||
except as expressly provided under this License. Any attempt
|
||||
otherwise to copy, modify, sublicense or distribute the Program is
|
||||
void, and will automatically terminate your rights under this License.
|
||||
However, parties who have received copies, or rights, from you under
|
||||
this License will not have their licenses terminated so long as such
|
||||
parties remain in full compliance.
|
||||
|
||||
5. You are not required to accept this License, since you have not
|
||||
signed it. However, nothing else grants you permission to modify or
|
||||
distribute the Program or its derivative works. These actions are
|
||||
prohibited by law if you do not accept this License. Therefore, by
|
||||
modifying or distributing the Program (or any work based on the
|
||||
Program), you indicate your acceptance of this License to do so, and
|
||||
all its terms and conditions for copying, distributing or modifying
|
||||
the Program or works based on it.
|
||||
|
||||
6. Each time you redistribute the Program (or any work based on the
|
||||
Program), the recipient automatically receives a license from the
|
||||
original licensor to copy, distribute or modify the Program subject to
|
||||
these terms and conditions. You may not impose any further
|
||||
restrictions on the recipients' exercise of the rights granted herein.
|
||||
You are not responsible for enforcing compliance by third parties to
|
||||
this License.
|
||||
|
||||
7. If, as a consequence of a court judgment or allegation of patent
|
||||
infringement or for any other reason (not limited to patent issues),
|
||||
conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot
|
||||
distribute so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you
|
||||
may not distribute the Program at all. For example, if a patent
|
||||
license would not permit royalty-free redistribution of the Program by
|
||||
all those who receive copies directly or indirectly through you, then
|
||||
the only way you could satisfy both it and this License would be to
|
||||
refrain entirely from distribution of the Program.
|
||||
|
||||
If any portion of this section is held invalid or unenforceable under
|
||||
any particular circumstance, the balance of the section is intended to
|
||||
apply and the section as a whole is intended to apply in other
|
||||
circumstances.
|
||||
|
||||
It is not the purpose of this section to induce you to infringe any
|
||||
patents or other property right claims or to contest validity of any
|
||||
such claims; this section has the sole purpose of protecting the
|
||||
integrity of the free software distribution system, which is
|
||||
implemented by public license practices. Many people have made
|
||||
generous contributions to the wide range of software distributed
|
||||
through that system in reliance on consistent application of that
|
||||
system; it is up to the author/donor to decide if he or she is willing
|
||||
to distribute software through any other system and a licensee cannot
|
||||
impose that choice.
|
||||
|
||||
This section is intended to make thoroughly clear what is believed to
|
||||
be a consequence of the rest of this License.
|
||||
|
||||
8. If the distribution and/or use of the Program is restricted in
|
||||
certain countries either by patents or by copyrighted interfaces, the
|
||||
original copyright holder who places the Program under this License
|
||||
may add an explicit geographical distribution limitation excluding
|
||||
those countries, so that distribution is permitted only in or among
|
||||
countries not thus excluded. In such case, this License incorporates
|
||||
the limitation as if written in the body of this License.
|
||||
|
||||
9. The Free Software Foundation may publish revised and/or new versions
|
||||
of the General Public License from time to time. Such new versions will
|
||||
be similar in spirit to the present version, but may differ in detail to
|
||||
address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the Program
|
||||
specifies a version number of this License which applies to it and "any
|
||||
later version", you have the option of following the terms and conditions
|
||||
either of that version or of any later version published by the Free
|
||||
Software Foundation. If the Program does not specify a version number of
|
||||
this License, you may choose any version ever published by the Free Software
|
||||
Foundation.
|
||||
|
||||
10. If you wish to incorporate parts of the Program into other free
|
||||
programs whose distribution conditions are different, write to the author
|
||||
to ask for permission. For software which is copyrighted by the Free
|
||||
Software Foundation, write to the Free Software Foundation; we sometimes
|
||||
make exceptions for this. Our decision will be guided by the two goals
|
||||
of preserving the free status of all derivatives of our free software and
|
||||
of promoting the sharing and reuse of software generally.
|
||||
|
||||
NO WARRANTY
|
||||
|
||||
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
|
||||
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
|
||||
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
|
||||
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
|
||||
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
|
||||
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
|
||||
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
|
||||
REPAIR OR CORRECTION.
|
||||
|
||||
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
|
||||
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
|
||||
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
|
||||
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
|
||||
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
|
||||
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
|
||||
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGES.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
free software which everyone can redistribute and change under these terms.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest
|
||||
to attach them to the start of each source file to most effectively
|
||||
convey the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License version 2
|
||||
as published by the Free Software Foundation.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along
|
||||
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If the program is interactive, make it output a short notice like this
|
||||
when it starts in an interactive mode:
|
||||
|
||||
Gnomovision version 69, Copyright (C) year name of author
|
||||
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||
This is free software, and you are welcome to redistribute it
|
||||
under certain conditions; type `show c' for details.
|
||||
|
||||
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||
parts of the General Public License. Of course, the commands you use may
|
||||
be called something other than `show w' and `show c'; they could even be
|
||||
mouse-clicks or menu items--whatever suits your program.
|
||||
|
||||
You should also get your employer (if you work as a programmer) or your
|
||||
school, if any, to sign a "copyright disclaimer" for the program, if
|
||||
necessary. Here is a sample; alter the names:
|
||||
|
||||
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
|
||||
`Gnomovision' (which makes passes at compilers) written by James Hacker.
|
||||
|
||||
<signature of Ty Coon>, 1 April 1989
|
||||
Ty Coon, President of Vice
|
||||
|
||||
This General Public License does not permit incorporating your program into
|
||||
proprietary programs. If your program is a subroutine library, you may
|
||||
consider it more useful to permit linking proprietary applications with the
|
||||
library. If this is what you want to do, use the GNU Lesser General
|
||||
Public License instead of this License.
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
16
Gopkg.lock
generated
16
Gopkg.lock
generated
@@ -1,16 +0,0 @@
|
||||
# This was generated by ./generate-vendor.sh
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "golang.org/x/crypto"
|
||||
revision = "1a580b3eff7814fc9b40602fd35256c63b50f491"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "golang.org/x/net"
|
||||
revision = "2491c5de3490fced2f6cff376127c667efeed857"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "golang.org/x/sys"
|
||||
revision = "7c87d13f8e835d2fb3a70a2912c811ed0c1d241b"
|
||||
|
||||
13
Gopkg.toml
13
Gopkg.toml
@@ -1,13 +0,0 @@
|
||||
# This was generated by ./generate-vendor.sh
|
||||
[[constraint]]
|
||||
branch = "master"
|
||||
name = "golang.org/x/crypto"
|
||||
|
||||
[[constraint]]
|
||||
branch = "master"
|
||||
name = "golang.org/x/net"
|
||||
|
||||
[[constraint]]
|
||||
branch = "master"
|
||||
name = "golang.org/x/sys"
|
||||
|
||||
34
Makefile
34
Makefile
@@ -1,16 +1,42 @@
|
||||
PREFIX ?= /usr
|
||||
DESTDIR ?=
|
||||
BINDIR ?= $(PREFIX)/bin
|
||||
export GOPATH ?= $(CURDIR)/.gopath
|
||||
export GO111MODULE := on
|
||||
|
||||
all: wireguard-go
|
||||
all: generate-version-and-build
|
||||
|
||||
ifeq ($(shell go env GOOS)|$(wildcard .git),linux|)
|
||||
$(error Do not build this for Linux. Instead use the Linux kernel module. See wireguard.com/install/ for more info.)
|
||||
else
|
||||
ireallywantobuildon_linux.go:
|
||||
@printf "WARNING: This software is meant for use on non-Linux\nsystems. For Linux, please use the kernel module\ninstead. See wireguard.com/install/ for more info.\n\n" >&2
|
||||
@printf 'package main\nconst UseTheKernelModuleInstead = 0xdeadbabe\n' > "$@"
|
||||
clean-ireallywantobuildon_linux.go:
|
||||
@rm -f ireallywantobuildon_linux.go
|
||||
.PHONY: clean-ireallywantobuildon_linux.go
|
||||
clean: clean-ireallywantobuildon_linux.go
|
||||
wireguard-go: ireallywantobuildon_linux.go
|
||||
endif
|
||||
|
||||
MAKEFLAGS += --no-print-directory
|
||||
|
||||
generate-version-and-build:
|
||||
@export GIT_CEILING_DIRECTORIES="$(realpath $(CURDIR)/..)" && \
|
||||
tag="$$(git describe --dirty 2>/dev/null)" && \
|
||||
ver="$$(printf 'package device\nconst WireGuardGoVersion = "%s"\n' "$$tag")" && \
|
||||
[ "$$(cat device/version.go 2>/dev/null)" != "$$ver" ] && \
|
||||
echo "$$ver" > device/version.go && \
|
||||
git update-index --assume-unchanged device/version.go || true
|
||||
@$(MAKE) wireguard-go
|
||||
|
||||
wireguard-go: $(wildcard *.go) $(wildcard */*.go)
|
||||
go build -v -o $@
|
||||
go build -v -o "$@"
|
||||
|
||||
install: wireguard-go
|
||||
@install -v -d "$(DESTDIR)$(BINDIR)" && install -m 0755 -v wireguard-go "$(DESTDIR)$(BINDIR)/wireguard-go"
|
||||
@install -v -d "$(DESTDIR)$(BINDIR)" && install -v -m 0755 "$<" "$(DESTDIR)$(BINDIR)/wireguard-go"
|
||||
|
||||
clean:
|
||||
rm -f wireguard-go
|
||||
|
||||
.PHONY: clean install
|
||||
.PHONY: all clean install generate-version-and-build
|
||||
|
||||
49
README.md
49
README.md
@@ -32,7 +32,7 @@ This will run on Linux; however **YOU SHOULD NOT RUN THIS ON LINUX**. Instead us
|
||||
|
||||
### macOS
|
||||
|
||||
This runs on macOS using the utun driver. It does not yet support sticky sockets, and won't support fwmarks because of Darwin limitations. Since the utun driver cannot have arbitrary interface names, you must either use `utun[0-9]+` for an explicit interface name or `utun` to have the kernel select one for you. If you choose `utun` as the interface name, and the environment variable `WG_DARWIN_UTUN_NAME_FILE` is defined, then the actual name of the interface chosen by the kernel is written to the file specified by that variable.
|
||||
This runs on macOS using the utun driver. It does not yet support sticky sockets, and won't support fwmarks because of Darwin limitations. Since the utun driver cannot have arbitrary interface names, you must either use `utun[0-9]+` for an explicit interface name or `utun` to have the kernel select one for you. If you choose `utun` as the interface name, and the environment variable `WG_TUN_NAME_FILE` is defined, then the actual name of the interface chosen by the kernel is written to the file specified by that variable.
|
||||
|
||||
### Windows
|
||||
|
||||
@@ -40,43 +40,40 @@ It is currently a work in progress to strip out the beginnings of an experiment
|
||||
|
||||
### FreeBSD
|
||||
|
||||
Work in progress, but nothing yet to share.
|
||||
This will run on FreeBSD. It does not yet support sticky sockets. Fwmark is mapped to `SO_USER_COOKIE`.
|
||||
|
||||
### OpenBSD
|
||||
|
||||
This will run on OpenBSD. It does not yet support sticky sockets. Fwmark is mapped to `SO_RTABLE`. Since the tun driver cannot have arbitrary interface names, you must either use `tun[0-9]+` for an explicit interface name or `tun` to have the program select one for you. If you choose `tun` as the interface name, and the environment variable `WG_TUN_NAME_FILE` is defined, then the actual name of the interface chosen by the kernel is written to the file specified by that variable.
|
||||
|
||||
## Building
|
||||
|
||||
You can satisfy dependencies with either `go get -d -v` or `dep ensure -vendor-only`. Then run `make`. As this is a Go project, a `GOPATH` is required. For example, wireguard-go can be built with:
|
||||
This requires an installation of [go](https://golang.org) ≥ 1.12.
|
||||
|
||||
```
|
||||
$ git clone https://git.zx2c4.com/wireguard-go
|
||||
$ cd wireguard-go
|
||||
$ export GOPATH="$PWD/gopath"
|
||||
$ go get -d -v
|
||||
$ make
|
||||
```
|
||||
|
||||
## License
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License version 2 as
|
||||
published by the Free Software Foundation.
|
||||
Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along
|
||||
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
---------------------------------------------------------------------------
|
||||
Additional Permissions For Submission to Apple App Store: Provided that you
|
||||
are otherwise in compliance with the GPLv2 for each covered work you convey
|
||||
(including without limitation making the Corresponding Source available in
|
||||
compliance with Section 3 of the GPLv2), you are granted the additional
|
||||
the additional permission to convey through the Apple App Store
|
||||
non-source executable versions of the Program as incorporated into each
|
||||
applicable covered work as Executable Versions only under the Mozilla
|
||||
Public License version 2.0 (https://www.mozilla.org/en-US/MPL/2.0/).
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
of the Software, and to permit persons to whom the Software is furnished to do
|
||||
so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
@echo off
|
||||
|
||||
REM builds wireguard for windows
|
||||
|
||||
go get
|
||||
go build -o wireguard-go.exe
|
||||
@@ -1,10 +1,9 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2018 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
|
||||
* Copyright (C) 2017-2018 Mathias N. Hall-Andersen <mathias@hall-andersen.dk>.
|
||||
* Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package main
|
||||
package device
|
||||
|
||||
import (
|
||||
"errors"
|
||||
@@ -1,10 +1,9 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2018 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
|
||||
* Copyright (C) 2017-2018 Mathias N. Hall-Andersen <mathias@hall-andersen.dk>.
|
||||
* Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package main
|
||||
package device
|
||||
|
||||
import (
|
||||
"math/rand"
|
||||
@@ -1,10 +1,9 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2018 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
|
||||
* Copyright (C) 2017-2018 Mathias N. Hall-Andersen <mathias@hall-andersen.dk>.
|
||||
* Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package main
|
||||
package device
|
||||
|
||||
import (
|
||||
"math/rand"
|
||||
@@ -1,10 +1,9 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2018 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
|
||||
* Copyright (C) 2017-2018 Mathias N. Hall-Andersen <mathias@hall-andersen.dk>.
|
||||
* Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package main
|
||||
package device
|
||||
|
||||
import "errors"
|
||||
|
||||
34
device/boundif_android.go
Normal file
34
device/boundif_android.go
Normal file
@@ -0,0 +1,34 @@
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package device
|
||||
|
||||
func (device *Device) PeekLookAtSocketFd4() (fd int, err error) {
|
||||
sysconn, err := device.net.bind.(*nativeBind).ipv4.SyscallConn()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = sysconn.Control(func(f uintptr) {
|
||||
fd = int(f)
|
||||
})
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (device *Device) PeekLookAtSocketFd6() (fd int, err error) {
|
||||
sysconn, err := device.net.bind.(*nativeBind).ipv6.SyscallConn()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = sysconn.Control(func(f uintptr) {
|
||||
fd = int(f)
|
||||
})
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
44
device/boundif_darwin.go
Normal file
44
device/boundif_darwin.go
Normal file
@@ -0,0 +1,44 @@
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package device
|
||||
|
||||
import (
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
func (device *Device) BindSocketToInterface4(interfaceIndex uint32) error {
|
||||
sysconn, err := device.net.bind.(*nativeBind).ipv4.SyscallConn()
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
err2 := sysconn.Control(func(fd uintptr) {
|
||||
err = unix.SetsockoptInt(int(fd), unix.IPPROTO_IP, unix.IP_BOUND_IF, int(interfaceIndex))
|
||||
})
|
||||
if err2 != nil {
|
||||
return err2
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (device *Device) BindSocketToInterface6(interfaceIndex uint32) error {
|
||||
sysconn, err := device.net.bind.(*nativeBind).ipv4.SyscallConn()
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
err2 := sysconn.Control(func(fd uintptr) {
|
||||
err = unix.SetsockoptInt(int(fd), unix.IPPROTO_IPV6, unix.IPV6_BOUND_IF, int(interfaceIndex))
|
||||
})
|
||||
if err2 != nil {
|
||||
return err2
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
56
device/boundif_windows.go
Normal file
56
device/boundif_windows.go
Normal file
@@ -0,0 +1,56 @@
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package device
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"golang.org/x/sys/windows"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
const (
|
||||
sockoptIP_UNICAST_IF = 31
|
||||
sockoptIPV6_UNICAST_IF = 31
|
||||
)
|
||||
|
||||
func (device *Device) BindSocketToInterface4(interfaceIndex uint32) error {
|
||||
/* MSDN says for IPv4 this needs to be in net byte order, so that it's like an IP address with leading zeros. */
|
||||
bytes := make([]byte, 4)
|
||||
binary.BigEndian.PutUint32(bytes, interfaceIndex)
|
||||
interfaceIndex = *(*uint32)(unsafe.Pointer(&bytes[0]))
|
||||
|
||||
sysconn, err := device.net.bind.(*nativeBind).ipv4.SyscallConn()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err2 := sysconn.Control(func(fd uintptr) {
|
||||
err = windows.SetsockoptInt(windows.Handle(fd), windows.IPPROTO_IP, sockoptIP_UNICAST_IF, int(interfaceIndex))
|
||||
})
|
||||
if err2 != nil {
|
||||
return err2
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (device *Device) BindSocketToInterface6(interfaceIndex uint32) error {
|
||||
sysconn, err := device.net.bind.(*nativeBind).ipv6.SyscallConn()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err2 := sysconn.Control(func(fd uintptr) {
|
||||
err = windows.SetsockoptInt(windows.Handle(fd), windows.IPPROTO_IPV6, sockoptIPV6_UNICAST_IF, int(interfaceIndex))
|
||||
})
|
||||
if err2 != nil {
|
||||
return err2
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -1,10 +1,9 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2018 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
|
||||
* Copyright (C) 2017-2018 Mathias N. Hall-Andersen <mathias@hall-andersen.dk>.
|
||||
* Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package main
|
||||
package device
|
||||
|
||||
import (
|
||||
"errors"
|
||||
@@ -66,8 +65,6 @@ func parseEndpoint(s string) (*net.UDPAddr, error) {
|
||||
return addr, err
|
||||
}
|
||||
|
||||
/* Must hold device and net lock
|
||||
*/
|
||||
func unsafeCloseBind(device *Device) error {
|
||||
var err error
|
||||
netc := &device.net
|
||||
@@ -75,13 +72,14 @@ func unsafeCloseBind(device *Device) error {
|
||||
err = netc.bind.Close()
|
||||
netc.bind = nil
|
||||
}
|
||||
netc.stopping.Wait()
|
||||
return err
|
||||
}
|
||||
|
||||
func (device *Device) BindSetMark(mark uint32) error {
|
||||
|
||||
device.net.mutex.Lock()
|
||||
defer device.net.mutex.Unlock()
|
||||
device.net.Lock()
|
||||
defer device.net.Unlock()
|
||||
|
||||
// check if modified
|
||||
|
||||
@@ -100,23 +98,23 @@ func (device *Device) BindSetMark(mark uint32) error {
|
||||
|
||||
// clear cached source addresses
|
||||
|
||||
device.peers.mutex.RLock()
|
||||
device.peers.RLock()
|
||||
for _, peer := range device.peers.keyMap {
|
||||
peer.mutex.Lock()
|
||||
defer peer.mutex.Unlock()
|
||||
peer.Lock()
|
||||
defer peer.Unlock()
|
||||
if peer.endpoint != nil {
|
||||
peer.endpoint.ClearSrc()
|
||||
}
|
||||
}
|
||||
device.peers.mutex.RUnlock()
|
||||
device.peers.RUnlock()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (device *Device) BindUpdate() error {
|
||||
|
||||
device.net.mutex.Lock()
|
||||
defer device.net.mutex.Unlock()
|
||||
device.net.Lock()
|
||||
defer device.net.Unlock()
|
||||
|
||||
// close existing sockets
|
||||
|
||||
@@ -150,22 +148,23 @@ func (device *Device) BindUpdate() error {
|
||||
|
||||
// clear cached source addresses
|
||||
|
||||
device.peers.mutex.RLock()
|
||||
device.peers.RLock()
|
||||
for _, peer := range device.peers.keyMap {
|
||||
peer.mutex.Lock()
|
||||
defer peer.mutex.Unlock()
|
||||
peer.Lock()
|
||||
defer peer.Unlock()
|
||||
if peer.endpoint != nil {
|
||||
peer.endpoint.ClearSrc()
|
||||
}
|
||||
}
|
||||
device.peers.mutex.RUnlock()
|
||||
device.peers.RUnlock()
|
||||
|
||||
// start receiving routines
|
||||
|
||||
device.state.starting.Add(ConnRoutineNumber)
|
||||
device.state.stopping.Add(ConnRoutineNumber)
|
||||
device.net.starting.Add(ConnRoutineNumber)
|
||||
device.net.stopping.Add(ConnRoutineNumber)
|
||||
go device.RoutineReceiveIncoming(ipv4.Version, netc.bind)
|
||||
go device.RoutineReceiveIncoming(ipv6.Version, netc.bind)
|
||||
device.net.starting.Wait()
|
||||
|
||||
device.log.Debug.Println("UDP bind has been updated")
|
||||
}
|
||||
@@ -174,8 +173,8 @@ func (device *Device) BindUpdate() error {
|
||||
}
|
||||
|
||||
func (device *Device) BindClose() error {
|
||||
device.net.mutex.Lock()
|
||||
device.net.Lock()
|
||||
err := unsafeCloseBind(device)
|
||||
device.net.mutex.Unlock()
|
||||
device.net.Unlock()
|
||||
return err
|
||||
}
|
||||
@@ -1,15 +1,16 @@
|
||||
// +build !linux
|
||||
// +build !linux android
|
||||
|
||||
/* SPDX-License-Identifier: GPL-2.0
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2018 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
|
||||
* Copyright (C) 2017-2018 Mathias N. Hall-Andersen <mathias@hall-andersen.dk>.
|
||||
* Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package main
|
||||
package device
|
||||
|
||||
import (
|
||||
"net"
|
||||
"os"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
/* This code is meant to be a temporary solution
|
||||
@@ -19,14 +20,14 @@ import (
|
||||
* See conn_linux.go for an implementation on the linux platform.
|
||||
*/
|
||||
|
||||
type NativeBind struct {
|
||||
type nativeBind struct {
|
||||
ipv4 *net.UDPConn
|
||||
ipv6 *net.UDPConn
|
||||
}
|
||||
|
||||
type NativeEndpoint net.UDPAddr
|
||||
|
||||
var _ Bind = (*NativeBind)(nil)
|
||||
var _ Bind = (*nativeBind)(nil)
|
||||
var _ Endpoint = (*NativeEndpoint)(nil)
|
||||
|
||||
func CreateEndpoint(s string) (Endpoint, error) {
|
||||
@@ -85,36 +86,57 @@ func listenNet(network string, port int) (*net.UDPConn, int, error) {
|
||||
return conn, uaddr.Port, nil
|
||||
}
|
||||
|
||||
func extractErrno(err error) error {
|
||||
opErr, ok := err.(*net.OpError)
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
syscallErr, ok := opErr.Err.(*os.SyscallError)
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
return syscallErr.Err
|
||||
}
|
||||
|
||||
func CreateBind(uport uint16, device *Device) (Bind, uint16, error) {
|
||||
var err error
|
||||
var bind NativeBind
|
||||
var bind nativeBind
|
||||
|
||||
port := int(uport)
|
||||
|
||||
bind.ipv4, port, err = listenNet("udp4", port)
|
||||
if err != nil {
|
||||
if err != nil && extractErrno(err) != syscall.EAFNOSUPPORT {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
bind.ipv6, port, err = listenNet("udp6", port)
|
||||
if err != nil {
|
||||
if err != nil && extractErrno(err) != syscall.EAFNOSUPPORT {
|
||||
bind.ipv4.Close()
|
||||
bind.ipv4 = nil
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
return &bind, uint16(port), nil
|
||||
}
|
||||
|
||||
func (bind *NativeBind) Close() error {
|
||||
err1 := bind.ipv4.Close()
|
||||
err2 := bind.ipv6.Close()
|
||||
func (bind *nativeBind) Close() error {
|
||||
var err1, err2 error
|
||||
if bind.ipv4 != nil {
|
||||
err1 = bind.ipv4.Close()
|
||||
}
|
||||
if bind.ipv6 != nil {
|
||||
err2 = bind.ipv6.Close()
|
||||
}
|
||||
if err1 != nil {
|
||||
return err1
|
||||
}
|
||||
return err2
|
||||
}
|
||||
|
||||
func (bind *NativeBind) ReceiveIPv4(buff []byte) (int, Endpoint, error) {
|
||||
func (bind *nativeBind) ReceiveIPv4(buff []byte) (int, Endpoint, error) {
|
||||
if bind.ipv4 == nil {
|
||||
return 0, nil, syscall.EAFNOSUPPORT
|
||||
}
|
||||
n, endpoint, err := bind.ipv4.ReadFromUDP(buff)
|
||||
if endpoint != nil {
|
||||
endpoint.IP = endpoint.IP.To4()
|
||||
@@ -122,22 +144,27 @@ func (bind *NativeBind) ReceiveIPv4(buff []byte) (int, Endpoint, error) {
|
||||
return n, (*NativeEndpoint)(endpoint), err
|
||||
}
|
||||
|
||||
func (bind *NativeBind) ReceiveIPv6(buff []byte) (int, Endpoint, error) {
|
||||
func (bind *nativeBind) ReceiveIPv6(buff []byte) (int, Endpoint, error) {
|
||||
if bind.ipv6 == nil {
|
||||
return 0, nil, syscall.EAFNOSUPPORT
|
||||
}
|
||||
n, endpoint, err := bind.ipv6.ReadFromUDP(buff)
|
||||
return n, (*NativeEndpoint)(endpoint), err
|
||||
}
|
||||
|
||||
func (bind *NativeBind) Send(buff []byte, endpoint Endpoint) error {
|
||||
func (bind *nativeBind) Send(buff []byte, endpoint Endpoint) error {
|
||||
var err error
|
||||
nend := endpoint.(*NativeEndpoint)
|
||||
if nend.IP.To4() != nil {
|
||||
if bind.ipv4 == nil {
|
||||
return syscall.EAFNOSUPPORT
|
||||
}
|
||||
_, err = bind.ipv4.WriteToUDP(buff, (*net.UDPAddr)(nend))
|
||||
} else {
|
||||
if bind.ipv6 == nil {
|
||||
return syscall.EAFNOSUPPORT
|
||||
}
|
||||
_, err = bind.ipv6.WriteToUDP(buff, (*net.UDPAddr)(nend))
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (bind *NativeBind) SetMark(_ uint32) error {
|
||||
return nil
|
||||
}
|
||||
@@ -1,7 +1,8 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0
|
||||
// +build !android
|
||||
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2018 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
|
||||
* Copyright (C) 2017-2018 Mathias N. Hall-Andersen <mathias@hall-andersen.dk>.
|
||||
* Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved.
|
||||
*
|
||||
* This implements userspace semantics of "sticky sockets", modeled after
|
||||
* WireGuard's kernelspace implementation. This is more or less a straight port
|
||||
@@ -13,17 +14,23 @@
|
||||
* So this code is remains platform dependent.
|
||||
*/
|
||||
|
||||
package main
|
||||
package device
|
||||
|
||||
import (
|
||||
"./rwcancel"
|
||||
"errors"
|
||||
"golang.org/x/sys/unix"
|
||||
"golang.zx2c4.com/wireguard/rwcancel"
|
||||
"net"
|
||||
"strconv"
|
||||
"sync"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
const (
|
||||
FD_ERR = -1
|
||||
)
|
||||
|
||||
type IPv4Source struct {
|
||||
src [4]byte
|
||||
ifindex int32
|
||||
@@ -56,7 +63,7 @@ func (endpoint *NativeEndpoint) dst6() *unix.SockaddrInet6 {
|
||||
return (*unix.SockaddrInet6)(unsafe.Pointer(&endpoint.dst[0]))
|
||||
}
|
||||
|
||||
type NativeBind struct {
|
||||
type nativeBind struct {
|
||||
sock4 int
|
||||
sock6 int
|
||||
netlinkSock int
|
||||
@@ -65,7 +72,7 @@ type NativeBind struct {
|
||||
}
|
||||
|
||||
var _ Endpoint = (*NativeEndpoint)(nil)
|
||||
var _ Bind = (*NativeBind)(nil)
|
||||
var _ Bind = (*nativeBind)(nil)
|
||||
|
||||
func CreateEndpoint(s string) (Endpoint, error) {
|
||||
var end NativeEndpoint
|
||||
@@ -120,9 +127,10 @@ func createNetlinkRouteSocket() (int, error) {
|
||||
|
||||
}
|
||||
|
||||
func CreateBind(port uint16, device *Device) (*NativeBind, uint16, error) {
|
||||
func CreateBind(port uint16, device *Device) (*nativeBind, uint16, error) {
|
||||
var err error
|
||||
var bind NativeBind
|
||||
var bind nativeBind
|
||||
var newPort uint16
|
||||
|
||||
bind.netlinkSock, err = createNetlinkRouteSocket()
|
||||
if err != nil {
|
||||
@@ -136,21 +144,40 @@ func CreateBind(port uint16, device *Device) (*NativeBind, uint16, error) {
|
||||
|
||||
go bind.routineRouteListener(device)
|
||||
|
||||
bind.sock6, port, err = create6(port)
|
||||
// attempt ipv6 bind, update port if succesful
|
||||
|
||||
bind.sock6, newPort, err = create6(port)
|
||||
if err != nil {
|
||||
if err != syscall.EAFNOSUPPORT {
|
||||
bind.netlinkCancel.Cancel()
|
||||
return nil, port, err
|
||||
return nil, 0, err
|
||||
}
|
||||
} else {
|
||||
port = newPort
|
||||
}
|
||||
|
||||
bind.sock4, port, err = create4(port)
|
||||
// attempt ipv4 bind, update port if succesful
|
||||
|
||||
bind.sock4, newPort, err = create4(port)
|
||||
if err != nil {
|
||||
if err != syscall.EAFNOSUPPORT {
|
||||
bind.netlinkCancel.Cancel()
|
||||
unix.Close(bind.sock6)
|
||||
return nil, 0, err
|
||||
}
|
||||
return &bind, port, err
|
||||
} else {
|
||||
port = newPort
|
||||
}
|
||||
|
||||
if bind.sock4 == FD_ERR && bind.sock6 == FD_ERR {
|
||||
return nil, 0, errors.New("ipv4 and ipv6 not supported")
|
||||
}
|
||||
|
||||
return &bind, port, nil
|
||||
}
|
||||
|
||||
func (bind *NativeBind) SetMark(value uint32) error {
|
||||
func (bind *nativeBind) SetMark(value uint32) error {
|
||||
if bind.sock6 != -1 {
|
||||
err := unix.SetsockoptInt(
|
||||
bind.sock6,
|
||||
unix.SOL_SOCKET,
|
||||
@@ -161,8 +188,10 @@ func (bind *NativeBind) SetMark(value uint32) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
err = unix.SetsockoptInt(
|
||||
if bind.sock4 != -1 {
|
||||
err := unix.SetsockoptInt(
|
||||
bind.sock4,
|
||||
unix.SOL_SOCKET,
|
||||
unix.SO_MARK,
|
||||
@@ -172,6 +201,7 @@ func (bind *NativeBind) SetMark(value uint32) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
bind.lastMark = value
|
||||
return nil
|
||||
@@ -183,10 +213,15 @@ func closeUnblock(fd int) error {
|
||||
return unix.Close(fd)
|
||||
}
|
||||
|
||||
func (bind *NativeBind) Close() error {
|
||||
err1 := closeUnblock(bind.sock6)
|
||||
err2 := closeUnblock(bind.sock4)
|
||||
err3 := bind.netlinkCancel.Cancel()
|
||||
func (bind *nativeBind) Close() error {
|
||||
var err1, err2, err3 error
|
||||
if bind.sock6 != -1 {
|
||||
err1 = closeUnblock(bind.sock6)
|
||||
}
|
||||
if bind.sock4 != -1 {
|
||||
err2 = closeUnblock(bind.sock4)
|
||||
}
|
||||
err3 = bind.netlinkCancel.Cancel()
|
||||
|
||||
if err1 != nil {
|
||||
return err1
|
||||
@@ -197,8 +232,11 @@ func (bind *NativeBind) Close() error {
|
||||
return err3
|
||||
}
|
||||
|
||||
func (bind *NativeBind) ReceiveIPv6(buff []byte) (int, Endpoint, error) {
|
||||
func (bind *nativeBind) ReceiveIPv6(buff []byte) (int, Endpoint, error) {
|
||||
var end NativeEndpoint
|
||||
if bind.sock6 == -1 {
|
||||
return 0, nil, syscall.EAFNOSUPPORT
|
||||
}
|
||||
n, err := receive6(
|
||||
bind.sock6,
|
||||
buff,
|
||||
@@ -207,8 +245,11 @@ func (bind *NativeBind) ReceiveIPv6(buff []byte) (int, Endpoint, error) {
|
||||
return n, &end, err
|
||||
}
|
||||
|
||||
func (bind *NativeBind) ReceiveIPv4(buff []byte) (int, Endpoint, error) {
|
||||
func (bind *nativeBind) ReceiveIPv4(buff []byte) (int, Endpoint, error) {
|
||||
var end NativeEndpoint
|
||||
if bind.sock4 == -1 {
|
||||
return 0, nil, syscall.EAFNOSUPPORT
|
||||
}
|
||||
n, err := receive4(
|
||||
bind.sock4,
|
||||
buff,
|
||||
@@ -217,11 +258,17 @@ func (bind *NativeBind) ReceiveIPv4(buff []byte) (int, Endpoint, error) {
|
||||
return n, &end, err
|
||||
}
|
||||
|
||||
func (bind *NativeBind) Send(buff []byte, end Endpoint) error {
|
||||
func (bind *nativeBind) Send(buff []byte, end Endpoint) error {
|
||||
nend := end.(*NativeEndpoint)
|
||||
if !nend.isV6 {
|
||||
if bind.sock4 == -1 {
|
||||
return syscall.EAFNOSUPPORT
|
||||
}
|
||||
return send4(bind.sock4, nend, buff)
|
||||
} else {
|
||||
if bind.sock6 == -1 {
|
||||
return syscall.EAFNOSUPPORT
|
||||
}
|
||||
return send6(bind.sock6, nend, buff)
|
||||
}
|
||||
}
|
||||
@@ -309,7 +356,7 @@ func create4(port uint16) (int, uint16, error) {
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
return -1, 0, err
|
||||
return FD_ERR, 0, err
|
||||
}
|
||||
|
||||
addr := unix.SockaddrInet4{
|
||||
@@ -340,7 +387,7 @@ func create4(port uint16) (int, uint16, error) {
|
||||
return unix.Bind(fd, &addr)
|
||||
}(); err != nil {
|
||||
unix.Close(fd)
|
||||
return -1, 0, err
|
||||
return FD_ERR, 0, err
|
||||
}
|
||||
|
||||
return fd, uint16(addr.Port), err
|
||||
@@ -357,7 +404,7 @@ func create6(port uint16) (int, uint16, error) {
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
return -1, 0, err
|
||||
return FD_ERR, 0, err
|
||||
}
|
||||
|
||||
// set sockopts and bind
|
||||
@@ -399,7 +446,7 @@ func create6(port uint16) (int, uint16, error) {
|
||||
|
||||
}(); err != nil {
|
||||
unix.Close(fd)
|
||||
return -1, 0, err
|
||||
return FD_ERR, 0, err
|
||||
}
|
||||
|
||||
return fd, uint16(addr.Port), err
|
||||
@@ -545,12 +592,13 @@ func receive6(sock int, buff []byte, end *NativeEndpoint) (int, error) {
|
||||
return size, nil
|
||||
}
|
||||
|
||||
func (bind *NativeBind) routineRouteListener(device *Device) {
|
||||
func (bind *nativeBind) routineRouteListener(device *Device) {
|
||||
type peerEndpointPtr struct {
|
||||
peer *Peer
|
||||
endpoint *Endpoint
|
||||
}
|
||||
var reqPeer map[uint32]peerEndpointPtr
|
||||
var reqPeerLock sync.Mutex
|
||||
|
||||
defer unix.Close(bind.netlinkSock)
|
||||
|
||||
@@ -559,7 +607,7 @@ func (bind *NativeBind) routineRouteListener(device *Device) {
|
||||
var msgn int
|
||||
for {
|
||||
msgn, _, _, _, err = unix.Recvmsg(bind.netlinkSock, msg[:], nil, 0)
|
||||
if err == nil || !rwcancel.ErrorIsEAGAIN(err) {
|
||||
if err == nil || !rwcancel.RetryAfterError(err) {
|
||||
break
|
||||
}
|
||||
if !bind.netlinkCancel.ReadyRead() {
|
||||
@@ -580,7 +628,7 @@ func (bind *NativeBind) routineRouteListener(device *Device) {
|
||||
|
||||
switch hdr.Type {
|
||||
case unix.RTM_NEWROUTE, unix.RTM_DELROUTE:
|
||||
if hdr.Seq <= MaxPeers {
|
||||
if hdr.Seq <= MaxPeers && hdr.Seq > 0 {
|
||||
if uint(len(remain)) < uint(hdr.Len) {
|
||||
break
|
||||
}
|
||||
@@ -596,42 +644,47 @@ func (bind *NativeBind) routineRouteListener(device *Device) {
|
||||
}
|
||||
if attrhdr.Type == unix.RTA_OIF && attrhdr.Len == unix.SizeofRtAttr+4 {
|
||||
ifidx := *(*uint32)(unsafe.Pointer(&attr[unix.SizeofRtAttr]))
|
||||
reqPeerLock.Lock()
|
||||
if reqPeer == nil {
|
||||
reqPeerLock.Unlock()
|
||||
break
|
||||
}
|
||||
pePtr, ok := reqPeer[hdr.Seq]
|
||||
reqPeerLock.Unlock()
|
||||
if !ok {
|
||||
break
|
||||
}
|
||||
pePtr.peer.mutex.Lock()
|
||||
pePtr.peer.Lock()
|
||||
if &pePtr.peer.endpoint != pePtr.endpoint {
|
||||
pePtr.peer.mutex.Unlock()
|
||||
pePtr.peer.Unlock()
|
||||
break
|
||||
}
|
||||
if uint32(pePtr.peer.endpoint.(*NativeEndpoint).src4().ifindex) == ifidx {
|
||||
pePtr.peer.mutex.Unlock()
|
||||
pePtr.peer.Unlock()
|
||||
break
|
||||
}
|
||||
pePtr.peer.endpoint.(*NativeEndpoint).ClearSrc()
|
||||
pePtr.peer.mutex.Unlock()
|
||||
pePtr.peer.Unlock()
|
||||
}
|
||||
attr = attr[attrhdr.Len:]
|
||||
}
|
||||
}
|
||||
break
|
||||
}
|
||||
reqPeerLock.Lock()
|
||||
reqPeer = make(map[uint32]peerEndpointPtr)
|
||||
reqPeerLock.Unlock()
|
||||
go func() {
|
||||
device.peers.mutex.RLock()
|
||||
device.peers.RLock()
|
||||
i := uint32(1)
|
||||
for _, peer := range device.peers.keyMap {
|
||||
peer.mutex.RLock()
|
||||
peer.RLock()
|
||||
if peer.endpoint == nil || peer.endpoint.(*NativeEndpoint) == nil {
|
||||
peer.mutex.RUnlock()
|
||||
peer.RUnlock()
|
||||
continue
|
||||
}
|
||||
if peer.endpoint.(*NativeEndpoint).isV6 || peer.endpoint.(*NativeEndpoint).src4().ifindex == 0 {
|
||||
peer.mutex.RUnlock()
|
||||
peer.RUnlock()
|
||||
break
|
||||
}
|
||||
nlmsg := struct {
|
||||
@@ -671,18 +724,20 @@ func (bind *NativeBind) routineRouteListener(device *Device) {
|
||||
uint32(bind.lastMark),
|
||||
}
|
||||
nlmsg.hdr.Len = uint32(unsafe.Sizeof(nlmsg))
|
||||
reqPeerLock.Lock()
|
||||
reqPeer[i] = peerEndpointPtr{
|
||||
peer: peer,
|
||||
endpoint: &peer.endpoint,
|
||||
}
|
||||
peer.mutex.RUnlock()
|
||||
reqPeerLock.Unlock()
|
||||
peer.RUnlock()
|
||||
i++
|
||||
_, err := bind.netlinkCancel.Write((*[unsafe.Sizeof(nlmsg)]byte)(unsafe.Pointer(&nlmsg))[:])
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
}
|
||||
device.peers.mutex.RUnlock()
|
||||
device.peers.RUnlock()
|
||||
}()
|
||||
}
|
||||
remain = remain[hdr.Len:]
|
||||
@@ -1,10 +1,9 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2018 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
|
||||
* Copyright (C) 2017-2018 Mathias N. Hall-Andersen <mathias@hall-andersen.dk>.
|
||||
* Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package main
|
||||
package device
|
||||
|
||||
import (
|
||||
"time"
|
||||
@@ -27,18 +26,14 @@ const (
|
||||
PaddingMultiple = 16
|
||||
)
|
||||
|
||||
/* Implementation specific constants */
|
||||
|
||||
const (
|
||||
QueueOutboundSize = 1024
|
||||
QueueInboundSize = 1024
|
||||
QueueHandshakeSize = 1024
|
||||
MaxSegmentSize = (1 << 16) - 1 // largest possible UDP datagram
|
||||
MinMessageSize = MessageKeepaliveSize // minimum size of transport message (keepalive)
|
||||
MaxMessageSize = MaxSegmentSize // maximum size of transport message
|
||||
MaxContentSize = MaxSegmentSize - MessageTransportSize // maximum size of transport message content
|
||||
)
|
||||
|
||||
/* Implementation constants */
|
||||
|
||||
const (
|
||||
UnderLoadQueueSize = QueueHandshakeSize / 8
|
||||
UnderLoadAfterTime = time.Second // how long does the device remain under load after detected
|
||||
@@ -1,13 +1,11 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2018 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
|
||||
* Copyright (C) 2017-2018 Mathias N. Hall-Andersen <mathias@hall-andersen.dk>.
|
||||
* Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package main
|
||||
package device
|
||||
|
||||
import (
|
||||
"./xchacha20poly1305"
|
||||
"crypto/hmac"
|
||||
"crypto/rand"
|
||||
"golang.org/x/crypto/blake2s"
|
||||
@@ -17,7 +15,7 @@ import (
|
||||
)
|
||||
|
||||
type CookieChecker struct {
|
||||
mutex sync.RWMutex
|
||||
sync.RWMutex
|
||||
mac1 struct {
|
||||
key [blake2s.Size]byte
|
||||
}
|
||||
@@ -29,7 +27,7 @@ type CookieChecker struct {
|
||||
}
|
||||
|
||||
type CookieGenerator struct {
|
||||
mutex sync.RWMutex
|
||||
sync.RWMutex
|
||||
mac1 struct {
|
||||
key [blake2s.Size]byte
|
||||
}
|
||||
@@ -43,8 +41,8 @@ type CookieGenerator struct {
|
||||
}
|
||||
|
||||
func (st *CookieChecker) Init(pk NoisePublicKey) {
|
||||
st.mutex.Lock()
|
||||
defer st.mutex.Unlock()
|
||||
st.Lock()
|
||||
defer st.Unlock()
|
||||
|
||||
// mac1 state
|
||||
|
||||
@@ -68,8 +66,8 @@ func (st *CookieChecker) Init(pk NoisePublicKey) {
|
||||
}
|
||||
|
||||
func (st *CookieChecker) CheckMAC1(msg []byte) bool {
|
||||
st.mutex.RLock()
|
||||
defer st.mutex.RUnlock()
|
||||
st.RLock()
|
||||
defer st.RUnlock()
|
||||
|
||||
size := len(msg)
|
||||
smac2 := size - blake2s.Size128
|
||||
@@ -85,8 +83,8 @@ func (st *CookieChecker) CheckMAC1(msg []byte) bool {
|
||||
}
|
||||
|
||||
func (st *CookieChecker) CheckMAC2(msg []byte, src []byte) bool {
|
||||
st.mutex.RLock()
|
||||
defer st.mutex.RUnlock()
|
||||
st.RLock()
|
||||
defer st.RUnlock()
|
||||
|
||||
if time.Now().Sub(st.mac2.secretSet) > CookieRefreshTime {
|
||||
return false
|
||||
@@ -121,21 +119,21 @@ func (st *CookieChecker) CreateReply(
|
||||
src []byte,
|
||||
) (*MessageCookieReply, error) {
|
||||
|
||||
st.mutex.RLock()
|
||||
st.RLock()
|
||||
|
||||
// refresh cookie secret
|
||||
|
||||
if time.Now().Sub(st.mac2.secretSet) > CookieRefreshTime {
|
||||
st.mutex.RUnlock()
|
||||
st.mutex.Lock()
|
||||
st.RUnlock()
|
||||
st.Lock()
|
||||
_, err := rand.Read(st.mac2.secret[:])
|
||||
if err != nil {
|
||||
st.mutex.Unlock()
|
||||
st.Unlock()
|
||||
return nil, err
|
||||
}
|
||||
st.mac2.secretSet = time.Now()
|
||||
st.mutex.Unlock()
|
||||
st.mutex.RLock()
|
||||
st.Unlock()
|
||||
st.RLock()
|
||||
}
|
||||
|
||||
// derive cookie
|
||||
@@ -160,26 +158,21 @@ func (st *CookieChecker) CreateReply(
|
||||
|
||||
_, err := rand.Read(reply.Nonce[:])
|
||||
if err != nil {
|
||||
st.mutex.RUnlock()
|
||||
st.RUnlock()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
xchacha20poly1305.Encrypt(
|
||||
reply.Cookie[:0],
|
||||
&reply.Nonce,
|
||||
cookie[:],
|
||||
msg[smac1:smac2],
|
||||
&st.mac2.encryptionKey,
|
||||
)
|
||||
xchapoly, _ := chacha20poly1305.NewX(st.mac2.encryptionKey[:])
|
||||
xchapoly.Seal(reply.Cookie[:0], reply.Nonce[:], cookie[:], msg[smac1:smac2])
|
||||
|
||||
st.mutex.RUnlock()
|
||||
st.RUnlock()
|
||||
|
||||
return reply, nil
|
||||
}
|
||||
|
||||
func (st *CookieGenerator) Init(pk NoisePublicKey) {
|
||||
st.mutex.Lock()
|
||||
defer st.mutex.Unlock()
|
||||
st.Lock()
|
||||
defer st.Unlock()
|
||||
|
||||
func() {
|
||||
hash, _ := blake2s.New256(nil)
|
||||
@@ -199,8 +192,8 @@ func (st *CookieGenerator) Init(pk NoisePublicKey) {
|
||||
}
|
||||
|
||||
func (st *CookieGenerator) ConsumeReply(msg *MessageCookieReply) bool {
|
||||
st.mutex.Lock()
|
||||
defer st.mutex.Unlock()
|
||||
st.Lock()
|
||||
defer st.Unlock()
|
||||
|
||||
if !st.mac2.hasLastMAC1 {
|
||||
return false
|
||||
@@ -208,13 +201,8 @@ func (st *CookieGenerator) ConsumeReply(msg *MessageCookieReply) bool {
|
||||
|
||||
var cookie [blake2s.Size128]byte
|
||||
|
||||
_, err := xchacha20poly1305.Decrypt(
|
||||
cookie[:0],
|
||||
&msg.Nonce,
|
||||
msg.Cookie[:],
|
||||
st.mac2.lastMAC1[:],
|
||||
&st.mac2.encryptionKey,
|
||||
)
|
||||
xchapoly, _ := chacha20poly1305.NewX(st.mac2.encryptionKey[:])
|
||||
_, err := xchapoly.Open(cookie[:0], msg.Nonce[:], msg.Cookie[:], st.mac2.lastMAC1[:])
|
||||
|
||||
if err != nil {
|
||||
return false
|
||||
@@ -235,8 +223,8 @@ func (st *CookieGenerator) AddMacs(msg []byte) {
|
||||
mac1 := msg[smac1:smac2]
|
||||
mac2 := msg[smac2:]
|
||||
|
||||
st.mutex.Lock()
|
||||
defer st.mutex.Unlock()
|
||||
st.Lock()
|
||||
defer st.Unlock()
|
||||
|
||||
// set mac1
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2018 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
|
||||
* Copyright (C) 2017-2018 Mathias N. Hall-Andersen <mathias@hall-andersen.dk>.
|
||||
* Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package main
|
||||
package device
|
||||
|
||||
import (
|
||||
"testing"
|
||||
@@ -1,13 +1,13 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2018 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
|
||||
* Copyright (C) 2017-2018 Mathias N. Hall-Andersen <mathias@hall-andersen.dk>.
|
||||
* Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package main
|
||||
package device
|
||||
|
||||
import (
|
||||
"./ratelimiter"
|
||||
"golang.zx2c4.com/wireguard/ratelimiter"
|
||||
"golang.zx2c4.com/wireguard/tun"
|
||||
"runtime"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
@@ -29,26 +29,28 @@ type Device struct {
|
||||
state struct {
|
||||
starting sync.WaitGroup
|
||||
stopping sync.WaitGroup
|
||||
mutex sync.Mutex
|
||||
sync.Mutex
|
||||
changing AtomicBool
|
||||
current bool
|
||||
}
|
||||
|
||||
net struct {
|
||||
mutex sync.RWMutex
|
||||
starting sync.WaitGroup
|
||||
stopping sync.WaitGroup
|
||||
sync.RWMutex
|
||||
bind Bind // bind interface
|
||||
port uint16 // listening port
|
||||
fwmark uint32 // mark value (0 = disabled)
|
||||
}
|
||||
|
||||
staticIdentity struct {
|
||||
mutex sync.RWMutex
|
||||
sync.RWMutex
|
||||
privateKey NoisePrivateKey
|
||||
publicKey NoisePublicKey
|
||||
}
|
||||
|
||||
peers struct {
|
||||
mutex sync.RWMutex
|
||||
sync.RWMutex
|
||||
keyMap map[NoisePublicKey]*Peer
|
||||
}
|
||||
|
||||
@@ -64,7 +66,12 @@ type Device struct {
|
||||
}
|
||||
|
||||
pool struct {
|
||||
messageBuffers sync.Pool
|
||||
messageBufferPool *sync.Pool
|
||||
messageBufferReuseChan chan *[MaxMessageSize]byte
|
||||
inboundElementPool *sync.Pool
|
||||
inboundElementReuseChan chan *QueueInboundElement
|
||||
outboundElementPool *sync.Pool
|
||||
outboundElementReuseChan chan *QueueOutboundElement
|
||||
}
|
||||
|
||||
queue struct {
|
||||
@@ -78,7 +85,7 @@ type Device struct {
|
||||
}
|
||||
|
||||
tun struct {
|
||||
device TUNDevice
|
||||
device tun.TUNDevice
|
||||
mtu int32
|
||||
}
|
||||
}
|
||||
@@ -86,7 +93,7 @@ type Device struct {
|
||||
/* Converts the peer into a "zombie", which remains in the peer map,
|
||||
* but processes no packets and does not exists in the routing table.
|
||||
*
|
||||
* Must hold device.peers.mutex.
|
||||
* Must hold device.peers.Mutex
|
||||
*/
|
||||
func unsafeRemovePeer(device *Device, peer *Peer, key NoisePublicKey) {
|
||||
|
||||
@@ -110,13 +117,13 @@ func deviceUpdateState(device *Device) {
|
||||
|
||||
// compare to current state of device
|
||||
|
||||
device.state.mutex.Lock()
|
||||
device.state.Lock()
|
||||
|
||||
newIsUp := device.isUp.Get()
|
||||
|
||||
if newIsUp == device.state.current {
|
||||
device.state.changing.Set(false)
|
||||
device.state.mutex.Unlock()
|
||||
device.state.Unlock()
|
||||
return
|
||||
}
|
||||
|
||||
@@ -128,26 +135,29 @@ func deviceUpdateState(device *Device) {
|
||||
device.isUp.Set(false)
|
||||
break
|
||||
}
|
||||
device.peers.mutex.RLock()
|
||||
device.peers.RLock()
|
||||
for _, peer := range device.peers.keyMap {
|
||||
peer.Start()
|
||||
if peer.persistentKeepaliveInterval > 0 {
|
||||
peer.SendKeepalive()
|
||||
}
|
||||
device.peers.mutex.RUnlock()
|
||||
}
|
||||
device.peers.RUnlock()
|
||||
|
||||
case false:
|
||||
device.BindClose()
|
||||
device.peers.mutex.RLock()
|
||||
device.peers.RLock()
|
||||
for _, peer := range device.peers.keyMap {
|
||||
peer.Stop()
|
||||
}
|
||||
device.peers.mutex.RUnlock()
|
||||
device.peers.RUnlock()
|
||||
}
|
||||
|
||||
// update state variables
|
||||
|
||||
device.state.current = newIsUp
|
||||
device.state.changing.Set(false)
|
||||
device.state.mutex.Unlock()
|
||||
device.state.Unlock()
|
||||
|
||||
// check for state change in the mean time
|
||||
|
||||
@@ -162,16 +172,12 @@ func (device *Device) Up() {
|
||||
return
|
||||
}
|
||||
|
||||
device.state.mutex.Lock()
|
||||
device.isUp.Set(true)
|
||||
device.state.mutex.Unlock()
|
||||
deviceUpdateState(device)
|
||||
}
|
||||
|
||||
func (device *Device) Down() {
|
||||
device.state.mutex.Lock()
|
||||
device.isUp.Set(false)
|
||||
device.state.mutex.Unlock()
|
||||
deviceUpdateState(device)
|
||||
}
|
||||
|
||||
@@ -196,11 +202,11 @@ func (device *Device) SetPrivateKey(sk NoisePrivateKey) error {
|
||||
|
||||
// lock required resources
|
||||
|
||||
device.staticIdentity.mutex.Lock()
|
||||
defer device.staticIdentity.mutex.Unlock()
|
||||
device.staticIdentity.Lock()
|
||||
defer device.staticIdentity.Unlock()
|
||||
|
||||
device.peers.mutex.Lock()
|
||||
defer device.peers.mutex.Unlock()
|
||||
device.peers.Lock()
|
||||
defer device.peers.Unlock()
|
||||
|
||||
for _, peer := range device.peers.keyMap {
|
||||
peer.handshake.mutex.RLock()
|
||||
@@ -244,15 +250,7 @@ func (device *Device) SetPrivateKey(sk NoisePrivateKey) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (device *Device) GetMessageBuffer() *[MaxMessageSize]byte {
|
||||
return device.pool.messageBuffers.Get().(*[MaxMessageSize]byte)
|
||||
}
|
||||
|
||||
func (device *Device) PutMessageBuffer(msg *[MaxMessageSize]byte) {
|
||||
device.pool.messageBuffers.Put(msg)
|
||||
}
|
||||
|
||||
func NewDevice(tun TUNDevice, logger *Logger) *Device {
|
||||
func NewDevice(tunDevice tun.TUNDevice, logger *Logger) *Device {
|
||||
device := new(Device)
|
||||
|
||||
device.isUp.Set(false)
|
||||
@@ -260,7 +258,7 @@ func NewDevice(tun TUNDevice, logger *Logger) *Device {
|
||||
|
||||
device.log = logger
|
||||
|
||||
device.tun.device = tun
|
||||
device.tun.device = tunDevice
|
||||
mtu, err := device.tun.device.MTU()
|
||||
if err != nil {
|
||||
logger.Error.Println("Trouble determining MTU, assuming default:", err)
|
||||
@@ -276,11 +274,7 @@ func NewDevice(tun TUNDevice, logger *Logger) *Device {
|
||||
device.indexTable.Init()
|
||||
device.allowedips.Reset()
|
||||
|
||||
device.pool.messageBuffers = sync.Pool{
|
||||
New: func() interface{} {
|
||||
return new([MaxMessageSize]byte)
|
||||
},
|
||||
}
|
||||
device.PopulatePools()
|
||||
|
||||
// create queues
|
||||
|
||||
@@ -319,15 +313,15 @@ func NewDevice(tun TUNDevice, logger *Logger) *Device {
|
||||
}
|
||||
|
||||
func (device *Device) LookupPeer(pk NoisePublicKey) *Peer {
|
||||
device.peers.mutex.RLock()
|
||||
defer device.peers.mutex.RUnlock()
|
||||
device.peers.RLock()
|
||||
defer device.peers.RUnlock()
|
||||
|
||||
return device.peers.keyMap[pk]
|
||||
}
|
||||
|
||||
func (device *Device) RemovePeer(key NoisePublicKey) {
|
||||
device.peers.mutex.Lock()
|
||||
defer device.peers.mutex.Unlock()
|
||||
device.peers.Lock()
|
||||
defer device.peers.Unlock()
|
||||
|
||||
// stop peer and remove from routing
|
||||
|
||||
@@ -338,8 +332,8 @@ func (device *Device) RemovePeer(key NoisePublicKey) {
|
||||
}
|
||||
|
||||
func (device *Device) RemoveAllPeers() {
|
||||
device.peers.mutex.Lock()
|
||||
defer device.peers.mutex.Unlock()
|
||||
device.peers.Lock()
|
||||
defer device.peers.Unlock()
|
||||
|
||||
for key, peer := range device.peers.keyMap {
|
||||
unsafeRemovePeer(device, peer, key)
|
||||
@@ -376,8 +370,8 @@ func (device *Device) Close() {
|
||||
|
||||
device.log.Info.Println("Device closing")
|
||||
device.state.changing.Set(true)
|
||||
device.state.mutex.Lock()
|
||||
defer device.state.mutex.Unlock()
|
||||
device.state.Lock()
|
||||
defer device.state.Unlock()
|
||||
|
||||
device.tun.device.Close()
|
||||
device.BindClose()
|
||||
@@ -386,10 +380,11 @@ func (device *Device) Close() {
|
||||
|
||||
close(device.signals.stop)
|
||||
|
||||
device.RemoveAllPeers()
|
||||
|
||||
device.state.stopping.Wait()
|
||||
device.FlushPacketQueues()
|
||||
|
||||
device.RemoveAllPeers()
|
||||
device.rate.limiter.Close()
|
||||
|
||||
device.state.changing.Set(false)
|
||||
@@ -1,10 +1,9 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2018 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
|
||||
* Copyright (C) 2017-2018 Mathias N. Hall-Andersen <mathias@hall-andersen.dk>.
|
||||
* Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package main
|
||||
package device
|
||||
|
||||
/* Create two device instances and simulate full WireGuard interaction
|
||||
* without network dependencies
|
||||
@@ -26,8 +25,8 @@ func TestDevice(t *testing.T) {
|
||||
t.Error("failed to create tun:", err.Error())
|
||||
}
|
||||
|
||||
println(tun1)
|
||||
println(tun2)
|
||||
_ = tun1
|
||||
_ = tun2
|
||||
|
||||
// prepare endpoints
|
||||
|
||||
@@ -41,8 +40,8 @@ func TestDevice(t *testing.T) {
|
||||
t.Error("failed to create endpoint:", err.Error())
|
||||
}
|
||||
|
||||
println(end1)
|
||||
println(end2)
|
||||
_ = end1
|
||||
_ = end2
|
||||
|
||||
// create binds
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2018 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
|
||||
* Copyright (C) 2017-2018 Mathias N. Hall-Andersen <mathias@hall-andersen.dk>.
|
||||
* Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package main
|
||||
package device
|
||||
|
||||
import (
|
||||
"math/rand"
|
||||
@@ -1,10 +1,9 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2018 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
|
||||
* Copyright (C) 2017-2018 Mathias N. Hall-Andersen <mathias@hall-andersen.dk>.
|
||||
* Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package main
|
||||
package device
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
@@ -19,7 +18,7 @@ type IndexTableEntry struct {
|
||||
}
|
||||
|
||||
type IndexTable struct {
|
||||
mutex sync.RWMutex
|
||||
sync.RWMutex
|
||||
table map[uint32]IndexTableEntry
|
||||
}
|
||||
|
||||
@@ -30,20 +29,20 @@ func randUint32() (uint32, error) {
|
||||
}
|
||||
|
||||
func (table *IndexTable) Init() {
|
||||
table.mutex.Lock()
|
||||
defer table.mutex.Unlock()
|
||||
table.Lock()
|
||||
defer table.Unlock()
|
||||
table.table = make(map[uint32]IndexTableEntry)
|
||||
}
|
||||
|
||||
func (table *IndexTable) Delete(index uint32) {
|
||||
table.mutex.Lock()
|
||||
defer table.mutex.Unlock()
|
||||
table.Lock()
|
||||
defer table.Unlock()
|
||||
delete(table.table, index)
|
||||
}
|
||||
|
||||
func (table *IndexTable) SwapIndexForKeypair(index uint32, keypair *Keypair) {
|
||||
table.mutex.Lock()
|
||||
defer table.mutex.Unlock()
|
||||
table.Lock()
|
||||
defer table.Unlock()
|
||||
entry, ok := table.table[index]
|
||||
if !ok {
|
||||
return
|
||||
@@ -66,19 +65,19 @@ func (table *IndexTable) NewIndexForHandshake(peer *Peer, handshake *Handshake)
|
||||
|
||||
// check if index used
|
||||
|
||||
table.mutex.RLock()
|
||||
table.RLock()
|
||||
_, ok := table.table[index]
|
||||
table.mutex.RUnlock()
|
||||
table.RUnlock()
|
||||
if ok {
|
||||
continue
|
||||
}
|
||||
|
||||
// check again while locked
|
||||
|
||||
table.mutex.Lock()
|
||||
table.Lock()
|
||||
_, found := table.table[index]
|
||||
if found {
|
||||
table.mutex.Unlock()
|
||||
table.Unlock()
|
||||
continue
|
||||
}
|
||||
table.table[index] = IndexTableEntry{
|
||||
@@ -86,13 +85,13 @@ func (table *IndexTable) NewIndexForHandshake(peer *Peer, handshake *Handshake)
|
||||
handshake: handshake,
|
||||
keypair: nil,
|
||||
}
|
||||
table.mutex.Unlock()
|
||||
table.Unlock()
|
||||
return index, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (table *IndexTable) Lookup(id uint32) IndexTableEntry {
|
||||
table.mutex.RLock()
|
||||
defer table.mutex.RUnlock()
|
||||
table.RLock()
|
||||
defer table.RUnlock()
|
||||
return table.table[id]
|
||||
}
|
||||
@@ -1,10 +1,9 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2018 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
|
||||
* Copyright (C) 2017-2018 Mathias N. Hall-Andersen <mathias@hall-andersen.dk>.
|
||||
* Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package main
|
||||
package device
|
||||
|
||||
import (
|
||||
"net"
|
||||
@@ -1,10 +1,9 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2018 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
|
||||
* Copyright (C) 2017-2018 Mathias N. Hall-Andersen <mathias@hall-andersen.dk>.
|
||||
* Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package main
|
||||
package device
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
@@ -1,13 +1,13 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2018 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
|
||||
* Copyright (C) 2017-2018 Mathias N. Hall-Andersen <mathias@hall-andersen.dk>.
|
||||
* Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package main
|
||||
package device
|
||||
|
||||
import (
|
||||
"crypto/cipher"
|
||||
"golang.zx2c4.com/wireguard/replay"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
@@ -23,7 +23,7 @@ type Keypair struct {
|
||||
sendNonce uint64
|
||||
send cipher.AEAD
|
||||
receive cipher.AEAD
|
||||
replayFilter ReplayFilter
|
||||
replayFilter replay.ReplayFilter
|
||||
isInitiator bool
|
||||
created time.Time
|
||||
localIndex uint32
|
||||
@@ -31,15 +31,15 @@ type Keypair struct {
|
||||
}
|
||||
|
||||
type Keypairs struct {
|
||||
mutex sync.RWMutex
|
||||
sync.RWMutex
|
||||
current *Keypair
|
||||
previous *Keypair
|
||||
next *Keypair
|
||||
}
|
||||
|
||||
func (kp *Keypairs) Current() *Keypair {
|
||||
kp.mutex.RLock()
|
||||
defer kp.mutex.RUnlock()
|
||||
kp.RLock()
|
||||
defer kp.RUnlock()
|
||||
return kp.current
|
||||
}
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2018 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
|
||||
* Copyright (C) 2017-2018 Mathias N. Hall-Andersen <mathias@hall-andersen.dk>.
|
||||
* Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package main
|
||||
package device
|
||||
|
||||
import (
|
||||
"io"
|
||||
12
device/mark_default.go
Normal file
12
device/mark_default.go
Normal file
@@ -0,0 +1,12 @@
|
||||
// +build !linux,!openbsd,!freebsd
|
||||
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package device
|
||||
|
||||
func (bind *nativeBind) SetMark(mark uint32) error {
|
||||
return nil
|
||||
}
|
||||
64
device/mark_unix.go
Normal file
64
device/mark_unix.go
Normal file
@@ -0,0 +1,64 @@
|
||||
// +build android openbsd freebsd
|
||||
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package device
|
||||
|
||||
import (
|
||||
"golang.org/x/sys/unix"
|
||||
"runtime"
|
||||
)
|
||||
|
||||
var fwmarkIoctl int
|
||||
|
||||
func init() {
|
||||
switch runtime.GOOS {
|
||||
case "linux", "android":
|
||||
fwmarkIoctl = 36 /* unix.SO_MARK */
|
||||
case "freebsd":
|
||||
fwmarkIoctl = 0x1015 /* unix.SO_USER_COOKIE */
|
||||
case "openbsd":
|
||||
fwmarkIoctl = 0x1021 /* unix.SO_RTABLE */
|
||||
}
|
||||
}
|
||||
|
||||
func (bind *nativeBind) SetMark(mark uint32) error {
|
||||
var operr error
|
||||
if fwmarkIoctl == 0 {
|
||||
return nil
|
||||
}
|
||||
if bind.ipv4 != nil {
|
||||
fd, err := bind.ipv4.SyscallConn()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = fd.Control(func(fd uintptr) {
|
||||
operr = unix.SetsockoptInt(int(fd), unix.SOL_SOCKET, fwmarkIoctl, int(mark))
|
||||
})
|
||||
if err == nil {
|
||||
err = operr
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if bind.ipv6 != nil {
|
||||
fd, err := bind.ipv6.SyscallConn()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = fd.Control(func(fd uintptr) {
|
||||
operr = unix.SetsockoptInt(int(fd), unix.SOL_SOCKET, fwmarkIoctl, int(mark))
|
||||
})
|
||||
if err == nil {
|
||||
err = operr
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
48
device/misc.go
Normal file
48
device/misc.go
Normal file
@@ -0,0 +1,48 @@
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package device
|
||||
|
||||
import (
|
||||
"sync/atomic"
|
||||
)
|
||||
|
||||
/* Atomic Boolean */
|
||||
|
||||
const (
|
||||
AtomicFalse = int32(iota)
|
||||
AtomicTrue
|
||||
)
|
||||
|
||||
type AtomicBool struct {
|
||||
int32
|
||||
}
|
||||
|
||||
func (a *AtomicBool) Get() bool {
|
||||
return atomic.LoadInt32(&a.int32) == AtomicTrue
|
||||
}
|
||||
|
||||
func (a *AtomicBool) Swap(val bool) bool {
|
||||
flag := AtomicFalse
|
||||
if val {
|
||||
flag = AtomicTrue
|
||||
}
|
||||
return atomic.SwapInt32(&a.int32, flag) == AtomicTrue
|
||||
}
|
||||
|
||||
func (a *AtomicBool) Set(val bool) {
|
||||
flag := AtomicFalse
|
||||
if val {
|
||||
flag = AtomicTrue
|
||||
}
|
||||
atomic.StoreInt32(&a.int32, flag)
|
||||
}
|
||||
|
||||
func min(a, b uint) uint {
|
||||
if a > b {
|
||||
return b
|
||||
}
|
||||
return a
|
||||
}
|
||||
@@ -1,10 +1,9 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2018 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
|
||||
* Copyright (C) 2017-2018 Mathias N. Hall-Andersen <mathias@hall-andersen.dk>.
|
||||
* Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package main
|
||||
package device
|
||||
|
||||
import (
|
||||
"crypto/hmac"
|
||||
@@ -79,12 +78,14 @@ func setZero(arr []byte) {
|
||||
}
|
||||
}
|
||||
|
||||
func newPrivateKey() (sk NoisePrivateKey, err error) {
|
||||
// clamping: https://cr.yp.to/ecdh.html
|
||||
_, err = rand.Read(sk[:])
|
||||
func (sk *NoisePrivateKey) clamp() {
|
||||
sk[0] &= 248
|
||||
sk[31] &= 127
|
||||
sk[31] |= 64
|
||||
sk[31] = (sk[31] & 127) | 64
|
||||
}
|
||||
|
||||
func newPrivateKey() (sk NoisePrivateKey, err error) {
|
||||
_, err = rand.Read(sk[:])
|
||||
sk.clamp()
|
||||
return
|
||||
}
|
||||
|
||||
@@ -1,17 +1,16 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2015-2018 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
|
||||
* Copyright (C) 2017-2018 Mathias N. Hall-Andersen <mathias@hall-andersen.dk>.
|
||||
* Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package main
|
||||
package device
|
||||
|
||||
import (
|
||||
"./tai64n"
|
||||
"errors"
|
||||
"golang.org/x/crypto/blake2s"
|
||||
"golang.org/x/crypto/chacha20poly1305"
|
||||
"golang.org/x/crypto/poly1305"
|
||||
"golang.zx2c4.com/wireguard/tai64n"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
@@ -90,7 +89,7 @@ type MessageTransport struct {
|
||||
type MessageCookieReply struct {
|
||||
Type uint32
|
||||
Receiver uint32
|
||||
Nonce [24]byte
|
||||
Nonce [chacha20poly1305.NonceSizeX]byte
|
||||
Cookie [blake2s.Size128 + poly1305.TagSize]byte
|
||||
}
|
||||
|
||||
@@ -155,8 +154,8 @@ func init() {
|
||||
|
||||
func (device *Device) CreateMessageInitiation(peer *Peer) (*MessageInitiation, error) {
|
||||
|
||||
device.staticIdentity.mutex.RLock()
|
||||
defer device.staticIdentity.mutex.RUnlock()
|
||||
device.staticIdentity.RLock()
|
||||
defer device.staticIdentity.RUnlock()
|
||||
|
||||
handshake := &peer.handshake
|
||||
handshake.mutex.Lock()
|
||||
@@ -242,8 +241,8 @@ func (device *Device) ConsumeMessageInitiation(msg *MessageInitiation) *Peer {
|
||||
return nil
|
||||
}
|
||||
|
||||
device.staticIdentity.mutex.RLock()
|
||||
defer device.staticIdentity.mutex.RUnlock()
|
||||
device.staticIdentity.RLock()
|
||||
defer device.staticIdentity.RUnlock()
|
||||
|
||||
mixHash(&hash, &InitialHash, device.staticIdentity.publicKey[:])
|
||||
mixHash(&hash, &hash, msg.Ephemeral[:])
|
||||
@@ -424,8 +423,8 @@ func (device *Device) ConsumeMessageResponse(msg *MessageResponse) *Peer {
|
||||
|
||||
// lock private key for reading
|
||||
|
||||
device.staticIdentity.mutex.RLock()
|
||||
defer device.staticIdentity.mutex.RUnlock()
|
||||
device.staticIdentity.RLock()
|
||||
defer device.staticIdentity.RUnlock()
|
||||
|
||||
// finish 3-way DH
|
||||
|
||||
@@ -555,8 +554,8 @@ func (peer *Peer) BeginSymmetricSession() error {
|
||||
// rotate key pairs
|
||||
|
||||
keypairs := &peer.keypairs
|
||||
keypairs.mutex.Lock()
|
||||
defer keypairs.mutex.Unlock()
|
||||
keypairs.Lock()
|
||||
defer keypairs.Unlock()
|
||||
|
||||
previous := keypairs.previous
|
||||
next := keypairs.next
|
||||
@@ -587,8 +586,8 @@ func (peer *Peer) ReceivedWithKeypair(receivedKeypair *Keypair) bool {
|
||||
if keypairs.next != receivedKeypair {
|
||||
return false
|
||||
}
|
||||
keypairs.mutex.Lock()
|
||||
defer keypairs.mutex.Unlock()
|
||||
keypairs.Lock()
|
||||
defer keypairs.Unlock()
|
||||
if keypairs.next != receivedKeypair {
|
||||
return false
|
||||
}
|
||||
@@ -1,10 +1,9 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2018 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
|
||||
* Copyright (C) 2017-2018 Mathias N. Hall-Andersen <mathias@hall-andersen.dk>.
|
||||
* Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package main
|
||||
package device
|
||||
|
||||
import (
|
||||
"crypto/subtle"
|
||||
@@ -46,8 +45,10 @@ func (key NoisePrivateKey) Equals(tar NoisePrivateKey) bool {
|
||||
return subtle.ConstantTimeCompare(key[:], tar[:]) == 1
|
||||
}
|
||||
|
||||
func (key *NoisePrivateKey) FromHex(src string) error {
|
||||
return loadExactHex(key[:], src)
|
||||
func (key *NoisePrivateKey) FromHex(src string) (err error) {
|
||||
err = loadExactHex(key[:], src)
|
||||
key.clamp()
|
||||
return
|
||||
}
|
||||
|
||||
func (key NoisePrivateKey) ToHex() string {
|
||||
@@ -1,10 +1,9 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2018 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
|
||||
* Copyright (C) 2017-2018 Mathias N. Hall-Andersen <mathias@hall-andersen.dk>.
|
||||
* Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package main
|
||||
package device
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
@@ -58,6 +57,7 @@ func TestNoiseHandshake(t *testing.T) {
|
||||
packet := make([]byte, 0, 256)
|
||||
writer := bytes.NewBuffer(packet)
|
||||
err = binary.Write(writer, binary.LittleEndian, msg1)
|
||||
assertNil(t, err)
|
||||
peer := dev2.ConsumeMessageInitiation(msg1)
|
||||
if peer == nil {
|
||||
t.Fatal("handshake failed at initiation message")
|
||||
@@ -1,10 +1,9 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2018 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
|
||||
* Copyright (C) 2017-2018 Mathias N. Hall-Andersen <mathias@hall-andersen.dk>.
|
||||
* Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package main
|
||||
package device
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
@@ -20,7 +19,7 @@ const (
|
||||
|
||||
type Peer struct {
|
||||
isRunning AtomicBool
|
||||
mutex sync.RWMutex // Mostly protects endpoint, but is generally taken whenever we modify peer
|
||||
sync.RWMutex // Mostly protects endpoint, but is generally taken whenever we modify peer
|
||||
keypairs Keypairs
|
||||
handshake Handshake
|
||||
device *Device
|
||||
@@ -40,9 +39,9 @@ type Peer struct {
|
||||
newHandshake *Timer
|
||||
zeroKeyMaterial *Timer
|
||||
persistentKeepalive *Timer
|
||||
handshakeAttempts uint
|
||||
needAnotherKeepalive bool
|
||||
sentLastMinuteHandshake bool
|
||||
handshakeAttempts uint32
|
||||
needAnotherKeepalive AtomicBool
|
||||
sentLastMinuteHandshake AtomicBool
|
||||
}
|
||||
|
||||
signals struct {
|
||||
@@ -54,11 +53,11 @@ type Peer struct {
|
||||
nonce chan *QueueOutboundElement // nonce / pre-handshake queue
|
||||
outbound chan *QueueOutboundElement // sequential ordering of work
|
||||
inbound chan *QueueInboundElement // sequential ordering of work
|
||||
packetInNonceQueueIsAwaitingKey bool
|
||||
packetInNonceQueueIsAwaitingKey AtomicBool
|
||||
}
|
||||
|
||||
routines struct {
|
||||
mutex sync.Mutex // held when stopping / starting routines
|
||||
sync.Mutex // held when stopping / starting routines
|
||||
starting sync.WaitGroup // routines pending start
|
||||
stopping sync.WaitGroup // routines pending stop
|
||||
stop chan struct{} // size 0, stop all go routines in peer
|
||||
@@ -75,11 +74,11 @@ func (device *Device) NewPeer(pk NoisePublicKey) (*Peer, error) {
|
||||
|
||||
// lock resources
|
||||
|
||||
device.staticIdentity.mutex.RLock()
|
||||
defer device.staticIdentity.mutex.RUnlock()
|
||||
device.staticIdentity.RLock()
|
||||
defer device.staticIdentity.RUnlock()
|
||||
|
||||
device.peers.mutex.Lock()
|
||||
defer device.peers.mutex.Unlock()
|
||||
device.peers.Lock()
|
||||
defer device.peers.Unlock()
|
||||
|
||||
// check if over limit
|
||||
|
||||
@@ -90,8 +89,8 @@ func (device *Device) NewPeer(pk NoisePublicKey) (*Peer, error) {
|
||||
// create peer
|
||||
|
||||
peer := new(Peer)
|
||||
peer.mutex.Lock()
|
||||
defer peer.mutex.Unlock()
|
||||
peer.Lock()
|
||||
defer peer.Unlock()
|
||||
|
||||
peer.cookieGenerator.Init(pk)
|
||||
peer.device = device
|
||||
@@ -127,15 +126,15 @@ func (device *Device) NewPeer(pk NoisePublicKey) (*Peer, error) {
|
||||
}
|
||||
|
||||
func (peer *Peer) SendBuffer(buffer []byte) error {
|
||||
peer.device.net.mutex.RLock()
|
||||
defer peer.device.net.mutex.RUnlock()
|
||||
peer.device.net.RLock()
|
||||
defer peer.device.net.RUnlock()
|
||||
|
||||
if peer.device.net.bind == nil {
|
||||
return errors.New("no bind")
|
||||
}
|
||||
|
||||
peer.mutex.RLock()
|
||||
defer peer.mutex.RUnlock()
|
||||
peer.RLock()
|
||||
defer peer.RUnlock()
|
||||
|
||||
if peer.endpoint == nil {
|
||||
return errors.New("no known endpoint for peer")
|
||||
@@ -163,15 +162,15 @@ func (peer *Peer) Start() {
|
||||
|
||||
// prevent simultaneous start/stop operations
|
||||
|
||||
peer.routines.mutex.Lock()
|
||||
defer peer.routines.mutex.Unlock()
|
||||
peer.routines.Lock()
|
||||
defer peer.routines.Unlock()
|
||||
|
||||
if peer.isRunning.Get() {
|
||||
return
|
||||
}
|
||||
|
||||
device := peer.device
|
||||
device.log.Debug.Println(peer, ": Starting...")
|
||||
device.log.Debug.Println(peer, "- Starting...")
|
||||
|
||||
// reset routine state
|
||||
|
||||
@@ -208,14 +207,14 @@ func (peer *Peer) ZeroAndFlushAll() {
|
||||
// clear key pairs
|
||||
|
||||
keypairs := &peer.keypairs
|
||||
keypairs.mutex.Lock()
|
||||
keypairs.Lock()
|
||||
device.DeleteKeypair(keypairs.previous)
|
||||
device.DeleteKeypair(keypairs.current)
|
||||
device.DeleteKeypair(keypairs.next)
|
||||
keypairs.previous = nil
|
||||
keypairs.current = nil
|
||||
keypairs.next = nil
|
||||
keypairs.mutex.Unlock()
|
||||
keypairs.Unlock()
|
||||
|
||||
// clear handshake state
|
||||
|
||||
@@ -238,10 +237,10 @@ func (peer *Peer) Stop() {
|
||||
|
||||
peer.routines.starting.Wait()
|
||||
|
||||
peer.routines.mutex.Lock()
|
||||
defer peer.routines.mutex.Unlock()
|
||||
peer.routines.Lock()
|
||||
defer peer.routines.Unlock()
|
||||
|
||||
peer.device.log.Debug.Println(peer, ": Stopping...")
|
||||
peer.device.log.Debug.Println(peer, "- Stopping...")
|
||||
|
||||
peer.timersStop()
|
||||
|
||||
@@ -258,3 +257,14 @@ func (peer *Peer) Stop() {
|
||||
|
||||
peer.ZeroAndFlushAll()
|
||||
}
|
||||
|
||||
var RoamingDisabled bool
|
||||
|
||||
func (peer *Peer) SetEndpointFromPacket(endpoint Endpoint) {
|
||||
if RoamingDisabled {
|
||||
return
|
||||
}
|
||||
peer.Lock()
|
||||
peer.endpoint = endpoint
|
||||
peer.Unlock()
|
||||
}
|
||||
89
device/pools.go
Normal file
89
device/pools.go
Normal file
@@ -0,0 +1,89 @@
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package device
|
||||
|
||||
import "sync"
|
||||
|
||||
func (device *Device) PopulatePools() {
|
||||
if PreallocatedBuffersPerPool == 0 {
|
||||
device.pool.messageBufferPool = &sync.Pool{
|
||||
New: func() interface{} {
|
||||
return new([MaxMessageSize]byte)
|
||||
},
|
||||
}
|
||||
device.pool.inboundElementPool = &sync.Pool{
|
||||
New: func() interface{} {
|
||||
return new(QueueInboundElement)
|
||||
},
|
||||
}
|
||||
device.pool.outboundElementPool = &sync.Pool{
|
||||
New: func() interface{} {
|
||||
return new(QueueOutboundElement)
|
||||
},
|
||||
}
|
||||
} else {
|
||||
device.pool.messageBufferReuseChan = make(chan *[MaxMessageSize]byte, PreallocatedBuffersPerPool)
|
||||
for i := 0; i < PreallocatedBuffersPerPool; i += 1 {
|
||||
device.pool.messageBufferReuseChan <- new([MaxMessageSize]byte)
|
||||
}
|
||||
device.pool.inboundElementReuseChan = make(chan *QueueInboundElement, PreallocatedBuffersPerPool)
|
||||
for i := 0; i < PreallocatedBuffersPerPool; i += 1 {
|
||||
device.pool.inboundElementReuseChan <- new(QueueInboundElement)
|
||||
}
|
||||
device.pool.outboundElementReuseChan = make(chan *QueueOutboundElement, PreallocatedBuffersPerPool)
|
||||
for i := 0; i < PreallocatedBuffersPerPool; i += 1 {
|
||||
device.pool.outboundElementReuseChan <- new(QueueOutboundElement)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (device *Device) GetMessageBuffer() *[MaxMessageSize]byte {
|
||||
if PreallocatedBuffersPerPool == 0 {
|
||||
return device.pool.messageBufferPool.Get().(*[MaxMessageSize]byte)
|
||||
} else {
|
||||
return <-device.pool.messageBufferReuseChan
|
||||
}
|
||||
}
|
||||
|
||||
func (device *Device) PutMessageBuffer(msg *[MaxMessageSize]byte) {
|
||||
if PreallocatedBuffersPerPool == 0 {
|
||||
device.pool.messageBufferPool.Put(msg)
|
||||
} else {
|
||||
device.pool.messageBufferReuseChan <- msg
|
||||
}
|
||||
}
|
||||
|
||||
func (device *Device) GetInboundElement() *QueueInboundElement {
|
||||
if PreallocatedBuffersPerPool == 0 {
|
||||
return device.pool.inboundElementPool.Get().(*QueueInboundElement)
|
||||
} else {
|
||||
return <-device.pool.inboundElementReuseChan
|
||||
}
|
||||
}
|
||||
|
||||
func (device *Device) PutInboundElement(msg *QueueInboundElement) {
|
||||
if PreallocatedBuffersPerPool == 0 {
|
||||
device.pool.inboundElementPool.Put(msg)
|
||||
} else {
|
||||
device.pool.inboundElementReuseChan <- msg
|
||||
}
|
||||
}
|
||||
|
||||
func (device *Device) GetOutboundElement() *QueueOutboundElement {
|
||||
if PreallocatedBuffersPerPool == 0 {
|
||||
return device.pool.outboundElementPool.Get().(*QueueOutboundElement)
|
||||
} else {
|
||||
return <-device.pool.outboundElementReuseChan
|
||||
}
|
||||
}
|
||||
|
||||
func (device *Device) PutOutboundElement(msg *QueueOutboundElement) {
|
||||
if PreallocatedBuffersPerPool == 0 {
|
||||
device.pool.outboundElementPool.Put(msg)
|
||||
} else {
|
||||
device.pool.outboundElementReuseChan <- msg
|
||||
}
|
||||
}
|
||||
16
device/queueconstants_android.go
Normal file
16
device/queueconstants_android.go
Normal file
@@ -0,0 +1,16 @@
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package device
|
||||
|
||||
/* Reduce memory consumption for Android */
|
||||
|
||||
const (
|
||||
QueueOutboundSize = 1024
|
||||
QueueInboundSize = 1024
|
||||
QueueHandshakeSize = 1024
|
||||
MaxSegmentSize = 2200
|
||||
PreallocatedBuffersPerPool = 4096
|
||||
)
|
||||
16
device/queueconstants_default.go
Normal file
16
device/queueconstants_default.go
Normal file
@@ -0,0 +1,16 @@
|
||||
// +build !android,!ios
|
||||
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package device
|
||||
|
||||
const (
|
||||
QueueOutboundSize = 1024
|
||||
QueueInboundSize = 1024
|
||||
QueueHandshakeSize = 1024
|
||||
MaxSegmentSize = (1 << 16) - 1 // largest possible UDP datagram
|
||||
PreallocatedBuffersPerPool = 0 // Disable and allow for infinite memory growth
|
||||
)
|
||||
18
device/queueconstants_ios.go
Normal file
18
device/queueconstants_ios.go
Normal file
@@ -0,0 +1,18 @@
|
||||
// +build ios
|
||||
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package device
|
||||
|
||||
/* Fit within memory limits for iOS's Network Extension API, which has stricter requirements */
|
||||
|
||||
const (
|
||||
QueueOutboundSize = 1024
|
||||
QueueInboundSize = 1024
|
||||
QueueHandshakeSize = 1024
|
||||
MaxSegmentSize = 1700
|
||||
PreallocatedBuffersPerPool = 1024
|
||||
)
|
||||
@@ -1,10 +1,9 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2018 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
|
||||
* Copyright (C) 2017-2018 Mathias N. Hall-Andersen <mathias@hall-andersen.dk>.
|
||||
* Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package main
|
||||
package device
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
@@ -28,7 +27,7 @@ type QueueHandshakeElement struct {
|
||||
|
||||
type QueueInboundElement struct {
|
||||
dropped int32
|
||||
mutex sync.Mutex
|
||||
sync.Mutex
|
||||
buffer *[MaxMessageSize]byte
|
||||
packet []byte
|
||||
counter uint64
|
||||
@@ -44,59 +43,29 @@ func (elem *QueueInboundElement) IsDropped() bool {
|
||||
return atomic.LoadInt32(&elem.dropped) == AtomicTrue
|
||||
}
|
||||
|
||||
func (device *Device) addToInboundQueue(
|
||||
queue chan *QueueInboundElement,
|
||||
element *QueueInboundElement,
|
||||
) {
|
||||
for {
|
||||
func (device *Device) addToInboundAndDecryptionQueues(inboundQueue chan *QueueInboundElement, decryptionQueue chan *QueueInboundElement, element *QueueInboundElement) bool {
|
||||
select {
|
||||
case queue <- element:
|
||||
return
|
||||
default:
|
||||
case inboundQueue <- element:
|
||||
select {
|
||||
case old := <-queue:
|
||||
old.Drop()
|
||||
case decryptionQueue <- element:
|
||||
return true
|
||||
default:
|
||||
element.Drop()
|
||||
element.Unlock()
|
||||
return false
|
||||
}
|
||||
}
|
||||
default:
|
||||
device.PutInboundElement(element)
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func (device *Device) addToDecryptionQueue(
|
||||
queue chan *QueueInboundElement,
|
||||
element *QueueInboundElement,
|
||||
) {
|
||||
for {
|
||||
func (device *Device) addToHandshakeQueue(queue chan QueueHandshakeElement, element QueueHandshakeElement) bool {
|
||||
select {
|
||||
case queue <- element:
|
||||
return
|
||||
return true
|
||||
default:
|
||||
select {
|
||||
case old := <-queue:
|
||||
// drop & release to potential consumer
|
||||
old.Drop()
|
||||
old.mutex.Unlock()
|
||||
default:
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (device *Device) addToHandshakeQueue(
|
||||
queue chan QueueHandshakeElement,
|
||||
element QueueHandshakeElement,
|
||||
) {
|
||||
for {
|
||||
select {
|
||||
case queue <- element:
|
||||
return
|
||||
default:
|
||||
select {
|
||||
case elem := <-queue:
|
||||
device.PutMessageBuffer(elem.buffer)
|
||||
default:
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
@@ -105,12 +74,12 @@ func (device *Device) addToHandshakeQueue(
|
||||
* NOTE: Not thread safe, but called by sequential receiver!
|
||||
*/
|
||||
func (peer *Peer) keepKeyFreshReceiving() {
|
||||
if peer.timers.sentLastMinuteHandshake {
|
||||
if peer.timers.sentLastMinuteHandshake.Get() {
|
||||
return
|
||||
}
|
||||
keypair := peer.keypairs.Current()
|
||||
if keypair != nil && keypair.isInitiator && time.Now().Sub(keypair.created) > (RejectAfterTime-KeepaliveTimeout-RekeyTimeout) {
|
||||
peer.timers.sentLastMinuteHandshake = true
|
||||
peer.timers.sentLastMinuteHandshake.Set(true)
|
||||
peer.SendHandshakeInitiation(false)
|
||||
}
|
||||
}
|
||||
@@ -125,11 +94,11 @@ func (device *Device) RoutineReceiveIncoming(IP int, bind Bind) {
|
||||
logDebug := device.log.Debug
|
||||
defer func() {
|
||||
logDebug.Println("Routine: receive incoming IPv" + strconv.Itoa(IP) + " - stopped")
|
||||
device.state.stopping.Done()
|
||||
device.net.stopping.Done()
|
||||
}()
|
||||
|
||||
logDebug.Println("Routine: receive incoming IPv" + strconv.Itoa(IP) + " - starting")
|
||||
device.state.starting.Done()
|
||||
logDebug.Println("Routine: receive incoming IPv" + strconv.Itoa(IP) + " - started")
|
||||
device.net.starting.Done()
|
||||
|
||||
// receive datagrams until conn is closed
|
||||
|
||||
@@ -155,6 +124,7 @@ func (device *Device) RoutineReceiveIncoming(IP int, bind Bind) {
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
device.PutMessageBuffer(buffer)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -177,7 +147,7 @@ func (device *Device) RoutineReceiveIncoming(IP int, bind Bind) {
|
||||
|
||||
// check size
|
||||
|
||||
if len(packet) < MessageTransportType {
|
||||
if len(packet) < MessageTransportSize {
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -199,24 +169,24 @@ func (device *Device) RoutineReceiveIncoming(IP int, bind Bind) {
|
||||
}
|
||||
|
||||
// create work element
|
||||
|
||||
peer := value.peer
|
||||
elem := &QueueInboundElement{
|
||||
packet: packet,
|
||||
buffer: buffer,
|
||||
keypair: keypair,
|
||||
dropped: AtomicFalse,
|
||||
endpoint: endpoint,
|
||||
}
|
||||
elem.mutex.Lock()
|
||||
elem := device.GetInboundElement()
|
||||
elem.packet = packet
|
||||
elem.buffer = buffer
|
||||
elem.keypair = keypair
|
||||
elem.dropped = AtomicFalse
|
||||
elem.endpoint = endpoint
|
||||
elem.counter = 0
|
||||
elem.Mutex = sync.Mutex{}
|
||||
elem.Lock()
|
||||
|
||||
// add to decryption queues
|
||||
|
||||
if peer.isRunning.Get() {
|
||||
device.addToDecryptionQueue(device.queue.decryption, elem)
|
||||
device.addToInboundQueue(peer.queue.inbound, elem)
|
||||
if device.addToInboundAndDecryptionQueues(peer.queue.inbound, device.queue.decryption, elem) {
|
||||
buffer = device.GetMessageBuffer()
|
||||
}
|
||||
}
|
||||
|
||||
continue
|
||||
|
||||
@@ -236,7 +206,7 @@ func (device *Device) RoutineReceiveIncoming(IP int, bind Bind) {
|
||||
}
|
||||
|
||||
if okay {
|
||||
device.addToHandshakeQueue(
|
||||
if (device.addToHandshakeQueue(
|
||||
device.queue.handshake,
|
||||
QueueHandshakeElement{
|
||||
msgType: msgType,
|
||||
@@ -244,10 +214,11 @@ func (device *Device) RoutineReceiveIncoming(IP int, bind Bind) {
|
||||
packet: packet,
|
||||
endpoint: endpoint,
|
||||
},
|
||||
)
|
||||
)) {
|
||||
buffer = device.GetMessageBuffer()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (device *Device) RoutineDecryption() {
|
||||
@@ -308,8 +279,9 @@ func (device *Device) RoutineDecryption() {
|
||||
)
|
||||
if err != nil {
|
||||
elem.Drop()
|
||||
device.PutMessageBuffer(elem.buffer)
|
||||
}
|
||||
elem.mutex.Unlock()
|
||||
elem.Unlock()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -322,18 +294,26 @@ func (device *Device) RoutineHandshake() {
|
||||
logError := device.log.Error
|
||||
logDebug := device.log.Debug
|
||||
|
||||
var elem QueueHandshakeElement
|
||||
var ok bool
|
||||
|
||||
defer func() {
|
||||
logDebug.Println("Routine: handshake worker - stopped")
|
||||
device.state.stopping.Done()
|
||||
if elem.buffer != nil {
|
||||
device.PutMessageBuffer(elem.buffer)
|
||||
}
|
||||
}()
|
||||
|
||||
logDebug.Println("Routine: handshake worker - started")
|
||||
device.state.starting.Done()
|
||||
|
||||
var elem QueueHandshakeElement
|
||||
var ok bool
|
||||
|
||||
for {
|
||||
if elem.buffer != nil {
|
||||
device.PutMessageBuffer(elem.buffer)
|
||||
elem.buffer = nil
|
||||
}
|
||||
|
||||
select {
|
||||
case elem, ok = <-device.queue.handshake:
|
||||
case <-device.signals.stop:
|
||||
@@ -371,7 +351,10 @@ func (device *Device) RoutineHandshake() {
|
||||
// consume reply
|
||||
|
||||
if peer := entry.peer; peer.isRunning.Get() {
|
||||
peer.cookieGenerator.ConsumeReply(&reply)
|
||||
logDebug.Println("Receiving cookie response from ", elem.endpoint.DstToString())
|
||||
if !peer.cookieGenerator.ConsumeReply(&reply) {
|
||||
logDebug.Println("Could not decrypt invalid cookie response")
|
||||
}
|
||||
}
|
||||
|
||||
continue
|
||||
@@ -440,12 +423,9 @@ func (device *Device) RoutineHandshake() {
|
||||
peer.timersAnyAuthenticatedPacketReceived()
|
||||
|
||||
// update endpoint
|
||||
peer.SetEndpointFromPacket(elem.endpoint)
|
||||
|
||||
peer.mutex.Lock()
|
||||
peer.endpoint = elem.endpoint
|
||||
peer.mutex.Unlock()
|
||||
|
||||
logDebug.Println(peer, ": Received handshake initiation")
|
||||
logDebug.Println(peer, "- Received handshake initiation")
|
||||
|
||||
peer.SendHandshakeResponse()
|
||||
|
||||
@@ -466,19 +446,16 @@ func (device *Device) RoutineHandshake() {
|
||||
peer := device.ConsumeMessageResponse(&msg)
|
||||
if peer == nil {
|
||||
logInfo.Println(
|
||||
"Recieved invalid response message from",
|
||||
"Received invalid response message from",
|
||||
elem.endpoint.DstToString(),
|
||||
)
|
||||
continue
|
||||
}
|
||||
|
||||
// update endpoint
|
||||
peer.SetEndpointFromPacket(elem.endpoint)
|
||||
|
||||
peer.mutex.Lock()
|
||||
peer.endpoint = elem.endpoint
|
||||
peer.mutex.Unlock()
|
||||
|
||||
logDebug.Println(peer, ": Received handshake response")
|
||||
logDebug.Println(peer, "- Received handshake response")
|
||||
|
||||
// update timers
|
||||
|
||||
@@ -490,7 +467,7 @@ func (device *Device) RoutineHandshake() {
|
||||
err = peer.BeginSymmetricSession()
|
||||
|
||||
if err != nil {
|
||||
logError.Println(peer, ": Failed to derive keypair:", err)
|
||||
logError.Println(peer, "- Failed to derive keypair:", err)
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -505,6 +482,33 @@ func (device *Device) RoutineHandshake() {
|
||||
}
|
||||
}
|
||||
|
||||
func (peer *Peer) elementStopOrFlush(shouldFlush *bool) (stop bool, elemOk bool, elem *QueueInboundElement) {
|
||||
if !*shouldFlush {
|
||||
select {
|
||||
case <-peer.routines.stop:
|
||||
stop = true
|
||||
return
|
||||
case elem, elemOk = <-peer.queue.inbound:
|
||||
return
|
||||
}
|
||||
} else {
|
||||
select {
|
||||
case <-peer.routines.stop:
|
||||
stop = true
|
||||
return
|
||||
case elem, elemOk = <-peer.queue.inbound:
|
||||
return
|
||||
default:
|
||||
*shouldFlush = false
|
||||
err := peer.device.tun.device.Flush()
|
||||
if err != nil {
|
||||
peer.device.log.Error.Printf("Unable to flush packets: %v", err)
|
||||
}
|
||||
return peer.elementStopOrFlush(shouldFlush)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (peer *Peer) RoutineSequentialReceiver() {
|
||||
|
||||
device := peer.device
|
||||
@@ -512,31 +516,44 @@ func (peer *Peer) RoutineSequentialReceiver() {
|
||||
logError := device.log.Error
|
||||
logDebug := device.log.Debug
|
||||
|
||||
var elem *QueueInboundElement
|
||||
var ok bool
|
||||
var stop bool
|
||||
|
||||
shouldFlush := false
|
||||
|
||||
defer func() {
|
||||
logDebug.Println(peer, ": Routine: sequential receiver - stopped")
|
||||
logDebug.Println(peer, "- Routine: sequential receiver - stopped")
|
||||
peer.routines.stopping.Done()
|
||||
if elem != nil {
|
||||
if !elem.IsDropped() {
|
||||
device.PutMessageBuffer(elem.buffer)
|
||||
}
|
||||
device.PutInboundElement(elem)
|
||||
}
|
||||
}()
|
||||
|
||||
logDebug.Println(peer, ": Routine: sequential receiver - started")
|
||||
logDebug.Println(peer, "- Routine: sequential receiver - started")
|
||||
|
||||
peer.routines.starting.Done()
|
||||
|
||||
for {
|
||||
if elem != nil {
|
||||
if !elem.IsDropped() {
|
||||
device.PutMessageBuffer(elem.buffer)
|
||||
}
|
||||
device.PutInboundElement(elem)
|
||||
elem = nil
|
||||
}
|
||||
|
||||
select {
|
||||
|
||||
case <-peer.routines.stop:
|
||||
return
|
||||
|
||||
case elem, ok := <-peer.queue.inbound:
|
||||
|
||||
if !ok {
|
||||
stop, ok, elem = peer.elementStopOrFlush(&shouldFlush)
|
||||
if stop || !ok {
|
||||
return
|
||||
}
|
||||
|
||||
// wait for decryption
|
||||
|
||||
elem.mutex.Lock()
|
||||
elem.Lock()
|
||||
|
||||
if elem.IsDropped() {
|
||||
continue
|
||||
@@ -544,15 +561,12 @@ func (peer *Peer) RoutineSequentialReceiver() {
|
||||
|
||||
// check for replay
|
||||
|
||||
if !elem.keypair.replayFilter.ValidateCounter(elem.counter) {
|
||||
if !elem.keypair.replayFilter.ValidateCounter(elem.counter, RejectAfterMessages) {
|
||||
continue
|
||||
}
|
||||
|
||||
// update endpoint
|
||||
|
||||
peer.mutex.Lock()
|
||||
peer.endpoint = elem.endpoint
|
||||
peer.mutex.Unlock()
|
||||
peer.SetEndpointFromPacket(elem.endpoint)
|
||||
|
||||
// check if using new keypair
|
||||
if peer.ReceivedWithKeypair(elem.keypair) {
|
||||
@@ -570,7 +584,7 @@ func (peer *Peer) RoutineSequentialReceiver() {
|
||||
// check for keepalive
|
||||
|
||||
if len(elem.packet) == 0 {
|
||||
logDebug.Println(peer, ": Receiving keepalive packet")
|
||||
logDebug.Println(peer, "- Receiving keepalive packet")
|
||||
continue
|
||||
}
|
||||
peer.timersDataReceived()
|
||||
@@ -642,13 +656,12 @@ func (peer *Peer) RoutineSequentialReceiver() {
|
||||
|
||||
offset := MessageTransportOffsetContent
|
||||
atomic.AddUint64(&peer.stats.rxBytes, uint64(len(elem.packet)))
|
||||
_, err := device.tun.device.Write(
|
||||
elem.buffer[:offset+len(elem.packet)],
|
||||
offset)
|
||||
device.PutMessageBuffer(elem.buffer)
|
||||
if err != nil {
|
||||
_, err := device.tun.device.Write(elem.buffer[:offset+len(elem.packet)], offset)
|
||||
if err == nil {
|
||||
shouldFlush = true
|
||||
}
|
||||
if err != nil && !device.isClosed.Get() {
|
||||
logError.Println("Failed to write packet to TUN device:", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,10 +1,9 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2018 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
|
||||
* Copyright (C) 2017-2018 Mathias N. Hall-Andersen <mathias@hall-andersen.dk>.
|
||||
* Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package main
|
||||
package device
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
@@ -44,7 +43,7 @@ import (
|
||||
|
||||
type QueueOutboundElement struct {
|
||||
dropped int32
|
||||
mutex sync.Mutex
|
||||
sync.Mutex
|
||||
buffer *[MaxMessageSize]byte // slice holding the packet data
|
||||
packet []byte // slice of "buffer" (always!)
|
||||
nonce uint64 // nonce for encryption
|
||||
@@ -53,10 +52,14 @@ type QueueOutboundElement struct {
|
||||
}
|
||||
|
||||
func (device *Device) NewOutboundElement() *QueueOutboundElement {
|
||||
return &QueueOutboundElement{
|
||||
dropped: AtomicFalse,
|
||||
buffer: device.pool.messageBuffers.Get().(*[MaxMessageSize]byte),
|
||||
}
|
||||
elem := device.GetOutboundElement()
|
||||
elem.dropped = AtomicFalse
|
||||
elem.buffer = device.GetMessageBuffer()
|
||||
elem.Mutex = sync.Mutex{}
|
||||
elem.nonce = 0
|
||||
elem.keypair = nil
|
||||
elem.peer = nil
|
||||
return elem
|
||||
}
|
||||
|
||||
func (elem *QueueOutboundElement) Drop() {
|
||||
@@ -67,10 +70,7 @@ func (elem *QueueOutboundElement) IsDropped() bool {
|
||||
return atomic.LoadInt32(&elem.dropped) == AtomicTrue
|
||||
}
|
||||
|
||||
func addToOutboundQueue(
|
||||
queue chan *QueueOutboundElement,
|
||||
element *QueueOutboundElement,
|
||||
) {
|
||||
func addToNonceQueue(queue chan *QueueOutboundElement, element *QueueOutboundElement, device *Device) {
|
||||
for {
|
||||
select {
|
||||
case queue <- element:
|
||||
@@ -78,53 +78,53 @@ func addToOutboundQueue(
|
||||
default:
|
||||
select {
|
||||
case old := <-queue:
|
||||
old.Drop()
|
||||
device.PutMessageBuffer(old.buffer)
|
||||
device.PutOutboundElement(old)
|
||||
default:
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func addToEncryptionQueue(
|
||||
queue chan *QueueOutboundElement,
|
||||
element *QueueOutboundElement,
|
||||
) {
|
||||
for {
|
||||
func addToOutboundAndEncryptionQueues(outboundQueue chan *QueueOutboundElement, encryptionQueue chan *QueueOutboundElement, element *QueueOutboundElement) {
|
||||
select {
|
||||
case queue <- element:
|
||||
case outboundQueue <- element:
|
||||
select {
|
||||
case encryptionQueue <- element:
|
||||
return
|
||||
default:
|
||||
select {
|
||||
case old := <-queue:
|
||||
// drop & release to potential consumer
|
||||
old.Drop()
|
||||
old.mutex.Unlock()
|
||||
element.Drop()
|
||||
element.peer.device.PutMessageBuffer(element.buffer)
|
||||
element.Unlock()
|
||||
}
|
||||
default:
|
||||
}
|
||||
}
|
||||
element.peer.device.PutMessageBuffer(element.buffer)
|
||||
element.peer.device.PutOutboundElement(element)
|
||||
}
|
||||
}
|
||||
|
||||
/* Queues a keepalive if no packets are queued for peer
|
||||
*/
|
||||
func (peer *Peer) SendKeepalive() bool {
|
||||
if len(peer.queue.nonce) != 0 || peer.queue.packetInNonceQueueIsAwaitingKey || !peer.isRunning.Get() {
|
||||
if len(peer.queue.nonce) != 0 || peer.queue.packetInNonceQueueIsAwaitingKey.Get() || !peer.isRunning.Get() {
|
||||
return false
|
||||
}
|
||||
elem := peer.device.NewOutboundElement()
|
||||
elem.packet = nil
|
||||
select {
|
||||
case peer.queue.nonce <- elem:
|
||||
peer.device.log.Debug.Println(peer, ": Sending keepalive packet")
|
||||
peer.device.log.Debug.Println(peer, "- Sending keepalive packet")
|
||||
return true
|
||||
default:
|
||||
peer.device.PutMessageBuffer(elem.buffer)
|
||||
peer.device.PutOutboundElement(elem)
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func (peer *Peer) SendHandshakeInitiation(isRetry bool) error {
|
||||
if !isRetry {
|
||||
peer.timers.handshakeAttempts = 0
|
||||
atomic.StoreUint32(&peer.timers.handshakeAttempts, 0)
|
||||
}
|
||||
|
||||
peer.handshake.mutex.RLock()
|
||||
@@ -142,11 +142,11 @@ func (peer *Peer) SendHandshakeInitiation(isRetry bool) error {
|
||||
peer.handshake.lastSentHandshake = time.Now()
|
||||
peer.handshake.mutex.Unlock()
|
||||
|
||||
peer.device.log.Debug.Println(peer, ": Sending handshake initiation")
|
||||
peer.device.log.Debug.Println(peer, "- Sending handshake initiation")
|
||||
|
||||
msg, err := peer.device.CreateMessageInitiation(peer)
|
||||
if err != nil {
|
||||
peer.device.log.Error.Println(peer, ": Failed to create initiation message:", err)
|
||||
peer.device.log.Error.Println(peer, "- Failed to create initiation message:", err)
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -161,7 +161,7 @@ func (peer *Peer) SendHandshakeInitiation(isRetry bool) error {
|
||||
|
||||
err = peer.SendBuffer(packet)
|
||||
if err != nil {
|
||||
peer.device.log.Error.Println(peer, ": Failed to send handshake initiation", err)
|
||||
peer.device.log.Error.Println(peer, "- Failed to send handshake initiation", err)
|
||||
}
|
||||
peer.timersHandshakeInitiated()
|
||||
|
||||
@@ -173,11 +173,11 @@ func (peer *Peer) SendHandshakeResponse() error {
|
||||
peer.handshake.lastSentHandshake = time.Now()
|
||||
peer.handshake.mutex.Unlock()
|
||||
|
||||
peer.device.log.Debug.Println(peer, ": Sending handshake response")
|
||||
peer.device.log.Debug.Println(peer, "- Sending handshake response")
|
||||
|
||||
response, err := peer.device.CreateMessageResponse(peer)
|
||||
if err != nil {
|
||||
peer.device.log.Error.Println(peer, ": Failed to create response message:", err)
|
||||
peer.device.log.Error.Println(peer, "- Failed to create response message:", err)
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -189,7 +189,7 @@ func (peer *Peer) SendHandshakeResponse() error {
|
||||
|
||||
err = peer.BeginSymmetricSession()
|
||||
if err != nil {
|
||||
peer.device.log.Error.Println(peer, ": Failed to derive keypair:", err)
|
||||
peer.device.log.Error.Println(peer, "- Failed to derive keypair:", err)
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -199,14 +199,14 @@ func (peer *Peer) SendHandshakeResponse() error {
|
||||
|
||||
err = peer.SendBuffer(packet)
|
||||
if err != nil {
|
||||
peer.device.log.Error.Println(peer, ": Failed to send handshake response", err)
|
||||
peer.device.log.Error.Println(peer, "- Failed to send handshake response", err)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (device *Device) SendHandshakeCookie(initiatingElem *QueueHandshakeElement) error {
|
||||
|
||||
device.log.Debug.Println("Sending cookie reply to:", initiatingElem.endpoint.DstToString())
|
||||
device.log.Debug.Println("Sending cookie response for denied handshake message for", initiatingElem.endpoint.DstToString())
|
||||
|
||||
sender := binary.LittleEndian.Uint32(initiatingElem.packet[4:8])
|
||||
reply, err := device.cookieChecker.CreateReply(initiatingElem.packet, sender, initiatingElem.endpoint.DstToBytes())
|
||||
@@ -243,8 +243,6 @@ func (peer *Peer) keepKeyFreshSending() {
|
||||
*/
|
||||
func (device *Device) RoutineReadFromTUN() {
|
||||
|
||||
elem := device.NewOutboundElement()
|
||||
|
||||
logDebug := device.log.Debug
|
||||
logError := device.log.Error
|
||||
|
||||
@@ -256,7 +254,14 @@ func (device *Device) RoutineReadFromTUN() {
|
||||
logDebug.Println("Routine: TUN reader - started")
|
||||
device.state.starting.Done()
|
||||
|
||||
var elem *QueueOutboundElement
|
||||
|
||||
for {
|
||||
if elem != nil {
|
||||
device.PutMessageBuffer(elem.buffer)
|
||||
device.PutOutboundElement(elem)
|
||||
}
|
||||
elem = device.NewOutboundElement()
|
||||
|
||||
// read packet
|
||||
|
||||
@@ -264,8 +269,12 @@ func (device *Device) RoutineReadFromTUN() {
|
||||
size, err := device.tun.device.Read(elem.buffer[:], offset)
|
||||
|
||||
if err != nil {
|
||||
if !device.isClosed.Get() {
|
||||
logError.Println("Failed to read packet from TUN device:", err)
|
||||
device.Close()
|
||||
}
|
||||
device.PutMessageBuffer(elem.buffer)
|
||||
device.PutOutboundElement(elem)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -304,11 +313,11 @@ func (device *Device) RoutineReadFromTUN() {
|
||||
// insert into nonce/pre-handshake queue
|
||||
|
||||
if peer.isRunning.Get() {
|
||||
if peer.queue.packetInNonceQueueIsAwaitingKey {
|
||||
if peer.queue.packetInNonceQueueIsAwaitingKey.Get() {
|
||||
peer.SendHandshakeInitiation(false)
|
||||
}
|
||||
addToOutboundQueue(peer.queue.nonce, elem)
|
||||
elem = device.NewOutboundElement()
|
||||
addToNonceQueue(peer.queue.nonce, elem, device)
|
||||
elem = nil
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -332,28 +341,31 @@ func (peer *Peer) RoutineNonce() {
|
||||
device := peer.device
|
||||
logDebug := device.log.Debug
|
||||
|
||||
defer func() {
|
||||
logDebug.Println(peer, ": Routine: nonce worker - stopped")
|
||||
peer.queue.packetInNonceQueueIsAwaitingKey = false
|
||||
peer.routines.stopping.Done()
|
||||
}()
|
||||
|
||||
flush := func() {
|
||||
for {
|
||||
select {
|
||||
case <-peer.queue.nonce:
|
||||
case elem := <-peer.queue.nonce:
|
||||
device.PutMessageBuffer(elem.buffer)
|
||||
device.PutOutboundElement(elem)
|
||||
default:
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
defer func() {
|
||||
flush()
|
||||
logDebug.Println(peer, "- Routine: nonce worker - stopped")
|
||||
peer.queue.packetInNonceQueueIsAwaitingKey.Set(false)
|
||||
peer.routines.stopping.Done()
|
||||
}()
|
||||
|
||||
peer.routines.starting.Done()
|
||||
logDebug.Println(peer, ": Routine: nonce worker - started")
|
||||
logDebug.Println(peer, "- Routine: nonce worker - started")
|
||||
|
||||
for {
|
||||
NextPacket:
|
||||
peer.queue.packetInNonceQueueIsAwaitingKey = false
|
||||
peer.queue.packetInNonceQueueIsAwaitingKey.Set(false)
|
||||
|
||||
select {
|
||||
case <-peer.routines.stop:
|
||||
@@ -381,7 +393,7 @@ func (peer *Peer) RoutineNonce() {
|
||||
break
|
||||
}
|
||||
}
|
||||
peer.queue.packetInNonceQueueIsAwaitingKey = true
|
||||
peer.queue.packetInNonceQueueIsAwaitingKey.Set(true)
|
||||
|
||||
// no suitable key pair, request for new handshake
|
||||
|
||||
@@ -394,21 +406,25 @@ func (peer *Peer) RoutineNonce() {
|
||||
|
||||
// wait for key to be established
|
||||
|
||||
logDebug.Println(peer, ": Awaiting keypair")
|
||||
logDebug.Println(peer, "- Awaiting keypair")
|
||||
|
||||
select {
|
||||
case <-peer.signals.newKeypairArrived:
|
||||
logDebug.Println(peer, ": Obtained awaited keypair")
|
||||
logDebug.Println(peer, "- Obtained awaited keypair")
|
||||
|
||||
case <-peer.signals.flushNonceQueue:
|
||||
device.PutMessageBuffer(elem.buffer)
|
||||
device.PutOutboundElement(elem)
|
||||
flush()
|
||||
goto NextPacket
|
||||
|
||||
case <-peer.routines.stop:
|
||||
device.PutMessageBuffer(elem.buffer)
|
||||
device.PutOutboundElement(elem)
|
||||
return
|
||||
}
|
||||
}
|
||||
peer.queue.packetInNonceQueueIsAwaitingKey = false
|
||||
peer.queue.packetInNonceQueueIsAwaitingKey.Set(false)
|
||||
|
||||
// populate work element
|
||||
|
||||
@@ -419,17 +435,17 @@ func (peer *Peer) RoutineNonce() {
|
||||
|
||||
if elem.nonce >= RejectAfterMessages {
|
||||
atomic.StoreUint64(&keypair.sendNonce, RejectAfterMessages)
|
||||
device.PutMessageBuffer(elem.buffer)
|
||||
device.PutOutboundElement(elem)
|
||||
goto NextPacket
|
||||
}
|
||||
|
||||
elem.keypair = keypair
|
||||
elem.dropped = AtomicFalse
|
||||
elem.mutex.Lock()
|
||||
elem.Lock()
|
||||
|
||||
// add to parallel and sequential queue
|
||||
|
||||
addToEncryptionQueue(device.queue.encryption, elem)
|
||||
addToOutboundQueue(peer.queue.outbound, elem)
|
||||
addToOutboundAndEncryptionQueues(peer.queue.outbound, device.queue.encryption, elem)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -446,6 +462,19 @@ func (device *Device) RoutineEncryption() {
|
||||
logDebug := device.log.Debug
|
||||
|
||||
defer func() {
|
||||
for {
|
||||
select {
|
||||
case elem, ok := <-device.queue.encryption:
|
||||
if ok && !elem.IsDropped() {
|
||||
elem.Drop()
|
||||
device.PutMessageBuffer(elem.buffer)
|
||||
elem.Unlock()
|
||||
}
|
||||
default:
|
||||
goto out
|
||||
}
|
||||
}
|
||||
out:
|
||||
logDebug.Println("Routine: encryption worker - stopped")
|
||||
device.state.stopping.Done()
|
||||
}()
|
||||
@@ -488,11 +517,13 @@ func (device *Device) RoutineEncryption() {
|
||||
// pad content to multiple of 16
|
||||
|
||||
mtu := int(atomic.LoadInt32(&device.tun.mtu))
|
||||
rem := len(elem.packet) % PaddingMultiple
|
||||
if rem > 0 {
|
||||
for i := 0; i < PaddingMultiple-rem && len(elem.packet) < mtu; i++ {
|
||||
elem.packet = append(elem.packet, 0)
|
||||
lastUnit := len(elem.packet) % mtu
|
||||
paddedSize := (lastUnit + PaddingMultiple - 1) & ^(PaddingMultiple - 1)
|
||||
if paddedSize > mtu {
|
||||
paddedSize = mtu
|
||||
}
|
||||
for i := len(elem.packet); i < paddedSize; i++ {
|
||||
elem.packet = append(elem.packet, 0)
|
||||
}
|
||||
|
||||
// encrypt content and release to consumer
|
||||
@@ -504,7 +535,7 @@ func (device *Device) RoutineEncryption() {
|
||||
elem.packet,
|
||||
nil,
|
||||
)
|
||||
elem.mutex.Unlock()
|
||||
elem.Unlock()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -519,13 +550,29 @@ func (peer *Peer) RoutineSequentialSender() {
|
||||
device := peer.device
|
||||
|
||||
logDebug := device.log.Debug
|
||||
logError := device.log.Error
|
||||
|
||||
defer func() {
|
||||
logDebug.Println(peer, ": Routine: sequential sender - stopped")
|
||||
for {
|
||||
select {
|
||||
case elem, ok := <-peer.queue.outbound:
|
||||
if ok {
|
||||
if !elem.IsDropped() {
|
||||
device.PutMessageBuffer(elem.buffer)
|
||||
elem.Drop()
|
||||
}
|
||||
device.PutOutboundElement(elem)
|
||||
}
|
||||
default:
|
||||
goto out
|
||||
}
|
||||
}
|
||||
out:
|
||||
logDebug.Println(peer, "- Routine: sequential sender - stopped")
|
||||
peer.routines.stopping.Done()
|
||||
}()
|
||||
|
||||
logDebug.Println(peer, ": Routine: sequential sender - started")
|
||||
logDebug.Println(peer, "- Routine: sequential sender - started")
|
||||
|
||||
peer.routines.starting.Done()
|
||||
|
||||
@@ -541,8 +588,9 @@ func (peer *Peer) RoutineSequentialSender() {
|
||||
return
|
||||
}
|
||||
|
||||
elem.mutex.Lock()
|
||||
elem.Lock()
|
||||
if elem.IsDropped() {
|
||||
device.PutOutboundElement(elem)
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -554,8 +602,9 @@ func (peer *Peer) RoutineSequentialSender() {
|
||||
length := uint64(len(elem.packet))
|
||||
err := peer.SendBuffer(elem.packet)
|
||||
device.PutMessageBuffer(elem.buffer)
|
||||
device.PutOutboundElement(elem)
|
||||
if err != nil {
|
||||
logDebug.Println("Failed to send authenticated packet to peer", peer)
|
||||
logError.Println(peer, "- Failed to send data packet", err)
|
||||
continue
|
||||
}
|
||||
atomic.AddUint64(&peer.stats.txBytes, length)
|
||||
@@ -1,11 +1,11 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2015-2018 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
|
||||
* Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved.
|
||||
*
|
||||
* This is based heavily on timers.c from the kernel implementation.
|
||||
*/
|
||||
|
||||
package main
|
||||
package device
|
||||
|
||||
import (
|
||||
"math/rand"
|
||||
@@ -19,15 +19,15 @@ import (
|
||||
*/
|
||||
|
||||
type Timer struct {
|
||||
timer *time.Timer
|
||||
modifyingLock sync.Mutex
|
||||
*time.Timer
|
||||
modifyingLock sync.RWMutex
|
||||
runningLock sync.Mutex
|
||||
isPending bool
|
||||
}
|
||||
|
||||
func (peer *Peer) NewTimer(expirationFunction func(*Peer)) *Timer {
|
||||
timer := &Timer{}
|
||||
timer.timer = time.AfterFunc(time.Hour, func() {
|
||||
timer.Timer = time.AfterFunc(time.Hour, func() {
|
||||
timer.runningLock.Lock()
|
||||
|
||||
timer.modifyingLock.Lock()
|
||||
@@ -42,21 +42,21 @@ func (peer *Peer) NewTimer(expirationFunction func(*Peer)) *Timer {
|
||||
expirationFunction(peer)
|
||||
timer.runningLock.Unlock()
|
||||
})
|
||||
timer.timer.Stop()
|
||||
timer.Stop()
|
||||
return timer
|
||||
}
|
||||
|
||||
func (timer *Timer) Mod(d time.Duration) {
|
||||
timer.modifyingLock.Lock()
|
||||
timer.isPending = true
|
||||
timer.timer.Reset(d)
|
||||
timer.Reset(d)
|
||||
timer.modifyingLock.Unlock()
|
||||
}
|
||||
|
||||
func (timer *Timer) Del() {
|
||||
timer.modifyingLock.Lock()
|
||||
timer.isPending = false
|
||||
timer.timer.Stop()
|
||||
timer.Stop()
|
||||
timer.modifyingLock.Unlock()
|
||||
}
|
||||
|
||||
@@ -67,13 +67,19 @@ func (timer *Timer) DelSync() {
|
||||
timer.runningLock.Unlock()
|
||||
}
|
||||
|
||||
func (timer *Timer) IsPending() bool {
|
||||
timer.modifyingLock.RLock()
|
||||
defer timer.modifyingLock.RUnlock()
|
||||
return timer.isPending
|
||||
}
|
||||
|
||||
func (peer *Peer) timersActive() bool {
|
||||
return peer.isRunning.Get() && peer.device != nil && peer.device.isUp.Get() && len(peer.device.peers.keyMap) > 0
|
||||
}
|
||||
|
||||
func expiredRetransmitHandshake(peer *Peer) {
|
||||
if peer.timers.handshakeAttempts > MaxTimerHandshakes {
|
||||
peer.device.log.Debug.Printf("%s: Handshake did not complete after %d attempts, giving up\n", peer, MaxTimerHandshakes+2)
|
||||
if atomic.LoadUint32(&peer.timers.handshakeAttempts) > MaxTimerHandshakes {
|
||||
peer.device.log.Debug.Printf("%s - Handshake did not complete after %d attempts, giving up\n", peer, MaxTimerHandshakes+2)
|
||||
|
||||
if peer.timersActive() {
|
||||
peer.timers.sendKeepalive.Del()
|
||||
@@ -87,19 +93,19 @@ func expiredRetransmitHandshake(peer *Peer) {
|
||||
/* We set a timer for destroying any residue that might be left
|
||||
* of a partial exchange.
|
||||
*/
|
||||
if peer.timersActive() && !peer.timers.zeroKeyMaterial.isPending {
|
||||
if peer.timersActive() && !peer.timers.zeroKeyMaterial.IsPending() {
|
||||
peer.timers.zeroKeyMaterial.Mod(RejectAfterTime * 3)
|
||||
}
|
||||
} else {
|
||||
peer.timers.handshakeAttempts++
|
||||
peer.device.log.Debug.Printf("%s: Handshake did not complete after %d seconds, retrying (try %d)\n", peer, int(RekeyTimeout.Seconds()), peer.timers.handshakeAttempts+1)
|
||||
atomic.AddUint32(&peer.timers.handshakeAttempts, 1)
|
||||
peer.device.log.Debug.Printf("%s - Handshake did not complete after %d seconds, retrying (try %d)\n", peer, int(RekeyTimeout.Seconds()), atomic.LoadUint32(&peer.timers.handshakeAttempts)+1)
|
||||
|
||||
/* We clear the endpoint address src address, in case this is the cause of trouble. */
|
||||
peer.mutex.Lock()
|
||||
peer.Lock()
|
||||
if peer.endpoint != nil {
|
||||
peer.endpoint.ClearSrc()
|
||||
}
|
||||
peer.mutex.Unlock()
|
||||
peer.Unlock()
|
||||
|
||||
peer.SendHandshakeInitiation(true)
|
||||
}
|
||||
@@ -107,8 +113,8 @@ func expiredRetransmitHandshake(peer *Peer) {
|
||||
|
||||
func expiredSendKeepalive(peer *Peer) {
|
||||
peer.SendKeepalive()
|
||||
if peer.timers.needAnotherKeepalive {
|
||||
peer.timers.needAnotherKeepalive = false
|
||||
if peer.timers.needAnotherKeepalive.Get() {
|
||||
peer.timers.needAnotherKeepalive.Set(false)
|
||||
if peer.timersActive() {
|
||||
peer.timers.sendKeepalive.Mod(KeepaliveTimeout)
|
||||
}
|
||||
@@ -116,19 +122,19 @@ func expiredSendKeepalive(peer *Peer) {
|
||||
}
|
||||
|
||||
func expiredNewHandshake(peer *Peer) {
|
||||
peer.device.log.Debug.Printf("%s: Retrying handshake because we stopped hearing back after %d seconds\n", peer, int((KeepaliveTimeout + RekeyTimeout).Seconds()))
|
||||
peer.device.log.Debug.Printf("%s - Retrying handshake because we stopped hearing back after %d seconds\n", peer, int((KeepaliveTimeout + RekeyTimeout).Seconds()))
|
||||
/* We clear the endpoint address src address, in case this is the cause of trouble. */
|
||||
peer.mutex.Lock()
|
||||
peer.Lock()
|
||||
if peer.endpoint != nil {
|
||||
peer.endpoint.ClearSrc()
|
||||
}
|
||||
peer.mutex.Unlock()
|
||||
peer.Unlock()
|
||||
peer.SendHandshakeInitiation(false)
|
||||
|
||||
}
|
||||
|
||||
func expiredZeroKeyMaterial(peer *Peer) {
|
||||
peer.device.log.Debug.Printf(":%s Removing all keys, since we haven't received a new one in %d seconds\n", peer, int((RejectAfterTime * 3).Seconds()))
|
||||
peer.device.log.Debug.Printf("%s - Removing all keys, since we haven't received a new one in %d seconds\n", peer, int((RejectAfterTime * 3).Seconds()))
|
||||
peer.ZeroAndFlushAll()
|
||||
}
|
||||
|
||||
@@ -140,7 +146,7 @@ func expiredPersistentKeepalive(peer *Peer) {
|
||||
|
||||
/* Should be called after an authenticated data packet is sent. */
|
||||
func (peer *Peer) timersDataSent() {
|
||||
if peer.timersActive() && !peer.timers.newHandshake.isPending {
|
||||
if peer.timersActive() && !peer.timers.newHandshake.IsPending() {
|
||||
peer.timers.newHandshake.Mod(KeepaliveTimeout + RekeyTimeout)
|
||||
}
|
||||
}
|
||||
@@ -148,10 +154,10 @@ func (peer *Peer) timersDataSent() {
|
||||
/* Should be called after an authenticated data packet is received. */
|
||||
func (peer *Peer) timersDataReceived() {
|
||||
if peer.timersActive() {
|
||||
if !peer.timers.sendKeepalive.isPending {
|
||||
if !peer.timers.sendKeepalive.IsPending() {
|
||||
peer.timers.sendKeepalive.Mod(KeepaliveTimeout)
|
||||
} else {
|
||||
peer.timers.needAnotherKeepalive = true
|
||||
peer.timers.needAnotherKeepalive.Set(true)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -182,8 +188,8 @@ func (peer *Peer) timersHandshakeComplete() {
|
||||
if peer.timersActive() {
|
||||
peer.timers.retransmitHandshake.Del()
|
||||
}
|
||||
peer.timers.handshakeAttempts = 0
|
||||
peer.timers.sentLastMinuteHandshake = false
|
||||
atomic.StoreUint32(&peer.timers.handshakeAttempts, 0)
|
||||
peer.timers.sentLastMinuteHandshake.Set(false)
|
||||
atomic.StoreInt64(&peer.stats.lastHandshakeNano, time.Now().UnixNano())
|
||||
}
|
||||
|
||||
@@ -207,9 +213,9 @@ func (peer *Peer) timersInit() {
|
||||
peer.timers.newHandshake = peer.NewTimer(expiredNewHandshake)
|
||||
peer.timers.zeroKeyMaterial = peer.NewTimer(expiredZeroKeyMaterial)
|
||||
peer.timers.persistentKeepalive = peer.NewTimer(expiredPersistentKeepalive)
|
||||
peer.timers.handshakeAttempts = 0
|
||||
peer.timers.sentLastMinuteHandshake = false
|
||||
peer.timers.needAnotherKeepalive = false
|
||||
atomic.StoreUint32(&peer.timers.handshakeAttempts, 0)
|
||||
peer.timers.sentLastMinuteHandshake.Set(false)
|
||||
peer.timers.needAnotherKeepalive.Set(false)
|
||||
}
|
||||
|
||||
func (peer *Peer) timersStop() {
|
||||
55
device/tun.go
Normal file
55
device/tun.go
Normal file
@@ -0,0 +1,55 @@
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package device
|
||||
|
||||
import (
|
||||
"golang.zx2c4.com/wireguard/tun"
|
||||
"sync/atomic"
|
||||
)
|
||||
|
||||
const DefaultMTU = 1420
|
||||
|
||||
func (device *Device) RoutineTUNEventReader() {
|
||||
setUp := false
|
||||
logDebug := device.log.Debug
|
||||
logInfo := device.log.Info
|
||||
logError := device.log.Error
|
||||
|
||||
logDebug.Println("Routine: event worker - started")
|
||||
device.state.starting.Done()
|
||||
|
||||
for event := range device.tun.device.Events() {
|
||||
if event&tun.TUNEventMTUUpdate != 0 {
|
||||
mtu, err := device.tun.device.MTU()
|
||||
old := atomic.LoadInt32(&device.tun.mtu)
|
||||
if err != nil {
|
||||
logError.Println("Failed to load updated MTU of device:", err)
|
||||
} else if int(old) != mtu {
|
||||
if mtu+MessageTransportSize > MaxMessageSize {
|
||||
logInfo.Println("MTU updated:", mtu, "(too large)")
|
||||
} else {
|
||||
logInfo.Println("MTU updated:", mtu)
|
||||
}
|
||||
atomic.StoreInt32(&device.tun.mtu, int32(mtu))
|
||||
}
|
||||
}
|
||||
|
||||
if event&tun.TUNEventUp != 0 && !setUp {
|
||||
logInfo.Println("Interface set up")
|
||||
setUp = true
|
||||
device.Up()
|
||||
}
|
||||
|
||||
if event&tun.TUNEventDown != 0 && setUp {
|
||||
logInfo.Println("Interface set down")
|
||||
setUp = false
|
||||
device.Down()
|
||||
}
|
||||
}
|
||||
|
||||
logDebug.Println("Routine: event worker - stopped")
|
||||
device.state.stopping.Done()
|
||||
}
|
||||
@@ -1,14 +1,14 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2018 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
|
||||
* Copyright (C) 2017-2018 Mathias N. Hall-Andersen <mathias@hall-andersen.dk>.
|
||||
* Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package main
|
||||
package device
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"golang.zx2c4.com/wireguard/ipc"
|
||||
"io"
|
||||
"net"
|
||||
"strconv"
|
||||
@@ -18,23 +18,18 @@ import (
|
||||
)
|
||||
|
||||
type IPCError struct {
|
||||
Code int64
|
||||
int64
|
||||
}
|
||||
|
||||
func (s *IPCError) Error() string {
|
||||
return fmt.Sprintf("IPC error: %d", s.Code)
|
||||
func (s IPCError) Error() string {
|
||||
return fmt.Sprintf("IPC error: %d", s.int64)
|
||||
}
|
||||
|
||||
func (s *IPCError) ErrorCode() int64 {
|
||||
return s.Code
|
||||
func (s IPCError) ErrorCode() int64 {
|
||||
return s.int64
|
||||
}
|
||||
|
||||
func ipcGetOperation(device *Device, socket *bufio.ReadWriter) *IPCError {
|
||||
|
||||
device.log.Debug.Println("UAPI: Processing get operation")
|
||||
|
||||
// create lines
|
||||
|
||||
func (device *Device) IpcGetOperation(socket *bufio.Writer) *IPCError {
|
||||
lines := make([]string, 0, 100)
|
||||
send := func(line string) {
|
||||
lines = append(lines, line)
|
||||
@@ -44,14 +39,14 @@ func ipcGetOperation(device *Device, socket *bufio.ReadWriter) *IPCError {
|
||||
|
||||
// lock required resources
|
||||
|
||||
device.net.mutex.RLock()
|
||||
defer device.net.mutex.RUnlock()
|
||||
device.net.RLock()
|
||||
defer device.net.RUnlock()
|
||||
|
||||
device.staticIdentity.mutex.RLock()
|
||||
defer device.staticIdentity.mutex.RUnlock()
|
||||
device.staticIdentity.RLock()
|
||||
defer device.staticIdentity.RUnlock()
|
||||
|
||||
device.peers.mutex.RLock()
|
||||
defer device.peers.mutex.RUnlock()
|
||||
device.peers.RLock()
|
||||
defer device.peers.RUnlock()
|
||||
|
||||
// serialize device related values
|
||||
|
||||
@@ -70,11 +65,12 @@ func ipcGetOperation(device *Device, socket *bufio.ReadWriter) *IPCError {
|
||||
// serialize each peer state
|
||||
|
||||
for _, peer := range device.peers.keyMap {
|
||||
peer.mutex.RLock()
|
||||
defer peer.mutex.RUnlock()
|
||||
peer.RLock()
|
||||
defer peer.RUnlock()
|
||||
|
||||
send("public_key=" + peer.handshake.remoteStatic.ToHex())
|
||||
send("preshared_key=" + peer.handshake.presharedKey.ToHex())
|
||||
send("protocol_version=1")
|
||||
if peer.endpoint != nil {
|
||||
send("endpoint=" + peer.endpoint.DstToString())
|
||||
}
|
||||
@@ -85,8 +81,8 @@ func ipcGetOperation(device *Device, socket *bufio.ReadWriter) *IPCError {
|
||||
|
||||
send(fmt.Sprintf("last_handshake_time_sec=%d", secs))
|
||||
send(fmt.Sprintf("last_handshake_time_nsec=%d", nano))
|
||||
send(fmt.Sprintf("tx_bytes=%d", peer.stats.txBytes))
|
||||
send(fmt.Sprintf("rx_bytes=%d", peer.stats.rxBytes))
|
||||
send(fmt.Sprintf("tx_bytes=%d", atomic.LoadUint64(&peer.stats.txBytes)))
|
||||
send(fmt.Sprintf("rx_bytes=%d", atomic.LoadUint64(&peer.stats.rxBytes)))
|
||||
send(fmt.Sprintf("persistent_keepalive_interval=%d", peer.persistentKeepaliveInterval))
|
||||
|
||||
for _, ip := range device.allowedips.EntriesForPeer(peer) {
|
||||
@@ -101,16 +97,14 @@ func ipcGetOperation(device *Device, socket *bufio.ReadWriter) *IPCError {
|
||||
for _, line := range lines {
|
||||
_, err := socket.WriteString(line + "\n")
|
||||
if err != nil {
|
||||
return &IPCError{
|
||||
Code: ipcErrorIO,
|
||||
}
|
||||
return &IPCError{ipc.IpcErrorIO}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func ipcSetOperation(device *Device, socket *bufio.ReadWriter) *IPCError {
|
||||
func (device *Device) IpcSetOperation(socket *bufio.Reader) *IPCError {
|
||||
scanner := bufio.NewScanner(socket)
|
||||
logError := device.log.Error
|
||||
logDebug := device.log.Debug
|
||||
@@ -130,7 +124,7 @@ func ipcSetOperation(device *Device, socket *bufio.ReadWriter) *IPCError {
|
||||
}
|
||||
parts := strings.Split(line, "=")
|
||||
if len(parts) != 2 {
|
||||
return &IPCError{Code: ipcErrorProtocol}
|
||||
return &IPCError{ipc.IpcErrorProtocol}
|
||||
}
|
||||
key := parts[0]
|
||||
value := parts[1]
|
||||
@@ -145,9 +139,9 @@ func ipcSetOperation(device *Device, socket *bufio.ReadWriter) *IPCError {
|
||||
err := sk.FromHex(value)
|
||||
if err != nil {
|
||||
logError.Println("Failed to set private_key:", err)
|
||||
return &IPCError{Code: ipcErrorInvalid}
|
||||
return &IPCError{ipc.IpcErrorInvalid}
|
||||
}
|
||||
logDebug.Println("UAPI: Updating device private key")
|
||||
logDebug.Println("UAPI: Updating private key")
|
||||
device.SetPrivateKey(sk)
|
||||
|
||||
case "listen_port":
|
||||
@@ -157,20 +151,20 @@ func ipcSetOperation(device *Device, socket *bufio.ReadWriter) *IPCError {
|
||||
port, err := strconv.ParseUint(value, 10, 16)
|
||||
if err != nil {
|
||||
logError.Println("Failed to parse listen_port:", err)
|
||||
return &IPCError{Code: ipcErrorInvalid}
|
||||
return &IPCError{ipc.IpcErrorInvalid}
|
||||
}
|
||||
|
||||
// update port and rebind
|
||||
|
||||
logDebug.Println("UAPI: Updating listen port")
|
||||
|
||||
device.net.mutex.Lock()
|
||||
device.net.Lock()
|
||||
device.net.port = uint16(port)
|
||||
device.net.mutex.Unlock()
|
||||
device.net.Unlock()
|
||||
|
||||
if err := device.BindUpdate(); err != nil {
|
||||
logError.Println("Failed to set listen_port:", err)
|
||||
return &IPCError{Code: ipcErrorPortInUse}
|
||||
return &IPCError{ipc.IpcErrorPortInUse}
|
||||
}
|
||||
|
||||
case "fwmark":
|
||||
@@ -187,14 +181,14 @@ func ipcSetOperation(device *Device, socket *bufio.ReadWriter) *IPCError {
|
||||
|
||||
if err != nil {
|
||||
logError.Println("Invalid fwmark", err)
|
||||
return &IPCError{Code: ipcErrorInvalid}
|
||||
return &IPCError{ipc.IpcErrorInvalid}
|
||||
}
|
||||
|
||||
logDebug.Println("UAPI: Updating fwmark")
|
||||
|
||||
if err := device.BindSetMark(uint32(fwmark)); err != nil {
|
||||
logError.Println("Failed to update fwmark:", err)
|
||||
return &IPCError{Code: ipcErrorPortInUse}
|
||||
return &IPCError{ipc.IpcErrorPortInUse}
|
||||
}
|
||||
|
||||
case "public_key":
|
||||
@@ -205,14 +199,14 @@ func ipcSetOperation(device *Device, socket *bufio.ReadWriter) *IPCError {
|
||||
case "replace_peers":
|
||||
if value != "true" {
|
||||
logError.Println("Failed to set replace_peers, invalid value:", value)
|
||||
return &IPCError{Code: ipcErrorInvalid}
|
||||
return &IPCError{ipc.IpcErrorInvalid}
|
||||
}
|
||||
logDebug.Println("UAPI: Removing all peers")
|
||||
device.RemoveAllPeers()
|
||||
|
||||
default:
|
||||
logError.Println("Invalid UAPI key (device configuration):", key)
|
||||
return &IPCError{Code: ipcErrorInvalid}
|
||||
logError.Println("Invalid UAPI device key:", key)
|
||||
return &IPCError{ipc.IpcErrorInvalid}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -226,15 +220,15 @@ func ipcSetOperation(device *Device, socket *bufio.ReadWriter) *IPCError {
|
||||
var publicKey NoisePublicKey
|
||||
err := publicKey.FromHex(value)
|
||||
if err != nil {
|
||||
logError.Println("Failed to get peer by public_key:", err)
|
||||
return &IPCError{Code: ipcErrorInvalid}
|
||||
logError.Println("Failed to get peer by public key:", err)
|
||||
return &IPCError{ipc.IpcErrorInvalid}
|
||||
}
|
||||
|
||||
// ignore peer with public key of device
|
||||
|
||||
device.staticIdentity.mutex.RLock()
|
||||
device.staticIdentity.RLock()
|
||||
dummy = device.staticIdentity.publicKey.Equals(publicKey)
|
||||
device.staticIdentity.mutex.RUnlock()
|
||||
device.staticIdentity.RUnlock()
|
||||
|
||||
if dummy {
|
||||
peer = &Peer{}
|
||||
@@ -246,9 +240,9 @@ func ipcSetOperation(device *Device, socket *bufio.ReadWriter) *IPCError {
|
||||
peer, err = device.NewPeer(publicKey)
|
||||
if err != nil {
|
||||
logError.Println("Failed to create new peer:", err)
|
||||
return &IPCError{Code: ipcErrorInvalid}
|
||||
return &IPCError{ipc.IpcErrorInvalid}
|
||||
}
|
||||
logDebug.Println("UAPI: Created new peer:", peer)
|
||||
logDebug.Println(peer, "- UAPI: Created")
|
||||
}
|
||||
|
||||
case "remove":
|
||||
@@ -257,10 +251,10 @@ func ipcSetOperation(device *Device, socket *bufio.ReadWriter) *IPCError {
|
||||
|
||||
if value != "true" {
|
||||
logError.Println("Failed to set remove, invalid value:", value)
|
||||
return &IPCError{Code: ipcErrorInvalid}
|
||||
return &IPCError{ipc.IpcErrorInvalid}
|
||||
}
|
||||
if !dummy {
|
||||
logDebug.Println("UAPI: Removing peer:", peer)
|
||||
logDebug.Println(peer, "- UAPI: Removing")
|
||||
device.RemovePeer(peer.handshake.remoteStatic)
|
||||
}
|
||||
peer = &Peer{}
|
||||
@@ -270,26 +264,26 @@ func ipcSetOperation(device *Device, socket *bufio.ReadWriter) *IPCError {
|
||||
|
||||
// update PSK
|
||||
|
||||
logDebug.Println("UAPI: Updating pre-shared key for peer:", peer)
|
||||
logDebug.Println(peer, "- UAPI: Updating preshared key")
|
||||
|
||||
peer.handshake.mutex.Lock()
|
||||
err := peer.handshake.presharedKey.FromHex(value)
|
||||
peer.handshake.mutex.Unlock()
|
||||
|
||||
if err != nil {
|
||||
logError.Println("Failed to set preshared_key:", err)
|
||||
return &IPCError{Code: ipcErrorInvalid}
|
||||
logError.Println("Failed to set preshared key:", err)
|
||||
return &IPCError{ipc.IpcErrorInvalid}
|
||||
}
|
||||
|
||||
case "endpoint":
|
||||
|
||||
// set endpoint destination
|
||||
|
||||
logDebug.Println("UAPI: Updating endpoint for peer:", peer)
|
||||
logDebug.Println(peer, "- UAPI: Updating endpoint")
|
||||
|
||||
err := func() error {
|
||||
peer.mutex.Lock()
|
||||
defer peer.mutex.Unlock()
|
||||
peer.Lock()
|
||||
defer peer.Unlock()
|
||||
endpoint, err := CreateEndpoint(value)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -299,20 +293,20 @@ func ipcSetOperation(device *Device, socket *bufio.ReadWriter) *IPCError {
|
||||
}()
|
||||
|
||||
if err != nil {
|
||||
logError.Println("Failed to set endpoint:", value)
|
||||
return &IPCError{Code: ipcErrorInvalid}
|
||||
logError.Println("Failed to set endpoint:", err, ":", value)
|
||||
return &IPCError{ipc.IpcErrorInvalid}
|
||||
}
|
||||
|
||||
case "persistent_keepalive_interval":
|
||||
|
||||
// update persistent keepalive interval
|
||||
|
||||
logDebug.Println("UAPI: Updating persistent_keepalive_interval for peer:", peer)
|
||||
logDebug.Println(peer, "- UAPI: Updating persistent keepalive interval")
|
||||
|
||||
secs, err := strconv.ParseUint(value, 10, 16)
|
||||
if err != nil {
|
||||
logError.Println("Failed to set persistent_keepalive_interval:", err)
|
||||
return &IPCError{Code: ipcErrorInvalid}
|
||||
logError.Println("Failed to set persistent keepalive interval:", err)
|
||||
return &IPCError{ipc.IpcErrorInvalid}
|
||||
}
|
||||
|
||||
old := peer.persistentKeepaliveInterval
|
||||
@@ -323,7 +317,7 @@ func ipcSetOperation(device *Device, socket *bufio.ReadWriter) *IPCError {
|
||||
if old == 0 && secs != 0 {
|
||||
if err != nil {
|
||||
logError.Println("Failed to get tun device status:", err)
|
||||
return &IPCError{Code: ipcErrorIO}
|
||||
return &IPCError{ipc.IpcErrorIO}
|
||||
}
|
||||
if device.isUp.Get() && !dummy {
|
||||
peer.SendKeepalive()
|
||||
@@ -332,11 +326,11 @@ func ipcSetOperation(device *Device, socket *bufio.ReadWriter) *IPCError {
|
||||
|
||||
case "replace_allowed_ips":
|
||||
|
||||
logDebug.Println("UAPI: Removing all allowedips for peer:", peer)
|
||||
logDebug.Println(peer, "- UAPI: Removing all allowedips")
|
||||
|
||||
if value != "true" {
|
||||
logError.Println("Failed to set replace_allowed_ips, invalid value:", value)
|
||||
return &IPCError{Code: ipcErrorInvalid}
|
||||
logError.Println("Failed to replace allowedips, invalid value:", value)
|
||||
return &IPCError{ipc.IpcErrorInvalid}
|
||||
}
|
||||
|
||||
if dummy {
|
||||
@@ -347,12 +341,12 @@ func ipcSetOperation(device *Device, socket *bufio.ReadWriter) *IPCError {
|
||||
|
||||
case "allowed_ip":
|
||||
|
||||
logDebug.Println("UAPI: Adding allowed_ip to peer:", peer)
|
||||
logDebug.Println(peer, "- UAPI: Adding allowedip")
|
||||
|
||||
_, network, err := net.ParseCIDR(value)
|
||||
if err != nil {
|
||||
logError.Println("Failed to set allowed_ip:", err)
|
||||
return &IPCError{Code: ipcErrorInvalid}
|
||||
logError.Println("Failed to set allowed ip:", err)
|
||||
return &IPCError{ipc.IpcErrorInvalid}
|
||||
}
|
||||
|
||||
if dummy {
|
||||
@@ -362,9 +356,16 @@ func ipcSetOperation(device *Device, socket *bufio.ReadWriter) *IPCError {
|
||||
ones, _ := network.Mask.Size()
|
||||
device.allowedips.Insert(network.IP, uint(ones), peer)
|
||||
|
||||
case "protocol_version":
|
||||
|
||||
if value != "1" {
|
||||
logError.Println("Invalid protocol version:", value)
|
||||
return &IPCError{ipc.IpcErrorInvalid}
|
||||
}
|
||||
|
||||
default:
|
||||
logError.Println("Invalid UAPI key (peer configuration):", key)
|
||||
return &IPCError{Code: ipcErrorInvalid}
|
||||
logError.Println("Invalid UAPI peer key:", key)
|
||||
return &IPCError{ipc.IpcErrorInvalid}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -372,7 +373,7 @@ func ipcSetOperation(device *Device, socket *bufio.ReadWriter) *IPCError {
|
||||
return nil
|
||||
}
|
||||
|
||||
func ipcHandle(device *Device, socket net.Conn) {
|
||||
func (device *Device) IpcHandle(socket net.Conn) {
|
||||
|
||||
// create buffered read/writer
|
||||
|
||||
@@ -397,12 +398,10 @@ func ipcHandle(device *Device, socket net.Conn) {
|
||||
|
||||
switch op {
|
||||
case "set=1\n":
|
||||
device.log.Debug.Println("Config, set operation")
|
||||
status = ipcSetOperation(device, buffered)
|
||||
status = device.IpcSetOperation(buffered.Reader)
|
||||
|
||||
case "get=1\n":
|
||||
device.log.Debug.Println("Config, get operation")
|
||||
status = ipcGetOperation(device, buffered)
|
||||
status = device.IpcGetOperation(buffered.Writer)
|
||||
|
||||
default:
|
||||
device.log.Error.Println("Invalid UAPI operation:", op)
|
||||
3
device/version.go
Normal file
3
device/version.go
Normal file
@@ -0,0 +1,3 @@
|
||||
package device
|
||||
|
||||
const WireGuardGoVersion = "0.0.20190409"
|
||||
15
donotuseon_linux.go
Normal file
15
donotuseon_linux.go
Normal file
@@ -0,0 +1,15 @@
|
||||
// +build !android
|
||||
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package main
|
||||
|
||||
const DoNotUseThisProgramOnLinux = UseTheKernelModuleInstead
|
||||
|
||||
// --------------------------------------------------------
|
||||
// Do not use this on Linux. Instead use the kernel module.
|
||||
// See wireguard.com/install for more information.
|
||||
// --------------------------------------------------------
|
||||
@@ -1,20 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
echo "# This was generated by ./generate-vendor.sh" > Gopkg.lock
|
||||
echo "# This was generated by ./generate-vendor.sh" > Gopkg.toml
|
||||
|
||||
while read -r package; do
|
||||
cat >> Gopkg.lock <<-_EOF
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "$package"
|
||||
revision = "$(< "$GOPATH/src/$package/.git/refs/heads/master")"
|
||||
|
||||
_EOF
|
||||
cat >> Gopkg.toml <<-_EOF
|
||||
[[constraint]]
|
||||
branch = "master"
|
||||
name = "$package"
|
||||
|
||||
_EOF
|
||||
done < <(sed -n 's/.*"\(golang.org\/x\/[^/]\+\)\/\?.*".*/\1/p' *.go */*.go | sort | uniq)
|
||||
8
go.mod
Normal file
8
go.mod
Normal file
@@ -0,0 +1,8 @@
|
||||
module golang.zx2c4.com/wireguard
|
||||
|
||||
require (
|
||||
github.com/Microsoft/go-winio v0.4.12
|
||||
golang.org/x/crypto v0.0.0-20190320223903-b7391e95e576
|
||||
golang.org/x/net v0.0.0-20190320064053-1272bf9dcd53
|
||||
golang.org/x/sys v0.0.0-20190321052220-f7bb7a8bee54
|
||||
)
|
||||
11
go.sum
Normal file
11
go.sum
Normal file
@@ -0,0 +1,11 @@
|
||||
github.com/Microsoft/go-winio v0.4.12 h1:xAfWHN1IrQ0NJ9TBC0KBZoqLjzDTr1ML+4MywiUOryc=
|
||||
github.com/Microsoft/go-winio v0.4.12/go.mod h1:VhR8bwka0BXejwEJY73c50VrPtXAaKcyvVC4A4RozmA=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190320223903-b7391e95e576 h1:aUX/1G2gFSs4AsJJg2cL3HuoRhCSCz733FE5GUSuaT4=
|
||||
golang.org/x/crypto v0.0.0-20190320223903-b7391e95e576/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/net v0.0.0-20190320064053-1272bf9dcd53 h1:kcXqo9vE6fsZY5X5Rd7R1l7fTgnWaDCVmln65REefiE=
|
||||
golang.org/x/net v0.0.0-20190320064053-1272bf9dcd53/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190321052220-f7bb7a8bee54 h1:xe1/2UUJRmA9iDglQSlkx8c5n3twv58+K0mPpC2zmhA=
|
||||
golang.org/x/sys v0.0.0-20190321052220-f7bb7a8bee54/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
@@ -1,10 +1,11 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0
|
||||
// +build darwin freebsd openbsd
|
||||
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2018 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
|
||||
* Copyright (C) 2017-2018 Mathias N. Hall-Andersen <mathias@hall-andersen.dk>.
|
||||
* Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package main
|
||||
package ipc
|
||||
|
||||
import (
|
||||
"errors"
|
||||
@@ -13,14 +14,16 @@ import (
|
||||
"net"
|
||||
"os"
|
||||
"path"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
var socketDirectory = "/var/run/wireguard"
|
||||
|
||||
const (
|
||||
ipcErrorIO = -int64(unix.EIO)
|
||||
ipcErrorProtocol = -int64(unix.EPROTO)
|
||||
ipcErrorInvalid = -int64(unix.EINVAL)
|
||||
ipcErrorPortInUse = -int64(unix.EADDRINUSE)
|
||||
socketDirectory = "/var/run/wireguard"
|
||||
IpcErrorIO = -int64(unix.EIO)
|
||||
IpcErrorProtocol = -int64(unix.EPROTO)
|
||||
IpcErrorInvalid = -int64(unix.EINVAL)
|
||||
IpcErrorPortInUse = -int64(unix.EADDRINUSE)
|
||||
socketName = "%s.sock"
|
||||
)
|
||||
|
||||
@@ -91,7 +94,7 @@ func UAPIListen(name string, file *os.File) (net.Listener, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
uapi.keventFd, err = unix.Open(socketDirectory, unix.O_EVTONLY, 0)
|
||||
uapi.keventFd, err = unix.Open(socketDirectory, unix.O_RDONLY, 0)
|
||||
if err != nil {
|
||||
unix.Close(uapi.kqueueFd)
|
||||
return nil, err
|
||||
@@ -99,11 +102,13 @@ func UAPIListen(name string, file *os.File) (net.Listener, error) {
|
||||
|
||||
go func(l *UAPIListener) {
|
||||
event := unix.Kevent_t{
|
||||
Ident: uint64(uapi.keventFd),
|
||||
Filter: unix.EVFILT_VNODE,
|
||||
Flags: unix.EV_ADD | unix.EV_ENABLE | unix.EV_ONESHOT,
|
||||
Fflags: unix.NOTE_WRITE,
|
||||
}
|
||||
// Allow this assignment to work with both the 32-bit and 64-bit version
|
||||
// of the above struct. If you know another way, please submit a patch.
|
||||
*(*uintptr)(unsafe.Pointer(&event.Ident)) = uintptr(uapi.keventFd)
|
||||
events := make([]unix.Kevent_t, 1)
|
||||
n := 1
|
||||
var kerr error
|
||||
@@ -145,7 +150,7 @@ func UAPIOpen(name string) (*os.File, error) {
|
||||
|
||||
// check if path exist
|
||||
|
||||
err := os.MkdirAll(socketDirectory, 0700)
|
||||
err := os.MkdirAll(socketDirectory, 0755)
|
||||
if err != nil && !os.IsExist(err) {
|
||||
return nil, err
|
||||
}
|
||||
@@ -162,6 +167,7 @@ func UAPIOpen(name string) (*os.File, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
oldUmask := unix.Umask(0077)
|
||||
listener, err := func() (*net.UnixListener, error) {
|
||||
|
||||
// initial connection attempt
|
||||
@@ -186,6 +192,7 @@ func UAPIOpen(name string) (*os.File, error) {
|
||||
}
|
||||
return net.ListenUnix("unix", addr)
|
||||
}()
|
||||
unix.Umask(oldUmask)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -1,27 +1,27 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2018 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
|
||||
* Copyright (C) 2017-2018 Mathias N. Hall-Andersen <mathias@hall-andersen.dk>.
|
||||
* Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package main
|
||||
package ipc
|
||||
|
||||
import (
|
||||
"./rwcancel"
|
||||
"errors"
|
||||
"fmt"
|
||||
"golang.org/x/sys/unix"
|
||||
"golang.zx2c4.com/wireguard/rwcancel"
|
||||
"net"
|
||||
"os"
|
||||
"path"
|
||||
)
|
||||
|
||||
var socketDirectory = "/var/run/wireguard"
|
||||
|
||||
const (
|
||||
ipcErrorIO = -int64(unix.EIO)
|
||||
ipcErrorProtocol = -int64(unix.EPROTO)
|
||||
ipcErrorInvalid = -int64(unix.EINVAL)
|
||||
ipcErrorPortInUse = -int64(unix.EADDRINUSE)
|
||||
socketDirectory = "/var/run/wireguard"
|
||||
IpcErrorIO = -int64(unix.EIO)
|
||||
IpcErrorProtocol = -int64(unix.EPROTO)
|
||||
IpcErrorInvalid = -int64(unix.EINVAL)
|
||||
IpcErrorPortInUse = -int64(unix.EADDRINUSE)
|
||||
socketName = "%s.sock"
|
||||
)
|
||||
|
||||
@@ -147,7 +147,7 @@ func UAPIOpen(name string) (*os.File, error) {
|
||||
|
||||
// check if path exist
|
||||
|
||||
err := os.MkdirAll(socketDirectory, 0700)
|
||||
err := os.MkdirAll(socketDirectory, 0755)
|
||||
if err != nil && !os.IsExist(err) {
|
||||
return nil, err
|
||||
}
|
||||
@@ -164,6 +164,7 @@ func UAPIOpen(name string) (*os.File, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
oldUmask := unix.Umask(0077)
|
||||
listener, err := func() (*net.UnixListener, error) {
|
||||
|
||||
// initial connection attempt
|
||||
@@ -188,6 +189,7 @@ func UAPIOpen(name string) (*os.File, error) {
|
||||
}
|
||||
return net.ListenUnix("unix", addr)
|
||||
}()
|
||||
unix.Umask(oldUmask)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
87
ipc/uapi_windows.go
Normal file
87
ipc/uapi_windows.go
Normal file
@@ -0,0 +1,87 @@
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package ipc
|
||||
|
||||
import (
|
||||
"github.com/Microsoft/go-winio"
|
||||
"net"
|
||||
)
|
||||
|
||||
//TODO: replace these with actual standard windows error numbers from the win package
|
||||
const (
|
||||
IpcErrorIO = -int64(5)
|
||||
IpcErrorProtocol = -int64(71)
|
||||
IpcErrorInvalid = -int64(22)
|
||||
IpcErrorPortInUse = -int64(98)
|
||||
)
|
||||
|
||||
type UAPIListener struct {
|
||||
listener net.Listener // unix socket listener
|
||||
connNew chan net.Conn
|
||||
connErr chan error
|
||||
kqueueFd int
|
||||
keventFd int
|
||||
}
|
||||
|
||||
func (l *UAPIListener) Accept() (net.Conn, error) {
|
||||
for {
|
||||
select {
|
||||
case conn := <-l.connNew:
|
||||
return conn, nil
|
||||
|
||||
case err := <-l.connErr:
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (l *UAPIListener) Close() error {
|
||||
return l.listener.Close()
|
||||
}
|
||||
|
||||
func (l *UAPIListener) Addr() net.Addr {
|
||||
return l.listener.Addr()
|
||||
}
|
||||
|
||||
func GetSystemSecurityDescriptor() string {
|
||||
//
|
||||
// SDDL encoded.
|
||||
//
|
||||
// (system = SECURITY_NT_AUTHORITY | SECURITY_LOCAL_SYSTEM_RID)
|
||||
// owner: system
|
||||
// grant: GENERIC_ALL to system
|
||||
//
|
||||
return "O:SYD:(A;;GA;;;SY)"
|
||||
}
|
||||
|
||||
func UAPIListen(name string) (net.Listener, error) {
|
||||
config := winio.PipeConfig{
|
||||
SecurityDescriptor: GetSystemSecurityDescriptor(),
|
||||
}
|
||||
listener, err := winio.ListenPipe("\\\\.\\pipe\\WireGuard\\"+name, &config)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
uapi := &UAPIListener{
|
||||
listener: listener,
|
||||
connNew: make(chan net.Conn, 1),
|
||||
connErr: make(chan error, 1),
|
||||
}
|
||||
|
||||
go func(l *UAPIListener) {
|
||||
for {
|
||||
conn, err := l.listener.Accept()
|
||||
if err != nil {
|
||||
l.connErr <- err
|
||||
break
|
||||
}
|
||||
l.connNew <- conn
|
||||
}
|
||||
}(uapi)
|
||||
|
||||
return uapi, nil
|
||||
}
|
||||
55
main.go
55
main.go
@@ -1,17 +1,22 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0
|
||||
// +build !windows
|
||||
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2018 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
|
||||
* Copyright (C) 2017-2018 Mathias N. Hall-Andersen <mathias@hall-andersen.dk>.
|
||||
* Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"golang.zx2c4.com/wireguard/device"
|
||||
"golang.zx2c4.com/wireguard/ipc"
|
||||
"golang.zx2c4.com/wireguard/tun"
|
||||
"os"
|
||||
"os/signal"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -72,6 +77,11 @@ func warning() {
|
||||
}
|
||||
|
||||
func main() {
|
||||
if len(os.Args) == 2 && os.Args[1] == "--version" {
|
||||
fmt.Printf("wireguard-go v%s\n\nUserspace WireGuard daemon for %s-%s.\nInformation available at https://www.wireguard.com.\nCopyright (C) Jason A. Donenfeld <Jason@zx2c4.com>.\n", device.WireGuardGoVersion, runtime.GOOS, runtime.GOARCH)
|
||||
return
|
||||
}
|
||||
|
||||
warning()
|
||||
|
||||
// parse arguments
|
||||
@@ -111,23 +121,23 @@ func main() {
|
||||
logLevel := func() int {
|
||||
switch os.Getenv("LOG_LEVEL") {
|
||||
case "debug":
|
||||
return LogLevelDebug
|
||||
return device.LogLevelDebug
|
||||
case "info":
|
||||
return LogLevelInfo
|
||||
return device.LogLevelInfo
|
||||
case "error":
|
||||
return LogLevelError
|
||||
return device.LogLevelError
|
||||
case "silent":
|
||||
return LogLevelSilent
|
||||
return device.LogLevelSilent
|
||||
}
|
||||
return LogLevelInfo
|
||||
return device.LogLevelInfo
|
||||
}()
|
||||
|
||||
// open TUN device (or use supplied fd)
|
||||
|
||||
tun, err := func() (TUNDevice, error) {
|
||||
tun, err := func() (tun.TUNDevice, error) {
|
||||
tunFdStr := os.Getenv(ENV_WG_TUN_FD)
|
||||
if tunFdStr == "" {
|
||||
return CreateTUN(interfaceName)
|
||||
return tun.CreateTUN(interfaceName, device.DefaultMTU)
|
||||
}
|
||||
|
||||
// construct tun device from supplied fd
|
||||
@@ -137,8 +147,13 @@ func main() {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = syscall.SetNonblock(int(fd), true)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
file := os.NewFile(uintptr(fd), "")
|
||||
return CreateTUNFromFile(file)
|
||||
return tun.CreateTUNFromFile(file, device.DefaultMTU)
|
||||
}()
|
||||
|
||||
if err == nil {
|
||||
@@ -148,11 +163,13 @@ func main() {
|
||||
}
|
||||
}
|
||||
|
||||
logger := NewLogger(
|
||||
logger := device.NewLogger(
|
||||
logLevel,
|
||||
fmt.Sprintf("(%s) ", interfaceName),
|
||||
)
|
||||
|
||||
logger.Info.Println("Starting wireguard-go version", device.WireGuardGoVersion)
|
||||
|
||||
logger.Debug.Println("Debug log enabled")
|
||||
|
||||
if err != nil {
|
||||
@@ -165,7 +182,7 @@ func main() {
|
||||
fileUAPI, err := func() (*os.File, error) {
|
||||
uapiFdStr := os.Getenv(ENV_WG_UAPI_FD)
|
||||
if uapiFdStr == "" {
|
||||
return UAPIOpen(interfaceName)
|
||||
return ipc.UAPIOpen(interfaceName)
|
||||
}
|
||||
|
||||
// use supplied fd
|
||||
@@ -191,7 +208,7 @@ func main() {
|
||||
env = append(env, fmt.Sprintf("%s=4", ENV_WG_UAPI_FD))
|
||||
env = append(env, fmt.Sprintf("%s=1", ENV_WG_PROCESS_FOREGROUND))
|
||||
files := [3]*os.File{}
|
||||
if os.Getenv("LOG_LEVEL") != "" && logLevel != LogLevelSilent {
|
||||
if os.Getenv("LOG_LEVEL") != "" && logLevel != device.LogLevelSilent {
|
||||
files[0], _ = os.Open(os.DevNull)
|
||||
files[1] = os.Stdout
|
||||
files[2] = os.Stderr
|
||||
@@ -231,14 +248,14 @@ func main() {
|
||||
return
|
||||
}
|
||||
|
||||
device := NewDevice(tun, logger)
|
||||
device := device.NewDevice(tun, logger)
|
||||
|
||||
logger.Info.Println("Device started")
|
||||
|
||||
errs := make(chan error)
|
||||
term := make(chan os.Signal)
|
||||
term := make(chan os.Signal, 1)
|
||||
|
||||
uapi, err := UAPIListen(interfaceName, fileUAPI)
|
||||
uapi, err := ipc.UAPIListen(interfaceName, fileUAPI)
|
||||
if err != nil {
|
||||
logger.Error.Println("Failed to listen on uapi socket:", err)
|
||||
os.Exit(ExitSetupFailed)
|
||||
@@ -251,7 +268,7 @@ func main() {
|
||||
errs <- err
|
||||
return
|
||||
}
|
||||
go ipcHandle(device, conn)
|
||||
go device.IpcHandle(conn)
|
||||
}
|
||||
}()
|
||||
|
||||
@@ -259,7 +276,7 @@ func main() {
|
||||
|
||||
// wait for program to terminate
|
||||
|
||||
signal.Notify(term, os.Kill)
|
||||
signal.Notify(term, syscall.SIGTERM)
|
||||
signal.Notify(term, os.Interrupt)
|
||||
|
||||
select {
|
||||
|
||||
91
main_windows.go
Normal file
91
main_windows.go
Normal file
@@ -0,0 +1,91 @@
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"golang.zx2c4.com/wireguard/device"
|
||||
"golang.zx2c4.com/wireguard/ipc"
|
||||
"os"
|
||||
"os/signal"
|
||||
"syscall"
|
||||
|
||||
"golang.zx2c4.com/wireguard/tun"
|
||||
)
|
||||
|
||||
const (
|
||||
ExitSetupSuccess = 0
|
||||
ExitSetupFailed = 1
|
||||
)
|
||||
|
||||
func main() {
|
||||
if len(os.Args) != 2 {
|
||||
os.Exit(ExitSetupFailed)
|
||||
}
|
||||
interfaceName := os.Args[1]
|
||||
|
||||
logger := device.NewLogger(
|
||||
device.LogLevelDebug,
|
||||
fmt.Sprintf("(%s) ", interfaceName),
|
||||
)
|
||||
logger.Info.Println("Starting wireguard-go version", WireGuardGoVersion)
|
||||
logger.Debug.Println("Debug log enabled")
|
||||
|
||||
tun, err := tun.CreateTUN(interfaceName)
|
||||
if err == nil {
|
||||
realInterfaceName, err2 := tun.Name()
|
||||
if err2 == nil {
|
||||
interfaceName = realInterfaceName
|
||||
}
|
||||
} else {
|
||||
logger.Error.Println("Failed to create TUN device:", err)
|
||||
os.Exit(ExitSetupFailed)
|
||||
}
|
||||
|
||||
device := device.NewDevice(tun, logger)
|
||||
device.Up()
|
||||
logger.Info.Println("Device started")
|
||||
|
||||
uapi, err := ipc.UAPIListen(interfaceName)
|
||||
if err != nil {
|
||||
logger.Error.Println("Failed to listen on uapi socket:", err)
|
||||
os.Exit(ExitSetupFailed)
|
||||
}
|
||||
|
||||
errs := make(chan error)
|
||||
term := make(chan os.Signal, 1)
|
||||
|
||||
go func() {
|
||||
for {
|
||||
conn, err := uapi.Accept()
|
||||
if err != nil {
|
||||
errs <- err
|
||||
return
|
||||
}
|
||||
go device.IpcHandle(conn)
|
||||
}
|
||||
}()
|
||||
logger.Info.Println("UAPI listener started")
|
||||
|
||||
// wait for program to terminate
|
||||
|
||||
signal.Notify(term, os.Interrupt)
|
||||
signal.Notify(term, os.Kill)
|
||||
signal.Notify(term, syscall.SIGTERM)
|
||||
|
||||
select {
|
||||
case <-term:
|
||||
case <-errs:
|
||||
case <-device.Wait():
|
||||
}
|
||||
|
||||
// clean up
|
||||
|
||||
uapi.Close()
|
||||
device.Close()
|
||||
|
||||
logger.Info.Println("Shutting down")
|
||||
}
|
||||
63
misc.go
63
misc.go
@@ -1,63 +0,0 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0
|
||||
*
|
||||
* Copyright (C) 2017-2018 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
|
||||
* Copyright (C) 2017-2018 Mathias N. Hall-Andersen <mathias@hall-andersen.dk>.
|
||||
*/
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"sync/atomic"
|
||||
)
|
||||
|
||||
/* Atomic Boolean */
|
||||
|
||||
const (
|
||||
AtomicFalse = int32(iota)
|
||||
AtomicTrue
|
||||
)
|
||||
|
||||
type AtomicBool struct {
|
||||
flag int32
|
||||
}
|
||||
|
||||
func (a *AtomicBool) Get() bool {
|
||||
return atomic.LoadInt32(&a.flag) == AtomicTrue
|
||||
}
|
||||
|
||||
func (a *AtomicBool) Swap(val bool) bool {
|
||||
flag := AtomicFalse
|
||||
if val {
|
||||
flag = AtomicTrue
|
||||
}
|
||||
return atomic.SwapInt32(&a.flag, flag) == AtomicTrue
|
||||
}
|
||||
|
||||
func (a *AtomicBool) Set(val bool) {
|
||||
flag := AtomicFalse
|
||||
if val {
|
||||
flag = AtomicTrue
|
||||
}
|
||||
atomic.StoreInt32(&a.flag, flag)
|
||||
}
|
||||
|
||||
/* Integer manipulation */
|
||||
|
||||
func toInt32(n uint32) int32 {
|
||||
mask := uint32(1 << 31)
|
||||
return int32(-(n & mask) + (n & ^mask))
|
||||
}
|
||||
|
||||
func min(a, b uint) uint {
|
||||
if a > b {
|
||||
return b
|
||||
}
|
||||
return a
|
||||
}
|
||||
|
||||
func minUint64(a uint64, b uint64) uint64 {
|
||||
if a > b {
|
||||
return b
|
||||
}
|
||||
return a
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2018 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
|
||||
* Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package ratelimiter
|
||||
@@ -20,38 +20,38 @@ const (
|
||||
)
|
||||
|
||||
type RatelimiterEntry struct {
|
||||
mutex sync.Mutex
|
||||
sync.Mutex
|
||||
lastTime time.Time
|
||||
tokens int64
|
||||
}
|
||||
|
||||
type Ratelimiter struct {
|
||||
mutex sync.RWMutex
|
||||
stop chan struct{}
|
||||
sync.RWMutex
|
||||
stopReset chan struct{}
|
||||
tableIPv4 map[[net.IPv4len]byte]*RatelimiterEntry
|
||||
tableIPv6 map[[net.IPv6len]byte]*RatelimiterEntry
|
||||
}
|
||||
|
||||
func (rate *Ratelimiter) Close() {
|
||||
rate.mutex.Lock()
|
||||
defer rate.mutex.Unlock()
|
||||
rate.Lock()
|
||||
defer rate.Unlock()
|
||||
|
||||
if rate.stop != nil {
|
||||
close(rate.stop)
|
||||
if rate.stopReset != nil {
|
||||
close(rate.stopReset)
|
||||
}
|
||||
}
|
||||
|
||||
func (rate *Ratelimiter) Init() {
|
||||
rate.mutex.Lock()
|
||||
defer rate.mutex.Unlock()
|
||||
rate.Lock()
|
||||
defer rate.Unlock()
|
||||
|
||||
// stop any ongoing garbage collection routine
|
||||
|
||||
if rate.stop != nil {
|
||||
close(rate.stop)
|
||||
if rate.stopReset != nil {
|
||||
close(rate.stopReset)
|
||||
}
|
||||
|
||||
rate.stop = make(chan struct{})
|
||||
rate.stopReset = make(chan struct{})
|
||||
rate.tableIPv4 = make(map[[net.IPv4len]byte]*RatelimiterEntry)
|
||||
rate.tableIPv6 = make(map[[net.IPv6len]byte]*RatelimiterEntry)
|
||||
|
||||
@@ -59,30 +59,39 @@ func (rate *Ratelimiter) Init() {
|
||||
|
||||
go func() {
|
||||
ticker := time.NewTicker(time.Second)
|
||||
ticker.Stop()
|
||||
for {
|
||||
select {
|
||||
case <-rate.stop:
|
||||
case _, ok := <-rate.stopReset:
|
||||
ticker.Stop()
|
||||
if ok {
|
||||
ticker = time.NewTicker(time.Second)
|
||||
} else {
|
||||
return
|
||||
}
|
||||
case <-ticker.C:
|
||||
func() {
|
||||
rate.mutex.Lock()
|
||||
defer rate.mutex.Unlock()
|
||||
rate.Lock()
|
||||
defer rate.Unlock()
|
||||
|
||||
for key, entry := range rate.tableIPv4 {
|
||||
entry.mutex.Lock()
|
||||
entry.Lock()
|
||||
if time.Now().Sub(entry.lastTime) > garbageCollectTime {
|
||||
delete(rate.tableIPv4, key)
|
||||
}
|
||||
entry.mutex.Unlock()
|
||||
entry.Unlock()
|
||||
}
|
||||
|
||||
for key, entry := range rate.tableIPv6 {
|
||||
entry.mutex.Lock()
|
||||
entry.Lock()
|
||||
if time.Now().Sub(entry.lastTime) > garbageCollectTime {
|
||||
delete(rate.tableIPv6, key)
|
||||
}
|
||||
entry.mutex.Unlock()
|
||||
entry.Unlock()
|
||||
}
|
||||
|
||||
if len(rate.tableIPv4) == 0 && len(rate.tableIPv6) == 0 {
|
||||
ticker.Stop()
|
||||
}
|
||||
}()
|
||||
}
|
||||
@@ -100,7 +109,7 @@ func (rate *Ratelimiter) Allow(ip net.IP) bool {
|
||||
IPv4 := ip.To4()
|
||||
IPv6 := ip.To16()
|
||||
|
||||
rate.mutex.RLock()
|
||||
rate.RLock()
|
||||
|
||||
if IPv4 != nil {
|
||||
copy(keyIPv4[:], IPv4)
|
||||
@@ -110,7 +119,7 @@ func (rate *Ratelimiter) Allow(ip net.IP) bool {
|
||||
entry = rate.tableIPv6[keyIPv6]
|
||||
}
|
||||
|
||||
rate.mutex.RUnlock()
|
||||
rate.RUnlock()
|
||||
|
||||
// make new entry if not found
|
||||
|
||||
@@ -118,19 +127,25 @@ func (rate *Ratelimiter) Allow(ip net.IP) bool {
|
||||
entry = new(RatelimiterEntry)
|
||||
entry.tokens = maxTokens - packetCost
|
||||
entry.lastTime = time.Now()
|
||||
rate.mutex.Lock()
|
||||
rate.Lock()
|
||||
if IPv4 != nil {
|
||||
rate.tableIPv4[keyIPv4] = entry
|
||||
if len(rate.tableIPv4) == 1 && len(rate.tableIPv6) == 0 {
|
||||
rate.stopReset <- struct{}{}
|
||||
}
|
||||
} else {
|
||||
rate.tableIPv6[keyIPv6] = entry
|
||||
if len(rate.tableIPv6) == 1 && len(rate.tableIPv4) == 0 {
|
||||
rate.stopReset <- struct{}{}
|
||||
}
|
||||
rate.mutex.Unlock()
|
||||
}
|
||||
rate.Unlock()
|
||||
return true
|
||||
}
|
||||
|
||||
// add tokens to entry
|
||||
|
||||
entry.mutex.Lock()
|
||||
entry.Lock()
|
||||
now := time.Now()
|
||||
entry.tokens += now.Sub(entry.lastTime).Nanoseconds()
|
||||
entry.lastTime = now
|
||||
@@ -142,9 +157,9 @@ func (rate *Ratelimiter) Allow(ip net.IP) bool {
|
||||
|
||||
if entry.tokens > packetCost {
|
||||
entry.tokens -= packetCost
|
||||
entry.mutex.Unlock()
|
||||
entry.Unlock()
|
||||
return true
|
||||
}
|
||||
entry.mutex.Unlock()
|
||||
entry.Unlock()
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2018 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
|
||||
* Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package ratelimiter
|
||||
|
||||
@@ -1,12 +1,9 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2018 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
|
||||
* Copyright (C) 2017-2018 Mathias N. Hall-Andersen <mathias@hall-andersen.dk>.
|
||||
* Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package main
|
||||
|
||||
/* Copyright (C) 2015-2017 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved. */
|
||||
package replay
|
||||
|
||||
/* Implementation of RFC6479
|
||||
* https://tools.ietf.org/html/rfc6479
|
||||
@@ -32,6 +29,13 @@ const (
|
||||
BacktrackWords = CounterBitsTotal / _WordSize
|
||||
)
|
||||
|
||||
func minUint64(a uint64, b uint64) uint64 {
|
||||
if a > b {
|
||||
return b
|
||||
}
|
||||
return a
|
||||
}
|
||||
|
||||
type ReplayFilter struct {
|
||||
counter uint64
|
||||
backtrack [BacktrackWords]uintptr
|
||||
@@ -42,8 +46,8 @@ func (filter *ReplayFilter) Init() {
|
||||
filter.backtrack[0] = 0
|
||||
}
|
||||
|
||||
func (filter *ReplayFilter) ValidateCounter(counter uint64) bool {
|
||||
if counter >= RejectAfterMessages {
|
||||
func (filter *ReplayFilter) ValidateCounter(counter uint64, limit uint64) bool {
|
||||
if counter >= limit {
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2018 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
|
||||
* Copyright (C) 2017-2018 Mathias N. Hall-Andersen <mathias@hall-andersen.dk>.
|
||||
* Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package main
|
||||
package replay
|
||||
|
||||
import (
|
||||
"testing"
|
||||
@@ -15,6 +14,8 @@ import (
|
||||
*
|
||||
*/
|
||||
|
||||
const RejectAfterMessages = (1 << 64) - (1 << 4) - 1
|
||||
|
||||
func TestReplay(t *testing.T) {
|
||||
var filter ReplayFilter
|
||||
|
||||
@@ -23,7 +24,7 @@ func TestReplay(t *testing.T) {
|
||||
testNumber := 0
|
||||
T := func(n uint64, v bool) {
|
||||
testNumber++
|
||||
if filter.ValidateCounter(n) != v {
|
||||
if filter.ValidateCounter(n, RejectAfterMessages) != v {
|
||||
t.Fatal("Test", testNumber, "failed", n, v)
|
||||
}
|
||||
}
|
||||
22
rwcancel/fdset.go
Normal file
22
rwcancel/fdset.go
Normal file
@@ -0,0 +1,22 @@
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package rwcancel
|
||||
|
||||
import "golang.org/x/sys/unix"
|
||||
|
||||
type fdSet struct {
|
||||
unix.FdSet
|
||||
}
|
||||
|
||||
func (fdset *fdSet) set(i int) {
|
||||
bits := 32 << (^uint(0) >> 63)
|
||||
fdset.Bits[i/bits] |= 1 << uint(i%bits)
|
||||
}
|
||||
|
||||
func (fdset *fdSet) check(i int) bool {
|
||||
bits := 32 << (^uint(0) >> 63)
|
||||
return (fdset.Bits[i/bits] & (1 << uint(i%bits))) != 0
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2018 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
|
||||
* Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package rwcancel
|
||||
@@ -12,26 +12,6 @@ import (
|
||||
"syscall"
|
||||
)
|
||||
|
||||
type RWCancel struct {
|
||||
fd int
|
||||
closingReader *os.File
|
||||
closingWriter *os.File
|
||||
}
|
||||
|
||||
type fdSet struct {
|
||||
fdset unix.FdSet
|
||||
}
|
||||
|
||||
func (fdset *fdSet) set(i int) {
|
||||
bits := 32 << (^uint(0) >> 63)
|
||||
fdset.fdset.Bits[i/bits] |= 1 << uint(i%bits)
|
||||
}
|
||||
|
||||
func (fdset *fdSet) check(i int) bool {
|
||||
bits := 32 << (^uint(0) >> 63)
|
||||
return (fdset.fdset.Bits[i/bits] & (1 << uint(i%bits))) != 0
|
||||
}
|
||||
|
||||
func max(a, b int) int {
|
||||
if a > b {
|
||||
return a
|
||||
@@ -39,6 +19,12 @@ func max(a, b int) int {
|
||||
return b
|
||||
}
|
||||
|
||||
type RWCancel struct {
|
||||
fd int
|
||||
closingReader *os.File
|
||||
closingWriter *os.File
|
||||
}
|
||||
|
||||
func NewRWCancel(fd int) (*RWCancel, error) {
|
||||
err := unix.SetNonblock(fd, true)
|
||||
if err != nil {
|
||||
@@ -54,15 +40,16 @@ func NewRWCancel(fd int) (*RWCancel, error) {
|
||||
return &rwcancel, nil
|
||||
}
|
||||
|
||||
/* https://golang.org/src/crypto/rand/eagain.go */
|
||||
func ErrorIsEAGAIN(err error) bool {
|
||||
func RetryAfterError(err error) bool {
|
||||
if pe, ok := err.(*os.PathError); ok {
|
||||
if errno, ok := pe.Err.(syscall.Errno); ok && errno == syscall.EAGAIN {
|
||||
err = pe.Err
|
||||
}
|
||||
if errno, ok := err.(syscall.Errno); ok {
|
||||
switch errno {
|
||||
case syscall.EAGAIN, syscall.EINTR:
|
||||
return true
|
||||
}
|
||||
}
|
||||
if errno, ok := err.(syscall.Errno); ok && errno == syscall.EAGAIN {
|
||||
return true
|
||||
|
||||
}
|
||||
return false
|
||||
}
|
||||
@@ -72,7 +59,7 @@ func (rw *RWCancel) ReadyRead() bool {
|
||||
fdset := fdSet{}
|
||||
fdset.set(rw.fd)
|
||||
fdset.set(closeFd)
|
||||
err := unixSelect(max(rw.fd, closeFd)+1, &fdset.fdset, nil, nil, nil)
|
||||
err := unixSelect(max(rw.fd, closeFd)+1, &fdset.FdSet, nil, nil, nil)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
@@ -87,7 +74,7 @@ func (rw *RWCancel) ReadyWrite() bool {
|
||||
fdset := fdSet{}
|
||||
fdset.set(rw.fd)
|
||||
fdset.set(closeFd)
|
||||
err := unixSelect(max(rw.fd, closeFd)+1, nil, &fdset.fdset, nil, nil)
|
||||
err := unixSelect(max(rw.fd, closeFd)+1, nil, &fdset.FdSet, nil, nil)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
@@ -100,7 +87,7 @@ func (rw *RWCancel) ReadyWrite() bool {
|
||||
func (rw *RWCancel) Read(p []byte) (n int, err error) {
|
||||
for {
|
||||
n, err := unix.Read(rw.fd, p)
|
||||
if err == nil || !ErrorIsEAGAIN(err) {
|
||||
if err == nil || !RetryAfterError(err) {
|
||||
return n, err
|
||||
}
|
||||
if !rw.ReadyRead() {
|
||||
@@ -112,7 +99,7 @@ func (rw *RWCancel) Read(p []byte) (n int, err error) {
|
||||
func (rw *RWCancel) Write(p []byte) (n int, err error) {
|
||||
for {
|
||||
n, err := unix.Write(rw.fd, p)
|
||||
if err == nil || !ErrorIsEAGAIN(err) {
|
||||
if err == nil || !RetryAfterError(err) {
|
||||
return n, err
|
||||
}
|
||||
if !rw.ReadyWrite() {
|
||||
@@ -1,6 +1,8 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0
|
||||
// +build !linux
|
||||
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2018 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
|
||||
* Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package rwcancel
|
||||
@@ -1,6 +1,6 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2018 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
|
||||
* Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package rwcancel
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2018 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
|
||||
* Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package tai64n
|
||||
@@ -12,7 +12,8 @@ import (
|
||||
)
|
||||
|
||||
const TimestampSize = 12
|
||||
const base = uint64(4611686018427387914)
|
||||
const base = uint64(0x400000000000000a)
|
||||
const whitenerMask = uint32(0x1000000 - 1)
|
||||
|
||||
type Timestamp [TimestampSize]byte
|
||||
|
||||
@@ -20,7 +21,7 @@ func Now() Timestamp {
|
||||
var tai64n Timestamp
|
||||
now := time.Now()
|
||||
secs := base + uint64(now.Unix())
|
||||
nano := uint32(now.UnixNano())
|
||||
nano := uint32(now.Nanosecond()) &^ whitenerMask
|
||||
binary.BigEndian.PutUint64(tai64n[:], secs)
|
||||
binary.BigEndian.PutUint32(tai64n[8:], nano)
|
||||
return tai64n
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2018 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
|
||||
* Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package tai64n
|
||||
|
||||
71
tun.go
71
tun.go
@@ -1,71 +0,0 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0
|
||||
*
|
||||
* Copyright (C) 2017-2018 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
|
||||
* Copyright (C) 2017-2018 Mathias N. Hall-Andersen <mathias@hall-andersen.dk>.
|
||||
*/
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"os"
|
||||
"sync/atomic"
|
||||
)
|
||||
|
||||
const DefaultMTU = 1420
|
||||
|
||||
type TUNEvent int
|
||||
|
||||
const (
|
||||
TUNEventUp = 1 << iota
|
||||
TUNEventDown
|
||||
TUNEventMTUUpdate
|
||||
)
|
||||
|
||||
type TUNDevice interface {
|
||||
File() *os.File // returns the file descriptor of the device
|
||||
Read([]byte, int) (int, error) // read a packet from the device (without any additional headers)
|
||||
Write([]byte, int) (int, error) // writes a packet to the device (without any additional headers)
|
||||
MTU() (int, error) // returns the MTU of the device
|
||||
Name() (string, error) // fetches and returns the current name
|
||||
Events() chan TUNEvent // returns a constant channel of events related to the device
|
||||
Close() error // stops the device and closes the event channel
|
||||
}
|
||||
|
||||
func (device *Device) RoutineTUNEventReader() {
|
||||
setUp := false
|
||||
logInfo := device.log.Info
|
||||
logError := device.log.Error
|
||||
|
||||
device.state.starting.Done()
|
||||
|
||||
for event := range device.tun.device.Events() {
|
||||
if event&TUNEventMTUUpdate != 0 {
|
||||
mtu, err := device.tun.device.MTU()
|
||||
old := atomic.LoadInt32(&device.tun.mtu)
|
||||
if err != nil {
|
||||
logError.Println("Failed to load updated MTU of device:", err)
|
||||
} else if int(old) != mtu {
|
||||
if mtu+MessageTransportSize > MaxMessageSize {
|
||||
logInfo.Println("MTU updated:", mtu, "(too large)")
|
||||
} else {
|
||||
logInfo.Println("MTU updated:", mtu)
|
||||
}
|
||||
atomic.StoreInt32(&device.tun.mtu, int32(mtu))
|
||||
}
|
||||
}
|
||||
|
||||
if event&TUNEventUp != 0 && !setUp {
|
||||
logInfo.Println("Interface set up")
|
||||
setUp = true
|
||||
device.Up()
|
||||
}
|
||||
|
||||
if event&TUNEventDown != 0 && setUp {
|
||||
logInfo.Println("Interface set down")
|
||||
setUp = false
|
||||
device.Down()
|
||||
}
|
||||
}
|
||||
|
||||
device.state.stopping.Done()
|
||||
}
|
||||
@@ -1,13 +1,14 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2018 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
|
||||
* Copyright (C) 2017-2018 Mathias N. Hall-Andersen <mathias@hall-andersen.dk>.
|
||||
* Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package main
|
||||
package tun
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"golang.zx2c4.com/wireguard/tun"
|
||||
"os"
|
||||
"testing"
|
||||
)
|
||||
@@ -19,7 +20,7 @@ type DummyTUN struct {
|
||||
name string
|
||||
mtu int
|
||||
packets chan []byte
|
||||
events chan TUNEvent
|
||||
events chan tun.TUNEvent
|
||||
}
|
||||
|
||||
func (tun *DummyTUN) File() *os.File {
|
||||
@@ -40,23 +41,29 @@ func (tun *DummyTUN) Write(d []byte, offset int) (int, error) {
|
||||
}
|
||||
|
||||
func (tun *DummyTUN) Close() error {
|
||||
close(tun.events)
|
||||
close(tun.packets)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (tun *DummyTUN) Events() chan TUNEvent {
|
||||
func (tun *DummyTUN) Events() chan tun.TUNEvent {
|
||||
return tun.events
|
||||
}
|
||||
|
||||
func (tun *DummyTUN) Read(d []byte, offset int) (int, error) {
|
||||
t := <-tun.packets
|
||||
t, ok := <-tun.packets
|
||||
if !ok {
|
||||
return 0, errors.New("device closed")
|
||||
}
|
||||
copy(d[offset:], t)
|
||||
return len(t), nil
|
||||
}
|
||||
|
||||
func CreateDummyTUN(name string) (TUNDevice, error) {
|
||||
func CreateDummyTUN(name string) (tun.TUNDevice, error) {
|
||||
var dummy DummyTUN
|
||||
dummy.mtu = 0
|
||||
dummy.packets = make(chan []byte, 100)
|
||||
dummy.events = make(chan tun.TUNEvent, 10)
|
||||
return &dummy, nil
|
||||
}
|
||||
|
||||
24
tun/operateonfd.go
Normal file
24
tun/operateonfd.go
Normal file
@@ -0,0 +1,24 @@
|
||||
// +build !windows
|
||||
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package tun
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
func (tun *NativeTun) operateOnFd(fn func(fd uintptr)) {
|
||||
sysconn, err := tun.tunFile.SyscallConn()
|
||||
if err != nil {
|
||||
tun.errors <- fmt.Errorf("unable to find sysconn for tunfile: %s", err.Error())
|
||||
return
|
||||
}
|
||||
err = sysconn.Control(fn)
|
||||
if err != nil {
|
||||
tun.errors <- fmt.Errorf("unable to control sysconn for tunfile: %s", err.Error())
|
||||
}
|
||||
}
|
||||
29
tun/tun.go
Normal file
29
tun/tun.go
Normal file
@@ -0,0 +1,29 @@
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package tun
|
||||
|
||||
import (
|
||||
"os"
|
||||
)
|
||||
|
||||
type TUNEvent int
|
||||
|
||||
const (
|
||||
TUNEventUp = 1 << iota
|
||||
TUNEventDown
|
||||
TUNEventMTUUpdate
|
||||
)
|
||||
|
||||
type TUNDevice interface {
|
||||
File() *os.File // returns the file descriptor of the device
|
||||
Read([]byte, int) (int, error) // read a packet from the device (without any additional headers)
|
||||
Write([]byte, int) (int, error) // writes a packet to the device (without any additional headers)
|
||||
Flush() error // flush all previous writes to the device
|
||||
MTU() (int, error) // returns the MTU of the device
|
||||
Name() (string, error) // fetches and returns the current name
|
||||
Events() chan TUNEvent // returns a constant channel of events related to the device
|
||||
Close() error // stops the device and closes the event channel
|
||||
}
|
||||
@@ -1,22 +1,18 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2018 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
|
||||
* Copyright (C) 2017-2018 Mathias N. Hall-Andersen <mathias@hall-andersen.dk>.
|
||||
* Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package main
|
||||
package tun
|
||||
|
||||
import (
|
||||
"./rwcancel"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
"golang.org/x/net/ipv6"
|
||||
"golang.org/x/sys/unix"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"os"
|
||||
"time"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
@@ -35,25 +31,77 @@ type sockaddrCtl struct {
|
||||
scReserved [5]uint32
|
||||
}
|
||||
|
||||
// NativeTun is a hack to work around the first 4 bytes "packet
|
||||
// information" because there doesn't seem to be an IFF_NO_PI for darwin.
|
||||
type NativeTun struct {
|
||||
name string
|
||||
fd *os.File
|
||||
rwcancel *rwcancel.RWCancel
|
||||
mtu int
|
||||
tunFile *os.File
|
||||
events chan TUNEvent
|
||||
errors chan error
|
||||
statusListenersShutdown chan struct{}
|
||||
routeSocket int
|
||||
}
|
||||
|
||||
var sockaddrCtlSize uintptr = 32
|
||||
|
||||
func CreateTUN(name string) (TUNDevice, error) {
|
||||
func (tun *NativeTun) routineRouteListener(tunIfindex int) {
|
||||
var (
|
||||
statusUp bool
|
||||
statusMTU int
|
||||
)
|
||||
|
||||
defer close(tun.events)
|
||||
|
||||
data := make([]byte, os.Getpagesize())
|
||||
for {
|
||||
retry:
|
||||
n, err := unix.Read(tun.routeSocket, data)
|
||||
if err != nil {
|
||||
if errno, ok := err.(syscall.Errno); ok && errno == syscall.EINTR {
|
||||
goto retry
|
||||
}
|
||||
tun.errors <- err
|
||||
return
|
||||
}
|
||||
|
||||
if n < 14 {
|
||||
continue
|
||||
}
|
||||
|
||||
if data[3 /* type */] != unix.RTM_IFINFO {
|
||||
continue
|
||||
}
|
||||
ifindex := int(*(*uint16)(unsafe.Pointer(&data[12 /* ifindex */])))
|
||||
if ifindex != tunIfindex {
|
||||
continue
|
||||
}
|
||||
|
||||
iface, err := net.InterfaceByIndex(ifindex)
|
||||
if err != nil {
|
||||
tun.errors <- err
|
||||
return
|
||||
}
|
||||
|
||||
// Up / Down event
|
||||
up := (iface.Flags & net.FlagUp) != 0
|
||||
if up != statusUp && up {
|
||||
tun.events <- TUNEventUp
|
||||
}
|
||||
if up != statusUp && !up {
|
||||
tun.events <- TUNEventDown
|
||||
}
|
||||
statusUp = up
|
||||
|
||||
// MTU changes
|
||||
if iface.MTU != statusMTU {
|
||||
tun.events <- TUNEventMTUUpdate
|
||||
}
|
||||
statusMTU = iface.MTU
|
||||
}
|
||||
}
|
||||
|
||||
func CreateTUN(name string, mtu int) (TUNDevice, error) {
|
||||
ifIndex := -1
|
||||
if name != "utun" {
|
||||
fmt.Sscanf(name, "utun%d", &ifIndex)
|
||||
if ifIndex < 0 {
|
||||
_, err := fmt.Sscanf(name, "utun%d", &ifIndex)
|
||||
if err != nil || ifIndex < 0 {
|
||||
return nil, fmt.Errorf("Interface name must be utun[0-9]*")
|
||||
}
|
||||
}
|
||||
@@ -103,10 +151,14 @@ func CreateTUN(name string) (TUNDevice, error) {
|
||||
return nil, fmt.Errorf("SYS_CONNECT: %v", errno)
|
||||
}
|
||||
|
||||
tun, err := CreateTUNFromFile(os.NewFile(uintptr(fd), ""))
|
||||
err = syscall.SetNonblock(fd, true)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
tun, err := CreateTUNFromFile(os.NewFile(uintptr(fd), ""), mtu)
|
||||
|
||||
if err == nil && name == "utun" {
|
||||
fname := os.Getenv("WG_DARWIN_UTUN_NAME_FILE")
|
||||
fname := os.Getenv("WG_TUN_NAME_FILE")
|
||||
if fname != "" {
|
||||
ioutil.WriteFile(fname, []byte(tun.(*NativeTun).name+"\n"), 0400)
|
||||
}
|
||||
@@ -115,90 +167,66 @@ func CreateTUN(name string) (TUNDevice, error) {
|
||||
return tun, err
|
||||
}
|
||||
|
||||
func CreateTUNFromFile(file *os.File) (TUNDevice, error) {
|
||||
|
||||
func CreateTUNFromFile(file *os.File, mtu int) (TUNDevice, error) {
|
||||
tun := &NativeTun{
|
||||
fd: file,
|
||||
mtu: 1500,
|
||||
tunFile: file,
|
||||
events: make(chan TUNEvent, 10),
|
||||
errors: make(chan error, 1),
|
||||
statusListenersShutdown: make(chan struct{}),
|
||||
errors: make(chan error, 5),
|
||||
}
|
||||
|
||||
_, err := tun.Name()
|
||||
name, err := tun.Name()
|
||||
if err != nil {
|
||||
tun.fd.Close()
|
||||
tun.tunFile.Close()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
tun.rwcancel, err = rwcancel.NewRWCancel(int(file.Fd()))
|
||||
tunIfindex, err := func() (int, error) {
|
||||
iface, err := net.InterfaceByName(name)
|
||||
if err != nil {
|
||||
tun.fd.Close()
|
||||
return -1, err
|
||||
}
|
||||
return iface.Index, nil
|
||||
}()
|
||||
if err != nil {
|
||||
tun.tunFile.Close()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// TODO: Fix this very naive implementation
|
||||
go func(tun *NativeTun) {
|
||||
var (
|
||||
statusUp bool
|
||||
statusMTU int
|
||||
)
|
||||
|
||||
for {
|
||||
intr, err := net.InterfaceByName(tun.name)
|
||||
tun.routeSocket, err = unix.Socket(unix.AF_ROUTE, unix.SOCK_RAW, unix.AF_UNSPEC)
|
||||
if err != nil {
|
||||
tun.errors <- err
|
||||
return
|
||||
tun.tunFile.Close()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Up / Down event
|
||||
up := (intr.Flags & net.FlagUp) != 0
|
||||
if up != statusUp && up {
|
||||
tun.events <- TUNEventUp
|
||||
}
|
||||
if up != statusUp && !up {
|
||||
tun.events <- TUNEventDown
|
||||
}
|
||||
statusUp = up
|
||||
go tun.routineRouteListener(tunIfindex)
|
||||
|
||||
// MTU changes
|
||||
if intr.MTU != statusMTU {
|
||||
tun.events <- TUNEventMTUUpdate
|
||||
}
|
||||
statusMTU = intr.MTU
|
||||
|
||||
select {
|
||||
case <-time.After(time.Second / 10):
|
||||
case <-tun.statusListenersShutdown:
|
||||
return
|
||||
}
|
||||
}
|
||||
}(tun)
|
||||
|
||||
// set default MTU
|
||||
err = tun.setMTU(DefaultMTU)
|
||||
if mtu > 0 {
|
||||
err = tun.setMTU(mtu)
|
||||
if err != nil {
|
||||
tun.Close()
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return tun, nil
|
||||
}
|
||||
|
||||
func (tun *NativeTun) Name() (string, error) {
|
||||
|
||||
var ifName struct {
|
||||
name [16]byte
|
||||
}
|
||||
ifNameSize := uintptr(16)
|
||||
|
||||
_, _, errno := unix.Syscall6(
|
||||
var errno syscall.Errno
|
||||
tun.operateOnFd(func(fd uintptr) {
|
||||
_, _, errno = unix.Syscall6(
|
||||
unix.SYS_GETSOCKOPT,
|
||||
uintptr(tun.fd.Fd()),
|
||||
fd,
|
||||
2, /* #define SYSPROTO_CONTROL 2 */
|
||||
2, /* #define UTUN_OPT_IFNAME 2 */
|
||||
uintptr(unsafe.Pointer(&ifName)),
|
||||
uintptr(unsafe.Pointer(&ifNameSize)), 0)
|
||||
})
|
||||
|
||||
if errno != 0 {
|
||||
return "", fmt.Errorf("SYS_GETSOCKOPT: %v", errno)
|
||||
@@ -209,20 +237,20 @@ func (tun *NativeTun) Name() (string, error) {
|
||||
}
|
||||
|
||||
func (tun *NativeTun) File() *os.File {
|
||||
return tun.fd
|
||||
return tun.tunFile
|
||||
}
|
||||
|
||||
func (tun *NativeTun) Events() chan TUNEvent {
|
||||
return tun.events
|
||||
}
|
||||
|
||||
func (tun *NativeTun) doRead(buff []byte, offset int) (int, error) {
|
||||
func (tun *NativeTun) Read(buff []byte, offset int) (int, error) {
|
||||
select {
|
||||
case err := <-tun.errors:
|
||||
return 0, err
|
||||
default:
|
||||
buff := buff[offset-4:]
|
||||
n, err := tun.fd.Read(buff[:])
|
||||
n, err := tun.tunFile.Read(buff[:])
|
||||
if n < 4 {
|
||||
return 0, err
|
||||
}
|
||||
@@ -230,18 +258,6 @@ func (tun *NativeTun) doRead(buff []byte, offset int) (int, error) {
|
||||
}
|
||||
}
|
||||
|
||||
func (tun *NativeTun) Read(buff []byte, offset int) (int, error) {
|
||||
for {
|
||||
n, err := tun.doRead(buff, offset)
|
||||
if err == nil || !rwcancel.ErrorIsEAGAIN(err) {
|
||||
return n, err
|
||||
}
|
||||
if !tun.rwcancel.ReadyRead() {
|
||||
return 0, errors.New("tun device closed")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (tun *NativeTun) Write(buff []byte, offset int) (int, error) {
|
||||
|
||||
// reserve space for header
|
||||
@@ -262,14 +278,24 @@ func (tun *NativeTun) Write(buff []byte, offset int) (int, error) {
|
||||
|
||||
// write
|
||||
|
||||
return tun.fd.Write(buff)
|
||||
return tun.tunFile.Write(buff)
|
||||
}
|
||||
|
||||
func (tun *NativeTun) Flush() error {
|
||||
//TODO: can flushing be implemented by buffering and using sendmmsg?
|
||||
return nil
|
||||
}
|
||||
|
||||
func (tun *NativeTun) Close() error {
|
||||
close(tun.statusListenersShutdown)
|
||||
err1 := tun.rwcancel.Cancel()
|
||||
err2 := tun.fd.Close()
|
||||
var err2 error
|
||||
err1 := tun.tunFile.Close()
|
||||
if tun.routeSocket != -1 {
|
||||
unix.Shutdown(tun.routeSocket, unix.SHUT_RDWR)
|
||||
err2 = unix.Close(tun.routeSocket)
|
||||
tun.routeSocket = -1
|
||||
} else if tun.events != nil {
|
||||
close(tun.events)
|
||||
}
|
||||
if err1 != nil {
|
||||
return err1
|
||||
}
|
||||
@@ -298,7 +324,7 @@ func (tun *NativeTun) setMTU(n int) error {
|
||||
|
||||
var ifr [32]byte
|
||||
copy(ifr[:], tun.name)
|
||||
binary.LittleEndian.PutUint32(ifr[16:20], uint32(n))
|
||||
*(*uint32)(unsafe.Pointer(&ifr[unix.IFNAMSIZ])) = uint32(n)
|
||||
_, _, errno := unix.Syscall(
|
||||
unix.SYS_IOCTL,
|
||||
uintptr(fd),
|
||||
@@ -307,7 +333,7 @@ func (tun *NativeTun) setMTU(n int) error {
|
||||
)
|
||||
|
||||
if errno != 0 {
|
||||
return fmt.Errorf("Failed to set MTU on %s", tun.name)
|
||||
return fmt.Errorf("failed to set MTU on %s", tun.name)
|
||||
}
|
||||
|
||||
return nil
|
||||
@@ -340,14 +366,8 @@ func (tun *NativeTun) MTU() (int, error) {
|
||||
uintptr(unsafe.Pointer(&ifr[0])),
|
||||
)
|
||||
if errno != 0 {
|
||||
return 0, fmt.Errorf("Failed to get MTU on %s", tun.name)
|
||||
return 0, fmt.Errorf("failed to get MTU on %s", tun.name)
|
||||
}
|
||||
|
||||
// convert result to signed 32-bit int
|
||||
|
||||
val := binary.LittleEndian.Uint32(ifr[16:20])
|
||||
if val >= (1 << 31) {
|
||||
return int(val-(1<<31)) - (1 << 31), nil
|
||||
}
|
||||
return int(val), nil
|
||||
return int(*(*int32)(unsafe.Pointer(&ifr[16]))), nil
|
||||
}
|
||||
501
tun/tun_freebsd.go
Normal file
501
tun/tun_freebsd.go
Normal file
@@ -0,0 +1,501 @@
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package tun
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"golang.org/x/net/ipv6"
|
||||
"golang.org/x/sys/unix"
|
||||
"net"
|
||||
"os"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// _TUNSIFHEAD, value derived from sys/net/{if_tun,ioccom}.h
|
||||
// const _TUNSIFHEAD = ((0x80000000) | (((4) & ((1 << 13) - 1) ) << 16) | (uint32(byte('t')) << 8) | (96))
|
||||
const _TUNSIFHEAD = 0x80047460
|
||||
const _TUNSIFMODE = 0x8004745e
|
||||
const _TUNSIFPID = 0x2000745f
|
||||
|
||||
// Iface status string max len
|
||||
const _IFSTATMAX = 800
|
||||
|
||||
const SIZEOF_UINTPTR = 4 << (^uintptr(0) >> 32 & 1)
|
||||
|
||||
// structure for iface requests with a pointer
|
||||
type ifreq_ptr struct {
|
||||
Name [unix.IFNAMSIZ]byte
|
||||
Data uintptr
|
||||
Pad0 [24 - SIZEOF_UINTPTR]byte
|
||||
}
|
||||
|
||||
// Structure for iface mtu get/set ioctls
|
||||
type ifreq_mtu struct {
|
||||
Name [unix.IFNAMSIZ]byte
|
||||
MTU uint32
|
||||
Pad0 [12]byte
|
||||
}
|
||||
|
||||
// Structure for interface status request ioctl
|
||||
type ifstat struct {
|
||||
IfsName [unix.IFNAMSIZ]byte
|
||||
Ascii [_IFSTATMAX]byte
|
||||
}
|
||||
|
||||
type NativeTun struct {
|
||||
name string
|
||||
tunFile *os.File
|
||||
events chan TUNEvent
|
||||
errors chan error
|
||||
routeSocket int
|
||||
}
|
||||
|
||||
func (tun *NativeTun) routineRouteListener(tunIfindex int) {
|
||||
var (
|
||||
statusUp bool
|
||||
statusMTU int
|
||||
)
|
||||
|
||||
defer close(tun.events)
|
||||
|
||||
data := make([]byte, os.Getpagesize())
|
||||
for {
|
||||
retry:
|
||||
n, err := unix.Read(tun.routeSocket, data)
|
||||
if err != nil {
|
||||
if errno, ok := err.(syscall.Errno); ok && errno == syscall.EINTR {
|
||||
goto retry
|
||||
}
|
||||
tun.errors <- err
|
||||
return
|
||||
}
|
||||
|
||||
if n < 14 {
|
||||
continue
|
||||
}
|
||||
|
||||
if data[3 /* type */] != unix.RTM_IFINFO {
|
||||
continue
|
||||
}
|
||||
ifindex := int(*(*uint16)(unsafe.Pointer(&data[12 /* ifindex */])))
|
||||
if ifindex != tunIfindex {
|
||||
continue
|
||||
}
|
||||
|
||||
iface, err := net.InterfaceByIndex(ifindex)
|
||||
if err != nil {
|
||||
tun.errors <- err
|
||||
return
|
||||
}
|
||||
|
||||
// Up / Down event
|
||||
up := (iface.Flags & net.FlagUp) != 0
|
||||
if up != statusUp && up {
|
||||
tun.events <- TUNEventUp
|
||||
}
|
||||
if up != statusUp && !up {
|
||||
tun.events <- TUNEventDown
|
||||
}
|
||||
statusUp = up
|
||||
|
||||
// MTU changes
|
||||
if iface.MTU != statusMTU {
|
||||
tun.events <- TUNEventMTUUpdate
|
||||
}
|
||||
statusMTU = iface.MTU
|
||||
}
|
||||
}
|
||||
|
||||
func tunName(fd uintptr) (string, error) {
|
||||
//Terrible hack to make up for freebsd not having a TUNGIFNAME
|
||||
|
||||
//First, make sure the tun pid matches this proc's pid
|
||||
_, _, errno := unix.Syscall(
|
||||
unix.SYS_IOCTL,
|
||||
uintptr(fd),
|
||||
uintptr(_TUNSIFPID),
|
||||
uintptr(0),
|
||||
)
|
||||
|
||||
if errno != 0 {
|
||||
return "", fmt.Errorf("failed to set tun device PID: %s", errno.Error())
|
||||
}
|
||||
|
||||
// Open iface control socket
|
||||
|
||||
confd, err := unix.Socket(
|
||||
unix.AF_INET,
|
||||
unix.SOCK_DGRAM,
|
||||
0,
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
defer unix.Close(confd)
|
||||
|
||||
procPid := os.Getpid()
|
||||
|
||||
//Try to find interface with matching PID
|
||||
for i := 1; ; i++ {
|
||||
iface, _ := net.InterfaceByIndex(i)
|
||||
if err != nil || iface == nil {
|
||||
break
|
||||
}
|
||||
|
||||
// Structs for getting data in and out of SIOCGIFSTATUS ioctl
|
||||
var ifstatus ifstat
|
||||
copy(ifstatus.IfsName[:], iface.Name)
|
||||
|
||||
// Make the syscall to get the status string
|
||||
_, _, errno := unix.Syscall(
|
||||
unix.SYS_IOCTL,
|
||||
uintptr(confd),
|
||||
uintptr(unix.SIOCGIFSTATUS),
|
||||
uintptr(unsafe.Pointer(&ifstatus)),
|
||||
)
|
||||
|
||||
if errno != 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
nullStr := ifstatus.Ascii[:]
|
||||
i := bytes.IndexByte(nullStr, 0)
|
||||
if i < 1 {
|
||||
continue
|
||||
}
|
||||
statStr := string(nullStr[:i])
|
||||
var pidNum int = 0
|
||||
|
||||
// Finally get the owning PID
|
||||
// Format string taken from sys/net/if_tun.c
|
||||
_, err := fmt.Sscanf(statStr, "\tOpened by PID %d\n", &pidNum)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
if pidNum == procPid {
|
||||
return iface.Name, nil
|
||||
}
|
||||
}
|
||||
|
||||
return "", nil
|
||||
}
|
||||
|
||||
// Destroy a named system interface
|
||||
func tunDestroy(name string) error {
|
||||
// open control socket
|
||||
var fd int
|
||||
|
||||
fd, err := unix.Socket(
|
||||
unix.AF_INET,
|
||||
unix.SOCK_DGRAM,
|
||||
0,
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
defer unix.Close(fd)
|
||||
|
||||
// do ioctl call
|
||||
|
||||
var ifr [32]byte
|
||||
copy(ifr[:], name)
|
||||
_, _, errno := unix.Syscall(
|
||||
unix.SYS_IOCTL,
|
||||
uintptr(fd),
|
||||
uintptr(unix.SIOCIFDESTROY),
|
||||
uintptr(unsafe.Pointer(&ifr[0])),
|
||||
)
|
||||
|
||||
if errno != 0 {
|
||||
return fmt.Errorf("failed to destroy interface %s: %s", name, errno.Error())
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func CreateTUN(name string, mtu int) (TUNDevice, error) {
|
||||
if len(name) > unix.IFNAMSIZ-1 {
|
||||
return nil, errors.New("interface name too long")
|
||||
}
|
||||
|
||||
// See if interface already exists
|
||||
iface, _ := net.InterfaceByName(name)
|
||||
if iface != nil {
|
||||
return nil, fmt.Errorf("interface %s already exists", name)
|
||||
}
|
||||
|
||||
tunFile, err := os.OpenFile("/dev/tun", unix.O_RDWR, 0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
tun := NativeTun{tunFile: tunFile}
|
||||
var assignedName string
|
||||
tun.operateOnFd(func(fd uintptr) {
|
||||
assignedName, err = tunName(fd)
|
||||
})
|
||||
if err != nil {
|
||||
tunFile.Close()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Enable ifhead mode, otherwise tun will complain if it gets a non-AF_INET packet
|
||||
ifheadmode := 1
|
||||
var errno syscall.Errno
|
||||
tun.operateOnFd(func(fd uintptr) {
|
||||
_, _, errno = unix.Syscall(
|
||||
unix.SYS_IOCTL,
|
||||
fd,
|
||||
uintptr(_TUNSIFHEAD),
|
||||
uintptr(unsafe.Pointer(&ifheadmode)),
|
||||
)
|
||||
})
|
||||
|
||||
if errno != 0 {
|
||||
return nil, fmt.Errorf("error %s", errno.Error())
|
||||
}
|
||||
|
||||
// Rename tun interface
|
||||
|
||||
// Open control socket
|
||||
confd, err := unix.Socket(
|
||||
unix.AF_INET,
|
||||
unix.SOCK_DGRAM,
|
||||
0,
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
defer unix.Close(confd)
|
||||
|
||||
// set up struct for iface rename
|
||||
var newnp [unix.IFNAMSIZ]byte
|
||||
copy(newnp[:], name)
|
||||
|
||||
var ifr ifreq_ptr
|
||||
copy(ifr.Name[:], assignedName)
|
||||
ifr.Data = uintptr(unsafe.Pointer(&newnp[0]))
|
||||
|
||||
//do actual ioctl to rename iface
|
||||
_, _, errno = unix.Syscall(
|
||||
unix.SYS_IOCTL,
|
||||
uintptr(confd),
|
||||
uintptr(unix.SIOCSIFNAME),
|
||||
uintptr(unsafe.Pointer(&ifr)),
|
||||
)
|
||||
if errno != 0 {
|
||||
tunFile.Close()
|
||||
tunDestroy(name)
|
||||
return nil, fmt.Errorf("failed to rename %s to %s: %s", assignedName, name, errno.Error())
|
||||
}
|
||||
|
||||
return CreateTUNFromFile(tunFile, mtu)
|
||||
}
|
||||
|
||||
func CreateTUNFromFile(file *os.File, mtu int) (TUNDevice, error) {
|
||||
|
||||
tun := &NativeTun{
|
||||
tunFile: file,
|
||||
events: make(chan TUNEvent, 10),
|
||||
errors: make(chan error, 1),
|
||||
}
|
||||
|
||||
name, err := tun.Name()
|
||||
if err != nil {
|
||||
tun.tunFile.Close()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
tunIfindex, err := func() (int, error) {
|
||||
iface, err := net.InterfaceByName(name)
|
||||
if err != nil {
|
||||
return -1, err
|
||||
}
|
||||
return iface.Index, nil
|
||||
}()
|
||||
if err != nil {
|
||||
tun.tunFile.Close()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
tun.routeSocket, err = unix.Socket(unix.AF_ROUTE, unix.SOCK_RAW, unix.AF_UNSPEC)
|
||||
if err != nil {
|
||||
tun.tunFile.Close()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
go tun.routineRouteListener(tunIfindex)
|
||||
|
||||
err = tun.setMTU(mtu)
|
||||
if err != nil {
|
||||
tun.Close()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return tun, nil
|
||||
}
|
||||
|
||||
func (tun *NativeTun) Name() (string, error) {
|
||||
var name string
|
||||
var err error
|
||||
tun.operateOnFd(func(fd uintptr) {
|
||||
name, err = tunName(fd)
|
||||
})
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
tun.name = name
|
||||
return name, nil
|
||||
}
|
||||
|
||||
func (tun *NativeTun) File() *os.File {
|
||||
return tun.tunFile
|
||||
}
|
||||
|
||||
func (tun *NativeTun) Events() chan TUNEvent {
|
||||
return tun.events
|
||||
}
|
||||
|
||||
func (tun *NativeTun) Read(buff []byte, offset int) (int, error) {
|
||||
select {
|
||||
case err := <-tun.errors:
|
||||
return 0, err
|
||||
default:
|
||||
buff := buff[offset-4:]
|
||||
n, err := tun.tunFile.Read(buff[:])
|
||||
if n < 4 {
|
||||
return 0, err
|
||||
}
|
||||
return n - 4, err
|
||||
}
|
||||
}
|
||||
|
||||
func (tun *NativeTun) Write(buff []byte, offset int) (int, error) {
|
||||
|
||||
// reserve space for header
|
||||
|
||||
buff = buff[offset-4:]
|
||||
|
||||
// add packet information header
|
||||
|
||||
buff[0] = 0x00
|
||||
buff[1] = 0x00
|
||||
buff[2] = 0x00
|
||||
|
||||
if buff[4]>>4 == ipv6.Version {
|
||||
buff[3] = unix.AF_INET6
|
||||
} else {
|
||||
buff[3] = unix.AF_INET
|
||||
}
|
||||
|
||||
// write
|
||||
|
||||
return tun.tunFile.Write(buff)
|
||||
}
|
||||
|
||||
func (tun *NativeTun) Flush() error {
|
||||
//TODO: can flushing be implemented by buffering and using sendmmsg?
|
||||
return nil
|
||||
}
|
||||
|
||||
func (tun *NativeTun) Close() error {
|
||||
var err3 error
|
||||
err1 := tun.tunFile.Close()
|
||||
err2 := tunDestroy(tun.name)
|
||||
if tun.routeSocket != -1 {
|
||||
unix.Shutdown(tun.routeSocket, unix.SHUT_RDWR)
|
||||
err3 = unix.Close(tun.routeSocket)
|
||||
tun.routeSocket = -1
|
||||
} else if tun.events != nil {
|
||||
close(tun.events)
|
||||
}
|
||||
if err1 != nil {
|
||||
return err1
|
||||
}
|
||||
if err2 != nil {
|
||||
return err2
|
||||
}
|
||||
return err3
|
||||
}
|
||||
|
||||
func (tun *NativeTun) setMTU(n int) error {
|
||||
// open datagram socket
|
||||
|
||||
var fd int
|
||||
|
||||
fd, err := unix.Socket(
|
||||
unix.AF_INET,
|
||||
unix.SOCK_DGRAM,
|
||||
0,
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
defer unix.Close(fd)
|
||||
|
||||
// do ioctl call
|
||||
|
||||
var ifr ifreq_mtu
|
||||
copy(ifr.Name[:], tun.name)
|
||||
ifr.MTU = uint32(n)
|
||||
|
||||
_, _, errno := unix.Syscall(
|
||||
unix.SYS_IOCTL,
|
||||
uintptr(fd),
|
||||
uintptr(unix.SIOCSIFMTU),
|
||||
uintptr(unsafe.Pointer(&ifr)),
|
||||
)
|
||||
|
||||
if errno != 0 {
|
||||
return fmt.Errorf("failed to set MTU on %s", tun.name)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (tun *NativeTun) MTU() (int, error) {
|
||||
// open datagram socket
|
||||
|
||||
fd, err := unix.Socket(
|
||||
unix.AF_INET,
|
||||
unix.SOCK_DGRAM,
|
||||
0,
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
defer unix.Close(fd)
|
||||
|
||||
// do ioctl call
|
||||
var ifr ifreq_mtu
|
||||
copy(ifr.Name[:], tun.name)
|
||||
|
||||
_, _, errno := unix.Syscall(
|
||||
unix.SYS_IOCTL,
|
||||
uintptr(fd),
|
||||
uintptr(unix.SIOCGIFMTU),
|
||||
uintptr(unsafe.Pointer(&ifr)),
|
||||
)
|
||||
if errno != 0 {
|
||||
return 0, fmt.Errorf("failed to get MTU on %s", tun.name)
|
||||
}
|
||||
|
||||
return int(*(*int32)(unsafe.Pointer(&ifr.MTU))), nil
|
||||
}
|
||||
@@ -1,27 +1,24 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2018 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
|
||||
* Copyright (C) 2017-2018 Mathias N. Hall-Andersen <mathias@hall-andersen.dk>.
|
||||
* Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
/* Copyright 2018 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved. */
|
||||
|
||||
package main
|
||||
package tun
|
||||
|
||||
/* Implementation of the TUN device interface for linux
|
||||
*/
|
||||
|
||||
import (
|
||||
"./rwcancel"
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
"golang.org/x/net/ipv6"
|
||||
"golang.org/x/sys/unix"
|
||||
"golang.zx2c4.com/wireguard/rwcancel"
|
||||
"net"
|
||||
"os"
|
||||
"strconv"
|
||||
"sync"
|
||||
"syscall"
|
||||
"time"
|
||||
"unsafe"
|
||||
)
|
||||
@@ -32,8 +29,7 @@ const (
|
||||
)
|
||||
|
||||
type NativeTun struct {
|
||||
fd *os.File
|
||||
fdCancel *rwcancel.RWCancel
|
||||
tunFile *os.File
|
||||
index int32 // if index
|
||||
name string // name of interface
|
||||
errors chan error // async error handling
|
||||
@@ -41,21 +37,30 @@ type NativeTun struct {
|
||||
nopi bool // the device was pased IFF_NO_PI
|
||||
netlinkSock int
|
||||
netlinkCancel *rwcancel.RWCancel
|
||||
|
||||
hackListenerClosed sync.Mutex
|
||||
statusListenersShutdown chan struct{}
|
||||
}
|
||||
|
||||
func (tun *NativeTun) File() *os.File {
|
||||
return tun.fd
|
||||
return tun.tunFile
|
||||
}
|
||||
|
||||
func (tun *NativeTun) RoutineHackListener() {
|
||||
func (tun *NativeTun) routineHackListener() {
|
||||
defer tun.hackListenerClosed.Unlock()
|
||||
/* This is needed for the detection to work across network namespaces
|
||||
* If you are reading this and know a better method, please get in touch.
|
||||
*/
|
||||
fd := int(tun.fd.Fd())
|
||||
for {
|
||||
_, err := unix.Write(fd, nil)
|
||||
sysconn, err := tun.tunFile.SyscallConn()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err2 := sysconn.Control(func(fd uintptr) {
|
||||
_, err = unix.Write(int(fd), nil)
|
||||
})
|
||||
if err2 != nil {
|
||||
return
|
||||
}
|
||||
switch err {
|
||||
case unix.EINVAL:
|
||||
tun.events <- TUNEventUp
|
||||
@@ -65,7 +70,7 @@ func (tun *NativeTun) RoutineHackListener() {
|
||||
return
|
||||
}
|
||||
select {
|
||||
case <-time.After(time.Second / 10):
|
||||
case <-time.After(time.Second):
|
||||
case <-tun.statusListenersShutdown:
|
||||
return
|
||||
}
|
||||
@@ -88,8 +93,12 @@ func createNetlinkSocket() (int, error) {
|
||||
return sock, nil
|
||||
}
|
||||
|
||||
func (tun *NativeTun) RoutineNetlinkListener() {
|
||||
defer unix.Close(tun.netlinkSock)
|
||||
func (tun *NativeTun) routineNetlinkListener() {
|
||||
defer func() {
|
||||
unix.Close(tun.netlinkSock)
|
||||
tun.hackListenerClosed.Lock()
|
||||
close(tun.events)
|
||||
}()
|
||||
|
||||
for msg := make([]byte, 1<<16); ; {
|
||||
|
||||
@@ -97,7 +106,7 @@ func (tun *NativeTun) RoutineNetlinkListener() {
|
||||
var msgn int
|
||||
for {
|
||||
msgn, _, _, _, err = unix.Recvmsg(tun.netlinkSock, msg[:], nil, 0)
|
||||
if err == nil || !rwcancel.ErrorIsEAGAIN(err) {
|
||||
if err == nil || !rwcancel.RetryAfterError(err) {
|
||||
break
|
||||
}
|
||||
if !tun.netlinkCancel.ReadyRead() {
|
||||
@@ -159,16 +168,12 @@ func (tun *NativeTun) isUp() (bool, error) {
|
||||
return inter.Flags&net.FlagUp != 0, err
|
||||
}
|
||||
|
||||
func getDummySock() (int, error) {
|
||||
return unix.Socket(
|
||||
func getIFIndex(name string) (int32, error) {
|
||||
fd, err := unix.Socket(
|
||||
unix.AF_INET,
|
||||
unix.SOCK_DGRAM,
|
||||
0,
|
||||
)
|
||||
}
|
||||
|
||||
func getIFIndex(name string) (int32, error) {
|
||||
fd, err := getDummySock()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
@@ -188,14 +193,11 @@ func getIFIndex(name string) (int32, error) {
|
||||
return 0, errno
|
||||
}
|
||||
|
||||
index := binary.LittleEndian.Uint32(ifr[unix.IFNAMSIZ:])
|
||||
return toInt32(index), nil
|
||||
return *(*int32)(unsafe.Pointer(&ifr[unix.IFNAMSIZ])), nil
|
||||
}
|
||||
|
||||
func (tun *NativeTun) setMTU(n int) error {
|
||||
|
||||
// open datagram socket
|
||||
|
||||
fd, err := unix.Socket(
|
||||
unix.AF_INET,
|
||||
unix.SOCK_DGRAM,
|
||||
@@ -212,7 +214,7 @@ func (tun *NativeTun) setMTU(n int) error {
|
||||
|
||||
var ifr [ifReqSize]byte
|
||||
copy(ifr[:], tun.name)
|
||||
binary.LittleEndian.PutUint32(ifr[16:20], uint32(n))
|
||||
*(*uint32)(unsafe.Pointer(&ifr[unix.IFNAMSIZ])) = uint32(n)
|
||||
_, _, errno := unix.Syscall(
|
||||
unix.SYS_IOCTL,
|
||||
uintptr(fd),
|
||||
@@ -221,16 +223,14 @@ func (tun *NativeTun) setMTU(n int) error {
|
||||
)
|
||||
|
||||
if errno != 0 {
|
||||
return errors.New("Failed to set MTU of TUN device")
|
||||
return errors.New("failed to set MTU of TUN device")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (tun *NativeTun) MTU() (int, error) {
|
||||
|
||||
// open datagram socket
|
||||
|
||||
fd, err := unix.Socket(
|
||||
unix.AF_INET,
|
||||
unix.SOCK_DGRAM,
|
||||
@@ -254,29 +254,32 @@ func (tun *NativeTun) MTU() (int, error) {
|
||||
uintptr(unsafe.Pointer(&ifr[0])),
|
||||
)
|
||||
if errno != 0 {
|
||||
return 0, errors.New("Failed to get MTU of TUN device: " + strconv.FormatInt(int64(errno), 10))
|
||||
return 0, errors.New("failed to get MTU of TUN device: " + errno.Error())
|
||||
}
|
||||
|
||||
// convert result to signed 32-bit int
|
||||
|
||||
val := binary.LittleEndian.Uint32(ifr[16:20])
|
||||
if val >= (1 << 31) {
|
||||
return int(toInt32(val)), nil
|
||||
}
|
||||
return int(val), nil
|
||||
return int(*(*int32)(unsafe.Pointer(&ifr[unix.IFNAMSIZ]))), nil
|
||||
}
|
||||
|
||||
func (tun *NativeTun) Name() (string, error) {
|
||||
|
||||
sysconn, err := tun.tunFile.SyscallConn()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
var ifr [ifReqSize]byte
|
||||
_, _, errno := unix.Syscall(
|
||||
var errno syscall.Errno
|
||||
err = sysconn.Control(func(fd uintptr) {
|
||||
_, _, errno = unix.Syscall(
|
||||
unix.SYS_IOCTL,
|
||||
tun.fd.Fd(),
|
||||
fd,
|
||||
uintptr(unix.TUNGETIFF),
|
||||
uintptr(unsafe.Pointer(&ifr[0])),
|
||||
)
|
||||
})
|
||||
if err != nil {
|
||||
return "", errors.New("failed to get name of TUN device: " + err.Error())
|
||||
}
|
||||
if errno != 0 {
|
||||
return "", errors.New("Failed to get name of TUN device: " + strconv.FormatInt(int64(errno), 10))
|
||||
return "", errors.New("failed to get name of TUN device: " + errno.Error())
|
||||
}
|
||||
nullStr := ifr[:]
|
||||
i := bytes.IndexByte(nullStr, 0)
|
||||
@@ -312,19 +315,24 @@ func (tun *NativeTun) Write(buff []byte, offset int) (int, error) {
|
||||
|
||||
// write
|
||||
|
||||
return tun.fd.Write(buff)
|
||||
return tun.tunFile.Write(buff)
|
||||
}
|
||||
|
||||
func (tun *NativeTun) doRead(buff []byte, offset int) (int, error) {
|
||||
func (tun *NativeTun) Flush() error {
|
||||
//TODO: can flushing be implemented by buffering and using sendmmsg?
|
||||
return nil
|
||||
}
|
||||
|
||||
func (tun *NativeTun) Read(buff []byte, offset int) (int, error) {
|
||||
select {
|
||||
case err := <-tun.errors:
|
||||
return 0, err
|
||||
default:
|
||||
if tun.nopi {
|
||||
return tun.fd.Read(buff[offset:])
|
||||
return tun.tunFile.Read(buff[offset:])
|
||||
} else {
|
||||
buff := buff[offset-4:]
|
||||
n, err := tun.fd.Read(buff[:])
|
||||
n, err := tun.tunFile.Read(buff[:])
|
||||
if n < 4 {
|
||||
return 0, err
|
||||
}
|
||||
@@ -333,64 +341,34 @@ func (tun *NativeTun) doRead(buff []byte, offset int) (int, error) {
|
||||
}
|
||||
}
|
||||
|
||||
func (tun *NativeTun) Read(buff []byte, offset int) (int, error) {
|
||||
for {
|
||||
n, err := tun.doRead(buff, offset)
|
||||
if err == nil || !rwcancel.ErrorIsEAGAIN(err) {
|
||||
return n, err
|
||||
}
|
||||
if !tun.fdCancel.ReadyRead() {
|
||||
return 0, errors.New("tun device closed")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (tun *NativeTun) Events() chan TUNEvent {
|
||||
return tun.events
|
||||
}
|
||||
|
||||
func (tun *NativeTun) Close() error {
|
||||
var err1 error
|
||||
if tun.statusListenersShutdown != nil {
|
||||
close(tun.statusListenersShutdown)
|
||||
if tun.netlinkCancel != nil {
|
||||
err1 = tun.netlinkCancel.Cancel()
|
||||
}
|
||||
err2 := tun.fd.Close()
|
||||
err3 := tun.fdCancel.Cancel()
|
||||
} else if tun.events != nil {
|
||||
close(tun.events)
|
||||
}
|
||||
err2 := tun.tunFile.Close()
|
||||
|
||||
if err1 != nil {
|
||||
return err1
|
||||
}
|
||||
if err2 != nil {
|
||||
return err2
|
||||
}
|
||||
return err3
|
||||
}
|
||||
|
||||
func CreateTUN(name string) (TUNDevice, error) {
|
||||
|
||||
// open clone device
|
||||
|
||||
// HACK: we open it as a raw Fd first, so that f.nonblock=false
|
||||
// when we make it into a file object.
|
||||
func CreateTUN(name string, mtu int) (TUNDevice, error) {
|
||||
nfd, err := unix.Open(cloneDevicePath, os.O_RDWR, 0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = unix.SetNonblock(nfd, true)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
fd := os.NewFile(uintptr(nfd), cloneDevicePath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// create new device
|
||||
|
||||
var ifr [ifReqSize]byte
|
||||
var flags uint16 = unix.IFF_TUN // | unix.IFF_NO_PI (disabled for TUN status hack)
|
||||
nameBytes := []byte(name)
|
||||
@@ -398,24 +376,32 @@ func CreateTUN(name string) (TUNDevice, error) {
|
||||
return nil, errors.New("interface name too long")
|
||||
}
|
||||
copy(ifr[:], nameBytes)
|
||||
binary.LittleEndian.PutUint16(ifr[16:], flags)
|
||||
*(*uint16)(unsafe.Pointer(&ifr[unix.IFNAMSIZ])) = flags
|
||||
|
||||
_, _, errno := unix.Syscall(
|
||||
unix.SYS_IOCTL,
|
||||
fd.Fd(),
|
||||
uintptr(nfd),
|
||||
uintptr(unix.TUNSETIFF),
|
||||
uintptr(unsafe.Pointer(&ifr[0])),
|
||||
)
|
||||
if errno != 0 {
|
||||
return nil, errno
|
||||
}
|
||||
err = unix.SetNonblock(nfd, true)
|
||||
|
||||
return CreateTUNFromFile(fd)
|
||||
// Note that the above -- open,ioctl,nonblock -- must happen prior to handing it to netpoll as below this line.
|
||||
|
||||
fd := os.NewFile(uintptr(nfd), cloneDevicePath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return CreateTUNFromFile(fd, mtu)
|
||||
}
|
||||
|
||||
func CreateTUNFromFile(fd *os.File) (TUNDevice, error) {
|
||||
func CreateTUNFromFile(file *os.File, mtu int) (TUNDevice, error) {
|
||||
tun := &NativeTun{
|
||||
fd: fd,
|
||||
tunFile: file,
|
||||
events: make(chan TUNEvent, 5),
|
||||
errors: make(chan error, 5),
|
||||
statusListenersShutdown: make(chan struct{}),
|
||||
@@ -423,15 +409,8 @@ func CreateTUNFromFile(fd *os.File) (TUNDevice, error) {
|
||||
}
|
||||
var err error
|
||||
|
||||
tun.fdCancel, err = rwcancel.NewRWCancel(int(fd.Fd()))
|
||||
if err != nil {
|
||||
tun.fd.Close()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
_, err = tun.Name()
|
||||
if err != nil {
|
||||
tun.fd.Close()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -444,25 +423,42 @@ func CreateTUNFromFile(fd *os.File) (TUNDevice, error) {
|
||||
|
||||
tun.netlinkSock, err = createNetlinkSocket()
|
||||
if err != nil {
|
||||
tun.fd.Close()
|
||||
return nil, err
|
||||
}
|
||||
tun.netlinkCancel, err = rwcancel.NewRWCancel(tun.netlinkSock)
|
||||
if err != nil {
|
||||
tun.fd.Close()
|
||||
unix.Close(tun.netlinkSock)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
go tun.RoutineNetlinkListener()
|
||||
go tun.RoutineHackListener() // cross namespace
|
||||
tun.hackListenerClosed.Lock()
|
||||
go tun.routineNetlinkListener()
|
||||
go tun.routineHackListener() // cross namespace
|
||||
|
||||
// set default MTU
|
||||
|
||||
err = tun.setMTU(DefaultMTU)
|
||||
err = tun.setMTU(mtu)
|
||||
if err != nil {
|
||||
tun.Close()
|
||||
unix.Close(tun.netlinkSock)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return tun, nil
|
||||
}
|
||||
|
||||
func CreateUnmonitoredTUNFromFD(fd int) (TUNDevice, string, error) {
|
||||
err := unix.SetNonblock(fd, true)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
file := os.NewFile(uintptr(fd), "/dev/tun")
|
||||
tun := &NativeTun{
|
||||
tunFile: file,
|
||||
events: make(chan TUNEvent, 5),
|
||||
errors: make(chan error, 5),
|
||||
nopi: true,
|
||||
}
|
||||
name, err := tun.Name()
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
return tun, name, nil
|
||||
}
|
||||
328
tun/tun_openbsd.go
Normal file
328
tun/tun_openbsd.go
Normal file
@@ -0,0 +1,328 @@
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package tun
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"golang.org/x/net/ipv6"
|
||||
"golang.org/x/sys/unix"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"os"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// Structure for iface mtu get/set ioctls
|
||||
type ifreq_mtu struct {
|
||||
Name [unix.IFNAMSIZ]byte
|
||||
MTU uint32
|
||||
Pad0 [12]byte
|
||||
}
|
||||
|
||||
const _TUNSIFMODE = 0x8004745d
|
||||
|
||||
type NativeTun struct {
|
||||
name string
|
||||
tunFile *os.File
|
||||
events chan TUNEvent
|
||||
errors chan error
|
||||
routeSocket int
|
||||
}
|
||||
|
||||
func (tun *NativeTun) routineRouteListener(tunIfindex int) {
|
||||
var (
|
||||
statusUp bool
|
||||
statusMTU int
|
||||
)
|
||||
|
||||
defer close(tun.events)
|
||||
|
||||
data := make([]byte, os.Getpagesize())
|
||||
for {
|
||||
retry:
|
||||
n, err := unix.Read(tun.routeSocket, data)
|
||||
if err != nil {
|
||||
if errno, ok := err.(syscall.Errno); ok && errno == syscall.EINTR {
|
||||
goto retry
|
||||
}
|
||||
tun.errors <- err
|
||||
return
|
||||
}
|
||||
|
||||
if n < 8 {
|
||||
continue
|
||||
}
|
||||
|
||||
if data[3 /* type */] != unix.RTM_IFINFO {
|
||||
continue
|
||||
}
|
||||
ifindex := int(*(*uint16)(unsafe.Pointer(&data[6 /* ifindex */])))
|
||||
if ifindex != tunIfindex {
|
||||
continue
|
||||
}
|
||||
|
||||
iface, err := net.InterfaceByIndex(ifindex)
|
||||
if err != nil {
|
||||
tun.errors <- err
|
||||
return
|
||||
}
|
||||
|
||||
// Up / Down event
|
||||
up := (iface.Flags & net.FlagUp) != 0
|
||||
if up != statusUp && up {
|
||||
tun.events <- TUNEventUp
|
||||
}
|
||||
if up != statusUp && !up {
|
||||
tun.events <- TUNEventDown
|
||||
}
|
||||
statusUp = up
|
||||
|
||||
// MTU changes
|
||||
if iface.MTU != statusMTU {
|
||||
tun.events <- TUNEventMTUUpdate
|
||||
}
|
||||
statusMTU = iface.MTU
|
||||
}
|
||||
}
|
||||
|
||||
func errorIsEBUSY(err error) bool {
|
||||
if pe, ok := err.(*os.PathError); ok {
|
||||
err = pe.Err
|
||||
}
|
||||
if errno, ok := err.(syscall.Errno); ok && errno == syscall.EBUSY {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func CreateTUN(name string, mtu int) (TUNDevice, error) {
|
||||
ifIndex := -1
|
||||
if name != "tun" {
|
||||
_, err := fmt.Sscanf(name, "tun%d", &ifIndex)
|
||||
if err != nil || ifIndex < 0 {
|
||||
return nil, fmt.Errorf("Interface name must be tun[0-9]*")
|
||||
}
|
||||
}
|
||||
|
||||
var tunfile *os.File
|
||||
var err error
|
||||
|
||||
if ifIndex != -1 {
|
||||
tunfile, err = os.OpenFile(fmt.Sprintf("/dev/tun%d", ifIndex), unix.O_RDWR, 0)
|
||||
} else {
|
||||
for ifIndex = 0; ifIndex < 256; ifIndex += 1 {
|
||||
tunfile, err = os.OpenFile(fmt.Sprintf("/dev/tun%d", ifIndex), unix.O_RDWR, 0)
|
||||
if err == nil || !errorIsEBUSY(err) {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
tun, err := CreateTUNFromFile(tunfile, mtu)
|
||||
|
||||
if err == nil && name == "tun" {
|
||||
fname := os.Getenv("WG_TUN_NAME_FILE")
|
||||
if fname != "" {
|
||||
ioutil.WriteFile(fname, []byte(tun.(*NativeTun).name+"\n"), 0400)
|
||||
}
|
||||
}
|
||||
|
||||
return tun, err
|
||||
}
|
||||
|
||||
func CreateTUNFromFile(file *os.File, mtu int) (TUNDevice, error) {
|
||||
|
||||
tun := &NativeTun{
|
||||
tunFile: file,
|
||||
events: make(chan TUNEvent, 10),
|
||||
errors: make(chan error, 1),
|
||||
}
|
||||
|
||||
name, err := tun.Name()
|
||||
if err != nil {
|
||||
tun.tunFile.Close()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
tunIfindex, err := func() (int, error) {
|
||||
iface, err := net.InterfaceByName(name)
|
||||
if err != nil {
|
||||
return -1, err
|
||||
}
|
||||
return iface.Index, nil
|
||||
}()
|
||||
if err != nil {
|
||||
tun.tunFile.Close()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
tun.routeSocket, err = unix.Socket(unix.AF_ROUTE, unix.SOCK_RAW, unix.AF_UNSPEC)
|
||||
if err != nil {
|
||||
tun.tunFile.Close()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
go tun.routineRouteListener(tunIfindex)
|
||||
|
||||
err = tun.setMTU(mtu)
|
||||
if err != nil {
|
||||
tun.Close()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return tun, nil
|
||||
}
|
||||
|
||||
func (tun *NativeTun) Name() (string, error) {
|
||||
gostat, err := tun.tunFile.Stat()
|
||||
if err != nil {
|
||||
tun.name = ""
|
||||
return "", err
|
||||
}
|
||||
stat := gostat.Sys().(*syscall.Stat_t)
|
||||
tun.name = fmt.Sprintf("tun%d", stat.Rdev%256)
|
||||
return tun.name, nil
|
||||
}
|
||||
|
||||
func (tun *NativeTun) File() *os.File {
|
||||
return tun.tunFile
|
||||
}
|
||||
|
||||
func (tun *NativeTun) Events() chan TUNEvent {
|
||||
return tun.events
|
||||
}
|
||||
|
||||
func (tun *NativeTun) Read(buff []byte, offset int) (int, error) {
|
||||
select {
|
||||
case err := <-tun.errors:
|
||||
return 0, err
|
||||
default:
|
||||
buff := buff[offset-4:]
|
||||
n, err := tun.tunFile.Read(buff[:])
|
||||
if n < 4 {
|
||||
return 0, err
|
||||
}
|
||||
return n - 4, err
|
||||
}
|
||||
}
|
||||
|
||||
func (tun *NativeTun) Write(buff []byte, offset int) (int, error) {
|
||||
|
||||
// reserve space for header
|
||||
|
||||
buff = buff[offset-4:]
|
||||
|
||||
// add packet information header
|
||||
|
||||
buff[0] = 0x00
|
||||
buff[1] = 0x00
|
||||
buff[2] = 0x00
|
||||
|
||||
if buff[4]>>4 == ipv6.Version {
|
||||
buff[3] = unix.AF_INET6
|
||||
} else {
|
||||
buff[3] = unix.AF_INET
|
||||
}
|
||||
|
||||
// write
|
||||
|
||||
return tun.tunFile.Write(buff)
|
||||
}
|
||||
|
||||
func (tun *NativeTun) Flush() error {
|
||||
//TODO: can flushing be implemented by buffering and using sendmmsg?
|
||||
return nil
|
||||
}
|
||||
|
||||
func (tun *NativeTun) Close() error {
|
||||
var err2 error
|
||||
err1 := tun.tunFile.Close()
|
||||
if tun.routeSocket != -1 {
|
||||
unix.Shutdown(tun.routeSocket, unix.SHUT_RDWR)
|
||||
err2 = unix.Close(tun.routeSocket)
|
||||
tun.routeSocket = -1
|
||||
} else if tun.events != nil {
|
||||
close(tun.events)
|
||||
}
|
||||
if err1 != nil {
|
||||
return err1
|
||||
}
|
||||
return err2
|
||||
}
|
||||
|
||||
func (tun *NativeTun) setMTU(n int) error {
|
||||
// open datagram socket
|
||||
|
||||
var fd int
|
||||
|
||||
fd, err := unix.Socket(
|
||||
unix.AF_INET,
|
||||
unix.SOCK_DGRAM,
|
||||
0,
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
defer unix.Close(fd)
|
||||
|
||||
// do ioctl call
|
||||
|
||||
var ifr ifreq_mtu
|
||||
copy(ifr.Name[:], tun.name)
|
||||
ifr.MTU = uint32(n)
|
||||
|
||||
_, _, errno := unix.Syscall(
|
||||
unix.SYS_IOCTL,
|
||||
uintptr(fd),
|
||||
uintptr(unix.SIOCSIFMTU),
|
||||
uintptr(unsafe.Pointer(&ifr)),
|
||||
)
|
||||
|
||||
if errno != 0 {
|
||||
return fmt.Errorf("failed to set MTU on %s", tun.name)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (tun *NativeTun) MTU() (int, error) {
|
||||
// open datagram socket
|
||||
|
||||
fd, err := unix.Socket(
|
||||
unix.AF_INET,
|
||||
unix.SOCK_DGRAM,
|
||||
0,
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
defer unix.Close(fd)
|
||||
|
||||
// do ioctl call
|
||||
var ifr ifreq_mtu
|
||||
copy(ifr.Name[:], tun.name)
|
||||
|
||||
_, _, errno := unix.Syscall(
|
||||
unix.SYS_IOCTL,
|
||||
uintptr(fd),
|
||||
uintptr(unix.SIOCGIFMTU),
|
||||
uintptr(unsafe.Pointer(&ifr)),
|
||||
)
|
||||
if errno != 0 {
|
||||
return 0, fmt.Errorf("failed to get MTU on %s", tun.name)
|
||||
}
|
||||
|
||||
return int(*(*int32)(unsafe.Pointer(&ifr.MTU))), nil
|
||||
}
|
||||
373
tun/tun_windows.go
Normal file
373
tun/tun_windows.go
Normal file
@@ -0,0 +1,373 @@
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2018-2019 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package tun
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"os"
|
||||
"sync"
|
||||
"syscall"
|
||||
"time"
|
||||
"unsafe"
|
||||
|
||||
"golang.org/x/sys/windows"
|
||||
"golang.zx2c4.com/wireguard/tun/wintun"
|
||||
)
|
||||
|
||||
const (
|
||||
packetExchangeMax uint32 = 256 // Number of packets that may be written at a time
|
||||
packetExchangeAlignment uint32 = 16 // Number of bytes packets are aligned to in exchange buffers
|
||||
packetSizeMax uint32 = 0xf000 - packetExchangeAlignment // Maximum packet size
|
||||
packetExchangeSize uint32 = 0x100000 // Exchange buffer size (defaults to 1MiB)
|
||||
retryRate = 4 // Number of retries per second to reopen device pipe
|
||||
retryTimeout = 30 // Number of seconds to tolerate adapter unavailable
|
||||
)
|
||||
|
||||
type exchgBufRead struct {
|
||||
data [packetExchangeSize]byte
|
||||
offset uint32
|
||||
avail uint32
|
||||
}
|
||||
|
||||
type exchgBufWrite struct {
|
||||
data [packetExchangeSize]byte
|
||||
offset uint32
|
||||
packetNum uint32
|
||||
}
|
||||
|
||||
type NativeTun struct {
|
||||
wt *wintun.Wintun
|
||||
tunFileRead *os.File
|
||||
tunFileWrite *os.File
|
||||
tunLock sync.Mutex
|
||||
close bool
|
||||
rdBuff *exchgBufRead
|
||||
wrBuff *exchgBufWrite
|
||||
events chan TUNEvent
|
||||
errors chan error
|
||||
forcedMtu int
|
||||
}
|
||||
|
||||
func packetAlign(size uint32) uint32 {
|
||||
return (size + (packetExchangeAlignment - 1)) &^ (packetExchangeAlignment - 1)
|
||||
}
|
||||
|
||||
//
|
||||
// CreateTUN creates a Wintun adapter with the given name. Should a Wintun
|
||||
// adapter with the same name exist, it is reused.
|
||||
//
|
||||
func CreateTUN(ifname string) (TUNDevice, error) {
|
||||
// Does an interface with this name already exist?
|
||||
wt, err := wintun.GetInterface(ifname, 0)
|
||||
if wt == nil {
|
||||
// Interface does not exist or an error occured. Create one.
|
||||
wt, _, err = wintun.CreateInterface("WireGuard Tunnel Adapter", 0)
|
||||
if err != nil {
|
||||
return nil, errors.New("Creating Wintun adapter failed: " + err.Error())
|
||||
}
|
||||
} else if err != nil {
|
||||
// Foreign interface with the same name found.
|
||||
// We could create a Wintun interface under a temporary name. But, should our
|
||||
// proces die without deleting this interface first, the interface would remain
|
||||
// orphaned.
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = wt.SetInterfaceName(ifname)
|
||||
if err != nil {
|
||||
wt.DeleteInterface(0)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = wt.FlushInterface()
|
||||
if err != nil {
|
||||
wt.DeleteInterface(0)
|
||||
return nil, errors.New("Flushing interface failed: " + err.Error())
|
||||
}
|
||||
|
||||
return &NativeTun{
|
||||
wt: wt,
|
||||
rdBuff: &exchgBufRead{},
|
||||
wrBuff: &exchgBufWrite{},
|
||||
events: make(chan TUNEvent, 10),
|
||||
errors: make(chan error, 1),
|
||||
forcedMtu: 1500,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (tun *NativeTun) openTUN() error {
|
||||
retries := retryTimeout * retryRate
|
||||
if tun.close {
|
||||
return os.ErrClosed
|
||||
}
|
||||
|
||||
var err error
|
||||
name := tun.wt.DataFileName()
|
||||
for tun.tunFileRead == nil {
|
||||
tun.tunFileRead, err = os.OpenFile(name, os.O_RDONLY, 0)
|
||||
if err != nil {
|
||||
if retries > 0 && !tun.close {
|
||||
time.Sleep(time.Second / retryRate)
|
||||
retries--
|
||||
continue
|
||||
}
|
||||
return err
|
||||
}
|
||||
}
|
||||
for tun.tunFileWrite == nil {
|
||||
tun.tunFileWrite, err = os.OpenFile(name, os.O_WRONLY, 0)
|
||||
if err != nil {
|
||||
if retries > 0 && !tun.close {
|
||||
time.Sleep(time.Second / retryRate)
|
||||
retries--
|
||||
continue
|
||||
}
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (tun *NativeTun) closeTUN() (err error) {
|
||||
for tun.tunFileRead != nil {
|
||||
tun.tunLock.Lock()
|
||||
if tun.tunFileRead == nil {
|
||||
tun.tunLock.Unlock()
|
||||
break
|
||||
}
|
||||
t := tun.tunFileRead
|
||||
tun.tunFileRead = nil
|
||||
windows.CancelIoEx(windows.Handle(t.Fd()), nil)
|
||||
err = t.Close()
|
||||
tun.tunLock.Unlock()
|
||||
break
|
||||
}
|
||||
for tun.tunFileWrite != nil {
|
||||
tun.tunLock.Lock()
|
||||
if tun.tunFileWrite == nil {
|
||||
tun.tunLock.Unlock()
|
||||
break
|
||||
}
|
||||
t := tun.tunFileWrite
|
||||
tun.tunFileWrite = nil
|
||||
windows.CancelIoEx(windows.Handle(t.Fd()), nil)
|
||||
err2 := t.Close()
|
||||
tun.tunLock.Unlock()
|
||||
if err == nil {
|
||||
err = err2
|
||||
}
|
||||
break
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (tun *NativeTun) getTUN() (read *os.File, write *os.File, err error) {
|
||||
read, write = tun.tunFileRead, tun.tunFileWrite
|
||||
if read == nil || write == nil {
|
||||
read, write = nil, nil
|
||||
tun.tunLock.Lock()
|
||||
if tun.tunFileRead != nil && tun.tunFileWrite != nil {
|
||||
read, write = tun.tunFileRead, tun.tunFileWrite
|
||||
tun.tunLock.Unlock()
|
||||
return
|
||||
}
|
||||
err = tun.closeTUN()
|
||||
if err != nil {
|
||||
tun.tunLock.Unlock()
|
||||
return
|
||||
}
|
||||
err = tun.openTUN()
|
||||
if err == nil {
|
||||
read, write = tun.tunFileRead, tun.tunFileWrite
|
||||
}
|
||||
tun.tunLock.Unlock()
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (tun *NativeTun) Name() (string, error) {
|
||||
return tun.wt.GetInterfaceName()
|
||||
}
|
||||
|
||||
func (tun *NativeTun) File() *os.File {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (tun *NativeTun) Events() chan TUNEvent {
|
||||
return tun.events
|
||||
}
|
||||
|
||||
func (tun *NativeTun) Close() error {
|
||||
tun.close = true
|
||||
err1 := tun.closeTUN()
|
||||
|
||||
if tun.events != nil {
|
||||
close(tun.events)
|
||||
}
|
||||
|
||||
_, _, err2 := tun.wt.DeleteInterface(0)
|
||||
if err1 == nil {
|
||||
err1 = err2
|
||||
}
|
||||
|
||||
return err1
|
||||
}
|
||||
|
||||
func (tun *NativeTun) MTU() (int, error) {
|
||||
return tun.forcedMtu, nil
|
||||
}
|
||||
|
||||
//TODO: This is a temporary hack. We really need to be monitoring the interface in real time and adapting to MTU changes.
|
||||
func (tun *NativeTun) ForceMtu(mtu int) {
|
||||
tun.forcedMtu = mtu
|
||||
}
|
||||
|
||||
func (tun *NativeTun) Read(buff []byte, offset int) (int, error) {
|
||||
select {
|
||||
case err := <-tun.errors:
|
||||
return 0, err
|
||||
default:
|
||||
}
|
||||
|
||||
for {
|
||||
if tun.rdBuff.offset+packetExchangeAlignment <= tun.rdBuff.avail {
|
||||
// Get packet from the exchange buffer.
|
||||
packet := tun.rdBuff.data[tun.rdBuff.offset:]
|
||||
size := *(*uint32)(unsafe.Pointer(&packet[0]))
|
||||
pSize := packetAlign(packetExchangeAlignment + size)
|
||||
if packetSizeMax < size || tun.rdBuff.avail < tun.rdBuff.offset+pSize {
|
||||
// Invalid packet size.
|
||||
tun.rdBuff.avail = 0
|
||||
continue
|
||||
}
|
||||
packet = packet[packetExchangeAlignment : packetExchangeAlignment+size]
|
||||
|
||||
// Copy data.
|
||||
copy(buff[offset:], packet)
|
||||
tun.rdBuff.offset += pSize
|
||||
return int(size), nil
|
||||
}
|
||||
|
||||
// Get TUN data pipe.
|
||||
file, _, err := tun.getTUN()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
// Fill queue.
|
||||
retries := 1000
|
||||
for {
|
||||
n, err := file.Read(tun.rdBuff.data[:])
|
||||
if err != nil {
|
||||
tun.rdBuff.offset = 0
|
||||
tun.rdBuff.avail = 0
|
||||
pe, ok := err.(*os.PathError)
|
||||
if tun.close {
|
||||
return 0, os.ErrClosed
|
||||
}
|
||||
if retries > 0 && ok && pe.Err == windows.ERROR_OPERATION_ABORTED {
|
||||
retries--
|
||||
continue
|
||||
}
|
||||
if ok && pe.Err == syscall.Errno(6) /*windows.ERROR_INVALID_HANDLE*/ {
|
||||
tun.closeTUN()
|
||||
break
|
||||
}
|
||||
return 0, err
|
||||
}
|
||||
tun.rdBuff.offset = 0
|
||||
tun.rdBuff.avail = uint32(n)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Note: flush() and putTunPacket() assume the caller comes only from a single thread; there's no locking.
|
||||
|
||||
func (tun *NativeTun) Flush() error {
|
||||
if tun.wrBuff.offset == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
for {
|
||||
// Get TUN data pipe.
|
||||
_, file, err := tun.getTUN()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Flush write buffer.
|
||||
retries := retryTimeout * retryRate
|
||||
for {
|
||||
_, err = file.Write(tun.wrBuff.data[:tun.wrBuff.offset])
|
||||
tun.wrBuff.packetNum = 0
|
||||
tun.wrBuff.offset = 0
|
||||
if err != nil {
|
||||
pe, ok := err.(*os.PathError)
|
||||
if tun.close {
|
||||
return os.ErrClosed
|
||||
}
|
||||
if retries > 0 && ok && pe.Err == windows.ERROR_OPERATION_ABORTED {
|
||||
time.Sleep(time.Second / retryRate)
|
||||
retries--
|
||||
continue
|
||||
}
|
||||
if ok && pe.Err == syscall.Errno(6) /*windows.ERROR_INVALID_HANDLE*/ {
|
||||
tun.closeTUN()
|
||||
break
|
||||
}
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (tun *NativeTun) putTunPacket(buff []byte) error {
|
||||
size := uint32(len(buff))
|
||||
if size == 0 {
|
||||
return errors.New("Empty packet")
|
||||
}
|
||||
if size > packetSizeMax {
|
||||
return errors.New("Packet too big")
|
||||
}
|
||||
pSize := packetAlign(packetExchangeAlignment + size)
|
||||
|
||||
if tun.wrBuff.packetNum >= packetExchangeMax || tun.wrBuff.offset+pSize >= packetExchangeSize {
|
||||
// Exchange buffer is full -> flush first.
|
||||
err := tun.Flush()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Write packet to the exchange buffer.
|
||||
packet := tun.wrBuff.data[tun.wrBuff.offset : tun.wrBuff.offset+pSize]
|
||||
*(*uint32)(unsafe.Pointer(&packet[0])) = size
|
||||
packet = packet[packetExchangeAlignment : packetExchangeAlignment+size]
|
||||
copy(packet, buff)
|
||||
|
||||
tun.wrBuff.packetNum++
|
||||
tun.wrBuff.offset += pSize
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (tun *NativeTun) Write(buff []byte, offset int) (int, error) {
|
||||
err := tun.putTunPacket(buff[offset:])
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return len(buff) - offset, nil
|
||||
}
|
||||
|
||||
//
|
||||
// GUID returns Windows adapter instance ID.
|
||||
//
|
||||
func (tun *NativeTun) GUID() windows.GUID {
|
||||
return tun.wt.CfgInstanceID
|
||||
}
|
||||
44
tun/wintun/guid/guid_windows.go
Normal file
44
tun/wintun/guid/guid_windows.go
Normal file
@@ -0,0 +1,44 @@
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2019 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package guid
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"syscall"
|
||||
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
//sys clsidFromString(lpsz *uint16, pclsid *windows.GUID) (hr int32) = ole32.CLSIDFromString
|
||||
|
||||
//
|
||||
// FromString parses "{XXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX}" string to GUID.
|
||||
//
|
||||
func FromString(str string) (*windows.GUID, error) {
|
||||
strUTF16, err := syscall.UTF16PtrFromString(str)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
guid := &windows.GUID{}
|
||||
|
||||
hr := clsidFromString(strUTF16, guid)
|
||||
if hr < 0 {
|
||||
return nil, syscall.Errno(hr)
|
||||
}
|
||||
|
||||
return guid, nil
|
||||
}
|
||||
|
||||
//
|
||||
// ToString function converts GUID to string
|
||||
// "{XXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX}".
|
||||
//
|
||||
// The resulting string is uppercase.
|
||||
//
|
||||
func ToString(guid *windows.GUID) string {
|
||||
return fmt.Sprintf("{%06X-%04X-%04X-%04X-%012X}", guid.Data1, guid.Data2, guid.Data3, guid.Data4[:2], guid.Data4[2:])
|
||||
}
|
||||
8
tun/wintun/guid/mksyscall.go
Normal file
8
tun/wintun/guid/mksyscall.go
Normal file
@@ -0,0 +1,8 @@
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2019 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package guid
|
||||
|
||||
//go:generate go run $GOROOT/src/syscall/mksyscall_windows.go -output zguid_windows.go guid_windows.go
|
||||
49
tun/wintun/guid/zguid_windows.go
Normal file
49
tun/wintun/guid/zguid_windows.go
Normal file
@@ -0,0 +1,49 @@
|
||||
// Code generated by 'go generate'; DO NOT EDIT.
|
||||
|
||||
package guid
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
"unsafe"
|
||||
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
var _ unsafe.Pointer
|
||||
|
||||
// Do the interface allocations only once for common
|
||||
// Errno values.
|
||||
const (
|
||||
errnoERROR_IO_PENDING = 997
|
||||
)
|
||||
|
||||
var (
|
||||
errERROR_IO_PENDING error = syscall.Errno(errnoERROR_IO_PENDING)
|
||||
)
|
||||
|
||||
// errnoErr returns common boxed Errno values, to prevent
|
||||
// allocations at runtime.
|
||||
func errnoErr(e syscall.Errno) error {
|
||||
switch e {
|
||||
case 0:
|
||||
return nil
|
||||
case errnoERROR_IO_PENDING:
|
||||
return errERROR_IO_PENDING
|
||||
}
|
||||
// TODO: add more here, after collecting data on the common
|
||||
// error values see on Windows. (perhaps when running
|
||||
// all.bat?)
|
||||
return e
|
||||
}
|
||||
|
||||
var (
|
||||
modole32 = windows.NewLazySystemDLL("ole32.dll")
|
||||
|
||||
procCLSIDFromString = modole32.NewProc("CLSIDFromString")
|
||||
)
|
||||
|
||||
func clsidFromString(lpsz *uint16, pclsid *windows.GUID) (hr int32) {
|
||||
r0, _, _ := syscall.Syscall(procCLSIDFromString.Addr(), 2, uintptr(unsafe.Pointer(lpsz)), uintptr(unsafe.Pointer(pclsid)), 0)
|
||||
hr = int32(r0)
|
||||
return
|
||||
}
|
||||
32
tun/wintun/netshell/netshell_windows.go
Normal file
32
tun/wintun/netshell/netshell_windows.go
Normal file
@@ -0,0 +1,32 @@
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2019 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package netshell
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
"unsafe"
|
||||
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
var (
|
||||
modnetshell = windows.NewLazySystemDLL("netshell.dll")
|
||||
procHrRenameConnection = modnetshell.NewProc("HrRenameConnection")
|
||||
)
|
||||
|
||||
func HrRenameConnection(guid *windows.GUID, newName *uint16) (err error) {
|
||||
err = procHrRenameConnection.Find()
|
||||
if err != nil {
|
||||
// Missing from servercore, so we can't presume it's always there.
|
||||
return
|
||||
}
|
||||
|
||||
ret, _, _ := syscall.Syscall(procHrRenameConnection.Addr(), 2, uintptr(unsafe.Pointer(guid)), uintptr(unsafe.Pointer(newName)), 0)
|
||||
if ret != 0 {
|
||||
err = syscall.Errno(ret)
|
||||
}
|
||||
return
|
||||
}
|
||||
42
tun/wintun/registryhacks_windows.go
Normal file
42
tun/wintun/registryhacks_windows.go
Normal file
@@ -0,0 +1,42 @@
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2019 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package wintun
|
||||
|
||||
import (
|
||||
"golang.org/x/sys/windows/registry"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
numRetries = 25
|
||||
retryTimeout = 100 * time.Millisecond
|
||||
)
|
||||
|
||||
func registryOpenKeyRetry(k registry.Key, path string, access uint32) (key registry.Key, err error) {
|
||||
for i := 0; i < numRetries; i++ {
|
||||
key, err = registry.OpenKey(k, path, access)
|
||||
if err == nil {
|
||||
break
|
||||
}
|
||||
if i != numRetries - 1 {
|
||||
time.Sleep(retryTimeout)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func keyGetStringValueRetry(k registry.Key, name string) (val string, valtype uint32, err error) {
|
||||
for i := 0; i < numRetries; i++ {
|
||||
val, valtype, err = k.GetStringValue(name)
|
||||
if err == nil {
|
||||
break
|
||||
}
|
||||
if i != numRetries - 1 {
|
||||
time.Sleep(retryTimeout)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
8
tun/wintun/setupapi/mksyscall.go
Normal file
8
tun/wintun/setupapi/mksyscall.go
Normal file
@@ -0,0 +1,8 @@
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2019 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package setupapi
|
||||
|
||||
//go:generate go run $GOROOT/src/syscall/mksyscall_windows.go -output zsetupapi_windows.go setupapi_windows.go
|
||||
458
tun/wintun/setupapi/setupapi_windows.go
Normal file
458
tun/wintun/setupapi/setupapi_windows.go
Normal file
@@ -0,0 +1,458 @@
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2019 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package setupapi
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
|
||||
"golang.org/x/sys/windows"
|
||||
"golang.org/x/sys/windows/registry"
|
||||
)
|
||||
|
||||
//sys setupDiCreateDeviceInfoListEx(classGUID *windows.GUID, hwndParent uintptr, machineName *uint16, reserved uintptr) (handle DevInfo, err error) [failretval==DevInfo(windows.InvalidHandle)] = setupapi.SetupDiCreateDeviceInfoListExW
|
||||
|
||||
// SetupDiCreateDeviceInfoListEx function creates an empty device information set on a remote or a local computer and optionally associates the set with a device setup class.
|
||||
func SetupDiCreateDeviceInfoListEx(classGUID *windows.GUID, hwndParent uintptr, machineName string) (deviceInfoSet DevInfo, err error) {
|
||||
var machineNameUTF16 *uint16
|
||||
if machineName != "" {
|
||||
machineNameUTF16, err = syscall.UTF16PtrFromString(machineName)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
return setupDiCreateDeviceInfoListEx(classGUID, hwndParent, machineNameUTF16, 0)
|
||||
}
|
||||
|
||||
//sys setupDiGetDeviceInfoListDetail(deviceInfoSet DevInfo, deviceInfoSetDetailData *DevInfoListDetailData) (err error) = setupapi.SetupDiGetDeviceInfoListDetailW
|
||||
|
||||
// SetupDiGetDeviceInfoListDetail function retrieves information associated with a device information set including the class GUID, remote computer handle, and remote computer name.
|
||||
func SetupDiGetDeviceInfoListDetail(deviceInfoSet DevInfo) (deviceInfoSetDetailData *DevInfoListDetailData, err error) {
|
||||
data := &DevInfoListDetailData{}
|
||||
data.size = uint32(unsafe.Sizeof(*data))
|
||||
|
||||
return data, setupDiGetDeviceInfoListDetail(deviceInfoSet, data)
|
||||
}
|
||||
|
||||
// GetDeviceInfoListDetail method retrieves information associated with a device information set including the class GUID, remote computer handle, and remote computer name.
|
||||
func (deviceInfoSet DevInfo) GetDeviceInfoListDetail() (*DevInfoListDetailData, error) {
|
||||
return SetupDiGetDeviceInfoListDetail(deviceInfoSet)
|
||||
}
|
||||
|
||||
//sys setupDiCreateDeviceInfo(deviceInfoSet DevInfo, DeviceName *uint16, classGUID *windows.GUID, DeviceDescription *uint16, hwndParent uintptr, CreationFlags DICD, deviceInfoData *DevInfoData) (err error) = setupapi.SetupDiCreateDeviceInfoW
|
||||
|
||||
// SetupDiCreateDeviceInfo function creates a new device information element and adds it as a new member to the specified device information set.
|
||||
func SetupDiCreateDeviceInfo(deviceInfoSet DevInfo, deviceName string, classGUID *windows.GUID, deviceDescription string, hwndParent uintptr, creationFlags DICD) (deviceInfoData *DevInfoData, err error) {
|
||||
deviceNameUTF16, err := syscall.UTF16PtrFromString(deviceName)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
var deviceDescriptionUTF16 *uint16
|
||||
if deviceDescription != "" {
|
||||
deviceDescriptionUTF16, err = syscall.UTF16PtrFromString(deviceDescription)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
data := &DevInfoData{}
|
||||
data.size = uint32(unsafe.Sizeof(*data))
|
||||
|
||||
return data, setupDiCreateDeviceInfo(deviceInfoSet, deviceNameUTF16, classGUID, deviceDescriptionUTF16, hwndParent, creationFlags, data)
|
||||
}
|
||||
|
||||
// CreateDeviceInfo method creates a new device information element and adds it as a new member to the specified device information set.
|
||||
func (deviceInfoSet DevInfo) CreateDeviceInfo(deviceName string, classGUID *windows.GUID, deviceDescription string, hwndParent uintptr, creationFlags DICD) (*DevInfoData, error) {
|
||||
return SetupDiCreateDeviceInfo(deviceInfoSet, deviceName, classGUID, deviceDescription, hwndParent, creationFlags)
|
||||
}
|
||||
|
||||
//sys setupDiEnumDeviceInfo(deviceInfoSet DevInfo, memberIndex uint32, deviceInfoData *DevInfoData) (err error) = setupapi.SetupDiEnumDeviceInfo
|
||||
|
||||
// SetupDiEnumDeviceInfo function returns a DevInfoData structure that specifies a device information element in a device information set.
|
||||
func SetupDiEnumDeviceInfo(deviceInfoSet DevInfo, memberIndex int) (*DevInfoData, error) {
|
||||
data := &DevInfoData{}
|
||||
data.size = uint32(unsafe.Sizeof(*data))
|
||||
|
||||
return data, setupDiEnumDeviceInfo(deviceInfoSet, uint32(memberIndex), data)
|
||||
}
|
||||
|
||||
// EnumDeviceInfo method returns a DevInfoData structure that specifies a device information element in a device information set.
|
||||
func (deviceInfoSet DevInfo) EnumDeviceInfo(memberIndex int) (*DevInfoData, error) {
|
||||
return SetupDiEnumDeviceInfo(deviceInfoSet, memberIndex)
|
||||
}
|
||||
|
||||
// SetupDiDestroyDeviceInfoList function deletes a device information set and frees all associated memory.
|
||||
//sys SetupDiDestroyDeviceInfoList(deviceInfoSet DevInfo) (err error) = setupapi.SetupDiDestroyDeviceInfoList
|
||||
|
||||
// Close method deletes a device information set and frees all associated memory.
|
||||
func (deviceInfoSet DevInfo) Close() error {
|
||||
return SetupDiDestroyDeviceInfoList(deviceInfoSet)
|
||||
}
|
||||
|
||||
//sys SetupDiBuildDriverInfoList(deviceInfoSet DevInfo, deviceInfoData *DevInfoData, driverType SPDIT) (err error) = setupapi.SetupDiBuildDriverInfoList
|
||||
|
||||
// BuildDriverInfoList method builds a list of drivers that is associated with a specific device or with the global class driver list for a device information set.
|
||||
func (deviceInfoSet DevInfo) BuildDriverInfoList(deviceInfoData *DevInfoData, driverType SPDIT) error {
|
||||
return SetupDiBuildDriverInfoList(deviceInfoSet, deviceInfoData, driverType)
|
||||
}
|
||||
|
||||
//sys SetupDiCancelDriverInfoSearch(deviceInfoSet DevInfo) (err error) = setupapi.SetupDiCancelDriverInfoSearch
|
||||
|
||||
// CancelDriverInfoSearch method cancels a driver list search that is currently in progress in a different thread.
|
||||
func (deviceInfoSet DevInfo) CancelDriverInfoSearch() error {
|
||||
return SetupDiCancelDriverInfoSearch(deviceInfoSet)
|
||||
}
|
||||
|
||||
//sys setupDiEnumDriverInfo(deviceInfoSet DevInfo, deviceInfoData *DevInfoData, driverType SPDIT, memberIndex uint32, driverInfoData *DrvInfoData) (err error) = setupapi.SetupDiEnumDriverInfoW
|
||||
|
||||
// SetupDiEnumDriverInfo function enumerates the members of a driver list.
|
||||
func SetupDiEnumDriverInfo(deviceInfoSet DevInfo, deviceInfoData *DevInfoData, driverType SPDIT, memberIndex int) (*DrvInfoData, error) {
|
||||
data := &DrvInfoData{}
|
||||
data.size = uint32(unsafe.Sizeof(*data))
|
||||
|
||||
return data, setupDiEnumDriverInfo(deviceInfoSet, deviceInfoData, driverType, uint32(memberIndex), data)
|
||||
}
|
||||
|
||||
// EnumDriverInfo method enumerates the members of a driver list.
|
||||
func (deviceInfoSet DevInfo) EnumDriverInfo(deviceInfoData *DevInfoData, driverType SPDIT, memberIndex int) (*DrvInfoData, error) {
|
||||
return SetupDiEnumDriverInfo(deviceInfoSet, deviceInfoData, driverType, memberIndex)
|
||||
}
|
||||
|
||||
//sys setupDiGetSelectedDriver(deviceInfoSet DevInfo, deviceInfoData *DevInfoData, driverInfoData *DrvInfoData) (err error) = setupapi.SetupDiGetSelectedDriverW
|
||||
|
||||
// SetupDiGetSelectedDriver function retrieves the selected driver for a device information set or a particular device information element.
|
||||
func SetupDiGetSelectedDriver(deviceInfoSet DevInfo, deviceInfoData *DevInfoData) (*DrvInfoData, error) {
|
||||
data := &DrvInfoData{}
|
||||
data.size = uint32(unsafe.Sizeof(*data))
|
||||
|
||||
return data, setupDiGetSelectedDriver(deviceInfoSet, deviceInfoData, data)
|
||||
}
|
||||
|
||||
// GetSelectedDriver method retrieves the selected driver for a device information set or a particular device information element.
|
||||
func (deviceInfoSet DevInfo) GetSelectedDriver(deviceInfoData *DevInfoData) (*DrvInfoData, error) {
|
||||
return SetupDiGetSelectedDriver(deviceInfoSet, deviceInfoData)
|
||||
}
|
||||
|
||||
//sys SetupDiSetSelectedDriver(deviceInfoSet DevInfo, deviceInfoData *DevInfoData, driverInfoData *DrvInfoData) (err error) = setupapi.SetupDiSetSelectedDriverW
|
||||
|
||||
// SetSelectedDriver method sets, or resets, the selected driver for a device information element or the selected class driver for a device information set.
|
||||
func (deviceInfoSet DevInfo) SetSelectedDriver(deviceInfoData *DevInfoData, driverInfoData *DrvInfoData) error {
|
||||
return SetupDiSetSelectedDriver(deviceInfoSet, deviceInfoData, driverInfoData)
|
||||
}
|
||||
|
||||
//sys setupDiGetDriverInfoDetail(deviceInfoSet DevInfo, deviceInfoData *DevInfoData, driverInfoData *DrvInfoData, driverInfoDetailData *DrvInfoDetailData, driverInfoDetailDataSize uint32, requiredSize *uint32) (err error) = setupapi.SetupDiGetDriverInfoDetailW
|
||||
|
||||
// SetupDiGetDriverInfoDetail function retrieves driver information detail for a device information set or a particular device information element in the device information set.
|
||||
func SetupDiGetDriverInfoDetail(deviceInfoSet DevInfo, deviceInfoData *DevInfoData, driverInfoData *DrvInfoData) (*DrvInfoDetailData, error) {
|
||||
const bufCapacity = 0x800
|
||||
buf := [bufCapacity]byte{}
|
||||
var bufLen uint32
|
||||
|
||||
data := (*DrvInfoDetailData)(unsafe.Pointer(&buf[0]))
|
||||
data.size = uint32(unsafe.Sizeof(*data))
|
||||
|
||||
err := setupDiGetDriverInfoDetail(deviceInfoSet, deviceInfoData, driverInfoData, data, bufCapacity, &bufLen)
|
||||
if err == nil {
|
||||
// The buffer was was sufficiently big.
|
||||
data.size = bufLen
|
||||
return data, nil
|
||||
}
|
||||
|
||||
if errWin, ok := err.(syscall.Errno); ok && errWin == windows.ERROR_INSUFFICIENT_BUFFER {
|
||||
// The buffer was too small. Now that we got the required size, create another one big enough and retry.
|
||||
buf := make([]byte, bufLen)
|
||||
data := (*DrvInfoDetailData)(unsafe.Pointer(&buf[0]))
|
||||
data.size = uint32(unsafe.Sizeof(*data))
|
||||
|
||||
err = setupDiGetDriverInfoDetail(deviceInfoSet, deviceInfoData, driverInfoData, data, bufLen, &bufLen)
|
||||
if err == nil {
|
||||
data.size = bufLen
|
||||
return data, nil
|
||||
}
|
||||
}
|
||||
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// GetDriverInfoDetail method retrieves driver information detail for a device information set or a particular device information element in the device information set.
|
||||
func (deviceInfoSet DevInfo) GetDriverInfoDetail(deviceInfoData *DevInfoData, driverInfoData *DrvInfoData) (*DrvInfoDetailData, error) {
|
||||
return SetupDiGetDriverInfoDetail(deviceInfoSet, deviceInfoData, driverInfoData)
|
||||
}
|
||||
|
||||
//sys SetupDiDestroyDriverInfoList(deviceInfoSet DevInfo, deviceInfoData *DevInfoData, driverType SPDIT) (err error) = setupapi.SetupDiDestroyDriverInfoList
|
||||
|
||||
// DestroyDriverInfoList method deletes a driver list.
|
||||
func (deviceInfoSet DevInfo) DestroyDriverInfoList(deviceInfoData *DevInfoData, driverType SPDIT) error {
|
||||
return SetupDiDestroyDriverInfoList(deviceInfoSet, deviceInfoData, driverType)
|
||||
}
|
||||
|
||||
//sys setupDiGetClassDevsEx(classGUID *windows.GUID, Enumerator *uint16, hwndParent uintptr, Flags DIGCF, deviceInfoSet DevInfo, machineName *uint16, reserved uintptr) (handle DevInfo, err error) [failretval==DevInfo(windows.InvalidHandle)] = setupapi.SetupDiGetClassDevsExW
|
||||
|
||||
// SetupDiGetClassDevsEx function returns a handle to a device information set that contains requested device information elements for a local or a remote computer.
|
||||
func SetupDiGetClassDevsEx(classGUID *windows.GUID, enumerator string, hwndParent uintptr, flags DIGCF, deviceInfoSet DevInfo, machineName string) (handle DevInfo, err error) {
|
||||
var enumeratorUTF16 *uint16
|
||||
if enumerator != "" {
|
||||
enumeratorUTF16, err = syscall.UTF16PtrFromString(enumerator)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
var machineNameUTF16 *uint16
|
||||
if machineName != "" {
|
||||
machineNameUTF16, err = syscall.UTF16PtrFromString(machineName)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
return setupDiGetClassDevsEx(classGUID, enumeratorUTF16, hwndParent, flags, deviceInfoSet, machineNameUTF16, 0)
|
||||
}
|
||||
|
||||
// SetupDiCallClassInstaller function calls the appropriate class installer, and any registered co-installers, with the specified installation request (DIF code).
|
||||
//sys SetupDiCallClassInstaller(installFunction DI_FUNCTION, deviceInfoSet DevInfo, deviceInfoData *DevInfoData) (err error) = setupapi.SetupDiCallClassInstaller
|
||||
|
||||
// CallClassInstaller member calls the appropriate class installer, and any registered co-installers, with the specified installation request (DIF code).
|
||||
func (deviceInfoSet DevInfo) CallClassInstaller(installFunction DI_FUNCTION, deviceInfoData *DevInfoData) error {
|
||||
return SetupDiCallClassInstaller(installFunction, deviceInfoSet, deviceInfoData)
|
||||
}
|
||||
|
||||
//sys setupDiOpenDevRegKey(deviceInfoSet DevInfo, deviceInfoData *DevInfoData, Scope DICS_FLAG, HwProfile uint32, KeyType DIREG, samDesired uint32) (key windows.Handle, err error) [failretval==windows.InvalidHandle] = setupapi.SetupDiOpenDevRegKey
|
||||
|
||||
// SetupDiOpenDevRegKey function opens a registry key for device-specific configuration information.
|
||||
func SetupDiOpenDevRegKey(deviceInfoSet DevInfo, deviceInfoData *DevInfoData, scope DICS_FLAG, hwProfile uint32, keyType DIREG, samDesired uint32) (registry.Key, error) {
|
||||
handle, err := setupDiOpenDevRegKey(deviceInfoSet, deviceInfoData, scope, hwProfile, keyType, samDesired)
|
||||
return registry.Key(handle), err
|
||||
}
|
||||
|
||||
// OpenDevRegKey method opens a registry key for device-specific configuration information.
|
||||
func (deviceInfoSet DevInfo) OpenDevRegKey(DeviceInfoData *DevInfoData, Scope DICS_FLAG, HwProfile uint32, KeyType DIREG, samDesired uint32) (registry.Key, error) {
|
||||
return SetupDiOpenDevRegKey(deviceInfoSet, DeviceInfoData, Scope, HwProfile, KeyType, samDesired)
|
||||
}
|
||||
|
||||
//sys setupDiGetDeviceRegistryProperty(deviceInfoSet DevInfo, deviceInfoData *DevInfoData, property SPDRP, propertyRegDataType *uint32, propertyBuffer *byte, propertyBufferSize uint32, requiredSize *uint32) (err error) = setupapi.SetupDiGetDeviceRegistryPropertyW
|
||||
|
||||
// SetupDiGetDeviceRegistryProperty function retrieves a specified Plug and Play device property.
|
||||
func SetupDiGetDeviceRegistryProperty(deviceInfoSet DevInfo, deviceInfoData *DevInfoData, property SPDRP) (value interface{}, err error) {
|
||||
buf := make([]byte, 0x100)
|
||||
var dataType, bufLen uint32
|
||||
err = setupDiGetDeviceRegistryProperty(deviceInfoSet, deviceInfoData, property, &dataType, &buf[0], uint32(cap(buf)), &bufLen)
|
||||
if err == nil {
|
||||
// The buffer was sufficiently big.
|
||||
return getRegistryValue(buf[:bufLen], dataType)
|
||||
}
|
||||
|
||||
if errWin, ok := err.(syscall.Errno); ok && errWin == windows.ERROR_INSUFFICIENT_BUFFER {
|
||||
// The buffer was too small. Now that we got the required size, create another one big enough and retry.
|
||||
buf = make([]byte, bufLen)
|
||||
err = setupDiGetDeviceRegistryProperty(deviceInfoSet, deviceInfoData, property, &dataType, &buf[0], uint32(cap(buf)), &bufLen)
|
||||
if err == nil {
|
||||
return getRegistryValue(buf[:bufLen], dataType)
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func getRegistryValue(buf []byte, dataType uint32) (interface{}, error) {
|
||||
switch dataType {
|
||||
case windows.REG_SZ:
|
||||
return windows.UTF16ToString(BufToUTF16(buf)), nil
|
||||
case windows.REG_EXPAND_SZ:
|
||||
return registry.ExpandString(windows.UTF16ToString(BufToUTF16(buf)))
|
||||
case windows.REG_BINARY:
|
||||
return buf, nil
|
||||
case windows.REG_DWORD_LITTLE_ENDIAN:
|
||||
return binary.LittleEndian.Uint32(buf), nil
|
||||
case windows.REG_DWORD_BIG_ENDIAN:
|
||||
return binary.BigEndian.Uint32(buf), nil
|
||||
case windows.REG_MULTI_SZ:
|
||||
bufW := BufToUTF16(buf)
|
||||
a := []string{}
|
||||
for i := 0; i < len(bufW); {
|
||||
j := i + wcslen(bufW[i:])
|
||||
if i < j {
|
||||
a = append(a, windows.UTF16ToString(bufW[i:j]))
|
||||
}
|
||||
i = j + 1
|
||||
}
|
||||
return a, nil
|
||||
case windows.REG_QWORD_LITTLE_ENDIAN:
|
||||
return binary.LittleEndian.Uint64(buf), nil
|
||||
default:
|
||||
return nil, fmt.Errorf("Unsupported registry value type: %v", dataType)
|
||||
}
|
||||
}
|
||||
|
||||
// BufToUTF16 function reinterprets []byte buffer as []uint16
|
||||
func BufToUTF16(buf []byte) []uint16 {
|
||||
sl := struct {
|
||||
addr *uint16
|
||||
len int
|
||||
cap int
|
||||
}{(*uint16)(unsafe.Pointer(&buf[0])), len(buf) / 2, cap(buf) / 2}
|
||||
return *(*[]uint16)(unsafe.Pointer(&sl))
|
||||
}
|
||||
|
||||
// UTF16ToBuf function reinterprets []uint16 as []byte
|
||||
func UTF16ToBuf(buf []uint16) []byte {
|
||||
sl := struct {
|
||||
addr *byte
|
||||
len int
|
||||
cap int
|
||||
}{(*byte)(unsafe.Pointer(&buf[0])), len(buf) * 2, cap(buf) * 2}
|
||||
return *(*[]byte)(unsafe.Pointer(&sl))
|
||||
}
|
||||
|
||||
func wcslen(str []uint16) int {
|
||||
for i := 0; i < len(str); i++ {
|
||||
if str[i] == 0 {
|
||||
return i
|
||||
}
|
||||
}
|
||||
return len(str)
|
||||
}
|
||||
|
||||
// GetDeviceRegistryProperty method retrieves a specified Plug and Play device property.
|
||||
func (deviceInfoSet DevInfo) GetDeviceRegistryProperty(deviceInfoData *DevInfoData, property SPDRP) (interface{}, error) {
|
||||
return SetupDiGetDeviceRegistryProperty(deviceInfoSet, deviceInfoData, property)
|
||||
}
|
||||
|
||||
//sys setupDiSetDeviceRegistryProperty(deviceInfoSet DevInfo, deviceInfoData *DevInfoData, property SPDRP, propertyBuffer *byte, propertyBufferSize uint32) (err error) = setupapi.SetupDiSetDeviceRegistryPropertyW
|
||||
|
||||
// SetupDiSetDeviceRegistryProperty function sets a Plug and Play device property for a device.
|
||||
func SetupDiSetDeviceRegistryProperty(deviceInfoSet DevInfo, deviceInfoData *DevInfoData, property SPDRP, propertyBuffers []byte) error {
|
||||
return setupDiSetDeviceRegistryProperty(deviceInfoSet, deviceInfoData, property, &propertyBuffers[0], uint32(len(propertyBuffers)))
|
||||
}
|
||||
|
||||
// SetDeviceRegistryProperty function sets a Plug and Play device property for a device.
|
||||
func (deviceInfoSet DevInfo) SetDeviceRegistryProperty(deviceInfoData *DevInfoData, property SPDRP, propertyBuffers []byte) error {
|
||||
return SetupDiSetDeviceRegistryProperty(deviceInfoSet, deviceInfoData, property, propertyBuffers)
|
||||
}
|
||||
|
||||
//sys setupDiGetDeviceInstallParams(deviceInfoSet DevInfo, deviceInfoData *DevInfoData, deviceInstallParams *DevInstallParams) (err error) = setupapi.SetupDiGetDeviceInstallParamsW
|
||||
|
||||
// SetupDiGetDeviceInstallParams function retrieves device installation parameters for a device information set or a particular device information element.
|
||||
func SetupDiGetDeviceInstallParams(deviceInfoSet DevInfo, deviceInfoData *DevInfoData) (*DevInstallParams, error) {
|
||||
params := &DevInstallParams{}
|
||||
params.size = uint32(unsafe.Sizeof(*params))
|
||||
|
||||
return params, setupDiGetDeviceInstallParams(deviceInfoSet, deviceInfoData, params)
|
||||
}
|
||||
|
||||
// GetDeviceInstallParams method retrieves device installation parameters for a device information set or a particular device information element.
|
||||
func (deviceInfoSet DevInfo) GetDeviceInstallParams(deviceInfoData *DevInfoData) (*DevInstallParams, error) {
|
||||
return SetupDiGetDeviceInstallParams(deviceInfoSet, deviceInfoData)
|
||||
}
|
||||
|
||||
// SetupDiGetClassInstallParams function retrieves class installation parameters for a device information set or a particular device information element.
|
||||
//sys SetupDiGetClassInstallParams(deviceInfoSet DevInfo, deviceInfoData *DevInfoData, classInstallParams *ClassInstallHeader, classInstallParamsSize uint32, requiredSize *uint32) (err error) = setupapi.SetupDiGetClassInstallParamsW
|
||||
|
||||
// GetClassInstallParams method retrieves class installation parameters for a device information set or a particular device information element.
|
||||
func (deviceInfoSet DevInfo) GetClassInstallParams(deviceInfoData *DevInfoData, classInstallParams *ClassInstallHeader, classInstallParamsSize uint32, requiredSize *uint32) error {
|
||||
return SetupDiGetClassInstallParams(deviceInfoSet, deviceInfoData, classInstallParams, classInstallParamsSize, requiredSize)
|
||||
}
|
||||
|
||||
//sys SetupDiSetDeviceInstallParams(deviceInfoSet DevInfo, deviceInfoData *DevInfoData, deviceInstallParams *DevInstallParams) (err error) = setupapi.SetupDiSetDeviceInstallParamsW
|
||||
|
||||
// SetDeviceInstallParams member sets device installation parameters for a device information set or a particular device information element.
|
||||
func (deviceInfoSet DevInfo) SetDeviceInstallParams(deviceInfoData *DevInfoData, deviceInstallParams *DevInstallParams) error {
|
||||
return SetupDiSetDeviceInstallParams(deviceInfoSet, deviceInfoData, deviceInstallParams)
|
||||
}
|
||||
|
||||
// SetupDiSetClassInstallParams function sets or clears class install parameters for a device information set or a particular device information element.
|
||||
//sys SetupDiSetClassInstallParams(deviceInfoSet DevInfo, deviceInfoData *DevInfoData, classInstallParams *ClassInstallHeader, classInstallParamsSize uint32) (err error) = setupapi.SetupDiSetClassInstallParamsW
|
||||
|
||||
// SetClassInstallParams method sets or clears class install parameters for a device information set or a particular device information element.
|
||||
func (deviceInfoSet DevInfo) SetClassInstallParams(deviceInfoData *DevInfoData, classInstallParams *ClassInstallHeader, classInstallParamsSize uint32) error {
|
||||
return SetupDiSetClassInstallParams(deviceInfoSet, deviceInfoData, classInstallParams, classInstallParamsSize)
|
||||
}
|
||||
|
||||
//sys setupDiClassNameFromGuidEx(classGUID *windows.GUID, className *uint16, classNameSize uint32, requiredSize *uint32, machineName *uint16, reserved uintptr) (err error) = setupapi.SetupDiClassNameFromGuidExW
|
||||
|
||||
// SetupDiClassNameFromGuidEx function retrieves the class name associated with a class GUID. The class can be installed on a local or remote computer.
|
||||
func SetupDiClassNameFromGuidEx(classGUID *windows.GUID, machineName string) (className string, err error) {
|
||||
var classNameUTF16 [MAX_CLASS_NAME_LEN]uint16
|
||||
|
||||
var machineNameUTF16 *uint16
|
||||
if machineName != "" {
|
||||
machineNameUTF16, err = syscall.UTF16PtrFromString(machineName)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
err = setupDiClassNameFromGuidEx(classGUID, &classNameUTF16[0], MAX_CLASS_NAME_LEN, nil, machineNameUTF16, 0)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
className = windows.UTF16ToString(classNameUTF16[:])
|
||||
return
|
||||
}
|
||||
|
||||
//sys setupDiClassGuidsFromNameEx(className *uint16, classGuidList *windows.GUID, classGuidListSize uint32, requiredSize *uint32, machineName *uint16, reserved uintptr) (err error) = setupapi.SetupDiClassGuidsFromNameExW
|
||||
|
||||
// SetupDiClassGuidsFromNameEx function retrieves the GUIDs associated with the specified class name. This resulting list contains the classes currently installed on a local or remote computer.
|
||||
func SetupDiClassGuidsFromNameEx(className string, machineName string) ([]windows.GUID, error) {
|
||||
classNameUTF16, err := syscall.UTF16PtrFromString(className)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
const bufCapacity = 4
|
||||
var buf [bufCapacity]windows.GUID
|
||||
var bufLen uint32
|
||||
|
||||
var machineNameUTF16 *uint16
|
||||
if machineName != "" {
|
||||
machineNameUTF16, err = syscall.UTF16PtrFromString(machineName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
err = setupDiClassGuidsFromNameEx(classNameUTF16, &buf[0], bufCapacity, &bufLen, machineNameUTF16, 0)
|
||||
if err == nil {
|
||||
// The GUID array was sufficiently big. Return its slice.
|
||||
return buf[:bufLen], nil
|
||||
}
|
||||
|
||||
if errWin, ok := err.(syscall.Errno); ok && errWin == windows.ERROR_INSUFFICIENT_BUFFER {
|
||||
// The GUID array was too small. Now that we got the required size, create another one big enough and retry.
|
||||
buf := make([]windows.GUID, bufLen)
|
||||
err = setupDiClassGuidsFromNameEx(classNameUTF16, &buf[0], bufLen, &bufLen, machineNameUTF16, 0)
|
||||
if err == nil {
|
||||
return buf[:bufLen], nil
|
||||
}
|
||||
}
|
||||
|
||||
return nil, err
|
||||
}
|
||||
|
||||
//sys setupDiGetSelectedDevice(deviceInfoSet DevInfo, deviceInfoData *DevInfoData) (err error) = setupapi.SetupDiGetSelectedDevice
|
||||
|
||||
// SetupDiGetSelectedDevice function retrieves the selected device information element in a device information set.
|
||||
func SetupDiGetSelectedDevice(deviceInfoSet DevInfo) (*DevInfoData, error) {
|
||||
data := &DevInfoData{}
|
||||
data.size = uint32(unsafe.Sizeof(*data))
|
||||
|
||||
return data, setupDiGetSelectedDevice(deviceInfoSet, data)
|
||||
}
|
||||
|
||||
// GetSelectedDevice method retrieves the selected device information element in a device information set.
|
||||
func (deviceInfoSet DevInfo) GetSelectedDevice() (*DevInfoData, error) {
|
||||
return SetupDiGetSelectedDevice(deviceInfoSet)
|
||||
}
|
||||
|
||||
// SetupDiSetSelectedDevice function sets a device information element as the selected member of a device information set. This function is typically used by an installation wizard.
|
||||
//sys SetupDiSetSelectedDevice(deviceInfoSet DevInfo, deviceInfoData *DevInfoData) (err error) = setupapi.SetupDiSetSelectedDevice
|
||||
|
||||
// SetSelectedDevice method sets a device information element as the selected member of a device information set. This function is typically used by an installation wizard.
|
||||
func (deviceInfoSet DevInfo) SetSelectedDevice(deviceInfoData *DevInfoData) error {
|
||||
return SetupDiSetSelectedDevice(deviceInfoSet, deviceInfoData)
|
||||
}
|
||||
483
tun/wintun/setupapi/setupapi_windows_test.go
Normal file
483
tun/wintun/setupapi/setupapi_windows_test.go
Normal file
@@ -0,0 +1,483 @@
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2019 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package setupapi
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"syscall"
|
||||
"testing"
|
||||
|
||||
"golang.org/x/sys/windows"
|
||||
"golang.zx2c4.com/wireguard/tun/wintun/guid"
|
||||
)
|
||||
|
||||
var deviceClassNetGUID = windows.GUID{Data1: 0x4d36e972, Data2: 0xe325, Data3: 0x11ce, Data4: [8]byte{0xbf, 0xc1, 0x08, 0x00, 0x2b, 0xe1, 0x03, 0x18}}
|
||||
var computerName string
|
||||
|
||||
func init() {
|
||||
computerName, _ = windows.ComputerName()
|
||||
}
|
||||
|
||||
func TestSetupDiCreateDeviceInfoListEx(t *testing.T) {
|
||||
devInfoList, err := SetupDiCreateDeviceInfoListEx(&deviceClassNetGUID, 0, "")
|
||||
if err == nil {
|
||||
devInfoList.Close()
|
||||
} else {
|
||||
t.Errorf("Error calling SetupDiCreateDeviceInfoListEx: %s", err.Error())
|
||||
}
|
||||
|
||||
devInfoList, err = SetupDiCreateDeviceInfoListEx(&deviceClassNetGUID, 0, computerName)
|
||||
if err == nil {
|
||||
devInfoList.Close()
|
||||
} else {
|
||||
t.Errorf("Error calling SetupDiCreateDeviceInfoListEx: %s", err.Error())
|
||||
}
|
||||
|
||||
devInfoList, err = SetupDiCreateDeviceInfoListEx(nil, 0, "")
|
||||
if err == nil {
|
||||
devInfoList.Close()
|
||||
} else {
|
||||
t.Errorf("Error calling SetupDiCreateDeviceInfoListEx(nil): %s", err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
func TestSetupDiGetDeviceInfoListDetail(t *testing.T) {
|
||||
devInfoList, err := SetupDiGetClassDevsEx(&deviceClassNetGUID, "", 0, DIGCF_PRESENT, DevInfo(0), "")
|
||||
if err != nil {
|
||||
t.Errorf("Error calling SetupDiGetClassDevsEx: %s", err.Error())
|
||||
}
|
||||
defer devInfoList.Close()
|
||||
|
||||
data, err := devInfoList.GetDeviceInfoListDetail()
|
||||
if err != nil {
|
||||
t.Errorf("Error calling SetupDiGetDeviceInfoListDetail: %s", err.Error())
|
||||
} else {
|
||||
if data.ClassGUID != deviceClassNetGUID {
|
||||
t.Error("SetupDiGetDeviceInfoListDetail returned different class GUID")
|
||||
}
|
||||
|
||||
if data.RemoteMachineHandle != windows.Handle(0) {
|
||||
t.Error("SetupDiGetDeviceInfoListDetail returned non-NULL remote machine handle")
|
||||
}
|
||||
|
||||
if data.GetRemoteMachineName() != "" {
|
||||
t.Error("SetupDiGetDeviceInfoListDetail returned non-NULL remote machine name")
|
||||
}
|
||||
}
|
||||
|
||||
devInfoList, err = SetupDiGetClassDevsEx(&deviceClassNetGUID, "", 0, DIGCF_PRESENT, DevInfo(0), computerName)
|
||||
if err != nil {
|
||||
t.Errorf("Error calling SetupDiGetClassDevsEx: %s", err.Error())
|
||||
}
|
||||
defer devInfoList.Close()
|
||||
|
||||
data, err = devInfoList.GetDeviceInfoListDetail()
|
||||
if err != nil {
|
||||
t.Errorf("Error calling SetupDiGetDeviceInfoListDetail: %s", err.Error())
|
||||
} else {
|
||||
if data.ClassGUID != deviceClassNetGUID {
|
||||
t.Error("SetupDiGetDeviceInfoListDetail returned different class GUID")
|
||||
}
|
||||
|
||||
if data.RemoteMachineHandle == windows.Handle(0) {
|
||||
t.Error("SetupDiGetDeviceInfoListDetail returned NULL remote machine handle")
|
||||
}
|
||||
|
||||
if data.GetRemoteMachineName() != computerName {
|
||||
t.Error("SetupDiGetDeviceInfoListDetail returned different remote machine name")
|
||||
}
|
||||
}
|
||||
|
||||
data = &DevInfoListDetailData{}
|
||||
data.SetRemoteMachineName("foobar")
|
||||
if data.GetRemoteMachineName() != "foobar" {
|
||||
t.Error("DevInfoListDetailData.(Get|Set)RemoteMachineName() differ")
|
||||
}
|
||||
}
|
||||
|
||||
func TestSetupDiCreateDeviceInfo(t *testing.T) {
|
||||
devInfoList, err := SetupDiCreateDeviceInfoListEx(&deviceClassNetGUID, 0, computerName)
|
||||
if err != nil {
|
||||
t.Errorf("Error calling SetupDiCreateDeviceInfoListEx: %s", err.Error())
|
||||
}
|
||||
defer devInfoList.Close()
|
||||
|
||||
deviceClassNetName, err := SetupDiClassNameFromGuidEx(&deviceClassNetGUID, computerName)
|
||||
if err != nil {
|
||||
t.Errorf("Error calling SetupDiClassNameFromGuidEx: %s", err.Error())
|
||||
}
|
||||
|
||||
devInfoData, err := devInfoList.CreateDeviceInfo(deviceClassNetName, &deviceClassNetGUID, "This is a test device", 0, DICD_GENERATE_ID)
|
||||
if err != nil {
|
||||
// Access denied is expected, as the SetupDiCreateDeviceInfo() require elevation to succeed.
|
||||
if errWin, ok := err.(syscall.Errno); !ok || errWin != windows.ERROR_ACCESS_DENIED {
|
||||
t.Errorf("Error calling SetupDiCreateDeviceInfo: %s", err.Error())
|
||||
}
|
||||
} else if devInfoData.ClassGUID != deviceClassNetGUID {
|
||||
t.Error("SetupDiCreateDeviceInfo returned different class GUID")
|
||||
}
|
||||
}
|
||||
|
||||
func TestSetupDiEnumDeviceInfo(t *testing.T) {
|
||||
devInfoList, err := SetupDiGetClassDevsEx(&deviceClassNetGUID, "", 0, DIGCF_PRESENT, DevInfo(0), "")
|
||||
if err != nil {
|
||||
t.Errorf("Error calling SetupDiGetClassDevsEx: %s", err.Error())
|
||||
}
|
||||
defer devInfoList.Close()
|
||||
|
||||
for i := 0; true; i++ {
|
||||
data, err := devInfoList.EnumDeviceInfo(i)
|
||||
if err != nil {
|
||||
if errWin, ok := err.(syscall.Errno); ok && errWin == 259 /*ERROR_NO_MORE_ITEMS*/ {
|
||||
break
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
if data.ClassGUID != deviceClassNetGUID {
|
||||
t.Error("SetupDiEnumDeviceInfo returned different class GUID")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestDevInfo_BuildDriverInfoList(t *testing.T) {
|
||||
devInfoList, err := SetupDiGetClassDevsEx(&deviceClassNetGUID, "", 0, DIGCF_PRESENT, DevInfo(0), "")
|
||||
if err != nil {
|
||||
t.Errorf("Error calling SetupDiGetClassDevsEx: %s", err.Error())
|
||||
}
|
||||
defer devInfoList.Close()
|
||||
|
||||
for i := 0; true; i++ {
|
||||
deviceData, err := devInfoList.EnumDeviceInfo(i)
|
||||
if err != nil {
|
||||
if errWin, ok := err.(syscall.Errno); ok && errWin == 259 /*ERROR_NO_MORE_ITEMS*/ {
|
||||
break
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
const driverType SPDIT = SPDIT_COMPATDRIVER
|
||||
err = devInfoList.BuildDriverInfoList(deviceData, driverType)
|
||||
if err != nil {
|
||||
t.Errorf("Error calling SetupDiBuildDriverInfoList: %s", err.Error())
|
||||
}
|
||||
defer devInfoList.DestroyDriverInfoList(deviceData, driverType)
|
||||
|
||||
var selectedDriverData *DrvInfoData
|
||||
for j := 0; true; j++ {
|
||||
driverData, err := devInfoList.EnumDriverInfo(deviceData, driverType, j)
|
||||
if err != nil {
|
||||
if errWin, ok := err.(syscall.Errno); ok && errWin == 259 /*ERROR_NO_MORE_ITEMS*/ {
|
||||
break
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
if driverData.DriverType == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
if !driverData.IsNewer(windows.Filetime{}, 0) {
|
||||
t.Error("Driver should have non-zero date and version")
|
||||
}
|
||||
if !driverData.IsNewer(windows.Filetime{HighDateTime: driverData.DriverDate.HighDateTime}, 0) {
|
||||
t.Error("Driver should have non-zero date and version")
|
||||
}
|
||||
if driverData.IsNewer(windows.Filetime{HighDateTime: driverData.DriverDate.HighDateTime + 1}, 0) {
|
||||
t.Error("Driver should report newer version on high-date-time")
|
||||
}
|
||||
if !driverData.IsNewer(windows.Filetime{HighDateTime: driverData.DriverDate.HighDateTime, LowDateTime: driverData.DriverDate.LowDateTime}, 0) {
|
||||
t.Error("Driver should have non-zero version")
|
||||
}
|
||||
if driverData.IsNewer(windows.Filetime{HighDateTime: driverData.DriverDate.HighDateTime, LowDateTime: driverData.DriverDate.LowDateTime + 1}, 0) {
|
||||
t.Error("Driver should report newer version on low-date-time")
|
||||
}
|
||||
if driverData.IsNewer(windows.Filetime{HighDateTime: driverData.DriverDate.HighDateTime, LowDateTime: driverData.DriverDate.LowDateTime}, driverData.DriverVersion) {
|
||||
t.Error("Driver should not be newer than itself")
|
||||
}
|
||||
if driverData.IsNewer(windows.Filetime{HighDateTime: driverData.DriverDate.HighDateTime, LowDateTime: driverData.DriverDate.LowDateTime}, driverData.DriverVersion+1) {
|
||||
t.Error("Driver should report newer version on version")
|
||||
}
|
||||
|
||||
err = devInfoList.SetSelectedDriver(deviceData, driverData)
|
||||
if err != nil {
|
||||
t.Errorf("Error calling SetupDiSetSelectedDriver: %s", err.Error())
|
||||
} else {
|
||||
selectedDriverData = driverData
|
||||
}
|
||||
|
||||
driverDetailData, err := devInfoList.GetDriverInfoDetail(deviceData, driverData)
|
||||
if err != nil {
|
||||
t.Errorf("Error calling SetupDiGetDriverInfoDetail: %s", err.Error())
|
||||
}
|
||||
|
||||
if driverDetailData.IsCompatible("foobar-aab6e3a4-144e-4786-88d3-6cec361e1edd") {
|
||||
t.Error("Invalid HWID compatibitlity reported")
|
||||
}
|
||||
if !driverDetailData.IsCompatible(strings.ToUpper(driverDetailData.GetHardwareID())) {
|
||||
t.Error("HWID compatibitlity missed")
|
||||
}
|
||||
a := driverDetailData.GetCompatIDs()
|
||||
for k := range a {
|
||||
if !driverDetailData.IsCompatible(strings.ToUpper(a[k])) {
|
||||
t.Error("HWID compatibitlity missed")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
selectedDriverData2, err := devInfoList.GetSelectedDriver(deviceData)
|
||||
if err != nil {
|
||||
t.Errorf("Error calling SetupDiGetSelectedDriver: %s", err.Error())
|
||||
} else if *selectedDriverData != *selectedDriverData2 {
|
||||
t.Error("SetupDiGetSelectedDriver should return driver selected with SetupDiSetSelectedDriver")
|
||||
}
|
||||
}
|
||||
|
||||
data := &DrvInfoData{}
|
||||
data.SetDescription("foobar")
|
||||
if data.GetDescription() != "foobar" {
|
||||
t.Error("DrvInfoData.(Get|Set)Description() differ")
|
||||
}
|
||||
data.SetMfgName("foobar")
|
||||
if data.GetMfgName() != "foobar" {
|
||||
t.Error("DrvInfoData.(Get|Set)MfgName() differ")
|
||||
}
|
||||
data.SetProviderName("foobar")
|
||||
if data.GetProviderName() != "foobar" {
|
||||
t.Error("DrvInfoData.(Get|Set)ProviderName() differ")
|
||||
}
|
||||
}
|
||||
|
||||
func TestSetupDiGetClassDevsEx(t *testing.T) {
|
||||
devInfoList, err := SetupDiGetClassDevsEx(&deviceClassNetGUID, "PCI", 0, DIGCF_PRESENT, DevInfo(0), computerName)
|
||||
if err == nil {
|
||||
devInfoList.Close()
|
||||
} else {
|
||||
t.Errorf("Error calling SetupDiGetClassDevsEx: %s", err.Error())
|
||||
}
|
||||
|
||||
devInfoList, err = SetupDiGetClassDevsEx(nil, "", 0, DIGCF_PRESENT, DevInfo(0), "")
|
||||
if err == nil {
|
||||
devInfoList.Close()
|
||||
t.Errorf("SetupDiGetClassDevsEx(nil, ...) should fail")
|
||||
} else {
|
||||
if errWin, ok := err.(syscall.Errno); !ok || errWin != 87 /*ERROR_INVALID_PARAMETER*/ {
|
||||
t.Errorf("SetupDiGetClassDevsEx(nil, ...) should fail with ERROR_INVALID_PARAMETER")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestSetupDiOpenDevRegKey(t *testing.T) {
|
||||
devInfoList, err := SetupDiGetClassDevsEx(&deviceClassNetGUID, "", 0, DIGCF_PRESENT, DevInfo(0), "")
|
||||
if err != nil {
|
||||
t.Errorf("Error calling SetupDiGetClassDevsEx: %s", err.Error())
|
||||
}
|
||||
defer devInfoList.Close()
|
||||
|
||||
for i := 0; true; i++ {
|
||||
data, err := devInfoList.EnumDeviceInfo(i)
|
||||
if err != nil {
|
||||
if errWin, ok := err.(syscall.Errno); ok && errWin == 259 /*ERROR_NO_MORE_ITEMS*/ {
|
||||
break
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
key, err := devInfoList.OpenDevRegKey(data, DICS_FLAG_GLOBAL, 0, DIREG_DRV, windows.KEY_READ)
|
||||
if err != nil {
|
||||
t.Errorf("Error calling SetupDiOpenDevRegKey: %s", err.Error())
|
||||
}
|
||||
defer key.Close()
|
||||
}
|
||||
}
|
||||
|
||||
func TestSetupDiGetDeviceRegistryProperty(t *testing.T) {
|
||||
devInfoList, err := SetupDiGetClassDevsEx(&deviceClassNetGUID, "", 0, DIGCF_PRESENT, DevInfo(0), "")
|
||||
if err != nil {
|
||||
t.Errorf("Error calling SetupDiGetClassDevsEx: %s", err.Error())
|
||||
}
|
||||
defer devInfoList.Close()
|
||||
|
||||
for i := 0; true; i++ {
|
||||
data, err := devInfoList.EnumDeviceInfo(i)
|
||||
if err != nil {
|
||||
if errWin, ok := err.(syscall.Errno); ok && errWin == 259 /*ERROR_NO_MORE_ITEMS*/ {
|
||||
break
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
val, err := devInfoList.GetDeviceRegistryProperty(data, SPDRP_CLASS)
|
||||
if err != nil {
|
||||
t.Errorf("Error calling SetupDiGetDeviceRegistryProperty(SPDRP_CLASS): %s", err.Error())
|
||||
} else if class, ok := val.(string); !ok || strings.ToLower(class) != "net" {
|
||||
t.Errorf("SetupDiGetDeviceRegistryProperty(SPDRP_CLASS) should return \"Net\"")
|
||||
}
|
||||
|
||||
val, err = devInfoList.GetDeviceRegistryProperty(data, SPDRP_CLASSGUID)
|
||||
if err != nil {
|
||||
t.Errorf("Error calling SetupDiGetDeviceRegistryProperty(SPDRP_CLASSGUID): %s", err.Error())
|
||||
} else if valStr, ok := val.(string); !ok {
|
||||
t.Errorf("SetupDiGetDeviceRegistryProperty(SPDRP_CLASSGUID) should return string")
|
||||
} else {
|
||||
classGUID, err := guid.FromString(valStr)
|
||||
if err != nil {
|
||||
t.Errorf("Error parsing GUID returned by SetupDiGetDeviceRegistryProperty(SPDRP_CLASSGUID): %s", err.Error())
|
||||
} else if *classGUID != deviceClassNetGUID {
|
||||
t.Errorf("SetupDiGetDeviceRegistryProperty(SPDRP_CLASSGUID) should return %x", deviceClassNetGUID)
|
||||
}
|
||||
}
|
||||
|
||||
val, err = devInfoList.GetDeviceRegistryProperty(data, SPDRP_COMPATIBLEIDS)
|
||||
if err != nil {
|
||||
// Some devices have no SPDRP_COMPATIBLEIDS.
|
||||
if errWin, ok := err.(syscall.Errno); !ok || errWin != 13 /*windows.ERROR_INVALID_DATA*/ {
|
||||
t.Errorf("Error calling SetupDiGetDeviceRegistryProperty(SPDRP_COMPATIBLEIDS): %s", err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
val, err = devInfoList.GetDeviceRegistryProperty(data, SPDRP_CONFIGFLAGS)
|
||||
if err != nil {
|
||||
t.Errorf("Error calling SetupDiGetDeviceRegistryProperty(SPDRP_CONFIGFLAGS): %s", err.Error())
|
||||
}
|
||||
|
||||
val, err = devInfoList.GetDeviceRegistryProperty(data, SPDRP_DEVICE_POWER_DATA)
|
||||
if err != nil {
|
||||
t.Errorf("Error calling SetupDiGetDeviceRegistryProperty(SPDRP_DEVICE_POWER_DATA): %s", err.Error())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestSetupDiGetDeviceInstallParams(t *testing.T) {
|
||||
devInfoList, err := SetupDiGetClassDevsEx(&deviceClassNetGUID, "", 0, DIGCF_PRESENT, DevInfo(0), "")
|
||||
if err != nil {
|
||||
t.Errorf("Error calling SetupDiGetClassDevsEx: %s", err.Error())
|
||||
}
|
||||
defer devInfoList.Close()
|
||||
|
||||
for i := 0; true; i++ {
|
||||
data, err := devInfoList.EnumDeviceInfo(i)
|
||||
if err != nil {
|
||||
if errWin, ok := err.(syscall.Errno); ok && errWin == 259 /*ERROR_NO_MORE_ITEMS*/ {
|
||||
break
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
_, err = devInfoList.GetDeviceInstallParams(data)
|
||||
if err != nil {
|
||||
t.Errorf("Error calling SetupDiGetDeviceInstallParams: %s", err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
params := &DevInstallParams{}
|
||||
params.SetDriverPath("foobar")
|
||||
if params.GetDriverPath() != "foobar" {
|
||||
t.Error("DevInstallParams.(Get|Set)DriverPath() differ")
|
||||
}
|
||||
}
|
||||
|
||||
func TestSetupDiClassNameFromGuidEx(t *testing.T) {
|
||||
deviceClassNetName, err := SetupDiClassNameFromGuidEx(&deviceClassNetGUID, "")
|
||||
if err != nil {
|
||||
t.Errorf("Error calling SetupDiClassNameFromGuidEx: %s", err.Error())
|
||||
} else if strings.ToLower(deviceClassNetName) != "net" {
|
||||
t.Errorf("SetupDiClassNameFromGuidEx(%x) should return \"Net\"", deviceClassNetGUID)
|
||||
}
|
||||
|
||||
deviceClassNetName, err = SetupDiClassNameFromGuidEx(&deviceClassNetGUID, computerName)
|
||||
if err != nil {
|
||||
t.Errorf("Error calling SetupDiClassNameFromGuidEx: %s", err.Error())
|
||||
} else if strings.ToLower(deviceClassNetName) != "net" {
|
||||
t.Errorf("SetupDiClassNameFromGuidEx(%x) should return \"Net\"", deviceClassNetGUID)
|
||||
}
|
||||
|
||||
_, err = SetupDiClassNameFromGuidEx(nil, "")
|
||||
if err == nil {
|
||||
t.Errorf("SetupDiClassNameFromGuidEx(nil) should fail")
|
||||
} else {
|
||||
if errWin, ok := err.(syscall.Errno); !ok || errWin != 1784 /*ERROR_INVALID_USER_BUFFER*/ {
|
||||
t.Errorf("SetupDiClassNameFromGuidEx(nil) should fail with ERROR_INVALID_USER_BUFFER")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestSetupDiClassGuidsFromNameEx(t *testing.T) {
|
||||
ClassGUIDs, err := SetupDiClassGuidsFromNameEx("Net", "")
|
||||
if err != nil {
|
||||
t.Errorf("Error calling SetupDiClassGuidsFromNameEx: %s", err.Error())
|
||||
} else {
|
||||
found := false
|
||||
for i := range ClassGUIDs {
|
||||
if ClassGUIDs[i] == deviceClassNetGUID {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
t.Errorf("SetupDiClassGuidsFromNameEx(\"Net\") should return %x", deviceClassNetGUID)
|
||||
}
|
||||
}
|
||||
|
||||
ClassGUIDs, err = SetupDiClassGuidsFromNameEx("foobar-34274a51-a6e6-45f0-80d6-c62be96dd5fe", computerName)
|
||||
if err != nil {
|
||||
t.Errorf("Error calling SetupDiClassGuidsFromNameEx: %s", err.Error())
|
||||
} else if len(ClassGUIDs) != 0 {
|
||||
t.Errorf("SetupDiClassGuidsFromNameEx(\"foobar-34274a51-a6e6-45f0-80d6-c62be96dd5fe\") should return an empty GUID set")
|
||||
}
|
||||
}
|
||||
|
||||
func TestSetupDiGetSelectedDevice(t *testing.T) {
|
||||
devInfoList, err := SetupDiGetClassDevsEx(&deviceClassNetGUID, "", 0, DIGCF_PRESENT, DevInfo(0), "")
|
||||
if err != nil {
|
||||
t.Errorf("Error calling SetupDiGetClassDevsEx: %s", err.Error())
|
||||
}
|
||||
defer devInfoList.Close()
|
||||
|
||||
for i := 0; true; i++ {
|
||||
data, err := devInfoList.EnumDeviceInfo(i)
|
||||
if err != nil {
|
||||
if errWin, ok := err.(syscall.Errno); ok && errWin == 259 /*ERROR_NO_MORE_ITEMS*/ {
|
||||
break
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
err = devInfoList.SetSelectedDevice(data)
|
||||
if err != nil {
|
||||
t.Errorf("Error calling SetupDiSetSelectedDevice: %s", err.Error())
|
||||
}
|
||||
|
||||
data2, err := devInfoList.GetSelectedDevice()
|
||||
if err != nil {
|
||||
t.Errorf("Error calling SetupDiGetSelectedDevice: %s", err.Error())
|
||||
} else if *data != *data2 {
|
||||
t.Error("SetupDiGetSelectedDevice returned different data than was set by SetupDiSetSelectedDevice")
|
||||
}
|
||||
}
|
||||
|
||||
err = devInfoList.SetSelectedDevice(nil)
|
||||
if err == nil {
|
||||
t.Errorf("SetupDiSetSelectedDevice(nil) should fail")
|
||||
} else {
|
||||
if errWin, ok := err.(syscall.Errno); !ok || errWin != 87 /*ERROR_INVALID_PARAMETER*/ {
|
||||
t.Errorf("SetupDiSetSelectedDevice(nil) should fail with ERROR_INVALID_USER_BUFFER")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestUTF16ToBuf(t *testing.T) {
|
||||
buf := []uint16{0x0123, 0x4567, 0x89ab, 0xcdef}
|
||||
buf2 := UTF16ToBuf(buf)
|
||||
if len(buf)*2 != len(buf2) ||
|
||||
cap(buf)*2 != cap(buf2) ||
|
||||
buf2[0] != 0x23 || buf2[1] != 0x01 ||
|
||||
buf2[2] != 0x67 || buf2[3] != 0x45 ||
|
||||
buf2[4] != 0xab || buf2[5] != 0x89 ||
|
||||
buf2[6] != 0xef || buf2[7] != 0xcd {
|
||||
t.Errorf("SetupDiSetSelectedDevice(nil) should fail with ERROR_INVALID_USER_BUFFER")
|
||||
}
|
||||
}
|
||||
539
tun/wintun/setupapi/types_windows.go
Normal file
539
tun/wintun/setupapi/types_windows.go
Normal file
@@ -0,0 +1,539 @@
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2019 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package setupapi
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
const (
|
||||
MAX_DEVICE_ID_LEN = 200
|
||||
MAX_DEVNODE_ID_LEN = MAX_DEVICE_ID_LEN
|
||||
MAX_GUID_STRING_LEN = 39 // 38 chars + terminator null
|
||||
MAX_CLASS_NAME_LEN = 32
|
||||
MAX_PROFILE_LEN = 80
|
||||
MAX_CONFIG_VALUE = 9999
|
||||
MAX_INSTANCE_VALUE = 9999
|
||||
CONFIGMG_VERSION = 0x0400
|
||||
)
|
||||
|
||||
//
|
||||
// Define maximum string length constants
|
||||
//
|
||||
const (
|
||||
LINE_LEN = 256 // Windows 9x-compatible maximum for displayable strings coming from a device INF.
|
||||
MAX_INF_STRING_LENGTH = 4096 // Actual maximum size of an INF string (including string substitutions).
|
||||
MAX_INF_SECTION_NAME_LENGTH = 255 // For Windows 9x compatibility, INF section names should be constrained to 32 characters.
|
||||
MAX_TITLE_LEN = 60
|
||||
MAX_INSTRUCTION_LEN = 256
|
||||
MAX_LABEL_LEN = 30
|
||||
MAX_SERVICE_NAME_LEN = 256
|
||||
MAX_SUBTITLE_LEN = 256
|
||||
)
|
||||
|
||||
const (
|
||||
// SP_MAX_MACHINENAME_LENGTH defines maximum length of a machine name in the format expected by ConfigMgr32 CM_Connect_Machine (i.e., "\\\\MachineName\0").
|
||||
SP_MAX_MACHINENAME_LENGTH = windows.MAX_PATH + 3
|
||||
)
|
||||
|
||||
// HSPFILEQ is type for setup file queue
|
||||
type HSPFILEQ uintptr
|
||||
|
||||
// DevInfo holds reference to device information set
|
||||
type DevInfo windows.Handle
|
||||
|
||||
// DevInfoData is a device information structure (references a device instance that is a member of a device information set)
|
||||
type DevInfoData struct {
|
||||
size uint32
|
||||
ClassGUID windows.GUID
|
||||
DevInst uint32 // DEVINST handle
|
||||
_ uintptr
|
||||
}
|
||||
|
||||
// DevInfoListDetailData is a structure for detailed information on a device information set (used for SetupDiGetDeviceInfoListDetail which supercedes the functionality of SetupDiGetDeviceInfoListClass).
|
||||
type DevInfoListDetailData struct {
|
||||
size uint32
|
||||
ClassGUID windows.GUID
|
||||
RemoteMachineHandle windows.Handle
|
||||
remoteMachineName [SP_MAX_MACHINENAME_LENGTH]uint16
|
||||
}
|
||||
|
||||
func (data *DevInfoListDetailData) GetRemoteMachineName() string {
|
||||
return windows.UTF16ToString(data.remoteMachineName[:])
|
||||
}
|
||||
|
||||
func (data *DevInfoListDetailData) SetRemoteMachineName(remoteMachineName string) error {
|
||||
str, err := syscall.UTF16FromString(remoteMachineName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
copy(data.remoteMachineName[:], str)
|
||||
return nil
|
||||
}
|
||||
|
||||
// DI_FUNCTION is function type for device installer
|
||||
type DI_FUNCTION uint32
|
||||
|
||||
const (
|
||||
DIF_SELECTDEVICE DI_FUNCTION = 0x00000001
|
||||
DIF_INSTALLDEVICE DI_FUNCTION = 0x00000002
|
||||
DIF_ASSIGNRESOURCES DI_FUNCTION = 0x00000003
|
||||
DIF_PROPERTIES DI_FUNCTION = 0x00000004
|
||||
DIF_REMOVE DI_FUNCTION = 0x00000005
|
||||
DIF_FIRSTTIMESETUP DI_FUNCTION = 0x00000006
|
||||
DIF_FOUNDDEVICE DI_FUNCTION = 0x00000007
|
||||
DIF_SELECTCLASSDRIVERS DI_FUNCTION = 0x00000008
|
||||
DIF_VALIDATECLASSDRIVERS DI_FUNCTION = 0x00000009
|
||||
DIF_INSTALLCLASSDRIVERS DI_FUNCTION = 0x0000000A
|
||||
DIF_CALCDISKSPACE DI_FUNCTION = 0x0000000B
|
||||
DIF_DESTROYPRIVATEDATA DI_FUNCTION = 0x0000000C
|
||||
DIF_VALIDATEDRIVER DI_FUNCTION = 0x0000000D
|
||||
DIF_DETECT DI_FUNCTION = 0x0000000F
|
||||
DIF_INSTALLWIZARD DI_FUNCTION = 0x00000010
|
||||
DIF_DESTROYWIZARDDATA DI_FUNCTION = 0x00000011
|
||||
DIF_PROPERTYCHANGE DI_FUNCTION = 0x00000012
|
||||
DIF_ENABLECLASS DI_FUNCTION = 0x00000013
|
||||
DIF_DETECTVERIFY DI_FUNCTION = 0x00000014
|
||||
DIF_INSTALLDEVICEFILES DI_FUNCTION = 0x00000015
|
||||
DIF_UNREMOVE DI_FUNCTION = 0x00000016
|
||||
DIF_SELECTBESTCOMPATDRV DI_FUNCTION = 0x00000017
|
||||
DIF_ALLOW_INSTALL DI_FUNCTION = 0x00000018
|
||||
DIF_REGISTERDEVICE DI_FUNCTION = 0x00000019
|
||||
DIF_NEWDEVICEWIZARD_PRESELECT DI_FUNCTION = 0x0000001A
|
||||
DIF_NEWDEVICEWIZARD_SELECT DI_FUNCTION = 0x0000001B
|
||||
DIF_NEWDEVICEWIZARD_PREANALYZE DI_FUNCTION = 0x0000001C
|
||||
DIF_NEWDEVICEWIZARD_POSTANALYZE DI_FUNCTION = 0x0000001D
|
||||
DIF_NEWDEVICEWIZARD_FINISHINSTALL DI_FUNCTION = 0x0000001E
|
||||
DIF_INSTALLINTERFACES DI_FUNCTION = 0x00000020
|
||||
DIF_DETECTCANCEL DI_FUNCTION = 0x00000021
|
||||
DIF_REGISTER_COINSTALLERS DI_FUNCTION = 0x00000022
|
||||
DIF_ADDPROPERTYPAGE_ADVANCED DI_FUNCTION = 0x00000023
|
||||
DIF_ADDPROPERTYPAGE_BASIC DI_FUNCTION = 0x00000024
|
||||
DIF_TROUBLESHOOTER DI_FUNCTION = 0x00000026
|
||||
DIF_POWERMESSAGEWAKE DI_FUNCTION = 0x00000027
|
||||
DIF_ADDREMOTEPROPERTYPAGE_ADVANCED DI_FUNCTION = 0x00000028
|
||||
DIF_UPDATEDRIVER_UI DI_FUNCTION = 0x00000029
|
||||
DIF_FINISHINSTALL_ACTION DI_FUNCTION = 0x0000002A
|
||||
)
|
||||
|
||||
// DevInstallParams is device installation parameters structure (associated with a particular device information element, or globally with a device information set)
|
||||
type DevInstallParams struct {
|
||||
size uint32
|
||||
Flags DI_FLAGS
|
||||
FlagsEx DI_FLAGSEX
|
||||
hwndParent uintptr
|
||||
InstallMsgHandler uintptr
|
||||
InstallMsgHandlerContext uintptr
|
||||
FileQueue HSPFILEQ
|
||||
_ uintptr
|
||||
_ uint32
|
||||
driverPath [windows.MAX_PATH]uint16
|
||||
}
|
||||
|
||||
func (params *DevInstallParams) GetDriverPath() string {
|
||||
return windows.UTF16ToString(params.driverPath[:])
|
||||
}
|
||||
|
||||
func (params *DevInstallParams) SetDriverPath(driverPath string) error {
|
||||
str, err := syscall.UTF16FromString(driverPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
copy(params.driverPath[:], str)
|
||||
return nil
|
||||
}
|
||||
|
||||
// DI_FLAGS is SP_DEVINSTALL_PARAMS.Flags values
|
||||
type DI_FLAGS uint32
|
||||
|
||||
const (
|
||||
// Flags for choosing a device
|
||||
DI_SHOWOEM DI_FLAGS = 0x00000001 // support Other... button
|
||||
DI_SHOWCOMPAT DI_FLAGS = 0x00000002 // show compatibility list
|
||||
DI_SHOWCLASS DI_FLAGS = 0x00000004 // show class list
|
||||
DI_SHOWALL DI_FLAGS = 0x00000007 // both class & compat list shown
|
||||
DI_NOVCP DI_FLAGS = 0x00000008 // don't create a new copy queue--use caller-supplied FileQueue
|
||||
DI_DIDCOMPAT DI_FLAGS = 0x00000010 // Searched for compatible devices
|
||||
DI_DIDCLASS DI_FLAGS = 0x00000020 // Searched for class devices
|
||||
DI_AUTOASSIGNRES DI_FLAGS = 0x00000040 // No UI for resources if possible
|
||||
|
||||
// Flags returned by DiInstallDevice to indicate need to reboot/restart
|
||||
DI_NEEDRESTART DI_FLAGS = 0x00000080 // Reboot required to take effect
|
||||
DI_NEEDREBOOT DI_FLAGS = 0x00000100 // ""
|
||||
|
||||
// Flags for device installation
|
||||
DI_NOBROWSE DI_FLAGS = 0x00000200 // no Browse... in InsertDisk
|
||||
|
||||
// Flags set by DiBuildDriverInfoList
|
||||
DI_MULTMFGS DI_FLAGS = 0x00000400 // Set if multiple manufacturers in class driver list
|
||||
|
||||
// Flag indicates that device is disabled
|
||||
DI_DISABLED DI_FLAGS = 0x00000800 // Set if device disabled
|
||||
|
||||
// Flags for Device/Class Properties
|
||||
DI_GENERALPAGE_ADDED DI_FLAGS = 0x00001000
|
||||
DI_RESOURCEPAGE_ADDED DI_FLAGS = 0x00002000
|
||||
|
||||
// Flag to indicate the setting properties for this Device (or class) caused a change so the Dev Mgr UI probably needs to be updated.
|
||||
DI_PROPERTIES_CHANGE DI_FLAGS = 0x00004000
|
||||
|
||||
// Flag to indicate that the sorting from the INF file should be used.
|
||||
DI_INF_IS_SORTED DI_FLAGS = 0x00008000
|
||||
|
||||
// Flag to indicate that only the the INF specified by SP_DEVINSTALL_PARAMS.DriverPath should be searched.
|
||||
DI_ENUMSINGLEINF DI_FLAGS = 0x00010000
|
||||
|
||||
// Flag that prevents ConfigMgr from removing/re-enumerating devices during device
|
||||
// registration, installation, and deletion.
|
||||
DI_DONOTCALLCONFIGMG DI_FLAGS = 0x00020000
|
||||
|
||||
// The following flag can be used to install a device disabled
|
||||
DI_INSTALLDISABLED DI_FLAGS = 0x00040000
|
||||
|
||||
// Flag that causes SetupDiBuildDriverInfoList to build a device's compatible driver
|
||||
// list from its existing class driver list, instead of the normal INF search.
|
||||
DI_COMPAT_FROM_CLASS DI_FLAGS = 0x00080000
|
||||
|
||||
// This flag is set if the Class Install params should be used.
|
||||
DI_CLASSINSTALLPARAMS DI_FLAGS = 0x00100000
|
||||
|
||||
// This flag is set if the caller of DiCallClassInstaller does NOT want the internal default action performed if the Class installer returns ERROR_DI_DO_DEFAULT.
|
||||
DI_NODI_DEFAULTACTION DI_FLAGS = 0x00200000
|
||||
|
||||
// Flags for device installation
|
||||
DI_QUIETINSTALL DI_FLAGS = 0x00800000 // don't confuse the user with questions or excess info
|
||||
DI_NOFILECOPY DI_FLAGS = 0x01000000 // No file Copy necessary
|
||||
DI_FORCECOPY DI_FLAGS = 0x02000000 // Force files to be copied from install path
|
||||
DI_DRIVERPAGE_ADDED DI_FLAGS = 0x04000000 // Prop provider added Driver page.
|
||||
DI_USECI_SELECTSTRINGS DI_FLAGS = 0x08000000 // Use Class Installer Provided strings in the Select Device Dlg
|
||||
DI_OVERRIDE_INFFLAGS DI_FLAGS = 0x10000000 // Override INF flags
|
||||
DI_PROPS_NOCHANGEUSAGE DI_FLAGS = 0x20000000 // No Enable/Disable in General Props
|
||||
|
||||
DI_NOSELECTICONS DI_FLAGS = 0x40000000 // No small icons in select device dialogs
|
||||
|
||||
DI_NOWRITE_IDS DI_FLAGS = 0x80000000 // Don't write HW & Compat IDs on install
|
||||
)
|
||||
|
||||
// DI_FLAGSEX is SP_DEVINSTALL_PARAMS.FlagsEx values
|
||||
type DI_FLAGSEX uint32
|
||||
|
||||
const (
|
||||
DI_FLAGSEX_CI_FAILED DI_FLAGSEX = 0x00000004 // Failed to Load/Call class installer
|
||||
DI_FLAGSEX_FINISHINSTALL_ACTION DI_FLAGSEX = 0x00000008 // Class/co-installer wants to get a DIF_FINISH_INSTALL action in client context.
|
||||
DI_FLAGSEX_DIDINFOLIST DI_FLAGSEX = 0x00000010 // Did the Class Info List
|
||||
DI_FLAGSEX_DIDCOMPATINFO DI_FLAGSEX = 0x00000020 // Did the Compat Info List
|
||||
DI_FLAGSEX_FILTERCLASSES DI_FLAGSEX = 0x00000040
|
||||
DI_FLAGSEX_SETFAILEDINSTALL DI_FLAGSEX = 0x00000080
|
||||
DI_FLAGSEX_DEVICECHANGE DI_FLAGSEX = 0x00000100
|
||||
DI_FLAGSEX_ALWAYSWRITEIDS DI_FLAGSEX = 0x00000200
|
||||
DI_FLAGSEX_PROPCHANGE_PENDING DI_FLAGSEX = 0x00000400 // One or more device property sheets have had changes made to them, and need to have a DIF_PROPERTYCHANGE occur.
|
||||
DI_FLAGSEX_ALLOWEXCLUDEDDRVS DI_FLAGSEX = 0x00000800
|
||||
DI_FLAGSEX_NOUIONQUERYREMOVE DI_FLAGSEX = 0x00001000
|
||||
DI_FLAGSEX_USECLASSFORCOMPAT DI_FLAGSEX = 0x00002000 // Use the device's class when building compat drv list. (Ignored if DI_COMPAT_FROM_CLASS flag is specified.)
|
||||
DI_FLAGSEX_NO_DRVREG_MODIFY DI_FLAGSEX = 0x00008000 // Don't run AddReg and DelReg for device's software (driver) key.
|
||||
DI_FLAGSEX_IN_SYSTEM_SETUP DI_FLAGSEX = 0x00010000 // Installation is occurring during initial system setup.
|
||||
DI_FLAGSEX_INET_DRIVER DI_FLAGSEX = 0x00020000 // Driver came from Windows Update
|
||||
DI_FLAGSEX_APPENDDRIVERLIST DI_FLAGSEX = 0x00040000 // Cause SetupDiBuildDriverInfoList to append a new driver list to an existing list.
|
||||
DI_FLAGSEX_PREINSTALLBACKUP DI_FLAGSEX = 0x00080000 // not used
|
||||
DI_FLAGSEX_BACKUPONREPLACE DI_FLAGSEX = 0x00100000 // not used
|
||||
DI_FLAGSEX_DRIVERLIST_FROM_URL DI_FLAGSEX = 0x00200000 // build driver list from INF(s) retrieved from URL specified in SP_DEVINSTALL_PARAMS.DriverPath (empty string means Windows Update website)
|
||||
DI_FLAGSEX_EXCLUDE_OLD_INET_DRIVERS DI_FLAGSEX = 0x00800000 // Don't include old Internet drivers when building a driver list. Ignored on Windows Vista and later.
|
||||
DI_FLAGSEX_POWERPAGE_ADDED DI_FLAGSEX = 0x01000000 // class installer added their own power page
|
||||
DI_FLAGSEX_FILTERSIMILARDRIVERS DI_FLAGSEX = 0x02000000 // only include similar drivers in class list
|
||||
DI_FLAGSEX_INSTALLEDDRIVER DI_FLAGSEX = 0x04000000 // only add the installed driver to the class or compat driver list. Used in calls to SetupDiBuildDriverInfoList
|
||||
DI_FLAGSEX_NO_CLASSLIST_NODE_MERGE DI_FLAGSEX = 0x08000000 // Don't remove identical driver nodes from the class list
|
||||
DI_FLAGSEX_ALTPLATFORM_DRVSEARCH DI_FLAGSEX = 0x10000000 // Build driver list based on alternate platform information specified in associated file queue
|
||||
DI_FLAGSEX_RESTART_DEVICE_ONLY DI_FLAGSEX = 0x20000000 // only restart the device drivers are being installed on as opposed to restarting all devices using those drivers.
|
||||
DI_FLAGSEX_RECURSIVESEARCH DI_FLAGSEX = 0x40000000 // Tell SetupDiBuildDriverInfoList to do a recursive search
|
||||
DI_FLAGSEX_SEARCH_PUBLISHED_INFS DI_FLAGSEX = 0x80000000 // Tell SetupDiBuildDriverInfoList to do a "published INF" search
|
||||
)
|
||||
|
||||
// ClassInstallHeader is the first member of any class install parameters structure. It contains the device installation request code that defines the format of the rest of the install parameters structure.
|
||||
type ClassInstallHeader struct {
|
||||
size uint32
|
||||
InstallFunction DI_FUNCTION
|
||||
}
|
||||
|
||||
func MakeClassInstallHeader(installFunction DI_FUNCTION) *ClassInstallHeader {
|
||||
hdr := &ClassInstallHeader{InstallFunction: installFunction}
|
||||
hdr.size = uint32(unsafe.Sizeof(*hdr))
|
||||
return hdr
|
||||
}
|
||||
|
||||
// DICS_FLAG specifies the scope of a device property change
|
||||
type DICS_FLAG uint32
|
||||
|
||||
const (
|
||||
DICS_FLAG_GLOBAL DICS_FLAG = 0x00000001 // make change in all hardware profiles
|
||||
DICS_FLAG_CONFIGSPECIFIC DICS_FLAG = 0x00000002 // make change in specified profile only
|
||||
DICS_FLAG_CONFIGGENERAL DICS_FLAG = 0x00000004 // 1 or more hardware profile-specific changes to follow
|
||||
)
|
||||
|
||||
// DI_REMOVEDEVICE specifies the scope of the device removal
|
||||
type DI_REMOVEDEVICE uint32
|
||||
|
||||
const (
|
||||
DI_REMOVEDEVICE_GLOBAL DI_REMOVEDEVICE = 0x00000001 // Make this change in all hardware profiles. Remove information about the device from the registry.
|
||||
DI_REMOVEDEVICE_CONFIGSPECIFIC DI_REMOVEDEVICE = 0x00000002 // Make this change to only the hardware profile specified by HwProfile. this flag only applies to root-enumerated devices. When Windows removes the device from the last hardware profile in which it was configured, Windows performs a global removal.
|
||||
)
|
||||
|
||||
// RemoveDeviceParams is a structure corresponding to a DIF_REMOVE install function.
|
||||
type RemoveDeviceParams struct {
|
||||
ClassInstallHeader ClassInstallHeader
|
||||
Scope DI_REMOVEDEVICE
|
||||
HwProfile uint32
|
||||
}
|
||||
|
||||
// DrvInfoData is driver information structure (member of a driver info list that may be associated with a particular device instance, or (globally) with a device information set)
|
||||
type DrvInfoData struct {
|
||||
size uint32
|
||||
DriverType uint32
|
||||
_ uintptr
|
||||
description [LINE_LEN]uint16
|
||||
mfgName [LINE_LEN]uint16
|
||||
providerName [LINE_LEN]uint16
|
||||
DriverDate windows.Filetime
|
||||
DriverVersion uint64
|
||||
}
|
||||
|
||||
func (data *DrvInfoData) GetDescription() string {
|
||||
return windows.UTF16ToString(data.description[:])
|
||||
}
|
||||
|
||||
func (data *DrvInfoData) SetDescription(description string) error {
|
||||
str, err := syscall.UTF16FromString(description)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
copy(data.description[:], str)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (data *DrvInfoData) GetMfgName() string {
|
||||
return windows.UTF16ToString(data.mfgName[:])
|
||||
}
|
||||
|
||||
func (data *DrvInfoData) SetMfgName(mfgName string) error {
|
||||
str, err := syscall.UTF16FromString(mfgName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
copy(data.mfgName[:], str)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (data *DrvInfoData) GetProviderName() string {
|
||||
return windows.UTF16ToString(data.providerName[:])
|
||||
}
|
||||
|
||||
func (data *DrvInfoData) SetProviderName(providerName string) error {
|
||||
str, err := syscall.UTF16FromString(providerName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
copy(data.providerName[:], str)
|
||||
return nil
|
||||
}
|
||||
|
||||
// IsNewer method returns true if DrvInfoData date and version is newer than supplied parameters.
|
||||
func (data *DrvInfoData) IsNewer(driverDate windows.Filetime, driverVersion uint64) bool {
|
||||
if data.DriverDate.HighDateTime > driverDate.HighDateTime {
|
||||
return true
|
||||
}
|
||||
if data.DriverDate.HighDateTime < driverDate.HighDateTime {
|
||||
return false
|
||||
}
|
||||
|
||||
if data.DriverDate.LowDateTime > driverDate.LowDateTime {
|
||||
return true
|
||||
}
|
||||
if data.DriverDate.LowDateTime < driverDate.LowDateTime {
|
||||
return false
|
||||
}
|
||||
|
||||
if data.DriverVersion > driverVersion {
|
||||
return true
|
||||
}
|
||||
if data.DriverVersion < driverVersion {
|
||||
return false
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// DrvInfoDetailData is driver information details structure (provides detailed information about a particular driver information structure)
|
||||
type DrvInfoDetailData struct {
|
||||
size uint32 // On input, this must be exactly the sizeof(DrvInfoDetailData). On output, we set this member to the actual size of structure data.
|
||||
InfDate windows.Filetime
|
||||
compatIDsOffset uint32
|
||||
compatIDsLength uint32
|
||||
_ uintptr
|
||||
sectionName [LINE_LEN]uint16
|
||||
infFileName [windows.MAX_PATH]uint16
|
||||
drvDescription [LINE_LEN]uint16
|
||||
hardwareID [1]uint16
|
||||
}
|
||||
|
||||
func (data *DrvInfoDetailData) GetSectionName() string {
|
||||
return windows.UTF16ToString(data.sectionName[:])
|
||||
}
|
||||
|
||||
func (data *DrvInfoDetailData) GetInfFileName() string {
|
||||
return windows.UTF16ToString(data.infFileName[:])
|
||||
}
|
||||
|
||||
func (data *DrvInfoDetailData) GetDrvDescription() string {
|
||||
return windows.UTF16ToString(data.drvDescription[:])
|
||||
}
|
||||
|
||||
func (data *DrvInfoDetailData) GetHardwareID() string {
|
||||
if data.compatIDsOffset > 1 {
|
||||
bufW := data.getBuf()
|
||||
return windows.UTF16ToString(bufW[:wcslen(bufW)])
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
func (data *DrvInfoDetailData) GetCompatIDs() []string {
|
||||
a := make([]string, 0)
|
||||
|
||||
if data.compatIDsLength > 0 {
|
||||
bufW := data.getBuf()
|
||||
bufW = bufW[data.compatIDsOffset : data.compatIDsOffset+data.compatIDsLength]
|
||||
for i := 0; i < len(bufW); {
|
||||
j := i + wcslen(bufW[i:])
|
||||
if i < j {
|
||||
a = append(a, windows.UTF16ToString(bufW[i:j]))
|
||||
}
|
||||
i = j + 1
|
||||
}
|
||||
}
|
||||
|
||||
return a
|
||||
}
|
||||
|
||||
func (data *DrvInfoDetailData) getBuf() []uint16 {
|
||||
len := (data.size - uint32(unsafe.Offsetof(data.hardwareID))) / 2
|
||||
sl := struct {
|
||||
addr *uint16
|
||||
len int
|
||||
cap int
|
||||
}{&data.hardwareID[0], int(len), int(len)}
|
||||
return *(*[]uint16)(unsafe.Pointer(&sl))
|
||||
}
|
||||
|
||||
// IsCompatible method tests if given hardware ID matches the driver or is listed on the compatible ID list.
|
||||
func (data *DrvInfoDetailData) IsCompatible(hwid string) bool {
|
||||
hwidLC := strings.ToLower(hwid)
|
||||
if strings.ToLower(data.GetHardwareID()) == hwidLC {
|
||||
return true
|
||||
}
|
||||
a := data.GetCompatIDs()
|
||||
for i := range a {
|
||||
if strings.ToLower(a[i]) == hwidLC {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// DICD flags control SetupDiCreateDeviceInfo
|
||||
type DICD uint32
|
||||
|
||||
const (
|
||||
DICD_GENERATE_ID DICD = 0x00000001
|
||||
DICD_INHERIT_CLASSDRVS DICD = 0x00000002
|
||||
)
|
||||
|
||||
//
|
||||
// SPDIT flags to distinguish between class drivers and
|
||||
// device drivers.
|
||||
// (Passed in 'DriverType' parameter of driver information list APIs)
|
||||
//
|
||||
type SPDIT uint32
|
||||
|
||||
const (
|
||||
SPDIT_NODRIVER SPDIT = 0x00000000
|
||||
SPDIT_CLASSDRIVER SPDIT = 0x00000001
|
||||
SPDIT_COMPATDRIVER SPDIT = 0x00000002
|
||||
)
|
||||
|
||||
// DIGCF flags control what is included in the device information set built by SetupDiGetClassDevs
|
||||
type DIGCF uint32
|
||||
|
||||
const (
|
||||
DIGCF_DEFAULT DIGCF = 0x00000001 // only valid with DIGCF_DEVICEINTERFACE
|
||||
DIGCF_PRESENT DIGCF = 0x00000002
|
||||
DIGCF_ALLCLASSES DIGCF = 0x00000004
|
||||
DIGCF_PROFILE DIGCF = 0x00000008
|
||||
DIGCF_DEVICEINTERFACE DIGCF = 0x00000010
|
||||
)
|
||||
|
||||
// DIREG specifies values for SetupDiCreateDevRegKey, SetupDiOpenDevRegKey, and SetupDiDeleteDevRegKey.
|
||||
type DIREG uint32
|
||||
|
||||
const (
|
||||
DIREG_DEV DIREG = 0x00000001 // Open/Create/Delete device key
|
||||
DIREG_DRV DIREG = 0x00000002 // Open/Create/Delete driver key
|
||||
DIREG_BOTH DIREG = 0x00000004 // Delete both driver and Device key
|
||||
)
|
||||
|
||||
//
|
||||
// SPDRP specifies device registry property codes
|
||||
// (Codes marked as read-only (R) may only be used for
|
||||
// SetupDiGetDeviceRegistryProperty)
|
||||
//
|
||||
// These values should cover the same set of registry properties
|
||||
// as defined by the CM_DRP codes in cfgmgr32.h.
|
||||
//
|
||||
// Note that SPDRP codes are zero based while CM_DRP codes are one based!
|
||||
//
|
||||
type SPDRP uint32
|
||||
|
||||
const (
|
||||
SPDRP_DEVICEDESC SPDRP = 0x00000000 // DeviceDesc (R/W)
|
||||
SPDRP_HARDWAREID SPDRP = 0x00000001 // HardwareID (R/W)
|
||||
SPDRP_COMPATIBLEIDS SPDRP = 0x00000002 // CompatibleIDs (R/W)
|
||||
SPDRP_SERVICE SPDRP = 0x00000004 // Service (R/W)
|
||||
SPDRP_CLASS SPDRP = 0x00000007 // Class (R--tied to ClassGUID)
|
||||
SPDRP_CLASSGUID SPDRP = 0x00000008 // ClassGUID (R/W)
|
||||
SPDRP_DRIVER SPDRP = 0x00000009 // Driver (R/W)
|
||||
SPDRP_CONFIGFLAGS SPDRP = 0x0000000A // ConfigFlags (R/W)
|
||||
SPDRP_MFG SPDRP = 0x0000000B // Mfg (R/W)
|
||||
SPDRP_FRIENDLYNAME SPDRP = 0x0000000C // FriendlyName (R/W)
|
||||
SPDRP_LOCATION_INFORMATION SPDRP = 0x0000000D // LocationInformation (R/W)
|
||||
SPDRP_PHYSICAL_DEVICE_OBJECT_NAME SPDRP = 0x0000000E // PhysicalDeviceObjectName (R)
|
||||
SPDRP_CAPABILITIES SPDRP = 0x0000000F // Capabilities (R)
|
||||
SPDRP_UI_NUMBER SPDRP = 0x00000010 // UiNumber (R)
|
||||
SPDRP_UPPERFILTERS SPDRP = 0x00000011 // UpperFilters (R/W)
|
||||
SPDRP_LOWERFILTERS SPDRP = 0x00000012 // LowerFilters (R/W)
|
||||
SPDRP_BUSTYPEGUID SPDRP = 0x00000013 // BusTypeGUID (R)
|
||||
SPDRP_LEGACYBUSTYPE SPDRP = 0x00000014 // LegacyBusType (R)
|
||||
SPDRP_BUSNUMBER SPDRP = 0x00000015 // BusNumber (R)
|
||||
SPDRP_ENUMERATOR_NAME SPDRP = 0x00000016 // Enumerator Name (R)
|
||||
SPDRP_SECURITY SPDRP = 0x00000017 // Security (R/W, binary form)
|
||||
SPDRP_SECURITY_SDS SPDRP = 0x00000018 // Security (W, SDS form)
|
||||
SPDRP_DEVTYPE SPDRP = 0x00000019 // Device Type (R/W)
|
||||
SPDRP_EXCLUSIVE SPDRP = 0x0000001A // Device is exclusive-access (R/W)
|
||||
SPDRP_CHARACTERISTICS SPDRP = 0x0000001B // Device Characteristics (R/W)
|
||||
SPDRP_ADDRESS SPDRP = 0x0000001C // Device Address (R)
|
||||
SPDRP_UI_NUMBER_DESC_FORMAT SPDRP = 0x0000001D // UiNumberDescFormat (R/W)
|
||||
SPDRP_DEVICE_POWER_DATA SPDRP = 0x0000001E // Device Power Data (R)
|
||||
SPDRP_REMOVAL_POLICY SPDRP = 0x0000001F // Removal Policy (R)
|
||||
SPDRP_REMOVAL_POLICY_HW_DEFAULT SPDRP = 0x00000020 // Hardware Removal Policy (R)
|
||||
SPDRP_REMOVAL_POLICY_OVERRIDE SPDRP = 0x00000021 // Removal Policy Override (RW)
|
||||
SPDRP_INSTALL_STATE SPDRP = 0x00000022 // Device Install State (R)
|
||||
SPDRP_LOCATION_PATHS SPDRP = 0x00000023 // Device Location Paths (R)
|
||||
SPDRP_BASE_CONTAINERID SPDRP = 0x00000024 // Base ContainerID (R)
|
||||
|
||||
SPDRP_MAXIMUM_PROPERTY SPDRP = 0x00000025 // Upper bound on ordinals
|
||||
)
|
||||
370
tun/wintun/setupapi/zsetupapi_windows.go
Normal file
370
tun/wintun/setupapi/zsetupapi_windows.go
Normal file
@@ -0,0 +1,370 @@
|
||||
// Code generated by 'go generate'; DO NOT EDIT.
|
||||
|
||||
package setupapi
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
"unsafe"
|
||||
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
var _ unsafe.Pointer
|
||||
|
||||
// Do the interface allocations only once for common
|
||||
// Errno values.
|
||||
const (
|
||||
errnoERROR_IO_PENDING = 997
|
||||
)
|
||||
|
||||
var (
|
||||
errERROR_IO_PENDING error = syscall.Errno(errnoERROR_IO_PENDING)
|
||||
)
|
||||
|
||||
// errnoErr returns common boxed Errno values, to prevent
|
||||
// allocations at runtime.
|
||||
func errnoErr(e syscall.Errno) error {
|
||||
switch e {
|
||||
case 0:
|
||||
return nil
|
||||
case errnoERROR_IO_PENDING:
|
||||
return errERROR_IO_PENDING
|
||||
}
|
||||
// TODO: add more here, after collecting data on the common
|
||||
// error values see on Windows. (perhaps when running
|
||||
// all.bat?)
|
||||
return e
|
||||
}
|
||||
|
||||
var (
|
||||
modsetupapi = windows.NewLazySystemDLL("setupapi.dll")
|
||||
|
||||
procSetupDiCreateDeviceInfoListExW = modsetupapi.NewProc("SetupDiCreateDeviceInfoListExW")
|
||||
procSetupDiGetDeviceInfoListDetailW = modsetupapi.NewProc("SetupDiGetDeviceInfoListDetailW")
|
||||
procSetupDiCreateDeviceInfoW = modsetupapi.NewProc("SetupDiCreateDeviceInfoW")
|
||||
procSetupDiEnumDeviceInfo = modsetupapi.NewProc("SetupDiEnumDeviceInfo")
|
||||
procSetupDiDestroyDeviceInfoList = modsetupapi.NewProc("SetupDiDestroyDeviceInfoList")
|
||||
procSetupDiBuildDriverInfoList = modsetupapi.NewProc("SetupDiBuildDriverInfoList")
|
||||
procSetupDiCancelDriverInfoSearch = modsetupapi.NewProc("SetupDiCancelDriverInfoSearch")
|
||||
procSetupDiEnumDriverInfoW = modsetupapi.NewProc("SetupDiEnumDriverInfoW")
|
||||
procSetupDiGetSelectedDriverW = modsetupapi.NewProc("SetupDiGetSelectedDriverW")
|
||||
procSetupDiSetSelectedDriverW = modsetupapi.NewProc("SetupDiSetSelectedDriverW")
|
||||
procSetupDiGetDriverInfoDetailW = modsetupapi.NewProc("SetupDiGetDriverInfoDetailW")
|
||||
procSetupDiDestroyDriverInfoList = modsetupapi.NewProc("SetupDiDestroyDriverInfoList")
|
||||
procSetupDiGetClassDevsExW = modsetupapi.NewProc("SetupDiGetClassDevsExW")
|
||||
procSetupDiCallClassInstaller = modsetupapi.NewProc("SetupDiCallClassInstaller")
|
||||
procSetupDiOpenDevRegKey = modsetupapi.NewProc("SetupDiOpenDevRegKey")
|
||||
procSetupDiGetDeviceRegistryPropertyW = modsetupapi.NewProc("SetupDiGetDeviceRegistryPropertyW")
|
||||
procSetupDiSetDeviceRegistryPropertyW = modsetupapi.NewProc("SetupDiSetDeviceRegistryPropertyW")
|
||||
procSetupDiGetDeviceInstallParamsW = modsetupapi.NewProc("SetupDiGetDeviceInstallParamsW")
|
||||
procSetupDiGetClassInstallParamsW = modsetupapi.NewProc("SetupDiGetClassInstallParamsW")
|
||||
procSetupDiSetDeviceInstallParamsW = modsetupapi.NewProc("SetupDiSetDeviceInstallParamsW")
|
||||
procSetupDiSetClassInstallParamsW = modsetupapi.NewProc("SetupDiSetClassInstallParamsW")
|
||||
procSetupDiClassNameFromGuidExW = modsetupapi.NewProc("SetupDiClassNameFromGuidExW")
|
||||
procSetupDiClassGuidsFromNameExW = modsetupapi.NewProc("SetupDiClassGuidsFromNameExW")
|
||||
procSetupDiGetSelectedDevice = modsetupapi.NewProc("SetupDiGetSelectedDevice")
|
||||
procSetupDiSetSelectedDevice = modsetupapi.NewProc("SetupDiSetSelectedDevice")
|
||||
)
|
||||
|
||||
func setupDiCreateDeviceInfoListEx(classGUID *windows.GUID, hwndParent uintptr, machineName *uint16, reserved uintptr) (handle DevInfo, err error) {
|
||||
r0, _, e1 := syscall.Syscall6(procSetupDiCreateDeviceInfoListExW.Addr(), 4, uintptr(unsafe.Pointer(classGUID)), uintptr(hwndParent), uintptr(unsafe.Pointer(machineName)), uintptr(reserved), 0, 0)
|
||||
handle = DevInfo(r0)
|
||||
if handle == DevInfo(windows.InvalidHandle) {
|
||||
if e1 != 0 {
|
||||
err = errnoErr(e1)
|
||||
} else {
|
||||
err = syscall.EINVAL
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func setupDiGetDeviceInfoListDetail(deviceInfoSet DevInfo, deviceInfoSetDetailData *DevInfoListDetailData) (err error) {
|
||||
r1, _, e1 := syscall.Syscall(procSetupDiGetDeviceInfoListDetailW.Addr(), 2, uintptr(deviceInfoSet), uintptr(unsafe.Pointer(deviceInfoSetDetailData)), 0)
|
||||
if r1 == 0 {
|
||||
if e1 != 0 {
|
||||
err = errnoErr(e1)
|
||||
} else {
|
||||
err = syscall.EINVAL
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func setupDiCreateDeviceInfo(deviceInfoSet DevInfo, DeviceName *uint16, classGUID *windows.GUID, DeviceDescription *uint16, hwndParent uintptr, CreationFlags DICD, deviceInfoData *DevInfoData) (err error) {
|
||||
r1, _, e1 := syscall.Syscall9(procSetupDiCreateDeviceInfoW.Addr(), 7, uintptr(deviceInfoSet), uintptr(unsafe.Pointer(DeviceName)), uintptr(unsafe.Pointer(classGUID)), uintptr(unsafe.Pointer(DeviceDescription)), uintptr(hwndParent), uintptr(CreationFlags), uintptr(unsafe.Pointer(deviceInfoData)), 0, 0)
|
||||
if r1 == 0 {
|
||||
if e1 != 0 {
|
||||
err = errnoErr(e1)
|
||||
} else {
|
||||
err = syscall.EINVAL
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func setupDiEnumDeviceInfo(deviceInfoSet DevInfo, memberIndex uint32, deviceInfoData *DevInfoData) (err error) {
|
||||
r1, _, e1 := syscall.Syscall(procSetupDiEnumDeviceInfo.Addr(), 3, uintptr(deviceInfoSet), uintptr(memberIndex), uintptr(unsafe.Pointer(deviceInfoData)))
|
||||
if r1 == 0 {
|
||||
if e1 != 0 {
|
||||
err = errnoErr(e1)
|
||||
} else {
|
||||
err = syscall.EINVAL
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func SetupDiDestroyDeviceInfoList(deviceInfoSet DevInfo) (err error) {
|
||||
r1, _, e1 := syscall.Syscall(procSetupDiDestroyDeviceInfoList.Addr(), 1, uintptr(deviceInfoSet), 0, 0)
|
||||
if r1 == 0 {
|
||||
if e1 != 0 {
|
||||
err = errnoErr(e1)
|
||||
} else {
|
||||
err = syscall.EINVAL
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func SetupDiBuildDriverInfoList(deviceInfoSet DevInfo, deviceInfoData *DevInfoData, driverType SPDIT) (err error) {
|
||||
r1, _, e1 := syscall.Syscall(procSetupDiBuildDriverInfoList.Addr(), 3, uintptr(deviceInfoSet), uintptr(unsafe.Pointer(deviceInfoData)), uintptr(driverType))
|
||||
if r1 == 0 {
|
||||
if e1 != 0 {
|
||||
err = errnoErr(e1)
|
||||
} else {
|
||||
err = syscall.EINVAL
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func SetupDiCancelDriverInfoSearch(deviceInfoSet DevInfo) (err error) {
|
||||
r1, _, e1 := syscall.Syscall(procSetupDiCancelDriverInfoSearch.Addr(), 1, uintptr(deviceInfoSet), 0, 0)
|
||||
if r1 == 0 {
|
||||
if e1 != 0 {
|
||||
err = errnoErr(e1)
|
||||
} else {
|
||||
err = syscall.EINVAL
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func setupDiEnumDriverInfo(deviceInfoSet DevInfo, deviceInfoData *DevInfoData, driverType SPDIT, memberIndex uint32, driverInfoData *DrvInfoData) (err error) {
|
||||
r1, _, e1 := syscall.Syscall6(procSetupDiEnumDriverInfoW.Addr(), 5, uintptr(deviceInfoSet), uintptr(unsafe.Pointer(deviceInfoData)), uintptr(driverType), uintptr(memberIndex), uintptr(unsafe.Pointer(driverInfoData)), 0)
|
||||
if r1 == 0 {
|
||||
if e1 != 0 {
|
||||
err = errnoErr(e1)
|
||||
} else {
|
||||
err = syscall.EINVAL
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func setupDiGetSelectedDriver(deviceInfoSet DevInfo, deviceInfoData *DevInfoData, driverInfoData *DrvInfoData) (err error) {
|
||||
r1, _, e1 := syscall.Syscall(procSetupDiGetSelectedDriverW.Addr(), 3, uintptr(deviceInfoSet), uintptr(unsafe.Pointer(deviceInfoData)), uintptr(unsafe.Pointer(driverInfoData)))
|
||||
if r1 == 0 {
|
||||
if e1 != 0 {
|
||||
err = errnoErr(e1)
|
||||
} else {
|
||||
err = syscall.EINVAL
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func SetupDiSetSelectedDriver(deviceInfoSet DevInfo, deviceInfoData *DevInfoData, driverInfoData *DrvInfoData) (err error) {
|
||||
r1, _, e1 := syscall.Syscall(procSetupDiSetSelectedDriverW.Addr(), 3, uintptr(deviceInfoSet), uintptr(unsafe.Pointer(deviceInfoData)), uintptr(unsafe.Pointer(driverInfoData)))
|
||||
if r1 == 0 {
|
||||
if e1 != 0 {
|
||||
err = errnoErr(e1)
|
||||
} else {
|
||||
err = syscall.EINVAL
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func setupDiGetDriverInfoDetail(deviceInfoSet DevInfo, deviceInfoData *DevInfoData, driverInfoData *DrvInfoData, driverInfoDetailData *DrvInfoDetailData, driverInfoDetailDataSize uint32, requiredSize *uint32) (err error) {
|
||||
r1, _, e1 := syscall.Syscall6(procSetupDiGetDriverInfoDetailW.Addr(), 6, uintptr(deviceInfoSet), uintptr(unsafe.Pointer(deviceInfoData)), uintptr(unsafe.Pointer(driverInfoData)), uintptr(unsafe.Pointer(driverInfoDetailData)), uintptr(driverInfoDetailDataSize), uintptr(unsafe.Pointer(requiredSize)))
|
||||
if r1 == 0 {
|
||||
if e1 != 0 {
|
||||
err = errnoErr(e1)
|
||||
} else {
|
||||
err = syscall.EINVAL
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func SetupDiDestroyDriverInfoList(deviceInfoSet DevInfo, deviceInfoData *DevInfoData, driverType SPDIT) (err error) {
|
||||
r1, _, e1 := syscall.Syscall(procSetupDiDestroyDriverInfoList.Addr(), 3, uintptr(deviceInfoSet), uintptr(unsafe.Pointer(deviceInfoData)), uintptr(driverType))
|
||||
if r1 == 0 {
|
||||
if e1 != 0 {
|
||||
err = errnoErr(e1)
|
||||
} else {
|
||||
err = syscall.EINVAL
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func setupDiGetClassDevsEx(classGUID *windows.GUID, Enumerator *uint16, hwndParent uintptr, Flags DIGCF, deviceInfoSet DevInfo, machineName *uint16, reserved uintptr) (handle DevInfo, err error) {
|
||||
r0, _, e1 := syscall.Syscall9(procSetupDiGetClassDevsExW.Addr(), 7, uintptr(unsafe.Pointer(classGUID)), uintptr(unsafe.Pointer(Enumerator)), uintptr(hwndParent), uintptr(Flags), uintptr(deviceInfoSet), uintptr(unsafe.Pointer(machineName)), uintptr(reserved), 0, 0)
|
||||
handle = DevInfo(r0)
|
||||
if handle == DevInfo(windows.InvalidHandle) {
|
||||
if e1 != 0 {
|
||||
err = errnoErr(e1)
|
||||
} else {
|
||||
err = syscall.EINVAL
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func SetupDiCallClassInstaller(installFunction DI_FUNCTION, deviceInfoSet DevInfo, deviceInfoData *DevInfoData) (err error) {
|
||||
r1, _, e1 := syscall.Syscall(procSetupDiCallClassInstaller.Addr(), 3, uintptr(installFunction), uintptr(deviceInfoSet), uintptr(unsafe.Pointer(deviceInfoData)))
|
||||
if r1 == 0 {
|
||||
if e1 != 0 {
|
||||
err = errnoErr(e1)
|
||||
} else {
|
||||
err = syscall.EINVAL
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func setupDiOpenDevRegKey(deviceInfoSet DevInfo, deviceInfoData *DevInfoData, Scope DICS_FLAG, HwProfile uint32, KeyType DIREG, samDesired uint32) (key windows.Handle, err error) {
|
||||
r0, _, e1 := syscall.Syscall6(procSetupDiOpenDevRegKey.Addr(), 6, uintptr(deviceInfoSet), uintptr(unsafe.Pointer(deviceInfoData)), uintptr(Scope), uintptr(HwProfile), uintptr(KeyType), uintptr(samDesired))
|
||||
key = windows.Handle(r0)
|
||||
if key == windows.InvalidHandle {
|
||||
if e1 != 0 {
|
||||
err = errnoErr(e1)
|
||||
} else {
|
||||
err = syscall.EINVAL
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func setupDiGetDeviceRegistryProperty(deviceInfoSet DevInfo, deviceInfoData *DevInfoData, property SPDRP, propertyRegDataType *uint32, propertyBuffer *byte, propertyBufferSize uint32, requiredSize *uint32) (err error) {
|
||||
r1, _, e1 := syscall.Syscall9(procSetupDiGetDeviceRegistryPropertyW.Addr(), 7, uintptr(deviceInfoSet), uintptr(unsafe.Pointer(deviceInfoData)), uintptr(property), uintptr(unsafe.Pointer(propertyRegDataType)), uintptr(unsafe.Pointer(propertyBuffer)), uintptr(propertyBufferSize), uintptr(unsafe.Pointer(requiredSize)), 0, 0)
|
||||
if r1 == 0 {
|
||||
if e1 != 0 {
|
||||
err = errnoErr(e1)
|
||||
} else {
|
||||
err = syscall.EINVAL
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func setupDiSetDeviceRegistryProperty(deviceInfoSet DevInfo, deviceInfoData *DevInfoData, property SPDRP, propertyBuffer *byte, propertyBufferSize uint32) (err error) {
|
||||
r1, _, e1 := syscall.Syscall6(procSetupDiSetDeviceRegistryPropertyW.Addr(), 5, uintptr(deviceInfoSet), uintptr(unsafe.Pointer(deviceInfoData)), uintptr(property), uintptr(unsafe.Pointer(propertyBuffer)), uintptr(propertyBufferSize), 0)
|
||||
if r1 == 0 {
|
||||
if e1 != 0 {
|
||||
err = errnoErr(e1)
|
||||
} else {
|
||||
err = syscall.EINVAL
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func setupDiGetDeviceInstallParams(deviceInfoSet DevInfo, deviceInfoData *DevInfoData, deviceInstallParams *DevInstallParams) (err error) {
|
||||
r1, _, e1 := syscall.Syscall(procSetupDiGetDeviceInstallParamsW.Addr(), 3, uintptr(deviceInfoSet), uintptr(unsafe.Pointer(deviceInfoData)), uintptr(unsafe.Pointer(deviceInstallParams)))
|
||||
if r1 == 0 {
|
||||
if e1 != 0 {
|
||||
err = errnoErr(e1)
|
||||
} else {
|
||||
err = syscall.EINVAL
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func SetupDiGetClassInstallParams(deviceInfoSet DevInfo, deviceInfoData *DevInfoData, classInstallParams *ClassInstallHeader, classInstallParamsSize uint32, requiredSize *uint32) (err error) {
|
||||
r1, _, e1 := syscall.Syscall6(procSetupDiGetClassInstallParamsW.Addr(), 5, uintptr(deviceInfoSet), uintptr(unsafe.Pointer(deviceInfoData)), uintptr(unsafe.Pointer(classInstallParams)), uintptr(classInstallParamsSize), uintptr(unsafe.Pointer(requiredSize)), 0)
|
||||
if r1 == 0 {
|
||||
if e1 != 0 {
|
||||
err = errnoErr(e1)
|
||||
} else {
|
||||
err = syscall.EINVAL
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func SetupDiSetDeviceInstallParams(deviceInfoSet DevInfo, deviceInfoData *DevInfoData, deviceInstallParams *DevInstallParams) (err error) {
|
||||
r1, _, e1 := syscall.Syscall(procSetupDiSetDeviceInstallParamsW.Addr(), 3, uintptr(deviceInfoSet), uintptr(unsafe.Pointer(deviceInfoData)), uintptr(unsafe.Pointer(deviceInstallParams)))
|
||||
if r1 == 0 {
|
||||
if e1 != 0 {
|
||||
err = errnoErr(e1)
|
||||
} else {
|
||||
err = syscall.EINVAL
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func SetupDiSetClassInstallParams(deviceInfoSet DevInfo, deviceInfoData *DevInfoData, classInstallParams *ClassInstallHeader, classInstallParamsSize uint32) (err error) {
|
||||
r1, _, e1 := syscall.Syscall6(procSetupDiSetClassInstallParamsW.Addr(), 4, uintptr(deviceInfoSet), uintptr(unsafe.Pointer(deviceInfoData)), uintptr(unsafe.Pointer(classInstallParams)), uintptr(classInstallParamsSize), 0, 0)
|
||||
if r1 == 0 {
|
||||
if e1 != 0 {
|
||||
err = errnoErr(e1)
|
||||
} else {
|
||||
err = syscall.EINVAL
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func setupDiClassNameFromGuidEx(classGUID *windows.GUID, className *uint16, classNameSize uint32, requiredSize *uint32, machineName *uint16, reserved uintptr) (err error) {
|
||||
r1, _, e1 := syscall.Syscall6(procSetupDiClassNameFromGuidExW.Addr(), 6, uintptr(unsafe.Pointer(classGUID)), uintptr(unsafe.Pointer(className)), uintptr(classNameSize), uintptr(unsafe.Pointer(requiredSize)), uintptr(unsafe.Pointer(machineName)), uintptr(reserved))
|
||||
if r1 == 0 {
|
||||
if e1 != 0 {
|
||||
err = errnoErr(e1)
|
||||
} else {
|
||||
err = syscall.EINVAL
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func setupDiClassGuidsFromNameEx(className *uint16, classGuidList *windows.GUID, classGuidListSize uint32, requiredSize *uint32, machineName *uint16, reserved uintptr) (err error) {
|
||||
r1, _, e1 := syscall.Syscall6(procSetupDiClassGuidsFromNameExW.Addr(), 6, uintptr(unsafe.Pointer(className)), uintptr(unsafe.Pointer(classGuidList)), uintptr(classGuidListSize), uintptr(unsafe.Pointer(requiredSize)), uintptr(unsafe.Pointer(machineName)), uintptr(reserved))
|
||||
if r1 == 0 {
|
||||
if e1 != 0 {
|
||||
err = errnoErr(e1)
|
||||
} else {
|
||||
err = syscall.EINVAL
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func setupDiGetSelectedDevice(deviceInfoSet DevInfo, deviceInfoData *DevInfoData) (err error) {
|
||||
r1, _, e1 := syscall.Syscall(procSetupDiGetSelectedDevice.Addr(), 2, uintptr(deviceInfoSet), uintptr(unsafe.Pointer(deviceInfoData)), 0)
|
||||
if r1 == 0 {
|
||||
if e1 != 0 {
|
||||
err = errnoErr(e1)
|
||||
} else {
|
||||
err = syscall.EINVAL
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func SetupDiSetSelectedDevice(deviceInfoSet DevInfo, deviceInfoData *DevInfoData) (err error) {
|
||||
r1, _, e1 := syscall.Syscall(procSetupDiSetSelectedDevice.Addr(), 2, uintptr(deviceInfoSet), uintptr(unsafe.Pointer(deviceInfoData)), 0)
|
||||
if r1 == 0 {
|
||||
if e1 != 0 {
|
||||
err = errnoErr(e1)
|
||||
} else {
|
||||
err = syscall.EINVAL
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
20
tun/wintun/setupapi/zsetupapi_windows_test.go
Normal file
20
tun/wintun/setupapi/zsetupapi_windows_test.go
Normal file
@@ -0,0 +1,20 @@
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2019 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package setupapi
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
"testing"
|
||||
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
func TestSetupDiDestroyDeviceInfoList(t *testing.T) {
|
||||
err := SetupDiDestroyDeviceInfoList(DevInfo(windows.InvalidHandle))
|
||||
if errWin, ok := err.(syscall.Errno); !ok || errWin != 6 /*ERROR_INVALID_HANDLE*/ {
|
||||
t.Errorf("SetupDiDestroyDeviceInfoList(nil, ...) should fail with ERROR_INVALID_HANDLE")
|
||||
}
|
||||
}
|
||||
510
tun/wintun/wintun_windows.go
Normal file
510
tun/wintun/wintun_windows.go
Normal file
@@ -0,0 +1,510 @@
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2019 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package wintun
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"golang.zx2c4.com/wireguard/tun/wintun/netshell"
|
||||
"strings"
|
||||
"syscall"
|
||||
"time"
|
||||
"unsafe"
|
||||
|
||||
"golang.org/x/sys/windows"
|
||||
"golang.org/x/sys/windows/registry"
|
||||
"golang.zx2c4.com/wireguard/tun/wintun/guid"
|
||||
"golang.zx2c4.com/wireguard/tun/wintun/setupapi"
|
||||
)
|
||||
|
||||
//
|
||||
// Wintun is a handle of a Wintun adapter
|
||||
//
|
||||
type Wintun struct {
|
||||
CfgInstanceID windows.GUID
|
||||
LUIDIndex uint32
|
||||
IfType uint32
|
||||
}
|
||||
|
||||
var deviceClassNetGUID = windows.GUID{Data1: 0x4d36e972, Data2: 0xe325, Data3: 0x11ce, Data4: [8]byte{0xbf, 0xc1, 0x08, 0x00, 0x2b, 0xe1, 0x03, 0x18}}
|
||||
|
||||
const hardwareID = "Wintun"
|
||||
const enumerator = ""
|
||||
const machineName = ""
|
||||
|
||||
//
|
||||
// MakeWintun creates interface handle and populates it from device registry key
|
||||
//
|
||||
func MakeWintun(deviceInfoSet setupapi.DevInfo, deviceInfoData *setupapi.DevInfoData) (*Wintun, error) {
|
||||
// Open HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Class\<class>\<id> registry key.
|
||||
key, err := deviceInfoSet.OpenDevRegKey(deviceInfoData, setupapi.DICS_FLAG_GLOBAL, 0, setupapi.DIREG_DRV, registry.READ)
|
||||
if err != nil {
|
||||
return nil, errors.New("Device-specific registry key open failed: " + err.Error())
|
||||
}
|
||||
defer key.Close()
|
||||
|
||||
var valueStr string
|
||||
var valueType uint32
|
||||
|
||||
// Read the NetCfgInstanceId value.
|
||||
valueStr, valueType, err = keyGetStringValueRetry(key, "NetCfgInstanceId")
|
||||
if err != nil {
|
||||
return nil, errors.New("RegQueryStringValue(\"NetCfgInstanceId\") failed: " + err.Error())
|
||||
}
|
||||
if valueType != registry.SZ {
|
||||
return nil, fmt.Errorf("NetCfgInstanceId registry value is not REG_SZ (expected: %v, provided: %v)", registry.SZ, valueType)
|
||||
}
|
||||
|
||||
// Convert to windows.GUID.
|
||||
ifid, err := guid.FromString(valueStr)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("NetCfgInstanceId registry value is not a GUID (expected: \"{...}\", provided: %q)", valueStr)
|
||||
}
|
||||
|
||||
// Read the NetLuidIndex value.
|
||||
luidIdx, valueType, err := key.GetIntegerValue("NetLuidIndex")
|
||||
if err != nil {
|
||||
return nil, errors.New("RegQueryValue(\"NetLuidIndex\") failed: " + err.Error())
|
||||
}
|
||||
|
||||
// Read the NetLuidIndex value.
|
||||
ifType, valueType, err := key.GetIntegerValue("*IfType")
|
||||
if err != nil {
|
||||
return nil, errors.New("RegQueryValue(\"*IfType\") failed: " + err.Error())
|
||||
}
|
||||
|
||||
return &Wintun{
|
||||
CfgInstanceID: *ifid,
|
||||
LUIDIndex: uint32(luidIdx),
|
||||
IfType: uint32(ifType),
|
||||
}, nil
|
||||
}
|
||||
|
||||
//
|
||||
// GetInterface finds interface by name.
|
||||
//
|
||||
// hwndParent is a handle to the top-level window to use for any user
|
||||
// interface that is related to non-device-specific actions (such as a select-
|
||||
// device dialog box that uses the global class driver list). This handle is
|
||||
// optional and can be 0. If a specific top-level window is not required, set
|
||||
// hwndParent to 0.
|
||||
//
|
||||
// Function returns interface if found, or nil otherwise. If the interface is
|
||||
// found but not Wintun-class, the function returns interface and an error.
|
||||
//
|
||||
func GetInterface(ifname string, hwndParent uintptr) (*Wintun, error) {
|
||||
// Create a list of network devices.
|
||||
devInfoList, err := setupapi.SetupDiGetClassDevsEx(&deviceClassNetGUID, enumerator, hwndParent, setupapi.DIGCF_PRESENT, setupapi.DevInfo(0), machineName)
|
||||
if err != nil {
|
||||
return nil, errors.New(fmt.Sprintf("SetupDiGetClassDevsEx(%v) failed: ", guid.ToString(&deviceClassNetGUID)) + err.Error())
|
||||
}
|
||||
defer devInfoList.Close()
|
||||
|
||||
// Windows requires each interface to have a different name. When
|
||||
// enforcing this, Windows treats interface names case-insensitive. If an
|
||||
// interface "FooBar" exists and this function reports there is no
|
||||
// interface "foobar", an attempt to create a new interface and name it
|
||||
// "foobar" would cause conflict with "FooBar".
|
||||
ifname = strings.ToLower(ifname)
|
||||
|
||||
for index := 0; ; index++ {
|
||||
// Get the device from the list. Should anything be wrong with this device, continue with next.
|
||||
deviceData, err := devInfoList.EnumDeviceInfo(index)
|
||||
if err != nil {
|
||||
if errWin, ok := err.(syscall.Errno); ok && errWin == 259 /*ERROR_NO_MORE_ITEMS*/ {
|
||||
break
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
// Get interface ID.
|
||||
wintun, err := MakeWintun(devInfoList, deviceData)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
// Get interface name.
|
||||
ifname2, err := wintun.GetInterfaceName()
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
if ifname == strings.ToLower(ifname2) {
|
||||
// Interface name found. Check its driver.
|
||||
const driverType = setupapi.SPDIT_COMPATDRIVER
|
||||
err = devInfoList.BuildDriverInfoList(deviceData, driverType)
|
||||
if err != nil {
|
||||
return nil, errors.New("SetupDiBuildDriverInfoList failed: " + err.Error())
|
||||
}
|
||||
defer devInfoList.DestroyDriverInfoList(deviceData, driverType)
|
||||
|
||||
for index := 0; ; index++ {
|
||||
// Get a driver from the list.
|
||||
driverData, err := devInfoList.EnumDriverInfo(deviceData, driverType, index)
|
||||
if err != nil {
|
||||
if errWin, ok := err.(syscall.Errno); ok && errWin == 259 /*ERROR_NO_MORE_ITEMS*/ {
|
||||
break
|
||||
}
|
||||
// Something is wrong with this driver. Skip it.
|
||||
continue
|
||||
}
|
||||
|
||||
// Get driver info details.
|
||||
driverDetailData, err := devInfoList.GetDriverInfoDetail(deviceData, driverData)
|
||||
if err != nil {
|
||||
// Something is wrong with this driver. Skip it.
|
||||
continue
|
||||
}
|
||||
|
||||
if driverDetailData.IsCompatible(hardwareID) {
|
||||
// Matching hardware ID found.
|
||||
return wintun, nil
|
||||
}
|
||||
}
|
||||
|
||||
// This interface is not using Wintun driver.
|
||||
return nil, errors.New("Foreign network interface with the same name exists")
|
||||
}
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
//
|
||||
// CreateInterface creates a TUN interface.
|
||||
//
|
||||
// description is a string that supplies the text description of the device.
|
||||
// description is optional and can be "".
|
||||
//
|
||||
// hwndParent is a handle to the top-level window to use for any user
|
||||
// interface that is related to non-device-specific actions (such as a select-
|
||||
// device dialog box that uses the global class driver list). This handle is
|
||||
// optional and can be 0. If a specific top-level window is not required, set
|
||||
// hwndParent to 0.
|
||||
//
|
||||
// Function returns the network interface ID and a flag if reboot is required.
|
||||
//
|
||||
func CreateInterface(description string, hwndParent uintptr) (*Wintun, bool, error) {
|
||||
// Create an empty device info set for network adapter device class.
|
||||
devInfoList, err := setupapi.SetupDiCreateDeviceInfoListEx(&deviceClassNetGUID, hwndParent, machineName)
|
||||
if err != nil {
|
||||
return nil, false, errors.New(fmt.Sprintf("SetupDiCreateDeviceInfoListEx(%v) failed: ", guid.ToString(&deviceClassNetGUID)) + err.Error())
|
||||
}
|
||||
|
||||
// Get the device class name from GUID.
|
||||
className, err := setupapi.SetupDiClassNameFromGuidEx(&deviceClassNetGUID, machineName)
|
||||
if err != nil {
|
||||
return nil, false, errors.New(fmt.Sprintf("SetupDiClassNameFromGuidEx(%v) failed: ", guid.ToString(&deviceClassNetGUID)) + err.Error())
|
||||
}
|
||||
|
||||
// Create a new device info element and add it to the device info set.
|
||||
deviceData, err := devInfoList.CreateDeviceInfo(className, &deviceClassNetGUID, description, hwndParent, setupapi.DICD_GENERATE_ID)
|
||||
if err != nil {
|
||||
return nil, false, errors.New("SetupDiCreateDeviceInfo failed: " + err.Error())
|
||||
}
|
||||
|
||||
// Set a device information element as the selected member of a device information set.
|
||||
err = devInfoList.SetSelectedDevice(deviceData)
|
||||
if err != nil {
|
||||
return nil, false, errors.New("SetupDiSetSelectedDevice failed: " + err.Error())
|
||||
}
|
||||
|
||||
// Set Plug&Play device hardware ID property.
|
||||
hwid, err := syscall.UTF16FromString(hardwareID)
|
||||
if err != nil {
|
||||
return nil, false, err // syscall.UTF16FromString(hardwareID) should never fail: hardwareID is const string without NUL chars.
|
||||
}
|
||||
err = devInfoList.SetDeviceRegistryProperty(deviceData, setupapi.SPDRP_HARDWAREID, setupapi.UTF16ToBuf(append(hwid, 0)))
|
||||
if err != nil {
|
||||
return nil, false, errors.New("SetupDiSetDeviceRegistryProperty(SPDRP_HARDWAREID) failed: " + err.Error())
|
||||
}
|
||||
|
||||
// Search for the driver.
|
||||
const driverType = setupapi.SPDIT_CLASSDRIVER
|
||||
err = devInfoList.BuildDriverInfoList(deviceData, driverType)
|
||||
if err != nil {
|
||||
return nil, false, errors.New("SetupDiBuildDriverInfoList failed: " + err.Error())
|
||||
}
|
||||
defer devInfoList.DestroyDriverInfoList(deviceData, driverType)
|
||||
|
||||
driverDate := windows.Filetime{}
|
||||
driverVersion := uint64(0)
|
||||
for index := 0; ; index++ {
|
||||
// Get a driver from the list.
|
||||
driverData, err := devInfoList.EnumDriverInfo(deviceData, driverType, index)
|
||||
if err != nil {
|
||||
if errWin, ok := err.(syscall.Errno); ok && errWin == 259 /*ERROR_NO_MORE_ITEMS*/ {
|
||||
break
|
||||
}
|
||||
// Something is wrong with this driver. Skip it.
|
||||
continue
|
||||
}
|
||||
|
||||
// Check the driver version first, since the check is trivial and will save us iterating over hardware IDs for any driver versioned prior our best match.
|
||||
if driverData.IsNewer(driverDate, driverVersion) {
|
||||
// Get driver info details.
|
||||
driverDetailData, err := devInfoList.GetDriverInfoDetail(deviceData, driverData)
|
||||
if err != nil {
|
||||
// Something is wrong with this driver. Skip it.
|
||||
continue
|
||||
}
|
||||
|
||||
if driverDetailData.IsCompatible(hardwareID) {
|
||||
// Matching hardware ID found. Select the driver.
|
||||
err := devInfoList.SetSelectedDriver(deviceData, driverData)
|
||||
if err != nil {
|
||||
// Something is wrong with this driver. Skip it.
|
||||
continue
|
||||
}
|
||||
|
||||
driverDate = driverData.DriverDate
|
||||
driverVersion = driverData.DriverVersion
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if driverVersion == 0 {
|
||||
return nil, false, fmt.Errorf("No driver for device %q installed", hardwareID)
|
||||
}
|
||||
|
||||
// Call appropriate class installer.
|
||||
err = devInfoList.CallClassInstaller(setupapi.DIF_REGISTERDEVICE, deviceData)
|
||||
if err != nil {
|
||||
return nil, false, errors.New("SetupDiCallClassInstaller(DIF_REGISTERDEVICE) failed: " + err.Error())
|
||||
}
|
||||
|
||||
// Register device co-installers if any. (Ignore errors)
|
||||
devInfoList.CallClassInstaller(setupapi.DIF_REGISTER_COINSTALLERS, deviceData)
|
||||
|
||||
// Install interfaces if any. (Ignore errors)
|
||||
devInfoList.CallClassInstaller(setupapi.DIF_INSTALLINTERFACES, deviceData)
|
||||
|
||||
var wintun *Wintun
|
||||
var rebootRequired bool
|
||||
|
||||
// Install the device.
|
||||
err = devInfoList.CallClassInstaller(setupapi.DIF_INSTALLDEVICE, deviceData)
|
||||
if err != nil {
|
||||
err = errors.New("SetupDiCallClassInstaller(DIF_INSTALLDEVICE) failed: " + err.Error())
|
||||
}
|
||||
|
||||
if err == nil {
|
||||
// Check if a system reboot is required. (Ignore errors)
|
||||
if ret, _ := checkReboot(devInfoList, deviceData); ret {
|
||||
rebootRequired = true
|
||||
}
|
||||
|
||||
// Get network interface. DIF_INSTALLDEVICE returns almost immediately, while the device
|
||||
// installation continues in the background. It might take a while, before all registry
|
||||
// keys and values are populated.
|
||||
for numAttempts := 0; numAttempts < 30; numAttempts++ {
|
||||
wintun, err = MakeWintun(devInfoList, deviceData)
|
||||
if err != nil {
|
||||
if errWin, ok := err.(syscall.Errno); ok && errWin == windows.ERROR_FILE_NOT_FOUND {
|
||||
// Wait and retry. TODO: Wait for a cancellable event instead.
|
||||
err = errors.New("Time-out waiting for adapter to get ready")
|
||||
time.Sleep(time.Second / 4)
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if err == nil {
|
||||
return wintun, rebootRequired, nil
|
||||
}
|
||||
|
||||
// The interface failed to install, or the interface ID was unobtainable. Clean-up.
|
||||
removeDeviceParams := setupapi.RemoveDeviceParams{
|
||||
ClassInstallHeader: *setupapi.MakeClassInstallHeader(setupapi.DIF_REMOVE),
|
||||
Scope: setupapi.DI_REMOVEDEVICE_GLOBAL,
|
||||
}
|
||||
|
||||
// Set class installer parameters for DIF_REMOVE.
|
||||
if devInfoList.SetClassInstallParams(deviceData, &removeDeviceParams.ClassInstallHeader, uint32(unsafe.Sizeof(removeDeviceParams))) == nil {
|
||||
// Call appropriate class installer.
|
||||
if devInfoList.CallClassInstaller(setupapi.DIF_REMOVE, deviceData) == nil {
|
||||
// Check if a system reboot is required. (Ignore errors)
|
||||
if ret, _ := checkReboot(devInfoList, deviceData); ret {
|
||||
rebootRequired = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil, rebootRequired, err
|
||||
}
|
||||
|
||||
//
|
||||
// DeleteInterface deletes a TUN interface.
|
||||
//
|
||||
// hwndParent is a handle to the top-level window to use for any user
|
||||
// interface that is related to non-device-specific actions (such as a select-
|
||||
// device dialog box that uses the global class driver list). This handle is
|
||||
// optional and can be 0. If a specific top-level window is not required, set
|
||||
// hwndParent to 0.
|
||||
//
|
||||
// Function returns true if the interface was found and deleted and a flag if
|
||||
// reboot is required.
|
||||
//
|
||||
func (wintun *Wintun) DeleteInterface(hwndParent uintptr) (bool, bool, error) {
|
||||
// Create a list of network devices.
|
||||
devInfoList, err := setupapi.SetupDiGetClassDevsEx(&deviceClassNetGUID, enumerator, hwndParent, setupapi.DIGCF_PRESENT, setupapi.DevInfo(0), machineName)
|
||||
if err != nil {
|
||||
return false, false, errors.New(fmt.Sprintf("SetupDiGetClassDevsEx(%v) failed: ", guid.ToString(&deviceClassNetGUID)) + err.Error())
|
||||
}
|
||||
defer devInfoList.Close()
|
||||
|
||||
// Iterate.
|
||||
for index := 0; ; index++ {
|
||||
// Get the device from the list. Should anything be wrong with this device, continue with next.
|
||||
deviceData, err := devInfoList.EnumDeviceInfo(index)
|
||||
if err != nil {
|
||||
if errWin, ok := err.(syscall.Errno); ok && errWin == 259 /*ERROR_NO_MORE_ITEMS*/ {
|
||||
break
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
// Get interface ID.
|
||||
wintun2, err := MakeWintun(devInfoList, deviceData)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
if wintun.CfgInstanceID == wintun2.CfgInstanceID {
|
||||
// Remove the device.
|
||||
removeDeviceParams := setupapi.RemoveDeviceParams{
|
||||
ClassInstallHeader: *setupapi.MakeClassInstallHeader(setupapi.DIF_REMOVE),
|
||||
Scope: setupapi.DI_REMOVEDEVICE_GLOBAL,
|
||||
}
|
||||
|
||||
// Set class installer parameters for DIF_REMOVE.
|
||||
err = devInfoList.SetClassInstallParams(deviceData, &removeDeviceParams.ClassInstallHeader, uint32(unsafe.Sizeof(removeDeviceParams)))
|
||||
if err != nil {
|
||||
return false, false, errors.New("SetupDiSetClassInstallParams failed: " + err.Error())
|
||||
}
|
||||
|
||||
// Call appropriate class installer.
|
||||
err = devInfoList.CallClassInstaller(setupapi.DIF_REMOVE, deviceData)
|
||||
if err != nil {
|
||||
return false, false, errors.New("SetupDiCallClassInstaller failed: " + err.Error())
|
||||
}
|
||||
|
||||
// Check if a system reboot is required. (Ignore errors)
|
||||
if ret, _ := checkReboot(devInfoList, deviceData); ret {
|
||||
return true, true, nil
|
||||
}
|
||||
|
||||
return true, false, nil
|
||||
}
|
||||
}
|
||||
|
||||
return false, false, nil
|
||||
}
|
||||
|
||||
//
|
||||
// FlushInterface removes all properties from the interface and gives it only a very
|
||||
// vanilla IPv4 and IPv6 configuration with no addresses of any sort assigned.
|
||||
//
|
||||
func (wintun *Wintun) FlushInterface() error {
|
||||
//TODO: implement me!
|
||||
return nil
|
||||
}
|
||||
|
||||
//
|
||||
// checkReboot checks device install parameters if a system reboot is required.
|
||||
//
|
||||
func checkReboot(deviceInfoSet setupapi.DevInfo, deviceInfoData *setupapi.DevInfoData) (bool, error) {
|
||||
devInstallParams, err := deviceInfoSet.GetDeviceInstallParams(deviceInfoData)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
if (devInstallParams.Flags & (setupapi.DI_NEEDREBOOT | setupapi.DI_NEEDRESTART)) != 0 {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
return false, nil
|
||||
}
|
||||
|
||||
//
|
||||
// GetInterfaceName returns network interface name.
|
||||
//
|
||||
func (wintun *Wintun) GetInterfaceName() (string, error) {
|
||||
key, err := registryOpenKeyRetry(registry.LOCAL_MACHINE, wintun.GetNetRegKeyName(), registry.QUERY_VALUE)
|
||||
if err != nil {
|
||||
return "", errors.New("Network-specific registry key open failed: " + err.Error())
|
||||
}
|
||||
defer key.Close()
|
||||
|
||||
// Get the interface name.
|
||||
return getRegStringValue(key, "Name")
|
||||
}
|
||||
|
||||
//
|
||||
// SetInterfaceName sets network interface name.
|
||||
//
|
||||
func (wintun *Wintun) SetInterfaceName(ifname string) error {
|
||||
// We open the registry key before calling HrRename, because the registry open will wait until the key exists.
|
||||
key, err := registryOpenKeyRetry(registry.LOCAL_MACHINE, wintun.GetNetRegKeyName(), registry.SET_VALUE)
|
||||
if err != nil {
|
||||
return errors.New("Network-specific registry key open failed: " + err.Error())
|
||||
}
|
||||
defer key.Close()
|
||||
|
||||
// We have to tell the various runtime COM services about the new name too. We ignore the
|
||||
// error because netshell isn't available on servercore.
|
||||
// TODO: netsh.exe falls back to NciSetConnection in this case. If somebody complains, maybe
|
||||
// we should do the same.
|
||||
_ = netshell.HrRenameConnection(&wintun.CfgInstanceID, windows.StringToUTF16Ptr(ifname))
|
||||
|
||||
// Set the interface name. The above line should have done this too, but in case it failed, we force it.
|
||||
return key.SetStringValue("Name", ifname)
|
||||
}
|
||||
|
||||
//
|
||||
// GetNetRegKeyName returns interface-specific network registry key name.
|
||||
//
|
||||
func (wintun *Wintun) GetNetRegKeyName() string {
|
||||
return fmt.Sprintf("SYSTEM\\CurrentControlSet\\Control\\Network\\%v\\%v\\Connection", guid.ToString(&deviceClassNetGUID), guid.ToString(&wintun.CfgInstanceID))
|
||||
}
|
||||
|
||||
//
|
||||
// getRegStringValue function reads a string value from registry.
|
||||
//
|
||||
// If the value type is REG_EXPAND_SZ the environment variables are expanded.
|
||||
// Should expanding fail, original string value and nil error are returned.
|
||||
//
|
||||
func getRegStringValue(key registry.Key, name string) (string, error) {
|
||||
// Read string value.
|
||||
value, valueType, err := keyGetStringValueRetry(key, name)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if valueType != registry.EXPAND_SZ {
|
||||
// Value does not require expansion.
|
||||
return value, nil
|
||||
}
|
||||
|
||||
valueExp, err := registry.ExpandString(value)
|
||||
if err != nil {
|
||||
// Expanding failed: return original sting value.
|
||||
return value, nil
|
||||
}
|
||||
|
||||
// Return expanded value.
|
||||
return valueExp, nil
|
||||
}
|
||||
|
||||
//
|
||||
// DataFileName returns Wintun device data pipe name.
|
||||
//
|
||||
func (wintun *Wintun) DataFileName() string {
|
||||
return fmt.Sprintf("\\\\.\\Global\\WINTUN%d", wintun.LUIDIndex)
|
||||
}
|
||||
483
tun_windows.go
483
tun_windows.go
@@ -1,483 +0,0 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0
|
||||
*
|
||||
* Copyright (C) 2017-2018 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
|
||||
* Copyright (C) 2017-2018 Mathias N. Hall-Andersen <mathias@hall-andersen.dk>.
|
||||
*/
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
"golang.org/x/sys/windows"
|
||||
"golang.org/x/sys/windows/registry"
|
||||
"net"
|
||||
"sync"
|
||||
"syscall"
|
||||
"time"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
/* Relies on the OpenVPN TAP-Windows driver (NDIS 6 version)
|
||||
*
|
||||
* https://github.com/OpenVPN/tap-windows
|
||||
*/
|
||||
|
||||
type NativeTUN struct {
|
||||
fd windows.Handle
|
||||
rl sync.Mutex
|
||||
wl sync.Mutex
|
||||
ro *windows.Overlapped
|
||||
wo *windows.Overlapped
|
||||
events chan TUNEvent
|
||||
name string
|
||||
}
|
||||
|
||||
const (
|
||||
METHOD_BUFFERED = 0
|
||||
ComponentID = "tap0901" // tap0801
|
||||
)
|
||||
|
||||
func ctl_code(device_type, function, method, access uint32) uint32 {
|
||||
return (device_type << 16) | (access << 14) | (function << 2) | method
|
||||
}
|
||||
|
||||
func TAP_CONTROL_CODE(request, method uint32) uint32 {
|
||||
return ctl_code(file_device_unknown, request, method, 0)
|
||||
}
|
||||
|
||||
var (
|
||||
errIfceNameNotFound = errors.New("Failed to find the name of interface")
|
||||
|
||||
TAP_IOCTL_GET_MAC = TAP_CONTROL_CODE(1, METHOD_BUFFERED)
|
||||
TAP_IOCTL_GET_VERSION = TAP_CONTROL_CODE(2, METHOD_BUFFERED)
|
||||
TAP_IOCTL_GET_MTU = TAP_CONTROL_CODE(3, METHOD_BUFFERED)
|
||||
TAP_IOCTL_GET_INFO = TAP_CONTROL_CODE(4, METHOD_BUFFERED)
|
||||
TAP_IOCTL_CONFIG_POINT_TO_POINT = TAP_CONTROL_CODE(5, METHOD_BUFFERED)
|
||||
TAP_IOCTL_SET_MEDIA_STATUS = TAP_CONTROL_CODE(6, METHOD_BUFFERED)
|
||||
TAP_IOCTL_CONFIG_DHCP_MASQ = TAP_CONTROL_CODE(7, METHOD_BUFFERED)
|
||||
TAP_IOCTL_GET_LOG_LINE = TAP_CONTROL_CODE(8, METHOD_BUFFERED)
|
||||
TAP_IOCTL_CONFIG_DHCP_SET_OPT = TAP_CONTROL_CODE(9, METHOD_BUFFERED)
|
||||
TAP_IOCTL_CONFIG_TUN = TAP_CONTROL_CODE(10, METHOD_BUFFERED)
|
||||
|
||||
file_device_unknown = uint32(0x00000022)
|
||||
nCreateEvent,
|
||||
nResetEvent,
|
||||
nGetOverlappedResult uintptr
|
||||
)
|
||||
|
||||
func init() {
|
||||
k32, err := windows.LoadLibrary("kernel32.dll")
|
||||
if err != nil {
|
||||
panic("LoadLibrary " + err.Error())
|
||||
}
|
||||
defer windows.FreeLibrary(k32)
|
||||
nCreateEvent = getProcAddr(k32, "CreateEventW")
|
||||
nResetEvent = getProcAddr(k32, "ResetEvent")
|
||||
nGetOverlappedResult = getProcAddr(k32, "GetOverlappedResult")
|
||||
}
|
||||
|
||||
/* implementation of the read/write/closer interface */
|
||||
|
||||
func getProcAddr(lib windows.Handle, name string) uintptr {
|
||||
addr, err := windows.GetProcAddress(lib, name)
|
||||
if err != nil {
|
||||
panic(name + " " + err.Error())
|
||||
}
|
||||
return addr
|
||||
}
|
||||
|
||||
func resetEvent(h windows.Handle) error {
|
||||
r, _, err := syscall.Syscall(nResetEvent, 1, uintptr(h), 0, 0)
|
||||
if r == 0 {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func getOverlappedResult(h windows.Handle, overlapped *windows.Overlapped) (int, error) {
|
||||
var n int
|
||||
r, _, err := syscall.Syscall6(
|
||||
nGetOverlappedResult,
|
||||
4,
|
||||
uintptr(h),
|
||||
uintptr(unsafe.Pointer(overlapped)),
|
||||
uintptr(unsafe.Pointer(&n)), 1, 0, 0)
|
||||
|
||||
if r == 0 {
|
||||
return n, err
|
||||
}
|
||||
return n, nil
|
||||
}
|
||||
|
||||
func newOverlapped() (*windows.Overlapped, error) {
|
||||
var overlapped windows.Overlapped
|
||||
r, _, err := syscall.Syscall6(nCreateEvent, 4, 0, 1, 0, 0, 0, 0)
|
||||
if r == 0 {
|
||||
return nil, err
|
||||
}
|
||||
overlapped.HEvent = windows.Handle(r)
|
||||
return &overlapped, nil
|
||||
}
|
||||
|
||||
func (f *NativeTUN) Events() chan TUNEvent {
|
||||
return f.events
|
||||
}
|
||||
|
||||
func (f *NativeTUN) Close() error {
|
||||
close(f.events)
|
||||
err := windows.Close(f.fd)
|
||||
return err
|
||||
}
|
||||
|
||||
func (f *NativeTUN) Write(b []byte) (int, error) {
|
||||
f.wl.Lock()
|
||||
defer f.wl.Unlock()
|
||||
|
||||
if err := resetEvent(f.wo.HEvent); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
var n uint32
|
||||
err := windows.WriteFile(f.fd, b, &n, f.wo)
|
||||
if err != nil && err != windows.ERROR_IO_PENDING {
|
||||
return int(n), err
|
||||
}
|
||||
return getOverlappedResult(f.fd, f.wo)
|
||||
}
|
||||
|
||||
func (f *NativeTUN) Read(b []byte) (int, error) {
|
||||
f.rl.Lock()
|
||||
defer f.rl.Unlock()
|
||||
|
||||
if err := resetEvent(f.ro.HEvent); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
var done uint32
|
||||
err := windows.ReadFile(f.fd, b, &done, f.ro)
|
||||
if err != nil && err != windows.ERROR_IO_PENDING {
|
||||
return int(done), err
|
||||
}
|
||||
return getOverlappedResult(f.fd, f.ro)
|
||||
}
|
||||
|
||||
func getdeviceid(
|
||||
targetComponentId string,
|
||||
targetDeviceName string,
|
||||
) (deviceid string, err error) {
|
||||
|
||||
getName := func(instanceId string) (string, error) {
|
||||
path := fmt.Sprintf(
|
||||
`SYSTEM\CurrentControlSet\Control\Network\{4D36E972-E325-11CE-BFC1-08002BE10318}\%s\Connection`,
|
||||
instanceId,
|
||||
)
|
||||
|
||||
key, err := registry.OpenKey(
|
||||
registry.LOCAL_MACHINE,
|
||||
path,
|
||||
registry.READ,
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer key.Close()
|
||||
|
||||
val, _, err := key.GetStringValue("Name")
|
||||
key.Close()
|
||||
return val, err
|
||||
}
|
||||
|
||||
getInstanceId := func(keyName string) (string, string, error) {
|
||||
path := fmt.Sprintf(
|
||||
`SYSTEM\CurrentControlSet\Control\Class\{4D36E972-E325-11CE-BFC1-08002BE10318}\%s`,
|
||||
keyName,
|
||||
)
|
||||
|
||||
key, err := registry.OpenKey(
|
||||
registry.LOCAL_MACHINE,
|
||||
path,
|
||||
registry.READ,
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
defer key.Close()
|
||||
|
||||
componentId, _, err := key.GetStringValue("ComponentId")
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
|
||||
instanceId, _, err := key.GetStringValue("NetCfgInstanceId")
|
||||
|
||||
return componentId, instanceId, err
|
||||
}
|
||||
|
||||
// find list of all network devices
|
||||
|
||||
k, err := registry.OpenKey(
|
||||
registry.LOCAL_MACHINE,
|
||||
`SYSTEM\CurrentControlSet\Control\Class\{4D36E972-E325-11CE-BFC1-08002BE10318}`,
|
||||
registry.READ,
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("Failed to open the adapter registry, TAP driver may be not installed, %v", err)
|
||||
}
|
||||
|
||||
defer k.Close()
|
||||
|
||||
keys, err := k.ReadSubKeyNames(-1)
|
||||
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
// look for matching component id and name
|
||||
|
||||
var componentFound bool
|
||||
|
||||
for _, v := range keys {
|
||||
|
||||
componentId, instanceId, err := getInstanceId(v)
|
||||
if err != nil || componentId != targetComponentId {
|
||||
continue
|
||||
}
|
||||
|
||||
componentFound = true
|
||||
|
||||
deviceName, err := getName(instanceId)
|
||||
if err != nil || deviceName != targetDeviceName {
|
||||
continue
|
||||
}
|
||||
|
||||
return instanceId, nil
|
||||
}
|
||||
|
||||
// provide a descriptive error message
|
||||
|
||||
if componentFound {
|
||||
return "", fmt.Errorf("Unable to find tun/tap device with name = %s", targetDeviceName)
|
||||
}
|
||||
|
||||
return "", fmt.Errorf(
|
||||
"Unable to find device in registry with ComponentId = %s, is tap-windows installed?",
|
||||
targetComponentId,
|
||||
)
|
||||
}
|
||||
|
||||
// setStatus is used to bring up or bring down the interface
|
||||
func setStatus(fd windows.Handle, status bool) error {
|
||||
var code [4]byte
|
||||
if status {
|
||||
binary.LittleEndian.PutUint32(code[:], 1)
|
||||
}
|
||||
|
||||
var bytesReturned uint32
|
||||
rdbbuf := make([]byte, windows.MAXIMUM_REPARSE_DATA_BUFFER_SIZE)
|
||||
return windows.DeviceIoControl(
|
||||
fd,
|
||||
TAP_IOCTL_SET_MEDIA_STATUS,
|
||||
&code[0],
|
||||
uint32(4),
|
||||
&rdbbuf[0],
|
||||
uint32(len(rdbbuf)),
|
||||
&bytesReturned,
|
||||
nil,
|
||||
)
|
||||
}
|
||||
|
||||
/* When operating in TUN mode we must assign an ip address & subnet to the device.
|
||||
*
|
||||
*/
|
||||
func setTUN(fd windows.Handle, network string) error {
|
||||
var bytesReturned uint32
|
||||
rdbbuf := make([]byte, windows.MAXIMUM_REPARSE_DATA_BUFFER_SIZE)
|
||||
localIP, remoteNet, err := net.ParseCIDR(network)
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("Failed to parse network CIDR in config, %v", err)
|
||||
}
|
||||
|
||||
if localIP.To4() == nil {
|
||||
return fmt.Errorf("Provided network(%s) is not a valid IPv4 address", network)
|
||||
}
|
||||
|
||||
var param [12]byte
|
||||
|
||||
copy(param[0:4], localIP.To4())
|
||||
copy(param[4:8], remoteNet.IP.To4())
|
||||
copy(param[8:12], remoteNet.Mask)
|
||||
|
||||
return windows.DeviceIoControl(
|
||||
fd,
|
||||
TAP_IOCTL_CONFIG_TUN,
|
||||
¶m[0],
|
||||
uint32(12),
|
||||
&rdbbuf[0],
|
||||
uint32(len(rdbbuf)),
|
||||
&bytesReturned,
|
||||
nil,
|
||||
)
|
||||
}
|
||||
|
||||
func (tun *NativeTUN) MTU() (int, error) {
|
||||
var mtu [4]byte
|
||||
var bytesReturned uint32
|
||||
err := windows.DeviceIoControl(
|
||||
tun.fd,
|
||||
TAP_IOCTL_GET_MTU,
|
||||
&mtu[0],
|
||||
uint32(len(mtu)),
|
||||
&mtu[0],
|
||||
uint32(len(mtu)),
|
||||
&bytesReturned,
|
||||
nil,
|
||||
)
|
||||
val := binary.LittleEndian.Uint32(mtu[:])
|
||||
return int(val), err
|
||||
}
|
||||
|
||||
func (tun *NativeTUN) Name() string {
|
||||
return tun.name
|
||||
}
|
||||
|
||||
func CreateTUN(name string) (TUNDevice, error) {
|
||||
|
||||
// find the device in registry.
|
||||
|
||||
deviceid, err := getdeviceid(ComponentID, name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
path := "\\\\.\\Global\\" + deviceid + ".tap"
|
||||
pathp, err := windows.UTF16PtrFromString(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// create TUN device
|
||||
|
||||
handle, err := windows.CreateFile(
|
||||
pathp,
|
||||
windows.GENERIC_READ|windows.GENERIC_WRITE,
|
||||
0,
|
||||
nil,
|
||||
windows.OPEN_EXISTING,
|
||||
windows.FILE_ATTRIBUTE_SYSTEM|windows.FILE_FLAG_OVERLAPPED,
|
||||
0,
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ro, err := newOverlapped()
|
||||
if err != nil {
|
||||
windows.Close(handle)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
wo, err := newOverlapped()
|
||||
if err != nil {
|
||||
windows.Close(handle)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
tun := &NativeTUN{
|
||||
fd: handle,
|
||||
name: name,
|
||||
ro: ro,
|
||||
wo: wo,
|
||||
events: make(chan TUNEvent, 5),
|
||||
}
|
||||
|
||||
// find addresses of interface
|
||||
// TODO: fix this hack, the question is how
|
||||
|
||||
inter, err := net.InterfaceByName(name)
|
||||
if err != nil {
|
||||
windows.Close(handle)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
addrs, err := inter.Addrs()
|
||||
if err != nil {
|
||||
windows.Close(handle)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var ip net.IP
|
||||
for _, addr := range addrs {
|
||||
ip = func() net.IP {
|
||||
switch v := addr.(type) {
|
||||
case *net.IPNet:
|
||||
return v.IP.To4()
|
||||
case *net.IPAddr:
|
||||
return v.IP.To4()
|
||||
}
|
||||
return nil
|
||||
}()
|
||||
if ip != nil {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if ip == nil {
|
||||
windows.Close(handle)
|
||||
return nil, errors.New("No IPv4 address found for interface")
|
||||
}
|
||||
|
||||
// bring up device.
|
||||
|
||||
if err := setStatus(handle, true); err != nil {
|
||||
windows.Close(handle)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// set tun mode
|
||||
|
||||
mask := ip.String() + "/0"
|
||||
if err := setTUN(handle, mask); err != nil {
|
||||
windows.Close(handle)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// start listener
|
||||
|
||||
go func(native *NativeTUN, ifname string) {
|
||||
// TODO: Fix this very niave implementation
|
||||
var (
|
||||
statusUp bool
|
||||
statusMTU int
|
||||
)
|
||||
|
||||
for ; ; time.Sleep(time.Second) {
|
||||
intr, err := net.InterfaceByName(name)
|
||||
if err != nil {
|
||||
// TODO: handle
|
||||
return
|
||||
}
|
||||
|
||||
// Up / Down event
|
||||
up := (intr.Flags & net.FlagUp) != 0
|
||||
if up != statusUp && up {
|
||||
native.events <- TUNEventUp
|
||||
}
|
||||
if up != statusUp && !up {
|
||||
native.events <- TUNEventDown
|
||||
}
|
||||
statusUp = up
|
||||
|
||||
// MTU changes
|
||||
if intr.MTU != statusMTU {
|
||||
native.events <- TUNEventMTUUpdate
|
||||
}
|
||||
statusMTU = intr.MTU
|
||||
}
|
||||
}(tun, name)
|
||||
|
||||
return tun, nil
|
||||
}
|
||||
@@ -1,50 +0,0 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0
|
||||
*
|
||||
* Copyright (C) 2017-2018 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
|
||||
* Copyright (C) 2017-2018 Mathias N. Hall-Andersen <mathias@hall-andersen.dk>.
|
||||
*/
|
||||
|
||||
package main
|
||||
|
||||
/* UAPI on windows uses a bidirectional named pipe
|
||||
*/
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/Microsoft/go-winio"
|
||||
"golang.org/x/sys/windows"
|
||||
"net"
|
||||
)
|
||||
|
||||
const (
|
||||
ipcErrorIO = -int64(windows.ERROR_BROKEN_PIPE)
|
||||
ipcErrorProtocol = -int64(windows.ERROR_INVALID_NAME)
|
||||
ipcErrorInvalid = -int64(windows.ERROR_INVALID_PARAMETER)
|
||||
ipcErrorPortInUse = -int64(windows.ERROR_ALREADY_EXISTS)
|
||||
)
|
||||
|
||||
const PipeNameFmt = "\\\\.\\pipe\\wireguard-ipc-%s"
|
||||
|
||||
type UAPIListener struct {
|
||||
listener net.Listener
|
||||
}
|
||||
|
||||
func (uapi *UAPIListener) Accept() (net.Conn, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (uapi *UAPIListener) Close() error {
|
||||
return uapi.listener.Close()
|
||||
}
|
||||
|
||||
func (uapi *UAPIListener) Addr() net.Addr {
|
||||
return nil
|
||||
}
|
||||
|
||||
func NewUAPIListener(name string) (net.Listener, error) {
|
||||
path := fmt.Sprintf(PipeNameFmt, name)
|
||||
return winio.ListenPipe(path, &winio.PipeConfig{
|
||||
InputBufferSize: 2048,
|
||||
OutputBufferSize: 2048,
|
||||
})
|
||||
}
|
||||
@@ -1,171 +0,0 @@
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2016 Andreas Auernhammer. All Rights Reserved.
|
||||
* Copyright (C) 2017-2018 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package xchacha20poly1305
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"golang.org/x/crypto/chacha20poly1305"
|
||||
)
|
||||
|
||||
func hChaCha20(out *[32]byte, nonce []byte, key *[32]byte) {
|
||||
|
||||
v00 := uint32(0x61707865)
|
||||
v01 := uint32(0x3320646e)
|
||||
v02 := uint32(0x79622d32)
|
||||
v03 := uint32(0x6b206574)
|
||||
|
||||
v04 := binary.LittleEndian.Uint32(key[0:])
|
||||
v05 := binary.LittleEndian.Uint32(key[4:])
|
||||
v06 := binary.LittleEndian.Uint32(key[8:])
|
||||
v07 := binary.LittleEndian.Uint32(key[12:])
|
||||
v08 := binary.LittleEndian.Uint32(key[16:])
|
||||
v09 := binary.LittleEndian.Uint32(key[20:])
|
||||
v10 := binary.LittleEndian.Uint32(key[24:])
|
||||
v11 := binary.LittleEndian.Uint32(key[28:])
|
||||
v12 := binary.LittleEndian.Uint32(nonce[0:])
|
||||
v13 := binary.LittleEndian.Uint32(nonce[4:])
|
||||
v14 := binary.LittleEndian.Uint32(nonce[8:])
|
||||
v15 := binary.LittleEndian.Uint32(nonce[12:])
|
||||
|
||||
for i := 0; i < 20; i += 2 {
|
||||
v00 += v04
|
||||
v12 ^= v00
|
||||
v12 = (v12 << 16) | (v12 >> 16)
|
||||
v08 += v12
|
||||
v04 ^= v08
|
||||
v04 = (v04 << 12) | (v04 >> 20)
|
||||
v00 += v04
|
||||
v12 ^= v00
|
||||
v12 = (v12 << 8) | (v12 >> 24)
|
||||
v08 += v12
|
||||
v04 ^= v08
|
||||
v04 = (v04 << 7) | (v04 >> 25)
|
||||
v01 += v05
|
||||
v13 ^= v01
|
||||
v13 = (v13 << 16) | (v13 >> 16)
|
||||
v09 += v13
|
||||
v05 ^= v09
|
||||
v05 = (v05 << 12) | (v05 >> 20)
|
||||
v01 += v05
|
||||
v13 ^= v01
|
||||
v13 = (v13 << 8) | (v13 >> 24)
|
||||
v09 += v13
|
||||
v05 ^= v09
|
||||
v05 = (v05 << 7) | (v05 >> 25)
|
||||
v02 += v06
|
||||
v14 ^= v02
|
||||
v14 = (v14 << 16) | (v14 >> 16)
|
||||
v10 += v14
|
||||
v06 ^= v10
|
||||
v06 = (v06 << 12) | (v06 >> 20)
|
||||
v02 += v06
|
||||
v14 ^= v02
|
||||
v14 = (v14 << 8) | (v14 >> 24)
|
||||
v10 += v14
|
||||
v06 ^= v10
|
||||
v06 = (v06 << 7) | (v06 >> 25)
|
||||
v03 += v07
|
||||
v15 ^= v03
|
||||
v15 = (v15 << 16) | (v15 >> 16)
|
||||
v11 += v15
|
||||
v07 ^= v11
|
||||
v07 = (v07 << 12) | (v07 >> 20)
|
||||
v03 += v07
|
||||
v15 ^= v03
|
||||
v15 = (v15 << 8) | (v15 >> 24)
|
||||
v11 += v15
|
||||
v07 ^= v11
|
||||
v07 = (v07 << 7) | (v07 >> 25)
|
||||
v00 += v05
|
||||
v15 ^= v00
|
||||
v15 = (v15 << 16) | (v15 >> 16)
|
||||
v10 += v15
|
||||
v05 ^= v10
|
||||
v05 = (v05 << 12) | (v05 >> 20)
|
||||
v00 += v05
|
||||
v15 ^= v00
|
||||
v15 = (v15 << 8) | (v15 >> 24)
|
||||
v10 += v15
|
||||
v05 ^= v10
|
||||
v05 = (v05 << 7) | (v05 >> 25)
|
||||
v01 += v06
|
||||
v12 ^= v01
|
||||
v12 = (v12 << 16) | (v12 >> 16)
|
||||
v11 += v12
|
||||
v06 ^= v11
|
||||
v06 = (v06 << 12) | (v06 >> 20)
|
||||
v01 += v06
|
||||
v12 ^= v01
|
||||
v12 = (v12 << 8) | (v12 >> 24)
|
||||
v11 += v12
|
||||
v06 ^= v11
|
||||
v06 = (v06 << 7) | (v06 >> 25)
|
||||
v02 += v07
|
||||
v13 ^= v02
|
||||
v13 = (v13 << 16) | (v13 >> 16)
|
||||
v08 += v13
|
||||
v07 ^= v08
|
||||
v07 = (v07 << 12) | (v07 >> 20)
|
||||
v02 += v07
|
||||
v13 ^= v02
|
||||
v13 = (v13 << 8) | (v13 >> 24)
|
||||
v08 += v13
|
||||
v07 ^= v08
|
||||
v07 = (v07 << 7) | (v07 >> 25)
|
||||
v03 += v04
|
||||
v14 ^= v03
|
||||
v14 = (v14 << 16) | (v14 >> 16)
|
||||
v09 += v14
|
||||
v04 ^= v09
|
||||
v04 = (v04 << 12) | (v04 >> 20)
|
||||
v03 += v04
|
||||
v14 ^= v03
|
||||
v14 = (v14 << 8) | (v14 >> 24)
|
||||
v09 += v14
|
||||
v04 ^= v09
|
||||
v04 = (v04 << 7) | (v04 >> 25)
|
||||
}
|
||||
|
||||
binary.LittleEndian.PutUint32(out[0:], v00)
|
||||
binary.LittleEndian.PutUint32(out[4:], v01)
|
||||
binary.LittleEndian.PutUint32(out[8:], v02)
|
||||
binary.LittleEndian.PutUint32(out[12:], v03)
|
||||
binary.LittleEndian.PutUint32(out[16:], v12)
|
||||
binary.LittleEndian.PutUint32(out[20:], v13)
|
||||
binary.LittleEndian.PutUint32(out[24:], v14)
|
||||
binary.LittleEndian.PutUint32(out[28:], v15)
|
||||
}
|
||||
|
||||
func Encrypt(
|
||||
dst []byte,
|
||||
nonceFull *[24]byte,
|
||||
plaintext []byte,
|
||||
additionalData []byte,
|
||||
key *[chacha20poly1305.KeySize]byte,
|
||||
) []byte {
|
||||
var nonce [chacha20poly1305.NonceSize]byte
|
||||
var derivedKey [chacha20poly1305.KeySize]byte
|
||||
hChaCha20(&derivedKey, nonceFull[:16], key)
|
||||
aead, _ := chacha20poly1305.New(derivedKey[:])
|
||||
copy(nonce[4:], nonceFull[16:])
|
||||
return aead.Seal(dst, nonce[:], plaintext, additionalData)
|
||||
}
|
||||
|
||||
func Decrypt(
|
||||
dst []byte,
|
||||
nonceFull *[24]byte,
|
||||
plaintext []byte,
|
||||
additionalData []byte,
|
||||
key *[chacha20poly1305.KeySize]byte,
|
||||
) ([]byte, error) {
|
||||
var nonce [chacha20poly1305.NonceSize]byte
|
||||
var derivedKey [chacha20poly1305.KeySize]byte
|
||||
hChaCha20(&derivedKey, nonceFull[:16], key)
|
||||
aead, _ := chacha20poly1305.New(derivedKey[:])
|
||||
copy(nonce[4:], nonceFull[16:])
|
||||
return aead.Open(dst, nonce[:], plaintext, additionalData)
|
||||
}
|
||||
@@ -1,101 +0,0 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0
|
||||
*
|
||||
* Copyright (C) 2017-2018 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package xchacha20poly1305
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"testing"
|
||||
)
|
||||
|
||||
type XChaCha20Test struct {
|
||||
Nonce string
|
||||
Key string
|
||||
PT string
|
||||
CT string
|
||||
}
|
||||
|
||||
func TestXChaCha20(t *testing.T) {
|
||||
|
||||
tests := []XChaCha20Test{
|
||||
{
|
||||
Nonce: "000000000000000000000000000000000000000000000000",
|
||||
Key: "0000000000000000000000000000000000000000000000000000000000000000",
|
||||
PT: "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
|
||||
CT: "789e9689e5208d7fd9e1f3c5b5341f48ef18a13e418998addadd97a3693a987f8e82ecd5c1433bfed1af49750c0f1ff29c4174a05b119aa3a9e8333812e0c0feb1299c5949d895ee01dbf50f8395dd84",
|
||||
},
|
||||
{
|
||||
Nonce: "0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f",
|
||||
Key: "0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f",
|
||||
PT: "0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f",
|
||||
CT: "e1a046aa7f71e2af8b80b6408b2fd8d3a350278cde79c94d9efaa475e1339b3dd490127b",
|
||||
},
|
||||
{
|
||||
Nonce: "d9a8213e8a697508805c2c171ad54487ead9e3e02d82d5bc",
|
||||
Key: "979196dbd78526f2f584f7534db3f5824d8ccfa858ca7e09bdd3656ecd36033c",
|
||||
PT: "43cc6d624e451bbed952c3e071dc6c03392ce11eb14316a94b2fdc98b22fedea",
|
||||
CT: "53c1e8bef2dbb8f2505ec010a7afe21d5a8e6dd8f987e4ea1a2ed5dfbc844ea400db34496fd2153526c6e87c36694200",
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
|
||||
nonce, err := hex.DecodeString(test.Nonce)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
key, err := hex.DecodeString(test.Key)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
pt, err := hex.DecodeString(test.PT)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
func() {
|
||||
var nonceArray [24]byte
|
||||
var keyArray [32]byte
|
||||
copy(nonceArray[:], nonce)
|
||||
copy(keyArray[:], key)
|
||||
|
||||
// test encryption
|
||||
|
||||
ct := Encrypt(
|
||||
nil,
|
||||
&nonceArray,
|
||||
pt,
|
||||
nil,
|
||||
&keyArray,
|
||||
)
|
||||
ctHex := hex.EncodeToString(ct)
|
||||
if ctHex != test.CT {
|
||||
t.Fatal("encryption failed, expected:", test.CT, "got", ctHex)
|
||||
}
|
||||
|
||||
// test decryption
|
||||
|
||||
ptp, err := Decrypt(
|
||||
nil,
|
||||
&nonceArray,
|
||||
ct,
|
||||
nil,
|
||||
&keyArray,
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
ptHex := hex.EncodeToString(ptp)
|
||||
if ptHex != test.PT {
|
||||
t.Fatal("decryption failed, expected:", test.PT, "got", ptHex)
|
||||
}
|
||||
}()
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user