Coverage Report

Created: 2025-12-05 06:31

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/frr/zebra/zebra_srte.c
Line
Count
Source
1
// SPDX-License-Identifier: GPL-2.0-or-later
2
/* Zebra SR-TE code
3
 * Copyright (C) 2020  NetDEF, Inc.
4
 */
5
6
#include <zebra.h>
7
8
#include "lib/zclient.h"
9
#include "lib/lib_errors.h"
10
11
#include "zebra/zebra_srte.h"
12
#include "zebra/zebra_mpls.h"
13
#include "zebra/zebra_rnh.h"
14
#include "zebra/zapi_msg.h"
15
16
2
DEFINE_MTYPE_STATIC(ZEBRA, ZEBRA_SR_POLICY, "SR Policy");
17
2
18
2
static void zebra_sr_policy_deactivate(struct zebra_sr_policy *policy);
19
2
20
2
/* Generate rb-tree of SR Policy instances. */
21
2
static inline int
22
2
zebra_sr_policy_instance_compare(const struct zebra_sr_policy *a,
23
2
         const struct zebra_sr_policy *b)
24
2
{
25
0
  return sr_policy_compare(&a->endpoint, &b->endpoint, a->color,
26
0
         b->color);
27
0
}
28
RB_GENERATE(zebra_sr_policy_instance_head, zebra_sr_policy, entry,
29
      zebra_sr_policy_instance_compare)
30
31
struct zebra_sr_policy_instance_head zebra_sr_policy_instances =
32
  RB_INITIALIZER(&zebra_sr_policy_instances);
33
34
struct zebra_sr_policy *zebra_sr_policy_add(uint32_t color,
35
              struct ipaddr *endpoint, char *name)
36
0
{
37
0
  struct zebra_sr_policy *policy;
38
39
0
  policy = XCALLOC(MTYPE_ZEBRA_SR_POLICY, sizeof(*policy));
40
0
  policy->color = color;
41
0
  policy->endpoint = *endpoint;
42
0
  strlcpy(policy->name, name, sizeof(policy->name));
43
0
  policy->status = ZEBRA_SR_POLICY_DOWN;
44
0
  RB_INSERT(zebra_sr_policy_instance_head, &zebra_sr_policy_instances,
45
0
      policy);
46
47
0
  return policy;
48
0
}
49
50
void zebra_sr_policy_del(struct zebra_sr_policy *policy)
51
0
{
52
0
  if (policy->status == ZEBRA_SR_POLICY_UP)
53
0
    zebra_sr_policy_deactivate(policy);
54
0
  RB_REMOVE(zebra_sr_policy_instance_head, &zebra_sr_policy_instances,
55
0
      policy);
56
0
  XFREE(MTYPE_ZEBRA_SR_POLICY, policy);
57
0
}
58
59
struct zebra_sr_policy *zebra_sr_policy_find(uint32_t color,
60
               struct ipaddr *endpoint)
61
0
{
62
0
  struct zebra_sr_policy policy = {};
63
64
0
  policy.color = color;
65
0
  policy.endpoint = *endpoint;
66
0
  return RB_FIND(zebra_sr_policy_instance_head,
67
0
           &zebra_sr_policy_instances, &policy);
68
0
}
69
70
struct zebra_sr_policy *zebra_sr_policy_find_by_name(char *name)
71
0
{
72
0
  struct zebra_sr_policy *policy;
73
74
  // TODO: create index for policy names
75
0
  RB_FOREACH (policy, zebra_sr_policy_instance_head,
76
0
        &zebra_sr_policy_instances) {
77
0
    if (strcmp(policy->name, name) == 0)
78
0
      return policy;
79
0
  }
80
81
0
  return NULL;
82
0
}
83
84
static int zebra_sr_policy_notify_update_client(struct zebra_sr_policy *policy,
85
            struct zserv *client)
86
0
{
87
0
  const struct zebra_nhlfe *nhlfe;
88
0
  struct stream *s;
89
0
  uint32_t message = 0;
90
0
  unsigned long nump = 0;
91
0
  uint8_t num;
92
0
  struct zapi_nexthop znh;
93
0
  int ret;
94
95
  /* Get output stream. */
96
0
  s = stream_new(ZEBRA_MAX_PACKET_SIZ);
97
98
0
  zclient_create_header(s, ZEBRA_NEXTHOP_UPDATE, zvrf_id(policy->zvrf));
99
100
  /* Message flags. */
101
0
  SET_FLAG(message, ZAPI_MESSAGE_SRTE);
102
0
  stream_putl(s, message);
103
104
0
  stream_putw(s, SAFI_UNICAST);
105
  /*
106
   * The prefix is copied twice because the ZEBRA_NEXTHOP_UPDATE
107
   * code was modified to send back both the matched against
108
   * as well as the actual matched.  There does not appear to
109
   * be an equivalent here so just send the same thing twice.
110
   */
111
0
  switch (policy->endpoint.ipa_type) {
112
0
  case IPADDR_V4:
113
0
    stream_putw(s, AF_INET);
114
0
    stream_putc(s, IPV4_MAX_BITLEN);
115
0
    stream_put_in_addr(s, &policy->endpoint.ipaddr_v4);
116
0
    stream_putw(s, AF_INET);
117
0
    stream_putc(s, IPV4_MAX_BITLEN);
118
0
    stream_put_in_addr(s, &policy->endpoint.ipaddr_v4);
119
0
    break;
120
0
  case IPADDR_V6:
121
0
    stream_putw(s, AF_INET6);
122
0
    stream_putc(s, IPV6_MAX_BITLEN);
123
0
    stream_put(s, &policy->endpoint.ipaddr_v6, IPV6_MAX_BYTELEN);
124
0
    stream_putw(s, AF_INET6);
125
0
    stream_putc(s, IPV6_MAX_BITLEN);
126
0
    stream_put(s, &policy->endpoint.ipaddr_v6, IPV6_MAX_BYTELEN);
127
0
    break;
128
0
  case IPADDR_NONE:
129
0
    flog_warn(EC_LIB_DEVELOPMENT,
130
0
        "%s: unknown policy endpoint address family: %u",
131
0
        __func__, policy->endpoint.ipa_type);
132
0
    exit(1);
133
0
  }
134
0
  stream_putl(s, policy->color);
135
136
0
  num = 0;
137
0
  frr_each (nhlfe_list_const, &policy->lsp->nhlfe_list, nhlfe) {
138
0
    if (!CHECK_FLAG(nhlfe->flags, NHLFE_FLAG_SELECTED)
139
0
        || CHECK_FLAG(nhlfe->flags, NHLFE_FLAG_DELETED))
140
0
      continue;
141
142
0
    if (num == 0) {
143
0
      stream_putc(s, re_type_from_lsp_type(nhlfe->type));
144
0
      stream_putw(s, 0); /* instance - not available */
145
0
      stream_putc(s, nhlfe->distance);
146
0
      stream_putl(s, 0); /* metric - not available */
147
0
      nump = stream_get_endp(s);
148
0
      stream_putc(s, 0);
149
0
    }
150
151
0
    zapi_nexthop_from_nexthop(&znh, nhlfe->nexthop);
152
0
    ret = zapi_nexthop_encode(s, &znh, 0, message);
153
0
    if (ret < 0)
154
0
      goto failure;
155
156
0
    num++;
157
0
  }
158
0
  stream_putc_at(s, nump, num);
159
0
  stream_putw_at(s, 0, stream_get_endp(s));
160
161
0
  client->nh_last_upd_time = monotime(NULL);
162
0
  return zserv_send_message(client, s);
163
164
0
failure:
165
166
0
  stream_free(s);
167
0
  return -1;
168
0
}
169
170
static void zebra_sr_policy_notify_update(struct zebra_sr_policy *policy)
171
0
{
172
0
  struct rnh *rnh;
173
0
  struct prefix p = {};
174
0
  struct zebra_vrf *zvrf;
175
0
  struct listnode *node;
176
0
  struct zserv *client;
177
178
0
  zvrf = policy->zvrf;
179
0
  switch (policy->endpoint.ipa_type) {
180
0
  case IPADDR_V4:
181
0
    p.family = AF_INET;
182
0
    p.prefixlen = IPV4_MAX_BITLEN;
183
0
    p.u.prefix4 = policy->endpoint.ipaddr_v4;
184
0
    break;
185
0
  case IPADDR_V6:
186
0
    p.family = AF_INET6;
187
0
    p.prefixlen = IPV6_MAX_BITLEN;
188
0
    p.u.prefix6 = policy->endpoint.ipaddr_v6;
189
0
    break;
190
0
  case IPADDR_NONE:
191
0
    flog_warn(EC_LIB_DEVELOPMENT,
192
0
        "%s: unknown policy endpoint address family: %u",
193
0
        __func__, policy->endpoint.ipa_type);
194
0
    exit(1);
195
0
  }
196
197
0
  rnh = zebra_lookup_rnh(&p, zvrf_id(zvrf), SAFI_UNICAST);
198
0
  if (!rnh)
199
0
    return;
200
201
0
  for (ALL_LIST_ELEMENTS_RO(rnh->client_list, node, client)) {
202
0
    if (policy->status == ZEBRA_SR_POLICY_UP)
203
0
      zebra_sr_policy_notify_update_client(policy, client);
204
0
    else
205
      /* Fallback to the IGP shortest path. */
206
0
      zebra_send_rnh_update(rnh, client, zvrf_id(zvrf),
207
0
                policy->color);
208
0
  }
209
0
}
210
211
static void zebra_sr_policy_activate(struct zebra_sr_policy *policy,
212
             struct zebra_lsp *lsp)
