/* *------------------------------------------------------------------ * tuntap.c - kernel stack (reverse) punt/inject path * * Copyright (c) 2009 Cisco and/or its affiliates. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. *------------------------------------------------------------------ */ /** * @file * @brief TunTap Kernel stack (reverse) punt/inject path. * * This driver runs in one of two distinct modes: * - "punt/inject" mode, where we send pkts not otherwise processed * by the forwarding to the Linux kernel stack, and * * - "normal interface" mode, where we treat the Linux kernel stack * as a peer. * * By default, we select punt/inject mode. */ #include /* for open */ #include #include #include #include #include /* for iovec */ #include #include #include #include #include #include #include #include #include #include static vnet_device_class_t tuntap_dev_class; static vnet_hw_interface_class_t tuntap_interface_class; static void tuntap_punt_frame (vlib_main_t * vm, vlib_node_runtime_t * node, vlib_frame_t * frame); static void tuntap_nopunt_frame (vlib_main_t * vm, vlib_node_runtime_t * node, vlib_frame_t * frame); typedef struct { u32 sw_if_index; u8 is_v6; u8 addr[16]; } subif_address_t; /** * @brief TUNTAP per thread struct */ typedef struct { /** Vector of VLIB rx buffers to use. We allocate them in blocks of VLIB_FRAME_SIZE (256). */ u32 *rx_buffers; /** Vector of iovecs for readv/writev calls. */ struct iovec *iovecs; } tuntap_per_thread_t; /** * @brief TUNTAP node main state */ typedef struct { /** per thread variables */ tuntap_per_thread_t *threads; /** File descriptors for /dev/net/tun and provisioning socket. */ int dev_net_tun_fd, dev_tap_fd; /** Create a "tap" [ethernet] encaps device */ int is_ether; /** 1 if a "normal" routed intfc, 0 if a punt/inject interface */ int have_normal_interface; /** tap device destination MAC address. Required, or Linux drops pkts */ u8 ether_dst_mac[6]; /** Interface MTU in bytes and # of default sized buffers. */ u32 mtu_bytes, mtu_buffers; /** Linux interface name for tun device. */ char *tun_name; /** Pool of subinterface addresses */ subif_address_t *subifs; /** Hash for subif addresses */ mhash_t subif_mhash; /** Unix file index */ u32 clib_file_index; /** For the "normal" interface, if configured */ u32 hw_if_index, sw_if_index; } tuntap_main_t; static tuntap_main_t tuntap_main = { .tun_name = "vnet", /** Suitable defaults for an Ethernet-like tun/tap device */ .mtu_bytes = 4096 + 256, }; /** * @brief tuntap_tx * @node tuntap-tx * * Output node, writes the buffers comprising the incoming frame * to the tun/tap device, aka hands them to the Linux kernel stack. * * @param *vm - vlib_main_t * @param *node - vlib_node_runtime_t * @param *frame - vlib_frame_t * * @return rc - uword * */ static uword tuntap_tx (vlib_main_t * vm, vlib_node_runtime_t * node, vlib_frame_t * frame) { u32 *buffers = vlib_frame_args (frame); uword n_packets = frame->n_vectors; tuntap_main_t *tm = &tuntap_main; vnet_main_t *vnm = vnet_get_main (); vnet_interface_main_t *im = &vnm->interface_main; u32 n_bytes = 0; int i; u16 thread_index = vm->thread_index; for (i = 0; i < n_packets; i++) { struct iovec *iov; vlib_buffer_t *b; uword l; b = vlib_get_buffer (vm, buffers[i]); if (tm->is_ether && (!tm->have_normal_interface)) { vlib_buffer_reset (b); clib_memcpy (vlib_buffer_get_current (b), tm->ether_dst_mac, 6); } /* Re-set iovecs if present. */ if (tm->threads[thread_index].iovecs) _vec_len (tm->threads[thread_index].iovecs) = 0; /** VLIB buffer chain -> Unix iovec(s). */ vec_add2 (tm->threads[thread_index].iovecs, iov, 1); iov->iov_base = b->data + b->current_data; iov->iov_len = l = b->current_length; if (PREDICT_FALSE (b->flags & VLIB_BUFFER_NEXT_PRESENT)) { do { b = vlib_get_buffer (vm, b->next_buffer); vec_add2 (tm->threads[thread_index].iovecs, iov, 1); iov->iov_base = b->data + b->current_data; iov->iov_len = b->current_length; l += b->current_length; } while (b->flags & VLIB_BUFFER_NEXT_PRESENT); } if (writev (tm->dev_net_tun_fd, tm->threads[thread_index].iovecs, vec_len (tm->threads[thread_index].iovecs)) < l) clib_unix_warning ("writev"); n_bytes += l; } /* Update tuntap interface output stats. */ vlib_increment_combined_counter (im->combined_sw_if_counters + VNET_INTERFACE_COUNTER_TX, vm->thread_index, tm->sw_if_index, n_packets, n_bytes); /** The normal interface path flattens the buffer chain */ if (tm->have_normal_interface) vlib_buffer_free_no_next (vm, buffers, n_packets); else vlib_buffer_free (vm, buffers, n_packets); return n_packets; } /* *INDENT-OFF* */ VLIB_REGISTER_NODE (tuntap_tx_node,static) = { .function = tuntap_tx, .name = "tuntap-tx", .type = VLIB_NODE_TYPE_INTERNAL, .vector_size = 4, }; /* *INDENT-ON* */ /** * @brief TUNTAP receive node * @node tuntap-rx * * @param *vm - vlib_main_t * @param *node - vlib_node_runtime_t * @param *frame - vlib_frame_t * * @return rc - uword * */ static uword tuntap_rx (vlib_main_t * vm, vlib_node_runtime_t * node, vlib_frame_t * frame) { tuntap_main_t *tm = &tuntap_main; vlib_buffer_t *b; u32 bi; const uword buffer_size = VLIB_BUFFER_DATA_SIZE; u16 thread_index = vm->thread_index; /** Make sure we have some RX buffers. */ { uword n_left = vec_len (tm->threads[thread_index].rx_buffers); uword n_alloc; if (n_left < VLIB_FRAME_SIZE / 2) { if (!tm->threads[thread_index].rx_buffers) vec_alloc (tm->threads[thread_index].rx_buffers, VLIB_FRAME_SIZE); n_alloc = vlib_buffer_alloc (vm, tm->threads[thread_index].rx_buffers + n_left, VLIB_FRAME_SIZE - n_left); _vec_len (tm->threads[thread_index].rx_buffers) = n_left + n_alloc; } } /** Allocate RX buffers from end of rx_buffers. Turn them into iovecs to pass to readv. */ { uword i_rx = vec_len (tm->threads[thread_index].rx_buffers) - 1; vlib_buffer_t *b; word i, n_bytes_left, n_bytes_in_packet; /** We should have enough buffers left for an MTU sized packet. */ ASSERT (vec_len (tm->threads[thread_index].rx_buffers) >= tm->mtu_buffers); vec_validate (tm->threads[thread_index].iovecs, tm->mtu_buffers - 1); for (i = 0; i < tm->mtu_buffers; i++) { b = vlib_get_buffer (vm, tm->threads[thread_index].rx_buffers[i_rx - i]); tm->threads[thread_index].iovecs[i].iov_base = b->data; tm->threads[thread_index].iovecs[i].iov_len = buffer_size; } n_bytes_left = readv (tm->dev_net_tun_fd, tm->threads[thread_index].iovecs, tm->mtu_buffers); n_bytes_in_packet = n_bytes_left; if (n_bytes_left <= 0) { if (errno != EAGAIN) clib_unix_warning ("readv %d", n_bytes_left); return 0; } bi = tm->threads[thread_index].rx_buffers[i_rx]; while (1) { b = vlib_get_buffer (vm, tm->threads[thread_index].rx_buffers[i_rx]); b->flags = 0; b->current_data = 0; b->current_length = n_bytes_left < buffer_size ? n_bytes_left : buffer_size; n_bytes_left -= buffer_size; if (n_bytes_left <= 0) { break; } i_rx--; b->flags |= VLIB_BUFFER_NEXT_PRESENT; b->next_buffe
/*
 * Copyright (c) 2015 Cisco and/or its affiliates.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at:
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <vnet/ipsec/ipsec.h>
#include <vnet/ipsec/ipsec_io.h>

int
ipsec_add_del_spd (vlib_main_t * vm, u32 spd_id, int is_add)
{
  ipsec_main_t *im = &ipsec_main;
  ipsec_spd_t *spd = 0;
  uword *p;
  u32 spd_index, k, v;

  p = hash_get (im->spd_index_by_spd_id, spd_id);
  if (p && is_add)
    return VNET_API_ERROR_ENTRY_ALREADY_EXISTS;
  if (!p && !is_add)
    return VNET_API_ERROR_NO_SUCH_ENTRY;

  if (!is_add)			/* delete */
    {
      spd_index = p[0];
      spd = pool_elt_at_index (im->spds, spd_index);
      if (!spd)
	return VNET_API_ERROR_INVALID_VALUE;
      /* *INDENT-OFF* */
      hash_foreach (k, v, im->spd_index_by_sw_if_index, ({
        if (v == spd_index)
          ipsec_set_interface_spd(vm, k, spd_id, 0);
      }));
      /* *INDENT-ON* */
      hash_unset (im->spd_index_by_spd_id, spd_id);
#define _(s,v) vec_free(spd->policies[IPSEC_SPD_POLICY_##s]);
      foreach_ipsec_spd_policy_type
#undef _
	pool_put (im->spds, spd);
    }
  else				/* create new SPD */
    {
      pool_get (im->spds, spd);
      clib_memset (spd, 0, sizeof (*spd));
      spd_index = spd - im->spds;
      spd->id = spd_id;
      hash_set (im->spd_index_by_spd_id, spd_id, spd_index);
    }
  return 0;
}

int
ipsec_set_interface_spd (vlib_main_t * vm, u32 sw_if_index, u32 spd_id,
			 int is_add)
{
  ipsec_main_t *im = &ipsec_main;
  ip4_ipsec_config_t config;

  u32 spd_index;
  uword *p;

  p = hash_get (im->spd_index_by_spd_id, spd_id);
  if (!p)
    return VNET_API_ERROR_SYSCALL_ERROR_1;	/* no such spd-id */

  spd_index = p[0];

  p = hash_get (im->spd_index_by_sw_if_index, sw_if_index);
  if (p && is_add)
    return VNET_API_ERROR_SYSCALL_ERROR_2;	/* spd already assigned */

  if (is_add)
    {
      hash_set (im->spd_index_by_sw_if_index, sw_if_index, spd_index);
    }
  else
    {
      hash_unset (im->spd_index_by_sw_if_index, sw_if_index</