X-Git-Url: https://gerrit.fd.io/r/gitweb?a=blobdiff_plain;f=vnet%2Fvnet%2Fl2tp%2Fdecap.c;h=e8986935e939e3ff11c2e13d41e98b39ec0ee642;hb=c4d1cf76072b54f630b0263cd86e01df82249568;hp=1a2bc4890d028e1e9688ba6b73895b1a97c50086;hpb=cb9cadad578297ffd78fa8a33670bdf1ab669e7e;p=vpp.git diff --git a/vnet/vnet/l2tp/decap.c b/vnet/vnet/l2tp/decap.c index 1a2bc4890d0..e8986935e93 100644 --- a/vnet/vnet/l2tp/decap.c +++ b/vnet/vnet/l2tp/decap.c @@ -23,218 +23,242 @@ #include /* Statistics (not really errors) */ -#define foreach_l2t_decap_error \ +#define foreach_l2t_decap_error \ _(USER_TO_NETWORK, "L2TP user (ip6) to L2 network pkts") \ _(SESSION_ID_MISMATCH, "l2tpv3 local session id mismatches") \ -_(COOKIE_MISMATCH, "l2tpv3 local cookie mismatches") +_(COOKIE_MISMATCH, "l2tpv3 local cookie mismatches") \ +_(NO_SESSION, "l2tpv3 session not found") \ +_(ADMIN_DOWN, "l2tpv3 tunnel is down") -static char * l2t_decap_error_strings[] = { +static char *l2t_decap_error_strings[] = { #define _(sym,string) string, foreach_l2t_decap_error #undef _ }; -typedef enum { +typedef enum +{ #define _(sym,str) L2T_DECAP_ERROR_##sym, - foreach_l2t_decap_error + foreach_l2t_decap_error #undef _ L2T_DECAP_N_ERROR, } l2t_DECAP_error_t; -typedef enum { - L2T_DECAP_NEXT_DROP, - L2T_DECAP_NEXT_L2_INPUT, - L2T_DECAP_N_NEXT, - /* Pseudo next index */ - L2T_DECAP_NEXT_NO_INTERCEPT = L2T_DECAP_N_NEXT, +typedef enum +{ + L2T_DECAP_NEXT_DROP, + L2T_DECAP_NEXT_L2_INPUT, + L2T_DECAP_N_NEXT, + /* Pseudo next index */ + L2T_DECAP_NEXT_NO_INTERCEPT = L2T_DECAP_N_NEXT, } l2t_decap_next_t; #define NSTAGES 3 -static inline void stage0 (vlib_main_t * vm, - vlib_node_runtime_t * node, - u32 buffer_index) +static inline void +stage0 (vlib_main_t * vm, vlib_node_runtime_t * node, u32 buffer_index) { - vlib_buffer_t *b = vlib_get_buffer (vm, buffer_index); - vlib_prefetch_buffer_header (b, STORE); - /* l2tpv3 header is a long way away, need 2 cache lines */ - CLIB_PREFETCH (b->data, 2*CLIB_CACHE_LINE_BYTES, STORE); + vlib_buffer_t *b = vlib_get_buffer (vm, buffer_index); + vlib_prefetch_buffer_header (b, STORE); + /* l2tpv3 header is a long way away, need 2 cache lines */ + CLIB_PREFETCH (b->data, 2 * CLIB_CACHE_LINE_BYTES, STORE); } -static inline void stage1 (vlib_main_t * vm, - vlib_node_runtime_t * node, - u32 bi) +static inline void +stage1 (vlib_main_t * vm, vlib_node_runtime_t * node, u32 bi) { - vlib_buffer_t *b = vlib_get_buffer (vm, bi); - l2t_main_t *lm = &l2t_main; - ip6_header_t * ip6 = vlib_buffer_get_current (b); - u32 session_index; - uword *p = 0; - l2tpv3_header_t * l2t; - - /* Not L2tpv3 (0x73, 0t115)? Use the normal path. */ - if (PREDICT_FALSE(ip6->protocol != IP_PROTOCOL_L2TP)) { - vnet_buffer(b)->l2t.next_index = L2T_DECAP_NEXT_NO_INTERCEPT; - return; + vlib_buffer_t *b = vlib_get_buffer (vm, bi); + l2t_main_t *lm = &l2t_main; + ip6_header_t *ip6 = vlib_buffer_get_current (b); + u32 session_index; + uword *p = 0; + l2tpv3_header_t *l2t; + + /* Not L2tpv3 (0x73, 0t115)? Use the normal path. */ + if (PREDICT_FALSE (ip6->protocol != IP_PROTOCOL_L2TP)) + { + vnet_buffer (b)->l2t.next_index = L2T_DECAP_NEXT_NO_INTERCEPT; + return; } - /* Make up your minds, people... */ - switch (lm->lookup_type) { + /* Make up your minds, people... */ + switch (lm->lookup_type) + { case L2T_LOOKUP_SRC_ADDRESS: - p = hash_get_mem (lm->session_by_src_address, &ip6->src_address); - break; + p = hash_get_mem (lm->session_by_src_address, &ip6->src_address); + break; case L2T_LOOKUP_DST_ADDRESS: - p = hash_get_mem (lm->session_by_dst_address, &ip6->dst_address); - break; + p = hash_get_mem (lm->session_by_dst_address, &ip6->dst_address); + break; case L2T_LOOKUP_SESSION_ID: - l2t = (l2tpv3_header_t*)(ip6+1); - p = hash_get (lm->session_by_session_id, l2t->session_id); - break; + l2t = (l2tpv3_header_t *) (ip6 + 1); + p = hash_get (lm->session_by_session_id, l2t->session_id); + break; default: - ASSERT(0); + ASSERT (0); } - if (PREDICT_FALSE(p == 0)) { - vnet_buffer(b)->l2t.next_index = L2T_DECAP_NEXT_NO_INTERCEPT; - return; - } else { - session_index = p[0]; + if (PREDICT_FALSE (p == 0)) + { + vnet_buffer (b)->l2t.next_index = L2T_DECAP_NEXT_NO_INTERCEPT; + return; + } + else + { + session_index = p[0]; } - /* Remember mapping index, prefetch the mini counter */ - vnet_buffer(b)->l2t.next_index = L2T_DECAP_NEXT_L2_INPUT; - vnet_buffer(b)->l2t.session_index = session_index; + /* Remember mapping index, prefetch the mini counter */ + vnet_buffer (b)->l2t.next_index = L2T_DECAP_NEXT_L2_INPUT; + vnet_buffer (b)->l2t.session_index = session_index; - /* $$$$$ prefetch counter */ + /* $$$$$ prefetch counter */ } -static inline u32 last_stage (vlib_main_t *vm, vlib_node_runtime_t *node, - u32 bi) +static inline u32 +last_stage (vlib_main_t * vm, vlib_node_runtime_t * node, u32 bi) { - vlib_buffer_t *b = vlib_get_buffer (vm, bi); - l2t_main_t *lm = &l2t_main; - ip6_header_t * ip6 = vlib_buffer_get_current (b); - vlib_node_t *n = vlib_get_node (vm, l2t_decap_node.index); - u32 node_counter_base_index = n->error_heap_index; - vlib_error_main_t * em = &vm->error_main; - l2tpv3_header_t * l2tp; - u32 counter_index; - l2t_session_t * session; - u32 session_index; - u32 next_index; - - /* Other-than-output pkt? We're done... */ - if (vnet_buffer(b)->l2t.next_index != L2T_DECAP_NEXT_L2_INPUT) { - next_index = vnet_buffer(b)->l2t.next_index; + vlib_buffer_t *b = vlib_get_buffer (vm, bi); + l2t_main_t *lm = &l2t_main; + ip6_header_t *ip6 = vlib_buffer_get_current (b); + vlib_node_t *n = vlib_get_node (vm, node->node_index); + u32 node_counter_base_index = n->error_heap_index; + vlib_error_main_t *em = &vm->error_main; + l2tpv3_header_t *l2tp; + u32 counter_index; + l2t_session_t *session = 0; + u32 session_index; + u32 next_index; + u8 l2tp_decap_local = (l2t_decap_local_node.index == n->index); + + /* Other-than-output pkt? We're done... */ + if (vnet_buffer (b)->l2t.next_index != L2T_DECAP_NEXT_L2_INPUT) + { + next_index = vnet_buffer (b)->l2t.next_index; goto done; } - em->counters[node_counter_base_index + L2T_DECAP_ERROR_USER_TO_NETWORK] += 1; - - session_index = vnet_buffer(b)->l2t.session_index; - - counter_index = - session_index_to_counter_index (session_index, - SESSION_COUNTER_USER_TO_NETWORK); - - /* per-mapping byte stats include the ethernet header */ - vlib_increment_combined_counter (&lm->counter_main, - os_get_cpu_number(), - counter_index, - 1 /* packet_increment */, - vlib_buffer_length_in_chain (vm, b) + - sizeof (ethernet_header_t)); - - session = pool_elt_at_index (lm->sessions, session_index); - - l2tp = vlib_buffer_get_current (b) + sizeof (*ip6); - - if (PREDICT_FALSE(l2tp->session_id != session->local_session_id)) { - // Key matched but session id does not. Assume packet is not for us. - em->counters[node_counter_base_index + L2T_DECAP_ERROR_SESSION_ID_MISMATCH] += 1; + em->counters[node_counter_base_index + L2T_DECAP_ERROR_USER_TO_NETWORK] += + 1; + + session_index = vnet_buffer (b)->l2t.session_index; + + counter_index = + session_index_to_counter_index (session_index, + SESSION_COUNTER_USER_TO_NETWORK); + + /* per-mapping byte stats include the ethernet header */ + vlib_increment_combined_counter (&lm->counter_main, + os_get_cpu_number (), + counter_index, 1 /* packet_increment */ , + vlib_buffer_length_in_chain (vm, b) + + sizeof (ethernet_header_t)); + + session = pool_elt_at_index (lm->sessions, session_index); + + l2tp = vlib_buffer_get_current (b) + sizeof (*ip6); + + if (PREDICT_FALSE (l2tp->session_id != session->local_session_id)) + { + /* Key matched but session id does not. Assume packet is not for us. */ + em->counters[node_counter_base_index + + L2T_DECAP_ERROR_SESSION_ID_MISMATCH] += 1; next_index = L2T_DECAP_NEXT_NO_INTERCEPT; goto done; } - if (PREDICT_FALSE (l2tp->cookie != session->local_cookie[0])) { - if (l2tp->cookie != session->local_cookie[1]) { - // Key and session ID matched, but cookie doesn't. Drop this packet. - b->error = node->errors[L2T_DECAP_ERROR_COOKIE_MISMATCH]; - next_index = L2T_DECAP_NEXT_DROP; - goto done; - } + if (PREDICT_FALSE (l2tp->cookie != session->local_cookie[0])) + { + if (l2tp->cookie != session->local_cookie[1]) + { + /* Key and session ID matched, but cookie doesn't. Drop this packet. */ + b->error = node->errors[L2T_DECAP_ERROR_COOKIE_MISMATCH]; + next_index = L2T_DECAP_NEXT_DROP; + goto done; + } } - vnet_buffer(b)->sw_if_index[VLIB_RX] = session->sw_if_index; - - /* strip the ip6 and L2TP header */ - vlib_buffer_advance (b, sizeof (*ip6) + session->l2tp_hdr_size); - - /* Required to make the l2 tag push / pop code work on l2 subifs */ - vnet_update_l2_len (b); - - if (PREDICT_FALSE(b->flags & VLIB_BUFFER_IS_TRACED)) { - l2t_trace_t *t = vlib_add_trace (vm, node, b, sizeof (*t)); - t->is_user_to_network = 1; - t->our_address.as_u64[0] = - ip6->dst_address.as_u64[0]; - t->our_address.as_u64[1] = - ip6->dst_address.as_u64[1]; - t->client_address.as_u64[0] = - ip6->src_address.as_u64[0]; - t->client_address.as_u64[1] = - ip6->src_address.as_u64[1]; - t->session_index = session_index; - } + vnet_buffer (b)->sw_if_index[VLIB_RX] = session->sw_if_index; - return L2T_DECAP_NEXT_L2_INPUT; + if (PREDICT_FALSE (!(session->admin_up))) + { + b->error = node->errors[L2T_DECAP_ERROR_ADMIN_DOWN]; + next_index = L2T_DECAP_NEXT_DROP; + goto done; + } - done: - if (next_index == L2T_DECAP_NEXT_NO_INTERCEPT) { - // Go to next node on the ip6 configuration chain - ip6_main_t * im = &ip6_main; - ip_lookup_main_t * lm = &im->lookup_main; - ip_config_main_t * cm = &lm->rx_config_mains[VNET_UNICAST]; - ip6_l2tpv3_config_t * c0; + /* strip the ip6 and L2TP header */ + vlib_buffer_advance (b, sizeof (*ip6) + session->l2tp_hdr_size); + + /* Required to make the l2 tag push / pop code work on l2 subifs */ + vnet_update_l2_len (b); + + if (PREDICT_FALSE (b->flags & VLIB_BUFFER_IS_TRACED)) + { + l2t_trace_t *t = vlib_add_trace (vm, node, b, sizeof (*t)); + t->is_user_to_network = 1; + t->our_address.as_u64[0] = ip6->dst_address.as_u64[0]; + t->our_address.as_u64[1] = ip6->dst_address.as_u64[1]; + t->client_address.as_u64[0] = ip6->src_address.as_u64[0]; + t->client_address.as_u64[1] = ip6->src_address.as_u64[1]; + t->session_index = session_index; + } - vnet_get_config_data (&cm->config_main, - &vnet_buffer (b)->ip.current_config_index, - &next_index, - sizeof (c0[0])); + return L2T_DECAP_NEXT_L2_INPUT; + +done: + if (next_index == L2T_DECAP_NEXT_NO_INTERCEPT) + { + /* Small behavioral change between l2tp-decap and l2tp-decap-local */ + if (l2tp_decap_local) + { + b->error = node->errors[L2T_DECAP_ERROR_NO_SESSION]; + next_index = L2T_DECAP_NEXT_DROP; + } + else + { + /* Go to next node on the ip6 configuration chain */ + if (PREDICT_TRUE (session != 0)) + vnet_feature_next (session->sw_if_index, &next_index, b); + } } - if (PREDICT_FALSE(b->flags & VLIB_BUFFER_IS_TRACED)) { - l2t_trace_t *t = vlib_add_trace (vm, node, b, sizeof (*t)); - t->is_user_to_network = 1; - t->our_address.as_u64[0] = - ip6->dst_address.as_u64[0]; - t->our_address.as_u64[1] = - ip6->dst_address.as_u64[1]; - t->client_address.as_u64[0] = - ip6->src_address.as_u64[0]; - t->client_address.as_u64[1] = - ip6->src_address.as_u64[1]; - t->session_index = ~0; + if (PREDICT_FALSE (b->flags & VLIB_BUFFER_IS_TRACED)) + { + l2t_trace_t *t = vlib_add_trace (vm, node, b, sizeof (*t)); + t->is_user_to_network = 1; + t->our_address.as_u64[0] = ip6->dst_address.as_u64[0]; + t->our_address.as_u64[1] = ip6->dst_address.as_u64[1]; + t->client_address.as_u64[0] = ip6->src_address.as_u64[0]; + t->client_address.as_u64[1] = ip6->src_address.as_u64[1]; + t->session_index = ~0; } - return next_index; + return next_index; } #include -static uword l2t_decap_node_fn (vlib_main_t * vm, - vlib_node_runtime_t * node, - vlib_frame_t * frame) +static uword +l2t_decap_node_fn (vlib_main_t * vm, + vlib_node_runtime_t * node, vlib_frame_t * frame) { - return dispatch_pipeline (vm, node, frame); + return dispatch_pipeline (vm, node, frame); } +/* + * l2tp-decap and l2tp-decap-local have very slightly different behavior. + * When a packet has no associated session l2tp-decap let it go to ip6 forward, + * while l2tp-decap-local drops it. + */ + +/* *INDENT-OFF* */ VLIB_REGISTER_NODE (l2t_decap_node) = { .function = l2t_decap_node_fn, .name = "l2tp-decap", .vector_size = sizeof (u32), .format_trace = format_l2t_trace, .type = VLIB_NODE_TYPE_INTERNAL, - + .n_errors = ARRAY_LEN(l2t_decap_error_strings), .error_strings = l2t_decap_error_strings, @@ -246,8 +270,40 @@ VLIB_REGISTER_NODE (l2t_decap_node) = { [L2T_DECAP_NEXT_DROP] = "error-drop", }, }; +/* *INDENT-ON* */ -void l2tp_decap_init (void) +VLIB_NODE_FUNCTION_MULTIARCH (l2t_decap_node, l2t_decap_node_fn); +/* *INDENT-OFF* */ +VLIB_REGISTER_NODE (l2t_decap_local_node) = { + .function = l2t_decap_node_fn, + .name = "l2tp-decap-local", + .vector_size = sizeof (u32), + .format_trace = format_l2t_trace, + .type = VLIB_NODE_TYPE_INTERNAL, + + .n_errors = ARRAY_LEN(l2t_decap_error_strings), + .error_strings = l2t_decap_error_strings, + + .n_next_nodes = L2T_DECAP_N_NEXT, + + /* edit / add dispositions here */ + .next_nodes = { + [L2T_DECAP_NEXT_L2_INPUT] = "l2-input", + [L2T_DECAP_NEXT_DROP] = "error-drop", + }, +}; +/* *INDENT-ON* */ + +void +l2tp_decap_init (void) { - ip6_register_protocol (IP_PROTOCOL_L2TP, l2t_decap_node.index); + ip6_register_protocol (IP_PROTOCOL_L2TP, l2t_decap_local_node.index); } + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */