diff options
author | Damjan Marion <damarion@cisco.com> | 2016-12-19 23:05:39 +0100 |
---|---|---|
committer | Damjan Marion <damarion@cisco.com> | 2016-12-28 12:25:14 +0100 |
commit | 7cd468a3d7dee7d6c92f69a0bb7061ae208ec727 (patch) | |
tree | 5de62f8dbd3a752f5a676ca600e43d2652d1ff1a /src/vnet/l2tp | |
parent | 696f1adec0df3b8f161862566dd9c86174302658 (diff) |
Reorganize source tree to use single autotools instance
Change-Id: I7b51f88292e057c6443b12224486f2d0c9f8ae23
Signed-off-by: Damjan Marion <damarion@cisco.com>
Diffstat (limited to 'src/vnet/l2tp')
-rw-r--r-- | src/vnet/l2tp/decap.c | 309 | ||||
-rw-r--r-- | src/vnet/l2tp/encap.c | 238 | ||||
-rw-r--r-- | src/vnet/l2tp/l2tp.api | 126 | ||||
-rw-r--r-- | src/vnet/l2tp/l2tp.c | 739 | ||||
-rw-r--r-- | src/vnet/l2tp/l2tp.h | 147 | ||||
-rw-r--r-- | src/vnet/l2tp/l2tp_api.c | 267 | ||||
-rw-r--r-- | src/vnet/l2tp/packet.h | 44 | ||||
-rw-r--r-- | src/vnet/l2tp/pg.c | 106 |
8 files changed, 1976 insertions, 0 deletions
diff --git a/src/vnet/l2tp/decap.c b/src/vnet/l2tp/decap.c new file mode 100644 index 00000000000..e8986935e93 --- /dev/null +++ b/src/vnet/l2tp/decap.c @@ -0,0 +1,309 @@ +/* + * decap.c : L2TPv3 tunnel decapsulation + * + * Copyright (c) 2013 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. + */ + +#include <vppinfra/error.h> +#include <vppinfra/hash.h> +#include <vnet/vnet.h> +#include <vnet/ip/ip.h> +#include <vnet/ethernet/ethernet.h> +#include <vnet/l2tp/l2tp.h> + +/* Statistics (not really errors) */ +#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") \ +_(NO_SESSION, "l2tpv3 session not found") \ +_(ADMIN_DOWN, "l2tpv3 tunnel is down") + +static char *l2t_decap_error_strings[] = { +#define _(sym,string) string, + foreach_l2t_decap_error +#undef _ +}; + +typedef enum +{ +#define _(sym,str) L2T_DECAP_ERROR_##sym, + 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, +} l2t_decap_next_t; + +#define NSTAGES 3 + +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); +} + +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; + } + + /* 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; + case L2T_LOOKUP_DST_ADDRESS: + 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; + default: + ASSERT (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; + + /* $$$$$ prefetch counter */ +} + +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, 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; + 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; + } + } + + vnet_buffer (b)->sw_if_index[VLIB_RX] = session->sw_if_index; + + if (PREDICT_FALSE (!(session->admin_up))) + { + b->error = node->errors[L2T_DECAP_ERROR_ADMIN_DOWN]; + next_index = L2T_DECAP_NEXT_DROP; + goto done; + } + + /* 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; + } + + 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; + } + return next_index; +} + +#include <vnet/pipeline.h> + +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); +} + +/* + * 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, + + .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* */ + +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_local_node.index); +} + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/src/vnet/l2tp/encap.c b/src/vnet/l2tp/encap.c new file mode 100644 index 00000000000..ed7a9580de1 --- /dev/null +++ b/src/vnet/l2tp/encap.c @@ -0,0 +1,238 @@ +/* + * encap.c : L2TPv3 tunnel encapsulation + * + * Copyright (c) 2013 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. + */ + +#include <vppinfra/error.h> +#include <vppinfra/hash.h> +#include <vnet/vnet.h> +#include <vnet/ip/ip.h> +#include <vnet/ethernet/ethernet.h> +#include <vnet/l2tp/l2tp.h> + +/* Statistics (not really errors) */ +#define foreach_l2t_encap_error \ +_(NETWORK_TO_USER, "L2TP L2 network to user (ip6) pkts") \ +_(LOOKUP_FAIL_TO_L3, "L2TP L2 session lookup failed pkts") \ +_(ADMIN_DOWN, "L2TP tunnel is down") + +static char *l2t_encap_error_strings[] = { +#define _(sym,string) string, + foreach_l2t_encap_error +#undef _ +}; + +typedef enum +{ +#define _(sym,str) L2T_ENCAP_ERROR_##sym, + foreach_l2t_encap_error +#undef _ + L2T_ENCAP_N_ERROR, +} l2t_encap_error_t; + + +typedef enum +{ + L2T_ENCAP_NEXT_DROP, + L2T_ENCAP_NEXT_IP6_LOOKUP, + L2T_ENCAP_N_NEXT, +} l2t_encap_next_t; + +typedef struct +{ + u32 cached_session_index; + u32 cached_sw_if_index; + vnet_main_t *vnet_main; +} l2tp_encap_runtime_t; + +vlib_node_registration_t l2t_encap_node; + +#define NSTAGES 3 + +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); + 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) +{ + l2tp_encap_runtime_t *rt = (void *) node->runtime_data; + vlib_buffer_t *b = vlib_get_buffer (vm, bi); + vnet_hw_interface_t *hi; + + u32 sw_if_index = vnet_buffer (b)->sw_if_index[VLIB_TX]; + u32 session_index = rt->cached_session_index; + + if (PREDICT_FALSE (rt->cached_sw_if_index != sw_if_index)) + { + hi = vnet_get_sup_hw_interface (rt->vnet_main, sw_if_index); + session_index = rt->cached_session_index = hi->dev_instance; + rt->cached_sw_if_index = sw_if_index; + } + + /* Remember mapping index, prefetch the mini counter */ + vnet_buffer (b)->l2t.next_index = L2T_ENCAP_NEXT_IP6_LOOKUP; + vnet_buffer (b)->l2t.session_index = session_index; + + /* $$$$ prefetch counter... */ +} + +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; + vlib_node_t *n = vlib_get_node (vm, l2t_encap_node.index); + u32 node_counter_base_index = n->error_heap_index; + vlib_error_main_t *em = &vm->error_main; + l2tpv3_header_t *l2tp; + u32 session_index; + u32 counter_index; + l2t_session_t *s; + ip6_header_t *ip6; + u16 payload_length; + u32 next_index = L2T_ENCAP_NEXT_IP6_LOOKUP; + + /* Other-than-output pkt? We're done... */ + if (vnet_buffer (b)->l2t.next_index != L2T_ENCAP_NEXT_IP6_LOOKUP) + return vnet_buffer (b)->l2t.next_index; + + em->counters[node_counter_base_index + L2T_ENCAP_ERROR_NETWORK_TO_USER] += + 1; + + session_index = vnet_buffer (b)->l2t.session_index; + + counter_index = + session_index_to_counter_index (session_index, + SESSION_COUNTER_NETWORK_TO_USER); + + /* 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)); + + s = pool_elt_at_index (lm->sessions, session_index); + + vnet_buffer (b)->sw_if_index[VLIB_TX] = s->encap_fib_index; + + /* Paint on an l2tpv3 hdr */ + vlib_buffer_advance (b, -(s->l2tp_hdr_size)); + l2tp = vlib_buffer_get_current (b); + + l2tp->session_id = s->remote_session_id; + l2tp->cookie = s->remote_cookie; + if (PREDICT_FALSE (s->l2_sublayer_present)) + { + l2tp->l2_specific_sublayer = 0; + } + + /* Paint on an ip6 header */ + vlib_buffer_advance (b, -(sizeof (*ip6))); + ip6 = vlib_buffer_get_current (b); + + if (PREDICT_FALSE (!(s->admin_up))) + { + b->error = node->errors[L2T_ENCAP_ERROR_ADMIN_DOWN]; + next_index = L2T_ENCAP_NEXT_DROP; + goto done; + } + + ip6->ip_version_traffic_class_and_flow_label = + clib_host_to_net_u32 (0x6 << 28); + + /* calculate ip6 payload length */ + payload_length = vlib_buffer_length_in_chain (vm, b); + payload_length -= sizeof (*ip6); + + ip6->payload_length = clib_host_to_net_u16 (payload_length); + ip6->protocol = IP_PROTOCOL_L2TP; + ip6->hop_limit = 0xff; + ip6->src_address.as_u64[0] = s->our_address.as_u64[0]; + ip6->src_address.as_u64[1] = s->our_address.as_u64[1]; + ip6->dst_address.as_u64[0] = s->client_address.as_u64[0]; + ip6->dst_address.as_u64[1] = s->client_address.as_u64[1]; + + +done: + 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 = 0; + t->our_address.as_u64[0] = ip6->src_address.as_u64[0]; + t->our_address.as_u64[1] = ip6->src_address.as_u64[1]; + t->client_address.as_u64[0] = ip6->dst_address.as_u64[0]; + t->client_address.as_u64[1] = ip6->dst_address.as_u64[1]; + t->session_index = session_index; + } + + return next_index; +} + +#include <vnet/pipeline.h> + +uword +l2t_encap_node_fn (vlib_main_t * vm, + vlib_node_runtime_t * node, vlib_frame_t * frame) +{ + return dispatch_pipeline (vm, node, frame); +} + + +/* *INDENT-OFF* */ +VLIB_REGISTER_NODE (l2t_encap_node) = { + .function = l2t_encap_node_fn, + .name = "l2tp-encap", + .vector_size = sizeof (u32), + .format_trace = format_l2t_trace, + .type = VLIB_NODE_TYPE_INTERNAL, + .runtime_data_bytes = sizeof (l2tp_encap_runtime_t), + + .n_errors = ARRAY_LEN(l2t_encap_error_strings), + .error_strings = l2t_encap_error_strings, + + .n_next_nodes = L2T_ENCAP_N_NEXT, + + /* add dispositions here */ + .next_nodes = { + [L2T_ENCAP_NEXT_IP6_LOOKUP] = "ip6-lookup", + [L2T_ENCAP_NEXT_DROP] = "error-drop", + }, +}; +/* *INDENT-ON* */ + +VLIB_NODE_FUNCTION_MULTIARCH (l2t_encap_node, l2t_encap_node_fn); +void +l2tp_encap_init (vlib_main_t * vm) +{ + l2tp_encap_runtime_t *rt; + + rt = vlib_node_get_runtime_data (vm, l2t_encap_node.index); + rt->vnet_main = vnet_get_main (); + rt->cached_sw_if_index = (u32) ~ 0; + rt->cached_session_index = (u32) ~ 0; +} + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/src/vnet/l2tp/l2tp.api b/src/vnet/l2tp/l2tp.api new file mode 100644 index 00000000000..5a5a5a48a68 --- /dev/null +++ b/src/vnet/l2tp/l2tp.api @@ -0,0 +1,126 @@ +/* + * Copyright (c) 2015-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. + */ + +/** \brief l2tpv3 tunnel interface create request + @param client_index - opaque cookie to identify the sender + @param context - sender context, to match reply w/ request + @param client_address - remote client tunnel ip address + @param client_address - local tunnel ip address + @param is_ipv6 - ipv6 if non-zero, else ipv4 + @param local_session_id - local tunnel session id + @param remote_session_id - remote tunnel session id + @param local_cookie - local tunnel cookie + @param l2_sublayer_present - l2 sublayer is present in packets if non-zero + @param encap_vrf_id - fib identifier used for outgoing encapsulated packets +*/ +define l2tpv3_create_tunnel +{ + u32 client_index; + u32 context; + u8 client_address[16]; + u8 our_address[16]; + u8 is_ipv6; + u32 local_session_id; + u32 remote_session_id; + u64 local_cookie; + u64 remote_cookie; + u8 l2_sublayer_present; + u32 encap_vrf_id; +}; + +/** \brief l2tpv3 tunnel interface create response + @param context - sender context, to match reply w/ request + @param retval - return code for the request + @param sw_if_index - index of the new tunnel interface +*/ +define l2tpv3_create_tunnel_reply +{ + u32 context; + i32 retval; + u32 sw_if_index; +}; + +define l2tpv3_set_tunnel_cookies +{ + u32 client_index; + u32 context; + u32 sw_if_index; + u64 new_local_cookie; + u64 new_remote_cookie; +}; + +/** \brief L2TP tunnel set cookies response + @param context - sender context, to match reply w/ request + @param retval - return code for the request +*/ +define l2tpv3_set_tunnel_cookies_reply +{ + u32 context; + i32 retval; +}; + +define sw_if_l2tpv3_tunnel_details +{ + u32 context; + u32 sw_if_index; + u8 interface_name[64]; + u8 client_address[16]; + u8 our_address[16]; + u32 local_session_id; + u32 remote_session_id; + u64 local_cookie[2]; + u64 remote_cookie; + u8 l2_sublayer_present; +}; + +define sw_if_l2tpv3_tunnel_dump +{ + u32 client_index; + u32 context; +}; + +define l2tpv3_interface_enable_disable +{ + u32 client_index; + u32 context; + u8 enable_disable; + u32 sw_if_index; +}; + +define l2tpv3_interface_enable_disable_reply +{ + u32 context; + i32 retval; +}; + +define l2tpv3_set_lookup_key +{ + u32 client_index; + u32 context; + /* 0 = ip6 src_address, 1 = ip6 dst_address, 2 = session_id */ + u8 key; +}; + +define l2tpv3_set_lookup_key_reply +{ + u32 context; + i32 retval; +}; + +/* + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/src/vnet/l2tp/l2tp.c b/src/vnet/l2tp/l2tp.c new file mode 100644 index 00000000000..a4531dab85c --- /dev/null +++ b/src/vnet/l2tp/l2tp.c @@ -0,0 +1,739 @@ +/* + * l2tp.c : L2TPv3 tunnel support + * + * Copyright (c) 2013 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. + */ + +#include <vppinfra/error.h> +#include <vppinfra/hash.h> +#include <vnet/vnet.h> +#include <vnet/ip/ip.h> +#include <vnet/l2/l2_input.h> +#include <vnet/ethernet/ethernet.h> +#include <vnet/l2tp/l2tp.h> + +l2t_main_t l2t_main; + +/* packet trace format function */ +u8 * +format_l2t_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 *); + l2t_trace_t *t = va_arg (*args, l2t_trace_t *); + + if (t->is_user_to_network) + s = format (s, "L2T: %U (client) -> %U (our) session %d", + format_ip6_address, &t->client_address, + format_ip6_address, &t->our_address, t->session_index); + else + s = format (s, "L2T: %U (our) -> %U (client) session %d)", + format_ip6_address, &t->our_address, + format_ip6_address, &t->client_address, t->session_index); + return s; +} + +u8 * +format_l2t_session (u8 * s, va_list * args) +{ + l2t_session_t *session = va_arg (*args, l2t_session_t *); + l2t_main_t *lm = &l2t_main; + u32 counter_index; + vlib_counter_t v; + + s = format (s, "[%d] %U (our) %U (client) %U (sw_if_index %d)\n", + session - lm->sessions, + format_ip6_address, &session->our_address, + format_ip6_address, &session->client_address, + format_vnet_sw_interface_name, lm->vnet_main, + vnet_get_sw_interface (lm->vnet_main, session->sw_if_index), + session->sw_if_index); + + s = format (s, " local cookies %016llx %016llx remote cookie %016llx\n", + clib_net_to_host_u64 (session->local_cookie[0]), + clib_net_to_host_u64 (session->local_cookie[1]), + clib_net_to_host_u64 (session->remote_cookie)); + + s = format (s, " local session-id %d remote session-id %d\n", + clib_net_to_host_u32 (session->local_session_id), + clib_net_to_host_u32 (session->remote_session_id)); + + s = format (s, " l2 specific sublayer %s\n", + session->l2_sublayer_present ? "preset" : "absent"); + + counter_index = + session_index_to_counter_index (session - lm->sessions, + SESSION_COUNTER_USER_TO_NETWORK); + + vlib_get_combined_counter (&lm->counter_main, counter_index, &v); + if (v.packets != 0) + s = format (s, " user-to-net: %llu pkts %llu bytes\n", + v.packets, v.bytes); + + vlib_get_combined_counter (&lm->counter_main, counter_index + 1, &v); + + if (v.packets != 0) + s = format (s, " net-to-user: %llu pkts %llu bytes\n", + v.packets, v.bytes); + return s; +} + +static clib_error_t * +show_l2tp_command_fn (vlib_main_t * vm, + unformat_input_t * input, vlib_cli_command_t * cmd) +{ + l2t_session_t *session; + l2t_main_t *lm = &l2t_main; + char *keystr = 0; + int verbose = 0; + + if (unformat (input, "verbose") || unformat (input, "v")) + verbose = 1; + + if (pool_elts (lm->sessions) == 0) + vlib_cli_output (vm, "No l2tp sessions..."); + else + vlib_cli_output (vm, "%u l2tp sessions...", pool_elts (lm->sessions)); + + if (verbose) + { + switch (lm->lookup_type) + { + case L2T_LOOKUP_SRC_ADDRESS: + keystr = "src address"; + break; + + case L2T_LOOKUP_DST_ADDRESS: + keystr = "dst address"; + break; + + case L2T_LOOKUP_SESSION_ID: + keystr = "session id"; + break; + + default: + keystr = "BOGUS!"; + break; + } + + vlib_cli_output (vm, "L2tp session lookup on %s", keystr); + + /* *INDENT-OFF* */ + pool_foreach (session, lm->sessions, + ({ + vlib_cli_output (vm, "%U", format_l2t_session, session); + })); + /* *INDENT-ON* */ + } + + return 0; +} + +/* *INDENT-OFF* */ +VLIB_CLI_COMMAND (show_session_detail_command, static) = { + .path = "show l2tpv3", + .short_help = "show l2tpv3 [verbose]", + .function = show_l2tp_command_fn, +}; +/* *INDENT-ON* */ + +static clib_error_t * +test_counters_command_fn (vlib_main_t * vm, + unformat_input_t * input, vlib_cli_command_t * cmd) +{ + l2t_session_t *session; + l2t_main_t *lm = &l2t_main; + u32 session_index; + u32 counter_index; + u32 nincr = 0; + u32 cpu_index = os_get_cpu_number (); + + /* *INDENT-OFF* */ + pool_foreach (session, lm->sessions, + ({ + session_index = session - lm->sessions; + counter_index = + session_index_to_counter_index (session_index, + SESSION_COUNTER_USER_TO_NETWORK); + vlib_increment_combined_counter (&lm->counter_main, + cpu_index, + counter_index, + 1/*pkt*/, 1111 /*bytes*/); + vlib_increment_combined_counter (&lm->counter_main, + cpu_index, + counter_index+1, + 1/*pkt*/, 2222 /*bytes*/); + nincr++; + + })); + /* *INDENT-ON* */ + vlib_cli_output (vm, "Incremented %d active counters\n", nincr); + + return 0; +} + +/* *INDENT-OFF* */ +VLIB_CLI_COMMAND (test_counters_command, static) = { + .path = "test counters", + .short_help = "increment all active counters", + .function = test_counters_command_fn, +}; +/* *INDENT-ON* */ + +static clib_error_t * +clear_counters_command_fn (vlib_main_t * vm, + unformat_input_t * input, vlib_cli_command_t * cmd) +{ + l2t_session_t *session; + l2t_main_t *lm = &l2t_main; + u32 session_index; + u32 counter_index; + u32 nincr = 0; + + /* *INDENT-OFF* */ + pool_foreach (session, lm->sessions, + ({ + session_index = session - lm->sessions; + counter_index = + session_index_to_counter_index (session_index, + SESSION_COUNTER_USER_TO_NETWORK); + vlib_zero_combined_counter (&lm->counter_main, counter_index); + vlib_zero_combined_counter (&lm->counter_main, counter_index+1); + nincr++; + })); + /* *INDENT-ON* */ + vlib_cli_output (vm, "Cleared %d active counters\n", nincr); + + return 0; +} + +/* *INDENT-OFF* */ +VLIB_CLI_COMMAND (clear_counters_command, static) = { + .path = "clear counters", + .short_help = "clear all active counters", + .function = clear_counters_command_fn, +}; +/* *INDENT-ON* */ + +static u8 * +format_l2tpv3_name (u8 * s, va_list * args) +{ + l2t_main_t *lm = &l2t_main; + u32 i = va_arg (*args, u32); + u32 show_dev_instance = ~0; + + if (i < vec_len (lm->dev_inst_by_real)) + show_dev_instance = lm->dev_inst_by_real[i]; + + if (show_dev_instance != ~0) + i = show_dev_instance; + + return format (s, "l2tpv3_tunnel%d", i); +} + +static int +l2tpv3_name_renumber (vnet_hw_interface_t * hi, u32 new_dev_instance) +{ + l2t_main_t *lm = &l2t_main; + + vec_validate_init_empty (lm->dev_inst_by_real, hi->dev_instance, ~0); + + lm->dev_inst_by_real[hi->dev_instance] = new_dev_instance; + + return 0; +} + +static uword +dummy_interface_tx (vlib_main_t * vm, + vlib_node_runtime_t * node, vlib_frame_t * frame) +{ + clib_warning ("you shouldn't be here, leaking buffers..."); + return frame->n_vectors; +} + +/* *INDENT-OFF* */ +VNET_DEVICE_CLASS (l2tpv3_device_class,static) = { + .name = "L2TPv3", + .format_device_name = format_l2tpv3_name, + .name_renumber = l2tpv3_name_renumber, + .tx_function = dummy_interface_tx, +}; +/* *INDENT-ON* */ + +static u8 * +format_l2tp_header_with_length (u8 * s, va_list * args) +{ + u32 dev_instance = va_arg (*args, u32); + s = format (s, "unimplemented dev %u", dev_instance); + return s; +} + +/* *INDENT-OFF* */ +VNET_HW_INTERFACE_CLASS (l2tpv3_hw_class) = { + .name = "L2TPV3", + .format_header = format_l2tp_header_with_length, + .build_rewrite = default_build_rewrite, + .flags = VNET_HW_INTERFACE_CLASS_FLAG_P2P, +}; +/* *INDENT-ON* */ + +int +create_l2tpv3_ipv6_tunnel (l2t_main_t * lm, + ip6_address_t * client_address, + ip6_address_t * our_address, + u32 local_session_id, + u32 remote_session_id, + u64 local_cookie, + u64 remote_cookie, + int l2_sublayer_present, + u32 encap_fib_index, u32 * sw_if_index) +{ + l2t_session_t *s = 0; + vnet_main_t *vnm = lm->vnet_main; + vnet_hw_interface_t *hi; + uword *p = (uword *) ~ 0; + u32 hw_if_index; + l2tpv3_header_t l2tp_hdr; + ip6_address_t *dst_address_copy, *src_address_copy; + u32 counter_index; + + remote_session_id = clib_host_to_net_u32 (remote_session_id); + local_session_id = clib_host_to_net_u32 (local_session_id); + + switch (lm->lookup_type) + { + case L2T_LOOKUP_SRC_ADDRESS: + p = hash_get_mem (lm->session_by_src_address, client_address); + break; + + case L2T_LOOKUP_DST_ADDRESS: + p = hash_get_mem (lm->session_by_dst_address, our_address); + break; + + case L2T_LOOKUP_SESSION_ID: + p = hash_get (lm->session_by_session_id, local_session_id); + break; + + default: + ASSERT (0); + } + + /* adding a session: session must not already exist */ + if (p) + return VNET_API_ERROR_INVALID_VALUE; + + pool_get (lm->sessions, s); + memset (s, 0, sizeof (*s)); + clib_memcpy (&s->our_address, our_address, sizeof (s->our_address)); + clib_memcpy (&s->client_address, client_address, + sizeof (s->client_address)); + s->local_cookie[0] = clib_host_to_net_u64 (local_cookie); + s->remote_cookie = clib_host_to_net_u64 (remote_cookie); + s->local_session_id = local_session_id; + s->remote_session_id = remote_session_id; + s->l2_sublayer_present = l2_sublayer_present; + /* precompute l2tp header size */ + s->l2tp_hdr_size = l2_sublayer_present ? + sizeof (l2tpv3_header_t) : + sizeof (l2tpv3_header_t) - sizeof (l2tp_hdr.l2_specific_sublayer); + s->admin_up = 0; + s->encap_fib_index = encap_fib_index; + + /* Setup hash table entries */ + switch (lm->lookup_type) + { + case L2T_LOOKUP_SRC_ADDRESS: + src_address_copy = clib_mem_alloc (sizeof (*src_address_copy)); + clib_memcpy (src_address_copy, client_address, + sizeof (*src_address_copy)); + hash_set_mem (lm->session_by_src_address, src_address_copy, + s - lm->sessions); + break; + case L2T_LOOKUP_DST_ADDRESS: + dst_address_copy = clib_mem_alloc (sizeof (*dst_address_copy)); + clib_memcpy (dst_address_copy, our_address, sizeof (*dst_address_copy)); + hash_set_mem (lm->session_by_dst_address, dst_address_copy, + s - lm->sessions); + break; + case L2T_LOOKUP_SESSION_ID: + hash_set (lm->session_by_session_id, local_session_id, + s - lm->sessions); + break; + + default: + ASSERT (0); + } + + /* validate counters */ + counter_index = + session_index_to_counter_index (s - lm->sessions, + SESSION_COUNTER_USER_TO_NETWORK); + vlib_validate_combined_counter (&lm->counter_main, counter_index); + vlib_validate_combined_counter (&lm->counter_main, counter_index + 1); + + if (vec_len (lm->free_l2tpv3_tunnel_hw_if_indices) > 0) + { + hw_if_index = lm->free_l2tpv3_tunnel_hw_if_indices + [vec_len (lm->free_l2tpv3_tunnel_hw_if_indices) - 1]; + _vec_len (lm->free_l2tpv3_tunnel_hw_if_indices) -= 1; + + hi = vnet_get_hw_interface (vnm, hw_if_index); + hi->dev_instance = s - lm->sessions; + hi->hw_instance = hi->dev_instance; + } + else + { + hw_if_index = vnet_register_interface + (vnm, l2tpv3_device_class.index, s - lm->sessions, + l2tpv3_hw_class.index, s - lm->sessions); + hi = vnet_get_hw_interface (vnm, hw_if_index); + hi->output_node_index = l2t_encap_node.index; + /* $$$$ initialize custom dispositions, if needed */ + } + + s->hw_if_index = hw_if_index; + s->sw_if_index = hi->sw_if_index; + + if (sw_if_index) + *sw_if_index = hi->sw_if_index; + + return 0; +} + +static clib_error_t * +create_l2tpv3_tunnel_command_fn (vlib_main_t * vm, + unformat_input_t * input, + vlib_cli_command_t * cmd) +{ + ip6_address_t client_address, our_address; + unformat_input_t _line_input, *line_input = &_line_input; + l2t_main_t *lm = &l2t_main; + u64 local_cookie = (u64) ~ 0, remote_cookie = (u64) ~ 0; + u32 local_session_id = 1, remote_session_id = 1; + int our_address_set = 0, client_address_set = 0; + int l2_sublayer_present = 0; + int rv; + u32 sw_if_index; + u32 encap_fib_id = ~0; + u32 encap_fib_index = ~0; + + /* Get a line of input. */ + if (!unformat_user (input, unformat_line_input, line_input)) + return 0; + + while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT) + { + if (unformat (line_input, "client %U", + unformat_ip6_address, &client_address)) + client_address_set = 1; + else if (unformat (line_input, "our %U", + unformat_ip6_address, &our_address)) + our_address_set = 1; + else if (unformat (line_input, "local-cookie %llx", &local_cookie)) + ; + else if (unformat (line_input, "remote-cookie %llx", &remote_cookie)) + ; + else if (unformat (line_input, "local-session-id %d", + &local_session_id)) + ; + else if (unformat (line_input, "remote-session-id %d", + &remote_session_id)) + ; + else if (unformat (line_input, "fib-id %d", &encap_fib_id)) + ; + else if (unformat (line_input, "l2-sublayer-present")) + l2_sublayer_present = 1; + else + return clib_error_return (0, "parse error: '%U'", + format_unformat_error, line_input); + } + + unformat_free (line_input); + + if (encap_fib_id != ~0) + { + uword *p; + ip6_main_t *im = &ip6_main; + if (!(p = hash_get (im->fib_index_by_table_id, encap_fib_id))) + return clib_error_return (0, "No fib with id %d", encap_fib_id); + encap_fib_index = p[0]; + } + else + { + encap_fib_index = ~0; + } + + if (our_address_set == 0) + return clib_error_return (0, "our address not specified"); + if (client_address_set == 0) + return clib_error_return (0, "client address not specified"); + + rv = create_l2tpv3_ipv6_tunnel (lm, &client_address, &our_address, + local_session_id, remote_session_id, + local_cookie, remote_cookie, + l2_sublayer_present, + encap_fib_index, &sw_if_index); + switch (rv) + { + case 0: + vlib_cli_output (vm, "%U\n", format_vnet_sw_if_index_name, + vnet_get_main (), sw_if_index); + break; + case VNET_API_ERROR_INVALID_VALUE: + return clib_error_return (0, "session already exists..."); + + case VNET_API_ERROR_NO_SUCH_ENTRY: + return clib_error_return (0, "session does not exist..."); + + default: + return clib_error_return (0, "l2tp_session_add_del returned %d", rv); + } + + return 0; +} + +/* *INDENT-OFF* */ +VLIB_CLI_COMMAND (create_l2tpv3_tunnel_command, static) = +{ + .path = "create l2tpv3 tunnel", + .short_help = + "create l2tpv3 tunnel client <ip6> our <ip6> local-cookie <hex> remote-cookie <hex> local-session <dec> remote-session <dec>", + .function = create_l2tpv3_tunnel_command_fn, +}; +/* *INDENT-ON* */ + +int +l2tpv3_set_tunnel_cookies (l2t_main_t * lm, + u32 sw_if_index, + u64 new_local_cookie, u64 new_remote_cookie) +{ + l2t_session_t *s; + vnet_hw_interface_t *hi; + vnet_main_t *vnm = vnet_get_main (); + hi = vnet_get_sup_hw_interface (vnm, sw_if_index); + + if (pool_is_free_index (lm->sessions, hi->dev_instance)) + return VNET_API_ERROR_INVALID_VALUE; + + s = pool_elt_at_index (lm->sessions, hi->dev_instance); + + s->local_cookie[1] = s->local_cookie[0]; + s->local_cookie[0] = clib_host_to_net_u64 (new_local_cookie); + s->remote_cookie = clib_host_to_net_u64 (new_remote_cookie); + + return 0; +} + + +static clib_error_t * +set_l2tp_tunnel_cookie_command_fn (vlib_main_t * vm, + unformat_input_t * input, + vlib_cli_command_t * cmd) +{ + l2t_main_t *lm = &l2t_main; + vnet_main_t *vnm = vnet_get_main (); + u32 sw_if_index = ~0; + u64 local_cookie = (u64) ~ 0, remote_cookie = (u64) ~ 0; + + int rv; + + while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) + { + if (unformat (input, "%U", unformat_vnet_sw_interface, vnm, + &sw_if_index)) + ; + else if (unformat (input, "local %llx", &local_cookie)) + ; + else if (unformat (input, "remote %llx", &remote_cookie)) + ; + else + break; + } + if (sw_if_index == ~0) + return clib_error_return (0, "unknown interface"); + if (local_cookie == ~0) + return clib_error_return (0, "local cookie required"); + if (remote_cookie == ~0) + return clib_error_return (0, "remote cookie required"); + + rv = l2tpv3_set_tunnel_cookies (lm, sw_if_index, + local_cookie, remote_cookie); + + switch (rv) + { + case 0: + break; + + case VNET_API_ERROR_INVALID_SW_IF_INDEX: + return clib_error_return (0, "invalid interface"); + + default: + return clib_error_return (0, "l2tp_session_set_cookies returned %d", + rv); + } + + return 0; +} + +/* *INDENT-OFF* */ +VLIB_CLI_COMMAND (set_l2tp_tunnel_cookie_command, static) = +{ + .path = "set l2tpv3 tunnel cookie", + .short_help = + "set l2tpv3 tunnel cookie <intfc> local <hex> remote <hex>", + .function = set_l2tp_tunnel_cookie_command_fn, +}; +/* *INDENT-ON* */ + +int +l2tpv3_interface_enable_disable (vnet_main_t * vnm, + u32 sw_if_index, int enable_disable) +{ + + if (pool_is_free_index (vnm->interface_main.sw_interfaces, sw_if_index)) + return VNET_API_ERROR_INVALID_SW_IF_INDEX; + + vnet_feature_enable_disable ("ip6-unicast", "l2tp-decap", sw_if_index, + enable_disable, 0, 0); + return 0; +} + +/* Enable/disable L2TPv3 intercept on IP6 fowarding path */ +static clib_error_t * +set_ip6_l2tpv3 (vlib_main_t * vm, + unformat_input_t * input, vlib_cli_command_t * cmd) +{ + u32 sw_if_index = ~0; + int is_add = 1; + int rv; + vnet_main_t *vnm = vnet_get_main (); + + while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) + { + if (unformat (input, "%U", unformat_vnet_sw_interface, vnm, + &sw_if_index)) + ; + else if (unformat (input, "del")) + is_add = 0; + else + break; + } + + if (sw_if_index == ~0) + return clib_error_return (0, "interface required"); + + rv = l2tpv3_interface_enable_disable (vnm, sw_if_index, is_add); + + switch (rv) + { + case 0: + break; + + case VNET_API_ERROR_INVALID_SW_IF_INDEX: + return clib_error_return (0, "invalid interface"); + + default: + return clib_error_return (0, + "l2tp_interface_enable_disable returned %d", + rv); + } + return 0; +} + +/* *INDENT-OFF* */ +VLIB_CLI_COMMAND (set_interface_ip6_l2tpv3, static) = +{ + .path = "set interface ip6 l2tpv3", + .function = set_ip6_l2tpv3, + .short_help = "set interface ip6 l2tpv3 <intfc> [del]", +}; +/* *INDENT-ON* */ + +static clib_error_t * +l2tp_config (vlib_main_t * vm, unformat_input_t * input) +{ + l2t_main_t *lm = &l2t_main; + + while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) + { + if (unformat (input, "lookup-v6-src")) + lm->lookup_type = L2T_LOOKUP_SRC_ADDRESS; + else if (unformat (input, "lookup-v6-dst")) + lm->lookup_type = L2T_LOOKUP_DST_ADDRESS; + else if (unformat (input, "lookup-session-id")) + lm->lookup_type = L2T_LOOKUP_SESSION_ID; + else + return clib_error_return (0, "unknown input `%U'", + format_unformat_error, input); + } + return 0; +} + +VLIB_CONFIG_FUNCTION (l2tp_config, "l2tp"); + + +clib_error_t * +l2tp_sw_interface_up_down (vnet_main_t * vnm, u32 sw_if_index, u32 flags) +{ + l2t_main_t *lm = &l2t_main; + vnet_hw_interface_t *hi = vnet_get_sup_hw_interface (vnm, sw_if_index); + if (hi->hw_class_index != l2tpv3_hw_class.index) + return 0; + + u32 session_index = hi->dev_instance; + l2t_session_t *s = pool_elt_at_index (lm->sessions, session_index); + s->admin_up = ! !(flags & VNET_SW_INTERFACE_FLAG_ADMIN_UP); + return 0; +} + +VNET_SW_INTERFACE_ADMIN_UP_DOWN_FUNCTION (l2tp_sw_interface_up_down); + +clib_error_t * +l2tp_init (vlib_main_t * vm) +{ + l2t_main_t *lm = &l2t_main; + ip_main_t *im = &ip_main; + ip_protocol_info_t *pi; + + lm->vnet_main = vnet_get_main (); + lm->vlib_main = vm; + lm->lookup_type = L2T_LOOKUP_DST_ADDRESS; + + lm->session_by_src_address = hash_create_mem + (0, sizeof (ip6_address_t) /* key bytes */ , + sizeof (u32) /* value bytes */ ); + lm->session_by_dst_address = hash_create_mem + (0, sizeof (ip6_address_t) /* key bytes */ , + sizeof (u32) /* value bytes */ ); + lm->session_by_session_id = hash_create (0, sizeof (uword)); + + pi = ip_get_protocol_info (im, IP_PROTOCOL_L2TP); + pi->unformat_pg_edit = unformat_pg_l2tp_header; + + /* insure these nodes are included in build */ + l2tp_encap_init (vm); + l2tp_decap_init (); + + return 0; +} + +VLIB_INIT_FUNCTION (l2tp_init); + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/src/vnet/l2tp/l2tp.h b/src/vnet/l2tp/l2tp.h new file mode 100644 index 00000000000..e7d2892cbdf --- /dev/null +++ b/src/vnet/l2tp/l2tp.h @@ -0,0 +1,147 @@ +/* + * l2tp.h : L2TPv3 tunnel support + * + * Copyright (c) 2013 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. + */ + +#ifndef __included_l2tp_h__ +#define __included_l2tp_h__ + +#include <vlib/vlib.h> +#include <vnet/ip/ip.h> +#include <vnet/l2tp/packet.h> + +typedef struct +{ + /* ip6 addresses */ + ip6_address_t our_address; + ip6_address_t client_address; + + /* l2tpv3 header parameters */ + u64 local_cookie[2]; + u64 remote_cookie; + u32 local_session_id; + u32 remote_session_id; + + /* tunnel interface */ + u32 hw_if_index; + u32 sw_if_index; + + /* fib index used for outgoing encapsulated packets */ + u32 encap_fib_index; + + u8 l2tp_hdr_size; + u8 l2_sublayer_present; + u8 cookie_flags; /* in host byte order */ + + u8 admin_up; +} l2t_session_t; + +typedef enum +{ + L2T_LOOKUP_SRC_ADDRESS = 0, + L2T_LOOKUP_DST_ADDRESS, + L2T_LOOKUP_SESSION_ID, +} ip6_to_l2_lookup_t; + +typedef struct +{ + /* session pool */ + l2t_session_t *sessions; + + /* ip6 -> l2 hash tables. Make up your minds, people... */ + uword *session_by_src_address; + uword *session_by_dst_address; + uword *session_by_session_id; + + ip6_to_l2_lookup_t lookup_type; + + /* Counters */ + vlib_combined_counter_main_t counter_main; + + /* vector of free l2tpv3 tunnel interfaces */ + u32 *free_l2tpv3_tunnel_hw_if_indices; + + /* show device instance by real device instance */ + u32 *dev_inst_by_real; + + /* convenience */ + vlib_main_t *vlib_main; + vnet_main_t *vnet_main; + +} l2t_main_t; + +/* Packet trace structure */ +typedef struct +{ + int is_user_to_network; + u32 session_index; + ip6_address_t our_address; + ip6_address_t client_address; +} l2t_trace_t; + +l2t_main_t l2t_main; +extern vlib_node_registration_t l2t_encap_node; +extern vlib_node_registration_t l2t_decap_node; +extern vlib_node_registration_t l2t_decap_local_node; + +enum +{ + SESSION_COUNTER_USER_TO_NETWORK = 0, + SESSION_COUNTER_NETWORK_TO_USER, +}; + +static inline u32 +session_index_to_counter_index (u32 session_index, u32 counter_id) +{ + return ((session_index << 1) + counter_id); +} + +u8 *format_l2t_trace (u8 * s, va_list * args); + +typedef struct +{ + /* Any per-interface config would go here */ +} ip6_l2tpv3_config_t; + +uword unformat_pg_l2tp_header (unformat_input_t * input, va_list * args); + +void l2tp_encap_init (vlib_main_t * vm); +void l2tp_decap_init (void); +int create_l2tpv3_ipv6_tunnel (l2t_main_t * lm, + ip6_address_t * client_address, + ip6_address_t * our_address, + u32 local_session_id, + u32 remote_session_id, + u64 local_cookie, + u64 remote_cookie, + int l2_sublayer_present, + u32 encap_fib_index, u32 * sw_if_index); + +int l2tpv3_set_tunnel_cookies (l2t_main_t * lm, + u32 sw_if_index, + u64 new_local_cookie, u64 new_remote_cookie); + +int l2tpv3_interface_enable_disable (vnet_main_t * vnm, + u32 sw_if_index, int enable_disable); + +#endif /* __included_l2tp_h__ */ + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/src/vnet/l2tp/l2tp_api.c b/src/vnet/l2tp/l2tp_api.c new file mode 100644 index 00000000000..88d758c9ff0 --- /dev/null +++ b/src/vnet/l2tp/l2tp_api.c @@ -0,0 +1,267 @@ +/* + *------------------------------------------------------------------ + * l2tp_api.c - l2tpv3 api + * + * 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. + *------------------------------------------------------------------ + */ + +#include <vnet/vnet.h> +#include <vlibmemory/api.h> + +#include <vnet/interface.h> +#include <vnet/api_errno.h> +#include <vnet/l2tp/l2tp.h> + +#include <vnet/vnet_msg_enum.h> + +#define vl_typedefs /* define message structures */ +#include <vnet/vnet_all_api_h.h> +#undef vl_typedefs + +#define vl_endianfun /* define message structures */ +#include <vnet/vnet_all_api_h.h> +#undef vl_endianfun + +/* instantiate all the print functions we know about */ +#define vl_print(handle, ...) vlib_cli_output (handle, __VA_ARGS__) +#define vl_printfun +#include <vnet/vnet_all_api_h.h> +#undef vl_printfun + +#include <vlibapi/api_helper_macros.h> + +#define foreach_vpe_api_msg \ +_(L2TPV3_CREATE_TUNNEL, l2tpv3_create_tunnel) \ +_(L2TPV3_SET_TUNNEL_COOKIES, l2tpv3_set_tunnel_cookies) \ +_(L2TPV3_INTERFACE_ENABLE_DISABLE, l2tpv3_interface_enable_disable) \ +_(L2TPV3_SET_LOOKUP_KEY, l2tpv3_set_lookup_key) \ +_(SW_IF_L2TPV3_TUNNEL_DUMP, sw_if_l2tpv3_tunnel_dump) + +static void +send_sw_if_l2tpv3_tunnel_details (vpe_api_main_t * am, + unix_shared_memory_queue_t * q, + l2t_session_t * s, + l2t_main_t * lm, u32 context) +{ + vl_api_sw_if_l2tpv3_tunnel_details_t *mp; + u8 *if_name = NULL; + vnet_sw_interface_t *si = NULL; + + si = vnet_get_hw_sw_interface (lm->vnet_main, s->hw_if_index); + + if_name = format (if_name, "%U", + format_vnet_sw_interface_name, lm->vnet_main, si); + + mp = vl_msg_api_alloc (sizeof (*mp)); + memset (mp, 0, sizeof (*mp)); + mp->_vl_msg_id = ntohs (VL_API_SW_IF_L2TPV3_TUNNEL_DETAILS); + strncpy ((char *) mp->interface_name, + (char *) if_name, ARRAY_LEN (mp->interface_name) - 1); + mp->sw_if_index = ntohl (si->sw_if_index); + mp->local_session_id = s->local_session_id; + mp->remote_session_id = s->remote_session_id; + mp->local_cookie[0] = s->local_cookie[0]; + mp->local_cookie[1] = s->local_cookie[1]; + mp->remote_cookie = s->remote_cookie; + clib_memcpy (mp->client_address, &s->client_address, + sizeof (s->client_address)); + clib_memcpy (mp->our_address, &s->our_address, sizeof (s->our_address)); + mp->l2_sublayer_present = s->l2_sublayer_present; + mp->context = context; + + vl_msg_api_send_shmem (q, (u8 *) & mp); +} + + +static void +vl_api_sw_if_l2tpv3_tunnel_dump_t_handler (vl_api_sw_if_l2tpv3_tunnel_dump_t * + mp) +{ + vpe_api_main_t *am = &vpe_api_main; + l2t_main_t *lm = &l2t_main; + unix_shared_memory_queue_t *q; + l2t_session_t *session; + + q = vl_api_client_index_to_input_queue (mp->client_index); + if (q == 0) + return; + + /* *INDENT-OFF* */ + pool_foreach (session, lm->sessions, + ({ + send_sw_if_l2tpv3_tunnel_details (am, q, session, lm, mp->context); + })); + /* *INDENT-ON* */ +} + +static void vl_api_l2tpv3_create_tunnel_t_handler + (vl_api_l2tpv3_create_tunnel_t * mp) +{ + vl_api_l2tpv3_create_tunnel_reply_t *rmp; + l2t_main_t *lm = &l2t_main; + u32 sw_if_index = (u32) ~ 0; + int rv; + + if (mp->is_ipv6 != 1) + { + rv = VNET_API_ERROR_UNIMPLEMENTED; + goto out; + } + + u32 encap_fib_index; + + if (mp->encap_vrf_id != ~0) + { + uword *p; + ip6_main_t *im = &ip6_main; + if (! + (p = + hash_get (im->fib_index_by_table_id, ntohl (mp->encap_vrf_id)))) + { + rv = VNET_API_ERROR_NO_SUCH_FIB; + goto out; + } + encap_fib_index = p[0]; + } + else + { + encap_fib_index = ~0; + } + + rv = create_l2tpv3_ipv6_tunnel (lm, + (ip6_address_t *) mp->client_address, + (ip6_address_t *) mp->our_address, + ntohl (mp->local_session_id), + ntohl (mp->remote_session_id), + clib_net_to_host_u64 (mp->local_cookie), + clib_net_to_host_u64 (mp->remote_cookie), + mp->l2_sublayer_present, + encap_fib_index, &sw_if_index); + +out: + /* *INDENT-OFF* */ + REPLY_MACRO2(VL_API_L2TPV3_CREATE_TUNNEL_REPLY, + ({ + rmp->sw_if_index = ntohl (sw_if_index); + })); + /* *INDENT-ON* */ +} + +static void vl_api_l2tpv3_set_tunnel_cookies_t_handler + (vl_api_l2tpv3_set_tunnel_cookies_t * mp) +{ + vl_api_l2tpv3_set_tunnel_cookies_reply_t *rmp; + l2t_main_t *lm = &l2t_main; + int rv; + + VALIDATE_SW_IF_INDEX (mp); + + rv = l2tpv3_set_tunnel_cookies (lm, ntohl (mp->sw_if_index), + clib_net_to_host_u64 (mp->new_local_cookie), + clib_net_to_host_u64 + (mp->new_remote_cookie)); + + BAD_SW_IF_INDEX_LABEL; + + REPLY_MACRO (VL_API_L2TPV3_SET_TUNNEL_COOKIES_REPLY); +} + +static void vl_api_l2tpv3_interface_enable_disable_t_handler + (vl_api_l2tpv3_interface_enable_disable_t * mp) +{ + int rv; + vnet_main_t *vnm = vnet_get_main (); + vl_api_l2tpv3_interface_enable_disable_reply_t *rmp; + + VALIDATE_SW_IF_INDEX (mp); + + rv = l2tpv3_interface_enable_disable + (vnm, ntohl (mp->sw_if_index), mp->enable_disable); + + BAD_SW_IF_INDEX_LABEL; + + REPLY_MACRO (VL_API_L2TPV3_INTERFACE_ENABLE_DISABLE_REPLY); +} + +static void vl_api_l2tpv3_set_lookup_key_t_handler + (vl_api_l2tpv3_set_lookup_key_t * mp) +{ + int rv = 0; + l2t_main_t *lm = &l2t_main; + vl_api_l2tpv3_set_lookup_key_reply_t *rmp; + + if (mp->key > L2T_LOOKUP_SESSION_ID) + { + rv = VNET_API_ERROR_INVALID_VALUE; + goto out; + } + + lm->lookup_type = mp->key; + +out: + REPLY_MACRO (VL_API_L2TPV3_SET_LOOKUP_KEY_REPLY); +} + +/* + * l2tp_api_hookup + * Add vpe's API message handlers to the table. + * vlib has alread mapped shared memory and + * added the client registration handlers. + * See .../vlib-api/vlibmemory/memclnt_vlib.c:memclnt_process() + */ +#define vl_msg_name_crc_list +#include <vnet/vnet_all_api_h.h> +#undef vl_msg_name_crc_list + +static void +setup_message_id_table (api_main_t * am) +{ +#define _(id,n,crc) vl_msg_api_add_msg_name_crc (am, #n "_" #crc, id); + foreach_vl_msg_name_crc_l2tp; +#undef _ +} + +static clib_error_t * +l2tp_api_hookup (vlib_main_t * vm) +{ + api_main_t *am = &api_main; + +#define _(N,n) \ + vl_msg_api_set_handlers(VL_API_##N, #n, \ + vl_api_##n##_t_handler, \ + vl_noop_handler, \ + vl_api_##n##_t_endian, \ + vl_api_##n##_t_print, \ + sizeof(vl_api_##n##_t), 1); + foreach_vpe_api_msg; +#undef _ + + /* + * Set up the (msg_name, crc, message-id) table + */ + setup_message_id_table (am); + + return 0; +} + +VLIB_API_INIT_FUNCTION (l2tp_api_hookup); + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/src/vnet/l2tp/packet.h b/src/vnet/l2tp/packet.h new file mode 100644 index 00000000000..66dfea2194c --- /dev/null +++ b/src/vnet/l2tp/packet.h @@ -0,0 +1,44 @@ +/* + * packet.h : L2TPv3 packet header format + * + * Copyright (c) 2013 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. + */ + +#ifndef __included_l2tp_packet_h__ +#define __included_l2tp_packet_h__ + +/* + * See RFC4719 for packet format. + * Note: the l2_specific_sublayer is present in current Linux l2tpv3 + * tunnels. It is not present in IOS XR l2tpv3 tunnels. + * The Linux implementation is almost certainly wrong. + */ +/* *INDENT-OFF* */ +typedef CLIB_PACKED (struct +{ + u32 session_id; + u64 cookie; u32 + l2_specific_sublayer; /* set to 0 (if present) */ +}) l2tpv3_header_t; +/* *INDENT-ON* */ + +#endif /* __included_l2tp_packet_h__ */ + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/src/vnet/l2tp/pg.c b/src/vnet/l2tp/pg.c new file mode 100644 index 00000000000..1e523d3bbb0 --- /dev/null +++ b/src/vnet/l2tp/pg.c @@ -0,0 +1,106 @@ +/* + * pg.c: packet generator for L2TPv3 header + * + * Copyright (c) 2013 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. + */ + +#include <vlib/vlib.h> +#include <vnet/pg/pg.h> +#include <vnet/l2tp/l2tp.h> + +typedef struct +{ + pg_edit_t session_id; + pg_edit_t cookie; +} pg_l2tp_header_t; + +typedef struct +{ + pg_edit_t l2_sublayer; +} pg_l2tp_header_l2_sublayer_t; + +static inline void +pg_l2tp_header_init (pg_l2tp_header_t * e) +{ + pg_edit_init (&e->session_id, l2tpv3_header_t, session_id); + pg_edit_init (&e->cookie, l2tpv3_header_t, cookie); +} + +uword +unformat_pg_l2tp_header (unformat_input_t * input, va_list * args) +{ + pg_stream_t *s = va_arg (*args, pg_stream_t *); + pg_l2tp_header_t *h; + u32 group_index, error; + vlib_main_t *vm = vlib_get_main (); + + h = pg_create_edit_group (s, sizeof (h[0]), + sizeof (l2tpv3_header_t) - sizeof (u32), + &group_index); + pg_l2tp_header_init (h); + + error = 1; + + /* session id and cookie are required */ + if (!unformat (input, "L2TP: session_id %U cookie %U", + unformat_pg_edit, unformat_pg_number, &h->session_id, + unformat_pg_edit, unformat_pg_number, &h->cookie)) + { + goto done; + } + + /* "l2_sublayer <value>" is optional */ + if (unformat (input, "l2_sublayer")) + { + pg_l2tp_header_l2_sublayer_t *h2; + + h2 = pg_add_edits (s, sizeof (h2[0]), sizeof (u32), group_index); + pg_edit_init (&h2->l2_sublayer, l2tpv3_header_t, l2_specific_sublayer); + if (!unformat_user (input, unformat_pg_edit, + unformat_pg_number, &h2->l2_sublayer)) + { + goto done; + } + } + + /* Parse an ethernet header if it is present */ + { + pg_node_t *pg_node = 0; + vlib_node_t *eth_lookup_node; + + eth_lookup_node = vlib_get_node_by_name (vm, (u8 *) "ethernet-input"); + ASSERT (eth_lookup_node); + + pg_node = pg_get_node (eth_lookup_node->index); + + if (pg_node && pg_node->unformat_edit + && unformat_user (input, pg_node->unformat_edit, s)) + ; + } + + error = 0; + +done: + if (error) + pg_free_edit_group (s); + return error == 0; +} + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ |