/* * 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. */ /* * cli.c: Unix stdin/socket CLI. * * Copyright (c) 2008 Eliot Dresselhaus * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /** * @file * @brief Unix stdin/socket command line interface. * Provides a command line interface so humans can interact with VPP. * This is predominantly a debugging and testing mechanism. */ /*? %%clicmd:group_label Command line session %% ?*/ /*? %%syscfg:group_label Command line session %% ?*/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include /** ANSI escape code. */ #define ESC "\x1b" /** ANSI Control Sequence Introducer. */ #define CSI ESC "[" /** ANSI clear screen. */ #define ANSI_CLEAR CSI "2J" CSI "1;1H" /** ANSI reset color settings. */ #define ANSI_RESET CSI "0m" /** ANSI Start bold text. */ #define ANSI_BOLD CSI "1m" /** ANSI Stop bold text. */ #define ANSI_DIM CSI "2m" /** ANSI Start dark red text. */ #define ANSI_DRED ANSI_DIM CSI "31m" /** ANSI Start bright red text. */ #define ANSI_BRED ANSI_BOLD CSI "31m" /** ANSI clear line cursor is on. */ #define ANSI_CLEARLINE CSI "2K" /** ANSI scroll screen down one line. */ #define ANSI_SCROLLDN CSI "1T" /** ANSI save cursor position. */ #define ANSI_SAVECURSOR CSI "s" /** ANSI restore cursor position if previously saved. */ #define ANSI_RESTCURSOR CSI "u" /** Maximum depth into a byte stream from which to compile a Telnet * protocol message. This is a safety measure. */ #define UNIX_CLI_MAX_DEPTH_TELNET 24 /** Maximum terminal width we will accept */ #define UNIX_CLI_MAX_TERMINAL_WIDTH 512 /** Maximum terminal height we will accept */ #define UNIX_CLI_MAX_TERMINAL_HEIGHT 512 /** Default terminal height */ #define UNIX_CLI_DEFAULT_TERMINAL_HEIGHT 24 /** Default terminal width */ #define UNIX_CLI_DEFAULT_TERMINAL_WIDTH 80 /** A CLI banner line. */ typedef struct { u8 *line; /**< The line to print. */ u32 length; /**< The length of the line without terminating NUL. */ } unix_cli_banner_t; #define _(a) { .line = (u8 *)(a), .length = sizeof(a) - 1 } /** Plain welcome banner. */ static unix_cli_banner_t unix_cli_banner[] = { _(" _______ _ _ _____ ___ \n"), _(" __/ __/ _ \\ (_)__ | | / / _ \\/ _ \\\n"), _(" _/ _// // / / / _ \\ | |/ / ___/ ___/\n"), _(" /_/ /____(_)_/\\___/ |___/_/ /_/ \n"), _("\n") }; /** ANSI color welcome banner. */ static unix_cli_banner_t unix_cli_banner_color[] = { _(ANSI_BRED " _______ _ " ANSI_RESET " _ _____ ___ \n"), _(ANSI_BRED " __/ __/ _ \\ (_)__ " ANSI_RESET " | | / / _ \\/ _ \\\n"), _(ANSI_BRED " _/ _// // / / / _ \\" ANSI_RESET " | |/ / ___/ ___/\n"), _(ANSI_BRED " /_/ /____(_)_/\\___/" ANSI_RESET " |___/_/ /_/ \n"), _("\n") }; #undef _ /** Pager line index */ typedef struct { /** Index into pager_vector */ u32 line; /** Offset of the string in the line */ u32 offset; /** Length of the string in the line */ u32 length; } unix_cli_pager_index_t; /** Unix CLI session. */ typedef struct { /** The file index held by unix.c */ u32 clib_file_index; /** Vector of output pending write to file descriptor. */ u8 *output_vector; /** Vector of input saved by Unix input node to be processed by CLI process. */ u8 *input_vector; /** This session has command history. */ u8 has_history; /** Array of vectors of commands in the history. */ u8 **command_history; /** The command currently pointed at by the history cursor. */ u8 *current_command; /** How far from the end of the history array the user has browsed. */ i32 excursion; /** Maximum number of history entries this session will store. */ u32 history_limit; /** Current command line counter */ u32 command_number; /** The string being searched for in the history. */ u8 *search_key; /** If non-zero then the CLI is searching in the history array. * - @c -1 means search backwards. * - @c 1 means search forwards. */ int search_mode; /** Position of the insert cursor on the current input line */ u32 cursor; /** Line mode or char mode */ u8 line_mode; /** Set if the CRLF mode wants CR + LF */ u8 crlf_mode; /** Can we do ANSI output? */ u8 ansi_capable; /** Has the session started? */ u8 started; /** Disable the pager? */ u8 no_pager; /** Whether the session is interactive or not. * Controls things like initial banner, the CLI prompt etc. */ u8 is_interactive; /** Whether the session is attached to a socket. */ u8 is_socket; /** If EPIPE has been detected, prevent further write-related * activity on the descriptor. */ u8 has_epipe; /** Pager buffer */ u8 **pager_vector; /** Index of line fragments in the pager buffer */ unix_cli_pager_index_t *pager_index; /** Line number of top of page */ u32 pager_start; /** Terminal width */ u32 width; /** Terminal height */ u32 height; /** Process node identifier */ u32 process_node_index; /** The current direction of cursor travel. * This is important since when advancing left-to-right, at the * right hand edge of the console the terminal typically defers * wrapping the cursor to the next line until a character is * actually displayed. * This messes up our heuristic for whether to use ANSI to return * the cursor to the end of the line and instead we have to * nudge the cursor to the next line. * A Value of @c 0 means we're advancing left-to-right; @c 1 means * the opposite. */ u8 cursor_direction; } unix_cli_file_t; /** Resets the pager buffer and other data. * @param f The CLI session whose pager needs to be reset. */ always_inline void unix_cli_pager_reset (unix_cli_file_t * f) { u8 **p; f->pager_start = 0; vec_free (f->pager_index); f->pager_index = 0; vec_foreach (p, f->pager_vector) { vec_free (*p); } vec_free (f->pager_vector); f->pager_vector = 0; } /** Release storage used by a CLI session. * @param f The CLI session whose storage needs to be released. */ always_inline void unix_cli_file_free (unix_cli_file_t * f) { vec_free (f->output_vector); vec_free (f->input_vector); unix_cli_pager_reset (f); } /** CLI actions */ typedef enum { UNIX_CLI_PARSE_ACTION_NOACTION = 0, /**< No action */ UNIX_CLI_PARSE_ACTION_CRLF, /**< Carriage return, newline or enter */ UNIX_CLI_PARSE_ACTION_TAB, /**< Tab key */ UNIX_CLI_PARSE_ACTION_ERASE, /**< Erase cursor left */ UNIX_CLI_PARSE_ACTION_ERASERIGHT, /**< Erase cursor right */ UNIX_CLI_PARSE_ACTION_UP, /**< Up arrow */ UNIX_CLI_PARSE_ACTION_DOWN, /**< Down arrow */ UNIX_CLI_PARSE_ACTION_LEFT, /**< Left arrow */ UNIX_CLI_PARSE_ACTION_RIGHT, /**< Right arrow */ UNIX_CLI_PARSE_ACTION_HOME, /**< Home key (jump to start of line) */ UNIX_CLI_PARSE_ACTION_END, /**< End key (jump to end of line) */ UNIX_CLI_PARSE_ACTION_WORDLEFT, /**< Jump cursor to start of left word */ UNIX_CLI_PARSE_ACTION_WORDRIGHT, /**< Jump cursor to start of right word */ UNIX_CLI_PARSE_ACTION_ERASELINELEFT, /**< Erase line to left of cursor */ UNIX_CLI_PARSE_ACTION_ERASELINERIGHT, /**< Erase line to right & including cursor */ UNIX_CLI_PARSE_ACTION_CLEAR, /**< Clear the terminal */ UNIX_CLI_PARSE_ACTION_REVSEARCH, /**< Search backwards in command history */ UNIX_CLI_PARSE_ACTION_FWDSEARCH, /**< Search forwards in command history */ UNIX_CLI_PARSE_ACTION_YANK, /**< Undo last erase action */ UNIX_CLI_PARSE_ACTION_TELNETIAC, /**< Telnet control code */ UNIX_CLI_PARSE_ACTION_PAGER_CRLF, /**< Enter pressed (CR, CRLF, LF, etc) */ UNIX_CLI_PARSE_ACTION_PAGER_QUIT, /**< Exit the pager session */ UNIX_CLI_PARSE_ACTION_PAGER_NEXT, /**< Scroll to next page */ UNIX_CLI_PARSE_ACTION_PAGER_DN, /**< Scroll to next line */ UNIX_CLI_PARSE_ACTION_PAGER_UP, /**< Scroll to previous line */ UNIX_CLI_PARSE_ACTION_PAGER_TOP, /**< Scroll to first line */ UNIX_CLI_PARSE_ACTION_PAGER_BOTTOM, /**< Scroll to last line */ UNIX_CLI_PARSE_ACTION_PAGER_PGDN, /**< Scroll to next page */ UNIX_CLI_PARSE_ACTION_PAGER_PGUP, /**< Scroll to previous page */ UNIX_CLI_PARSE_ACTION_PAGER_REDRAW, /**< Clear and redraw the page on the terminal */ UNIX_CLI_PARSE_ACTION_PAGER_SEARCH, /**< Search the pager buffer */ UNIX_CLI_PARSE_ACTION_PARTIALMATCH, /**< Action parser found a partial match */ UNIX_CLI_PARSE_ACTION_NOMATCH /**< Action parser did not find any match */ } unix_cli_parse_action_t; /** @brief Mapping of input buffer strings to action values. * @note This won't work as a hash since we need to be able to do * partial matches on the string. */ typedef struct { u8 *input; /**< Input string to match. */ u32 len; /**< Length of input without final NUL. */ unix_cli_parse_action_t action; /**< Action to take when matched. */ } unix_cli_parse_actions_t; /** @brief Given a capital ASCII letter character return a @c NUL terminated * string with the control code for that letter. * * @param c An ASCII character. * @return A @c NUL terminated string of type @c u8[]. * * @par Example * @c CTL('A') returns { 0x01, 0x00 } as a @c u8[]. */ #define CTL(c) (u8[]){ (c) - '@', 0 } #define _(a,b) { .input = (u8 *)(a), .len = sizeof(a) - 1, .action = (b) } /** * Patterns to match on a CLI input stream. * @showinitializer */ static unix_cli_parse_actions_t unix_cli_parse_strings[] = { /* Line handling */ _("\r\n", UNIX_CLI_PARSE_ACTION_CRLF), /* Must be before '\r' */ _("\n", UNIX_CLI_PARSE_ACTION_CRLF), _("\r\0", UNIX_CLI_PARSE_ACTION_CRLF), /* Telnet does this */ _("\r", UNIX_CLI_PARSE_ACTION_CRLF), /* Unix shell control codes */ _(CTL ('B'), UNIX_CLI_PARSE_ACTION_LEFT), _(CTL ('F'), UNIX_CLI_PARSE_ACTION_RIGHT), _(CTL ('P'), UNIX_CLI_PARSE_ACTION_UP), _(CTL ('N'), UNIX_CLI_PARSE_ACTION_DOWN), _(CTL ('A'), UNIX_CLI_PARSE_ACTION_HOME), _(CTL ('E'), UNIX_CLI_PARSE_ACTION_END), _(CTL ('D'), UNIX_CLI_PARSE_ACTION_ERASERIGHT), _(CTL ('U'), UNIX_CLI_PARSE_ACTION_ERASELINELEFT), _(CTL ('K'), UNIX_CLI_PARSE_ACTION_ERASELINERIGHT), _(CTL ('Y'), UNIX_CLI_PARSE_ACTION_YANK), _(CTL ('L'), UNIX_CLI_PARSE_ACTION_CLEAR), _(ESC "b", UNIX_CLI_PARSE_ACTION_WORDLEFT), /* Alt-B */ _(ESC "f", UNIX_CLI_PARSE_ACTION_WORDRIGHT), /* Alt-F */ _("\b", UNIX_CLI_PARSE_ACTION_ERASE), /* ^H */ _("\x7f", UNIX_CLI_PARSE_ACTION_ERASE), /* Backspace */ _("\t", UNIX_CLI_PARSE_ACTION_TAB), /* ^I */ /* VT100 Normal mode - Broadest support */ _(CSI "A", UNIX_CLI_PARSE_ACTION_UP), _(CSI "B", UNIX_CLI_PARSE_ACTION_DOWN), _(CSI "C", UNIX_CLI_PARSE_ACTION_RIGHT), _(CSI "D", UNIX_CLI_PARSE_ACTION_LEFT), _(CSI "H", UNIX_CLI_PARSE_ACTION_HOME), _(CSI "F", UNIX_CLI_PARSE_ACTION_END), _(CSI "3~", UNIX_CLI_PARSE_ACTION_ERASERIGHT), /* Delete */ _(CSI "1;5D", UNIX_CLI_PARSE_ACTION_WORDLEFT), /* C-Left */ _(CSI "1;5C", UNIX_CLI_PARSE_ACTION_WORDRIGHT), /* C-Right */ /* VT100 Application mode - Some Gnome Terminal functions use these */ _(ESC "OA", UNIX_CLI_PARSE_ACTION_UP), _(ESC "OB", UNIX_CLI_PARSE_ACTION_DOWN), _(ESC "OC", UNIX_CLI_PARSE_ACTION_RIGHT), _(ESC "OD", UNIX_CLI_PARSE_ACTION_LEFT), _(ESC "OH", UNIX_CLI_PARSE_ACTION_HOME), _(ESC "OF", UNIX_CLI_PARSE_ACTION_END), /* ANSI X3.41-1974 - sent by Microsoft Telnet and PuTTY */ _(CSI "1~", UNIX_CLI_PARSE_ACTION_HOME), _(CSI "4~", UNIX_CLI_PARSE_ACTION_END), /* Emacs-ish history search */ _(CTL ('S'), UNIX_CLI_PARSE_ACTION_FWDSEARCH), _(CTL ('R'), UNIX_CLI_PARSE_ACTION_REVSEARCH), /* Other protocol things */ _("\xff", UNIX_CLI_PARSE_ACTION_TELNETIAC), /* IAC */ _("\0", UNIX_CLI_PARSE_ACTION_NOACTION), /* NUL */ _(NULL, UNIX_CLI_PARSE_ACTION_NOMATCH) }; /** * Patterns to match when a CLI session is in the pager. * @showinitializer */ static unix_cli_parse_actions_t unix_cli_parse_pager[] = { /* Line handling */ _("\r\n", UNIX_CLI_PARSE_ACTION_PAGER_CRLF), /* Must be before '\r' */ _("\n", UNIX_CLI_PARSE_ACTION_PAGER_CRLF), _("\r\0", UNIX_CLI_PARSE_ACTION_PAGER_CRLF), /* Telnet does this */ _("\r", UNIX_CLI_PARSE_ACTION_PAGER_CRLF), /* Pager commands */ _(" ", UNIX_CLI_PARSE_ACTION_PAGER_NEXT), _("q", UNIX_CLI_PARSE_ACTION_PAGER_QUIT), _(CTL ('L'), UNIX_CLI_PARSE_ACTION_PAGER_REDRAW), _(CTL ('R'), UNIX_CLI_PARSE_ACTION_PAGER_REDRAW), _("/", UNIX_CLI_PARSE_ACTION_PAGER_SEARCH), /* VT100 */ _(CSI "A", UNIX_CLI_PARSE_ACTION_PAGER_UP), _(CSI "B", UNIX_CLI_PARSE_ACTION_PAGER_DN), _(CSI "H", UNIX_CLI_PARSE_ACTION_PAGER_TOP), _(CSI "F", UNIX_CLI_PARSE_ACTION_PAGER_BOTTOM), /* VT100 Application mode */ _(ESC "OA", UNIX_CLI_PARSE_ACTION_PAGER_UP), _(ESC "OB", UNIX_CLI_PARSE_ACTION_PAGER_DN), _(ESC "OH", UNIX_CLI_PARSE_ACTION_PAGER_TOP), _(ESC "OF", UNIX_CLI_PARSE_ACTION_PAGER_BOTTOM), /* ANSI X3.41-1974 */ _(CSI "1~", UNIX_CLI_PARSE_ACTION_PAGER_TOP), _(CSI "4~", UNIX_CLI_PARSE_ACTION_PAGER_BOTTOM), _(CSI "5~", UNIX_CLI_PARSE_ACTION_PAGER_PGUP), _(CSI "6~", UNIX_CLI_PARSE_ACTION_PAGER_PGDN), /* Other protocol things */ _("\xff", UNIX_CLI_PARSE_ACTION_TELNETIAC), /* IAC */ _("\0", UNIX_CLI_PARSE_ACTION_NOACTION), /* NUL */ _(NULL, UNIX_CLI_PARSE_ACTION_NOMATCH) }; #undef _ /** CLI session events. */ typedef enum { UNIX_CLI_PROCESS_EVENT_READ_READY, /**< A file descriptor has data to be read. */ UNIX_CLI_PROCESS_EVENT_QUIT, /**< A CLI session wants to close. */ } unix_cli_process_event_type_t; /** CLI global state. */ typedef struct { /** Prompt string for CLI. */ u8 *cli_prompt; /** Vec pool of CLI sessions. */ unix_cli_file_t *cli_file_pool; /** Vec pool of unused session indices. */ u32 *unused_cli_process_node_indices; /** The session index of the stdin cli */ u32 stdin_cli_file_index; /** File pool index of current input. */ u32 current_input_file_index; } unix_cli_main_t; /** CLI global state */ static unix_cli_main_t unix_cli_main; /** * @brief Search for a byte sequence in the action list. * * Searches the @ref unix_cli_parse_actions_t list in @a a for a match with * the bytes in @a input of maximum length @a ilen bytes. * When a match is made @a *matched indicates how many bytes were matched. * Returns a value from the enum @ref unix_cli_parse_action_t to indicate * whether no match was found, a partial match was found or a complete * match was found and what action, if any, should be taken. * * @param[in] a Actions list to search within. * @param[in] input String fragment to search for. * @param[in] ilen Length of the string in 'input'. * @param[out] matched Pointer to an integer that will contain the number * of bytes matched when a complete match is found. * * @return Action from @ref unix_cli_parse_action_t that the string fragment * matches. * @ref UNIX_CLI_PARSE_ACTION_PARTIALMATCH is returned when the * whole input string matches the start of at least one action. * @ref UNIX_CLI_PARSE_ACTION_NOMATCH is returned when there is no * match at all. */ static unix_cli_parse_action_t unix_cli_match_action (unix_cli_parse_actions_t * a, u8 * input, u32 ilen, i32 * matched) { u8 partial = 0; while (a->input) { if (ilen >= a->len) { /* see if the start of the input buffer exactly matches the current * action string. */ if (memcmp (input, a->input, a->len) == 0) { *matched = a->len; return a->action; } } else { /* if the first ilen characters match, flag this as a partial - * meaning keep collecting bytes in case of a future match */ if (memcmp (input, a->input, ilen) == 0) partial = 1; } /* check next action */ a++; } return partial ? UNIX_CLI_PARSE_ACTION_PARTIALMATCH : UNIX_CLI_PARSE_ACTION_NOMATCH; } /** Add bytes to the output vector and then flagg the I/O system that bytes * are available to be sent. */ static void unix_cli_add_pending_output (clib_file_t * uf, unix_cli_file_t * cf, u8 * buffer, uword buffer_bytes) { clib_file_main_t *fm = &file_main; vec_add (cf->output_vector, buffer, buffer_bytes); if (vec_l
/*
 * Copyright (c) 2016 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 L2-GRE over IPSec packet processing.
 *
 * Add GRE header to thr packet and send it to the esp-encrypt node.
*/

