Coverage Report

Created: 2026-01-01 06:18

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/frr/bgpd/bgp_ecommunity.c
Line
Count
Source
1
// SPDX-License-Identifier: GPL-2.0-or-later
2
/* BGP Extended Communities Attribute
3
 * Copyright (C) 2000 Kunihiro Ishiguro <kunihiro@zebra.org>
4
 */
5
6
#include <zebra.h>
7
8
#include "hash.h"
9
#include "memory.h"
10
#include "prefix.h"
11
#include "command.h"
12
#include "queue.h"
13
#include "filter.h"
14
#include "jhash.h"
15
#include "stream.h"
16
17
#include "lib/printfrr.h"
18
19
#include "bgpd/bgpd.h"
20
#include "bgpd/bgp_ecommunity.h"
21
#include "bgpd/bgp_lcommunity.h"
22
#include "bgpd/bgp_aspath.h"
23
#include "bgpd/bgp_flowspec_private.h"
24
#include "bgpd/bgp_pbr.h"
25
26
/* struct used to dump the rate contained in FS set traffic-rate EC */
27
union traffic_rate {
28
  float rate_float;
29
  uint8_t rate_byte[4];
30
};
31
32
/* Hash of community attribute. */
33
static struct hash *ecomhash;
34
35
/* Allocate a new ecommunities.  */
36
struct ecommunity *ecommunity_new(void)
37
139
{
38
139
  struct ecommunity *ecom;
39
40
139
  ecom = (struct ecommunity *)XCALLOC(MTYPE_ECOMMUNITY,
41
139
              sizeof(struct ecommunity));
42
139
  ecom->unit_size = ECOMMUNITY_SIZE;
43
139
  return ecom;
44
139
}
45
46
void ecommunity_strfree(char **s)
47
0
{
48
0
  XFREE(MTYPE_ECOMMUNITY_STR, *s);
49
0
}
50
51
/* Free ecommunities.  */
52
void ecommunity_free(struct ecommunity **ecom)
53
139
{
54
139
  if (!(*ecom))
55
0
    return;
56
57
139
  XFREE(MTYPE_ECOMMUNITY_VAL, (*ecom)->val);
58
139
  XFREE(MTYPE_ECOMMUNITY_STR, (*ecom)->str);
59
139
  XFREE(MTYPE_ECOMMUNITY, *ecom);
60
139
}
61
62
static void ecommunity_hash_free(struct ecommunity *ecom)
63
0
{
64
0
  ecommunity_free(&ecom);
65
0
}
66
67
68
/* Add a new Extended Communities value to Extended Communities
69
   Attribute structure.  When the value is already exists in the
70
   structure, we don't add the value.  Newly added value is sorted by
71
   numerical order.  When the value is added to the structure return 1
72
   else return 0.
73
   The additional parameters 'unique' and 'overwrite' ensure a particular
74
   extended community (based on type and sub-type) is present only
75
   once and whether the new value should replace what is existing or
76
   not.
77
*/
78
static bool ecommunity_add_val_internal(struct ecommunity *ecom,
79
          const void *eval,
80
          bool unique, bool overwrite,
81
          uint8_t ecom_size)
82
13.3k
{
83
13.3k
  uint32_t c, ins_idx;
84
13.3k
  const struct ecommunity_val *eval4 = (struct ecommunity_val *)eval;
85
13.3k
  const struct ecommunity_val_ipv6 *eval6 =
86
13.3k
    (struct ecommunity_val_ipv6 *)eval;
87
88
  /* When this is fist value, just add it. */
89
13.3k
  if (ecom->val == NULL) {
90
139
    ecom->size = 1;
91
139
    ecom->val = XMALLOC(MTYPE_ECOMMUNITY_VAL,
92
139
            ecom_length_size(ecom, ecom_size));
93
139
    memcpy(ecom->val, eval, ecom_size);
94
139
    return true;
95
139
  }
96
97
  /* If the value already exists in the structure return 0.  */
98
  /* check also if the extended community itself exists. */
99
13.1k
  c = 0;
100
101
13.1k
  ins_idx = UINT32_MAX;
102
512k
  for (uint8_t *p = ecom->val; c < ecom->size;
103
511k
       p += ecom_size, c++) {
104
511k
    if (unique) {
105
0
      if (ecom_size == ECOMMUNITY_SIZE) {
106
0
        if (p[0] == eval4->val[0] &&
107
0
            p[1] == eval4->val[1]) {
108
0
          if (overwrite) {
109
0
            memcpy(p, eval4->val,
110
0
                   ecom_size);
111
0
            return true;
112
0
          }
113
0
          return false;
114
0
        }
115
0
      } else {
116
0
        if (p[0] == eval6->val[0] &&
117
0
            p[1] == eval6->val[1]) {
118
0
          if (overwrite) {
119
0
            memcpy(p, eval6->val,
120
0
                   ecom_size);
121
0
            return true;
122
0
          }
123
0
          return false;
124
0
        }
125
0
      }
126
0
    }
127
511k
    int ret = memcmp(p, eval, ecom_size);
128
511k
    if (ret == 0)
129
1.60k
      return false;
130
510k
    if (ret > 0) {
131
11.1k
      if (!unique)
132
11.1k
        break;
133
0
      if (ins_idx == UINT32_MAX)
134
0
        ins_idx = c;
135
0
    }
136
510k
  }
137
138
11.5k
  if (ins_idx == UINT32_MAX)
139
11.5k
    ins_idx = c;
140
141
  /* Add the value to the structure with numerical sorting.  */
142
11.5k
  ecom->size++;
143
11.5k
  ecom->val = XREALLOC(MTYPE_ECOMMUNITY_VAL, ecom->val,
144
11.5k
       ecom_length_size(ecom, ecom_size));
145
146
11.5k
  memmove(ecom->val + ((ins_idx + 1) * ecom_size),
147
11.5k
    ecom->val + (ins_idx * ecom_size),
148
11.5k
    (ecom->size - 1 - ins_idx) * ecom_size);
149
11.5k
  memcpy(ecom->val + (ins_idx * ecom_size),
150
11.5k
         eval, ecom_size);
151
152
11.5k
  return true;
153
13.1k
}
154
155
/* Add a new Extended Communities value to Extended Communities
156
 * Attribute structure.  When the value is already exists in the
157
 * structure, we don't add the value.  Newly added value is sorted by
158
 * numerical order.  When the value is added to the structure return 1
159
 * else return 0.
160
 */
161
bool ecommunity_add_val(struct ecommunity *ecom, struct ecommunity_val *eval,
162
           bool unique, bool overwrite)
163
0
{
164
0
  return ecommunity_add_val_internal(ecom, (const void *)eval, unique,
165
0
             overwrite, ECOMMUNITY_SIZE);
166
0
}
167
168
bool ecommunity_add_val_ipv6(struct ecommunity *ecom,
169
           struct ecommunity_val_ipv6 *eval,
170
           bool unique, bool overwrite)
171
0
{
172
0
  return ecommunity_add_val_internal(ecom, (const void *)eval, unique,
173
0
             overwrite, IPV6_ECOMMUNITY_SIZE);
174
0
}
175
176
static struct ecommunity *
177
ecommunity_uniq_sort_internal(struct ecommunity *ecom,
178
            unsigned short ecom_size)
179
139
{
180
139
  uint32_t i;
181
139
  struct ecommunity *new;
182
139
  const void *eval;
183
184
139
  if (!ecom)
185
0
    return NULL;
186
187
139
  new = ecommunity_new();
188
139
  new->unit_size = ecom_size;
189
139
  new->disable_ieee_floating = ecom->disable_ieee_floating;
190
191
13.4k
  for (i = 0; i < ecom->size; i++) {
192
13.3k
    eval = (void *)(ecom->val + (i * ecom_size));
193
13.3k
    ecommunity_add_val_internal(new, eval, false, false, ecom_size);
194
13.3k
  }
195
139
  return new;
196
139
}
197
198
/* This function takes pointer to Extended Communites structure then
199
 * create a new Extended Communities structure by uniq and sort each
200
 * Extended Communities value.
201
 */
202
struct ecommunity *ecommunity_uniq_sort(struct ecommunity *ecom)
203
0
{
204
0
  return ecommunity_uniq_sort_internal(ecom, ECOMMUNITY_SIZE);
205
0
}
206
207
/* Parse Extended Communites Attribute in BGP packet.  */
208
static struct ecommunity *ecommunity_parse_internal(uint8_t *pnt,
209
                unsigned short length,
210
                unsigned short size_ecom,
211
                bool disable_ieee_floating)
212
143
{
213
143
  struct ecommunity tmp;
214
143
  struct ecommunity *new;
215
216
  /* Length check.  */
217
143
  if (length % size_ecom)
218
4
    return NULL;
219
220
  /* Prepare tmporary structure for making a new Extended Communities
221
     Attribute.  */
222
139
  tmp.size = length / size_ecom;
223
139
  tmp.val = pnt;
224
139
  tmp.disable_ieee_floating = disable_ieee_floating;
225
226
  /* Create a new Extended Communities Attribute by uniq and sort each
227
     Extended Communities value  */
228
139
  new = ecommunity_uniq_sort_internal(&tmp, size_ecom);
229
230
139
  return ecommunity_intern(new);
231
143
}
232
233
struct ecommunity *ecommunity_parse(uint8_t *pnt, unsigned short length,
234
            bool disable_ieee_floating)
235
109
{
236
109
  return ecommunity_parse_internal(pnt, length, ECOMMUNITY_SIZE,
237
109
           disable_ieee_floating);
238
109
}
239
240
struct ecommunity *ecommunity_parse_ipv6(uint8_t *pnt, unsigned short length,
241
           bool disable_ieee_floating)
242
34
{
243
34
  return ecommunity_parse_internal(pnt, length, IPV6_ECOMMUNITY_SIZE,
244
34
           disable_ieee_floating);
245
34
}
246
247
/* Duplicate the Extended Communities Attribute structure.  */
248
struct ecommunity *ecommunity_dup(struct ecommunity *ecom)
249
0
{
250
0
  struct ecommunity *new;
251
252
0
  new = XCALLOC(MTYPE_ECOMMUNITY, sizeof(struct ecommunity));
253
0
  new->size = ecom->size;
254
0
  new->unit_size = ecom->unit_size;
255
0
  if (new->size) {
256
0
    new->val = XMALLOC(MTYPE_ECOMMUNITY_VAL,
257
0
           ecom->size * ecom->unit_size);
258
0
    memcpy(new->val, ecom->val,
259
0
           (size_t)ecom->size * (size_t)ecom->unit_size);
260
0
  } else
261
0
    new->val = NULL;
262
0
  return new;
263
0
}
264
265
/* Return string representation of ecommunities attribute. */
266
char *ecommunity_str(struct ecommunity *ecom)
267
24
{
268
24
  if (!ecom->str)
269
0
    ecom->str =
270
0
      ecommunity_ecom2str(ecom, ECOMMUNITY_FORMAT_DISPLAY, 0);
271
24
  return ecom->str;
272
24
}
273
274
/* Merge two Extended Communities Attribute structure.  */
275
struct ecommunity *ecommunity_merge(struct ecommunity *ecom1,
276
            struct ecommunity *ecom2)
277
0
{
278
0
  ecom1->val = XREALLOC(MTYPE_ECOMMUNITY_VAL, ecom1->val,
279
0
            (size_t)(ecom1->size + ecom2->size)
280
0
              * (size_t)ecom1->unit_size);
281
282
0
  memcpy(ecom1->val + (ecom1->size * ecom1->unit_size), ecom2->val,
283
0
         (size_t)ecom2->size * (size_t)ecom1->unit_size);
284
0
  ecom1->size += ecom2->size;
285
286
0
  return ecom1;
287
0
}
288
289
/* Intern Extended Communities Attribute.  */
290
struct ecommunity *ecommunity_intern(struct ecommunity *ecom)
291
139
{
292
139
  struct ecommunity *find;
293
294
139
  assert(ecom->refcnt == 0);
295
139
  find = (struct ecommunity *)hash_get(ecomhash, ecom, hash_alloc_intern);
296
139
  if (find != ecom)
297
0
    ecommunity_free(&ecom);
298
299
139
  find->refcnt++;
300
301
139
  if (!find->str)
302
139
    find->str =
303
139
      ecommunity_ecom2str(find, ECOMMUNITY_FORMAT_DISPLAY, 0);
304
305
139
  return find;
306
139
}
307
308
/* Unintern Extended Communities Attribute.  */
309
void ecommunity_unintern(struct ecommunity **ecom)
310
1.02k
{
311
1.02k
  struct ecommunity *ret;
312
313
1.02k
  if (!*ecom)
314
885
    return;
315
316
139
  if ((*ecom)->refcnt)
317
139
    (*ecom)->refcnt--;
318
319
  /* Pull off from hash.  */
320
139
  if ((*ecom)->refcnt == 0) {
321
    /* Extended community must be in the hash.  */
322
139
    ret = (struct ecommunity *)hash_release(ecomhash, *ecom);
323
139
    assert(ret != NULL);
324
325
139
    ecommunity_free(ecom);
326
139
  }
327
139
}
328
329
/* Utinity function to make hash key.  */
330
unsigned int ecommunity_hash_make(const void *arg)
331
278
{
332
278
  const struct ecommunity *ecom = arg;
333
278
  int size = ecom->size * ecom->unit_size;
334
335
278
  return jhash(ecom->val, size, 0x564321ab);
336
278
}
337
338
/* Compare two Extended Communities Attribute structure.  */
339
bool ecommunity_cmp(const void *arg1, const void *arg2)
340
139
{
341
139
  const struct ecommunity *ecom1 = arg1;
342
139
  const struct ecommunity *ecom2 = arg2;
343
344
139
  if (ecom1 == NULL && ecom2 == NULL)
345
0
    return true;
346
347
139
  if (ecom1 == NULL || ecom2 == NULL)
348
0
    return false;
349
350
139
  if (ecom1->unit_size != ecom2->unit_size)
351
0
    return false;
352
353
139
  return (ecom1->size == ecom2->size
354
139
    && memcmp(ecom1->val, ecom2->val, ecom1->size *
355
139
        ecom1->unit_size) == 0);
356
139
}
357
358
/* Initialize Extended Comminities related hash. */
359
void ecommunity_init(void)
360
1
{
361
1
  ecomhash = hash_create(ecommunity_hash_make, ecommunity_cmp,
362
1
             "BGP ecommunity hash");
363
1
}
364
365
void ecommunity_finish(void)
366
0
{
367
0
  hash_clean_and_free(&ecomhash, (void (*)(void *))ecommunity_hash_free);
368
0
}
369
370
/* Extended Communities token enum. */
371
enum ecommunity_token {
372
  ecommunity_token_unknown = 0,
373
  ecommunity_token_rt,
374
  ecommunity_token_nt,
375
  ecommunity_token_soo,
376
  ecommunity_token_val,
377
  ecommunity_token_rt6,
378
  ecommunity_token_val6,
379
};
380
381
static const char *ecommunity_origin_validation_state2str(
382
  enum ecommunity_origin_validation_states state)
383
28
{
384
28
  switch (state) {
385
1
  case ECOMMUNITY_ORIGIN_VALIDATION_STATE_VALID:
386
1
    return "valid";
387
0
  case ECOMMUNITY_ORIGIN_VALIDATION_STATE_NOTFOUND:
388
0
    return "not-found";
389
0
  case ECOMMUNITY_ORIGIN_VALIDATION_STATE_INVALID:
390
0
    return "invalid";
391
0
  case ECOMMUNITY_ORIGIN_VALIDATION_STATE_NOTUSED:
392
0
    return "not-used";
393
28
  }
394
395
27
  return "ERROR";
396
28
}
397
398
static void ecommunity_origin_validation_state_str(char *buf, size_t bufsz,
399
               uint8_t *ptr)
400
28
{
401
  /* Origin Validation State is encoded in the last octet
402
   *
403
   * 0                   1                   2                   3
404
   * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
405
   * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
406
   * |       0x43    |      0x00     |             Reserved          |
407
   * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
408
   * |                    Reserved                   |validationstate|
409
   * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
410
   */
411
28
  uint8_t state = *(ptr + ECOMMUNITY_SIZE - 3);
412
413
28
  snprintf(buf, bufsz, "OVS:%s",
414
28
     ecommunity_origin_validation_state2str(state));
415
416
28
  (void)ptr; /* consume value */
417
28
}
418
419
bool ecommunity_node_target_match(struct ecommunity *ecom,
420
          struct in_addr *local_id)
421
191
{
422
191
  uint32_t i;
423
191
  bool match = false;
424
425
191
  if (!ecom || !ecom->size)
426
0
    return NULL;
427
428
9.82k
  for (i = 0; i < ecom->size; i++) {
429
9.62k
    const uint8_t *pnt;
430
9.62k
    uint8_t type, sub_type;
431
432
9.62k
    pnt = (ecom->val + (i * ECOMMUNITY_SIZE));
433
9.62k
    type = *pnt++;
434
9.62k
    sub_type = *pnt++;
435
436
9.62k
    if (type == ECOMMUNITY_ENCODE_IP &&
437
684
        sub_type == ECOMMUNITY_NODE_TARGET) {
438
      /* Node Target ID is encoded as A.B.C.D:0 */
439
400
      if (IPV4_ADDR_SAME((struct in_addr *)pnt, local_id))
440
150
        match = true;
441
400
      (void)pnt;
442
400
    }
443
9.62k
  }
444
445
191
  return match;
446
191
}
447
448
static void ecommunity_node_target_str(char *buf, size_t bufsz, uint8_t *ptr,
449
               int format)
450
56
{
451
  /*
452
   *  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
453
   *  | 0x01 or 0x41 | Sub-Type(0x09) |    Target BGP Identifier      |
454
   *  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
455
   *  | Target BGP Identifier (cont.) |           Reserved            |
456
   *  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
457
   */
458
56
  struct in_addr node_id = {};
459
460
56
  IPV4_ADDR_COPY(&node_id, (struct in_addr *)ptr);
461
462
463
56
  snprintfrr(buf, bufsz, "%s%pI4%s",
464
56
       format == ECOMMUNITY_FORMAT_COMMUNITY_LIST ? "nt " : "NT:",
465
56
       &node_id,
466
56
       format == ECOMMUNITY_FORMAT_COMMUNITY_LIST ? ":0" : "");
467
468
56
  (void)ptr; /* consume value */
469
56
}
470
471
static int ecommunity_encode_internal(uint8_t type, uint8_t sub_type,
472
              int trans, as_t as,
473
              struct in_addr *ip,
474
              struct in6_addr *ip6,
475
              uint32_t val,
476
              void *eval_ptr)
