Initial commit of vpp code.
[vpp.git] / vpp / app / l2t_ip6.c
1 /*
2  * Copyright (c) 2015 Cisco and/or its affiliates.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at:
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 #include <vnet/vnet.h>
16 #include <vnet/ip/ip.h>
17 #include <vnet/ethernet/ethernet.h>
18
19 #if DPDK == 0
20 #include <vnet/devices/pci/ixgev.h>
21 #include <vnet/devices/pci/ixge.h>
22 #include <vnet/devices/pci/ige.h>
23 #include <vnet/devices/pci/vice.h>
24 #else
25 #include <vnet/devices/dpdk/dpdk.h>
26 #endif
27
28 #include <vppinfra/error.h>
29 #include <vppinfra/hash.h>
30 #include <app/l2t.h>
31
32 l2t_main_t l2t_main;
33
34 /* Statistics (not really errors) */
35 #define foreach_l2t_ip6_error                                   \
36 _(USER_TO_NETWORK, "User (v6) to L2 network pkts")              \
37 _(SESSION_ID_MISMATCH, "l2tpv3 local session id mismatches")    \
38 _(COOKIE_MISMATCH, "l2tpv3 local cookie mismatches")  
39
40 static char * l2t_ip6_error_strings[] = {
41 #define _(sym,string) string,
42   foreach_l2t_ip6_error
43 #undef _
44 };
45
46 typedef enum {
47 #define _(sym,str) L2T_IP6_ERROR_##sym,
48     foreach_l2t_ip6_error
49 #undef _
50     L2T_IP6_N_ERROR,
51 } l2t_ip6_error_t;
52
53 /*
54  * Packets go to ip6-input when they don't match a mapping, 
55  * example: v6 neighbor discovery. They go to ip4-input
56  * when they do match, and are decapsulated.
57  */
58 typedef enum { 
59     L2T_IP6_NEXT_DROP,
60     L2T_IP6_NEXT_IP6_INPUT,
61     L2T_IP6_N_NEXT,
62     /* Pseudo next, fixed in last_stage */
63     L2T_IP6_NEXT_OUTPUT = L2T_IP6_N_NEXT,
64 } l2t_ip6_next_t;
65
66 vlib_node_registration_t l2t_ip6_node;
67
68 #define NSTAGES 3
69
70 static inline void stage0 (vlib_main_t * vm,
71                            vlib_node_runtime_t * node,
72                            u32 buffer_index)
73 {
74     vlib_buffer_t *b = vlib_get_buffer (vm, buffer_index);
75     vlib_prefetch_buffer_header (b, STORE);
76     /* l2tpv3 header is a long way away, need 2 cache lines */
77     CLIB_PREFETCH (b->data, 2*CLIB_CACHE_LINE_BYTES, STORE);
78 }
79
80 static inline void stage1 (vlib_main_t * vm,
81                            vlib_node_runtime_t * node,
82                            u32 bi)
83 {
84     vlib_buffer_t *b = vlib_get_buffer (vm, bi);
85     l2t_main_t *lm = &l2t_main;
86     ip6_header_t * ip6 = vlib_buffer_get_current (b);
87     u32 session_index;
88     uword *p;
89     l2tpv3_header_t * l2t;
90
91     /* Not L2tpv3 (0x73, 0t115)? Use the normal path. */
92     if (PREDICT_FALSE(ip6->protocol != 0x73)) {
93         vnet_buffer(b)->l2t.next_index = L2T_IP6_NEXT_IP6_INPUT;
94         return;
95     }
96
97     /* Make up your minds, people... */
98     switch (lm->lookup_type) {
99     case L2T_LOOKUP_SRC_ADDRESS:
100         p = hash_get_mem (lm->session_by_src_address, &ip6->src_address);
101         break;
102     case L2T_LOOKUP_DST_ADDRESS:
103         p = hash_get_mem (lm->session_by_dst_address, &ip6->dst_address);
104         break;
105     case L2T_LOOKUP_SESSION_ID:
106         l2t = (l2tpv3_header_t*)(ip6+1);
107         p = hash_get (lm->session_by_session_id, l2t->session_id);
108         break;
109     default:
110         ASSERT(0);
111     }
112
113     if (PREDICT_FALSE(p == 0)) {
114         vnet_buffer(b)->l2t.next_index = L2T_IP6_NEXT_IP6_INPUT;
115         return;
116     } else {
117         session_index = p[0];
118     }
119
120     /* Remember mapping index, prefetch the mini counter */
121     vnet_buffer(b)->l2t.next_index = L2T_IP6_NEXT_OUTPUT;
122     vnet_buffer(b)->l2t.session_index = session_index;
123
124     /* Each mapping has 2 x (pkt, byte) counters, hence the shift */
125     CLIB_PREFETCH(lm->counter_main.mini + (p[0]<<1), CLIB_CACHE_LINE_BYTES,
126                   STORE);
127 }
128
129 static inline u32 last_stage (vlib_main_t *vm, vlib_node_runtime_t *node,
130                               u32 bi)
131 {
132     vlib_buffer_t *b = vlib_get_buffer (vm, bi);
133     l2t_main_t *lm = &l2t_main;
134     ip6_header_t * ip6 = vlib_buffer_get_current (b);
135     vlib_node_t *n = vlib_get_node (vm, l2t_ip6_node.index);
136     u32 node_counter_base_index = n->error_heap_index;
137     vlib_error_main_t * em = &vm->error_main;
138     ethernet_header_t * l2_payload;
139     l2tpv3_header_t * l2t;      /* original l2 header */
140     ethernet_vlan_header_t * vh; /* synthesized 802.1q vlan header */
141     u32 counter_index;
142     l2t_session_t * session;
143     u16 payload_ethertype;
144     u8 dst_mac_address[6];
145     u8 src_mac_address[6];
146     u8 *vlan_header_pos;
147     
148     /* Other-than-output pkt? We're done... */
149     if (vnet_buffer(b)->l2t.next_index != L2T_IP6_NEXT_OUTPUT) 
150         return vnet_buffer(b)->l2t.next_index;
151
152     em->counters[node_counter_base_index + L2T_IP6_ERROR_USER_TO_NETWORK] += 1;
153     
154     counter_index = 
155         session_index_to_counter_index (vnet_buffer(b)->l2t.session_index,
156                                         SESSION_COUNTER_USER_TO_NETWORK);
157
158     /* per-mapping byte stats include the ethernet header */
159     vlib_increment_combined_counter (&lm->counter_main, counter_index,
160                                      1 /* packet_increment */,
161                                      vlib_buffer_length_in_chain (vm, b) +
162                                      sizeof (ethernet_header_t));
163     
164     session = pool_elt_at_index (lm->sessions, 
165                                  vnet_buffer(b)->l2t.session_index);
166
167     /* build the 802.1q encaps. Advance past ip6 and l2tpv3 hds */
168     vlib_buffer_advance (b, sizeof (*ip6));
169     l2t = vlib_buffer_get_current (b);
170     
171     /* $$$ wonder if we really need these checks... */
172     if (PREDICT_FALSE(l2t->session_id != session->local_session_id)) {
173           b->error = lm->ip6_error_node->
174               errors[L2T_IP6_ERROR_SESSION_ID_MISMATCH];
175           return L2T_IP6_NEXT_DROP;
176     }
177
178     if (PREDICT_FALSE(!((l2t->cookie == session->local_cookie) || 
179                         ((session->cookie_flags & L2TP_COOKIE_ROLLOVER_LOCAL) &&
180                          (l2t->cookie == session->lcl_ro_cookie))))) {
181           b->error = lm->ip6_error_node->
182               errors[L2T_IP6_ERROR_COOKIE_MISMATCH];
183           return L2T_IP6_NEXT_DROP;
184     }
185
186     vnet_buffer(b)->sw_if_index[VLIB_TX] = session->l2_output_sw_if_index;
187
188     vlib_buffer_advance (b, sizeof (*l2t));
189
190     /* point at currrent l2 hdr */
191     l2_payload = vlib_buffer_get_current (b);
192
193     /* $$$$ rework for speed */
194
195     /* Save type */
196     payload_ethertype = l2_payload->type;
197
198     /* Save src/dst MAC addresses */
199 #define _(i)  dst_mac_address[i] = l2_payload->dst_address[i];
200     _(0) _(1) _(2) _(3) _(4) _(5);
201 #undef _
202 #define _(i)  src_mac_address[i] = l2_payload->src_address[i];
203     _(0) _(1) _(2) _(3) _(4) _(5);
204 #undef _
205
206     /* Punch in space for 802.1q tag */
207     vlib_buffer_advance (b, -4);
208     l2_payload = vlib_buffer_get_current (b);
209
210     /* Restore MAC addresses */
211 #define _(i)  l2_payload->dst_address[i] = dst_mac_address[i];
212     _(0) _(1) _(2) _(3) _(4) _(5);
213 #undef _
214 #define _(i)  l2_payload->src_address[i] = src_mac_address[i];
215     _(0) _(1) _(2) _(3) _(4) _(5);
216 #undef _
217     /* Set (outer) ethertype to 802.1q vlan */
218     l2_payload->type = clib_host_to_net_u16 (0x8100);
219     vlan_header_pos = (u8 *)(l2_payload+1);
220 #if 0
221     vlan_header_pos = session->l2_sublayer_present ? 
222         vlan_header_pos : vlan_header_pos - 4;
223 #endif
224     vh = (ethernet_vlan_header_t *)vlan_header_pos;
225     vh->priority_cfi_and_id = session->vlan_id;
226     vh->type = payload_ethertype;
227
228     if (PREDICT_FALSE(b->flags & VLIB_BUFFER_IS_TRACED)) {
229         l2t_trace_t *t = vlib_add_trace (vm, node, b, sizeof (*t));
230         t->is_user_to_network = 1;
231         t->our_address.as_u64[0] = 
232             ip6->dst_address.as_u64[0];
233         t->our_address.as_u64[1] = 
234             ip6->dst_address.as_u64[1];
235         t->client_address.as_u64[0] = 
236             ip6->src_address.as_u64[0];
237         t->client_address.as_u64[1] = 
238             ip6->src_address.as_u64[1];
239         t->session_index = vnet_buffer(b)->l2t.session_index;
240         t->vlan_id_host_byte_order = clib_net_to_host_u16 (session->vlan_id);
241     }
242
243     return session->l2_output_next_index;
244 }
245
246 #include <vnet/pipeline.h>
247
248 static uword ip6_l2t_node_fn (vlib_main_t * vm,
249                               vlib_node_runtime_t * node,
250                               vlib_frame_t * frame)
251 {
252     l2t_main_t *lm = &l2t_main;
253     lm->ip6_error_node = vlib_node_get_runtime (vm, l2t_ip6_node.index);
254
255     return dispatch_pipeline (vm, node, frame);
256 }
257
258 static VLIB_REGISTER_NODE (sw6_ip6_node) = {
259   .function = ip6_l2t_node_fn,
260   .name = "ip6-l2t-input",
261   .vector_size = sizeof (u32),
262   .format_trace = format_l2t_trace,
263   .type = VLIB_NODE_TYPE_INTERNAL,
264   
265   .n_errors = ARRAY_LEN(l2t_ip6_error_strings),
266   .error_strings = l2t_ip6_error_strings,
267
268   .n_next_nodes = L2T_IP6_N_NEXT,
269
270   /* edit / add dispositions here */
271   .next_nodes = {
272         [L2T_IP6_NEXT_IP6_INPUT] = "ip6-input",
273         [L2T_IP6_NEXT_DROP] = "error-drop",
274   },
275 };
276
277 static clib_error_t *
278 l2tp_config (vlib_main_t * vm, unformat_input_t * input)
279 {
280     l2t_main_t *lm = &l2t_main;
281     
282     while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) {
283         if (unformat (input, "lookup-v6-src"))
284             lm->lookup_type = L2T_LOOKUP_SRC_ADDRESS;
285         else if (unformat (input, "lookup-v6-dst"))
286             lm->lookup_type = L2T_LOOKUP_DST_ADDRESS;
287         else if (unformat (input, "lookup-session-id"))
288             lm->lookup_type = L2T_LOOKUP_SESSION_ID;
289         else return clib_error_return (0, "unknown input `%U'",
290                                        format_unformat_error, input);
291     }
292     return 0;
293 }
294
295 VLIB_CONFIG_FUNCTION (l2tp_config, "l2tp");
296
297 clib_error_t *
298 l2t_ip6_init (vlib_main_t *vm)
299 {
300     l2t_main_t *lm = &l2t_main;
301
302     lm->vnet_main = vnet_get_main();
303     lm->vlib_main = vm;
304     lm->lookup_type = L2T_LOOKUP_DST_ADDRESS;
305
306     lm->session_by_src_address = hash_create_mem 
307         (0, sizeof (ip6_address_t) /* key bytes */,
308          sizeof (u32) /* value bytes */);
309     lm->session_by_dst_address = hash_create_mem 
310         (0, sizeof (ip6_address_t) /* key bytes */,
311          sizeof (u32) /* value bytes */);
312     lm->session_by_session_id = hash_create (0, sizeof (uword));
313
314     lm->session_by_vlan_and_rx_sw_if_index = hash_create (0, sizeof (uword));
315     
316 #if DPDK == 0
317     vice_set_next_node (VICE_RX_NEXT_IP6_INPUT, "ip6-l2t-input");
318     ixgev_set_next_node (IXGEV_RX_NEXT_IP6_INPUT, "ip6-l2t-input");
319     ixge_set_next_node (IXGE_RX_NEXT_IP6_INPUT, "ip6-l2t-input");
320     ige_set_next_node (IGE_RX_NEXT_IP6_INPUT, "ip6-l2t-input");
321 #else
322     dpdk_set_next_node (DPDK_RX_NEXT_IP6_INPUT, "ip6-l2t-input");
323 #endif
324     return 0;
325 }
326
327 VLIB_INIT_FUNCTION (l2t_ip6_init);