attrs.c 56.7 KB
Newer Older
1 2 3 4
/*
 *	BIRD -- BGP Attributes
 *
 *	(c) 2000 Martin Mares <mj@ucw.cz>
Ondřej Zajíček's avatar
Ondřej Zajíček committed
5 6
 *	(c) 2008--2016 Ondrej Zajicek <santiago@crfreenet.org>
 *	(c) 2008--2016 CZ.NIC z.s.p.o.
7 8 9 10
 *
 *	Can be freely distributed and used under the terms of the GNU GPL.
 */

Martin Mareš's avatar
Martin Mareš committed
11
#undef LOCAL_DEBUG
12

13 14
#include <stdlib.h>

15 16 17 18
#include "nest/bird.h"
#include "nest/iface.h"
#include "nest/protocol.h"
#include "nest/route.h"
19
#include "nest/attrs.h"
20
#include "conf/conf.h"
21 22 23
#include "lib/resource.h"
#include "lib/string.h"
#include "lib/unaligned.h"
24 25

#include "bgp.h"
26

27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
/*
 *   UPDATE message error handling
 *
 * All checks from RFC 4271 6.3 are done as specified with these exceptions:
 *  - The semantic check of an IP address from NEXT_HOP attribute is missing.
 *  - Checks of some optional attribute values are missing.
 *  - Syntactic and semantic checks of NLRIs (done in DECODE_PREFIX())
 *    are probably inadequate.
 *
 * Loop detection based on AS_PATH causes updates to be withdrawn. RFC
 * 4271 does not explicitly specifiy the behavior in that case.
 *
 * Loop detection related to route reflection (based on ORIGINATOR_ID
 * and CLUSTER_LIST) causes updates to be withdrawn. RFC 4456 8
 * specifies that such updates should be ignored, but that is generally
 * a bad idea.
 *
Ondřej Zajíček's avatar
Ondřej Zajíček committed
44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64
 * BGP attribute table has several hooks:
 *
 * export - Hook that validates and normalizes attribute during export phase.
 * Receives eattr, may modify it (e.g., sort community lists for canonical
 * representation), UNSET() it (e.g., skip empty lists), or WITHDRAW() it if
 * necessary. May assume that eattr has value valid w.r.t. its type, but may be
 * invalid w.r.t. BGP constraints. Optional.
 *
 * encode - Hook that converts internal representation to external one during
 * packet writing. Receives eattr and puts it in the buffer (including attribute
 * header). Returns number of bytes, or -1 if not enough space. May assume that
 * eattr has value valid w.r.t. its type and validated by export hook. Mandatory
 * for all known attributes that exist internally after export phase (i.e., all
 * except pseudoattributes MP_(UN)REACH_NLRI).
 *
 * decode - Hook that converts external representation to internal one during
 * packet parsing. Receives attribute data in buffer, validates it and adds
 * attribute to ea_list. If data are invalid, steps DISCARD(), WITHDRAW() or
 * bgp_parse_error() may be used to escape. Mandatory for all known attributes.
 *
 * format - Optional hook that converts eattr to textual representation.
65 66
 */

67

Ondřej Zajíček's avatar
Ondřej Zajíček committed
68 69 70 71 72 73 74 75
struct bgp_attr_desc {
  const char *name;
  uint type;
  uint flags;
  void (*export)(struct bgp_export_state *s, eattr *a);
  int  (*encode)(struct bgp_write_state *s, eattr *a, byte *buf, uint size);
  void (*decode)(struct bgp_parse_state *s, uint code, uint flags, byte *data, uint len, ea_list **to);
  void (*format)(eattr *ea, byte *buf, uint size);
76 77
};

Ondřej Zajíček's avatar
Ondřej Zajíček committed
78 79 80 81 82 83 84 85 86
static const struct bgp_attr_desc bgp_attr_table[];

static inline int bgp_attr_known(uint code);

eattr *
bgp_set_attr(ea_list **attrs, struct linpool *pool, uint code, uint flags, uintptr_t val)
{
  ASSERT(bgp_attr_known(code));

87 88 89 90 91 92 93 94
  return ea_set_attr(
      attrs,
      pool,
      EA_CODE(PROTOCOL_BGP, code),
      flags,
      bgp_attr_table[code].type,
      val
  );
Ondřej Zajíček's avatar
Ondřej Zajíček committed
95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143
}



