From 9080096f7c548415fc4d5354c7e582a3eda1a5ed Mon Sep 17 00:00:00 2001 From: Dave Barach Date: Fri, 24 May 2019 13:03:01 -0400 Subject: [PATCH] Add an ip6 local hop-by-hop protocol demux table Add a minimal ip6 hbh header processing test. ioam plugin: use ip6_local_hop_by_hop_register_protocol() in udp_ping_init(). Please test the ioam plugin udp_ping path AYEC, so I can publish the patch. Change-Id: I74e35276d6c38c31022026cfd238fad5e4a54485 Signed-off-by: Dave Barach --- src/plugins/ioam/udp-ping/udp_ping_node.c | 6 +- src/vnet/ip/ip6.h | 1 + src/vnet/ip/ip6_hop_by_hop.c | 355 ++++++++++++++++++++++++++++-- src/vnet/ip/ip6_hop_by_hop.h | 7 + test/test_ip6.py | 15 +- 5 files changed, 357 insertions(+), 27 deletions(-) diff --git a/src/plugins/ioam/udp-ping/udp_ping_node.c b/src/plugins/ioam/udp-ping/udp_ping_node.c index f48a15c7c0c..cf38bf6c46a 100644 --- a/src/plugins/ioam/udp-ping/udp_ping_node.c +++ b/src/plugins/ioam/udp-ping/udp_ping_node.c @@ -823,10 +823,8 @@ udp_ping_init (vlib_main_t * vm) udp_ping_main.vnet_main = vnet_get_main (); udp_ping_main.timer_interval = 1e9; - /* This steals MLDv2 listener reports. Disable until we properly handle - * hop-by-hop options in ip6-local */ - /* ip6_register_protocol (IP_PROTOCOL_IP6_HOP_BY_HOP_OPTIONS, - udp_ping_local.index); */ + ip6_local_hop_by_hop_register_protocol (IP_PROTOCOL_UDP, + udp_ping_local.index); return 0; } diff --git a/src/vnet/ip/ip6.h b/src/vnet/ip/ip6.h index 1a57c417342..e66bbdd0827 100644 --- a/src/vnet/ip/ip6.h +++ b/src/vnet/ip/ip6.h @@ -388,6 +388,7 @@ u16 ip6_tcp_udp_icmp_compute_checksum (vlib_main_t * vm, vlib_buffer_t * p0, int *bogus_lengthp); void ip6_register_protocol (u32 protocol, u32 node_index); +void ip6_local_hop_by_hop_register_protocol (u32 protocol, u32 node_index); serialize_function_t serialize_vnet_ip6_main, unserialize_vnet_ip6_main; diff --git a/src/vnet/ip/ip6_hop_by_hop.c b/src/vnet/ip/ip6_hop_by_hop.c index da0197061e2..9923b416b65 100644 --- a/src/vnet/ip/ip6_hop_by_hop.c +++ b/src/vnet/ip/ip6_hop_by_hop.c @@ -441,22 +441,20 @@ VLIB_NODE_FN (ip6_add_hop_by_hop_node) (vlib_main_t * vm, /* *INDENT-OFF* */ VLIB_REGISTER_NODE (ip6_add_hop_by_hop_node) = /* *INDENT-OFF* */ { - .name = - "ip6-add-hop-by-hop",.vector_size = sizeof (u32),.format_trace = - format_ip6_add_hop_by_hop_trace,.type = - VLIB_NODE_TYPE_INTERNAL,.n_errors = - ARRAY_LEN (ip6_add_hop_by_hop_error_strings),.error_strings = - ip6_add_hop_by_hop_error_strings, - /* See ip/lookup.h */ - .n_next_nodes = IP6_HBYH_IOAM_INPUT_N_NEXT,.next_nodes = - { + .name = "ip6-add-hop-by-hop", + .vector_size = sizeof (u32), + .format_trace = format_ip6_add_hop_by_hop_trace, + .type = VLIB_NODE_TYPE_INTERNAL, + .n_errors = ARRAY_LEN (ip6_add_hop_by_hop_error_strings), + .error_strings = ip6_add_hop_by_hop_error_strings, + /* See ip/lookup.h */ + .n_next_nodes = IP6_HBYH_IOAM_INPUT_N_NEXT, + .next_nodes = { #define _(s,n) [IP6_HBYH_IOAM_INPUT_NEXT_##s] = n, foreach_ip6_hbyh_ioam_input_next #undef _ - } -,}; -/* *INDENT-ON* */ - + }, +}; /* *INDENT-ON* */ /* The main h-b-h tracer was already invoked, no need to do much here */ @@ -783,17 +781,296 @@ VLIB_NODE_FN (ip6_pop_hop_by_hop_node) (vlib_main_t * vm, /* *INDENT-OFF* */ VLIB_REGISTER_NODE (ip6_pop_hop_by_hop_node) = { - .name = - "ip6-pop-hop-by-hop",.vector_size = sizeof (u32),.format_trace = - format_ip6_pop_hop_by_hop_trace,.type = - VLIB_NODE_TYPE_INTERNAL,.sibling_of = "ip6-lookup",.n_errors = - ARRAY_LEN (ip6_pop_hop_by_hop_error_strings),.error_strings = - ip6_pop_hop_by_hop_error_strings, - /* See ip/lookup.h */ -.n_next_nodes = 0,}; + .name = "ip6-pop-hop-by-hop", + .vector_size = sizeof (u32), + .format_trace = format_ip6_pop_hop_by_hop_trace, + .type = VLIB_NODE_TYPE_INTERNAL, + .sibling_of = "ip6-lookup", + .n_errors = ARRAY_LEN (ip6_pop_hop_by_hop_error_strings), + .error_strings = ip6_pop_hop_by_hop_error_strings, + /* See ip/lookup.h */ + .n_next_nodes = 0, +}; +/* *INDENT-ON* */ + +typedef struct +{ + u32 protocol; + u32 next_index; +} ip6_local_hop_by_hop_trace_t; + +#ifndef CLIB_MARCH_VARIANT + +/* packet trace format function */ +static u8 * +format_ip6_local_hop_by_hop_trace (u8 * s, va_list * args) +{ + CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *); + CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *); + ip6_local_hop_by_hop_trace_t *t = + va_arg (*args, ip6_local_hop_by_hop_trace_t *); + + s = format (s, "IP6_LOCAL_HOP_BY_HOP: protocol %d, next index %d\n", + t->protocol, t->next_index); + return s; +} + +vlib_node_registration_t ip6_local_hop_by_hop_node; + +#endif /* CLIB_MARCH_VARIANT */ + +#define foreach_ip6_local_hop_by_hop_error \ +_(UNKNOWN, "Unknown protocol ip6 local h-b-h packets dropped") \ +_(OK, "Good ip6 local h-b-h packets") + +typedef enum +{ +#define _(sym,str) IP6_LOCAL_HOP_BY_HOP_ERROR_##sym, + foreach_ip6_local_hop_by_hop_error +#undef _ + IP6_LOCAL_HOP_BY_HOP_N_ERROR, +} ip6_local_hop_by_hop_error_t; + +#ifndef CLIB_MARCH_VARIANT +static char *ip6_local_hop_by_hop_error_strings[] = { +#define _(sym,string) string, + foreach_ip6_local_hop_by_hop_error +#undef _ +}; +#endif /* CLIB_MARCH_VARIANT */ + +typedef enum +{ + IP6_LOCAL_HOP_BY_HOP_NEXT_DROP, + IP6_LOCAL_HOP_BY_HOP_N_NEXT, +} ip6_local_hop_by_hop_next_t; + +always_inline uword +ip6_local_hop_by_hop_inline (vlib_main_t * vm, + vlib_node_runtime_t * node, vlib_frame_t * frame, + int is_trace) +{ + u32 n_left_from, *from; + vlib_buffer_t *bufs[VLIB_FRAME_SIZE], **b; + u16 nexts[VLIB_FRAME_SIZE], *next; + u32 ok = 0; + u32 unknown_proto_error = node->errors[IP6_LOCAL_HOP_BY_HOP_ERROR_UNKNOWN]; + ip6_hop_by_hop_ioam_main_t *hm = &ip6_hop_by_hop_ioam_main; + + /* Note: there is only one of these */ + ip6_local_hop_by_hop_runtime_t *rt = hm->ip6_local_hbh_runtime; + + from = vlib_frame_vector_args (frame); + n_left_from = frame->n_vectors; + + vlib_get_buffers (vm, from, bufs, n_left_from); + b = bufs; + next = nexts; + + while (n_left_from >= 4) + { + ip6_header_t *ip0, *ip1, *ip2, *ip3; + u8 *hbh0, *hbh1, *hbh2, *hbh3; + + /* Prefetch next iteration. */ + if (PREDICT_TRUE (n_left_from >= 8)) + { + vlib_prefetch_buffer_header (b[4], STORE); + vlib_prefetch_buffer_header (b[5], STORE); + vlib_prefetch_buffer_header (b[6], STORE); + vlib_prefetch_buffer_header (b[7], STORE); + CLIB_PREFETCH (b[4]->data, CLIB_CACHE_LINE_BYTES, STORE); + CLIB_PREFETCH (b[5]->data, CLIB_CACHE_LINE_BYTES, STORE); + CLIB_PREFETCH (b[6]->data, CLIB_CACHE_LINE_BYTES, STORE); + CLIB_PREFETCH (b[7]->data, CLIB_CACHE_LINE_BYTES, STORE); + } + + /* + * Leave current_data pointing at the IP header. + * It's reasonably likely that any registered handler + * will want to know where to find the ip6 header. + */ + ip0 = vlib_buffer_get_current (b[0]); + ip1 = vlib_buffer_get_current (b[1]); + ip2 = vlib_buffer_get_current (b[2]); + ip3 = vlib_buffer_get_current (b[3]); + + /* Look at hop-by-hop header */ + hbh0 = ip6_next_header (ip0); + hbh1 = ip6_next_header (ip1); + hbh2 = ip6_next_header (ip2); + hbh3 = ip6_next_header (ip3); + + /* + * ... to find the next header type and see if we + * have a handler for it... + */ + next[0] = rt->next_index_by_protocol[*hbh0]; + next[1] = rt->next_index_by_protocol[*hbh1]; + next[2] = rt->next_index_by_protocol[*hbh2]; + next[3] = rt->next_index_by_protocol[*hbh3]; + + b[0]->error = unknown_proto_error; + b[1]->error = unknown_proto_error; + b[2]->error = unknown_proto_error; + b[3]->error = unknown_proto_error; + + /* Account for non-drop pkts */ + ok += next[0] != 0; + ok += next[1] != 0; + ok += next[2] != 0; + ok += next[3] != 0; + + if (is_trace) + { + if (b[0]->flags & VLIB_BUFFER_IS_TRACED) + { + ip6_local_hop_by_hop_trace_t *t = + vlib_add_trace (vm, node, b[0], sizeof (*t)); + t->next_index = next[0]; + t->protocol = *hbh0; + } + if (b[1]->flags & VLIB_BUFFER_IS_TRACED) + { + ip6_local_hop_by_hop_trace_t *t = + vlib_add_trace (vm, node, b[1], sizeof (*t)); + t->next_index = next[1]; + t->protocol = *hbh1; + } + if (b[2]->flags & VLIB_BUFFER_IS_TRACED) + { + ip6_local_hop_by_hop_trace_t *t = + vlib_add_trace (vm, node, b[2], sizeof (*t)); + t->next_index = next[2]; + t->protocol = *hbh2; + } + if (b[3]->flags & VLIB_BUFFER_IS_TRACED) + { + ip6_local_hop_by_hop_trace_t *t = + vlib_add_trace (vm, node, b[3], sizeof (*t)); + t->next_index = next[3]; + t->protocol = *hbh3; + } + } + + b += 4; + next += 4; + n_left_from -= 4; + } + + while (n_left_from > 0) + { + ip6_header_t *ip0; + u8 *hbh0; + + ip0 = vlib_buffer_get_current (b[0]); + hbh0 = ip6_next_header (ip0); + + next[0] = rt->next_index_by_protocol[*hbh0]; + + b[0]->error = unknown_proto_error; + ok += next[0] != 0; + + if (is_trace) + { + if (b[0]->flags & VLIB_BUFFER_IS_TRACED) + { + ip6_local_hop_by_hop_trace_t *t = + vlib_add_trace (vm, node, b[0], sizeof (*t)); + t->next_index = next[0]; + t->protocol = *hbh0; + } + } + + b += 1; + next += 1; + n_left_from -= 1; + } + + vlib_buffer_enqueue_to_next (vm, node, from, nexts, frame->n_vectors); + + vlib_node_increment_counter (vm, node->node_index, + IP6_LOCAL_HOP_BY_HOP_ERROR_OK, ok); + return frame->n_vectors; +} + +VLIB_NODE_FN (ip6_local_hop_by_hop_node) (vlib_main_t * vm, + vlib_node_runtime_t * node, + vlib_frame_t * frame) +{ + if (PREDICT_FALSE (node->flags & VLIB_NODE_FLAG_TRACE)) + return ip6_local_hop_by_hop_inline (vm, node, frame, 1 /* is_trace */ ); + else + return ip6_local_hop_by_hop_inline (vm, node, frame, 0 /* is_trace */ ); +} + +#ifndef CLIB_MARCH_VARIANT +/* *INDENT-OFF* */ +VLIB_REGISTER_NODE (ip6_local_hop_by_hop_node) = +{ + .name = "ip6-local-hop-by-hop", + .vector_size = sizeof (u32), + .format_trace = format_ip6_local_hop_by_hop_trace, + .type = VLIB_NODE_TYPE_INTERNAL, + + .n_errors = ARRAY_LEN(ip6_local_hop_by_hop_error_strings), + .error_strings = ip6_local_hop_by_hop_error_strings, + + .n_next_nodes = IP6_LOCAL_HOP_BY_HOP_N_NEXT, + + /* edit / add dispositions here */ + .next_nodes = + { + [IP6_LOCAL_HOP_BY_HOP_NEXT_DROP] = "error-drop", + }, +}; +/* *INDENT-ON* */ + +clib_error_t * +show_ip6_hbh_command_fn (vlib_main_t * vm, + unformat_input_t * input, vlib_cli_command_t * cmd) +{ + int i; + u32 next_index; + ip6_hop_by_hop_ioam_main_t *hm = &ip6_hop_by_hop_ioam_main; + ip6_local_hop_by_hop_runtime_t *rt = hm->ip6_local_hbh_runtime; + vlib_node_t *n = vlib_get_node (vm, ip6_local_hop_by_hop_node.index); + + vlib_cli_output (vm, "%-6s%s", "Proto", "Node Name"); + + for (i = 0; i < ARRAY_LEN (rt->next_index_by_protocol); i++) + { + if ((next_index = rt->next_index_by_protocol[i])) + { + u32 next_node_index = n->next_nodes[next_index]; + vlib_node_t *next_n = vlib_get_node (vm, next_node_index); + vlib_cli_output (vm, "[%3d] %v", i, next_n->name); + } + } + + return 0; +} + +/*? + * Display the set of ip6 local hop-by-hop next protocol handler nodes + * + * @cliexpar + * Display ip6 local hop-by-hop next protocol handler nodes + * @cliexcmd{show ip6 hbh} +?*/ +/* *INDENT-OFF* */ +VLIB_CLI_COMMAND (show_ip6_hbh, static) = { + .path = "show ip6 hbh", + .short_help = "show ip6 hbh", + .function = show_ip6_hbh_command_fn, +}; /* *INDENT-ON* */ + +#endif /* CLIB_MARCH_VARIANT */ + + #ifndef CLIB_MARCH_VARIANT static clib_error_t * ip6_hop_by_hop_ioam_init (vlib_main_t * vm) @@ -817,11 +1094,45 @@ ip6_hop_by_hop_ioam_init (vlib_main_t * vm) clib_memset (hm->options_size, 0, sizeof (hm->options_size)); vnet_classify_register_unformat_opaque_index_fn (unformat_opaque_ioam); + hm->ip6_local_hbh_runtime = clib_mem_alloc_aligned + (sizeof (ip6_local_hop_by_hop_runtime_t), CLIB_CACHE_LINE_BYTES); + memset (hm->ip6_local_hbh_runtime, 0, + sizeof (ip6_local_hop_by_hop_runtime_t)); + + ip6_register_protocol (IP_PROTOCOL_IP6_HOP_BY_HOP_OPTIONS, + ip6_local_hop_by_hop_node.index); return (0); } -VLIB_INIT_FUNCTION (ip6_hop_by_hop_ioam_init); +/* *INDENT-OFF* */ +VLIB_INIT_FUNCTION (ip6_hop_by_hop_ioam_init) = +{ + .runs_after = VLIB_INITS("ip_main_init", "ip6_lookup_init"), +}; +/* *INDENT-ON* */ + +void +ip6_local_hop_by_hop_register_protocol (u32 protocol, u32 node_index) +{ + ip6_hop_by_hop_ioam_main_t *hm = &ip6_hop_by_hop_ioam_main; + vlib_main_t *vm = hm->vlib_main; + ip6_local_hop_by_hop_runtime_t *local_hbh_runtime + = hm->ip6_local_hbh_runtime; + u32 old_next_index; + + ASSERT (protocol < ARRAY_LEN (local_hbh_runtime->next_index_by_protocol)); + + old_next_index = local_hbh_runtime->next_index_by_protocol[protocol]; + + local_hbh_runtime->next_index_by_protocol[protocol] = + vlib_node_add_next (vm, ip6_local_hop_by_hop_node.index, node_index); + + /* Someone will eventually do this. Trust me. */ + if (old_next_index && + (old_next_index != local_hbh_runtime->next_index_by_protocol[protocol])) + clib_warning ("WARNING: replaced next index for protocol %d", protocol); +} int ip6_ioam_set_rewrite (u8 ** rwp, int has_trace_option, diff --git a/src/vnet/ip/ip6_hop_by_hop.h b/src/vnet/ip/ip6_hop_by_hop.h index 6ae2a2e8b51..e8ecab6c6d5 100644 --- a/src/vnet/ip/ip6_hop_by_hop.h +++ b/src/vnet/ip/ip6_hop_by_hop.h @@ -40,6 +40,11 @@ typedef struct u8 flow_name[64]; } flow_data_t; +typedef struct +{ + u8 next_index_by_protocol[256]; +} ip6_local_hop_by_hop_runtime_t; + typedef struct { /* The current rewrite we're using */ @@ -85,6 +90,8 @@ typedef struct u32 (*flow_handler[MAX_IP6_HBH_OPTION]) (u32 flow_ctx, u8 add); flow_data_t *flows; + ip6_local_hop_by_hop_runtime_t *ip6_local_hbh_runtime; + /* convenience */ vlib_main_t *vlib_main; vnet_main_t *vnet_main; diff --git a/test/test_ip6.py b/test/test_ip6.py index 7c9df465707..b64dbc1d871 100644 --- a/test/test_ip6.py +++ b/test/test_ip6.py @@ -10,7 +10,7 @@ from scapy.contrib.mpls import MPLS from scapy.layers.inet6 import IPv6, ICMPv6ND_NS, ICMPv6ND_RS, \ ICMPv6ND_RA, ICMPv6NDOptMTU, ICMPv6NDOptSrcLLAddr, ICMPv6NDOptPrefixInfo, \ ICMPv6ND_NA, ICMPv6NDOptDstLLAddr, ICMPv6DestUnreach, icmp6types, \ - ICMPv6TimeExceeded, ICMPv6EchoRequest, ICMPv6EchoReply + ICMPv6TimeExceeded, ICMPv6EchoRequest, ICMPv6EchoReply, IPv6ExtHdrHopByHop from scapy.layers.l2 import Ether, Dot1Q from scapy.packet import Raw from scapy.utils import inet_pton, inet_ntop @@ -2313,6 +2313,19 @@ class TestIP6Input(VppTestCase): remark=msg or "", timeout=timeout) + def test_hop_by_hop(self): + """ Hop-by-hop header test """ + + p = (Ether(src=self.pg0.remote_mac, + dst=self.pg0.local_mac) / + IPv6(src=self.pg0.remote_ip6, dst=self.pg0.local_ip6) / + IPv6ExtHdrHopByHop() / + inet6.UDP(sport=1234, dport=1234) / + Raw('\xa5' * 100)) + + self.pg0.add_stream(p) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() if __name__ == '__main__': unittest.main(testRunner=VppTestRunner) -- 2.16.6