aboutsummaryrefslogtreecommitdiffstats
path: root/src/vnet/span/span.c
blob: b7292cf1e800d8ea55b45180ee9cb79d0a354563 (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
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
/*
 * 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 <vlib/vlib.h>
#include <vppinfra/error.h>
#include <vnet/feature/feature.h>
#include <vnet/l2/l2_input.h>
#include <vnet/l2/l2_output.h>

#include <vnet/span/span.h>

span_main_t span_main;

typedef enum
{
  SPAN_DISABLE = 0,
  SPAN_RX = 1,
  SPAN_TX = 2,
  SPAN_BOTH = SPAN_RX | SPAN_TX
} span_state_t;

static_always_inline u32
span_dst_set (span_mirror_t * sm, u32 dst_sw_if_index, int enable)
{
  if (dst_sw_if_index == ~0)
    {
      ASSERT (enable == 0);
      clib_bitmap_zero (sm->mirror_ports);
    }
  else
    sm->mirror_ports =
      clib_bitmap_set (sm->mirror_ports, dst_sw_if_index, enable);

  u32 last = sm->num_mirror_ports;
  sm->num_mirror_ports = clib_bitmap_count_set_bits (sm->mirror_ports);
  return last;
}

int
span_add_delete_entry (vlib_main_t * vm,
		       u32 src_sw_if_index, u32 dst_sw_if_index, u8 state,
		       span_feat_t sf)
{
  span_main_t *sm = &span_main;

  if (state > SPAN_BOTH)
    return VNET_API_ERROR_UNIMPLEMENTED;

  if ((src_sw_if_index == ~0) || (dst_sw_if_index == ~0 && state > 0)
      || (src_sw_if_index == dst_sw_if_index))
    return VNET_API_ERROR_INVALID_INTERFACE;

  vec_validate_aligned (sm->interfaces, src_sw_if_index,
			CLIB_CACHE_LINE_BYTES);

  span_interface_t *si = vec_elt_at_index (sm->interfaces, src_sw_if_index);

  int rx = ! !(state & SPAN_RX);
  int tx = ! !(state & SPAN_TX);

  span_mirror_t *rxm = &si->mirror_rxtx[sf][VLIB_RX];
  span_mirror_t *txm = &si->mirror_rxtx[sf][VLIB_TX];

  u32 last_rx_ports_count = span_dst_set (rxm, dst_sw_if_index, rx);
  u32 last_tx_ports_count = span_dst_set (txm, dst_sw_if_index, tx);

  int enable_rx = last_rx_ports_count == 0 && rxm->num_mirror_ports == 1;
  int disable_rx = last_rx_ports_count > 0 && rxm->num_mirror_ports == 0;
  int enable_tx = last_tx_ports_count == 0 && txm->num_mirror_ports == 1;
  int disable_tx = last_tx_ports_count > 0 && txm->num_mirror_ports == 0;

  switch (sf)
    {
    case SPAN_FEAT_DEVICE:
      if (enable_rx || disable_rx)
	vnet_feature_enable_disable ("device-input", "span-input",
				     src_sw_if_index, rx, 0, 0);
      if (enable_tx || disable_tx)
	vnet_feature_enable_disable ("interface-output", "span-output",
				     src_sw_if_index, tx, 0, 0);
      break;
    case SPAN_FEAT_L2:
      if (enable_rx || disable_rx)
	l2input_intf_bitmap_enable (src_sw_if_index, L2INPUT_FEAT_SPAN, rx);
      if (enable_tx || disable_tx)
	l2output_intf_bitmap_enable (src_sw_if_index, L2OUTPUT_FEAT_SPAN, tx);
      break;
    default:
      return VNET_API_ERROR_UNIMPLEMENTED;
    }

  if (dst_sw_if_index != ~0 && dst_sw_if_index > sm->max_sw_if_index)
    sm->max_sw_if_index = dst_sw_if_index;

  return 0;
}

static uword
unformat_span_state (unformat_input_t * input, va_list * args)
{
  span_state_t *state = va_arg (*args, span_state_t *);
  if (unformat (input, "disable"))
    *state = SPAN_DISABLE;
  else if (unformat (input, "rx"))
    *state = SPAN_RX;
  else if (unformat (input, "tx"))
    *state = SPAN_TX;
  else if (unformat (input, "both"))
    *state = SPAN_BOTH;
  else
    return 0;
  return 1;
}

static clib_error_t *
set_interface_span_command_fn (vlib_main_t * vm,
			       unformat_input_t * input,
			       vlib_cli_command_t * cmd)
{
  span_main_t *sm = &span_main;
  u32 src_sw_if_index = ~0;
  u32 dst_sw_if_index = ~0;
  span_feat_t sf = SPAN_FEAT_DEVICE;
  span_state_t state = SPAN_BOTH;
  int state_set = 0;

  while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
    {
      if (unformat (input, "%U", unformat_vnet_sw_interface,
		    sm->vnet_main, &src_sw_if_index))
	;
      else if (unformat (input, "destination %U", unformat_vnet_sw_interface,
			 sm->vnet_main, &dst_sw_if_index))
	;
      else if (unformat (input, "%U", unformat_span_state, &state))
	{
	  if (state_set)
	    return clib_error_return (0, "Multiple mirror states in input");
	  state_set = 1;
	}
      else if (unformat (input, "l2"))
	sf = SPAN_FEAT_L2;
      else
	return clib_error_return (0, "Invalid input");
    }

  int rv =
    span_add_delete_entry (vm, src_sw_if_index, dst_sw_if_index, state, sf);
  if (rv == VNET_API_ERROR_INVALID_INTERFACE)
    return clib_error_return (0, "Invalid interface");
  return 0;
}

/* *INDENT-OFF* */
VLIB_CLI_COMMAND (set_interface_span_command, static) = {
  .path = "set interface span",
  .short_help = "set interface span <if-name> [l2] {disable | destination <if-name> [both|rx|tx]}",
  .function = set_interface_span_command_fn,
};
/* *INDENT-ON* */

static clib_error_t *
show_interfaces_span_command_fn (vlib_main_t * vm,
				 unformat_input_t * input,
				 vlib_cli_command_t * cmd)
{
  span_main_t *sm = &span_main;
  span_interface_t *si;
  vnet_main_t *vnm = &vnet_main;
  u8 header = 1;
  static const char *states[] = {
    [SPAN_DISABLE] = "none",
    [SPAN_RX] = "rx",
    [SPAN_TX] = "tx",
    [SPAN_BOTH] = "both"
  };
  u8 *s = 0;

  /* *INDENT-OFF* */
  vec_foreach (si, sm->interfaces)
  {
  span_mirror_t * drxm = &si->mirror_rxtx[SPAN_FEAT_DEVICE][VLIB_RX];
  span_mirror_t * dtxm = &si->mirror_rxtx[SPAN_FEAT_DEVICE][VLIB_TX];

  span_mirror_t * lrxm = &si->mirror_rxtx[SPAN_FEAT_L2][VLIB_RX];
  span_mirror_t * ltxm = &si->mirror_rxtx[SPAN_FEAT_L2][VLIB_TX];

    if (drxm->num_mirror_ports || dtxm->num_mirror_ports ||
        lrxm->num_mirror_ports || ltxm->num_mirror_ports)
      {
	u32 i;
	clib_bitmap_t *d = clib_bitmap_dup_or (drxm->mirror_ports, dtxm->mirror_ports);
	clib_bitmap_t *l = clib_bitmap_dup_or (lrxm->mirror_ports, ltxm->mirror_ports);
	clib_bitmap_t *b = clib_bitmap_dup_or (d, l);
	if (header)
	  {
	    vlib_cli_output (vm, "%-20s %-20s  %6s   %6s", "Source", "Destination",
			     "Device", "L2");
	    header = 0;
	  }
	s = format (s, "%U", format_vnet_sw_if_index_name, vnm,
		    si - sm->interfaces);
	clib_bitmap_foreach (i, b, (
	  {
	    int device = (clib_bitmap_get (drxm->mirror_ports, i) +
		         clib_bitmap_get (dtxm->mirror_ports, i) * 2);
	    int l2 = (clib_bitmap_get (lrxm->mirror_ports, i) +
		      clib_bitmap_get (ltxm->mirror_ports, i) * 2);

	    vlib_cli_output (vm, "%-20v %-20U (%6s) (%6s)", s,
			     format_vnet_sw_if_index_name, vnm, i,
			     states[device], states[l2]);
	    vec_reset_length (s);
	  }));
	clib_bitmap_free (b);
	clib_bitmap_free (l);
	clib_bitmap_free (d);
      }
      }
  /* *INDENT-ON* */
  vec_free (s);
  return 0;
}

/* *INDENT-OFF* */
VLIB_CLI_COMMAND (show_interfaces_span_command, static) = {
  .path = "show interface span",
  .short_help = "Shows SPAN mirror table",
  .function = show_interfaces_span_command_fn,
};
/* *INDENT-ON* */

/*
 * fd.io coding-style-patch-verification: ON
 *
 * Local Variables:
 * eval: (c-set-style "gnu")
 * End:
 */
s="p">[IP].src) self.assertEqual(ip.dst, saved_packet[IP].dst) self.assertEqual(udp.sport, saved_packet[UDP].sport) self.assertEqual(udp.dport, saved_packet[UDP].dport) except: self.logger.error(ppp("Unexpected or invalid packet:", packet)) raise for i in self.pg_interfaces: remaining_packet = self.get_next_packet_info_for_interface2( i, dst_sw_if_index, last_info[i.sw_if_index]) self.assertTrue( remaining_packet is None, "Port %u: Packet expected from source %u didn't arrive" % (dst_sw_if_index, i.sw_if_index)) def run_l2bd_test(self, pkts_per_burst): """ L2BD MAC learning test """ # Create incoming packet streams for packet-generator interfaces for i in self.pg_interfaces: packet_sizes = self.sub_if_packet_sizes if hasattr(i, 'sub_if') \ else self.pg_if_packet_sizes pkts = self.create_stream(i, packet_sizes, pkts_per_burst) i.add_stream(pkts) # Enable packet capture and start packet sending self.pg_enable_capture(self.pg_interfaces) self.pg_start() # Verify outgoing packet streams per packet-generator interface for i in self.pg_interfaces: capture = i.get_capture() self.logger.info("Verifying capture on interface %s" % i.name) self.verify_capture(i, capture) def test_l2bd_sl(self): """ L2BD MAC learning single-loop test Test scenario: 1.config MAC learning enabled learn 100 MAC entries 3 interfaces: untagged, dot1q, dot1ad (dot1q used instead of dot1ad in the first version) 2.sending l2 eth pkts between 3 interface 64B, 512B, 1518B, 9200B (ether_size) burst of 2 pkts per interface """ self.run_l2bd_test(self.sl_pkts_per_burst) def test_l2bd_dl(self): """ L2BD MAC learning dual-loop test Test scenario: 1.config MAC learning enabled learn 100 MAC entries 3 interfaces: untagged, dot1q, dot1ad (dot1q used instead of dot1ad in the first version) 2.sending l2 eth pkts between 3 interface 64B, 512B, 1518B, 9200B (ether_size) burst of 257 pkts per interface """ self.run_l2bd_test(self.dl_pkts_per_burst) if __name__ == '__main__': unittest.main(testRunner=VppTestRunner)