/src/frr/zebra/zebra_srv6.c
Line | Count | Source |
1 | | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | | /* |
3 | | * Zebra SRv6 definitions |
4 | | * Copyright (C) 2020 Hiroki Shirokura, LINE Corporation |
5 | | * Copyright (C) 2020 Masakazu Asama |
6 | | */ |
7 | | |
8 | | #include <zebra.h> |
9 | | |
10 | | #include "network.h" |
11 | | #include "prefix.h" |
12 | | #include "stream.h" |
13 | | #include "srv6.h" |
14 | | #include "zebra/debug.h" |
15 | | #include "zebra/zapi_msg.h" |
16 | | #include "zebra/zserv.h" |
17 | | #include "zebra/zebra_router.h" |
18 | | #include "zebra/zebra_srv6.h" |
19 | | #include "zebra/zebra_errors.h" |
20 | | #include <stdio.h> |
21 | | #include <string.h> |
22 | | #include <stdlib.h> |
23 | | #include <arpa/inet.h> |
24 | | #include <netinet/in.h> |
25 | | |
26 | | #include <stdio.h> |
27 | | #include <string.h> |
28 | | #include <stdlib.h> |
29 | | #include <arpa/inet.h> |
30 | | #include <netinet/in.h> |
31 | | |
32 | | |
33 | | DEFINE_MGROUP(SRV6_MGR, "SRv6 Manager"); |
34 | 2 | DEFINE_MTYPE_STATIC(SRV6_MGR, SRV6M_CHUNK, "SRv6 Manager Chunk"); |
35 | 2 | |
36 | 2 | /* define hooks for the basic API, so that it can be specialized or served |
37 | 2 | * externally |
38 | 2 | */ |
39 | 2 | |
40 | 2 | DEFINE_HOOK(srv6_manager_client_connect, |
41 | 2 | (struct zserv *client, vrf_id_t vrf_id), |
42 | 2 | (client, vrf_id)); |
43 | 2 | DEFINE_HOOK(srv6_manager_client_disconnect, |
44 | 2 | (struct zserv *client), (client)); |
45 | 2 | DEFINE_HOOK(srv6_manager_get_chunk, |
46 | 2 | (struct srv6_locator **loc, |
47 | 2 | struct zserv *client, |
48 | 2 | const char *locator_name, |
49 | 2 | vrf_id_t vrf_id), |
50 | 2 | (loc, client, locator_name, vrf_id)); |
51 | 2 | DEFINE_HOOK(srv6_manager_release_chunk, |
52 | 2 | (struct zserv *client, |
53 | 2 | const char *locator_name, |
54 | 2 | vrf_id_t vrf_id), |
55 | 2 | (client, locator_name, vrf_id)); |
56 | 2 | |
57 | 2 | /* define wrappers to be called in zapi_msg.c (as hooks must be called in |
58 | 2 | * source file where they were defined) |
59 | 2 | */ |
60 | 2 | |
61 | 2 | void srv6_manager_client_connect_call(struct zserv *client, vrf_id_t vrf_id) |
62 | 2 | { |
63 | 0 | hook_call(srv6_manager_client_connect, client, vrf_id); |
64 | 0 | } |
65 | | |
66 | | void srv6_manager_get_locator_chunk_call(struct srv6_locator **loc, |
67 | | struct zserv *client, |
68 | | const char *locator_name, |
69 | | vrf_id_t vrf_id) |
70 | 0 | { |
71 | 0 | hook_call(srv6_manager_get_chunk, loc, client, locator_name, vrf_id); |
72 | 0 | } |
73 | | |
74 | | void srv6_manager_release_locator_chunk_call(struct zserv *client, |
75 | | const char *locator_name, |
76 | | vrf_id_t vrf_id) |
77 | 0 | { |
78 | 0 | hook_call(srv6_manager_release_chunk, client, locator_name, vrf_id); |
79 | 0 | } |
80 | | |
81 | | int srv6_manager_client_disconnect_cb(struct zserv *client) |
82 | 0 | { |
83 | 0 | hook_call(srv6_manager_client_disconnect, client); |
84 | 0 | return 0; |
85 | 0 | } |
86 | | |
87 | | static int zebra_srv6_cleanup(struct zserv *client) |
88 | 0 | { |
89 | 0 | return 0; |
90 | 0 | } |
91 | | |
92 | | void zebra_srv6_locator_add(struct srv6_locator *locator) |
93 | 0 | { |
94 | 0 | struct zebra_srv6 *srv6 = zebra_srv6_get_default(); |
95 | 0 | struct srv6_locator *tmp; |
96 | 0 | struct listnode *node; |
97 | 0 | struct zserv *client; |
98 | |
|
99 | 0 | tmp = zebra_srv6_locator_lookup(locator->name); |
100 | 0 | if (!tmp) |
101 | 0 | listnode_add(srv6->locators, locator); |
102 | | |
103 | | /* |
104 | | * Notify new locator info to zclients. |
105 | | * |
106 | | * The srv6 locators and their prefixes are managed by zserv(zebra). |
107 | | * And an actual configuration the srv6 sid in the srv6 locator is done |
108 | | * by zclient(bgpd, isisd, etc). The configuration of each locator |
109 | | * allocation and specify it by zserv and zclient should be |
110 | | * asynchronous. For that, zclient should be received the event via |
111 | | * ZAPI when a srv6 locator is added on zebra. |
112 | | * Basically, in SRv6, adding/removing SRv6 locators is performed less |
113 | | * frequently than adding rib entries, so a broad to all zclients will |
114 | | * not degrade the overall performance of FRRouting. |
115 | | */ |
116 | 0 | for (ALL_LIST_ELEMENTS_RO(zrouter.client_list, node, client)) |
117 | 0 | zsend_zebra_srv6_locator_add(client, locator); |
118 | 0 | } |
119 | | |
120 | | void zebra_srv6_locator_delete(struct srv6_locator *locator) |
121 | 0 | { |
122 | 0 | struct listnode *n; |
123 | 0 | struct srv6_locator_chunk *c; |
124 | 0 | struct zebra_srv6 *srv6 = zebra_srv6_get_default(); |
125 | 0 | struct zserv *client; |
126 | | |
127 | | /* |
128 | | * Notify deleted locator info to zclients if needed. |
129 | | * |
130 | | * zclient(bgpd,isisd,etc) allocates a sid from srv6 locator chunk and |
131 | | * uses it for its own purpose. For example, in the case of BGP L3VPN, |
132 | | * the SID assigned to vpn unicast rib will be given. |
133 | | * And when the locator is deleted by zserv(zebra), those SIDs need to |
134 | | * be withdrawn. The zclient must initiate the withdrawal of the SIDs |
135 | | * by ZEBRA_SRV6_LOCATOR_DELETE, and this notification is sent to the |
136 | | * owner of each chunk. |
137 | | */ |
138 | 0 | for (ALL_LIST_ELEMENTS_RO((struct list *)locator->chunks, n, c)) { |
139 | 0 | if (c->proto == ZEBRA_ROUTE_SYSTEM) |
140 | 0 | continue; |
141 | 0 | client = zserv_find_client(c->proto, c->instance); |
142 | 0 | if (!client) { |
143 | 0 | zlog_warn( |
144 | 0 | "%s: Not found zclient(proto=%u, instance=%u).", |
145 | 0 | __func__, c->proto, c->instance); |
146 | 0 | continue; |
147 | 0 | } |
148 | 0 | zsend_zebra_srv6_locator_delete(client, locator); |
149 | 0 | } |
150 | |
|
151 | 0 | listnode_delete(srv6->locators, locator); |
152 | 0 | srv6_locator_free(locator); |
153 | 0 | } |
154 | | |
155 | | struct srv6_locator *zebra_srv6_locator_lookup(const char *name) |
156 | 0 | { |
157 | 0 | struct zebra_srv6 *srv6 = zebra_srv6_get_default(); |
158 | 0 | struct srv6_locator *locator; |
159 | 0 | struct listnode *node; |
160 | |
|
161 | 0 | for (ALL_LIST_ELEMENTS_RO(srv6->locators, node, locator)) |
162 | 0 | if (!strncmp(name, locator->name, SRV6_LOCNAME_SIZE)) |
163 | 0 | return locator; |
164 | 0 | return NULL; |
165 | 0 | } |
166 | | |
167 | | void zebra_notify_srv6_locator_add(struct srv6_locator *locator) |
168 | 0 | { |
169 | 0 | struct listnode *node; |
170 | 0 | struct zserv *client; |
171 | | |
172 | | /* |
173 | | * Notify new locator info to zclients. |
174 | | * |
175 | | * The srv6 locators and their prefixes are managed by zserv(zebra). |
176 | | * And an actual configuration the srv6 sid in the srv6 locator is done |
177 | | * by zclient(bgpd, isisd, etc). The configuration of each locator |
178 | | * allocation and specify it by zserv and zclient should be |
179 | | * asynchronous. For that, zclient should be received the event via |
180 | | * ZAPI when a srv6 locator is added on zebra. |
181 | | * Basically, in SRv6, adding/removing SRv6 locators is performed less |
182 | | * frequently than adding rib entries, so a broad to all zclients will |
183 | | * not degrade the overall performance of FRRouting. |
184 | | */ |
185 | 0 | for (ALL_LIST_ELEMENTS_RO(zrouter.client_list, node, client)) |
186 | 0 | zsend_zebra_srv6_locator_add(client, locator); |
187 | 0 | } |
188 | | |
189 | | void zebra_notify_srv6_locator_delete(struct srv6_locator *locator) |
190 | 0 | { |
191 | 0 | struct listnode *n; |
192 | 0 | struct srv6_locator_chunk *c; |
193 | 0 | struct zserv *client; |
194 | | |
195 | | /* |
196 | | * Notify deleted locator info to zclients if needed. |
197 | | * |
198 | | * zclient(bgpd,isisd,etc) allocates a sid from srv6 locator chunk and |
199 | | * uses it for its own purpose. For example, in the case of BGP L3VPN, |
200 | | * the SID assigned to vpn unicast rib will be given. |
201 | | * And when the locator is deleted by zserv(zebra), those SIDs need to |
202 | | * be withdrawn. The zclient must initiate the withdrawal of the SIDs |
203 | | * by ZEBRA_SRV6_LOCATOR_DELETE, and this notification is sent to the |
204 | | * owner of each chunk. |
205 | | */ |
206 | 0 | for (ALL_LIST_ELEMENTS_RO((struct list *)locator->chunks, n, c)) { |
207 | 0 | if (c->proto == ZEBRA_ROUTE_SYSTEM) |
208 | 0 | continue; |
209 | 0 | client = zserv_find_client(c->proto, c->instance); |
210 | 0 | if (!client) { |
211 | 0 | zlog_warn("Not found zclient(proto=%u, instance=%u).", |
212 | 0 | c->proto, c->instance); |
213 | 0 | continue; |
214 | 0 | } |
215 | 0 | zsend_zebra_srv6_locator_delete(client, locator); |
216 | 0 | } |
217 | 0 | } |
218 | | |
219 | | struct zebra_srv6 *zebra_srv6_get_default(void) |
220 | 0 | { |
221 | 0 | static struct zebra_srv6 srv6; |
222 | 0 | static bool first_execution = true; |
223 | |
|
224 | 0 | if (first_execution) { |
225 | 0 | first_execution = false; |
226 | 0 | srv6.locators = list_new(); |
227 | 0 | } |
228 | 0 | return &srv6; |
229 | 0 | } |
230 | | |
231 | | /** |
232 | | * Core function, assigns srv6-locator chunks |
233 | | * |
234 | | * It first searches through the list to check if there's one available |
235 | | * (previously released). Otherwise it creates and assigns a new one |
236 | | * |
237 | | * @param proto Daemon protocol of client, to identify the owner |
238 | | * @param instance Instance, to identify the owner |
239 | | * @param session_id SessionID of client |
240 | | * @param name Name of SRv6-locator |
241 | | * @return Pointer to the assigned srv6-locator chunk, |
242 | | * or NULL if the request could not be satisfied |
243 | | */ |
244 | | static struct srv6_locator * |
245 | | assign_srv6_locator_chunk(uint8_t proto, |
246 | | uint16_t instance, |
247 | | uint32_t session_id, |
248 | | const char *locator_name) |
249 | 0 | { |
250 | 0 | bool chunk_found = false; |
251 | 0 | struct listnode *node = NULL; |
252 | 0 | struct srv6_locator *loc = NULL; |
253 | 0 | struct srv6_locator_chunk *chunk = NULL; |
254 | |
|
255 | 0 | loc = zebra_srv6_locator_lookup(locator_name); |
256 | 0 | if (!loc) { |
257 | 0 | zlog_info("%s: locator %s was not found", |
258 | 0 | __func__, locator_name); |
259 | 0 | return NULL; |
260 | 0 | } |
261 | | |
262 | 0 | for (ALL_LIST_ELEMENTS_RO((struct list *)loc->chunks, node, chunk)) { |
263 | 0 | if (chunk->proto != NO_PROTO && chunk->proto != proto) |
264 | 0 | continue; |
265 | 0 | chunk_found = true; |
266 | 0 | break; |
267 | 0 | } |
268 | |
|
269 | 0 | if (!chunk_found) { |
270 | 0 | zlog_info("%s: locator is already owned", __func__); |
271 | 0 | return NULL; |
272 | 0 | } |
273 | | |
274 | 0 | chunk->proto = proto; |
275 | 0 | chunk->instance = instance; |
276 | 0 | chunk->session_id = session_id; |
277 | 0 | return loc; |
278 | 0 | } |
279 | | |
280 | | static int zebra_srv6_manager_get_locator_chunk(struct srv6_locator **loc, |
281 | | struct zserv *client, |
282 | | const char *locator_name, |
283 | | vrf_id_t vrf_id) |
284 | 0 | { |
285 | 0 | int ret = 0; |
286 | |
|
287 | 0 | *loc = assign_srv6_locator_chunk(client->proto, client->instance, |
288 | 0 | client->session_id, locator_name); |
289 | |
|
290 | 0 | if (!*loc) |
291 | 0 | zlog_err("Unable to assign locator chunk to %s instance %u", |
292 | 0 | zebra_route_string(client->proto), client->instance); |
293 | 0 | else if (IS_ZEBRA_DEBUG_PACKET) |
294 | 0 | zlog_info("Assigned locator chunk %s to %s instance %u", |
295 | 0 | (*loc)->name, zebra_route_string(client->proto), |
296 | 0 | client->instance); |
297 | |
|
298 | 0 | if (*loc && (*loc)->status_up) |
299 | 0 | ret = zsend_srv6_manager_get_locator_chunk_response(client, |
300 | 0 | vrf_id, |
301 | 0 | *loc); |
302 | 0 | return ret; |
303 | 0 | } |
304 | | |
305 | | /** |
306 | | * Core function, release no longer used srv6-locator chunks |
307 | | * |
308 | | * @param proto Daemon protocol of client, to identify the owner |
309 | | * @param instance Instance, to identify the owner |
310 | | * @param session_id Zclient session ID, to identify the zclient session |
311 | | * @param locator_name SRv6-locator name, to identify the actual locator |
312 | | * @return 0 on success, -1 otherwise |
313 | | */ |
314 | | static int release_srv6_locator_chunk(uint8_t proto, uint16_t instance, |
315 | | uint32_t session_id, |
316 | | const char *locator_name) |
317 | 0 | { |
318 | 0 | int ret = -1; |
319 | 0 | struct listnode *node; |
320 | 0 | struct srv6_locator_chunk *chunk; |
321 | 0 | struct srv6_locator *loc = NULL; |
322 | |
|
323 | 0 | loc = zebra_srv6_locator_lookup(locator_name); |
324 | 0 | if (!loc) |
325 | 0 | return -1; |
326 | | |
327 | 0 | if (IS_ZEBRA_DEBUG_PACKET) |
328 | 0 | zlog_debug("%s: Releasing srv6-locator on %s", __func__, |
329 | 0 | locator_name); |
330 | |
|
331 | 0 | for (ALL_LIST_ELEMENTS_RO((struct list *)loc->chunks, node, chunk)) { |
332 | 0 | if (chunk->proto != proto || |
333 | 0 | chunk->instance != instance || |
334 | 0 | chunk->session_id != session_id) |
335 | 0 | continue; |
336 | 0 | chunk->proto = NO_PROTO; |
337 | 0 | chunk->instance = 0; |
338 | 0 | chunk->session_id = 0; |
339 | 0 | chunk->keep = 0; |
340 | 0 | ret = 0; |
341 | 0 | break; |
342 | 0 | } |
343 | |
|
344 | 0 | if (ret != 0) |
345 | 0 | flog_err(EC_ZEBRA_SRV6M_UNRELEASED_LOCATOR_CHUNK, |
346 | 0 | "%s: SRv6 locator chunk not released", __func__); |
347 | |
|
348 | 0 | return ret; |
349 | 0 | } |
350 | | |
351 | | static int zebra_srv6_manager_release_locator_chunk(struct zserv *client, |
352 | | const char *locator_name, |
353 | | vrf_id_t vrf_id) |
354 | 0 | { |
355 | 0 | if (vrf_id != VRF_DEFAULT) { |
356 | 0 | zlog_err("SRv6 locator doesn't support vrf"); |
357 | 0 | return -1; |
358 | 0 | } |
359 | | |
360 | 0 | return release_srv6_locator_chunk(client->proto, client->instance, |
361 | 0 | client->session_id, locator_name); |
362 | 0 | } |
363 | | |
364 | | /** |
365 | | * Release srv6-locator chunks from a client. |
366 | | * |
367 | | * Called on client disconnection or reconnection. It only releases chunks |
368 | | * with empty keep value. |
369 | | * |
370 | | * @param proto Daemon protocol of client, to identify the owner |
371 | | * @param instance Instance, to identify the owner |
372 | | * @return Number of chunks released |
373 | | */ |
374 | | int release_daemon_srv6_locator_chunks(struct zserv *client) |
375 | 0 | { |
376 | 0 | int ret; |
377 | 0 | int count = 0; |
378 | 0 | struct zebra_srv6 *srv6 = zebra_srv6_get_default(); |
379 | 0 | struct listnode *loc_node; |
380 | 0 | struct listnode *chunk_node; |
381 | 0 | struct srv6_locator *loc; |
382 | 0 | struct srv6_locator_chunk *chunk; |
383 | |
|
384 | 0 | if (IS_ZEBRA_DEBUG_PACKET) |
385 | 0 | zlog_debug("%s: Releasing chunks for client proto %s, instance %d, session %u", |
386 | 0 | __func__, zebra_route_string(client->proto), |
387 | 0 | client->instance, client->session_id); |
388 | |
|
389 | 0 | for (ALL_LIST_ELEMENTS_RO(srv6->locators, loc_node, loc)) { |
390 | 0 | for (ALL_LIST_ELEMENTS_RO(loc->chunks, chunk_node, chunk)) { |
391 | 0 | if (chunk->proto == client->proto && |
392 | 0 | chunk->instance == client->instance && |
393 | 0 | chunk->session_id == client->session_id && |
394 | 0 | chunk->keep == 0) { |
395 | 0 | ret = release_srv6_locator_chunk( |
396 | 0 | chunk->proto, chunk->instance, |
397 | 0 | chunk->session_id, loc->name); |
398 | 0 | if (ret == 0) |
399 | 0 | count++; |
400 | 0 | } |
401 | 0 | } |
402 | 0 | } |
403 | |
|
404 | 0 | if (IS_ZEBRA_DEBUG_PACKET) |
405 | 0 | zlog_debug("%s: Released %d srv6-locator chunks", |
406 | 0 | __func__, count); |
407 | |
|
408 | 0 | return count; |
409 | 0 | } |
410 | | |
411 | | void zebra_srv6_init(void) |
412 | 0 | { |
413 | 0 | hook_register(zserv_client_close, zebra_srv6_cleanup); |
414 | 0 | hook_register(srv6_manager_get_chunk, |
415 | 0 | zebra_srv6_manager_get_locator_chunk); |
416 | 0 | hook_register(srv6_manager_release_chunk, |
417 | 0 | zebra_srv6_manager_release_locator_chunk); |
418 | 0 | } |
419 | | |
420 | | bool zebra_srv6_is_enable(void) |
421 | 0 | { |
422 | 0 | struct zebra_srv6 *srv6 = zebra_srv6_get_default(); |
423 | |
|
424 | 0 | return listcount(srv6->locators); |
425 | 0 | } |