tap: revert clean-up when linux will delete the tap interface
[vpp.git] / src / vnet / devices / tap / tap.c
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
43 tap_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
52 static u32
53 virtio_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
61 static int
62 open_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
81 void
82 tap_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
440 error:
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
465 done:
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
474 int
475 tap_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
528 int
529 tap_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
566 error:
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
576 int
577 tap_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
637 static clib_error_t *
638 tap_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. */
655 VLIB_CONFIG_FUNCTION (tap_mtu_config, "tap");
656
657 static clib_error_t *
658 tap_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
671 VLIB_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  */