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