#include <vnet/vnet.h>
#include <vnet/ipsec-gre/ipsec_gre.h>

ipsec_gre_main_t ipsec_gre_main;

/**
 * @brief IPv4 and GRE header union.
 *
*/
typedef struct
{
  union
  {
    ip4_and_gre_header_t ip4_and_gre;
    u64 as_u64[3];
  };
} ip4_and_gre_union_t;

/**
 * @brief Packet trace.
 *
*/
typedef struct
{
  u32 tunnel_id; /**< Tunnel-id / index in tunnel vector */

  u32 length; /**< pkt length */

  ip4_address_t src; /**< tunnel src IPv4 address */
  ip4_address_t dst; /**< tunnel dst IPv4 address */

  u32 sa_id; /**< tunnel IPSec SA id */
} ipsec_gre_tx_trace_t;

u8 *
format_ipsec_gre_tx_trace (u8 * s, va_list * args)
{
  CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
  CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
  ipsec_gre_tx_trace_t *t = va_arg (*args, ipsec_gre_tx_trace_t *);

  s = format (s, "GRE: tunnel %d len %d src %U dst %U sa-id %d",
	      t->tunnel_id, clib_net_to_host_u16 (t->length),
	      format_ip4_address, &t->src.as_u8,
	      format_ip4_address, &t->dst.as_u8, t->sa_id);
  return s;
}

