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