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