db4b4f5a29be0bd223379488ca1a545ee1733014
[vpp.git] / vnet / vnet / l2tp / l2tp.c
1 /*
2  * l2tp.c : L2TPv3 tunnel support
3  *
4  * Copyright (c) 2013 Cisco and/or its affiliates.
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at:
8  *
9  *     http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */
17
18 #include <vppinfra/error.h>
19 #include <vppinfra/hash.h>
20 #include <vnet/vnet.h>
21 #include <vnet/ip/ip.h>
22 #include <vnet/l2/l2_input.h>
23 #include <vnet/ethernet/ethernet.h>
24 #include <vnet/l2tp/l2tp.h>
25
26 l2t_main_t l2t_main;
27
28 /* packet trace format function */
29 u8 * format_l2t_trace (u8 * s, va_list * args)
30 {
31   CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
32   CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
33   l2t_trace_t * t = va_arg (*args, l2t_trace_t *);
34   
35   if (t->is_user_to_network)
36     s = format (s, "L2T: %U (client) -> %U (our) session %d", 
37                 format_ip6_address, &t->client_address,
38                 format_ip6_address, &t->our_address,
39                 t->session_index);
40   else
41     s = format (s, "L2T: %U (our) -> %U (client) session %d)",
42                 format_ip6_address, &t->our_address,
43                   format_ip6_address, &t->client_address,
44                   t->session_index);
45   return s;
46 }
47
48 u8 * format_l2t_session (u8 * s, va_list * args)
49 {
50   l2t_session_t * session = va_arg (*args, l2t_session_t *);
51   l2t_main_t * lm = &l2t_main;
52   u32 counter_index;
53   vlib_counter_t v;
54
55   s = format (s, "[%d] %U (our) %U (client) %U (sw_if_index %d)\n", 
56               session - lm->sessions,
57               format_ip6_address, &session->our_address,
58               format_ip6_address, &session->client_address,
59               format_vnet_sw_interface_name, lm->vnet_main,
60               vnet_get_sw_interface (lm->vnet_main, session->sw_if_index),
61               session->sw_if_index);
62
63   s = format (s, "   local cookies %016llx %016llx remote cookie %016llx\n",
64               clib_net_to_host_u64 (session->local_cookie[0]),
65               clib_net_to_host_u64 (session->local_cookie[1]),
66               clib_net_to_host_u64 (session->remote_cookie));
67
68   s = format (s, "   local session-id %d remote session-id %d\n",
69               clib_net_to_host_u32 (session->local_session_id),
70               clib_net_to_host_u32 (session->remote_session_id));
71
72   s = format (s, "   l2 specific sublayer %s\n", 
73               session->l2_sublayer_present ? "preset" : "absent");
74
75   counter_index = 
76     session_index_to_counter_index (session - lm->sessions,
77                                     SESSION_COUNTER_USER_TO_NETWORK);
78
79   vlib_get_combined_counter (&lm->counter_main, counter_index, &v);
80   if (v.packets != 0)
81     s = format (s, "   user-to-net: %llu pkts %llu bytes\n",
82                 v.packets, v.bytes);
83
84   vlib_get_combined_counter (&lm->counter_main, counter_index+1, &v);
85
86   if (v.packets != 0)
87     s = format (s, "   net-to-user: %llu pkts %llu bytes\n",
88                 v.packets, v.bytes);
89   return s;
90 }
91
92 static clib_error_t *
93 show_l2tp_command_fn (vlib_main_t * vm,
94                       unformat_input_t * input,
95                       vlib_cli_command_t * cmd)
96 {
97   l2t_session_t *session;
98   l2t_main_t *lm = &l2t_main;
99   char * keystr = 0;
100   int verbose = 0;
101   
102   if (unformat (input, "verbose") || unformat (input, "v"))
103     verbose = 1;
104
105   if (pool_elts (lm->sessions) == 0)
106       vlib_cli_output (vm, "No l2tp sessions...");
107   else
108       vlib_cli_output (vm, "%u l2tp sessions...", pool_elts (lm->sessions));
109
110   if (verbose)
111     {
112       switch (lm->lookup_type)
113         {
114         case L2T_LOOKUP_SRC_ADDRESS:
115           keystr = "src address";
116           break;
117
118         case L2T_LOOKUP_DST_ADDRESS:
119           keystr = "dst address";
120           break;
121
122         case L2T_LOOKUP_SESSION_ID:
123           keystr = "session id";
124           break;
125
126         default:
127           keystr = "BOGUS!";
128           break;
129         }
130
131       vlib_cli_output (vm, "L2tp session lookup on %s", keystr);
132
133       pool_foreach (session, lm->sessions, 
134       ({
135         vlib_cli_output (vm, "%U", format_l2t_session, session);
136       }));
137     }
138   
139   return 0;
140 }
141
142 VLIB_CLI_COMMAND (show_session_detail_command, static) = {
143   .path = "show l2tpv3",
144   .short_help = "show l2tpv3 [verbose]",
145   .function = show_l2tp_command_fn,
146 };
147
148 static clib_error_t *
149 test_counters_command_fn (vlib_main_t * vm,
150                           unformat_input_t * input,
151                           vlib_cli_command_t * cmd)
152 {
153   l2t_session_t *session;
154   l2t_main_t *lm = &l2t_main;
155   u32 session_index;
156   u32 counter_index;
157   u32 nincr=0;
158   u32 cpu_index = os_get_cpu_number();
159
160   pool_foreach (session, lm->sessions, 
161   ({
162     session_index = session - lm->sessions;
163     counter_index = 
164       session_index_to_counter_index (session_index, 
165                                       SESSION_COUNTER_USER_TO_NETWORK);
166     vlib_increment_combined_counter (&lm->counter_main,
167                                      cpu_index, 
168                                      counter_index, 
169                                      1/*pkt*/, 1111 /*bytes*/);
170     vlib_increment_combined_counter (&lm->counter_main,
171                                      cpu_index, 
172                                      counter_index+1, 
173                                      1/*pkt*/, 2222 /*bytes*/);
174     nincr++;
175     
176   }));
177   vlib_cli_output (vm, "Incremented %d active counters\n", nincr);
178   
179   return 0;
180 }
181
182 VLIB_CLI_COMMAND (test_counters_command, static) = {
183     .path = "test counters",
184     .short_help = "increment all active counters",
185     .function = test_counters_command_fn,
186 };
187
188 static clib_error_t *
189 clear_counters_command_fn (vlib_main_t * vm,
190                           unformat_input_t * input,
191                           vlib_cli_command_t * cmd)
192 {
193   l2t_session_t *session;
194   l2t_main_t *lm = &l2t_main;
195   u32 session_index;
196   u32 counter_index;
197   u32 nincr=0;
198   
199   pool_foreach (session, lm->sessions, 
200   ({
201     session_index = session - lm->sessions;
202     counter_index = 
203       session_index_to_counter_index (session_index, 
204                                       SESSION_COUNTER_USER_TO_NETWORK);
205     vlib_zero_combined_counter (&lm->counter_main, counter_index);
206     vlib_zero_combined_counter (&lm->counter_main, counter_index+1);
207     nincr++;
208     
209   }));
210   vlib_cli_output (vm, "Cleared %d active counters\n", nincr);
211   
212   return 0;
213 }
214
215 VLIB_CLI_COMMAND (clear_counters_command, static) = {
216     .path = "clear counters",
217     .short_help = "clear all active counters",
218     .function = clear_counters_command_fn,
219 };
220
221 static u8 * format_l2tpv3_name (u8 * s, va_list * args)
222 {
223   l2t_main_t *lm = &l2t_main;
224   u32 i = va_arg (*args, u32);
225   u32 show_dev_instance = ~0;
226
227   if (i < vec_len (lm->dev_inst_by_real))
228     show_dev_instance = lm->dev_inst_by_real[i];
229
230   if (show_dev_instance != ~0)
231     i = show_dev_instance;
232
233   return format (s, "l2tpv3_tunnel%d", i);
234 }
235
236 static int l2tpv3_name_renumber (vnet_hw_interface_t * hi,
237                                  u32 new_dev_instance)
238 {
239   l2t_main_t *lm = &l2t_main;
240
241   vec_validate_init_empty (lm->dev_inst_by_real, hi->dev_instance, ~0);
242
243   lm->dev_inst_by_real [hi->dev_instance] = new_dev_instance;
244
245   return 0;
246 }
247
248 static uword dummy_interface_tx (vlib_main_t * vm,
249                                  vlib_node_runtime_t * node,
250                                  vlib_frame_t * frame)
251 {
252   clib_warning ("you shouldn't be here, leaking buffers...");
253   return frame->n_vectors;
254 }
255
256 VNET_DEVICE_CLASS (l2tpv3_device_class,static) = {
257   .name = "L2TPv3",
258   .format_device_name = format_l2tpv3_name,
259   .name_renumber = l2tpv3_name_renumber,
260   .tx_function = dummy_interface_tx,
261 };
262
263 static uword dummy_set_rewrite (vnet_main_t * vnm,
264                                 u32 sw_if_index,
265                                 u32 l3_type,
266                                 void * dst_address,
267                                 void * rewrite,
268                                 uword max_rewrite_bytes)
269 {
270   /*
271    * Conundrum: packets from tun/tap destined for the tunnel
272    * actually have this rewrite applied. Transit packets do not.
273    * To make the two cases equivalent, don't generate a
274    * rewrite here, build the entire header in the fast path.
275    */
276   return 0;
277 }
278
279 static u8 * format_l2tp_header_with_length (u8 * s, va_list * args)
280 {
281   u32 dev_instance = va_arg (*args, u32);
282   s = format (s, "unimplemented dev %u", dev_instance);
283   return s;
284 }
285
286 VNET_HW_INTERFACE_CLASS (l2tpv3_hw_class) = {
287   .name = "L2TPV3",
288   .format_header = format_l2tp_header_with_length,
289   .set_rewrite = dummy_set_rewrite,
290 };
291
292 int create_l2tpv3_ipv6_tunnel (l2t_main_t * lm,
293                                ip6_address_t * client_address,
294                                ip6_address_t * our_address,
295                                u32 local_session_id,
296                                u32 remote_session_id,
297                                u64 local_cookie,
298                                u64 remote_cookie,
299                                int l2_sublayer_present, 
300                                u32 * sw_if_index)
301 {
302   l2t_session_t *s = 0;
303   vnet_main_t * vnm = lm->vnet_main;
304   vnet_hw_interface_t * hi;
305   uword * p = (uword *) ~0;
306   u32 hw_if_index;
307   l2tpv3_header_t l2tp_hdr;
308   ip6_address_t * dst_address_copy, * src_address_copy;
309   u32 counter_index;
310
311   remote_session_id = clib_host_to_net_u32 (remote_session_id);
312   local_session_id  = clib_host_to_net_u32 (local_session_id);
313
314   switch (lm->lookup_type) {
315   case L2T_LOOKUP_SRC_ADDRESS:
316     p = hash_get_mem (lm->session_by_src_address, client_address);
317     break;
318     
319   case L2T_LOOKUP_DST_ADDRESS:
320     p = hash_get_mem (lm->session_by_dst_address, our_address);
321     break;
322
323   case L2T_LOOKUP_SESSION_ID:
324     p = hash_get (lm->session_by_session_id, local_session_id);
325     break;
326
327   default:
328     ASSERT(0);
329   }
330
331   /* adding a session: session must not already exist */
332   if (p) 
333     return VNET_API_ERROR_INVALID_VALUE;
334
335   pool_get (lm->sessions, s);
336   memset (s, 0, sizeof (*s));
337   clib_memcpy (&s->our_address, our_address, sizeof (s->our_address));
338   clib_memcpy (&s->client_address, client_address, sizeof (s->client_address));
339   s->local_cookie[0] = clib_host_to_net_u64 (local_cookie);
340   s->remote_cookie = clib_host_to_net_u64 (remote_cookie);
341   s->local_session_id = local_session_id;
342   s->remote_session_id = remote_session_id;
343   s->l2_sublayer_present = l2_sublayer_present;
344   /* precompute l2tp header size */
345   s->l2tp_hdr_size = l2_sublayer_present ? 
346     sizeof (l2tpv3_header_t) :
347     sizeof (l2tpv3_header_t) - sizeof(l2tp_hdr.l2_specific_sublayer);
348
349   /* Setup hash table entries */
350   switch (lm->lookup_type) {
351   case L2T_LOOKUP_SRC_ADDRESS:
352     src_address_copy = clib_mem_alloc (sizeof (*src_address_copy));
353     clib_memcpy (src_address_copy, client_address, sizeof (*src_address_copy));
354     hash_set_mem (lm->session_by_src_address, src_address_copy, 
355                   s - lm->sessions);
356     break;
357   case L2T_LOOKUP_DST_ADDRESS:
358     dst_address_copy = clib_mem_alloc (sizeof (*dst_address_copy));
359     clib_memcpy (dst_address_copy, our_address, sizeof (*dst_address_copy));
360     hash_set_mem (lm->session_by_dst_address, dst_address_copy, 
361                   s - lm->sessions);
362     break;
363   case L2T_LOOKUP_SESSION_ID:
364     hash_set (lm->session_by_session_id, local_session_id, 
365               s - lm->sessions);
366     break;
367
368   default:
369     ASSERT(0);
370   }
371
372   /* validate counters */
373   counter_index = 
374     session_index_to_counter_index (s - lm->sessions,
375                                     SESSION_COUNTER_USER_TO_NETWORK);
376   vlib_validate_combined_counter (&lm->counter_main, counter_index);
377   vlib_validate_combined_counter (&lm->counter_main, counter_index+1);
378   
379   if (vec_len (lm->free_l2tpv3_tunnel_hw_if_indices) > 0)
380     {
381       hw_if_index = lm->free_l2tpv3_tunnel_hw_if_indices
382         [vec_len (lm->free_l2tpv3_tunnel_hw_if_indices)-1];
383       _vec_len (lm->free_l2tpv3_tunnel_hw_if_indices) -= 1;
384
385       hi = vnet_get_hw_interface (vnm, hw_if_index);
386       hi->dev_instance = s - lm->sessions;
387       hi->hw_instance = hi->dev_instance;
388     }
389   else 
390     {
391       hw_if_index = vnet_register_interface
392         (vnm, l2tpv3_device_class.index, s - lm->sessions,
393          l2tpv3_hw_class.index, s - lm->sessions);
394       hi = vnet_get_hw_interface (vnm, hw_if_index);
395       hi->output_node_index = l2t_encap_node.index;
396       /* $$$$ initialize custom dispositions, if needed */
397     }
398   
399   s->hw_if_index = hw_if_index;
400   s->sw_if_index = hi->sw_if_index;
401
402   if (sw_if_index)
403     *sw_if_index = hi->sw_if_index;
404
405   vnet_sw_interface_set_flags (vnm, hi->sw_if_index, 
406                                VNET_SW_INTERFACE_FLAG_ADMIN_UP);
407
408   return 0;
409 }
410
411 static clib_error_t *
412 create_l2tpv3_tunnel_command_fn (vlib_main_t * vm,
413                                  unformat_input_t * input,
414                                  vlib_cli_command_t * cmd)
415 {
416   ip6_address_t client_address, our_address;
417   unformat_input_t _line_input, * line_input = &_line_input;
418   l2t_main_t *lm = &l2t_main;
419   u64 local_cookie = (u64)~0, remote_cookie = (u64)~0;
420   u32 local_session_id = 1, remote_session_id = 1;
421   int our_address_set = 0, client_address_set = 0;
422   int l2_sublayer_present = 0;
423   int rv;
424   u32 sw_if_index;
425
426   /* Get a line of input. */
427   if (! unformat_user (input, unformat_line_input, line_input))
428     return 0;
429
430   while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT) {
431     if (unformat (line_input, "client %U", 
432                   unformat_ip6_address, &client_address))
433       client_address_set = 1;
434     else if (unformat (line_input, "our %U",
435                        unformat_ip6_address, &our_address))
436       our_address_set = 1;
437     else if (unformat (line_input, "local-cookie %llx", &local_cookie))
438       ;
439     else if (unformat (line_input, "remote-cookie %llx", &remote_cookie))
440       ;
441     else if (unformat (line_input, "local-session-id %d", 
442                        &local_session_id))
443       ;
444     else if (unformat (line_input, "remote-session-id %d", 
445                        &remote_session_id))
446       ;
447     else if (unformat (line_input, "l2-sublayer-present"))
448       l2_sublayer_present = 1;
449     else 
450       return clib_error_return (0, "parse error: '%U'", 
451                                 format_unformat_error, line_input);
452   }
453
454   unformat_free (line_input);
455
456   if (our_address_set == 0)
457     return clib_error_return (0, "our address not specified");
458   if (client_address_set == 0)
459     return clib_error_return (0, "client address not specified");
460   
461   rv = create_l2tpv3_ipv6_tunnel (lm, &client_address, &our_address,
462                                   local_session_id, remote_session_id,
463                                   local_cookie, remote_cookie,
464                                   l2_sublayer_present, 
465                                   &sw_if_index);
466   switch(rv)
467     {
468     case 0:
469       vlib_cli_output(vm, "%U\n", format_vnet_sw_if_index_name, vnet_get_main(), sw_if_index);
470       break;
471     case VNET_API_ERROR_INVALID_VALUE:
472       return clib_error_return (0, "session already exists...");
473
474     case VNET_API_ERROR_NO_SUCH_ENTRY:
475       return clib_error_return (0, "session does not exist...");
476
477     default:
478       return clib_error_return (0, "l2tp_session_add_del returned %d", rv);
479     }
480
481   return 0;
482 }
483
484 VLIB_CLI_COMMAND (create_l2tpv3_tunnel_command, static) = {
485     .path = "create l2tpv3 tunnel",
486     .short_help = 
487     "create l2tpv3 tunnel client <ip6> our <ip6> local-cookie <hex> remote-cookie <hex> local-session <dec> remote-session <dec>",
488     .function = create_l2tpv3_tunnel_command_fn,
489 };
490
491 int l2tpv3_set_tunnel_cookies (l2t_main_t * lm,
492                                u32 sw_if_index,
493                                u64 new_local_cookie,
494                                u64 new_remote_cookie)
495 {
496     l2t_session_t *s;
497     vnet_hw_interface_t * hi;
498     vnet_main_t * vnm = vnet_get_main();
499     hi = vnet_get_sup_hw_interface (vnm, sw_if_index);
500
501     if (pool_is_free_index (lm->sessions, hi->dev_instance))
502         return VNET_API_ERROR_INVALID_VALUE;
503
504     s = pool_elt_at_index (lm->sessions, hi->dev_instance);
505
506     s->local_cookie[1] = s->local_cookie[0];
507     s->local_cookie[0] = clib_host_to_net_u64(new_local_cookie);
508     s->remote_cookie = clib_host_to_net_u64(new_remote_cookie);
509
510     return 0;
511 }
512
513
514 static clib_error_t *
515 set_l2tp_tunnel_cookie_command_fn (vlib_main_t * vm,
516                                 unformat_input_t * input,
517                                 vlib_cli_command_t * cmd)
518 {
519   l2t_main_t *lm = &l2t_main;
520   vnet_main_t * vnm = vnet_get_main();
521   u32 sw_if_index = ~0;
522   u64 local_cookie = (u64)~0, remote_cookie = (u64)~0;
523
524   int rv;
525   
526   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
527     {
528       if (unformat (input, "%U", unformat_vnet_sw_interface, vnm, 
529                     &sw_if_index))
530         ;
531       else if (unformat (input, "local %llx", &local_cookie))
532         ;
533       else if (unformat (input, "remote %llx", &remote_cookie))
534         ;
535       else
536         break;
537     }
538   if (sw_if_index == ~0)
539     return clib_error_return (0, "unknown interface");
540   if (local_cookie == ~0)
541     return clib_error_return (0, "local cookie required");
542   if (remote_cookie == ~0)
543     return clib_error_return (0, "remote cookie required");
544
545   rv = l2tpv3_set_tunnel_cookies (lm, sw_if_index, 
546                                   local_cookie, remote_cookie);
547
548   switch (rv)
549     {
550     case 0:
551       break;
552
553     case VNET_API_ERROR_INVALID_SW_IF_INDEX:
554       return clib_error_return (0, "invalid interface");
555
556     default:
557       return clib_error_return (0, "l2tp_session_set_cookies returned %d",
558                                 rv);
559     }
560
561     return 0;
562 }
563
564 VLIB_CLI_COMMAND (set_l2tp_tunnel_cookie_command, static) = {
565     .path = "set l2tpv3 tunnel cookie",
566     .short_help = 
567     "set l2tpv3 tunnel cookie <intfc> local <hex> remote <hex>",
568     .function = set_l2tp_tunnel_cookie_command_fn,
569 };
570
571 int l2tpv3_interface_enable_disable (vnet_main_t * vnm, 
572                                      u32 sw_if_index, 
573                                      int enable_disable)
574 {
575   ip6_main_t * im = &ip6_main;
576   ip_lookup_main_t * lm = &im->lookup_main;
577   ip_config_main_t * rx_cm = &lm->rx_config_mains[VNET_UNICAST];
578   u32 ci;
579   ip6_l2tpv3_config_t config;
580   u32 feature_index;
581
582   if (pool_is_free_index (vnm->interface_main.sw_interfaces, sw_if_index))
583     return VNET_API_ERROR_INVALID_SW_IF_INDEX;
584
585   feature_index = im->ip6_unicast_rx_feature_ipsec;
586
587   ci = rx_cm->config_index_by_sw_if_index[sw_if_index];
588   ci = (enable_disable
589         ? vnet_config_add_feature
590         : vnet_config_del_feature)
591     (vlib_get_main(), &rx_cm->config_main,
592      ci,
593      feature_index,
594      &config,
595      sizeof (config));
596   rx_cm->config_index_by_sw_if_index[sw_if_index] = ci;
597   return 0;
598 }
599
600 /* Enable/disable L2TPv3 intercept on IP6 fowarding path */
601 static clib_error_t *
602 set_ip6_l2tpv3 (vlib_main_t * vm,
603                 unformat_input_t * input,
604                 vlib_cli_command_t * cmd)
605 {
606   u32 sw_if_index = ~0;
607   int is_add = 1;
608   int rv;
609   vnet_main_t * vnm = vnet_get_main();
610
611   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
612     {
613       if (unformat (input, "%U", unformat_vnet_sw_interface, vnm, 
614                     &sw_if_index))
615         ;
616       else if (unformat (input, "del"))
617         is_add = 0;
618       else
619         break;
620     }
621
622   if (sw_if_index == ~0)
623     return clib_error_return (0, "interface required");
624   
625   rv = l2tpv3_interface_enable_disable (vnm, sw_if_index, is_add);
626
627   switch (rv)
628     {
629     case 0:
630       break;
631
632     case VNET_API_ERROR_INVALID_SW_IF_INDEX:
633       return clib_error_return (0, "invalid interface");
634
635     default:
636       return clib_error_return (0, "l2tp_interface_enable_disable returned %d",
637                                 rv);
638     }
639   return 0;
640 }
641
642 VLIB_CLI_COMMAND (set_interface_ip6_l2tpv3, static) = {
643   .path = "set interface ip6 l2tpv3",
644   .function = set_ip6_l2tpv3,
645   .short_help = "set interface ip6 l2tpv3 <intfc> [del]",
646 };
647
648 static clib_error_t *
649 l2tp_config (vlib_main_t * vm, unformat_input_t * input)
650 {
651     l2t_main_t *lm = &l2t_main;
652
653     while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) {
654         if (unformat (input, "lookup-v6-src"))
655             lm->lookup_type = L2T_LOOKUP_SRC_ADDRESS;
656         else if (unformat (input, "lookup-v6-dst"))
657             lm->lookup_type = L2T_LOOKUP_DST_ADDRESS;
658         else if (unformat (input, "lookup-session-id"))
659             lm->lookup_type = L2T_LOOKUP_SESSION_ID;
660         else return clib_error_return (0, "unknown input `%U'",
661                                        format_unformat_error, input);
662     }
663     return 0;
664 }
665
666 VLIB_CONFIG_FUNCTION (l2tp_config, "l2tp");
667
668 clib_error_t *l2tp_init (vlib_main_t *vm)
669 {
670     l2t_main_t *lm = &l2t_main;
671     ip_main_t * im = &ip_main;
672     ip_protocol_info_t * pi;
673
674     lm->vnet_main = vnet_get_main();
675     lm->vlib_main = vm;
676     lm->lookup_type = L2T_LOOKUP_DST_ADDRESS;
677
678     lm->session_by_src_address = hash_create_mem
679         (0, sizeof (ip6_address_t) /* key bytes */,
680          sizeof (u32) /* value bytes */);
681     lm->session_by_dst_address = hash_create_mem
682         (0, sizeof (ip6_address_t) /* key bytes */,
683          sizeof (u32) /* value bytes */);
684     lm->session_by_session_id = hash_create (0, sizeof (uword));
685
686     pi = ip_get_protocol_info (im, IP_PROTOCOL_L2TP);
687     pi->unformat_pg_edit = unformat_pg_l2tp_header;
688
689     /* insure these nodes are included in build */
690     l2tp_encap_init(vm);
691     l2tp_decap_init();
692
693     return 0;
694 }
695
696 VLIB_INIT_FUNCTION(l2tp_init);
697