New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

networkd: add support for wireguard vpn #4191

Closed
wants to merge 3 commits into
base: master
from

Conversation

@Mic92
Contributor

Mic92 commented Sep 20, 2016

This pull request adds support for wireguard in systemd-networkd. This is work
in progress and I want to open a discussion about inclusion of this feature
before putting too much effort in it.

What is WireGuard:

  • WireGuard is a new kernel-builtin vpn with modern fast cryptography
  • it competes with IPsec and Openvpn and beats both in terms of throughput, latency and usability

Why I want to include support for WireGuard into systemd-networkd:

WireGuard implements a new interface type called "wireguard".
WireGuard itself ships a command line util called wg for key management,
but lacks of support to configure ip addresses and routes on the interface.
My pull request intend to close this gap by implementing key management as
well as the new interface type in systemd-networkd.

Status of the WireGuard project:

  • it is an out-of-tree kernel module (>= linux v4.1) and packaged already for
    the following distribution: Debian, Fedora, Archlinux, OpenSUSE, Gentoo,
    NixOS, Openwrt, (Ubuntu as PPA)
  • the code is production ready
  • the protocol is still under development and review. Once it reaches 1.0, developers will attempt to upstream the kernel implementation into mainline

Questions to systemd maintainers:

Under what prerequisits wireguard support would be applicable for beeing merged?

Questions to wireguard team (@zx2c4):

WireGuard keys are managed via ioctl, is this API public?
If not, is header file uapi.h
public or will there be a netlink interface in future, which can be extented
backward-compatible?

cc: @steigr

@Mic92 Mic92 changed the title from add basic support for wireguard vpn to [networkd] add basic support for wireguard vpn Sep 20, 2016

@zx2c4

This comment has been minimized.

Show comment
Hide comment
@zx2c4

zx2c4 Sep 20, 2016

Contributor

Hey guys,

This is awesome. I'm very pleased to see this. I'll review the code and give a thorough answer to your questions shortly.

Jason

Contributor

zx2c4 commented Sep 20, 2016

Hey guys,

This is awesome. I'm very pleased to see this. I'll review the code and give a thorough answer to your questions shortly.

Jason

@zx2c4

This comment has been minimized.

Show comment
Hide comment
@zx2c4

zx2c4 Sep 21, 2016

Contributor

Hey again,

So, looking at the commit, it's pretty bare bones. In order for WireGuard to be properly supported in systemd-networkd, you'll need to actually configure the WireGuard-particular settings; otherwise it's not very interesting.

Fortunately, when I designed the userspace interface for WireGuard, I had in mind various nice interfaces for wg(8). One of those interfaces is a configuration file syntax. And, that configuration file syntax resembles the INI format, a choice already inspired by systemd-networkd. So, in an amazing full circle, systemd-networkd and WireGuard have a certain semantic closeness, which should make this part extremely easy. A WireGuard configuration file looks like this:

[Interface]
PrivateKey = yAnz5TF+lXXJte14tji3zlMNq+hd2rYUIgJBgB3fBmk=
ListenPort = 41414

[Peer]
PublicKey = xTIBA5rboUvnH4htodjb6e697QjLERt1NAB4mZqp8Dg=
AllowedIPs = 10.192.122.3/32, 10.192.124.1/24

[Peer]
PublicKey = TrMvSoP4jYQlY6RIzBgbssQqY3vxI2Pi+y71lOWWXX0=
AllowedIPs = 10.192.122.4/32, 192.168.0.0/16
PersistentKeepalive = 28

[Peer]
PublicKey = gN65BkIKy1eCE9pP1wdc8ROUtkHLF2PfAqYdyYBz6EA=
AllowedIPs = 10.10.10.230/32

That's pretty much it. It's straight forward to understand and parse. So I don't think you'll have any trouble with this part -- mapping it to systemd-networkd should be simple.

Onto your questions:

WireGuard keys are managed via ioctl, is this API public?

Yes, absolutely! I tried to make it a very pleasant interface for other applications to use.

is header file uapi.h public

Yes, this header file is public. Feel free to copy it into systemd-networkd if you'd like to do it that way. I'll then make a [physical] note to submit a pull request here in case that API is ever augmented.

will there be a netlink interface in future, which can be extented backward-compatible

Depending on various ongoing discussions with the other kernel netdev developers, we may wind up adding a netlink interface. But, for now, consider that uapi.h ioctl quite stable.

Contributor

zx2c4 commented Sep 21, 2016

Hey again,

So, looking at the commit, it's pretty bare bones. In order for WireGuard to be properly supported in systemd-networkd, you'll need to actually configure the WireGuard-particular settings; otherwise it's not very interesting.

Fortunately, when I designed the userspace interface for WireGuard, I had in mind various nice interfaces for wg(8). One of those interfaces is a configuration file syntax. And, that configuration file syntax resembles the INI format, a choice already inspired by systemd-networkd. So, in an amazing full circle, systemd-networkd and WireGuard have a certain semantic closeness, which should make this part extremely easy. A WireGuard configuration file looks like this:

[Interface]
PrivateKey = yAnz5TF+lXXJte14tji3zlMNq+hd2rYUIgJBgB3fBmk=
ListenPort = 41414

[Peer]
PublicKey = xTIBA5rboUvnH4htodjb6e697QjLERt1NAB4mZqp8Dg=
AllowedIPs = 10.192.122.3/32, 10.192.124.1/24

[Peer]
PublicKey = TrMvSoP4jYQlY6RIzBgbssQqY3vxI2Pi+y71lOWWXX0=
AllowedIPs = 10.192.122.4/32, 192.168.0.0/16
PersistentKeepalive = 28

[Peer]
PublicKey = gN65BkIKy1eCE9pP1wdc8ROUtkHLF2PfAqYdyYBz6EA=
AllowedIPs = 10.10.10.230/32

That's pretty much it. It's straight forward to understand and parse. So I don't think you'll have any trouble with this part -- mapping it to systemd-networkd should be simple.

Onto your questions:

WireGuard keys are managed via ioctl, is this API public?

Yes, absolutely! I tried to make it a very pleasant interface for other applications to use.

is header file uapi.h public

Yes, this header file is public. Feel free to copy it into systemd-networkd if you'd like to do it that way. I'll then make a [physical] note to submit a pull request here in case that API is ever augmented.

will there be a netlink interface in future, which can be extented backward-compatible

Depending on various ongoing discussions with the other kernel netdev developers, we may wind up adding a netlink interface. But, for now, consider that uapi.h ioctl quite stable.

@Mic92 Mic92 changed the title from [networkd] add basic support for wireguard vpn to [networkd][wip] add basic support for wireguard vpn Sep 21, 2016

@keszybz

This comment has been minimized.

Show comment
Hide comment
@keszybz

keszybz Sep 23, 2016

Member

What's the upstreaming status of wireguard? I guess the systemd-networkd part for this would be rather self-contained, so we could discuss merging the support on provisional basis, but normally we'd wait for the kernel to merge support first.

As for the patch set: it would need to be extended to actually configure the interface. I guess reading the wireguard configuration file for the Peer list and Interface config would be OK, if the format is stable and public, and doesn't mix other stuff. Otherwise, a [Wireguard] section could be added to our config files. Do you have a proposal for how this would look, and how the configuration and support will be split?

Member

keszybz commented Sep 23, 2016

What's the upstreaming status of wireguard? I guess the systemd-networkd part for this would be rather self-contained, so we could discuss merging the support on provisional basis, but normally we'd wait for the kernel to merge support first.

As for the patch set: it would need to be extended to actually configure the interface. I guess reading the wireguard configuration file for the Peer list and Interface config would be OK, if the format is stable and public, and doesn't mix other stuff. Otherwise, a [Wireguard] section could be added to our config files. Do you have a proposal for how this would look, and how the configuration and support will be split?

@keszybz keszybz added the network label Sep 23, 2016

@Mic92

This comment has been minimized.

Show comment
Hide comment
@Mic92

Mic92 Sep 23, 2016

Contributor

@keszybz thanks for you feedback. I am currently working on adding a [wireguard] section to networkd's config, so the implementation will be self-contained. Waiting for a merge into kernel would be ok for me. my proposal would be the following:

# wg0.netdev
[Match]
Name=wg0
Kind=wireguard

[Wireguard]
# this is mandatory, if systemd is supposed to configure wireguard completely
PrivateKey=<private_key> 
# optional, makes wireguard ready for the post-quantum world
PresharedKey=<key>
# mandatory
ListenPort=6666

[Peer]
# mandatory
PublicKey=<public_key>
# I am not sure what the would the best solution here
# with growing number of subnets, this is easier to read
AllowedIP=10.0.0.1/32
AllowedIP=[fd42::1]/64
# ALTERNATIVE: this is what wireguard's config look like
# however sometimes people use the wrong separator, 
# so the version above is harder to mess up.
AllowedIPs=10.0.0.1/32, [fd42::1]/64
# optional
Endpoint=some.host.tld:443

# multiple peers allowed.
[Peer]
PublicKey=<public_key2>
Contributor

Mic92 commented Sep 23, 2016

@keszybz thanks for you feedback. I am currently working on adding a [wireguard] section to networkd's config, so the implementation will be self-contained. Waiting for a merge into kernel would be ok for me. my proposal would be the following:

# wg0.netdev
[Match]
Name=wg0
Kind=wireguard

[Wireguard]
# this is mandatory, if systemd is supposed to configure wireguard completely
PrivateKey=<private_key> 
# optional, makes wireguard ready for the post-quantum world
PresharedKey=<key>
# mandatory
ListenPort=6666

[Peer]
# mandatory
PublicKey=<public_key>
# I am not sure what the would the best solution here
# with growing number of subnets, this is easier to read
AllowedIP=10.0.0.1/32
AllowedIP=[fd42::1]/64
# ALTERNATIVE: this is what wireguard's config look like
# however sometimes people use the wrong separator, 
# so the version above is harder to mess up.
AllowedIPs=10.0.0.1/32, [fd42::1]/64
# optional
Endpoint=some.host.tld:443

# multiple peers allowed.
[Peer]
PublicKey=<public_key2>
@zx2c4

This comment has been minimized.

Show comment
Hide comment
@zx2c4

zx2c4 Sep 23, 2016

Contributor

Hi @Mic92,

You wrote:

AllowedIP=10.0.0.1/32
AllowedIP=[fd42::1]/64

You SHOULD HAVE written:

AllowedIPs=10.0.0.1/32
AllowedIPs=[fd42::1]/64

EVERYWHERE else, WireGuard consistently uses "AllowedIPs", because this represents a list or a mask (or just a single IP). Please fix this PR to use the same label.

Update: fixed in the latest commit, thanks!

