772c3bca75fa36800cb5abe137af0b67b9b50e40
[vpp.git] / vnet / vnet / 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  * interface.c: VNET interfaces/sub-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/plugin/plugin.h>
42
43 #define VNET_INTERFACE_SET_FLAGS_HELPER_IS_CREATE (1 << 0)
44 #define VNET_INTERFACE_SET_FLAGS_HELPER_WANT_REDISTRIBUTE (1 << 1)
45
46 static clib_error_t *vnet_hw_interface_set_flags_helper (vnet_main_t * vnm,
47                                                          u32 hw_if_index,
48                                                          u32 flags,
49                                                          u32 helper_flags);
50
51 static clib_error_t *vnet_sw_interface_set_flags_helper (vnet_main_t * vnm,
52                                                          u32 sw_if_index,
53                                                          u32 flags,
54                                                          u32 helper_flags);
55
56 static clib_error_t *vnet_hw_interface_set_class_helper (vnet_main_t * vnm,
57                                                          u32 hw_if_index,
58                                                          u32 hw_class_index,
59                                                          u32 redistribute);
60
61 typedef struct
62 {
63   /* Either sw or hw interface index. */
64   u32 sw_hw_if_index;
65
66   /* Flags. */
67   u32 flags;
68 } vnet_sw_hw_interface_state_t;
69
70 static void
71 serialize_vec_vnet_sw_hw_interface_state (serialize_main_t * m, va_list * va)
72 {
73   vnet_sw_hw_interface_state_t *s =
74     va_arg (*va, vnet_sw_hw_interface_state_t *);
75   u32 n = va_arg (*va, u32);
76   u32 i;
77   for (i = 0; i < n; i++)
78     {
79       serialize_integer (m, s[i].sw_hw_if_index,
80                          sizeof (s[i].sw_hw_if_index));
81       serialize_integer (m, s[i].flags, sizeof (s[i].flags));
82     }
83 }
84
85 static void
86 unserialize_vec_vnet_sw_hw_interface_state (serialize_main_t * m,
87                                             va_list * va)
88 {
89   vnet_sw_hw_interface_state_t *s =
90     va_arg (*va, vnet_sw_hw_interface_state_t *);
91   u32 n = va_arg (*va, u32);
92   u32 i;
93   for (i = 0; i < n; i++)
94     {
95       unserialize_integer (m, &s[i].sw_hw_if_index,
96                            sizeof (s[i].sw_hw_if_index));
97       unserialize_integer (m, &s[i].flags, sizeof (s[i].flags));
98     }
99 }
100
101 static void
102 serialize_vnet_sw_hw_interface_set_flags (serialize_main_t * m, va_list * va)
103 {
104   vnet_sw_hw_interface_state_t *s =
105     va_arg (*va, vnet_sw_hw_interface_state_t *);
106   serialize (m, serialize_vec_vnet_sw_hw_interface_state, s, 1);
107 }
108
109 static void
110 unserialize_vnet_sw_interface_set_flags (serialize_main_t * m, va_list * va)
111 {
112   CLIB_UNUSED (mc_main_t * mc) = va_arg (*va, mc_main_t *);
113   vnet_sw_hw_interface_state_t s;
114
115   unserialize (m, unserialize_vec_vnet_sw_hw_interface_state, &s, 1);
116
117   vnet_sw_interface_set_flags_helper
118     (vnet_get_main (), s.sw_hw_if_index, s.flags,
119      /* helper_flags no redistribution */ 0);
120 }
121
122 static void
123 unserialize_vnet_hw_interface_set_flags (serialize_main_t * m, va_list * va)
124 {
125   CLIB_UNUSED (mc_main_t * mc) = va_arg (*va, mc_main_t *);
126   vnet_sw_hw_interface_state_t s;
127
128   unserialize (m, unserialize_vec_vnet_sw_hw_interface_state, &s, 1);
129
130   vnet_hw_interface_set_flags_helper
131     (vnet_get_main (), s.sw_hw_if_index, s.flags,
132      /* helper_flags no redistribution */ 0);
133 }
134
135 MC_SERIALIZE_MSG (vnet_sw_interface_set_flags_msg, static) =
136 {
137 .name = "vnet_sw_interface_set_flags",.serialize =
138     serialize_vnet_sw_hw_interface_set_flags,.unserialize =
139     unserialize_vnet_sw_interface_set_flags,};
140
141 MC_SERIALIZE_MSG (vnet_hw_interface_set_flags_msg, static) =
142 {
143 .name = "vnet_hw_interface_set_flags",.serialize =
144     serialize_vnet_sw_hw_interface_set_flags,.unserialize =
145     unserialize_vnet_hw_interface_set_flags,};
146
147 void
148 serialize_vnet_interface_state (serialize_main_t * m, va_list * va)
149 {
150   vnet_main_t *vnm = va_arg (*va, vnet_main_t *);
151   vnet_sw_hw_interface_state_t *sts = 0, *st;
152   vnet_sw_interface_t *sif;
153   vnet_hw_interface_t *hif;
154   vnet_interface_main_t *im = &vnm->interface_main;
155
156   /* Serialize hardware interface classes since they may have changed.
157      Must do this before sending up/down flags. */
158   /* *INDENT-OFF* */
159   pool_foreach (hif, im->hw_interfaces, ({
160     vnet_hw_interface_class_t * hw_class = vnet_get_hw_interface_class (vnm, hif->hw_class_index);
161     serialize_cstring (m, hw_class->name);
162   }));
163   /* *INDENT-ON* */
164
165   /* Send sw/hw interface state when non-zero. */
166   /* *INDENT-OFF* */
167   pool_foreach (sif, im->sw_interfaces, ({
168     if (sif->flags != 0)
169       {
170         vec_add2 (sts, st, 1);
171         st->sw_hw_if_index = sif->sw_if_index;
172         st->flags = sif->flags;
173       }
174   }));
175   /* *INDENT-ON* */
176
177   vec_serialize (m, sts, serialize_vec_vnet_sw_hw_interface_state);
178
179   if (sts)
180     _vec_len (sts) = 0;
181
182   /* *INDENT-OFF* */
183   pool_foreach (hif, im->hw_interfaces, ({
184     if (hif->flags != 0)
185       {
186         vec_add2 (sts, st, 1);
187         st->sw_hw_if_index = hif->hw_if_index;
188         st->flags = hif->flags;
189       }
190   }));
191   /* *INDENT-ON* */
192
193   vec_serialize (m, sts, serialize_vec_vnet_sw_hw_interface_state);
194
195   vec_free (sts);
196 }
197
198 void
199 unserialize_vnet_interface_state (serialize_main_t * m, va_list * va)
200 {
201   vnet_main_t *vnm = va_arg (*va, vnet_main_t *);
202   vnet_sw_hw_interface_state_t *sts = 0, *st;
203
204   /* First set interface hardware class. */
205   {
206     vnet_interface_main_t *im = &vnm->interface_main;
207     vnet_hw_interface_t *hif;
208     char *class_name;
209     uword *p;
210     clib_error_t *error;
211
212     /* *INDENT-OFF* */
213     pool_foreach (hif, im->hw_interfaces, ({
214       unserialize_cstring (m, &class_name);
215       p = hash_get_mem (im->hw_interface_class_by_name, class_name);
216       ASSERT (p != 0);
217       error = vnet_hw_interface_set_class_helper (vnm, hif->hw_if_index, p[0], /* redistribute */ 0);
218       if (error)
219         clib_error_report (error);
220       vec_free (class_name);
221     }));
222     /* *INDENT-ON* */
223   }
224
225   vec_unserialize (m, &sts, unserialize_vec_vnet_sw_hw_interface_state);
226   vec_foreach (st, sts)
227     vnet_sw_interface_set_flags_helper (vnm, st->sw_hw_if_index, st->flags,
228                                         /* no distribute */ 0);
229   vec_free (sts);
230
231   vec_unserialize (m, &sts, unserialize_vec_vnet_sw_hw_interface_state);
232   vec_foreach (st, sts)
233     vnet_hw_interface_set_flags_helper (vnm, st->sw_hw_if_index, st->flags,
234                                         /* no distribute */ 0);
235   vec_free (sts);
236 }
237
238 static clib_error_t *
239 call_elf_section_interface_callbacks (vnet_main_t * vnm, u32 if_index,
240                                       u32 flags,
241                                       _vnet_interface_function_list_elt_t *
242                                       elt)
243 {
244   clib_error_t *error = 0;
245
246   while (elt)
247     {
248       error = elt->fp (vnm, if_index, flags);
249       if (error)
250         return error;
251       elt = elt->next_interface_function;
252     }
253   return error;
254 }
255
256 static clib_error_t *
257 call_hw_interface_add_del_callbacks (vnet_main_t * vnm, u32 hw_if_index,
258                                      u32 is_create)
259 {
260   vnet_hw_interface_t *hi = vnet_get_hw_interface (vnm, hw_if_index);
261   vnet_hw_interface_class_t *hw_class =
262     vnet_get_hw_interface_class (vnm, hi->hw_class_index);
263   vnet_device_class_t *dev_class =
264     vnet_get_device_class (vnm, hi->dev_class_index);
265   clib_error_t *error = 0;
266
267   if (hw_class->interface_add_del_function
268       && (error =
269           hw_class->interface_add_del_function (vnm, hw_if_index, is_create)))
270     return error;
271
272   if (dev_class->interface_add_del_function
273       && (error =
274           dev_class->interface_add_del_function (vnm, hw_if_index,
275                                                  is_create)))
276     return error;
277
278   error = call_elf_section_interface_callbacks
279     (vnm, hw_if_index, is_create, vnm->hw_interface_add_del_functions);
280
281   return error;
282 }
283
284 static clib_error_t *
285 call_sw_interface_add_del_callbacks (vnet_main_t * vnm, u32 sw_if_index,
286                                      u32 is_create)
287 {
288   return call_elf_section_interface_callbacks
289     (vnm, sw_if_index, is_create, vnm->sw_interface_add_del_functions);
290 }
291
292 #define VNET_INTERFACE_SET_FLAGS_HELPER_IS_CREATE (1 << 0)
293 #define VNET_INTERFACE_SET_FLAGS_HELPER_WANT_REDISTRIBUTE (1 << 1)
294
295 static clib_error_t *
296 vnet_hw_interface_set_flags_helper (vnet_main_t * vnm, u32 hw_if_index,
297                                     u32 flags, u32 helper_flags)
298 {
299   vnet_hw_interface_t *hi = vnet_get_hw_interface (vnm, hw_if_index);
300   vnet_hw_interface_class_t *hw_class =
301     vnet_get_hw_interface_class (vnm, hi->hw_class_index);
302   vnet_device_class_t *dev_class =
303     vnet_get_device_class (vnm, hi->dev_class_index);
304   vlib_main_t *vm = vnm->vlib_main;
305   u32 mask;
306   clib_error_t *error = 0;
307   u32 is_create =
308     (helper_flags & VNET_INTERFACE_SET_FLAGS_HELPER_IS_CREATE) != 0;
309
310   mask =
311     (VNET_HW_INTERFACE_FLAG_LINK_UP | VNET_HW_INTERFACE_FLAG_DUPLEX_MASK |
312      VNET_HW_INTERFACE_FLAG_SPEED_MASK);
313   flags &= mask;
314
315   /* Call hardware interface add/del callbacks. */
316   if (is_create)
317     call_hw_interface_add_del_callbacks (vnm, hw_if_index, is_create);
318
319   /* Already in the desired state? */
320   if (!is_create && (hi->flags & mask) == flags)
321     goto done;
322
323   /* Some interface classes do not redistribute (e.g. are local). */
324   if (!dev_class->redistribute)
325     helper_flags &= ~VNET_INTERFACE_SET_FLAGS_HELPER_WANT_REDISTRIBUTE;
326
327   if (vm->mc_main
328       && (helper_flags & VNET_INTERFACE_SET_FLAGS_HELPER_WANT_REDISTRIBUTE))
329     {
330       vnet_sw_hw_interface_state_t s;
331       s.sw_hw_if_index = hw_if_index;
332       s.flags = flags;
333       mc_serialize (vm->mc_main, &vnet_hw_interface_set_flags_msg, &s);
334     }
335
336   if ((hi->flags & VNET_HW_INTERFACE_FLAG_LINK_UP) !=
337       (flags & VNET_HW_INTERFACE_FLAG_LINK_UP))
338     {
339       /* Do hardware class (e.g. ethernet). */
340       if (hw_class->link_up_down_function
341           && (error = hw_class->link_up_down_function (vnm, hw_if_index,
342                                                        flags)))
343         goto done;
344
345       error = call_elf_section_interface_callbacks
346         (vnm, hw_if_index, flags, vnm->hw_interface_link_up_down_functions);
347
348       if (error)
349         goto done;
350     }
351
352   hi->flags &= ~mask;
353   hi->flags |= flags;
354
355 done:
356   return error;
357 }
358
359 static clib_error_t *
360 vnet_sw_interface_set_flags_helper (vnet_main_t * vnm, u32 sw_if_index,
361                                     u32 flags, u32 helper_flags)
362 {
363   vnet_sw_interface_t *si = vnet_get_sw_interface (vnm, sw_if_index);
364   vlib_main_t *vm = vnm->vlib_main;
365   u32 mask;
366   clib_error_t *error = 0;
367   u32 is_create =
368     (helper_flags & VNET_INTERFACE_SET_FLAGS_HELPER_IS_CREATE) != 0;
369   u32 old_flags;
370
371   mask = VNET_SW_INTERFACE_FLAG_ADMIN_UP | VNET_SW_INTERFACE_FLAG_PUNT;
372   flags &= mask;
373
374   if (is_create)
375     {
376       error =
377         call_sw_interface_add_del_callbacks (vnm, sw_if_index, is_create);
378       if (error)
379         goto done;
380
381       if (flags & VNET_SW_INTERFACE_FLAG_ADMIN_UP)
382         {
383           /* Notify everyone when the interface is created as admin up */
384           error = call_elf_section_interface_callbacks (vnm, sw_if_index,
385                                                         flags,
386                                                         vnm->
387                                                         sw_interface_admin_up_down_functions);
388           if (error)
389             goto done;
390         }
391     }
392   else
393     {
394       vnet_sw_interface_t *si_sup = si;
395
396       /* Check that super interface is in correct state. */
397       if (si->type == VNET_SW_INTERFACE_TYPE_SUB)
398         {
399           si_sup = vnet_get_sw_interface (vnm, si->sup_sw_if_index);
400
401           /* Check to see if we're bringing down the soft interface and if it's parent is up */
402           if ((flags != (si_sup->flags & mask)) &&
403               (!((flags == 0)
404                  && ((si_sup->flags & mask) ==
405                      VNET_SW_INTERFACE_FLAG_ADMIN_UP))))
406             {
407               error = clib_error_return (0, "super-interface %U must be %U",
408                                          format_vnet_sw_interface_name, vnm,
409                                          si_sup,
410                                          format_vnet_sw_interface_flags,
411                                          flags);
412               goto done;
413             }
414         }
415
416       /* Donot change state for slave link of bonded interfaces */
417       if (si->flags & VNET_SW_INTERFACE_FLAG_BOND_SLAVE)
418         {
419           error = clib_error_return
420             (0, "not allowed as %U belong to a BondEthernet interface",
421              format_vnet_sw_interface_name, vnm, si);
422           goto done;
423         }
424
425       /* Already in the desired state? */
426       if ((si->flags & mask) == flags)
427         goto done;
428
429       /* Sub-interfaces of hardware interfaces that do no redistribute,
430          do not redistribute themselves. */
431       if (si_sup->type == VNET_SW_INTERFACE_TYPE_HARDWARE)
432         {
433           vnet_hw_interface_t *hi =
434             vnet_get_hw_interface (vnm, si_sup->hw_if_index);
435           vnet_device_class_t *dev_class =
436             vnet_get_device_class (vnm, hi->dev_class_index);
437           if (!dev_class->redistribute)
438             helper_flags &=
439               ~VNET_INTERFACE_SET_FLAGS_HELPER_WANT_REDISTRIBUTE;
440         }
441
442       if (vm->mc_main
443           && (helper_flags &
444               VNET_INTERFACE_SET_FLAGS_HELPER_WANT_REDISTRIBUTE))
445         {
446           vnet_sw_hw_interface_state_t s;
447           s.sw_hw_if_index = sw_if_index;
448           s.flags = flags;
449           mc_serialize (vm->mc_main, &vnet_sw_interface_set_flags_msg, &s);
450         }
451
452       error = call_elf_section_interface_callbacks
453         (vnm, sw_if_index, flags, vnm->sw_interface_admin_up_down_functions);
454
455       if (error)
456         goto done;
457
458       if (si->type == VNET_SW_INTERFACE_TYPE_HARDWARE)
459         {
460           vnet_hw_interface_t *hi =
461             vnet_get_hw_interface (vnm, si->hw_if_index);
462           vnet_hw_interface_class_t *hw_class =
463             vnet_get_hw_interface_class (vnm, hi->hw_class_index);
464           vnet_device_class_t *dev_class =
465             vnet_get_device_class (vnm, hi->dev_class_index);
466
467           /* save the si admin up flag */
468           old_flags = si->flags;
469
470           /* update si admin up flag in advance if we are going admin down */
471           if (!(flags & VNET_SW_INTERFACE_FLAG_ADMIN_UP))
472             si->flags &= ~VNET_SW_INTERFACE_FLAG_ADMIN_UP;
473
474           if (dev_class->admin_up_down_function
475               && (error = dev_class->admin_up_down_function (vnm,
476                                                              si->hw_if_index,
477                                                              flags)))
478             {
479               /* restore si admin up flag to it's original state on errors */
480               si->flags = old_flags;
481               goto done;
482             }
483
484           if (hw_class->admin_up_down_function
485               && (error = hw_class->admin_up_down_function (vnm,
486                                                             si->hw_if_index,
487                                                             flags)))
488             {
489               /* restore si admin up flag to it's original state on errors */
490               si->flags = old_flags;
491               goto done;
492             }
493
494           /* Admin down implies link down. */
495           if (!(flags & VNET_SW_INTERFACE_FLAG_ADMIN_UP)
496               && (hi->flags & VNET_HW_INTERFACE_FLAG_LINK_UP))
497             vnet_hw_interface_set_flags_helper (vnm, si->hw_if_index,
498                                                 hi->flags &
499                                                 ~VNET_HW_INTERFACE_FLAG_LINK_UP,
500                                                 helper_flags);
501         }
502     }
503
504   si->flags &= ~mask;
505   si->flags |= flags;
506
507 done:
508   return error;
509 }
510
511 clib_error_t *
512 vnet_hw_interface_set_flags (vnet_main_t * vnm, u32 hw_if_index, u32 flags)
513 {
514   return vnet_hw_interface_set_flags_helper
515     (vnm, hw_if_index, flags,
516      VNET_INTERFACE_SET_FLAGS_HELPER_WANT_REDISTRIBUTE);
517 }
518
519 clib_error_t *
520 vnet_sw_interface_set_flags (vnet_main_t * vnm, u32 sw_if_index, u32 flags)
521 {
522   return vnet_sw_interface_set_flags_helper
523     (vnm, sw_if_index, flags,
524      VNET_INTERFACE_SET_FLAGS_HELPER_WANT_REDISTRIBUTE);
525 }
526
527 static u32
528 vnet_create_sw_interface_no_callbacks (vnet_main_t * vnm,
529                                        vnet_sw_interface_t * template)
530 {
531   vnet_interface_main_t *im = &vnm->interface_main;
532   vnet_sw_interface_t *sw;
533   u32 sw_if_index;
534
535   pool_get (im->sw_interfaces, sw);
536   sw_if_index = sw - im->sw_interfaces;
537
538   sw[0] = template[0];
539
540   sw->flags = 0;
541   sw->sw_if_index = sw_if_index;
542   if (sw->type == VNET_SW_INTERFACE_TYPE_HARDWARE)
543     sw->sup_sw_if_index = sw->sw_if_index;
544
545   /* Allocate counters for this interface. */
546   {
547     u32 i;
548
549     vnet_interface_counter_lock (im);
550
551     for (i = 0; i < vec_len (im->sw_if_counters); i++)
552       {
553         vlib_validate_simple_counter (&im->sw_if_counters[i], sw_if_index);
554         vlib_zero_simple_counter (&im->sw_if_counters[i], sw_if_index);
555       }
556
557     for (i = 0; i < vec_len (im->combined_sw_if_counters); i++)
558       {
559         vlib_validate_combined_counter (&im->combined_sw_if_counters[i],
560                                         sw_if_index);
561         vlib_zero_combined_counter (&im->combined_sw_if_counters[i],
562                                     sw_if_index);
563       }
564
565     vnet_interface_counter_unlock (im);
566   }
567
568   return sw_if_index;
569 }
570
571 clib_error_t *
572 vnet_create_sw_interface (vnet_main_t * vnm, vnet_sw_interface_t * template,
573                           u32 * sw_if_index)
574 {
575   clib_error_t *error;
576   vnet_hw_interface_t *hi;
577   vnet_device_class_t *dev_class;
578
579   hi = vnet_get_sup_hw_interface (vnm, template->sup_sw_if_index);
580   dev_class = vnet_get_device_class (vnm, hi->dev_class_index);
581
582   if (template->type == VNET_SW_INTERFACE_TYPE_SUB &&
583       dev_class->subif_add_del_function)
584     {
585       error = dev_class->subif_add_del_function (vnm, hi->hw_if_index,
586                                                  (struct vnet_sw_interface_t
587                                                   *) template, 1);
588       if (error)
589         return error;
590     }
591
592   *sw_if_index = vnet_create_sw_interface_no_callbacks (vnm, template);
593   error = vnet_sw_interface_set_flags_helper
594     (vnm, *sw_if_index, template->flags,
595      VNET_INTERFACE_SET_FLAGS_HELPER_IS_CREATE);
596
597   if (error)
598     {
599       /* undo the work done by vnet_create_sw_interface_no_callbacks() */
600       vnet_interface_main_t *im = &vnm->interface_main;
601       vnet_sw_interface_t *sw =
602         pool_elt_at_index (im->sw_interfaces, *sw_if_index);
603       pool_put (im->sw_interfaces, sw);
604     }
605
606   return error;
607 }
608
609 void
610 vnet_delete_sw_interface (vnet_main_t * vnm, u32 sw_if_index)
611 {
612   vnet_interface_main_t *im = &vnm->interface_main;
613   vnet_sw_interface_t *sw =
614     pool_elt_at_index (im->sw_interfaces, sw_if_index);
615
616   /* Bring down interface in case it is up. */
617   if (sw->flags != 0)
618     vnet_sw_interface_set_flags (vnm, sw_if_index, /* flags */ 0);
619
620   call_sw_interface_add_del_callbacks (vnm, sw_if_index, /* is_create */ 0);
621
622   pool_put (im->sw_interfaces, sw);
623 }
624
625 static void
626 setup_tx_node (vlib_main_t * vm,
627                u32 node_index, vnet_device_class_t * dev_class)
628 {
629   vlib_node_t *n = vlib_get_node (vm, node_index);
630
631   n->function = dev_class->tx_function;
632   n->format_trace = dev_class->format_tx_trace;
633   vlib_register_errors (vm, node_index,
634                         dev_class->tx_function_n_errors,
635                         dev_class->tx_function_error_strings);
636 }
637
638 static void
639 setup_output_node (vlib_main_t * vm,
640                    u32 node_index, vnet_hw_interface_class_t * hw_class)
641 {
642   vlib_node_t *n = vlib_get_node (vm, node_index);
643   n->format_buffer = hw_class->format_header;
644   n->unformat_buffer = hw_class->unformat_header;
645 }
646
647 /* Register an interface instance. */
648 u32
649 vnet_register_interface (vnet_main_t * vnm,
650                          u32 dev_class_index,
651                          u32 dev_instance,
652                          u32 hw_class_index, u32 hw_instance)
653 {
654   vnet_interface_main_t *im = &vnm->interface_main;
655   vnet_hw_interface_t *hw;
656   vnet_device_class_t *dev_class =
657     vnet_get_device_class (vnm, dev_class_index);
658   vnet_hw_interface_class_t *hw_class =
659     vnet_get_hw_interface_class (vnm, hw_class_index);
660   vlib_main_t *vm = vnm->vlib_main;
661   u32 hw_index;
662   char *tx_node_name, *output_node_name;
663
664   pool_get (im->hw_interfaces, hw);
665
666   hw_index = hw - im->hw_interfaces;
667   hw->hw_if_index = hw_index;
668
669   if (dev_class->format_device_name)
670     hw->name = format (0, "%U", dev_class->format_device_name, dev_instance);
671   else if (hw_class->format_interface_name)
672     hw->name = format (0, "%U", hw_class->format_interface_name,
673                        dev_instance);
674   else
675     hw->name = format (0, "%s%x", hw_class->name, dev_instance);
676
677   if (!im->hw_interface_by_name)
678     im->hw_interface_by_name = hash_create_vec ( /* size */ 0,
679                                                 sizeof (hw->name[0]),
680                                                 sizeof (uword));
681
682   hash_set_mem (im->hw_interface_by_name, hw->name, hw_index);
683
684   /* Make hardware interface point to software interface. */
685   {
686     vnet_sw_interface_t sw;
687
688     memset (&sw, 0, sizeof (sw));
689     sw.type = VNET_SW_INTERFACE_TYPE_HARDWARE;
690     sw.hw_if_index = hw_index;
691     hw->sw_if_index = vnet_create_sw_interface_no_callbacks (vnm, &sw);
692   }
693
694   hw->dev_class_index = dev_class_index;
695   hw->dev_instance = dev_instance;
696   hw->hw_class_index = hw_class_index;
697   hw->hw_instance = hw_instance;
698
699   hw->max_rate_bits_per_sec = 0;
700   hw->min_packet_bytes = 0;
701   hw->per_packet_overhead_bytes = 0;
702   hw->max_l3_packet_bytes[VLIB_RX] = ~0;
703   hw->max_l3_packet_bytes[VLIB_TX] = ~0;
704
705   tx_node_name = (char *) format (0, "%v-tx", hw->name);
706   output_node_name = (char *) format (0, "%v-output", hw->name);
707
708   /* If we have previously deleted interface nodes, re-use them. */
709   if (vec_len (im->deleted_hw_interface_nodes) > 0)
710     {
711       vnet_hw_interface_nodes_t *hn;
712       vnet_interface_output_runtime_t *rt;
713
714       hn = vec_end (im->deleted_hw_interface_nodes) - 1;
715
716       hw->tx_node_index = hn->tx_node_index;
717       hw->output_node_index = hn->output_node_index;
718
719       vlib_node_rename (vm, hw->tx_node_index, "%v", tx_node_name);
720       vlib_node_rename (vm, hw->output_node_index, "%v", output_node_name);
721
722       rt = vlib_node_get_runtime_data (vm, hw->output_node_index);
723       ASSERT (rt->is_deleted == 1);
724       rt->is_deleted = 0;
725       rt->hw_if_index = hw_index;
726       rt->sw_if_index = hw->sw_if_index;
727       rt->dev_instance = hw->dev_instance;
728
729       rt = vlib_node_get_runtime_data (vm, hw->tx_node_index);
730       rt->hw_if_index = hw_index;
731       rt->sw_if_index = hw->sw_if_index;
732       rt->dev_instance = hw->dev_instance;
733
734       vlib_worker_thread_node_runtime_update ();
735       _vec_len (im->deleted_hw_interface_nodes) -= 1;
736     }
737   else
738     {
739       vlib_node_registration_t r;
740       vnet_interface_output_runtime_t rt = {
741         .hw_if_index = hw_index,
742         .sw_if_index = hw->sw_if_index,
743         .dev_instance = hw->dev_instance,
744         .is_deleted = 0,
745       };
746
747       memset (&r, 0, sizeof (r));
748       r.type = VLIB_NODE_TYPE_INTERNAL;
749       r.runtime_data = &rt;
750       r.runtime_data_bytes = sizeof (rt);
751       r.scalar_size = 0;
752       r.vector_size = sizeof (u32);
753
754       r.flags = VLIB_NODE_FLAG_IS_OUTPUT;
755       r.name = tx_node_name;
756       r.function = dev_class->tx_function;
757
758       hw->tx_node_index = vlib_register_node (vm, &r);
759
760       vlib_node_add_named_next_with_slot (vm, hw->tx_node_index,
761                                           "error-drop",
762                                           VNET_INTERFACE_TX_NEXT_DROP);
763
764       r.flags = 0;
765       r.name = output_node_name;
766       r.function = dev_class->no_flatten_output_chains ?
767         vnet_interface_output_node_no_flatten_multiarch_select () :
768         vnet_interface_output_node_multiarch_select ();
769       r.format_trace = format_vnet_interface_output_trace;
770
771       {
772         static char *e[] = {
773           "interface is down",
774           "interface is deleted",
775         };
776
777         r.n_errors = ARRAY_LEN (e);
778         r.error_strings = e;
779       }
780       hw->output_node_index = vlib_register_node (vm, &r);
781
782 #define _(sym,str) vlib_node_add_named_next_with_slot (vm, \
783                      hw->output_node_index, str,           \
784                      VNET_INTERFACE_OUTPUT_NEXT_##sym);
785       foreach_intf_output_feat
786 #undef _
787         vlib_node_add_named_next_with_slot (vm, hw->output_node_index,
788                                             "error-drop",
789                                             VNET_INTERFACE_OUTPUT_NEXT_DROP);
790       vlib_node_add_next_with_slot (vm, hw->output_node_index,
791                                     hw->tx_node_index,
792                                     VNET_INTERFACE_OUTPUT_NEXT_TX);
793     }
794
795   setup_output_node (vm, hw->output_node_index, hw_class);
796   setup_tx_node (vm, hw->tx_node_index, dev_class);
797
798   /* Call all up/down callbacks with zero flags when interface is created. */
799   vnet_sw_interface_set_flags_helper (vnm, hw->sw_if_index, /* flags */ 0,
800                                       VNET_INTERFACE_SET_FLAGS_HELPER_IS_CREATE);
801   vnet_hw_interface_set_flags_helper (vnm, hw_index, /* flags */ 0,
802                                       VNET_INTERFACE_SET_FLAGS_HELPER_IS_CREATE);
803
804   return hw_index;
805 }
806
807 void
808 vnet_delete_hw_interface (vnet_main_t * vnm, u32 hw_if_index)
809 {
810   vnet_interface_main_t *im = &vnm->interface_main;
811   vnet_hw_interface_t *hw = vnet_get_hw_interface (vnm, hw_if_index);
812   vlib_main_t *vm = vnm->vlib_main;
813
814   /* If it is up, mark it down. */
815   if (hw->flags != 0)
816     vnet_hw_interface_set_flags (vnm, hw_if_index, /* flags */ 0);
817
818   /* Call delete callbacks. */
819   call_hw_interface_add_del_callbacks (vnm, hw_if_index, /* is_create */ 0);
820
821   /* Delete software interface corresponding to hardware interface. */
822   vnet_delete_sw_interface (vnm, hw->sw_if_index);
823
824   /* Delete any sub-interfaces. */
825   {
826     u32 id, sw_if_index;
827     /* *INDENT-OFF* */
828     hash_foreach (id, sw_if_index, hw->sub_interface_sw_if_index_by_id, ({
829       vnet_delete_sw_interface (vnm, sw_if_index);
830     }));
831     /* *INDENT-ON* */
832   }
833
834   {
835     vnet_hw_interface_nodes_t *dn;
836     vnet_interface_output_runtime_t *rt =
837       vlib_node_get_runtime_data (vm, hw->output_node_index);
838
839     /* Mark node runtime as deleted so output node (if called) will drop packets. */
840     rt->is_deleted = 1;
841
842     vlib_node_rename (vm, hw->output_node_index,
843                       "interface-%d-output-deleted", hw_if_index);
844     vlib_node_rename (vm, hw->tx_node_index, "interface-%d-tx-deleted",
845                       hw_if_index);
846     vec_add2 (im->deleted_hw_interface_nodes, dn, 1);
847     dn->tx_node_index = hw->tx_node_index;
848     dn->output_node_index = hw->output_node_index;
849   }
850
851   hash_unset_mem (im->hw_interface_by_name, hw->name);
852   vec_free (hw->name);
853
854   pool_put (im->hw_interfaces, hw);
855 }
856
857 static void
858 serialize_vnet_hw_interface_set_class (serialize_main_t * m, va_list * va)
859 {
860   u32 hw_if_index = va_arg (*va, u32);
861   char *hw_class_name = va_arg (*va, char *);
862   serialize_integer (m, hw_if_index, sizeof (hw_if_index));
863   serialize_cstring (m, hw_class_name);
864 }
865
866 static void
867 unserialize_vnet_hw_interface_set_class (serialize_main_t * m, va_list * va)
868 {
869   CLIB_UNUSED (mc_main_t * mc) = va_arg (*va, mc_main_t *);
870   vnet_main_t *vnm = vnet_get_main ();
871   u32 hw_if_index;
872   char *hw_class_name;
873   uword *p;
874   clib_error_t *error;
875
876   unserialize_integer (m, &hw_if_index, sizeof (hw_if_index));
877   unserialize_cstring (m, &hw_class_name);
878   p =
879     hash_get (vnm->interface_main.hw_interface_class_by_name, hw_class_name);
880   ASSERT (p != 0);
881   error = vnet_hw_interface_set_class_helper (vnm, hw_if_index, p[0],
882                                               /* redistribute */ 0);
883   if (error)
884     clib_error_report (error);
885 }
886
887 MC_SERIALIZE_MSG (vnet_hw_interface_set_class_msg, static) =
888 {
889 .name = "vnet_hw_interface_set_class",.serialize =
890     serialize_vnet_hw_interface_set_class,.unserialize =
891     unserialize_vnet_hw_interface_set_class,};
892
893 void
894 vnet_hw_interface_init_for_class (vnet_main_t * vnm, u32 hw_if_index,
895                                   u32 hw_class_index, u32 hw_instance)
896 {
897   vnet_hw_interface_t *hi = vnet_get_hw_interface (vnm, hw_if_index);
898   vnet_hw_interface_class_t *hc =
899     vnet_get_hw_interface_class (vnm, hw_class_index);
900
901   hi->hw_class_index = hw_class_index;
902   hi->hw_instance = hw_instance;
903   setup_output_node (vnm->vlib_main, hi->output_node_index, hc);
904 }
905
906 static clib_error_t *
907 vnet_hw_interface_set_class_helper (vnet_main_t * vnm, u32 hw_if_index,
908                                     u32 hw_class_index, u32 redistribute)
909 {
910   vnet_hw_interface_t *hi = vnet_get_hw_interface (vnm, hw_if_index);
911   vnet_sw_interface_t *si = vnet_get_sw_interface (vnm, hi->sw_if_index);
912   vnet_hw_interface_class_t *old_class =
913     vnet_get_hw_interface_class (vnm, hi->hw_class_index);
914   vnet_hw_interface_class_t *new_class =
915     vnet_get_hw_interface_class (vnm, hw_class_index);
916   vnet_device_class_t *dev_class =
917     vnet_get_device_class (vnm, hi->dev_class_index);
918   clib_error_t *error = 0;
919
920   /* New class equals old class?  Nothing to do. */
921   if (hi->hw_class_index == hw_class_index)
922     return 0;
923
924   /* No need (and incorrect since admin up flag may be set) to do error checking when
925      receiving unserialize message. */
926   if (redistribute)
927     {
928       if (si->flags & VNET_SW_INTERFACE_FLAG_ADMIN_UP)
929         return clib_error_return (0,
930                                   "%v must be admin down to change class from %s to %s",
931                                   hi->name, old_class->name, new_class->name);
932
933       /* Make sure interface supports given class. */
934       if ((new_class->is_valid_class_for_interface
935            && !new_class->is_valid_class_for_interface (vnm, hw_if_index,
936                                                         hw_class_index))
937           || (dev_class->is_valid_class_for_interface
938               && !dev_class->is_valid_class_for_interface (vnm, hw_if_index,
939                                                            hw_class_index)))
940         return clib_error_return (0,
941                                   "%v class cannot be changed from %s to %s",
942                                   hi->name, old_class->name, new_class->name);
943
944       if (vnm->vlib_main->mc_main)
945         {
946           mc_serialize (vnm->vlib_main->mc_main,
947                         &vnet_hw_interface_set_class_msg, hw_if_index,
948                         new_class->name);
949           return 0;
950         }
951     }
952
953   if (old_class->hw_class_change)
954     old_class->hw_class_change (vnm, hw_if_index, old_class->index,
955                                 new_class->index);
956
957   vnet_hw_interface_init_for_class (vnm, hw_if_index, new_class->index,
958                                     /* instance */ ~0);
959
960   if (new_class->hw_class_change)
961     new_class->hw_class_change (vnm, hw_if_index, old_class->index,
962                                 new_class->index);
963
964   if (dev_class->hw_class_change)
965     dev_class->hw_class_change (vnm, hw_if_index, new_class->index);
966
967   return error;
968 }
969
970 clib_error_t *
971 vnet_hw_interface_set_class (vnet_main_t * vnm, u32 hw_if_index,
972                              u32 hw_class_index)
973 {
974   return vnet_hw_interface_set_class_helper (vnm, hw_if_index, hw_class_index,
975                                              /* redistribute */ 1);
976 }
977
978 static int
979 vnet_hw_interface_rx_redirect_to_node_helper (vnet_main_t * vnm,
980                                               u32 hw_if_index,
981                                               u32 node_index,
982                                               u32 redistribute)
983 {
984   vnet_hw_interface_t *hi = vnet_get_hw_interface (vnm, hw_if_index);
985   vnet_device_class_t *dev_class = vnet_get_device_class
986     (vnm, hi->dev_class_index);
987
988   if (redistribute)
989     {
990       /* $$$$ fixme someday maybe */
991       ASSERT (vnm->vlib_main->mc_main == 0);
992     }
993   if (dev_class->rx_redirect_to_node)
994     {
995       dev_class->rx_redirect_to_node (vnm, hw_if_index, node_index);
996       return 0;
997     }
998
999   return VNET_API_ERROR_UNIMPLEMENTED;
1000 }
1001
1002 int
1003 vnet_hw_interface_rx_redirect_to_node (vnet_main_t * vnm, u32 hw_if_index,
1004                                        u32 node_index)
1005 {
1006   return vnet_hw_interface_rx_redirect_to_node_helper (vnm, hw_if_index,
1007                                                        node_index,
1008                                                        1 /* redistribute */ );
1009 }
1010
1011 word
1012 vnet_sw_interface_compare (vnet_main_t * vnm,
1013                            uword sw_if_index0, uword sw_if_index1)
1014 {
1015   vnet_sw_interface_t *sup0 = vnet_get_sup_sw_interface (vnm, sw_if_index0);
1016   vnet_sw_interface_t *sup1 = vnet_get_sup_sw_interface (vnm, sw_if_index1);
1017   vnet_hw_interface_t *h0 = vnet_get_hw_interface (vnm, sup0->hw_if_index);
1018   vnet_hw_interface_t *h1 = vnet_get_hw_interface (vnm, sup1->hw_if_index);
1019
1020   if (h0 != h1)
1021     return vec_cmp (h0->name, h1->name);
1022   return (word) h0->hw_instance - (word) h1->hw_instance;
1023 }
1024
1025 word
1026 vnet_hw_interface_compare (vnet_main_t * vnm,
1027                            uword hw_if_index0, uword hw_if_index1)
1028 {
1029   vnet_hw_interface_t *h0 = vnet_get_hw_interface (vnm, hw_if_index0);
1030   vnet_hw_interface_t *h1 = vnet_get_hw_interface (vnm, hw_if_index1);
1031
1032   if (h0 != h1)
1033     return vec_cmp (h0->name, h1->name);
1034   return (word) h0->hw_instance - (word) h1->hw_instance;
1035 }
1036
1037 clib_error_t *
1038 vnet_interface_init (vlib_main_t * vm)
1039 {
1040   vnet_main_t *vnm = vnet_get_main ();
1041   vnet_interface_main_t *im = &vnm->interface_main;
1042   vlib_buffer_t *b = 0;
1043   vnet_buffer_opaque_t *o = 0;
1044
1045   /*
1046    * Keep people from shooting themselves in the foot.
1047    */
1048   if (sizeof (b->opaque) != sizeof (vnet_buffer_opaque_t))
1049     {
1050 #define _(a) if (sizeof(o->a) > sizeof (o->unused))                     \
1051       clib_warning                                                      \
1052         ("FATAL: size of opaque union subtype %s is %d (max %d)",       \
1053          #a, sizeof(o->a), sizeof (o->unused));
1054       foreach_buffer_opaque_union_subtype;
1055 #undef _
1056
1057       return clib_error_return
1058         (0, "FATAL: size of vlib buffer opaque %d, size of vnet opaque %d",
1059          sizeof (b->opaque), sizeof (vnet_buffer_opaque_t));
1060     }
1061
1062   im->sw_if_counter_lock = clib_mem_alloc_aligned (CLIB_CACHE_LINE_BYTES,
1063                                                    CLIB_CACHE_LINE_BYTES);
1064   im->sw_if_counter_lock[0] = 1;        /* should be no need */
1065
1066   vec_validate (im->sw_if_counters, VNET_N_SIMPLE_INTERFACE_COUNTER - 1);
1067   im->sw_if_counters[VNET_INTERFACE_COUNTER_DROP].name = "drops";
1068   im->sw_if_counters[VNET_INTERFACE_COUNTER_PUNT].name = "punts";
1069   im->sw_if_counters[VNET_INTERFACE_COUNTER_IP4].name = "ip4";
1070   im->sw_if_counters[VNET_INTERFACE_COUNTER_IP6].name = "ip6";
1071   im->sw_if_counters[VNET_INTERFACE_COUNTER_RX_NO_BUF].name = "rx-no-buf";
1072   im->sw_if_counters[VNET_INTERFACE_COUNTER_RX_MISS].name = "rx-miss";
1073   im->sw_if_counters[VNET_INTERFACE_COUNTER_RX_ERROR].name = "rx-error";
1074   im->sw_if_counters[VNET_INTERFACE_COUNTER_TX_ERROR].name = "tx-error";
1075
1076   vec_validate (im->combined_sw_if_counters,
1077                 VNET_N_COMBINED_INTERFACE_COUNTER - 1);
1078   im->combined_sw_if_counters[VNET_INTERFACE_COUNTER_RX].name = "rx";
1079   im->combined_sw_if_counters[VNET_INTERFACE_COUNTER_TX].name = "tx";
1080
1081   im->sw_if_counter_lock[0] = 0;
1082
1083   im->device_class_by_name = hash_create_string ( /* size */ 0,
1084                                                  sizeof (uword));
1085   {
1086     vnet_device_class_t *c;
1087
1088     c = vnm->device_class_registrations;
1089
1090     while (c)
1091       {
1092         c->index = vec_len (im->device_classes);
1093         hash_set_mem (im->device_class_by_name, c->name, c->index);
1094         vec_add1 (im->device_classes, c[0]);
1095         c = c->next_class_registration;
1096       }
1097   }
1098
1099   im->hw_interface_class_by_name = hash_create_string ( /* size */ 0,
1100                                                        sizeof (uword));
1101
1102   im->sw_if_index_by_sup_and_sub = hash_create_mem (0, sizeof (u64),
1103                                                     sizeof (uword));
1104   {
1105     vnet_hw_interface_class_t *c;
1106
1107     c = vnm->hw_interface_class_registrations;
1108
1109     while (c)
1110       {
1111         c->index = vec_len (im->hw_interface_classes);
1112         hash_set_mem (im->hw_interface_class_by_name, c->name, c->index);
1113         vec_add1 (im->hw_interface_classes, c[0]);
1114         c = c->next_class_registration;
1115       }
1116   }
1117
1118   {
1119     clib_error_t *error;
1120
1121     if ((error = vlib_call_init_function (vm, vnet_interface_cli_init)))
1122       return error;
1123
1124     return error;
1125   }
1126 }
1127
1128 VLIB_INIT_FUNCTION (vnet_interface_init);
1129
1130 /* Kludge to renumber interface names [only!] */
1131 int
1132 vnet_interface_name_renumber (u32 sw_if_index, u32 new_show_dev_instance)
1133 {
1134   int rv;
1135   vnet_main_t *vnm = vnet_get_main ();
1136   vnet_interface_main_t *im = &vnm->interface_main;
1137   vnet_hw_interface_t *hi = vnet_get_sup_hw_interface (vnm, sw_if_index);
1138
1139   vnet_device_class_t *dev_class = vnet_get_device_class
1140     (vnm, hi->dev_class_index);
1141
1142   if (dev_class->name_renumber == 0 || dev_class->format_device_name == 0)
1143     return VNET_API_ERROR_UNIMPLEMENTED;
1144
1145   rv = dev_class->name_renumber (hi, new_show_dev_instance);
1146
1147   if (rv)
1148     return rv;
1149
1150   hash_unset_mem (im->hw_interface_by_name, hi->name);
1151   vec_free (hi->name);
1152   /* Use the mapping we set up to call it Ishmael */
1153   hi->name = format (0, "%U", dev_class->format_device_name,
1154                      hi->dev_instance);
1155
1156   hash_set_mem (im->hw_interface_by_name, hi->name, hi->hw_if_index);
1157   return rv;
1158 }
1159
1160 int
1161 vnet_interface_add_del_feature (vnet_main_t * vnm,
1162                                 vlib_main_t * vm,
1163                                 u32 sw_if_index,
1164                                 intf_output_feat_t feature, int is_add)
1165 {
1166   vnet_sw_interface_t *sw;
1167
1168   sw = vnet_get_sw_interface (vnm, sw_if_index);
1169
1170   if (is_add)
1171     {
1172
1173       sw->output_feature_bitmap |= (1 << feature);
1174       sw->output_feature_bitmap |= (1 << INTF_OUTPUT_FEAT_DONE);
1175
1176     }
1177   else
1178     {                           /* delete */
1179
1180       sw->output_feature_bitmap &= ~(1 << feature);
1181       if (sw->output_feature_bitmap == (1 << INTF_OUTPUT_FEAT_DONE))
1182         sw->output_feature_bitmap = 0;
1183
1184     }
1185   return 0;
1186 }
1187
1188 clib_error_t *
1189 vnet_rename_interface (vnet_main_t * vnm, u32 hw_if_index, char *new_name)
1190 {
1191   vnet_interface_main_t *im = &vnm->interface_main;
1192   vlib_main_t *vm = vnm->vlib_main;
1193   vnet_hw_interface_t *hw;
1194   u8 *old_name;
1195   clib_error_t *error = 0;
1196
1197   hw = vnet_get_hw_interface (vnm, hw_if_index);
1198   if (!hw)
1199     {
1200       return clib_error_return (0,
1201                                 "unable to find hw interface for index %u",
1202                                 hw_if_index);
1203     }
1204
1205   old_name = hw->name;
1206
1207   /* set new hw->name */
1208   hw->name = format (0, "%s", new_name);
1209
1210   /* remove the old name to hw_if_index mapping and install the new one */
1211   hash_unset_mem (im->hw_interface_by_name, old_name);
1212   hash_set_mem (im->hw_interface_by_name, hw->name, hw_if_index);
1213
1214   /* rename tx/output nodes */
1215   vlib_node_rename (vm, hw->tx_node_index, "%v-tx", hw->name);
1216   vlib_node_rename (vm, hw->output_node_index, "%v-output", hw->name);
1217
1218   /* free the old name vector */
1219   vec_free (old_name);
1220
1221   return error;
1222 }
1223
1224 /*
1225  * fd.io coding-style-patch-verification: ON
1226  *
1227  * Local Variables:
1228  * eval: (c-set-style "gnu")
1229  * End:
1230  */