84c8708851c9f66f92304abdac365ddc4def2bae
[vpp.git] / vnet / vnet / fib / fib_table.c
1 /*
2  * Copyright (c) 2016 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 #include <vlib/vlib.h>
17 #include <vnet/dpo/drop_dpo.h>
18
19 #include <vnet/fib/fib_table.h>
20 #include <vnet/fib/fib_entry_cover.h>
21 #include <vnet/fib/fib_internal.h>
22 #include <vnet/fib/ip4_fib.h>
23 #include <vnet/fib/ip6_fib.h>
24 #include <vnet/fib/mpls_fib.h>
25
26 fib_table_t *
27 fib_table_get (fib_node_index_t index,
28                fib_protocol_t proto)
29 {
30     switch (proto)
31     {
32     case FIB_PROTOCOL_IP4:
33         return (pool_elt_at_index(ip4_main.fibs, index));
34     case FIB_PROTOCOL_IP6:
35         return (pool_elt_at_index(ip6_main.fibs, index));
36     case FIB_PROTOCOL_MPLS:
37         return (pool_elt_at_index(mpls_main.fibs, index));
38     }
39     ASSERT(0);
40     return (NULL);
41 }
42
43 static inline fib_node_index_t
44 fib_table_lookup_i (fib_table_t *fib_table,
45                     const fib_prefix_t *prefix)
46 {
47     switch (prefix->fp_proto)
48     {
49     case FIB_PROTOCOL_IP4:
50         return (ip4_fib_table_lookup(&fib_table->v4,
51                                      &prefix->fp_addr.ip4,
52                                      prefix->fp_len));
53     case FIB_PROTOCOL_IP6:
54         return (ip6_fib_table_lookup(fib_table->ft_index,
55                                      &prefix->fp_addr.ip6,
56                                      prefix->fp_len));
57     case FIB_PROTOCOL_MPLS:
58         return (mpls_fib_table_lookup(&fib_table->mpls,
59                                       prefix->fp_label,
60                                       prefix->fp_eos));
61     }
62     return (FIB_NODE_INDEX_INVALID);
63 }
64
65 fib_node_index_t
66 fib_table_lookup (u32 fib_index,
67                   const fib_prefix_t *prefix)
68 {
69     return (fib_table_lookup_i(fib_table_get(fib_index, prefix->fp_proto), prefix));
70 }
71
72 static inline fib_node_index_t
73 fib_table_lookup_exact_match_i (const fib_table_t *fib_table,
74                                 const fib_prefix_t *prefix)
75 {
76     switch (prefix->fp_proto)
77     {
78     case FIB_PROTOCOL_IP4:
79         return (ip4_fib_table_lookup_exact_match(&fib_table->v4,
80                                                  &prefix->fp_addr.ip4,
81                                                  prefix->fp_len));
82     case FIB_PROTOCOL_IP6:
83         return (ip6_fib_table_lookup_exact_match(fib_table->ft_index,
84                                                  &prefix->fp_addr.ip6,
85                                                  prefix->fp_len));
86     case FIB_PROTOCOL_MPLS:
87         return (mpls_fib_table_lookup(&fib_table->mpls,
88                                       prefix->fp_label,
89                                       prefix->fp_eos));
90     }
91     return (FIB_NODE_INDEX_INVALID);
92 }
93
94 fib_node_index_t
95 fib_table_lookup_exact_match (u32 fib_index,
96                               const fib_prefix_t *prefix)
97 {
98     return (fib_table_lookup_exact_match_i(fib_table_get(fib_index,
99                                                          prefix->fp_proto),
100                                            prefix));
101 }
102
103 static fib_node_index_t
104 fib_table_get_less_specific_i (fib_table_t *fib_table,
105                                const fib_prefix_t *prefix)
106 {
107     fib_prefix_t pfx;
108
109     pfx = *prefix;
110
111     if (FIB_PROTOCOL_MPLS == pfx.fp_proto)
112     {
113         return (FIB_NODE_INDEX_INVALID);
114     }
115
116     /*
117      * in the absence of a tree structure for the table that allows for an O(1)
118      * parent get, a cheeky way to find the cover is to LPM for the prefix with
119      * mask-1.
120      * there should always be a cover, though it may be the default route. the
121      * default route's cover is the default route.
122      */
123     if (pfx.fp_len != 0) {
124         pfx.fp_len -= 1;
125     }
126
127     return (fib_table_lookup_i(fib_table, &pfx));    
128 }
129
130 fib_node_index_t
131 fib_table_get_less_specific (u32 fib_index,
132                              const fib_prefix_t *prefix)
133 {
134     return (fib_table_get_less_specific_i(fib_table_get(fib_index,
135                                                         prefix->fp_proto),
136                                           prefix));
137 }
138
139 static void
140 fib_table_entry_remove (fib_table_t *fib_table,
141                         const fib_prefix_t *prefix,
142                         fib_node_index_t fib_entry_index)
143 {
144     vlib_smp_unsafe_warning();
145
146     fib_table->ft_total_route_counts--;
147
148     switch (prefix->fp_proto)
149     {
150     case FIB_PROTOCOL_IP4:
151         ip4_fib_table_entry_remove(&fib_table->v4,
152                                    &prefix->fp_addr.ip4,
153                                    prefix->fp_len);
154         break;
155     case FIB_PROTOCOL_IP6:
156         ip6_fib_table_entry_remove(fib_table->ft_index,
157                                    &prefix->fp_addr.ip6,
158                                    prefix->fp_len);
159         break;
160     case FIB_PROTOCOL_MPLS:
161         mpls_fib_table_entry_remove(&fib_table->mpls,
162                                     prefix->fp_label,
163                                     prefix->fp_eos);
164         break;
165     }
166
167     fib_entry_unlock(fib_entry_index);
168 }
169
170 static void
171 fib_table_post_insert_actions (fib_table_t *fib_table,
172                                const fib_prefix_t *prefix,
173                                fib_node_index_t fib_entry_index)
174 {
175     fib_node_index_t fib_entry_cover_index;
176
177     /*
178      * no cover relationships in the MPLS FIB
179      */
180     if (FIB_PROTOCOL_MPLS == prefix->fp_proto)
181         return;
182
183     /*
184      * find and inform the covering entry that a new more specific
185      * has been inserted beneath it
186      */
187     fib_entry_cover_index = fib_table_get_less_specific_i(fib_table, prefix);
188     /*
189      * the indicies are the same when the default route is first added
190      */
191     if (fib_entry_cover_index != fib_entry_index)
192     {
193         fib_entry_cover_change_notify(fib_entry_cover_index,
194                                       fib_entry_index);
195     }
196 }
197
198 static void
199 fib_table_entry_insert (fib_table_t *fib_table,
200                         const fib_prefix_t *prefix,
201                         fib_node_index_t fib_entry_index)
202 {
203     vlib_smp_unsafe_warning();
204
205     fib_entry_lock(fib_entry_index);
206     fib_table->ft_total_route_counts++;
207
208     switch (prefix->fp_proto)
209     {
210     case FIB_PROTOCOL_IP4:
211         ip4_fib_table_entry_insert(&fib_table->v4,
212                                    &prefix->fp_addr.ip4,
213                                    prefix->fp_len,
214                                    fib_entry_index);
215         break;
216     case FIB_PROTOCOL_IP6:
217         ip6_fib_table_entry_insert(fib_table->ft_index,
218                                    &prefix->fp_addr.ip6,
219                                    prefix->fp_len,
220                                    fib_entry_index);
221         break;
222     case FIB_PROTOCOL_MPLS:
223         mpls_fib_table_entry_insert(&fib_table->mpls,
224                                     prefix->fp_label,
225                                     prefix->fp_eos,
226                                     fib_entry_index);
227         break;
228     }
229
230     fib_table_post_insert_actions(fib_table, prefix, fib_entry_index);
231 }
232
233 void
234 fib_table_fwding_dpo_update (u32 fib_index,
235                              const fib_prefix_t *prefix,
236                              const dpo_id_t *dpo)
237 {
238     vlib_smp_unsafe_warning();
239
240     switch (prefix->fp_proto)
241     {
242     case FIB_PROTOCOL_IP4:
243         return (ip4_fib_table_fwding_dpo_update(ip4_fib_get(fib_index),
244                                                 &prefix->fp_addr.ip4,
245                                                 prefix->fp_len,
246                                                 dpo));
247     case FIB_PROTOCOL_IP6:
248         return (ip6_fib_table_fwding_dpo_update(fib_index,
249                                                 &prefix->fp_addr.ip6,
250                                                 prefix->fp_len,
251                                                 dpo));
252     case FIB_PROTOCOL_MPLS:
253         return (mpls_fib_forwarding_table_update(mpls_fib_get(fib_index),
254                                                  prefix->fp_label,
255                                                  prefix->fp_eos,
256                                                  dpo));
257     }
258 }
259
260 void
261 fib_table_fwding_dpo_remove (u32 fib_index,
262                              const fib_prefix_t *prefix,
263                              const dpo_id_t *dpo)
264 {
265     vlib_smp_unsafe_warning();
266
267     switch (prefix->fp_proto)
268     {
269     case FIB_PROTOCOL_IP4:
270         return (ip4_fib_table_fwding_dpo_remove(ip4_fib_get(fib_index),
271                                                 &prefix->fp_addr.ip4,
272                                                 prefix->fp_len,
273                                                 dpo));
274     case FIB_PROTOCOL_IP6:
275         return (ip6_fib_table_fwding_dpo_remove(fib_index,
276                                                 &prefix->fp_addr.ip6,
277                                                 prefix->fp_len,
278                                                 dpo));
279     case FIB_PROTOCOL_MPLS:
280         return (mpls_fib_forwarding_table_reset(mpls_fib_get(fib_index),
281                                                 prefix->fp_label,
282                                                 prefix->fp_eos));
283     }
284 }
285
286
287 fib_node_index_t
288 fib_table_entry_special_dpo_add (u32 fib_index,
289                                  const fib_prefix_t *prefix,
290                                  fib_source_t source,
291                                  fib_entry_flag_t flags,
292                                  const dpo_id_t *dpo)
293 {
294     fib_node_index_t fib_entry_index;
295     fib_table_t *fib_table;
296
297     fib_table = fib_table_get(fib_index, prefix->fp_proto);
298     fib_entry_index = fib_table_lookup_exact_match_i(fib_table, prefix);
299
300     if (FIB_NODE_INDEX_INVALID == fib_entry_index)
301     {
302         fib_entry_index = fib_entry_create_special(fib_index, prefix,
303                                                    source, flags,
304                                                    dpo);
305
306         fib_table_entry_insert(fib_table, prefix, fib_entry_index);
307         fib_table->ft_src_route_counts[source]++;
308     }
309     else
310     {
311         int was_sourced;
312
313         was_sourced = fib_entry_is_sourced(fib_entry_index, source);
314         fib_entry_special_add(fib_entry_index, source, flags, dpo);
315
316         if (was_sourced != fib_entry_is_sourced(fib_entry_index, source))
317         {
318             fib_table->ft_src_route_counts[source]++;
319         }
320     }
321
322
323     return (fib_entry_index);
324 }
325
326 fib_node_index_t
327 fib_table_entry_special_add (u32 fib_index,
328                              const fib_prefix_t *prefix,
329                              fib_source_t source,
330                              fib_entry_flag_t flags,
331                              adj_index_t adj_index)
332 {
333     fib_node_index_t fib_entry_index;
334     dpo_id_t tmp_dpo = DPO_NULL;
335
336     if (ADJ_INDEX_INVALID != adj_index)
337     {
338         dpo_set(&tmp_dpo,
339                 DPO_ADJACENCY,
340                 FIB_PROTOCOL_MAX,
341                 adj_index);
342     }
343     else
344     {
345         dpo_copy(&tmp_dpo, drop_dpo_get(fib_proto_to_dpo(prefix->fp_proto)));
346     }
347  
348     fib_entry_index = fib_table_entry_special_dpo_add(fib_index, prefix, source,
349                                                       flags, &tmp_dpo);
350
351     dpo_unlock(&tmp_dpo);
352
353     return (fib_entry_index);
354 }
355
356 void
357 fib_table_entry_special_dpo_update (fib_node_index_t fib_entry_index,
358                                     fib_source_t source,
359                                     fib_entry_flag_t flags,
360                                     const dpo_id_t *dpo)
361 {
362     fib_prefix_t prefix;
363     u32 fib_index;
364
365     fib_entry_get_prefix(fib_entry_index, &prefix);
366     fib_index = fib_entry_get_fib_index(fib_entry_index);
367
368     fib_table_entry_special_dpo_add(fib_index, &prefix, source, flags, dpo);
369     fib_table_entry_special_remove(fib_index, &prefix, source);
370 }
371
372 void
373 fib_table_entry_special_remove (u32 fib_index,
374                                 const fib_prefix_t *prefix,
375                                 fib_source_t source)
376 {
377     /*
378      * 1 is it present
379      *   yes => remove source
380      *    2 - is it still sourced?
381      *      no => cover walk
382      */
383     fib_node_index_t fib_entry_index;
384     fib_table_t *fib_table;
385
386     fib_table = fib_table_get(fib_index, prefix->fp_proto);
387     fib_entry_index = fib_table_lookup_exact_match_i(fib_table, prefix);
388
389     if (FIB_NODE_INDEX_INVALID == fib_entry_index)
390     {
391         /*
392          * removing an etry that does not exist. i'll allow it.
393          */
394     }
395     else
396     {
397         fib_entry_src_flag_t src_flag;
398         int was_sourced;
399
400         /*
401          * don't nobody go nowhere
402          */
403         fib_entry_lock(fib_entry_index);
404         was_sourced = fib_entry_is_sourced(fib_entry_index, source);
405
406         src_flag = fib_entry_special_remove(fib_entry_index, source);
407
408         if (!(FIB_ENTRY_SRC_FLAG_ADDED & src_flag))
409         {
410             /*
411              * last source gone. remove from the table
412              */
413             fib_table_entry_remove(fib_table, prefix, fib_entry_index);
414
415             /*
416              * now the entry is no longer in the table, we can
417              * inform the entries that it covers to re-calculate their cover
418              */
419             fib_entry_cover_change_notify(fib_entry_index,
420                                           FIB_NODE_INDEX_INVALID);
421         }
422         /*
423          * else
424          *   still has sources, leave it be.
425          */
426         if (was_sourced != fib_entry_is_sourced(fib_entry_index, source))
427         {
428             fib_table->ft_src_route_counts[source]--;
429         }
430
431         fib_entry_unlock(fib_entry_index);
432     }
433 }
434
435 /**
436  * fib_table_route_path_fixup
437  *
438  * Convert attached hosts to attached next-hops.
439  * 
440  * This special case is required because an attached path will link to a
441  * glean, and the FIB entry will have the interface or API/CLI source. When
442  * the ARP/ND process is completes then that source (which will provide a
443  * complete adjacency) will be lower priority and so the FIB entry will
444  * remain linked to a glean and traffic will never reach the hosts. For
445  * an ATTAHCED_HOST path we can link the path directly to the [incomplete]
446  * adjacency.
447  */
448 static void
449 fib_table_route_path_fixup (const fib_prefix_t *prefix,
450                             fib_route_path_t *path)
451 {
452     if (fib_prefix_is_host(prefix) &&
453         ip46_address_is_zero(&path->frp_addr) &&
454         path->frp_sw_if_index != ~0)
455     {
456         path->frp_addr = prefix->fp_addr;
457     }
458 }                 
459
460 fib_node_index_t
461 fib_table_entry_path_add (u32 fib_index,
462                           const fib_prefix_t *prefix,
463                           fib_source_t source,
464                           fib_entry_flag_t flags,
465                           fib_protocol_t next_hop_proto,
466                           const ip46_address_t *next_hop,
467                           u32 next_hop_sw_if_index,
468                           u32 next_hop_fib_index,
469                           u32 next_hop_weight,
470                           mpls_label_t next_hop_label,
471                           fib_route_path_flags_t path_flags)
472 {
473     fib_route_path_t path = {
474         .frp_proto = next_hop_proto,
475         .frp_addr = (NULL == next_hop? zero_addr : *next_hop),
476         .frp_sw_if_index = next_hop_sw_if_index,
477         .frp_fib_index = next_hop_fib_index,
478         .frp_weight = next_hop_weight,
479         .frp_flags = path_flags,
480         .frp_label = next_hop_label,
481     };
482     fib_node_index_t fib_entry_index;
483     fib_route_path_t *paths = NULL;
484
485     fib_table_route_path_fixup(prefix, &path);
486     vec_add1(paths, path);
487
488     fib_entry_index = fib_table_entry_path_add2(fib_index, prefix,
489                                                 source, flags, paths);
490
491     vec_free(paths);
492     return (fib_entry_index);
493 }
494
495 fib_node_index_t
496 fib_table_entry_path_add2 (u32 fib_index,
497                            const fib_prefix_t *prefix,
498                            fib_source_t source,
499                            fib_entry_flag_t flags,
500                            const fib_route_path_t *rpath)
501 {
502     fib_node_index_t fib_entry_index;
503     fib_table_t *fib_table;
504
505     fib_table = fib_table_get(fib_index, prefix->fp_proto);
506     fib_entry_index = fib_table_lookup_exact_match_i(fib_table, prefix);
507
508     if (FIB_NODE_INDEX_INVALID == fib_entry_index)
509     {
510         fib_entry_index = fib_entry_create(fib_index, prefix,
511                                            source, flags,
512                                            rpath);
513
514         fib_table_entry_insert(fib_table, prefix, fib_entry_index);
515         fib_table->ft_src_route_counts[source]++;
516     }
517     else
518     {
519         int was_sourced;
520
521         was_sourced = fib_entry_is_sourced(fib_entry_index, source);
522         fib_entry_path_add(fib_entry_index, source, flags, rpath);;
523
524         if (was_sourced != fib_entry_is_sourced(fib_entry_index, source))
525         {
526             fib_table->ft_src_route_counts[source]++;
527         }
528     }
529
530     return (fib_entry_index);
531 }
532
533 void
534 fib_table_entry_path_remove2 (u32 fib_index,
535                              const fib_prefix_t *prefix,
536                              fib_source_t source,
537                               const fib_route_path_t *rpath)
538 {
539     /*
540      * 1 is it present
541      *   yes => remove source
542      *    2 - is it still sourced?
543      *      no => cover walk
544      */
545     fib_node_index_t fib_entry_index;
546     fib_table_t *fib_table;
547
548     fib_table = fib_table_get(fib_index, prefix->fp_proto);
549     fib_entry_index = fib_table_lookup_exact_match_i(fib_table, prefix);
550
551     if (FIB_NODE_INDEX_INVALID == fib_entry_index)
552     {
553         /*
554          * removing an etry that does not exist. i'll allow it.
555          */
556     }
557     else
558     {
559         fib_entry_src_flag_t src_flag;
560         int was_sourced;
561
562         /*
563          * don't nobody go nowhere
564          */
565         fib_entry_lock(fib_entry_index);
566         was_sourced = fib_entry_is_sourced(fib_entry_index, source);
567
568         src_flag = fib_entry_path_remove(fib_entry_index, source, rpath);
569
570         if (!(FIB_ENTRY_SRC_FLAG_ADDED & src_flag))
571         {
572             /*
573              * last source gone. remove from the table
574              */
575             fib_table_entry_remove(fib_table, prefix, fib_entry_index);
576
577             /*
578              * now the entry is no longer in the table, we can
579              * inform the entries that it covers to re-calculate their cover
580              */
581             fib_entry_cover_change_notify(fib_entry_index,
582                                           FIB_NODE_INDEX_INVALID);
583         }
584         /*
585          * else
586          *   still has sources, leave it be.
587          */
588         if (was_sourced != fib_entry_is_sourced(fib_entry_index, source))
589         {
590             fib_table->ft_src_route_counts[source]--;
591         }
592
593         fib_entry_unlock(fib_entry_index);
594     }
595 }
596
597 void
598 fib_table_entry_path_remove (u32 fib_index,
599                              const fib_prefix_t *prefix,
600                              fib_source_t source,
601                              fib_protocol_t next_hop_proto,
602                              const ip46_address_t *next_hop,
603                              u32 next_hop_sw_if_index,
604                              u32 next_hop_fib_index,
605                              u32 next_hop_weight,
606                              fib_route_path_flags_t path_flags)
607 {
608     /*
609      * 1 is it present
610      *   yes => remove source
611      *    2 - is it still sourced?
612      *      no => cover walk
613      */
614     fib_route_path_t path = {
615         .frp_proto = next_hop_proto,
616         .frp_addr = (NULL == next_hop? zero_addr : *next_hop),
617         .frp_sw_if_index = next_hop_sw_if_index,
618         .frp_fib_index = next_hop_fib_index,
619         .frp_weight = next_hop_weight,
620         .frp_flags = path_flags,
621     };
622     fib_route_path_t *paths = NULL;
623
624     fib_table_route_path_fixup(prefix, &path);
625     vec_add1(paths, path);
626
627     fib_table_entry_path_remove2(fib_index, prefix, source, paths);
628
629     vec_free(paths);
630 }
631
632 static int
633 fib_route_path_cmp_for_sort (void * v1,
634                              void * v2)
635 {
636     return (fib_route_path_cmp(v1, v2));
637 }
638
639 fib_node_index_t
640 fib_table_entry_update (u32 fib_index,
641                         const fib_prefix_t *prefix,
642                         fib_source_t source,
643                         fib_entry_flag_t flags,
644                         const fib_route_path_t *paths)
645 {
646     fib_node_index_t fib_entry_index;
647     fib_table_t *fib_table;
648
649     fib_table = fib_table_get(fib_index, prefix->fp_proto);
650     fib_entry_index = fib_table_lookup_exact_match_i(fib_table, prefix);
651
652     /*
653      * sort the paths provided by the control plane. this means
654      * the paths and the extension on the entry will be sorted.
655      */
656     vec_sort_with_function(((fib_route_path_t*)paths), // const cast
657                            fib_route_path_cmp_for_sort);
658
659     if (FIB_NODE_INDEX_INVALID == fib_entry_index)
660     {
661         fib_entry_index = fib_entry_create(fib_index, prefix,
662                                            source, flags,
663                                            paths);
664
665         fib_table_entry_insert(fib_table, prefix, fib_entry_index);
666         fib_table->ft_src_route_counts[source]++;
667     }
668     else
669     {
670         int was_sourced;
671
672         was_sourced = fib_entry_is_sourced(fib_entry_index, source);
673         fib_entry_update(fib_entry_index, source, flags, paths);
674
675         if (was_sourced != fib_entry_is_sourced(fib_entry_index, source))
676         {
677             fib_table->ft_src_route_counts[source]++;
678         }
679     }
680
681     return (fib_entry_index);
682 }
683
684 fib_node_index_t
685 fib_table_entry_update_one_path (u32 fib_index,
686                                  const fib_prefix_t *prefix,
687                                  fib_source_t source,
688                                  fib_entry_flag_t flags,
689                                  fib_protocol_t next_hop_proto,
690                                  const ip46_address_t *next_hop,
691                                  u32 next_hop_sw_if_index,
692                                  u32 next_hop_fib_index,
693                                  u32 next_hop_weight,
694                                  mpls_label_t next_hop_label,
695                                  fib_route_path_flags_t path_flags)
696 {
697     fib_node_index_t fib_entry_index;
698     fib_route_path_t path = {
699         .frp_proto = next_hop_proto,
700         .frp_addr = (NULL == next_hop? zero_addr : *next_hop),
701         .frp_sw_if_index = next_hop_sw_if_index,
702         .frp_fib_index = next_hop_fib_index,
703         .frp_weight = next_hop_weight,
704         .frp_flags = path_flags,
705         .frp_label = next_hop_label,
706     };
707     fib_route_path_t *paths = NULL;
708
709     fib_table_route_path_fixup(prefix, &path);
710     vec_add1(paths, path);
711
712     fib_entry_index = 
713         fib_table_entry_update(fib_index, prefix, source, flags, paths);
714
715     vec_free(paths);
716
717     return (fib_entry_index);
718 }
719
720 static void
721 fib_table_entry_delete_i (u32 fib_index,
722                           fib_node_index_t fib_entry_index,
723                           const fib_prefix_t *prefix,
724                           fib_source_t source)
725 {
726     fib_entry_src_flag_t src_flag;
727     fib_table_t *fib_table;
728     int was_sourced;
729
730     fib_table = fib_table_get(fib_index, prefix->fp_proto);
731     was_sourced = fib_entry_is_sourced(fib_entry_index, source);
732
733     /*
734      * don't nobody go nowhere
735      */
736     fib_entry_lock(fib_entry_index);
737
738     src_flag = fib_entry_delete(fib_entry_index, source);
739
740     if (!(FIB_ENTRY_SRC_FLAG_ADDED & src_flag))
741     {
742         /*
743          * last source gone. remove from the table
744          */
745         fib_table_entry_remove(fib_table, prefix, fib_entry_index);
746
747         /*
748          * now the entry is no longer in the table, we can
749          * inform the entries that it covers to re-calculate their cover
750          */
751         fib_entry_cover_change_notify(fib_entry_index,
752                                       FIB_NODE_INDEX_INVALID);
753     }
754     /*
755      * else
756      *   still has sources, leave it be.
757      */
758     if (was_sourced != fib_entry_is_sourced(fib_entry_index, source))
759     {
760         fib_table->ft_src_route_counts[source]--;
761     }
762
763     fib_entry_unlock(fib_entry_index);
764 }
765
766 void
767 fib_table_entry_delete (u32 fib_index,
768                         const fib_prefix_t *prefix,
769                         fib_source_t source)
770 {
771     fib_node_index_t fib_entry_index;
772
773     fib_entry_index = fib_table_lookup_exact_match(fib_index, prefix);
774
775     if (FIB_NODE_INDEX_INVALID == fib_entry_index)
776     {
777         /*
778          * removing an etry that does not exist.
779          * i'll allow it, but i won't like it.
780          */
781         clib_warning("%U not in FIB", format_fib_prefix, prefix);
782     }
783     else
784     {
785         fib_table_entry_delete_i(fib_index, fib_entry_index, prefix, source);
786     }
787 }
788
789 void
790 fib_table_entry_delete_index (fib_node_index_t fib_entry_index,
791                               fib_source_t source)
792 {
793     fib_prefix_t prefix;
794
795     fib_entry_get_prefix(fib_entry_index, &prefix);
796
797     fib_table_entry_delete_i(fib_entry_get_fib_index(fib_entry_index),
798                              fib_entry_index, &prefix, source);
799 }
800
801 fib_node_index_t
802 fib_table_entry_local_label_add (u32 fib_index,
803                                  const fib_prefix_t *prefix,
804                                  mpls_label_t label)
805 {
806     fib_node_index_t fib_entry_index;
807  
808     fib_entry_index = fib_table_entry_special_dpo_add(fib_index, prefix, 
809                                                       FIB_SOURCE_MPLS,
810                                                       FIB_ENTRY_FLAG_NONE,
811                                                       NULL);
812     fib_entry_set_source_data(fib_entry_index, FIB_SOURCE_MPLS, &label);
813
814     return (fib_entry_index);
815 }
816
817 void
818 fib_table_entry_local_label_remove (u32 fib_index,
819                                     const fib_prefix_t *prefix,
820                                     mpls_label_t label)
821 {
822     fib_node_index_t fib_entry_index;
823     const void *data;
824     mpls_label_t pl;
825
826     fib_entry_index = fib_table_lookup_exact_match(fib_index, prefix);
827
828     if (FIB_NODE_INDEX_INVALID == fib_entry_index)
829         return;
830
831     data = fib_entry_get_source_data(fib_entry_index, FIB_SOURCE_MPLS);
832
833     if (NULL == data)
834         return;
835
836     pl = *(mpls_label_t*)data;
837
838     if (pl != label)
839         return;
840
841     pl = MPLS_LABEL_INVALID;
842
843     fib_entry_set_source_data(fib_entry_index, FIB_SOURCE_MPLS, &pl);
844     fib_table_entry_special_remove(fib_index,
845                                    prefix,
846                                    FIB_SOURCE_MPLS);
847 }
848
849 u32
850 fib_table_get_index_for_sw_if_index (fib_protocol_t proto,
851                                      u32 sw_if_index)
852 {
853     switch (proto)
854     {
855     case FIB_PROTOCOL_IP4:
856         return (ip4_fib_table_get_index_for_sw_if_index(sw_if_index));
857     case FIB_PROTOCOL_IP6:
858         return (ip6_fib_table_get_index_for_sw_if_index(sw_if_index));
859     case FIB_PROTOCOL_MPLS:
860         return (mpls_fib_table_get_index_for_sw_if_index(sw_if_index));
861     }
862     return (~0);
863 }
864
865 flow_hash_config_t
866 fib_table_get_flow_hash_config (u32 fib_index,
867                                 fib_protocol_t proto)
868 {
869     switch (proto)
870     {
871     case FIB_PROTOCOL_IP4:
872         return (ip4_fib_table_get_flow_hash_config(fib_index));
873     case FIB_PROTOCOL_IP6:
874         return (ip6_fib_table_get_flow_hash_config(fib_index));
875     case FIB_PROTOCOL_MPLS:
876         return (mpls_fib_table_get_flow_hash_config(fib_index));
877     }
878     return (0);
879 }
880
881
882 u32
883 fib_table_get_table_id_for_sw_if_index (fib_protocol_t proto,
884                                         u32 sw_if_index)
885 {
886     fib_table_t *fib_table;
887
888     fib_table = fib_table_get(fib_table_get_index_for_sw_if_index(
889                                   proto, sw_if_index),
890                               proto);
891
892     return ((NULL != fib_table ? fib_table->ft_table_id : ~0));
893 }
894
895 u32
896 fib_table_find (fib_protocol_t proto,
897                 u32 table_id)
898 {
899     switch (proto)
900     {
901     case FIB_PROTOCOL_IP4:
902         return (ip4_fib_index_from_table_id(table_id));
903     case FIB_PROTOCOL_IP6:
904         return (ip6_fib_index_from_table_id(table_id));
905     case FIB_PROTOCOL_MPLS:
906         return (mpls_fib_index_from_table_id(table_id));
907     }
908     return (~0);
909 }
910
911 u32
912 fib_table_find_or_create_and_lock (fib_protocol_t proto,
913                                    u32 table_id)
914 {
915     fib_table_t *fib_table;
916     fib_node_index_t fi;
917
918     switch (proto)
919     {
920     case FIB_PROTOCOL_IP4:
921         fi = ip4_fib_table_find_or_create_and_lock(table_id);
922         break;
923     case FIB_PROTOCOL_IP6:
924         fi = ip6_fib_table_find_or_create_and_lock(table_id);
925         break;
926     case FIB_PROTOCOL_MPLS:
927         fi = mpls_fib_table_find_or_create_and_lock(table_id);
928         break;
929     default:
930         return (~0);        
931     }
932
933     fib_table = fib_table_get(fi, proto);
934
935     fib_table->ft_desc = format(NULL, "%U-VRF:%d",
936                                 format_fib_protocol, proto,
937                                 table_id);
938
939     return (fi);
940 }
941
942 u32
943 fib_table_create_and_lock (fib_protocol_t proto,
944                            const char *const fmt,
945                            ...)
946 {
947     fib_table_t *fib_table;
948     fib_node_index_t fi;
949     va_list ap;
950
951     va_start(ap, fmt);
952
953     switch (proto)
954     {
955     case FIB_PROTOCOL_IP4:
956         fi = ip4_fib_table_create_and_lock();
957         break;
958     case FIB_PROTOCOL_IP6:
959         fi = ip6_fib_table_create_and_lock();
960         break;
961      case FIB_PROTOCOL_MPLS:
962         fi = mpls_fib_table_create_and_lock();
963         break;
964    default:
965         return (~0);        
966     }
967
968     fib_table = fib_table_get(fi, proto);
969
970     fib_table->ft_desc = va_format(fib_table->ft_desc, fmt, &ap);
971
972     va_end(ap);
973     return (fi);
974 }
975
976 static void
977 fib_table_destroy (fib_table_t *fib_table)
978 {
979     vec_free(fib_table->ft_desc);
980
981     switch (fib_table->ft_proto)
982     {
983     case FIB_PROTOCOL_IP4:
984         ip4_fib_table_destroy(&fib_table->v4);
985         break;
986     case FIB_PROTOCOL_IP6:
987         ip6_fib_table_destroy(fib_table->ft_index);
988         break;
989     case FIB_PROTOCOL_MPLS:
990         mpls_fib_table_destroy(&fib_table->mpls);
991         break;
992     }
993 }
994
995 void
996 fib_table_unlock (u32 fib_index,
997                   fib_protocol_t proto)
998 {
999     fib_table_t *fib_table;
1000
1001     fib_table = fib_table_get(fib_index, proto);
1002     fib_table->ft_locks--;
1003
1004     if (0 == fib_table->ft_locks)
1005     {
1006         fib_table_destroy(fib_table);
1007     }
1008 }
1009 void
1010 fib_table_lock (u32 fib_index,
1011                 fib_protocol_t proto)
1012 {
1013     fib_table_t *fib_table;
1014
1015     fib_table = fib_table_get(fib_index, proto);
1016     fib_table->ft_locks++;
1017 }
1018
1019 u32
1020 fib_table_get_num_entries (u32 fib_index,
1021                            fib_protocol_t proto,
1022                            fib_source_t source)
1023 {
1024     fib_table_t *fib_table;
1025
1026     fib_table = fib_table_get(fib_index, proto);
1027
1028     return (fib_table->ft_src_route_counts[source]);
1029 }
1030
1031 u8*
1032 format_fib_table_name (u8* s, va_list ap)
1033 {
1034     fib_node_index_t fib_index = va_arg(ap, fib_node_index_t);
1035     fib_protocol_t proto = va_arg(ap, int); // int promotion
1036     fib_table_t *fib_table;
1037
1038     fib_table = fib_table_get(fib_index, proto);
1039
1040     s = format(s, "%v", fib_table->ft_desc);
1041
1042     return (s);
1043 }
1044
1045 void
1046 fib_table_flush (u32 fib_index,
1047                  fib_protocol_t proto,
1048                  fib_source_t source)
1049 {
1050     // FIXME
1051     ASSERT(0);
1052 }