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;
211 /* Make sure we have some RX buffers. */
213 uword n_left = vec_len (tm->rx_buffers);
216 if (n_left < VLIB_FRAME_SIZE / 2)
218 if (! tm->rx_buffers)
219 vec_alloc (tm->rx_buffers, VLIB_FRAME_SIZE);
221 n_alloc = vlib_buffer_alloc_from_free_list
222 (vm, tm->rx_buffers + n_left, VLIB_FRAME_SIZE - n_left,
224 _vec_len (tm->rx_buffers) = n_left + n_alloc;
228 /* Allocate RX buffers from end of rx_buffers.
229 Turn them into iovecs to pass to readv. */
231 uword i_rx = vec_len (tm->rx_buffers) - 1;
233 word i, n_bytes_left, n_bytes_in_packet;
235 /* We should have enough buffers left for an MTU sized packet. */
236 ASSERT (vec_len (tm->rx_buffers) >= tm->mtu_buffers);
238 vec_validate (tm->iovecs, tm->mtu_buffers - 1);
239 for (i = 0; i < tm->mtu_buffers; i++)
241 b = vlib_get_buffer (vm, tm->rx_buffers[i_rx - i]);
242 tm->iovecs[i].iov_base = b->data;
243 tm->iovecs[i].iov_len = buffer_size;
246 n_bytes_left = readv (tm->dev_net_tun_fd, tm->iovecs, tm->mtu_buffers);
247 n_bytes_in_packet = n_bytes_left;
248 if (n_bytes_left <= 0)
251 clib_unix_warning ("readv %d", n_bytes_left);
255 bi = tm->rx_buffers[i_rx];
260 struct rte_mbuf * mb;
262 b = vlib_get_buffer (vm, tm->rx_buffers[i_rx]);
264 mb = rte_mbuf_from_vlib_buffer(b);
268 b->current_length = n_bytes_left < buffer_size ? n_bytes_left : buffer_size;
270 n_bytes_left -= buffer_size;
272 rte_pktmbuf_data_len (mb) = b->current_length;
275 if (n_bytes_left <= 0)
278 rte_pktmbuf_pkt_len (mb) = n_bytes_in_packet;
284 b->flags |= VLIB_BUFFER_NEXT_PRESENT;
285 b->next_buffer = tm->rx_buffers[i_rx];
288 // ((struct rte_pktmbuf *)(b->mb))->next =
289 // vlib_get_buffer (vm, tm->rx_buffers[i_rx])->mb;
293 /* Interface counters for tuntap interface. */
294 vlib_increment_combined_counter
295 (vnet_main.interface_main.combined_sw_if_counters
296 + VNET_INTERFACE_COUNTER_RX,
299 1, n_bytes_in_packet);
301 _vec_len (tm->rx_buffers) = i_rx;
304 b = vlib_get_buffer (vm, bi);
308 uword n_trace = vlib_get_trace_count (vm, node);
310 vnet_buffer (b)->sw_if_index[VLIB_RX] = tm->sw_if_index;
311 vnet_buffer (b)->sw_if_index[VLIB_TX] = (u32)~0;
314 * Turn this on if you run into
315 * "bad monkey" contexts, and you want to know exactly
316 * which nodes they've visited...
318 if (VLIB_BUFFER_TRACE_TRAJECTORY)
321 b->error = node->errors[0];
325 next_index = TUNTAP_RX_NEXT_ETHERNET_INPUT;
328 switch (b->data[0] & 0xf0)
331 next_index = TUNTAP_RX_NEXT_IP4_INPUT;
334 next_index = TUNTAP_RX_NEXT_IP6_INPUT;
337 next_index = TUNTAP_RX_NEXT_DROP;
341 /* The linux kernel couldn't care less if our interface is up */
342 if (tm->have_normal_interface)
344 vnet_main_t *vnm = vnet_get_main();
345 vnet_sw_interface_t * si;
346 si = vnet_get_sw_interface (vnm, tm->sw_if_index);
347 if (!(si->flags & VNET_SW_INTERFACE_FLAG_ADMIN_UP))
348 next_index = TUNTAP_RX_NEXT_DROP;
351 vlib_set_next_frame_buffer (vm, node, next_index, bi);
355 vlib_trace_buffer (vm, node, next_index,
356 b, /* follow_chain */ 1);
357 vlib_set_trace_count (vm, node, n_trace - 1);
364 static char * tuntap_rx_error_strings[] = {
365 "unknown packet type",
368 VLIB_REGISTER_NODE (tuntap_rx_node,static) = {
369 .function = tuntap_rx,
371 .type = VLIB_NODE_TYPE_INPUT,
372 .state = VLIB_NODE_STATE_INTERRUPT,
375 .error_strings = tuntap_rx_error_strings,
377 .n_next_nodes = TUNTAP_RX_N_NEXT,
379 [TUNTAP_RX_NEXT_IP4_INPUT] = "ip4-input-no-checksum",
380 [TUNTAP_RX_NEXT_IP6_INPUT] = "ip6-input",
381 [TUNTAP_RX_NEXT_DROP] = "error-drop",
382 [TUNTAP_RX_NEXT_ETHERNET_INPUT] = "ethernet-input",
386 /* Gets called when file descriptor is ready from epoll. */
387 static clib_error_t * tuntap_read_ready (unix_file_t * uf)
389 vlib_main_t * vm = vlib_get_main();
390 vlib_node_set_interrupt_pending (vm, tuntap_rx_node.index);
396 * Clean up the tun/tap device
399 static clib_error_t *
400 tuntap_exit (vlib_main_t * vm)
402 tuntap_main_t *tm = &tuntap_main;
407 if (! tm->dev_net_tun_fd || tm->dev_net_tun_fd < 0)
410 sfd = socket (AF_INET, SOCK_STREAM, 0);
412 clib_unix_warning("provisioning socket");
414 memset(&ifr, 0, sizeof (ifr));
415 strncpy (ifr.ifr_name, tm->tun_name, sizeof (ifr.ifr_name)-1);
417 /* get flags, modify to bring down interface... */
418 if (ioctl (sfd, SIOCGIFFLAGS, &ifr) < 0)
419 clib_unix_warning ("SIOCGIFFLAGS");
421 ifr.ifr_flags &= ~(IFF_UP | IFF_RUNNING);
423 if (ioctl (sfd, SIOCSIFFLAGS, &ifr) < 0)
424 clib_unix_warning ("SIOCSIFFLAGS");
426 /* Turn off persistence */
427 if (ioctl (tm->dev_net_tun_fd, TUNSETPERSIST, 0) < 0)
428 clib_unix_warning ("TUNSETPERSIST");
429 close(tm->dev_tap_fd);
430 if (tm->dev_net_tun_fd >= 0)
431 close(tm->dev_net_tun_fd);
438 VLIB_MAIN_LOOP_EXIT_FUNCTION (tuntap_exit);
440 static clib_error_t *
441 tuntap_config (vlib_main_t * vm, unformat_input_t * input)
443 tuntap_main_t *tm = &tuntap_main;
444 clib_error_t * error = 0;
447 int flags = IFF_TUN | IFF_NO_PI;
448 int is_enabled = 0, is_ether = 0, have_normal_interface = 0;
449 const uword buffer_size = VLIB_BUFFER_DATA_SIZE;
451 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
453 if (unformat (input, "mtu %d", &tm->mtu_bytes))
455 else if (unformat (input, "enable"))
457 else if (unformat (input, "disable"))
459 else if (unformat (input, "ethernet") ||
460 unformat (input, "ether"))
462 else if (unformat (input, "have-normal-interface") ||
463 unformat (input, "have-normal"))
464 have_normal_interface = 1;
465 else if (unformat (input, "name %s", &name))
466 tm->tun_name = (char *) name;
468 return clib_error_return (0, "unknown input `%U'",
469 format_unformat_error, input);
472 tm->dev_net_tun_fd = -1;
480 clib_warning ("tuntap disabled: must be superuser");
484 tm->is_ether = is_ether;
485 tm->have_normal_interface = have_normal_interface;
488 flags = IFF_TAP | IFF_NO_PI;
490 if ((tm->dev_net_tun_fd = open ("/dev/net/tun", O_RDWR)) < 0)
492 error = clib_error_return_unix (0, "open /dev/net/tun");
496 memset (&ifr, 0, sizeof (ifr));
497 strncpy(ifr.ifr_name, tm->tun_name, sizeof(ifr.ifr_name)-1);
498 ifr.ifr_flags = flags;
499 if (ioctl (tm->dev_net_tun_fd, TUNSETIFF, (void *)&ifr) < 0)
501 error = clib_error_return_unix (0, "ioctl TUNSETIFF");
505 /* Make it persistent, at least until we split. */
506 if (ioctl (tm->dev_net_tun_fd, TUNSETPERSIST, 1) < 0)
508 error = clib_error_return_unix (0, "TUNSETPERSIST");
512 /* Open a provisioning socket */
513 if ((tm->dev_tap_fd = socket(PF_PACKET, SOCK_RAW,
514 htons(ETH_P_ALL))) < 0 )
516 error = clib_error_return_unix (0, "socket");
520 /* Find the interface index. */
523 struct sockaddr_ll sll;
525 memset (&ifr, 0, sizeof(ifr));
526 strncpy (ifr.ifr_name, tm->tun_name, sizeof(ifr.ifr_name)-1);
527 if (ioctl (tm->dev_tap_fd, SIOCGIFINDEX, &ifr) < 0 )
529 error = clib_error_return_unix (0, "ioctl SIOCGIFINDEX");
533 /* Bind the provisioning socket to the interface. */
534 memset(&sll, 0, sizeof(sll));
535 sll.sll_family = AF_PACKET;
536 sll.sll_ifindex = ifr.ifr_ifindex;
537 sll.sll_protocol = htons(ETH_P_ALL);
539 if (bind(tm->dev_tap_fd, (struct sockaddr*) &sll, sizeof(sll)) < 0)
541 error = clib_error_return_unix (0, "bind");
546 /* non-blocking I/O on /dev/tapX */
549 if (ioctl (tm->dev_net_tun_fd, FIONBIO, &one) < 0)
551 error = clib_error_return_unix (0, "ioctl FIONBIO");
556 tm->mtu_buffers = (tm->mtu_bytes + (buffer_size - 1)) / buffer_size;
558 ifr.ifr_mtu = tm->mtu_bytes;
559 if (ioctl (tm->dev_tap_fd, SIOCSIFMTU, &ifr) < 0)
561 error = clib_error_return_unix (0, "ioctl SIOCSIFMTU");
565 /* get flags, modify to bring up interface... */
566 if (ioctl (tm->dev_tap_fd, SIOCGIFFLAGS, &ifr) < 0)
568 error = clib_error_return_unix (0, "ioctl SIOCGIFFLAGS");
572 ifr.ifr_flags |= (IFF_UP | IFF_RUNNING);
574 if (ioctl (tm->dev_tap_fd, SIOCSIFFLAGS, &ifr) < 0)
576 error = clib_error_return_unix (0, "ioctl SIOCSIFFLAGS");
582 if (ioctl (tm->dev_tap_fd, SIOCGIFHWADDR, &ifr) < 0)
584 error = clib_error_return_unix (0, "ioctl SIOCGIFHWADDR");
588 clib_memcpy (tm->ether_dst_mac, ifr.ifr_hwaddr.sa_data, 6);
591 if (have_normal_interface)
593 vnet_main_t *vnm = vnet_get_main();
594 error = ethernet_register_interface
596 tuntap_dev_class.index,
597 0 /* device instance */,
598 tm->ether_dst_mac /* ethernet address */,
600 0 /* flag change */);
602 clib_error_report (error);
603 tm->sw_if_index = tm->hw_if_index;
604 vm->os_punt_frame = tuntap_nopunt_frame;
608 vnet_main_t *vnm = vnet_get_main();
609 vnet_hw_interface_t * hi;
611 vm->os_punt_frame = tuntap_punt_frame;
613 tm->hw_if_index = vnet_register_interface
615 tuntap_dev_class.index, 0 /* device instance */,
616 tuntap_interface_class.index, 0);
617 hi = vnet_get_hw_interface (vnm, tm->hw_if_index);
618 tm->sw_if_index = hi->sw_if_index;
620 /* Interface is always up. */
621 vnet_hw_interface_set_flags (vnm, tm->hw_if_index,
622 VNET_HW_INTERFACE_FLAG_LINK_UP);
623 vnet_sw_interface_set_flags (vnm, tm->sw_if_index,
624 VNET_SW_INTERFACE_FLAG_ADMIN_UP);
628 unix_file_t template = {0};
629 template.read_function = tuntap_read_ready;
630 template.file_descriptor = tm->dev_net_tun_fd;
631 tm->unix_file_index = unix_file_add (&unix_main, &template);
637 if (tm->dev_net_tun_fd >= 0)
638 close (tm->dev_net_tun_fd);
639 if (tm->dev_tap_fd >= 0)
640 close (tm->dev_tap_fd);
646 VLIB_CONFIG_FUNCTION (tuntap_config, "tuntap");
649 tuntap_ip4_add_del_interface_address (ip4_main_t * im,
652 ip4_address_t * address,
654 u32 if_address_index,
657 tuntap_main_t * tm = &tuntap_main;
659 subif_address_t subif_addr, * ap;
662 /* Tuntap disabled, or using a "normal" interface. */
663 if (tm->have_normal_interface || tm->dev_tap_fd < 0)
666 /* See if we already know about this subif */
667 memset (&subif_addr, 0, sizeof (subif_addr));
668 subif_addr.sw_if_index = sw_if_index;
669 clib_memcpy (&subif_addr.addr, address, sizeof (*address));
671 p = mhash_get (&tm->subif_mhash, &subif_addr);
674 ap = pool_elt_at_index (tm->subifs, p[0]);
677 pool_get (tm->subifs, ap);
679 mhash_set (&tm->subif_mhash, ap, ap - tm->subifs, 0);
682 /* Use subif pool index to select alias device. */
683 memset (&ifr, 0, sizeof (ifr));
684 snprintf (ifr.ifr_name, sizeof(ifr.ifr_name),
685 "%s:%d", tm->tun_name, (int)(ap - tm->subifs));
689 struct sockaddr_in * sin;
691 sin = (struct sockaddr_in *)&ifr.ifr_addr;
693 /* Set ipv4 address, netmask. */
694 sin->sin_family = AF_INET;
695 clib_memcpy (&sin->sin_addr.s_addr, address, 4);
696 if (ioctl (tm->dev_tap_fd, SIOCSIFADDR, &ifr) < 0)
697 clib_unix_warning ("ioctl SIOCSIFADDR");
699 sin->sin_addr.s_addr = im->fib_masks[address_length];
700 if (ioctl (tm->dev_tap_fd, SIOCSIFNETMASK, &ifr) < 0)
701 clib_unix_warning ("ioctl SIOCSIFNETMASK");
705 mhash_unset (&tm->subif_mhash, &subif_addr, 0 /* old value ptr */);
706 pool_put (tm->subifs, ap);
709 /* get flags, modify to bring up interface... */
710 if (ioctl (tm->dev_tap_fd, SIOCGIFFLAGS, &ifr) < 0)
711 clib_unix_warning ("ioctl SIOCGIFFLAGS");
714 ifr.ifr_flags &= ~(IFF_UP | IFF_RUNNING);
716 ifr.ifr_flags |= (IFF_UP | IFF_RUNNING);
718 if (ioctl (tm->dev_tap_fd, SIOCSIFFLAGS, &ifr) < 0)
719 clib_unix_warning ("ioctl SIOCSIFFLAGS");
723 * $$$$ gross workaround for a known #include bug
724 * #include <linux/ipv6.h> causes multiple definitions if
725 * netinet/in.h is also included.
728 struct in6_addr ifr6_addr;
734 * Both the v6 interface address API and the way ifconfig
735 * displays subinterfaces differ from their v4 couterparts.
736 * The code given here seems to work but YMMV.
739 tuntap_ip6_add_del_interface_address (ip6_main_t * im,
742 ip6_address_t * address,
744 u32 if_address_index,
747 tuntap_main_t * tm = &tuntap_main;
749 struct in6_ifreq ifr6;
750 subif_address_t subif_addr, * ap;
753 /* Tuntap disabled, or using a "normal" interface. */
754 if (tm->have_normal_interface || tm->dev_tap_fd < 0)
757 /* See if we already know about this subif */
758 memset (&subif_addr, 0, sizeof (subif_addr));
759 subif_addr.sw_if_index = sw_if_index;
760 subif_addr.is_v6 = 1;
761 clib_memcpy (&subif_addr.addr, address, sizeof (*address));
763 p = mhash_get (&tm->subif_mhash, &subif_addr);
766 ap = pool_elt_at_index (tm->subifs, p[0]);
769 pool_get (tm->subifs, ap);
771 mhash_set (&tm->subif_mhash, ap, ap - tm->subifs, 0);
774 /* Use subif pool index to select alias device. */
775 memset (&ifr, 0, sizeof (ifr));
776 memset (&ifr6, 0, sizeof (ifr6));
777 snprintf (ifr.ifr_name, sizeof(ifr.ifr_name),
778 "%s:%d", tm->tun_name, (int)(ap - tm->subifs));
782 int sockfd = socket (AF_INET6, SOCK_STREAM, 0);
784 clib_unix_warning ("get ifindex socket");
786 if (ioctl (sockfd, SIOGIFINDEX, &ifr) < 0)
787 clib_unix_warning ("get ifindex");
789 ifr6.ifr6_ifindex = ifr.ifr_ifindex;
790 ifr6.ifr6_prefixlen = address_length;
791 clib_memcpy (&ifr6.ifr6_addr, address, 16);
793 if (ioctl (sockfd, SIOCSIFADDR, &ifr6) < 0)
794 clib_unix_warning ("set address");
801 int sockfd = socket (AF_INET6, SOCK_STREAM, 0);
803 clib_unix_warning ("get ifindex socket");
805 if (ioctl (sockfd, SIOGIFINDEX, &ifr) < 0)
806 clib_unix_warning ("get ifindex");
808 ifr6.ifr6_ifindex = ifr.ifr_ifindex;
809 ifr6.ifr6_prefixlen = address_length;
810 clib_memcpy (&ifr6.ifr6_addr, address, 16);
812 if (ioctl (sockfd, SIOCDIFADDR, &ifr6) < 0)
813 clib_unix_warning ("del address");
818 mhash_unset (&tm->subif_mhash, &subif_addr, 0 /* old value ptr */);
819 pool_put (tm->subifs, ap);
824 tuntap_punt_frame (vlib_main_t * vm,
825 vlib_node_runtime_t * node,
826 vlib_frame_t * frame)
828 tuntap_tx (vm, node, frame);
829 vlib_frame_free (vm, node, frame);
833 tuntap_nopunt_frame (vlib_main_t * vm,
834 vlib_node_runtime_t * node,
835 vlib_frame_t * frame)
837 u32 * buffers = vlib_frame_args (frame);
838 uword n_packets = frame->n_vectors;
839 vlib_buffer_free (vm, buffers, n_packets);
840 vlib_frame_free (vm, node, frame);
843 VNET_HW_INTERFACE_CLASS (tuntap_interface_class,static) = {
847 static u8 * format_tuntap_interface_name (u8 * s, va_list * args)
849 u32 i = va_arg (*args, u32);
851 s = format (s, "tuntap-%d", i);
856 tuntap_intfc_tx (vlib_main_t * vm,
857 vlib_node_runtime_t * node,
858 vlib_frame_t * frame)
860 tuntap_main_t * tm = &tuntap_main;
861 u32 * buffers = vlib_frame_args (frame);
862 uword n_buffers = frame->n_vectors;
864 /* Normal interface transmit happens only on the normal interface... */
865 if (tm->have_normal_interface)
866 return tuntap_tx (vm, node, frame);
868 vlib_buffer_free (vm, buffers, n_buffers);
872 VNET_DEVICE_CLASS (tuntap_dev_class,static) = {
874 .tx_function = tuntap_intfc_tx,
875 .format_device_name = format_tuntap_interface_name,
878 static clib_error_t *
879 tuntap_init (vlib_main_t * vm)
881 clib_error_t * error;
882 ip4_main_t * im4 = &ip4_main;
883 ip6_main_t * im6 = &ip6_main;
884 ip4_add_del_interface_address_callback_t cb4;
885 ip6_add_del_interface_address_callback_t cb6;
886 tuntap_main_t * tm = &tuntap_main;
888 error = vlib_call_init_function (vm, ip4_init);
892 mhash_init (&tm->subif_mhash, sizeof (u32), sizeof(subif_address_t));
894 cb4.function = tuntap_ip4_add_del_interface_address;
895 cb4.function_opaque = 0;
896 vec_add1 (im4->add_del_interface_address_callbacks, cb4);
898 cb6.function = tuntap_ip6_add_del_interface_address;
899 cb6.function_opaque = 0;
900 vec_add1 (im6->add_del_interface_address_callbacks, cb6);
905 VLIB_INIT_FUNCTION (tuntap_init);