1*07ef3965Sclaudio /* $OpenBSD: rde_community.c,v 1.16 2024/09/10 08:53:20 claudio Exp $ */ 2e7adcfeaSclaudio 3e7adcfeaSclaudio /* 4e7adcfeaSclaudio * Copyright (c) 2019 Claudio Jeker <claudio@openbsd.org> 5e7adcfeaSclaudio * 6e7adcfeaSclaudio * Permission to use, copy, modify, and distribute this software for any 7e7adcfeaSclaudio * purpose with or without fee is hereby granted, provided that the above 8e7adcfeaSclaudio * copyright notice and this permission notice appear in all copies. 9e7adcfeaSclaudio * 10e7adcfeaSclaudio * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11e7adcfeaSclaudio * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12e7adcfeaSclaudio * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13e7adcfeaSclaudio * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14e7adcfeaSclaudio * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15e7adcfeaSclaudio * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16e7adcfeaSclaudio * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17e7adcfeaSclaudio */ 18e7adcfeaSclaudio #include <sys/queue.h> 19e7adcfeaSclaudio 20e7adcfeaSclaudio #include <endian.h> 21e7adcfeaSclaudio #include <limits.h> 22e7adcfeaSclaudio #include <stdlib.h> 23e7adcfeaSclaudio #include <string.h> 24e7adcfeaSclaudio 25e7adcfeaSclaudio #include "bgpd.h" 26e7adcfeaSclaudio #include "rde.h" 27e7adcfeaSclaudio #include "log.h" 28e7adcfeaSclaudio 29e7adcfeaSclaudio static int 3039386878Sclaudio apply_flag(uint32_t in, uint8_t flag, struct rde_peer *peer, uint32_t *out, 3139386878Sclaudio uint32_t *mask) 32e7adcfeaSclaudio { 33e7adcfeaSclaudio switch (flag) { 34e7adcfeaSclaudio case COMMUNITY_ANY: 35e7adcfeaSclaudio if (mask == NULL) 36e7adcfeaSclaudio return -1; 37e7adcfeaSclaudio *out = 0; 38e7adcfeaSclaudio *mask = 0; 39e7adcfeaSclaudio return 0; 40e7adcfeaSclaudio case COMMUNITY_NEIGHBOR_AS: 41e7adcfeaSclaudio if (peer == NULL) 42e7adcfeaSclaudio return -1; 43e7adcfeaSclaudio *out = peer->conf.remote_as; 44e7adcfeaSclaudio break; 45e7adcfeaSclaudio case COMMUNITY_LOCAL_AS: 46e7adcfeaSclaudio if (peer == NULL) 47e7adcfeaSclaudio return -1; 48e7adcfeaSclaudio *out = peer->conf.local_as; 49e7adcfeaSclaudio break; 50e7adcfeaSclaudio default: 51e7adcfeaSclaudio *out = in; 52e7adcfeaSclaudio break; 53e7adcfeaSclaudio } 54e7adcfeaSclaudio if (mask) 55e7adcfeaSclaudio *mask = UINT32_MAX; 56e7adcfeaSclaudio return 0; 57e7adcfeaSclaudio } 58e7adcfeaSclaudio 59e7adcfeaSclaudio static int 60e7adcfeaSclaudio fc2c(struct community *fc, struct rde_peer *peer, struct community *c, 61e7adcfeaSclaudio struct community *m) 62e7adcfeaSclaudio { 63f8162053Sclaudio int type; 6439386878Sclaudio uint8_t subtype; 65e7adcfeaSclaudio 66e7adcfeaSclaudio memset(c, 0, sizeof(*c)); 67e7adcfeaSclaudio if (m) 68e7adcfeaSclaudio memset(m, 0xff, sizeof(*m)); 69e7adcfeaSclaudio 7039386878Sclaudio c->flags = (uint8_t)fc->flags; 71e7adcfeaSclaudio 7239386878Sclaudio switch ((uint8_t)c->flags) { 73e7adcfeaSclaudio case COMMUNITY_TYPE_BASIC: 74e7adcfeaSclaudio if (apply_flag(fc->data1, fc->flags >> 8, peer, 75e7adcfeaSclaudio &c->data1, m ? &m->data1 : NULL)) 76e7adcfeaSclaudio return -1; 77e7adcfeaSclaudio if (apply_flag(fc->data2, fc->flags >> 16, peer, 78e7adcfeaSclaudio &c->data2, m ? &m->data2 : NULL)) 79e7adcfeaSclaudio return -1; 80e7adcfeaSclaudio 81e7adcfeaSclaudio /* check that values fit */ 82e7adcfeaSclaudio if (c->data1 > USHRT_MAX || c->data2 > USHRT_MAX) 83e7adcfeaSclaudio return -1; 84e7adcfeaSclaudio return 0; 85e7adcfeaSclaudio case COMMUNITY_TYPE_LARGE: 86e7adcfeaSclaudio if (apply_flag(fc->data1, fc->flags >> 8, peer, 87e7adcfeaSclaudio &c->data1, m ? &m->data1 : NULL)) 88e7adcfeaSclaudio return -1; 89e7adcfeaSclaudio if (apply_flag(fc->data2, fc->flags >> 16, peer, 90e7adcfeaSclaudio &c->data2, m ? &m->data2 : NULL)) 91e7adcfeaSclaudio return -1; 92e7adcfeaSclaudio if (apply_flag(fc->data3, fc->flags >> 24, peer, 93e7adcfeaSclaudio &c->data3, m ? &m->data3 : NULL)) 94e7adcfeaSclaudio return -1; 95e7adcfeaSclaudio return 0; 96e7adcfeaSclaudio case COMMUNITY_TYPE_EXT: 97e7adcfeaSclaudio type = (int32_t)fc->data3 >> 8; 98e7adcfeaSclaudio subtype = fc->data3 & 0xff; 99e7adcfeaSclaudio 10041c70dd7Sclaudio if ((fc->flags >> 24 & 0xff) == COMMUNITY_ANY) { 10141c70dd7Sclaudio /* special case for 'ext-community * *' */ 10241c70dd7Sclaudio if (m == NULL) 10341c70dd7Sclaudio return -1; 10441c70dd7Sclaudio m->data1 = 0; 10541c70dd7Sclaudio m->data2 = 0; 10641c70dd7Sclaudio m->data3 = 0; 10741c70dd7Sclaudio return 0; 10841c70dd7Sclaudio } 10941c70dd7Sclaudio 110f8162053Sclaudio if (type == -1) { 111e7adcfeaSclaudio /* special case for 'ext-community rt *' */ 112e7adcfeaSclaudio if ((fc->flags >> 8 & 0xff) != COMMUNITY_ANY || 113e7adcfeaSclaudio m == NULL) 114e7adcfeaSclaudio return -1; 115e7adcfeaSclaudio c->data3 = subtype; 116e7adcfeaSclaudio m->data1 = 0; 117e7adcfeaSclaudio m->data2 = 0; 118e7adcfeaSclaudio m->data3 = 0xff; 119e7adcfeaSclaudio return 0; 120f8162053Sclaudio } 121f8162053Sclaudio 122f8162053Sclaudio c->data3 = type << 8 | subtype; 123f8162053Sclaudio switch (type & EXT_COMMUNITY_VALUE) { 124e7adcfeaSclaudio case EXT_COMMUNITY_TRANS_TWO_AS: 125b2402ebbSclaudio case EXT_COMMUNITY_TRANS_FOUR_AS: 126e7adcfeaSclaudio if ((fc->flags >> 8 & 0xff) == COMMUNITY_ANY) 127e7adcfeaSclaudio break; 128e7adcfeaSclaudio 129e7adcfeaSclaudio if (apply_flag(fc->data1, fc->flags >> 8, peer, 130e7adcfeaSclaudio &c->data1, m ? &m->data1 : NULL)) 131e7adcfeaSclaudio return -1; 132e7adcfeaSclaudio if (apply_flag(fc->data2, fc->flags >> 16, peer, 133e7adcfeaSclaudio &c->data2, m ? &m->data2 : NULL)) 134e7adcfeaSclaudio return -1; 135b2402ebbSclaudio if (m) 136b2402ebbSclaudio m->data3 &= ~(EXT_COMMUNITY_TRANS_FOUR_AS << 8); 137e7adcfeaSclaudio return 0; 138e7adcfeaSclaudio case EXT_COMMUNITY_TRANS_IPV4: 139e7adcfeaSclaudio if ((fc->flags >> 8 & 0xff) == COMMUNITY_ANY) 140e7adcfeaSclaudio break; 141e7adcfeaSclaudio 142e7adcfeaSclaudio if (apply_flag(fc->data1, fc->flags >> 8, peer, 143e7adcfeaSclaudio &c->data1, m ? &m->data1 : NULL)) 144e7adcfeaSclaudio return -1; 145e7adcfeaSclaudio if (apply_flag(fc->data2, fc->flags >> 16, peer, 146e7adcfeaSclaudio &c->data2, m ? &m->data2 : NULL)) 147e7adcfeaSclaudio return -1; 148e7adcfeaSclaudio /* check that values fit */ 149e7adcfeaSclaudio if (c->data2 > USHRT_MAX) 150e7adcfeaSclaudio return -1; 151e7adcfeaSclaudio return 0; 152e7adcfeaSclaudio case EXT_COMMUNITY_TRANS_OPAQUE: 153e7adcfeaSclaudio case EXT_COMMUNITY_TRANS_EVPN: 154e7adcfeaSclaudio if ((fc->flags >> 8 & 0xff) == COMMUNITY_ANY) 155e7adcfeaSclaudio break; 156e7adcfeaSclaudio 157e7adcfeaSclaudio c->data1 = fc->data1; 158e7adcfeaSclaudio c->data2 = fc->data2; 159ab8e3451Sderaadt return 0; 160e7adcfeaSclaudio } 161e7adcfeaSclaudio 16241c70dd7Sclaudio /* this is for 'ext-community subtype *' */ 16341c70dd7Sclaudio if (m == NULL) 16441c70dd7Sclaudio return -1; 165e7adcfeaSclaudio m->data1 = 0; 166e7adcfeaSclaudio m->data2 = 0; 167e7adcfeaSclaudio return 0; 168e7adcfeaSclaudio default: 16939386878Sclaudio fatalx("%s: unknown type %d", __func__, (uint8_t)c->flags); 170e7adcfeaSclaudio } 171e7adcfeaSclaudio } 172e7adcfeaSclaudio 173e7adcfeaSclaudio static int 174e7adcfeaSclaudio fast_match(const void *va, const void *vb) 175e7adcfeaSclaudio { 176e7adcfeaSclaudio const struct community *a = va; 177e7adcfeaSclaudio const struct community *b = vb; 178e7adcfeaSclaudio 17939386878Sclaudio if ((uint8_t)a->flags != (uint8_t)b->flags) 18039386878Sclaudio return (uint8_t)a->flags > (uint8_t)b->flags ? 1 : -1; 181e7adcfeaSclaudio 182e7adcfeaSclaudio if (a->data1 != b->data1) 183e7adcfeaSclaudio return a->data1 > b->data1 ? 1 : -1; 184e7adcfeaSclaudio if (a->data2 != b->data2) 185e7adcfeaSclaudio return a->data2 > b->data2 ? 1 : -1; 186e7adcfeaSclaudio if (a->data3 != b->data3) 187e7adcfeaSclaudio return a->data3 > b->data3 ? 1 : -1; 188e7adcfeaSclaudio return 0; 189e7adcfeaSclaudio } 190e7adcfeaSclaudio 191e7adcfeaSclaudio static int 192e7adcfeaSclaudio mask_match(struct community *a, struct community *b, struct community *m) 193e7adcfeaSclaudio { 19439386878Sclaudio if ((uint8_t)a->flags != (uint8_t)b->flags) 19539386878Sclaudio return (uint8_t)a->flags > (uint8_t)b->flags ? 1 : -1; 196e7adcfeaSclaudio 197e7adcfeaSclaudio if ((a->data1 & m->data1) != (b->data1 & m->data1)) { 198e7adcfeaSclaudio if ((a->data1 & m->data1) > (b->data1 & m->data1)) 199e7adcfeaSclaudio return 1; 200e7adcfeaSclaudio return -1; 201e7adcfeaSclaudio } 202e7adcfeaSclaudio if ((a->data2 & m->data2) != (b->data2 & m->data2)) { 203e7adcfeaSclaudio if ((a->data2 & m->data2) > (b->data2 & m->data2)) 204e7adcfeaSclaudio return 1; 205e7adcfeaSclaudio return -1; 206e7adcfeaSclaudio } 207e7adcfeaSclaudio if ((a->data3 & m->data3) != (b->data3 & m->data3)) { 208e7adcfeaSclaudio if ((a->data3 & m->data3) > (b->data3 & m->data3)) 209e7adcfeaSclaudio return 1; 210e7adcfeaSclaudio return -1; 211e7adcfeaSclaudio } 212e7adcfeaSclaudio return 0; 213e7adcfeaSclaudio } 214e7adcfeaSclaudio 215e7adcfeaSclaudio /* 216e7adcfeaSclaudio * Insert a community keeping the list sorted. Don't add if already present. 217e7adcfeaSclaudio */ 218e7adcfeaSclaudio static void 219e7adcfeaSclaudio insert_community(struct rde_community *comm, struct community *c) 220e7adcfeaSclaudio { 22123f5897dSclaudio int l; 222e7adcfeaSclaudio int r; 223e7adcfeaSclaudio 224e7adcfeaSclaudio if (comm->nentries + 1 > comm->size) { 225e7adcfeaSclaudio struct community *new; 22623f5897dSclaudio int newsize = comm->size + 8; 227e7adcfeaSclaudio 22866b1afa0Sclaudio if ((new = recallocarray(comm->communities, comm->size, 22966b1afa0Sclaudio newsize, sizeof(struct community))) == NULL) 230e7adcfeaSclaudio fatal(__func__); 231e7adcfeaSclaudio comm->communities = new; 232e7adcfeaSclaudio comm->size = newsize; 233e7adcfeaSclaudio } 234e7adcfeaSclaudio 235e7adcfeaSclaudio /* XXX can be made faster by binary search */ 236e7adcfeaSclaudio for (l = 0; l < comm->nentries; l++) { 237e7adcfeaSclaudio r = fast_match(comm->communities + l, c); 238e7adcfeaSclaudio if (r == 0) { 239e7adcfeaSclaudio /* already present, nothing to do */ 240e7adcfeaSclaudio return; 241e7adcfeaSclaudio } else if (r > 0) { 242e7adcfeaSclaudio /* shift reminder by one slot */ 243e7adcfeaSclaudio memmove(comm->communities + l + 1, 244e7adcfeaSclaudio comm->communities + l, 245e7adcfeaSclaudio (comm->nentries - l) * sizeof(*c)); 246e7adcfeaSclaudio break; 247e7adcfeaSclaudio } 248e7adcfeaSclaudio } 249e7adcfeaSclaudio 250e7adcfeaSclaudio /* insert community at slot l */ 251e7adcfeaSclaudio comm->communities[l] = *c; 252e7adcfeaSclaudio comm->nentries++; 253e7adcfeaSclaudio } 254e7adcfeaSclaudio 255e7adcfeaSclaudio static int 256f8162053Sclaudio non_transitive_ext_community(struct community *c) 257e7adcfeaSclaudio { 25866b1afa0Sclaudio if ((uint8_t)c->flags != COMMUNITY_TYPE_EXT) 25966b1afa0Sclaudio return 0; 260f8162053Sclaudio if ((c->data3 >> 8) & EXT_COMMUNITY_NON_TRANSITIVE) 261e7adcfeaSclaudio return 1; 262e7adcfeaSclaudio return 0; 263e7adcfeaSclaudio } 264e7adcfeaSclaudio 265e7adcfeaSclaudio /* 266e7adcfeaSclaudio * Check if a community is present. This function will expand local-as and 267e7adcfeaSclaudio * neighbor-as and also mask of bits to support partial matches. 268e7adcfeaSclaudio */ 269e7adcfeaSclaudio int 270e7adcfeaSclaudio community_match(struct rde_community *comm, struct community *fc, 271e7adcfeaSclaudio struct rde_peer *peer) 272e7adcfeaSclaudio { 273e7adcfeaSclaudio struct community test, mask; 27423f5897dSclaudio int l; 275e7adcfeaSclaudio 276e7adcfeaSclaudio if (fc->flags >> 8 == 0) { 277e7adcfeaSclaudio /* fast path */ 278e7adcfeaSclaudio return (bsearch(fc, comm->communities, comm->nentries, 279e7adcfeaSclaudio sizeof(*fc), fast_match) != NULL); 280e7adcfeaSclaudio } else { 281e7adcfeaSclaudio /* slow path */ 282e7adcfeaSclaudio if (fc2c(fc, peer, &test, &mask) == -1) 283e7adcfeaSclaudio return 0; 284e7adcfeaSclaudio 285e7adcfeaSclaudio for (l = 0; l < comm->nentries; l++) { 286e7adcfeaSclaudio if (mask_match(&comm->communities[l], &test, 287e7adcfeaSclaudio &mask) == 0) 288e7adcfeaSclaudio return 1; 289e7adcfeaSclaudio } 290e7adcfeaSclaudio return 0; 291e7adcfeaSclaudio } 292e7adcfeaSclaudio } 293e7adcfeaSclaudio 294e7adcfeaSclaudio /* 295bd0e176eSclaudio * Count the number of communities of type type. 296bd0e176eSclaudio */ 297bd0e176eSclaudio int 298bd0e176eSclaudio community_count(struct rde_community *comm, uint8_t type) 299bd0e176eSclaudio { 30023f5897dSclaudio int l; 301bd0e176eSclaudio int count = 0; 302bd0e176eSclaudio 303bd0e176eSclaudio /* use the fact that the array is ordered by type */ 304bd0e176eSclaudio switch (type) { 305bd0e176eSclaudio case COMMUNITY_TYPE_BASIC: 306bd0e176eSclaudio for (l = 0; l < comm->nentries; l++) { 307bd0e176eSclaudio if ((uint8_t)comm->communities[l].flags == type) 308bd0e176eSclaudio count++; 309bd0e176eSclaudio else 310bd0e176eSclaudio break; 311bd0e176eSclaudio } 312bd0e176eSclaudio break; 313bd0e176eSclaudio case COMMUNITY_TYPE_EXT: 314bd0e176eSclaudio for (l = 0; l < comm->nentries; l++) { 315bd0e176eSclaudio if ((uint8_t)comm->communities[l].flags == type) 316bd0e176eSclaudio count++; 317bd0e176eSclaudio else if ((uint8_t)comm->communities[l].flags > type) 318bd0e176eSclaudio break; 319bd0e176eSclaudio } 320bd0e176eSclaudio break; 321bd0e176eSclaudio case COMMUNITY_TYPE_LARGE: 322bd0e176eSclaudio for (l = comm->nentries; l > 0; l--) { 323bd0e176eSclaudio if ((uint8_t)comm->communities[l - 1].flags == type) 324bd0e176eSclaudio count++; 325bd0e176eSclaudio else 326bd0e176eSclaudio break; 327bd0e176eSclaudio } 328bd0e176eSclaudio break; 329bd0e176eSclaudio } 330bd0e176eSclaudio return count; 331bd0e176eSclaudio } 332bd0e176eSclaudio 333bd0e176eSclaudio /* 334e7adcfeaSclaudio * Insert a community, expanding local-as and neighbor-as if needed. 335e7adcfeaSclaudio */ 336e7adcfeaSclaudio int 337e7adcfeaSclaudio community_set(struct rde_community *comm, struct community *fc, 338e7adcfeaSclaudio struct rde_peer *peer) 339e7adcfeaSclaudio { 340e7adcfeaSclaudio struct community set; 341e7adcfeaSclaudio 342e7adcfeaSclaudio if (fc->flags >> 8 == 0) { 343e7adcfeaSclaudio /* fast path */ 344e7adcfeaSclaudio insert_community(comm, fc); 345e7adcfeaSclaudio } else { 346e7adcfeaSclaudio if (fc2c(fc, peer, &set, NULL) == -1) 347e7adcfeaSclaudio return 0; 348b2402ebbSclaudio if ((uint8_t)set.flags == COMMUNITY_TYPE_EXT) { 349b2402ebbSclaudio int type = (int)set.data3 >> 8; 350b2402ebbSclaudio switch (type & EXT_COMMUNITY_VALUE) { 351b2402ebbSclaudio case EXT_COMMUNITY_TRANS_TWO_AS: 352b2402ebbSclaudio case EXT_COMMUNITY_TRANS_FOUR_AS: 353b2402ebbSclaudio /* check that values fit */ 354b2402ebbSclaudio if (set.data1 > USHRT_MAX && 355b2402ebbSclaudio set.data2 > USHRT_MAX) 356b2402ebbSclaudio return 0; 357b2402ebbSclaudio if (set.data1 > USHRT_MAX) 358b2402ebbSclaudio set.data3 = (set.data3 & 0xff) | 359b2402ebbSclaudio EXT_COMMUNITY_TRANS_FOUR_AS << 8; 360b2402ebbSclaudio else 361b2402ebbSclaudio set.data3 = (set.data3 & 0xff) | 362b2402ebbSclaudio EXT_COMMUNITY_TRANS_TWO_AS << 8; 363b2402ebbSclaudio break; 364b2402ebbSclaudio } 365b2402ebbSclaudio } 366e7adcfeaSclaudio insert_community(comm, &set); 367e7adcfeaSclaudio } 368e7adcfeaSclaudio return 1; 369e7adcfeaSclaudio } 370e7adcfeaSclaudio 371e7adcfeaSclaudio /* 372e7adcfeaSclaudio * Remove a community if present, This function will expand local-as and 373e7adcfeaSclaudio * neighbor-as and also mask of bits to support partial matches. 374e7adcfeaSclaudio */ 375e7adcfeaSclaudio void 376e7adcfeaSclaudio community_delete(struct rde_community *comm, struct community *fc, 377e7adcfeaSclaudio struct rde_peer *peer) 378e7adcfeaSclaudio { 379e7adcfeaSclaudio struct community test, mask; 380e7adcfeaSclaudio struct community *match; 38123f5897dSclaudio int l = 0; 382e7adcfeaSclaudio 383e7adcfeaSclaudio if (fc->flags >> 8 == 0) { 384e7adcfeaSclaudio /* fast path */ 385e7adcfeaSclaudio match = bsearch(fc, comm->communities, comm->nentries, 386e7adcfeaSclaudio sizeof(*fc), fast_match); 387e7adcfeaSclaudio if (match == NULL) 388e7adcfeaSclaudio return; 389e7adcfeaSclaudio /* move everything after match down by 1 */ 390e7adcfeaSclaudio memmove(match, match + 1, 391e7adcfeaSclaudio (char *)(comm->communities + comm->nentries) - 392e7adcfeaSclaudio (char *)(match + 1)); 393e7adcfeaSclaudio comm->nentries--; 394e7adcfeaSclaudio return; 395e7adcfeaSclaudio } else { 396e7adcfeaSclaudio if (fc2c(fc, peer, &test, &mask) == -1) 397e7adcfeaSclaudio return; 398e7adcfeaSclaudio 399e7adcfeaSclaudio while (l < comm->nentries) { 400e7adcfeaSclaudio if (mask_match(&comm->communities[l], &test, 401e7adcfeaSclaudio &mask) == 0) { 402e7adcfeaSclaudio memmove(comm->communities + l, 403e7adcfeaSclaudio comm->communities + l + 1, 404e7adcfeaSclaudio (comm->nentries - l - 1) * sizeof(test)); 405e7adcfeaSclaudio comm->nentries--; 406e7adcfeaSclaudio continue; 407e7adcfeaSclaudio } 408e7adcfeaSclaudio l++; 409e7adcfeaSclaudio } 410e7adcfeaSclaudio } 411e7adcfeaSclaudio } 412e7adcfeaSclaudio 413e7adcfeaSclaudio /* 414e7adcfeaSclaudio * Internalize communities from the wireformat. 415e7adcfeaSclaudio * Store the partial flag in struct rde_community so it is not lost. 416e7adcfeaSclaudio * - community_add for ATTR_COMMUNITUES 417e7adcfeaSclaudio * - community_large_add for ATTR_LARGE_COMMUNITIES 418e7adcfeaSclaudio * - community_ext_add for ATTR_EXT_COMMUNITIES 419e7adcfeaSclaudio */ 420e7adcfeaSclaudio int 421b3b1d939Sclaudio community_add(struct rde_community *comm, int flags, struct ibuf *buf) 422e7adcfeaSclaudio { 423e7adcfeaSclaudio struct community set = { .flags = COMMUNITY_TYPE_BASIC }; 424b3b1d939Sclaudio uint16_t data1, data2; 425e7adcfeaSclaudio 426b3b1d939Sclaudio if (ibuf_size(buf) == 0 || ibuf_size(buf) % 4 != 0) 427e7adcfeaSclaudio return -1; 428e7adcfeaSclaudio 429e7adcfeaSclaudio if (flags & ATTR_PARTIAL) 430e7adcfeaSclaudio comm->flags |= PARTIAL_COMMUNITIES; 431e7adcfeaSclaudio 432b3b1d939Sclaudio while (ibuf_size(buf) > 0) { 433b3b1d939Sclaudio if (ibuf_get_n16(buf, &data1) == -1 || 434b3b1d939Sclaudio ibuf_get_n16(buf, &data2) == -1) 435b3b1d939Sclaudio return -1; 436b3b1d939Sclaudio set.data1 = data1; 437b3b1d939Sclaudio set.data2 = data2; 438e7adcfeaSclaudio insert_community(comm, &set); 439e7adcfeaSclaudio } 440e7adcfeaSclaudio 441e7adcfeaSclaudio return 0; 442e7adcfeaSclaudio } 443e7adcfeaSclaudio 444e7adcfeaSclaudio int 445b3b1d939Sclaudio community_large_add(struct rde_community *comm, int flags, struct ibuf *buf) 446e7adcfeaSclaudio { 447e7adcfeaSclaudio struct community set = { .flags = COMMUNITY_TYPE_LARGE }; 448e7adcfeaSclaudio 449b3b1d939Sclaudio if (ibuf_size(buf) == 0 || ibuf_size(buf) % 12 != 0) 450e7adcfeaSclaudio return -1; 451e7adcfeaSclaudio 452e7adcfeaSclaudio if (flags & ATTR_PARTIAL) 453e7adcfeaSclaudio comm->flags |= PARTIAL_LARGE_COMMUNITIES; 454e7adcfeaSclaudio 455b3b1d939Sclaudio while (ibuf_size(buf) > 0) { 456b3b1d939Sclaudio if (ibuf_get_n32(buf, &set.data1) == -1 || 457b3b1d939Sclaudio ibuf_get_n32(buf, &set.data2) == -1 || 458b3b1d939Sclaudio ibuf_get_n32(buf, &set.data3) == -1) 459b3b1d939Sclaudio return -1; 460e7adcfeaSclaudio insert_community(comm, &set); 461e7adcfeaSclaudio } 462e7adcfeaSclaudio 463e7adcfeaSclaudio return 0; 464e7adcfeaSclaudio } 465e7adcfeaSclaudio 466e7adcfeaSclaudio int 467f8162053Sclaudio community_ext_add(struct rde_community *comm, int flags, int ebgp, 468b3b1d939Sclaudio struct ibuf *buf) 469e7adcfeaSclaudio { 470e7adcfeaSclaudio struct community set = { .flags = COMMUNITY_TYPE_EXT }; 47139386878Sclaudio uint64_t c; 472b3b1d939Sclaudio uint8_t type; 473e7adcfeaSclaudio 474b3b1d939Sclaudio if (ibuf_size(buf) == 0 || ibuf_size(buf) % 8 != 0) 475e7adcfeaSclaudio return -1; 476e7adcfeaSclaudio 477e7adcfeaSclaudio if (flags & ATTR_PARTIAL) 478e7adcfeaSclaudio comm->flags |= PARTIAL_EXT_COMMUNITIES; 479e7adcfeaSclaudio 480b3b1d939Sclaudio while (ibuf_size(buf) > 0) { 481b3b1d939Sclaudio if (ibuf_get_n64(buf, &c) == -1) 482b3b1d939Sclaudio return (-1); 483e7adcfeaSclaudio 484e7adcfeaSclaudio type = c >> 56; 485f8162053Sclaudio /* filter out non-transitive ext communuties from ebgp peers */ 486f8162053Sclaudio if (ebgp && (type & EXT_COMMUNITY_NON_TRANSITIVE)) 487f8162053Sclaudio continue; 488f8162053Sclaudio switch (type & EXT_COMMUNITY_VALUE) { 489e7adcfeaSclaudio case EXT_COMMUNITY_TRANS_TWO_AS: 490e7adcfeaSclaudio case EXT_COMMUNITY_TRANS_OPAQUE: 491e7adcfeaSclaudio case EXT_COMMUNITY_TRANS_EVPN: 492e7adcfeaSclaudio set.data1 = c >> 32 & 0xffff; 493e7adcfeaSclaudio set.data2 = c; 494e7adcfeaSclaudio break; 495e7adcfeaSclaudio case EXT_COMMUNITY_TRANS_FOUR_AS: 496e7adcfeaSclaudio case EXT_COMMUNITY_TRANS_IPV4: 497e7adcfeaSclaudio set.data1 = c >> 16; 498e7adcfeaSclaudio set.data2 = c & 0xffff; 499e7adcfeaSclaudio break; 500e7adcfeaSclaudio } 501e7adcfeaSclaudio set.data3 = c >> 48; 502e7adcfeaSclaudio 503e7adcfeaSclaudio insert_community(comm, &set); 504e7adcfeaSclaudio } 505e7adcfeaSclaudio 506e7adcfeaSclaudio return 0; 507e7adcfeaSclaudio } 508e7adcfeaSclaudio 509e7adcfeaSclaudio /* 510e7adcfeaSclaudio * Convert communities back to the wireformat. 511e7adcfeaSclaudio * When writing ATTR_EXT_COMMUNITIES non-transitive communities need to 51266b1afa0Sclaudio * be skipped if they are sent to an ebgp peer. 513e7adcfeaSclaudio */ 514e7adcfeaSclaudio int 51566b1afa0Sclaudio community_writebuf(struct rde_community *comm, uint8_t type, int ebgp, 51666b1afa0Sclaudio struct ibuf *buf) 517e7adcfeaSclaudio { 518e7adcfeaSclaudio struct community *cp; 51939386878Sclaudio uint64_t ext; 52066b1afa0Sclaudio int l, size, start, end, num; 52166b1afa0Sclaudio int flags = ATTR_OPTIONAL | ATTR_TRANSITIVE; 52266b1afa0Sclaudio uint8_t t; 523e7adcfeaSclaudio 52466b1afa0Sclaudio switch (type) { 52566b1afa0Sclaudio case ATTR_COMMUNITIES: 52666b1afa0Sclaudio if (comm->flags & PARTIAL_COMMUNITIES) 52766b1afa0Sclaudio flags |= ATTR_PARTIAL; 52866b1afa0Sclaudio size = 4; 52966b1afa0Sclaudio t = COMMUNITY_TYPE_BASIC; 53066b1afa0Sclaudio break; 53166b1afa0Sclaudio case ATTR_EXT_COMMUNITIES: 532e7adcfeaSclaudio if (comm->flags & PARTIAL_EXT_COMMUNITIES) 533e7adcfeaSclaudio flags |= ATTR_PARTIAL; 53466b1afa0Sclaudio size = 8; 53566b1afa0Sclaudio t = COMMUNITY_TYPE_EXT; 53666b1afa0Sclaudio break; 53766b1afa0Sclaudio case ATTR_LARGE_COMMUNITIES: 53866b1afa0Sclaudio if (comm->flags & PARTIAL_LARGE_COMMUNITIES) 53966b1afa0Sclaudio flags |= ATTR_PARTIAL; 54066b1afa0Sclaudio size = 12; 54166b1afa0Sclaudio t = COMMUNITY_TYPE_LARGE; 54266b1afa0Sclaudio break; 54366b1afa0Sclaudio default: 54466b1afa0Sclaudio return -1; 54566b1afa0Sclaudio } 546e7adcfeaSclaudio 547e7adcfeaSclaudio /* first count how many communities will be written */ 54866b1afa0Sclaudio num = 0; 54966b1afa0Sclaudio start = -1; 55066b1afa0Sclaudio for (l = 0; l < comm->nentries; l++) { 55166b1afa0Sclaudio cp = &comm->communities[l]; 552578f8c88Sclaudio if ((uint8_t)cp->flags == t) { 55366b1afa0Sclaudio if (ebgp && non_transitive_ext_community(cp)) 55466b1afa0Sclaudio continue; 55566b1afa0Sclaudio num++; 55666b1afa0Sclaudio if (start == -1) 55766b1afa0Sclaudio start = l; 55866b1afa0Sclaudio } 55966b1afa0Sclaudio if ((uint8_t)cp->flags > t) 56066b1afa0Sclaudio break; 56166b1afa0Sclaudio } 56266b1afa0Sclaudio end = l; 56366b1afa0Sclaudio 56466b1afa0Sclaudio /* no communities for this type present */ 56566b1afa0Sclaudio if (num == 0) 566e7adcfeaSclaudio return 0; 567e7adcfeaSclaudio 56866b1afa0Sclaudio if (num > INT16_MAX / size) 569e7adcfeaSclaudio return -1; 57066b1afa0Sclaudio 57166b1afa0Sclaudio /* write attribute header */ 57266b1afa0Sclaudio if (attr_writebuf(buf, flags, type, NULL, num * size) == -1) 57366b1afa0Sclaudio return -1; 574e7adcfeaSclaudio 575e7adcfeaSclaudio /* write out the communities */ 57666b1afa0Sclaudio for (l = start; l < end; l++) { 57766b1afa0Sclaudio cp = &comm->communities[l]; 57866b1afa0Sclaudio 57966b1afa0Sclaudio switch (type) { 58066b1afa0Sclaudio case ATTR_COMMUNITIES: 58166b1afa0Sclaudio if (ibuf_add_n16(buf, cp->data1) == -1) 58266b1afa0Sclaudio return -1; 58366b1afa0Sclaudio if (ibuf_add_n16(buf, cp->data2) == -1) 58466b1afa0Sclaudio return -1; 585e7adcfeaSclaudio break; 58666b1afa0Sclaudio case ATTR_EXT_COMMUNITIES: 58766b1afa0Sclaudio if (ebgp && non_transitive_ext_community(cp)) 58853372d18Sclaudio continue; 58953372d18Sclaudio 59039386878Sclaudio ext = (uint64_t)cp->data3 << 48; 591f8162053Sclaudio switch ((cp->data3 >> 8) & EXT_COMMUNITY_VALUE) { 59253372d18Sclaudio case EXT_COMMUNITY_TRANS_TWO_AS: 59353372d18Sclaudio case EXT_COMMUNITY_TRANS_OPAQUE: 59453372d18Sclaudio case EXT_COMMUNITY_TRANS_EVPN: 59539386878Sclaudio ext |= ((uint64_t)cp->data1 & 0xffff) << 32; 59639386878Sclaudio ext |= (uint64_t)cp->data2; 59753372d18Sclaudio break; 59853372d18Sclaudio case EXT_COMMUNITY_TRANS_FOUR_AS: 59953372d18Sclaudio case EXT_COMMUNITY_TRANS_IPV4: 60039386878Sclaudio ext |= (uint64_t)cp->data1 << 16; 60139386878Sclaudio ext |= (uint64_t)cp->data2 & 0xffff; 60253372d18Sclaudio break; 60353372d18Sclaudio } 60466b1afa0Sclaudio if (ibuf_add_n64(buf, ext) == -1) 60553372d18Sclaudio return -1; 60666b1afa0Sclaudio break; 60766b1afa0Sclaudio case ATTR_LARGE_COMMUNITIES: 60866b1afa0Sclaudio if (ibuf_add_n32(buf, cp->data1) == -1) 60966b1afa0Sclaudio return -1; 61066b1afa0Sclaudio if (ibuf_add_n32(buf, cp->data2) == -1) 61166b1afa0Sclaudio return -1; 61266b1afa0Sclaudio if (ibuf_add_n32(buf, cp->data3) == -1) 61366b1afa0Sclaudio return -1; 61466b1afa0Sclaudio break; 61553372d18Sclaudio } 61653372d18Sclaudio } 61753372d18Sclaudio return 0; 61853372d18Sclaudio } 61953372d18Sclaudio 62053372d18Sclaudio /* 621e7adcfeaSclaudio * Global RIB cache for communities 622e7adcfeaSclaudio */ 62323f5897dSclaudio static inline int 62423f5897dSclaudio communities_compare(struct rde_community *a, struct rde_community *b) 625e7adcfeaSclaudio { 62623f5897dSclaudio if (a->nentries != b->nentries) 62723f5897dSclaudio return a->nentries > b->nentries ? 1 : -1; 62823f5897dSclaudio if (a->flags != b->flags) 62923f5897dSclaudio return a->flags > b->flags ? 1 : -1; 630e7adcfeaSclaudio 63123f5897dSclaudio return memcmp(a->communities, b->communities, 63223f5897dSclaudio a->nentries * sizeof(struct community)); 633e7adcfeaSclaudio } 634e7adcfeaSclaudio 63523f5897dSclaudio RB_HEAD(comm_tree, rde_community) commtable = RB_INITIALIZER(&commtable); 63623f5897dSclaudio RB_GENERATE_STATIC(comm_tree, rde_community, entry, communities_compare); 637e7adcfeaSclaudio 638e7adcfeaSclaudio void 639e7adcfeaSclaudio communities_shutdown(void) 640e7adcfeaSclaudio { 64123f5897dSclaudio if (!RB_EMPTY(&commtable)) 642e7adcfeaSclaudio log_warnx("%s: free non-free table", __func__); 643e7adcfeaSclaudio } 644e7adcfeaSclaudio 645e7adcfeaSclaudio struct rde_community * 646e7adcfeaSclaudio communities_lookup(struct rde_community *comm) 647e7adcfeaSclaudio { 64823f5897dSclaudio return RB_FIND(comm_tree, &commtable, comm); 649e7adcfeaSclaudio } 650e7adcfeaSclaudio 651e7adcfeaSclaudio struct rde_community * 652e7adcfeaSclaudio communities_link(struct rde_community *comm) 653e7adcfeaSclaudio { 65423f5897dSclaudio struct rde_community *n, *f; 655e7adcfeaSclaudio 656e7adcfeaSclaudio if ((n = malloc(sizeof(*n))) == NULL) 657e7adcfeaSclaudio fatal(__func__); 658e7adcfeaSclaudio communities_copy(n, comm); 659e7adcfeaSclaudio 66023f5897dSclaudio if ((f = RB_INSERT(comm_tree, &commtable, n)) != NULL) { 66123f5897dSclaudio log_warnx("duplicate communities collection inserted"); 66223f5897dSclaudio free(n->communities); 66323f5897dSclaudio free(n); 66423f5897dSclaudio return f; 66523f5897dSclaudio } 666e7adcfeaSclaudio n->refcnt = 1; /* initial reference by the cache */ 667e7adcfeaSclaudio 668e7adcfeaSclaudio rdemem.comm_size += n->size; 669e7adcfeaSclaudio rdemem.comm_nmemb += n->nentries; 670e7adcfeaSclaudio rdemem.comm_cnt++; 671e7adcfeaSclaudio 672e7adcfeaSclaudio return n; 673e7adcfeaSclaudio } 674e7adcfeaSclaudio 675e7adcfeaSclaudio void 676e7adcfeaSclaudio communities_unlink(struct rde_community *comm) 677e7adcfeaSclaudio { 678e7adcfeaSclaudio if (comm->refcnt != 1) 679e7adcfeaSclaudio fatalx("%s: unlinking still referenced communities", __func__); 680e7adcfeaSclaudio 68123f5897dSclaudio RB_REMOVE(comm_tree, &commtable, comm); 682e7adcfeaSclaudio 683e7adcfeaSclaudio rdemem.comm_size -= comm->size; 684e7adcfeaSclaudio rdemem.comm_nmemb -= comm->nentries; 685e7adcfeaSclaudio rdemem.comm_cnt--; 686e7adcfeaSclaudio 687e7adcfeaSclaudio free(comm->communities); 688e7adcfeaSclaudio free(comm); 689e7adcfeaSclaudio } 690e7adcfeaSclaudio 691e7adcfeaSclaudio /* 692e7adcfeaSclaudio * Return true/1 if the two communities collections are identical, 693e7adcfeaSclaudio * otherwise returns zero. 694e7adcfeaSclaudio */ 695e7adcfeaSclaudio int 696e7adcfeaSclaudio communities_equal(struct rde_community *a, struct rde_community *b) 697e7adcfeaSclaudio { 698e7adcfeaSclaudio if (a->nentries != b->nentries) 699e7adcfeaSclaudio return 0; 700e7adcfeaSclaudio if (a->flags != b->flags) 701e7adcfeaSclaudio return 0; 702e7adcfeaSclaudio 703e7adcfeaSclaudio return (memcmp(a->communities, b->communities, 704e7adcfeaSclaudio a->nentries * sizeof(struct community)) == 0); 705e7adcfeaSclaudio } 706e7adcfeaSclaudio 707e7adcfeaSclaudio /* 708e7adcfeaSclaudio * Copy communities to a new unreferenced struct. Needs to call 709e7adcfeaSclaudio * communities_clean() when done. to can be statically allocated, 710e7adcfeaSclaudio * it will be cleaned first. 711e7adcfeaSclaudio */ 712e7adcfeaSclaudio void 713e7adcfeaSclaudio communities_copy(struct rde_community *to, struct rde_community *from) 714e7adcfeaSclaudio { 715e7adcfeaSclaudio memset(to, 0, sizeof(*to)); 716e7adcfeaSclaudio 7173a50f0a9Sjmc /* ignore from->size and allocate the perfect amount */ 718*07ef3965Sclaudio to->size = from->nentries; 719e7adcfeaSclaudio to->nentries = from->nentries; 720e7adcfeaSclaudio to->flags = from->flags; 721e7adcfeaSclaudio 722*07ef3965Sclaudio if (to->nentries == 0) 723*07ef3965Sclaudio return; 724*07ef3965Sclaudio 725e7adcfeaSclaudio if ((to->communities = reallocarray(NULL, to->size, 726e7adcfeaSclaudio sizeof(struct community))) == NULL) 727e7adcfeaSclaudio fatal(__func__); 728e7adcfeaSclaudio 729e7adcfeaSclaudio memcpy(to->communities, from->communities, 730e7adcfeaSclaudio to->nentries * sizeof(struct community)); 731e7adcfeaSclaudio } 732e7adcfeaSclaudio 733e7adcfeaSclaudio /* 734e7adcfeaSclaudio * Clean up the communities by freeing any dynamically allocated memory. 735e7adcfeaSclaudio */ 736e7adcfeaSclaudio void 737e7adcfeaSclaudio communities_clean(struct rde_community *comm) 738e7adcfeaSclaudio { 739e7adcfeaSclaudio if (comm->refcnt != 0) 740e7adcfeaSclaudio fatalx("%s: cleaning still referenced communities", __func__); 741e7adcfeaSclaudio 742e7adcfeaSclaudio free(comm->communities); 743e7adcfeaSclaudio memset(comm, 0, sizeof(*comm)); 744e7adcfeaSclaudio } 745e7adcfeaSclaudio 746e7adcfeaSclaudio int 74739386878Sclaudio community_to_rd(struct community *fc, uint64_t *community) 748e7adcfeaSclaudio { 749e7adcfeaSclaudio struct community c; 75039386878Sclaudio uint64_t rd; 751e7adcfeaSclaudio 752e7adcfeaSclaudio if (fc2c(fc, NULL, &c, NULL) == -1) 753e7adcfeaSclaudio return -1; 754e7adcfeaSclaudio 755f8162053Sclaudio switch ((c.data3 >> 8) & EXT_COMMUNITY_VALUE) { 756e7adcfeaSclaudio case EXT_COMMUNITY_TRANS_TWO_AS: 757e7adcfeaSclaudio rd = (0ULL << 48); 75839386878Sclaudio rd |= ((uint64_t)c.data1 & 0xffff) << 32; 75939386878Sclaudio rd |= (uint64_t)c.data2; 760e7adcfeaSclaudio break; 761e7adcfeaSclaudio case EXT_COMMUNITY_TRANS_IPV4: 762e7adcfeaSclaudio rd = (1ULL << 48); 76339386878Sclaudio rd |= (uint64_t)c.data1 << 16; 76439386878Sclaudio rd |= (uint64_t)c.data2 & 0xffff; 765e7adcfeaSclaudio break; 766e7adcfeaSclaudio case EXT_COMMUNITY_TRANS_FOUR_AS: 767e7adcfeaSclaudio rd = (2ULL << 48); 76839386878Sclaudio rd |= (uint64_t)c.data1 << 16; 76939386878Sclaudio rd |= (uint64_t)c.data2 & 0xffff; 770e7adcfeaSclaudio break; 771e7adcfeaSclaudio default: 772e7adcfeaSclaudio return -1; 773e7adcfeaSclaudio } 774e7adcfeaSclaudio 775e7adcfeaSclaudio *community = htobe64(rd); 776e7adcfeaSclaudio return 0; 777e7adcfeaSclaudio } 778