Contributor

zx2c4 commented Sep 23, 2016

Hi @Mic92,

You wrote:

AllowedIP=10.0.0.1/32
AllowedIP=[fd42::1]/64

You SHOULD HAVE written:

AllowedIPs=10.0.0.1/32
AllowedIPs=[fd42::1]/64

EVERYWHERE else, WireGuard consistently uses "AllowedIPs", because this represents a list or a mask (or just a single IP). Please fix this PR to use the same label.

Update: fixed in the latest commit, thanks!

@Mic92

This comment has been minimized.

Show comment
Hide comment
@Mic92

Mic92 Sep 23, 2016

Contributor

I was aware, that wireguard is using plural and my first proposed option was singular.
The idea was to split up the list into single item options, because this for example easier to
generate in deployment scripts and I once had the case, where somebody used the list variant wrong.
But it is also a fair point, that this syntax would not match with the output of wg(8),
so I will stick with this version.

Contributor

Mic92 commented Sep 23, 2016

I was aware, that wireguard is using plural and my first proposed option was singular.
The idea was to split up the list into single item options, because this for example easier to
generate in deployment scripts and I once had the case, where somebody used the list variant wrong.
But it is also a fair point, that this syntax would not match with the output of wg(8),
so I will stick with this version.

@zx2c4

This comment has been minimized.

Show comment
Hide comment
@zx2c4

zx2c4 Sep 23, 2016

Contributor

Even with one item per line, /24 still expands to 255 distinct IPs, so the name AllowedIPs still makes sense, even if its one per line.

Contributor

zx2c4 commented Sep 23, 2016

Even with one item per line, /24 still expands to 255 distinct IPs, so the name AllowedIPs still makes sense, even if its one per line.

@zx2c4

This is a great start! Thanks so much.

Show outdated Hide outdated src/network/networkd-netdev-gperf.gperf
@@ -53,6 +54,9 @@ Tunnel.CopyDSCP, config_parse_bool, 0,
Tunnel.EncapsulationLimit, config_parse_encap_limit, 0, offsetof(Tunnel, encap_limit)
Peer.Name, config_parse_ifname, 0, offsetof(Veth, ifname_peer)
Peer.MACAddress, config_parse_hwaddr, 0, offsetof(Veth, mac_peer)
Peer.PublicKey, config_parse_wireguard_public_key, 0, 0
Peer.AllowedIPs, config_parse_wireguard_ips, 0, 0

This comment has been minimized.

@zx2c4

zx2c4 Sep 23, 2016

Contributor

---> config_parse_wireguard_allowed_ips

@zx2c4

zx2c4 Sep 23, 2016

Contributor

---> config_parse_wireguard_allowed_ips

This comment has been minimized.

@poettering

poettering Oct 11, 2016

Member

