40 #define foreach_ip6_hbyh_input_next \ 41 _(IP6_REWRITE, "ip6-rewrite") \ 42 _(IP6_LOOKUP, "ip6-lookup") \ 43 _(IP6_HBYH, "ip6-hop-by-hop")\ 44 _(IP6_POP_HBYH, "ip6-pop-hop-by-hop")\ 48 #define _(s,n) IP6_HBYH_INPUT_NEXT_##s, 103 u8 trace_data_size = 0;
116 return trace_data_size;
121 u32 *elt = va_arg (*args,
u32 *);
122 u8 *trace_type_p = va_arg (*args,
u8 *);
123 u8 trace_type = *trace_type_p;
128 u32 ttl_node_id_host_byte_order = clib_net_to_host_u32 (*elt);
129 s =
format (s,
"ttl 0x%x node id 0x%x ",
130 ttl_node_id_host_byte_order>>24,
131 ttl_node_id_host_byte_order & 0x00FFFFFF);
138 u32 ingress_host_byte_order = clib_net_to_host_u32(*elt);
139 s =
format (s,
"ingress 0x%x egress 0x%x ",
140 ingress_host_byte_order >> 16,
141 ingress_host_byte_order &0xFFFF);
147 u32 ts_in_host_byte_order = clib_net_to_host_u32 (*elt);
148 s =
format (s,
"ts 0x%x \n", ts_in_host_byte_order);
154 u32 appdata_in_host_byte_order = clib_net_to_host_u32 (*elt);
155 s =
format (s,
"app 0x%x ", appdata_in_host_byte_order);
163 ioam_pow_option_t * pow0 = va_arg (*args, ioam_pow_option_t *);
164 u64 random, cumulative;
165 random = cumulative = 0;
168 random = clib_net_to_host_u64 (pow0->random);
169 cumulative = clib_net_to_host_u64 (pow0->cumulative);
172 s =
format (s,
"random = 0x%Lx, Cumulative = 0x%Lx, Index = 0x%x",
173 random, cumulative, pow0->reserved_profile_id);
184 ioam_trace_option_t * trace0;
185 u8 trace_data_size_in_words = 0;
189 ioam_pow_option_t * pow0;
193 s =
format (s,
"IP6_HOP_BY_HOP: next index %d len %d traced %d\n",
199 while (opt0 < limit0)
206 trace0 = (ioam_trace_option_t *)opt0;
207 s =
format (s,
" Trace Type 0x%x , %d elts left ts msb(s) 0x%x\n",
208 trace0->ioam_trace_type, trace0->data_list_elts_left,
210 trace_data_size_in_words =
212 elt0 = &trace0->elts[0];
214 ((
u8 *)(&trace0->elts[0]) + trace0->hdr.length - 2
217 s =
format (s,
" [%d] %U\n",elt_index,
219 elt0, &trace0->ioam_trace_type);
221 elt0 += trace_data_size_in_words;
230 s =
format (s,
" POW opt present\n");
231 pow0 = (ioam_pow_option_t *) opt0;
235 (((
u8 *)opt0) +
sizeof (ioam_pow_option_t));
243 s =
format (s,
"Unknown %d", type0);
255 #define foreach_ip6_hop_by_hop_error \ 256 _(PROCESSED, "Pkts with ip6 hop-by-hop options") \ 257 _(PROFILE_MISS, "Pkts with ip6 hop-by-hop options but no profile set") \ 258 _(UNKNOWN_OPTION, "Unknown ip6 hop-by-hop options") 261 #define _(sym,str) IP6_HOP_BY_HOP_ERROR_##sym, 268 #define _(sym,string) string, 281 u32 n_left_from, * from, * to_next;
283 u32 processed = 0, unknown_opts = 0;
292 while (n_left_from > 0)
297 to_next, n_left_to_next);
300 while (n_left_from >= 4 && n_left_to_next >= 2)
302 u32 next0 = IP6_HOP_BY_HOP_NEXT_INTERFACE_OUTPUT;
303 u32 next1 = IP6_HOP_BY_HOP_NEXT_INTERFACE_OUTPUT;
304 u32 sw_if_index0, sw_if_index1;
325 to_next[0] = bi0 = from[0];
326 to_next[1] = bi1 = from[1];
353 t->sw_if_index = sw_if_index0;
360 t->sw_if_index = sw_if_index1;
367 to_next, n_left_to_next,
368 bi0, bi1, next0, next1);
372 while (n_left_from > 0 && n_left_to_next > 0)
382 ioam_trace_option_t * trace0;
383 ioam_pow_option_t * pow0;
386 u64 random = 0, cumulative = 0;
405 ((
u8 *)hbh0 + ((hbh0->
length+1)<<3));
408 while (opt0 < limit0)
414 trace0 = (ioam_trace_option_t *)opt0;
417 trace0->data_list_elts_left--;
421 elt_index = trace0->data_list_elts_left *
423 elt0 = &trace0->elts[elt_index];
427 clib_host_to_net_u32 ((ip0->
hop_limit<<24)
435 (
vnet_buffer(b0)->sw_if_index[
VLIB_RX]&0xFFFF) << 16 | (adj0->rewrite_header.sw_if_index & 0xFFFF);
436 *elt0 = clib_host_to_net_u32(*elt0);
448 *elt0 = clib_host_to_net_u32(time_u64.
as_u32[0]);
455 *elt0 = clib_host_to_net_u32(hm->
app_data);
471 IP6_HOP_BY_HOP_ERROR_PROFILE_MISS, 1);
474 (((
u8 *)opt0) +
sizeof (ioam_pow_option_t));
477 pow0 = (ioam_pow_option_t *) opt0;
478 pow_encap = (pow0->random == 0);
485 u16 new_profile_index;
507 pow0->reserved_profile_id =
526 IP6_HOP_BY_HOP_ERROR_PROFILE_MISS, 1);
528 pow0->reserved_profile_id,
544 if (pow0->random == 0)
546 pow0->random = clib_host_to_net_u64(
548 pow0->cumulative = 0;
550 random = clib_net_to_host_u64(pow0->random);
551 cumulative = clib_net_to_host_u64(pow0->cumulative);
552 pow0->cumulative = clib_host_to_net_u64(
557 (((
u8 *)opt0) +
sizeof (ioam_pow_option_t));
575 IP6_HBYH_INPUT_NEXT_IP6_POP_HBYH : IP6_HBYH_INPUT_NEXT_IP6_REWRITE;
596 to_next, n_left_to_next,
605 IP6_HOP_BY_HOP_ERROR_UNKNOWN_OPTION, unknown_opts);
609 IP6_HOP_BY_HOP_ERROR_PROCESSED, processed);
615 .name =
"ip6-hop-by-hop",
616 .vector_size =
sizeof (
u32),
625 #define _(s,n) [IP6_HBYH_INPUT_NEXT_##s] = n, 644 s =
format (s,
"IP6_ADD_HOP_BY_HOP: next index %d",
651 #define foreach_ip6_add_hop_by_hop_error \ 652 _(PROCESSED, "Pkts w/ added ip6 hop-by-hop options") 655 #define _(sym,str) IP6_ADD_HOP_BY_HOP_ERROR_##sym, 662 #define _(sym,string) string, 673 u32 n_left_from, * from, * to_next;
683 while (n_left_from > 0)
688 to_next, n_left_to_next);
691 while (n_left_from >= 4 && n_left_to_next >= 2)
693 u32 next0 = IP6_ADD_HOP_BY_HOP_NEXT_INTERFACE_OUTPUT;
694 u32 next1 = IP6_ADD_HOP_BY_HOP_NEXT_INTERFACE_OUTPUT;
695 u32 sw_if_index0, sw_if_index1;
716 to_next[0] = bi0 = from[0];
717 to_next[1] = bi1 = from[1];
744 t->sw_if_index = sw_if_index0;
751 t->sw_if_index = sw_if_index1;
758 to_next, n_left_to_next,
759 bi0, bi1, next0, next1);
763 while (n_left_from > 0 && n_left_to_next > 0)
770 u64 * copy_src0, * copy_dst0;
786 copy_dst0 = (
u64 *)(((
u8 *)ip0) - rewrite_length);
787 copy_src0 = (
u64 *) ip0;
789 copy_dst0 [0] = copy_src0 [0];
790 copy_dst0 [1] = copy_src0 [1];
791 copy_dst0 [2] = copy_src0 [2];
792 copy_dst0 [3] = copy_src0 [3];
793 copy_dst0 [4] = copy_src0 [4];
803 new_l0 = clib_net_to_host_u16 (ip0->
payload_length) + rewrite_length;
807 next0 = IP6_HBYH_INPUT_NEXT_IP6_LOOKUP;
821 to_next, n_left_to_next,
829 IP6_ADD_HOP_BY_HOP_ERROR_PROCESSED, processed);
835 .name =
"ip6-add-hop-by-hop",
836 .vector_size =
sizeof (
u32),
846 #define _(s,n) [IP6_HBYH_INPUT_NEXT_##s] = n, 865 s =
format (s,
"IP6_POP_HOP_BY_HOP: next index %d",
872 #define foreach_ip6_pop_hop_by_hop_error \ 873 _(PROCESSED, "Pkts w/ removed ip6 hop-by-hop options") \ 874 _(NO_HOHO, "Pkts w/ no ip6 hop-by-hop options") \ 875 _(SCV_PASSED, "Pkts with SCV in Policy") \ 876 _(SCV_FAILED, "Pkts with SCV out of Policy") 879 #define _(sym,str) IP6_POP_HOP_BY_HOP_ERROR_##sym, 886 #define _(sym,string) string, 896 ioam_pow_option_t * pow0;
898 u64 final_cumulative = 0;
902 if (!hbh0 || !ip0)
return;
906 ((
u8 *)hbh0 + ((hbh0->
length+1)<<3));
909 while (opt0 < limit0)
921 pow0 = (ioam_pow_option_t *) opt0;
922 random = clib_net_to_host_u64(pow0->random);
923 final_cumulative = clib_net_to_host_u64(pow0->cumulative);
925 final_cumulative, random);
930 IP6_POP_HOP_BY_HOP_ERROR_SCV_PASSED, result);
935 IP6_POP_HOP_BY_HOP_ERROR_SCV_FAILED, 1);
939 (((
u8 *)opt0) +
sizeof (ioam_pow_option_t));
947 format(0,
"Something is wrong\n");
962 u32 n_left_from, * from, * to_next;
976 while (n_left_from > 0)
981 to_next, n_left_to_next);
984 while (n_left_from >= 4 && n_left_to_next >= 2)
986 u32 next0 = IP6_POP_HOP_BY_HOP_NEXT_INTERFACE_OUTPUT;
987 u32 next1 = IP6_POP_HOP_BY_HOP_NEXT_INTERFACE_OUTPUT;
988 u32 sw_if_index0, sw_if_index1;
992 vlib_buffer_t * b0, * b1;
996 vlib_buffer_t * p2, * p3;
1009 to_next[0] = bi0 = from[0];
1010 to_next[1] = bi1 = from[1];
1014 n_left_to_next -= 2;
1037 t->sw_if_index = sw_if_index0;
1044 t->sw_if_index = sw_if_index1;
1051 to_next, n_left_to_next,
1052 bi0, bi1, next0, next1);
1056 while (n_left_from > 0 && n_left_to_next > 0)
1065 u64 * copy_dst0, * copy_src0;
1074 n_left_to_next -= 1;
1089 next0 = IP6_HBYH_INPUT_NEXT_IP6_LOOKUP;
1095 next0 = ioam_end_of_path_cb ?
1096 ioam_end_of_path_cb (vm, node, b0, ip0, adj0) :
1097 IP6_HBYH_INPUT_NEXT_IP6_REWRITE;
1106 copy_src0 = (
u64 *)ip0;
1107 copy_dst0 = copy_src0 + (hbh0->
length+1);
1108 copy_dst0 [4] = copy_src0[4];
1109 copy_dst0 [3] = copy_src0[3];
1110 copy_dst0 [2] = copy_src0[2];
1111 copy_dst0 [1] = copy_src0[1];
1112 copy_dst0 [0] = copy_src0[0];
1117 next0 = IP6_HBYH_INPUT_NEXT_IP6_LOOKUP;
1132 to_next, n_left_to_next,
1140 IP6_POP_HOP_BY_HOP_ERROR_PROCESSED, processed);
1142 IP6_POP_HOP_BY_HOP_ERROR_NO_HOHO, no_header);
1148 .name =
"ip6-pop-hop-by-hop",
1149 .vector_size =
sizeof (
u32),
1159 #define _(s,n) [IP6_HBYH_INPUT_NEXT_##s] = n, 1184 int has_pow_option,
int has_ppc_option)
1189 ioam_trace_option_t * trace_option;
1190 ioam_pow_option_t * pow_option;
1192 u8 trace_data_size = 0;
1196 if (trace_option_elts == 0 && has_pow_option == 0)
1202 if (trace_option_elts)
1207 if (trace_data_size == 0)
1208 return VNET_API_ERROR_INVALID_VALUE;
1210 if (trace_option_elts * trace_data_size > 254)
1211 return VNET_API_ERROR_INVALID_VALUE;
1213 size += trace_option_elts * trace_data_size;
1218 size +=
sizeof (ioam_pow_option_t);
1222 rnd_size = (size + 7) & ~7;
1229 hbh->
length = (rnd_size>>3) - 1;
1230 current = (
u8 *)(hbh+1);
1232 if (trace_option_elts)
1234 trace_option = (ioam_trace_option_t *)current;
1237 trace_option->hdr.length =
1239 trace_option_elts * trace_data_size;
1241 trace_option->data_list_elts_left = trace_option_elts;
1242 current +=
sizeof (ioam_trace_option_t) +
1243 trace_option_elts * trace_data_size;
1247 pow_option = (ioam_pow_option_t *)current;
1250 pow_option->hdr.length =
sizeof (ioam_pow_option_t) -
1252 current +=
sizeof (ioam_pow_option_t);
1285 .path =
"clear ioam rewrite",
1286 .short_help =
"clear ioam rewrite",
1292 u32 app_data,
int has_pow_option,
u32 trace_tsp,
1298 has_pow_option, has_ppc_option);
1325 u32 trace_option_elts = 0;
1326 u32 trace_type = 0, node_id = 0;
1328 int has_pow_option = 0;
1329 int has_ppc_option = 0;
1334 if (
unformat (input,
"trace-type 0x%x trace-elts %d " 1335 "trace-tsp %d node-id 0x%x app-data 0x%x",
1336 &trace_type, &trace_option_elts, &trace_tsp,
1337 &node_id, &app_data))
1341 else if (
unformat (input,
"ppc encap"))
1343 else if (
unformat (input,
"ppc decap"))
1345 else if (
unformat (input,
"ppc none"))
1353 app_data, has_pow_option, trace_tsp, has_ppc_option);
1360 .path =
"set ioam rewrite",
1361 .short_help =
"set ioam rewrite trace-type <0x1f|0x3|0x9|0x11|0x19> trace-elts <nn> trace-tsp <0|1|2|3> node-id <node id in hex> app-data <app_data in hex> [pow] [ppc <encap|decap>]",
1376 s =
format(s,
" REWRITE FLOW CONFIGS - \n");
1377 s =
format(s,
" Destination Address : %U\n",
1385 s =
format(s,
" REWRITE FLOW CONFIGS - Not configured\n");
1390 s =
format(s,
" HOP BY HOP OPTIONS - TRACE CONFIG - \n");
1391 s =
format(s,
" Trace Type : 0x%x (%d)\n",
1393 s =
format(s,
" Trace timestamp precision : %d (%s)\n", hm->
trace_tsp,
1397 s =
format(s,
" Num of trace nodes : %d\n",
1399 s =
format(s,
" Node-id : 0x%x (%d)\n",
1401 s =
format(s,
" App Data : 0x%x (%d)\n",
1406 s =
format(s,
" HOP BY HOP OPTIONS - TRACE CONFIG - Not configured\n");
1409 s =
format(s,
" POW OPTION - %d (%s)\n",
1412 s =
format(s,
"Try 'show ioam sc-profile' for more information\n");
1414 s =
format(s,
" EDGE TO EDGE - PPC OPTION - %d (%s)\n",
1417 s =
format(s,
"Try 'show ioam ppc' for more information\n");
1425 .path =
"show ioam summary",
1426 .short_help =
"Summary of IOAM configuration",
1431 int is_add,
int is_pop,
int is_none)
1441 BVT(clib_bihash_kv) kv, value;
1443 if ((is_add + is_pop + is_none) != 1)
1444 return VNET_API_ERROR_INVALID_VALUE_2;
1450 return VNET_API_ERROR_NO_SUCH_FIB;
1456 for (i = 0; i < len; i++)
1461 if (dst_address_length != mask_width)
1466 kv.key[2] = ((
u64)((fib_index))<<32) | dst_address_length;
1473 return VNET_API_ERROR_NO_SUCH_ENTRY;
1478 adj_index = value.value;
1489 if (is_add || is_pop)
1510 u32 mask_width = ~0;
1522 else if (
unformat (input,
"vrf-id %d", &vrf_id))
1534 if ((is_add + is_pop + is_none) != 1)
1536 if (mask_width == ~0)
1540 is_add, is_pop, is_none);
1554 .path =
"set ioam destination",
1555 .short_help =
"set ioam destination <ip6-address>/<width> add | pop | none",
#define vec_validate(V, I)
Make sure vector is long enough for given index (no header, unspecified alignment) ...
static uword ip6_pop_hop_by_hop_node_fn(vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *frame)
scv_profile * scv_profile_find(u16 id)
void vlib_put_next_frame(vlib_main_t *vm, vlib_node_runtime_t *r, u32 next_index, u32 n_vectors_left)
ip6_hop_by_hop_main_t ip6_hop_by_hop_main
#define clib_error_return_code(e, code, flags, args...)
sll srl srl sll sra u16x4 i
static clib_error_t * ip6_set_ioam_destination_command_fn(vlib_main_t *vm, unformat_input_t *input, vlib_cli_command_t *cmd)
u16 saved_lookup_next_index
static uword ip6_hop_by_hop_node_fn(vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *frame)
bad routing header type(not 4)") sr_error (NO_MORE_SEGMENTS
static clib_error_t * ip6_show_ioam_summary_cmd_fn(vlib_main_t *vm, unformat_input_t *input, vlib_cli_command_t *cmd)
ip_lookup_next_t lookup_next_index
#define HBH_OPTION_TYPE_IOAM_PROOF_OF_WORK
static clib_error_t * ip6_set_ioam_rewrite_command_fn(vlib_main_t *vm, unformat_input_t *input, vlib_cli_command_t *cmd)
struct _vlib_node_registration vlib_node_registration_t
static u8 * format_ip6_add_hop_by_hop_trace(u8 *s, va_list *args)
static f64 trace_tsp_mul[4]
static u8 * format_ip6_hop_by_hop_trace(u8 *s, va_list *args)
always_inline void * vlib_buffer_get_current(vlib_buffer_t *b)
Get pointer to current data to process.
static uword ip6_add_hop_by_hop_node_fn(vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *frame)
vnet_main_t * vnet_get_main(void)
static char * ip6_add_hop_by_hop_error_strings[]
i16 current_data
signed offset in data[], pre_data[] that we are currently processing.
u64 scv_update_cumulative(scv_profile *profile, u64 cumulative, u64 random)
static char * ip6_hop_by_hop_error_strings[]
#define VLIB_INIT_FUNCTION(x)
#define HBH_OPTION_TYPE_IOAM_EDGE_TO_EDGE
ip6_hop_by_hop_main_t * hm
u8 scv_validate(scv_profile *profile, u64 cumulative, u64 random)
#define BIT_ING_INTERFACE
ip6_pop_hop_by_hop_error_t
vlib_node_registration_t ip6_pop_hop_by_hop_node
(constructor) VLIB_REGISTER_NODE (ip6_pop_hop_by_hop_node)
always_inline void * vlib_frame_vector_args(vlib_frame_t *f)
int ip6_ioam_set_rewrite(u8 **rwp, u32 trace_type, u32 trace_option_elts, int has_pow_option, int has_ppc_option)
scv_profile * pow_profile
#define foreach_ip6_pop_hop_by_hop_error
static u8 fetch_trace_data_size(u8 trace_type)
always_inline void vlib_node_increment_counter(vlib_main_t *vm, u32 node_index, u32 counter_index, u64 increment)
#define vlib_validate_buffer_enqueue_x2(vm, node, next_index, to_next, n_left_to_next, bi0, bi1, next0, next1)
#define vlib_validate_buffer_enqueue_x1(vm, node, next_index, to_next, n_left_to_next, bi0, next0)
#define vlib_get_next_frame(vm, node, next_index, vectors, n_vectors_left)
void vlib_cli_output(vlib_main_t *vm, char *fmt,...)
void * ioam_end_of_path_cb
uword * fib_index_by_table_id
ip6_address_t fib_masks[129]
static u16 scv_get_next_profile_id(vlib_main_t *vm, u16 id)
static u8 * format_ip6_pop_hop_by_hop_trace(u8 *s, va_list *args)
#define CLIB_PREFETCH(addr, size, type)
u64 scv_generate_random(scv_profile *profile)
#define vec_free(V)
Free vector's memory (no header).
#define clib_memcpy(a, b, c)
void vnet_register_ioam_end_of_path_callback(void *cb)
#define VLIB_CLI_COMMAND(x,...)
static char * ip6_pop_hop_by_hop_error_strings[]
ip_lookup_main_t lookup_main
#define foreach_ip6_hbyh_input_next
static u8 is_zero_ip6_address(ip6_address_t *a)
u8 * prefix_lengths_in_search_order
static u8 * format_ioam_pow(u8 *s, va_list *args)
#define VLIB_NODE_FLAG_TRACE
static void scv_profile_invalidate(vlib_main_t *vm, ip6_hop_by_hop_main_t *hm, u16 id, u8 is_encap)
static u8 * format_ioam_data_list_element(u8 *s, va_list *args)
#define HBH_OPTION_TYPE_IOAM_TRACE_DATA_LIST
#define VLIB_BUFFER_IS_TRACED
static int BV() clib_bihash_search_inline_2(BVT(clib_bihash)*h, BVT(clib_bihash_kv)*search_key, BVT(clib_bihash_kv)*valuep)
always_inline ip_adjacency_t * ip_get_adjacency(ip_lookup_main_t *lm, u32 adj_index)
static void ioam_end_of_path_validation(vlib_main_t *vm, ip6_header_t *ip0, ip6_hop_by_hop_header_t *hbh0)
clib_error_t * ip6_ioam_trace_profile_set(u32 trace_option_elts, u32 trace_type, u32 node_id, u32 app_data, int has_pow_option, u32 trace_tsp, int has_ppc_option)
#define vec_len(v)
Number of elements in vector (rvalue-only, NULL tolerant)
always_inline void vlib_buffer_advance(vlib_buffer_t *b, word l)
Advance current data pointer by the supplied (signed!) amount.
vlib_node_registration_t ip6_hop_by_hop_node
(constructor) VLIB_REGISTER_NODE (ip6_hop_by_hop_node)
u64 total_pkts_using_this_profile
#define foreach_ip6_hop_by_hop_error
clib_error_t * clear_ioam_rewrite_command_fn(vlib_main_t *vm, unformat_input_t *input, vlib_cli_command_t *cmd)
always_inline void * vlib_add_trace(vlib_main_t *vm, vlib_node_runtime_t *r, vlib_buffer_t *b, u32 n_data_bytes)
#define HBH_OPTION_TYPE_DATA_CHANGE_ENROUTE
#define vlib_prefetch_buffer_header(b, type)
Prefetch buffer metadata.
#define TRACE_TYPE_IF_TS_APP
#define foreach_ip6_add_hop_by_hop_error
#define VLIB_REGISTER_NODE(x,...)
clib_error_t * clear_ioam_rewrite_fn(void)
always_inline f64 vlib_time_now(vlib_main_t *vm)
#define clib_error_return(e, args...)
#define CLIB_CACHE_LINE_BYTES
u32 flags
buffer flags: VLIB_BUFFER_IS_TRACED: trace this buffer.
int ip6_ioam_set_destination(ip6_address_t *addr, u32 mask_width, u32 vrf_id, int is_add, int is_pop, int is_none)
#define HBH_OPTION_TYPE_MASK
always_inline vlib_buffer_t * vlib_get_buffer(vlib_main_t *vm, u32 buffer_index)
Translate buffer index into buffer pointer.
vlib_node_registration_t ip6_add_hop_by_hop_node
(constructor) VLIB_REGISTER_NODE (ip6_add_hop_by_hop_node)
static clib_error_t * ip6_hop_by_hop_init(vlib_main_t *vm)
#define TRACE_TYPE_TS_APP
ip6_add_hop_by_hop_error_t