]> git.proxmox.com Git - mirror_frr.git/blob - zebra/zebra_srv6.c
*: auto-convert to SPDX License IDs
[mirror_frr.git] / zebra / zebra_srv6.c
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 DEFINE_MTYPE_STATIC(SRV6_MGR, SRV6M_CHUNK, "SRv6 Manager Chunk");
35
36 /* define hooks for the basic API, so that it can be specialized or served
37 * externally
38 */
39
40 DEFINE_HOOK(srv6_manager_client_connect,
41 (struct zserv *client, vrf_id_t vrf_id),
42 (client, vrf_id));
43 DEFINE_HOOK(srv6_manager_client_disconnect,
44 (struct zserv *client), (client));
45 DEFINE_HOOK(srv6_manager_get_chunk,
46 (struct srv6_locator **loc,
47 struct zserv *client,
48 const char *locator_name,
49 vrf_id_t vrf_id),
50 (loc, client, locator_name, vrf_id));
51 DEFINE_HOOK(srv6_manager_release_chunk,
52 (struct zserv *client,
53 const char *locator_name,
54 vrf_id_t vrf_id),
55 (client, locator_name, vrf_id));
56
57 /* define wrappers to be called in zapi_msg.c (as hooks must be called in
58 * source file where they were defined)
59 */
60
61 void srv6_manager_client_connect_call(struct zserv *client, vrf_id_t vrf_id)
62 {
63 hook_call(srv6_manager_client_connect, client, vrf_id);
64 }
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 {
71 hook_call(srv6_manager_get_chunk, loc, client, locator_name, vrf_id);
72 }
73
74 void srv6_manager_release_locator_chunk_call(struct zserv *client,
75 const char *locator_name,
76 vrf_id_t vrf_id)
77 {
78 hook_call(srv6_manager_release_chunk, client, locator_name, vrf_id);
79 }
80
81 int srv6_manager_client_disconnect_cb(struct zserv *client)
82 {
83 hook_call(srv6_manager_client_disconnect, client);
84 return 0;
85 }
86
87 static int zebra_srv6_cleanup(struct zserv *client)
88 {
89 return 0;
90 }
91
92 void zebra_srv6_locator_add(struct srv6_locator *locator)
93 {
94 struct zebra_srv6 *srv6 = zebra_srv6_get_default();
95 struct srv6_locator *tmp;
96 struct listnode *node;
97 struct zserv *client;
98
99 tmp = zebra_srv6_locator_lookup(locator->name);
100 if (!tmp)
101 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 for (ALL_LIST_ELEMENTS_RO(zrouter.client_list, node, client))
117 zsend_zebra_srv6_locator_add(client, locator);
118 }
119
120 void zebra_srv6_locator_delete(struct srv6_locator *locator)
121 {
122 struct listnode *n;
123 struct srv6_locator_chunk *c;
124 struct zebra_srv6 *srv6 = zebra_srv6_get_default();
125 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 for (ALL_LIST_ELEMENTS_RO((struct list *)locator->chunks, n, c)) {
139 if (c->proto == ZEBRA_ROUTE_SYSTEM)
140 continue;
141 client = zserv_find_client(c->proto, c->instance);
142 if (!client) {
143 zlog_warn(
144 "%s: Not found zclient(proto=%u, instance=%u).",
145 __func__, c->proto, c->instance);
146 continue;
147 }
148 zsend_zebra_srv6_locator_delete(client, locator);
149 }
150
151 listnode_delete(srv6->locators, locator);
152 srv6_locator_free(locator);
153 }
154
155 struct srv6_locator *zebra_srv6_locator_lookup(const char *name)
156 {
157 struct zebra_srv6 *srv6 = zebra_srv6_get_default();
158 struct srv6_locator *locator;
159 struct listnode *node;
160
161 for (ALL_LIST_ELEMENTS_RO(srv6->locators, node, locator))
162 if (!strncmp(name, locator->name, SRV6_LOCNAME_SIZE))
163 return locator;
164 return NULL;
165 }
166
167 void zebra_notify_srv6_locator_add(struct srv6_locator *locator)
168 {
169 struct listnode *node;
170 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 for (ALL_LIST_ELEMENTS_RO(zrouter.client_list, node, client))
186 zsend_zebra_srv6_locator_add(client, locator);
187 }
188
189 void zebra_notify_srv6_locator_delete(struct srv6_locator *locator)
190 {
191 struct listnode *n;
192 struct srv6_locator_chunk *c;
193 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 for (ALL_LIST_ELEMENTS_RO((struct list *)locator->chunks, n, c)) {
207 if (c->proto == ZEBRA_ROUTE_SYSTEM)
208 continue;
209 client = zserv_find_client(c->proto, c->instance);
210 if (!client) {
211 zlog_warn("Not found zclient(proto=%u, instance=%u).",
212 c->proto, c->instance);
213 continue;
214 }
215 zsend_zebra_srv6_locator_delete(client, locator);
216 }
217 }
218
219 struct zebra_srv6 *zebra_srv6_get_default(void)
220 {
221 static struct zebra_srv6 srv6;
222 static bool first_execution = true;
223
224 if (first_execution) {
225 first_execution = false;
226 srv6.locators = list_new();
227 }
228 return &srv6;
229 }
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 {
250 bool chunk_found = false;
251 struct listnode *node = NULL;
252 struct srv6_locator *loc = NULL;
253 struct srv6_locator_chunk *chunk = NULL;
254
255 loc = zebra_srv6_locator_lookup(locator_name);
256 if (!loc) {
257 zlog_info("%s: locator %s was not found",
258 __func__, locator_name);
259 return NULL;
260 }
261
262 for (ALL_LIST_ELEMENTS_RO((struct list *)loc->chunks, node, chunk)) {
263 if (chunk->proto != NO_PROTO && chunk->proto != proto)
264 continue;
265 chunk_found = true;
266 break;
267 }
268
269 if (!chunk_found) {
270 zlog_info("%s: locator is already owned", __func__);
271 return NULL;
272 }
273
274 chunk->proto = proto;
275 chunk->instance = instance;
276 chunk->session_id = session_id;
277 return loc;
278 }
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 {
285 int ret = 0;
286
287 *loc = assign_srv6_locator_chunk(client->proto, client->instance,
288 client->session_id, locator_name);
289
290 if (!*loc)
291 zlog_err("Unable to assign locator chunk to %s instance %u",
292 zebra_route_string(client->proto), client->instance);
293 else if (IS_ZEBRA_DEBUG_PACKET)
294 zlog_info("Assigned locator chunk %s to %s instance %u",
295 (*loc)->name, zebra_route_string(client->proto),
296 client->instance);
297
298 if (*loc && (*loc)->status_up)
299 ret = zsend_srv6_manager_get_locator_chunk_response(client,
300 vrf_id,
301 *loc);
302 return ret;
303 }
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 {
318 int ret = -1;
319 struct listnode *node;
320 struct srv6_locator_chunk *chunk;
321 struct srv6_locator *loc = NULL;
322
323 loc = zebra_srv6_locator_lookup(locator_name);
324 if (!loc)
325 return -1;
326
327 if (IS_ZEBRA_DEBUG_PACKET)
328 zlog_debug("%s: Releasing srv6-locator on %s", __func__,
329 locator_name);
330
331 for (ALL_LIST_ELEMENTS_RO((struct list *)loc->chunks, node, chunk)) {
332 if (chunk->proto != proto ||
333 chunk->instance != instance ||
334 chunk->session_id != session_id)
335 continue;
336 chunk->proto = NO_PROTO;
337 chunk->instance = 0;
338 chunk->session_id = 0;
339 chunk->keep = 0;
340 ret = 0;
341 break;
342 }
343
344 if (ret != 0)
345 flog_err(EC_ZEBRA_SRV6M_UNRELEASED_LOCATOR_CHUNK,
346 "%s: SRv6 locator chunk not released", __func__);
347
348 return ret;
349 }
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 {
355 if (vrf_id != VRF_DEFAULT) {
356 zlog_err("SRv6 locator doesn't support vrf");
357 return -1;
358 }
359
360 return release_srv6_locator_chunk(client->proto, client->instance,
361 client->session_id, locator_name);
362 }
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 {
376 int ret;
377 int count = 0;
378 struct zebra_srv6 *srv6 = zebra_srv6_get_default();
379 struct listnode *loc_node;
380 struct listnode *chunk_node;
381 struct srv6_locator *loc;
382 struct srv6_locator_chunk *chunk;
383
384 if (IS_ZEBRA_DEBUG_PACKET)
385 zlog_debug("%s: Releasing chunks for client proto %s, instance %d, session %u",
386 __func__, zebra_route_string(client->proto),
387 client->instance, client->session_id);
388
389 for (ALL_LIST_ELEMENTS_RO(srv6->locators, loc_node, loc)) {
390 for (ALL_LIST_ELEMENTS_RO(loc->chunks, chunk_node, chunk)) {
391 if (chunk->proto == client->proto &&
392 chunk->instance == client->instance &&
393 chunk->session_id == client->session_id &&
394 chunk->keep == 0) {
395 ret = release_srv6_locator_chunk(
396 chunk->proto, chunk->instance,
397 chunk->session_id, loc->name);
398 if (ret == 0)
399 count++;
400 }
401 }
402 }
403
404 if (IS_ZEBRA_DEBUG_PACKET)
405 zlog_debug("%s: Released %d srv6-locator chunks",
406 __func__, count);
407
408 return count;
409 }
410
411 void zebra_srv6_init(void)
412 {
413 hook_register(zserv_client_close, zebra_srv6_cleanup);
414 hook_register(srv6_manager_get_chunk,
415 zebra_srv6_manager_get_locator_chunk);
416 hook_register(srv6_manager_release_chunk,
417 zebra_srv6_manager_release_locator_chunk);
418 }
419
420 bool zebra_srv6_is_enable(void)
421 {
422 struct zebra_srv6 *srv6 = zebra_srv6_get_default();
423
424 return listcount(srv6->locators);
425 }