util: support functions for mac/portprofile associations on hostdev

This patch adds the following:

- functions to set and get vf configs
- Functions to replace and store vf configs (Only mac address is handled today.
  But the functions can be easily extended for vlans and other vf configs)
- function to dump link dev info (This is moved from virnetdevvportprofile.c)

Signed-off-by: Roopa Prabhu <roprabhu@cisco.com>
This commit is contained in:
Roopa Prabhu 2012-03-05 17:12:34 -08:00 committed by Laine Stump
parent b8b702734c
commit 5095bf06f1
4 changed files with 569 additions and 1 deletions

View File

@ -1213,11 +1213,15 @@ virNetDevGetMTU;
virNetDevGetPhysicalFunction;
virNetDevGetVLanID;
virNetDevGetVirtualFunctionIndex;
virNetDevGetVirtualFunctionInfo;
virNetDevGetVirtualFunctions;
virNetDevIsOnline;
virNetDevIsVirtualFunction;
virNetDevLinkDump;
virNetDevReplaceMacAddress;
virNetDevReplaceNetConfig;
virNetDevRestoreMacAddress;
virNetDevRestoreNetConfig;
virNetDevSetIPv4Address;
virNetDevSetMAC;
virNetDevSetMTU;

View File

@ -1127,8 +1127,47 @@ virNetDevGetPhysicalFunction(const char *ifname, char **pfname)
return ret;
}
#else /* !__linux__ */
/**
* virNetDevGetVirtualFunctionInfo:
* @vfname: name of the virtual function interface
* @pfname: name of the physical function
* @vf: vf index
*
* Returns 0 on success, -errno on failure.
*
*/
int
virNetDevGetVirtualFunctionInfo(const char *vfname, char **pfname,
int *vf)
{
char *pf_sysfs_path = NULL, *vf_sysfs_path = NULL;
int ret = -1;
*pfname = NULL;
if (virNetDevGetPhysicalFunction(vfname, pfname) < 0)
return ret;
if (virNetDevSysfsFile(&pf_sysfs_path, *pfname, "device") < 0)
goto cleanup;
if (virNetDevSysfsFile(&vf_sysfs_path, vfname, "device") < 0)
goto cleanup;
ret = pciGetVirtualFunctionIndex(pf_sysfs_path, vf_sysfs_path, vf);
cleanup:
if (ret < 0)
VIR_FREE(*pfname);
VIR_FREE(vf_sysfs_path);
VIR_FREE(pf_sysfs_path);
return ret;
}
#else /* !__linux__ */
int
virNetDevGetVirtualFunctions(const char *pfname ATTRIBUTE_UNUSED,
char ***vfname ATTRIBUTE_UNUSED,
@ -1165,4 +1204,509 @@ virNetDevGetPhysicalFunction(const char *ifname ATTRIBUTE_UNUSED,
_("Unable to get physical function status on this platform"));
return -1;
}
int
virNetDevGetVirtualFunctionInfo(const char *vfname ATTRIBUTE_UNUSED,
char **pfname ATTRIBUTE_UNUSED,
int *vf ATTRIBUTE_UNUSED)
{
virReportSystemError(ENOSYS, "%s",
_("Unable to get virtual function info on this platform"));
return -1;
}
#endif /* !__linux__ */
#if defined(__linux__) && defined(HAVE_LIBNL)
static struct nla_policy ifla_vf_policy[IFLA_VF_MAX+1] = {
[IFLA_VF_MAC] = { .type = NLA_UNSPEC,
.maxlen = sizeof(struct ifla_vf_mac) },
[IFLA_VF_VLAN] = { .type = NLA_UNSPEC,
.maxlen = sizeof(struct ifla_vf_vlan) },
};
/**
* virNetDevLinkDump:
*
* @ifname: The name of the interface; only use if ifindex < 0
* @ifindex: The interface index; may be < 0 if ifname is given
* @nltarget_kernel: whether to send the message to the kernel or another
* process
* @nlattr: pointer to a pointer of netlink attributes that will contain
* the results
* @recvbuf: Pointer to the buffer holding the returned netlink response
* message; free it, once not needed anymore
* @getPidFunc: Pointer to a function that will be invoked if the kernel
* is not the target of the netlink message but it is to be
* sent to another process.
*
* Get information about an interface given its name or index.
*
* Returns 0 on success, -1 on fatal error.
*/
int
virNetDevLinkDump(const char *ifname, int ifindex,
bool nltarget_kernel, struct nlattr **tb,
unsigned char **recvbuf,
uint32_t (*getPidFunc)(void))
{
int rc = 0;
struct nlmsghdr *resp;
struct nlmsgerr *err;
struct ifinfomsg ifinfo = {
.ifi_family = AF_UNSPEC,
.ifi_index = ifindex
};
unsigned int recvbuflen;
uint32_t pid = 0;
struct nl_msg *nl_msg;
*recvbuf = NULL;
if (ifname && ifindex <= 0 && virNetDevGetIndex(ifname, &ifindex) < 0)
return -1;
ifinfo.ifi_index = ifindex;
nl_msg = nlmsg_alloc_simple(RTM_GETLINK, NLM_F_REQUEST);
if (!nl_msg) {
virReportOOMError();
return -1;
}
if (nlmsg_append(nl_msg, &ifinfo, sizeof(ifinfo), NLMSG_ALIGNTO) < 0)
goto buffer_too_small;
if (ifname) {
if (nla_put(nl_msg, IFLA_IFNAME, strlen(ifname)+1, ifname) < 0)
goto buffer_too_small;
}
if (!nltarget_kernel) {
pid = getPidFunc();
if (pid == 0) {
rc = -1;
goto cleanup;
}
}
if (virNetlinkCommand(nl_msg, recvbuf, &recvbuflen, pid) < 0) {
rc = -1;
goto cleanup;
}
if (recvbuflen < NLMSG_LENGTH(0) || *recvbuf == NULL)
goto malformed_resp;
resp = (struct nlmsghdr *)*recvbuf;
switch (resp->nlmsg_type) {
case NLMSG_ERROR:
err = (struct nlmsgerr *)NLMSG_DATA(resp);
if (resp->nlmsg_len < NLMSG_LENGTH(sizeof(*err)))
goto malformed_resp;
if (err->error) {
virReportSystemError(-err->error,
_("error dumping %s (%d) interface"),
ifname, ifindex);
rc = -1;
}
break;
case GENL_ID_CTRL:
case NLMSG_DONE:
rc = nlmsg_parse(resp, sizeof(struct ifinfomsg),
tb, IFLA_MAX, NULL);
if (rc < 0)
goto malformed_resp;
break;
default:
goto malformed_resp;
}
if (rc != 0)
VIR_FREE(*recvbuf);
cleanup:
nlmsg_free(nl_msg);
return rc;
malformed_resp:
nlmsg_free(nl_msg);
virNetDevError(VIR_ERR_INTERNAL_ERROR, "%s",
_("malformed netlink response message"));
VIR_FREE(*recvbuf);
return -1;
buffer_too_small:
nlmsg_free(nl_msg);
virNetDevError(VIR_ERR_INTERNAL_ERROR, "%s",
_("allocated netlink buffer is too small"));
return -1;
}
static int
virNetDevSetVfConfig(const char *ifname, int ifindex, int vf,
bool nltarget_kernel, const unsigned char *macaddr,
int vlanid, uint32_t (*getPidFunc)(void))
{
int rc = -1;
struct nlmsghdr *resp;
struct nlmsgerr *err;
unsigned char *recvbuf = NULL;
unsigned int recvbuflen = 0;
uint32_t pid = 0;
struct nl_msg *nl_msg;
struct nlattr *vfinfolist, *vfinfo;
struct ifinfomsg ifinfo = {
.ifi_family = AF_UNSPEC,
.ifi_index = ifindex
};
if (!macaddr && vlanid < 0)
return -1;
nl_msg = nlmsg_alloc_simple(RTM_SETLINK, NLM_F_REQUEST);
if (!nl_msg) {
virReportOOMError();
return rc;
}
if (nlmsg_append(nl_msg, &ifinfo, sizeof(ifinfo), NLMSG_ALIGNTO) < 0)
goto buffer_too_small;
if (ifname &&
nla_put(nl_msg, IFLA_IFNAME, strlen(ifname)+1, ifname) < 0)
goto buffer_too_small;
if (!(vfinfolist = nla_nest_start(nl_msg, IFLA_VFINFO_LIST)))
goto buffer_too_small;
if (!(vfinfo = nla_nest_start(nl_msg, IFLA_VF_INFO)))
goto buffer_too_small;
if (macaddr) {
struct ifla_vf_mac ifla_vf_mac = {
.vf = vf,
.mac = { 0, },
};
memcpy(ifla_vf_mac.mac, macaddr, VIR_MAC_BUFLEN);
if (nla_put(nl_msg, IFLA_VF_MAC, sizeof(ifla_vf_mac),
&ifla_vf_mac) < 0)
goto buffer_too_small;
}
if (vlanid >= 0) {
struct ifla_vf_vlan ifla_vf_vlan = {
.vf = vf,
.vlan = vlanid,
.qos = 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 (!nltarget_kernel) {
pid = getPidFunc();
if (pid == 0) {
rc = -1;
goto cleanup;
}
}
if (virNetlinkCommand(nl_msg, &recvbuf, &recvbuflen, pid) < 0)
goto cleanup;
if (recvbuflen < NLMSG_LENGTH(0) || recvbuf == NULL)
goto malformed_resp;
resp = (struct nlmsghdr *)recvbuf;
switch (resp->nlmsg_type) {
case NLMSG_ERROR:
err = (struct nlmsgerr *)NLMSG_DATA(resp);
if (resp->nlmsg_len < NLMSG_LENGTH(sizeof(*err)))
goto malformed_resp;
if (err->error) {
virReportSystemError(-err->error,
_("error during set %s of ifindex %d"),
(macaddr ? (vlanid >= 0 ? "mac/vlan" : "mac") : "vlanid"),
ifindex);
goto cleanup;
}
break;
case NLMSG_DONE:
break;
default:
goto malformed_resp;
}
rc = 0;
cleanup:
nlmsg_free(nl_msg);
VIR_FREE(recvbuf);
return rc;
malformed_resp:
nlmsg_free(nl_msg);
virNetDevError(VIR_ERR_INTERNAL_ERROR, "%s",
_("malformed netlink response message"));
VIR_FREE(recvbuf);
return rc;
buffer_too_small:
nlmsg_free(nl_msg);
virNetDevError(VIR_ERR_INTERNAL_ERROR, "%s",
_("allocated netlink buffer is too small"));
return rc;
}
static int
virNetDevParseVfConfig(struct nlattr **tb, int32_t vf, unsigned char *mac,
int *vlanid)
{
const char *msg = NULL;
int rc = -1;
if (tb[IFLA_VFINFO_LIST]) {
struct ifla_vf_mac *vf_mac;
struct ifla_vf_vlan *vf_vlan;
struct nlattr *tb_vf_info = {NULL, };
struct nlattr *tb_vf[IFLA_VF_MAX+1];
int found = 0;
int rem;
nla_for_each_nested(tb_vf_info, tb[IFLA_VFINFO_LIST], rem) {
if (nla_type(tb_vf_info) != IFLA_VF_INFO)
continue;
if (nla_parse_nested(tb_vf, IFLA_VF_MAX, tb_vf_info,
ifla_vf_policy)) {
msg = _("error parsing IFLA_VF_INFO");
goto cleanup;
}
if (tb[IFLA_VF_MAC]) {
vf_mac = RTA_DATA(tb_vf[IFLA_VF_MAC]);
if (vf_mac && vf_mac->vf == vf) {
memcpy(mac, vf_mac->mac, VIR_MAC_BUFLEN);
found = 1;
}
}
if (tb[IFLA_VF_VLAN]) {
vf_vlan = RTA_DATA(tb_vf[IFLA_VF_VLAN]);
if (vf_vlan && vf_vlan->vf == vf) {
*vlanid = vf_vlan->vlan;
found = 1;
}
}
if (found) {
rc = 0;
break;
}
}
}
cleanup:
if (msg)
virNetDevError(VIR_ERR_INTERNAL_ERROR, "%s", msg);
return rc;
}
static int
virNetDevGetVfConfig(const char *ifname, int vf, unsigned char *mac,
int *vlanid)
{
int rc = -1;
unsigned char *recvbuf = NULL;
struct nlattr *tb[IFLA_MAX + 1] = {NULL, };
int ifindex = -1;
rc = virNetDevLinkDump(ifname, ifindex, true, tb, &recvbuf, NULL);
if (rc < 0)
return rc;
rc = virNetDevParseVfConfig(tb, vf, mac, vlanid);
VIR_FREE(recvbuf);
return rc;
}
static int
virNetDevReplaceVfConfig(const char *pflinkdev, int vf,
const unsigned char *macaddress,
int vlanid,
const char *stateDir)
{
unsigned char oldmac[6];
int oldvlanid = -1;
char *path = NULL;
char macstr[VIR_MAC_STRING_BUFLEN];
int ifindex = -1;
if (virNetDevGetVfConfig(pflinkdev, vf, oldmac, &oldvlanid) < 0)
return -1;
if (virAsprintf(&path, "%s/%s_vf%d",
stateDir, pflinkdev, vf) < 0) {
virReportOOMError();
return -1;
}
virMacAddrFormat(oldmac, macstr);
if (virFileWriteStr(path, macstr, O_CREAT|O_TRUNC|O_WRONLY) < 0) {
virReportSystemError(errno, _("Unable to preserve mac for pf = %s,"
" vf = %d"), pflinkdev, vf);
VIR_FREE(path);
return -1;
}
VIR_FREE(path);
return virNetDevSetVfConfig(pflinkdev, ifindex, vf, true,
macaddress, vlanid, NULL);
}
static int
virNetDevRestoreVfConfig(const char *pflinkdev, int vf,
const char *stateDir)
{
int rc = -1;
char *macstr = NULL;
char *path = NULL;
unsigned char oldmac[6];
int vlanid = -1;
int ifindex = -1;
if (virAsprintf(&path, "%s/%s_vf%d",
stateDir, pflinkdev, vf) < 0) {
virReportOOMError();
return rc;
}
if (virFileReadAll(path, VIR_MAC_STRING_BUFLEN, &macstr) < 0) {
goto cleanup;
}
if (virMacAddrParse(macstr, &oldmac[0]) != 0) {
virNetDevError(VIR_ERR_INTERNAL_ERROR,
_("Cannot parse MAC address from '%s'"),
macstr);
goto cleanup;
}
/*reset mac and remove file-ignore results*/
rc = virNetDevSetVfConfig(pflinkdev, ifindex, vf, true,
oldmac, vlanid, NULL);
ignore_value(unlink(path));
cleanup:
VIR_FREE(path);
VIR_FREE(macstr);
return rc;
}
/**
* virNetDevReplaceNetConfig:
* @linkdev: name of the interface
* @vf: vf index if linkdev is a pf
* @macaddress: new MAC address for interface
* @vlanid: new vlanid
* @stateDir: directory to store old net config
*
* Returns 0 on success, -1 on failure
*
*/
int
virNetDevReplaceNetConfig(char *linkdev, int vf,
const unsigned char *macaddress, int vlanid,
char *stateDir)
{
if (vf == -1)
return virNetDevReplaceMacAddress(linkdev, macaddress, stateDir);
else
return virNetDevReplaceVfConfig(linkdev, vf, macaddress, vlanid,
stateDir);
}
/**
* virNetDevRestoreNetConfig:
* @linkdev: name of the interface
* @vf: vf index if linkdev is a pf
* @stateDir: directory containing old net config
*
* Returns 0 on success, -errno on failure.
*
*/
int
virNetDevRestoreNetConfig(char *linkdev, int vf, char *stateDir)
{
if (vf == -1)
return virNetDevRestoreMacAddress(linkdev, stateDir);
else
return virNetDevRestoreVfConfig(linkdev, vf, stateDir);
}
#else /* defined(__linux__) && defined(HAVE_LIBNL) */
int
virNetDevLinkDump(const char *ifname ATTRIBUTE_UNUSED,
int ifindex ATTRIBUTE_UNUSED,
bool nltarget_kernel ATTRIBUTE_UNUSED,
struct nlattr **tb ATTRIBUTE_UNUSED,
unsigned char **recvbuf ATTRIBUTE_UNUSED,
uint32_t (*getPidFunc)(void) ATTRIBUTE_UNUSED)
{
virReportSystemError(ENOSYS, "%s",
_("Unable to dump link info on this platform"));
return -1;
}
int
virNetDevReplaceNetConfig(char *linkdev ATTRIBUTE_UNUSED,
int vf ATTRIBUTE_UNUSED,
const unsigned char *macaddress ATTRIBUTE_UNUSED,
int vlanid ATTRIBUTE_UNUSED,
char *stateDir ATTRIBUTE_UNUSED)
{
virReportSystemError(ENOSYS, "%s",
_("Unable to replace net config on this platform"));
return -1;
}
int
virNetDevRestoreNetConfig(char *linkdev ATTRIBUTE_UNUSED,
int vf ATTRIBUTE_UNUSED,
char *stateDir ATTRIBUTE_UNUSED)
{
virReportSystemError(ENOSYS, "%s",
_("Unable to restore net config on this platform"));
return -1;
}
#endif /* defined(__linux__) && defined(HAVE_LIBNL) */

View File

@ -24,6 +24,7 @@
# define __VIR_NETDEV_H__
# include "virsocketaddr.h"
# include "virnetlink.h"
int virNetDevExists(const char *brname)
ATTRIBUTE_NONNULL(1) ATTRIBUTE_RETURN_CHECK;
@ -105,4 +106,22 @@ int virNetDevGetVirtualFunctions(const char *pfname,
ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) ATTRIBUTE_NONNULL(3)
ATTRIBUTE_RETURN_CHECK;
int virNetDevLinkDump(const char *ifname, int ifindex,
bool nltarget_kernel, struct nlattr **tb,
unsigned char **recvbuf,
uint32_t (*getPidFunc)(void))
ATTRIBUTE_RETURN_CHECK;
int virNetDevReplaceNetConfig(char *linkdev, int vf,
const unsigned char *macaddress, int vlanid,
char *stateDir)
ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(3) ATTRIBUTE_NONNULL(5);
int virNetDevRestoreNetConfig(char *linkdev, int vf, char *stateDir)
ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(3);
int virNetDevGetVirtualFunctionInfo(const char *vfname, char **pfname,
int *vf)
ATTRIBUTE_NONNULL(1);
#endif /* __VIR_NETDEV_H__ */

View File

@ -31,6 +31,7 @@
struct nl_msg;
struct sockaddr_nl;
struct nlattr;
# endif /* __linux__ */