
Before this commit, several output features that happen to be the last in the list of features to be executed, send the packets directly to <interfaceName>-output. To do this, they use l2_output_dispatch, which builds a list of sw_if_index to next index mappings. When interfaces are deleted and the new interfaces are created, these mappings become stale, and cause the packets being sent to wrong interface output nodes. This patch (thanks John Lo for the brilliant idea!) adds a feature node "output", whose sole purpose is dispatching the packets to the correct interface output nodes. To do that, it uses the l2output_main.next_nodes, which is already taken care of for the case of the sw_if_index reuse, so this makes the dependent features all work correctly. Since this changes the packet path, for the features that were always the last ones it has triggered a side problem of the output feat_next_node_index not being properly initalized. These two users are l2-output-classify node and the output nodes belonging to the acl-plugin. For the first one the less invasive fix is just to initialize that field. For the acl-plugin nodes, rewrite the affected part of the code to use feat_bitmap_get_next_node_index since this is essentially what the conditional in l2_output_dispatch does, and fix the compiler warnings generated. Change-Id: If44457b1c1c3e197b78470c08555720d0872c6e5 Signed-off-by: Andrew Yourtchenko <ayourtch@gmail.com>
252 lines
7.3 KiB
C
252 lines
7.3 KiB
C
/*
|
|
* 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.
|
|
*/
|
|
/*
|
|
*------------------------------------------------------------------
|
|
* l2sess.c - simple MAC-swap API / debug CLI handling
|
|
*------------------------------------------------------------------
|
|
*/
|
|
|
|
#include <vnet/vnet.h>
|
|
#include <vnet/plugin/plugin.h>
|
|
#include <acl/l2sess.h>
|
|
|
|
#include <vlibapi/api.h>
|
|
#include <vlibmemory/api.h>
|
|
#include <vlibsocket/api.h>
|
|
#include <vppinfra/timing_wheel.h>
|
|
|
|
#include <vnet/l2/l2_output.h>
|
|
#include <vnet/l2/l2_input.h>
|
|
|
|
void
|
|
l2sess_vlib_plugin_register (vlib_main_t * vm, void* hh,
|
|
int from_early_init)
|
|
{
|
|
l2sess_main_t *sm = &l2sess_main;
|
|
vnet_plugin_handoff_t * h = hh;
|
|
memset (sm, 0, sizeof (*sm));
|
|
|
|
sm->vlib_main = vm;
|
|
sm->vnet_main = h->vnet_main;
|
|
sm->ethernet_main = h->ethernet_main;
|
|
}
|
|
|
|
void
|
|
l2sess_init_next_features (vlib_main_t * vm, l2sess_main_t * sm)
|
|
{
|
|
#define _(node_name, node_var, is_out, is_ip6, is_track) \
|
|
if (is_out) \
|
|
feat_bitmap_init_next_nodes(vm, node_var.index, L2OUTPUT_N_FEAT, \
|
|
l2output_get_feat_names (), \
|
|
sm->node_var ## _feat_next_node_index); \
|
|
else \
|
|
feat_bitmap_init_next_nodes(vm, node_var.index, L2INPUT_N_FEAT, \
|
|
l2input_get_feat_names (), \
|
|
sm->node_var ## _feat_next_node_index);
|
|
|
|
foreach_l2sess_node
|
|
#undef _
|
|
}
|
|
|
|
void
|
|
l2sess_add_our_next_nodes (vlib_main_t * vm, l2sess_main_t * sm,
|
|
u8 * prev_node_name, int add_output_nodes)
|
|
{
|
|
vlib_node_t *n;
|
|
n = vlib_get_node_by_name (vm, prev_node_name);
|
|
#define _(node_name, node_var, is_out, is_ip6, is_track) \
|
|
if (is_out == add_output_nodes) { \
|
|
u32 idx = vlib_node_add_next_with_slot(vm, n->index, node_var.index, ~0); \
|
|
if (is_track) { \
|
|
sm->next_slot_track_node_by_is_ip6_is_out[is_ip6][is_out] = idx; \
|
|
} \
|
|
}
|
|
foreach_l2sess_node
|
|
#undef _
|
|
}
|
|
|
|
void
|
|
l2sess_setup_nodes (void)
|
|
{
|
|
vlib_main_t *vm = vlib_get_main ();
|
|
l2sess_main_t *sm = &l2sess_main;
|
|
|
|
l2sess_init_next_features (vm, sm);
|
|
|
|
l2sess_add_our_next_nodes (vm, sm, (u8 *) "l2-input-classify", 0);
|
|
l2sess_add_our_next_nodes (vm, sm, (u8 *) "l2-output-classify", 1);
|
|
|
|
}
|
|
|
|
static char *
|
|
get_l4_proto_str (int is_ip6, uint8_t l4_proto)
|
|
{
|
|
switch (l4_proto)
|
|
{
|
|
case 6:
|
|
return "tcp";
|
|
case 17:
|
|
return "udp";
|
|
case 1:
|
|
return "icmp";
|
|
case 58:
|
|
return "icmp6";
|
|
default:
|
|
return "<?l4-unknown?>";
|
|
}
|
|
}
|
|
|
|
static clib_error_t *
|
|
l2sess_show_command_fn (vlib_main_t * vm,
|
|
unformat_input_t * input, vlib_cli_command_t * cmd)
|
|
{
|
|
l2sess_main_t *sm = &l2sess_main;
|
|
clib_time_t *ct = &vm->clib_time;
|
|
l2s_session_t *s;
|
|
u64 now = clib_cpu_time_now ();
|
|
|
|
vlib_cli_output (vm, "Timing wheel info: \n%U", format_timing_wheel,
|
|
&sm->timing_wheel, 255);
|
|
|
|
pool_foreach (s, sm->sessions, (
|
|
{
|
|
f64 ctime =
|
|
(now -
|
|
s->create_time) * ct->seconds_per_clock;
|
|
f64 atime0 =
|
|
(now -
|
|
s->side[0].active_time) *
|
|
ct->seconds_per_clock;
|
|
f64 atime1 =
|
|
(now -
|
|
s->side[1].active_time) *
|
|
ct->seconds_per_clock;
|
|
/*
|
|
f64 ctime = (s->create_time - vm->cpu_time_main_loop_start) * ct->seconds_per_clock;
|
|
f64 atime0 = (s->side[0].active_time - vm->cpu_time_main_loop_start) * ct->seconds_per_clock;
|
|
f64 atime1 = (s->side[1].active_time - vm->cpu_time_main_loop_start) * ct->seconds_per_clock;
|
|
*/
|
|
u8 * out0 =
|
|
format (0,
|
|
"%5d: create time: %U pkts/bytes/active time: [ %ld %ld %U : %ld %ld %U ]\n",
|
|
(s - sm->sessions),
|
|
format_time_interval, "h:m:s:u",
|
|
ctime, s->side[0].n_packets,
|
|
s->side[0].n_bytes,
|
|
format_time_interval, "h:m:s:u",
|
|
atime0, s->side[1].n_packets,
|
|
s->side[1].n_bytes,
|
|
format_time_interval, "h:m:s:u",
|
|
atime1); u8 * out1 = 0;
|
|
if (s->is_ip6)
|
|
{
|
|
out1 =
|
|
format (0, "%s %U :%u <-> %U :%u",
|
|
get_l4_proto_str (s->is_ip6,
|
|
s->l4_proto),
|
|
format_ip6_address,
|
|
&s->side[0].addr.ip6,
|
|
s->side[0].port,
|
|
format_ip6_address,
|
|
&s->side[1].addr.ip6,
|
|
s->side[1].port);}
|
|
else
|
|
{
|
|
out1 =
|
|
format (0, "%s %U :%u <-> %U :%u",
|
|
get_l4_proto_str (s->is_ip6,
|
|
s->l4_proto),
|
|
format_ip4_address,
|
|
&s->side[0].addr.ip4,
|
|
s->side[0].port,
|
|
format_ip4_address,
|
|
&s->side[1].addr.ip4,
|
|
s->side[1].port);}
|
|
vlib_cli_output (vm, "%s %s", out0,
|
|
out1); vec_free (out0);
|
|
vec_free (out1);}
|
|
));
|
|
return 0;
|
|
}
|
|
|
|
static clib_error_t *
|
|
l2sess_show_count_command_fn (vlib_main_t * vm,
|
|
unformat_input_t * input,
|
|
vlib_cli_command_t * cmd)
|
|
{
|
|
l2sess_main_t *sm = &l2sess_main;
|
|
|
|
vlib_cli_output (vm, "Timing wheel info: \n%U", format_timing_wheel,
|
|
&sm->timing_wheel, 255);
|
|
vlib_cli_output (vm, "session pool len: %d, pool elts: %d",
|
|
pool_len (sm->sessions), pool_elts (sm->sessions));
|
|
vlib_cli_output (vm,
|
|
"attempted to delete sessions which were already free: %d",
|
|
sm->counter_attempted_delete_free_session);
|
|
return 0;
|
|
}
|
|
|
|
|
|
/* *INDENT-OFF* */
|
|
VLIB_CLI_COMMAND (l2sess_show_command, static) = {
|
|
.path = "show l2sess",
|
|
.short_help = "show l2sess",
|
|
.function = l2sess_show_command_fn,
|
|
};
|
|
|
|
VLIB_CLI_COMMAND (l2sess_show_count_command, static) = {
|
|
.path = "show l2sess count",
|
|
.short_help = "show l2sess count",
|
|
.function = l2sess_show_count_command_fn,
|
|
};
|
|
/* *INDENT-OFF* */
|
|
|
|
static inline u64
|
|
time_sec_to_clock( clib_time_t *ct, f64 sec)
|
|
{
|
|
return (u64)(((f64)sec)/ct->seconds_per_clock);
|
|
}
|
|
|
|
static clib_error_t * l2sess_init (vlib_main_t * vm)
|
|
{
|
|
l2sess_main_t * sm = &l2sess_main;
|
|
clib_error_t * error = 0;
|
|
u64 cpu_time_now = clib_cpu_time_now();
|
|
|
|
|
|
clib_time_t *ct = &vm->clib_time;
|
|
sm->udp_session_idle_timeout = time_sec_to_clock(ct, UDP_SESSION_IDLE_TIMEOUT_SEC);
|
|
sm->tcp_session_idle_timeout = time_sec_to_clock(ct, TCP_SESSION_IDLE_TIMEOUT_SEC);
|
|
sm->tcp_session_transient_timeout = time_sec_to_clock(ct, TCP_SESSION_TRANSIENT_TIMEOUT_SEC);
|
|
|
|
/* The min sched time of 10e-1 causes erroneous behavior... */
|
|
sm->timing_wheel.min_sched_time = 10e-2;
|
|
sm->timing_wheel.max_sched_time = 3600.0*48.0;
|
|
timing_wheel_init (&sm->timing_wheel, cpu_time_now, vm->clib_time.clocks_per_second);
|
|
sm->timer_wheel_next_expiring_time = 0;
|
|
sm->timer_wheel_tick = time_sec_to_clock(ct, sm->timing_wheel.min_sched_time);
|
|
/* Pre-allocate expired nodes. */
|
|
vec_alloc (sm->data_from_advancing_timing_wheel, 32);
|
|
|
|
l2sess_setup_nodes();
|
|
l2output_init_output_node_vec (&sm->output_next_nodes.output_node_index_vec);
|
|
|
|
return error;
|
|
}
|
|
|
|
VLIB_INIT_FUNCTION (l2sess_init);
|
|
|
|
|