vcl: allow more rx events on peek
[vpp.git] / src / vnet / adj / adj_glean.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 <vnet/adj/adj.h>
17 #include <vnet/adj/adj_internal.h>
18 #include <vnet/fib/fib_walk.h>
19
20 /*
21  * The 'DB' of all glean adjs.
22  * There is one glean per-{interface, protocol, connected prefix}
23  */
24 static uword **adj_gleans[FIB_PROTOCOL_IP_MAX];
25
26 static inline u32
27 adj_get_glean_node (fib_protocol_t proto)
28 {
29     switch (proto) {
30     case FIB_PROTOCOL_IP4:
31         return (ip4_glean_node.index);
32     case FIB_PROTOCOL_IP6:
33         return (ip6_glean_node.index);
34     case FIB_PROTOCOL_MPLS:
35         break;
36     }
37     ASSERT(0);
38     return (~0);
39 }
40
41 static adj_index_t
42 adj_glean_db_lookup (fib_protocol_t proto,
43                      u32 sw_if_index,
44                      const ip46_address_t *nh_addr)
45 {
46     uword *p;
47
48     if ((proto >= FIB_PROTOCOL_IP_MAX) || vec_len(adj_gleans[proto]) <= sw_if_index)
49         return (ADJ_INDEX_INVALID);
50
51     p = hash_get_mem (adj_gleans[proto][sw_if_index], nh_addr);
52
53     if (p)
54         return (p[0]);
55
56     return (ADJ_INDEX_INVALID);
57 }
58
59 static void
60 adj_glean_db_insert (fib_protocol_t proto,
61                      u32 sw_if_index,
62                      const ip46_address_t *nh_addr,
63                      adj_index_t ai)
64 {
65     vlib_main_t *vm = vlib_get_main();
66
67     vlib_worker_thread_barrier_sync(vm);
68
69     ASSERT(proto < FIB_PROTOCOL_IP_MAX);
70     vec_validate(adj_gleans[proto], sw_if_index);
71
72     if (NULL == adj_gleans[proto][sw_if_index])
73     {
74         adj_gleans[proto][sw_if_index] =
75             hash_create_mem (0, sizeof(ip46_address_t), sizeof(adj_index_t));
76     }
77
78     hash_set_mem_alloc (&adj_gleans[proto][sw_if_index],
79                         nh_addr, ai);
80
81     vlib_worker_thread_barrier_release(vm);
82 }
83
84 static void
85 adj_glean_db_remove (fib_protocol_t proto,
86                      u32 sw_if_index,
87                      const ip46_address_t *nh_addr)
88 {
89     vlib_main_t *vm = vlib_get_main();
90
91     vlib_worker_thread_barrier_sync(vm);
92
93     ASSERT(ADJ_INDEX_INVALID != adj_glean_db_lookup(proto, sw_if_index, nh_addr));
94     hash_unset_mem_free (&adj_gleans[proto][sw_if_index],
95                          nh_addr);
96
97     if (0 == hash_elts(adj_gleans[proto][sw_if_index]))
98     {
99         hash_free(adj_gleans[proto][sw_if_index]);
100         adj_gleans[proto][sw_if_index] = NULL;
101     }
102     vlib_worker_thread_barrier_release(vm);
103 }
104
105 /*
106  * adj_glean_add_or_lock
107  *
108  * The next_hop address here is used for source address selection in the DP.
109  * The glean adj is added to an interface's connected prefix, the next-hop
110  * passed here is the local prefix on the same interface.
111  */
112 adj_index_t
113 adj_glean_add_or_lock (fib_protocol_t proto,
114                        vnet_link_t linkt,
115                        u32 sw_if_index,
116                        const fib_prefix_t *conn)
117 {
118     ip_adjacency_t * adj;
119     adj_index_t ai;
120
121     ai = adj_glean_db_lookup(proto, sw_if_index, &conn->fp_addr);
122
123     if (ADJ_INDEX_INVALID == ai)
124     {
125         adj = adj_alloc(proto);
126
127         adj->lookup_next_index = IP_LOOKUP_NEXT_GLEAN;
128         adj->ia_nh_proto = proto;
129         adj->ia_link = linkt;
130         adj->ia_node_index = adj_get_glean_node(proto);
131         ai = adj_get_index(adj);
132         adj_lock(ai);
133
134         ASSERT(conn);
135         fib_prefix_normalize(conn, &adj->sub_type.glean.rx_pfx);
136         adj->rewrite_header.sw_if_index = sw_if_index;
137         adj->rewrite_header.data_bytes = 0;
138         adj->rewrite_header.max_l3_packet_bytes =
139           vnet_sw_interface_get_mtu(vnet_get_main(), sw_if_index,
140                                     vnet_link_to_mtu(linkt));
141
142         vnet_update_adjacency_for_sw_interface(vnet_get_main(),
143                                                sw_if_index,
144                                                ai);
145
146         adj_glean_db_insert(proto, sw_if_index,
147                             &adj->sub_type.glean.rx_pfx.fp_addr, ai);
148     }
149     else
150     {
151         adj = adj_get(ai);
152         adj_lock(ai);
153     }
154
155     adj_delegate_adj_created(adj);
156
157     return (ai);
158 }
159
160 /**
161  * adj_glean_update_rewrite
162  */
163 void
164 adj_glean_update_rewrite (adj_index_t adj_index)
165 {
166     ip_adjacency_t *adj;
167
168     ASSERT(ADJ_INDEX_INVALID != adj_index);
169
170     adj = adj_get(adj_index);
171
172     vnet_rewrite_for_sw_interface(vnet_get_main(),
173                                   adj_fib_proto_2_nd(adj->ia_nh_proto),
174                                   adj->rewrite_header.sw_if_index,
175                                   adj->ia_node_index,
176                                   VNET_REWRITE_FOR_SW_INTERFACE_ADDRESS_BROADCAST,
177                                   &adj->rewrite_header,
178                                   sizeof (adj->rewrite_data));
179 }
180
181 static adj_walk_rc_t
182 adj_glean_update_rewrite_walk (adj_index_t ai,
183                                void *data)
184 {
185     adj_glean_update_rewrite(ai);
186
187     return (ADJ_WALK_RC_CONTINUE);
188 }
189
190 static void
191 adj_glean_walk_proto (fib_protocol_t proto,
192                       u32 sw_if_index,
193                       adj_walk_cb_t cb,
194                       void *data)
195 {
196     adj_index_t ai, *aip, *ais = NULL;
197     ip46_address_t *conn;
198
199     ASSERT(proto < FIB_PROTOCOL_IP_MAX);
200     if (vec_len(adj_gleans[proto]) <= sw_if_index ||
201         NULL == adj_gleans[proto][sw_if_index])
202         return;
203
204     /*
205      * Walk first to collect the indices
206      * then walk the collection. This is safe
207      * to modifications of the hash table
208      */
209     hash_foreach_mem(conn, ai, adj_gleans[proto][sw_if_index],
210     ({
211         vec_add1(ais, ai);
212     }));
213
214     vec_foreach(aip, ais)
215     {
216         if (ADJ_WALK_RC_STOP == cb(*aip, data))
217             break;
218     }
219     vec_free(ais);
220 }
221
222 void
223 adj_glean_walk (u32 sw_if_index,
224                 adj_walk_cb_t cb,
225                 void *data)
226 {
227     fib_protocol_t proto;
228
229     FOR_EACH_FIB_IP_PROTOCOL(proto)
230     {
231       adj_glean_walk_proto (proto, sw_if_index, cb, data);
232     }
233 }
234
235 adj_index_t
236 adj_glean_get (fib_protocol_t proto,
237                u32 sw_if_index,
238                const ip46_address_t *nh)
239 {
240     if (NULL != nh)
241     {
242         return adj_glean_db_lookup(proto, sw_if_index, nh);
243     }
244     else
245     {
246         ip46_address_t *conn;
247         adj_index_t ai;
248
249         ASSERT(proto < FIB_PROTOCOL_IP_MAX);
250         if (vec_len(adj_gleans[proto]) <= sw_if_index ||
251             NULL == adj_gleans[proto][sw_if_index])
252             return (ADJ_INDEX_INVALID);
253
254         hash_foreach_mem(conn, ai, adj_gleans[proto][sw_if_index],
255         ({
256             return (ai);
257         }));
258     }
259     return (ADJ_INDEX_INVALID);
260 }
261
262 const ip46_address_t *
263 adj_glean_get_src (fib_protocol_t proto,
264                    u32 sw_if_index,
265                    const ip46_address_t *nh)
266 {
267     const ip46_address_t *conn, *source;
268     const ip_adjacency_t *adj;
269     adj_index_t ai;
270
271     ASSERT(proto < FIB_PROTOCOL_IP_MAX);
272     if (vec_len(adj_gleans[proto]) <= sw_if_index ||
273         NULL == adj_gleans[proto][sw_if_index])
274         return (NULL);
275
276     fib_prefix_t pfx = {
277         .fp_len = fib_prefix_get_host_length(proto),
278         .fp_proto = proto,
279     };
280
281     if (nh)
282         pfx.fp_addr = *nh;
283
284     /*
285      * An interface can have more than one glean address. Where
286      * possible we want to return a source address from the same
287      * subnet as the destination. If this is not possible then any address
288      * will do.
289      */
290     source = NULL;
291
292     hash_foreach_mem(conn, ai, adj_gleans[proto][sw_if_index],
293     ({
294         adj = adj_get(ai);
295
296         if (adj->sub_type.glean.rx_pfx.fp_len > 0)
297         {
298             source = &adj->sub_type.glean.rx_pfx.fp_addr;
299
300             /* if no destination is specified use the just glean */
301             if (NULL == nh)
302                 return (source);
303
304             /* check the clean covers the desintation */
305             if (fib_prefix_is_cover(&adj->sub_type.glean.rx_pfx, &pfx))
306                 return (source);
307         }
308     }));
309
310     return (source);
311 }
312
313 void
314 adj_glean_remove (ip_adjacency_t *adj)
315 {
316     fib_prefix_t norm;
317
318     fib_prefix_normalize(&adj->sub_type.glean.rx_pfx,
319                          &norm);
320     adj_glean_db_remove(adj->ia_nh_proto,
321                         adj->rewrite_header.sw_if_index,
322                         &norm.fp_addr);
323 }
324
325 static adj_walk_rc_t
326 adj_glean_start_backwalk (adj_index_t ai,
327                           void *data)
328 {
329     fib_node_back_walk_ctx_t bw_ctx = *(fib_node_back_walk_ctx_t*) data;
330
331     fib_walk_sync(FIB_NODE_TYPE_ADJ, ai, &bw_ctx);
332
333     return (ADJ_WALK_RC_CONTINUE);
334 }
335
336 static clib_error_t *
337 adj_glean_interface_state_change (vnet_main_t * vnm,
338                                   u32 sw_if_index,
339                                   u32 flags)
340 {
341     /*
342      * for each glean on the interface trigger a walk back to the children
343      */
344     fib_node_back_walk_ctx_t bw_ctx = {
345         .fnbw_reason = (flags & VNET_SW_INTERFACE_FLAG_ADMIN_UP ?
346                         FIB_NODE_BW_REASON_FLAG_INTERFACE_UP :
347                         FIB_NODE_BW_REASON_FLAG_INTERFACE_DOWN),
348     };
349
350     adj_glean_walk (sw_if_index, adj_glean_start_backwalk, &bw_ctx);
351
352     return (NULL);
353 }
354
355 VNET_SW_INTERFACE_ADMIN_UP_DOWN_FUNCTION(adj_glean_interface_state_change);
356
357 /**
358  * @brief Invoked on each SW interface of a HW interface when the
359  * HW interface state changes
360  */
361 static walk_rc_t
362 adj_nbr_hw_sw_interface_state_change (vnet_main_t * vnm,
363                                       u32 sw_if_index,
364                                       void *arg)
365 {
366     adj_glean_interface_state_change(vnm, sw_if_index, (uword) arg);
367
368     return (WALK_CONTINUE);
369 }
370
371 /**
372  * @brief Registered callback for HW interface state changes
373  */
374 static clib_error_t *
375 adj_glean_hw_interface_state_change (vnet_main_t * vnm,
376                                      u32 hw_if_index,
377                                      u32 flags)
378 {
379     /*
380      * walk SW interfaces on the HW
381      */
382     uword sw_flags;
383
384     sw_flags = ((flags & VNET_HW_INTERFACE_FLAG_LINK_UP) ?
385                 VNET_SW_INTERFACE_FLAG_ADMIN_UP :
386                 0);
387
388     vnet_hw_interface_walk_sw(vnm, hw_if_index,
389                               adj_nbr_hw_sw_interface_state_change,
390                               (void*) sw_flags);
391
392     return (NULL);
393 }
394
395 VNET_HW_INTERFACE_LINK_UP_DOWN_FUNCTION(
396     adj_glean_hw_interface_state_change);
397
398 static clib_error_t *
399 adj_glean_interface_delete (vnet_main_t * vnm,
400                             u32 sw_if_index,
401                             u32 is_add)
402 {
403     if (is_add)
404     {
405         /*
406          * not interested in interface additions. we will not back walk
407          * to resolve paths through newly added interfaces. Why? The control
408          * plane should have the brains to add interfaces first, then routes.
409          * So the case where there are paths with a interface that matches
410          * one just created is the case where the path resolved through an
411          * interface that was deleted, and still has not been removed. The
412          * new interface added, is NO GUARANTEE that the interface being
413          * added now, even though it may have the same sw_if_index, is the
414          * same interface that the path needs. So tough!
415          * If the control plane wants these routes to resolve it needs to
416          * remove and add them again.
417          */
418         return (NULL);
419     }
420
421     /*
422      * for each glean on the interface trigger a walk back to the children
423      */
424     fib_node_back_walk_ctx_t bw_ctx = {
425         .fnbw_reason =  FIB_NODE_BW_REASON_FLAG_INTERFACE_DELETE,
426     };
427
428     adj_glean_walk (sw_if_index, adj_glean_start_backwalk, &bw_ctx);
429
430     return (NULL);
431 }
432
433 VNET_SW_INTERFACE_ADD_DEL_FUNCTION(adj_glean_interface_delete);
434
435 /**
436  * Callback function invoked when an interface's MAC Address changes
437  */
438 static void
439 adj_glean_ethernet_change_mac (ethernet_main_t * em,
440                                u32 sw_if_index,
441                                uword opaque)
442 {
443     adj_glean_walk (sw_if_index, adj_glean_update_rewrite_walk, NULL);
444 }
445
446 static void
447 adj_glean_table_bind (fib_protocol_t fproto,
448                       u32 sw_if_index,
449                       u32 itf_fib_index)
450 {
451     /*
452      * for each glean on the interface trigger a walk back to the children
453      */
454     fib_node_back_walk_ctx_t bw_ctx = {
455         .fnbw_reason =  FIB_NODE_BW_REASON_FLAG_INTERFACE_BIND,
456         .interface_bind = {
457             .fnbw_to_fib_index = itf_fib_index,
458         },
459     };
460
461     adj_glean_walk_proto (fproto, sw_if_index, adj_glean_start_backwalk, &bw_ctx);
462 }
463
464
465 /**
466  * Callback function invoked when an interface's IPv6 Table
467  * binding changes
468  */
469 static void
470 adj_glean_ip6_table_bind (ip6_main_t * im,
471                           uword opaque,
472                           u32 sw_if_index,
473                           u32 new_fib_index,
474                           u32 old_fib_index)
475 {
476   adj_glean_table_bind (FIB_PROTOCOL_IP6, sw_if_index, new_fib_index);
477 }
478
479 /**
480  * Callback function invoked when an interface's IPv4 Table
481  * binding changes
482  */
483 static void
484 adj_glean_ip4_table_bind (ip4_main_t * im,
485                           uword opaque,
486                           u32 sw_if_index,
487                           u32 new_fib_index,
488                           u32 old_fib_index)
489 {
490   adj_glean_table_bind (FIB_PROTOCOL_IP4, sw_if_index, new_fib_index);
491 }
492
493 u8*
494 format_adj_glean (u8* s, va_list *ap)
495 {
496     index_t index = va_arg(*ap, index_t);
497     CLIB_UNUSED(u32 indent) = va_arg(*ap, u32);
498     ip_adjacency_t * adj = adj_get(index);
499
500     s = format(s, "%U-glean: [src:%U] %U",
501                format_fib_protocol, adj->ia_nh_proto,
502                format_fib_prefix, &adj->sub_type.glean.rx_pfx,
503                format_vnet_rewrite,
504                &adj->rewrite_header, sizeof (adj->rewrite_data), 0);
505
506     return (s);
507 }
508
509 u32
510 adj_glean_db_size (void)
511 {
512     fib_protocol_t proto;
513     u32 sw_if_index = 0;
514     u64 count = 0;
515
516     FOR_EACH_FIB_IP_PROTOCOL(proto)
517     {
518         vec_foreach_index(sw_if_index, adj_gleans[proto])
519         {
520             if (NULL != adj_gleans[proto][sw_if_index])
521             {
522                 count += hash_elts(adj_gleans[proto][sw_if_index]);
523             }
524         }
525     }
526     return (count);
527 }
528
529 static void
530 adj_dpo_lock (dpo_id_t *dpo)
531 {
532     adj_lock(dpo->dpoi_index);
533 }
534 static void
535 adj_dpo_unlock (dpo_id_t *dpo)
536 {
537     adj_unlock(dpo->dpoi_index);
538 }
539
540 const static dpo_vft_t adj_glean_dpo_vft = {
541     .dv_lock = adj_dpo_lock,
542     .dv_unlock = adj_dpo_unlock,
543     .dv_format = format_adj_glean,
544     .dv_get_urpf = adj_dpo_get_urpf,
545     .dv_get_mtu = adj_dpo_get_mtu,
546 };
547
548 /**
549  * @brief The per-protocol VLIB graph nodes that are assigned to a glean
550  *        object.
551  *
552  * this means that these graph nodes are ones from which a glean is the
553  * parent object in the DPO-graph.
554  */
555 const static char* const glean_ip4_nodes[] =
556 {
557     "ip4-glean",
558     NULL,
559 };
560 const static char* const glean_ip6_nodes[] =
561 {
562     "ip6-glean",
563     NULL,
564 };
565
566 const static char* const * const glean_nodes[DPO_PROTO_NUM] =
567 {
568     [DPO_PROTO_IP4]  = glean_ip4_nodes,
569     [DPO_PROTO_IP6]  = glean_ip6_nodes,
570     [DPO_PROTO_MPLS] = NULL,
571 };
572
573 void
574 adj_glean_module_init (void)
575 {
576     dpo_register(DPO_ADJACENCY_GLEAN, &adj_glean_dpo_vft, glean_nodes);
577
578     ethernet_address_change_ctx_t ctx = {
579         .function = adj_glean_ethernet_change_mac,
580         .function_opaque = 0,
581     };
582     vec_add1 (ethernet_main.address_change_callbacks, ctx);
583
584     ip6_table_bind_callback_t cbt6 = {
585         .function = adj_glean_ip6_table_bind,
586     };
587     vec_add1 (ip6_main.table_bind_callbacks, cbt6);
588
589     ip4_table_bind_callback_t cbt4 = {
590         .function = adj_glean_ip4_table_bind,
591     };
592     vec_add1 (ip4_main.table_bind_callbacks, cbt4);
593 }