477
0
{
478
0
  struct ecommunity_val *eval = (struct ecommunity_val *)eval_ptr;
479
0
  struct ecommunity_val_ipv6 *eval6 =
480
0
    (struct ecommunity_val_ipv6 *)eval_ptr;
481
482
0
  assert(eval);
483
0
  if (type == ECOMMUNITY_ENCODE_AS) {
484
0
    if (as > BGP_AS_MAX)
485
0
      return -1;
486
0
  } else if (type == ECOMMUNITY_ENCODE_IP
487
0
       || type == ECOMMUNITY_ENCODE_AS4) {
488
0
    if (val > UINT16_MAX)
489
0
      return -1;
490
0
  } else if (type == ECOMMUNITY_ENCODE_TRANS_EXP &&
491
0
       sub_type == ECOMMUNITY_FLOWSPEC_REDIRECT_IPV6 &&
492
0
       (!ip6 || val > UINT16_MAX)) {
493
0
    return -1;
494
0
  }
495
496
  /* Fill in the values. */
497
0
  eval->val[0] = type;
498
0
  if (!trans)
499
0
    eval->val[0] |= ECOMMUNITY_FLAG_NON_TRANSITIVE;
500
0
  eval->val[1] = sub_type;
501
0
  if (type == ECOMMUNITY_ENCODE_AS) {
502
0
    encode_route_target_as(as, val, eval, trans);
503
0
  } else if (type == ECOMMUNITY_ENCODE_IP) {
504
0
    if (sub_type == ECOMMUNITY_NODE_TARGET)
505
0
      encode_node_target(ip, eval, trans);
506
0
    else
507
0
      encode_route_target_ip(ip, val, eval, trans);
508
0
  } else if (type == ECOMMUNITY_ENCODE_TRANS_EXP &&
509
0
       sub_type == ECOMMUNITY_FLOWSPEC_REDIRECT_IPV6) {
510
0
    memcpy(&eval6->val[2], ip6, sizeof(struct in6_addr));
511
0
    eval6->val[18] = (val >> 8) & 0xff;
512
0
    eval6->val[19] = val & 0xff;
513
0
  } else {
514
0
    encode_route_target_as4(as, val, eval, trans);
515
0
  }
516
517
0
  return 0;
518
0
}
519
520
/*
521
 * Encode BGP extended community from passed values. Supports types
522
 * defined in RFC 4360 and well-known sub-types.
523
 */
524
static int ecommunity_encode(uint8_t type, uint8_t sub_type, int trans, as_t as,
525
           struct in_addr ip, uint32_t val,
526
           struct ecommunity_val *eval)
527
0
{
528
0
  return ecommunity_encode_internal(type, sub_type, trans, as,
529
0
            &ip, NULL, val, (void *)eval);
530
0
}
531
532
/* Get next Extended Communities token from the string. */
533
static const char *ecommunity_gettoken(const char *str, void *eval_ptr,
534
               enum ecommunity_token *token, int type)
535
0
{
536
0
  int ret;
537
0
  int dot = 0;
538
0
  int digit = 0;
539
0
  int separator = 0;
540
0
  const char *p = str;
541
0
  char *endptr;
542
0
  struct in_addr ip;
543
0
  struct in6_addr ip6;
544
0
  as_t as = 0;
545
0
  uint32_t val = 0;
546
0
  uint8_t ecomm_type;
547
0
  char buf[INET_ADDRSTRLEN + 1];
548
0
  struct ecommunity_val *eval = (struct ecommunity_val *)eval_ptr;
549
0
  uint64_t tmp_as = 0;
550
551
  /* Skip white space. */
552
0
  while (isspace((unsigned char)*p)) {
553
0
    p++;
554
0
    str++;
555
0
  }
556
557
  /* Check the end of the line. */
558
0
  if (*p == '\0')
559
0
    return NULL;
560
561
  /* "rt", "nt", and "soo" keyword parse. */
562
0
  if (!isdigit((unsigned char)*p)) {
563
    /* "rt" match check.  */
564
0
    if (tolower((unsigned char)*p) == 'r') {
565
0
      p++;
566
0
      if (tolower((unsigned char)*p) == 't') {
567
0
        p++;
568
0
        if (*p != '\0' && tolower((int)*p) == '6')
569
0
          *token = ecommunity_token_rt6;
570
0
        else
571
0
          *token = ecommunity_token_rt;
572
0
        return p;
573
0
      }
574
0
      if (isspace((unsigned char)*p) || *p == '\0') {
575
0
        *token = ecommunity_token_rt;
576
0
        return p;
577
0
      }
578
0
      goto error;
579
0
    }
580
    /* "nt" match check. */
581
0
    if (tolower((unsigned char)*p) == 'n') {
582
0
      p++;
583
0
      if (tolower((unsigned char)*p) == 't') {
584
0
        p++;
585
0
        *token = ecommunity_token_nt;
586
0
        return p;
587
0
      }
588
0
      if (isspace((unsigned char)*p) || *p == '\0') {
589
0
        *token = ecommunity_token_nt;
590
0
        return p;
591
0
      }
592
0
      goto error;
593
0
    }
594
    /* "soo" match check.  */
595
0
    else if (tolower((unsigned char)*p) == 's') {
596
0
      p++;
597
0
      if (tolower((unsigned char)*p) == 'o') {
598
0
        p++;
599
0
        if (tolower((unsigned char)*p) == 'o') {
600
0
          p++;
601
0
          *token = ecommunity_token_soo;
602
0
          return p;
603
0
        }
604
0
        if (isspace((unsigned char)*p) || *p == '\0') {
605
0
          *token = ecommunity_token_soo;
606
0
          return p;
607
0
        }
608
0
        goto error;
609
0
      }
610
0
      if (isspace((unsigned char)*p) || *p == '\0') {
611
0
        *token = ecommunity_token_soo;
612
0
        return p;
613
0
      }
614
0
      goto error;
615
0
    }
616
0
    goto error;
617
0
  }
618
619
  /* What a mess, there are several possibilities:
620
   *
621
   * a) A.B.C.D:MN
622
   * b) EF:OPQR
623
   * c) GHJK:MN
624
   * d) <IPV6>:MN (only with rt6)
625
   *
626
   * A.B.C.D: Four Byte IP
627
   * EF:      Two byte ASN
628
   * GHJK:    Four-byte ASN
629
   * MN:      Two byte value
630
   * OPQR:    Four byte value
631
   *
632
   */
633
  /* IPv6 case : look for last ':' */
634
0
  if (*token == ecommunity_token_rt6 ||
635
0
      *token == ecommunity_token_val6) {
636
0
    char *limit;
637
638
0
    limit = endptr = strrchr(p, ':');
639
0
    if (!endptr)
640
0
      goto error;
641
642
0
    endptr++;
643
0
    errno = 0;
644
0
    tmp_as = strtoul(endptr, &endptr, 10);
645
    /* 'unsigned long' is a uint64 on 64-bit
646
     * systems, and uint32 on 32-bit systems. So for
647
     * 64-bit we can just directly check the value
648
     * against BGP_AS4_MAX/UINT32_MAX, and for
649
     * 32-bit we can check for errno (set to ERANGE
650
     * upon overflow).
651
     */
652
0
    if (*endptr != '\0' || tmp_as == BGP_AS4_MAX || errno)
653
0
      goto error;
654
0
    as = (as_t)tmp_as;
655
656
0
    memcpy(buf, p, (limit - p));
657
0
    buf[limit - p] = '\0';
658
0
    ret = inet_pton(AF_INET6, buf, &ip6);
659
0
    if (ret == 0)
660
0
      goto error;
661
662
0
    ecomm_type = ECOMMUNITY_ENCODE_TRANS_EXP;
663
0
    if (ecommunity_encode_internal(ecomm_type,
664
0
          ECOMMUNITY_FLOWSPEC_REDIRECT_IPV6,
665
0
          1, 0, NULL, &ip6, as, eval_ptr))
666
0
      goto error;
667
668
0
    *token = ecommunity_token_val6;
669
0
    while (isdigit((int)*p) || *p == ':' || *p == '.') {
670
0
      p++;
671
0
    }
672
0
    return p;
673
0
  }
674
0
  while (isdigit((unsigned char)*p) || *p == ':' || *p == '.') {
675
0
    if (*p == ':') {
676
0
      if (separator)
677
0
        goto error;
678
679
0
      separator = 1;
680
0
      digit = 0;
681
682
0
      if ((p - str) > INET_ADDRSTRLEN)
683
0
        goto error;
684
0
      memset(buf, 0, INET_ADDRSTRLEN + 1);
685
0
      memcpy(buf, str, p - str);
686
687
0
      if (dot) {
688
        /* Parsing A.B.C.D in:
689
         * A.B.C.D:MN
690
         */
691
0
        ret = inet_aton(buf, &ip);
692
0
        if (ret == 0)
693
0
          goto error;
694
0
      } else {
695
        /* ASN */
696
0
        errno = 0;
697
0
        tmp_as = strtoul(buf, &endptr, 10);
698
        /* 'unsigned long' is a uint64 on 64-bit
699
         * systems, and uint32 on 32-bit systems. So for
700
         * 64-bit we can just directly check the value
701
         * against BGP_AS4_MAX/UINT32_MAX, and for
702
         * 32-bit we can check for errno (set to ERANGE
703
         * upon overflow).
704
         */
705
0
        if (*endptr != '\0' || tmp_as > BGP_AS4_MAX ||
706
0
            errno)
707
0
          goto error;
708
0
        as = (as_t)tmp_as;
709
0
      }
710
0
    } else if (*p == '.') {
711
0
      if (separator)
712
0
        goto error;
713
0
      dot++;
714
0
      if (dot > 4)
715
0
        goto error;
716
0
    } else {
717
0
      digit = 1;
718
719
      /* We're past the IP/ASN part */
720
0
      if (separator) {
721
0
        val *= 10;
722
0
        val += (*p - '0');
723
0
      }
724
0
    }
725
0
    p++;
726
0
  }
727
728
  /* Low digit part must be there. */
729
0
  if (!digit || !separator)
730
0
    goto error;
731
732
  /* Encode result into extended community.  */
733
0
  if (dot)
734
0
    ecomm_type = ECOMMUNITY_ENCODE_IP;
735
0
  else if (as > BGP_AS_MAX)
736
0
    ecomm_type = ECOMMUNITY_ENCODE_AS4;
737
0
  else
738
0
    ecomm_type = ECOMMUNITY_ENCODE_AS;
739
0
  if (ecommunity_encode(ecomm_type, type, 1, as, ip, val, eval))
740
0
    goto error;
741
0
  *token = ecommunity_token_val;
742
0
  return p;
743
744
0
error:
745
0
  *token = ecommunity_token_unknown;
746
0
  return p;
747
0
}
748
749
static struct ecommunity *ecommunity_str2com_internal(const char *str, int type,
750
                  int keyword_included,
751
                  bool is_ipv6_extcomm)
