aboutsummaryrefslogtreecommitdiffstats
path: root/src/vnet/bfd/bfd_cli.c
blob: 1d100b077eb4f7639841640f6cecb266f6f4b86a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
/*
 * 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.
 */


#include <vnet/vnet.h>
#include <vnet/mfib/mfib_signal.h>
#include <vppinfra/dlist.h>

/**
 * @brief Pool of signals
 */
static mfib_signal_t *mfib_signal_pool;

/**
 * @brief pool of dlist elements
 */
static dlist_elt_t *mfib_signal_dlist_pool;

/**
 * the list/set of interfaces with signals pending
 */
typedef struct mfib_signal_q_t_
{
    /**
     * the dlist indext that is the head of the list
     */
    u32 mip_head;

    /**
     * Spin lock to protect the list
     */
    int mip_lock;
} mfib_signal_q_t;

/**
 * @brief The pending queue of signals to deliver to the control plane
 */
static mfib_signal_q_t mfib_signal_pending ;

static void
mfib_signal_list_init (void)
{
    dlist_elt_t *head;
    u32 hi;

    pool_get(mfib_signal_dlist_pool, head);
    hi = head - mfib_signal_dlist_pool;

    mfib_signal_pending.mip_head = hi;
    clib_dlist_init(mfib_signal_dlist_pool, hi);
}

void
mfib_signal_module_init (void)
{
    mfib_signal_list_init();
}

static inline void
mfib_signal_lock_aquire (void)
{
    while (clib_atomic_test_and_set (&mfib_signal_pending.mip_lock))
        ;
}

static inline void
mfib_signal_lock_release (void)
{
  clib_atomic_release(&mfib_signal_pending.mip_lock);
}

#define MFIB_SIGNAL_CRITICAL_SECTION(_body) \
{                                           \
    mfib_signal_lock_aquire();              \
    do {                                    \
        _body;                              \
    } while (0);                            \
    mfib_signal_lock_release();             \
}

int
mfib_signal_send_one (struct vl_api_registration_ *reg,
                      u32 context)
{
    u32 li, si;

    /*
     * with the lock held, pop a signal from the q.
     */
    MFIB_SIGNAL_CRITICAL_SECTION(
    ({
        li = clib_dlist_remove_head(mfib_signal_dlist_pool,
                                    mfib_signal_pending.mip_head);
    }));

    if (~0 != li)
    {
        mfib_signal_t *mfs;
        mfib_itf_t *mfi;
        dlist_elt_t *elt;

        elt = pool_elt_at_index(mfib_signal_dlist_pool, li);
        si = elt->value;

        mfs = pool_elt_at_index(mfib_signal_pool, si);
        mfi = mfib_itf_get(mfs->mfs_itf);
        mfi->mfi_si = INDEX_INVALID;
        clib_atomic_fetch_and(&mfi->mfi_flags,
			      ~MFIB_ITF_FLAG_SIGNAL_PRESENT);


        vl_mfib_signal_send_one(reg, context, mfs);

        /*
         * with the lock held, return the resoruces of the signals posted
         */
        MFIB_SIGNAL_CRITICAL_SECTION(
        ({
            pool_put_index(mfib_signal_pool, si);
            pool_put_index(mfib_signal_dlist_pool, li);
        }));

        return (1);
    }
    return (0);
}

void
mfib_signal_push (const mfib_entry_t *mfe,
                  mfib_itf_t *mfi,
                  vlib_buffer_t *b0)
{
    mfib_signal_t *mfs;
    dlist_elt_t *elt;
    u32 si, li;

    MFIB_SIGNAL_CRITICAL_SECTION(
    ({
        pool_get(mfib_signal_pool, mfs);
        pool_get(mfib_signal_dlist_pool, elt);

        si = mfs - mfib_signal_pool;
        li = elt - mfib_signal_dlist_pool;

        elt->value = si;
        mfi->mfi_si = li;

        clib_dlist_addhead(mfib_signal_dlist_pool,
                           mfib_signal_pending.mip_head,
                           li);
    }));

    mfs->mfs_entry = mfib_entry_get_index(mfe);
    mfs->mfs_itf = mfib_itf_get_index(mfi);

    if (NULL != b0)
    {
        mfs->mfs_buffer_len = b0->current_length;
        memcpy(mfs->mfs_buffer,
               vlib_buffer_get_current(b0),
               (mfs->mfs_buffer_len > MFIB_SIGNAL_BUFFER_SIZE ?
                MFIB_SIGNAL_BUFFER_SIZE :
                mfs->mfs_buffer_len));
    }
    else
    {
        mfs->mfs_buffer_len = 0;
    }
}

void
mfib_signal_remove_itf (const mfib_itf_t *mfi)
{
    u32 li;

    /*
     * lock the queue to prevent further additions while we fiddle.
     */
    li = mfi->mfi_si;

    if (INDEX_INVALID != li)
    {
        /*
         * it's in the pending q
         */
        MFIB_SIGNAL_CRITICAL_SECTION(
        ({
            dlist_elt_t *elt;

            /*
             * with the lock held;
             *  - remove the signal from the pending list
             *  - free up the signal and list entry obejcts
             */
            clib_dlist_remove(mfib_signal_dlist_pool, li);

            elt = pool_elt_at_index(mfib_signal_dlist_pool, li);
            pool_put_index(mfib_signal_pool, elt->value);
            pool_put(mfib_signal_dlist_pool, elt);
        }));
    }
}
f='#n891'>891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981
/*
 * Copyright (c) 2011-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 BFD CLI implementation
 */

