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