213
0
{
214
0
  policy->status = ZEBRA_SR_POLICY_UP;
215
0
  policy->lsp = lsp;
216
0
  (void)zebra_sr_policy_bsid_install(policy);
217
0
  zsend_sr_policy_notify_status(policy->color, &policy->endpoint,
218
0
              policy->name, ZEBRA_SR_POLICY_UP);
219
0
  zebra_sr_policy_notify_update(policy);
220
0
}
221
222
static void zebra_sr_policy_update(struct zebra_sr_policy *policy,
223
           struct zebra_lsp *lsp,
224
           struct zapi_srte_tunnel *old_tunnel)
225
0
{
226
0
  bool bsid_changed;
227
0
  bool segment_list_changed;
228
229
0
  policy->lsp = lsp;
230
231
0
  bsid_changed =
232
0
    policy->segment_list.local_label != old_tunnel->local_label;
233
0
  segment_list_changed =
234
0
    policy->segment_list.label_num != old_tunnel->label_num
235
0
    || memcmp(policy->segment_list.labels, old_tunnel->labels,
236
0
        sizeof(mpls_label_t)
237
0
          * policy->segment_list.label_num);
238
239
  /* Re-install label stack if necessary. */
240
0
  if (bsid_changed || segment_list_changed) {
241
0
    zebra_sr_policy_bsid_uninstall(policy, old_tunnel->local_label);
242
0
    (void)zebra_sr_policy_bsid_install(policy);
243
0
  }
244
245
0
  zsend_sr_policy_notify_status(policy->color, &policy->endpoint,
246
0
              policy->name, ZEBRA_SR_POLICY_UP);
247
248
  /* Handle segment-list update. */
249
0
  if (segment_list_changed)
250
0
    zebra_sr_policy_notify_update(policy);
251
0
}
252
253
static void zebra_sr_policy_deactivate(struct zebra_sr_policy *policy)
254
0
{
255
0
  policy->status = ZEBRA_SR_POLICY_DOWN;
256
0
  policy->lsp = NULL;
257
0
  zebra_sr_policy_bsid_uninstall(policy,
258
0
               policy->segment_list.local_label);
259
0
  zsend_sr_policy_notify_status(policy->color, &policy->endpoint,
260
0
              policy->name, ZEBRA_SR_POLICY_DOWN);
261
0
  zebra_sr_policy_notify_update(policy);
262
0
}
263
264
int zebra_sr_policy_validate(struct zebra_sr_policy *policy,
265
           struct zapi_srte_tunnel *new_tunnel)