752
0
{
753
0
  struct ecommunity *ecom = NULL;
754
0
  enum ecommunity_token token = ecommunity_token_unknown;
755
0
  struct ecommunity_val_ipv6 eval;
756
0
  int keyword = 0;
757
758
0
  if (is_ipv6_extcomm)
759
0
    token = ecommunity_token_rt6;
760
0
  while ((str = ecommunity_gettoken(str, (void *)&eval, &token, type))) {
761
0
    switch (token) {
762
0
    case ecommunity_token_rt:
763
0
    case ecommunity_token_nt:
764
0
    case ecommunity_token_rt6:
765
0
    case ecommunity_token_soo:
766
0
      if (!keyword_included || keyword) {
767
0
        if (ecom)
768
0
          ecommunity_free(&ecom);
769
0
        return NULL;
770
0
      }
771
0
      keyword = 1;
772
773
0
      if (token == ecommunity_token_rt ||
774
0
          token == ecommunity_token_rt6) {
775
0
        type = ECOMMUNITY_ROUTE_TARGET;
776
0
      }
777
0
      if (token == ecommunity_token_soo) {
778
0
        type = ECOMMUNITY_SITE_ORIGIN;
779
0
      }
780
0
      if (token == ecommunity_token_nt) {
781
0
        type = ECOMMUNITY_NODE_TARGET;
782
0
      }
783
0
      break;
784
0
    case ecommunity_token_val:
785
0
      if (keyword_included) {
786
0
        if (!keyword) {
787
0
          ecommunity_free(&ecom);
788
0
          return NULL;
789
0
        }
790
0
        keyword = 0;
791
0
      }
792
0
      if (ecom == NULL)
793
0
        ecom = ecommunity_new();
794
0
      eval.val[1] = type;
795
0
      ecommunity_add_val_internal(ecom, (void *)&eval,
796
0
                false, false,
797
0
                ecom->unit_size);
798
0
      break;
799
0
    case ecommunity_token_val6:
800
0
      if (keyword_included) {
801
0
        if (!keyword) {
802
0
          ecommunity_free(&ecom);
803
0
          return NULL;
804
0
        }
805
0
        keyword = 0;
806
0
      }
807
0
      if (ecom == NULL)
808
0
        ecom = ecommunity_new();
809
0
      ecom->unit_size = IPV6_ECOMMUNITY_SIZE;
810
0
      eval.val[1] = type;
811
0
      ecommunity_add_val_internal(ecom, (void *)&eval, false, false,
812
0
                ecom->unit_size);
813
0
      break;
814
0
    case ecommunity_token_unknown:
815
0
      if (ecom)
816
0
        ecommunity_free(&ecom);
817
0
      return NULL;
818
0
    }
819
0
  }
820
0
  return ecom;
821
0
}
822
823
/* Convert string to extended community attribute.
824
 *
825
 * When type is already known, please specify both str and type.  str
826
 * should not include keyword such as "rt" and "soo".  Type is
827
 * ECOMMUNITY_ROUTE_TARGET or ECOMMUNITY_SITE_ORIGIN.
828
 * keyword_included should be zero.
829
 *
830
 * For example route-map's "set extcommunity" command case:
831
 *
832
 * "rt 100:1 100:2 100:3"        -> str = "100:1 100:2 100:3"
833
 *            type = ECOMMUNITY_ROUTE_TARGET
834
 *            keyword_included = 0
835
 *
836
 * "soo 100:1"                   -> str = "100:1"
837
 *            type = ECOMMUNITY_SITE_ORIGIN
838
 *            keyword_included = 0
839
 *
840
 * When string includes keyword for each extended community value.
841
 * Please specify keyword_included as non-zero value.
842
 *
843
 * For example standard extcommunity-list case:
844
 *
845
 * "rt 100:1 rt 100:2 soo 100:1" -> str = "rt 100:1 rt 100:2 soo 100:1"
846
 *            type = 0
847
 *            keyword_include = 1
848
 */
849
struct ecommunity *ecommunity_str2com(const char *str, int type,
850
              int keyword_included)
851
0
{
852
0
  return ecommunity_str2com_internal(str, type,
853
0
             keyword_included, false);
854
0
}
855
856
struct ecommunity *ecommunity_str2com_ipv6(const char *str, int type,
857
             int keyword_included)
858
0
{
859
0
  return ecommunity_str2com_internal(str, type,
860
0
             keyword_included, true);
861
0
}
862
863
static int ecommunity_rt_soo_str_internal(char *buf, size_t bufsz,
864
            const uint8_t *pnt, int type,
865
            int sub_type, int format,
866
            unsigned short ecom_size)
867
502
{
868
502
  int len = 0;
869
502
  const char *prefix;
870
502
  char buf_local[INET6_ADDRSTRLEN];
871
872
  /* For parse Extended Community attribute tupple. */
873
502
  struct ecommunity_as eas;
874
502
  struct ecommunity_ip eip;
875
502
  struct ecommunity_ip6 eip6;
876
877
  /* Determine prefix for string, if any. */
878
502
  switch (format) {
879
0
  case ECOMMUNITY_FORMAT_COMMUNITY_LIST:
880
0
    prefix = (sub_type == ECOMMUNITY_ROUTE_TARGET ? "rt " : "soo ");
881
0
    break;
882
502
  case ECOMMUNITY_FORMAT_DISPLAY:
883
502
    prefix = (sub_type == ECOMMUNITY_ROUTE_TARGET ? "RT:" : "SoO:");
884
502
    break;
885
0
  case ECOMMUNITY_FORMAT_ROUTE_MAP:
886
0
    prefix = "";
887
0
    break;
888
0
  default:
889
0
    prefix = "";
890
0
    break;
891
502
  }
892
893
  /* Put string into buffer.  */
894
502
  if (type == ECOMMUNITY_ENCODE_AS4) {
895
134
    pnt = ptr_get_be32(pnt, &eas.as);
896
134
    eas.val = (*pnt++ << 8);
897
134
    eas.val |= (*pnt++);
898
899
134
    len = snprintf(buf, bufsz, "%s%u:%u", prefix, eas.as, eas.val);
900
368
  } else if (type == ECOMMUNITY_ENCODE_AS) {
901
285
    if (ecom_size == ECOMMUNITY_SIZE) {
902
150
      eas.as = (*pnt++ << 8);
903
150
      eas.as |= (*pnt++);
904
150
      pnt = ptr_get_be32(pnt, &eas.val);
905
906
150
      len = snprintf(buf, bufsz, "%s%u:%u", prefix, eas.as,
907
150
               eas.val);
908
150
    } else {
909
      /* this is an IPv6 ext community
910
       * first 16 bytes stands for IPv6 addres
911
       */
912
135
      memcpy(&eip6.ip, pnt, 16);
913
135
      pnt += 16;
914
135
      eip6.val = (*pnt++ << 8);
915
135
      eip6.val |= (*pnt++);
916
917
135
      inet_ntop(AF_INET6, &eip6.ip, buf_local,
918
135
          sizeof(buf_local));
919
135
      len = snprintf(buf, bufsz, "%s%s:%u", prefix,
920
135
               buf_local, eip6.val);
921
135
    }
922
285
  } else if (type == ECOMMUNITY_ENCODE_IP) {
923
83
    memcpy(&eip.ip, pnt, 4);
924
83
    pnt += 4;
925
83
    eip.val = (*pnt++ << 8);
926
83
    eip.val |= (*pnt++);
927
928
83
    len = snprintfrr(buf, bufsz, "%s%pI4:%u", prefix, &eip.ip,
929
83
         eip.val);
930
83
  }
931
932
  /* consume value */
933
502
  (void)pnt;
934
935
502
  return len;
936
502
}
937
938
static int ecommunity_rt_soo_str(char *buf, size_t bufsz, const uint8_t *pnt,
939
         int type, int sub_type, int format)
940
340
{
941
340
  return ecommunity_rt_soo_str_internal(buf, bufsz, pnt, type,
942
340
                sub_type, format,
943
340
                ECOMMUNITY_SIZE);
944
340
}
945
946
/* Helper function to convert IEEE-754 Floating Point to uint32 */
947
static uint32_t ieee_float_uint32_to_uint32(uint32_t u)
948
89
{
949
89
  union {
950
89
    float r;
951
89
    uint32_t d;
952
89
  } f = {.d = u};
953
954
89
  return (uint32_t)f.r;
955
89
}
956
957
static int ecommunity_lb_str(char *buf, size_t bufsz, const uint8_t *pnt,
958
           bool disable_ieee_floating)
