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 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;
204 const uword buffer_size = VLIB_BUFFER_DEFAULT_FREE_LIST_BYTES;
205 u32 free_list_index = VLIB_BUFFER_DEFAULT_FREE_LIST_INDEX;
207 dpdk_main_t * dm = &dpdk_main;
208 const uword buffer_size = MBUF_SIZE;
209 u32 free_list_index = dm->vlib_buffer_free_list_index;
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 = (((struct rte_mbuf *)b)-1);
269 b->current_length = n_bytes_left < buffer_size ? n_bytes_left : buffer_size;
271 n_bytes_left -= buffer_size;
273 rte_pktmbuf_data_len (mb) = b->current_length;
276 if (n_bytes_left <= 0)
279 rte_pktmbuf_pkt_len (mb) = n_bytes_in_packet;
285 b->flags |= VLIB_BUFFER_NEXT_PRESENT;
286 b->next_buffer = tm->rx_buffers[i_rx];
289 // ((struct rte_pktmbuf *)(b->mb))->next =
290 // vlib_get_buffer (vm, tm->rx_buffers[i_rx])->mb;
294 /* Interface counters for tuntap interface. */
295 vlib_increment_combined_counter
296 (vnet_main.interface_main.combined_sw_if_counters
297 + VNET_INTERFACE_COUNTER_RX,
300 1, n_bytes_in_packet);
302 _vec_len (tm->rx_buffers) = i_rx;
305 b = vlib_get_buffer (vm, bi);
309 uword n_trace = vlib_get_trace_count (vm, node);
311 vnet_buffer (b)->sw_if_index[VLIB_RX] = tm->sw_if_index;
312 vnet_buffer (b)->sw_if_index[VLIB_TX] = (u32)~0;
315 * Turn this on if you run into
316 * "bad monkey" contexts, and you want to know exactly
317 * which nodes they've visited...
319 if (VLIB_BUFFER_TRACE_TRAJECTORY)
322 b->error = node->errors[0];
326 next_index = TUNTAP_RX_NEXT_ETHERNET_INPUT;
329 switch (b->data[0] & 0xf0)
332 next_index = TUNTAP_RX_NEXT_IP4_INPUT;
335 next_index = TUNTAP_RX_NEXT_IP6_INPUT;
338 next_index = TUNTAP_RX_NEXT_DROP;
342 /* The linux kernel couldn't care less if our interface is up */
343 if (tm->have_normal_interface)
345 vnet_main_t *vnm = vnet_get_main();
346 vnet_sw_interface_t * si;
347 si = vnet_get_sw_interface (vnm, tm->sw_if_index);
348 if (!(si->flags & VNET_SW_INTERFACE_FLAG_ADMIN_UP))
349 next_index = TUNTAP_RX_NEXT_DROP;
352 vlib_set_next_frame_buffer (vm, node, next_index, bi);
356 vlib_trace_buffer (vm, node, next_index,
357 b, /* follow_chain */ 1);
358 vlib_set_trace_count (vm, node, n_trace - 1);
365 static char * tuntap_rx_error_strings[] = {
366 "unknown packet type",
369 VLIB_REGISTER_NODE (tuntap_rx_node,static) = {
370 .function = tuntap_rx,
372 .type = VLIB_NODE_TYPE_INPUT,
373 .state = VLIB_NODE_STATE_INTERRUPT,
376 .error_strings = tuntap_rx_error_strings,
378 .n_next_nodes = TUNTAP_RX_N_NEXT,
380 [TUNTAP_RX_NEXT_IP4_INPUT] = "ip4-input-no-checksum",
381 [TUNTAP_RX_NEXT_IP6_INPUT] = "ip6-input",
382 [TUNTAP_RX_NEXT_DROP] = "error-drop",
383 [TUNTAP_RX_NEXT_ETHERNET_INPUT] = "ethernet-input",
387 /* Gets called when file descriptor is ready from epoll. */
388 static clib_error_t * tuntap_read_ready (unix_file_t * uf)
390 vlib_main_t * vm = vlib_get_main();
391 vlib_node_set_interrupt_pending (vm, tuntap_rx_node.index);
397 * Clean up the tun/tap device
400 static clib_error_t *
401 tuntap_exit (vlib_main_t * vm)
403 tuntap_main_t *tm = &tuntap_main;
408 if (! tm->dev_net_tun_fd || tm->dev_net_tun_fd < 0)
411 sfd = socket (AF_INET, SOCK_STREAM, 0);
413 clib_unix_warning("provisioning socket");
415 memset(&ifr, 0, sizeof (ifr));
416 strncpy (ifr.ifr_name, tm->tun_name, sizeof (ifr.ifr_name)-1);
418 /* get flags, modify to bring down interface... */
419 if (ioctl (sfd, SIOCGIFFLAGS, &ifr) < 0)
420 clib_unix_warning ("SIOCGIFFLAGS");
422 ifr.ifr_flags &= ~(IFF_UP | IFF_RUNNING);
424 if (ioctl (sfd, SIOCSIFFLAGS, &ifr) < 0)
425 clib_unix_warning ("SIOCSIFFLAGS");
427 /* Turn off persistence */
428 if (ioctl (tm->dev_net_tun_fd, TUNSETPERSIST, 0) < 0)
429 clib_unix_warning ("TUNSETPERSIST");
430 close(tm->dev_tap_fd);
431 close(tm->dev_net_tun_fd);
437 VLIB_MAIN_LOOP_EXIT_FUNCTION (tuntap_exit);
439 static clib_error_t *
440 tuntap_config (vlib_main_t * vm, unformat_input_t * input)
442 tuntap_main_t *tm = &tuntap_main;
443 clib_error_t * error = 0;
446 int flags = IFF_TUN | IFF_NO_PI;
447 int is_enabled = 0, is_ether = 0, have_normal_interface = 0;
449 const uword buffer_size = VLIB_BUFFER_DEFAULT_FREE_LIST_BYTES;
451 const uword buffer_size = MBUF_SIZE;
455 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
457 if (unformat (input, "mtu %d", &tm->mtu_bytes))
459 else if (unformat (input, "enable"))
461 else if (unformat (input, "disable"))
463 else if (unformat (input, "ethernet") ||
464 unformat (input, "ether"))
466 else if (unformat (input, "have-normal-interface") ||
467 unformat (input, "have-normal"))
468 have_normal_interface = 1;
469 else if (unformat (input, "name %s", &name))
470 tm->tun_name = (char *) name;
472 return clib_error_return (0, "unknown input `%U'",
473 format_unformat_error, input);
476 tm->dev_net_tun_fd = -1;
484 clib_warning ("tuntap disabled: must be superuser");
488 tm->is_ether = is_ether;
489 tm->have_normal_interface = have_normal_interface;
492 flags = IFF_TAP | IFF_NO_PI;
494 if ((tm->dev_net_tun_fd = open ("/dev/net/tun", O_RDWR)) < 0)
496 error = clib_error_return_unix (0, "open /dev/net/tun");
500 memset (&ifr, 0, sizeof (ifr));
501 strncpy(ifr.ifr_name, tm->tun_name, sizeof(ifr.ifr_name)-1);
502 ifr.ifr_flags = flags;
503 if (ioctl (tm->dev_net_tun_fd, TUNSETIFF, (void *)&ifr) < 0)
505 error = clib_error_return_unix (0, "ioctl TUNSETIFF");
509 /* Make it persistent, at least until we split. */
510 if (ioctl (tm->dev_net_tun_fd, TUNSETPERSIST, 1) < 0)
512 error = clib_error_return_unix (0, "TUNSETPERSIST");
516 /* Open a provisioning socket */
517 if ((tm->dev_tap_fd = socket(PF_PACKET, SOCK_RAW,
518 htons(ETH_P_ALL))) < 0 )
520 error = clib_error_return_unix (0, "socket");
524 /* Find the interface index. */
527 struct sockaddr_ll sll;
529 memset (&ifr, 0, sizeof(ifr));
530 strncpy (ifr.ifr_name, tm->tun_name, sizeof(ifr.ifr_name)-1);
531 if (ioctl (tm->dev_tap_fd, SIOCGIFINDEX, &ifr) < 0 )
533 error = clib_error_return_unix (0, "ioctl SIOCGIFINDEX");
537 /* Bind the provisioning socket to the interface. */
538 memset(&sll, 0, sizeof(sll));
539 sll.sll_family = AF_PACKET;
540 sll.sll_ifindex = ifr.ifr_ifindex;
541 sll.sll_protocol = htons(ETH_P_ALL);
543 if (bind(tm->dev_tap_fd, (struct sockaddr*) &sll, sizeof(sll)) < 0)
545 error = clib_error_return_unix (0, "bind");
550 /* non-blocking I/O on /dev/tapX */
553 if (ioctl (tm->dev_net_tun_fd, FIONBIO, &one) < 0)
555 error = clib_error_return_unix (0, "ioctl FIONBIO");
560 tm->mtu_buffers = (tm->mtu_bytes + (buffer_size - 1)) / buffer_size;
562 ifr.ifr_mtu = tm->mtu_bytes;
563 if (ioctl (tm->dev_tap_fd, SIOCSIFMTU, &ifr) < 0)
565 error = clib_error_return_unix (0, "ioctl SIOCSIFMTU");
569 /* get flags, modify to bring up interface... */
570 if (ioctl (tm->dev_tap_fd, SIOCGIFFLAGS, &ifr) < 0)
572 error = clib_error_return_unix (0, "ioctl SIOCGIFFLAGS");
576 ifr.ifr_flags |= (IFF_UP | IFF_RUNNING);
578 if (ioctl (tm->dev_tap_fd, SIOCSIFFLAGS, &ifr) < 0)
580 error = clib_error_return_unix (0, "ioctl SIOCSIFFLAGS");
586 if (ioctl (tm->dev_tap_fd, SIOCGIFHWADDR, &ifr) < 0)
588 error = clib_error_return_unix (0, "ioctl SIOCGIFHWADDR");
592 memcpy (tm->ether_dst_mac, ifr.ifr_hwaddr.sa_data, 6);
595 if (have_normal_interface)
597 vnet_main_t *vnm = vnet_get_main();
598 error = ethernet_register_interface
600 tuntap_dev_class.index,
601 0 /* device instance */,
602 tm->ether_dst_mac /* ethernet address */,
604 0 /* flag change */);
606 clib_error_report (error);
607 tm->sw_if_index = tm->hw_if_index;
608 vm->os_punt_frame = tuntap_nopunt_frame;
612 vnet_main_t *vnm = vnet_get_main();
613 vnet_hw_interface_t * hi;
615 vm->os_punt_frame = tuntap_punt_frame;
617 tm->hw_if_index = vnet_register_interface
619 tuntap_dev_class.index, 0 /* device instance */,
620 tuntap_interface_class.index, 0);
621 hi = vnet_get_hw_interface (vnm, tm->hw_if_index);
622 tm->sw_if_index = hi->sw_if_index;
624 /* Interface is always up. */
625 vnet_hw_interface_set_flags (vnm, tm->hw_if_index,
626 VNET_HW_INTERFACE_FLAG_LINK_UP);
627 vnet_sw_interface_set_flags (vnm, tm->sw_if_index,
628 VNET_SW_INTERFACE_FLAG_ADMIN_UP);
632 unix_file_t template = {0};
633 template.read_function = tuntap_read_ready;
634 template.file_descriptor = tm->dev_net_tun_fd;
635 tm->unix_file_index = unix_file_add (&unix_main, &template);
641 if (tm->dev_net_tun_fd >= 0)
642 close (tm->dev_net_tun_fd);
643 if (tm->dev_tap_fd >= 0)
644 close (tm->dev_tap_fd);
650 VLIB_CONFIG_FUNCTION (tuntap_config, "tuntap");
653 tuntap_ip4_add_del_interface_address (ip4_main_t * im,
656 ip4_address_t * address,
658 u32 if_address_index,
661 tuntap_main_t * tm = &tuntap_main;
663 subif_address_t subif_addr, * ap;
666 /* Tuntap disabled, or using a "normal" interface. */
667 if (tm->have_normal_interface || tm->dev_tap_fd < 0)
670 /* See if we already know about this subif */
671 memset (&subif_addr, 0, sizeof (subif_addr));
672 subif_addr.sw_if_index = sw_if_index;
673 memcpy (&subif_addr.addr, address, sizeof (*address));
675 p = mhash_get (&tm->subif_mhash, &subif_addr);
678 ap = pool_elt_at_index (tm->subifs, p[0]);
681 pool_get (tm->subifs, ap);
683 mhash_set (&tm->subif_mhash, ap, ap - tm->subifs, 0);
686 /* Use subif pool index to select alias device. */
687 memset (&ifr, 0, sizeof (ifr));
688 snprintf (ifr.ifr_name, sizeof(ifr.ifr_name),
689 "%s:%d", tm->tun_name, (int)(ap - tm->subifs));
693 struct sockaddr_in * sin;
695 sin = (struct sockaddr_in *)&ifr.ifr_addr;
697 /* Set ipv4 address, netmask. */
698 sin->sin_family = AF_INET;
699 memcpy (&sin->sin_addr.s_addr, address, 4);
700 if (ioctl (tm->dev_tap_fd, SIOCSIFADDR, &ifr) < 0)
701 clib_unix_warning ("ioctl SIOCSIFADDR");
703 sin->sin_addr.s_addr = im->fib_masks[address_length];
704 if (ioctl (tm->dev_tap_fd, SIOCSIFNETMASK, &ifr) < 0)
705 clib_unix_warning ("ioctl SIOCSIFNETMASK");
709 mhash_unset (&tm->subif_mhash, &subif_addr, 0 /* old value ptr */);
710 pool_put (tm->subifs, ap);
713 /* get flags, modify to bring up interface... */
714 if (ioctl (tm->dev_tap_fd, SIOCGIFFLAGS, &ifr) < 0)
715 clib_unix_warning ("ioctl SIOCGIFFLAGS");
718 ifr.ifr_flags &= ~(IFF_UP | IFF_RUNNING);
720 ifr.ifr_flags |= (IFF_UP | IFF_RUNNING);
722 if (ioctl (tm->dev_tap_fd, SIOCSIFFLAGS, &ifr) < 0)
723 clib_unix_warning ("ioctl SIOCSIFFLAGS");
727 * $$$$ gross workaround for a known #include bug
728 * #include <linux/ipv6.h> causes multiple definitions if
729 * netinet/in.h is also included.
732 struct in6_addr ifr6_addr;
738 * Both the v6 interface address API and the way ifconfig
739 * displays subinterfaces differ from their v4 couterparts.
740 * The code given here seems to work but YMMV.
743 tuntap_ip6_add_del_interface_address (ip6_main_t * im,
746 ip6_address_t * address,
748 u32 if_address_index,
751 tuntap_main_t * tm = &tuntap_main;
753 struct in6_ifreq ifr6;
754 subif_address_t subif_addr, * ap;
757 /* Tuntap disabled, or using a "normal" interface. */
758 if (tm->have_normal_interface || tm->dev_tap_fd < 0)
761 /* See if we already know about this subif */
762 memset (&subif_addr, 0, sizeof (subif_addr));
763 subif_addr.sw_if_index = sw_if_index;
764 subif_addr.is_v6 = 1;
765 memcpy (&subif_addr.addr, address, sizeof (*address));
767 p = mhash_get (&tm->subif_mhash, &subif_addr);
770 ap = pool_elt_at_index (tm->subifs, p[0]);
773 pool_get (tm->subifs, ap);
775 mhash_set (&tm->subif_mhash, ap, ap - tm->subifs, 0);
778 /* Use subif pool index to select alias device. */
779 memset (&ifr, 0, sizeof (ifr));
780 memset (&ifr6, 0, sizeof (ifr6));
781 snprintf (ifr.ifr_name, sizeof(ifr.ifr_name),
782 "%s:%d", tm->tun_name, (int)(ap - tm->subifs));
786 int sockfd = socket (AF_INET6, SOCK_STREAM, 0);
788 clib_unix_warning ("get ifindex socket");
790 if (ioctl (sockfd, SIOGIFINDEX, &ifr) < 0)
791 clib_unix_warning ("get ifindex");
793 ifr6.ifr6_ifindex = ifr.ifr_ifindex;
794 ifr6.ifr6_prefixlen = address_length;
795 memcpy (&ifr6.ifr6_addr, address, 16);
797 if (ioctl (sockfd, SIOCSIFADDR, &ifr6) < 0)
798 clib_unix_warning ("set address");
804 int sockfd = socket (AF_INET6, SOCK_STREAM, 0);
806 clib_unix_warning ("get ifindex socket");
808 if (ioctl (sockfd, SIOGIFINDEX, &ifr) < 0)
809 clib_unix_warning ("get ifindex");
811 ifr6.ifr6_ifindex = ifr.ifr_ifindex;
812 ifr6.ifr6_prefixlen = address_length;
813 memcpy (&ifr6.ifr6_addr, address, 16);
815 if (ioctl (sockfd, SIOCDIFADDR, &ifr6) < 0)
816 clib_unix_warning ("del address");
820 mhash_unset (&tm->subif_mhash, &subif_addr, 0 /* old value ptr */);
821 pool_put (tm->subifs, ap);
826 tuntap_punt_frame (vlib_main_t * vm,
827 vlib_node_runtime_t * node,
828 vlib_frame_t * frame)
830 tuntap_tx (vm, node, frame);
831 vlib_frame_free (vm, node, frame);
835 tuntap_nopunt_frame (vlib_main_t * vm,
836 vlib_node_runtime_t * node,
837 vlib_frame_t * frame)
839 u32 * buffers = vlib_frame_args (frame);
840 uword n_packets = frame->n_vectors;
841 vlib_buffer_free (vm, buffers, n_packets);
842 vlib_frame_free (vm, node, frame);
845 VNET_HW_INTERFACE_CLASS (tuntap_interface_class,static) = {
849 static u8 * format_tuntap_interface_name (u8 * s, va_list * args)
851 u32 i = va_arg (*args, u32);
853 s = format (s, "tuntap-%d", i);
858 tuntap_intfc_tx (vlib_main_t * vm,
859 vlib_node_runtime_t * node,
860 vlib_frame_t * frame)
862 tuntap_main_t * tm = &tuntap_main;
863 u32 * buffers = vlib_frame_args (frame);
864 uword n_buffers = frame->n_vectors;
866 /* Normal interface transmit happens only on the normal interface... */
867 if (tm->have_normal_interface)
868 return tuntap_tx (vm, node, frame);
870 vlib_buffer_free (vm, buffers, n_buffers);
874 VNET_DEVICE_CLASS (tuntap_dev_class,static) = {
876 .tx_function = tuntap_intfc_tx,
877 .format_device_name = format_tuntap_interface_name,
880 static clib_error_t *
881 tuntap_init (vlib_main_t * vm)
883 clib_error_t * error;
884 ip4_main_t * im4 = &ip4_main;
885 ip6_main_t * im6 = &ip6_main;
886 ip4_add_del_interface_address_callback_t cb4;
887 ip6_add_del_interface_address_callback_t cb6;
888 tuntap_main_t * tm = &tuntap_main;
890 error = vlib_call_init_function (vm, ip4_init);
894 mhash_init (&tm->subif_mhash, sizeof (u32), sizeof(subif_address_t));
896 cb4.function = tuntap_ip4_add_del_interface_address;
897 cb4.function_opaque = 0;
898 vec_add1 (im4->add_del_interface_address_callbacks, cb4);
900 cb6.function = tuntap_ip6_add_del_interface_address;
901 cb6.function_opaque = 0;
902 vec_add1 (im6->add_del_interface_address_callbacks, cb6);
907 VLIB_INIT_FUNCTION (tuntap_init);