BFD-FIB interactions
[vpp.git] / src / vnet / fib / fib_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/fib/fib_entry_delegate.h>
19 #include <vnet/fib/fib_entry.h>
20 #include <vnet/fib/fib_table.h>
21 #include <vnet/fib/fib_walk.h>
22
23 static fib_bfd_state_t
24 fib_bfd_bfd_state_to_fib (bfd_state_e bstate)
25 {
26     switch (bstate)
27     {
28     case BFD_STATE_up:
29         return (FIB_BFD_STATE_UP);
30     case BFD_STATE_down:
31     case BFD_STATE_admin_down:
32     case BFD_STATE_init:
33         return (FIB_BFD_STATE_DOWN);
34     }
35     return (FIB_BFD_STATE_DOWN);
36 }
37
38 static void
39 fib_bfd_update_walk (fib_node_index_t fei)
40 {
41     /*
42      * initiate a backwalk of dependent children
43      * to notify of the state change of this entry.
44      */
45     fib_node_back_walk_ctx_t ctx = {
46         .fnbw_reason = FIB_NODE_BW_REASON_FLAG_EVALUATE,
47     };
48     fib_walk_sync(FIB_NODE_TYPE_ENTRY, fei, &ctx);
49 }
50
51 /**
52  * @brief Callback function registered with BFD module to receive notifications
53  * of the CRUD of BFD sessions
54  * would be static but for the fact it's called from the unit-tests
55  */
56 void
57 fib_bfd_notify (bfd_listen_event_e event,
58                 const bfd_session_t *session)
59 {
60     fib_entry_delegate_t *fed;
61     const bfd_udp_key_t *key;
62     fib_node_index_t fei;
63
64     if (BFD_HOP_TYPE_MULTI != 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     fib_prefix_t pfx = {
76         .fp_addr = key->peer_addr,
77         .fp_proto = (ip46_address_is_ip4 (&key->peer_addr) ?
78                      FIB_PROTOCOL_IP4:
79                      FIB_PROTOCOL_IP6),
80         .fp_len = (ip46_address_is_ip4 (&key->peer_addr) ?
81                    32:
82                    128),
83     };
84
85     /*
86      * get the FIB entry
87      */
88     fei = fib_table_lookup_exact_match(key->fib_index, &pfx);
89
90     switch (event)
91     {
92     case BFD_LISTEN_EVENT_CREATE:
93         /*
94          * The creation of a new session
95          */
96         if ((FIB_NODE_INDEX_INVALID != fei) &&
97             (fed = fib_entry_delegate_get(fib_entry_get(fei),
98                                           FIB_ENTRY_DELEGATE_BFD)))
99         {
100             /*
101              * already got state for this entry
102              */
103         }
104         else
105         {
106             /*
107              * source and lock the entry. add the delegate
108              */
109             fei = fib_table_entry_special_add(key->fib_index,
110                                               &pfx,
111                                               FIB_SOURCE_RR,
112                                               FIB_ENTRY_FLAG_NONE,
113                                               ADJ_INDEX_INVALID);
114             fib_entry_lock(fei);
115
116             fed = fib_entry_delegate_find_or_add(fib_entry_get(fei),
117                                                  FIB_ENTRY_DELEGATE_BFD);
118
119             /*
120              * pretend the session is up and skip the walk.
121              * If we set it down then we get traffic loss on new children.
122              * if we walk then we lose traffic for existing children. Wait
123              * for the first BFD UP/DOWN before we let the session's state
124              * influence forwarding.
125              */
126             fed->fd_bfd_state = FIB_BFD_STATE_UP;
127         }
128         break;
129
130     case BFD_LISTEN_EVENT_UPDATE:
131         /*
132          * state change up/dowm and
133          */
134         ASSERT(FIB_NODE_INDEX_INVALID != fei);
135
136         fed = fib_entry_delegate_get(fib_entry_get(fei),
137                                      FIB_ENTRY_DELEGATE_BFD);
138
139         if (NULL != fed)
140         {
141             fed->fd_bfd_state = fib_bfd_bfd_state_to_fib(session->local_state);
142             fib_bfd_update_walk(fei);
143         }
144         /*
145          * else
146          *   no BFD state
147          */
148         break;
149
150     case BFD_LISTEN_EVENT_DELETE:
151         /*
152          * session has been removed.
153          */
154         if (FIB_NODE_INDEX_INVALID == fei)
155         {
156             /*
157              * no FIB entry
158              */
159         }
160         else if (fib_entry_delegate_get(fib_entry_get(fei),
161                                         FIB_ENTRY_DELEGATE_BFD))
162         {
163             /*
164              * has an associated BFD tracking delegate
165              * usource the entry and remove the BFD tracking deletgate
166              */
167             fib_entry_delegate_remove(fib_entry_get(fei),
168                                       FIB_ENTRY_DELEGATE_BFD);
169             fib_bfd_update_walk(fei);
170
171             fib_table_entry_special_remove(key->fib_index,
172                                            &pfx,
173                                            FIB_SOURCE_RR);
174             fib_entry_unlock(fei);
175         }
176         /*
177          * else
178          * no BFD associated state
179          */
180         break;
181     }
182 }
183
184 static clib_error_t *
185 fib_bfd_main_init (vlib_main_t * vm)
186 {
187     clib_error_t * error = NULL;
188
189     if ((error = vlib_call_init_function (vm, bfd_main_init)))
190         return (error);
191
192     bfd_register_listener(fib_bfd_notify);
193
194     return (error);
195 }
196
197 VLIB_INIT_FUNCTION (fib_bfd_main_init);