Line data Source code
1 : /*
2 : * teib.h: Tunnel Endpoint Information Base
3 : *
4 : * Copyright (c) 2020 Cisco and/or its affiliates.
5 : * Licensed under the Apache License, Version 2.0 (the "License");
6 : * you may not use this file except in compliance with the License.
7 : * You may obtain a copy of the License at:
8 : *
9 : * http://www.apache.org/licenses/LICENSE-2.0
10 : *
11 : * Unless required by applicable law or agreed to in writing, software
12 : * distributed under the License is distributed on an "AS IS" BASIS,
13 : * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 : * See the License for the specific language governing permissions and
15 : * limitations under the License.
16 : */
17 :
18 :
19 : #include <vnet/teib/teib.h>
20 : #include <vnet/fib/fib_table.h>
21 : #include <vnet/adj/adj_midchain.h>
22 : #include <vnet/ip/ip6_ll_table.h>
23 :
24 : typedef struct teib_key_t_
25 : {
26 : ip_address_t tk_peer;
27 : u8 __pad[3];
28 : u32 tk_sw_if_index;
29 : } __clib_packed teib_key_t;
30 :
31 : STATIC_ASSERT_SIZEOF (teib_key_t, 24);
32 :
33 : struct teib_entry_t_
34 : {
35 : teib_key_t *te_key;
36 : fib_prefix_t te_nh;
37 : u32 te_nh_fib_index;
38 : };
39 :
40 : typedef struct teib_db_t_
41 : {
42 : u32 td_n_entries[N_AF];
43 : uword *td_db;
44 : } teib_db_t;
45 :
46 : static teib_db_t teib_db;
47 : static teib_entry_t *teib_pool;
48 : static teib_vft_t *teib_vfts;
49 : static vlib_log_class_t teib_logger;
50 :
51 : #define TEIB_NOTIFY(_te, _fn) { \
52 : teib_vft_t *_vft; \
53 : vec_foreach(_vft, teib_vfts) { \
54 : if (_vft->_fn) { \
55 : _vft->_fn(_te); \
56 : } \
57 : } \
58 : }
59 :
60 : #define TEIB_DBG(...) \
61 : vlib_log_debug (teib_logger, __VA_ARGS__);
62 :
63 : #define TEIB_INFO(...) \
64 : vlib_log_notice (teib_logger, __VA_ARGS__);
65 :
66 : #define TEIB_TE_DBG(_te, _fmt, _args...) \
67 : vlib_log_debug (teib_logger, "[%U]: " _fmt, format_teib_entry, _te - teib_pool, ##_args)
68 : #define TEIB_TE_INFO(_te, _fmt, _args...) \
69 : vlib_log_notice (teib_logger, "[%U]: " _fmt, format_teib_entry, _te - teib_pool, ##_args)
70 :
71 : u32
72 1349 : teib_entry_get_sw_if_index (const teib_entry_t * te)
73 : {
74 1349 : return (te->te_key->tk_sw_if_index);
75 : }
76 :
77 : static ip_address_family_t
78 12 : teib_entry_get_af (const teib_entry_t * te)
79 : {
80 12 : return (ip_addr_version (&te->te_key->tk_peer));
81 : }
82 :
83 : u32
84 791 : teib_entry_get_fib_index (const teib_entry_t * te)
85 : {
86 791 : return (te->te_nh_fib_index);
87 : }
88 :
89 : const ip_address_t *
90 961 : teib_entry_get_peer (const teib_entry_t * te)
91 : {
92 961 : return (&te->te_key->tk_peer);
93 : }
94 :
95 : const fib_prefix_t *
96 947 : teib_entry_get_nh (const teib_entry_t * te)
97 : {
98 947 : return (&te->te_nh);
99 : }
100 :
101 : void
102 126 : teib_entry_adj_stack (const teib_entry_t * te, adj_index_t ai)
103 : {
104 126 : adj_midchain_delegate_stack (ai, te->te_nh_fib_index, &te->te_nh);
105 126 : }
106 :
107 : teib_entry_t *
108 1017 : teib_entry_get (index_t tei)
109 : {
110 1017 : return pool_elt_at_index (teib_pool, tei);
111 : }
112 :
113 : teib_entry_t *
114 277 : teib_entry_find (u32 sw_if_index, const ip_address_t * peer)
115 : {
116 277 : teib_key_t nk = {
117 : .tk_peer = *peer,
118 : .tk_sw_if_index = sw_if_index,
119 : };
120 : uword *p;
121 :
122 277 : p = hash_get_mem (teib_db.td_db, &nk);
123 :
124 277 : if (NULL != p)
125 142 : return teib_entry_get (p[0]);
126 :
127 135 : return (NULL);
128 : }
129 :
130 : teib_entry_t *
131 75 : teib_entry_find_46 (u32 sw_if_index,
132 : fib_protocol_t fproto, const ip46_address_t * peer)
133 : {
134 : ip_address_t ip;
135 :
136 75 : ip_address_from_46 (peer, fproto, &ip);
137 :
138 75 : return (teib_entry_find (sw_if_index, &ip));
139 : }
140 :
141 : static void
142 97 : teib_adj_fib_add (const ip_address_t *ip, u32 sw_if_index, u32 peer_fib_index)
143 : {
144 126 : if (AF_IP6 == ip_addr_version (ip) &&
145 29 : ip6_address_is_link_local_unicast (&ip_addr_v6 (ip)))
146 1 : {
147 1 : ip6_ll_prefix_t pfx = {
148 : .ilp_addr = ip_addr_v6 (ip),
149 : .ilp_sw_if_index = sw_if_index,
150 : };
151 1 : ip6_ll_table_entry_update (&pfx, FIB_ROUTE_PATH_FLAG_NONE);
152 : }
153 : else
154 : {
155 : fib_prefix_t pfx;
156 :
157 96 : ip_address_to_fib_prefix (ip, &pfx);
158 96 : fib_table_entry_path_add (
159 : peer_fib_index, &pfx, FIB_SOURCE_ADJ, FIB_ENTRY_FLAG_ATTACHED,
160 96 : fib_proto_to_dpo (pfx.fp_proto), &pfx.fp_addr, sw_if_index, ~0, 1,
161 : NULL, FIB_ROUTE_PATH_FLAG_NONE);
162 :
163 96 : if (0 == teib_db.td_n_entries[ip_addr_version (ip)]++)
164 8 : fib_table_lock (peer_fib_index, pfx.fp_proto, FIB_SOURCE_ADJ);
165 : }
166 97 : }
167 :
168 : static void
169 97 : teib_adj_fib_remove (ip_address_t *ip, u32 sw_if_index, u32 peer_fib_index)
170 : {
171 126 : if (AF_IP6 == ip_addr_version (ip) &&
172 29 : ip6_address_is_link_local_unicast (&ip_addr_v6 (ip)))
173 1 : {
174 1 : ip6_ll_prefix_t pfx = {
175 : .ilp_addr = ip_addr_v6 (ip),
176 : .ilp_sw_if_index = sw_if_index,
177 : };
178 1 : ip6_ll_table_entry_delete (&pfx);
179 : }
180 : else
181 : {
182 : fib_prefix_t pfx;
183 :
184 96 : ip_address_to_fib_prefix (ip, &pfx);
185 96 : fib_table_entry_path_remove (
186 96 : peer_fib_index, &pfx, FIB_SOURCE_ADJ, fib_proto_to_dpo (pfx.fp_proto),
187 : &pfx.fp_addr, sw_if_index, ~0, 1, FIB_ROUTE_PATH_FLAG_NONE);
188 :
189 96 : if (0 == --teib_db.td_n_entries[ip_addr_version (ip)])
190 8 : fib_table_unlock (peer_fib_index, pfx.fp_proto, FIB_SOURCE_ADJ);
191 : }
192 97 : }
193 :
194 : int
195 85 : teib_entry_add (u32 sw_if_index,
196 : const ip_address_t * peer,
197 : u32 nh_table_id, const ip_address_t * nh)
198 : {
199 : fib_protocol_t nh_proto;
200 : teib_entry_t *te;
201 : u32 nh_fib_index, peer_fib_index;
202 : index_t tei;
203 :
204 85 : nh_proto = (AF_IP4 == ip_addr_version (nh) ?
205 85 : FIB_PROTOCOL_IP4 : FIB_PROTOCOL_IP6);
206 :
207 85 : peer_fib_index = fib_table_get_index_for_sw_if_index (
208 85 : ip_address_family_to_fib_proto (peer->version), sw_if_index);
209 85 : nh_fib_index = fib_table_find (nh_proto, nh_table_id);
210 :
211 85 : if (~0 == nh_fib_index)
212 : {
213 0 : return (VNET_API_ERROR_NO_SUCH_FIB);
214 : }
215 :
216 85 : te = teib_entry_find (sw_if_index, peer);
217 :
218 85 : if (NULL == te)
219 : {
220 85 : teib_key_t nk = {
221 : .tk_peer = *peer,
222 : .tk_sw_if_index = sw_if_index,
223 : };
224 : teib_entry_t *te;
225 :
226 85 : pool_get_zero (teib_pool, te);
227 :
228 85 : tei = te - teib_pool;
229 85 : te->te_key = clib_mem_alloc (sizeof (*te->te_key));
230 85 : clib_memcpy (te->te_key, &nk, sizeof (*te->te_key));
231 :
232 85 : ip_address_to_fib_prefix (nh, &te->te_nh);
233 85 : te->te_nh_fib_index = nh_fib_index;
234 :
235 170 : hash_set_mem (teib_db.td_db, te->te_key, tei);
236 :
237 : /* we how have a /32 in the overlay, add an adj-fib */
238 85 : teib_adj_fib_add (&te->te_key->tk_peer, sw_if_index, peer_fib_index);
239 :
240 340 : TEIB_NOTIFY (te, nv_added);
241 85 : TEIB_TE_INFO (te, "created");
242 : }
243 : else
244 : {
245 0 : TEIB_TE_INFO (te, "exists");
246 0 : return (VNET_API_ERROR_ENTRY_ALREADY_EXISTS);
247 : }
248 85 : return 0;
249 : }
250 :
251 : int
252 85 : teib_entry_del (u32 sw_if_index, const ip_address_t * peer)
253 : {
254 : teib_entry_t *te;
255 :
256 85 : te = teib_entry_find (sw_if_index, peer);
257 :
258 85 : if (te != NULL)
259 : {
260 85 : TEIB_TE_INFO (te, "removed");
261 :
262 : u32 peer_fib_index;
263 :
264 85 : peer_fib_index = fib_table_get_index_for_sw_if_index (
265 85 : ip_address_family_to_fib_proto (peer->version), sw_if_index);
266 :
267 85 : teib_adj_fib_remove (&te->te_key->tk_peer, sw_if_index, peer_fib_index);
268 :
269 170 : hash_unset_mem (teib_db.td_db, te->te_key);
270 :
271 340 : TEIB_NOTIFY (te, nv_deleted);
272 :
273 85 : clib_mem_free (te->te_key);
274 85 : pool_put (teib_pool, te);
275 : }
276 : else
277 : {
278 0 : TEIB_INFO ("no such entry: %U, %U", format_vnet_sw_if_index_name,
279 : vnet_get_main (), sw_if_index, format_ip_address, peer);
280 0 : return (VNET_API_ERROR_NO_SUCH_ENTRY);
281 : }
282 85 : return 0;
283 : }
284 :
285 : u8 *
286 182 : format_teib_entry (u8 * s, va_list * args)
287 : {
288 182 : index_t tei = va_arg (*args, index_t);
289 182 : vnet_main_t *vnm = vnet_get_main ();
290 : teib_entry_t *te;
291 :
292 182 : te = teib_entry_get (tei);
293 :
294 182 : s = format (s, "[%d] ", tei);
295 182 : s = format (s, "%U:", format_vnet_sw_if_index_name,
296 182 : vnm, te->te_key->tk_sw_if_index);
297 182 : s = format (s, "%U", format_ip_address,
298 182 : &te->te_key->tk_peer, IP46_TYPE_ANY);
299 182 : s = format (s, " via [%d]:%U",
300 182 : fib_table_get_table_id (te->te_nh_fib_index, te->te_nh.fp_proto),
301 : format_fib_prefix, &te->te_nh);
302 :
303 182 : return (s);
304 : }
305 :
306 : void
307 102 : teib_walk (teib_walk_cb_t fn, void *ctx)
308 : {
309 : index_t tei;
310 :
311 : /* *INDENT-OFF* */
312 723 : pool_foreach_index (tei, teib_pool)
313 : {
314 621 : fn(tei, ctx);
315 : }
316 : /* *INDENT-ON* */
317 102 : }
318 :
319 : void
320 18 : teib_walk_itf (u32 sw_if_index, teib_walk_cb_t fn, void *ctx)
321 : {
322 : index_t tei;
323 :
324 : /* *INDENT-OFF* */
325 36 : pool_foreach_index (tei, teib_pool)
326 : {
327 18 : if (sw_if_index == teib_entry_get_sw_if_index(teib_entry_get(tei)))
328 0 : fn(tei, ctx);
329 : }
330 : /* *INDENT-ON* */
331 18 : }
332 :
333 : static void
334 1625 : teib_walk_itf_proto (u32 sw_if_index,
335 : ip_address_family_t af, teib_walk_cb_t fn, void *ctx)
336 : {
337 : index_t tei;
338 :
339 : /* *INDENT-OFF* */
340 1655 : pool_foreach_index (tei, teib_pool)
341 : {
342 42 : if (sw_if_index == teib_entry_get_sw_if_index(teib_entry_get(tei)) &&
343 12 : af == teib_entry_get_af(teib_entry_get(tei)))
344 12 : fn(tei, ctx);
345 : }
346 : /* *INDENT-ON* */
347 1625 : }
348 :
349 : typedef struct teib_table_bind_ctx_t_
350 : {
351 : u32 new_peer_fib_index;
352 : u32 old_peer_fib_index;
353 : } teib_table_bind_ctx_t;
354 :
355 : static walk_rc_t
356 12 : teib_walk_table_bind (index_t tei, void *arg)
357 : {
358 12 : teib_table_bind_ctx_t *ctx = arg;
359 : teib_entry_t *te;
360 :
361 12 : te = teib_entry_get (tei);
362 :
363 12 : TEIB_TE_INFO (te, "bind: %d -> %d", ctx->old_peer_fib_index,
364 : ctx->new_peer_fib_index);
365 :
366 12 : teib_adj_fib_remove (&te->te_key->tk_peer, te->te_key->tk_sw_if_index,
367 : ctx->old_peer_fib_index);
368 12 : teib_adj_fib_add (&te->te_key->tk_peer, te->te_key->tk_sw_if_index,
369 : ctx->new_peer_fib_index);
370 :
371 12 : return (WALK_CONTINUE);
372 : }
373 :
374 : static void
375 896 : teib_table_bind_v4 (ip4_main_t * im,
376 : uword opaque,
377 : u32 sw_if_index, u32 new_fib_index, u32 old_fib_index)
378 : {
379 896 : teib_table_bind_ctx_t ctx = {
380 : .old_peer_fib_index = old_fib_index,
381 : .new_peer_fib_index = new_fib_index,
382 : };
383 :
384 896 : teib_walk_itf_proto (sw_if_index, AF_IP4, teib_walk_table_bind, &ctx);
385 896 : }
386 :
387 : static void
388 729 : teib_table_bind_v6 (ip6_main_t * im,
389 : uword opaque,
390 : u32 sw_if_index, u32 new_fib_index, u32 old_fib_index)
391 : {
392 729 : teib_table_bind_ctx_t ctx = {
393 : .old_peer_fib_index = old_fib_index,
394 : .new_peer_fib_index = new_fib_index,
395 : };
396 :
397 729 : teib_walk_itf_proto (sw_if_index, AF_IP6, teib_walk_table_bind, &ctx);
398 729 : }
399 :
400 : void
401 1677 : teib_register (const teib_vft_t * vft)
402 : {
403 1677 : vec_add1 (teib_vfts, *vft);
404 1677 : }
405 :
406 : static clib_error_t *
407 559 : teib_init (vlib_main_t * vm)
408 : {
409 559 : teib_db.td_db = hash_create_mem (0, sizeof (teib_key_t), sizeof (u32));
410 :
411 559 : ip4_table_bind_callback_t cb4 = {
412 : .function = teib_table_bind_v4,
413 : };
414 559 : vec_add1 (ip4_main.table_bind_callbacks, cb4);
415 :
416 559 : ip6_table_bind_callback_t cb6 = {
417 : .function = teib_table_bind_v6,
418 : };
419 559 : vec_add1 (ip6_main.table_bind_callbacks, cb6);
420 :
421 559 : teib_logger = vlib_log_register_class ("teib", "teib");
422 :
423 559 : return (NULL);
424 : }
425 :
426 92959 : VLIB_INIT_FUNCTION (teib_init);
427 :
428 : /*
429 : * fd.io coding-style-patch-verification: ON
430 : *
431 : * Local Variables:
432 : * eval: (c-set-style "gnu")
433 : * End:
434 : */
|