#include <vlib/vlib.h>
#include <vlib/cli.h>
#include <vppinfra/format.h>
#include <vppinfra/warnings.h>
#include <vnet/api_errno.h>
#include <vnet/ip/format.h>
#include <vnet/bfd/bfd_api.h>
#include <vnet/bfd/bfd_main.h>

static u8 *
format_bfd_session_cli (u8 * s, va_list * args)
{
  vlib_main_t *vm = va_arg (*args, vlib_main_t *);
  bfd_session_t *bs = va_arg (*args, bfd_session_t *);
  switch (bs->transport)
    {
    case BFD_TRANSPORT_UDP4:
      s = format (s, "%=10u %-32s %20U %20U\n", bs->bs_idx, "IPv4 address",
		  format_ip4_address, bs->udp.key.local_addr.ip4.as_u8,
		  format_ip4_address, bs->udp.key.peer_addr.ip4.as_u8);
      break;
    case BFD_TRANSPORT_UDP6:
      s = format (s, "%=10u %-32s %20U %20U\n", bs->bs_idx, "IPv6 address",
		  format_ip6_address, &bs->udp.key.local_addr.ip6,
		  format_ip6_address, &bs->udp.key.peer_addr.ip6);
      break;
    }
  s = format (s, "%10s %-32s %20s %20s\n", "", "Session state",
	      bfd_state_string (bs->local_state),
	      bfd_state_string (bs->remote_state));
  s = format (s, "%10s %-32s %20s %20s\n", "", "Diagnostic code",
	      bfd_diag_code_string (bs->local_diag),
	      bfd_diag_code_string (bs->remote_diag));
  s = format (s, "%10s %-32s %20u %20u\n", "", "Detect multiplier",
	      bs->local_detect_mult, bs->remote_detect_mult);
  s = format (s, "%10s %-32s %20u %20llu\n", "",
	      "Required Min Rx Interval (usec)",
	      bs->config_required_min_rx_usec, bs->remote_min_rx_usec);
  s = format (s, "%10s %-32s %20u %20u\n", "",
	      "Desired Min Tx Interval (usec)",
	      bs->config_desired_min_tx_usec,
	      bfd_nsec_to_usec (bs->remote_desired_min_tx_nsec));
  s =
    format (s, "%10s %-32s %20u\n", "", "Transmit interval",
	    bfd_nsec_to_usec (bs->transmit_interval_nsec));
  u64 now = clib_cpu_time_now ();
  u8 *tmp = NULL;
  if (bs->last_tx_nsec > 0)
    {
      tmp = format (tmp, "%.2fs ago", (now - bs->last_tx_nsec) *
		    vm->clib_time.seconds_per_clock);
      s = format (s, "%10s %-32s %20v\n", "", "Last control frame tx", tmp);
      vec_reset_length (tmp);
    }
  if (bs->last_rx_nsec)
    {
      tmp = format (tmp, "%.2fs ago", (now - bs->last_rx_nsec) *
		    vm->clib_time.seconds_per_clock);
      s = format (s, "%10s %-32s %20v\n", "", "Last control frame rx", tmp);
      vec_reset_length (tmp);
    }
  s =
    format (s, "%10s %-32s %20u %20llu\n", "", "Min Echo Rx Interval (usec)",
	    1, bs->remote_min_echo_rx_usec);
  if (bs->echo)
    {
      s =
	format (s, "%10s %-32s %20u\n", "", "Echo transmit interval",
		bfd_nsec_to_usec (bs->echo_transmit_interval_nsec));
      tmp =
	format (tmp, "%.2fs ago",
		(now -
		 bs->echo_last_tx_nsec) * vm->clib_time.seconds_per_clock);
      s = format (s, "%10s %-32s %20v\n", "", "Last echo frame tx", tmp);
      vec_reset_length (tmp);
      tmp = format (tmp, "%.6fs",
		    (bs->echo_last_rx_nsec - bs->echo_last_tx_nsec) *
		    vm->clib_time.seconds_per_clock);
      s =
	format (s, "%10s %-32s %20v\n", "", "Last echo frame roundtrip time",
		tmp);
    }
  vec_free (tmp);
  tmp = NULL;
  s = format (s, "%10s %-32s %20s %20s\n", "", "Demand mode", "no",
	      bs->remote_demand ? "yes" : "no");
  s = format (s, "%10s %-32s %20s\n", "", "Poll state",
	      bfd_poll_state_string (bs->poll_state));
  if (bs->auth.curr_key)
    {
      s = format (s, "%10s %-32s %20u\n", "", "Authentication config key ID",
		  bs->auth.curr_key->conf_key_id);
      s = format (s, "%10s %-32s %20u\n", "", "Authentication BFD key ID",
		  bs->auth.curr_bfd_key_id);
      s = format (s, "%10s %-32s %20u %20u\n", "", "Sequence number",
		  bs->auth.local_seq_number, bs->auth.remote_seq_number);
    }
  return s;
}

