traffic). DataBitRate=, DataSamplePoint=, FDMode=, FDNonISO= have
been added to configure various CAN-FD aspects.
- * systemd-networkd's [DHCPv6] section gained a new WithoutRA= boolean
- setting. If enabled, DHCPv6 will be attempted right-away without
- requiring an Router Advertisement packet suggesting it
- first. Conversely, the [IPv6AcceptRA] gained a boolean option
- DHCPv6Client= that may be used to turn off the DHCPv6 client even if
- the RA packets suggest it.
+ * systemd-networkd's [DHCPv6] section gained a new WithoutRA= setting.
+ If enabled, DHCPv6 will be attempted right-away without requiring an
+ Router Advertisement packet suggesting it first. Conversely, the
+ [IPv6AcceptRA] section gained a boolean option DHCPv6Client= that may
+ be used to turn off the DHCPv6 client even if the RA packets suggest
+ it.
* systemd-networkd's [DHCPv4] section gained a new setting UseGateway=
which may be used to turn off use of the gateway information provided
</variablelist>
</refsect1>
+ <refsect1>
+ <title>[SR-IOV] Section Options</title>
+ <para>The <literal>[SR-IOV]</literal> section accepts the
+ following keys. Specify several <literal>[SR-IOV]</literal>
+ sections to configure several SR-IOVs. SR-IOV provides the ability to partition a single physical PCI resource
+ into virtual PCI functions which can then be injected into a VM. In the case of network VFs, SR-IOV improves
+ north-south network performance (that is, traffic with endpoints outside the host machine) by allowing traffic to
+ bypass the host machine’s network stack.</para>
+
+ <variablelist class='network-directives'>
+ <varlistentry>
+ <term><varname>VirtualFunction=</varname></term>
+ <listitem>
+ <para>Specifies a Virtual Function (VF), lightweight PCIe function designed solely to move data
+ in and out. Takes an unsigned integer in the range 0..2147483646. This option is compulsory.</para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><varname>VLANId=</varname></term>
+ <listitem>
+ <para>Specifies VLAN ID of the virtual function. Takes an unsigned integer in the range 1..4095.</para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><varname>QualityOfService=</varname></term>
+ <listitem>
+ <para>Specifies quality of service of the virtual function. Takes an unsigned integer in the range 1..4294967294.</para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><varname>VLANProtocol=</varname></term>
+ <listitem>
+ <para>Specifies VLAN protocol of the virtual function. Takes <literal>802.1Q</literal> or
+ <literal>802.1ad</literal>.</para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><varname>MACSpoofCheck=</varname></term>
+ <listitem>
+ <para>Takes a boolean. Controls the MAC spoof checking. When unset, the kernel's default will be used.</para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><varname>QueryReceiveSideScaling=</varname></term>
+ <listitem>
+ <para>Takes a boolean. Toggle the ability of querying the receive side scaling (RSS)
+ configuration of the virtual function (VF). The VF RSS information like RSS hash key may be
+ considered sensitive on some devices where this information is shared between VF and the
+ physical function (PF). When unset, the kernel's default will be used.</para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><varname>Trust=</varname></term>
+ <listitem>
+ <para>Takes a boolean. Allows to set trust mode of the virtual function (VF). When set, VF
+ users can set a specific feature which may impact security and/or performance. When unset,
+ the kernel's default will be used.</para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><varname>LinkState=</varname></term>
+ <listitem>
+ <para>Allows to set the link state of the virtual function (VF). Takes a boolean or a
+ special value <literal>auto</literal>. Setting to <literal>auto</literal> means a
+ reflection of the physical function (PF) link state, <literal>yes</literal> lets the VF to
+ communicate with other VFs on this host even if the PF link state is down,
+ <literal>no</literal> causes the hardware to drop any packets sent by the VF. When unset,
+ the kernel's default will be used.</para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><varname>MACAddress=</varname></term>
+ <listitem>
+ <para>Specifies the MAC address for the virtual function.</para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+ </refsect1>
+
<refsect1>
<title>[Network] Section Options</title>
int socket_bind_to_ifindex(int fd, int ifindex) {
char ifname[IF_NAMESIZE + 1];
+ int r;
assert(fd >= 0);
return 0;
}
- if (setsockopt(fd, SOL_SOCKET, SO_BINDTOIFINDEX, &ifindex, sizeof(ifindex)) >= 0)
- return 0;
- if (errno != ENOPROTOOPT)
- return -errno;
+ r = setsockopt_int(fd, SOL_SOCKET, SO_BINDTOIFINDEX, ifindex);
+ if (r != -ENOPROTOOPT)
+ return r;
/* Fall back to SO_BINDTODEVICE on kernels < 5.0 which didn't have SO_BINDTOIFINDEX */
if (!format_ifname(ifindex, ifname))
int dhcp6_option_parse_domainname(const uint8_t *optval, uint16_t optlen,
char ***str_arr);
-int dhcp6_network_bind_udp_socket(int index, struct in6_addr *address);
+int dhcp6_network_bind_udp_socket(int ifindex, struct in6_addr *address);
int dhcp6_network_send_udp_socket(int s, struct in6_addr *address,
const void *packet, size_t len);
#include "fd-util.h"
#include "socket-util.h"
-int dhcp6_network_bind_udp_socket(int index, struct in6_addr *local_address) {
+int dhcp6_network_bind_udp_socket(int ifindex, struct in6_addr *local_address) {
union sockaddr_union src = {
.in6.sin6_family = AF_INET6,
.in6.sin6_port = htobe16(DHCP6_PORT_CLIENT),
- .in6.sin6_scope_id = index,
+ .in6.sin6_scope_id = ifindex,
};
_cleanup_close_ int s = -1;
int r;
- assert(index > 0);
+ assert(ifindex > 0);
assert(local_address);
src.in6.sin6_addr = *local_address;
return TAKE_FD(s);
}
-int icmp6_bind_router_solicitation(int index) {
+int icmp6_bind_router_solicitation(int ifindex) {
struct icmp6_filter filter = {};
struct ipv6_mreq mreq = {
.ipv6mr_multiaddr = IN6ADDR_ALL_NODES_MULTICAST_INIT,
- .ipv6mr_interface = index,
+ .ipv6mr_interface = ifindex,
};
ICMP6_FILTER_SETBLOCKALL(&filter);
return icmp6_bind_router_message(&filter, &mreq);
}
-int icmp6_bind_router_advertisement(int index) {
+int icmp6_bind_router_advertisement(int ifindex) {
struct icmp6_filter filter = {};
struct ipv6_mreq mreq = {
.ipv6mr_multiaddr = IN6ADDR_ALL_ROUTERS_MULTICAST_INIT,
- .ipv6mr_interface = index,
+ .ipv6mr_interface = ifindex,
};
ICMP6_FILTER_SETBLOCKALL(&filter);
{ { { 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 } } }
-int icmp6_bind_router_solicitation(int index);
-int icmp6_bind_router_advertisement(int index);
+int icmp6_bind_router_solicitation(int ifindex);
+int icmp6_bind_router_advertisement(int ifindex);
int icmp6_send_router_solicitation(int s, const struct ether_addr *ether_addr);
int icmp6_receive(int fd, void *buffer, size_t size, struct in6_addr *dst,
triple_timestamp *timestamp);
int sd_dhcp6_client_set_ifindex(sd_dhcp6_client *client, int ifindex) {
assert_return(client, -EINVAL);
- assert_return(ifindex >= -1, -EINVAL);
+ assert_return(ifindex > 0, -EINVAL);
assert_return(IN_SET(client->state, DHCP6_STATE_STOPPED), -EBUSY);
client->ifindex = ifindex;
_public_ int sd_radv_set_ifindex(sd_radv *ra, int ifindex) {
assert_return(ra, -EINVAL);
- assert_return(ifindex >= -1, -EINVAL);
+ assert_return(ifindex > 0, -EINVAL);
if (ra->state != SD_RADV_STATE_IDLE)
return -EBUSY;
}
int dhcp_network_bind_raw_socket(
- int index,
+ int ifindex,
union sockaddr_union *link,
uint32_t id,
const uint8_t *addr, size_t addr_len,
static sd_event_source *hangcheck;
static int test_dhcp_fd[2];
-static int test_index = 42;
+static int test_ifindex = 42;
static int test_client_message_num;
static be32_t test_iaid = 0;
static uint8_t test_duid[14] = { };
assert_se(sd_dhcp6_client_set_ifindex(client, 15) == 0);
assert_se(sd_dhcp6_client_set_ifindex(client, -42) == -EINVAL);
- assert_se(sd_dhcp6_client_set_ifindex(client, -1) == 0);
+ assert_se(sd_dhcp6_client_set_ifindex(client, -1) == -EINVAL);
assert_se(sd_dhcp6_client_set_ifindex(client, 42) >= 0);
assert_se(sd_dhcp6_client_set_mac(client, (const uint8_t *) &mac_addr,
return len;
}
-int dhcp6_network_bind_udp_socket(int index, struct in6_addr *local_address) {
- assert_se(index == test_index);
+int dhcp6_network_bind_udp_socket(int ifindex, struct in6_addr *local_address) {
+ assert_se(ifindex == test_ifindex);
if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0, test_dhcp_fd) < 0)
return -errno;
assert_se(sd_dhcp6_client_attach_event(client, e, 0) >= 0);
- assert_se(sd_dhcp6_client_set_ifindex(client, test_index) == 0);
+ assert_se(sd_dhcp6_client_set_ifindex(client, test_ifindex) == 0);
assert_se(sd_dhcp6_client_set_mac(client, (const uint8_t *) &mac_addr,
sizeof (mac_addr),
ARPHRD_ETHER) >= 0);
return arp_network_send_raw_socket(fd, ifindex, &ea);
}
-int arp_network_bind_raw_socket(int index, be32_t address, const struct ether_addr *eth_mac) {
+int arp_network_bind_raw_socket(int ifindex, be32_t address, const struct ether_addr *eth_mac) {
if (socketpair(AF_UNIX, SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0, test_fd) < 0)
return -errno;
assert_se(ra);
assert_se(sd_radv_set_ifindex(NULL, 0) < 0);
- assert_se(sd_radv_set_ifindex(ra, 0) >= 0);
- assert_se(sd_radv_set_ifindex(ra, -1) >= 0);
+ assert_se(sd_radv_set_ifindex(ra, 0) < 0);
+ assert_se(sd_radv_set_ifindex(ra, -1) < 0);
assert_se(sd_radv_set_ifindex(ra, -2) < 0);
assert_se(sd_radv_set_ifindex(ra, 42) >= 0);
assert_se(!ra);
}
-int icmp6_bind_router_solicitation(int index) {
+int icmp6_bind_router_solicitation(int ifindex) {
return -ENOSYS;
}
-int icmp6_bind_router_advertisement(int index) {
- assert_se(index == 42);
+int icmp6_bind_router_advertisement(int ifindex) {
+ assert_se(ifindex == 42);
return test_fd[1];
}
return 0;
}
-int icmp6_bind_router_solicitation(int index) {
- assert_se(index == 42);
+int icmp6_bind_router_solicitation(int ifindex) {
+ assert_se(ifindex == 42);
if (socketpair(AF_UNIX, SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0, test_fd) < 0)
return -errno;
return test_fd[0];
}
-int icmp6_bind_router_advertisement(int index) {
-
+int icmp6_bind_router_advertisement(int ifindex) {
return -ENOSYS;
}
.types = rtnl_prop_list_types,
};
+static const NLType rtnl_vf_vlan_list_types[] = {
+ [IFLA_VF_VLAN_INFO] = { .size = sizeof(struct ifla_vf_vlan_info) },
+};
+
+static const NLTypeSystem rtnl_vf_vlan_type_system = {
+ .count = ELEMENTSOF(rtnl_vf_vlan_list_types),
+ .types = rtnl_vf_vlan_list_types,
+};
+
+static const NLType rtnl_vf_vlan_info_types[] = {
+ [IFLA_VF_MAC] = { .size = sizeof(struct ifla_vf_mac) },
+ [IFLA_VF_VLAN] = { .size = sizeof(struct ifla_vf_vlan) },
+ [IFLA_VF_VLAN_LIST] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_vf_vlan_type_system},
+ [IFLA_VF_TX_RATE] = { .size = sizeof(struct ifla_vf_tx_rate) },
+ [IFLA_VF_SPOOFCHK] = { .size = sizeof(struct ifla_vf_spoofchk) },
+ [IFLA_VF_RATE] = { .size = sizeof(struct ifla_vf_rate) },
+ [IFLA_VF_LINK_STATE] = { .size = sizeof(struct ifla_vf_link_state) },
+ [IFLA_VF_RSS_QUERY_EN] = { .size = sizeof(struct ifla_vf_rss_query_en) },
+ [IFLA_VF_TRUST] = { .size = sizeof(struct ifla_vf_trust) },
+ [IFLA_VF_IB_NODE_GUID] = { .size = sizeof(struct ifla_vf_guid) },
+ [IFLA_VF_IB_PORT_GUID] = { .size = sizeof(struct ifla_vf_guid) },
+};
+
+static const NLTypeSystem rtnl_vf_vlan_info_type_system = {
+ .count = ELEMENTSOF(rtnl_vf_vlan_info_types),
+ .types = rtnl_vf_vlan_info_types,
+};
+
+static const NLType rtnl_link_io_srv_types[] = {
+ [IFLA_VF_INFO] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_vf_vlan_info_type_system },
+};
+
+static const NLTypeSystem rtnl_io_srv_type_system = {
+ .count = ELEMENTSOF(rtnl_link_io_srv_types),
+ .types = rtnl_link_io_srv_types,
+};
+
static const NLType rtnl_link_types[] = {
[IFLA_ADDRESS] = { .type = NETLINK_TYPE_ETHER_ADDR },
[IFLA_BROADCAST] = { .type = NETLINK_TYPE_ETHER_ADDR },
[IFLA_LINKINFO] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_link_info_type_system },
[IFLA_NET_NS_PID] = { .type = NETLINK_TYPE_U32 },
[IFLA_IFALIAS] = { .type = NETLINK_TYPE_STRING, .size = IFALIASZ - 1 },
-/*
- [IFLA_NUM_VF],
- [IFLA_VFINFO_LIST] = {. type = NETLINK_TYPE_NESTED, },
-*/
+ [IFLA_NUM_VF] = { .type = NETLINK_TYPE_U32 },
+ [IFLA_VFINFO_LIST] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_io_srv_type_system },
[IFLA_STATS64] = { .size = sizeof(struct rtnl_link_stats64) },
/*
[IFLA_VF_PORTS] = { .type = NETLINK_TYPE_NESTED },
networkd-routing-policy-rule.h
networkd-speed-meter.c
networkd-speed-meter.h
+ networkd-sriov.c
+ networkd-sriov.h
networkd-util.c
networkd-util.h
networkd-wifi.c
#include <netinet/in.h>
#include <linux/if.h>
#include <linux/if_arp.h>
+#include <linux/if_link.h>
#include <unistd.h>
#include "alloc-util.h"
#include "networkd-manager.h"
#include "networkd-ndisc.h"
#include "networkd-neighbor.h"
+#include "networkd-sriov.h"
#include "networkd-radv.h"
#include "networkd-routing-policy-rule.h"
#include "networkd-wifi.h"
if (!link->tc_configured)
return;
+ if (!link->sr_iov_configured)
+ return;
+
if (link_has_carrier(link) || !link->network->configure_without_carrier) {
if (link_ipv4ll_enabled(link, ADDRESS_FAMILY_IPV4) && !link->ipv4ll_address)
return 0;
}
+static int link_configure_sr_iov(Link *link) {
+ SRIOV *sr_iov;
+ Iterator i;
+ int r;
+
+ link->sr_iov_configured = false;
+ link->sr_iov_messages = 0;
+
+ ORDERED_HASHMAP_FOREACH(sr_iov, link->network->sr_iov_by_section, i) {
+ r = sr_iov_configure(link, sr_iov);
+ if (r < 0)
+ return r;
+ }
+
+ if (link->sr_iov_messages == 0)
+ link->sr_iov_configured = true;
+ else
+ log_link_debug(link, "Configuring SR-IOV");
+
+ return 0;
+}
+
static int link_configure(Link *link) {
int r;
if (r < 0)
return r;
+ r = link_configure_sr_iov(link);
+ if (r < 0)
+ return r;
+
if (link->iftype == ARPHRD_CAN)
return link_configure_can(link);
unsigned routing_policy_rule_messages;
unsigned routing_policy_rule_remove_messages;
unsigned tc_messages;
+ unsigned sr_iov_messages;
unsigned enslaving;
Set *addresses;
bool static_nexthops_configured:1;
bool routing_policy_rules_configured:1;
bool tc_configured:1;
+ bool sr_iov_configured:1;
bool setting_mtu:1;
bool setting_genmode:1;
bool ipv6_mtu_set:1;
#include "networkd-ipv4ll.h"
#include "networkd-ndisc.h"
#include "networkd-network.h"
+#include "networkd-sriov.h"
#include "qdisc.h"
#include "tclass.h"
#include "vlan-util.h"
Link.AllMulticast, config_parse_tristate, 0, offsetof(Network, allmulticast)
Link.Unmanaged, config_parse_bool, 0, offsetof(Network, unmanaged)
Link.RequiredForOnline, config_parse_required_for_online, 0, 0
+SR-IOV.VirtualFunction, config_parse_sr_iov_uint32, 0, 0
+SR-IOV.VLANId, config_parse_sr_iov_uint32, 0, 0
+SR-IOV.QualityOfService, config_parse_sr_iov_uint32, 0, 0
+SR-IOV.VLANProtocol, config_parse_sr_iov_vlan_proto, 0, 0
+SR-IOV.MACSpoofCheck, config_parse_sr_iov_boolean, 0, 0
+SR-IOV.QueryReceiveSideScaling, config_parse_sr_iov_boolean, 0, 0
+SR-IOV.Trust, config_parse_sr_iov_boolean, 0, 0
+SR-IOV.LinkState, config_parse_sr_iov_link_state, 0, 0
+SR-IOV.MACAddress, config_parse_sr_iov_mac, 0, 0
Network.Description, config_parse_string, 0, offsetof(Network, description)
Network.Bridge, config_parse_ifname, 0, offsetof(Network, bridge_name)
Network.Bond, config_parse_ifname, 0, offsetof(Network, bond_name)
#include "network-internal.h"
#include "networkd-manager.h"
#include "networkd-network.h"
+#include "networkd-sriov.h"
#include "parse-util.h"
#include "path-lookup.h"
#include "set.h"
Route *route, *route_next;
FdbEntry *fdb, *fdb_next;
TrafficControl *tc;
+ SRIOV *sr_iov;
Iterator i;
assert(network);
if (traffic_control_section_verify(tc, &has_root, &has_clsact) < 0)
traffic_control_free(tc);
+ ORDERED_HASHMAP_FOREACH(sr_iov, network->sr_iov_by_section, i)
+ if (sr_iov_section_verify(sr_iov) < 0)
+ sr_iov_free(sr_iov);
+
return 0;
}
filename, NETWORK_DIRS, dropin_dirname,
"Match\0"
"Link\0"
+ "SR-IOV\0"
"Network\0"
"Address\0"
"Neighbor\0"
hashmap_free(network->prefixes_by_section);
hashmap_free(network->route_prefixes_by_section);
hashmap_free(network->rules_by_section);
+ ordered_hashmap_free_with_destructor(network->sr_iov_by_section, sr_iov_free);
ordered_hashmap_free_with_destructor(network->tc_by_section, traffic_control_free);
if (network->manager &&
Hashmap *route_prefixes_by_section;
Hashmap *rules_by_section;
OrderedHashmap *tc_by_section;
+ OrderedHashmap *sr_iov_by_section;
/* All kinds of DNS configuration */
struct in_addr_data *dns;
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1+
+ * Copyright © 2020 VMware, Inc. */
+
+#include "alloc-util.h"
+#include "netlink-util.h"
+#include "networkd-manager.h"
+#include "networkd-sriov.h"
+#include "parse-util.h"
+#include "set.h"
+#include "string-util.h"
+
+static int sr_iov_new(SRIOV **ret) {
+ SRIOV *sr_iov;
+
+ sr_iov = new(SRIOV, 1);
+ if (!sr_iov)
+ return -ENOMEM;
+
+ *sr_iov = (SRIOV) {
+ .vf = (uint32_t) -1,
+ .vlan_proto = ETH_P_8021Q,
+ .vf_spoof_check_setting = -1,
+ .trust = -1,
+ .query_rss = -1,
+ .link_state = _SR_IOV_LINK_STATE_INVALID,
+ };
+
+ *ret = TAKE_PTR(sr_iov);
+
+ return 0;
+}
+
+static int sr_iov_new_static(Network *network, const char *filename, unsigned section_line, SRIOV **ret) {
+ _cleanup_(network_config_section_freep) NetworkConfigSection *n = NULL;
+ _cleanup_(sr_iov_freep) SRIOV *sr_iov = NULL;
+ SRIOV *existing = NULL;
+ int r;
+
+ assert(network);
+ assert(ret);
+ assert(filename);
+ assert(section_line > 0);
+
+ r = network_config_section_new(filename, section_line, &n);
+ if (r < 0)
+ return r;
+
+ existing = ordered_hashmap_get(network->sr_iov_by_section, n);
+ if (existing) {
+ *ret = existing;
+ return 0;
+ }
+
+ r = sr_iov_new(&sr_iov);
+ if (r < 0)
+ return r;
+
+ sr_iov->network = network;
+ sr_iov->section = TAKE_PTR(n);
+
+ r = ordered_hashmap_ensure_allocated(&network->sr_iov_by_section, &network_config_hash_ops);
+ if (r < 0)
+ return r;
+
+ r = ordered_hashmap_put(network->sr_iov_by_section, sr_iov->section, sr_iov);
+ if (r < 0)
+ return r;
+
+ *ret = TAKE_PTR(sr_iov);
+ return 0;
+}
+
+SRIOV *sr_iov_free(SRIOV *sr_iov) {
+ if (!sr_iov)
+ return NULL;
+
+ if (sr_iov->network && sr_iov->section)
+ ordered_hashmap_remove(sr_iov->network->sr_iov_by_section, sr_iov->section);
+
+ network_config_section_free(sr_iov->section);
+
+ return mfree(sr_iov);
+}
+
+static int sr_iov_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
+ int r;
+
+ assert(link);
+ assert(link->sr_iov_messages > 0);
+ link->sr_iov_messages--;
+
+ if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
+ return 1;
+
+ r = sd_netlink_message_get_errno(m);
+ if (r < 0 && r != -EEXIST) {
+ log_link_message_error_errno(link, m, r, "Could not set up SR-IOV");
+ link_enter_failed(link);
+ return 1;
+ }
+
+ if (link->sr_iov_messages == 0) {
+ log_link_debug(link, "SR-IOV configured");
+ link->sr_iov_configured = true;
+ link_check_ready(link);
+ }
+
+ return 1;
+}
+
+int sr_iov_configure(Link *link, SRIOV *sr_iov) {
+ _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL;
+ int r;
+
+ assert(link);
+ assert(link->manager);
+ assert(link->manager->rtnl);
+ assert(link->ifindex > 0);
+
+ log_link_debug(link, "Setting SR-IOV virtual function %"PRIu32, sr_iov->vf);
+
+ r = sd_rtnl_message_new_link(link->manager->rtnl, &req, RTM_SETLINK, link->ifindex);
+ if (r < 0)
+ return log_link_error_errno(link, r, "Could not allocate RTM_SETLINK message: %m");
+
+ r = sd_netlink_message_open_container(req, IFLA_VFINFO_LIST);
+ if (r < 0)
+ return log_link_error_errno(link, r, "Could not open IFLA_VFINFO_LIST container: %m");
+
+ r = sd_netlink_message_open_container(req, IFLA_VF_INFO);
+ if (r < 0)
+ return log_link_error_errno(link, r, "Could not open IFLA_VF_INFO container: %m");
+
+ if (!ether_addr_is_null(&sr_iov->mac)) {
+ struct ifla_vf_mac ivm = {
+ .vf = sr_iov->vf,
+ };
+
+ memcpy(ivm.mac, &sr_iov->mac, ETH_ALEN);
+ r = sd_netlink_message_append_data(req, IFLA_VF_MAC, &ivm, sizeof(struct ifla_vf_mac));
+ if (r < 0)
+ return log_link_error_errno(link, r, "Could not append IFLA_VF_MAC: %m");
+ }
+
+ if (sr_iov->vf_spoof_check_setting >= 0) {
+ struct ifla_vf_spoofchk ivs = {
+ .vf = sr_iov->vf,
+ .setting = sr_iov->vf_spoof_check_setting,
+ };
+
+ r = sd_netlink_message_append_data(req, IFLA_VF_SPOOFCHK, &ivs, sizeof(struct ifla_vf_spoofchk));
+ if (r < 0)
+ return log_link_error_errno(link, r, "Could not append IFLA_VF_SPOOFCHK: %m");
+ }
+
+ if (sr_iov->query_rss >= 0) {
+ struct ifla_vf_rss_query_en ivs = {
+ .vf = sr_iov->vf,
+ .setting = sr_iov->query_rss,
+ };
+
+ r = sd_netlink_message_append_data(req, IFLA_VF_RSS_QUERY_EN, &ivs, sizeof(struct ifla_vf_rss_query_en));
+ if (r < 0)
+ return log_link_error_errno(link, r, "Could not append IFLA_VF_RSS_QUERY_EN: %m");
+ }
+
+ if (sr_iov->trust >= 0) {
+ struct ifla_vf_trust ivt = {
+ .vf = sr_iov->vf,
+ .setting = sr_iov->trust,
+ };
+
+ r = sd_netlink_message_append_data(req, IFLA_VF_TRUST, &ivt, sizeof(struct ifla_vf_trust));
+ if (r < 0)
+ return log_link_error_errno(link, r, "Could not append IFLA_VF_TRUST: %m");
+ }
+
+ if (sr_iov->link_state >= 0) {
+ struct ifla_vf_link_state ivl = {
+ .vf = sr_iov->vf,
+ .link_state = sr_iov->link_state,
+ };
+
+ r = sd_netlink_message_append_data(req, IFLA_VF_LINK_STATE, &ivl, sizeof(struct ifla_vf_link_state));
+ if (r < 0)
+ return log_link_error_errno(link, r, "Could not append IFLA_VF_LINK_STATE: %m");
+ }
+
+ if (sr_iov->vlan > 0) {
+ /* Because of padding, first the buffer must be initialized with 0. */
+ struct ifla_vf_vlan_info ivvi = {};
+ ivvi.vf = sr_iov->vf;
+ ivvi.vlan = sr_iov->vlan;
+ ivvi.qos = sr_iov->qos;
+ ivvi.vlan_proto = htobe16(sr_iov->vlan_proto);
+
+ r = sd_netlink_message_open_container(req, IFLA_VF_VLAN_LIST);
+ if (r < 0)
+ return log_link_error_errno(link, r, "Could not open IFLA_VF_VLAN_LIST container: %m");
+
+ r = sd_netlink_message_append_data(req, IFLA_VF_VLAN_INFO, &ivvi, sizeof(struct ifla_vf_vlan_info));
+ if (r < 0)
+ return log_link_error_errno(link, r, "Could not append IFLA_VF_VLAN_INFO: %m");
+
+ r = sd_netlink_message_close_container(req);
+ if (r < 0)
+ return log_link_error_errno(link, r, "Could not close IFLA_VF_VLAN_LIST container: %m");
+ }
+
+ r = sd_netlink_message_close_container(req);
+ if (r < 0)
+ return log_link_error_errno(link, r, "Could not close IFLA_VF_INFO container: %m");
+
+ r = sd_netlink_message_close_container(req);
+ if (r < 0)
+ return log_link_error_errno(link, r, "Could not close IFLA_VFINFO_LIST container: %m");
+
+ r = netlink_call_async(link->manager->rtnl, NULL, req, sr_iov_handler,
+ link_netlink_destroy_callback, link);
+ if (r < 0)
+ return log_link_error_errno(link, r, "Could not send rtnetlink message: %m");
+
+ link_ref(link);
+ link->sr_iov_messages++;
+
+ return 0;
+}
+
+int sr_iov_section_verify(SRIOV *sr_iov) {
+ assert(sr_iov);
+
+ if (section_is_invalid(sr_iov->section))
+ return -EINVAL;
+
+ if (sr_iov->vf == (uint32_t) -1)
+ return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
+ "%s: [SRIOV] section without VirtualFunction= field configured. "
+ "Ignoring [SRIOV] section from line %u.",
+ sr_iov->section->filename, sr_iov->section->line);
+
+ return 0;
+}
+
+int config_parse_sr_iov_uint32(
+ 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) {
+
+ _cleanup_(sr_iov_free_or_set_invalidp) SRIOV *sr_iov = NULL;
+ Network *network = data;
+ uint32_t k;
+ int r;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+ assert(data);
+
+ r = sr_iov_new_static(network, filename, section_line, &sr_iov);
+ if (r < 0)
+ return r;
+
+ if (isempty(rvalue)) {
+ if (streq(lvalue, "VirtualFunction"))
+ sr_iov->vf = (uint32_t) -1;
+ else if (streq(lvalue, "VLANId"))
+ sr_iov->vlan = 0;
+ else if (streq(lvalue, "QualityOfService"))
+ sr_iov->qos = 0;
+ else
+ assert_not_reached("Invalid lvalue");
+
+ TAKE_PTR(sr_iov);
+ return 0;
+ }
+
+ r = safe_atou32(rvalue, &k);
+ if (r < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, r,
+ "Failed to parse SR-IOV '%s=', ignoring assignment: %s", lvalue, rvalue);
+ return 0;
+ }
+
+ if (streq(lvalue, "VLANId")) {
+ if (k == 0 || k > 4095) {
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid SR-IOV VLANId: %d", k);
+ return 0;
+ }
+ sr_iov->vlan = k;
+ } else if (streq(lvalue, "VirtualFunction")) {
+ if (k >= INT_MAX) {
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid SR-IOV virtual function: %d", k);
+ return 0;
+ }
+ sr_iov->vf = k;
+ } else if (streq(lvalue, "QualityOfService"))
+ sr_iov->qos = k;
+ else
+ assert_not_reached("Invalid lvalue");
+
+ TAKE_PTR(sr_iov);
+ return 0;
+}
+
+int config_parse_sr_iov_vlan_proto(
+ 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) {
+
+ _cleanup_(sr_iov_free_or_set_invalidp) SRIOV *sr_iov = NULL;
+ Network *network = data;
+ int r;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+ assert(data);
+
+ r = sr_iov_new_static(network, filename, section_line, &sr_iov);
+ if (r < 0)
+ return r;
+
+ if (isempty(rvalue) || streq(rvalue, "802.1Q"))
+ sr_iov->vlan_proto = ETH_P_8021Q;
+ else if (streq(rvalue, "802.1ad"))
+ sr_iov->vlan_proto = ETH_P_8021AD;
+ else {
+ log_syntax(unit, LOG_ERR, filename, line, 0,
+ "Invalid SR-IOV '%s=', ignoring assignment: %s", lvalue, rvalue);
+ return 0;
+ }
+
+ TAKE_PTR(sr_iov);
+ return 0;
+}
+
+int config_parse_sr_iov_link_state(
+ 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) {
+
+ _cleanup_(sr_iov_free_or_set_invalidp) SRIOV *sr_iov = NULL;
+ Network *network = data;
+ int r;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+ assert(data);
+
+ r = sr_iov_new_static(network, filename, section_line, &sr_iov);
+ if (r < 0)
+ return r;
+
+ /* Unfortunately, SR_IOV_LINK_STATE_DISABLE is 2, not 0. So, we cannot use
+ * DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN() macro. */
+
+ if (isempty(rvalue)) {
+ sr_iov->link_state = _SR_IOV_LINK_STATE_INVALID;
+ TAKE_PTR(sr_iov);
+ return 0;
+ }
+
+ if (streq(rvalue, "auto")) {
+ sr_iov->link_state = SR_IOV_LINK_STATE_AUTO;
+ TAKE_PTR(sr_iov);
+ return 0;
+ }
+
+ r = parse_boolean(rvalue);
+ if (r < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, r,
+ "Failed to parse SR-IOV '%s=', ignoring assignment: %s", lvalue, rvalue);
+ return 0;
+ }
+
+ sr_iov->link_state = r ? SR_IOV_LINK_STATE_ENABLE : SR_IOV_LINK_STATE_DISABLE;
+ TAKE_PTR(sr_iov);
+ return 0;
+}
+
+int config_parse_sr_iov_boolean(
+ 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) {
+
+ _cleanup_(sr_iov_free_or_set_invalidp) SRIOV *sr_iov = NULL;
+ Network *network = data;
+ int r;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+ assert(data);
+
+ r = sr_iov_new_static(network, filename, section_line, &sr_iov);
+ if (r < 0)
+ return r;
+
+ if (isempty(rvalue)) {
+ if (streq(lvalue, "MACSpoofCheck"))
+ sr_iov->vf_spoof_check_setting = -1;
+ else if (streq(lvalue, "QueryReceiveSideScaling"))
+ sr_iov->query_rss = -1;
+ else if (streq(lvalue, "Trust"))
+ sr_iov->trust = -1;
+ else
+ assert_not_reached("Invalid lvalue");
+
+ TAKE_PTR(sr_iov);
+ return 0;
+ }
+
+ r = parse_boolean(rvalue);
+ if (r < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse '%s=', ignoring: %s", lvalue, rvalue);
+ return 0;
+ }
+
+ if (streq(lvalue, "MACSpoofCheck"))
+ sr_iov->vf_spoof_check_setting = r;
+ else if (streq(lvalue, "QueryReceiveSideScaling"))
+ sr_iov->query_rss = r;
+ else if (streq(lvalue, "Trust"))
+ sr_iov->trust = r;
+ else
+ assert_not_reached("Invalid lvalue");
+
+ TAKE_PTR(sr_iov);
+ return 0;
+}
+
+int config_parse_sr_iov_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) {
+
+ _cleanup_(sr_iov_free_or_set_invalidp) SRIOV *sr_iov = NULL;
+ Network *network = data;
+ int r;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+ assert(data);
+
+ r = sr_iov_new_static(network, filename, section_line, &sr_iov);
+ if (r < 0)
+ return r;
+
+ if (isempty(rvalue)) {
+ sr_iov->mac = ETHER_ADDR_NULL;
+ TAKE_PTR(sr_iov);
+ return 0;
+ }
+
+ r = ether_addr_from_string(rvalue, &sr_iov->mac);
+ if (r < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, 0,
+ "Failed to parse SR-IOV '%s=', ignoring assignment: %s", lvalue, rvalue);
+ return 0;
+ }
+
+ TAKE_PTR(sr_iov);
+ return 0;
+}
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1+
+ * Copyright © 2020 VMware, Inc. */
+#pragma once
+
+#include <linux/if_link.h>
+
+#include "conf-parser.h"
+#include "networkd-link.h"
+#include "networkd-network.h"
+#include "networkd-util.h"
+
+typedef enum SRIOVLinkState {
+ SR_IOV_LINK_STATE_AUTO = IFLA_VF_LINK_STATE_AUTO,
+ SR_IOV_LINK_STATE_ENABLE = IFLA_VF_LINK_STATE_ENABLE,
+ SR_IOV_LINK_STATE_DISABLE = IFLA_VF_LINK_STATE_DISABLE,
+ _SR_IOV_LINK_STATE_MAX,
+ _SR_IOV_LINK_STATE_INVALID = -1,
+} SRIOVLinkState;
+
+typedef struct SRIOV {
+ NetworkConfigSection *section;
+ Network *network;
+
+ uint32_t vf; /* 0 - 2147483646 */
+ uint32_t vlan; /* 0 - 4095, 0 disables VLAN filter */
+ uint32_t qos;
+ uint16_t vlan_proto; /* ETH_P_8021Q or ETH_P_8021AD */
+ int vf_spoof_check_setting;
+ int query_rss;
+ int trust;
+ SRIOVLinkState link_state;
+ struct ether_addr mac;
+} SRIOV;
+
+SRIOV *sr_iov_free(SRIOV *sr_iov);
+
+int sr_iov_configure(Link *link, SRIOV *sr_iov);
+int sr_iov_section_verify(SRIOV *sr_iov);
+
+DEFINE_NETWORK_SECTION_FUNCTIONS(SRIOV, sr_iov_free);
+
+CONFIG_PARSER_PROTOTYPE(config_parse_sr_iov_uint32);
+CONFIG_PARSER_PROTOTYPE(config_parse_sr_iov_boolean);
+CONFIG_PARSER_PROTOTYPE(config_parse_sr_iov_link_state);
+CONFIG_PARSER_PROTOTYPE(config_parse_sr_iov_vlan_proto);
+CONFIG_PARSER_PROTOTYPE(config_parse_sr_iov_mac);
s->ifindex = manager_find_ifindex(s->manager, s->local.sa.sa_family, s->local.sa.sa_family == AF_INET ? (union in_addr_union*) &s->local.in.sin_addr : (union in_addr_union*) &s->local.in6.sin6_addr);
if (s->protocol == DNS_PROTOCOL_LLMNR && s->ifindex > 0) {
- uint32_t ifindex = htobe32(s->ifindex);
+ be32_t ifindex = htobe32(s->ifindex);
/* Make sure all packets for this connection are sent on the same interface */
if (s->local.sa.sa_family == AF_INET) {
Multicast=
MACAddress=
Group=
+[SR-IOV]
+VirtualFunction=
+MACSpoofCheck=
+VLANId=
+VLANProtocol=
+QualityOfService=
+QueryReceiveSideScaling=
+Trust=
+LinkState=
+MACAddress=
[BridgeFDB]
VLANId=
MACAddress=
--- /dev/null
+[Match]
+Name=eni99np1
+
+[Network]
+Address=192.168.100.100/24
+
+[SR-IOV]
+VirtualFunction=0
+VLANId=5
+VLANProtocol=802.1ad
+QualityOfService=1
+MACSpoofCheck=yes
+QueryReceiveSideScaling=yes
+Trust=yes
+LinkState=yes
+MACAddress=00:11:22:33:44:55
+
+[SR-IOV]
+VirtualFunction=1
+VLANId=6
+VLANProtocol=802.1Q
+QualityOfService=2
+MACSpoofCheck=no
+QueryReceiveSideScaling=no
+Trust=no
+LinkState=no
+MACAddress=00:11:22:33:44:56
+
+[SR-IOV]
+VirtualFunction=2
+VLANId=7
+QualityOfService=3
+MACSpoofCheck=no
+QueryReceiveSideScaling=no
+Trust=no
+LinkState=auto
+MACAddress=00:11:22:33:44:57
return f
+def expectedFailureIfNetdevsimWithSRIOVIsNotAvailable():
+ def f(func):
+ call('rmmod netdevsim', stderr=subprocess.DEVNULL)
+ rc = call('modprobe netdevsim', stderr=subprocess.DEVNULL)
+ if rc != 0:
+ return unittest.expectedFailure(func)
+
+ try:
+ with open('/sys/bus/netdevsim/new_device', mode='w') as f:
+ f.write('99 1')
+ except Exception as error:
+ return unittest.expectedFailure(func)
+
+ call('udevadm settle')
+ call('udevadm info -w10s /sys/devices/netdevsim99/net/eni99np1', stderr=subprocess.DEVNULL)
+ try:
+ with open('/sys/class/net/eni99np1/device/sriov_numvfs', mode='w') as f:
+ f.write('3')
+ except Exception as error:
+ call('rmmod netdevsim', stderr=subprocess.DEVNULL)
+ return unittest.expectedFailure(func)
+
+ call('rmmod netdevsim', stderr=subprocess.DEVNULL)
+ return func
+
+ return f
+
def expectedFailureIfCAKEIsNotAvailable():
def f(func):
call('ip link add dummy98 type dummy', stderr=subprocess.DEVNULL)
'25-route-vrf.network',
'25-gateway-static.network',
'25-gateway-next-static.network',
+ '25-sriov.network',
'25-sysctl-disable-ipv6.network',
'25-sysctl.network',
'25-test1.network',
self.assertRegex(output, 'inet6 .* scope link')
output = check_output('ip -4 route show dev dummy98')
print(output)
- self.assertEqual(output, '10.2.0.0/16 proto kernel scope link src 10.2.3.4')
+ self.assertRegex(output, '10.2.0.0/16 proto kernel scope link src 10.2.3.4')
output = check_output('ip -6 route show dev dummy98')
print(output)
self.assertRegex(output, 'default via 2607:5300:203:39ff:ff:ff:ff:ff proto static')
self.assertRegex(output, 'inet6 .* scope link')
output = check_output('ip -4 route show dev dummy98')
print(output)
- self.assertEqual(output, '10.2.0.0/16 proto kernel scope link src 10.2.3.4')
+ self.assertRegex(output, '10.2.0.0/16 proto kernel scope link src 10.2.3.4')
output = check_output('ip -6 route show dev dummy98')
print(output)
self.assertRegex(output, 'default via 2607:5300:203:39ff:ff:ff:ff:ff proto static')
self.assertRegex(output, 'quanta 1 2 3 4 5')
self.assertRegex(output, 'priomap 3 4 5 6 7')
+ @expectedFailureIfNetdevsimWithSRIOVIsNotAvailable()
+ def test_sriov(self):
+ call('rmmod netdevsim', stderr=subprocess.DEVNULL)
+ call('modprobe netdevsim', stderr=subprocess.DEVNULL)
+ with open('/sys/bus/netdevsim/new_device', mode='w') as f:
+ f.write('99 1')
+
+ call('udevadm settle')
+ call('udevadm info -w10s /sys/devices/netdevsim99/net/eni99np1', stderr=subprocess.DEVNULL)
+ with open('/sys/class/net/eni99np1/device/sriov_numvfs', mode='w') as f:
+ f.write('3')
+
+ copy_unit_to_networkd_unit_path('25-sriov.network')
+ start_networkd()
+ self.wait_online(['eni99np1:routable'])
+
+ output = check_output('ip link show dev eni99np1')
+ print(output)
+ self.assertRegex(output,
+ 'vf 0 .*00:11:22:33:44:55.*vlan 5, qos 1, vlan protocol 802.1ad, spoof checking on, link-state enable, trust on, query_rss on\n *'
+ 'vf 1 .*00:11:22:33:44:56.*vlan 6, qos 2, spoof checking off, link-state disable, trust off, query_rss off\n *'
+ 'vf 2 .*00:11:22:33:44:57.*vlan 7, qos 3, spoof checking off, link-state auto, trust off, query_rss off'
+ )
+
+ call('rmmod netdevsim', stderr=subprocess.DEVNULL)
+
class NetworkdStateFileTests(unittest.TestCase, Utilities):
links = [
'dummy98',