FD.io VPP  v16.06
Vector Packet Processing
l2_learn.c
Go to the documentation of this file.
1 /*
2  * l2_learn.c : layer 2 learning using l2fib
3  *
4  * Copyright (c) 2013 Cisco and/or its affiliates.
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at:
8  *
9  * http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */
17 
18 #include <vlib/vlib.h>
19 #include <vnet/vnet.h>
20 #include <vnet/pg/pg.h>
21 #include <vnet/ethernet/ethernet.h>
22 #include <vlib/cli.h>
23 
24 #include <vnet/l2/l2_input.h>
25 #include <vnet/l2/feat_bitmap.h>
26 #include <vnet/l2/l2_fib.h>
27 #include <vnet/l2/l2_learn.h>
28 
29 #include <vppinfra/error.h>
30 #include <vppinfra/hash.h>
31 
32 /*
33  * Ethernet bridge learning
34  *
35  * Populate the mac table with entries mapping the packet's source mac + bridge
36  * domain ID to the input sw_if_index.
37  *
38  * Note that learning and forwarding are separate graph nodes. This means that
39  * for a set of packets, all learning is performed first, then all nodes are
40  * forwarded. The forwarding is done based on the end-state of the mac table,
41  * instead of the state after each packet. Thus the forwarding results could
42  * differ in certain cases (mac move tests), but this not expected to cause
43  * problems in real-world networks. It is much simpler to separate learning
44  * and forwarding into separate nodes.
45  */
46 
47 
48 typedef struct {
49  u8 src[6];
50  u8 dst[6];
54 
55 
56 /* packet trace format function */
57 static u8 * format_l2learn_trace (u8 * s, va_list * args)
58 {
59  CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
60  CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
61  l2learn_trace_t * t = va_arg (*args, l2learn_trace_t *);
62 
63  s = format (s, "l2-learn: sw_if_index %d dst %U src %U bd_index %d",
64  t->sw_if_index,
67  t->bd_index);
68  return s;
69 }
70 
72 
73 #define foreach_l2learn_error \
74 _(L2LEARN, "L2 learn packets") \
75 _(MISS, "L2 learn misses") \
76 _(MAC_MOVE, "L2 mac moves") \
77 _(MAC_MOVE_VIOLATE, "L2 mac move violations") \
78 _(LIMIT, "L2 not learned due to limit") \
79 _(HIT, "L2 learn hits") \
80 _(FILTER_DROP, "L2 filter mac drops")
81 
82 typedef enum {
83 #define _(sym,str) L2LEARN_ERROR_##sym,
85 #undef _
88 
89 static char * l2learn_error_strings[] = {
90 #define _(sym,string) string,
92 #undef _
93 };
94 
95 typedef enum {
100 
101 
102 // Perform learning on one packet based on the mac table lookup result
103 
106  l2learn_main_t * msm,
107  u64 * counter_base,
108  vlib_buffer_t * b0,
109  u32 sw_if_index0,
110  l2fib_entry_key_t * key0,
111  l2fib_entry_key_t * cached_key,
112  u32 * bucket0,
113  l2fib_entry_result_t * result0,
114  u32 * next0)
115 {
116  u32 feature_bitmap;
117 
118  // Set up the default next node (typically L2FWD)
119 
120  // Remove ourself from the feature bitmap
121  feature_bitmap = vnet_buffer(b0)->l2.feature_bitmap & ~L2INPUT_FEAT_LEARN;
122 
123  // Save for next feature graph nodes
124  vnet_buffer(b0)->l2.feature_bitmap = feature_bitmap;
125 
126  // Determine the next node
128  feature_bitmap);
129 
130  // Check mac table lookup result
131 
132  if (PREDICT_TRUE (result0->fields.sw_if_index == sw_if_index0)) {
133  // The entry was in the table, and the sw_if_index matched, the normal case
134 
135  // TODO: for dataplane learning and aging, do this:
136  // if refresh=0 and not a static mac, set refresh=1
137  counter_base[L2LEARN_ERROR_HIT] += 1;
138 
139  } else if (result0->raw == ~0) {
140 
141  // The entry was not in table, so add it
142 
143  counter_base[L2LEARN_ERROR_MISS] += 1;
144 
145  if (msm->global_learn_count == msm->global_learn_limit) {
146  // Global limit reached. Do not learn the mac but forward the packet.
147  // In the future, limits could also be per-interface or bridge-domain.
148  counter_base[L2LEARN_ERROR_LIMIT] += 1;
149  goto done;
150 
151  } else {
152  BVT(clib_bihash_kv) kv;
153  // It is ok to learn
154 
155  result0->raw = 0; // clear all fields
156  result0->fields.sw_if_index = sw_if_index0;
157  // TODO: set timestamp in entry to clock for dataplane aging
158  kv.key = key0->raw;
159  kv.value = result0->raw;
160 
161  BV(clib_bihash_add_del) (msm->mac_table, &kv, 1 /* is_add */);
162 
163  cached_key->raw = ~0; // invalidate the cache
164  msm->global_learn_count++;
165  }
166 
167  } else {
168 
169  // The entry was in the table, but with the wrong sw_if_index mapping (mac move)
170  counter_base[L2LEARN_ERROR_MAC_MOVE] += 1;
171 
172  if (result0->fields.static_mac) {
173  // Don't overwrite a static mac
174  // TODO: Check violation policy. For now drop the packet
175  b0->error = node->errors[L2LEARN_ERROR_MAC_MOVE_VIOLATE];
176  *next0 = L2LEARN_NEXT_DROP;
177  } else {
178  // Update the entry
179  // TODO: may want to rate limit mac moves
180  // TODO: check global/bridge domain/interface learn limits
181  BVT(clib_bihash_kv) kv;
182 
183  result0->raw = 0; // clear all fields
184  result0->fields.sw_if_index = sw_if_index0;
185 
186  kv.key = key0->raw;
187  kv.value = result0->raw;
188 
189  cached_key->raw = ~0; // invalidate the cache
190 
191  BV(clib_bihash_add_del) (msm->mac_table, &kv, 1 /* is_add */);
192  }
193  }
194 
195  if (result0->fields.filter) {
196  // drop packet because lookup matched a filter mac entry
197 
198  if (*next0 != L2LEARN_NEXT_DROP) {
199  // if we're not already dropping the packet, do it now
200  b0->error = node->errors[L2LEARN_ERROR_FILTER_DROP];
201  *next0 = L2LEARN_NEXT_DROP;
202  }
203  }
204 
205 done:
206  return;
207 }
208 
209 
210 static uword
212  vlib_node_runtime_t * node,
213  vlib_frame_t * frame)
214 {
215  u32 n_left_from, * from, * to_next;
216  l2learn_next_t next_index;
217  l2learn_main_t * msm = &l2learn_main;
218  vlib_node_t *n = vlib_get_node (vm, l2learn_node.index);
219  u32 node_counter_base_index = n->error_heap_index;
220  vlib_error_main_t * em = &vm->error_main;
221  l2fib_entry_key_t cached_key;
222  l2fib_entry_result_t cached_result;
223 
224  from = vlib_frame_vector_args (frame);
225  n_left_from = frame->n_vectors; /* number of packets to process */
226  next_index = node->cached_next_index;
227 
228  // Clear the one-entry cache in case mac table was updated
229  cached_key.raw = ~0;
230  cached_result.raw = ~0; /* warning be gone */
231 
232  while (n_left_from > 0)
233  {
234  u32 n_left_to_next;
235 
236  /* get space to enqueue frame to graph node "next_index" */
237  vlib_get_next_frame (vm, node, next_index,
238  to_next, n_left_to_next);
239 
240  while (n_left_from >= 4 && n_left_to_next >= 2)
241  {
242  u32 bi0, bi1;
243  vlib_buffer_t * b0, * b1;
244  u32 next0, next1;
245  u32 sw_if_index0, sw_if_index1;
246  ethernet_header_t * h0, * h1;
247  l2fib_entry_key_t key0, key1;
248  l2fib_entry_result_t result0, result1;
249  u32 bucket0, bucket1;
250 
251  /* Prefetch next iteration. */
252  {
253  vlib_buffer_t * p2, * p3;
254 
255  p2 = vlib_get_buffer (vm, from[2]);
256  p3 = vlib_get_buffer (vm, from[3]);
257 
258  vlib_prefetch_buffer_header (p2, LOAD);
259  vlib_prefetch_buffer_header (p3, LOAD);
260 
263  }
264 
265  /* speculatively enqueue b0 and b1 to the current next frame */
266  /* bi is "buffer index", b is pointer to the buffer */
267  to_next[0] = bi0 = from[0];
268  to_next[1] = bi1 = from[1];
269  from += 2;
270  to_next += 2;
271  n_left_from -= 2;
272  n_left_to_next -= 2;
273 
274  b0 = vlib_get_buffer (vm, bi0);
275  b1 = vlib_get_buffer (vm, bi1);
276 
277  /* RX interface handles */
278  sw_if_index0 = vnet_buffer(b0)->sw_if_index[VLIB_RX];
279  sw_if_index1 = vnet_buffer(b1)->sw_if_index[VLIB_RX];
280 
281  /* Process 2 x pkts */
282 
283  h0 = vlib_buffer_get_current (b0);
284  h1 = vlib_buffer_get_current (b1);
285 
287  {
288  if (b0->flags & VLIB_BUFFER_IS_TRACED)
289  {
290  l2learn_trace_t *t =
291  vlib_add_trace (vm, node, b0, sizeof (*t));
292  t->sw_if_index = sw_if_index0;
293  t->bd_index = vnet_buffer(b0)->l2.bd_index;
294  clib_memcpy(t->src, h0->src_address, 6);
295  clib_memcpy(t->dst, h0->dst_address, 6);
296  }
297  if (b1->flags & VLIB_BUFFER_IS_TRACED)
298  {
299  l2learn_trace_t *t =
300  vlib_add_trace (vm, node, b1, sizeof (*t));
301  t->sw_if_index = sw_if_index1;
302  t->bd_index = vnet_buffer(b1)->l2.bd_index;
303  clib_memcpy(t->src, h1->src_address, 6);
304  clib_memcpy(t->dst, h1->dst_address, 6);
305  }
306  }
307 
308  /* process 2 pkts */
309  em->counters[node_counter_base_index + L2LEARN_ERROR_L2LEARN] += 2;
310 
311  l2fib_lookup_2 (msm->mac_table, &cached_key, &cached_result,
312  h0->src_address,
313  h1->src_address,
314  vnet_buffer(b0)->l2.bd_index,
315  vnet_buffer(b1)->l2.bd_index,
316  &key0,
317  &key1,
318  &bucket0,
319  &bucket1,
320  &result0,
321  &result1);
322 
323  l2learn_process (node, msm, &em->counters[node_counter_base_index],
324  b0, sw_if_index0, &key0, &cached_key,
325  &bucket0, &result0, &next0);
326 
327  l2learn_process (node, msm, &em->counters[node_counter_base_index],
328  b1, sw_if_index1, &key1, &cached_key,
329  &bucket1, &result1, &next1);
330 
331  /* verify speculative enqueues, maybe switch current next frame */
332  /* if next0==next1==next_index then nothing special needs to be done */
333  vlib_validate_buffer_enqueue_x2 (vm, node, next_index,
334  to_next, n_left_to_next,
335  bi0, bi1, next0, next1);
336  }
337 
338  while (n_left_from > 0 && n_left_to_next > 0)
339  {
340  u32 bi0;
341  vlib_buffer_t * b0;
342  u32 next0;
343  u32 sw_if_index0;
344  ethernet_header_t * h0;
345  l2fib_entry_key_t key0;
346  l2fib_entry_result_t result0;
347  u32 bucket0;
348 
349  /* speculatively enqueue b0 to the current next frame */
350  bi0 = from[0];
351  to_next[0] = bi0;
352  from += 1;
353  to_next += 1;
354  n_left_from -= 1;
355  n_left_to_next -= 1;
356 
357  b0 = vlib_get_buffer (vm, bi0);
358 
359  sw_if_index0 = vnet_buffer(b0)->sw_if_index[VLIB_RX];
360 
361  h0 = vlib_buffer_get_current (b0);
362 
364  && (b0->flags & VLIB_BUFFER_IS_TRACED))) {
365  l2learn_trace_t *t =
366  vlib_add_trace (vm, node, b0, sizeof (*t));
367  t->sw_if_index = sw_if_index0;
368  t->bd_index = vnet_buffer(b0)->l2.bd_index;
369  clib_memcpy(t->src, h0->src_address, 6);
370  clib_memcpy(t->dst, h0->dst_address, 6);
371  }
372 
373  /* process 1 pkt */
374  em->counters[node_counter_base_index + L2LEARN_ERROR_L2LEARN] += 1;
375 
376  l2fib_lookup_1 (msm->mac_table, &cached_key, &cached_result,
377  h0->src_address, vnet_buffer(b0)->l2.bd_index,
378  &key0,
379  &bucket0,
380  &result0);
381 
382  l2learn_process (node, msm, &em->counters[node_counter_base_index],
383  b0, sw_if_index0, &key0, &cached_key,
384  &bucket0, &result0, &next0);
385 
386  /* verify speculative enqueue, maybe switch current next frame */
387  vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
388  to_next, n_left_to_next,
389  bi0, next0);
390  }
391 
392  vlib_put_next_frame (vm, node, next_index, n_left_to_next);
393  }
394 
395  return frame->n_vectors;
396 }
397 
398 
399 VLIB_REGISTER_NODE (l2learn_node,static) = {
400  .function = l2learn_node_fn,
401  .name = "l2-learn",
402  .vector_size = sizeof (u32),
403  .format_trace = format_l2learn_trace,
405 
406  .n_errors = ARRAY_LEN(l2learn_error_strings),
407  .error_strings = l2learn_error_strings,
408 
409  .n_next_nodes = L2LEARN_N_NEXT,
410 
411  /* edit / add dispositions here */
412  .next_nodes = {
413  [L2LEARN_NEXT_DROP] = "error-drop",
414  [L2LEARN_NEXT_L2FWD] = "l2-fwd",
415  },
416 };
417 
418 
420 {
422 
423  mp->vlib_main = vm;
424  mp->vnet_main = vnet_get_main();
425 
426  // Initialize the feature next-node indexes
428  l2learn_node.index,
432 
433  /* init the hash table ptr */
434  mp->mac_table = get_mac_table();
435 
436  // Set the default number of dynamically learned macs to the number
437  // of buckets.
439 
440  return 0;
441 }
442 
444 
445 
446 // set subinterface learn enable/disable
447 // The CLI format is:
448 // set interface l2 learn <interface> [disable]
449 static clib_error_t *
451  unformat_input_t * input,
452  vlib_cli_command_t * cmd)
453 {
454  vnet_main_t * vnm = vnet_get_main();
455  clib_error_t * error = 0;
456  u32 sw_if_index;
457  u32 enable;
458 
459  if (! unformat_user (input, unformat_vnet_sw_interface, vnm, &sw_if_index))
460  {
461  error = clib_error_return (0, "unknown interface `%U'",
462  format_unformat_error, input);
463  goto done;
464  }
465 
466  enable = 1;
467  if (unformat (input, "disable")) {
468  enable = 0;
469  }
470 
471  // set the interface flag
472  l2input_intf_bitmap_enable(sw_if_index, L2INPUT_FEAT_LEARN, enable);
473 
474  done:
475  return error;
476 }
477 
478 VLIB_CLI_COMMAND (int_learn_cli, static) = {
479  .path = "set interface l2 learn",
480  .short_help = "set interface l2 learn <interface> [disable]",
481  .function = int_learn,
482 };
483 
484 
485 static clib_error_t *
487 {
489 
491  {
492  if (unformat (input, "limit %d", &mp->global_learn_limit))
493  ;
494 
495  else
496  return clib_error_return (0, "unknown input `%U'",
497  format_unformat_error, input);
498  }
499 
500  return 0;
501 }
502 
504 
void vlib_put_next_frame(vlib_main_t *vm, vlib_node_runtime_t *r, u32 next_index, u32 n_vectors_left)
Definition: main.c:459
u32 error_heap_index
Definition: node.h:244
u32 global_learn_count
Definition: l2_learn.h:31
#define CLIB_UNUSED(x)
Definition: clib.h:79
uword unformat(unformat_input_t *i, char *fmt,...)
Definition: unformat.c:942
always_inline vlib_node_t * vlib_get_node(vlib_main_t *vm, u32 i)
Definition: node_funcs.h:46
bad routing header type(not 4)") sr_error (NO_MORE_SEGMENTS
#define PREDICT_TRUE(x)
Definition: clib.h:98
#define UNFORMAT_END_OF_INPUT
Definition: format.h:142
u8 src_address[6]
Definition: packet.h:52
struct _vlib_node_registration vlib_node_registration_t
clib_error_t * l2learn_init(vlib_main_t *vm)
Definition: l2_learn.c:419
unformat_function_t unformat_vnet_sw_interface
Definition: l2_fib.h:50
vlib_error_t * errors
Definition: node.h:378
int BV() clib_bihash_add_del(BVT(clib_bihash)*h, BVT(clib_bihash_kv)*add_v, int is_add)
always_inline void * vlib_buffer_get_current(vlib_buffer_t *b)
Get pointer to current data to process.
Definition: buffer.h:184
always_inline uword unformat_check_input(unformat_input_t *i)
Definition: format.h:168
vnet_main_t * vnet_get_main(void)
Definition: misc.c:45
#define static_always_inline
Definition: clib.h:85
u8 * format_ethernet_address(u8 *s, va_list *args)
Definition: format.c:43
#define VLIB_INIT_FUNCTION(x)
Definition: init.h:109
u8 dst_address[6]
Definition: packet.h:51
unsigned long u64
Definition: types.h:89
static_always_inline void l2learn_process(vlib_node_runtime_t *node, l2learn_main_t *msm, u64 *counter_base, vlib_buffer_t *b0, u32 sw_if_index0, l2fib_entry_key_t *key0, l2fib_entry_key_t *cached_key, u32 *bucket0, l2fib_entry_result_t *result0, u32 *next0)
Definition: l2_learn.c:105
uword unformat_user(unformat_input_t *input, unformat_function_t *func,...)
Definition: unformat.c:953
always_inline void * vlib_frame_vector_args(vlib_frame_t *f)
Definition: node_funcs.h:202
static char * l2learn_error_strings[]
Definition: l2_learn.c:89
vlib_error_main_t error_main
Definition: main.h:124
vnet_main_t * vnet_main
Definition: l2_learn.h:41
#define PREDICT_FALSE(x)
Definition: clib.h:97
#define VLIB_CONFIG_FUNCTION(x, n,...)
Definition: init.h:116
#define vlib_validate_buffer_enqueue_x2(vm, node, next_index, to_next, n_left_to_next, bi0, bi1, next0, next1)
Definition: buffer_node.h:43
#define L2FIB_NUM_BUCKETS
Definition: l2_fib.h:27
static u8 * format_l2learn_trace(u8 *s, va_list *args)
Definition: l2_learn.c:57
#define vlib_validate_buffer_enqueue_x1(vm, node, next_index, to_next, n_left_to_next, bi0, next0)
Definition: buffer_node.h:83
#define vlib_get_next_frame(vm, node, next_index, vectors, n_vectors_left)
Definition: node_funcs.h:265
u64 raw
Definition: l2_fib.h:63
vlib_error_t error
Error code for buffers to be enqueued to error handler.
Definition: buffer.h:129
static vlib_node_registration_t l2learn_node
(constructor) VLIB_REGISTER_NODE (l2learn_node)
Definition: l2_learn.c:71
u64 * counters
Definition: error.h:73
u16 n_vectors
Definition: node.h:307
#define CLIB_PREFETCH(addr, size, type)
Definition: cache.h:82
#define foreach_l2learn_error
Definition: l2_learn.c:73
static clib_error_t * l2learn_config(vlib_main_t *vm, unformat_input_t *input)
Definition: l2_learn.c:486
#define clib_memcpy(a, b, c)
Definition: string.h:63
l2learn_next_t
Definition: l2_learn.c:95
static clib_error_t * int_learn(vlib_main_t *vm, unformat_input_t *input, vlib_cli_command_t *cmd)
Definition: l2_learn.c:450
#define ARRAY_LEN(x)
Definition: clib.h:59
u32 feat_next_node_index[32]
Definition: l2_learn.h:37
char ** l2input_get_feat_names(void)
Definition: l2_input.c:45
static_always_inline void l2fib_lookup_2(BVT(clib_bihash)*mac_table, l2fib_entry_key_t *cached_key, l2fib_entry_result_t *cached_result, u8 *mac0, u8 *mac1, u16 bd_index0, u16 bd_index1, l2fib_entry_key_t *key0, l2fib_entry_key_t *key1, u32 *bucket0, u32 *bucket1, l2fib_entry_result_t *result0, l2fib_entry_result_t *result1)
Definition: l2_fib.h:161
struct l2fib_entry_result_t::@128::@130 fields
#define VLIB_CLI_COMMAND(x,...)
Definition: cli.h:150
l2learn_error_t
Definition: l2_learn.c:82
u16 cached_next_index
Definition: node.h:422
unsigned int u32
Definition: types.h:88
u8 * format_unformat_error(u8 *s, va_list *va)
Definition: unformat.c:87
#define vnet_buffer(b)
Definition: buffer.h:300
u8 * format(u8 *s, char *fmt,...)
Definition: format.c:405
Definition: l2_fib.h:33
#define BV(a)
#define VLIB_NODE_FLAG_TRACE
Definition: node.h:225
u32 global_learn_limit
Definition: l2_learn.h:34
static uword l2learn_node_fn(vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *frame)
Definition: l2_learn.c:211
#define VLIB_BUFFER_IS_TRACED
Definition: buffer.h:91
u64 uword
Definition: types.h:112
u32 l2input_intf_bitmap_enable(u32 sw_if_index, u32 feature_bitmap, u32 enable)
Definition: l2_input.c:473
unsigned short u16
Definition: types.h:57
#define BVT(a)
unsigned char u8
Definition: types.h:56
always_inline u32 feat_bitmap_get_next_node_index(u32 *next_nodes, u32 bitmap)
Definition: feat_bitmap.h:71
always_inline void * vlib_add_trace(vlib_main_t *vm, vlib_node_runtime_t *r, vlib_buffer_t *b, u32 n_data_bytes)
Definition: trace_funcs.h:55
vlib_main_t * vlib_main
Definition: l2_learn.h:40
#define vlib_prefetch_buffer_header(b, type)
Prefetch buffer metadata.
Definition: buffer.h:162
#define VLIB_REGISTER_NODE(x,...)
Definition: node.h:140
l2learn_main_t l2learn_main
Definition: l2_learn.h:45
u8 data[0]
Packet data.
Definition: buffer.h:150
static_always_inline void l2fib_lookup_1(BVT(clib_bihash)*mac_table, l2fib_entry_key_t *cached_key, l2fib_entry_result_t *cached_result, u8 *mac0, u16 bd_index0, l2fib_entry_key_t *key0, u32 *bucket0, l2fib_entry_result_t *result0)
Definition: l2_fib.h:118
#define clib_error_return(e, args...)
Definition: error.h:112
struct _unformat_input_t unformat_input_t
#define CLIB_CACHE_LINE_BYTES
Definition: cache.h:67
u32 flags
buffer flags: VLIB_BUFFER_IS_TRACED: trace this buffer.
Definition: buffer.h:84
always_inline vlib_buffer_t * vlib_get_buffer(vlib_main_t *vm, u32 buffer_index)
Translate buffer index into buffer pointer.
Definition: buffer_funcs.h:69
always_inline void feat_bitmap_init_next_nodes(vlib_main_t *vm, u32 node_index, u32 num_features, char **feat_names, u32 *next_nodes)
Definition: feat_bitmap.h:41
Definition: defs.h:45
u64 raw
Definition: l2_fib.h:43