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);
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;
448 const uword buffer_size = VLIB_BUFFER_DATA_SIZE;
450 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
452 if (unformat (input, "mtu %d", &tm->mtu_bytes))
454 else if (unformat (input, "enable"))
456 else if (unformat (input, "disable"))
458 else if (unformat (input, "ethernet") ||
459 unformat (input, "ether"))
461 else if (unformat (input, "have-normal-interface") ||
462 unformat (input, "have-normal"))
463 have_normal_interface = 1;
464 else if (unformat (input, "name %s", &name))
465 tm->tun_name = (char *) name;
467 return clib_error_return (0, "unknown input `%U'",
468 format_unformat_error, input);
471 tm->dev_net_tun_fd = -1;
479 clib_warning ("tuntap disabled: must be superuser");
483 tm->is_ether = is_ether;
484 tm->have_normal_interface = have_normal_interface;
487 flags = IFF_TAP | IFF_NO_PI;
489 if ((tm->dev_net_tun_fd = open ("/dev/net/tun", O_RDWR)) < 0)
491 error = clib_error_return_unix (0, "open /dev/net/tun");
495 memset (&ifr, 0, sizeof (ifr));
496 strncpy(ifr.ifr_name, tm->tun_name, sizeof(ifr.ifr_name)-1);
497 ifr.ifr_flags = flags;
498 if (ioctl (tm->dev_net_tun_fd, TUNSETIFF, (void *)&ifr) < 0)
500 error = clib_error_return_unix (0, "ioctl TUNSETIFF");
504 /* Make it persistent, at least until we split. */
505 if (ioctl (tm->dev_net_tun_fd, TUNSETPERSIST, 1) < 0)
507 error = clib_error_return_unix (0, "TUNSETPERSIST");
511 /* Open a provisioning socket */
512 if ((tm->dev_tap_fd = socket(PF_PACKET, SOCK_RAW,
513 htons(ETH_P_ALL))) < 0 )
515 error = clib_error_return_unix (0, "socket");
519 /* Find the interface index. */
522 struct sockaddr_ll sll;
524 memset (&ifr, 0, sizeof(ifr));
525 strncpy (ifr.ifr_name, tm->tun_name, sizeof(ifr.ifr_name)-1);
526 if (ioctl (tm->dev_tap_fd, SIOCGIFINDEX, &ifr) < 0 )
528 error = clib_error_return_unix (0, "ioctl SIOCGIFINDEX");
532 /* Bind the provisioning socket to the interface. */
533 memset(&sll, 0, sizeof(sll));
534 sll.sll_family = AF_PACKET;
535 sll.sll_ifindex = ifr.ifr_ifindex;
536 sll.sll_protocol = htons(ETH_P_ALL);
538 if (bind(tm->dev_tap_fd, (struct sockaddr*) &sll, sizeof(sll)) < 0)
540 error = clib_error_return_unix (0, "bind");
545 /* non-blocking I/O on /dev/tapX */
548 if (ioctl (tm->dev_net_tun_fd, FIONBIO, &one) < 0)
550 error = clib_error_return_unix (0, "ioctl FIONBIO");
555 tm->mtu_buffers = (tm->mtu_bytes + (buffer_size - 1)) / buffer_size;
557 ifr.ifr_mtu = tm->mtu_bytes;
558 if (ioctl (tm->dev_tap_fd, SIOCSIFMTU, &ifr) < 0)
560 error = clib_error_return_unix (0, "ioctl SIOCSIFMTU");
564 /* get flags, modify to bring up interface... */
565 if (ioctl (tm->dev_tap_fd, SIOCGIFFLAGS, &ifr) < 0)
567 error = clib_error_return_unix (0, "ioctl SIOCGIFFLAGS");
571 ifr.ifr_flags |= (IFF_UP | IFF_RUNNING);
573 if (ioctl (tm->dev_tap_fd, SIOCSIFFLAGS, &ifr) < 0)
575 error = clib_error_return_unix (0, "ioctl SIOCSIFFLAGS");
581 if (ioctl (tm->dev_tap_fd, SIOCGIFHWADDR, &ifr) < 0)
583 error = clib_error_return_unix (0, "ioctl SIOCGIFHWADDR");
587 clib_memcpy (tm->ether_dst_mac, ifr.ifr_hwaddr.sa_data, 6);
590 if (have_normal_interface)
592 vnet_main_t *vnm = vnet_get_main();
593 error = ethernet_register_interface
595 tuntap_dev_class.index,
596 0 /* device instance */,
597 tm->ether_dst_mac /* ethernet address */,
599 0 /* flag change */);
601 clib_error_report (error);
602 tm->sw_if_index = tm->hw_if_index;
603 vm->os_punt_frame = tuntap_nopunt_frame;
607 vnet_main_t *vnm = vnet_get_main();
608 vnet_hw_interface_t * hi;
610 vm->os_punt_frame = tuntap_punt_frame;
612 tm->hw_if_index = vnet_register_interface
614 tuntap_dev_class.index, 0 /* device instance */,
615 tuntap_interface_class.index, 0);
616 hi = vnet_get_hw_interface (vnm, tm->hw_if_index);
617 tm->sw_if_index = hi->sw_if_index;
619 /* Interface is always up. */
620 vnet_hw_interface_set_flags (vnm, tm->hw_if_index,
621 VNET_HW_INTERFACE_FLAG_LINK_UP);
622 vnet_sw_interface_set_flags (vnm, tm->sw_if_index,
623 VNET_SW_INTERFACE_FLAG_ADMIN_UP);
627 unix_file_t template = {0};
628 template.read_function = tuntap_read_ready;
629 template.file_descriptor = tm->dev_net_tun_fd;
630 tm->unix_file_index = unix_file_add (&unix_main, &template);
636 if (tm->dev_net_tun_fd >= 0)
637 close (tm->dev_net_tun_fd);
638 if (tm->dev_tap_fd >= 0)
639 close (tm->dev_tap_fd);
645 VLIB_CONFIG_FUNCTION (tuntap_config, "tuntap");
648 tuntap_ip4_add_del_interface_address (ip4_main_t * im,
651 ip4_address_t * address,
653 u32 if_address_index,
656 tuntap_main_t * tm = &tuntap_main;
658 subif_address_t subif_addr, * ap;
661 /* Tuntap disabled, or using a "normal" interface. */
662 if (tm->have_normal_interface || tm->dev_tap_fd < 0)
665 /* See if we already know about this subif */
666 memset (&subif_addr, 0, sizeof (subif_addr));
667 subif_addr.sw_if_index = sw_if_index;
668 clib_memcpy (&subif_addr.addr, address, sizeof (*address));
670 p = mhash_get (&tm->subif_mhash, &subif_addr);
673 ap = pool_elt_at_index (tm->subifs, p[0]);
676 pool_get (tm->subifs, ap);
678 mhash_set (&tm->subif_mhash, ap, ap - tm->subifs, 0);
681 /* Use subif pool index to select alias device. */
682 memset (&ifr, 0, sizeof (ifr));
683 snprintf (ifr.ifr_name, sizeof(ifr.ifr_name),
684 "%s:%d", tm->tun_name, (int)(ap - tm->subifs));
688 struct sockaddr_in * sin;
690 sin = (struct sockaddr_in *)&ifr.ifr_addr;
692 /* Set ipv4 address, netmask. */
693 sin->sin_family = AF_INET;
694 clib_memcpy (&sin->sin_addr.s_addr, address, 4);
695 if (ioctl (tm->dev_tap_fd, SIOCSIFADDR, &ifr) < 0)
696 clib_unix_warning ("ioctl SIOCSIFADDR");
698 sin->sin_addr.s_addr = im->fib_masks[address_length];
699 if (ioctl (tm->dev_tap_fd, SIOCSIFNETMASK, &ifr) < 0)
700 clib_unix_warning ("ioctl SIOCSIFNETMASK");
704 mhash_unset (&tm->subif_mhash, &subif_addr, 0 /* old value ptr */);
705 pool_put (tm->subifs, ap);
708 /* get flags, modify to bring up interface... */
709 if (ioctl (tm->dev_tap_fd, SIOCGIFFLAGS, &ifr) < 0)
710 clib_unix_warning ("ioctl SIOCGIFFLAGS");
713 ifr.ifr_flags &= ~(IFF_UP | IFF_RUNNING);
715 ifr.ifr_flags |= (IFF_UP | IFF_RUNNING);
717 if (ioctl (tm->dev_tap_fd, SIOCSIFFLAGS, &ifr) < 0)
718 clib_unix_warning ("ioctl SIOCSIFFLAGS");
722 * $$$$ gross workaround for a known #include bug
723 * #include <linux/ipv6.h> causes multiple definitions if
724 * netinet/in.h is also included.
727 struct in6_addr ifr6_addr;
733 * Both the v6 interface address API and the way ifconfig
734 * displays subinterfaces differ from their v4 couterparts.
735 * The code given here seems to work but YMMV.
738 tuntap_ip6_add_del_interface_address (ip6_main_t * im,
741 ip6_address_t * address,
743 u32 if_address_index,
746 tuntap_main_t * tm = &tuntap_main;
748 struct in6_ifreq ifr6;
749 subif_address_t subif_addr, * ap;
752 /* Tuntap disabled, or using a "normal" interface. */
753 if (tm->have_normal_interface || tm->dev_tap_fd < 0)
756 /* See if we already know about this subif */
757 memset (&subif_addr, 0, sizeof (subif_addr));
758 subif_addr.sw_if_index = sw_if_index;
759 subif_addr.is_v6 = 1;
760 clib_memcpy (&subif_addr.addr, address, sizeof (*address));
762 p = mhash_get (&tm->subif_mhash, &subif_addr);
765 ap = pool_elt_at_index (tm->subifs, p[0]);
768 pool_get (tm->subifs, ap);
770 mhash_set (&tm->subif_mhash, ap, ap - tm->subifs, 0);
773 /* Use subif pool index to select alias device. */
774 memset (&ifr, 0, sizeof (ifr));
775 memset (&ifr6, 0, sizeof (ifr6));
776 snprintf (ifr.ifr_name, sizeof(ifr.ifr_name),
777 "%s:%d", tm->tun_name, (int)(ap - tm->subifs));
781 int sockfd = socket (AF_INET6, SOCK_STREAM, 0);
783 clib_unix_warning ("get ifindex socket");
785 if (ioctl (sockfd, SIOGIFINDEX, &ifr) < 0)
786 clib_unix_warning ("get ifindex");
788 ifr6.ifr6_ifindex = ifr.ifr_ifindex;
789 ifr6.ifr6_prefixlen = address_length;
790 clib_memcpy (&ifr6.ifr6_addr, address, 16);
792 if (ioctl (sockfd, SIOCSIFADDR, &ifr6) < 0)
793 clib_unix_warning ("set address");
799 int sockfd = socket (AF_INET6, SOCK_STREAM, 0);
801 clib_unix_warning ("get ifindex socket");
803 if (ioctl (sockfd, SIOGIFINDEX, &ifr) < 0)
804 clib_unix_warning ("get ifindex");
806 ifr6.ifr6_ifindex = ifr.ifr_ifindex;
807 ifr6.ifr6_prefixlen = address_length;
808 clib_memcpy (&ifr6.ifr6_addr, address, 16);
810 if (ioctl (sockfd, SIOCDIFADDR, &ifr6) < 0)
811 clib_unix_warning ("del address");
816 mhash_unset (&tm->subif_mhash, &subif_addr, 0 /* old value ptr */);
817 pool_put (tm->subifs, ap);
822 tuntap_punt_frame (vlib_main_t * vm,
823 vlib_node_runtime_t * node,
824 vlib_frame_t * frame)
826 tuntap_tx (vm, node, frame);
827 vlib_frame_free (vm, node, frame);
831 tuntap_nopunt_frame (vlib_main_t * vm,
832 vlib_node_runtime_t * node,
833 vlib_frame_t * frame)
835 u32 * buffers = vlib_frame_args (frame);
836 uword n_packets = frame->n_vectors;
837 vlib_buffer_free (vm, buffers, n_packets);
838 vlib_frame_free (vm, node, frame);
841 VNET_HW_INTERFACE_CLASS (tuntap_interface_class,static) = {
845 static u8 * format_tuntap_interface_name (u8 * s, va_list * args)
847 u32 i = va_arg (*args, u32);
849 s = format (s, "tuntap-%d", i);
854 tuntap_intfc_tx (vlib_main_t * vm,
855 vlib_node_runtime_t * node,
856 vlib_frame_t * frame)
858 tuntap_main_t * tm = &tuntap_main;
859 u32 * buffers = vlib_frame_args (frame);
860 uword n_buffers = frame->n_vectors;
862 /* Normal interface transmit happens only on the normal interface... */
863 if (tm->have_normal_interface)
864 return tuntap_tx (vm, node, frame);
866 vlib_buffer_free (vm, buffers, n_buffers);
870 VNET_DEVICE_CLASS (tuntap_dev_class,static) = {
872 .tx_function = tuntap_intfc_tx,
873 .format_device_name = format_tuntap_interface_name,
876 static clib_error_t *
877 tuntap_init (vlib_main_t * vm)
879 clib_error_t * error;
880 ip4_main_t * im4 = &ip4_main;
881 ip6_main_t * im6 = &ip6_main;
882 ip4_add_del_interface_address_callback_t cb4;
883 ip6_add_del_interface_address_callback_t cb6;
884 tuntap_main_t * tm = &tuntap_main;
886 error = vlib_call_init_function (vm, ip4_init);
890 mhash_init (&tm->subif_mhash, sizeof (u32), sizeof(subif_address_t));
892 cb4.function = tuntap_ip4_add_del_interface_address;
893 cb4.function_opaque = 0;
894 vec_add1 (im4->add_del_interface_address_callbacks, cb4);
896 cb6.function = tuntap_ip6_add_del_interface_address;
897 cb6.function_opaque = 0;
898 vec_add1 (im6->add_del_interface_address_callbacks, cb6);
903 VLIB_INIT_FUNCTION (tuntap_init);