dpdk: Add support for Mellanox ConnectX-4 devices
[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_dpo_update (u32 fib_index,
328                                     const fib_prefix_t *prefix,
329                                     fib_source_t source,
330                                     fib_entry_flag_t flags,
331                                     const dpo_id_t *dpo)
332 {
333     fib_node_index_t fib_entry_index;
334     fib_table_t *fib_table;
335
336     fib_table = fib_table_get(fib_index, prefix->fp_proto);
337     fib_entry_index = fib_table_lookup_exact_match_i(fib_table, prefix);
338
339     if (FIB_NODE_INDEX_INVALID == fib_entry_index)
340     {
341         fib_entry_index = fib_entry_create_special(fib_index, prefix,
342                                                    source, flags,
343                                                    dpo);
344
345         fib_table_entry_insert(fib_table, prefix, fib_entry_index);
346         fib_table->ft_src_route_counts[source]++;
347     }
348     else
349     {
350         int was_sourced;
351
352         was_sourced = fib_entry_is_sourced(fib_entry_index, source);
353
354         if (was_sourced)
355             fib_entry_special_update(fib_entry_index, source, flags, dpo);
356         else
357             fib_entry_special_add(fib_entry_index, source, flags, dpo);
358
359         if (was_sourced != fib_entry_is_sourced(fib_entry_index, source))
360         {
361             fib_table->ft_src_route_counts[source]++;
362         }
363     }
364
365     return (fib_entry_index);
366 }
367
368 fib_node_index_t
369 fib_table_entry_special_add (u32 fib_index,
370                              const fib_prefix_t *prefix,
371                              fib_source_t source,
372                              fib_entry_flag_t flags,
373                              adj_index_t adj_index)
374 {
375     fib_node_index_t fib_entry_index;
376     dpo_id_t tmp_dpo = DPO_INVALID;
377
378     if (ADJ_INDEX_INVALID != adj_index)
379     {
380         dpo_set(&tmp_dpo,
381                 DPO_ADJACENCY,
382                 FIB_PROTOCOL_MAX,
383                 adj_index);
384     }
385     else
386     {
387         dpo_copy(&tmp_dpo, drop_dpo_get(fib_proto_to_dpo(prefix->fp_proto)));
388     }
389  
390     fib_entry_index = fib_table_entry_special_dpo_add(fib_index, prefix, source,
391                                                       flags, &tmp_dpo);
392
393     dpo_unlock(&tmp_dpo);
394
395     return (fib_entry_index);
396 }
397
398 void
399 fib_table_entry_special_remove (u32 fib_index,
400                                 const fib_prefix_t *prefix,
401                                 fib_source_t source)
402 {
403     /*
404      * 1 is it present
405      *   yes => remove source
406      *    2 - is it still sourced?
407      *      no => cover walk
408      */
409     fib_node_index_t fib_entry_index;
410     fib_table_t *fib_table;
411
412     fib_table = fib_table_get(fib_index, prefix->fp_proto);
413     fib_entry_index = fib_table_lookup_exact_match_i(fib_table, prefix);
414
415     if (FIB_NODE_INDEX_INVALID == fib_entry_index)
416     {
417         /*
418          * removing an etry that does not exist. i'll allow it.
419          */
420     }
421     else
422     {
423         fib_entry_src_flag_t src_flag;
424         int was_sourced;
425
426         /*
427          * don't nobody go nowhere
428          */
429         fib_entry_lock(fib_entry_index);
430         was_sourced = fib_entry_is_sourced(fib_entry_index, source);
431
432         src_flag = fib_entry_special_remove(fib_entry_index, source);
433
434         if (!(FIB_ENTRY_SRC_FLAG_ADDED & src_flag))
435         {
436             /*
437              * last source gone. remove from the table
438              */
439             fib_table_entry_remove(fib_table, prefix, fib_entry_index);
440
441             /*
442              * now the entry is no longer in the table, we can
443              * inform the entries that it covers to re-calculate their cover
444              */
445             fib_entry_cover_change_notify(fib_entry_index,
446                                           FIB_NODE_INDEX_INVALID);
447         }
448         /*
449          * else
450          *   still has sources, leave it be.
451          */
452         if (was_sourced != fib_entry_is_sourced(fib_entry_index, source))
453         {
454             fib_table->ft_src_route_counts[source]--;
455         }
456
457         fib_entry_unlock(fib_entry_index);
458     }
459 }
460
461 /**
462  * fib_table_route_path_fixup
463  *
464  * Convert attached hosts to attached next-hops.
465  * 
466  * This special case is required because an attached path will link to a
467  * glean, and the FIB entry will have the interface or API/CLI source. When
468  * the ARP/ND process is completes then that source (which will provide a
469  * complete adjacency) will be lower priority and so the FIB entry will
470  * remain linked to a glean and traffic will never reach the hosts. For
471  * an ATTAHCED_HOST path we can link the path directly to the [incomplete]
472  * adjacency.
473  */
474 static void
475 fib_table_route_path_fixup (const fib_prefix_t *prefix,
476                             fib_route_path_t *path)
477 {
478     if (fib_prefix_is_host(prefix) &&
479         ip46_address_is_zero(&path->frp_addr) &&
480         path->frp_sw_if_index != ~0)
481     {
482         path->frp_addr = prefix->fp_addr;
483     }
484 }                 
485
486 fib_node_index_t
487 fib_table_entry_path_add (u32 fib_index,
488                           const fib_prefix_t *prefix,
489                           fib_source_t source,
490                           fib_entry_flag_t flags,
491                           fib_protocol_t next_hop_proto,
492                           const ip46_address_t *next_hop,
493                           u32 next_hop_sw_if_index,
494                           u32 next_hop_fib_index,
495                           u32 next_hop_weight,
496                           mpls_label_t *next_hop_labels,
497                           fib_route_path_flags_t path_flags)
498 {
499     fib_route_path_t path = {
500         .frp_proto = next_hop_proto,
501         .frp_addr = (NULL == next_hop? zero_addr : *next_hop),
502         .frp_sw_if_index = next_hop_sw_if_index,
503         .frp_fib_index = next_hop_fib_index,
504         .frp_weight = next_hop_weight,
505         .frp_flags = path_flags,
506         .frp_label_stack = next_hop_labels,
507     };
508     fib_node_index_t fib_entry_index;
509     fib_route_path_t *paths = NULL;
510
511     vec_add1(paths, path);
512
513     fib_entry_index = fib_table_entry_path_add2(fib_index, prefix,
514                                                 source, flags, paths);
515
516     vec_free(paths);
517     return (fib_entry_index);
518 }
519
520 fib_node_index_t
521 fib_table_entry_path_add2 (u32 fib_index,
522                            const fib_prefix_t *prefix,
523                            fib_source_t source,
524                            fib_entry_flag_t flags,
525                            fib_route_path_t *rpath)
526 {
527     fib_node_index_t fib_entry_index;
528     fib_table_t *fib_table;
529     u32 ii;
530
531     fib_table = fib_table_get(fib_index, prefix->fp_proto);
532     fib_entry_index = fib_table_lookup_exact_match_i(fib_table, prefix);
533
534     for (ii = 0; ii < vec_len(rpath); ii++)
535     {
536         fib_table_route_path_fixup(prefix, &rpath[ii]);
537     }
538
539     if (FIB_NODE_INDEX_INVALID == fib_entry_index)
540     {
541         fib_entry_index = fib_entry_create(fib_index, prefix,
542                                            source, flags,
543                                            rpath);
544
545         fib_table_entry_insert(fib_table, prefix, fib_entry_index);
546         fib_table->ft_src_route_counts[source]++;
547     }
548     else
549     {
550         int was_sourced;
551
552         was_sourced = fib_entry_is_sourced(fib_entry_index, source);
553         fib_entry_path_add(fib_entry_index, source, flags, rpath);;
554
555         if (was_sourced != fib_entry_is_sourced(fib_entry_index, source))
556         {
557             fib_table->ft_src_route_counts[source]++;
558         }
559     }
560
561     return (fib_entry_index);
562 }
563
564 void
565 fib_table_entry_path_remove2 (u32 fib_index,
566                               const fib_prefix_t *prefix,
567                               fib_source_t source,
568                               fib_route_path_t *rpath)
569 {
570     /*
571      * 1 is it present
572      *   yes => remove source
573      *    2 - is it still sourced?
574      *      no => cover walk
575      */
576     fib_node_index_t fib_entry_index;
577     fib_table_t *fib_table;
578     u32 ii;
579
580     fib_table = fib_table_get(fib_index, prefix->fp_proto);
581     fib_entry_index = fib_table_lookup_exact_match_i(fib_table, prefix);
582
583     for (ii = 0; ii < vec_len(rpath); ii++)
584     {
585         fib_table_route_path_fixup(prefix, &rpath[ii]);
586     }
587
588     if (FIB_NODE_INDEX_INVALID == fib_entry_index)
589     {
590         /*
591          * removing an etry that does not exist. i'll allow it.
592          */
593     }
594     else
595     {
596         fib_entry_src_flag_t src_flag;
597         int was_sourced;
598
599         /*
600          * don't nobody go nowhere
601          */
602         fib_entry_lock(fib_entry_index);
603         was_sourced = fib_entry_is_sourced(fib_entry_index, source);
604
605         src_flag = fib_entry_path_remove(fib_entry_index, source, rpath);
606
607         if (!(FIB_ENTRY_SRC_FLAG_ADDED & src_flag))
608         {
609             /*
610              * last source gone. remove from the table
611              */
612             fib_table_entry_remove(fib_table, prefix, fib_entry_index);
613
614             /*
615              * now the entry is no longer in the table, we can
616              * inform the entries that it covers to re-calculate their cover
617              */
618             fib_entry_cover_change_notify(fib_entry_index,
619                                           FIB_NODE_INDEX_INVALID);
620         }
621         /*
622          * else
623          *   still has sources, leave it be.
624          */
625         if (was_sourced != fib_entry_is_sourced(fib_entry_index, source))
626         {
627             fib_table->ft_src_route_counts[source]--;
628         }
629
630         fib_entry_unlock(fib_entry_index);
631     }
632 }
633
634 void
635 fib_table_entry_path_remove (u32 fib_index,
636                              const fib_prefix_t *prefix,
637                              fib_source_t source,
638                              fib_protocol_t next_hop_proto,
639                              const ip46_address_t *next_hop,
640                              u32 next_hop_sw_if_index,
641                              u32 next_hop_fib_index,
642                              u32 next_hop_weight,
643                              fib_route_path_flags_t path_flags)
644 {
645     /*
646      * 1 is it present
647      *   yes => remove source
648      *    2 - is it still sourced?
649      *      no => cover walk
650      */
651     fib_route_path_t path = {
652         .frp_proto = next_hop_proto,
653         .frp_addr = (NULL == next_hop? zero_addr : *next_hop),
654         .frp_sw_if_index = next_hop_sw_if_index,
655         .frp_fib_index = next_hop_fib_index,
656         .frp_weight = next_hop_weight,
657         .frp_flags = path_flags,
658     };
659     fib_route_path_t *paths = NULL;
660
661     fib_table_route_path_fixup(prefix, &path);
662     vec_add1(paths, path);
663
664     fib_table_entry_path_remove2(fib_index, prefix, source, paths);
665
666     vec_free(paths);
667 }
668
669 static int
670 fib_route_path_cmp_for_sort (void * v1,
671                              void * v2)
672 {
673     return (fib_route_path_cmp(v1, v2));
674 }
675
676 fib_node_index_t
677 fib_table_entry_update (u32 fib_index,
678                         const fib_prefix_t *prefix,
679                         fib_source_t source,
680                         fib_entry_flag_t flags,
681                         fib_route_path_t *paths)
682 {
683     fib_node_index_t fib_entry_index;
684     fib_table_t *fib_table;
685     u32 ii;
686
687     fib_table = fib_table_get(fib_index, prefix->fp_proto);
688     fib_entry_index = fib_table_lookup_exact_match_i(fib_table, prefix);
689
690     for (ii = 0; ii < vec_len(paths); ii++)
691     {
692         fib_table_route_path_fixup(prefix, &paths[ii]);
693     }
694     /*
695      * sort the paths provided by the control plane. this means
696      * the paths and the extension on the entry will be sorted.
697      */
698     vec_sort_with_function(paths, fib_route_path_cmp_for_sort);
699
700     if (FIB_NODE_INDEX_INVALID == fib_entry_index)
701     {
702         fib_entry_index = fib_entry_create(fib_index, prefix,
703                                            source, flags,
704                                            paths);
705
706         fib_table_entry_insert(fib_table, prefix, fib_entry_index);
707         fib_table->ft_src_route_counts[source]++;
708     }
709     else
710     {
711         int was_sourced;
712
713         was_sourced = fib_entry_is_sourced(fib_entry_index, source);
714         fib_entry_update(fib_entry_index, source, flags, paths);
715
716         if (was_sourced != fib_entry_is_sourced(fib_entry_index, source))
717         {
718             fib_table->ft_src_route_counts[source]++;
719         }
720     }
721
722     return (fib_entry_index);
723 }
724
725 fib_node_index_t
726 fib_table_entry_update_one_path (u32 fib_index,
727                                  const fib_prefix_t *prefix,
728                                  fib_source_t source,
729                                  fib_entry_flag_t flags,
730                                  fib_protocol_t next_hop_proto,
731                                  const ip46_address_t *next_hop,
732                                  u32 next_hop_sw_if_index,
733                                  u32 next_hop_fib_index,
734                                  u32 next_hop_weight,
735                                  mpls_label_t *next_hop_labels,
736                                  fib_route_path_flags_t path_flags)
737 {
738     fib_node_index_t fib_entry_index;
739     fib_route_path_t path = {
740         .frp_proto = next_hop_proto,
741         .frp_addr = (NULL == next_hop? zero_addr : *next_hop),
742         .frp_sw_if_index = next_hop_sw_if_index,
743         .frp_fib_index = next_hop_fib_index,
744         .frp_weight = next_hop_weight,
745         .frp_flags = path_flags,
746         .frp_label_stack = next_hop_labels,
747     };
748     fib_route_path_t *paths = NULL;
749
750     fib_table_route_path_fixup(prefix, &path);
751     vec_add1(paths, path);
752
753     fib_entry_index = 
754         fib_table_entry_update(fib_index, prefix, source, flags, paths);
755
756     vec_free(paths);
757
758     return (fib_entry_index);
759 }
760
761 static void
762 fib_table_entry_delete_i (u32 fib_index,
763                           fib_node_index_t fib_entry_index,
764                           const fib_prefix_t *prefix,
765                           fib_source_t source)
766 {
767     fib_entry_src_flag_t src_flag;
768     fib_table_t *fib_table;
769     int was_sourced;
770
771     fib_table = fib_table_get(fib_index, prefix->fp_proto);
772     was_sourced = fib_entry_is_sourced(fib_entry_index, source);
773
774     /*
775      * don't nobody go nowhere
776      */
777     fib_entry_lock(fib_entry_index);
778
779     src_flag = fib_entry_delete(fib_entry_index, source);
780
781     if (!(FIB_ENTRY_SRC_FLAG_ADDED & src_flag))
782     {
783         /*
784          * last source gone. remove from the table
785          */
786         fib_table_entry_remove(fib_table, prefix, fib_entry_index);
787
788         /*
789          * now the entry is no longer in the table, we can
790          * inform the entries that it covers to re-calculate their cover
791          */
792         fib_entry_cover_change_notify(fib_entry_index,
793                                       FIB_NODE_INDEX_INVALID);
794     }
795     /*
796      * else
797      *   still has sources, leave it be.
798      */
799     if (was_sourced != fib_entry_is_sourced(fib_entry_index, source))
800     {
801         fib_table->ft_src_route_counts[source]--;
802     }
803
804     fib_entry_unlock(fib_entry_index);
805 }
806
807 void
808 fib_table_entry_delete (u32 fib_index,
809                         const fib_prefix_t *prefix,
810                         fib_source_t source)
811 {
812     fib_node_index_t fib_entry_index;
813
814     fib_entry_index = fib_table_lookup_exact_match(fib_index, prefix);
815
816     if (FIB_NODE_INDEX_INVALID == fib_entry_index)
817     {
818         /*
819          * removing an etry that does not exist.
820          * i'll allow it, but i won't like it.
821          */
822         clib_warning("%U not in FIB", format_fib_prefix, prefix);
823     }
824     else
825     {
826         fib_table_entry_delete_i(fib_index, fib_entry_index, prefix, source);
827     }
828 }
829
830 void
831 fib_table_entry_delete_index (fib_node_index_t fib_entry_index,
832                               fib_source_t source)
833 {
834     fib_prefix_t prefix;
835
836     fib_entry_get_prefix(fib_entry_index, &prefix);
837
838     fib_table_entry_delete_i(fib_entry_get_fib_index(fib_entry_index),
839                              fib_entry_index, &prefix, source);
840 }
841
842 fib_node_index_t
843 fib_table_entry_local_label_add (u32 fib_index,
844                                  const fib_prefix_t *prefix,
845                                  mpls_label_t label)
846 {
847     fib_node_index_t fib_entry_index;
848  
849     fib_entry_index = fib_table_lookup_exact_match(fib_index, prefix);
850
851     if (FIB_NODE_INDEX_INVALID == fib_entry_index ||
852         !fib_entry_is_sourced(fib_entry_index, FIB_SOURCE_MPLS))
853     {
854         /*
855          * only source the prefix once. this allows the label change
856          * operation to work
857          */
858         fib_entry_index = fib_table_entry_special_dpo_add(fib_index, prefix,
859                                                           FIB_SOURCE_MPLS,
860                                                           FIB_ENTRY_FLAG_NONE,
861                                                           NULL);
862     }
863
864     fib_entry_set_source_data(fib_entry_index, FIB_SOURCE_MPLS, &label);
865
866     return (fib_entry_index);
867 }
868
869 void
870 fib_table_entry_local_label_remove (u32 fib_index,
871                                     const fib_prefix_t *prefix,
872                                     mpls_label_t label)
873 {
874     fib_node_index_t fib_entry_index;
875     const void *data;
876     mpls_label_t pl;
877
878     fib_entry_index = fib_table_lookup_exact_match(fib_index, prefix);
879
880     if (FIB_NODE_INDEX_INVALID == fib_entry_index)
881         return;
882
883     data = fib_entry_get_source_data(fib_entry_index, FIB_SOURCE_MPLS);
884
885     if (NULL == data)
886         return;
887
888     pl = *(mpls_label_t*)data;
889
890     if (pl != label)
891         return;
892
893     pl = MPLS_LABEL_INVALID;
894
895     fib_entry_set_source_data(fib_entry_index, FIB_SOURCE_MPLS, &pl);
896     fib_table_entry_special_remove(fib_index,
897                                    prefix,
898                                    FIB_SOURCE_MPLS);
899 }
900
901 u32
902 fib_table_get_index_for_sw_if_index (fib_protocol_t proto,
903                                      u32 sw_if_index)
904 {
905     switch (proto)
906     {
907     case FIB_PROTOCOL_IP4:
908         return (ip4_fib_table_get_index_for_sw_if_index(sw_if_index));
909     case FIB_PROTOCOL_IP6:
910         return (ip6_fib_table_get_index_for_sw_if_index(sw_if_index));
911     case FIB_PROTOCOL_MPLS:
912         return (mpls_fib_table_get_index_for_sw_if_index(sw_if_index));
913     }
914     return (~0);
915 }
916
917 flow_hash_config_t
918 fib_table_get_flow_hash_config (u32 fib_index,
919                                 fib_protocol_t proto)
920 {
921     switch (proto)
922     {
923     case FIB_PROTOCOL_IP4:
924         return (ip4_fib_table_get_flow_hash_config(fib_index));
925     case FIB_PROTOCOL_IP6:
926         return (ip6_fib_table_get_flow_hash_config(fib_index));
927     case FIB_PROTOCOL_MPLS:
928         return (mpls_fib_table_get_flow_hash_config(fib_index));
929     }
930     return (0);
931 }
932
933
934 u32
935 fib_table_get_table_id_for_sw_if_index (fib_protocol_t proto,
936                                         u32 sw_if_index)
937 {
938     fib_table_t *fib_table;
939
940     fib_table = fib_table_get(fib_table_get_index_for_sw_if_index(
941                                   proto, sw_if_index),
942                               proto);
943
944     return ((NULL != fib_table ? fib_table->ft_table_id : ~0));
945 }
946
947 u32
948 fib_table_find (fib_protocol_t proto,
949                 u32 table_id)
950 {
951     switch (proto)
952     {
953     case FIB_PROTOCOL_IP4:
954         return (ip4_fib_index_from_table_id(table_id));
955     case FIB_PROTOCOL_IP6:
956         return (ip6_fib_index_from_table_id(table_id));
957     case FIB_PROTOCOL_MPLS:
958         return (mpls_fib_index_from_table_id(table_id));
959     }
960     return (~0);
961 }
962
963 u32
964 fib_table_find_or_create_and_lock (fib_protocol_t proto,
965                                    u32 table_id)
966 {
967     fib_table_t *fib_table;
968     fib_node_index_t fi;
969
970     switch (proto)
971     {
972     case FIB_PROTOCOL_IP4:
973         fi = ip4_fib_table_find_or_create_and_lock(table_id);
974         break;
975     case FIB_PROTOCOL_IP6:
976         fi = ip6_fib_table_find_or_create_and_lock(table_id);
977         break;
978     case FIB_PROTOCOL_MPLS:
979         fi = mpls_fib_table_find_or_create_and_lock(table_id);
980         break;
981     default:
982         return (~0);        
983     }
984
985     fib_table = fib_table_get(fi, proto);
986
987     fib_table->ft_desc = format(NULL, "%U-VRF:%d",
988                                 format_fib_protocol, proto,
989                                 table_id);
990
991     return (fi);
992 }
993
994 u32
995 fib_table_create_and_lock (fib_protocol_t proto,
996                            const char *const fmt,
997                            ...)
998 {
999     fib_table_t *fib_table;
1000     fib_node_index_t fi;
1001     va_list ap;
1002
1003     va_start(ap, fmt);
1004
1005     switch (proto)
1006     {
1007     case FIB_PROTOCOL_IP4:
1008         fi = ip4_fib_table_create_and_lock();
1009         break;
1010     case FIB_PROTOCOL_IP6:
1011         fi = ip6_fib_table_create_and_lock();
1012         break;
1013      case FIB_PROTOCOL_MPLS:
1014         fi = mpls_fib_table_create_and_lock();
1015         break;
1016    default:
1017         return (~0);        
1018     }
1019
1020     fib_table = fib_table_get(fi, proto);
1021
1022     fib_table->ft_desc = va_format(fib_table->ft_desc, fmt, &ap);
1023
1024     va_end(ap);
1025     return (fi);
1026 }
1027
1028 static void
1029 fib_table_destroy (fib_table_t *fib_table)
1030 {
1031     vec_free(fib_table->ft_desc);
1032
1033     switch (fib_table->ft_proto)
1034     {
1035     case FIB_PROTOCOL_IP4:
1036         ip4_fib_table_destroy(&fib_table->v4);
1037         break;
1038     case FIB_PROTOCOL_IP6:
1039         ip6_fib_table_destroy(fib_table->ft_index);
1040         break;
1041     case FIB_PROTOCOL_MPLS:
1042         mpls_fib_table_destroy(&fib_table->mpls);
1043         break;
1044     }
1045 }
1046
1047 void
1048 fib_table_unlock (u32 fib_index,
1049                   fib_protocol_t proto)
1050 {
1051     fib_table_t *fib_table;
1052
1053     fib_table = fib_table_get(fib_index, proto);
1054     fib_table->ft_locks--;
1055
1056     if (0 == fib_table->ft_locks)
1057     {
1058         fib_table_destroy(fib_table);
1059     }
1060 }
1061 void
1062 fib_table_lock (u32 fib_index,
1063                 fib_protocol_t proto)
1064 {
1065     fib_table_t *fib_table;
1066
1067     fib_table = fib_table_get(fib_index, proto);
1068     fib_table->ft_locks++;
1069 }
1070
1071 u32
1072 fib_table_get_num_entries (u32 fib_index,
1073                            fib_protocol_t proto,
1074                            fib_source_t source)
1075 {
1076     fib_table_t *fib_table;
1077
1078     fib_table = fib_table_get(fib_index, proto);
1079
1080     return (fib_table->ft_src_route_counts[source]);
1081 }
1082
1083 u8*
1084 format_fib_table_name (u8* s, va_list ap)
1085 {
1086     fib_node_index_t fib_index = va_arg(ap, fib_node_index_t);
1087     fib_protocol_t proto = va_arg(ap, int); // int promotion
1088     fib_table_t *fib_table;
1089
1090     fib_table = fib_table_get(fib_index, proto);
1091
1092     s = format(s, "%v", fib_table->ft_desc);
1093
1094     return (s);
1095 }
1096
1097 void
1098 fib_table_flush (u32 fib_index,
1099                  fib_protocol_t proto,
1100                  fib_source_t source)
1101 {
1102     // FIXME
1103     ASSERT(0);
1104 }