959
60
{
960
60
  int len = 0;
961
60
  as_t as;
962
60
  uint32_t bw_tmp, bw;
963
60
  char bps_buf[20] = {0};
964
965
63
#define ONE_GBPS_BYTES (1000 * 1000 * 1000 / 8)
966
60
#define ONE_MBPS_BYTES (1000 * 1000 / 8)
967
60
#define ONE_KBPS_BYTES (1000 / 8)
968
969
60
  as = (*pnt++ << 8);
970
60
  as |= (*pnt++);
971
60
  (void)ptr_get_be32(pnt, &bw_tmp);
972
973
60
  bw = disable_ieee_floating ? bw_tmp
974
60
           : ieee_float_uint32_to_uint32(bw_tmp);
975
976
60
  if (bw >= ONE_GBPS_BYTES)
977
3
    snprintf(bps_buf, sizeof(bps_buf), "%.3f Gbps",
978
3
       (float)(bw / ONE_GBPS_BYTES));
979
57
  else if (bw >= ONE_MBPS_BYTES)
980
0
    snprintf(bps_buf, sizeof(bps_buf), "%.3f Mbps",
981
0
       (float)(bw / ONE_MBPS_BYTES));
982
57
  else if (bw >= ONE_KBPS_BYTES)
983
0
    snprintf(bps_buf, sizeof(bps_buf), "%.3f Kbps",
984
0
       (float)(bw / ONE_KBPS_BYTES));
985
57
  else
986
57
    snprintf(bps_buf, sizeof(bps_buf), "%u bps", bw * 8);
987
988
60
  len = snprintf(buf, bufsz, "LB:%u:%u (%s)", as, bw, bps_buf);
989
60
  return len;
990
60
}
991
992
/* Convert extended community attribute to string.
993
994
   Due to historical reason of industry standard implementation, there
995
   are three types of format.
996
997
   route-map set extcommunity format
998
  "rt 100:1 100:2soo 100:3"
999
1000
   extcommunity-list
1001
  "rt 100:1 rt 100:2 soo 100:3show [ip] bgp" and extcommunity-list regular expression matching
1002
  "RT:100:1 RT:100:2 SoO:100:3"
1003
1004
   For each formath please use below definition for format:
1005
1006
   ECOMMUNITY_FORMAT_ROUTE_MAP
1007
   ECOMMUNITY_FORMAT_COMMUNITY_LIST
1008
   ECOMMUNITY_FORMAT_DISPLAY
1009
1010
   Filter is added to display only ECOMMUNITY_ROUTE_TARGET in some cases.
1011
   0 value displays all
1012
*/
1013
char *ecommunity_ecom2str(struct ecommunity *ecom, int format, int filter)
1014
139
{
1015
139
  uint32_t i;
1016
139
  uint8_t *pnt;
1017
139
  uint8_t type = 0;
1018
139
  uint8_t sub_type = 0;
1019
139
  int str_size;
1020
139
  char *str_buf;
1021
1022
139
  if (!ecom || ecom->size == 0)
1023
0
    return XCALLOC(MTYPE_ECOMMUNITY_STR, 1);
1024
1025
  /* ecom strlen + space + null term */
1026
139
  str_size = (ecom->size * (ECOMMUNITY_STRLEN + 1)) + 1;
1027
139
  str_buf = XCALLOC(MTYPE_ECOMMUNITY_STR, str_size);
1028
1029
139
  char encbuf[128];
1030
1031
11.8k
  for (i = 0; i < ecom->size; i++) {
1032
11.7k
    int unk_ecom = 0;
1033
11.7k
    memset(encbuf, 0x00, sizeof(encbuf));
1034
1035
    /* Space between each value.  */
1036
11.7k
    if (i > 0)
1037
11.5k
      strlcat(str_buf, " ", str_size);
1038
1039
    /* Retrieve value field */
1040
11.7k
    pnt = ecom->val + (i * ecom->unit_size);
1041
1042
    /* High-order octet is the type */
1043
11.7k
    type = *pnt++;
1044
1045
11.7k
    if (type == ECOMMUNITY_ENCODE_AS || type == ECOMMUNITY_ENCODE_IP
1046
8.54k
        || type == ECOMMUNITY_ENCODE_AS4) {
1047
      /* Low-order octet of type. */
1048
3.59k
      sub_type = *pnt++;
1049
3.59k
      if (sub_type != ECOMMUNITY_ROUTE_TARGET
1050
3.36k
          && sub_type != ECOMMUNITY_SITE_ORIGIN) {
1051
3.26k
        if (sub_type ==
1052
3.26k
            ECOMMUNITY_FLOWSPEC_REDIRECT_IPV4 &&
1053
8
            type == ECOMMUNITY_ENCODE_IP) {
1054
2
          struct in_addr *ipv4 =
1055
2
            (struct in_addr *)pnt;
1056
2
          snprintfrr(encbuf, sizeof(encbuf),
1057
2
               "NH:%pI4:%d", ipv4, pnt[5]);
1058
3.26k
        } else if (sub_type ==
1059
3.26k
             ECOMMUNITY_LINK_BANDWIDTH &&
1060
82
             type == ECOMMUNITY_ENCODE_AS) {
1061
57
          ecommunity_lb_str(
1062
57
            encbuf, sizeof(encbuf), pnt,
1063
57
            ecom->disable_ieee_floating);
1064
3.20k
        } else if (sub_type == ECOMMUNITY_NODE_TARGET &&
1065
155
             type == ECOMMUNITY_ENCODE_IP) {
1066
56
          ecommunity_node_target_str(
1067
56
            encbuf, sizeof(encbuf), pnt,
1068
56
            format);
1069
56
        } else
1070
3.14k
          unk_ecom = 1;
1071
3.26k
      } else {
1072
336
        ecommunity_rt_soo_str(encbuf, sizeof(encbuf),
1073
336
                  pnt, type, sub_type,
1074
336
                  format);
1075
336
      }
1076
8.11k
    } else if (type == ECOMMUNITY_ENCODE_OPAQUE) {
1077
269
      if (filter == ECOMMUNITY_ROUTE_TARGET)
1078
0
        continue;
1079
269
      if (*pnt == ECOMMUNITY_OPAQUE_SUBTYPE_ENCAP) {
1080
22
        uint16_t tunneltype;
1081
22
        memcpy(&tunneltype, pnt + 5, 2);
1082
22
        tunneltype = ntohs(tunneltype);
1083
1084
22
        snprintf(encbuf, sizeof(encbuf), "ET:%d",
1085
22
           tunneltype);
1086
247
      } else if (*pnt == ECOMMUNITY_EVPN_SUBTYPE_DEF_GW) {
1087
13
        strlcpy(encbuf, "Default Gateway",
1088
13
          sizeof(encbuf));
1089
234
      } else {
1090
234
        unk_ecom = 1;
1091
234
      }
1092
7.84k
    } else if (type == ECOMMUNITY_ENCODE_EVPN) {
1093
761
      if (filter == ECOMMUNITY_ROUTE_TARGET)
1094
0
        continue;
1095
761
      if (*pnt == ECOMMUNITY_EVPN_SUBTYPE_ROUTERMAC) {
1096
6
        struct ethaddr rmac;
1097
6
        pnt++;
1098
6
        memcpy(&rmac, pnt, ETH_ALEN);
1099
1100
6
        snprintf(encbuf, sizeof(encbuf),
1101
6
           "Rmac:%02x:%02x:%02x:%02x:%02x:%02x",
1102
6
           (uint8_t)rmac.octet[0],
1103
6
           (uint8_t)rmac.octet[1],
1104
6
           (uint8_t)rmac.octet[2],
1105
6
           (uint8_t)rmac.octet[3],
1106
6
           (uint8_t)rmac.octet[4],
1107
6
           (uint8_t)rmac.octet[5]);
1108
755
      } else if (*pnt
1109
755
           == ECOMMUNITY_EVPN_SUBTYPE_MACMOBILITY) {
1110
69
        uint32_t seqnum;
1111
69
        uint8_t flags = *++pnt;
1112
1113
69
        memcpy(&seqnum, pnt + 2, 4);
1114
69
        seqnum = ntohl(seqnum);
1115
1116
69
        snprintf(encbuf, sizeof(encbuf), "MM:%u",
1117
69
           seqnum);
1118
1119
69
        if (CHECK_FLAG(
1120
69
              flags,
1121
69
              ECOMMUNITY_EVPN_SUBTYPE_MACMOBILITY_FLAG_STICKY))
1122
23
          strlcat(encbuf, ", sticky MAC",
1123
23
            sizeof(encbuf));
1124
686
      } else if (*pnt == ECOMMUNITY_EVPN_SUBTYPE_ND) {
1125
113
        uint8_t flags = *++pnt;
1126
1127
113
        if (CHECK_FLAG(
1128
113
              flags,
1129
113
              ECOMMUNITY_EVPN_SUBTYPE_ND_ROUTER_FLAG))
1130
35
          strlcpy(encbuf, "ND:Router Flag",
1131
35
            sizeof(encbuf));
1132
113
        if (CHECK_FLAG(
1133
113
              flags,
1134
113
              ECOMMUNITY_EVPN_SUBTYPE_PROXY_FLAG))
1135
68
          strlcpy(encbuf, "ND:Proxy",
1136
68
            sizeof(encbuf));
1137
573
      } else if (*pnt
1138
573
           == ECOMMUNITY_EVPN_SUBTYPE_ES_IMPORT_RT) {
1139
51
        struct ethaddr mac;
1140
1141
51
        pnt++;
1142
51
        memcpy(&mac, pnt, ETH_ALEN);
1143
51
        snprintf(encbuf,
1144
51
          sizeof(encbuf),
1145
51
          "ES-Import-Rt:%02x:%02x:%02x:%02x:%02x:%02x",
1146
51
          (uint8_t)mac.octet[0],
1147
51
          (uint8_t)mac.octet[1],
1148
51
          (uint8_t)mac.octet[2],
1149
51
          (uint8_t)mac.octet[3],
1150
51
          (uint8_t)mac.octet[4],
1151
51
          (uint8_t)mac.octet[5]);
1152
522
      } else if (*pnt
1153
522
           == ECOMMUNITY_EVPN_SUBTYPE_ESI_LABEL) {
1154
16
        uint8_t flags = *++pnt;
1155
1156
16
        snprintf(encbuf,
1157
16
          sizeof(encbuf), "ESI-label-Rt:%s",
1158
16
          (flags &
1159
16
           ECOMMUNITY_EVPN_SUBTYPE_ESI_SA_FLAG) ?
1160
15
          "SA":"AA");
1161
506
      } else if (*pnt
1162
506
           == ECOMMUNITY_EVPN_SUBTYPE_DF_ELECTION) {
1163
254
        uint8_t alg;
1164
254
        uint16_t pref;
1165
254
        uint16_t bmap;
1166
1167
254
        alg = *(pnt + 1);
1168
254
        memcpy(&bmap, pnt + 2, 2);
1169
254
        bmap = ntohs(bmap);
1170
254
        memcpy(&pref, pnt + 5, 2);
1171
254
        pref = ntohs(pref);
1172
1173
254
        if (bmap)
1174
235
          snprintf(
1175
235
            encbuf, sizeof(encbuf),
1176
235
            "DF: (alg: %u, bmap: 0x%x pref: %u)",
1177
235
            alg, bmap, pref);
1178
19
        else
1179
19
          snprintf(encbuf, sizeof(encbuf),
1180
19
             "DF: (alg: %u, pref: %u)", alg,
1181
19
             pref);
1182
254
      } else
1183
252
        unk_ecom = 1;
1184
7.08k
    } else if (type == ECOMMUNITY_ENCODE_REDIRECT_IP_NH) {
1185
254
      sub_type = *pnt++;
1186
254
      if (sub_type == ECOMMUNITY_REDIRECT_IP_NH) {
1187
56
        snprintf(encbuf, sizeof(encbuf),
1188
56
           "FS:redirect IP 0x%x", *(pnt + 5));
1189
56
      } else
1190
198
        unk_ecom = 1;
1191
6.82k
    } else if (type == ECOMMUNITY_ENCODE_TRANS_EXP ||
1192
6.45k
         type == ECOMMUNITY_EXTENDED_COMMUNITY_PART_2 ||
1193
6.32k
         type == ECOMMUNITY_EXTENDED_COMMUNITY_PART_3) {
1194
600
      sub_type = *pnt++;
1195
1196
600
      if (sub_type == ECOMMUNITY_ROUTE_TARGET) {
1197
151
        char buf[ECOMMUNITY_STRLEN];
1198
1199
151
        memset(buf, 0, sizeof(buf));
1200
151
        ecommunity_rt_soo_str_internal(buf, sizeof(buf),
1201
151
            (const uint8_t *)pnt,
1202
151
            type &
1203
151
            ~ECOMMUNITY_ENCODE_TRANS_EXP,
1204
151
            ECOMMUNITY_ROUTE_TARGET,
1205
151
            format,
1206
151
            ecom->unit_size);
1207
151
        snprintf(encbuf, sizeof(encbuf), "%s", buf);
1208
449
      } else if (sub_type ==
1209
449
           ECOMMUNITY_FLOWSPEC_REDIRECT_IPV6) {
1210
11
        char buf[64];
1211
1212
11
        memset(buf, 0, sizeof(buf));
1213
11
        ecommunity_rt_soo_str_internal(buf, sizeof(buf),
1214
11
            (const uint8_t *)pnt,
1215
11
            type &
1216
11
            ~ECOMMUNITY_ENCODE_TRANS_EXP,
1217
11
            ECOMMUNITY_ROUTE_TARGET,
1218
11
            ECOMMUNITY_FORMAT_DISPLAY,
1219
11
            ecom->unit_size);
1220
11
        snprintf(encbuf, sizeof(encbuf),
1221
11
           "FS:redirect VRF %s", buf);
1222
438
      } else if (sub_type == ECOMMUNITY_REDIRECT_VRF) {
1223
4
        char buf[16];
1224
1225
4
        memset(buf, 0, sizeof(buf));
1226
4
        ecommunity_rt_soo_str(buf, sizeof(buf),
1227
4
            (const uint8_t *)pnt,
1228
4
            type &
1229
4
            ~ECOMMUNITY_ENCODE_TRANS_EXP,
1230
4
            ECOMMUNITY_ROUTE_TARGET,
1231
4
            ECOMMUNITY_FORMAT_DISPLAY);
1232
4
        snprintf(encbuf, sizeof(encbuf),
1233
4
           "FS:redirect VRF %s", buf);
1234
4
        snprintf(encbuf, sizeof(encbuf),
1235
4
           "FS:redirect VRF %s", buf);
1236
434
      } else if (type != ECOMMUNITY_ENCODE_TRANS_EXP)
1237
217
        unk_ecom = 1;
1238
217
      else if (sub_type == ECOMMUNITY_TRAFFIC_ACTION) {
1239
50
        char action[64];
1240
1241
50
        if (*(pnt+3) ==
1242
50
            1 << FLOWSPEC_TRAFFIC_ACTION_TERMINAL)
1243
8
          strlcpy(action, "terminate (apply)",
1244
8
            sizeof(action));
1245
42
        else
1246
42
          strlcpy(action, "eval stops",
1247
42
            sizeof(action));
1248
1249
50
        if (*(pnt+3) ==
1250
50
            1 << FLOWSPEC_TRAFFIC_ACTION_SAMPLE)
1251
22
          strlcat(action, ", sample",
1252
22
            sizeof(action));
1253
1254
1255
50
        snprintf(encbuf, sizeof(encbuf), "FS:action %s",
1256
50
           action);
1257
167
      } else if (sub_type == ECOMMUNITY_TRAFFIC_RATE) {
1258
4
        union traffic_rate data;
1259
1260
4
        data.rate_byte[3] = *(pnt+2);
1261
4
        data.rate_byte[2] = *(pnt+3);
1262
4
        data.rate_byte[1] = *(pnt+4);
1263
4
        data.rate_byte[0] = *(pnt+5);
1264
4
        snprintf(encbuf, sizeof(encbuf), "FS:rate %f",
1265
4
           data.rate_float);
1266
163
      } else if (sub_type == ECOMMUNITY_TRAFFIC_MARKING) {
1267
2
        snprintf(encbuf, sizeof(encbuf),
1268
2
           "FS:marking %u", *(pnt + 5));
1269
2
      } else
1270
161
        unk_ecom = 1;
1271
6.22k
    } else if (type == ECOMMUNITY_ENCODE_AS_NON_TRANS) {
1272
133
      sub_type = *pnt++;
1273
133
      if (sub_type == ECOMMUNITY_LINK_BANDWIDTH)
1274
3
        ecommunity_lb_str(encbuf, sizeof(encbuf), pnt,
1275
3
              ecom->disable_ieee_floating);
1276
130
      else
1277
130
        unk_ecom = 1;
1278
6.09k
    } else if (type == ECOMMUNITY_ENCODE_IP_NON_TRANS) {
1279
199
      sub_type = *pnt++;
1280
199
      if (sub_type == ECOMMUNITY_NODE_TARGET)
1281
0
        ecommunity_node_target_str(
1282
0
          encbuf, sizeof(encbuf), pnt, format);
1283
199
      else
1284
199
        unk_ecom = 1;
1285
5.89k
    } else if (type == ECOMMUNITY_ENCODE_OPAQUE_NON_TRANS) {
1286
111
      sub_type = *pnt++;
1287
111
      if (sub_type == ECOMMUNITY_ORIGIN_VALIDATION_STATE)
1288
28
        ecommunity_origin_validation_state_str(
1289
28
          encbuf, sizeof(encbuf), pnt);
1290
83
      else
1291
83
        unk_ecom = 1;
1292
5.78k
    } else {
1293
5.78k
      sub_type = *pnt++;
1294
5.78k
      unk_ecom = 1;
1295
5.78k
    }
1296
1297
11.7k
    if (unk_ecom)
1298
10.4k
      snprintf(encbuf, sizeof(encbuf), "UNK:%d, %d", type,
1299
10.4k
         sub_type);
1300
1301
11.7k
    int r = strlcat(str_buf, encbuf, str_size);
1302
11.7k
    assert(r < str_size);
1303
11.7k
  }
1304
1305
139
  return str_buf;
1306
139
}
1307
1308
bool ecommunity_include(struct ecommunity *e1, struct ecommunity *e2)
1309
0
{
1310
0
  uint32_t i, j;
1311
1312
0
  if (!e1 || !e2)
1313
0
    return false;
1314
0
  for (i = 0; i < e1->size; ++i) {
1315
0
    for (j = 0; j < e2->size; ++j) {
1316
0
      if (!memcmp(e1->val + (i * e1->unit_size),
1317
0
            e2->val + (j * e2->unit_size),
1318
0
            e1->unit_size))
1319
0
        return true;
1320
0
    }
1321
0
  }
1322
0
  return false;
1323
0
}
1324
1325
bool ecommunity_match(const struct ecommunity *ecom1,
1326
          const struct ecommunity *ecom2)
