Page MenuHomeFreeBSD

D36952.id111712.diff
No OneTemporary

D36952.id111712.diff

This file is larger than 256 KB, so syntax highlighting was skipped.
diff --git a/contrib/wireguard-tools/.gitattributes b/contrib/wireguard-tools/.gitattributes
new file mode 100644
--- /dev/null
+++ b/contrib/wireguard-tools/.gitattributes
@@ -0,0 +1,2 @@
+.gitattributes export-ignore
+.gitignore export-ignore
diff --git a/contrib/wireguard-tools/.gitignore b/contrib/wireguard-tools/.gitignore
new file mode 100644
--- /dev/null
+++ b/contrib/wireguard-tools/.gitignore
@@ -0,0 +1,14 @@
+cscope.out
+*.o
+*.d
+*.dwo
+src/wg
+src/wg.exe
+*.swp
+*.id0
+*.id1
+*.id2
+*.nam
+*.til
+*.pro.user
+maint/
diff --git a/contrib/wireguard-tools/COPYING b/contrib/wireguard-tools/COPYING
new file mode 100644
--- /dev/null
+++ b/contrib/wireguard-tools/COPYING
@@ -0,0 +1,339 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ 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.
+
+ 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 as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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.
diff --git a/contrib/wireguard-tools/README.md b/contrib/wireguard-tools/README.md
new file mode 100644
--- /dev/null
+++ b/contrib/wireguard-tools/README.md
@@ -0,0 +1,78 @@
+# [wireguard-tools](https://git.zx2c4.com/wireguard-tools/about/) &mdash; tools for configuring [WireGuard](https://www.wireguard.com/)
+
+This supplies the main userspace tooling for using and configuring WireGuard
+tunnels, including the
+[`wg(8)`](https://git.zx2c4.com/wireguard-tools/about/src/man/wg.8) and
+[`wg-quick(8)`](https://git.zx2c4.com/wireguard-tools/about/src/man/wg-quick.8)
+utilities. This project supports Linux, OpenBSD, FreeBSD, macOS, Windows, and
+Android.
+
+**More information may be found at [WireGuard.com](https://www.wireguard.com/).**
+
+## Building
+
+ $ cd src
+ $ make
+
+There are no dependencies other than a good C compiler and a sane libc.
+
+## Installing
+
+ # make install
+
+This command takes into account several environment variables:
+
+ * `PREFIX` default: `/usr`
+ * `DESTDIR` default:
+ * `BINDIR` default: `$(PREFIX)/bin`
+ * `LIBDIR` default: `$(PREFIX)/lib`
+ * `MANDIR` default: `$(PREFIX)/share/man`
+ * `BASHCOMPDIR` default: `$(PREFIX)/share/bash-completion/completions`
+ * `RUNSTATEDIR` default: `/var/run`
+ * `PKG_CONFIG` default: `pkg-config`
+
+ * `WITH_BASHCOMPLETION` default: [auto-detect]
+ * `WITH_WGQUICK` default: [auto-detect]
+ * `WITH_SYSTEMDUNITS` default: [auto-detect]
+ * `DEBUG` default:
+
+The first section is rather standard. The second section is not:
+
+ * `WITH_BASHCOMPLETION` decides whether or not bash completion files for the
+ tools are installed. This is just a nice thing for people who have bash.
+ If you don't have bash, or don't want this, set the environment variable
+ to `no`. If you'd like to force its use, even if bash-completion isn't
+ detected in `DESTDIR`, then set it to `yes`.
+
+ * `WITH_WGQUICK` decides whether or not the wg-quick(8) script is installed.
+ This is a very quick and dirty bash script for reading a few extra
+ variables from wg(8)-style configuration files, and automatically
+ configures the interface. If you don't have bash, you probably don't want
+ this at all. Likewise, if you already have a working network management
+ tool or configuration, you probably want to integrate wg(8) or the direct
+ WireGuard API into your network manager, rather than using wg-quick(8).
+ But for folks who like simple quick and dirty scripts, this is nice. If you'd
+ like to force its use, even if bash isn't detected in DESTDIR, then set it
+ to `yes`.
+
+ * `WITH_SYSTEMDUNITS` decides whether or not systemd units are installed for
+ wg-quick(8). If you don't use systemd, you certainly don't want this, and
+ should set it to `no`. If systemd isn't auto-detected, but you still would
+ like to install it, set this to `yes`.
+
+ * `DEBUG` decides whether to build with `-g`, when set to `yes`.
+
+If you're a simple `make && make install` kind of user, you can get away with
+not setting these variables and relying on the auto-detection. However, if
+you're writing a package for a distro, you'll want to explicitly set these,
+depending on what you want.
+
+## `contrib/`
+
+The `contrib/` subdirectory contains various scripts and examples. Most of these
+are not immediately useful for production use, but should provide inspiration for
+creating fully-featured tools. See the `README` in each directory.
+
+## License
+
+This project is released under the [GPLv2](COPYING).
diff --git a/contrib/wireguard-tools/contrib/dns-hatchet/README b/contrib/wireguard-tools/contrib/dns-hatchet/README
new file mode 100644
--- /dev/null
+++ b/contrib/wireguard-tools/contrib/dns-hatchet/README
@@ -0,0 +1,8 @@
+The DNS Hatchet
+===============
+
+This is a workaround for distributions without resolvconf or any proper
+mechanism of setting the DNS. Running 'apply.sh` in this directory will
+insert 'hatchet.bash` into the right place in 'wg-quick.bash`. It is
+recommended that distributions without any resolvconf available run this
+before calling 'make install` in their packaging scripts.
diff --git a/contrib/wireguard-tools/contrib/dns-hatchet/apply.sh b/contrib/wireguard-tools/contrib/dns-hatchet/apply.sh
new file mode 100755
--- /dev/null
+++ b/contrib/wireguard-tools/contrib/dns-hatchet/apply.sh
@@ -0,0 +1,9 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+#
+# Copyright (C) 2015-2020 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
+
+ME="$(readlink -f "$(dirname "$(readlink -f "$0")")")"
+TOOLS="$ME/../../src"
+
+sed -i "/~~ function override insertion point ~~/r $ME/hatchet.bash" "$TOOLS/wg-quick/linux.bash"
diff --git a/contrib/wireguard-tools/contrib/dns-hatchet/hatchet.bash b/contrib/wireguard-tools/contrib/dns-hatchet/hatchet.bash
new file mode 100644
--- /dev/null
+++ b/contrib/wireguard-tools/contrib/dns-hatchet/hatchet.bash
@@ -0,0 +1,44 @@
+set_dns() {
+ [[ ${#DNS[@]} -gt 0 ]] || return 0
+
+ if [[ $(resolvconf --version 2>/dev/null) == openresolv\ * ]]; then
+ { printf 'nameserver %s\n' "${DNS[@]}"
+ [[ ${#DNS_SEARCH[@]} -eq 0 ]] || printf 'search %s\n' "${DNS_SEARCH[*]}"
+ } | cmd resolvconf -a "$INTERFACE" -m 0 -x
+ else
+ echo "[#] mount \`${DNS[*]}' /etc/resolv.conf" >&2
+ [[ -e /etc/resolv.conf ]] || touch /etc/resolv.conf
+ { cat <<-_EOF
+ # This file was generated by wg-quick(8) for use with
+ # the WireGuard interface $INTERFACE. It cannot be
+ # removed or altered directly. You may remove this file
+ # by running \`wg-quick down $INTERFACE', or if that
+ # poses problems, run \`umount /etc/resolv.conf'.
+
+ _EOF
+ printf 'nameserver %s\n' "${DNS[@]}"
+ [[ ${#DNS_SEARCH[@]} -eq 0 ]] || printf 'search %s\n' "${DNS_SEARCH[*]}"
+ } | unshare -m --propagation shared bash -c "$(cat <<-_EOF
+ set -e
+ context="\$(stat -c %C /etc/resolv.conf 2>/dev/null)" || unset context
+ mount --make-private /dev/shm
+ mount -t tmpfs none /dev/shm
+ cat > /dev/shm/resolv.conf
+ [[ -z \$context || \$context == "?" ]] || chcon "\$context" /dev/shm/resolv.conf 2>/dev/null || true
+ mount -o remount,ro /dev/shm
+ mount -o bind,ro /dev/shm/resolv.conf /etc/resolv.conf
+ _EOF
+ )"
+ fi
+ HAVE_SET_DNS=1
+}
+
+unset_dns() {
+ [[ ${#DNS[@]} -gt 0 ]] || return 0
+
+ if [[ $(resolvconf --version 2>/dev/null) == openresolv\ * ]]; then
+ cmd resolvconf -d "$INTERFACE"
+ else
+ cmd umount /etc/resolv.conf
+ fi
+}
diff --git a/contrib/wireguard-tools/contrib/embeddable-wg-library/.gitignore b/contrib/wireguard-tools/contrib/embeddable-wg-library/.gitignore
new file mode 100644
--- /dev/null
+++ b/contrib/wireguard-tools/contrib/embeddable-wg-library/.gitignore
@@ -0,0 +1 @@
+test
diff --git a/contrib/wireguard-tools/contrib/embeddable-wg-library/Makefile b/contrib/wireguard-tools/contrib/embeddable-wg-library/Makefile
new file mode 100644
--- /dev/null
+++ b/contrib/wireguard-tools/contrib/embeddable-wg-library/Makefile
@@ -0,0 +1,7 @@
+CFLAGS += -Wall
+
+test: test.c wireguard.c wireguard.h
+
+clean:
+ rm -f test
+.PHONY: clean
diff --git a/contrib/wireguard-tools/contrib/embeddable-wg-library/README b/contrib/wireguard-tools/contrib/embeddable-wg-library/README
new file mode 100644
--- /dev/null
+++ b/contrib/wireguard-tools/contrib/embeddable-wg-library/README
@@ -0,0 +1,23 @@
+Embeddable WireGuard C Library
+==============================
+
+This is a mini single-file library, meant to be embedded directly into the
+source code of your program. It is *not* meant to be built as a shared
+library.
+
+
+Usage
+-----
+
+Copy wireguard.c and wireguard.h into your project. They should build with
+any C89 compiler. There are no dependencies except libc.
+
+Please see the set of simple functions in wireguard.h for information on
+how to use, as well as the example code in test.c.
+
+
+License
+-------
+
+Because this uses code from libmnl, wireguard.c and wireguard.h are licensed
+under the LGPL-2.1+.
diff --git a/contrib/wireguard-tools/contrib/embeddable-wg-library/test.c b/contrib/wireguard-tools/contrib/embeddable-wg-library/test.c
new file mode 100644
--- /dev/null
+++ b/contrib/wireguard-tools/contrib/embeddable-wg-library/test.c
@@ -0,0 +1,80 @@
+// SPDX-License-Identifier: LGPL-2.1+
+/*
+ * Copyright (C) 2015-2020 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
+ */
+
+#include "wireguard.h"
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+void list_devices(void)
+{
+ char *device_names, *device_name;
+ size_t len;
+
+ device_names = wg_list_device_names();
+ if (!device_names) {
+ perror("Unable to get device names");
+ exit(1);
+ }
+ wg_for_each_device_name(device_names, device_name, len) {
+ wg_device *device;
+ wg_peer *peer;
+ wg_key_b64_string key;
+
+ if (wg_get_device(&device, device_name) < 0) {
+ perror("Unable to get device");
+ continue;
+ }
+ if (device->flags & WGDEVICE_HAS_PUBLIC_KEY) {
+ wg_key_to_base64(key, device->public_key);
+ printf("%s has public key %s\n", device_name, key);
+ } else
+ printf("%s has no public key\n", device_name);
+ wg_for_each_peer(device, peer) {
+ wg_key_to_base64(key, peer->public_key);
+ printf(" - peer %s\n", key);
+ }
+ wg_free_device(device);
+ }
+ free(device_names);
+}
+
+int main(int argc, char *argv[])
+{
+ wg_peer new_peer = {
+ .flags = WGPEER_HAS_PUBLIC_KEY | WGPEER_REPLACE_ALLOWEDIPS
+ };
+ wg_device new_device = {
+ .name = "wgtest0",
+ .listen_port = 1234,
+ .flags = WGDEVICE_HAS_PRIVATE_KEY | WGDEVICE_HAS_LISTEN_PORT,
+ .first_peer = &new_peer,
+ .last_peer = &new_peer
+ };
+ wg_key temp_private_key;
+
+ wg_generate_private_key(temp_private_key);
+ wg_generate_public_key(new_peer.public_key, temp_private_key);
+ wg_generate_private_key(new_device.private_key);
+
+ if (wg_add_device(new_device.name) < 0) {
+ perror("Unable to add device");
+ exit(1);
+ }
+
+ if (wg_set_device(&new_device) < 0) {
+ perror("Unable to set device");
+ exit(1);
+ }
+
+ list_devices();
+
+ if (wg_del_device(new_device.name) < 0) {
+ perror("Unable to delete device");
+ exit(1);
+ }
+
+ return 0;
+}
diff --git a/contrib/wireguard-tools/contrib/embeddable-wg-library/wireguard.h b/contrib/wireguard-tools/contrib/embeddable-wg-library/wireguard.h
new file mode 100644
--- /dev/null
+++ b/contrib/wireguard-tools/contrib/embeddable-wg-library/wireguard.h
@@ -0,0 +1,105 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+/*
+ * Copyright (C) 2015-2020 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
+ */
+
+#ifndef WIREGUARD_H
+#define WIREGUARD_H
+
+#include <net/if.h>
+#include <netinet/in.h>
+#include <sys/socket.h>
+#include <time.h>
+#include <stdint.h>
+#include <stdbool.h>
+
+typedef uint8_t wg_key[32];
+typedef char wg_key_b64_string[((sizeof(wg_key) + 2) / 3) * 4 + 1];
+
+/* Cross platform __kernel_timespec */
+struct timespec64 {
+ int64_t tv_sec;
+ int64_t tv_nsec;
+};
+
+typedef struct wg_allowedip {
+ uint16_t family;
+ union {
+ struct in_addr ip4;
+ struct in6_addr ip6;
+ };
+ uint8_t cidr;
+ struct wg_allowedip *next_allowedip;
+} wg_allowedip;
+
+enum wg_peer_flags {
+ WGPEER_REMOVE_ME = 1U << 0,
+ WGPEER_REPLACE_ALLOWEDIPS = 1U << 1,
+ WGPEER_HAS_PUBLIC_KEY = 1U << 2,
+ WGPEER_HAS_PRESHARED_KEY = 1U << 3,
+ WGPEER_HAS_PERSISTENT_KEEPALIVE_INTERVAL = 1U << 4
+};
+
+typedef union wg_endpoint {
+ struct sockaddr addr;
+ struct sockaddr_in addr4;
+ struct sockaddr_in6 addr6;
+} wg_endpoint;
+
+typedef struct wg_peer {
+ enum wg_peer_flags flags;
+
+ wg_key public_key;
+ wg_key preshared_key;
+
+ wg_endpoint endpoint;
+
+ struct timespec64 last_handshake_time;
+ uint64_t rx_bytes, tx_bytes;
+ uint16_t persistent_keepalive_interval;
+
+ struct wg_allowedip *first_allowedip, *last_allowedip;
+ struct wg_peer *next_peer;
+} wg_peer;
+
+enum wg_device_flags {
+ WGDEVICE_REPLACE_PEERS = 1U << 0,
+ WGDEVICE_HAS_PRIVATE_KEY = 1U << 1,
+ WGDEVICE_HAS_PUBLIC_KEY = 1U << 2,
+ WGDEVICE_HAS_LISTEN_PORT = 1U << 3,
+ WGDEVICE_HAS_FWMARK = 1U << 4
+};
+
+typedef struct wg_device {
+ char name[IFNAMSIZ];
+ uint32_t ifindex;
+
+ enum wg_device_flags flags;
+
+ wg_key public_key;
+ wg_key private_key;
+
+ uint32_t fwmark;
+ uint16_t listen_port;
+
+ struct wg_peer *first_peer, *last_peer;
+} wg_device;
+
+#define wg_for_each_device_name(__names, __name, __len) for ((__name) = (__names), (__len) = 0; ((__len) = strlen(__name)); (__name) += (__len) + 1)
+#define wg_for_each_peer(__dev, __peer) for ((__peer) = (__dev)->first_peer; (__peer); (__peer) = (__peer)->next_peer)
+#define wg_for_each_allowedip(__peer, __allowedip) for ((__allowedip) = (__peer)->first_allowedip; (__allowedip); (__allowedip) = (__allowedip)->next_allowedip)
+
+int wg_set_device(wg_device *dev);
+int wg_get_device(wg_device **dev, const char *device_name);
+int wg_add_device(const char *device_name);
+int wg_del_device(const char *device_name);
+void wg_free_device(wg_device *dev);
+char *wg_list_device_names(void); /* first\0second\0third\0forth\0last\0\0 */
+void wg_key_to_base64(wg_key_b64_string base64, const wg_key key);
+int wg_key_from_base64(wg_key key, const wg_key_b64_string base64);
+bool wg_key_is_zero(const wg_key key);
+void wg_generate_public_key(wg_key public_key, const wg_key private_key);
+void wg_generate_private_key(wg_key private_key);
+void wg_generate_preshared_key(wg_key preshared_key);
+
+#endif
diff --git a/contrib/wireguard-tools/contrib/embeddable-wg-library/wireguard.c b/contrib/wireguard-tools/contrib/embeddable-wg-library/wireguard.c
new file mode 100644
--- /dev/null
+++ b/contrib/wireguard-tools/contrib/embeddable-wg-library/wireguard.c
@@ -0,0 +1,1755 @@
+// SPDX-License-Identifier: LGPL-2.1+
+/*
+ * Copyright (C) 2015-2020 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
+ * Copyright (C) 2008-2012 Pablo Neira Ayuso <pablo@netfilter.org>.
+ */
+
+#define _GNU_SOURCE
+
+#include <errno.h>
+#include <linux/genetlink.h>
+#include <linux/if_link.h>
+#include <linux/netlink.h>
+#include <linux/rtnetlink.h>
+#include <netinet/in.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <time.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <assert.h>
+
+#include "wireguard.h"
+
+/* wireguard.h netlink uapi: */
+
+#define WG_GENL_NAME "wireguard"
+#define WG_GENL_VERSION 1
+
+enum wg_cmd {
+ WG_CMD_GET_DEVICE,
+ WG_CMD_SET_DEVICE,
+ __WG_CMD_MAX
+};
+
+enum wgdevice_flag {
+ WGDEVICE_F_REPLACE_PEERS = 1U << 0
+};
+enum wgdevice_attribute {
+ WGDEVICE_A_UNSPEC,
+ WGDEVICE_A_IFINDEX,
+ WGDEVICE_A_IFNAME,
+ WGDEVICE_A_PRIVATE_KEY,
+ WGDEVICE_A_PUBLIC_KEY,
+ WGDEVICE_A_FLAGS,
+ WGDEVICE_A_LISTEN_PORT,
+ WGDEVICE_A_FWMARK,
+ WGDEVICE_A_PEERS,
+ __WGDEVICE_A_LAST
+};
+
+enum wgpeer_flag {
+ WGPEER_F_REMOVE_ME = 1U << 0,
+ WGPEER_F_REPLACE_ALLOWEDIPS = 1U << 1
+};
+enum wgpeer_attribute {
+ WGPEER_A_UNSPEC,
+ WGPEER_A_PUBLIC_KEY,
+ WGPEER_A_PRESHARED_KEY,
+ WGPEER_A_FLAGS,
+ WGPEER_A_ENDPOINT,
+ WGPEER_A_PERSISTENT_KEEPALIVE_INTERVAL,
+ WGPEER_A_LAST_HANDSHAKE_TIME,
+ WGPEER_A_RX_BYTES,
+ WGPEER_A_TX_BYTES,
+ WGPEER_A_ALLOWEDIPS,
+ WGPEER_A_PROTOCOL_VERSION,
+ __WGPEER_A_LAST
+};
+
+enum wgallowedip_attribute {
+ WGALLOWEDIP_A_UNSPEC,
+ WGALLOWEDIP_A_FAMILY,
+ WGALLOWEDIP_A_IPADDR,
+ WGALLOWEDIP_A_CIDR_MASK,
+ __WGALLOWEDIP_A_LAST
+};
+
+/* libmnl mini library: */
+
+#define MNL_SOCKET_AUTOPID 0
+#define MNL_ALIGNTO 4
+#define MNL_ALIGN(len) (((len)+MNL_ALIGNTO-1) & ~(MNL_ALIGNTO-1))
+#define MNL_NLMSG_HDRLEN MNL_ALIGN(sizeof(struct nlmsghdr))
+#define MNL_ATTR_HDRLEN MNL_ALIGN(sizeof(struct nlattr))
+
+enum mnl_attr_data_type {
+ MNL_TYPE_UNSPEC,
+ MNL_TYPE_U8,
+ MNL_TYPE_U16,
+ MNL_TYPE_U32,
+ MNL_TYPE_U64,
+ MNL_TYPE_STRING,
+ MNL_TYPE_FLAG,
+ MNL_TYPE_MSECS,
+ MNL_TYPE_NESTED,
+ MNL_TYPE_NESTED_COMPAT,
+ MNL_TYPE_NUL_STRING,
+ MNL_TYPE_BINARY,
+ MNL_TYPE_MAX,
+};
+
+#define mnl_attr_for_each(attr, nlh, offset) \
+ for ((attr) = mnl_nlmsg_get_payload_offset((nlh), (offset)); \
+ mnl_attr_ok((attr), (char *)mnl_nlmsg_get_payload_tail(nlh) - (char *)(attr)); \
+ (attr) = mnl_attr_next(attr))
+
+#define mnl_attr_for_each_nested(attr, nest) \
+ for ((attr) = mnl_attr_get_payload(nest); \
+ mnl_attr_ok((attr), (char *)mnl_attr_get_payload(nest) + mnl_attr_get_payload_len(nest) - (char *)(attr)); \
+ (attr) = mnl_attr_next(attr))
+
+#define mnl_attr_for_each_payload(payload, payload_size) \
+ for ((attr) = (payload); \
+ mnl_attr_ok((attr), (char *)(payload) + payload_size - (char *)(attr)); \
+ (attr) = mnl_attr_next(attr))
+
+#define MNL_CB_ERROR -1
+#define MNL_CB_STOP 0
+#define MNL_CB_OK 1
+
+typedef int (*mnl_attr_cb_t)(const struct nlattr *attr, void *data);
+typedef int (*mnl_cb_t)(const struct nlmsghdr *nlh, void *data);
+
+#ifndef MNL_ARRAY_SIZE
+#define MNL_ARRAY_SIZE(a) (sizeof(a)/sizeof((a)[0]))
+#endif
+
+static size_t mnl_ideal_socket_buffer_size(void)
+{
+ static size_t size = 0;
+
+ if (size)
+ return size;
+ size = (size_t)sysconf(_SC_PAGESIZE);
+ if (size > 8192)
+ size = 8192;
+ return size;
+}
+
+static size_t mnl_nlmsg_size(size_t len)
+{
+ return len + MNL_NLMSG_HDRLEN;
+}
+
+static struct nlmsghdr *mnl_nlmsg_put_header(void *buf)
+{
+ int len = MNL_ALIGN(sizeof(struct nlmsghdr));
+ struct nlmsghdr *nlh = buf;
+
+ memset(buf, 0, len);
+ nlh->nlmsg_len = len;
+ return nlh;
+}
+
+static void *mnl_nlmsg_put_extra_header(struct nlmsghdr *nlh, size_t size)
+{
+ char *ptr = (char *)nlh + nlh->nlmsg_len;
+ size_t len = MNL_ALIGN(size);
+ nlh->nlmsg_len += len;
+ memset(ptr, 0, len);
+ return ptr;
+}
+
+static void *mnl_nlmsg_get_payload(const struct nlmsghdr *nlh)
+{
+ return (void *)nlh + MNL_NLMSG_HDRLEN;
+}
+
+static void *mnl_nlmsg_get_payload_offset(const struct nlmsghdr *nlh, size_t offset)
+{
+ return (void *)nlh + MNL_NLMSG_HDRLEN + MNL_ALIGN(offset);
+}
+
+static bool mnl_nlmsg_ok(const struct nlmsghdr *nlh, int len)
+{
+ return len >= (int)sizeof(struct nlmsghdr) &&
+ nlh->nlmsg_len >= sizeof(struct nlmsghdr) &&
+ (int)nlh->nlmsg_len <= len;
+}
+
+static struct nlmsghdr *mnl_nlmsg_next(const struct nlmsghdr *nlh, int *len)
+{
+ *len -= MNL_ALIGN(nlh->nlmsg_len);
+ return (struct nlmsghdr *)((void *)nlh + MNL_ALIGN(nlh->nlmsg_len));
+}
+
+static void *mnl_nlmsg_get_payload_tail(const struct nlmsghdr *nlh)
+{
+ return (void *)nlh + MNL_ALIGN(nlh->nlmsg_len);
+}
+
+static bool mnl_nlmsg_seq_ok(const struct nlmsghdr *nlh, unsigned int seq)
+{
+ return nlh->nlmsg_seq && seq ? nlh->nlmsg_seq == seq : true;
+}
+
+static bool mnl_nlmsg_portid_ok(const struct nlmsghdr *nlh, unsigned int portid)
+{
+ return nlh->nlmsg_pid && portid ? nlh->nlmsg_pid == portid : true;
+}
+
+static uint16_t mnl_attr_get_type(const struct nlattr *attr)
+{
+ return attr->nla_type & NLA_TYPE_MASK;
+}
+
+static uint16_t mnl_attr_get_payload_len(const struct nlattr *attr)
+{
+ return attr->nla_len - MNL_ATTR_HDRLEN;
+}
+
+static void *mnl_attr_get_payload(const struct nlattr *attr)
+{
+ return (void *)attr + MNL_ATTR_HDRLEN;
+}
+
+static bool mnl_attr_ok(const struct nlattr *attr, int len)
+{
+ return len >= (int)sizeof(struct nlattr) &&
+ attr->nla_len >= sizeof(struct nlattr) &&
+ (int)attr->nla_len <= len;
+}
+
+static struct nlattr *mnl_attr_next(const struct nlattr *attr)
+{
+ return (struct nlattr *)((void *)attr + MNL_ALIGN(attr->nla_len));
+}
+
+static int mnl_attr_type_valid(const struct nlattr *attr, uint16_t max)
+{
+ if (mnl_attr_get_type(attr) > max) {
+ errno = EOPNOTSUPP;
+ return -1;
+ }
+ return 1;
+}
+
+static int __mnl_attr_validate(const struct nlattr *attr,
+ enum mnl_attr_data_type type, size_t exp_len)
+{
+ uint16_t attr_len = mnl_attr_get_payload_len(attr);
+ const char *attr_data = mnl_attr_get_payload(attr);
+
+ if (attr_len < exp_len) {
+ errno = ERANGE;
+ return -1;
+ }
+ switch(type) {
+ case MNL_TYPE_FLAG:
+ if (attr_len > 0) {
+ errno = ERANGE;
+ return -1;
+ }
+ break;
+ case MNL_TYPE_NUL_STRING:
+ if (attr_len == 0) {
+ errno = ERANGE;
+ return -1;
+ }
+ if (attr_data[attr_len-1] != '\0') {
+ errno = EINVAL;
+ return -1;
+ }
+ break;
+ case MNL_TYPE_STRING:
+ if (attr_len == 0) {
+ errno = ERANGE;
+ return -1;
+ }
+ break;
+ case MNL_TYPE_NESTED:
+
+ if (attr_len == 0)
+ break;
+
+ if (attr_len < MNL_ATTR_HDRLEN) {
+ errno = ERANGE;
+ return -1;
+ }
+ break;
+ default:
+
+ break;
+ }
+ if (exp_len && attr_len > exp_len) {
+ errno = ERANGE;
+ return -1;
+ }
+ return 0;
+}
+
+static const size_t mnl_attr_data_type_len[MNL_TYPE_MAX] = {
+ [MNL_TYPE_U8] = sizeof(uint8_t),
+ [MNL_TYPE_U16] = sizeof(uint16_t),
+ [MNL_TYPE_U32] = sizeof(uint32_t),
+ [MNL_TYPE_U64] = sizeof(uint64_t),
+ [MNL_TYPE_MSECS] = sizeof(uint64_t),
+};
+
+static int mnl_attr_validate(const struct nlattr *attr, enum mnl_attr_data_type type)
+{
+ int exp_len;
+
+ if (type >= MNL_TYPE_MAX) {
+ errno = EINVAL;
+ return -1;
+ }
+ exp_len = mnl_attr_data_type_len[type];
+ return __mnl_attr_validate(attr, type, exp_len);
+}
+
+static int mnl_attr_parse(const struct nlmsghdr *nlh, unsigned int offset,
+ mnl_attr_cb_t cb, void *data)
+{
+ int ret = MNL_CB_OK;
+ const struct nlattr *attr;
+
+ mnl_attr_for_each(attr, nlh, offset)
+ if ((ret = cb(attr, data)) <= MNL_CB_STOP)
+ return ret;
+ return ret;
+}
+
+static int mnl_attr_parse_nested(const struct nlattr *nested, mnl_attr_cb_t cb,
+ void *data)
+{
+ int ret = MNL_CB_OK;
+ const struct nlattr *attr;
+
+ mnl_attr_for_each_nested(attr, nested)
+ if ((ret = cb(attr, data)) <= MNL_CB_STOP)
+ return ret;
+ return ret;
+}
+
+static uint8_t mnl_attr_get_u8(const struct nlattr *attr)
+{
+ return *((uint8_t *)mnl_attr_get_payload(attr));
+}
+
+static uint16_t mnl_attr_get_u16(const struct nlattr *attr)
+{
+ return *((uint16_t *)mnl_attr_get_payload(attr));
+}
+
+static uint32_t mnl_attr_get_u32(const struct nlattr *attr)
+{
+ return *((uint32_t *)mnl_attr_get_payload(attr));
+}
+
+static uint64_t mnl_attr_get_u64(const struct nlattr *attr)
+{
+ uint64_t tmp;
+ memcpy(&tmp, mnl_attr_get_payload(attr), sizeof(tmp));
+ return tmp;
+}
+
+static const char *mnl_attr_get_str(const struct nlattr *attr)
+{
+ return mnl_attr_get_payload(attr);
+}
+
+static void mnl_attr_put(struct nlmsghdr *nlh, uint16_t type, size_t len,
+ const void *data)
+{
+ struct nlattr *attr = mnl_nlmsg_get_payload_tail(nlh);
+ uint16_t payload_len = MNL_ALIGN(sizeof(struct nlattr)) + len;
+ int pad;
+
+ attr->nla_type = type;
+ attr->nla_len = payload_len;
+ memcpy(mnl_attr_get_payload(attr), data, len);
+ nlh->nlmsg_len += MNL_ALIGN(payload_len);
+ pad = MNL_ALIGN(len) - len;
+ if (pad > 0)
+ memset(mnl_attr_get_payload(attr) + len, 0, pad);
+}
+
+static void mnl_attr_put_u16(struct nlmsghdr *nlh, uint16_t type, uint16_t data)
+{
+ mnl_attr_put(nlh, type, sizeof(uint16_t), &data);
+}
+
+static void mnl_attr_put_u32(struct nlmsghdr *nlh, uint16_t type, uint32_t data)
+{
+ mnl_attr_put(nlh, type, sizeof(uint32_t), &data);
+}
+
+static void mnl_attr_put_strz(struct nlmsghdr *nlh, uint16_t type, const char *data)
+{
+ mnl_attr_put(nlh, type, strlen(data)+1, data);
+}
+
+static struct nlattr *mnl_attr_nest_start(struct nlmsghdr *nlh, uint16_t type)
+{
+ struct nlattr *start = mnl_nlmsg_get_payload_tail(nlh);
+
+ start->nla_type = NLA_F_NESTED | type;
+ nlh->nlmsg_len += MNL_ALIGN(sizeof(struct nlattr));
+ return start;
+}
+
+static bool mnl_attr_put_check(struct nlmsghdr *nlh, size_t buflen,
+ uint16_t type, size_t len, const void *data)
+{
+ if (nlh->nlmsg_len + MNL_ATTR_HDRLEN + MNL_ALIGN(len) > buflen)
+ return false;
+ mnl_attr_put(nlh, type, len, data);
+ return true;
+}
+
+static bool mnl_attr_put_u8_check(struct nlmsghdr *nlh, size_t buflen,
+ uint16_t type, uint8_t data)
+{
+ return mnl_attr_put_check(nlh, buflen, type, sizeof(uint8_t), &data);
+}
+
+static bool mnl_attr_put_u16_check(struct nlmsghdr *nlh, size_t buflen,
+ uint16_t type, uint16_t data)
+{
+ return mnl_attr_put_check(nlh, buflen, type, sizeof(uint16_t), &data);
+}
+
+static bool mnl_attr_put_u32_check(struct nlmsghdr *nlh, size_t buflen,
+ uint16_t type, uint32_t data)
+{
+ return mnl_attr_put_check(nlh, buflen, type, sizeof(uint32_t), &data);
+}
+
+static struct nlattr *mnl_attr_nest_start_check(struct nlmsghdr *nlh, size_t buflen,
+ uint16_t type)
+{
+ if (nlh->nlmsg_len + MNL_ATTR_HDRLEN > buflen)
+ return NULL;
+ return mnl_attr_nest_start(nlh, type);
+}
+
+static void mnl_attr_nest_end(struct nlmsghdr *nlh, struct nlattr *start)
+{
+ start->nla_len = mnl_nlmsg_get_payload_tail(nlh) - (void *)start;
+}
+
+static void mnl_attr_nest_cancel(struct nlmsghdr *nlh, struct nlattr *start)
+{
+ nlh->nlmsg_len -= mnl_nlmsg_get_payload_tail(nlh) - (void *)start;
+}
+
+static int mnl_cb_noop(__attribute__((unused)) const struct nlmsghdr *nlh, __attribute__((unused)) void *data)
+{
+ return MNL_CB_OK;
+}
+
+static int mnl_cb_error(const struct nlmsghdr *nlh, __attribute__((unused)) void *data)
+{
+ const struct nlmsgerr *err = mnl_nlmsg_get_payload(nlh);
+
+ if (nlh->nlmsg_len < mnl_nlmsg_size(sizeof(struct nlmsgerr))) {
+ errno = EBADMSG;
+ return MNL_CB_ERROR;
+ }
+
+ if (err->error < 0)
+ errno = -err->error;
+ else
+ errno = err->error;
+
+ return err->error == 0 ? MNL_CB_STOP : MNL_CB_ERROR;
+}
+
+static int mnl_cb_stop(__attribute__((unused)) const struct nlmsghdr *nlh, __attribute__((unused)) void *data)
+{
+ return MNL_CB_STOP;
+}
+
+static const mnl_cb_t default_cb_array[NLMSG_MIN_TYPE] = {
+ [NLMSG_NOOP] = mnl_cb_noop,
+ [NLMSG_ERROR] = mnl_cb_error,
+ [NLMSG_DONE] = mnl_cb_stop,
+ [NLMSG_OVERRUN] = mnl_cb_noop,
+};
+
+static int __mnl_cb_run(const void *buf, size_t numbytes,
+ unsigned int seq, unsigned int portid,
+ mnl_cb_t cb_data, void *data,
+ const mnl_cb_t *cb_ctl_array,
+ unsigned int cb_ctl_array_len)
+{
+ int ret = MNL_CB_OK, len = numbytes;
+ const struct nlmsghdr *nlh = buf;
+
+ while (mnl_nlmsg_ok(nlh, len)) {
+
+ if (!mnl_nlmsg_portid_ok(nlh, portid)) {
+ errno = ESRCH;
+ return -1;
+ }
+
+ if (!mnl_nlmsg_seq_ok(nlh, seq)) {
+ errno = EPROTO;
+ return -1;
+ }
+
+ if (nlh->nlmsg_flags & NLM_F_DUMP_INTR) {
+ errno = EINTR;
+ return -1;
+ }
+
+ if (nlh->nlmsg_type >= NLMSG_MIN_TYPE) {
+ if (cb_data){
+ ret = cb_data(nlh, data);
+ if (ret <= MNL_CB_STOP)
+ goto out;
+ }
+ } else if (nlh->nlmsg_type < cb_ctl_array_len) {
+ if (cb_ctl_array && cb_ctl_array[nlh->nlmsg_type]) {
+ ret = cb_ctl_array[nlh->nlmsg_type](nlh, data);
+ if (ret <= MNL_CB_STOP)
+ goto out;
+ }
+ } else if (default_cb_array[nlh->nlmsg_type]) {
+ ret = default_cb_array[nlh->nlmsg_type](nlh, data);
+ if (ret <= MNL_CB_STOP)
+ goto out;
+ }
+ nlh = mnl_nlmsg_next(nlh, &len);
+ }
+out:
+ return ret;
+}
+
+static int mnl_cb_run2(const void *buf, size_t numbytes, unsigned int seq,
+ unsigned int portid, mnl_cb_t cb_data, void *data,
+ const mnl_cb_t *cb_ctl_array, unsigned int cb_ctl_array_len)
+{
+ return __mnl_cb_run(buf, numbytes, seq, portid, cb_data, data,
+ cb_ctl_array, cb_ctl_array_len);
+}
+
+static int mnl_cb_run(const void *buf, size_t numbytes, unsigned int seq,
+ unsigned int portid, mnl_cb_t cb_data, void *data)
+{
+ return __mnl_cb_run(buf, numbytes, seq, portid, cb_data, data, NULL, 0);
+}
+
+struct mnl_socket {
+ int fd;
+ struct sockaddr_nl addr;
+};
+
+static unsigned int mnl_socket_get_portid(const struct mnl_socket *nl)
+{
+ return nl->addr.nl_pid;
+}
+
+static struct mnl_socket *__mnl_socket_open(int bus, int flags)
+{
+ struct mnl_socket *nl;
+
+ nl = calloc(1, sizeof(struct mnl_socket));
+ if (nl == NULL)
+ return NULL;
+
+ nl->fd = socket(AF_NETLINK, SOCK_RAW | flags, bus);
+ if (nl->fd == -1) {
+ free(nl);
+ return NULL;
+ }
+
+ return nl;
+}
+
+static struct mnl_socket *mnl_socket_open(int bus)
+{
+ return __mnl_socket_open(bus, 0);
+}
+
+static int mnl_socket_bind(struct mnl_socket *nl, unsigned int groups, pid_t pid)
+{
+ int ret;
+ socklen_t addr_len;
+
+ nl->addr.nl_family = AF_NETLINK;
+ nl->addr.nl_groups = groups;
+ nl->addr.nl_pid = pid;
+
+ ret = bind(nl->fd, (struct sockaddr *) &nl->addr, sizeof (nl->addr));
+ if (ret < 0)
+ return ret;
+
+ addr_len = sizeof(nl->addr);
+ ret = getsockname(nl->fd, (struct sockaddr *) &nl->addr, &addr_len);
+ if (ret < 0)
+ return ret;
+
+ if (addr_len != sizeof(nl->addr)) {
+ errno = EINVAL;
+ return -1;
+ }
+ if (nl->addr.nl_family != AF_NETLINK) {
+ errno = EINVAL;
+ return -1;
+ }
+ return 0;
+}
+
+static ssize_t mnl_socket_sendto(const struct mnl_socket *nl, const void *buf,
+ size_t len)
+{
+ static const struct sockaddr_nl snl = {
+ .nl_family = AF_NETLINK
+ };
+ return sendto(nl->fd, buf, len, 0,
+ (struct sockaddr *) &snl, sizeof(snl));
+}
+
+static ssize_t mnl_socket_recvfrom(const struct mnl_socket *nl, void *buf,
+ size_t bufsiz)
+{
+ ssize_t ret;
+ struct sockaddr_nl addr;
+ struct iovec iov = {
+ .iov_base = buf,
+ .iov_len = bufsiz,
+ };
+ struct msghdr msg = {
+ .msg_name = &addr,
+ .msg_namelen = sizeof(struct sockaddr_nl),
+ .msg_iov = &iov,
+ .msg_iovlen = 1,
+ .msg_control = NULL,
+ .msg_controllen = 0,
+ .msg_flags = 0,
+ };
+ ret = recvmsg(nl->fd, &msg, 0);
+ if (ret == -1)
+ return ret;
+
+ if (msg.msg_flags & MSG_TRUNC) {
+ errno = ENOSPC;
+ return -1;
+ }
+ if (msg.msg_namelen != sizeof(struct sockaddr_nl)) {
+ errno = EINVAL;
+ return -1;
+ }
+ return ret;
+}
+
+static int mnl_socket_close(struct mnl_socket *nl)
+{
+ int ret = close(nl->fd);
+ free(nl);
+ return ret;
+}
+
+/* mnlg mini library: */
+
+struct mnlg_socket {
+ struct mnl_socket *nl;
+ char *buf;
+ uint16_t id;
+ uint8_t version;
+ unsigned int seq;
+ unsigned int portid;
+};
+
+static struct nlmsghdr *__mnlg_msg_prepare(struct mnlg_socket *nlg, uint8_t cmd,
+ uint16_t flags, uint16_t id,
+ uint8_t version)
+{
+ struct nlmsghdr *nlh;
+ struct genlmsghdr *genl;
+
+ nlh = mnl_nlmsg_put_header(nlg->buf);
+ nlh->nlmsg_type = id;
+ nlh->nlmsg_flags = flags;
+ nlg->seq = time(NULL);
+ nlh->nlmsg_seq = nlg->seq;
+
+ genl = mnl_nlmsg_put_extra_header(nlh, sizeof(struct genlmsghdr));
+ genl->cmd = cmd;
+ genl->version = version;
+
+ return nlh;
+}
+
+static struct nlmsghdr *mnlg_msg_prepare(struct mnlg_socket *nlg, uint8_t cmd,
+ uint16_t flags)
+{
+ return __mnlg_msg_prepare(nlg, cmd, flags, nlg->id, nlg->version);
+}
+
+static int mnlg_socket_send(struct mnlg_socket *nlg, const struct nlmsghdr *nlh)
+{
+ return mnl_socket_sendto(nlg->nl, nlh, nlh->nlmsg_len);
+}
+
+static int mnlg_cb_noop(const struct nlmsghdr *nlh, void *data)
+{
+ (void)nlh;
+ (void)data;
+ return MNL_CB_OK;
+}
+
+static int mnlg_cb_error(const struct nlmsghdr *nlh, void *data)
+{
+ const struct nlmsgerr *err = mnl_nlmsg_get_payload(nlh);
+ (void)data;
+
+ if (nlh->nlmsg_len < mnl_nlmsg_size(sizeof(struct nlmsgerr))) {
+ errno = EBADMSG;
+ return MNL_CB_ERROR;
+ }
+ /* Netlink subsystems returns the errno value with different signess */
+ if (err->error < 0)
+ errno = -err->error;
+ else
+ errno = err->error;
+
+ return err->error == 0 ? MNL_CB_STOP : MNL_CB_ERROR;
+}
+
+static int mnlg_cb_stop(const struct nlmsghdr *nlh, void *data)
+{
+ (void)data;
+ if (nlh->nlmsg_flags & NLM_F_MULTI && nlh->nlmsg_len == mnl_nlmsg_size(sizeof(int))) {
+ int error = *(int *)mnl_nlmsg_get_payload(nlh);
+ /* Netlink subsystems returns the errno value with different signess */
+ if (error < 0)
+ errno = -error;
+ else
+ errno = error;
+
+ return error == 0 ? MNL_CB_STOP : MNL_CB_ERROR;
+ }
+ return MNL_CB_STOP;
+}
+
+static const mnl_cb_t mnlg_cb_array[] = {
+ [NLMSG_NOOP] = mnlg_cb_noop,
+ [NLMSG_ERROR] = mnlg_cb_error,
+ [NLMSG_DONE] = mnlg_cb_stop,
+ [NLMSG_OVERRUN] = mnlg_cb_noop,
+};
+
+static int mnlg_socket_recv_run(struct mnlg_socket *nlg, mnl_cb_t data_cb, void *data)
+{
+ int err;
+
+ do {
+ err = mnl_socket_recvfrom(nlg->nl, nlg->buf,
+ mnl_ideal_socket_buffer_size());
+ if (err <= 0)
+ break;
+ err = mnl_cb_run2(nlg->buf, err, nlg->seq, nlg->portid,
+ data_cb, data, mnlg_cb_array, MNL_ARRAY_SIZE(mnlg_cb_array));
+ } while (err > 0);
+
+ return err;
+}
+
+static int get_family_id_attr_cb(const struct nlattr *attr, void *data)
+{
+ const struct nlattr **tb = data;
+ int type = mnl_attr_get_type(attr);
+
+ if (mnl_attr_type_valid(attr, CTRL_ATTR_MAX) < 0)
+ return MNL_CB_ERROR;
+
+ if (type == CTRL_ATTR_FAMILY_ID &&
+ mnl_attr_validate(attr, MNL_TYPE_U16) < 0)
+ return MNL_CB_ERROR;
+ tb[type] = attr;
+ return MNL_CB_OK;
+}
+
+static int get_family_id_cb(const struct nlmsghdr *nlh, void *data)
+{
+ uint16_t *p_id = data;
+ struct nlattr *tb[CTRL_ATTR_MAX + 1] = { 0 };
+
+ mnl_attr_parse(nlh, sizeof(struct genlmsghdr), get_family_id_attr_cb, tb);
+ if (!tb[CTRL_ATTR_FAMILY_ID])
+ return MNL_CB_ERROR;
+ *p_id = mnl_attr_get_u16(tb[CTRL_ATTR_FAMILY_ID]);
+ return MNL_CB_OK;
+}
+
+static struct mnlg_socket *mnlg_socket_open(const char *family_name, uint8_t version)
+{
+ struct mnlg_socket *nlg;
+ struct nlmsghdr *nlh;
+ int err;
+
+ nlg = malloc(sizeof(*nlg));
+ if (!nlg)
+ return NULL;
+ nlg->id = 0;
+
+ err = -ENOMEM;
+ nlg->buf = malloc(mnl_ideal_socket_buffer_size());
+ if (!nlg->buf)
+ goto err_buf_alloc;
+
+ nlg->nl = mnl_socket_open(NETLINK_GENERIC);
+ if (!nlg->nl) {
+ err = -errno;
+ goto err_mnl_socket_open;
+ }
+
+ if (mnl_socket_bind(nlg->nl, 0, MNL_SOCKET_AUTOPID) < 0) {
+ err = -errno;
+ goto err_mnl_socket_bind;
+ }
+
+ nlg->portid = mnl_socket_get_portid(nlg->nl);
+
+ nlh = __mnlg_msg_prepare(nlg, CTRL_CMD_GETFAMILY,
+ NLM_F_REQUEST | NLM_F_ACK, GENL_ID_CTRL, 1);
+ mnl_attr_put_strz(nlh, CTRL_ATTR_FAMILY_NAME, family_name);
+
+ if (mnlg_socket_send(nlg, nlh) < 0) {
+ err = -errno;
+ goto err_mnlg_socket_send;
+ }
+
+ errno = 0;
+ if (mnlg_socket_recv_run(nlg, get_family_id_cb, &nlg->id) < 0) {
+ errno = errno == ENOENT ? EPROTONOSUPPORT : errno;
+ err = errno ? -errno : -ENOSYS;
+ goto err_mnlg_socket_recv_run;
+ }
+
+ nlg->version = version;
+ errno = 0;
+ return nlg;
+
+err_mnlg_socket_recv_run:
+err_mnlg_socket_send:
+err_mnl_socket_bind:
+ mnl_socket_close(nlg->nl);
+err_mnl_socket_open:
+ free(nlg->buf);
+err_buf_alloc:
+ free(nlg);
+ errno = -err;
+ return NULL;
+}
+
+static void mnlg_socket_close(struct mnlg_socket *nlg)
+{
+ mnl_socket_close(nlg->nl);
+ free(nlg->buf);
+ free(nlg);
+}
+
+/* wireguard-specific parts: */
+
+struct string_list {
+ char *buffer;
+ size_t len;
+ size_t cap;
+};
+
+static int string_list_add(struct string_list *list, const char *str)
+{
+ size_t len = strlen(str) + 1;
+
+ if (len == 1)
+ return 0;
+
+ if (len >= list->cap - list->len) {
+ char *new_buffer;
+ size_t new_cap = list->cap * 2;
+
+ if (new_cap < list->len +len + 1)
+ new_cap = list->len + len + 1;
+ new_buffer = realloc(list->buffer, new_cap);
+ if (!new_buffer)
+ return -errno;
+ list->buffer = new_buffer;
+ list->cap = new_cap;
+ }
+ memcpy(list->buffer + list->len, str, len);
+ list->len += len;
+ list->buffer[list->len] = '\0';
+ return 0;
+}
+
+struct interface {
+ const char *name;
+ bool is_wireguard;
+};
+
+static int parse_linkinfo(const struct nlattr *attr, void *data)
+{
+ struct interface *interface = data;
+
+ if (mnl_attr_get_type(attr) == IFLA_INFO_KIND && !strcmp(WG_GENL_NAME, mnl_attr_get_str(attr)))
+ interface->is_wireguard = true;
+ return MNL_CB_OK;
+}
+
+static int parse_infomsg(const struct nlattr *attr, void *data)
+{
+ struct interface *interface = data;
+
+ if (mnl_attr_get_type(attr) == IFLA_LINKINFO)
+ return mnl_attr_parse_nested(attr, parse_linkinfo, data);
+ else if (mnl_attr_get_type(attr) == IFLA_IFNAME)
+ interface->name = mnl_attr_get_str(attr);
+ return MNL_CB_OK;
+}
+
+static int read_devices_cb(const struct nlmsghdr *nlh, void *data)
+{
+ struct string_list *list = data;
+ struct interface interface = { 0 };
+ int ret;
+
+ ret = mnl_attr_parse(nlh, sizeof(struct ifinfomsg), parse_infomsg, &interface);
+ if (ret != MNL_CB_OK)
+ return ret;
+ if (interface.name && interface.is_wireguard)
+ ret = string_list_add(list, interface.name);
+ if (ret < 0)
+ return ret;
+ if (nlh->nlmsg_type != NLMSG_DONE)
+ return MNL_CB_OK + 1;
+ return MNL_CB_OK;
+}
+
+static int fetch_device_names(struct string_list *list)
+{
+ struct mnl_socket *nl = NULL;
+ char *rtnl_buffer = NULL;
+ size_t message_len;
+ unsigned int portid, seq;
+ ssize_t len;
+ int ret = 0;
+ struct nlmsghdr *nlh;
+ struct ifinfomsg *ifm;
+
+ ret = -ENOMEM;
+ rtnl_buffer = calloc(mnl_ideal_socket_buffer_size(), 1);
+ if (!rtnl_buffer)
+ goto cleanup;
+
+ nl = mnl_socket_open(NETLINK_ROUTE);
+ if (!nl) {
+ ret = -errno;
+ goto cleanup;
+ }
+
+ if (mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID) < 0) {
+ ret = -errno;
+ goto cleanup;
+ }
+
+ seq = time(NULL);
+ portid = mnl_socket_get_portid(nl);
+ nlh = mnl_nlmsg_put_header(rtnl_buffer);
+ nlh->nlmsg_type = RTM_GETLINK;
+ nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK | NLM_F_DUMP;
+ nlh->nlmsg_seq = seq;
+ ifm = mnl_nlmsg_put_extra_header(nlh, sizeof(*ifm));
+ ifm->ifi_family = AF_UNSPEC;
+ message_len = nlh->nlmsg_len;
+
+ if (mnl_socket_sendto(nl, rtnl_buffer, message_len) < 0) {
+ ret = -errno;
+ goto cleanup;
+ }
+
+another:
+ if ((len = mnl_socket_recvfrom(nl, rtnl_buffer, mnl_ideal_socket_buffer_size())) < 0) {
+ ret = -errno;
+ goto cleanup;
+ }
+ if ((len = mnl_cb_run(rtnl_buffer, len, seq, portid, read_devices_cb, list)) < 0) {
+ /* Netlink returns NLM_F_DUMP_INTR if the set of all tunnels changed
+ * during the dump. That's unfortunate, but is pretty common on busy
+ * systems that are adding and removing tunnels all the time. Rather
+ * than retrying, potentially indefinitely, we just work with the
+ * partial results. */
+ if (errno != EINTR) {
+ ret = -errno;
+ goto cleanup;
+ }
+ }
+ if (len == MNL_CB_OK + 1)
+ goto another;
+ ret = 0;
+
+cleanup:
+ free(rtnl_buffer);
+ if (nl)
+ mnl_socket_close(nl);
+ return ret;
+}
+
+static int add_del_iface(const char *ifname, bool add)
+{
+ struct mnl_socket *nl = NULL;
+ char *rtnl_buffer;
+ ssize_t len;
+ int ret;
+ struct nlmsghdr *nlh;
+ struct ifinfomsg *ifm;
+ struct nlattr *nest;
+
+ rtnl_buffer = calloc(mnl_ideal_socket_buffer_size(), 1);
+ if (!rtnl_buffer) {
+ ret = -ENOMEM;
+ goto cleanup;
+ }
+
+ nl = mnl_socket_open(NETLINK_ROUTE);
+ if (!nl) {
+ ret = -errno;
+ goto cleanup;
+ }
+
+ if (mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID) < 0) {
+ ret = -errno;
+ goto cleanup;
+ }
+
+ nlh = mnl_nlmsg_put_header(rtnl_buffer);
+ nlh->nlmsg_type = add ? RTM_NEWLINK : RTM_DELLINK;
+ nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK | (add ? NLM_F_CREATE | NLM_F_EXCL : 0);
+ nlh->nlmsg_seq = time(NULL);
+ ifm = mnl_nlmsg_put_extra_header(nlh, sizeof(*ifm));
+ ifm->ifi_family = AF_UNSPEC;
+ mnl_attr_put_strz(nlh, IFLA_IFNAME, ifname);
+ nest = mnl_attr_nest_start(nlh, IFLA_LINKINFO);
+ mnl_attr_put_strz(nlh, IFLA_INFO_KIND, WG_GENL_NAME);
+ mnl_attr_nest_end(nlh, nest);
+
+ if (mnl_socket_sendto(nl, rtnl_buffer, nlh->nlmsg_len) < 0) {
+ ret = -errno;
+ goto cleanup;
+ }
+ if ((len = mnl_socket_recvfrom(nl, rtnl_buffer, mnl_ideal_socket_buffer_size())) < 0) {
+ ret = -errno;
+ goto cleanup;
+ }
+ if (mnl_cb_run(rtnl_buffer, len, nlh->nlmsg_seq, mnl_socket_get_portid(nl), NULL, NULL) < 0) {
+ ret = -errno;
+ goto cleanup;
+ }
+ ret = 0;
+
+cleanup:
+ free(rtnl_buffer);
+ if (nl)
+ mnl_socket_close(nl);
+ return ret;
+}
+
+int wg_set_device(wg_device *dev)
+{
+ int ret = 0;
+ wg_peer *peer = NULL;
+ wg_allowedip *allowedip = NULL;
+ struct nlattr *peers_nest, *peer_nest, *allowedips_nest, *allowedip_nest;
+ struct nlmsghdr *nlh;
+ struct mnlg_socket *nlg;
+
+ nlg = mnlg_socket_open(WG_GENL_NAME, WG_GENL_VERSION);
+ if (!nlg)
+ return -errno;
+
+again:
+ nlh = mnlg_msg_prepare(nlg, WG_CMD_SET_DEVICE, NLM_F_REQUEST | NLM_F_ACK);
+ mnl_attr_put_strz(nlh, WGDEVICE_A_IFNAME, dev->name);
+
+ if (!peer) {
+ uint32_t flags = 0;
+
+ if (dev->flags & WGDEVICE_HAS_PRIVATE_KEY)
+ mnl_attr_put(nlh, WGDEVICE_A_PRIVATE_KEY, sizeof(dev->private_key), dev->private_key);
+ if (dev->flags & WGDEVICE_HAS_LISTEN_PORT)
+ mnl_attr_put_u16(nlh, WGDEVICE_A_LISTEN_PORT, dev->listen_port);
+ if (dev->flags & WGDEVICE_HAS_FWMARK)
+ mnl_attr_put_u32(nlh, WGDEVICE_A_FWMARK, dev->fwmark);
+ if (dev->flags & WGDEVICE_REPLACE_PEERS)
+ flags |= WGDEVICE_F_REPLACE_PEERS;
+ if (flags)
+ mnl_attr_put_u32(nlh, WGDEVICE_A_FLAGS, flags);
+ }
+ if (!dev->first_peer)
+ goto send;
+ peers_nest = peer_nest = allowedips_nest = allowedip_nest = NULL;
+ peers_nest = mnl_attr_nest_start(nlh, WGDEVICE_A_PEERS);
+ for (peer = peer ? peer : dev->first_peer; peer; peer = peer->next_peer) {
+ uint32_t flags = 0;
+
+ peer_nest = mnl_attr_nest_start_check(nlh, mnl_ideal_socket_buffer_size(), 0);
+ if (!peer_nest)
+ goto toobig_peers;
+ if (!mnl_attr_put_check(nlh, mnl_ideal_socket_buffer_size(), WGPEER_A_PUBLIC_KEY, sizeof(peer->public_key), peer->public_key))
+ goto toobig_peers;
+ if (peer->flags & WGPEER_REMOVE_ME)
+ flags |= WGPEER_F_REMOVE_ME;
+ if (!allowedip) {
+ if (peer->flags & WGPEER_REPLACE_ALLOWEDIPS)
+ flags |= WGPEER_F_REPLACE_ALLOWEDIPS;
+ if (peer->flags & WGPEER_HAS_PRESHARED_KEY) {
+ if (!mnl_attr_put_check(nlh, mnl_ideal_socket_buffer_size(), WGPEER_A_PRESHARED_KEY, sizeof(peer->preshared_key), peer->preshared_key))
+ goto toobig_peers;
+ }
+ if (peer->endpoint.addr.sa_family == AF_INET) {
+ if (!mnl_attr_put_check(nlh, mnl_ideal_socket_buffer_size(), WGPEER_A_ENDPOINT, sizeof(peer->endpoint.addr4), &peer->endpoint.addr4))
+ goto toobig_peers;
+ } else if (peer->endpoint.addr.sa_family == AF_INET6) {
+ if (!mnl_attr_put_check(nlh, mnl_ideal_socket_buffer_size(), WGPEER_A_ENDPOINT, sizeof(peer->endpoint.addr6), &peer->endpoint.addr6))
+ goto toobig_peers;
+ }
+ if (peer->flags & WGPEER_HAS_PERSISTENT_KEEPALIVE_INTERVAL) {
+ if (!mnl_attr_put_u16_check(nlh, mnl_ideal_socket_buffer_size(), WGPEER_A_PERSISTENT_KEEPALIVE_INTERVAL, peer->persistent_keepalive_interval))
+ goto toobig_peers;
+ }
+ }
+ if (flags) {
+ if (!mnl_attr_put_u32_check(nlh, mnl_ideal_socket_buffer_size(), WGPEER_A_FLAGS, flags))
+ goto toobig_peers;
+ }
+ if (peer->first_allowedip) {
+ if (!allowedip)
+ allowedip = peer->first_allowedip;
+ allowedips_nest = mnl_attr_nest_start_check(nlh, mnl_ideal_socket_buffer_size(), WGPEER_A_ALLOWEDIPS);
+ if (!allowedips_nest)
+ goto toobig_allowedips;
+ for (; allowedip; allowedip = allowedip->next_allowedip) {
+ allowedip_nest = mnl_attr_nest_start_check(nlh, mnl_ideal_socket_buffer_size(), 0);
+ if (!allowedip_nest)
+ goto toobig_allowedips;
+ if (!mnl_attr_put_u16_check(nlh, mnl_ideal_socket_buffer_size(), WGALLOWEDIP_A_FAMILY, allowedip->family))
+ goto toobig_allowedips;
+ if (allowedip->family == AF_INET) {
+ if (!mnl_attr_put_check(nlh, mnl_ideal_socket_buffer_size(), WGALLOWEDIP_A_IPADDR, sizeof(allowedip->ip4), &allowedip->ip4))
+ goto toobig_allowedips;
+ } else if (allowedip->family == AF_INET6) {
+ if (!mnl_attr_put_check(nlh, mnl_ideal_socket_buffer_size(), WGALLOWEDIP_A_IPADDR, sizeof(allowedip->ip6), &allowedip->ip6))
+ goto toobig_allowedips;
+ }
+ if (!mnl_attr_put_u8_check(nlh, mnl_ideal_socket_buffer_size(), WGALLOWEDIP_A_CIDR_MASK, allowedip->cidr))
+ goto toobig_allowedips;
+ mnl_attr_nest_end(nlh, allowedip_nest);
+ allowedip_nest = NULL;
+ }
+ mnl_attr_nest_end(nlh, allowedips_nest);
+ allowedips_nest = NULL;
+ }
+
+ mnl_attr_nest_end(nlh, peer_nest);
+ peer_nest = NULL;
+ }
+ mnl_attr_nest_end(nlh, peers_nest);
+ peers_nest = NULL;
+ goto send;
+toobig_allowedips:
+ if (allowedip_nest)
+ mnl_attr_nest_cancel(nlh, allowedip_nest);
+ if (allowedips_nest)
+ mnl_attr_nest_end(nlh, allowedips_nest);
+ mnl_attr_nest_end(nlh, peer_nest);
+ mnl_attr_nest_end(nlh, peers_nest);
+ goto send;
+toobig_peers:
+ if (peer_nest)
+ mnl_attr_nest_cancel(nlh, peer_nest);
+ mnl_attr_nest_end(nlh, peers_nest);
+ goto send;
+send:
+ if (mnlg_socket_send(nlg, nlh) < 0) {
+ ret = -errno;
+ goto out;
+ }
+ errno = 0;
+ if (mnlg_socket_recv_run(nlg, NULL, NULL) < 0) {
+ ret = errno ? -errno : -EINVAL;
+ goto out;
+ }
+ if (peer)
+ goto again;
+
+out:
+ mnlg_socket_close(nlg);
+ errno = -ret;
+ return ret;
+}
+
+static int parse_allowedip(const struct nlattr *attr, void *data)
+{
+ wg_allowedip *allowedip = data;
+
+ switch (mnl_attr_get_type(attr)) {
+ case WGALLOWEDIP_A_UNSPEC:
+ break;
+ case WGALLOWEDIP_A_FAMILY:
+ if (!mnl_attr_validate(attr, MNL_TYPE_U16))
+ allowedip->family = mnl_attr_get_u16(attr);
+ break;
+ case WGALLOWEDIP_A_IPADDR:
+ if (mnl_attr_get_payload_len(attr) == sizeof(allowedip->ip4))
+ memcpy(&allowedip->ip4, mnl_attr_get_payload(attr), sizeof(allowedip->ip4));
+ else if (mnl_attr_get_payload_len(attr) == sizeof(allowedip->ip6))
+ memcpy(&allowedip->ip6, mnl_attr_get_payload(attr), sizeof(allowedip->ip6));
+ break;
+ case WGALLOWEDIP_A_CIDR_MASK:
+ if (!mnl_attr_validate(attr, MNL_TYPE_U8))
+ allowedip->cidr = mnl_attr_get_u8(attr);
+ break;
+ }
+
+ return MNL_CB_OK;
+}
+
+static int parse_allowedips(const struct nlattr *attr, void *data)
+{
+ wg_peer *peer = data;
+ wg_allowedip *new_allowedip = calloc(1, sizeof(wg_allowedip));
+ int ret;
+
+ if (!new_allowedip)
+ return MNL_CB_ERROR;
+ if (!peer->first_allowedip)
+ peer->first_allowedip = peer->last_allowedip = new_allowedip;
+ else {
+ peer->last_allowedip->next_allowedip = new_allowedip;
+ peer->last_allowedip = new_allowedip;
+ }
+ ret = mnl_attr_parse_nested(attr, parse_allowedip, new_allowedip);
+ if (!ret)
+ return ret;
+ if (!((new_allowedip->family == AF_INET && new_allowedip->cidr <= 32) || (new_allowedip->family == AF_INET6 && new_allowedip->cidr <= 128))) {
+ errno = EAFNOSUPPORT;
+ return MNL_CB_ERROR;
+ }
+ return MNL_CB_OK;
+}
+
+bool wg_key_is_zero(const wg_key key)
+{
+ volatile uint8_t acc = 0;
+ unsigned int i;
+
+ for (i = 0; i < sizeof(wg_key); ++i) {
+ acc |= key[i];
+ __asm__ ("" : "=r" (acc) : "0" (acc));
+ }
+ return 1 & ((acc - 1) >> 8);
+}
+
+static int parse_peer(const struct nlattr *attr, void *data)
+{
+ wg_peer *peer = data;
+
+ switch (mnl_attr_get_type(attr)) {
+ case WGPEER_A_UNSPEC:
+ break;
+ case WGPEER_A_PUBLIC_KEY:
+ if (mnl_attr_get_payload_len(attr) == sizeof(peer->public_key)) {
+ memcpy(peer->public_key, mnl_attr_get_payload(attr), sizeof(peer->public_key));
+ peer->flags |= WGPEER_HAS_PUBLIC_KEY;
+ }
+ break;
+ case WGPEER_A_PRESHARED_KEY:
+ if (mnl_attr_get_payload_len(attr) == sizeof(peer->preshared_key)) {
+ memcpy(peer->preshared_key, mnl_attr_get_payload(attr), sizeof(peer->preshared_key));
+ if (!wg_key_is_zero(peer->preshared_key))
+ peer->flags |= WGPEER_HAS_PRESHARED_KEY;
+ }
+ break;
+ case WGPEER_A_ENDPOINT: {
+ struct sockaddr *addr;
+
+ if (mnl_attr_get_payload_len(attr) < sizeof(*addr))
+ break;
+ addr = mnl_attr_get_payload(attr);
+ if (addr->sa_family == AF_INET && mnl_attr_get_payload_len(attr) == sizeof(peer->endpoint.addr4))
+ memcpy(&peer->endpoint.addr4, addr, sizeof(peer->endpoint.addr4));
+ else if (addr->sa_family == AF_INET6 && mnl_attr_get_payload_len(attr) == sizeof(peer->endpoint.addr6))
+ memcpy(&peer->endpoint.addr6, addr, sizeof(peer->endpoint.addr6));
+ break;
+ }
+ case WGPEER_A_PERSISTENT_KEEPALIVE_INTERVAL:
+ if (!mnl_attr_validate(attr, MNL_TYPE_U16))
+ peer->persistent_keepalive_interval = mnl_attr_get_u16(attr);
+ break;
+ case WGPEER_A_LAST_HANDSHAKE_TIME:
+ if (mnl_attr_get_payload_len(attr) == sizeof(peer->last_handshake_time))
+ memcpy(&peer->last_handshake_time, mnl_attr_get_payload(attr), sizeof(peer->last_handshake_time));
+ break;
+ case WGPEER_A_RX_BYTES:
+ if (!mnl_attr_validate(attr, MNL_TYPE_U64))
+ peer->rx_bytes = mnl_attr_get_u64(attr);
+ break;
+ case WGPEER_A_TX_BYTES:
+ if (!mnl_attr_validate(attr, MNL_TYPE_U64))
+ peer->tx_bytes = mnl_attr_get_u64(attr);
+ break;
+ case WGPEER_A_ALLOWEDIPS:
+ return mnl_attr_parse_nested(attr, parse_allowedips, peer);
+ }
+
+ return MNL_CB_OK;
+}
+
+static int parse_peers(const struct nlattr *attr, void *data)
+{
+ wg_device *device = data;
+ wg_peer *new_peer = calloc(1, sizeof(wg_peer));
+ int ret;
+
+ if (!new_peer)
+ return MNL_CB_ERROR;
+ if (!device->first_peer)
+ device->first_peer = device->last_peer = new_peer;
+ else {
+ device->last_peer->next_peer = new_peer;
+ device->last_peer = new_peer;
+ }
+ ret = mnl_attr_parse_nested(attr, parse_peer, new_peer);
+ if (!ret)
+ return ret;
+ if (!(new_peer->flags & WGPEER_HAS_PUBLIC_KEY)) {
+ errno = ENXIO;
+ return MNL_CB_ERROR;
+ }
+ return MNL_CB_OK;
+}
+
+static int parse_device(const struct nlattr *attr, void *data)
+{
+ wg_device *device = data;
+
+ switch (mnl_attr_get_type(attr)) {
+ case WGDEVICE_A_UNSPEC:
+ break;
+ case WGDEVICE_A_IFINDEX:
+ if (!mnl_attr_validate(attr, MNL_TYPE_U32))
+ device->ifindex = mnl_attr_get_u32(attr);
+ break;
+ case WGDEVICE_A_IFNAME:
+ if (!mnl_attr_validate(attr, MNL_TYPE_STRING)) {
+ strncpy(device->name, mnl_attr_get_str(attr), sizeof(device->name) - 1);
+ device->name[sizeof(device->name) - 1] = '\0';
+ }
+ break;
+ case WGDEVICE_A_PRIVATE_KEY:
+ if (mnl_attr_get_payload_len(attr) == sizeof(device->private_key)) {
+ memcpy(device->private_key, mnl_attr_get_payload(attr), sizeof(device->private_key));
+ device->flags |= WGDEVICE_HAS_PRIVATE_KEY;
+ }
+ break;
+ case WGDEVICE_A_PUBLIC_KEY:
+ if (mnl_attr_get_payload_len(attr) == sizeof(device->public_key)) {
+ memcpy(device->public_key, mnl_attr_get_payload(attr), sizeof(device->public_key));
+ device->flags |= WGDEVICE_HAS_PUBLIC_KEY;
+ }
+ break;
+ case WGDEVICE_A_LISTEN_PORT:
+ if (!mnl_attr_validate(attr, MNL_TYPE_U16))
+ device->listen_port = mnl_attr_get_u16(attr);
+ break;
+ case WGDEVICE_A_FWMARK:
+ if (!mnl_attr_validate(attr, MNL_TYPE_U32))
+ device->fwmark = mnl_attr_get_u32(attr);
+ break;
+ case WGDEVICE_A_PEERS:
+ return mnl_attr_parse_nested(attr, parse_peers, device);
+ }
+
+ return MNL_CB_OK;
+}
+
+static int read_device_cb(const struct nlmsghdr *nlh, void *data)
+{
+ return mnl_attr_parse(nlh, sizeof(struct genlmsghdr), parse_device, data);
+}
+
+static void coalesce_peers(wg_device *device)
+{
+ wg_peer *old_next_peer, *peer = device->first_peer;
+
+ while (peer && peer->next_peer) {
+ if (memcmp(peer->public_key, peer->next_peer->public_key, sizeof(wg_key))) {
+ peer = peer->next_peer;
+ continue;
+ }
+ if (!peer->first_allowedip) {
+ peer->first_allowedip = peer->next_peer->first_allowedip;
+ peer->last_allowedip = peer->next_peer->last_allowedip;
+ } else {
+ peer->last_allowedip->next_allowedip = peer->next_peer->first_allowedip;
+ peer->last_allowedip = peer->next_peer->last_allowedip;
+ }
+ old_next_peer = peer->next_peer;
+ peer->next_peer = old_next_peer->next_peer;
+ free(old_next_peer);
+ }
+}
+
+int wg_get_device(wg_device **device, const char *device_name)
+{
+ int ret = 0;
+ struct nlmsghdr *nlh;
+ struct mnlg_socket *nlg;
+
+try_again:
+ *device = calloc(1, sizeof(wg_device));
+ if (!*device)
+ return -errno;
+
+ nlg = mnlg_socket_open(WG_GENL_NAME, WG_GENL_VERSION);
+ if (!nlg) {
+ wg_free_device(*device);
+ *device = NULL;
+ return -errno;
+ }
+
+ nlh = mnlg_msg_prepare(nlg, WG_CMD_GET_DEVICE, NLM_F_REQUEST | NLM_F_ACK | NLM_F_DUMP);
+ mnl_attr_put_strz(nlh, WGDEVICE_A_IFNAME, device_name);
+ if (mnlg_socket_send(nlg, nlh) < 0) {
+ ret = -errno;
+ goto out;
+ }
+ errno = 0;
+ if (mnlg_socket_recv_run(nlg, read_device_cb, *device) < 0) {
+ ret = errno ? -errno : -EINVAL;
+ goto out;
+ }
+ coalesce_peers(*device);
+
+out:
+ if (nlg)
+ mnlg_socket_close(nlg);
+ if (ret) {
+ wg_free_device(*device);
+ if (ret == -EINTR)
+ goto try_again;
+ *device = NULL;
+ }
+ errno = -ret;
+ return ret;
+}
+
+/* first\0second\0third\0forth\0last\0\0 */
+char *wg_list_device_names(void)
+{
+ struct string_list list = { 0 };
+ int ret = fetch_device_names(&list);
+
+ errno = -ret;
+ if (errno) {
+ free(list.buffer);
+ return NULL;
+ }
+ return list.buffer ?: strdup("\0");
+}
+
+int wg_add_device(const char *device_name)
+{
+ return add_del_iface(device_name, true);
+}
+
+int wg_del_device(const char *device_name)
+{
+ return add_del_iface(device_name, false);
+}
+
+void wg_free_device(wg_device *dev)
+{
+ wg_peer *peer, *np;
+ wg_allowedip *allowedip, *na;
+
+ if (!dev)
+ return;
+ for (peer = dev->first_peer, np = peer ? peer->next_peer : NULL; peer; peer = np, np = peer ? peer->next_peer : NULL) {
+ for (allowedip = peer->first_allowedip, na = allowedip ? allowedip->next_allowedip : NULL; allowedip; allowedip = na, na = allowedip ? allowedip->next_allowedip : NULL)
+ free(allowedip);
+ free(peer);
+ }
+ free(dev);
+}
+
+static void encode_base64(char dest[static 4], const uint8_t src[static 3])
+{
+ const uint8_t input[] = { (src[0] >> 2) & 63, ((src[0] << 4) | (src[1] >> 4)) & 63, ((src[1] << 2) | (src[2] >> 6)) & 63, src[2] & 63 };
+ unsigned int i;
+
+ for (i = 0; i < 4; ++i)
+ dest[i] = input[i] + 'A'
+ + (((25 - input[i]) >> 8) & 6)
+ - (((51 - input[i]) >> 8) & 75)
+ - (((61 - input[i]) >> 8) & 15)
+ + (((62 - input[i]) >> 8) & 3);
+
+}
+
+void wg_key_to_base64(wg_key_b64_string base64, const wg_key key)
+{
+ unsigned int i;
+
+ for (i = 0; i < 32 / 3; ++i)
+ encode_base64(&base64[i * 4], &key[i * 3]);
+ encode_base64(&base64[i * 4], (const uint8_t[]){ key[i * 3 + 0], key[i * 3 + 1], 0 });
+ base64[sizeof(wg_key_b64_string) - 2] = '=';
+ base64[sizeof(wg_key_b64_string) - 1] = '\0';
+}
+
+static int decode_base64(const char src[static 4])
+{
+ int val = 0;
+ unsigned int i;
+
+ for (i = 0; i < 4; ++i)
+ val |= (-1
+ + ((((('A' - 1) - src[i]) & (src[i] - ('Z' + 1))) >> 8) & (src[i] - 64))
+ + ((((('a' - 1) - src[i]) & (src[i] - ('z' + 1))) >> 8) & (src[i] - 70))
+ + ((((('0' - 1) - src[i]) & (src[i] - ('9' + 1))) >> 8) & (src[i] + 5))
+ + ((((('+' - 1) - src[i]) & (src[i] - ('+' + 1))) >> 8) & 63)
+ + ((((('/' - 1) - src[i]) & (src[i] - ('/' + 1))) >> 8) & 64)
+ ) << (18 - 6 * i);
+ return val;
+}
+
+int wg_key_from_base64(wg_key key, const wg_key_b64_string base64)
+{
+ unsigned int i;
+ int val;
+ volatile uint8_t ret = 0;
+
+ if (strlen(base64) != sizeof(wg_key_b64_string) - 1 || base64[sizeof(wg_key_b64_string) - 2] != '=') {
+ errno = EINVAL;
+ goto out;
+ }
+
+ for (i = 0; i < 32 / 3; ++i) {
+ val = decode_base64(&base64[i * 4]);
+ ret |= (uint32_t)val >> 31;
+ key[i * 3 + 0] = (val >> 16) & 0xff;
+ key[i * 3 + 1] = (val >> 8) & 0xff;
+ key[i * 3 + 2] = val & 0xff;
+ }
+ val = decode_base64((const char[]){ base64[i * 4 + 0], base64[i * 4 + 1], base64[i * 4 + 2], 'A' });
+ ret |= ((uint32_t)val >> 31) | (val & 0xff);
+ key[i * 3 + 0] = (val >> 16) & 0xff;
+ key[i * 3 + 1] = (val >> 8) & 0xff;
+ errno = EINVAL & ~((ret - 1) >> 8);
+out:
+ return -errno;
+}
+
+typedef int64_t fe[16];
+
+static __attribute__((noinline)) void memzero_explicit(void *s, size_t count)
+{
+ memset(s, 0, count);
+ __asm__ __volatile__("": :"r"(s) :"memory");
+}
+
+static void carry(fe o)
+{
+ int i;
+
+ for (i = 0; i < 16; ++i) {
+ o[(i + 1) % 16] += (i == 15 ? 38 : 1) * (o[i] >> 16);
+ o[i] &= 0xffff;
+ }
+}
+
+static void cswap(fe p, fe q, int b)
+{
+ int i;
+ int64_t t, c = ~(b - 1);
+
+ for (i = 0; i < 16; ++i) {
+ t = c & (p[i] ^ q[i]);
+ p[i] ^= t;
+ q[i] ^= t;
+ }
+
+ memzero_explicit(&t, sizeof(t));
+ memzero_explicit(&c, sizeof(c));
+ memzero_explicit(&b, sizeof(b));
+}
+
+static void pack(uint8_t *o, const fe n)
+{
+ int i, j, b;
+ fe m, t;
+
+ memcpy(t, n, sizeof(t));
+ carry(t);
+ carry(t);
+ carry(t);
+ for (j = 0; j < 2; ++j) {
+ m[0] = t[0] - 0xffed;
+ for (i = 1; i < 15; ++i) {
+ m[i] = t[i] - 0xffff - ((m[i - 1] >> 16) & 1);
+ m[i - 1] &= 0xffff;
+ }
+ m[15] = t[15] - 0x7fff - ((m[14] >> 16) & 1);
+ b = (m[15] >> 16) & 1;
+ m[14] &= 0xffff;
+ cswap(t, m, 1 - b);
+ }
+ for (i = 0; i < 16; ++i) {
+ o[2 * i] = t[i] & 0xff;
+ o[2 * i + 1] = t[i] >> 8;
+ }
+
+ memzero_explicit(m, sizeof(m));
+ memzero_explicit(t, sizeof(t));
+ memzero_explicit(&b, sizeof(b));
+}
+
+static void add(fe o, const fe a, const fe b)
+{
+ int i;
+
+ for (i = 0; i < 16; ++i)
+ o[i] = a[i] + b[i];
+}
+
+static void subtract(fe o, const fe a, const fe b)
+{
+ int i;
+
+ for (i = 0; i < 16; ++i)
+ o[i] = a[i] - b[i];
+}
+
+static void multmod(fe o, const fe a, const fe b)
+{
+ int i, j;
+ int64_t t[31] = { 0 };
+
+ for (i = 0; i < 16; ++i) {
+ for (j = 0; j < 16; ++j)
+ t[i + j] += a[i] * b[j];
+ }
+ for (i = 0; i < 15; ++i)
+ t[i] += 38 * t[i + 16];
+ memcpy(o, t, sizeof(fe));
+ carry(o);
+ carry(o);
+
+ memzero_explicit(t, sizeof(t));
+}
+
+static void invert(fe o, const fe i)
+{
+ fe c;
+ int a;
+
+ memcpy(c, i, sizeof(c));
+ for (a = 253; a >= 0; --a) {
+ multmod(c, c, c);
+ if (a != 2 && a != 4)
+ multmod(c, c, i);
+ }
+ memcpy(o, c, sizeof(fe));
+
+ memzero_explicit(c, sizeof(c));
+}
+
+static void clamp_key(uint8_t *z)
+{
+ z[31] = (z[31] & 127) | 64;
+ z[0] &= 248;
+}
+
+void wg_generate_public_key(wg_key public_key, const wg_key private_key)
+{
+ int i, r;
+ uint8_t z[32];
+ fe a = { 1 }, b = { 9 }, c = { 0 }, d = { 1 }, e, f;
+
+ memcpy(z, private_key, sizeof(z));
+ clamp_key(z);
+
+ for (i = 254; i >= 0; --i) {
+ r = (z[i >> 3] >> (i & 7)) & 1;
+ cswap(a, b, r);
+ cswap(c, d, r);
+ add(e, a, c);
+ subtract(a, a, c);
+ add(c, b, d);
+ subtract(b, b, d);
+ multmod(d, e, e);
+ multmod(f, a, a);
+ multmod(a, c, a);
+ multmod(c, b, e);
+ add(e, a, c);
+ subtract(a, a, c);
+ multmod(b, a, a);
+ subtract(c, d, f);
+ multmod(a, c, (const fe){ 0xdb41, 1 });
+ add(a, a, d);
+ multmod(c, c, a);
+ multmod(a, d, f);
+ multmod(d, b, (const fe){ 9 });
+ multmod(b, e, e);
+ cswap(a, b, r);
+ cswap(c, d, r);
+ }
+ invert(c, c);
+ multmod(a, a, c);
+ pack(public_key, a);
+
+ memzero_explicit(&r, sizeof(r));
+ memzero_explicit(z, sizeof(z));
+ memzero_explicit(a, sizeof(a));
+ memzero_explicit(b, sizeof(b));
+ memzero_explicit(c, sizeof(c));
+ memzero_explicit(d, sizeof(d));
+ memzero_explicit(e, sizeof(e));
+ memzero_explicit(f, sizeof(f));
+}
+
+void wg_generate_private_key(wg_key private_key)
+{
+ wg_generate_preshared_key(private_key);
+ clamp_key(private_key);
+}
+
+void wg_generate_preshared_key(wg_key preshared_key)
+{
+ ssize_t ret;
+ size_t i;
+ int fd;
+#if defined(__OpenBSD__) || (defined(__APPLE__) && MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_12) || (defined(__GLIBC__) && (__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 25)))
+ if (!getentropy(preshared_key, sizeof(wg_key)))
+ return;
+#endif
+#if defined(__NR_getrandom) && defined(__linux__)
+ if (syscall(__NR_getrandom, preshared_key, sizeof(wg_key), 0) == sizeof(wg_key))
+ return;
+#endif
+ fd = open("/dev/urandom", O_RDONLY);
+ assert(fd >= 0);
+ for (i = 0; i < sizeof(wg_key); i += ret) {
+ ret = read(fd, preshared_key + i, sizeof(wg_key) - i);
+ assert(ret > 0);
+ }
+ close(fd);
+}
diff --git a/contrib/wireguard-tools/contrib/external-tests/go/.gitignore b/contrib/wireguard-tools/contrib/external-tests/go/.gitignore
new file mode 100644
--- /dev/null
+++ b/contrib/wireguard-tools/contrib/external-tests/go/.gitignore
@@ -0,0 +1 @@
+go
diff --git a/contrib/wireguard-tools/contrib/external-tests/go/main.go b/contrib/wireguard-tools/contrib/external-tests/go/main.go
new file mode 100644
--- /dev/null
+++ b/contrib/wireguard-tools/contrib/external-tests/go/main.go
@@ -0,0 +1,187 @@
+/* Copyright (C) 2015-2020 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved. */
+
+package main
+
+import (
+ "crypto/rand"
+ "encoding/base64"
+ "encoding/binary"
+ "log"
+ "net"
+ "time"
+
+ "github.com/titanous/noise"
+ "golang.org/x/net/icmp"
+ "golang.org/x/net/ipv4"
+ "golang.org/x/crypto/blake2s"
+)
+
+func ipChecksum(buf []byte) uint16 {
+ sum := uint32(0)
+ for ; len(buf) >= 2; buf = buf[2:] {
+ sum += uint32(buf[0])<<8 | uint32(buf[1])
+ }
+ if len(buf) > 0 {
+ sum += uint32(buf[0]) << 8
+ }
+ for sum > 0xffff {
+ sum = (sum >> 16) + (sum & 0xffff)
+ }
+ csum := ^uint16(sum)
+ if csum == 0 {
+ csum = 0xffff
+ }
+ return csum
+}
+
+func main() {
+ ourPrivate, _ := base64.StdEncoding.DecodeString("WAmgVYXkbT2bCtdcDwolI88/iVi/aV3/PHcUBTQSYmo=")
+ ourPublic, _ := base64.StdEncoding.DecodeString("K5sF9yESrSBsOXPd6TcpKNgqoy1Ik3ZFKl4FolzrRyI=")
+ theirPublic, _ := base64.StdEncoding.DecodeString("qRCwZSKInrMAq5sepfCdaCsRJaoLe5jhtzfiw7CjbwM=")
+ preshared, _ := base64.StdEncoding.DecodeString("FpCyhws9cxwWoV4xELtfJvjJN+zQVRPISllRWgeopVE=")
+ cs := noise.NewCipherSuite(noise.DH25519, noise.CipherChaChaPoly, noise.HashBLAKE2s)
+ hs, _ := noise.NewHandshakeState(noise.Config{
+ CipherSuite: cs,
+ Random: rand.Reader,
+ Pattern: noise.HandshakeIK,
+ Initiator: true,
+ Prologue: []byte("WireGuard v1 zx2c4 Jason@zx2c4.com"),
+ PresharedKey: preshared,
+ PresharedKeyPlacement: 2,
+ StaticKeypair: noise.DHKey{Private: ourPrivate, Public: ourPublic},
+ PeerStatic: theirPublic,
+ })
+ conn, err := net.Dial("udp", "demo.wireguard.com:12913")
+ if err != nil {
+ log.Fatalf("error dialing udp socket: %s", err)
+ }
+ defer conn.Close()
+
+ // write handshake initiation packet
+ now := time.Now()
+ tai64n := make([]byte, 12)
+ binary.BigEndian.PutUint64(tai64n[:], 4611686018427387914+uint64(now.Unix()))
+ binary.BigEndian.PutUint32(tai64n[8:], uint32(now.Nanosecond()))
+ initiationPacket := make([]byte, 8)
+ initiationPacket[0] = 1 // Type: Initiation
+ initiationPacket[1] = 0 // Reserved
+ initiationPacket[2] = 0 // Reserved
+ initiationPacket[3] = 0 // Reserved
+ binary.LittleEndian.PutUint32(initiationPacket[4:], 28) // Sender index: 28 (arbitrary)
+ initiationPacket, _, _, _ = hs.WriteMessage(initiationPacket, tai64n)
+ hasher, _ := blake2s.New256(nil)
+ hasher.Write([]byte("mac1----"))
+ hasher.Write(theirPublic)
+ hasher, _ = blake2s.New128(hasher.Sum(nil))
+ hasher.Write(initiationPacket)
+ initiationPacket = append(initiationPacket, hasher.Sum(nil)[:16]...)
+ initiationPacket = append(initiationPacket, make([]byte, 16)...)
+ if _, err := conn.Write(initiationPacket); err != nil {
+ log.Fatalf("error writing initiation packet: %s", err)
+ }
+
+ // read handshake response packet
+ responsePacket := make([]byte, 92)
+ n, err := conn.Read(responsePacket)
+ if err != nil {
+ log.Fatalf("error reading response packet: %s", err)
+ }
+ if n != len(responsePacket) {
+ log.Fatalf("response packet too short: want %d, got %d", len(responsePacket), n)
+ }
+ if responsePacket[0] != 2 { // Type: Response
+ log.Fatalf("response packet type wrong: want %d, got %d", 2, responsePacket[0])
+ }
+ if responsePacket[1] != 0 || responsePacket[2] != 0 || responsePacket[3] != 0 {
+ log.Fatalf("response packet has non-zero reserved fields")
+ }
+ theirIndex := binary.LittleEndian.Uint32(responsePacket[4:])
+ ourIndex := binary.LittleEndian.Uint32(responsePacket[8:])
+ if ourIndex != 28 {
+ log.Fatalf("response packet index wrong: want %d, got %d", 28, ourIndex)
+ }
+ payload, sendCipher, receiveCipher, err := hs.ReadMessage(nil, responsePacket[12:60])
+ if err != nil {
+ log.Fatalf("error reading handshake message: %s", err)
+ }
+ if len(payload) > 0 {
+ log.Fatalf("unexpected payload: %x", payload)
+ }
+
+ // write ICMP Echo packet
+ pingMessage, _ := (&icmp.Message{
+ Type: ipv4.ICMPTypeEcho,
+ Body: &icmp.Echo{
+ ID: 921,
+ Seq: 438,
+ Data: []byte("WireGuard"),
+ },
+ }).Marshal(nil)
+ pingHeader, err := (&ipv4.Header{
+ Version: ipv4.Version,
+ Len: ipv4.HeaderLen,
+ TotalLen: ipv4.HeaderLen + len(pingMessage),
+ Protocol: 1, // ICMP
+ TTL: 20,
+ Src: net.IPv4(10, 189, 129, 2),
+ Dst: net.IPv4(10, 189, 129, 1),
+ }).Marshal()
+ binary.BigEndian.PutUint16(pingHeader[2:], uint16(ipv4.HeaderLen+len(pingMessage))) // fix the length endianness on BSDs
+ pingData := append(append(pingHeader, pingMessage...), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)
+ binary.BigEndian.PutUint16(pingData[10:], ipChecksum(pingData))
+ pingPacket := make([]byte, 16)
+ pingPacket[0] = 4 // Type: Data
+ pingPacket[1] = 0 // Reserved
+ pingPacket[2] = 0 // Reserved
+ pingPacket[3] = 0 // Reserved
+ binary.LittleEndian.PutUint32(pingPacket[4:], theirIndex) // Their index
+ binary.LittleEndian.PutUint64(pingPacket[8:], 0) // Nonce
+ pingPacket = sendCipher.Encrypt(pingPacket, nil, pingData) // Payload data
+ if _, err := conn.Write(pingPacket); err != nil {
+ log.Fatalf("error writing ping message: %s", err)
+ }
+
+ // read ICMP Echo Reply packet
+ replyPacket := make([]byte, 80)
+ n, err = conn.Read(replyPacket)
+ if err != nil {
+ log.Fatalf("error reading ping reply message: %s", err)
+ }
+ replyPacket = replyPacket[:n]
+ if replyPacket[0] != 4 { // Type: Data
+ log.Fatalf("unexpected reply packet type: %d", replyPacket[0])
+ }
+ if replyPacket[1] != 0 || replyPacket[2] != 0 || replyPacket[3] != 0 {
+ log.Fatalf("reply packet has non-zero reserved fields")
+ }
+ replyPacket, err = receiveCipher.Decrypt(nil, nil, replyPacket[16:])
+ if err != nil {
+ log.Fatalf("error decrypting reply packet: %s", err)
+ }
+ replyHeaderLen := int(replyPacket[0]&0x0f) << 2
+ replyLen := binary.BigEndian.Uint16(replyPacket[2:])
+ replyMessage, err := icmp.ParseMessage(1, replyPacket[replyHeaderLen:replyLen])
+ if err != nil {
+ log.Fatalf("error parsing echo: %s", err)
+ }
+ echo, ok := replyMessage.Body.(*icmp.Echo)
+ if !ok {
+ log.Fatalf("unexpected reply body type %T", replyMessage.Body)
+ }
+
+ if echo.ID != 921 || echo.Seq != 438 || string(echo.Data) != "WireGuard" {
+ log.Fatalf("incorrect echo response: %#v", echo)
+ }
+
+ keepalivePacket := make([]byte, 16)
+ keepalivePacket[0] = 4 // Type: Data
+ keepalivePacket[1] = 0 // Reserved
+ keepalivePacket[2] = 0 // Reserved
+ keepalivePacket[3] = 0 // Reserved
+ binary.LittleEndian.PutUint32(keepalivePacket[4:], theirIndex) // Their index
+ binary.LittleEndian.PutUint64(keepalivePacket[8:], 1) // Nonce
+ keepalivePacket = sendCipher.Encrypt(keepalivePacket, nil, nil) // Empty data means keepalive
+ if _, err := conn.Write(keepalivePacket); err != nil {
+ log.Fatalf("error writing keepalive message: %s", err)
+ }
+}
diff --git a/contrib/wireguard-tools/contrib/external-tests/haskell/Setup.hs b/contrib/wireguard-tools/contrib/external-tests/haskell/Setup.hs
new file mode 100644
--- /dev/null
+++ b/contrib/wireguard-tools/contrib/external-tests/haskell/Setup.hs
@@ -0,0 +1,2 @@
+import Distribution.Simple
+main = defaultMain
diff --git a/contrib/wireguard-tools/contrib/external-tests/haskell/package.yaml b/contrib/wireguard-tools/contrib/external-tests/haskell/package.yaml
new file mode 100644
--- /dev/null
+++ b/contrib/wireguard-tools/contrib/external-tests/haskell/package.yaml
@@ -0,0 +1,36 @@
+name: cacophony-wg
+version: 0.1.0
+license: PublicDomain
+maintainer: John Galt <jgalt@centromere.net>
+category: Cryptography
+ghc-options: -Wall
+
+executables:
+ cacophony-wg:
+ main: Main.hs
+ source-dirs: src
+
+ dependencies:
+ - base
+ - base16-bytestring
+ - base64-bytestring
+ - blake2
+ - bytestring
+ - cacophony >= 0.10
+ - cereal
+ - cryptonite
+ - memory
+ - network
+ - time
+
+ ghc-options:
+ - -O2
+ - -rtsopts
+ - -threaded
+ - -with-rtsopts=-N
+
+ other-modules:
+ - Data.Time.TAI64
+
+ default-extensions:
+ - OverloadedStrings
diff --git a/contrib/wireguard-tools/contrib/external-tests/haskell/src/Data/Time/TAI64.hs b/contrib/wireguard-tools/contrib/external-tests/haskell/src/Data/Time/TAI64.hs
new file mode 100644
--- /dev/null
+++ b/contrib/wireguard-tools/contrib/external-tests/haskell/src/Data/Time/TAI64.hs
@@ -0,0 +1,86 @@
+module Data.Time.TAI64 (
+ TAI64(..)
+ , TAI64N(..)
+ , TAI64NA(..)
+ , posixToTAI64
+ , posixToTAI64N
+ , posixToTAI64NA
+ , getCurrentTAI64
+ , getCurrentTAI64N
+ , getCurrentTAI64NA
+ , tAI64ToPosix
+ , tAI64NToPosix
+ , tAI64NAToPosix
+) where
+
+import Data.Serialize
+import Control.Monad
+import Data.Word
+
+import Data.Time.Clock
+import Data.Time.Clock.POSIX
+
+import Numeric
+
+data TAI64 = TAI64
+ {-# UNPACK #-} !Word64
+ deriving (Eq, Ord)
+
+data TAI64N = TAI64N
+ {-# UNPACK #-} !TAI64
+ {-# UNPACK #-} !Word32
+ deriving (Eq, Ord, Show)
+
+data TAI64NA = TAI64NA
+ {-# UNPACK #-} !TAI64N
+ {-# UNPACK #-} !Word32
+ deriving (Eq, Ord, Show)
+
+instance Show TAI64 where
+ show (TAI64 t) = "TAI64 0x" ++ showHex t ""
+
+instance Serialize TAI64 where
+ put (TAI64 t) = putWord64be t
+ get = liftM TAI64 get
+
+instance Serialize TAI64N where
+ put (TAI64N t' nt) = put t' >> putWord32be nt
+ get = liftM2 TAI64N get get
+
+instance Serialize TAI64NA where
+ put (TAI64NA t' at) = put t' >> putWord32be at
+ get = liftM2 TAI64NA get get
+
+
+posixToTAI64 :: POSIXTime -> TAI64
+posixToTAI64 = TAI64 . (2^62 +) . truncate . realToFrac
+
+posixToTAI64N :: POSIXTime -> TAI64N
+posixToTAI64N pt = TAI64N t' ns where
+ t' = posixToTAI64 pt
+ ns = (`mod` 10^9) $ truncate (pts * 10**9)
+ pts = realToFrac pt
+
+posixToTAI64NA :: POSIXTime -> TAI64NA -- | PICOsecond precision
+posixToTAI64NA pt = TAI64NA t' as where
+ t' = posixToTAI64N pt
+ as = (`mod` 10^9) $ truncate (pts * 10**18)
+ pts = realToFrac pt
+
+getCurrentTAI64 :: IO TAI64
+getCurrentTAI64N :: IO TAI64N
+getCurrentTAI64NA :: IO TAI64NA
+getCurrentTAI64 = liftM posixToTAI64 getPOSIXTime
+getCurrentTAI64N = liftM posixToTAI64N getPOSIXTime
+getCurrentTAI64NA = liftM posixToTAI64NA getPOSIXTime
+
+tAI64ToPosix :: TAI64 -> POSIXTime
+tAI64ToPosix (TAI64 s) = fromRational . fromIntegral $ s - 2^62
+
+tAI64NToPosix :: TAI64N -> POSIXTime
+tAI64NToPosix (TAI64N t' n) = tAI64ToPosix t' + nanopart where
+ nanopart = fromRational $ (toRational $ 10**(-9)) * toRational n -- TODO: optimize?
+
+tAI64NAToPosix :: TAI64NA -> POSIXTime
+tAI64NAToPosix (TAI64NA t' a) = tAI64NToPosix t' + attopart where
+ attopart = fromRational $ (toRational $ 10**(-18)) * toRational a
diff --git a/contrib/wireguard-tools/contrib/external-tests/haskell/src/Main.hs b/contrib/wireguard-tools/contrib/external-tests/haskell/src/Main.hs
new file mode 100644
--- /dev/null
+++ b/contrib/wireguard-tools/contrib/external-tests/haskell/src/Main.hs
@@ -0,0 +1,138 @@
+module Main where
+
+import Control.Monad (void)
+import Crypto.Hash.BLAKE2.BLAKE2s (hash)
+import Data.ByteArray (ScrubbedBytes, convert)
+import Data.ByteString (ByteString, replicate, take, drop)
+import qualified Data.ByteString.Base16 as B16
+import qualified Data.ByteString.Base64 as B64
+import Data.Maybe (fromMaybe)
+import Data.Monoid ((<>))
+import qualified Data.Serialize as S
+import Network.Socket
+import qualified Network.Socket.ByteString as NBS
+import Prelude hiding (replicate, take, drop)
+
+import Crypto.Noise
+import Crypto.Noise.Cipher
+import Crypto.Noise.Cipher.ChaChaPoly1305
+import Crypto.Noise.DH
+import Crypto.Noise.DH.Curve25519
+import Crypto.Noise.HandshakePatterns (noiseIKpsk2)
+import Crypto.Noise.Hash hiding (hash)
+import Crypto.Noise.Hash.BLAKE2s
+
+import Data.Time.TAI64
+
+sampleICMPRequest :: ByteString
+sampleICMPRequest = fst . B16.decode $
+ "450000250000000014018f5b0abd81020abd810108001bfa039901b6576972654775617264"
+
+validateICMPResponse :: ByteString
+ -> Bool
+validateICMPResponse r =
+ -- Strip off part of IPv4 header because this is only a demo.
+ drop 12 sample == drop 12 r
+ where
+ sample = fst . B16.decode $ "45000025e3030000400180570abd81010abd8102000023fa039901b65769726547756172640000000000000000000000"
+
+unsafeMessage :: (Cipher c, DH d, Hash h)
+ => Bool
+ -> Maybe ScrubbedBytes
+ -> ScrubbedBytes
+ -> NoiseState c d h
+ -> (ScrubbedBytes, NoiseState c d h)
+unsafeMessage write mpsk msg ns = case operation msg ns of
+ NoiseResultMessage ct ns' -> (ct, ns')
+
+ NoiseResultNeedPSK ns' -> case mpsk of
+ Nothing -> error "psk required but not provided"
+ Just k -> case operation k ns' of
+ NoiseResultMessage ct ns'' -> (ct, ns'')
+ _ -> error "something terrible happened"
+
+ _ -> error "something terrible happened"
+ where
+ operation = if write then writeMessage else readMessage
+
+main :: IO ()
+main = do
+ let ip = "demo.wireguard.com"
+ port = "12913"
+ myKeyB64 = "WAmgVYXkbT2bCtdcDwolI88/iVi/aV3/PHcUBTQSYmo=" -- private key
+ serverKeyB64 = "qRCwZSKInrMAq5sepfCdaCsRJaoLe5jhtzfiw7CjbwM=" -- public key
+ pskB64 = "FpCyhws9cxwWoV4xELtfJvjJN+zQVRPISllRWgeopVE="
+
+ addrInfo <- head <$> getAddrInfo Nothing (Just ip) (Just port)
+ sock <- socket (addrFamily addrInfo) Datagram defaultProtocol
+
+ let addr = addrAddress addrInfo
+ myStaticKey = fromMaybe (error "invalid private key")
+ . dhBytesToPair
+ . convert
+ . either (error "error Base64 decoding my private key") id
+ . B64.decode
+ $ myKeyB64 :: KeyPair Curve25519
+
+ serverKey = fromMaybe (error "invalid public key")
+ . dhBytesToPub
+ . convert
+ . either (error "error Base64 decoding server public key") id
+ . B64.decode
+ $ serverKeyB64 :: PublicKey Curve25519
+
+ psk = convert
+ . either (error "error decoding PSK") id
+ . B64.decode
+ $ pskB64 :: ScrubbedBytes
+
+ myEphemeralKey <- dhGenKey
+
+ let dho = defaultHandshakeOpts InitiatorRole "WireGuard v1 zx2c4 Jason@zx2c4.com"
+ opts = setLocalEphemeral (Just myEphemeralKey)
+ . setLocalStatic (Just myStaticKey)
+ . setRemoteStatic (Just serverKey)
+ $ dho
+ ns0 = noiseState opts noiseIKpsk2 :: NoiseState ChaChaPoly1305 Curve25519 BLAKE2s
+
+ tai64n <- convert . S.encode <$> getCurrentTAI64N
+
+ -- Handshake: Initiator to responder -----------------------------------------
+
+ let (msg0, ns1) = unsafeMessage True Nothing tai64n ns0
+ macKey = hash 32 mempty $ "mac1----" `mappend` (convert . dhPubToBytes) serverKey
+ initiation = "\x01\x00\x00\x00\x1c\x00\x00\x00" <> convert msg0 -- sender index = 28 to match other examples
+ mac1 = hash 16 macKey initiation
+
+ void $ NBS.sendTo sock (initiation <> mac1 <> replicate 16 0) addr
+
+ -- Handshake: Responder to initiator -----------------------------------------
+
+ (response0, _) <- NBS.recvFrom sock 1024
+
+ let theirIndex = take 4 . drop 4 $ response0
+ (_, ns2) = unsafeMessage False (Just psk) (convert . take 48 . drop 12 $ response0) ns1
+
+ -- ICMP: Initiator to responder ----------------------------------------------
+
+ let (msg1, ns3) = unsafeMessage True Nothing (convert sampleICMPRequest) ns2
+ icmp = "\x04\x00\x00\x00" <> theirIndex <> replicate 8 0 <> convert msg1
+
+ void $ NBS.sendTo sock icmp addr
+
+ -- ICMP: Responder to initiator ----------------------------------------------
+
+ (response1, _) <- NBS.recvFrom sock 1024
+
+ let (icmpPayload, ns4) = unsafeMessage False Nothing (convert . drop 16 $ response1) ns3
+
+ -- KeepAlive: Initiator to responder -----------------------------------------
+
+ if validateICMPResponse . convert $ icmpPayload
+ then do
+ let (msg2, _) = unsafeMessage True Nothing mempty ns4
+ keepAlive = "\x04\x00\x00\x00" <> theirIndex <> "\x01" <> replicate 7 0 <> convert msg2
+
+ void $ NBS.sendTo sock keepAlive addr
+
+ else error "unexpected ICMP response from server!"
diff --git a/contrib/wireguard-tools/contrib/external-tests/haskell/stack.yaml b/contrib/wireguard-tools/contrib/external-tests/haskell/stack.yaml
new file mode 100644
--- /dev/null
+++ b/contrib/wireguard-tools/contrib/external-tests/haskell/stack.yaml
@@ -0,0 +1,6 @@
+resolver: lts-8.18
+packages:
+ - '.'
+extra-deps: []
+flags: {}
+extra-package-dbs: []
diff --git a/contrib/wireguard-tools/contrib/external-tests/python/main.py b/contrib/wireguard-tools/contrib/external-tests/python/main.py
new file mode 100755
--- /dev/null
+++ b/contrib/wireguard-tools/contrib/external-tests/python/main.py
@@ -0,0 +1,94 @@
+#!/usr/bin/python3
+
+# SPDX-License-Identifier: MIT
+# Author: Piotr Lizonczyk <plizonczyk.public@gmail.com>
+
+import base64
+import datetime
+from hashlib import blake2s
+import socket
+import struct
+
+from scapy.layers.inet import IP, ICMP
+
+from noise.connection import NoiseConnection, Keypair
+
+
+address = ('demo.wireguard.com', 12913)
+
+our_private = base64.b64decode('WAmgVYXkbT2bCtdcDwolI88/iVi/aV3/PHcUBTQSYmo=')
+their_public = base64.b64decode('qRCwZSKInrMAq5sepfCdaCsRJaoLe5jhtzfiw7CjbwM=')
+preshared = base64.b64decode('FpCyhws9cxwWoV4xELtfJvjJN+zQVRPISllRWgeopVE=')
+prologue = b'WireGuard v1 zx2c4 Jason@zx2c4.com'
+
+noise = NoiseConnection.from_name(b'Noise_IKpsk2_25519_ChaChaPoly_BLAKE2s')
+noise.set_as_initiator()
+noise.set_keypair_from_private_bytes(Keypair.STATIC, our_private)
+noise.set_keypair_from_public_bytes(Keypair.REMOTE_STATIC, their_public)
+noise.set_psks(psk=preshared)
+noise.set_prologue(prologue)
+noise.start_handshake()
+
+sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
+
+
+# 1. Prepare and send handshake initiation packet
+now = datetime.datetime.now()
+tai = struct.pack('!qi', 4611686018427387914 + int(now.timestamp()), int(now.microsecond * 1e3))
+initiation_packet = b'\x01' # Type: initiation
+initiation_packet += b'\x00' * 3 # Reserved
+initiation_packet += struct.pack('<i', 28) # Sender index: 28 (arbitrary)
+initiation_packet += noise.write_message(payload=tai)
+mac_key = blake2s(b'mac1----' + their_public).digest()
+initiation_packet += blake2s(initiation_packet, digest_size=16, key=mac_key).digest()
+initiation_packet += b'\x00' * 16
+
+sock.sendto(initiation_packet, address)
+
+
+# 2. Receive response to finalize handshake
+response_packet = sock.recv(92)
+assert response_packet[0] == 2 # Type: response
+assert response_packet[1:4] == b'\x00' * 3 # Reserved
+their_index, our_index = struct.unpack('<ii', response_packet[4:12])
+assert our_index == 28
+payload = noise.read_message(response_packet[12:60])
+assert payload == b''
+assert noise.handshake_finished
+
+
+# 3. Prepare, encrypt and send ping packet
+icmp_packet = ICMP(type=8, id=921, seq=438)/b'WireGuard'
+ip_packet = IP(proto=1, ttl=20, src="10.189.129.2", dst="10.189.129.1", id=0)/icmp_packet
+ping_packet = b'\x04' # Type: data
+ping_packet += b'\x00' * 3 # Reserved
+ping_packet += struct.pack('<iq', their_index, 0)
+ping_packet += noise.encrypt(bytes(ip_packet))
+
+sock.sendto(ping_packet, address)
+
+
+# 4. Retrieve ping response, decrypt and verify
+encrypted_response = sock.recv(80)
+assert encrypted_response[0] == 4 # Type: data
+assert encrypted_response[1:4] == b'\x00' * 3 # Reserved
+our_index, nonce = struct.unpack('<iq', encrypted_response[4:16])
+assert our_index == 28
+assert nonce == 0
+ip = IP(noise.decrypt(encrypted_response[16:]))
+icmp = ip[1]
+payload = ip[2]
+assert icmp.type == 0
+assert icmp.code == 0
+assert icmp.id == 921
+assert icmp.seq == 438
+assert payload.load == b'WireGuard'
+
+
+# 5. Send keepalive
+keepalive = b'\x04' # Type: data
+keepalive += b'\x00' * 3 # Reserved
+keepalive += struct.pack('<iq', their_index, 1)
+keepalive += noise.encrypt(b'')
+
+sock.sendto(keepalive, address)
diff --git a/contrib/wireguard-tools/contrib/external-tests/rust/.gitignore b/contrib/wireguard-tools/contrib/external-tests/rust/.gitignore
new file mode 100644
--- /dev/null
+++ b/contrib/wireguard-tools/contrib/external-tests/rust/.gitignore
@@ -0,0 +1,2 @@
+Cargo.lock
+target/
diff --git a/contrib/wireguard-tools/contrib/external-tests/rust/Cargo.toml b/contrib/wireguard-tools/contrib/external-tests/rust/Cargo.toml
new file mode 100644
--- /dev/null
+++ b/contrib/wireguard-tools/contrib/external-tests/rust/Cargo.toml
@@ -0,0 +1,16 @@
+[package]
+name = "wireguard-ping"
+version = "0.1.0"
+authors = ["jason@zx2c4.com", "me@jake.su"]
+publish = false
+
+[dependencies]
+snow = "^0.1.0-preview"
+base64 = "^0.5"
+rust-crypto = "*"
+byteorder = "*"
+time = "*"
+
+[dependencies.pnet]
+version = "*"
+features = [ ]
diff --git a/contrib/wireguard-tools/contrib/external-tests/rust/src/main.rs b/contrib/wireguard-tools/contrib/external-tests/rust/src/main.rs
new file mode 100644
--- /dev/null
+++ b/contrib/wireguard-tools/contrib/external-tests/rust/src/main.rs
@@ -0,0 +1,137 @@
+/* Copyright (C) 2015-2020 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved. */
+
+extern crate snow;
+extern crate base64;
+extern crate time;
+extern crate byteorder;
+extern crate crypto;
+extern crate pnet;
+
+use byteorder::{ByteOrder, BigEndian, LittleEndian};
+use crypto::blake2s::Blake2s;
+use snow::NoiseBuilder;
+use pnet::packet::Packet;
+use pnet::packet::ip::IpNextHeaderProtocols;
+use pnet::packet::ipv4::{MutableIpv4Packet, self};
+use pnet::packet::icmp::{MutableIcmpPacket, IcmpTypes, echo_reply, echo_request, self};
+use std::net::*;
+use std::str::FromStr;
+
+static TEST_SERVER: &'static str = "demo.wireguard.com:12913";
+
+fn memcpy(out: &mut [u8], data: &[u8]) {
+ out[..data.len()].copy_from_slice(data);
+}
+
+fn main() {
+ let socket = UdpSocket::bind("0.0.0.0:0").unwrap();
+
+ let their_public = base64::decode(&"qRCwZSKInrMAq5sepfCdaCsRJaoLe5jhtzfiw7CjbwM=").unwrap();
+ let my_private = base64::decode(&"WAmgVYXkbT2bCtdcDwolI88/iVi/aV3/PHcUBTQSYmo=").unwrap();
+ let my_preshared = base64::decode(&"FpCyhws9cxwWoV4xELtfJvjJN+zQVRPISllRWgeopVE=").unwrap();
+
+ let mut noise = NoiseBuilder::new("Noise_IKpsk2_25519_ChaChaPoly_BLAKE2s".parse().unwrap())
+ .local_private_key(&my_private[..])
+ .remote_public_key(&their_public[..])
+ .prologue("WireGuard v1 zx2c4 Jason@zx2c4.com".as_bytes())
+ .psk(2, &my_preshared[..])
+ .build_initiator().unwrap();
+
+ let now = time::get_time();
+ let mut tai64n = [0; 12];
+ BigEndian::write_i64(&mut tai64n[0..], 4611686018427387914 + now.sec);
+ BigEndian::write_i32(&mut tai64n[8..], now.nsec);
+ let mut initiation_packet = [0; 148];
+ initiation_packet[0] = 1; /* Type: Initiation */
+ initiation_packet[1] = 0; /* Reserved */
+ initiation_packet[2] = 0; /* Reserved */
+ initiation_packet[3] = 0; /* Reserved */
+ LittleEndian::write_u32(&mut initiation_packet[4..], 28); /* Sender index: 28 (arbitrary) */
+ noise.write_message(&tai64n, &mut initiation_packet[8..]).unwrap();
+ let mut mac_key_input = [0; 40];
+ let mut mac_key = [0; 32];
+ memcpy(&mut mac_key_input, b"mac1----");
+ memcpy(&mut mac_key_input[8..], &their_public);
+ Blake2s::blake2s(&mut mac_key, &mac_key_input, &[0; 0]);
+ let mut mac = [0; 16];
+ Blake2s::blake2s(&mut mac, &initiation_packet[0..116], &mac_key);
+ memcpy(&mut initiation_packet[116..], &mac);
+ socket.send_to(&initiation_packet, TEST_SERVER).unwrap();
+
+ let mut response_packet = [0; 92];
+ socket.recv_from(&mut response_packet).unwrap();
+ assert!(response_packet[0] == 2 /* Type: Response */);
+ assert!(response_packet[1] == 0 /* Reserved */);
+ assert!(response_packet[2] == 0 /* Reserved */);
+ assert!(response_packet[3] == 0 /* Reserved */);
+ let their_index = LittleEndian::read_u32(&response_packet[4..]);
+ let our_index = LittleEndian::read_u32(&response_packet[8..]);
+ assert!(our_index == 28);
+ let payload_len = noise.read_message(&response_packet[12..60], &mut []).unwrap();
+ assert!(payload_len == 0);
+ noise = noise.into_transport_mode().unwrap();
+
+ let mut icmp_packet = [0; 48];
+ {
+ let mut ipv4 = MutableIpv4Packet::new(&mut icmp_packet).unwrap();
+ ipv4.set_version(4);
+ ipv4.set_header_length(5);
+ ipv4.set_total_length(37);
+ ipv4.set_ttl(20);
+ ipv4.set_next_level_protocol(IpNextHeaderProtocols::Icmp);
+ ipv4.set_source(Ipv4Addr::from_str("10.189.129.2").unwrap());
+ ipv4.set_destination(Ipv4Addr::from_str("10.189.129.1").unwrap());
+ let checksum = ipv4::checksum(&ipv4.to_immutable());
+ ipv4.set_checksum(checksum);
+ }
+ {
+ let mut icmp = echo_request::MutableEchoRequestPacket::new(&mut icmp_packet[20..]).unwrap();
+ icmp.set_icmp_type(IcmpTypes::EchoRequest);
+ icmp.set_icmp_code(echo_request::IcmpCodes::NoCode);
+ icmp.set_identifier(921);
+ icmp.set_sequence_number(438);
+ icmp.set_payload(b"WireGuard");
+ }
+ {
+ let mut icmp = MutableIcmpPacket::new(&mut icmp_packet[20..]).unwrap();
+ let checksum = icmp::checksum(&icmp.to_immutable());
+ icmp.set_checksum(checksum);
+ }
+
+ let mut ping_packet = [0; 80];
+ ping_packet[0] = 4; /* Type: Data */
+ ping_packet[1] = 0; /* Reserved */
+ ping_packet[2] = 0; /* Reserved */
+ ping_packet[3] = 0; /* Reserved */
+ LittleEndian::write_u32(&mut ping_packet[4..], their_index);
+ LittleEndian::write_u64(&mut ping_packet[8..], 0);
+ noise.write_message(&icmp_packet, &mut ping_packet[16..]).unwrap();
+ socket.send_to(&ping_packet, TEST_SERVER).unwrap();
+
+ socket.recv_from(&mut ping_packet).unwrap();
+ assert!(ping_packet[0] == 4 /* Type: Data */);
+ assert!(ping_packet[1] == 0 /* Reserved */);
+ assert!(ping_packet[2] == 0 /* Reserved */);
+ assert!(ping_packet[3] == 0 /* Reserved */);
+ let our_index_received = LittleEndian::read_u32(&ping_packet[4..]);
+ assert!(our_index_received == 28);
+ let nonce = LittleEndian::read_u64(&ping_packet[8..]);
+ assert!(nonce == 0);
+ let payload_len = noise.read_message(&ping_packet[16..], &mut icmp_packet).unwrap();
+ assert!(payload_len == 48);
+ let icmp_reply = echo_reply::EchoReplyPacket::new(&icmp_packet[20..37]).unwrap();
+ assert!(icmp_reply.get_icmp_type() == IcmpTypes::EchoReply && icmp_reply.get_icmp_code() == echo_reply::IcmpCodes::NoCode);
+ assert!(icmp_reply.get_identifier() == 921 && icmp_reply.get_sequence_number() == 438);
+ assert!(icmp_reply.payload() == b"WireGuard");
+
+ let mut keepalive_packet = [0; 32];
+ keepalive_packet[0] = 4; /* Type: Data */
+ keepalive_packet[1] = 0; /* Reserved */
+ keepalive_packet[2] = 0; /* Reserved */
+ keepalive_packet[3] = 0; /* Reserved */
+ LittleEndian::write_u32(&mut keepalive_packet[4..], their_index);
+ LittleEndian::write_u64(&mut keepalive_packet[8..], 1);
+ let empty_payload = [0; 0]; /* Empty payload means keepalive */
+ noise.write_message(&empty_payload, &mut keepalive_packet[16..]).unwrap();
+ socket.send_to(&keepalive_packet, TEST_SERVER).unwrap();
+}
diff --git a/contrib/wireguard-tools/contrib/extract-handshakes/.gitignore b/contrib/wireguard-tools/contrib/extract-handshakes/.gitignore
new file mode 100644
--- /dev/null
+++ b/contrib/wireguard-tools/contrib/extract-handshakes/.gitignore
@@ -0,0 +1,3 @@
+offset-finder.o
+offset-finder
+offsets.include
diff --git a/contrib/wireguard-tools/contrib/extract-handshakes/Makefile b/contrib/wireguard-tools/contrib/extract-handshakes/Makefile
new file mode 100644
--- /dev/null
+++ b/contrib/wireguard-tools/contrib/extract-handshakes/Makefile
@@ -0,0 +1,24 @@
+ifeq ($(KERNELRELEASE),)
+KERNELDIR ?= /lib/modules/$(shell uname -r)/build
+PWD := $(shell pwd)
+CFLAGS ?= -O3 -march=native
+CFLAGS += -Wall -pedantic -std=gnu11
+
+offsets.include: offset-finder
+ ./$^ > $@
+
+offset-finder: offset-finder.c offset-finder.o
+ $(CC) $(CFLAGS) $(CPPFLAGS) -o $@ $^
+
+offset-finder.o: offset-finder.c
+ $(MAKE) -C $(KERNELDIR) M=$(PWD) $@
+ objcopy -j '.rodata*' $@ $@
+
+clean:
+ rm -f offset-finder offsets.include
+ $(MAKE) -C $(KERNELDIR) M=$(PWD) clean
+
+.PHONY: clean
+else
+obj-m := offset-finder.o
+endif
diff --git a/contrib/wireguard-tools/contrib/extract-handshakes/README b/contrib/wireguard-tools/contrib/extract-handshakes/README
new file mode 100644
--- /dev/null
+++ b/contrib/wireguard-tools/contrib/extract-handshakes/README
@@ -0,0 +1,20 @@
+Handshake Extractor
+===================
+
+This will extract private keys from outgoing handshake sessions, prior
+to them being sent, via kprobes. It exports the bare minimum to be
+able to then decrypt all packets in the handshake and in the subsequent
+transport data session.
+
+Build:
+
+ $ make
+
+Run (as root):
+
+ # ./extract-handshakes.sh
+ New handshake session:
+ LOCAL_STATIC_PRIVATE_KEY = QChaGDXeH3eQsbFAhueUNWFdq9KfpF3yl+eITjZbXEk=
+ REMOTE_STATIC_PUBLIC_KEY = HzgTY6aWXtuSyW/PUquZtg8LB/DyMwEXGkPiEmdSsUU=
+ LOCAL_EPHEMERAL_PRIVATE_KEY = UNGdRHuKDeqbFvmiV5FD4wP7a8PqI6v3Xnnz6Jc6NXQ=
+ PRESHARED_KEY = AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=
diff --git a/contrib/wireguard-tools/contrib/extract-handshakes/extract-handshakes.sh b/contrib/wireguard-tools/contrib/extract-handshakes/extract-handshakes.sh
new file mode 100755
--- /dev/null
+++ b/contrib/wireguard-tools/contrib/extract-handshakes/extract-handshakes.sh
@@ -0,0 +1,80 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+#
+# Copyright (C) 2015-2020 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
+# Copyright (C) 2017-2018 Peter Wu <peter@lekensteyn.nl>. All Rights Reserved.
+
+set -e
+
+ME_DIR="${BASH_SOURCE[0]}"
+ME_DIR="${ME_DIR%/*}"
+source "$ME_DIR/offsets.include" || { echo "Did you forget to run make?" >&2; exit 1; }
+
+case "$(uname -m)" in
+ x86_64) ARGUMENT_REGISTER="%si" ;;
+ i386|i686) ARGUMENT_REGISTER="%dx" ;;
+ aarch64) ARGUMENT_REGISTER="%x1" ;;
+ arm) ARGUMENT_REGISTER="%r1" ;;
+ *) echo "ERROR: Unknown architecture" >&2; exit 1 ;;
+esac
+
+ARGS=( )
+REGEX=".*: idxadd: .*"
+for key in "${!OFFSETS[@]}"; do
+ values="${OFFSETS[$key]}"
+ values=( ${values//,/ } )
+ for i in {0..3}; do
+ value="$ARGUMENT_REGISTER"
+ for indirection in "${values[@]:1}"; do
+ value="+$indirection($value)"
+ done
+ value="+$((i * 8 + values[0]))($value)"
+ ARGS+=( "${key,,}$i=$value:x64" )
+ REGEX="$REGEX ${key,,}$i=0x([0-9a-f]+)"
+ done
+done
+
+turn_off() {
+ set +e
+ [[ -f /sys/kernel/debug/tracing/events/wireguard/idxadd/enable ]] || exit
+ echo 0 > /sys/kernel/debug/tracing/events/wireguard/idxadd/enable
+ echo "-:wireguard/idxadd" >> /sys/kernel/debug/tracing/kprobe_events
+ exit
+}
+
+trap turn_off INT TERM EXIT
+echo "p:wireguard/idxadd index_hashtable_insert ${ARGS[*]}" >> /sys/kernel/debug/tracing/kprobe_events
+echo 1 > /sys/kernel/debug/tracing/events/wireguard/idxadd/enable
+
+unpack_u64() {
+ local i expanded="$1"
+ if [[ $ENDIAN == big ]]; then
+ printf -v expanded "%.*s$expanded" $((16 - ${#expanded})) 0000000000000000
+ for i in {0..7}; do
+ echo -n "\\x${expanded:(i * 2):2}"
+ done
+ elif [[ $ENDIAN == little ]]; then
+ (( ${#expanded} % 2 == 1 )) && expanded="0$expanded"
+ expanded="${expanded}0000000000000000"
+ for i in {0..7}; do
+ echo -n "\\x${expanded:((7 - i) * 2):2}"
+ done
+ else
+ echo "ERROR: Unable to determine endian" >&2
+ exit 1
+ fi
+}
+
+while read -r line; do
+ [[ $line =~ $REGEX ]] || continue
+ echo "New handshake session:"
+ j=1
+ for key in "${!OFFSETS[@]}"; do
+ bytes=""
+ for i in {0..3}; do
+ bytes="$bytes$(unpack_u64 "${BASH_REMATCH[j]}")"
+ ((++j))
+ done
+ echo " $key = $(printf "$bytes" | base64)"
+ done
+done < /sys/kernel/debug/tracing/trace_pipe
diff --git a/contrib/wireguard-tools/contrib/extract-handshakes/offset-finder.c b/contrib/wireguard-tools/contrib/extract-handshakes/offset-finder.c
new file mode 100644
--- /dev/null
+++ b/contrib/wireguard-tools/contrib/extract-handshakes/offset-finder.c
@@ -0,0 +1,44 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2015-2020 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
+ */
+
+struct def {
+ const char *name;
+ unsigned long offset;
+ unsigned long indirection_offset;
+};
+extern const struct def defs[];
+
+#ifdef __KERNEL__
+#include "../drivers/net/wireguard/noise.h"
+
+const struct def defs[] = {
+ { "LOCAL_STATIC_PRIVATE_KEY", offsetof(struct noise_static_identity, static_private), offsetof(struct noise_handshake, static_identity) },
+ { "LOCAL_EPHEMERAL_PRIVATE_KEY", offsetof(struct noise_handshake, ephemeral_private), -1 },
+ { "REMOTE_STATIC_PUBLIC_KEY", offsetof(struct noise_handshake, remote_static), -1 },
+ { "PRESHARED_KEY", offsetof(struct noise_handshake, preshared_key), -1 },
+ { NULL, 0 }
+};
+#else
+#include <stdio.h>
+int main(int argc, char *argv[])
+{
+ puts("declare -A OFFSETS=(");
+ for (const struct def *def = defs; def->name; ++def) {
+ printf("\t[%s]=%ld", def->name, def->offset);
+ if (def->indirection_offset != -1)
+ printf(",%ld", def->indirection_offset);
+ putchar('\n');
+ }
+ puts(")");
+#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
+ puts("ENDIAN=big");
+#elif __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
+ puts("ENDIAN=little");
+#else
+#error "Unsupported endianness"
+#endif
+ return 0;
+}
+#endif
diff --git a/contrib/wireguard-tools/contrib/extract-keys/.gitignore b/contrib/wireguard-tools/contrib/extract-keys/.gitignore
new file mode 100644
--- /dev/null
+++ b/contrib/wireguard-tools/contrib/extract-keys/.gitignore
@@ -0,0 +1,4 @@
+config.h
+extract-keys
+config.h
+config
diff --git a/contrib/wireguard-tools/contrib/extract-keys/Makefile b/contrib/wireguard-tools/contrib/extract-keys/Makefile
new file mode 100644
--- /dev/null
+++ b/contrib/wireguard-tools/contrib/extract-keys/Makefile
@@ -0,0 +1,27 @@
+ifeq ($(KERNELRELEASE),)
+KERNELDIR ?= /lib/modules/$(shell uname -r)/build
+PWD := $(shell pwd)
+CFLAGS ?= -O3 -march=native
+CFLAGS += -Wall -pedantic -std=gnu11
+
+extract-keys: extract-keys.c config.h
+ $(CC) $(CFLAGS) $(CPPFLAGS) -D_FILE_OFFSET_BITS=64 -o $@ -lresolv $<
+
+config.o: config.c
+ $(MAKE) -C $(KERNELDIR) M=$(PWD) $@
+ objcopy -j '.rodata*' $@ $@
+
+config: config.c config.o
+ $(CC) $(CFLAGS) $(CPPFLAGS) -o $@ $^
+
+config.h: config
+ ./$< > $@
+
+clean:
+ rm -f extract-keys config config.h
+ $(MAKE) -C $(KERNELDIR) M=$(PWD) clean
+
+.PHONY: clean
+else
+obj-m := config.o
+endif
diff --git a/contrib/wireguard-tools/contrib/extract-keys/README b/contrib/wireguard-tools/contrib/extract-keys/README
new file mode 100644
--- /dev/null
+++ b/contrib/wireguard-tools/contrib/extract-keys/README
@@ -0,0 +1,23 @@
+Key Extractor
+=============
+
+This will extract the symmetric ChaCha20Poly1305 session keys from the kernel
+for a WireGuard interface, for use in making packet dissectors.
+
+
+Build:
+ $ make
+
+Run (as root):
+ # ./extract-keys INTERFACE
+
+Output:
+ REMOTE_KEY_ID SENDING_KEY
+ LOCAL_KEY_ID RECEIVING_KEY
+
+Example:
+ # ./extract-keys wg0
+ 0x57b56068 tMTSEOJpEYFAQV2UviDiYooX0A1AD/ONqrzoQVHa1rQ=
+ 0xa182fd19 xvQSkQ5HTX5RUeJ74eAAb/xfNhdrDThxG91GXZIPKmY=
+ 0x01662508 LbMc84JULzXJiHotSkdSOPZ0bHh6IDwOrbxWLfwosTs=
+ 0xbd819021 4VA8lZ3I1HjnJcWTmhEzBdC92W1Aag9Lnyy2GkroOYI=
diff --git a/contrib/wireguard-tools/contrib/extract-keys/config.c b/contrib/wireguard-tools/contrib/extract-keys/config.c
new file mode 100644
--- /dev/null
+++ b/contrib/wireguard-tools/contrib/extract-keys/config.c
@@ -0,0 +1,39 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2015-2020 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
+ */
+
+struct def {
+ const char *name;
+ long long value;
+};
+extern const struct def defs[];
+
+#ifdef __KERNEL__
+#include "../drivers/net/wireguard/device.h"
+#include "../drivers/net/wireguard/peer.h"
+#include "../drivers/net/wireguard/noise.h"
+const struct def defs[] = {
+ { "SOCK_DEVICE_OFFSET", offsetof(struct sock, sk_user_data) },
+ { "DEVICE_NAME_OFFSET", -ALIGN(sizeof(struct net_device), NETDEV_ALIGN) + offsetof(struct net_device, name) },
+ { "IFNAMSIZ", IFNAMSIZ },
+ { "DEVICE_PEERS_OFFSET", offsetof(struct wg_device, peer_list) },
+ { "PEERS_PEER_OFFSET", -offsetof(struct wg_peer, peer_list) },
+ { "PEER_CURRENTKEY_OFFSET", offsetof(struct wg_peer, keypairs.current_keypair) },
+ { "PEER_PREVIOUSKEY_OFFSET", offsetof(struct wg_peer, keypairs.previous_keypair) },
+ { "PEER_NEXTKEY_OFFSET", offsetof(struct wg_peer, keypairs.next_keypair) },
+ { "KEY_LOCALID_OFFSET", offsetof(struct noise_keypair, entry.index) },
+ { "KEY_REMOTEID_OFFSET", offsetof(struct noise_keypair, remote_index) },
+ { "KEY_SENDING_OFFSET", offsetof(struct noise_keypair, sending.key) },
+ { "KEY_RECEIVING_OFFSET", offsetof(struct noise_keypair, receiving.key) },
+ { NULL, 0 }
+};
+#else
+#include <stdio.h>
+int main(int argc, char *argv[])
+{
+ for (const struct def *def = defs; def->name; ++def)
+ printf("#define %s %lld\n", def->name, def->value);
+ return 0;
+}
+#endif
diff --git a/contrib/wireguard-tools/contrib/extract-keys/extract-keys.c b/contrib/wireguard-tools/contrib/extract-keys/extract-keys.c
new file mode 100644
--- /dev/null
+++ b/contrib/wireguard-tools/contrib/extract-keys/extract-keys.c
@@ -0,0 +1,144 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2015-2020 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
+ */
+
+#include <endian.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <resolv.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include "config.h"
+
+static int fd;
+
+static void open_kmem(void)
+{
+ fd = open("/dev/kmem", O_RDONLY);
+ if (fd < 0) {
+ perror("open(/dev/kmem)");
+ exit(errno);
+ }
+}
+
+static void read_kmem(void *buffer, size_t len, unsigned long addr)
+{
+ if (lseek(fd, addr, SEEK_SET) == (off_t)-1) {
+ perror("lseek");
+ exit(errno);
+ }
+ if (read(fd, buffer, len) != len) {
+ perror("read");
+ exit(errno);
+ }
+}
+
+static inline unsigned int read_int(unsigned long addr)
+{
+ unsigned int ret;
+ read_kmem(&ret, sizeof(ret), addr);
+ return ret;
+}
+
+static inline unsigned long read_long(unsigned long addr)
+{
+ unsigned long ret;
+ read_kmem(&ret, sizeof(ret), addr);
+ return ret;
+}
+
+static unsigned long find_interface(const char *interface)
+{
+ FILE *f = fopen("/proc/net/udp", "r");
+ char line[256], *ptr;
+ unsigned long addr = 0;
+ char name[IFNAMSIZ + 1] = { 0 };
+
+ if (!f) {
+ perror("fopen(/proc/net/udp)");
+ exit(errno);
+ }
+ if (!fgets(line, 256, f))
+ goto out;
+ while (fgets(line, 256, f)) {
+ ptr = line + strlen(line) - 1;
+ while (*--ptr == ' ');
+ while (*--ptr != ' ');
+ while (*(--ptr - 1) != ' ');
+ addr = strtoul(ptr, NULL, 16);
+ if (!addr)
+ continue;
+ addr = read_long(addr + SOCK_DEVICE_OFFSET);
+ if (!addr)
+ continue;
+ read_kmem(name, IFNAMSIZ, addr + DEVICE_NAME_OFFSET);
+ if (!strcmp(name, interface))
+ goto out;
+ }
+ addr = 0;
+out:
+ fclose(f);
+ return addr;
+}
+
+static bool print_key(unsigned long key)
+{
+ unsigned char sending[32], receiving[32];
+ char sending_b64[45], receiving_b64[45];
+ unsigned int local_index, remote_index;
+
+ if (!key)
+ return false;
+
+ local_index = le32toh(read_int(key + KEY_LOCALID_OFFSET));
+ remote_index = le32toh(read_int(key + KEY_REMOTEID_OFFSET));
+ read_kmem(sending, 32, key + KEY_SENDING_OFFSET);
+ read_kmem(receiving, 32, key + KEY_RECEIVING_OFFSET);
+
+ b64_ntop(sending, 32, sending_b64, 45);
+ b64_ntop(receiving, 32, receiving_b64, 45);
+
+ printf("0x%08x %s\n", local_index, receiving_b64);
+ printf("0x%08x %s\n", remote_index, sending_b64);
+ return true;
+}
+
+static bool walk_peers(unsigned long peer_head)
+{
+ unsigned long peer, peer_entry;
+ bool found = false;
+ for (peer_entry = read_long(peer_head); peer_entry != peer_head; peer_entry = read_long(peer_entry)) {
+ peer = peer_entry + PEERS_PEER_OFFSET;
+ if (print_key(read_long(peer + PEER_CURRENTKEY_OFFSET)))
+ found = true;
+ if (print_key(read_long(peer + PEER_PREVIOUSKEY_OFFSET)))
+ found = true;
+ if (print_key(read_long(peer + PEER_NEXTKEY_OFFSET)))
+ found = true;
+ }
+ return found;
+}
+
+int main(int argc, char *argv[])
+{
+ unsigned long wireguard_device;
+ if (argc < 2) {
+ fprintf(stderr, "Usage: %s WIREGUARD_INTERFACE\n", argv[0]);
+ return EINVAL;
+ }
+ open_kmem();
+ wireguard_device = find_interface(argv[1]);
+ if (!wireguard_device) {
+ fprintf(stderr, "Could not find interface %s\n", argv[1]);
+ return EBADSLT;
+ }
+ if (!walk_peers(wireguard_device + DEVICE_PEERS_OFFSET)) {
+ fprintf(stderr, "No active sessions\n");
+ return ENOKEY;
+ }
+ return 0;
+}
diff --git a/contrib/wireguard-tools/contrib/highlighter/Makefile b/contrib/wireguard-tools/contrib/highlighter/Makefile
new file mode 100644
--- /dev/null
+++ b/contrib/wireguard-tools/contrib/highlighter/Makefile
@@ -0,0 +1,25 @@
+CFLAGS ?= -O3 -march=native
+CFLAGS += -std=gnu99
+CFLAGS += -Wall
+CFLAGS += -MMD -MP
+
+highlight: highlight.o highlighter.o
+
+fuzz: CC := clang
+fuzz: CFLAGS += -fsanitize=fuzzer
+fuzz: fuzz.c highlighter.c
+
+gui/Makefile: gui/highlight.pro
+ cd gui && qmake
+gui: gui/Makefile
+ @$(MAKE) -C gui
+
+clean:
+ rm -f highlight fuzz *.o *.d
+ @if [ -f gui/Makefile ]; then $(MAKE) -C gui distclean; fi
+
+.PHONY: clean gui
+.DEFAULT_GOAL: highlight
+MAKEFLAGS += --no-print-directory
+
+-include *.d
diff --git a/contrib/wireguard-tools/contrib/highlighter/README b/contrib/wireguard-tools/contrib/highlighter/README
new file mode 100644
--- /dev/null
+++ b/contrib/wireguard-tools/contrib/highlighter/README
@@ -0,0 +1,22 @@
+wg(8) and wg-quick(8) syntax highlighter library
+================================================
+
+highlighter.c contains a simple portable highlighter for the wg(8) and
+wg-quick(8) configuration files. Simply copy `highlight.c` and
+`highlight.h` into your project wholesale.
+
+As a demo, a simple console highlighter program is included, alongside a
+simple Qt5 GUI app to show its usage in realtime.
+
+There is also a basic fuzzer, because why not?
+
+Usage:
+
+ $ make
+ $ ./highlight < path/to/tunnel.conf
+
+ $ make gui
+ $ ./gui/highlight
+
+ $ make fuzz
+ $ ./fuzz -workers=$(nproc) -jobs=$(nproc) ./corpus
diff --git a/contrib/wireguard-tools/contrib/highlighter/fuzz.c b/contrib/wireguard-tools/contrib/highlighter/fuzz.c
new file mode 100644
--- /dev/null
+++ b/contrib/wireguard-tools/contrib/highlighter/fuzz.c
@@ -0,0 +1,22 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2015-2020 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include "highlighter.h"
+
+int LLVMFuzzerTestOneInput(const char *data, size_t size)
+{
+ char *str = strndup(data, size);
+ if (!str)
+ return 0;
+ struct highlight_span *spans = highlight_config(str);
+ if (!spans)
+ return 0;
+ for (struct highlight_span *span = spans; span->type != HighlightEnd; ++span);
+ free(spans);
+ free(str);
+ return 0;
+}
diff --git a/contrib/wireguard-tools/contrib/highlighter/gui/highlight.cpp b/contrib/wireguard-tools/contrib/highlighter/gui/highlight.cpp
new file mode 100644
--- /dev/null
+++ b/contrib/wireguard-tools/contrib/highlighter/gui/highlight.cpp
@@ -0,0 +1,90 @@
+#include <QApplication>
+#include <QPlainTextEdit>
+#include <QPalette>
+#include <QFontDatabase>
+#include <QVBoxLayout>
+#include <QPushButton>
+
+extern "C" {
+#include "../highlighter.h"
+}
+
+static QColor colormap[] = {
+ [HighlightSection] = QColor("#ababab"),
+ [HighlightField] = QColor("#70c0b1"),
+ [HighlightPrivateKey] = QColor("#7aa6da"),
+ [HighlightPublicKey] = QColor("#7aa6da"),
+ [HighlightPresharedKey] = QColor("#7aa6da"),
+ [HighlightIP] = QColor("#b9ca4a"),
+ [HighlightCidr] = QColor("#e78c45"),
+ [HighlightHost] = QColor("#b9ca4a"),
+ [HighlightPort] = QColor("#e78c45"),
+ [HighlightMTU] = QColor("#c397d8"),
+ [HighlightKeepalive] = QColor("#c397d8"),
+ [HighlightComment] = QColor("#969896"),
+ [HighlightDelimiter] = QColor("#7aa6da"),
+#ifndef MOBILE_WGQUICK_SUBSET
+ [HighlightTable] = QColor("#c397d8"),
+ [HighlightFwMark] = QColor("#c397d8"),
+ [HighlightSaveConfig] = QColor("#c397d8"),
+ [HighlightCmd] = QColor("#969896"),
+#endif
+ [HighlightError] = QColor("#d54e53")
+};
+
+int main(int argc, char *argv[])
+{
+ QApplication a(argc, argv);
+ QWidget w;
+ w.setWindowTitle(QObject::tr("WireGuard Configuration Highlighter"));
+ QVBoxLayout v;
+ w.setLayout(&v);
+ QPlainTextEdit e;
+ v.addWidget(&e);
+ QPalette p(e.palette());
+ p.setColor(QPalette::Base, QColor("#010101"));
+ p.setColor(QPalette::Text, QColor("#eaeaea"));
+ e.setPalette(p);
+ QFont f(QFontDatabase::systemFont(QFontDatabase::FixedFont));
+ f.setPointSize(16);
+ e.setFont(f);
+ e.setMinimumSize(400, 500);
+ bool guard = false;
+ QObject::connect(&e, &QPlainTextEdit::textChanged, [&]() {
+ if (guard)
+ return;
+ struct highlight_span *spans = highlight_config(e.toPlainText().toLatin1().data());
+ if (!spans)
+ return;
+ QTextCursor cursor(e.document());
+ QTextCharFormat format;
+ cursor.beginEditBlock();
+ cursor.movePosition(QTextCursor::Start, QTextCursor::MoveAnchor);
+ cursor.movePosition(QTextCursor::End, QTextCursor::KeepAnchor);
+ format.setForeground(p.color(QPalette::Text));
+ format.setUnderlineStyle(QTextCharFormat::NoUnderline);
+ cursor.mergeCharFormat(format);
+ for (struct highlight_span *span = spans; span->type != HighlightEnd; ++span) {
+ cursor.movePosition(QTextCursor::Start, QTextCursor::MoveAnchor);
+ cursor.movePosition(QTextCursor::NextCharacter, QTextCursor::MoveAnchor, span->start);
+ cursor.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor, span->len);
+ format.setForeground(colormap[span->type]);
+ format.setUnderlineStyle(span->type == HighlightError ? QTextCharFormat::SpellCheckUnderline : QTextCharFormat::NoUnderline);
+ cursor.mergeCharFormat(format);
+ }
+ free(spans);
+ guard = true;
+ cursor.endEditBlock();
+ guard = false;
+ });
+ QPushButton b;
+ v.addWidget(&b);
+ b.setText(QObject::tr("&Randomize colors"));
+ QObject::connect(&b, &QPushButton::clicked, [&]() {
+ for (size_t i = 0; i < sizeof(colormap) / sizeof(colormap[0]); ++i)
+ colormap[i] = QColor::fromHsl(qrand() % 360, qrand() % 192 + 64, qrand() % 128 + 128);
+ e.setPlainText(e.toPlainText());
+ });
+ w.show();
+ return a.exec();
+}
diff --git a/contrib/wireguard-tools/contrib/highlighter/gui/highlight.pro b/contrib/wireguard-tools/contrib/highlighter/gui/highlight.pro
new file mode 100644
--- /dev/null
+++ b/contrib/wireguard-tools/contrib/highlighter/gui/highlight.pro
@@ -0,0 +1,5 @@
+QT += core gui widgets
+TEMPLATE = app
+TARGET = highlight
+SOURCES += highlight.cpp ../highlighter.c
+HEADERS += ../highlighter.h
diff --git a/contrib/wireguard-tools/contrib/highlighter/highlight.c b/contrib/wireguard-tools/contrib/highlighter/highlight.c
new file mode 100644
--- /dev/null
+++ b/contrib/wireguard-tools/contrib/highlighter/highlight.c
@@ -0,0 +1,83 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2015-2020 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "highlighter.h"
+
+#define TERMINAL_FG_BLACK "\x1b[30m"
+#define TERMINAL_FG_RED "\x1b[31m"
+#define TERMINAL_FG_GREEN "\x1b[32m"
+#define TERMINAL_FG_YELLOW "\x1b[33m"
+#define TERMINAL_FG_BLUE "\x1b[34m"
+#define TERMINAL_FG_MAGENTA "\x1b[35m"
+#define TERMINAL_FG_CYAN "\x1b[36m"
+#define TERMINAL_FG_WHITE "\x1b[37m"
+#define TERMINAL_FG_DEFAULT "\x1b[39m"
+
+#define TERMINAL_BG_BLACK "\x1b[40m"
+#define TERMINAL_BG_RED "\x1b[41m"
+#define TERMINAL_BG_GREEN "\x1b[42m"
+#define TERMINAL_BG_YELLOW "\x1b[43m"
+#define TERMINAL_BG_BLUE "\x1b[44m"
+#define TERMINAL_BG_MAGENTA "\x1b[45m"
+#define TERMINAL_BG_CYAN "\x1b[46m"
+#define TERMINAL_BG_WHITE "\x1b[47m"
+#define TERMINAL_BG_DEFAULT "\x1b[49m"
+
+#define TERMINAL_BOLD "\x1b[1m"
+#define TERMINAL_NO_BOLD "\x1b[22m"
+#define TERMINAL_UNDERLINE "\x1b[4m"
+#define TERMINAL_NO_UNDERLINE "\x1b[24m"
+
+#define TERMINAL_RESET "\x1b[0m"
+
+static const char *colormap[] = {
+ [HighlightSection] = TERMINAL_FG_BLACK TERMINAL_BOLD,
+ [HighlightField] = TERMINAL_FG_BLUE TERMINAL_BOLD,
+ [HighlightPrivateKey] = TERMINAL_FG_YELLOW TERMINAL_BOLD,
+ [HighlightPublicKey] = TERMINAL_FG_YELLOW TERMINAL_BOLD,
+ [HighlightPresharedKey] = TERMINAL_FG_YELLOW TERMINAL_BOLD,
+ [HighlightIP] = TERMINAL_FG_GREEN,
+ [HighlightCidr] = TERMINAL_FG_YELLOW,
+ [HighlightHost] = TERMINAL_FG_GREEN TERMINAL_BOLD,
+ [HighlightPort] = TERMINAL_FG_MAGENTA,
+ [HighlightMTU] = TERMINAL_FG_BLUE,
+ [HighlightKeepalive] = TERMINAL_FG_BLUE,
+ [HighlightComment] = TERMINAL_FG_CYAN,
+ [HighlightDelimiter] = TERMINAL_FG_CYAN,
+#ifndef MOBILE_WGQUICK_SUBSET
+ [HighlightTable] = TERMINAL_FG_BLUE,
+ [HighlightFwMark] = TERMINAL_FG_BLUE,
+ [HighlightSaveConfig] = TERMINAL_FG_BLUE,
+ [HighlightCmd] = TERMINAL_FG_WHITE,
+#endif
+ [HighlightError] = TERMINAL_FG_RED TERMINAL_UNDERLINE
+};
+
+int main(int argc, char *argv[])
+{
+ char input[1024 * 1024];
+ struct highlight_span *spans;
+ size_t last = 0, total_len;
+
+ total_len = fread(input, 1, sizeof(input) - 1, stdin);
+ input[total_len] = '\0';
+ spans = highlight_config(input);
+
+ fputs(TERMINAL_RESET, stdout);
+ for (struct highlight_span *span = spans; span->type != HighlightEnd; ++span) {
+ fwrite(input + last, 1, span->start - last, stdout);
+ fputs(colormap[span->type], stdout);
+ fwrite(input + span->start, 1, span->len, stdout);
+ fputs(TERMINAL_RESET, stdout);
+ last = span->start + span->len;
+ }
+ fwrite(input + last, 1, total_len - last, stdout);
+
+ free(spans);
+ return 0;
+}
diff --git a/contrib/wireguard-tools/contrib/highlighter/highlighter.h b/contrib/wireguard-tools/contrib/highlighter/highlighter.h
new file mode 100644
--- /dev/null
+++ b/contrib/wireguard-tools/contrib/highlighter/highlighter.h
@@ -0,0 +1,37 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2015-2020 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
+ */
+
+#include <sys/types.h>
+
+enum highlight_type {
+ HighlightSection,
+ HighlightField,
+ HighlightPrivateKey,
+ HighlightPublicKey,
+ HighlightPresharedKey,
+ HighlightIP,
+ HighlightCidr,
+ HighlightHost,
+ HighlightPort,
+ HighlightMTU,
+ HighlightKeepalive,
+ HighlightComment,
+ HighlightDelimiter,
+#ifndef MOBILE_WGQUICK_SUBSET
+ HighlightTable,
+ HighlightFwMark,
+ HighlightSaveConfig,
+ HighlightCmd,
+#endif
+ HighlightError,
+ HighlightEnd
+};
+
+struct highlight_span {
+ enum highlight_type type;
+ size_t start, len;
+};
+
+struct highlight_span *highlight_config(const char *config);
diff --git a/contrib/wireguard-tools/contrib/highlighter/highlighter.c b/contrib/wireguard-tools/contrib/highlighter/highlighter.c
new file mode 100644
--- /dev/null
+++ b/contrib/wireguard-tools/contrib/highlighter/highlighter.c
@@ -0,0 +1,641 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2015-2020 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
+ */
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include "highlighter.h"
+
+typedef struct {
+ const char *s;
+ size_t len;
+} string_span_t;
+
+static bool is_decimal(char c)
+{
+ return c >= '0' && c <= '9';
+}
+
+static bool is_hexadecimal(char c)
+{
+ return is_decimal(c) || ((c | 32) >= 'a' && (c | 32) <= 'f');
+}
+
+static bool is_alphabet(char c)
+{
+ return (c | 32) >= 'a' && (c | 32) <= 'z';
+}
+
+static bool is_same(string_span_t s, const char *c)
+{
+ size_t len = strlen(c);
+
+ if (len != s.len)
+ return false;
+ return !memcmp(s.s, c, len);
+}
+
+static bool is_caseless_same(string_span_t s, const char *c)
+{
+ size_t len = strlen(c);
+
+ if (len != s.len)
+ return false;
+ for (size_t i = 0; i < len; ++i) {
+ char a = c[i], b = s.s[i];
+ if ((unsigned)a - 'a' < 26)
+ a &= 95;
+ if ((unsigned)b - 'a' < 26)
+ b &= 95;
+ if (a != b)
+ return false;
+ }
+ return true;
+}
+
+static bool is_valid_key(string_span_t s)
+{
+ if (s.len != 44 || s.s[43] != '=')
+ return false;
+
+ for (size_t i = 0; i < 42; ++i) {
+ if (!is_decimal(s.s[i]) && !is_alphabet(s.s[i]) &&
+ s.s[i] != '/' && s.s[i] != '+')
+ return false;
+ }
+ switch (s.s[42]) {
+ case 'A':
+ case 'E':
+ case 'I':
+ case 'M':
+ case 'Q':
+ case 'U':
+ case 'Y':
+ case 'c':
+ case 'g':
+ case 'k':
+ case 'o':
+ case 's':
+ case 'w':
+ case '4':
+ case '8':
+ case '0':
+ break;
+ default:
+ return false;
+ }
+ return true;
+}
+
+static bool is_valid_hostname(string_span_t s)
+{
+ size_t num_digit = 0, num_entity = s.len;
+
+ if (s.len > 63 || !s.len)
+ return false;
+ if (s.s[0] == '-' || s.s[s.len - 1] == '-')
+ return false;
+ if (s.s[0] == '.' || s.s[s.len - 1] == '.')
+ return false;
+
+ for (size_t i = 0; i < s.len; ++i) {
+ if (is_decimal(s.s[i])) {
+ ++num_digit;
+ continue;
+ }
+ if (s.s[i] == '.') {
+ --num_entity;
+ continue;
+ }
+
+ if (!is_alphabet(s.s[i]) && s.s[i] != '-')
+ return false;
+
+ if (i && s.s[i] == '.' && s.s[i - 1] == '.')
+ return false;
+ }
+ return num_digit != num_entity;
+}
+
+static bool is_valid_ipv4(string_span_t s)
+{
+ for (size_t j, i = 0, pos = 0; i < 4 && pos < s.len; ++i) {
+ uint32_t val = 0;
+
+ for (j = 0; j < 3 && pos + j < s.len && is_decimal(s.s[pos + j]); ++j)
+ val = 10 * val + s.s[pos + j] - '0';
+ if (j == 0 || (j > 1 && s.s[pos] == '0') || val > 255)
+ return false;
+ if (pos + j == s.len && i == 3)
+ return true;
+ if (s.s[pos + j] != '.')
+ return false;
+ pos += j + 1;
+ }
+ return false;
+}
+
+static bool is_valid_ipv6(string_span_t s)
+{
+ size_t pos = 0;
+ bool seen_colon = false;
+
+ if (s.len < 2)
+ return false;
+ if (s.s[pos] == ':' && s.s[++pos] != ':')
+ return false;
+ if (s.s[s.len - 1] == ':' && s.s[s.len - 2] != ':')
+ return false;
+
+ for (size_t j, i = 0; pos < s.len; ++i) {
+ if (s.s[pos] == ':' && !seen_colon) {
+ seen_colon = true;
+ if (++pos == s.len)
+ break;
+ if (i == 7)
+ return false;
+ continue;
+ }
+ for (j = 0; j < 4 && pos + j < s.len && is_hexadecimal(s.s[pos + j]); ++j);
+ if (j == 0)
+ return false;
+ if (pos + j == s.len && (seen_colon || i == 7))
+ break;
+ if (i == 7)
+ return false;
+ if (s.s[pos + j] != ':') {
+ if (s.s[pos + j] != '.' || (i < 6 && !seen_colon))
+ return false;
+ return is_valid_ipv4((string_span_t){ s.s + pos, s.len - pos });
+ }
+ pos += j + 1;
+ }
+ return true;
+}
+
+static bool is_valid_uint(string_span_t s, bool support_hex, uint64_t min, uint64_t max)
+{
+ uint64_t val = 0;
+
+ /* Bound this around 32 bits, so that we don't have to write overflow logic. */
+ if (s.len > 10 || !s.len)
+ return false;
+
+ if (support_hex && s.len > 2 && s.s[0] == '0' && s.s[1] == 'x') {
+ for (size_t i = 2; i < s.len; ++i) {
+ if ((unsigned)s.s[i] - '0' < 10)
+ val = 16 * val + (s.s[i] - '0');
+ else if (((unsigned)s.s[i] | 32) - 'a' < 6)
+ val = 16 * val + (s.s[i] | 32) - 'a' + 10;
+ else
+ return false;
+ }
+ } else {
+ for (size_t i = 0; i < s.len; ++i) {
+ if (!is_decimal(s.s[i]))
+ return false;
+ val = 10 * val + s.s[i] - '0';
+ }
+ }
+ return val <= max && val >= min;
+}
+
+static bool is_valid_port(string_span_t s)
+{
+ return is_valid_uint(s, false, 0, 65535);
+}
+
+static bool is_valid_mtu(string_span_t s)
+{
+ return is_valid_uint(s, false, 576, 65535);
+}
+
+static bool is_valid_persistentkeepalive(string_span_t s)
+{
+ if (is_same(s, "off"))
+ return true;
+ return is_valid_uint(s, false, 0, 65535);
+}
+
+#ifndef MOBILE_WGQUICK_SUBSET
+
+static bool is_valid_fwmark(string_span_t s)
+{
+ if (is_same(s, "off"))
+ return true;
+ return is_valid_uint(s, true, 0, 4294967295);
+}
+
+static bool is_valid_table(string_span_t s)
+{
+ if (is_same(s, "auto"))
+ return true;
+ if (is_same(s, "off"))
+ return true;
+ /* This pretty much invalidates the other checks, but rt_names.c's
+ * fread_id_name does no validation aside from this. */
+ if (s.len < 512)
+ return true;
+ return is_valid_uint(s, false, 0, 4294967295);
+}
+
+static bool is_valid_saveconfig(string_span_t s)
+{
+ return is_same(s, "true") || is_same(s, "false");
+}
+
+static bool is_valid_prepostupdown(string_span_t s)
+{
+ /* It's probably not worthwhile to try to validate a bash expression.
+ * So instead we just demand non-zero length. */
+ return s.len;
+}
+#endif
+
+static bool is_valid_scope(string_span_t s)
+{
+ if (s.len > 64 || !s.len)
+ return false;
+ for (size_t i = 0; i < s.len; ++i) {
+ if (!is_alphabet(s.s[i]) && !is_decimal(s.s[i]) &&
+ s.s[i] != '_' && s.s[i] != '=' && s.s[i] != '+' &&
+ s.s[i] != '.' && s.s[i] != '-')
+ return false;
+ }
+ return true;
+}
+
+static bool is_valid_endpoint(string_span_t s)
+{
+
+ if (!s.len)
+ return false;
+
+ if (s.s[0] == '[') {
+ bool seen_scope = false;
+ string_span_t hostspan = { s.s + 1, 0 };
+
+ for (size_t i = 1; i < s.len; ++i) {
+ if (s.s[i] == '%') {
+ if (seen_scope)
+ return false;
+ seen_scope = true;
+ if (!is_valid_ipv6(hostspan))
+ return false;
+ hostspan = (string_span_t){ s.s + i + 1, 0 };
+ } else if (s.s[i] == ']') {
+ if (seen_scope) {
+ if (!is_valid_scope(hostspan))
+ return false;
+ } else if (!is_valid_ipv6(hostspan)) {
+ return false;
+ }
+ if (i == s.len - 1 || s.s[i + 1] != ':')
+ return false;
+ return is_valid_port((string_span_t){ s.s + i + 2, s.len - i - 2 });
+ } else {
+ ++hostspan.len;
+ }
+ }
+ return false;
+ }
+ for (size_t i = 0; i < s.len; ++i) {
+ if (s.s[i] == ':') {
+ string_span_t host = { s.s, i }, port = { s.s + i + 1, s.len - i - 1};
+ return is_valid_port(port) && (is_valid_ipv4(host) || is_valid_hostname(host));
+ }
+ }
+ return false;
+}
+
+static bool is_valid_network(string_span_t s)
+{
+ for (size_t i = 0; i < s.len; ++i) {
+ if (s.s[i] == '/') {
+ string_span_t ip = { s.s, i }, cidr = { s.s + i + 1, s.len - i - 1};
+ uint16_t cidrval = 0;
+
+ if (cidr.len > 3 || !cidr.len)
+ return false;
+
+ for (size_t j = 0; j < cidr.len; ++j) {
+ if (!is_decimal(cidr.s[j]))
+ return false;
+ cidrval = 10 * cidrval + cidr.s[j] - '0';
+ }
+ if (is_valid_ipv4(ip))
+ return cidrval <= 32;
+ else if (is_valid_ipv6(ip))
+ return cidrval <= 128;
+ return false;
+ }
+ }
+ return is_valid_ipv4(s) || is_valid_ipv6(s);
+}
+
+enum field {
+ InterfaceSection,
+ PrivateKey,
+ ListenPort,
+ Address,
+ DNS,
+ MTU,
+#ifndef MOBILE_WGQUICK_SUBSET
+ FwMark,
+ Table,
+ PreUp, PostUp, PreDown, PostDown,
+ SaveConfig,
+#endif
+
+ PeerSection,
+ PublicKey,
+ PresharedKey,
+ AllowedIPs,
+ Endpoint,
+ PersistentKeepalive,
+
+ Invalid
+};
+
+static enum field section_for_field(enum field t)
+{
+ if (t > InterfaceSection && t < PeerSection)
+ return InterfaceSection;
+ if (t > PeerSection && t < Invalid)
+ return PeerSection;
+ return Invalid;
+}
+
+static enum field get_field(string_span_t s)
+{
+#define check_enum(t) do { if (is_caseless_same(s, #t)) return t; } while (0)
+ check_enum(PrivateKey);
+ check_enum(ListenPort);
+ check_enum(Address);
+ check_enum(DNS);
+ check_enum(MTU);
+ check_enum(PublicKey);
+ check_enum(PresharedKey);
+ check_enum(AllowedIPs);
+ check_enum(Endpoint);
+ check_enum(PersistentKeepalive);
+#ifndef MOBILE_WGQUICK_SUBSET
+ check_enum(FwMark);
+ check_enum(Table);
+ check_enum(PreUp);
+ check_enum(PostUp);
+ check_enum(PreDown);
+ check_enum(PostDown);
+ check_enum(SaveConfig);
+#endif
+ return Invalid;
+#undef check_enum
+}
+
+static enum field get_sectiontype(string_span_t s)
+{
+ if (is_caseless_same(s, "[Peer]"))
+ return PeerSection;
+ if (is_caseless_same(s, "[Interface]"))
+ return InterfaceSection;
+ return Invalid;
+}
+
+struct highlight_span_array {
+ size_t len, capacity;
+ struct highlight_span *spans;
+};
+
+/* A useful OpenBSD-ism. */
+static void *realloc_array(void *optr, size_t nmemb, size_t size)
+{
+ if ((nmemb >= (size_t)1 << (sizeof(size_t) * 4) ||
+ size >= (size_t)1 << (sizeof(size_t) * 4)) &&
+ nmemb > 0 && SIZE_MAX / nmemb < size) {
+ errno = ENOMEM;
+ return NULL;
+ }
+ return realloc(optr, size * nmemb);
+}
+
+static bool append_highlight_span(struct highlight_span_array *a, const char *o, string_span_t s, enum highlight_type t)
+{
+ if (!s.len)
+ return true;
+ if (a->len >= a->capacity) {
+ struct highlight_span *resized;
+
+ a->capacity = a->capacity ? a->capacity * 2 : 64;
+ resized = realloc_array(a->spans, a->capacity, sizeof(*resized));
+ if (!resized) {
+ free(a->spans);
+ memset(a, 0, sizeof(*a));
+ return false;
+ }
+ a->spans = resized;
+ }
+ a->spans[a->len++] = (struct highlight_span){ t, s.s - o, s.len };
+ return true;
+}
+
+static void highlight_multivalue_value(struct highlight_span_array *ret, const string_span_t parent, const string_span_t s, enum field section)
+{
+ switch (section) {
+ case DNS:
+ if (is_valid_ipv4(s) || is_valid_ipv6(s))
+ append_highlight_span(ret, parent.s, s, HighlightIP);
+ else if (is_valid_hostname(s))
+ append_highlight_span(ret, parent.s, s, HighlightHost);
+ else
+ append_highlight_span(ret, parent.s, s, HighlightError);
+ break;
+ case Address:
+ case AllowedIPs: {
+ size_t slash;
+
+ if (!is_valid_network(s)) {
+ append_highlight_span(ret, parent.s, s, HighlightError);
+ break;
+ }
+ for (slash = 0; slash < s.len; ++slash) {
+ if (s.s[slash] == '/')
+ break;
+ }
+ if (slash == s.len) {
+ append_highlight_span(ret, parent.s, s, HighlightIP);
+ } else {
+ append_highlight_span(ret, parent.s, (string_span_t){ s.s, slash }, HighlightIP);
+ append_highlight_span(ret, parent.s, (string_span_t){ s.s + slash, 1 }, HighlightDelimiter);
+ append_highlight_span(ret, parent.s, (string_span_t){ s.s + slash + 1, s.len - slash - 1 }, HighlightCidr);
+ }
+ break;
+ }
+ default:
+ append_highlight_span(ret, parent.s, s, HighlightError);
+ }
+}
+
+static void highlight_multivalue(struct highlight_span_array *ret, const string_span_t parent, const string_span_t s, enum field section)
+{
+ string_span_t current_span = { s.s, 0 };
+ size_t len_at_last_space = 0;
+
+ for (size_t i = 0; i < s.len; ++i) {
+ if (s.s[i] == ',') {
+ current_span.len = len_at_last_space;
+ highlight_multivalue_value(ret, parent, current_span, section);
+ append_highlight_span(ret, parent.s, (string_span_t){ s.s + i, 1 }, HighlightDelimiter);
+ len_at_last_space = 0;
+ current_span = (string_span_t){ s.s + i + 1, 0 };
+ } else if (s.s[i] == ' ' || s.s[i] == '\t') {
+ if (&s.s[i] == current_span.s && !current_span.len)
+ ++current_span.s;
+ else
+ ++current_span.len;
+ } else {
+ len_at_last_space = ++current_span.len;
+ }
+ }
+ current_span.len = len_at_last_space;
+ if (current_span.len)
+ highlight_multivalue_value(ret, parent, current_span, section);
+ else if (ret->spans[ret->len - 1].type == HighlightDelimiter)
+ ret->spans[ret->len - 1].type = HighlightError;
+}
+
+static void highlight_value(struct highlight_span_array *ret, const string_span_t parent, const string_span_t s, enum field section)
+{
+ switch (section) {
+ case PrivateKey:
+ append_highlight_span(ret, parent.s, s, is_valid_key(s) ? HighlightPrivateKey : HighlightError);
+ break;
+ case PublicKey:
+ append_highlight_span(ret, parent.s, s, is_valid_key(s) ? HighlightPublicKey : HighlightError);
+ break;
+ case PresharedKey:
+ append_highlight_span(ret, parent.s, s, is_valid_key(s) ? HighlightPresharedKey : HighlightError);
+ break;
+ case MTU:
+ append_highlight_span(ret, parent.s, s, is_valid_mtu(s) ? HighlightMTU : HighlightError);
+ break;
+#ifndef MOBILE_WGQUICK_SUBSET
+ case SaveConfig:
+ append_highlight_span(ret, parent.s, s, is_valid_saveconfig(s) ? HighlightSaveConfig : HighlightError);
+ break;
+ case FwMark:
+ append_highlight_span(ret, parent.s, s, is_valid_fwmark(s) ? HighlightFwMark : HighlightError);
+ break;
+ case Table:
+ append_highlight_span(ret, parent.s, s, is_valid_table(s) ? HighlightTable : HighlightError);
+ break;
+ case PreUp:
+ case PostUp:
+ case PreDown:
+ case PostDown:
+ append_highlight_span(ret, parent.s, s, is_valid_prepostupdown(s) ? HighlightCmd : HighlightError);
+ break;
+#endif
+ case ListenPort:
+ append_highlight_span(ret, parent.s, s, is_valid_port(s) ? HighlightPort : HighlightError);
+ break;
+ case PersistentKeepalive:
+ append_highlight_span(ret, parent.s, s, is_valid_persistentkeepalive(s) ? HighlightKeepalive : HighlightError);
+ break;
+ case Endpoint: {
+ size_t colon;
+
+ if (!is_valid_endpoint(s)) {
+ append_highlight_span(ret, parent.s, s, HighlightError);
+ break;
+ }
+ for (colon = s.len; colon --> 0;) {
+ if (s.s[colon] == ':')
+ break;
+ }
+ append_highlight_span(ret, parent.s, (string_span_t){ s.s, colon }, HighlightHost);
+ append_highlight_span(ret, parent.s, (string_span_t){ s.s + colon, 1 }, HighlightDelimiter);
+ append_highlight_span(ret, parent.s, (string_span_t){ s.s + colon + 1, s.len - colon - 1 }, HighlightPort);
+ break;
+ }
+ case Address:
+ case DNS:
+ case AllowedIPs:
+ highlight_multivalue(ret, parent, s, section);
+ break;
+ default:
+ append_highlight_span(ret, parent.s, s, HighlightError);
+ }
+}
+
+struct highlight_span *highlight_config(const char *config)
+{
+ struct highlight_span_array ret = { 0 };
+ const string_span_t s = { config, strlen(config) };
+ string_span_t current_span = { s.s, 0 };
+ enum field current_section = Invalid, current_field = Invalid;
+ enum { OnNone, OnKey, OnValue, OnComment, OnSection } state = OnNone;
+ size_t len_at_last_space = 0, equals_location = 0;
+
+ for (size_t i = 0; i <= s.len; ++i) {
+ if (i == s.len || s.s[i] == '\n' || (state != OnComment && s.s[i] == '#')) {
+ if (state == OnKey) {
+ current_span.len = len_at_last_space;
+ append_highlight_span(&ret, s.s, current_span, HighlightError);
+ } else if (state == OnValue) {
+ if (current_span.len) {
+ append_highlight_span(&ret, s.s, (string_span_t){ s.s + equals_location, 1 }, HighlightDelimiter);
+ current_span.len = len_at_last_space;
+ highlight_value(&ret, s, current_span, current_field);
+ } else {
+ append_highlight_span(&ret, s.s, (string_span_t){ s.s + equals_location, 1 }, HighlightError);
+ }
+ } else if (state == OnSection) {
+ current_span.len = len_at_last_space;
+ current_section = get_sectiontype(current_span);
+ append_highlight_span(&ret, s.s, current_span, current_section == Invalid ? HighlightError : HighlightSection);
+ } else if (state == OnComment) {
+ append_highlight_span(&ret, s.s, current_span, HighlightComment);
+ }
+ if (i == s.len)
+ break;
+ len_at_last_space = 0;
+ current_field = Invalid;
+ if (s.s[i] == '#') {
+ current_span = (string_span_t){ s.s + i, 1 };
+ state = OnComment;
+ } else {
+ current_span = (string_span_t){ s.s + i + 1, 0 };
+ state = OnNone;
+ }
+ } else if (state == OnComment) {
+ ++current_span.len;
+ } else if (s.s[i] == ' ' || s.s[i] == '\t') {
+ if (&s.s[i] == current_span.s && !current_span.len)
+ ++current_span.s;
+ else
+ ++current_span.len;
+ } else if (s.s[i] == '=' && state == OnKey) {
+ current_span.len = len_at_last_space;
+ current_field = get_field(current_span);
+ enum field section = section_for_field(current_field);
+ if (section == Invalid || current_field == Invalid || section != current_section)
+ append_highlight_span(&ret, s.s, current_span, HighlightError);
+ else
+ append_highlight_span(&ret, s.s, current_span, HighlightField);
+ equals_location = i;
+ current_span = (string_span_t){ s.s + i + 1, 0 };
+ state = OnValue;
+ } else {
+ if (state == OnNone)
+ state = s.s[i] == '[' ? OnSection : OnKey;
+ len_at_last_space = ++current_span.len;
+ }
+ }
+
+ append_highlight_span(&ret, s.s, (string_span_t){ s.s, -1 }, HighlightEnd);
+ return ret.spans;
+}
diff --git a/contrib/wireguard-tools/contrib/json/README b/contrib/wireguard-tools/contrib/json/README
new file mode 100644
--- /dev/null
+++ b/contrib/wireguard-tools/contrib/json/README
@@ -0,0 +1,8 @@
+wg-json
+=======
+
+This will dump all current WireGuard status as JSON output.
+
+Usage:
+
+ # wg-json
diff --git a/contrib/wireguard-tools/contrib/json/wg-json b/contrib/wireguard-tools/contrib/json/wg-json
new file mode 100755
--- /dev/null
+++ b/contrib/wireguard-tools/contrib/json/wg-json
@@ -0,0 +1,52 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+#
+# Copyright (C) 2015-2020 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
+
+exec < <(exec wg show all dump)
+
+printf '{'
+while read -r -d $'\t' device; do
+ if [[ $device != "$last_device" ]]; then
+ [[ -z $last_device ]] && printf '\n' || printf '%s,\n' "$end"
+ last_device="$device"
+ read -r private_key public_key listen_port fwmark
+ printf '\t"%s": {' "$device"
+ delim=$'\n'
+ [[ $private_key == "(none)" ]] || { printf '%s\t\t"privateKey": "%s"' "$delim" "$private_key"; delim=$',\n'; }
+ [[ $public_key == "(none)" ]] || { printf '%s\t\t"publicKey": "%s"' "$delim" "$public_key"; delim=$',\n'; }
+ [[ $listen_port == "0" ]] || { printf '%s\t\t"listenPort": %u' "$delim" $(( $listen_port )); delim=$',\n'; }
+ [[ $fwmark == "off" ]] || { printf '%s\t\t"fwmark": %u' "$delim" $(( $fwmark )); delim=$',\n'; }
+ printf '%s\t\t"peers": {' "$delim"; end=$'\n\t\t}\n\t}'
+ delim=$'\n'
+ else
+ read -r public_key preshared_key endpoint allowed_ips latest_handshake transfer_rx transfer_tx persistent_keepalive
+ printf '%s\t\t\t"%s": {' "$delim" "$public_key"
+ delim=$'\n'
+ [[ $preshared_key == "(none)" ]] || { printf '%s\t\t\t\t"presharedKey": "%s"' "$delim" "$preshared_key"; delim=$',\n'; }
+ [[ $endpoint == "(none)" ]] || { printf '%s\t\t\t\t"endpoint": "%s"' "$delim" "$endpoint"; delim=$',\n'; }
+ [[ $latest_handshake == "0" ]] || { printf '%s\t\t\t\t"latestHandshake": %u' "$delim" $(( $latest_handshake )); delim=$',\n'; }
+ [[ $transfer_rx == "0" ]] || { printf '%s\t\t\t\t"transferRx": %u' "$delim" $(( $transfer_rx )); delim=$',\n'; }
+ [[ $transfer_tx == "0" ]] || { printf '%s\t\t\t\t"transferTx": %u' "$delim" $(( $transfer_tx )); delim=$',\n'; }
+ [[ $persistent_keepalive == "off" ]] || { printf '%s\t\t\t\t"persistentKeepalive": %u' "$delim" $(( $persistent_keepalive )); delim=$',\n'; }
+ printf '%s\t\t\t\t"allowedIps": [' "$delim"
+ delim=$'\n'
+ if [[ $allowed_ips != "(none)" ]]; then
+ old_ifs="$IFS"
+ IFS=,
+ for ip in $allowed_ips; do
+ printf '%s\t\t\t\t\t"%s"' "$delim" "$ip"
+ delim=$',\n'
+ done
+ IFS="$old_ifs"
+ delim=$'\n'
+ fi
+ printf '%s\t\t\t\t]' "$delim"
+ printf '\n\t\t\t}'
+ delim=$',\n'
+ fi
+
+
+done
+printf '%s\n' "$end"
+printf '}\n'
diff --git a/contrib/wireguard-tools/contrib/keygen-html/.gitignore b/contrib/wireguard-tools/contrib/keygen-html/.gitignore
new file mode 100644
--- /dev/null
+++ b/contrib/wireguard-tools/contrib/keygen-html/.gitignore
@@ -0,0 +1 @@
+curve25519_generate.js
diff --git a/contrib/wireguard-tools/contrib/keygen-html/README b/contrib/wireguard-tools/contrib/keygen-html/README
new file mode 100644
--- /dev/null
+++ b/contrib/wireguard-tools/contrib/keygen-html/README
@@ -0,0 +1,19 @@
+WireGuard Key Generation in JavaScript
+======================================
+
+Various people believe in JavaScript crypto, unfortunately. This small
+example helps them fuel their poor taste.
+
+It's possible to generate WireGuard keys (and thus configurations) in the
+browser. The webpage here simulates talking to a server to exchange keys
+and then generates a configuration file for the user to download.
+
+Bugs
+----
+
+Who knows how emscripten actually compiles this and whether or not it
+introduces interesting side-channel attacks.
+
+Secrets aren't zerored after use. Maybe you can get around this with
+some tricks taking advantage of browser allocator behavior and different
+processes, but it seems pretty hard.
diff --git a/contrib/wireguard-tools/contrib/keygen-html/keygen.html b/contrib/wireguard-tools/contrib/keygen-html/keygen.html
new file mode 100644
--- /dev/null
+++ b/contrib/wireguard-tools/contrib/keygen-html/keygen.html
@@ -0,0 +1,178 @@
+<script src="wireguard.js"></script>
+<script>
+/* SPDX-License-Identifier: GPL-2.0
+ *
+ * Copyright (C) 2015-2020 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
+ */
+
+function sendPubkeyToServer(pubkey, username, password)
+{
+ var node = document.createElement("li");
+ node.innerText = "Sending " + username + ":" + password + " to server for new pubkey " + pubkey + "...";
+ document.body.appendChild(node);
+
+ // send info to server
+
+ var serverResponse = {
+ publicKey: "6spHEFoJrp9pijbxjJoS6fHjZaAWQqtdFFO/OtpVe3w=",
+ allowedIPs: [ "0.0.0.0/0", "::/0" ],
+ endpoint: "demo.wireguard.com:63321",
+ address: [ "192.168.18.42/32", "fd08:1234:1111::77/128" ],
+ dns: [ "8.8.8.8", "8.8.4.4" ]
+ }
+
+ return serverResponse;
+}
+
+function downloadNewConfiguration()
+{
+ var keypair = wireguard.generateKeypair();
+ var serverResponse = sendPubkeyToServer(keypair.publicKey, "zx2c4", "supersecretpassword");
+
+ var config = [];
+ config.push("[Interface]");
+ config.push("PrivateKey = " + keypair.privateKey);
+ config.push("Address = " + serverResponse.address.join(", "));
+ config.push("DNS = " + serverResponse.dns.join(", "));
+ config.push("");
+ config.push("[Peer]");
+ config.push("PublicKey = " + serverResponse.publicKey);
+ config.push("AllowedIPs = " + serverResponse.allowedIPs.join(", "));
+ config.push("Endpoint = " + serverResponse.endpoint);
+ config.push("");
+ return config.join("\n");
+}
+
+function giveOneConfigurationToUser()
+{
+ var config = downloadNewConfiguration();
+ var blob = new Blob([config], { type: "text/plain" });
+ var a = document.createElement("a");
+ a.download = "demo0.conf";
+ a.href = URL.createObjectURL(blob);
+ a.style.display = "none";
+ document.body.appendChild(a);
+ a.click();
+ document.body.removeChild(a);
+}
+
+function putU32(b, n)
+{
+ b.push(n & 0xff, (n >>> 8) & 0xff, (n >>> 16) & 0xff, (n >>> 24) & 0xff);
+}
+
+function putU16(b, n)
+{
+ b.push(n & 0xff, (n >>> 8) & 0xff);
+}
+
+function putBytes(b, a)
+{
+ for (var i = 0; i < a.length; ++i)
+ b.push(a[i] & 0xff);
+}
+
+function encodeString(s)
+{
+ var utf8 = unescape(encodeURIComponent(s));
+ var b = new Uint8Array(utf8.length);
+ for (var i = 0; i < utf8.length; ++i)
+ b[i] = utf8.charCodeAt(i);
+ return b;
+}
+
+function crc32(b)
+{
+ if (!crc32.table) {
+ crc32.table = [];
+ for (var c = 0, n = 0; n < 256; c = ++n) {
+ for (var k = 0; k < 8; ++k)
+ c = ((c & 1) ? (0xedb88320 ^ (c >>> 1)) : (c >>> 1));
+ crc32.table[n] = c;
+ }
+ }
+ var crc = -1;
+ for (var i = 0; i < b.length; ++i)
+ crc = (crc >>> 8) ^ crc32.table[(crc ^ b[i]) & 0xff];
+ return (crc ^ (-1)) >>> 0;
+}
+
+function createZipFile(files)
+{
+ var b = [];
+ var cd = [];
+ var offset = 0;
+
+ for (var i = 0; i < files.length; ++i) {
+ var name = encodeString(files[i].name);
+ var contents = encodeString(files[i].contents);
+ var crc = crc32(contents);
+
+ putU32(b, 0x04034b50); /* signature */
+ putU16(b, 20); /* version needed */
+ putU16(b, 0); /* flags */
+ putU16(b, 0); /* compression method */
+ putU16(b, 0); /* mtime */
+ putU16(b, 0); /* mdate */
+ putU32(b, crc); /* crc32 */
+ putU32(b, contents.length); /* compressed size */
+ putU32(b, contents.length); /* uncompressed size */
+ putU16(b, name.length); /* file name length */
+ putU16(b, 0); /* extra field length */
+ putBytes(b, name);
+ putBytes(b, contents);
+
+ putU32(cd, 0x02014b50); /* signature */
+ putU16(cd, 0); /* version made */
+ putU16(cd, 20); /* version needed */
+ putU16(cd, 0); /* flags */
+ putU16(cd, 0); /* compression method */
+ putU16(cd, 0); /* mtime */
+ putU16(cd, 0); /* mdate */
+ putU32(cd, crc); /* crc32 */
+ putU32(cd, contents.length); /* compressed size */
+ putU32(cd, contents.length); /* uncompressed size */
+ putU16(cd, name.length); /* file name length */
+ putU16(cd, 0); /* extra field length */
+ putU16(cd, 0); /* file comment length */
+ putU16(cd, 0); /* disk number start */
+ putU16(cd, 0); /* internal file attributes */
+ putU32(cd, 32); /* external file attributes - 'archive' bit set (32) */
+ putU32(cd, offset); /* relative offset of local header */
+ putBytes(cd, name); /* file name */
+
+ offset += 30 + contents.length + name.length
+ }
+ putBytes(b, cd); /* central directory */
+ putU32(b, 0x06054b50); /* end of central directory signature */
+ putU16(b, 0); /* number of this disk */
+ putU16(b, 0); /* number of disk with central directory start */
+ putU16(b, files.length); /* number of entries on disk */
+ putU16(b, files.length); /* number of entries */
+ putU32(b, cd.length); /* length of central directory */
+ putU32(b, offset); /* offset to start of central directory */
+ putU16(b, 0); /* zip comment size */
+ return Uint8Array.from(b);
+}
+
+function giveMultipleConfigurationsToUser()
+{
+ var zipFile = createZipFile([
+ { name: "demo0.conf", contents: downloadNewConfiguration() },
+ { name: "demo1.conf", contents: downloadNewConfiguration() },
+ { name: "demo2.conf", contents: downloadNewConfiguration() }
+ ]);
+ var blob = new Blob([zipFile], { type: "application/zip" });
+ var a = document.createElement("a");
+ a.download = "demo-configs.zip";
+ a.href = URL.createObjectURL(blob);
+ a.style.display = "none";
+ document.body.appendChild(a);
+ a.click();
+ document.body.removeChild(a);
+}
+
+</script>
+
+<p><a href="javascript:giveOneConfigurationToUser()">Download a WireGuard configuration file</a></p>
+<p><a href="javascript:giveMultipleConfigurationsToUser()">Download several WireGuard configuration files</a></p>
diff --git a/contrib/wireguard-tools/contrib/keygen-html/wireguard.js b/contrib/wireguard-tools/contrib/keygen-html/wireguard.js
new file mode 100644
--- /dev/null
+++ b/contrib/wireguard-tools/contrib/keygen-html/wireguard.js
@@ -0,0 +1,184 @@
+/*! SPDX-License-Identifier: GPL-2.0
+ *
+ * Copyright (C) 2015-2020 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
+ */
+
+(function() {
+ function gf(init) {
+ var r = new Float64Array(16);
+ if (init) {
+ for (var i = 0; i < init.length; ++i)
+ r[i] = init[i];
+ }
+ return r;
+ }
+
+ function pack(o, n) {
+ var b, m = gf(), t = gf();
+ for (var i = 0; i < 16; ++i)
+ t[i] = n[i];
+ carry(t);
+ carry(t);
+ carry(t);
+ for (var j = 0; j < 2; ++j) {
+ m[0] = t[0] - 0xffed;
+ for (var i = 1; i < 15; ++i) {
+ m[i] = t[i] - 0xffff - ((m[i - 1] >> 16) & 1);
+ m[i - 1] &= 0xffff;
+ }
+ m[15] = t[15] - 0x7fff - ((m[14] >> 16) & 1);
+ b = (m[15] >> 16) & 1;
+ m[14] &= 0xffff;
+ cswap(t, m, 1 - b);
+ }
+ for (var i = 0; i < 16; ++i) {
+ o[2 * i] = t[i] & 0xff;
+ o[2 * i + 1] = t[i] >> 8;
+ }
+ }
+
+ function carry(o) {
+ var c;
+ for (var i = 0; i < 16; ++i) {
+ o[(i + 1) % 16] += (i < 15 ? 1 : 38) * Math.floor(o[i] / 65536);
+ o[i] &= 0xffff;
+ }
+ }
+
+ function cswap(p, q, b) {
+ var t, c = ~(b - 1);
+ for (var i = 0; i < 16; ++i) {
+ t = c & (p[i] ^ q[i]);
+ p[i] ^= t;
+ q[i] ^= t;
+ }
+ }
+
+ function add(o, a, b) {
+ for (var i = 0; i < 16; ++i)
+ o[i] = (a[i] + b[i]) | 0;
+ }
+
+ function subtract(o, a, b) {
+ for (var i = 0; i < 16; ++i)
+ o[i] = (a[i] - b[i]) | 0;
+ }
+
+ function multmod(o, a, b) {
+ var t = new Float64Array(31);
+ for (var i = 0; i < 16; ++i) {
+ for (var j = 0; j < 16; ++j)
+ t[i + j] += a[i] * b[j];
+ }
+ for (var i = 0; i < 15; ++i)
+ t[i] += 38 * t[i + 16];
+ for (var i = 0; i < 16; ++i)
+ o[i] = t[i];
+ carry(o);
+ carry(o);
+ }
+
+ function invert(o, i) {
+ var c = gf();
+ for (var a = 0; a < 16; ++a)
+ c[a] = i[a];
+ for (var a = 253; a >= 0; --a) {
+ multmod(c, c, c);
+ if (a !== 2 && a !== 4)
+ multmod(c, c, i);
+ }
+ for (var a = 0; a < 16; ++a)
+ o[a] = c[a];
+ }
+
+ function clamp(z) {
+ z[31] = (z[31] & 127) | 64;
+ z[0] &= 248;
+ }
+
+ function generatePublicKey(privateKey) {
+ var r, z = new Uint8Array(32);
+ var a = gf([1]),
+ b = gf([9]),
+ c = gf(),
+ d = gf([1]),
+ e = gf(),
+ f = gf(),
+ _121665 = gf([0xdb41, 1]),
+ _9 = gf([9]);
+ for (var i = 0; i < 32; ++i)
+ z[i] = privateKey[i];
+ clamp(z);
+ for (var i = 254; i >= 0; --i) {
+ r = (z[i >>> 3] >>> (i & 7)) & 1;
+ cswap(a, b, r);
+ cswap(c, d, r);
+ add(e, a, c);
+ subtract(a, a, c);
+ add(c, b, d);
+ subtract(b, b, d);
+ multmod(d, e, e);
+ multmod(f, a, a);
+ multmod(a, c, a);
+ multmod(c, b, e);
+ add(e, a, c);
+ subtract(a, a, c);
+ multmod(b, a, a);
+ subtract(c, d, f);
+ multmod(a, c, _121665);
+ add(a, a, d);
+ multmod(c, c, a);
+ multmod(a, d, f);
+ multmod(d, b, _9);
+ multmod(b, e, e);
+ cswap(a, b, r);
+ cswap(c, d, r);
+ }
+ invert(c, c);
+ multmod(a, a, c);
+ pack(z, a);
+ return z;
+ }
+
+ function generatePresharedKey() {
+ var privateKey = new Uint8Array(32);
+ window.crypto.getRandomValues(privateKey);
+ return privateKey;
+ }
+
+ function generatePrivateKey() {
+ var privateKey = generatePresharedKey();
+ clamp(privateKey);
+ return privateKey;
+ }
+
+ function encodeBase64(dest, src) {
+ var input = Uint8Array.from([(src[0] >> 2) & 63, ((src[0] << 4) | (src[1] >> 4)) & 63, ((src[1] << 2) | (src[2] >> 6)) & 63, src[2] & 63]);
+ for (var i = 0; i < 4; ++i)
+ dest[i] = input[i] + 65 +
+ (((25 - input[i]) >> 8) & 6) -
+ (((51 - input[i]) >> 8) & 75) -
+ (((61 - input[i]) >> 8) & 15) +
+ (((62 - input[i]) >> 8) & 3);
+ }
+
+ function keyToBase64(key) {
+ var i, base64 = new Uint8Array(44);
+ for (i = 0; i < 32 / 3; ++i)
+ encodeBase64(base64.subarray(i * 4), key.subarray(i * 3));
+ encodeBase64(base64.subarray(i * 4), Uint8Array.from([key[i * 3 + 0], key[i * 3 + 1], 0]));
+ base64[43] = 61;
+ return String.fromCharCode.apply(null, base64);
+ }
+
+ window.wireguard = {
+ generateKeypair: function() {
+ var privateKey = generatePrivateKey();
+ var publicKey = generatePublicKey(privateKey);
+ return {
+ publicKey: keyToBase64(publicKey),
+ privateKey: keyToBase64(privateKey)
+ };
+ }
+ };
+})();
diff --git a/contrib/wireguard-tools/contrib/launchd/README b/contrib/wireguard-tools/contrib/launchd/README
new file mode 100644
--- /dev/null
+++ b/contrib/wireguard-tools/contrib/launchd/README
@@ -0,0 +1,12 @@
+WireGuard for Launchd
+=====================
+
+The example `com.wireguard.wg0.plist` file may be used for running wg-quick(8)
+as a launchd service. Note that the `PATH` variable is modified to point to
+the PATH used by Homebrew or Macports, so that it uses the non-system bash(1).
+
+Usage
+-----
+
+$ sudo cp com.wireguard.wg0.plist /Library/LaunchDaemons
+$ sudo launchctl load /Library/LaunchDaemons/com.wireguard.wg0.plist
diff --git a/contrib/wireguard-tools/contrib/launchd/com.wireguard.wg0.plist b/contrib/wireguard-tools/contrib/launchd/com.wireguard.wg0.plist
new file mode 100644
--- /dev/null
+++ b/contrib/wireguard-tools/contrib/launchd/com.wireguard.wg0.plist
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>Label</key>
+ <string>com.wireguard.wg0</string>
+ <key>ProgramArguments</key>
+ <array>
+ <string>/usr/local/bin/wg-quick</string>
+ <string>up</string>
+ <string>/usr/local/etc/wireguard/wg0.conf</string>
+ </array>
+ <key>OnDemand</key>
+ <false/>
+ <key>RunAtLoad</key>
+ <true/>
+ <key>TimeOut</key>
+ <integer>90</integer>
+ <key>EnvironmentVariables</key>
+ <dict>
+ <key>PATH</key>
+ <string>/usr/local/sbin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin</string>
+ </dict>
+</dict>
+</plist>
diff --git a/contrib/wireguard-tools/contrib/nat-hole-punching/README b/contrib/wireguard-tools/contrib/nat-hole-punching/README
new file mode 100644
--- /dev/null
+++ b/contrib/wireguard-tools/contrib/nat-hole-punching/README
@@ -0,0 +1,41 @@
+== NAT Hole Punching Example ==
+
+This code should never be used, ever. But, it's a nice demonstration of how
+to punch holes and have two NAT'd peers talk to each other.
+
+Compile with:
+ $ gcc nat-punch-client.c -o client -lresolv
+ $ gcc nat-punch-server.c -o server
+
+
+Server is 1.2.3.4 and is on the public internet accepting UDP:49918.
+Client A is NAT'd and doesn't know its IP address.
+Client B is NAT'd and doesn't know its IP address.
+
+
+Server runs:
+ $ ./server
+
+Client A runs:
+ # ip link add wg0 type wireguard
+ # ip addr add 10.200.200.1 peer 10.200.200.2 dev wg0
+ # wg set wg0 private-key ... peer ... allowed-ips 10.200.200.2/32
+ # ./client 1.2.3.4 wg0
+ # ping 10.200.200.2
+
+Client B runs:
+ # ip link add wg0 type wireguard
+ # ip addr add 10.200.200.2 peer 10.200.200.1 dev wg0
+ # wg set wg0 private-key ... peer ... allowed-ips 10.200.200.1/32
+ # ./client 1.2.3.4 wg0
+ # ping 10.200.200.1
+
+And voila! Client A and Client B can speak from behind NAT.
+
+
+
+-----
+Keep in mind that this is proof-of-concept example code. It is not code that
+should be used in production, ever. It is woefully insecure, and is unsuitable
+for any real usage. With that said, this is useful as a learning example of
+how NAT hole punching might work within a more developed solution.
diff --git a/contrib/wireguard-tools/contrib/nat-hole-punching/nat-punch-client.c b/contrib/wireguard-tools/contrib/nat-hole-punching/nat-punch-client.c
new file mode 100644
--- /dev/null
+++ b/contrib/wireguard-tools/contrib/nat-hole-punching/nat-punch-client.c
@@ -0,0 +1,208 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2015-2020 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
+ *
+ * Example only. Do not run in production.
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netinet/udp.h>
+#include <netinet/ip.h>
+#include <linux/filter.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <stdarg.h>
+#include <string.h>
+#include <resolv.h>
+#include <stdint.h>
+#include <stdbool.h>
+
+enum { MAX_PEERS = 65536, PORT = 49918 };
+
+static struct {
+ uint8_t base64_key[45];
+ bool have_seen;
+} peers[MAX_PEERS];
+static unsigned int total_peers;
+
+static const char *cmd(const char *line, ...)
+{
+ static char buf[2048];
+ char full_cmd[2048] = { 0 };
+ size_t len;
+ FILE *f;
+ va_list args;
+ va_start(args, line);
+ vsnprintf(full_cmd, 2047, line, args);
+ va_end(args);
+ f = popen(full_cmd, "r");
+ if (!f) {
+ perror("popen");
+ exit(errno);
+ }
+ if (!fgets(buf, 2048, f)) {
+ pclose(f);
+ return NULL;
+ }
+ pclose(f);
+ len = strlen(buf);
+ if (!len)
+ return NULL;
+ if (buf[len - 1] == '\n')
+ buf[len - 1] = '\0';
+ return buf;
+}
+
+static void read_peers(const char *interface)
+{
+ char full_cmd[2048] = { 0 };
+ size_t len;
+ FILE *f;
+ snprintf(full_cmd, 2047, "wg show %s peers", interface);
+ f = popen(full_cmd, "r");
+ if (!f) {
+ perror("popen");
+ exit(errno);
+ }
+ for (;;) {
+ if (!fgets(peers[total_peers].base64_key, 45, f))
+ break;
+ len = strlen(peers[total_peers].base64_key);
+ if (len != 44 && len != 45)
+ continue;
+ if (peers[total_peers].base64_key[len - 1] == '\n')
+ peers[total_peers].base64_key[len - 1] = '\0';
+ ++total_peers;
+ }
+ pclose(f);
+}
+
+static void unbase64(uint8_t dstkey[32], const char *srckey)
+{
+ uint8_t buf[33];
+ if (b64_pton(srckey, buf, 33) != 32) {
+ fprintf(stderr, "Could not parse base64 key: %s\n", srckey);
+ exit(EINVAL);
+ }
+ memcpy(dstkey, buf, 32);
+}
+
+static void apply_bpf(int sock, uint16_t port, uint32_t ip)
+{
+ struct sock_filter filter[] = {
+ BPF_STMT(BPF_LD + BPF_W + BPF_ABS, 12 /* src ip */),
+ BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ip, 0, 5),
+ BPF_STMT(BPF_LD + BPF_H + BPF_ABS, 20 /* src port */),
+ BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, PORT, 0, 3),
+ BPF_STMT(BPF_LD + BPF_H + BPF_ABS, 22 /* dst port */),
+ BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, port, 0, 1),
+ BPF_STMT(BPF_RET + BPF_K, -1),
+ BPF_STMT(BPF_RET + BPF_K, 0)
+ };
+ struct sock_fprog filter_prog = {
+ .len = sizeof(filter) / sizeof(filter[0]),
+ .filter = filter
+ };
+ if (setsockopt(sock, SOL_SOCKET, SO_ATTACH_FILTER, &filter_prog, sizeof(filter_prog)) < 0) {
+ perror("setsockopt(bpf)");
+ exit(errno);
+ }
+}
+
+int main(int argc, char *argv[])
+{
+ struct sockaddr_in addr = {
+ .sin_family = AF_INET
+ };
+ struct {
+ struct udphdr udp;
+ uint8_t my_pubkey[32];
+ uint8_t their_pubkey[32];
+ } __attribute__((packed)) packet = {
+ .udp = {
+ .len = htons(sizeof(packet)),
+ .dest = htons(PORT)
+ }
+ };
+ struct {
+ struct iphdr iphdr;
+ struct udphdr udp;
+ uint32_t ip;
+ uint16_t port;
+ } __attribute__((packed)) reply;
+ ssize_t len;
+ int sock, i;
+ bool repeat;
+ struct hostent *ent;
+ const char *server = argv[1], *interface = argv[2];
+
+ if (argc < 3) {
+ fprintf(stderr, "Usage: %s SERVER WIREGUARD_INTERFACE\nExample:\n %s demo.wireguard.com wg0\n", argv[0], argv[0]);
+ return EINVAL;
+ }
+
+ if (getuid() != 0) {
+ fprintf(stderr, "Must be root!\n");
+ return EPERM;
+ }
+
+ ent = gethostbyname2(server, AF_INET);
+ if (!ent) {
+ herror("gethostbyname2");
+ return h_errno;
+ }
+ addr.sin_addr = *(struct in_addr *)ent->h_addr;
+ read_peers(interface);
+ cmd("ip link set %s up", interface);
+ unbase64(packet.my_pubkey, cmd("wg show %s public-key", interface));
+ packet.udp.source = htons(atoi(cmd("wg show %s listen-port", interface)));
+
+ /* We use raw sockets so that the WireGuard interface can actually own the real socket. */
+ sock = socket(AF_INET, SOCK_RAW, IPPROTO_UDP);
+ if (sock < 0) {
+ perror("socket");
+ return errno;
+ }
+ apply_bpf(sock, ntohs(packet.udp.source), ntohl(addr.sin_addr.s_addr));
+
+check_again:
+ repeat = false;
+ for (i = 0; i < total_peers; ++i) {
+ if (peers[i].have_seen)
+ continue;
+ printf("[+] Requesting IP and port of %s: ", peers[i].base64_key);
+ unbase64(packet.their_pubkey, peers[i].base64_key);
+ if (sendto(sock, &packet, sizeof(packet), 0, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
+ putchar('\n');
+ perror("sendto");
+ return errno;
+ }
+ len = recv(sock, &reply, sizeof(reply), 0);
+ if (len < 0) {
+ putchar('\n');
+ perror("recv");
+ return errno;
+ }
+ if (len != sizeof(reply)) {
+ printf("server does not yet have it\n");
+ repeat = true;
+ } else {
+ printf("%s:%d\n", inet_ntoa(*(struct in_addr *)&reply.ip), ntohs(reply.port));
+ peers[i].have_seen = true;
+ cmd("wg set %s peer %s persistent-keepalive 25 endpoint %s:%d", interface, peers[i].base64_key, inet_ntoa(*(struct in_addr *)&reply.ip), ntohs(reply.port));
+ }
+ }
+ if (repeat) {
+ sleep(2);
+ goto check_again;
+ }
+
+ close(sock);
+ return 0;
+}
diff --git a/contrib/wireguard-tools/contrib/nat-hole-punching/nat-punch-server.c b/contrib/wireguard-tools/contrib/nat-hole-punching/nat-punch-server.c
new file mode 100644
--- /dev/null
+++ b/contrib/wireguard-tools/contrib/nat-hole-punching/nat-punch-server.c
@@ -0,0 +1,115 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2015-2020 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
+ *
+ * Example only. Do not run in production.
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+struct entry {
+ uint8_t pubkey[32];
+ uint32_t ip;
+ uint16_t port;
+};
+
+enum { MAX_ENTRIES = 65536, PORT = 49918 };
+
+static struct entry entries[MAX_ENTRIES];
+static unsigned int next_entry;
+
+/* XX: this should use a hash table */
+static struct entry *find_entry(uint8_t key[32])
+{
+ int i;
+ for (i = 0; i < MAX_ENTRIES; ++i) {
+ if (!memcmp(entries[i].pubkey, key, 32))
+ return &entries[i];
+ }
+ return NULL;
+}
+
+/* XX: this is obviously vulnerable to DoS */
+static struct entry *find_or_insert_entry(uint8_t key[32])
+{
+ struct entry *entry = find_entry(key);
+ if (!entry) {
+ entry = &entries[next_entry++ % MAX_ENTRIES];
+ memcpy(entry->pubkey, key, 32);
+ }
+ return entry;
+}
+
+int main(int argc, char *argv[])
+{
+ struct sockaddr_in addr = {
+ .sin_family = AF_INET,
+ .sin_addr = { .s_addr = htonl(INADDR_ANY) },
+ .sin_port = htons(PORT)
+ };
+ struct {
+ uint8_t my_pubkey[32];
+ uint8_t their_pubkey[32];
+ } __attribute__((packed)) packet;
+ struct {
+ uint32_t ip;
+ uint16_t port;
+ } __attribute__((packed)) reply;
+ struct entry *entry;
+ socklen_t len;
+ ssize_t retlen;
+ int optval;
+ int sock = socket(AF_INET, SOCK_DGRAM, 0);
+ if (sock < 0) {
+ perror("socket");
+ return errno;
+ }
+
+ optval = 1;
+ if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval)) < 0) {
+ perror("setsockopt");
+ return errno;
+ }
+
+ if (bind(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
+ perror("bind");
+ return errno;
+ }
+
+ for (;;) {
+ len = sizeof(addr);
+ if (recvfrom(sock, &packet, sizeof(packet), 0, (struct sockaddr *)&addr, &len) != sizeof(packet)) {
+ perror("recvfrom");
+ continue;
+ }
+ entry = find_or_insert_entry(packet.my_pubkey);
+ entry->ip = addr.sin_addr.s_addr;
+ entry->port = addr.sin_port;
+ entry = find_entry(packet.their_pubkey);
+ if (entry) {
+ reply.ip = entry->ip;
+ reply.port = entry->port;
+ if (sendto(sock, &reply, sizeof(reply), 0, (struct sockaddr *)&addr, len) < 0) {
+ perror("sendto");
+ continue;
+ }
+ } else {
+ if (sendto(sock, NULL, 0, 0, (struct sockaddr *)&addr, len) < 0) {
+ perror("sendto");
+ continue;
+ }
+ }
+ }
+
+ close(sock);
+ return 0;
+}
diff --git a/contrib/wireguard-tools/contrib/ncat-client-server/README b/contrib/wireguard-tools/contrib/ncat-client-server/README
new file mode 100644
--- /dev/null
+++ b/contrib/wireguard-tools/contrib/ncat-client-server/README
@@ -0,0 +1,16 @@
+ === IMPORTANT NOTE ===
+
+Do not use these scripts in production. They are simply a
+demonstration of how easy the `wg(8)` tool is at the command
+line, but by no means should you actually attempt to use
+these. They are horribly insecure and defeat the purpose
+of WireGuard.
+ STAY AWAY!
+
+That all said, this is a pretty cool example of just how
+darn easy WireGuard can be.
+
+Disclaimer:
+ The `demo.wireguard.com` server in client.sh is for testing
+ purposes only. You may not use this server for abusive or
+ illegal purposes.
diff --git a/contrib/wireguard-tools/contrib/ncat-client-server/client-quick.sh b/contrib/wireguard-tools/contrib/ncat-client-server/client-quick.sh
new file mode 100755
--- /dev/null
+++ b/contrib/wireguard-tools/contrib/ncat-client-server/client-quick.sh
@@ -0,0 +1,35 @@
+#!/usr/bin/env bash
+# SPDX-License-Identifier: GPL-2.0
+#
+# Copyright (C) 2015-2020 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
+
+set -e
+
+echo "[!] Warning: This server is for testing purposes only. You may not use this server for abusive or illegal purposes."
+
+echo "[+] Generating private key."
+privatekey="$(wg genkey)"
+
+echo "[+] Sending public key to server."
+exec 7<>/dev/tcp/demo.wireguard.com/42912
+wg pubkey <<<"$privatekey" >&7
+
+echo "[+] Parsing server response."
+IFS=: read -r status server_pubkey server_port internal_ip <&7
+[[ $status == OK ]] || exit 1
+
+echo "[+] Writing config file."
+[[ $UID -eq 0 ]] || sudo=sudo
+$sudo sh -c 'umask 077; mkdir -p /etc/wireguard; cat > /etc/wireguard/demo.conf' <<_EOF
+[Interface]
+PrivateKey = $privatekey
+Address = $internal_ip/24
+DNS = 8.8.8.8, 8.8.4.4, 1.1.1.1, 1.0.0.1
+
+[Peer]
+PublicKey = $server_pubkey
+Endpoint = demo.wireguard.com:$server_port
+AllowedIPs = 0.0.0.0/0
+_EOF
+
+echo "[+] Success. Run \`wg-quick up demo\` to turn on the tunnel to the demo server and \`wg-quick down demo\` to turn it off."
diff --git a/contrib/wireguard-tools/contrib/ncat-client-server/client.sh b/contrib/wireguard-tools/contrib/ncat-client-server/client.sh
new file mode 100755
--- /dev/null
+++ b/contrib/wireguard-tools/contrib/ncat-client-server/client.sh
@@ -0,0 +1,23 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+#
+# Copyright (C) 2015-2020 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
+
+set -e
+[[ $UID == 0 ]] || { echo "You must be root to run this."; exit 1; }
+exec 3<>/dev/tcp/demo.wireguard.com/42912
+privatekey="$(wg genkey)"
+wg pubkey <<<"$privatekey" >&3
+IFS=: read -r status server_pubkey server_port internal_ip <&3
+[[ $status == OK ]]
+ip link del dev wg0 2>/dev/null || true
+ip link add dev wg0 type wireguard
+wg set wg0 private-key <(echo "$privatekey") peer "$server_pubkey" allowed-ips 0.0.0.0/0 endpoint "demo.wireguard.com:$server_port" persistent-keepalive 25
+ip address add "$internal_ip"/24 dev wg0
+ip link set up dev wg0
+if [ "$1" == "default-route" ]; then
+ host="$(wg show wg0 endpoints | sed -n 's/.*\t\(.*\):.*/\1/p')"
+ ip route add $(ip route get $host | sed '/ via [0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}/{s/^\(.* via [0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}\).*/\1/}' | head -n 1) 2>/dev/null || true
+ ip route add 0/1 dev wg0
+ ip route add 128/1 dev wg0
+fi
diff --git a/contrib/wireguard-tools/contrib/ncat-client-server/server.sh b/contrib/wireguard-tools/contrib/ncat-client-server/server.sh
new file mode 100755
--- /dev/null
+++ b/contrib/wireguard-tools/contrib/ncat-client-server/server.sh
@@ -0,0 +1,18 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+#
+# Copyright (C) 2015-2020 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
+
+if [[ -z $NCAT_REMOTE_ADDR ]]; then
+ ip link del dev wg0 2>/dev/null
+ set -e
+ ip link add dev wg0 type wireguard
+ ip address add 192.168.4.1/24 dev wg0
+ wg set wg0 private-key <(wg genkey) listen-port 12912
+ ip link set up dev wg0
+ exec ncat -e "$(readlink -f "$0")" -k -l -p 42912 -v
+fi
+read -r public_key
+[[ $(wg show wg0 peers | wc -l) -ge 253 ]] && wg set wg0 peer $(wg show wg0 latest-handshakes | sort -k 2 -b -n | head -n 1 | cut -f 1) remove
+next_ip=$(all="$(wg show wg0 allowed-ips)"; for ((i=2; i<=254; i++)); do ip="192.168.4.$i"; [[ $all != *$ip/32* ]] && echo $ip && break; done)
+wg set wg0 peer "$public_key" allowed-ips $next_ip/32 2>/dev/null && echo "OK:$(wg show wg0 private-key | wg pubkey):$(wg show wg0 listen-port):$next_ip" || echo ERROR
diff --git a/contrib/wireguard-tools/contrib/reresolve-dns/README b/contrib/wireguard-tools/contrib/reresolve-dns/README
new file mode 100644
--- /dev/null
+++ b/contrib/wireguard-tools/contrib/reresolve-dns/README
@@ -0,0 +1,9 @@
+reresolve-dns
+=============
+
+Run this script from cron every thirty seconds or so, and it will ensure
+that if, when using a dynamic DNS service, the DNS entry for a hosts
+changes, the kernel will get the update to the DNS entry.
+
+This works by parsing configuration files, and simply running:
+ $ wg set wg0 peer ... endpoint ...
diff --git a/contrib/wireguard-tools/contrib/reresolve-dns/reresolve-dns.sh b/contrib/wireguard-tools/contrib/reresolve-dns/reresolve-dns.sh
new file mode 100755
--- /dev/null
+++ b/contrib/wireguard-tools/contrib/reresolve-dns/reresolve-dns.sh
@@ -0,0 +1,44 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+#
+# Copyright (C) 2015-2020 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
+
+set -e
+shopt -s nocasematch
+shopt -s extglob
+export LC_ALL=C
+
+CONFIG_FILE="$1"
+[[ $CONFIG_FILE =~ ^[a-zA-Z0-9_=+.-]{1,15}$ ]] && CONFIG_FILE="/etc/wireguard/$CONFIG_FILE.conf"
+[[ $CONFIG_FILE =~ /?([a-zA-Z0-9_=+.-]{1,15})\.conf$ ]]
+INTERFACE="${BASH_REMATCH[1]}"
+
+process_peer() {
+ [[ $PEER_SECTION -ne 1 || -z $PUBLIC_KEY || -z $ENDPOINT ]] && return 0
+ [[ $(wg show "$INTERFACE" latest-handshakes) =~ ${PUBLIC_KEY//+/\\+}\ ([0-9]+) ]] || return 0
+ (( ($EPOCHSECONDS - ${BASH_REMATCH[1]}) > 135 )) || return 0
+ wg set "$INTERFACE" peer "$PUBLIC_KEY" endpoint "$ENDPOINT"
+ reset_peer_section
+}
+
+reset_peer_section() {
+ PEER_SECTION=0
+ PUBLIC_KEY=""
+ ENDPOINT=""
+}
+
+reset_peer_section
+while read -r line || [[ -n $line ]]; do
+ stripped="${line%%\#*}"
+ key="${stripped%%=*}"; key="${key##*([[:space:]])}"; key="${key%%*([[:space:]])}"
+ value="${stripped#*=}"; value="${value##*([[:space:]])}"; value="${value%%*([[:space:]])}"
+ [[ $key == "["* ]] && { process_peer; reset_peer_section; }
+ [[ $key == "[Peer]" ]] && PEER_SECTION=1
+ if [[ $PEER_SECTION -eq 1 ]]; then
+ case "$key" in
+ PublicKey) PUBLIC_KEY="$value"; continue ;;
+ Endpoint) ENDPOINT="$value"; continue ;;
+ esac
+ fi
+done < "$CONFIG_FILE"
+process_peer
diff --git a/contrib/wireguard-tools/contrib/sticky-sockets/README b/contrib/wireguard-tools/contrib/sticky-sockets/README
new file mode 100644
--- /dev/null
+++ b/contrib/wireguard-tools/contrib/sticky-sockets/README
@@ -0,0 +1,5 @@
+Sticky Sockets
+==============
+
+This is a small userspace mini-library that implements as close to
+possible how the kernel does its sticky src address sending.
diff --git a/contrib/wireguard-tools/contrib/sticky-sockets/sticky-sockets.c b/contrib/wireguard-tools/contrib/sticky-sockets/sticky-sockets.c
new file mode 100644
--- /dev/null
+++ b/contrib/wireguard-tools/contrib/sticky-sockets/sticky-sockets.c
@@ -0,0 +1,331 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2015-2020 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
+ *
+ * This implements userspace semantics of "sticky sockets", modeled after
+ * WireGuard's kernelspace implementation.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <linux/ipv6.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netinet/udp.h>
+#include <arpa/inet.h>
+
+struct magic_endpoint {
+ union {
+ struct sockaddr addr;
+ struct sockaddr_in addr4;
+ struct sockaddr_in6 addr6;
+ };
+ union {
+ struct {
+ struct in_addr src4;
+ int src_if4; /* Essentially the same as addr6->scope_id */
+ };
+ struct in6_addr src6;
+ };
+};
+
+ssize_t magic_send4(int sock, struct magic_endpoint *endpoint, void *buffer, size_t len)
+{
+ ssize_t ret;
+ struct iovec iovec = {
+ .iov_base = buffer,
+ .iov_len = len
+ };
+ struct {
+ struct cmsghdr cmsghdr;
+ struct in_pktinfo pktinfo;
+ } cmsg = {
+ .cmsghdr.cmsg_level = IPPROTO_IP,
+ .cmsghdr.cmsg_type = IP_PKTINFO,
+ .cmsghdr.cmsg_len = CMSG_LEN(sizeof(cmsg.pktinfo)),
+ .pktinfo.ipi_spec_dst = endpoint->src4,
+ .pktinfo.ipi_ifindex = endpoint->src_if4
+ };
+ struct msghdr msghdr = {
+ .msg_iov = &iovec,
+ .msg_iovlen = 1,
+ .msg_name = &endpoint->addr4,
+ .msg_namelen = sizeof(endpoint->addr4),
+ .msg_control = &cmsg,
+ .msg_controllen = sizeof(cmsg)
+ };
+ ret = sendmsg(sock, &msghdr, 0);
+ if (ret < 0 && errno == EINVAL) {
+ memset(&cmsg.pktinfo, 0, sizeof(cmsg.pktinfo));
+ endpoint->src4.s_addr = endpoint->src_if4 = 0;
+ return sendmsg(sock, &msghdr, 0);
+ }
+ return ret;
+}
+
+ssize_t magic_send6(int sock, struct magic_endpoint *endpoint, void *buffer, size_t len)
+{
+ ssize_t ret;
+ struct iovec iovec = {
+ .iov_base = buffer,
+ .iov_len = len
+ };
+ struct {
+ struct cmsghdr cmsghdr;
+ struct in6_pktinfo pktinfo;
+ } cmsg = {
+ .cmsghdr.cmsg_level = IPPROTO_IPV6,
+ .cmsghdr.cmsg_type = IPV6_PKTINFO,
+ .cmsghdr.cmsg_len = CMSG_LEN(sizeof(cmsg.pktinfo)),
+ .pktinfo.ipi6_addr = endpoint->src6,
+ .pktinfo.ipi6_ifindex = memcmp(&in6addr_any, &endpoint->src6, sizeof(endpoint->src6)) ? endpoint->addr6.sin6_scope_id : 0
+ };
+ struct msghdr msghdr = {
+ .msg_iov = &iovec,
+ .msg_iovlen = 1,
+ .msg_name = &endpoint->addr6,
+ .msg_namelen = sizeof(endpoint->addr6),
+ .msg_control = &cmsg,
+ .msg_controllen = sizeof(cmsg)
+ };
+
+ ret = sendmsg(sock, &msghdr, 0);
+ if (ret < 0 && errno == EINVAL) {
+ memset(&cmsg.pktinfo, 0, sizeof(cmsg.pktinfo));
+ memset(&endpoint->src6, 0, sizeof(endpoint->src6));
+ return sendmsg(sock, &msghdr, 0);
+ }
+ return ret;
+}
+
+ssize_t magic_receive4(int sock, struct magic_endpoint *endpoint, void *buffer, size_t len)
+{
+ ssize_t ret;
+ struct iovec iovec = {
+ .iov_base = buffer,
+ .iov_len = len
+ };
+ struct {
+ struct cmsghdr cmsghdr;
+ struct in_pktinfo pktinfo;
+ } cmsg;
+ struct msghdr msghdr = {
+ .msg_iov = &iovec,
+ .msg_iovlen = 1,
+ .msg_name = &endpoint->addr4,
+ .msg_namelen = sizeof(endpoint->addr4),
+ .msg_control = &cmsg,
+ .msg_controllen = sizeof(cmsg)
+ };
+
+ ret = recvmsg(sock, &msghdr, 0);
+ if (ret < 0)
+ return ret;
+ if (cmsg.cmsghdr.cmsg_level == IPPROTO_IP && cmsg.cmsghdr.cmsg_type == IP_PKTINFO && cmsg.cmsghdr.cmsg_len >= CMSG_LEN(sizeof(cmsg.pktinfo))) {
+ endpoint->src4 = cmsg.pktinfo.ipi_spec_dst;
+ endpoint->src_if4 = cmsg.pktinfo.ipi_ifindex;
+ }
+ return ret;
+}
+
+ssize_t magic_receive6(int sock, struct magic_endpoint *endpoint, void *buffer, size_t len)
+{
+ ssize_t ret;
+ struct iovec iovec = {
+ .iov_base = buffer,
+ .iov_len = len
+ };
+ struct {
+ struct cmsghdr cmsghdr;
+ struct in6_pktinfo pktinfo;
+ } cmsg;
+ struct msghdr msghdr = {
+ .msg_iov = &iovec,
+ .msg_iovlen = 1,
+ .msg_name = &endpoint->addr6,
+ .msg_namelen = sizeof(endpoint->addr6),
+ .msg_control = &cmsg,
+ .msg_controllen = sizeof(cmsg)
+ };
+
+ ret = recvmsg(sock, &msghdr, 0);
+ if (ret < 0)
+ return ret;
+ if (cmsg.cmsghdr.cmsg_level == IPPROTO_IPV6 && cmsg.cmsghdr.cmsg_type == IPV6_PKTINFO && cmsg.cmsghdr.cmsg_len >= CMSG_LEN(sizeof(cmsg.pktinfo))) {
+ endpoint->src6 = cmsg.pktinfo.ipi6_addr;
+ endpoint->addr6.sin6_scope_id = cmsg.pktinfo.ipi6_ifindex;
+ }
+ return ret;
+}
+
+void magic_endpoint_clearsrc(struct magic_endpoint *endpoint)
+{
+ if (endpoint->addr.sa_family == AF_INET)
+ endpoint->src4.s_addr = endpoint->src_if4 = 0;
+ else if (endpoint->addr.sa_family == AF_INET6)
+ memset(&endpoint->src6, 0, sizeof(endpoint->src6));
+ else
+ memset(endpoint, 0, sizeof(*endpoint));
+}
+
+void magic_endpoint_set(struct magic_endpoint *endpoint, const struct sockaddr *addr)
+{
+ if (addr->sa_family == AF_INET)
+ endpoint->addr4 = *(struct sockaddr_in *)addr;
+ else if (addr->sa_family == AF_INET6)
+ endpoint->addr6 = *(struct sockaddr_in6 *)addr;
+ magic_endpoint_clearsrc(endpoint);
+}
+
+int magic_create_sock4(uint16_t listen_port)
+{
+ static const int on = 1;
+ struct sockaddr_in listen_addr = {
+ .sin_family = AF_INET,
+ .sin_port = htons(listen_port),
+ .sin_addr = INADDR_ANY
+ };
+ int fd, ret;
+
+ fd = socket(AF_INET, SOCK_DGRAM, 0);
+ if (fd < 0)
+ return fd;
+
+ ret = setsockopt(fd, IPPROTO_IP, IP_PKTINFO, &on, sizeof(on));
+ if (ret < 0)
+ goto err;
+
+ ret = bind(fd, (struct sockaddr *)&listen_addr, sizeof(listen_addr));
+ if (ret < 0)
+ goto err;
+
+ return fd;
+
+err:
+ close(fd);
+ return ret;
+}
+
+int magic_create_sock6(uint16_t listen_port)
+{
+ static const int on = 1;
+ struct sockaddr_in6 listen_addr = {
+ .sin6_family = AF_INET6,
+ .sin6_port = htons(listen_port),
+ .sin6_addr = IN6ADDR_ANY_INIT
+ };
+ int fd, ret;
+
+ fd = socket(AF_INET6, SOCK_DGRAM, 0);
+ if (fd < 0)
+ return fd;
+
+ ret = setsockopt(fd, IPPROTO_IPV6, IPV6_RECVPKTINFO, &on, sizeof(on));
+ if (ret < 0)
+ goto err;
+
+ ret = setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof(on));
+ if (ret < 0)
+ goto err;
+
+ ret = bind(fd, (struct sockaddr *)&listen_addr, sizeof(listen_addr));
+ if (ret < 0)
+ goto err;
+
+ return fd;
+
+err:
+ close(fd);
+ return ret;
+}
+
+int main(int argc, char *argv[])
+{
+ struct magic_endpoint endpoint = { 0 };
+ int sock;
+ ssize_t ret;
+ uint8_t buffer[1024] = { 0 };
+ char srcaddr[40], dstaddr[40];
+
+ if (argc == 2 && !strcmp(argv[1], "-4"))
+ goto v4;
+ if (argc == 2 && !strcmp(argv[1], "-6"))
+ goto v6;
+ return 1;
+
+v6:
+ sock = magic_create_sock6(51820);
+ if (sock < 0) {
+ perror("magic_create_sock6");
+ return 1;
+ }
+
+ ret = magic_receive6(sock, &endpoint, buffer, sizeof(buffer));
+ if (ret < 0) {
+ perror("magic_receive6");
+ return 1;
+ }
+
+ if (!inet_ntop(AF_INET6, &endpoint.src6, srcaddr, sizeof(srcaddr))) {
+ perror("inet_ntop");
+ return 1;
+ }
+
+ if (!inet_ntop(AF_INET6, &endpoint.addr6.sin6_addr, dstaddr, sizeof(dstaddr))) {
+ perror("inet_ntop");
+ return 1;
+ }
+
+ printf("if:%d src:%s dst:%s\n", endpoint.addr6.sin6_scope_id, srcaddr, dstaddr);
+ printf("Received a packet. Sleeping for 10 seconds before replying, so you have time to mess with your networking setup.\n");
+ sleep(10);
+
+ ret = magic_send6(sock, &endpoint, buffer, sizeof(buffer));
+ if (ret < 0) {
+ perror("magic_send6");
+ return 1;
+ }
+
+ close(sock);
+ return 0;
+
+v4:
+ sock = magic_create_sock4(51820);
+ if (sock < 0) {
+ perror("magic_create_sock4");
+ return 1;
+ }
+
+ ret = magic_receive4(sock, &endpoint, buffer, sizeof(buffer));
+ if (ret < 0) {
+ perror("magic_receive4");
+ return 1;
+ }
+
+ if (!inet_ntop(AF_INET, &endpoint.src4, srcaddr, sizeof(srcaddr))) {
+ perror("inet_ntop");
+ return 1;
+ }
+
+ if (!inet_ntop(AF_INET, &endpoint.addr4.sin_addr, dstaddr, sizeof(dstaddr))) {
+ perror("inet_ntop");
+ return 1;
+ }
+
+ printf("if:%d src:%s dst:%s\n", endpoint.src_if4, srcaddr, dstaddr);
+ printf("Received a packet. Sleeping for 10 seconds before replying, so you have time to mess with your networking setup.\n");
+ sleep(10);
+
+ ret = magic_send4(sock, &endpoint, buffer, sizeof(buffer));
+ if (ret < 0) {
+ perror("magic_send4");
+ return 1;
+ }
+
+ close(sock);
+ return 0;
+}
diff --git a/contrib/wireguard-tools/contrib/synergy/README b/contrib/wireguard-tools/contrib/synergy/README
new file mode 100644
--- /dev/null
+++ b/contrib/wireguard-tools/contrib/synergy/README
@@ -0,0 +1,3 @@
+These scripts should be modified according to your precise setup.
+They provide a very simple way of tunneling synergy inside of a
+WireGuard tunnel, to protect your data in transit.
diff --git a/contrib/wireguard-tools/contrib/synergy/synergy-client.sh b/contrib/wireguard-tools/contrib/synergy/synergy-client.sh
new file mode 100755
--- /dev/null
+++ b/contrib/wireguard-tools/contrib/synergy/synergy-client.sh
@@ -0,0 +1,22 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+#
+# Copyright (C) 2015-2020 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
+
+set -ex
+if [[ $UID == 0 ]]; then
+ ip link del dev synergy || true
+ ip link add dev synergy type wireguard
+ ip address add 10.193.125.39/32 peer 10.193.125.38/32 dev synergy
+ wg set synergy \
+ listen-port 29184 \
+ private-key <(echo oNcsXA5Ma56q9xHmvvKuzLfwXYy7Uqy+bTmmXg/XtVs=) \
+ peer m321UMZXoJ6qw8Jli2spbAVBc2MdOzV/EHDKfZQy0g0= \
+ allowed-ips 10.193.125.38/32 \
+ endpoint 10.10.10.100:29184
+ ip link set up dev synergy
+else
+ sudo "$(readlink -f "$0")"
+ killall synergyc || true
+ synergyc 10.193.125.38:38382
+fi
diff --git a/contrib/wireguard-tools/contrib/synergy/synergy-server.sh b/contrib/wireguard-tools/contrib/synergy/synergy-server.sh
new file mode 100755
--- /dev/null
+++ b/contrib/wireguard-tools/contrib/synergy/synergy-server.sh
@@ -0,0 +1,21 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+#
+# Copyright (C) 2015-2020 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
+
+set -ex
+if [[ $UID == 0 ]]; then
+ ip link del dev synergy || true
+ ip link add dev synergy type wireguard
+ ip address add 10.193.125.38/32 peer 10.193.125.39/32 dev synergy
+ wg set synergy \
+ listen-port 29184 \
+ private-key <(echo 2InSrlZA5eQfI/MvnvPieqNTBo9cd+udc3SOO9yFpXo=) \
+ peer CBnoidQLjlbRsrqrI56WQbANWwkll41w/rVUIW9zISI= \
+ allowed-ips 10.193.125.39/32
+ ip link set up dev synergy
+else
+ sudo "$(readlink -f "$0")"
+ killall synergys || true
+ synergys -a 10.193.125.38:38382
+fi
diff --git a/contrib/wireguard-tools/src/Makefile b/contrib/wireguard-tools/src/Makefile
new file mode 100644
--- /dev/null
+++ b/contrib/wireguard-tools/src/Makefile
@@ -0,0 +1,114 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+# Copyright (C) 2015-2020 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
+
+PKG_CONFIG ?= pkg-config
+PREFIX ?= /usr
+DESTDIR ?=
+SYSCONFDIR ?= /etc
+BINDIR ?= $(PREFIX)/bin
+LIBDIR ?= $(PREFIX)/lib
+MANDIR ?= $(PREFIX)/share/man
+BASHCOMPDIR ?= $(PREFIX)/share/bash-completion/completions
+SYSTEMDUNITDIR ?= $(shell $(PKG_CONFIG) --variable=systemdsystemunitdir systemd 2>/dev/null || echo "$(PREFIX)/lib/systemd/system")
+RUNSTATEDIR ?= /var/run
+WITH_BASHCOMPLETION ?=
+WITH_WGQUICK ?=
+WITH_SYSTEMDUNITS ?=
+
+ifeq ($(WITH_BASHCOMPLETION),)
+ifneq ($(strip $(wildcard $(BASHCOMPDIR))),)
+WITH_BASHCOMPLETION := yes
+endif
+endif
+ifeq ($(WITH_WGQUICK),)
+ifneq ($(strip $(wildcard $(BINDIR)/bash)),)
+WITH_WGQUICK := yes
+endif
+ifneq ($(strip $(wildcard $(DESTDIR)/bin/bash)),)
+WITH_WGQUICK := yes
+endif
+endif
+ifeq ($(WITH_SYSTEMDUNITS),)
+ifneq ($(strip $(wildcard $(SYSTEMDUNITDIR))),)
+WITH_SYSTEMDUNITS := yes
+endif
+endif
+
+PLATFORM ?= $(shell uname -s | tr '[:upper:]' '[:lower:]')
+
+CFLAGS ?= -O3
+ifneq ($(wildcard uapi/$(PLATFORM)/.),)
+CFLAGS += -idirafter uapi/$(PLATFORM)
+endif
+CFLAGS += -std=gnu99 -D_GNU_SOURCE
+CFLAGS += -Wall -Wextra
+CFLAGS += -MMD -MP
+CFLAGS += -DRUNSTATEDIR="\"$(RUNSTATEDIR)\""
+ifeq ($(DEBUG),yes)
+CFLAGS += -g
+endif
+WIREGUARD_TOOLS_VERSION = $(patsubst v%,%,$(shell GIT_DIR="$(PWD)/../.git" git describe --dirty 2>/dev/null))
+ifneq ($(WIREGUARD_TOOLS_VERSION),)
+CFLAGS += -D'WIREGUARD_TOOLS_VERSION="$(WIREGUARD_TOOLS_VERSION)"'
+endif
+ifeq ($(PLATFORM),freebsd)
+LDLIBS += -lnv
+endif
+ifeq ($(PLATFORM),haiku)
+LDLIBS += -lnetwork -lbsd
+endif
+ifeq ($(PLATFORM),windows)
+CC := x86_64-w64-mingw32-clang
+WINDRES := $(shell $(CC) $(CFLAGS) -print-prog-name=windres 2>/dev/null)
+CFLAGS += -Iwincompat/include -include wincompat/compat.h -DWINVER=0x0601 -D_WIN32_WINNT=0x0601 -flto
+LDLIBS += -lws2_32 -lsetupapi -lole32 -ladvapi32 -lntdll -Lwincompat
+LDFLAGS += -flto -Wl,--dynamicbase -Wl,--nxcompat -Wl,--tsaware -mconsole
+LDFLAGS += -Wl,--major-os-version=6 -Wl,--minor-os-version=1 -Wl,--major-subsystem-version=6 -Wl,--minor-subsystem-version=1
+# The use of -Wl,/delayload: here implies we're using llvm-mingw
+LDFLAGS += -Wl,/delayload:ws2_32.dll -Wl,/delayload:setupapi.dll -Wl,/delayload:ole32.dll -Wl,/delayload:advapi32.dll
+VERSION := $(patsubst "%",%,$(filter "%",$(file < version.h)))
+wg: wincompat/libc.o wincompat/init.o wincompat/loader.o wincompat/resources.o
+wincompat/resources.o: wincompat/resources.rc wincompat/manifest.xml
+ $(WINDRES) -DVERSION_STR=$(VERSION) -O coff -c 65001 -i $< -o $@
+endif
+
+ifneq ($(V),1)
+BUILT_IN_LINK.o := $(LINK.o)
+LINK.o = @echo " LD $@";
+LINK.o += $(BUILT_IN_LINK.o)
+BUILT_IN_COMPILE.c := $(COMPILE.c)
+COMPILE.c = @echo " CC $@";
+COMPILE.c += $(BUILT_IN_COMPILE.c)
+BUILT_IN_RM := $(RM)
+RM := @a() { echo " CLEAN $$@"; $(BUILT_IN_RM) "$$@"; }; a
+WINDRES := @a() { echo " WINDRES $${@: -1}"; $(WINDRES) "$$@"; }; a
+endif
+
+wg: $(sort $(patsubst %.c,%.o,$(wildcard *.c)))
+
+clean:
+ $(RM) wg *.o *.d $(wildcard wincompat/*.o wincompat/*.lib wincompat/*.dll)
+
+install: wg
+ @install -v -d "$(DESTDIR)$(BINDIR)" && install -v -m 0755 wg "$(DESTDIR)$(BINDIR)/wg"
+ @install -v -d "$(DESTDIR)$(MANDIR)/man8" && install -v -m 0644 man/wg.8 "$(DESTDIR)$(MANDIR)/man8/wg.8"
+ @[ "$(WITH_BASHCOMPLETION)" = "yes" ] || exit 0; \
+ install -v -d "$(DESTDIR)$(BASHCOMPDIR)" && install -v -m 0644 completion/wg.bash-completion "$(DESTDIR)$(BASHCOMPDIR)/wg"
+ @[ "$(WITH_WGQUICK)" = "yes" ] || exit 0; \
+ install -v -m 0755 wg-quick/$(PLATFORM).bash "$(DESTDIR)$(BINDIR)/wg-quick" && install -v -m 0700 -d "$(DESTDIR)$(SYSCONFDIR)/wireguard"
+ @[ "$(WITH_WGQUICK)" = "yes" ] || exit 0; \
+ install -v -m 0644 man/wg-quick.8 "$(DESTDIR)$(MANDIR)/man8/wg-quick.8"
+ @[ "$(WITH_WGQUICK)" = "yes" -a "$(WITH_BASHCOMPLETION)" = "yes" ] || exit 0; \
+ install -v -m 0644 completion/wg-quick.bash-completion "$(DESTDIR)$(BASHCOMPDIR)/wg-quick"
+ @[ "$(WITH_WGQUICK)" = "yes" -a "$(WITH_SYSTEMDUNITS)" = "yes" ] || exit 0; \
+ install -v -d "$(DESTDIR)$(SYSTEMDUNITDIR)" && install -v -m 0644 systemd/* "$(DESTDIR)$(SYSTEMDUNITDIR)/"
+
+check: clean
+ scan-build --html-title=wireguard-tools -maxloop 100 --view --keep-going $(MAKE) wg
+
+all: wg
+.DEFAULT_GOAL: all
+.PHONY: clean install check
+
+-include *.d
diff --git a/contrib/wireguard-tools/src/completion/wg-quick.bash-completion b/contrib/wireguard-tools/src/completion/wg-quick.bash-completion
new file mode 100644
--- /dev/null
+++ b/contrib/wireguard-tools/src/completion/wg-quick.bash-completion
@@ -0,0 +1,43 @@
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (C) 2015-2020 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
+
+_wg_quick_completion() {
+ local p i a search_paths old_glob
+ search_paths=( /etc/wireguard )
+
+ old_glob="$(shopt -p nullglob)"
+ shopt -s nullglob
+
+ [[ $OSTYPE == *freebsd* || $OSTYPE == *darwin* ]] && search_paths+=( /usr/local/etc/wireguard )
+
+ if [[ $COMP_CWORD -eq 1 ]]; then
+ COMPREPLY+=( $(compgen -W "up down" -- "${COMP_WORDS[1]}") )
+ elif [[ $COMP_CWORD -eq 2 ]]; then
+ if [[ ${COMP_WORDS[1]} == up ]]; then
+ for p in "${search_paths[@]}"; do
+ for i in "$p"/*.conf; do
+ i="${i##*/}"; i="${i%.conf}"
+ mapfile -t a < <(compgen -W "$i" -- "${COMP_WORDS[2]}")
+ COMPREPLY+=( "${a[@]}" )
+ done
+ done
+ mapfile -t a < <(compgen -f -X '!*.conf' -- "${COMP_WORDS[2]}")
+ COMPREPLY+=( "${a[@]}" )
+ mapfile -t a < <(compgen -d -- "${COMP_WORDS[2]}")
+ COMPREPLY+=( "${a[@]}" )
+ elif [[ ${COMP_WORDS[1]} == down ]]; then
+ if [[ $OSTYPE == *openbsd* || $OSTYPE == *darwin* ]]; then
+ for i in /var/run/wireguard/*.name; do
+ i="${i##*/}"; i="${i%.name}"
+ mapfile -t a < <(compgen -W "$i" -- "${COMP_WORDS[2]}")
+ COMPREPLY+=( "${a[@]}" )
+ done
+ else
+ COMPREPLY+=( $(compgen -W "$(wg show interfaces)" -- "${COMP_WORDS[2]}") )
+ fi
+ fi
+ fi
+ eval "$old_glob"
+}
+
+complete -o filenames -o nosort -F _wg_quick_completion wg-quick
diff --git a/contrib/wireguard-tools/src/completion/wg.bash-completion b/contrib/wireguard-tools/src/completion/wg.bash-completion
new file mode 100644
--- /dev/null
+++ b/contrib/wireguard-tools/src/completion/wg.bash-completion
@@ -0,0 +1,99 @@
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (C) 2015-2020 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
+
+_wg_completion() {
+ local a
+
+ if [[ $COMP_CWORD -eq 1 ]]; then
+ COMPREPLY+=( $(compgen -W "help show showconf set setconf addconf syncconf genkey genpsk pubkey" -- "${COMP_WORDS[1]}") )
+ return
+ fi
+ case "${COMP_WORDS[1]}" in
+ genkey|genpsk|pubkey|help) return; ;;
+ show|showconf|set|setconf|addconf|syncconf) ;;
+ *) return;
+ esac
+
+ if [[ $COMP_CWORD -eq 2 ]]; then
+ local extra
+ [[ ${COMP_WORDS[1]} == show ]] && extra=" all interfaces"
+ COMPREPLY+=( $(compgen -W "$(wg show interfaces 2>/dev/null)$extra" -- "${COMP_WORDS[2]}") )
+ return
+ fi
+
+ if [[ $COMP_CWORD -eq 3 && ${COMP_WORDS[1]} == show && ${COMP_WORDS[2]} != interfaces ]]; then
+ COMPREPLY+=( $(compgen -W "public-key private-key listen-port peers preshared-keys endpoints allowed-ips fwmark latest-handshakes persistent-keepalive transfer dump" -- "${COMP_WORDS[3]}") )
+ return
+ fi
+
+ if [[ $COMP_CWORD -eq 3 && ( ${COMP_WORDS[1]} == setconf || ${COMP_WORDS[1]} == addconf || ${COMP_WORDS[1]} == syncconf) ]]; then
+ compopt -o filenames
+ mapfile -t a < <(compgen -f -- "${COMP_WORDS[3]}")
+ COMPREPLY+=( "${a[@]}" )
+ return
+ fi
+
+ [[ ${COMP_WORDS[1]} == set ]] || return
+
+ local has_listen_port=0 has_fwmark=0 has_private_key=0 has_preshared_key=0 has_peer=0 has_remove=0 has_endpoint=0 has_persistent_keepalive=0 has_allowed_ips=0 words=() i j
+ for ((i=3;i<COMP_CWORD;i+=2)); do
+ [[ ${COMP_WORDS[i]} == listen-port ]] && has_listen_port=1
+ [[ ${COMP_WORDS[i]} == fwmark ]] && has_fwmark=1
+ [[ ${COMP_WORDS[i]} == private-key ]] && has_private_key=1
+ [[ ${COMP_WORDS[i]} == peer ]] && { has_peer=$i; break; }
+ done
+ if [[ $has_peer -eq 0 ]]; then
+ if ((COMP_CWORD % 2 != 0)); then
+ [[ $has_listen_port -eq 1 ]] || words+=( listen-port )
+ [[ $has_fwmark -eq 1 ]] || words+=( fwmark )
+ [[ $has_private_key -eq 1 ]] || words+=( private-key )
+ words+=( peer )
+ COMPREPLY+=( $(compgen -W "${words[*]}" -- "${COMP_WORDS[COMP_CWORD]}") )
+ elif [[ ${COMP_WORDS[COMP_CWORD-1]} == *-key ]]; then
+ compopt -o filenames
+ mapfile -t a < <(compgen -f -- "${COMP_WORDS[COMP_CWORD]}")
+ COMPREPLY+=( "${a[@]}" )
+ fi
+ return
+ fi
+
+ if [[ ${COMP_WORDS[COMP_CWORD-1]} == peer ]]; then
+ COMPREPLY+=( $(compgen -W "$(wg show "${COMP_WORDS[2]}" peers 2>/dev/null)" -- "${COMP_WORDS[COMP_CWORD]}") )
+ return
+ fi
+
+ for ((i=has_peer;i<COMP_CWORD;i++)); do
+ j=i
+ if [[ ${COMP_WORDS[i]} == peer ]]; then
+ has_remove=0
+ has_endpoint=0
+ has_persistent_keepalive=0
+ has_allowed_ips=0
+ has_preshared_key=0
+ [[ ${COMP_WORDS[i+2]} == = ]] && ((i+=2)) || ((i++))
+ continue
+ fi
+ [[ ${COMP_WORDS[i]} == remove ]] && has_remove=1
+ [[ ${COMP_WORDS[i]} == endpoint ]] && has_endpoint=1
+ [[ ${COMP_WORDS[i]} == persistent-keepalive ]] && has_persistent_keepalive=1
+ [[ ${COMP_WORDS[i]} == allowed-ips ]] && has_allowed_ips=1
+ [[ ${COMP_WORDS[i]} == preshared-key ]] && has_preshared_key=1
+
+ [[ ${COMP_WORDS[i]} == remove ]] || ((i++))
+ done
+
+ ((COMP_CWORD == j)) || return
+
+ if [[ $has_remove -ne 1 ]]; then
+ [[ $has_preshared_key -eq 1 ]] || words+=( preshared-key )
+ [[ $has_endpoint -eq 1 ]] || words+=( endpoint )
+ [[ $has_allowed_ips -eq 1 ]] || words+=( allowed-ips )
+ [[ $has_persistent_keepalive -eq 1 ]] || words+=( persistent-keepalive )
+ words+=( remove )
+ fi
+ words+=( peer )
+
+ COMPREPLY+=( $(compgen -W "${words[*]}" -- "${COMP_WORDS[COMP_CWORD]}") )
+}
+
+complete -o nosort -F _wg_completion wg
diff --git a/contrib/wireguard-tools/src/config.h b/contrib/wireguard-tools/src/config.h
new file mode 100644
--- /dev/null
+++ b/contrib/wireguard-tools/src/config.h
@@ -0,0 +1,27 @@
+/* SPDX-License-Identifier: GPL-2.0 OR MIT */
+/*
+ * Copyright (C) 2015-2020 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
+ */
+
+#ifndef CONFIG_H
+#define CONFIG_H
+
+#include <stdbool.h>
+
+struct wgdevice;
+struct wgpeer;
+struct wgallowedip;
+
+struct config_ctx {
+ struct wgdevice *device;
+ struct wgpeer *last_peer;
+ struct wgallowedip *last_allowedip;
+ bool is_peer_section, is_device_section;
+};
+
+struct wgdevice *config_read_cmd(const char *argv[], int argc);
+bool config_read_init(struct config_ctx *ctx, bool append);
+bool config_read_line(struct config_ctx *ctx, const char *line);
+struct wgdevice *config_read_finish(struct config_ctx *ctx);
+
+#endif
diff --git a/contrib/wireguard-tools/src/config.c b/contrib/wireguard-tools/src/config.c
new file mode 100644
--- /dev/null
+++ b/contrib/wireguard-tools/src/config.c
@@ -0,0 +1,650 @@
+// SPDX-License-Identifier: GPL-2.0 OR MIT
+/*
+ * Copyright (C) 2015-2020 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
+ */
+
+#include <arpa/inet.h>
+#include <limits.h>
+#include <netdb.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <errno.h>
+
+#include "config.h"
+#include "containers.h"
+#include "ipc.h"
+#include "encoding.h"
+#include "ctype.h"
+
+#define COMMENT_CHAR '#'
+
+static const char *get_value(const char *line, const char *key)
+{
+ size_t linelen = strlen(line);
+ size_t keylen = strlen(key);
+
+ if (keylen >= linelen)
+ return NULL;
+
+ if (strncasecmp(line, key, keylen))
+ return NULL;
+
+ return line + keylen;
+}
+
+static inline bool parse_port(uint16_t *port, uint32_t *flags, const char *value)
+{
+ int ret;
+ struct addrinfo *resolved;
+ struct addrinfo hints = {
+ .ai_family = AF_UNSPEC,
+ .ai_socktype = SOCK_DGRAM,
+ .ai_protocol = IPPROTO_UDP,
+ .ai_flags = AI_PASSIVE
+ };
+
+ if (!strlen(value)) {
+ fprintf(stderr, "Unable to parse empty port\n");
+ return false;
+ }
+
+ ret = getaddrinfo(NULL, value, &hints, &resolved);
+ if (ret) {
+ fprintf(stderr, "%s: `%s'\n", ret == EAI_SYSTEM ? strerror(errno) : gai_strerror(ret), value);
+ return false;
+ }
+
+ ret = -1;
+ if (resolved->ai_family == AF_INET && resolved->ai_addrlen == sizeof(struct sockaddr_in)) {
+ *port = ntohs(((struct sockaddr_in *)resolved->ai_addr)->sin_port);
+ ret = 0;
+ } else if (resolved->ai_family == AF_INET6 && resolved->ai_addrlen == sizeof(struct sockaddr_in6)) {
+ *port = ntohs(((struct sockaddr_in6 *)resolved->ai_addr)->sin6_port);
+ ret = 0;
+ } else
+ fprintf(stderr, "Neither IPv4 nor IPv6 address found: `%s'\n", value);
+
+ freeaddrinfo(resolved);
+ if (!ret)
+ *flags |= WGDEVICE_HAS_LISTEN_PORT;
+ return ret == 0;
+}
+
+static inline bool parse_fwmark(uint32_t *fwmark, uint32_t *flags, const char *value)
+{
+ unsigned long ret;
+ char *end;
+ int base = 10;
+
+ if (!strcasecmp(value, "off")) {
+ *fwmark = 0;
+ *flags |= WGDEVICE_HAS_FWMARK;
+ return true;
+ }
+
+ if (!char_is_digit(value[0]))
+ goto err;
+
+ if (strlen(value) > 2 && value[0] == '0' && value[1] == 'x')
+ base = 16;
+
+ ret = strtoul(value, &end, base);
+ if (*end || ret > UINT32_MAX)
+ goto err;
+
+ *fwmark = ret;
+ *flags |= WGDEVICE_HAS_FWMARK;
+ return true;
+err:
+ fprintf(stderr, "Fwmark is neither 0/off nor 0-0xffffffff: `%s'\n", value);
+ return false;
+}
+
+static inline bool parse_key(uint8_t key[static WG_KEY_LEN], const char *value)
+{
+ if (!key_from_base64(key, value)) {
+ fprintf(stderr, "Key is not the correct length or format: `%s'\n", value);
+ memset(key, 0, WG_KEY_LEN);
+ return false;
+ }
+ return true;
+}
+
+static bool parse_keyfile(uint8_t key[static WG_KEY_LEN], const char *path)
+{
+ FILE *f;
+ int c;
+ char dst[WG_KEY_LEN_BASE64];
+ bool ret = false;
+
+ f = fopen(path, "r");
+ if (!f) {
+ perror("fopen");
+ return false;
+ }
+
+ if (fread(dst, WG_KEY_LEN_BASE64 - 1, 1, f) != 1) {
+ /* If we're at the end and we didn't read anything, we're /dev/null or an empty file. */
+ if (!ferror(f) && feof(f) && !ftell(f)) {
+ memset(key, 0, WG_KEY_LEN);
+ ret = true;
+ goto out;
+ }
+
+ fprintf(stderr, "Invalid length key in key file\n");
+ goto out;
+ }
+ dst[WG_KEY_LEN_BASE64 - 1] = '\0';
+
+ while ((c = getc(f)) != EOF) {
+ if (!char_is_space(c)) {
+ fprintf(stderr, "Found trailing character in key file: `%c'\n", c);
+ goto out;
+ }
+ }
+ if (ferror(f) && errno) {
+ perror("getc");
+ goto out;
+ }
+ ret = parse_key(key, dst);
+
+out:
+ fclose(f);
+ return ret;
+}
+
+static inline bool parse_ip(struct wgallowedip *allowedip, const char *value)
+{
+ allowedip->family = AF_UNSPEC;
+ if (strchr(value, ':')) {
+ if (inet_pton(AF_INET6, value, &allowedip->ip6) == 1)
+ allowedip->family = AF_INET6;
+ } else {
+ if (inet_pton(AF_INET, value, &allowedip->ip4) == 1)
+ allowedip->family = AF_INET;
+ }
+ if (allowedip->family == AF_UNSPEC) {
+ fprintf(stderr, "Unable to parse IP address: `%s'\n", value);
+ return false;
+ }
+ return true;
+}
+
+static inline int parse_dns_retries(void)
+{
+ unsigned long ret;
+ char *retries = getenv("WG_ENDPOINT_RESOLUTION_RETRIES"), *end;
+
+ if (!retries)
+ return 15;
+ if (!strcmp(retries, "infinity"))
+ return -1;
+
+ ret = strtoul(retries, &end, 10);
+ if (*end || ret > INT_MAX) {
+ fprintf(stderr, "Unable to parse WG_ENDPOINT_RESOLUTION_RETRIES: `%s'\n", retries);
+ exit(1);
+ }
+ return (int)ret;
+}
+
+static inline bool parse_endpoint(struct sockaddr *endpoint, const char *value)
+{
+ char *mutable = strdup(value);
+ char *begin, *end;
+ int ret, retries = parse_dns_retries();
+ struct addrinfo *resolved;
+ struct addrinfo hints = {
+ .ai_family = AF_UNSPEC,
+ .ai_socktype = SOCK_DGRAM,
+ .ai_protocol = IPPROTO_UDP
+ };
+ if (!mutable) {
+ perror("strdup");
+ return false;
+ }
+ if (!strlen(value)) {
+ free(mutable);
+ fprintf(stderr, "Unable to parse empty endpoint\n");
+ return false;
+ }
+ if (mutable[0] == '[') {
+ begin = &mutable[1];
+ end = strchr(mutable, ']');
+ if (!end) {
+ free(mutable);
+ fprintf(stderr, "Unable to find matching brace of endpoint: `%s'\n", value);
+ return false;
+ }
+ *end++ = '\0';
+ if (*end++ != ':' || !*end) {
+ free(mutable);
+ fprintf(stderr, "Unable to find port of endpoint: `%s'\n", value);
+ return false;
+ }
+ } else {
+ begin = mutable;
+ end = strrchr(mutable, ':');
+ if (!end || !*(end + 1)) {
+ free(mutable);
+ fprintf(stderr, "Unable to find port of endpoint: `%s'\n", value);
+ return false;
+ }
+ *end++ = '\0';
+ }
+
+ #define min(a, b) ((a) < (b) ? (a) : (b))
+ for (unsigned int timeout = 1000000;; timeout = min(20000000, timeout * 6 / 5)) {
+ ret = getaddrinfo(begin, end, &hints, &resolved);
+ if (!ret)
+ break;
+ /* The set of return codes that are "permanent failures". All other possibilities are potentially transient.
+ *
+ * This is according to https://sourceware.org/glibc/wiki/NameResolver which states:
+ * "From the perspective of the application that calls getaddrinfo() it perhaps
+ * doesn't matter that much since EAI_FAIL, EAI_NONAME and EAI_NODATA are all
+ * permanent failure codes and the causes are all permanent failures in the
+ * sense that there is no point in retrying later."
+ *
+ * So this is what we do, except FreeBSD removed EAI_NODATA some time ago, so that's conditional.
+ */
+ if (ret == EAI_NONAME || ret == EAI_FAIL ||
+ #ifdef EAI_NODATA
+ ret == EAI_NODATA ||
+ #endif
+ (retries >= 0 && !retries--)) {
+ free(mutable);
+ fprintf(stderr, "%s: `%s'\n", ret == EAI_SYSTEM ? strerror(errno) : gai_strerror(ret), value);
+ return false;
+ }
+ fprintf(stderr, "%s: `%s'. Trying again in %.2f seconds...\n", ret == EAI_SYSTEM ? strerror(errno) : gai_strerror(ret), value, timeout / 1000000.0);
+ usleep(timeout);
+ }
+
+ if ((resolved->ai_family == AF_INET && resolved->ai_addrlen == sizeof(struct sockaddr_in)) ||
+ (resolved->ai_family == AF_INET6 && resolved->ai_addrlen == sizeof(struct sockaddr_in6)))
+ memcpy(endpoint, resolved->ai_addr, resolved->ai_addrlen);
+ else {
+ freeaddrinfo(resolved);
+ free(mutable);
+ fprintf(stderr, "Neither IPv4 nor IPv6 address found: `%s'\n", value);
+ return false;
+ }
+ freeaddrinfo(resolved);
+ free(mutable);
+ return true;
+}
+
+static inline bool parse_persistent_keepalive(uint16_t *interval, uint32_t *flags, const char *value)
+{
+ unsigned long ret;
+ char *end;
+
+ if (!strcasecmp(value, "off")) {
+ *interval = 0;
+ *flags |= WGPEER_HAS_PERSISTENT_KEEPALIVE_INTERVAL;
+ return true;
+ }
+
+ if (!char_is_digit(value[0]))
+ goto err;
+
+ ret = strtoul(value, &end, 10);
+ if (*end || ret > 65535)
+ goto err;
+
+ *interval = (uint16_t)ret;
+ *flags |= WGPEER_HAS_PERSISTENT_KEEPALIVE_INTERVAL;
+ return true;
+err:
+ fprintf(stderr, "Persistent keepalive interval is neither 0/off nor 1-65535: `%s'\n", value);
+ return false;
+}
+
+static bool validate_netmask(struct wgallowedip *allowedip)
+{
+ uint32_t *ip;
+ int last;
+
+ switch (allowedip->family) {
+ case AF_INET:
+ last = 0;
+ ip = (uint32_t *)&allowedip->ip4;
+ break;
+ case AF_INET6:
+ last = 3;
+ ip = (uint32_t *)&allowedip->ip6;
+ break;
+ default:
+ return true; /* We don't know how to validate it, so say 'okay'. */
+ }
+
+ for (int i = last; i >= 0; --i) {
+ uint32_t mask = ~0;
+
+ if (allowedip->cidr >= 32 * (i + 1))
+ break;
+ if (allowedip->cidr > 32 * i)
+ mask >>= (allowedip->cidr - 32 * i);
+ if (ntohl(ip[i]) & mask)
+ return false;
+ }
+
+ return true;
+}
+
+static inline bool parse_allowedips(struct wgpeer *peer, struct wgallowedip **last_allowedip, const char *value)
+{
+ struct wgallowedip *allowedip = *last_allowedip, *new_allowedip;
+ char *mask, *mutable = strdup(value), *sep, *saved_entry;
+
+ if (!mutable) {
+ perror("strdup");
+ return false;
+ }
+ peer->flags |= WGPEER_REPLACE_ALLOWEDIPS;
+ if (!strlen(value)) {
+ free(mutable);
+ return true;
+ }
+ sep = mutable;
+ while ((mask = strsep(&sep, ","))) {
+ unsigned long cidr;
+ char *end, *ip;
+
+ saved_entry = strdup(mask);
+ ip = strsep(&mask, "/");
+
+ new_allowedip = calloc(1, sizeof(*new_allowedip));
+ if (!new_allowedip) {
+ perror("calloc");
+ free(saved_entry);
+ free(mutable);
+ return false;
+ }
+
+ if (!parse_ip(new_allowedip, ip)) {
+ free(new_allowedip);
+ free(saved_entry);
+ free(mutable);
+ return false;
+ }
+
+ if (mask) {
+ if (!char_is_digit(mask[0]))
+ goto err;
+ cidr = strtoul(mask, &end, 10);
+ if (*end || (cidr > 32 && new_allowedip->family == AF_INET) || (cidr > 128 && new_allowedip->family == AF_INET6))
+ goto err;
+ } else if (new_allowedip->family == AF_INET)
+ cidr = 32;
+ else if (new_allowedip->family == AF_INET6)
+ cidr = 128;
+ else
+ goto err;
+ new_allowedip->cidr = cidr;
+
+ if (!validate_netmask(new_allowedip))
+ fprintf(stderr, "Warning: AllowedIP has nonzero host part: %s/%s\n", ip, mask);
+
+ if (allowedip)
+ allowedip->next_allowedip = new_allowedip;
+ else
+ peer->first_allowedip = new_allowedip;
+ allowedip = new_allowedip;
+ free(saved_entry);
+ }
+ free(mutable);
+ *last_allowedip = allowedip;
+ return true;
+
+err:
+ free(new_allowedip);
+ free(mutable);
+ fprintf(stderr, "AllowedIP is not in the correct format: `%s'\n", saved_entry);
+ free(saved_entry);
+ return false;
+}
+
+static bool process_line(struct config_ctx *ctx, const char *line)
+{
+ const char *value;
+ bool ret = true;
+
+ if (!strcasecmp(line, "[Interface]")) {
+ ctx->is_peer_section = false;
+ ctx->is_device_section = true;
+ return true;
+ }
+ if (!strcasecmp(line, "[Peer]")) {
+ struct wgpeer *new_peer = calloc(1, sizeof(struct wgpeer));
+
+ if (!new_peer) {
+ perror("calloc");
+ return false;
+ }
+ ctx->last_allowedip = NULL;
+ if (ctx->last_peer)
+ ctx->last_peer->next_peer = new_peer;
+ else
+ ctx->device->first_peer = new_peer;
+ ctx->last_peer = new_peer;
+ ctx->is_peer_section = true;
+ ctx->is_device_section = false;
+ ctx->last_peer->flags |= WGPEER_REPLACE_ALLOWEDIPS;
+ return true;
+ }
+
+#define key_match(key) (value = get_value(line, key "="))
+
+ if (ctx->is_device_section) {
+ if (key_match("ListenPort"))
+ ret = parse_port(&ctx->device->listen_port, &ctx->device->flags, value);
+ else if (key_match("FwMark"))
+ ret = parse_fwmark(&ctx->device->fwmark, &ctx->device->flags, value);
+ else if (key_match("PrivateKey")) {
+ ret = parse_key(ctx->device->private_key, value);
+ if (ret)
+ ctx->device->flags |= WGDEVICE_HAS_PRIVATE_KEY;
+ } else
+ goto error;
+ } else if (ctx->is_peer_section) {
+ if (key_match("Endpoint"))
+ ret = parse_endpoint(&ctx->last_peer->endpoint.addr, value);
+ else if (key_match("PublicKey")) {
+ ret = parse_key(ctx->last_peer->public_key, value);
+ if (ret)
+ ctx->last_peer->flags |= WGPEER_HAS_PUBLIC_KEY;
+ } else if (key_match("AllowedIPs"))
+ ret = parse_allowedips(ctx->last_peer, &ctx->last_allowedip, value);
+ else if (key_match("PersistentKeepalive"))
+ ret = parse_persistent_keepalive(&ctx->last_peer->persistent_keepalive_interval, &ctx->last_peer->flags, value);
+ else if (key_match("PresharedKey")) {
+ ret = parse_key(ctx->last_peer->preshared_key, value);
+ if (ret)
+ ctx->last_peer->flags |= WGPEER_HAS_PRESHARED_KEY;
+ } else
+ goto error;
+ } else
+ goto error;
+ return ret;
+
+#undef key_match
+
+error:
+ fprintf(stderr, "Line unrecognized: `%s'\n", line);
+ return false;
+}
+
+bool config_read_line(struct config_ctx *ctx, const char *input)
+{
+ size_t len, cleaned_len = 0;
+ char *line, *comment;
+ bool ret = true;
+
+ /* This is what strchrnul is for, but that isn't portable. */
+ comment = strchr(input, COMMENT_CHAR);
+ if (comment)
+ len = comment - input;
+ else
+ len = strlen(input);
+
+ line = calloc(len + 1, sizeof(char));
+ if (!line) {
+ perror("calloc");
+ ret = false;
+ goto out;
+ }
+
+ for (size_t i = 0; i < len; ++i) {
+ if (!char_is_space(input[i]))
+ line[cleaned_len++] = input[i];
+ }
+ if (!cleaned_len)
+ goto out;
+ ret = process_line(ctx, line);
+out:
+ free(line);
+ if (!ret)
+ free_wgdevice(ctx->device);
+ return ret;
+}
+
+bool config_read_init(struct config_ctx *ctx, bool append)
+{
+ memset(ctx, 0, sizeof(*ctx));
+ ctx->device = calloc(1, sizeof(*ctx->device));
+ if (!ctx->device) {
+ perror("calloc");
+ return false;
+ }
+ if (!append)
+ ctx->device->flags |= WGDEVICE_REPLACE_PEERS | WGDEVICE_HAS_PRIVATE_KEY | WGDEVICE_HAS_FWMARK | WGDEVICE_HAS_LISTEN_PORT;
+ return true;
+}
+
+struct wgdevice *config_read_finish(struct config_ctx *ctx)
+{
+ struct wgpeer *peer;
+
+ for_each_wgpeer(ctx->device, peer) {
+ if (!(peer->flags & WGPEER_HAS_PUBLIC_KEY)) {
+ fprintf(stderr, "A peer is missing a public key\n");
+ goto err;
+ }
+ }
+ return ctx->device;
+err:
+ free_wgdevice(ctx->device);
+ return NULL;
+}
+
+static char *strip_spaces(const char *in)
+{
+ char *out;
+ size_t t, l, i;
+
+ t = strlen(in);
+ out = calloc(t + 1, sizeof(char));
+ if (!out) {
+ perror("calloc");
+ return NULL;
+ }
+ for (i = 0, l = 0; i < t; ++i) {
+ if (!char_is_space(in[i]))
+ out[l++] = in[i];
+ }
+ return out;
+}
+
+struct wgdevice *config_read_cmd(const char *argv[], int argc)
+{
+ struct wgdevice *device = calloc(1, sizeof(*device));
+ struct wgpeer *peer = NULL;
+ struct wgallowedip *allowedip = NULL;
+
+ if (!device) {
+ perror("calloc");
+ return false;
+ }
+ while (argc > 0) {
+ if (!strcmp(argv[0], "listen-port") && argc >= 2 && !peer) {
+ if (!parse_port(&device->listen_port, &device->flags, argv[1]))
+ goto error;
+ argv += 2;
+ argc -= 2;
+ } else if (!strcmp(argv[0], "fwmark") && argc >= 2 && !peer) {
+ if (!parse_fwmark(&device->fwmark, &device->flags, argv[1]))
+ goto error;
+ argv += 2;
+ argc -= 2;
+ } else if (!strcmp(argv[0], "private-key") && argc >= 2 && !peer) {
+ if (!parse_keyfile(device->private_key, argv[1]))
+ goto error;
+ device->flags |= WGDEVICE_HAS_PRIVATE_KEY;
+ argv += 2;
+ argc -= 2;
+ } else if (!strcmp(argv[0], "peer") && argc >= 2) {
+ struct wgpeer *new_peer = calloc(1, sizeof(*new_peer));
+
+ allowedip = NULL;
+ if (!new_peer) {
+ perror("calloc");
+ goto error;
+ }
+ if (peer)
+ peer->next_peer = new_peer;
+ else
+ device->first_peer = new_peer;
+ peer = new_peer;
+ if (!parse_key(peer->public_key, argv[1]))
+ goto error;
+ peer->flags |= WGPEER_HAS_PUBLIC_KEY;
+ argv += 2;
+ argc -= 2;
+ } else if (!strcmp(argv[0], "remove") && argc >= 1 && peer) {
+ peer->flags |= WGPEER_REMOVE_ME;
+ argv += 1;
+ argc -= 1;
+ } else if (!strcmp(argv[0], "endpoint") && argc >= 2 && peer) {
+ if (!parse_endpoint(&peer->endpoint.addr, argv[1]))
+ goto error;
+ argv += 2;
+ argc -= 2;
+ } else if (!strcmp(argv[0], "allowed-ips") && argc >= 2 && peer) {
+ char *line = strip_spaces(argv[1]);
+
+ if (!line)
+ goto error;
+ if (!parse_allowedips(peer, &allowedip, line)) {
+ free(line);
+ goto error;
+ }
+ free(line);
+ argv += 2;
+ argc -= 2;
+ } else if (!strcmp(argv[0], "persistent-keepalive") && argc >= 2 && peer) {
+ if (!parse_persistent_keepalive(&peer->persistent_keepalive_interval, &peer->flags, argv[1]))
+ goto error;
+ argv += 2;
+ argc -= 2;
+ } else if (!strcmp(argv[0], "preshared-key") && argc >= 2 && peer) {
+ if (!parse_keyfile(peer->preshared_key, argv[1]))
+ goto error;
+ peer->flags |= WGPEER_HAS_PRESHARED_KEY;
+ argv += 2;
+ argc -= 2;
+ } else {
+ fprintf(stderr, "Invalid argument: %s\n", argv[0]);
+ goto error;
+ }
+ }
+ return device;
+error:
+ free_wgdevice(device);
+ return false;
+}
diff --git a/contrib/wireguard-tools/src/containers.h b/contrib/wireguard-tools/src/containers.h
new file mode 100644
--- /dev/null
+++ b/contrib/wireguard-tools/src/containers.h
@@ -0,0 +1,107 @@
+/* SPDX-License-Identifier: GPL-2.0 OR MIT */
+/*
+ * Copyright (C) 2015-2020 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
+ */
+
+#ifndef CONTAINERS_H
+#define CONTAINERS_H
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <time.h>
+#include <sys/socket.h>
+#include <net/if.h>
+#include <netinet/in.h>
+#if defined(__linux__)
+#include <linux/wireguard.h>
+#elif defined(__OpenBSD__)
+#include <net/if_wg.h>
+#endif
+
+#ifndef WG_KEY_LEN
+#define WG_KEY_LEN 32
+#endif
+
+/* Cross platform __kernel_timespec */
+struct timespec64 {
+ int64_t tv_sec;
+ int64_t tv_nsec;
+};
+
+struct wgallowedip {
+ uint16_t family;
+ union {
+ struct in_addr ip4;
+ struct in6_addr ip6;
+ };
+ uint8_t cidr;
+ struct wgallowedip *next_allowedip;
+};
+
+enum {
+ WGPEER_REMOVE_ME = 1U << 0,
+ WGPEER_REPLACE_ALLOWEDIPS = 1U << 1,
+ WGPEER_HAS_PUBLIC_KEY = 1U << 2,
+ WGPEER_HAS_PRESHARED_KEY = 1U << 3,
+ WGPEER_HAS_PERSISTENT_KEEPALIVE_INTERVAL = 1U << 4
+};
+
+struct wgpeer {
+ uint32_t flags;
+
+ uint8_t public_key[WG_KEY_LEN];
+ uint8_t preshared_key[WG_KEY_LEN];
+
+ union {
+ struct sockaddr addr;
+ struct sockaddr_in addr4;
+ struct sockaddr_in6 addr6;
+ } endpoint;
+
+ struct timespec64 last_handshake_time;
+ uint64_t rx_bytes, tx_bytes;
+ uint16_t persistent_keepalive_interval;
+
+ struct wgallowedip *first_allowedip, *last_allowedip;
+ struct wgpeer *next_peer;
+};
+
+enum {
+ WGDEVICE_REPLACE_PEERS = 1U << 0,
+ WGDEVICE_HAS_PRIVATE_KEY = 1U << 1,
+ WGDEVICE_HAS_PUBLIC_KEY = 1U << 2,
+ WGDEVICE_HAS_LISTEN_PORT = 1U << 3,
+ WGDEVICE_HAS_FWMARK = 1U << 4
+};
+
+struct wgdevice {
+ char name[IFNAMSIZ];
+ uint32_t ifindex;
+
+ uint32_t flags;
+
+ uint8_t public_key[WG_KEY_LEN];
+ uint8_t private_key[WG_KEY_LEN];
+
+ uint32_t fwmark;
+ uint16_t listen_port;
+
+ struct wgpeer *first_peer, *last_peer;
+};
+
+#define for_each_wgpeer(__dev, __peer) for ((__peer) = (__dev)->first_peer; (__peer); (__peer) = (__peer)->next_peer)
+#define for_each_wgallowedip(__peer, __allowedip) for ((__allowedip) = (__peer)->first_allowedip; (__allowedip); (__allowedip) = (__allowedip)->next_allowedip)
+
+static inline void free_wgdevice(struct wgdevice *dev)
+{
+ if (!dev)
+ return;
+ for (struct wgpeer *peer = dev->first_peer, *np = peer ? peer->next_peer : NULL; peer; peer = np, np = peer ? peer->next_peer : NULL) {
+ for (struct wgallowedip *allowedip = peer->first_allowedip, *na = allowedip ? allowedip->next_allowedip : NULL; allowedip; allowedip = na, na = allowedip ? allowedip->next_allowedip : NULL)
+ free(allowedip);
+ free(peer);
+ }
+ free(dev);
+}
+
+#endif
diff --git a/contrib/wireguard-tools/src/ctype.h b/contrib/wireguard-tools/src/ctype.h
new file mode 100644
--- /dev/null
+++ b/contrib/wireguard-tools/src/ctype.h
@@ -0,0 +1,29 @@
+/* SPDX-License-Identifier: GPL-2.0 OR MIT */
+/*
+ * Copyright (C) 2015-2020 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
+ *
+ * Specialized constant-time ctype.h reimplementations that aren't locale-specific.
+ */
+
+#ifndef CTYPE_H
+#define CTYPE_H
+
+#include <stdbool.h>
+
+static inline bool char_is_space(int c)
+{
+ unsigned char d = c - 9;
+ return (0x80001FU >> (d & 31)) & (1U >> (d >> 5));
+}
+
+static inline bool char_is_digit(int c)
+{
+ return (unsigned int)(('0' - 1 - c) & (c - ('9' + 1))) >> (sizeof(c) * 8 - 1);
+}
+
+static inline bool char_is_alpha(int c)
+{
+ return (unsigned int)(('a' - 1 - (c | 32)) & ((c | 32) - ('z' + 1))) >> (sizeof(c) * 8 - 1);
+}
+
+#endif
diff --git a/contrib/wireguard-tools/src/curve25519-fiat32.h b/contrib/wireguard-tools/src/curve25519-fiat32.h
new file mode 100644
--- /dev/null
+++ b/contrib/wireguard-tools/src/curve25519-fiat32.h
@@ -0,0 +1,860 @@
+// SPDX-License-Identifier: GPL-2.0 OR MIT
+/*
+ * Copyright (C) 2015-2016 The fiat-crypto Authors.
+ * Copyright (C) 2018-2020 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
+ *
+ * This is a machine-generated formally verified implementation of Curve25519
+ * ECDH from: <https://github.com/mit-plv/fiat-crypto>. Though originally
+ * machine generated, it has been tweaked to be suitable for use in the kernel.
+ * It is optimized for 32-bit machines and machines that cannot work efficiently
+ * with 128-bit integer types.
+ */
+
+/* fe means field element. Here the field is \Z/(2^255-19). An element t,
+ * entries t[0]...t[9], represents the integer t[0]+2^26 t[1]+2^51 t[2]+2^77
+ * t[3]+2^102 t[4]+...+2^230 t[9].
+ * fe limbs are bounded by 1.125*2^26,1.125*2^25,1.125*2^26,1.125*2^25,etc.
+ * Multiplication and carrying produce fe from fe_loose.
+ */
+typedef struct fe { u32 v[10]; } fe;
+
+/* fe_loose limbs are bounded by 3.375*2^26,3.375*2^25,3.375*2^26,3.375*2^25,etc
+ * Addition and subtraction produce fe_loose from (fe, fe).
+ */
+typedef struct fe_loose { u32 v[10]; } fe_loose;
+
+static __always_inline void fe_frombytes_impl(u32 h[10], const u8 *s)
+{
+ /* Ignores top bit of s. */
+ u32 a0 = get_unaligned_le32(s);
+ u32 a1 = get_unaligned_le32(s+4);
+ u32 a2 = get_unaligned_le32(s+8);
+ u32 a3 = get_unaligned_le32(s+12);
+ u32 a4 = get_unaligned_le32(s+16);
+ u32 a5 = get_unaligned_le32(s+20);
+ u32 a6 = get_unaligned_le32(s+24);
+ u32 a7 = get_unaligned_le32(s+28);
+ h[0] = a0&((1<<26)-1); /* 26 used, 32-26 left. 26 */
+ h[1] = (a0>>26) | ((a1&((1<<19)-1))<< 6); /* (32-26) + 19 = 6+19 = 25 */
+ h[2] = (a1>>19) | ((a2&((1<<13)-1))<<13); /* (32-19) + 13 = 13+13 = 26 */
+ h[3] = (a2>>13) | ((a3&((1<< 6)-1))<<19); /* (32-13) + 6 = 19+ 6 = 25 */
+ h[4] = (a3>> 6); /* (32- 6) = 26 */
+ h[5] = a4&((1<<25)-1); /* 25 */
+ h[6] = (a4>>25) | ((a5&((1<<19)-1))<< 7); /* (32-25) + 19 = 7+19 = 26 */
+ h[7] = (a5>>19) | ((a6&((1<<12)-1))<<13); /* (32-19) + 12 = 13+12 = 25 */
+ h[8] = (a6>>12) | ((a7&((1<< 6)-1))<<20); /* (32-12) + 6 = 20+ 6 = 26 */
+ h[9] = (a7>> 6)&((1<<25)-1); /* 25 */
+}
+
+static __always_inline void fe_frombytes(fe *h, const u8 *s)
+{
+ fe_frombytes_impl(h->v, s);
+}
+
+static __always_inline u8 /*bool*/
+addcarryx_u25(u8 /*bool*/ c, u32 a, u32 b, u32 *low)
+{
+ /* This function extracts 25 bits of result and 1 bit of carry
+ * (26 total), so a 32-bit intermediate is sufficient.
+ */
+ u32 x = a + b + c;
+ *low = x & ((1 << 25) - 1);
+ return (x >> 25) & 1;
+}
+
+static __always_inline u8 /*bool*/
+addcarryx_u26(u8 /*bool*/ c, u32 a, u32 b, u32 *low)
+{
+ /* This function extracts 26 bits of result and 1 bit of carry
+ * (27 total), so a 32-bit intermediate is sufficient.
+ */
+ u32 x = a + b + c;
+ *low = x & ((1 << 26) - 1);
+ return (x >> 26) & 1;
+}
+
+static __always_inline u8 /*bool*/
+subborrow_u25(u8 /*bool*/ c, u32 a, u32 b, u32 *low)
+{
+ /* This function extracts 25 bits of result and 1 bit of borrow
+ * (26 total), so a 32-bit intermediate is sufficient.
+ */
+ u32 x = a - b - c;
+ *low = x & ((1 << 25) - 1);
+ return x >> 31;
+}
+
+static __always_inline u8 /*bool*/
+subborrow_u26(u8 /*bool*/ c, u32 a, u32 b, u32 *low)
+{
+ /* This function extracts 26 bits of result and 1 bit of borrow
+ *(27 total), so a 32-bit intermediate is sufficient.
+ */
+ u32 x = a - b - c;
+ *low = x & ((1 << 26) - 1);
+ return x >> 31;
+}
+
+static __always_inline u32 cmovznz32(u32 t, u32 z, u32 nz)
+{
+ t = -!!t; /* all set if nonzero, 0 if 0 */
+ return (t&nz) | ((~t)&z);
+}
+
+static __always_inline void fe_freeze(u32 out[10], const u32 in1[10])
+{
+ { const u32 x17 = in1[9];
+ { const u32 x18 = in1[8];
+ { const u32 x16 = in1[7];
+ { const u32 x14 = in1[6];
+ { const u32 x12 = in1[5];
+ { const u32 x10 = in1[4];
+ { const u32 x8 = in1[3];
+ { const u32 x6 = in1[2];
+ { const u32 x4 = in1[1];
+ { const u32 x2 = in1[0];
+ { u32 x20; u8/*bool*/ x21 = subborrow_u26(0x0, x2, 0x3ffffed, &x20);
+ { u32 x23; u8/*bool*/ x24 = subborrow_u25(x21, x4, 0x1ffffff, &x23);
+ { u32 x26; u8/*bool*/ x27 = subborrow_u26(x24, x6, 0x3ffffff, &x26);
+ { u32 x29; u8/*bool*/ x30 = subborrow_u25(x27, x8, 0x1ffffff, &x29);
+ { u32 x32; u8/*bool*/ x33 = subborrow_u26(x30, x10, 0x3ffffff, &x32);
+ { u32 x35; u8/*bool*/ x36 = subborrow_u25(x33, x12, 0x1ffffff, &x35);
+ { u32 x38; u8/*bool*/ x39 = subborrow_u26(x36, x14, 0x3ffffff, &x38);
+ { u32 x41; u8/*bool*/ x42 = subborrow_u25(x39, x16, 0x1ffffff, &x41);
+ { u32 x44; u8/*bool*/ x45 = subborrow_u26(x42, x18, 0x3ffffff, &x44);
+ { u32 x47; u8/*bool*/ x48 = subborrow_u25(x45, x17, 0x1ffffff, &x47);
+ { u32 x49 = cmovznz32(x48, 0x0, 0xffffffff);
+ { u32 x50 = (x49 & 0x3ffffed);
+ { u32 x52; u8/*bool*/ x53 = addcarryx_u26(0x0, x20, x50, &x52);
+ { u32 x54 = (x49 & 0x1ffffff);
+ { u32 x56; u8/*bool*/ x57 = addcarryx_u25(x53, x23, x54, &x56);
+ { u32 x58 = (x49 & 0x3ffffff);
+ { u32 x60; u8/*bool*/ x61 = addcarryx_u26(x57, x26, x58, &x60);
+ { u32 x62 = (x49 & 0x1ffffff);
+ { u32 x64; u8/*bool*/ x65 = addcarryx_u25(x61, x29, x62, &x64);
+ { u32 x66 = (x49 & 0x3ffffff);
+ { u32 x68; u8/*bool*/ x69 = addcarryx_u26(x65, x32, x66, &x68);
+ { u32 x70 = (x49 & 0x1ffffff);
+ { u32 x72; u8/*bool*/ x73 = addcarryx_u25(x69, x35, x70, &x72);
+ { u32 x74 = (x49 & 0x3ffffff);
+ { u32 x76; u8/*bool*/ x77 = addcarryx_u26(x73, x38, x74, &x76);
+ { u32 x78 = (x49 & 0x1ffffff);
+ { u32 x80; u8/*bool*/ x81 = addcarryx_u25(x77, x41, x78, &x80);
+ { u32 x82 = (x49 & 0x3ffffff);
+ { u32 x84; u8/*bool*/ x85 = addcarryx_u26(x81, x44, x82, &x84);
+ { u32 x86 = (x49 & 0x1ffffff);
+ { u32 x88; addcarryx_u25(x85, x47, x86, &x88);
+ out[0] = x52;
+ out[1] = x56;
+ out[2] = x60;
+ out[3] = x64;
+ out[4] = x68;
+ out[5] = x72;
+ out[6] = x76;
+ out[7] = x80;
+ out[8] = x84;
+ out[9] = x88;
+ }}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}
+}
+
+static __always_inline void fe_tobytes(u8 s[32], const fe *f)
+{
+ u32 h[10];
+ fe_freeze(h, f->v);
+ s[0] = h[0] >> 0;
+ s[1] = h[0] >> 8;
+ s[2] = h[0] >> 16;
+ s[3] = (h[0] >> 24) | (h[1] << 2);
+ s[4] = h[1] >> 6;
+ s[5] = h[1] >> 14;
+ s[6] = (h[1] >> 22) | (h[2] << 3);
+ s[7] = h[2] >> 5;
+ s[8] = h[2] >> 13;
+ s[9] = (h[2] >> 21) | (h[3] << 5);
+ s[10] = h[3] >> 3;
+ s[11] = h[3] >> 11;
+ s[12] = (h[3] >> 19) | (h[4] << 6);
+ s[13] = h[4] >> 2;
+ s[14] = h[4] >> 10;
+ s[15] = h[4] >> 18;
+ s[16] = h[5] >> 0;
+ s[17] = h[5] >> 8;
+ s[18] = h[5] >> 16;
+ s[19] = (h[5] >> 24) | (h[6] << 1);
+ s[20] = h[6] >> 7;
+ s[21] = h[6] >> 15;
+ s[22] = (h[6] >> 23) | (h[7] << 3);
+ s[23] = h[7] >> 5;
+ s[24] = h[7] >> 13;
+ s[25] = (h[7] >> 21) | (h[8] << 4);
+ s[26] = h[8] >> 4;
+ s[27] = h[8] >> 12;
+ s[28] = (h[8] >> 20) | (h[9] << 6);
+ s[29] = h[9] >> 2;
+ s[30] = h[9] >> 10;
+ s[31] = h[9] >> 18;
+}
+
+/* h = f */
+static __always_inline void fe_copy(fe *h, const fe *f)
+{
+ memmove(h, f, sizeof(u32) * 10);
+}
+
+static __always_inline void fe_copy_lt(fe_loose *h, const fe *f)
+{
+ memmove(h, f, sizeof(u32) * 10);
+}
+
+/* h = 0 */
+static __always_inline void fe_0(fe *h)
+{
+ memset(h, 0, sizeof(u32) * 10);
+}
+
+/* h = 1 */
+static __always_inline void fe_1(fe *h)
+{
+ memset(h, 0, sizeof(u32) * 10);
+ h->v[0] = 1;
+}
+
+static void fe_add_impl(u32 out[10], const u32 in1[10], const u32 in2[10])
+{
+ { const u32 x20 = in1[9];
+ { const u32 x21 = in1[8];
+ { const u32 x19 = in1[7];
+ { const u32 x17 = in1[6];
+ { const u32 x15 = in1[5];
+ { const u32 x13 = in1[4];
+ { const u32 x11 = in1[3];
+ { const u32 x9 = in1[2];
+ { const u32 x7 = in1[1];
+ { const u32 x5 = in1[0];
+ { const u32 x38 = in2[9];
+ { const u32 x39 = in2[8];
+ { const u32 x37 = in2[7];
+ { const u32 x35 = in2[6];
+ { const u32 x33 = in2[5];
+ { const u32 x31 = in2[4];
+ { const u32 x29 = in2[3];
+ { const u32 x27 = in2[2];
+ { const u32 x25 = in2[1];
+ { const u32 x23 = in2[0];
+ out[0] = (x5 + x23);
+ out[1] = (x7 + x25);
+ out[2] = (x9 + x27);
+ out[3] = (x11 + x29);
+ out[4] = (x13 + x31);
+ out[5] = (x15 + x33);
+ out[6] = (x17 + x35);
+ out[7] = (x19 + x37);
+ out[8] = (x21 + x39);
+ out[9] = (x20 + x38);
+ }}}}}}}}}}}}}}}}}}}}
+}
+
+/* h = f + g
+ * Can overlap h with f or g.
+ */
+static __always_inline void fe_add(fe_loose *h, const fe *f, const fe *g)
+{
+ fe_add_impl(h->v, f->v, g->v);
+}
+
+static void fe_sub_impl(u32 out[10], const u32 in1[10], const u32 in2[10])
+{
+ { const u32 x20 = in1[9];
+ { const u32 x21 = in1[8];
+ { const u32 x19 = in1[7];
+ { const u32 x17 = in1[6];
+ { const u32 x15 = in1[5];
+ { const u32 x13 = in1[4];
+ { const u32 x11 = in1[3];
+ { const u32 x9 = in1[2];
+ { const u32 x7 = in1[1];
+ { const u32 x5 = in1[0];
+ { const u32 x38 = in2[9];
+ { const u32 x39 = in2[8];
+ { const u32 x37 = in2[7];
+ { const u32 x35 = in2[6];
+ { const u32 x33 = in2[5];
+ { const u32 x31 = in2[4];
+ { const u32 x29 = in2[3];
+ { const u32 x27 = in2[2];
+ { const u32 x25 = in2[1];
+ { const u32 x23 = in2[0];
+ out[0] = ((0x7ffffda + x5) - x23);
+ out[1] = ((0x3fffffe + x7) - x25);
+ out[2] = ((0x7fffffe + x9) - x27);
+ out[3] = ((0x3fffffe + x11) - x29);
+ out[4] = ((0x7fffffe + x13) - x31);
+ out[5] = ((0x3fffffe + x15) - x33);
+ out[6] = ((0x7fffffe + x17) - x35);
+ out[7] = ((0x3fffffe + x19) - x37);
+ out[8] = ((0x7fffffe + x21) - x39);
+ out[9] = ((0x3fffffe + x20) - x38);
+ }}}}}}}}}}}}}}}}}}}}
+}
+
+/* h = f - g
+ * Can overlap h with f or g.
+ */
+static __always_inline void fe_sub(fe_loose *h, const fe *f, const fe *g)
+{
+ fe_sub_impl(h->v, f->v, g->v);
+}
+
+static void fe_mul_impl(u32 out[10], const u32 in1[10], const u32 in2[10])
+{
+ { const u32 x20 = in1[9];
+ { const u32 x21 = in1[8];
+ { const u32 x19 = in1[7];
+ { const u32 x17 = in1[6];
+ { const u32 x15 = in1[5];
+ { const u32 x13 = in1[4];
+ { const u32 x11 = in1[3];
+ { const u32 x9 = in1[2];
+ { const u32 x7 = in1[1];
+ { const u32 x5 = in1[0];
+ { const u32 x38 = in2[9];
+ { const u32 x39 = in2[8];
+ { const u32 x37 = in2[7];
+ { const u32 x35 = in2[6];
+ { const u32 x33 = in2[5];
+ { const u32 x31 = in2[4];
+ { const u32 x29 = in2[3];
+ { const u32 x27 = in2[2];
+ { const u32 x25 = in2[1];
+ { const u32 x23 = in2[0];
+ { u64 x40 = ((u64)x23 * x5);
+ { u64 x41 = (((u64)x23 * x7) + ((u64)x25 * x5));
+ { u64 x42 = ((((u64)(0x2 * x25) * x7) + ((u64)x23 * x9)) + ((u64)x27 * x5));
+ { u64 x43 = (((((u64)x25 * x9) + ((u64)x27 * x7)) + ((u64)x23 * x11)) + ((u64)x29 * x5));
+ { u64 x44 = (((((u64)x27 * x9) + (0x2 * (((u64)x25 * x11) + ((u64)x29 * x7)))) + ((u64)x23 * x13)) + ((u64)x31 * x5));
+ { u64 x45 = (((((((u64)x27 * x11) + ((u64)x29 * x9)) + ((u64)x25 * x13)) + ((u64)x31 * x7)) + ((u64)x23 * x15)) + ((u64)x33 * x5));
+ { u64 x46 = (((((0x2 * ((((u64)x29 * x11) + ((u64)x25 * x15)) + ((u64)x33 * x7))) + ((u64)x27 * x13)) + ((u64)x31 * x9)) + ((u64)x23 * x17)) + ((u64)x35 * x5));
+ { u64 x47 = (((((((((u64)x29 * x13) + ((u64)x31 * x11)) + ((u64)x27 * x15)) + ((u64)x33 * x9)) + ((u64)x25 * x17)) + ((u64)x35 * x7)) + ((u64)x23 * x19)) + ((u64)x37 * x5));
+ { u64 x48 = (((((((u64)x31 * x13) + (0x2 * (((((u64)x29 * x15) + ((u64)x33 * x11)) + ((u64)x25 * x19)) + ((u64)x37 * x7)))) + ((u64)x27 * x17)) + ((u64)x35 * x9)) + ((u64)x23 * x21)) + ((u64)x39 * x5));
+ { u64 x49 = (((((((((((u64)x31 * x15) + ((u64)x33 * x13)) + ((u64)x29 * x17)) + ((u64)x35 * x11)) + ((u64)x27 * x19)) + ((u64)x37 * x9)) + ((u64)x25 * x21)) + ((u64)x39 * x7)) + ((u64)x23 * x20)) + ((u64)x38 * x5));
+ { u64 x50 = (((((0x2 * ((((((u64)x33 * x15) + ((u64)x29 * x19)) + ((u64)x37 * x11)) + ((u64)x25 * x20)) + ((u64)x38 * x7))) + ((u64)x31 * x17)) + ((u64)x35 * x13)) + ((u64)x27 * x21)) + ((u64)x39 * x9));
+ { u64 x51 = (((((((((u64)x33 * x17) + ((u64)x35 * x15)) + ((u64)x31 * x19)) + ((u64)x37 * x13)) + ((u64)x29 * x21)) + ((u64)x39 * x11)) + ((u64)x27 * x20)) + ((u64)x38 * x9));
+ { u64 x52 = (((((u64)x35 * x17) + (0x2 * (((((u64)x33 * x19) + ((u64)x37 * x15)) + ((u64)x29 * x20)) + ((u64)x38 * x11)))) + ((u64)x31 * x21)) + ((u64)x39 * x13));
+ { u64 x53 = (((((((u64)x35 * x19) + ((u64)x37 * x17)) + ((u64)x33 * x21)) + ((u64)x39 * x15)) + ((u64)x31 * x20)) + ((u64)x38 * x13));
+ { u64 x54 = (((0x2 * ((((u64)x37 * x19) + ((u64)x33 * x20)) + ((u64)x38 * x15))) + ((u64)x35 * x21)) + ((u64)x39 * x17));
+ { u64 x55 = (((((u64)x37 * x21) + ((u64)x39 * x19)) + ((u64)x35 * x20)) + ((u64)x38 * x17));
+ { u64 x56 = (((u64)x39 * x21) + (0x2 * (((u64)x37 * x20) + ((u64)x38 * x19))));
+ { u64 x57 = (((u64)x39 * x20) + ((u64)x38 * x21));
+ { u64 x58 = ((u64)(0x2 * x38) * x20);
+ { u64 x59 = (x48 + (x58 << 0x4));
+ { u64 x60 = (x59 + (x58 << 0x1));
+ { u64 x61 = (x60 + x58);
+ { u64 x62 = (x47 + (x57 << 0x4));
+ { u64 x63 = (x62 + (x57 << 0x1));
+ { u64 x64 = (x63 + x57);
+ { u64 x65 = (x46 + (x56 << 0x4));
+ { u64 x66 = (x65 + (x56 << 0x1));
+ { u64 x67 = (x66 + x56);
+ { u64 x68 = (x45 + (x55 << 0x4));
+ { u64 x69 = (x68 + (x55 << 0x1));
+ { u64 x70 = (x69 + x55);
+ { u64 x71 = (x44 + (x54 << 0x4));
+ { u64 x72 = (x71 + (x54 << 0x1));
+ { u64 x73 = (x72 + x54);
+ { u64 x74 = (x43 + (x53 << 0x4));
+ { u64 x75 = (x74 + (x53 << 0x1));
+ { u64 x76 = (x75 + x53);
+ { u64 x77 = (x42 + (x52 << 0x4));
+ { u64 x78 = (x77 + (x52 << 0x1));
+ { u64 x79 = (x78 + x52);
+ { u64 x80 = (x41 + (x51 << 0x4));
+ { u64 x81 = (x80 + (x51 << 0x1));
+ { u64 x82 = (x81 + x51);
+ { u64 x83 = (x40 + (x50 << 0x4));
+ { u64 x84 = (x83 + (x50 << 0x1));
+ { u64 x85 = (x84 + x50);
+ { u64 x86 = (x85 >> 0x1a);
+ { u32 x87 = ((u32)x85 & 0x3ffffff);
+ { u64 x88 = (x86 + x82);
+ { u64 x89 = (x88 >> 0x19);
+ { u32 x90 = ((u32)x88 & 0x1ffffff);
+ { u64 x91 = (x89 + x79);
+ { u64 x92 = (x91 >> 0x1a);
+ { u32 x93 = ((u32)x91 & 0x3ffffff);
+ { u64 x94 = (x92 + x76);
+ { u64 x95 = (x94 >> 0x19);
+ { u32 x96 = ((u32)x94 & 0x1ffffff);
+ { u64 x97 = (x95 + x73);
+ { u64 x98 = (x97 >> 0x1a);
+ { u32 x99 = ((u32)x97 & 0x3ffffff);
+ { u64 x100 = (x98 + x70);
+ { u64 x101 = (x100 >> 0x19);
+ { u32 x102 = ((u32)x100 & 0x1ffffff);
+ { u64 x103 = (x101 + x67);
+ { u64 x104 = (x103 >> 0x1a);
+ { u32 x105 = ((u32)x103 & 0x3ffffff);
+ { u64 x106 = (x104 + x64);
+ { u64 x107 = (x106 >> 0x19);
+ { u32 x108 = ((u32)x106 & 0x1ffffff);
+ { u64 x109 = (x107 + x61);
+ { u64 x110 = (x109 >> 0x1a);
+ { u32 x111 = ((u32)x109 & 0x3ffffff);
+ { u64 x112 = (x110 + x49);
+ { u64 x113 = (x112 >> 0x19);
+ { u32 x114 = ((u32)x112 & 0x1ffffff);
+ { u64 x115 = (x87 + (0x13 * x113));
+ { u32 x116 = (u32) (x115 >> 0x1a);
+ { u32 x117 = ((u32)x115 & 0x3ffffff);
+ { u32 x118 = (x116 + x90);
+ { u32 x119 = (x118 >> 0x19);
+ { u32 x120 = (x118 & 0x1ffffff);
+ out[0] = x117;
+ out[1] = x120;
+ out[2] = (x119 + x93);
+ out[3] = x96;
+ out[4] = x99;
+ out[5] = x102;
+ out[6] = x105;
+ out[7] = x108;
+ out[8] = x111;
+ out[9] = x114;
+ }}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}
+}
+
+static __always_inline void fe_mul_ttt(fe *h, const fe *f, const fe *g)
+{
+ fe_mul_impl(h->v, f->v, g->v);
+}
+
+static __always_inline void fe_mul_tlt(fe *h, const fe_loose *f, const fe *g)
+{
+ fe_mul_impl(h->v, f->v, g->v);
+}
+
+static __always_inline void
+fe_mul_tll(fe *h, const fe_loose *f, const fe_loose *g)
+{
+ fe_mul_impl(h->v, f->v, g->v);
+}
+
+static void fe_sqr_impl(u32 out[10], const u32 in1[10])
+{
+ { const u32 x17 = in1[9];
+ { const u32 x18 = in1[8];
+ { const u32 x16 = in1[7];
+ { const u32 x14 = in1[6];
+ { const u32 x12 = in1[5];
+ { const u32 x10 = in1[4];
+ { const u32 x8 = in1[3];
+ { const u32 x6 = in1[2];
+ { const u32 x4 = in1[1];
+ { const u32 x2 = in1[0];
+ { u64 x19 = ((u64)x2 * x2);
+ { u64 x20 = ((u64)(0x2 * x2) * x4);
+ { u64 x21 = (0x2 * (((u64)x4 * x4) + ((u64)x2 * x6)));
+ { u64 x22 = (0x2 * (((u64)x4 * x6) + ((u64)x2 * x8)));
+ { u64 x23 = ((((u64)x6 * x6) + ((u64)(0x4 * x4) * x8)) + ((u64)(0x2 * x2) * x10));
+ { u64 x24 = (0x2 * ((((u64)x6 * x8) + ((u64)x4 * x10)) + ((u64)x2 * x12)));
+ { u64 x25 = (0x2 * (((((u64)x8 * x8) + ((u64)x6 * x10)) + ((u64)x2 * x14)) + ((u64)(0x2 * x4) * x12)));
+ { u64 x26 = (0x2 * (((((u64)x8 * x10) + ((u64)x6 * x12)) + ((u64)x4 * x14)) + ((u64)x2 * x16)));
+ { u64 x27 = (((u64)x10 * x10) + (0x2 * ((((u64)x6 * x14) + ((u64)x2 * x18)) + (0x2 * (((u64)x4 * x16) + ((u64)x8 * x12))))));
+ { u64 x28 = (0x2 * ((((((u64)x10 * x12) + ((u64)x8 * x14)) + ((u64)x6 * x16)) + ((u64)x4 * x18)) + ((u64)x2 * x17)));
+ { u64 x29 = (0x2 * (((((u64)x12 * x12) + ((u64)x10 * x14)) + ((u64)x6 * x18)) + (0x2 * (((u64)x8 * x16) + ((u64)x4 * x17)))));
+ { u64 x30 = (0x2 * (((((u64)x12 * x14) + ((u64)x10 * x16)) + ((u64)x8 * x18)) + ((u64)x6 * x17)));
+ { u64 x31 = (((u64)x14 * x14) + (0x2 * (((u64)x10 * x18) + (0x2 * (((u64)x12 * x16) + ((u64)x8 * x17))))));
+ { u64 x32 = (0x2 * ((((u64)x14 * x16) + ((u64)x12 * x18)) + ((u64)x10 * x17)));
+ { u64 x33 = (0x2 * ((((u64)x16 * x16) + ((u64)x14 * x18)) + ((u64)(0x2 * x12) * x17)));
+ { u64 x34 = (0x2 * (((u64)x16 * x18) + ((u64)x14 * x17)));
+ { u64 x35 = (((u64)x18 * x18) + ((u64)(0x4 * x16) * x17));
+ { u64 x36 = ((u64)(0x2 * x18) * x17);
+ { u64 x37 = ((u64)(0x2 * x17) * x17);
+ { u64 x38 = (x27 + (x37 << 0x4));
+ { u64 x39 = (x38 + (x37 << 0x1));
+ { u64 x40 = (x39 + x37);
+ { u64 x41 = (x26 + (x36 << 0x4));
+ { u64 x42 = (x41 + (x36 << 0x1));
+ { u64 x43 = (x42 + x36);
+ { u64 x44 = (x25 + (x35 << 0x4));
+ { u64 x45 = (x44 + (x35 << 0x1));
+ { u64 x46 = (x45 + x35);
+ { u64 x47 = (x24 + (x34 << 0x4));
+ { u64 x48 = (x47 + (x34 << 0x1));
+ { u64 x49 = (x48 + x34);
+ { u64 x50 = (x23 + (x33 << 0x4));
+ { u64 x51 = (x50 + (x33 << 0x1));
+ { u64 x52 = (x51 + x33);
+ { u64 x53 = (x22 + (x32 << 0x4));
+ { u64 x54 = (x53 + (x32 << 0x1));
+ { u64 x55 = (x54 + x32);
+ { u64 x56 = (x21 + (x31 << 0x4));
+ { u64 x57 = (x56 + (x31 << 0x1));
+ { u64 x58 = (x57 + x31);
+ { u64 x59 = (x20 + (x30 << 0x4));
+ { u64 x60 = (x59 + (x30 << 0x1));
+ { u64 x61 = (x60 + x30);
+ { u64 x62 = (x19 + (x29 << 0x4));
+ { u64 x63 = (x62 + (x29 << 0x1));
+ { u64 x64 = (x63 + x29);
+ { u64 x65 = (x64 >> 0x1a);
+ { u32 x66 = ((u32)x64 & 0x3ffffff);
+ { u64 x67 = (x65 + x61);
+ { u64 x68 = (x67 >> 0x19);
+ { u32 x69 = ((u32)x67 & 0x1ffffff);
+ { u64 x70 = (x68 + x58);
+ { u64 x71 = (x70 >> 0x1a);
+ { u32 x72 = ((u32)x70 & 0x3ffffff);
+ { u64 x73 = (x71 + x55);
+ { u64 x74 = (x73 >> 0x19);
+ { u32 x75 = ((u32)x73 & 0x1ffffff);
+ { u64 x76 = (x74 + x52);
+ { u64 x77 = (x76 >> 0x1a);
+ { u32 x78 = ((u32)x76 & 0x3ffffff);
+ { u64 x79 = (x77 + x49);
+ { u64 x80 = (x79 >> 0x19);
+ { u32 x81 = ((u32)x79 & 0x1ffffff);
+ { u64 x82 = (x80 + x46);
+ { u64 x83 = (x82 >> 0x1a);
+ { u32 x84 = ((u32)x82 & 0x3ffffff);
+ { u64 x85 = (x83 + x43);
+ { u64 x86 = (x85 >> 0x19);
+ { u32 x87 = ((u32)x85 & 0x1ffffff);
+ { u64 x88 = (x86 + x40);
+ { u64 x89 = (x88 >> 0x1a);
+ { u32 x90 = ((u32)x88 & 0x3ffffff);
+ { u64 x91 = (x89 + x28);
+ { u64 x92 = (x91 >> 0x19);
+ { u32 x93 = ((u32)x91 & 0x1ffffff);
+ { u64 x94 = (x66 + (0x13 * x92));
+ { u32 x95 = (u32) (x94 >> 0x1a);
+ { u32 x96 = ((u32)x94 & 0x3ffffff);
+ { u32 x97 = (x95 + x69);
+ { u32 x98 = (x97 >> 0x19);
+ { u32 x99 = (x97 & 0x1ffffff);
+ out[0] = x96;
+ out[1] = x99;
+ out[2] = (x98 + x72);
+ out[3] = x75;
+ out[4] = x78;
+ out[5] = x81;
+ out[6] = x84;
+ out[7] = x87;
+ out[8] = x90;
+ out[9] = x93;
+ }}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}
+}
+
+static __always_inline void fe_sq_tl(fe *h, const fe_loose *f)
+{
+ fe_sqr_impl(h->v, f->v);
+}
+
+static __always_inline void fe_sq_tt(fe *h, const fe *f)
+{
+ fe_sqr_impl(h->v, f->v);
+}
+
+static __always_inline void fe_loose_invert(fe *out, const fe_loose *z)
+{
+ fe t0;
+ fe t1;
+ fe t2;
+ fe t3;
+ int i;
+
+ fe_sq_tl(&t0, z);
+ fe_sq_tt(&t1, &t0);
+ for (i = 1; i < 2; ++i)
+ fe_sq_tt(&t1, &t1);
+ fe_mul_tlt(&t1, z, &t1);
+ fe_mul_ttt(&t0, &t0, &t1);
+ fe_sq_tt(&t2, &t0);
+ fe_mul_ttt(&t1, &t1, &t2);
+ fe_sq_tt(&t2, &t1);
+ for (i = 1; i < 5; ++i)
+ fe_sq_tt(&t2, &t2);
+ fe_mul_ttt(&t1, &t2, &t1);
+ fe_sq_tt(&t2, &t1);
+ for (i = 1; i < 10; ++i)
+ fe_sq_tt(&t2, &t2);
+ fe_mul_ttt(&t2, &t2, &t1);
+ fe_sq_tt(&t3, &t2);
+ for (i = 1; i < 20; ++i)
+ fe_sq_tt(&t3, &t3);
+ fe_mul_ttt(&t2, &t3, &t2);
+ fe_sq_tt(&t2, &t2);
+ for (i = 1; i < 10; ++i)
+ fe_sq_tt(&t2, &t2);
+ fe_mul_ttt(&t1, &t2, &t1);
+ fe_sq_tt(&t2, &t1);
+ for (i = 1; i < 50; ++i)
+ fe_sq_tt(&t2, &t2);
+ fe_mul_ttt(&t2, &t2, &t1);
+ fe_sq_tt(&t3, &t2);
+ for (i = 1; i < 100; ++i)
+ fe_sq_tt(&t3, &t3);
+ fe_mul_ttt(&t2, &t3, &t2);
+ fe_sq_tt(&t2, &t2);
+ for (i = 1; i < 50; ++i)
+ fe_sq_tt(&t2, &t2);
+ fe_mul_ttt(&t1, &t2, &t1);
+ fe_sq_tt(&t1, &t1);
+ for (i = 1; i < 5; ++i)
+ fe_sq_tt(&t1, &t1);
+ fe_mul_ttt(out, &t1, &t0);
+}
+
+static __always_inline void fe_invert(fe *out, const fe *z)
+{
+ fe_loose l;
+ fe_copy_lt(&l, z);
+ fe_loose_invert(out, &l);
+}
+
+/* Replace (f,g) with (g,f) if b == 1;
+ * replace (f,g) with (f,g) if b == 0.
+ *
+ * Preconditions: b in {0,1}
+ */
+static __always_inline void fe_cswap(fe *f, fe *g, unsigned int b)
+{
+ unsigned i;
+ b = 0 - b;
+ for (i = 0; i < 10; i++) {
+ u32 x = f->v[i] ^ g->v[i];
+ x &= b;
+ f->v[i] ^= x;
+ g->v[i] ^= x;
+ }
+}
+
+/* NOTE: based on fiat-crypto fe_mul, edited for in2=121666, 0, 0.*/
+static __always_inline void fe_mul_121666_impl(u32 out[10], const u32 in1[10])
+{
+ { const u32 x20 = in1[9];
+ { const u32 x21 = in1[8];
+ { const u32 x19 = in1[7];
+ { const u32 x17 = in1[6];
+ { const u32 x15 = in1[5];
+ { const u32 x13 = in1[4];
+ { const u32 x11 = in1[3];
+ { const u32 x9 = in1[2];
+ { const u32 x7 = in1[1];
+ { const u32 x5 = in1[0];
+ { const u32 x38 = 0;
+ { const u32 x39 = 0;
+ { const u32 x37 = 0;
+ { const u32 x35 = 0;
+ { const u32 x33 = 0;
+ { const u32 x31 = 0;
+ { const u32 x29 = 0;
+ { const u32 x27 = 0;
+ { const u32 x25 = 0;
+ { const u32 x23 = 121666;
+ { u64 x40 = ((u64)x23 * x5);
+ { u64 x41 = (((u64)x23 * x7) + ((u64)x25 * x5));
+ { u64 x42 = ((((u64)(0x2 * x25) * x7) + ((u64)x23 * x9)) + ((u64)x27 * x5));
+ { u64 x43 = (((((u64)x25 * x9) + ((u64)x27 * x7)) + ((u64)x23 * x11)) + ((u64)x29 * x5));
+ { u64 x44 = (((((u64)x27 * x9) + (0x2 * (((u64)x25 * x11) + ((u64)x29 * x7)))) + ((u64)x23 * x13)) + ((u64)x31 * x5));
+ { u64 x45 = (((((((u64)x27 * x11) + ((u64)x29 * x9)) + ((u64)x25 * x13)) + ((u64)x31 * x7)) + ((u64)x23 * x15)) + ((u64)x33 * x5));
+ { u64 x46 = (((((0x2 * ((((u64)x29 * x11) + ((u64)x25 * x15)) + ((u64)x33 * x7))) + ((u64)x27 * x13)) + ((u64)x31 * x9)) + ((u64)x23 * x17)) + ((u64)x35 * x5));
+ { u64 x47 = (((((((((u64)x29 * x13) + ((u64)x31 * x11)) + ((u64)x27 * x15)) + ((u64)x33 * x9)) + ((u64)x25 * x17)) + ((u64)x35 * x7)) + ((u64)x23 * x19)) + ((u64)x37 * x5));
+ { u64 x48 = (((((((u64)x31 * x13) + (0x2 * (((((u64)x29 * x15) + ((u64)x33 * x11)) + ((u64)x25 * x19)) + ((u64)x37 * x7)))) + ((u64)x27 * x17)) + ((u64)x35 * x9)) + ((u64)x23 * x21)) + ((u64)x39 * x5));
+ { u64 x49 = (((((((((((u64)x31 * x15) + ((u64)x33 * x13)) + ((u64)x29 * x17)) + ((u64)x35 * x11)) + ((u64)x27 * x19)) + ((u64)x37 * x9)) + ((u64)x25 * x21)) + ((u64)x39 * x7)) + ((u64)x23 * x20)) + ((u64)x38 * x5));
+ { u64 x50 = (((((0x2 * ((((((u64)x33 * x15) + ((u64)x29 * x19)) + ((u64)x37 * x11)) + ((u64)x25 * x20)) + ((u64)x38 * x7))) + ((u64)x31 * x17)) + ((u64)x35 * x13)) + ((u64)x27 * x21)) + ((u64)x39 * x9));
+ { u64 x51 = (((((((((u64)x33 * x17) + ((u64)x35 * x15)) + ((u64)x31 * x19)) + ((u64)x37 * x13)) + ((u64)x29 * x21)) + ((u64)x39 * x11)) + ((u64)x27 * x20)) + ((u64)x38 * x9));
+ { u64 x52 = (((((u64)x35 * x17) + (0x2 * (((((u64)x33 * x19) + ((u64)x37 * x15)) + ((u64)x29 * x20)) + ((u64)x38 * x11)))) + ((u64)x31 * x21)) + ((u64)x39 * x13));
+ { u64 x53 = (((((((u64)x35 * x19) + ((u64)x37 * x17)) + ((u64)x33 * x21)) + ((u64)x39 * x15)) + ((u64)x31 * x20)) + ((u64)x38 * x13));
+ { u64 x54 = (((0x2 * ((((u64)x37 * x19) + ((u64)x33 * x20)) + ((u64)x38 * x15))) + ((u64)x35 * x21)) + ((u64)x39 * x17));
+ { u64 x55 = (((((u64)x37 * x21) + ((u64)x39 * x19)) + ((u64)x35 * x20)) + ((u64)x38 * x17));
+ { u64 x56 = (((u64)x39 * x21) + (0x2 * (((u64)x37 * x20) + ((u64)x38 * x19))));
+ { u64 x57 = (((u64)x39 * x20) + ((u64)x38 * x21));
+ { u64 x58 = ((u64)(0x2 * x38) * x20);
+ { u64 x59 = (x48 + (x58 << 0x4));
+ { u64 x60 = (x59 + (x58 << 0x1));
+ { u64 x61 = (x60 + x58);
+ { u64 x62 = (x47 + (x57 << 0x4));
+ { u64 x63 = (x62 + (x57 << 0x1));
+ { u64 x64 = (x63 + x57);
+ { u64 x65 = (x46 + (x56 << 0x4));
+ { u64 x66 = (x65 + (x56 << 0x1));
+ { u64 x67 = (x66 + x56);
+ { u64 x68 = (x45 + (x55 << 0x4));
+ { u64 x69 = (x68 + (x55 << 0x1));
+ { u64 x70 = (x69 + x55);
+ { u64 x71 = (x44 + (x54 << 0x4));
+ { u64 x72 = (x71 + (x54 << 0x1));
+ { u64 x73 = (x72 + x54);
+ { u64 x74 = (x43 + (x53 << 0x4));
+ { u64 x75 = (x74 + (x53 << 0x1));
+ { u64 x76 = (x75 + x53);
+ { u64 x77 = (x42 + (x52 << 0x4));
+ { u64 x78 = (x77 + (x52 << 0x1));
+ { u64 x79 = (x78 + x52);
+ { u64 x80 = (x41 + (x51 << 0x4));
+ { u64 x81 = (x80 + (x51 << 0x1));
+ { u64 x82 = (x81 + x51);
+ { u64 x83 = (x40 + (x50 << 0x4));
+ { u64 x84 = (x83 + (x50 << 0x1));
+ { u64 x85 = (x84 + x50);
+ { u64 x86 = (x85 >> 0x1a);
+ { u32 x87 = ((u32)x85 & 0x3ffffff);
+ { u64 x88 = (x86 + x82);
+ { u64 x89 = (x88 >> 0x19);
+ { u32 x90 = ((u32)x88 & 0x1ffffff);
+ { u64 x91 = (x89 + x79);
+ { u64 x92 = (x91 >> 0x1a);
+ { u32 x93 = ((u32)x91 & 0x3ffffff);
+ { u64 x94 = (x92 + x76);
+ { u64 x95 = (x94 >> 0x19);
+ { u32 x96 = ((u32)x94 & 0x1ffffff);
+ { u64 x97 = (x95 + x73);
+ { u64 x98 = (x97 >> 0x1a);
+ { u32 x99 = ((u32)x97 & 0x3ffffff);
+ { u64 x100 = (x98 + x70);
+ { u64 x101 = (x100 >> 0x19);
+ { u32 x102 = ((u32)x100 & 0x1ffffff);
+ { u64 x103 = (x101 + x67);
+ { u64 x104 = (x103 >> 0x1a);
+ { u32 x105 = ((u32)x103 & 0x3ffffff);
+ { u64 x106 = (x104 + x64);
+ { u64 x107 = (x106 >> 0x19);
+ { u32 x108 = ((u32)x106 & 0x1ffffff);
+ { u64 x109 = (x107 + x61);
+ { u64 x110 = (x109 >> 0x1a);
+ { u32 x111 = ((u32)x109 & 0x3ffffff);
+ { u64 x112 = (x110 + x49);
+ { u64 x113 = (x112 >> 0x19);
+ { u32 x114 = ((u32)x112 & 0x1ffffff);
+ { u64 x115 = (x87 + (0x13 * x113));
+ { u32 x116 = (u32) (x115 >> 0x1a);
+ { u32 x117 = ((u32)x115 & 0x3ffffff);
+ { u32 x118 = (x116 + x90);
+ { u32 x119 = (x118 >> 0x19);
+ { u32 x120 = (x118 & 0x1ffffff);
+ out[0] = x117;
+ out[1] = x120;
+ out[2] = (x119 + x93);
+ out[3] = x96;
+ out[4] = x99;
+ out[5] = x102;
+ out[6] = x105;
+ out[7] = x108;
+ out[8] = x111;
+ out[9] = x114;
+ }}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}
+}
+
+static __always_inline void fe_mul121666(fe *h, const fe_loose *f)
+{
+ fe_mul_121666_impl(h->v, f->v);
+}
+
+static void curve25519_generic(u8 out[CURVE25519_KEY_SIZE],
+ const u8 scalar[CURVE25519_KEY_SIZE],
+ const u8 point[CURVE25519_KEY_SIZE])
+{
+ fe x1, x2, z2, x3, z3;
+ fe_loose x2l, z2l, x3l;
+ unsigned swap = 0;
+ int pos;
+ u8 e[32];
+
+ memcpy(e, scalar, 32);
+ curve25519_clamp_secret(e);
+
+ /* The following implementation was transcribed to Coq and proven to
+ * correspond to unary scalar multiplication in affine coordinates given
+ * that x1 != 0 is the x coordinate of some point on the curve. It was
+ * also checked in Coq that doing a ladderstep with x1 = x3 = 0 gives
+ * z2' = z3' = 0, and z2 = z3 = 0 gives z2' = z3' = 0. The statement was
+ * quantified over the underlying field, so it applies to Curve25519
+ * itself and the quadratic twist of Curve25519. It was not proven in
+ * Coq that prime-field arithmetic correctly simulates extension-field
+ * arithmetic on prime-field values. The decoding of the byte array
+ * representation of e was not considered.
+ *
+ * Specification of Montgomery curves in affine coordinates:
+ * <https://github.com/mit-plv/fiat-crypto/blob/2456d821825521f7e03e65882cc3521795b0320f/src/Spec/MontgomeryCurve.v#L27>
+ *
+ * Proof that these form a group that is isomorphic to a Weierstrass
+ * curve:
+ * <https://github.com/mit-plv/fiat-crypto/blob/2456d821825521f7e03e65882cc3521795b0320f/src/Curves/Montgomery/AffineProofs.v#L35>
+ *
+ * Coq transcription and correctness proof of the loop
+ * (where scalarbits=255):
+ * <https://github.com/mit-plv/fiat-crypto/blob/2456d821825521f7e03e65882cc3521795b0320f/src/Curves/Montgomery/XZ.v#L118>
+ * <https://github.com/mit-plv/fiat-crypto/blob/2456d821825521f7e03e65882cc3521795b0320f/src/Curves/Montgomery/XZProofs.v#L278>
+ * preconditions: 0 <= e < 2^255 (not necessarily e < order),
+ * fe_invert(0) = 0
+ */
+ fe_frombytes(&x1, point);
+ fe_1(&x2);
+ fe_0(&z2);
+ fe_copy(&x3, &x1);
+ fe_1(&z3);
+
+ for (pos = 254; pos >= 0; --pos) {
+ fe tmp0, tmp1;
+ fe_loose tmp0l, tmp1l;
+ /* loop invariant as of right before the test, for the case
+ * where x1 != 0:
+ * pos >= -1; if z2 = 0 then x2 is nonzero; if z3 = 0 then x3
+ * is nonzero
+ * let r := e >> (pos+1) in the following equalities of
+ * projective points:
+ * to_xz (r*P) === if swap then (x3, z3) else (x2, z2)
+ * to_xz ((r+1)*P) === if swap then (x2, z2) else (x3, z3)
+ * x1 is the nonzero x coordinate of the nonzero
+ * point (r*P-(r+1)*P)
+ */
+ unsigned b = 1 & (e[pos / 8] >> (pos & 7));
+ swap ^= b;
+ fe_cswap(&x2, &x3, swap);
+ fe_cswap(&z2, &z3, swap);
+ swap = b;
+ /* Coq transcription of ladderstep formula (called from
+ * transcribed loop):
+ * <https://github.com/mit-plv/fiat-crypto/blob/2456d821825521f7e03e65882cc3521795b0320f/src/Curves/Montgomery/XZ.v#L89>
+ * <https://github.com/mit-plv/fiat-crypto/blob/2456d821825521f7e03e65882cc3521795b0320f/src/Curves/Montgomery/XZProofs.v#L131>
+ * x1 != 0 <https://github.com/mit-plv/fiat-crypto/blob/2456d821825521f7e03e65882cc3521795b0320f/src/Curves/Montgomery/XZProofs.v#L217>
+ * x1 = 0 <https://github.com/mit-plv/fiat-crypto/blob/2456d821825521f7e03e65882cc3521795b0320f/src/Curves/Montgomery/XZProofs.v#L147>
+ */
+ fe_sub(&tmp0l, &x3, &z3);
+ fe_sub(&tmp1l, &x2, &z2);
+ fe_add(&x2l, &x2, &z2);
+ fe_add(&z2l, &x3, &z3);
+ fe_mul_tll(&z3, &tmp0l, &x2l);
+ fe_mul_tll(&z2, &z2l, &tmp1l);
+ fe_sq_tl(&tmp0, &tmp1l);
+ fe_sq_tl(&tmp1, &x2l);
+ fe_add(&x3l, &z3, &z2);
+ fe_sub(&z2l, &z3, &z2);
+ fe_mul_ttt(&x2, &tmp1, &tmp0);
+ fe_sub(&tmp1l, &tmp1, &tmp0);
+ fe_sq_tl(&z2, &z2l);
+ fe_mul121666(&z3, &tmp1l);
+ fe_sq_tl(&x3, &x3l);
+ fe_add(&tmp0l, &tmp0, &z3);
+ fe_mul_ttt(&z3, &x1, &z2);
+ fe_mul_tll(&z2, &tmp1l, &tmp0l);
+ }
+ /* here pos=-1, so r=e, so to_xz (e*P) === if swap then (x3, z3)
+ * else (x2, z2)
+ */
+ fe_cswap(&x2, &x3, swap);
+ fe_cswap(&z2, &z3, swap);
+
+ fe_invert(&z2, &z2);
+ fe_mul_ttt(&x2, &x2, &z2);
+ fe_tobytes(out, &x2);
+
+ memzero_explicit(&x1, sizeof(x1));
+ memzero_explicit(&x2, sizeof(x2));
+ memzero_explicit(&z2, sizeof(z2));
+ memzero_explicit(&x3, sizeof(x3));
+ memzero_explicit(&z3, sizeof(z3));
+ memzero_explicit(&x2l, sizeof(x2l));
+ memzero_explicit(&z2l, sizeof(z2l));
+ memzero_explicit(&x3l, sizeof(x3l));
+ memzero_explicit(&e, sizeof(e));
+}
diff --git a/contrib/wireguard-tools/src/curve25519-hacl64.h b/contrib/wireguard-tools/src/curve25519-hacl64.h
new file mode 100644
--- /dev/null
+++ b/contrib/wireguard-tools/src/curve25519-hacl64.h
@@ -0,0 +1,784 @@
+// SPDX-License-Identifier: GPL-2.0 OR MIT
+/*
+ * Copyright (C) 2016-2017 INRIA and Microsoft Corporation.
+ * Copyright (C) 2018-2020 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
+ *
+ * This is a machine-generated formally verified implementation of Curve25519
+ * ECDH from: <https://github.com/mitls/hacl-star>. Though originally machine
+ * generated, it has been tweaked to be suitable for use in the kernel. It is
+ * optimized for 64-bit machines that can efficiently work with 128-bit
+ * integer types.
+ */
+
+typedef __uint128_t u128;
+
+static __always_inline u64 u64_eq_mask(u64 a, u64 b)
+{
+ u64 x = a ^ b;
+ u64 minus_x = ~x + (u64)1U;
+ u64 x_or_minus_x = x | minus_x;
+ u64 xnx = x_or_minus_x >> (u32)63U;
+ u64 c = xnx - (u64)1U;
+ return c;
+}
+
+static __always_inline u64 u64_gte_mask(u64 a, u64 b)
+{
+ u64 x = a;
+ u64 y = b;
+ u64 x_xor_y = x ^ y;
+ u64 x_sub_y = x - y;
+ u64 x_sub_y_xor_y = x_sub_y ^ y;
+ u64 q = x_xor_y | x_sub_y_xor_y;
+ u64 x_xor_q = x ^ q;
+ u64 x_xor_q_ = x_xor_q >> (u32)63U;
+ u64 c = x_xor_q_ - (u64)1U;
+ return c;
+}
+
+static __always_inline void modulo_carry_top(u64 *b)
+{
+ u64 b4 = b[4];
+ u64 b0 = b[0];
+ u64 b4_ = b4 & 0x7ffffffffffffLLU;
+ u64 b0_ = b0 + 19 * (b4 >> 51);
+ b[4] = b4_;
+ b[0] = b0_;
+}
+
+static __always_inline void fproduct_copy_from_wide_(u64 *output, u128 *input)
+{
+ {
+ u128 xi = input[0];
+ output[0] = ((u64)(xi));
+ }
+ {
+ u128 xi = input[1];
+ output[1] = ((u64)(xi));
+ }
+ {
+ u128 xi = input[2];
+ output[2] = ((u64)(xi));
+ }
+ {
+ u128 xi = input[3];
+ output[3] = ((u64)(xi));
+ }
+ {
+ u128 xi = input[4];
+ output[4] = ((u64)(xi));
+ }
+}
+
+static __always_inline void
+fproduct_sum_scalar_multiplication_(u128 *output, u64 *input, u64 s)
+{
+ output[0] += (u128)input[0] * s;
+ output[1] += (u128)input[1] * s;
+ output[2] += (u128)input[2] * s;
+ output[3] += (u128)input[3] * s;
+ output[4] += (u128)input[4] * s;
+}
+
+static __always_inline void fproduct_carry_wide_(u128 *tmp)
+{
+ {
+ u32 ctr = 0;
+ u128 tctr = tmp[ctr];
+ u128 tctrp1 = tmp[ctr + 1];
+ u64 r0 = ((u64)(tctr)) & 0x7ffffffffffffLLU;
+ u128 c = ((tctr) >> (51));
+ tmp[ctr] = ((u128)(r0));
+ tmp[ctr + 1] = ((tctrp1) + (c));
+ }
+ {
+ u32 ctr = 1;
+ u128 tctr = tmp[ctr];
+ u128 tctrp1 = tmp[ctr + 1];
+ u64 r0 = ((u64)(tctr)) & 0x7ffffffffffffLLU;
+ u128 c = ((tctr) >> (51));
+ tmp[ctr] = ((u128)(r0));
+ tmp[ctr + 1] = ((tctrp1) + (c));
+ }
+
+ {
+ u32 ctr = 2;
+ u128 tctr = tmp[ctr];
+ u128 tctrp1 = tmp[ctr + 1];
+ u64 r0 = ((u64)(tctr)) & 0x7ffffffffffffLLU;
+ u128 c = ((tctr) >> (51));
+ tmp[ctr] = ((u128)(r0));
+ tmp[ctr + 1] = ((tctrp1) + (c));
+ }
+ {
+ u32 ctr = 3;
+ u128 tctr = tmp[ctr];
+ u128 tctrp1 = tmp[ctr + 1];
+ u64 r0 = ((u64)(tctr)) & 0x7ffffffffffffLLU;
+ u128 c = ((tctr) >> (51));
+ tmp[ctr] = ((u128)(r0));
+ tmp[ctr + 1] = ((tctrp1) + (c));
+ }
+}
+
+static __always_inline void fmul_shift_reduce(u64 *output)
+{
+ u64 tmp = output[4];
+ u64 b0;
+ {
+ u32 ctr = 5 - 0 - 1;
+ u64 z = output[ctr - 1];
+ output[ctr] = z;
+ }
+ {
+ u32 ctr = 5 - 1 - 1;
+ u64 z = output[ctr - 1];
+ output[ctr] = z;
+ }
+ {
+ u32 ctr = 5 - 2 - 1;
+ u64 z = output[ctr - 1];
+ output[ctr] = z;
+ }
+ {
+ u32 ctr = 5 - 3 - 1;
+ u64 z = output[ctr - 1];
+ output[ctr] = z;
+ }
+ output[0] = tmp;
+ b0 = output[0];
+ output[0] = 19 * b0;
+}
+
+static __always_inline void fmul_mul_shift_reduce_(u128 *output, u64 *input,
+ u64 *input21)
+{
+ u32 i;
+ u64 input2i;
+ {
+ u64 input2i = input21[0];
+ fproduct_sum_scalar_multiplication_(output, input, input2i);
+ fmul_shift_reduce(input);
+ }
+ {
+ u64 input2i = input21[1];
+ fproduct_sum_scalar_multiplication_(output, input, input2i);
+ fmul_shift_reduce(input);
+ }
+ {
+ u64 input2i = input21[2];
+ fproduct_sum_scalar_multiplication_(output, input, input2i);
+ fmul_shift_reduce(input);
+ }
+ {
+ u64 input2i = input21[3];
+ fproduct_sum_scalar_multiplication_(output, input, input2i);
+ fmul_shift_reduce(input);
+ }
+ i = 4;
+ input2i = input21[i];
+ fproduct_sum_scalar_multiplication_(output, input, input2i);
+}
+
+static __always_inline void fmul_fmul(u64 *output, u64 *input, u64 *input21)
+{
+ u64 tmp[5] = { input[0], input[1], input[2], input[3], input[4] };
+ {
+ u128 b4;
+ u128 b0;
+ u128 b4_;
+ u128 b0_;
+ u64 i0;
+ u64 i1;
+ u64 i0_;
+ u64 i1_;
+ u128 t[5] = { 0 };
+ fmul_mul_shift_reduce_(t, tmp, input21);
+ fproduct_carry_wide_(t);
+ b4 = t[4];
+ b0 = t[0];
+ b4_ = ((b4) & (((u128)(0x7ffffffffffffLLU))));
+ b0_ = ((b0) + (((u128)(19) * (((u64)(((b4) >> (51))))))));
+ t[4] = b4_;
+ t[0] = b0_;
+ fproduct_copy_from_wide_(output, t);
+ i0 = output[0];
+ i1 = output[1];
+ i0_ = i0 & 0x7ffffffffffffLLU;
+ i1_ = i1 + (i0 >> 51);
+ output[0] = i0_;
+ output[1] = i1_;
+ }
+}
+
+static __always_inline void fsquare_fsquare__(u128 *tmp, u64 *output)
+{
+ u64 r0 = output[0];
+ u64 r1 = output[1];
+ u64 r2 = output[2];
+ u64 r3 = output[3];
+ u64 r4 = output[4];
+ u64 d0 = r0 * 2;
+ u64 d1 = r1 * 2;
+ u64 d2 = r2 * 2 * 19;
+ u64 d419 = r4 * 19;
+ u64 d4 = d419 * 2;
+ u128 s0 = ((((((u128)(r0) * (r0))) + (((u128)(d4) * (r1))))) +
+ (((u128)(d2) * (r3))));
+ u128 s1 = ((((((u128)(d0) * (r1))) + (((u128)(d4) * (r2))))) +
+ (((u128)(r3 * 19) * (r3))));
+ u128 s2 = ((((((u128)(d0) * (r2))) + (((u128)(r1) * (r1))))) +
+ (((u128)(d4) * (r3))));
+ u128 s3 = ((((((u128)(d0) * (r3))) + (((u128)(d1) * (r2))))) +
+ (((u128)(r4) * (d419))));
+ u128 s4 = ((((((u128)(d0) * (r4))) + (((u128)(d1) * (r3))))) +
+ (((u128)(r2) * (r2))));
+ tmp[0] = s0;
+ tmp[1] = s1;
+ tmp[2] = s2;
+ tmp[3] = s3;
+ tmp[4] = s4;
+}
+
+static __always_inline void fsquare_fsquare_(u128 *tmp, u64 *output)
+{
+ u128 b4;
+ u128 b0;
+ u128 b4_;
+ u128 b0_;
+ u64 i0;
+ u64 i1;
+ u64 i0_;
+ u64 i1_;
+ fsquare_fsquare__(tmp, output);
+ fproduct_carry_wide_(tmp);
+ b4 = tmp[4];
+ b0 = tmp[0];
+ b4_ = ((b4) & (((u128)(0x7ffffffffffffLLU))));
+ b0_ = ((b0) + (((u128)(19) * (((u64)(((b4) >> (51))))))));
+ tmp[4] = b4_;
+ tmp[0] = b0_;
+ fproduct_copy_from_wide_(output, tmp);
+ i0 = output[0];
+ i1 = output[1];
+ i0_ = i0 & 0x7ffffffffffffLLU;
+ i1_ = i1 + (i0 >> 51);
+ output[0] = i0_;
+ output[1] = i1_;
+}
+
+static __always_inline void fsquare_fsquare_times_(u64 *output, u128 *tmp,
+ u32 count1)
+{
+ u32 i;
+ fsquare_fsquare_(tmp, output);
+ for (i = 1; i < count1; ++i)
+ fsquare_fsquare_(tmp, output);
+}
+
+static __always_inline void fsquare_fsquare_times(u64 *output, u64 *input,
+ u32 count1)
+{
+ u128 t[5];
+ memcpy(output, input, 5 * sizeof(*input));
+ fsquare_fsquare_times_(output, t, count1);
+}
+
+static __always_inline void fsquare_fsquare_times_inplace(u64 *output,
+ u32 count1)
+{
+ u128 t[5];
+ fsquare_fsquare_times_(output, t, count1);
+}
+
+static __always_inline void crecip_crecip(u64 *out, u64 *z)
+{
+ u64 buf[20] = { 0 };
+ u64 *a0 = buf;
+ u64 *t00 = buf + 5;
+ u64 *b0 = buf + 10;
+ u64 *t01;
+ u64 *b1;
+ u64 *c0;
+ u64 *a;
+ u64 *t0;
+ u64 *b;
+ u64 *c;
+ fsquare_fsquare_times(a0, z, 1);
+ fsquare_fsquare_times(t00, a0, 2);
+ fmul_fmul(b0, t00, z);
+ fmul_fmul(a0, b0, a0);
+ fsquare_fsquare_times(t00, a0, 1);
+ fmul_fmul(b0, t00, b0);
+ fsquare_fsquare_times(t00, b0, 5);
+ t01 = buf + 5;
+ b1 = buf + 10;
+ c0 = buf + 15;
+ fmul_fmul(b1, t01, b1);
+ fsquare_fsquare_times(t01, b1, 10);
+ fmul_fmul(c0, t01, b1);
+ fsquare_fsquare_times(t01, c0, 20);
+ fmul_fmul(t01, t01, c0);
+ fsquare_fsquare_times_inplace(t01, 10);
+ fmul_fmul(b1, t01, b1);
+ fsquare_fsquare_times(t01, b1, 50);
+ a = buf;
+ t0 = buf + 5;
+ b = buf + 10;
+ c = buf + 15;
+ fmul_fmul(c, t0, b);
+ fsquare_fsquare_times(t0, c, 100);
+ fmul_fmul(t0, t0, c);
+ fsquare_fsquare_times_inplace(t0, 50);
+ fmul_fmul(t0, t0, b);
+ fsquare_fsquare_times_inplace(t0, 5);
+ fmul_fmul(out, t0, a);
+}
+
+static __always_inline void fsum(u64 *a, u64 *b)
+{
+ a[0] += b[0];
+ a[1] += b[1];
+ a[2] += b[2];
+ a[3] += b[3];
+ a[4] += b[4];
+}
+
+static __always_inline void fdifference(u64 *a, u64 *b)
+{
+ u64 tmp[5] = { 0 };
+ u64 b0;
+ u64 b1;
+ u64 b2;
+ u64 b3;
+ u64 b4;
+ memcpy(tmp, b, 5 * sizeof(*b));
+ b0 = tmp[0];
+ b1 = tmp[1];
+ b2 = tmp[2];
+ b3 = tmp[3];
+ b4 = tmp[4];
+ tmp[0] = b0 + 0x3fffffffffff68LLU;
+ tmp[1] = b1 + 0x3ffffffffffff8LLU;
+ tmp[2] = b2 + 0x3ffffffffffff8LLU;
+ tmp[3] = b3 + 0x3ffffffffffff8LLU;
+ tmp[4] = b4 + 0x3ffffffffffff8LLU;
+ {
+ u64 xi = a[0];
+ u64 yi = tmp[0];
+ a[0] = yi - xi;
+ }
+ {
+ u64 xi = a[1];
+ u64 yi = tmp[1];
+ a[1] = yi - xi;
+ }
+ {
+ u64 xi = a[2];
+ u64 yi = tmp[2];
+ a[2] = yi - xi;
+ }
+ {
+ u64 xi = a[3];
+ u64 yi = tmp[3];
+ a[3] = yi - xi;
+ }
+ {
+ u64 xi = a[4];
+ u64 yi = tmp[4];
+ a[4] = yi - xi;
+ }
+}
+
+static __always_inline void fscalar(u64 *output, u64 *b, u64 s)
+{
+ u128 tmp[5];
+ u128 b4;
+ u128 b0;
+ u128 b4_;
+ u128 b0_;
+ {
+ u64 xi = b[0];
+ tmp[0] = ((u128)(xi) * (s));
+ }
+ {
+ u64 xi = b[1];
+ tmp[1] = ((u128)(xi) * (s));
+ }
+ {
+ u64 xi = b[2];
+ tmp[2] = ((u128)(xi) * (s));
+ }
+ {
+ u64 xi = b[3];
+ tmp[3] = ((u128)(xi) * (s));
+ }
+ {
+ u64 xi = b[4];
+ tmp[4] = ((u128)(xi) * (s));
+ }
+ fproduct_carry_wide_(tmp);
+ b4 = tmp[4];
+ b0 = tmp[0];
+ b4_ = ((b4) & (((u128)(0x7ffffffffffffLLU))));
+ b0_ = ((b0) + (((u128)(19) * (((u64)(((b4) >> (51))))))));
+ tmp[4] = b4_;
+ tmp[0] = b0_;
+ fproduct_copy_from_wide_(output, tmp);
+}
+
+static __always_inline void fmul(u64 *output, u64 *a, u64 *b)
+{
+ fmul_fmul(output, a, b);
+}
+
+static __always_inline void crecip(u64 *output, u64 *input)
+{
+ crecip_crecip(output, input);
+}
+
+static __always_inline void point_swap_conditional_step(u64 *a, u64 *b,
+ u64 swap1, u32 ctr)
+{
+ u32 i = ctr - 1;
+ u64 ai = a[i];
+ u64 bi = b[i];
+ u64 x = swap1 & (ai ^ bi);
+ u64 ai1 = ai ^ x;
+ u64 bi1 = bi ^ x;
+ a[i] = ai1;
+ b[i] = bi1;
+}
+
+static __always_inline void point_swap_conditional5(u64 *a, u64 *b, u64 swap1)
+{
+ point_swap_conditional_step(a, b, swap1, 5);
+ point_swap_conditional_step(a, b, swap1, 4);
+ point_swap_conditional_step(a, b, swap1, 3);
+ point_swap_conditional_step(a, b, swap1, 2);
+ point_swap_conditional_step(a, b, swap1, 1);
+}
+
+static __always_inline void point_swap_conditional(u64 *a, u64 *b, u64 iswap)
+{
+ u64 swap1 = 0 - iswap;
+ point_swap_conditional5(a, b, swap1);
+ point_swap_conditional5(a + 5, b + 5, swap1);
+}
+
+static __always_inline void point_copy(u64 *output, u64 *input)
+{
+ memcpy(output, input, 5 * sizeof(*input));
+ memcpy(output + 5, input + 5, 5 * sizeof(*input));
+}
+
+static __always_inline void addanddouble_fmonty(u64 *pp, u64 *ppq, u64 *p,
+ u64 *pq, u64 *qmqp)
+{
+ u64 *qx = qmqp;
+ u64 *x2 = pp;
+ u64 *z2 = pp + 5;
+ u64 *x3 = ppq;
+ u64 *z3 = ppq + 5;
+ u64 *x = p;
+ u64 *z = p + 5;
+ u64 *xprime = pq;
+ u64 *zprime = pq + 5;
+ u64 buf[40] = { 0 };
+ u64 *origx = buf;
+ u64 *origxprime0 = buf + 5;
+ u64 *xxprime0;
+ u64 *zzprime0;
+ u64 *origxprime;
+ xxprime0 = buf + 25;
+ zzprime0 = buf + 30;
+ memcpy(origx, x, 5 * sizeof(*x));
+ fsum(x, z);
+ fdifference(z, origx);
+ memcpy(origxprime0, xprime, 5 * sizeof(*xprime));
+ fsum(xprime, zprime);
+ fdifference(zprime, origxprime0);
+ fmul(xxprime0, xprime, z);
+ fmul(zzprime0, x, zprime);
+ origxprime = buf + 5;
+ {
+ u64 *xx0;
+ u64 *zz0;
+ u64 *xxprime;
+ u64 *zzprime;
+ u64 *zzzprime;
+ xx0 = buf + 15;
+ zz0 = buf + 20;
+ xxprime = buf + 25;
+ zzprime = buf + 30;
+ zzzprime = buf + 35;
+ memcpy(origxprime, xxprime, 5 * sizeof(*xxprime));
+ fsum(xxprime, zzprime);
+ fdifference(zzprime, origxprime);
+ fsquare_fsquare_times(x3, xxprime, 1);
+ fsquare_fsquare_times(zzzprime, zzprime, 1);
+ fmul(z3, zzzprime, qx);
+ fsquare_fsquare_times(xx0, x, 1);
+ fsquare_fsquare_times(zz0, z, 1);
+ {
+ u64 *zzz;
+ u64 *xx;
+ u64 *zz;
+ u64 scalar;
+ zzz = buf + 10;
+ xx = buf + 15;
+ zz = buf + 20;
+ fmul(x2, xx, zz);
+ fdifference(zz, xx);
+ scalar = 121665;
+ fscalar(zzz, zz, scalar);
+ fsum(zzz, xx);
+ fmul(z2, zzz, zz);
+ }
+ }
+}
+
+static __always_inline void
+ladder_smallloop_cmult_small_loop_step(u64 *nq, u64 *nqpq, u64 *nq2, u64 *nqpq2,
+ u64 *q, u8 byt)
+{
+ u64 bit0 = (u64)(byt >> 7);
+ u64 bit;
+ point_swap_conditional(nq, nqpq, bit0);
+ addanddouble_fmonty(nq2, nqpq2, nq, nqpq, q);
+ bit = (u64)(byt >> 7);
+ point_swap_conditional(nq2, nqpq2, bit);
+}
+
+static __always_inline void
+ladder_smallloop_cmult_small_loop_double_step(u64 *nq, u64 *nqpq, u64 *nq2,
+ u64 *nqpq2, u64 *q, u8 byt)
+{
+ u8 byt1;
+ ladder_smallloop_cmult_small_loop_step(nq, nqpq, nq2, nqpq2, q, byt);
+ byt1 = byt << 1;
+ ladder_smallloop_cmult_small_loop_step(nq2, nqpq2, nq, nqpq, q, byt1);
+}
+
+static __always_inline void
+ladder_smallloop_cmult_small_loop(u64 *nq, u64 *nqpq, u64 *nq2, u64 *nqpq2,
+ u64 *q, u8 byt, u32 i)
+{
+ while (i--) {
+ ladder_smallloop_cmult_small_loop_double_step(nq, nqpq, nq2,
+ nqpq2, q, byt);
+ byt <<= 2;
+ }
+}
+
+static __always_inline void ladder_bigloop_cmult_big_loop(u8 *n1, u64 *nq,
+ u64 *nqpq, u64 *nq2,
+ u64 *nqpq2, u64 *q,
+ u32 i)
+{
+ while (i--) {
+ u8 byte = n1[i];
+ ladder_smallloop_cmult_small_loop(nq, nqpq, nq2, nqpq2, q,
+ byte, 4);
+ }
+}
+
+static void ladder_cmult(u64 *result, u8 *n1, u64 *q)
+{
+ u64 point_buf[40] = { 0 };
+ u64 *nq = point_buf;
+ u64 *nqpq = point_buf + 10;
+ u64 *nq2 = point_buf + 20;
+ u64 *nqpq2 = point_buf + 30;
+ point_copy(nqpq, q);
+ nq[0] = 1;
+ ladder_bigloop_cmult_big_loop(n1, nq, nqpq, nq2, nqpq2, q, 32);
+ point_copy(result, nq);
+}
+
+static __always_inline void format_fexpand(u64 *output, const u8 *input)
+{
+ const u8 *x00 = input + 6;
+ const u8 *x01 = input + 12;
+ const u8 *x02 = input + 19;
+ const u8 *x0 = input + 24;
+ u64 i0, i1, i2, i3, i4, output0, output1, output2, output3, output4;
+ i0 = get_unaligned_le64(input);
+ i1 = get_unaligned_le64(x00);
+ i2 = get_unaligned_le64(x01);
+ i3 = get_unaligned_le64(x02);
+ i4 = get_unaligned_le64(x0);
+ output0 = i0 & 0x7ffffffffffffLLU;
+ output1 = i1 >> 3 & 0x7ffffffffffffLLU;
+ output2 = i2 >> 6 & 0x7ffffffffffffLLU;
+ output3 = i3 >> 1 & 0x7ffffffffffffLLU;
+ output4 = i4 >> 12 & 0x7ffffffffffffLLU;
+ output[0] = output0;
+ output[1] = output1;
+ output[2] = output2;
+ output[3] = output3;
+ output[4] = output4;
+}
+
+static __always_inline void format_fcontract_first_carry_pass(u64 *input)
+{
+ u64 t0 = input[0];
+ u64 t1 = input[1];
+ u64 t2 = input[2];
+ u64 t3 = input[3];
+ u64 t4 = input[4];
+ u64 t1_ = t1 + (t0 >> 51);
+ u64 t0_ = t0 & 0x7ffffffffffffLLU;
+ u64 t2_ = t2 + (t1_ >> 51);
+ u64 t1__ = t1_ & 0x7ffffffffffffLLU;
+ u64 t3_ = t3 + (t2_ >> 51);
+ u64 t2__ = t2_ & 0x7ffffffffffffLLU;
+ u64 t4_ = t4 + (t3_ >> 51);
+ u64 t3__ = t3_ & 0x7ffffffffffffLLU;
+ input[0] = t0_;
+ input[1] = t1__;
+ input[2] = t2__;
+ input[3] = t3__;
+ input[4] = t4_;
+}
+
+static __always_inline void format_fcontract_first_carry_full(u64 *input)
+{
+ format_fcontract_first_carry_pass(input);
+ modulo_carry_top(input);
+}
+
+static __always_inline void format_fcontract_second_carry_pass(u64 *input)
+{
+ u64 t0 = input[0];
+ u64 t1 = input[1];
+ u64 t2 = input[2];
+ u64 t3 = input[3];
+ u64 t4 = input[4];
+ u64 t1_ = t1 + (t0 >> 51);
+ u64 t0_ = t0 & 0x7ffffffffffffLLU;
+ u64 t2_ = t2 + (t1_ >> 51);
+ u64 t1__ = t1_ & 0x7ffffffffffffLLU;
+ u64 t3_ = t3 + (t2_ >> 51);
+ u64 t2__ = t2_ & 0x7ffffffffffffLLU;
+ u64 t4_ = t4 + (t3_ >> 51);
+ u64 t3__ = t3_ & 0x7ffffffffffffLLU;
+ input[0] = t0_;
+ input[1] = t1__;
+ input[2] = t2__;
+ input[3] = t3__;
+ input[4] = t4_;
+}
+
+static __always_inline void format_fcontract_second_carry_full(u64 *input)
+{
+ u64 i0;
+ u64 i1;
+ u64 i0_;
+ u64 i1_;
+ format_fcontract_second_carry_pass(input);
+ modulo_carry_top(input);
+ i0 = input[0];
+ i1 = input[1];
+ i0_ = i0 & 0x7ffffffffffffLLU;
+ i1_ = i1 + (i0 >> 51);
+ input[0] = i0_;
+ input[1] = i1_;
+}
+
+static __always_inline void format_fcontract_trim(u64 *input)
+{
+ u64 a0 = input[0];
+ u64 a1 = input[1];
+ u64 a2 = input[2];
+ u64 a3 = input[3];
+ u64 a4 = input[4];
+ u64 mask0 = u64_gte_mask(a0, 0x7ffffffffffedLLU);
+ u64 mask1 = u64_eq_mask(a1, 0x7ffffffffffffLLU);
+ u64 mask2 = u64_eq_mask(a2, 0x7ffffffffffffLLU);
+ u64 mask3 = u64_eq_mask(a3, 0x7ffffffffffffLLU);
+ u64 mask4 = u64_eq_mask(a4, 0x7ffffffffffffLLU);
+ u64 mask = (((mask0 & mask1) & mask2) & mask3) & mask4;
+ u64 a0_ = a0 - (0x7ffffffffffedLLU & mask);
+ u64 a1_ = a1 - (0x7ffffffffffffLLU & mask);
+ u64 a2_ = a2 - (0x7ffffffffffffLLU & mask);
+ u64 a3_ = a3 - (0x7ffffffffffffLLU & mask);
+ u64 a4_ = a4 - (0x7ffffffffffffLLU & mask);
+ input[0] = a0_;
+ input[1] = a1_;
+ input[2] = a2_;
+ input[3] = a3_;
+ input[4] = a4_;
+}
+
+static __always_inline void format_fcontract_store(u8 *output, u64 *input)
+{
+ u64 t0 = input[0];
+ u64 t1 = input[1];
+ u64 t2 = input[2];
+ u64 t3 = input[3];
+ u64 t4 = input[4];
+ u64 o0 = t1 << 51 | t0;
+ u64 o1 = t2 << 38 | t1 >> 13;
+ u64 o2 = t3 << 25 | t2 >> 26;
+ u64 o3 = t4 << 12 | t3 >> 39;
+ u8 *b0 = output;
+ u8 *b1 = output + 8;
+ u8 *b2 = output + 16;
+ u8 *b3 = output + 24;
+ put_unaligned_le64(o0, b0);
+ put_unaligned_le64(o1, b1);
+ put_unaligned_le64(o2, b2);
+ put_unaligned_le64(o3, b3);
+}
+
+static __always_inline void format_fcontract(u8 *output, u64 *input)
+{
+ format_fcontract_first_carry_full(input);
+ format_fcontract_second_carry_full(input);
+ format_fcontract_trim(input);
+ format_fcontract_store(output, input);
+}
+
+static __always_inline void format_scalar_of_point(u8 *scalar, u64 *point)
+{
+ u64 *x = point;
+ u64 *z = point + 5;
+ u64 buf[10] __aligned(32) = { 0 };
+ u64 *zmone = buf;
+ u64 *sc = buf + 5;
+ crecip(zmone, z);
+ fmul(sc, x, zmone);
+ format_fcontract(scalar, sc);
+}
+
+static void curve25519_generic(u8 mypublic[CURVE25519_KEY_SIZE],
+ const u8 secret[CURVE25519_KEY_SIZE],
+ const u8 basepoint[CURVE25519_KEY_SIZE])
+{
+ u64 buf0[10] __aligned(32) = { 0 };
+ u64 *x0 = buf0;
+ u64 *z = buf0 + 5;
+ u64 *q;
+ format_fexpand(x0, basepoint);
+ z[0] = 1;
+ q = buf0;
+ {
+ u8 e[32] __aligned(32) = { 0 };
+ u8 *scalar;
+ memcpy(e, secret, 32);
+ curve25519_clamp_secret(e);
+ scalar = e;
+ {
+ u64 buf[15] = { 0 };
+ u64 *nq = buf;
+ u64 *x = nq;
+ x[0] = 1;
+ ladder_cmult(nq, scalar, q);
+ format_scalar_of_point(mypublic, nq);
+ memzero_explicit(buf, sizeof(buf));
+ }
+ memzero_explicit(e, sizeof(e));
+ }
+ memzero_explicit(buf0, sizeof(buf0));
+}
diff --git a/contrib/wireguard-tools/src/curve25519.h b/contrib/wireguard-tools/src/curve25519.h
new file mode 100644
--- /dev/null
+++ b/contrib/wireguard-tools/src/curve25519.h
@@ -0,0 +1,24 @@
+/* SPDX-License-Identifier: GPL-2.0 OR MIT */
+/*
+ * Copyright (C) 2015-2020 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
+ */
+
+#ifndef CURVE25519_H
+#define CURVE25519_H
+
+#include <stdint.h>
+#include <sys/types.h>
+
+enum curve25519_lengths {
+ CURVE25519_KEY_SIZE = 32
+};
+
+void curve25519(uint8_t mypublic[static CURVE25519_KEY_SIZE], const uint8_t secret[static CURVE25519_KEY_SIZE], const uint8_t basepoint[static CURVE25519_KEY_SIZE]);
+void curve25519_generate_public(uint8_t pub[static CURVE25519_KEY_SIZE], const uint8_t secret[static CURVE25519_KEY_SIZE]);
+static inline void curve25519_clamp_secret(uint8_t secret[static CURVE25519_KEY_SIZE])
+{
+ secret[0] &= 248;
+ secret[31] = (secret[31] & 127) | 64;
+}
+
+#endif
diff --git a/contrib/wireguard-tools/src/curve25519.c b/contrib/wireguard-tools/src/curve25519.c
new file mode 100644
--- /dev/null
+++ b/contrib/wireguard-tools/src/curve25519.c
@@ -0,0 +1,98 @@
+// SPDX-License-Identifier: GPL-2.0 OR MIT
+/*
+ * Copyright (C) 2018-2020 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
+ */
+
+#include "curve25519.h"
+
+#include <stdint.h>
+#include <string.h>
+
+#ifndef __BYTE_ORDER__
+#include <sys/param.h>
+#if !defined(BYTE_ORDER) || !defined(BIG_ENDIAN) || !defined(LITTLE_ENDIAN)
+#error "Unable to determine endianness."
+#endif
+#define __BYTE_ORDER__ BYTE_ORDER
+#define __ORDER_BIG_ENDIAN__ BIG_ENDIAN
+#define __ORDER_LITTLE_ENDIAN__ LITTLE_ENDIAN
+#endif
+
+#ifdef __linux__
+#include <linux/types.h>
+typedef __u64 u64;
+typedef __u32 u32;
+typedef __u8 u8;
+typedef __s64 s64;
+#else
+typedef uint64_t u64, __le64;
+typedef uint32_t u32, __le32;
+typedef uint8_t u8;
+typedef int64_t s64;
+#endif
+#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
+#define le64_to_cpup(a) __builtin_bswap64(*(a))
+#define le32_to_cpup(a) __builtin_bswap32(*(a))
+#define cpu_to_le64(a) __builtin_bswap64(a)
+#else
+#define le64_to_cpup(a) (*(a))
+#define le32_to_cpup(a) (*(a))
+#define cpu_to_le64(a) (a)
+#endif
+#ifndef __unused
+#define __unused __attribute__((unused))
+#endif
+#ifndef __always_inline
+#define __always_inline __inline __attribute__((__always_inline__))
+#endif
+#ifndef noinline
+#define noinline __attribute__((noinline))
+#endif
+#ifndef __aligned
+#define __aligned(x) __attribute__((aligned(x)))
+#endif
+#ifndef __force
+#define __force
+#endif
+
+static __always_inline __unused __le32 get_unaligned_le32(const u8 *a)
+{
+ __le32 l;
+ __builtin_memcpy(&l, a, sizeof(l));
+ return le32_to_cpup(&l);
+}
+static __always_inline __unused __le64 get_unaligned_le64(const u8 *a)
+{
+ __le64 l;
+ __builtin_memcpy(&l, a, sizeof(l));
+ return le64_to_cpup(&l);
+}
+static __always_inline __unused void put_unaligned_le64(u64 s, u8 *d)
+{
+ __le64 l = cpu_to_le64(s);
+ __builtin_memcpy(d, &l, sizeof(l));
+}
+
+static noinline void memzero_explicit(void *s, size_t count)
+{
+ memset(s, 0, count);
+ asm volatile("": :"r"(s) : "memory");
+}
+
+#ifdef __SIZEOF_INT128__
+#include "curve25519-hacl64.h"
+#else
+#include "curve25519-fiat32.h"
+#endif
+
+void curve25519_generate_public(uint8_t pub[static CURVE25519_KEY_SIZE], const uint8_t secret[static CURVE25519_KEY_SIZE])
+{
+ static const uint8_t basepoint[CURVE25519_KEY_SIZE] __aligned(sizeof(uintptr_t)) = { 9 };
+
+ curve25519(pub, secret, basepoint);
+}
+
+void curve25519(uint8_t mypublic[static CURVE25519_KEY_SIZE], const uint8_t secret[static CURVE25519_KEY_SIZE], const uint8_t basepoint[static CURVE25519_KEY_SIZE])
+{
+ curve25519_generic(mypublic, secret, basepoint);
+}
diff --git a/contrib/wireguard-tools/src/encoding.h b/contrib/wireguard-tools/src/encoding.h
new file mode 100644
--- /dev/null
+++ b/contrib/wireguard-tools/src/encoding.h
@@ -0,0 +1,24 @@
+/* SPDX-License-Identifier: GPL-2.0 OR MIT */
+/*
+ * Copyright (C) 2015-2020 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
+ */
+
+#ifndef ENCODING_H
+#define ENCODING_H
+
+#include <stdbool.h>
+#include <stdint.h>
+#include "containers.h"
+
+#define WG_KEY_LEN_BASE64 ((((WG_KEY_LEN) + 2) / 3) * 4 + 1)
+#define WG_KEY_LEN_HEX (WG_KEY_LEN * 2 + 1)
+
+void key_to_base64(char base64[static WG_KEY_LEN_BASE64], const uint8_t key[static WG_KEY_LEN]);
+bool key_from_base64(uint8_t key[static WG_KEY_LEN], const char *base64);
+
+void key_to_hex(char hex[static WG_KEY_LEN_HEX], const uint8_t key[static WG_KEY_LEN]);
+bool key_from_hex(uint8_t key[static WG_KEY_LEN], const char *hex);
+
+bool key_is_zero(const uint8_t key[static WG_KEY_LEN]);
+
+#endif
diff --git a/contrib/wireguard-tools/src/encoding.c b/contrib/wireguard-tools/src/encoding.c
new file mode 100644
--- /dev/null
+++ b/contrib/wireguard-tools/src/encoding.c
@@ -0,0 +1,125 @@
+// SPDX-License-Identifier: GPL-2.0 OR MIT
+/*
+ * Copyright (C) 2015-2020 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
+ *
+ * This is a specialized constant-time base64/hex implementation that resists side-channel attacks.
+ */
+
+#include <string.h>
+#include "encoding.h"
+
+static inline void encode_base64(char dest[static 4], const uint8_t src[static 3])
+{
+ const uint8_t input[] = { (src[0] >> 2) & 63, ((src[0] << 4) | (src[1] >> 4)) & 63, ((src[1] << 2) | (src[2] >> 6)) & 63, src[2] & 63 };
+
+ for (unsigned int i = 0; i < 4; ++i)
+ dest[i] = input[i] + 'A'
+ + (((25 - input[i]) >> 8) & 6)
+ - (((51 - input[i]) >> 8) & 75)
+ - (((61 - input[i]) >> 8) & 15)
+ + (((62 - input[i]) >> 8) & 3);
+
+}
+
+void key_to_base64(char base64[static WG_KEY_LEN_BASE64], const uint8_t key[static WG_KEY_LEN])
+{
+ unsigned int i;
+
+ for (i = 0; i < WG_KEY_LEN / 3; ++i)
+ encode_base64(&base64[i * 4], &key[i * 3]);
+ encode_base64(&base64[i * 4], (const uint8_t[]){ key[i * 3 + 0], key[i * 3 + 1], 0 });
+ base64[WG_KEY_LEN_BASE64 - 2] = '=';
+ base64[WG_KEY_LEN_BASE64 - 1] = '\0';
+}
+
+static inline int decode_base64(const char src[static 4])
+{
+ int val = 0;
+
+ for (unsigned int i = 0; i < 4; ++i)
+ val |= (-1
+ + ((((('A' - 1) - src[i]) & (src[i] - ('Z' + 1))) >> 8) & (src[i] - 64))
+ + ((((('a' - 1) - src[i]) & (src[i] - ('z' + 1))) >> 8) & (src[i] - 70))
+ + ((((('0' - 1) - src[i]) & (src[i] - ('9' + 1))) >> 8) & (src[i] + 5))
+ + ((((('+' - 1) - src[i]) & (src[i] - ('+' + 1))) >> 8) & 63)
+ + ((((('/' - 1) - src[i]) & (src[i] - ('/' + 1))) >> 8) & 64)
+ ) << (18 - 6 * i);
+ return val;
+}
+
+bool key_from_base64(uint8_t key[static WG_KEY_LEN], const char *base64)
+{
+ unsigned int i;
+ volatile uint8_t ret = 0;
+ int val;
+
+ if (strlen(base64) != WG_KEY_LEN_BASE64 - 1 || base64[WG_KEY_LEN_BASE64 - 2] != '=')
+ return false;
+
+ for (i = 0; i < WG_KEY_LEN / 3; ++i) {
+ val = decode_base64(&base64[i * 4]);
+ ret |= (uint32_t)val >> 31;
+ key[i * 3 + 0] = (val >> 16) & 0xff;
+ key[i * 3 + 1] = (val >> 8) & 0xff;
+ key[i * 3 + 2] = val & 0xff;
+ }
+ val = decode_base64((const char[]){ base64[i * 4 + 0], base64[i * 4 + 1], base64[i * 4 + 2], 'A' });
+ ret |= ((uint32_t)val >> 31) | (val & 0xff);
+ key[i * 3 + 0] = (val >> 16) & 0xff;
+ key[i * 3 + 1] = (val >> 8) & 0xff;
+
+ return 1 & ((ret - 1) >> 8);
+}
+
+void key_to_hex(char hex[static WG_KEY_LEN_HEX], const uint8_t key[static WG_KEY_LEN])
+{
+ unsigned int i;
+
+ for (i = 0; i < WG_KEY_LEN; ++i) {
+ hex[i * 2] = 87U + (key[i] >> 4) + ((((key[i] >> 4) - 10U) >> 8) & ~38U);
+ hex[i * 2 + 1] = 87U + (key[i] & 0xf) + ((((key[i] & 0xf) - 10U) >> 8) & ~38U);
+ }
+ hex[i * 2] = '\0';
+}
+
+bool key_from_hex(uint8_t key[static WG_KEY_LEN], const char *hex)
+{
+ uint8_t c, c_acc, c_alpha0, c_alpha, c_num0, c_num, c_val;
+ volatile uint8_t ret = 0;
+
+ if (strlen(hex) != WG_KEY_LEN_HEX - 1)
+ return false;
+
+ for (unsigned int i = 0; i < WG_KEY_LEN_HEX - 1; i += 2) {
+ c = (uint8_t)hex[i];
+ c_num = c ^ 48U;
+ c_num0 = (c_num - 10U) >> 8;
+ c_alpha = (c & ~32U) - 55U;
+ c_alpha0 = ((c_alpha - 10U) ^ (c_alpha - 16U)) >> 8;
+ ret |= ((c_num0 | c_alpha0) - 1) >> 8;
+ c_val = (c_num0 & c_num) | (c_alpha0 & c_alpha);
+ c_acc = c_val * 16U;
+
+ c = (uint8_t)hex[i + 1];
+ c_num = c ^ 48U;
+ c_num0 = (c_num - 10U) >> 8;
+ c_alpha = (c & ~32U) - 55U;
+ c_alpha0 = ((c_alpha - 10U) ^ (c_alpha - 16U)) >> 8;
+ ret |= ((c_num0 | c_alpha0) - 1) >> 8;
+ c_val = (c_num0 & c_num) | (c_alpha0 & c_alpha);
+ key[i / 2] = c_acc | c_val;
+ }
+
+ return 1 & ((ret - 1) >> 8);
+}
+
+bool key_is_zero(const uint8_t key[static WG_KEY_LEN])
+{
+ volatile uint8_t acc = 0;
+
+ for (unsigned int i = 0; i < WG_KEY_LEN; ++i) {
+ acc |= key[i];
+ asm volatile("" : "=r"(acc) : "0"(acc));
+ }
+ return 1 & ((acc - 1) >> 8);
+}
diff --git a/contrib/wireguard-tools/src/fuzz/.gitignore b/contrib/wireguard-tools/src/fuzz/.gitignore
new file mode 100644
--- /dev/null
+++ b/contrib/wireguard-tools/src/fuzz/.gitignore
@@ -0,0 +1,6 @@
+config
+uapi
+stringlist
+cmd
+set
+setconf
diff --git a/contrib/wireguard-tools/src/fuzz/Makefile b/contrib/wireguard-tools/src/fuzz/Makefile
new file mode 100644
--- /dev/null
+++ b/contrib/wireguard-tools/src/fuzz/Makefile
@@ -0,0 +1,34 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+# Copyright (C) 2018-2020 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
+
+FUZZERS := config uapi stringlist cmd set setconf
+
+all: $(FUZZERS)
+
+CFLAGS ?= -O3 -march=native -g
+CFLAGS += -fsanitize=fuzzer -fsanitize=address -std=gnu11 -idirafter ../uapi -D_GNU_SOURCE
+CC := clang
+
+config: config.c ../config.c ../encoding.c
+ $(CC) $(CFLAGS) -o $@ $<
+
+uapi: uapi.c ../ipc.c ../curve25519.c ../encoding.c
+ $(CC) $(CFLAGS) -o $@ $<
+
+stringlist: stringlist.c ../ipc.c ../curve25519.c ../encoding.c
+ $(CC) $(CFLAGS) -o $@ $<
+
+cmd: cmd.c $(wildcard ../*.c)
+ $(CC) $(CFLAGS) -D'RUNSTATEDIR="/var/empty"' -D'main(a,b)=wg_main(a,b)' -o $@ $^
+
+set: set.c ../set.c ../ipc.c ../encoding.c ../curve25519.c ../config.c
+ $(CC) $(CFLAGS) -o $@ $<
+
+setconf: setconf.c ../setconf.c ../ipc.c ../encoding.c ../curve25519.c ../config.c
+ $(CC) $(CFLAGS) -o $@ $<
+
+clean:
+ $(RM) $(FUZZERS)
+
+.PHONY: all clean
diff --git a/contrib/wireguard-tools/src/fuzz/cmd.c b/contrib/wireguard-tools/src/fuzz/cmd.c
new file mode 100644
--- /dev/null
+++ b/contrib/wireguard-tools/src/fuzz/cmd.c
@@ -0,0 +1,72 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2018-2020 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
+ */
+
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <assert.h>
+
+const char *__asan_default_options()
+{
+ return "verbosity=1";
+}
+
+int wg_main(int argc, char *argv[]);
+
+static FILE *devnull;
+
+int LLVMFuzzerTestOneInput(const char *data, size_t data_len)
+{
+ char *argv[8192] = { 0 }, *args;
+ size_t argc = 0;
+ FILE *fake_stdin = NULL;
+
+ if (!devnull) {
+ assert((devnull = fopen("/dev/null", "r+")));
+ stdin = stdout = stderr = devnull;
+ }
+
+ assert((args = malloc(data_len)));
+ memcpy(args, data, data_len);
+ if (data_len)
+ args[data_len - 1] = '\0';
+
+ for (const char *arg = args; argc < 8192 && arg - args < data_len; arg += strlen(arg) + 1) {
+ if (arg[0])
+ assert((argv[argc++] = strdup(arg)));
+ }
+ if (!argc)
+ assert((argv[argc++] = strdup("no argv[0]!")));
+ if (argc > 2 && (!strcmp(argv[1], "show") || !strcmp(argv[1], "showconf") || !strcmp(argv[1], "set") || !strcmp(argv[1], "setconf") || !strcmp(argv[1], "addconf") || !strcmp(argv[1], "syncconf"))) {
+ free(argv[2]);
+ assert((argv[2] = strdup("wg0")));
+ }
+ if (argc >= 2 && !strcmp(argv[1], "pubkey")) {
+ char *arg;
+ size_t len;
+
+ for (size_t i = 2; i < argc; ++i)
+ free(argv[i]);
+ argc = 2;
+ arg = args;
+ for (; !arg[0]; ++arg);
+ arg += strlen(arg) + 1;
+ for (; !arg[0]; ++arg);
+ arg += strlen(arg) + 1;
+ len = data_len - (arg - args);
+ if (len <= 1)
+ goto done;
+ assert((fake_stdin = fmemopen(arg, len - 1, "r")));
+ stdin = fake_stdin;
+ }
+ wg_main(argc, argv);
+done:
+ for (size_t i = 0; i < argc; ++i)
+ free(argv[i]);
+ free(args);
+ if (fake_stdin)
+ fclose(fake_stdin);
+ return 0;
+}
diff --git a/contrib/wireguard-tools/src/fuzz/config.c b/contrib/wireguard-tools/src/fuzz/config.c
new file mode 100644
--- /dev/null
+++ b/contrib/wireguard-tools/src/fuzz/config.c
@@ -0,0 +1,69 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2018-2020 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
+ */
+
+#include <stdio.h>
+#undef stderr
+#define stderr stdin
+#include "../config.c"
+#include "../encoding.c"
+#undef stderr
+
+#include <stdint.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include "../config.h"
+
+const char *__asan_default_options()
+{
+ return "verbosity=1";
+}
+
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t len)
+{
+ bool file;
+ char *input;
+
+ if (len < 2)
+ return 0;
+ file = !!(data[0] >> 7);
+ input = malloc(len);
+ if (!input)
+ return 0;
+ memcpy(input, data + 1, len - 1);
+ input[len - 1] = '\0';
+
+ if (file) {
+ struct config_ctx ctx;
+ char *saveptr;
+
+ config_read_init(&ctx, false);
+ for (char *line = strtok_r(input, "\n", &saveptr); line; line = strtok_r(NULL, "\n", &saveptr)) {
+ if (!config_read_line(&ctx, line))
+ config_read_init(&ctx, false);
+ }
+ free_wgdevice(config_read_finish(&ctx));
+ } else {
+ size_t spaces = 0;
+ char **argv, *saveptr;
+
+ for (char *c = input; *c; ++c) {
+ if (*c == ' ')
+ ++spaces;
+ }
+ argv = calloc(spaces + 1, sizeof(char *));
+ if (!argv)
+ goto out;
+ spaces = 0;
+ for (char *token = strtok_r(input, " ", &saveptr); token; token = strtok_r(NULL, " ", &saveptr))
+ argv[spaces++] = token;
+ free_wgdevice(config_read_cmd(argv, spaces));
+ free(argv);
+ }
+
+out:
+ free(input);
+ return 0;
+}
diff --git a/contrib/wireguard-tools/src/fuzz/set.c b/contrib/wireguard-tools/src/fuzz/set.c
new file mode 100644
--- /dev/null
+++ b/contrib/wireguard-tools/src/fuzz/set.c
@@ -0,0 +1,56 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2018-2020 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
+ */
+
+#include <stdio.h>
+#undef stderr
+#define stderr stdin
+#define RUNSTATEDIR "/var/empty"
+#include "../curve25519.c"
+#define parse_allowedips parse_allowedips_ipc
+#include "../ipc.c"
+#undef parse_allowedips
+#include "../encoding.c"
+static FILE *hacked_fopen(const char *pathname, const char *mode);
+#define fopen hacked_fopen
+#include "../config.c"
+#include "../set.c"
+#undef stderr
+
+#include <string.h>
+#include <stdlib.h>
+#include <assert.h>
+
+const char *__asan_default_options()
+{
+ return "verbosity=1";
+}
+
+const char *PROG_NAME = "wg";
+
+static FILE *hacked_fopen(const char *pathname, const char *mode)
+{
+ return fmemopen((char *)pathname, strlen(pathname), "r");
+}
+
+int LLVMFuzzerTestOneInput(const char *data, size_t data_len)
+{
+ char *argv[8192] = { "set", "wg0" }, *args;
+ size_t argc = 2;
+
+ if (!data_len)
+ return 0;
+
+ assert((args = malloc(data_len)));
+ memcpy(args, data, data_len);
+ args[data_len - 1] = '\0';
+
+ for (char *arg = strtok(args, " \t\n\r"); arg && argc < 8192; arg = strtok(NULL, " \t\n\r")) {
+ if (arg[0])
+ argv[argc++] = arg;
+ }
+ set_main(argc, argv);
+ free(args);
+ return 0;
+}
diff --git a/contrib/wireguard-tools/src/fuzz/setconf.c b/contrib/wireguard-tools/src/fuzz/setconf.c
new file mode 100644
--- /dev/null
+++ b/contrib/wireguard-tools/src/fuzz/setconf.c
@@ -0,0 +1,53 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2018-2020 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
+ */
+
+#include <stdio.h>
+#undef stderr
+#define stderr stdin
+#define RUNSTATEDIR "/var/empty"
+#include "../curve25519.c"
+#define parse_allowedips parse_allowedips_ipc
+#include "../ipc.c"
+#undef parse_allowedips
+#include "../encoding.c"
+#include "../config.c"
+static FILE *hacked_fopen(const char *pathname, const char *mode);
+#define fopen hacked_fopen
+#include "../setconf.c"
+#undef fopen
+#undef stderr
+
+#include <string.h>
+#include <stdlib.h>
+#include <assert.h>
+
+const char *__asan_default_options()
+{
+ return "verbosity=1";
+}
+
+const char *PROG_NAME = "wg";
+
+struct hacked_pointers {
+ const char *data;
+ size_t data_len;
+};
+
+static FILE *hacked_fopen(const char *pathname, const char *mode)
+{
+ struct hacked_pointers *h = (struct hacked_pointers *)strtoul(pathname, NULL, 10);
+ return fmemopen((char *)h->data, h->data_len, "r");
+}
+
+int LLVMFuzzerTestOneInput(const char *data, size_t data_len)
+{
+ char strptr[32];
+ char *argv[3] = { "setconf", "wg0", strptr };
+ struct hacked_pointers h = { data, data_len };
+
+ snprintf(strptr, sizeof(strptr), "%lu", (unsigned long)&h);
+ setconf_main(3, argv);
+ return 0;
+}
diff --git a/contrib/wireguard-tools/src/fuzz/stringlist.c b/contrib/wireguard-tools/src/fuzz/stringlist.c
new file mode 100644
--- /dev/null
+++ b/contrib/wireguard-tools/src/fuzz/stringlist.c
@@ -0,0 +1,59 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2018-2020 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
+ */
+
+#define RUNSTATEDIR "/var/empty"
+#include "../curve25519.c"
+#undef __linux__
+#include "../ipc.c"
+#include "../encoding.c"
+
+#include <stdint.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <assert.h>
+
+const char *__asan_default_options()
+{
+ return "verbosity=1";
+}
+
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t data_len)
+{
+ struct string_list list = { 0 };
+ char *interfaces;
+
+ if (!data_len)
+ return 0;
+
+ interfaces = malloc(data_len);
+ assert(interfaces);
+ memcpy(interfaces, data, data_len);
+ interfaces[data_len - 1] = '\0';
+
+ for (char *interface = interfaces; interface - interfaces < data_len; interface += strlen(interface) + 1)
+ assert(string_list_add(&list, interface) == 0);
+
+ for (char *interface = interfaces, *interface2 = list.buffer;;) {
+ size_t len;
+
+ if (interface - interfaces >= data_len) {
+ assert(!interface2 || !strlen(interface2));
+ break;
+ }
+ len = strlen(interface);
+ if (!len) {
+ ++interface;
+ continue;
+ }
+ assert(strlen(interface2) == len);
+ assert(!memcmp(interface, interface2, len + 1));
+ interface += len + 1;
+ interface2 += len + 1;
+ }
+ free(list.buffer);
+ free(interfaces);
+ return 0;
+}
diff --git a/contrib/wireguard-tools/src/fuzz/uapi.c b/contrib/wireguard-tools/src/fuzz/uapi.c
new file mode 100644
--- /dev/null
+++ b/contrib/wireguard-tools/src/fuzz/uapi.c
@@ -0,0 +1,56 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2018-2020 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
+ */
+
+#include <stdio.h>
+#include <sys/stat.h>
+static FILE *hacked_userspace_interface_file(const char *iface);
+#define stat(a, b) ({ return hacked_userspace_interface_file(iface); 0; })
+#define RUNSTATEDIR "/var/empty"
+#include "../curve25519.c"
+#undef __linux__
+#include "../ipc.c"
+#include "../encoding.c"
+
+#include <stdint.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+const char *__asan_default_options()
+{
+ return "verbosity=1";
+}
+
+union hackiface {
+ char ifname[IFNAMSIZ];
+ struct {
+ const uint8_t *data;
+ size_t len;
+ };
+};
+
+static FILE *hacked_userspace_interface_file(const char *iface)
+{
+ union hackiface *hack = (union hackiface *)iface;
+ FILE *f = fmemopen(NULL, hack->len + 7, "r+");
+ fseek(f, 7, SEEK_SET);
+ fwrite(hack->data, hack->len, 1, f);
+ fseek(f, 0, SEEK_SET);
+ memcpy(hack->ifname, "hack", 5);
+ return f;
+}
+
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t len)
+{
+ union hackiface hack = {
+ .data = data,
+ .len = len
+ };
+ struct wgdevice *dev = NULL;
+
+ userspace_get_device(&dev, (const char *)&hack);
+ free_wgdevice(dev);
+ return 0;
+}
diff --git a/contrib/wireguard-tools/src/genkey.c b/contrib/wireguard-tools/src/genkey.c
new file mode 100644
--- /dev/null
+++ b/contrib/wireguard-tools/src/genkey.c
@@ -0,0 +1,99 @@
+// SPDX-License-Identifier: GPL-2.0 OR MIT
+/*
+ * Copyright (C) 2015-2020 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
+ */
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdbool.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <string.h>
+#include <fcntl.h>
+#ifdef __linux__
+#include <sys/syscall.h>
+#endif
+#ifdef __APPLE__
+#include <AvailabilityMacros.h>
+#ifndef MAC_OS_X_VERSION_10_12
+#define MAC_OS_X_VERSION_10_12 101200
+#endif
+#if MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_12
+#include <sys/random.h>
+#endif
+#endif
+
+#include "curve25519.h"
+#include "encoding.h"
+#include "subcommands.h"
+
+#ifndef _WIN32
+static inline bool __attribute__((__warn_unused_result__)) get_random_bytes(uint8_t *out, size_t len)
+{
+ ssize_t ret = 0;
+ size_t i;
+ int fd;
+
+ if (len > 256) {
+ errno = EOVERFLOW;
+ return false;
+ }
+
+#if defined(__OpenBSD__) || (defined(__APPLE__) && MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_12) || (defined(__GLIBC__) && (__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 25)))
+ if (!getentropy(out, len))
+ return true;
+#endif
+
+#if defined(__NR_getrandom) && defined(__linux__)
+ if (syscall(__NR_getrandom, out, len, 0) == (ssize_t)len)
+ return true;
+#endif
+
+ fd = open("/dev/urandom", O_RDONLY);
+ if (fd < 0)
+ return false;
+ for (errno = 0, i = 0; i < len; i += ret, ret = 0) {
+ ret = read(fd, out + i, len - i);
+ if (ret <= 0) {
+ ret = errno ? -errno : -EIO;
+ break;
+ }
+ }
+ close(fd);
+ errno = -ret;
+ return i == len;
+}
+#else
+#include <ntsecapi.h>
+static inline bool __attribute__((__warn_unused_result__)) get_random_bytes(uint8_t *out, size_t len)
+{
+ return RtlGenRandom(out, len);
+}
+#endif
+
+int genkey_main(int argc, const char *argv[])
+{
+ uint8_t key[WG_KEY_LEN];
+ char base64[WG_KEY_LEN_BASE64];
+ struct stat stat;
+
+ if (argc != 1) {
+ fprintf(stderr, "Usage: %s %s\n", PROG_NAME, argv[0]);
+ return 1;
+ }
+
+ if (!fstat(STDOUT_FILENO, &stat) && S_ISREG(stat.st_mode) && stat.st_mode & S_IRWXO)
+ fputs("Warning: writing to world accessible file.\nConsider setting the umask to 077 and trying again.\n", stderr);
+
+ if (!get_random_bytes(key, WG_KEY_LEN)) {
+ perror("getrandom");
+ return 1;
+ }
+ if (!strcmp(argv[0], "genkey"))
+ curve25519_clamp_secret(key);
+
+ key_to_base64(base64, key);
+ puts(base64);
+ return 0;
+}
diff --git a/contrib/wireguard-tools/src/ipc-freebsd.h b/contrib/wireguard-tools/src/ipc-freebsd.h
new file mode 100644
--- /dev/null
+++ b/contrib/wireguard-tools/src/ipc-freebsd.h
@@ -0,0 +1,350 @@
+// SPDX-License-Identifier: MIT
+/*
+ * Copyright (C) 2015-2021 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
+ *
+ */
+
+#include <sys/nv.h>
+#include <sys/sockio.h>
+#include <dev/if_wg/if_wg.h>
+
+#define IPC_SUPPORTS_KERNEL_INTERFACE
+
+static int get_dgram_socket(void)
+{
+ static int sock = -1;
+ if (sock < 0)
+ sock = socket(AF_INET, SOCK_DGRAM, 0);
+ return sock;
+}
+
+static int kernel_get_wireguard_interfaces(struct string_list *list)
+{
+ struct ifgroupreq ifgr = { .ifgr_name = "wg" };
+ struct ifg_req *ifg;
+ int s = get_dgram_socket(), ret = 0;
+
+ if (s < 0)
+ return -errno;
+
+ if (ioctl(s, SIOCGIFGMEMB, (caddr_t)&ifgr) < 0)
+ return errno == ENOENT ? 0 : -errno;
+
+ ifgr.ifgr_groups = calloc(1, ifgr.ifgr_len);
+ if (!ifgr.ifgr_groups)
+ return -errno;
+ if (ioctl(s, SIOCGIFGMEMB, (caddr_t)&ifgr) < 0) {
+ ret = -errno;
+ goto out;
+ }
+
+ for (ifg = ifgr.ifgr_groups; ifg && ifgr.ifgr_len > 0; ++ifg) {
+ if ((ret = string_list_add(list, ifg->ifgrq_member)) < 0)
+ goto out;
+ ifgr.ifgr_len -= sizeof(struct ifg_req);
+ }
+
+out:
+ free(ifgr.ifgr_groups);
+ return ret;
+}
+
+static int kernel_get_device(struct wgdevice **device, const char *ifname)
+{
+ struct wg_data_io wgd = { 0 };
+ nvlist_t *nvl_device = NULL;
+ const nvlist_t *const *nvl_peers;
+ struct wgdevice *dev = NULL;
+ size_t size, peer_count, i;
+ uint64_t number;
+ const void *binary;
+ int ret = 0, s;
+
+ *device = NULL;
+ s = get_dgram_socket();
+ if (s < 0)
+ goto err;
+
+ strlcpy(wgd.wgd_name, ifname, sizeof(wgd.wgd_name));
+ if (ioctl(s, SIOCGWG, &wgd) < 0)
+ goto err;
+
+ wgd.wgd_data = malloc(wgd.wgd_size);
+ if (!wgd.wgd_data)
+ goto err;
+ if (ioctl(s, SIOCGWG, &wgd) < 0)
+ goto err;
+
+ dev = calloc(1, sizeof(*dev));
+ if (!dev)
+ goto err;
+ strlcpy(dev->name, ifname, sizeof(dev->name));
+ nvl_device = nvlist_unpack(wgd.wgd_data, wgd.wgd_size, 0);
+ if (!nvl_device)
+ goto err;
+
+ if (nvlist_exists_number(nvl_device, "listen-port")) {
+ number = nvlist_get_number(nvl_device, "listen-port");
+ if (number <= UINT16_MAX) {
+ dev->listen_port = number;
+ dev->flags |= WGDEVICE_HAS_LISTEN_PORT;
+ }
+ }
+ if (nvlist_exists_number(nvl_device, "user-cookie")) {
+ number = nvlist_get_number(nvl_device, "user-cookie");
+ if (number <= UINT32_MAX) {
+ dev->fwmark = number;
+ dev->flags |= WGDEVICE_HAS_FWMARK;
+ }
+ }
+ if (nvlist_exists_binary(nvl_device, "public-key")) {
+ binary = nvlist_get_binary(nvl_device, "public-key", &size);
+ if (binary && size == sizeof(dev->public_key)) {
+ memcpy(dev->public_key, binary, sizeof(dev->public_key));
+ dev->flags |= WGDEVICE_HAS_PUBLIC_KEY;
+ }
+ }
+ if (nvlist_exists_binary(nvl_device, "private-key")) {
+ binary = nvlist_get_binary(nvl_device, "private-key", &size);
+ if (binary && size == sizeof(dev->private_key)) {
+ memcpy(dev->private_key, binary, sizeof(dev->private_key));
+ dev->flags |= WGDEVICE_HAS_PRIVATE_KEY;
+ }
+ }
+ if (!nvlist_exists_nvlist_array(nvl_device, "peers"))
+ goto skip_peers;
+ nvl_peers = nvlist_get_nvlist_array(nvl_device, "peers", &peer_count);
+ if (!nvl_peers)
+ goto skip_peers;
+ for (i = 0; i < peer_count; ++i) {
+ struct wgpeer *peer;
+ struct wgallowedip *aip;
+ const nvlist_t *const *nvl_aips;
+ size_t aip_count, j;
+
+ peer = calloc(1, sizeof(*peer));
+ if (!peer)
+ goto err_peer;
+ if (nvlist_exists_binary(nvl_peers[i], "public-key")) {
+ binary = nvlist_get_binary(nvl_peers[i], "public-key", &size);
+ if (binary && size == sizeof(peer->public_key)) {
+ memcpy(peer->public_key, binary, sizeof(peer->public_key));
+ peer->flags |= WGPEER_HAS_PUBLIC_KEY;
+ }
+ }
+ if (nvlist_exists_binary(nvl_peers[i], "preshared-key")) {
+ binary = nvlist_get_binary(nvl_peers[i], "preshared-key", &size);
+ if (binary && size == sizeof(peer->preshared_key)) {
+ memcpy(peer->preshared_key, binary, sizeof(peer->preshared_key));
+ if (!key_is_zero(peer->preshared_key))
+ peer->flags |= WGPEER_HAS_PRESHARED_KEY;
+ }
+ }
+ if (nvlist_exists_number(nvl_peers[i], "persistent-keepalive-interval")) {
+ number = nvlist_get_number(nvl_peers[i], "persistent-keepalive-interval");
+ if (number <= UINT16_MAX) {
+ peer->persistent_keepalive_interval = number;
+ peer->flags |= WGPEER_HAS_PERSISTENT_KEEPALIVE_INTERVAL;
+ }
+ }
+ if (nvlist_exists_binary(nvl_peers[i], "endpoint")) {
+ const struct sockaddr *endpoint = nvlist_get_binary(nvl_peers[i], "endpoint", &size);
+ if (endpoint && size <= sizeof(peer->endpoint) && size >= sizeof(peer->endpoint.addr) &&
+ (endpoint->sa_family == AF_INET || endpoint->sa_family == AF_INET6))
+ memcpy(&peer->endpoint.addr, endpoint, size);
+ }
+ if (nvlist_exists_number(nvl_peers[i], "rx-bytes"))
+ peer->rx_bytes = nvlist_get_number(nvl_peers[i], "rx-bytes");
+ if (nvlist_exists_number(nvl_peers[i], "tx-bytes"))
+ peer->tx_bytes = nvlist_get_number(nvl_peers[i], "tx-bytes");
+ if (nvlist_exists_binary(nvl_peers[i], "last-handshake-time")) {
+ binary = nvlist_get_binary(nvl_peers[i], "last-handshake-time", &size);
+ if (binary && size == sizeof(peer->last_handshake_time))
+ memcpy(&peer->last_handshake_time, binary, sizeof(peer->last_handshake_time));
+ }
+
+ if (!nvlist_exists_nvlist_array(nvl_peers[i], "allowed-ips"))
+ goto skip_allowed_ips;
+ nvl_aips = nvlist_get_nvlist_array(nvl_peers[i], "allowed-ips", &aip_count);
+ if (!aip_count || !nvl_aips)
+ goto skip_allowed_ips;
+ for (j = 0; j < aip_count; ++j) {
+ aip = calloc(1, sizeof(*aip));
+ if (!aip)
+ goto err_allowed_ips;
+ if (!nvlist_exists_number(nvl_aips[j], "cidr"))
+ continue;
+ number = nvlist_get_number(nvl_aips[j], "cidr");
+ if (nvlist_exists_binary(nvl_aips[j], "ipv4")) {
+ binary = nvlist_get_binary(nvl_aips[j], "ipv4", &size);
+ if (!binary || number > 32) {
+ ret = EINVAL;
+ goto err_allowed_ips;
+ }
+ aip->family = AF_INET;
+ aip->cidr = number;
+ memcpy(&aip->ip4, binary, sizeof(aip->ip4));
+ } else if (nvlist_exists_binary(nvl_aips[j], "ipv6")) {
+ binary = nvlist_get_binary(nvl_aips[j], "ipv6", &size);
+ if (!binary || number > 128) {
+ ret = EINVAL;
+ goto err_allowed_ips;
+ }
+ aip->family = AF_INET6;
+ aip->cidr = number;
+ memcpy(&aip->ip6, binary, sizeof(aip->ip6));
+ } else
+ continue;
+
+ if (!peer->first_allowedip)
+ peer->first_allowedip = aip;
+ else
+ peer->last_allowedip->next_allowedip = aip;
+ peer->last_allowedip = aip;
+ continue;
+
+ err_allowed_ips:
+ if (!ret)
+ ret = -errno;
+ free(aip);
+ goto err_peer;
+ }
+ skip_allowed_ips:
+ if (!dev->first_peer)
+ dev->first_peer = peer;
+ else
+ dev->last_peer->next_peer = peer;
+ dev->last_peer = peer;
+ continue;
+
+ err_peer:
+ if (!ret)
+ ret = -errno;
+ free(peer);
+ goto err;
+ }
+
+skip_peers:
+ free(wgd.wgd_data);
+ nvlist_destroy(nvl_device);
+ *device = dev;
+ return 0;
+
+err:
+ if (!ret)
+ ret = -errno;
+ free(wgd.wgd_data);
+ nvlist_destroy(nvl_device);
+ free(dev);
+ return ret;
+}
+
+
+static int kernel_set_device(struct wgdevice *dev)
+{
+ struct wg_data_io wgd = { 0 };
+ nvlist_t *nvl_device = NULL, **nvl_peers = NULL;
+ size_t peer_count = 0, i = 0;
+ struct wgpeer *peer;
+ int ret = 0, s;
+
+ strlcpy(wgd.wgd_name, dev->name, sizeof(wgd.wgd_name));
+
+ nvl_device = nvlist_create(0);
+ if (!nvl_device)
+ goto err;
+
+ for_each_wgpeer(dev, peer)
+ ++peer_count;
+ if (peer_count) {
+ nvl_peers = calloc(peer_count, sizeof(*nvl_peers));
+ if (!nvl_peers)
+ goto err;
+ }
+ if (dev->flags & WGDEVICE_HAS_PRIVATE_KEY)
+ nvlist_add_binary(nvl_device, "private-key", dev->private_key, sizeof(dev->private_key));
+ if (dev->flags & WGDEVICE_HAS_LISTEN_PORT)
+ nvlist_add_number(nvl_device, "listen-port", dev->listen_port);
+ if (dev->flags & WGDEVICE_HAS_FWMARK)
+ nvlist_add_number(nvl_device, "user-cookie", dev->fwmark);
+ if (dev->flags & WGDEVICE_REPLACE_PEERS)
+ nvlist_add_bool(nvl_device, "replace-peers", true);
+
+ for_each_wgpeer(dev, peer) {
+ size_t aip_count = 0, j = 0;
+ nvlist_t **nvl_aips = NULL;
+ struct wgallowedip *aip;
+
+ nvl_peers[i] = nvlist_create(0);
+ if (!nvl_peers[i])
+ goto err_peer;
+ for_each_wgallowedip(peer, aip)
+ ++aip_count;
+ if (aip_count) {
+ nvl_aips = calloc(aip_count, sizeof(*nvl_aips));
+ if (!nvl_aips)
+ goto err_peer;
+ }
+ nvlist_add_binary(nvl_peers[i], "public-key", peer->public_key, sizeof(peer->public_key));
+ if (peer->flags & WGPEER_HAS_PRESHARED_KEY)
+ nvlist_add_binary(nvl_peers[i], "preshared-key", peer->preshared_key, sizeof(peer->preshared_key));
+ if (peer->flags & WGPEER_HAS_PERSISTENT_KEEPALIVE_INTERVAL)
+ nvlist_add_number(nvl_peers[i], "persistent-keepalive-interval", peer->persistent_keepalive_interval);
+ if (peer->endpoint.addr.sa_family == AF_INET || peer->endpoint.addr.sa_family == AF_INET6)
+ nvlist_add_binary(nvl_peers[i], "endpoint", &peer->endpoint.addr, peer->endpoint.addr.sa_len);
+ if (peer->flags & WGPEER_REPLACE_ALLOWEDIPS)
+ nvlist_add_bool(nvl_peers[i], "replace-allowedips", true);
+ if (peer->flags & WGPEER_REMOVE_ME)
+ nvlist_add_bool(nvl_peers[i], "remove", true);
+ for_each_wgallowedip(peer, aip) {
+ nvl_aips[j] = nvlist_create(0);
+ if (!nvl_aips[j])
+ goto err_peer;
+ nvlist_add_number(nvl_aips[j], "cidr", aip->cidr);
+ if (aip->family == AF_INET)
+ nvlist_add_binary(nvl_aips[j], "ipv4", &aip->ip4, sizeof(aip->ip4));
+ else if (aip->family == AF_INET6)
+ nvlist_add_binary(nvl_aips[j], "ipv6", &aip->ip6, sizeof(aip->ip6));
+ ++j;
+ }
+ if (j) {
+ nvlist_add_nvlist_array(nvl_peers[i], "allowed-ips", (const nvlist_t *const *)nvl_aips, j);
+ for (j = 0; j < aip_count; ++j)
+ nvlist_destroy(nvl_aips[j]);
+ free(nvl_aips);
+ }
+ ++i;
+ continue;
+
+ err_peer:
+ ret = -errno;
+ for (j = 0; j < aip_count && nvl_aips; ++j)
+ nvlist_destroy(nvl_aips[j]);
+ free(nvl_aips);
+ nvlist_destroy(nvl_peers[i]);
+ goto err;
+ }
+ if (i) {
+ nvlist_add_nvlist_array(nvl_device, "peers", (const nvlist_t *const *)nvl_peers, i);
+ for (i = 0; i < peer_count; ++i)
+ nvlist_destroy(nvl_peers[i]);
+ free(nvl_peers);
+ }
+ wgd.wgd_data = nvlist_pack(nvl_device, &wgd.wgd_size);
+ nvlist_destroy(nvl_device);
+ if (!wgd.wgd_data)
+ goto err;
+ s = get_dgram_socket();
+ if (s < 0)
+ return -errno;
+ return ioctl(s, SIOCSWG, &wgd);
+
+err:
+ if (!ret)
+ ret = -errno;
+ for (i = 0; i < peer_count && nvl_peers; ++i)
+ nvlist_destroy(nvl_peers[i]);
+ free(nvl_peers);
+ nvlist_destroy(nvl_device);
+ return ret;
+}
diff --git a/contrib/wireguard-tools/src/ipc-linux.h b/contrib/wireguard-tools/src/ipc-linux.h
new file mode 100644
--- /dev/null
+++ b/contrib/wireguard-tools/src/ipc-linux.h
@@ -0,0 +1,519 @@
+// SPDX-License-Identifier: MIT
+/*
+ * Copyright (C) 2015-2020 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
+ */
+
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+#include <time.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <linux/genetlink.h>
+#include <linux/if_link.h>
+#include <linux/netlink.h>
+#include <linux/rtnetlink.h>
+#include <linux/wireguard.h>
+#include <netinet/in.h>
+#include "containers.h"
+#include "encoding.h"
+#include "netlink.h"
+
+#define IPC_SUPPORTS_KERNEL_INTERFACE
+
+#define SOCKET_BUFFER_SIZE (mnl_ideal_socket_buffer_size())
+
+struct interface {
+ const char *name;
+ bool is_wireguard;
+};
+
+static int parse_linkinfo(const struct nlattr *attr, void *data)
+{
+ struct interface *interface = data;
+
+ if (mnl_attr_get_type(attr) == IFLA_INFO_KIND && !strcmp(WG_GENL_NAME, mnl_attr_get_str(attr)))
+ interface->is_wireguard = true;
+ return MNL_CB_OK;
+}
+
+static int parse_infomsg(const struct nlattr *attr, void *data)
+{
+ struct interface *interface = data;
+
+ if (mnl_attr_get_type(attr) == IFLA_LINKINFO)
+ return mnl_attr_parse_nested(attr, parse_linkinfo, data);
+ else if (mnl_attr_get_type(attr) == IFLA_IFNAME)
+ interface->name = mnl_attr_get_str(attr);
+ return MNL_CB_OK;
+}
+
+static int read_devices_cb(const struct nlmsghdr *nlh, void *data)
+{
+ struct string_list *list = data;
+ struct interface interface = { 0 };
+ int ret;
+
+ ret = mnl_attr_parse(nlh, sizeof(struct ifinfomsg), parse_infomsg, &interface);
+ if (ret != MNL_CB_OK)
+ return ret;
+ if (interface.name && interface.is_wireguard)
+ ret = string_list_add(list, interface.name);
+ if (ret < 0)
+ return ret;
+ if (nlh->nlmsg_type != NLMSG_DONE)
+ return MNL_CB_OK + 1;
+ return MNL_CB_OK;
+}
+
+static int kernel_get_wireguard_interfaces(struct string_list *list)
+{
+ struct mnl_socket *nl = NULL;
+ char *rtnl_buffer = NULL;
+ size_t message_len;
+ unsigned int portid, seq;
+ ssize_t len;
+ int ret = 0;
+ struct nlmsghdr *nlh;
+ struct ifinfomsg *ifm;
+
+ ret = -ENOMEM;
+ rtnl_buffer = calloc(SOCKET_BUFFER_SIZE, 1);
+ if (!rtnl_buffer)
+ goto cleanup;
+
+ nl = mnl_socket_open(NETLINK_ROUTE);
+ if (!nl) {
+ ret = -errno;
+ goto cleanup;
+ }
+
+ if (mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID) < 0) {
+ ret = -errno;
+ goto cleanup;
+ }
+
+ seq = time(NULL);
+ portid = mnl_socket_get_portid(nl);
+ nlh = mnl_nlmsg_put_header(rtnl_buffer);
+ nlh->nlmsg_type = RTM_GETLINK;
+ nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK | NLM_F_DUMP;
+ nlh->nlmsg_seq = seq;
+ ifm = mnl_nlmsg_put_extra_header(nlh, sizeof(*ifm));
+ ifm->ifi_family = AF_UNSPEC;
+ message_len = nlh->nlmsg_len;
+
+ if (mnl_socket_sendto(nl, rtnl_buffer, message_len) < 0) {
+ ret = -errno;
+ goto cleanup;
+ }
+
+another:
+ if ((len = mnl_socket_recvfrom(nl, rtnl_buffer, SOCKET_BUFFER_SIZE)) < 0) {
+ ret = -errno;
+ goto cleanup;
+ }
+ if ((len = mnl_cb_run(rtnl_buffer, len, seq, portid, read_devices_cb, list)) < 0) {
+ /* Netlink returns NLM_F_DUMP_INTR if the set of all tunnels changed
+ * during the dump. That's unfortunate, but is pretty common on busy
+ * systems that are adding and removing tunnels all the time. Rather
+ * than retrying, potentially indefinitely, we just work with the
+ * partial results. */
+ if (errno != EINTR) {
+ ret = -errno;
+ goto cleanup;
+ }
+ }
+ if (len == MNL_CB_OK + 1)
+ goto another;
+ ret = 0;
+
+cleanup:
+ free(rtnl_buffer);
+ if (nl)
+ mnl_socket_close(nl);
+ return ret;
+}
+
+static int kernel_set_device(struct wgdevice *dev)
+{
+ int ret = 0;
+ struct wgpeer *peer = NULL;
+ struct wgallowedip *allowedip = NULL;
+ struct nlattr *peers_nest, *peer_nest, *allowedips_nest, *allowedip_nest;
+ struct nlmsghdr *nlh;
+ struct mnlg_socket *nlg;
+
+ nlg = mnlg_socket_open(WG_GENL_NAME, WG_GENL_VERSION);
+ if (!nlg)
+ return -errno;
+
+again:
+ nlh = mnlg_msg_prepare(nlg, WG_CMD_SET_DEVICE, NLM_F_REQUEST | NLM_F_ACK);
+ mnl_attr_put_strz(nlh, WGDEVICE_A_IFNAME, dev->name);
+
+ if (!peer) {
+ uint32_t flags = 0;
+
+ if (dev->flags & WGDEVICE_HAS_PRIVATE_KEY)
+ mnl_attr_put(nlh, WGDEVICE_A_PRIVATE_KEY, sizeof(dev->private_key), dev->private_key);
+ if (dev->flags & WGDEVICE_HAS_LISTEN_PORT)
+ mnl_attr_put_u16(nlh, WGDEVICE_A_LISTEN_PORT, dev->listen_port);
+ if (dev->flags & WGDEVICE_HAS_FWMARK)
+ mnl_attr_put_u32(nlh, WGDEVICE_A_FWMARK, dev->fwmark);
+ if (dev->flags & WGDEVICE_REPLACE_PEERS)
+ flags |= WGDEVICE_F_REPLACE_PEERS;
+ if (flags)
+ mnl_attr_put_u32(nlh, WGDEVICE_A_FLAGS, flags);
+ }
+ if (!dev->first_peer)
+ goto send;
+ peers_nest = peer_nest = allowedips_nest = allowedip_nest = NULL;
+ peers_nest = mnl_attr_nest_start(nlh, WGDEVICE_A_PEERS);
+ for (peer = peer ? peer : dev->first_peer; peer; peer = peer->next_peer) {
+ uint32_t flags = 0;
+
+ peer_nest = mnl_attr_nest_start_check(nlh, SOCKET_BUFFER_SIZE, 0);
+ if (!peer_nest)
+ goto toobig_peers;
+ if (!mnl_attr_put_check(nlh, SOCKET_BUFFER_SIZE, WGPEER_A_PUBLIC_KEY, sizeof(peer->public_key), peer->public_key))
+ goto toobig_peers;
+ if (peer->flags & WGPEER_REMOVE_ME)
+ flags |= WGPEER_F_REMOVE_ME;
+ if (!allowedip) {
+ if (peer->flags & WGPEER_REPLACE_ALLOWEDIPS)
+ flags |= WGPEER_F_REPLACE_ALLOWEDIPS;
+ if (peer->flags & WGPEER_HAS_PRESHARED_KEY) {
+ if (!mnl_attr_put_check(nlh, SOCKET_BUFFER_SIZE, WGPEER_A_PRESHARED_KEY, sizeof(peer->preshared_key), peer->preshared_key))
+ goto toobig_peers;
+ }
+ if (peer->endpoint.addr.sa_family == AF_INET) {
+ if (!mnl_attr_put_check(nlh, SOCKET_BUFFER_SIZE, WGPEER_A_ENDPOINT, sizeof(peer->endpoint.addr4), &peer->endpoint.addr4))
+ goto toobig_peers;
+ } else if (peer->endpoint.addr.sa_family == AF_INET6) {
+ if (!mnl_attr_put_check(nlh, SOCKET_BUFFER_SIZE, WGPEER_A_ENDPOINT, sizeof(peer->endpoint.addr6), &peer->endpoint.addr6))
+ goto toobig_peers;
+ }
+ if (peer->flags & WGPEER_HAS_PERSISTENT_KEEPALIVE_INTERVAL) {
+ if (!mnl_attr_put_u16_check(nlh, SOCKET_BUFFER_SIZE, WGPEER_A_PERSISTENT_KEEPALIVE_INTERVAL, peer->persistent_keepalive_interval))
+ goto toobig_peers;
+ }
+ }
+ if (flags) {
+ if (!mnl_attr_put_u32_check(nlh, SOCKET_BUFFER_SIZE, WGPEER_A_FLAGS, flags))
+ goto toobig_peers;
+ }
+ if (peer->first_allowedip) {
+ if (!allowedip)
+ allowedip = peer->first_allowedip;
+ allowedips_nest = mnl_attr_nest_start_check(nlh, SOCKET_BUFFER_SIZE, WGPEER_A_ALLOWEDIPS);
+ if (!allowedips_nest)
+ goto toobig_allowedips;
+ for (; allowedip; allowedip = allowedip->next_allowedip) {
+ allowedip_nest = mnl_attr_nest_start_check(nlh, SOCKET_BUFFER_SIZE, 0);
+ if (!allowedip_nest)
+ goto toobig_allowedips;
+ if (!mnl_attr_put_u16_check(nlh, SOCKET_BUFFER_SIZE, WGALLOWEDIP_A_FAMILY, allowedip->family))
+ goto toobig_allowedips;
+ if (allowedip->family == AF_INET) {
+ if (!mnl_attr_put_check(nlh, SOCKET_BUFFER_SIZE, WGALLOWEDIP_A_IPADDR, sizeof(allowedip->ip4), &allowedip->ip4))
+ goto toobig_allowedips;
+ } else if (allowedip->family == AF_INET6) {
+ if (!mnl_attr_put_check(nlh, SOCKET_BUFFER_SIZE, WGALLOWEDIP_A_IPADDR, sizeof(allowedip->ip6), &allowedip->ip6))
+ goto toobig_allowedips;
+ }
+ if (!mnl_attr_put_u8_check(nlh, SOCKET_BUFFER_SIZE, WGALLOWEDIP_A_CIDR_MASK, allowedip->cidr))
+ goto toobig_allowedips;
+ mnl_attr_nest_end(nlh, allowedip_nest);
+ allowedip_nest = NULL;
+ }
+ mnl_attr_nest_end(nlh, allowedips_nest);
+ allowedips_nest = NULL;
+ }
+
+ mnl_attr_nest_end(nlh, peer_nest);
+ peer_nest = NULL;
+ }
+ mnl_attr_nest_end(nlh, peers_nest);
+ peers_nest = NULL;
+ goto send;
+toobig_allowedips:
+ if (allowedip_nest)
+ mnl_attr_nest_cancel(nlh, allowedip_nest);
+ if (allowedips_nest)
+ mnl_attr_nest_end(nlh, allowedips_nest);
+ mnl_attr_nest_end(nlh, peer_nest);
+ mnl_attr_nest_end(nlh, peers_nest);
+ goto send;
+toobig_peers:
+ if (peer_nest)
+ mnl_attr_nest_cancel(nlh, peer_nest);
+ mnl_attr_nest_end(nlh, peers_nest);
+ goto send;
+send:
+ if (mnlg_socket_send(nlg, nlh) < 0) {
+ ret = -errno;
+ goto out;
+ }
+ errno = 0;
+ if (mnlg_socket_recv_run(nlg, NULL, NULL) < 0) {
+ ret = errno ? -errno : -EINVAL;
+ goto out;
+ }
+ if (peer)
+ goto again;
+
+out:
+ mnlg_socket_close(nlg);
+ errno = -ret;
+ return ret;
+}
+
+static int parse_allowedip(const struct nlattr *attr, void *data)
+{
+ struct wgallowedip *allowedip = data;
+
+ switch (mnl_attr_get_type(attr)) {
+ case WGALLOWEDIP_A_UNSPEC:
+ break;
+ case WGALLOWEDIP_A_FAMILY:
+ if (!mnl_attr_validate(attr, MNL_TYPE_U16))
+ allowedip->family = mnl_attr_get_u16(attr);
+ break;
+ case WGALLOWEDIP_A_IPADDR:
+ if (mnl_attr_get_payload_len(attr) == sizeof(allowedip->ip4))
+ memcpy(&allowedip->ip4, mnl_attr_get_payload(attr), sizeof(allowedip->ip4));
+ else if (mnl_attr_get_payload_len(attr) == sizeof(allowedip->ip6))
+ memcpy(&allowedip->ip6, mnl_attr_get_payload(attr), sizeof(allowedip->ip6));
+ break;
+ case WGALLOWEDIP_A_CIDR_MASK:
+ if (!mnl_attr_validate(attr, MNL_TYPE_U8))
+ allowedip->cidr = mnl_attr_get_u8(attr);
+ break;
+ }
+
+ return MNL_CB_OK;
+}
+
+static int parse_allowedips(const struct nlattr *attr, void *data)
+{
+ struct wgpeer *peer = data;
+ struct wgallowedip *new_allowedip = calloc(1, sizeof(*new_allowedip));
+ int ret;
+
+ if (!new_allowedip) {
+ perror("calloc");
+ return MNL_CB_ERROR;
+ }
+ if (!peer->first_allowedip)
+ peer->first_allowedip = peer->last_allowedip = new_allowedip;
+ else {
+ peer->last_allowedip->next_allowedip = new_allowedip;
+ peer->last_allowedip = new_allowedip;
+ }
+ ret = mnl_attr_parse_nested(attr, parse_allowedip, new_allowedip);
+ if (!ret)
+ return ret;
+ if (!((new_allowedip->family == AF_INET && new_allowedip->cidr <= 32) || (new_allowedip->family == AF_INET6 && new_allowedip->cidr <= 128)))
+ return MNL_CB_ERROR;
+ return MNL_CB_OK;
+}
+
+static int parse_peer(const struct nlattr *attr, void *data)
+{
+ struct wgpeer *peer = data;
+
+ switch (mnl_attr_get_type(attr)) {
+ case WGPEER_A_UNSPEC:
+ break;
+ case WGPEER_A_PUBLIC_KEY:
+ if (mnl_attr_get_payload_len(attr) == sizeof(peer->public_key)) {
+ memcpy(peer->public_key, mnl_attr_get_payload(attr), sizeof(peer->public_key));
+ peer->flags |= WGPEER_HAS_PUBLIC_KEY;
+ }
+ break;
+ case WGPEER_A_PRESHARED_KEY:
+ if (mnl_attr_get_payload_len(attr) == sizeof(peer->preshared_key)) {
+ memcpy(peer->preshared_key, mnl_attr_get_payload(attr), sizeof(peer->preshared_key));
+ if (!key_is_zero(peer->preshared_key))
+ peer->flags |= WGPEER_HAS_PRESHARED_KEY;
+ }
+ break;
+ case WGPEER_A_ENDPOINT: {
+ struct sockaddr *addr;
+
+ if (mnl_attr_get_payload_len(attr) < sizeof(*addr))
+ break;
+ addr = mnl_attr_get_payload(attr);
+ if (addr->sa_family == AF_INET && mnl_attr_get_payload_len(attr) == sizeof(peer->endpoint.addr4))
+ memcpy(&peer->endpoint.addr4, addr, sizeof(peer->endpoint.addr4));
+ else if (addr->sa_family == AF_INET6 && mnl_attr_get_payload_len(attr) == sizeof(peer->endpoint.addr6))
+ memcpy(&peer->endpoint.addr6, addr, sizeof(peer->endpoint.addr6));
+ break;
+ }
+ case WGPEER_A_PERSISTENT_KEEPALIVE_INTERVAL:
+ if (!mnl_attr_validate(attr, MNL_TYPE_U16))
+ peer->persistent_keepalive_interval = mnl_attr_get_u16(attr);
+ break;
+ case WGPEER_A_LAST_HANDSHAKE_TIME:
+ if (mnl_attr_get_payload_len(attr) == sizeof(peer->last_handshake_time))
+ memcpy(&peer->last_handshake_time, mnl_attr_get_payload(attr), sizeof(peer->last_handshake_time));
+ break;
+ case WGPEER_A_RX_BYTES:
+ if (!mnl_attr_validate(attr, MNL_TYPE_U64))
+ peer->rx_bytes = mnl_attr_get_u64(attr);
+ break;
+ case WGPEER_A_TX_BYTES:
+ if (!mnl_attr_validate(attr, MNL_TYPE_U64))
+ peer->tx_bytes = mnl_attr_get_u64(attr);
+ break;
+ case WGPEER_A_ALLOWEDIPS:
+ return mnl_attr_parse_nested(attr, parse_allowedips, peer);
+ }
+
+ return MNL_CB_OK;
+}
+
+static int parse_peers(const struct nlattr *attr, void *data)
+{
+ struct wgdevice *device = data;
+ struct wgpeer *new_peer = calloc(1, sizeof(*new_peer));
+ int ret;
+
+ if (!new_peer) {
+ perror("calloc");
+ return MNL_CB_ERROR;
+ }
+ if (!device->first_peer)
+ device->first_peer = device->last_peer = new_peer;
+ else {
+ device->last_peer->next_peer = new_peer;
+ device->last_peer = new_peer;
+ }
+ ret = mnl_attr_parse_nested(attr, parse_peer, new_peer);
+ if (!ret)
+ return ret;
+ if (!(new_peer->flags & WGPEER_HAS_PUBLIC_KEY))
+ return MNL_CB_ERROR;
+ return MNL_CB_OK;
+}
+
+static int parse_device(const struct nlattr *attr, void *data)
+{
+ struct wgdevice *device = data;
+
+ switch (mnl_attr_get_type(attr)) {
+ case WGDEVICE_A_UNSPEC:
+ break;
+ case WGDEVICE_A_IFINDEX:
+ if (!mnl_attr_validate(attr, MNL_TYPE_U32))
+ device->ifindex = mnl_attr_get_u32(attr);
+ break;
+ case WGDEVICE_A_IFNAME:
+ if (!mnl_attr_validate(attr, MNL_TYPE_STRING)) {
+ strncpy(device->name, mnl_attr_get_str(attr), sizeof(device->name) - 1);
+ device->name[sizeof(device->name) - 1] = '\0';
+ }
+ break;
+ case WGDEVICE_A_PRIVATE_KEY:
+ if (mnl_attr_get_payload_len(attr) == sizeof(device->private_key)) {
+ memcpy(device->private_key, mnl_attr_get_payload(attr), sizeof(device->private_key));
+ device->flags |= WGDEVICE_HAS_PRIVATE_KEY;
+ }
+ break;
+ case WGDEVICE_A_PUBLIC_KEY:
+ if (mnl_attr_get_payload_len(attr) == sizeof(device->public_key)) {
+ memcpy(device->public_key, mnl_attr_get_payload(attr), sizeof(device->public_key));
+ device->flags |= WGDEVICE_HAS_PUBLIC_KEY;
+ }
+ break;
+ case WGDEVICE_A_LISTEN_PORT:
+ if (!mnl_attr_validate(attr, MNL_TYPE_U16))
+ device->listen_port = mnl_attr_get_u16(attr);
+ break;
+ case WGDEVICE_A_FWMARK:
+ if (!mnl_attr_validate(attr, MNL_TYPE_U32))
+ device->fwmark = mnl_attr_get_u32(attr);
+ break;
+ case WGDEVICE_A_PEERS:
+ return mnl_attr_parse_nested(attr, parse_peers, device);
+ }
+
+ return MNL_CB_OK;
+}
+
+static int read_device_cb(const struct nlmsghdr *nlh, void *data)
+{
+ return mnl_attr_parse(nlh, sizeof(struct genlmsghdr), parse_device, data);
+}
+
+static void coalesce_peers(struct wgdevice *device)
+{
+ struct wgpeer *old_next_peer, *peer = device->first_peer;
+
+ while (peer && peer->next_peer) {
+ if (memcmp(peer->public_key, peer->next_peer->public_key, sizeof(peer->public_key))) {
+ peer = peer->next_peer;
+ continue;
+ }
+ if (!peer->first_allowedip) {
+ peer->first_allowedip = peer->next_peer->first_allowedip;
+ peer->last_allowedip = peer->next_peer->last_allowedip;
+ } else {
+ peer->last_allowedip->next_allowedip = peer->next_peer->first_allowedip;
+ peer->last_allowedip = peer->next_peer->last_allowedip;
+ }
+ old_next_peer = peer->next_peer;
+ peer->next_peer = old_next_peer->next_peer;
+ free(old_next_peer);
+ }
+}
+
+static int kernel_get_device(struct wgdevice **device, const char *iface)
+{
+ int ret;
+ struct nlmsghdr *nlh;
+ struct mnlg_socket *nlg;
+
+try_again:
+ ret = 0;
+ *device = calloc(1, sizeof(**device));
+ if (!*device)
+ return -errno;
+
+ nlg = mnlg_socket_open(WG_GENL_NAME, WG_GENL_VERSION);
+ if (!nlg) {
+ free_wgdevice(*device);
+ *device = NULL;
+ return -errno;
+ }
+
+ nlh = mnlg_msg_prepare(nlg, WG_CMD_GET_DEVICE, NLM_F_REQUEST | NLM_F_ACK | NLM_F_DUMP);
+ mnl_attr_put_strz(nlh, WGDEVICE_A_IFNAME, iface);
+ if (mnlg_socket_send(nlg, nlh) < 0) {
+ ret = -errno;
+ goto out;
+ }
+ errno = 0;
+ if (mnlg_socket_recv_run(nlg, read_device_cb, *device) < 0) {
+ ret = errno ? -errno : -EINVAL;
+ goto out;
+ }
+ coalesce_peers(*device);
+
+out:
+ if (nlg)
+ mnlg_socket_close(nlg);
+ if (ret) {
+ free_wgdevice(*device);
+ if (ret == -EINTR)
+ goto try_again;
+ *device = NULL;
+ }
+ errno = -ret;
+ return ret;
+}
diff --git a/contrib/wireguard-tools/src/ipc-openbsd.h b/contrib/wireguard-tools/src/ipc-openbsd.h
new file mode 100644
--- /dev/null
+++ b/contrib/wireguard-tools/src/ipc-openbsd.h
@@ -0,0 +1,281 @@
+// SPDX-License-Identifier: MIT
+/*
+ * Copyright (C) 2015-2020 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
+ */
+
+#include <errno.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <sys/sockio.h>
+#include <sys/types.h>
+#include <net/if.h>
+#include <net/if_wg.h>
+#include <netinet/in.h>
+#include "containers.h"
+
+#define IPC_SUPPORTS_KERNEL_INTERFACE
+
+static int get_dgram_socket(void)
+{
+ static int sock = -1;
+ if (sock < 0)
+ sock = socket(AF_INET, SOCK_DGRAM, 0);
+ return sock;
+}
+
+static int kernel_get_wireguard_interfaces(struct string_list *list)
+{
+ struct ifgroupreq ifgr = { .ifgr_name = "wg" };
+ struct ifg_req *ifg;
+ int s = get_dgram_socket(), ret = 0;
+
+ if (s < 0)
+ return -errno;
+
+ if (ioctl(s, SIOCGIFGMEMB, (caddr_t)&ifgr) < 0)
+ return errno == ENOENT ? 0 : -errno;
+
+ ifgr.ifgr_groups = calloc(1, ifgr.ifgr_len);
+ if (!ifgr.ifgr_groups)
+ return -errno;
+ if (ioctl(s, SIOCGIFGMEMB, (caddr_t)&ifgr) < 0) {
+ ret = -errno;
+ goto out;
+ }
+
+ for (ifg = ifgr.ifgr_groups; ifg && ifgr.ifgr_len > 0; ++ifg) {
+ if ((ret = string_list_add(list, ifg->ifgrq_member)) < 0)
+ goto out;
+ ifgr.ifgr_len -= sizeof(struct ifg_req);
+ }
+
+out:
+ free(ifgr.ifgr_groups);
+ return ret;
+}
+
+static int kernel_get_device(struct wgdevice **device, const char *iface)
+{
+ struct wg_data_io wgdata = { .wgd_size = 0 };
+ struct wg_interface_io *wg_iface;
+ struct wg_peer_io *wg_peer;
+ struct wg_aip_io *wg_aip;
+ struct wgdevice *dev;
+ struct wgpeer *peer;
+ struct wgallowedip *aip;
+ int s = get_dgram_socket(), ret;
+
+ if (s < 0)
+ return -errno;
+
+ *device = NULL;
+ strlcpy(wgdata.wgd_name, iface, sizeof(wgdata.wgd_name));
+ for (size_t last_size = wgdata.wgd_size;; last_size = wgdata.wgd_size) {
+ if (ioctl(s, SIOCGWG, (caddr_t)&wgdata) < 0)
+ goto out;
+ if (last_size >= wgdata.wgd_size)
+ break;
+ wgdata.wgd_interface = realloc(wgdata.wgd_interface, wgdata.wgd_size);
+ if (!wgdata.wgd_interface)
+ goto out;
+ }
+
+ wg_iface = wgdata.wgd_interface;
+ dev = calloc(1, sizeof(*dev));
+ if (!dev)
+ goto out;
+ strlcpy(dev->name, iface, sizeof(dev->name));
+
+ if (wg_iface->i_flags & WG_INTERFACE_HAS_RTABLE) {
+ dev->fwmark = wg_iface->i_rtable;
+ dev->flags |= WGDEVICE_HAS_FWMARK;
+ }
+
+ if (wg_iface->i_flags & WG_INTERFACE_HAS_PORT) {
+ dev->listen_port = wg_iface->i_port;
+ dev->flags |= WGDEVICE_HAS_LISTEN_PORT;
+ }
+
+ if (wg_iface->i_flags & WG_INTERFACE_HAS_PUBLIC) {
+ memcpy(dev->public_key, wg_iface->i_public, sizeof(dev->public_key));
+ dev->flags |= WGDEVICE_HAS_PUBLIC_KEY;
+ }
+
+ if (wg_iface->i_flags & WG_INTERFACE_HAS_PRIVATE) {
+ memcpy(dev->private_key, wg_iface->i_private, sizeof(dev->private_key));
+ dev->flags |= WGDEVICE_HAS_PRIVATE_KEY;
+ }
+
+ wg_peer = &wg_iface->i_peers[0];
+ for (size_t i = 0; i < wg_iface->i_peers_count; ++i) {
+ peer = calloc(1, sizeof(*peer));
+ if (!peer)
+ goto out;
+
+ if (dev->first_peer == NULL)
+ dev->first_peer = peer;
+ else
+ dev->last_peer->next_peer = peer;
+ dev->last_peer = peer;
+
+ if (wg_peer->p_flags & WG_PEER_HAS_PUBLIC) {
+ memcpy(peer->public_key, wg_peer->p_public, sizeof(peer->public_key));
+ peer->flags |= WGPEER_HAS_PUBLIC_KEY;
+ }
+
+ if (wg_peer->p_flags & WG_PEER_HAS_PSK) {
+ memcpy(peer->preshared_key, wg_peer->p_psk, sizeof(peer->preshared_key));
+ if (!key_is_zero(peer->preshared_key))
+ peer->flags |= WGPEER_HAS_PRESHARED_KEY;
+ }
+
+ if (wg_peer->p_flags & WG_PEER_HAS_PKA) {
+ peer->persistent_keepalive_interval = wg_peer->p_pka;
+ peer->flags |= WGPEER_HAS_PERSISTENT_KEEPALIVE_INTERVAL;
+ }
+
+ if (wg_peer->p_flags & WG_PEER_HAS_ENDPOINT && wg_peer->p_sa.sa_len <= sizeof(peer->endpoint.addr))
+ memcpy(&peer->endpoint.addr, &wg_peer->p_sa, wg_peer->p_sa.sa_len);
+
+ peer->rx_bytes = wg_peer->p_rxbytes;
+ peer->tx_bytes = wg_peer->p_txbytes;
+
+ peer->last_handshake_time.tv_sec = wg_peer->p_last_handshake.tv_sec;
+ peer->last_handshake_time.tv_nsec = wg_peer->p_last_handshake.tv_nsec;
+
+ wg_aip = &wg_peer->p_aips[0];
+ for (size_t j = 0; j < wg_peer->p_aips_count; ++j) {
+ aip = calloc(1, sizeof(*aip));
+ if (!aip)
+ goto out;
+
+ if (peer->first_allowedip == NULL)
+ peer->first_allowedip = aip;
+ else
+ peer->last_allowedip->next_allowedip = aip;
+ peer->last_allowedip = aip;
+
+ aip->family = wg_aip->a_af;
+ if (wg_aip->a_af == AF_INET) {
+ memcpy(&aip->ip4, &wg_aip->a_ipv4, sizeof(aip->ip4));
+ aip->cidr = wg_aip->a_cidr;
+ } else if (wg_aip->a_af == AF_INET6) {
+ memcpy(&aip->ip6, &wg_aip->a_ipv6, sizeof(aip->ip6));
+ aip->cidr = wg_aip->a_cidr;
+ }
+ ++wg_aip;
+ }
+ wg_peer = (struct wg_peer_io *)wg_aip;
+ }
+ *device = dev;
+ errno = 0;
+out:
+ ret = -errno;
+ free(wgdata.wgd_interface);
+ return ret;
+}
+
+static int kernel_set_device(struct wgdevice *dev)
+{
+ struct wg_data_io wgdata = { .wgd_size = sizeof(struct wg_interface_io) };
+ struct wg_interface_io *wg_iface;
+ struct wg_peer_io *wg_peer;
+ struct wg_aip_io *wg_aip;
+ struct wgpeer *peer;
+ struct wgallowedip *aip;
+ int s = get_dgram_socket(), ret;
+ size_t peer_count, aip_count;
+
+ if (s < 0)
+ return -errno;
+
+ for_each_wgpeer(dev, peer) {
+ wgdata.wgd_size += sizeof(struct wg_peer_io);
+ for_each_wgallowedip(peer, aip)
+ wgdata.wgd_size += sizeof(struct wg_aip_io);
+ }
+ wg_iface = wgdata.wgd_interface = calloc(1, wgdata.wgd_size);
+ if (!wgdata.wgd_interface)
+ return -errno;
+ strlcpy(wgdata.wgd_name, dev->name, sizeof(wgdata.wgd_name));
+
+ if (dev->flags & WGDEVICE_HAS_PRIVATE_KEY) {
+ memcpy(wg_iface->i_private, dev->private_key, sizeof(wg_iface->i_private));
+ wg_iface->i_flags |= WG_INTERFACE_HAS_PRIVATE;
+ }
+
+ if (dev->flags & WGDEVICE_HAS_LISTEN_PORT) {
+ wg_iface->i_port = dev->listen_port;
+ wg_iface->i_flags |= WG_INTERFACE_HAS_PORT;
+ }
+
+ if (dev->flags & WGDEVICE_HAS_FWMARK) {
+ wg_iface->i_rtable = dev->fwmark;
+ wg_iface->i_flags |= WG_INTERFACE_HAS_RTABLE;
+ }
+
+ if (dev->flags & WGDEVICE_REPLACE_PEERS)
+ wg_iface->i_flags |= WG_INTERFACE_REPLACE_PEERS;
+
+ peer_count = 0;
+ wg_peer = &wg_iface->i_peers[0];
+ for_each_wgpeer(dev, peer) {
+ wg_peer->p_flags = WG_PEER_HAS_PUBLIC;
+ memcpy(wg_peer->p_public, peer->public_key, sizeof(wg_peer->p_public));
+
+ if (peer->flags & WGPEER_HAS_PRESHARED_KEY) {
+ memcpy(wg_peer->p_psk, peer->preshared_key, sizeof(wg_peer->p_psk));
+ wg_peer->p_flags |= WG_PEER_HAS_PSK;
+ }
+
+ if (peer->flags & WGPEER_HAS_PERSISTENT_KEEPALIVE_INTERVAL) {
+ wg_peer->p_pka = peer->persistent_keepalive_interval;
+ wg_peer->p_flags |= WG_PEER_HAS_PKA;
+ }
+
+ if ((peer->endpoint.addr.sa_family == AF_INET || peer->endpoint.addr.sa_family == AF_INET6) &&
+ peer->endpoint.addr.sa_len <= sizeof(wg_peer->p_endpoint)) {
+ memcpy(&wg_peer->p_endpoint, &peer->endpoint.addr, peer->endpoint.addr.sa_len);
+ wg_peer->p_flags |= WG_PEER_HAS_ENDPOINT;
+ }
+
+ if (peer->flags & WGPEER_REPLACE_ALLOWEDIPS)
+ wg_peer->p_flags |= WG_PEER_REPLACE_AIPS;
+
+ if (peer->flags & WGPEER_REMOVE_ME)
+ wg_peer->p_flags |= WG_PEER_REMOVE;
+
+ aip_count = 0;
+ wg_aip = &wg_peer->p_aips[0];
+ for_each_wgallowedip(peer, aip) {
+ wg_aip->a_af = aip->family;
+ wg_aip->a_cidr = aip->cidr;
+
+ if (aip->family == AF_INET)
+ memcpy(&wg_aip->a_ipv4, &aip->ip4, sizeof(wg_aip->a_ipv4));
+ else if (aip->family == AF_INET6)
+ memcpy(&wg_aip->a_ipv6, &aip->ip6, sizeof(wg_aip->a_ipv6));
+ else
+ continue;
+ ++aip_count;
+ ++wg_aip;
+ }
+ wg_peer->p_aips_count = aip_count;
+ ++peer_count;
+ wg_peer = (struct wg_peer_io *)wg_aip;
+ }
+ wg_iface->i_peers_count = peer_count;
+
+ if (ioctl(s, SIOCSWG, (caddr_t)&wgdata) < 0)
+ goto out;
+ errno = 0;
+
+out:
+ ret = -errno;
+ free(wgdata.wgd_interface);
+ return ret;
+}
diff --git a/contrib/wireguard-tools/src/ipc-uapi-unix.h b/contrib/wireguard-tools/src/ipc-uapi-unix.h
new file mode 100644
--- /dev/null
+++ b/contrib/wireguard-tools/src/ipc-uapi-unix.h
@@ -0,0 +1,119 @@
+// SPDX-License-Identifier: MIT
+/*
+ * Copyright (C) 2015-2020 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
+ */
+
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <dirent.h>
+#include <errno.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/un.h>
+
+#define SOCK_PATH RUNSTATEDIR "/wireguard/"
+#define SOCK_SUFFIX ".sock"
+
+static FILE *userspace_interface_file(const char *iface)
+{
+ struct stat sbuf;
+ struct sockaddr_un addr = { .sun_family = AF_UNIX };
+ int fd = -1, ret;
+ FILE *f = NULL;
+
+ errno = EINVAL;
+ if (strchr(iface, '/'))
+ goto out;
+ ret = snprintf(addr.sun_path, sizeof(addr.sun_path), SOCK_PATH "%s" SOCK_SUFFIX, iface);
+ if (ret < 0)
+ goto out;
+ ret = stat(addr.sun_path, &sbuf);
+ if (ret < 0)
+ goto out;
+ errno = EBADF;
+ if (!S_ISSOCK(sbuf.st_mode))
+ goto out;
+
+ ret = fd = socket(AF_UNIX, SOCK_STREAM, 0);
+ if (ret < 0)
+ goto out;
+
+ ret = connect(fd, (struct sockaddr *)&addr, sizeof(addr));
+ if (ret < 0) {
+ if (errno == ECONNREFUSED) /* If the process is gone, we try to clean up the socket. */
+ unlink(addr.sun_path);
+ goto out;
+ }
+ f = fdopen(fd, "r+");
+ if (f)
+ errno = 0;
+out:
+ ret = -errno;
+ if (ret) {
+ if (fd >= 0)
+ close(fd);
+ errno = -ret;
+ return NULL;
+ }
+ return f;
+}
+
+static bool userspace_has_wireguard_interface(const char *iface)
+{
+ struct stat sbuf;
+ struct sockaddr_un addr = { .sun_family = AF_UNIX };
+ int fd, ret;
+
+ if (strchr(iface, '/'))
+ return false;
+ if (snprintf(addr.sun_path, sizeof(addr.sun_path), SOCK_PATH "%s" SOCK_SUFFIX, iface) < 0)
+ return false;
+ if (stat(addr.sun_path, &sbuf) < 0)
+ return false;
+ if (!S_ISSOCK(sbuf.st_mode))
+ return false;
+ ret = fd = socket(AF_UNIX, SOCK_STREAM, 0);
+ if (ret < 0)
+ return false;
+ ret = connect(fd, (struct sockaddr *)&addr, sizeof(addr));
+ if (ret < 0 && errno == ECONNREFUSED) { /* If the process is gone, we try to clean up the socket. */
+ close(fd);
+ unlink(addr.sun_path);
+ return false;
+ }
+ close(fd);
+ return true;
+}
+
+static int userspace_get_wireguard_interfaces(struct string_list *list)
+{
+ DIR *dir;
+ struct dirent *ent;
+ size_t len;
+ char *end;
+ int ret = 0;
+
+ dir = opendir(SOCK_PATH);
+ if (!dir)
+ return errno == ENOENT ? 0 : -errno;
+ while ((ent = readdir(dir))) {
+ len = strlen(ent->d_name);
+ if (len <= strlen(SOCK_SUFFIX))
+ continue;
+ end = &ent->d_name[len - strlen(SOCK_SUFFIX)];
+ if (strncmp(end, SOCK_SUFFIX, strlen(SOCK_SUFFIX)))
+ continue;
+ *end = '\0';
+ if (!userspace_has_wireguard_interface(ent->d_name))
+ continue;
+ ret = string_list_add(list, ent->d_name);
+ if (ret < 0)
+ goto out;
+ }
+out:
+ closedir(dir);
+ return ret;
+}
diff --git a/contrib/wireguard-tools/src/ipc-uapi-windows.h b/contrib/wireguard-tools/src/ipc-uapi-windows.h
new file mode 100644
--- /dev/null
+++ b/contrib/wireguard-tools/src/ipc-uapi-windows.h
@@ -0,0 +1,107 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2015-2020 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
+ */
+
+#include <windows.h>
+#include <tlhelp32.h>
+#include <accctrl.h>
+#include <aclapi.h>
+#include <stdio.h>
+#include <stdbool.h>
+#include <fcntl.h>
+#include <hashtable.h>
+
+static FILE *userspace_interface_file(const char *iface)
+{
+ char fname[MAX_PATH];
+ HANDLE pipe_handle;
+ SID expected_sid;
+ DWORD bytes = sizeof(expected_sid);
+ PSID pipe_sid;
+ PSECURITY_DESCRIPTOR pipe_sd;
+ bool equal;
+ int fd;
+
+ if (!CreateWellKnownSid(WinLocalSystemSid, NULL, &expected_sid, &bytes))
+ goto err;
+
+ snprintf(fname, sizeof(fname), "\\\\.\\pipe\\ProtectedPrefix\\Administrators\\WireGuard\\%s", iface);
+ pipe_handle = CreateFileA(fname, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);
+ if (pipe_handle == INVALID_HANDLE_VALUE)
+ goto err;
+ if (GetSecurityInfo(pipe_handle, SE_FILE_OBJECT, OWNER_SECURITY_INFORMATION, &pipe_sid, NULL, NULL, NULL, &pipe_sd) != ERROR_SUCCESS)
+ goto err_close;
+ equal = EqualSid(&expected_sid, pipe_sid);
+ LocalFree(pipe_sd);
+ if (!equal)
+ goto err_close;
+ fd = _open_osfhandle((intptr_t)pipe_handle, _O_RDWR);
+ if (fd == -1) {
+ CloseHandle(pipe_handle);
+ return NULL;
+ }
+ return _fdopen(fd, "r+");
+err_close:
+ CloseHandle(pipe_handle);
+err:
+ errno = EACCES;
+ return NULL;
+}
+
+static bool have_cached_interfaces;
+static struct hashtable cached_interfaces;
+
+static bool userspace_has_wireguard_interface(const char *iface)
+{
+ char fname[MAX_PATH];
+ WIN32_FIND_DATA find_data;
+ HANDLE find_handle;
+ bool ret = false;
+
+ if (have_cached_interfaces)
+ return hashtable_find_entry(&cached_interfaces, iface) != NULL;
+
+ snprintf(fname, sizeof(fname), "ProtectedPrefix\\Administrators\\WireGuard\\%s", iface);
+ find_handle = FindFirstFile("\\\\.\\pipe\\*", &find_data);
+ if (find_handle == INVALID_HANDLE_VALUE)
+ return -EIO;
+ do {
+ if (!strcmp(fname, find_data.cFileName)) {
+ ret = true;
+ break;
+ }
+ } while (FindNextFile(find_handle, &find_data));
+ FindClose(find_handle);
+ return ret;
+}
+
+static int userspace_get_wireguard_interfaces(struct string_list *list)
+{
+ static const char prefix[] = "ProtectedPrefix\\Administrators\\WireGuard\\";
+ WIN32_FIND_DATA find_data;
+ HANDLE find_handle;
+ char *iface;
+ int ret = 0;
+
+ find_handle = FindFirstFile("\\\\.\\pipe\\*", &find_data);
+ if (find_handle == INVALID_HANDLE_VALUE)
+ return -EIO;
+ do {
+ if (strncmp(prefix, find_data.cFileName, strlen(prefix)))
+ continue;
+ iface = find_data.cFileName + strlen(prefix);
+ ret = string_list_add(list, iface);
+ if (ret < 0)
+ goto out;
+ if (!hashtable_find_or_insert_entry(&cached_interfaces, iface)) {
+ ret = -errno;
+ goto out;
+ }
+ } while (FindNextFile(find_handle, &find_data));
+ have_cached_interfaces = true;
+
+out:
+ FindClose(find_handle);
+ return ret;
+}
diff --git a/contrib/wireguard-tools/src/ipc-uapi.h b/contrib/wireguard-tools/src/ipc-uapi.h
new file mode 100644
--- /dev/null
+++ b/contrib/wireguard-tools/src/ipc-uapi.h
@@ -0,0 +1,297 @@
+// SPDX-License-Identifier: MIT
+/*
+ * Copyright (C) 2015-2020 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
+ */
+
+#include <arpa/inet.h>
+#include <errno.h>
+#include <limits.h>
+#include <net/if.h>
+#include <netdb.h>
+#include <netinet/in.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/socket.h>
+#include "containers.h"
+#include "curve25519.h"
+#include "encoding.h"
+#include "ctype.h"
+
+#ifdef _WIN32
+#include "ipc-uapi-windows.h"
+#else
+#include "ipc-uapi-unix.h"
+#endif
+
+static int userspace_set_device(struct wgdevice *dev)
+{
+ char hex[WG_KEY_LEN_HEX], ip[INET6_ADDRSTRLEN], host[4096 + 1], service[512 + 1];
+ struct wgpeer *peer;
+ struct wgallowedip *allowedip;
+ FILE *f;
+ int ret, set_errno = -EPROTO;
+ socklen_t addr_len;
+ size_t line_buffer_len = 0, line_len;
+ char *key = NULL, *value;
+
+ f = userspace_interface_file(dev->name);
+ if (!f)
+ return -errno;
+ fprintf(f, "set=1\n");
+
+ if (dev->flags & WGDEVICE_HAS_PRIVATE_KEY) {
+ key_to_hex(hex, dev->private_key);
+ fprintf(f, "private_key=%s\n", hex);
+ }
+ if (dev->flags & WGDEVICE_HAS_LISTEN_PORT)
+ fprintf(f, "listen_port=%u\n", dev->listen_port);
+ if (dev->flags & WGDEVICE_HAS_FWMARK)
+ fprintf(f, "fwmark=%u\n", dev->fwmark);
+ if (dev->flags & WGDEVICE_REPLACE_PEERS)
+ fprintf(f, "replace_peers=true\n");
+
+ for_each_wgpeer(dev, peer) {
+ key_to_hex(hex, peer->public_key);
+ fprintf(f, "public_key=%s\n", hex);
+ if (peer->flags & WGPEER_REMOVE_ME) {
+ fprintf(f, "remove=true\n");
+ continue;
+ }
+ if (peer->flags & WGPEER_HAS_PRESHARED_KEY) {
+ key_to_hex(hex, peer->preshared_key);
+ fprintf(f, "preshared_key=%s\n", hex);
+ }
+ if (peer->endpoint.addr.sa_family == AF_INET || peer->endpoint.addr.sa_family == AF_INET6) {
+ addr_len = 0;
+ if (peer->endpoint.addr.sa_family == AF_INET)
+ addr_len = sizeof(struct sockaddr_in);
+ else if (peer->endpoint.addr.sa_family == AF_INET6)
+ addr_len = sizeof(struct sockaddr_in6);
+ if (!getnameinfo(&peer->endpoint.addr, addr_len, host, sizeof(host), service, sizeof(service), NI_DGRAM | NI_NUMERICSERV | NI_NUMERICHOST)) {
+ if (peer->endpoint.addr.sa_family == AF_INET6 && strchr(host, ':'))
+ fprintf(f, "endpoint=[%s]:%s\n", host, service);
+ else
+ fprintf(f, "endpoint=%s:%s\n", host, service);
+ }
+ }
+ if (peer->flags & WGPEER_HAS_PERSISTENT_KEEPALIVE_INTERVAL)
+ fprintf(f, "persistent_keepalive_interval=%u\n", peer->persistent_keepalive_interval);
+ if (peer->flags & WGPEER_REPLACE_ALLOWEDIPS)
+ fprintf(f, "replace_allowed_ips=true\n");
+ for_each_wgallowedip(peer, allowedip) {
+ if (allowedip->family == AF_INET) {
+ if (!inet_ntop(AF_INET, &allowedip->ip4, ip, INET6_ADDRSTRLEN))
+ continue;
+ } else if (allowedip->family == AF_INET6) {
+ if (!inet_ntop(AF_INET6, &allowedip->ip6, ip, INET6_ADDRSTRLEN))
+ continue;
+ } else
+ continue;
+ fprintf(f, "allowed_ip=%s/%d\n", ip, allowedip->cidr);
+ }
+ }
+ fprintf(f, "\n");
+ fflush(f);
+
+ while (getline(&key, &line_buffer_len, f) > 0) {
+ line_len = strlen(key);
+ ret = set_errno;
+ if (line_len == 1 && key[0] == '\n')
+ goto out;
+ value = strchr(key, '=');
+ if (!value || line_len == 0 || key[line_len - 1] != '\n')
+ break;
+ *value++ = key[--line_len] = '\0';
+
+ if (!strcmp(key, "errno")) {
+ long long num;
+ char *end;
+ if (value[0] != '-' && !char_is_digit(value[0]))
+ break;
+ num = strtoll(value, &end, 10);
+ if (*end || num > INT_MAX || num < INT_MIN)
+ break;
+ set_errno = num;
+ }
+ }
+ ret = errno ? -errno : -EPROTO;
+out:
+ free(key);
+ fclose(f);
+ errno = -ret;
+ return ret;
+}
+
+#define NUM(max) ({ \
+ unsigned long long num; \
+ char *end; \
+ if (!char_is_digit(value[0])) \
+ break; \
+ num = strtoull(value, &end, 10); \
+ if (*end || num > max) \
+ break; \
+ num; \
+})
+
+static int userspace_get_device(struct wgdevice **out, const char *iface)
+{
+ struct wgdevice *dev;
+ struct wgpeer *peer = NULL;
+ struct wgallowedip *allowedip = NULL;
+ size_t line_buffer_len = 0, line_len;
+ char *key = NULL, *value;
+ FILE *f;
+ int ret = -EPROTO;
+
+ *out = dev = calloc(1, sizeof(*dev));
+ if (!dev)
+ return -errno;
+
+ f = userspace_interface_file(iface);
+ if (!f) {
+ ret = -errno;
+ free(dev);
+ *out = NULL;
+ return ret;
+ }
+
+ fprintf(f, "get=1\n\n");
+ fflush(f);
+
+ strncpy(dev->name, iface, IFNAMSIZ - 1);
+ dev->name[IFNAMSIZ - 1] = '\0';
+
+ while (getline(&key, &line_buffer_len, f) > 0) {
+ line_len = strlen(key);
+ if (line_len == 1 && key[0] == '\n')
+ goto err;
+ value = strchr(key, '=');
+ if (!value || line_len == 0 || key[line_len - 1] != '\n')
+ break;
+ *value++ = key[--line_len] = '\0';
+
+ if (!peer && !strcmp(key, "private_key")) {
+ if (!key_from_hex(dev->private_key, value))
+ break;
+ curve25519_generate_public(dev->public_key, dev->private_key);
+ dev->flags |= WGDEVICE_HAS_PRIVATE_KEY | WGDEVICE_HAS_PUBLIC_KEY;
+ } else if (!peer && !strcmp(key, "listen_port")) {
+ dev->listen_port = NUM(0xffffU);
+ dev->flags |= WGDEVICE_HAS_LISTEN_PORT;
+ } else if (!peer && !strcmp(key, "fwmark")) {
+ dev->fwmark = NUM(0xffffffffU);
+ dev->flags |= WGDEVICE_HAS_FWMARK;
+ } else if (!strcmp(key, "public_key")) {
+ struct wgpeer *new_peer = calloc(1, sizeof(*new_peer));
+
+ if (!new_peer) {
+ ret = -ENOMEM;
+ goto err;
+ }
+ allowedip = NULL;
+ if (peer)
+ peer->next_peer = new_peer;
+ else
+ dev->first_peer = new_peer;
+ peer = new_peer;
+ if (!key_from_hex(peer->public_key, value))
+ break;
+ peer->flags |= WGPEER_HAS_PUBLIC_KEY;
+ } else if (peer && !strcmp(key, "preshared_key")) {
+ if (!key_from_hex(peer->preshared_key, value))
+ break;
+ if (!key_is_zero(peer->preshared_key))
+ peer->flags |= WGPEER_HAS_PRESHARED_KEY;
+ } else if (peer && !strcmp(key, "endpoint")) {
+ char *begin, *end;
+ struct addrinfo *resolved;
+ struct addrinfo hints = {
+ .ai_family = AF_UNSPEC,
+ .ai_socktype = SOCK_DGRAM,
+ .ai_protocol = IPPROTO_UDP
+ };
+ if (!strlen(value))
+ break;
+ if (value[0] == '[') {
+ begin = &value[1];
+ end = strchr(value, ']');
+ if (!end)
+ break;
+ *end++ = '\0';
+ if (*end++ != ':' || !*end)
+ break;
+ } else {
+ begin = value;
+ end = strrchr(value, ':');
+ if (!end || !*(end + 1))
+ break;
+ *end++ = '\0';
+ }
+ if (getaddrinfo(begin, end, &hints, &resolved) != 0) {
+ ret = ENETUNREACH;
+ goto err;
+ }
+ if ((resolved->ai_family == AF_INET && resolved->ai_addrlen == sizeof(struct sockaddr_in)) ||
+ (resolved->ai_family == AF_INET6 && resolved->ai_addrlen == sizeof(struct sockaddr_in6)))
+ memcpy(&peer->endpoint.addr, resolved->ai_addr, resolved->ai_addrlen);
+ else {
+ freeaddrinfo(resolved);
+ break;
+ }
+ freeaddrinfo(resolved);
+ } else if (peer && !strcmp(key, "persistent_keepalive_interval")) {
+ peer->persistent_keepalive_interval = NUM(0xffffU);
+ peer->flags |= WGPEER_HAS_PERSISTENT_KEEPALIVE_INTERVAL;
+ } else if (peer && !strcmp(key, "allowed_ip")) {
+ struct wgallowedip *new_allowedip;
+ char *end, *mask = value, *ip = strsep(&mask, "/");
+
+ if (!mask || !char_is_digit(mask[0]))
+ break;
+ new_allowedip = calloc(1, sizeof(*new_allowedip));
+ if (!new_allowedip) {
+ ret = -ENOMEM;
+ goto err;
+ }
+ if (allowedip)
+ allowedip->next_allowedip = new_allowedip;
+ else
+ peer->first_allowedip = new_allowedip;
+ allowedip = new_allowedip;
+ allowedip->family = AF_UNSPEC;
+ if (strchr(ip, ':')) {
+ if (inet_pton(AF_INET6, ip, &allowedip->ip6) == 1)
+ allowedip->family = AF_INET6;
+ } else {
+ if (inet_pton(AF_INET, ip, &allowedip->ip4) == 1)
+ allowedip->family = AF_INET;
+ }
+ allowedip->cidr = strtoul(mask, &end, 10);
+ if (*end || allowedip->family == AF_UNSPEC || (allowedip->family == AF_INET6 && allowedip->cidr > 128) || (allowedip->family == AF_INET && allowedip->cidr > 32))
+ break;
+ } else if (peer && !strcmp(key, "last_handshake_time_sec"))
+ peer->last_handshake_time.tv_sec = NUM(0x7fffffffffffffffULL);
+ else if (peer && !strcmp(key, "last_handshake_time_nsec"))
+ peer->last_handshake_time.tv_nsec = NUM(0x7fffffffffffffffULL);
+ else if (peer && !strcmp(key, "rx_bytes"))
+ peer->rx_bytes = NUM(0xffffffffffffffffULL);
+ else if (peer && !strcmp(key, "tx_bytes"))
+ peer->tx_bytes = NUM(0xffffffffffffffffULL);
+ else if (!strcmp(key, "errno"))
+ ret = -NUM(0x7fffffffU);
+ }
+ ret = -EPROTO;
+err:
+ free(key);
+ if (ret) {
+ free_wgdevice(dev);
+ *out = NULL;
+ }
+ fclose(f);
+ errno = -ret;
+ return ret;
+
+}
+#undef NUM
diff --git a/contrib/wireguard-tools/src/ipc-windows.h b/contrib/wireguard-tools/src/ipc-windows.h
new file mode 100644
--- /dev/null
+++ b/contrib/wireguard-tools/src/ipc-windows.h
@@ -0,0 +1,450 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2015-2021 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
+ */
+
+#include "containers.h"
+#include <windows.h>
+#include <setupapi.h>
+#include <cfgmgr32.h>
+#include <iphlpapi.h>
+#include <initguid.h>
+#include <devguid.h>
+#include <ddk/ndisguid.h>
+#include <wireguard.h>
+#include <hashtable.h>
+
+#define IPC_SUPPORTS_KERNEL_INTERFACE
+
+static bool have_cached_kernel_interfaces;
+static struct hashtable cached_kernel_interfaces;
+static const DEVPROPKEY devpkey_name = DEVPKEY_WG_NAME;
+extern bool is_win7;
+
+static int kernel_get_wireguard_interfaces(struct string_list *list)
+{
+ HDEVINFO dev_info = SetupDiGetClassDevsExW(&GUID_DEVCLASS_NET, is_win7 ? L"ROOT\\WIREGUARD" : L"SWD\\WireGuard", NULL, DIGCF_PRESENT, NULL, NULL, NULL);
+ bool will_have_cached_kernel_interfaces = true;
+
+ if (dev_info == INVALID_HANDLE_VALUE) {
+ errno = EACCES;
+ return -errno;
+ }
+
+ for (DWORD i = 0;; ++i) {
+ DWORD buf_len;
+ WCHAR adapter_name[MAX_ADAPTER_NAME];
+ SP_DEVINFO_DATA dev_info_data = { .cbSize = sizeof(SP_DEVINFO_DATA) };
+ DEVPROPTYPE prop_type;
+ ULONG status, problem_code;
+ char *interface_name;
+ struct hashtable_entry *entry;
+
+ if (!SetupDiEnumDeviceInfo(dev_info, i, &dev_info_data)) {
+ if (GetLastError() == ERROR_NO_MORE_ITEMS)
+ break;
+ continue;
+ }
+
+ if (!SetupDiGetDevicePropertyW(dev_info, &dev_info_data, &devpkey_name,
+ &prop_type, (PBYTE)adapter_name,
+ sizeof(adapter_name), NULL, 0) ||
+ prop_type != DEVPROP_TYPE_STRING)
+ continue;
+ adapter_name[_countof(adapter_name) - 1] = L'0';
+ if (!adapter_name[0])
+ continue;
+ buf_len = WideCharToMultiByte(CP_UTF8, 0, adapter_name, -1, NULL, 0, NULL, NULL);
+ if (!buf_len)
+ continue;
+ interface_name = malloc(buf_len);
+ if (!interface_name)
+ continue;
+ buf_len = WideCharToMultiByte(CP_UTF8, 0, adapter_name, -1, interface_name, buf_len, NULL, NULL);
+ if (!buf_len) {
+ free(interface_name);
+ continue;
+ }
+
+ if (CM_Get_DevNode_Status(&status, &problem_code, dev_info_data.DevInst, 0) == CR_SUCCESS &&
+ (status & (DN_DRIVER_LOADED | DN_STARTED)) == (DN_DRIVER_LOADED | DN_STARTED))
+ string_list_add(list, interface_name);
+
+ entry = hashtable_find_or_insert_entry(&cached_kernel_interfaces, interface_name);
+ free(interface_name);
+ if (!entry)
+ continue;
+
+ if (SetupDiGetDeviceInstanceIdW(dev_info, &dev_info_data, NULL, 0, &buf_len) || GetLastError() != ERROR_INSUFFICIENT_BUFFER)
+ continue;
+ entry->value = calloc(sizeof(WCHAR), buf_len);
+ if (!entry->value)
+ continue;
+ if (!SetupDiGetDeviceInstanceIdW(dev_info, &dev_info_data, entry->value, buf_len, &buf_len)) {
+ free(entry->value);
+ entry->value = NULL;
+ continue;
+ }
+
+ will_have_cached_kernel_interfaces = true;
+ }
+ SetupDiDestroyDeviceInfoList(dev_info);
+ have_cached_kernel_interfaces = will_have_cached_kernel_interfaces;
+ return 0;
+}
+
+static HANDLE kernel_interface_handle(const char *iface)
+{
+ HDEVINFO dev_info;
+ WCHAR *interfaces = NULL;
+ HANDLE handle;
+
+ if (have_cached_kernel_interfaces) {
+ struct hashtable_entry *entry = hashtable_find_entry(&cached_kernel_interfaces, iface);
+ if (entry) {
+ DWORD buf_len;
+ if (CM_Get_Device_Interface_List_SizeW(
+ &buf_len, (GUID *)&GUID_DEVINTERFACE_NET, (DEVINSTID_W)entry->value,
+ CM_GET_DEVICE_INTERFACE_LIST_PRESENT) != CR_SUCCESS)
+ goto err_hash;
+ interfaces = calloc(buf_len, sizeof(*interfaces));
+ if (!interfaces)
+ goto err_hash;
+ if (CM_Get_Device_Interface_ListW(
+ (GUID *)&GUID_DEVINTERFACE_NET, (DEVINSTID_W)entry->value, interfaces, buf_len,
+ CM_GET_DEVICE_INTERFACE_LIST_PRESENT) != CR_SUCCESS || !interfaces[0]) {
+ free(interfaces);
+ interfaces = NULL;
+ goto err_hash;
+ }
+ handle = CreateFileW(interfaces, GENERIC_READ | GENERIC_WRITE,
+ FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL,
+ OPEN_EXISTING, 0, NULL);
+ free(interfaces);
+ if (handle == INVALID_HANDLE_VALUE)
+ goto err_hash;
+ return handle;
+err_hash:
+ errno = EACCES;
+ return NULL;
+ }
+ }
+
+ dev_info = SetupDiGetClassDevsExW(&GUID_DEVCLASS_NET, is_win7 ? L"ROOT\\WIREGUARD" : L"SWD\\WireGuard", NULL, DIGCF_PRESENT, NULL, NULL, NULL);
+ if (dev_info == INVALID_HANDLE_VALUE)
+ return NULL;
+
+ for (DWORD i = 0; !interfaces; ++i) {
+ bool found;
+ DWORD buf_len;
+ WCHAR *buf, adapter_name[MAX_ADAPTER_NAME];
+ SP_DEVINFO_DATA dev_info_data = { .cbSize = sizeof(SP_DEVINFO_DATA) };
+ DEVPROPTYPE prop_type;
+ char *interface_name;
+
+ if (!SetupDiEnumDeviceInfo(dev_info, i, &dev_info_data)) {
+ if (GetLastError() == ERROR_NO_MORE_ITEMS)
+ break;
+ continue;
+ }
+
+ if (!SetupDiGetDevicePropertyW(dev_info, &dev_info_data, &devpkey_name,
+ &prop_type, (PBYTE)adapter_name,
+ sizeof(adapter_name), NULL, 0) ||
+ prop_type != DEVPROP_TYPE_STRING)
+ continue;
+ adapter_name[_countof(adapter_name) - 1] = L'0';
+ if (!adapter_name[0])
+ continue;
+ buf_len = WideCharToMultiByte(CP_UTF8, 0, adapter_name, -1, NULL, 0, NULL, NULL);
+ if (!buf_len)
+ continue;
+ interface_name = malloc(buf_len);
+ if (!interface_name)
+ continue;
+ buf_len = WideCharToMultiByte(CP_UTF8, 0, adapter_name, -1, interface_name, buf_len, NULL, NULL);
+ if (!buf_len) {
+ free(interface_name);
+ continue;
+ }
+ found = !strcmp(interface_name, iface);
+ free(interface_name);
+ if (!found)
+ continue;
+
+ if (SetupDiGetDeviceInstanceIdW(dev_info, &dev_info_data, NULL, 0, &buf_len) || GetLastError() != ERROR_INSUFFICIENT_BUFFER)
+ continue;
+ buf = calloc(sizeof(*buf), buf_len);
+ if (!buf)
+ continue;
+ if (!SetupDiGetDeviceInstanceIdW(dev_info, &dev_info_data, buf, buf_len, &buf_len))
+ goto cleanup_instance_id;
+ if (CM_Get_Device_Interface_List_SizeW(
+ &buf_len, (GUID *)&GUID_DEVINTERFACE_NET, (DEVINSTID_W)buf,
+ CM_GET_DEVICE_INTERFACE_LIST_PRESENT) != CR_SUCCESS)
+ goto cleanup_instance_id;
+ interfaces = calloc(buf_len, sizeof(*interfaces));
+ if (!interfaces)
+ goto cleanup_instance_id;
+ if (CM_Get_Device_Interface_ListW(
+ (GUID *)&GUID_DEVINTERFACE_NET, (DEVINSTID_W)buf, interfaces, buf_len,
+ CM_GET_DEVICE_INTERFACE_LIST_PRESENT) != CR_SUCCESS || !interfaces[0]) {
+ free(interfaces);
+ interfaces = NULL;
+ goto cleanup_instance_id;
+ }
+cleanup_instance_id:
+ free(buf);
+ }
+ SetupDiDestroyDeviceInfoList(dev_info);
+ if (!interfaces) {
+ errno = ENOENT;
+ return NULL;
+ }
+ handle = CreateFileW(interfaces, GENERIC_READ | GENERIC_WRITE,
+ FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL,
+ OPEN_EXISTING, 0, NULL);
+ free(interfaces);
+ if (handle == INVALID_HANDLE_VALUE) {
+ errno = EACCES;
+ return NULL;
+ }
+ return handle;
+}
+
+static int kernel_get_device(struct wgdevice **device, const char *iface)
+{
+ WG_IOCTL_INTERFACE *wg_iface;
+ WG_IOCTL_PEER *wg_peer;
+ WG_IOCTL_ALLOWED_IP *wg_aip;
+ void *buf = NULL;
+ DWORD buf_len = 0;
+ HANDLE handle = kernel_interface_handle(iface);
+ struct wgdevice *dev;
+ struct wgpeer *peer;
+ struct wgallowedip *aip;
+ int ret;
+
+ *device = NULL;
+
+ if (!handle)
+ return -errno;
+
+ while (!DeviceIoControl(handle, WG_IOCTL_GET, NULL, 0, buf, buf_len, &buf_len, NULL)) {
+ free(buf);
+ if (GetLastError() != ERROR_MORE_DATA) {
+ errno = EACCES;
+ return -errno;
+ }
+ buf = malloc(buf_len);
+ if (!buf)
+ return -errno;
+ }
+
+ wg_iface = (WG_IOCTL_INTERFACE *)buf;
+ dev = calloc(1, sizeof(*dev));
+ if (!dev)
+ goto out;
+ strncpy(dev->name, iface, sizeof(dev->name));
+ dev->name[sizeof(dev->name) - 1] = '\0';
+
+ if (wg_iface->Flags & WG_IOCTL_INTERFACE_HAS_LISTEN_PORT) {
+ dev->listen_port = wg_iface->ListenPort;
+ dev->flags |= WGDEVICE_HAS_LISTEN_PORT;
+ }
+
+ if (wg_iface->Flags & WG_IOCTL_INTERFACE_HAS_PUBLIC_KEY) {
+ memcpy(dev->public_key, wg_iface->PublicKey, sizeof(dev->public_key));
+ dev->flags |= WGDEVICE_HAS_PUBLIC_KEY;
+ }
+
+ if (wg_iface->Flags & WG_IOCTL_INTERFACE_HAS_PRIVATE_KEY) {
+ memcpy(dev->private_key, wg_iface->PrivateKey, sizeof(dev->private_key));
+ dev->flags |= WGDEVICE_HAS_PRIVATE_KEY;
+ }
+
+ wg_peer = buf + sizeof(WG_IOCTL_INTERFACE);
+ for (ULONG i = 0; i < wg_iface->PeersCount; ++i) {
+ peer = calloc(1, sizeof(*peer));
+ if (!peer)
+ goto out;
+
+ if (dev->first_peer == NULL)
+ dev->first_peer = peer;
+ else
+ dev->last_peer->next_peer = peer;
+ dev->last_peer = peer;
+
+ if (wg_peer->Flags & WG_IOCTL_PEER_HAS_PUBLIC_KEY) {
+ memcpy(peer->public_key, wg_peer->PublicKey, sizeof(peer->public_key));
+ peer->flags |= WGPEER_HAS_PUBLIC_KEY;
+ }
+
+ if (wg_peer->Flags & WG_IOCTL_PEER_HAS_PRESHARED_KEY) {
+ memcpy(peer->preshared_key, wg_peer->PresharedKey, sizeof(peer->preshared_key));
+ if (!key_is_zero(peer->preshared_key))
+ peer->flags |= WGPEER_HAS_PRESHARED_KEY;
+ }
+
+ if (wg_peer->Flags & WG_IOCTL_PEER_HAS_PERSISTENT_KEEPALIVE) {
+ peer->persistent_keepalive_interval = wg_peer->PersistentKeepalive;
+ peer->flags |= WGPEER_HAS_PERSISTENT_KEEPALIVE_INTERVAL;
+ }
+
+ if (wg_peer->Flags & WG_IOCTL_PEER_HAS_ENDPOINT) {
+ if (wg_peer->Endpoint.si_family == AF_INET)
+ peer->endpoint.addr4 = wg_peer->Endpoint.Ipv4;
+ else if (wg_peer->Endpoint.si_family == AF_INET6)
+ peer->endpoint.addr6 = wg_peer->Endpoint.Ipv6;
+ }
+
+ peer->rx_bytes = wg_peer->RxBytes;
+ peer->tx_bytes = wg_peer->TxBytes;
+
+ if (wg_peer->LastHandshake) {
+ peer->last_handshake_time.tv_sec = wg_peer->LastHandshake / 10000000 - 11644473600LL;
+ peer->last_handshake_time.tv_nsec = wg_peer->LastHandshake % 10000000 * 100;
+ }
+
+ wg_aip = (void *)wg_peer + sizeof(WG_IOCTL_PEER);
+ for (ULONG j = 0; j < wg_peer->AllowedIPsCount; ++j) {
+ aip = calloc(1, sizeof(*aip));
+ if (!aip)
+ goto out;
+
+ if (peer->first_allowedip == NULL)
+ peer->first_allowedip = aip;
+ else
+ peer->last_allowedip->next_allowedip = aip;
+ peer->last_allowedip = aip;
+
+ aip->family = wg_aip->AddressFamily;
+ if (wg_aip->AddressFamily == AF_INET) {
+ memcpy(&aip->ip4, &wg_aip->Address.V4, sizeof(aip->ip4));
+ aip->cidr = wg_aip->Cidr;
+ } else if (wg_aip->AddressFamily == AF_INET6) {
+ memcpy(&aip->ip6, &wg_aip->Address.V6, sizeof(aip->ip6));
+ aip->cidr = wg_aip->Cidr;
+ }
+ ++wg_aip;
+ }
+ wg_peer = (WG_IOCTL_PEER *)wg_aip;
+ }
+ *device = dev;
+ errno = 0;
+out:
+ ret = -errno;
+ free(buf);
+ CloseHandle(handle);
+ return ret;
+}
+
+static int kernel_set_device(struct wgdevice *dev)
+{
+ WG_IOCTL_INTERFACE *wg_iface = NULL;
+ WG_IOCTL_PEER *wg_peer;
+ WG_IOCTL_ALLOWED_IP *wg_aip;
+ DWORD buf_len = sizeof(WG_IOCTL_INTERFACE);
+ HANDLE handle = kernel_interface_handle(dev->name);
+ struct wgpeer *peer;
+ struct wgallowedip *aip;
+ size_t peer_count, aip_count;
+ int ret = 0;
+
+ if (!handle)
+ return -errno;
+
+ for_each_wgpeer(dev, peer) {
+ if (DWORD_MAX - buf_len < sizeof(WG_IOCTL_PEER)) {
+ errno = EOVERFLOW;
+ goto out;
+ }
+ buf_len += sizeof(WG_IOCTL_PEER);
+ for_each_wgallowedip(peer, aip) {
+ if (DWORD_MAX - buf_len < sizeof(WG_IOCTL_ALLOWED_IP)) {
+ errno = EOVERFLOW;
+ goto out;
+ }
+ buf_len += sizeof(WG_IOCTL_ALLOWED_IP);
+ }
+ }
+ wg_iface = calloc(1, buf_len);
+ if (!wg_iface)
+ goto out;
+
+ if (dev->flags & WGDEVICE_HAS_PRIVATE_KEY) {
+ memcpy(wg_iface->PrivateKey, dev->private_key, sizeof(wg_iface->PrivateKey));
+ wg_iface->Flags |= WG_IOCTL_INTERFACE_HAS_PRIVATE_KEY;
+ }
+
+ if (dev->flags & WGDEVICE_HAS_LISTEN_PORT) {
+ wg_iface->ListenPort = dev->listen_port;
+ wg_iface->Flags |= WG_IOCTL_INTERFACE_HAS_LISTEN_PORT;
+ }
+
+ if (dev->flags & WGDEVICE_REPLACE_PEERS)
+ wg_iface->Flags |= WG_IOCTL_INTERFACE_REPLACE_PEERS;
+
+ peer_count = 0;
+ wg_peer = (void *)wg_iface + sizeof(WG_IOCTL_INTERFACE);
+ for_each_wgpeer(dev, peer) {
+ wg_peer->Flags = WG_IOCTL_PEER_HAS_PUBLIC_KEY;
+ memcpy(wg_peer->PublicKey, peer->public_key, sizeof(wg_peer->PublicKey));
+
+ if (peer->flags & WGPEER_HAS_PRESHARED_KEY) {
+ memcpy(wg_peer->PresharedKey, peer->preshared_key, sizeof(wg_peer->PresharedKey));
+ wg_peer->Flags |= WG_IOCTL_PEER_HAS_PRESHARED_KEY;
+ }
+
+ if (peer->flags & WGPEER_HAS_PERSISTENT_KEEPALIVE_INTERVAL) {
+ wg_peer->PersistentKeepalive = peer->persistent_keepalive_interval;
+ wg_peer->Flags |= WG_IOCTL_PEER_HAS_PERSISTENT_KEEPALIVE;
+ }
+
+ if (peer->endpoint.addr.sa_family == AF_INET) {
+ wg_peer->Endpoint.Ipv4 = peer->endpoint.addr4;
+ wg_peer->Flags |= WG_IOCTL_PEER_HAS_ENDPOINT;
+ } else if (peer->endpoint.addr.sa_family == AF_INET6) {
+ wg_peer->Endpoint.Ipv6 = peer->endpoint.addr6;
+ wg_peer->Flags |= WG_IOCTL_PEER_HAS_ENDPOINT;
+ }
+
+ if (peer->flags & WGPEER_REPLACE_ALLOWEDIPS)
+ wg_peer->Flags |= WG_IOCTL_PEER_REPLACE_ALLOWED_IPS;
+
+ if (peer->flags & WGPEER_REMOVE_ME)
+ wg_peer->Flags |= WG_IOCTL_PEER_REMOVE;
+
+ aip_count = 0;
+ wg_aip = (void *)wg_peer + sizeof(WG_IOCTL_PEER);
+ for_each_wgallowedip(peer, aip) {
+ wg_aip->AddressFamily = aip->family;
+ wg_aip->Cidr = aip->cidr;
+
+ if (aip->family == AF_INET)
+ wg_aip->Address.V4 = aip->ip4;
+ else if (aip->family == AF_INET6)
+ wg_aip->Address.V6 = aip->ip6;
+ else
+ continue;
+ ++aip_count;
+ ++wg_aip;
+ }
+ wg_peer->AllowedIPsCount = aip_count;
+ ++peer_count;
+ wg_peer = (WG_IOCTL_PEER *)wg_aip;
+ }
+ wg_iface->PeersCount = peer_count;
+
+ if (!DeviceIoControl(handle, WG_IOCTL_SET, NULL, 0, wg_iface, buf_len, &buf_len, NULL)) {
+ errno = EACCES;
+ goto out;
+ }
+ errno = 0;
+
+out:
+ ret = -errno;
+ free(wg_iface);
+ CloseHandle(handle);
+ return ret;
+}
diff --git a/contrib/wireguard-tools/src/ipc.h b/contrib/wireguard-tools/src/ipc.h
new file mode 100644
--- /dev/null
+++ b/contrib/wireguard-tools/src/ipc.h
@@ -0,0 +1,17 @@
+/* SPDX-License-Identifier: GPL-2.0 OR MIT */
+/*
+ * Copyright (C) 2015-2020 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
+ */
+
+#ifndef IPC_H
+#define IPC_H
+
+#include <stdbool.h>
+
+struct wgdevice;
+
+int ipc_set_device(struct wgdevice *dev);
+int ipc_get_device(struct wgdevice **dev, const char *interface);
+char *ipc_list_devices(void);
+
+#endif
diff --git a/contrib/wireguard-tools/src/ipc.c b/contrib/wireguard-tools/src/ipc.c
new file mode 100644
--- /dev/null
+++ b/contrib/wireguard-tools/src/ipc.c
@@ -0,0 +1,98 @@
+// SPDX-License-Identifier: GPL-2.0 OR MIT
+/*
+ * Copyright (C) 2015-2020 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
+ */
+
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+#include "containers.h"
+#include "ipc.h"
+
+struct string_list {
+ char *buffer;
+ size_t len;
+ size_t cap;
+};
+
+static int string_list_add(struct string_list *list, const char *str)
+{
+ size_t len = strlen(str) + 1;
+
+ if (len == 1)
+ return 0;
+
+ if (len >= list->cap - list->len) {
+ char *new_buffer;
+ size_t new_cap = list->cap * 2;
+
+ if (new_cap < list->len + len + 1)
+ new_cap = list->len + len + 1;
+ new_buffer = realloc(list->buffer, new_cap);
+ if (!new_buffer)
+ return -errno;
+ list->buffer = new_buffer;
+ list->cap = new_cap;
+ }
+ memcpy(list->buffer + list->len, str, len);
+ list->len += len;
+ list->buffer[list->len] = '\0';
+ return 0;
+}
+
+#include "ipc-uapi.h"
+#if defined(__linux__)
+#include "ipc-linux.h"
+#elif defined(__OpenBSD__)
+#include "ipc-openbsd.h"
+#elif defined(__FreeBSD__)
+#include "ipc-freebsd.h"
+#elif defined(_WIN32)
+#include "ipc-windows.h"
+#endif
+
+/* first\0second\0third\0forth\0last\0\0 */
+char *ipc_list_devices(void)
+{
+ struct string_list list = { 0 };
+ int ret;
+
+#ifdef IPC_SUPPORTS_KERNEL_INTERFACE
+ ret = kernel_get_wireguard_interfaces(&list);
+ if (ret < 0)
+ goto cleanup;
+#endif
+ ret = userspace_get_wireguard_interfaces(&list);
+ if (ret < 0)
+ goto cleanup;
+
+cleanup:
+ errno = -ret;
+ if (errno) {
+ free(list.buffer);
+ return NULL;
+ }
+ return list.buffer ?: strdup("\0");
+}
+
+int ipc_get_device(struct wgdevice **dev, const char *iface)
+{
+#ifdef IPC_SUPPORTS_KERNEL_INTERFACE
+ if (userspace_has_wireguard_interface(iface))
+ return userspace_get_device(dev, iface);
+ return kernel_get_device(dev, iface);
+#else
+ return userspace_get_device(dev, iface);
+#endif
+}
+
+int ipc_set_device(struct wgdevice *dev)
+{
+#ifdef IPC_SUPPORTS_KERNEL_INTERFACE
+ if (userspace_has_wireguard_interface(dev->name))
+ return userspace_set_device(dev);
+ return kernel_set_device(dev);
+#else
+ return userspace_set_device(dev);
+#endif
+}
diff --git a/contrib/wireguard-tools/src/man/wg-quick.8 b/contrib/wireguard-tools/src/man/wg-quick.8
new file mode 100644
--- /dev/null
+++ b/contrib/wireguard-tools/src/man/wg-quick.8
@@ -0,0 +1,277 @@
+.TH WG-QUICK 8 "2016 January 1" ZX2C4 "WireGuard"
+
+.SH NAME
+wg-quick - set up a WireGuard interface simply
+
+.SH SYNOPSIS
+.B wg-quick
+[
+.I up
+|
+.I down
+|
+.I save
+|
+.I strip
+] [
+.I CONFIG_FILE
+|
+.I INTERFACE
+]
+
+.SH DESCRIPTION
+
+This is an extremely simple script for easily bringing up a WireGuard interface,
+suitable for a few common use cases.
+
+Use \fIup\fP to add and set up an interface, and use \fIdown\fP to tear down and remove
+an interface. Running \fIup\fP adds a WireGuard interface, brings up the interface with the
+supplied IP addresses, sets up mtu and routes, and optionally runs pre/post up scripts. Running \fIdown\fP
+optionally saves the current configuration, removes the WireGuard interface, and optionally
+runs pre/post down scripts. Running \fIsave\fP saves the configuration of an existing
+interface without bringing the interface down. Use \fIstrip\fP to output a configuration file
+with all
+.BR wg-quick (8)-specific
+options removed, suitable for use with
+.BR wg (8).
+
+\fICONFIG_FILE\fP is a configuration file, whose filename is the interface name
+followed by `.conf'. Otherwise, \fIINTERFACE\fP is an interface name, with configuration
+found at `/etc/wireguard/\fIINTERFACE\fP.conf', searched first, followed by distro-specific
+search paths.
+
+Generally speaking, this utility is just a simple script that wraps invocations to
+.BR wg (8)
+and
+.BR ip (8)
+in order to set up a WireGuard interface. It is designed for users with simple
+needs, and users with more advanced needs are highly encouraged to use a more
+specific tool, a more complete network manager, or otherwise just use
+.BR wg (8)
+and
+.BR ip (8),
+as usual.
+
+.SH CONFIGURATION
+
+The configuration file adds a few extra configuration values to the format understood by
+.BR wg (8)
+in order to configure additional attributes of an interface. It handles the
+values that it understands, and then it passes the remaining ones directly to
+.BR wg (8)
+for further processing.
+
+It infers all routes from the list of peers' allowed IPs, and automatically adds
+them to the system routing table. If one of those routes is the default route
+(0.0.0.0/0 or ::/0), then it uses
+.BR ip-rule (8)
+to handle overriding of the default gateway.
+
+The configuration file will be passed directly to \fBwg\fP(8)'s `setconf'
+sub-command, with the exception of the following additions to the \fIInterface\fP section,
+which are handled by this tool:
+
+.IP \(bu
+Address \(em a comma-separated list of IP (v4 or v6) addresses (optionally with CIDR masks)
+to be assigned to the interface. May be specified multiple times.
+.IP \(bu
+DNS \(em a comma-separated list of IP (v4 or v6) addresses to be set as the interface's
+DNS servers, or non-IP hostnames to be set as the interface's DNS search domains. May be
+specified multiple times. Upon bringing the interface up, this runs
+`resolvconf -a tun.\fIINTERFACE\fP -m 0 -x` and upon bringing it down, this runs
+`resolvconf -d tun.\fIINTERFACE\fP`. If these particular invocations of
+.BR resolvconf (8)
+are undesirable, the PostUp and PostDown keys below may be used instead.
+.IP \(bu
+MTU \(em if not specified, the MTU is automatically determined from the endpoint addresses
+or the system default route, which is usually a sane choice. However, to manually specify
+an MTU to override this automatic discovery, this value may be specified explicitly.
+.IP \(bu
+Table \(em Controls the routing table to which routes are added. There are two
+special values: `off' disables the creation of routes altogether, and `auto'
+(the default) adds routes to the default table and enables special handling of
+default routes.
+.IP \(bu
+PreUp, PostUp, PreDown, PostDown \(em script snippets which will be executed by
+.BR bash (1)
+before/after setting up/tearing down the interface, most commonly used
+to configure custom DNS options or firewall rules. The special string `%i'
+is expanded to \fIINTERFACE\fP. Each one may be specified multiple times, in which case
+the commands are executed in order.
+.IP \(bu
+SaveConfig \(em if set to `true', the configuration is saved from the current state of the
+interface upon shutdown. Any changes made to the configuration file before the
+interface is removed will therefore be overwritten.
+
+.P
+Recommended \fIINTERFACE\fP names include `wg0' or `wgvpn0' or even `wgmgmtlan0'.
+However, the number at the end is in fact optional, and really
+any free-form string [a-zA-Z0-9_=+.-]{1,15} will work. So even interface names corresponding
+to geographic locations would suffice, such as `cincinnati', `nyc', or `paris', if that's
+somehow desirable.
+
+.SH EXAMPLES
+
+These examples draw on the same syntax found for
+.BR wg (8),
+and a more complete description may be found there. Bold lines below are for options that extend
+.BR wg (8).
+
+The following might be used for connecting as a client to a VPN gateway for tunneling all
+traffic:
+
+ [Interface]
+.br
+ \fBAddress = 10.200.100.8/24\fP
+.br
+ \fBDNS = 10.200.100.1\fP
+.br
+ PrivateKey = oK56DE9Ue9zK76rAc8pBl6opph+1v36lm7cXXsQKrQM=
+.br
+
+.br
+ [Peer]
+.br
+ PublicKey = GtL7fZc/bLnqZldpVofMCD6hDjrK28SsdLxevJ+qtKU=
+.br
+ PresharedKey = /UwcSPg38hW/D9Y3tcS1FOV0K1wuURMbS0sesJEP5ak=
+.br
+ AllowedIPs = 0.0.0.0/0
+.br
+ Endpoint = demo.wireguard.com:51820
+.br
+
+The `Address` field is added here in order to set up the address for the interface. The `DNS` field
+indicates that a DNS server for the interface should be configured via
+.BR resolvconf (8).
+The peer's allowed IPs entry implies that this interface should be configured as the default gateway,
+which this script does.
+
+Building on the last example, one might attempt the so-called ``kill-switch'', in order
+to prevent the flow of unencrypted packets through the non-WireGuard interfaces, by adding the following
+two lines `PostUp` and `PreDown` lines to the `[Interface]` section:
+
+ \fBPostUp = iptables -I OUTPUT ! -o %i -m mark ! --mark $(wg show %i fwmark) -m addrtype ! --dst-type LOCAL -j REJECT\fP
+.br
+ \fBPreDown = iptables -D OUTPUT ! -o %i -m mark ! --mark $(wg show %i fwmark) -m addrtype ! --dst-type LOCAL -j REJECT\fP
+.br
+
+The `PostUp' and `PreDown' fields have been added to specify an
+.BR iptables (8)
+command which, when used with interfaces that have a peer that specifies 0.0.0.0/0 as part of the
+`AllowedIPs', works together with wg-quick's fwmark usage in order to drop all packets that
+are either not coming out of the tunnel encrypted or not going through the tunnel itself. (Note
+that this continues to allow most DHCP traffic through, since most DHCP clients make use of PF_PACKET
+sockets, which bypass Netfilter.) When IPv6 is in use, additional similar lines could be added using
+.BR ip6tables (8).
+
+Or, perhaps it is desirable to store private keys in encrypted form, such as through use of
+.BR pass (1):
+
+ \fBPostUp = wg set %i private-key <(pass WireGuard/private-keys/%i)\fP
+.br
+
+For use on a server, the following is a more complicated example involving multiple peers:
+
+ [Interface]
+.br
+ \fBAddress = 10.192.122.1/24\fP
+.br
+ \fBAddress = 10.10.0.1/16\fP
+.br
+ \fBSaveConfig = true\fP
+.br
+ PrivateKey = yAnz5TF+lXXJte14tji3zlMNq+hd2rYUIgJBgB3fBmk=
+.br
+ ListenPort = 51820
+.br
+
+.br
+ [Peer]
+.br
+ PublicKey = xTIBA5rboUvnH4htodjb6e697QjLERt1NAB4mZqp8Dg=
+.br
+ AllowedIPs = 10.192.122.3/32, 10.192.124.1/24
+.br
+
+.br
+ [Peer]
+.br
+ PublicKey = TrMvSoP4jYQlY6RIzBgbssQqY3vxI2Pi+y71lOWWXX0=
+.br
+ AllowedIPs = 10.192.122.4/32, 192.168.0.0/16
+.br
+
+.br
+ [Peer]
+.br
+ PublicKey = gN65BkIKy1eCE9pP1wdc8ROUtkHLF2PfAqYdyYBz6EA=
+.br
+ AllowedIPs = 10.10.10.230/32
+
+Notice the two `Address' lines at the top, and that `SaveConfig' is set to `true', indicating
+that the configuration file should be saved on shutdown using the current status of the
+interface.
+
+A combination of the `Table', `PostUp', and `PreDown' fields may be used for policy routing
+as well. For example, the following may be used to send SSH traffic (TCP port 22) traffic
+through the tunnel:
+
+ [Interface]
+.br
+ Address = 10.192.122.1/24
+.br
+ PrivateKey = yAnz5TF+lXXJte14tji3zlMNq+hd2rYUIgJBgB3fBmk=
+.br
+ ListenPort = 51820
+.br
+ \fBTable = 1234\fP
+.br
+ \fBPostUp = ip rule add ipproto tcp dport 22 table 1234\fP
+.br
+ \fBPreDown = ip rule delete ipproto tcp dport 22 table 1234\fP
+.br
+
+.br
+ [Peer]
+.br
+ PublicKey = xTIBA5rboUvnH4htodjb6e697QjLERt1NAB4mZqp8Dg=
+.br
+ AllowedIPs = 0.0.0.0/0
+
+These configuration files may be placed in any directory, putting the desired interface name
+in the filename:
+
+\fB # wg-quick up /path/to/wgnet0.conf\fP
+
+For convenience, if only an interface name is supplied, it automatically chooses a path in
+`/etc/wireguard/':
+
+\fB # wg-quick up wgnet0\fP
+
+This will load the configuration file `/etc/wireguard/wgnet0.conf'.
+
+The \fIstrip\fP command is useful for reloading configuration files without disrupting active
+sessions:
+
+\fB # wg syncconf wgnet0 <(wg-quick strip wgnet0)\fP
+
+.SH SEE ALSO
+.BR wg (8),
+.BR ip (8),
+.BR ip-link (8),
+.BR ip-address (8),
+.BR ip-route (8),
+.BR ip-rule (8),
+.BR resolvconf (8).
+
+.SH AUTHOR
+.B wg-quick
+was written by
+.MT Jason@zx2c4.com
+Jason A. Donenfeld
+.ME .
+For updates and more information, a project page is available on the
+.UR https://\:www.wireguard.com/
+World Wide Web
+.UE .
diff --git a/contrib/wireguard-tools/src/man/wg.8 b/contrib/wireguard-tools/src/man/wg.8
new file mode 100644
--- /dev/null
+++ b/contrib/wireguard-tools/src/man/wg.8
@@ -0,0 +1,258 @@
+.TH WG 8 "2015 August 13" ZX2C4 "WireGuard"
+
+.SH NAME
+wg - set and retrieve configuration of WireGuard interfaces
+
+.SH SYNOPSIS
+.B wg
+[
+.I COMMAND
+] [
+.I OPTIONS
+]... [
+.I ARGS
+]...
+
+.SH DESCRIPTION
+
+.B wg
+is the configuration utility for getting and setting the configuration of
+WireGuard tunnel interfaces. The interfaces themselves can be added and removed
+using
+.BR ip-link (8)
+and their IP addresses and routing tables can be set using
+.BR ip-address (8)
+and
+.BR ip-route (8).
+The
+.B wg
+utility provides a series of sub-commands for changing WireGuard-specific
+aspects of WireGuard interfaces.
+
+If no COMMAND is specified, COMMAND defaults to
+.BR show .
+Sub-commands that take an INTERFACE must be passed a WireGuard interface.
+
+.SH COMMANDS
+
+.TP
+\fBshow\fP { \fI<interface>\fP | \fIall\fP | \fIinterfaces\fP } [\fIpublic-key\fP | \fIprivate-key\fP | \fIlisten-port\fP | \fIfwmark\fP | \fIpeers\fP | \fIpreshared-keys\fP | \fIendpoints\fP | \fIallowed-ips\fP | \fIlatest-handshakes\fP | \fIpersistent-keepalive\fP | \fItransfer\fP | \fIdump\fP]
+Shows current WireGuard configuration and runtime information of specified \fI<interface>\fP.
+If no \fI<interface>\fP is specified, \fI<interface>\fP defaults to \fIall\fP.
+If \fIinterfaces\fP is specified, prints a list of all WireGuard interfaces,
+one per line, and quits. If no options are given after the interface
+specification, then prints a list of all attributes in a visually pleasing way
+meant for the terminal. Otherwise, prints specified information grouped by
+newlines and tabs, meant to be used in scripts. For this script-friendly display,
+if \fIall\fP is specified, then the first field for all categories of information
+is the interface name. If \fPdump\fP is specified, then several lines are printed;
+the first contains in order separated by tab: private-key, public-key, listen-port,
+fwmark. Subsequent lines are printed for each peer and contain in order separated
+by tab: public-key, preshared-key, endpoint, allowed-ips, latest-handshake,
+transfer-rx, transfer-tx, persistent-keepalive.
+.TP
+\fBshowconf\fP \fI<interface>\fP
+Shows the current configuration of \fI<interface>\fP in the format described
+by \fICONFIGURATION FILE FORMAT\fP below.
+.TP
+\fBset\fP \fI<interface>\fP [\fIlisten-port\fP \fI<port>\fP] [\fIfwmark\fP \fI<fwmark>\fP] [\fIprivate-key\fP \fI<file-path>\fP] [\fIpeer\fP \fI<base64-public-key>\fP [\fIremove\fP] [\fIpreshared-key\fP \fI<file-path>\fP] [\fIendpoint\fP \fI<ip>:<port>\fP] [\fIpersistent-keepalive\fP \fI<interval seconds>\fP] [\fIallowed-ips\fP \fI<ip1>/<cidr1>\fP[,\fI<ip2>/<cidr2>\fP]...] ]...
+Sets configuration values for the specified \fI<interface>\fP. Multiple
+\fIpeer\fPs may be specified, and if the \fIremove\fP argument is given
+for a peer, that peer is removed, not configured. If \fIlisten-port\fP
+is not specified, or set to 0, the port will be chosen randomly when the
+interface comes up. Both \fIprivate-key\fP and \fIpreshared-key\fP must
+be files, because command line arguments are not considered private on
+most systems but if you are using
+.BR bash (1),
+you may safely pass in a string by specifying as \fIprivate-key\fP or
+\fIpreshared-key\fP the expression: <(echo PRIVATEKEYSTRING). If
+\fI/dev/null\fP or another empty file is specified as the filename for
+either \fIprivate-key\fP or \fIpreshared-key\fP, the key is removed from
+the device. The use of \fIpreshared-key\fP is optional, and may be omitted;
+it adds an additional layer of symmetric-key cryptography to be mixed into
+the already existing public-key cryptography, for post-quantum resistance.
+If \fIallowed-ips\fP is specified, but the value is the empty string, all
+allowed ips are removed from the peer. The use of \fIpersistent-keepalive\fP
+is optional and is by default off; setting it to 0 or "off" disables it.
+Otherwise it represents, in seconds, between 1 and 65535 inclusive, how often
+to send an authenticated empty packet to the peer, for the purpose of keeping
+a stateful firewall or NAT mapping valid persistently. For example, if the
+interface very rarely sends traffic, but it might at anytime receive traffic
+from a peer, and it is behind NAT, the interface might benefit from having a
+persistent keepalive interval of 25 seconds; however, most users will not need
+this. The use of \fIfwmark\fP is optional and is by default off; setting it to
+0 or "off" disables it. Otherwise it is a 32-bit fwmark for outgoing packets
+and may be specified in hexadecimal by prepending "0x".
+.TP
+\fBsetconf\fP \fI<interface>\fP \fI<configuration-filename>\fP
+Sets the current configuration of \fI<interface>\fP to the contents of
+\fI<configuration-filename>\fP, which must be in the format described
+by \fICONFIGURATION FILE FORMAT\fP below.
+.TP
+\fBaddconf\fP \fI<interface>\fP \fI<configuration-filename>\fP
+Appends the contents of \fI<configuration-filename>\fP, which must
+be in the format described by \fICONFIGURATION FILE FORMAT\fP below,
+to the current configuration of \fI<interface>\fP.
+.TP
+\fBsyncconf\fP \fI<interface>\fP \fI<configuration-filename>\fP
+Like \fBsetconf\fP, but reads back the existing configuration first
+and only makes changes that are explicitly different between the configuration
+file and the interface. This is much less efficient than \fBsetconf\fP,
+but has the benefit of not disrupting current peer sessions. The contents of
+\fI<configuration-filename>\fP must be in the format described by
+\fICONFIGURATION FILE FORMAT\fP below.
+.TP
+\fBgenkey\fP
+Generates a random \fIprivate\fP key in base64 and prints it to
+standard output.
+.TP
+\fBgenpsk\fP
+Generates a random \fIpreshared\fP key in base64 and prints it to
+standard output.
+.TP
+\fBpubkey\fP
+Calculates a \fIpublic\fP key and prints it in base64 to standard
+output from a corresponding \fIprivate\fP key (generated with
+\fIgenkey\fP) given in base64 on standard input.
+
+A private key and a corresponding public key may be generated at once by calling:
+.br
+ $ umask 077
+.br
+ $ wg genkey | tee private.key | wg pubkey > public.key
+.TP
+\fBhelp\fP
+Shows usage message.
+
+.SH CONFIGURATION FILE FORMAT
+The configuration file format is based on \fIINI\fP. There are two top level sections
+-- \fIInterface\fP and \fIPeer\fP. Multiple \fIPeer\fP sections may be specified, but
+only one \fIInterface\fP section may be specified.
+
+.P
+The \fIInterface\fP section may contain the following fields:
+.IP \(bu
+PrivateKey \(em a base64 private key generated by \fIwg genkey\fP. Required.
+.IP \(bu
+ListenPort \(em a 16-bit port for listening. Optional; if not specified, chosen
+randomly.
+.IP \(bu
+FwMark \(em a 32-bit fwmark for outgoing packets. If set to 0 or "off", this
+option is disabled. May be specified in hexadecimal by prepending "0x". Optional.
+.P
+The \fIPeer\fP sections may contain the following fields:
+.IP \(bu
+PublicKey \(em a base64 public key calculated by \fIwg pubkey\fP from a
+private key, and usually transmitted out of band to the author of the
+configuration file. Required.
+.IP \(bu
+PresharedKey \(em a base64 preshared key generated by \fIwg genpsk\fP. Optional,
+and may be omitted. This option adds an additional layer of symmetric-key
+cryptography to be mixed into the already existing public-key cryptography,
+for post-quantum resistance.
+.IP \(bu
+AllowedIPs \(em a comma-separated list of IP (v4 or v6) addresses with
+CIDR masks from which incoming traffic for this peer is allowed and to
+which outgoing traffic for this peer is directed. The catch-all
+\fI0.0.0.0/0\fP may be specified for matching all IPv4 addresses, and
+\fI::/0\fP may be specified for matching all IPv6 addresses. May be specified
+multiple times.
+.IP \(bu
+Endpoint \(em an endpoint IP or hostname, followed by a colon, and then a
+port number. This endpoint will be updated automatically to the most recent
+source IP address and port of correctly authenticated packets from the peer.
+Optional.
+.IP \(bu
+PersistentKeepalive \(em a seconds interval, between 1 and 65535 inclusive, of
+how often to send an authenticated empty packet to the peer for the purpose of keeping a
+stateful firewall or NAT mapping valid persistently. For example, if the interface
+very rarely sends traffic, but it might at anytime receive traffic from a peer,
+and it is behind NAT, the interface might benefit from having a persistent keepalive
+interval of 25 seconds. If set to 0 or "off", this option is disabled. By default or
+when unspecified, this option is off. Most users will not need this. Optional.
+
+.SH CONFIGURATION FILE FORMAT EXAMPLE
+This example may be used as a model for writing configuration files, following an
+INI-like syntax. Characters after and including a '#' are considered comments and
+are thus ignored.
+
+ [Interface]
+.br
+ PrivateKey = yAnz5TF+lXXJte14tji3zlMNq+hd2rYUIgJBgB3fBmk=
+.br
+ ListenPort = 51820
+.br
+
+.br
+ [Peer]
+.br
+ PublicKey = xTIBA5rboUvnH4htodjb6e697QjLERt1NAB4mZqp8Dg=
+.br
+ Endpoint = 192.95.5.67:1234
+.br
+ AllowedIPs = 10.192.122.3/32, 10.192.124.1/24
+.br
+
+.br
+ [Peer]
+.br
+ PublicKey = TrMvSoP4jYQlY6RIzBgbssQqY3vxI2Pi+y71lOWWXX0=
+.br
+ Endpoint = [2607:5300:60:6b0::c05f:543]:2468
+.br
+ AllowedIPs = 10.192.122.4/32, 192.168.0.0/16
+.br
+
+.br
+ [Peer]
+.br
+ PublicKey = gN65BkIKy1eCE9pP1wdc8ROUtkHLF2PfAqYdyYBz6EA=
+.br
+ Endpoint = test.wireguard.com:18981
+.br
+ AllowedIPs = 10.10.10.230/32
+
+.SH DEBUGGING INFORMATION
+Sometimes it is useful to have information on the current runtime state of a tunnel. When using the Linux kernel module on a kernel that supports dynamic debugging, debugging information can be written into
+.BR dmesg (1)
+by running as root:
+
+\fB # modprobe wireguard && echo module wireguard +p > /sys/kernel/debug/dynamic_debug/control\fP
+
+On OpenBSD and FreeBSD, debugging information can be written into
+.BR dmesg (1)
+on a per-interface basis by using
+.BR ifconfig (1):
+
+\fB # ifconfig wg0 debug
+
+On userspace implementations, it is customary to set the \fILOG_LEVEL\fP environment variable to \fIverbose\fP.
+
+.SH ENVIRONMENT VARIABLES
+.TP
+.I WG_COLOR_MODE
+If set to \fIalways\fP, always print ANSI colorized output. If set to \fInever\fP, never print ANSI colorized output. If set to \fIauto\fP, something invalid, or unset, then print ANSI colorized output only when writing to a TTY.
+.TP
+.I WG_HIDE_KEYS
+If set to \fInever\fP, then the pretty-printing \fBshow\fP sub-command will show private and preshared keys in the output. If set to \fIalways\fP, something invalid, or unset, then private and preshared keys will be printed as "(hidden)".
+.TP
+.I WG_ENDPOINT_RESOLUTION_RETRIES
+If set to an integer or to \fIinfinity\fP, DNS resolution for each peer's endpoint will be retried that many times for non-permanent errors, with an increasing delay between retries. If unset, the default is 15 retries.
+
+.SH SEE ALSO
+.BR wg-quick (8),
+.BR ip (8),
+.BR ip-link (8),
+.BR ip-address (8),
+.BR ip-route (8).
+
+.SH AUTHOR
+.B wg
+was written by
+.MT Jason@zx2c4.com
+Jason A. Donenfeld
+.ME .
+For updates and more information, a project page is available on the
+.UR https://\:www.wireguard.com/
+World Wide Web
+.UE .
diff --git a/contrib/wireguard-tools/src/netlink.h b/contrib/wireguard-tools/src/netlink.h
new file mode 100644
--- /dev/null
+++ b/contrib/wireguard-tools/src/netlink.h
@@ -0,0 +1,797 @@
+// SPDX-License-Identifier: LGPL-2.1+
+/*
+ * Copyright (C) 2015-2020 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
+ * Copyright (C) 2008-2012 Pablo Neira Ayuso <pablo@netfilter.org>.
+ */
+
+/* This is a minimized version of libmnl meant to be #include'd */
+
+#include <unistd.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <time.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <linux/netlink.h>
+#include <linux/genetlink.h>
+
+#define MNL_SOCKET_AUTOPID 0
+#define MNL_ALIGNTO 4
+#define MNL_ALIGN(len) (((len)+MNL_ALIGNTO-1) & ~(MNL_ALIGNTO-1))
+#define MNL_NLMSG_HDRLEN MNL_ALIGN(sizeof(struct nlmsghdr))
+#define MNL_ATTR_HDRLEN MNL_ALIGN(sizeof(struct nlattr))
+
+enum mnl_attr_data_type {
+ MNL_TYPE_UNSPEC,
+ MNL_TYPE_U8,
+ MNL_TYPE_U16,
+ MNL_TYPE_U32,
+ MNL_TYPE_U64,
+ MNL_TYPE_STRING,
+ MNL_TYPE_FLAG,
+ MNL_TYPE_MSECS,
+ MNL_TYPE_NESTED,
+ MNL_TYPE_NESTED_COMPAT,
+ MNL_TYPE_NUL_STRING,
+ MNL_TYPE_BINARY,
+ MNL_TYPE_MAX,
+};
+
+#define mnl_attr_for_each(attr, nlh, offset) \
+ for ((attr) = mnl_nlmsg_get_payload_offset((nlh), (offset)); \
+ mnl_attr_ok((attr), (char *)mnl_nlmsg_get_payload_tail(nlh) - (char *)(attr)); \
+ (attr) = mnl_attr_next(attr))
+
+#define mnl_attr_for_each_nested(attr, nest) \
+ for ((attr) = mnl_attr_get_payload(nest); \
+ mnl_attr_ok((attr), (char *)mnl_attr_get_payload(nest) + mnl_attr_get_payload_len(nest) - (char *)(attr)); \
+ (attr) = mnl_attr_next(attr))
+
+#define mnl_attr_for_each_payload(payload, payload_size) \
+ for ((attr) = (payload); \
+ mnl_attr_ok((attr), (char *)(payload) + payload_size - (char *)(attr)); \
+ (attr) = mnl_attr_next(attr))
+
+#define MNL_CB_ERROR -1
+#define MNL_CB_STOP 0
+#define MNL_CB_OK 1
+
+typedef int (*mnl_attr_cb_t)(const struct nlattr *attr, void *data);
+typedef int (*mnl_cb_t)(const struct nlmsghdr *nlh, void *data);
+
+#ifndef MNL_ARRAY_SIZE
+#define MNL_ARRAY_SIZE(a) (sizeof(a)/sizeof((a)[0]))
+#endif
+
+static size_t mnl_ideal_socket_buffer_size(void)
+{
+ static size_t size = 0;
+
+ if (size)
+ return size;
+ size = (size_t)sysconf(_SC_PAGESIZE);
+ if (size > 8192)
+ size = 8192;
+ return size;
+}
+
+static size_t mnl_nlmsg_size(size_t len)
+{
+ return len + MNL_NLMSG_HDRLEN;
+}
+
+static struct nlmsghdr *mnl_nlmsg_put_header(void *buf)
+{
+ int len = MNL_ALIGN(sizeof(struct nlmsghdr));
+ struct nlmsghdr *nlh = buf;
+
+ memset(buf, 0, len);
+ nlh->nlmsg_len = len;
+ return nlh;
+}
+
+static void *mnl_nlmsg_put_extra_header(struct nlmsghdr *nlh, size_t size)
+{
+ char *ptr = (char *)nlh + nlh->nlmsg_len;
+ size_t len = MNL_ALIGN(size);
+ nlh->nlmsg_len += len;
+ memset(ptr, 0, len);
+ return ptr;
+}
+
+static void *mnl_nlmsg_get_payload(const struct nlmsghdr *nlh)
+{
+ return (void *)nlh + MNL_NLMSG_HDRLEN;
+}
+
+static void *mnl_nlmsg_get_payload_offset(const struct nlmsghdr *nlh, size_t offset)
+{
+ return (void *)nlh + MNL_NLMSG_HDRLEN + MNL_ALIGN(offset);
+}
+
+static bool mnl_nlmsg_ok(const struct nlmsghdr *nlh, int len)
+{
+ return len >= (int)sizeof(struct nlmsghdr) &&
+ nlh->nlmsg_len >= sizeof(struct nlmsghdr) &&
+ (int)nlh->nlmsg_len <= len;
+}
+
+static struct nlmsghdr *mnl_nlmsg_next(const struct nlmsghdr *nlh, int *len)
+{
+ *len -= MNL_ALIGN(nlh->nlmsg_len);
+ return (struct nlmsghdr *)((void *)nlh + MNL_ALIGN(nlh->nlmsg_len));
+}
+
+static void *mnl_nlmsg_get_payload_tail(const struct nlmsghdr *nlh)
+{
+ return (void *)nlh + MNL_ALIGN(nlh->nlmsg_len);
+}
+
+static bool mnl_nlmsg_seq_ok(const struct nlmsghdr *nlh, unsigned int seq)
+{
+ return nlh->nlmsg_seq && seq ? nlh->nlmsg_seq == seq : true;
+}
+
+static bool mnl_nlmsg_portid_ok(const struct nlmsghdr *nlh, unsigned int portid)
+{
+ return nlh->nlmsg_pid && portid ? nlh->nlmsg_pid == portid : true;
+}
+
+static uint16_t mnl_attr_get_type(const struct nlattr *attr)
+{
+ return attr->nla_type & NLA_TYPE_MASK;
+}
+
+static uint16_t mnl_attr_get_payload_len(const struct nlattr *attr)
+{
+ return attr->nla_len - MNL_ATTR_HDRLEN;
+}
+
+static void *mnl_attr_get_payload(const struct nlattr *attr)
+{
+ return (void *)attr + MNL_ATTR_HDRLEN;
+}
+
+static bool mnl_attr_ok(const struct nlattr *attr, int len)
+{
+ return len >= (int)sizeof(struct nlattr) &&
+ attr->nla_len >= sizeof(struct nlattr) &&
+ (int)attr->nla_len <= len;
+}
+
+static struct nlattr *mnl_attr_next(const struct nlattr *attr)
+{
+ return (struct nlattr *)((void *)attr + MNL_ALIGN(attr->nla_len));
+}
+
+static int mnl_attr_type_valid(const struct nlattr *attr, uint16_t max)
+{
+ if (mnl_attr_get_type(attr) > max) {
+ errno = EOPNOTSUPP;
+ return -1;
+ }
+ return 1;
+}
+
+static int __mnl_attr_validate(const struct nlattr *attr,
+ enum mnl_attr_data_type type, size_t exp_len)
+{
+ uint16_t attr_len = mnl_attr_get_payload_len(attr);
+ const char *attr_data = mnl_attr_get_payload(attr);
+
+ if (attr_len < exp_len) {
+ errno = ERANGE;
+ return -1;
+ }
+ switch(type) {
+ case MNL_TYPE_FLAG:
+ if (attr_len > 0) {
+ errno = ERANGE;
+ return -1;
+ }
+ break;
+ case MNL_TYPE_NUL_STRING:
+ if (attr_len == 0) {
+ errno = ERANGE;
+ return -1;
+ }
+ if (attr_data[attr_len-1] != '\0') {
+ errno = EINVAL;
+ return -1;
+ }
+ break;
+ case MNL_TYPE_STRING:
+ if (attr_len == 0) {
+ errno = ERANGE;
+ return -1;
+ }
+ break;
+ case MNL_TYPE_NESTED:
+
+ if (attr_len == 0)
+ break;
+
+ if (attr_len < MNL_ATTR_HDRLEN) {
+ errno = ERANGE;
+ return -1;
+ }
+ break;
+ default:
+
+ break;
+ }
+ if (exp_len && attr_len > exp_len) {
+ errno = ERANGE;
+ return -1;
+ }
+ return 0;
+}
+
+static const size_t mnl_attr_data_type_len[MNL_TYPE_MAX] = {
+ [MNL_TYPE_U8] = sizeof(uint8_t),
+ [MNL_TYPE_U16] = sizeof(uint16_t),
+ [MNL_TYPE_U32] = sizeof(uint32_t),
+ [MNL_TYPE_U64] = sizeof(uint64_t),
+ [MNL_TYPE_MSECS] = sizeof(uint64_t),
+};
+
+static int mnl_attr_validate(const struct nlattr *attr, enum mnl_attr_data_type type)
+{
+ int exp_len;
+
+ if (type >= MNL_TYPE_MAX) {
+ errno = EINVAL;
+ return -1;
+ }
+ exp_len = mnl_attr_data_type_len[type];
+ return __mnl_attr_validate(attr, type, exp_len);
+}
+
+static int mnl_attr_parse(const struct nlmsghdr *nlh, unsigned int offset,
+ mnl_attr_cb_t cb, void *data)
+{
+ int ret = MNL_CB_OK;
+ const struct nlattr *attr;
+
+ mnl_attr_for_each(attr, nlh, offset)
+ if ((ret = cb(attr, data)) <= MNL_CB_STOP)
+ return ret;
+ return ret;
+}
+
+static int mnl_attr_parse_nested(const struct nlattr *nested, mnl_attr_cb_t cb,
+ void *data)
+{
+ int ret = MNL_CB_OK;
+ const struct nlattr *attr;
+
+ mnl_attr_for_each_nested(attr, nested)
+ if ((ret = cb(attr, data)) <= MNL_CB_STOP)
+ return ret;
+ return ret;
+}
+
+static uint8_t mnl_attr_get_u8(const struct nlattr *attr)
+{
+ return *((uint8_t *)mnl_attr_get_payload(attr));
+}
+
+static uint16_t mnl_attr_get_u16(const struct nlattr *attr)
+{
+ return *((uint16_t *)mnl_attr_get_payload(attr));
+}
+
+static uint32_t mnl_attr_get_u32(const struct nlattr *attr)
+{
+ return *((uint32_t *)mnl_attr_get_payload(attr));
+}
+
+static uint64_t mnl_attr_get_u64(const struct nlattr *attr)
+{
+ uint64_t tmp;
+ memcpy(&tmp, mnl_attr_get_payload(attr), sizeof(tmp));
+ return tmp;
+}
+
+static const char *mnl_attr_get_str(const struct nlattr *attr)
+{
+ return mnl_attr_get_payload(attr);
+}
+
+static void mnl_attr_put(struct nlmsghdr *nlh, uint16_t type, size_t len,
+ const void *data)
+{
+ struct nlattr *attr = mnl_nlmsg_get_payload_tail(nlh);
+ uint16_t payload_len = MNL_ALIGN(sizeof(struct nlattr)) + len;
+ int pad;
+
+ attr->nla_type = type;
+ attr->nla_len = payload_len;
+ memcpy(mnl_attr_get_payload(attr), data, len);
+ nlh->nlmsg_len += MNL_ALIGN(payload_len);
+ pad = MNL_ALIGN(len) - len;
+ if (pad > 0)
+ memset(mnl_attr_get_payload(attr) + len, 0, pad);
+}
+
+static void mnl_attr_put_u16(struct nlmsghdr *nlh, uint16_t type, uint16_t data)
+{
+ mnl_attr_put(nlh, type, sizeof(uint16_t), &data);
+}
+
+static void mnl_attr_put_u32(struct nlmsghdr *nlh, uint16_t type, uint32_t data)
+{
+ mnl_attr_put(nlh, type, sizeof(uint32_t), &data);
+}
+
+static void mnl_attr_put_strz(struct nlmsghdr *nlh, uint16_t type, const char *data)
+{
+ mnl_attr_put(nlh, type, strlen(data)+1, data);
+}
+
+static struct nlattr *mnl_attr_nest_start(struct nlmsghdr *nlh, uint16_t type)
+{
+ struct nlattr *start = mnl_nlmsg_get_payload_tail(nlh);
+
+ start->nla_type = NLA_F_NESTED | type;
+ nlh->nlmsg_len += MNL_ALIGN(sizeof(struct nlattr));
+ return start;
+}
+
+static bool mnl_attr_put_check(struct nlmsghdr *nlh, size_t buflen,
+ uint16_t type, size_t len, const void *data)
+{
+ if (nlh->nlmsg_len + MNL_ATTR_HDRLEN + MNL_ALIGN(len) > buflen)
+ return false;
+ mnl_attr_put(nlh, type, len, data);
+ return true;
+}
+
+static bool mnl_attr_put_u8_check(struct nlmsghdr *nlh, size_t buflen,
+ uint16_t type, uint8_t data)
+{
+ return mnl_attr_put_check(nlh, buflen, type, sizeof(uint8_t), &data);
+}
+
+static bool mnl_attr_put_u16_check(struct nlmsghdr *nlh, size_t buflen,
+ uint16_t type, uint16_t data)
+{
+ return mnl_attr_put_check(nlh, buflen, type, sizeof(uint16_t), &data);
+}
+
+static bool mnl_attr_put_u32_check(struct nlmsghdr *nlh, size_t buflen,
+ uint16_t type, uint32_t data)
+{
+ return mnl_attr_put_check(nlh, buflen, type, sizeof(uint32_t), &data);
+}
+
+static struct nlattr *mnl_attr_nest_start_check(struct nlmsghdr *nlh, size_t buflen,
+ uint16_t type)
+{
+ if (nlh->nlmsg_len + MNL_ATTR_HDRLEN > buflen)
+ return NULL;
+ return mnl_attr_nest_start(nlh, type);
+}
+
+static void mnl_attr_nest_end(struct nlmsghdr *nlh, struct nlattr *start)
+{
+ start->nla_len = mnl_nlmsg_get_payload_tail(nlh) - (void *)start;
+}
+
+static void mnl_attr_nest_cancel(struct nlmsghdr *nlh, struct nlattr *start)
+{
+ nlh->nlmsg_len -= mnl_nlmsg_get_payload_tail(nlh) - (void *)start;
+}
+
+static int mnl_cb_noop(__attribute__((unused)) const struct nlmsghdr *nlh, __attribute__((unused)) void *data)
+{
+ return MNL_CB_OK;
+}
+
+static int mnl_cb_error(const struct nlmsghdr *nlh, __attribute__((unused)) void *data)
+{
+ const struct nlmsgerr *err = mnl_nlmsg_get_payload(nlh);
+
+ if (nlh->nlmsg_len < mnl_nlmsg_size(sizeof(struct nlmsgerr))) {
+ errno = EBADMSG;
+ return MNL_CB_ERROR;
+ }
+
+ if (err->error < 0)
+ errno = -err->error;
+ else
+ errno = err->error;
+
+ return err->error == 0 ? MNL_CB_STOP : MNL_CB_ERROR;
+}
+
+static int mnl_cb_stop(__attribute__((unused)) const struct nlmsghdr *nlh, __attribute__((unused)) void *data)
+{
+ return MNL_CB_STOP;
+}
+
+static const mnl_cb_t default_cb_array[NLMSG_MIN_TYPE] = {
+ [NLMSG_NOOP] = mnl_cb_noop,
+ [NLMSG_ERROR] = mnl_cb_error,
+ [NLMSG_DONE] = mnl_cb_stop,
+ [NLMSG_OVERRUN] = mnl_cb_noop,
+};
+
+static int __mnl_cb_run(const void *buf, size_t numbytes,
+ unsigned int seq, unsigned int portid,
+ mnl_cb_t cb_data, void *data,
+ const mnl_cb_t *cb_ctl_array,
+ unsigned int cb_ctl_array_len)
+{
+ int ret = MNL_CB_OK, len = numbytes;
+ const struct nlmsghdr *nlh = buf;
+
+ while (mnl_nlmsg_ok(nlh, len)) {
+
+ if (!mnl_nlmsg_portid_ok(nlh, portid)) {
+ errno = ESRCH;
+ return -1;
+ }
+
+ if (!mnl_nlmsg_seq_ok(nlh, seq)) {
+ errno = EPROTO;
+ return -1;
+ }
+
+ if (nlh->nlmsg_flags & NLM_F_DUMP_INTR) {
+ errno = EINTR;
+ return -1;
+ }
+
+ if (nlh->nlmsg_type >= NLMSG_MIN_TYPE) {
+ if (cb_data){
+ ret = cb_data(nlh, data);
+ if (ret <= MNL_CB_STOP)
+ goto out;
+ }
+ } else if (nlh->nlmsg_type < cb_ctl_array_len) {
+ if (cb_ctl_array && cb_ctl_array[nlh->nlmsg_type]) {
+ ret = cb_ctl_array[nlh->nlmsg_type](nlh, data);
+ if (ret <= MNL_CB_STOP)
+ goto out;
+ }
+ } else if (default_cb_array[nlh->nlmsg_type]) {
+ ret = default_cb_array[nlh->nlmsg_type](nlh, data);
+ if (ret <= MNL_CB_STOP)
+ goto out;
+ }
+ nlh = mnl_nlmsg_next(nlh, &len);
+ }
+out:
+ return ret;
+}
+
+static int mnl_cb_run2(const void *buf, size_t numbytes, unsigned int seq,
+ unsigned int portid, mnl_cb_t cb_data, void *data,
+ const mnl_cb_t *cb_ctl_array, unsigned int cb_ctl_array_len)
+{
+ return __mnl_cb_run(buf, numbytes, seq, portid, cb_data, data,
+ cb_ctl_array, cb_ctl_array_len);
+}
+
+static int mnl_cb_run(const void *buf, size_t numbytes, unsigned int seq,
+ unsigned int portid, mnl_cb_t cb_data, void *data)
+{
+ return __mnl_cb_run(buf, numbytes, seq, portid, cb_data, data, NULL, 0);
+}
+
+struct mnl_socket {
+ int fd;
+ struct sockaddr_nl addr;
+};
+
+static unsigned int mnl_socket_get_portid(const struct mnl_socket *nl)
+{
+ return nl->addr.nl_pid;
+}
+
+static struct mnl_socket *__mnl_socket_open(int bus, int flags)
+{
+ struct mnl_socket *nl;
+
+ nl = calloc(1, sizeof(struct mnl_socket));
+ if (nl == NULL)
+ return NULL;
+
+ nl->fd = socket(AF_NETLINK, SOCK_RAW | flags, bus);
+ if (nl->fd == -1) {
+ free(nl);
+ return NULL;
+ }
+
+ return nl;
+}
+
+static struct mnl_socket *mnl_socket_open(int bus)
+{
+ return __mnl_socket_open(bus, 0);
+}
+
+static int mnl_socket_bind(struct mnl_socket *nl, unsigned int groups, pid_t pid)
+{
+ int ret;
+ socklen_t addr_len;
+
+ nl->addr.nl_family = AF_NETLINK;
+ nl->addr.nl_groups = groups;
+ nl->addr.nl_pid = pid;
+
+ ret = bind(nl->fd, (struct sockaddr *) &nl->addr, sizeof (nl->addr));
+ if (ret < 0)
+ return ret;
+
+ addr_len = sizeof(nl->addr);
+ ret = getsockname(nl->fd, (struct sockaddr *) &nl->addr, &addr_len);
+ if (ret < 0)
+ return ret;
+
+ if (addr_len != sizeof(nl->addr)) {
+ errno = EINVAL;
+ return -1;
+ }
+ if (nl->addr.nl_family != AF_NETLINK) {
+ errno = EINVAL;
+ return -1;
+ }
+ return 0;
+}
+
+static ssize_t mnl_socket_sendto(const struct mnl_socket *nl, const void *buf,
+ size_t len)
+{
+ static const struct sockaddr_nl snl = {
+ .nl_family = AF_NETLINK
+ };
+ return sendto(nl->fd, buf, len, 0,
+ (struct sockaddr *) &snl, sizeof(snl));
+}
+
+static ssize_t mnl_socket_recvfrom(const struct mnl_socket *nl, void *buf,
+ size_t bufsiz)
+{
+ ssize_t ret;
+ struct sockaddr_nl addr;
+ struct iovec iov = {
+ .iov_base = buf,
+ .iov_len = bufsiz,
+ };
+ struct msghdr msg = {
+ .msg_name = &addr,
+ .msg_namelen = sizeof(struct sockaddr_nl),
+ .msg_iov = &iov,
+ .msg_iovlen = 1,
+ .msg_control = NULL,
+ .msg_controllen = 0,
+ .msg_flags = 0,
+ };
+ ret = recvmsg(nl->fd, &msg, 0);
+ if (ret == -1)
+ return ret;
+
+ if (msg.msg_flags & MSG_TRUNC) {
+ errno = ENOSPC;
+ return -1;
+ }
+ if (msg.msg_namelen != sizeof(struct sockaddr_nl)) {
+ errno = EINVAL;
+ return -1;
+ }
+ return ret;
+}
+
+static int mnl_socket_close(struct mnl_socket *nl)
+{
+ int ret = close(nl->fd);
+ free(nl);
+ return ret;
+}
+
+/* This is a wrapper for generic netlink, originally from Jiri Pirko <jiri@mellanox.com>: */
+
+struct mnlg_socket {
+ struct mnl_socket *nl;
+ char *buf;
+ uint16_t id;
+ uint8_t version;
+ unsigned int seq;
+ unsigned int portid;
+};
+
+static struct nlmsghdr *__mnlg_msg_prepare(struct mnlg_socket *nlg, uint8_t cmd,
+ uint16_t flags, uint16_t id,
+ uint8_t version)
+{
+ struct nlmsghdr *nlh;
+ struct genlmsghdr *genl;
+
+ nlh = mnl_nlmsg_put_header(nlg->buf);
+ nlh->nlmsg_type = id;
+ nlh->nlmsg_flags = flags;
+ nlg->seq = time(NULL);
+ nlh->nlmsg_seq = nlg->seq;
+
+ genl = mnl_nlmsg_put_extra_header(nlh, sizeof(struct genlmsghdr));
+ genl->cmd = cmd;
+ genl->version = version;
+
+ return nlh;
+}
+
+static struct nlmsghdr *mnlg_msg_prepare(struct mnlg_socket *nlg, uint8_t cmd,
+ uint16_t flags)
+{
+ return __mnlg_msg_prepare(nlg, cmd, flags, nlg->id, nlg->version);
+}
+
+static int mnlg_socket_send(struct mnlg_socket *nlg, const struct nlmsghdr *nlh)
+{
+ return mnl_socket_sendto(nlg->nl, nlh, nlh->nlmsg_len);
+}
+
+static int mnlg_cb_noop(const struct nlmsghdr *nlh, void *data)
+{
+ (void)nlh;
+ (void)data;
+ return MNL_CB_OK;
+}
+
+static int mnlg_cb_error(const struct nlmsghdr *nlh, void *data)
+{
+ const struct nlmsgerr *err = mnl_nlmsg_get_payload(nlh);
+ (void)data;
+
+ if (nlh->nlmsg_len < mnl_nlmsg_size(sizeof(struct nlmsgerr))) {
+ errno = EBADMSG;
+ return MNL_CB_ERROR;
+ }
+ /* Netlink subsystems returns the errno value with different signess */
+ if (err->error < 0)
+ errno = -err->error;
+ else
+ errno = err->error;
+
+ return err->error == 0 ? MNL_CB_STOP : MNL_CB_ERROR;
+}
+
+static int mnlg_cb_stop(const struct nlmsghdr *nlh, void *data)
+{
+ (void)data;
+ if (nlh->nlmsg_flags & NLM_F_MULTI && nlh->nlmsg_len == mnl_nlmsg_size(sizeof(int))) {
+ int error = *(int *)mnl_nlmsg_get_payload(nlh);
+ /* Netlink subsystems returns the errno value with different signess */
+ if (error < 0)
+ errno = -error;
+ else
+ errno = error;
+
+ return error == 0 ? MNL_CB_STOP : MNL_CB_ERROR;
+ }
+ return MNL_CB_STOP;
+}
+
+static const mnl_cb_t mnlg_cb_array[] = {
+ [NLMSG_NOOP] = mnlg_cb_noop,
+ [NLMSG_ERROR] = mnlg_cb_error,
+ [NLMSG_DONE] = mnlg_cb_stop,
+ [NLMSG_OVERRUN] = mnlg_cb_noop,
+};
+
+static int mnlg_socket_recv_run(struct mnlg_socket *nlg, mnl_cb_t data_cb, void *data)
+{
+ int err;
+
+ do {
+ err = mnl_socket_recvfrom(nlg->nl, nlg->buf,
+ mnl_ideal_socket_buffer_size());
+ if (err <= 0)
+ break;
+ err = mnl_cb_run2(nlg->buf, err, nlg->seq, nlg->portid,
+ data_cb, data, mnlg_cb_array, MNL_ARRAY_SIZE(mnlg_cb_array));
+ } while (err > 0);
+
+ return err;
+}
+
+static int get_family_id_attr_cb(const struct nlattr *attr, void *data)
+{
+ const struct nlattr **tb = data;
+ int type = mnl_attr_get_type(attr);
+
+ if (mnl_attr_type_valid(attr, CTRL_ATTR_MAX) < 0)
+ return MNL_CB_ERROR;
+
+ if (type == CTRL_ATTR_FAMILY_ID &&
+ mnl_attr_validate(attr, MNL_TYPE_U16) < 0)
+ return MNL_CB_ERROR;
+ tb[type] = attr;
+ return MNL_CB_OK;
+}
+
+static int get_family_id_cb(const struct nlmsghdr *nlh, void *data)
+{
+ uint16_t *p_id = data;
+ struct nlattr *tb[CTRL_ATTR_MAX + 1] = { 0 };
+
+ mnl_attr_parse(nlh, sizeof(struct genlmsghdr), get_family_id_attr_cb, tb);
+ if (!tb[CTRL_ATTR_FAMILY_ID])
+ return MNL_CB_ERROR;
+ *p_id = mnl_attr_get_u16(tb[CTRL_ATTR_FAMILY_ID]);
+ return MNL_CB_OK;
+}
+
+static struct mnlg_socket *mnlg_socket_open(const char *family_name, uint8_t version)
+{
+ struct mnlg_socket *nlg;
+ struct nlmsghdr *nlh;
+ int err;
+
+ nlg = malloc(sizeof(*nlg));
+ if (!nlg)
+ return NULL;
+ nlg->id = 0;
+
+ err = -ENOMEM;
+ nlg->buf = malloc(mnl_ideal_socket_buffer_size());
+ if (!nlg->buf)
+ goto err_buf_alloc;
+
+ nlg->nl = mnl_socket_open(NETLINK_GENERIC);
+ if (!nlg->nl) {
+ err = -errno;
+ goto err_mnl_socket_open;
+ }
+
+ if (mnl_socket_bind(nlg->nl, 0, MNL_SOCKET_AUTOPID) < 0) {
+ err = -errno;
+ goto err_mnl_socket_bind;
+ }
+
+ nlg->portid = mnl_socket_get_portid(nlg->nl);
+
+ nlh = __mnlg_msg_prepare(nlg, CTRL_CMD_GETFAMILY,
+ NLM_F_REQUEST | NLM_F_ACK, GENL_ID_CTRL, 1);
+ mnl_attr_put_strz(nlh, CTRL_ATTR_FAMILY_NAME, family_name);
+
+ if (mnlg_socket_send(nlg, nlh) < 0) {
+ err = -errno;
+ goto err_mnlg_socket_send;
+ }
+
+ errno = 0;
+ if (mnlg_socket_recv_run(nlg, get_family_id_cb, &nlg->id) < 0) {
+ errno = errno == ENOENT ? EPROTONOSUPPORT : errno;
+ err = errno ? -errno : -ENOSYS;
+ goto err_mnlg_socket_recv_run;
+ }
+
+ nlg->version = version;
+ errno = 0;
+ return nlg;
+
+err_mnlg_socket_recv_run:
+err_mnlg_socket_send:
+err_mnl_socket_bind:
+ mnl_socket_close(nlg->nl);
+err_mnl_socket_open:
+ free(nlg->buf);
+err_buf_alloc:
+ free(nlg);
+ errno = -err;
+ return NULL;
+}
+
+static void mnlg_socket_close(struct mnlg_socket *nlg)
+{
+ mnl_socket_close(nlg->nl);
+ free(nlg->buf);
+ free(nlg);
+}
diff --git a/contrib/wireguard-tools/src/pubkey.c b/contrib/wireguard-tools/src/pubkey.c
new file mode 100644
--- /dev/null
+++ b/contrib/wireguard-tools/src/pubkey.c
@@ -0,0 +1,50 @@
+// SPDX-License-Identifier: GPL-2.0 OR MIT
+/*
+ * Copyright (C) 2015-2020 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
+ */
+
+#include <errno.h>
+#include <stdio.h>
+
+#include "curve25519.h"
+#include "encoding.h"
+#include "subcommands.h"
+#include "ctype.h"
+
+int pubkey_main(int argc, const char *argv[])
+{
+ uint8_t key[WG_KEY_LEN] __attribute__((aligned(sizeof(uintptr_t))));
+ char base64[WG_KEY_LEN_BASE64];
+ int trailing_char;
+
+ if (argc != 1) {
+ fprintf(stderr, "Usage: %s %s\n", PROG_NAME, argv[0]);
+ return 1;
+ }
+
+ if (fread(base64, 1, sizeof(base64) - 1, stdin) != sizeof(base64) - 1) {
+ errno = EINVAL;
+ fprintf(stderr, "%s: Key is not the correct length or format\n", PROG_NAME);
+ return 1;
+ }
+ base64[WG_KEY_LEN_BASE64 - 1] = '\0';
+
+ for (;;) {
+ trailing_char = getc(stdin);
+ if (!trailing_char || char_is_space(trailing_char))
+ continue;
+ if (trailing_char == EOF)
+ break;
+ fprintf(stderr, "%s: Trailing characters found after key\n", PROG_NAME);
+ return 1;
+ }
+
+ if (!key_from_base64(key, base64)) {
+ fprintf(stderr, "%s: Key is not the correct length or format\n", PROG_NAME);
+ return 1;
+ }
+ curve25519_generate_public(key, key);
+ key_to_base64(base64, key);
+ puts(base64);
+ return 0;
+}
diff --git a/contrib/wireguard-tools/src/set.c b/contrib/wireguard-tools/src/set.c
new file mode 100644
--- /dev/null
+++ b/contrib/wireguard-tools/src/set.c
@@ -0,0 +1,41 @@
+// SPDX-License-Identifier: GPL-2.0 OR MIT
+/*
+ * Copyright (C) 2015-2020 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "containers.h"
+#include "config.h"
+#include "ipc.h"
+#include "subcommands.h"
+
+int set_main(int argc, const char *argv[])
+{
+ struct wgdevice *device = NULL;
+ int ret = 1;
+
+ if (argc < 3) {
+ fprintf(stderr, "Usage: %s %s <interface> [listen-port <port>] [fwmark <mark>] [private-key <file path>] [peer <base64 public key> [remove] [preshared-key <file path>] [endpoint <ip>:<port>] [persistent-keepalive <interval seconds>] [allowed-ips <ip1>/<cidr1>[,<ip2>/<cidr2>]...] ]...\n", PROG_NAME, argv[0]);
+ return 1;
+ }
+
+ device = config_read_cmd(argv + 2, argc - 2);
+ if (!device)
+ goto cleanup;
+ strncpy(device->name, argv[1], IFNAMSIZ - 1);
+ device->name[IFNAMSIZ - 1] = '\0';
+
+ if (ipc_set_device(device) != 0) {
+ perror("Unable to modify interface");
+ goto cleanup;
+ }
+
+ ret = 0;
+
+cleanup:
+ free_wgdevice(device);
+ return ret;
+}
diff --git a/contrib/wireguard-tools/src/setconf.c b/contrib/wireguard-tools/src/setconf.c
new file mode 100644
--- /dev/null
+++ b/contrib/wireguard-tools/src/setconf.c
@@ -0,0 +1,156 @@
+// SPDX-License-Identifier: GPL-2.0 OR MIT
+/*
+ * Copyright (C) 2015-2020 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
+ */
+
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "containers.h"
+#include "config.h"
+#include "ipc.h"
+#include "subcommands.h"
+
+struct pubkey_origin {
+ uint8_t *pubkey;
+ bool from_file;
+};
+
+static int pubkey_cmp(const void *first, const void *second)
+{
+ const struct pubkey_origin *a = first, *b = second;
+ int ret = memcmp(a->pubkey, b->pubkey, WG_KEY_LEN);
+ if (ret)
+ return ret;
+ return a->from_file - b->from_file;
+}
+
+static bool sync_conf(struct wgdevice *file)
+{
+ struct wgdevice *runtime;
+ struct wgpeer *peer;
+ struct pubkey_origin *pubkeys;
+ size_t peer_count = 0, i = 0;
+
+ if (!file->first_peer)
+ return true;
+
+ for_each_wgpeer(file, peer)
+ ++peer_count;
+
+ if (ipc_get_device(&runtime, file->name) != 0) {
+ perror("Unable to retrieve current interface configuration");
+ return false;
+ }
+
+ if (!runtime->first_peer) {
+ free_wgdevice(runtime);
+ return true;
+ }
+
+ file->flags &= ~WGDEVICE_REPLACE_PEERS;
+
+ for_each_wgpeer(runtime, peer)
+ ++peer_count;
+
+ pubkeys = calloc(peer_count, sizeof(*pubkeys));
+ if (!pubkeys) {
+ free_wgdevice(runtime);
+ perror("Public key allocation");
+ return false;
+ }
+
+ for_each_wgpeer(file, peer) {
+ pubkeys[i].pubkey = peer->public_key;
+ pubkeys[i].from_file = true;
+ ++i;
+ }
+ for_each_wgpeer(runtime, peer) {
+ pubkeys[i].pubkey = peer->public_key;
+ pubkeys[i].from_file = false;
+ ++i;
+ }
+ qsort(pubkeys, peer_count, sizeof(*pubkeys), pubkey_cmp);
+
+ for (i = 0; i < peer_count; ++i) {
+ if (pubkeys[i].from_file)
+ continue;
+ if (i == peer_count - 1 || !pubkeys[i + 1].from_file || memcmp(pubkeys[i].pubkey, pubkeys[i + 1].pubkey, WG_KEY_LEN)) {
+ peer = calloc(1, sizeof(struct wgpeer));
+ if (!peer) {
+ free_wgdevice(runtime);
+ free(pubkeys);
+ perror("Peer allocation");
+ return false;
+ }
+ peer->flags = WGPEER_REMOVE_ME;
+ memcpy(peer->public_key, pubkeys[i].pubkey, WG_KEY_LEN);
+ peer->next_peer = file->first_peer;
+ file->first_peer = peer;
+ if (!file->last_peer)
+ file->last_peer = peer;
+ }
+ }
+ free_wgdevice(runtime);
+ free(pubkeys);
+ return true;
+}
+
+int setconf_main(int argc, const char *argv[])
+{
+ struct wgdevice *device = NULL;
+ struct config_ctx ctx;
+ FILE *config_input = NULL;
+ char *config_buffer = NULL;
+ size_t config_buffer_len = 0;
+ int ret = 1;
+
+ if (argc != 3) {
+ fprintf(stderr, "Usage: %s %s <interface> <configuration filename>\n", PROG_NAME, argv[0]);
+ return 1;
+ }
+
+ config_input = fopen(argv[2], "r");
+ if (!config_input) {
+ perror("fopen");
+ return 1;
+ }
+ if (!config_read_init(&ctx, !strcmp(argv[0], "addconf"))) {
+ fclose(config_input);
+ return 1;
+ }
+ while (getline(&config_buffer, &config_buffer_len, config_input) >= 0) {
+ if (!config_read_line(&ctx, config_buffer)) {
+ fprintf(stderr, "Configuration parsing error\n");
+ goto cleanup;
+ }
+ }
+ device = config_read_finish(&ctx);
+ if (!device) {
+ fprintf(stderr, "Invalid configuration\n");
+ goto cleanup;
+ }
+ strncpy(device->name, argv[1], IFNAMSIZ - 1);
+ device->name[IFNAMSIZ - 1] = '\0';
+
+ if (!strcmp(argv[0], "syncconf")) {
+ if (!sync_conf(device))
+ goto cleanup;
+ }
+
+ if (ipc_set_device(device) != 0) {
+ perror("Unable to modify interface");
+ goto cleanup;
+ }
+
+ ret = 0;
+
+cleanup:
+ if (config_input)
+ fclose(config_input);
+ free(config_buffer);
+ free_wgdevice(device);
+ return ret;
+}
diff --git a/contrib/wireguard-tools/src/show.c b/contrib/wireguard-tools/src/show.c
new file mode 100644
--- /dev/null
+++ b/contrib/wireguard-tools/src/show.c
@@ -0,0 +1,454 @@
+// SPDX-License-Identifier: GPL-2.0 OR MIT
+/*
+ * Copyright (C) 2015-2020 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
+ */
+
+#include <arpa/inet.h>
+#include <inttypes.h>
+#include <netinet/in.h>
+#include <sys/socket.h>
+#include <net/if.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <time.h>
+#include <netdb.h>
+
+#include "containers.h"
+#include "ipc.h"
+#include "terminal.h"
+#include "encoding.h"
+#include "subcommands.h"
+
+static int peer_cmp(const void *first, const void *second)
+{
+ time_t diff;
+ const struct wgpeer *a = *(const void **)first, *b = *(const void **)second;
+
+ if (!a->last_handshake_time.tv_sec && !a->last_handshake_time.tv_nsec && (b->last_handshake_time.tv_sec || b->last_handshake_time.tv_nsec))
+ return 1;
+ if (!b->last_handshake_time.tv_sec && !b->last_handshake_time.tv_nsec && (a->last_handshake_time.tv_sec || a->last_handshake_time.tv_nsec))
+ return -1;
+ diff = a->last_handshake_time.tv_sec - b->last_handshake_time.tv_sec;
+ if (!diff)
+ diff = a->last_handshake_time.tv_nsec - b->last_handshake_time.tv_nsec;
+ if (diff < 0)
+ return 1;
+ if (diff > 0)
+ return -1;
+ return 0;
+}
+
+/* This, hilariously, is not the right way to sort a linked list... */
+static void sort_peers(struct wgdevice *device)
+{
+ size_t peer_count = 0, i = 0;
+ struct wgpeer *peer, **peers;
+
+ for_each_wgpeer(device, peer)
+ ++peer_count;
+ if (!peer_count)
+ return;
+ peers = calloc(peer_count, sizeof(*peers));
+ if (!peers)
+ return;
+ for_each_wgpeer(device, peer)
+ peers[i++] = peer;
+ qsort(peers, peer_count, sizeof(*peers), peer_cmp);
+ device->first_peer = peers[0];
+ for (i = 1; i < peer_count; ++i) {
+ peers[i - 1]->next_peer = peers[i];
+ }
+ peers[peer_count - 1]->next_peer = NULL;
+ free(peers);
+}
+
+static char *key(const uint8_t key[static WG_KEY_LEN])
+{
+ static char base64[WG_KEY_LEN_BASE64];
+
+ key_to_base64(base64, key);
+ return base64;
+}
+
+static const char *maybe_key(const uint8_t maybe_key[static WG_KEY_LEN], bool have_it)
+{
+ if (!have_it)
+ return "(none)";
+ return key(maybe_key);
+}
+
+static const char *masked_key(const uint8_t masked_key[static WG_KEY_LEN])
+{
+ const char *var = getenv("WG_HIDE_KEYS");
+
+ if (var && !strcmp(var, "never"))
+ return key(masked_key);
+ return "(hidden)";
+}
+
+static char *ip(const struct wgallowedip *ip)
+{
+ static char buf[INET6_ADDRSTRLEN + 1];
+
+ memset(buf, 0, INET6_ADDRSTRLEN + 1);
+ if (ip->family == AF_INET)
+ inet_ntop(AF_INET, &ip->ip4, buf, INET6_ADDRSTRLEN);
+ else if (ip->family == AF_INET6)
+ inet_ntop(AF_INET6, &ip->ip6, buf, INET6_ADDRSTRLEN);
+ return buf;
+}
+
+static char *endpoint(const struct sockaddr *addr)
+{
+ char host[4096 + 1];
+ char service[512 + 1];
+ static char buf[sizeof(host) + sizeof(service) + 4];
+ int ret;
+ socklen_t addr_len = 0;
+
+ memset(buf, 0, sizeof(buf));
+ if (addr->sa_family == AF_INET)
+ addr_len = sizeof(struct sockaddr_in);
+ else if (addr->sa_family == AF_INET6)
+ addr_len = sizeof(struct sockaddr_in6);
+
+ ret = getnameinfo(addr, addr_len, host, sizeof(host), service, sizeof(service), NI_DGRAM | NI_NUMERICSERV | NI_NUMERICHOST);
+ if (ret) {
+ strncpy(buf, gai_strerror(ret), sizeof(buf) - 1);
+ buf[sizeof(buf) - 1] = '\0';
+ } else
+ snprintf(buf, sizeof(buf), (addr->sa_family == AF_INET6 && strchr(host, ':')) ? "[%s]:%s" : "%s:%s", host, service);
+ return buf;
+}
+
+static size_t pretty_time(char *buf, const size_t len, unsigned long long left)
+{
+ size_t offset = 0;
+ unsigned long long years, days, hours, minutes, seconds;
+
+ years = left / (365 * 24 * 60 * 60);
+ left = left % (365 * 24 * 60 * 60);
+ days = left / (24 * 60 * 60);
+ left = left % (24 * 60 * 60);
+ hours = left / (60 * 60);
+ left = left % (60 * 60);
+ minutes = left / 60;
+ seconds = left % 60;
+
+ if (years)
+ offset += snprintf(buf + offset, len - offset, "%s%llu " TERMINAL_FG_CYAN "year%s" TERMINAL_RESET, offset ? ", " : "", years, years == 1 ? "" : "s");
+ if (days)
+ offset += snprintf(buf + offset, len - offset, "%s%llu " TERMINAL_FG_CYAN "day%s" TERMINAL_RESET, offset ? ", " : "", days, days == 1 ? "" : "s");
+ if (hours)
+ offset += snprintf(buf + offset, len - offset, "%s%llu " TERMINAL_FG_CYAN "hour%s" TERMINAL_RESET, offset ? ", " : "", hours, hours == 1 ? "" : "s");
+ if (minutes)
+ offset += snprintf(buf + offset, len - offset, "%s%llu " TERMINAL_FG_CYAN "minute%s" TERMINAL_RESET, offset ? ", " : "", minutes, minutes == 1 ? "" : "s");
+ if (seconds)
+ offset += snprintf(buf + offset, len - offset, "%s%llu " TERMINAL_FG_CYAN "second%s" TERMINAL_RESET, offset ? ", " : "", seconds, seconds == 1 ? "" : "s");
+
+ return offset;
+}
+
+static char *ago(const struct timespec64 *t)
+{
+ static char buf[1024];
+ size_t offset;
+ time_t now = time(NULL);
+
+ if (now == t->tv_sec)
+ strncpy(buf, "Now", sizeof(buf) - 1);
+ else if (now < t->tv_sec)
+ strncpy(buf, "(" TERMINAL_FG_RED "System clock wound backward; connection problems may ensue." TERMINAL_RESET ")", sizeof(buf) - 1);
+ else {
+ offset = pretty_time(buf, sizeof(buf), now - t->tv_sec);
+ strncpy(buf + offset, " ago", sizeof(buf) - offset - 1);
+ }
+ buf[sizeof(buf) - 1] = '\0';
+
+ return buf;
+}
+
+static char *every(uint16_t seconds)
+{
+ static char buf[1024] = "every ";
+
+ pretty_time(buf + strlen("every "), sizeof(buf) - strlen("every ") - 1, seconds);
+ return buf;
+}
+
+static char *bytes(uint64_t b)
+{
+ static char buf[1024];
+
+ if (b < 1024ULL)
+ snprintf(buf, sizeof(buf), "%u " TERMINAL_FG_CYAN "B" TERMINAL_RESET, (unsigned int)b);
+ else if (b < 1024ULL * 1024ULL)
+ snprintf(buf, sizeof(buf), "%.2f " TERMINAL_FG_CYAN "KiB" TERMINAL_RESET, (double)b / 1024);
+ else if (b < 1024ULL * 1024ULL * 1024ULL)
+ snprintf(buf, sizeof(buf), "%.2f " TERMINAL_FG_CYAN "MiB" TERMINAL_RESET, (double)b / (1024 * 1024));
+ else if (b < 1024ULL * 1024ULL * 1024ULL * 1024ULL)
+ snprintf(buf, sizeof(buf), "%.2f " TERMINAL_FG_CYAN "GiB" TERMINAL_RESET, (double)b / (1024 * 1024 * 1024));
+ else
+ snprintf(buf, sizeof(buf), "%.2f " TERMINAL_FG_CYAN "TiB" TERMINAL_RESET, (double)b / (1024 * 1024 * 1024) / 1024);
+
+ return buf;
+}
+
+static const char *COMMAND_NAME;
+static void show_usage(void)
+{
+ fprintf(stderr, "Usage: %s %s { <interface> | all | interfaces } [public-key | private-key | listen-port | fwmark | peers | preshared-keys | endpoints | allowed-ips | latest-handshakes | transfer | persistent-keepalive | dump]\n", PROG_NAME, COMMAND_NAME);
+}
+
+static void pretty_print(struct wgdevice *device)
+{
+ struct wgpeer *peer;
+ struct wgallowedip *allowedip;
+
+ terminal_printf(TERMINAL_RESET);
+ terminal_printf(TERMINAL_FG_GREEN TERMINAL_BOLD "interface" TERMINAL_RESET ": " TERMINAL_FG_GREEN "%s" TERMINAL_RESET "\n", device->name);
+ if (device->flags & WGDEVICE_HAS_PUBLIC_KEY)
+ terminal_printf(" " TERMINAL_BOLD "public key" TERMINAL_RESET ": %s\n", key(device->public_key));
+ if (device->flags & WGDEVICE_HAS_PRIVATE_KEY)
+ terminal_printf(" " TERMINAL_BOLD "private key" TERMINAL_RESET ": %s\n", masked_key(device->private_key));
+ if (device->listen_port)
+ terminal_printf(" " TERMINAL_BOLD "listening port" TERMINAL_RESET ": %u\n", device->listen_port);
+ if (device->fwmark)
+ terminal_printf(" " TERMINAL_BOLD "fwmark" TERMINAL_RESET ": 0x%x\n", device->fwmark);
+ if (device->first_peer) {
+ sort_peers(device);
+ terminal_printf("\n");
+ }
+ for_each_wgpeer(device, peer) {
+ terminal_printf(TERMINAL_FG_YELLOW TERMINAL_BOLD "peer" TERMINAL_RESET ": " TERMINAL_FG_YELLOW "%s" TERMINAL_RESET "\n", key(peer->public_key));
+ if (peer->flags & WGPEER_HAS_PRESHARED_KEY)
+ terminal_printf(" " TERMINAL_BOLD "preshared key" TERMINAL_RESET ": %s\n", masked_key(peer->preshared_key));
+ if (peer->endpoint.addr.sa_family == AF_INET || peer->endpoint.addr.sa_family == AF_INET6)
+ terminal_printf(" " TERMINAL_BOLD "endpoint" TERMINAL_RESET ": %s\n", endpoint(&peer->endpoint.addr));
+ terminal_printf(" " TERMINAL_BOLD "allowed ips" TERMINAL_RESET ": ");
+ if (peer->first_allowedip) {
+ for_each_wgallowedip(peer, allowedip)
+ terminal_printf("%s" TERMINAL_FG_CYAN "/" TERMINAL_RESET "%u%s", ip(allowedip), allowedip->cidr, allowedip->next_allowedip ? ", " : "\n");
+ } else
+ terminal_printf("(none)\n");
+ if (peer->last_handshake_time.tv_sec)
+ terminal_printf(" " TERMINAL_BOLD "latest handshake" TERMINAL_RESET ": %s\n", ago(&peer->last_handshake_time));
+ if (peer->rx_bytes || peer->tx_bytes) {
+ terminal_printf(" " TERMINAL_BOLD "transfer" TERMINAL_RESET ": ");
+ terminal_printf("%s received, ", bytes(peer->rx_bytes));
+ terminal_printf("%s sent\n", bytes(peer->tx_bytes));
+ }
+ if (peer->persistent_keepalive_interval)
+ terminal_printf(" " TERMINAL_BOLD "persistent keepalive" TERMINAL_RESET ": %s\n", every(peer->persistent_keepalive_interval));
+ if (peer->next_peer)
+ terminal_printf("\n");
+ }
+}
+
+static void dump_print(struct wgdevice *device, bool with_interface)
+{
+ struct wgpeer *peer;
+ struct wgallowedip *allowedip;
+
+ if (with_interface)
+ printf("%s\t", device->name);
+ printf("%s\t", maybe_key(device->private_key, device->flags & WGDEVICE_HAS_PRIVATE_KEY));
+ printf("%s\t", maybe_key(device->public_key, device->flags & WGDEVICE_HAS_PUBLIC_KEY));
+ printf("%u\t", device->listen_port);
+ if (device->fwmark)
+ printf("0x%x\n", device->fwmark);
+ else
+ printf("off\n");
+ for_each_wgpeer(device, peer) {
+ if (with_interface)
+ printf("%s\t", device->name);
+ printf("%s\t", key(peer->public_key));
+ printf("%s\t", maybe_key(peer->preshared_key, peer->flags & WGPEER_HAS_PRESHARED_KEY));
+ if (peer->endpoint.addr.sa_family == AF_INET || peer->endpoint.addr.sa_family == AF_INET6)
+ printf("%s\t", endpoint(&peer->endpoint.addr));
+ else
+ printf("(none)\t");
+ if (peer->first_allowedip) {
+ for_each_wgallowedip(peer, allowedip)
+ printf("%s/%u%c", ip(allowedip), allowedip->cidr, allowedip->next_allowedip ? ',' : '\t');
+ } else
+ printf("(none)\t");
+ printf("%llu\t", (unsigned long long)peer->last_handshake_time.tv_sec);
+ printf("%" PRIu64 "\t%" PRIu64 "\t", (uint64_t)peer->rx_bytes, (uint64_t)peer->tx_bytes);
+ if (peer->persistent_keepalive_interval)
+ printf("%u\n", peer->persistent_keepalive_interval);
+ else
+ printf("off\n");
+ }
+}
+
+static bool ugly_print(struct wgdevice *device, const char *param, bool with_interface)
+{
+ struct wgpeer *peer;
+ struct wgallowedip *allowedip;
+
+ if (!strcmp(param, "public-key")) {
+ if (with_interface)
+ printf("%s\t", device->name);
+ printf("%s\n", maybe_key(device->public_key, device->flags & WGDEVICE_HAS_PUBLIC_KEY));
+ } else if (!strcmp(param, "private-key")) {
+ if (with_interface)
+ printf("%s\t", device->name);
+ printf("%s\n", maybe_key(device->private_key, device->flags & WGDEVICE_HAS_PRIVATE_KEY));
+ } else if (!strcmp(param, "listen-port")) {
+ if (with_interface)
+ printf("%s\t", device->name);
+ printf("%u\n", device->listen_port);
+ } else if (!strcmp(param, "fwmark")) {
+ if (with_interface)
+ printf("%s\t", device->name);
+ if (device->fwmark)
+ printf("0x%x\n", device->fwmark);
+ else
+ printf("off\n");
+ } else if (!strcmp(param, "endpoints")) {
+ if (with_interface)
+ printf("%s\t", device->name);
+ for_each_wgpeer(device, peer) {
+ printf("%s\t", key(peer->public_key));
+ if (peer->endpoint.addr.sa_family == AF_INET || peer->endpoint.addr.sa_family == AF_INET6)
+ printf("%s\n", endpoint(&peer->endpoint.addr));
+ else
+ printf("(none)\n");
+ }
+ } else if (!strcmp(param, "allowed-ips")) {
+ for_each_wgpeer(device, peer) {
+ if (with_interface)
+ printf("%s\t", device->name);
+ printf("%s\t", key(peer->public_key));
+ if (peer->first_allowedip) {
+ for_each_wgallowedip(peer, allowedip)
+ printf("%s/%u%c", ip(allowedip), allowedip->cidr, allowedip->next_allowedip ? ' ' : '\n');
+ } else
+ printf("(none)\n");
+ }
+ } else if (!strcmp(param, "latest-handshakes")) {
+ for_each_wgpeer(device, peer) {
+ if (with_interface)
+ printf("%s\t", device->name);
+ printf("%s\t%llu\n", key(peer->public_key), (unsigned long long)peer->last_handshake_time.tv_sec);
+ }
+ } else if (!strcmp(param, "transfer")) {
+ for_each_wgpeer(device, peer) {
+ if (with_interface)
+ printf("%s\t", device->name);
+ printf("%s\t%" PRIu64 "\t%" PRIu64 "\n", key(peer->public_key), (uint64_t)peer->rx_bytes, (uint64_t)peer->tx_bytes);
+ }
+ } else if (!strcmp(param, "persistent-keepalive")) {
+ for_each_wgpeer(device, peer) {
+ if (with_interface)
+ printf("%s\t", device->name);
+ if (peer->persistent_keepalive_interval)
+ printf("%s\t%u\n", key(peer->public_key), peer->persistent_keepalive_interval);
+ else
+ printf("%s\toff\n", key(peer->public_key));
+ }
+ } else if (!strcmp(param, "preshared-keys")) {
+ for_each_wgpeer(device, peer) {
+ if (with_interface)
+ printf("%s\t", device->name);
+ printf("%s\t", key(peer->public_key));
+ printf("%s\n", maybe_key(peer->preshared_key, peer->flags & WGPEER_HAS_PRESHARED_KEY));
+ }
+ } else if (!strcmp(param, "peers")) {
+ for_each_wgpeer(device, peer) {
+ if (with_interface)
+ printf("%s\t", device->name);
+ printf("%s\n", key(peer->public_key));
+ }
+ } else if (!strcmp(param, "dump"))
+ dump_print(device, with_interface);
+ else {
+ fprintf(stderr, "Invalid parameter: `%s'\n", param);
+ show_usage();
+ return false;
+ }
+ return true;
+}
+
+int show_main(int argc, const char *argv[])
+{
+ int ret = 0;
+
+ COMMAND_NAME = argv[0];
+
+ if (argc > 3) {
+ show_usage();
+ return 1;
+ }
+
+ if (argc == 1 || !strcmp(argv[1], "all")) {
+ char *interfaces = ipc_list_devices(), *interface;
+
+ if (!interfaces) {
+ perror("Unable to list interfaces");
+ return 1;
+ }
+ ret = !!*interfaces;
+ interface = interfaces;
+ for (size_t len = 0; (len = strlen(interface)); interface += len + 1) {
+ struct wgdevice *device = NULL;
+
+ if (ipc_get_device(&device, interface) < 0) {
+ fprintf(stderr, "Unable to access interface %s: %s\n", interface, strerror(errno));
+ continue;
+ }
+ if (argc == 3) {
+ if (!ugly_print(device, argv[2], true)) {
+ ret = 1;
+ free_wgdevice(device);
+ break;
+ }
+ } else {
+ pretty_print(device);
+ if (strlen(interface + len + 1))
+ printf("\n");
+ }
+ free_wgdevice(device);
+ ret = 0;
+ }
+ free(interfaces);
+ } else if (!strcmp(argv[1], "interfaces")) {
+ char *interfaces, *interface;
+
+ if (argc > 2) {
+ show_usage();
+ return 1;
+ }
+ interfaces = ipc_list_devices();
+ if (!interfaces) {
+ perror("Unable to list interfaces");
+ return 1;
+ }
+ interface = interfaces;
+ for (size_t len = 0; (len = strlen(interface)); interface += len + 1)
+ printf("%s%c", interface, strlen(interface + len + 1) ? ' ' : '\n');
+ free(interfaces);
+ } else if (argc == 2 && (!strcmp(argv[1], "-h") || !strcmp(argv[1], "--help") || !strcmp(argv[1], "help")))
+ show_usage();
+ else {
+ struct wgdevice *device = NULL;
+
+ if (ipc_get_device(&device, argv[1]) < 0) {
+ perror("Unable to access interface");
+ return 1;
+ }
+ if (argc == 3) {
+ if (!ugly_print(device, argv[2], false))
+ ret = 1;
+ } else
+ pretty_print(device);
+ free_wgdevice(device);
+ }
+ return ret;
+}
diff --git a/contrib/wireguard-tools/src/showconf.c b/contrib/wireguard-tools/src/showconf.c
new file mode 100644
--- /dev/null
+++ b/contrib/wireguard-tools/src/showconf.c
@@ -0,0 +1,103 @@
+// SPDX-License-Identifier: GPL-2.0 OR MIT
+/*
+ * Copyright (C) 2015-2020 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
+ */
+
+#include <arpa/inet.h>
+#include <netinet/in.h>
+#include <sys/socket.h>
+#include <net/if.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <netdb.h>
+
+#include "containers.h"
+#include "encoding.h"
+#include "ipc.h"
+#include "subcommands.h"
+
+int showconf_main(int argc, const char *argv[])
+{
+ char base64[WG_KEY_LEN_BASE64];
+ char ip[INET6_ADDRSTRLEN];
+ struct wgdevice *device = NULL;
+ struct wgpeer *peer;
+ struct wgallowedip *allowedip;
+ int ret = 1;
+
+ if (argc != 2) {
+ fprintf(stderr, "Usage: %s %s <interface>\n", PROG_NAME, argv[0]);
+ return 1;
+ }
+
+ if (ipc_get_device(&device, argv[1])) {
+ perror("Unable to access interface");
+ goto cleanup;
+ }
+
+ printf("[Interface]\n");
+ if (device->listen_port)
+ printf("ListenPort = %u\n", device->listen_port);
+ if (device->fwmark)
+ printf("FwMark = 0x%x\n", device->fwmark);
+ if (device->flags & WGDEVICE_HAS_PRIVATE_KEY) {
+ key_to_base64(base64, device->private_key);
+ printf("PrivateKey = %s\n", base64);
+ }
+ printf("\n");
+ for_each_wgpeer(device, peer) {
+ key_to_base64(base64, peer->public_key);
+ printf("[Peer]\nPublicKey = %s\n", base64);
+ if (peer->flags & WGPEER_HAS_PRESHARED_KEY) {
+ key_to_base64(base64, peer->preshared_key);
+ printf("PresharedKey = %s\n", base64);
+ }
+ if (peer->first_allowedip)
+ printf("AllowedIPs = ");
+ for_each_wgallowedip(peer, allowedip) {
+ if (allowedip->family == AF_INET) {
+ if (!inet_ntop(AF_INET, &allowedip->ip4, ip, INET6_ADDRSTRLEN))
+ continue;
+ } else if (allowedip->family == AF_INET6) {
+ if (!inet_ntop(AF_INET6, &allowedip->ip6, ip, INET6_ADDRSTRLEN))
+ continue;
+ } else
+ continue;
+ printf("%s/%d", ip, allowedip->cidr);
+ if (allowedip->next_allowedip)
+ printf(", ");
+ }
+ if (peer->first_allowedip)
+ printf("\n");
+
+ if (peer->endpoint.addr.sa_family == AF_INET || peer->endpoint.addr.sa_family == AF_INET6) {
+ char host[4096 + 1];
+ char service[512 + 1];
+ socklen_t addr_len = 0;
+
+ if (peer->endpoint.addr.sa_family == AF_INET)
+ addr_len = sizeof(struct sockaddr_in);
+ else if (peer->endpoint.addr.sa_family == AF_INET6)
+ addr_len = sizeof(struct sockaddr_in6);
+ if (!getnameinfo(&peer->endpoint.addr, addr_len, host, sizeof(host), service, sizeof(service), NI_DGRAM | NI_NUMERICSERV | NI_NUMERICHOST)) {
+ if (peer->endpoint.addr.sa_family == AF_INET6 && strchr(host, ':'))
+ printf("Endpoint = [%s]:%s\n", host, service);
+ else
+ printf("Endpoint = %s:%s\n", host, service);
+ }
+ }
+
+ if (peer->persistent_keepalive_interval)
+ printf("PersistentKeepalive = %u\n", peer->persistent_keepalive_interval);
+
+ if (peer->next_peer)
+ printf("\n");
+ }
+ ret = 0;
+
+cleanup:
+ free_wgdevice(device);
+ return ret;
+}
diff --git a/contrib/wireguard-tools/src/subcommands.h b/contrib/wireguard-tools/src/subcommands.h
new file mode 100644
--- /dev/null
+++ b/contrib/wireguard-tools/src/subcommands.h
@@ -0,0 +1,17 @@
+/* SPDX-License-Identifier: GPL-2.0 OR MIT */
+/*
+ * Copyright (C) 2015-2020 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
+ */
+
+#ifndef SUBCOMMANDS_H
+#define SUBCOMMANDS_H
+
+extern const char *PROG_NAME;
+int show_main(int argc, const char *argv[]);
+int showconf_main(int argc, const char *argv[]);
+int set_main(int argc, const char *argv[]);
+int setconf_main(int argc, const char *argv[]);
+int genkey_main(int argc, const char *argv[]);
+int pubkey_main(int argc, const char *argv[]);
+
+#endif
diff --git a/contrib/wireguard-tools/src/systemd/wg-quick.target b/contrib/wireguard-tools/src/systemd/wg-quick.target
new file mode 100644
--- /dev/null
+++ b/contrib/wireguard-tools/src/systemd/wg-quick.target
@@ -0,0 +1,2 @@
+[Unit]
+Description=WireGuard Tunnels via wg-quick(8)
diff --git a/contrib/wireguard-tools/src/systemd/wg-quick@.service b/contrib/wireguard-tools/src/systemd/wg-quick@.service
new file mode 100644
--- /dev/null
+++ b/contrib/wireguard-tools/src/systemd/wg-quick@.service
@@ -0,0 +1,22 @@
+[Unit]
+Description=WireGuard via wg-quick(8) for %I
+After=network-online.target nss-lookup.target
+Wants=network-online.target nss-lookup.target
+PartOf=wg-quick.target
+Documentation=man:wg-quick(8)
+Documentation=man:wg(8)
+Documentation=https://www.wireguard.com/
+Documentation=https://www.wireguard.com/quickstart/
+Documentation=https://git.zx2c4.com/wireguard-tools/about/src/man/wg-quick.8
+Documentation=https://git.zx2c4.com/wireguard-tools/about/src/man/wg.8
+
+[Service]
+Type=oneshot
+RemainAfterExit=yes
+ExecStart=/usr/bin/wg-quick up %i
+ExecStop=/usr/bin/wg-quick down %i
+ExecReload=/bin/bash -c 'exec /usr/bin/wg syncconf %i <(exec /usr/bin/wg-quick strip %i)'
+Environment=WG_ENDPOINT_RESOLUTION_RETRIES=infinity
+
+[Install]
+WantedBy=multi-user.target
diff --git a/contrib/wireguard-tools/src/terminal.h b/contrib/wireguard-tools/src/terminal.h
new file mode 100644
--- /dev/null
+++ b/contrib/wireguard-tools/src/terminal.h
@@ -0,0 +1,51 @@
+/* SPDX-License-Identifier: GPL-2.0 OR MIT */
+/*
+ * Copyright (C) 2015-2020 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
+ */
+
+#ifndef TERMINAL_H
+#define TERMINAL_H
+
+#define TERMINAL_FG_BLACK "\x1b[30m"
+#define TERMINAL_FG_RED "\x1b[31m"
+#define TERMINAL_FG_GREEN "\x1b[32m"
+#define TERMINAL_FG_YELLOW "\x1b[33m"
+#define TERMINAL_FG_BLUE "\x1b[34m"
+#define TERMINAL_FG_MAGENTA "\x1b[35m"
+#define TERMINAL_FG_CYAN "\x1b[36m"
+#define TERMINAL_FG_WHITE "\x1b[37m"
+#define TERMINAL_FG_DEFAULT "\x1b[39m"
+
+#define TERMINAL_BG_BLACK "\x1b[40m"
+#define TERMINAL_BG_RED "\x1b[41m"
+#define TERMINAL_BG_GREEN "\x1b[42m"
+#define TERMINAL_BG_YELLOW "\x1b[43m"
+#define TERMINAL_BG_BLUE "\x1b[44m"
+#define TERMINAL_BG_MAGENTA "\x1b[45m"
+#define TERMINAL_BG_CYAN "\x1b[46m"
+#define TERMINAL_BG_WHITE "\x1b[47m"
+#define TERMINAL_BG_DEFAULT "\x1b[49m"
+
+#define TERMINAL_BOLD "\x1b[1m"
+#define TERMINAL_NO_BOLD "\x1b[22m"
+#define TERMINAL_UNDERLINE "\x1b[4m"
+#define TERMINAL_NO_UNDERLINE "\x1b[24m"
+
+#define TERMINAL_RESET "\x1b[0m"
+
+#define TERMINAL_SAVE_CURSOR "\x1b[s"
+#define TERMINAL_RESTORE_CURSOR "\x1b[u"
+#define TERMINAL_UP_CURSOR(l) "\x1b[" #l "A"
+#define TERMINAL_DOWN_CURSOR(l) "\x1b[" #l "B"
+#define TERMINAL_RIGHT_CURSOR(c) "\x1b[" #c "C"
+#define TERMINAL_LEFT_CURSOR(c) "\x1b[" #c "D"
+#define TERMINAL_CLEAR_DOWN "\x1b[0J"
+#define TERMINAL_CLEAR_UP "\x1b[1J"
+#define TERMINAL_CLEAR_RIGHT "\x1b[0K"
+#define TERMINAL_CLEAR_LEFT "\x1b[1K"
+#define TERMINAL_CLEAR_LINE "\x1b[2K"
+#define TERMINAL_CLEAR_ALL "\x1b[2J"
+
+void terminal_printf(const char *fmt, ...) __attribute__((format(printf, 1, 2)));
+
+#endif
diff --git a/contrib/wireguard-tools/src/terminal.c b/contrib/wireguard-tools/src/terminal.c
new file mode 100644
--- /dev/null
+++ b/contrib/wireguard-tools/src/terminal.c
@@ -0,0 +1,76 @@
+// SPDX-License-Identifier: GPL-2.0 OR MIT
+/*
+ * Copyright (C) 2015-2020 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
+ */
+
+#include <stdarg.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdbool.h>
+#include <unistd.h>
+#include "ctype.h"
+#include "terminal.h"
+
+static bool color_mode(void)
+{
+ static int mode = -1;
+ const char *var;
+
+ if (mode != -1)
+ return mode;
+ var = getenv("WG_COLOR_MODE");
+ if (var && !strcmp(var, "always"))
+ mode = true;
+ else if (var && !strcmp(var, "never"))
+ mode = false;
+ else
+ mode = isatty(fileno(stdout));
+ return mode;
+}
+
+static void filter_ansi(const char *fmt, va_list args)
+{
+ char *str = NULL;
+ size_t len, i, j;
+
+ if (color_mode()) {
+ vfprintf(stdout, fmt, args);
+ return;
+ }
+
+ len = vasprintf(&str, fmt, args);
+
+ if (len >= 2) {
+ for (i = 0; i < len - 2; ++i) {
+ if (str[i] == '\x1b' && str[i + 1] == '[') {
+ str[i] = str[i + 1] = '\0';
+ for (j = i + 2; j < len; ++j) {
+ if (char_is_alpha(str[j]))
+ break;
+ str[j] = '\0';
+ }
+ str[j] = '\0';
+ }
+ }
+ }
+ for (i = 0; i < len; i = j) {
+ fputs(&str[i], stdout);
+ for (j = i + strlen(&str[i]); j < len; ++j) {
+ if (str[j] != '\0')
+ break;
+ }
+ }
+
+ free(str);
+}
+
+void terminal_printf(const char *fmt, ...)
+{
+ va_list args;
+
+ va_start(args, fmt);
+ filter_ansi(fmt, args);
+ va_end(args);
+}
diff --git a/contrib/wireguard-tools/src/uapi/freebsd/dev/if_wg/if_wg.h b/contrib/wireguard-tools/src/uapi/freebsd/dev/if_wg/if_wg.h
new file mode 100644
--- /dev/null
+++ b/contrib/wireguard-tools/src/uapi/freebsd/dev/if_wg/if_wg.h
@@ -0,0 +1,16 @@
+#ifndef __IF_WG_H__
+#define __IF_WG_H__
+
+#include <net/if.h>
+#include <netinet/in.h>
+
+struct wg_data_io {
+ char wgd_name[IFNAMSIZ];
+ void *wgd_data;
+ size_t wgd_size;
+};
+
+#define SIOCSWG _IOWR('i', 210, struct wg_data_io)
+#define SIOCGWG _IOWR('i', 211, struct wg_data_io)
+
+#endif
diff --git a/contrib/wireguard-tools/src/uapi/linux/linux/wireguard.h b/contrib/wireguard-tools/src/uapi/linux/linux/wireguard.h
new file mode 100644
--- /dev/null
+++ b/contrib/wireguard-tools/src/uapi/linux/linux/wireguard.h
@@ -0,0 +1,196 @@
+/* SPDX-License-Identifier: (GPL-2.0 WITH Linux-syscall-note) OR MIT */
+/*
+ * Copyright (C) 2015-2020 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
+ *
+ * Documentation
+ * =============
+ *
+ * The below enums and macros are for interfacing with WireGuard, using generic
+ * netlink, with family WG_GENL_NAME and version WG_GENL_VERSION. It defines two
+ * methods: get and set. Note that while they share many common attributes,
+ * these two functions actually accept a slightly different set of inputs and
+ * outputs.
+ *
+ * WG_CMD_GET_DEVICE
+ * -----------------
+ *
+ * May only be called via NLM_F_REQUEST | NLM_F_DUMP. The command should contain
+ * one but not both of:
+ *
+ * WGDEVICE_A_IFINDEX: NLA_U32
+ * WGDEVICE_A_IFNAME: NLA_NUL_STRING, maxlen IFNAMSIZ - 1
+ *
+ * The kernel will then return several messages (NLM_F_MULTI) containing the
+ * following tree of nested items:
+ *
+ * WGDEVICE_A_IFINDEX: NLA_U32
+ * WGDEVICE_A_IFNAME: NLA_NUL_STRING, maxlen IFNAMSIZ - 1
+ * WGDEVICE_A_PRIVATE_KEY: NLA_EXACT_LEN, len WG_KEY_LEN
+ * WGDEVICE_A_PUBLIC_KEY: NLA_EXACT_LEN, len WG_KEY_LEN
+ * WGDEVICE_A_LISTEN_PORT: NLA_U16
+ * WGDEVICE_A_FWMARK: NLA_U32
+ * WGDEVICE_A_PEERS: NLA_NESTED
+ * 0: NLA_NESTED
+ * WGPEER_A_PUBLIC_KEY: NLA_EXACT_LEN, len WG_KEY_LEN
+ * WGPEER_A_PRESHARED_KEY: NLA_EXACT_LEN, len WG_KEY_LEN
+ * WGPEER_A_ENDPOINT: NLA_MIN_LEN(struct sockaddr), struct sockaddr_in or struct sockaddr_in6
+ * WGPEER_A_PERSISTENT_KEEPALIVE_INTERVAL: NLA_U16
+ * WGPEER_A_LAST_HANDSHAKE_TIME: NLA_EXACT_LEN, struct __kernel_timespec
+ * WGPEER_A_RX_BYTES: NLA_U64
+ * WGPEER_A_TX_BYTES: NLA_U64
+ * WGPEER_A_ALLOWEDIPS: NLA_NESTED
+ * 0: NLA_NESTED
+ * WGALLOWEDIP_A_FAMILY: NLA_U16
+ * WGALLOWEDIP_A_IPADDR: NLA_MIN_LEN(struct in_addr), struct in_addr or struct in6_addr
+ * WGALLOWEDIP_A_CIDR_MASK: NLA_U8
+ * 0: NLA_NESTED
+ * ...
+ * 0: NLA_NESTED
+ * ...
+ * ...
+ * WGPEER_A_PROTOCOL_VERSION: NLA_U32
+ * 0: NLA_NESTED
+ * ...
+ * ...
+ *
+ * It is possible that all of the allowed IPs of a single peer will not
+ * fit within a single netlink message. In that case, the same peer will
+ * be written in the following message, except it will only contain
+ * WGPEER_A_PUBLIC_KEY and WGPEER_A_ALLOWEDIPS. This may occur several
+ * times in a row for the same peer. It is then up to the receiver to
+ * coalesce adjacent peers. Likewise, it is possible that all peers will
+ * not fit within a single message. So, subsequent peers will be sent
+ * in following messages, except those will only contain WGDEVICE_A_IFNAME
+ * and WGDEVICE_A_PEERS. It is then up to the receiver to coalesce these
+ * messages to form the complete list of peers.
+ *
+ * Since this is an NLA_F_DUMP command, the final message will always be
+ * NLMSG_DONE, even if an error occurs. However, this NLMSG_DONE message
+ * contains an integer error code. It is either zero or a negative error
+ * code corresponding to the errno.
+ *
+ * WG_CMD_SET_DEVICE
+ * -----------------
+ *
+ * May only be called via NLM_F_REQUEST. The command should contain the
+ * following tree of nested items, containing one but not both of
+ * WGDEVICE_A_IFINDEX and WGDEVICE_A_IFNAME:
+ *
+ * WGDEVICE_A_IFINDEX: NLA_U32
+ * WGDEVICE_A_IFNAME: NLA_NUL_STRING, maxlen IFNAMSIZ - 1
+ * WGDEVICE_A_FLAGS: NLA_U32, 0 or WGDEVICE_F_REPLACE_PEERS if all current
+ * peers should be removed prior to adding the list below.
+ * WGDEVICE_A_PRIVATE_KEY: len WG_KEY_LEN, all zeros to remove
+ * WGDEVICE_A_LISTEN_PORT: NLA_U16, 0 to choose randomly
+ * WGDEVICE_A_FWMARK: NLA_U32, 0 to disable
+ * WGDEVICE_A_PEERS: NLA_NESTED
+ * 0: NLA_NESTED
+ * WGPEER_A_PUBLIC_KEY: len WG_KEY_LEN
+ * WGPEER_A_FLAGS: NLA_U32, 0 and/or WGPEER_F_REMOVE_ME if the
+ * specified peer should not exist at the end of the
+ * operation, rather than added/updated and/or
+ * WGPEER_F_REPLACE_ALLOWEDIPS if all current allowed
+ * IPs of this peer should be removed prior to adding
+ * the list below and/or WGPEER_F_UPDATE_ONLY if the
+ * peer should only be set if it already exists.
+ * WGPEER_A_PRESHARED_KEY: len WG_KEY_LEN, all zeros to remove
+ * WGPEER_A_ENDPOINT: struct sockaddr_in or struct sockaddr_in6
+ * WGPEER_A_PERSISTENT_KEEPALIVE_INTERVAL: NLA_U16, 0 to disable
+ * WGPEER_A_ALLOWEDIPS: NLA_NESTED
+ * 0: NLA_NESTED
+ * WGALLOWEDIP_A_FAMILY: NLA_U16
+ * WGALLOWEDIP_A_IPADDR: struct in_addr or struct in6_addr
+ * WGALLOWEDIP_A_CIDR_MASK: NLA_U8
+ * 0: NLA_NESTED
+ * ...
+ * 0: NLA_NESTED
+ * ...
+ * ...
+ * WGPEER_A_PROTOCOL_VERSION: NLA_U32, should not be set or used at
+ * all by most users of this API, as the
+ * most recent protocol will be used when
+ * this is unset. Otherwise, must be set
+ * to 1.
+ * 0: NLA_NESTED
+ * ...
+ * ...
+ *
+ * It is possible that the amount of configuration data exceeds that of
+ * the maximum message length accepted by the kernel. In that case, several
+ * messages should be sent one after another, with each successive one
+ * filling in information not contained in the prior. Note that if
+ * WGDEVICE_F_REPLACE_PEERS is specified in the first message, it probably
+ * should not be specified in fragments that come after, so that the list
+ * of peers is only cleared the first time but appended after. Likewise for
+ * peers, if WGPEER_F_REPLACE_ALLOWEDIPS is specified in the first message
+ * of a peer, it likely should not be specified in subsequent fragments.
+ *
+ * If an error occurs, NLMSG_ERROR will reply containing an errno.
+ */
+
+#ifndef _WG_UAPI_WIREGUARD_H
+#define _WG_UAPI_WIREGUARD_H
+
+#define WG_GENL_NAME "wireguard"
+#define WG_GENL_VERSION 1
+
+#define WG_KEY_LEN 32
+
+enum wg_cmd {
+ WG_CMD_GET_DEVICE,
+ WG_CMD_SET_DEVICE,
+ __WG_CMD_MAX
+};
+#define WG_CMD_MAX (__WG_CMD_MAX - 1)
+
+enum wgdevice_flag {
+ WGDEVICE_F_REPLACE_PEERS = 1U << 0,
+ __WGDEVICE_F_ALL = WGDEVICE_F_REPLACE_PEERS
+};
+enum wgdevice_attribute {
+ WGDEVICE_A_UNSPEC,
+ WGDEVICE_A_IFINDEX,
+ WGDEVICE_A_IFNAME,
+ WGDEVICE_A_PRIVATE_KEY,
+ WGDEVICE_A_PUBLIC_KEY,
+ WGDEVICE_A_FLAGS,
+ WGDEVICE_A_LISTEN_PORT,
+ WGDEVICE_A_FWMARK,
+ WGDEVICE_A_PEERS,
+ __WGDEVICE_A_LAST
+};
+#define WGDEVICE_A_MAX (__WGDEVICE_A_LAST - 1)
+
+enum wgpeer_flag {
+ WGPEER_F_REMOVE_ME = 1U << 0,
+ WGPEER_F_REPLACE_ALLOWEDIPS = 1U << 1,
+ WGPEER_F_UPDATE_ONLY = 1U << 2,
+ __WGPEER_F_ALL = WGPEER_F_REMOVE_ME | WGPEER_F_REPLACE_ALLOWEDIPS |
+ WGPEER_F_UPDATE_ONLY
+};
+enum wgpeer_attribute {
+ WGPEER_A_UNSPEC,
+ WGPEER_A_PUBLIC_KEY,
+ WGPEER_A_PRESHARED_KEY,
+ WGPEER_A_FLAGS,
+ WGPEER_A_ENDPOINT,
+ WGPEER_A_PERSISTENT_KEEPALIVE_INTERVAL,
+ WGPEER_A_LAST_HANDSHAKE_TIME,
+ WGPEER_A_RX_BYTES,
+ WGPEER_A_TX_BYTES,
+ WGPEER_A_ALLOWEDIPS,
+ WGPEER_A_PROTOCOL_VERSION,
+ __WGPEER_A_LAST
+};
+#define WGPEER_A_MAX (__WGPEER_A_LAST - 1)
+
+enum wgallowedip_attribute {
+ WGALLOWEDIP_A_UNSPEC,
+ WGALLOWEDIP_A_FAMILY,
+ WGALLOWEDIP_A_IPADDR,
+ WGALLOWEDIP_A_CIDR_MASK,
+ __WGALLOWEDIP_A_LAST
+};
+#define WGALLOWEDIP_A_MAX (__WGALLOWEDIP_A_LAST - 1)
+
+#endif /* _WG_UAPI_WIREGUARD_H */
diff --git a/contrib/wireguard-tools/src/uapi/openbsd/net/if_wg.h b/contrib/wireguard-tools/src/uapi/openbsd/net/if_wg.h
new file mode 100644
--- /dev/null
+++ b/contrib/wireguard-tools/src/uapi/openbsd/net/if_wg.h
@@ -0,0 +1,92 @@
+/* SPDX-License-Identifier: ISC */
+/*
+ * Copyright (C) 2020 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
+ * Copyright (c) 2020 Matt Dunwoodie <ncon@noconroy.net>
+ */
+
+#ifndef __IF_WG_H__
+#define __IF_WG_H__
+
+#include <sys/limits.h>
+#include <sys/errno.h>
+
+#include <net/if.h>
+#include <netinet/in.h>
+
+
+/*
+ * This is the public interface to the WireGuard network interface.
+ *
+ * It is designed to be used by tools such as ifconfig(8) and wg(8).
+ */
+
+#define WG_KEY_LEN 32
+
+#define SIOCSWG _IOWR('i', 210, struct wg_data_io)
+#define SIOCGWG _IOWR('i', 211, struct wg_data_io)
+
+#define a_ipv4 a_addr.addr_ipv4
+#define a_ipv6 a_addr.addr_ipv6
+
+struct wg_aip_io {
+ sa_family_t a_af;
+ int a_cidr;
+ union wg_aip_addr {
+ struct in_addr addr_ipv4;
+ struct in6_addr addr_ipv6;
+ } a_addr;
+};
+
+#define WG_PEER_HAS_PUBLIC (1 << 0)
+#define WG_PEER_HAS_PSK (1 << 1)
+#define WG_PEER_HAS_PKA (1 << 2)
+#define WG_PEER_HAS_ENDPOINT (1 << 3)
+#define WG_PEER_REPLACE_AIPS (1 << 4)
+#define WG_PEER_REMOVE (1 << 5)
+#define WG_PEER_UPDATE (1 << 6)
+
+#define p_sa p_endpoint.sa_sa
+#define p_sin p_endpoint.sa_sin
+#define p_sin6 p_endpoint.sa_sin6
+
+struct wg_peer_io {
+ int p_flags;
+ int p_protocol_version;
+ uint8_t p_public[WG_KEY_LEN];
+ uint8_t p_psk[WG_KEY_LEN];
+ uint16_t p_pka;
+ union wg_peer_endpoint {
+ struct sockaddr sa_sa;
+ struct sockaddr_in sa_sin;
+ struct sockaddr_in6 sa_sin6;
+ } p_endpoint;
+ uint64_t p_txbytes;
+ uint64_t p_rxbytes;
+ struct timespec p_last_handshake; /* nanotime */
+ size_t p_aips_count;
+ struct wg_aip_io p_aips[];
+};
+
+#define WG_INTERFACE_HAS_PUBLIC (1 << 0)
+#define WG_INTERFACE_HAS_PRIVATE (1 << 1)
+#define WG_INTERFACE_HAS_PORT (1 << 2)
+#define WG_INTERFACE_HAS_RTABLE (1 << 3)
+#define WG_INTERFACE_REPLACE_PEERS (1 << 4)
+
+struct wg_interface_io {
+ uint8_t i_flags;
+ in_port_t i_port;
+ int i_rtable;
+ uint8_t i_public[WG_KEY_LEN];
+ uint8_t i_private[WG_KEY_LEN];
+ size_t i_peers_count;
+ struct wg_peer_io i_peers[];
+};
+
+struct wg_data_io {
+ char wgd_name[IFNAMSIZ];
+ size_t wgd_size; /* total size of the memory pointed to by wgd_interface */
+ struct wg_interface_io *wgd_interface;
+};
+
+#endif /* __IF_WG_H__ */
diff --git a/contrib/wireguard-tools/src/uapi/windows/wireguard.h b/contrib/wireguard-tools/src/uapi/windows/wireguard.h
new file mode 100644
--- /dev/null
+++ b/contrib/wireguard-tools/src/uapi/windows/wireguard.h
@@ -0,0 +1,80 @@
+/* SPDX-License-Identifier: GPL-2.0
+ *
+ * Copyright (C) 2021 WireGuard LLC. All Rights Reserved.
+ */
+
+#ifndef _WIREGUARD_NT_H
+#define _WIREGUARD_NT_H
+
+#include <ntdef.h>
+#include <ws2def.h>
+#include <ws2ipdef.h>
+#include <inaddr.h>
+#include <in6addr.h>
+
+#define WG_KEY_LEN 32
+
+typedef struct _WG_IOCTL_ALLOWED_IP
+{
+ union
+ {
+ IN_ADDR V4;
+ IN6_ADDR V6;
+ } Address;
+ ADDRESS_FAMILY AddressFamily;
+ UCHAR Cidr;
+} __attribute__((aligned(8))) WG_IOCTL_ALLOWED_IP;
+
+typedef enum
+{
+ WG_IOCTL_PEER_HAS_PUBLIC_KEY = 1 << 0,
+ WG_IOCTL_PEER_HAS_PRESHARED_KEY = 1 << 1,
+ WG_IOCTL_PEER_HAS_PERSISTENT_KEEPALIVE = 1 << 2,
+ WG_IOCTL_PEER_HAS_ENDPOINT = 1 << 3,
+ WG_IOCTL_PEER_HAS_PROTOCOL_VERSION = 1 << 4,
+ WG_IOCTL_PEER_REPLACE_ALLOWED_IPS = 1 << 5,
+ WG_IOCTL_PEER_REMOVE = 1 << 6,
+ WG_IOCTL_PEER_UPDATE = 1 << 7
+} WG_IOCTL_PEER_FLAG;
+
+typedef struct _WG_IOCTL_PEER
+{
+ WG_IOCTL_PEER_FLAG Flags;
+ ULONG ProtocolVersion; /* 0 = latest protocol, 1 = this protocol. */
+ UCHAR PublicKey[WG_KEY_LEN];
+ UCHAR PresharedKey[WG_KEY_LEN];
+ USHORT PersistentKeepalive;
+ SOCKADDR_INET Endpoint;
+ ULONG64 TxBytes;
+ ULONG64 RxBytes;
+ ULONG64 LastHandshake;
+ ULONG AllowedIPsCount;
+} __attribute__((aligned(8))) WG_IOCTL_PEER;
+
+typedef enum
+{
+ WG_IOCTL_INTERFACE_HAS_PUBLIC_KEY = 1 << 0,
+ WG_IOCTL_INTERFACE_HAS_PRIVATE_KEY = 1 << 1,
+ WG_IOCTL_INTERFACE_HAS_LISTEN_PORT = 1 << 2,
+ WG_IOCTL_INTERFACE_REPLACE_PEERS = 1 << 3
+} WG_IOCTL_INTERFACE_FLAG;
+
+typedef struct _WG_IOCTL_INTERFACE
+{
+ WG_IOCTL_INTERFACE_FLAG Flags;
+ USHORT ListenPort;
+ UCHAR PrivateKey[WG_KEY_LEN];
+ UCHAR PublicKey[WG_KEY_LEN];
+ ULONG PeersCount;
+} __attribute__((aligned(8))) WG_IOCTL_INTERFACE;
+
+#define WG_IOCTL_GET CTL_CODE(45208U, 321, METHOD_OUT_DIRECT, FILE_READ_DATA | FILE_WRITE_DATA)
+#define WG_IOCTL_SET CTL_CODE(45208U, 322, METHOD_IN_DIRECT, FILE_READ_DATA | FILE_WRITE_DATA)
+
+#define DEVPKEY_WG_NAME (DEVPROPKEY) { \
+ { 0x65726957, 0x7547, 0x7261, { 0x64, 0x4e, 0x61, 0x6d, 0x65, 0x4b, 0x65, 0x79 } }, \
+ DEVPROPID_FIRST_USABLE + 1 \
+ }
+
+
+#endif
diff --git a/contrib/wireguard-tools/src/version.h b/contrib/wireguard-tools/src/version.h
new file mode 100644
--- /dev/null
+++ b/contrib/wireguard-tools/src/version.h
@@ -0,0 +1,3 @@
+#ifndef WIREGUARD_TOOLS_VERSION
+#define WIREGUARD_TOOLS_VERSION "1.0.20210914"
+#endif
diff --git a/contrib/wireguard-tools/src/wg-quick/android.c b/contrib/wireguard-tools/src/wg-quick/android.c
new file mode 100644
--- /dev/null
+++ b/contrib/wireguard-tools/src/wg-quick/android.c
@@ -0,0 +1,1303 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2015-2021 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
+ *
+ * This is a shell script written in C. It very intentionally still functions like
+ * a shell script, calling out to external executables such as ip(8).
+ */
+
+#define _GNU_SOURCE
+#include <stddef.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdbool.h>
+#include <string.h>
+#include <strings.h>
+#include <stdarg.h>
+#include <stdint.h>
+#include <ctype.h>
+#include <time.h>
+#include <unistd.h>
+#include <errno.h>
+#include <regex.h>
+#include <dlfcn.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <sys/param.h>
+#include <sys/system_properties.h>
+
+#ifndef WG_PACKAGE_NAME
+#define WG_PACKAGE_NAME "com.wireguard.android"
+#endif
+#ifndef WG_CONFIG_SEARCH_PATHS
+#define WG_CONFIG_SEARCH_PATHS "/data/misc/wireguard /data/data/" WG_PACKAGE_NAME "/files"
+#endif
+
+#define _printf_(x, y) __attribute__((format(printf, x, y)))
+#define _cleanup_(x) __attribute__((cleanup(x)))
+#define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0]))
+
+static bool is_exiting = false;
+static bool binder_available = false;
+static unsigned int sdk_version;
+
+static void *xmalloc(size_t size)
+{
+ void *ret = malloc(size);
+ if (ret)
+ return ret;
+ perror("Error: malloc");
+ exit(errno);
+}
+
+static void *xcalloc(size_t nmemb, size_t size)
+{
+ void *ret = calloc(nmemb, size);
+ if (ret)
+ return ret;
+ perror("Error: calloc");
+ exit(errno);
+}
+
+static void *xstrdup(const char *str)
+{
+ char *ret = strdup(str);
+ if (ret)
+ return ret;
+ perror("Error: strdup");
+ exit(errno);
+}
+
+static void xregcomp(regex_t *preg, const char *regex, int cflags)
+{
+ if (regcomp(preg, regex, cflags)) {
+ fprintf(stderr, "Error: Regex compilation error\n");
+ exit(EBADR);
+ }
+}
+
+static char *concat(char *first, ...)
+{
+ va_list args;
+ size_t len = 0;
+ char *ret;
+
+ va_start(args, first);
+ for (char *i = first; i; i = va_arg(args, char *))
+ len += strlen(i);
+ va_end(args);
+
+ ret = xmalloc(len + 1);
+ ret[0] = '\0';
+
+ va_start(args, first);
+ for (char *i = first; i; i = va_arg(args, char *))
+ strcat(ret, i);
+ va_end(args);
+
+ return ret;
+}
+
+static char *concat_and_free(char *orig, const char *delim, const char *new_line)
+{
+ char *ret;
+
+ if (!orig)
+ ret = xstrdup(new_line);
+ else
+ ret = concat(orig, delim, new_line, NULL);
+ free(orig);
+ return ret;
+}
+
+struct command_buffer {
+ char *line;
+ size_t len;
+ FILE *stream;
+};
+
+static void free_command_buffer(struct command_buffer *c)
+{
+ if (!c)
+ return;
+ if (c->stream)
+ pclose(c->stream);
+ free(c->line);
+}
+
+static void freep(void *p)
+{
+ free(*(void **)p);
+}
+static void fclosep(FILE **f)
+{
+ if (*f)
+ fclose(*f);
+}
+#define _cleanup_free_ _cleanup_(freep)
+#define _cleanup_fclose_ _cleanup_(fclosep)
+#define _cleanup_regfree_ _cleanup_(regfree)
+
+#define DEFINE_CMD(name) _cleanup_(free_command_buffer) struct command_buffer name = { 0 };
+
+static char *vcmd_ret(struct command_buffer *c, const char *cmd_fmt, va_list args)
+{
+ _cleanup_free_ char *cmd = NULL;
+
+ if (!c->stream && !cmd_fmt)
+ return NULL;
+ if (c->stream && cmd_fmt)
+ pclose(c->stream);
+
+ if (cmd_fmt) {
+ if (vasprintf(&cmd, cmd_fmt, args) < 0) {
+ perror("Error: vasprintf");
+ exit(errno);
+ }
+
+ c->stream = popen(cmd, "r");
+ if (!c->stream) {
+ perror("Error: popen");
+ exit(errno);
+ }
+ }
+ errno = 0;
+ if (getline(&c->line, &c->len, c->stream) < 0) {
+ if (errno) {
+ perror("Error: getline");
+ exit(errno);
+ }
+ return NULL;
+ }
+ return c->line;
+}
+
+_printf_(1, 2) static void cmd(const char *cmd_fmt, ...)
+{
+ _cleanup_free_ char *cmd = NULL;
+ va_list args;
+ int ret;
+
+ va_start(args, cmd_fmt);
+ if (vasprintf(&cmd, cmd_fmt, args) < 0) {
+ perror("Error: vasprintf");
+ exit(errno);
+ }
+ va_end(args);
+
+ printf("[#] %s\n", cmd);
+ ret = system(cmd);
+
+ if (ret < 0)
+ ret = ESRCH;
+ else if (ret > 0)
+ ret = WIFEXITED(ret) ? WEXITSTATUS(ret) : EIO;
+
+ if (ret && !is_exiting)
+ exit(ret);
+}
+
+_printf_(2, 3) static char *cmd_ret(struct command_buffer *c, const char *cmd_fmt, ...)
+{
+ va_list args;
+ char *ret;
+
+ va_start(args, cmd_fmt);
+ ret = vcmd_ret(c, cmd_fmt, args);
+ va_end(args);
+ return ret;
+}
+
+_printf_(1, 2) static void cndc(const char *cmd_fmt, ...)
+{
+ DEFINE_CMD(c);
+ int error_code;
+ char *ret;
+ va_list args;
+ _cleanup_free_ char *ndc_fmt = concat("ndc ", cmd_fmt, NULL);
+
+ va_start(args, cmd_fmt);
+ printf("[#] ");
+ vprintf(ndc_fmt, args);
+ printf("\n");
+ va_end(args);
+
+ va_start(args, cmd_fmt);
+ ret = vcmd_ret(&c, ndc_fmt, args);
+ va_end(args);
+
+ if (!ret) {
+ fprintf(stderr, "Error: could not call ndc\n");
+ exit(ENOSYS);
+ }
+
+ error_code = atoi(ret);
+ if (error_code >= 400 && error_code < 600) {
+ fprintf(stderr, "Error: %s\n", ret);
+ exit(ENONET);
+ }
+}
+
+/* Values are from AOSP repository platform/frameworks/native in libs/binder/ndk/include_ndk/android/binder_status.h. */
+enum {
+ STATUS_OK = 0,
+ STATUS_UNKNOWN_ERROR = -2147483647 - 1,
+ STATUS_NO_MEMORY = -ENOMEM,
+ STATUS_INVALID_OPERATION = -ENOSYS,
+ STATUS_BAD_VALUE = -EINVAL,
+ STATUS_BAD_TYPE = STATUS_UNKNOWN_ERROR + 1,
+ STATUS_NAME_NOT_FOUND = -ENOENT,
+ STATUS_PERMISSION_DENIED = -EPERM,
+ STATUS_NO_INIT = -ENODEV,
+ STATUS_ALREADY_EXISTS = -EEXIST,
+ STATUS_DEAD_OBJECT = -EPIPE,
+ STATUS_FAILED_TRANSACTION = STATUS_UNKNOWN_ERROR + 2,
+ STATUS_BAD_INDEX = -EOVERFLOW,
+ STATUS_NOT_ENOUGH_DATA = -ENODATA,
+ STATUS_WOULD_BLOCK = -EWOULDBLOCK,
+ STATUS_TIMED_OUT = -ETIMEDOUT,
+ STATUS_UNKNOWN_TRANSACTION = -EBADMSG,
+ STATUS_FDS_NOT_ALLOWED = STATUS_UNKNOWN_ERROR + 7,
+ STATUS_UNEXPECTED_NULL = STATUS_UNKNOWN_ERROR + 8
+};
+enum {
+ EX_NONE = 0,
+ EX_SECURITY = -1,
+ EX_BAD_PARCELABLE = -2,
+ EX_ILLEGAL_ARGUMENT = -3,
+ EX_NULL_POINTER = -4,
+ EX_ILLEGAL_STATE = -5,
+ EX_NETWORK_MAIN_THREAD = -6,
+ EX_UNSUPPORTED_OPERATION = -7,
+ EX_SERVICE_SPECIFIC = -8,
+ EX_PARCELABLE = -9,
+ EX_TRANSACTION_FAILED = -129
+};
+enum {
+ FLAG_ONEWAY = 0x01,
+};
+enum {
+ FIRST_CALL_TRANSACTION = 0x00000001,
+ LAST_CALL_TRANSACTION = 0x00ffffff
+};
+struct AIBinder;
+struct AParcel;
+struct AStatus;
+struct AIBinder_Class;
+typedef struct AIBinder AIBinder;
+typedef struct AParcel AParcel;
+typedef struct AStatus AStatus;
+typedef struct AIBinder_Class AIBinder_Class;
+typedef int32_t binder_status_t;
+typedef int32_t binder_exception_t;
+typedef uint32_t transaction_code_t;
+typedef uint32_t binder_flags_t;
+typedef void *(*AIBinder_Class_onCreate)(void *args);
+typedef void (*AIBinder_Class_onDestroy)(void *userData);
+typedef binder_status_t (*AIBinder_Class_onTransact)(AIBinder *binder, transaction_code_t code, const AParcel *in, AParcel *out);
+typedef const char *(*AParcel_stringArrayElementGetter)(const void *arrayData, size_t index, int32_t *outLength);
+static AIBinder_Class *(*AIBinder_Class_define)(const char *interfaceDescriptor, AIBinder_Class_onCreate onCreate, AIBinder_Class_onDestroy onDestroy, AIBinder_Class_onTransact onTransact) __attribute__((warn_unused_result));
+static bool (*AIBinder_associateClass)(AIBinder *binder, const AIBinder_Class *clazz);
+static void (*AIBinder_decStrong)(AIBinder *binder);
+static binder_status_t (*AIBinder_prepareTransaction)(AIBinder *binder, AParcel **in);
+static binder_status_t (*AIBinder_transact)(AIBinder *binder, transaction_code_t code, AParcel **in, AParcel **out, binder_flags_t flags);
+static binder_status_t (*AIBinder_ping)(AIBinder *binder);
+static binder_status_t (*AIBinder_dump)(AIBinder *binder, int fd, const char **args, uint32_t numArgs);
+static binder_status_t (*AParcel_readStatusHeader)(const AParcel *parcel, AStatus **status);
+static binder_status_t (*AParcel_readBool)(const AParcel *parcel, bool *value);
+static void (*AParcel_delete)(AParcel *parcel);
+static binder_status_t (*AParcel_setDataPosition)(const AParcel *parcel, int32_t position);
+static int32_t (*AParcel_getDataPosition)(const AParcel *parcel);
+static binder_status_t (*AParcel_writeInt32)(AParcel *parcel, int32_t value);
+static binder_status_t (*AParcel_writeStringArray)(AParcel *parcel, const void *arrayData, int32_t length, AParcel_stringArrayElementGetter getter);
+static binder_status_t (*AParcel_writeString)(AParcel *parcel, const char *string, int32_t length);
+static bool (*AStatus_isOk)(const AStatus *status);
+static void (*AStatus_delete)(AStatus *status);
+static binder_exception_t (*AStatus_getExceptionCode)(const AStatus *status);
+static int32_t (*AStatus_getServiceSpecificError)(const AStatus *status);
+static const char* (*AStatus_getMessage)(const AStatus *status);
+static binder_status_t (*AStatus_getStatus)(const AStatus *status);
+static AIBinder *(*AServiceManager_getService)(const char *instance) __attribute__((__warn_unused_result__));
+
+static __attribute__((__constructor__(65535))) void load_symbols(void)
+{
+ void *handle = dlopen("libbinder_ndk.so", RTLD_LAZY);
+ binder_available = !!handle;
+ if (!binder_available)
+ return;
+
+#define X(symb) do { \
+ if (!((symb) = (typeof(symb))dlsym(handle, #symb))) { \
+ fprintf(stderr, "Error: unable to import " #symb " from libbinder_ndk.so\n"); \
+ exit(ELIBACC); \
+ } \
+ } while (0)
+ X(AIBinder_Class_define);
+ X(AIBinder_associateClass);
+ X(AIBinder_decStrong);
+ X(AIBinder_prepareTransaction);
+ X(AIBinder_transact);
+ X(AIBinder_ping);
+ X(AIBinder_dump);
+ X(AParcel_readStatusHeader);
+ X(AParcel_readBool);
+ X(AParcel_delete);
+ X(AParcel_setDataPosition);
+ X(AParcel_getDataPosition);
+ X(AParcel_writeInt32);
+ X(AParcel_writeStringArray);
+ X(AParcel_writeString);
+ X(AStatus_isOk);
+ X(AStatus_delete);
+ X(AStatus_getExceptionCode);
+ X(AStatus_getServiceSpecificError);
+ X(AStatus_getMessage);
+ X(AStatus_getStatus);
+ X(AServiceManager_getService);
+#undef X
+}
+
+static void cleanup_binder(AIBinder **binder)
+{
+ if (*binder)
+ AIBinder_decStrong(*binder);
+}
+static void cleanup_status(AStatus **status)
+{
+ if (*status)
+ AStatus_delete(*status);
+}
+static void cleanup_parcel(AParcel **parcel)
+{
+ if (*parcel)
+ AParcel_delete(*parcel);
+}
+
+#define _cleanup_status_ __attribute__((__cleanup__(cleanup_status)))
+#define _cleanup_parcel_ __attribute__((__cleanup__(cleanup_parcel)))
+#define _cleanup_binder_ __attribute__((__cleanup__(cleanup_binder)))
+
+static int32_t string_size(const char *str)
+{
+ return str ? strlen(str) : -1;
+}
+
+static int32_t string_array_size(char *const *array)
+{
+ int32_t size = -1;
+ if (!array)
+ return size;
+ for (size = 0; array[size]; ++size);
+ return size;
+}
+
+static const char *string_array_getter(const void *array_data, size_t index, int32_t *out_length)
+{
+ const char **array = (const char **)array_data;
+ *out_length = array[index] ? strlen(array[index]) : -1;
+ return array[index];
+}
+
+static binder_status_t meaningful_binder_status(const AStatus *status_out)
+{
+ binder_status_t status = STATUS_OK;
+ binder_exception_t exc_code;
+ int32_t exc_code_service;
+ const char *message;
+
+ if (!AStatus_isOk(status_out)) {
+ exc_code = AStatus_getExceptionCode(status_out);
+ if (exc_code == EX_TRANSACTION_FAILED) {
+ status = AStatus_getStatus(status_out);
+ fprintf(stderr, "Error: transaction failed: %d\n", status);
+ }
+ else {
+ message = AStatus_getMessage(status_out);
+
+ if (exc_code == EX_SERVICE_SPECIFIC) {
+ exc_code_service = AStatus_getServiceSpecificError(status_out);
+ fprintf(stderr, "Error: service specific exception code: %d%s%s\n", exc_code_service, message ? ": " : "", message ?: "");
+ }
+ else
+ fprintf(stderr, "Error: exception code: %d%s%s\n", exc_code, message ? ": " : "", message ?: "");
+
+ status = STATUS_FAILED_TRANSACTION;
+ }
+ }
+
+ return status;
+}
+
+/* These values are default values observed in AOSP. */
+enum {
+ DNSRESOLVER_SAMPLE_VALIDITY = 1800 /* sec */,
+ DNSRESOLVER_SUCCESS_THRESHOLD = 25,
+ DNSRESOLVER_MIN_SAMPLES = 8,
+ DNSRESOLVER_MAX_SAMPLES = 8,
+ DNSRESOLVER_BASE_TIMEOUT = 5000 /* msec */,
+ DNSRESOLVER_RETRY_COUNT = 2
+};
+
+struct dnsresolver_params {
+ int32_t netid;
+ int32_t sample_validity_seconds;
+ int32_t success_threshold;
+ int32_t min_samples;
+ int32_t max_samples;
+ int32_t base_timeout_msec;
+ int32_t retry_count;
+ char **servers; /* NULL terminated array of zero-terminated UTF-8 strings */
+ char **domains; /* NULL terminated array of zero-terminated UTF-8 strings */
+ char *tls_name; /* zero-terminated UTF-8 string */
+ char **tls_servers; /* NULL terminated array of zero-terminated UTF-8 strings */
+ char **tls_fingerprints; /* NULL terminated array of zero-terminated UTF-8 strings */
+};
+
+static void *on_create()
+{
+ fprintf(stderr, "Error: on_create called on proxy object\n");
+ exit(ENOTSUP);
+ return NULL;
+}
+
+static void on_destroy()
+{
+ fprintf(stderr, "Error: on_destroy called on proxy object\n");
+ exit(ENOTSUP);
+}
+
+static binder_status_t on_transact()
+{
+ fprintf(stderr, "Error: on_transact called on a proxy object\n");
+ exit(ENOTSUP);
+ return 0;
+}
+
+static AIBinder *dnsresolver_get_handle(void)
+{
+ AIBinder *binder;
+ AIBinder_Class *clazz;
+
+ if (!binder_available)
+ return NULL;
+
+ binder = AServiceManager_getService("dnsresolver");
+ if (!binder)
+ return NULL;
+ clazz = AIBinder_Class_define("android.net.IDnsResolver", &on_create, &on_destroy, &on_transact);
+ if (!clazz)
+ goto error;
+
+ if (!AIBinder_associateClass(binder, clazz))
+ goto error;
+
+ return binder;
+error:
+ AIBinder_decStrong(binder);
+ return NULL;
+}
+
+static int32_t dnsresolver_create_network_cache(void *handle, int32_t netid)
+{
+ AIBinder *const binder = handle;
+ binder_status_t status;
+ _cleanup_parcel_ AParcel *parcel_in = NULL;
+ _cleanup_parcel_ AParcel *parcel_out = NULL;
+ _cleanup_status_ AStatus *status_out = NULL;
+
+ status = AIBinder_prepareTransaction(binder, &parcel_in);
+ if (status != STATUS_OK)
+ return status;
+
+ status = AParcel_writeInt32(parcel_in, netid);
+ if (status != STATUS_OK)
+ return status;
+
+ status = AIBinder_transact(binder, FIRST_CALL_TRANSACTION + 7 /* createNetworkCache */, &parcel_in, &parcel_out, 0);
+ if (status != STATUS_OK)
+ return status;
+
+ status = AParcel_readStatusHeader(parcel_out, &status_out);
+ if (status != STATUS_OK)
+ return status;
+
+ if (!AStatus_isOk(status_out))
+ return meaningful_binder_status(status_out);
+
+ return STATUS_OK;
+}
+
+static int32_t dnsresolver_set_resolver_configuration(void *handle, const struct dnsresolver_params *params)
+{
+ AIBinder *const binder = handle;
+ binder_status_t status;
+ _cleanup_parcel_ AParcel *parcel_in = NULL;
+ _cleanup_parcel_ AParcel *parcel_out = NULL;
+ _cleanup_status_ AStatus *status_out = NULL;
+ int32_t start_position, end_position;
+
+ status = AIBinder_prepareTransaction(binder, &parcel_in);
+ if (status != STATUS_OK)
+ return status;
+
+ status = AParcel_writeInt32(parcel_in, 1);
+ if (status != STATUS_OK)
+ return status;
+
+ start_position = AParcel_getDataPosition(parcel_in);
+ status = AParcel_writeInt32(parcel_in, 0);
+ if (status != STATUS_OK)
+ return status;
+
+ status = AParcel_writeInt32(parcel_in, params->netid);
+ if (status != STATUS_OK)
+ return status;
+ status = AParcel_writeInt32(parcel_in, params->sample_validity_seconds);
+ if (status != STATUS_OK)
+ return status;
+ status = AParcel_writeInt32(parcel_in, params->success_threshold);
+ if (status != STATUS_OK)
+ return status;
+ status = AParcel_writeInt32(parcel_in, params->min_samples);
+ if (status != STATUS_OK)
+ return status;
+ status = AParcel_writeInt32(parcel_in, params->max_samples);
+ if (status != STATUS_OK)
+ return status;
+ status = AParcel_writeInt32(parcel_in, params->base_timeout_msec);
+ if (status != STATUS_OK)
+ return status;
+ status = AParcel_writeInt32(parcel_in, params->retry_count);
+ if (status != STATUS_OK)
+ return status;
+ status = AParcel_writeStringArray(parcel_in, params->servers, string_array_size(params->servers), &string_array_getter);
+ if (status != STATUS_OK)
+ return status;
+ status = AParcel_writeStringArray(parcel_in, params->domains, string_array_size(params->domains), &string_array_getter);
+ if (status != STATUS_OK)
+ return status;
+ status = AParcel_writeString(parcel_in, params->tls_name, string_size(params->tls_name));
+ if (status != STATUS_OK)
+ return status;
+ status = AParcel_writeStringArray(parcel_in, params->tls_servers, string_array_size(params->tls_servers), &string_array_getter);
+ if (status != STATUS_OK)
+ return status;
+ status = AParcel_writeStringArray(parcel_in, params->tls_fingerprints, string_array_size(params->tls_fingerprints), &string_array_getter);
+ if (status != STATUS_OK)
+ return status;
+
+ end_position = AParcel_getDataPosition(parcel_in);
+ status = AParcel_setDataPosition(parcel_in, start_position);
+ if (status != STATUS_OK)
+ return status;
+ status = AParcel_writeInt32(parcel_in, end_position - start_position);
+ if (status != STATUS_OK)
+ return status;
+ status = AParcel_setDataPosition(parcel_in, end_position);
+ if (status != STATUS_OK)
+ return status;
+
+ status = AIBinder_transact(binder, FIRST_CALL_TRANSACTION + 2 /* setResolverConfiguration */, &parcel_in, &parcel_out, 0);
+ if (status != STATUS_OK)
+ return status;
+
+ status = AParcel_readStatusHeader(parcel_out, &status_out);
+ if (status != STATUS_OK)
+ return status;
+
+ return meaningful_binder_status(status_out);
+}
+
+static void auto_su(int argc, char *argv[])
+{
+ char *args[argc + 4];
+
+ if (!getuid())
+ return;
+
+ args[0] = "su";
+ args[1] = "-p";
+ args[2] = "-c";
+ memcpy(&args[3], argv, argc * sizeof(*args));
+ args[argc + 3] = NULL;
+
+ printf("[$] su -p -c ");
+ for (int i = 0; i < argc; ++i)
+ printf("%s%c", argv[i], i == argc - 1 ? '\n' : ' ');
+
+ execvp("su", args);
+ exit(errno);
+}
+
+static void add_if(const char *iface)
+{
+ cmd("ip link add %s type wireguard", iface);
+}
+
+static void del_if(const char *iface)
+{
+ DEFINE_CMD(c_rule);
+ DEFINE_CMD(c_iptables);
+ DEFINE_CMD(c_ip6tables);
+ _cleanup_regfree_ regex_t rule_reg = { 0 }, iptables_reg = { 0 };
+ regmatch_t matches[2];
+ char *netid = NULL;
+ _cleanup_free_ char *rule_regex = concat("0xc([0-9a-f]+)/0xcffff lookup ", iface, NULL);
+ _cleanup_free_ char *iptables_regex = concat("^-A (.* --comment \"wireguard rule ", iface, "\"[^\n]*)\n*$", NULL);
+
+ xregcomp(&rule_reg, rule_regex, REG_EXTENDED);
+ xregcomp(&iptables_reg, iptables_regex, REG_EXTENDED);
+
+ cmd("ip link del %s", iface);
+
+ for (char *rule = cmd_ret(&c_iptables, "iptables-save"); rule; rule = cmd_ret(&c_iptables, NULL)) {
+ if (!regexec(&iptables_reg, rule, ARRAY_SIZE(matches), matches, 0)) {
+ rule[matches[1].rm_eo] = '\0';
+ cmd("iptables -D %s", &rule[matches[1].rm_so]);
+ }
+ }
+ for (char *rule = cmd_ret(&c_ip6tables, "ip6tables-save"); rule; rule = cmd_ret(&c_ip6tables, NULL)) {
+ if (!regexec(&iptables_reg, rule, ARRAY_SIZE(matches), matches, 0)) {
+ rule[matches[1].rm_eo] = '\0';
+ cmd("ip6tables -D %s", &rule[matches[1].rm_so]);
+ }
+ }
+
+ for (char *rule = cmd_ret(&c_rule, "ip rule show"); rule; rule = cmd_ret(&c_rule, NULL)) {
+ if (!regexec(&rule_reg, rule, ARRAY_SIZE(matches), matches, 0)) {
+ rule[matches[1].rm_eo] = '\0';
+ netid = &rule[matches[1].rm_so];
+ break;
+ }
+ }
+ if (netid)
+ cndc("network destroy %lu", strtoul(netid, NULL, 16));
+}
+
+static bool should_block_ipv6(const char *iface)
+{
+ DEFINE_CMD(c);
+ bool has_ipv6 = false, has_all_none = true;
+
+ for (char *endpoint = cmd_ret(&c, "wg show %s endpoints", iface); endpoint; endpoint = cmd_ret(&c, NULL)) {
+ char *start = strchr(endpoint, '\t');
+
+ if (!start)
+ continue;
+ ++start;
+ if (start[0] != '(')
+ has_all_none = false;
+ if (start[0] == '[')
+ has_ipv6 = true;
+ }
+ return !has_ipv6 && !has_all_none;
+}
+
+static uint16_t determine_listen_port(const char *iface)
+{
+ DEFINE_CMD(c);
+ unsigned long listen_port = 0;
+ char *value;
+
+ cmd("ip link set up dev %s", iface);
+ value = cmd_ret(&c, "wg show %s listen-port", iface);
+ if (!value)
+ goto set_back_down;
+ listen_port = strtoul(value, NULL, 10);
+ if (listen_port > UINT16_MAX || !listen_port) {
+ listen_port = 0;
+ goto set_back_down;
+ }
+set_back_down:
+ cmd("ip link set down dev %s", iface);
+ return listen_port;
+}
+
+static void up_if(unsigned int *netid, const char *iface, uint16_t listen_port)
+{
+ srandom(time(NULL) ^ getpid()); /* Not real randomness. */
+
+ while (*netid < 4096)
+ *netid = random() & 0xfffe;
+
+ cmd("wg set %s fwmark 0x20000", iface);
+ cmd("iptables -I OUTPUT 1 -m mark --mark 0x20000 -j ACCEPT -m comment --comment \"wireguard rule %s\"", iface);
+ cmd("ip6tables -I OUTPUT 1 -m mark --mark 0x20000 -j ACCEPT -m comment --comment \"wireguard rule %s\"", iface);
+ if (listen_port) {
+ cmd("iptables -I INPUT 1 -p udp --dport %u -j ACCEPT -m comment --comment \"wireguard rule %s\"", listen_port, iface);
+ cmd("ip6tables -I INPUT 1 -p udp --dport %u -j %s -m comment --comment \"wireguard rule %s\"", listen_port, should_block_ipv6(iface) ? "DROP" : "ACCEPT", iface);
+ }
+ cmd("ip link set up dev %s", iface);
+ cndc(sdk_version < 31 ? "network create %u vpn 1 1" : "network create %u vpn 1", *netid);
+ cndc("network interface add %u %s", *netid, iface);
+}
+
+static int compare_uid(const void *a, const void *b)
+{
+ return *(const uid_t *)a - *(const uid_t *)b;
+}
+
+static uid_t *get_uid_list(const char *selected_applications)
+{
+ _cleanup_fclose_ FILE *package_list = NULL;
+ _cleanup_free_ char *line = NULL;
+ uid_t package_uid;
+ size_t line_len = 0, i;
+ const char *comma, *start;
+ uid_t *uid_list;
+
+ if (!selected_applications)
+ return xcalloc(1, sizeof(*uid_list));
+
+ for (i = 1, comma = selected_applications; comma; comma = strchr(comma + 1, ','), ++i);
+ uid_list = xcalloc(i, sizeof(*uid_list));
+ i = 0;
+
+ package_list = fopen("/data/system/packages.list", "r");
+ if (!package_list) {
+ perror("Error: Unable to open package list");
+ exit(errno);
+ }
+
+ while (getline(&line, &line_len, package_list) >= 0) {
+ char *package_name, *package_uid_str, *endptr;
+
+ package_name = line;
+ package_uid_str = strchr(package_name, ' ');
+ if (!package_uid_str)
+ continue;
+ *package_uid_str++ = '\0';
+ *strchrnul(package_uid_str, ' ') = '\0';
+ package_uid = strtoul(package_uid_str, &endptr, 10);
+ if (!package_uid || !*package_uid_str || *endptr)
+ continue;
+
+ for (start = selected_applications; comma = strchrnul(start, ','), *start; start = comma + 1) {
+ ptrdiff_t token_len = comma - start;
+
+ if (token_len && strlen(package_name) == token_len && !strncmp(start, package_name, token_len))
+ uid_list[i++] = package_uid;
+ }
+ }
+ qsort(uid_list, i, sizeof(*uid_list), compare_uid);
+ return uid_list;
+}
+
+static void set_users(unsigned int netid, const char *excluded_applications, const char *included_applications)
+{
+ _cleanup_free_ uid_t *uids = NULL;
+ uid_t *uid;
+ unsigned int args_per_command = 0;
+ _cleanup_free_ char *ranges = NULL;
+ char range[22];
+ uid_t start;
+
+ if (excluded_applications && included_applications) {
+ fprintf(stderr, "Error: only one of ExcludedApplications and IncludedApplications may be specified, but not both\n");
+ exit(EEXIST);
+ }
+
+ if (excluded_applications || !included_applications) {
+ uid = uids = get_uid_list(excluded_applications);
+ for (start = 0; *uid; start = *uid + 1, ++uid) {
+ if (start > *uid - 1)
+ continue;
+ else if (start == *uid - 1)
+ snprintf(range, sizeof(range), "%u", start);
+ else
+ snprintf(range, sizeof(range), "%u-%u", start, *uid - 1);
+ ranges = concat_and_free(ranges, " ", range);
+ if (++args_per_command % 18 == 0) {
+ cndc("network users add %u %s", netid, ranges);
+ free(ranges);
+ ranges = NULL;
+ }
+ }
+ if (start < 99999) {
+ snprintf(range, sizeof(range), "%u-99999", start);
+ ranges = concat_and_free(ranges, " ", range);
+ }
+ } else {
+ for (uid = uids = get_uid_list(included_applications); *uid; ++uid) {
+ snprintf(range, sizeof(range), "%u", *uid);
+ ranges = concat_and_free(ranges, " ", range);
+ if (++args_per_command % 18 == 0) {
+ cndc("network users add %u %s", netid, ranges);
+ free(ranges);
+ ranges = NULL;
+ }
+ }
+ }
+
+ if (ranges)
+ cndc("network users add %u %s", netid, ranges);
+}
+
+static void set_dnses(unsigned int netid, const char *dnses)
+{
+ size_t len = strlen(dnses);
+ if (len > (1<<16))
+ return;
+ _cleanup_free_ char *mutable = xstrdup(dnses);
+ _cleanup_free_ char *dns_shell_arglist = xmalloc(len * 4 + 1);
+ _cleanup_free_ char *dns_search_shell_arglist = xmalloc(len * 4 + 1);
+ _cleanup_free_ char *dns_function_arglist = xmalloc(len * 4 + 1);
+ _cleanup_free_ char *dns_search_function_arglist = xmalloc(len * 4 + 1);
+ _cleanup_free_ char *arg = xmalloc(len + 4);
+ _cleanup_free_ char **dns_list = NULL;
+ _cleanup_free_ char **dns_search_list = NULL;
+ _cleanup_binder_ AIBinder *handle = NULL;
+ _cleanup_regfree_ regex_t regex_ipnothost = { 0 };
+ size_t dns_list_size = 0, dns_search_list_size = 0;
+ bool is_ip;
+
+ if (!len)
+ return;
+
+ xregcomp(&regex_ipnothost, "(^[0-9.]+$)|(^.*:.*$)", REG_EXTENDED | REG_NOSUB);
+ for (char *dns = strtok(mutable, ", \t\n"); dns; dns = strtok(NULL, ", \t\n")) {
+ if (strchr(dns, '\'') || strchr(dns, '\\'))
+ continue;
+ ++*(!regexec(&regex_ipnothost, dns, 0, NULL, 0) ? &dns_list_size : &dns_search_list_size);
+ }
+ if (!dns_list_size)
+ return;
+ dns_list = xcalloc(dns_list_size + 1, sizeof(*dns_list));
+ dns_search_list = xcalloc(dns_search_list_size + 1, sizeof(*dns_search_list));
+ free(mutable);
+ mutable = xstrdup(dnses);
+
+ dns_shell_arglist[0] = '\0';
+ dns_search_shell_arglist[0] = '\0';
+ dns_function_arglist[0] = '\0';
+ dns_search_function_arglist[0] = '\0';
+ dns_list_size = 0;
+ dns_search_list_size = 0;
+ for (char *dns = strtok(mutable, ", \t\n"); dns; dns = strtok(NULL, ", \t\n")) {
+ if (strchr(dns, '\'') || strchr(dns, '\\'))
+ continue;
+ is_ip = !regexec(&regex_ipnothost, dns, 0, NULL, 0);
+ snprintf(arg, len + 3, "'%s' ", dns);
+ strncat(is_ip ? dns_shell_arglist : dns_search_shell_arglist, arg, len * 4 - 1);
+ snprintf(arg, len + 2, (is_ip ? dns_function_arglist[0] : dns_search_function_arglist[0]) == '\0' ? "%s" : ", %s", dns);
+ strncat(is_ip ? dns_function_arglist : dns_search_function_arglist, arg, len * 4 - 1);
+ *(is_ip ? &dns_list[dns_list_size++] : &dns_search_list[dns_search_list_size++]) = dns;
+ }
+
+ if ((handle = dnsresolver_get_handle())) {
+ binder_status_t status;
+
+ printf("[#] <binder>::dnsResolver->createNetworkCache(%u)\n", netid);
+ status = dnsresolver_create_network_cache(handle, netid);
+ if (status != 0) {
+ fprintf(stderr, "Error: unable to create network cache\n");
+ exit(ENONET);
+ }
+
+ struct dnsresolver_params params = {
+ .netid = netid,
+ .sample_validity_seconds = DNSRESOLVER_SAMPLE_VALIDITY,
+ .success_threshold = DNSRESOLVER_SUCCESS_THRESHOLD,
+ .min_samples = DNSRESOLVER_MIN_SAMPLES,
+ .max_samples = DNSRESOLVER_MAX_SAMPLES,
+ .base_timeout_msec = DNSRESOLVER_BASE_TIMEOUT,
+ .retry_count = DNSRESOLVER_RETRY_COUNT,
+ .servers = dns_list,
+ .domains = dns_search_list,
+ .tls_name = "",
+ .tls_servers = (char *[]){NULL},
+ .tls_fingerprints = (char *[]){NULL}
+ };
+
+ printf("[#] <binder>::dnsResolver->setResolverConfiguration(%u, [%s], [%s], %d, %d, %d, %d, %d, %d, [], [])\n",
+ netid, dns_function_arglist, dns_search_function_arglist, DNSRESOLVER_SAMPLE_VALIDITY,
+ DNSRESOLVER_SUCCESS_THRESHOLD, DNSRESOLVER_MIN_SAMPLES, DNSRESOLVER_MAX_SAMPLES,
+ DNSRESOLVER_BASE_TIMEOUT, DNSRESOLVER_RETRY_COUNT);
+ status = dnsresolver_set_resolver_configuration(handle, &params);
+
+ if (status != 0) {
+ fprintf(stderr, "Error: unable to set DNS servers through Binder: %d\n", status);
+ exit(ENONET);
+ }
+ } else
+ cndc("resolver setnetdns %u '%s' %s", netid, dns_search_shell_arglist, dns_shell_arglist);
+}
+
+static void add_addr(const char *iface, const char *addr)
+{
+ if (strchr(addr, ':')) {
+ cndc("interface ipv6 %s enable", iface);
+ cmd("ip -6 addr add '%s' dev %s", addr, iface);
+ } else {
+ _cleanup_free_ char *mut_addr = strdup(addr);
+ char *slash = strchr(mut_addr, '/');
+ unsigned char mask = 32;
+
+ if (slash) {
+ *slash = '\0';
+ mask = atoi(slash + 1);
+ }
+ cndc("interface setcfg %s '%s' %u", iface, mut_addr, mask);
+ }
+}
+
+static void set_addr(const char *iface, const char *addrs)
+{
+ _cleanup_free_ char *mutable = xstrdup(addrs);
+
+ for (char *addr = strtok(mutable, ", \t\n"); addr; addr = strtok(NULL, ", \t\n")) {
+ if (strchr(addr, '\'') || strchr(addr, '\\'))
+ continue;
+ add_addr(iface, addr);
+ }
+}
+
+static int get_route_mtu(const char *endpoint)
+{
+ DEFINE_CMD(c_route);
+ DEFINE_CMD(c_dev);
+ regmatch_t matches[2];
+ _cleanup_regfree_ regex_t regex_mtu = { 0 }, regex_dev = { 0 };
+ char *route, *mtu, *dev;
+
+ xregcomp(&regex_mtu, "mtu ([0-9]+)", REG_EXTENDED);
+ xregcomp(&regex_dev, "dev ([^ ]+)", REG_EXTENDED);
+
+ if (strcmp(endpoint, "default"))
+ route = cmd_ret(&c_route, "ip -o route get %s", endpoint);
+ else
+ route = cmd_ret(&c_route, "ip -o route show %s", endpoint);
+ if (!route)
+ return -1;
+
+ if (!regexec(&regex_mtu, route, ARRAY_SIZE(matches), matches, 0)) {
+ route[matches[1].rm_eo] = '\0';
+ mtu = &route[matches[1].rm_so];
+ } else if (!regexec(&regex_dev, route, ARRAY_SIZE(matches), matches, 0)) {
+ route[matches[1].rm_eo] = '\0';
+ dev = &route[matches[1].rm_so];
+ route = cmd_ret(&c_dev, "ip -o link show dev %s", dev);
+ if (!route)
+ return -1;
+ if (regexec(&regex_mtu, route, ARRAY_SIZE(matches), matches, 0))
+ return -1;
+ route[matches[1].rm_eo] = '\0';
+ mtu = &route[matches[1].rm_so];
+ } else
+ return -1;
+ return atoi(mtu);
+}
+
+static void set_mtu(const char *iface, unsigned int mtu)
+{
+ DEFINE_CMD(c_endpoints);
+ _cleanup_regfree_ regex_t regex_endpoint = { 0 };
+ regmatch_t matches[2];
+ int endpoint_mtu, next_mtu;
+
+ if (mtu) {
+ cndc("interface setmtu %s %u", iface, mtu);
+ return;
+ }
+
+ xregcomp(&regex_endpoint, "^\\[?([a-z0-9:.]+)\\]?:[0-9]+$", REG_EXTENDED);
+
+ endpoint_mtu = get_route_mtu("default");
+ if (endpoint_mtu == -1)
+ endpoint_mtu = 1500;
+
+ for (char *endpoint = cmd_ret(&c_endpoints, "wg show %s endpoints", iface); endpoint; endpoint = cmd_ret(&c_endpoints, NULL)) {
+ if (regexec(&regex_endpoint, endpoint, ARRAY_SIZE(matches), matches, 0))
+ continue;
+ endpoint[matches[1].rm_eo] = '\0';
+ endpoint = &endpoint[matches[1].rm_so];
+
+ next_mtu = get_route_mtu(endpoint);
+ if (next_mtu > 0 && next_mtu < endpoint_mtu)
+ endpoint_mtu = next_mtu;
+ }
+
+ cndc("interface setmtu %s %d", iface, endpoint_mtu - 80);
+}
+
+static void add_route(const char *iface, unsigned int netid, const char *route)
+{
+ cndc("network route add %u %s %s", netid, iface, route);
+}
+
+static void set_routes(const char *iface, unsigned int netid)
+{
+ DEFINE_CMD(c);
+
+ for (char *allowedips = cmd_ret(&c, "wg show %s allowed-ips", iface); allowedips; allowedips = cmd_ret(&c, NULL)) {
+ char *start = strchr(allowedips, '\t');
+
+ if (!start)
+ continue;
+ ++start;
+ for (char *allowedip = strtok(start, " \n"); allowedip; allowedip = strtok(NULL, " \n")) {
+ if (!strcmp(allowedip, "(none)"))
+ continue;
+ add_route(iface, netid, allowedip);
+ }
+ }
+}
+
+static void set_config(const char *iface, const char *config)
+{
+ FILE *config_writer;
+ _cleanup_free_ char *cmd = concat("wg setconf ", iface, " /proc/self/fd/0", NULL);
+ int ret;
+
+ printf("[#] %s\n", cmd);
+
+ config_writer = popen(cmd, "w");
+ if (!config_writer) {
+ perror("Error: popen");
+ exit(errno);
+ }
+ if (fputs(config, config_writer) < 0) {
+ perror("Error: fputs");
+ exit(errno);
+ }
+ ret = pclose(config_writer);
+ if (ret)
+ exit(WIFEXITED(ret) ? WEXITSTATUS(ret) : EIO);
+}
+
+static void broadcast_change(void)
+{
+ const char *pkg = getenv("CALLING_PACKAGE");
+
+ if (!pkg || strcmp(pkg, WG_PACKAGE_NAME))
+ cmd("am broadcast -a com.wireguard.android.action.REFRESH_TUNNEL_STATES " WG_PACKAGE_NAME);
+}
+
+static void print_search_paths(FILE *file, const char *prefix)
+{
+ _cleanup_free_ char *paths = strdup(WG_CONFIG_SEARCH_PATHS);
+
+ for (char *path = strtok(paths, " "); path; path = strtok(NULL, " "))
+ fprintf(file, "%s%s\n", prefix, path);
+}
+
+static void cmd_usage(const char *program)
+{
+ printf( "Usage: %s [ up | down ] [ CONFIG_FILE | INTERFACE ]\n"
+ "\n"
+ " CONFIG_FILE is a configuration file, whose filename is the interface name\n"
+ " followed by `.conf'. Otherwise, INTERFACE is an interface name, with\n"
+ " configuration found at:\n\n", program);
+ print_search_paths(stdout, " - ");
+ printf( "\n It is to be readable by wg(8)'s `setconf' sub-command, with the exception\n"
+ " of the following additions to the [Interface] section, which are handled by\n"
+ " this program:\n\n"
+ " - Address: may be specified one or more times and contains one or more\n"
+ " IP addresses (with an optional CIDR mask) to be set for the interface.\n"
+ " - MTU: an optional MTU for the interface; if unspecified, auto-calculated.\n"
+ " - DNS: an optional DNS server to use while the device is up.\n"
+ " - ExcludedApplications: optional blacklist of applications to exclude from the tunnel.\n\n"
+ " - IncludedApplications: optional whitelist of applications to include in the tunnel.\n\n"
+ " See wg-quick(8) for more info and examples.\n");
+}
+
+static char *cleanup_iface = NULL;
+
+static void cmd_up_cleanup(void)
+{
+ is_exiting = true;
+ if (cleanup_iface)
+ del_if(cleanup_iface);
+ free(cleanup_iface);
+}
+
+static void cmd_up(const char *iface, const char *config, unsigned int mtu, const char *addrs, const char *dnses, const char *excluded_applications, const char *included_applications)
+{
+ DEFINE_CMD(c);
+ unsigned int netid = 0;
+ uint16_t listen_port;
+
+ if (cmd_ret(&c, "ip link show dev %s 2>/dev/null", iface)) {
+ fprintf(stderr, "Error: %s already exists\n", iface);
+ exit(EEXIST);
+ }
+
+ cleanup_iface = xstrdup(iface);
+ atexit(cmd_up_cleanup);
+
+ add_if(iface);
+ set_config(iface, config);
+ listen_port = determine_listen_port(iface);
+ up_if(&netid, iface, listen_port);
+ set_addr(iface, addrs);
+ set_dnses(netid, dnses);
+ set_routes(iface, netid);
+ set_mtu(iface, mtu);
+ set_users(netid, excluded_applications, included_applications);
+ broadcast_change();
+
+ free(cleanup_iface);
+ cleanup_iface = NULL;
+ exit(EXIT_SUCCESS);
+}
+
+static void cmd_down(const char *iface)
+{
+ DEFINE_CMD(c);
+ bool found = false;
+
+ char *ifaces = cmd_ret(&c, "wg show interfaces");
+ if (ifaces) {
+ for (char *eiface = strtok(ifaces, " \n"); eiface; eiface = strtok(NULL, " \n")) {
+ if (!strcmp(iface, eiface)) {
+ found = true;
+ break;
+ }
+ }
+ }
+ if (!found) {
+ fprintf(stderr, "Error: %s is not a WireGuard interface\n", iface);
+ exit(EMEDIUMTYPE);
+ }
+
+ del_if(iface);
+ broadcast_change();
+ exit(EXIT_SUCCESS);
+}
+
+static void parse_options(char **iface, char **config, unsigned int *mtu, char **addrs, char **dnses, char **excluded_applications, char **included_applications, const char *arg)
+{
+ _cleanup_fclose_ FILE *file = NULL;
+ _cleanup_free_ char *line = NULL;
+ _cleanup_free_ char *filename = NULL;
+ _cleanup_free_ char *paths = strdup(WG_CONFIG_SEARCH_PATHS);
+ _cleanup_regfree_ regex_t regex_iface = { 0 }, regex_conf = { 0 };
+ regmatch_t matches[2];
+ struct stat sbuf;
+ size_t n = 0;
+ bool in_interface_section = false;
+
+ *iface = *config = *addrs = *dnses = NULL;
+ *mtu = 0;
+
+ xregcomp(&regex_iface, "^[a-zA-Z0-9_=+.-]{1,15}$", REG_EXTENDED | REG_NOSUB);
+ xregcomp(&regex_conf, "/?([a-zA-Z0-9_=+.-]{1,15})\\.conf$", REG_EXTENDED);
+
+ if (!regexec(&regex_iface, arg, 0, NULL, 0)) {
+ for (char *path = strtok(paths, " "); path; path = strtok(NULL, " ")) {
+ free(filename);
+ if (asprintf(&filename, "%s/%s.conf", path, arg) < 0) {
+ perror("Error: asprintf");
+ exit(errno);
+ }
+ file = fopen(filename, "r");
+ if (file)
+ break;
+ }
+ if (!file) {
+ fprintf(stderr, "Error: Unable to find configuration file for `%s' in:\n", arg);
+ print_search_paths(stderr, "- ");
+ exit(errno);
+ }
+ } else {
+ filename = xstrdup(arg);
+ file = fopen(filename, "r");
+ if (!file) {
+ fprintf(stderr, "Error: Unable to find configuration file at `%s'\n", filename);
+ exit(errno);
+ }
+ }
+
+ if (regexec(&regex_conf, filename, ARRAY_SIZE(matches), matches, 0)) {
+ fprintf(stderr, "Error: The config file must be a valid interface name, followed by .conf\n");
+ exit(EINVAL);
+ }
+
+ if (fstat(fileno(file), &sbuf) < 0) {
+ perror("Error: fstat");
+ exit(errno);
+ }
+ if (sbuf.st_mode & 0007)
+ fprintf(stderr, "Warning: `%s' is world accessible\n", filename);
+
+ filename[matches[1].rm_eo] = '\0';
+ *iface = xstrdup(&filename[matches[1].rm_so]);
+
+ while (getline(&line, &n, file) >= 0) {
+ size_t len = strlen(line), j = 0;
+ if (len > (1<<16))
+ return;
+ _cleanup_free_ char *clean = xmalloc(len + 1);
+
+ for (size_t i = 0; i < len; ++i) {
+ if (!isspace(line[i]))
+ clean[j++] = line[i];
+ }
+ clean[j] = '\0';
+
+ if (clean[0] == '[')
+ in_interface_section = false;
+ if (!strcasecmp(clean, "[Interface]"))
+ in_interface_section = true;
+ if (in_interface_section) {
+ if (!strncasecmp(clean, "Address=", 8) && j > 8) {
+ *addrs = concat_and_free(*addrs, ",", clean + 8);
+ continue;
+ } else if (!strncasecmp(clean, "DNS=", 4) && j > 4) {
+ *dnses = concat_and_free(*dnses, ",", clean + 4);
+ continue;
+ } else if (!strncasecmp(clean, "ExcludedApplications=", 21) && j > 4) {
+ *excluded_applications = concat_and_free(*excluded_applications, ",", clean + 21);
+ continue;
+ } else if (!strncasecmp(clean, "IncludedApplications=", 21) && j > 4) {
+ *included_applications = concat_and_free(*included_applications, ",", clean + 21);
+ continue;
+ } else if (!strncasecmp(clean, "MTU=", 4) && j > 4) {
+ *mtu = atoi(clean + 4);
+ continue;
+ }
+ }
+ *config = concat_and_free(*config, "", line);
+ }
+
+ if (!*iface)
+ *iface = xstrdup("");
+ if (!*config)
+ *config = xstrdup("");
+ if (!*addrs)
+ *addrs = xstrdup("");
+ if (!*dnses)
+ *dnses = xstrdup("");
+}
+
+int main(int argc, char *argv[])
+{
+ _cleanup_free_ char *iface = NULL;
+ _cleanup_free_ char *config = NULL;
+ _cleanup_free_ char *addrs = NULL;
+ _cleanup_free_ char *dnses = NULL;
+ _cleanup_free_ char *excluded_applications = NULL;
+ _cleanup_free_ char *included_applications = NULL;
+ unsigned int mtu;
+ char prop[PROP_VALUE_MAX + 1];
+
+ if (__system_property_get("ro.build.version.sdk", prop))
+ sdk_version = atoi(prop);
+
+ if (argc == 2 && (!strcmp(argv[1], "help") || !strcmp(argv[1], "--help") || !strcmp(argv[1], "-h")))
+ cmd_usage(argv[0]);
+ else if (argc == 3 && !strcmp(argv[1], "up")) {
+ auto_su(argc, argv);
+ parse_options(&iface, &config, &mtu, &addrs, &dnses, &excluded_applications, &included_applications, argv[2]);
+ cmd_up(iface, config, mtu, addrs, dnses, excluded_applications, included_applications);
+ } else if (argc == 3 && !strcmp(argv[1], "down")) {
+ auto_su(argc, argv);
+ parse_options(&iface, &config, &mtu, &addrs, &dnses, &excluded_applications, &included_applications, argv[2]);
+ cmd_down(iface);
+ } else {
+ cmd_usage(argv[0]);
+ return 1;
+ }
+ return 0;
+}
diff --git a/contrib/wireguard-tools/src/wg-quick/darwin.bash b/contrib/wireguard-tools/src/wg-quick/darwin.bash
new file mode 100755
--- /dev/null
+++ b/contrib/wireguard-tools/src/wg-quick/darwin.bash
@@ -0,0 +1,522 @@
+#!/usr/bin/env bash
+# SPDX-License-Identifier: GPL-2.0
+#
+# Copyright (C) 2015-2020 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
+#
+
+set -e -o pipefail
+shopt -s extglob
+export LC_ALL=C
+
+SELF="${BASH_SOURCE[0]}"
+[[ $SELF == */* ]] || SELF="./$SELF"
+SELF="$(cd "${SELF%/*}" && pwd -P)/${SELF##*/}"
+export PATH="/usr/bin:/bin:/usr/sbin:/sbin:${SELF%/*}:$PATH"
+
+WG_CONFIG=""
+INTERFACE=""
+ADDRESSES=( )
+MTU=""
+DNS=( )
+DNS_SEARCH=( )
+TABLE=""
+PRE_UP=( )
+POST_UP=( )
+PRE_DOWN=( )
+POST_DOWN=( )
+SAVE_CONFIG=0
+CONFIG_FILE=""
+PROGRAM="${0##*/}"
+ARGS=( "$@" )
+
+cmd() {
+ echo "[#] $*" >&2
+ "$@"
+}
+
+die() {
+ echo "$PROGRAM: $*" >&2
+ exit 1
+}
+
+[[ ${BASH_VERSINFO[0]} -ge 4 ]] || die "Version mismatch: bash ${BASH_VERSINFO[0]} detected, when bash 4+ required"
+
+CONFIG_SEARCH_PATHS=( /etc/wireguard /usr/local/etc/wireguard )
+
+parse_options() {
+ local interface_section=0 line key value stripped path v
+ CONFIG_FILE="$1"
+ if [[ $CONFIG_FILE =~ ^[a-zA-Z0-9_=+.-]{1,15}$ ]]; then
+ for path in "${CONFIG_SEARCH_PATHS[@]}"; do
+ CONFIG_FILE="$path/$1.conf"
+ [[ -e $CONFIG_FILE ]] && break
+ done
+ fi
+ [[ -e $CONFIG_FILE ]] || die "\`$CONFIG_FILE' does not exist"
+ [[ $CONFIG_FILE =~ (^|/)([a-zA-Z0-9_=+.-]{1,15})\.conf$ ]] || die "The config file must be a valid interface name, followed by .conf"
+ CONFIG_FILE="$(cd "${CONFIG_FILE%/*}" && pwd -P)/${CONFIG_FILE##*/}"
+ ((($(stat -f '0%#p' "$CONFIG_FILE") & $(stat -f '0%#p' "${CONFIG_FILE%/*}") & 0007) == 0)) || echo "Warning: \`$CONFIG_FILE' is world accessible" >&2
+ INTERFACE="${BASH_REMATCH[2]}"
+ shopt -s nocasematch
+ while read -r line || [[ -n $line ]]; do
+ stripped="${line%%\#*}"
+ key="${stripped%%=*}"; key="${key##*([[:space:]])}"; key="${key%%*([[:space:]])}"
+ value="${stripped#*=}"; value="${value##*([[:space:]])}"; value="${value%%*([[:space:]])}"
+ [[ $key == "["* ]] && interface_section=0
+ [[ $key == "[Interface]" ]] && interface_section=1
+ if [[ $interface_section -eq 1 ]]; then
+ case "$key" in
+ Address) ADDRESSES+=( ${value//,/ } ); continue ;;
+ MTU) MTU="$value"; continue ;;
+ DNS) for v in ${value//,/ }; do
+ [[ $v =~ (^[0-9.]+$)|(^.*:.*$) ]] && DNS+=( $v ) || DNS_SEARCH+=( $v )
+ done; continue ;;
+ Table) TABLE="$value"; continue ;;
+ PreUp) PRE_UP+=( "$value" ); continue ;;
+ PreDown) PRE_DOWN+=( "$value" ); continue ;;
+ PostUp) POST_UP+=( "$value" ); continue ;;
+ PostDown) POST_DOWN+=( "$value" ); continue ;;
+ SaveConfig) read_bool SAVE_CONFIG "$value"; continue ;;
+ esac
+ fi
+ WG_CONFIG+="$line"$'\n'
+ done < "$CONFIG_FILE"
+ shopt -u nocasematch
+}
+
+detect_launchd() {
+ unset LAUNCHED_BY_LAUNCHD
+ local line
+ while read -r line; do
+ if [[ $line =~ ^\s*domain\ =\ ]]; then
+ LAUNCHED_BY_LAUNCHD=1
+ break
+ fi
+ done < <(launchctl procinfo $$ 2>/dev/null)
+}
+
+read_bool() {
+ case "$2" in
+ true) printf -v "$1" 1 ;;
+ false) printf -v "$1" 0 ;;
+ *) die "\`$2' is neither true nor false"
+ esac
+}
+
+auto_su() {
+ [[ $UID == 0 ]] || exec sudo -p "$PROGRAM must be run as root. Please enter the password for %u to continue: " -- "$BASH" -- "$SELF" "${ARGS[@]}"
+}
+
+get_real_interface() {
+ local interface diff
+ wg show interfaces >/dev/null
+ [[ -f "/var/run/wireguard/$INTERFACE.name" ]] || return 1
+ interface="$(< "/var/run/wireguard/$INTERFACE.name")"
+ [[ -n $interface && -S "/var/run/wireguard/$interface.sock" ]] || return 1
+ diff=$(( $(stat -f %m "/var/run/wireguard/$interface.sock" 2>/dev/null || echo 200) - $(stat -f %m "/var/run/wireguard/$INTERFACE.name" 2>/dev/null || echo 100) ))
+ [[ $diff -ge 2 || $diff -le -2 ]] && return 1
+ REAL_INTERFACE="$interface"
+ echo "[+] Interface for $INTERFACE is $REAL_INTERFACE" >&2
+ return 0
+}
+
+add_if() {
+ export WG_TUN_NAME_FILE="/var/run/wireguard/$INTERFACE.name"
+ mkdir -p "/var/run/wireguard/"
+ cmd "${WG_QUICK_USERSPACE_IMPLEMENTATION:-wireguard-go}" utun
+ get_real_interface
+}
+
+del_routes() {
+ [[ -n $REAL_INTERFACE ]] || return 0
+ local todelete=( ) destination gateway netif
+ while read -r destination _ _ _ _ netif _; do
+ [[ $netif == "$REAL_INTERFACE" ]] && todelete+=( "$destination" )
+ done < <(netstat -nr -f inet)
+ for destination in "${todelete[@]}"; do
+ cmd route -q -n delete -inet "$destination" >/dev/null || true
+ done
+ todelete=( )
+ while read -r destination gateway _ netif; do
+ [[ $netif == "$REAL_INTERFACE" || ( $netif == lo* && $gateway == "$REAL_INTERFACE" ) ]] && todelete+=( "$destination" )
+ done < <(netstat -nr -f inet6)
+ for destination in "${todelete[@]}"; do
+ cmd route -q -n delete -inet6 "$destination" >/dev/null || true
+ done
+ for destination in "${ENDPOINTS[@]}"; do
+ if [[ $destination == *:* ]]; then
+ cmd route -q -n delete -inet6 "$destination" >/dev/null || true
+ else
+ cmd route -q -n delete -inet "$destination" >/dev/null || true
+ fi
+ done
+}
+
+del_if() {
+ [[ -z $REAL_INTERFACE ]] || cmd rm -f "/var/run/wireguard/$REAL_INTERFACE.sock"
+ cmd rm -f "/var/run/wireguard/$INTERFACE.name"
+}
+
+up_if() {
+ cmd ifconfig "$REAL_INTERFACE" up
+}
+
+add_addr() {
+ if [[ $1 == *:* ]]; then
+ cmd ifconfig "$REAL_INTERFACE" inet6 "$1" alias
+ else
+ cmd ifconfig "$REAL_INTERFACE" inet "$1" "${1%%/*}" alias
+ fi
+}
+
+set_mtu() {
+ # TODO: use better set_mtu algorithm from freebsd.bash
+ local mtu=0 current_mtu=-1 destination netif defaultif
+ if [[ -n $MTU ]]; then
+ cmd ifconfig "$REAL_INTERFACE" mtu "$MTU"
+ return
+ fi
+ while read -r destination _ _ _ _ netif _; do
+ if [[ $destination == default ]]; then
+ defaultif="$netif"
+ break
+ fi
+ done < <(netstat -nr -f inet)
+ [[ -n $defaultif && $(ifconfig "$defaultif") =~ mtu\ ([0-9]+) ]] && mtu="${BASH_REMATCH[1]}"
+ [[ $mtu -gt 0 ]] || mtu=1500
+ mtu=$(( mtu - 80 ))
+ [[ $(ifconfig "$REAL_INTERFACE") =~ mtu\ ([0-9]+) ]] && current_mtu="${BASH_REMATCH[1]}"
+ [[ $mtu -eq $current_mtu ]] || cmd ifconfig "$REAL_INTERFACE" mtu "$mtu"
+}
+
+collect_gateways() {
+ local destination gateway
+
+ GATEWAY4=""
+ while read -r destination gateway _; do
+ [[ $destination == default && $gateway != "link#"* ]] || continue
+ GATEWAY4="$gateway"
+ break
+ done < <(netstat -nr -f inet)
+
+ GATEWAY6=""
+ while read -r destination gateway _; do
+ [[ $destination == default && $gateway != "link#"* ]] || continue
+ GATEWAY6="$gateway"
+ break
+ done < <(netstat -nr -f inet6)
+}
+
+collect_endpoints() {
+ ENDPOINTS=( )
+ while read -r _ endpoint; do
+ [[ $endpoint =~ ^\[?([a-z0-9:.]+)\]?:[0-9]+$ ]] || continue
+ ENDPOINTS+=( "${BASH_REMATCH[1]}" )
+ done < <(wg show "$REAL_INTERFACE" endpoints)
+}
+
+declare -A SERVICE_DNS
+declare -A SERVICE_DNS_SEARCH
+collect_new_service_dns() {
+ local service get_response
+ local -A found_services
+ { read -r _ && while read -r service; do
+ [[ $service == "*"* ]] && service="${service:1}"
+ found_services["$service"]=1
+ [[ -n ${SERVICE_DNS["$service"]} ]] && continue
+ get_response="$(cmd networksetup -getdnsservers "$service")"
+ [[ $get_response == *" "* ]] && get_response="Empty"
+ [[ -n $get_response ]] && SERVICE_DNS["$service"]="$get_response"
+ get_response="$(cmd networksetup -getsearchdomains "$service")"
+ [[ $get_response == *" "* ]] && get_response="Empty"
+ [[ -n $get_response ]] && SERVICE_DNS_SEARCH["$service"]="$get_response"
+ done; } < <(networksetup -listallnetworkservices)
+
+ for service in "${!SERVICE_DNS[@]}"; do
+ if ! [[ -n ${found_services["$service"]} ]]; then
+ unset SERVICE_DNS["$service"]
+ unset SERVICE_DNS_SEARCH["$service"]
+ fi
+ done
+}
+
+set_endpoint_direct_route() {
+ local old_endpoints endpoint old_gateway4 old_gateway6 remove_all_old=0 added=( )
+ old_endpoints=( "${ENDPOINTS[@]}" )
+ old_gateway4="$GATEWAY4"
+ old_gateway6="$GATEWAY6"
+ collect_gateways
+ collect_endpoints
+
+ [[ $old_gateway4 != "$GATEWAY4" || $old_gateway6 != "$GATEWAY6" ]] && remove_all_old=1
+
+ if [[ $remove_all_old -eq 1 ]]; then
+ for endpoint in "${ENDPOINTS[@]}"; do
+ [[ " ${old_endpoints[*]} " == *" $endpoint "* ]] || old_endpoints+=( "$endpoint" )
+ done
+ fi
+
+ for endpoint in "${old_endpoints[@]}"; do
+ [[ $remove_all_old -eq 0 && " ${ENDPOINTS[*]} " == *" $endpoint "* ]] && continue
+ if [[ $endpoint == *:* && $AUTO_ROUTE6 -eq 1 ]]; then
+ cmd route -q -n delete -inet6 "$endpoint" >/dev/null 2>&1 || true
+ elif [[ $AUTO_ROUTE4 -eq 1 ]]; then
+ cmd route -q -n delete -inet "$endpoint" >/dev/null 2>&1 || true
+ fi
+ done
+
+ for endpoint in "${ENDPOINTS[@]}"; do
+ if [[ $remove_all_old -eq 0 && " ${old_endpoints[*]} " == *" $endpoint "* ]]; then
+ added+=( "$endpoint" )
+ continue
+ fi
+ if [[ $endpoint == *:* && $AUTO_ROUTE6 -eq 1 ]]; then
+ if [[ -n $GATEWAY6 ]]; then
+ cmd route -q -n add -inet6 "$endpoint" -gateway "$GATEWAY6" >/dev/null || true
+ else
+ # Prevent routing loop
+ cmd route -q -n add -inet6 "$endpoint" ::1 -blackhole >/dev/null || true
+ fi
+ added+=( "$endpoint" )
+ elif [[ $AUTO_ROUTE4 -eq 1 ]]; then
+ if [[ -n $GATEWAY4 ]]; then
+ cmd route -q -n add -inet "$endpoint" -gateway "$GATEWAY4" >/dev/null || true
+ else
+ # Prevent routing loop
+ cmd route -q -n add -inet "$endpoint" 127.0.0.1 -blackhole >/dev/null || true
+ fi
+ added+=( "$endpoint" )
+ fi
+ done
+ ENDPOINTS=( "${added[@]}" )
+}
+
+set_dns() {
+ collect_new_service_dns
+ local service response
+ for service in "${!SERVICE_DNS[@]}"; do
+ while read -r response; do
+ [[ $response == *Error* ]] && echo "$response" >&2
+ done < <(
+ cmd networksetup -setdnsservers "$service" "${DNS[@]}"
+ if [[ ${#DNS_SEARCH[@]} -eq 0 ]]; then
+ cmd networksetup -setsearchdomains "$service" Empty
+ else
+ cmd networksetup -setsearchdomains "$service" "${DNS_SEARCH[@]}"
+ fi
+ )
+ done
+}
+
+del_dns() {
+ local service response
+ for service in "${!SERVICE_DNS[@]}"; do
+ while read -r response; do
+ [[ $response == *Error* ]] && echo "$response" >&2
+ done < <(
+ cmd networksetup -setdnsservers "$service" ${SERVICE_DNS["$service"]} || true
+ cmd networksetup -setsearchdomains "$service" ${SERVICE_DNS_SEARCH["$service"]} || true
+ )
+ done
+}
+
+monitor_daemon() {
+ echo "[+] Backgrounding route monitor" >&2
+ (trap 'del_routes; del_dns; exit 0' INT TERM EXIT
+ exec >/dev/null 2>&1
+ exec 19< <(exec route -n monitor)
+ local event bpid=$BASHPID mpid=$!
+ [[ ${#DNS[@]} -gt 0 ]] && trap set_dns ALRM
+ # TODO: this should also check to see if the endpoint actually changes
+ # in response to incoming packets, and then call set_endpoint_direct_route
+ # then too. That function should be able to gracefully cleanup if the
+ # endpoints change.
+ while read -u 19 -r event; do
+ [[ $event == RTM_* ]] || continue
+ ifconfig "$REAL_INTERFACE" >/dev/null 2>&1 || break
+ [[ $AUTO_ROUTE4 -eq 1 || $AUTO_ROUTE6 -eq 1 ]] && set_endpoint_direct_route
+ [[ -z $MTU ]] && set_mtu
+ if [[ ${#DNS[@]} -gt 0 ]]; then
+ set_dns
+ sleep 2 && kill -ALRM $bpid 2>/dev/null &
+ fi
+ done
+ kill $mpid) &
+ [[ -n $LAUNCHED_BY_LAUNCHD ]] || disown
+}
+
+add_route() {
+ [[ $TABLE != off ]] || return 0
+
+ local family=inet
+ [[ $1 == *:* ]] && family=inet6
+
+ if [[ $1 == */0 && ( -z $TABLE || $TABLE == auto ) ]]; then
+ if [[ $1 == *:* ]]; then
+ AUTO_ROUTE6=1
+ cmd route -q -n add -inet6 ::/1 -interface "$REAL_INTERFACE" >/dev/null
+ cmd route -q -n add -inet6 8000::/1 -interface "$REAL_INTERFACE" >/dev/null
+ else
+ AUTO_ROUTE4=1
+ cmd route -q -n add -inet 0.0.0.0/1 -interface "$REAL_INTERFACE" >/dev/null
+ cmd route -q -n add -inet 128.0.0.0/1 -interface "$REAL_INTERFACE" >/dev/null
+ fi
+ else
+ [[ $TABLE == main || $TABLE == auto || -z $TABLE ]] || die "Darwin only supports TABLE=auto|main|off"
+ [[ $(route -n get "-$family" "$1" 2>/dev/null) =~ interface:\ $REAL_INTERFACE$'\n' ]] || cmd route -q -n add -$family "$1" -interface "$REAL_INTERFACE" >/dev/null
+
+ fi
+}
+
+set_config() {
+ cmd wg setconf "$REAL_INTERFACE" <(echo "$WG_CONFIG")
+}
+
+save_config() {
+ local old_umask new_config current_config address cmd
+ new_config=$'[Interface]\n'
+ while read -r address; do
+ [[ $address =~ inet6?\ ([^ ]+) ]] && new_config+="Address = ${BASH_REMATCH[1]}"$'\n'
+ done < <(ifconfig "$REAL_INTERFACE")
+ # TODO: actually determine current DNS for interface
+ for address in "${DNS[@]}"; do
+ new_config+="DNS = $address"$'\n'
+ done
+ [[ -n $MTU ]] && new_config+="MTU = $MTU"$'\n'
+ [[ -n $TABLE ]] && new_config+="Table = $TABLE"$'\n'
+ [[ $SAVE_CONFIG -eq 0 ]] || new_config+=$'SaveConfig = true\n'
+ for cmd in "${PRE_UP[@]}"; do
+ new_config+="PreUp = $cmd"$'\n'
+ done
+ for cmd in "${POST_UP[@]}"; do
+ new_config+="PostUp = $cmd"$'\n'
+ done
+ for cmd in "${PRE_DOWN[@]}"; do
+ new_config+="PreDown = $cmd"$'\n'
+ done
+ for cmd in "${POST_DOWN[@]}"; do
+ new_config+="PostDown = $cmd"$'\n'
+ done
+ old_umask="$(umask)"
+ umask 077
+ current_config="$(cmd wg showconf "$REAL_INTERFACE")"
+ trap 'rm -f "$CONFIG_FILE.tmp"; exit' INT TERM EXIT
+ echo "${current_config/\[Interface\]$'\n'/$new_config}" > "$CONFIG_FILE.tmp" || die "Could not write configuration file"
+ sync "$CONFIG_FILE.tmp"
+ mv "$CONFIG_FILE.tmp" "$CONFIG_FILE" || die "Could not move configuration file"
+ trap - INT TERM EXIT
+ umask "$old_umask"
+}
+
+execute_hooks() {
+ local hook
+ for hook in "$@"; do
+ hook="${hook//%i/$REAL_INTERFACE}"
+ hook="${hook//%I/$INTERFACE}"
+ echo "[#] $hook" >&2
+ (eval "$hook")
+ done
+}
+
+cmd_usage() {
+ cat >&2 <<-_EOF
+ Usage: $PROGRAM [ up | down | save | strip ] [ CONFIG_FILE | INTERFACE ]
+
+ CONFIG_FILE is a configuration file, whose filename is the interface name
+ followed by \`.conf'. Otherwise, INTERFACE is an interface name, with
+ configuration found at:
+ ${CONFIG_SEARCH_PATHS[@]/%//INTERFACE.conf}.
+ It is to be readable by wg(8)'s \`setconf' sub-command, with the exception
+ of the following additions to the [Interface] section, which are handled
+ by $PROGRAM:
+
+ - Address: may be specified one or more times and contains one or more
+ IP addresses (with an optional CIDR mask) to be set for the interface.
+ - DNS: an optional DNS server to use while the device is up.
+ - MTU: an optional MTU for the interface; if unspecified, auto-calculated.
+ - Table: an optional routing table to which routes will be added; if
+ unspecified or \`auto', the default table is used. If \`off', no routes
+ are added. Besides \`auto' and \`off', only \`main' is supported on
+ this platform.
+ - PreUp, PostUp, PreDown, PostDown: script snippets which will be executed
+ by bash(1) at the corresponding phases of the link, most commonly used
+ to configure DNS. The string \`%i' is expanded to INTERFACE.
+ - SaveConfig: if set to \`true', the configuration is saved from the current
+ state of the interface upon shutdown.
+
+ See wg-quick(8) for more info and examples.
+ _EOF
+}
+
+cmd_up() {
+ local i
+ get_real_interface && die "\`$INTERFACE' already exists as \`$REAL_INTERFACE'"
+ trap 'del_if; del_routes; exit' INT TERM EXIT
+ execute_hooks "${PRE_UP[@]}"
+ add_if
+ set_config
+ for i in "${ADDRESSES[@]}"; do
+ add_addr "$i"
+ done
+ set_mtu
+ up_if
+ for i in $(while read -r _ i; do for i in $i; do [[ $i =~ ^[0-9a-z:.]+/[0-9]+$ ]] && echo "$i"; done; done < <(wg show "$REAL_INTERFACE" allowed-ips) | sort -nr -k 2 -t /); do
+ add_route "$i"
+ done
+ [[ $AUTO_ROUTE4 -eq 1 || $AUTO_ROUTE6 -eq 1 ]] && set_endpoint_direct_route
+ [[ ${#DNS[@]} -gt 0 ]] && set_dns
+ monitor_daemon
+ execute_hooks "${POST_UP[@]}"
+ trap - INT TERM EXIT
+}
+
+cmd_down() {
+ if ! get_real_interface || [[ " $(wg show interfaces) " != *" $REAL_INTERFACE "* ]]; then
+ die "\`$INTERFACE' is not a WireGuard interface"
+ fi
+ execute_hooks "${PRE_DOWN[@]}"
+ [[ $SAVE_CONFIG -eq 0 ]] || save_config
+ del_if
+ execute_hooks "${POST_DOWN[@]}"
+}
+
+cmd_save() {
+ if ! get_real_interface || [[ " $(wg show interfaces) " != *" $REAL_INTERFACE "* ]]; then
+ die "\`$INTERFACE' is not a WireGuard interface"
+ fi
+ save_config
+}
+
+cmd_strip() {
+ echo "$WG_CONFIG"
+}
+
+# ~~ function override insertion point ~~
+
+if [[ $# -eq 1 && ( $1 == --help || $1 == -h || $1 == help ) ]]; then
+ cmd_usage
+elif [[ $# -eq 2 && $1 == up ]]; then
+ auto_su
+ detect_launchd
+ parse_options "$2"
+ cmd_up
+elif [[ $# -eq 2 && $1 == down ]]; then
+ auto_su
+ parse_options "$2"
+ cmd_down
+elif [[ $# -eq 2 && $1 == save ]]; then
+ auto_su
+ parse_options "$2"
+ cmd_save
+elif [[ $# -eq 2 && $1 == strip ]]; then
+ auto_su
+ parse_options "$2"
+ cmd_strip
+else
+ cmd_usage
+ exit 1
+fi
+
+[[ -n $LAUNCHED_BY_LAUNCHD ]] && wait
+
+exit 0
diff --git a/contrib/wireguard-tools/src/wg-quick/freebsd.bash b/contrib/wireguard-tools/src/wg-quick/freebsd.bash
new file mode 100755
--- /dev/null
+++ b/contrib/wireguard-tools/src/wg-quick/freebsd.bash
@@ -0,0 +1,487 @@
+#!/usr/local/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+#
+# Copyright (C) 2015-2020 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
+#
+
+set -e -o pipefail
+shopt -s extglob
+export LC_ALL=C
+
+exec 3>&2
+SELF="$(readlink -f "${BASH_SOURCE[0]}")"
+export PATH="${SELF%/*}:$PATH"
+
+WG_CONFIG=""
+INTERFACE=""
+ADDRESSES=( )
+MTU=""
+DNS=( )
+DNS_SEARCH=( )
+TABLE=""
+PRE_UP=( )
+POST_UP=( )
+PRE_DOWN=( )
+POST_DOWN=( )
+SAVE_CONFIG=0
+CONFIG_FILE=""
+PROGRAM="${0##*/}"
+ARGS=( "$@" )
+
+cmd() {
+ echo "[#] $*" >&3
+ "$@"
+}
+
+die() {
+ echo "$PROGRAM: $*" >&2
+ exit 1
+}
+
+CONFIG_SEARCH_PATHS=( /etc/wireguard /usr/local/etc/wireguard )
+
+unset ORIGINAL_TMPDIR
+make_temp() {
+ local old_umask
+
+ [[ -v ORIGINAL_TMPDIR ]] && export TMPDIR="$ORIGINAL_TMPDIR"
+ ORIGINAL_TMPDIR="$TMPDIR"
+ [[ -z $TMPDIR ]] && unset TMPDIR
+
+ old_umask="$(umask)"
+ umask 077
+ export TMPDIR="$(mktemp -d)"
+ umask "$old_umask"
+
+ [[ -d $TMPDIR ]] || die "Unable to create safe temporary directory"
+ CLEANUP_TMPDIR="$TMPDIR"
+}
+
+clean_temp() {
+ [[ -n $CLEANUP_TMPDIR ]] && rm -rf "$CLEANUP_TMPDIR"
+}
+
+parse_options() {
+ local interface_section=0 line key value stripped path v
+ CONFIG_FILE="$1"
+ if [[ $CONFIG_FILE =~ ^[a-zA-Z0-9_=+.-]{1,15}$ ]]; then
+ for path in "${CONFIG_SEARCH_PATHS[@]}"; do
+ CONFIG_FILE="$path/$1.conf"
+ [[ -e $CONFIG_FILE ]] && break
+ done
+ fi
+ [[ -e $CONFIG_FILE ]] || die "\`$CONFIG_FILE' does not exist"
+ [[ $CONFIG_FILE =~ (^|/)([a-zA-Z0-9_=+.-]{1,15})\.conf$ ]] || die "The config file must be a valid interface name, followed by .conf"
+ CONFIG_FILE="$(readlink -f "$CONFIG_FILE")"
+ ((($(stat -f '0%#p' "$CONFIG_FILE") & $(stat -f '0%#p' "${CONFIG_FILE%/*}") & 0007) == 0)) || echo "Warning: \`$CONFIG_FILE' is world accessible" >&2
+ INTERFACE="${BASH_REMATCH[2]}"
+ shopt -s nocasematch
+ while read -r line || [[ -n $line ]]; do
+ stripped="${line%%\#*}"
+ key="${stripped%%=*}"; key="${key##*([[:space:]])}"; key="${key%%*([[:space:]])}"
+ value="${stripped#*=}"; value="${value##*([[:space:]])}"; value="${value%%*([[:space:]])}"
+ [[ $key == "["* ]] && interface_section=0
+ [[ $key == "[Interface]" ]] && interface_section=1
+ if [[ $interface_section -eq 1 ]]; then
+ case "$key" in
+ Address) ADDRESSES+=( ${value//,/ } ); continue ;;
+ MTU) MTU="$value"; continue ;;
+ DNS) for v in ${value//,/ }; do
+ [[ $v =~ (^[0-9.]+$)|(^.*:.*$) ]] && DNS+=( $v ) || DNS_SEARCH+=( $v )
+ done; continue ;;
+ Table) TABLE="$value"; continue ;;
+ PreUp) PRE_UP+=( "$value" ); continue ;;
+ PreDown) PRE_DOWN+=( "$value" ); continue ;;
+ PostUp) POST_UP+=( "$value" ); continue ;;
+ PostDown) POST_DOWN+=( "$value" ); continue ;;
+ SaveConfig) read_bool SAVE_CONFIG "$value"; continue ;;
+ esac
+ fi
+ WG_CONFIG+="$line"$'\n'
+ done < "$CONFIG_FILE"
+ shopt -u nocasematch
+}
+
+read_bool() {
+ case "$2" in
+ true) printf -v "$1" 1 ;;
+ false) printf -v "$1" 0 ;;
+ *) die "\`$2' is neither true nor false"
+ esac
+}
+
+auto_su() {
+ [[ $UID == 0 ]] || exec sudo -p "$PROGRAM must be run as root. Please enter the password for %u to continue: " -- "$BASH" -- "$SELF" "${ARGS[@]}"
+}
+
+add_if() {
+ local ret rc
+ if ret="$(cmd ifconfig wg create name "$INTERFACE" 2>&1 >/dev/null)"; then
+ return 0
+ fi
+ rc=$?
+ if [[ $ret == *"ifconfig: ioctl SIOCSIFNAME (set name): File exists"* ]]; then
+ echo "$ret" >&3
+ return $rc
+ fi
+ echo "[!] Missing WireGuard kernel support ($ret). Falling back to slow userspace implementation." >&3
+ cmd "${WG_QUICK_USERSPACE_IMPLEMENTATION:-wireguard-go}" "$INTERFACE"
+}
+
+del_routes() {
+ local todelete=( ) destination gateway netif
+ while read -r destination _ _ _ _ netif _; do
+ [[ $netif == "$INTERFACE" ]] && todelete+=( "$destination" )
+ done < <(netstat -nr -f inet)
+ for destination in "${todelete[@]}"; do
+ cmd route -q -n delete -inet "$destination" || true
+ done
+ todelete=( )
+ while read -r destination gateway _ netif; do
+ [[ $netif == "$INTERFACE" || ( $netif == lo* && $gateway == "$INTERFACE" ) ]] && todelete+=( "$destination" )
+ done < <(netstat -nr -f inet6)
+ for destination in "${todelete[@]}"; do
+ cmd route -q -n delete -inet6 "$destination" || true
+ done
+ for destination in "${ENDPOINTS[@]}"; do
+ if [[ $destination == *:* ]]; then
+ cmd route -q -n delete -inet6 "$destination" || true
+ else
+ cmd route -q -n delete -inet "$destination" || true
+ fi
+ done
+}
+
+del_if() {
+ [[ $HAVE_SET_DNS -eq 0 ]] || unset_dns
+ if [[ -S /var/run/wireguard/$INTERFACE.sock ]]; then
+ cmd rm -f "/var/run/wireguard/$INTERFACE.sock"
+ else
+ cmd ifconfig "$INTERFACE" destroy
+ fi
+ while ifconfig "$INTERFACE" >/dev/null 2>&1; do
+ # HACK: it would be nice to `route monitor` here and wait for RTM_IFANNOUNCE
+ # but it turns out that the announcement is made before the interface
+ # disappears so we sometimes get a hang. So, we're instead left with polling
+ # in a sleep loop like this.
+ sleep 0.1
+ done
+}
+
+up_if() {
+ cmd ifconfig "$INTERFACE" up
+}
+
+add_addr() {
+ if [[ $1 == *:* ]]; then
+ cmd ifconfig "$INTERFACE" inet6 "$1" alias
+ else
+ cmd ifconfig "$INTERFACE" inet "$1" alias
+ fi
+}
+
+set_mtu() {
+ local mtu=0 endpoint output family
+ if [[ -n $MTU ]]; then
+ cmd ifconfig "$INTERFACE" mtu "$MTU"
+ return
+ fi
+ while read -r _ endpoint; do
+ [[ $endpoint =~ ^\[?([a-z0-9:.]+)\]?:[0-9]+$ ]] || continue
+ family=inet
+ [[ ${BASH_REMATCH[1]} == *:* ]] && family=inet6
+ output="$(route -n get "-$family" "${BASH_REMATCH[1]}" || true)"
+ [[ $output =~ interface:\ ([^ ]+)$'\n' && $(ifconfig "${BASH_REMATCH[1]}") =~ mtu\ ([0-9]+) && ${BASH_REMATCH[1]} -gt $mtu ]] && mtu="${BASH_REMATCH[1]}"
+ done < <(wg show "$INTERFACE" endpoints)
+ if [[ $mtu -eq 0 ]]; then
+ read -r output < <(route -n get default || true) || true
+ [[ $output =~ interface:\ ([^ ]+)$'\n' && $(ifconfig "${BASH_REMATCH[1]}") =~ mtu\ ([0-9]+) && ${BASH_REMATCH[1]} -gt $mtu ]] && mtu="${BASH_REMATCH[1]}"
+ fi
+ [[ $mtu -gt 0 ]] || mtu=1500
+ cmd ifconfig "$INTERFACE" mtu $(( mtu - 80 ))
+}
+
+
+collect_gateways() {
+ local destination gateway
+
+ GATEWAY4=""
+ while read -r destination gateway _; do
+ [[ $destination == default ]] || continue
+ GATEWAY4="$gateway"
+ break
+ done < <(netstat -nr -f inet)
+
+ GATEWAY6=""
+ while read -r destination gateway _; do
+ [[ $destination == default ]] || continue
+ GATEWAY6="$gateway"
+ break
+ done < <(netstat -nr -f inet6)
+}
+
+collect_endpoints() {
+ ENDPOINTS=( )
+ while read -r _ endpoint; do
+ [[ $endpoint =~ ^\[?([a-z0-9:.]+)\]?:[0-9]+$ ]] || continue
+ ENDPOINTS+=( "${BASH_REMATCH[1]}" )
+ done < <(wg show "$INTERFACE" endpoints)
+}
+
+set_endpoint_direct_route() {
+ local old_endpoints endpoint old_gateway4 old_gateway6 remove_all_old=0 added=( )
+ old_endpoints=( "${ENDPOINTS[@]}" )
+ old_gateway4="$GATEWAY4"
+ old_gateway6="$GATEWAY6"
+ collect_gateways
+ collect_endpoints
+
+ [[ $old_gateway4 != "$GATEWAY4" || $old_gateway6 != "$GATEWAY6" ]] && remove_all_old=1
+
+ if [[ $remove_all_old -eq 1 ]]; then
+ for endpoint in "${ENDPOINTS[@]}"; do
+ [[ " ${old_endpoints[*]} " == *" $endpoint "* ]] || old_endpoints+=( "$endpoint" )
+ done
+ fi
+
+ for endpoint in "${old_endpoints[@]}"; do
+ [[ $remove_all_old -eq 0 && " ${ENDPOINTS[*]} " == *" $endpoint "* ]] && continue
+ if [[ $endpoint == *:* && $AUTO_ROUTE6 -eq 1 ]]; then
+ cmd route -q -n delete -inet6 "$endpoint" 2>/dev/null || true
+ elif [[ $AUTO_ROUTE4 -eq 1 ]]; then
+ cmd route -q -n delete -inet "$endpoint" 2>/dev/null || true
+ fi
+ done
+
+ for endpoint in "${ENDPOINTS[@]}"; do
+ if [[ $remove_all_old -eq 0 && " ${old_endpoints[*]} " == *" $endpoint "* ]]; then
+ added+=( "$endpoint" )
+ continue
+ fi
+ if [[ $endpoint == *:* && $AUTO_ROUTE6 -eq 1 ]]; then
+ if [[ -n $GATEWAY6 ]]; then
+ cmd route -q -n add -inet6 "$endpoint" -gateway "$GATEWAY6" || true
+ else
+ # Prevent routing loop
+ cmd route -q -n add -inet6 "$endpoint" ::1 -blackhole || true
+ fi
+ added+=( "$endpoint" )
+ elif [[ $AUTO_ROUTE4 -eq 1 ]]; then
+ if [[ -n $GATEWAY4 ]]; then
+ cmd route -q -n add -inet "$endpoint" -gateway "$GATEWAY4" || true
+ else
+ # Prevent routing loop
+ cmd route -q -n add -inet "$endpoint" 127.0.0.1 -blackhole || true
+ fi
+ added+=( "$endpoint" )
+ fi
+ done
+ ENDPOINTS=( "${added[@]}" )
+}
+
+monitor_daemon() {
+ echo "[+] Backgrounding route monitor" >&2
+ (make_temp
+ trap 'del_routes; clean_temp; exit 0' INT TERM EXIT
+ exec >/dev/null 2>&1
+ exec 19< <(exec route -n monitor)
+ local event pid=$!
+ # TODO: this should also check to see if the endpoint actually changes
+ # in response to incoming packets, and then call set_endpoint_direct_route
+ # then too. That function should be able to gracefully cleanup if the
+ # endpoints change.
+ while read -u 19 -r event; do
+ [[ $event == RTM_* ]] || continue
+ ifconfig "$INTERFACE" >/dev/null 2>&1 || break
+ [[ $AUTO_ROUTE4 -eq 1 || $AUTO_ROUTE6 -eq 1 ]] && set_endpoint_direct_route
+ # TODO: set the mtu as well, but only if up
+ done
+ kill $pid) & disown
+}
+
+HAVE_SET_DNS=0
+set_dns() {
+ [[ ${#DNS[@]} -gt 0 ]] || return 0
+ { printf 'nameserver %s\n' "${DNS[@]}"
+ [[ ${#DNS_SEARCH[@]} -eq 0 ]] || printf 'search %s\n' "${DNS_SEARCH[*]}"
+ } | cmd resolvconf -a "$INTERFACE" -x
+ HAVE_SET_DNS=1
+}
+
+unset_dns() {
+ [[ ${#DNS[@]} -gt 0 ]] || return 0
+ cmd resolvconf -d "$INTERFACE"
+}
+
+add_route() {
+ [[ $TABLE != off ]] || return 0
+
+ local family=inet
+ [[ $1 == *:* ]] && family=inet6
+
+ if [[ -n $TABLE && $TABLE != auto ]]; then
+ cmd route -q -n add "-$family" -fib "$TABLE" "$1" -interface "$INTERFACE"
+ elif [[ $1 == */0 ]]; then
+ if [[ $1 == *:* ]]; then
+ AUTO_ROUTE6=1
+ cmd route -q -n add -inet6 ::/1 -interface "$INTERFACE"
+ cmd route -q -n add -inet6 8000::/1 -interface "$INTERFACE"
+ else
+ AUTO_ROUTE4=1
+ cmd route -q -n add -inet 0.0.0.0/1 -interface "$INTERFACE"
+ cmd route -q -n add -inet 128.0.0.0/1 -interface "$INTERFACE"
+ fi
+ else
+ [[ $(route -n get "-$family" "$1" 2>/dev/null) =~ interface:\ $INTERFACE$'\n' ]] || cmd route -q -n add "-$family" "$1" -interface "$INTERFACE"
+ fi
+}
+
+set_config() {
+ echo "$WG_CONFIG" | cmd wg setconf "$INTERFACE" /dev/stdin
+}
+
+save_config() {
+ local old_umask new_config current_config address cmd
+ new_config=$'[Interface]\n'
+ { read -r _; while read -r _ _ _ address _; do
+ new_config+="Address = $address"$'\n'
+ done } < <(netstat -I "$INTERFACE" -n -W -f inet)
+ { read -r _; while read -r _ _ _ address _; do
+ new_config+="Address = $address"$'\n'
+ done } < <(netstat -I "$INTERFACE" -n -W -f inet6)
+ while read -r address; do
+ [[ $address =~ ^nameserver\ ([a-zA-Z0-9_=+:%.-]+)$ ]] && new_config+="DNS = ${BASH_REMATCH[1]}"$'\n'
+ done < <(resolvconf -l "$INTERFACE" 2>/dev/null)
+ [[ -n $MTU ]] && new_config+="MTU = $MTU"$'\n'
+ [[ -n $TABLE ]] && new_config+="Table = $TABLE"$'\n'
+ [[ $SAVE_CONFIG -eq 0 ]] || new_config+=$'SaveConfig = true\n'
+ for cmd in "${PRE_UP[@]}"; do
+ new_config+="PreUp = $cmd"$'\n'
+ done
+ for cmd in "${POST_UP[@]}"; do
+ new_config+="PostUp = $cmd"$'\n'
+ done
+ for cmd in "${PRE_DOWN[@]}"; do
+ new_config+="PreDown = $cmd"$'\n'
+ done
+ for cmd in "${POST_DOWN[@]}"; do
+ new_config+="PostDown = $cmd"$'\n'
+ done
+ old_umask="$(umask)"
+ umask 077
+ current_config="$(cmd wg showconf "$INTERFACE")"
+ trap 'rm -f "$CONFIG_FILE.tmp"; clean_temp; exit' INT TERM EXIT
+ echo "${current_config/\[Interface\]$'\n'/$new_config}" > "$CONFIG_FILE.tmp" || die "Could not write configuration file"
+ sync "$CONFIG_FILE.tmp"
+ mv "$CONFIG_FILE.tmp" "$CONFIG_FILE" || die "Could not move configuration file"
+ trap 'clean_temp; exit' INT TERM EXIT
+ umask "$old_umask"
+}
+
+execute_hooks() {
+ local hook
+ for hook in "$@"; do
+ hook="${hook//%i/$INTERFACE}"
+ echo "[#] $hook" >&2
+ (eval "$hook")
+ done
+}
+
+cmd_usage() {
+ cat >&2 <<-_EOF
+ Usage: $PROGRAM [ up | down | save | strip ] [ CONFIG_FILE | INTERFACE ]
+
+ CONFIG_FILE is a configuration file, whose filename is the interface name
+ followed by \`.conf'. Otherwise, INTERFACE is an interface name, with
+ configuration found at:
+ ${CONFIG_SEARCH_PATHS[@]/%//INTERFACE.conf}.
+ It is to be readable by wg(8)'s \`setconf' sub-command, with the exception
+ of the following additions to the [Interface] section, which are handled
+ by $PROGRAM:
+
+ - Address: may be specified one or more times and contains one or more
+ IP addresses (with an optional CIDR mask) to be set for the interface.
+ - DNS: an optional DNS server to use while the device is up.
+ - MTU: an optional MTU for the interface; if unspecified, auto-calculated.
+ - Table: an optional routing table to which routes will be added; if
+ unspecified or \`auto', the default table is used. If \`off', no routes
+ are added.
+ - PreUp, PostUp, PreDown, PostDown: script snippets which will be executed
+ by bash(1) at the corresponding phases of the link, most commonly used
+ to configure DNS. The string \`%i' is expanded to INTERFACE.
+ - SaveConfig: if set to \`true', the configuration is saved from the current
+ state of the interface upon shutdown.
+
+ See wg-quick(8) for more info and examples.
+ _EOF
+}
+
+cmd_up() {
+ local i
+ [[ -z $(ifconfig "$INTERFACE" 2>/dev/null) ]] || die "\`$INTERFACE' already exists"
+ trap 'del_if; del_routes; clean_temp; exit' INT TERM EXIT
+ execute_hooks "${PRE_UP[@]}"
+ add_if
+ set_config
+ for i in "${ADDRESSES[@]}"; do
+ add_addr "$i"
+ done
+ set_mtu
+ up_if
+ set_dns
+ for i in $(while read -r _ i; do for i in $i; do [[ $i =~ ^[0-9a-z:.]+/[0-9]+$ ]] && echo "$i"; done; done < <(wg show "$INTERFACE" allowed-ips) | sort -nr -k 2 -t /); do
+ add_route "$i"
+ done
+ [[ $AUTO_ROUTE4 -eq 1 || $AUTO_ROUTE6 -eq 1 ]] && set_endpoint_direct_route
+ monitor_daemon
+ execute_hooks "${POST_UP[@]}"
+ trap 'clean_temp; exit' INT TERM EXIT
+}
+
+cmd_down() {
+ [[ " $(wg show interfaces) " == *" $INTERFACE "* ]] || die "\`$INTERFACE' is not a WireGuard interface"
+ execute_hooks "${PRE_DOWN[@]}"
+ [[ $SAVE_CONFIG -eq 0 ]] || save_config
+ del_if
+ unset_dns
+ execute_hooks "${POST_DOWN[@]}"
+}
+
+cmd_save() {
+ [[ " $(wg show interfaces) " == *" $INTERFACE "* ]] || die "\`$INTERFACE' is not a WireGuard interface"
+ save_config
+}
+
+cmd_strip() {
+ echo "$WG_CONFIG"
+}
+
+# ~~ function override insertion point ~~
+
+make_temp
+trap 'clean_temp; exit' INT TERM EXIT
+
+if [[ $# -eq 1 && ( $1 == --help || $1 == -h || $1 == help ) ]]; then
+ cmd_usage
+elif [[ $# -eq 2 && $1 == up ]]; then
+ auto_su
+ parse_options "$2"
+ cmd_up
+elif [[ $# -eq 2 && $1 == down ]]; then
+ auto_su
+ parse_options "$2"
+ cmd_down
+elif [[ $# -eq 2 && $1 == save ]]; then
+ auto_su
+ parse_options "$2"
+ cmd_save
+elif [[ $# -eq 2 && $1 == strip ]]; then
+ auto_su
+ parse_options "$2"
+ cmd_strip
+else
+ cmd_usage
+ exit 1
+fi
+
+exit 0
diff --git a/contrib/wireguard-tools/src/wg-quick/linux.bash b/contrib/wireguard-tools/src/wg-quick/linux.bash
new file mode 100755
--- /dev/null
+++ b/contrib/wireguard-tools/src/wg-quick/linux.bash
@@ -0,0 +1,389 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+#
+# Copyright (C) 2015-2020 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
+#
+
+set -e -o pipefail
+shopt -s extglob
+export LC_ALL=C
+
+SELF="$(readlink -f "${BASH_SOURCE[0]}")"
+export PATH="${SELF%/*}:$PATH"
+
+WG_CONFIG=""
+INTERFACE=""
+ADDRESSES=( )
+MTU=""
+DNS=( )
+DNS_SEARCH=( )
+TABLE=""
+PRE_UP=( )
+POST_UP=( )
+PRE_DOWN=( )
+POST_DOWN=( )
+SAVE_CONFIG=0
+CONFIG_FILE=""
+PROGRAM="${0##*/}"
+ARGS=( "$@" )
+
+cmd() {
+ echo "[#] $*" >&2
+ "$@"
+}
+
+die() {
+ echo "$PROGRAM: $*" >&2
+ exit 1
+}
+
+parse_options() {
+ local interface_section=0 line key value stripped v
+ CONFIG_FILE="$1"
+ [[ $CONFIG_FILE =~ ^[a-zA-Z0-9_=+.-]{1,15}$ ]] && CONFIG_FILE="/etc/wireguard/$CONFIG_FILE.conf"
+ [[ -e $CONFIG_FILE ]] || die "\`$CONFIG_FILE' does not exist"
+ [[ $CONFIG_FILE =~ (^|/)([a-zA-Z0-9_=+.-]{1,15})\.conf$ ]] || die "The config file must be a valid interface name, followed by .conf"
+ CONFIG_FILE="$(readlink -f "$CONFIG_FILE")"
+ ((($(stat -c '0%#a' "$CONFIG_FILE") & $(stat -c '0%#a' "${CONFIG_FILE%/*}") & 0007) == 0)) || echo "Warning: \`$CONFIG_FILE' is world accessible" >&2
+ INTERFACE="${BASH_REMATCH[2]}"
+ shopt -s nocasematch
+ while read -r line || [[ -n $line ]]; do
+ stripped="${line%%\#*}"
+ key="${stripped%%=*}"; key="${key##*([[:space:]])}"; key="${key%%*([[:space:]])}"
+ value="${stripped#*=}"; value="${value##*([[:space:]])}"; value="${value%%*([[:space:]])}"
+ [[ $key == "["* ]] && interface_section=0
+ [[ $key == "[Interface]" ]] && interface_section=1
+ if [[ $interface_section -eq 1 ]]; then
+ case "$key" in
+ Address) ADDRESSES+=( ${value//,/ } ); continue ;;
+ MTU) MTU="$value"; continue ;;
+ DNS) for v in ${value//,/ }; do
+ [[ $v =~ (^[0-9.]+$)|(^.*:.*$) ]] && DNS+=( $v ) || DNS_SEARCH+=( $v )
+ done; continue ;;
+ Table) TABLE="$value"; continue ;;
+ PreUp) PRE_UP+=( "$value" ); continue ;;
+ PreDown) PRE_DOWN+=( "$value" ); continue ;;
+ PostUp) POST_UP+=( "$value" ); continue ;;
+ PostDown) POST_DOWN+=( "$value" ); continue ;;
+ SaveConfig) read_bool SAVE_CONFIG "$value"; continue ;;
+ esac
+ fi
+ WG_CONFIG+="$line"$'\n'
+ done < "$CONFIG_FILE"
+ shopt -u nocasematch
+}
+
+read_bool() {
+ case "$2" in
+ true) printf -v "$1" 1 ;;
+ false) printf -v "$1" 0 ;;
+ *) die "\`$2' is neither true nor false"
+ esac
+}
+
+auto_su() {
+ [[ $UID == 0 ]] || exec sudo -p "$PROGRAM must be run as root. Please enter the password for %u to continue: " -- "$BASH" -- "$SELF" "${ARGS[@]}"
+}
+
+add_if() {
+ local ret
+ if ! cmd ip link add "$INTERFACE" type wireguard; then
+ ret=$?
+ [[ -e /sys/module/wireguard ]] || ! command -v "${WG_QUICK_USERSPACE_IMPLEMENTATION:-wireguard-go}" >/dev/null && exit $ret
+ echo "[!] Missing WireGuard kernel module. Falling back to slow userspace implementation." >&2
+ cmd "${WG_QUICK_USERSPACE_IMPLEMENTATION:-wireguard-go}" "$INTERFACE"
+ fi
+}
+
+del_if() {
+ local table
+ [[ $HAVE_SET_DNS -eq 0 ]] || unset_dns
+ [[ $HAVE_SET_FIREWALL -eq 0 ]] || remove_firewall
+ if [[ -z $TABLE || $TABLE == auto ]] && get_fwmark table && [[ $(wg show "$INTERFACE" allowed-ips) =~ /0(\ |$'\n'|$) ]]; then
+ while [[ $(ip -4 rule show 2>/dev/null) == *"lookup $table"* ]]; do
+ cmd ip -4 rule delete table $table
+ done
+ while [[ $(ip -4 rule show 2>/dev/null) == *"from all lookup main suppress_prefixlength 0"* ]]; do
+ cmd ip -4 rule delete table main suppress_prefixlength 0
+ done
+ while [[ $(ip -6 rule show 2>/dev/null) == *"lookup $table"* ]]; do
+ cmd ip -6 rule delete table $table
+ done
+ while [[ $(ip -6 rule show 2>/dev/null) == *"from all lookup main suppress_prefixlength 0"* ]]; do
+ cmd ip -6 rule delete table main suppress_prefixlength 0
+ done
+ fi
+ cmd ip link delete dev "$INTERFACE"
+}
+
+add_addr() {
+ local proto=-4
+ [[ $1 == *:* ]] && proto=-6
+ cmd ip $proto address add "$1" dev "$INTERFACE"
+}
+
+set_mtu_up() {
+ local mtu=0 endpoint output
+ if [[ -n $MTU ]]; then
+ cmd ip link set mtu "$MTU" up dev "$INTERFACE"
+ return
+ fi
+ while read -r _ endpoint; do
+ [[ $endpoint =~ ^\[?([a-z0-9:.]+)\]?:[0-9]+$ ]] || continue
+ output="$(ip route get "${BASH_REMATCH[1]}" || true)"
+ [[ ( $output =~ mtu\ ([0-9]+) || ( $output =~ dev\ ([^ ]+) && $(ip link show dev "${BASH_REMATCH[1]}") =~ mtu\ ([0-9]+) ) ) && ${BASH_REMATCH[1]} -gt $mtu ]] && mtu="${BASH_REMATCH[1]}"
+ done < <(wg show "$INTERFACE" endpoints)
+ if [[ $mtu -eq 0 ]]; then
+ read -r output < <(ip route show default || true) || true
+ [[ ( $output =~ mtu\ ([0-9]+) || ( $output =~ dev\ ([^ ]+) && $(ip link show dev "${BASH_REMATCH[1]}") =~ mtu\ ([0-9]+) ) ) && ${BASH_REMATCH[1]} -gt $mtu ]] && mtu="${BASH_REMATCH[1]}"
+ fi
+ [[ $mtu -gt 0 ]] || mtu=1500
+ cmd ip link set mtu $(( mtu - 80 )) up dev "$INTERFACE"
+}
+
+resolvconf_iface_prefix() {
+ [[ -f /etc/resolvconf/interface-order ]] || return 0
+ local iface
+ while read -r iface; do
+ [[ $iface =~ ^([A-Za-z0-9-]+)\*$ ]] || continue
+ echo "${BASH_REMATCH[1]}." && return 0
+ done < /etc/resolvconf/interface-order
+}
+
+HAVE_SET_DNS=0
+set_dns() {
+ [[ ${#DNS[@]} -gt 0 ]] || return 0
+ { printf 'nameserver %s\n' "${DNS[@]}"
+ [[ ${#DNS_SEARCH[@]} -eq 0 ]] || printf 'search %s\n' "${DNS_SEARCH[*]}"
+ } | cmd resolvconf -a "$(resolvconf_iface_prefix)$INTERFACE" -m 0 -x
+ HAVE_SET_DNS=1
+}
+
+unset_dns() {
+ [[ ${#DNS[@]} -gt 0 ]] || return 0
+ cmd resolvconf -d "$(resolvconf_iface_prefix)$INTERFACE" -f
+}
+
+add_route() {
+ local proto=-4
+ [[ $1 == *:* ]] && proto=-6
+ [[ $TABLE != off ]] || return 0
+
+ if [[ -n $TABLE && $TABLE != auto ]]; then
+ cmd ip $proto route add "$1" dev "$INTERFACE" table "$TABLE"
+ elif [[ $1 == */0 ]]; then
+ add_default "$1"
+ else
+ [[ -n $(ip $proto route show dev "$INTERFACE" match "$1" 2>/dev/null) ]] || cmd ip $proto route add "$1" dev "$INTERFACE"
+ fi
+}
+
+get_fwmark() {
+ local fwmark
+ fwmark="$(wg show "$INTERFACE" fwmark)" || return 1
+ [[ -n $fwmark && $fwmark != off ]] || return 1
+ printf -v "$1" "%d" "$fwmark"
+ return 0
+}
+
+remove_firewall() {
+ if type -p nft >/dev/null; then
+ local table nftcmd
+ while read -r table; do
+ [[ $table == *" wg-quick-$INTERFACE" ]] && printf -v nftcmd '%sdelete %s\n' "$nftcmd" "$table"
+ done < <(nft list tables 2>/dev/null)
+ [[ -z $nftcmd ]] || cmd nft -f <(echo -n "$nftcmd")
+ fi
+ if type -p iptables >/dev/null; then
+ local line iptables found restore
+ for iptables in iptables ip6tables; do
+ restore="" found=0
+ while read -r line; do
+ [[ $line == "*"* || $line == COMMIT || $line == "-A "*"-m comment --comment \"wg-quick(8) rule for $INTERFACE\""* ]] || continue
+ [[ $line == "-A"* ]] && found=1
+ printf -v restore '%s%s\n' "$restore" "${line/#-A/-D}"
+ done < <($iptables-save 2>/dev/null)
+ [[ $found -ne 1 ]] || echo -n "$restore" | cmd $iptables-restore -n
+ done
+ fi
+}
+
+HAVE_SET_FIREWALL=0
+add_default() {
+ local table line
+ if ! get_fwmark table; then
+ table=51820
+ while [[ -n $(ip -4 route show table $table 2>/dev/null) || -n $(ip -6 route show table $table 2>/dev/null) ]]; do
+ ((table++))
+ done
+ cmd wg set "$INTERFACE" fwmark $table
+ fi
+ local proto=-4 iptables=iptables pf=ip
+ [[ $1 == *:* ]] && proto=-6 iptables=ip6tables pf=ip6
+ cmd ip $proto rule add not fwmark $table table $table
+ cmd ip $proto rule add table main suppress_prefixlength 0
+ cmd ip $proto route add "$1" dev "$INTERFACE" table $table
+
+ local marker="-m comment --comment \"wg-quick(8) rule for $INTERFACE\"" restore=$'*raw\n' nftable="wg-quick-$INTERFACE" nftcmd
+ printf -v nftcmd '%sadd table %s %s\n' "$nftcmd" "$pf" "$nftable"
+ printf -v nftcmd '%sadd chain %s %s preraw { type filter hook prerouting priority -300; }\n' "$nftcmd" "$pf" "$nftable"
+ printf -v nftcmd '%sadd chain %s %s premangle { type filter hook prerouting priority -150; }\n' "$nftcmd" "$pf" "$nftable"
+ printf -v nftcmd '%sadd chain %s %s postmangle { type filter hook postrouting priority -150; }\n' "$nftcmd" "$pf" "$nftable"
+ while read -r line; do
+ [[ $line =~ .*inet6?\ ([0-9a-f:.]+)/[0-9]+.* ]] || continue
+ printf -v restore '%s-I PREROUTING ! -i %s -d %s -m addrtype ! --src-type LOCAL -j DROP %s\n' "$restore" "$INTERFACE" "${BASH_REMATCH[1]}" "$marker"
+ printf -v nftcmd '%sadd rule %s %s preraw iifname != "%s" %s daddr %s fib saddr type != local drop\n' "$nftcmd" "$pf" "$nftable" "$INTERFACE" "$pf" "${BASH_REMATCH[1]}"
+ done < <(ip -o $proto addr show dev "$INTERFACE" 2>/dev/null)
+ printf -v restore '%sCOMMIT\n*mangle\n-I POSTROUTING -m mark --mark %d -p udp -j CONNMARK --save-mark %s\n-I PREROUTING -p udp -j CONNMARK --restore-mark %s\nCOMMIT\n' "$restore" $table "$marker" "$marker"
+ printf -v nftcmd '%sadd rule %s %s postmangle meta l4proto udp mark %d ct mark set mark \n' "$nftcmd" "$pf" "$nftable" $table
+ printf -v nftcmd '%sadd rule %s %s premangle meta l4proto udp meta mark set ct mark \n' "$nftcmd" "$pf" "$nftable"
+ [[ $proto == -4 ]] && cmd sysctl -q net.ipv4.conf.all.src_valid_mark=1
+ if type -p nft >/dev/null; then
+ cmd nft -f <(echo -n "$nftcmd")
+ else
+ echo -n "$restore" | cmd $iptables-restore -n
+ fi
+ HAVE_SET_FIREWALL=1
+ return 0
+}
+
+set_config() {
+ cmd wg setconf "$INTERFACE" <(echo "$WG_CONFIG")
+}
+
+save_config() {
+ local old_umask new_config current_config address cmd
+ [[ $(ip -all -brief address show dev "$INTERFACE") =~ ^$INTERFACE\ +\ [A-Z]+\ +(.+)$ ]] || true
+ new_config=$'[Interface]\n'
+ for address in ${BASH_REMATCH[1]}; do
+ new_config+="Address = $address"$'\n'
+ done
+ while read -r address; do
+ [[ $address =~ ^nameserver\ ([a-zA-Z0-9_=+:%.-]+)$ ]] && new_config+="DNS = ${BASH_REMATCH[1]}"$'\n'
+ done < <(resolvconf -l "$(resolvconf_iface_prefix)$INTERFACE" 2>/dev/null || cat "/etc/resolvconf/run/interface/$(resolvconf_iface_prefix)$INTERFACE" 2>/dev/null)
+ [[ -n $MTU && $(ip link show dev "$INTERFACE") =~ mtu\ ([0-9]+) ]] && new_config+="MTU = ${BASH_REMATCH[1]}"$'\n'
+ [[ -n $TABLE ]] && new_config+="Table = $TABLE"$'\n'
+ [[ $SAVE_CONFIG -eq 0 ]] || new_config+=$'SaveConfig = true\n'
+ for cmd in "${PRE_UP[@]}"; do
+ new_config+="PreUp = $cmd"$'\n'
+ done
+ for cmd in "${POST_UP[@]}"; do
+ new_config+="PostUp = $cmd"$'\n'
+ done
+ for cmd in "${PRE_DOWN[@]}"; do
+ new_config+="PreDown = $cmd"$'\n'
+ done
+ for cmd in "${POST_DOWN[@]}"; do
+ new_config+="PostDown = $cmd"$'\n'
+ done
+ old_umask="$(umask)"
+ umask 077
+ current_config="$(cmd wg showconf "$INTERFACE")"
+ trap 'rm -f "$CONFIG_FILE.tmp"; exit' INT TERM EXIT
+ echo "${current_config/\[Interface\]$'\n'/$new_config}" > "$CONFIG_FILE.tmp" || die "Could not write configuration file"
+ sync "$CONFIG_FILE.tmp"
+ mv "$CONFIG_FILE.tmp" "$CONFIG_FILE" || die "Could not move configuration file"
+ trap - INT TERM EXIT
+ umask "$old_umask"
+}
+
+execute_hooks() {
+ local hook
+ for hook in "$@"; do
+ hook="${hook//%i/$INTERFACE}"
+ echo "[#] $hook" >&2
+ (eval "$hook")
+ done
+}
+
+cmd_usage() {
+ cat >&2 <<-_EOF
+ Usage: $PROGRAM [ up | down | save | strip ] [ CONFIG_FILE | INTERFACE ]
+
+ CONFIG_FILE is a configuration file, whose filename is the interface name
+ followed by \`.conf'. Otherwise, INTERFACE is an interface name, with
+ configuration found at /etc/wireguard/INTERFACE.conf. It is to be readable
+ by wg(8)'s \`setconf' sub-command, with the exception of the following additions
+ to the [Interface] section, which are handled by $PROGRAM:
+
+ - Address: may be specified one or more times and contains one or more
+ IP addresses (with an optional CIDR mask) to be set for the interface.
+ - DNS: an optional DNS server to use while the device is up.
+ - MTU: an optional MTU for the interface; if unspecified, auto-calculated.
+ - Table: an optional routing table to which routes will be added; if
+ unspecified or \`auto', the default table is used. If \`off', no routes
+ are added.
+ - PreUp, PostUp, PreDown, PostDown: script snippets which will be executed
+ by bash(1) at the corresponding phases of the link, most commonly used
+ to configure DNS. The string \`%i' is expanded to INTERFACE.
+ - SaveConfig: if set to \`true', the configuration is saved from the current
+ state of the interface upon shutdown.
+
+ See wg-quick(8) for more info and examples.
+ _EOF
+}
+
+cmd_up() {
+ local i
+ [[ -z $(ip link show dev "$INTERFACE" 2>/dev/null) ]] || die "\`$INTERFACE' already exists"
+ trap 'del_if; exit' INT TERM EXIT
+ execute_hooks "${PRE_UP[@]}"
+ add_if
+ set_config
+ for i in "${ADDRESSES[@]}"; do
+ add_addr "$i"
+ done
+ set_mtu_up
+ set_dns
+ for i in $(while read -r _ i; do for i in $i; do [[ $i =~ ^[0-9a-z:.]+/[0-9]+$ ]] && echo "$i"; done; done < <(wg show "$INTERFACE" allowed-ips) | sort -nr -k 2 -t /); do
+ add_route "$i"
+ done
+ execute_hooks "${POST_UP[@]}"
+ trap - INT TERM EXIT
+}
+
+cmd_down() {
+ [[ " $(wg show interfaces) " == *" $INTERFACE "* ]] || die "\`$INTERFACE' is not a WireGuard interface"
+ execute_hooks "${PRE_DOWN[@]}"
+ [[ $SAVE_CONFIG -eq 0 ]] || save_config
+ del_if
+ unset_dns || true
+ remove_firewall || true
+ execute_hooks "${POST_DOWN[@]}"
+}
+
+cmd_save() {
+ [[ " $(wg show interfaces) " == *" $INTERFACE "* ]] || die "\`$INTERFACE' is not a WireGuard interface"
+ save_config
+}
+
+cmd_strip() {
+ echo "$WG_CONFIG"
+}
+
+# ~~ function override insertion point ~~
+
+if [[ $# -eq 1 && ( $1 == --help || $1 == -h || $1 == help ) ]]; then
+ cmd_usage
+elif [[ $# -eq 2 && $1 == up ]]; then
+ auto_su
+ parse_options "$2"
+ cmd_up
+elif [[ $# -eq 2 && $1 == down ]]; then
+ auto_su
+ parse_options "$2"
+ cmd_down
+elif [[ $# -eq 2 && $1 == save ]]; then
+ auto_su
+ parse_options "$2"
+ cmd_save
+elif [[ $# -eq 2 && $1 == strip ]]; then
+ auto_su
+ parse_options "$2"
+ cmd_strip
+else
+ cmd_usage
+ exit 1
+fi
+
+exit 0
diff --git a/contrib/wireguard-tools/src/wg-quick/openbsd.bash b/contrib/wireguard-tools/src/wg-quick/openbsd.bash
new file mode 100755
--- /dev/null
+++ b/contrib/wireguard-tools/src/wg-quick/openbsd.bash
@@ -0,0 +1,481 @@
+#!/usr/local/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+#
+# Copyright (C) 2015-2020 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
+#
+
+set -e -o pipefail
+shopt -s extglob
+export LC_ALL=C
+
+exec 3>&2
+SELF="$(readlink -f "${BASH_SOURCE[0]}")"
+export PATH="${SELF%/*}:$PATH"
+
+WG_CONFIG=""
+INTERFACE=""
+ADDRESSES=( )
+MTU=""
+DNS=( )
+DNS_SEARCH=( )
+TABLE=""
+PRE_UP=( )
+POST_UP=( )
+PRE_DOWN=( )
+POST_DOWN=( )
+SAVE_CONFIG=0
+CONFIG_FILE=""
+PROGRAM="${0##*/}"
+ARGS=( "$@" )
+
+cmd() {
+ echo "[#] $*" >&3
+ "$@"
+}
+
+die() {
+ echo "$PROGRAM: $*" >&2
+ exit 1
+}
+
+parse_options() {
+ local interface_section=0 line key value stripped
+ CONFIG_FILE="$1"
+ [[ $CONFIG_FILE =~ ^[a-zA-Z0-9_=+.-]{1,15}$ ]] && CONFIG_FILE="/etc/wireguard/$CONFIG_FILE.conf"
+ [[ -e $CONFIG_FILE ]] || die "\`$CONFIG_FILE' does not exist"
+ [[ $CONFIG_FILE =~ (^|/)([a-zA-Z0-9_=+.-]{1,15})\.conf$ ]] || die "The config file must be a valid interface name, followed by .conf"
+ CONFIG_FILE="$(readlink -f "$CONFIG_FILE")"
+ ((($(stat -f '0%#p' "$CONFIG_FILE") & $(stat -f '0%#p' "${CONFIG_FILE%/*}") & 0007) == 0)) || echo "Warning: \`$CONFIG_FILE' is world accessible" >&2
+ INTERFACE="${BASH_REMATCH[2]}"
+ shopt -s nocasematch
+ while read -r line || [[ -n $line ]]; do
+ stripped="${line%%\#*}"
+ key="${stripped%%=*}"; key="${key##*([[:space:]])}"; key="${key%%*([[:space:]])}"
+ value="${stripped#*=}"; value="${value##*([[:space:]])}"; value="${value%%*([[:space:]])}"
+ [[ $key == "["* ]] && interface_section=0
+ [[ $key == "[Interface]" ]] && interface_section=1
+ if [[ $interface_section -eq 1 ]]; then
+ case "$key" in
+ Address) ADDRESSES+=( ${value//,/ } ); continue ;;
+ MTU) MTU="$value"; continue ;;
+ DNS) for v in ${value//,/ }; do
+ [[ $v =~ (^[0-9.]+$)|(^.*:.*$) ]] && DNS+=( $v ) || DNS_SEARCH+=( $v )
+ done; continue ;;
+ Table) TABLE="$value"; continue ;;
+ PreUp) PRE_UP+=( "$value" ); continue ;;
+ PreDown) PRE_DOWN+=( "$value" ); continue ;;
+ PostUp) POST_UP+=( "$value" ); continue ;;
+ PostDown) POST_DOWN+=( "$value" ); continue ;;
+ SaveConfig) read_bool SAVE_CONFIG "$value"; continue ;;
+ esac
+ fi
+ WG_CONFIG+="$line"$'\n'
+ done < "$CONFIG_FILE"
+ shopt -u nocasematch
+}
+
+read_bool() {
+ case "$2" in
+ true) printf -v "$1" 1 ;;
+ false) printf -v "$1" 0 ;;
+ *) die "\`$2' is neither true nor false"
+ esac
+}
+
+auto_su() {
+ [[ $UID == 0 ]] || exec doas -- "$BASH" -- "$SELF" "${ARGS[@]}"
+}
+
+
+get_real_interface() {
+ local interface line
+ while IFS= read -r line; do
+ if [[ $line =~ ^([a-z]+[0-9]+):\ .+ ]]; then
+ interface="${BASH_REMATCH[1]}"
+ continue
+ fi
+ if [[ $interface == wg* && $line =~ ^\ description:\ wg-quick:\ (.+) && ${BASH_REMATCH[1]} == "$INTERFACE" ]]; then
+ REAL_INTERFACE="$interface"
+ return 0
+ fi
+ done < <(ifconfig)
+ return 1
+}
+
+add_if() {
+ while true; do
+ local -A existing_ifs="( $(wg show interfaces | sed 's/\([^ ]*\)/[\1]=1/g') )"
+ local index ret
+ for ((index=0; index <= 2147483647; ++index)); do [[ -v existing_ifs[wg$index] ]] || break; done
+ if ret="$(cmd ifconfig wg$index create description "wg-quick: $INTERFACE" 2>&1)"; then
+ REAL_INTERFACE="wg$index"
+ return 0
+ fi
+ [[ $ret == *"ifconfig: SIOCIFCREATE: File exists"* ]] && continue
+ echo "$ret" >&3
+ return 1
+ done
+}
+
+del_routes() {
+ local todelete=( ) destination gateway netif
+ [[ -n $REAL_INTERFACE ]] || return 0
+ while read -r destination _ _ _ _ netif _; do
+ [[ $netif == "$REAL_INTERFACE" ]] && todelete+=( "$destination" )
+ done < <(netstat -nr -f inet)
+ for destination in "${todelete[@]}"; do
+ cmd route -q -n delete -inet "$destination" || true
+ done
+ todelete=( )
+ while read -r destination gateway _ netif; do
+ [[ $netif == "$REAL_INTERFACE" || ( $netif == lo* && $gateway == "$REAL_INTERFACE" ) ]] && todelete+=( "$destination" )
+ done < <(netstat -nr -f inet6)
+ for destination in "${todelete[@]}"; do
+ cmd route -q -n delete -inet6 "$destination" || true
+ done
+ for destination in "${ENDPOINTS[@]}"; do
+ if [[ $destination == *:* ]]; then
+ cmd route -q -n delete -inet6 "$destination" || true
+ else
+ cmd route -q -n delete -inet "$destination" || true
+ fi
+ done
+}
+
+del_if() {
+ unset_dns
+ [[ -n $REAL_INTERFACE ]] && cmd ifconfig $REAL_INTERFACE destroy
+}
+
+up_if() {
+ cmd ifconfig "$REAL_INTERFACE" up
+}
+
+add_addr() {
+ local family
+ if [[ $1 == *:* ]]; then
+ family=inet6
+ [[ -n $FIRSTADDR6 ]] || FIRSTADDR6="${1%/*}"
+ else
+ family=inet
+ [[ -n $FIRSTADDR4 ]] || FIRSTADDR4="${1%/*}"
+ fi
+ cmd ifconfig "$REAL_INTERFACE" $family "$1" alias
+}
+
+set_mtu() {
+ local mtu=0 endpoint output family
+ if [[ -n $MTU ]]; then
+ cmd ifconfig "$REAL_INTERFACE" mtu "$MTU"
+ return
+ fi
+ while read -r _ endpoint; do
+ [[ $endpoint =~ ^\[?([a-z0-9:.]+)\]?:[0-9]+$ ]] || continue
+ family=inet
+ [[ ${BASH_REMATCH[1]} == *:* ]] && family=inet6
+ output="$(route -n get "-$family" "${BASH_REMATCH[1]}" || true)"
+ [[ $output =~ interface:\ ([^ ]+)$'\n' && $(ifconfig "${BASH_REMATCH[1]}") =~ mtu\ ([0-9]+) && ${BASH_REMATCH[1]} -gt $mtu ]] && mtu="${BASH_REMATCH[1]}"
+ done < <(wg show "$REAL_INTERFACE" endpoints)
+ if [[ $mtu -eq 0 ]]; then
+ read -r output < <(route -n get default || true) || true
+ [[ $output =~ interface:\ ([^ ]+)$'\n' && $(ifconfig "${BASH_REMATCH[1]}") =~ mtu\ ([0-9]+) && ${BASH_REMATCH[1]} -gt $mtu ]] && mtu="${BASH_REMATCH[1]}"
+ fi
+ [[ $mtu -gt 0 ]] || mtu=1500
+ cmd ifconfig "$REAL_INTERFACE" mtu $(( mtu - 80 ))
+}
+
+
+collect_gateways() {
+ local destination gateway
+
+ GATEWAY4=""
+ while read -r destination gateway _; do
+ [[ $destination == default ]] || continue
+ GATEWAY4="$gateway"
+ break
+ done < <(netstat -nr -f inet)
+
+ GATEWAY6=""
+ while read -r destination gateway _; do
+ [[ $destination == default ]] || continue
+ GATEWAY6="$gateway"
+ break
+ done < <(netstat -nr -f inet6)
+}
+
+collect_endpoints() {
+ ENDPOINTS=( )
+ while read -r _ endpoint; do
+ [[ $endpoint =~ ^\[?([a-z0-9:.]+)\]?:[0-9]+$ ]] || continue
+ ENDPOINTS+=( "${BASH_REMATCH[1]}" )
+ done < <(wg show "$REAL_INTERFACE" endpoints)
+}
+
+set_endpoint_direct_route() {
+ local old_endpoints endpoint old_gateway4 old_gateway6 remove_all_old=0 added=( )
+ old_endpoints=( "${ENDPOINTS[@]}" )
+ old_gateway4="$GATEWAY4"
+ old_gateway6="$GATEWAY6"
+ collect_gateways
+ collect_endpoints
+
+ [[ $old_gateway4 != "$GATEWAY4" || $old_gateway6 != "$GATEWAY6" ]] && remove_all_old=1
+
+ if [[ $remove_all_old -eq 1 ]]; then
+ for endpoint in "${ENDPOINTS[@]}"; do
+ [[ " ${old_endpoints[*]} " == *" $endpoint "* ]] || old_endpoints+=( "$endpoint" )
+ done
+ fi
+
+ for endpoint in "${old_endpoints[@]}"; do
+ [[ $remove_all_old -eq 0 && " ${ENDPOINTS[*]} " == *" $endpoint "* ]] && continue
+ if [[ $endpoint == *:* && $AUTO_ROUTE6 -eq 1 ]]; then
+ cmd route -q -n delete -inet6 "$endpoint" 2>/dev/null || true
+ elif [[ $AUTO_ROUTE4 -eq 1 ]]; then
+ cmd route -q -n delete -inet "$endpoint" 2>/dev/null || true
+ fi
+ done
+
+ for endpoint in "${ENDPOINTS[@]}"; do
+ if [[ $remove_all_old -eq 0 && " ${old_endpoints[*]} " == *" $endpoint "* ]]; then
+ added+=( "$endpoint" )
+ continue
+ fi
+ if [[ $endpoint == *:* && $AUTO_ROUTE6 -eq 1 ]]; then
+ if [[ -n $GATEWAY6 ]]; then
+ cmd route -q -n add -inet6 "$endpoint" -gateway "$GATEWAY6" || true
+ else
+ # Prevent routing loop
+ cmd route -q -n add -inet6 "$endpoint" ::1 -blackhole || true
+ fi
+ added+=( "$endpoint" )
+ elif [[ $AUTO_ROUTE4 -eq 1 ]]; then
+ if [[ -n $GATEWAY4 ]]; then
+ cmd route -q -n add -inet "$endpoint" -gateway "$GATEWAY4" || true
+ else
+ # Prevent routing loop
+ cmd route -q -n add -inet "$endpoint" 127.0.0.1 -blackhole || true
+ fi
+ added+=( "$endpoint" )
+ fi
+ done
+ ENDPOINTS=( "${added[@]}" )
+}
+
+monitor_daemon() {
+ echo "[+] Backgrounding route monitor" >&2
+ (trap 'del_routes; exit 0' INT TERM EXIT
+ exec >/dev/null 2>&1
+ exec 19< <(exec route -n monitor)
+ local event pid=$!
+ # TODO: this should also check to see if the endpoint actually changes
+ # in response to incoming packets, and then call set_endpoint_direct_route
+ # then too. That function should be able to gracefully cleanup if the
+ # endpoints change.
+ while read -u 19 -r event; do
+ [[ $event == RTM_* ]] || continue
+ ifconfig "$REAL_INTERFACE" >/dev/null 2>&1 || break
+ [[ $AUTO_ROUTE4 -eq 1 || $AUTO_ROUTE6 -eq 1 ]] && set_endpoint_direct_route
+ # TODO: set the mtu as well, but only if up
+ done
+ kill $pid) & disown
+}
+
+set_dns() {
+ [[ ${#DNS[@]} -gt 0 ]] || return 0
+
+ # TODO: add exclusive support for nameservers
+ if pgrep -qx unwind; then
+ echo "[!] WARNING: unwind will leak DNS queries" >&2
+ elif pgrep -qx resolvd; then
+ echo "[!] WARNING: resolvd may leak DNS queries" >&2
+ else
+ echo "[+] resolvd is not running, DNS will not be configured" >&2
+ return 0
+ fi
+
+ cmd cp /etc/resolv.conf "/etc/resolv.conf.wg-quick-backup.$INTERFACE"
+ [[ ${#DNS_SEARCH[@]} -eq 0 ]] || cmd printf 'search %s\n' "${DNS_SEARCH[*]}" > /etc/resolv.conf
+ route nameserver ${REAL_INTERFACE} ${DNS[@]}
+}
+
+unset_dns() {
+ [[ -f "/etc/resolv.conf.wg-quick-backup.$INTERFACE" ]] || return 0
+ route nameserver ${REAL_INTERFACE}
+ cmd mv "/etc/resolv.conf.wg-quick-backup.$INTERFACE" /etc/resolv.conf
+}
+
+add_route() {
+ [[ $TABLE != off ]] || return 0
+ local family ifaceroute
+
+ if [[ $1 == *:* ]]; then
+ family=inet6
+ [[ -n $FIRSTADDR6 ]] || die "Local IPv6 address must be set to have routes"
+ ifaceroute="$FIRSTADDR6"
+ else
+ family=inet
+ [[ -n $FIRSTADDR4 ]] || die "Local IPv4 address must be set to have routes"
+ ifaceroute="$FIRSTADDR4"
+ fi
+
+ if [[ -n $TABLE && $TABLE != auto ]]; then
+ cmd route -q -n -T "$TABLE" add "-$family" "$1" -iface "$ifaceroute"
+ elif [[ $1 == */0 ]]; then
+ if [[ $1 == *:* ]]; then
+ AUTO_ROUTE6=1
+ cmd route -q -n add -inet6 ::/1 -iface "$ifaceroute"
+ cmd route -q -n add -inet6 8000::/1 -iface "$ifaceroute"
+ else
+ AUTO_ROUTE4=1
+ cmd route -q -n add -inet 0.0.0.0/1 -iface "$ifaceroute"
+ cmd route -q -n add -inet 128.0.0.0/1 -iface "$ifaceroute"
+ fi
+ else
+ [[ $(route -n get "-$family" "$1" 2>/dev/null) =~ interface:\ $REAL_INTERFACE$'\n' ]] || cmd route -q -n add "-$family" "$1" -iface "$ifaceroute"
+ fi
+}
+
+set_config() {
+ cmd wg setconf "$REAL_INTERFACE" <(echo "$WG_CONFIG")
+}
+
+save_config() {
+ local old_umask new_config current_config address network cmd
+ new_config=$'[Interface]\n'
+ { read -r _; while read -r _ _ network address _; do
+ [[ $network == *Link* ]] || new_config+="Address = $address"$'\n'
+ done } < <(netstat -I "$REAL_INTERFACE" -n -v)
+ # TODO: actually determine current DNS for interface
+ for address in "${DNS[@]}"; do
+ new_config+="DNS = $address"$'\n'
+ done
+ [[ -n $MTU ]] && new_config+="MTU = $MTU"$'\n'
+ [[ -n $TABLE ]] && new_config+="Table = $TABLE"$'\n'
+ [[ $SAVE_CONFIG -eq 0 ]] || new_config+=$'SaveConfig = true\n'
+ for cmd in "${PRE_UP[@]}"; do
+ new_config+="PreUp = $cmd"$'\n'
+ done
+ for cmd in "${POST_UP[@]}"; do
+ new_config+="PostUp = $cmd"$'\n'
+ done
+ for cmd in "${PRE_DOWN[@]}"; do
+ new_config+="PreDown = $cmd"$'\n'
+ done
+ for cmd in "${POST_DOWN[@]}"; do
+ new_config+="PostDown = $cmd"$'\n'
+ done
+ old_umask="$(umask)"
+ umask 077
+ current_config="$(cmd wg showconf "$REAL_INTERFACE")"
+ trap 'rm -f "$CONFIG_FILE.tmp"; exit' INT TERM EXIT
+ echo "${current_config/\[Interface\]$'\n'/$new_config}" > "$CONFIG_FILE.tmp" || die "Could not write configuration file"
+ sync "$CONFIG_FILE.tmp"
+ mv "$CONFIG_FILE.tmp" "$CONFIG_FILE" || die "Could not move configuration file"
+ trap - INT TERM EXIT
+ umask "$old_umask"
+}
+
+execute_hooks() {
+ local hook
+ for hook in "$@"; do
+ hook="${hook//%i/$REAL_INTERFACE}"
+ hook="${hook//%I/$INTERFACE}"
+ echo "[#] $hook" >&2
+ (eval "$hook")
+ done
+}
+
+cmd_usage() {
+ cat >&2 <<-_EOF
+ Usage: $PROGRAM [ up | down | save | strip ] [ CONFIG_FILE | INTERFACE ]
+
+ CONFIG_FILE is a configuration file, whose filename is the interface name
+ followed by \`.conf'. Otherwise, INTERFACE is an interface name, with
+ configuration found at /etc/wireguard/INTERFACE.conf. It is to be readable
+ by wg(8)'s \`setconf' sub-command, with the exception of the following additions
+ to the [Interface] section, which are handled by $PROGRAM:
+
+ - Address: may be specified one or more times and contains one or more
+ IP addresses (with an optional CIDR mask) to be set for the interface.
+ - DNS: an optional DNS server to use while the device is up.
+ - MTU: an optional MTU for the interface; if unspecified, auto-calculated.
+ - Table: an optional routing table to which routes will be added; if
+ unspecified or \`auto', the default table is used. If \`off', no routes
+ are added.
+ - PreUp, PostUp, PreDown, PostDown: script snippets which will be executed
+ by bash(1) at the corresponding phases of the link, most commonly used
+ to configure DNS. The string \`%i' is expanded to INTERFACE.
+ - SaveConfig: if set to \`true', the configuration is saved from the current
+ state of the interface upon shutdown.
+
+ See wg-quick(8) for more info and examples.
+ _EOF
+}
+
+cmd_up() {
+ local i
+ get_real_interface && die "\`$INTERFACE' already exists as \`$REAL_INTERFACE'"
+ trap 'del_if; del_routes; exit' INT TERM EXIT
+ execute_hooks "${PRE_UP[@]}"
+ add_if
+ set_config
+ for i in "${ADDRESSES[@]}"; do
+ add_addr "$i"
+ done
+ set_mtu
+ up_if
+ set_dns
+ for i in $(while read -r _ i; do for i in $i; do [[ $i =~ ^[0-9a-z:.]+/[0-9]+$ ]] && echo "$i"; done; done < <(wg show "$REAL_INTERFACE" allowed-ips) | sort -nr -k 2 -t /); do
+ add_route "$i"
+ done
+ [[ $AUTO_ROUTE4 -eq 1 || $AUTO_ROUTE6 -eq 1 ]] && set_endpoint_direct_route
+ monitor_daemon
+ execute_hooks "${POST_UP[@]}"
+ trap - INT TERM EXIT
+}
+
+cmd_down() {
+ get_real_interface || die "\`$INTERFACE' is not a WireGuard interface"
+ execute_hooks "${PRE_DOWN[@]}"
+ [[ $SAVE_CONFIG -eq 0 ]] || save_config
+ del_if
+ unset_dns
+ execute_hooks "${POST_DOWN[@]}"
+}
+
+cmd_save() {
+ get_real_interface || die "\`$INTERFACE' is not a WireGuard interface"
+ save_config
+}
+
+cmd_strip() {
+ echo "$WG_CONFIG"
+}
+
+# ~~ function override insertion point ~~
+
+if [[ $# -eq 1 && ( $1 == --help || $1 == -h || $1 == help ) ]]; then
+ cmd_usage
+elif [[ $# -eq 2 && $1 == up ]]; then
+ auto_su
+ parse_options "$2"
+ cmd_up
+elif [[ $# -eq 2 && $1 == down ]]; then
+ auto_su
+ parse_options "$2"
+ cmd_down
+elif [[ $# -eq 2 && $1 == save ]]; then
+ auto_su
+ parse_options "$2"
+ cmd_save
+elif [[ $# -eq 2 && $1 == strip ]]; then
+ auto_su
+ parse_options "$2"
+ cmd_strip
+else
+ cmd_usage
+ exit 1
+fi
+
+exit 0
diff --git a/contrib/wireguard-tools/src/wg-quick/wg b/contrib/wireguard-tools/src/wg-quick/wg
new file mode 120000
--- /dev/null
+++ b/contrib/wireguard-tools/src/wg-quick/wg
@@ -0,0 +1 @@
+../wg
\ No newline at end of file
diff --git a/contrib/wireguard-tools/src/wg.c b/contrib/wireguard-tools/src/wg.c
new file mode 100644
--- /dev/null
+++ b/contrib/wireguard-tools/src/wg.c
@@ -0,0 +1,66 @@
+// SPDX-License-Identifier: GPL-2.0 OR MIT
+/*
+ * Copyright (C) 2015-2020 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
+ */
+
+#include <stddef.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "subcommands.h"
+#include "version.h"
+
+const char *PROG_NAME;
+
+static const struct {
+ const char *subcommand;
+ int (*function)(int, const char**);
+ const char *description;
+} subcommands[] = {
+ { "show", show_main, "Shows the current configuration and device information" },
+ { "showconf", showconf_main, "Shows the current configuration of a given WireGuard interface, for use with `setconf'" },
+ { "set", set_main, "Change the current configuration, add peers, remove peers, or change peers" },
+ { "setconf", setconf_main, "Applies a configuration file to a WireGuard interface" },
+ { "addconf", setconf_main, "Appends a configuration file to a WireGuard interface" },
+ { "syncconf", setconf_main, "Synchronizes a configuration file to a WireGuard interface" },
+ { "genkey", genkey_main, "Generates a new private key and writes it to stdout" },
+ { "genpsk", genkey_main, "Generates a new preshared key and writes it to stdout" },
+ { "pubkey", pubkey_main, "Reads a private key from stdin and writes a public key to stdout" }
+};
+
+static void show_usage(FILE *file)
+{
+ fprintf(file, "Usage: %s <cmd> [<args>]\n\n", PROG_NAME);
+ fprintf(file, "Available subcommands:\n");
+ for (size_t i = 0; i < sizeof(subcommands) / sizeof(subcommands[0]); ++i)
+ fprintf(file, " %s: %s\n", subcommands[i].subcommand, subcommands[i].description);
+ fprintf(file, "You may pass `--help' to any of these subcommands to view usage.\n");
+}
+
+int main(int argc, const char *argv[])
+{
+ PROG_NAME = argv[0];
+
+ if (argc == 2 && (!strcmp(argv[1], "-v") || !strcmp(argv[1], "--version") || !strcmp(argv[1], "version"))) {
+ printf("wireguard-tools v%s - https://git.zx2c4.com/wireguard-tools/\n", WIREGUARD_TOOLS_VERSION);
+ return 0;
+ }
+ if (argc == 2 && (!strcmp(argv[1], "-h") || !strcmp(argv[1], "--help") || !strcmp(argv[1], "help"))) {
+ show_usage(stdout);
+ return 0;
+ }
+
+ if (argc == 1) {
+ static const char *new_argv[] = { "show", NULL };
+ return show_main(1, new_argv);
+ }
+
+ for (size_t i = 0; i < sizeof(subcommands) / sizeof(subcommands[0]); ++i) {
+ if (!strcmp(argv[1], subcommands[i].subcommand))
+ return subcommands[i].function(argc - 1, argv + 1);
+ }
+
+ fprintf(stderr, "Invalid subcommand: `%s'\n", argv[1]);
+ show_usage(stderr);
+ return 1;
+}
diff --git a/contrib/wireguard-tools/src/wincompat/compat.h b/contrib/wireguard-tools/src/wincompat/compat.h
new file mode 100644
--- /dev/null
+++ b/contrib/wireguard-tools/src/wincompat/compat.h
@@ -0,0 +1,27 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2015-2020 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
+ */
+
+#define __USE_MINGW_ANSI_STDIO 1
+#include <stdio.h>
+#include <stdbool.h>
+#include <stdint.h>
+
+#include <winsock2.h>
+#include <ws2ipdef.h>
+#include <ws2tcpip.h>
+#include <in6addr.h>
+#include <windows.h>
+
+#undef interface
+#undef min
+#undef max
+
+#define IFNAMSIZ 64
+#define EAI_SYSTEM -99
+
+/* libc.c */
+char *strsep(char **str, const char *sep);
+ssize_t getdelim(char **buf, size_t *bufsiz, int delimiter, FILE *fp);
+ssize_t getline(char **buf, size_t *bufsiz, FILE *fp);
diff --git a/contrib/wireguard-tools/src/wincompat/include/arpa/inet.h b/contrib/wireguard-tools/src/wincompat/include/arpa/inet.h
new file mode 100644
diff --git a/contrib/wireguard-tools/src/wincompat/include/hashtable.h b/contrib/wireguard-tools/src/wincompat/include/hashtable.h
new file mode 100644
--- /dev/null
+++ b/contrib/wireguard-tools/src/wincompat/include/hashtable.h
@@ -0,0 +1,61 @@
+/* SPDX-License-Identifier: GPL-2.0
+ *
+ * Copyright (C) 2018-2021 WireGuard LLC. All Rights Reserved.
+ */
+
+#ifndef _HASHTABLE_H
+#define _HASHTABLE_H
+
+#include <string.h>
+
+enum { HASHTABLE_ENTRY_BUCKETS_POW2 = 1 << 10 };
+
+struct hashtable_entry {
+ char *key;
+ void *value;
+ struct hashtable_entry *next;
+};
+
+struct hashtable {
+ struct hashtable_entry *entry_buckets[HASHTABLE_ENTRY_BUCKETS_POW2];
+};
+
+static unsigned int hashtable_bucket(const char *str)
+{
+ unsigned long hash = 5381;
+ char c;
+ while ((c = *str++))
+ hash = ((hash << 5) + hash) ^ c;
+ return hash & (HASHTABLE_ENTRY_BUCKETS_POW2 - 1);
+}
+
+static struct hashtable_entry *hashtable_find_entry(struct hashtable *hashtable, const char *key)
+{
+ struct hashtable_entry *entry;
+ for (entry = hashtable->entry_buckets[hashtable_bucket(key)]; entry; entry = entry->next) {
+ if (!strcmp(entry->key, key))
+ return entry;
+ }
+ return NULL;
+}
+
+static struct hashtable_entry *hashtable_find_or_insert_entry(struct hashtable *hashtable, const char *key)
+{
+ struct hashtable_entry **entry;
+ for (entry = &hashtable->entry_buckets[hashtable_bucket(key)]; *entry; entry = &(*entry)->next) {
+ if (!strcmp((*entry)->key, key))
+ return *entry;
+ }
+ *entry = calloc(1, sizeof(**entry));
+ if (!*entry)
+ return NULL;
+ (*entry)->key = strdup(key);
+ if (!(*entry)->key) {
+ free(*entry);
+ *entry = NULL;
+ return NULL;
+ }
+ return *entry;
+}
+
+#endif
diff --git a/contrib/wireguard-tools/src/wincompat/include/net/if.h b/contrib/wireguard-tools/src/wincompat/include/net/if.h
new file mode 100644
diff --git a/contrib/wireguard-tools/src/wincompat/include/netdb.h b/contrib/wireguard-tools/src/wincompat/include/netdb.h
new file mode 100644
diff --git a/contrib/wireguard-tools/src/wincompat/include/netinet/in.h b/contrib/wireguard-tools/src/wincompat/include/netinet/in.h
new file mode 100644
diff --git a/contrib/wireguard-tools/src/wincompat/include/sys/socket.h b/contrib/wireguard-tools/src/wincompat/include/sys/socket.h
new file mode 100644
diff --git a/contrib/wireguard-tools/src/wincompat/init.c b/contrib/wireguard-tools/src/wincompat/init.c
new file mode 100644
--- /dev/null
+++ b/contrib/wireguard-tools/src/wincompat/init.c
@@ -0,0 +1,49 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2015-2020 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
+ */
+
+#include <winsock2.h>
+#include <windows.h>
+
+#ifndef ENABLE_VIRTUAL_TERMINAL_PROCESSING
+#define ENABLE_VIRTUAL_TERMINAL_PROCESSING 0x4
+#endif
+
+extern void NTAPI RtlGetNtVersionNumbers(DWORD *major, DWORD *minor, DWORD *build);
+bool is_win7 = false;
+
+__attribute__((constructor)) static void init(void)
+{
+ char *colormode;
+ DWORD console_mode, major, minor;
+ HANDLE stdout_handle;
+ WSADATA wsaData;
+
+ if (!SetDllDirectoryA("") || !SetDefaultDllDirectories(LOAD_LIBRARY_SEARCH_SYSTEM32))
+ abort();
+
+ RtlGetNtVersionNumbers(&major, &minor, NULL);
+ is_win7 = (major == 6 && minor <= 1) || major < 6;
+
+ WSAStartup(MAKEWORD(2, 2), &wsaData);
+
+ stdout_handle = GetStdHandle(STD_OUTPUT_HANDLE); // We don't close this.
+ if (stdout_handle == INVALID_HANDLE_VALUE)
+ goto no_color;
+ if (!GetConsoleMode(stdout_handle, &console_mode))
+ goto no_color;
+ if (!SetConsoleMode(stdout_handle, ENABLE_VIRTUAL_TERMINAL_PROCESSING | console_mode))
+ goto no_color;
+ return;
+
+no_color:
+ colormode = getenv("WG_COLOR_MODE");
+ if (!colormode)
+ putenv("WG_COLOR_MODE=never");
+}
+
+__attribute__((destructor)) static void deinit(void)
+{
+ WSACleanup();
+}
diff --git a/contrib/wireguard-tools/src/wincompat/libc.c b/contrib/wireguard-tools/src/wincompat/libc.c
new file mode 100644
--- /dev/null
+++ b/contrib/wireguard-tools/src/wincompat/libc.c
@@ -0,0 +1,69 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2015-2020 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
+ */
+
+#include <stdio.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <windows.h>
+
+char *strsep(char **str, const char *sep)
+{
+ char *s = *str, *end;
+ if (!s)
+ return NULL;
+ end = s + strcspn(s, sep);
+ if (*end)
+ *end++ = 0;
+ else
+ end = 0;
+ *str = end;
+ return s;
+}
+
+ssize_t getdelim(char **buf, size_t *bufsiz, int delimiter, FILE *fp)
+{
+ char *ptr, *eptr;
+
+ if (!*buf || !*bufsiz) {
+ *bufsiz = BUFSIZ;
+ if (!(*buf = malloc(*bufsiz)))
+ return -1;
+ }
+
+ for (ptr = *buf, eptr = *buf + *bufsiz;;) {
+ int c = fgetc(fp);
+ if (c == -1) {
+ if (feof(fp)) {
+ ssize_t diff = (ssize_t)(ptr - *buf);
+ if (diff != 0) {
+ *ptr = '\0';
+ return diff;
+ }
+ }
+ return -1;
+ }
+ *ptr++ = c;
+ if (c == delimiter) {
+ *ptr = '\0';
+ return ptr - *buf;
+ }
+ if (ptr + 2 >= eptr) {
+ char *nbuf;
+ size_t nbufsiz = *bufsiz * 2;
+ ssize_t d = ptr - *buf;
+ if ((nbuf = realloc(*buf, nbufsiz)) == NULL)
+ return -1;
+ *buf = nbuf;
+ *bufsiz = nbufsiz;
+ eptr = nbuf + nbufsiz;
+ ptr = nbuf + d;
+ }
+ }
+}
+
+ssize_t getline(char **buf, size_t *bufsiz, FILE *fp)
+{
+ return getdelim(buf, bufsiz, '\n', fp);
+}
diff --git a/contrib/wireguard-tools/src/wincompat/loader.c b/contrib/wireguard-tools/src/wincompat/loader.c
new file mode 100644
--- /dev/null
+++ b/contrib/wireguard-tools/src/wincompat/loader.c
@@ -0,0 +1,20 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2015-2021 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
+ */
+
+#include <windows.h>
+#include <delayimp.h>
+
+static FARPROC WINAPI delayed_load_library_hook(unsigned dliNotify, PDelayLoadInfo pdli)
+{
+ HMODULE library;
+ if (dliNotify != dliNotePreLoadLibrary)
+ return NULL;
+ library = LoadLibraryExA(pdli->szDll, NULL, LOAD_LIBRARY_SEARCH_SYSTEM32);
+ if (!library)
+ abort();
+ return (FARPROC)library;
+}
+
+PfnDliHook __pfnDliNotifyHook2 = delayed_load_library_hook;
diff --git a/contrib/wireguard-tools/src/wincompat/manifest.xml b/contrib/wireguard-tools/src/wincompat/manifest.xml
new file mode 100644
--- /dev/null
+++ b/contrib/wireguard-tools/src/wincompat/manifest.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
+ <assemblyIdentity version="1.0.0.0" processorArchitecture="*" name="wg" type="win32" />
+ <compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
+ <application>
+ <!-- Windows 10 -->
+ <supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}" />
+ <!-- Windows 8.1 -->
+ <supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}" />
+ <!-- Windows 8 -->
+ <supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}" />
+ <!-- Windows 7 -->
+ <supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}" />
+ </application>
+ </compatibility>
+ <trustInfo xmlns="urn:schemas-microsoft-com:asm.v2">
+ <security>
+ <requestedPrivileges xmlns="urn:schemas-microsoft-com:asm.v3">
+ <requestedExecutionLevel level="asInvoker" />
+ </requestedPrivileges>
+ </security>
+ </trustInfo>
+</assembly>
diff --git a/contrib/wireguard-tools/src/wincompat/resources.rc b/contrib/wireguard-tools/src/wincompat/resources.rc
new file mode 100644
--- /dev/null
+++ b/contrib/wireguard-tools/src/wincompat/resources.rc
@@ -0,0 +1,40 @@
+/* SPDX-License-Identifier: GPL-2.0
+ *
+ * Copyright (C) 2020 Jason A. Donenfeld. All Rights Reserved.
+ */
+
+#include <windows.h>
+
+#pragma code_page(65001) // UTF-8
+
+LANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRAL
+CREATEPROCESS_MANIFEST_RESOURCE_ID RT_MANIFEST manifest.xml
+
+#define STRINGIZE(x) #x
+#define EXPAND(x) STRINGIZE(x)
+
+VS_VERSION_INFO VERSIONINFO
+FILEOS VOS_NT_WINDOWS32
+FILETYPE VFT_APP
+FILESUBTYPE VFT2_UNKNOWN
+BEGIN
+ BLOCK "StringFileInfo"
+ BEGIN
+ BLOCK "040904b0"
+ BEGIN
+ VALUE "CompanyName", "WireGuard LLC"
+ VALUE "FileDescription", "WireGuard wg(8) CLI: Fast, Modern, Secure VPN Tunnel"
+ VALUE "FileVersion", EXPAND(VERSION_STR)
+ VALUE "InternalName", "wg"
+ VALUE "LegalCopyright", "Copyright © 2015-2020 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved."
+ VALUE "OriginalFilename", "wg.exe"
+ VALUE "ProductName", "WireGuard"
+ VALUE "ProductVersion", EXPAND(VERSION_STR)
+ VALUE "Comments", "https://www.wireguard.com/"
+ END
+ END
+ BLOCK "VarFileInfo"
+ BEGIN
+ VALUE "Translation", 0x409, 0x4b0
+ END
+END
diff --git a/usr.bin/Makefile b/usr.bin/Makefile
--- a/usr.bin/Makefile
+++ b/usr.bin/Makefile
@@ -174,6 +174,7 @@
w \
wall \
wc \
+ wg \
what \
whereis \
which \
diff --git a/usr.bin/wg/Makefile b/usr.bin/wg/Makefile
new file mode 100644
--- /dev/null
+++ b/usr.bin/wg/Makefile
@@ -0,0 +1,41 @@
+
+PROG= wg
+MAN= wg.8
+
+LIBADD= nv
+
+.PATH: ${SRCTOP}/contrib/wireguard-tools/src
+.PATH: ${SRCTOP}/contrib/wireguard-tools/src/man
+
+SRCS+= wg.c
+
+SRCS+= config.c
+SRCS+= encoding.c
+SRCS+= ipc.c
+SRCS+= terminal.c
+
+SRCS+= curve25519.c
+
+# Subcommands
+SRCS+= show.c
+SRCS+= showconf.c
+SRCS+= set.c
+SRCS+= setconf.c
+SRCS+= genkey.c
+SRCS+= pubkey.c
+
+# Base should pull RUNSTATEDIR from paths.h, _PATH_VARRUN
+CFLAGS+= -include paths.h
+CFLAGS+= -DRUNSTATEDIR="_PATH_VARRUN"
+
+# Not going to change this one.
+CWARNFLAGS.curve25519.c+= -Wno-shadow
+
+# sockaddr{,_in} alignment
+CWARNFLAGS.config.c+= -Wno-cast-align
+CWARNFLAGS.ipc.c+= -Wno-cast-align
+
+# qsort comparator
+CWARNFLAGS.show.c+= -Wno-cast-qual
+
+.include <bsd.prog.mk>

File Metadata

Mime Type
text/plain
Expires
Tue, Apr 29, 4:00 PM (13 h, 51 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
17842521
Default Alt Text
D36952.id111712.diff (549 KB)

Event Timeline