/**
 * @brief IPSec-GRE tunnel interface tx function.
 *
 * Add GRE header to the packet.
 *
 * @param vm vlib_main_t corresponding to the current thread.
 * @param node vlib_node_runtime_t data for this node.
 * @param frame vlib_frame_t whose contents should be dispatched.
 *
 * @par Graph mechanics: buffer metadata, next index usage
 *
 * <em>Uses:</em>
 * - <code>node->runtime_data</code>
 *     - Match tunnel by <code>rd->dev_instance</code> in IPSec-GRE tunnels
 *       pool.
 *
 * <em>Sets:</em>
 * - <code>vnet_buffer(b)->output_features.ipsec_sad_index</code>
 *     - Set IPSec Security Association for packet encryption.
 * - <code>vnet_buffer(b)->sw_if_index[VLIB_TX]</code>
 *     - Reset output sw_if_index.
 *
 * <em>Nexd Index:</em>
 * - Dispatches the packet to the esp-encrypt node.
*/
static uword
ipsec_gre_interface_tx (vlib_main_t * vm,
			vlib_node_runtime_t * node, vlib_frame_t * frame)
{
  ipsec_gre_main_t *igm = &ipsec_gre_main;
  u32 next_index;
  u32 *from, *to_next, n_left_from, n_left_to_next;
  vnet_interface_output_runtime_t *rd = (void *) node->runtime_data;
  ipsec_gre_tunnel_t *t = pool_elt_at_index (igm->tunnels, rd->dev_instance);

  /* Vector of buffer / pkt indices we're supposed to process */
  from = vlib_frame_vector_args (frame);

  /* Number of buffers / pkts */
  n_left_from = frame->n_vectors;

  /* Speculatively send the first buffer to the last disposition we used */
  next_index = node->cached_next_index;

  while (n_left_from > 0)
    {
      /* set up to enqueue to our disposition with index = next_index */
      vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);

      /*
       * As long as we have enough pkts left to process two pkts
       * and prefetch two pkts...
       */
      while (n_left_from >= 4 && n_left_to_next >= 2)
	{
	  vlib_buffer_t *b0, *b1;
	  ip4_header_t *ip0, *ip1;
	  ip4_and_gre_union_t *h0, *h1;
	  u32 bi0, next0, bi1, next1;
	  __attribute__ ((unused)) u8 error0, error1;
	  u16 gre_protocol0, gre_protocol1;

	  /* Prefetch the next iteration */
	  {
	    vlib_buffer_t *p2, *p3;

	    p2 = vlib_get_buffer (vm, from[2]);
	    p3 = vlib_get_buffer (vm, from[3]);

	    vlib_prefetch_buffer_header (p2, LOAD);
	    vlib_prefetch_buffer_header (p3, LOAD);

	    /*
	     * Prefetch packet data. We expect to overwrite
	     * the inbound L2 header with an ip header and a
	     * gre header. Might want to prefetch the last line
	     * of rewrite space as well; need profile data
	     */
	    CLIB_PREFETCH (p2->data, CLIB_CACHE_LINE_BYTES, STORE);
	    CLIB_PREFETCH (p3->data, CLIB_CACHE_LINE_BYTES, STORE);
	  }

	  /* Pick up the next two buffer indices */
	  bi0 = from[0];
	  bi1 = from[1];

	  /* Speculatively enqueue them where we sent the last buffer */
	  to_next[0] = bi0;
	  to_next[1] = bi1;
	  from += 2;
	  to_next += 2;
	  n_left_to_next -= 2;
	  n_left_from -= 2;

	  b0 = vlib_get_buffer (vm, bi0);
	  b1 = vlib_get_buffer (vm, bi1);

	  ip0 = vlib_buffer_get_current (b0);
	  gre_protocol0 = clib_net_to_host_u16 (0x01);

	  ip1 = vlib_buffer_get_current (b1);
	  gre_protocol1 = clib_net_to_host_u16 (0x01);

	  vlib_buffer_advance (b0, -sizeof (*h0));
	  vlib_buffer_advance (b1, -sizeof (*h1));

	  h0 = vlib_buffer_get_current (b0);
	  h1 = vlib_buffer_get_current (b1);
	  h0->as_u64[0] = 0;
	  h0->as_u64[1] = 0;
	  h0->as_u64[2] = 0;

	  h1->as_u64[0] = 0;
	  h1->as_u64[1] = 0;
	  h1->as_u64[2] = 0;

	  ip0 = &h0->ip4_and_gre.ip4;
	  h0->ip4_and_gre.gre.protocol = gre_protocol0;
	  ip0->ip_version_and_header_length = 0x45;
	  ip0->ttl = 254;
	  ip0->protocol = IP_PROTOCOL_GRE;

	  ip1 = &h1->ip4_and_gre.ip4;
	  h1->ip4_and_gre.gre.protocol = gre_protocol1;
	  ip1->ip_version_and_header_length = 0x45;
	  ip1->ttl = 254;
	  ip1->protocol = IP_PROTOCOL_GRE;

	  ip0->length =
	    clib_host_to_net_u16 (vlib_buffer_length_in_chain (vm, b0));
	  ip1->length =
	    clib_host_to_net_u16 (vlib_buffer_length_in_chain (vm, b1));
	  ip0->src_address.as_u32 = t->tunnel_src.as_u32;
	  ip1->src_address.as_u32 = t->tunnel_src.as_u32;
	  ip0->dst_address.as_u32 = t->tunnel_dst.as_u32;
	  ip1->dst_address.as_u32 = t->tunnel_dst.as_u32;
	  ip0->checksum = ip4_header_checksum (ip0);
	  ip1->checksum = ip4_header_checksum (ip1);

	  vnet_buffer (b0)->sw_if_index[VLIB_RX] =
	    vnet_buffer (b0)->sw_if_index[VLIB_TX];
	  vnet_buffer (b1)->sw_if_index[VLIB_RX] =
	    vnet_buffer (b1)->sw_if_index[VLIB_TX];

	  vnet_buffer (b0)->ipsec.sad_index = t->local_sa;
	  vnet_buffer (b1)->ipsec.sad_index = t->local_sa;

	  vnet_buffer (b0)->sw_if_index[VLIB_TX] = (u32) ~ 0;
	  vnet_buffer (b1)->sw_if_index[VLIB_TX] = (u32) ~ 0;

	  next0 = IPSEC_GRE_OUTPUT_NEXT_ESP_ENCRYPT;
	  next1 = IPSEC_GRE_OUTPUT_NEXT_ESP_ENCRYPT;
	  error0 = IPSEC_GRE_ERROR_NONE;
	  error1 = IPSEC_GRE_ERROR_NONE;

	  if (PREDICT_FALSE (b0->flags & VLIB_BUFFER_IS_TRACED))
	    {
	      ipsec_gre_tx_trace_t *tr = vlib_add_trace (vm, node,
							 b0, sizeof (*tr));
	      tr->tunnel_id = t - igm->tunnels;
	      tr->length = ip0->length;
	      tr->src.as_u32 = ip0->src_address.as_u32;
	      tr->dst.as_u32 = ip0->dst_address.as_u32;
	      tr->sa_id = t->local_sa_id;
	    }

	  if (PREDICT_FALSE (b1->flags & VLIB_BUFFER_IS_TRACED))
	    {
	      ipsec_gre_tx_trace_t *tr = vlib_add_trace (vm, node,
							 b1, sizeof (*tr));
	      tr->tunnel_id = t - igm->tunnels;
	      tr->length = ip1->length;
	      tr->src.as_u32 = ip1->src_address.as_u32;
	      tr->dst.as_u32 = ip1->dst_address.as_u32;
	      tr->sa_id = t->local_sa_id;
	    }

	  vlib_validate_buffer_enqueue_x2 (vm, node, next_index,
					   to_next, n_left_to_next,
					   bi0, bi1, next0, next1);
	}

      while (n_left_from > 0 && n_left_to_next > 0)
	{
	  vlib_buffer_t *b0;
	  ip4_header_t *ip0;
	  ip4_and_gre_union_t *h0;
	  u32 bi0, next0;
	  __attribute__ ((unused)) u8 error0;
	  u16 gre_protocol0;

	  bi0 = to_next[0] = from[0];
	  from += 1;
	  n_left_from -= 1;
	  to_next += 1;
	  n_left_to_next -= 1;

	  b0 = vlib_get_buffer (vm, bi0);

	  gre_protocol0 = clib_net_to_host_u16 (0x01);

	  vlib_buffer_advance (b0, -sizeof (*h0));

	  h0 = vlib_buffer_get_current (b0);
	  h0->as_u64[0] = 0;
	  h0->as_u64[1] = 0;
	  h0->as_u64[2] = 0;

	  ip0 = &h0->ip4_and_gre.ip4;
	  h0->ip4_and_gre.gre.protocol = gre_protocol0;
	  ip0->ip_version_and_header_length = 0x45;
	  ip0->ttl = 254;
	  ip0->protocol = IP_PROTOCOL_GRE;
	  ip0->length =
	    clib_host_to_net_u16 (vlib_buffer_length_in_chain (vm, b0));
	  ip0->src_address.as_u32 = t->tunnel_src.as_u32;
	  ip0->dst_address.as_u32 = t->tunnel_dst.as_u32;
	  ip0->checksum = ip4_header_checksum (ip0);

	  vnet_buffer (b0)->sw_if_index[VLIB_RX] =
	    vnet_buffer (b0)->sw_if_index[VLIB_TX];
	  vnet_buffer (b0)->ipsec.sad_index = t->local_sa;
	  vnet_buffer (b0)->sw_if_index[VLIB_TX] = (u32) ~ 0;

	  next0 = IPSEC_GRE_OUTPUT_NEXT_ESP_ENCRYPT;
	  error0 = IPSEC_GRE_ERROR_NONE;

	  if (PREDICT_FALSE (b0->flags & VLIB_BUFFER_IS_TRACED))
	    {
	      ipsec_gre_tx_trace_t *tr = vlib_add_trace (vm, node,
							 b0, sizeof (*tr));
	      tr->tunnel_id = t - igm->tunnels;
	      tr->length = ip0->length;
	      tr->src.as_u32 = ip0->src_address.as_u32;
	      tr->dst.as_u32 = ip0->dst_address.as_u32;
	      tr->sa_id = t->local_sa_id;
	    }

	  vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
					   to_next, n_left_to_next,
					   bi0, next0);
	}

      vlib_put_next_frame (vm, node, next_index, n_left_to_next);
    }

  vlib_node_increment_counter (vm, ipsec_gre_input_node.index,
			       IPSEC_GRE_ERROR_PKTS_ENCAP, frame->n_vectors);

  return frame->n_vectors;
}

