#define _GNU_SOURCE
#include <sys/types.h>
#include <sys/stat.h>
+#include <sys/socket.h>
#include <fcntl.h>
#include <net/if.h>
#include <linux/if_tun.h>
#include <sys/ioctl.h>
+#include <linux/ethtool.h>
+#include <linux/sockios.h>
#include <sys/eventfd.h>
#include <net/if_arp.h>
-#include <sched.h>
#include <limits.h>
#include <linux/netlink.h>
#include <vlib/vlib.h>
#include <vlib/physmem.h>
#include <vlib/unix/unix.h>
+#include <vppinfra/linux/netns.h>
#include <vnet/ethernet/ethernet.h>
#include <vnet/ip/ip4_packet.h>
#include <vnet/ip/ip6_packet.h>
};
/* *INDENT-ON* */
+#define TUN_MAX_PACKET_BYTES 65355
+#define TUN_MIN_PACKET_BYTES 64
+#define TUN_DEFAULT_PACKET_BYTES 1500
+
static u32
virtio_eth_flag_change (vnet_main_t * vnm, vnet_hw_interface_t * hi,
u32 flags)
return 0;
}
-static int
-open_netns_fd (char *netns)
-{
- u8 *s = 0;
- int fd;
-
- if (strncmp (netns, "pid:", 4) == 0)
- s = format (0, "/proc/%u/ns/net%c", atoi (netns + 4), 0);
- else if (netns[0] == '/')
- s = format (0, "%s%c", netns, 0);
- else
- s = format (0, "/var/run/netns/%s%c", netns, 0);
-
- fd = open ((char *) s, O_RDONLY);
- vec_free (s);
- return fd;
-}
-
#define TAP_MAX_INSTANCE 1024
static void
virtio_vring_free_tx (vm, vif, TX_QUEUE (i));
/* *INDENT-ON* */
- _IOCTL (vif->tap_fds[0], TUNSETPERSIST, (void *) (uintptr_t) 0);
- tap_log_dbg (vif, "TUNSETPERSIST: unset");
+ if (vif->tap_fds)
+ {
+ _IOCTL (vif->tap_fds[0], TUNSETPERSIST, (void *) (uintptr_t) 0);
+ tap_log_dbg (vif, "TUNSETPERSIST: unset");
+ }
error:
vec_foreach_index (i, vif->tap_fds) close (vif->tap_fds[i]);
}
if (args->host_namespace)
{
- old_netns_fd = open ("/proc/self/ns/net", O_RDONLY);
- if ((nfd = open_netns_fd ((char *) args->host_namespace)) == -1)
+ old_netns_fd = clib_netns_open (NULL /* self */);
+ if ((nfd = clib_netns_open (args->host_namespace)) == -1)
{
args->rv = VNET_API_ERROR_SYSCALL_ERROR_2;
- args->error = clib_error_return_unix (0, "open_netns_fd '%s'",
+ args->error = clib_error_return_unix (0, "clib_netns_open '%s'",
args->host_namespace);
goto error;
}
- if (setns (nfd, CLONE_NEWNET) == -1)
+ if (clib_setns (nfd) == -1)
{
args->rv = VNET_API_ERROR_SYSCALL_ERROR_3;
args->error = clib_error_return_unix (0, "setns '%s'",
after we change our net namespace */
if (args->host_namespace)
{
- old_netns_fd = open ("/proc/self/ns/net", O_RDONLY);
- if ((nfd = open_netns_fd ((char *) args->host_namespace)) == -1)
+ old_netns_fd = clib_netns_open (NULL /* self */);
+ if ((nfd = clib_netns_open (args->host_namespace)) == -1)
{
args->rv = VNET_API_ERROR_SYSCALL_ERROR_2;
- args->error = clib_error_return_unix (0, "open_netns_fd '%s'",
+ args->error = clib_error_return_unix (0, "clib_netns_open '%s'",
args->host_namespace);
goto error;
}
args->rv = VNET_API_ERROR_NETLINK_ERROR;
goto error;
}
- if (setns (nfd, CLONE_NEWNET) == -1)
+ if (clib_setns (nfd) == -1)
{
args->rv = VNET_API_ERROR_SYSCALL_ERROR_3;
args->error = clib_error_return_unix (0, "setns '%s'",
/* switch back to old net namespace */
if (args->host_namespace)
{
- if (setns (old_netns_fd, CLONE_NEWNET) == -1)
+ if (clib_setns (old_netns_fd) == -1)
{
args->rv = VNET_API_ERROR_SYSCALL_ERROR_2;
args->error = clib_error_return_unix (0, "setns '%s'",
args->sw_if_index = vif->sw_if_index;
args->rv = 0;
hw = vnet_get_hw_interface (vnm, vif->hw_if_index);
- hw->flags |= VNET_HW_INTERFACE_FLAG_SUPPORTS_INT_MODE;
+ hw->caps |= VNET_HW_INTERFACE_CAP_SUPPORTS_INT_MODE;
if (args->tap_flags & TAP_FLAG_GSO)
{
- hw->flags |= VNET_HW_INTERFACE_FLAG_SUPPORTS_GSO |
- VNET_HW_INTERFACE_FLAG_SUPPORTS_TX_L4_CKSUM_OFFLOAD;
+ hw->caps |= VNET_HW_INTERFACE_CAP_SUPPORTS_TCP_GSO |
+ VNET_HW_INTERFACE_CAP_SUPPORTS_TX_TCP_CKSUM |
+ VNET_HW_INTERFACE_CAP_SUPPORTS_TX_UDP_CKSUM;
}
else if (args->tap_flags & TAP_FLAG_CSUM_OFFLOAD)
{
- hw->flags |= VNET_HW_INTERFACE_FLAG_SUPPORTS_TX_L4_CKSUM_OFFLOAD;
+ hw->caps |= VNET_HW_INTERFACE_CAP_SUPPORTS_TX_TCP_CKSUM |
+ VNET_HW_INTERFACE_CAP_SUPPORTS_TX_UDP_CKSUM;
}
if ((args->tap_flags & TAP_FLAG_GSO)
&& (args->tap_flags & TAP_FLAG_GRO_COALESCE))
{
virtio_set_packet_coalesce (vif);
}
- vnet_hw_interface_set_input_node (vnm, vif->hw_if_index,
- virtio_input_node.index);
-
- for (i = 0; i < vif->num_rxqs; i++)
+ if (vif->type == VIRTIO_IF_TYPE_TUN)
{
- vnet_hw_interface_assign_rx_thread (vnm, vif->hw_if_index, i, ~0);
- vnet_hw_interface_set_rx_mode (vnm, vif->hw_if_index, i,
- VNET_HW_IF_RX_MODE_DEFAULT);
- virtio_vring_set_numa_node (vm, vif, RX_QUEUE (i));
+ hw->max_supported_packet_bytes = TUN_MAX_PACKET_BYTES;
+ hw->min_packet_bytes = hw->min_supported_packet_bytes =
+ TUN_MIN_PACKET_BYTES;
+ hw->max_packet_bytes =
+ args->host_mtu_size ? args->host_mtu_size : TUN_DEFAULT_PACKET_BYTES;
+ vnet_sw_interface_set_mtu (vnm, hw->sw_if_index, hw->max_packet_bytes);
}
+ virtio_vring_set_rx_queues (vm, vif);
+
vif->per_interface_next_index = ~0;
vif->flags |= VIRTIO_IF_FLAG_ADMIN_UP;
vnet_hw_interface_set_flags (vnm, vif->hw_if_index,
VNET_HW_INTERFACE_FLAG_LINK_UP);
+ /*
+ * Host tun/tap driver link carrier state is "up" at creation. The
+ * driver never changes this unless the backend (VPP) changes it using
+ * TUNSETCARRIER ioctl(). See tap_set_carrier().
+ */
+ vif->host_carrier_up = 1;
vif->cxq_vring = NULL;
goto done;
{
vnet_main_t *vnm = vnet_get_main ();
virtio_main_t *mm = &virtio_main;
- int i;
virtio_if_t *vif;
vnet_hw_interface_t *hw;
/* bring down the interface */
vnet_hw_interface_set_flags (vnm, vif->hw_if_index, 0);
vnet_sw_interface_set_flags (vnm, vif->sw_if_index, 0);
- for (i = 0; i < vif->num_rxqs; i++)
- vnet_hw_interface_unassign_rx_thread (vnm, vif->hw_if_index, i);
if (vif->type == VIRTIO_IF_TYPE_TAP)
ethernet_delete_interface (vnm, vif->hw_if_index);
vif->packet_coalesce = 0;
vif->csum_offload_enabled = enable_disable ? 1 : 0;
- if ((hw->flags & VNET_HW_INTERFACE_FLAG_SUPPORTS_GSO) != 0)
+ if ((hw->caps & VNET_HW_INTERFACE_CAP_SUPPORTS_TCP_GSO) != 0)
{
- hw->flags &= ~VNET_HW_INTERFACE_FLAG_SUPPORTS_GSO;
+ hw->caps &= ~VNET_HW_INTERFACE_CAP_SUPPORTS_TCP_GSO;
}
if (enable_disable)
{
- if ((hw->flags & VNET_HW_INTERFACE_FLAG_SUPPORTS_TX_L4_CKSUM_OFFLOAD) ==
- 0)
- {
- hw->flags |= VNET_HW_INTERFACE_FLAG_SUPPORTS_TX_L4_CKSUM_OFFLOAD;
- }
+ hw->caps |= VNET_HW_INTERFACE_CAP_SUPPORTS_L4_TX_CKSUM;
}
else
{
- if ((hw->flags & VNET_HW_INTERFACE_FLAG_SUPPORTS_TX_L4_CKSUM_OFFLOAD) !=
- 0)
- {
- hw->flags &= ~VNET_HW_INTERFACE_FLAG_SUPPORTS_TX_L4_CKSUM_OFFLOAD;
- }
+ hw->caps &= ~VNET_HW_INTERFACE_CAP_SUPPORTS_L4_TX_CKSUM;
}
error:
vif->csum_offload_enabled = 0;
if (enable_disable)
{
- if ((hw->flags & VNET_HW_INTERFACE_FLAG_SUPPORTS_GSO) == 0)
+ if ((hw->caps & VNET_HW_INTERFACE_CAP_SUPPORTS_TCP_GSO) == 0)
{
- hw->flags |= VNET_HW_INTERFACE_FLAG_SUPPORTS_GSO |
- VNET_HW_INTERFACE_FLAG_SUPPORTS_TX_L4_CKSUM_OFFLOAD;
+ hw->caps |= VNET_HW_INTERFACE_CAP_SUPPORTS_TCP_GSO |
+ VNET_HW_INTERFACE_CAP_SUPPORTS_L4_TX_CKSUM;
}
if (is_packet_coalesce)
{
}
else
{
- if ((hw->flags & VNET_HW_INTERFACE_FLAG_SUPPORTS_GSO) != 0)
+ if ((hw->caps & VNET_HW_INTERFACE_CAP_SUPPORTS_TCP_GSO) != 0)
{
- hw->flags &= ~(VNET_HW_INTERFACE_FLAG_SUPPORTS_GSO |
- VNET_HW_INTERFACE_FLAG_SUPPORTS_TX_L4_CKSUM_OFFLOAD);
+ hw->caps &= ~(VNET_HW_INTERFACE_CAP_SUPPORTS_TCP_GSO |
+ VNET_HW_INTERFACE_CAP_SUPPORTS_L4_TX_CKSUM);
}
vif->packet_coalesce = 0;
}
tap_interface_details_t *tapid = NULL;
/* *INDENT-OFF* */
- pool_foreach (vif, mm->interfaces,
+ pool_foreach (vif, mm->interfaces) {
if ((vif->type != VIRTIO_IF_TYPE_TAP)
&& (vif->type != VIRTIO_IF_TYPE_TUN))
continue;
clib_memcpy(tapid->host_ip6_addr.as_u8, &vif->host_ip6_addr, 16);
tapid->host_ip6_prefix_len = vif->host_ip6_prefix_len;
tapid->host_mtu_size = vif->host_mtu_size;
- );
+ }
/* *INDENT-ON* */
*out_tapids = r_tapids;
return 0;
}
+/*
+ * Set host tap/tun interface carrier state so it will appear to host
+ * applications that the interface's link state changed.
+ *
+ * If the kernel we're building against does not have support for the
+ * TUNSETCARRIER ioctl command, do nothing.
+ */
+int
+tap_set_carrier (u32 hw_if_index, u32 carrier_up)
+{
+ int ret = 0;
+#ifdef TUNSETCARRIER
+ vnet_main_t *vnm = vnet_get_main ();
+ vnet_hw_interface_t *hi = vnet_get_hw_interface (vnm, hw_if_index);
+ virtio_main_t *mm = &virtio_main;
+ virtio_if_t *vif;
+ int *fd;
+
+ vif = pool_elt_at_index (mm->interfaces, hi->dev_instance);
+ vec_foreach (fd, vif->tap_fds)
+ {
+ ret = ioctl (*fd, TUNSETCARRIER, &carrier_up);
+ if (ret < 0)
+ {
+ clib_warning ("ioctl (TUNSETCARRIER) returned %d", ret);
+ break;
+ }
+ }
+ if (!ret)
+ vif->host_carrier_up = (carrier_up != 0);
+#endif
+
+ return ret;
+}
+
static clib_error_t *
tap_mtu_config (vlib_main_t * vm, unformat_input_t * input)
{
return 0;
}
+/*
+ * Set host tap/tun interface speed in Mbps.
+ */
+int
+tap_set_speed (u32 hw_if_index, u32 speed)
+{
+ vnet_main_t *vnm = vnet_get_main ();
+ vnet_hw_interface_t *hi = vnet_get_hw_interface (vnm, hw_if_index);
+ virtio_main_t *mm = &virtio_main;
+ virtio_if_t *vif;
+ int old_netns_fd = -1;
+ int nfd = -1;
+ int ctl_fd = -1;
+ struct ifreq ifr;
+ struct ethtool_cmd ecmd;
+ int ret = -1;
+
+ vif = pool_elt_at_index (mm->interfaces, hi->dev_instance);
+
+ if (vif->net_ns)
+ {
+ old_netns_fd = clib_netns_open (NULL /* self */);
+ if ((nfd = clib_netns_open (vif->net_ns)) == -1)
+ {
+ clib_warning ("Cannot open netns");
+ goto done;
+ }
+ if (clib_setns (nfd) == -1)
+ {
+ clib_warning ("Cannot set ns");
+ goto done;
+ }
+ }
+
+ if ((ctl_fd = socket (AF_INET, SOCK_STREAM, 0)) == -1)
+ {
+ clib_warning ("Cannot open control socket");
+ goto done;
+ }
+
+ ecmd.cmd = ETHTOOL_GSET;
+ clib_memset (&ifr, 0, sizeof (ifr));
+ clib_memcpy (ifr.ifr_name, vif->host_if_name,
+ strlen ((const char *) vif->host_if_name));
+ ifr.ifr_data = (void *) &ecmd;
+ if ((ret = ioctl (ctl_fd, SIOCETHTOOL, &ifr)) < 0)
+ {
+ clib_warning ("Cannot get device settings");
+ goto done;
+ }
+
+ if (ethtool_cmd_speed (&ecmd) != speed)
+ {
+ ecmd.cmd = ETHTOOL_SSET;
+ ethtool_cmd_speed_set (&ecmd, speed);
+ if ((ret = ioctl (ctl_fd, SIOCETHTOOL, &ifr)) < 0)
+ {
+ clib_warning ("Cannot set device settings");
+ goto done;
+ }
+ }
+
+done:
+ if (old_netns_fd != -1)
+ {
+ if (clib_setns (old_netns_fd) == -1)
+ {
+ clib_warning ("Cannot set old ns");
+ }
+ close (old_netns_fd);
+ }
+ if (nfd != -1)
+ close (nfd);
+ if (ctl_fd != -1)
+ close (ctl_fd);
+
+ return ret;
+}
+
/* tap { host-mtu <size> } configuration. */
VLIB_CONFIG_FUNCTION (tap_mtu_config, "tap");