Initial commit of vpp code.
[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   memcpy (&s->our_address, our_address, sizeof (s->our_address));
338   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     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     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       break;
470     case VNET_API_ERROR_INVALID_VALUE:
471       return clib_error_return (0, "session already exists...");
472
473     case VNET_API_ERROR_NO_SUCH_ENTRY:
474       return clib_error_return (0, "session does not exist...");
475
476     default:
477       return clib_error_return (0, "l2tp_session_add_del returned %d", rv);
478     }
479
480   return 0;
481 }
482
483 VLIB_CLI_COMMAND (create_l2tpv3_tunnel_command, static) = {
484     .path = "create l2tpv3 tunnel",
485     .short_help = 
486     "create l2tpv3 tunnel client <ip6> our <ip6> local-cookie <hex> remote-cookie <hex> local-session <dec> remote-session <dec>",
487     .function = create_l2tpv3_tunnel_command_fn,
488 };
489
490 int l2tpv3_set_tunnel_cookies (l2t_main_t * lm,
491                                u32 sw_if_index,
492                                u64 new_local_cookie,
493                                u64 new_remote_cookie)
494 {
495     l2t_session_t *s;
496     vnet_hw_interface_t * hi;
497     vnet_main_t * vnm = vnet_get_main();
498     hi = vnet_get_sup_hw_interface (vnm, sw_if_index);
499
500     if (pool_is_free_index (lm->sessions, hi->dev_instance))
501         return VNET_API_ERROR_INVALID_VALUE;
502
503     s = pool_elt_at_index (lm->sessions, hi->dev_instance);
504
505     s->local_cookie[1] = s->local_cookie[0];
506     s->local_cookie[0] = clib_host_to_net_u64(new_local_cookie);
507     s->remote_cookie = clib_host_to_net_u64(new_remote_cookie);
508
509     return 0;
510 }
511
512
513 static clib_error_t *
514 set_l2tp_tunnel_cookie_command_fn (vlib_main_t * vm,
515                                 unformat_input_t * input,
516                                 vlib_cli_command_t * cmd)
517 {
518   l2t_main_t *lm = &l2t_main;
519   vnet_main_t * vnm = vnet_get_main();
520   u32 sw_if_index = ~0;
521   u64 local_cookie = (u64)~0, remote_cookie = (u64)~0;
522
523   int rv;
524   
525   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
526     {
527       if (unformat (input, "%U", unformat_vnet_sw_interface, vnm, 
528                     &sw_if_index))
529         ;
530       else if (unformat (input, "local %llx", &local_cookie))
531         ;
532       else if (unformat (input, "remote %llx", &remote_cookie))
533         ;
534       else
535         break;
536     }
537   if (sw_if_index == ~0)
538     return clib_error_return (0, "unknown interface");
539   if (local_cookie == ~0)
540     return clib_error_return (0, "local cookie required");
541   if (remote_cookie == ~0)
542     return clib_error_return (0, "remote cookie required");
543
544   rv = l2tpv3_set_tunnel_cookies (lm, sw_if_index, 
545                                   local_cookie, remote_cookie);
546
547   switch (rv)
548     {
549     case 0:
550       break;
551
552     case VNET_API_ERROR_INVALID_SW_IF_INDEX:
553       return clib_error_return (0, "invalid interface");
554
555     default:
556       return clib_error_return (0, "l2tp_session_set_cookies returned %d",
557                                 rv);
558     }
559
560     return 0;
561 }
562
563 VLIB_CLI_COMMAND (set_l2tp_tunnel_cookie_command, static) = {
564     .path = "set l2tpv3 tunnel cookie",
565     .short_help = 
566     "set l2tpv3 tunnel cookie <intfc> local <hex> remote <hex>",
567     .function = set_l2tp_tunnel_cookie_command_fn,
568 };
569
570 int l2tpv3_interface_enable_disable (vnet_main_t * vnm, 
571                                      u32 sw_if_index, 
572                                      int enable_disable)
573 {
574   ip6_main_t * im = &ip6_main;
575   ip_lookup_main_t * lm = &im->lookup_main;
576   ip_config_main_t * rx_cm = &lm->rx_config_mains[VNET_UNICAST];
577   u32 ci;
578   ip6_l2tpv3_config_t config;
579   ip4_rx_feature_type_t type;
580
581   if (pool_is_free_index (vnm->interface_main.sw_interfaces, sw_if_index))
582     return VNET_API_ERROR_INVALID_SW_IF_INDEX;
583
584   type = IP6_RX_FEATURE_L2TPV3;
585
586   ci = rx_cm->config_index_by_sw_if_index[sw_if_index];
587   ci = (enable_disable
588         ? vnet_config_add_feature
589         : vnet_config_del_feature)
590     (vlib_get_main(), &rx_cm->config_main,
591      ci,
592      type,
593      &config,
594      sizeof (config));
595   rx_cm->config_index_by_sw_if_index[sw_if_index] = ci;
596   return 0;
597 }
598
599 /* Enable/disable L2TPv3 intercept on IP6 fowarding path */
600 static clib_error_t *
601 set_ip6_l2tpv3 (vlib_main_t * vm,
602                 unformat_input_t * input,
603                 vlib_cli_command_t * cmd)
604 {
605   u32 sw_if_index = ~0;
606   int is_add = 1;
607   int rv;
608   vnet_main_t * vnm = vnet_get_main();
609
610   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
611     {
612       if (unformat (input, "%U", unformat_vnet_sw_interface, vnm, 
613                     &sw_if_index))
614         ;
615       else if (unformat (input, "del"))
616         is_add = 0;
617       else
618         break;
619     }
620
621   if (sw_if_index == ~0)
622     return clib_error_return (0, "interface required");
623   
624   rv = l2tpv3_interface_enable_disable (vnm, sw_if_index, is_add);
625
626   switch (rv)
627     {
628     case 0:
629       break;
630
631     case VNET_API_ERROR_INVALID_SW_IF_INDEX:
632       return clib_error_return (0, "invalid interface");
633
634     default:
635       return clib_error_return (0, "l2tp_interface_enable_disable returned %d",
636                                 rv);
637     }
638   return 0;
639 }
640
641 VLIB_CLI_COMMAND (set_interface_ip6_l2tpv3, static) = {
642   .path = "set interface ip6 l2tpv3",
643   .function = set_ip6_l2tpv3,
644   .short_help = "set interface ip6 l2tpv3 <intfc> [del]",
645 };
646
647 static clib_error_t *
648 l2tp_config (vlib_main_t * vm, unformat_input_t * input)
649 {
650     l2t_main_t *lm = &l2t_main;
651
652     while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) {
653         if (unformat (input, "lookup-v6-src"))
654             lm->lookup_type = L2T_LOOKUP_SRC_ADDRESS;
655         else if (unformat (input, "lookup-v6-dst"))
656             lm->lookup_type = L2T_LOOKUP_DST_ADDRESS;
657         else if (unformat (input, "lookup-session-id"))
658             lm->lookup_type = L2T_LOOKUP_SESSION_ID;
659         else return clib_error_return (0, "unknown input `%U'",
660                                        format_unformat_error, input);
661     }
662     return 0;
663 }
664
665 VLIB_CONFIG_FUNCTION (l2tp_config, "l2tp");
666
667 clib_error_t *l2tp_init (vlib_main_t *vm)
668 {
669     l2t_main_t *lm = &l2t_main;
670     ip_main_t * im = &ip_main;
671     ip_protocol_info_t * pi;
672
673     lm->vnet_main = vnet_get_main();
674     lm->vlib_main = vm;
675     lm->lookup_type = L2T_LOOKUP_DST_ADDRESS;
676
677     lm->session_by_src_address = hash_create_mem
678         (0, sizeof (ip6_address_t) /* key bytes */,
679          sizeof (u32) /* value bytes */);
680     lm->session_by_dst_address = hash_create_mem
681         (0, sizeof (ip6_address_t) /* key bytes */,
682          sizeof (u32) /* value bytes */);
683     lm->session_by_session_id = hash_create (0, sizeof (uword));
684
685     pi = ip_get_protocol_info (im, IP_PROTOCOL_L2TP);
686     pi->unformat_pg_edit = unformat_pg_l2tp_header;
687
688     /* insure these nodes are included in build */
689     l2tp_encap_init(vm);
690     l2tp_decap_init();
691
692     return 0;
693 }
694
695 VLIB_INIT_FUNCTION(l2tp_init);
696