diff --git a/src/util/virnetlink.c b/src/util/virnetlink.c index fdcb0dc0cd..9bd7339054 100644 --- a/src/util/virnetlink.c +++ b/src/util/virnetlink.c @@ -353,6 +353,82 @@ int virNetlinkCommand(struct nl_msg *nl_msg, 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 virNetlinkDumpCommand(struct nl_msg *nl_msg, virNetlinkDumpCallback callback, @@ -396,6 +472,7 @@ virNetlinkDumpCommand(struct nl_msg *nl_msg, return 0; } + /** * virNetlinkDumpLink: * @@ -420,15 +497,14 @@ virNetlinkDumpLink(const char *ifname, int ifindex, void **nlData, struct nlattr **tb, uint32_t src_pid, uint32_t dst_pid) { - int rc = -1; - struct nlmsgerr *err; struct ifinfomsg ifinfo = { .ifi_family = AF_UNSPEC, .ifi_index = ifindex }; - unsigned int recvbuflen; g_autoptr(virNetlinkMsg) nl_msg = NULL; g_autofree struct nlmsghdr *resp = NULL; + unsigned int resp_len = 0; + int error = 0; if (ifname && ifindex <= 0 && virNetDevGetIndex(ifname, &ifindex) < 0) return -1; @@ -459,46 +535,25 @@ virNetlinkDumpLink(const char *ifname, int ifindex, } # endif - if (virNetlinkCommand(nl_msg, &resp, &recvbuflen, - src_pid, dst_pid, NETLINK_ROUTE, 0) < 0) + if (virNetlinkTalk(ifname, nl_msg, src_pid, dst_pid, + &resp, &resp_len, &error, NULL) < 0) { + virReportSystemError(-error, + _("error dumping %s (%d) interface"), + ifname, ifindex); return -1; + } - if (recvbuflen < NLMSG_LENGTH(0) || resp == NULL) - goto malformed_resp; - - 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); - 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; + if ((resp->nlmsg_type != NLMSG_ERROR && + resp->nlmsg_type != GENL_ID_CTRL && + resp->nlmsg_type != NLMSG_DONE) || + nlmsg_parse(resp, sizeof(struct ifinfomsg), tb, IFLA_MAX, NULL) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("malformed netlink response message")); + return -1; } *nlData = g_steal_pointer(&resp); 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, int *error) { - struct nlmsgerr *err; struct nlattr *linkinfo = NULL; struct nlattr *infodata = NULL; - unsigned int buflen; struct ifinfomsg ifinfo = { .ifi_family = AF_UNSPEC }; g_autoptr(virNetlinkMsg) nl_msg = NULL; g_autofree struct nlmsghdr *resp = NULL; + unsigned int resp_len = 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; - if (buflen < NLMSG_LENGTH(0) || resp == NULL) - goto malformed_resp; - - 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 < 0) { - *error = err->error; - return -1; - } - break; - - case NLMSG_DONE: - break; - - default: - goto malformed_resp; + if (resp->nlmsg_type != NLMSG_ERROR && + resp->nlmsg_type != NLMSG_DONE) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("malformed netlink response message")); + return -1; } 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. */ int -virNetlinkDelLink(const char *ifname, virNetlinkDelLinkFallback fallback) +virNetlinkDelLink(const char *ifname, virNetlinkTalkFallback fallback) { - struct nlmsgerr *err; struct ifinfomsg ifinfo = { .ifi_family = AF_UNSPEC }; - unsigned int recvbuflen; g_autoptr(virNetlinkMsg) nl_msg = 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); if (!nl_msg) { @@ -659,44 +694,22 @@ virNetlinkDelLink(const char *ifname, virNetlinkDelLinkFallback fallback) NETLINK_MSG_PUT(nl_msg, IFLA_IFNAME, (strlen(ifname) + 1), ifname); - if (virNetlinkCommand(nl_msg, &resp, &recvbuflen, 0, 0, - NETLINK_ROUTE, 0) < 0) { + if (virNetlinkTalk(ifname, nl_msg, 0, 0, + &resp, &resp_len, &error, fallback) < 0) { + virReportSystemError(-error, + _("error destroying network device %s"), + ifname); return -1; } - if (recvbuflen < NLMSG_LENGTH(0) || resp == NULL) - goto malformed_resp; - - 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 == 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; + if (resp->nlmsg_type != NLMSG_ERROR && + resp->nlmsg_type != NLMSG_DONE) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("malformed netlink response message")); + return -1; } 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. * - * Returns 0 on success, -1 on fatal error. + * Returns length of the raw data from netlink on success, -1 on fatal error. */ int virNetlinkGetNeighbor(void **nlData, uint32_t src_pid, uint32_t dst_pid) { - struct nlmsgerr *err; struct ndmsg ndinfo = { .ndm_family = AF_UNSPEC, }; - unsigned int recvbuflen; g_autoptr(virNetlinkMsg) nl_msg = 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); 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); - if (virNetlinkCommand(nl_msg, &resp, &recvbuflen, - src_pid, dst_pid, NETLINK_ROUTE, 0) < 0) + if (virNetlinkTalk(NULL, nl_msg, src_pid, dst_pid, + &resp, &resp_len, &error, NULL) < 0) { + virReportSystemError(-error, "%s", _("error dumping neighbor table")); return -1; + } - if (recvbuflen < NLMSG_LENGTH(0) || resp == NULL) - goto malformed_resp; - - 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, - "%s", _("error dumping")); - return -1; - } - break; - - case RTM_NEWNEIGH: - break; - - default: - goto malformed_resp; + if (resp->nlmsg_type != NLMSG_ERROR && + resp->nlmsg_type != RTM_NEWNEIGH) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("malformed netlink response message")); + return -1; } *nlData = g_steal_pointer(&resp); - return recvbuflen; - - malformed_resp: - virReportError(VIR_ERR_INTERNAL_ERROR, "%s", - _("malformed netlink response message")); - return -1; + return resp_len; } int @@ -1300,7 +1294,7 @@ virNetlinkDumpLink(const char *ifname G_GNUC_UNUSED, int virNetlinkDelLink(const char *ifname G_GNUC_UNUSED, - virNetlinkDelLinkFallback fallback G_GNUC_UNUSED) + virNetlinkTalkFallback fallback G_GNUC_UNUSED) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _(unsupported)); return -1; diff --git a/src/util/virnetlink.h b/src/util/virnetlink.h index 966d6db3f0..cab685feea 100644 --- a/src/util/virnetlink.h +++ b/src/util/virnetlink.h @@ -69,9 +69,9 @@ int virNetlinkNewLink(const char *ifname, virNetlinkNewLinkDataPtr data, 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);