mirror of
https://gitlab.com/libvirt/libvirt.git
synced 2025-01-18 10:35:20 +00:00
netlink: Introduce a helper function to simplify netlink functions
Extract common code as helper function virNetlinkTalk, then simplify the functions virNetlink[DumpLink|NewLink|DelLink|GetNeighbor]. Signed-off-by: Shi Lei <shi_lei@massclouds.com> Signed-off-by: Michal Privoznik <mprivozn@redhat.com> Reviewed-by: Michal Privoznik <mprivozn@redhat.com>
This commit is contained in:
parent
871eba4d99
commit
037ea5d10c
@ -353,6 +353,82 @@ int virNetlinkCommand(struct nl_msg *nl_msg,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* virNetlinkTalk:
|
||||||
|
* @ifname: name of the link
|
||||||
|
* @nl_msg: pointer to netlink message
|
||||||
|
* @src_pid: pid used for nl_pid of the local end of the netlink message
|
||||||
|
* (0 == "use getpid()")
|
||||||
|
* @dst_pid: pid of destination nl_pid if the kernel
|
||||||
|
* is not the target of the netlink message but it is to be
|
||||||
|
* sent to another process (0 if sending to the kernel)
|
||||||
|
* @resp: pointer to pointer where response buffer will be allocated
|
||||||
|
* @resp_len: pointer to integer holding the size of the response buffer
|
||||||
|
* on return of the function
|
||||||
|
* @error: pointer to store netlink error (-errno)
|
||||||
|
* @fallback: pointer to an alternate function that will be called in case
|
||||||
|
* netlink fails with EOPNOTSUPP (any other error will simply be
|
||||||
|
* treated as an error)
|
||||||
|
*
|
||||||
|
* Simple wrapper around virNetlinkCommand(). The returned netlink message
|
||||||
|
* is allocated at @resp. Please note that according to netlink(7) man page,
|
||||||
|
* reply with type of NLMSG_ERROR and @error == 0 is an acknowledgment and
|
||||||
|
* thus not an error.
|
||||||
|
*
|
||||||
|
* Returns: 0 on success,
|
||||||
|
* -1 otherwise (error reported if @error == NULL)
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
virNetlinkTalk(const char *ifname,
|
||||||
|
virNetlinkMsg *nl_msg,
|
||||||
|
uint32_t src_pid,
|
||||||
|
uint32_t dst_pid,
|
||||||
|
struct nlmsghdr **resp,
|
||||||
|
unsigned int *resp_len,
|
||||||
|
int *error,
|
||||||
|
virNetlinkTalkFallback fallback)
|
||||||
|
{
|
||||||
|
if (virNetlinkCommand(nl_msg, resp, resp_len,
|
||||||
|
src_pid, dst_pid, NETLINK_ROUTE, 0) < 0)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
if (*resp_len < NLMSG_LENGTH(0) || *resp == NULL)
|
||||||
|
goto malformed_resp;
|
||||||
|
|
||||||
|
if ((*resp)->nlmsg_type == NLMSG_ERROR) {
|
||||||
|
struct nlmsgerr *err;
|
||||||
|
|
||||||
|
err = (struct nlmsgerr *) NLMSG_DATA(*resp);
|
||||||
|
|
||||||
|
if ((*resp)->nlmsg_len < NLMSG_LENGTH(sizeof(*err)))
|
||||||
|
goto malformed_resp;
|
||||||
|
|
||||||
|
if (-err->error == EOPNOTSUPP && fallback)
|
||||||
|
return fallback(ifname);
|
||||||
|
|
||||||
|
if (err->error < 0) {
|
||||||
|
if (error)
|
||||||
|
*error = err->error;
|
||||||
|
else
|
||||||
|
virReportSystemError(-err->error, "%s", _("netlink error"));
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* According to netlink(7) man page NLMSG_ERROR packet with error
|
||||||
|
* field set to 0 is an acknowledgment packet and thus not an error. */
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
malformed_resp:
|
||||||
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
||||||
|
_("malformed netlink response message"));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
int
|
int
|
||||||
virNetlinkDumpCommand(struct nl_msg *nl_msg,
|
virNetlinkDumpCommand(struct nl_msg *nl_msg,
|
||||||
virNetlinkDumpCallback callback,
|
virNetlinkDumpCallback callback,
|
||||||
@ -396,6 +472,7 @@ virNetlinkDumpCommand(struct nl_msg *nl_msg,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* virNetlinkDumpLink:
|
* virNetlinkDumpLink:
|
||||||
*
|
*
|
||||||
@ -420,15 +497,14 @@ virNetlinkDumpLink(const char *ifname, int ifindex,
|
|||||||
void **nlData, struct nlattr **tb,
|
void **nlData, struct nlattr **tb,
|
||||||
uint32_t src_pid, uint32_t dst_pid)
|
uint32_t src_pid, uint32_t dst_pid)
|
||||||
{
|
{
|
||||||
int rc = -1;
|
|
||||||
struct nlmsgerr *err;
|
|
||||||
struct ifinfomsg ifinfo = {
|
struct ifinfomsg ifinfo = {
|
||||||
.ifi_family = AF_UNSPEC,
|
.ifi_family = AF_UNSPEC,
|
||||||
.ifi_index = ifindex
|
.ifi_index = ifindex
|
||||||
};
|
};
|
||||||
unsigned int recvbuflen;
|
|
||||||
g_autoptr(virNetlinkMsg) nl_msg = NULL;
|
g_autoptr(virNetlinkMsg) nl_msg = NULL;
|
||||||
g_autofree struct nlmsghdr *resp = NULL;
|
g_autofree struct nlmsghdr *resp = NULL;
|
||||||
|
unsigned int resp_len = 0;
|
||||||
|
int error = 0;
|
||||||
|
|
||||||
if (ifname && ifindex <= 0 && virNetDevGetIndex(ifname, &ifindex) < 0)
|
if (ifname && ifindex <= 0 && virNetDevGetIndex(ifname, &ifindex) < 0)
|
||||||
return -1;
|
return -1;
|
||||||
@ -459,46 +535,25 @@ virNetlinkDumpLink(const char *ifname, int ifindex,
|
|||||||
}
|
}
|
||||||
# endif
|
# endif
|
||||||
|
|
||||||
if (virNetlinkCommand(nl_msg, &resp, &recvbuflen,
|
if (virNetlinkTalk(ifname, nl_msg, src_pid, dst_pid,
|
||||||
src_pid, dst_pid, NETLINK_ROUTE, 0) < 0)
|
&resp, &resp_len, &error, NULL) < 0) {
|
||||||
|
virReportSystemError(-error,
|
||||||
|
_("error dumping %s (%d) interface"),
|
||||||
|
ifname, ifindex);
|
||||||
return -1;
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
if (recvbuflen < NLMSG_LENGTH(0) || resp == NULL)
|
if ((resp->nlmsg_type != NLMSG_ERROR &&
|
||||||
goto malformed_resp;
|
resp->nlmsg_type != GENL_ID_CTRL &&
|
||||||
|
resp->nlmsg_type != NLMSG_DONE) ||
|
||||||
switch (resp->nlmsg_type) {
|
nlmsg_parse(resp, sizeof(struct ifinfomsg), tb, IFLA_MAX, NULL) < 0) {
|
||||||
case NLMSG_ERROR:
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
||||||
err = (struct nlmsgerr *)NLMSG_DATA(resp);
|
_("malformed netlink response message"));
|
||||||
if (resp->nlmsg_len < NLMSG_LENGTH(sizeof(*err)))
|
return -1;
|
||||||
goto malformed_resp;
|
|
||||||
|
|
||||||
if (err->error) {
|
|
||||||
virReportSystemError(-err->error,
|
|
||||||
_("error dumping %s (%d) interface"),
|
|
||||||
ifname, ifindex);
|
|
||||||
return -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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
*nlData = g_steal_pointer(&resp);
|
*nlData = g_steal_pointer(&resp);
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
malformed_resp:
|
|
||||||
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
||||||
_("malformed netlink response message"));
|
|
||||||
return rc;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -523,13 +578,12 @@ virNetlinkNewLink(const char *ifname,
|
|||||||
virNetlinkNewLinkDataPtr extra_args,
|
virNetlinkNewLinkDataPtr extra_args,
|
||||||
int *error)
|
int *error)
|
||||||
{
|
{
|
||||||
struct nlmsgerr *err;
|
|
||||||
struct nlattr *linkinfo = NULL;
|
struct nlattr *linkinfo = NULL;
|
||||||
struct nlattr *infodata = NULL;
|
struct nlattr *infodata = NULL;
|
||||||
unsigned int buflen;
|
|
||||||
struct ifinfomsg ifinfo = { .ifi_family = AF_UNSPEC };
|
struct ifinfomsg ifinfo = { .ifi_family = AF_UNSPEC };
|
||||||
g_autoptr(virNetlinkMsg) nl_msg = NULL;
|
g_autoptr(virNetlinkMsg) nl_msg = NULL;
|
||||||
g_autofree struct nlmsghdr *resp = NULL;
|
g_autofree struct nlmsghdr *resp = NULL;
|
||||||
|
unsigned int resp_len = 0;
|
||||||
|
|
||||||
*error = 0;
|
*error = 0;
|
||||||
|
|
||||||
@ -591,37 +645,18 @@ virNetlinkNewLink(const char *ifname,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (virNetlinkCommand(nl_msg, &resp, &buflen, 0, 0, NETLINK_ROUTE, 0) < 0)
|
if (virNetlinkTalk(ifname, nl_msg, 0, 0,
|
||||||
|
&resp, &resp_len, error, NULL) < 0)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
if (buflen < NLMSG_LENGTH(0) || resp == NULL)
|
if (resp->nlmsg_type != NLMSG_ERROR &&
|
||||||
goto malformed_resp;
|
resp->nlmsg_type != NLMSG_DONE) {
|
||||||
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
||||||
switch (resp->nlmsg_type) {
|
_("malformed netlink response message"));
|
||||||
case NLMSG_ERROR:
|
return -1;
|
||||||
err = (struct nlmsgerr *)NLMSG_DATA(resp);
|
|
||||||
if (resp->nlmsg_len < NLMSG_LENGTH(sizeof(*err)))
|
|
||||||
goto malformed_resp;
|
|
||||||
|
|
||||||
if (err->error < 0) {
|
|
||||||
*error = err->error;
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case NLMSG_DONE:
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
goto malformed_resp;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
malformed_resp:
|
|
||||||
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
||||||
_("malformed netlink response message"));
|
|
||||||
return -1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -641,13 +676,13 @@ virNetlinkNewLink(const char *ifname,
|
|||||||
* Returns 0 on success, -1 on fatal error.
|
* Returns 0 on success, -1 on fatal error.
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
virNetlinkDelLink(const char *ifname, virNetlinkDelLinkFallback fallback)
|
virNetlinkDelLink(const char *ifname, virNetlinkTalkFallback fallback)
|
||||||
{
|
{
|
||||||
struct nlmsgerr *err;
|
|
||||||
struct ifinfomsg ifinfo = { .ifi_family = AF_UNSPEC };
|
struct ifinfomsg ifinfo = { .ifi_family = AF_UNSPEC };
|
||||||
unsigned int recvbuflen;
|
|
||||||
g_autoptr(virNetlinkMsg) nl_msg = NULL;
|
g_autoptr(virNetlinkMsg) nl_msg = NULL;
|
||||||
g_autofree struct nlmsghdr *resp = NULL;
|
g_autofree struct nlmsghdr *resp = NULL;
|
||||||
|
unsigned int resp_len = 0;
|
||||||
|
int error = 0;
|
||||||
|
|
||||||
nl_msg = nlmsg_alloc_simple(RTM_DELLINK, NLM_F_REQUEST);
|
nl_msg = nlmsg_alloc_simple(RTM_DELLINK, NLM_F_REQUEST);
|
||||||
if (!nl_msg) {
|
if (!nl_msg) {
|
||||||
@ -659,44 +694,22 @@ virNetlinkDelLink(const char *ifname, virNetlinkDelLinkFallback fallback)
|
|||||||
|
|
||||||
NETLINK_MSG_PUT(nl_msg, IFLA_IFNAME, (strlen(ifname) + 1), ifname);
|
NETLINK_MSG_PUT(nl_msg, IFLA_IFNAME, (strlen(ifname) + 1), ifname);
|
||||||
|
|
||||||
if (virNetlinkCommand(nl_msg, &resp, &recvbuflen, 0, 0,
|
if (virNetlinkTalk(ifname, nl_msg, 0, 0,
|
||||||
NETLINK_ROUTE, 0) < 0) {
|
&resp, &resp_len, &error, fallback) < 0) {
|
||||||
|
virReportSystemError(-error,
|
||||||
|
_("error destroying network device %s"),
|
||||||
|
ifname);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (recvbuflen < NLMSG_LENGTH(0) || resp == NULL)
|
if (resp->nlmsg_type != NLMSG_ERROR &&
|
||||||
goto malformed_resp;
|
resp->nlmsg_type != NLMSG_DONE) {
|
||||||
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
||||||
switch (resp->nlmsg_type) {
|
_("malformed netlink response message"));
|
||||||
case NLMSG_ERROR:
|
return -1;
|
||||||
err = (struct nlmsgerr *)NLMSG_DATA(resp);
|
|
||||||
if (resp->nlmsg_len < NLMSG_LENGTH(sizeof(*err)))
|
|
||||||
goto malformed_resp;
|
|
||||||
|
|
||||||
if (-err->error == EOPNOTSUPP && fallback)
|
|
||||||
return fallback(ifname);
|
|
||||||
|
|
||||||
if (err->error) {
|
|
||||||
virReportSystemError(-err->error,
|
|
||||||
_("error destroying network device %s"),
|
|
||||||
ifname);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case NLMSG_DONE:
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
goto malformed_resp;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
malformed_resp:
|
|
||||||
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
||||||
_("malformed netlink response message"));
|
|
||||||
return -1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -712,18 +725,18 @@ virNetlinkDelLink(const char *ifname, virNetlinkDelLinkFallback fallback)
|
|||||||
*
|
*
|
||||||
* Get neighbor table entry from netlink.
|
* Get neighbor table entry from netlink.
|
||||||
*
|
*
|
||||||
* Returns 0 on success, -1 on fatal error.
|
* Returns length of the raw data from netlink on success, -1 on fatal error.
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
virNetlinkGetNeighbor(void **nlData, uint32_t src_pid, uint32_t dst_pid)
|
virNetlinkGetNeighbor(void **nlData, uint32_t src_pid, uint32_t dst_pid)
|
||||||
{
|
{
|
||||||
struct nlmsgerr *err;
|
|
||||||
struct ndmsg ndinfo = {
|
struct ndmsg ndinfo = {
|
||||||
.ndm_family = AF_UNSPEC,
|
.ndm_family = AF_UNSPEC,
|
||||||
};
|
};
|
||||||
unsigned int recvbuflen;
|
|
||||||
g_autoptr(virNetlinkMsg) nl_msg = NULL;
|
g_autoptr(virNetlinkMsg) nl_msg = NULL;
|
||||||
g_autofree struct nlmsghdr *resp = NULL;
|
g_autofree struct nlmsghdr *resp = NULL;
|
||||||
|
unsigned int resp_len = 0;
|
||||||
|
int error = 0;
|
||||||
|
|
||||||
nl_msg = nlmsg_alloc_simple(RTM_GETNEIGH, NLM_F_DUMP | NLM_F_REQUEST);
|
nl_msg = nlmsg_alloc_simple(RTM_GETNEIGH, NLM_F_DUMP | NLM_F_REQUEST);
|
||||||
if (!nl_msg) {
|
if (!nl_msg) {
|
||||||
@ -733,40 +746,21 @@ virNetlinkGetNeighbor(void **nlData, uint32_t src_pid, uint32_t dst_pid)
|
|||||||
|
|
||||||
NETLINK_MSG_APPEND(nl_msg, sizeof(ndinfo), &ndinfo);
|
NETLINK_MSG_APPEND(nl_msg, sizeof(ndinfo), &ndinfo);
|
||||||
|
|
||||||
if (virNetlinkCommand(nl_msg, &resp, &recvbuflen,
|
if (virNetlinkTalk(NULL, nl_msg, src_pid, dst_pid,
|
||||||
src_pid, dst_pid, NETLINK_ROUTE, 0) < 0)
|
&resp, &resp_len, &error, NULL) < 0) {
|
||||||
|
virReportSystemError(-error, "%s", _("error dumping neighbor table"));
|
||||||
return -1;
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
if (recvbuflen < NLMSG_LENGTH(0) || resp == NULL)
|
if (resp->nlmsg_type != NLMSG_ERROR &&
|
||||||
goto malformed_resp;
|
resp->nlmsg_type != RTM_NEWNEIGH) {
|
||||||
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
||||||
switch (resp->nlmsg_type) {
|
_("malformed netlink response message"));
|
||||||
case NLMSG_ERROR:
|
return -1;
|
||||||
err = (struct nlmsgerr *)NLMSG_DATA(resp);
|
|
||||||
if (resp->nlmsg_len < NLMSG_LENGTH(sizeof(*err)))
|
|
||||||
goto malformed_resp;
|
|
||||||
|
|
||||||
if (err->error) {
|
|
||||||
virReportSystemError(-err->error,
|
|
||||||
"%s", _("error dumping"));
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case RTM_NEWNEIGH:
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
goto malformed_resp;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
*nlData = g_steal_pointer(&resp);
|
*nlData = g_steal_pointer(&resp);
|
||||||
return recvbuflen;
|
return resp_len;
|
||||||
|
|
||||||
malformed_resp:
|
|
||||||
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
||||||
_("malformed netlink response message"));
|
|
||||||
return -1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
@ -1300,7 +1294,7 @@ virNetlinkDumpLink(const char *ifname G_GNUC_UNUSED,
|
|||||||
|
|
||||||
int
|
int
|
||||||
virNetlinkDelLink(const char *ifname G_GNUC_UNUSED,
|
virNetlinkDelLink(const char *ifname G_GNUC_UNUSED,
|
||||||
virNetlinkDelLinkFallback fallback G_GNUC_UNUSED)
|
virNetlinkTalkFallback fallback G_GNUC_UNUSED)
|
||||||
{
|
{
|
||||||
virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _(unsupported));
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _(unsupported));
|
||||||
return -1;
|
return -1;
|
||||||
|
@ -69,9 +69,9 @@ int virNetlinkNewLink(const char *ifname,
|
|||||||
virNetlinkNewLinkDataPtr data,
|
virNetlinkNewLinkDataPtr data,
|
||||||
int *error);
|
int *error);
|
||||||
|
|
||||||
typedef int (*virNetlinkDelLinkFallback)(const char *ifname);
|
typedef int (*virNetlinkTalkFallback)(const char *ifname);
|
||||||
|
|
||||||
int virNetlinkDelLink(const char *ifname, virNetlinkDelLinkFallback fallback);
|
int virNetlinkDelLink(const char *ifname, virNetlinkTalkFallback fallback);
|
||||||
|
|
||||||
int virNetlinkGetErrorCode(struct nlmsghdr *resp, unsigned int recvbuflen);
|
int virNetlinkGetErrorCode(struct nlmsghdr *resp, unsigned int recvbuflen);
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user