276 lines
7.4 KiB
C
276 lines
7.4 KiB
C
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||
|
|
||
|
/**
|
||
|
* \file bgpgrep_dump.c
|
||
|
*
|
||
|
* BGP message dump logic.
|
||
|
*
|
||
|
* \copyright The DoubleFourteen Code Forge (C) All Rights Reserved
|
||
|
* \author Lorenzo Cogotti
|
||
|
*/
|
||
|
|
||
|
#include "bgpgrep_local.h"
|
||
|
|
||
|
#include "sys/con.h"
|
||
|
#include "sys/endian.h"
|
||
|
|
||
|
#include <assert.h>
|
||
|
#include <string.h>
|
||
|
|
||
|
// NOTE: Each dump function is *RESPONSIBLE* to set S.dropEntryFrame
|
||
|
|
||
|
static void FixBgpAttributeTableForRib(Bgpattrtab tab, Boolean isRibv2)
|
||
|
{
|
||
|
// HACK ALERT:
|
||
|
//
|
||
|
// This is an innocent hack to speed up RIBv2 dumps on BGP messages
|
||
|
// that were already rebuilt (e.g. non-trivial filtering was necessary).
|
||
|
//
|
||
|
// The optimization is based on the fact that we know all offsets
|
||
|
// we've calculated inside the rebuilt BGP attribute list are valid up to
|
||
|
// the occurrence of MP_REACH_NLRI or MP_UNREACH_NLRI, whichever comes
|
||
|
// first. Moreover what we haven't found inside the BGP message attribute
|
||
|
// list isn't in the RIB either.
|
||
|
//
|
||
|
// NOTE: A LOT of RIBs also include the MP_UNREACH attribute,
|
||
|
// which is unfortunate. We clear the MP_UNREACH_NLRI attribute during
|
||
|
// filtering (to avoid printing false positives), but it still messes
|
||
|
// with the offsets of the subsequent attributes.
|
||
|
//
|
||
|
// NOTE: There is no chance to get BGP_ATTR_UNKNOWN inside the table,
|
||
|
// when BGP messages are rebuilt their offset table is filled up entirely.
|
||
|
|
||
|
// We don't need thread safety over Bgpattrtab, as bgpgrep is single-threaded.
|
||
|
|
||
|
Sint16 off = tab[bgp_attrTabIdx[BGP_ATTR_MP_UNREACH_NLRI]];
|
||
|
Uint16 maxoff = 0xffffu;
|
||
|
if (off != BGP_ATTR_NOTFOUND)
|
||
|
maxoff = (Uint16) off;
|
||
|
|
||
|
if (isRibv2) {
|
||
|
off = tab[bgp_attrTabIdx[BGP_ATTR_MP_REACH_NLRI]];
|
||
|
if (off != BGP_ATTR_NOTFOUND && maxoff > (Uint16) off)
|
||
|
maxoff = (Uint16) off;
|
||
|
}
|
||
|
if (maxoff == 0xffffu) // no MP_REACH or MP_UNREACH, table is perfectly ok
|
||
|
return;
|
||
|
|
||
|
// Reset any offset after maxoff
|
||
|
for (int i = 0; i < BGP_ATTRTAB_LEN; i++) {
|
||
|
off = tab[i];
|
||
|
if (off == BGP_ATTR_NOTFOUND)
|
||
|
continue;
|
||
|
if ((Uint16) off > maxoff)
|
||
|
tab[i] = BGP_ATTR_UNKNOWN;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void OutputBgp4mp(const Mrthdr *hdr, Bgpattrtab tab)
|
||
|
{
|
||
|
S.lenientBgpErrors = TRUE;
|
||
|
S.outFmt->DumpBgp4mp(hdr, STM_CONHN(STDOUT), Stm_ConOps, tab);
|
||
|
S.lenientBgpErrors = FALSE;
|
||
|
}
|
||
|
|
||
|
void BgpgrepD_Bgp4mp(void)
|
||
|
{
|
||
|
const Mrthdr *hdr = MRT_HDR(&S.rec);
|
||
|
const Bgp4mphdr *bgp4mp = Bgp_GetBgp4mpHdr(&S.rec, NULL);
|
||
|
|
||
|
if (BGP4MP_ISSTATECHANGE(hdr->subtype)) {
|
||
|
OutputBgp4mp(hdr, NULL);
|
||
|
goto done;
|
||
|
}
|
||
|
if (!BGP4MP_ISMESSAGE(hdr->subtype))
|
||
|
goto done; // don't care for anything else
|
||
|
|
||
|
// NOTE: Optimizing BGP4MP to avoid message rebuild isn't worth the effort
|
||
|
|
||
|
// Setup for BGP4MP
|
||
|
S.peerAs = BGP4MP_GETPEERADDR(hdr->subtype, &S.peerAddr, bgp4mp);
|
||
|
S.timestampSecs = beswap32(hdr->timestamp);
|
||
|
S.timestampMicrosecs = 0;
|
||
|
if (hdr->subtype == MRT_BGP4MP_ET)
|
||
|
S.timestampMicrosecs = beswap32(((const Mrthdrex *) hdr)->microsecs);
|
||
|
|
||
|
// Dump MRT data
|
||
|
Bgp_UnwrapBgp4mp(&S.rec, &S.msg, /*flags=*/BGPF_UNOWNED);
|
||
|
if (Bgp_VmExec(&S.vm, &S.msg))
|
||
|
OutputBgp4mp(hdr, S.msg.table);
|
||
|
|
||
|
Bgp_ClearMsg(&S.msg);
|
||
|
|
||
|
done:
|
||
|
Bgp_ClearMrt(&S.rec);
|
||
|
}
|
||
|
|
||
|
static void OutputZebra(const Mrthdr *hdr, Bgpattrtab tab)
|
||
|
{
|
||
|
S.lenientBgpErrors = TRUE;
|
||
|
S.outFmt->DumpZebra(hdr, STM_CONHN(STDOUT), Stm_ConOps, tab);
|
||
|
S.lenientBgpErrors = FALSE;
|
||
|
}
|
||
|
|
||
|
void BgpgrepD_Zebra(void)
|
||
|
{
|
||
|
const Mrthdr *hdr = MRT_HDR(&S.rec);
|
||
|
const Zebrahdr *zebra = Bgp_GetZebraHdr(&S.rec, NULL);
|
||
|
|
||
|
if (hdr->subtype == ZEBRA_STATE_CHANGE) {
|
||
|
OutputZebra(hdr, NULL);
|
||
|
goto done;
|
||
|
}
|
||
|
if (!ZEBRA_ISMESSAGE(hdr->subtype))
|
||
|
goto done; // don't care for anything else
|
||
|
|
||
|
if (S.isTrivialFilter) {
|
||
|
// FAST PATH - avoid rebuilding original message
|
||
|
BGP_CLRATTRTAB(S.msg.table);
|
||
|
OutputZebra(hdr, S.msg.table);
|
||
|
|
||
|
goto done;
|
||
|
}
|
||
|
|
||
|
// FILTERING PATH - Setup filter
|
||
|
S.peerAs = ASN16BIT(zebra->peerAs);
|
||
|
S.peerAddr.family = IP4;
|
||
|
S.peerAddr.v4 = zebra->peerAddr;
|
||
|
|
||
|
S.timestampSecs = beswap32(hdr->timestamp);
|
||
|
S.timestampMicrosecs = 0;
|
||
|
|
||
|
// Filter and dump BGP data
|
||
|
Bgp_UnwrapZebra(&S.rec, &S.msg, /*flags=*/0);
|
||
|
if (Bgp_VmExec(&S.vm, &S.msg))
|
||
|
OutputZebra(hdr, S.msg.table);
|
||
|
|
||
|
Bgp_ClearMsg(&S.msg);
|
||
|
|
||
|
done:
|
||
|
Bgp_ClearMrt(&S.rec);
|
||
|
}
|
||
|
|
||
|
static void OutputRibv2(const Mrthdr *hdr,
|
||
|
const Mrtpeerentv2 *peerent,
|
||
|
const Mrtribentv2 *ent,
|
||
|
Bgpattrtab tab)
|
||
|
{
|
||
|
S.lenientBgpErrors = TRUE;
|
||
|
S.outFmt->DumpRibv2(hdr, peerent, ent, STM_CONHN(STDOUT), Stm_ConOps, tab);
|
||
|
S.lenientBgpErrors = FALSE;
|
||
|
}
|
||
|
|
||
|
void BgpgrepD_TableDumpv2(void)
|
||
|
{
|
||
|
const Mrthdr *hdr = MRT_HDR(&S.rec);
|
||
|
if (hdr->subtype == TABLE_DUMPV2_PEER_INDEX_TABLE) {
|
||
|
// Store record as PEER_INDEX_TABLE
|
||
|
Bgp_ClearMrt(&S.peerIndex);
|
||
|
|
||
|
MRT_MOVEREC(&S.peerIndex, &S.rec);
|
||
|
S.hasPeerIndex = TRUE;
|
||
|
return;
|
||
|
}
|
||
|
if (!TABLE_DUMPV2_ISRIB(hdr->subtype))
|
||
|
goto done; // don't care for anything but RIBs
|
||
|
|
||
|
// We may only dump record if we've got a PEER_INDEX_TABLE
|
||
|
if (!S.hasPeerIndex)
|
||
|
Bgpgrep_DropRecord("SKIPPING TABLE_DUMPV2 RECORD - No PEER_INDEX_TABLE found yet");
|
||
|
|
||
|
// Scan every entry inside RIBv2
|
||
|
const Mrtribentv2 *ent;
|
||
|
const Mrtribhdrv2 *ribhdr = Bgp_GetMrtRibHdrv2(&S.rec, NULL);
|
||
|
|
||
|
Mrtribiterv2 ribents;
|
||
|
Bgp_StartMrtRibEntriesv2(&ribents, &S.rec);
|
||
|
while ((ent = Bgp_NextRibEntryv2(&ribents)) != NULL) {
|
||
|
// If we get a corrupted entry, we must still scan what's next
|
||
|
if (setjmp_fast(S.dropMsgFrame))
|
||
|
continue;
|
||
|
|
||
|
// Fetch Peer entry
|
||
|
Uint16 idx = beswap16(ent->peerIndex);
|
||
|
const Mrtpeerentv2 *peerent = Bgp_GetMrtPeerByIndex(&S.peerIndex, idx);
|
||
|
if (S.isTrivialFilter) {
|
||
|
// FAST PATH - avoid BGP message rebuild
|
||
|
BGP_CLRATTRTAB(S.msg.table);
|
||
|
OutputRibv2(hdr, peerent, ent, S.msg.table);
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
// FILTERING PATH
|
||
|
|
||
|
Prefix pfx;
|
||
|
RIBV2_GETNLRI(hdr->subtype, &pfx, ribhdr, ent);
|
||
|
|
||
|
// Setup filter
|
||
|
S.peerAs = MRT_GETPEERADDR(&S.peerAddr, peerent);
|
||
|
S.timestampSecs = beswap32(ent->originatedTime);
|
||
|
S.timestampMicrosecs = 0;
|
||
|
|
||
|
// Execute filter and dump RIBv2s
|
||
|
const Bgpattrseg *tpa = RIBV2_GETATTRIBS(hdr->subtype, ent);
|
||
|
|
||
|
Bgp_RebuildMsgFromRib(&pfx, tpa, &S.msg, /*flags=*/BGPF_RIBV2|BGPF_CLEARUNREACH);
|
||
|
if (Bgp_VmExec(&S.vm, &S.msg)) {
|
||
|
FixBgpAttributeTableForRib(S.msg.table, /*isRibv2=*/TRUE);
|
||
|
|
||
|
OutputRibv2(hdr, peerent, ent, S.msg.table);
|
||
|
}
|
||
|
|
||
|
Bgp_ClearMsg(&S.msg);
|
||
|
}
|
||
|
|
||
|
done:
|
||
|
Bgp_ClearMrt(&S.rec);
|
||
|
}
|
||
|
|
||
|
static void OutputRib(const Mrthdr *hdr, const Mrtribent *ent, Bgpattrtab tab)
|
||
|
{
|
||
|
S.lenientBgpErrors = TRUE;
|
||
|
S.outFmt->DumpRib(hdr, ent, STM_CONHN(STDOUT), Stm_ConOps, tab);
|
||
|
S.lenientBgpErrors = FALSE;
|
||
|
}
|
||
|
|
||
|
void BgpgrepD_TableDump(void)
|
||
|
{
|
||
|
const Mrthdr *hdr = MRT_HDR(&S.rec);
|
||
|
const Mrtribent *ent = Bgp_GetMrtRibHdr(&S.rec);
|
||
|
|
||
|
if (S.isTrivialFilter) {
|
||
|
// FAST PATH - No need to rebuild BGP
|
||
|
BGP_CLRATTRTAB(S.msg.table);
|
||
|
OutputRib(hdr, ent, S.msg.table);
|
||
|
goto done;
|
||
|
}
|
||
|
|
||
|
// FILTERING PATH - Setup VM for TABLE_DUMP
|
||
|
S.peerAs = RIB_GETPEERADDR(hdr->subtype, &S.peerAddr, ent);
|
||
|
S.timestampSecs = beswap32(RIB_GETORIGINATED(hdr->subtype, ent));
|
||
|
S.timestampMicrosecs = 0;
|
||
|
|
||
|
// Rebuild message and execute filter
|
||
|
Prefix pfx;
|
||
|
|
||
|
pfx.afi = hdr->subtype;
|
||
|
pfx.safi = SAFI_UNICAST;
|
||
|
pfx.isAddPath = FALSE;
|
||
|
pfx.pathId = 0; // unimportant
|
||
|
RIB_GETPFX(hdr->subtype, PLAINPFX(&pfx), ent);
|
||
|
|
||
|
const Bgpattrseg *tpa = RIB_GETATTRIBS(hdr->subtype, ent);
|
||
|
|
||
|
Bgp_RebuildMsgFromRib(&pfx, tpa, &S.msg, /*flags=*/BGPF_CLEARUNREACH);
|
||
|
if (Bgp_VmExec(&S.vm, &S.msg)) {
|
||
|
FixBgpAttributeTableForRib(S.msg.table, /*isRibv2=*/FALSE);
|
||
|
|
||
|
OutputRib(hdr, ent, S.msg.table);
|
||
|
}
|
||
|
|
||
|
Bgp_ClearMsg(&S.msg);
|
||
|
|
||
|
done:
|
||
|
Bgp_ClearMrt(&S.rec);
|
||
|
}
|