static clib_error_t *
show_bfd (vlib_main_t * vm, unformat_input_t * input,
	  CLIB_UNUSED (vlib_cli_command_t * lmd))
{
  bfd_main_t *bm = &bfd_main;
  bfd_session_t *bs = NULL;
  unformat_input_t _line_input, *line_input = &_line_input;

  /* Get a line of input. */
  if (!unformat_user (input, unformat_line_input, line_input))
    return 0;

  if (unformat (line_input, "keys"))
    {
      bfd_auth_key_t *key = NULL;
      u8 *s = format (NULL, "%=10s %=25s %=10s\n", "Configuration Key ID",
		      "Type", "Use Count");
      /* *INDENT-OFF* */
      pool_foreach (key, bm->auth_keys) {
        s = format (s, "%10u %-25s %10u\n", key->conf_key_id,
                    bfd_auth_type_str (key->auth_type), key->use_count);
      }
      /* *INDENT-ON* */
      vlib_cli_output (vm, "%v\n", s);
      vec_free (s);
      vlib_cli_output (vm, "Number of configured BFD keys: %lu\n",
		       (u64) pool_elts (bm->auth_keys));
    }
  else if (unformat (line_input, "sessions"))
    {
      u8 *s = format (NULL, "%=10s %=32s %=20s %=20s\n", "Index", "Property",
		      "Local value", "Remote value");
      /* *INDENT-OFF* */
      pool_foreach (bs, bm->sessions) {
        s = format (s, "%U", format_bfd_session_cli, vm, bs);
      }
      /* *INDENT-ON* */
      vlib_cli_output (vm, "%v", s);
      vec_free (s);
      vlib_cli_output (vm, "Number of configured BFD sessions: %lu\n",
		       (u64) pool_elts (bm->sessions));
    }
  else if (unformat (line_input, "echo-source"))
    {
      int is_set;
      u32 sw_if_index;
      int have_usable_ip4;
      ip4_address_t ip4;
      int have_usable_ip6;
      ip6_address_t ip6;
      bfd_udp_get_echo_source (&is_set, &sw_if_index, &have_usable_ip4, &ip4,
			       &have_usable_ip6, &ip6);
      if (is_set)
	{
	  vnet_sw_interface_t *sw_if =
	    vnet_get_sw_interface_or_null (&vnet_main, sw_if_index);
	  vnet_hw_interface_t *hw_if =
	    vnet_get_hw_interface (&vnet_main, sw_if->hw_if_index);
	  u8 *s = format (NULL, "UDP echo source is: %v\n", hw_if->name);
	  s = format (s, "IPv4 address usable as echo source: ");
	  if (have_usable_ip4)
	    {
	      s = format (s, "%U\n", format_ip4_address, &ip4);
	    }
	  else
	    {
	      s = format (s, "none\n");
	    }
	  s = format (s, "IPv6 address usable as echo source: ");
	  if (have_usable_ip6)
	    {
	      s = format (s, "%U\n", format_ip6_address, &ip6);
	    }
	  else
	    {
	      s = format (s, "none\n");
	    }
	  vlib_cli_output (vm, "%v", s);
	  vec_free (s);
	}
      else
	{
	  vlib_cli_output (vm, "UDP echo source is not set.\n");
	}
    }
  else
    {
      vlib_cli_output (vm, "Number of configured BFD sessions: %lu\n",
		       (u64) pool_elts (bm->sessions));
      vlib_cli_output (vm, "Number of configured BFD keys: %lu\n",
		       (u64) pool_elts (bm->auth_keys));
    }
  return 0;
}

/* *INDENT-OFF* */
VLIB_CLI_COMMAND (show_bfd_command, static) = {
  .path = "show bfd",
  .short_help = "show bfd [keys|sessions|echo-source]",
  .function = show_bfd,
};
/* *INDENT-ON* */

