network: add support for HSR netdev

Add support for creating HSR/PRP interfaces. HSR (High-availability Seamless
Redundancy) and PRP (Parallel Redundancy Protocol) are two protocols that
provide seamless failover against failure of any single network component. They
are both implemented by the "hsr" kernel driver.
This commit is contained in:
Beniamino Galvani
2025-04-05 05:23:53 +09:00
committed by Yu Watanabe
parent c1b0e39ffd
commit f7996e2a33
11 changed files with 276 additions and 1 deletions
@@ -239,6 +239,13 @@ static const NLAPolicy rtnl_link_info_data_gre_policies[] = {
[IFLA_GRE_ERSPAN_HWID] = BUILD_POLICY(U16),
};
static const NLAPolicy rtnl_link_info_data_hsr_policies[] = {
[IFLA_HSR_SLAVE1] = BUILD_POLICY(U32),
[IFLA_HSR_SLAVE2] = BUILD_POLICY(U32),
[IFLA_HSR_MULTICAST_SPEC] = BUILD_POLICY(U8),
[IFLA_HSR_PROTOCOL] = BUILD_POLICY(U8),
};
static const NLAPolicy rtnl_link_info_data_ipoib_policies[] = {
[IFLA_IPOIB_PKEY] = BUILD_POLICY(U16),
[IFLA_IPOIB_MODE] = BUILD_POLICY(U16),
@@ -413,8 +420,8 @@ static const NLAPolicySetUnionElement rtnl_link_info_data_policy_set_union_eleme
BUILD_UNION_ELEMENT_BY_STRING("gretap", rtnl_link_info_data_gre),
/*
BUILD_UNION_ELEMENT_BY_STRING("gtp", rtnl_link_info_data_gtp),
BUILD_UNION_ELEMENT_BY_STRING("hsr", rtnl_link_info_data_hsr),
*/
BUILD_UNION_ELEMENT_BY_STRING("hsr", rtnl_link_info_data_hsr),
BUILD_UNION_ELEMENT_BY_STRING("ip6erspan", rtnl_link_info_data_gre),
BUILD_UNION_ELEMENT_BY_STRING("ip6gre", rtnl_link_info_data_gre),
BUILD_UNION_ELEMENT_BY_STRING("ip6gretap", rtnl_link_info_data_gre),
+1
View File
@@ -10,6 +10,7 @@ sources = files(
'netdev/dummy.c',
'netdev/fou-tunnel.c',
'netdev/geneve.c',
'netdev/hsr.c',
'netdev/ifb.c',
'netdev/ipoib.c',
'netdev/ipvlan.c',
+127
View File
@@ -0,0 +1,127 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
/* Make sure the net/if.h header is included before any linux/ one */
#include <net/if.h>
#include <linux/if_arp.h>
#include <netinet/in.h>
#include "hsr.h"
#include "netlink-util.h"
#include "networkd-manager.h"
#include "string-table.h"
#include "string-util.h"
#include "strv.h"
static const char * const hsr_protocol_table[_NETDEV_HSR_PROTOCOL_MAX] = {
[NETDEV_HSR_PROTOCOL_HSR] = "hsr",
[NETDEV_HSR_PROTOCOL_PRP] = "prp",
};
DEFINE_STRING_TABLE_LOOKUP_FROM_STRING(hsr_protocol, HsrProtocol);
DEFINE_CONFIG_PARSE_ENUM(config_parse_hsr_protocol, hsr_protocol, HsrProtocol);
static int hsr_get_port_links(NetDev *netdev, Link **ret1, Link **ret2) {
Hsr *h = ASSERT_PTR(HSR(netdev));
Link *link1, *link2;
int r;
r = link_get_by_name(netdev->manager, h->ports[0], &link1);
if (r < 0)
return r;
r = link_get_by_name(netdev->manager, h->ports[1], &link2);
if (r < 0)
return r;
if (ret1)
*ret1 = link1;
if (ret2)
*ret2 = link2;
return 0;
}
static int netdev_hsr_fill_message_create(NetDev *netdev, Link *link, sd_netlink_message *m) {
Hsr *h = ASSERT_PTR(HSR(netdev));
Link *link1, *link2;
int r;
assert(m);
r = hsr_get_port_links(netdev, &link1, &link2);
if (r < 0)
return r;
if (link1->ifindex == link2->ifindex)
return log_netdev_warning_errno(
netdev, SYNTHETIC_ERRNO(EINVAL), "the two HSR ports must be different");
r = sd_netlink_message_append_u32(m, IFLA_HSR_SLAVE1, link1->ifindex);
if (r < 0)
return r;
r = sd_netlink_message_append_u32(m, IFLA_HSR_SLAVE2, link2->ifindex);
if (r < 0)
return r;
r = sd_netlink_message_append_u8(m, IFLA_HSR_PROTOCOL, h->protocol);
if (r < 0)
return r;
r = sd_netlink_message_append_u8(m, IFLA_HSR_MULTICAST_SPEC, h->supervision);
if (r < 0)
return r;
return 0;
}
static int netdev_hsr_config_verify(NetDev *netdev, const char *filename) {
Hsr *h = ASSERT_PTR(HSR(netdev));
assert(filename);
if (strv_length(h->ports) != 2)
return log_netdev_warning_errno(
netdev,
SYNTHETIC_ERRNO(EINVAL),
"HSR needs two ports set, ignoring \"%s\".",
filename);
if (streq(h->ports[0], h->ports[1]))
return log_netdev_warning_errno(
netdev,
SYNTHETIC_ERRNO(EINVAL),
"the two HSR ports must be different, ignoring \"%s\".",
filename);
return 0;
}
static int netdev_hsr_is_ready_to_create(NetDev *netdev, Link *link) {
return hsr_get_port_links(netdev, NULL, NULL) >= 0;
}
static void netdev_hsr_done(NetDev *netdev) {
Hsr *h = ASSERT_PTR(HSR(netdev));
strv_free(h->ports);
}
static void netdev_hsr_init(NetDev *netdev) {
Hsr *h = ASSERT_PTR(HSR(netdev));
h->protocol = NETDEV_HSR_PROTOCOL_HSR;
}
const NetDevVTable hsr_vtable = {
.object_size = sizeof(Hsr),
.init = netdev_hsr_init,
.done = netdev_hsr_done,
.config_verify = netdev_hsr_config_verify,
.is_ready_to_create = netdev_hsr_is_ready_to_create,
.fill_message_create = netdev_hsr_fill_message_create,
.sections = NETDEV_COMMON_SECTIONS "HSR\0",
.create_type = NETDEV_CREATE_INDEPENDENT,
.iftype = ARPHRD_ETHER,
.generate_mac = true,
};
+30
View File
@@ -0,0 +1,30 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#pragma once
typedef struct Hsr Hsr;
#include <linux/if_link.h>
#include "netdev.h"
typedef enum HsrProtocol {
NETDEV_HSR_PROTOCOL_HSR = HSR_PROTOCOL_HSR,
NETDEV_HSR_PROTOCOL_PRP = HSR_PROTOCOL_PRP,
_NETDEV_HSR_PROTOCOL_MAX,
_NETDEV_HSR_PROTOCOL_INVALID = -EINVAL,
} HsrProtocol;
struct Hsr {
NetDev meta;
char **ports;
HsrProtocol protocol;
uint8_t supervision;
};
DEFINE_NETDEV_CAST(HSR, Hsr);
extern const NetDevVTable hsr_vtable;
HsrProtocol hsr_protocol_from_string(const char *d) _pure_;
CONFIG_PARSER_PROTOTYPE(config_parse_hsr_protocol);
+4
View File
@@ -12,6 +12,7 @@ _Pragma("GCC diagnostic ignored \"-Wzero-as-null-pointer-constant\"")
#include "conf-parser.h"
#include "fou-tunnel.h"
#include "geneve.h"
#include "hsr.h"
#include "ipoib.h"
#include "ipvlan.h"
#include "l2tp-tunnel.h"
@@ -172,6 +173,9 @@ GENEVE.DestinationPort, config_parse_ip_port,
GENEVE.IPDoNotFragment, config_parse_geneve_df, 0, offsetof(Geneve, geneve_df)
GENEVE.FlowLabel, config_parse_geneve_flow_label, 0, 0
GENEVE.InheritInnerProtocol, config_parse_bool, 0, offsetof(Geneve, inherit_inner_protocol)
HSR.Ports, config_parse_ifnames, IFNAME_VALID_ALTERNATIVE, offsetof(Hsr, ports)
HSR.Protocol, config_parse_hsr_protocol, 0, offsetof(Hsr, protocol)
HSR.Supervision, config_parse_uint8, 0, offsetof(Hsr, supervision)
MACsec.Port, config_parse_macsec_port, 0, 0
MACsec.Encrypt, config_parse_tristate, 0, offsetof(MACsec, encrypt)
MACsecReceiveChannel.Port, config_parse_macsec_port, 0, 0
+3
View File
@@ -18,6 +18,7 @@
#include "fd-util.h"
#include "fou-tunnel.h"
#include "geneve.h"
#include "hsr.h"
#include "ifb.h"
#include "ipoib.h"
#include "ipvlan.h"
@@ -65,6 +66,7 @@ const NetDevVTable * const netdev_vtable[_NETDEV_KIND_MAX] = {
[NETDEV_KIND_GENEVE] = &geneve_vtable,
[NETDEV_KIND_GRE] = &gre_vtable,
[NETDEV_KIND_GRETAP] = &gretap_vtable,
[NETDEV_KIND_HSR] = &hsr_vtable,
[NETDEV_KIND_IFB] = &ifb_vtable,
[NETDEV_KIND_IP6GRE] = &ip6gre_vtable,
[NETDEV_KIND_IP6GRETAP] = &ip6gretap_vtable,
@@ -106,6 +108,7 @@ static const char* const netdev_kind_table[_NETDEV_KIND_MAX] = {
[NETDEV_KIND_GENEVE] = "geneve",
[NETDEV_KIND_GRE] = "gre",
[NETDEV_KIND_GRETAP] = "gretap",
[NETDEV_KIND_HSR] = "hsr",
[NETDEV_KIND_IFB] = "ifb",
[NETDEV_KIND_IP6GRE] = "ip6gre",
[NETDEV_KIND_IP6GRETAP] = "ip6gretap",
+2
View File
@@ -24,6 +24,7 @@
"-Bridge\0" \
"-FooOverUDP\0" \
"-GENEVE\0" \
"-HSR\0" \
"-IPoIB\0" \
"-IPVLAN\0" \
"-IPVTAP\0" \
@@ -59,6 +60,7 @@ typedef enum NetDevKind {
NETDEV_KIND_GENEVE,
NETDEV_KIND_GRE,
NETDEV_KIND_GRETAP,
NETDEV_KIND_HSR,
NETDEV_KIND_IFB,
NETDEV_KIND_IP6GRE,
NETDEV_KIND_IP6GRETAP,