2 *------------------------------------------------------------------
3 * tuntap.c - kernel stack (reverse) punt/inject path
5 * Copyright (c) 2009 Cisco and/or its affiliates.
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at:
10 * http://www.apache.org/licenses/LICENSE-2.0
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
17 *------------------------------------------------------------------
20 #include <fcntl.h> /* for open */
21 #include <sys/ioctl.h>
22 #include <sys/socket.h>
24 #include <sys/types.h>
25 #include <sys/uio.h> /* for iovec */
26 #include <netinet/in.h>
28 #include <linux/if_arp.h>
29 #include <linux/if_tun.h>
31 #include <vlib/vlib.h>
32 #include <vlib/unix/unix.h>
34 #include <vnet/ip/ip.h>
36 #include <vnet/ethernet/ethernet.h>
39 #include <vnet/devices/dpdk/dpdk.h>
42 static vnet_device_class_t tuntap_dev_class;
43 static vnet_hw_interface_class_t tuntap_interface_class;
45 static void tuntap_punt_frame (vlib_main_t * vm,
46 vlib_node_runtime_t * node,
47 vlib_frame_t * frame);
48 static void tuntap_nopunt_frame (vlib_main_t * vm,
49 vlib_node_runtime_t * node,
50 vlib_frame_t * frame);
53 * This driver runs in one of two distinct modes:
54 * "punt/inject" mode, where we send pkts not otherwise processed
55 * by the forwarding to the Linux kernel stack, and
56 * "normal interface" mode, where we treat the Linux kernel stack
59 * By default, we select punt/inject mode.
69 /* Vector of iovecs for readv/writev calls. */
70 struct iovec * iovecs;
72 /* Vector of VLIB rx buffers to use. We allocate them in blocks
73 of VLIB_FRAME_SIZE (256). */
76 /* File descriptors for /dev/net/tun and provisioning socket. */
77 int dev_net_tun_fd, dev_tap_fd;
79 /* Create a "tap" [ethernet] encaps device */
82 /* 1 if a "normal" routed intfc, 0 if a punt/inject interface */
84 int have_normal_interface;
86 /* tap device destination MAC address. Required, or Linux drops pkts */
89 /* Interface MTU in bytes and # of default sized buffers. */
90 u32 mtu_bytes, mtu_buffers;
92 /* Linux interface name for tun device. */
95 /* Pool of subinterface addresses */
96 subif_address_t *subifs;
98 /* Hash for subif addresses */
103 /* For the "normal" interface, if configured */
104 u32 hw_if_index, sw_if_index;
108 static tuntap_main_t tuntap_main = {
111 /* Suitable defaults for an Ethernet-like tun/tap device */
112 .mtu_bytes = 4096 + 256,
117 * Output node, writes the buffers comprising the incoming frame
118 * to the tun/tap device, aka hands them to the Linux kernel stack.
122 tuntap_tx (vlib_main_t * vm,
123 vlib_node_runtime_t * node,
124 vlib_frame_t * frame)
126 u32 * buffers = vlib_frame_args (frame);
127 uword n_packets = frame->n_vectors;
128 tuntap_main_t * tm = &tuntap_main;
131 for (i = 0; i < n_packets; i++)
137 b = vlib_get_buffer (vm, buffers[i]);
139 if (tm->is_ether && (!tm->have_normal_interface))
141 vlib_buffer_reset(b);
142 clib_memcpy (vlib_buffer_get_current (b), tm->ether_dst_mac, 6);
145 /* Re-set iovecs if present. */
147 _vec_len (tm->iovecs) = 0;
149 /* VLIB buffer chain -> Unix iovec(s). */
150 vec_add2 (tm->iovecs, iov, 1);
151 iov->iov_base = b->data + b->current_data;
152 iov->iov_len = l = b->current_length;
154 if (PREDICT_FALSE (b->flags & VLIB_BUFFER_NEXT_PRESENT))
157 b = vlib_get_buffer (vm, b->next_buffer);
159 vec_add2 (tm->iovecs, iov, 1);
161 iov->iov_base = b->data + b->current_data;
162 iov->iov_len = b->current_length;
163 l += b->current_length;
164 } while (b->flags & VLIB_BUFFER_NEXT_PRESENT);
167 if (writev (tm->dev_net_tun_fd, tm->iovecs, vec_len (tm->iovecs)) < l)
168 clib_unix_warning ("writev");
171 /* The normal interface path flattens the buffer chain */
172 if (tm->have_normal_interface)
173 vlib_buffer_free_no_next (vm, buffers, n_packets);
175 vlib_buffer_free (vm, buffers, n_packets);
180 VLIB_REGISTER_NODE (tuntap_tx_node,static) = {
181 .function = tuntap_tx,
183 .type = VLIB_NODE_TYPE_INTERNAL,
188 TUNTAP_RX_NEXT_IP4_INPUT,
189 TUNTAP_RX_NEXT_IP6_INPUT,
190 TUNTAP_RX_NEXT_ETHERNET_INPUT,
196 tuntap_rx (vlib_main_t * vm,
197 vlib_node_runtime_t * node,
198 vlib_frame_t * frame)
200 tuntap_main_t * tm = &tuntap_main;
203 const uword buffer_size = VLIB_BUFFER_DATA_SIZE;
205 u32 free_list_index = VLIB_BUFFER_DEFAULT_FREE_LIST_INDEX;
207 dpdk_main_t * dm = &dpdk_main;
208 u32 free_list_index = dm->vlib_buffer_free_list_index;
209 struct rte_mbuf *first_mb = NULL, *prev_mb = NULL;
212 /* Make sure we have some RX buffers. */
214 uword n_left = vec_len (tm->rx_buffers);
217 if (n_left < VLIB_FRAME_SIZE / 2)
219 if (! tm->rx_buffers)
220 vec_alloc (tm->rx_buffers, VLIB_FRAME_SIZE);
222 n_alloc = vlib_buffer_alloc_from_free_list
223 (vm, tm->rx_buffers + n_left, VLIB_FRAME_SIZE - n_left,
225 _vec_len (tm->rx_buffers) = n_left + n_alloc;
229 /* Allocate RX buffers from end of rx_buffers.
230 Turn them into iovecs to pass to readv. */
232 uword i_rx = vec_len (tm->rx_buffers) - 1;
234 word i, n_bytes_left, n_bytes_in_packet;
236 /* We should have enough buffers left for an MTU sized packet. */
237 ASSERT (vec_len (tm->rx_buffers) >= tm->mtu_buffers);
239 vec_validate (tm->iovecs, tm->mtu_buffers - 1);
240 for (i = 0; i < tm->mtu_buffers; i++)
242 b = vlib_get_buffer (vm, tm->rx_buffers[i_rx - i]);
243 tm->iovecs[i].iov_base = b->data;
244 tm->iovecs[i].iov_len = buffer_size;
247 n_bytes_left = readv (tm->dev_net_tun_fd, tm->iovecs, tm->mtu_buffers);
248 n_bytes_in_packet = n_bytes_left;
249 if (n_bytes_left <= 0)
252 clib_unix_warning ("readv %d", n_bytes_left);
256 bi = tm->rx_buffers[i_rx];
261 struct rte_mbuf * mb;
263 b = vlib_get_buffer (vm, tm->rx_buffers[i_rx]);
265 mb = rte_mbuf_from_vlib_buffer(b);
267 if (first_mb == NULL)
278 b->current_length = n_bytes_left < buffer_size ? n_bytes_left : buffer_size;
280 n_bytes_left -= buffer_size;
282 rte_pktmbuf_data_len (mb) = b->current_length;
283 mb->data_off = RTE_PKTMBUF_HEADROOM + b->current_data;
286 if (n_bytes_left <= 0)
289 rte_pktmbuf_pkt_len (first_mb) = n_bytes_in_packet;
295 b->flags |= VLIB_BUFFER_NEXT_PRESENT;
296 b->next_buffer = tm->rx_buffers[i_rx];
302 /* Interface counters for tuntap interface. */
303 vlib_increment_combined_counter
304 (vnet_main.interface_main.combined_sw_if_counters
305 + VNET_INTERFACE_COUNTER_RX,
308 1, n_bytes_in_packet);
310 _vec_len (tm->rx_buffers) = i_rx;
313 b = vlib_get_buffer (vm, bi);
317 uword n_trace = vlib_get_trace_count (vm, node);
319 vnet_buffer (b)->sw_if_index[VLIB_RX] = tm->sw_if_index;
320 vnet_buffer (b)->sw_if_index[VLIB_TX] = (u32)~0;
323 * Turn this on if you run into
324 * "bad monkey" contexts, and you want to know exactly
325 * which nodes they've visited...
327 if (VLIB_BUFFER_TRACE_TRAJECTORY)
330 b->error = node->errors[0];
334 next_index = TUNTAP_RX_NEXT_ETHERNET_INPUT;
337 switch (b->data[0] & 0xf0)
340 next_index = TUNTAP_RX_NEXT_IP4_INPUT;
343 next_index = TUNTAP_RX_NEXT_IP6_INPUT;
346 next_index = TUNTAP_RX_NEXT_DROP;
350 /* The linux kernel couldn't care less if our interface is up */
351 if (tm->have_normal_interface)
353 vnet_main_t *vnm = vnet_get_main();
354 vnet_sw_interface_t * si;
355 si = vnet_get_sw_interface (vnm, tm->sw_if_index);
356 if (!(si->flags & VNET_SW_INTERFACE_FLAG_ADMIN_UP))
357 next_index = TUNTAP_RX_NEXT_DROP;
360 vlib_set_next_frame_buffer (vm, node, next_index, bi);
364 vlib_trace_buffer (vm, node, next_index,
365 b, /* follow_chain */ 1);
366 vlib_set_trace_count (vm, node, n_trace - 1);
373 static char * tuntap_rx_error_strings[] = {
374 "unknown packet type",
377 VLIB_REGISTER_NODE (tuntap_rx_node,static) = {
378 .function = tuntap_rx,
380 .type = VLIB_NODE_TYPE_INPUT,
381 .state = VLIB_NODE_STATE_INTERRUPT,
384 .error_strings = tuntap_rx_error_strings,
386 .n_next_nodes = TUNTAP_RX_N_NEXT,
388 [TUNTAP_RX_NEXT_IP4_INPUT] = "ip4-input-no-checksum",
389 [TUNTAP_RX_NEXT_IP6_INPUT] = "ip6-input",
390 [TUNTAP_RX_NEXT_DROP] = "error-drop",
391 [TUNTAP_RX_NEXT_ETHERNET_INPUT] = "ethernet-input",
395 /* Gets called when file descriptor is ready from epoll. */
396 static clib_error_t * tuntap_read_ready (unix_file_t * uf)
398 vlib_main_t * vm = vlib_get_main();
399 vlib_node_set_interrupt_pending (vm, tuntap_rx_node.index);
405 * Clean up the tun/tap device
408 static clib_error_t *
409 tuntap_exit (vlib_main_t * vm)
411 tuntap_main_t *tm = &tuntap_main;
416 if (! tm->dev_net_tun_fd || tm->dev_net_tun_fd < 0)
419 sfd = socket (AF_INET, SOCK_STREAM, 0);
421 clib_unix_warning("provisioning socket");
423 memset(&ifr, 0, sizeof (ifr));
424 strncpy (ifr.ifr_name, tm->tun_name, sizeof (ifr.ifr_name)-1);
426 /* get flags, modify to bring down interface... */
427 if (ioctl (sfd, SIOCGIFFLAGS, &ifr) < 0)
428 clib_unix_warning ("SIOCGIFFLAGS");
430 ifr.ifr_flags &= ~(IFF_UP | IFF_RUNNING);
432 if (ioctl (sfd, SIOCSIFFLAGS, &ifr) < 0)
433 clib_unix_warning ("SIOCSIFFLAGS");
435 /* Turn off persistence */
436 if (ioctl (tm->dev_net_tun_fd, TUNSETPERSIST, 0) < 0)
437 clib_unix_warning ("TUNSETPERSIST");
438 close(tm->dev_tap_fd);
439 if (tm->dev_net_tun_fd >= 0)
440 close(tm->dev_net_tun_fd);
447 VLIB_MAIN_LOOP_EXIT_FUNCTION (tuntap_exit);
449 static clib_error_t *
450 tuntap_config (vlib_main_t * vm, unformat_input_t * input)
452 tuntap_main_t *tm = &tuntap_main;
453 clib_error_t * error = 0;
456 int flags = IFF_TUN | IFF_NO_PI;
457 int is_enabled = 0, is_ether = 0, have_normal_interface = 0;
458 const uword buffer_size = VLIB_BUFFER_DATA_SIZE;
460 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
462 if (unformat (input, "mtu %d", &tm->mtu_bytes))
464 else if (unformat (input, "enable"))
466 else if (unformat (input, "disable"))
468 else if (unformat (input, "ethernet") ||
469 unformat (input, "ether"))
471 else if (unformat (input, "have-normal-interface") ||
472 unformat (input, "have-normal"))
473 have_normal_interface = 1;
474 else if (unformat (input, "name %s", &name))
475 tm->tun_name = (char *) name;
477 return clib_error_return (0, "unknown input `%U'",
478 format_unformat_error, input);
481 tm->dev_net_tun_fd = -1;
489 clib_warning ("tuntap disabled: must be superuser");
493 tm->is_ether = is_ether;
494 tm->have_normal_interface = have_normal_interface;
497 flags = IFF_TAP | IFF_NO_PI;
499 if ((tm->dev_net_tun_fd = open ("/dev/net/tun", O_RDWR)) < 0)
501 error = clib_error_return_unix (0, "open /dev/net/tun");
505 memset (&ifr, 0, sizeof (ifr));
506 strncpy(ifr.ifr_name, tm->tun_name, sizeof(ifr.ifr_name)-1);
507 ifr.ifr_flags = flags;
508 if (ioctl (tm->dev_net_tun_fd, TUNSETIFF, (void *)&ifr) < 0)
510 error = clib_error_return_unix (0, "ioctl TUNSETIFF");
514 /* Make it persistent, at least until we split. */
515 if (ioctl (tm->dev_net_tun_fd, TUNSETPERSIST, 1) < 0)
517 error = clib_error_return_unix (0, "TUNSETPERSIST");
521 /* Open a provisioning socket */
522 if ((tm->dev_tap_fd = socket(PF_PACKET, SOCK_RAW,
523 htons(ETH_P_ALL))) < 0 )
525 error = clib_error_return_unix (0, "socket");
529 /* Find the interface index. */
532 struct sockaddr_ll sll;
534 memset (&ifr, 0, sizeof(ifr));
535 strncpy (ifr.ifr_name, tm->tun_name, sizeof(ifr.ifr_name)-1);
536 if (ioctl (tm->dev_tap_fd, SIOCGIFINDEX, &ifr) < 0 )
538 error = clib_error_return_unix (0, "ioctl SIOCGIFINDEX");
542 /* Bind the provisioning socket to the interface. */
543 memset(&sll, 0, sizeof(sll));
544 sll.sll_family = AF_PACKET;
545 sll.sll_ifindex = ifr.ifr_ifindex;
546 sll.sll_protocol = htons(ETH_P_ALL);
548 if (bind(tm->dev_tap_fd, (struct sockaddr*) &sll, sizeof(sll)) < 0)
550 error = clib_error_return_unix (0, "bind");
555 /* non-blocking I/O on /dev/tapX */
558 if (ioctl (tm->dev_net_tun_fd, FIONBIO, &one) < 0)
560 error = clib_error_return_unix (0, "ioctl FIONBIO");
565 tm->mtu_buffers = (tm->mtu_bytes + (buffer_size - 1)) / buffer_size;
567 ifr.ifr_mtu = tm->mtu_bytes;
568 if (ioctl (tm->dev_tap_fd, SIOCSIFMTU, &ifr) < 0)
570 error = clib_error_return_unix (0, "ioctl SIOCSIFMTU");
574 /* get flags, modify to bring up interface... */
575 if (ioctl (tm->dev_tap_fd, SIOCGIFFLAGS, &ifr) < 0)
577 error = clib_error_return_unix (0, "ioctl SIOCGIFFLAGS");
581 ifr.ifr_flags |= (IFF_UP | IFF_RUNNING);
583 if (ioctl (tm->dev_tap_fd, SIOCSIFFLAGS, &ifr) < 0)
585 error = clib_error_return_unix (0, "ioctl SIOCSIFFLAGS");
591 if (ioctl (tm->dev_tap_fd, SIOCGIFHWADDR, &ifr) < 0)
593 error = clib_error_return_unix (0, "ioctl SIOCGIFHWADDR");
597 clib_memcpy (tm->ether_dst_mac, ifr.ifr_hwaddr.sa_data, 6);
600 if (have_normal_interface)
602 vnet_main_t *vnm = vnet_get_main();
603 error = ethernet_register_interface
605 tuntap_dev_class.index,
606 0 /* device instance */,
607 tm->ether_dst_mac /* ethernet address */,
609 0 /* flag change */);
611 clib_error_report (error);
612 tm->sw_if_index = tm->hw_if_index;
613 vm->os_punt_frame = tuntap_nopunt_frame;
617 vnet_main_t *vnm = vnet_get_main();
618 vnet_hw_interface_t * hi;
620 vm->os_punt_frame = tuntap_punt_frame;
622 tm->hw_if_index = vnet_register_interface
624 tuntap_dev_class.index, 0 /* device instance */,
625 tuntap_interface_class.index, 0);
626 hi = vnet_get_hw_interface (vnm, tm->hw_if_index);
627 tm->sw_if_index = hi->sw_if_index;
629 /* Interface is always up. */
630 vnet_hw_interface_set_flags (vnm, tm->hw_if_index,
631 VNET_HW_INTERFACE_FLAG_LINK_UP);
632 vnet_sw_interface_set_flags (vnm, tm->sw_if_index,
633 VNET_SW_INTERFACE_FLAG_ADMIN_UP);
637 unix_file_t template = {0};
638 template.read_function = tuntap_read_ready;
639 template.file_descriptor = tm->dev_net_tun_fd;
640 tm->unix_file_index = unix_file_add (&unix_main, &template);
646 if (tm->dev_net_tun_fd >= 0)
647 close (tm->dev_net_tun_fd);
648 if (tm->dev_tap_fd >= 0)
649 close (tm->dev_tap_fd);
655 VLIB_CONFIG_FUNCTION (tuntap_config, "tuntap");
658 tuntap_ip4_add_del_interface_address (ip4_main_t * im,
661 ip4_address_t * address,
663 u32 if_address_index,
666 tuntap_main_t * tm = &tuntap_main;
668 subif_address_t subif_addr, * ap;
671 /* Tuntap disabled, or using a "normal" interface. */
672 if (tm->have_normal_interface || tm->dev_tap_fd < 0)
675 /* See if we already know about this subif */
676 memset (&subif_addr, 0, sizeof (subif_addr));
677 subif_addr.sw_if_index = sw_if_index;
678 clib_memcpy (&subif_addr.addr, address, sizeof (*address));
680 p = mhash_get (&tm->subif_mhash, &subif_addr);
683 ap = pool_elt_at_index (tm->subifs, p[0]);
686 pool_get (tm->subifs, ap);
688 mhash_set (&tm->subif_mhash, ap, ap - tm->subifs, 0);
691 /* Use subif pool index to select alias device. */
692 memset (&ifr, 0, sizeof (ifr));
693 snprintf (ifr.ifr_name, sizeof(ifr.ifr_name),
694 "%s:%d", tm->tun_name, (int)(ap - tm->subifs));
698 struct sockaddr_in * sin;
700 sin = (struct sockaddr_in *)&ifr.ifr_addr;
702 /* Set ipv4 address, netmask. */
703 sin->sin_family = AF_INET;
704 clib_memcpy (&sin->sin_addr.s_addr, address, 4);
705 if (ioctl (tm->dev_tap_fd, SIOCSIFADDR, &ifr) < 0)
706 clib_unix_warning ("ioctl SIOCSIFADDR");
708 sin->sin_addr.s_addr = im->fib_masks[address_length];
709 if (ioctl (tm->dev_tap_fd, SIOCSIFNETMASK, &ifr) < 0)
710 clib_unix_warning ("ioctl SIOCSIFNETMASK");
714 mhash_unset (&tm->subif_mhash, &subif_addr, 0 /* old value ptr */);
715 pool_put (tm->subifs, ap);
718 /* get flags, modify to bring up interface... */
719 if (ioctl (tm->dev_tap_fd, SIOCGIFFLAGS, &ifr) < 0)
720 clib_unix_warning ("ioctl SIOCGIFFLAGS");
723 ifr.ifr_flags &= ~(IFF_UP | IFF_RUNNING);
725 ifr.ifr_flags |= (IFF_UP | IFF_RUNNING);
727 if (ioctl (tm->dev_tap_fd, SIOCSIFFLAGS, &ifr) < 0)
728 clib_unix_warning ("ioctl SIOCSIFFLAGS");
732 * $$$$ gross workaround for a known #include bug
733 * #include <linux/ipv6.h> causes multiple definitions if
734 * netinet/in.h is also included.
737 struct in6_addr ifr6_addr;
743 * Both the v6 interface address API and the way ifconfig
744 * displays subinterfaces differ from their v4 couterparts.
745 * The code given here seems to work but YMMV.
748 tuntap_ip6_add_del_interface_address (ip6_main_t * im,
751 ip6_address_t * address,
753 u32 if_address_index,
756 tuntap_main_t * tm = &tuntap_main;
758 struct in6_ifreq ifr6;
759 subif_address_t subif_addr, * ap;
762 /* Tuntap disabled, or using a "normal" interface. */
763 if (tm->have_normal_interface || tm->dev_tap_fd < 0)
766 /* See if we already know about this subif */
767 memset (&subif_addr, 0, sizeof (subif_addr));
768 subif_addr.sw_if_index = sw_if_index;
769 subif_addr.is_v6 = 1;
770 clib_memcpy (&subif_addr.addr, address, sizeof (*address));
772 p = mhash_get (&tm->subif_mhash, &subif_addr);
775 ap = pool_elt_at_index (tm->subifs, p[0]);
778 pool_get (tm->subifs, ap);
780 mhash_set (&tm->subif_mhash, ap, ap - tm->subifs, 0);
783 /* Use subif pool index to select alias device. */
784 memset (&ifr, 0, sizeof (ifr));
785 memset (&ifr6, 0, sizeof (ifr6));
786 snprintf (ifr.ifr_name, sizeof(ifr.ifr_name),
787 "%s:%d", tm->tun_name, (int)(ap - tm->subifs));
791 int sockfd = socket (AF_INET6, SOCK_STREAM, 0);
793 clib_unix_warning ("get ifindex socket");
795 if (ioctl (sockfd, SIOGIFINDEX, &ifr) < 0)
796 clib_unix_warning ("get ifindex");
798 ifr6.ifr6_ifindex = ifr.ifr_ifindex;
799 ifr6.ifr6_prefixlen = address_length;
800 clib_memcpy (&ifr6.ifr6_addr, address, 16);
802 if (ioctl (sockfd, SIOCSIFADDR, &ifr6) < 0)
803 clib_unix_warning ("set address");
810 int sockfd = socket (AF_INET6, SOCK_STREAM, 0);
812 clib_unix_warning ("get ifindex socket");
814 if (ioctl (sockfd, SIOGIFINDEX, &ifr) < 0)
815 clib_unix_warning ("get ifindex");
817 ifr6.ifr6_ifindex = ifr.ifr_ifindex;
818 ifr6.ifr6_prefixlen = address_length;
819 clib_memcpy (&ifr6.ifr6_addr, address, 16);
821 if (ioctl (sockfd, SIOCDIFADDR, &ifr6) < 0)
822 clib_unix_warning ("del address");
827 mhash_unset (&tm->subif_mhash, &subif_addr, 0 /* old value ptr */);
828 pool_put (tm->subifs, ap);
833 tuntap_punt_frame (vlib_main_t * vm,
834 vlib_node_runtime_t * node,
835 vlib_frame_t * frame)
837 tuntap_tx (vm, node, frame);
838 vlib_frame_free (vm, node, frame);
842 tuntap_nopunt_frame (vlib_main_t * vm,
843 vlib_node_runtime_t * node,
844 vlib_frame_t * frame)
846 u32 * buffers = vlib_frame_args (frame);
847 uword n_packets = frame->n_vectors;
848 vlib_buffer_free (vm, buffers, n_packets);
849 vlib_frame_free (vm, node, frame);
852 VNET_HW_INTERFACE_CLASS (tuntap_interface_class,static) = {
856 static u8 * format_tuntap_interface_name (u8 * s, va_list * args)
858 u32 i = va_arg (*args, u32);
860 s = format (s, "tuntap-%d", i);
865 tuntap_intfc_tx (vlib_main_t * vm,
866 vlib_node_runtime_t * node,
867 vlib_frame_t * frame)
869 tuntap_main_t * tm = &tuntap_main;
870 u32 * buffers = vlib_frame_args (frame);
871 uword n_buffers = frame->n_vectors;
873 /* Normal interface transmit happens only on the normal interface... */
874 if (tm->have_normal_interface)
875 return tuntap_tx (vm, node, frame);
877 vlib_buffer_free (vm, buffers, n_buffers);
881 VNET_DEVICE_CLASS (tuntap_dev_class,static) = {
883 .tx_function = tuntap_intfc_tx,
884 .format_device_name = format_tuntap_interface_name,
887 static clib_error_t *
888 tuntap_init (vlib_main_t * vm)
890 clib_error_t * error;
891 ip4_main_t * im4 = &ip4_main;
892 ip6_main_t * im6 = &ip6_main;
893 ip4_add_del_interface_address_callback_t cb4;
894 ip6_add_del_interface_address_callback_t cb6;
895 tuntap_main_t * tm = &tuntap_main;
897 error = vlib_call_init_function (vm, ip4_init);
901 mhash_init (&tm->subif_mhash, sizeof (u32), sizeof(subif_address_t));
903 cb4.function = tuntap_ip4_add_del_interface_address;
904 cb4.function_opaque = 0;
905 vec_add1 (im4->add_del_interface_address_callbacks, cb4);
907 cb6.function = tuntap_ip6_add_del_interface_address;
908 cb6.function_opaque = 0;
909 vec_add1 (im6->add_del_interface_address_callbacks, cb6);
914 VLIB_INIT_FUNCTION (tuntap_init);