static clib_error_t *
bfd_cli_key_add (vlib_main_t * vm, unformat_input_t * input,
		 CLIB_UNUSED (vlib_cli_command_t * lmd))
{
  clib_error_t *ret = NULL;
  int have_key_id = 0;
  u32 key_id = 0;
  u8 *vec_auth_type = NULL;
  bfd_auth_type_e auth_type = BFD_AUTH_TYPE_reserved;
  u8 *secret = NULL;
  unformat_input_t _line_input, *line_input = &_line_input;
  static const u8 keyed_sha1[] = "keyed-sha1";
  static const u8 meticulous_keyed_sha1[] = "meticulous-keyed-sha1";

  /* Get a line of input. */
  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, "conf-key-id %u", &key_id))
	{
	  have_key_id = 1;
	}
      else if (unformat (line_input, "type %U", unformat_token, "a-zA-Z0-9-",
			 &vec_auth_type))
	{
	  if (vec_len (vec_auth_type) == sizeof (keyed_sha1) - 1 &&
	      0 == memcmp (vec_auth_type, keyed_sha1,
			   sizeof (keyed_sha1) - 1))
	    {
	      auth_type = BFD_AUTH_TYPE_keyed_sha1;
	    }
	  else if (vec_len (vec_auth_type) ==
		   sizeof (meticulous_keyed_sha1) - 1 &&
		   0 == memcmp (vec_auth_type, meticulous_keyed_sha1,
				sizeof (meticulous_keyed_sha1) - 1))
	    {
	      auth_type = BFD_AUTH_TYPE_meticulous_keyed_sha1;
	    }
	  else
	    {
	      ret = clib_error_return (0, "invalid type `%v'", vec_auth_type);
	      goto out;
	    }
	}
      else
	if (unformat (line_input, "secret %U", unformat_hex_string, &secret))
	{
	  /* nothing to do here */
	}
      else
	{
	  ret = clib_error_return (0, "Unknown input `%U'",
				   format_unformat_error, line_input);
	  goto out;
	}
    }

  if (!have_key_id)
    {
      ret =
	clib_error_return (0, "required parameter missing: `conf-key-id'");
      goto out;
    }
  if (!vec_auth_type)
    {
      ret = clib_error_return (0, "required parameter missing: `type'");
      goto out;
    }
  if (!secret)
    {
      ret = clib_error_return (0, "required parameter missing: `secret'");
      goto out;
    }

  vnet_api_error_t rv =
    bfd_auth_set_key (key_id, auth_type, vec_len (secret), secret);
  if (rv)
    {
      ret =
	clib_error_return (0, "`bfd_auth_set_key' API call failed, rv=%d:%U",
			   (int) rv, format_vnet_api_errno, rv);
    }

out:
  vec_free (vec_auth_type);
  return ret;
}

/* *INDENT-OFF* */
VLIB_CLI_COMMAND (bfd_cli_key_add_command, static) = {
  .path = "bfd key set",
  .short_help = "bfd key set"
                " conf-key-id <id>"
                " type <keyed-sha1|meticulous-keyed-sha1> "
                " secret <secret>",
  .function = bfd_cli_key_add,
};
/* *INDENT-ON* */

static clib_error_t *
bfd_cli_key_del (vlib_main_t * vm, unformat_input_t * input,
		 CLIB_UNUSED (vlib_cli_command_t * lmd))
{
  clib_error_t *ret = NULL;
  u32 key_id = 0;
  unformat_input_t _line_input, *line_input = &_line_input;

  /* Get a line of input. */
  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, "conf-key-id %u", &key_id))
	{
	  ret = clib_error_return (0, "Unknown input `%U'",
				   format_unformat_error, line_input);
	  goto out;
	}
    }

  vnet_api_error_t rv = bfd_auth_del_key (key_id);
  if (rv)
    {
      ret =
	clib_error_return (0, "`bfd_auth_del_key' API call failed, rv=%d:%U",
			   (int) rv, format_vnet_api_errno, rv);
    }

out:
  return ret;
}

/* *INDENT-OFF* */
VLIB_CLI_COMMAND (bfd_cli_key_del_command, static) = {
  .path = "bfd key del",
  .short_help = "bfd key del conf-key-id <id>",
  .function = bfd_cli_key_del,
};
/* *INDENT-ON* */

#define INTERFACE_STR "interface"
#define LOCAL_ADDR_STR "local-addr"
#define PEER_ADDR_STR "peer-addr"
#define CONF_KEY_ID_STR "conf-key-id"
#define BFD_KEY_ID_STR "bfd-key-id"
#define DESIRED_MIN_TX_STR "desired-min-tx"
#define REQUIRED_MIN_RX_STR "required-min-rx"
#define DETECT_MULT_STR "detect-mult"
#define ADMIN_STR "admin"
#define DELAYED_STR "delayed"

static const unsigned mandatory = 1;
static const unsigned optional = 0;

#define DECLARE(t, n, s, r, ...) \
  int have_##n = 0;              \
  t n;

#define UNFORMAT(t, n, s, r, ...)              \
  if (unformat (line_input, s " " __VA_ARGS__, &n)) \
    {                                          \
      something_parsed = 1;                    \
      have_##n = 1;                            \
    }