1327
0
{
1328
0
  uint32_t i = 0;
1329
0
  uint32_t j = 0;
1330
1331
0
  if (ecom1 == NULL && ecom2 == NULL)
1332
0
    return true;
1333
1334
0
  if (ecom1 == NULL || ecom2 == NULL)
1335
0
    return false;
1336
1337
0
  if (ecom1->size < ecom2->size)
1338
0
    return false;
1339
1340
  /* Every community on com2 needs to be on com1 for this to match */
1341
0
  while (i < ecom1->size && j < ecom2->size) {
1342
0
    if (memcmp(ecom1->val + i * ecom1->unit_size,
1343
0
         ecom2->val + j * ecom2->unit_size,
1344
0
         ecom2->unit_size)
1345
0
        == 0)
1346
0
      j++;
1347
0
    i++;
1348
0
  }
1349
1350
0
  if (j == ecom2->size)
1351
0
    return true;
1352
0
  else
1353
0
    return false;
1354
0
}
1355
1356
/* return first occurence of type */
1357
extern struct ecommunity_val *ecommunity_lookup(const struct ecommunity *ecom,
1358
            uint8_t type, uint8_t subtype)
1359
295
{
1360
295
  uint8_t *p;
1361
295
  uint32_t c;
1362
1363
  /* If the value already exists in the structure return 0.  */
1364
295
  c = 0;
1365
3.72k
  for (p = ecom->val; c < ecom->size; p += ecom->unit_size, c++) {
1366
3.62k
    if (p == NULL) {
1367
0
      continue;
1368
0
    }
1369
3.62k
    if (p[0] == type && p[1] == subtype)
1370
191
      return (struct ecommunity_val *)p;
1371
3.62k
  }
1372
104
  return NULL;
1373
295
}
1374
1375
/* remove ext. community matching type and subtype
1376
 * return 1 on success ( removed ), 0 otherwise (not present)
1377
 */
