Revert "Adj: VFTs for adjacency sub-blocks"
[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 static adj_bfd_state_t
23 adj_bfd_bfd_state_to_fib (bfd_state_e bstate)
24 {
25     switch (bstate)
26     {
27     case BFD_STATE_up:
28         return (ADJ_BFD_STATE_UP);
29     case BFD_STATE_down:
30     case BFD_STATE_admin_down:
31     case BFD_STATE_init:
32         return (ADJ_BFD_STATE_DOWN);
33     }
34     return (ADJ_BFD_STATE_DOWN);
35 }
36
37 static void
38 adj_bfd_update_walk (adj_index_t ai)
39 {
40     /*
41      * initiate a backwalk of dependent children
42      * to notify of the state change of this adj.
43      */
44     fib_node_back_walk_ctx_t ctx = {
45         .fnbw_reason = FIB_NODE_BW_REASON_FLAG_ADJ_UPDATE,
46     };
47     fib_walk_sync(FIB_NODE_TYPE_ADJ, ai, &ctx);
48 }
49
50 /**
51  * @brief Callback function registered with BFD module to receive notifications
52  * of the CRUD of BFD sessions
53  * would be static but for the fact it's called from the unit-tests
54  */
55 void
56 adj_bfd_notify (bfd_listen_event_e event,
57                 const bfd_session_t *session)
58 {
59     const bfd_udp_key_t *key;
60     fib_protocol_t fproto;
61     adj_delegate_t *aed;
62     adj_index_t ai;
63
64     if (BFD_HOP_TYPE_SINGLE != session->hop_type)
65     {
66         /*
67          * multi-hop BFD sessions attach directly to the FIB entry
68          * single-hop adj to the associate adjacency.
69          */
70         return;
71     }
72
73     key = &session->udp.key;
74
75     fproto = (ip46_address_is_ip4 (&key->peer_addr) ?
76               FIB_PROTOCOL_IP4:
77               FIB_PROTOCOL_IP6);
78
79     /*
80      * find the adj that corresponds to the BFD session.
81      */
82     ai = adj_nbr_add_or_lock(fproto,
83                              fib_proto_to_link(fproto),
84                              &key->peer_addr,
85                              key->sw_if_index);
86
87     switch (event)
88     {
89     case BFD_LISTEN_EVENT_CREATE:
90         /*
91          * The creation of a new session
92          */
93         if ((ADJ_INDEX_INVALID != ai) &&
94             (aed = adj_delegate_get(adj_get(ai),
95                                     ADJ_DELEGATE_BFD)))
96         {
97             /*
98              * already got state for this adj
99              */
100         }
101         else
102         {
103             /*
104              * lock the adj. add the delegate.
105              * Lockinging the adj prevents it being removed and thus maintains
106              * the BFD derived states
107              */
108             adj_lock(ai);
109
110             aed = adj_delegate_find_or_add(adj_get(ai), ADJ_DELEGATE_BFD);
111
112             /*
113              * pretend the session is up and skip the walk.
114              * If we set it down then we get traffic loss on new children.
115              * if we walk then we lose traffic for existing children. Wait
116              * for the first BFD UP/DOWN before we let the session's state
117              * influence forwarding.
118              */
119             aed->ad_bfd_state = ADJ_BFD_STATE_UP;
120             aed->ad_bfd_index = session->bs_idx;
121         }
122         break;
123
124     case BFD_LISTEN_EVENT_UPDATE:
125         /*
126          * state change up/dowm and
127          */
128         aed = adj_delegate_get(adj_get(ai), ADJ_DELEGATE_BFD);
129
130         if (NULL != aed)
131         {
132             aed->ad_bfd_state = adj_bfd_bfd_state_to_fib(session->local_state);
133             adj_bfd_update_walk(ai);
134         }
135         /*
136          * else
137          *   not an adj with BFD state
138          */
139         break;
140
141     case BFD_LISTEN_EVENT_DELETE:
142         /*
143          * session has been removed.
144          */
145
146         if (adj_delegate_get(adj_get(ai), ADJ_DELEGATE_BFD))
147         {
148             /*
149              * has an associated BFD tracking delegate
150              * remove the BFD tracking deletgate, update children, then
151              * unlock the adj
152              */
153             adj_delegate_remove(adj_get(ai), ADJ_DELEGATE_BFD);
154
155             adj_bfd_update_walk(ai);
156             adj_unlock(ai);
157         }
158         /*
159          * else
160          *  no BFD associated state
161          */
162         break;
163     }
164
165     /*
166      * unlock match of the add-or-lock at the start
167      */
168     adj_unlock(ai);
169 }
170
171 static clib_error_t *
172 adj_bfd_main_init (vlib_main_t * vm)
173 {
174     clib_error_t * error = NULL;
175
176     if ((error = vlib_call_init_function (vm, bfd_main_init)))
177         return (error);
178
179     bfd_register_listener(adj_bfd_notify);
180
181     return (error);
182 }
183
184 VLIB_INIT_FUNCTION (adj_bfd_main_init);