L2TP: Add option for custom fib id for outgoing encapsulated packets
[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 encap_fib_index,
301                                u32 * sw_if_index)
302 {
303   l2t_session_t *s = 0;
304   vnet_main_t * vnm = lm->vnet_main;
305   vnet_hw_interface_t * hi;
306   uword * p = (uword *) ~0;
307   u32 hw_if_index;
308   l2tpv3_header_t l2tp_hdr;
309   ip6_address_t * dst_address_copy, * src_address_copy;
310   u32 counter_index;
311
312   remote_session_id = clib_host_to_net_u32 (remote_session_id);
313   local_session_id  = clib_host_to_net_u32 (local_session_id);
314
315   switch (lm->lookup_type) {
316   case L2T_LOOKUP_SRC_ADDRESS:
317     p = hash_get_mem (lm->session_by_src_address, client_address);
318     break;
319     
320   case L2T_LOOKUP_DST_ADDRESS:
321     p = hash_get_mem (lm->session_by_dst_address, our_address);
322     break;
323
324   case L2T_LOOKUP_SESSION_ID:
325     p = hash_get (lm->session_by_session_id, local_session_id);
326     break;
327
328   default:
329     ASSERT(0);
330   }
331
332   /* adding a session: session must not already exist */
333   if (p) 
334     return VNET_API_ERROR_INVALID_VALUE;
335
336   pool_get (lm->sessions, s);
337   memset (s, 0, sizeof (*s));
338   clib_memcpy (&s->our_address, our_address, sizeof (s->our_address));
339   clib_memcpy (&s->client_address, client_address, sizeof (s->client_address));
340   s->local_cookie[0] = clib_host_to_net_u64 (local_cookie);
341   s->remote_cookie = clib_host_to_net_u64 (remote_cookie);
342   s->local_session_id = local_session_id;
343   s->remote_session_id = remote_session_id;
344   s->l2_sublayer_present = l2_sublayer_present;
345   /* precompute l2tp header size */
346   s->l2tp_hdr_size = l2_sublayer_present ? 
347     sizeof (l2tpv3_header_t) :
348     sizeof (l2tpv3_header_t) - sizeof(l2tp_hdr.l2_specific_sublayer);
349   s->admin_up = 0;
350   s->encap_fib_index = encap_fib_index;
351
352   /* Setup hash table entries */
353   switch (lm->lookup_type) {
354   case L2T_LOOKUP_SRC_ADDRESS:
355     src_address_copy = clib_mem_alloc (sizeof (*src_address_copy));
356     clib_memcpy (src_address_copy, client_address, sizeof (*src_address_copy));
357     hash_set_mem (lm->session_by_src_address, src_address_copy, 
358                   s - lm->sessions);
359     break;
360   case L2T_LOOKUP_DST_ADDRESS:
361     dst_address_copy = clib_mem_alloc (sizeof (*dst_address_copy));
362     clib_memcpy (dst_address_copy, our_address, sizeof (*dst_address_copy));
363     hash_set_mem (lm->session_by_dst_address, dst_address_copy, 
364                   s - lm->sessions);
365     break;
366   case L2T_LOOKUP_SESSION_ID:
367     hash_set (lm->session_by_session_id, local_session_id, 
368               s - lm->sessions);
369     break;
370
371   default:
372     ASSERT(0);
373   }
374
375   /* validate counters */
376   counter_index = 
377     session_index_to_counter_index (s - lm->sessions,
378                                     SESSION_COUNTER_USER_TO_NETWORK);
379   vlib_validate_combined_counter (&lm->counter_main, counter_index);
380   vlib_validate_combined_counter (&lm->counter_main, counter_index+1);
381   
382   if (vec_len (lm->free_l2tpv3_tunnel_hw_if_indices) > 0)
383     {
384       hw_if_index = lm->free_l2tpv3_tunnel_hw_if_indices
385         [vec_len (lm->free_l2tpv3_tunnel_hw_if_indices)-1];
386       _vec_len (lm->free_l2tpv3_tunnel_hw_if_indices) -= 1;
387
388       hi = vnet_get_hw_interface (vnm, hw_if_index);
389       hi->dev_instance = s - lm->sessions;
390       hi->hw_instance = hi->dev_instance;
391     }
392   else 
393     {
394       hw_if_index = vnet_register_interface
395         (vnm, l2tpv3_device_class.index, s - lm->sessions,
396          l2tpv3_hw_class.index, s - lm->sessions);
397       hi = vnet_get_hw_interface (vnm, hw_if_index);
398       hi->output_node_index = l2t_encap_node.index;
399       /* $$$$ initialize custom dispositions, if needed */
400     }
401   
402   s->hw_if_index = hw_if_index;
403   s->sw_if_index = hi->sw_if_index;
404
405   if (sw_if_index)
406     *sw_if_index = hi->sw_if_index;
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   u32 encap_fib_id = ~0;
426   u32 encap_fib_index = ~0;
427
428   /* Get a line of input. */
429   if (! unformat_user (input, unformat_line_input, line_input))
430     return 0;
431
432   while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT) {
433     if (unformat (line_input, "client %U", 
434                   unformat_ip6_address, &client_address))
435       client_address_set = 1;
436     else if (unformat (line_input, "our %U",
437                        unformat_ip6_address, &our_address))
438       our_address_set = 1;
439     else if (unformat (line_input, "local-cookie %llx", &local_cookie))
440       ;
441     else if (unformat (line_input, "remote-cookie %llx", &remote_cookie))
442       ;
443     else if (unformat (line_input, "local-session-id %d", 
444                        &local_session_id))
445       ;
446     else if (unformat (line_input, "remote-session-id %d", 
447                        &remote_session_id))
448       ;
449     else if (unformat (line_input, "fib-id %d",
450                            &encap_fib_id))
451           ;
452     else if (unformat (line_input, "l2-sublayer-present"))
453       l2_sublayer_present = 1;
454     else 
455       return clib_error_return (0, "parse error: '%U'", 
456                                 format_unformat_error, line_input);
457   }
458
459   unformat_free (line_input);
460
461   if (encap_fib_id != ~0) {
462       uword *p;
463       ip6_main_t *im = &ip6_main;
464       if (!(p = hash_get (im->fib_index_by_table_id, encap_fib_id)))
465           return clib_error_return (0, "No fib with id %d", encap_fib_id);
466       encap_fib_index = p[0];
467   } else {
468       encap_fib_index = ~0;
469   }
470
471   if (our_address_set == 0)
472     return clib_error_return (0, "our address not specified");
473   if (client_address_set == 0)
474     return clib_error_return (0, "client address not specified");
475   
476   rv = create_l2tpv3_ipv6_tunnel (lm, &client_address, &our_address,
477                                   local_session_id, remote_session_id,
478                                   local_cookie, remote_cookie,
479                                   l2_sublayer_present, 
480                                   encap_fib_index,
481                                   &sw_if_index);
482   switch(rv)
483     {
484     case 0:
485       vlib_cli_output(vm, "%U\n", format_vnet_sw_if_index_name, vnet_get_main(), sw_if_index);
486       break;
487     case VNET_API_ERROR_INVALID_VALUE:
488       return clib_error_return (0, "session already exists...");
489
490     case VNET_API_ERROR_NO_SUCH_ENTRY:
491       return clib_error_return (0, "session does not exist...");
492
493     default:
494       return clib_error_return (0, "l2tp_session_add_del returned %d", rv);
495     }
496
497   return 0;
498 }
499
500 VLIB_CLI_COMMAND (create_l2tpv3_tunnel_command, static) = {
501     .path = "create l2tpv3 tunnel",
502     .short_help = 
503     "create l2tpv3 tunnel client <ip6> our <ip6> local-cookie <hex> remote-cookie <hex> local-session <dec> remote-session <dec>",
504     .function = create_l2tpv3_tunnel_command_fn,
505 };
506
507 int l2tpv3_set_tunnel_cookies (l2t_main_t * lm,
508                                u32 sw_if_index,
509                                u64 new_local_cookie,
510                                u64 new_remote_cookie)
511 {
512     l2t_session_t *s;
513     vnet_hw_interface_t * hi;
514     vnet_main_t * vnm = vnet_get_main();
515     hi = vnet_get_sup_hw_interface (vnm, sw_if_index);
516
517     if (pool_is_free_index (lm->sessions, hi->dev_instance))
518         return VNET_API_ERROR_INVALID_VALUE;
519
520     s = pool_elt_at_index (lm->sessions, hi->dev_instance);
521
522     s->local_cookie[1] = s->local_cookie[0];
523     s->local_cookie[0] = clib_host_to_net_u64(new_local_cookie);
524     s->remote_cookie = clib_host_to_net_u64(new_remote_cookie);
525
526     return 0;
527 }
528
529
530 static clib_error_t *
531 set_l2tp_tunnel_cookie_command_fn (vlib_main_t * vm,
532                                 unformat_input_t * input,
533                                 vlib_cli_command_t * cmd)
534 {
535   l2t_main_t *lm = &l2t_main;
536   vnet_main_t * vnm = vnet_get_main();
537   u32 sw_if_index = ~0;
538   u64 local_cookie = (u64)~0, remote_cookie = (u64)~0;
539
540   int rv;
541   
542   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
543     {
544       if (unformat (input, "%U", unformat_vnet_sw_interface, vnm, 
545                     &sw_if_index))
546         ;
547       else if (unformat (input, "local %llx", &local_cookie))
548         ;
549       else if (unformat (input, "remote %llx", &remote_cookie))
550         ;
551       else
552         break;
553     }
554   if (sw_if_index == ~0)
555     return clib_error_return (0, "unknown interface");
556   if (local_cookie == ~0)
557     return clib_error_return (0, "local cookie required");
558   if (remote_cookie == ~0)
559     return clib_error_return (0, "remote cookie required");
560
561   rv = l2tpv3_set_tunnel_cookies (lm, sw_if_index, 
562                                   local_cookie, remote_cookie);
563
564   switch (rv)
565     {
566     case 0:
567       break;
568
569     case VNET_API_ERROR_INVALID_SW_IF_INDEX:
570       return clib_error_return (0, "invalid interface");
571
572     default:
573       return clib_error_return (0, "l2tp_session_set_cookies returned %d",
574                                 rv);
575     }
576
577     return 0;
578 }
579
580 VLIB_CLI_COMMAND (set_l2tp_tunnel_cookie_command, static) = {
581     .path = "set l2tpv3 tunnel cookie",
582     .short_help = 
583     "set l2tpv3 tunnel cookie <intfc> local <hex> remote <hex>",
584     .function = set_l2tp_tunnel_cookie_command_fn,
585 };
586
587 int l2tpv3_interface_enable_disable (vnet_main_t * vnm, 
588                                      u32 sw_if_index, 
589                                      int enable_disable)
590 {
591   ip6_main_t * im = &ip6_main;
592   ip_lookup_main_t * lm = &im->lookup_main;
593   ip_config_main_t * rx_cm = &lm->rx_config_mains[VNET_UNICAST];
594   u32 ci;
595   ip6_l2tpv3_config_t config;
596   u32 feature_index;
597
598   if (pool_is_free_index (vnm->interface_main.sw_interfaces, sw_if_index))
599     return VNET_API_ERROR_INVALID_SW_IF_INDEX;
600
601   feature_index = im->ip6_unicast_rx_feature_l2tp_decap;
602
603   ci = rx_cm->config_index_by_sw_if_index[sw_if_index];
604   ci = (enable_disable
605         ? vnet_config_add_feature
606         : vnet_config_del_feature)
607     (vlib_get_main(), &rx_cm->config_main,
608      ci,
609      feature_index,
610      &config,
611      sizeof (config));
612   rx_cm->config_index_by_sw_if_index[sw_if_index] = ci;
613   return 0;
614 }
615
616 /* Enable/disable L2TPv3 intercept on IP6 fowarding path */
617 static clib_error_t *
618 set_ip6_l2tpv3 (vlib_main_t * vm,
619                 unformat_input_t * input,
620                 vlib_cli_command_t * cmd)
621 {
622   u32 sw_if_index = ~0;
623   int is_add = 1;
624   int rv;
625   vnet_main_t * vnm = vnet_get_main();
626
627   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
628     {
629       if (unformat (input, "%U", unformat_vnet_sw_interface, vnm, 
630                     &sw_if_index))
631         ;
632       else if (unformat (input, "del"))
633         is_add = 0;
634       else
635         break;
636     }
637
638   if (sw_if_index == ~0)
639     return clib_error_return (0, "interface required");
640   
641   rv = l2tpv3_interface_enable_disable (vnm, sw_if_index, is_add);
642
643   switch (rv)
644     {
645     case 0:
646       break;
647
648     case VNET_API_ERROR_INVALID_SW_IF_INDEX:
649       return clib_error_return (0, "invalid interface");
650
651     default:
652       return clib_error_return (0, "l2tp_interface_enable_disable returned %d",
653                                 rv);
654     }
655   return 0;
656 }
657
658 VLIB_CLI_COMMAND (set_interface_ip6_l2tpv3, static) = {
659   .path = "set interface ip6 l2tpv3",
660   .function = set_ip6_l2tpv3,
661   .short_help = "set interface ip6 l2tpv3 <intfc> [del]",
662 };
663
664 static clib_error_t *
665 l2tp_config (vlib_main_t * vm, unformat_input_t * input)
666 {
667     l2t_main_t *lm = &l2t_main;
668
669     while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) {
670         if (unformat (input, "lookup-v6-src"))
671             lm->lookup_type = L2T_LOOKUP_SRC_ADDRESS;
672         else if (unformat (input, "lookup-v6-dst"))
673             lm->lookup_type = L2T_LOOKUP_DST_ADDRESS;
674         else if (unformat (input, "lookup-session-id"))
675             lm->lookup_type = L2T_LOOKUP_SESSION_ID;
676         else return clib_error_return (0, "unknown input `%U'",
677                                        format_unformat_error, input);
678     }
679     return 0;
680 }
681
682 VLIB_CONFIG_FUNCTION (l2tp_config, "l2tp");
683
684
685 clib_error_t *
686 l2tp_sw_interface_up_down (vnet_main_t * vnm,
687                                    u32 sw_if_index,
688                                    u32 flags)
689 {
690   l2t_main_t *lm = &l2t_main;
691   vnet_hw_interface_t *hi = vnet_get_sup_hw_interface (vnm, sw_if_index);
692   if (hi->hw_class_index != l2tpv3_hw_class.index)
693     return 0;
694
695   u32 session_index = hi->dev_instance;
696   l2t_session_t *s = pool_elt_at_index (lm->sessions, session_index);
697   s->admin_up = !! (flags & VNET_SW_INTERFACE_FLAG_ADMIN_UP);
698   return 0;
699 }
700
701 VNET_SW_INTERFACE_ADMIN_UP_DOWN_FUNCTION (l2tp_sw_interface_up_down);
702
703 clib_error_t *l2tp_init (vlib_main_t *vm)
704 {
705     l2t_main_t *lm = &l2t_main;
706     ip_main_t * im = &ip_main;
707     ip_protocol_info_t * pi;
708
709     lm->vnet_main = vnet_get_main();
710     lm->vlib_main = vm;
711     lm->lookup_type = L2T_LOOKUP_DST_ADDRESS;
712
713     lm->session_by_src_address = hash_create_mem
714         (0, sizeof (ip6_address_t) /* key bytes */,
715          sizeof (u32) /* value bytes */);
716     lm->session_by_dst_address = hash_create_mem
717         (0, sizeof (ip6_address_t) /* key bytes */,
718          sizeof (u32) /* value bytes */);
719     lm->session_by_session_id = hash_create (0, sizeof (uword));
720
721     pi = ip_get_protocol_info (im, IP_PROTOCOL_L2TP);
722     pi->unformat_pg_edit = unformat_pg_l2tp_header;
723
724     /* insure these nodes are included in build */
725     l2tp_encap_init(vm);
726     l2tp_decap_init();
727
728     return 0;
729 }
730
731 VLIB_INIT_FUNCTION(l2tp_init);
732