static clib_error_t *
ipsec_gre_interface_admin_up_down (vnet_main_t * vnm, u32 hw_if_index,
				   u32 flags)
{
  if (flags & VNET_SW_INTERFACE_FLAG_ADMIN_UP)
    vnet_hw_interface_set_flags (vnm, hw_if_index,
				 VNET_HW_INTERFACE_FLAG_LINK_UP);
  else
    vnet_hw_interface_set_flags (vnm, hw_if_index, 0 /* down */ );

  return /* no error */ 0;
}

static u8 *
format_ipsec_gre_tunnel_name (u8 * s, va_list * args)
{
  u32 dev_instance = va_arg (*args, u32);
  return format (s, "ipsec-gre%d", dev_instance);
}

static u8 *
format_ipsec_gre_device (u8 * s, va_list * args)
{
  u32 dev_instance = va_arg (*args, u32);
  CLIB_UNUSED (int verbose) = va_arg (*args, int);

  s = format (s, "IPSEC-GRE tunnel: id %d\n", dev_instance);
  return s;
}

/* *INDENT-OFF* */
VNET_DEVICE_CLASS (ipsec_gre_device_class) = {
  .name = "IPSec GRE tunnel device",
  .format_device_name = format_ipsec_gre_tunnel_name,
  .format_device = format_ipsec_gre_device,
  .format_tx_trace = format_ipsec_gre_tx_trace,
  .tx_function = ipsec_gre_interface_tx,
  .admin_up_down_function = ipsec_gre_interface_admin_up_down,
};

