FIB table add/delete API
[vpp.git] / src / vnet / mfib / mfib_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/mfib/mfib_table.h>
20 #include <vnet/mfib/ip4_mfib.h>
21 #include <vnet/mfib/ip6_mfib.h>
22 #include <vnet/mfib/mfib_entry.h>
23 #include <vnet/mfib/mfib_signal.h>
24
25 mfib_table_t *
26 mfib_table_get (fib_node_index_t index,
27                 fib_protocol_t proto)
28 {
29     switch (proto)
30     {
31     case FIB_PROTOCOL_IP4:
32         return (pool_elt_at_index(ip4_main.mfibs, index));
33     case FIB_PROTOCOL_IP6:
34         return (pool_elt_at_index(ip6_main.mfibs, index));
35     case FIB_PROTOCOL_MPLS:
36         break;
37     }
38     ASSERT(0);
39     return (NULL);
40 }
41
42 static inline fib_node_index_t
43 mfib_table_lookup_i (const mfib_table_t *mfib_table,
44                      const mfib_prefix_t *prefix)
45 {
46     switch (prefix->fp_proto)
47     {
48     case FIB_PROTOCOL_IP4:
49         return (ip4_mfib_table_lookup(&mfib_table->v4,
50                                       &prefix->fp_src_addr.ip4,
51                                       &prefix->fp_grp_addr.ip4,
52                                       prefix->fp_len));
53     case FIB_PROTOCOL_IP6:
54         return (ip6_mfib_table_lookup(&mfib_table->v6,
55                                       &prefix->fp_src_addr.ip6,
56                                       &prefix->fp_grp_addr.ip6,
57                                       prefix->fp_len));
58     case FIB_PROTOCOL_MPLS:
59         break;
60     }
61     return (FIB_NODE_INDEX_INVALID);
62 }
63
64 fib_node_index_t
65 mfib_table_lookup (u32 fib_index,
66                    const mfib_prefix_t *prefix)
67 {
68     return (mfib_table_lookup_i(mfib_table_get(fib_index, prefix->fp_proto), prefix));
69 }
70
71 static inline fib_node_index_t
72 mfib_table_lookup_exact_match_i (const mfib_table_t *mfib_table,
73                                  const mfib_prefix_t *prefix)
74 {
75     switch (prefix->fp_proto)
76     {
77     case FIB_PROTOCOL_IP4:
78         return (ip4_mfib_table_lookup_exact_match(&mfib_table->v4,
79                                                   &prefix->fp_grp_addr.ip4,
80                                                   &prefix->fp_src_addr.ip4,
81                                                   prefix->fp_len));
82     case FIB_PROTOCOL_IP6:
83         return (ip6_mfib_table_lookup_exact_match(&mfib_table->v6,
84                                                   &prefix->fp_grp_addr.ip6,
85                                                   &prefix->fp_src_addr.ip6,
86                                                   prefix->fp_len));
87     case FIB_PROTOCOL_MPLS:
88         break;
89     }
90     return (FIB_NODE_INDEX_INVALID);
91 }
92
93 fib_node_index_t
94 mfib_table_lookup_exact_match (u32 fib_index,
95                               const mfib_prefix_t *prefix)
96 {
97     return (mfib_table_lookup_exact_match_i(mfib_table_get(fib_index,
98                                                           prefix->fp_proto),
99                                             prefix));
100 }
101
102 static void
103 mfib_table_entry_remove (mfib_table_t *mfib_table,
104                          const mfib_prefix_t *prefix,
105                          fib_node_index_t fib_entry_index)
106 {
107     vlib_smp_unsafe_warning();
108
109     mfib_table->mft_total_route_counts--;
110
111     switch (prefix->fp_proto)
112     {
113     case FIB_PROTOCOL_IP4:
114         ip4_mfib_table_entry_remove(&mfib_table->v4,
115                                     &prefix->fp_grp_addr.ip4,
116                                     &prefix->fp_src_addr.ip4,
117                                     prefix->fp_len);
118         break;
119     case FIB_PROTOCOL_IP6:
120         ip6_mfib_table_entry_remove(&mfib_table->v6,
121                                     &prefix->fp_grp_addr.ip6,
122                                     &prefix->fp_src_addr.ip6,
123                                     prefix->fp_len);
124         break;
125     case FIB_PROTOCOL_MPLS:
126         ASSERT(0);
127         break;
128     }
129
130     mfib_entry_unlock(fib_entry_index);
131 }
132
133 static void
134 mfib_table_entry_insert (mfib_table_t *mfib_table,
135                          const mfib_prefix_t *prefix,
136                          fib_node_index_t mfib_entry_index)
137 {
138     vlib_smp_unsafe_warning();
139
140     mfib_entry_lock(mfib_entry_index);
141     mfib_table->mft_total_route_counts++;
142
143     switch (prefix->fp_proto)
144     {
145     case FIB_PROTOCOL_IP4:
146         ip4_mfib_table_entry_insert(&mfib_table->v4,
147                                     &prefix->fp_grp_addr.ip4,
148                                     &prefix->fp_src_addr.ip4,
149                                     prefix->fp_len,
150                                     mfib_entry_index);
151         break;
152     case FIB_PROTOCOL_IP6:
153         ip6_mfib_table_entry_insert(&mfib_table->v6,
154                                     &prefix->fp_grp_addr.ip6,
155                                     &prefix->fp_src_addr.ip6,
156                                     prefix->fp_len,
157                                     mfib_entry_index);
158         break;
159     case FIB_PROTOCOL_MPLS:
160         break;
161     }
162 }
163
164 fib_node_index_t
165 mfib_table_entry_update (u32 fib_index,
166                          const mfib_prefix_t *prefix,
167                          mfib_source_t source,
168                          fib_rpf_id_t rpf_id,
169                          mfib_entry_flags_t entry_flags)
170 {
171     fib_node_index_t mfib_entry_index;
172     mfib_table_t *mfib_table;
173
174     mfib_table = mfib_table_get(fib_index, prefix->fp_proto);
175     mfib_entry_index = mfib_table_lookup_exact_match_i(mfib_table, prefix);
176
177     if (FIB_NODE_INDEX_INVALID == mfib_entry_index)
178     {
179         if (MFIB_ENTRY_FLAG_NONE != entry_flags)
180         {
181             /*
182              * update to a non-existing entry with non-zero flags
183              */
184             mfib_entry_index = mfib_entry_create(fib_index, source,
185                                                  prefix, rpf_id,
186                                                  entry_flags);
187
188             mfib_table_entry_insert(mfib_table, prefix, mfib_entry_index);
189         }
190         /*
191          * else
192          *   the entry doesn't exist and the request is to set no flags
193          *   the result would be an entry that doesn't exist - so do nothing
194          */
195     }
196     else
197     {
198         mfib_entry_lock(mfib_entry_index);
199
200         if (mfib_entry_update(mfib_entry_index,
201                               source,
202                               entry_flags,
203                               rpf_id,
204                               INDEX_INVALID))
205         {
206             /*
207              * this update means we can now remove the entry.
208              */
209             mfib_table_entry_remove(mfib_table, prefix, mfib_entry_index);
210         }
211
212         mfib_entry_unlock(mfib_entry_index);
213     }
214
215     return (mfib_entry_index);
216 }
217
218 fib_node_index_t
219 mfib_table_entry_path_update (u32 fib_index,
220                               const mfib_prefix_t *prefix,
221                               mfib_source_t source,
222                               const fib_route_path_t *rpath,
223                               mfib_itf_flags_t itf_flags)
224 {
225     fib_node_index_t mfib_entry_index;
226     mfib_table_t *mfib_table;
227
228     mfib_table = mfib_table_get(fib_index, prefix->fp_proto);
229     mfib_entry_index = mfib_table_lookup_exact_match_i(mfib_table, prefix);
230
231     if (FIB_NODE_INDEX_INVALID == mfib_entry_index)
232     {
233         mfib_entry_index = mfib_entry_create(fib_index,
234                                              source,
235                                              prefix,
236                                              MFIB_RPF_ID_NONE,
237                                              MFIB_ENTRY_FLAG_NONE);
238
239         mfib_table_entry_insert(mfib_table, prefix, mfib_entry_index);
240     }
241
242     mfib_entry_path_update(mfib_entry_index,
243                            source,
244                            rpath,
245                            itf_flags);
246
247     return (mfib_entry_index);
248 }
249
250 void
251 mfib_table_entry_path_remove (u32 fib_index,
252                               const mfib_prefix_t *prefix,
253                               mfib_source_t source,
254                               const fib_route_path_t *rpath)
255 {
256     fib_node_index_t mfib_entry_index;
257     mfib_table_t *mfib_table;
258
259     mfib_table = mfib_table_get(fib_index, prefix->fp_proto);
260     mfib_entry_index = mfib_table_lookup_exact_match_i(mfib_table, prefix);
261
262     if (FIB_NODE_INDEX_INVALID == mfib_entry_index)
263     {
264         /*
265          * removing an etry that does not exist. i'll allow it.
266          */
267     }
268     else
269     {
270         int no_more_sources;
271
272         /*
273          * don't nobody go nowhere
274          */
275         mfib_entry_lock(mfib_entry_index);
276
277         no_more_sources = mfib_entry_path_remove(mfib_entry_index,
278                                                  source,
279                                                  rpath);
280
281         if (no_more_sources)
282         {
283             /*
284              * last source gone. remove from the table
285              */
286             mfib_table_entry_remove(mfib_table, prefix, mfib_entry_index);
287         }
288
289         mfib_entry_unlock(mfib_entry_index);
290     }
291 }
292
293 fib_node_index_t
294 mfib_table_entry_special_add (u32 fib_index,
295                               const mfib_prefix_t *prefix,
296                               mfib_source_t source,
297                               mfib_entry_flags_t entry_flags,
298                               index_t rep_dpo)
299 {
300     fib_node_index_t mfib_entry_index;
301     mfib_table_t *mfib_table;
302
303     mfib_table = mfib_table_get(fib_index, prefix->fp_proto);
304     mfib_entry_index = mfib_table_lookup_exact_match_i(mfib_table, prefix);
305
306     if (FIB_NODE_INDEX_INVALID == mfib_entry_index)
307     {
308         mfib_entry_index = mfib_entry_create(fib_index,
309                                              source,
310                                              prefix,
311                                              MFIB_RPF_ID_NONE,
312                                              MFIB_ENTRY_FLAG_NONE);
313
314         mfib_table_entry_insert(mfib_table, prefix, mfib_entry_index);
315     }
316
317     mfib_entry_update(mfib_entry_index, source,
318                       (MFIB_ENTRY_FLAG_EXCLUSIVE | entry_flags),
319                       MFIB_RPF_ID_NONE,
320                       rep_dpo);
321
322     return (mfib_entry_index);
323 }
324
325 static void
326 mfib_table_entry_delete_i (u32 fib_index,
327                            fib_node_index_t mfib_entry_index,
328                            const mfib_prefix_t *prefix,
329                            mfib_source_t source)
330 {
331     mfib_table_t *mfib_table;
332
333     mfib_table = mfib_table_get(fib_index, prefix->fp_proto);
334
335     /*
336      * don't nobody go nowhere
337      */
338     mfib_entry_lock(mfib_entry_index);
339
340     if (mfib_entry_delete(mfib_entry_index, source))
341     {
342         /*
343          * last source gone. remove from the table
344          */
345         mfib_table_entry_remove(mfib_table, prefix, mfib_entry_index);
346     }
347     /*
348      * else
349      *   still has sources, leave it be.
350      */
351
352     mfib_entry_unlock(mfib_entry_index);
353 }
354
355 void
356 mfib_table_entry_delete (u32 fib_index,
357                          const mfib_prefix_t *prefix,
358                          mfib_source_t source)
359 {
360     fib_node_index_t mfib_entry_index;
361
362     mfib_entry_index = mfib_table_lookup_exact_match(fib_index, prefix);
363
364     if (FIB_NODE_INDEX_INVALID == mfib_entry_index)
365     {
366         /*
367          * removing an etry that does not exist.
368          * i'll allow it, but i won't like it.
369          */
370         clib_warning("%U not in FIB", format_mfib_prefix, prefix);
371     }
372     else
373     {
374         mfib_table_entry_delete_i(fib_index, mfib_entry_index,
375                                   prefix, source);
376     }
377 }
378
379 void
380 mfib_table_entry_delete_index (fib_node_index_t mfib_entry_index,
381                                mfib_source_t source)
382 {
383     mfib_prefix_t prefix;
384
385     mfib_entry_get_prefix(mfib_entry_index, &prefix);
386
387     mfib_table_entry_delete_i(mfib_entry_get_fib_index(mfib_entry_index),
388                               mfib_entry_index, &prefix, source);
389 }
390
391 u32
392 mfib_table_get_index_for_sw_if_index (fib_protocol_t proto,
393                                       u32 sw_if_index)
394 {
395     switch (proto)
396     {
397     case FIB_PROTOCOL_IP4:
398         return (ip4_mfib_table_get_index_for_sw_if_index(sw_if_index));
399     case FIB_PROTOCOL_IP6:
400         return (ip6_mfib_table_get_index_for_sw_if_index(sw_if_index));
401     case FIB_PROTOCOL_MPLS:
402         ASSERT(0);
403         break;
404     }
405     return (~0);
406 }
407
408 u32
409 mfib_table_find (fib_protocol_t proto,
410                  u32 table_id)
411 {
412     switch (proto)
413     {
414     case FIB_PROTOCOL_IP4:
415         return (ip4_mfib_index_from_table_id(table_id));
416     case FIB_PROTOCOL_IP6:
417         return (ip6_mfib_index_from_table_id(table_id));
418     case FIB_PROTOCOL_MPLS:
419         ASSERT(0);
420         break;
421     }
422     return (~0);
423 }
424
425 u32
426 mfib_table_find_or_create_and_lock (fib_protocol_t proto,
427                                     u32 table_id,
428                                     mfib_source_t src)
429 {
430     mfib_table_t *mfib_table;
431     fib_node_index_t fi;
432
433     switch (proto)
434     {
435     case FIB_PROTOCOL_IP4:
436         fi = ip4_mfib_table_find_or_create_and_lock(table_id, src);
437         break;
438     case FIB_PROTOCOL_IP6:
439         fi = ip6_mfib_table_find_or_create_and_lock(table_id, src);
440         break;
441     case FIB_PROTOCOL_MPLS:
442     default:
443         return (~0);
444     }
445
446     mfib_table = mfib_table_get(fi, proto);
447
448     mfib_table->mft_desc = format(NULL, "%U-VRF:%d",
449                                   format_fib_protocol, proto,
450                                   table_id);
451
452     return (fi);
453 }
454
455 /**
456  * @brief Table flush context. Store the indicies of matching FIB entries
457  * that need to be removed.
458  */
459 typedef struct mfib_table_flush_ctx_t_
460 {
461     /**
462      * The list of entries to flush
463      */
464     fib_node_index_t *mftf_entries;
465
466     /**
467      * The source we are flushing
468      */
469     mfib_source_t mftf_source;
470 } mfib_table_flush_ctx_t;
471
472 static int
473 mfib_table_flush_cb (fib_node_index_t mfib_entry_index,
474                      void *arg)
475 {
476     mfib_table_flush_ctx_t *ctx = arg;
477
478     if (mfib_entry_is_sourced(mfib_entry_index, ctx->mftf_source))
479     {
480         vec_add1(ctx->mftf_entries, mfib_entry_index);
481     }
482     return (1);
483 }
484
485 void
486 mfib_table_flush (u32 mfib_index,
487                   fib_protocol_t proto,
488                   mfib_source_t source)
489 {
490     fib_node_index_t *mfib_entry_index;
491     mfib_table_flush_ctx_t ctx = {
492         .mftf_entries = NULL,
493         .mftf_source = source,
494     };
495
496     mfib_table_walk(mfib_index, proto,
497                     mfib_table_flush_cb,
498                     &ctx);
499
500     vec_foreach(mfib_entry_index, ctx.mftf_entries)
501     {
502         mfib_table_entry_delete_index(*mfib_entry_index, source);
503     }
504
505     vec_free(ctx.mftf_entries);
506 }
507
508 static void
509 mfib_table_destroy (mfib_table_t *mfib_table)
510 {
511     vec_free(mfib_table->mft_desc);
512
513     switch (mfib_table->mft_proto)
514     {
515     case FIB_PROTOCOL_IP4:
516         ip4_mfib_table_destroy(&mfib_table->v4);
517         break;
518     case FIB_PROTOCOL_IP6:
519         ip6_mfib_table_destroy(&mfib_table->v6);
520         break;
521     case FIB_PROTOCOL_MPLS:
522         ASSERT(0);
523         break;
524     }
525 }
526
527 void
528 mfib_table_unlock (u32 fib_index,
529                    fib_protocol_t proto,
530                    mfib_source_t source)
531 {
532     mfib_table_t *mfib_table;
533
534     mfib_table = mfib_table_get(fib_index, proto);
535     mfib_table->mft_locks[source]--;
536     mfib_table->mft_locks[MFIB_TABLE_TOTAL_LOCKS]--;
537
538     if (0 == mfib_table->mft_locks[source])
539     {
540         /*
541          * The source no longer needs the table. flush any routes
542          * from it just in case
543          */
544         mfib_table_flush(fib_index, proto, source);
545     }
546
547     if (0 == mfib_table->mft_locks[MFIB_TABLE_TOTAL_LOCKS])
548     {
549         /*
550          * no more locak from any source - kill it
551          */
552         mfib_table_destroy(mfib_table);
553     }
554 }
555
556 void
557 mfib_table_lock (u32 fib_index,
558                  fib_protocol_t proto,
559                  mfib_source_t source)
560 {
561     mfib_table_t *mfib_table;
562
563     mfib_table = mfib_table_get(fib_index, proto);
564     mfib_table->mft_locks[source]++;
565     mfib_table->mft_locks[MFIB_TABLE_TOTAL_LOCKS]++;
566 }
567
568 void
569 mfib_table_walk (u32 fib_index,
570                  fib_protocol_t proto,
571                  mfib_table_walk_fn_t fn,
572                  void *ctx)
573 {
574     switch (proto)
575     {
576     case FIB_PROTOCOL_IP4:
577         ip4_mfib_table_walk(ip4_mfib_get(fib_index), fn, ctx);
578         break;
579     case FIB_PROTOCOL_IP6:
580         ip6_mfib_table_walk(ip6_mfib_get(fib_index), fn, ctx);
581         break;
582     case FIB_PROTOCOL_MPLS:
583         break;
584     }
585 }
586
587 u8*
588 format_mfib_table_name (u8* s, va_list ap)
589 {
590     fib_node_index_t fib_index = va_arg(ap, fib_node_index_t);
591     fib_protocol_t proto = va_arg(ap, int); // int promotion
592     mfib_table_t *mfib_table;
593
594     mfib_table = mfib_table_get(fib_index, proto);
595
596     s = format(s, "%v", mfib_table->mft_desc);
597
598     return (s);
599 }
600
601 static clib_error_t *
602 mfib_module_init (vlib_main_t * vm)
603 {
604     clib_error_t * error;
605
606     if ((error = vlib_call_init_function (vm, fib_module_init)))
607         return (error);
608     if ((error = vlib_call_init_function (vm, rn_module_init)))
609         return (error);
610
611     mfib_entry_module_init();
612     mfib_signal_module_init();
613
614     return (error);
615 }
616
617 VLIB_INIT_FUNCTION(mfib_module_init);