1378
bool ecommunity_strip(struct ecommunity *ecom, uint8_t type,
1379
          uint8_t subtype)
1380
0
{
1381
0
  uint8_t *p, *q, *new;
1382
0
  uint32_t c, found = 0;
1383
  /* When this is fist value, just add it.  */
1384
0
  if (ecom == NULL || ecom->val == NULL)
1385
0
    return false;
1386
1387
  /* Check if any existing ext community matches. */
1388
  /* Certain extended communities like the Route Target can be present
1389
   * multiple times, handle that.
1390
   */
1391
0
  c = 0;
1392
0
  for (p = ecom->val; c < ecom->size; p += ecom->unit_size, c++) {
1393
0
    if (p[0] == type && p[1] == subtype)
1394
0
      found++;
1395
0
  }
1396
  /* If no matching ext community exists, return. */
1397
0
  if (found == 0)
1398
0
    return false;
1399
1400
  /* Handle the case where everything needs to be stripped. */
1401
0
  if (found == ecom->size) {
1402
0
    XFREE(MTYPE_ECOMMUNITY_VAL, ecom->val);
1403
0
    ecom->size = 0;
1404
0
    return true;
1405
0
  }
1406
1407
  /* Strip matching ext community(ies). */
1408
0
  new = XMALLOC(MTYPE_ECOMMUNITY_VAL,
1409
0
          (ecom->size - found) * ecom->unit_size);
1410
0
  q = new;
1411
0
  for (c = 0, p = ecom->val; c < ecom->size; c++, p += ecom->unit_size) {
1412
0
    if (!(p[0] == type && p[1] == subtype)) {
1413
0
      memcpy(q, p, ecom->unit_size);
1414
0
      q += ecom->unit_size;
1415
0
    }
1416
0
  }
1417
0
  XFREE(MTYPE_ECOMMUNITY_VAL, ecom->val);
1418
0
  ecom->val = new;
1419
0
  ecom->size -= found;
1420
0
  return true;
1421
0
}
1422
1423
/*
1424
 * Remove specified extended community value from extended community.
1425
 * Returns 1 if value was present (and hence, removed), 0 otherwise.
1426
 */
1427
bool ecommunity_del_val(struct ecommunity *ecom, struct ecommunity_val *eval)
1428
0
{
1429
0
  uint8_t *p;
1430
0
  uint32_t c, found = 0;
1431
1432
  /* Make sure specified value exists. */
1433
0
  if (ecom == NULL || ecom->val == NULL)
1434
0
    return false;
1435
0
  c = 0;
1436
0
  for (p = ecom->val; c < ecom->size; p += ecom->unit_size, c++) {
1437
0
    if (!memcmp(p, eval->val, ecom->unit_size)) {
1438
0
      found = 1;
1439
0
      break;
1440
0
    }
1441
0
  }
1442
0
  if (found == 0)
1443
0
    return false;
1444
1445
  /* Delete the selected value */
1446
0
  ecom->size--;
1447
0
  if (ecom->size) {
1448
0
    p = XMALLOC(MTYPE_ECOMMUNITY_VAL, ecom->size * ecom->unit_size);
1449
0
    if (c != 0)
1450
0
      memcpy(p, ecom->val, c * ecom->unit_size);
1451
0
    if ((ecom->size - c) != 0)
1452
0
      memcpy(p + (c)*ecom->unit_size,
1453
0
             ecom->val + (c + 1) * ecom->unit_size,
1454
0
             (ecom->size - c) * ecom->unit_size);
1455
0
    XFREE(MTYPE_ECOMMUNITY_VAL, ecom->val);
1456
0
    ecom->val = p;
1457
0
  } else
1458
0
    XFREE(MTYPE_ECOMMUNITY_VAL, ecom->val);
1459
1460
0
  return true;
1461
0
}
1462
1463
int ecommunity_fill_pbr_action(struct ecommunity_val *ecom_eval,
1464
             struct bgp_pbr_entry_action *api,
1465
             afi_t afi)
1466
0
{
1467
0
  if (ecom_eval->val[1] == ECOMMUNITY_TRAFFIC_RATE) {
1468
0
    api->action = ACTION_TRAFFICRATE;
1469
0
    api->u.r.rate_info[3] = ecom_eval->val[4];
1470
0
    api->u.r.rate_info[2] = ecom_eval->val[5];
1471
0
    api->u.r.rate_info[1] = ecom_eval->val[6];
1472
0
    api->u.r.rate_info[0] = ecom_eval->val[7];
1473
0
  } else if (ecom_eval->val[1] == ECOMMUNITY_TRAFFIC_ACTION) {
1474
0
    api->action = ACTION_TRAFFIC_ACTION;
1475
    /* else distribute code is set by default */
1476
0
    if (ecom_eval->val[5] & (1 << FLOWSPEC_TRAFFIC_ACTION_TERMINAL))
1477
0
      api->u.za.filter |= TRAFFIC_ACTION_TERMINATE;
1478
0
    else
1479
0
      api->u.za.filter |= TRAFFIC_ACTION_DISTRIBUTE;
1480
0
    if (ecom_eval->val[5] == 1 << FLOWSPEC_TRAFFIC_ACTION_SAMPLE)
1481
0
      api->u.za.filter |= TRAFFIC_ACTION_SAMPLE;
1482
1483
0
  } else if (ecom_eval->val[1] == ECOMMUNITY_TRAFFIC_MARKING) {
1484
0
    api->action = ACTION_MARKING;
1485
0
    api->u.marking_dscp = ecom_eval->val[7];
1486
0
  } else if (ecom_eval->val[1] == ECOMMUNITY_REDIRECT_VRF) {
1487
    /* must use external function */
1488
0
    return 0;
1489
0
  } else if (ecom_eval->val[1] == ECOMMUNITY_REDIRECT_IP_NH &&
1490
0
       afi == AFI_IP) {
1491
    /* see draft-ietf-idr-flowspec-redirect-ip-02
1492
     * Q1: how come a ext. community can host ipv6 address
1493
     * Q2 : from cisco documentation:
1494
     * Announces the reachability of one or more flowspec NLRI.
1495
     * When a BGP speaker receives an UPDATE message with the
1496
     * redirect-to-IP extended community, it is expected to
1497
     * create a traffic filtering rule for every flow-spec
1498
     * NLRI in the message that has this path as its best
1499
     * path. The filter entry matches the IP packets
1500
     * described in the NLRI field and redirects them or
1501
     * copies them towards the IPv4 or IPv6 address specified
1502
     * in the 'Network Address of Next- Hop'
1503
     * field of the associated MP_REACH_NLRI.
1504
     */
1505
0
    struct ecommunity_ip *ip_ecom = (struct ecommunity_ip *)
1506
0
      ecom_eval + 2;
1507
1508
0
    api->u.zr.redirect_ip_v4 = ip_ecom->ip;
1509
0
  } else
1510
0
    return -1;
1511
0
  return 0;
1512
0
}
1513
1514
static struct ecommunity *bgp_aggr_ecommunity_lookup(
1515
            struct bgp_aggregate *aggregate,
1516
            struct ecommunity *ecommunity)
1517
0
{
1518
0
  return hash_lookup(aggregate->ecommunity_hash, ecommunity);
1519
0
}
1520
1521
static void *bgp_aggr_ecommunty_hash_alloc(void *p)
1522
0
{
1523
0
  struct ecommunity *ref = (struct ecommunity *)p;
1524
0
  struct ecommunity *ecommunity = NULL;
1525
1526
0
  ecommunity = ecommunity_dup(ref);
1527
0
  return ecommunity;
1528
0
}
1529
1530
static void bgp_aggr_ecommunity_prepare(struct hash_bucket *hb, void *arg)
1531
0
{
1532
0
  struct ecommunity *hb_ecommunity = hb->data;
1533
0
  struct ecommunity **aggr_ecommunity = arg;
1534
1535
0
  if (*aggr_ecommunity)
1536
0
    *aggr_ecommunity = ecommunity_merge(*aggr_ecommunity,
1537
0
                hb_ecommunity);
1538
0
  else
1539
0
    *aggr_ecommunity = ecommunity_dup(hb_ecommunity);
1540
0
}
1541
1542
void bgp_aggr_ecommunity_remove(void *arg)
1543
0
{
1544
0
  struct ecommunity *ecommunity = arg;
1545
1546
0
  ecommunity_free(&ecommunity);
1547
0
}
1548
1549
void bgp_compute_aggregate_ecommunity(struct bgp_aggregate *aggregate,
1550
              struct ecommunity *ecommunity)
