ethernet: new interface registration function
[vpp.git] / src / vnet / ethernet / interface.c
1 /*
2  * Copyright (c) 2015 Cisco and/or its affiliates.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at:
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 /*
16  * ethernet_interface.c: ethernet interfaces
17  *
18  * Copyright (c) 2008 Eliot Dresselhaus
19  *
20  * Permission is hereby granted, free of charge, to any person obtaining
21  * a copy of this software and associated documentation files (the
22  * "Software"), to deal in the Software without restriction, including
23  * without limitation the rights to use, copy, modify, merge, publish,
24  * distribute, sublicense, and/or sell copies of the Software, and to
25  * permit persons to whom the Software is furnished to do so, subject to
26  * the following conditions:
27  *
28  * The above copyright notice and this permission notice shall be
29  * included in all copies or substantial portions of the Software.
30  *
31  *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
32  *  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
33  *  MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
34  *  NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
35  *  LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
36  *  OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
37  *  WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
38  */
39
40 #include <vnet/vnet.h>
41 #include <vnet/ip/ip.h>
42 #include <vnet/pg/pg.h>
43 #include <vnet/ethernet/ethernet.h>
44 //#include <vnet/ethernet/arp.h>
45 #include <vnet/l2/l2_input.h>
46 #include <vnet/l2/l2_bd.h>
47 #include <vnet/adj/adj.h>
48 #include <vnet/adj/adj_mcast.h>
49 #include <vnet/ip-neighbor/ip_neighbor.h>
50
51 /**
52  * @file
53  * @brief Loopback Interfaces.
54  *
55  * This file contains code to manage loopback interfaces.
56  */
57
58 static const u8 *
59 ethernet_ip4_mcast_dst_addr (void)
60 {
61   const static u8 ethernet_mcast_dst_mac[] = {
62     0x1, 0x0, 0x5e, 0x0, 0x0, 0x0,
63   };
64
65   return (ethernet_mcast_dst_mac);
66 }
67
68 static const u8 *
69 ethernet_ip6_mcast_dst_addr (void)
70 {
71   const static u8 ethernet_mcast_dst_mac[] = {
72     0x33, 0x33, 0x00, 0x0, 0x0, 0x0,
73   };
74
75   return (ethernet_mcast_dst_mac);
76 }
77
78 /**
79  * @brief build a rewrite string to use for sending packets of type 'link_type'
80  * to 'dst_address'
81  */
82 u8 *
83 ethernet_build_rewrite (vnet_main_t * vnm,
84                         u32 sw_if_index,
85                         vnet_link_t link_type, const void *dst_address)
86 {
87   vnet_sw_interface_t *sub_sw = vnet_get_sw_interface (vnm, sw_if_index);
88   vnet_sw_interface_t *sup_sw = vnet_get_sup_sw_interface (vnm, sw_if_index);
89   vnet_hw_interface_t *hw = vnet_get_sup_hw_interface (vnm, sw_if_index);
90   ethernet_main_t *em = &ethernet_main;
91   ethernet_interface_t *ei;
92   ethernet_header_t *h;
93   ethernet_type_t type;
94   uword n_bytes = sizeof (h[0]);
95   u8 *rewrite = NULL;
96   u8 is_p2p = 0;
97
98   if ((sub_sw->type == VNET_SW_INTERFACE_TYPE_P2P) ||
99       (sub_sw->type == VNET_SW_INTERFACE_TYPE_PIPE))
100     is_p2p = 1;
101   if (sub_sw != sup_sw)
102     {
103       if (sub_sw->sub.eth.flags.one_tag)
104         {
105           n_bytes += sizeof (ethernet_vlan_header_t);
106         }
107       else if (sub_sw->sub.eth.flags.two_tags)
108         {
109           n_bytes += 2 * (sizeof (ethernet_vlan_header_t));
110         }
111       else if (PREDICT_FALSE (is_p2p))
112         {
113           n_bytes = sizeof (ethernet_header_t);
114         }
115       if (PREDICT_FALSE (!is_p2p))
116         {
117           // Check for encaps that are not supported for L3 interfaces
118           if (!(sub_sw->sub.eth.flags.exact_match) ||
119               (sub_sw->sub.eth.flags.default_sub) ||
120               (sub_sw->sub.eth.flags.outer_vlan_id_any) ||
121               (sub_sw->sub.eth.flags.inner_vlan_id_any))
122             {
123               return 0;
124             }
125         }
126       else
127         {
128           n_bytes = sizeof (ethernet_header_t);
129         }
130     }
131
132   switch (link_type)
133     {
134 #define _(a,b) case VNET_LINK_##a: type = ETHERNET_TYPE_##b; break
135       _(IP4, IP4);
136       _(IP6, IP6);
137       _(MPLS, MPLS);
138       _(ARP, ARP);
139 #undef _
140     default:
141       return NULL;
142     }
143
144   vec_validate (rewrite, n_bytes - 1);
145   h = (ethernet_header_t *) rewrite;
146   ei = pool_elt_at_index (em->interfaces, hw->hw_instance);
147   clib_memcpy (h->src_address, &ei->address, sizeof (h->src_address));
148   if (is_p2p)
149     {
150       clib_memcpy (h->dst_address, sub_sw->p2p.client_mac,
151                    sizeof (h->dst_address));
152     }
153   else
154     {
155       if (dst_address)
156         clib_memcpy (h->dst_address, dst_address, sizeof (h->dst_address));
157       else
158         clib_memset (h->dst_address, ~0, sizeof (h->dst_address));      /* broadcast */
159     }
160
161   if (PREDICT_FALSE (!is_p2p) && sub_sw->sub.eth.flags.one_tag)
162     {
163       ethernet_vlan_header_t *outer = (void *) (h + 1);
164
165       h->type = sub_sw->sub.eth.flags.dot1ad ?
166         clib_host_to_net_u16 (ETHERNET_TYPE_DOT1AD) :
167         clib_host_to_net_u16 (ETHERNET_TYPE_VLAN);
168       outer->priority_cfi_and_id =
169         clib_host_to_net_u16 (sub_sw->sub.eth.outer_vlan_id);
170       outer->type = clib_host_to_net_u16 (type);
171
172     }
173   else if (PREDICT_FALSE (!is_p2p) && sub_sw->sub.eth.flags.two_tags)
174     {
175       ethernet_vlan_header_t *outer = (void *) (h + 1);
176       ethernet_vlan_header_t *inner = (void *) (outer + 1);
177
178       h->type = sub_sw->sub.eth.flags.dot1ad ?
179         clib_host_to_net_u16 (ETHERNET_TYPE_DOT1AD) :
180         clib_host_to_net_u16 (ETHERNET_TYPE_VLAN);
181       outer->priority_cfi_and_id =
182         clib_host_to_net_u16 (sub_sw->sub.eth.outer_vlan_id);
183       outer->type = clib_host_to_net_u16 (ETHERNET_TYPE_VLAN);
184       inner->priority_cfi_and_id =
185         clib_host_to_net_u16 (sub_sw->sub.eth.inner_vlan_id);
186       inner->type = clib_host_to_net_u16 (type);
187
188     }
189   else
190     {
191       h->type = clib_host_to_net_u16 (type);
192     }
193
194   return (rewrite);
195 }
196
197 void
198 ethernet_update_adjacency (vnet_main_t * vnm, u32 sw_if_index, u32 ai)
199 {
200   vnet_sw_interface_t *si = vnet_get_sw_interface (vnm, sw_if_index);
201
202   if ((si->type == VNET_SW_INTERFACE_TYPE_P2P) ||
203       (si->type == VNET_SW_INTERFACE_TYPE_PIPE))
204     {
205       default_update_adjacency (vnm, sw_if_index, ai);
206     }
207   else
208     {
209       ip_adjacency_t *adj;
210
211       adj = adj_get (ai);
212
213       switch (adj->lookup_next_index)
214         {
215         case IP_LOOKUP_NEXT_GLEAN:
216           adj_glean_update_rewrite (ai);
217           break;
218         case IP_LOOKUP_NEXT_ARP:
219         case IP_LOOKUP_NEXT_REWRITE:
220           ip_neighbor_update (vnm, ai);
221           break;
222         case IP_LOOKUP_NEXT_BCAST:
223           adj_nbr_update_rewrite (ai,
224                                   ADJ_NBR_REWRITE_FLAG_COMPLETE,
225                                   ethernet_build_rewrite
226                                   (vnm,
227                                    adj->rewrite_header.sw_if_index,
228                                    adj->ia_link,
229                                    VNET_REWRITE_FOR_SW_INTERFACE_ADDRESS_BROADCAST));
230           break;
231         case IP_LOOKUP_NEXT_MCAST:
232           {
233             /*
234              * Construct a partial rewrite from the known ethernet mcast dest MAC
235              */
236             u8 *rewrite;
237             u8 offset;
238
239             rewrite = ethernet_build_rewrite
240               (vnm,
241                sw_if_index,
242                adj->ia_link,
243                (adj->ia_nh_proto == FIB_PROTOCOL_IP6 ?
244                 ethernet_ip6_mcast_dst_addr () :
245                 ethernet_ip4_mcast_dst_addr ()));
246
247             /*
248              * Complete the remaining fields of the adj's rewrite to direct the
249              * complete of the rewrite at switch time by copying in the IP
250              * dst address's bytes.
251              * Ofset is 2 bytes into the destintation address.
252              */
253             offset = vec_len (rewrite) - 2;
254             adj_mcast_update_rewrite (ai, rewrite, offset);
255
256             break;
257           }
258         case IP_LOOKUP_NEXT_DROP:
259         case IP_LOOKUP_NEXT_PUNT:
260         case IP_LOOKUP_NEXT_LOCAL:
261         case IP_LOOKUP_NEXT_MCAST_MIDCHAIN:
262         case IP_LOOKUP_NEXT_MIDCHAIN:
263         case IP_LOOKUP_NEXT_ICMP_ERROR:
264         case IP_LOOKUP_N_NEXT:
265           ASSERT (0);
266           break;
267         }
268     }
269 }
270
271 static void
272 ethernet_interface_address_copy (ethernet_interface_address_t * dst,
273                                  const u8 * mac)
274 {
275   clib_memcpy (&dst->mac, (u8 *) mac, sizeof (dst->mac));
276   /*
277    * ethernet dataplane loads mac as u64, makes sure the last 2 bytes are 0
278    * for comparison purpose
279    */
280   dst->zero = 0;
281 }
282
283 static void
284 ethernet_set_mac (vnet_hw_interface_t * hi, ethernet_interface_t * ei,
285                   const u8 * mac_address)
286 {
287   vec_validate (hi->hw_address, sizeof (mac_address_t) - 1);
288   clib_memcpy (hi->hw_address, mac_address, sizeof (mac_address_t));
289   ethernet_interface_address_copy (&ei->address, mac_address);
290 }
291
292 static clib_error_t *
293 ethernet_mac_change (vnet_hw_interface_t * hi,
294                      const u8 * old_address, const u8 * mac_address)
295 {
296   ethernet_interface_t *ei;
297   ethernet_main_t *em;
298
299   em = &ethernet_main;
300   ei = pool_elt_at_index (em->interfaces, hi->hw_instance);
301
302   ethernet_set_mac (hi, ei, mac_address);
303
304   {
305     ethernet_address_change_ctx_t *cb;
306     vec_foreach (cb, em->address_change_callbacks)
307       cb->function (em, hi->sw_if_index, cb->function_opaque);
308   }
309
310   return (NULL);
311 }
312
313 /* *INDENT-OFF* */
314 VNET_HW_INTERFACE_CLASS (ethernet_hw_interface_class) = {
315   .name = "Ethernet",
316   .tx_hash_fn_type = VNET_HASH_FN_TYPE_ETHERNET,
317   .format_address = format_ethernet_address,
318   .format_header = format_ethernet_header_with_length,
319   .unformat_hw_address = unformat_ethernet_address,
320   .unformat_header = unformat_ethernet_header,
321   .build_rewrite = ethernet_build_rewrite,
322   .update_adjacency = ethernet_update_adjacency,
323   .mac_addr_change_function = ethernet_mac_change,
324 };
325 /* *INDENT-ON* */
326
327 uword
328 unformat_ethernet_interface (unformat_input_t * input, va_list * args)
329 {
330   vnet_main_t *vnm = va_arg (*args, vnet_main_t *);
331   u32 *result = va_arg (*args, u32 *);
332   u32 hw_if_index;
333   ethernet_main_t *em = &ethernet_main;
334   ethernet_interface_t *eif;
335
336   if (!unformat_user (input, unformat_vnet_hw_interface, vnm, &hw_if_index))
337     return 0;
338
339   eif = ethernet_get_interface (em, hw_if_index);
340   if (eif)
341     {
342       *result = hw_if_index;
343       return 1;
344     }
345   return 0;
346 }
347
348 u32
349 vnet_eth_register_interface (vnet_main_t *vnm,
350                              vnet_eth_interface_registration_t *r)
351 {
352   ethernet_main_t *em = &ethernet_main;
353   ethernet_interface_t *ei;
354   vnet_hw_interface_t *hi;
355   u32 hw_if_index;
356
357   pool_get (em->interfaces, ei);
358   clib_memcpy (&ei->cb, &r->cb, sizeof (vnet_eth_if_callbacks_t));
359
360   hw_if_index = vnet_register_interface (
361     vnm, r->dev_class_index, r->dev_instance,
362     ethernet_hw_interface_class.index, ei - em->interfaces);
363
364   hi = vnet_get_hw_interface (vnm, hw_if_index);
365
366   ethernet_setup_node (vnm->vlib_main, hi->output_node_index);
367
368   hi->min_packet_bytes = hi->min_supported_packet_bytes =
369     ETHERNET_MIN_PACKET_BYTES;
370   hi->max_packet_bytes = hi->max_supported_packet_bytes =
371     ETHERNET_MAX_PACKET_BYTES;
372
373   /* Default ethernet MTU, 9000 unless set by ethernet_config see below */
374   vnet_sw_interface_set_mtu (vnm, hi->sw_if_index, em->default_mtu);
375
376   ethernet_set_mac (hi, ei, r->address);
377   return hw_if_index;
378 }
379
380 void
381 ethernet_delete_interface (vnet_main_t * vnm, u32 hw_if_index)
382 {
383   ethernet_main_t *em = &ethernet_main;
384   ethernet_interface_t *ei;
385   vnet_hw_interface_t *hi;
386   main_intf_t *main_intf;
387   vlan_table_t *vlan_table;
388   u32 idx;
389
390   hi = vnet_get_hw_interface (vnm, hw_if_index);
391   ei = pool_elt_at_index (em->interfaces, hi->hw_instance);
392
393   /* Delete vlan mapping table for dot1q and dot1ad. */
394   main_intf = vec_elt_at_index (em->main_intfs, hi->hw_if_index);
395   if (main_intf->dot1q_vlans)
396     {
397       vlan_table = vec_elt_at_index (em->vlan_pool, main_intf->dot1q_vlans);
398       for (idx = 0; idx < ETHERNET_N_VLAN; idx++)
399         {
400           if (vlan_table->vlans[idx].qinqs)
401             {
402               pool_put_index (em->qinq_pool, vlan_table->vlans[idx].qinqs);
403               vlan_table->vlans[idx].qinqs = 0;
404             }
405         }
406       pool_put_index (em->vlan_pool, main_intf->dot1q_vlans);
407       main_intf->dot1q_vlans = 0;
408     }
409   if (main_intf->dot1ad_vlans)
410     {
411       vlan_table = vec_elt_at_index (em->vlan_pool, main_intf->dot1ad_vlans);
412       for (idx = 0; idx < ETHERNET_N_VLAN; idx++)
413         {
414           if (vlan_table->vlans[idx].qinqs)
415             {
416               pool_put_index (em->qinq_pool, vlan_table->vlans[idx].qinqs);
417               vlan_table->vlans[idx].qinqs = 0;
418             }
419         }
420       pool_put_index (em->vlan_pool, main_intf->dot1ad_vlans);
421       main_intf->dot1ad_vlans = 0;
422     }
423
424   vnet_delete_hw_interface (vnm, hw_if_index);
425   pool_put (em->interfaces, ei);
426 }
427
428 u32
429 ethernet_set_flags (vnet_main_t * vnm, u32 hw_if_index, u32 flags)
430 {
431   ethernet_main_t *em = &ethernet_main;
432   vnet_hw_interface_t *hi;
433   ethernet_interface_t *ei;
434   u32 opn_flags = flags & ETHERNET_INTERFACE_FLAGS_SET_OPN_MASK;
435
436   hi = vnet_get_hw_interface (vnm, hw_if_index);
437
438   ASSERT (hi->hw_class_index == ethernet_hw_interface_class.index);
439
440   ei = pool_elt_at_index (em->interfaces, hi->hw_instance);
441
442   /* preserve status bits and update last set operation bits */
443   ei->flags = (ei->flags & ETHERNET_INTERFACE_FLAGS_STATUS_MASK) | opn_flags;
444
445   if (ei->cb.flag_change)
446     {
447       switch (opn_flags)
448         {
449         case ETHERNET_INTERFACE_FLAG_DEFAULT_L3:
450           if (hi->caps & VNET_HW_IF_CAP_MAC_FILTER)
451             {
452               if (ei->cb.flag_change (vnm, hi, opn_flags) != ~0)
453                 {
454                   ei->flags |= ETHERNET_INTERFACE_FLAG_STATUS_L3;
455                   return 0;
456                 }
457               ei->flags &= ~ETHERNET_INTERFACE_FLAG_STATUS_L3;
458               return ~0;
459             }
460           /* fall through */
461         case ETHERNET_INTERFACE_FLAG_ACCEPT_ALL:
462           ei->flags &= ~ETHERNET_INTERFACE_FLAG_STATUS_L3;
463           /* fall through */
464         case ETHERNET_INTERFACE_FLAG_MTU:
465           return ei->cb.flag_change (vnm, hi, opn_flags);
466         default:
467           return ~0;
468         }
469     }
470   return ~0;
471 }
472
473 /**
474  * Echo packets back to ethernet/l2-input.
475  */
476 static uword
477 simulated_ethernet_interface_tx (vlib_main_t * vm,
478                                  vlib_node_runtime_t *
479                                  node, vlib_frame_t * frame)
480 {
481   u32 n_left_from, *from;
482   u32 next_index = 0;
483   u32 n_bytes;
484   u32 thread_index = vm->thread_index;
485   vnet_main_t *vnm = vnet_get_main ();
486   vnet_interface_main_t *im = &vnm->interface_main;
487   l2_input_config_t *config;
488   vlib_buffer_t *bufs[VLIB_FRAME_SIZE], **b;
489   u16 nexts[VLIB_FRAME_SIZE], *next;
490   u32 new_rx_sw_if_index = ~0;
491   u32 new_tx_sw_if_index = ~0;
492
493   n_left_from = frame->n_vectors;
494   from = vlib_frame_vector_args (frame);
495
496   vlib_get_buffers (vm, from, bufs, n_left_from);
497   b = bufs;
498   next = nexts;
499
500   /* Ordinarily, this is the only config lookup. */
501   config = l2input_intf_config (vnet_buffer (b[0])->sw_if_index[VLIB_TX]);
502   next_index = (l2_input_is_bridge (config) ?
503                 VNET_SIMULATED_ETHERNET_TX_NEXT_L2_INPUT :
504                 VNET_SIMULATED_ETHERNET_TX_NEXT_ETHERNET_INPUT);
505   new_tx_sw_if_index = l2_input_is_bvi (config) ? L2INPUT_BVI : ~0;
506   new_rx_sw_if_index = vnet_buffer (b[0])->sw_if_index[VLIB_TX];
507
508   while (n_left_from >= 4)
509     {
510       u32 sw_if_index0, sw_if_index1, sw_if_index2, sw_if_index3;
511       u32 not_all_match_config;
512
513       /* Prefetch next iteration. */
514       if (PREDICT_TRUE (n_left_from >= 8))
515         {
516           vlib_prefetch_buffer_header (b[4], STORE);
517           vlib_prefetch_buffer_header (b[5], STORE);
518           vlib_prefetch_buffer_header (b[6], STORE);
519           vlib_prefetch_buffer_header (b[7], STORE);
520         }
521
522       /* Make sure all pkts were transmitted on the same (loop) intfc */
523       sw_if_index0 = vnet_buffer (b[0])->sw_if_index[VLIB_TX];
524       sw_if_index1 = vnet_buffer (b[1])->sw_if_index[VLIB_TX];
525       sw_if_index2 = vnet_buffer (b[2])->sw_if_index[VLIB_TX];
526       sw_if_index3 = vnet_buffer (b[3])->sw_if_index[VLIB_TX];
527
528       not_all_match_config = (sw_if_index0 ^ sw_if_index1)
529         ^ (sw_if_index2 ^ sw_if_index3);
530       not_all_match_config += sw_if_index0 ^ new_rx_sw_if_index;
531
532       /* Speed path / expected case: all pkts on the same intfc */
533       if (PREDICT_TRUE (not_all_match_config == 0))
534         {
535           next[0] = next_index;
536           next[1] = next_index;
537           next[2] = next_index;
538           next[3] = next_index;
539           vnet_buffer (b[0])->sw_if_index[VLIB_RX] = new_rx_sw_if_index;
540           vnet_buffer (b[1])->sw_if_index[VLIB_RX] = new_rx_sw_if_index;
541           vnet_buffer (b[2])->sw_if_index[VLIB_RX] = new_rx_sw_if_index;
542           vnet_buffer (b[3])->sw_if_index[VLIB_RX] = new_rx_sw_if_index;
543           vnet_buffer (b[0])->sw_if_index[VLIB_TX] = new_tx_sw_if_index;
544           vnet_buffer (b[1])->sw_if_index[VLIB_TX] = new_tx_sw_if_index;
545           vnet_buffer (b[2])->sw_if_index[VLIB_TX] = new_tx_sw_if_index;
546           vnet_buffer (b[3])->sw_if_index[VLIB_TX] = new_tx_sw_if_index;
547           n_bytes = vlib_buffer_length_in_chain (vm, b[0]);
548           n_bytes += vlib_buffer_length_in_chain (vm, b[1]);
549           n_bytes += vlib_buffer_length_in_chain (vm, b[2]);
550           n_bytes += vlib_buffer_length_in_chain (vm, b[3]);
551
552           if (next_index == VNET_SIMULATED_ETHERNET_TX_NEXT_L2_INPUT)
553             {
554               vnet_update_l2_len (b[0]);
555               vnet_update_l2_len (b[1]);
556               vnet_update_l2_len (b[2]);
557               vnet_update_l2_len (b[3]);
558             }
559
560           /* increment TX interface stat */
561           vlib_increment_combined_counter (im->combined_sw_if_counters +
562                                            VNET_INTERFACE_COUNTER_TX,
563                                            thread_index, new_rx_sw_if_index,
564                                            4 /* pkts */ , n_bytes);
565           b += 4;
566           next += 4;
567           n_left_from -= 4;
568           continue;
569         }
570
571       /*
572        * Slow path: we know that at least one of the pkts
573        * was transmitted on a different sw_if_index, so
574        * check each sw_if_index against the cached data and proceed
575        * accordingly.
576        *
577        * This shouldn't happen, but code can (and does) bypass the
578        * per-interface output node, so deal with it.
579        */
580       if (PREDICT_FALSE (vnet_buffer (b[0])->sw_if_index[VLIB_TX]
581                          != new_rx_sw_if_index))
582         {
583           config = l2input_intf_config
584             (vnet_buffer (b[0])->sw_if_index[VLIB_TX]);
585           next_index = (l2_input_is_bridge (config) ?
586                         VNET_SIMULATED_ETHERNET_TX_NEXT_L2_INPUT :
587                         VNET_SIMULATED_ETHERNET_TX_NEXT_ETHERNET_INPUT);
588           new_tx_sw_if_index = l2_input_is_bvi (config) ? L2INPUT_BVI : ~0;
589           new_rx_sw_if_index = vnet_buffer (b[0])->sw_if_index[VLIB_TX];
590         }
591       next[0] = next_index;
592       vnet_buffer (b[0])->sw_if_index[VLIB_RX] = new_rx_sw_if_index;
593       vnet_buffer (b[0])->sw_if_index[VLIB_TX] = new_tx_sw_if_index;
594       n_bytes = vlib_buffer_length_in_chain (vm, b[0]);
595       if (next_index == VNET_SIMULATED_ETHERNET_TX_NEXT_L2_INPUT)
596         vnet_update_l2_len (b[0]);
597
598       vlib_increment_combined_counter (im->combined_sw_if_counters +
599                                        VNET_INTERFACE_COUNTER_TX,
600                                        thread_index, new_rx_sw_if_index,
601                                        1 /* pkts */ , n_bytes);
602
603       if (PREDICT_FALSE (vnet_buffer (b[1])->sw_if_index[VLIB_TX]
604                          != new_rx_sw_if_index))
605         {
606           config = l2input_intf_config
607             (vnet_buffer (b[1])->sw_if_index[VLIB_TX]);
608           next_index = (l2_input_is_bridge (config) ?
609                         VNET_SIMULATED_ETHERNET_TX_NEXT_L2_INPUT :
610                         VNET_SIMULATED_ETHERNET_TX_NEXT_ETHERNET_INPUT);
611           new_rx_sw_if_index = vnet_buffer (b[1])->sw_if_index[VLIB_TX];
612           new_tx_sw_if_index = l2_input_is_bvi (config) ? L2INPUT_BVI : ~0;
613         }
614       next[1] = next_index;
615       vnet_buffer (b[1])->sw_if_index[VLIB_RX] = new_rx_sw_if_index;
616       vnet_buffer (b[1])->sw_if_index[VLIB_TX] = new_tx_sw_if_index;
617       n_bytes = vlib_buffer_length_in_chain (vm, b[1]);
618       if (next_index == VNET_SIMULATED_ETHERNET_TX_NEXT_L2_INPUT)
619         vnet_update_l2_len (b[1]);
620
621       vlib_increment_combined_counter (im->combined_sw_if_counters +
622                                        VNET_INTERFACE_COUNTER_TX,
623                                        thread_index, new_rx_sw_if_index,
624                                        1 /* pkts */ , n_bytes);
625
626       if (PREDICT_FALSE (vnet_buffer (b[2])->sw_if_index[VLIB_TX]
627                          != new_rx_sw_if_index))
628         {
629           config = l2input_intf_config
630             (vnet_buffer (b[2])->sw_if_index[VLIB_TX]);
631           next_index = (l2_input_is_bridge (config) ?
632                         VNET_SIMULATED_ETHERNET_TX_NEXT_L2_INPUT :
633                         VNET_SIMULATED_ETHERNET_TX_NEXT_ETHERNET_INPUT);
634           new_rx_sw_if_index = vnet_buffer (b[2])->sw_if_index[VLIB_TX];
635           new_tx_sw_if_index = l2_input_is_bvi (config) ? L2INPUT_BVI : ~0;
636         }
637       next[2] = next_index;
638       vnet_buffer (b[2])->sw_if_index[VLIB_RX] = new_rx_sw_if_index;
639       vnet_buffer (b[2])->sw_if_index[VLIB_TX] = new_tx_sw_if_index;
640       n_bytes = vlib_buffer_length_in_chain (vm, b[2]);
641       if (next_index == VNET_SIMULATED_ETHERNET_TX_NEXT_L2_INPUT)
642         vnet_update_l2_len (b[2]);
643
644       vlib_increment_combined_counter (im->combined_sw_if_counters +
645                                        VNET_INTERFACE_COUNTER_TX,
646                                        thread_index, new_rx_sw_if_index,
647                                        1 /* pkts */ , n_bytes);
648
649       if (PREDICT_FALSE (vnet_buffer (b[3])->sw_if_index[VLIB_TX]
650                          != new_rx_sw_if_index))
651         {
652           config = l2input_intf_config
653             (vnet_buffer (b[3])->sw_if_index[VLIB_TX]);
654           next_index = (l2_input_is_bridge (config) ?
655                         VNET_SIMULATED_ETHERNET_TX_NEXT_L2_INPUT :
656                         VNET_SIMULATED_ETHERNET_TX_NEXT_ETHERNET_INPUT);
657           new_rx_sw_if_index = vnet_buffer (b[3])->sw_if_index[VLIB_TX];
658           new_tx_sw_if_index = l2_input_is_bvi (config) ? L2INPUT_BVI : ~0;
659         }
660       next[3] = next_index;
661       vnet_buffer (b[3])->sw_if_index[VLIB_RX] = new_rx_sw_if_index;
662       vnet_buffer (b[3])->sw_if_index[VLIB_TX] = new_tx_sw_if_index;
663       n_bytes = vlib_buffer_length_in_chain (vm, b[3]);
664       if (next_index == VNET_SIMULATED_ETHERNET_TX_NEXT_L2_INPUT)
665         vnet_update_l2_len (b[3]);
666
667       vlib_increment_combined_counter (im->combined_sw_if_counters +
668                                        VNET_INTERFACE_COUNTER_TX,
669                                        thread_index, new_rx_sw_if_index,
670                                        1 /* pkts */ , n_bytes);
671       b += 4;
672       next += 4;
673       n_left_from -= 4;
674     }
675   while (n_left_from > 0)
676     {
677       if (PREDICT_FALSE (vnet_buffer (b[0])->sw_if_index[VLIB_TX]
678                          != new_rx_sw_if_index))
679         {
680           config = l2input_intf_config
681             (vnet_buffer (b[0])->sw_if_index[VLIB_TX]);
682           next_index = (l2_input_is_bridge (config) ?
683                         VNET_SIMULATED_ETHERNET_TX_NEXT_L2_INPUT :
684                         VNET_SIMULATED_ETHERNET_TX_NEXT_ETHERNET_INPUT);
685           new_tx_sw_if_index = l2_input_is_bvi (config) ? L2INPUT_BVI : ~0;
686           new_rx_sw_if_index = vnet_buffer (b[0])->sw_if_index[VLIB_TX];
687         }
688       next[0] = next_index;
689       vnet_buffer (b[0])->sw_if_index[VLIB_RX] = new_rx_sw_if_index;
690       vnet_buffer (b[0])->sw_if_index[VLIB_TX] = new_tx_sw_if_index;
691       n_bytes = vlib_buffer_length_in_chain (vm, b[0]);
692       if (next_index == VNET_SIMULATED_ETHERNET_TX_NEXT_L2_INPUT)
693         vnet_update_l2_len (b[0]);
694
695       vlib_increment_combined_counter (im->combined_sw_if_counters +
696                                        VNET_INTERFACE_COUNTER_TX,
697                                        thread_index, new_rx_sw_if_index,
698                                        1 /* pkts */ , n_bytes);
699       b += 1;
700       next += 1;
701       n_left_from -= 1;
702     }
703
704   vlib_buffer_enqueue_to_next (vm, node, from, nexts, frame->n_vectors);
705
706   return frame->n_vectors;
707 }
708
709 static u8 *
710 format_simulated_ethernet_name (u8 * s, va_list * args)
711 {
712   u32 dev_instance = va_arg (*args, u32);
713   return format (s, "loop%d", dev_instance);
714 }
715
716 static clib_error_t *
717 simulated_ethernet_admin_up_down (vnet_main_t * vnm, u32 hw_if_index,
718                                   u32 flags)
719 {
720   u32 hw_flags = (flags & VNET_SW_INTERFACE_FLAG_ADMIN_UP) ?
721     VNET_HW_INTERFACE_FLAG_LINK_UP : 0;
722   vnet_hw_interface_set_flags (vnm, hw_if_index, hw_flags);
723   return 0;
724 }
725
726 static clib_error_t *
727 simulated_ethernet_mac_change (vnet_hw_interface_t * hi,
728                                const u8 * old_address, const u8 * mac_address)
729 {
730   l2input_interface_mac_change (hi->sw_if_index, old_address, mac_address);
731
732   return (NULL);
733 }
734
735
736 /* *INDENT-OFF* */
737 VNET_DEVICE_CLASS (ethernet_simulated_device_class) = {
738   .name = "Loopback",
739   .format_device_name = format_simulated_ethernet_name,
740   .tx_function = simulated_ethernet_interface_tx,
741   .admin_up_down_function = simulated_ethernet_admin_up_down,
742   .mac_addr_change_function = simulated_ethernet_mac_change,
743 };
744 /* *INDENT-ON* */
745
746 /*
747  * Maintain a bitmap of allocated loopback instance numbers.
748  */
749 #define LOOPBACK_MAX_INSTANCE           (16 * 1024)
750
751 static u32
752 loopback_instance_alloc (u8 is_specified, u32 want)
753 {
754   ethernet_main_t *em = &ethernet_main;
755
756   /*
757    * Check for dynamically allocaetd instance number.
758    */
759   if (!is_specified)
760     {
761       u32 bit;
762
763       bit = clib_bitmap_first_clear (em->bm_loopback_instances);
764       if (bit >= LOOPBACK_MAX_INSTANCE)
765         {
766           return ~0;
767         }
768       em->bm_loopback_instances = clib_bitmap_set (em->bm_loopback_instances,
769                                                    bit, 1);
770       return bit;
771     }
772
773   /*
774    * In range?
775    */
776   if (want >= LOOPBACK_MAX_INSTANCE)
777     {
778       return ~0;
779     }
780
781   /*
782    * Already in use?
783    */
784   if (clib_bitmap_get (em->bm_loopback_instances, want))
785     {
786       return ~0;
787     }
788
789   /*
790    * Grant allocation request.
791    */
792   em->bm_loopback_instances = clib_bitmap_set (em->bm_loopback_instances,
793                                                want, 1);
794
795   return want;
796 }
797
798 static int
799 loopback_instance_free (u32 instance)
800 {
801   ethernet_main_t *em = &ethernet_main;
802
803   if (instance >= LOOPBACK_MAX_INSTANCE)
804     {
805       return -1;
806     }
807
808   if (clib_bitmap_get (em->bm_loopback_instances, instance) == 0)
809     {
810       return -1;
811     }
812
813   em->bm_loopback_instances = clib_bitmap_set (em->bm_loopback_instances,
814                                                instance, 0);
815   return 0;
816 }
817
818 int
819 vnet_create_loopback_interface (u32 * sw_if_indexp, u8 * mac_address,
820                                 u8 is_specified, u32 user_instance)
821 {
822   vnet_main_t *vnm = vnet_get_main ();
823   vlib_main_t *vm = vlib_get_main ();
824   u32 instance;
825   u8 address[6];
826   u32 hw_if_index;
827   vnet_hw_interface_t *hw_if;
828   u32 slot;
829
830   ASSERT (sw_if_indexp);
831
832   *sw_if_indexp = (u32) ~ 0;
833
834   clib_memset (address, 0, sizeof (address));
835
836   /*
837    * Allocate a loopback instance.  Either select on dynamically
838    * or try to use the desired user_instance number.
839    */
840   instance = loopback_instance_alloc (is_specified, user_instance);
841   if (instance == ~0)
842     {
843       return VNET_API_ERROR_INVALID_REGISTRATION;
844     }
845
846   /*
847    * Default MAC address (dead:0000:0000 + instance) is allocated
848    * if zero mac_address is configured. Otherwise, user-configurable MAC
849    * address is programmed on the loopback interface.
850    */
851   if (memcmp (address, mac_address, sizeof (address)))
852     clib_memcpy (address, mac_address, sizeof (address));
853   else
854     {
855       address[0] = 0xde;
856       address[1] = 0xad;
857       address[5] = instance;
858     }
859
860   vnet_eth_interface_registration_t eir = {};
861   eir.dev_class_index = ethernet_simulated_device_class.index;
862   eir.dev_instance = instance;
863   eir.address = address;
864   hw_if_index = vnet_eth_register_interface (vnm, &eir);
865   hw_if = vnet_get_hw_interface (vnm, hw_if_index);
866   slot = vlib_node_add_named_next_with_slot
867     (vm, hw_if->tx_node_index,
868      "ethernet-input", VNET_SIMULATED_ETHERNET_TX_NEXT_ETHERNET_INPUT);
869   ASSERT (slot == VNET_SIMULATED_ETHERNET_TX_NEXT_ETHERNET_INPUT);
870
871   slot = vlib_node_add_named_next_with_slot
872     (vm, hw_if->tx_node_index,
873      "l2-input", VNET_SIMULATED_ETHERNET_TX_NEXT_L2_INPUT);
874   ASSERT (slot == VNET_SIMULATED_ETHERNET_TX_NEXT_L2_INPUT);
875
876   {
877     vnet_sw_interface_t *si = vnet_get_hw_sw_interface (vnm, hw_if_index);
878     *sw_if_indexp = si->sw_if_index;
879
880     /* By default don't flood to loopbacks, as packets just keep
881      * coming back ... If this loopback becomes a BVI, we'll change it */
882     si->flood_class = VNET_FLOOD_CLASS_NO_FLOOD;
883   }
884
885   return 0;
886 }
887
888 static clib_error_t *
889 create_simulated_ethernet_interfaces (vlib_main_t * vm,
890                                       unformat_input_t * input,
891                                       vlib_cli_command_t * cmd)
892 {
893   int rv;
894   u32 sw_if_index;
895   u8 mac_address[6];
896   u8 is_specified = 0;
897   u32 user_instance = 0;
898
899   clib_memset (mac_address, 0, sizeof (mac_address));
900
901   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
902     {
903       if (unformat (input, "mac %U", unformat_ethernet_address, mac_address))
904         ;
905       if (unformat (input, "instance %d", &user_instance))
906         is_specified = 1;
907       else
908         break;
909     }
910
911   rv = vnet_create_loopback_interface (&sw_if_index, mac_address,
912                                        is_specified, user_instance);
913
914   if (rv)
915     return clib_error_return (0, "vnet_create_loopback_interface failed");
916
917   vlib_cli_output (vm, "%U\n", format_vnet_sw_if_index_name, vnet_get_main (),
918                    sw_if_index);
919   return 0;
920 }
921
922 /*?
923  * Create a loopback interface. Optionally, a MAC Address can be
924  * provided. If not provided, de:ad:00:00:00:<loopId> will be used.
925  *
926  * @cliexpar
927  * The following two command syntaxes are equivalent:
928  * @cliexcmd{loopback create-interface [mac <mac-addr>] [instance <instance>]}
929  * @cliexcmd{create loopback interface [mac <mac-addr>] [instance <instance>]}
930  * Example of how to create a loopback interface:
931  * @cliexcmd{loopback create-interface}
932 ?*/
933 /* *INDENT-OFF* */
934 VLIB_CLI_COMMAND (create_simulated_ethernet_interface_command, static) = {
935   .path = "loopback create-interface",
936   .short_help = "loopback create-interface [mac <mac-addr>] [instance <instance>]",
937   .function = create_simulated_ethernet_interfaces,
938 };
939 /* *INDENT-ON* */
940
941 /*?
942  * Create a loopback interface. Optionally, a MAC Address can be
943  * provided. If not provided, de:ad:00:00:00:<loopId> will be used.
944  *
945  * @cliexpar
946  * The following two command syntaxes are equivalent:
947  * @cliexcmd{loopback create-interface [mac <mac-addr>] [instance <instance>]}
948  * @cliexcmd{create loopback interface [mac <mac-addr>] [instance <instance>]}
949  * Example of how to create a loopback interface:
950  * @cliexcmd{create loopback interface}
951 ?*/
952 /* *INDENT-OFF* */
953 VLIB_CLI_COMMAND (create_loopback_interface_command, static) = {
954   .path = "create loopback interface",
955   .short_help = "create loopback interface [mac <mac-addr>] [instance <instance>]",
956   .function = create_simulated_ethernet_interfaces,
957 };
958 /* *INDENT-ON* */
959
960 ethernet_interface_t *
961 ethernet_get_interface (ethernet_main_t * em, u32 hw_if_index)
962 {
963   vnet_hw_interface_t *i =
964     vnet_get_hw_interface (vnet_get_main (), hw_if_index);
965   return (i->hw_class_index ==
966           ethernet_hw_interface_class.
967           index ? pool_elt_at_index (em->interfaces, i->hw_instance) : 0);
968 }
969
970 mac_address_t *
971 ethernet_interface_add_del_address (ethernet_main_t * em,
972                                     u32 hw_if_index, const u8 * address,
973                                     u8 is_add)
974 {
975   ethernet_interface_t *ei = ethernet_get_interface (em, hw_if_index);
976   ethernet_interface_address_t *if_addr = 0;
977   int found = 0;
978
979   /* return if there is not an ethernet interface for this hw interface */
980   if (!ei)
981     return 0;
982
983   /* determine whether the address is configured on the interface */
984   vec_foreach (if_addr, ei->secondary_addrs)
985   {
986     if (ethernet_mac_address_equal (if_addr->mac.bytes, address))
987       {
988         found = 1;
989         break;
990       }
991   }
992
993   if (is_add)
994     {
995       if (!found)
996         {
997           /* address not found yet: add it */
998           vec_add2 (ei->secondary_addrs, if_addr, 1);
999           ethernet_interface_address_copy (if_addr, address);
1000         }
1001       return &if_addr->mac;
1002     }
1003
1004   /* delete case */
1005   if (found)
1006     vec_delete (ei->secondary_addrs, 1, if_addr - ei->secondary_addrs);
1007
1008   return 0;
1009 }
1010
1011 int
1012 vnet_delete_loopback_interface (u32 sw_if_index)
1013 {
1014   vnet_main_t *vnm = vnet_get_main ();
1015
1016   if (pool_is_free_index (vnm->interface_main.sw_interfaces, sw_if_index))
1017     return VNET_API_ERROR_INVALID_SW_IF_INDEX;
1018
1019   vnet_hw_interface_t *hw = vnet_get_sup_hw_interface (vnm, sw_if_index);
1020   if (hw == 0 || hw->dev_class_index != ethernet_simulated_device_class.index)
1021     return VNET_API_ERROR_INVALID_SW_IF_INDEX;
1022
1023   if (loopback_instance_free (hw->dev_instance) < 0)
1024     return VNET_API_ERROR_INVALID_SW_IF_INDEX;
1025
1026   ethernet_delete_interface (vnm, hw->hw_if_index);
1027
1028   return 0;
1029 }
1030
1031 int
1032 vnet_create_sub_interface (u32 sw_if_index, u32 id,
1033                            u32 flags, u16 inner_vlan_id, u16 outer_vlan_id,
1034                            u32 * sub_sw_if_index)
1035 {
1036   vnet_main_t *vnm = vnet_get_main ();
1037   vnet_interface_main_t *im = &vnm->interface_main;
1038   vnet_hw_interface_t *hi;
1039   u64 sup_and_sub_key = ((u64) (sw_if_index) << 32) | (u64) id;
1040   vnet_sw_interface_t template;
1041   uword *p;
1042   u64 *kp;
1043
1044   hi = vnet_get_sup_hw_interface (vnm, sw_if_index);
1045
1046   p = hash_get_mem (im->sw_if_index_by_sup_and_sub, &sup_and_sub_key);
1047   if (p)
1048     {
1049       return (VNET_API_ERROR_VLAN_ALREADY_EXISTS);
1050     }
1051
1052   clib_memset (&template, 0, sizeof (template));
1053   template.type = VNET_SW_INTERFACE_TYPE_SUB;
1054   template.flood_class = VNET_FLOOD_CLASS_NORMAL;
1055   template.sup_sw_if_index = sw_if_index;
1056   template.sub.id = id;
1057   template.sub.eth.raw_flags = flags;
1058   template.sub.eth.outer_vlan_id = outer_vlan_id;
1059   template.sub.eth.inner_vlan_id = inner_vlan_id;
1060
1061   if (vnet_create_sw_interface (vnm, &template, sub_sw_if_index))
1062     return (VNET_API_ERROR_UNSPECIFIED);
1063
1064   kp = clib_mem_alloc (sizeof (*kp));
1065   *kp = sup_and_sub_key;
1066
1067   hash_set (hi->sub_interface_sw_if_index_by_id, id, *sub_sw_if_index);
1068   hash_set_mem (im->sw_if_index_by_sup_and_sub, kp, *sub_sw_if_index);
1069
1070   return (0);
1071 }
1072
1073 int
1074 vnet_delete_sub_interface (u32 sw_if_index)
1075 {
1076   vnet_main_t *vnm = vnet_get_main ();
1077   vnet_sw_interface_t *si;
1078   int rv = 0;
1079
1080   if (pool_is_free_index (vnm->interface_main.sw_interfaces, sw_if_index))
1081     return VNET_API_ERROR_INVALID_SW_IF_INDEX;
1082
1083   si = vnet_get_sw_interface (vnm, sw_if_index);
1084   if (si->type == VNET_SW_INTERFACE_TYPE_SUB ||
1085       si->type == VNET_SW_INTERFACE_TYPE_PIPE ||
1086       si->type == VNET_SW_INTERFACE_TYPE_P2P)
1087     {
1088       vnet_interface_main_t *im = &vnm->interface_main;
1089       vnet_hw_interface_t *hi = vnet_get_sup_hw_interface (vnm, sw_if_index);
1090       u64 sup_and_sub_key =
1091         ((u64) (si->sup_sw_if_index) << 32) | (u64) si->sub.id;
1092       hash_unset_mem_free (&im->sw_if_index_by_sup_and_sub, &sup_and_sub_key);
1093       hash_unset (hi->sub_interface_sw_if_index_by_id, si->sub.id);
1094       vnet_delete_sw_interface (vnm, sw_if_index);
1095     }
1096   else
1097     rv = VNET_API_ERROR_INVALID_SUB_SW_IF_INDEX;
1098
1099   return rv;
1100 }
1101
1102 static clib_error_t *
1103 delete_simulated_ethernet_interfaces (vlib_main_t * vm,
1104                                       unformat_input_t * input,
1105                                       vlib_cli_command_t * cmd)
1106 {
1107   int rv;
1108   u32 sw_if_index = ~0;
1109   vnet_main_t *vnm = vnet_get_main ();
1110
1111   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1112     {
1113       if (unformat (input, "intfc %U",
1114                     unformat_vnet_sw_interface, vnm, &sw_if_index))
1115         ;
1116       else
1117         break;
1118     }
1119
1120   if (sw_if_index == ~0)
1121     return clib_error_return (0, "interface not specified");
1122
1123   rv = vnet_delete_loopback_interface (sw_if_index);
1124
1125   if (rv)
1126     return clib_error_return (0, "vnet_delete_loopback_interface failed");
1127
1128   return 0;
1129 }
1130
1131 static clib_error_t *
1132 delete_sub_interface (vlib_main_t * vm,
1133                       unformat_input_t * input, vlib_cli_command_t * cmd)
1134 {
1135   int rv = 0;
1136   u32 sw_if_index = ~0;
1137   vnet_main_t *vnm = vnet_get_main ();
1138
1139   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1140     {
1141       if (unformat
1142           (input, "%U", unformat_vnet_sw_interface, vnm, &sw_if_index))
1143         ;
1144       else
1145         break;
1146     }
1147   if (sw_if_index == ~0)
1148     return clib_error_return (0, "interface doesn't exist");
1149
1150   if (pool_is_free_index (vnm->interface_main.sw_interfaces, sw_if_index))
1151     rv = VNET_API_ERROR_INVALID_SW_IF_INDEX;
1152   else
1153     rv = vnet_delete_sub_interface (sw_if_index);
1154   if (rv)
1155     return clib_error_return (0, "delete_subinterface_interface failed");
1156   return 0;
1157 }
1158
1159 /*?
1160  * Delete a loopback interface.
1161  *
1162  * @cliexpar
1163  * The following two command syntaxes are equivalent:
1164  * @cliexcmd{loopback delete-interface intfc <interface>}
1165  * @cliexcmd{delete loopback interface intfc <interface>}
1166  * Example of how to delete a loopback interface:
1167  * @cliexcmd{loopback delete-interface intfc loop0}
1168 ?*/
1169 /* *INDENT-OFF* */
1170 VLIB_CLI_COMMAND (delete_simulated_ethernet_interface_command, static) = {
1171   .path = "loopback delete-interface",
1172   .short_help = "loopback delete-interface intfc <interface>",
1173   .function = delete_simulated_ethernet_interfaces,
1174 };
1175 /* *INDENT-ON* */
1176
1177 /*?
1178  * Delete a loopback interface.
1179  *
1180  * @cliexpar
1181  * The following two command syntaxes are equivalent:
1182  * @cliexcmd{loopback delete-interface intfc <interface>}
1183  * @cliexcmd{delete loopback interface intfc <interface>}
1184  * Example of how to delete a loopback interface:
1185  * @cliexcmd{delete loopback interface intfc loop0}
1186 ?*/
1187 /* *INDENT-OFF* */
1188 VLIB_CLI_COMMAND (delete_loopback_interface_command, static) = {
1189   .path = "delete loopback interface",
1190   .short_help = "delete loopback interface intfc <interface>",
1191   .function = delete_simulated_ethernet_interfaces,
1192 };
1193 /* *INDENT-ON* */
1194
1195 /*?
1196  * Delete a sub-interface.
1197  *
1198  * @cliexpar
1199  * Example of how to delete a sub-interface:
1200  * @cliexcmd{delete sub-interface GigabitEthernet0/8/0.200}
1201 ?*/
1202 /* *INDENT-OFF* */
1203 VLIB_CLI_COMMAND (delete_sub_interface_command, static) = {
1204   .path = "delete sub-interface",
1205   .short_help = "delete sub-interface <interface>",
1206   .function = delete_sub_interface,
1207 };
1208 /* *INDENT-ON* */
1209
1210 /* ethernet { ... } configuration. */
1211 /*?
1212  *
1213  * @cfgcmd{default-mtu &lt;n&gt;}
1214  * Specify the default mtu in the range of 64-9000. The default is 9000 bytes.
1215  *
1216  */
1217 static clib_error_t *
1218 ethernet_config (vlib_main_t * vm, unformat_input_t * input)
1219 {
1220   ethernet_main_t *em = &ethernet_main;
1221
1222   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1223     {
1224       if (unformat (input, "default-mtu %u", &em->default_mtu))
1225         {
1226           if (em->default_mtu < 64 || em->default_mtu > 9000)
1227             return clib_error_return (0, "default MTU must be >=64, <=9000");
1228         }
1229       else
1230         {
1231           return clib_error_return (0, "unknown input '%U'",
1232                                     format_unformat_error, input);
1233         }
1234     }
1235   return 0;
1236 }
1237
1238 VLIB_CONFIG_FUNCTION (ethernet_config, "ethernet");
1239
1240 /*
1241  * fd.io coding-style-patch-verification: ON
1242  *
1243  * Local Variables:
1244  * eval: (c-set-style "gnu")
1245  * End:
1246  */