#define CHECK_MANDATORY(t, n, s, r, ...)                                  \
WARN_OFF(tautological-compare)                                            \
  if (mandatory == r && !have_##n)                                        \
    {                                                                     \
      WARN_ON(tautological-compare)                                       \
      ret = clib_error_return (0, "Required parameter `%s' missing.", s); \
      goto out;                                                           \
    }

static clib_error_t *
bfd_cli_udp_session_add (vlib_main_t * vm, unformat_input_t * input,
			 CLIB_UNUSED (vlib_cli_command_t * lmd))
{
  clib_error_t *ret = NULL;
  unformat_input_t _line_input, *line_input = &_line_input;
#define foreach_bfd_cli_udp_session_add_cli_param(F)              \
  F (u32, sw_if_index, INTERFACE_STR, mandatory, "%U",            \
     unformat_vnet_sw_interface, &vnet_main)                      \
  F (ip46_address_t, local_addr, LOCAL_ADDR_STR, mandatory, "%U", \
     unformat_ip46_address)                                       \
  F (ip46_address_t, peer_addr, PEER_ADDR_STR, mandatory, "%U",   \
     unformat_ip46_address)                                       \
  F (u32, desired_min_tx, DESIRED_MIN_TX_STR, mandatory, "%u")    \
  F (u32, required_min_rx, REQUIRED_MIN_RX_STR, mandatory, "%u")  \
  F (u32, detect_mult, DETECT_MULT_STR, mandatory, "%u")          \
  F (u32, conf_key_id, CONF_KEY_ID_STR, optional, "%u")           \
  F (u32, bfd_key_id, BFD_KEY_ID_STR, optional, "%u")

  foreach_bfd_cli_udp_session_add_cli_param (DECLARE);

  /* Get a line of input. */
  if (!unformat_user (input, unformat_line_input, line_input))
    return 0;

  while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
    {
      int something_parsed = 0;
      foreach_bfd_cli_udp_session_add_cli_param (UNFORMAT);

      if (!something_parsed)
	{
	  ret = clib_error_return (0, "Unknown input `%U'",
				   format_unformat_error, line_input);
	  goto out;
	}
    }

  foreach_bfd_cli_udp_session_add_cli_param (CHECK_MANDATORY);

  if (1 == have_conf_key_id + have_bfd_key_id)
    {
      ret = clib_error_return (0, "Incompatible parameter combination, `%s' "
			       "and `%s' must be either both specified or none",
			       CONF_KEY_ID_STR, BFD_KEY_ID_STR);
      goto out;
    }

  if (detect_mult > 255)
    {
      ret = clib_error_return (0, "%s value `%u' out of range <1,255>",
			       DETECT_MULT_STR, detect_mult);
      goto out;
    }

  if (have_bfd_key_id && bfd_key_id > 255)
    {
      ret = clib_error_return (0, "%s value `%u' out of range <1,255>",
			       BFD_KEY_ID_STR, bfd_key_id);
      goto out;
    }

  vnet_api_error_t rv =
    bfd_udp_add_session (sw_if_index, &local_addr, &peer_addr, desired_min_tx,
			 required_min_rx,
			 detect_mult, have_conf_key_id, conf_key_id,
			 bfd_key_id);
  if (rv)
    {
      ret =
	clib_error_return (0,
			   "`bfd_add_add_session' API call failed, rv=%d:%U",
			   (int) rv, format_vnet_api_errno, rv);
      goto out;
    }

out:
  return ret;
}

/* *INDENT-OFF* */
VLIB_CLI_COMMAND (bfd_cli_udp_session_add_command, static) = {
  .path = "bfd udp session add",
  .short_help = "bfd udp session add"
                " interface <interface>"
                " local-addr <local-address>"
                " peer-addr <peer-address>"
                " desired-min-tx <desired min tx interval>"
                " required-min-rx <required min rx interval>"
                " detect-mult <detect multiplier> "
                "["
                " conf-key-id <config key ID>"
                " bfd-key-id <BFD key ID>"
                "]",
  .function = bfd_cli_udp_session_add,
};
/* *INDENT-ON* */

static clib_error_t *
bfd_cli_udp_session_mod (vlib_main_t * vm, unformat_input_t * input,
			 CLIB_UNUSED (vlib_cli_command_t * lmd))
{
  clib_error_t *ret = NULL;
  unformat_input_t _line_input, *line_input = &_line_input;
#define foreach_bfd_cli_udp_session_mod_cli_param(F)              \
  F (u32, sw_if_index, INTERFACE_STR, mandatory, "%U",            \
     unformat_vnet_sw_interface, &vnet_main)                      \
  F (ip46_address_t, local_addr, LOCAL_ADDR_STR, mandatory, "%U", \
     unformat_ip46_address)                                       \
  F (ip46_address_t, peer_addr, PEER_ADDR_STR, mandatory, "%U",   \
     unformat_ip46_address)                                       \
  F (u32, desired_min_tx, DESIRED_MIN_TX_STR, mandatory, "%u")    \
  F (u32, required_min_rx, REQUIRED_MIN_RX_STR, mandatory, "%u")  \
  F (u32, detect_mult, DETECT_MULT_STR, mandatory, "%u")

  foreach_bfd_cli_udp_session_mod_cli_param (DECLARE);

  /* Get a line of input. */
  if (!unformat_user (input, unformat_line_input, line_input))
    return 0;

  while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
    {
      int something_parsed = 0;
      foreach_bfd_cli_udp_session_mod_cli_param (UNFORMAT);

      if (!something_parsed)
	{
	  ret = clib_error_return (0, "Unknown input `%U'",
				   format_unformat_error, line_input);
	  goto out;
	}
    }

  foreach_bfd_cli_udp_session_mod_cli_param (CHECK_MANDATORY);

  if (detect_mult > 255)
    {
      ret = clib_error_return (0, "%s value `%u' out of range <1,255>",
			       DETECT_MULT_STR, detect_mult);
      goto out;
    }

  vnet_api_error_t rv =
    bfd_udp_mod_session (sw_if_index, &local_addr, &peer_addr,
			 desired_min_tx, required_min_rx, detect_mult);
  if (rv)
    {
      ret =
	clib_error_return (0,
			   "`bfd_udp_mod_session' API call failed, rv=%d:%U",
			   (int) rv, format_vnet_api_errno, rv);
      goto out;
    }

out:
  return ret;
}

/* *INDENT-OFF* */
VLIB_CLI_COMMAND (bfd_cli_udp_session_mod_command, static) = {
  .path = "bfd udp session mod",
  .short_help = "bfd udp session mod interface"
                " <interface> local-addr"
                " <local-address> peer-addr"
                " <peer-address> desired-min-tx"
                " <desired min tx interval> required-min-rx"
                " <required min rx interval> detect-mult"
                " <detect multiplier> ",
  .function = bfd_cli_udp_session_mod,
};
/* *INDENT-ON* */

static clib_error_t *
bfd_cli_udp_session_del (vlib_main_t * vm, unformat_input_t * input,
			 CLIB_UNUSED (vlib_cli_command_t * lmd))
{
  clib_error_t *ret = NULL;
  unformat_input_t _line_input, *line_input = &_line_input;
#define foreach_bfd_cli_udp_session_del_cli_param(F)              \
  F (u32, sw_if_index, INTERFACE_STR, mandatory, "%U",            \
     unformat_vnet_sw_interface, &vnet_main)                      \
  F (ip46_address_t, local_addr, LOCAL_ADDR_STR, mandatory, "%U", \
     unformat_ip46_address)                                       \
  F (ip46_address_t, peer_addr, PEER_ADDR_STR, mandatory, "%U",   \
     unformat_ip46_address)

  foreach_bfd_cli_udp_session_del_cli_param (DECLARE);

  /* Get a line of input. */
  if (!unformat_user (input, unformat_line_input, line_input))
    return 0;

  while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
    {
      int something_parsed = 0;
      foreach_bfd_cli_udp_session_del_cli_param (UNFORMAT);

      if (!something_parsed)
	{
	  ret = clib_error_return (0, "Unknown input `%U'",
				   format_unformat_error, line_input);
	  goto out;
	}
    }

  foreach_bfd_cli_udp_session_del_cli_param (CHECK_MANDATORY);

  vnet_api_error_t rv =
    bfd_udp_del_session (sw_if_index, &local_addr, &peer_addr);
  if (rv)
    {
      ret =
	clib_error_return (0,
			   "`bfd_udp_del_session' API call failed, rv=%d:%U",
			   (int) rv, format_vnet_api_errno, rv);
      goto out;
    }

out:
  return ret;
}

/* *INDENT-OFF* */
VLIB_CLI_COMMAND (bfd_cli_udp_session_del_command, static) = {
  .path = "bfd udp session del",
  .short_help = "bfd udp session del interface"
                " <interface> local-addr"
                " <local-address> peer-addr"
                "<peer-address> ",
  .function = bfd_cli_udp_session_del,
};
/* *INDENT-ON* */

static clib_error_t *
bfd_cli_udp_session_set_flags (vlib_main_t * vm, unformat_input_t * input,
			       CLIB_UNUSED (vlib_cli_command_t * lmd))
{
  clib_error_t *ret = NULL;
  unformat_input_t _line_input, *line_input = &_line_input;
#define foreach_bfd_cli_udp_session_set_flags_cli_param(F)        \
  F (u32, sw_if_index, INTERFACE_STR, mandatory, "%U",            \
     unformat_vnet_sw_interface, &vnet_main)                      \
  F (ip46_address_t, local_addr, LOCAL_ADDR_STR, mandatory, "%U", \
     unformat_ip46_address)                                       \
  F (ip46_address_t, peer_addr, PEER_ADDR_STR, mandatory, "%U",   \
     unformat_ip46_address)                                       \
  F (u8 *, admin_up_down_token, ADMIN_STR, mandatory, "%v",       \
     &admin_up_down_token)

  foreach_bfd_cli_udp_session_set_flags_cli_param (DECLARE);

  /* Get a line of input. */
  if (!unformat_user (input, unformat_line_input, line_input))
    return 0;

  while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
    {
      int something_parsed = 0;
      foreach_bfd_cli_udp_session_set_flags_cli_param (UNFORMAT);

      if (!something_parsed)
	{
	  ret = clib_error_return (0, "Unknown input `%U'",
				   format_unformat_error, line_input);
	  goto out;
	}
    }

  foreach_bfd_cli_udp_session_set_flags_cli_param (CHECK_MANDATORY);

  u8 admin_up_down;
  static const char up[] = "up";
  static const char down[] = "down";
  if (!memcmp (admin_up_down_token, up, sizeof (up) - 1))
    {
      admin_up_down = 1;
    }
  else if (!memcmp (admin_up_down_token, down, sizeof (down) - 1))
    {
      admin_up_down = 0;
    }
  else
    {
      ret =
	clib_error_return (0, "Unrecognized value for `%s' parameter: `%v'",
			   ADMIN_STR, admin_up_down_token);
      goto out;
    }
  vnet_api_error_t rv =
    bfd_udp_session_set_flags (vm, sw_if_index, &local_addr,
			       &peer_addr, admin_up_down);
  if (rv)
    {
      ret =
	clib_error_return (0,
			   "`bfd_udp_session_set_flags' API call failed, rv=%d:%U",
			   (int) rv, format_vnet_api_errno, rv);
      goto out;
    }

out:
  return ret;
}

/* *INDENT-OFF* */
VLIB_CLI_COMMAND (bfd_cli_udp_session_set_flags_command, static) = {
  .path = "bfd udp session set-flags",
  .short_help = "bfd udp session set-flags"
                " interface <interface>"
                " local-addr <local-address>"
                " peer-addr <peer-address>"
                " admin <up|down>",
  .function = bfd_cli_udp_session_set_flags,
};
/* *INDENT-ON* */

static clib_error_t *
bfd_cli_udp_session_auth_activate (vlib_main_t * vm,
				   unformat_input_t * input,
				   CLIB_UNUSED (vlib_cli_command_t * lmd))
{
  clib_error_t *ret = NULL;
  unformat_input_t _line_input, *line_input = &_line_input;
#define foreach_bfd_cli_udp_session_auth_activate_cli_param(F)    \
  F (u32, sw_if_index, INTERFACE_STR, mandatory, "%U",            \
     unformat_vnet_sw_interface, &vnet_main)                      \
  F (ip46_address_t, local_addr, LOCAL_ADDR_STR, mandatory, "%U", \
     unformat_ip46_address)                                       \
  F (ip46_address_t, peer_addr, PEER_ADDR_STR, mandatory, "%U",   \
     unformat_ip46_address)                                       \
  F (u8 *, delayed_token, DELAYED_STR, optional, "%v")            \
  F (u32, conf_key_id, CONF_KEY_ID_STR, mandatory, "%u")          \
  F (u32, bfd_key_id, BFD_KEY_ID_STR, mandatory, "%u")

  foreach_bfd_cli_udp_session_auth_activate_cli_param (DECLARE);

  /* Get a line of input. */
  if (!unformat_user (input, unformat_line_input, line_input))
    return 0;

  while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
    {
      int something_parsed = 0;
      foreach_bfd_cli_udp_session_auth_activate_cli_param (UNFORMAT);

      if (!something_parsed)
	{
	  ret = clib_error_return (0, "Unknown input `%U'",
				   format_unformat_error, line_input);
	  goto out;
	}
    }

  foreach_bfd_cli_udp_session_auth_activate_cli_param (CHECK_MANDATORY);

  u8 is_delayed = 0;
  if (have_delayed_token)
    {
      static const char yes[] = "yes";
      static const char no[] = "no";
      if (!memcmp (delayed_token, yes, sizeof (yes) - 1))
	{
	  is_delayed = 1;
	}
      else if (!memcmp (delayed_token, no, sizeof (no) - 1))
	{
	  is_delayed = 0;
	}
      else
	{
	  ret =
	    clib_error_return (0,
			       "Unrecognized value for `%s' parameter: `%v'",
			       DELAYED_STR, delayed_token);
	  goto out;
	}
    }

  if (have_bfd_key_id && bfd_key_id > 255)
    {
      ret = clib_error_return (0, "%s value `%u' out of range <1,255>",
			       BFD_KEY_ID_STR, bfd_key_id);
      goto out;
    }

  vnet_api_error_t rv =
    bfd_udp_auth_activate (sw_if_index, &local_addr, &peer_addr, conf_key_id,
			   bfd_key_id, is_delayed);
  if (rv)
    {
      ret =
	clib_error_return (0,
			   "`bfd_udp_auth_activate' API call failed, rv=%d:%U",
			   (int) rv, format_vnet_api_errno, rv);
      goto out;
    }

out:
  return ret;
}

/* *INDENT-OFF* */
VLIB_CLI_COMMAND (bfd_cli_udp_session_auth_activate_command, static) = {
  .path = "bfd udp session auth activate",
  .short_help = "bfd udp session auth activate"
                " interface <interface>"
                " local-addr <local-address>"
                " peer-addr <peer-address>"
                " conf-key-id <config key ID>"
                " bfd-key-id <BFD key ID>"
                " [ delayed <yes|no> ]",
  .function = bfd_cli_udp_session_auth_activate,
};

static clib_error_t *
bfd_cli_udp_session_auth_deactivate (vlib_main_t *vm, unformat_input_t *input,
                                     CLIB_UNUSED (vlib_cli_command_t *lmd))
{
  clib_error_t *ret = NULL;
  unformat_input_t _line_input, *line_input = &_line_input;
#define foreach_bfd_cli_udp_session_auth_deactivate_cli_param(F)  \
  F (u32, sw_if_index, INTERFACE_STR, mandatory, "%U",            \
     unformat_vnet_sw_interface, &vnet_main)                      \
  F (ip46_address_t, local_addr, LOCAL_ADDR_STR, mandatory, "%U", \
     unformat_ip46_address)                                       \
  F (ip46_address_t, peer_addr, PEER_ADDR_STR, mandatory, "%U",   \
     unformat_ip46_address)                                       \
  F (u8 *, delayed_token, DELAYED_STR, optional, "%v")

  foreach_bfd_cli_udp_session_auth_deactivate_cli_param (DECLARE);

  /* Get a line of input. */
  if (!unformat_user (input, unformat_line_input, line_input))
    return 0;

  while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
    {
      int something_parsed = 0;
      foreach_bfd_cli_udp_session_auth_deactivate_cli_param (UNFORMAT);

      if (!something_parsed)
        {
          ret = clib_error_return (0, "Unknown input `%U'",
                                   format_unformat_error, input);
          goto out;
        }
    }

  foreach_bfd_cli_udp_session_auth_deactivate_cli_param (CHECK_MANDATORY);

  u8 is_delayed = 0;
  if (have_delayed_token)
    {
      static const char yes[] = "yes";
      static const char no[] = "no";
      if (!memcmp (delayed_token, yes, sizeof (yes) - 1))
        {
          is_delayed = 1;
        }
      else if (!memcmp (delayed_token, no, sizeof (no) - 1))
        {
          is_delayed = 0;
        }
      else
        {
          ret = clib_error_return (
              0, "Unrecognized value for `%s' parameter: `%v'", DELAYED_STR,
              delayed_token);
          goto out;
        }
    }

  vnet_api_error_t rv = bfd_udp_auth_deactivate (sw_if_index, &local_addr,
                                                 &peer_addr, is_delayed);
  if (rv)
    {
      ret = clib_error_return (
          0, "`bfd_udp_auth_deactivate' API call failed, rv=%d:%U", (int)rv,
          format_vnet_api_errno, rv);
      goto out;
    }

out:
  return ret;
}

/* *INDENT-OFF* */
VLIB_CLI_COMMAND (bfd_cli_udp_session_auth_deactivate_command, static) = {
  .path = "bfd udp session auth deactivate",
  .short_help = "bfd udp session auth deactivate"
                " interface <interface>"
                " local-addr <local-address>"
                " peer-addr <peer-address>"
                "[ delayed <yes|no> ]",
  .function = bfd_cli_udp_session_auth_deactivate,
};
/* *INDENT-ON* */

static clib_error_t *
bfd_cli_udp_set_echo_source (vlib_main_t * vm, unformat_input_t * input,
			     CLIB_UNUSED (vlib_cli_command_t * lmd))
{
  clib_error_t *ret = NULL;
  unformat_input_t _line_input, *line_input = &_line_input;
#define foreach_bfd_cli_udp_set_echo_source_cli_param(F) \
  F (u32, sw_if_index, INTERFACE_STR, mandatory, "%U",   \
     unformat_vnet_sw_interface, &vnet_main)

  foreach_bfd_cli_udp_set_echo_source_cli_param (DECLARE);

  /* Get a line of input. */
  if (!unformat_user (input, unformat_line_input, line_input))
    return 0;

  while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
    {
      int something_parsed = 0;
      foreach_bfd_cli_udp_set_echo_source_cli_param (UNFORMAT);

      if (!something_parsed)
	{
	  ret = clib_error_return (0, "Unknown input `%U'",
				   format_unformat_error, line_input);
	  goto out;
	}
    }

  foreach_bfd_cli_udp_set_echo_source_cli_param (CHECK_MANDATORY);

  vnet_api_error_t rv = bfd_udp_set_echo_source (sw_if_index);
  if (rv)
    {
      ret =
	clib_error_return (0,
			   "`bfd_udp_set_echo_source' API call failed, rv=%d:%U",
			   (int) rv, format_vnet_api_errno, rv);
      goto out;
    }

out:
  return ret;
}

/* *INDENT-OFF* */
VLIB_CLI_COMMAND (bfd_cli_udp_set_echo_source_cmd, static) = {
  .path = "bfd udp echo-source set",
  .short_help = "bfd udp echo-source set interface <interface>",
  .function = bfd_cli_udp_set_echo_source,
};
/* *INDENT-ON* */

static clib_error_t *
bfd_cli_udp_del_echo_source (vlib_main_t * vm, unformat_input_t * input,
			     CLIB_UNUSED (vlib_cli_command_t * lmd))
{
  vnet_api_error_t rv = bfd_udp_del_echo_source ();
  if (rv)
    {
      return clib_error_return (0,
				"`bfd_udp_del_echo_source' API call failed, rv=%d:%U",
				(int) rv, format_vnet_api_errno, rv);
    }

  return 0;
}

/* *INDENT-OFF* */
VLIB_CLI_COMMAND (bfd_cli_udp_del_echo_source_cmd, static) = {
  .path = "bfd udp echo-source del",
  .short_help = "bfd udp echo-source del",
  .function = bfd_cli_udp_del_echo_source,
};
/* *INDENT-ON* */

/*
 * fd.io coding-style-patch-verification: ON
 *
 * Local Variables:
 * eval: (c-set-style "gnu")
 * End:
 */