diff --git a/src/libsystemd/sd-netlink/netlink-types.c b/src/libsystemd/sd-netlink/netlink-types.c index cd5cdcc6e55..bb7e8c33bae 100644 --- a/src/libsystemd/sd-netlink/netlink-types.c +++ b/src/libsystemd/sd-netlink/netlink-types.c @@ -732,7 +732,7 @@ static const NLTypeSystem genl_wireguard_peer_type_system = { static const NLType genl_wireguard_set_device_types[] = { [WGDEVICE_A_IFINDEX] = { .type = NETLINK_TYPE_U32 }, - [WGDEVICE_A_IFNAME] = { .type = NETLINK_TYPE_STRING }, + [WGDEVICE_A_IFNAME] = { .type = NETLINK_TYPE_STRING, .size = IFNAMSIZ-1 }, [WGDEVICE_A_FLAGS] = { .type = NETLINK_TYPE_U32 }, [WGDEVICE_A_PRIVATE_KEY] = { .size = WG_KEY_LEN }, [WGDEVICE_A_LISTEN_PORT] = { .type = NETLINK_TYPE_U16 }, diff --git a/src/network/netdev/wireguard.c b/src/network/netdev/wireguard.c index 45b7c7c3306..1efd8863f68 100644 --- a/src/network/netdev/wireguard.c +++ b/src/network/netdev/wireguard.c @@ -45,22 +45,137 @@ static WireguardPeer *wireguard_peer_new(Wireguard *w, unsigned section) { return peer; } -static int set_wireguard_interface(NetDev *netdev) { +static int wireguard_set_ipmask_one(NetDev *netdev, sd_netlink_message *message, const WireguardIPmask *mask, uint16_t index) { int r; - unsigned i, j; - WireguardPeer *peer, *peer_start; - WireguardIPmask *mask, *mask_start = NULL; + + assert(message); + assert(mask); + assert(index > 0); + + /* This returns 1 on success, 0 on recoverable error, and negative errno on failure. */ + + r = sd_netlink_message_open_array(message, index); + if (r < 0) + return 0; + + r = sd_netlink_message_append_u16(message, WGALLOWEDIP_A_FAMILY, mask->family); + if (r < 0) + goto cancel; + + if (mask->family == AF_INET) + r = sd_netlink_message_append_in_addr(message, WGALLOWEDIP_A_IPADDR, &mask->ip.in); + else if (mask->family == AF_INET6) + r = sd_netlink_message_append_in6_addr(message, WGALLOWEDIP_A_IPADDR, &mask->ip.in6); + if (r < 0) + goto cancel; + + r = sd_netlink_message_append_u8(message, WGALLOWEDIP_A_CIDR_MASK, mask->cidr); + if (r < 0) + goto cancel; + + r = sd_netlink_message_close_container(message); + if (r < 0) + return log_netdev_error_errno(netdev, r, "Could not add wireguard allowed ip: %m"); + + return 1; + +cancel: + r = sd_netlink_message_cancel_array(message); + if (r < 0) + return log_netdev_error_errno(netdev, r, "Could not cancel wireguard allowed ip message attribute: %m"); + + return 0; +} + +static int wireguard_set_peer_one(NetDev *netdev, sd_netlink_message *message, const WireguardPeer *peer, uint16_t index, WireguardIPmask **mask_start) { + WireguardIPmask *mask, *start; + uint16_t j = 0; + int r; + + assert(message); + assert(peer); + assert(index > 0); + assert(mask_start); + + /* This returns 1 on success, 0 on recoverable error, and negative errno on failure. */ + + start = *mask_start ?: peer->ipmasks; + + r = sd_netlink_message_open_array(message, index); + if (r < 0) + return 0; + + r = sd_netlink_message_append_data(message, WGPEER_A_PUBLIC_KEY, &peer->public_key, sizeof(peer->public_key)); + if (r < 0) + goto cancel; + + if (!start) { + r = sd_netlink_message_append_data(message, WGPEER_A_PRESHARED_KEY, &peer->preshared_key, WG_KEY_LEN); + if (r < 0) + goto cancel; + + r = sd_netlink_message_append_u32(message, WGPEER_A_FLAGS, peer->flags); + if (r < 0) + goto cancel; + + r = sd_netlink_message_append_u16(message, WGPEER_A_PERSISTENT_KEEPALIVE_INTERVAL, peer->persistent_keepalive_interval); + if (r < 0) + goto cancel; + + if (peer->endpoint.sa.sa_family == AF_INET) + r = sd_netlink_message_append_data(message, WGPEER_A_ENDPOINT, &peer->endpoint.in, sizeof(peer->endpoint.in)); + else if (peer->endpoint.sa.sa_family == AF_INET6) + r = sd_netlink_message_append_data(message, WGPEER_A_ENDPOINT, &peer->endpoint.in6, sizeof(peer->endpoint.in6)); + if (r < 0) + goto cancel; + } + + r = sd_netlink_message_open_container(message, WGPEER_A_ALLOWEDIPS); + if (r < 0) + goto cancel; + + LIST_FOREACH(ipmasks, mask, start) { + r = wireguard_set_ipmask_one(netdev, message, mask, ++j); + if (r < 0) + return r; + if (r == 0) + break; + } + + r = sd_netlink_message_close_container(message); + if (r < 0) + return log_netdev_error_errno(netdev, r, "Could not add wireguard allowed ip: %m"); + + r = sd_netlink_message_close_container(message); + if (r < 0) + return log_netdev_error_errno(netdev, r, "Could not add wireguard peer: %m"); + + *mask_start = mask; /* Start next cycle from this mask. */ + return !mask; + +cancel: + r = sd_netlink_message_cancel_array(message); + if (r < 0) + return log_netdev_error_errno(netdev, r, "Could not cancel wireguard peers: %m"); + + return 0; +} + +static int wireguard_set_interface(NetDev *netdev) { _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *message = NULL; - Wireguard *w; + WireguardIPmask *mask_start = NULL; + WireguardPeer *peer, *peer_start; uint32_t serial; + Wireguard *w; + int r; assert(netdev); w = WIREGUARD(netdev); assert(w); - peer_start = w->peers; + for (peer_start = w->peers; peer_start; ) { + uint16_t i = 0; - do { message = sd_netlink_message_unref(message); r = sd_genl_message_new(netdev->manager->genl, SD_GENL_WIREGUARD, WG_CMD_SET_DEVICE, &message); @@ -93,97 +208,14 @@ static int set_wireguard_interface(NetDev *netdev) { if (r < 0) return log_netdev_error_errno(netdev, r, "Could not append wireguard peer attributes: %m"); - i = 0; - LIST_FOREACH(peers, peer, peer_start) { - r = sd_netlink_message_open_array(message, ++i); + r = wireguard_set_peer_one(netdev, message, peer, ++i, &mask_start); if (r < 0) + return r; + if (r == 0) break; - - r = sd_netlink_message_append_data(message, WGPEER_A_PUBLIC_KEY, &peer->public_key, sizeof(peer->public_key)); - if (r < 0) - break; - - if (!mask_start) { - r = sd_netlink_message_append_data(message, WGPEER_A_PRESHARED_KEY, &peer->preshared_key, WG_KEY_LEN); - if (r < 0) - break; - - r = sd_netlink_message_append_u32(message, WGPEER_A_FLAGS, peer->flags); - if (r < 0) - break; - - r = sd_netlink_message_append_u16(message, WGPEER_A_PERSISTENT_KEEPALIVE_INTERVAL, peer->persistent_keepalive_interval); - if (r < 0) - break; - - if (peer->endpoint.sa.sa_family == AF_INET) { - r = sd_netlink_message_append_data(message, WGPEER_A_ENDPOINT, &peer->endpoint.in, sizeof(peer->endpoint.in)); - if (r < 0) - break; - } else if (peer->endpoint.sa.sa_family == AF_INET6) { - r = sd_netlink_message_append_data(message, WGPEER_A_ENDPOINT, &peer->endpoint.in6, sizeof(peer->endpoint.in6)); - if (r < 0) - break; - } - - mask_start = peer->ipmasks; - } - - r = sd_netlink_message_open_container(message, WGPEER_A_ALLOWEDIPS); - if (r < 0) { - mask_start = NULL; - break; - } - j = 0; - LIST_FOREACH(ipmasks, mask, mask_start) { - r = sd_netlink_message_open_array(message, ++j); - if (r < 0) - break; - - r = sd_netlink_message_append_u16(message, WGALLOWEDIP_A_FAMILY, mask->family); - if (r < 0) - break; - - if (mask->family == AF_INET) { - r = sd_netlink_message_append_in_addr(message, WGALLOWEDIP_A_IPADDR, &mask->ip.in); - if (r < 0) - break; - } else if (mask->family == AF_INET6) { - r = sd_netlink_message_append_in6_addr(message, WGALLOWEDIP_A_IPADDR, &mask->ip.in6); - if (r < 0) - break; - } - - r = sd_netlink_message_append_u8(message, WGALLOWEDIP_A_CIDR_MASK, mask->cidr); - if (r < 0) - break; - - r = sd_netlink_message_close_container(message); - if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not add wireguard allowed ip: %m"); - } - mask_start = mask; - if (mask_start) { - r = sd_netlink_message_cancel_array(message); - if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not cancel wireguard allowed ip message attribute: %m"); - } - r = sd_netlink_message_close_container(message); - if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not add wireguard allowed ip: %m"); - - r = sd_netlink_message_close_container(message); - if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not add wireguard peer: %m"); - } - - peer_start = peer; - if (peer_start && !mask_start) { - r = sd_netlink_message_cancel_array(message); - if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not cancel wireguard peers: %m"); } + peer_start = peer; /* Start next cycle from this peer. */ r = sd_netlink_message_close_container(message); if (r < 0) @@ -192,8 +224,7 @@ static int set_wireguard_interface(NetDev *netdev) { r = sd_netlink_send(netdev->manager->genl, message, &serial); if (r < 0) return log_netdev_error_errno(netdev, r, "Could not set wireguard device: %m"); - - } while (peer || mask_start); + } return 0; } @@ -278,7 +309,7 @@ static int wireguard_resolve_handler(sd_resolve_query *q, return 0; } - set_wireguard_interface(netdev); + (void) wireguard_set_interface(netdev); if (w->failed_endpoints) { _cleanup_(sd_event_source_unrefp) sd_event_source *s = NULL; @@ -353,7 +384,7 @@ static int netdev_wireguard_post_create(NetDev *netdev, Link *link, sd_netlink_m w = WIREGUARD(netdev); assert(w); - set_wireguard_interface(netdev); + (void) wireguard_set_interface(netdev); resolve_endpoints(netdev); return 0; } diff --git a/test/test-network/conf/25-wireguard-23-peers.netdev b/test/test-network/conf/25-wireguard-23-peers.netdev new file mode 100644 index 00000000000..7f77dc17431 --- /dev/null +++ b/test/test-network/conf/25-wireguard-23-peers.netdev @@ -0,0 +1,148 @@ +[NetDev] +Name=wg98 +Kind=wireguard +Description=For issue #11404 +# Generated by the script https://launchpadlibrarian.net/405947185/systemd-wg + +[WireGuard] +# 51820 is common port for Wireguard, 4500 is IPSec/UDP +ListenPort=4500 +PrivateKey=CJQUtcS9emY2fLYqDlpSZiE/QJyHkPWr+WHtZLZ90FU= + +# peer 1 +[WireGuardPeer] +PublicKey=TxVmU/YJ2R3G3cbGKUiIx02y6CgcKlElVGAkzrwJuXg= +AllowedIPs=fd8d:4d6d:3ccb:0500:0c79:2339:edce:ece1/128 +AllowedIPs=fd8d:4d6d:3ccb:0c79:2339:edce::/96 + +# peer 2 +[WireGuardPeer] +PublicKey=coGr5lLn1RsCeh1RWBXn1GvcDqKSQ82HSeN0GrUugHg= +AllowedIPs=fd8d:4d6d:3ccb:0500:a072:80da:de4f:add1/128 +AllowedIPs=fd8d:4d6d:3ccb:a072:80da:de4f::/96 + +# peer 3 +[WireGuardPeer] +PublicKey=PeiULTZjyfjqg/OOqnzKtLSWnrU+ipinqMsMw0hY+1w= +AllowedIPs=fd8d:4d6d:3ccb:0500:f349:c4f0:10c1:06b4/128 +AllowedIPs=fd8d:4d6d:3ccb:f349:c4f0:10c1::/96 + +# peer 4 +[WireGuardPeer] +PublicKey=PjdC8+BmQPdgheY7gle9s3gvM7r07L6A+gMBe5bOZXk= +AllowedIPs=fd8d:4d6d:3ccb:0500:b684:4f81:2e3e:132e/128 +AllowedIPs=fd8d:4d6d:3ccb:b684:4f81:2e3e::/96 + +# peer 5 +[WireGuardPeer] +PublicKey=1MGQurlRaQIAgdH/sd0qDNamDKAepMy/+pzZUx9oEDI= +AllowedIPs=fd8d:4d6d:3ccb:0500:c624:6bf7:4c09:3b59/128 +AllowedIPs=fd8d:4d6d:3ccb:c624:6bf7:4c09::/96 + +# peer 6 +[WireGuardPeer] +PublicKey=+FgzhoGfPIzNBvtIZfBwNtWls2FSGt/6Kve3M9Z1ZlE= +AllowedIPs=fd8d:4d6d:3ccb:0500:9c11:d820:2e96:9be0/128 +AllowedIPs=fd8d:4d6d:3ccb:9c11:d820:2e96::/96 + +# peer 7 +[WireGuardPeer] +PublicKey=Kb2ozFhzg9huKmV4miLlWgh05ToP+xVqd2N0e7Ebmyc= +AllowedIPs=fd8d:4d6d:3ccb:0500:bad5:495d:8e9c:3427/128 +AllowedIPs=fd8d:4d6d:3ccb:bad5:495d:8e9c::/96 + +# peer 8 +[WireGuardPeer] +PublicKey=p2kY786d4vFO/PpstaQrn7UPuakoHRABUHmcDKzXVi4= +AllowedIPs=fd8d:4d6d:3ccb:0500:1e54:1415:35d0:a47c/128 +AllowedIPs=fd8d:4d6d:3ccb:1e54:1415:35d0::/96 + +# peer 9 +[WireGuardPeer] +PublicKey=FNiQqmeizNXTmd9jEU/gvNkuEs1MoWovNp8IpPkoqz4= +AllowedIPs=fd8d:4d6d:3ccb:0500:1dbf:ca8a:32d3:dd81/128 +AllowedIPs=fd8d:4d6d:3ccb:1dbf:ca8a:32d3::/96 + +# peer 10 +[WireGuardPeer] +PublicKey=wAwyAuLSQTO7lwQnUQHTumrMgkwigIExGR26AthWTU8= +AllowedIPs=fd8d:4d6d:3ccb:0500:dcdd:d33b:90c9:6088/128 +AllowedIPs=fd8d:4d6d:3ccb:dcdd:d33b:90c9::/96 + +# peer 11 +[WireGuardPeer] +PublicKey=8gRYdXRyhgjiMSbqk3sj5kzXGsQqkZ4defvK2ONqHA0= +AllowedIPs=fd8d:4d6d:3ccb:0500:6f2e:6888:c6fd:dfb9/128 +AllowedIPs=fd8d:4d6d:3ccb:6f2e:6888:c6fd::/96 + +# peer 12 +[WireGuardPeer] +PublicKey=4Bj9Dalwnq2Trf5Bl7iJCpSOaxC83YEbxgrgBl0ljQk= +AllowedIPs=fd8d:4d6d:3ccb:0500:d4f9:05dc:9296:0a1a/128 +AllowedIPs=fd8d:4d6d:3ccb:d4f9:05dc:9296::/96 + +# peer 13 +[WireGuardPeer] +PublicKey=BOTxUDlPSIzYucVmML4IYcTIaX0TiqC7DOnfLUI7RRg= +AllowedIPs=fd8d:4d6d:3ccb:0500:b39c:9cdc:755a:ead3/128 +AllowedIPs=fd8d:4d6d:3ccb:b39c:9cdc:755a::/96 + +# peer 14 +[WireGuardPeer] +PublicKey=PiqEvBzow5vElGD2uOtRtZG6G60tM82kmjbyJP02mFQ= +AllowedIPs=fd8d:4d6d:3ccb:0500:bfe5:c3c3:5d77:0fcb/128 +AllowedIPs=fd8d:4d6d:3ccb:bfe5:c3c3:5d77::/96 + +# peer 15 +[WireGuardPeer] +PublicKey=Eor9QBsIoUG6C3ZKsKdqm4Vkt0n2N7qpSh2LzphQeho= +AllowedIPs=fd8d:4d6d:3ccb:0500:900c:d437:ec27:8822/128 +AllowedIPs=fd8d:4d6d:3ccb:900c:d437:ec27::/96 + +# peer 16 +[WireGuardPeer] +PublicKey=acXPW0Ar+TiTOqKuUDpop9AVLuPNdzqf0l+V8k5t7CM= +AllowedIPs=fd8d:4d6d:3ccb:0500:270d:b5dd:4a3f:8909/128 +AllowedIPs=fd8d:4d6d:3ccb:270d:b5dd:4a3f::/96 + +# peer 17 +[WireGuardPeer] +PublicKey=+0g/5jzbitHyfhB4gbJnWrhSMsSjCcOE2rftWTDEW3E= +AllowedIPs=fd8d:4d6d:3ccb:0500:e2e1:ae15:103f:f376/128 +AllowedIPs=fd8d:4d6d:3ccb:e2e1:ae15:103f::/96 + +# peer 18 +[WireGuardPeer] +PublicKey=FC7mjuWpkM8bjM3vaLPbba2HwHGbTSDK9QsLEZvUZ2I= +AllowedIPs=fd8d:4d6d:3ccb:0500:5660:679d:3532:94d8/128 +AllowedIPs=fd8d:4d6d:3ccb:5660:679d:3532::/96 + +# peer 19 +[WireGuardPeer] +PublicKey=fRdm/tbE2jGuLd6zMxSQYMP6pPfMmXYW84TUPJCPjHM= +AllowedIPs=fd8d:4d6d:3ccb:0500:6825:573f:30f3:9472/128 +AllowedIPs=fd8d:4d6d:3ccb:6825:573f:30f3::/96 + +# peer 20 +[WireGuardPeer] +PublicKey=xm540z0UwfTF5f0YwfjmCX0eGdHY8bOb/4ovVA2SgCI= +AllowedIPs=fd8d:4d6d:3ccb:0500:a94b:cd6a:a32d:90e6/128 +AllowedIPs=fd8d:4d6d:3ccb:a94b:cd6a:a32d::/96 + +# peer 21 +[WireGuardPeer] +PublicKey=5X5+9IiRBjaadazEKNO+CozpomiKPMIBQT7uJ4SnbVs= +AllowedIPs=fd8d:4d6d:3ccb:0500:8d4d:0bab:7280:a09a/128 +AllowedIPs=fd8d:4d6d:3ccb:8d4d:0bab:7280::/96 + +# peer 22 +[WireGuardPeer] +PublicKey=d61T4K0wmS4Z3lK9M8/Z48IXzldLCOCm7a6Mx1r/A3I= +AllowedIPs=fd8d:4d6d:3ccb:0500:a3f3:df38:19b0:0721/128 +AllowedIPs=fd8d:4d6d:3ccb:a3f3:df38:19b0::/96 + +# peer 23 +[WireGuardPeer] +PublicKey=Nt7N3hXfpMWsIsx9me4ID77okka+0Oe5eqnFDp00IFI= +AllowedIPs=fd8d:4d6d:3ccb:0500:9742:9931:5217:18d5/128 +AllowedIPs=fd8d:4d6d:3ccb:9742:9931:5217::/96 diff --git a/test/test-network/conf/25-wireguard-23-peers.network b/test/test-network/conf/25-wireguard-23-peers.network new file mode 100644 index 00000000000..4dc87f8d4c7 --- /dev/null +++ b/test/test-network/conf/25-wireguard-23-peers.network @@ -0,0 +1,97 @@ +[Match] +Name=wg98 + +[Network] +Address=fd8d:4d6d:3ccb:0500::1/64 + +# nat64 via 1 +[Route] +Destination = fd8d:4d6d:3ccb:0c79:2339:edce::/96 + +# nat64 via 2 +[Route] +Destination = fd8d:4d6d:3ccb:a072:80da:de4f::/96 + +# nat64 via 3 +[Route] +Destination = fd8d:4d6d:3ccb:f349:c4f0:10c1::/96 + +# nat64 via 4 +[Route] +Destination = fd8d:4d6d:3ccb:b684:4f81:2e3e::/96 + +# nat64 via 5 +[Route] +Destination = fd8d:4d6d:3ccb:c624:6bf7:4c09::/96 + +# nat64 via 6 +[Route] +Destination = fd8d:4d6d:3ccb:9c11:d820:2e96::/96 + +# nat64 via 7 +[Route] +Destination = fd8d:4d6d:3ccb:bad5:495d:8e9c::/96 + +# nat64 via 8 +[Route] +Destination = fd8d:4d6d:3ccb:1e54:1415:35d0::/96 + +# nat64 via 9 +[Route] +Destination = fd8d:4d6d:3ccb:1dbf:ca8a:32d3::/96 + +# nat64 via 10 +[Route] +Destination = fd8d:4d6d:3ccb:dcdd:d33b:90c9::/96 + +# nat64 via 11 +[Route] +Destination = fd8d:4d6d:3ccb:6f2e:6888:c6fd::/96 + +# nat64 via 12 +[Route] +Destination = fd8d:4d6d:3ccb:d4f9:05dc:9296::/96 + +# nat64 via 13 +[Route] +Destination = fd8d:4d6d:3ccb:b39c:9cdc:755a::/96 + +# nat64 via 14 +[Route] +Destination = fd8d:4d6d:3ccb:bfe5:c3c3:5d77::/96 + +# nat64 via 15 +[Route] +Destination = fd8d:4d6d:3ccb:900c:d437:ec27::/96 + +# nat64 via 16 +[Route] +Destination = fd8d:4d6d:3ccb:270d:b5dd:4a3f::/96 + +# nat64 via 17 +[Route] +Destination = fd8d:4d6d:3ccb:e2e1:ae15:103f::/96 + +# nat64 via 18 +[Route] +Destination = fd8d:4d6d:3ccb:5660:679d:3532::/96 + +# nat64 via 19 +[Route] +Destination = fd8d:4d6d:3ccb:6825:573f:30f3::/96 + +# nat64 via 20 +[Route] +Destination = fd8d:4d6d:3ccb:a94b:cd6a:a32d::/96 + +# nat64 via 21 +[Route] +Destination = fd8d:4d6d:3ccb:8d4d:0bab:7280::/96 + +# nat64 via 22 +[Route] +Destination = fd8d:4d6d:3ccb:a3f3:df38:19b0::/96 + +# nat64 via 23 +[Route] +Destination = fd8d:4d6d:3ccb:9742:9931:5217::/96 diff --git a/test/test-network/systemd-networkd-tests.py b/test/test-network/systemd-networkd-tests.py index 86a07c53b3d..5f57631cfb7 100755 --- a/test/test-network/systemd-networkd-tests.py +++ b/test/test-network/systemd-networkd-tests.py @@ -200,6 +200,7 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities): 'vti6tun99', 'vtitun99', 'vxlan99', + 'wg98', 'wg99'] units = [ @@ -233,6 +234,8 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities): '25-vti6-tunnel.netdev', '25-vti-tunnel.netdev', '25-vxlan.netdev', + '25-wireguard-23-peers.netdev', + '25-wireguard-23-peers.network', '25-wireguard.netdev', '6rd.network', 'gre.network', @@ -390,6 +393,16 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities): self.assertTrue(self.link_exits('wg99')) + @expectedFailureIfModuleIsNotAvailable('wireguard') + def test_wireguard_23_peers(self): + self.copy_unit_to_networkd_unit_path('25-wireguard-23-peers.netdev', '25-wireguard-23-peers.network') + self.start_networkd() + + if shutil.which('wg'): + subprocess.call('wg') + + self.assertTrue(self.link_exits('wg98')) + def test_geneve(self): self.copy_unit_to_networkd_unit_path('25-geneve.netdev') self.start_networkd()