- u32 bi0, bi1, bi2, bi3;
- vlib_buffer_t *b0, *b1, *b2, *b3;
- u32 next0, next1, next2, next3;
- u32 sw_if_index0, sw_if_index1, sw_if_index2, sw_if_index3;
- ethernet_header_t *h0, *h1, *h2, *h3;
- l2_output_config_t *config0, *config1, *config2, *config3;
- u32 feature_bitmap0, feature_bitmap1;
- u32 feature_bitmap2, feature_bitmap3;
-
- /* Prefetch next iteration. */
- {
- vlib_buffer_t *p4, *p5, *p6, *p7;
-
- p4 = vlib_get_buffer (vm, from[4]);
- p5 = vlib_get_buffer (vm, from[5]);
- p6 = vlib_get_buffer (vm, from[6]);
- p7 = vlib_get_buffer (vm, from[7]);
-
- /* Prefetch the buffer header for the N+2 loop iteration */
- vlib_prefetch_buffer_header (p4, LOAD);
- vlib_prefetch_buffer_header (p5, LOAD);
- vlib_prefetch_buffer_header (p6, LOAD);
- vlib_prefetch_buffer_header (p7, LOAD);
- }
-
- /* speculatively enqueue b0 and b1 to the current next frame */
- /* bi is "buffer index", b is pointer to the buffer */
- to_next[0] = bi0 = from[0];
- to_next[1] = bi1 = from[1];
- to_next[2] = bi2 = from[2];
- to_next[3] = bi3 = from[3];
- from += 4;
- to_next += 4;
- n_left_from -= 4;
- n_left_to_next -= 4;
-
- b0 = vlib_get_buffer (vm, bi0);
- b1 = vlib_get_buffer (vm, bi1);
- b2 = vlib_get_buffer (vm, bi2);
- b3 = vlib_get_buffer (vm, bi3);
-
- /* TX interface handles */
- sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_TX];
- sw_if_index1 = vnet_buffer (b1)->sw_if_index[VLIB_TX];
- sw_if_index2 = vnet_buffer (b2)->sw_if_index[VLIB_TX];
- sw_if_index3 = vnet_buffer (b3)->sw_if_index[VLIB_TX];
-
- vlib_node_increment_counter (vm, l2output_node.index,
- L2OUTPUT_ERROR_L2OUTPUT, 4);
-
- /* Get config for the output interface */
- config0 = vec_elt_at_index (msm->configs, sw_if_index0);
- config1 = vec_elt_at_index (msm->configs, sw_if_index1);
- config2 = vec_elt_at_index (msm->configs, sw_if_index2);
- config3 = vec_elt_at_index (msm->configs, sw_if_index3);
-
- /*
- * Get features from the config
- * TODO: mask out any non-applicable features
- */
- feature_bitmap0 = config0->feature_bitmap;
- feature_bitmap1 = config1->feature_bitmap;
- feature_bitmap2 = config2->feature_bitmap;
- feature_bitmap3 = config3->feature_bitmap;
-
- /* Determine next node */
- l2_output_dispatch (b0, node, &cached_sw_if_index,
- &cached_next_index, sw_if_index0,
- feature_bitmap0, &next0);
- l2_output_dispatch (b1, node, &cached_sw_if_index,
- &cached_next_index, sw_if_index1,
- feature_bitmap1, &next1);
- l2_output_dispatch (b2, node, &cached_sw_if_index,
- &cached_next_index, sw_if_index2,
- feature_bitmap2, &next2);
- l2_output_dispatch (b3, node, &cached_sw_if_index,
- &cached_next_index, sw_if_index3,
- feature_bitmap3, &next3);
-
- l2output_vtr (node, config0, feature_bitmap0, b0, &next0);
- l2output_vtr (node, config1, feature_bitmap1, b1, &next1);
- l2output_vtr (node, config2, feature_bitmap2, b2, &next2);
- l2output_vtr (node, config3, feature_bitmap3, b3, &next3);
-
- if (do_trace)
- {
- h0 = vlib_buffer_get_current (b0);
- h1 = vlib_buffer_get_current (b1);
- h2 = vlib_buffer_get_current (b2);
- h3 = vlib_buffer_get_current (b3);
- if (b0->flags & VLIB_BUFFER_IS_TRACED)
- {
- l2output_trace_t *t =
- vlib_add_trace (vm, node, b0, sizeof (*t));
- t->sw_if_index = sw_if_index0;
- clib_memcpy (t->src, h0->src_address, 6);
- clib_memcpy (t->dst, h0->dst_address, 6);
- clib_memcpy (t->raw, &h0->type, sizeof (t->raw));
- }
- if (b1->flags & VLIB_BUFFER_IS_TRACED)
- {
- l2output_trace_t *t =
- vlib_add_trace (vm, node, b1, sizeof (*t));
- t->sw_if_index = sw_if_index1;
- clib_memcpy (t->src, h1->src_address, 6);
- clib_memcpy (t->dst, h1->dst_address, 6);
- clib_memcpy (t->raw, &h1->type, sizeof (t->raw));
- }
- if (b2->flags & VLIB_BUFFER_IS_TRACED)
- {
- l2output_trace_t *t =
- vlib_add_trace (vm, node, b2, sizeof (*t));
- t->sw_if_index = sw_if_index2;
- clib_memcpy (t->src, h2->src_address, 6);
- clib_memcpy (t->dst, h2->dst_address, 6);
- clib_memcpy (t->raw, &h2->type, sizeof (t->raw));
- }
- if (b3->flags & VLIB_BUFFER_IS_TRACED)
- {
- l2output_trace_t *t =
- vlib_add_trace (vm, node, b3, sizeof (*t));
- t->sw_if_index = sw_if_index3;
- clib_memcpy (t->src, h3->src_address, 6);
- clib_memcpy (t->dst, h3->dst_address, 6);
- clib_memcpy (t->raw, &h3->type, sizeof (t->raw));
- }
- }
-
- /*
- * Perform the split horizon check
- * The check can only fail for non-zero shg's
- */
- if (PREDICT_FALSE (config0->shg + config1->shg +
- config2->shg + config3->shg))
- {
- /* one of the checks might fail, check both */
- if (split_horizon_violation
- (config0->shg, vnet_buffer (b0)->l2.shg))
- {
- next0 = L2OUTPUT_NEXT_DROP;
- b0->error = node->errors[L2OUTPUT_ERROR_SHG_DROP];
- }
- if (split_horizon_violation
- (config1->shg, vnet_buffer (b1)->l2.shg))
- {
- next1 = L2OUTPUT_NEXT_DROP;
- b1->error = node->errors[L2OUTPUT_ERROR_SHG_DROP];
- }
- if (split_horizon_violation
- (config2->shg, vnet_buffer (b2)->l2.shg))
- {
- next2 = L2OUTPUT_NEXT_DROP;
- b2->error = node->errors[L2OUTPUT_ERROR_SHG_DROP];
- }
- if (split_horizon_violation
- (config3->shg, vnet_buffer (b3)->l2.shg))
- {
- next3 = L2OUTPUT_NEXT_DROP;
- b3->error = node->errors[L2OUTPUT_ERROR_SHG_DROP];
- }
- }
-
- /* verify speculative enqueues, maybe switch current next frame */
- /* if next0==next1==next_index then nothing special needs to be done */
- vlib_validate_buffer_enqueue_x4 (vm, node, next_index,
- to_next, n_left_to_next,
- bi0, bi1, bi2, bi3,
- next0, next1, next2, next3);