VLIB_DEVICE_TX_FUNCTION_MULTIARCH (ipsec_gre_device_class,
				   ipsec_gre_interface_tx)


VNET_HW_INTERFACE_CLASS (ipsec_gre_hw_interface_class) = {
  .name = "IPSEC-GRE",
};
/* *INDENT-ON* */

static clib_error_t *
ipsec_gre_init (vlib_main_t * vm)
{
  ipsec_gre_main_t *igm = &ipsec_gre_main;
  clib_error_t *error;

  memset (igm, 0, sizeof (igm[0]));
  igm->vlib_main = vm;
  igm->vnet_main = vnet_get_main ();

  if ((error = vlib_call_init_function (vm, ip_main_init)))
    return error;

  if ((error = vlib_call_init_function (vm, ip4_lookup_init)))
    return error;

  igm->tunnel_by_key = hash_create (0, sizeof (uword));

  return vlib_call_init_function (vm, ipsec_gre_input_init);
}

VLIB_INIT_FUNCTION (ipsec_gre_init);

/*
* fd.io coding-style-patch-verification: ON
*
* Local Variables:
* eval: (c-set-style "gnu")
* End:
*/
_add (fm, &template); cf->output_vector = 0; cf->input_vector = 0; vlib_start_process (vm, n->runtime_index); vlib_process_t *p = vlib_get_process_from_node (vm, n); p->output_function = unix_vlib_cli_output; p->output_function_arg = cf - cm->cli_file_pool; return cf - cm->cli_file_pool; } /** Telnet listening socket has a new connection. */ static clib_error_t * unix_cli_listen_read_ready (clib_file_t * uf) { unix_main_t *um = &unix_main; clib_file_main_t *fm = &file_main; unix_cli_main_t *cm = &unix_cli_main; clib_socket_t *s = &um->cli_listen_socket; clib_socket_t client; char *client_name; clib_error_t *error; unix_cli_file_t *cf; u32 cf_index; error = clib_socket_accept (s, &client); if (error) return error; client_name = (char *) format (0, "%U%c", format_sockaddr, &client.peer, 0); cf_index = unix_cli_file_add (cm, client_name, client.fd); cf = pool_elt_at_index (cm->cli_file_pool, cf_index); cf->is_socket = 1; /* No longer need CLIB version of socket. */ clib_socket_free (&client); vec_free (client_name); /* if we're supposed to run telnet session in character mode (default) */ if (um->cli_line_mode == 0) { /* * Set telnet client character mode, echo on, suppress "go-ahead". * Technically these should be negotiated, but this works. */ u8 charmode_option[] = { IAC, WONT, TELOPT_LINEMODE, /* server will do char-by-char */ IAC, DONT, TELOPT_LINEMODE, /* client should do char-by-char */ IAC, WILL, TELOPT_SGA, /* server willl supress GA */ IAC, DO, TELOPT_SGA, /* client should supress Go Ahead */ IAC, WILL, TELOPT_ECHO, /* server will do echo */ IAC, DONT, TELOPT_ECHO, /* client should not echo */ IAC, DO, TELOPT_TTYPE, /* client should tell us its term type */ IAC, SB, TELOPT_TTYPE, 1, IAC, SE, /* now tell me ttype */ IAC, DO, TELOPT_NAWS, /* client should tell us its window sz */ IAC, SB, TELOPT_NAWS, 1, IAC, SE, /* now tell me window size */ }; /* Enable history on this CLI */ cf->history_limit = um->cli_history_limit; cf->has_history = cf->history_limit != 0; /* This is an interactive session until we decide otherwise */ cf->is_interactive = 1; /* Make sure this session is in line mode */ cf->line_mode = 0; /* We need CRLF */ cf->crlf_mode = 1; /* Setup the pager */ cf->no_pager = um->cli_no_pager; /* Default terminal dimensions, should the terminal * fail to provide any. */ cf->width = UNIX_CLI_DEFAULT_TERMINAL_WIDTH; cf->height = UNIX_CLI_DEFAULT_TERMINAL_HEIGHT; /* Send the telnet options */ uf = pool_elt_at_index (fm->file_pool, cf->clib_file_index); unix_vlib_cli_output_raw (cf, uf, charmode_option, ARRAY_LEN (charmode_option)); /* In case the client doesn't negotiate terminal type, use * a timer to kick off the initial prompt. */ timer_call (unix_cli_file_welcome_timer, cf_index, 1); } return error; } /** The system terminal has informed us that the window size * has changed. */ static void unix_cli_resize_interrupt (int signum) { clib_file_main_t *fm = &file_main; unix_cli_main_t *cm = &unix_cli_main; unix_cli_file_t *cf = pool_elt_at_index (cm->cli_file_pool, cm->stdin_cli_file_index); clib_file_t *uf = pool_elt_at_index (fm->file_pool, cf->clib_file_index); struct winsize ws; (void) signum; /* Terminal resized, fetch the new size */ if (ioctl (STDIN_FILENO, TIOCGWINSZ, &ws) < 0) { /* "Should never happen..." */ clib_unix_warning ("TIOCGWINSZ"); /* We can't trust ws.XXX... */ return; } cf->width = ws.ws_col; if (cf->width > UNIX_CLI_MAX_TERMINAL_WIDTH) cf->width = UNIX_CLI_MAX_TERMINAL_WIDTH; if (cf->width == 0) cf->width = UNIX_CLI_DEFAULT_TERMINAL_WIDTH; cf->height = ws.ws_row; if (cf->height > UNIX_CLI_MAX_TERMINAL_HEIGHT) cf->height = UNIX_CLI_MAX_TERMINAL_HEIGHT; if (cf->height == 0) cf->height = UNIX_CLI_DEFAULT_TERMINAL_HEIGHT; /* Reindex the pager buffer */ unix_cli_pager_reindex (cf); /* Redraw the page */ unix_cli_pager_redraw (cf, uf); } /** Handle configuration directives in the @em unix section. */ static clib_error_t * unix_cli_config (vlib_main_t * vm, unformat_input_t * input) { unix_main_t *um = &unix_main; clib_file_main_t *fm = &file_main; unix_cli_main_t *cm = &unix_cli_main; int flags; clib_error_t *error = 0; unix_cli_file_t *cf; u32 cf_index; struct termios tio; struct sigaction sa; struct winsize ws; u8 *term; /* We depend on unix flags being set. */ if ((error = vlib_call_config_function (vm, unix_config))) return error; if (um->flags & UNIX_FLAG_INTERACTIVE) { /* Set stdin to be non-blocking. */ if ((flags = fcntl (STDIN_FILENO, F_GETFL, 0)) < 0) flags = 0; (void) fcntl (STDIN_FILENO, F_SETFL, flags | O_NONBLOCK); cf_index = unix_cli_file_add (cm, "stdin", STDIN_FILENO); cf = pool_elt_at_index (cm->cli_file_pool, cf_index); cm->stdin_cli_file_index = cf_index; /* If stdin is a tty and we are using chacracter mode, enable * history on the CLI and set the tty line discipline accordingly. */ if (isatty (STDIN_FILENO) && um->cli_line_mode == 0) { /* Capture terminal resize events */ memset (&sa, 0, sizeof (sa)); sa.sa_handler = unix_cli_resize_interrupt; if (sigaction (SIGWINCH, &sa, 0) < 0) clib_panic ("sigaction"); /* Retrieve the current terminal size */ ioctl (STDIN_FILENO, TIOCGWINSZ, &ws); cf->width = ws.ws_col; cf->height = ws.ws_row; if (cf->width == 0 || cf->height == 0) { /* * We have a tty, but no size. Use defaults. * vpp "unix interactive" inside emacs + gdb ends up here. */ cf->width = UNIX_CLI_DEFAULT_TERMINAL_WIDTH; cf->height = UNIX_CLI_DEFAULT_TERMINAL_HEIGHT; } /* Setup the history */ cf->history_limit = um->cli_history_limit; cf->has_history = cf->history_limit != 0; /* Setup the pager */ cf->no_pager = um->cli_no_pager; /* This is an interactive session until we decide otherwise */ cf->is_interactive = 1; /* We're going to be in char by char mode */ cf->line_mode = 0; /* Save the original tty state so we can restore it later */ tcgetattr (STDIN_FILENO, &um->tio_stdin); um->tio_isset = 1; /* Tweak the tty settings */ tio = um->tio_stdin; /* echo off, canonical mode off, ext'd input processing off */ tio.c_lflag &= ~(ECHO | ICANON | IEXTEN); /* disable XON/XOFF, so ^S invokes the history search */ tio.c_iflag &= ~(IXON | IXOFF); tio.c_cc[VMIN] = 1; /* 1 byte at a time */ tio.c_cc[VTIME] = 0; /* no timer */ tio.c_cc[VSTOP] = _POSIX_VDISABLE; /* not ^S */ tio.c_cc[VSTART] = _POSIX_VDISABLE; /* not ^Q */ tcsetattr (STDIN_FILENO, TCSAFLUSH, &tio); /* See if we can do ANSI/VT100 output */ term = (u8 *) getenv ("TERM"); if (term != NULL) { int len = strlen ((char *) term); cf->ansi_capable = unix_cli_terminal_type_ansi (term, len); if (unix_cli_terminal_type_noninteractive (term, len)) unix_cli_set_session_noninteractive (cf); } } else { /* No tty, so make sure the session doesn't have tty-like features */ unix_cli_set_session_noninteractive (cf); } /* Send banner and initial prompt */ unix_cli_file_welcome (cm, cf); } /* If we have socket config, LISTEN, otherwise, don't */ clib_socket_t *s = &um->cli_listen_socket; if (s->config && s->config[0] != 0) { /* CLI listen. */ clib_file_t template = { 0 }; /* mkdir of file socketu, only under /run */ if (strncmp (s->config, "/run", 4) == 0) { u8 *tmp = format (0, "%s", s->config); int i = vec_len (tmp); while (i && tmp[--i] != '/') ; tmp[i] = 0; if (i) vlib_unix_recursive_mkdir ((char *) tmp); vec_free (tmp); } s->flags = CLIB_SOCKET_F_IS_SERVER | /* listen, don't connect */ CLIB_SOCKET_F_ALLOW_GROUP_WRITE; /* PF_LOCAL socket only */ error = clib_socket_init (s); if (error) return error; template.read_function = unix_cli_listen_read_ready; template.file_descriptor = s->fd; template.description = format (0, "cli listener %s", s->config); clib_file_add (fm, &template); } /* Set CLI prompt. */ if (!cm->cli_prompt) cm->cli_prompt = format (0, "VLIB: "); return 0; } /*? * This module has no configurable parameters. ?*/ VLIB_CONFIG_FUNCTION (unix_cli_config, "unix-cli"); /** Called when VPP is shutting down, this restores the system * terminal state if previously saved. */ static clib_error_t * unix_cli_exit (vlib_main_t * vm) { unix_main_t *um = &unix_main; /* If stdin is a tty and we saved the tty state, reset the tty state */ if (isatty (STDIN_FILENO) && um->tio_isset) tcsetattr (STDIN_FILENO, TCSAFLUSH, &um->tio_stdin); return 0; } VLIB_MAIN_LOOP_EXIT_FUNCTION (unix_cli_exit); /** Set the CLI prompt. * @param prompt The C string to set the prompt to. * @note This setting is global; it impacts all current * and future CLI sessions. */ void vlib_unix_cli_set_prompt (char *prompt) { char *fmt = (prompt[strlen (prompt) - 1] == ' ') ? "%s" : "%s "; unix_cli_main_t *cm = &unix_cli_main; if (cm->cli_prompt) vec_free (cm->cli_prompt); cm->cli_prompt = format (0, fmt, prompt); } /** CLI command to quit the terminal session. * @note If this is a stdin session then this will * shutdown VPP also. */ static clib_error_t * unix_cli_quit (vlib_main_t * vm, unformat_input_t * input, vlib_cli_command_t * cmd) { unix_cli_main_t *cm = &unix_cli_main; unix_cli_file_t *cf = pool_elt_at_index (cm->cli_file_pool, cm->current_input_file_index); /* Cosmetic: suppress the final prompt from appearing before we die */ cf->is_interactive = 0; cf->started = 1; vlib_process_signal_event (vm, vlib_current_process (vm), UNIX_CLI_PROCESS_EVENT_QUIT, cm->current_input_file_index); return 0; } /*? * Terminates the current CLI session. * * If VPP is running in @em interactive mode and this is the console session * (that is, the session on @c stdin) then this will also terminate VPP. ?*/ /* *INDENT-OFF* */ VLIB_CLI_COMMAND (unix_cli_quit_command, static) = { .path = "quit", .short_help = "Exit CLI", .function = unix_cli_quit, }; /* *INDENT-ON* */ /* *INDENT-OFF* */ VLIB_CLI_COMMAND (unix_cli_q_command, static) = { .path = "q", .short_help = "Exit CLI", .function = unix_cli_quit, }; /* *INDENT-ON* */ /** CLI command to execute a VPP command script. */ static clib_error_t * unix_cli_exec (vlib_main_t * vm, unformat_input_t * input, vlib_cli_command_t * cmd) { char *file_name; int fd; unformat_input_t sub_input; clib_error_t *error; file_name = 0; fd = -1; error = 0; if (!unformat (input, "%s", &file_name)) { error = clib_error_return (0, "expecting file name, got `%U'", format_unformat_error, input); goto done; } fd = open (file_name, O_RDONLY); if (fd < 0) { error = clib_error_return_unix (0, "failed to open `%s'", file_name); goto done; } /* Make sure its a regular file. */ { struct stat s; if (fstat (fd, &s) < 0) { error = clib_error_return_unix (0, "failed to stat `%s'", file_name); goto done; } if (!(S_ISREG (s.st_mode) || S_ISLNK (s.st_mode))) { error = clib_error_return (0, "not a regular file `%s'", file_name); goto done; } } unformat_init_clib_file (&sub_input, fd); vlib_cli_input (vm, &sub_input, 0, 0); unformat_free (&sub_input); done: if (fd > 0) close (fd); vec_free (file_name); return error; } /*? * Executes a sequence of CLI commands which are read from a file. If * a command is unrecognised or otherwise invalid then the usual CLI * feedback will be generated, however execution of subsequent commands * from the file will continue. * * The VPP code is indifferent to the file location. However, if SELinux * is enabled, then the file needs to have an SELinux label the VPP * process is allowed to access. For example, if a file is created in * '/usr/share/vpp/', it will be allowed. However, files manually * created in '/tmp/' or '/home//' will not be accessible by the VPP * process when SELinux is enabled. * * @cliexpar * Sample file: * @clistart * $ cat /usr/share/vpp/scripts/gigup.txt * set interface state GigabitEthernet0/8/0 up * set interface state GigabitEthernet0/9/0 up * @cliend * Example of how to execute a set of CLI commands from a file: * @cliexcmd{exec /usr/share/vpp/scripts/gigup.txt} ?*/ /* *INDENT-OFF* */ VLIB_CLI_COMMAND (cli_exec, static) = { .path = "exec", .short_help = "exec ", .function = unix_cli_exec, .is_mp_safe = 1, }; /* *INDENT-ON* */ /** CLI command to show various unix error statistics. */ static clib_error_t * unix_show_errors (vlib_main_t * vm, unformat_input_t * input, vlib_cli_command_t * cmd) { unix_main_t *um = &unix_main; clib_error_t *error = 0; int i, n_errors_to_show; unix_error_history_t *unix_errors = 0; n_errors_to_show = 1 << 30; if (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) { if (!unformat (input, "%d", &n_errors_to_show)) { error = clib_error_return (0, "expecting integer number of errors to show, got `%U'", format_unformat_error, input); goto done; } } n_errors_to_show = clib_min (ARRAY_LEN (um->error_history), n_errors_to_show); i = um->error_history_index > 0 ? um->error_history_index - 1 : ARRAY_LEN (um->error_history) - 1; while (n_errors_to_show > 0) { unix_error_history_t *eh = um->error_history + i; if (!eh->error) break; vec_add1 (unix_errors, eh[0]); n_errors_to_show -= 1; if (i == 0) i = ARRAY_LEN (um->error_history) - 1; else i--; } if (vec_len (unix_errors) == 0) vlib_cli_output (vm, "no Unix errors so far"); else { vlib_cli_output (vm, "%Ld total errors seen", um->n_total_errors); for (i = vec_len (unix_errors) - 1; i >= 0; i--) { unix_error_history_t *eh = vec_elt_at_index (unix_errors, i); vlib_cli_output (vm, "%U: %U", format_time_interval, "h:m:s:u", eh->time, format_clib_error, eh->error); } vlib_cli_output (vm, "%U: time now", format_time_interval, "h:m:s:u", vlib_time_now (vm)); } done: vec_free (unix_errors); return error; } /* *INDENT-OFF* */ VLIB_CLI_COMMAND (cli_unix_show_errors, static) = { .path = "show unix errors", .short_help = "Show Unix system call error history", .function = unix_show_errors, }; /* *INDENT-ON* */ /** CLI command to show various unix error statistics. */ static clib_error_t * unix_show_files (vlib_main_t * vm, unformat_input_t * input, vlib_cli_command_t * cmd) { clib_error_t *error = 0; clib_file_main_t *fm = &file_main; clib_file_t *f; char path[PATH_MAX]; u8 *s = 0; vlib_cli_output (vm, "%3s %6s %12s %12s %12s %-32s %s", "FD", "Thread", "Read", "Write", "Error", "File Name", "Description"); /* *INDENT-OFF* */ pool_foreach (f, fm->file_pool,( { int rv; s = format (s, "/proc/self/fd/%d%c", f->file_descriptor, 0); rv = readlink((char *) s, path, PATH_MAX - 1); path[rv < 0 ? 0 : rv] = 0; vlib_cli_output (vm, "%3d %6d %12d %12d %12d %-32s %v", f->file_descriptor, f->polling_thread_index, f->read_events, f->write_events, f->error_events, path, f->description); vec_reset_length (s); })); /* *INDENT-ON* */ vec_free (s); return error; } /* *INDENT-OFF* */ VLIB_CLI_COMMAND (cli_unix_show_files, static) = { .path = "show unix files", .short_help = "Show Unix files in use", .function = unix_show_files, }; /* *INDENT-ON* */ /** CLI command to show session command history. */ static clib_error_t * unix_cli_show_history (vlib_main_t * vm, unformat_input_t * input, vlib_cli_command_t * cmd) { unix_cli_main_t *cm = &unix_cli_main; unix_cli_file_t *cf; int i, j; cf = pool_elt_at_index (cm->cli_file_pool, cm->current_input_file_index); if (!cf->is_interactive) return clib_error_return (0, "invalid for non-interactive sessions"); if (cf->has_history && cf->history_limit) { i = 1 + cf->command_number - vec_len (cf->command_history); for (j = 0; j < vec_len (cf->command_history); j++) vlib_cli_output (vm, "%d %v\n", i + j, cf->command_history[j]); } else { vlib_cli_output (vm, "History not enabled.\n"); } return 0; } /*? * Displays the command history for the current session, if any. ?*/ /* *INDENT-OFF* */ VLIB_CLI_COMMAND (cli_unix_cli_show_history, static) = { .path = "history", .short_help = "Show current session command history", .function = unix_cli_show_history, }; /* *INDENT-ON* */ /** CLI command to show terminal status. */ static clib_error_t * unix_cli_show_terminal (vlib_main_t * vm, unformat_input_t * input, vlib_cli_command_t * cmd) { unix_main_t *um = &unix_main; unix_cli_main_t *cm = &unix_cli_main; unix_cli_file_t *cf; vlib_node_t *n; cf = pool_elt_at_index (cm->cli_file_pool, cm->current_input_file_index); n = vlib_get_node (vm, cf->process_node_index); vlib_cli_output (vm, "Terminal name: %v\n", n->name); vlib_cli_output (vm, "Terminal mode: %s\n", cf->line_mode ? "line-by-line" : "char-by-char"); vlib_cli_output (vm, "Terminal width: %d\n", cf->width); vlib_cli_output (vm, "Terminal height: %d\n", cf->height); vlib_cli_output (vm, "ANSI capable: %s\n", cf->ansi_capable ? "yes" : "no"); vlib_cli_output (vm, "Interactive: %s\n", cf->is_interactive ? "yes" : "no"); vlib_cli_output (vm, "History enabled: %s%s\n", cf->has_history ? "yes" : "no", !cf->has_history || cf->history_limit ? "" : " (disabled by history limit)"); if (cf->has_history) vlib_cli_output (vm, "History limit: %d\n", cf->history_limit); vlib_cli_output (vm, "Pager enabled: %s%s%s\n", cf->no_pager ? "no" : "yes", cf->no_pager || cf->height ? "" : " (disabled by terminal height)", cf->no_pager || um->cli_pager_buffer_limit ? "" : " (disabled by buffer limit)"); if (!cf->no_pager) vlib_cli_output (vm, "Pager limit: %d\n", um->cli_pager_buffer_limit); vlib_cli_output (vm, "CRLF mode: %s\n", cf->crlf_mode ? "CR+LF" : "LF"); return 0; } /*? * Displays various information about the state of the current terminal * session. * * @cliexpar * @cliexstart{show terminal} * Terminal name: unix-cli-stdin * Terminal mode: char-by-char * Terminal width: 123 * Terminal height: 48 * ANSI capable: yes * Interactive: yes * History enabled: yes * History limit: 50 * Pager enabled: yes * Pager limit: 100000 * CRLF mode: LF * @cliexend ?*/ /* *INDENT-OFF* */ VLIB_CLI_COMMAND (cli_unix_cli_show_terminal, static) = { .path = "show terminal", .short_help = "Show current session terminal settings", .function = unix_cli_show_terminal, }; /* *INDENT-ON* */ /** CLI command to display a list of CLI sessions. */ static clib_error_t * unix_cli_show_cli_sessions (vlib_main_t * vm, unformat_input_t * input, vlib_cli_command_t * cmd) { unix_cli_main_t *cm = &unix_cli_main; clib_file_main_t *fm = &file_main; unix_cli_file_t *cf; clib_file_t *uf; vlib_node_t *n; vlib_cli_output (vm, "%-5s %-5s %-20s %s", "PNI", "FD", "Name", "Flags"); #define fl(x, y) ( (x) ? toupper((y)) : tolower((y)) ) /* *INDENT-OFF* */ pool_foreach (cf, cm->cli_file_pool, ({ uf = pool_elt_at_index (fm->file_pool, cf->clib_file_index); n = vlib_get_node (vm, cf->process_node_index); vlib_cli_output (vm, "%-5d %-5d %-20v %c%c%c%c%c\n", cf->process_node_index, uf->file_descriptor, n->name, fl (cf->is_interactive, 'i'), fl (cf->is_socket, 's'), fl (cf->line_mode, 'l'), fl (cf->has_epipe, 'p'), fl (cf->ansi_capable, 'a')); })); /* *INDENT-ON* */ #undef fl return 0; } /*? * Displays a summary of all the current CLI sessions. * * Typically used to diagnose connection issues with the CLI * socket. * * @cliexpar * @cliexstart{show cli-sessions} * PNI FD Name Flags * 343 0 unix-cli-stdin IslpA * 344 7 unix-cli-local:20 ISlpA * 346 8 unix-cli-local:21 iSLpa * @cliexend * In this example we have the debug console of the running process * on stdin/out, we have an interactive socket session and we also * have a non-interactive socket session. * * Fields: * * - @em PNI: Process node index. * - @em FD: Unix file descriptor. * - @em Name: Name of the session. * - @em Flags: Various flags that describe the state of the session. * * @em Flags have the following meanings; lower-case typically negates * upper-case: * * - @em I Interactive session. * - @em S Connected by socket. * - @em s Not a socket, likely stdin. * - @em L Line-by-line mode. * - @em l Char-by-char mode. * - @em P EPIPE detected on connection; it will close soon. * - @em A ANSI-capable terminal. ?*/ /* *INDENT-OFF* */ VLIB_CLI_COMMAND (cli_unix_cli_show_cli_sessions, static) = { .path = "show cli-sessions", .short_help = "Show current CLI sessions", .function = unix_cli_show_cli_sessions, }; /* *INDENT-ON* */ /** CLI command to set terminal pager settings. */ static clib_error_t * unix_cli_set_terminal_pager (vlib_main_t * vm, unformat_input_t * input, vlib_cli_command_t * cmd) { unix_main_t *um = &unix_main; unix_cli_main_t *cm = &unix_cli_main; unix_cli_file_t *cf; unformat_input_t _line_input, *line_input = &_line_input; clib_error_t *error = 0; cf = pool_elt_at_index (cm->cli_file_pool, cm->current_input_file_index); if (!cf->is_interactive) return clib_error_return (0, "invalid for non-interactive sessions"); if (!unformat_user (input, unformat_line_input, line_input)) return 0; while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT) { if (unformat (line_input, "on")) cf->no_pager = 0; else if (unformat (line_input, "off")) cf->no_pager = 1; else if (unformat (line_input, "limit %u", &um->cli_pager_buffer_limit)) vlib_cli_output (vm, "Pager limit set to %u lines; note, this is global.\n", um->cli_pager_buffer_limit); else { error = clib_error_return (0, "unknown parameter: `%U`", format_unformat_error, line_input); goto done; } } done: unformat_free (line_input); return error; } /*? * Enables or disables the terminal pager for this session. Generally * this defaults to enabled. * * Additionally allows the pager buffer size to be set; though note that * this value is set globally and not per session. ?*/ /* *INDENT-OFF* */ VLIB_CLI_COMMAND (cli_unix_cli_set_terminal_pager, static) = { .path = "set terminal pager", .short_help = "set terminal pager [on|off] [limit ]", .function = unix_cli_set_terminal_pager, }; /* *INDENT-ON* */ /** CLI command to set terminal history settings. */ static clib_error_t * unix_cli_set_terminal_history (vlib_main_t * vm, unformat_input_t * input, vlib_cli_command_t * cmd) { unix_cli_main_t *cm = &unix_cli_main; unix_cli_file_t *cf; unformat_input_t _line_input, *line_input = &_line_input; u32 limit; clib_error_t *error = 0; cf = pool_elt_at_index (cm->cli_file_pool, cm->current_input_file_index); if (!cf->is_interactive) return clib_error_return (0, "invalid for non-interactive sessions"); if (!unformat_user (input, unformat_line_input, line_input)) return 0; while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT) { if (unformat (line_input, "on")) cf->has_history = 1; else if (unformat (line_input, "off")) cf->has_history = 0; else if (unformat (line_input, "limit %u", &cf->history_limit)) ; else { error = clib_error_return (0, "unknown parameter: `%U`", format_unformat_error, line_input); goto done; } /* If we reduced history size, or turned it off, purge the history */ limit = cf->has_history ? cf->history_limit : 0; while (cf->command_history && vec_len (cf->command_history) >= limit) { vec_free (cf->command_history[0]); vec_delete (cf->command_history, 1, 0); } } done: unformat_free (line_input); return error; } /*? * Enables or disables the command history function of the current * terminal. Generally this defaults to enabled. * * This command also allows the maximum size of the history buffer for * this session to be altered. ?*/ /* *INDENT-OFF* */ VLIB_CLI_COMMAND (cli_unix_cli_set_terminal_history, static) = { .path = "set terminal history", .short_help = "set terminal history [on|off] [limit ]", .function = unix_cli_set_terminal_history, }; /* *INDENT-ON* */ /** CLI command to set terminal ANSI settings. */ static clib_error_t * unix_cli_set_terminal_ansi (vlib_main_t * vm, unformat_input_t * input, vlib_cli_command_t * cmd) { unix_cli_main_t *cm = &unix_cli_main; unix_cli_file_t *cf; cf = pool_elt_at_index (cm->cli_file_pool, cm->current_input_file_index); if (!cf->is_interactive) return clib_error_return (0, "invalid for non-interactive sessions"); if (unformat (input, "on")) cf->ansi_capable = 1; else if (unformat (input, "off")) cf->ansi_capable = 0; else return clib_error_return (0, "unknown parameter: `%U`", format_unformat_error, input); return 0; } /*? * Enables or disables the use of ANSI control sequences by this terminal. * The default will vary based on terminal detection at the start of the * session. * * ANSI control sequences are used in a small number of places to provide, * for example, color text output and to control the cursor in the pager. ?*/ /* *INDENT-OFF* */ VLIB_CLI_COMMAND (cli_unix_cli_set_terminal_ansi, static) = { .path = "set terminal ansi", .short_help = "set terminal ansi [on|off]", .function = unix_cli_set_terminal_ansi, }; /* *INDENT-ON* */ static clib_error_t * unix_cli_init (vlib_main_t * vm) { return 0; } VLIB_INIT_FUNCTION (unix_cli_init); /* * fd.io coding-style-patch-verification: ON * * Local Variables: * eval: (c-set-style "gnu") * End: */