ethernet: check destination mac for L3 in ethernet-input node
[vpp.git] / src / vnet / fib / fib_entry_src_interpose.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/ip/format.h>
18 #include <vnet/ip/lookup.h>
19 #include <vnet/adj/adj.h>
20 #include <vnet/dpo/drop_dpo.h>
21
22 #include "fib_entry_src.h"
23 #include "fib_entry_src_rr.h"
24 #include "fib_entry_cover.h"
25 #include "fib_entry.h"
26 #include "fib_table.h"
27
28 /*
29  * Source initialisation Function
30  */
31 static void
32 fib_entry_src_interpose_init (fib_entry_src_t *src)
33 {
34     src->u.interpose.fesi_cover = FIB_NODE_INDEX_INVALID;
35     src->u.interpose.fesi_sibling = FIB_NODE_INDEX_INVALID;
36 }
37
38 /*
39  * Source deinitialisation Function
40  */
41 static void
42 fib_entry_src_interpose_deinit (fib_entry_src_t *src)
43 {
44     ASSERT(src->u.interpose.fesi_cover == FIB_NODE_INDEX_INVALID);
45
46     src->u.interpose.fesi_cover = FIB_NODE_INDEX_INVALID;
47     src->u.interpose.fesi_sibling = FIB_NODE_INDEX_INVALID;
48
49     dpo_reset(&src->u.interpose.fesi_dpo);
50 }
51
52 static fib_entry_src_t *
53 fib_entry_src_rr_get_next_best (const fib_entry_src_t *src,
54                                 const fib_entry_t *fib_entry)
55 {
56     fib_entry_src_t *next_src, *best_src = NULL;
57     fib_source_t source;
58
59     FOR_EACH_SRC_ADDED(fib_entry, next_src, source,
60     ({
61         /*
62          * skip to the next best source after this one
63          */
64         switch (fib_source_cmp(source, src->fes_src))
65         {
66         case FIB_SOURCE_CMP_BETTER:
67         case FIB_SOURCE_CMP_EQUAL:
68             continue;
69         case FIB_SOURCE_CMP_WORSE:
70             best_src = next_src;
71             goto out;
72         }
73     }));
74  out:
75     return (best_src);
76 }
77
78 /*
79  * Source activation. Called when the source is the new best source on the entry
80  */
81 static int
82 fib_entry_src_interpose_activate (fib_entry_src_t *src,
83                                   const fib_entry_t *fib_entry)
84 {
85     fib_entry_src_t *best_src;
86     fib_node_index_t old_pl;
87     fib_entry_t *cover;
88
89     old_pl = src->fes_pl;
90     src->fes_pl = FIB_NODE_INDEX_INVALID;
91
92     /*
93      * The goal here is to find a path-list that will contribute forwarding
94      * for the entry.
95      * First check this entry for other sources that have a path-list
96      */
97     best_src = fib_entry_src_rr_get_next_best(src, fib_entry);
98
99     if (NULL != best_src)
100     {
101         const fib_entry_src_vft_t *vft;
102
103         best_src->fes_flags |= FIB_ENTRY_SRC_FLAG_CONTRIBUTING;
104         vft = fib_entry_src_get_vft(best_src);
105         /*
106          * there is another source for this entry. activate it so it
107          * can provide forwarding
108          */
109         if (NULL != vft->fesv_activate)
110         {
111             if (vft->fesv_activate(best_src, fib_entry))
112             {
113                 /*
114                  * next best source activated ok, use its path list
115                  */
116                 src->fes_pl = best_src->fes_pl;
117             }
118             else
119             {
120                 /*
121                  * the best source won't install so will use a drop
122                  */
123                 dpo_proto_t dproto;
124
125                 dproto = fib_proto_to_dpo(fib_entry->fe_prefix.fp_proto);
126
127                 src->fes_pl =
128                     fib_path_list_create_special(dproto,
129                                                  FIB_PATH_LIST_FLAG_DROP,
130                                                  drop_dpo_get(dproto));
131             }
132         }
133         else
134         {
135             /*
136              * next best source does not require activation, use its path list
137              */
138             src->fes_pl = best_src->fes_pl;
139         }
140     }
141     else
142     {
143         /*
144          * find the covering prefix. become a dependent thereof.
145          * for IP there should always be a cover, though it may be the default route.
146          * For MPLS there is never a cover.
147          */
148         if (FIB_PROTOCOL_MPLS == fib_entry->fe_prefix.fp_proto)
149         {
150             src->fes_pl = fib_path_list_create_special(DPO_PROTO_MPLS,
151                                                        FIB_PATH_LIST_FLAG_DROP,
152                                                        NULL);
153         }
154         else
155         {
156             src->u.interpose.fesi_cover =
157                 fib_table_get_less_specific(fib_entry->fe_fib_index,
158                                             &fib_entry->fe_prefix);
159
160             ASSERT(FIB_NODE_INDEX_INVALID != src->u.interpose.fesi_cover);
161
162             cover = fib_entry_get(src->u.interpose.fesi_cover);
163
164             src->u.interpose.fesi_sibling =
165                 fib_entry_cover_track(cover, fib_entry_get_index(fib_entry));
166
167             /*
168              * if the cover is attached then install an attached-host path
169              * (like an adj-fib). Otherwise inherit the forwarding from the cover
170              */
171             if (FIB_ENTRY_FLAG_ATTACHED & fib_entry_get_flags_i(cover))
172             {
173                 fib_entry_src_rr_resolve_via_connected(src, fib_entry, cover);
174             }
175             else
176             {
177                 fib_entry_src_rr_use_covers_pl(src, fib_entry, cover);
178             }
179         }
180     }
181
182     fib_path_list_unlock(old_pl);
183     fib_path_list_lock(src->fes_pl);
184
185     /*
186      * return go for install
187      */
188     return (!0);
189 }
190
191 /**
192  * Source Deactivate.
193  * Called when the source is no longer best source on the entry
194  */
195 static void
196 fib_entry_src_interpose_deactivate (fib_entry_src_t *src,
197                                     const fib_entry_t *fib_entry)
198 {
199     fib_entry_t *cover;
200
201     if (FIB_NODE_INDEX_INVALID != src->u.interpose.fesi_cover)
202     {
203         /*
204          * remove the dependency on the covering entry, if that's
205          * what was contributing the path-list
206          */
207         cover = fib_entry_get(src->u.interpose.fesi_cover);
208         fib_entry_cover_untrack(cover, src->u.interpose.fesi_sibling);
209         src->u.interpose.fesi_cover = FIB_NODE_INDEX_INVALID;
210     }
211     else
212     {
213         fib_entry_src_t *best_src;
214
215         best_src = fib_entry_src_rr_get_next_best(src, fib_entry);
216
217         if (best_src)
218         {
219             best_src->fes_flags &= ~FIB_ENTRY_SRC_FLAG_CONTRIBUTING;
220             /*
221              * there is another source for this entry. activate it so it
222              * can provide forwarding
223              */
224             FIB_ENTRY_SRC_VFT_INVOKE(fib_entry, best_src, fesv_deactivate,
225                                      (best_src, fib_entry));
226         }
227     }
228
229     fib_path_list_unlock(src->fes_pl);
230     src->fes_pl = FIB_NODE_INDEX_INVALID;
231     src->fes_entry_flags &= ~FIB_ENTRY_FLAGS_RR_INHERITED;
232 }
233
234 static int
235 fib_entry_src_interpose_reactivate (fib_entry_src_t *src,
236                                     const fib_entry_t *fib_entry)
237 {
238     fib_entry_src_interpose_deactivate(src, fib_entry);
239     return (fib_entry_src_interpose_activate(src, fib_entry));
240 }
241
242 static fib_entry_src_cover_res_t
243 fib_entry_src_interpose_cover_change (fib_entry_src_t *src,
244                                       const fib_entry_t *fib_entry)
245 {
246     fib_entry_src_cover_res_t res = {
247        .install = !0,
248        .bw_reason = FIB_NODE_BW_REASON_FLAG_NONE,
249     };
250
251     if (FIB_NODE_INDEX_INVALID == src->u.interpose.fesi_cover)
252     {
253        /*
254         * the source may be added, but it is not active
255         * if it is not tracking the cover.
256         */
257        return (res);
258     }
259
260     /*
261      * this function is called when this entry's cover has a more specific
262      * entry inserted beneath it. That does not necessarily mean that this
263      * entry is covered by the new prefix. check that
264      */
265     if (src->u.interpose.fesi_cover !=
266         fib_table_get_less_specific(fib_entry->fe_fib_index,
267                                     &fib_entry->fe_prefix))
268     {
269        fib_entry_src_interpose_deactivate(src, fib_entry);
270        fib_entry_src_interpose_activate(src, fib_entry);
271
272        /*
273         * dependent children need to re-resolve to the new forwarding info
274         */
275        res.bw_reason = FIB_NODE_BW_REASON_FLAG_EVALUATE;
276     }
277     return (res);
278 }
279
280 static void
281 fib_entry_src_interpose_add (fib_entry_src_t *src,
282                              const fib_entry_t *entry,
283                              fib_entry_flag_t flags,
284                              dpo_proto_t proto,
285                              const dpo_id_t *dpo)
286 {
287     dpo_copy(&src->u.interpose.fesi_dpo, dpo);
288 }
289
290 static void
291 fib_entry_src_interpose_remove (fib_entry_src_t *src)
292 {
293     dpo_reset(&src->u.interpose.fesi_dpo);
294 }
295
296 static void
297 fib_entry_src_interpose_set_data (fib_entry_src_t *src,
298                                   const fib_entry_t *fib_entry,
299                                   const void *data)
300 {
301     const dpo_id_t *dpo = data;
302
303     dpo_copy(&src->u.interpose.fesi_dpo, dpo);
304 }
305
306 /**
307  * Contribute forwarding to interpose in the chain
308  */
309 const dpo_id_t* fib_entry_src_interpose_contribute(const fib_entry_src_t *src,
310                                                    const fib_entry_t *fib_entry)
311 {
312     return (&src->u.interpose.fesi_dpo);
313 }
314
315 static void
316 fib_entry_src_interpose_copy (const fib_entry_src_t *orig_src,
317                               const fib_entry_t *fib_entry,
318                               fib_entry_src_t *copy_src)
319 {
320     copy_src->u.interpose.fesi_cover = orig_src->u.interpose.fesi_cover;
321
322     if (FIB_NODE_INDEX_INVALID != copy_src->u.interpose.fesi_cover)
323     {
324         fib_entry_t *cover;
325
326         cover = fib_entry_get(orig_src->u.interpose.fesi_cover);
327         copy_src->u.interpose.fesi_sibling =
328             fib_entry_cover_track(cover, fib_entry_get_index(fib_entry));
329     }
330
331     dpo_copy(&copy_src->u.interpose.fesi_dpo,
332              &orig_src->u.interpose.fesi_dpo);
333 }
334
335 static void
336 fib_entry_src_interpose_flag_change (fib_entry_src_t *src,
337                                      const fib_entry_t *fib_entry,
338                                      fib_entry_flag_t new_flags)
339 {
340     if (!(new_flags & FIB_ENTRY_FLAG_INTERPOSE))
341     {
342         /*
343          * stop tracking the source contributing forwarding
344          * and reset the interposer DPO
345          */
346         fib_entry_src_interpose_deactivate(src, fib_entry);
347         fib_entry_src_interpose_deinit(src);
348     }
349 }
350
351 static u8*
352 fib_entry_src_interpose_format (fib_entry_src_t *src,
353                                 u8* s)
354 {
355     s = format(s, " cover:%d interpose:\n%U%U",
356                src->u.interpose.fesi_cover,
357                format_white_space, 6,
358                format_dpo_id, &src->u.interpose.fesi_dpo, 8);
359
360     return (s);
361 }
362
363 const static fib_entry_src_vft_t interpose_src_vft = {
364     .fesv_init = fib_entry_src_interpose_init,
365     .fesv_deinit = fib_entry_src_interpose_deinit,
366     .fesv_activate = fib_entry_src_interpose_activate,
367     .fesv_reactivate = fib_entry_src_interpose_reactivate,
368     .fesv_deactivate = fib_entry_src_interpose_deactivate,
369     .fesv_cover_change = fib_entry_src_interpose_cover_change,
370     .fesv_cover_update = fib_entry_src_rr_cover_update,
371     .fesv_format = fib_entry_src_interpose_format,
372     .fesv_add = fib_entry_src_interpose_add,
373     .fesv_remove = fib_entry_src_interpose_remove,
374     .fesv_contribute_interpose = fib_entry_src_interpose_contribute,
375     .fesv_set_data = fib_entry_src_interpose_set_data,
376     .fesv_copy = fib_entry_src_interpose_copy,
377     .fesv_flags_change = fib_entry_src_interpose_flag_change,
378 };
379
380 void
381 fib_entry_src_interpose_register (void)
382 {
383     fib_entry_src_behaviour_register(FIB_SOURCE_BH_INTERPOSE,
384                                      &interpose_src_vft);
385 }