+/*
+ * 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)
+{
+ tap_main_t *tm = &tap_main;
+
+ while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
+ {
+ if (unformat (input, "host-mtu %d", &tm->host_mtu_size))
+ ;
+ else
+ return clib_error_return (0, "unknown input `%U'",
+ format_unformat_error, 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");
+