blob: a4e7e277298dca60101024c07dd87bb1d5a57a71 [file] [log] [blame]
Neale Ranns88fc83e2017-04-05 08:11:14 -07001/*
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
Neale Rannsd79a43c2018-02-19 02:36:19 -080022/**
23 * Distillation of the BFD session states into a go/no-go for using
24 * the associated tracked adjacency
25 */
26typedef 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 */
35typedef 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*/
56static adj_bfd_delegate_t *abd_pool;
57
58static inline adj_bfd_delegate_t*
59adj_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
70static inline const adj_bfd_delegate_t*
71adj_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
Neale Ranns88fc83e2017-04-05 08:11:14 -070082static adj_bfd_state_t
83adj_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
97static void
98adj_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 */
115void
116adj_bfd_notify (bfd_listen_event_e event,
117 const bfd_session_t *session)
118{
119 const bfd_udp_key_t *key;
Neale Rannsd79a43c2018-02-19 02:36:19 -0800120 adj_bfd_delegate_t *abd;
Neale Ranns88fc83e2017-04-05 08:11:14 -0700121 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
Neale Rannsd79a43c2018-02-19 02:36:19 -0800171 /*
172 * allocate and init a new delegate struct
173 */
174 pool_get(abd_pool, abd);
Neale Ranns88fc83e2017-04-05 08:11:14 -0700175
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 */
Neale Rannsd79a43c2018-02-19 02:36:19 -0800183 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);
Neale Ranns88fc83e2017-04-05 08:11:14 -0700187 }
188 break;
189
190 case BFD_LISTEN_EVENT_UPDATE:
191 /*
192 * state change up/dowm and
193 */
Neale Rannsd79a43c2018-02-19 02:36:19 -0800194 abd = adj_bfd_from_base(adj_delegate_get(adj_get(ai), ADJ_DELEGATE_BFD));
Neale Ranns88fc83e2017-04-05 08:11:14 -0700195
Neale Rannsd79a43c2018-02-19 02:36:19 -0800196 if (NULL != abd)
Neale Ranns88fc83e2017-04-05 08:11:14 -0700197 {
Neale Rannsd79a43c2018-02-19 02:36:19 -0800198 abd->abd_state = adj_bfd_bfd_state_to_fib(session->local_state);
Neale Ranns88fc83e2017-04-05 08:11:14 -0700199 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 */
Neale Rannsd79a43c2018-02-19 02:36:19 -0800211 abd = adj_bfd_from_base(adj_delegate_get(adj_get(ai), ADJ_DELEGATE_BFD));
Neale Ranns88fc83e2017-04-05 08:11:14 -0700212
Neale Rannsd79a43c2018-02-19 02:36:19 -0800213 if (NULL != abd)
Neale Ranns88fc83e2017-04-05 08:11:14 -0700214 {
215 /*
216 * has an associated BFD tracking delegate
217 * remove the BFD tracking deletgate, update children, then
218 * unlock the adj
219 */
Neale Rannsd79a43c2018-02-19 02:36:19 -0800220 adj_delegate_remove(ai, ADJ_DELEGATE_BFD);
221 pool_put(abd_pool, abd);
Neale Ranns88fc83e2017-04-05 08:11:14 -0700222
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
Neale Rannsd79a43c2018-02-19 02:36:19 -0800239int
240adj_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
Ole Troan793c7fe2018-02-15 16:14:56 +0100262/**
263 * Print a delegate that represents BFD tracking
264 */
265static u8 *
266adj_delegate_fmt_bfd (const adj_delegate_t *aed, u8 *s)
267{
Neale Rannsd79a43c2018-02-19 02:36:19 -0800268 const adj_bfd_delegate_t *abd = adj_bfd_from_const_base(aed);
269
Ole Troan793c7fe2018-02-15 16:14:56 +0100270 s = format(s, "BFD:[state:%d index:%d]",
Neale Rannsd79a43c2018-02-19 02:36:19 -0800271 abd->abd_state,
272 abd->abd_index);
Ole Troan793c7fe2018-02-15 16:14:56 +0100273
274 return (s);
275}
276
277const static adj_delegate_vft_t adj_delegate_vft = {
278 .adv_format = adj_delegate_fmt_bfd,
279};
280
Neale Ranns88fc83e2017-04-05 08:11:14 -0700281static clib_error_t *
282adj_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
Ole Troan793c7fe2018-02-15 16:14:56 +0100291 adj_delegate_register_type (ADJ_DELEGATE_BFD, &adj_delegate_vft);
292
Neale Ranns88fc83e2017-04-05 08:11:14 -0700293 return (error);
294}
295
296VLIB_INIT_FUNCTION (adj_bfd_main_init);