I'd probably name the "PublicKey" option "WiregardPublicKey" or so if it is in the generic [Peer] section. It's fine to leave fully generic options with generic names (i.e. AllowedIPs= is pretty generic I'd say), but I'd guess that the wireguard public keys would be in a different format, than let's say ipsec would have them, hence I'd prefix the setting here to indicate that this option is supposed to be in wireguard's format, and thing else.

@poettering

poettering Oct 11, 2016

Member

I'd probably name the "PublicKey" option "WiregardPublicKey" or so if it is in the generic [Peer] section. It's fine to leave fully generic options with generic names (i.e. AllowedIPs= is pretty generic I'd say), but I'd guess that the wireguard public keys would be in a different format, than let's say ipsec would have them, hence I'd prefix the setting here to indicate that this option is supposed to be in wireguard's format, and thing else.

Show outdated Hide outdated src/network/networkd-netdev-wireguard.c
const char *rvalue,
void *data,
void *userdata) {
return 0;

This comment has been minimized.

@zx2c4

zx2c4 Sep 23, 2016

Contributor

Fortunately this is just a simple unbase64 too.

@zx2c4

zx2c4 Sep 23, 2016

Contributor

Fortunately this is just a simple unbase64 too.

Show outdated Hide outdated src/network/networkd-netdev-wireguard.c
const char *rvalue,
void *data,
void *userdata) {
return 0;

This comment has been minimized.

@zx2c4
@zx2c4

This comment has been minimized.

@keszybz

keszybz Sep 24, 2016

Member

There's very similar parsing in config_parse_address in src/network/networkd-address.c. You might need to factor out the middle part of that function into a new function.

@keszybz

keszybz Sep 24, 2016

Member

There's very similar parsing in config_parse_address in src/network/networkd-address.c. You might need to factor out the middle part of that function into a new function.

This comment has been minimized.

@zx2c4

zx2c4 Sep 27, 2016

Contributor

Using those parsing functions sounds okay to me.

@zx2c4

zx2c4 Sep 27, 2016

Contributor

Using those parsing functions sounds okay to me.

Show outdated Hide outdated src/network/networkd-netdev-wireguard.c
void *data,
void *userdata) {
return 0;
}

This comment has been minimized.

@zx2c4

zx2c4 Sep 23, 2016

Contributor

Likewise, you can copy the code used here:
https://git.zx2c4.com/WireGuard/tree/src/tools/config.c#n121

@zx2c4

zx2c4 Sep 23, 2016

Contributor

Likewise, you can copy the code used here:
https://git.zx2c4.com/WireGuard/tree/src/tools/config.c#n121

Show outdated Hide outdated src/network/networkd-netdev-wireguard.c
const char *rvalue,
void *data,
void *userdata) {
return 0;

This comment has been minimized.

@zx2c4

zx2c4 Sep 23, 2016

Contributor

To make this a bit nicer, I use getaddrinfo to lookup the port. You can copy the code here:
https://git.zx2c4.com/WireGuard/tree/src/tools/config.c#n59

@zx2c4

zx2c4 Sep 23, 2016

Contributor

To make this a bit nicer, I use getaddrinfo to lookup the port. You can copy the code here:
https://git.zx2c4.com/WireGuard/tree/src/tools/config.c#n59

Show outdated Hide outdated src/network/networkd-netdev-wireguard.h
#define WG_KEY_LEN 32
struct Wireguard {
NetDev meta;

This comment has been minimized.

@zx2c4

zx2c4 Sep 23, 2016

Contributor

You could just embed the uapi struct inside this one, so you don't have to replicate each member.

@zx2c4

zx2c4 Sep 23, 2016

Contributor

You could just embed the uapi struct inside this one, so you don't have to replicate each member.

Show outdated Hide outdated src/network/networkd-netdev-gperf.gperf
@@ -53,6 +54,9 @@ Tunnel.CopyDSCP, config_parse_bool, 0,
Tunnel.EncapsulationLimit, config_parse_encap_limit, 0, offsetof(Tunnel, encap_limit)
Peer.Name, config_parse_ifname, 0, offsetof(Veth, ifname_peer)
Peer.MACAddress, config_parse_hwaddr, 0, offsetof(Veth, mac_peer)
Peer.PublicKey, config_parse_wireguard_public_key, 0, 0
Peer.AllowedIPs, config_parse_wireguard_ips, 0, 0
Peer.Endpoint, config_parse_wireguard_endpoint, 0, 0

This comment has been minimized.

@zx2c4

zx2c4 Sep 23, 2016

Contributor

TODO, I assume?

@zx2c4

zx2c4 Sep 23, 2016

Contributor

TODO, I assume?

This comment has been minimized.

@Mic92

Mic92 Sep 23, 2016

Contributor

yes, hence the name WIP in the commit message.

@Mic92

Mic92 Sep 23, 2016

Contributor

yes, hence the name WIP in the commit message.

@keszybz

This comment has been minimized.

Show comment
Hide comment
@keszybz

keszybz Sep 24, 2016

Member

The multiline version of AllowedIPs is better (more readable). We can always allow multiple items on the same line later if wanted.

Member

keszybz commented Sep 24, 2016

The multiline version of AllowedIPs is better (more readable). We can always allow multiple items on the same line later if wanted.

@keszybz keszybz changed the title from [networkd][wip] add basic support for wireguard vpn to [wip] networkd: add basic support for wireguard vpn Sep 24, 2016

@zx2c4

This comment has been minimized.

Show comment
Hide comment
@zx2c4

zx2c4 Sep 27, 2016

Contributor

@keszybz

The multiline version of AllowedIPs is better (more readable). We can always allow multiple items on the same line later if wanted.

The mainline wireguard-tools (wg(8)) actually support multiple AllowedIPs entries lines:

[Peer]
PublicKey=mF3bG9/gY17AKLJnOfICWtQzCRbPE31Yr9Jw0COwk2U=
AllowedIPs=10.0.0.0/16
AllowedIPs=[fd42::1]/64
AllowedIPs=192.168.1.0/24,[abcd::]/96
Endpoint=some.host.tld:443

Notice that they're all titled AllowedIPs, since a mask like this doesn't imply a single IP.

The above config snippet is valid in wg(8). So, I think your and @Mic92's assessment is correct about this, and now both utilities use the same syntax, which is great.

Contributor

zx2c4 commented Sep 27, 2016

@keszybz

The multiline version of AllowedIPs is better (more readable). We can always allow multiple items on the same line later if wanted.

The mainline wireguard-tools (wg(8)) actually support multiple AllowedIPs entries lines:

[Peer]
PublicKey=mF3bG9/gY17AKLJnOfICWtQzCRbPE31Yr9Jw0COwk2U=
AllowedIPs=10.0.0.0/16
AllowedIPs=[fd42::1]/64
AllowedIPs=192.168.1.0/24,[abcd::]/96
Endpoint=some.host.tld:443

Notice that they're all titled AllowedIPs, since a mask like this doesn't imply a single IP.

The above config snippet is valid in wg(8). So, I think your and @Mic92's assessment is correct about this, and now both utilities use the same syntax, which is great.

@keszybz

This comment has been minimized.

Show comment
Hide comment
@keszybz

keszybz Sep 27, 2016

Member

On Tue, Sep 27, 2016 at 12:03:21PM -0700, Jason A. Donenfeld wrote:

The mainline wireguard-tools (wg(8)) actually support multiple AllowedIPs entries lines:

Then yes, it's probably best to implement compatible syntax in
systemd-networkd too.

Member

keszybz commented Sep 27, 2016

On Tue, Sep 27, 2016 at 12:03:21PM -0700, Jason A. Donenfeld wrote:

The mainline wireguard-tools (wg(8)) actually support multiple AllowedIPs entries lines:

Then yes, it's probably best to implement compatible syntax in
systemd-networkd too.

@johannbg

This comment has been minimized.

Show comment
Hide comment
@johannbg

johannbg Sep 27, 2016

Contributor

Let's see how this progresses with the upstream kernel community before implementing this and take into consideration how best this could be implemented and managed within and out containers .

Contributor

johannbg commented Sep 27, 2016

Let's see how this progresses with the upstream kernel community before implementing this and take into consideration how best this could be implemented and managed within and out containers .

@zx2c4

This comment has been minimized.

Show comment
Hide comment
@zx2c4

zx2c4 Sep 27, 2016

Contributor

Hi @johannbg -- I see you're not marked as a systemd project member, so I'll take what you suggested with a grain of salt, but you do bring up two good points for which I think I have two good answers, below.

Let's see how this progresses with the upstream kernel community

One approach is to wait for upstream, sure, and this sort of policy will be decided by the systemd devs for when they want to pull the patch. However, I firmly believe that implementing this early on -- whether or not its merged immediately -- is very important. The reason is that the experience of seeing how integrations work out (even if they're living in a pull request) is often very helpful for determining the roadmap and direction of both projects, the integrator and the integratee. For example, I can imagine WireGuard upstream learns a lot from systemd-networkd developers' experiences with implementing the integration. And I can imagine systemd-networkd developers learn a lot about the future of networking by working on the integrations. As an investigative exercise, it can only benefit everybody. Then, when things are looking perfect, we'll look into where and when we want to merge, and what the best plan for that is. Since we'll already have done astute research work, this will positively benefit the upstream kernel conversation. In the end, everybody will be happier. The more experience, the better.

take into consideration how best this could be implemented and managed within and out containers .

This particular technical matter is something to which upstream has given a lot of thought, and there is an extended document describing this here: https://www.wireguard.io/netns/
I'd highly suggest interested systemd-networkd developers read this, as there's a lot of food for thought. Probably discussion about this that isn't directly related to this pull request is best directed toward the WireGuard upstream mailing list: https://lists.zx2c4.com/mailman/listinfo/wireguard

Contributor

zx2c4 commented Sep 27, 2016

Hi @johannbg -- I see you're not marked as a systemd project member, so I'll take what you suggested with a grain of salt, but you do bring up two good points for which I think I have two good answers, below.

Let's see how this progresses with the upstream kernel community

One approach is to wait for upstream, sure, and this sort of policy will be decided by the systemd devs for when they want to pull the patch. However, I firmly believe that implementing this early on -- whether or not its merged immediately -- is very important. The reason is that the experience of seeing how integrations work out (even if they're living in a pull request) is often very helpful for determining the roadmap and direction of both projects, the integrator and the integratee. For example, I can imagine WireGuard upstream learns a lot from systemd-networkd developers' experiences with implementing the integration. And I can imagine systemd-networkd developers learn a lot about the future of networking by working on the integrations. As an investigative exercise, it can only benefit everybody. Then, when things are looking perfect, we'll look into where and when we want to merge, and what the best plan for that is. Since we'll already have done astute research work, this will positively benefit the upstream kernel conversation. In the end, everybody will be happier. The more experience, the better.

take into consideration how best this could be implemented and managed within and out containers .

This particular technical matter is something to which upstream has given a lot of thought, and there is an extended document describing this here: https://www.wireguard.io/netns/
I'd highly suggest interested systemd-networkd developers read this, as there's a lot of food for thought. Probably discussion about this that isn't directly related to this pull request is best directed toward the WireGuard upstream mailing list: https://lists.zx2c4.com/mailman/listinfo/wireguard

@johannbg

This comment has been minimized.

Show comment
Hide comment
@johannbg

johannbg Sep 28, 2016

Contributor

Throw some pepper on that grain of salt and get your stuff accepted upstream first.
Have you even submitted it any form upstream yet or is that still on hold or do you really expect that everybody wants to sign them self up for carrying out of tree patches/module to the kernel indefinitely to be able to use this?.

Let's wait and see Tom approves this without this actually being merged upstream first. Lennart did so with cgroupv2 so that might be a trend now what once was frown upon.

Contributor

johannbg commented Sep 28, 2016

Throw some pepper on that grain of salt and get your stuff accepted upstream first.
Have you even submitted it any form upstream yet or is that still on hold or do you really expect that everybody wants to sign them self up for carrying out of tree patches/module to the kernel indefinitely to be able to use this?.

Let's wait and see Tom approves this without this actually being merged upstream first. Lennart did so with cgroupv2 so that might be a trend now what once was frown upon.

@zx2c4

This comment has been minimized.

Show comment
Hide comment
@zx2c4

zx2c4 Sep 28, 2016

Contributor

Throw some pepper on that grain of salt and get your stuff accepted upstream first.

That's not really a very intellectually constructive way to move the discussion forward. I'm interested in other peoples' opinions at this point; I think you've made your point now twice. I'll make mine one more time too: regardless of what's decided on the "when" question for merging a pull request like this, there's a lot to be learned and gained from both upstream wireguard and from systemd-networkd in seeing what an implementation would look like. It is research I am extremely interested in as a means of understanding more precisely how the ecosystem puts parts together.

Have you even submitted it any form upstream yet

do you really expect that everybody wants to sign them self up for carrying out of tree patches/module to the kernel indefinitely to be able to use this

I have never, not once, suggested anything of that nature.

Let's wait and see

Let's not wait and see. Instead, let's forge ahead with doing valuable research on future networking technologies, and learning how ecosystem integrations work. I know I don't intend to stop tinkering around with the systemd codebase in relation to wireguard, and I certainly wouldn't encourage anybody else to stop either.


@johannbg - please do not reply to this message or to this thread. You've made your point; I've made mine. Now I'd like to resume the useful technical discussion on here.

Contributor

zx2c4 commented Sep 28, 2016

Throw some pepper on that grain of salt and get your stuff accepted upstream first.

That's not really a very intellectually constructive way to move the discussion forward. I'm interested in other peoples' opinions at this point; I think you've made your point now twice. I'll make mine one more time too: regardless of what's decided on the "when" question for merging a pull request like this, there's a lot to be learned and gained from both upstream wireguard and from systemd-networkd in seeing what an implementation would look like. It is research I am extremely interested in as a means of understanding more precisely how the ecosystem puts parts together.

Have you even submitted it any form upstream yet

do you really expect that everybody wants to sign them self up for carrying out of tree patches/module to the kernel indefinitely to be able to use this

I have never, not once, suggested anything of that nature.

Let's wait and see

Let's not wait and see. Instead, let's forge ahead with doing valuable research on future networking technologies, and learning how ecosystem integrations work. I know I don't intend to stop tinkering around with the systemd codebase in relation to wireguard, and I certainly wouldn't encourage anybody else to stop either.


@johannbg - please do not reply to this message or to this thread. You've made your point; I've made mine. Now I'd like to resume the useful technical discussion on here.

@teg

This comment has been minimized.

Show comment
Hide comment
@teg

teg Oct 1, 2016

Contributor

@Mic92 thanks for the patch! I'm happy to merge support for this. However, we try not to merge support for things before they are upstream, so let's keep the discussion going and once the module is has been merged upstream we can apply your patch.

One request: it would be very nice if the kernel api was done as a netlink api rather than ioctls (though I admit I did not look into if there was a good reason not to do that), so that we could manage this like everything else.

Contributor

teg commented Oct 1, 2016

@Mic92 thanks for the patch! I'm happy to merge support for this. However, we try not to merge support for things before they are upstream, so let's keep the discussion going and once the module is has been merged upstream we can apply your patch.

One request: it would be very nice if the kernel api was done as a netlink api rather than ioctls (though I admit I did not look into if there was a good reason not to do that), so that we could manage this like everything else.

@zx2c4

This comment has been minimized.

Show comment
Hide comment
@zx2c4

zx2c4 Oct 1, 2016

Contributor

Hey @teg -- good to hear from you.

let's keep the discussion going and once the module is has been merged upstream we can apply your patch

Sounds good to me.

it would be very nice if the kernel api was done as a netlink api rather than ioctls

So in developing WireGuard, for each component, I did several different implementations for each one. For example, there's a very particular lookup data structure for some odd part of wireguard, and I implemented this as a linked list, as a radix trie, as an lc-trie, as an art table, and so forth. Each implementation I did with a fresh mind. At the end, I chose the one that I thought came out the best, with an emphasis on being clean, minimal, maintainable, and auditable.

So too, I did the same thing with implementing the configuration interface. I wrote four different netlink implementations, each one written about a month apart, with a fresh mind and fresh ideas on how I could make it the prettiest. This might seem like an excessive exercise by somebody who likes to code a little too much, but it did pay off; each one was better than the previous. However, one day I wondered, "I wonder what an old school simple ioctl-based implementation would look like." Not more than an hour later did I have something half the length and double the clarity. It was just so much ridiculously better and nicer to work with that I dropped the netlink work all together and moved to the simpler ioctl interface. The resultant API is here, in uapi.h. I think you'll agree that this is a pretty nice and simple interface.

One of the reasons I liked the ioctl-based approach a lot more than the netlink one is that it didn't require me to buffer anything in the kernel. With netlink, the entire message gets composed and buffered in kernel space, and then it's transferred to userspace. When returning a peer list to userspace, with 65536 possible total peers, it's obviously not feasible to stuff them inside one netlink message, so an ugly paging multi-packet interface is required, and this gets ugly really fast. However, with the ioctl, userspace can first ask, "how much memory do I need to allocate", then userspace allocates this memory, passes a pointer to kernelspace, and then the kernel can safely and iteratively copy into this userspace region, with no huge kernelspace allocations required. This is quite nice, and I think the implementation turned out very simply.

So, with all this in mind, what precisely is my motivation for moving to netlink? (The strongest arguments in favor of netlink I've heard are related to syscall filtering being difficult with private ioctls and opaque fds, but I don't find this entirely compelling.) And after reading the above, if you believe I still should, do you have in mind any constructive suggestions for the nicest way to design such an netlink interface that won't wind up being ugly or cumbersome?

(The other [slightly less compelling but still important nonetheless] benefit to ioctl is that the same serialization format can very easily be used to talk to userspace wireguard implementations on linux, mac, and windows alike, and in fact a client app speaking this interface over a unix socket already lives in mac's homebrew.)

Contributor

zx2c4 commented Oct 1, 2016

Hey @teg -- good to hear from you.

let's keep the discussion going and once the module is has been merged upstream we can apply your patch

Sounds good to me.

it would be very nice if the kernel api was done as a netlink api rather than ioctls

So in developing WireGuard, for each component, I did several different implementations for each one. For example, there's a very particular lookup data structure for some odd part of wireguard, and I implemented this as a linked list, as a radix trie, as an lc-trie, as an art table, and so forth. Each implementation I did with a fresh mind. At the end, I chose the one that I thought came out the best, with an emphasis on being clean, minimal, maintainable, and auditable.

So too, I did the same thing with implementing the configuration interface. I wrote four different netlink implementations, each one written about a month apart, with a fresh mind and fresh ideas on how I could make it the prettiest. This might seem like an excessive exercise by somebody who likes to code a little too much, but it did pay off; each one was better than the previous. However, one day I wondered, "I wonder what an old school simple ioctl-based implementation would look like." Not more than an hour later did I have something half the length and double the clarity. It was just so much ridiculously better and nicer to work with that I dropped the netlink work all together and moved to the simpler ioctl interface. The resultant API is here, in uapi.h. I think you'll agree that this is a pretty nice and simple interface.

One of the reasons I liked the ioctl-based approach a lot more than the netlink one is that it didn't require me to buffer anything in the kernel. With netlink, the entire message gets composed and buffered in kernel space, and then it's transferred to userspace. When returning a peer list to userspace, with 65536 possible total peers, it's obviously not feasible to stuff them inside one netlink message, so an ugly paging multi-packet interface is required, and this gets ugly really fast. However, with the ioctl, userspace can first ask, "how much memory do I need to allocate", then userspace allocates this memory, passes a pointer to kernelspace, and then the kernel can safely and iteratively copy into this userspace region, with no huge kernelspace allocations required. This is quite nice, and I think the implementation turned out very simply.

So, with all this in mind, what precisely is my motivation for moving to netlink? (The strongest arguments in favor of netlink I've heard are related to syscall filtering being difficult with private ioctls and opaque fds, but I don't find this entirely compelling.) And after reading the above, if you believe I still should, do you have in mind any constructive suggestions for the nicest way to design such an netlink interface that won't wind up being ugly or cumbersome?

(The other [slightly less compelling but still important nonetheless] benefit to ioctl is that the same serialization format can very easily be used to talk to userspace wireguard implementations on linux, mac, and windows alike, and in fact a client app speaking this interface over a unix socket already lives in mac's homebrew.)

@poettering

This comment has been minimized.

Show comment
Hide comment
@poettering

poettering Oct 11, 2016

Member

So, yeah, I think this looks great, and we should merge it, but I figure we should wait until it's clear that this will go in upstream in the kernel. (we don't have to wait until it's actually merged, but at least until the time where it is clear that everything is good, and the kernel/userspace interface for it is considered stable.)

Member

poettering commented Oct 11, 2016

So, yeah, I think this looks great, and we should merge it, but I figure we should wait until it's clear that this will go in upstream in the kernel. (we don't have to wait until it's actually merged, but at least until the time where it is clear that everything is good, and the kernel/userspace interface for it is considered stable.)

@poettering

I commented on a couple of superficial review issues

Show outdated Hide outdated src/network/networkd-netdev-wireguard.c
#include "networkd-netdev-wireguard.h"
const NetDevVTable wireguard_vtable = {

This comment has been minimized.

@poettering

poettering Oct 11, 2016

Member

spurious double newline...

@poettering

poettering Oct 11, 2016

Member

spurious double newline...

Show outdated Hide outdated src/network/networkd-netdev-gperf.gperf
@@ -53,6 +54,9 @@ Tunnel.CopyDSCP, config_parse_bool, 0,
Tunnel.EncapsulationLimit, config_parse_encap_limit, 0, offsetof(Tunnel, encap_limit)
Peer.Name, config_parse_ifname, 0, offsetof(Veth, ifname_peer)
Peer.MACAddress, config_parse_hwaddr, 0, offsetof(Veth, mac_peer)
Peer.PublicKey, config_parse_wireguard_public_key, 0, 0
Peer.AllowedIPs, config_parse_wireguard_ips, 0, 0

This comment has been minimized.

@poettering

poettering Oct 11, 2016

Member

I'd probably name the "PublicKey" option "WiregardPublicKey" or so if it is in the generic [Peer] section. It's fine to leave fully generic options with generic names (i.e. AllowedIPs= is pretty generic I'd say), but I'd guess that the wireguard public keys would be in a different format, than let's say ipsec would have them, hence I'd prefix the setting here to indicate that this option is supposed to be in wireguard's format, and thing else.

@poettering

poettering Oct 11, 2016

Member

I'd probably name the "PublicKey" option "WiregardPublicKey" or so if it is in the generic [Peer] section. It's fine to leave fully generic options with generic names (i.e. AllowedIPs= is pretty generic I'd say), but I'd guess that the wireguard public keys would be in a different format, than let's say ipsec would have them, hence I'd prefix the setting here to indicate that this option is supposed to be in wireguard's format, and thing else.

Show outdated Hide outdated src/network/networkd-netdev-wireguard.c
fd = socket(AF_INET, SOCK_DGRAM, 0);
if (fd < 0) {
return log_netdev_error_errno(netdev, -errno, "Failed to open AF_INET to configure wireguard: %m");
}

This comment has been minimized.

@poettering

poettering Oct 11, 2016

Member

we prefer not to use {} on single-line if blocks, see CODING_STYLE.

Also, no need to negate the errno parameter to log_netdev_error_errno(), it will negate it as necessary on its own.

(And there are spurious double whitespaces after the errno argument)

@poettering

poettering Oct 11, 2016

Member

we prefer not to use {} on single-line if blocks, see CODING_STYLE.

Also, no need to negate the errno parameter to log_netdev_error_errno(), it will negate it as necessary on its own.

(And there are spurious double whitespaces after the errno argument)

@Mic92 Mic92 changed the title from [wip] networkd: add basic support for wireguard vpn to networkd: add basic support for wireguard vpn Nov 4, 2016

Show outdated Hide outdated src/network/networkd-netdev-wireguard.c
w->dev.replace_peer_list = true;
}
int config_parse_wireguard_address_family(const char *unit,

This comment has been minimized.

@zx2c4

zx2c4 Nov 9, 2016

Contributor

This function and configuration option is absolutely incorrect and should be removed.

It is up to getaddrinfo to return the best match for DNS resolution. WireGuard should not handle determining the address family.

Please remove this option.

@zx2c4

zx2c4 Nov 9, 2016

Contributor

This function and configuration option is absolutely incorrect and should be removed.

It is up to getaddrinfo to return the best match for DNS resolution. WireGuard should not handle determining the address family.

Please remove this option.

This comment has been minimized.

@Mic92

Mic92 Nov 9, 2016

Contributor

The default behavior would be as you described it and return the best match. My reasoning was that in some network environments (dual-stack, 6to4 aka he.net, ipv6 only via vpn) both protocol version are available, but only one is desired.
In those environments I found it useful to override default behavior only for the vpn. Protocol specific tunnel may even break wireguard because of lower the MTU.
The workaround would be to setup domains which only contains A or AAAA records or globally modify gai.conf for all applications. I am open for other solutions for this particular problem. Or I remove this option, if you guys think this is not a problem in common networks.

@Mic92

Mic92 Nov 9, 2016

Contributor

The default behavior would be as you described it and return the best match. My reasoning was that in some network environments (dual-stack, 6to4 aka he.net, ipv6 only via vpn) both protocol version are available, but only one is desired.
In those environments I found it useful to override default behavior only for the vpn. Protocol specific tunnel may even break wireguard because of lower the MTU.
The workaround would be to setup domains which only contains A or AAAA records or globally modify gai.conf for all applications. I am open for other solutions for this particular problem. Or I remove this option, if you guys think this is not a problem in common networks.

This comment has been minimized.

@andir

andir Nov 9, 2016

Contributor

Since @Mic92 asked me about my opinion:

While that one can be handy switch I agree with @zx2c4. If you require an IPv4 or IPv6 only DNS record you can always add one (ip4.foo.bar / ip6.foo.bar).

If you want to provide that option (besides DNS record) maybe default the address familiy to "auto" and then use the DNS result?

@andir

andir Nov 9, 2016

Contributor

Since @Mic92 asked me about my opinion:

While that one can be handy switch I agree with @zx2c4. If you require an IPv4 or IPv6 only DNS record you can always add one (ip4.foo.bar / ip6.foo.bar).

If you want to provide that option (besides DNS record) maybe default the address familiy to "auto" and then use the DNS result?

This comment has been minimized.

@Mic92

Mic92 Nov 9, 2016

Contributor

"auto" is the default, it is called "any" in my case - which means AddressFamily is not a required option. Providing a dedicated subdomain is what I end up doing - so let's hope users can always control their hostnames?

@Mic92

Mic92 Nov 9, 2016

Contributor

"auto" is the default, it is called "any" in my case - which means AddressFamily is not a required option. Providing a dedicated subdomain is what I end up doing - so let's hope users can always control their hostnames?

This comment has been minimized.

@zx2c4

zx2c4 Nov 11, 2016

Contributor

@poettering I'll defer to your judgement here. However, as upstream of WireGuard, my opinion is that specifying "AddressFamily" here is the wrong layer at which to do this, and this is an issue for GAI instead.

@zx2c4

zx2c4 Nov 11, 2016

Contributor

@poettering I'll defer to your judgement here. However, as upstream of WireGuard, my opinion is that specifying "AddressFamily" here is the wrong layer at which to do this, and this is an issue for GAI instead.

@Mic92

This comment has been minimized.

Show comment
Hide comment
@Mic92

Mic92 Nov 9, 2016

Contributor

This is the configuration I used for testing. If wireguard gets upstream, I will add a small integration test as well.

# wg0.netdev
[NetDev]
Name=wg0
Kind=wireguard

[Wireguard]
PrivateKey=EEGlnEPYJV//kbvvIqxKkQwOiS+UENyPncC4bF46ong=
PresharedKey=EEGlnEPYJV//kbvvIqxKkQwOiS+UENyPncC4bF46ong=
ListenPort=8633

[WireguardPeer]
AllowedIPs=192.168.0.0/24,192.168.1.0/24,::/0
Endpoint=192.168.1.2:8080
PublicKey=GkyAu7MxoG68fvLVqKYkDgu2MlpCivAulZfFTsTmGyw=
PersistentKeepalive=1

[WireguardPeer]
AllowedIPs=192.168.1.1
Endpoint=google-public-dns-a.google.com:60000
PublicKey=niwbJ/Kb2GPReslzT8LcOcRqgGPVu21Q6YxOWk/biyc=

[WireguardPeer]
AllowedIPs=192.168.1.2
Endpoint=heise.de:443
AddressFamily=ipv4
PublicKey=ZojW2Xhp2F7/BPkDYfS8R08WmgZ7NJ/em1PUGk4oHTU=

[WireguardPeer]
AllowedIPs=192.168.1.3
Endpoint=heise.de:http
AddressFamily=ipv6
PublicKey=RDf+LSpeEre7YEIKaxg+wbpsNV7du+ktR99uBEtIiCA=

[WireguardPeer]
AllowedIPs=192.168.4.0/24
Endpoint=host.invalid:8080
PublicKey=ie0u/yx5yqWlmdJbZ2WWy0953De+znQpfx299IiWqwY=
PersistentKeepalive=1
# wg0.network
[Match]
Name=wg0

[Network]
Address=fe80::1/64
Contributor

Mic92 commented Nov 9, 2016

This is the configuration I used for testing. If wireguard gets upstream, I will add a small integration test as well.

# wg0.netdev
[NetDev]
Name=wg0
Kind=wireguard

[Wireguard]
PrivateKey=EEGlnEPYJV//kbvvIqxKkQwOiS+UENyPncC4bF46ong=
PresharedKey=EEGlnEPYJV//kbvvIqxKkQwOiS+UENyPncC4bF46ong=
ListenPort=8633

[WireguardPeer]
AllowedIPs=192.168.0.0/24,192.168.1.0/24,::/0
Endpoint=192.168.1.2:8080
PublicKey=GkyAu7MxoG68fvLVqKYkDgu2MlpCivAulZfFTsTmGyw=
PersistentKeepalive=1

[WireguardPeer]
AllowedIPs=192.168.1.1
Endpoint=google-public-dns-a.google.com:60000
PublicKey=niwbJ/Kb2GPReslzT8LcOcRqgGPVu21Q6YxOWk/biyc=

[WireguardPeer]
AllowedIPs=192.168.1.2
Endpoint=heise.de:443
AddressFamily=ipv4
PublicKey=ZojW2Xhp2F7/BPkDYfS8R08WmgZ7NJ/em1PUGk4oHTU=

[WireguardPeer]
AllowedIPs=192.168.1.3
Endpoint=heise.de:http
AddressFamily=ipv6
PublicKey=RDf+LSpeEre7YEIKaxg+wbpsNV7du+ktR99uBEtIiCA=

[WireguardPeer]
AllowedIPs=192.168.4.0/24
Endpoint=host.invalid:8080
PublicKey=ie0u/yx5yqWlmdJbZ2WWy0953De+znQpfx299IiWqwY=
PersistentKeepalive=1
# wg0.network
[Match]
Name=wg0

[Network]
Address=fe80::1/64
@poettering

a couple of notes

Show outdated Hide outdated src/network/networkd-netdev-wireguard.c
static void resolve_endpoints(NetDev *netdev);
static WireguardPeer *new_peer(Wireguard *w, unsigned section) {

This comment has been minimized.

@poettering

poettering Nov 11, 2016

Member

hmm, to follow our usualy coding style, please name this "wireguard_peer_new()"...

@poettering

poettering Nov 11, 2016

Member

hmm, to follow our usualy coding style, please name this "wireguard_peer_new()"...

Show outdated Hide outdated src/network/networkd-netdev-wireguard.c
}
static int update_wireguard_config(NetDev *netdev) {
_cleanup_close_ int fd = -2;

This comment has been minimized.

@poettering

poettering Nov 11, 2016

Member

why -2? we usually initialize fds to -1...

@poettering

poettering Nov 11, 2016

Member

why -2? we usually initialize fds to -1...

Show outdated Hide outdated src/network/networkd-netdev-wireguard.c
_cleanup_close_ int fd = -2;
_cleanup_free_ void *data = NULL;
void *pos = NULL;
struct ifreq ifreq = {0};

This comment has been minimized.

@poettering

poettering Nov 11, 2016

Member

please write this as simply = {}...

@poettering

poettering Nov 11, 2016

Member

please write this as simply = {}...

Show outdated Hide outdated src/network/networkd-netdev-wireguard.c
assert(w);
fd = socket(AF_INET, SOCK_DGRAM, 0);

This comment has been minimized.

@poettering

poettering Nov 11, 2016

Member

please always open fds with cloexec right away. i.e. add SOCK_CLOEXEC here to the second apram...

@poettering

poettering Nov 11, 2016

Member

please always open fds with cloexec right away. i.e. add SOCK_CLOEXEC here to the second apram...

Show outdated Hide outdated src/network/networkd-netdev-wireguard.c
fd = socket(AF_INET, SOCK_DGRAM, 0);
if (fd < 0)
return log_netdev_error_errno(netdev, -errno, "Failed to open AF_INET to configure wireguard: %m");

This comment has been minimized.

@poettering

poettering Nov 11, 2016

Member

no need to negate "errno" here, the logging functions do that on their own.

@poettering

poettering Nov 11, 2016

Member

no need to negate "errno" here, the logging functions do that on their own.

Show outdated Hide outdated src/network/networkd-netdev-wireguard.c
endpoint = new0(WireguardEndpoint, 1);
if (!endpoint)
return -ENOMEM;

This comment has been minimized.

@poettering

poettering Nov 11, 2016

Member

dito

@poettering
Show outdated Hide outdated src/network/networkd-netdev-wireguard.c
host = strndup(begin, len);
if (!host)
return -ENOMEM;

This comment has been minimized.

@poettering

poettering Nov 11, 2016

Member

log_oom

@poettering

poettering Nov 11, 2016

Member

log_oom

Show outdated Hide outdated src/network/networkd-netdev-wireguard.c
port = strdup(end);
if (!port)
return -ENOMEM;

This comment has been minimized.

@poettering

poettering Nov 11, 2016

Member

dito

@poettering
Show outdated Hide outdated src/network/networkd-netdev-wireguard.c
peer = new_peer(w, section_line);
if (!peer)
return -ENOMEM;

This comment has been minimized.

@poettering

poettering Nov 11, 2016

Member

log_oom()

@poettering

poettering Nov 11, 2016

Member

log_oom()

Show outdated Hide outdated src/network/networkd-netdev-wireguard.c
LIST_FOREACH(peers, peer, w->peers) {
LIST_FOREACH(ipmasks, mask, peer->ipmasks) {
free(mask);
}

This comment has been minimized.

@poettering

poettering Nov 11, 2016

Member

no {} here...

@poettering

poettering Nov 11, 2016

Member

no {} here...

@Mic92

This comment has been minimized.

Show comment
Hide comment
@Mic92

Mic92 Nov 15, 2016

Contributor

I see three problems remaining, when resolving endpoints:

  1. When the endpoint is resolved it could fail due temporary network problems.
    As this is a long running daemon there is compared to wg(8) no easy way to recover from those errors as a user. I thought about adding exponential backoff, if network failures are detected while resolving.
  2. People might want to set wireguard as a their internet gateway - for end-users this probably the most common use case for a vpn. Endpoints are currently resolved asynchronous. Defaults routes to wireguard are likely applied before resolving is done. Resolved would gets this one right as it will use the interface where the dns server was set. However the adoption of it in upstream distributions is not high yet.
  3. When a dns cache is used such as dnsmasq or pdns (NetworkManager has a plugin for first one),
    endpoints can be resolved even if no default gateway is present. Usually a resolver will return addresses based on what routes are available - therefor it should pick the correct address family. This will not work if network is down when resolving which could happen if systemd-networkd is restarted.
    Resolved would also get this right - it flushes the cache, but again the preferred option is still the glibc resolver in most setups.
Contributor

Mic92 commented Nov 15, 2016

I see three problems remaining, when resolving endpoints:

  1. When the endpoint is resolved it could fail due temporary network problems.
    As this is a long running daemon there is compared to wg(8) no easy way to recover from those errors as a user. I thought about adding exponential backoff, if network failures are detected while resolving.
  2. People might want to set wireguard as a their internet gateway - for end-users this probably the most common use case for a vpn. Endpoints are currently resolved asynchronous. Defaults routes to wireguard are likely applied before resolving is done. Resolved would gets this one right as it will use the interface where the dns server was set. However the adoption of it in upstream distributions is not high yet.
  3. When a dns cache is used such as dnsmasq or pdns (NetworkManager has a plugin for first one),
    endpoints can be resolved even if no default gateway is present. Usually a resolver will return addresses based on what routes are available - therefor it should pick the correct address family. This will not work if network is down when resolving which could happen if systemd-networkd is restarted.
    Resolved would also get this right - it flushes the cache, but again the preferred option is still the glibc resolver in most setups.
@eliasp

This comment has been minimized.

Show comment
Hide comment
@eliasp

eliasp Nov 15, 2016

Contributor

Resolved would gets this one right as it will the use the interface where the dns server was set. However the adoption of it in upstream distributions is not high yet.

I wouldn't care about this too much! A distribution which is modern enough to adopt wireguard/systemd-wireguard will also be modern enough to have systemd-resolved in place.

Users running one of the distributions not using systemd-resolved yet, but still wanting to use systemd-wireguard will be competent enough to take care of a potentially missing systemd-resolved on their own.

Contributor

eliasp commented Nov 15, 2016

Resolved would gets this one right as it will the use the interface where the dns server was set. However the adoption of it in upstream distributions is not high yet.

I wouldn't care about this too much! A distribution which is modern enough to adopt wireguard/systemd-wireguard will also be modern enough to have systemd-resolved in place.

Users running one of the distributions not using systemd-resolved yet, but still wanting to use systemd-wireguard will be competent enough to take care of a potentially missing systemd-resolved on their own.

@poettering

some more nitpicks.

What's the current state of the kernel patch going upstream?

Show outdated Hide outdated src/network/networkd-util.c
r = safe_atou16(s, port);
if (r < 0 || *port == 0)
return -EINVAL;

This comment has been minimized.

@poettering

poettering Nov 15, 2016

Member

as a general rule: please don't clobber return values on failure, also see CODING_STYLE. Specifically, this means: invoke safe_atou16() with a temporary variable as second argument, and write it to *port only on success.

@poettering

poettering Nov 15, 2016

Member

as a general rule: please don't clobber return values on failure, also see CODING_STYLE. Specifically, this means: invoke safe_atou16() with a temporary variable as second argument, and write it to *port only on success.

Show outdated Hide outdated src/network/networkd-util.c
return 0;
}
int cidr_from_string(const char *s, int *family, union in_addr_union *addr, unsigned char *prefixlen) {

This comment has been minimized.

@poettering

poettering Nov 15, 2016

Member

i'd call this parse_address_and_prefixlen() I must say.

The kernel exposes this as "address" and "prefixlen" fields, and doesn't use the term "cidr", and I think we should follow suit here...

@poettering

poettering Nov 15, 2016

Member

i'd call this parse_address_and_prefixlen() I must say.

The kernel exposes this as "address" and "prefixlen" fields, and doesn't use the term "cidr", and I think we should follow suit here...

Show outdated Hide outdated src/network/networkd-util.c
r = in_addr_from_string_auto(address, family, addr);
if (r < 0) {
return r;
}

This comment has been minimized.

@poettering

poettering Nov 15, 2016

Member

no {} around single-line if blocks please

@poettering

poettering Nov 15, 2016

Member

no {} around single-line if blocks please

Show outdated Hide outdated src/network/networkd-util.c
if (r < 0 ||
(*family == AF_INET && *prefixlen > 32) ||
(*family == AF_INET6 && *prefixlen > 128))
return -EINVAL;

This comment has been minimized.

@poettering

poettering Nov 15, 2016

Member

please do not clobber return values on error (see above)

@poettering

poettering Nov 15, 2016

Member

please do not clobber return values on error (see above)

Show outdated Hide outdated src/network/netdev/wireguard.h
} WireguardEndpoint;
struct Wireguard {

This comment has been minimized.

@poettering

poettering Nov 15, 2016

Member

spurious double newline

@poettering

poettering Nov 15, 2016

Member

spurious double newline

Show outdated Hide outdated src/network/netdev/wireguard.c
}
peer->fields.persistent_keepalive_interval = keepalive;

This comment has been minimized.

@poettering

poettering Nov 15, 2016

Member

spurious double newline

@poettering

poettering Nov 15, 2016

Member

spurious double newline

Show outdated Hide outdated src/network/netdev/wireguard.c
r = cidr_from_string(word, &family, &addr, &prefixlen);
if (r < 0) {

This comment has been minimized.

@poettering

poettering Nov 15, 2016

Member

please keep function calls and their error handling together, i.e. drop the newline above

@poettering

poettering Nov 15, 2016

Member

please keep function calls and their error handling together, i.e. drop the newline above

Show outdated Hide outdated src/network/netdev/wireguard.c
}
int config_parse_wireguard_private_key(const char *unit,

This comment has been minimized.

@poettering

poettering Nov 15, 2016

Member

spurious double newline

@poettering

poettering Nov 15, 2016

Member

spurious double newline

Show outdated Hide outdated src/network/netdev/wireguard.c
int r, i = 0;
Wireguard *w;
WireguardEndpoint *endpoint;
struct addrinfo hints = {

This comment has been minimized.

@poettering

poettering Nov 15, 2016

Member

this can be made static const, no?

@poettering

poettering Nov 15, 2016

Member

this can be made static const, no?

@Mic92

This comment has been minimized.

Show comment
Hide comment
@Mic92

Mic92 Nov 15, 2016

Contributor

Addressed the issues.

Contributor

Mic92 commented Nov 15, 2016

Addressed the issues.

@Mic92

This comment has been minimized.

Show comment
Hide comment
@Mic92

Mic92 Nov 22, 2016

Contributor

the code handle resolving errors now.

Contributor

Mic92 commented Nov 22, 2016

the code handle resolving errors now.

@Mic92 Mic92 changed the title from networkd: add basic support for wireguard vpn to networkd: add support for wireguard vpn Nov 30, 2016

@Mic92

This comment has been minimized.

Show comment
Hide comment
@Mic92

Mic92 Mar 31, 2017

Contributor

Also handles WG_API_VERSION_MAGIC now.

Contributor

Mic92 commented Mar 31, 2017

Also handles WG_API_VERSION_MAGIC now.

Show outdated Hide outdated src/network/netdev/wireguard.c
// TODO:
// this should be the default, but is currently not possible as
// WGPEER_REMOVE_PRESHARED_KEY is not allowed for new peers
//peer->fields.flags = WGPEER_REPLACE_IPMASKS | WGPEER_REMOVE_PRESHARED_KEY;

This comment has been minimized.

@Mic92

Mic92 May 19, 2017

Contributor

The pull request is now updated for wireguard 0.0.20170517. Everything will works except removing an existing pre-shared key from a peer. @zx2c4 could you allow WGPEER_REMOVE_PRESHARED_KEY for non-existing peers? That would allow stateless atomic reconfiguration in networkd, as I would have not to check for the existence of a particular peer. I would like just remove pre-shared keys, if there are no longer present in the current configuration.

@Mic92

Mic92 May 19, 2017

Contributor

The pull request is now updated for wireguard 0.0.20170517. Everything will works except removing an existing pre-shared key from a peer. @zx2c4 could you allow WGPEER_REMOVE_PRESHARED_KEY for non-existing peers? That would allow stateless atomic reconfiguration in networkd, as I would have not to check for the existence of a particular peer. I would like just remove pre-shared keys, if there are no longer present in the current configuration.

This comment has been minimized.

@zx2c4

zx2c4 May 19, 2017

Contributor

That's almost fair point, but not quite; see below. The semantics of this will change anyway with the move to Netlink, which upstream requires.

But one small question -- how do you handle removing peers? Looking at the source, you always set WGDEVICE_REPLACE_PEERS, so that the peers you do provide will replace the existing ones. That makes sense to achieve your objective. However, in that case, you don't actually need to use WGPEER_REMOVE_PRESHARED_KEY or any of the other REPLACE/REMOVE flags.

@zx2c4

zx2c4 May 19, 2017

Contributor

That's almost fair point, but not quite; see below. The semantics of this will change anyway with the move to Netlink, which upstream requires.

But one small question -- how do you handle removing peers? Looking at the source, you always set WGDEVICE_REPLACE_PEERS, so that the peers you do provide will replace the existing ones. That makes sense to achieve your objective. However, in that case, you don't actually need to use WGPEER_REMOVE_PRESHARED_KEY or any of the other REPLACE/REMOVE flags.

Show outdated Hide outdated src/network/netdev/wireguard.c
w = WIREGUARD(data);
assert(w);
if (!streq(rvalue, "auto")) {

This comment has been minimized.

@zx2c4

zx2c4 May 19, 2017

Contributor

Please do not add special token meanings that do not match or exist in wg(8).

@zx2c4

zx2c4 May 19, 2017

Contributor

Please do not add special token meanings that do not match or exist in wg(8).

This comment has been minimized.

@Mic92

Mic92 May 19, 2017

Contributor

that was an request by @poettering and you agreed at that point in time: #4191 (comment)

@Mic92

Mic92 May 19, 2017

Contributor

that was an request by @poettering and you agreed at that point in time: #4191 (comment)

This comment has been minimized.

@zx2c4

zx2c4 May 19, 2017

Contributor

Ahhh, alright then.

@zx2c4

zx2c4 May 19, 2017

Contributor

Ahhh, alright then.

if (!endpoint)
return log_oom();
if (rvalue[0] == '[') {

This comment has been minimized.

@zx2c4

zx2c4 May 19, 2017

Contributor

I'm pretty sure this parsing algorithm was copy and pasted from a prior git revision of WireGuard. As such, you'll need to retain my copyright line in this file.

	if (mutable[0] == '[') {
		begin = &mutable[1];
		end = strchr(mutable, ']');
		if (!end) {
			free(mutable);
			fprintf(stderr, "Unable to find matching brace of endpoint: `%s`\n", value);
			return false;
		}
		*end = '\0';
		++end;
		if (*end != ':' || !*(end + 1)) {
			free(mutable);
			fprintf(stderr, "Unable to find port of endpoint: `%s`\n", value);
			return false;
		}
		++end;
	} else {
		begin = mutable;
		end = strrchr(mutable, ':');
		if (!end || !*(end + 1)) {
			free(mutable);
			fprintf(stderr, "Unable to find port of endpoint: `%s`\n", value);
			return false;
		}
		*end = '\0';
		++end;
	}
@zx2c4

zx2c4 May 19, 2017

Contributor

I'm pretty sure this parsing algorithm was copy and pasted from a prior git revision of WireGuard. As such, you'll need to retain my copyright line in this file.

	if (mutable[0] == '[') {
		begin = &mutable[1];
		end = strchr(mutable, ']');
		if (!end) {
			free(mutable);
			fprintf(stderr, "Unable to find matching brace of endpoint: `%s`\n", value);
			return false;
		}
		*end = '\0';
		++end;
		if (*end != ':' || !*(end + 1)) {
			free(mutable);
			fprintf(stderr, "Unable to find port of endpoint: `%s`\n", value);
			return false;
		}
		++end;
	} else {
		begin = mutable;
		end = strrchr(mutable, ':');
		if (!end || !*(end + 1)) {
			free(mutable);
			fprintf(stderr, "Unable to find port of endpoint: `%s`\n", value);
			return false;
		}
		*end = '\0';
		++end;
	}

This comment has been minimized.

@keszybz

keszybz Sep 13, 2017

Member

Yes, please err on the side of too many copyright headers, rather than too few. Even if the code is not similar anymore, but was based on some other code, it's better to mention the original authorship (and add your own (C) line).

@keszybz

keszybz Sep 13, 2017

Member

Yes, please err on the side of too many copyright headers, rather than too few. Even if the code is not similar anymore, but was based on some other code, it's better to mention the original authorship (and add your own (C) line).

This comment has been minimized.

@Mic92

Mic92 Sep 13, 2017

Contributor

The authorship was updated in the meantime.

@Mic92

Mic92 Sep 13, 2017

Contributor

The authorship was updated in the meantime.

This comment has been minimized.

@zx2c4

zx2c4 Dec 11, 2017

Contributor

As I wrote set_wireguard_interface for this commit, my copyright has been added to the top anyway with that, so this comment here is now resolved.

@zx2c4

zx2c4 Dec 11, 2017

Contributor

As I wrote set_wireguard_interface for this commit, my copyright has been added to the top anyway with that, so this comment here is now resolved.

@zx2c4

This comment has been minimized.

Show comment
Hide comment
@zx2c4

zx2c4 Dec 12, 2017

Contributor

What's the current state of wireguard and being merged into the kernel, btw?

Work in progress. Dave Miller told me at netdevconf to make it happen sooner rather than later. Greg seems amped about it too.

Contributor

zx2c4 commented Dec 12, 2017

What's the current state of wireguard and being merged into the kernel, btw?

Work in progress. Dave Miller told me at netdevconf to make it happen sooner rather than later. Greg seems amped about it too.

@Mic92

This comment has been minimized.

Show comment
Hide comment
@Mic92

Mic92 Dec 14, 2017

Contributor

I have addressed all comments except the ones, where there were some concerns and a further response is appreciated.

Contributor

Mic92 commented Dec 14, 2017

I have addressed all comments except the ones, where there were some concerns and a further response is appreciated.

@zx2c4

This comment has been minimized.

Show comment
Hide comment
@zx2c4

zx2c4 Dec 14, 2017

Contributor

Thanks for the changes, @Mic92. I'm looking forward to hearing @poettering's feedback on the discussion, as well as have @keszybz's eye on the implementation. I think the one thing mentioned here also was squashing and splitting this into two commits -- one for genl and one for wireguard -- but maybe you've opted to wait until the review is complete to muck around with rebase?

Contributor

zx2c4 commented Dec 14, 2017

Thanks for the changes, @Mic92. I'm looking forward to hearing @poettering's feedback on the discussion, as well as have @keszybz's eye on the implementation. I think the one thing mentioned here also was squashing and splitting this into two commits -- one for genl and one for wireguard -- but maybe you've opted to wait until the review is complete to muck around with rebase?

Show outdated Hide outdated src/libsystemd/sd-netlink/netlink-message.c
@@ -186,6 +190,14 @@ static int add_rtattr(sd_netlink_message *m, unsigned short type, const void *da
/* get the new message size (with padding at the end) */
message_length = offset + RTA_ALIGN(rta_length);
/* Buffer should fit one page as this choice is good for headerless malloc.
* Limited to 8K so that userspace to avoid message truncation when
* page size is very large. */

This comment has been minimized.

@zx2c4

zx2c4 Dec 14, 2017

Contributor

typo: "should fit in one page" "so that userspace can avoid"

The real reason for this, though, is that this is the kernel's default receive buffer size for netlink messages, making it naturally then the good fit for userspace too.

@zx2c4

zx2c4 Dec 14, 2017

Contributor

typo: "should fit in one page" "so that userspace can avoid"

The real reason for this, though, is that this is the kernel's default receive buffer size for netlink messages, making it naturally then the good fit for userspace too.

@Mic92

This comment has been minimized.

Show comment
Hide comment
@Mic92

Mic92 Dec 14, 2017

Contributor

@zx2c4 yes, I will squash commits later. For incremental review smaller commits help reviewer to see how I addressed feedback.

Contributor

Mic92 commented Dec 14, 2017

@zx2c4 yes, I will squash commits later. For incremental review smaller commits help reviewer to see how I addressed feedback.

@keszybz

This comment has been minimized.

Show comment
Hide comment
@keszybz

keszybz Dec 18, 2017

Member

Semaphore Platform's upstream provider has recovered from partial network issues caused by BGP hijacking in an outer Telia network., huh.

Member

keszybz commented Dec 18, 2017

Semaphore Platform's upstream provider has recovered from partial network issues caused by BGP hijacking in an outer Telia network., huh.

@zx2c4

This comment has been minimized.

Show comment
Hide comment
@zx2c4

zx2c4 Dec 18, 2017

Contributor

@keszybz - Are you responding to something you saw in the code that confused you? (This string somehow?) Or did a cat jump on your keyboard while you were reviewing this? Or has your account been hijacked?

Contributor

zx2c4 commented Dec 18, 2017

@keszybz - Are you responding to something you saw in the code that confused you? (This string somehow?) Or did a cat jump on your keyboard while you were reviewing this? Or has your account been hijacked?

@keszybz

This comment has been minimized.

Show comment
Hide comment
@keszybz

keszybz Dec 18, 2017

Member

Please rebase/squash the commits into the final form. The way that review works best (for me, but also for others from what I can tell), is a new version of a PR with any fixes squashed in is pushed, and then a short list of things which changed is added in a comment on github.

Builds fail:

/usr/include/netinet/in.h:101:5: error: expected identifier before numeric constant
     IPPROTO_HOPOPTS = 0,   /* IPv6 Hop-by-Hop options.  */
     ^
In file included from ../src/systemd/sd-netlink.h:26:0,
                 from ../src/libsystemd/sd-netlink/netlink-types.c:51:
/usr/include/netinet/in.h:211:8: error: redefinition of ‘struct in6_addr’
 struct in6_addr
        ^
In file included from ../src/libsystemd/sd-netlink/netlink-types.c:27:0:
/usr/include/linux/in6.h:32:8: note: originally defined here
 struct in6_addr {
        ^
In file included from ../src/systemd/sd-netlink.h:26:0,
                 from ../src/libsystemd/sd-netlink/netlink-types.c:51:
/usr/include/netinet/in.h:254:8: error: redefinition of ‘struct sockaddr_in6’
 struct sockaddr_in6
        ^
In file included from ../src/libsystemd/sd-netlink/netlink-types.c:22:0:
/usr/include/x86_64-linux-gnu/sys/socket.h:90:17: note: originally defined here
 typedef union { __SOCKADDR_ALLTYPES
                 ^
In file included from ../src/systemd/sd-netlink.h:26:0,
                 from ../src/libsystemd/sd-netlink/netlink-types.c:51:
/usr/include/netinet/in.h:290:8: error: redefinition of ‘struct ipv6_mreq’
 struct ipv6_mreq
        ^
In file included from ../src/libsystemd/sd-netlink/netlink-types.c:27:0:
/usr/include/linux/in6.h:59:8: note: originally defined here
 struct ipv6_mreq {
        ^

It'd would be good to fix this. It seems like this is some fuckup in the kernel headers, but we cannot realistically break CI, so this needs to be fixed.

Member

keszybz commented Dec 18, 2017

Please rebase/squash the commits into the final form. The way that review works best (for me, but also for others from what I can tell), is a new version of a PR with any fixes squashed in is pushed, and then a short list of things which changed is added in a comment on github.

Builds fail:

/usr/include/netinet/in.h:101:5: error: expected identifier before numeric constant
     IPPROTO_HOPOPTS = 0,   /* IPv6 Hop-by-Hop options.  */
     ^
In file included from ../src/systemd/sd-netlink.h:26:0,
                 from ../src/libsystemd/sd-netlink/netlink-types.c:51:
/usr/include/netinet/in.h:211:8: error: redefinition of ‘struct in6_addr’
 struct in6_addr
        ^
In file included from ../src/libsystemd/sd-netlink/netlink-types.c:27:0:
/usr/include/linux/in6.h:32:8: note: originally defined here
 struct in6_addr {
        ^
In file included from ../src/systemd/sd-netlink.h:26:0,
                 from ../src/libsystemd/sd-netlink/netlink-types.c:51:
/usr/include/netinet/in.h:254:8: error: redefinition of ‘struct sockaddr_in6’
 struct sockaddr_in6
        ^
In file included from ../src/libsystemd/sd-netlink/netlink-types.c:22:0:
/usr/include/x86_64-linux-gnu/sys/socket.h:90:17: note: originally defined here
 typedef union { __SOCKADDR_ALLTYPES
                 ^
In file included from ../src/systemd/sd-netlink.h:26:0,
                 from ../src/libsystemd/sd-netlink/netlink-types.c:51:
/usr/include/netinet/in.h:290:8: error: redefinition of ‘struct ipv6_mreq’
 struct ipv6_mreq
        ^
In file included from ../src/libsystemd/sd-netlink/netlink-types.c:27:0:
/usr/include/linux/in6.h:59:8: note: originally defined here
 struct ipv6_mreq {
        ^

It'd would be good to fix this. It seems like this is some fuckup in the kernel headers, but we cannot realistically break CI, so this needs to be fixed.

@keszybz

This comment has been minimized.

Show comment
Hide comment
@keszybz

keszybz Dec 18, 2017

Member

Are you responding to something you saw in the code that confused you?

No.

(This string somehow?)

Not really.

Or did a cat jump on your keyboard while you were reviewing this?

Not this time.

Or has your account been hijacked?

Are you saying that there are Samaritan hackers who break into one's machine to do reviews? I'd love that ;)

The sad truth is that this is from semaphore CI: the reason why the build on semaphore failed is network issues as a result of some attack. The Ubuntu CI failed with the other error pasted later.

Member

keszybz commented Dec 18, 2017

Are you responding to something you saw in the code that confused you?

No.

(This string somehow?)

Not really.

Or did a cat jump on your keyboard while you were reviewing this?

Not this time.

Or has your account been hijacked?

Are you saying that there are Samaritan hackers who break into one's machine to do reviews? I'd love that ;)

The sad truth is that this is from semaphore CI: the reason why the build on semaphore failed is network issues as a result of some attack. The Ubuntu CI failed with the other error pasted later.

@keszybz

This comment has been minimized.

Show comment
Hide comment
@keszybz

keszybz Dec 18, 2017

Member

Oh, I see you pushed again. Just to summarize the CI status from last round:
Fedora rawhide: test-netlink fails, but no useful output.
artful i386: the same.
artful s390x: the same
semaphore: network problems
xenial amd64: #4191 (comment)

I rebuilt that version on zesty, and it builds OK. I'll check xenial too.

Member

keszybz commented Dec 18, 2017

Oh, I see you pushed again. Just to summarize the CI status from last round:
Fedora rawhide: test-netlink fails, but no useful output.
artful i386: the same.
artful s390x: the same
semaphore: network problems
xenial amd64: #4191 (comment)

I rebuilt that version on zesty, and it builds OK. I'll check xenial too.

@zx2c4

This comment has been minimized.

Show comment
Hide comment
@zx2c4

zx2c4 Dec 18, 2017

Contributor

@keszybz I just squashed this down into two commits for you, as requested.

I imagine the header situation is due to something strange about including <linux/genetlink.h>... I'll investigate too.

Contributor

zx2c4 commented Dec 18, 2017

@keszybz I just squashed this down into two commits for you, as requested.

I imagine the header situation is due to something strange about including <linux/genetlink.h>... I'll investigate too.

@keszybz

This comment has been minimized.

Show comment
Hide comment
@keszybz

keszybz Dec 18, 2017

Member

Hmm, this builds just fine for me on xenial, after I remove the version check for libmount version. In the Ubuntu CI, we install some backported packages. Apparently this causes some conflict. Let's leave this for now.

Member

keszybz commented Dec 18, 2017

Hmm, this builds just fine for me on xenial, after I remove the version check for libmount version. In the Ubuntu CI, we install some backported packages. Apparently this causes some conflict. Let's leave this for now.

@keszybz

This comment has been minimized.

Show comment
Hide comment
@keszybz

keszybz Dec 18, 2017

Member

test-netlink failure:

$ valgrind build-xenial/test-netlink
==19728== Memcheck, a memory error detector
==19728== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==19728== Using Valgrind-3.12.0 and LibVEX; rerun with -h for copyright info
==19728== Command: build-xenial/test-netlink
==19728== 
==19728== Invalid read of size 4
==19728==    at 0x4FCDF45: message_new (netlink-message.c:73)
==19728==    by 0x4FD3AC5: rtnl_message_new_synthetic_error (netlink-util.c:107)
==19728==    by 0x10C38E: test_message (test-netlink.c:375)
==19728==    by 0x10C43A: main (test-netlink.c:387)
==19728==  Address 0x18 is not stack'd, malloc'd or (recently) free'd
==19728== 
==19728== 
==19728== Process terminating with default action of signal 11 (SIGSEGV): dumping core
==19728==  Access not within mapped region at address 0x18
==19728==    at 0x4FCDF45: message_new (netlink-message.c:73)
==19728==    by 0x4FD3AC5: rtnl_message_new_synthetic_error (netlink-util.c:107)
==19728==    by 0x10C38E: test_message (test-netlink.c:375)
==19728==    by 0x10C43A: main (test-netlink.c:387)
Member

keszybz commented Dec 18, 2017

test-netlink failure:

$ valgrind build-xenial/test-netlink
==19728== Memcheck, a memory error detector
==19728== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==19728== Using Valgrind-3.12.0 and LibVEX; rerun with -h for copyright info
==19728== Command: build-xenial/test-netlink
==19728== 
==19728== Invalid read of size 4
==19728==    at 0x4FCDF45: message_new (netlink-message.c:73)
==19728==    by 0x4FD3AC5: rtnl_message_new_synthetic_error (netlink-util.c:107)
==19728==    by 0x10C38E: test_message (test-netlink.c:375)
==19728==    by 0x10C43A: main (test-netlink.c:387)
==19728==  Address 0x18 is not stack'd, malloc'd or (recently) free'd
==19728== 
==19728== 
==19728== Process terminating with default action of signal 11 (SIGSEGV): dumping core
==19728==  Access not within mapped region at address 0x18
==19728==    at 0x4FCDF45: message_new (netlink-message.c:73)
==19728==    by 0x4FD3AC5: rtnl_message_new_synthetic_error (netlink-util.c:107)
==19728==    by 0x10C38E: test_message (test-netlink.c:375)
==19728==    by 0x10C43A: main (test-netlink.c:387)
@zx2c4

This comment has been minimized.

Show comment
Hide comment
@zx2c4

zx2c4 Dec 18, 2017

Contributor

@keszybz - Thanks for pointing that out. Stepping in for @Mic92, I fixed that up.

Contributor

zx2c4 commented Dec 18, 2017

@keszybz - Thanks for pointing that out. Stepping in for @Mic92, I fixed that up.

@Mic92

This comment has been minimized.

Show comment
Hide comment
@Mic92

Mic92 Dec 18, 2017

Contributor

The last fix was incomplete. Now it works on my machine:

$ sudo ./test-netlink
got link info about lo
1 left in pipe. got reply: Success
0 left in pipe. got reply: Success
got link info about lo2
got IPv6 address on ifindex 12
got IPv6 address on ifindex 11
got IPv6 address on ifindex 10
got IPv6 address on ifindex 6
got IPv6 address on ifindex 6
got IPv6 address on ifindex 6
got IPv6 address on ifindex 5
got IPv6 address on ifindex 5
got IPv6 address on ifindex 4
got IPv6 address on ifindex 1
got IPv4 address on ifindex 12
got IPv4 address on ifindex 11
got IPv4 address on ifindex 10
got IPv4 address on ifindex 7
got IPv4 address on ifindex 6
got IPv4 address on ifindex 5
got IPv4 address on ifindex 4
got IPv4 address on ifindex 4
got IPv4 address on ifindex 1
$ sudo valgrind ./test-netlink
==19442== Memcheck, a memory error detector
==19442== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==19442== Using Valgrind-3.13.0 and LibVEX; rerun with -h for copyright info
==19442== Command: ./test-netlink
==19442==
==19442==ASan runtime does not come first in initial library list; you should either link runtime to your application or manually preload it with LD_PRELOAD.
==19442==
==19442== HEAP SUMMARY:
==19442==     in use at exit: 0 bytes in 0 blocks
==19442==   total heap usage: 0 allocs, 0 frees, 0 bytes allocated
==19442==
==19442== All heap blocks were freed -- no leaks are possible
==19442==
==19442== For counts of detected and suppressed errors, rerun with: -v
==19442== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
Contributor

Mic92 commented Dec 18, 2017

The last fix was incomplete. Now it works on my machine:

$ sudo ./test-netlink
got link info about lo
1 left in pipe. got reply: Success
0 left in pipe. got reply: Success
got link info about lo2
got IPv6 address on ifindex 12
got IPv6 address on ifindex 11
got IPv6 address on ifindex 10
got IPv6 address on ifindex 6
got IPv6 address on ifindex 6
got IPv6 address on ifindex 6
got IPv6 address on ifindex 5
got IPv6 address on ifindex 5
got IPv6 address on ifindex 4
got IPv6 address on ifindex 1
got IPv4 address on ifindex 12
got IPv4 address on ifindex 11
got IPv4 address on ifindex 10
got IPv4 address on ifindex 7
got IPv4 address on ifindex 6
got IPv4 address on ifindex 5
got IPv4 address on ifindex 4
got IPv4 address on ifindex 4
got IPv4 address on ifindex 1
$ sudo valgrind ./test-netlink
==19442== Memcheck, a memory error detector
==19442== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==19442== Using Valgrind-3.13.0 and LibVEX; rerun with -h for copyright info
==19442== Command: ./test-netlink
==19442==
==19442==ASan runtime does not come first in initial library list; you should either link runtime to your application or manually preload it with LD_PRELOAD.
==19442==
==19442== HEAP SUMMARY:
==19442==     in use at exit: 0 bytes in 0 blocks
==19442==   total heap usage: 0 allocs, 0 frees, 0 bytes allocated
==19442==
==19442== All heap blocks were freed -- no leaks are possible
==19442==
==19442== For counts of detected and suppressed errors, rerun with: -v
==19442== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
@keszybz

Looks nice in general.

The first big question is whether the option names should follow systemd style ("FirewallMark") or wg ("FWMark"). I would prefer systemd convention, but @zx2c4 is adamant about the wg convention, so let's go with that. This has the advantage that it'll simplify adoption.

Another issue is the order of arguments. There was variation wrt to this in systemd in the past. But some time back we started putting the "output" argument either first, in case of "constructor" functions, or last, when something is created based on some arguments. This is pretty nice, because the reader immediately knows where the output goes. I made some comments where I think the order of args should be changed. If you disagree, I'm fine with leaving that for later. Nothing is set in stone until the next release, and a separate PR would be easier to discuss.

There's some indentation issues, typos, etc. Please fix those. Also make sure that the code compiles and passes tests after each commit.

I haven't actually tested any of this yet, but I assume that it works. I think the best approach would be to merge after fixing the obvious stuff, and do any polishing in later PRs.

Show outdated Hide outdated src/libsystemd/sd-netlink/generic-netlink.c
}
static int lookup_id(sd_netlink *nl, sd_genl_family family, uint16_t *id);
static int genl_message_new(sd_netlink *nl, sd_netlink_message **ret, sd_genl_family family, uint16_t nlmsg_type, uint8_t cmd) {

This comment has been minimized.

@keszybz

keszybz Dec 18, 2017

Member

Nowadays we make functions take the return parameters as the final argument. So this should be

static int genl_message_new(sd_netlink *nl, sd_genl_family family, uint16_t nlmsg_type, uint8_t cmd, sd_netlink_message **ret) {
@keszybz

keszybz Dec 18, 2017

Member

Nowadays we make functions take the return parameters as the final argument. So this should be

static int genl_message_new(sd_netlink *nl, sd_genl_family family, uint16_t nlmsg_type, uint8_t cmd, sd_netlink_message **ret) {
Show outdated Hide outdated src/libsystemd/sd-netlink/generic-netlink.c
return 0;
}
int sd_genl_message_new(sd_netlink *nl, sd_netlink_message **ret, sd_genl_family family, uint8_t cmd) {

This comment has been minimized.

@keszybz

keszybz Dec 18, 2017

Member

Here too, **ret should be last.

@keszybz

keszybz Dec 18, 2017

Member

Here too, **ret should be last.

Show outdated Hide outdated src/libsystemd/sd-netlink/generic-netlink.c
if (r < 0)
return r;
r = sd_netlink_message_read_u16(reply, CTRL_ATTR_FAMILY_ID, &_id);

This comment has been minimized.

@keszybz

keszybz Dec 18, 2017

Member

Maybe fold lines 96-102 into

return sd_netlink_message_read_u16(reply, CTRL_ATTR_FAMILY_ID, id);

?

@keszybz

keszybz Dec 18, 2017

Member

Maybe fold lines 96-102 into

return sd_netlink_message_read_u16(reply, CTRL_ATTR_FAMILY_ID, id);

?

Show outdated Hide outdated man/systemd.netdev.xml
<title>[WireGuard] Section Options</title>
<para>The <literal>[WireGuard]</literal> section accepts the following
key:</para>

This comment has been minimized.

@keszybz

keszybz Dec 18, 2017

Member

keys

@keszybz
Show outdated Hide outdated man/systemd.netdev.xml
<title>[WireGuardPeer] Section Options</title>
<para>The <literal>[WireGuardPeer]</literal> section accepts the following
key:</para>

This comment has been minimized.

@keszybz

keszybz Dec 18, 2017

Member

keys

@keszybz
static const NLType genl_families[] = {
[SD_GENL_ID_CTRL] = { .type = NETLINK_TYPE_NESTED, .type_system = &genl_ctrl_id_ctrl_type_system },
[SD_GENL_WIREGUARD] = { .type = NETLINK_TYPE_NESTED, .type_system = &genl_wireguard_type_system },

This comment has been minimized.

@keszybz

keszybz Dec 18, 2017

Member

This is undefined at this point. Please either move this chunk to the second commit or add the necessary defines in the first commit. Each commit should compile.

@keszybz

keszybz Dec 18, 2017

Member

This is undefined at this point. Please either move this chunk to the second commit or add the necessary defines in the first commit. Each commit should compile.

This comment has been minimized.

@Mic92

Mic92 Dec 18, 2017

Contributor

Each commit compiles for me now.

@Mic92

Mic92 Dec 18, 2017

Contributor

Each commit compiles for me now.

This comment has been minimized.

@zx2c4

zx2c4 Dec 18, 2017

Contributor

Indeed the diff in the first commit is just:

+static const NLType genl_families[] = {
+        [SD_GENL_ID_CTRL]  = { .type = NETLINK_TYPE_NESTED, .type_system = &genl_ctrl_id_ctrl_type_system },
+};

And then in the second one we get:

 static const NLType genl_families[] = {
         [SD_GENL_ID_CTRL]  = { .type = NETLINK_TYPE_NESTED, .type_system = &genl_ctrl_id_ctrl_type_system },
+        [SD_GENL_WIREGUARD] = { .type = NETLINK_TYPE_NESTED, .type_system = &genl_wireguard_type_system },
 };

So I think this segment is fine w.r.t. the independence of commits.

@zx2c4