#define REPORT(msg, args...) \
  ({ log(L_REMOTE "%s: " msg, s->proto->p.name, ## args); })

#define DISCARD(msg, args...) \
  ({ REPORT(msg, ## args); return; })

#define WITHDRAW(msg, args...) \
  ({ REPORT(msg, ## args); s->err_withdraw = 1; return; })

#define UNSET(a) \
  ({ a->type = EAF_TYPE_UNDEF; return; })

#define NEW_BGP		"Discarding %s attribute received from AS4-aware neighbor"
#define BAD_EBGP	"Discarding %s attribute received from EBGP neighbor"
#define BAD_LENGTH	"Malformed %s attribute - invalid length (%u)"
#define BAD_VALUE	"Malformed %s attribute - invalid value (%u)"
#define NO_MANDATORY	"Missing mandatory %s attribute"


static inline int
bgp_put_attr_hdr3(byte *buf, uint code, uint flags, uint len)
{
  *buf++ = flags;
  *buf++ = code;
  *buf++ = len;
  return 3;
}

static inline int
bgp_put_attr_hdr4(byte *buf, uint code, uint flags, uint len)
{
  *buf++ = flags | BAF_EXT_LEN;
  *buf++ = code;
  put_u16(buf, len);
  return 4;
}

static inline int
bgp_put_attr_hdr(byte *buf, uint code, uint flags, uint len)
{
  if (len < 256)
    return bgp_put_attr_hdr3(buf, code, flags, len);
  else
    return bgp_put_attr_hdr4(buf, code, flags, len);
}
144

145
static int
Ondřej Zajíček's avatar
Ondřej Zajíček committed
146
bgp_encode_u8(struct bgp_write_state *s UNUSED, eattr *a, byte *buf, uint size)
147
{
Ondřej Zajíček's avatar
Ondřej Zajíček committed
148 149 150 151 152 153 154
  if (size < (3+1))
    return -1;

  bgp_put_attr_hdr3(buf, EA_ID(a->id), a->flags, 1);
  buf[3] = a->u.data;

  return 3+1;
155 156
}

Ondřej Zajíček's avatar
Ondřej Zajíček committed
157 158
static int
bgp_encode_u32(struct bgp_write_state *s UNUSED, eattr *a, byte *buf, uint size)
159
{
Ondřej Zajíček's avatar
Ondřej Zajíček committed
160 161 162 163 164
  if (size < (3+4))
    return -1;

  bgp_put_attr_hdr3(buf, EA_ID(a->id), a->flags, 4);
  put_u32(buf+3, a->u.data);
165

Ondřej Zajíček's avatar
Ondřej Zajíček committed
166
  return 3+4;
167 168 169
}

static int
Ondřej Zajíček's avatar
Ondřej Zajíček committed
170
bgp_encode_u32s(struct bgp_write_state *s UNUSED, eattr *a, byte *buf, uint size)
171
{
Ondřej Zajíček's avatar
Ondřej Zajíček committed
172
  uint len = a->u.ptr->length;
173

Ondřej Zajíček's avatar
Ondřej Zajíček committed
174 175
  if (size < (4+len))
    return -1;
176

Ondřej Zajíček's avatar
Ondřej Zajíček committed
177 178 179 180
  uint hdr = bgp_put_attr_hdr(buf, EA_ID(a->id), a->flags, len);
  put_u32s(buf + hdr, (u32 *) a->u.ptr->data, len / 4);

  return hdr + len;
181 182
}

183
static int
Ondřej Zajíček's avatar
Ondřej Zajíček committed
184
bgp_put_attr(byte *buf, uint size, uint code, uint flags, byte *data, uint len)
185
{
Ondřej Zajíček's avatar
Ondřej Zajíček committed
186 187
  if (size < (4+len))
    return -1;
188

Ondřej Zajíček's avatar
Ondřej Zajíček committed
189 190
  uint hdr = bgp_put_attr_hdr(buf, code, flags, len);
  memcpy(buf + hdr, data, len);
191

Ondřej Zajíček's avatar
Ondřej Zajíček committed
192 193 194 195 196 197 198 199 200 201 202 203 204
  return hdr + len;
}

static int
bgp_encode_raw(struct bgp_write_state *s UNUSED, eattr *a, byte *buf, uint size)
{
  return bgp_put_attr(buf, size, EA_ID(a->id), a->flags, a->u.ptr->data, a->u.ptr->length);
}


/*
 *	Attribute hooks
 */
205

Ondřej Zajíček's avatar
Ondřej Zajíček committed
206 207 208 209 210
static void
bgp_export_origin(struct bgp_export_state *s, eattr *a)
{
  if (a->u.data > 2)
    WITHDRAW(BAD_VALUE, "ORIGIN", a->u.data);
211 212
}

Ondřej Zajíček's avatar
Ondřej Zajíček committed
213 214
static void
bgp_decode_origin(struct bgp_parse_state *s, uint code UNUSED, uint flags, byte *data, uint len, ea_list **to)
215
{
Ondřej Zajíček's avatar
Ondřej Zajíček committed
216 217 218 219 220 221 222
  if (len != 1)
    WITHDRAW(BAD_LENGTH, "ORIGIN", len);

  if (data[0] > 2)
    WITHDRAW(BAD_VALUE, "ORIGIN", data[0]);

  bgp_set_attr_u32(to, s->pool, BA_ORIGIN, flags, data[0]);
223 224
}

Ondřej Zajíček's avatar
Ondřej Zajíček committed
225 226
static void
bgp_format_origin(eattr *a, byte *buf, uint size UNUSED)
227
{
Ondřej Zajíček's avatar
Ondřej Zajíček committed
228 229 230
  static const char *bgp_origin_names[] = { "IGP", "EGP", "Incomplete" };

  bsprintf(buf, (a->u.data <= 2) ? bgp_origin_names[a->u.data] : "?");
231 232
}

Ondřej Zajíček's avatar
Ondřej Zajíček committed
233

234
static int
Ondřej Zajíček's avatar
Ondřej Zajíček committed
235
bgp_encode_as_path(struct bgp_write_state *s, eattr *a, byte *buf, uint size)
236
{
Ondřej Zajíček's avatar
Ondřej Zajíček committed
237 238
  byte *data = a->u.ptr->data;
  uint len = a->u.ptr->length;
239

Ondřej Zajíček's avatar
Ondřej Zajíček committed
240 241 242 243 244 245 246 247 248 249 250 251 252 253
  if (!s->as4_session)
  {
    /* Prepare 16-bit AS_PATH (from 32-bit one) in a temporary buffer */
    byte *src = data;
    data = alloca(len);
    len = as_path_32to16(data, src, len);
  }

  return bgp_put_attr(buf, size, BA_AS_PATH, a->flags, data, len);
}

static void
bgp_decode_as_path(struct bgp_parse_state *s, uint code UNUSED, uint flags, byte *data, uint len, ea_list **to)
{
254 255 256
  struct bgp_proto *p = s->proto;
  int as_length = s->as4_session ? 4 : 2;
  int as_confed = p->cf->confederation && p->is_interior;
Ondřej Zajíček's avatar
Ondřej Zajíček committed
257 258
  char err[128];

259
  if (!as_path_valid(data, len, as_length, as_confed, err, sizeof(err)))
Ondřej Zajíček's avatar
Ondřej Zajíček committed
260 261
    WITHDRAW("Malformed AS_PATH attribute - %s", err);

262 263 264 265 266
  /* In some circumstances check for initial AS_CONFED_SEQUENCE; RFC 5065 5.0 */
  if (p->is_interior && !p->is_internal &&
      ((len < 2) || (data[0] != AS_PATH_CONFED_SEQUENCE)))
    WITHDRAW("Malformed AS_PATH attribute - %s", "missing initial AS_CONFED_SEQUENCE");

Ondřej Zajíček's avatar
Ondřej Zajíček committed
267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287
  if (!s->as4_session)
  {
    /* Prepare 32-bit AS_PATH (from 16-bit one) in a temporary buffer */
    byte *src = data;
    data = alloca(2*len);
    len = as_path_16to32(data, src, len);
  }

  bgp_set_attr_data(to, s->pool, BA_AS_PATH, flags, data, len);
}


static int
bgp_encode_next_hop(struct bgp_write_state *s, eattr *a, byte *buf, uint size)
{
  /*
   * The NEXT_HOP attribute is used only in traditional (IPv4) BGP. In MP-BGP,
   * the next hop is encoded as a part of the MP_REACH_NLRI attribute, so we
   * store it and encode it later by AFI-specific hooks.
   */

Ondřej Zajíček's avatar
Ondřej Zajíček committed
288
  if (!s->mp_reach)
Ondřej Zajíček's avatar
Ondřej Zajíček committed
289
  {
Ondřej Zajíček's avatar
Ondřej Zajíček committed
290 291 292 293 294 295
    // ASSERT(a->u.ptr->length == sizeof(ip_addr));

    /* FIXME: skip IPv6 next hops for IPv4 routes during MRT dump */
    ip_addr *addr = (void *) a->u.ptr->data;
    if ((a->u.ptr->length != sizeof(ip_addr)) || !ipa_is_ip4(*addr))
      return 0;
Ondřej Zajíček's avatar
Ondřej Zajíček committed
296 297 298 299 300

    if (size < (3+4))
      return -1;

    bgp_put_attr_hdr3(buf, BA_NEXT_HOP, a->flags, 4);
Ondřej Zajíček's avatar
Ondřej Zajíček committed
301
    put_ip4(buf+3, ipa_to_ip4(*addr));
Ondřej Zajíček's avatar
Ondřej Zajíček committed
302 303 304 305 306 307

    return 3+4;
  }
  else
  {
    s->mp_next_hop = a;
308
    return 0;
Ondřej Zajíček's avatar
Ondřej Zajíček committed
309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334
  }
}

static void
bgp_decode_next_hop(struct bgp_parse_state *s, uint code UNUSED, uint flags UNUSED, byte *data, uint len, ea_list **to UNUSED)
{
  if (len != 4)
    WITHDRAW(BAD_LENGTH, "NEXT_HOP", len);

  /* Semantic checks are done later */
  s->ip_next_hop_len = len;
  s->ip_next_hop_data = data;
}

/* TODO: This function should use AF-specific hook */
static void
bgp_format_next_hop(eattr *a, byte *buf, uint size UNUSED)
{
  ip_addr *nh = (void *) a->u.ptr->data;
  uint len = a->u.ptr->length;

  ASSERT((len == 16) || (len == 32));

  /* in IPv6, we may have two addresses in NEXT HOP */
  if ((len == 16) || ipa_zero(nh[1]))
    bsprintf(buf, "%I", nh[0]);
335
  else
Ondřej Zajíček's avatar
Ondřej Zajíček committed
336
    bsprintf(buf, "%I %I", nh[0], nh[1]);
Martin Mareš's avatar
Martin Mareš committed
337 338
}

Ondřej Zajíček's avatar
Ondřej Zajíček committed
339

340
static void
Ondřej Zajíček's avatar
Ondřej Zajíček committed
341
bgp_decode_med(struct bgp_parse_state *s, uint code UNUSED, uint flags, byte *data, uint len, ea_list **to)
342
{
Ondřej Zajíček's avatar
Ondřej Zajíček committed
343 344
  if (len != 4)
    WITHDRAW(BAD_LENGTH, "MULTI_EXIT_DISC", len);
345

Ondřej Zajíček's avatar
Ondřej Zajíček committed
346 347
  u32 val = get_u32(data);
  bgp_set_attr_u32(to, s->pool, BA_MULTI_EXIT_DISC, flags, val);
348 349
}

Ondřej Zajíček's avatar
Ondřej Zajíček committed
350 351 352

static void
bgp_export_local_pref(struct bgp_export_state *s, eattr *a)
353
{
354
  if (!s->proto->is_interior && !s->proto->cf->allow_local_pref)
Ondřej Zajíček's avatar
Ondřej Zajíček committed
355
    UNSET(a);
356 357
}

358
static void
Ondřej Zajíček's avatar
Ondřej Zajíček committed
359
bgp_decode_local_pref(struct bgp_parse_state *s, uint code UNUSED, uint flags, byte *data, uint len, ea_list **to)
360
{
361
  if (!s->proto->is_interior && !s->proto->cf->allow_local_pref)
Ondřej Zajíček's avatar
Ondřej Zajíček committed
362
    DISCARD(BAD_EBGP, "LOCAL_PREF");
363

Ondřej Zajíček's avatar
Ondřej Zajíček committed
364 365
  if (len != 4)
    WITHDRAW(BAD_LENGTH, "LOCAL_PREF", len);
366

Ondřej Zajíček's avatar
Ondřej Zajíček committed
367 368
  u32 val = get_u32(data);
  bgp_set_attr_u32(to, s->pool, BA_LOCAL_PREF, flags, val);
369 370
}

Ondřej Zajíček's avatar
Ondřej Zajíček committed
371 372 373

static void
bgp_decode_atomic_aggr(struct bgp_parse_state *s, uint code UNUSED, uint flags, byte *data UNUSED, uint len, ea_list **to)
374
{
Ondřej Zajíček's avatar
Ondřej Zajíček committed
375 376 377 378
  if (len != 0)
    DISCARD(BAD_LENGTH, "ATOMIC_AGGR", len);

  bgp_set_attr_data(to, s->pool, BA_ATOMIC_AGGR, flags, NULL, 0);
379 380
}

381
static int
Ondřej Zajíček's avatar
Ondřej Zajíček committed
382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446
bgp_encode_aggregator(struct bgp_write_state *s, eattr *a, byte *buf, uint size)
{
  byte *data = a->u.ptr->data;
  uint len = a->u.ptr->length;

  if (!s->as4_session)
  {
    /* Prepare 16-bit AGGREGATOR (from 32-bit one) in a temporary buffer */
    byte *src = data;
    data = alloca(6);
    len = aggregator_32to16(data, src);
  }

  return bgp_put_attr(buf, size, BA_AGGREGATOR, a->flags, data, len);
}

static void
bgp_decode_aggregator(struct bgp_parse_state *s, uint code UNUSED, uint flags, byte *data, uint len, ea_list **to)
{
  if (len != (s->as4_session ? 8 : 6))
    DISCARD(BAD_LENGTH, "AGGREGATOR", len);

  if (!s->as4_session)
  {
    /* Prepare 32-bit AGGREGATOR (from 16-bit one) in a temporary buffer */
    byte *src = data;
    data = alloca(8);
    len = aggregator_16to32(data, src);
  }

  bgp_set_attr_data(to, s->pool, BA_AGGREGATOR, flags, data, len);
}

static void
bgp_format_aggregator(eattr *a, byte *buf, uint size UNUSED)
{
  byte *data = a->u.ptr->data;

  bsprintf(buf, "%I4 AS%u", get_ip4(data+4), get_u32(data+0));
}


static void
bgp_export_community(struct bgp_export_state *s, eattr *a)
{
  if (a->u.ptr->length == 0)
    UNSET(a);

  a->u.ptr = int_set_sort(s->pool, a->u.ptr);
}

static void
bgp_decode_community(struct bgp_parse_state *s, uint code UNUSED, uint flags, byte *data, uint len, ea_list **to)
{
  if (!len || (len % 4))
    WITHDRAW(BAD_LENGTH, "COMMUNITY", len);

  struct adata *ad = lp_alloc_adata(s->pool, len);
  get_u32s(data, (u32 *) ad->data, len / 4);
  bgp_set_attr_ptr(to, s->pool, BA_COMMUNITY, flags, ad);
}


static void
bgp_export_originator_id(struct bgp_export_state *s, eattr *a)
447
{
Ondřej Zajíček's avatar
Ondřej Zajíček committed
448 449
  if (!s->proto->is_internal)
    UNSET(a);
450 451
}

452
static void
Ondřej Zajíček's avatar
Ondřej Zajíček committed
453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491
bgp_decode_originator_id(struct bgp_parse_state *s, uint code UNUSED, uint flags, byte *data, uint len, ea_list **to)
{
  if (!s->proto->is_internal)
    DISCARD(BAD_EBGP, "ORIGINATOR_ID");

  if (len != 4)
    WITHDRAW(BAD_LENGTH, "ORIGINATOR_ID", len);

  u32 val = get_u32(data);
  bgp_set_attr_u32(to, s->pool, BA_ORIGINATOR_ID, flags, val);
}


static void
bgp_export_cluster_list(struct bgp_export_state *s UNUSED, eattr *a)
{
  if (!s->proto->is_internal)
    UNSET(a);

  if (a->u.ptr->length == 0)
    UNSET(a);
}

static void
bgp_decode_cluster_list(struct bgp_parse_state *s, uint code UNUSED, uint flags, byte *data, uint len, ea_list **to)
{
  if (!s->proto->is_internal)
    DISCARD(BAD_EBGP, "CLUSTER_LIST");

  if (!len || (len % 4))
    WITHDRAW(BAD_LENGTH, "CLUSTER_LIST", len);

  struct adata *ad = lp_alloc_adata(s->pool, len);
  get_u32s(data, (u32 *) ad->data, len / 4);
  bgp_set_attr_ptr(to, s->pool, BA_CLUSTER_LIST, flags, ad);
}

static void
bgp_format_cluster_list(eattr *a, byte *buf, uint size)
492
{
493
  /* Truncates cluster lists larger than buflen, probably not a problem */
Ondřej Zajíček's avatar
Ondřej Zajíček committed
494
  int_set_format(a->u.ptr, 0, -1, buf, size);
495 496
}

Ondřej Zajíček's avatar
Ondřej Zajíček committed
497 498 499

static inline u32
get_af3(byte *buf)
Martin Mareš's avatar
Martin Mareš committed
500
{
Ondřej Zajíček's avatar
Ondřej Zajíček committed
501
  return (get_u16(buf) << 16) | buf[2];
Martin Mareš's avatar
Martin Mareš committed
502 503
}

Ondřej Zajíček's avatar
Ondřej Zajíček committed
504 505
static void
bgp_decode_mp_reach_nlri(struct bgp_parse_state *s, uint code UNUSED, uint flags UNUSED, byte *data, uint len, ea_list **to UNUSED)
Martin Mareš's avatar
Martin Mareš committed
506
{
Ondřej Zajíček's avatar
Ondřej Zajíček committed
507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523
  /*
   *	2 B	MP_REACH_NLRI data - Address Family Identifier
   *	1 B	MP_REACH_NLRI data - Subsequent Address Family Identifier
   *	1 B	MP_REACH_NLRI data - Length of Next Hop Network Address
   *	var	MP_REACH_NLRI data - Network Address of Next Hop
   *	1 B	MP_REACH_NLRI data - Reserved (zero)
   *	var	MP_REACH_NLRI data - Network Layer Reachability Information
   */

  if ((len < 5) || (len < (5 + (uint) data[3])))
    bgp_parse_error(s, 9);

  s->mp_reach_af = get_af3(data);
  s->mp_next_hop_len = data[3];
  s->mp_next_hop_data = data + 4;
  s->mp_reach_len = len - 5 - s->mp_next_hop_len;
  s->mp_reach_nlri = data + 5 + s->mp_next_hop_len;
524 525
}

Ondřej Zajíček's avatar
Ondřej Zajíček committed
526 527 528

static void
bgp_decode_mp_unreach_nlri(struct bgp_parse_state *s, uint code UNUSED, uint flags UNUSED, byte *data, uint len, ea_list **to UNUSED)
529
{
Ondřej Zajíček's avatar
Ondřej Zajíček committed
530 531 532 533 534 535 536 537 538 539 540 541
  /*
   *	2 B	MP_UNREACH_NLRI data - Address Family Identifier
   *	1 B	MP_UNREACH_NLRI data - Subsequent Address Family Identifier
   *	var	MP_UNREACH_NLRI data - Network Layer Reachability Information
   */

  if (len < 3)
    bgp_parse_error(s, 9);

  s->mp_unreach_af = get_af3(data);
  s->mp_unreach_len = len - 3;
  s->mp_unreach_nlri = data + 3;
542 543
}

Ondřej Zajíček's avatar
Ondřej Zajíček committed
544 545 546 547

static void
bgp_export_ext_community(struct bgp_export_state *s, eattr *a)
{
548 549
  a->u.ptr = ec_set_del_nontrans(s->pool, a->u.ptr);

Ondřej Zajíček's avatar
Ondřej Zajíček committed
550 551 552
  if (a->u.ptr->length == 0)
    UNSET(a);

553
  ec_set_sort_x(a->u.ptr);
Ondřej Zajíček's avatar
Ondřej Zajíček committed
554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590
}

static void
bgp_decode_ext_community(struct bgp_parse_state *s, uint code UNUSED, uint flags, byte *data, uint len, ea_list **to)
{
  if (!len || (len % 8))
    WITHDRAW(BAD_LENGTH, "EXT_COMMUNITY", len);

  struct adata *ad = lp_alloc_adata(s->pool, len);
  get_u32s(data, (u32 *) ad->data, len / 4);
  bgp_set_attr_ptr(to, s->pool, BA_EXT_COMMUNITY, flags, ad);
}


static void
bgp_decode_as4_aggregator(struct bgp_parse_state *s, uint code UNUSED, uint flags, byte *data, uint len, ea_list **to)
{
  if (s->as4_session)
    DISCARD(NEW_BGP, "AS4_AGGREGATOR");

  if (len != 8)
    DISCARD(BAD_LENGTH, "AS4_AGGREGATOR", len);

  bgp_set_attr_data(to, s->pool, BA_AS4_AGGREGATOR, flags, data, len);
}

static void
bgp_decode_as4_path(struct bgp_parse_state *s, uint code UNUSED, uint flags, byte *data, uint len, ea_list **to)
{
  char err[128];

  if (s->as4_session)
    DISCARD(NEW_BGP, "AS4_PATH");

  if (len < 6)
    DISCARD(BAD_LENGTH, "AS4_PATH", len);

591
  if (!as_path_valid(data, len, 4, 1, err, sizeof(err)))
Ondřej Zajíček's avatar
Ondřej Zajíček committed
592 593
    DISCARD("Malformed AS4_PATH attribute - %s", err);

594 595 596 597 598 599 600 601 602 603 604
  struct adata *a = lp_alloc_adata(s->pool, len);
  memcpy(a->data, data, len);

  /* AS_CONFED* segments are invalid in AS4_PATH; RFC 6793 6 */
  if (as_path_contains_confed(a))
  {
    REPORT("Discarding AS_CONFED* segment from AS4_PATH attribute");
    a = as_path_strip_confed(s->pool, a);
  }

  bgp_set_attr_ptr(to, s->pool, BA_AS4_PATH, flags, a);
Ondřej Zajíček's avatar
Ondřej Zajíček committed
605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626
}

static void
bgp_export_large_community(struct bgp_export_state *s, eattr *a)
{
  if (a->u.ptr->length == 0)
    UNSET(a);

  a->u.ptr = lc_set_sort(s->pool, a->u.ptr);
}

static void
bgp_decode_large_community(struct bgp_parse_state *s, uint code UNUSED, uint flags, byte *data, uint len, ea_list **to)
{
  if (!len || (len % 12))
    WITHDRAW(BAD_LENGTH, "LARGE_COMMUNITY", len);

  struct adata *ad = lp_alloc_adata(s->pool, len);
  get_u32s(data, (u32 *) ad->data, len / 4);
  bgp_set_attr_ptr(to, s->pool, BA_LARGE_COMMUNITY, flags, ad);
}

627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695
static void
bgp_export_mpls_label_stack(struct bgp_export_state *s, eattr *a)
{
  net_addr *n = s->route->net->n.addr;
  u32 *labels = (u32 *) a->u.ptr->data;
  uint lnum = a->u.ptr->length / 4;

  /* Perhaps we should just ignore it? */
  if (!s->mpls)
    WITHDRAW("Unexpected MPLS stack");

  /* Empty MPLS stack is not allowed */
  if (!lnum)
    WITHDRAW("Malformed MPLS stack - empty");

  /* This is ugly, but we must ensure that labels fit into NLRI field */
  if ((24*lnum + (net_is_vpn(n) ? 64 : 0) + net_pxlen(n)) > 255)
    WITHDRAW("Malformed MPLS stack - too many labels (%u)", lnum);

  for (uint i = 0; i < lnum; i++)
  {
    if (labels[i] > 0xfffff)
      WITHDRAW("Malformed MPLS stack - invalid label (%u)", labels[i]);

    /* TODO: Check for special-purpose label values? */
  }
}

static int
bgp_encode_mpls_label_stack(struct bgp_write_state *s, eattr *a, byte *buf UNUSED, uint size UNUSED)
{
  /*
   * MPLS labels are encoded as a part of the NLRI in MP_REACH_NLRI attribute,
   * so we store MPLS_LABEL_STACK and encode it later by AFI-specific hooks.
   */

  s->mpls_labels = a->u.ptr;
  return 0;
}

static void
bgp_decode_mpls_label_stack(struct bgp_parse_state *s, uint code UNUSED, uint flags UNUSED, byte *data UNUSED, uint len UNUSED, ea_list **to UNUSED)
{
  DISCARD("Discarding received attribute #0");
}

static void
bgp_format_mpls_label_stack(eattr *a, byte *buf, uint size)
{
  u32 *labels = (u32 *) a->u.ptr->data;
  uint lnum = a->u.ptr->length / 4;
  char *pos = buf;

  for (uint i = 0; i < lnum; i++)
  {
    if (size < 20)
    {
      bsprintf(pos, "...");
      return;
    }

    uint l = bsprintf(pos, "%d/", labels[i]);
    ADVANCE(pos, size, l);
  }

  /* Clear last slash or terminate empty string */
  pos[lnum ? -1 : 0] = 0;
}

Ondřej Zajíček's avatar
Ondřej Zajíček committed
696 697 698
static inline void
bgp_decode_unknown(struct bgp_parse_state *s, uint code, uint flags, byte *data, uint len, ea_list **to)
{
699
  /* Cannot use bgp_set_attr_data() as it works on known attributes only */
700
  ea_set_attr_data(to, s->pool, EA_CODE(PROTOCOL_BGP, code), flags, EAF_TYPE_OPAQUE, data, len);
Ondřej Zajíček's avatar
Ondřej Zajíček committed
701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830
}


/*
 *	Attribute table
 */

static const struct bgp_attr_desc bgp_attr_table[] = {
  [BA_ORIGIN] = {
    .name = "origin",
    .type = EAF_TYPE_INT,
    .flags = BAF_TRANSITIVE,
    .export = bgp_export_origin,
    .encode = bgp_encode_u8,
    .decode = bgp_decode_origin,
    .format = bgp_format_origin,
  },
  [BA_AS_PATH] = {
    .name = "as_path",
    .type = EAF_TYPE_AS_PATH,
    .flags = BAF_TRANSITIVE,
    .encode = bgp_encode_as_path,
    .decode = bgp_decode_as_path,
  },
  [BA_NEXT_HOP] = {
    .name = "next_hop",
    .type = EAF_TYPE_IP_ADDRESS,
    .flags = BAF_TRANSITIVE,
    .encode = bgp_encode_next_hop,
    .decode = bgp_decode_next_hop,
    .format = bgp_format_next_hop,
  },
  [BA_MULTI_EXIT_DISC] = {
    .name = "med",
    .type = EAF_TYPE_INT,
    .flags = BAF_OPTIONAL,
    .encode = bgp_encode_u32,
    .decode = bgp_decode_med,
  },
  [BA_LOCAL_PREF] = {
    .name = "local_pref",
    .type = EAF_TYPE_INT,
    .flags = BAF_TRANSITIVE,
    .export = bgp_export_local_pref,
    .encode = bgp_encode_u32,
    .decode = bgp_decode_local_pref,
  },
  [BA_ATOMIC_AGGR] = {
    .name = "atomic_aggr",
    .type = EAF_TYPE_OPAQUE,
    .flags = BAF_TRANSITIVE,
    .encode = bgp_encode_raw,
    .decode = bgp_decode_atomic_aggr,
  },
  [BA_AGGREGATOR] = {
    .name = "aggregator",
    .type = EAF_TYPE_OPAQUE,
    .flags = BAF_OPTIONAL | BAF_TRANSITIVE,
    .encode = bgp_encode_aggregator,
    .decode = bgp_decode_aggregator,
    .format = bgp_format_aggregator,
  },
  [BA_COMMUNITY] = {
    .name = "community",
    .type = EAF_TYPE_INT_SET,
    .flags = BAF_OPTIONAL | BAF_TRANSITIVE,
    .export = bgp_export_community,
    .encode = bgp_encode_u32s,
    .decode = bgp_decode_community,
  },
  [BA_ORIGINATOR_ID] = {
    .name = "originator_id",
    .type = EAF_TYPE_ROUTER_ID,
    .flags = BAF_OPTIONAL,
    .export = bgp_export_originator_id,
    .encode = bgp_encode_u32,
    .decode = bgp_decode_originator_id,
  },
  [BA_CLUSTER_LIST] = {
    .name = "cluster_list",
    .type = EAF_TYPE_INT_SET,
    .flags = BAF_OPTIONAL,
    .export = bgp_export_cluster_list,
    .encode = bgp_encode_u32s,
    .decode = bgp_decode_cluster_list,
    .format = bgp_format_cluster_list,
  },
  [BA_MP_REACH_NLRI] = {
    .name = "mp_reach_nlri",
    .type = EAF_TYPE_OPAQUE,
    .flags = BAF_OPTIONAL,
    .decode = bgp_decode_mp_reach_nlri,
  },
  [BA_MP_UNREACH_NLRI] = {
    .name = "mp_unreach_nlri",
    .type = EAF_TYPE_OPAQUE,
    .flags = BAF_OPTIONAL,
    .decode = bgp_decode_mp_unreach_nlri,
  },
  [BA_EXT_COMMUNITY] = {
    .name = "ext_community",
    .type = EAF_TYPE_EC_SET,
    .flags = BAF_OPTIONAL | BAF_TRANSITIVE,
    .export = bgp_export_ext_community,
    .encode = bgp_encode_u32s,
    .decode = bgp_decode_ext_community,
  },
  [BA_AS4_PATH] = {
    .name = "as4_path",
    .type = EAF_TYPE_AS_PATH,
    .flags = BAF_OPTIONAL | BAF_TRANSITIVE,
    .encode = bgp_encode_raw,
    .decode = bgp_decode_as4_path,
  },
  [BA_AS4_AGGREGATOR] = {
    .name = "as4_aggregator",
    .type = EAF_TYPE_OPAQUE,
    .flags = BAF_OPTIONAL | BAF_TRANSITIVE,
    .encode = bgp_encode_raw,
    .decode = bgp_decode_as4_aggregator,
    .format = bgp_format_aggregator,
  },
  [BA_LARGE_COMMUNITY] = {
    .name = "large_community",
    .type = EAF_TYPE_LC_SET,
    .flags = BAF_OPTIONAL | BAF_TRANSITIVE,
    .export = bgp_export_large_community,
    .encode = bgp_encode_u32s,
    .decode = bgp_decode_large_community,
  },
831 832 833 834 835 836 837 838
  [BA_MPLS_LABEL_STACK] = {
    .name = "mpls_label_stack",
    .type = EAF_TYPE_INT_SET,
    .export = bgp_export_mpls_label_stack,
    .encode = bgp_encode_mpls_label_stack,
    .decode = bgp_decode_mpls_label_stack,
    .format = bgp_format_mpls_label_stack,
  },
839 840
};

Ondřej Zajíček's avatar
Ondřej Zajíček committed
841 842 843 844 845 846 847 848 849
static inline int
bgp_attr_known(uint code)
{
  return (code < ARRAY_SIZE(bgp_attr_table)) && bgp_attr_table[code].name;
}


/*
 *	Attribute export
850 851
 */

Ondřej Zajíček's avatar
Ondřej Zajíček committed
852 853 854
static inline void
bgp_export_attr(struct bgp_export_state *s, eattr *a, ea_list *to)
{
855
  if (EA_PROTO(a->id) != PROTOCOL_BGP)
Ondřej Zajíček's avatar
Ondřej Zajíček committed
856 857 858 859 860 861 862 863 864 865 866 867 868 869 870
    return;

  uint code = EA_ID(a->id);

  if (bgp_attr_known(code))
  {
    const struct bgp_attr_desc *desc = &bgp_attr_table[code];

    /* The flags might have been zero if the attr was added by filters */
    a->flags = (a->flags & BAF_PARTIAL) | desc->flags;

    /* Set partial bit if new opt-trans attribute is attached to non-local route */
    if ((s->src != NULL) && (a->type & EAF_ORIGINATED) &&
	(a->flags & BAF_OPTIONAL) && (a->flags & BAF_TRANSITIVE))
      a->flags |= BAF_PARTIAL;
871

Ondřej Zajíček's avatar
Ondřej Zajíček committed
872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906
    /* Call specific hook */
    CALL(desc->export, s, a);

    /* Attribute might become undefined in hook */
    if ((a->type & EAF_TYPE_MASK) == EAF_TYPE_UNDEF)
      return;
  }
  else
  {
    /* Don't re-export unknown non-transitive attributes */
    if (!(a->flags & BAF_TRANSITIVE))
      return;

    a->flags |= BAF_PARTIAL;
  }

  /* Append updated attribute */
  to->attrs[to->count++] = *a;
}

/**
 * bgp_export_attrs - export BGP attributes
 * @s: BGP export state
 * @attrs: a list of extended attributes
 *
 * The bgp_export_attrs() function takes a list of attributes and merges it to
 * one newly allocated and sorted segment. Attributes are validated and
 * normalized by type-specific export hooks and attribute flags are updated.
 * Some attributes may be eliminated (e.g. unknown non-tranitive attributes, or
 * empty community sets).
 *
 * Result: one sorted attribute list segment, or NULL if attributes are unsuitable.
 */
static inline ea_list *
bgp_export_attrs(struct bgp_export_state *s, ea_list *attrs)
907
{
Ondřej Zajíček's avatar
Ondřej Zajíček committed
908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924
  /* Merge the attribute list */
  ea_list *new = lp_alloc(s->pool, ea_scan(attrs));
  ea_merge(attrs, new);
  ea_sort(new);

  uint i, count;
  count = new->count;
  new->count = 0;

  /* Export each attribute */
  for (i = 0; i < count; i++)
    bgp_export_attr(s, &new->attrs[i], new);

  if (s->err_withdraw)
    return NULL;

  return new;
925 926
}

Ondřej Zajíček's avatar
Ondřej Zajíček committed
927 928 929 930 931 932 933

/*
 *	Attribute encoding
 */

static inline int
bgp_encode_attr(struct bgp_write_state *s, eattr *a, byte *buf, uint size)
934
{
935
  ASSERT(EA_PROTO(a->id) == PROTOCOL_BGP);
Ondřej Zajíček's avatar
Ondřej Zajíček committed
936 937 938 939 940

  uint code = EA_ID(a->id);

  if (bgp_attr_known(code))
    return bgp_attr_table[code].encode(s, a, buf, size);
941
  else
Ondřej Zajíček's avatar
Ondřej Zajíček committed
942
    return bgp_encode_raw(s, a, buf, size);
943 944
}

Ondřej Zajíček's avatar
Ondřej Zajíček committed
945 946 947 948 949 950 951 952 953
/**
 * bgp_encode_attrs - encode BGP attributes
 * @s: BGP write state
 * @attrs: a list of extended attributes
 * @buf: buffer
 * @end: buffer end
 *
 * The bgp_encode_attrs() function takes a list of extended attributes
 * and converts it to its BGP representation (a part of an Update message).
Ondřej Zajíček's avatar
Ondřej Zajíček committed
954
 * BGP write state may be fake when called from MRT protocol.
Ondřej Zajíček's avatar
Ondřej Zajíček committed
955 956 957 958 959
 *
 * Result: Length of the attribute block generated or -1 if not enough space.
 */
int
bgp_encode_attrs(struct bgp_write_state *s, ea_list *attrs, byte *buf, byte *end)
960
{
Ondřej Zajíček's avatar
Ondřej Zajíček committed
961 962 963 964 965 966 967 968 969 970 971 972 973 974
  byte *pos = buf;
  int i, len;

  for (i = 0; i < attrs->count; i++)
  {
    len = bgp_encode_attr(s, &attrs->attrs[i], pos, end - pos);

    if (len < 0)
      return -1;

    pos += len;
  }

  return pos - buf;
975 976
}

Ondřej Zajíček's avatar
Ondřej Zajíček committed
977 978 979 980 981 982 983 984 985

/*
 *	Attribute decoding
 */

static void bgp_process_as4_attrs(ea_list **attrs, struct linpool *pool);

static inline int
bgp_as_path_loopy(struct bgp_proto *p, ea_list *attrs, u32 asn)
986
{
Ondřej Zajíček's avatar
Ondřej Zajíček committed
987 988 989
  eattr *e = bgp_find_attr(attrs, BA_AS_PATH);
  int num = p->cf->allow_local_as + 1;
  return (e && (num > 0) && as_path_contains(e->u.ptr, asn, num));
990 991
}

Ondřej Zajíček's avatar
Ondřej Zajíček committed
992 993
static inline int
bgp_originator_id_loopy(struct bgp_proto *p, ea_list *attrs)
994
{
Ondřej Zajíček's avatar
Ondřej Zajíček committed
995 996
  eattr *e = bgp_find_attr(attrs, BA_ORIGINATOR_ID);
  return (e && (e->u.data == p->local_id));
997 998
}

Ondřej Zajíček's avatar
Ondřej Zajíček committed
999 1000
static inline int
bgp_cluster_list_loopy(struct bgp_proto *p, ea_list *attrs)
1001
{
Ondřej Zajíček's avatar
Ondřej Zajíček committed
1002 1003 1004
  eattr *e = bgp_find_attr(attrs, BA_CLUSTER_LIST);
  return (e && int_set_contains(e->u.ptr, p->rr_cluster_id));
}
1005

Ondřej Zajíček's avatar
Ondřej Zajíček committed
1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017
static inline void
bgp_decode_attr(struct bgp_parse_state *s, uint code, uint flags, byte *data, uint len, ea_list **to)
{
  /* Handle duplicate attributes; RFC 7606 3 (g) */
  if (BIT32_TEST(s->attrs_seen, code))
  {
    if ((code == BA_MP_REACH_NLRI) || (code == BA_MP_UNREACH_NLRI))
      bgp_parse_error(s, 1);
    else
      DISCARD("Discarding duplicate attribute (code %u)", code);
  }
  BIT32_SET(s->attrs_seen, code);
1018

Ondřej Zajíček's avatar
Ondřej Zajíček committed
1019 1020 1021 1022 1023 1024 1025
  if (bgp_attr_known(code))
  {
    const struct bgp_attr_desc *desc = &bgp_attr_table[code];

    /* Handle conflicting flags; RFC 7606 3 (c) */
    if ((flags ^ desc->flags) & (BAF_OPTIONAL | BAF_TRANSITIVE))
      WITHDRAW("Malformed %s attribute - conflicting flags (%02x)", desc->name, flags);
1026

Ondřej Zajíček's avatar
Ondřej Zajíček committed
1027 1028 1029 1030 1031 1032 1033 1034 1035
    desc->decode(s, code, flags, data, len, to);
  }
  else /* Unknown attribute */
  {
    if (!(flags & BAF_OPTIONAL))
      WITHDRAW("Unknown attribute (code %u) - conflicting flags (%02x)", code, flags);

    bgp_decode_unknown(s, code, flags, data, len, to);
  }
1036 1037
}

Ondřej Zajíček's avatar
Ondřej Zajíček committed
1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049
/**
 * bgp_decode_attrs - check and decode BGP attributes
 * @s: BGP parse state
 * @data: start of attribute block
 * @len: length of attribute block
 *
 * This function takes a BGP attribute block (a part of an Update message), checks
 * its consistency and converts it to a list of BIRD route attributes represented
 * by an (uncached) &rta.
 */
ea_list *
bgp_decode_attrs(struct bgp_parse_state *s, byte *data, uint len)
1050
{
Ondřej Zajíček's avatar
Ondřej Zajíček committed
1051 1052 1053 1054
  struct bgp_proto *p = s->proto;
  ea_list *attrs = NULL;
  uint code, flags, alen;
  byte *pos = data;
1055

Ondřej Zajíček's avatar
Ondřej Zajíček committed
1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069
  /* Parse the attributes */
  while (len)
  {
    alen = 0;

    /* Read attribute type */
    if (len < 2)
      goto framing_error;
    flags = pos[0];
    code = pos[1];
    ADVANCE(pos, len, 2);

    /* Read attribute length */
    if (flags & BAF_EXT_LEN)
1070
    {
Ondřej Zajíček's avatar
Ondřej Zajíček committed
1071 1072 1073 1074
      if (len < 2)
	goto framing_error;
      alen = get_u16(pos);
      ADVANCE(pos, len, 2);
1075
    }
Ondřej Zajíček's avatar
Ondřej Zajíček committed
1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106
    else
    {
      if (len < 1)
	goto framing_error;
      alen = *pos;
      ADVANCE(pos, len, 1);
    }

    if (alen > len)
      goto framing_error;

    DBG("Attr %02x %02x %u\n", code, flags, alen);

    bgp_decode_attr(s, code, flags, pos, alen, &attrs);
    ADVANCE(pos, len, alen);
  }

  if (s->err_withdraw)
    goto withdraw;

  /* If there is no reachability NLRI, we are finished */
  if (!s->ip_reach_len && !s->mp_reach_len)
    return NULL;


  /* Handle missing mandatory attributes; RFC 7606 3 (d) */
  if (!BIT32_TEST(s->attrs_seen, BA_ORIGIN))
  { REPORT(NO_MANDATORY, "ORIGIN"); goto withdraw; }

  if (!BIT32_TEST(s->attrs_seen, BA_AS_PATH))
  { REPORT(NO_MANDATORY, "AS_PATH"); goto withdraw; }
1107

1108 1109 1110
  if (s->ip_reach_len && !BIT32_TEST(s->attrs_seen, BA_NEXT_HOP))
  { REPORT(NO_MANDATORY, "NEXT_HOP"); goto withdraw; }

Ondřej Zajíček's avatar
Ondřej Zajíček committed
1111 1112 1113 1114
  /* When receiving attributes from non-AS4-aware BGP speaker, we have to
     reconstruct AS_PATH and AGGREGATOR attributes; RFC 6793 4.2.3 */
  if (!p->as4_session)
    bgp_process_as4_attrs(&attrs, s->pool);
1115

Ondřej Zajíček's avatar
Ondřej Zajíček committed
1116 1117 1118
  /* Reject routes with our ASN in AS_PATH attribute */
  if (bgp_as_path_loopy(p, attrs, p->local_as))
    goto withdraw;
1119

1120
  /* Reject routes with our Confederation ID in AS_PATH attribute; RFC 5065 4.0 */
Ondřej Zajíček's avatar
Ondřej Zajíček committed
1121 1122
  if ((p->public_as != p->local_as) && bgp_as_path_loopy(p, attrs, p->public_as))
    goto withdraw;
1123

Ondřej Zajíček's avatar
Ondřej Zajíček committed
1124 1125 1126
  /* Reject routes with our Router ID in ORIGINATOR_ID attribute; RFC 4456 8 */
  if (p->is_internal && bgp_originator_id_loopy(p, attrs))
    goto withdraw;
1127

Ondřej Zajíček's avatar
Ondřej Zajíček committed
1128 1129 1130
  /* Reject routes with our Cluster ID in CLUSTER_LIST attribute; RFC 4456 8 */
  if (p->rr_client && bgp_cluster_list_loopy(p, attrs))
    goto withdraw;
1131

Ondřej Zajíček's avatar
Ondřej Zajíček committed
1132 1133 1134
  /* If there is no local preference, define one */
  if (!BIT32_TEST(s->attrs_seen, BA_LOCAL_PREF))
    bgp_set_attr_u32(&attrs, s->pool, BA_LOCAL_PREF, 0, p->cf->default_local_pref);
1135

Ondřej Zajíček's avatar
Ondřej Zajíček committed
1136
  return attrs;
1137

1138

Ondřej Zajíček's avatar
Ondřej Zajíček committed
1139 1140 1141 1142
framing_error:
  /* RFC 7606 4 - handle attribute framing errors */
  REPORT("Malformed attribute list - framing error (%u/%u) at %d",
	 alen, len, (int) (pos - s->attrs));
1143

Ondřej Zajíček's avatar
Ondřej Zajíček committed
1144 1145 1146 1147
withdraw:
  /* RFC 7606 5.2 - handle missing NLRI during errors */
  if (!s->ip_reach_len && !s->mp_reach_len)
    bgp_parse_error(s, 1);
Martin Mareš's avatar
Martin Mareš committed
1148

Ondřej Zajíček's avatar
Ondřej Zajíček committed
1149 1150
  s->err_withdraw = 1;
  return NULL;
1151 1152
}

1153

Ondřej Zajíček's avatar
Ondřej Zajíček committed
1154 1155 1156
/*
 *	Route bucket hash table
 */
1157

Ondřej Zajíček's avatar
Ondřej Zajíček committed
1158 1159 1160 1161
#define RBH_KEY(b)		b->eattrs, b->hash
#define RBH_NEXT(b)		b->next
#define RBH_EQ(a1,h1,a2,h2)	h1 == h2 && ea_same(a1, a2)
#define RBH_FN(a,h)		h
1162

Ondřej Zajíček's avatar
Ondřej Zajíček committed
1163 1164
#define RBH_REHASH		bgp_rbh_rehash
#define RBH_PARAMS		/8, *2, 2, 2, 8, 20
1165 1166


Ondřej Zajíček's avatar
Ondřej Zajíček committed
1167
HASH_DEFINE_REHASH_FN(RBH, struct bgp_bucket)
1168

Ondřej Zajíček's avatar
Ondřej Zajíček committed
1169 1170
void
bgp_init_bucket_table(struct bgp_channel *c)
1171
{
Ondřej Zajíček's avatar
Ondřej Zajíček committed
1172
  HASH_INIT(c->bucket_hash, c->pool, 8);
1173

Ondřej Zajíček's avatar
Ondřej Zajíček committed
1174 1175
  init_list(&c->bucket_queue);
  c->withdraw_bucket = NULL;
1176 1177
}

Ondřej Zajíček's avatar
Ondřej Zajíček committed
1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193
void
bgp_free_bucket_table(struct bgp_channel *c)
{
  HASH_FREE(c->bucket_hash);

  struct bgp_bucket *b;
  WALK_LIST_FIRST(b, c->bucket_queue)
  {
    rem_node(&b->send_node);
    mb_free(b);
  }

  mb_free(c->withdraw_bucket);
  c->withdraw_bucket = NULL;
}

1194
static struct bgp_bucket *
Ondřej Zajíček's avatar
Ondřej Zajíček committed
1195
bgp_get_bucket(struct bgp_channel *c, ea_list *new)
1196
{
Ondřej Zajíček's avatar
Ondřej Zajíček committed
1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207
  /* Hash and lookup */
  u32 hash = ea_hash(new);
  struct bgp_bucket *b = HASH_FIND(c->bucket_hash, RBH, new, hash);

  if (b)
    return b;

  uint ea_size = sizeof(ea_list) + new->count * sizeof(eattr);
  uint ea_size_aligned = BIRD_ALIGN(ea_size, CPU_STRUCT_ALIGN);
  uint size = sizeof(struct bgp_bucket) + ea_size_aligned;
  uint i;
1208 1209 1210
  byte *dest;

  /* Gather total size of non-inline attributes */
Ondřej Zajíček's avatar
Ondřej Zajíček committed
1211 1212 1213
  for (i = 0; i < new->count; i++)
  {
    eattr *a = &new->attrs[i];
1214

Ondřej Zajíček's avatar
Ondřej Zajíček committed
1215 1216 1217 1218 1219 1220
    if (!(a->type & EAF_EMBEDDED))
      size += BIRD_ALIGN(sizeof(struct adata) + a->u.ptr->length, CPU_STRUCT_ALIGN);
  }

  /* Create the bucket */
  b = mb_alloc(c->pool, size);
1221
  init_list(&b->prefixes);
Ondřej Zajíček's avatar
Ondřej Zajíček committed
1222 1223 1224
  b->hash = hash;

  /* Copy list of extended attributes */
1225
  memcpy(b->eattrs, new, ea_size);
Ondřej Zajíček's avatar
Ondřej Zajíček committed
1226
  dest = ((byte *) b->eattrs) + ea_size_aligned;
1227 1228

  /* Copy values of non-inline attributes */
Ondřej Zajíček's avatar
Ondřej Zajíček committed
1229 1230 1231 1232 1233
  for (i = 0; i < new->count; i++)
  {
    eattr *a = &b->eattrs->attrs[i];

    if (!(a->type & EAF_EMBEDDED))
1234
    {
Ondřej Zajíček's avatar
Ondřej Zajíček committed
1235 1236 1237 1238 1239
      struct adata *oa = a->u.ptr;
      struct adata *na = (struct adata *) dest;
      memcpy(na, oa, sizeof(struct adata) + oa->length);
      a->u.ptr = na;
      dest += BIRD_ALIGN(sizeof(struct adata) + na->length, CPU_STRUCT_ALIGN);
1240
    }
Ondřej Zajíček's avatar
Ondřej Zajíček committed
1241
  }
1242

Ondřej Zajíček's avatar
Ondřej Zajíček committed
1243 1244 1245
  /* Insert the bucket to send queue and bucket hash */
  add_tail(&c->bucket_queue, &b->send_node);
  HASH_INSERT2(c->bucket_hash, RBH, c->pool, b);
1246 1247 1248 1249 1250

  return b;
}

static struct bgp_bucket *
Ondřej Zajíček's avatar
Ondřej Zajíček committed
1251
bgp_get_withdraw_bucket(struct bgp_channel *c)
1252
{
Ondřej Zajíček's avatar
Ondřej Zajíček committed
1253 1254 1255 1256 1257
  if (!c->withdraw_bucket)
  {
    c->withdraw_bucket = mb_allocz(c->pool, sizeof(struct bgp_bucket));
    init_list(&c->withdraw_bucket->prefixes);
  }
1258

Ondřej Zajíček's avatar
Ondřej Zajíček committed
1259 1260
  return c->withdraw_bucket;
}
1261

Ondřej Zajíček's avatar
Ondřej Zajíček committed
1262 1263 1264 1265 1266 1267 1268
void
bgp_free_bucket(struct bgp_channel *c, struct bgp_bucket *b)
{
  rem_node(&b->send_node);
  HASH_REMOVE2(c->bucket_hash, RBH, c->pool, b);
  mb_free(b);
}
1269

Ondřej Zajíček's avatar
Ondřej Zajíček committed
1270 1271 1272 1273 1274
void
bgp_defer_bucket(struct bgp_channel *c, struct bgp_bucket *b)
{
  rem_node(&b->send_node);
  add_tail(&c->bucket_queue, &b->send_node);
1275 1276
}

1277
void
Ondřej Zajíček's avatar
Ondřej Zajíček committed
1278
bgp_withdraw_bucket(struct bgp_channel *c, struct bgp_bucket *b)
1279
{
Ondřej Zajíček's avatar
Ondřej Zajíček committed
1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291
  struct bgp_proto *p = (void *) c->c.proto;
  struct bgp_bucket *wb = bgp_get_withdraw_bucket(c);

  log(L_ERR "%s: Attribute list too long", p->p.name);
  while (!EMPTY_LIST(b->prefixes))
  {
    struct bgp_prefix *px = HEAD(b->prefixes);

    log(L_ERR "%s: - withdrawing %N", p->p.name, &px->net);
    rem_node(&px->buck_node);
    add_tail(&wb->prefixes, &px->buck_node);
  }
1292 1293
}

1294

Ondřej Zajíček's avatar
Ondřej Zajíček committed
1295 1296 1297
/*
 *	Prefix hash table
 */
1298

Ondřej Zajíček's avatar
Ondřej Zajíček committed
1299 1300 1301 1302
#define PXH_KEY(px)		px->net, px->path_id, px->hash
#define PXH_NEXT(px)		px->next
#define PXH_EQ(n1,i1,h1,n2,i2,h2) h1 == h2 && i1 == i2 && net_equal(n1, n2)
#define PXH_FN(n,i,h)		h
Ondřej Zajíček's avatar
Ondřej Zajíček committed
1303 1304 1305 1306

#define PXH_REHASH		bgp_pxh_rehash
#define PXH_PARAMS		/8, *2, 2, 2, 8, 20

1307

Ondřej Zajíček's avatar
Ondřej Zajíček committed
1308
HASH_DEFINE_REHASH_FN(PXH, struct bgp_prefix)
1309 1310

void
Ondřej Zajíček's avatar
Ondřej Zajíček committed
1311
bgp_init_prefix_table(struct bgp_channel *c)
1312
{
Ondřej Zajíček's avatar
Ondřej Zajíček committed
1313
  HASH_INIT(c->prefix_hash, c->pool, 8);
1314

1315 1316
  uint alen = net_addr_length[c->c.net_type];
  c->prefix_slab = alen ? sl_new(c->pool, sizeof(struct bgp_prefix) + alen) : NULL;
1317 1318
}

1319
void
1320
bgp_free_prefix_table(struct bgp_channel *c)
1321
{
1322
  HASH_FREE(c->prefix_hash);
1323

1324 1325
  rfree(c->prefix_slab);
  c->prefix_slab = NULL;
1326 1327 1328
}

static struct bgp_prefix *
Ondřej Zajíček's avatar
Ondřej Zajíček committed
1329
bgp_get_prefix(struct bgp_channel *c, net_addr *net, u32 path_id)
1330
{
Ondřej Zajíček's avatar
Ondřej Zajíček committed
1331 1332
  u32 hash = net_hash(net) ^ u32_hash(path_id);
  struct bgp_prefix *px = HASH_FIND(c->prefix_hash, PXH, net, path_id, hash);
1333

Ondřej Zajíček's avatar
Ondřej Zajíček committed
1334 1335 1336 1337 1338
  if (px)
  {
    rem_node(&px->buck_node);
    return px;
  }
1339

1340 1341 1342 1343 1344
  if (c->prefix_slab)
    px = sl_alloc(c->prefix_slab);
  else
    px = mb_alloc(c->pool, sizeof(struct bgp_prefix) + net->length);

Ondřej Zajíček's avatar
Ondřej Zajíček committed
1345 1346 1347 1348 1349
  px->buck_node.next = NULL;
  px->buck_node.prev = NULL;
  px->hash = hash;
  px->path_id = path_id;
  net_copy(px->net, net);
1350

Ondřej Zajíček's avatar
Ondřej Zajíček committed
1351
  HASH_INSERT2(c->prefix_hash, PXH, c->pool, px);
1352

Ondřej Zajíček's avatar
Ondřej Zajíček committed
1353
  return px;
1354 1355 1356
}

void
Ondřej Zajíček's avatar
Ondřej Zajíček committed
1357
bgp_free_prefix(struct bgp_channel *c, struct bgp_prefix *px)
1358
{
Ondřej Zajíček's avatar
Ondřej Zajíček committed
1359 1360
  rem_node(&px->buck_node);
  HASH_REMOVE2(c->prefix_hash, PXH, c->pool, px);
1361 1362 1363 1364 1365

  if (c->prefix_slab)
    sl_free(c->prefix_slab, px);
  else
    mb_free(px);
1366 1367 1368
}


Ondřej Zajíček's avatar
Ondřej Zajíček committed
1369 1370 1371
/*
 *	BGP protocol glue
 */
Martin Mareš's avatar
Martin Mareš committed
1372

Ondřej Zajíček's avatar
Ondřej Zajíček committed
1373
int
1374
bgp_preexport(struct proto *P, rte **new, struct linpool *pool UNUSED)
Martin Mareš's avatar
Martin Mareš committed
1375
{
Ondřej Zajíček's avatar
Ondřej Zajíček committed
1376 1377 1378 1379
  rte *e = *new;
  struct proto *SRC = e->attrs->src->proto;
  struct bgp_proto *p = (struct bgp_proto *) P;
  struct bgp_proto *src = (SRC->proto == &proto_bgp) ? (struct bgp_proto *) SRC : NULL;
Martin Mareš's avatar
Martin Mareš committed
1380

Ondřej Zajíček's avatar
Ondřej Zajíček committed
1381 1382 1383
  /* Reject our routes */
  if (src == p)
    return -1;
1384

Ondřej Zajíček's avatar
Ondřej Zajíček committed
1385 1386 1387
  /* Accept non-BGP routes */
  if (src == NULL)
    return 0;
1388

Ondřej Zajíček's avatar
Ondřej Zajíček committed
1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404
  /* IBGP route reflection, RFC 4456 */
  if (p->is_internal && src->is_internal && (p->local_as == src->local_as))
  {
    /* Rejected unless configured as route reflector */
    if (!p->rr_client && !src->rr_client)
      return -1;

    /* Generally, this should be handled when path is received, but we check it
       also here as rr_cluster_id may be undefined or different in src. */
    if (p->rr_cluster_id && bgp_cluster_list_loopy(p, e->attrs->eattrs))
      return -1;
  }

  /* Handle well-known communities, RFC 1997 */
  struct eattr *c;
  if (p->cf->interpret_communities &&
1405
      (c = ea_find(e->attrs->eattrs, EA_CODE(PROTOCOL_BGP, BA_COMMUNITY))))
Ondřej Zajíček's avatar
Ondřej Zajíček committed
1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419
  {
    struct adata *d = c->u.ptr;

    /* Do not export anywhere */
    if (int_set_contains(d, BGP_COMM_NO_ADVERTISE))
      return -1;

    /* Do not export outside of AS (or member-AS) */
    if (!p->is_internal && int_set_contains(d, BGP_COMM_NO_EXPORT_SUBCONFED))
      return -1;

    /* Do not export outside of AS (or confederation) */
    if (!p->is_interior && int_set_contains(d, BGP_COMM_NO_EXPORT))
      return -1;
1420 1421 1422 1423

    /* Do not export LLGR_STALE routes to LLGR-ignorant peers */
    if (!p->conn->remote_caps->llgr_aware && int_set_contains(d, BGP_COMM_LLGR_STALE))
      return -1;
Ondřej Zajíček's avatar
Ondřej Zajíček committed
1424
  }
1425

Ondřej Zajíček's avatar
Ondřej Zajíček committed
1426
  return 0;
1427 1428 1429
}


1430
static adata null_adata;	/* adata of length 0 */
Martin Mareš's avatar
Martin Mareš committed
1431

Ondřej Zajíček's avatar
Ondřej Zajíček committed
1432
static ea_list *
Ondřej Zajíček's avatar
Ondřej Zajíček committed
1433
bgp_update_attrs(struct bgp_proto *p, struct bgp_channel *c, rte *e, ea_list *attrs0, struct linpool *pool)
Martin Mareš's avatar
Martin Mareš committed
1434
{
Ondřej Zajíček's avatar
Ondřej Zajíček committed
1435 1436
  struct proto *SRC = e->attrs->src->proto;
  struct bgp_proto *src = (SRC->proto == &proto_bgp) ? (void *) SRC : NULL;
1437
  struct bgp_export_state s = { .proto = p, .channel = c, .pool = pool, .src = src, .route = e, .mpls = c->desc->mpls };
Ondřej Zajíček's avatar
Ondřej Zajíček committed
1438
  ea_list *attrs = attrs0;
1439
  eattr *a;
Ondřej Zajíček's avatar
Ondřej Zajíček committed
1440
  adata *ad;
1441

Ondřej Zajíček's avatar
Ondřej Zajíček committed
1442
  /* ORIGIN attribute - mandatory, attach if missing */
Ondřej Zajíček's avatar
Ondřej Zajíček committed
1443
  if (! bgp_find_attr(attrs0, BA_ORIGIN))
Ondřej Zajíček's avatar
Ondřej Zajíček committed
1444 1445
    bgp_set_attr_u32(&attrs, pool, BA_ORIGIN, 0, src ? ORIGIN_INCOMPLETE : ORIGIN_IGP);

Ondřej Zajíček's avatar
Ondřej Zajíček committed
1446 1447 1448
  /* AS_PATH attribute - mandatory */
  a = bgp_find_attr(attrs0, BA_AS_PATH);
  ad = a ? a->u.ptr : &null_adata;
1449 1450 1451 1452 1453

  /* AS_PATH attribute - strip AS_CONFED* segments outside confederation */
  if ((!p->cf->confederation || !p->is_interior) && as_path_contains_confed(ad))
    ad = as_path_strip_confed(pool, ad);

Ondřej Zajíček's avatar
Ondřej Zajíček committed
1454
  /* AS_PATH attribute - keep or prepend ASN */
1455
  if (p->is_internal || p->rs_client)
Ondřej Zajíček's avatar
Ondřej Zajíček committed
1456 1457
  {
    /* IBGP or route server -> just ensure there is one */
1458 1459
    if (!a)
      bgp_set_attr_ptr(&attrs, pool, BA_AS_PATH, 0, &null_adata);
Ondřej Zajíček's avatar
Ondřej Zajíček committed
1460 1461 1462
  }
  else if (p->is_interior)
  {
1463 1464 1465
    /* Confederation -> prepend ASN as AS_CONFED_SEQUENCE */
    ad = as_path_prepend2(pool, ad, AS_PATH_CONFED_SEQUENCE, p->public_as);
    bgp_set_attr_ptr(&attrs, pool, BA_AS_PATH, 0, ad);
Ondřej Zajíček's avatar
Ondřej Zajíček committed
1466 1467 1468
  }
  else /* Regular EBGP (no RS, no confederation) */
  {
1469 1470 1471
    /* Regular EBGP -> prepend ASN as regular sequence */
    ad = as_path_prepend2(pool, ad, AS_PATH_SEQUENCE, p->public_as);
    bgp_set_attr_ptr(&attrs, pool, BA_AS_PATH, 0, ad);
Ondřej Zajíček's avatar
Ondřej Zajíček committed
1472 1473

    /* MULTI_EXIT_DESC attribute - accept only if set in export filter */
Ondřej Zajíček's avatar
Ondřej Zajíček committed
1474
    a = bgp_find_attr(attrs0, BA_MULTI_EXIT_DISC);
Ondřej Zajíček's avatar
Ondřej Zajíček committed
1475 1476 1477 1478 1479
    if (a && !(a->type & EAF_FRESH))
      bgp_unset_attr(&attrs, pool, BA_MULTI_EXIT_DISC);
  }

  /* NEXT_HOP attribute - delegated to AF-specific hook */
Ondřej Zajíček's avatar
Ondřej Zajíček committed
1480
  a = bgp_find_attr(attrs0, BA_NEXT_HOP);
Ondřej Zajíček's avatar
Ondřej Zajíček committed
1481 1482 1483
  bgp_update_next_hop(&s, a, &attrs);

  /* LOCAL_PREF attribute - required for IBGP, attach if missing */
Ondřej Zajíček's avatar
Ondřej Zajíček committed
1484
  if (p->is_interior && ! bgp_find_attr(attrs0, BA_LOCAL_PREF))
Ondřej Zajíček's avatar
Ondřej Zajíček committed
1485 1486 1487 1488 1489 1490
    bgp_set_attr_u32(&attrs, pool, BA_LOCAL_PREF, 0, p->cf->default_local_pref);

  /* IBGP route reflection, RFC 4456 */
  if (src && src->is_internal && p->is_internal && (src->local_as == p->local_as))
  {
    /* ORIGINATOR_ID attribute - attach if not already set */
Ondřej Zajíček's avatar
Ondřej Zajíček committed
1491
    if (! bgp_find_attr(attrs0, BA_ORIGINATOR_ID))
Ondřej Zajíček's avatar
Ondřej Zajíček committed
1492 1493 1494
      bgp_set_attr_u32(&attrs, pool, BA_ORIGINATOR_ID, 0, src->remote_id);

    /* CLUSTER_LIST attribute - prepend cluster ID */
Ondřej Zajíček's avatar
Ondřej Zajíček committed
1495 1496 1497 1498
    a = bgp_find_attr(attrs0, BA_CLUSTER_LIST);
    ad = a ? a->u.ptr : NULL;

    /* Prepend src cluster ID */
Ondřej Zajíček's avatar
Ondřej Zajíček committed
1499
    if (src->rr_cluster_id)
1500
      ad = int_set_prepend(pool, ad, src->rr_cluster_id);
Ondřej Zajíček's avatar
Ondřej Zajíček committed
1501

Ondřej Zajíček's avatar
Ondřej Zajíček committed
1502
    /* Prepend dst cluster ID if src and dst clusters are different */
Ondřej Zajíček's avatar
Ondřej Zajíček committed
1503
    if (p->rr_cluster_id && (src->rr_cluster_id != p->rr_cluster_id))
1504
      ad = int_set_prepend(pool, ad, p->rr_cluster_id);
Ondřej Zajíček's avatar
Ondřej Zajíček committed
1505 1506 1507

    /* Should be at least one prepended cluster ID */
    bgp_set_attr_ptr(&attrs, pool, BA_CLUSTER_LIST, 0, ad);
Ondřej Zajíček's avatar
Ondřej Zajíček committed
1508 1509 1510 1511 1512 1513 1514
  }

  /* AS4_* transition attributes, RFC 6793 4.2.2 */
  if (! p->as4_session)
  {
    a = bgp_find_attr(attrs, BA_AS_PATH);
    if (a && as_path_contains_as4(a->u.ptr))
1515
    {
Ondřej Zajíček's avatar
Ondřej Zajíček committed
1516 1517
      bgp_set_attr_ptr(&attrs, pool, BA_AS_PATH, 0, as_path_to_old(pool, a->u.ptr));
      bgp_set_attr_ptr(&attrs, pool, BA_AS4_PATH, 0, as_path_strip_confed(pool, a->u.ptr));
1518 1519
    }

Ondřej Zajíček's avatar
Ondřej Zajíček committed
1520 1521
    a = bgp_find_attr(attrs, BA_AGGREGATOR);
    if (a && aggregator_contains_as4(a->u.ptr))
1522
    {
Ondřej Zajíček's avatar
Ondřej Zajíček committed
1523 1524
      bgp_set_attr_ptr(&attrs, pool, BA_AGGREGATOR, 0, aggregator_to_old(pool, a->u.ptr));
      bgp_set_attr_ptr(&attrs, pool, BA_AS4_AGGREGATOR, 0, a->u.ptr);
1525
    }
Ondřej Zajíček's avatar
Ondřej Zajíček committed
1526
  }
Martin Mareš's avatar
Martin Mareš committed
1527

Ondřej Zajíček's avatar
Ondřej Zajíček committed
1528 1529 1530 1531 1532 1533
  /*
   * Presence of mandatory attributes ORIGIN and AS_PATH is ensured by above
   * conditions. Presence and validity of quasi-mandatory NEXT_HOP attribute
   * should be checked in AF-specific hooks.
   */

Ondřej Zajíček's avatar
Ondřej Zajíček committed
1534 1535
  /* Apply per-attribute export hooks for validatation and normalization */
  return bgp_export_attrs(&s, attrs);
Martin Mareš's avatar
Martin Mareš committed
1536 1537
}

Ondřej Zajíček's avatar
Ondřej Zajíček committed
1538
void
1539
bgp_rt_notify(struct proto *P, struct channel *C, net *n, rte *new, rte *old)
1540
{
Ondřej Zajíček's avatar
Ondřej Zajíček committed
1541 1542 1543 1544 1545
  struct bgp_proto *p = (void *) P;
  struct bgp_channel *c = (void *) C;
  struct bgp_bucket *buck;
  struct bgp_prefix *px;
  u32 path;
1546

Ondřej Zajíček's avatar
Ondřej Zajíček committed
1547 1548
  if (new)
  {
1549
    struct ea_list *attrs = bgp_update_attrs(p, c, new, new->attrs->eattrs, bgp_linpool2);
1550

Ondřej Zajíček's avatar
Ondřej Zajíček committed
1551 1552 1553
    /* If attributes are invalid, we fail back to withdraw */
    buck = attrs ? bgp_get_bucket(c, attrs) : bgp_get_withdraw_bucket(c);
    path = new->attrs->src->global_id;
Martin Mareš's avatar
Martin Mareš committed
1554

1555
    lp_flush(bgp_linpool2);
Ondřej Zajíček's avatar
Ondřej Zajíček committed
1556
  }
Martin Mareš's avatar
Martin Mareš committed
1557
  else
Ondřej Zajíček's avatar
Ondřej Zajíček committed
1558 1559 1560 1561 1562 1563 1564 1565 1566
  {
    buck = bgp_get_withdraw_bucket(c);
    path = old->attrs->src->global_id;
  }

  px = bgp_get_prefix(c, n->n.addr, c->add_path_tx ? path : 0);
  add_tail(&buck->prefixes, &px->buck_node);

  bgp_schedule_packet(p->conn, c, PKT_UPDATE);
Martin Mareš's avatar
Martin Mareš committed
1567 1568
}

Ondřej Zajíček's avatar
Ondřej Zajíček committed
1569

1570 1571 1572
static inline u32
bgp_get_neighbor(rte *r)
{
1573
  eattr *e = ea_find(r->attrs->eattrs, EA_CODE(PROTOCOL_BGP, BA_AS_PATH));
1574 1575
  u32 as;

1576
  if (e && as_path_get_first_regular(e->u.ptr, &as))
1577
    return as;
1578 1579 1580 1581

  /* If AS_PATH is not defined, we treat rte as locally originated */
  struct bgp_proto *p = (void *) r->attrs->src->proto;
  return p->cf->confederation ?: p->local_as;
1582 1583
}

1584 1585 1586
static inline int
rte_resolvable(rte *rt)
{
Ondřej Zajíček's avatar
Ondřej Zajíček committed
1587
  return rt->attrs->dest == RTD_UNICAST;
1588 1589
}

1590 1591 1592 1593 1594 1595 1596 1597 1598 1599 1600 1601 1602
static inline int
rte_stale(rte *r)
{
  if (r->u.bgp.stale < 0)
  {
    /* If staleness is unknown, compute and cache it */
    eattr *a = ea_find(r->attrs->eattrs, EA_CODE(PROTOCOL_BGP, BA_COMMUNITY));
    r->u.bgp.stale = a && int_set_contains(a->u.ptr, BGP_COMM_LLGR_STALE);
  }

  return r->u.bgp.stale;
}

Martin Mareš's avatar
Martin Mareš committed
1603 1604 1605
int
bgp_rte_better(rte *new, rte *old)
{
1606 1607
  struct bgp_proto *new_bgp = (struct bgp_proto *) new->attrs->src->proto;
  struct bgp_proto *old_bgp = (struct bgp_proto *) old->attrs->src->proto;
1608 1609
  eattr *x, *y;
  u32 n, o;
Martin Mareš's avatar
Martin Mareš committed
1610

1611 1612 1613 1614 1615 1616 1617 1618
  /* Skip suppressed routes (see bgp_rte_recalculate()) */
  n = new->u.bgp.suppressed;
  o = old->u.bgp.suppressed;
  if (n > o)
    return 0;
  if (n < o)
    return 1;

1619
  /* RFC 4271 9.1.2.1. Route resolvability test */
1620 1621
  n = rte_resolvable(new);
  o = rte_resolvable(old);
1622 1623 1624 1625 1626
  if (n > o)
    return 1;
  if (n < o)
    return 0;

1627 1628 1629 1630 1631 1632 1633 1634 1635
  /* LLGR draft - depreference stale routes */
  n = rte_stale(new);
  o = rte_stale(old);
  if (n > o)
    return 0;
  if (n < o)
    return 1;

 /* Start with local preferences */
1636 1637
  x = ea_find(new->attrs->eattrs, EA_CODE(PROTOCOL_BGP, BA_LOCAL_PREF));
  y = ea_find(old->attrs->eattrs, EA_CODE(PROTOCOL_BGP, BA_LOCAL_PREF));
1638 1639 1640 1641 1642 1643 1644
  n = x ? x->u.data : new_bgp->cf->default_local_pref;
  o = y ? y->u.data : old_bgp->cf->default_local_pref;
  if (n > o)
    return 1;
  if (n < o)
    return 0;

1645
  /* RFC 4271 9.1.2.2. a)  Use AS path lengths */
1646
  if (new_bgp->cf->compare_path_lengths || old_bgp->cf->compare_path_lengths)
Ondřej Zajíček's avatar
Ondřej Zajíček committed
1647
  {