diff options
Diffstat (limited to 'src/network/netdev')
31 files changed, 805 insertions, 338 deletions
diff --git a/src/network/netdev/bond.c b/src/network/netdev/bond.c index 5840a966ab..550a7f8914 100644 --- a/src/network/netdev/bond.c +++ b/src/network/netdev/bond.c @@ -7,6 +7,7 @@ #include "alloc-util.h" #include "conf-parser.h" +#include "ether-addr-util.h" #include "extract-word.h" #include "missing.h" #include "netdev/bond.h" @@ -284,10 +285,34 @@ static int netdev_bond_fill_message_create(NetDev *netdev, Link *link, sd_netlin return log_netdev_error_errno(netdev, r, "Could not append IFLA_BOND_MIN_LINKS attribute: %m"); } + if (b->ad_actor_sys_prio != 0) { + r = sd_netlink_message_append_u16(m, IFLA_BOND_AD_ACTOR_SYS_PRIO, b->ad_actor_sys_prio); + if (r < 0) + return log_netdev_error_errno(netdev, r, "Could not append IFLA_BOND_AD_ACTOR_SYS_PRIO attribute: %m"); + } + + if (b->ad_user_port_key != 0) { + r = sd_netlink_message_append_u16(m, IFLA_BOND_AD_USER_PORT_KEY, b->ad_user_port_key); + if (r < 0) + return log_netdev_error_errno(netdev, r, "Could not append IFLA_BOND_AD_USER_PORT_KEY attribute: %m"); + } + + if (b->ad_actor_system) { + r = sd_netlink_message_append_ether_addr(m, IFLA_BOND_AD_ACTOR_SYSTEM, b->ad_actor_system); + if (r < 0) + return log_netdev_error_errno(netdev, r, "Could not append IFLA_BOND_AD_ACTOR_SYSTEM attribute: %m"); + } + r = sd_netlink_message_append_u8(m, IFLA_BOND_ALL_SLAVES_ACTIVE, b->all_slaves_active); if (r < 0) return log_netdev_error_errno(netdev, r, "Could not append IFLA_BOND_ALL_SLAVES_ACTIVE attribute: %m"); + if (b->tlb_dynamic_lb >= 0) { + r = sd_netlink_message_append_u8(m, IFLA_BOND_TLB_DYNAMIC_LB, b->tlb_dynamic_lb); + if (r < 0) + return log_netdev_error_errno(netdev, r, "Could not append IFLA_BOND_TLB_DYNAMIC_LB attribute: %m"); + } + if (b->arp_interval > 0) { if (b->n_arp_ip_targets > 0) { @@ -357,10 +382,8 @@ int config_parse_arp_ip_target_address(const char *unit, return 0; } - LIST_PREPEND(arp_ip_target, b->arp_ip_targets, buffer); + LIST_PREPEND(arp_ip_target, b->arp_ip_targets, TAKE_PTR(buffer)); b->n_arp_ip_targets++; - - buffer = NULL; } if (b->n_arp_ip_targets > NETDEV_BOND_ARP_TARGETS_MAX) @@ -371,6 +394,115 @@ int config_parse_arp_ip_target_address(const char *unit, return 0; } +int config_parse_ad_actor_sys_prio(const char *unit, + const char *filename, + unsigned line, + const char *section, + unsigned section_line, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata) { + Bond *b = userdata; + uint16_t v; + int r; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(data); + + r = safe_atou16(rvalue, &v); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse actor system priority '%s', ignoring: %m", rvalue); + return 0; + } + + if (v == 0) { + log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse actor system priority '%s'. Range is [1,65535], ignoring.", rvalue); + return 0; + } + + b->ad_actor_sys_prio = v; + + return 0; +} + +int config_parse_ad_user_port_key(const char *unit, + const char *filename, + unsigned line, + const char *section, + unsigned section_line, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata) { + Bond *b = userdata; + uint16_t v; + int r; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(data); + + r = safe_atou16(rvalue, &v); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse user port key '%s', ignoring: %m", rvalue); + return 0; + } + + if (v > 1023) { + log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse user port key '%s'. Range is [0,1023], ignoring.", rvalue); + return 0; + } + + b->ad_user_port_key = v; + + return 0; +} + +int config_parse_ad_actor_system(const char *unit, + const char *filename, + unsigned line, + const char *section, + unsigned section_line, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata) { + Bond *b = userdata; + _cleanup_free_ struct ether_addr *n = NULL; + int r; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(data); + + n = new0(struct ether_addr, 1); + if (!n) + return log_oom(); + + r = ether_addr_from_string(rvalue, n); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, r, "Not a valid MAC address %s. Ignoring assignment: %m", rvalue); + return 0; + } + + if (ether_addr_is_null(n) || (n->ether_addr_octet[0] & 0x01)) { + log_syntax(unit, LOG_ERR, filename, line, 0, "Not a valid MAC address %s, can not be null or multicast. Ignoring assignment.", rvalue); + return 0; + } + + free_and_replace(b->ad_actor_system, n); + + return 0; +} + static void bond_done(NetDev *netdev) { ArpIpTarget *t = NULL, *n = NULL; Bond *b; @@ -381,6 +513,8 @@ static void bond_done(NetDev *netdev) { assert(b); + free(b->ad_actor_system); + LIST_FOREACH_SAFE(arp_ip_target, t, n, b->arp_ip_targets) free(t); @@ -406,6 +540,7 @@ static void bond_init(NetDev *netdev) { b->primary_reselect = _NETDEV_BOND_PRIMARY_RESELECT_INVALID; b->all_slaves_active = false; + b->tlb_dynamic_lb = -1; b->resend_igmp = RESEND_IGMP_DEFAULT; b->packets_per_slave = PACKETS_PER_SLAVE_DEFAULT; diff --git a/src/network/netdev/bond.h b/src/network/netdev/bond.h index fd68a18985..31b922b032 100644 --- a/src/network/netdev/bond.h +++ b/src/network/netdev/bond.h @@ -1,7 +1,6 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once - #include "in-addr-util.h" #include "list.h" @@ -100,6 +99,8 @@ typedef struct Bond { BondArpAllTargets arp_all_targets; BondPrimaryReselect primary_reselect; + int tlb_dynamic_lb; + bool all_slaves_active; unsigned resend_igmp; @@ -107,6 +108,10 @@ typedef struct Bond { unsigned num_grat_arp; unsigned min_links; + uint16_t ad_actor_sys_prio; + uint16_t ad_user_port_key; + struct ether_addr *ad_actor_system; + usec_t miimon; usec_t updelay; usec_t downdelay; @@ -144,12 +149,15 @@ BondArpAllTargets bond_arp_all_targets_from_string(const char *d) _pure_; const char *bond_primary_reselect_to_string(BondPrimaryReselect d) _const_; BondPrimaryReselect bond_primary_reselect_from_string(const char *d) _pure_; -int config_parse_bond_mode(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); -int config_parse_bond_xmit_hash_policy(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); -int config_parse_bond_lacp_rate(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); -int config_parse_bond_ad_select(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); -int config_parse_bond_fail_over_mac(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); -int config_parse_bond_arp_validate(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); -int config_parse_bond_arp_all_targets(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); -int config_parse_bond_primary_reselect(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); -int config_parse_arp_ip_target_address(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); +CONFIG_PARSER_PROTOTYPE(config_parse_bond_mode); +CONFIG_PARSER_PROTOTYPE(config_parse_bond_xmit_hash_policy); +CONFIG_PARSER_PROTOTYPE(config_parse_bond_lacp_rate); +CONFIG_PARSER_PROTOTYPE(config_parse_bond_ad_select); +CONFIG_PARSER_PROTOTYPE(config_parse_bond_fail_over_mac); +CONFIG_PARSER_PROTOTYPE(config_parse_bond_arp_validate); +CONFIG_PARSER_PROTOTYPE(config_parse_bond_arp_all_targets); +CONFIG_PARSER_PROTOTYPE(config_parse_bond_primary_reselect); +CONFIG_PARSER_PROTOTYPE(config_parse_arp_ip_target_address); +CONFIG_PARSER_PROTOTYPE(config_parse_ad_actor_sys_prio); +CONFIG_PARSER_PROTOTYPE(config_parse_ad_user_port_key); +CONFIG_PARSER_PROTOTYPE(config_parse_ad_actor_system); diff --git a/src/network/netdev/bridge.c b/src/network/netdev/bridge.c index be5914a2fa..0c804adb2e 100644 --- a/src/network/netdev/bridge.c +++ b/src/network/netdev/bridge.c @@ -9,8 +9,7 @@ #include "vlan-util.h" /* callback for brige netdev's parameter set */ -static int netdev_bridge_set_handler(sd_netlink *rtnl, sd_netlink_message *m, void *userdata) { - _cleanup_(netdev_unrefp) NetDev *netdev = userdata; +static int netdev_bridge_set_handler(sd_netlink *rtnl, sd_netlink_message *m, NetDev *netdev) { int r; assert(netdev); @@ -129,7 +128,8 @@ static int netdev_bridge_post_create(NetDev *netdev, Link *link, sd_netlink_mess if (r < 0) return log_netdev_error_errno(netdev, r, "Could not append IFLA_INFO_DATA attribute: %m"); - r = sd_netlink_call_async(netdev->manager->rtnl, req, netdev_bridge_set_handler, netdev, 0, NULL); + r = netlink_call_async(netdev->manager->rtnl, NULL, req, netdev_bridge_set_handler, + netdev_destroy_callback, netdev); if (r < 0) return log_netdev_error_errno(netdev, r, "Could not send rtnetlink message: %m"); diff --git a/src/network/netdev/bridge.h b/src/network/netdev/bridge.h index 4854e46953..3edc93a767 100644 --- a/src/network/netdev/bridge.h +++ b/src/network/netdev/bridge.h @@ -1,7 +1,6 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once - #include "netdev/netdev.h" typedef struct Bridge { diff --git a/src/network/netdev/dummy.h b/src/network/netdev/dummy.h index f4679a640d..93e0651f7d 100644 --- a/src/network/netdev/dummy.h +++ b/src/network/netdev/dummy.h @@ -1,7 +1,6 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once - #include "netdev/netdev.h" typedef struct Dummy { diff --git a/src/network/netdev/fou-tunnel.c b/src/network/netdev/fou-tunnel.c new file mode 100644 index 0000000000..65dad384e2 --- /dev/null +++ b/src/network/netdev/fou-tunnel.c @@ -0,0 +1,129 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ + +#include <arpa/inet.h> +#include <net/if.h> +#include <linux/ip.h> + +#include "conf-parser.h" +#include "missing.h" +#include "netdev/fou-tunnel.h" +#include "networkd-link.h" +#include "networkd-manager.h" +#include "parse-util.h" +#include "sd-netlink.h" +#include "string-table.h" +#include "string-util.h" +#include "util.h" + +static const char* const fou_encap_type_table[_NETDEV_FOO_OVER_UDP_ENCAP_MAX] = { + [NETDEV_FOO_OVER_UDP_ENCAP_DIRECT] = "FooOverUDP", + [NETDEV_FOO_OVER_UDP_ENCAP_GUE] = "GenericUDPEncapsulation", +}; + +DEFINE_STRING_TABLE_LOOKUP(fou_encap_type, FooOverUDPEncapType); +DEFINE_CONFIG_PARSE_ENUM(config_parse_fou_encap_type, fou_encap_type, FooOverUDPEncapType, + "Failed to parse Encapsulation="); + +static int netdev_fill_fou_tunnel_message(NetDev *netdev, sd_netlink_message **ret) { + _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL; + FouTunnel *t; + int r; + + assert(netdev); + + t = FOU(netdev); + + assert(t); + + r = sd_genl_message_new(netdev->manager->genl, SD_GENL_FOU, FOU_CMD_ADD, &m); + if (r < 0) + return log_netdev_error_errno(netdev, r, "Failed to allocate generic netlink message: %m"); + + r = sd_netlink_message_append_u16(m, FOU_ATTR_PORT, htobe16(t->port)); + if (r < 0) + return log_netdev_error_errno(netdev, r, "Could not append FOU_ATTR_PORT attribute: %m"); + + r = sd_netlink_message_append_u8(m, FOU_ATTR_TYPE, FOU_ENCAP_GUE); + if (r < 0) + return log_netdev_error_errno(netdev, r, "Could not append FOU_ATTR_TYPE attribute: %m"); + + r = sd_netlink_message_append_u8(m, FOU_ATTR_AF, AF_INET); + if (r < 0) + return log_netdev_error_errno(netdev, r, "Could not append FOU_ATTR_AF attribute: %m"); + + r = sd_netlink_message_append_u8(m, FOU_ATTR_IPPROTO, t->fou_protocol); + if (r < 0) + return log_netdev_error_errno(netdev, r, "Could not append FOU_ATTR_IPPROTO attribute: %m"); + + *ret = m; + m = NULL; + + return 0; +} + +static int netdev_fou_tunnel_create(NetDev *netdev) { + _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL; + uint32_t serial; + FouTunnel *t; + int r; + + assert(netdev); + + t = FOU(netdev); + + assert(t); + + r = netdev_fill_fou_tunnel_message(netdev, &m); + if (r < 0) + return r; + + r = sd_netlink_send(netdev->manager->genl, m, &serial); + if (r < 0 && r != -EADDRINUSE) + return log_netdev_error_errno(netdev, r, "Failed to add FooOverUDP tunnel: %m"); + + return 0; +} + +static int netdev_fou_tunnel_verify(NetDev *netdev, const char *filename) { + FouTunnel *t; + + assert(netdev); + assert(filename); + + t = FOU(netdev); + + assert(t); + + if (t->fou_encap_type == NETDEV_FOO_OVER_UDP_ENCAP_DIRECT && t->fou_protocol <= 0) { + log_netdev_error(netdev, "FooOverUDP protocol not configured in %s. Rejecting configuration.", filename); + return -EINVAL; + } + + if (t->fou_encap_type == NETDEV_FOO_OVER_UDP_ENCAP_GUE && t->fou_protocol > 0) { + log_netdev_error(netdev, "FooOverUDP GUE can't be set with protocol configured in %s. Rejecting configuration.", filename); + return -EINVAL; + } + + return 0; +} + +static void fou_tunnel_init(NetDev *netdev) { + FouTunnel *t; + + assert(netdev); + + t = FOU(netdev); + + assert(t); + + t->fou_encap_type = NETDEV_FOO_OVER_UDP_ENCAP_DIRECT; +} + +const NetDevVTable foutnl_vtable = { + .object_size = sizeof(FouTunnel), + .init = fou_tunnel_init, + .sections = "Match\0NetDev\0FooOverUDP\0", + .create = netdev_fou_tunnel_create, + .create_type = NETDEV_CREATE_INDEPENDENT, + .config_verify = netdev_fou_tunnel_verify, +}; diff --git a/src/network/netdev/fou-tunnel.h b/src/network/netdev/fou-tunnel.h new file mode 100644 index 0000000000..b8abed19bd --- /dev/null +++ b/src/network/netdev/fou-tunnel.h @@ -0,0 +1,36 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ +#pragma once + +#if HAVE_LINUX_FOU_H +#include <linux/fou.h> +#endif + +#include "in-addr-util.h" +#include "missing_fou.h" +#include "netdev/netdev.h" + +typedef enum FooOverUDPEncapType { + NETDEV_FOO_OVER_UDP_ENCAP_UNSPEC = FOU_ENCAP_UNSPEC, + NETDEV_FOO_OVER_UDP_ENCAP_DIRECT = FOU_ENCAP_DIRECT, + NETDEV_FOO_OVER_UDP_ENCAP_GUE = FOU_ENCAP_GUE, + _NETDEV_FOO_OVER_UDP_ENCAP_MAX, + _NETDEV_FOO_OVER_UDP_ENCAP_INVALID = -1, +} FooOverUDPEncapType; + +typedef struct FouTunnel { + NetDev meta; + + uint8_t fou_protocol; + + uint16_t port; + + FooOverUDPEncapType fou_encap_type; +} FouTunnel; + +DEFINE_NETDEV_CAST(FOU, FouTunnel); +extern const NetDevVTable foutnl_vtable; + +const char *fou_encap_type_to_string(FooOverUDPEncapType d) _const_; +FooOverUDPEncapType fou_encap_type_from_string(const char *d) _pure_; + +CONFIG_PARSER_PROTOTYPE(config_parse_fou_encap_type); diff --git a/src/network/netdev/geneve.c b/src/network/netdev/geneve.c index 18b6b359f3..089bbfea22 100644 --- a/src/network/netdev/geneve.c +++ b/src/network/netdev/geneve.c @@ -2,12 +2,14 @@ #include <net/if.h> +#include "sd-netlink.h" + #include "alloc-util.h" #include "conf-parser.h" #include "extract-word.h" #include "geneve.h" +#include "netlink-util.h" #include "parse-util.h" -#include "sd-netlink.h" #include "string-util.h" #include "strv.h" #include "missing.h" @@ -17,10 +19,10 @@ #define DEFAULT_GENEVE_DESTINATION_PORT 6081 /* callback for geneve netdev's created without a backing Link */ -static int geneve_netdev_create_handler(sd_netlink *rtnl, sd_netlink_message *m, void *userdata) { - _cleanup_(netdev_unrefp) NetDev *netdev = userdata; +static int geneve_netdev_create_handler(sd_netlink *rtnl, sd_netlink_message *m, NetDev *netdev) { int r; + assert(netdev); assert(netdev->state != _NETDEV_STATE_INVALID); r = sd_netlink_message_get_errno(m); @@ -135,12 +137,12 @@ static int netdev_geneve_create(NetDev *netdev) { if (r < 0) return log_netdev_error_errno(netdev, r, "Could not append IFLA_LINKINFO attribute: %m"); - r = sd_netlink_call_async(netdev->manager->rtnl, m, geneve_netdev_create_handler, netdev, 0, NULL); + r = netlink_call_async(netdev->manager->rtnl, NULL, m, geneve_netdev_create_handler, + netdev_destroy_callback, netdev); if (r < 0) return log_netdev_error_errno(netdev, r, "Could not send rtnetlink message: %m"); netdev_ref(netdev); - netdev->state = NETDEV_STATE_CREATING; log_netdev_debug(netdev, "Creating"); diff --git a/src/network/netdev/geneve.h b/src/network/netdev/geneve.h index dab9a4a553..c201981e02 100644 --- a/src/network/netdev/geneve.h +++ b/src/network/netdev/geneve.h @@ -1,7 +1,6 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once - typedef struct Geneve Geneve; #include "in-addr-util.h" @@ -34,35 +33,6 @@ struct Geneve { DEFINE_NETDEV_CAST(GENEVE, Geneve); extern const NetDevVTable geneve_vtable; -int config_parse_geneve_vni(const char *unit, - const char *filename, - unsigned line, - const char *section, - unsigned section_line, - const char *lvalue, - int ltype, - const char *rvalue, - void *data, - void *userdata); - -int config_parse_geneve_address(const char *unit, - const char *filename, - unsigned line, - const char *section, - unsigned section_line, - const char *lvalue, - int ltype, - const char *rvalue, - void *data, - void *userdata); - -int config_parse_geneve_flow_label(const char *unit, - const char *filename, - unsigned line, - const char *section, - unsigned section_line, - const char *lvalue, - int ltype, - const char *rvalue, - void *data, - void *userdata); +CONFIG_PARSER_PROTOTYPE(config_parse_geneve_vni); +CONFIG_PARSER_PROTOTYPE(config_parse_geneve_address); +CONFIG_PARSER_PROTOTYPE(config_parse_geneve_flow_label); diff --git a/src/network/netdev/ipvlan.h b/src/network/netdev/ipvlan.h index 8b698ebd1a..fb426d37e5 100644 --- a/src/network/netdev/ipvlan.h +++ b/src/network/netdev/ipvlan.h @@ -1,10 +1,9 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once - #include <linux/if_link.h> -#include "missing.h" +#include "missing_if_link.h" #include "netdev/netdev.h" typedef enum IPVlanMode { @@ -39,5 +38,5 @@ IPVlanMode ipvlan_mode_from_string(const char *d) _pure_; const char *ipvlan_flags_to_string(IPVlanFlags d) _const_; IPVlanFlags ipvlan_flags_from_string(const char *d) _pure_; -int config_parse_ipvlan_mode(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); -int config_parse_ipvlan_flags(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); +CONFIG_PARSER_PROTOTYPE(config_parse_ipvlan_mode); +CONFIG_PARSER_PROTOTYPE(config_parse_ipvlan_flags); diff --git a/src/network/netdev/macvlan.h b/src/network/netdev/macvlan.h index dcc0a3fd35..b473f1e19f 100644 --- a/src/network/netdev/macvlan.h +++ b/src/network/netdev/macvlan.h @@ -1,7 +1,6 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once - typedef struct MacVlan MacVlan; #include "netdev/netdev.h" @@ -29,4 +28,4 @@ extern const NetDevVTable macvtap_vtable; const char *macvlan_mode_to_string(MacVlanMode d) _const_; MacVlanMode macvlan_mode_from_string(const char *d) _pure_; -int config_parse_macvlan_mode(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); +CONFIG_PARSER_PROTOTYPE(config_parse_macvlan_mode); diff --git a/src/network/netdev/netdev-gperf.gperf b/src/network/netdev/netdev-gperf.gperf index dfcac9adbf..f7ca98fa46 100644 --- a/src/network/netdev/netdev-gperf.gperf +++ b/src/network/netdev/netdev-gperf.gperf @@ -19,6 +19,7 @@ _Pragma("GCC diagnostic ignored \"-Wimplicit-fallthrough\"") #include "netdev/netdev.h" #include "netdev/vxcan.h" #include "netdev/wireguard.h" +#include "netdev/fou-tunnel.h" #include "vlan-util.h" %} struct ConfigPerfItem; @@ -65,6 +66,17 @@ Tunnel.CopyDSCP, config_parse_bool, 0, Tunnel.EncapsulationLimit, config_parse_encap_limit, 0, offsetof(Tunnel, encap_limit) Tunnel.Independent, config_parse_bool, 0, offsetof(Tunnel, independent) Tunnel.AllowLocalRemote, config_parse_tristate, 0, offsetof(Tunnel, allow_localremote) +Tunnel.FooOverUDP, config_parse_bool, 0, offsetof(Tunnel, fou_tunnel) +Tunnel.FOUDestinationPort, config_parse_ip_port, 0, offsetof(Tunnel, fou_destination_port) +Tunnel.FOUSourcePort, config_parse_ip_port, 0, offsetof(Tunnel, encap_src_port) +Tunnel.Encapsulation, config_parse_fou_encap_type, 0, offsetof(Tunnel, fou_encap_type) +Tunnel.IPv6RapidDeploymentPrefix, config_parse_6rd_prefix, 0, 0 +Tunnel.ERSPANIndex, config_parse_uint32, 0, offsetof(Tunnel, erspan_index) +Tunnel.SerializeTunneledPackets, config_parse_tristate, 0, offsetof(Tunnel, erspan_sequence) +Tunnel.ISATAP, config_parse_tristate, 0, offsetof(Tunnel, isatap) +FooOverUDP.Protocol, config_parse_uint8, 0, offsetof(FouTunnel, fou_protocol) +FooOverUDP.Encapsulation, config_parse_fou_encap_type, 0, offsetof(FouTunnel, fou_encap_type) +FooOverUDP.Port, config_parse_ip_port, 0, offsetof(FouTunnel, port) Peer.Name, config_parse_ifname, 0, offsetof(Veth, ifname_peer) Peer.MACAddress, config_parse_hwaddr, 0, offsetof(Veth, mac_peer) VXCAN.Peer, config_parse_ifname, 0, offsetof(VxCan, ifname_peer) @@ -100,7 +112,9 @@ GENEVE.TOS, config_parse_uint8, 0, GENEVE.TTL, config_parse_uint8, 0, offsetof(Geneve, ttl) GENEVE.UDPChecksum, config_parse_bool, 0, offsetof(Geneve, udpcsum) GENEVE.UDP6ZeroCheckSumRx, config_parse_bool, 0, offsetof(Geneve, udp6zerocsumrx) +GENEVE.UDP6ZeroChecksumRx, config_parse_bool, 0, offsetof(Geneve, udp6zerocsumrx) GENEVE.UDP6ZeroCheckSumTx, config_parse_bool, 0, offsetof(Geneve, udp6zerocsumtx) +GENEVE.UDP6ZeroChecksumTx, config_parse_bool, 0, offsetof(Geneve, udp6zerocsumtx) GENEVE.DestinationPort, config_parse_ip_port, 0, offsetof(Geneve, dest_port) GENEVE.FlowLabel, config_parse_geneve_flow_label, 0, 0 Tun.OneQueue, config_parse_bool, 0, offsetof(TunTap, one_queue) @@ -126,13 +140,17 @@ Bond.PrimaryReselectPolicy, config_parse_bond_primary_reselect, 0, Bond.ResendIGMP, config_parse_unsigned, 0, offsetof(Bond, resend_igmp) Bond.PacketsPerSlave, config_parse_unsigned, 0, offsetof(Bond, packets_per_slave) Bond.GratuitousARP, config_parse_unsigned, 0, offsetof(Bond, num_grat_arp) -Bond.AllSlavesActive, config_parse_unsigned, 0, offsetof(Bond, all_slaves_active) +Bond.AllSlavesActive, config_parse_bool, 0, offsetof(Bond, all_slaves_active) +Bond.DynamicTransmitLoadBalancing, config_parse_tristate, 0, offsetof(Bond, tlb_dynamic_lb) Bond.MinLinks, config_parse_unsigned, 0, offsetof(Bond, min_links) Bond.MIIMonitorSec, config_parse_sec, 0, offsetof(Bond, miimon) Bond.UpDelaySec, config_parse_sec, 0, offsetof(Bond, updelay) Bond.DownDelaySec, config_parse_sec, 0, offsetof(Bond, downdelay) Bond.ARPIntervalSec, config_parse_sec, 0, offsetof(Bond, arp_interval) Bond.LearnPacketIntervalSec, config_parse_sec, 0, offsetof(Bond, lp_interval) +Bond.AdActorSystemPriority, config_parse_ad_actor_sys_prio, 0, offsetof(Bond, ad_actor_sys_prio) +Bond.AdUserPortKey, config_parse_ad_user_port_key, 0, offsetof(Bond, ad_user_port_key) +Bond.AdActorSystem, config_parse_ad_actor_system, 0, offsetof(Bond, ad_actor_system) Bridge.HelloTimeSec, config_parse_sec, 0, offsetof(Bridge, hello_time) Bridge.MaxAgeSec, config_parse_sec, 0, offsetof(Bridge, max_age) Bridge.AgeingTimeSec, config_parse_sec, 0, offsetof(Bridge, ageing_time) diff --git a/src/network/netdev/netdev.c b/src/network/netdev/netdev.c index 82ce88402f..f0e9d00246 100644 --- a/src/network/netdev/netdev.c +++ b/src/network/netdev/netdev.c @@ -16,6 +16,7 @@ #include "stat-util.h" #include "string-table.h" #include "string-util.h" +#include "strv.h" #include "netdev/bridge.h" #include "netdev/bond.h" @@ -33,6 +34,7 @@ #include "netdev/vxcan.h" #include "netdev/wireguard.h" #include "netdev/netdevsim.h" +#include "netdev/fou-tunnel.h" const NetDevVTable * const netdev_vtable[_NETDEV_KIND_MAX] = { [NETDEV_KIND_BRIDGE] = &bridge_vtable, @@ -61,6 +63,8 @@ const NetDevVTable * const netdev_vtable[_NETDEV_KIND_MAX] = { [NETDEV_KIND_VXCAN] = &vxcan_vtable, [NETDEV_KIND_WIREGUARD] = &wireguard_vtable, [NETDEV_KIND_NETDEVSIM] = &netdevsim_vtable, + [NETDEV_KIND_FOU] = &foutnl_vtable, + [NETDEV_KIND_ERSPAN] = &erspan_vtable, }; static const char* const netdev_kind_table[_NETDEV_KIND_MAX] = { @@ -90,45 +94,74 @@ static const char* const netdev_kind_table[_NETDEV_KIND_MAX] = { [NETDEV_KIND_VXCAN] = "vxcan", [NETDEV_KIND_WIREGUARD] = "wireguard", [NETDEV_KIND_NETDEVSIM] = "netdevsim", + [NETDEV_KIND_FOU] = "fou", + [NETDEV_KIND_ERSPAN] = "erspan", }; DEFINE_STRING_TABLE_LOOKUP(netdev_kind, NetDevKind); -DEFINE_CONFIG_PARSE_ENUM(config_parse_netdev_kind, netdev_kind, NetDevKind, "Failed to parse netdev kind"); -static void netdev_cancel_callbacks(NetDev *netdev) { - _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL; - netdev_join_callback *callback; +int config_parse_netdev_kind( + const char *unit, + const char *filename, + unsigned line, + const char *section, + unsigned section_line, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata) { + + NetDevKind k, *kind = data; + + assert(rvalue); + assert(data); + + k = netdev_kind_from_string(rvalue); + if (k < 0) { + log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse netdev kind, ignoring assignment: %s", rvalue); + return 0; + } - if (!netdev || !netdev->manager) - return; + if (*kind != _NETDEV_KIND_INVALID && *kind != k) { + log_syntax(unit, LOG_ERR, filename, line, 0, + "Specified netdev kind is different from the previous value '%s', ignoring assignment: %s", + netdev_kind_to_string(*kind), rvalue); + return 0; + } - rtnl_message_new_synthetic_error(netdev->manager->rtnl, -ENODEV, 0, &m); + *kind = k; - while ((callback = netdev->callbacks)) { - if (m) { - assert(callback->link); - assert(callback->callback); - assert(netdev->manager); - assert(netdev->manager->rtnl); + return 0; +} - callback->callback(netdev->manager->rtnl, m, callback->link); - } +static void netdev_callbacks_clear(NetDev *netdev) { + netdev_join_callback *callback; + if (!netdev) + return; + + while ((callback = netdev->callbacks)) { LIST_REMOVE(callbacks, netdev->callbacks, callback); link_unref(callback->link); free(callback); } } -static void netdev_free(NetDev *netdev) { - if (!netdev) - return; - - netdev_cancel_callbacks(netdev); - +static void netdev_detach_from_manager(NetDev *netdev) { if (netdev->ifname && netdev->manager) hashmap_remove(netdev->manager->netdevs, netdev->ifname); + netdev->manager = NULL; +} + +static NetDev *netdev_free(NetDev *netdev) { + assert(netdev); + + netdev_callbacks_clear(netdev); + + netdev_detach_from_manager(netdev); + free(netdev->filename); free(netdev->description); @@ -153,22 +186,10 @@ static void netdev_free(NetDev *netdev) { NETDEV_VTABLE(netdev)->done) NETDEV_VTABLE(netdev)->done(netdev); - free(netdev); + return mfree(netdev); } -NetDev *netdev_unref(NetDev *netdev) { - if (netdev && (-- netdev->n_ref <= 0)) - netdev_free(netdev); - - return NULL; -} - -NetDev *netdev_ref(NetDev *netdev) { - if (netdev) - assert_se(++ netdev->n_ref >= 2); - - return netdev; -} +DEFINE_TRIVIAL_REF_UNREF_FUNC(NetDev, netdev, netdev_free); void netdev_drop(NetDev *netdev) { if (!netdev || netdev->state == NETDEV_STATE_LINGER) @@ -178,7 +199,9 @@ void netdev_drop(NetDev *netdev) { log_netdev_debug(netdev, "netdev removed"); - netdev_cancel_callbacks(netdev); + netdev_callbacks_clear(netdev); + + netdev_detach_from_manager(netdev); netdev_unref(netdev); @@ -206,12 +229,12 @@ int netdev_get(Manager *manager, const char *name, NetDev **ret) { static int netdev_enter_failed(NetDev *netdev) { netdev->state = NETDEV_STATE_FAILED; - netdev_cancel_callbacks(netdev); + netdev_callbacks_clear(netdev); return 0; } -static int netdev_enslave_ready(NetDev *netdev, Link* link, sd_netlink_message_handler_t callback) { +static int netdev_enslave_ready(NetDev *netdev, Link* link, link_netlink_message_handler_t callback) { _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL; int r; @@ -238,7 +261,8 @@ static int netdev_enslave_ready(NetDev *netdev, Link* link, sd_netlink_message_h if (r < 0) return log_netdev_error_errno(netdev, r, "Could not append IFLA_MASTER attribute: %m"); - r = sd_netlink_call_async(netdev->manager->rtnl, req, callback, link, 0, NULL); + r = netlink_call_async(netdev->manager->rtnl, NULL, req, callback, + link_netlink_destroy_callback, link); if (r < 0) return log_netdev_error_errno(netdev, r, "Could not send rtnetlink message: %m"); @@ -282,10 +306,10 @@ static int netdev_enter_ready(NetDev *netdev) { } /* callback for netdev's created without a backing Link */ -static int netdev_create_handler(sd_netlink *rtnl, sd_netlink_message *m, void *userdata) { - _cleanup_(netdev_unrefp) NetDev *netdev = userdata; +static int netdev_create_handler(sd_netlink *rtnl, sd_netlink_message *m, NetDev *netdev) { int r; + assert(netdev); assert(netdev->state != _NETDEV_STATE_INVALID); r = sd_netlink_message_get_errno(m); @@ -303,7 +327,7 @@ static int netdev_create_handler(sd_netlink *rtnl, sd_netlink_message *m, void * return 1; } -int netdev_enslave(NetDev *netdev, Link *link, sd_netlink_message_handler_t callback) { +static int netdev_enslave(NetDev *netdev, Link *link, link_netlink_message_handler_t callback) { int r; assert(netdev); @@ -325,13 +349,14 @@ int netdev_enslave(NetDev *netdev, Link *link, sd_netlink_message_handler_t call /* the netdev is not yet read, save this request for when it is */ netdev_join_callback *cb; - cb = new0(netdev_join_callback, 1); + cb = new(netdev_join_callback, 1); if (!cb) return log_oom(); - cb->callback = callback; - cb->link = link; - link_ref(link); + *cb = (netdev_join_callback) { + .callback = callback, + .link = link_ref(link), + }; LIST_PREPEND(callbacks, netdev->callbacks, cb); @@ -478,8 +503,7 @@ int netdev_get_mac(const char *ifname, struct ether_addr **ret) { return 0; } -static int netdev_create(NetDev *netdev, Link *link, - sd_netlink_message_handler_t callback) { +static int netdev_create(NetDev *netdev, Link *link, link_netlink_message_handler_t callback) { int r; assert(netdev); @@ -546,13 +570,15 @@ static int netdev_create(NetDev *netdev, Link *link, return log_netdev_error_errno(netdev, r, "Could not append IFLA_LINKINFO attribute: %m"); if (link) { - r = sd_netlink_call_async(netdev->manager->rtnl, m, callback, link, 0, NULL); + r = netlink_call_async(netdev->manager->rtnl, NULL, m, callback, + link_netlink_destroy_callback, link); if (r < 0) return log_netdev_error_errno(netdev, r, "Could not send rtnetlink message: %m"); link_ref(link); } else { - r = sd_netlink_call_async(netdev->manager->rtnl, m, netdev_create_handler, netdev, 0, NULL); + r = netlink_call_async(netdev->manager->rtnl, NULL, m, netdev_create_handler, + netdev_destroy_callback, netdev); if (r < 0) return log_netdev_error_errno(netdev, r, "Could not send rtnetlink message: %m"); @@ -568,7 +594,7 @@ static int netdev_create(NetDev *netdev, Link *link, } /* the callback must be called, possibly after a timeout, as otherwise the Link will hang */ -int netdev_join(NetDev *netdev, Link *link, sd_netlink_message_handler_t callback) { +int netdev_join(NetDev *netdev, Link *link, link_netlink_message_handler_t callback) { int r; assert(netdev); @@ -596,7 +622,7 @@ int netdev_join(NetDev *netdev, Link *link, sd_netlink_message_handler_t callbac return 0; } -static int netdev_load_one(Manager *manager, const char *filename) { +int netdev_load_one(Manager *manager, const char *filename) { _cleanup_(netdev_unrefp) NetDev *netdev_raw = NULL, *netdev = NULL; _cleanup_fclose_ FILE *file = NULL; const char *dropin_dirname; @@ -619,13 +645,15 @@ static int netdev_load_one(Manager *manager, const char *filename) { return 0; } - netdev_raw = new0(NetDev, 1); + netdev_raw = new(NetDev, 1); if (!netdev_raw) return log_oom(); - netdev_raw->n_ref = 1; - netdev_raw->kind = _NETDEV_KIND_INVALID; - netdev_raw->state = _NETDEV_STATE_INVALID; /* an invalid state means done() of the implementation won't be called on destruction */ + *netdev_raw = (NetDev) { + .n_ref = 1, + .kind = _NETDEV_KIND_INVALID, + .state = _NETDEV_STATE_INVALID, /* an invalid state means done() of the implementation won't be called on destruction */ + }; dropin_dirname = strjoina(basename(filename), ".d"); r = config_parse_many(filename, network_dirs, dropin_dirname, @@ -669,10 +697,10 @@ static int netdev_load_one(Manager *manager, const char *filename) { if (NETDEV_VTABLE(netdev)->init) NETDEV_VTABLE(netdev)->init(netdev); - r = config_parse(NULL, filename, file, - NETDEV_VTABLE(netdev)->sections, - config_item_perf_lookup, network_netdev_gperf_lookup, - CONFIG_PARSE_WARN, netdev); + r = config_parse_many(filename, network_dirs, dropin_dirname, + NETDEV_VTABLE(netdev)->sections, + config_item_perf_lookup, network_netdev_gperf_lookup, + CONFIG_PARSE_WARN, netdev); if (r < 0) return r; @@ -693,6 +721,10 @@ static int netdev_load_one(Manager *manager, const char *filename) { return log_error_errno(r, "Failed to generate predictable MAC address for %s: %m", netdev->ifname); } + r = hashmap_ensure_allocated(&netdev->manager->netdevs, &string_hash_ops); + if (r < 0) + return r; + r = hashmap_put(netdev->manager->netdevs, netdev->ifname, netdev); if (r < 0) return r; @@ -758,14 +790,12 @@ static int netdev_load_one(Manager *manager, const char *filename) { int netdev_load(Manager *manager) { _cleanup_strv_free_ char **files = NULL; - NetDev *netdev; char **f; int r; assert(manager); - while ((netdev = hashmap_first(manager->netdevs))) - netdev_unref(netdev); + hashmap_clear_with_destructor(manager->netdevs, netdev_unref); r = conf_files_list_strv(&files, ".netdev", NULL, 0, network_dirs); if (r < 0) diff --git a/src/network/netdev/netdev.h b/src/network/netdev/netdev.h index 8df26c0ac3..bfe1094181 100644 --- a/src/network/netdev/netdev.h +++ b/src/network/netdev/netdev.h @@ -1,17 +1,17 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once - #include "sd-netlink.h" +#include "conf-parser.h" #include "list.h" +#include "../networkd-link.h" #include "time-util.h" typedef struct netdev_join_callback netdev_join_callback; -typedef struct Link Link; struct netdev_join_callback { - sd_netlink_message_handler_t callback; + link_netlink_message_handler_t callback; Link *link; LIST_FIELDS(netdev_join_callback, callbacks); @@ -44,6 +44,8 @@ typedef enum NetDevKind { NETDEV_KIND_VXCAN, NETDEV_KIND_WIREGUARD, NETDEV_KIND_NETDEVSIM, + NETDEV_KIND_FOU, + NETDEV_KIND_ERSPAN, _NETDEV_KIND_MAX, _NETDEV_KIND_INVALID = -1 } NetDevKind; @@ -72,7 +74,7 @@ typedef struct Condition Condition; typedef struct NetDev { Manager *manager; - int n_ref; + unsigned n_ref; char *filename; @@ -146,23 +148,23 @@ extern const NetDevVTable * const netdev_vtable[_NETDEV_KIND_MAX]; #define NETDEV(n) (&(n)->meta) int netdev_load(Manager *manager); +int netdev_load_one(Manager *manager, const char *filename); void netdev_drop(NetDev *netdev); NetDev *netdev_unref(NetDev *netdev); NetDev *netdev_ref(NetDev *netdev); - +DEFINE_TRIVIAL_DESTRUCTOR(netdev_destroy_callback, NetDev, netdev_unref); DEFINE_TRIVIAL_CLEANUP_FUNC(NetDev*, netdev_unref); int netdev_get(Manager *manager, const char *name, NetDev **ret); int netdev_set_ifindex(NetDev *netdev, sd_netlink_message *newlink); -int netdev_enslave(NetDev *netdev, Link *link, sd_netlink_message_handler_t callback); int netdev_get_mac(const char *ifname, struct ether_addr **ret); -int netdev_join(NetDev *netdev, Link *link, sd_netlink_message_handler_t cb); +int netdev_join(NetDev *netdev, Link *link, link_netlink_message_handler_t cb); const char *netdev_kind_to_string(NetDevKind d) _const_; NetDevKind netdev_kind_from_string(const char *d) _pure_; -int config_parse_netdev_kind(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); +CONFIG_PARSER_PROTOTYPE(config_parse_netdev_kind); /* gperf */ const struct ConfigPerfItem* network_netdev_gperf_lookup(const char *key, GPERF_LEN_TYPE length); diff --git a/src/network/netdev/netdevsim.h b/src/network/netdev/netdevsim.h index 7287360f15..d3ed0c0d4f 100644 --- a/src/network/netdev/netdevsim.h +++ b/src/network/netdev/netdevsim.h @@ -1,7 +1,6 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once - typedef struct NetDevSim NetDevSim; #include "netdev/netdev.h" diff --git a/src/network/netdev/tunnel.c b/src/network/netdev/tunnel.c index c15ca74717..684edddb5f 100644 --- a/src/network/netdev/tunnel.c +++ b/src/network/netdev/tunnel.c @@ -6,6 +6,10 @@ #include <linux/if_tunnel.h> #include <linux/ip6_tunnel.h> +#if HAVE_LINUX_FOU_H +#include <linux/fou.h> +#endif + #include "sd-netlink.h" #include "conf-parser.h" @@ -61,6 +65,21 @@ static int netdev_ipip_fill_message_create(NetDev *netdev, Link *link, sd_netlin if (r < 0) return log_netdev_error_errno(netdev, r, "Could not append IFLA_IPTUN_PMTUDISC attribute: %m"); + if (t->fou_tunnel) { + + r = sd_netlink_message_append_u16(m, IFLA_IPTUN_ENCAP_TYPE, t->fou_encap_type); + if (r < 0) + return log_netdev_error_errno(netdev, r, "Could not append IFLA_IPTUN_ENCAP_TYPE attribute: %m"); + + r = sd_netlink_message_append_u16(m, IFLA_IPTUN_ENCAP_SPORT, htobe16(t->encap_src_port)); + if (r < 0) + return log_netdev_error_errno(netdev, r, "Could not append IFLA_IPTUN_ENCAP_SPORT attribute: %m"); + + r = sd_netlink_message_append_u16(m, IFLA_IPTUN_ENCAP_DPORT, htobe16(t->fou_destination_port)); + if (r < 0) + return log_netdev_error_errno(netdev, r, "Could not append IFLA_IPTUN_ENCAP_DPORT attribute: %m"); + } + return r; } @@ -95,6 +114,29 @@ static int netdev_sit_fill_message_create(NetDev *netdev, Link *link, sd_netlink if (r < 0) return log_netdev_error_errno(netdev, r, "Could not append IFLA_IPTUN_PMTUDISC attribute: %m"); + if (t->sixrd_prefixlen > 0) { + r = sd_netlink_message_append_in6_addr(m, IFLA_IPTUN_6RD_PREFIX, &t->sixrd_prefix); + if (r < 0) + return log_netdev_error_errno(netdev, r, "Could not append IFLA_IPTUN_6RD_PREFIX attribute: %m"); + + /* u16 is deliberate here, even though we're passing a netmask that can never be >128. The kernel is + * expecting to receive the prefixlen as a u16. + */ + r = sd_netlink_message_append_u16(m, IFLA_IPTUN_6RD_PREFIXLEN, t->sixrd_prefixlen); + if (r < 0) + return log_netdev_error_errno(netdev, r, "Could not append IFLA_IPTUN_6RD_PREFIXLEN attribute: %m"); + } + + if (t->isatap >= 0) { + uint16_t flags = 0; + + SET_FLAG(flags, SIT_ISATAP, t->isatap); + + r = sd_netlink_message_append_u16(m, IFLA_IPTUN_FLAGS, flags); + if (r < 0) + return log_netdev_error_errno(netdev, r, "Could not append IFLA_IPTUN_FLAGS attribute: %m"); + } + return r; } @@ -142,6 +184,77 @@ static int netdev_gre_fill_message_create(NetDev *netdev, Link *link, sd_netlink return r; } +static int netdev_erspan_fill_message_create(NetDev *netdev, Link *link, sd_netlink_message *m) { + uint32_t ikey = 0; + uint32_t okey = 0; + uint16_t iflags = 0; + uint16_t oflags = 0; + Tunnel *t; + int r; + + assert(netdev); + + t = ERSPAN(netdev); + + assert(t); + assert(IN_SET(t->family, AF_INET, AF_UNSPEC)); + assert(m); + + r = sd_netlink_message_append_u32(m, IFLA_GRE_ERSPAN_INDEX, t->erspan_index); + if (r < 0) + return log_netdev_error_errno(netdev, r, "Could not append IFLA_GRE_ERSPAN_INDEX attribute: %m"); + + if (t->key != 0) { + ikey = okey = htobe32(t->key); + iflags |= GRE_KEY; + oflags |= GRE_KEY; + } + + if (t->ikey != 0) { + ikey = htobe32(t->ikey); + iflags |= GRE_KEY; + } + + if (t->okey != 0) { + okey = htobe32(t->okey); + oflags |= GRE_KEY; + } + + if (t->erspan_sequence > 0) { + iflags |= GRE_SEQ; + oflags |= GRE_SEQ; + } else if (t->erspan_sequence == 0) { + iflags &= ~GRE_SEQ; + oflags &= ~GRE_SEQ; + } + + r = sd_netlink_message_append_u32(m, IFLA_GRE_IKEY, ikey); + if (r < 0) + return log_netdev_error_errno(netdev, r, "Could not append IFLA_GRE_IKEY attribute: %m"); + + r = sd_netlink_message_append_u32(m, IFLA_GRE_OKEY, okey); + if (r < 0) + return log_netdev_error_errno(netdev, r, "Could not append IFLA_GRE_OKEY attribute: %m"); + + r = sd_netlink_message_append_u16(m, IFLA_GRE_IFLAGS, iflags); + if (r < 0) + return log_netdev_error_errno(netdev, r, "Could not append IFLA_GRE_IFLAGS attribute: %m"); + + r = sd_netlink_message_append_u16(m, IFLA_GRE_OFLAGS, oflags); + if (r < 0) + return log_netdev_error_errno(netdev, r, "Could not append IFLA_GRE_OFLAGS, attribute: %m"); + + r = sd_netlink_message_append_in_addr(m, IFLA_GRE_LOCAL, &t->local.in); + if (r < 0) + return log_netdev_error_errno(netdev, r, "Could not append IFLA_GRE_LOCAL attribute: %m"); + + r = sd_netlink_message_append_in_addr(m, IFLA_GRE_REMOTE, &t->remote.in); + if (r < 0) + log_netdev_error_errno(netdev, r, "Could not append IFLA_GRE_REMOTE attribute: %m"); + + return r; +} + static int netdev_ip6gre_fill_message_create(NetDev *netdev, Link *link, sd_netlink_message *m) { Tunnel *t; int r; @@ -384,6 +497,9 @@ static int netdev_tunnel_verify(NetDev *netdev, const char *filename) { case NETDEV_KIND_IP6TNL: t = IP6TNL(netdev); break; + case NETDEV_KIND_ERSPAN: + t = ERSPAN(netdev); + break; default: assert_not_reached("Invalid tunnel kind"); } @@ -396,17 +512,17 @@ static int netdev_tunnel_verify(NetDev *netdev, const char *filename) { return -EINVAL; } - if (IN_SET(netdev->kind, NETDEV_KIND_VTI, NETDEV_KIND_IPIP, NETDEV_KIND_GRE, NETDEV_KIND_GRETAP) && + if (IN_SET(netdev->kind, NETDEV_KIND_VTI, NETDEV_KIND_IPIP, NETDEV_KIND_SIT, NETDEV_KIND_GRE, NETDEV_KIND_GRETAP, NETDEV_KIND_ERSPAN) && (t->family != AF_INET || in_addr_is_null(t->family, &t->local))) { log_netdev_error(netdev, - "vti/ipip/gre/gretap tunnel without a local IPv4 address configured in %s. Ignoring", filename); + "vti/ipip/sit/gre/gretap/erspan tunnel without a local IPv4 address configured in %s. Ignoring", filename); return -EINVAL; } - if (IN_SET(netdev->kind, NETDEV_KIND_VTI6, NETDEV_KIND_IP6TNL, NETDEV_KIND_IP6GRE) && + if (IN_SET(netdev->kind, NETDEV_KIND_VTI6, NETDEV_KIND_IP6TNL, NETDEV_KIND_IP6GRE, NETDEV_KIND_IP6GRETAP) && (t->family != AF_INET6 || in_addr_is_null(t->family, &t->local))) { log_netdev_error(netdev, - "vti6/ip6tnl/ip6gre tunnel without a local IPv6 address configured in %s. Ignoring", filename); + "vti6/ip6tnl/ip6gre/ip6gretap tunnel without a local IPv6 address configured in %s. Ignoring", filename); return -EINVAL; } @@ -417,6 +533,16 @@ static int netdev_tunnel_verify(NetDev *netdev, const char *filename) { return -EINVAL; } + if (t->fou_tunnel && t->fou_destination_port <= 0) { + log_netdev_error(netdev, "FooOverUDP missing port configured in %s. Ignoring", filename); + return -EINVAL; + } + + if (netdev->kind == NETDEV_KIND_ERSPAN && (t->erspan_index >= (1 << 20) || t->erspan_index == 0)) { + log_netdev_error(netdev, "Invalid erspan index %d. Ignoring", t->erspan_index); + return -EINVAL; + } + return 0; } @@ -593,6 +719,42 @@ int config_parse_encap_limit(const char* unit, return 0; } +int config_parse_6rd_prefix(const char* unit, + const char *filename, + unsigned line, + const char *section, + unsigned section_line, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata) { + Tunnel *t = userdata; + + assert(filename); + assert(lvalue); + assert(rvalue); + + union in_addr_union p; + uint8_t l; + int r; + + r = in_addr_prefix_from_string(rvalue, AF_INET6, &p, &l); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse 6rd prefix \"%s\", ignoring: %m", rvalue); + return 0; + } + if (l == 0) { + log_syntax(unit, LOG_ERR, filename, line, 0, "6rd prefix length of \"%s\" must be greater than zero, ignoring", rvalue); + return 0; + } + + t->sixrd_prefix = p.in6; + t->sixrd_prefixlen = l; + + return 0; +} + static void ipip_init(NetDev *n) { Tunnel *t = IPIP(n); @@ -600,6 +762,7 @@ static void ipip_init(NetDev *n) { assert(t); t->pmtudisc = true; + t->fou_encap_type = FOU_ENCAP_DIRECT; } static void sit_init(NetDev *n) { @@ -609,6 +772,7 @@ static void sit_init(NetDev *n) { assert(t); t->pmtudisc = true; + t->isatap = -1; } static void vti_init(NetDev *n) { @@ -656,6 +820,18 @@ static void ip6gre_init(NetDev *n) { t->ttl = DEFAULT_TNL_HOP_LIMIT; } +static void erspan_init(NetDev *n) { + Tunnel *t; + + assert(n); + + t = ERSPAN(n); + + assert(t); + + t->erspan_sequence = -1; +} + static void ip6tnl_init(NetDev *n) { Tunnel *t = IP6TNL(n); @@ -749,3 +925,12 @@ const NetDevVTable ip6tnl_vtable = { .create_type = NETDEV_CREATE_STACKED, .config_verify = netdev_tunnel_verify, }; + +const NetDevVTable erspan_vtable = { + .object_size = sizeof(Tunnel), + .init = erspan_init, + .sections = "Match\0NetDev\0Tunnel\0", + .fill_message_create = netdev_erspan_fill_message_create, + .create_type = NETDEV_CREATE_INDEPENDENT, + .config_verify = netdev_tunnel_verify, +}; diff --git a/src/network/netdev/tunnel.h b/src/network/netdev/tunnel.h index b54b58ddee..8f511dd1f6 100644 --- a/src/network/netdev/tunnel.h +++ b/src/network/netdev/tunnel.h @@ -1,10 +1,11 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once - #include "in-addr-util.h" +#include "conf-parser.h" #include "netdev/netdev.h" +#include "netdev/fou-tunnel.h" typedef enum Ip6TnlMode { NETDEV_IP6_TNL_MODE_IP6IP6, @@ -28,6 +29,8 @@ typedef struct Tunnel { int family; int ipv6_flowlabel; int allow_localremote; + int erspan_sequence; + int isatap; unsigned ttl; unsigned tos; @@ -36,15 +39,24 @@ typedef struct Tunnel { uint32_t key; uint32_t ikey; uint32_t okey; + uint32_t erspan_index; union in_addr_union local; union in_addr_union remote; Ip6TnlMode ip6tnl_mode; + FooOverUDPEncapType fou_encap_type; bool pmtudisc; bool copy_dscp; bool independent; + bool fou_tunnel; + + uint16_t encap_src_port; + uint16_t fou_destination_port; + + struct in6_addr sixrd_prefix; + uint8_t sixrd_prefixlen; } Tunnel; DEFINE_NETDEV_CAST(IPIP, Tunnel); @@ -56,6 +68,7 @@ DEFINE_NETDEV_CAST(SIT, Tunnel); DEFINE_NETDEV_CAST(VTI, Tunnel); DEFINE_NETDEV_CAST(VTI6, Tunnel); DEFINE_NETDEV_CAST(IP6TNL, Tunnel); +DEFINE_NETDEV_CAST(ERSPAN, Tunnel); extern const NetDevVTable ipip_vtable; extern const NetDevVTable sit_vtable; extern const NetDevVTable vti_vtable; @@ -65,40 +78,14 @@ extern const NetDevVTable gretap_vtable; extern const NetDevVTable ip6gre_vtable; extern const NetDevVTable ip6gretap_vtable; extern const NetDevVTable ip6tnl_vtable; +extern const NetDevVTable erspan_vtable; const char *ip6tnl_mode_to_string(Ip6TnlMode d) _const_; Ip6TnlMode ip6tnl_mode_from_string(const char *d) _pure_; -int config_parse_ip6tnl_mode(const char *unit, const char *filename, - unsigned line, const char *section, - unsigned section_line, const char *lvalue, - int ltype, const char *rvalue, void *data, - void *userdata); - -int config_parse_tunnel_address(const char *unit, - const char *filename, - unsigned line, - const char *section, - unsigned section_line, - const char *lvalue, - int ltype, - const char *rvalue, - void *data, - void *userdata); - -int config_parse_ipv6_flowlabel(const char *unit, const char *filename, - unsigned line, const char *section, - unsigned section_line, const char *lvalue, - int ltype, const char *rvalue, void *data, - void *userdata); - -int config_parse_encap_limit(const char *unit, const char *filename, - unsigned line, const char *section, - unsigned section_line, const char *lvalue, - int ltype, const char *rvalue, void *data, - void *userdata); -int config_parse_tunnel_key(const char *unit, const char *filename, - unsigned line, const char *section, - unsigned section_line, const char *lvalue, - int ltype, const char *rvalue, void *data, - void *userdata); +CONFIG_PARSER_PROTOTYPE(config_parse_ip6tnl_mode); +CONFIG_PARSER_PROTOTYPE(config_parse_tunnel_address); +CONFIG_PARSER_PROTOTYPE(config_parse_ipv6_flowlabel); +CONFIG_PARSER_PROTOTYPE(config_parse_encap_limit); +CONFIG_PARSER_PROTOTYPE(config_parse_tunnel_key); +CONFIG_PARSER_PROTOTYPE(config_parse_6rd_prefix); diff --git a/src/network/netdev/tuntap.c b/src/network/netdev/tuntap.c index 21fb7ab067..951138d257 100644 --- a/src/network/netdev/tuntap.c +++ b/src/network/netdev/tuntap.c @@ -60,12 +60,11 @@ static int netdev_tuntap_add(NetDev *netdev, struct ifreq *ifr) { assert(netdev); assert(ifr); - fd = open(TUN_DEV, O_RDWR); + fd = open(TUN_DEV, O_RDWR|O_CLOEXEC); if (fd < 0) return log_netdev_error_errno(netdev, -errno, "Failed to open tun dev: %m"); - r = ioctl(fd, TUNSETIFF, ifr); - if (r < 0) + if (ioctl(fd, TUNSETIFF, ifr) < 0) return log_netdev_error_errno(netdev, -errno, "TUNSETIFF failed on tun dev: %m"); if (netdev->kind == NETDEV_KIND_TAP) @@ -76,34 +75,29 @@ static int netdev_tuntap_add(NetDev *netdev, struct ifreq *ifr) { assert(t); if (t->user_name) { - user = t->user_name; - r = get_user_creds(&user, &uid, NULL, NULL, NULL); + r = get_user_creds(&user, &uid, NULL, NULL, NULL, USER_CREDS_ALLOW_MISSING); if (r < 0) return log_netdev_error_errno(netdev, r, "Cannot resolve user name %s: %m", t->user_name); - r = ioctl(fd, TUNSETOWNER, uid); - if (r < 0) + if (ioctl(fd, TUNSETOWNER, uid) < 0) return log_netdev_error_errno(netdev, -errno, "TUNSETOWNER failed on tun dev: %m"); } if (t->group_name) { - group = t->group_name; - r = get_group_creds(&group, &gid); + r = get_group_creds(&group, &gid, USER_CREDS_ALLOW_MISSING); if (r < 0) return log_netdev_error_errno(netdev, r, "Cannot resolve group name %s: %m", t->group_name); - r = ioctl(fd, TUNSETGROUP, gid); - if (r < 0) + if (ioctl(fd, TUNSETGROUP, gid) < 0) return log_netdev_error_errno(netdev, -errno, "TUNSETGROUP failed on tun dev: %m"); } - r = ioctl(fd, TUNSETPERSIST, 1); - if (r < 0) + if (ioctl(fd, TUNSETPERSIST, 1) < 0) return log_netdev_error_errno(netdev, -errno, "TUNSETPERSIST failed on tun dev: %m"); return 0; diff --git a/src/network/netdev/tuntap.h b/src/network/netdev/tuntap.h index 6e8c66a000..9d9f9922b5 100644 --- a/src/network/netdev/tuntap.h +++ b/src/network/netdev/tuntap.h @@ -1,7 +1,6 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once - typedef struct TunTap TunTap; #include "netdev/netdev.h" diff --git a/src/network/netdev/vcan.c b/src/network/netdev/vcan.c index f06ad0fb42..574d1cad31 100644 --- a/src/network/netdev/vcan.c +++ b/src/network/netdev/vcan.c @@ -4,5 +4,6 @@ const NetDevVTable vcan_vtable = { .object_size = sizeof(VCan), + .sections = "Match\0NetDev\0", .create_type = NETDEV_CREATE_INDEPENDENT, }; diff --git a/src/network/netdev/vcan.h b/src/network/netdev/vcan.h index d2540645c8..6f62686d08 100644 --- a/src/network/netdev/vcan.h +++ b/src/network/netdev/vcan.h @@ -1,7 +1,6 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once - typedef struct VCan VCan; #include <linux/can/netlink.h> diff --git a/src/network/netdev/veth.h b/src/network/netdev/veth.h index 29cddab573..0bb9947bbd 100644 --- a/src/network/netdev/veth.h +++ b/src/network/netdev/veth.h @@ -1,7 +1,6 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once - typedef struct Veth Veth; #include "netdev/netdev.h" diff --git a/src/network/netdev/vlan.h b/src/network/netdev/vlan.h index f3629f903b..b815e03dc5 100644 --- a/src/network/netdev/vlan.h +++ b/src/network/netdev/vlan.h @@ -1,7 +1,6 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once - typedef struct VLan VLan; #include "netdev/netdev.h" diff --git a/src/network/netdev/vrf.c b/src/network/netdev/vrf.c index f341061344..5efab537cc 100644 --- a/src/network/netdev/vrf.c +++ b/src/network/netdev/vrf.c @@ -1,7 +1,4 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ -/*** - Copyright © 2016 Andreas Rammhold <andreas@rammhold.de> -***/ #include <net/if.h> @@ -30,7 +27,7 @@ static int netdev_vrf_fill_message_create(NetDev *netdev, Link *link, sd_netlink const NetDevVTable vrf_vtable = { .object_size = sizeof(Vrf), - .sections = "NetDev\0VRF\0", + .sections = "Match\0NetDev\0VRF\0", .fill_message_create = netdev_vrf_fill_message_create, .create_type = NETDEV_CREATE_MASTER, }; diff --git a/src/network/netdev/vrf.h b/src/network/netdev/vrf.h index d1ac6f6976..05b3937856 100644 --- a/src/network/netdev/vrf.h +++ b/src/network/netdev/vrf.h @@ -1,10 +1,6 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once -/*** - Copyright © 2016 Andreas Rammhold <andreas@rammhold.de> -***/ - typedef struct Vrf Vrf; #include "netdev/netdev.h" diff --git a/src/network/netdev/vxcan.c b/src/network/netdev/vxcan.c index f8139b52c3..e8ea70a1ed 100644 --- a/src/network/netdev/vxcan.c +++ b/src/network/netdev/vxcan.c @@ -1,7 +1,11 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ -#include "netdev/vxcan.h" +#if HAVE_LINUX_CAN_VXCAN_H +#include <linux/can/vxcan.h> +#endif + #include "missing.h" +#include "netdev/vxcan.h" static int netdev_vxcan_fill_message_create(NetDev *netdev, Link *link, sd_netlink_message *m) { VxCan *v; diff --git a/src/network/netdev/vxcan.h b/src/network/netdev/vxcan.h index 84a39ee3cb..b5de19740f 100644 --- a/src/network/netdev/vxcan.h +++ b/src/network/netdev/vxcan.h @@ -1,13 +1,8 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once - typedef struct VxCan VxCan; -#if HAVE_VXCAN_INFO_PEER -#include <linux/can/vxcan.h> -#endif - #include "netdev/netdev.h" struct VxCan { diff --git a/src/network/netdev/vxlan.c b/src/network/netdev/vxlan.c index e23dd13648..4cb2eca3d2 100644 --- a/src/network/netdev/vxlan.c +++ b/src/network/netdev/vxlan.c @@ -42,7 +42,6 @@ static int netdev_vxlan_fill_message_create(NetDev *netdev, Link *link, sd_netli if (r < 0) return log_netdev_error_errno(netdev, r, "Could not append IFLA_VXLAN_GROUP attribute: %m"); - } if (!in_addr_is_null(v->local_family, &v->local)) { @@ -54,20 +53,19 @@ static int netdev_vxlan_fill_message_create(NetDev *netdev, Link *link, sd_netli if (r < 0) return log_netdev_error_errno(netdev, r, "Could not append IFLA_VXLAN_LOCAL attribute: %m"); - } r = sd_netlink_message_append_u32(m, IFLA_VXLAN_LINK, link->ifindex); if (r < 0) return log_netdev_error_errno(netdev, r, "Could not append IFLA_VXLAN_LINK attribute: %m"); - if (v->ttl) { + if (v->ttl != 0) { r = sd_netlink_message_append_u8(m, IFLA_VXLAN_TTL, v->ttl); if (r < 0) return log_netdev_error_errno(netdev, r, "Could not append IFLA_VXLAN_TTL attribute: %m"); } - if (v->tos) { + if (v->tos != 0) { r = sd_netlink_message_append_u8(m, IFLA_VXLAN_TOS, v->tos); if (r < 0) return log_netdev_error_errno(netdev, r, "Could not append IFLA_VXLAN_TOS attribute: %m"); @@ -93,13 +91,13 @@ static int netdev_vxlan_fill_message_create(NetDev *netdev, Link *link, sd_netli if (r < 0) return log_netdev_error_errno(netdev, r, "Could not append IFLA_VXLAN_L3MISS attribute: %m"); - if (v->fdb_ageing) { + if (v->fdb_ageing != 0) { r = sd_netlink_message_append_u32(m, IFLA_VXLAN_AGEING, v->fdb_ageing / USEC_PER_SEC); if (r < 0) return log_netdev_error_errno(netdev, r, "Could not append IFLA_VXLAN_AGEING attribute: %m"); } - if (v->max_fdb) { + if (v->max_fdb != 0) { r = sd_netlink_message_append_u32(m, IFLA_VXLAN_LIMIT, v->max_fdb); if (r < 0) return log_netdev_error_errno(netdev, r, "Could not append IFLA_VXLAN_LIMIT attribute: %m"); @@ -129,7 +127,7 @@ static int netdev_vxlan_fill_message_create(NetDev *netdev, Link *link, sd_netli if (r < 0) return log_netdev_error_errno(netdev, r, "Could not append IFLA_VXLAN_PORT attribute: %m"); - if (v->port_range.low || v->port_range.high) { + if (v->port_range.low != 0 || v->port_range.high != 0) { struct ifla_vxlan_port_range port_range; port_range.low = htobe16(v->port_range.low); @@ -214,9 +212,8 @@ int config_parse_port_range(const char *unit, const char *rvalue, void *data, void *userdata) { - _cleanup_free_ char *word = NULL; VxLan *v = userdata; - unsigned low, high; + uint16_t low, high; int r; assert(filename); @@ -224,30 +221,10 @@ int config_parse_port_range(const char *unit, assert(rvalue); assert(data); - r = extract_first_word(&rvalue, &word, NULL, 0); - if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, "Failed to extract VXLAN port range, ignoring: %s", rvalue); - return 0; - } - - if (r == 0) - return 0; - - r = parse_range(word, &low, &high); + r = parse_ip_port_range(rvalue, &low, &high); if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse VXLAN port range '%s'", word); - return 0; - } - - if (low <= 0 || low > 65535 || high <= 0 || high > 65535) { - log_syntax(unit, LOG_ERR, filename, line, r, - "Failed to parse VXLAN port range '%s'. Port should be greater than 0 and less than 65535.", word); - return 0; - } - - if (high < low) { log_syntax(unit, LOG_ERR, filename, line, r, - "Failed to parse VXLAN port range '%s'. Port range %u .. %u not valid", word, low, high); + "Failed to parse VXLAN port range '%s'. Port should be greater than 0 and less than 65535.", rvalue); return 0; } diff --git a/src/network/netdev/vxlan.h b/src/network/netdev/vxlan.h index e5eb20f187..3b273e97bd 100644 --- a/src/network/netdev/vxlan.h +++ b/src/network/netdev/vxlan.h @@ -1,7 +1,6 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once - typedef struct VxLan VxLan; #include "in-addr-util.h" @@ -48,34 +47,6 @@ struct VxLan { DEFINE_NETDEV_CAST(VXLAN, VxLan); extern const NetDevVTable vxlan_vtable; -int config_parse_vxlan_address(const char *unit, - const char *filename, - unsigned line, - const char *section, - unsigned section_line, - const char *lvalue, - int ltype, - const char *rvalue, - void *data, - void *userdata); -int config_parse_port_range(const char *unit, - const char *filename, - unsigned line, - const char *section, - unsigned section_line, - const char *lvalue, - int ltype, - const char *rvalue, - void *data, - void *userdata); - -int config_parse_flow_label(const char *unit, - const char *filename, - unsigned line, - const char *section, - unsigned section_line, - const char *lvalue, - int ltype, - const char *rvalue, - void *data, - void *userdata); +CONFIG_PARSER_PROTOTYPE(config_parse_vxlan_address); +CONFIG_PARSER_PROTOTYPE(config_parse_port_range); +CONFIG_PARSER_PROTOTYPE(config_parse_flow_label); diff --git a/src/network/netdev/wireguard.c b/src/network/netdev/wireguard.c index fb91997f7a..167cf65046 100644 --- a/src/network/netdev/wireguard.c +++ b/src/network/netdev/wireguard.c @@ -1,22 +1,24 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ /*** - Copyright © 2016-2017 Jörg Thalheim <joerg@thalheim.io> Copyright © 2015-2017 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved. ***/ #include <sys/ioctl.h> #include <net/if.h> +#include "sd-resolve.h" + #include "alloc-util.h" -#include "parse-util.h" #include "fd-util.h" -#include "strv.h" #include "hexdecoct.h" -#include "string-util.h" -#include "wireguard.h" #include "networkd-link.h" -#include "networkd-util.h" #include "networkd-manager.h" +#include "networkd-util.h" +#include "parse-util.h" +#include "resolve-private.h" +#include "string-util.h" +#include "strv.h" +#include "wireguard.h" #include "wireguard-netlink.h" static void resolve_endpoints(NetDev *netdev); @@ -29,10 +31,13 @@ static WireguardPeer *wireguard_peer_new(Wireguard *w, unsigned section) { if (w->last_peer_section == section && w->peers) return w->peers; - peer = new0(WireguardPeer, 1); + peer = new(WireguardPeer, 1); if (!peer) return NULL; - peer->flags = WGPEER_F_REPLACE_ALLOWEDIPS; + + *peer = (WireguardPeer) { + .flags = WGPEER_F_REPLACE_ALLOWEDIPS, + }; LIST_PREPEND(peers, w->peers, peer); w->last_peer_section = section; @@ -42,7 +47,7 @@ static WireguardPeer *wireguard_peer_new(Wireguard *w, unsigned section) { static int set_wireguard_interface(NetDev *netdev) { int r; - unsigned int i, j; + unsigned i, j; WireguardPeer *peer, *peer_start; WireguardIPmask *mask, *mask_start = NULL; _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *message = NULL; @@ -108,7 +113,7 @@ static int set_wireguard_interface(NetDev *netdev) { if (r < 0) break; - r = sd_netlink_message_append_u32(message, WGPEER_A_PERSISTENT_KEEPALIVE_INTERVAL, peer->persistent_keepalive_interval); + r = sd_netlink_message_append_u16(message, WGPEER_A_PERSISTENT_KEEPALIVE_INTERVAL, peer->persistent_keepalive_interval); if (r < 0) break; @@ -196,12 +201,19 @@ static int set_wireguard_interface(NetDev *netdev) { static WireguardEndpoint* wireguard_endpoint_free(WireguardEndpoint *e) { if (!e) return NULL; - netdev_unref(e->netdev); e->host = mfree(e->host); e->port = mfree(e->port); return mfree(e); } +static void wireguard_endpoint_destroy_callback(WireguardEndpoint *e) { + assert(e); + assert(e->netdev); + + netdev_unref(e->netdev); + wireguard_endpoint_free(e); +} + DEFINE_TRIVIAL_CLEANUP_FUNC(WireguardEndpoint*, wireguard_endpoint_free); static int on_resolve_retry(sd_event_source *s, usec_t usec, void *userdata) { @@ -212,8 +224,11 @@ static int on_resolve_retry(sd_event_source *s, usec_t usec, void *userdata) { w = WIREGUARD(netdev); assert(w); - w->resolve_retry_event_source = sd_event_source_unref(w->resolve_retry_event_source); + if (!netdev->manager) + /* The netdev is detached. */ + return 0; + assert(!w->unresolved_endpoints); w->unresolved_endpoints = TAKE_PTR(w->failed_endpoints); resolve_endpoints(netdev); @@ -232,28 +247,30 @@ static int exponential_backoff_milliseconds(unsigned n_retries) { static int wireguard_resolve_handler(sd_resolve_query *q, int ret, const struct addrinfo *ai, - void *userdata) { + WireguardEndpoint *e) { + _cleanup_(netdev_unrefp) NetDev *netdev_will_unrefed = NULL; NetDev *netdev; Wireguard *w; - _cleanup_(wireguard_endpoint_freep) WireguardEndpoint *e; int r; - assert(userdata); - e = userdata; - netdev = e->netdev; + assert(e); + assert(e->netdev); - assert(netdev); + netdev = e->netdev; w = WIREGUARD(netdev); assert(w); - w->resolve_query = sd_resolve_query_unref(w->resolve_query); + if (!netdev->manager) + /* The netdev is detached. */ + return 0; if (ret != 0) { log_netdev_error(netdev, "Failed to resolve host '%s:%s': %s", e->host, e->port, gai_strerror(ret)); LIST_PREPEND(endpoints, w->failed_endpoints, e); - e = NULL; + (void) sd_resolve_query_set_destroy_callback(q, NULL); /* Avoid freeing endpoint by destroy callback. */ + netdev_will_unrefed = netdev; /* But netdev needs to be unrefed. */ } else if ((ai->ai_family == AF_INET && ai->ai_addrlen == sizeof(struct sockaddr_in)) || - (ai->ai_family == AF_INET6 && ai->ai_addrlen == sizeof(struct sockaddr_in6))) + (ai->ai_family == AF_INET6 && ai->ai_addrlen == sizeof(struct sockaddr_in6))) memcpy(&e->peer->endpoint, ai->ai_addr, ai->ai_addrlen); else log_netdev_error(netdev, "Neither IPv4 nor IPv6 address found for peer endpoint: %s:%s", e->host, e->port); @@ -265,51 +282,69 @@ static int wireguard_resolve_handler(sd_resolve_query *q, set_wireguard_interface(netdev); if (w->failed_endpoints) { + _cleanup_(sd_event_source_unrefp) sd_event_source *s = NULL; + w->n_retries++; r = sd_event_add_time(netdev->manager->event, - &w->resolve_retry_event_source, + &s, CLOCK_MONOTONIC, now(CLOCK_MONOTONIC) + exponential_backoff_milliseconds(w->n_retries), 0, on_resolve_retry, netdev); - if (r < 0) + if (r < 0) { log_netdev_warning_errno(netdev, r, "Could not arm resolve retry handler: %m"); + return 0; + } + + r = sd_event_source_set_destroy_callback(s, (sd_event_destroy_t) netdev_destroy_callback); + if (r < 0) { + log_netdev_warning_errno(netdev, r, "Failed to set destroy callback to event source: %m"); + return 0; + } + + (void) sd_event_source_set_floating(s, true); + netdev_ref(netdev); } return 0; } static void resolve_endpoints(NetDev *netdev) { - int r = 0; - Wireguard *w; - WireguardEndpoint *endpoint; static const struct addrinfo hints = { .ai_family = AF_UNSPEC, .ai_socktype = SOCK_DGRAM, .ai_protocol = IPPROTO_UDP }; + WireguardEndpoint *endpoint; + Wireguard *w; + int r = 0; assert(netdev); w = WIREGUARD(netdev); assert(w); LIST_FOREACH(endpoints, endpoint, w->unresolved_endpoints) { - r = sd_resolve_getaddrinfo(netdev->manager->resolve, - &w->resolve_query, - endpoint->host, - endpoint->port, - &hints, - wireguard_resolve_handler, - endpoint); + r = resolve_getaddrinfo(netdev->manager->resolve, + NULL, + endpoint->host, + endpoint->port, + &hints, + wireguard_resolve_handler, + wireguard_endpoint_destroy_callback, + endpoint); if (r == -ENOBUFS) break; + if (r < 0) { + log_netdev_error_errno(netdev, r, "Failed to create resolver: %m"); + continue; + } - LIST_REMOVE(endpoints, w->unresolved_endpoints, endpoint); + /* Avoid freeing netdev. It will be unrefed by the destroy callback. */ + netdev_ref(netdev); - if (r < 0) - log_netdev_error_errno(netdev, r, "Failed create resolver: %m"); + LIST_REMOVE(endpoints, w->unresolved_endpoints, endpoint); } } @@ -532,12 +567,15 @@ int config_parse_wireguard_allowed_ips(const char *unit, return 0; } - ipmask = new0(WireguardIPmask, 1); + ipmask = new(WireguardIPmask, 1); if (!ipmask) return log_oom(); - ipmask->family = family; - ipmask->ip.in6 = addr.in6; - ipmask->cidr = prefixlen; + + *ipmask = (WireguardIPmask) { + .family = family, + .ip.in6 = addr.in6, + .cidr = prefixlen, + }; LIST_PREPEND(ipmasks, peer->ipmasks, ipmask); } @@ -573,10 +611,6 @@ int config_parse_wireguard_endpoint(const char *unit, if (!peer) return log_oom(); - endpoint = new0(WireguardEndpoint, 1); - if (!endpoint) - return log_oom(); - if (rvalue[0] == '[') { begin = &rvalue[1]; end = strchr(rvalue, ']'); @@ -610,12 +644,17 @@ int config_parse_wireguard_endpoint(const char *unit, if (!port) return log_oom(); - endpoint->peer = TAKE_PTR(peer); - endpoint->host = TAKE_PTR(host); - endpoint->port = TAKE_PTR(port); - endpoint->netdev = netdev_ref(data); - LIST_PREPEND(endpoints, w->unresolved_endpoints, endpoint); - endpoint = NULL; + endpoint = new(WireguardEndpoint, 1); + if (!endpoint) + return log_oom(); + + *endpoint = (WireguardEndpoint) { + .peer = TAKE_PTR(peer), + .host = TAKE_PTR(host), + .port = TAKE_PTR(port), + .netdev = data, + }; + LIST_PREPEND(endpoints, w->unresolved_endpoints, TAKE_PTR(endpoint)); return 0; } @@ -674,11 +713,11 @@ static void wireguard_done(NetDev *netdev) { Wireguard *w; WireguardPeer *peer; WireguardIPmask *mask; + WireguardEndpoint *e; assert(netdev); w = WIREGUARD(netdev); - assert(!w->unresolved_endpoints); - w->resolve_retry_event_source = sd_event_source_unref(w->resolve_retry_event_source); + assert(w); while ((peer = w->peers)) { LIST_REMOVE(peers, w->peers, peer); @@ -688,6 +727,16 @@ static void wireguard_done(NetDev *netdev) { } free(peer); } + + while ((e = w->unresolved_endpoints)) { + LIST_REMOVE(endpoints, w->unresolved_endpoints, e); + wireguard_endpoint_free(e); + } + + while ((e = w->failed_endpoints)) { + LIST_REMOVE(endpoints, w->failed_endpoints, e); + wireguard_endpoint_free(e); + } } const NetDevVTable wireguard_vtable = { diff --git a/src/network/netdev/wireguard.h b/src/network/netdev/wireguard.h index 6fdbf09a7e..143e93cdc5 100644 --- a/src/network/netdev/wireguard.h +++ b/src/network/netdev/wireguard.h @@ -1,16 +1,11 @@ #pragma once -/*** - Copyright © 2016 Jörg Thalheim <joerg@thalheim.io> -***/ - typedef struct Wireguard Wireguard; +#include "in-addr-util.h" #include "netdev.h" -#include "sd-resolve.h" -#include "wireguard-netlink.h" #include "socket-util.h" -#include "in-addr-util.h" +#include "wireguard-netlink.h" #ifndef IFNAMSIZ #define IFNAMSIZ 16 @@ -51,33 +46,28 @@ struct Wireguard { NetDev meta; unsigned last_peer_section; - char interface[IFNAMSIZ]; uint32_t flags; - uint8_t public_key[WG_KEY_LEN]; uint8_t private_key[WG_KEY_LEN]; uint32_t fwmark; uint16_t port; LIST_HEAD(WireguardPeer, peers); - size_t allocation_size; - sd_event_source *resolve_retry_event_source; LIST_HEAD(WireguardEndpoint, unresolved_endpoints); LIST_HEAD(WireguardEndpoint, failed_endpoints); unsigned n_retries; - sd_resolve_query *resolve_query; }; DEFINE_NETDEV_CAST(WIREGUARD, Wireguard); extern const NetDevVTable wireguard_vtable; -int config_parse_wireguard_allowed_ips(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); -int config_parse_wireguard_endpoint(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); -int config_parse_wireguard_listen_port(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); +CONFIG_PARSER_PROTOTYPE(config_parse_wireguard_allowed_ips); +CONFIG_PARSER_PROTOTYPE(config_parse_wireguard_endpoint); +CONFIG_PARSER_PROTOTYPE(config_parse_wireguard_listen_port); -int config_parse_wireguard_public_key(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); -int config_parse_wireguard_private_key(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); -int config_parse_wireguard_preshared_key(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); -int config_parse_wireguard_keepalive(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); +CONFIG_PARSER_PROTOTYPE(config_parse_wireguard_public_key); +CONFIG_PARSER_PROTOTYPE(config_parse_wireguard_private_key); +CONFIG_PARSER_PROTOTYPE(config_parse_wireguard_preshared_key); +CONFIG_PARSER_PROTOTYPE(config_parse_wireguard_keepalive); |