1551
0
{
1552
0
  bgp_compute_aggregate_ecommunity_hash(aggregate, ecommunity);
1553
0
  bgp_compute_aggregate_ecommunity_val(aggregate);
1554
0
}
1555
1556
1557
void bgp_compute_aggregate_ecommunity_hash(struct bgp_aggregate *aggregate,
1558
             struct ecommunity *ecommunity)
1559
0
{
1560
0
  struct ecommunity *aggr_ecommunity = NULL;
1561
1562
0
  if ((aggregate == NULL) || (ecommunity == NULL))
1563
0
    return;
1564
1565
  /* Create hash if not already created.
1566
   */
1567
0
  if (aggregate->ecommunity_hash == NULL)
1568
0
    aggregate->ecommunity_hash = hash_create(
1569
0
          ecommunity_hash_make, ecommunity_cmp,
1570
0
          "BGP Aggregator ecommunity hash");
1571
1572
0
  aggr_ecommunity = bgp_aggr_ecommunity_lookup(aggregate, ecommunity);
1573
0
  if (aggr_ecommunity == NULL) {
1574
    /* Insert ecommunity into hash.
1575
     */
1576
0
    aggr_ecommunity = hash_get(aggregate->ecommunity_hash,
1577
0
             ecommunity,
1578
0
             bgp_aggr_ecommunty_hash_alloc);
1579
0
  }
1580
1581
  /* Increment reference counter.
1582
   */
1583
0
  aggr_ecommunity->refcnt++;
1584
0
}
1585
1586
void bgp_compute_aggregate_ecommunity_val(struct bgp_aggregate *aggregate)
1587
0
{
1588
0
  struct ecommunity *ecommerge = NULL;
1589
1590
0
  if (aggregate == NULL)
1591
0
    return;
1592
1593
  /* Re-compute aggregate's ecommunity.
1594
   */
1595
0
  if (aggregate->ecommunity)
1596
0
    ecommunity_free(&aggregate->ecommunity);
1597
0
  if (aggregate->ecommunity_hash
1598
0
      && aggregate->ecommunity_hash->count) {
1599
0
    hash_iterate(aggregate->ecommunity_hash,
1600
0
           bgp_aggr_ecommunity_prepare,
1601
0
           &aggregate->ecommunity);
1602
0
    ecommerge = aggregate->ecommunity;
1603
0
    aggregate->ecommunity = ecommunity_uniq_sort(ecommerge);
1604
0
    if (ecommerge)
1605
0
      ecommunity_free(&ecommerge);
1606
0
  }
1607
0
}
1608
1609
void bgp_remove_ecommunity_from_aggregate(struct bgp_aggregate *aggregate,
1610
            struct ecommunity *ecommunity)
1611
0
{
1612
0
  struct ecommunity *aggr_ecommunity = NULL;
1613
0
  struct ecommunity *ret_ecomm = NULL;
1614
1615
0
  if ((!aggregate)
1616
0
      || (!aggregate->ecommunity_hash)
1617
0
      || (!ecommunity))
1618
0
    return;
1619
1620
  /* Look-up the ecommunity in the hash.
1621
   */
1622
0
  aggr_ecommunity = bgp_aggr_ecommunity_lookup(aggregate, ecommunity);
1623
0
  if (aggr_ecommunity) {
1624
0
    aggr_ecommunity->refcnt--;
1625
1626
0
    if (aggr_ecommunity->refcnt == 0) {
1627
0
      ret_ecomm = hash_release(aggregate->ecommunity_hash,
1628
0
             aggr_ecommunity);
1629
0
      ecommunity_free(&ret_ecomm);
1630
0
      bgp_compute_aggregate_ecommunity_val(aggregate);
1631
0
    }
1632
0
  }
1633
0
}
1634
1635
void bgp_remove_ecomm_from_aggregate_hash(struct bgp_aggregate *aggregate,
1636
            struct ecommunity *ecommunity)
1637
0
{
1638
1639
0
  struct ecommunity *aggr_ecommunity = NULL;
1640
0
  struct ecommunity *ret_ecomm = NULL;
1641
1642
0
  if ((!aggregate)
1643
0
      || (!aggregate->ecommunity_hash)
1644
0
      || (!ecommunity))
1645
0
    return;
1646
1647
  /* Look-up the ecommunity in the hash.
1648
   */
1649
0
  aggr_ecommunity = bgp_aggr_ecommunity_lookup(aggregate, ecommunity);
1650
0
  if (aggr_ecommunity) {
1651
0
    aggr_ecommunity->refcnt--;
1652
1653
0
    if (aggr_ecommunity->refcnt == 0) {
1654
0
      ret_ecomm = hash_release(aggregate->ecommunity_hash,
1655
0
             aggr_ecommunity);
1656
0
      ecommunity_free(&ret_ecomm);
1657
0
    }
1658
0
  }
1659
0
}
1660
1661
struct ecommunity *
1662
ecommunity_add_origin_validation_state(enum rpki_states rpki_state,
1663
               struct ecommunity *old)
1664
0
{
1665
0
  struct ecommunity *new = NULL;
1666
0
  struct ecommunity ovs_ecomm = {0};
1667
0
  struct ecommunity_val ovs_eval;
1668
1669
0
  encode_origin_validation_state(rpki_state, &ovs_eval);
1670
1671
0
  if (old) {
1672
0
    new = ecommunity_dup(old);
1673
0
    ecommunity_add_val(new, &ovs_eval, true, true);
1674
0
    if (!old->refcnt)
1675
0
      ecommunity_free(&old);
1676
0
  } else {
1677
0
    ovs_ecomm.size = 1;
1678
0
    ovs_ecomm.unit_size = ECOMMUNITY_SIZE;
1679
0
    ovs_ecomm.val = (uint8_t *)&ovs_eval.val;
1680
0
    new = ecommunity_dup(&ovs_ecomm);
1681
0
  }
1682
1683
0
  return new;
1684
0
}
1685
1686
/*
1687
 * return the BGP link bandwidth extended community, if present;
1688
 * the actual bandwidth is returned via param
1689
 */
1690
const uint8_t *ecommunity_linkbw_present(struct ecommunity *ecom, uint32_t *bw)
1691
108
{
1692
108
  const uint8_t *eval;
1693
108
  uint32_t i;
1694
1695
108
  if (bw)
1696
108
    *bw = 0;
1697
1698
108
  if (!ecom || !ecom->size)
1699
0
    return NULL;
1700
1701
8.28k
  for (i = 0; i < ecom->size; i++) {
1702
8.20k
    const uint8_t *pnt;
1703
8.20k
    uint8_t type, sub_type;
1704
8.20k
    uint32_t bwval;
1705
1706
8.20k
    eval = pnt = (ecom->val + (i * ECOMMUNITY_SIZE));
1707
8.20k
    type = *pnt++;
1708
8.20k
    sub_type = *pnt++;
1709
1710
8.20k
    if ((type == ECOMMUNITY_ENCODE_AS ||
1711
6.12k
         type == ECOMMUNITY_ENCODE_AS_NON_TRANS) &&
1712
2.20k
        sub_type == ECOMMUNITY_LINK_BANDWIDTH) {
1713
29
      pnt += 2; /* bandwidth is encoded as AS:val */
1714
29
      pnt = ptr_get_be32(pnt, &bwval);
1715
29
      (void)pnt; /* consume value */
1716
29
      if (bw)
1717
29
        *bw = ecom->disable_ieee_floating
1718
29
                ? bwval
1719
29
                : ieee_float_uint32_to_uint32(
1720
29
              bwval);
1721
29
      return eval;
1722
29
    }
1723
8.20k
  }
1724
1725
79
  return NULL;
1726
108
}
1727
1728
1729
struct ecommunity *ecommunity_replace_linkbw(as_t as, struct ecommunity *ecom,
1730
               uint64_t cum_bw,
1731
               bool disable_ieee_floating)
1732
0
{
1733
0
  struct ecommunity *new;
1734
0
  struct ecommunity_val lb_eval;
1735
0
  const uint8_t *eval;
1736
0
  uint8_t type;
1737
0
  uint32_t cur_bw;
1738
1739
  /* Nothing to replace if link-bandwidth doesn't exist or
1740
   * is non-transitive - just return existing extcommunity.
1741
   */
1742
0
  new = ecom;
1743
0
  if (!ecom || !ecom->size)
1744
0
    return new;
1745
1746
0
  eval = ecommunity_linkbw_present(ecom, &cur_bw);
1747
0
  if (!eval)
1748
0
    return new;
1749
1750
0
  type = *eval;
1751
0
  if (type & ECOMMUNITY_FLAG_NON_TRANSITIVE)
1752
0
    return new;
1753
1754
  /* Transitive link-bandwidth exists, replace with the passed
1755
   * (cumulative) bandwidth value. We need to create a new
1756
   * extcommunity for this - refer to AS-Path replace function
1757
   * for reference.
1758
   */
1759
0
  if (cum_bw > 0xFFFFFFFF)
1760
0
    cum_bw = 0xFFFFFFFF;
1761
0
  encode_lb_extcomm(as > BGP_AS_MAX ? BGP_AS_TRANS : as, cum_bw, false,
1762
0
        &lb_eval, disable_ieee_floating);
1763
0
  new = ecommunity_dup(ecom);
1764
0
  ecommunity_add_val(new, &lb_eval, true, true);
1765
1766
0
  return new;
1767
0
}