/** * virNetDevMacVLanTapOpen: * @ifname: Name of the macvtap interface * @tapfd: array of file descriptor return value for the new macvtap device * @tapfdSize: number of file descriptors in @tapfd * @retries : Number of retries in case udev for example may need to be * waited for to create the tap chardev * * Open the macvtap's tap device, possibly multiple times if @tapfdSize > 1. * * Returns 0 on success, -1 otherwise. */ static int virNetDevMacVLanTapOpen(const char *ifname, int *tapfd, size_t tapfdSize, int retries) { int ret = -1; int ifindex; char *tapname = NULL; size_t i = 0; if (virNetDevGetIndex(ifname, &ifindex) < 0) return -1; if (virAsprintf(&tapname, "/dev/tap%d", ifindex) < 0) goto cleanup; for (i = 0; i < tapfdSize; i++) { int fd = -1; while (fd < 0) { if ((fd = open(tapname, O_RDWR)) >= 0) { tapfd[i] = fd; } else if (retries-- > 0) { /* may need to wait for udev to be done */ usleep(20000); } else { /* However, if haven't succeeded, quit. */ virReportSystemError(errno, _("cannot open macvtap tap device %s"), tapname); goto cleanup; } } } ret = 0; cleanup: if (ret < 0) { while (i--) VIR_FORCE_CLOSE(tapfd[i]); } VIR_FREE(tapname); return ret; }
/** * virNetDevMacVLanCreate: * * @ifname: The name the interface is supposed to have; optional parameter * @type: The type of device, i.e., "macvtap", "macvlan" * @macaddress: The MAC address of the device * @srcdev: The name of the 'link' device * @macvlan_mode: The macvlan mode to use * @retry: Pointer to integer that will be '1' upon return if an interface * with the same name already exists and it is worth to try * again with a different name * * Create a macvtap device with the given properties. * * Returns 0 on success, -1 on fatal error. */ int virNetDevMacVLanCreate(const char *ifname, const char *type, const unsigned char *macaddress, const char *srcdev, uint32_t macvlan_mode, int *retry) { int rc = -1; struct nlmsghdr *resp; struct nlmsgerr *err; struct ifinfomsg ifinfo = { .ifi_family = AF_UNSPEC }; int ifindex; unsigned char *recvbuf = NULL; unsigned int recvbuflen; struct nl_msg *nl_msg; struct nlattr *linkinfo, *info_data; if (virNetDevGetIndex(srcdev, &ifindex) < 0) return -1; *retry = 0; nl_msg = nlmsg_alloc_simple(RTM_NEWLINK, NLM_F_REQUEST | NLM_F_CREATE | NLM_F_EXCL); if (!nl_msg) { virReportOOMError(); return -1; } if (nlmsg_append(nl_msg, &ifinfo, sizeof(ifinfo), NLMSG_ALIGNTO) < 0) goto buffer_too_small; if (nla_put_u32(nl_msg, IFLA_LINK, ifindex) < 0) goto buffer_too_small; if (nla_put(nl_msg, IFLA_ADDRESS, VIR_MAC_BUFLEN, macaddress) < 0) goto buffer_too_small; if (ifname && nla_put(nl_msg, IFLA_IFNAME, strlen(ifname)+1, ifname) < 0) goto buffer_too_small; if (!(linkinfo = nla_nest_start(nl_msg, IFLA_LINKINFO))) goto buffer_too_small; if (nla_put(nl_msg, IFLA_INFO_KIND, strlen(type), type) < 0) goto buffer_too_small; if (macvlan_mode > 0) { if (!(info_data = nla_nest_start(nl_msg, IFLA_INFO_DATA))) goto buffer_too_small; if (nla_put(nl_msg, IFLA_MACVLAN_MODE, sizeof(macvlan_mode), &macvlan_mode) < 0) goto buffer_too_small; nla_nest_end(nl_msg, info_data); } nla_nest_end(nl_msg, linkinfo); if (virNetlinkCommand(nl_msg, &recvbuf, &recvbuflen, 0) < 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; switch (err->error) { case 0: break; case -EEXIST: *retry = 1; goto cleanup; default: virReportSystemError(-err->error, _("error creating %s type of interface"), type); 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: virNetDevError(VIR_ERR_INTERNAL_ERROR, "%s", _("malformed netlink response message")); goto cleanup; buffer_too_small: virNetDevError(VIR_ERR_INTERNAL_ERROR, "%s", _("allocated netlink buffer is too small")); goto cleanup; }
/** * virNetlinkCommand: * @nlmsg: pointer to netlink message * @respbuf: pointer to pointer where response buffer will be allocated * @respbuflen: pointer to integer holding the size of the response buffer * on return of the function. * @src_pid: the pid of the process to send a message * @dst_pid: the pid of the process to talk to, i.e., pid = 0 for kernel * @protocol: netlink protocol * @groups: the group identifier * * Send the given message to the netlink layer and receive response. * Returns 0 on success, -1 on error. In case of error, no response * buffer will be returned. */ int virNetlinkCommand(struct nl_msg *nl_msg, struct nlmsghdr **resp, unsigned int *respbuflen, uint32_t src_pid, uint32_t dst_pid, unsigned int protocol, unsigned int groups) { int ret = -1; struct sockaddr_nl nladdr = { .nl_family = AF_NETLINK, .nl_pid = dst_pid, .nl_groups = 0, }; ssize_t nbytes; struct pollfd fds[1]; int fd; int n; struct nlmsghdr *nlmsg = nlmsg_hdr(nl_msg); virNetlinkHandle *nlhandle = NULL; int len = 0; if (protocol >= MAX_LINKS) { virReportSystemError(EINVAL, _("invalid protocol argument: %d"), protocol); goto cleanup; } if (!(nlhandle = virNetlinkCreateSocket(protocol))) goto cleanup; fd = nl_socket_get_fd(nlhandle); if (fd < 0) { virReportSystemError(errno, "%s", _("cannot get netlink socket fd")); goto cleanup; } if (groups && nl_socket_add_membership(nlhandle, groups) < 0) { virReportSystemError(errno, "%s", _("cannot add netlink membership")); goto cleanup; } nlmsg_set_dst(nl_msg, &nladdr); nlmsg->nlmsg_pid = src_pid ? src_pid : getpid(); nbytes = nl_send_auto_complete(nlhandle, nl_msg); if (nbytes < 0) { virReportSystemError(errno, "%s", _("cannot send to netlink socket")); goto cleanup; } memset(fds, 0, sizeof(fds)); fds[0].fd = fd; fds[0].events = POLLIN; n = poll(fds, ARRAY_CARDINALITY(fds), NETLINK_ACK_TIMEOUT_S); if (n <= 0) { if (n < 0) virReportSystemError(errno, "%s", _("error in poll call")); if (n == 0) virReportSystemError(ETIMEDOUT, "%s", _("no valid netlink response was received")); goto cleanup; } len = nl_recv(nlhandle, &nladdr, (unsigned char **)resp, NULL); if (len == 0) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("nl_recv failed - returned 0 bytes")); goto cleanup; } if (len < 0) { virReportSystemError(errno, "%s", _("nl_recv failed")); goto cleanup; } ret = 0; *respbuflen = len; cleanup: if (ret < 0) { *resp = NULL; *respbuflen = 0; } virNetlinkFree(nlhandle); return ret; } /** * virNetlinkDumpLink: * * @ifname: The name of the interface; only use if ifindex <= 0 * @ifindex: The interface index; may be <= 0 if ifname is given * @data: Gets a pointer to the raw data from netlink. MUST BE FREED BY CALLER! * @nlattr: Pointer to a pointer of netlink attributes that will contain * the results * @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) * * Get information from netlink about an interface given its name or index. * * Returns 0 on success, -1 on fatal error. */ int virNetlinkDumpLink(const char *ifname, int ifindex, void **nlData, struct nlattr **tb, uint32_t src_pid, uint32_t dst_pid) { int rc = -1; struct nlmsghdr *resp = NULL; struct nlmsgerr *err; struct ifinfomsg ifinfo = { .ifi_family = AF_UNSPEC, .ifi_index = ifindex }; unsigned int recvbuflen; struct nl_msg *nl_msg; 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; } # ifdef RTEXT_FILTER_VF /* if this filter exists in the kernel's netlink implementation, * we need to set it, otherwise the response message will not * contain the IFLA_VFINFO_LIST that we're looking for. */ { uint32_t ifla_ext_mask = RTEXT_FILTER_VF; if (nla_put(nl_msg, IFLA_EXT_MASK, sizeof(ifla_ext_mask), &ifla_ext_mask) < 0) { goto buffer_too_small; } } # endif if (virNetlinkCommand(nl_msg, &resp, &recvbuflen, src_pid, dst_pid, NETLINK_ROUTE, 0) < 0) goto cleanup; 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); goto cleanup; } 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; } rc = 0; cleanup: nlmsg_free(nl_msg); if (rc < 0) VIR_FREE(resp); *nlData = resp; return rc; malformed_resp: virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("malformed netlink response message")); goto cleanup; buffer_too_small: virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("allocated netlink buffer is too small")); goto cleanup; }