Adjacency Delegate updates
[vpp.git] / src / vnet / adj / adj_bfd.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/bfd/bfd_main.h>
17
18 #include <vnet/adj/adj_delegate.h>
19 #include <vnet/adj/adj_nbr.h>
20 #include <vnet/fib/fib_walk.h>
21
22 /**
23  * Distillation of the BFD session states into a go/no-go for using
24  * the associated tracked adjacency
25  */
26 typedef enum adj_bfd_state_t_
27 {
28     ADJ_BFD_STATE_DOWN,
29     ADJ_BFD_STATE_UP,
30 } adj_bfd_state_t;
31
32 /**
33  * BFD delegate daa
34  */
35 typedef struct adj_bfd_delegate_t_
36 {
37     /**
38      * Base class,linkage to the adjacency
39      */
40     adj_delegate_t abd_link;
41
42     /**
43      * BFD session state
44      */
45     adj_bfd_state_t abd_state;
46
47     /**
48      * BFD session index
49      */
50     u32 abd_index;
51 } adj_bfd_delegate_t;
52
53 /**
54  * Pool of delegates
55 */
56 static adj_bfd_delegate_t *abd_pool;
57
58 static inline adj_bfd_delegate_t*
59 adj_bfd_from_base (adj_delegate_t *ad)
60 {
61     if (NULL == ad)
62     {
63         return (NULL);
64     }
65     return ((adj_bfd_delegate_t*)((char*)ad -
66                                   STRUCT_OFFSET_OF(adj_bfd_delegate_t,
67                                                    abd_link)));
68 }
69
70 static inline const adj_bfd_delegate_t*
71 adj_bfd_from_const_base (const adj_delegate_t *ad)
72 {
73     if (NULL == ad)
74     {
75         return (NULL);
76     }
77     return ((adj_bfd_delegate_t*)((char*)ad -
78                                   STRUCT_OFFSET_OF(adj_bfd_delegate_t,
79                                                    abd_link)));
80 }
81
82 static adj_bfd_state_t
83 adj_bfd_bfd_state_to_fib (bfd_state_e bstate)
84 {
85     switch (bstate)
86     {
87     case BFD_STATE_up:
88         return (ADJ_BFD_STATE_UP);
89     case BFD_STATE_down:
90     case BFD_STATE_admin_down:
91     case BFD_STATE_init:
92         return (ADJ_BFD_STATE_DOWN);
93     }
94     return (ADJ_BFD_STATE_DOWN);
95 }
96
97 static void
98 adj_bfd_update_walk (adj_index_t ai)
99 {
100     /*
101      * initiate a backwalk of dependent children
102      * to notify of the state change of this adj.
103      */
104     fib_node_back_walk_ctx_t ctx = {
105         .fnbw_reason = FIB_NODE_BW_REASON_FLAG_ADJ_UPDATE,
106     };
107     fib_walk_sync(FIB_NODE_TYPE_ADJ, ai, &ctx);
108 }
109
110 /**
111  * @brief Callback function registered with BFD module to receive notifications
112  * of the CRUD of BFD sessions
113  * would be static but for the fact it's called from the unit-tests
114  */
115 void
116 adj_bfd_notify (bfd_listen_event_e event,
117                 const bfd_session_t *session)
118 {
119     const bfd_udp_key_t *key;
120     adj_bfd_delegate_t *abd;
121     fib_protocol_t fproto;
122     adj_delegate_t *aed;
123     adj_index_t ai;
124
125     if (BFD_HOP_TYPE_SINGLE != session->hop_type)
126     {
127         /*
128          * multi-hop BFD sessions attach directly to the FIB entry
129          * single-hop adj to the associate adjacency.
130          */
131         return;
132     }
133
134     key = &session->udp.key;
135
136     fproto = (ip46_address_is_ip4 (&key->peer_addr) ?
137               FIB_PROTOCOL_IP4:
138               FIB_PROTOCOL_IP6);
139
140     /*
141      * find the adj that corresponds to the BFD session.
142      */
143     ai = adj_nbr_add_or_lock(fproto,
144                              fib_proto_to_link(fproto),
145                              &key->peer_addr,
146                              key->sw_if_index);
147
148     switch (event)
149     {
150     case BFD_LISTEN_EVENT_CREATE:
151         /*
152          * The creation of a new session
153          */
154         if ((ADJ_INDEX_INVALID != ai) &&
155             (aed = adj_delegate_get(adj_get(ai),
156                                     ADJ_DELEGATE_BFD)))
157         {
158             /*
159              * already got state for this adj
160              */
161         }
162         else
163         {
164             /*
165              * lock the adj. add the delegate.
166              * Lockinging the adj prevents it being removed and thus maintains
167              * the BFD derived states
168              */
169             adj_lock(ai);
170
171             /*
172              * allocate and init a new delegate struct
173              */
174             pool_get(abd_pool, abd);
175
176             /*
177              * pretend the session is up and skip the walk.
178              * If we set it down then we get traffic loss on new children.
179              * if we walk then we lose traffic for existing children. Wait
180              * for the first BFD UP/DOWN before we let the session's state
181              * influence forwarding.
182              */
183             abd->abd_state = ADJ_BFD_STATE_UP;
184             abd->abd_index = session->bs_idx;
185
186             adj_delegate_add(adj_get(ai), ADJ_DELEGATE_BFD, &abd->abd_link);
187         }
188         break;
189
190     case BFD_LISTEN_EVENT_UPDATE:
191         /*
192          * state change up/dowm and
193          */
194         abd = adj_bfd_from_base(adj_delegate_get(adj_get(ai), ADJ_DELEGATE_BFD));
195
196         if (NULL != abd)
197         {
198             abd->abd_state = adj_bfd_bfd_state_to_fib(session->local_state);
199             adj_bfd_update_walk(ai);
200         }
201         /*
202          * else
203          *   not an adj with BFD state
204          */
205         break;
206
207     case BFD_LISTEN_EVENT_DELETE:
208         /*
209          * session has been removed.
210          */
211         abd = adj_bfd_from_base(adj_delegate_get(adj_get(ai), ADJ_DELEGATE_BFD));
212
213         if (NULL != abd)
214         {
215             /*
216              * has an associated BFD tracking delegate
217              * remove the BFD tracking deletgate, update children, then
218              * unlock the adj
219              */
220             adj_delegate_remove(ai, ADJ_DELEGATE_BFD);
221             pool_put(abd_pool, abd);
222
223             adj_bfd_update_walk(ai);
224             adj_unlock(ai);
225         }
226         /*
227          * else
228          *  no BFD associated state
229          */
230         break;
231     }
232
233     /*
234      * unlock match of the add-or-lock at the start
235      */
236     adj_unlock(ai);
237 }
238
239 int
240 adj_bfd_is_up (adj_index_t ai)
241 {
242     const adj_bfd_delegate_t *abd;
243
244     abd = adj_bfd_from_base(adj_delegate_get(adj_get(ai), ADJ_DELEGATE_BFD));
245
246     if (NULL == abd)
247     {
248         /*
249          * no BFD tracking - resolved
250          */
251         return (!0);
252     }
253     else
254     {
255         /*
256          * defer to the state of the BFD tracking
257          */
258         return (ADJ_BFD_STATE_UP == abd->abd_state);
259     }
260 }
261
262 /**
263  * Print a delegate that represents BFD tracking
264  */
265 static u8 *
266 adj_delegate_fmt_bfd (const adj_delegate_t *aed, u8 *s)
267 {
268     const adj_bfd_delegate_t *abd = adj_bfd_from_const_base(aed);
269
270     s = format(s, "BFD:[state:%d index:%d]",
271                abd->abd_state,
272                abd->abd_index);
273
274     return (s);
275 }
276
277 const static adj_delegate_vft_t adj_delegate_vft = {
278   .adv_format = adj_delegate_fmt_bfd,
279 };
280
281 static clib_error_t *
282 adj_bfd_main_init (vlib_main_t * vm)
283 {
284     clib_error_t * error = NULL;
285
286     if ((error = vlib_call_init_function (vm, bfd_main_init)))
287         return (error);
288
289     bfd_register_listener(adj_bfd_notify);
290
291     adj_delegate_register_type (ADJ_DELEGATE_BFD, &adj_delegate_vft);
292
293     return (error);
294 }
295
296 VLIB_INIT_FUNCTION (adj_bfd_main_init);