/* SPDX-License-Identifier: BSD-3-Clause
* Copyright 2017 6WIND S.A.
- * Copyright 2017 Mellanox Technologies, Ltd.
+ * Copyright 2017 Mellanox Technologies, Ltd
*/
#include <errno.h>
#include <fcntl.h>
#include <inttypes.h>
#include <linux/sockios.h>
+#include <linux/netlink.h>
+#include <linux/rtnetlink.h>
#include <net/if.h>
#include <net/if_arp.h>
#include <netinet/ip.h>
#include <rte_errno.h>
#include <rte_ethdev.h>
#include <rte_ether.h>
+#include <rte_hypervisor.h>
#include <rte_kvargs.h>
#include <rte_log.h>
#define VDEV_NETVSC_DRIVER net_vdev_netvsc
+#define VDEV_NETVSC_DRIVER_NAME RTE_STR(VDEV_NETVSC_DRIVER)
#define VDEV_NETVSC_ARG_IFACE "iface"
#define VDEV_NETVSC_ARG_MAC "mac"
+#define VDEV_NETVSC_ARG_FORCE "force"
+#define VDEV_NETVSC_ARG_IGNORE "ignore"
#define VDEV_NETVSC_PROBE_MS 1000
#define NETVSC_CLASS_ID "{f8615163-df3e-46c5-913f-f2d2f965ed0e}"
+#define NETVSC_MAX_ROUTE_LINE_SIZE 300
#define DRV_LOG(level, ...) \
rte_log(RTE_LOG_ ## level, \
vdev_netvsc_logtype, \
- RTE_FMT(RTE_STR(VDEV_NETVSC_DRIVER) ": " \
+ RTE_FMT(VDEV_NETVSC_DRIVER_NAME ": " \
RTE_FMT_HEAD(__VA_ARGS__,) "\n", \
RTE_FMT_TAIL(__VA_ARGS__,)))
return ret;
}
+/**
+ * Determine if a network interface has a route.
+ *
+ * @param[in] name
+ * Network device name.
+ * @param[in] family
+ * Address family: AF_INET for IPv4 or AF_INET6 for IPv6.
+ *
+ * @return
+ * 1 when interface has a route, negative errno value in case of error and
+ * 0 otherwise.
+ */
+static int
+vdev_netvsc_has_route(const struct if_nameindex *iface,
+ const unsigned char family)
+{
+ /*
+ * The implementation can be simpler by getifaddrs() function usage but
+ * it works for IPv6 only starting from glibc 2.3.3.
+ */
+ char buf[4096];
+ int len;
+ int ret = 0;
+ int res;
+ int sock;
+ struct nlmsghdr *retmsg = (struct nlmsghdr *)buf;
+ struct sockaddr_nl sa;
+ struct {
+ struct nlmsghdr nlhdr;
+ struct ifaddrmsg addrmsg;
+ } msg;
+
+ if (!iface || (family != AF_INET && family != AF_INET6)) {
+ DRV_LOG(ERR, "%s", rte_strerror(EINVAL));
+ return -EINVAL;
+ }
+ sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
+ if (sock == -1) {
+ DRV_LOG(ERR, "cannot open socket: %s", rte_strerror(errno));
+ return -errno;
+ }
+ memset(&sa, 0, sizeof(sa));
+ sa.nl_family = AF_NETLINK;
+ sa.nl_groups = RTMGRP_LINK | RTMGRP_IPV4_IFADDR;
+ res = bind(sock, (struct sockaddr *)&sa, sizeof(sa));
+ if (res == -1) {
+ ret = -errno;
+ DRV_LOG(ERR, "cannot bind socket: %s", rte_strerror(errno));
+ goto close;
+ }
+ memset(&msg, 0, sizeof(msg));
+ msg.nlhdr.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg));
+ msg.nlhdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
+ msg.nlhdr.nlmsg_type = RTM_GETADDR;
+ msg.nlhdr.nlmsg_pid = getpid();
+ msg.addrmsg.ifa_family = family;
+ msg.addrmsg.ifa_index = iface->if_index;
+ res = send(sock, &msg, msg.nlhdr.nlmsg_len, 0);
+ if (res == -1) {
+ ret = -errno;
+ DRV_LOG(ERR, "cannot send socket message: %s",
+ rte_strerror(errno));
+ goto close;
+ }
+ memset(buf, 0, sizeof(buf));
+ len = recv(sock, buf, sizeof(buf), 0);
+ if (len == -1) {
+ ret = -errno;
+ DRV_LOG(ERR, "cannot receive socket message: %s",
+ rte_strerror(errno));
+ goto close;
+ }
+ while (NLMSG_OK(retmsg, (unsigned int)len)) {
+ struct ifaddrmsg *retaddr =
+ (struct ifaddrmsg *)NLMSG_DATA(retmsg);
+
+ if (retaddr->ifa_family == family &&
+ retaddr->ifa_index == iface->if_index) {
+ struct rtattr *retrta = IFA_RTA(retaddr);
+ int attlen = IFA_PAYLOAD(retmsg);
+
+ while (RTA_OK(retrta, attlen)) {
+ if (retrta->rta_type == IFA_ADDRESS) {
+ ret = 1;
+ DRV_LOG(DEBUG, "interface %s has IP",
+ iface->if_name);
+ goto close;
+ }
+ retrta = RTA_NEXT(retrta, attlen);
+ }
+ }
+ retmsg = NLMSG_NEXT(retmsg, len);
+ }
+close:
+ close(sock);
+ return ret;
+}
+
/**
* Retrieve network interface data from sysfs symbolic link.
*
LIST_FOREACH(ctx, &vdev_netvsc_ctx_list, entry) {
ret = vdev_netvsc_foreach_iface(vdev_netvsc_device_probe, ctx);
- if (ret)
+ if (ret < 0)
break;
}
if (!vdev_netvsc_ctx_count)
* - struct rte_kvargs *kvargs:
* Device arguments provided to current driver instance.
*
+ * - int force:
+ * Accept specified interface even if not detected as NetVSC.
+ *
* - unsigned int specified:
* Number of specific netdevices provided as device arguments.
*
{
const char *name = va_arg(ap, const char *);
struct rte_kvargs *kvargs = va_arg(ap, struct rte_kvargs *);
+ int force = va_arg(ap, int);
unsigned int specified = va_arg(ap, unsigned int);
unsigned int *matched = va_arg(ap, unsigned int *);
unsigned int i;
return 0;
}
if (!vdev_netvsc_iface_is_netvsc(iface)) {
- if (!specified)
+ if (!specified || !force)
return 0;
DRV_LOG(WARNING,
- "interface \"%s\" (index %u) is not NetVSC,"
- " skipping",
+ "using non-NetVSC interface \"%s\" (index %u)",
iface->if_name, iface->if_index);
- return 0;
+ }
+ /* Routed NetVSC should not be probed. */
+ if (vdev_netvsc_has_route(iface, AF_INET) ||
+ vdev_netvsc_has_route(iface, AF_INET6)) {
+ if (!specified)
+ return 0;
+ DRV_LOG(WARNING, "probably using routed NetVSC interface \"%s\""
+ " (index %u)", iface->if_name, iface->if_index);
}
/* Create interface context. */
ctx = calloc(1, sizeof(*ctx));
name, ctx->id);
if (ret == -1 || (size_t)ret >= sizeof(ctx->name))
++i;
- ret = snprintf(ctx->devname, sizeof(ctx->devname), "net_failsafe_%s",
- ctx->name);
+ ret = snprintf(ctx->devname, sizeof(ctx->devname), "net_failsafe_vsc%u",
+ ctx->id);
if (ret == -1 || (size_t)ret >= sizeof(ctx->devname))
++i;
ret = snprintf(ctx->devargs, sizeof(ctx->devargs),
- "fd(%d),dev(net_tap_%s,remote=%s)",
- ctx->pipe[0], ctx->name, ctx->if_name);
+ "fd(%d),dev(net_tap_vsc%u,remote=%s)",
+ ctx->pipe[0], ctx->id, ctx->if_name);
if (ret == -1 || (size_t)ret >= sizeof(ctx->devargs))
++i;
if (i) {
static const char *const vdev_netvsc_arg[] = {
VDEV_NETVSC_ARG_IFACE,
VDEV_NETVSC_ARG_MAC,
+ VDEV_NETVSC_ARG_FORCE,
+ VDEV_NETVSC_ARG_IGNORE,
NULL,
};
const char *name = rte_vdev_device_name(dev);
vdev_netvsc_arg);
unsigned int specified = 0;
unsigned int matched = 0;
+ int force = 0;
+ int ignore = 0;
unsigned int i;
int ret;
for (i = 0; i != kvargs->count; ++i) {
const struct rte_kvargs_pair *pair = &kvargs->pairs[i];
- if (!strcmp(pair->key, VDEV_NETVSC_ARG_IFACE) ||
- !strcmp(pair->key, VDEV_NETVSC_ARG_MAC))
+ if (!strcmp(pair->key, VDEV_NETVSC_ARG_FORCE))
+ force = !!atoi(pair->value);
+ else if (!strcmp(pair->key, VDEV_NETVSC_ARG_IGNORE))
+ ignore = !!atoi(pair->value);
+ else if (!strcmp(pair->key, VDEV_NETVSC_ARG_IFACE) ||
+ !strcmp(pair->key, VDEV_NETVSC_ARG_MAC))
++specified;
}
+ if (ignore) {
+ if (kvargs)
+ rte_kvargs_free(kvargs);
+ return 0;
+ }
rte_eal_alarm_cancel(vdev_netvsc_alarm, NULL);
/* Gather interfaces. */
ret = vdev_netvsc_foreach_iface(vdev_netvsc_netvsc_probe, name, kvargs,
- specified, &matched);
+ force, specified, &matched);
if (ret < 0)
goto error;
if (matched < specified)
RTE_PMD_REGISTER_ALIAS(VDEV_NETVSC_DRIVER, eth_vdev_netvsc);
RTE_PMD_REGISTER_PARAM_STRING(net_vdev_netvsc,
VDEV_NETVSC_ARG_IFACE "=<string> "
- VDEV_NETVSC_ARG_MAC "=<string>");
+ VDEV_NETVSC_ARG_MAC "=<string> "
+ VDEV_NETVSC_ARG_FORCE "=<int> "
+ VDEV_NETVSC_ARG_IGNORE "=<int>");
/** Initialize driver log type. */
RTE_INIT(vdev_netvsc_init_log)
if (vdev_netvsc_logtype >= 0)
rte_log_set_level(vdev_netvsc_logtype, RTE_LOG_NOTICE);
}
+
+/** Compare function for vdev find device operation. */
+static int
+vdev_netvsc_cmp_rte_device(const struct rte_device *dev1,
+ __rte_unused const void *_dev2)
+{
+ return strcmp(dev1->devargs->name, VDEV_NETVSC_DRIVER_NAME);
+}
+
+/**
+ * A callback called by vdev bus scan function to ensure this driver probing
+ * automatically in Hyper-V VM system unless it already exists in the
+ * devargs list.
+ */
+static void
+vdev_netvsc_scan_callback(__rte_unused void *arg)
+{
+ struct rte_vdev_device *dev;
+ struct rte_devargs *devargs;
+ struct rte_bus *vbus = rte_bus_find_by_name("vdev");
+
+ RTE_EAL_DEVARGS_FOREACH("vdev", devargs)
+ if (!strcmp(devargs->name, VDEV_NETVSC_DRIVER_NAME))
+ return;
+ dev = (struct rte_vdev_device *)vbus->find_device(NULL,
+ vdev_netvsc_cmp_rte_device, VDEV_NETVSC_DRIVER_NAME);
+ if (dev)
+ return;
+ if (rte_devargs_add(RTE_DEVTYPE_VIRTUAL, VDEV_NETVSC_DRIVER_NAME))
+ DRV_LOG(ERR, "unable to add netvsc devargs.");
+}
+
+/** Initialize the custom scan. */
+RTE_INIT(vdev_netvsc_custom_scan_add)
+{
+ if (rte_hypervisor_get() == RTE_HYPERVISOR_HYPERV)
+ rte_vdev_add_custom_scan(vdev_netvsc_scan_callback, NULL);
+}