Coverage Report

Created: 2026-01-25 06:17

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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
}