// 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 #include // 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); }