misc: remove GNU Indent directives
[vpp.git] / src / plugins / pppoe / pppoe.c
1 /*
2  *------------------------------------------------------------------
3  * Copyright (c) 2017 Intel and/or its affiliates.
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at:
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  *------------------------------------------------------------------
16  */
17 #include <stdint.h>
18 #include <net/if.h>
19 #include <sys/ioctl.h>
20 #include <inttypes.h>
21
22 #include <vlib/vlib.h>
23 #include <vlib/unix/unix.h>
24 #include <vnet/ethernet/ethernet.h>
25 #include <vnet/fib/fib_entry.h>
26 #include <vnet/fib/fib_table.h>
27 #include <vnet/dpo/interface_tx_dpo.h>
28 #include <vnet/plugin/plugin.h>
29 #include <vpp/app/version.h>
30 #include <vnet/ppp/packet.h>
31 #include <pppoe/pppoe.h>
32 #include <vnet/adj/adj_midchain.h>
33 #include <vnet/adj/adj_mcast.h>
34
35 #include <vppinfra/hash.h>
36 #include <vppinfra/bihash_template.c>
37
38 pppoe_main_t pppoe_main;
39
40 static fib_source_t pppoe_fib_src;
41
42 u8 *
43 format_pppoe_session (u8 * s, va_list * args)
44 {
45   pppoe_session_t *t = va_arg (*args, pppoe_session_t *);
46   pppoe_main_t *pem = &pppoe_main;
47
48   s = format (s, "[%d] sw-if-index %d client-ip %U session-id %d ",
49               t - pem->sessions, t->sw_if_index,
50               format_ip46_address, &t->client_ip, IP46_TYPE_ANY,
51               t->session_id);
52
53   s = format (s, "encap-if-index %d decap-fib-index %d\n",
54               t->encap_if_index, t->decap_fib_index);
55
56   s = format (s, "    local-mac %U  client-mac %U",
57               format_ethernet_address, t->local_mac,
58               format_ethernet_address, t->client_mac);
59
60   return s;
61 }
62
63 static u8 *
64 format_pppoe_name (u8 * s, va_list * args)
65 {
66   u32 dev_instance = va_arg (*args, u32);
67   return format (s, "pppoe_session%d", dev_instance);
68 }
69
70 static clib_error_t *
71 pppoe_interface_admin_up_down (vnet_main_t * vnm, u32 hw_if_index, u32 flags)
72 {
73   u32 hw_flags = (flags & VNET_SW_INTERFACE_FLAG_ADMIN_UP) ?
74     VNET_HW_INTERFACE_FLAG_LINK_UP : 0;
75   vnet_hw_interface_set_flags (vnm, hw_if_index, hw_flags);
76
77   return /* no error */ 0;
78 }
79
80 VNET_DEVICE_CLASS (pppoe_device_class,static) = {
81   .name = "PPPoE",
82   .format_device_name = format_pppoe_name,
83   .admin_up_down_function = pppoe_interface_admin_up_down,
84 };
85
86 static u8 *
87 format_pppoe_header_with_length (u8 * s, va_list * args)
88 {
89   u32 dev_instance = va_arg (*args, u32);
90   s = format (s, "unimplemented dev %u", dev_instance);
91   return s;
92 }
93
94 static u8 *
95 pppoe_build_rewrite (vnet_main_t * vnm,
96                      u32 sw_if_index,
97                      vnet_link_t link_type, const void *dst_address)
98 {
99   pppoe_main_t *pem = &pppoe_main;
100   pppoe_session_t *t;
101   vnet_hw_interface_t *hi;
102   vnet_sw_interface_t *si;
103   pppoe_header_t *pppoe;
104   u32 session_id;
105   u8 *rw = 0;
106
107   session_id = pem->session_index_by_sw_if_index[sw_if_index];
108   t = pool_elt_at_index (pem->sessions, session_id);
109
110   int len = sizeof (pppoe_header_t) + sizeof (ethernet_header_t);
111   si = vnet_get_sw_interface (vnm, t->encap_if_index);
112   if (si->type == VNET_SW_INTERFACE_TYPE_SUB)
113     {
114       if (si->sub.eth.flags.one_tag == 1)
115         {
116           len += sizeof (ethernet_vlan_header_t);
117         }
118     }
119
120   vec_validate_aligned (rw, len - 1, CLIB_CACHE_LINE_BYTES);
121
122   ethernet_header_t *eth_hdr = (ethernet_header_t *) rw;
123   eth_hdr->type = clib_host_to_net_u16 (ETHERNET_TYPE_PPPOE_SESSION);
124   pppoe = (pppoe_header_t *) (eth_hdr + 1);
125
126   if (si->type == VNET_SW_INTERFACE_TYPE_SUB)
127     {
128       if (si->sub.eth.flags.one_tag == 1)
129         {
130           eth_hdr->type = clib_host_to_net_u16 (ETHERNET_TYPE_VLAN);
131           ethernet_vlan_header_t *vlan =
132             (ethernet_vlan_header_t *) (eth_hdr + 1);
133           vlan->type = clib_host_to_net_u16 (ETHERNET_TYPE_PPPOE_SESSION);
134           vlan->priority_cfi_and_id =
135             clib_host_to_net_u16 (si->sub.eth.outer_vlan_id);
136           pppoe = (pppoe_header_t *) (vlan + 1);
137         }
138       si = vnet_get_sw_interface (vnm, si->sup_sw_if_index);
139     }
140
141   // set the right mac addresses
142   hi = vnet_get_hw_interface (vnm, si->hw_if_index);
143   clib_memcpy (eth_hdr->src_address, hi->hw_address, 6);
144   clib_memcpy (eth_hdr->dst_address, t->client_mac, 6);
145
146   pppoe->ver_type = PPPOE_VER_TYPE;
147   pppoe->code = 0;
148   pppoe->session_id = clib_host_to_net_u16 (t->session_id);
149   pppoe->length = 0;            /* To be filled in at run-time */
150
151   switch (link_type)
152     {
153     case VNET_LINK_IP4:
154       pppoe->ppp_proto = clib_host_to_net_u16 (PPP_PROTOCOL_ip4);
155       break;
156     case VNET_LINK_IP6:
157       pppoe->ppp_proto = clib_host_to_net_u16 (PPP_PROTOCOL_ip6);
158       break;
159     default:
160       break;
161     }
162
163   return rw;
164 }
165
166 /**
167  * @brief Fixup the adj rewrite post encap. Insert the packet's length
168  */
169 static void
170 pppoe_fixup (vlib_main_t * vm,
171              const ip_adjacency_t * adj, vlib_buffer_t * b0, const void *data)
172 {
173   //const pppoe_session_t *t;
174   pppoe_header_t *pppoe0;
175   uword len = (uword) data;
176
177   /* update the rewrite string */
178   pppoe0 = vlib_buffer_get_current (b0) + len;
179
180   pppoe0->length = clib_host_to_net_u16 (vlib_buffer_length_in_chain (vm, b0)
181                                          - sizeof (pppoe_header_t)
182                                          + sizeof (pppoe0->ppp_proto) - len);
183 }
184
185 static void
186 pppoe_update_adj (vnet_main_t * vnm, u32 sw_if_index, adj_index_t ai)
187 {
188   pppoe_main_t *pem = &pppoe_main;
189   dpo_id_t dpo = DPO_INVALID;
190   ip_adjacency_t *adj;
191   pppoe_session_t *t;
192   vnet_sw_interface_t *si;
193   u32 session_id;
194
195   ASSERT (ADJ_INDEX_INVALID != ai);
196
197   adj = adj_get (ai);
198   session_id = pem->session_index_by_sw_if_index[sw_if_index];
199   t = pool_elt_at_index (pem->sessions, session_id);
200
201   uword len = sizeof (ethernet_header_t);
202
203   si = vnet_get_sw_interface (vnm, t->encap_if_index);
204   if (si->type == VNET_SW_INTERFACE_TYPE_SUB)
205     {
206       if (si->sub.eth.flags.one_tag == 1)
207         {
208           len += sizeof (ethernet_vlan_header_t);
209         }
210     }
211
212   switch (adj->lookup_next_index)
213     {
214     case IP_LOOKUP_NEXT_ARP:
215     case IP_LOOKUP_NEXT_GLEAN:
216     case IP_LOOKUP_NEXT_BCAST:
217       adj_nbr_midchain_update_rewrite (ai, pppoe_fixup, (void *) len,
218                                        ADJ_FLAG_NONE,
219                                        pppoe_build_rewrite (vnm,
220                                                             sw_if_index,
221                                                             adj->ia_link,
222                                                             NULL));
223       break;
224     case IP_LOOKUP_NEXT_MCAST:
225       /*
226        * Construct a partial rewrite from the known ethernet mcast dest MAC
227        * There's no MAC fixup, so the last 2 parameters are 0
228        */
229       adj_mcast_midchain_update_rewrite (ai, pppoe_fixup, (void *) len,
230                                          ADJ_FLAG_NONE,
231                                          pppoe_build_rewrite (vnm,
232                                                               sw_if_index,
233                                                               adj->ia_link,
234                                                               NULL), 0, 0);
235       break;
236
237     case IP_LOOKUP_NEXT_DROP:
238     case IP_LOOKUP_NEXT_PUNT:
239     case IP_LOOKUP_NEXT_LOCAL:
240     case IP_LOOKUP_NEXT_REWRITE:
241     case IP_LOOKUP_NEXT_MIDCHAIN:
242     case IP_LOOKUP_NEXT_MCAST_MIDCHAIN:
243     case IP_LOOKUP_NEXT_ICMP_ERROR:
244     case IP_LOOKUP_N_NEXT:
245       ASSERT (0);
246       break;
247     }
248
249   interface_tx_dpo_add_or_lock (vnet_link_to_dpo_proto (adj->ia_link),
250                                 t->encap_if_index, &dpo);
251
252   adj_nbr_midchain_stack (ai, &dpo);
253
254   dpo_reset (&dpo);
255 }
256
257 VNET_HW_INTERFACE_CLASS (pppoe_hw_class) =
258 {
259   .name = "PPPoE",
260   .format_header = format_pppoe_header_with_length,
261   .build_rewrite = pppoe_build_rewrite,
262   .update_adjacency = pppoe_update_adj,
263   .flags = VNET_HW_INTERFACE_CLASS_FLAG_P2P,
264 };
265
266 #define foreach_copy_field                      \
267 _(session_id)                                   \
268 _(encap_if_index)                               \
269 _(decap_fib_index)                              \
270 _(client_ip)
271
272 static bool
273 pppoe_decap_next_is_valid (pppoe_main_t * pem, u32 is_ip6,
274                            u32 decap_fib_index)
275 {
276   vlib_main_t *vm = pem->vlib_main;
277   u32 input_idx = (!is_ip6) ? ip4_input_node.index : ip6_input_node.index;
278   vlib_node_runtime_t *r = vlib_node_get_runtime (vm, input_idx);
279
280   return decap_fib_index < r->n_next_nodes;
281 }
282
283 int vnet_pppoe_add_del_session
284   (vnet_pppoe_add_del_session_args_t * a, u32 * sw_if_indexp)
285 {
286   pppoe_main_t *pem = &pppoe_main;
287   pppoe_session_t *t = 0;
288   vnet_main_t *vnm = pem->vnet_main;
289   u32 hw_if_index = ~0;
290   u32 sw_if_index = ~0;
291   u32 is_ip6 = a->is_ip6;
292   pppoe_entry_key_t cached_key;
293   pppoe_entry_result_t cached_result;
294   u32 bucket;
295   pppoe_entry_key_t key;
296   pppoe_entry_result_t result;
297   vnet_hw_interface_t *hi;
298   vnet_sw_interface_t *si;
299   fib_prefix_t pfx;
300
301   cached_key.raw = ~0;
302   cached_result.raw = ~0;       /* warning be gone */
303   clib_memset (&pfx, 0, sizeof (pfx));
304
305   if (!is_ip6)
306     {
307       pfx.fp_addr.ip4.as_u32 = a->client_ip.ip4.as_u32;
308       pfx.fp_len = 32;
309       pfx.fp_proto = FIB_PROTOCOL_IP4;
310     }
311   else
312     {
313       pfx.fp_addr.ip6.as_u64[0] = a->client_ip.ip6.as_u64[0];
314       pfx.fp_addr.ip6.as_u64[1] = a->client_ip.ip6.as_u64[1];
315       pfx.fp_len = 128;
316       pfx.fp_proto = FIB_PROTOCOL_IP6;
317     }
318
319   /* Get encap_if_index and local mac address from link_table */
320   pppoe_lookup_1 (&pem->link_table, &cached_key, &cached_result,
321                   a->client_mac, 0, &key, &bucket, &result);
322   a->encap_if_index = result.fields.sw_if_index;
323
324   if (a->encap_if_index == ~0)
325     return VNET_API_ERROR_INVALID_SW_IF_INDEX;
326
327   si = vnet_get_sw_interface (vnm, a->encap_if_index);
328   hi = vnet_get_hw_interface (vnm, si->hw_if_index);
329
330   /* lookup session_table */
331   pppoe_lookup_1 (&pem->session_table, &cached_key, &cached_result,
332                   a->client_mac, clib_host_to_net_u16 (a->session_id),
333                   &key, &bucket, &result);
334
335   /* learn client session */
336   pppoe_learn_process (&pem->session_table, a->encap_if_index,
337                        &key, &cached_key, &bucket, &result);
338
339   if (a->is_add)
340     {
341       /* adding a session: session must not already exist */
342       if (result.fields.session_index != ~0)
343         return VNET_API_ERROR_TUNNEL_EXIST;
344
345       /*if not set explicitly, default to ip4 */
346       if (!pppoe_decap_next_is_valid (pem, is_ip6, a->decap_fib_index))
347         return VNET_API_ERROR_INVALID_DECAP_NEXT;
348
349       pool_get_aligned (pem->sessions, t, CLIB_CACHE_LINE_BYTES);
350       clib_memset (t, 0, sizeof (*t));
351
352       clib_memcpy (t->local_mac, hi->hw_address, vec_len (hi->hw_address));
353
354       /* copy from arg structure */
355 #define _(x) t->x = a->x;
356       foreach_copy_field;
357 #undef _
358
359       clib_memcpy (t->client_mac, a->client_mac, 6);
360
361       /* update pppoe fib with session_index */
362       result.fields.session_index = t - pem->sessions;
363       pppoe_update_1 (&pem->session_table,
364                       a->client_mac, clib_host_to_net_u16 (a->session_id),
365                       &key, &bucket, &result);
366
367       vnet_hw_interface_t *hi;
368       if (vec_len (pem->free_pppoe_session_hw_if_indices) > 0)
369         {
370           vnet_interface_main_t *im = &vnm->interface_main;
371           hw_if_index = pem->free_pppoe_session_hw_if_indices
372             [vec_len (pem->free_pppoe_session_hw_if_indices) - 1];
373           vec_dec_len (pem->free_pppoe_session_hw_if_indices, 1);
374
375           hi = vnet_get_hw_interface (vnm, hw_if_index);
376           hi->dev_instance = t - pem->sessions;
377           hi->hw_instance = hi->dev_instance;
378
379           /* clear old stats of freed session before reuse */
380           sw_if_index = hi->sw_if_index;
381           vnet_interface_counter_lock (im);
382           vlib_zero_combined_counter
383             (&im->combined_sw_if_counters[VNET_INTERFACE_COUNTER_TX],
384              sw_if_index);
385           vlib_zero_combined_counter (&im->combined_sw_if_counters
386                                       [VNET_INTERFACE_COUNTER_RX],
387                                       sw_if_index);
388           vlib_zero_simple_counter (&im->sw_if_counters
389                                     [VNET_INTERFACE_COUNTER_DROP],
390                                     sw_if_index);
391           vnet_interface_counter_unlock (im);
392         }
393       else
394         {
395           hw_if_index = vnet_register_interface
396             (vnm, pppoe_device_class.index, t - pem->sessions,
397              pppoe_hw_class.index, t - pem->sessions);
398           hi = vnet_get_hw_interface (vnm, hw_if_index);
399         }
400
401       t->hw_if_index = hw_if_index;
402       t->sw_if_index = sw_if_index = hi->sw_if_index;
403
404       vec_validate_init_empty (pem->session_index_by_sw_if_index, sw_if_index,
405                                ~0);
406       pem->session_index_by_sw_if_index[sw_if_index] = t - pem->sessions;
407
408       vnet_sw_interface_t *si = vnet_get_sw_interface (vnm, sw_if_index);
409       si->flags &= ~VNET_SW_INTERFACE_FLAG_HIDDEN;
410       vnet_sw_interface_set_flags (vnm, sw_if_index,
411                                    VNET_SW_INTERFACE_FLAG_ADMIN_UP);
412       vnet_set_interface_l3_output_node (vnm->vlib_main, sw_if_index,
413                                          (u8 *) "tunnel-output");
414
415       /* add reverse route for client ip */
416       fib_table_entry_path_add (a->decap_fib_index, &pfx,
417                                 pppoe_fib_src, FIB_ENTRY_FLAG_NONE,
418                                 fib_proto_to_dpo (pfx.fp_proto),
419                                 &pfx.fp_addr, sw_if_index, ~0,
420                                 1, NULL, FIB_ROUTE_PATH_FLAG_NONE);
421
422     }
423   else
424     {
425       /* deleting a session: session must exist */
426       if (result.fields.session_index == ~0)
427         return VNET_API_ERROR_NO_SUCH_ENTRY;
428
429       t = pool_elt_at_index (pem->sessions, result.fields.session_index);
430       sw_if_index = t->sw_if_index;
431
432       vnet_reset_interface_l3_output_node (vnm->vlib_main, sw_if_index);
433       vnet_sw_interface_set_flags (vnm, t->sw_if_index, 0 /* down */ );
434       vnet_sw_interface_t *si = vnet_get_sw_interface (vnm, t->sw_if_index);
435       si->flags |= VNET_SW_INTERFACE_FLAG_HIDDEN;
436
437       vec_add1 (pem->free_pppoe_session_hw_if_indices, t->hw_if_index);
438
439       pem->session_index_by_sw_if_index[t->sw_if_index] = ~0;
440
441       /* update pppoe fib with session_inde=~0x */
442       result.fields.session_index = ~0;
443       pppoe_update_1 (&pem->session_table,
444                       a->client_mac, clib_host_to_net_u16 (a->session_id),
445                       &key, &bucket, &result);
446
447
448       /* delete reverse route for client ip */
449       fib_table_entry_path_remove (a->decap_fib_index, &pfx,
450                                    pppoe_fib_src,
451                                    fib_proto_to_dpo (pfx.fp_proto),
452                                    &pfx.fp_addr,
453                                    sw_if_index, ~0, 1,
454                                    FIB_ROUTE_PATH_FLAG_NONE);
455
456       pool_put (pem->sessions, t);
457     }
458
459   if (sw_if_indexp)
460     *sw_if_indexp = sw_if_index;
461
462   return 0;
463 }
464
465 static clib_error_t *
466 pppoe_add_del_session_command_fn (vlib_main_t * vm,
467                                   unformat_input_t * input,
468                                   vlib_cli_command_t * cmd)
469 {
470   unformat_input_t _line_input, *line_input = &_line_input;
471   u16 session_id = 0;
472   ip46_address_t client_ip;
473   u8 is_add = 1;
474   u8 client_ip_set = 0;
475   u8 ipv4_set = 0;
476   u8 ipv6_set = 0;
477   u32 encap_if_index = 0;
478   u32 decap_fib_index = 0;
479   u8 client_mac[6] = { 0 };
480   u8 client_mac_set = 0;
481   int rv;
482   u32 tmp;
483   vnet_pppoe_add_del_session_args_t _a, *a = &_a;
484   u32 session_sw_if_index;
485   clib_error_t *error = NULL;
486
487   /* Cant "universally zero init" (={0}) due to GCC bug 53119 */
488   clib_memset (&client_ip, 0, sizeof client_ip);
489
490   /* Get a line of input. */
491   if (!unformat_user (input, unformat_line_input, line_input))
492     return 0;
493
494   while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
495     {
496       if (unformat (line_input, "del"))
497         {
498           is_add = 0;
499         }
500       else if (unformat (line_input, "session-id %d", &session_id))
501         ;
502       else if (unformat (line_input, "client-ip %U",
503                          unformat_ip4_address, &client_ip.ip4))
504         {
505           client_ip_set = 1;
506           ipv4_set = 1;
507         }
508       else if (unformat (line_input, "client-ip %U",
509                          unformat_ip6_address, &client_ip.ip6))
510         {
511           client_ip_set = 1;
512           ipv6_set = 1;
513         }
514       else if (unformat (line_input, "decap-vrf-id %d", &tmp))
515         {
516           if (ipv6_set)
517             decap_fib_index = fib_table_find (FIB_PROTOCOL_IP6, tmp);
518           else
519             decap_fib_index = fib_table_find (FIB_PROTOCOL_IP4, tmp);
520
521           if (decap_fib_index == ~0)
522             {
523               error =
524                 clib_error_return (0, "nonexistent decap fib id %d", tmp);
525               goto done;
526             }
527         }
528       else
529         if (unformat
530             (line_input, "client-mac %U", unformat_ethernet_address,
531              client_mac))
532         client_mac_set = 1;
533       else
534         {
535           error = clib_error_return (0, "parse error: '%U'",
536                                      format_unformat_error, line_input);
537           goto done;
538         }
539     }
540
541   if (client_ip_set == 0)
542     {
543       error =
544         clib_error_return (0, "session client ip address not specified");
545       goto done;
546     }
547
548   if (ipv4_set && ipv6_set)
549     {
550       error = clib_error_return (0, "both IPv4 and IPv6 addresses specified");
551       goto done;
552     }
553
554   if (client_mac_set == 0)
555     {
556       error = clib_error_return (0, "session client mac not specified");
557       goto done;
558     }
559
560   clib_memset (a, 0, sizeof (*a));
561
562   a->is_add = is_add;
563   a->is_ip6 = ipv6_set;
564
565 #define _(x) a->x = x;
566   foreach_copy_field;
567 #undef _
568
569   clib_memcpy (a->client_mac, client_mac, 6);
570
571   rv = vnet_pppoe_add_del_session (a, &session_sw_if_index);
572
573   switch (rv)
574     {
575     case 0:
576       if (is_add)
577         vlib_cli_output (vm, "%U\n", format_vnet_sw_if_index_name,
578                          vnet_get_main (), session_sw_if_index);
579       break;
580
581     case VNET_API_ERROR_TUNNEL_EXIST:
582       error = clib_error_return (0, "session already exists...");
583       goto done;
584
585     case VNET_API_ERROR_NO_SUCH_ENTRY:
586       error = clib_error_return (0, "session does not exist...");
587       goto done;
588
589     default:
590       error = clib_error_return
591         (0, "vnet_pppoe_add_del_session returned %d", rv);
592       goto done;
593     }
594
595 done:
596   unformat_free (line_input);
597
598   return error;
599 }
600
601 /*?
602  * Add or delete a PPPoE Session.
603  *
604  * @cliexpar
605  * Example of how to create a PPPoE Session:
606  * @cliexcmd{create pppoe session client-ip 10.0.3.1 session-id 13
607  *             client-mac 00:01:02:03:04:05 }
608  * Example of how to delete a PPPoE Session:
609  * @cliexcmd{create pppoe session client-ip 10.0.3.1 session-id 13
610  *             client-mac 00:01:02:03:04:05 del }
611  ?*/
612 VLIB_CLI_COMMAND (create_pppoe_session_command, static) = {
613   .path = "create pppoe session",
614   .short_help =
615   "create pppoe session client-ip <client-ip> session-id <nn>"
616   " client-mac <client-mac> [decap-vrf-id <nn>] [del]",
617   .function = pppoe_add_del_session_command_fn,
618 };
619
620 static clib_error_t *
621 show_pppoe_session_command_fn (vlib_main_t * vm,
622                                unformat_input_t * input,
623                                vlib_cli_command_t * cmd)
624 {
625   pppoe_main_t *pem = &pppoe_main;
626   pppoe_session_t *t;
627
628   if (pool_elts (pem->sessions) == 0)
629     vlib_cli_output (vm, "No pppoe sessions configured...");
630
631   pool_foreach (t, pem->sessions)
632                  {
633                     vlib_cli_output (vm, "%U",format_pppoe_session, t);
634                 }
635
636   return 0;
637 }
638
639 /*?
640  * Display all the PPPoE Session entries.
641  *
642  * @cliexpar
643  * Example of how to display the PPPoE Session entries:
644  * @cliexstart{show pppoe session}
645  * [0] client-ip 10.0.3.1 session_id 13 encap-if-index 0 decap-vrf-id 13 sw_if_index 5
646  *     local-mac a0:b0:c0:d0:e0:f0 client-mac 00:01:02:03:04:05
647  * @cliexend
648  ?*/
649 VLIB_CLI_COMMAND (show_pppoe_session_command, static) = {
650     .path = "show pppoe session",
651     .short_help = "show pppoe session",
652     .function = show_pppoe_session_command_fn,
653 };
654
655 typedef struct pppoe_show_walk_ctx_t_
656 {
657   vlib_main_t *vm;
658   u8 first_entry;
659   u32 total_entries;
660 } pppoe_show_walk_ctx_t;
661
662 static int
663 pppoe_show_walk_cb (BVT (clib_bihash_kv) * kvp, void *arg)
664 {
665   pppoe_show_walk_ctx_t *ctx = arg;
666   pppoe_entry_result_t result;
667   pppoe_entry_key_t key;
668
669   if (ctx->first_entry)
670     {
671       ctx->first_entry = 0;
672       vlib_cli_output (ctx->vm,
673                        "%=19s%=12s%=13s%=14s",
674                        "Mac-Address", "session_id", "sw_if_index",
675                        "session_index");
676     }
677
678   key.raw = kvp->key;
679   result.raw = kvp->value;
680
681   vlib_cli_output (ctx->vm,
682                    "%=19U%=12d%=13d%=14d",
683                    format_ethernet_address, key.fields.mac,
684                    clib_net_to_host_u16 (key.fields.session_id),
685                    result.fields.sw_if_index == ~0
686                    ? -1 : result.fields.sw_if_index,
687                    result.fields.session_index == ~0
688                    ? -1 : result.fields.session_index);
689   ctx->total_entries++;
690
691   return (BIHASH_WALK_CONTINUE);
692 }
693
694 /** Display the contents of the PPPoE Fib. */
695 static clib_error_t *
696 show_pppoe_fib_command_fn (vlib_main_t * vm,
697                            unformat_input_t * input, vlib_cli_command_t * cmd)
698 {
699   pppoe_main_t *pem = &pppoe_main;
700   pppoe_show_walk_ctx_t ctx = {
701     .first_entry = 1,
702     .vm = vm,
703   };
704
705   BV (clib_bihash_foreach_key_value_pair)
706     (&pem->session_table, pppoe_show_walk_cb, &ctx);
707
708   if (ctx.total_entries == 0)
709     vlib_cli_output (vm, "no pppoe fib entries");
710   else
711     vlib_cli_output (vm, "%lld pppoe fib entries", ctx.total_entries);
712
713   return 0;
714 }
715
716 /*?
717  * This command displays the MAC Address entries of the PPPoE FIB table.
718  * Output can be filtered to just get the number of MAC Addresses or display
719  * each MAC Address.
720  *
721  * @cliexpar
722  * Example of how to display the number of MAC Address entries in the PPPoE
723  * FIB table:
724  * @cliexstart{show pppoe fib}
725  *    Mac Address    session_id    Interface         sw_if_index session_index
726  * 52:54:00:53:18:33   1        GigabitEthernet0/8/0      2          0
727  * 52:54:00:53:18:55   2        GigabitEthernet0/8/1      3          1
728  * @cliexend
729 ?*/
730 VLIB_CLI_COMMAND (show_pppoe_fib_command, static) = {
731     .path = "show pppoe fib",
732     .short_help = "show pppoe fib",
733     .function = show_pppoe_fib_command_fn,
734 };
735
736 clib_error_t *
737 pppoe_init (vlib_main_t * vm)
738 {
739   pppoe_main_t *pem = &pppoe_main;
740
741   pem->vnet_main = vnet_get_main ();
742   pem->vlib_main = vm;
743
744   /* Create the hash table  */
745   BV (clib_bihash_init) (&pem->link_table, "pppoe link table",
746                          PPPOE_NUM_BUCKETS, PPPOE_MEMORY_SIZE);
747
748   BV (clib_bihash_init) (&pem->session_table, "pppoe session table",
749                          PPPOE_NUM_BUCKETS, PPPOE_MEMORY_SIZE);
750
751   ethernet_register_input_type (vm, ETHERNET_TYPE_PPPOE_SESSION,
752                                 pppoe_input_node.index);
753
754   ethernet_register_input_type (vm, ETHERNET_TYPE_PPPOE_DISCOVERY,
755                                 pppoe_cp_dispatch_node.index);
756
757   pppoe_fib_src = fib_source_allocate ("pppoe",
758                                        FIB_SOURCE_PRIORITY_HI,
759                                        FIB_SOURCE_BH_API);
760
761   return 0;
762 }
763
764 VLIB_INIT_FUNCTION (pppoe_init);
765
766 VLIB_PLUGIN_REGISTER () = {
767     .version = VPP_BUILD_VER,
768     .description = "PPP over Ethernet (PPPoE)",
769 };
770
771 /*
772  * fd.io coding-style-patch-verification: ON
773  *
774  * Local Variables:
775  * eval: (c-set-style "gnu")
776  * End:
777  */