From and at mullvad.net Wed Oct 4 11:46:20 2023 From: and at mullvad.net (Andrej Mihajlov) Date: Wed, 4 Oct 2023 13:46:20 +0200 Subject: =?utf-8?Q?Re=3A_Wireguard-apple_1=2E0=2E16-27_can=E2=80=99t_be_ad?= =?utf-8?Q?ded_via_SPM?= In-Reply-To: References: Message-ID: Hi, Right. I think that preamble in Package.swift should be changed from: > // swift-tools-version:5.3 to at least: > // swift-tools-version:5.5 Which works with Xcode 15 based on my experience. Jason could you please bump it? Best, Andrej > On 30 Sep 2023, at 21:09, Houman wrote: > > Hello Jason, Andrej > > Please try to add the latest Wireguard-Apple 1.0.16-27 via Swift > Package Manager and you will see that fails. The issue could be > related to swift-tools-version. > > Please advise what to do? > Thank you. > Houman From houmie at gmail.com Wed Oct 4 16:04:50 2023 From: houmie at gmail.com (Houman) Date: Wed, 4 Oct 2023 17:04:50 +0100 Subject: =?UTF-8?Q?Re=3A_Wireguard=2Dapple_1=2E0=2E16=2D27_can=E2=80=99t_be_added_via_S?= =?UTF-8?Q?PM?= In-Reply-To: References: Message-ID: Hello Andrej, Thank you for your reply. I have already forked the branch and set Package.swift to swift-tools-version:5.5. But I still get the same error as before. Swift-tools-version:5.5 only worked with the previous version 1.0.15-26 that was released on 23rd Sep 2021. The 1.0.16-27 that was tagged on 15th Feb 2023 is completely broken and doesn't work with SPM. I have tried swift-tools-versions 5.6, 5.7, 5.8 and 5.9. I strongly believe that this commit from Jason ccc7472fd7d1c7c19584e6a30c45a56b8ba57790 on 14th Feb 2023 has broken it. I don't think he tested it afterwards with SPM. This (outdated) PR proves that more people are having problems with installing the latest version since the February release: https://github.com/WireGuard/wireguard-apple/pull/24 Error Message: Invalid manifest (compiled with: "/Applications/Xcode app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/us/bin/swift", "visoverlay", "/ar/folders/9m/lwx244892997kpg7vb5vssh0000gn/T/TemporaryDirectory. BBHXP8/vfs.yaml*, "-", "/Applicationscode. app/Contents/SharedFrameworks/SwiftM.framework/SharedSupport/ManifestA*, "PackageDescription, "-Xlinker", "-rath", "-Xlinker", "/Applicatio.. Xcode version: Version 15.0 (15A240d) Mac version: 14.0 (23A344) Your help is much appreciated, Thanks, Houman On Wed, 4 Oct 2023 at 12:46, Andrej Mihajlov wrote: > > Hi, > > Right. I think that preamble in Package.swift should be changed from: > > > // swift-tools-version:5.3 > > to at least: > > > // swift-tools-version:5.5 > > Which works with Xcode 15 based on my experience. > > Jason could you please bump it? > > Best, > > Andrej > > > On 30 Sep 2023, at 21:09, Houman wrote: > > > > Hello Jason, Andrej > > > > Please try to add the latest Wireguard-Apple 1.0.16-27 via Swift > > Package Manager and you will see that fails. The issue could be > > related to swift-tools-version. > > > > Please advise what to do? > > Thank you. > > Houman > From and at mullvad.net Thu Oct 5 09:28:03 2023 From: and at mullvad.net (Andrej Mihajlov) Date: Thu, 5 Oct 2023 11:28:03 +0200 Subject: =?utf-8?Q?Re=3A_Wireguard-apple_1=2E0=2E16-27_can=E2=80=99t_be_ad?= =?utf-8?Q?ded_via_SPM?= In-Reply-To: References: Message-ID: Hi Houman, I maintain a fork of wireguard-apple for the past couple of years and it works with Xcode 15 and Swift 5.9: github.com/mullvad/wireguard-apple (branch: mullvad-master) In the app I maintain, WireGuardGoBridge target is built using bash script for flexibility, instead of bash sorcery so you might want this too: github.com/mullvad/mullvadvpn-app/blob/main/ios/build-wireguard-go.sh Hope any of this helps. Best, Andrej > On 4 Oct 2023, at 18:04, Houman wrote: > > Hello Andrej, > > Thank you for your reply. I have already forked the branch and set > Package.swift to swift-tools-version:5.5. But I still get the same > error as before. > > Swift-tools-version:5.5 only worked with the previous version > 1.0.15-26 that was released on 23rd Sep 2021. > The 1.0.16-27 that was tagged on 15th Feb 2023 is completely broken > and doesn't work with SPM. I have tried swift-tools-versions 5.6, > 5.7, 5.8 and 5.9. I strongly believe that this commit from Jason > ccc7472fd7d1c7c19584e6a30c45a56b8ba57790 on 14th Feb 2023 has broken > it. I don't think he tested it afterwards with SPM. > > This (outdated) PR proves that more people are having problems with > installing the latest version since the February release: > https://github.com/WireGuard/wireguard-apple/pull/24 > > Error Message: > Invalid manifest (compiled with: "/Applications/Xcode > app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/us/bin/swift", > "visoverlay", "/ar/folders/9m/lwx244892997kpg7vb5vssh0000gn/T/TemporaryDirectory. > BBHXP8/vfs.yaml*, "-", "/Applicationscode. > app/Contents/SharedFrameworks/SwiftM.framework/SharedSupport/ManifestA*, > "PackageDescription, "-Xlinker", "-rath", "-Xlinker", "/Applicatio.. > > Xcode version: Version 15.0 (15A240d) > Mac version: 14.0 (23A344) > > Your help is much appreciated, > Thanks, > Houman > > > On Wed, 4 Oct 2023 at 12:46, Andrej Mihajlov wrote: >> >> Hi, >> >> Right. I think that preamble in Package.swift should be changed from: >> >>> // swift-tools-version:5.3 >> >> to at least: >> >>> // swift-tools-version:5.5 >> >> Which works with Xcode 15 based on my experience. >> >> Jason could you please bump it? >> >> Best, >> >> Andrej >> >>> On 30 Sep 2023, at 21:09, Houman wrote: >>> >>> Hello Jason, Andrej >>> >>> Please try to add the latest Wireguard-Apple 1.0.16-27 via Swift >>> Package Manager and you will see that fails. The issue could be >>> related to swift-tools-version. >>> >>> Please advise what to do? >>> Thank you. >>> Houman >> From houmie at gmail.com Thu Oct 5 10:08:04 2023 From: houmie at gmail.com (Houman) Date: Thu, 5 Oct 2023 11:08:04 +0100 Subject: =?UTF-8?Q?Re=3A_Wireguard=2Dapple_1=2E0=2E16=2D27_can=E2=80=99t_be_added_via_S?= =?UTF-8?Q?PM?= In-Reply-To: References: Message-ID: Hi Andrej, That's absolutely brilliant. Thank you for showing love to this project. I have pointed my SPM to your repo and it worked right away. What is the WireguardKitType? That wasn't there before, but It seems to be working even without adding that to my project. I've starred your project too. Well done. Best, Houman On Thu, 5 Oct 2023 at 10:28, Andrej Mihajlov wrote: > > Hi Houman, > > I maintain a fork of wireguard-apple for the past couple of years and it works with Xcode 15 and Swift 5.9: > github.com/mullvad/wireguard-apple (branch: mullvad-master) > > In the app I maintain, WireGuardGoBridge target is built using bash script for flexibility, instead of bash sorcery so you might want this too: > github.com/mullvad/mullvadvpn-app/blob/main/ios/build-wireguard-go.sh > > Hope any of this helps. > > Best, > Andrej > > > On 4 Oct 2023, at 18:04, Houman wrote: > > > > Hello Andrej, > > > > Thank you for your reply. I have already forked the branch and set > > Package.swift to swift-tools-version:5.5. But I still get the same > > error as before. > > > > Swift-tools-version:5.5 only worked with the previous version > > 1.0.15-26 that was released on 23rd Sep 2021. > > The 1.0.16-27 that was tagged on 15th Feb 2023 is completely broken > > and doesn't work with SPM. I have tried swift-tools-versions 5.6, > > 5.7, 5.8 and 5.9. I strongly believe that this commit from Jason > > ccc7472fd7d1c7c19584e6a30c45a56b8ba57790 on 14th Feb 2023 has broken > > it. I don't think he tested it afterwards with SPM. > > > > This (outdated) PR proves that more people are having problems with > > installing the latest version since the February release: > > https://github.com/WireGuard/wireguard-apple/pull/24 > > > > Error Message: > > Invalid manifest (compiled with: "/Applications/Xcode > > app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/us/bin/swift", > > "visoverlay", "/ar/folders/9m/lwx244892997kpg7vb5vssh0000gn/T/TemporaryDirectory. > > BBHXP8/vfs.yaml*, "-", "/Applicationscode. > > app/Contents/SharedFrameworks/SwiftM.framework/SharedSupport/ManifestA*, > > "PackageDescription, "-Xlinker", "-rath", "-Xlinker", "/Applicatio.. > > > > Xcode version: Version 15.0 (15A240d) > > Mac version: 14.0 (23A344) > > > > Your help is much appreciated, > > Thanks, > > Houman > > > > > > On Wed, 4 Oct 2023 at 12:46, Andrej Mihajlov wrote: > >> > >> Hi, > >> > >> Right. I think that preamble in Package.swift should be changed from: > >> > >>> // swift-tools-version:5.3 > >> > >> to at least: > >> > >>> // swift-tools-version:5.5 > >> > >> Which works with Xcode 15 based on my experience. > >> > >> Jason could you please bump it? > >> > >> Best, > >> > >> Andrej > >> > >>> On 30 Sep 2023, at 21:09, Houman wrote: > >>> > >>> Hello Jason, Andrej > >>> > >>> Please try to add the latest Wireguard-Apple 1.0.16-27 via Swift > >>> Package Manager and you will see that fails. The issue could be > >>> related to swift-tools-version. > >>> > >>> Please advise what to do? > >>> Thank you. > >>> Houman > >> > From and at mullvad.net Thu Oct 5 10:35:59 2023 From: and at mullvad.net (Andrej Mihajlov) Date: Thu, 5 Oct 2023 12:35:59 +0200 Subject: =?utf-8?Q?Re=3A_Wireguard-apple_1=2E0=2E16-27_can=E2=80=99t_be_ad?= =?utf-8?Q?ded_via_SPM?= In-Reply-To: References: Message-ID: <6B97F367-1E1C-4F13-AC03-3611511899EA@mullvad.net> Houman, Great! I have added WireguardKitType as a dynamic library which contains all of the Swift data types used by WireGuardKit, such as: PrivateKey, PublicKey, IPAddressRange, etc. It does not depend on WireGuardGoBridge, which proved to be problematic to include in unit tests (if I recall that right). Apart from that I noticed that go runtime spawns some worker threads when binary is linked against wg-go, so we didn?t want that in our main bundle or app extensions. So in short, when using any of data types provided by WireGuardKit: - Import WireGuardKitTypes in main bundle or app extensions. - Import WireGuardKit in network extension. Internally WireGuardKit re-exports all types from WireGuardKitTypes. There is a handful of other improvements besides that and I hope that all of them will be upstreamed over time. Best, Andrej > On 5 Oct 2023, at 12:08, Houman wrote: > > Hi Andrej, > > That's absolutely brilliant. Thank you for showing love to this > project. I have pointed my SPM to your repo and it worked right away. > What is the WireguardKitType? That wasn't there before, but It seems > to be working even without adding that to my project. > > I've starred your project too. Well done. > > Best, > Houman > > > > > > On Thu, 5 Oct 2023 at 10:28, Andrej Mihajlov wrote: >> >> Hi Houman, >> >> I maintain a fork of wireguard-apple for the past couple of years and it works with Xcode 15 and Swift 5.9: >> github.com/mullvad/wireguard-apple (branch: mullvad-master) >> >> In the app I maintain, WireGuardGoBridge target is built using bash script for flexibility, instead of bash sorcery so you might want this too: >> github.com/mullvad/mullvadvpn-app/blob/main/ios/build-wireguard-go.sh >> >> Hope any of this helps. >> >> Best, >> Andrej >> >>> On 4 Oct 2023, at 18:04, Houman wrote: >>> >>> Hello Andrej, >>> >>> Thank you for your reply. I have already forked the branch and set >>> Package.swift to swift-tools-version:5.5. But I still get the same >>> error as before. >>> >>> Swift-tools-version:5.5 only worked with the previous version >>> 1.0.15-26 that was released on 23rd Sep 2021. >>> The 1.0.16-27 that was tagged on 15th Feb 2023 is completely broken >>> and doesn't work with SPM. I have tried swift-tools-versions 5.6, >>> 5.7, 5.8 and 5.9. I strongly believe that this commit from Jason >>> ccc7472fd7d1c7c19584e6a30c45a56b8ba57790 on 14th Feb 2023 has broken >>> it. I don't think he tested it afterwards with SPM. >>> >>> This (outdated) PR proves that more people are having problems with >>> installing the latest version since the February release: >>> https://github.com/WireGuard/wireguard-apple/pull/24 >>> >>> Error Message: >>> Invalid manifest (compiled with: "/Applications/Xcode >>> app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/us/bin/swift", >>> "visoverlay", "/ar/folders/9m/lwx244892997kpg7vb5vssh0000gn/T/TemporaryDirectory. >>> BBHXP8/vfs.yaml*, "-", "/Applicationscode. >>> app/Contents/SharedFrameworks/SwiftM.framework/SharedSupport/ManifestA*, >>> "PackageDescription, "-Xlinker", "-rath", "-Xlinker", "/Applicatio.. >>> >>> Xcode version: Version 15.0 (15A240d) >>> Mac version: 14.0 (23A344) >>> >>> Your help is much appreciated, >>> Thanks, >>> Houman >>> >>> >>> On Wed, 4 Oct 2023 at 12:46, Andrej Mihajlov wrote: >>>> >>>> Hi, >>>> >>>> Right. I think that preamble in Package.swift should be changed from: >>>> >>>>> // swift-tools-version:5.3 >>>> >>>> to at least: >>>> >>>>> // swift-tools-version:5.5 >>>> >>>> Which works with Xcode 15 based on my experience. >>>> >>>> Jason could you please bump it? >>>> >>>> Best, >>>> >>>> Andrej >>>> >>>>> On 30 Sep 2023, at 21:09, Houman wrote: >>>>> >>>>> Hello Jason, Andrej >>>>> >>>>> Please try to add the latest Wireguard-Apple 1.0.16-27 via Swift >>>>> Package Manager and you will see that fails. The issue could be >>>>> related to swift-tools-version. >>>>> >>>>> Please advise what to do? >>>>> Thank you. >>>>> Houman >>>> >> From syzbot+66ff56c4661498a22ae8 at syzkaller.appspotmail.com Sun Oct 8 15:27:45 2023 From: syzbot+66ff56c4661498a22ae8 at syzkaller.appspotmail.com (syzbot) Date: Sun, 08 Oct 2023 08:27:45 -0700 Subject: [syzbot] [wireguard?] WARNING in kthread_unpark Message-ID: <0000000000008f393c0607361dd5@google.com> Hello, syzbot found the following issue on: HEAD commit: 2e530aeb342b Merge branch 'for-next/core', remote-tracking.. git tree: git://git.kernel.org/pub/scm/linux/kernel/git/arm64/linux.git for-kernelci console output: https://syzkaller.appspot.com/x/log.txt?x=13ef2062680000 kernel config: https://syzkaller.appspot.com/x/.config?x=5cc95add722fb0c1 dashboard link: https://syzkaller.appspot.com/bug?extid=66ff56c4661498a22ae8 compiler: Debian clang version 15.0.6, GNU ld (GNU Binutils for Debian) 2.40 userspace arch: arm64 Unfortunately, I don't have any reproducer for this issue yet. Downloadable assets: disk image: https://storage.googleapis.com/syzbot-assets/2468ab4c933e/disk-2e530aeb.raw.xz vmlinux: https://storage.googleapis.com/syzbot-assets/bd3708bf8a20/vmlinux-2e530aeb.xz kernel image: https://storage.googleapis.com/syzbot-assets/86bc7d3468f9/Image-2e530aeb.gz.xz IMPORTANT: if you fix the issue, please add the following tag to the commit: Reported-by: syzbot+66ff56c4661498a22ae8 at syzkaller.appspotmail.com ------------[ cut here ]------------ WARNING: CPU: 1 PID: 11 at kernel/kthread.c:524 __kthread_bind kernel/kthread.c:537 [inline] WARNING: CPU: 1 PID: 11 at kernel/kthread.c:524 kthread_unpark+0x148/0x204 kernel/kthread.c:630 Modules linked in: CPU: 1 PID: 11 Comm: kworker/u4:0 Not tainted 6.6.0-rc3-syzkaller-g2e530aeb342b #0 Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS Google 08/04/2023 Workqueue: netns cleanup_net pstate: 80400005 (Nzcv daif +PAN -UAO -TCO -DIT -SSBS BTYPE=--) pc : __kthread_bind kernel/kthread.c:537 [inline] pc : kthread_unpark+0x148/0x204 kernel/kthread.c:630 lr : __kthread_bind kernel/kthread.c:537 [inline] lr : kthread_unpark+0x148/0x204 kernel/kthread.c:630 sp : ffff800092b376d0 x29: ffff800092b376d0 x28: ffff0000c63f0000 x27: dfff800000000000 x26: 1fffe00019602005 x25: dfff800000000000 x24: 1fffe00018c7e29c x23: 0000000000000000 x22: 0000000000000000 x21: ffff0000cd704400 x20: ffff0000cb01002c x19: ffff0000cb010000 x18: ffff800092b371c0 x17: ffff80008e19d000 x16: ffff8000802771bc x15: 0000000000000001 x14: 1fffe00019602130 x13: 0000000000000000 x12: 0000000000000003 x11: 0000000000000000 x10: 0000000000000000 x9 : 0000000000000000 x8 : ffff0000c199b780 x7 : 0000000000000000 x6 : 0000000000000000 x5 : 0000000000000020 x4 : 0000000000000000 x3 : 0000000000000000 x2 : 0000000000000006 x1 : 0000000000000000 x0 : 0000000000000000 Call trace: __kthread_bind kernel/kthread.c:537 [inline] kthread_unpark+0x148/0x204 kernel/kthread.c:630 kthread_stop+0x188/0x704 kernel/kthread.c:706 destroy_workqueue+0x124/0xdc4 kernel/workqueue.c:4805 wg_destruct+0x1c8/0x2dc drivers/net/wireguard/device.c:258 netdev_run_todo+0xc34/0xe08 net/core/dev.c:10445 rtnl_unlock+0x14/0x20 net/core/rtnetlink.c:151 default_device_exit_batch+0x6cc/0x744 net/core/dev.c:11454 ops_exit_list net/core/net_namespace.c:175 [inline] cleanup_net+0x5dc/0x8d0 net/core/net_namespace.c:614 process_one_work+0x694/0x1204 kernel/workqueue.c:2630 process_scheduled_works kernel/workqueue.c:2703 [inline] worker_thread+0x938/0xef4 kernel/workqueue.c:2784 kthread+0x288/0x310 kernel/kthread.c:388 ret_from_fork+0x10/0x20 arch/arm64/kernel/entry.S:857 irq event stamp: 8730562 hardirqs last enabled at (8730561): [] __raw_spin_unlock_irqrestore include/linux/spinlock_api_smp.h:151 [inline] hardirqs last enabled at (8730561): [] _raw_spin_unlock_irqrestore+0x38/0x98 kernel/locking/spinlock.c:194 hardirqs last disabled at (8730562): [] el1_dbg+0x24/0x80 arch/arm64/kernel/entry-common.c:436 softirqs last enabled at (8730490): [] spin_unlock_bh include/linux/spinlock.h:396 [inline] softirqs last enabled at (8730490): [] wg_packet_purge_staged_packets+0x1b8/0x1f4 drivers/net/wireguard/send.c:338 softirqs last disabled at (8730488): [] spin_lock_bh include/linux/spinlock.h:356 [inline] softirqs last disabled at (8730488): [] wg_packet_purge_staged_packets+0x3c/0x1f4 drivers/net/wireguard/send.c:335 ---[ end trace 0000000000000000 ]--- --- This report is generated by a bot. It may contain errors. See https://goo.gl/tpsmEJ for more information about syzbot. syzbot engineers can be reached at syzkaller at googlegroups.com. syzbot will keep track of this issue. See: https://goo.gl/tpsmEJ#status for how to communicate with syzbot. If the bug is already fixed, let syzbot know by replying with: #syz fix: exact-commit-title If you want to overwrite bug's subsystems, reply with: #syz set subsystems: new-subsystem (See the list of subsystem names on the web dashboard) If the bug is a duplicate of another bug, reply with: #syz dup: exact-subject-of-another-report If you want to undo deduplication, reply with: #syz undup From dxld at darkboxed.org Mon Oct 23 13:06:09 2023 From: dxld at darkboxed.org (=?UTF-8?q?Daniel=20Gr=C3=B6ber?=) Date: Mon, 23 Oct 2023 15:06:09 +0200 Subject: [PATCH] wireguard: Fix leaking sockets in wg_socket_init error paths Message-ID: <20231023130609.595122-1-dxld@darkboxed.org> This doesn't seem to be reachable normally, but while working on a patch for the address binding code I ended up triggering this leak and had to reboot to get rid of the leaking wg sockets. --- drivers/net/wireguard/socket.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireguard/socket.c b/drivers/net/wireguard/socket.c index 0414d7a6ce74..c35163f503e7 100644 --- a/drivers/net/wireguard/socket.c +++ b/drivers/net/wireguard/socket.c @@ -387,7 +387,7 @@ int wg_socket_init(struct wg_device *wg, u16 port) ret = udp_sock_create(net, &port4, &new4); if (ret < 0) { pr_err("%s: Could not create IPv4 socket\n", wg->dev->name); - goto out; + goto err; } set_sock_opts(new4); setup_udp_tunnel_sock(net, new4, &cfg); @@ -402,7 +402,7 @@ int wg_socket_init(struct wg_device *wg, u16 port) goto retry; pr_err("%s: Could not create IPv6 socket\n", wg->dev->name); - goto out; + goto err; } set_sock_opts(new6); setup_udp_tunnel_sock(net, new6, &cfg); @@ -414,6 +414,11 @@ int wg_socket_init(struct wg_device *wg, u16 port) out: put_net(net); return ret; + +err: + sock_free(new4 ? new4->sk : NULL); + sock_free(new6 ? new6->sk : NULL); + goto out; } void wg_socket_reinit(struct wg_device *wg, struct sock *new4, -- 2.39.2 From Jason at zx2c4.com Mon Oct 23 14:04:13 2023 From: Jason at zx2c4.com (Jason A. Donenfeld) Date: Mon, 23 Oct 2023 16:04:13 +0200 Subject: [PATCH] wireguard: Fix leaking sockets in wg_socket_init error paths In-Reply-To: <20231023130609.595122-1-dxld@darkboxed.org> References: <20231023130609.595122-1-dxld@darkboxed.org> Message-ID: Hi, The signed-off-by is missing and the subject does not match the format of any other wireguard commits. On Mon, Oct 23, 2023 at 03:06:09PM +0200, Daniel Gr?ber wrote: > This doesn't seem to be reachable normally, but while working on a patch "Normally" as in what? At all? Or? > for the address binding code I ended up triggering this leak and had to > reboot to get rid of the leaking wg sockets. This commit message doesn't describe any rationale for this patch. Can you describe the bug? > --- > drivers/net/wireguard/socket.c | 9 +++++++-- > 1 file changed, 7 insertions(+), 2 deletions(-) > > diff --git a/drivers/net/wireguard/socket.c b/drivers/net/wireguard/socket.c > index 0414d7a6ce74..c35163f503e7 100644 > --- a/drivers/net/wireguard/socket.c > +++ b/drivers/net/wireguard/socket.c > @@ -387,7 +387,7 @@ int wg_socket_init(struct wg_device *wg, u16 port) > ret = udp_sock_create(net, &port4, &new4); > if (ret < 0) { > pr_err("%s: Could not create IPv4 socket\n", wg->dev->name); > - goto out; > + goto err; `new4` is either NULL or has already been freed here in the `goto retry` case. `new6` is NULL here. > } > set_sock_opts(new4); > setup_udp_tunnel_sock(net, new4, &cfg); > @@ -402,7 +402,7 @@ int wg_socket_init(struct wg_device *wg, u16 port) > goto retry; > pr_err("%s: Could not create IPv6 socket\n", > wg->dev->name); > - goto out; > + goto err; `new4` has just been freed by `udp_tunnel_sock_release` just above the context. `new6` is NULL. > } > set_sock_opts(new6); > setup_udp_tunnel_sock(net, new6, &cfg); > @@ -414,6 +414,11 @@ int wg_socket_init(struct wg_device *wg, u16 port) > out: > put_net(net); > return ret; > + > +err: > + sock_free(new4 ? new4->sk : NULL); > + sock_free(new6 ? new6->sk : NULL); > + goto out; > } > > void wg_socket_reinit(struct wg_device *wg, struct sock *new4, I don't see the bug. If there is one, maybe try again with a real patch that describes it better. If there isn't one, what is the point? Jason From winebugs140 at gmail.com Mon Oct 9 18:51:15 2023 From: winebugs140 at gmail.com (Jan Kleks) Date: Mon, 9 Oct 2023 20:51:15 +0200 Subject: WireGuard Android TV app feature parity? Message-ID: Hello, Are there any plans to implement the features from the standard Android app into the Android TV one? There is no info on the TODO list regarding this matter: https://docs.google.com/document/d/15i0Z750eL_OlUyrypUFLF7Hoi5eoF-dTFWj-Rs4RXGI The TV app is really barebones, there is even no split tunneling setting... Regards Jan Kleks From ngoossens at duckduckgo.com Wed Oct 11 11:14:13 2023 From: ngoossens at duckduckgo.com (Nick Goossens) Date: Wed, 11 Oct 2023 13:14:13 +0200 Subject: [PATCH] tun/netstack: close stack on netTun close Message-ID: <20231011111425.3329-1-ngoossens@duckduckgo.com> If the stack is not closed when closing netTun then goroutines will be leaked because the the transport protocols remain open. This adds the needed close call. --- tun/netstack/tun.go | 1 + 1 file changed, 1 insertion(+) diff --git a/tun/netstack/tun.go b/tun/netstack/tun.go index 2b73054..a06bc21 100644 --- a/tun/netstack/tun.go +++ b/tun/netstack/tun.go @@ -167,6 +167,7 @@ func (tun *netTun) WriteNotify() { func (tun *netTun) Close() error { tun.stack.RemoveNIC(1) + tun.stack.Close() if tun.events != nil { close(tun.events) -- 2.42.0 From ngoossens at duckduckgo.com Mon Oct 23 14:33:47 2023 From: ngoossens at duckduckgo.com (Nick Goossens) Date: Mon, 23 Oct 2023 16:33:47 +0200 Subject: [PATCH] tun/netstack: close stack on netTun close Message-ID: <20231023143356.6889-1-ngoossens@duckduckgo.com> If the stack is not closed when closing netTun then goroutines will be leaked because the the transport protocols remain open. This adds the needed close call. Signed-off-by: Nick Goossens --- tun/netstack/tun.go | 1 + 1 file changed, 1 insertion(+) diff --git a/tun/netstack/tun.go b/tun/netstack/tun.go index 2b73054..a06bc21 100644 --- a/tun/netstack/tun.go +++ b/tun/netstack/tun.go @@ -167,6 +167,7 @@ func (tun *netTun) WriteNotify() { func (tun *netTun) Close() error { tun.stack.RemoveNIC(1) + tun.stack.Close() if tun.events != nil { close(tun.events) -- 2.42.0 From dxld at darkboxed.org Mon Oct 23 15:59:50 2023 From: dxld at darkboxed.org (Daniel =?utf-8?Q?Gr=C3=B6ber?=) Date: Mon, 23 Oct 2023 17:59:50 +0200 Subject: [PATCH] wireguard: Fix leaking sockets in wg_socket_init error paths In-Reply-To: References: <20231023130609.595122-1-dxld@darkboxed.org> Message-ID: <20231023155950.oyl2olisob6dnvwo@House.clients.dxld.at> Hi Jason, On Mon, Oct 23, 2023 at 04:04:13PM +0200, Jason A. Donenfeld wrote: > The signed-off-by is missing and the subject does not match the format > of any other wireguard commits. Ah, I don't usually send kernel patches. Forgot to do format.signOff=true. > On Mon, Oct 23, 2023 at 03:06:09PM +0200, Daniel Gr?ber wrote: > > This doesn't seem to be reachable normally, but while working on a patch > > "Normally" as in what? At all? Or? I committed this while working on my address/ifindex binding patch[1] (which I will also resend shortly), at the time I thought this fix makes sense in isolation but apparently not. [1]: https://lists.zx2c4.com/pipermail/wireguard/2023-August/008148.html, > > for the address binding code I ended up triggering this leak and had to > > reboot to get rid of the leaking wg sockets. > > This commit message doesn't describe any rationale for this patch. Can > you describe the bug? It's been a while since I wrote this patch. Unfortunately you didn't respond to my initial mail in Aug, so some context has already been lost to time. I may have been under the mistaken impression that udp_sock_create can return <0 while leaving *sockp!=NULL, but as I recall it I did re-test with this patch and it fixed the bug, that I wish I remembered how to trigger now. Unsatisfying. --Daniel From dxld at darkboxed.org Mon Oct 23 16:00:06 2023 From: dxld at darkboxed.org (=?UTF-8?q?Daniel=20Gr=C3=B6ber?=) Date: Mon, 23 Oct 2023 18:00:06 +0200 Subject: [PATCH] wireguard: Add netlink attrs for binding to address and netdev Message-ID: <20231023160006.85992-1-dxld@darkboxed.org> Multihomed hosts may want to run distinct wg tunnels across all their uplinks for redundant connectivity. Currently this entails picking different ports for each wg tunnel since we allow only binding to the wildcard address. Sharing a single port-number for all uplink connections (but bound to a particular IP/netdev) simplifies managment considerably. A closely related use-case that also touches the socket binding code is having a wg socket be part of a VRF. This mirrors how we support socket and wg device in distinct namespaces. To make using VRFs with wg easy we want to be able to bind to a particular device as this will cause the kernel to automatically route all outgoing packets with the VRF's routing table and (in the default udp_l3mdev_accept=0 config) only accept packets from interfaces in the VRF without the need for netfilter rules. While users can currently use VRFs for wg tunnel traffic by configuring fwmark ip-rules and setting sysctl udp_l3mdev_accept=1 (with or without additional nft filtering) this is at best a cludge. When VRF membership changes it becomes a major hassle to keep ip-rules up to date. Signed-off-by: Daniel Gr?ber --- drivers/net/wireguard/device.c | 4 +-- drivers/net/wireguard/device.h | 3 +- drivers/net/wireguard/netlink.c | 51 ++++++++++++++++++++++++++++----- drivers/net/wireguard/socket.c | 41 +++++++++++++++++--------- drivers/net/wireguard/socket.h | 3 +- include/uapi/linux/wireguard.h | 6 ++++ 6 files changed, 83 insertions(+), 25 deletions(-) diff --git a/drivers/net/wireguard/device.c b/drivers/net/wireguard/device.c index 258dcc103921..fdaaf0238a49 100644 --- a/drivers/net/wireguard/device.c +++ b/drivers/net/wireguard/device.c @@ -48,7 +48,7 @@ static int wg_open(struct net_device *dev) dev_v6->cnf.addr_gen_mode = IN6_ADDR_GEN_MODE_NONE; mutex_lock(&wg->device_update_lock); - ret = wg_socket_init(wg, wg->incoming_port); + ret = wg_socket_init(wg, wg->port_cfg); if (ret < 0) goto out; list_for_each_entry(peer, &wg->peer_list, peer_list) { @@ -249,7 +249,7 @@ static void wg_destruct(struct net_device *dev) rtnl_unlock(); mutex_lock(&wg->device_update_lock); rcu_assign_pointer(wg->creating_net, NULL); - wg->incoming_port = 0; + memzero_explicit(&wg->port_cfg, sizeof(wg->port_cfg)); wg_socket_reinit(wg, NULL, NULL); /* The final references are cleared in the below calls to destroy_workqueue. */ wg_peer_remove_all(wg); diff --git a/drivers/net/wireguard/device.h b/drivers/net/wireguard/device.h index 43c7cebbf50b..ac4092d8c9d0 100644 --- a/drivers/net/wireguard/device.h +++ b/drivers/net/wireguard/device.h @@ -17,6 +17,7 @@ #include #include #include +#include struct wg_device; @@ -53,7 +54,7 @@ struct wg_device { atomic_t handshake_queue_len; unsigned int num_peers, device_update_gen; u32 fwmark; - u16 incoming_port; + struct udp_port_cfg port_cfg; }; int wg_device_init(void); diff --git a/drivers/net/wireguard/netlink.c b/drivers/net/wireguard/netlink.c index dc09b75a3248..28e5b8668e72 100644 --- a/drivers/net/wireguard/netlink.c +++ b/drivers/net/wireguard/netlink.c @@ -26,6 +26,8 @@ static const struct nla_policy device_policy[WGDEVICE_A_MAX + 1] = { [WGDEVICE_A_PUBLIC_KEY] = NLA_POLICY_EXACT_LEN(NOISE_PUBLIC_KEY_LEN), [WGDEVICE_A_FLAGS] = { .type = NLA_U32 }, [WGDEVICE_A_LISTEN_PORT] = { .type = NLA_U16 }, + [WGDEVICE_A_LISTEN_ADDR] = NLA_POLICY_MIN_LEN(sizeof(struct in_addr)), + [WGDEVICE_A_LISTEN_IFINDEX] = { .type = NLA_U32 }, [WGDEVICE_A_FWMARK] = { .type = NLA_U32 }, [WGDEVICE_A_PEERS] = { .type = NLA_NESTED } }; @@ -230,11 +232,20 @@ static int wg_get_device_dump(struct sk_buff *skb, struct netlink_callback *cb) if (!ctx->next_peer) { if (nla_put_u16(skb, WGDEVICE_A_LISTEN_PORT, - wg->incoming_port) || + ntohs(wg->port_cfg.local_udp_port)) || + nla_put_u32(skb, WGDEVICE_A_LISTEN_IFINDEX, wg->port_cfg.bind_ifindex) || nla_put_u32(skb, WGDEVICE_A_FWMARK, wg->fwmark) || nla_put_u32(skb, WGDEVICE_A_IFINDEX, wg->dev->ifindex) || nla_put_string(skb, WGDEVICE_A_IFNAME, wg->dev->name)) goto out; + if (wg->port_cfg.family == AF_INET && + nla_put_in_addr(skb, WGDEVICE_A_LISTEN_ADDR, + wg->port_cfg.local_ip.s_addr)) + goto out; + if (wg->port_cfg.family == AF_INET6 && + nla_put_in6_addr(skb, WGDEVICE_A_LISTEN_ADDR, + &wg->port_cfg.local_ip6)) + goto out; down_read(&wg->static_identity.lock); if (wg->static_identity.has_identity) { @@ -311,19 +322,46 @@ static int wg_get_device_done(struct netlink_callback *cb) return 0; } -static int set_port(struct wg_device *wg, u16 port) +static int set_port_cfg(struct wg_device *wg, struct nlattr **attrs) { struct wg_peer *peer; + struct udp_port_cfg port_cfg = { + .family = AF_UNSPEC, + }; + + if (attrs[WGDEVICE_A_LISTEN_PORT]) + port_cfg.local_udp_port = + htons(nla_get_u16(attrs[WGDEVICE_A_LISTEN_PORT])); + if (attrs[WGDEVICE_A_LISTEN_ADDR]) { + union { + struct in_addr addr4; + struct in6_addr addr6; + } *u_addr = nla_data(attrs[WGDEVICE_A_LISTEN_ADDR]); + size_t len = nla_len(attrs[WGDEVICE_A_LISTEN_ADDR]); + if (len == sizeof(struct in_addr)) { + port_cfg.family = AF_INET; + port_cfg.local_ip = u_addr->addr4; + } else if (len == sizeof(struct in6_addr)) { + if (!IS_ENABLED(CONFIG_IPV6)) + return -EAFNOSUPPORT; + port_cfg.family = AF_INET6; + port_cfg.local_ip6 = u_addr->addr6; + } + } + if (attrs[WGDEVICE_A_LISTEN_IFINDEX]) { + port_cfg.bind_ifindex = + nla_get_u32(attrs[WGDEVICE_A_LISTEN_IFINDEX]); + } - if (wg->incoming_port == port) + if (memcmp(&port_cfg, &wg->port_cfg, sizeof(port_cfg)) == 0) return 0; list_for_each_entry(peer, &wg->peer_list, peer_list) wg_socket_clear_peer_endpoint_src(peer); if (!netif_running(wg->dev)) { - wg->incoming_port = port; + wg->port_cfg = port_cfg; return 0; } - return wg_socket_init(wg, port); + return wg_socket_init(wg, port_cfg); } static int set_allowedip(struct wg_peer *peer, struct nlattr **attrs) @@ -531,8 +569,7 @@ static int wg_set_device(struct sk_buff *skb, struct genl_info *info) } if (info->attrs[WGDEVICE_A_LISTEN_PORT]) { - ret = set_port(wg, - nla_get_u16(info->attrs[WGDEVICE_A_LISTEN_PORT])); + ret = set_port_cfg(wg, info->attrs); if (ret) goto out; } diff --git a/drivers/net/wireguard/socket.c b/drivers/net/wireguard/socket.c index c35163f503e7..cbd3958af890 100644 --- a/drivers/net/wireguard/socket.c +++ b/drivers/net/wireguard/socket.c @@ -346,7 +346,7 @@ static void set_sock_opts(struct socket *sock) sk_set_memalloc(sock->sk); } -int wg_socket_init(struct wg_device *wg, u16 port) +int wg_socket_init(struct wg_device *wg, struct udp_port_cfg port_cfg) { struct net *net; int ret; @@ -356,12 +356,7 @@ int wg_socket_init(struct wg_device *wg, u16 port) .encap_rcv = wg_receive }; struct socket *new4 = NULL, *new6 = NULL; - struct udp_port_cfg port4 = { - .family = AF_INET, - .local_ip.s_addr = htonl(INADDR_ANY), - .local_udp_port = htons(port), - .use_udp_checksums = true - }; + struct udp_port_cfg port4; #if IS_ENABLED(CONFIG_IPV6) int retries = 0; struct udp_port_cfg port6 = { @@ -373,6 +368,23 @@ int wg_socket_init(struct wg_device *wg, u16 port) }; #endif + if (port_cfg.family == AF_UNSPEC) { + port4 = (struct udp_port_cfg) { + .family = AF_INET, + .local_ip.s_addr = htonl(INADDR_ANY), + .local_udp_port = port_cfg.local_udp_port, + .use_udp_checksums = true + }; + } else { + port4 = port_cfg; + port4.use_udp_checksums = true; + if (IS_ENABLED(CONFIG_IPV6) && port_cfg.family == AF_INET6) { + port4.use_udp6_tx_checksums = true; + port4.use_udp6_rx_checksums = true; + port4.ipv6_v6only = true; + } + } + rcu_read_lock(); net = rcu_dereference(wg->creating_net); net = net ? maybe_get_net(net) : NULL; @@ -380,10 +392,6 @@ int wg_socket_init(struct wg_device *wg, u16 port) if (unlikely(!net)) return -ENONET; -#if IS_ENABLED(CONFIG_IPV6) -retry: -#endif - ret = udp_sock_create(net, &port4, &new4); if (ret < 0) { pr_err("%s: Could not create IPv4 socket\n", wg->dev->name); @@ -392,13 +400,18 @@ int wg_socket_init(struct wg_device *wg, u16 port) set_sock_opts(new4); setup_udp_tunnel_sock(net, new4, &cfg); + if (port_cfg.family != AF_UNSPEC) + goto reinit; + #if IS_ENABLED(CONFIG_IPV6) +retry: if (ipv6_mod_enabled()) { port6.local_udp_port = inet_sk(new4->sk)->inet_sport; ret = udp_sock_create(net, &port6, &new6); if (ret < 0) { udp_tunnel_sock_release(new4); - if (ret == -EADDRINUSE && !port && retries++ < 100) + if (ret == -EADDRINUSE && !port_cfg.local_udp_port && + retries++ < 100) goto retry; pr_err("%s: Could not create IPv6 socket\n", wg->dev->name); @@ -409,6 +422,8 @@ int wg_socket_init(struct wg_device *wg, u16 port) } #endif +reinit: + wg->port_cfg = port_cfg; wg_socket_reinit(wg, new4->sk, new6 ? new6->sk : NULL); ret = 0; out: @@ -433,8 +448,6 @@ void wg_socket_reinit(struct wg_device *wg, struct sock *new4, lockdep_is_held(&wg->socket_update_lock)); rcu_assign_pointer(wg->sock4, new4); rcu_assign_pointer(wg->sock6, new6); - if (new4) - wg->incoming_port = ntohs(inet_sk(new4)->inet_sport); mutex_unlock(&wg->socket_update_lock); synchronize_net(); sock_free(old4); diff --git a/drivers/net/wireguard/socket.h b/drivers/net/wireguard/socket.h index bab5848efbcd..1532a263c518 100644 --- a/drivers/net/wireguard/socket.h +++ b/drivers/net/wireguard/socket.h @@ -10,8 +10,9 @@ #include #include #include +#include -int wg_socket_init(struct wg_device *wg, u16 port); +int wg_socket_init(struct wg_device *wg, struct udp_port_cfg port); void wg_socket_reinit(struct wg_device *wg, struct sock *new4, struct sock *new6); int wg_socket_send_buffer_to_peer(struct wg_peer *peer, void *data, diff --git a/include/uapi/linux/wireguard.h b/include/uapi/linux/wireguard.h index ae88be14c947..240d1c850dfd 100644 --- a/include/uapi/linux/wireguard.h +++ b/include/uapi/linux/wireguard.h @@ -28,6 +28,8 @@ * 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_LISTEN_ADDR : NLA_MIN_LEN(struct sockaddr), struct sockaddr_in or struct sockaddr_in6 + * WGDEVICE_A_LISTEN_IFINDEX : NLA_U32 * WGDEVICE_A_FWMARK: NLA_U32 * WGDEVICE_A_PEERS: NLA_NESTED * 0: NLA_NESTED @@ -82,6 +84,8 @@ * 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_LISTEN_ADDR : struct sockaddr_in or struct sockaddr_in6. + * WGDEVICE_A_LISTEN_IFINDEX : NLA_U32 * WGDEVICE_A_FWMARK: NLA_U32, 0 to disable * WGDEVICE_A_PEERS: NLA_NESTED * 0: NLA_NESTED @@ -157,6 +161,8 @@ enum wgdevice_attribute { WGDEVICE_A_LISTEN_PORT, WGDEVICE_A_FWMARK, WGDEVICE_A_PEERS, + WGDEVICE_A_LISTEN_ADDR, + WGDEVICE_A_LISTEN_IFINDEX, __WGDEVICE_A_LAST }; #define WGDEVICE_A_MAX (__WGDEVICE_A_LAST - 1) -- 2.39.2 From dxld at darkboxed.org Mon Oct 23 17:08:58 2023 From: dxld at darkboxed.org (=?UTF-8?q?Daniel=20Gr=C3=B6ber?=) Date: Mon, 23 Oct 2023 19:08:58 +0200 Subject: [PATCH v2 4/6] wg: Check sockaddr_inet field offsets against system sockaddr In-Reply-To: <20231023170900.131473-1-dxld@darkboxed.org> References: <20231023170900.131473-1-dxld@darkboxed.org> Message-ID: <20231023170900.131473-4-dxld@darkboxed.org> Some systems may have the sockaddr fields in a different arrangement and need #ifdef'ing this makes this obvious to any future porters. Signed-off-by: Daniel Gr?ber --- src/containers.h | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/src/containers.h b/src/containers.h index 2f3d88f..357df77 100644 --- a/src/containers.h +++ b/src/containers.h @@ -8,6 +8,8 @@ #include #include +#include +#include #include #include #include @@ -44,6 +46,25 @@ struct sockaddr_inet { }; }; +#if defined(_MSC_VER) +#define static_assert(x) static_assert(x, #x) +#elif !defined(static_assert) +#define static_assert(x) _Static_assert((x), #x) +#endif + +static_assert(offsetof(struct sockaddr_in, sin_port) == + offsetof(struct sockaddr_inet, sinet_port)); +static_assert(offsetof(struct sockaddr_in6, sin6_port) == + offsetof(struct sockaddr_inet, sinet_port)); + +#define assert_offsets_match(tyx, tyy, field) \ + static_assert(offsetof(tyx, field) == offsetof(tyy, field)) + +assert_offsets_match(struct sockaddr_in, struct sockaddr_inet, sin_addr); +assert_offsets_match(struct sockaddr_in6, struct sockaddr_inet, sin6_flowinfo); +assert_offsets_match(struct sockaddr_in6, struct sockaddr_inet, sin6_addr); +assert_offsets_match(struct sockaddr_in6, struct sockaddr_inet, sin6_scope_id); + struct wgallowedip { uint16_t family; union { -- 2.39.2 From dxld at darkboxed.org Mon Oct 23 17:08:55 2023 From: dxld at darkboxed.org (=?UTF-8?q?Daniel=20Gr=C3=B6ber?=) Date: Mon, 23 Oct 2023 19:08:55 +0200 Subject: [PATCH v2 1/6] uapi/linux: Add definitions for address/netdev bound listen sockets Message-ID: <20231023170900.131473-1-dxld@darkboxed.org> Signed-off-by: Daniel Gr?ber --- src/uapi/linux/linux/wireguard.h | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/uapi/linux/linux/wireguard.h b/src/uapi/linux/linux/wireguard.h index 0efd52c..36afb66 100644 --- a/src/uapi/linux/linux/wireguard.h +++ b/src/uapi/linux/linux/wireguard.h @@ -28,6 +28,8 @@ * 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_LISTEN_ADDR : NLA_MIN_LEN(struct sockaddr), struct sockaddr_in or struct sockaddr_in6 + * WGDEVICE_A_LISTEN_IFINDEX : NLA_U32 * WGDEVICE_A_FWMARK: NLA_U32 * WGDEVICE_A_PEERS: NLA_NESTED * 0: NLA_NESTED @@ -82,6 +84,8 @@ * 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_LISTEN_ADDR : struct sockaddr_in or struct sockaddr_in6. + * WGDEVICE_A_LISTEN_IFINDEX : NLA_U32 * WGDEVICE_A_FWMARK: NLA_U32, 0 to disable * WGDEVICE_A_PEERS: NLA_NESTED * 0: NLA_NESTED @@ -157,6 +161,8 @@ enum wgdevice_attribute { WGDEVICE_A_LISTEN_PORT, WGDEVICE_A_FWMARK, WGDEVICE_A_PEERS, + WGDEVICE_A_LISTEN_ADDR, + WGDEVICE_A_LISTEN_IFINDEX, __WGDEVICE_A_LAST }; #define WGDEVICE_A_MAX (__WGDEVICE_A_LAST - 1) -- 2.39.2 From dxld at darkboxed.org Mon Oct 23 17:08:56 2023 From: dxld at darkboxed.org (=?UTF-8?q?Daniel=20Gr=C3=B6ber?=) Date: Mon, 23 Oct 2023 19:08:56 +0200 Subject: [PATCH v2 2/6] wg: Support binding to specific addr and iface for multihomed hosts In-Reply-To: <20231023170900.131473-1-dxld@darkboxed.org> References: <20231023170900.131473-1-dxld@darkboxed.org> Message-ID: <20231023170900.131473-2-dxld@darkboxed.org> Signed-off-by: Daniel Gr?ber --- src/config.c | 116 +++++++++++++++++++++++++++------------------- src/containers.h | 33 +++++++++++-- src/ipc-freebsd.h | 4 ++ src/ipc-linux.h | 38 ++++++++++++++- src/ipc-openbsd.h | 4 ++ src/ipc-uapi.h | 2 + src/ipc-windows.h | 4 ++ src/man/wg.8 | 27 +++++++---- src/set.c | 2 +- src/show.c | 65 +++++++++++++++++++++++--- src/show.h | 13 ++++++ src/showconf.c | 12 +++-- 12 files changed, 246 insertions(+), 74 deletions(-) create mode 100644 src/show.h diff --git a/src/config.c b/src/config.c index f9980fe..01c73f9 100644 --- a/src/config.c +++ b/src/config.c @@ -36,44 +36,6 @@ static const char *get_value(const char *line, const char *key) 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; @@ -192,10 +154,12 @@ static inline int parse_dns_retries(void) return (int)ret; } -static inline bool parse_endpoint(struct sockaddr *endpoint, const char *value, int family) +static inline bool parse_endpoint(struct sockaddr_inet *endpoint, const char *value, int family, int allow_retry) { + bool ok; char *mutable = strdup(value); char *begin, *end; + char *scope = NULL; int ret, retries = parse_dns_retries(); struct addrinfo *resolved; struct addrinfo hints = { @@ -203,6 +167,8 @@ static inline bool parse_endpoint(struct sockaddr *endpoint, const char *value, .ai_socktype = SOCK_DGRAM, .ai_protocol = IPPROTO_UDP }; + if (!allow_retry) + retries = 0; if (!mutable) { perror("strdup"); return false; @@ -214,16 +180,20 @@ static inline bool parse_endpoint(struct sockaddr *endpoint, const char *value, } if (mutable[0] == '[') { begin = &mutable[1]; + + scope = strchr(begin, '%'); + if (scope) + scope++; end = strchr(mutable, ']'); if (!end) { free(mutable); - fprintf(stderr, "Unable to find matching brace of endpoint: `%s'\n", value); + fprintf(stderr, "Unable to find matching brace in address: `%s'\n", value); return false; } *end++ = '\0'; if (*end++ != ':' || !*end) { free(mutable); - fprintf(stderr, "Unable to find port of endpoint: `%s'\n", value); + fprintf(stderr, "Unable to find port in address: `%s'\n", value); return false; } } else { @@ -231,7 +201,7 @@ static inline bool parse_endpoint(struct sockaddr *endpoint, const char *value, end = strrchr(mutable, ':'); if (!end || !*(end + 1)) { free(mutable); - fprintf(stderr, "Unable to find port of endpoint: `%s'\n", value); + fprintf(stderr, "Unable to find port in address: `%s'\n", value); return false; } *end++ = '\0'; @@ -269,16 +239,59 @@ static inline bool parse_endpoint(struct sockaddr *endpoint, const char *value, (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); + ok = false; fprintf(stderr, "Neither IPv4 nor IPv6 address found: `%s'\n", value); - return false; + goto out; + } + if(scope) { + unsigned ifindex = if_nametoindex(scope); + if (resolved->ai_family == AF_INET) + endpoint->sin_scope_id = ifindex; + else if (resolved->ai_family == AF_INET6) + endpoint->sin6_scope_id = ifindex; } + + ok = true; +out: freeaddrinfo(resolved); free(mutable); + return ok; +} + + +static inline bool parse_listen(struct sockaddr_inet *listen, uint32_t *flags, const char *value) +{ + if (!parse_endpoint(listen, value, AF_UNSPEC, /*allow_retry=*/0)) + return false; + + listen->sinet_port = ntohs(listen->sinet_port); + + *flags |= WGDEVICE_HAS_LISTEN; return true; } +static inline bool parse_port(struct sockaddr_inet *listen, uint32_t *flags, const char *value) +{ + bool err; + char *addr_str = NULL; + asprintf(&addr_str, "[::]:%s", value); + if (!addr_str) { + perror("asprintf"); + return false; + } + + err = parse_listen(listen, flags, addr_str); + free(addr_str); + + listen->sinet_family = AF_UNSPEC; + + if (!err) { + *flags |= WGDEVICE_HAS_LISTEN_PORT; + *flags &= ~WGDEVICE_HAS_LISTEN; + } + return err; +} + static inline bool parse_address_family(int *family, const char *value) { if (strcmp(value, "inet") == 0) @@ -457,7 +470,9 @@ static bool process_line(struct config_ctx *ctx, const char *line) if (ctx->is_device_section) { if (key_match("ListenPort")) - ret = parse_port(&ctx->device->listen_port, &ctx->device->flags, value); + ret = parse_port(&ctx->device->listen_inet, &ctx->device->flags, value); + else if (key_match("Listen")) + ret = parse_listen(&ctx->device->listen_inet, &ctx->device->flags, value); else if (key_match("FwMark")) ret = parse_fwmark(&ctx->device->fwmark, &ctx->device->flags, value); else if (key_match("PrivateKey")) { @@ -561,7 +576,7 @@ struct wgdevice *config_read_finish(struct wgdevice *device) goto err; } - if (!parse_endpoint(&peer->endpoint.addr, peer->endpoint_value, peer->addr_fam)) + if (!parse_endpoint(&peer->endpoint.addr_inet, peer->endpoint_value, peer->addr_fam, /*allow_retry=*/1)) goto err; } return device; @@ -600,7 +615,12 @@ struct wgdevice *config_read_cmd(const char *argv[], int argc) } while (argc > 0) { if (!strcmp(argv[0], "listen-port") && argc >= 2 && !peer) { - if (!parse_port(&device->listen_port, &device->flags, argv[1])) + if (!parse_port(&device->listen_inet, &device->flags, argv[1])) + goto error; + argv += 2; + argc -= 2; + } else if (!strcmp(argv[0], "listen") && argc >= 2 && !peer) { + if (!parse_listen(&device->listen_inet, &device->flags, argv[1])) goto error; argv += 2; argc -= 2; diff --git a/src/containers.h b/src/containers.h index c111621..2f3d88f 100644 --- a/src/containers.h +++ b/src/containers.h @@ -13,7 +13,7 @@ #include #include #if defined(__linux__) -#include +#include "uapi/linux/linux/wireguard.h" #elif defined(__OpenBSD__) #include #endif @@ -28,6 +28,22 @@ struct timespec64 { int64_t tv_nsec; }; +struct sockaddr_inet { + sa_family_t sinet_family; + in_port_t sinet_port; + union { + struct { + struct in_addr sin_addr; + uint32_t sin_scope_id; // on top of sockaddr_in padding + }; + struct { + uint32_t sin6_flowinfo; + struct in6_addr sin6_addr; + uint32_t sin6_scope_id; + }; + }; +}; + struct wgallowedip { uint16_t family; union { @@ -57,6 +73,7 @@ struct wgpeer { struct sockaddr addr; struct sockaddr_in addr4; struct sockaddr_in6 addr6; + struct sockaddr_inet addr_inet; } endpoint; int addr_fam; @@ -74,7 +91,8 @@ enum { WGDEVICE_HAS_PRIVATE_KEY = 1U << 1, WGDEVICE_HAS_PUBLIC_KEY = 1U << 2, WGDEVICE_HAS_LISTEN_PORT = 1U << 3, - WGDEVICE_HAS_FWMARK = 1U << 4 + WGDEVICE_HAS_LISTEN = 1U << 4, + WGDEVICE_HAS_FWMARK = 1U << 5, }; struct wgdevice { @@ -87,7 +105,16 @@ struct wgdevice { uint8_t private_key[WG_KEY_LEN]; uint32_t fwmark; - uint16_t listen_port; + union { + struct sockaddr listen; + struct sockaddr_in listen4; + struct sockaddr_in6 listen6; + struct sockaddr_inet listen_inet; + struct { + sa_family_t listen_family; + in_port_t listen_port; + }; + }; struct wgpeer *first_peer, *last_peer; }; diff --git a/src/ipc-freebsd.h b/src/ipc-freebsd.h index fa74edd..a06b245 100644 --- a/src/ipc-freebsd.h +++ b/src/ipc-freebsd.h @@ -272,6 +272,10 @@ static int kernel_set_device(struct wgdevice *dev) 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_LISTEN) { + errno = EOPNOTSUPP; + goto err; + } if (dev->flags & WGDEVICE_HAS_FWMARK) nvlist_add_number(nvl_device, "user-cookie", dev->fwmark); if (dev->flags & WGDEVICE_REPLACE_PEERS) diff --git a/src/ipc-linux.h b/src/ipc-linux.h index d29c0c5..3e3f27c 100644 --- a/src/ipc-linux.h +++ b/src/ipc-linux.h @@ -17,11 +17,11 @@ #include #include #include -#include #include #include "containers.h" #include "encoding.h" #include "netlink.h" +#include "uapi/linux/linux/wireguard.h" #define IPC_SUPPORTS_KERNEL_INTERFACE @@ -163,6 +163,17 @@ again: 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_LISTEN) { + mnl_attr_put_u16(nlh, WGDEVICE_A_LISTEN_PORT, dev->listen_port); + if (dev->listen_family == AF_INET) { + mnl_attr_put(nlh, WGDEVICE_A_LISTEN_ADDR, sizeof(struct in_addr), &dev->listen4.sin_addr); + mnl_attr_put_u32(nlh, WGDEVICE_A_LISTEN_IFINDEX, dev->listen_inet.sin_scope_id); + } else if (dev->listen_family == AF_INET6) { + mnl_attr_put(nlh, WGDEVICE_A_LISTEN_ADDR, sizeof(struct in6_addr), &dev->listen6.sin6_addr); + mnl_attr_put_u32(nlh, WGDEVICE_A_LISTEN_IFINDEX, dev->listen_inet.sin6_scope_id); + } + } + if (dev->flags & WGDEVICE_HAS_FWMARK) mnl_attr_put_u32(nlh, WGDEVICE_A_FWMARK, dev->fwmark); if (dev->flags & WGDEVICE_REPLACE_PEERS) @@ -406,6 +417,8 @@ static int parse_device(const struct nlattr *attr, void *data) { struct wgdevice *device = data; + uint32_t listen_ifindex = 0; + switch (mnl_attr_get_type(attr)) { case WGDEVICE_A_UNSPEC: break; @@ -435,6 +448,24 @@ static int parse_device(const struct nlattr *attr, void *data) if (!mnl_attr_validate(attr, MNL_TYPE_U16)) device->listen_port = mnl_attr_get_u16(attr); break; + case WGDEVICE_A_LISTEN_ADDR: { + union { + struct in_addr addr4; + struct in6_addr addr6; + } *u = mnl_attr_get_payload(attr); + if (mnl_attr_get_payload_len(attr) == sizeof(u->addr4)) { + device->listen4.sin_family = AF_INET; + memcpy(&device->listen4.sin_addr, &u->addr4, sizeof(device->listen4.sin_addr)); + } else if (mnl_attr_get_payload_len(attr) == sizeof(u->addr6)) { + device->listen6.sin6_family = AF_INET6; + memcpy(&device->listen6.sin6_addr, &u->addr6, sizeof(device->listen6.sin6_addr)); + } + break; + } + case WGDEVICE_A_LISTEN_IFINDEX: + if (!mnl_attr_validate(attr, MNL_TYPE_U32)) + listen_ifindex = mnl_attr_get_u32(attr); + break; case WGDEVICE_A_FWMARK: if (!mnl_attr_validate(attr, MNL_TYPE_U32)) device->fwmark = mnl_attr_get_u32(attr); @@ -443,6 +474,11 @@ static int parse_device(const struct nlattr *attr, void *data) return mnl_attr_parse_nested(attr, parse_peers, device); } + if (listen_ifindex && device->listen_family == AF_INET) + device->listen_inet.sin_scope_id = listen_ifindex; + else if (listen_ifindex && device->listen_family == AF_INET6) + device->listen6.sin6_scope_id = listen_ifindex; + return MNL_CB_OK; } diff --git a/src/ipc-openbsd.h b/src/ipc-openbsd.h index 03fbdb5..eddec45 100644 --- a/src/ipc-openbsd.h +++ b/src/ipc-openbsd.h @@ -212,6 +212,10 @@ static int kernel_set_device(struct wgdevice *dev) wg_iface->i_port = dev->listen_port; wg_iface->i_flags |= WG_INTERFACE_HAS_PORT; } + if (dev->flags & WGDEVICE_HAS_LISTEN) { + errno = EOPNOTSUPP; + goto out; + } if (dev->flags & WGDEVICE_HAS_FWMARK) { wg_iface->i_rtable = dev->fwmark; diff --git a/src/ipc-uapi.h b/src/ipc-uapi.h index f582916..7079fbd 100644 --- a/src/ipc-uapi.h +++ b/src/ipc-uapi.h @@ -47,6 +47,8 @@ static int userspace_set_device(struct wgdevice *dev) } if (dev->flags & WGDEVICE_HAS_LISTEN_PORT) fprintf(f, "listen_port=%u\n", dev->listen_port); + if (dev->flags & WGDEVICE_HAS_LISTEN) + return -EOPNOTSUPP; if (dev->flags & WGDEVICE_HAS_FWMARK) fprintf(f, "fwmark=%u\n", dev->fwmark); if (dev->flags & WGDEVICE_REPLACE_PEERS) diff --git a/src/ipc-windows.h b/src/ipc-windows.h index d237fc9..77e32b3 100644 --- a/src/ipc-windows.h +++ b/src/ipc-windows.h @@ -381,6 +381,10 @@ static int kernel_set_device(struct wgdevice *dev) wg_iface->ListenPort = dev->listen_port; wg_iface->Flags |= WG_IOCTL_INTERFACE_HAS_LISTEN_PORT; } + if (dev->flags & WGDEVICE_HAS_LISTEN) { + errno = EOPNOTSUPP; + goto out; + } if (dev->flags & WGDEVICE_REPLACE_PEERS) wg_iface->Flags |= WG_IOCTL_INTERFACE_REPLACE_PEERS; diff --git a/src/man/wg.8 b/src/man/wg.8 index 48f084d..debfada 100644 --- a/src/man/wg.8 +++ b/src/man/wg.8 @@ -36,7 +36,7 @@ Sub-commands that take an INTERFACE must be passed a WireGuard interface. .SH COMMANDS .TP -\fBshow\fP { \fI\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] +\fBshow\fP { \fI\fP | \fIall\fP | \fIinterfaces\fP } [\fIpublic-key\fP | \fIprivate-key\fP | \fIlisten-port\fP | \fIlisten\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\fP. If no \fI\fP is specified, \fI\fP defaults to \fIall\fP. If \fIinterfaces\fP is specified, prints a list of all WireGuard interfaces, @@ -46,7 +46,7 @@ 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, +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. @@ -55,11 +55,13 @@ transfer-rx, transfer-tx, persistent-keepalive. Shows the current configuration of \fI\fP in the format described by \fICONFIGURATION FILE FORMAT\fP below. .TP -\fBset\fP \fI\fP [\fIlisten-port\fP \fI\fP] [\fIfwmark\fP \fI\fP] [\fIprivate-key\fP \fI\fP] [\fIpeer\fP \fI\fP [\fIremove\fP] [\fIpreshared-key\fP \fI\fP] [\fIendpoint\fP \fI:\fP] [\fIaddress-family\fP \fI\fP] [\fIpersistent-keepalive\fP \fI\fP] [\fIallowed-ips\fP \fI/\fP[,\fI/\fP]...] ]... +\fBset\fP \fI\fP [\fIlisten-port\fP \fI\fP] [\fIlisten\fP \fI[%]:\fP] [\fIfwmark\fP \fI\fP] [\fIprivate-key\fP \fI\fP] [\fIpeer\fP \fI\fP [\fIremove\fP] [\fIpreshared-key\fP \fI\fP] [\fIendpoint\fP \fI:\fP] [\fIaddress-family\fP \fI\fP] [\fIpersistent-keepalive\fP \fI\fP] [\fIallowed-ips\fP \fI/\fP[,\fI/\fP]...] ]... Sets configuration values for the specified \fI\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 +for a peer, that peer is removed, not configured. The \fIlisten-port\fP +and \fIlisten\fP options override each other. If a \fIport\fP is not set +using either after the interface is created, or is 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 @@ -139,6 +141,13 @@ PrivateKeyFile \(em path to a file containing a base64 private key. May be used ListenPort \(em a 16-bit port for listening. Optional; if not specified, chosen randomly. .IP \(bu +Listen \(em an address:port tupel to use for listening. A network interface +to bind to may be specified using the [address%iface]:port form. Note that +an IPv4 address may be spcified inside square brackets, even together with an +iface. A hostname may be used instead of a numeric IP but no resolution +retries will be done so use of DNS is discouraged here. Optional. Overrides +ListenPort. +.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 @@ -162,10 +171,10 @@ which outgoing traffic for this peer is directed. The catch-all \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. +Endpoint \(em an endpoint IP (optionally enclosed in []) 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 AddressFamily \(em one of \fIinet\fP, \fIinet6\fP or \fIunspec\fP. When a hostname is given for \fIEndpoint\fP, setting this to \fIinet\fP or diff --git a/src/set.c b/src/set.c index 20ee85e..30482bd 100644 --- a/src/set.c +++ b/src/set.c @@ -18,7 +18,7 @@ int set_main(int argc, const char *argv[]) int ret = 1; if (argc < 3) { - fprintf(stderr, "Usage: %s %s [listen-port ] [fwmark ] [private-key ] [peer [remove] [preshared-key ] [endpoint :] [address-family ] [persistent-keepalive ] [allowed-ips /[,/]...] ]...\n", PROG_NAME, argv[0]); + fprintf(stderr, "Usage: %s %s [listen-port ] [listen %%:] [fwmark ] [private-key ] [peer [remove] [preshared-key ] [endpoint :] [address-family ] [persistent-keepalive ] [allowed-ips /[,/]...] ]...\n", PROG_NAME, argv[0]); return 1; } diff --git a/src/show.c b/src/show.c index 13777cf..754f952 100644 --- a/src/show.c +++ b/src/show.c @@ -18,6 +18,7 @@ #include #include +#include "show.h" #include "containers.h" #include "ipc.h" #include "terminal.h" @@ -103,7 +104,7 @@ static char *ip(const struct wgallowedip *ip) return buf; } -static char *endpoint(const struct sockaddr *addr) +char *print_endpoint(const struct sockaddr *addr) { char host[4096 + 1]; char service[512 + 1]; @@ -126,6 +127,47 @@ static char *endpoint(const struct sockaddr *addr) return buf; } +char *print_sockaddr_inet(const struct sockaddr_inet *sa_const) +{ + char host[4096 + 1], service[512 + 1], ifname_buf[IF_NAMESIZE+10] = "%"; + static char buf[sizeof(host) + sizeof(service) + sizeof(ifname_buf) + 4]; + struct sockaddr_inet sa = *sa_const; + socklen_t sa_len = 0; + unsigned int ifindex = 0; + int ret; + + sa.sinet_port = htons(sa.sinet_port); + + if (sa.sinet_family == AF_INET) { + sa_len = sizeof(struct sockaddr_in); + ifindex = sa.sin_scope_id; + } else if (sa.sinet_family == AF_INET6) { + sa_len = sizeof(struct sockaddr_in6); + ifindex = sa.sin6_scope_id; + } + ret = getnameinfo((struct sockaddr*)&sa, sa_len, host, sizeof(host), service, sizeof(service), NI_DGRAM | NI_NUMERICSERV | NI_NUMERICHOST); + if (ret) { + buf[0] = '\0'; + goto out; + } + + const char *ifname = ""; + if (ifindex) { + ifname = if_indextoname(ifindex , ifname_buf+1); + if (!ifname) { + snprintf(ifname_buf, sizeof(ifname_buf), "%%%u", ifindex); + ifname = ifname_buf; + } + } + + if ((sa.sinet_family == AF_INET6 && strchr(host, ':')) || ifindex) + snprintf(buf, sizeof(buf), "[%s%s]:%s", host, ifname, service); + else + snprintf(buf, sizeof(buf), "%s:%s", host, service); +out: + return buf; +} + static size_t pretty_time(char *buf, const size_t len, unsigned long long left) { size_t offset = 0; @@ -202,7 +244,7 @@ static char *bytes(uint64_t b) static const char *COMMAND_NAME; static void show_usage(void) { - fprintf(stderr, "Usage: %s %s { | 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); + fprintf(stderr, "Usage: %s %s { | all | interfaces } [public-key | private-key | listen-port | listen | 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) @@ -216,7 +258,9 @@ static void pretty_print(struct wgdevice *device) 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) + if (device->listen_family != AF_UNSPEC) + terminal_printf(" " TERMINAL_BOLD "listening on" TERMINAL_RESET ": %s\n", print_sockaddr_inet(&device->listen_inet)); + else 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); @@ -229,7 +273,7 @@ static void pretty_print(struct wgdevice *device) 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 "endpoint" TERMINAL_RESET ": %s\n", print_endpoint(&peer->endpoint.addr)); terminal_printf(" " TERMINAL_BOLD "allowed ips" TERMINAL_RESET ": "); if (peer->first_allowedip) { for_each_wgallowedip(peer, allowedip) @@ -259,7 +303,10 @@ static void dump_print(struct wgdevice *device, bool 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->listen_family != AF_UNSPEC) + printf("%s\t", print_sockaddr_inet(&device->listen_inet)); + else + printf("%u\t", device->listen_port); if (device->fwmark) printf("0x%x\n", device->fwmark); else @@ -270,7 +317,7 @@ static void dump_print(struct wgdevice *device, bool with_interface) 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)); + printf("%s\t", print_endpoint(&peer->endpoint.addr)); else printf("(none)\t"); if (peer->first_allowedip) { @@ -304,6 +351,10 @@ static bool ugly_print(struct wgdevice *device, const char *param, bool with_int if (with_interface) printf("%s\t", device->name); printf("%u\n", device->listen_port); + } else if (!strcmp(param, "listen")) { + if (with_interface) + printf("%s\t", device->name); + printf("%s\n", print_sockaddr_inet(&device->listen_inet)); } else if (!strcmp(param, "fwmark")) { if (with_interface) printf("%s\t", device->name); @@ -317,7 +368,7 @@ static bool ugly_print(struct wgdevice *device, const char *param, bool with_int printf("%s\t", device->name); 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)); + printf("%s\n", print_endpoint(&peer->endpoint.addr)); else printf("(none)\n"); } diff --git a/src/show.h b/src/show.h new file mode 100644 index 0000000..3673b65 --- /dev/null +++ b/src/show.h @@ -0,0 +1,13 @@ +/* SPDX-License-Identifier: GPL-2.0 OR MIT */ +/* + * Copyright (C) 2015-2020 Jason A. Donenfeld . All Rights Reserved. + */ + +#ifndef SHOW_H +#define SHOW_H +struct sockaddr_inet; + +char *print_endpoint(const struct sockaddr *addr); +char *print_sockaddr_inet(const struct sockaddr_inet *addr); + +#endif diff --git a/src/showconf.c b/src/showconf.c index 62070dc..d165eb2 100644 --- a/src/showconf.c +++ b/src/showconf.c @@ -13,6 +13,7 @@ #include #include +#include "show.h" #include "containers.h" #include "encoding.h" #include "ipc.h" @@ -22,6 +23,8 @@ int showconf_main(int argc, const char *argv[]) { char base64[WG_KEY_LEN_BASE64]; char ip[INET6_ADDRSTRLEN]; + char host[4096 + 1], service[512 + 1]; + socklen_t addr_len = 0; struct wgdevice *device = NULL; struct wgpeer *peer; struct wgallowedip *allowedip; @@ -38,7 +41,9 @@ int showconf_main(int argc, const char *argv[]) } printf("[Interface]\n"); - if (device->listen_port) + if (device->listen_family != AF_UNSPEC) + printf("Listen = %s", print_sockaddr_inet(&device->listen_inet)); + else if (device->listen_port) printf("ListenPort = %u\n", device->listen_port); if (device->fwmark) printf("FwMark = 0x%x\n", device->fwmark); @@ -72,11 +77,8 @@ int showconf_main(int argc, const char *argv[]) if (peer->first_allowedip) printf("\n"); + // TODO: use print_endpoint 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) -- 2.39.2 From dxld at darkboxed.org Mon Oct 23 17:08:57 2023 From: dxld at darkboxed.org (=?UTF-8?q?Daniel=20Gr=C3=B6ber?=) Date: Mon, 23 Oct 2023 19:08:57 +0200 Subject: [PATCH v2 3/6] wg: Store sockaddr listen port in net-byte-order as is conventional In-Reply-To: <20231023170900.131473-1-dxld@darkboxed.org> References: <20231023170900.131473-1-dxld@darkboxed.org> Message-ID: <20231023170900.131473-3-dxld@darkboxed.org> This will allow more codesharing with code dealing with the peer endpoints. Signed-off-by: Daniel Gr?ber --- src/config.c | 2 -- src/ipc-freebsd.h | 2 +- src/ipc-linux.h | 6 +++--- src/ipc-openbsd.h | 4 ++-- src/ipc-uapi.h | 2 +- src/ipc-windows.h | 4 ++-- src/show.c | 24 +++++++++++------------- src/showconf.c | 2 +- 8 files changed, 21 insertions(+), 25 deletions(-) diff --git a/src/config.c b/src/config.c index 01c73f9..5c8594b 100644 --- a/src/config.c +++ b/src/config.c @@ -264,8 +264,6 @@ static inline bool parse_listen(struct sockaddr_inet *listen, uint32_t *flags, c if (!parse_endpoint(listen, value, AF_UNSPEC, /*allow_retry=*/0)) return false; - listen->sinet_port = ntohs(listen->sinet_port); - *flags |= WGDEVICE_HAS_LISTEN; return true; } diff --git a/src/ipc-freebsd.h b/src/ipc-freebsd.h index a06b245..75f692b 100644 --- a/src/ipc-freebsd.h +++ b/src/ipc-freebsd.h @@ -271,7 +271,7 @@ static int kernel_set_device(struct wgdevice *dev) 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); + nvlist_add_number(nvl_device, "listen-port", ntohs(dev->listen_port)); if (dev->flags & WGDEVICE_HAS_LISTEN) { errno = EOPNOTSUPP; goto err; diff --git a/src/ipc-linux.h b/src/ipc-linux.h index 3e3f27c..735c49f 100644 --- a/src/ipc-linux.h +++ b/src/ipc-linux.h @@ -162,9 +162,9 @@ again: 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); + mnl_attr_put_u16(nlh, WGDEVICE_A_LISTEN_PORT, ntohs(dev->listen_port)); if (dev->flags & WGDEVICE_HAS_LISTEN) { - mnl_attr_put_u16(nlh, WGDEVICE_A_LISTEN_PORT, dev->listen_port); + mnl_attr_put_u16(nlh, WGDEVICE_A_LISTEN_PORT, ntohs(dev->listen_port)); if (dev->listen_family == AF_INET) { mnl_attr_put(nlh, WGDEVICE_A_LISTEN_ADDR, sizeof(struct in_addr), &dev->listen4.sin_addr); mnl_attr_put_u32(nlh, WGDEVICE_A_LISTEN_IFINDEX, dev->listen_inet.sin_scope_id); @@ -446,7 +446,7 @@ static int parse_device(const struct nlattr *attr, void *data) break; case WGDEVICE_A_LISTEN_PORT: if (!mnl_attr_validate(attr, MNL_TYPE_U16)) - device->listen_port = mnl_attr_get_u16(attr); + device->listen_port = htons(mnl_attr_get_u16(attr)); break; case WGDEVICE_A_LISTEN_ADDR: { union { diff --git a/src/ipc-openbsd.h b/src/ipc-openbsd.h index eddec45..478b4c6 100644 --- a/src/ipc-openbsd.h +++ b/src/ipc-openbsd.h @@ -96,7 +96,7 @@ static int kernel_get_device(struct wgdevice **device, const char *iface) } if (wg_iface->i_flags & WG_INTERFACE_HAS_PORT) { - dev->listen_port = wg_iface->i_port; + dev->listen_port = htons(wg_iface->i_port); dev->flags |= WGDEVICE_HAS_LISTEN_PORT; } @@ -209,7 +209,7 @@ static int kernel_set_device(struct wgdevice *dev) } if (dev->flags & WGDEVICE_HAS_LISTEN_PORT) { - wg_iface->i_port = dev->listen_port; + wg_iface->i_port = ntohs(dev->listen_port); wg_iface->i_flags |= WG_INTERFACE_HAS_PORT; } if (dev->flags & WGDEVICE_HAS_LISTEN) { diff --git a/src/ipc-uapi.h b/src/ipc-uapi.h index 7079fbd..0fc1524 100644 --- a/src/ipc-uapi.h +++ b/src/ipc-uapi.h @@ -46,7 +46,7 @@ static int userspace_set_device(struct wgdevice *dev) fprintf(f, "private_key=%s\n", hex); } if (dev->flags & WGDEVICE_HAS_LISTEN_PORT) - fprintf(f, "listen_port=%u\n", dev->listen_port); + fprintf(f, "listen_port=%u\n", ntohs(dev->listen_port)); if (dev->flags & WGDEVICE_HAS_LISTEN) return -EOPNOTSUPP; if (dev->flags & WGDEVICE_HAS_FWMARK) diff --git a/src/ipc-windows.h b/src/ipc-windows.h index 77e32b3..fb8f35c 100644 --- a/src/ipc-windows.h +++ b/src/ipc-windows.h @@ -249,7 +249,7 @@ static int kernel_get_device(struct wgdevice **device, const char *iface) dev->name[sizeof(dev->name) - 1] = '\0'; if (wg_iface->Flags & WG_IOCTL_INTERFACE_HAS_LISTEN_PORT) { - dev->listen_port = wg_iface->ListenPort; + dev->listen_port = htons(wg_iface->ListenPort); dev->flags |= WGDEVICE_HAS_LISTEN_PORT; } @@ -378,7 +378,7 @@ static int kernel_set_device(struct wgdevice *dev) } if (dev->flags & WGDEVICE_HAS_LISTEN_PORT) { - wg_iface->ListenPort = dev->listen_port; + wg_iface->ListenPort = ntohs(dev->listen_port); wg_iface->Flags |= WG_IOCTL_INTERFACE_HAS_LISTEN_PORT; } if (dev->flags & WGDEVICE_HAS_LISTEN) { diff --git a/src/show.c b/src/show.c index 754f952..3048183 100644 --- a/src/show.c +++ b/src/show.c @@ -127,26 +127,24 @@ char *print_endpoint(const struct sockaddr *addr) return buf; } -char *print_sockaddr_inet(const struct sockaddr_inet *sa_const) +char *print_sockaddr_inet(const struct sockaddr_inet *sa) { char host[4096 + 1], service[512 + 1], ifname_buf[IF_NAMESIZE+10] = "%"; static char buf[sizeof(host) + sizeof(service) + sizeof(ifname_buf) + 4]; - struct sockaddr_inet sa = *sa_const; socklen_t sa_len = 0; unsigned int ifindex = 0; int ret; - sa.sinet_port = htons(sa.sinet_port); - - if (sa.sinet_family == AF_INET) { + if (sa->sinet_family == AF_INET) { sa_len = sizeof(struct sockaddr_in); - ifindex = sa.sin_scope_id; - } else if (sa.sinet_family == AF_INET6) { + ifindex = sa->sin_scope_id; + } else if (sa->sinet_family == AF_INET6) { sa_len = sizeof(struct sockaddr_in6); - ifindex = sa.sin6_scope_id; + ifindex = sa->sin6_scope_id; } - ret = getnameinfo((struct sockaddr*)&sa, sa_len, host, sizeof(host), service, sizeof(service), NI_DGRAM | NI_NUMERICSERV | NI_NUMERICHOST); + ret = getnameinfo((struct sockaddr*)sa, sa_len, host, sizeof(host), service, sizeof(service), NI_DGRAM | NI_NUMERICSERV | NI_NUMERICHOST); if (ret) { + fprintf(stderr, "error: print_sockaddr_inet: %s", gai_strerror(ret)); buf[0] = '\0'; goto out; } @@ -160,7 +158,7 @@ char *print_sockaddr_inet(const struct sockaddr_inet *sa_const) } } - if ((sa.sinet_family == AF_INET6 && strchr(host, ':')) || ifindex) + if ((sa->sinet_family == AF_INET6 && strchr(host, ':')) || ifindex) snprintf(buf, sizeof(buf), "[%s%s]:%s", host, ifname, service); else snprintf(buf, sizeof(buf), "%s:%s", host, service); @@ -261,7 +259,7 @@ static void pretty_print(struct wgdevice *device) if (device->listen_family != AF_UNSPEC) terminal_printf(" " TERMINAL_BOLD "listening on" TERMINAL_RESET ": %s\n", print_sockaddr_inet(&device->listen_inet)); else if (device->listen_port) - terminal_printf(" " TERMINAL_BOLD "listening port" TERMINAL_RESET ": %u\n", device->listen_port); + terminal_printf(" " TERMINAL_BOLD "listening port" TERMINAL_RESET ": %u\n", ntohs(device->listen_port)); if (device->fwmark) terminal_printf(" " TERMINAL_BOLD "fwmark" TERMINAL_RESET ": 0x%x\n", device->fwmark); if (device->first_peer) { @@ -306,7 +304,7 @@ static void dump_print(struct wgdevice *device, bool with_interface) if (device->listen_family != AF_UNSPEC) printf("%s\t", print_sockaddr_inet(&device->listen_inet)); else - printf("%u\t", device->listen_port); + printf("%u\t", ntohs(device->listen_port)); if (device->fwmark) printf("0x%x\n", device->fwmark); else @@ -350,7 +348,7 @@ static bool ugly_print(struct wgdevice *device, const char *param, bool with_int } else if (!strcmp(param, "listen-port")) { if (with_interface) printf("%s\t", device->name); - printf("%u\n", device->listen_port); + printf("%u\n", ntohs(device->listen_port)); } else if (!strcmp(param, "listen")) { if (with_interface) printf("%s\t", device->name); diff --git a/src/showconf.c b/src/showconf.c index d165eb2..c99a6a0 100644 --- a/src/showconf.c +++ b/src/showconf.c @@ -44,7 +44,7 @@ int showconf_main(int argc, const char *argv[]) if (device->listen_family != AF_UNSPEC) printf("Listen = %s", print_sockaddr_inet(&device->listen_inet)); else if (device->listen_port) - printf("ListenPort = %u\n", device->listen_port); + printf("ListenPort = %u\n", ntohs(device->listen_port)); if (device->fwmark) printf("FwMark = 0x%x\n", device->fwmark); if (device->flags & WGDEVICE_HAS_PRIVATE_KEY) { -- 2.39.2 From dxld at darkboxed.org Mon Oct 23 17:08:59 2023 From: dxld at darkboxed.org (=?UTF-8?q?Daniel=20Gr=C3=B6ber?=) Date: Mon, 23 Oct 2023 19:08:59 +0200 Subject: [PATCH v2 5/6] wg: Replace print_endpoint with print_sockaddr_inet In-Reply-To: <20231023170900.131473-1-dxld@darkboxed.org> References: <20231023170900.131473-1-dxld@darkboxed.org> Message-ID: <20231023170900.131473-5-dxld@darkboxed.org> Note this changes the commandline behaviour slightly. Previously we would output the gai_strerror message instead of the address when getnameinfo fails. I don't think this behaviour is very useful for scripts as it's hard to match for since we're missing, say, an "error: " prefix. Instead print the error to stderr and just don't print anything on stdout in this case. Empty string is easier to detect than an arbitrary set of (possibly localised!) error messages. Signed-off-by: Daniel Gr?ber --- Changes in v2: - Remove now redundant print_endpoint prototype src/show.c | 29 +++-------------------------- src/show.h | 1 - 2 files changed, 3 insertions(+), 27 deletions(-) diff --git a/src/show.c b/src/show.c index 3048183..ec830d1 100644 --- a/src/show.c +++ b/src/show.c @@ -104,29 +104,6 @@ static char *ip(const struct wgallowedip *ip) return buf; } -char *print_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; -} - char *print_sockaddr_inet(const struct sockaddr_inet *sa) { char host[4096 + 1], service[512 + 1], ifname_buf[IF_NAMESIZE+10] = "%"; @@ -271,7 +248,7 @@ static void pretty_print(struct wgdevice *device) 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", print_endpoint(&peer->endpoint.addr)); + terminal_printf(" " TERMINAL_BOLD "endpoint" TERMINAL_RESET ": %s\n", print_sockaddr_inet(&peer->endpoint.addr_inet)); terminal_printf(" " TERMINAL_BOLD "allowed ips" TERMINAL_RESET ": "); if (peer->first_allowedip) { for_each_wgallowedip(peer, allowedip) @@ -315,7 +292,7 @@ static void dump_print(struct wgdevice *device, bool with_interface) 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", print_endpoint(&peer->endpoint.addr)); + printf("%s\t", print_sockaddr_inet(&peer->endpoint.addr_inet)); else printf("(none)\t"); if (peer->first_allowedip) { @@ -366,7 +343,7 @@ static bool ugly_print(struct wgdevice *device, const char *param, bool with_int printf("%s\t", device->name); 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", print_endpoint(&peer->endpoint.addr)); + printf("%s\n", print_sockaddr_inet(&peer->endpoint.addr_inet)); else printf("(none)\n"); } diff --git a/src/show.h b/src/show.h index 3673b65..2490301 100644 --- a/src/show.h +++ b/src/show.h @@ -7,7 +7,6 @@ #define SHOW_H struct sockaddr_inet; -char *print_endpoint(const struct sockaddr *addr); char *print_sockaddr_inet(const struct sockaddr_inet *addr); #endif -- 2.39.2 From dxld at darkboxed.org Mon Oct 23 17:09:00 2023 From: dxld at darkboxed.org (=?UTF-8?q?Daniel=20Gr=C3=B6ber?=) Date: Mon, 23 Oct 2023 19:09:00 +0200 Subject: [PATCH v2 6/6] wg: Simplify showconf_main by using print_sockaddr_inet In-Reply-To: <20231023170900.131473-1-dxld@darkboxed.org> References: <20231023170900.131473-1-dxld@darkboxed.org> Message-ID: <20231023170900.131473-6-dxld@darkboxed.org> Signed-off-by: Daniel Gr?ber --- src/showconf.c | 17 ++--------------- 1 file changed, 2 insertions(+), 15 deletions(-) diff --git a/src/showconf.c b/src/showconf.c index c99a6a0..f2c6a68 100644 --- a/src/showconf.c +++ b/src/showconf.c @@ -23,8 +23,6 @@ int showconf_main(int argc, const char *argv[]) { char base64[WG_KEY_LEN_BASE64]; char ip[INET6_ADDRSTRLEN]; - char host[4096 + 1], service[512 + 1]; - socklen_t addr_len = 0; struct wgdevice *device = NULL; struct wgpeer *peer; struct wgallowedip *allowedip; @@ -77,19 +75,8 @@ int showconf_main(int argc, const char *argv[]) if (peer->first_allowedip) printf("\n"); - // TODO: use print_endpoint - if (peer->endpoint.addr.sa_family == AF_INET || peer->endpoint.addr.sa_family == AF_INET6) { - 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->endpoint.addr.sa_family == AF_INET || peer->endpoint.addr.sa_family == AF_INET6) + printf("Endpoint = %s\n", print_sockaddr_inet(&peer->endpoint.addr_inet)); if (peer->persistent_keepalive_interval) printf("PersistentKeepalive = %u\n", peer->persistent_keepalive_interval); -- 2.39.2 From dxld at darkboxed.org Mon Oct 23 17:14:26 2023 From: dxld at darkboxed.org (=?UTF-8?q?Daniel=20Gr=C3=B6ber?=) Date: Mon, 23 Oct 2023 19:14:26 +0200 Subject: [PATCH v2 RESEND^2] wg: Allow config to read secret keys from file Message-ID: <20231023171425.135251-1-dxld@darkboxed.org> This adds two new config keys PrivateKeyFile= and PresharedKeyFile= that simply hook up the existing code for the `wg set ... private-key /file` codepath. By using the new options wireguard configs can become a lot easier to manage and deploy as we don't have to treat them as secrets anymore. This way they can, for example, be tracked in public git repos while the secret keys can be provisioned using an out of band system or with a manual one-time step instead. Before this patch we were using an ugly hack: it's possible to simply omit PrivateKey= and set it using `PostUp = wg set %i private-key /some/file`. However this breaks when we try to use setconf or synconf as they will (rightly) unset the private key when it's missing in the underlying config file breaking connectivity. Reviewed-By: Michael Tokarev Signed-off-by: Daniel Gr?ber --- src/config.c | 8 ++++++++ src/man/wg.8 | 4 ++++ 2 files changed, 12 insertions(+) diff --git a/src/config.c b/src/config.c index 81ccb47..1e924c7 100644 --- a/src/config.c +++ b/src/config.c @@ -450,6 +450,10 @@ static bool process_line(struct config_ctx *ctx, const char *line) ret = parse_key(ctx->device->private_key, value); if (ret) ctx->device->flags |= WGDEVICE_HAS_PRIVATE_KEY; + } else if (key_match("PrivateKeyFile")) { + ret = parse_keyfile(ctx->device->private_key, value); + if (ret) + ctx->device->flags |= WGDEVICE_HAS_PRIVATE_KEY; } else goto error; } else if (ctx->is_peer_section) { @@ -467,6 +471,10 @@ static bool process_line(struct config_ctx *ctx, const char *line) ret = parse_key(ctx->last_peer->preshared_key, value); if (ret) ctx->last_peer->flags |= WGPEER_HAS_PRESHARED_KEY; + } else if (key_match("PresharedKeyFile")) { + ret = parse_keyfile(ctx->last_peer->preshared_key, value); + if (ret) + ctx->last_peer->flags |= WGPEER_HAS_PRESHARED_KEY; } else goto error; } else diff --git a/src/man/wg.8 b/src/man/wg.8 index 7984539..a5d8bcf 100644 --- a/src/man/wg.8 +++ b/src/man/wg.8 @@ -134,6 +134,8 @@ 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 +PrivateKeyFile \(em path to a file containing a base64 private key. May be used instead of \fIPrivateKey\fP. Optional. +.IP \(bu ListenPort \(em a 16-bit port for listening. Optional; if not specified, chosen randomly. .IP \(bu @@ -151,6 +153,8 @@ 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 +PresharedKeyFile \(em path to a file containing a base64 preshared key. May be used instead of \fIPresharedKey\fP. Optional. +.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 -- 2.39.2 From dxld at darkboxed.org Mon Oct 23 17:17:23 2023 From: dxld at darkboxed.org (=?UTF-8?q?Daniel=20Gr=C3=B6ber?=) Date: Mon, 23 Oct 2023 19:17:23 +0200 Subject: [PATCH v3 RESEND^2] wg: Support restricting address family of DNS resolved Endpoint Message-ID: <20231023171723.137097-1-dxld@darkboxed.org> When using wireguard tunnels for providing IPv6 connectivity to machines it can be important to pin which IP address family should be used. Consider a peer using a DNS name with both A/AAAA records, wg will currently blindly follow system policy and use the first address returned by getaddrinfo(). In typical deployments this will cause the IPv6 address of the peer to be used, however when the whole IPv6 internet is being routed over our wg iface all this accomplishes is a traffic black hole. Naturally this can be worked around by having different DNS names for v4-only / dual-stack addresses, however this may not be possible in some situations where, say, a dynamic-DNS service is also in use. To fix this we allow users to control which address family they want using the new AddressFamily= config option, see wg.8 for details. We also update reresolve-dns to take the AddressFamily option into account. We would like to note that the not_oif patch[1] would also alleviate this problem but since this never got merged it's not a workable solution. [1]: http://marc.info/?t=145452167200014&r=1&w=2 Signed-off-by: Daniel Gr?ber --- contrib/reresolve-dns/reresolve-dns.sh | 4 ++- src/config.c | 41 ++++++++++++++++++++------ src/config.h | 2 +- src/containers.h | 5 ++++ src/man/wg.8 | 8 ++++- src/set.c | 9 +++++- src/setconf.c | 2 +- 7 files changed, 57 insertions(+), 14 deletions(-) diff --git a/contrib/reresolve-dns/reresolve-dns.sh b/contrib/reresolve-dns/reresolve-dns.sh index 711c332..bdb47ac 100755 --- a/contrib/reresolve-dns/reresolve-dns.sh +++ b/contrib/reresolve-dns/reresolve-dns.sh @@ -17,7 +17,7 @@ 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" + wg set "$INTERFACE" peer "$PUBLIC_KEY" endpoint "$ENDPOINT" address-family "$FAMILY" reset_peer_section } @@ -25,6 +25,7 @@ reset_peer_section() { PEER_SECTION=0 PUBLIC_KEY="" ENDPOINT="" + FAMILY=unspec } reset_peer_section @@ -38,6 +39,7 @@ while read -r line || [[ -n $line ]]; do case "$key" in PublicKey) PUBLIC_KEY="$value"; continue ;; Endpoint) ENDPOINT="$value"; continue ;; + AddressFamily) FAMILY="$value"; continue ;; esac fi done < "$CONFIG_FILE" diff --git a/src/config.c b/src/config.c index 1e924c7..f9980fe 100644 --- a/src/config.c +++ b/src/config.c @@ -192,14 +192,14 @@ static inline int parse_dns_retries(void) return (int)ret; } -static inline bool parse_endpoint(struct sockaddr *endpoint, const char *value) +static inline bool parse_endpoint(struct sockaddr *endpoint, const char *value, int family) { char *mutable = strdup(value); char *begin, *end; int ret, retries = parse_dns_retries(); struct addrinfo *resolved; struct addrinfo hints = { - .ai_family = AF_UNSPEC, + .ai_family = family, .ai_socktype = SOCK_DGRAM, .ai_protocol = IPPROTO_UDP }; @@ -279,6 +279,20 @@ static inline bool parse_endpoint(struct sockaddr *endpoint, const char *value) return true; } +static inline bool parse_address_family(int *family, const char *value) +{ + if (strcmp(value, "inet") == 0) + *family = AF_INET; + else if (strcmp(value, "inet6") == 0) + *family = AF_INET6; + else if (strcmp(value, "unspec") == 0) + *family = AF_UNSPEC; + else + return false; + + return true; +} + static inline bool parse_persistent_keepalive(uint16_t *interval, uint32_t *flags, const char *value) { unsigned long ret; @@ -458,8 +472,10 @@ static bool process_line(struct config_ctx *ctx, const char *line) 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")) { + ctx->last_peer->endpoint_value = strdup(value); + else if (key_match("AddressFamily")) { + ret = parse_address_family(&ctx->last_peer->addr_fam, 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; @@ -535,19 +551,22 @@ bool config_read_init(struct config_ctx *ctx, bool append) return true; } -struct wgdevice *config_read_finish(struct config_ctx *ctx) +struct wgdevice *config_read_finish(struct wgdevice *device) { struct wgpeer *peer; - for_each_wgpeer(ctx->device, peer) { + for_each_wgpeer(device, peer) { if (!(peer->flags & WGPEER_HAS_PUBLIC_KEY)) { fprintf(stderr, "A peer is missing a public key\n"); goto err; } + + if (!parse_endpoint(&peer->endpoint.addr, peer->endpoint_value, peer->addr_fam)) + goto err; } - return ctx->device; + return device; err: - free_wgdevice(ctx->device); + free_wgdevice(device); return NULL; } @@ -619,7 +638,11 @@ struct wgdevice *config_read_cmd(const char *argv[], int argc) argv += 1; argc -= 1; } else if (!strcmp(argv[0], "endpoint") && argc >= 2 && peer) { - if (!parse_endpoint(&peer->endpoint.addr, argv[1])) + peer->endpoint_value = strdup(argv[1]); + argv += 2; + argc -= 2; + } else if (!strcmp(argv[0], "address-family") && argc >= 2 && peer) { + if (!parse_address_family(&peer->addr_fam, argv[1])) goto error; argv += 2; argc -= 2; diff --git a/src/config.h b/src/config.h index 443cf21..6f81da2 100644 --- a/src/config.h +++ b/src/config.h @@ -22,6 +22,6 @@ struct config_ctx { 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); +struct wgdevice *config_read_finish(struct wgdevice *device); #endif diff --git a/src/containers.h b/src/containers.h index a82e8dd..c111621 100644 --- a/src/containers.h +++ b/src/containers.h @@ -52,12 +52,15 @@ struct wgpeer { uint8_t public_key[WG_KEY_LEN]; uint8_t preshared_key[WG_KEY_LEN]; + char *endpoint_value; union { struct sockaddr addr; struct sockaddr_in addr4; struct sockaddr_in6 addr6; } endpoint; + int addr_fam; + struct timespec64 last_handshake_time; uint64_t rx_bytes, tx_bytes; uint16_t persistent_keepalive_interval; @@ -99,6 +102,8 @@ static inline void free_wgdevice(struct wgdevice *dev) 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); + if (peer->endpoint_value) + free(peer->endpoint_value); free(peer); } free(dev); diff --git a/src/man/wg.8 b/src/man/wg.8 index a5d8bcf..48f084d 100644 --- a/src/man/wg.8 +++ b/src/man/wg.8 @@ -55,7 +55,7 @@ transfer-rx, transfer-tx, persistent-keepalive. Shows the current configuration of \fI\fP in the format described by \fICONFIGURATION FILE FORMAT\fP below. .TP -\fBset\fP \fI\fP [\fIlisten-port\fP \fI\fP] [\fIfwmark\fP \fI\fP] [\fIprivate-key\fP \fI\fP] [\fIpeer\fP \fI\fP [\fIremove\fP] [\fIpreshared-key\fP \fI\fP] [\fIendpoint\fP \fI:\fP] [\fIpersistent-keepalive\fP \fI\fP] [\fIallowed-ips\fP \fI/\fP[,\fI/\fP]...] ]... +\fBset\fP \fI\fP [\fIlisten-port\fP \fI\fP] [\fIfwmark\fP \fI\fP] [\fIprivate-key\fP \fI\fP] [\fIpeer\fP \fI\fP [\fIremove\fP] [\fIpreshared-key\fP \fI\fP] [\fIendpoint\fP \fI:\fP] [\fIaddress-family\fP \fI\fP] [\fIpersistent-keepalive\fP \fI\fP] [\fIallowed-ips\fP \fI/\fP[,\fI/\fP]...] ]... Sets configuration values for the specified \fI\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 @@ -167,6 +167,12 @@ 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 +AddressFamily \(em one of \fIinet\fP, \fIinet6\fP or \fIunspec\fP. When a +hostname is given for \fIEndpoint\fP, setting this to \fIinet\fP or +\fIinet6\fP will allow only addresses of the given family to be +used. Defaults to \fIunspec\fP, which causes the first returned address of +any type to be used. +.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 diff --git a/src/set.c b/src/set.c index 75560fd..20ee85e 100644 --- a/src/set.c +++ b/src/set.c @@ -18,13 +18,20 @@ int set_main(int argc, const char *argv[]) int ret = 1; if (argc < 3) { - fprintf(stderr, "Usage: %s %s [listen-port ] [fwmark ] [private-key ] [peer [remove] [preshared-key ] [endpoint :] [persistent-keepalive ] [allowed-ips /[,/]...] ]...\n", PROG_NAME, argv[0]); + fprintf(stderr, "Usage: %s %s [listen-port ] [fwmark ] [private-key ] [peer [remove] [preshared-key ] [endpoint :] [address-family ] [persistent-keepalive ] [allowed-ips /[,/]...] ]...\n", PROG_NAME, argv[0]); return 1; } device = config_read_cmd(argv + 2, argc - 2); if (!device) goto cleanup; + + device = config_read_finish(device); + if (!device) { + fprintf(stderr, "Invalid configuration\n"); + goto cleanup; + } + strncpy(device->name, argv[1], IFNAMSIZ - 1); device->name[IFNAMSIZ - 1] = '\0'; diff --git a/src/setconf.c b/src/setconf.c index 1c5b138..c90fd30 100644 --- a/src/setconf.c +++ b/src/setconf.c @@ -127,7 +127,7 @@ int setconf_main(int argc, const char *argv[]) goto cleanup; } } - device = config_read_finish(&ctx); + device = config_read_finish(ctx.device); if (!device) { fprintf(stderr, "Invalid configuration\n"); goto cleanup; -- 2.39.2 From m-k-mailling-list at mk16.de Tue Oct 24 09:37:55 2023 From: m-k-mailling-list at mk16.de (Marek =?UTF-8?B?S8O8dGhl?=) Date: Tue, 24 Oct 2023 11:37:55 +0200 Subject: AllowedIPs = ::/0 routes IPv4 - on Android? In-Reply-To: <63bb2149-2d0b-df64-27f9-6e003dfdc577@openoffice.nl> References: <63bb2149-2d0b-df64-27f9-6e003dfdc577@openoffice.nl> Message-ID: <20231024113755.6a786c71@parrot> Hello Valentijn, ::/0 does not describe no IPv4 address, but all IP addresses. So when you write ::/0, all IPv4 addresses are routed through the tunnel. If you don't want IPv4 routes, you can simply omit them in AllowedIPs (so only put IPv6 addresses there, or only address which should go through the tunnel). Greetings Marek K?the On Tue, 5 Sep 2023 16:04:34 +0200 Valentijn Sessink wrote: > Hi List, > > I have a WG endpoint configured with > AllowedIPs = ::/0 > ... on an Android phone. > > To my surprise, I found out that this also tries to route IPv4 addresses > to the other WG side. > > I was able to change that with a single "bogus" IPv4 address, > "AllowedIPs = ::/0, 192.0.2.99/32" > > Is this a known feature? Android 13, WireGuard for Android > v1.0.20230707, (from AOSP). > > Best regards, > > Valentijn -- Marek K?the m.k at mk16.de er/ihm he/him -------------- next part -------------- A non-text attachment was scrubbed... Name: not available Type: application/pgp-signature Size: 833 bytes Desc: OpenPGP digital signature URL: From nico.schottelius at ungleich.ch Tue Oct 24 11:08:14 2023 From: nico.schottelius at ungleich.ch (Nico Schottelius) Date: Tue, 24 Oct 2023 13:08:14 +0200 Subject: AllowedIPs = ::/0 routes IPv4 - on Android? In-Reply-To: <20231024113755.6a786c71@parrot> References: <63bb2149-2d0b-df64-27f9-6e003dfdc577@openoffice.nl> <20231024113755.6a786c71@parrot> Message-ID: <875y2wgtv7.fsf@ungleich.ch> Hello Marek, since when does ::/0 refer to IPv4 addresses? To my knowledge, ::/0 is the IPv6 all route and does not include any IPv4. Best regards, Nico Marek K?the writes: > [[PGP Signed Part:Undecided]] > Hello Valentijn, > > ::/0 does not describe no IPv4 address, but all IP addresses. So when > you write ::/0, all IPv4 addresses are routed through the tunnel. If > you don't want IPv4 routes, you can simply omit them in AllowedIPs (so > only put IPv6 addresses there, or only address which should go through > the tunnel). > > Greetings > Marek K?the > > On Tue, 5 Sep 2023 16:04:34 +0200 > Valentijn Sessink wrote: > >> Hi List, >> >> I have a WG endpoint configured with >> AllowedIPs = ::/0 >> ... on an Android phone. >> >> To my surprise, I found out that this also tries to route IPv4 addresses >> to the other WG side. >> >> I was able to change that with a single "bogus" IPv4 address, >> "AllowedIPs = ::/0, 192.0.2.99/32" >> >> Is this a known feature? Android 13, WireGuard for Android >> v1.0.20230707, (from AOSP). >> >> Best regards, >> >> Valentijn -- Sustainable and modern Infrastructures by ungleich.ch From v at sess.ink Tue Oct 24 11:13:26 2023 From: v at sess.ink (Valentijn Sessink) Date: Tue, 24 Oct 2023 13:13:26 +0200 Subject: AllowedIPs = ::/0 routes IPv4 - on Android? In-Reply-To: <20231024113755.6a786c71@parrot> References: <63bb2149-2d0b-df64-27f9-6e003dfdc577@openoffice.nl> <20231024113755.6a786c71@parrot> Message-ID: Hi Marek, Do you have any sources for that? AFAIK, ::/0 is a convenient way to describe "any IPv6 address". Still, if you are correct and should ::/0 include IPv4, then the exact same setup under Linux operates differently, because here ::/0 only routes IPv6, not IPv4. Note the empty result for the ipv4 routing table: # AllowedIPs = ::/0 v at lnx:~$ ip ro li ta 51820 v at lnx:~$ ip -6 ro li ta 51820 default dev wgip6 metric 1024 pref medium # AllowedIPs = ::/0, 0.0.0.0/0 v at lnx:~$ ip ro li ta 51820 default dev wgip6 scope link v at lnx:~$ ip -6 ro li ta 51820 default dev wgip6 metric 1024 pref medium So the question remains: ::/0 under Android routes all IPv4 traffic to the WG interface, while under Linux, it will only route IPv6. Is this known behaviour? The wg-quick manpage is ambiguous, saying that "if one of those routes is the default route (0.0.0.0/0 or ::/0), then it uses ip-rule(8) to handle overriding of the default gateway." No information on routing IPv4 or IPv6 differently. A rephrase could be something like "if one of those routes is 0.0.0.0/0, or ::/0, it uses ip-rule(8) to handle overriding the default route for IPv4 or IPv6 respectivally." Best regards, Valentijn On 24-10-2023 11:37, Marek K?the wrote: > ::/0 does not describe no IPv4 address, but all IP addresses. So when [...] > On Tue, 5 Sep 2023 16:04:34 +0200 > Valentijn Sessink wrote: >> AllowedIPs = ::/0 [...] >> To my surprise, I found out that this also tries to route IPv4 addresses >> to the other WG side. >> Is this a known feature? Android 13, WireGuard for Android >> v1.0.20230707, (from AOSP).-- http://www.openoffice.nl/ Open Office - Linux Office Solutions Valentijn Sessink v.sessink at openoffice.nl +31(0)20-4214059 From lkp at intel.com Tue Oct 24 18:43:26 2023 From: lkp at intel.com (kernel test robot) Date: Wed, 25 Oct 2023 02:43:26 +0800 Subject: [PATCH] wireguard: Add netlink attrs for binding to address and netdev In-Reply-To: <20231023160006.85992-1-dxld@darkboxed.org> References: <20231023160006.85992-1-dxld@darkboxed.org> Message-ID: <202310250256.kXWoLwJm-lkp@intel.com> Hi Daniel, kernel test robot noticed the following build errors: [auto build test ERROR on linus/master] [also build test ERROR on v6.6-rc7 next-20231024] [If your patch is applied to the wrong git tree, kindly drop us a note. And when submitting patch, we suggest to use '--base' as documented in https://git-scm.com/docs/git-format-patch#_base_tree_information] url: https://github.com/intel-lab-lkp/linux/commits/Daniel-Gr-ber/wireguard-Add-netlink-attrs-for-binding-to-address-and-netdev/20231024-000158 base: linus/master patch link: https://lore.kernel.org/r/20231023160006.85992-1-dxld%40darkboxed.org patch subject: [PATCH] wireguard: Add netlink attrs for binding to address and netdev config: um-randconfig-002-20231024 (https://download.01.org/0day-ci/archive/20231025/202310250256.kXWoLwJm-lkp at intel.com/config) compiler: gcc-12 (Debian 12.2.0-14) 12.2.0 reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20231025/202310250256.kXWoLwJm-lkp at intel.com/reproduce) If you fix the issue in a separate patch/commit (i.e. not just a new version of the same patch/commit), kindly add following tags | Reported-by: kernel test robot | Closes: https://lore.kernel.org/oe-kbuild-all/202310250256.kXWoLwJm-lkp at intel.com/ All errors (new ones prefixed by >>): drivers/net/wireguard/netlink.c: In function 'wg_get_device_dump': >> drivers/net/wireguard/netlink.c:247:52: error: 'struct udp_port_cfg' has no member named 'local_ip6'; did you mean 'local_ip'? 247 | &wg->port_cfg.local_ip6)) | ^~~~~~~~~ | local_ip drivers/net/wireguard/netlink.c: In function 'set_port_cfg': drivers/net/wireguard/netlink.c:348:34: error: 'struct udp_port_cfg' has no member named 'local_ip6'; did you mean 'local_ip'? 348 | port_cfg.local_ip6 = u_addr->addr6; | ^~~~~~~~~ | local_ip vim +247 drivers/net/wireguard/netlink.c 211 212 static int wg_get_device_dump(struct sk_buff *skb, struct netlink_callback *cb) 213 { 214 struct wg_peer *peer, *next_peer_cursor; 215 struct dump_ctx *ctx = DUMP_CTX(cb); 216 struct wg_device *wg = ctx->wg; 217 struct nlattr *peers_nest; 218 int ret = -EMSGSIZE; 219 bool done = true; 220 void *hdr; 221 222 rtnl_lock(); 223 mutex_lock(&wg->device_update_lock); 224 cb->seq = wg->device_update_gen; 225 next_peer_cursor = ctx->next_peer; 226 227 hdr = genlmsg_put(skb, NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq, 228 &genl_family, NLM_F_MULTI, WG_CMD_GET_DEVICE); 229 if (!hdr) 230 goto out; 231 genl_dump_check_consistent(cb, hdr); 232 233 if (!ctx->next_peer) { 234 if (nla_put_u16(skb, WGDEVICE_A_LISTEN_PORT, 235 ntohs(wg->port_cfg.local_udp_port)) || 236 nla_put_u32(skb, WGDEVICE_A_LISTEN_IFINDEX, wg->port_cfg.bind_ifindex) || 237 nla_put_u32(skb, WGDEVICE_A_FWMARK, wg->fwmark) || 238 nla_put_u32(skb, WGDEVICE_A_IFINDEX, wg->dev->ifindex) || 239 nla_put_string(skb, WGDEVICE_A_IFNAME, wg->dev->name)) 240 goto out; 241 if (wg->port_cfg.family == AF_INET && 242 nla_put_in_addr(skb, WGDEVICE_A_LISTEN_ADDR, 243 wg->port_cfg.local_ip.s_addr)) 244 goto out; 245 if (wg->port_cfg.family == AF_INET6 && 246 nla_put_in6_addr(skb, WGDEVICE_A_LISTEN_ADDR, > 247 &wg->port_cfg.local_ip6)) 248 goto out; 249 250 down_read(&wg->static_identity.lock); 251 if (wg->static_identity.has_identity) { 252 if (nla_put(skb, WGDEVICE_A_PRIVATE_KEY, 253 NOISE_PUBLIC_KEY_LEN, 254 wg->static_identity.static_private) || 255 nla_put(skb, WGDEVICE_A_PUBLIC_KEY, 256 NOISE_PUBLIC_KEY_LEN, 257 wg->static_identity.static_public)) { 258 up_read(&wg->static_identity.lock); 259 goto out; 260 } 261 } 262 up_read(&wg->static_identity.lock); 263 } 264 265 peers_nest = nla_nest_start(skb, WGDEVICE_A_PEERS); 266 if (!peers_nest) 267 goto out; 268 ret = 0; 269 /* If the last cursor was removed via list_del_init in peer_remove, then 270 * we just treat this the same as there being no more peers left. The 271 * reason is that seq_nr should indicate to userspace that this isn't a 272 * coherent dump anyway, so they'll try again. 273 */ 274 if (list_empty(&wg->peer_list) || 275 (ctx->next_peer && list_empty(&ctx->next_peer->peer_list))) { 276 nla_nest_cancel(skb, peers_nest); 277 goto out; 278 } 279 lockdep_assert_held(&wg->device_update_lock); 280 peer = list_prepare_entry(ctx->next_peer, &wg->peer_list, peer_list); 281 list_for_each_entry_continue(peer, &wg->peer_list, peer_list) { 282 if (get_peer(peer, skb, ctx)) { 283 done = false; 284 break; 285 } 286 next_peer_cursor = peer; 287 } 288 nla_nest_end(skb, peers_nest); 289 290 out: 291 if (!ret && !done && next_peer_cursor) 292 wg_peer_get(next_peer_cursor); 293 wg_peer_put(ctx->next_peer); 294 mutex_unlock(&wg->device_update_lock); 295 rtnl_unlock(); 296 297 if (ret) { 298 genlmsg_cancel(skb, hdr); 299 return ret; 300 } 301 genlmsg_end(skb, hdr); 302 if (done) { 303 ctx->next_peer = NULL; 304 return 0; 305 } 306 ctx->next_peer = next_peer_cursor; 307 return skb->len; 308 309 /* At this point, we can't really deal ourselves with safely zeroing out 310 * the private key material after usage. This will need an additional API 311 * in the kernel for marking skbs as zero_on_free. 312 */ 313 } 314 -- 0-DAY CI Kernel Test Service https://github.com/intel/lkp-tests/wiki From blurt_overkill882 at simplelogin.com Sun Oct 29 16:20:04 2023 From: blurt_overkill882 at simplelogin.com (blurt_overkill882 at simplelogin.com) Date: Sun, 29 Oct 2023 16:20:04 +0000 Subject: Windows kill-switch with IP exceptions Message-ID: <169859641326.7.9409086915341472769.203421242@simplelogin.com> Howdy WireGuard team! I was wondering if it is at all possible to enable the kill-switch on the Windows client with exceptions for local IP addresses. I have noticed the kill-switch will automatically enable when you specify the allowed IPs 0.0.0.0/0. Unfortunately this is not ideal for my machine as I would like to allow all LAN connections to bypass the VPN. One way I can achieve this is by disabling the kill-switch, which sets the allowed IPs 0.0.0.0/1 and 128.0.0.0/1. This does work, however some apps on Windows can still bind themselves to the non VPN adapter, which completely bypasses the VPN even on these IP ranges. The kill-switch feature works great at stopping apps binding to the non VPN tunnel. I was hoping that it might be possible to have the kill-switch continue to do this baring all local addresses. Surfshark have implemented this with their kill-switch feature, it would be nice to be able to replicate the same with the WireGuard client. Is this at all possible, please may you explain why or why not if you have the time? Thanks for the software, and more importantly thanks for reading my query! Regards, avid WireGuard user. From tristancamilleri2 at gmail.com Thu Oct 19 06:20:09 2023 From: tristancamilleri2 at gmail.com (Tristan Camilleri) Date: Thu, 19 Oct 2023 06:20:09 -0000 Subject: Wireguard and Windows metrics priority Message-ID: Dear all, I hope that you are all well. We have a wireguard service running for the VPN solution, and currently we are testing a scenario with the Windows metrics. We are allocating the network adapter metrics as follows: VPN (Wireguard): 10 Ethernet adapters: 20 Wifi Adapters: 100 Bluetooth: 150 While configuring this, we were testing if this really works to prefer wired over wifi or not. While having the wireguard service running and connected to a wifi connection. We connected a LAN cable to the device, but the device's connection traffic did not switch from WiFi to wired. We tested this with the service of wireguard down, and it switched automatically. We wish that it would work when connected to wifi; it switches to wired when the wireguard service is running. Do you have any suggestions for this? Maybe you have encountered this. Thanks and regards, Tristan From dave.mifsud at um.edu.mt Thu Oct 19 07:44:14 2023 From: dave.mifsud at um.edu.mt (Dave Mifsud) Date: Thu, 19 Oct 2023 07:44:14 -0000 Subject: Wireguard Windows keeps using lower priority interface (wifi) when a higher priority interface (wired) becomes available Message-ID: We are implementing Wireguard as a VPN solution for our network on Windows 10/11 clients. One issue we have encountered is that Wireguard VPN traffic keeps transiting the wifi interface, even when wired becomes available. We have tested the same Windows device without Wireguard, and when the wired connection becomes available, traffic switches to the wired connection. Unfortunately users do not realise this, and at times are located in an area where wifi connectivity is not optimal. Has anyone come across this issue? Can anything be done, apart from creating a trigger in windows such that whenever a wired connection becomes available Wireguard is restarted? We would like to avoid this, as the solution seems too drastic. Dave From tomxor at gmail.com Sun Oct 29 19:23:00 2023 From: tomxor at gmail.com (Thomas Brierley) Date: Sun, 29 Oct 2023 19:23:00 -0000 Subject: [PATCH] wg-quick: linux: fix MTU calculation (use PMTUD) Message-ID: <20231029192210.120316-1-tomxor@gmail.com> Currently MTU calculation fails to successfully utilise the kernel's built-in path MTU discovery mechanism (PMTUD). Fixing this required a re-write of the set_mtu_up() function, which also addresses two related MTU issues as a side effect... 1. Trigger PMTUD Before Query Currently the endpoint path MTU acquired from `ip route get` will almost definitely be empty, because this only queries the routing cache. To trigger PMTUD on the endpoint and fill this cache, it is necessary to send an ICMP with the DF bit set. We now perform a ping beforehand with a total packet size equal to the interface MTU, larger will not trigger PMTUD, and smaller can miss a bottleneck. To calculate the ping payload, the device MTU and IP header size must be determined first. 2. Consider IPv6/4 Header Size Currently an 80 byte header size is assumed i.e. IPv6=40 + WireGuard=40. However this is not optimal in the case of IPv4. Since determining the IP header size is required for PMTUD anyway, this is now optimised as a side effect of endpoint MTU calculation. 3. Use Smallest Endpoint MTU Currently in the case of multiple endpoints the largest endpoint path MTU is used. However WireGuard will dynamically switch between endpoints when e.g. one fails, so the smallest MTU is now used to ensure all endpoints will function correctly. Signed-off-by: Thomas Brierley --- src/wg-quick/linux.bash | 41 ++++++++++++++++++++++++++--------------- 1 file changed, 26 insertions(+), 15 deletions(-) diff --git a/src/wg-quick/linux.bash b/src/wg-quick/linux.bash index 4193ce5..5aba2cb 100755 --- a/src/wg-quick/linux.bash +++ b/src/wg-quick/linux.bash @@ -123,22 +123,33 @@ add_addr() { } 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]}" + local dev devmtu end endmtu iph=40 wgh=40 mtu + # Device MTU + if [[ -n $(ip route show default) ]]; then + [[ $(ip route show default) =~ dev\ ([^ ]+) ]] + dev=${BASH_REMATCH[1]} + [[ $(ip addr show $dev scope global) =~ inet6 ]] && + iph=40 || iph=20 + if [[ $(ip link show dev $dev) =~ mtu\ ([0-9]+) ]]; then + devmtu=${BASH_REMATCH[1]} + [[ $(( devmtu - iph - wgh )) -gt $mtu ]] && + mtu=$(( devmtu - iph - wgh )) + fi + # Endpoint MTU + while read -r _ end; do + [[ $end =~ ^\[?([a-f0-9:.]+)\]?:[0-9]+$ ]] + end=${BASH_REMATCH[1]} + [[ $end =~ [:] ]] && + iph=40 || iph=20 + ping -w 1 -M do -s $(( devmtu - iph - 8 )) $end &> /dev/null || true + if [[ $(ip route get $end) =~ mtu\ ([0-9]+) ]]; then + endmtu=${BASH_REMATCH[1]} + [[ $(( endmtu - iph - wgh )) -lt $mtu ]] && + mtu=$(( endmtu - iph - wgh )) + fi + done < <(wg show "$INTERFACE" endpoints) fi - [[ $mtu -gt 0 ]] || mtu=1500 - cmd ip link set mtu $(( mtu - 80 )) up dev "$INTERFACE" + cmd ip link set mtu ${MTU:-${mtu:-1420}} up dev "$INTERFACE" } resolvconf_iface_prefix() { -- 2.30.2 From nohajc at gmail.com Sat Oct 7 22:47:20 2023 From: nohajc at gmail.com (Jan Noha) Date: Sat, 07 Oct 2023 22:47:20 -0000 Subject: UAPI socket for the macOS sandboxed Wireguard app Message-ID: Hello, I want to submit a series of patches concerning Wireguard on macOS. If it's ok, I will just link to a github PR which links to three other PRs (in wireguard-apple, wireguard-go and wireguard-tools). https://github.com/WireGuard/wgctrl-go/pull/143 Let me explain what this is about. I've been trying to automate Wireguard tunnel configuration for some P2P use cases and I wanted to use wgctrl-go library for the task. This already works fine on Linux and Windows. On macOS, it's a bit more complicated. If you only use CLI for creating tun interfaces (using wireguard from homebrew for example), it also works. Specifically, wgctrl-go communicates with the wireguard user-space daemon via a unix domain socket located in /var/run/wireguard/ (this is referred to as UAPI in the code). However, if you want to use Wireguard from the App Store - which has some other advantages besides the UI (such as on-demand VPN and generally nice OS integration) - it comes as a sandboxed Network Extension. Currently, it does not expose any UAPI socket, so wgctrl-go cannot be used to configure it. The socket can be opened except it has to be inside the sandbox home directory. There is no problem connecting to it from "outside" using cli tools which are not sandboxed themselves. That's basically what I did here. Changes were needed in wireguard-apple and wireguard-go to open the socket in a macOS-specific location, then I updated wgctrl-go and wireguard-tools (so that wg commands work too) to look for UAPI sockets in both the sandbox location and the default one. If you're interested in discussing this topic further, I'll look forward to any feedback. Thank you, Jan Noha From drl at bsd.my Sat Oct 21 01:30:08 2023 From: drl at bsd.my (Darryl Yeoh) Date: Sat, 21 Oct 2023 01:30:08 -0000 Subject: AllowedIPs Message-ID: Hi lists, I've a Debian 11 box in my LAN set up as a WG client. When I set: AllowedIPs 0.0.0.0/0, ::/0 everything works. However when I set: AllowedIPs 0.0.0.0/1, 128.0.0.0/1, ::/1, 8000::/1 nothing works. Like ping/dig/curl just hangs until I hit Ctrl+c to return to prompt. When I test this config on my Android phone, it works with both of the AllowedIPs. Seems odd. Any ideas ? Regards, -Darryl From rca at koalox.com Tue Oct 3 19:24:18 2023 From: rca at koalox.com (Raphael Catolino) Date: Tue, 03 Oct 2023 19:24:18 -0000 Subject: [PATCH 1/1] wireguard-linux: add netlink multicast group for notifications on peer change Message-ID: <2328185.xqv9EDfTUt@desktop> From: Linus Karl Add a multicast group to the wireguard netlink family, and three message types to notify subscribers when a peer has changed . Signed-off-by: Linus Karl Co-developed-by: Raphael Catolino Signed-off-by: Raphael Catolino --- drivers/net/wireguard/device.h | 2 + drivers/net/wireguard/netlink.c | 231 +++++++++++++++++++++++++++++++- drivers/net/wireguard/netlink.h | 6 + drivers/net/wireguard/peer.c | 7 +- drivers/net/wireguard/socket.c | 5 + include/uapi/linux/wireguard.h | 69 ++++++++++ 6 files changed, 316 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireguard/device.h b/drivers/net/wireguard/device.h index 43c7cebbf50b..137b1b517815 100644 --- a/drivers/net/wireguard/device.h +++ b/drivers/net/wireguard/device.h @@ -54,6 +54,8 @@ struct wg_device { unsigned int num_peers, device_update_gen; u32 fwmark; u16 incoming_port; + bool endpoint_monitor; + bool peers_monitor; }; int wg_device_init(void); diff --git a/drivers/net/wireguard/netlink.c b/drivers/net/wireguard/netlink.c index 43c8c84e7ea8..b40b56e697f2 100644 --- a/drivers/net/wireguard/netlink.c +++ b/drivers/net/wireguard/netlink.c @@ -27,7 +27,9 @@ static const struct nla_policy device_policy[WGDEVICE_A_MAX + 1] = { [WGDEVICE_A_FLAGS] = { .type = NLA_U32 }, [WGDEVICE_A_LISTEN_PORT] = { .type = NLA_U16 }, [WGDEVICE_A_FWMARK] = { .type = NLA_U32 }, - [WGDEVICE_A_PEERS] = { .type = NLA_NESTED } + [WGDEVICE_A_PEERS] = { .type = NLA_NESTED }, + [WGDEVICE_A_MONITOR] = { .type = NLA_U8 }, + [WGDEVICE_A_PEER] = { .type = NLA_NESTED } }; static const struct nla_policy peer_policy[WGPEER_A_MAX + 1] = { @@ -233,7 +235,10 @@ static int wg_get_device_dump(struct sk_buff *skb, struct netlink_callback *cb) wg->incoming_port) || nla_put_u32(skb, WGDEVICE_A_FWMARK, wg->fwmark) || nla_put_u32(skb, WGDEVICE_A_IFINDEX, wg->dev->ifindex) || - nla_put_string(skb, WGDEVICE_A_IFNAME, wg->dev->name)) + nla_put_string(skb, WGDEVICE_A_IFNAME, wg->dev->name) || + nla_put_u8(skb, WGDEVICE_A_MONITOR, + (wg->endpoint_monitor ? WGDEVICE_MONITOR_F_ENDPOINT : 0) | + (wg->endpoint_monitor ? WGDEVICE_MONITOR_F_PEERS : 0))) goto out; down_read(&wg->static_identity.lock); @@ -482,6 +487,10 @@ static int set_peer(struct wg_device *wg, struct nlattr **attrs) if (netif_running(wg->dev)) wg_packet_send_staged_packets(peer); + if (wg->peers_monitor) { + wg_genl_mcast_peer_set(peer); + } + out: wg_peer_put(peer); if (attrs[WGPEER_A_PRESHARED_KEY]) @@ -537,6 +546,18 @@ static int wg_set_device(struct sk_buff *skb, struct genl_info *info) goto out; } + if (info->attrs[WGDEVICE_A_MONITOR]) { + u8 monitor = nla_get_u8(info->attrs[WGDEVICE_A_MONITOR]); + + if (monitor & ~__WGDEVICE_MONITOR_F_ALL) + goto out; + + wg->endpoint_monitor = + (monitor & WGDEVICE_MONITOR_F_ENDPOINT) == WGDEVICE_MONITOR_F_ENDPOINT; + wg->peers_monitor = + (monitor & WGDEVICE_MONITOR_F_PEERS) == WGDEVICE_MONITOR_F_PEERS; + } + if (flags & WGDEVICE_F_REPLACE_PEERS) wg_peer_remove_all(wg); @@ -617,6 +638,12 @@ static const struct genl_ops genl_ops[] = { } }; +static const struct genl_multicast_group wg_genl_mcgrps[] = { + { + .name = WG_MULTICAST_GROUP_PEERS + } +}; + static struct genl_family genl_family __ro_after_init = { .ops = genl_ops, .n_ops = ARRAY_SIZE(genl_ops), @@ -626,7 +653,9 @@ static struct genl_family genl_family __ro_after_init = { .maxattr = WGDEVICE_A_MAX, .module = THIS_MODULE, .policy = device_policy, - .netnsok = true + .netnsok = true, + .mcgrps = wg_genl_mcgrps, + .n_mcgrps = ARRAY_SIZE(wg_genl_mcgrps) }; int __init wg_genetlink_init(void) @@ -638,3 +667,199 @@ void __exit wg_genetlink_uninit(void) { genl_unregister_family(&genl_family); } + +int wg_genl_mcast_peer_set(struct wg_peer *peer) +{ + struct sk_buff *skb; + void *hdr; + struct nlattr *allowedips_nest, *peer_nest; + struct allowedips_node *allowedips_node; + int fail = 0; + + skb = genlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL); + if (skb == NULL) + return -ENOMEM; + + hdr = genlmsg_put(skb, 0, 0, &genl_family, 0, WG_CMD_SET_PEER); + if (hdr == NULL) { + fail = -EMSGSIZE; + goto err; + } + + if (nla_put_u32(skb, WGDEVICE_A_IFINDEX, peer->device->dev->ifindex) || + nla_put_string(skb, WGDEVICE_A_IFNAME, peer->device->dev->name)) + goto err; + + peer_nest = nla_nest_start(skb, WGDEVICE_A_PEER); + if (!peer_nest) { + fail = -EMSGSIZE; + goto err; + } + + if (nla_put_u16(skb, WGPEER_A_PERSISTENT_KEEPALIVE_INTERVAL, + peer->persistent_keepalive_interval)) + goto err; + + down_read(&peer->handshake.lock); + fail = nla_put(skb, WGPEER_A_PUBLIC_KEY, NOISE_PUBLIC_KEY_LEN, + peer->handshake.remote_static); + up_read(&peer->handshake.lock); + if (fail) + goto err; + + read_lock_bh(&peer->endpoint_lock); + if (peer->endpoint.addr.sa_family == AF_INET) + fail = nla_put(skb, WGPEER_A_ENDPOINT, + sizeof(peer->endpoint.addr4), &peer->endpoint.addr4); + else if (peer->endpoint.addr.sa_family == AF_INET6) + fail = nla_put(skb, WGPEER_A_ENDPOINT, + sizeof(peer->endpoint.addr6), &peer->endpoint.addr6); + read_unlock_bh(&peer->endpoint_lock); + if (fail) + goto err; + + allowedips_node = list_first_entry_or_null(&peer->allowedips_list, + struct allowedips_node, peer_list); + if (!allowedips_node) + goto no_allowedips; + + allowedips_nest = nla_nest_start(skb, WGPEER_A_ALLOWEDIPS); + if (!allowedips_nest) { + fail = -EMSGSIZE; + goto err; + } + + list_for_each_entry_from(allowedips_node, &peer->allowedips_list, + peer_list) { + u8 cidr, ip[16] __aligned(__alignof(u64)); + int family; + + family = wg_allowedips_read_node(allowedips_node, ip, &cidr); + if (get_allowedips(skb, ip, cidr, family)) { + nla_nest_end(skb, allowedips_nest); + goto err; + } + } + + nla_nest_end(skb, allowedips_nest); + +no_allowedips: + nla_nest_end(skb, peer_nest); + genlmsg_end(skb, hdr); + fail = genlmsg_multicast_netns(&genl_family, dev_net(peer->device->dev), + skb, 0, 0, GFP_KERNEL); + return fail; + +err: + nlmsg_free(skb); + return fail; +} + +int wg_genl_mcast_peer_remove(struct wg_peer *peer) +{ + struct sk_buff *skb; + void *hdr; + int fail = 0; + struct nlattr *peer_nest; + + skb = genlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL); + if (skb == NULL) + return -ENOMEM; + + hdr = genlmsg_put(skb, 0, 0, &genl_family, 0, WG_CMD_REMOVED_PEER); + if (hdr == NULL) { + fail = -EMSGSIZE; + goto err; + } + + if (nla_put_u32(skb, WGDEVICE_A_IFINDEX, peer->device->dev->ifindex) || + nla_put_string(skb, WGDEVICE_A_IFNAME, peer->device->dev->name)) { + fail = -EMSGSIZE; + goto err; + } + + peer_nest = nla_nest_start(skb, WGDEVICE_A_PEER); + if (!peer_nest) { + fail = -EMSGSIZE; + goto err; + } + + down_read(&peer->handshake.lock); + fail = nla_put(skb, WGPEER_A_PUBLIC_KEY, NOISE_PUBLIC_KEY_LEN, + peer->handshake.remote_static); + up_read(&peer->handshake.lock); + if (fail) { + goto err; + } + + nla_nest_end(skb, peer_nest); + genlmsg_end(skb, hdr); + fail = genlmsg_multicast_netns(&genl_family, dev_net(peer->device->dev), + skb, 0, 0, GFP_KERNEL); + return fail; + +err: + nlmsg_free(skb); + return fail; +} + +int wg_genl_mcast_peer_endpoint_change(struct wg_peer *peer) +{ + struct sk_buff *skb; + struct nlattr *peer_nest; + void *hdr; + int fail = 0; + + skb = genlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL); + if (skb == NULL) + return -ENOMEM; + + hdr = genlmsg_put(skb, 0, 0, &genl_family, 0, WG_CMD_CHANGED_ENDPOINT); + if (hdr == NULL) { + fail = -EMSGSIZE; + goto err; + } + + if (nla_put_u32(skb, WGDEVICE_A_IFINDEX, peer->device->dev->ifindex) || + nla_put_string(skb, WGDEVICE_A_IFNAME, peer->device->dev->name)) { + fail = -EMSGSIZE; + goto err; + } + + peer_nest = nla_nest_start(skb, WGDEVICE_A_PEER); + if (!peer_nest) { + fail = -EMSGSIZE; + goto err; + } + + down_read(&peer->handshake.lock); + fail = nla_put(skb, WGPEER_A_PUBLIC_KEY, NOISE_PUBLIC_KEY_LEN, + peer->handshake.remote_static); + up_read(&peer->handshake.lock); + if (fail) + goto err; + + read_lock_bh(&peer->endpoint_lock); + if (peer->endpoint.addr.sa_family == AF_INET) + fail = nla_put(skb, WGPEER_A_ENDPOINT, + sizeof(peer->endpoint.addr4), + &peer->endpoint.addr4); + else if (peer->endpoint.addr.sa_family == AF_INET6) + fail = nla_put(skb, WGPEER_A_ENDPOINT, + sizeof(peer->endpoint.addr6), + &peer->endpoint.addr6); + read_unlock_bh(&peer->endpoint_lock); + if (fail) + goto err; + + nla_nest_end(skb, peer_nest); + + genlmsg_end(skb, hdr); + fail = genlmsg_multicast_netns(&genl_family, dev_net(peer->device->dev), + skb, 0, 0, GFP_KERNEL); + return fail; + +err: + nlmsg_free(skb); + return fail; +} diff --git a/drivers/net/wireguard/netlink.h b/drivers/net/wireguard/netlink.h index 15100d92e2e3..28e2e307c838 100644 --- a/drivers/net/wireguard/netlink.h +++ b/drivers/net/wireguard/netlink.h @@ -6,6 +6,12 @@ #ifndef _WG_NETLINK_H #define _WG_NETLINK_H +#include "peer.h" + +int wg_genl_mcast_peer_endpoint_change(struct wg_peer *peer); +int wg_genl_mcast_peer_remove(struct wg_peer *peer); +int wg_genl_mcast_peer_set(struct wg_peer *peer); + int wg_genetlink_init(void); void wg_genetlink_uninit(void); diff --git a/drivers/net/wireguard/peer.c b/drivers/net/wireguard/peer.c index 1cb502a932e0..97407304152d 100644 --- a/drivers/net/wireguard/peer.c +++ b/drivers/net/wireguard/peer.c @@ -9,6 +9,7 @@ #include "timers.h" #include "peerlookup.h" #include "noise.h" +#include "netlink.h" #include #include @@ -157,8 +158,12 @@ void wg_peer_remove(struct wg_peer *peer) { if (unlikely(!peer)) return; - lockdep_assert_held(&peer->device->device_update_lock); + if (peer->device->peers_monitor) { + wg_genl_mcast_peer_remove(peer); + } + + lockdep_assert_held(&peer->device->device_update_lock); peer_make_dead(peer); synchronize_net(); peer_remove_after_dead(peer); diff --git a/drivers/net/wireguard/socket.c b/drivers/net/wireguard/socket.c index 0414d7a6ce74..33e4da2a37ee 100644 --- a/drivers/net/wireguard/socket.c +++ b/drivers/net/wireguard/socket.c @@ -8,6 +8,7 @@ #include "socket.h" #include "queueing.h" #include "messages.h" +#include "netlink.h" #include #include @@ -294,6 +295,10 @@ void wg_socket_set_peer_endpoint(struct wg_peer *peer, dst_cache_reset(&peer->endpoint_cache); out: write_unlock_bh(&peer->endpoint_lock); + + if (peer->device->endpoint_monitor) { + wg_genl_mcast_peer_endpoint_change(peer); + } } void wg_socket_set_peer_endpoint_from_skb(struct wg_peer *peer, diff --git a/include/uapi/linux/wireguard.h b/include/uapi/linux/wireguard.h index ae88be14c947..b28368773f74 100644 --- a/include/uapi/linux/wireguard.h +++ b/include/uapi/linux/wireguard.h @@ -29,6 +29,7 @@ * WGDEVICE_A_PUBLIC_KEY: NLA_EXACT_LEN, len WG_KEY_LEN * WGDEVICE_A_LISTEN_PORT: NLA_U16 * WGDEVICE_A_FWMARK: NLA_U32 + * WGDEVICE_A_MONITOR: NLA_U8 * WGDEVICE_A_PEERS: NLA_NESTED * 0: NLA_NESTED * WGPEER_A_PUBLIC_KEY: NLA_EXACT_LEN, len WG_KEY_LEN @@ -83,6 +84,9 @@ * 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_MONITOR: NLA_U8, set to a value of wgdevice_monitor_flag to + * enable monitoring of events using multicast messages + * over netlink * WGDEVICE_A_PEERS: NLA_NESTED * 0: NLA_NESTED * WGPEER_A_PUBLIC_KEY: len WG_KEY_LEN @@ -126,6 +130,59 @@ * of a peer, it likely should not be specified in subsequent fragments. * * If an error occurs, NLMSG_ERROR will reply containing an errno. + * + * WG_CMD_CHANGED_ENDPOINT + * ---------------------- + * + * This command is sent on the multicast group WG_MULTICAST_GROUP_PEERS + * when the endpoint of a peer is changed, either administratively or because + * of roaming. + * The kernel will send a single message containing the + * following tree of nested items: + * + * WGDEVICE_A_IFINDEX: NLA_U32 + * WGDEVICE_A_IFNAME: NLA_NUL_STRING, maxlen IFNAMSIZ - 1 + * WGDEVICE_A_PEER: NLA_NESTED + * WGPEER_A_PUBLIC_KEY: NLA_EXACT_LEN, len WG_KEY_LEN + * WGPEER_A_ENDPOINT: NLA_MIN_LEN(struct sockaddr), struct sockaddr_in or struct sockaddr_in6 + * + * WG_CMD_REMOVED_PEER + * ------------------- + * + * This command is sent on the multicast group WG_MULTICAST_GROUP_PEERS + * when a peer is removed. + * The kernel will send a single message containing the + * following tree of nested items: + * + * WGDEVICE_A_IFINDEX: NLA_U32 + * WGDEVICE_A_IFNAME: NLA_NUL_STRING, maxlen IFNAMSIZ - 1 + * WGDEVICE_A_PEER: NLA_NESTED + * WGPEER_A_PUBLIC_KEY: NLA_EXACT_LEN, len WG_KEY_LEN + * + * WG_CMD_SET_PEER + * --------------- + * + * This command is sent on the multicast group WG_MULTICAST_GROUP_PEERS + * when a peer is added or changed. + * The kernel will send a single message containing the + * following tree of nested items: + * + * WGDEVICE_A_IFINDEX: NLA_U32 + * WGDEVICE_A_IFNAME: NLA_NUL_STRING, maxlen IFNAMSIZ - 1 + * WGDEVICE_A_PEER: NLA_NESTED + * WGPEER_A_PUBLIC_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_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 + * ... + * */ #ifndef _WG_UAPI_WIREGUARD_H @@ -136,9 +193,14 @@ #define WG_KEY_LEN 32 +#define WG_MULTICAST_GROUP_PEERS "peers" + enum wg_cmd { WG_CMD_GET_DEVICE, WG_CMD_SET_DEVICE, + WG_CMD_CHANGED_ENDPOINT, + WG_CMD_REMOVED_PEER, + WG_CMD_SET_PEER, __WG_CMD_MAX }; #define WG_CMD_MAX (__WG_CMD_MAX - 1) @@ -157,9 +219,16 @@ enum wgdevice_attribute { WGDEVICE_A_LISTEN_PORT, WGDEVICE_A_FWMARK, WGDEVICE_A_PEERS, + WGDEVICE_A_MONITOR, + WGDEVICE_A_PEER, __WGDEVICE_A_LAST }; #define WGDEVICE_A_MAX (__WGDEVICE_A_LAST - 1) +enum wgdevice_monitor_flag { + WGDEVICE_MONITOR_F_ENDPOINT = 1U << 0, + WGDEVICE_MONITOR_F_PEERS = 1U << 1, + __WGDEVICE_MONITOR_F_ALL = WGDEVICE_MONITOR_F_ENDPOINT | WGDEVICE_MONITOR_F_PEERS +}; enum wgpeer_flag { WGPEER_F_REMOVE_ME = 1U << 0, base-commit: 0cf9deb3005f552a3d125436fc8ccedd31a925a9 -- 2.42.0 From evan at ewpratten.com Mon Oct 23 23:42:18 2023 From: evan at ewpratten.com (Evan Pratten) Date: Mon, 23 Oct 2023 23:42:18 -0000 Subject: [PATCH v3 RESEND^2] wg: Support restricting address family of DNS resolved Endpoint In-Reply-To: <20231023171723.137097-1-dxld@darkboxed.org> References: <20231023171723.137097-1-dxld@darkboxed.org> Message-ID: I'd also like to +1 this patch. While its manageable to keep a dedicated v4.vpn.example.com and v6.vpn.example.com for single-stack hosts, it would be much cleaner to properly handle this case. Plus, this seems to trip up lots of people who are deploying WireGuard+BGP setups to bring IPv6 to their homes (of which, the list keeps growing). Of everyone I've helped deploy such a setup, much more than half end up accidentally blackhole-ing their devices due to an accidental AAAA record. - Evan On Mon, Oct 23, 2023 at 1:37?PM Daniel Gr?ber wrote: > > When using wireguard tunnels for providing IPv6 connectivity to machines it > can be important to pin which IP address family should be used. > > Consider a peer using a DNS name with both A/AAAA records, wg will > currently blindly follow system policy and use the first address returned > by getaddrinfo(). In typical deployments this will cause the IPv6 address > of the peer to be used, however when the whole IPv6 internet is being > routed over our wg iface all this accomplishes is a traffic black hole. > > Naturally this can be worked around by having different DNS names for > v4-only / dual-stack addresses, however this may not be possible in some > situations where, say, a dynamic-DNS service is also in use. > > To fix this we allow users to control which address family they want using > the new AddressFamily= config option, see wg.8 for details. We also update > reresolve-dns to take the AddressFamily option into account. > > We would like to note that the not_oif patch[1] would also alleviate this > problem but since this never got merged it's not a workable solution. > > [1]: http://marc.info/?t=145452167200014&r=1&w=2 > > Signed-off-by: Daniel Gr?ber > --- > contrib/reresolve-dns/reresolve-dns.sh | 4 ++- > src/config.c | 41 ++++++++++++++++++++------ > src/config.h | 2 +- > src/containers.h | 5 ++++ > src/man/wg.8 | 8 ++++- > src/set.c | 9 +++++- > src/setconf.c | 2 +- > 7 files changed, 57 insertions(+), 14 deletions(-) > > diff --git a/contrib/reresolve-dns/reresolve-dns.sh b/contrib/reresolve-dns/reresolve-dns.sh > index 711c332..bdb47ac 100755 > --- a/contrib/reresolve-dns/reresolve-dns.sh > +++ b/contrib/reresolve-dns/reresolve-dns.sh > @@ -17,7 +17,7 @@ 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" > + wg set "$INTERFACE" peer "$PUBLIC_KEY" endpoint "$ENDPOINT" address-family "$FAMILY" > reset_peer_section > } > > @@ -25,6 +25,7 @@ reset_peer_section() { > PEER_SECTION=0 > PUBLIC_KEY="" > ENDPOINT="" > + FAMILY=unspec > } > > reset_peer_section > @@ -38,6 +39,7 @@ while read -r line || [[ -n $line ]]; do > case "$key" in > PublicKey) PUBLIC_KEY="$value"; continue ;; > Endpoint) ENDPOINT="$value"; continue ;; > + AddressFamily) FAMILY="$value"; continue ;; > esac > fi > done < "$CONFIG_FILE" > diff --git a/src/config.c b/src/config.c > index 1e924c7..f9980fe 100644 > --- a/src/config.c > +++ b/src/config.c > @@ -192,14 +192,14 @@ static inline int parse_dns_retries(void) > return (int)ret; > } > > -static inline bool parse_endpoint(struct sockaddr *endpoint, const char *value) > +static inline bool parse_endpoint(struct sockaddr *endpoint, const char *value, int family) > { > char *mutable = strdup(value); > char *begin, *end; > int ret, retries = parse_dns_retries(); > struct addrinfo *resolved; > struct addrinfo hints = { > - .ai_family = AF_UNSPEC, > + .ai_family = family, > .ai_socktype = SOCK_DGRAM, > .ai_protocol = IPPROTO_UDP > }; > @@ -279,6 +279,20 @@ static inline bool parse_endpoint(struct sockaddr *endpoint, const char *value) > return true; > } > > +static inline bool parse_address_family(int *family, const char *value) > +{ > + if (strcmp(value, "inet") == 0) > + *family = AF_INET; > + else if (strcmp(value, "inet6") == 0) > + *family = AF_INET6; > + else if (strcmp(value, "unspec") == 0) > + *family = AF_UNSPEC; > + else > + return false; > + > + return true; > +} > + > static inline bool parse_persistent_keepalive(uint16_t *interval, uint32_t *flags, const char *value) > { > unsigned long ret; > @@ -458,8 +472,10 @@ static bool process_line(struct config_ctx *ctx, const char *line) > 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")) { > + ctx->last_peer->endpoint_value = strdup(value); > + else if (key_match("AddressFamily")) { > + ret = parse_address_family(&ctx->last_peer->addr_fam, 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; > @@ -535,19 +551,22 @@ bool config_read_init(struct config_ctx *ctx, bool append) > return true; > } > > -struct wgdevice *config_read_finish(struct config_ctx *ctx) > +struct wgdevice *config_read_finish(struct wgdevice *device) > { > struct wgpeer *peer; > > - for_each_wgpeer(ctx->device, peer) { > + for_each_wgpeer(device, peer) { > if (!(peer->flags & WGPEER_HAS_PUBLIC_KEY)) { > fprintf(stderr, "A peer is missing a public key\n"); > goto err; > } > + > + if (!parse_endpoint(&peer->endpoint.addr, peer->endpoint_value, peer->addr_fam)) > + goto err; > } > - return ctx->device; > + return device; > err: > - free_wgdevice(ctx->device); > + free_wgdevice(device); > return NULL; > } > > @@ -619,7 +638,11 @@ struct wgdevice *config_read_cmd(const char *argv[], int argc) > argv += 1; > argc -= 1; > } else if (!strcmp(argv[0], "endpoint") && argc >= 2 && peer) { > - if (!parse_endpoint(&peer->endpoint.addr, argv[1])) > + peer->endpoint_value = strdup(argv[1]); > + argv += 2; > + argc -= 2; > + } else if (!strcmp(argv[0], "address-family") && argc >= 2 && peer) { > + if (!parse_address_family(&peer->addr_fam, argv[1])) > goto error; > argv += 2; > argc -= 2; > diff --git a/src/config.h b/src/config.h > index 443cf21..6f81da2 100644 > --- a/src/config.h > +++ b/src/config.h > @@ -22,6 +22,6 @@ struct config_ctx { > 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); > +struct wgdevice *config_read_finish(struct wgdevice *device); > > #endif > diff --git a/src/containers.h b/src/containers.h > index a82e8dd..c111621 100644 > --- a/src/containers.h > +++ b/src/containers.h > @@ -52,12 +52,15 @@ struct wgpeer { > uint8_t public_key[WG_KEY_LEN]; > uint8_t preshared_key[WG_KEY_LEN]; > > + char *endpoint_value; > union { > struct sockaddr addr; > struct sockaddr_in addr4; > struct sockaddr_in6 addr6; > } endpoint; > > + int addr_fam; > + > struct timespec64 last_handshake_time; > uint64_t rx_bytes, tx_bytes; > uint16_t persistent_keepalive_interval; > @@ -99,6 +102,8 @@ static inline void free_wgdevice(struct wgdevice *dev) > 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); > + if (peer->endpoint_value) > + free(peer->endpoint_value); > free(peer); > } > free(dev); > diff --git a/src/man/wg.8 b/src/man/wg.8 > index a5d8bcf..48f084d 100644 > --- a/src/man/wg.8 > +++ b/src/man/wg.8 > @@ -55,7 +55,7 @@ transfer-rx, transfer-tx, persistent-keepalive. > Shows the current configuration of \fI\fP in the format described > by \fICONFIGURATION FILE FORMAT\fP below. > .TP > -\fBset\fP \fI\fP [\fIlisten-port\fP \fI\fP] [\fIfwmark\fP \fI\fP] [\fIprivate-key\fP \fI\fP] [\fIpeer\fP \fI\fP [\fIremove\fP] [\fIpreshared-key\fP \fI\fP] [\fIendpoint\fP \fI:\fP] [\fIpersistent-keepalive\fP \fI\fP] [\fIallowed-ips\fP \fI/\fP[,\fI/\fP]...] ]... > +\fBset\fP \fI\fP [\fIlisten-port\fP \fI\fP] [\fIfwmark\fP \fI\fP] [\fIprivate-key\fP \fI\fP] [\fIpeer\fP \fI\fP [\fIremove\fP] [\fIpreshared-key\fP \fI\fP] [\fIendpoint\fP \fI:\fP] [\fIaddress-family\fP \fI\fP] [\fIpersistent-keepalive\fP \fI\fP] [\fIallowed-ips\fP \fI/\fP[,\fI/\fP]...] ]... > Sets configuration values for the specified \fI\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 > @@ -167,6 +167,12 @@ 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 > +AddressFamily \(em one of \fIinet\fP, \fIinet6\fP or \fIunspec\fP. When a > +hostname is given for \fIEndpoint\fP, setting this to \fIinet\fP or > +\fIinet6\fP will allow only addresses of the given family to be > +used. Defaults to \fIunspec\fP, which causes the first returned address of > +any type to be used. > +.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 > diff --git a/src/set.c b/src/set.c > index 75560fd..20ee85e 100644 > --- a/src/set.c > +++ b/src/set.c > @@ -18,13 +18,20 @@ int set_main(int argc, const char *argv[]) > int ret = 1; > > if (argc < 3) { > - fprintf(stderr, "Usage: %s %s [listen-port ] [fwmark ] [private-key ] [peer [remove] [preshared-key ] [endpoint :] [persistent-keepalive ] [allowed-ips /[,/]...] ]...\n", PROG_NAME, argv[0]); > + fprintf(stderr, "Usage: %s %s [listen-port ] [fwmark ] [private-key ] [peer [remove] [preshared-key ] [endpoint :] [address-family ] [persistent-keepalive ] [allowed-ips /[,/]...] ]...\n", PROG_NAME, argv[0]); > return 1; > } > > device = config_read_cmd(argv + 2, argc - 2); > if (!device) > goto cleanup; > + > + device = config_read_finish(device); > + if (!device) { > + fprintf(stderr, "Invalid configuration\n"); > + goto cleanup; > + } > + > strncpy(device->name, argv[1], IFNAMSIZ - 1); > device->name[IFNAMSIZ - 1] = '\0'; > > diff --git a/src/setconf.c b/src/setconf.c > index 1c5b138..c90fd30 100644 > --- a/src/setconf.c > +++ b/src/setconf.c > @@ -127,7 +127,7 @@ int setconf_main(int argc, const char *argv[]) > goto cleanup; > } > } > - device = config_read_finish(&ctx); > + device = config_read_finish(ctx.device); > if (!device) { > fprintf(stderr, "Invalid configuration\n"); > goto cleanup; > -- > 2.39.2 > >