Initial commit of vpp code.
[vpp.git] / vpp / app / l2t.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/ixge.h>
21 #else
22 #include <vnet/devices/dpdk/dpdk.h>
23 #endif
24
25 #include <vppinfra/error.h>
26 #include <vppinfra/hash.h>
27 #include <app/l2t.h>
28
29 l2t_main_t l2t_main;
30
31 /* $$$$ unused? 
32  * get_interface_ethernet_address
33  * paints the ethernet address for a given interface
34  * into the supplied destination
35  */
36 void get_interface_ethernet_address (l2t_main_t *lm, 
37                                      u8 *dst, u32 sw_if_index)
38 {
39     ethernet_main_t *em = ethernet_get_main (lm->vlib_main);
40     ethernet_interface_t *ei;
41     vnet_hw_interface_t *hi;
42
43     hi = vnet_get_sup_hw_interface (lm->vnet_main, sw_if_index);
44     ei = pool_elt_at_index (em->interfaces, hi->hw_instance);
45     memcpy (dst, ei->address, sizeof (ei->address));
46 }
47
48 /* packet trace format function */
49 u8 * format_l2t_trace (u8 * s, va_list * args)
50 {
51   CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
52   CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
53   l2t_trace_t * t = va_arg (*args, l2t_trace_t *);
54     
55   if (t->is_user_to_network)
56       s = format (s, "L2T: %U (client) -> %U (our) session %d", 
57                   format_ip6_address, &t->client_address,
58                   format_ip6_address, &t->our_address,
59                   t->session_index);
60   else
61       s = format (s, "L2T: %U (our) -> %U (client) session %d)",
62                   format_ip6_address, &t->our_address,
63                   format_ip6_address, &t->client_address,
64                   t->session_index);
65   return s;
66 }
67
68 u8 * format_l2t_session (u8 * s, va_list * args)
69 {
70   l2t_session_t * session = va_arg (*args, l2t_session_t *);
71   l2t_main_t * lm = &l2t_main;
72   u32 counter_index;
73   vlib_counter_t v;
74
75   s = format (s, "[%d] %U (our) %U (client) vlan-id %d rx_sw_if_index %d\n", 
76               session - lm->sessions,
77               format_ip6_address, &session->our_address,
78               format_ip6_address, &session->client_address,
79               clib_net_to_host_u16(session->vlan_id), session->sw_if_index);
80
81   s = format (s, "   local cookie %llx remote cookie %llx\n",
82               clib_net_to_host_u64 (session->local_cookie),
83               clib_net_to_host_u64 (session->remote_cookie));
84
85   if (session->cookie_flags & L2TP_COOKIE_ROLLOVER_LOCAL) {
86       s = format (s, "   local rollover cookie %llx\n", 
87                   clib_net_to_host_u64 (session->lcl_ro_cookie));
88   }
89
90   s = format (s, "   local session-id %d remote session-id %d\n",
91               clib_net_to_host_u32 (session->local_session_id),
92               clib_net_to_host_u32 (session->remote_session_id));
93
94   s = format (s, "   l2 specific sublayer %s\n", 
95               session->l2_sublayer_present ? "preset" : "absent");
96
97   counter_index = 
98       session_index_to_counter_index (session - lm->sessions,
99                                       SESSION_COUNTER_USER_TO_NETWORK);
100
101   vlib_get_combined_counter (&lm->counter_main, counter_index, &v);
102   if (v.packets != 0)
103       s = format (s, "   user-to-net: %llu pkts %llu bytes\n",
104                   v.packets, v.bytes);
105
106   vlib_get_combined_counter (&lm->counter_main, counter_index+1, &v);
107
108   if (v.packets != 0)
109       s = format (s, "   net-to-user: %llu pkts %llu bytes\n",
110                   v.packets, v.bytes);
111   return s;
112 }
113
114 static clib_error_t *
115 show_session_summary_command_fn (vlib_main_t * vm,
116                                 unformat_input_t * input,
117                                 vlib_cli_command_t * cmd)
118 {
119     l2t_main_t *lm = &l2t_main;
120     
121     vlib_cli_output (vm, "%d active sessions\n", pool_elts (lm->sessions));
122
123     return 0;
124 }
125
126 static VLIB_CLI_COMMAND (show_session_summary_command) = {
127     .path = "show session",
128     .short_help = "show session summary",
129     .function = show_session_summary_command_fn,
130 };
131
132 static clib_error_t *
133 show_session_detail_command_fn (vlib_main_t * vm,
134                                 unformat_input_t * input,
135                                 vlib_cli_command_t * cmd)
136 {
137     l2t_session_t *session;
138     l2t_main_t *lm = &l2t_main;
139
140     pool_foreach (session, lm->sessions, 
141     ({
142         vlib_cli_output (vm, "%U", format_l2t_session, session);
143     }));
144
145     return 0;
146 }
147
148 static VLIB_CLI_COMMAND (show_session_detail_command) = {
149     .path = "show session detail",
150     .short_help = "show session table detail",
151     .function = show_session_detail_command_fn,
152 };
153
154 static clib_error_t *
155 test_counters_command_fn (vlib_main_t * vm,
156                           unformat_input_t * input,
157                           vlib_cli_command_t * cmd)
158 {
159     l2t_session_t *session;
160     l2t_main_t *lm = &l2t_main;
161     u32 session_index;
162     u32 counter_index;
163     u32 nincr=0;
164
165     pool_foreach (session, lm->sessions, 
166     ({
167         session_index = session - lm->sessions;
168         counter_index = 
169             session_index_to_counter_index (session_index, 
170                                             SESSION_COUNTER_USER_TO_NETWORK);
171         vlib_increment_combined_counter (&lm->counter_main,
172                                          counter_index, 
173                                          1/*pkt*/, 1111 /*bytes*/);
174         vlib_increment_combined_counter (&lm->counter_main,
175                                          counter_index+1, 
176                                          1/*pkt*/, 2222 /*bytes*/);
177         nincr++;
178         
179     }));
180     vlib_cli_output (vm, "Incremented %d active counters\n", nincr);
181
182     return 0;
183 }
184
185 static VLIB_CLI_COMMAND (test_counters_command) = {
186     .path = "test counters",
187     .short_help = "increment all active counters",
188     .function = test_counters_command_fn,
189 };
190
191 static clib_error_t *
192 clear_counters_command_fn (vlib_main_t * vm,
193                           unformat_input_t * input,
194                           vlib_cli_command_t * cmd)
195 {
196     l2t_session_t *session;
197     l2t_main_t *lm = &l2t_main;
198     u32 session_index;
199     u32 counter_index;
200     u32 nincr=0;
201
202     pool_foreach (session, lm->sessions, 
203     ({
204         session_index = session - lm->sessions;
205         counter_index = 
206             session_index_to_counter_index (session_index, 
207                                             SESSION_COUNTER_USER_TO_NETWORK);
208         vlib_zero_combined_counter (&lm->counter_main, counter_index);
209         vlib_zero_combined_counter (&lm->counter_main, counter_index+1);
210         nincr++;
211         
212     }));
213     vlib_cli_output (vm, "Cleared %d active counters\n", nincr);
214
215     return 0;
216 }
217
218 static VLIB_CLI_COMMAND (clear_counters_command) = {
219     .path = "clear counters",
220     .short_help = "clear all active counters",
221     .function = clear_counters_command_fn,
222 };
223
224 static clib_error_t *
225 l2tp_session_add_command_fn (vlib_main_t * vm,
226                              unformat_input_t * input,
227                              vlib_cli_command_t * cmd)
228 {
229     ip6_address_t client_address, our_address;
230     ip6_address_t * dst_address_copy, * src_address_copy;
231     unformat_input_t _line_input, * line_input = &_line_input;
232     u32 vlan_id;
233     u32 sw_if_index = (u32)~0;
234     l2t_main_t *lm = &l2t_main;
235     l2t_session_t *s;
236     uword *p;
237     vnet_hw_interface_t * hi;
238     vnet_sw_interface_t * si;
239     u32 next_index;
240     uword vlan_and_sw_if_index_key;
241     u32 counter_index;
242     u64 local_cookie = (u64)~0, remote_cookie = (u64)~0;
243     u32 local_session_id = 1, remote_session_id = 1;
244     int our_address_set = 0, client_address_set = 0;
245     int l2_sublayer_present = 0;
246
247     /* Get a line of input. */
248     if (! unformat_user (input, unformat_line_input, line_input))
249         return 0;
250
251     while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT) {
252         if (unformat (line_input, "client %U", 
253                       unformat_ip6_address, &client_address))
254             client_address_set = 1;
255         else if (unformat (line_input, "our %U",
256                            unformat_ip6_address, &our_address))
257             our_address_set = 1;
258         else if (unformat (line_input, "vlan %d", &vlan_id))
259             ;
260         else if (unformat (line_input, "l2-interface %U",
261                            unformat_vnet_sw_interface, 
262                            vnet_get_main(), &sw_if_index))
263             ;
264         else if (unformat (line_input, "interface %U",
265                            unformat_vnet_sw_interface, 
266                            vnet_get_main(), &sw_if_index))
267             ;
268         else if (unformat (line_input, "local-cookie %llx", &local_cookie))
269             ;
270         else if (unformat (line_input, "remote-cookie %llx", &remote_cookie))
271             ;
272         else if (unformat (line_input, "local-session-id %d", 
273                            &local_session_id))
274             ;
275         else if (unformat (line_input, "remote-session-id %d", 
276                            &remote_session_id))
277             ;
278         else if (unformat (line_input, "l2-sublayer-present"))
279             l2_sublayer_present = 1;
280         else 
281             return clib_error_return (0, "parse error: '%U'", 
282                                       format_unformat_error, line_input);
283     }
284
285     unformat_free (line_input);
286
287     if (sw_if_index == (u32)~0)
288         return clib_error_return (0, "l2-interface not specified");
289     if (our_address_set == 0)
290         return clib_error_return (0, "our address not specified");
291     if (client_address_set == 0)
292         return clib_error_return (0, "client address not specified");
293
294     remote_session_id = clib_host_to_net_u32 (remote_session_id);
295     local_session_id  = clib_host_to_net_u32 (local_session_id);
296
297     switch (lm->lookup_type) {
298     case L2T_LOOKUP_SRC_ADDRESS:
299         p = hash_get_mem (lm->session_by_src_address, &client_address);
300         if (p) 
301             return clib_error_return 
302                 (0, "Session w/ client address %U already exists",
303                  format_ip6_address, &client_address);
304         break;
305         
306     case L2T_LOOKUP_DST_ADDRESS:
307         p = hash_get_mem (lm->session_by_dst_address, &our_address);
308         if (p) 
309             return clib_error_return 
310                 (0, "Session w/ our address %U already exists",
311                  format_ip6_address, &our_address);
312         break;
313
314     case L2T_LOOKUP_SESSION_ID:
315         p = hash_get (lm->session_by_session_id, local_session_id);
316         if (p) 
317             return clib_error_return 
318                 (0, 
319                  "Session w/ local session id %d already exists",
320                  clib_net_to_host_u32 (local_session_id));
321         break;
322
323     default:
324         ASSERT(0);
325     }
326     
327     pool_get (lm->sessions, s);
328     memset (s, 0, sizeof (*s));
329     memcpy (&s->our_address, &our_address, sizeof (s->our_address));
330     memcpy (&s->client_address, &client_address, sizeof (s->client_address));
331     s->sw_if_index = sw_if_index;
332     s->vlan_id = clib_host_to_net_u16 (vlan_id);
333     s->local_cookie = clib_host_to_net_u64 (local_cookie);
334     l2tp_session_set_remote_cookie (s, remote_cookie);
335     s->local_session_id = local_session_id;
336     s->remote_session_id = remote_session_id;
337     s->l2_sublayer_present = l2_sublayer_present;
338     
339     hi = vnet_get_sup_hw_interface (lm->vnet_main, sw_if_index);
340     si = vnet_get_sup_sw_interface (lm->vnet_main, sw_if_index);
341
342     next_index = vlib_node_add_next (vm, l2t_ip6_node.index,
343                                      hi->output_node_index);
344     s->l2_output_next_index = next_index;
345     s->l2_output_sw_if_index = si->sw_if_index;
346
347     /* Setup hash table entries */
348     switch (lm->lookup_type) {
349     case L2T_LOOKUP_SRC_ADDRESS:
350         src_address_copy = clib_mem_alloc (sizeof (*src_address_copy));
351         memcpy (src_address_copy, &client_address, sizeof (*src_address_copy));
352         hash_set_mem (lm->session_by_src_address, src_address_copy, 
353                       s - lm->sessions);
354         break;
355     case L2T_LOOKUP_DST_ADDRESS:
356         dst_address_copy = clib_mem_alloc (sizeof (*dst_address_copy));
357         memcpy (dst_address_copy, &our_address, sizeof (*dst_address_copy));
358         hash_set_mem (lm->session_by_dst_address, dst_address_copy, 
359                       s - lm->sessions);
360         break;
361     case L2T_LOOKUP_SESSION_ID:
362         hash_set (lm->session_by_session_id, local_session_id, 
363                   s - lm->sessions);
364         break;
365
366     default:
367         ASSERT(0);
368     }
369
370     vlan_and_sw_if_index_key = ((uword)(s->vlan_id)<<32) | sw_if_index;
371     hash_set (lm->session_by_vlan_and_rx_sw_if_index,
372               vlan_and_sw_if_index_key, s - lm->sessions);
373     
374     /* validate counters */
375     counter_index = 
376         session_index_to_counter_index (s - lm->sessions,
377                                         SESSION_COUNTER_USER_TO_NETWORK);
378     vlib_validate_counter (&lm->counter_main, counter_index);
379     vlib_validate_counter (&lm->counter_main, counter_index+1);
380         
381     /* Set promiscuous mode on the l2 interface */
382     ethernet_set_flags (lm->vnet_main, hi->hw_if_index, 
383                         ETHERNET_INTERFACE_FLAG_ACCEPT_ALL);
384     vnet_hw_interface_rx_redirect_to_node (lm->vnet_main, hi->hw_if_index,
385                                            l2t_l2_node.index);
386     return 0;
387 }
388
389 static VLIB_CLI_COMMAND (l2tp_session_add_command) = {
390     .path = "l2tp session add",
391     .short_help = 
392     "l2tp session add client <ip6> our <ip6> vlan <id> local-cookie <hex> remote-cookie <hex> local-session <dec> remote-session <dec> l2-interface <int>",
393     .function = l2tp_session_add_command_fn,
394 };
395
396 static clib_error_t *
397 l2tp_session_del_command_fn (vlib_main_t * vm,
398                              unformat_input_t * input,
399                              vlib_cli_command_t * cmd)
400 {
401     l2t_main_t *lm = &l2t_main;
402     u32 session_index;
403     l2t_session_t *s;
404     hash_pair_t * hp;
405     void *key;
406     uword vlan_and_sw_if_index_key;
407
408     if (!unformat (input, "%d", &session_index)) 
409         return clib_error_return (0, "missing session index: '%U'", 
410                                   format_unformat_error, input);
411         
412     if (pool_is_free_index (lm->sessions, session_index))
413         return clib_error_return (0, "session %d not in use", session_index);
414
415     s = pool_elt_at_index (lm->sessions, session_index);
416
417     switch (lm->lookup_type) {
418     case L2T_LOOKUP_SRC_ADDRESS:
419         hp = hash_get_pair_mem (lm->session_by_src_address, &s->client_address);
420         if (hp) {
421             key = (void *)(hp->key);
422             hash_unset_mem (lm->session_by_src_address, &s->client_address);
423             clib_mem_free (key);
424         } else
425             clib_warning ("session %d src address key %U AWOL", 
426                           s - lm->sessions, 
427                           format_ip6_address, &s->client_address);
428         break;
429
430     case L2T_LOOKUP_DST_ADDRESS:
431         hp = hash_get_pair_mem (lm->session_by_dst_address, &s->our_address);
432         if (hp) {
433             key = (void *)(hp->key);
434             hash_unset_mem (lm->session_by_dst_address, &s->our_address);
435             clib_mem_free (key);
436         } else
437             clib_warning ("session %d dst address key %U AWOL", 
438                           s - lm->sessions, 
439                           format_ip6_address, &s->our_address);
440         break;
441
442     case L2T_LOOKUP_SESSION_ID:
443         hash_unset (lm->session_by_session_id, s->local_session_id);
444         break;
445         
446     default:
447         ASSERT(0);
448     }
449         
450     vlan_and_sw_if_index_key = ((uword)(s->vlan_id)<<32) | s->sw_if_index;
451
452     hash_unset (lm->session_by_vlan_and_rx_sw_if_index, vlan_and_sw_if_index_key);
453         
454     pool_put (lm->sessions, s);
455     return 0;
456 }
457
458 static VLIB_CLI_COMMAND (l2tp_session_del_command) = {
459     .path = "l2tp session delete",
460     .short_help = 
461     "l2tp session delete <session-id>",
462     .function = l2tp_session_del_command_fn,
463 };
464
465 static clib_error_t *
466 l2tp_session_cookie_command_fn (vlib_main_t * vm,
467                                 unformat_input_t * input,
468                                 vlib_cli_command_t * cmd)
469 {
470     l2t_main_t *lm = &l2t_main;
471     u32 session_index;
472     l2t_session_t *s;
473     u64 lcl_ro_cookie = (u64)~0, rem_ro_cookie = (u64)~0;
474     u8 cookie_flags = 0;
475
476     if (!unformat (input, "%d", &session_index)) 
477         return clib_error_return (0, "missing session index: '%U'", 
478                                   format_unformat_error, input);
479         
480     if (pool_is_free_index (lm->sessions, session_index))
481         return clib_error_return (0, "session %d not in use", session_index);
482
483     s = pool_elt_at_index (lm->sessions, session_index);
484
485     if (unformat (input, "commit")) {
486         if (!s->cookie_flags) {
487             return clib_error_return (0,
488                                       "no rollover cookie ready to commit");
489         } else {
490             l2tp_session_cookie_commit (s);
491             return 0;
492         }
493     }
494     if (!unformat (input, "rollover"))
495         return clib_error_return (0, "missing 'commit|rollover': '%U'", 
496                                   format_unformat_error, input);
497     if (unformat (input, "local %llx", &lcl_ro_cookie)) {
498         cookie_flags |= L2TP_COOKIE_ROLLOVER_LOCAL;
499         l2tp_session_set_local_rollover_cookie (s, lcl_ro_cookie);
500     }
501     if (unformat (input, "remote %llx", &rem_ro_cookie)) {
502         cookie_flags |= L2TP_COOKIE_ROLLOVER_REMOTE;
503         l2tp_session_set_remote_cookie (s, rem_ro_cookie);
504     }
505     if (!cookie_flags)
506         return clib_error_return (0, "no rollover cookie specified");
507
508     return 0;
509 }
510
511 static VLIB_CLI_COMMAND (l2tp_session_cookie_command) = {
512     .path = "l2tp session cookie",
513     .short_help = 
514     "l2tp session cookie <session id> commit|rollover [local <hex>] [remote <hex>]",
515     .function = l2tp_session_cookie_command_fn,
516 };