1244 lines
32 KiB
C
1244 lines
32 KiB
C
// SPDX-License-Identifier: LGPL-3.0-or-later
|
|
|
|
/**
|
|
* \file bgp/attribute.c
|
|
*
|
|
* Deals with BGP Path Attributes.
|
|
*
|
|
* \copyright The DoubleFourteen Code Forge (C) All Rights Reserved
|
|
* \author Lorenzo Cogotti
|
|
*/
|
|
|
|
#include "bgp/bgp_local.h"
|
|
#include "bgp/mrt.h"
|
|
#include "sys/endian.h"
|
|
#include "sys/interlocked.h"
|
|
|
|
#include <assert.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#if 0
|
|
|
|
#include <assert.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
|
|
static int AttrCodeComp(const void *lhs, const void *rhs)
|
|
{
|
|
int a = *(BgpAttrCode *) lhs;
|
|
int b = *(BgpAttrCode *) rhs;
|
|
|
|
return a - b;
|
|
}
|
|
|
|
int main(void)
|
|
{
|
|
BgpAttrCode attributes[] = {
|
|
BGP_ATTR_ORIGIN,
|
|
BGP_ATTR_NEXT_HOP,
|
|
BGP_ATTR_ATOMIC_AGGREGATE,
|
|
BGP_ATTR_AGGREGATOR,
|
|
BGP_ATTR_AS4_AGGREGATOR,
|
|
BGP_ATTR_AS_PATH,
|
|
BGP_ATTR_AS4_PATH,
|
|
BGP_ATTR_MP_REACH_NLRI,
|
|
BGP_ATTR_MP_UNREACH_NLRI,
|
|
BGP_ATTR_COMMUNITY,
|
|
BGP_ATTR_EXTENDED_COMMUNITY,
|
|
BGP_ATTR_LARGE_COMMUNITY
|
|
};
|
|
|
|
assert(BGP_ATTRTAB_LEN == ARRAY_SIZE(attributes));
|
|
assert(ARRAY_SIZE(attributes) < 0x80); // we store indexes inside Sint8
|
|
|
|
qsort(attributes, ARRAY_SIZE(attributes), sizeof(*attributes), AttrCodeComp);
|
|
|
|
// Make attribute to lookup index table, assign an
|
|
// incremental index to each notable attribute in ascending order.
|
|
printf("const Sint8 bgp_attrTabIdx[256] = {\n");
|
|
|
|
const int LINE_ELS = 16;
|
|
const int LINE_COUNT = 256 / LINE_ELS;
|
|
assert(256 % LINE_ELS == 0);
|
|
|
|
const int LAST_LINE = LINE_COUNT - 1;
|
|
|
|
int nextIdx = 0;
|
|
BgpAttrCode code = 0;
|
|
for (int i = 0; i < LINE_COUNT; i++) {
|
|
for (int j = 0; j < LINE_ELS; j++) {
|
|
if (j == 0)
|
|
printf("\t");
|
|
else
|
|
printf(", ");
|
|
|
|
if (bsearch(&code, attributes, ARRAY_SIZE(attributes), sizeof(*attributes), AttrCodeComp))
|
|
printf("%2d", nextIdx++);
|
|
else
|
|
printf("%d", -1);
|
|
|
|
code++;
|
|
}
|
|
|
|
if (i != LAST_LINE)
|
|
printf(",");
|
|
|
|
printf("\n");
|
|
}
|
|
|
|
printf("};\n");
|
|
return 0;
|
|
}
|
|
|
|
#endif
|
|
|
|
const Sint8 bgp_attrTabIdx[256] = {
|
|
-1, 0, 1, 2, -1, -1, 3, 4, 5, -1, -1, -1, -1, -1, 6, 7,
|
|
8, 9, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
|
11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
|
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
|
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
|
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
|
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
|
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
|
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
|
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
|
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
|
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
|
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
|
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
|
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
|
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1
|
|
};
|
|
|
|
#define BGP_MP_REACH_MINLEN (2 + 1 + 1 + 1)
|
|
#define BGP_MP_UNREACH_MINLEN (2 + 1)
|
|
|
|
#define BGP_ISATTRSET(mask, code) \
|
|
(((mask)[((Uint8) (code)) >> 6] & (1uLL << (((Uint8) (code)) & 0x3f))) != 0)
|
|
#define BGP_SETATTR(mask, code) \
|
|
((void) ((mask)[((Uint8) (code)) >> 6] |= (1uLL << (((Uint8) (code)) & 0x3f))))
|
|
|
|
static Boolean Bgp_IsRibv2RfcCompliant(const Uint8 *data,
|
|
size_t len,
|
|
Afi afi,
|
|
Safi safi)
|
|
{
|
|
if (len > 2 + 1 + 1) { // AFI-SAFI and NEXT HOP length
|
|
// Could be non-compliant - we determine that the attribute
|
|
// is NOT compliant if:
|
|
//
|
|
// AFI and SAFI are coherent with `afi` and `safi` and
|
|
// the next byte that follows AFI-SAFI can be interpreted as a
|
|
// NEXT HOP length without overflowing the attribute length
|
|
const Bgpmpfam *fam = (const Bgpmpfam *) data;
|
|
if (fam->afi == afi &&
|
|
fam->safi == safi &&
|
|
2 + 1 + (size_t) data[3] <= len)
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static Boolean Bgp_EnsureMsgBuffer(Bgpmsg *msg, size_t *bufsiz, size_t nbytes)
|
|
{
|
|
if (*bufsiz < nbytes) {
|
|
if (!BGP_ISEXMSG(msg->flags) || nbytes > BGP_EXMSGSIZ) {
|
|
Bgp_SetErrStat(BGPEOVRSIZ);
|
|
return FALSE;
|
|
}
|
|
|
|
const MemOps *ops = BGP_MEMOPS(msg);
|
|
void *newBuf = ops->Alloc(msg->allocp, BGP_EXMSGSIZ, msg->buf);
|
|
if (!newBuf) {
|
|
Bgp_SetErrStat(BGPENOMEM);
|
|
return FALSE;
|
|
}
|
|
|
|
msg->buf = (Uint8 *) newBuf;
|
|
*bufsiz = BGP_EXMSGSIZ;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
Judgement Bgp_RebuildMsgFromRib(const Prefix *nlri,
|
|
const Bgpattrseg *tpa,
|
|
Bgpmsg *msg,
|
|
unsigned flags)
|
|
{
|
|
/* NOTE: TABLE_DUMP_V2 imposes every AS to be 32 bits wide,
|
|
* regardless of the ASN32BIT flag, so any attempt to consistently
|
|
* rebuild a message that originally contained 16 bits ASes is
|
|
* futile.
|
|
* Legacy TABLE_DUMP, on the other hand, mandates 16 bits AS numbers,
|
|
* it is incapable of storing AS_PATH with ASN32BIT.
|
|
*
|
|
* It looks like they couldn't find a way to have both.
|
|
*/
|
|
if (flags & BGPF_RIBV2) {
|
|
if (nlri->isAddPath)
|
|
flags |= BGPF_ADDPATH;
|
|
|
|
flags |= BGPF_ASN32BIT; // always the case for TABLE_DUMPV2
|
|
} else {
|
|
flags &= ~(BGPF_ASN32BIT|BGPF_ADDPATH);
|
|
}
|
|
|
|
// Prepare pointers for NLRI inclusion (prefix pointer and length)
|
|
const void *nlriPtr = &nlri->width;
|
|
size_t nlriLen = 1 + PFXLEN(nlri->width);
|
|
if (flags & BGPF_ADDPATH) {
|
|
nlriPtr = &nlri->pathId;
|
|
nlriLen += 4;
|
|
} else
|
|
flags &= ~BGPF_ADDPATH;
|
|
|
|
// Write down message flags
|
|
msg->flags = flags & (BGPF_ADDPATH|BGPF_ASN32BIT|BGPF_EXMSG);
|
|
|
|
// Prepare an attribute offset table:
|
|
// We scan the entire attribute set, hence what we don't see isn't there
|
|
for (unsigned i = 0; i < BGP_ATTRTAB_LEN; i++)
|
|
msg->table[i] = BGP_ATTR_NOTFOUND;
|
|
|
|
// Allocate initial buffer
|
|
size_t bufsiz = BGP_MSGSIZ;
|
|
|
|
msg->buf = (Uint8 *) BGP_MEMOPS(msg)->Alloc(msg->allocp, bufsiz, NULL);
|
|
if (!msg->buf)
|
|
return Bgp_SetErrStat(BGPENOMEM);
|
|
|
|
// Begin recreating UPDATE message
|
|
Bgphdr *hdr = BGP_HDR(msg);
|
|
memset(hdr->marker, 0xff, sizeof(hdr->marker));
|
|
// hdr->len is updated after rebuild process
|
|
hdr->type = BGP_UPDATE;
|
|
|
|
// No withdrawn, zero-out 2 bytes
|
|
size_t off = BGP_HDRSIZ;
|
|
|
|
msg->buf[off++] = 0;
|
|
msg->buf[off++] = 0;
|
|
|
|
// Leave out 2 bytes for attributes length (updated after rebuild)
|
|
Uint16 tpaLen;
|
|
|
|
size_t tpaLenOff = off;
|
|
off += 2;
|
|
|
|
// Rebuild UPDATE message's TPA (we also populate lookup table ourselves)
|
|
Bgpattriter it;
|
|
const Bgpattr *attr;
|
|
|
|
Boolean shouldAppendNlri = TRUE; // unless proven otherwise
|
|
|
|
Bgp_StartUpdateAttributes(&it, tpa, NULL);
|
|
|
|
tpaLen = 0;
|
|
while ((attr = Bgp_NcNextAttribute(&it)) != NULL) {
|
|
size_t hdrSiz = BGP_ATTRHDRSIZ(attr);
|
|
size_t attrLen = BGP_ATTRLEN(attr);
|
|
size_t attrSiz = hdrSiz + attrLen;
|
|
|
|
int idx = bgp_attrTabIdx[attr->code];
|
|
|
|
if (attr->code == BGP_ATTR_MP_REACH_NLRI && (flags & BGPF_RIBV2) != 0) {
|
|
// Rebuild a BGP compliant MP_REACH attribute
|
|
if (attrLen < 1) {
|
|
Bgp_SetErrStat(BGPEBADRIBV2MPREACH);
|
|
goto error;
|
|
}
|
|
|
|
// TABLE_DUMPV2 MP_REACH lacks NLRI and removes reserved field,
|
|
// leaving only NEXT HOP length and contents
|
|
// ...by RFC at least, following code takes care of tolerating some
|
|
// common relaxed formats
|
|
|
|
const Uint8 *nextHop = (Uint8 *) BGP_ATTRPTR(attr);
|
|
|
|
Boolean isRfcCompliant;
|
|
if (flags & BGPF_STRICT_RFC6396)
|
|
isRfcCompliant = TRUE;
|
|
else
|
|
isRfcCompliant = Bgp_IsRibv2RfcCompliant(nextHop, attrLen, nlri->afi, nlri->safi);
|
|
|
|
if (!isRfcCompliant) {
|
|
// Non-conforming MP_REACH with extraneous AFI-SAFI:
|
|
// many MRT dumps leave them inside MP_REACH, despite being
|
|
// against RFC 6396.
|
|
// RFC also mandates that NEXT HOP length and NEXT HOP
|
|
// data should be the only contents included in MP_REACH_NLRI,
|
|
// but when the encoding is NOT compliant, sometimes even
|
|
// RESERVED field and NLRI are left there, so we also make
|
|
// attrLen consistent.
|
|
//
|
|
// NOTE: We already made sure that AFI, SAFI and NEXT HOP length
|
|
// inside the dump are plausible in
|
|
// Bgp_IsRibv2RfcCompliant().
|
|
|
|
nextHop += 2 + 1;
|
|
attrLen = 1 + *nextHop;
|
|
}
|
|
|
|
if ((size_t) *nextHop + 1 != attrLen) {
|
|
Bgp_SetErrStat(BGPEBADRIBV2MPREACH);
|
|
goto error;
|
|
}
|
|
|
|
// Calculate attribute size and ensure sufficient buffer size
|
|
size_t mpReachLen = 2 + 1 + attrLen + 1 + nlriLen;
|
|
Boolean isExtendedLen = (mpReachLen > 0xff);
|
|
|
|
attrSiz = 3 + ((size_t) isExtendedLen) + mpReachLen;
|
|
if (!Bgp_EnsureMsgBuffer(msg, &bufsiz, off + attrSiz))
|
|
goto error;
|
|
|
|
// Proceed with rebuild
|
|
if (isExtendedLen) {
|
|
msg->buf[off++] = attr->flags | BGP_ATTR_EXTENDED;
|
|
msg->buf[off++] = BGP_ATTR_MP_REACH_NLRI;
|
|
msg->buf[off++] = mpReachLen >> 8;
|
|
} else {
|
|
msg->buf[off++] = attr->flags & ~BGP_ATTR_EXTENDED;
|
|
msg->buf[off++] = BGP_ATTR_MP_REACH_NLRI;
|
|
}
|
|
|
|
msg->buf[off++] = mpReachLen & 0xff;
|
|
|
|
memcpy(msg->buf + off, &nlri->afi, sizeof(nlri->afi));
|
|
off += sizeof(nlri->afi);
|
|
|
|
msg->buf[off++] = nlri->safi;
|
|
|
|
memcpy(msg->buf + off, nextHop, attrLen);
|
|
off += attrLen;
|
|
|
|
msg->buf[off++] = 0; // reserved field
|
|
|
|
memcpy(msg->buf + off, nlriPtr, nlriLen);
|
|
off += nlriLen;
|
|
|
|
// We shouldn't add NLRI if we've seen an MP_REACH
|
|
// inside a TABLE_DUMPV2
|
|
shouldAppendNlri = FALSE;
|
|
|
|
} else {
|
|
if (attr->code == BGP_ATTR_MP_UNREACH_NLRI) {
|
|
if (flags & BGPF_STRIPUNREACH) {
|
|
// Kill off a spurious MP_UNREACH_NLRI
|
|
assert(idx >= 0);
|
|
msg->table[idx] = BGP_ATTR_NOTFOUND;
|
|
continue;
|
|
}
|
|
if (flags & BGPF_CLEARUNREACH) {
|
|
// Keep the attribute, but clear its contents
|
|
const Bgpmpfam *srcFam = Bgp_GetMpFamily(attr);
|
|
if (!srcFam)
|
|
goto error; // error already set
|
|
|
|
// Rebuild attribute on the fly with original AFI and SAFI
|
|
attrSiz = 3 + 2 + 1;
|
|
|
|
Bgpattr *mpUnreach = (Bgpattr *) alloca(attrSiz);
|
|
mpUnreach->flags = attr->flags & ~BGP_ATTR_EXTENDED;
|
|
mpUnreach->code = BGP_ATTR_MP_UNREACH_NLRI;
|
|
mpUnreach->len = 3;
|
|
|
|
Bgpmpfam *dstFam = (Bgpmpfam *) BGP_ATTRPTR(mpUnreach);
|
|
dstFam->afi = srcFam->afi;
|
|
dstFam->safi = srcFam->safi;
|
|
|
|
attr = mpUnreach;
|
|
}
|
|
}
|
|
|
|
// If we're seeing a MP_REACH_NLRI, then we're
|
|
// dealing with a TABLE_DUMP, AFI should be consistent with
|
|
// our NLRI, otherwise attribute should be evicted
|
|
if (attr->code == BGP_ATTR_MP_REACH_NLRI) {
|
|
const Bgpmpfam *family = Bgp_GetMpFamily(attr);
|
|
if (!family)
|
|
goto error; // error already set
|
|
|
|
if (family->afi != nlri->afi) {
|
|
// Discard spurious AFI from dump
|
|
assert(idx >= 0);
|
|
msg->table[idx] = BGP_ATTR_NOTFOUND;
|
|
continue;
|
|
}
|
|
|
|
// Force MP_REACH_NLRI to only contain the requested NLRI
|
|
// We create an attribute on the fly removing every extraneous
|
|
// prefix, leaving only the relevant one
|
|
|
|
size_t nextHopLen;
|
|
void *nextHop = Bgp_GetMpNextHop(attr, &nextHopLen);
|
|
if (!nextHop)
|
|
goto error; // error already set
|
|
|
|
size_t mpReachLen = 2 + 1 + 1 + nextHopLen + 1 + nlriLen;
|
|
Boolean isExtendedLen = (mpReachLen > 0xff);
|
|
|
|
attrSiz = 3 + ((size_t) isExtendedLen) + mpReachLen;
|
|
|
|
Bgpattr *mpReach = (Bgpattr *) alloca(attrSiz);
|
|
|
|
Uint8 *ptr = (Uint8 *) mpReach;
|
|
if (isExtendedLen) {
|
|
*ptr++ = attr->flags | BGP_ATTR_EXTENDED;
|
|
*ptr++ = BGP_ATTR_MP_REACH_NLRI;
|
|
*ptr++ = mpReachLen >> 8;
|
|
} else {
|
|
*ptr++ = attr->flags & ~BGP_ATTR_EXTENDED;
|
|
*ptr++ = BGP_ATTR_MP_REACH_NLRI;
|
|
}
|
|
|
|
*ptr++ = mpReachLen & 0xff;
|
|
|
|
memcpy(ptr, &nlri->afi, sizeof(nlri->afi));
|
|
ptr += sizeof(nlri->afi);
|
|
|
|
*ptr++ = family->safi; // preserve attribute SAFI, TABLE_DUMP RIB doesn't provide such info
|
|
|
|
*ptr++ = nextHopLen;
|
|
|
|
memcpy(ptr, nextHop, nextHopLen);
|
|
ptr += nextHopLen;
|
|
|
|
*ptr++ = 0; // reserved
|
|
|
|
memcpy(ptr, nlriPtr, nlriLen);
|
|
|
|
attr = mpReach; // copy from newly created attribute
|
|
shouldAppendNlri = FALSE; // ...and don't generate NLRI
|
|
}
|
|
|
|
if (!Bgp_EnsureMsgBuffer(msg, &bufsiz, off + attrSiz))
|
|
goto error;
|
|
|
|
memcpy(msg->buf + off, attr, attrSiz);
|
|
off += attrSiz;
|
|
}
|
|
|
|
// Update offset table if necessary and move on
|
|
if (idx >= 0)
|
|
msg->table[idx] = tpaLen;
|
|
|
|
tpaLen += attrSiz;
|
|
}
|
|
if (Bgp_GetErrStat(NULL))
|
|
goto error; // iteration failed
|
|
|
|
// Write out attributes length
|
|
tpaLen = beswap16(tpaLen);
|
|
memcpy(msg->buf + tpaLenOff, &tpaLen, sizeof(tpaLen));
|
|
|
|
// Include NLRI if MP_REACH_NLRI wasn't seen anywhere
|
|
if (shouldAppendNlri) {
|
|
// MP_REACH is mandatory in case of an AFI_IP6 address
|
|
if (nlri->afi == AFI_IP6) {
|
|
Bgp_SetErrStat(BGPERIBNOMPREACH);
|
|
goto error;
|
|
}
|
|
|
|
if (!Bgp_EnsureMsgBuffer(msg, &bufsiz, off + nlriLen))
|
|
goto error;
|
|
|
|
memcpy(msg->buf + off, nlriPtr, nlriLen);
|
|
off += nlriLen;
|
|
}
|
|
|
|
// Write out UPDATE message length
|
|
hdr->len = beswap16(off);
|
|
|
|
// Attempt buffer shrinking
|
|
void *newBuf = BGP_MEMOPS(msg)->Alloc(msg->allocp, off, msg->buf);
|
|
if (newBuf) // PARANOID
|
|
msg->buf = (Uint8 *) newBuf;
|
|
|
|
return OK; // error status already cleared by the iterator
|
|
|
|
error:
|
|
Bgp_ClearMsg(msg);
|
|
return NG; // error status already set
|
|
}
|
|
|
|
Bgpmpfam *Bgp_GetMpFamily(const Bgpattr *attr)
|
|
{
|
|
if (attr->code != BGP_ATTR_MP_REACH_NLRI &&
|
|
attr->code != BGP_ATTR_MP_UNREACH_NLRI) {
|
|
Bgp_SetErrStat(BGPEBADATTRTYPE);
|
|
return NULL;
|
|
}
|
|
if (BGP_ATTRLEN(attr) < 2 + 1) {
|
|
Bgp_SetErrStat(BGPETRUNCATTR);
|
|
return NULL;
|
|
}
|
|
|
|
Bgp_SetErrStat(BGPENOERR);
|
|
return (Bgpmpfam *) BGP_ATTRPTR(attr);
|
|
}
|
|
|
|
void *Bgp_GetMpNextHop(const Bgpattr *attr, size_t *nbytes)
|
|
{
|
|
if (attr->code != BGP_ATTR_MP_REACH_NLRI) {
|
|
Bgp_SetErrStat(BGPEBADATTRTYPE);
|
|
return NULL;
|
|
}
|
|
|
|
size_t off = 2 + 1; // skip MP_REACH
|
|
size_t len = BGP_ATTRLEN(attr);
|
|
|
|
Uint8 *data = (Uint8 *) BGP_ATTRPTR(attr);
|
|
if (len < off + 1 || len < off + 1 + data[off]) {
|
|
Bgp_SetErrStat(BGPETRUNCATTR);
|
|
return NULL;
|
|
}
|
|
|
|
Bgp_SetErrStat(BGPENOERR);
|
|
if (nbytes)
|
|
*nbytes = data[off];
|
|
|
|
return data + off + 1;
|
|
}
|
|
|
|
Bgpattr *Bgp_GetRealAggregator(const Bgpattrseg *tpa,
|
|
Boolean *isAsn32bit,
|
|
Bgpattrtab table)
|
|
{
|
|
/* RFC 6793
|
|
* A NEW BGP speaker MUST also be prepared to receive the AS4_AGGREGATOR
|
|
* attribute along with the AGGREGATOR attribute from an OLD BGP
|
|
* speaker. When both of the attributes are received, if the AS number
|
|
* in the AGGREGATOR attribute is not AS_TRANS, then:
|
|
*
|
|
* - the AS4_AGGREGATOR attribute and the AS4_PATH attribute SHALL be ignored,
|
|
* - the AGGREGATOR attribute SHALL be taken as the information
|
|
* about the aggregating node, and
|
|
* - the AS_PATH attribute SHALL be taken as the AS path
|
|
* information.
|
|
*
|
|
* Otherwise,
|
|
* - the AGGREGATOR attribute SHALL be ignored,
|
|
* - the AS4_AGGREGATOR attribute SHALL be taken as the information
|
|
* about the aggregating node, and
|
|
* - the AS path information would need to be constructed, as in all
|
|
* other cases.
|
|
*/
|
|
|
|
assert(isAsn32bit != NULL);
|
|
|
|
Bgpattr *attr = Bgp_GetUpdateAttribute(tpa, BGP_ATTR_AGGREGATOR, table);
|
|
if (!attr)
|
|
return NULL; // error, if any, already set
|
|
|
|
Boolean asn32bit = *isAsn32bit;
|
|
if (!BGP_CHKAGGRSIZ(attr, asn32bit)) {
|
|
Bgp_SetErrStat(BGPEBADAGGR);
|
|
return NULL;
|
|
}
|
|
|
|
Bgpaggr *aggr = (Bgpaggr *) BGP_ATTRPTR(attr);
|
|
if (ISASTRANS(BGP_AGGRAS(aggr, asn32bit))) {
|
|
Bgpattr *attr4 = Bgp_GetUpdateAttribute(tpa, BGP_ATTR_AS4_AGGREGATOR, table);
|
|
if (Bgp_GetErrStat(NULL))
|
|
return NULL; // forward error
|
|
|
|
if (attr4) {
|
|
if (!BGP_CHKAGGRSIZ(attr4, TRUE)) {
|
|
Bgp_SetErrStat(BGPEBADAGGR4);
|
|
return NULL;
|
|
}
|
|
|
|
attr = attr4;
|
|
asn32bit = TRUE;
|
|
}
|
|
}
|
|
|
|
*isAsn32bit = asn32bit;
|
|
return attr; // error already cleared
|
|
}
|
|
|
|
Bgpattr *Bgp_GetRealMsgAggregator(Bgpmsg *msg, Boolean *isAsn32bit)
|
|
{
|
|
const Bgpupdate *update = Bgp_GetMsgUpdate(msg);
|
|
if (!update)
|
|
return NULL; // error already set
|
|
|
|
const Bgpattrseg *tpa = Bgp_GetUpdateAttributes(update);
|
|
if (!tpa)
|
|
return NULL; // error already set
|
|
|
|
Boolean flag = BGP_ISASN32BIT(msg->flags);
|
|
Bgpattr *attr = Bgp_GetRealAggregator(tpa, &flag, msg->table);
|
|
if (!attr)
|
|
return NULL;
|
|
|
|
if (isAsn32bit)
|
|
*isAsn32bit = flag;
|
|
|
|
return attr;
|
|
}
|
|
|
|
#define BGP_ASSEGSIZ(seg, isAsn32bit) \
|
|
(2 + (((size_t) (seg)->len) << (1 + ((isAsn32bit) != 0))))
|
|
|
|
static void Bgp_DoStartAsSegments(Assegiter *it, const Bgpattr *attr, Boolean asn32bit)
|
|
{
|
|
it->base = (Uint8 *) BGP_ATTRPTR(attr);
|
|
it->lim = it->base + BGP_ATTRLEN(attr);
|
|
it->ptr = it->base;
|
|
it->asn32bit = asn32bit;
|
|
}
|
|
|
|
Judgement Bgp_StartAsSegments(Assegiter *it, const Bgpattr *attr, Boolean asn32bit)
|
|
{
|
|
if (attr->code != BGP_ATTR_AS_PATH)
|
|
return Bgp_SetErrStat(BGPEBADATTRTYPE);
|
|
|
|
Bgp_DoStartAsSegments(it, attr, asn32bit);
|
|
return Bgp_SetErrStat(BGPENOERR);
|
|
}
|
|
|
|
Judgement Bgp_StartAs4Segments(Assegiter *it, const Bgpattr *attr)
|
|
{
|
|
if (attr->code != BGP_ATTR_AS4_PATH)
|
|
return Bgp_SetErrStat(BGPEBADATTRTYPE);
|
|
|
|
Bgp_DoStartAsSegments(it, attr, /*asn32bit=*/TRUE);
|
|
return Bgp_SetErrStat(BGPENOERR);
|
|
}
|
|
|
|
Asseg *Bgp_NextAsSegment(Assegiter *it)
|
|
{
|
|
if (it->ptr >= it->lim) {
|
|
Bgp_SetErrStat(BGPENOERR);
|
|
return NULL;
|
|
}
|
|
|
|
size_t left = it->lim - it->ptr;
|
|
if (left < 2) {
|
|
Bgp_SetErrStat(BGPETRUNCATTR);
|
|
return NULL;
|
|
}
|
|
|
|
Asseg *seg = (Asseg *) it->ptr;
|
|
size_t siz = BGP_ASSEGSIZ(seg, it->asn32bit);
|
|
if (left < siz) {
|
|
Bgp_SetErrStat(BGPETRUNCATTR);
|
|
return NULL;
|
|
}
|
|
|
|
it->ptr += siz;
|
|
|
|
Bgp_SetErrStat(BGPENOERR);
|
|
return seg;
|
|
}
|
|
|
|
Judgement Bgp_StartMsgRealAsPath(Aspathiter *it, Bgpmsg *msg)
|
|
{
|
|
const Bgpupdate *update = Bgp_GetMsgUpdate(msg);
|
|
if (!update)
|
|
return NG; // error already set
|
|
|
|
const Bgpattrseg *tpa = Bgp_GetUpdateAttributes(update);
|
|
if (!tpa)
|
|
return NG; // error already set
|
|
|
|
return Bgp_StartRealAsPath(it, tpa, BGP_ISASN32BIT(msg->flags), msg->table);
|
|
}
|
|
|
|
Judgement Bgp_StartRealAsPath(Aspathiter *it,
|
|
const Bgpattrseg *tpa,
|
|
Boolean isAsn32bit,
|
|
Bgpattrtab table)
|
|
{
|
|
Bgpattr *asPath = Bgp_GetUpdateAttribute(tpa, BGP_ATTR_AS_PATH, table);
|
|
if (!asPath) {
|
|
memset(it, 0, sizeof(*it)); // set to empty iterator
|
|
it->maxCount = 0xffffu;
|
|
it->asIdx = 0xffffu;
|
|
return Bgp_GetErrStat(NULL) ? NG : OK; // propagate error
|
|
}
|
|
|
|
assert(asPath->code == BGP_ATTR_AS_PATH);
|
|
|
|
// Setup for iteration at AS_PATH
|
|
if (Bgp_StartAsSegments(&it->segs, asPath, isAsn32bit) != OK)
|
|
return NG; // propagate error
|
|
|
|
it->nextAttr = NULL;
|
|
it->maxCount = 0xffffu;
|
|
it->asIdx = 0xffffu;
|
|
|
|
// Peel off first segment
|
|
Asseg *seg = Bgp_NextAsSegment(&it->segs);
|
|
if (seg) {
|
|
// We've got at least one segment, setup base and limit
|
|
it->base = (Uint8 *) seg;
|
|
it->lim = it->base + BGP_ASSEGSIZ(seg, isAsn32bit);
|
|
it->ptr = seg->data;
|
|
} else {
|
|
if (Bgp_GetErrStat(NULL))
|
|
return NG; // propagate error
|
|
|
|
// AS_PATH is empty
|
|
it->lim = it->base = it->ptr = NULL;
|
|
}
|
|
|
|
// Check whether we have to account for AS4_PATH
|
|
Bgpattr *attr = Bgp_GetUpdateAttribute(tpa, BGP_ATTR_AGGREGATOR, table);
|
|
if (Bgp_GetErrStat(NULL))
|
|
return NG; // propagate error
|
|
|
|
Bgpattr *attr4 = Bgp_GetUpdateAttribute(tpa, BGP_ATTR_AS4_AGGREGATOR, table);
|
|
if (Bgp_GetErrStat(NULL))
|
|
return NG; // propagate error
|
|
|
|
if (!attr || !attr4)
|
|
return OK; // we're fine as it is
|
|
|
|
if (!BGP_CHKAGGRSIZ(attr, isAsn32bit))
|
|
return Bgp_SetErrStat(BGPEBADAGGR);
|
|
if (!BGP_CHKAGGRSIZ(attr4, TRUE))
|
|
return Bgp_SetErrStat(BGPEBADAGGR4);
|
|
|
|
Bgpattr *as4Path = Bgp_GetUpdateAttribute(tpa, BGP_ATTR_AS4_PATH, table);
|
|
if (!as4Path)
|
|
return Bgp_GetErrStat(NULL) ? NG : OK; // propagate error
|
|
|
|
assert(as4Path->code == BGP_ATTR_AS4_PATH);
|
|
|
|
// Count AS numbers available inside both AS_PATH and AS4_PATH
|
|
unsigned asCount = 0, as4Count = 0;
|
|
|
|
Assegiter segs;
|
|
|
|
Judgement res = Bgp_StartAsSegments(&segs, asPath, isAsn32bit); USED(res);
|
|
assert(res == OK); // definitely succeeds (we've done it before)
|
|
|
|
while ((seg = Bgp_NextAsSegment(&segs)) != NULL) {
|
|
if (seg->type == AS_SET)
|
|
asCount++;
|
|
else
|
|
asCount += seg->len;
|
|
}
|
|
if (Bgp_GetErrStat(NULL))
|
|
return NG; // propagate error
|
|
|
|
if (Bgp_StartAs4Segments(&segs, as4Path) != OK)
|
|
return NG; // propagate error
|
|
|
|
while ((seg = Bgp_NextAsSegment(&segs)) != NULL) {
|
|
if (seg->type == AS_SET)
|
|
as4Count++;
|
|
else
|
|
as4Count += seg->len;
|
|
}
|
|
if (Bgp_GetErrStat(NULL))
|
|
return NG; // propagate error
|
|
|
|
if (asCount >= as4Count) {
|
|
// Must read some ASes from asPath and then switch to as4Path
|
|
it->nextAttr = as4Path;
|
|
it->maxCount = asCount - as4Count;
|
|
}
|
|
return OK; // error already cleared
|
|
}
|
|
|
|
Asn Bgp_NextAsPath(Aspathiter *it)
|
|
{
|
|
if (it->maxCount == 0) {
|
|
// Force attribute swap (if any)
|
|
it->segs.ptr = it->segs.lim;
|
|
it->ptr = it->lim;
|
|
}
|
|
|
|
while (it->ptr >= it->lim) {
|
|
// Need to fetch another segment
|
|
Asseg *seg = Bgp_NextAsSegment(&it->segs);
|
|
|
|
if (seg) {
|
|
// New segment acquired, move on
|
|
it->base = (Uint8 *) seg;
|
|
it->lim = it->base + BGP_ASSEGSIZ(seg, it->segs.asn32bit);
|
|
it->ptr = seg->data;
|
|
} else {
|
|
if (Bgp_GetErrStat(NULL))
|
|
return -1; // report error
|
|
|
|
if (!it->nextAttr) {
|
|
// End of iteration, all good
|
|
Bgp_SetErrStat(BGPENOERR);
|
|
it->asIdx = 0xffffu;
|
|
return -1;
|
|
}
|
|
|
|
// Need to switch iterator
|
|
if (Bgp_StartAs4Segments(&it->segs, it->nextAttr) != OK)
|
|
return -1; // error already set
|
|
|
|
it->base = it->lim = it->ptr = NULL; // force a new segment fetch
|
|
it->nextAttr = NULL; // no more attributes follow
|
|
it->maxCount = 0xffffu; // no ASN limit
|
|
}
|
|
}
|
|
|
|
size_t left = it->lim - it->ptr;
|
|
assert(left > 0);
|
|
|
|
Asn32 a32;
|
|
Asn16 a16;
|
|
Asn asn;
|
|
|
|
if (it->segs.asn32bit) {
|
|
if (left < sizeof(a32)) {
|
|
Bgp_SetErrStat(BGPETRUNCATTR);
|
|
return -1;
|
|
}
|
|
|
|
memcpy(&a32, it->ptr, sizeof(a32));
|
|
it->ptr += sizeof(a32);
|
|
|
|
asn = ASN32BIT(a32);
|
|
} else {
|
|
if (left < sizeof(a16)) {
|
|
Bgp_SetErrStat(BGPETRUNCATTR);
|
|
return -1;
|
|
}
|
|
|
|
memcpy(&a16, it->ptr, sizeof(a16));
|
|
it->ptr += sizeof(a16);
|
|
|
|
asn = ASN16BIT(a16);
|
|
}
|
|
|
|
if (BGP_CURASSEG(it)->type == AS_SEQUENCE || it->ptr == it->lim)
|
|
it->maxCount--; // decrease max ASN limit, if any
|
|
|
|
it->asIdx++; // increment returned ASN counter
|
|
|
|
Bgp_SetErrStat(BGPENOERR);
|
|
return asn;
|
|
}
|
|
|
|
static Boolean Bgp_SwitchNextHop(Nexthopiter *it)
|
|
{
|
|
Boolean res = FALSE; // iteration end, unless found otherwise
|
|
|
|
if (it->nextHop) {
|
|
// Setup for read from NEXT_HOP
|
|
if (!BGP_CHKNEXTHOPLEN(it->nextHop)) {
|
|
Bgp_SetErrStat(BGPETRUNCATTR); // TODO BGPEBADNEXTHOP
|
|
return FALSE;
|
|
}
|
|
|
|
it->base = (Uint8 *) BGP_ATTRPTR(it->nextHop);
|
|
it->ptr = it->base;
|
|
it->lim = it->ptr + IPV4_SIZE;
|
|
it->afi = AFI_IP;
|
|
it->safi = SAFI_UNICAST;
|
|
|
|
it->nextHop = NULL; // consume NEXT_HOP
|
|
res = TRUE;
|
|
|
|
} else if (it->mpReach) {
|
|
// Setup for read from MP_REACH_NLRI
|
|
Bgpmpnexthop *nh;
|
|
|
|
if (it->isRibv2 &&
|
|
Bgp_IsRibv2RfcCompliant(BGP_ATTRPTR(it->mpReach),
|
|
BGP_ATTRLEN(it->mpReach),
|
|
it->afiRibv2, it->safiRibv2)) {
|
|
// We're dealing with a truncated MP_REACH_NLRI,
|
|
// it only has next hop data
|
|
it->afi = it->afiRibv2;
|
|
it->safi = it->safiRibv2;
|
|
|
|
nh = (Bgpmpnexthop *) BGP_ATTRPTR(it->mpReach);
|
|
} else {
|
|
// Full MP_REACH_NLRI, or non-compliant TABLE_DUMPV2 RIB MP_REACH_NLRI
|
|
if (!BGP_CHKMPREACH(it->mpReach)) {
|
|
Bgp_SetErrStat(BGPETRUNCATTR);
|
|
return FALSE;
|
|
}
|
|
|
|
Bgpmpfam *fam = BGP_MPFAMILY(it->mpReach);
|
|
if (it->isRibv2 && (fam->afi != it->afiRibv2 || it->safi != it->safiRibv2)) {
|
|
// discard MP_REACH_NLRI if it doesn't match with RIB AFI/SAFI
|
|
it->mpReach = NULL;
|
|
goto done;
|
|
}
|
|
|
|
nh = BGP_MPNEXTHOP(it->mpReach);
|
|
it->afi = fam->afi;
|
|
it->safi = fam->safi;
|
|
}
|
|
|
|
it->base = (Uint8 *) nh;
|
|
it->ptr = nh->data;
|
|
it->lim = &nh->data[nh->len];
|
|
|
|
it->mpReach = NULL; // consume MP_REACH_NLRI
|
|
res = TRUE;
|
|
}
|
|
|
|
done:
|
|
Bgp_SetErrStat(BGPENOERR);
|
|
return res;
|
|
}
|
|
|
|
Judgement Bgp_StartAllNextHops(Nexthopiter *it,
|
|
const Bgpattrseg *tpa,
|
|
Bgpattrtab table)
|
|
{
|
|
// Lookup relevant attributes
|
|
it->nextHop = Bgp_GetUpdateAttribute(tpa, BGP_ATTR_NEXT_HOP, table);
|
|
if (Bgp_GetErrStat(NULL))
|
|
return NG;
|
|
|
|
it->mpReach = Bgp_GetUpdateAttribute(tpa, BGP_ATTR_MP_REACH_NLRI, table);
|
|
if (Bgp_GetErrStat(NULL))
|
|
return NG;
|
|
|
|
// Force segment setup on first call to Bgp_NextNextHop()
|
|
it->base = it->lim = it->ptr = NULL;
|
|
it->afi = (Afi) 0; // meaningful only after a Bgp_NextNextHop()
|
|
it->safi = (Safi) 0; // ditto
|
|
it->isRibv2 = FALSE;
|
|
it->afiRibv2 = (Afi) 0;
|
|
it->safiRibv2 = (Safi) 0;
|
|
return OK; // error already cleared by lookup
|
|
}
|
|
|
|
Judgement Bgp_StartAllRibv2NextHops(Nexthopiter *it,
|
|
const Mrthdr *hdr,
|
|
const Bgpattrseg *tpa,
|
|
Bgpattrtab table)
|
|
{
|
|
if (hdr->type != MRT_TABLE_DUMPV2 || !TABLE_DUMPV2_ISRIB(hdr->subtype)) {
|
|
Bgp_SetErrStat(BGPEBADMRTTYPE);
|
|
return NG;
|
|
}
|
|
|
|
const Mrtribhdrv2 *ribhdr = RIBV2_HDR(hdr);
|
|
if (Bgp_StartAllNextHops(it, tpa, table) != OK)
|
|
return NG;
|
|
|
|
it->isRibv2 = TRUE;
|
|
if (TABLE_DUMPV2_ISIPV4RIB(hdr->subtype)) {
|
|
it->afiRibv2 = AFI_IP;
|
|
it->safiRibv2 = TABLE_DUMPV2_ISMULTICASTRIB(hdr->subtype) ?
|
|
SAFI_MULTICAST : SAFI_UNICAST;
|
|
|
|
} else if (TABLE_DUMPV2_ISIPV6RIB(hdr->subtype)) {
|
|
it->afiRibv2 = AFI_IP6;
|
|
it->safiRibv2 = TABLE_DUMPV2_ISMULTICASTRIB(hdr->subtype) ?
|
|
SAFI_MULTICAST : SAFI_UNICAST;
|
|
|
|
} else {
|
|
assert(TABLE_DUMPV2_ISGENERICRIB(hdr->subtype));
|
|
|
|
it->afiRibv2 = ribhdr->gen.afi;
|
|
it->safiRibv2 = ribhdr->gen.safi;
|
|
}
|
|
return OK;
|
|
}
|
|
|
|
Judgement Bgp_StartAllMsgNextHops(Nexthopiter *it, Bgpmsg *msg)
|
|
{
|
|
const Bgpupdate *update = Bgp_GetMsgUpdate(msg);
|
|
if (!update)
|
|
return NG; // error already set
|
|
|
|
const Bgpattrseg *tpa = Bgp_GetUpdateAttributes(update);
|
|
if (!tpa)
|
|
return NG; // error already set
|
|
|
|
return Bgp_StartAllNextHops(it, tpa, msg->table);
|
|
}
|
|
|
|
Ipadr *Bgp_NextNextHop(Nexthopiter *it)
|
|
{
|
|
if (it->ptr >= it->lim && !Bgp_SwitchNextHop(it))
|
|
return NULL; // end of iteration or failure, status set
|
|
|
|
size_t left = it->lim - it->ptr;
|
|
assert(left > 0);
|
|
|
|
switch (it->afi) {
|
|
case AFI_IP:
|
|
if (left < IPV4_SIZE) {
|
|
Bgp_SetErrStat(BGPETRUNCATTR);
|
|
return NULL;
|
|
}
|
|
|
|
it->addr.family = IP4;
|
|
memcpy(it->addr.bytes, it->ptr, IPV4_SIZE);
|
|
it->ptr += IPV4_SIZE;
|
|
break;
|
|
|
|
case AFI_IP6:
|
|
if (left < IPV6_SIZE) {
|
|
Bgp_SetErrStat(BGPETRUNCATTR);
|
|
return NULL;
|
|
}
|
|
|
|
it->addr.family = IP6;
|
|
memcpy(it->addr.bytes, it->ptr, IPV6_SIZE);
|
|
it->ptr += IPV6_SIZE;
|
|
break;
|
|
|
|
default:
|
|
// Should never happen
|
|
Bgp_SetErrStat(BGPEAFIUNSUP);
|
|
return NULL;
|
|
}
|
|
|
|
Bgp_SetErrStat(BGPENOERR);
|
|
return &it->addr;
|
|
}
|
|
|
|
#define BGP_COMMATTR(it) ((Bgpattr *) (it)->base)
|
|
|
|
static void *Bgp_DoNextCommunity(Bgpcommiter *it, BgpAttrCode code, size_t siz)
|
|
{
|
|
if (BGP_COMMATTR(it)->code != code) {
|
|
// Bad operation
|
|
Bgp_SetErrStat(BGPEBADATTRTYPE);
|
|
return NULL;
|
|
}
|
|
if (it->ptr >= it->lim) {
|
|
// End of iteration
|
|
Bgp_SetErrStat(BGPENOERR);
|
|
return NULL;
|
|
}
|
|
|
|
size_t left = it->lim - it->ptr;
|
|
assert(left > 0);
|
|
if (left < siz) {
|
|
Bgp_SetErrStat(BGPETRUNCATTR);
|
|
return NULL;
|
|
}
|
|
|
|
void *res = it->ptr;
|
|
it->ptr += siz;
|
|
|
|
Bgp_SetErrStat(BGPENOERR);
|
|
return res;
|
|
}
|
|
|
|
Judgement Bgp_StartCommunity(Bgpcommiter *it, const Bgpattr *attr)
|
|
{
|
|
if (attr->code != BGP_ATTR_COMMUNITY &&
|
|
attr->code != BGP_ATTR_EXTENDED_COMMUNITY &&
|
|
attr->code != BGP_ATTR_LARGE_COMMUNITY)
|
|
return Bgp_SetErrStat(BGPEBADATTRTYPE);
|
|
|
|
it->base = (Uint8 *) attr;
|
|
it->ptr = (Uint8 *) BGP_ATTRPTR(attr);
|
|
it->lim = it->ptr + BGP_ATTRLEN(attr);
|
|
return Bgp_SetErrStat(BGPENOERR);
|
|
}
|
|
|
|
Bgpcomm *Bgp_NextCommunity(Bgpcommiter *it)
|
|
{
|
|
return (Bgpcomm *) Bgp_DoNextCommunity(it, BGP_ATTR_COMMUNITY, 4);
|
|
}
|
|
|
|
Bgpexcomm *Bgp_NextExtendedCommunity(Bgpcommiter *it)
|
|
{
|
|
return (Bgpexcomm *) Bgp_DoNextCommunity(it, BGP_ATTR_EXTENDED_COMMUNITY, 2+6);
|
|
}
|
|
|
|
Bgplgcomm *Bgp_NextLargeCommunity(Bgpcommiter *it)
|
|
{
|
|
return (Bgplgcomm *) Bgp_DoNextCommunity(it, BGP_ATTR_LARGE_COMMUNITY, 3*4);
|
|
}
|
|
|
|
void *Bgp_GetMpRoutes(const Bgpattr *attr, size_t *nbytes)
|
|
{
|
|
if (attr->code != BGP_ATTR_MP_REACH_NLRI
|
|
&& attr->code != BGP_ATTR_MP_UNREACH_NLRI) {
|
|
Bgp_SetErrStat(BGPEBADATTRTYPE);
|
|
return NULL;
|
|
}
|
|
|
|
size_t off = 2 + 1; // skip AFI-SAFI
|
|
size_t len = BGP_ATTRLEN(attr);
|
|
|
|
Uint8 *data = (Uint8 *) BGP_ATTRPTR(attr);
|
|
if (len < off) {
|
|
Bgp_SetErrStat(BGPETRUNCATTR);
|
|
return NULL;
|
|
}
|
|
if (attr->code == BGP_ATTR_MP_REACH_NLRI) {
|
|
// skip NEXT-HOP and RESERVED octet
|
|
if (len == off || len < off + 1 + data[off] + 1) {
|
|
Bgp_SetErrStat(BGPETRUNCATTR);
|
|
return NULL;
|
|
}
|
|
|
|
off += 1 + data[off] + 1;
|
|
}
|
|
|
|
Bgp_SetErrStat(BGPENOERR);
|
|
if (nbytes)
|
|
*nbytes = len - off;
|
|
|
|
return data + off;
|
|
}
|
|
|
|
void Bgp_StartUpdateAttributes(Bgpattriter *it,
|
|
const Bgpattrseg *tpa,
|
|
Bgpattrtab table)
|
|
{
|
|
it->base = (Uint8 *) tpa->attrs;
|
|
it->lim = it->base + beswap16(tpa->len);
|
|
it->ptr = it->base;
|
|
memset(it->attrMask, 0, sizeof(it->attrMask));
|
|
it->table = table;
|
|
}
|
|
|
|
Judgement Bgp_StartMsgAttributes(Bgpattriter *it, Bgpmsg *msg)
|
|
{
|
|
const Bgpupdate *update = Bgp_GetMsgUpdate(msg);
|
|
if (!update)
|
|
return NG; // error already set
|
|
|
|
const Bgpattrseg *tpa = Bgp_GetUpdateAttributes(update);
|
|
if (!tpa)
|
|
return NG; // error already set
|
|
|
|
Bgp_StartUpdateAttributes(it, tpa, msg->table);
|
|
return OK; // error already cleared
|
|
}
|
|
|
|
Bgpattr *Bgp_NcNextAttribute(Bgpattriter *it)
|
|
{
|
|
size_t left, hdrSiz, len, siz;
|
|
Bgpattr *attr;
|
|
|
|
nextAttribute: // Label only used when skipping duplicates
|
|
|
|
if (it->ptr >= it->lim) {
|
|
Bgp_SetErrStat(BGPENOERR);
|
|
return NULL;
|
|
}
|
|
|
|
// Ensure size is correct (account for extended attributes)
|
|
left = it->lim - it->ptr;
|
|
attr = (Bgpattr *) it->ptr;
|
|
if (left < 3 || left < (hdrSiz = BGP_ATTRHDRSIZ(attr))) {
|
|
Bgp_SetErrStat(BGPETRUNCMSG);
|
|
return NULL;
|
|
}
|
|
if (left < hdrSiz + (len = BGP_ATTRLEN(attr))) {
|
|
Bgp_SetErrStat(BGPETRUNCMSG);
|
|
return NULL;
|
|
}
|
|
|
|
siz = hdrSiz + len;
|
|
|
|
// Deal with duplicate attributes
|
|
if (BGP_ISATTRSET(it->attrMask, attr->code)) {
|
|
/* RFC 7606 3.g Revision to BGP UPDATE Message Error Handling
|
|
*
|
|
* If the MP_REACH_NLRI attribute or the MP_UNREACH_NLRI [RFC4760]
|
|
* attribute appears more than once in the UPDATE message, then a
|
|
* NOTIFICATION message MUST be sent with the Error Subcode
|
|
* "Malformed Attribute List". If any other attribute (whether
|
|
* recognized or unrecognized) appears more than once in an UPDATE
|
|
* message, then all the occurrences of the attribute other than the
|
|
* first one SHALL be discarded and the UPDATE message will continue
|
|
* to be processed.
|
|
*/
|
|
if (attr->code != BGP_ATTR_MP_REACH_NLRI &&
|
|
attr->code != BGP_ATTR_MP_UNREACH_NLRI) {
|
|
// Attribute must be ignored
|
|
it->ptr += siz;
|
|
goto nextAttribute;
|
|
}
|
|
|
|
Bgp_SetErrStat(BGPEDUPNLRIATTR);
|
|
return NULL;
|
|
}
|
|
BGP_SETATTR(it->attrMask, attr->code); // seen new attribute type
|
|
|
|
// Advance and return
|
|
it->ptr += siz;
|
|
Bgp_SetErrStat(BGPENOERR);
|
|
return attr;
|
|
}
|
|
|
|
Bgpattr *Bgp_NextAttribute(Bgpattriter *it)
|
|
{
|
|
Bgpattr *attr = Bgp_NcNextAttribute(it);
|
|
if (!attr)
|
|
return NULL; // error already set if any
|
|
|
|
// Include in attribute table if needed
|
|
int idx = bgp_attrTabIdx[attr->code];
|
|
if (idx >= 0)
|
|
Smp_AtomicStore16Rx(&it->table[idx], (Uint8 *) attr - it->base);
|
|
|
|
return attr;
|
|
}
|
|
|
|
Bgpattr *Bgp_GetUpdateAttribute(const Bgpattrseg *tpa,
|
|
BgpAttrCode code,
|
|
Bgpattrtab table)
|
|
{
|
|
Bgpattr *attr;
|
|
|
|
// Attempt quick lookup from table
|
|
int idx = bgp_attrTabIdx[code];
|
|
if (idx >= 0) {
|
|
// Attribute is indexed inside table
|
|
Sint16 off = Smp_AtomicLoad16Rx(&table[idx]);
|
|
if (off == BGP_ATTR_NOTFOUND)
|
|
return NULL; // not available
|
|
if (off != BGP_ATTR_UNKNOWN) {
|
|
// Attribute cached in table
|
|
attr = (Bgpattr *) (tpa->attrs + (Uint16) off);
|
|
|
|
assert(attr->code == code);
|
|
return attr;
|
|
}
|
|
}
|
|
|
|
// Slow scan for the attribute
|
|
Bgpattriter it;
|
|
|
|
Bgp_StartUpdateAttributes(&it, tpa, table);
|
|
while ((attr = Bgp_NextAttribute(&it)) != NULL) {
|
|
if (attr->code == code)
|
|
return attr; // found
|
|
}
|
|
if (Bgp_GetErrStat(NULL))
|
|
return NULL; // iteration failed
|
|
|
|
// If the iteration was successful we know we've scanned the whole
|
|
// attribute list, so set all unknown attributes in table to not found
|
|
for (unsigned i = 0; i < BGP_ATTRTAB_LEN; i++)
|
|
Smp_AtomicCas16Rx(&table[i], BGP_ATTR_UNKNOWN, BGP_ATTR_NOTFOUND);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
Bgpattr *Bgp_GetMsgAttribute(Bgpmsg *msg, BgpAttrCode code)
|
|
{
|
|
Bgpupdate *update = Bgp_GetMsgUpdate(msg);
|
|
if (!update)
|
|
return NULL; // error already set
|
|
|
|
Bgpattrseg *tpa = Bgp_GetUpdateAttributes(update);
|
|
if (!tpa)
|
|
return NULL; // error already set
|
|
|
|
return Bgp_GetUpdateAttribute(tpa, code, msg->table);
|
|
}
|