266
0
{
267
0
  struct zapi_srte_tunnel old_tunnel = policy->segment_list;
268
0
  struct zebra_lsp *lsp;
269
270
0
  if (new_tunnel)
271
0
    policy->segment_list = *new_tunnel;
272
273
  /* Try to resolve the Binding-SID nexthops. */
274
0
  lsp = mpls_lsp_find(policy->zvrf, policy->segment_list.labels[0]);
275
0
  if (!lsp || !lsp->best_nhlfe
276
0
      || lsp->addr_family != ipaddr_family(&policy->endpoint)) {
277
0
    if (policy->status == ZEBRA_SR_POLICY_UP)
278
0
      zebra_sr_policy_deactivate(policy);
279
0
    return -1;
280
0
  }
281
282
  /* First label was resolved successfully. */
283
0
  if (policy->status == ZEBRA_SR_POLICY_DOWN)
284
0
    zebra_sr_policy_activate(policy, lsp);
285
0
  else
286
0
    zebra_sr_policy_update(policy, lsp, &old_tunnel);
287
288
0
  return 0;
289
0
}
290
291
int zebra_sr_policy_bsid_install(struct zebra_sr_policy *policy)
292
0
{
293
0
  struct zapi_srte_tunnel *zt = &policy->segment_list;
294
0
  struct zebra_nhlfe *nhlfe;
295
296
0
  if (zt->local_label == MPLS_LABEL_NONE)
297
0
    return 0;
298
299
0
  frr_each_safe (nhlfe_list, &policy->lsp->nhlfe_list, nhlfe) {
300
0
    uint8_t num_out_labels;
301
0
    mpls_label_t *out_labels;
302
0
    mpls_label_t null_label = MPLS_LABEL_IMPLICIT_NULL;
303
304
0
    if (!CHECK_FLAG(nhlfe->flags, NHLFE_FLAG_SELECTED)
305
0
        || CHECK_FLAG(nhlfe->flags, NHLFE_FLAG_DELETED))
306
0
      continue;
307
308
    /*
309
     * Don't push the first SID if the corresponding action in the
310
     * LFIB is POP.
311
     */
312
0
    if (!nhlfe->nexthop->nh_label
313
0
        || !nhlfe->nexthop->nh_label->num_labels
314
0
        || nhlfe->nexthop->nh_label->label[0]
315
0
             == MPLS_LABEL_IMPLICIT_NULL) {
316
0
      if (zt->label_num > 1) {
317
0
        num_out_labels = zt->label_num - 1;
318
0
        out_labels = &zt->labels[1];
319
0
      } else {
320
0
        num_out_labels = 1;
321
0
        out_labels = &null_label;
322
0
      }
323
0
    } else {
324
0
      num_out_labels = zt->label_num;
325
0
      out_labels = zt->labels;
326
0
    }
327
328
0
    if (mpls_lsp_install(
329
0
          policy->zvrf, zt->type, zt->local_label,
330
0
          num_out_labels, out_labels, nhlfe->nexthop->type,
331
0
          &nhlfe->nexthop->gate, nhlfe->nexthop->ifindex)
332
0
        < 0)
333
0
      return -1;
334
0
  }
335
336
0
  return 0;
337
0
}
338
339
void zebra_sr_policy_bsid_uninstall(struct zebra_sr_policy *policy,
340
            mpls_label_t old_bsid)
341
0
{
342
0
  struct zapi_srte_tunnel *zt = &policy->segment_list;
343
344
0
  mpls_lsp_uninstall_all_vrf(policy->zvrf, zt->type, old_bsid);
345
0
}
346
347
int zebra_sr_policy_label_update(mpls_label_t label,
348
         enum zebra_sr_policy_update_label_mode mode)
349
0
{
350
0
  struct zebra_sr_policy *policy;
351
352
0
  RB_FOREACH (policy, zebra_sr_policy_instance_head,
353
0
        &zebra_sr_policy_instances) {
354
0
    mpls_label_t next_hop_label;
355
356
0
    next_hop_label = policy->segment_list.labels[0];
357
0
    if (next_hop_label != label)
358
0
      continue;
359
360
0
    switch (mode) {
361
0
    case ZEBRA_SR_POLICY_LABEL_CREATED:
362
0
    case ZEBRA_SR_POLICY_LABEL_UPDATED:
363
0
    case ZEBRA_SR_POLICY_LABEL_REMOVED:
364
0
      zebra_sr_policy_validate(policy, NULL);
365
0
      break;
366
0
    }
367
0
  }
368
369
0
  return 0;
370
0
}
371
372
static int zebra_srte_client_close_cleanup(struct zserv *client)
373
0
{
374
0
  int sock = client->sock;
375
0
  struct zebra_sr_policy *policy, *policy_temp;
376
377
0
  if (!sock)
378
0
    return 0;
379
380
0
  RB_FOREACH_SAFE (policy, zebra_sr_policy_instance_head,
381
0
       &zebra_sr_policy_instances, policy_temp) {
382
0
    if (policy->sock == sock)
383
0
      zebra_sr_policy_del(policy);
384
0
  }
385
0
  return 1;
386
0
}
387
388
void zebra_srte_init(void)
389
0
{
390
0
  hook_register(zserv_client_close, zebra_srte_client_close_cleanup);
391
0
}