From nobody Sun May 5 09:47:15 2024 Delivered-To: importer@patchew.org Received-SPF: pass (zohomail.com: domain of redhat.com designates 170.10.133.124 as permitted sender) client-ip=170.10.133.124; envelope-from=libvir-list-bounces@redhat.com; helo=us-smtp-delivery-124.mimecast.com; Authentication-Results: mx.zohomail.com; spf=pass (zohomail.com: domain of redhat.com designates 170.10.133.124 as permitted sender) smtp.mailfrom=libvir-list-bounces@redhat.com; dmarc=fail(p=none dis=none) header.from=canonical.com Return-Path: Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.133.124]) by mx.zohomail.com with SMTPS id 163657408026365.33605950378853; Wed, 10 Nov 2021 11:54:40 -0800 (PST) Received: from mimecast-mx01.redhat.com (mimecast-mx01.redhat.com [209.132.183.4]) (Using TLS) by relay.mimecast.com with ESMTP id us-mta-558-5ylHeYaNMRG-0RRIocjF7w-1; Wed, 10 Nov 2021 14:54:37 -0500 Received: from smtp.corp.redhat.com (int-mx04.intmail.prod.int.phx2.redhat.com [10.5.11.14]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx01.redhat.com (Postfix) with ESMTPS id 644A587D541; Wed, 10 Nov 2021 19:54:30 +0000 (UTC) Received: from colo-mx.corp.redhat.com (colo-mx01.intmail.prod.int.phx2.redhat.com [10.5.11.20]) by smtp.corp.redhat.com (Postfix) with ESMTPS id 21DBF5DA61; Wed, 10 Nov 2021 19:54:29 +0000 (UTC) Received: from lists01.pubmisc.prod.ext.phx2.redhat.com (lists01.pubmisc.prod.ext.phx2.redhat.com [10.5.19.33]) by colo-mx.corp.redhat.com (Postfix) with ESMTP id 760E318199EE; Wed, 10 Nov 2021 19:54:25 +0000 (UTC) Received: from smtp.corp.redhat.com (int-mx01.intmail.prod.int.rdu2.redhat.com [10.11.54.1]) by lists01.pubmisc.prod.ext.phx2.redhat.com (8.13.8/8.13.8) with ESMTP id 1AAJgETQ009544 for ; Wed, 10 Nov 2021 14:42:14 -0500 Received: by smtp.corp.redhat.com (Postfix) id 40607400DB2F; Wed, 10 Nov 2021 19:42:14 +0000 (UTC) Received: from mimecast-mx02.redhat.com (mimecast01.extmail.prod.ext.rdu2.redhat.com [10.11.55.17]) by smtp.corp.redhat.com (Postfix) with ESMTPS id 3AD284010E9F for ; Wed, 10 Nov 2021 19:42:14 +0000 (UTC) Received: from us-smtp-1.mimecast.com (us-smtp-delivery-1.mimecast.com [205.139.110.120]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mimecast-mx02.redhat.com (Postfix) with ESMTPS id 1F58985A5B5 for ; Wed, 10 Nov 2021 19:42:14 +0000 (UTC) Received: from smtp-relay-internal-1.canonical.com (smtp-relay-internal-1.canonical.com [185.125.188.123]) (Using TLS) by relay.mimecast.com with ESMTP id us-mta-4-Ev1JWxK5MJS1VYv6q0qqCg-1; Wed, 10 Nov 2021 14:42:11 -0500 Received: from mail-lf1-f72.google.com (mail-lf1-f72.google.com [209.85.167.72]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by smtp-relay-internal-1.canonical.com (Postfix) with ESMTPS id 1BEB23F1CA for ; Wed, 10 Nov 2021 19:42:10 +0000 (UTC) Received: by mail-lf1-f72.google.com with SMTP id h40-20020a0565123ca800b00402514d959fso1681653lfv.7 for ; Wed, 10 Nov 2021 11:42:10 -0800 (PST) Received: from ws.lan.d-node.is ([95.165.29.203]) by smtp.gmail.com with ESMTPSA id m9sm62342ljg.80.2021.11.10.11.42.07 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 10 Nov 2021 11:42:07 -0800 (PST) X-MC-Unique: 5ylHeYaNMRG-0RRIocjF7w-1 X-MC-Unique: Ev1JWxK5MJS1VYv6q0qqCg-1 X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:from:to:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=lppsWKqUqDqbQ0SXSn9UwVKKT4y4HYGsuTcL21sJ8Og=; b=ijJ9jdJ1jLYMYUkbuwIo+xgpEsoZB2dIU0np0Rn2X7rDV7kmrFdcu0nm2PPG0bfyUu MZSsUf5tdD4X4W0DYvlH6rT/vI5970BwM8vw1gmgGM+1lHcQG5cCMmIdKo0qMp+Tdhqn A3pzCuXibSPHtXgM6Rj4jw7QlZH94UmqA/tRoB2AEYy3BJRqZgIiIqLEaPux3bDM+02E Lp3cmMyarMl4sJw0Ys2tCewMKNJEfT1tQVxu2BSidKMykRc4AfZPhTkwKKxkq81AeVsT tvE0pKWrouwNud3mNXORaRSVKk2MxEmkFxoVMTAfFhmA3ufIj+Ofyrea1Uwmtak2N5ZO /IEQ== X-Gm-Message-State: AOAM530h4a3uP1hGCRdvOSyTcu+UH6yCMvOSD1T+CcPJGeWMifwF9yuT My/NqGHtciqgPe6N+dm/SwPgtLh4sgYiJ5fxg95vXAXta3hUZrc1Tr1zfs1qovpRa6f9z4O5fCp p5RvYU+hnobrN9NKLaeFKJgvRemtrnhXKMw== X-Received: by 2002:a05:6512:2346:: with SMTP id p6mr1460016lfu.360.1636573328514; Wed, 10 Nov 2021 11:42:08 -0800 (PST) X-Google-Smtp-Source: ABdhPJw79nMsyNFJbKQwt/B8RZDCjGkbXAK5AOTLCWZo8hbaH4nu7FknlxEfVhT8vsCIhZdVAvO09w== X-Received: by 2002:a05:6512:2346:: with SMTP id p6mr1459980lfu.360.1636573328061; Wed, 10 Nov 2021 11:42:08 -0800 (PST) From: Dmitrii Shcherbakov To: dmitrii.shcherbakov@canonical.com, libvir-list@redhat.com Subject: [libvirt PATCH v2 1/1] Ignore EPERM on attempts to clear VF VLAN ID Date: Wed, 10 Nov 2021 22:42:06 +0300 Message-Id: <20211110194206.433227-2-dmitrii.shcherbakov@canonical.com> In-Reply-To: <20211110194206.433227-1-dmitrii.shcherbakov@canonical.com> References: <20211110194206.433227-1-dmitrii.shcherbakov@canonical.com> MIME-Version: 1.0 X-Mimecast-Impersonation-Protect: Policy=CLT - Impersonation Protection Definition; Similar Internal Domain=false; Similar Monitored External Domain=false; Custom External Domain=false; Mimecast External Domain=false; Newly Observed Domain=false; Internal User Name=false; Custom Display Name List=false; Reply-to Address Mismatch=false; Targeted Threat Dictionary=false; Mimecast Threat Dictionary=false; Custom Threat Dictionary=false X-Scanned-By: MIMEDefang 2.84 on 10.11.54.1 X-MIME-Autoconverted: from quoted-printable to 8bit by lists01.pubmisc.prod.ext.phx2.redhat.com id 1AAJgETQ009544 X-loop: libvir-list@redhat.com X-BeenThere: libvir-list@redhat.com X-Mailman-Version: 2.1.12 Precedence: junk List-Id: Development discussions about the libvirt library & tools List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: libvir-list-bounces@redhat.com Errors-To: libvir-list-bounces@redhat.com X-Scanned-By: MIMEDefang 2.79 on 10.5.11.14 Authentication-Results: relay.mimecast.com; auth=pass smtp.auth=CUSA124A263 smtp.mailfrom=libvir-list-bounces@redhat.com X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com Content-Transfer-Encoding: quoted-printable X-ZM-MESSAGEID: 1636574082185100003 Content-Type: text/plain; charset="utf-8" SmartNIC DPUs may not expose some privileged eswitch operations to the hypervisor hosts. For example, this happens with Bluefield devices running in the ECPF (default) mode for security reasons. While VF MAC address programming is possible via an RTM_SETLINK operation, trying to set a VLAN ID in the same operation will fail with EPERM. The equivalent ip link commands below provide an illustration: 1. This works: sudo ip link set enp130s0f0 vf 2 mac de:ad:be:ef:ca:fe 2. Setting (or clearing) a VLAN fails with EPERM: sudo ip link set enp130s0f0 vf 2 vlan 0 RTNETLINK answers: Operation not permitted 3. This is what Libvirt attempts to do today (when trying to clear a VF VLAN at the same time as programming a VF MAC). sudo ip link set enp130s0f0 vf 2 vlan 0 mac de:ad:be:ef:ca:fe RTNETLINK answers: Operation not permitted If setting an explicit VLAN ID results in an EPERM, clearing a VLAN (setting a VLAN ID to 0) can be handled gracefully by ignoring the EPERM error with the rationale being that if we cannot set this state in the first place, we cannot clear it either. Thus, virNetDevSetVfConfig is split into two distinct functions. If clearing a VLAN ID fails with EPERM, the error is simply ignored. Both new functions rely virNetDevSendVfSetLinkRequest that implements common functionality related to formatting a request, sending it and handling error conditions and returns 0 or an error since in both cases the payload is either NLMSG_DONE (no error) or NLMSG_ERROR where an error message is needed by the caller to handle known cases appropriately. This function allows the conditional code to be unit tested. An alternative to this could be providing a higher level control plane mechanism that would provide metadata about a device being remotely managed in which case Libvirt would avoid trying to set or clear a VLAN ID. This would be more complicated since other software (like Nova in the OpenStack case) would have to annotate every guest device with an attribute indicating whether a device is remotely managed or not based on operator provided configuration so that Libvirt can act on this and avoid VLAN programming. Signed-off-by: Dmitrii Shcherbakov --- src/libvirt_private.syms | 7 ++ src/util/virnetdev.c | 199 ++++++++++++++++++++++------------- src/util/virnetdevpriv.h | 44 ++++++++ tests/virnetdevtest.c | 222 ++++++++++++++++++++++++++++++++++++++- 4 files changed, 396 insertions(+), 76 deletions(-) create mode 100644 src/util/virnetdevpriv.h diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 9ee8fda25f..4b8e3e2b15 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -2851,6 +2851,13 @@ virNetDevOpenvswitchSetTimeout; virNetDevOpenvswitchUpdateVlan; =20 =20 +#util/virnetdevpriv.h +virNetDevSendVfSetLinkRequest; +virNetDevSetVfConfig; +virNetDevSetVfMac; +virNetDevSetVfVlan; + + # util/virnetdevtap.h virNetDevTapAttachBridge; virNetDevTapCreate; diff --git a/src/util/virnetdev.c b/src/util/virnetdev.c index 58f7360a0f..04b1ee9951 100644 --- a/src/util/virnetdev.c +++ b/src/util/virnetdev.c @@ -19,7 +19,9 @@ #include #include =20 -#include "virnetdev.h" +#define LIBVIRT_VIRNETDEVPRIV_H_ALLOW + +#include "virnetdevpriv.h" #include "viralloc.h" #include "virnetlink.h" #include "virmacaddr.h" @@ -1527,16 +1529,12 @@ static struct nla_policy ifla_vfstats_policy[IFLA_V= F_STATS_MAX+1] =3D { [IFLA_VF_STATS_MULTICAST] =3D { .type =3D NLA_U64 }, }; =20 - -static int -virNetDevSetVfConfig(const char *ifname, int vf, - const virMacAddr *macaddr, int vlanid, - bool *allowRetry) +int +virNetDevSendVfSetLinkRequest(const char *ifname, int vfInfoType, + const void *payload, const size_t payloadLen) { - int rc =3D -1; - char macstr[VIR_MAC_STRING_BUFLEN]; g_autofree struct nlmsghdr *resp =3D NULL; - struct nlmsgerr *err; + struct nlmsgerr *err =3D NULL; unsigned int recvbuflen =3D 0; struct nl_msg *nl_msg; struct nlattr *vfinfolist, *vfinfo; @@ -1544,9 +1542,11 @@ virNetDevSetVfConfig(const char *ifname, int vf, .ifi_family =3D AF_UNSPEC, .ifi_index =3D -1, }; + int rc =3D 0; =20 - if (!macaddr && vlanid < 0) + if (payload =3D=3D NULL || payloadLen =3D=3D 0) { return -1; + } =20 nl_msg =3D virNetlinkMsgNew(RTM_SETLINK, NLM_F_REQUEST); =20 @@ -1564,33 +1564,8 @@ virNetDevSetVfConfig(const char *ifname, int vf, if (!(vfinfo =3D nla_nest_start(nl_msg, IFLA_VF_INFO))) goto buffer_too_small; =20 - if (macaddr) { - struct ifla_vf_mac ifla_vf_mac =3D { - .vf =3D vf, - .mac =3D { 0, }, - }; - - virMacAddrGetRaw(macaddr, ifla_vf_mac.mac); - - if (nla_put(nl_msg, IFLA_VF_MAC, sizeof(ifla_vf_mac), - &ifla_vf_mac) < 0) - goto buffer_too_small; - } - - if (vlanid >=3D 0) { - struct ifla_vf_vlan ifla_vf_vlan =3D { - .vf =3D vf, - .vlan =3D vlanid, - .qos =3D 0, - }; - - if (nla_put(nl_msg, IFLA_VF_VLAN, sizeof(ifla_vf_vlan), - &ifla_vf_vlan) < 0) - goto buffer_too_small; - } - - nla_nest_end(nl_msg, vfinfo); - nla_nest_end(nl_msg, vfinfolist); + if (nla_put(nl_msg, vfInfoType, payloadLen, payload) < 0) + goto buffer_too_small; =20 if (virNetlinkCommand(nl_msg, &resp, &recvbuflen, 0, 0, NETLINK_ROUTE, 0) < 0) @@ -1600,48 +1575,20 @@ virNetDevSetVfConfig(const char *ifname, int vf, goto malformed_resp; =20 switch (resp->nlmsg_type) { - case NLMSG_ERROR: - err =3D (struct nlmsgerr *)NLMSG_DATA(resp); - if (resp->nlmsg_len < NLMSG_LENGTH(sizeof(*err))) + case NLMSG_ERROR: + err =3D (struct nlmsgerr *)NLMSG_DATA(resp); + if (resp->nlmsg_len < NLMSG_LENGTH(sizeof(*err))) + goto malformed_resp; + rc =3D err->error; + break; + case NLMSG_DONE: + rc =3D 0; + break; + default: goto malformed_resp; - - /* if allowRetry is true and the error was EINVAL, then - * silently return a failure so the caller can retry with a - * different MAC address - */ - if (err->error =3D=3D -EINVAL && *allowRetry && - macaddr && !virMacAddrCmp(macaddr, &zeroMAC)) { - goto cleanup; - } else if (err->error) { - /* other errors are permanent */ - virReportSystemError(-err->error, - _("Cannot set interface MAC/vlanid to %s/= %d " - "for ifname %s vf %d"), - (macaddr - ? virMacAddrFormat(macaddr, macstr) - : "(unchanged)"), - vlanid, - ifname ? ifname : "(unspecified)", - vf); - *allowRetry =3D false; /* no use retrying */ - goto cleanup; - } - break; - - case NLMSG_DONE: - break; - - default: - goto malformed_resp; } =20 - rc =3D 0; cleanup: - VIR_DEBUG("RTM_SETLINK %s vf %d MAC=3D%s vlanid=3D%d - %s", - ifname, vf, - macaddr ? virMacAddrFormat(macaddr, macstr) : "(unchanged)", - vlanid, rc < 0 ? "Fail" : "Success"); - nlmsg_free(nl_msg); return rc; =20 @@ -1656,6 +1603,108 @@ virNetDevSetVfConfig(const char *ifname, int vf, goto cleanup; } =20 +int +virNetDevSetVfVlan(const char *ifname, int vf, int vlanid) +{ + int rc =3D -1; + int requestError =3D 0; + + struct ifla_vf_vlan ifla_vf_vlan =3D { + .vf =3D vf, + .vlan =3D vlanid, + .qos =3D 0, + }; + + /* VLAN ids 0 and 4095 are reserved per 802.1Q but are valid values. */ + if (vlanid < 0 || vlanid > 4095) + return -1; + + requestError =3D virNetDevSendVfSetLinkRequest(ifname, IFLA_VF_VLAN, + &ifla_vf_vlan, sizeof(ifl= a_vf_vlan)); + + /* If vlanid is 0 - we are attempting to clear an existing VLAN id. + * An EPERM received at this stage is an indicator that the embedded + * switch is not exposed to this host and the network driver is not + * able to set a VLAN for a VF. */ + if (requestError =3D=3D -EPERM && vlanid =3D=3D 0) { + rc =3D 0; + } else if (requestError) { + virReportSystemError(-requestError, + _("Cannot set interface vlanid to %d " + "for ifname %s vf %d"), + vlanid, ifname ? ifname : "(unspecified)", vf= ); + rc =3D -1; + } else { + rc =3D 0; + } + VIR_DEBUG("RTM_SETLINK %s vf %d vlanid=3D%d - %s", + ifname, vf, + vlanid, rc < 0 ? "Fail" : "Success"); + return rc; +} + +int +virNetDevSetVfMac(const char *ifname, int vf, + const virMacAddr *macaddr, + bool *allowRetry) +{ + int rc =3D 0; + int requestError =3D 0; + char macstr[VIR_MAC_STRING_BUFLEN]; + + struct ifla_vf_mac ifla_vf_mac =3D { + .vf =3D vf, + .mac =3D { 0, }, + }; + + if (macaddr =3D=3D NULL || allowRetry =3D=3D NULL) + return -1; + + virMacAddrGetRaw(macaddr, ifla_vf_mac.mac); + + requestError =3D virNetDevSendVfSetLinkRequest(ifname, IFLA_VF_MAC, + &ifla_vf_mac, sizeof(ifla= _vf_mac)); + /* if allowRetry is true and the error was EINVAL, then + * silently return a failure so the caller can retry with a + * different MAC address. */ + if (requestError =3D=3D -EINVAL && *allowRetry && !virMacAddrCmp(macad= dr, &zeroMAC)) { + rc =3D -2; + } else if (requestError) { + /* other errors are permanent */ + virReportSystemError(-requestError, + _("Cannot set interface MAC to %s " + "for ifname %s vf %d"), + (macaddr + ? virMacAddrFormat(macaddr, macstr) + : "(unchanged)"), + ifname ? ifname : "(unspecified)", + vf); + *allowRetry =3D false; /* no use retrying */ + rc =3D -1; + } else { + rc =3D 0; + } + VIR_DEBUG("RTM_SETLINK %s vf %d MAC=3D%s - %s", + ifname, vf, + macaddr ? virMacAddrFormat(macaddr, macstr) : "(unchanged)", + rc < 0 ? "Fail" : "Success"); + return rc; +} + +int +virNetDevSetVfConfig(const char *ifname, int vf, + const virMacAddr *macaddr, int vlanid, + bool *allowRetry) +{ + int rc =3D 0; + if ((rc =3D virNetDevSetVfMac(ifname, vf, macaddr, allowRetry)) < 0) { + return rc; + } else if ((rc =3D virNetDevSetVfVlan(ifname, vf, vlanid)) < 0) { + return rc; + } + return rc; +} + /** * virNetDevParseVfInfo: * Get the VF interface information from kernel by netlink, To make netlink diff --git a/src/util/virnetdevpriv.h b/src/util/virnetdevpriv.h new file mode 100644 index 0000000000..7990bf5269 --- /dev/null +++ b/src/util/virnetdevpriv.h @@ -0,0 +1,44 @@ +/* + * virnetdevpriv.h: private virnetdev header for unit testing + * + * Copyright (C) 2021 Canonical Ltd. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; If not, see + * . + */ + +#ifndef LIBVIRT_VIRNETDEVPRIV_H_ALLOW +# error "virnetdevpriv.h may only be included by virnetdev.c or test suite= s" +#endif /* LIBVIRT_VIRNETDEVPRIV_H_ALLOW */ + +#pragma once + +#include "virnetdev.h" + +int +virNetDevSendVfSetLinkRequest(const char *ifname, int vfInfoType, + const void *payload, const size_t payloadLen= ); + +int +virNetDevSetVfVlan(const char *ifname, int vf, int vlanid); + +int +virNetDevSetVfMac(const char *ifname, int vf, + const virMacAddr *macaddr, + bool *allowRetry); + +int +virNetDevSetVfConfig(const char *ifname, int vf, + const virMacAddr *macaddr, int vlanid, + bool *allowRetry); diff --git a/tests/virnetdevtest.c b/tests/virnetdevtest.c index aadbeb1ef4..bdaa94e83c 100644 --- a/tests/virnetdevtest.c +++ b/tests/virnetdevtest.c @@ -18,11 +18,17 @@ =20 #include =20 +#include "internal.h" #include "testutils.h" =20 +#include "virnetlink.h" + +#define LIBVIRT_VIRNETDEVPRIV_H_ALLOW + #ifdef __linux__ =20 -# include "virnetdev.h" +# include "virmock.h" +# include "virnetdevpriv.h" =20 # define VIR_FROM_THIS VIR_FROM_NONE =20 @@ -59,6 +65,211 @@ testVirNetDevGetLinkInfo(const void *opaque) return 0; } =20 +int +(*real_virNetDevSendVfSetLinkRequest)(const char *ifname, int vfInfoType, + const void *payload, const size_t pa= yloadLen); + +int +(*real_virNetDevSetVfMac)(const char *ifname, int vf, const virMacAddr *ma= caddr, bool *allowRetry); + +int +(*real_virNetDevSetVfVlan)(const char *ifname, int vf, int vlanid); + +static void +init_syms(void) +{ + VIR_MOCK_REAL_INIT(virNetDevSendVfSetLinkRequest); + VIR_MOCK_REAL_INIT(virNetDevSetVfMac); + VIR_MOCK_REAL_INIT(virNetDevSetVfVlan); +} + +int +virNetDevSetVfMac(const char *ifname, int vf, + const virMacAddr *macaddr, + bool *allowRetry) +{ + init_syms(); + + if (STREQ_NULLABLE(ifname, "fakeiface-macerror")) { + return -1; + } else if (STREQ_NULLABLE(ifname, "fakeiface-altmacerror")) { + return -2; + } else if (STREQ_NULLABLE(ifname, "fakeiface-macerror-novlanerror")) { + return -1; + } else if (STREQ_NULLABLE(ifname, "fakeiface-macerror-vlanerror")) { + return -1; + } else if (STREQ_NULLABLE(ifname, "fakeiface-nomacerror-novlanerror"))= { + return 0; + } + return real_virNetDevSetVfMac(ifname, vf, macaddr, allowRetry); +} + +int +virNetDevSetVfVlan(const char *ifname, int vf, int vlanid) +{ + init_syms(); + + if (STREQ_NULLABLE(ifname, "fakeiface-macerror-vlanerror")) { + return -1; + } else if (STREQ_NULLABLE(ifname, "fakeiface-macerror-novlanerror")) { + return 0; + } else if (STREQ_NULLABLE(ifname, "fakeiface-nomacerror-novlanerror"))= { + return 0; + } + return real_virNetDevSetVfVlan(ifname, vf, vlanid); +} + +int +virNetDevSendVfSetLinkRequest(const char *ifname, int vfInfoType, + const void *payload, const size_t payloadLen) +{ + init_syms(); + + if (STREQ_NULLABLE(ifname, "fakeiface-eperm")) { + return -EPERM; + } else if (STREQ_NULLABLE(ifname, "fakeiface-eagain")) { + return -EAGAIN; + } else if (STREQ_NULLABLE(ifname, "fakeiface-einval")) { + return -EINVAL; + } else if (STREQ_NULLABLE(ifname, "fakeiface-ok")) { + return 0; + } + return real_virNetDevSendVfSetLinkRequest(ifname, vfInfoType, payload,= payloadLen); +} + +static int +testVirNetDevSetVfMac(const void *opaque G_GNUC_UNUSED) +{ + struct testCase { + const char *ifname; + const int vf_num; + const virMacAddr macaddr; + bool allow_retry; + const int rc; + }; + size_t i =3D 0; + int rc =3D 0; + struct testCase testCases[] =3D { + { .ifname =3D "fakeiface-ok", .vf_num =3D 1, + .macaddr =3D { .addr =3D { 0, 0, 0, 0, 0, 0 } }, .allow_retry = =3D false, .rc =3D 0 }, + { .ifname =3D "fakeiface-ok", .vf_num =3D 2, + .macaddr =3D { .addr =3D { 0, 0, 0, 7, 7, 7 } }, .allow_retry = =3D false, .rc =3D 0 }, + { .ifname =3D "fakeiface-ok", .vf_num =3D 3, + .macaddr =3D { .addr =3D { 0, 0, 0, 0, 0, 0 } }, .allow_retry = =3D true, .rc =3D 0 }, + { .ifname =3D "fakeiface-ok", .vf_num =3D 4, + .macaddr =3D { .addr =3D { 0, 0, 0, 7, 7, 7 } }, .allow_retry = =3D true, .rc =3D 0 }, + { .ifname =3D "fakeiface-eperm", .vf_num =3D 5, + .macaddr =3D { .addr =3D { 0, 0, 0, 0, 0, 0 } }, .allow_retry = =3D false, .rc =3D -1 }, + { .ifname =3D "fakeiface-einval", .vf_num =3D 6, + .macaddr =3D { .addr =3D { 0, 0, 0, 0, 0, 0 } }, .allow_retry = =3D false, .rc =3D -1 }, + { .ifname =3D "fakeiface-einval", .vf_num =3D 7, + .macaddr =3D { .addr =3D { 0, 0, 0, 0, 0, 0 } }, .allow_retry = =3D true, .rc =3D -2 }, + { .ifname =3D "fakeiface-einval", .vf_num =3D 8, + .macaddr =3D { .addr =3D { 0, 0, 0, 7, 7, 7 } }, .allow_retry = =3D false, .rc =3D -1 }, + { .ifname =3D "fakeiface-einval", .vf_num =3D 9, + .macaddr =3D { .addr =3D { 0, 0, 0, 7, 7, 7 } }, .allow_retry = =3D true, .rc =3D -1 }, + }; + + for (i =3D 0; i < sizeof(testCases) / sizeof(struct testCase); ++i) { + rc =3D virNetDevSetVfMac(testCases[i].ifname, testCases[i].vf_num, + &testCases[i].macaddr, &testCases[i].allow_r= etry); + if (rc !=3D testCases[i].rc) { + return -1; + } + } + return 0; +} + +static int +testVirNetDevSetVfMissingMac(const void *opaque G_GNUC_UNUSED) +{ + bool allowRetry =3D false; + /* NULL MAC pointer. */ + if (virNetDevSetVfMac("fakeiface-ok", 1, NULL, &allowRetry) !=3D -1) { + return -1; + } + allowRetry =3D true; + if (virNetDevSetVfMac("fakeiface-ok", 1, NULL, &allowRetry) !=3D -1) { + return -1; + } + return 0; +} + +static int +testVirNetDevSetVfVlan(const void *opaque G_GNUC_UNUSED) +{ + struct testCase { + const char *ifname; + const int vf_num; + const int vlan_id; + const int rc; + }; + size_t i =3D 0; + int rc =3D 0; + const struct testCase testCases[] =3D { + /* VLAN ID is out of range of valid values (0-4095). */ + { .ifname =3D "enxdeadbeefcafe", .vf_num =3D 1, .vlan_id =3D 4096,= .rc =3D -1 }, + { .ifname =3D "enxdeadbeefcafe", .vf_num =3D 1, .vlan_id =3D -1, .= rc =3D -1 }, + /* Requests with vlan id 0 (trying to clear a VLAN that return EPE= RM should + * not result in an error. They simply need to be ignored because = it may + * indicate that the NIC switch functionality is not exposed to th= e host. */ + { .ifname =3D "fakeiface-eperm", .vf_num =3D 1, .vlan_id =3D 0, .r= c =3D 0 }, + /* Requests with vlan id 0 with an error output different from EPE= RM are failures. */ + { .ifname =3D "fakeiface-eagain", .vf_num =3D 1, .vlan_id =3D 0, .= rc =3D -1 }, + /* Successful requests with vlan id 0 need to have a zero return c= ode. */ + { .ifname =3D "fakeiface-ok", .vf_num =3D 1, .vlan_id =3D 0, .rc = =3D 0 }, + /* Requests with a non-zero VLAN ID that result in an EPERM need t= o result in failures. + * failures. */ + { .ifname =3D "fakeiface-eperm", .vf_num =3D 1, .vlan_id =3D 42, .= rc =3D -1 }, + /* Requests with a non-zero VLAN ID that result in some other erro= rs need to result in + * failures. */ + { .ifname =3D "fakeiface-eagain", .vf_num =3D 1, .vlan_id =3D 42, = .rc =3D -1 }, + /* Successful requests with a non-zero VLAN ID */ + { .ifname =3D "fakeiface-ok", .vf_num =3D 1, .vlan_id =3D 42, .rc = =3D 0 }, + }; + + for (i =3D 0; i < sizeof(testCases) / sizeof(struct testCase); ++i) { + rc =3D virNetDevSetVfVlan(testCases[i].ifname, testCases[i].vf_num,= testCases[i].vlan_id); + if (rc !=3D testCases[i].rc) { + return -1; + } + } + + return 0; +} + +static int +testVirNetDevSetVfConfig(const void *opaque G_GNUC_UNUSED) +{ + struct testCase { + const char *ifname; + const int rc; + }; + int rc =3D 0; + size_t i =3D 0; + /* Nested functions are mocked so dummy values are used. */ + const virMacAddr mac =3D { .addr =3D { 0xDE, 0xAD, 0xBE, 0xEF, 0xCA, 0= xFE }}; + const int vfNum =3D 1; + const int vlanid =3D 0; + bool *allowRetry =3D NULL; + + const struct testCase testCases[] =3D { + { .ifname =3D "fakeiface-macerror", .rc =3D -1 }, + { .ifname =3D "fakeiface-altmacerror", .rc =3D -2 }, + { .ifname =3D "fakeiface-macerror-novlanerror", .rc =3D -1 }, + { .ifname =3D "fakeiface-macerror-vlanerror", .rc =3D -1 }, + { .ifname =3D "fakeiface-nomacerror-novlanerror", .rc =3D 0 }, + }; + + for (i =3D 0; i < sizeof(testCases) / sizeof(struct testCase); ++i) { + rc =3D virNetDevSetVfConfig(testCases[i].ifname, vfNum, &mac, vlani= d, allowRetry); + if (rc !=3D testCases[i].rc) { + return -1; + } + } + return 0; +} + static int mymain(void) { @@ -76,6 +287,15 @@ mymain(void) DO_TEST_LINK("lo", VIR_NETDEV_IF_STATE_UNKNOWN, 0); DO_TEST_LINK("eth0-broken", VIR_NETDEV_IF_STATE_DOWN, 0); =20 + if (virTestRun("Set VF MAC", testVirNetDevSetVfMac, NULL) < 0) + ret =3D -1; + if (virTestRun("Set VF MAC: missing MAC pointer", testVirNetDevSetVfMi= ssingMac, NULL) < 0) + ret =3D -1; + if (virTestRun("Set VF VLAN", testVirNetDevSetVfVlan, NULL) < 0) + ret =3D -1; + if (virTestRun("Set VF Config", testVirNetDevSetVfConfig, NULL) < 0) + ret =3D -1; + return ret =3D=3D 0 ? EXIT_SUCCESS : EXIT_FAILURE; } =20 --=20 2.32.0