Line data Source code
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 : #define ADJ_BFD_STATES { \
33 : [ADJ_BFD_STATE_DOWN] = "down", \
34 : [ADJ_BFD_STATE_UP] = "up", \
35 : }
36 :
37 : static const char *adj_bfd_state_names[] = ADJ_BFD_STATES;
38 :
39 : /**
40 : * BFD delegate daa
41 : */
42 : typedef struct adj_bfd_delegate_t_
43 : {
44 : /**
45 : * BFD session state
46 : */
47 : adj_bfd_state_t abd_state;
48 :
49 : /**
50 : * BFD session index
51 : */
52 : u32 abd_index;
53 : } adj_bfd_delegate_t;
54 :
55 : /**
56 : * Pool of delegates
57 : */
58 : static adj_bfd_delegate_t *abd_pool;
59 :
60 : static inline adj_bfd_delegate_t*
61 13097 : adj_bfd_from_base (adj_delegate_t *ad)
62 : {
63 13097 : if (NULL != ad)
64 : {
65 466 : return (pool_elt_at_index(abd_pool, ad->ad_index));
66 : }
67 12631 : return (NULL);
68 : }
69 :
70 : static inline const adj_bfd_delegate_t*
71 0 : adj_bfd_from_const_base (const adj_delegate_t *ad)
72 : {
73 0 : if (NULL != ad)
74 : {
75 0 : return (pool_elt_at_index(abd_pool, ad->ad_index));
76 : }
77 0 : return (NULL);
78 : }
79 :
80 : static adj_bfd_state_t
81 130 : adj_bfd_bfd_state_to_fib (bfd_state_e bstate)
82 : {
83 130 : switch (bstate)
84 : {
85 76 : case BFD_STATE_up:
86 76 : return (ADJ_BFD_STATE_UP);
87 54 : case BFD_STATE_down:
88 : case BFD_STATE_admin_down:
89 : case BFD_STATE_init:
90 54 : return (ADJ_BFD_STATE_DOWN);
91 : }
92 0 : return (ADJ_BFD_STATE_DOWN);
93 : }
94 :
95 : static void
96 340 : adj_bfd_update_walk (adj_index_t ai)
97 : {
98 : /*
99 : * initiate a backwalk of dependent children
100 : * to notify of the state change of this adj.
101 : */
102 340 : fib_node_back_walk_ctx_t ctx = {
103 : .fnbw_reason = FIB_NODE_BW_REASON_FLAG_ADJ_UPDATE,
104 : };
105 340 : fib_walk_sync(FIB_NODE_TYPE_ADJ, ai, &ctx);
106 340 : }
107 :
108 : /**
109 : * @brief Callback function registered with BFD module to receive notifications
110 : * of the CRUD of BFD sessions
111 : * would be static but for the fact it's called from the unit-tests
112 : */
113 : void
114 341 : adj_bfd_notify (bfd_listen_event_e event,
115 : const bfd_session_t *session)
116 : {
117 : adj_bfd_delegate_t *abd;
118 : adj_delegate_t *aed;
119 : adj_index_t ai;
120 :
121 341 : if (BFD_HOP_TYPE_SINGLE != session->hop_type)
122 : {
123 : /*
124 : * multi-hop BFD sessions attach directly to the FIB entry
125 : * single-hop adj to the associate adjacency.
126 : */
127 0 : return;
128 : }
129 :
130 341 : switch (session->transport)
131 : {
132 341 : case BFD_TRANSPORT_UDP4:
133 : case BFD_TRANSPORT_UDP6:
134 : /*
135 : * pick up the same adjacency that the BFD session is using
136 : * to send. The BFD session is holding a lock on this adj.
137 : */
138 341 : ai = session->udp.adj_index;
139 341 : break;
140 0 : default:
141 : /*
142 : * Don't know what adj this session uses
143 : */
144 0 : return;
145 : }
146 :
147 341 : if (INDEX_INVALID == ai)
148 : {
149 : /* No associated Adjacency with the session */
150 0 : return;
151 : }
152 :
153 341 : switch (event)
154 : {
155 105 : case BFD_LISTEN_EVENT_CREATE:
156 : /*
157 : * The creation of a new session
158 : */
159 210 : if ((ADJ_INDEX_INVALID != ai) &&
160 105 : (aed = adj_delegate_get(adj_get(ai),
161 : ADJ_DELEGATE_BFD)))
162 : {
163 : /*
164 : * already got state for this adj
165 : */
166 : }
167 : else
168 : {
169 : /*
170 : * allocate and init a new delegate struct
171 : */
172 105 : pool_get(abd_pool, abd);
173 :
174 : /*
175 : * it would be best here if we could ignore this create and just
176 : * wait for the first update, but this is not possible because
177 : * BFD sessions are created in the down state, and can remain this
178 : * way without transitioning to another state if the peer is
179 : * unresponsive. So we have to assume down and wait for up.
180 : */
181 105 : abd->abd_state = ADJ_BFD_STATE_DOWN;
182 105 : abd->abd_index = session->bs_idx;
183 :
184 105 : adj_delegate_add(adj_get(ai), ADJ_DELEGATE_BFD, abd - abd_pool);
185 105 : adj_bfd_update_walk(ai);
186 : }
187 105 : break;
188 :
189 130 : case BFD_LISTEN_EVENT_UPDATE:
190 : /*
191 : * state change up/down and
192 : */
193 130 : abd = adj_bfd_from_base(adj_delegate_get(adj_get(ai), ADJ_DELEGATE_BFD));
194 :
195 130 : if (NULL != abd)
196 : {
197 130 : abd->abd_state = adj_bfd_bfd_state_to_fib(session->local_state);
198 130 : adj_bfd_update_walk(ai);
199 : }
200 : /*
201 : * else
202 : * not an adj with BFD state
203 : */
204 130 : break;
205 :
206 106 : case BFD_LISTEN_EVENT_DELETE:
207 : /*
208 : * session has been removed.
209 : */
210 106 : abd = adj_bfd_from_base(adj_delegate_get(adj_get(ai), ADJ_DELEGATE_BFD));
211 :
212 106 : if (NULL != abd)
213 : {
214 : /*
215 : * has an associated BFD tracking delegate
216 : * remove the BFD tracking delegate, update children
217 : */
218 105 : adj_delegate_remove(ai, ADJ_DELEGATE_BFD);
219 105 : pool_put(abd_pool, abd);
220 :
221 105 : adj_bfd_update_walk(ai);
222 : }
223 : /*
224 : * else
225 : * no BFD associated state
226 : */
227 106 : break;
228 : }
229 341 : }
230 :
231 : int
232 12861 : adj_bfd_is_up (adj_index_t ai)
233 : {
234 : const adj_bfd_delegate_t *abd;
235 :
236 12861 : abd = adj_bfd_from_base(adj_delegate_get(adj_get(ai), ADJ_DELEGATE_BFD));
237 :
238 12861 : if (NULL == abd)
239 : {
240 : /*
241 : * no BFD tracking - resolved
242 : */
243 12630 : return (!0);
244 : }
245 : else
246 : {
247 : /*
248 : * defer to the state of the BFD tracking
249 : */
250 231 : return (ADJ_BFD_STATE_UP == abd->abd_state);
251 : }
252 : }
253 :
254 : /**
255 : * Print a delegate that represents BFD tracking
256 : */
257 : static u8 *
258 0 : adj_delegate_fmt_bfd (const adj_delegate_t *aed, u8 *s)
259 : {
260 0 : const adj_bfd_delegate_t *abd = adj_bfd_from_const_base(aed);
261 :
262 0 : s = format(s, "BFD:[state:%s index:%d]",
263 0 : adj_bfd_state_names[abd->abd_state],
264 : abd->abd_index);
265 :
266 0 : return (s);
267 : }
268 :
269 : const static adj_delegate_vft_t adj_delegate_vft = {
270 : .adv_format = adj_delegate_fmt_bfd,
271 : };
272 :
273 : static clib_error_t *
274 559 : adj_bfd_main_init (vlib_main_t * vm)
275 : {
276 559 : bfd_register_listener(adj_bfd_notify);
277 :
278 559 : adj_delegate_register_type (ADJ_DELEGATE_BFD, &adj_delegate_vft);
279 :
280 559 : return (0);
281 : }
282 :
283 : /* *INDENT-OFF* */
284 82879 : VLIB_INIT_FUNCTION (adj_bfd_main_init)=
285 : {
286 : .runs_after = VLIB_INITS("bfd_main_init"),
287 : };
288 : /* *INDENT-ON* */
|