tap.c revision a069762e
1/*
2 *------------------------------------------------------------------
3 * Copyright (c) 2017 Cisco and/or its affiliates.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at:
7 *
8 *     http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 *------------------------------------------------------------------
16 */
17
18#define _GNU_SOURCE
19#include <sys/types.h>
20#include <sys/stat.h>
21#include <fcntl.h>
22#include <net/if.h>
23#include <linux/if_tun.h>
24#include <sys/ioctl.h>
25#include <linux/virtio_net.h>
26#include <linux/vhost.h>
27#include <sys/eventfd.h>
28#include <sched.h>
29
30#include <linux/netlink.h>
31#include <linux/rtnetlink.h>
32
33#include <vlib/vlib.h>
34#include <vlib/physmem.h>
35#include <vlib/unix/unix.h>
36#include <vnet/ethernet/ethernet.h>
37#include <vnet/ip/ip4_packet.h>
38#include <vnet/ip/ip6_packet.h>
39#include <vnet/devices/netlink.h>
40#include <vnet/devices/virtio/virtio.h>
41#include <vnet/devices/tap/tap.h>
42
43tap_main_t tap_main;
44
45#define _IOCTL(fd,a,...) \
46  if (ioctl (fd, a, __VA_ARGS__) < 0) \
47    { \
48      err = clib_error_return_unix (0, "ioctl(" #a ")"); \
49      goto error; \
50    }
51
52static u32
53virtio_eth_flag_change (vnet_main_t * vnm, vnet_hw_interface_t * hi,
54			u32 flags)
55{
56  /* nothing for now */
57  //TODO On MTU change call vnet_netlink_set_if_mtu
58  return 0;
59}
60
61static int
62open_netns_fd (char *netns)
63{
64  u8 *s = 0;
65  int fd;
66
67  if (strncmp (netns, "pid:", 4) == 0)
68    s = format (0, "/proc/%u/ns/net%c", atoi (netns + 4), 0);
69  else if (netns[0] == '/')
70    s = format (0, "%s%c", netns, 0);
71  else
72    s = format (0, "/var/run/netns/%s%c", netns, 0);
73
74  fd = open ((char *) s, O_RDONLY);
75  vec_free (s);
76  return fd;
77}
78
79#define TAP_MAX_INSTANCE 1024
80
81void
82tap_create_if (vlib_main_t * vm, tap_create_if_args_t * args)
83{
84  vlib_physmem_main_t *vpm = &vm->physmem_main;
85  vnet_main_t *vnm = vnet_get_main ();
86  virtio_main_t *vim = &virtio_main;
87  tap_main_t *tm = &tap_main;
88  vnet_sw_interface_t *sw;
89  vnet_hw_interface_t *hw;
90  int i;
91  int old_netns_fd = -1;
92  struct ifreq ifr;
93  size_t hdrsz;
94  struct vhost_memory *vhost_mem = 0;
95  virtio_if_t *vif = 0;
96  clib_error_t *err = 0;
97  int fd = -1;
98  char *host_if_name = 0;
99
100  if (args->id != ~0)
101    {
102      if (clib_bitmap_get (tm->tap_ids, args->id))
103	{
104	  args->rv = VNET_API_ERROR_INVALID_INTERFACE;
105	  args->error = clib_error_return (0, "interface already exists");
106	  return;
107	}
108    }
109  else
110    {
111      args->id = clib_bitmap_first_clear (tm->tap_ids);
112    }
113
114  if (args->id > TAP_MAX_INSTANCE)
115    {
116      args->rv = VNET_API_ERROR_UNSPECIFIED;
117      args->error = clib_error_return (0, "cannot find free interface id");
118      return;
119    }
120
121  clib_memset (&ifr, 0, sizeof (ifr));
122  pool_get (vim->interfaces, vif);
123  vif->dev_instance = vif - vim->interfaces;
124  vif->tap_fd = -1;
125  vif->id = args->id;
126
127  if ((vif->fd = open ("/dev/vhost-net", O_RDWR | O_NONBLOCK)) < 0)
128    {
129      args->rv = VNET_API_ERROR_SYSCALL_ERROR_1;
130      args->error = clib_error_return_unix (0, "open '/dev/vhost-net'");
131      goto error;
132    }
133
134  _IOCTL (vif->fd, VHOST_GET_FEATURES, &vif->remote_features);
135
136  if ((vif->remote_features & VIRTIO_FEATURE (VIRTIO_NET_F_MRG_RXBUF)) == 0)
137    {
138      args->rv = VNET_API_ERROR_UNSUPPORTED;
139      args->error = clib_error_return (0, "vhost-net backend doesn't support "
140				       "VIRTIO_NET_F_MRG_RXBUF feature");
141      goto error;
142    }
143
144  if ((vif->remote_features & VIRTIO_FEATURE (VIRTIO_RING_F_INDIRECT_DESC)) ==
145      0)
146    {
147      args->rv = VNET_API_ERROR_UNSUPPORTED;
148      args->error = clib_error_return (0, "vhost-net backend doesn't support "
149				       "VIRTIO_RING_F_INDIRECT_DESC feature");
150      goto error;
151    }
152
153  if ((vif->remote_features & VIRTIO_FEATURE (VIRTIO_F_VERSION_1)) == 0)
154    {
155      args->rv = VNET_API_ERROR_UNSUPPORTED;
156      args->error = clib_error_return (0, "vhost-net backend doesn't support "
157				       "VIRTIO_F_VERSION_1 features");
158      goto error;
159    }
160
161  vif->features |= VIRTIO_FEATURE (VIRTIO_NET_F_MRG_RXBUF);
162  vif->features |= VIRTIO_FEATURE (VIRTIO_F_VERSION_1);
163  vif->features |= VIRTIO_FEATURE (VIRTIO_RING_F_INDIRECT_DESC);
164
165  virtio_set_net_hdr_size (vif);
166
167  _IOCTL (vif->fd, VHOST_SET_FEATURES, &vif->features);
168
169  if ((vif->tap_fd = open ("/dev/net/tun", O_RDWR | O_NONBLOCK)) < 0)
170    {
171      args->rv = VNET_API_ERROR_SYSCALL_ERROR_2;
172      args->error = clib_error_return_unix (0, "open '/dev/net/tun'");
173      goto error;
174    }
175
176  ifr.ifr_flags = IFF_TAP | IFF_NO_PI | IFF_ONE_QUEUE | IFF_VNET_HDR;
177  _IOCTL (vif->tap_fd, TUNSETIFF, (void *) &ifr);
178  vif->ifindex = if_nametoindex (ifr.ifr_ifrn.ifrn_name);
179
180  if (!args->host_if_name)
181    host_if_name = ifr.ifr_ifrn.ifrn_name;
182  else
183    host_if_name = (char *) args->host_if_name;
184
185  unsigned int offload = 0;
186  hdrsz = sizeof (struct virtio_net_hdr_v1);
187  if (args->tap_flags & TAP_FLAG_GSO)
188    {
189      offload = TUN_F_CSUM | TUN_F_TSO4 | TUN_F_TSO6;
190      vif->gso_enabled = 1;
191    }
192  else
193    {
194      vif->gso_enabled = 0;
195    }
196
197  _IOCTL (vif->tap_fd, TUNSETOFFLOAD, offload);
198  _IOCTL (vif->tap_fd, TUNSETVNETHDRSZ, &hdrsz);
199  _IOCTL (vif->fd, VHOST_SET_OWNER, 0);
200
201  /* if namespace is specified, all further netlink messages should be excuted
202     after we change our net namespace */
203  if (args->host_namespace)
204    {
205      old_netns_fd = open ("/proc/self/ns/net", O_RDONLY);
206      if ((fd = open_netns_fd ((char *) args->host_namespace)) == -1)
207	{
208	  args->rv = VNET_API_ERROR_SYSCALL_ERROR_2;
209	  args->error = clib_error_return_unix (0, "open_netns_fd '%s'",
210						args->host_namespace);
211	  goto error;
212	}
213      args->error = vnet_netlink_set_link_netns (vif->ifindex, fd,
214						 host_if_name);
215      if (args->error)
216	{
217	  args->rv = VNET_API_ERROR_NETLINK_ERROR;
218	  goto error;
219	}
220      if (setns (fd, CLONE_NEWNET) == -1)
221	{
222	  args->rv = VNET_API_ERROR_SYSCALL_ERROR_3;
223	  args->error = clib_error_return_unix (0, "setns '%s'",
224						args->host_namespace);
225	  goto error;
226	}
227      if ((vif->ifindex = if_nametoindex (host_if_name)) == 0)
228	{
229	  args->rv = VNET_API_ERROR_SYSCALL_ERROR_3;
230	  args->error = clib_error_return_unix (0, "if_nametoindex '%s'",
231						host_if_name);
232	  goto error;
233	}
234    }
235  else
236    {
237      if (host_if_name)
238	{
239	  args->error = vnet_netlink_set_link_name (vif->ifindex,
240						    host_if_name);
241	  if (args->error)
242	    {
243	      args->rv = VNET_API_ERROR_NETLINK_ERROR;
244	      goto error;
245	    }
246	}
247    }
248
249  if (!ethernet_mac_address_is_zero (args->host_mac_addr))
250    {
251      args->error = vnet_netlink_set_link_addr (vif->ifindex,
252						args->host_mac_addr);
253      if (args->error)
254	{
255	  args->rv = VNET_API_ERROR_NETLINK_ERROR;
256	  goto error;
257	}
258    }
259
260  if (args->host_bridge)
261    {
262      args->error = vnet_netlink_set_link_master (vif->ifindex,
263						  (char *) args->host_bridge);
264      if (args->error)
265	{
266	  args->rv = VNET_API_ERROR_NETLINK_ERROR;
267	  goto error;
268	}
269    }
270
271
272  if (args->host_ip4_prefix_len)
273    {
274      args->error = vnet_netlink_add_ip4_addr (vif->ifindex,
275					       &args->host_ip4_addr,
276					       args->host_ip4_prefix_len);
277      if (args->error)
278	{
279	  args->rv = VNET_API_ERROR_NETLINK_ERROR;
280	  goto error;
281	}
282    }
283
284  if (args->host_ip6_prefix_len)
285    {
286      args->error = vnet_netlink_add_ip6_addr (vif->ifindex,
287					       &args->host_ip6_addr,
288					       args->host_ip6_prefix_len);
289      if (args->error)
290	{
291	  args->rv = VNET_API_ERROR_NETLINK_ERROR;
292	  goto error;
293	}
294    }
295
296  args->error = vnet_netlink_set_link_state (vif->ifindex, 1 /* UP */ );
297  if (args->error)
298    {
299      args->rv = VNET_API_ERROR_NETLINK_ERROR;
300      goto error;
301    }
302
303  if (args->host_ip4_gw_set)
304    {
305      args->error = vnet_netlink_add_ip4_route (0, 0, &args->host_ip4_gw);
306      if (args->error)
307	{
308	  args->rv = VNET_API_ERROR_NETLINK_ERROR;
309	  goto error;
310	}
311    }
312
313  if (args->host_ip6_gw_set)
314    {
315      args->error = vnet_netlink_add_ip6_route (0, 0, &args->host_ip6_gw);
316      if (args->error)
317	{
318	  args->rv = VNET_API_ERROR_NETLINK_ERROR;
319	  goto error;
320	}
321    }
322
323  /* switch back to old net namespace */
324  if (args->host_namespace)
325    {
326      if (setns (old_netns_fd, CLONE_NEWNET) == -1)
327	{
328	  args->rv = VNET_API_ERROR_SYSCALL_ERROR_2;
329	  args->error = clib_error_return_unix (0, "setns '%s'",
330						args->host_namespace);
331	  goto error;
332	}
333    }
334
335  if (args->host_mtu_set)
336    {
337      args->error =
338	vnet_netlink_set_link_mtu (vif->ifindex, args->host_mtu_size);
339      if (args->error)
340	{
341	  args->rv = VNET_API_ERROR_NETLINK_ERROR;
342	  goto error;
343	}
344    }
345  else if (tm->host_mtu_size != 0)
346    {
347      args->error =
348	vnet_netlink_set_link_mtu (vif->ifindex, tm->host_mtu_size);
349      if (args->error)
350	{
351	  args->rv = VNET_API_ERROR_NETLINK_ERROR;
352	  goto error;
353	}
354      args->host_mtu_set = 1;
355      args->host_mtu_size = tm->host_mtu_size;
356    }
357
358  /* Set vhost memory table */
359  i = sizeof (struct vhost_memory) + sizeof (struct vhost_memory_region);
360  vhost_mem = clib_mem_alloc (i);
361  clib_memset (vhost_mem, 0, i);
362  vhost_mem->nregions = 1;
363  vhost_mem->regions[0].memory_size = vpm->max_size;
364  vhost_mem->regions[0].guest_phys_addr = vpm->base_addr;
365  vhost_mem->regions[0].userspace_addr =
366    vhost_mem->regions[0].guest_phys_addr;
367  _IOCTL (vif->fd, VHOST_SET_MEM_TABLE, vhost_mem);
368
369  if ((args->error =
370       virtio_vring_init (vm, vif, RX_QUEUE (0), args->rx_ring_sz)))
371    {
372      args->rv = VNET_API_ERROR_INIT_FAILED;
373      goto error;
374    }
375  vif->num_rxqs = 1;
376
377  if ((args->error =
378       virtio_vring_init (vm, vif, TX_QUEUE (0), args->tx_ring_sz)))
379    {
380      args->rv = VNET_API_ERROR_INIT_FAILED;
381      goto error;
382    }
383  vif->num_txqs = 1;
384
385  if (!args->mac_addr_set)
386    ethernet_mac_address_generate (args->mac_addr);
387
388  clib_memcpy (vif->mac_addr, args->mac_addr, 6);
389
390  vif->host_if_name = format (0, "%s%c", host_if_name, 0);
391  vif->net_ns = format (0, "%s%c", args->host_namespace, 0);
392  vif->host_bridge = format (0, "%s%c", args->host_bridge, 0);
393  vif->host_mtu_size = args->host_mtu_size;
394  clib_memcpy (vif->host_mac_addr, args->host_mac_addr, 6);
395  vif->host_ip4_prefix_len = args->host_ip4_prefix_len;
396  vif->host_ip6_prefix_len = args->host_ip6_prefix_len;
397  if (args->host_ip4_prefix_len)
398    clib_memcpy (&vif->host_ip4_addr, &args->host_ip4_addr, 4);
399  if (args->host_ip6_prefix_len)
400    clib_memcpy (&vif->host_ip6_addr, &args->host_ip6_addr, 16);
401
402  vif->type = VIRTIO_IF_TYPE_TAP;
403  args->error = ethernet_register_interface (vnm, virtio_device_class.index,
404					     vif->dev_instance,
405					     vif->mac_addr,
406					     &vif->hw_if_index,
407					     virtio_eth_flag_change);
408  if (args->error)
409    {
410      args->rv = VNET_API_ERROR_INVALID_REGISTRATION;
411      goto error;
412    }
413
414  tm->tap_ids = clib_bitmap_set (tm->tap_ids, vif->id, 1);
415  sw = vnet_get_hw_sw_interface (vnm, vif->hw_if_index);
416  vif->sw_if_index = sw->sw_if_index;
417  args->sw_if_index = vif->sw_if_index;
418  args->rv = 0;
419  hw = vnet_get_hw_interface (vnm, vif->hw_if_index);
420  hw->flags |= VNET_HW_INTERFACE_FLAG_SUPPORTS_INT_MODE;
421  if (args->tap_flags & TAP_FLAG_GSO)
422    {
423      hw->flags |= VNET_HW_INTERFACE_FLAG_SUPPORTS_GSO;
424      vnm->interface_main.gso_interface_count++;
425    }
426  vnet_hw_interface_set_input_node (vnm, vif->hw_if_index,
427				    virtio_input_node.index);
428  vnet_hw_interface_assign_rx_thread (vnm, vif->hw_if_index, 0, ~0);
429  vnet_hw_interface_set_rx_mode (vnm, vif->hw_if_index, 0,
430				 VNET_HW_INTERFACE_RX_MODE_DEFAULT);
431  vif->per_interface_next_index = ~0;
432  virtio_vring_set_numa_node (vm, vif, RX_QUEUE (0));
433  vif->flags |= VIRTIO_IF_FLAG_ADMIN_UP;
434  vnet_hw_interface_set_flags (vnm, vif->hw_if_index,
435			       VNET_HW_INTERFACE_FLAG_LINK_UP);
436  vif->cxq_vring = NULL;
437
438  goto done;
439
440error:
441  if (err)
442    {
443      ASSERT (args->error == 0);
444      args->error = err;
445      args->rv = VNET_API_ERROR_SYSCALL_ERROR_3;
446    }
447  if (vif->tap_fd != -1)
448    close (vif->tap_fd);
449  if (vif->fd != -1)
450    close (vif->fd);
451  vec_foreach_index (i, vif->rxq_vrings) virtio_vring_free_rx (vm, vif,
452							       RX_QUEUE (i));
453  vec_foreach_index (i, vif->txq_vrings) virtio_vring_free_tx (vm, vif,
454							       TX_QUEUE (i));
455  vec_free (vif->rxq_vrings);
456  vec_free (vif->txq_vrings);
457
458  vec_free (vif->host_if_name);
459  vec_free (vif->net_ns);
460  vec_free (vif->host_bridge);
461
462  clib_memset (vif, 0, sizeof (virtio_if_t));
463  pool_put (vim->interfaces, vif);
464
465done:
466  if (vhost_mem)
467    clib_mem_free (vhost_mem);
468  if (old_netns_fd != -1)
469    close (old_netns_fd);
470  if (fd != -1)
471    close (fd);
472}
473
474int
475tap_delete_if (vlib_main_t * vm, u32 sw_if_index)
476{
477  vnet_main_t *vnm = vnet_get_main ();
478  virtio_main_t *mm = &virtio_main;
479  tap_main_t *tm = &tap_main;
480  int i;
481  virtio_if_t *vif;
482  vnet_hw_interface_t *hw;
483
484  hw = vnet_get_sup_hw_interface_api_visible_or_null (vnm, sw_if_index);
485  if (hw == NULL || virtio_device_class.index != hw->dev_class_index)
486    return VNET_API_ERROR_INVALID_SW_IF_INDEX;
487
488  vif = pool_elt_at_index (mm->interfaces, hw->dev_instance);
489
490  if (vif->type != VIRTIO_IF_TYPE_TAP)
491    return VNET_API_ERROR_INVALID_INTERFACE;
492
493  /* decrement if this was a GSO interface */
494  if (hw->flags & VNET_HW_INTERFACE_FLAG_SUPPORTS_GSO)
495    vnm->interface_main.gso_interface_count--;
496
497  /* bring down the interface */
498  vnet_hw_interface_set_flags (vnm, vif->hw_if_index, 0);
499  vnet_sw_interface_set_flags (vnm, vif->sw_if_index, 0);
500  vnet_hw_interface_unassign_rx_thread (vnm, vif->hw_if_index, RX_QUEUE (0));
501
502  ethernet_delete_interface (vnm, vif->hw_if_index);
503  vif->hw_if_index = ~0;
504
505  if (vif->tap_fd != -1)
506    close (vif->tap_fd);
507  if (vif->fd != -1)
508    close (vif->fd);
509
510  vec_foreach_index (i, vif->rxq_vrings) virtio_vring_free_rx (vm, vif,
511							       RX_QUEUE (i));
512  vec_foreach_index (i, vif->txq_vrings) virtio_vring_free_tx (vm, vif,
513							       TX_QUEUE (i));
514  vec_free (vif->rxq_vrings);
515  vec_free (vif->txq_vrings);
516
517  vec_free (vif->host_if_name);
518  vec_free (vif->net_ns);
519  vec_free (vif->host_bridge);
520
521  tm->tap_ids = clib_bitmap_set (tm->tap_ids, vif->id, 0);
522  clib_memset (vif, 0, sizeof (*vif));
523  pool_put (mm->interfaces, vif);
524
525  return 0;
526}
527
528int
529tap_gso_enable_disable (vlib_main_t * vm, u32 sw_if_index, int enable_disable)
530{
531  vnet_main_t *vnm = vnet_get_main ();
532  virtio_main_t *mm = &virtio_main;
533  virtio_if_t *vif;
534  vnet_hw_interface_t *hw;
535  clib_error_t *err = 0;
536
537  hw = vnet_get_sup_hw_interface_api_visible_or_null (vnm, sw_if_index);
538
539  if (hw == NULL || virtio_device_class.index != hw->dev_class_index)
540    return VNET_API_ERROR_INVALID_SW_IF_INDEX;
541
542  vif = pool_elt_at_index (mm->interfaces, hw->dev_instance);
543
544  const unsigned int gso_on = TUN_F_CSUM | TUN_F_TSO4 | TUN_F_TSO6;
545  const unsigned int gso_off = 0;
546  unsigned int offload = enable_disable ? gso_on : gso_off;
547  _IOCTL (vif->tap_fd, TUNSETOFFLOAD, offload);
548  vif->gso_enabled = enable_disable ? 1 : 0;
549  if (enable_disable)
550    {
551      if ((hw->flags & VNET_HW_INTERFACE_FLAG_SUPPORTS_GSO) == 0)
552	{
553	  vnm->interface_main.gso_interface_count++;
554	  hw->flags |= VNET_HW_INTERFACE_FLAG_SUPPORTS_GSO;
555	}
556    }
557  else
558    {
559      if ((hw->flags & VNET_HW_INTERFACE_FLAG_SUPPORTS_GSO) != 0)
560	{
561	  vnm->interface_main.gso_interface_count--;
562	  hw->flags &= ~VNET_HW_INTERFACE_FLAG_SUPPORTS_GSO;
563	}
564    }
565
566error:
567  if (err)
568    {
569      clib_warning ("Error %s gso on sw_if_index %d",
570		    enable_disable ? "enabling" : "disabling", sw_if_index);
571      return VNET_API_ERROR_SYSCALL_ERROR_3;
572    }
573  return 0;
574}
575
576int
577tap_dump_ifs (tap_interface_details_t ** out_tapids)
578{
579  vnet_main_t *vnm = vnet_get_main ();
580  virtio_main_t *mm = &virtio_main;
581  virtio_if_t *vif;
582  virtio_vring_t *vring;
583  vnet_hw_interface_t *hi;
584  tap_interface_details_t *r_tapids = NULL;
585  tap_interface_details_t *tapid = NULL;
586
587  /* *INDENT-OFF* */
588  pool_foreach (vif, mm->interfaces,
589    if (vif->type != VIRTIO_IF_TYPE_TAP)
590      continue;
591    vec_add2(r_tapids, tapid, 1);
592    clib_memset (tapid, 0, sizeof (*tapid));
593    tapid->id = vif->id;
594    tapid->sw_if_index = vif->sw_if_index;
595    hi = vnet_get_hw_interface (vnm, vif->hw_if_index);
596    clib_memcpy(tapid->dev_name, hi->name,
597                MIN (ARRAY_LEN (tapid->dev_name) - 1,
598                     strlen ((const char *) hi->name)));
599    vring = vec_elt_at_index (vif->rxq_vrings, RX_QUEUE_ACCESS(0));
600    tapid->rx_ring_sz = vring->size;
601    vring = vec_elt_at_index (vif->txq_vrings, TX_QUEUE_ACCESS(0));
602    tapid->tx_ring_sz = vring->size;
603    clib_memcpy(tapid->host_mac_addr, vif->host_mac_addr, 6);
604    if (vif->host_if_name)
605      {
606        clib_memcpy(tapid->host_if_name, vif->host_if_name,
607                    MIN (ARRAY_LEN (tapid->host_if_name) - 1,
608                    strlen ((const char *) vif->host_if_name)));
609      }
610    if (vif->net_ns)
611      {
612        clib_memcpy(tapid->host_namespace, vif->net_ns,
613                    MIN (ARRAY_LEN (tapid->host_namespace) - 1,
614                    strlen ((const char *) vif->net_ns)));
615      }
616    if (vif->host_bridge)
617      {
618        clib_memcpy(tapid->host_bridge, vif->host_bridge,
619                    MIN (ARRAY_LEN (tapid->host_bridge) - 1,
620                    strlen ((const char *) vif->host_bridge)));
621      }
622    if (vif->host_ip4_prefix_len)
623      clib_memcpy(tapid->host_ip4_addr, &vif->host_ip4_addr, 4);
624    tapid->host_ip4_prefix_len = vif->host_ip4_prefix_len;
625    if (vif->host_ip6_prefix_len)
626      clib_memcpy(tapid->host_ip6_addr, &vif->host_ip6_addr, 16);
627    tapid->host_ip6_prefix_len = vif->host_ip6_prefix_len;
628    tapid->host_mtu_size = vif->host_mtu_size;
629  );
630  /* *INDENT-ON* */
631
632  *out_tapids = r_tapids;
633
634  return 0;
635}
636
637static clib_error_t *
638tap_mtu_config (vlib_main_t * vm, unformat_input_t * input)
639{
640  tap_main_t *tm = &tap_main;
641
642  while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
643    {
644      if (unformat (input, "host-mtu %d", &tm->host_mtu_size))
645	;
646      else
647	return clib_error_return (0, "unknown input `%U'",
648				  format_unformat_error, input);
649    }
650
651  return 0;
652}
653
654/* tap { host-mtu <size> } configuration. */
655VLIB_CONFIG_FUNCTION (tap_mtu_config, "tap");
656
657static clib_error_t *
658tap_init (vlib_main_t * vm)
659{
660  tap_main_t *tm = &tap_main;
661  clib_error_t *error = 0;
662
663  tm->log_default = vlib_log_register_class ("tap", 0);
664  vlib_log_debug (tm->log_default, "initialized");
665
666  tm->host_mtu_size = 0;
667
668  return error;
669}
670
671VLIB_INIT_FUNCTION (tap_init);
672
673/*
674 * fd.io coding-style-patch-verification: ON
675 *
676 * Local Variables:
677 * eval: (c-set-style "gnu")
678 * End:
679 */
680