mirror of
https://codeberg.org/1414codeforge/ubgpsuite.git
synced 2025-06-05 21:29:11 +02:00
[*] Initial commit
This commit is contained in:
1235
lonetix/bgp/attribute.c
Normal file
1235
lonetix/bgp/attribute.c
Normal file
File diff suppressed because it is too large
Load Diff
611
lonetix/bgp/bgp.c
Normal file
611
lonetix/bgp/bgp.c
Normal file
@@ -0,0 +1,611 @@
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
|
||||
/**
|
||||
* \file bgp/bgp.c
|
||||
*
|
||||
* BGP message decoding.
|
||||
*
|
||||
* \copyright The DoubleFourteen Code Forge (C) All Rights Reserved
|
||||
* \author Lorenzo Cogotti
|
||||
*/
|
||||
|
||||
#include "bgp/bgp_local.h"
|
||||
#include "sys/sys_local.h"
|
||||
#include "sys/dbg.h"
|
||||
#include "sys/endian.h"
|
||||
#include "sys/ip.h"
|
||||
#include "sys/con.h"
|
||||
#include "argv.h"
|
||||
#include "numlib.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
static const Uint8 bgp_marker[] = {
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
|
||||
};
|
||||
|
||||
STATIC_ASSERT(sizeof(bgp_marker) == BGP_MARKER_LEN, "Malformed 'bgp_marker'");
|
||||
|
||||
static THREAD_LOCAL BgpErrStat bgp_errs;
|
||||
|
||||
const char *Bgp_ErrorString(BgpRet code)
|
||||
{
|
||||
switch (code) {
|
||||
case BGPEBADVM: return "Attempting operation on BGP VM under error state";
|
||||
case BGPEVMNOPROG: return "Attempt to execute BGP VM with empty bytecode";
|
||||
case BGPEVMBADCOMTCH: return "Bad COMMUNITY match expression";
|
||||
case BGPEVMASMTCHESIZE: return "BGP VM AS_PATH match expression too complex";
|
||||
case BGPEVMASNGRPLIM: return "BGP VM AS_PATH match expression group limit hit";
|
||||
case BGPEVMBADASMTCH: return "Bad AS_PATH match expression";
|
||||
case BGPEVMBADJMP: return "BGP VM jump instruction target is out of bounds";
|
||||
case BGPEVMILL: return "Illegal instruction in BGP VM bytecode";
|
||||
case BGPEVMOOM: return "BGP VM heap memory exhausted";
|
||||
case BGPEVMBADENDBLK: return "Encountered ENDBLK with no corresponding BLK";
|
||||
case BGPEVMUFLOW: return "BGP VM stack underflow";
|
||||
case BGPEVMOFLOW: return "BGP VM stack overflow";
|
||||
case BGPEVMBADFN: return "CALL instruction index is out of bounds";
|
||||
case BGPEVMBADK: return "LOADK instruction index is out of bounds";
|
||||
case BGPEVMMSGERR: return "Error encountered during BGP message access";
|
||||
case BGPEVMBADOP: return "BGP VM instruction has invalid operand";
|
||||
case BGPENOERR: return "No error";
|
||||
case BGPEIO: return "Input/Output error";
|
||||
case BGPEBADTYPE: return "Provided BGP message has inconsistent type";
|
||||
case BGPENOADDPATH: return "Provided BGP message contains no ADD_PATH information";
|
||||
case BGPEBADATTRTYPE: return "Provided BGP attribute has inconsistent type";
|
||||
case BGPEBADMARKER: return "BGP message marker mismatch";
|
||||
case BGPENOMEM: return "Memory allocation failure";
|
||||
case BGPETRUNCMSG: return "Truncated BGP message";
|
||||
case BGPEOVRSIZ: return "Oversized BGP message";
|
||||
case BGPEBADOPENLEN: return "BGP OPEN message has inconsistent length";
|
||||
case BGPEDUPNLRIATTR: return "Duplicate NLRI attribute detected inside UPDATE message";
|
||||
case BGPEBADPFXWIDTH: return "Bad prefix width";
|
||||
case BGPETRUNCPFX: return "Truncated prefix";
|
||||
case BGPETRUNCATTR: return "Truncated BGP attribute";
|
||||
case BGPEBADAGGR: return "Malformed AGGREGATOR attribute";
|
||||
case BGPEBADAGGR4: return "Malformed AS4_AGGREGATOR attribute";
|
||||
case BGPEAFIUNSUP: return "Unsupported Address Family Identifier";
|
||||
case BGPESAFIUNSUP: return "Unsupported Subsequent Address Family Identifier";
|
||||
case BGPEBADMRTTYPE: return "Provided MRT record has inconsistent type";
|
||||
case BGPETRUNCMRT: return "Truncated MRT record";
|
||||
case BGPEBADPEERIDXCNT: return "TABLE_DUMPV2 Peer Index Table record has incoherent peer entry count";
|
||||
case BGPETRUNCPEERV2: return "Truncated peer entry in TABLE_DUMPV2 Peer Index Table record";
|
||||
case BGPEBADRIBV2CNT: return "TABLE_DUMPV2 RIB record has incoherent RIB entry count";
|
||||
case BGPETRUNCRIBV2: return "Truncated entry in TABLE_DUMPV2 RIB record";
|
||||
case BGPEBADRIBV2MPREACH: return "TABLE_DUMPV2 RIB record contains an illegal MP_REACH attribute";
|
||||
case BGPERIBNOMPREACH: return "IPv6 RIB entry lacks MP_REACH_NLRI attribute";
|
||||
case BGPEBADPEERIDX: return "Peer entry index is out of bounds";
|
||||
default: return "Unknown BGP error";
|
||||
}
|
||||
}
|
||||
|
||||
static NOINLINE NORETURN void Bgp_Quit(BgpRet code, Srcloc *loc, void *obj)
|
||||
{
|
||||
USED(obj);
|
||||
|
||||
loc->call_depth++; // include Bgp_Quit() itself
|
||||
|
||||
if (com_progName) {
|
||||
Sys_Print(STDERR, com_progName);
|
||||
Sys_Print(STDERR, ": ");
|
||||
}
|
||||
|
||||
Sys_Print(STDERR, "Terminate called in response to an unrecoverable BGP error.\n\t");
|
||||
|
||||
if (loc) {
|
||||
#ifndef NDEBUG
|
||||
if (loc->filename && loc->line > 0) {
|
||||
char buf[64];
|
||||
|
||||
Utoa(loc->line, buf);
|
||||
Sys_Print(STDERR, "Error occurred in file ");
|
||||
Sys_Print(STDERR, loc->filename);
|
||||
Sys_Print(STDERR, ":");
|
||||
Sys_Print(STDERR, buf);
|
||||
Sys_Print(STDERR, "\n\t");
|
||||
}
|
||||
#endif
|
||||
if (loc->func) {
|
||||
Sys_Print(STDERR, "[");
|
||||
Sys_Print(STDERR, loc->func);
|
||||
Sys_Print(STDERR, "()]: ");
|
||||
}
|
||||
}
|
||||
|
||||
Sys_Print(STDERR, Bgp_ErrorString(code));
|
||||
Sys_Print(STDERR, ".\n");
|
||||
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
Judgement _Bgp_SetErrStat(BgpRet code,
|
||||
const char *filename,
|
||||
const char *func,
|
||||
unsigned long long line,
|
||||
unsigned depth)
|
||||
{
|
||||
// Don't clobber error code on BGP message access error inside filtering VM
|
||||
if (code != BGPEVMMSGERR)
|
||||
bgp_errs.code = code;
|
||||
if (code == BGPENOERR)
|
||||
return OK; // usual case
|
||||
|
||||
void (*err_func)(BgpRet, Srcloc *, void *);
|
||||
Srcloc loc;
|
||||
|
||||
err_func = bgp_errs.func;
|
||||
if (err_func) {
|
||||
loc.filename = filename;
|
||||
loc.func = func;
|
||||
loc.line = line;
|
||||
loc.call_depth = depth + 1;
|
||||
|
||||
if (err_func == BGP_ERR_QUIT)
|
||||
err_func = Bgp_Quit;
|
||||
|
||||
err_func(code, &loc, bgp_errs.obj);
|
||||
}
|
||||
|
||||
return NG;
|
||||
}
|
||||
|
||||
void Bgp_SetErrFunc(void (*func)(BgpRet, Srcloc *, void *),
|
||||
void *arg)
|
||||
{
|
||||
bgp_errs.func = func;
|
||||
bgp_errs.obj = arg;
|
||||
}
|
||||
|
||||
BgpRet Bgp_GetErrStat(BgpErrStat *stat)
|
||||
{
|
||||
if (stat)
|
||||
*stat = bgp_errs;
|
||||
|
||||
return bgp_errs.code;
|
||||
}
|
||||
|
||||
Uint16 Bgp_CheckMsgHdr(const void *data,
|
||||
size_t nbytes,
|
||||
Boolean allowExtendedSize)
|
||||
{
|
||||
if (nbytes < BGP_HDRSIZ) {
|
||||
Bgp_SetErrStat(BGPETRUNCMSG);
|
||||
return 0;
|
||||
}
|
||||
|
||||
const Bgphdr *hdr = (const Bgphdr *) data;
|
||||
if (memcmp(hdr->marker, bgp_marker, BGP_MARKER_LEN) != 0) {
|
||||
Bgp_SetErrStat(BGPEBADMARKER);
|
||||
return 0;
|
||||
}
|
||||
|
||||
Uint16 len = beswap16(hdr->len);
|
||||
if (len < BGP_HDRSIZ) {
|
||||
Bgp_SetErrStat(BGPETRUNCMSG);
|
||||
return 0;
|
||||
}
|
||||
if (len > BGP_MSGSIZ && !allowExtendedSize) {
|
||||
Bgp_SetErrStat(BGPEOVRSIZ);
|
||||
return 0;
|
||||
}
|
||||
if (len > nbytes) {
|
||||
Bgp_SetErrStat(BGPETRUNCMSG);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
Judgement Bgp_MsgFromBuf(Bgpmsg *msg,
|
||||
const void *data,
|
||||
size_t nbytes,
|
||||
unsigned flags)
|
||||
{
|
||||
// Immediately initialize flags (mask away superflous ones)
|
||||
msg->flags = flags & (BGPF_ADDPATH|BGPF_ASN32BIT|BGPF_EXMSG|BGPF_UNOWNED);
|
||||
|
||||
// Check header data for correctness
|
||||
Uint16 len = Bgp_CheckMsgHdr(data, nbytes, BGP_ISEXMSG(msg->flags));
|
||||
if (len == 0)
|
||||
return NG; // error already set by Bgp_CheckHdr()
|
||||
|
||||
if (BGP_ISUNOWNED(msg->flags))
|
||||
msg->buf = (Uint8 *) data; // don't copy data over
|
||||
|
||||
else {
|
||||
// Copy over relevant data
|
||||
const MemOps *memOps = BGP_MEMOPS(msg);
|
||||
|
||||
msg->buf = (Uint8 *) memOps->Alloc(msg->allocp, len, NULL);
|
||||
if (!msg->buf)
|
||||
return Bgp_SetErrStat(BGPENOMEM);
|
||||
|
||||
memcpy(msg->buf, data, len);
|
||||
}
|
||||
|
||||
BGP_CLRATTRTAB(msg->table);
|
||||
return Bgp_SetErrStat(BGPENOERR);
|
||||
}
|
||||
|
||||
Judgement Bgp_ReadMsg(Bgpmsg *msg,
|
||||
void *streamp,
|
||||
const StmOps *ops,
|
||||
unsigned flags)
|
||||
{
|
||||
// Immediately initialize flags (mask away superflous ones)
|
||||
msg->flags = flags & (BGPF_ADDPATH|BGPF_ASN32BIT|BGPF_EXMSG|BGPF_UNOWNED);
|
||||
|
||||
Uint8 buf[BGP_HDRSIZ];
|
||||
Sint64 n = ops->Read(streamp, buf, BGP_HDRSIZ);
|
||||
if (n == 0) {
|
||||
// precisely at end of stream, no error, just no more BGP messages
|
||||
Bgp_SetErrStat(BGPENOERR);
|
||||
return NG;
|
||||
}
|
||||
if (n < 0)
|
||||
return Bgp_SetErrStat(BGPEIO);
|
||||
if ((size_t) n != BGP_HDRSIZ)
|
||||
return Bgp_SetErrStat(BGPEIO);
|
||||
|
||||
// Retrieve memory allocator
|
||||
const MemOps *memOps = BGP_MEMOPS(msg);
|
||||
|
||||
// Check header and allocate message
|
||||
Uint16 len = Bgp_CheckMsgHdr(buf, BGP_HDRSIZ, BGP_ISEXMSG(msg->flags));
|
||||
if (len == 0)
|
||||
return NG;
|
||||
|
||||
msg->buf = (Uint8 *) memOps->Alloc(msg->allocp, len, NULL);
|
||||
if (!msg->buf)
|
||||
return Bgp_SetErrStat(BGPENOMEM);
|
||||
|
||||
// Copy over the message
|
||||
memcpy(msg->buf, buf, BGP_HDRSIZ);
|
||||
len -= BGP_HDRSIZ;
|
||||
|
||||
n = ops->Read(streamp, msg->buf + BGP_HDRSIZ, len);
|
||||
if (n < 0) {
|
||||
Bgp_SetErrStat(BGPEIO);
|
||||
goto fail;
|
||||
}
|
||||
if ((size_t) n != len) {
|
||||
Bgp_SetErrStat(BGPEIO);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
BGP_CLRATTRTAB(msg->table);
|
||||
return Bgp_SetErrStat(BGPENOERR);
|
||||
|
||||
fail:
|
||||
memOps->Free(msg->allocp, msg->buf);
|
||||
return NG;
|
||||
}
|
||||
|
||||
#define BGP_OPEN_MINSIZ (BGP_HDRSIZ + 1uLL + 2uLL + 2uLL + 4uLL + 1uLL)
|
||||
|
||||
Bgpopen *Bgp_GetMsgOpen(Bgpmsg *msg)
|
||||
{
|
||||
Bgphdr *hdr = BGP_HDR(msg);
|
||||
if (hdr->type != BGP_OPEN) {
|
||||
Bgp_SetErrStat(BGPEBADTYPE);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
size_t len = beswap16(hdr->len);
|
||||
if (len < BGP_OPEN_MINSIZ) {
|
||||
Bgp_SetErrStat(BGPETRUNCMSG);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Bgpopen *open = (Bgpopen *) hdr;
|
||||
size_t nbytes = BGP_OPEN_MINSIZ + open->parmsLen;
|
||||
if (nbytes != len) {
|
||||
Bgp_SetErrStat((nbytes > len) ? BGPETRUNCMSG : BGPEBADOPENLEN);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Bgp_SetErrStat(BGPENOERR);
|
||||
return open;
|
||||
}
|
||||
|
||||
#define BGP_UPDATE_MINSIZ (BGP_HDRSIZ + 2uLL + 2uLL)
|
||||
|
||||
Bgpupdate *Bgp_GetMsgUpdate(Bgpmsg *msg)
|
||||
{
|
||||
Bgphdr *hdr = BGP_HDR(msg);
|
||||
if (hdr->type != BGP_UPDATE) {
|
||||
Bgp_SetErrStat(BGPEBADTYPE);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Uint16 len = beswap16(hdr->len);
|
||||
if (len < BGP_UPDATE_MINSIZ) {
|
||||
Bgp_SetErrStat(BGPETRUNCMSG);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Bgp_SetErrStat(BGPENOERR);
|
||||
return (Bgpupdate *) hdr;
|
||||
}
|
||||
|
||||
static Boolean Bgp_SwitchMpIterator(Bgpmpiter *it)
|
||||
{
|
||||
if (!it->nextAttr)
|
||||
return FALSE; // no additional attribute to iterate, we're done
|
||||
|
||||
// Switch iterator
|
||||
const Bgpmpfam *family = Bgp_GetMpFamily(it->nextAttr); // sets error
|
||||
if (!family)
|
||||
return FALSE; // corrupted attribute
|
||||
|
||||
size_t nbytes;
|
||||
void *routes = Bgp_GetMpRoutes(it->nextAttr, &nbytes);
|
||||
if (!routes)
|
||||
return FALSE; // corrupted message
|
||||
|
||||
// Begin subsequent iteration
|
||||
Afi afi = family->afi;
|
||||
Safi safi = family->safi;
|
||||
if (Bgp_StartPrefixes(&it->rng, afi, safi, routes, nbytes, it->rng.isAddPath) != OK)
|
||||
return FALSE; // shouldn't happen
|
||||
|
||||
// Switch complete, clear attribute and move on
|
||||
it->nextAttr = NULL;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
Judgement Bgp_StartMsgWithdrawn(Prefixiter *it, Bgpmsg *msg)
|
||||
{
|
||||
Bgpupdate *update = Bgp_GetMsgUpdate(msg);
|
||||
if (!update)
|
||||
return NG;
|
||||
|
||||
Bgpwithdrawnseg *withdrawn = Bgp_GetUpdateWithdrawn(update);
|
||||
if (!withdrawn)
|
||||
return NG;
|
||||
|
||||
return Bgp_StartPrefixes(it, AFI_IP, SAFI_UNICAST,
|
||||
withdrawn->nlri, beswap16(withdrawn->len),
|
||||
BGP_ISADDPATH(msg->flags));
|
||||
}
|
||||
|
||||
Judgement Bgp_StartAllMsgWithdrawn(Bgpmpiter *it, Bgpmsg *msg)
|
||||
{
|
||||
it->nextAttr = Bgp_GetMsgAttribute(msg, BGP_ATTR_MP_UNREACH_NLRI);
|
||||
if (!it->nextAttr && Bgp_GetErrStat(NULL))
|
||||
return NG;
|
||||
if (Bgp_StartMsgWithdrawn(&it->rng, msg) != OK)
|
||||
return NG;
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
void Bgp_InitMpWithdrawn(Bgpmpiter *it,
|
||||
const Bgpwithdrawnseg *withdrawn,
|
||||
const Bgpattr *mpUnreach,
|
||||
Boolean isAddPath)
|
||||
{
|
||||
it->nextAttr = (Bgpattr *) mpUnreach;
|
||||
Bgp_StartPrefixes(&it->rng,
|
||||
AFI_IP, SAFI_UNICAST,
|
||||
withdrawn->nlri, beswap16(withdrawn->len),
|
||||
isAddPath);
|
||||
}
|
||||
|
||||
void Bgp_InitMpNlri(Bgpmpiter *it,
|
||||
const void *nlri,
|
||||
size_t nbytes,
|
||||
const Bgpattr *mpReach,
|
||||
Boolean isAddPath)
|
||||
{
|
||||
it->nextAttr = (Bgpattr *) mpReach;
|
||||
Bgp_StartPrefixes(&it->rng, AFI_IP, SAFI_UNICAST, nlri, nbytes, isAddPath);
|
||||
}
|
||||
|
||||
Judgement Bgp_StartMsgNlri(Prefixiter *it, Bgpmsg *msg)
|
||||
{
|
||||
Bgpupdate *update = Bgp_GetMsgUpdate(msg);
|
||||
if (!update)
|
||||
return NG;
|
||||
|
||||
size_t nbytes;
|
||||
void *nlri = Bgp_GetUpdateNlri(update, &nbytes);
|
||||
if (!nlri)
|
||||
return NG;
|
||||
|
||||
return Bgp_StartPrefixes(it, AFI_IP, SAFI_UNICAST,
|
||||
nlri, nbytes,
|
||||
BGP_ISADDPATH(msg->flags));
|
||||
}
|
||||
|
||||
Judgement Bgp_StartAllMsgNlri(Bgpmpiter *it, Bgpmsg *msg)
|
||||
{
|
||||
it->nextAttr = Bgp_GetMsgAttribute(msg, BGP_ATTR_MP_REACH_NLRI);
|
||||
if (!it->nextAttr && Bgp_GetErrStat(NULL))
|
||||
return NG;
|
||||
if (Bgp_StartMsgNlri(&it->rng, msg) != OK)
|
||||
return NG;
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
Prefix *Bgp_NextMpPrefix(Bgpmpiter *it)
|
||||
{
|
||||
void *rawPfx;
|
||||
do {
|
||||
rawPfx = Bgp_NextPrefix(&it->rng); // sets error
|
||||
if (!rawPfx) {
|
||||
// Swap iterator if necessary and try again
|
||||
if (Bgp_GetErrStat(NULL))
|
||||
return NULL;
|
||||
if (!Bgp_SwitchMpIterator(it))
|
||||
return NULL;
|
||||
}
|
||||
} while (!rawPfx);
|
||||
|
||||
// Extended prefix info
|
||||
Prefix *cur = &it->pfx;
|
||||
cur->afi = it->rng.afi;
|
||||
cur->safi = it->rng.safi;
|
||||
if (it->rng.isAddPath) {
|
||||
// ADD-PATH enabled prefix
|
||||
const ApRawPrefix *pfx = (const ApRawPrefix *) rawPfx;
|
||||
|
||||
cur->isAddPath = TRUE;
|
||||
cur->pathId = pfx->pathId;
|
||||
cur->width = pfx->width;
|
||||
memcpy(cur->bytes, pfx->bytes, PFXLEN(pfx->width));
|
||||
} else {
|
||||
// Regular prefix
|
||||
const RawPrefix *pfx = (const RawPrefix *) rawPfx;
|
||||
|
||||
cur->isAddPath = FALSE;
|
||||
cur->pathId = 0;
|
||||
cur->width = pfx->width;
|
||||
memcpy(cur->bytes, pfx->bytes, PFXLEN(pfx->width));
|
||||
}
|
||||
|
||||
return cur;
|
||||
}
|
||||
|
||||
#define BGP_NOTIFICATION_MINSIZ (BGP_HDRSIZ + 1uLL + 1uLL)
|
||||
|
||||
Bgpnotification *Bgp_GetNotification(Bgpmsg *msg)
|
||||
{
|
||||
Bgphdr *hdr = BGP_HDR(msg);
|
||||
if (hdr->type != BGP_NOTIFICATION) {
|
||||
Bgp_SetErrStat(BGPEBADTYPE);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Uint16 len = beswap16(hdr->len);
|
||||
if (len < BGP_NOTIFICATION_MINSIZ) {
|
||||
Bgp_SetErrStat(BGPETRUNCMSG);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Bgp_SetErrStat(BGPENOERR);
|
||||
return (Bgpnotification *) hdr;
|
||||
}
|
||||
|
||||
Bgpparmseg *Bgp_GetParmsFromMemory(const void *data, size_t size)
|
||||
{
|
||||
const size_t BGP_OPEN_PARMSOFF = BGP_OPEN_MINSIZ - BGP_HDRSIZ;
|
||||
|
||||
if (size < BGP_OPEN_PARMSOFF) {
|
||||
Bgp_SetErrStat(BGPETRUNCMSG);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Bgpparmseg *parms = (Bgpparmseg *) ((Uint8 *) data + BGP_OPEN_PARMSOFF - 1);
|
||||
size_t nbytes = BGP_OPEN_PARMSOFF + parms->len;
|
||||
if (nbytes != size) {
|
||||
Bgp_SetErrStat((nbytes > size) ? BGPETRUNCMSG : BGPEBADOPENLEN);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Bgp_SetErrStat(BGPENOERR);
|
||||
return parms;
|
||||
}
|
||||
|
||||
Bgpparmseg *Bgp_GetOpenParms(const Bgpopen *open)
|
||||
{
|
||||
assert(open->hdr.type == BGP_OPEN);
|
||||
|
||||
size_t len = beswap16(open->hdr.len) - BGP_HDRSIZ;
|
||||
|
||||
return Bgp_GetParmsFromMemory(&open->version, len);
|
||||
}
|
||||
|
||||
Bgpwithdrawnseg *Bgp_GetWithdrawnFromMemory(const void *data, size_t size)
|
||||
{
|
||||
if (size < 2uLL) {
|
||||
Bgp_SetErrStat(BGPETRUNCMSG);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Bgpwithdrawnseg *withdrawn = (Bgpwithdrawnseg *) data;
|
||||
size -= 2;
|
||||
|
||||
if (size < beswap16(withdrawn->len) + 2uLL) { // also accounts for TPA length
|
||||
Bgp_SetErrStat(BGPETRUNCMSG);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Bgp_SetErrStat(BGPENOERR);
|
||||
return withdrawn;
|
||||
}
|
||||
|
||||
Bgpwithdrawnseg *Bgp_GetUpdateWithdrawn(const Bgpupdate *msg)
|
||||
{
|
||||
assert(msg->hdr.type == BGP_UPDATE);
|
||||
|
||||
size_t len = beswap16(msg->hdr.len);
|
||||
|
||||
return Bgp_GetWithdrawnFromMemory(msg->data, len - BGP_HDRSIZ);
|
||||
}
|
||||
|
||||
Bgpattrseg *Bgp_GetAttributesFromMemory(const void *data, size_t size)
|
||||
{
|
||||
Bgpwithdrawnseg *withdrawn = Bgp_GetWithdrawnFromMemory(data, size);
|
||||
if (!withdrawn)
|
||||
return NULL; // sets error
|
||||
|
||||
size_t withdrawnLen = beswap16(withdrawn->len);
|
||||
|
||||
Bgpattrseg *tpa = (Bgpattrseg *) &withdrawn->nlri[withdrawnLen];
|
||||
size_t tpaLen = beswap16(tpa->len);
|
||||
if (size < 2uLL + withdrawnLen + 2uLL + tpaLen) {
|
||||
Bgp_SetErrStat(BGPETRUNCMSG);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return tpa; // error already cleared
|
||||
}
|
||||
|
||||
Bgpattrseg *Bgp_GetUpdateAttributes(const Bgpupdate *msg)
|
||||
{
|
||||
assert(msg->hdr.type == BGP_UPDATE);
|
||||
|
||||
size_t len = beswap16(msg->hdr.len);
|
||||
|
||||
return Bgp_GetAttributesFromMemory(msg->data, len - BGP_HDRSIZ);
|
||||
}
|
||||
|
||||
void *Bgp_GetNlriFromMemory(const void *nlri, size_t size, size_t *nbytes)
|
||||
{
|
||||
Bgpattrseg *tpa = Bgp_GetAttributesFromMemory(nlri, size);
|
||||
if (!tpa)
|
||||
return NULL; // error already set
|
||||
|
||||
size_t tpaLen = beswap16(tpa->len);
|
||||
if (nbytes) {
|
||||
size_t offset = &tpa->attrs[tpaLen] - (Uint8 *) nlri;
|
||||
|
||||
*nbytes = size - offset;
|
||||
}
|
||||
|
||||
return &tpa->attrs[tpaLen];
|
||||
}
|
||||
|
||||
void *Bgp_GetUpdateNlri(const Bgpupdate *msg, size_t *nbytes)
|
||||
{
|
||||
assert(msg->hdr.type == BGP_UPDATE);
|
||||
|
||||
size_t len = beswap16(msg->hdr.len);
|
||||
|
||||
return Bgp_GetNlriFromMemory(msg->data, len - BGP_HDRSIZ, nbytes);
|
||||
}
|
||||
|
||||
void Bgp_ClearMsg(Bgpmsg *msg)
|
||||
{
|
||||
if (!BGP_ISUNOWNED(msg->flags))
|
||||
BGP_MEMOPS(msg)->Free(msg->allocp, msg->buf);
|
||||
|
||||
msg->flags = 0;
|
||||
msg->buf = NULL;
|
||||
}
|
45
lonetix/bgp/bgp_local.h
Normal file
45
lonetix/bgp/bgp_local.h
Normal file
@@ -0,0 +1,45 @@
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
|
||||
/**
|
||||
* \file bgp/bgp_local.h
|
||||
*
|
||||
* Private BGP library header.
|
||||
*
|
||||
* \copyright The DoubleFourteen Code Forge (C) All Rights Reserved
|
||||
* \author Lorenzo Cogotti
|
||||
*/
|
||||
|
||||
#ifndef DF_BGP_LOCAL_H_
|
||||
#define DF_BGP_LOCAL_H_
|
||||
|
||||
#include "bgp/mrt.h"
|
||||
|
||||
// Low level prefix operations
|
||||
|
||||
void Bgp_InitMpWithdrawn(Bgpmpiter *it, const Bgpwithdrawnseg *withdrawn, const Bgpattr *mpUnreach, Boolean isAddPath);
|
||||
void Bgp_InitMpNlri(Bgpmpiter *it, const void *data, size_t nbytes, const Bgpattr *mpReach, Boolean isAddPath);
|
||||
|
||||
// Low level BGP operations
|
||||
|
||||
Uint16 Bgp_CheckMsgHdr(const void *data, size_t nbytes, Boolean allowExtendedSize);
|
||||
|
||||
Bgpparmseg *Bgp_GetParmsFromMemory(const void *data, size_t size);
|
||||
Bgpwithdrawnseg *Bgp_GetWithdrawnFromMemory(const void *data, size_t size);
|
||||
Bgpattrseg *Bgp_GetAttributesFromMemory(const void *data, size_t size);
|
||||
void *Bgp_GetNlriFromMemory(const void *nlri, size_t size, size_t *nbytes);
|
||||
|
||||
// Extension in attribute.c special iteration on attributes
|
||||
|
||||
/// Non-caching variant of `Bgp_NextAttribute()`, doesn't update `it->table`.
|
||||
Bgpattr *Bgp_NcNextAttribute(Bgpattriter *it);
|
||||
|
||||
#define Bgp_SetErrStat(code) \
|
||||
_Bgp_SetErrStat(code, __FILE__, __func__, __LINE__, 0)
|
||||
|
||||
NOINLINE Judgement _Bgp_SetErrStat(BgpRet code,
|
||||
const char *filename,
|
||||
const char *func,
|
||||
unsigned long long line,
|
||||
unsigned depth);
|
||||
|
||||
#endif
|
177
lonetix/bgp/bytebuf.c
Normal file
177
lonetix/bgp/bytebuf.c
Normal file
@@ -0,0 +1,177 @@
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
|
||||
/**
|
||||
* \file bgp/bytebuf.c
|
||||
*
|
||||
* Trivial BGP memory allocator for basic BGP workloads.
|
||||
*
|
||||
* \copyright The DoubleFourteen Code Forge (C) All Rights Reserved
|
||||
* \author Lorenzo Cogotti
|
||||
*/
|
||||
|
||||
#include "bgp/bytebuf.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
STATIC_ASSERT(BGP_MEMBUF_ALIGN >= 4, "bytebuf.c assumes Uint32 header");
|
||||
|
||||
#define USEDBIT BIT(0)
|
||||
#define MAXBUFCHUNKSIZ 0xffffffffuLL
|
||||
|
||||
#define BLKSIZ(ptr) ((*(Uint32 *) (ptr)) & ~USEDBIT)
|
||||
#define ISUSED(ptr) (((*(Uint32 *) (ptr)) & USEDBIT) != 0)
|
||||
#define SETUSED(ptr) ((void) ((*(Uint32 *) (ptr)) |= USEDBIT))
|
||||
#define CLRUSED(ptr) ((void) ((*(Uint32 *) (ptr)) &= ~USEDBIT))
|
||||
|
||||
static Boolean Mem_IsInBuffer(Bgpbytebuf *buf, void *ptr)
|
||||
{
|
||||
return (Uint8 *) ptr >= buf->base &&
|
||||
(Uint8 *) ptr < buf->base + buf->size;
|
||||
}
|
||||
|
||||
static Uint8 *Mem_FindPrevChunk(Bgpbytebuf *buf, void *chunk)
|
||||
{
|
||||
assert(Mem_IsInBuffer(buf, chunk));
|
||||
|
||||
Uint8 *p = buf->base;
|
||||
while (p < (Uint8 *) chunk) {
|
||||
size_t siz = BLKSIZ(p);
|
||||
|
||||
if (p + siz == (Uint8 *) chunk)
|
||||
return p;
|
||||
|
||||
p += siz;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void Mem_BgpFree(void *allocator, void *ptr)
|
||||
{
|
||||
Bgpbytebuf *buf = (Bgpbytebuf *) allocator;
|
||||
|
||||
// Regular free() for out of buffer allocations
|
||||
if (!Mem_IsInBuffer(buf, ptr)) {
|
||||
free(ptr);
|
||||
return;
|
||||
}
|
||||
|
||||
// Get pointer to chunk
|
||||
Uint8 *p = (Uint8 *) ptr - 4;
|
||||
assert(ISUSED(p));
|
||||
// Get buffer limit
|
||||
Uint8 *lim = buf->base + buf->pos;
|
||||
|
||||
Uint32 siz = BLKSIZ(p);
|
||||
CLRUSED(p); // toggle off USEDBIT
|
||||
|
||||
// Find successor if any
|
||||
Uint8 *next = p + siz;
|
||||
if (next < lim && !ISUSED(next)) {
|
||||
// Merge forward
|
||||
siz += BLKSIZ(next);
|
||||
|
||||
*(Uint32 *) p = siz;
|
||||
}
|
||||
|
||||
// Find predecessor, if any
|
||||
Uint8 *prev = Mem_FindPrevChunk(buf, p);
|
||||
if (prev && !ISUSED(prev)) {
|
||||
// Merge backwards
|
||||
siz += BLKSIZ(prev);
|
||||
p = prev;
|
||||
|
||||
*(Uint32 *) p = siz;
|
||||
}
|
||||
|
||||
// Move position backwards when freeing last block
|
||||
if (p + siz == lim)
|
||||
buf->pos -= siz;
|
||||
}
|
||||
|
||||
static void *Mem_BgpDoRealloc(Bgpbytebuf *buf, void *oldp, size_t nbytes)
|
||||
{
|
||||
// Use plain realloc() if we're not managing a buffered pointer
|
||||
if (!Mem_IsInBuffer(buf, oldp))
|
||||
return realloc(oldp, nbytes);
|
||||
|
||||
assert(IS_PTR_ALIGNED(oldp, BGP_MEMBUF_ALIGN));
|
||||
|
||||
Uint8 *ptr = (Uint8 *) oldp - 4;
|
||||
assert(ISUSED(ptr));
|
||||
|
||||
Uint32 oldSiz = BLKSIZ(ptr);
|
||||
assert(buf->pos >= oldSiz);
|
||||
|
||||
size_t siz = 4 + ALIGN(nbytes, BGP_MEMBUF_ALIGN);
|
||||
if (oldSiz >= siz) {
|
||||
// Shrink operation, free up the trailing part if we're the last chunk
|
||||
if (ptr + oldSiz == buf->base + buf->pos) {
|
||||
*(Uint32 *) ptr = siz | USEDBIT;
|
||||
buf->pos -= (oldSiz - siz);
|
||||
}
|
||||
|
||||
return oldp;
|
||||
}
|
||||
|
||||
// May only grow a chunk if this is the last one and we don't overflow
|
||||
if (ptr + oldSiz != buf->base + buf->pos)
|
||||
return NULL;
|
||||
|
||||
size_t newPos = buf->pos + (siz - oldSiz);
|
||||
if (newPos > buf->size)
|
||||
return NULL;
|
||||
|
||||
// Ok to grow the chunk
|
||||
*(Uint32 *) ptr = siz | USEDBIT;
|
||||
buf->pos = newPos;
|
||||
return oldp;
|
||||
}
|
||||
|
||||
static void *Mem_BgpDoAlloc(Bgpbytebuf *buf, size_t nbytes)
|
||||
{
|
||||
// Use plain malloc() for large allocations or when out of buffer space
|
||||
size_t siz = 4 + ALIGN(nbytes, BGP_MEMBUF_ALIGN);
|
||||
if (buf->pos + siz > buf->size || siz > MAXBUFCHUNKSIZ)
|
||||
return malloc(nbytes);
|
||||
|
||||
// Return the next chunk available
|
||||
Uint32 *ptr = (Uint32 *) (buf->base + buf->pos);
|
||||
buf->pos += siz;
|
||||
assert((siz & USEDBIT) == 0);
|
||||
|
||||
*ptr++ = siz | USEDBIT;
|
||||
return ptr;
|
||||
}
|
||||
|
||||
static void *Mem_BgpAlloc(void *allocator, size_t nbytes, void *oldp)
|
||||
{
|
||||
Bgpbytebuf *buf = (Bgpbytebuf *) allocator;
|
||||
|
||||
// Handle common allocations with no `oldp`
|
||||
if (!oldp)
|
||||
return Mem_BgpDoAlloc(buf, nbytes);
|
||||
|
||||
// Attempt memory reuse
|
||||
void *ptr = Mem_BgpDoRealloc(buf, oldp, nbytes);
|
||||
if (ptr)
|
||||
return ptr;
|
||||
|
||||
// Fallback to simple allocation+memcpy()
|
||||
ptr = Mem_BgpDoAlloc(buf, nbytes);
|
||||
if (ptr) {
|
||||
memcpy(ptr, oldp, nbytes);
|
||||
Mem_BgpFree(buf, oldp);
|
||||
}
|
||||
|
||||
return ptr;
|
||||
}
|
||||
|
||||
static const MemOps mem_bgpBufTable = {
|
||||
Mem_BgpAlloc,
|
||||
Mem_BgpFree
|
||||
};
|
||||
|
||||
const MemOps *const Mem_BgpBufOps = &mem_bgpBufTable;
|
82
lonetix/bgp/dump.c
Normal file
82
lonetix/bgp/dump.c
Normal file
@@ -0,0 +1,82 @@
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
|
||||
/**
|
||||
* \file bgp/dump.c
|
||||
*
|
||||
* General BGP dump functions wrappers.
|
||||
*
|
||||
* \copyright The DoubleFourteen Code Forge (C) All Rights Reserved
|
||||
* \author Lorenzo Cogotti
|
||||
*/
|
||||
|
||||
#include "bgp/bgp_local.h"
|
||||
#include "bgp/dump.h"
|
||||
#include "sys/endian.h"
|
||||
|
||||
#define CALLFMT(fn, ...) \
|
||||
((fn) ? (fn(__VA_ARGS__)) : ((Sint64) Bgp_SetErrStat(BGPENOERR)))
|
||||
|
||||
Sint64 Bgp_DumpMrtUpdate(const Mrthdr *hdr,
|
||||
void *streamp, const StmOps *ops,
|
||||
const BgpDumpfmt *fmt)
|
||||
{
|
||||
Bgpattrtab table;
|
||||
|
||||
if (!ops->Write) {
|
||||
Bgp_SetErrStat(BGPENOERR);
|
||||
return 0;
|
||||
}
|
||||
|
||||
BGP_CLRATTRTAB(table);
|
||||
if (MRT_ISBGP4MP(hdr->type)) {
|
||||
return CALLFMT(fmt->DumpBgp4mp, hdr, streamp, ops, table);
|
||||
|
||||
} else if (hdr->type == MRT_BGP) {
|
||||
return CALLFMT(fmt->DumpZebra, hdr, streamp, ops, table);
|
||||
|
||||
} else {
|
||||
Bgp_SetErrStat(BGPEBADMRTTYPE);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
Sint64 Bgp_DumpMrtRibv2(const Mrthdr *hdr,
|
||||
const Mrtpeerentv2 *peer, const Mrtribentv2 *ent,
|
||||
void *streamp, const StmOps *ops,
|
||||
const BgpDumpfmt *fmt)
|
||||
{
|
||||
Bgpattrtab table;
|
||||
|
||||
if (!ops->Write) {
|
||||
Bgp_SetErrStat(BGPENOERR);
|
||||
return 0;
|
||||
}
|
||||
if (hdr->type != MRT_TABLE_DUMPV2 || !TABLE_DUMPV2_ISRIB(hdr->subtype)) {
|
||||
Bgp_SetErrStat(BGPEBADMRTTYPE);
|
||||
return -1;
|
||||
}
|
||||
|
||||
BGP_CLRATTRTAB(table);
|
||||
return CALLFMT(fmt->DumpRibv2, hdr, peer, ent, streamp, ops, table);
|
||||
}
|
||||
|
||||
Sint64 Bgp_DumpMrtRib(const Mrthdr *hdr,
|
||||
const Mrtribent *ent,
|
||||
void *streamp, const StmOps *ops,
|
||||
const BgpDumpfmt *fmt)
|
||||
{
|
||||
Bgpattrtab table;
|
||||
|
||||
if (!ops->Write) {
|
||||
Bgp_SetErrStat(BGPENOERR);
|
||||
return 0;
|
||||
}
|
||||
if (hdr->type != MRT_TABLE_DUMP) {
|
||||
Bgp_SetErrStat(BGPEBADMRTTYPE);
|
||||
return -1;
|
||||
}
|
||||
|
||||
BGP_CLRATTRTAB(table);
|
||||
return CALLFMT(fmt->DumpRib, hdr, ent, streamp, ops, table);
|
||||
}
|
||||
|
1085
lonetix/bgp/dump_isolario.c
Normal file
1085
lonetix/bgp/dump_isolario.c
Normal file
File diff suppressed because it is too large
Load Diff
751
lonetix/bgp/mrt.c
Normal file
751
lonetix/bgp/mrt.c
Normal file
@@ -0,0 +1,751 @@
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
|
||||
/**
|
||||
* \file bgp/mrt.c
|
||||
*
|
||||
* Deals with Multi-Threaded Routing Toolkit (MRT) format.
|
||||
*
|
||||
* \copyright The DoubleFourteen Code Forge (C) All Rights Reserved
|
||||
* \author Lorenzo Cogotti
|
||||
*/
|
||||
|
||||
#include "bgp/bgp_local.h"
|
||||
#include "sys/endian.h"
|
||||
#include "sys/interlocked.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
|
||||
static void *MRT_DATAPTR(const Mrtrecord *rec)
|
||||
{
|
||||
return rec->buf + MRT_HDRSIZ + (MRT_ISEXHDRTYPE(MRT_HDR(rec)->type) << 2);
|
||||
}
|
||||
|
||||
Judgement Bgp_MrtFromBuf(Mrtrecord *rec, const void *buf, size_t nbytes)
|
||||
{
|
||||
if (nbytes < MRT_HDRSIZ)
|
||||
return Bgp_SetErrStat(BGPETRUNCMRT);
|
||||
|
||||
const Mrthdr *hdr = (const Mrthdr *) buf;
|
||||
|
||||
size_t left = beswap32(hdr->len);
|
||||
if (MRT_ISEXHDRTYPE(hdr->type))
|
||||
left += 4; // account for extended timestamp
|
||||
|
||||
size_t siz = sizeof(*hdr) + left;
|
||||
if (siz > nbytes)
|
||||
return Bgp_SetErrStat(BGPETRUNCMRT);
|
||||
|
||||
const MemOps *memOps = MRT_MEMOPS(rec);
|
||||
|
||||
rec->buf = (Uint8 *) memOps->Alloc(rec->allocp, siz, NULL);
|
||||
if (!rec->buf)
|
||||
return Bgp_SetErrStat(BGPENOMEM);
|
||||
|
||||
rec->peerOffTab = NULL;
|
||||
memcpy(rec->buf, buf, siz);
|
||||
return Bgp_SetErrStat(BGPENOERR);
|
||||
}
|
||||
|
||||
Judgement Bgp_ReadMrt(Mrtrecord *rec, void *streamp, const StmOps *ops)
|
||||
{
|
||||
Mrthdr hdr;
|
||||
|
||||
// Read header
|
||||
Sint64 n = ops->Read(streamp, &hdr, sizeof(hdr));
|
||||
if (n == 0) {
|
||||
// Precisely at end of file, no error, just no more records
|
||||
Bgp_SetErrStat(BGPENOERR);
|
||||
return NG;
|
||||
}
|
||||
if (n < 0)
|
||||
return Bgp_SetErrStat(BGPEIO);
|
||||
if ((size_t) n != sizeof(hdr))
|
||||
return Bgp_SetErrStat(BGPEIO);
|
||||
|
||||
size_t left = beswap32(hdr.len);
|
||||
if (MRT_ISEXHDRTYPE(hdr.type))
|
||||
left += 4; // account for extended timestamp
|
||||
|
||||
// Allocate buffer
|
||||
// NOTE: MRT header length doesn't account for header size itself
|
||||
size_t siz = sizeof(hdr) + left;
|
||||
const MemOps *memOps = MRT_MEMOPS(rec);
|
||||
|
||||
rec->buf = (Uint8 *) memOps->Alloc(rec->allocp, siz, NULL);
|
||||
if (!rec->buf)
|
||||
return Bgp_SetErrStat(BGPENOMEM);
|
||||
|
||||
// Populate buffer
|
||||
memcpy(rec->buf, &hdr, sizeof(hdr));
|
||||
n = ops->Read(streamp, rec->buf + sizeof(hdr), left);
|
||||
if (n < 0) {
|
||||
Bgp_SetErrStat(BGPEIO);
|
||||
goto fail;
|
||||
}
|
||||
if ((size_t) n != left) {
|
||||
Bgp_SetErrStat(BGPEIO);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
rec->peerOffTab = NULL;
|
||||
return Bgp_SetErrStat(BGPENOERR);
|
||||
|
||||
fail:
|
||||
memOps->Free(rec->allocp, rec->buf);
|
||||
return NG;
|
||||
}
|
||||
|
||||
#define MRT_PEERIDX_MINSIZ (4 + 2 + 2)
|
||||
|
||||
Mrtpeeridx *Bgp_GetMrtPeerIndex(Mrtrecord *rec)
|
||||
{
|
||||
const Mrthdr *hdr = MRT_HDR(rec);
|
||||
if (hdr->type != MRT_TABLE_DUMPV2 || hdr->subtype != TABLE_DUMPV2_PEER_INDEX_TABLE) {
|
||||
Bgp_SetErrStat(BGPEBADMRTTYPE);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Basic size check (only for fixed size portion)
|
||||
size_t len = beswap32(hdr->len);
|
||||
size_t siz = MRT_PEERIDX_MINSIZ;
|
||||
if (len < siz) {
|
||||
Bgp_SetErrStat(BGPETRUNCMRT);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// NOTE: PEER_INDEX_TABLE cannot have extended timestamp
|
||||
assert(!MRT_ISEXHDRTYPE(hdr->subtype));
|
||||
Mrtpeeridx *peerIdx = (Mrtpeeridx *) (hdr + 1);
|
||||
|
||||
// View Name field size check
|
||||
siz += beswap16(peerIdx->viewNameLen);
|
||||
if (len < siz) {
|
||||
Bgp_SetErrStat(BGPETRUNCMRT);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Bgp_SetErrStat(BGPENOERR);
|
||||
return peerIdx;
|
||||
}
|
||||
|
||||
void *Bgp_GetMrtPeerIndexPeers(Mrtrecord *rec, size_t *peersCount, size_t *nbytes)
|
||||
{
|
||||
const Mrthdr *hdr = MRT_HDR(rec);
|
||||
if (hdr->type != MRT_TABLE_DUMPV2 || hdr->subtype != TABLE_DUMPV2_PEER_INDEX_TABLE) {
|
||||
Bgp_SetErrStat(BGPEBADMRTTYPE);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Basic size check
|
||||
size_t len = beswap32(hdr->len);
|
||||
size_t off = 4; // BGP Identifier
|
||||
if (len < off + 2) { // view name length
|
||||
Bgp_SetErrStat(BGPETRUNCMRT);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// NOTE: PEER_INDEX_TABLE cannot have extended timestamp
|
||||
assert(!MRT_ISEXHDRTYPE(hdr->subtype));
|
||||
Mrtpeeridx *peerIdx = (Mrtpeeridx *) (hdr + 1);
|
||||
|
||||
// View Name size check
|
||||
off += 2 + beswap16(peerIdx->viewNameLen); // skip view name
|
||||
if (len < off + 2) { // entry count
|
||||
Bgp_SetErrStat(BGPETRUNCMRT);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Bgp_SetErrStat(BGPENOERR);
|
||||
|
||||
// Calculate relevant sizes and return peers chunk
|
||||
if (peersCount) {
|
||||
Uint16 count;
|
||||
|
||||
memcpy(&count, (Uint8 *) peerIdx + off, sizeof(count));
|
||||
*peersCount = beswap16(count);
|
||||
}
|
||||
|
||||
off += 2; // skip peers count
|
||||
|
||||
if (nbytes)
|
||||
*nbytes = len - off;
|
||||
|
||||
return (Uint8 *) peerIdx + off;
|
||||
}
|
||||
|
||||
static size_t MRT_PEERENTSIZ(const Mrtpeerentv2 *ent)
|
||||
{
|
||||
size_t len = 1 + 4;
|
||||
len += MRT_ISPEERASN32BIT(ent->type) ? 4 : 2;
|
||||
len += MRT_ISPEERIPV6(ent->type) ? IPV6_SIZE : IPV4_SIZE;
|
||||
return len;
|
||||
}
|
||||
|
||||
static Mrtpeertabv2 *Bgp_GetPeerOffsetTable(Mrtrecord *rec)
|
||||
{
|
||||
Mrtpeertabv2 *tab;
|
||||
while (TRUE) {
|
||||
tab = (Mrtpeertabv2 *) Smp_AtomicLoadPtrAcq(&rec->peerOffTab);
|
||||
if (tab)
|
||||
break; // already allocated
|
||||
|
||||
// Must allocate the table anew
|
||||
size_t peerCount;
|
||||
if (!Bgp_GetMrtPeerIndexPeers(rec, &peerCount, /*nbytes=*/NULL))
|
||||
return NULL; // bad record type or corrupted PEER_INDEX_TABLE
|
||||
|
||||
const MemOps *memOps = MRT_MEMOPS(rec);
|
||||
|
||||
tab = (Mrtpeertabv2 *) memOps->Alloc(rec->allocp, offsetof(Mrtpeertabv2, offsets[peerCount]), NULL);
|
||||
if (!tab) {
|
||||
Bgp_SetErrStat(BGPENOMEM);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Smp_AtomicStore16Rx(&tab->validCount, 0);
|
||||
Smp_AtomicStore16Rx(&tab->peerCount, peerCount);
|
||||
if (Smp_AtomicCasPtrRel(&rec->peerOffTab, NULL, tab))
|
||||
break; // all good
|
||||
|
||||
memOps->Free(rec->allocp, tab); // ...somebody just allocated the table for us
|
||||
}
|
||||
|
||||
return tab;
|
||||
}
|
||||
|
||||
Mrtpeerentv2 *Bgp_GetMrtPeerByIndex(Mrtrecord *rec, Uint16 idx)
|
||||
{
|
||||
// NOTE: no extended timestamp TABLE_DUMPV2 exists, so we can simplify
|
||||
// record access
|
||||
|
||||
Mrtpeertabv2 *tab = Bgp_GetPeerOffsetTable(rec);
|
||||
if (!tab)
|
||||
return NULL;
|
||||
|
||||
// If we have a `Mrtpeertabv2` we're positively sure that `Mrtpeeridx`
|
||||
// is well formed.
|
||||
const Mrthdr *hdr = MRT_HDR(rec);
|
||||
const Mrtpeeridx *peerIdx = (const Mrtpeeridx *) (hdr + 1);
|
||||
|
||||
size_t baseOff = MRT_HDRSIZ + MRT_PEERIDX_MINSIZ + beswap16(peerIdx->viewNameLen);
|
||||
|
||||
// IMPORTANT INVARIANT: `tab->validCount` may change, but will only ever be
|
||||
// incremented.
|
||||
|
||||
Uint16 validCount = Smp_AtomicLoad16Acq(&tab->validCount);
|
||||
if (idx < validCount) {
|
||||
// FAST PATH: Offset was cached
|
||||
Uint32 off = Smp_AtomicLoad32Rx(&tab->offsets[idx]);
|
||||
|
||||
Bgp_SetErrStat(BGPENOERR);
|
||||
return (Mrtpeerentv2 *) (rec->buf + baseOff + off);
|
||||
}
|
||||
|
||||
// SLOW PATH: Must scan PEER_INDEX_TABLE and update offsets
|
||||
|
||||
// Check that a valid peer was actually requested
|
||||
Uint16 peerCount = Smp_AtomicLoad16Rx(&tab->peerCount);
|
||||
if (idx >= peerCount) {
|
||||
Bgp_SetErrStat(BGPEBADPEERIDX);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* NOTE: We cheat a bit:
|
||||
* - if we have a `peerOffTab`, then we know PEER_INDEX_TABLE header is
|
||||
* well formed, so we can build a `Mrtpeeriterv2` confidently
|
||||
* without checking data integrity;
|
||||
* - we know the data was well formed at least up to the last valid
|
||||
* peer entry, so we can resume iteration there;
|
||||
*/
|
||||
|
||||
Mrtpeeriterv2 it;
|
||||
|
||||
Mrtpeerentv2 *ent;
|
||||
Uint32 lastOff;
|
||||
|
||||
// Initialize iterator to last known offset
|
||||
if (validCount == 0)
|
||||
lastOff = 0;
|
||||
|
||||
else {
|
||||
lastOff = Smp_AtomicLoad32Rx(&tab->offsets[validCount - 1]);
|
||||
ent = (Mrtpeerentv2 *) (rec->buf + baseOff + lastOff);
|
||||
|
||||
lastOff += MRT_PEERENTSIZ(ent);
|
||||
}
|
||||
|
||||
it.base = rec->buf + baseOff;
|
||||
it.lim = rec->buf + MRT_HDRSIZ + beswap32(hdr->len);
|
||||
it.ptr = it.base + lastOff;
|
||||
|
||||
it.peerCount = peerCount;
|
||||
it.nextIdx = validCount;
|
||||
|
||||
// Keep iterating to find the new entry, update table in the process
|
||||
|
||||
/* NOTE: We don't care if we concurrently write offsets to the table
|
||||
* while some other thread also updates that, we know we'll be writing
|
||||
* the same offsets in the same slots there.
|
||||
*/
|
||||
|
||||
Uint16 newValidCount = validCount;
|
||||
do {
|
||||
ent = Bgp_NextMrtPeerv2(&it);
|
||||
if (!ent)
|
||||
return NULL; // error status already set by iterator
|
||||
|
||||
Uint32 off = (Uint8 *) ent - it.base;
|
||||
Smp_AtomicStore32Rx(&tab->offsets[newValidCount], off);
|
||||
|
||||
newValidCount++;
|
||||
} while (idx >= newValidCount);
|
||||
|
||||
// Signal what we've done to the world, don't update anything
|
||||
// if somebody else changed the table under our feet.
|
||||
Smp_AtomicCas16Rel(&tab->validCount, validCount, newValidCount);
|
||||
|
||||
return ent; // success status already set by iterator
|
||||
}
|
||||
|
||||
Judgement Bgp_StartMrtPeersv2(Mrtpeeriterv2 *it, Mrtrecord *rec)
|
||||
{
|
||||
size_t peerCount, nbytes;
|
||||
void *peers = (Uint8 *) Bgp_GetMrtPeerIndexPeers(rec, &peerCount, &nbytes);
|
||||
if (!peers)
|
||||
return NG;
|
||||
|
||||
it->base = (Uint8 *) peers;
|
||||
it->lim = it->base + nbytes;
|
||||
it->ptr = it->base;
|
||||
|
||||
it->peerCount = peerCount;
|
||||
it->nextIdx = 0;
|
||||
return OK; // success already set
|
||||
}
|
||||
|
||||
Mrtpeerentv2 *Bgp_NextMrtPeerv2(Mrtpeeriterv2 *it)
|
||||
{
|
||||
if (it->ptr >= it->lim) {
|
||||
// End of iteration, check for correct peer count
|
||||
if (it->nextIdx == it->peerCount)
|
||||
Bgp_SetErrStat(BGPENOERR);
|
||||
else
|
||||
Bgp_SetErrStat(BGPEBADPEERIDXCNT);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
size_t left = it->lim - it->ptr;
|
||||
assert(left > 0);
|
||||
|
||||
Mrtpeerentv2 *ent = (Mrtpeerentv2 *) it->ptr;
|
||||
|
||||
size_t len = MRT_PEERENTSIZ(ent);
|
||||
if (left < len) {
|
||||
Bgp_SetErrStat(BGPETRUNCPEERV2);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Bgp_SetErrStat(BGPENOERR);
|
||||
it->ptr += len;
|
||||
it->nextIdx++;
|
||||
return ent;
|
||||
}
|
||||
|
||||
Mrtribhdrv2 *Bgp_GetMrtRibHdrv2(Mrtrecord *rec, size_t *nbytes)
|
||||
{
|
||||
const Mrthdr *hdr = MRT_HDR(rec);
|
||||
if (hdr->type != MRT_TABLE_DUMPV2) {
|
||||
Bgp_SetErrStat(BGPEBADMRTTYPE);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Mrtribhdrv2 *rib = RIBV2_HDR(hdr);
|
||||
size_t len = beswap32(hdr->len);
|
||||
size_t offset = 0;
|
||||
|
||||
offset += 4; // sequence number
|
||||
|
||||
size_t maxPfxWidth;
|
||||
if (TABLE_DUMPV2_ISGENERICRIB(hdr->subtype)) {
|
||||
offset += 2 + 1; // AFI, SAFI
|
||||
if (len < offset) {
|
||||
Bgp_SetErrStat(BGPETRUNCMRT);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
switch (rib->gen.afi) {
|
||||
case AFI_IP: maxPfxWidth = IPV4_WIDTH; break;
|
||||
case AFI_IP6: maxPfxWidth = IPV6_WIDTH; break;
|
||||
default:
|
||||
Bgp_SetErrStat(BGPEAFIUNSUP);
|
||||
return NULL;
|
||||
}
|
||||
if (rib->gen.safi != SAFI_UNICAST && rib->gen.safi != SAFI_MULTICAST) {
|
||||
Bgp_SetErrStat(BGPESAFIUNSUP);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
} else if (TABLE_DUMPV2_ISIPV4RIB(hdr->subtype)) {
|
||||
maxPfxWidth = IPV4_WIDTH;
|
||||
} else if (TABLE_DUMPV2_ISIPV6RIB(hdr->subtype)) {
|
||||
maxPfxWidth = IPV6_WIDTH;
|
||||
} else {
|
||||
Bgp_SetErrStat(BGPEBADMRTTYPE);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const RawPrefix *pfx = (const RawPrefix *) ((Uint8 *) rib + offset);
|
||||
|
||||
offset++; // prefix width
|
||||
if (len < offset) {
|
||||
Bgp_SetErrStat(BGPETRUNCMRT);
|
||||
return NULL;
|
||||
}
|
||||
if (pfx->width > maxPfxWidth) {
|
||||
Bgp_SetErrStat(BGPEBADPFXWIDTH);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
offset += PFXLEN(pfx->width);
|
||||
if (len < offset) {
|
||||
Bgp_SetErrStat(BGPETRUNCMRT);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Bgp_SetErrStat(BGPENOERR);
|
||||
if (nbytes)
|
||||
*nbytes = offset;
|
||||
|
||||
return rib;
|
||||
}
|
||||
|
||||
Mrtribentriesv2 *Bgp_GetMrtRibEntriesv2(Mrtrecord *rec, size_t *nbytes)
|
||||
{
|
||||
const Mrthdr *hdr = MRT_HDR(rec);
|
||||
if (hdr->type != MRT_TABLE_DUMPV2) {
|
||||
Bgp_SetErrStat(BGPEBADMRTTYPE);
|
||||
return NULL;
|
||||
}
|
||||
if (!TABLE_DUMPV2_ISRIB(hdr->subtype)) {
|
||||
Bgp_SetErrStat(BGPEBADMRTTYPE);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
size_t len = beswap32(hdr->len);
|
||||
|
||||
size_t offset;
|
||||
Mrtribhdrv2 *rib = Bgp_GetMrtRibHdrv2(rec, &offset);
|
||||
if (!rib)
|
||||
return NULL; // error already set
|
||||
|
||||
Mrtribentriesv2 *ents = (Mrtribentriesv2 *) ((Uint8 *) rib + offset);
|
||||
|
||||
offset += 2; // entries count
|
||||
if (len < offset) {
|
||||
Bgp_SetErrStat(BGPETRUNCMRT);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (nbytes)
|
||||
*nbytes = len - offset;
|
||||
|
||||
return ents;
|
||||
}
|
||||
|
||||
Judgement Bgp_StartMrtRibEntriesv2(Mrtribiterv2 *it, Mrtrecord *rec)
|
||||
{
|
||||
size_t nbytes;
|
||||
Mrtribentriesv2 *ents = Bgp_GetMrtRibEntriesv2(rec, &nbytes);
|
||||
if (!ents)
|
||||
return NG;
|
||||
|
||||
it->base = ents->entries;
|
||||
it->lim = it->base + nbytes;
|
||||
it->ptr = it->base;
|
||||
|
||||
it->isAddPath = TABLE_DUMPV2_ISADDPATHRIB(MRT_HDR(rec)->subtype);
|
||||
it->entryCount = beswap16(ents->entryCount);
|
||||
it->nextIdx = 0;
|
||||
return OK;
|
||||
}
|
||||
|
||||
Mrtribentv2 *Bgp_NextRibEntryv2(Mrtribiterv2 *it)
|
||||
{
|
||||
if (it->ptr >= it->lim) {
|
||||
if (it->nextIdx == it->entryCount)
|
||||
Bgp_SetErrStat(BGPENOERR);
|
||||
else
|
||||
Bgp_SetErrStat(BGPEBADRIBV2CNT);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
size_t left = it->lim - it->ptr;
|
||||
assert(left > 0);
|
||||
|
||||
Mrtribentv2 *ent = (Mrtribentv2 *) it->ptr;
|
||||
|
||||
size_t offset = 2 + 4; // peer index, originated time
|
||||
if (it->isAddPath)
|
||||
offset += 4; // path id
|
||||
|
||||
if (left < offset + 2) { // attributes length
|
||||
Bgp_SetErrStat(BGPETRUNCRIBV2);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Bgpattrseg *tpa = (Bgpattrseg *) ((Uint8 *) ent + offset);
|
||||
offset += 2 + beswap16(tpa->len);
|
||||
if (left < offset) {
|
||||
Bgp_SetErrStat(BGPETRUNCRIBV2);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Bgp_SetErrStat(BGPENOERR);
|
||||
it->ptr += offset;
|
||||
it->nextIdx++;
|
||||
return ent;
|
||||
}
|
||||
|
||||
Bgp4mphdr *Bgp_GetBgp4mpHdr(Mrtrecord *rec, size_t *nbytes)
|
||||
{
|
||||
const Mrthdr *hdr = MRT_HDR(rec);
|
||||
if (!MRT_ISBGP4MP(hdr->type)) {
|
||||
Bgp_SetErrStat(BGPEBADMRTTYPE);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
size_t len = beswap32(hdr->len);
|
||||
size_t offset = 0;
|
||||
Afi afi;
|
||||
|
||||
Bgp4mphdr *bgp4mp = (Bgp4mphdr *) MRT_DATAPTR(rec);
|
||||
if (BGP4MP_ISASN32BIT(hdr->subtype)) {
|
||||
offset += 2 * 4;
|
||||
offset += 2 + 2;
|
||||
if (len < offset) {
|
||||
Bgp_SetErrStat(BGPETRUNCMRT);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
afi = bgp4mp->a32.afi;
|
||||
} else {
|
||||
offset += 2 * 2;
|
||||
offset += 2 + 2;
|
||||
if (len < offset) {
|
||||
Bgp_SetErrStat(BGPETRUNCMRT);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
afi = bgp4mp->a16.afi;
|
||||
}
|
||||
|
||||
switch (afi) {
|
||||
case AFI_IP: offset += 2 * IPV4_SIZE; break;
|
||||
case AFI_IP6: offset += 2 * IPV6_SIZE; break;
|
||||
default:
|
||||
Bgp_SetErrStat(BGPEAFIUNSUP);
|
||||
return NULL;
|
||||
}
|
||||
if (BGP4MP_ISSTATECHANGE(hdr->subtype))
|
||||
offset += 2 * 2;
|
||||
|
||||
if (len < offset) {
|
||||
Bgp_SetErrStat(BGPETRUNCMRT);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Bgp_SetErrStat(BGPENOERR);
|
||||
if (nbytes)
|
||||
*nbytes = offset;
|
||||
|
||||
return bgp4mp;
|
||||
}
|
||||
|
||||
Judgement Bgp_UnwrapBgp4mp(Mrtrecord *rec, Bgpmsg *dest, unsigned flags)
|
||||
{
|
||||
const Mrthdr *hdr = MRT_HDR(rec);
|
||||
if (!MRT_ISBGP4MP(hdr->type))
|
||||
return Bgp_SetErrStat(BGPEBADMRTTYPE);
|
||||
if (!BGP4MP_ISMESSAGE(hdr->subtype))
|
||||
return Bgp_SetErrStat(BGPEBADMRTTYPE);
|
||||
|
||||
Uint32 len = beswap32(hdr->len);
|
||||
Uint8 *base = (Uint8 *) MRT_DATAPTR(rec);
|
||||
|
||||
// Skip header
|
||||
size_t siz = BGP4MP_ISASN32BIT(hdr->subtype) ? 2*4 : 2*2; // skip ASN
|
||||
siz += 2; // skip interface index
|
||||
|
||||
Afi afi;
|
||||
if (len < siz + sizeof(afi))
|
||||
return Bgp_SetErrStat(BGPETRUNCMRT);
|
||||
|
||||
// Skip AFI and addresses
|
||||
memcpy(&afi, base + siz, sizeof(afi));
|
||||
siz += sizeof(afi);
|
||||
|
||||
switch (afi) {
|
||||
case AFI_IP:
|
||||
siz += 2 * sizeof(Ipv4adr);
|
||||
break;
|
||||
|
||||
case AFI_IP6:
|
||||
siz += 2 * sizeof(Ipv6adr);
|
||||
break;
|
||||
|
||||
default:
|
||||
return Bgp_SetErrStat(BGPEAFIUNSUP);
|
||||
}
|
||||
if (len < siz)
|
||||
return Bgp_SetErrStat(BGPETRUNCMRT);
|
||||
|
||||
size_t msgsiz = len - siz;
|
||||
void *buf = base + siz;
|
||||
|
||||
// Mask away ignored flags
|
||||
flags &= ~(BGPF_ADDPATH|BGPF_ASN32BIT);
|
||||
|
||||
// ...and automatically reset them as defined by BGP4MP subtype
|
||||
if (BGP4MP_ISASN32BIT(hdr->subtype))
|
||||
flags |= BGPF_ASN32BIT;
|
||||
if (BGP4MP_ISADDPATH(hdr->subtype))
|
||||
flags |= BGPF_ADDPATH;
|
||||
|
||||
// Unwrap BGP message
|
||||
return Bgp_MsgFromBuf(dest, buf, msgsiz, flags);
|
||||
}
|
||||
|
||||
#define TABLE_DUMP_MINSIZ (2 + 2 /*+ PFX*/ + 1 + 1 + 4 /*+ IP*/ + 2 + 2)
|
||||
|
||||
Mrtribent *Bgp_GetMrtRibHdr(Mrtrecord *rec)
|
||||
{
|
||||
Mrthdr *hdr = MRT_HDR(rec);
|
||||
if (hdr->type != MRT_TABLE_DUMP) {
|
||||
Bgp_SetErrStat(BGPEBADMRTTYPE);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
size_t len = beswap32(hdr->len);
|
||||
size_t siz = TABLE_DUMP_MINSIZ - 2; // do not include attribute length
|
||||
switch (hdr->subtype) {
|
||||
case AFI_IP: siz += 2 * IPV4_SIZE; break;
|
||||
case AFI_IP6: siz += 2 * IPV6_SIZE; break;
|
||||
|
||||
default:
|
||||
Bgp_SetErrStat(BGPEAFIUNSUP);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (len < siz + 2) { // include attribute length in size check
|
||||
Bgp_SetErrStat(BGPETRUNCMRT);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// NOTE: TABLE_DUMP has no extended timestamp variant
|
||||
Mrtribent *ent = (Mrtribent *) (hdr + 1);
|
||||
|
||||
Uint16 attrLen;
|
||||
memcpy(&attrLen, (Uint8 *) ent + siz, sizeof(attrLen));
|
||||
siz += 2; // now include offset
|
||||
|
||||
siz += beswap16(attrLen);
|
||||
if (len < siz) {
|
||||
Bgp_SetErrStat(BGPETRUNCMRT);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Bgp_SetErrStat(BGPENOERR);
|
||||
return (Mrtribent *) (hdr + 1);
|
||||
}
|
||||
|
||||
Zebrahdr *Bgp_GetZebraHdr(Mrtrecord *rec, size_t *nbytes)
|
||||
{
|
||||
Mrthdr *hdr = MRT_HDR(rec);
|
||||
if (hdr->type != MRT_BGP) {
|
||||
Bgp_SetErrStat(BGPEBADMRTTYPE);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
size_t len = beswap32(hdr->len);
|
||||
size_t offset = 2 + IPV4_SIZE;
|
||||
if (ZEBRA_ISMESSAGE(hdr->subtype))
|
||||
offset += 2 + IPV4_SIZE;
|
||||
else if (hdr->subtype == ZEBRA_STATE_CHANGE)
|
||||
offset += 2 * 2;
|
||||
|
||||
if (len < offset) {
|
||||
Bgp_SetErrStat(BGPETRUNCMRT);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Bgp_SetErrStat(BGPENOERR);
|
||||
if (nbytes)
|
||||
*nbytes = offset;
|
||||
|
||||
// NOTE: Legacy ZEBRA type doesn't have extended timestamp variants
|
||||
return (Zebrahdr *) (hdr + 1);
|
||||
}
|
||||
|
||||
#define ZEBRA_MSGSIZ (2uLL + IPV4_SIZE + 2uLL + IPV4_SIZE)
|
||||
|
||||
Judgement Bgp_UnwrapZebra(Mrtrecord *rec, Bgpmsg *dest, unsigned flags)
|
||||
{
|
||||
Mrthdr *hdr = MRT_HDR(rec);
|
||||
if (hdr->type != MRT_BGP)
|
||||
return Bgp_SetErrStat(BGPEBADMRTTYPE);
|
||||
|
||||
// Resolve BGP type from ZEBRA subtype
|
||||
BgpType type;
|
||||
switch (hdr->subtype) {
|
||||
case ZEBRA_UPDATE: type = BGP_UPDATE; break;
|
||||
case ZEBRA_OPEN: type = BGP_OPEN; break;
|
||||
case ZEBRA_KEEPALIVE: type = BGP_KEEPALIVE; break;
|
||||
case ZEBRA_NOTIFY: type = BGP_NOTIFICATION; break;
|
||||
|
||||
default: return Bgp_SetErrStat(BGPEBADMRTTYPE);
|
||||
}
|
||||
|
||||
Zebramsghdr *zebra = (Zebramsghdr *) (hdr + 1); // NOTE: ZEBRA doesn't have extended timestamp variants
|
||||
size_t len = beswap32(hdr->len);
|
||||
if (len < ZEBRA_MSGSIZ)
|
||||
return Bgp_SetErrStat(BGPETRUNCMRT);
|
||||
|
||||
// ZEBRA dumps don't include BGP message header
|
||||
size_t zebralen = len - ZEBRA_MSGSIZ;
|
||||
size_t msglen = BGP_HDRSIZ + zebralen;
|
||||
|
||||
// Validate message size
|
||||
if (msglen > BGP_EXMSGSIZ || (msglen > BGP_MSGSIZ && !BGP_ISEXMSG(flags)))
|
||||
return Bgp_SetErrStat(BGPEOVRSIZ);
|
||||
|
||||
// Allocate new message
|
||||
const MemOps *ops = BGP_MEMOPS(dest);
|
||||
Bgphdr *msg = (Bgphdr *) ops->Alloc(dest->allocp, msglen, NULL);
|
||||
if (!msg)
|
||||
return Bgp_SetErrStat(BGPENOMEM);
|
||||
|
||||
// Build a valid BGP header
|
||||
memset(msg->marker, 0xff, sizeof(msg->marker));
|
||||
msg->len = beswap16(msglen);
|
||||
msg->type = type;
|
||||
memcpy(msg + 1, zebra->msg, zebralen);
|
||||
|
||||
// Populate `dest`
|
||||
dest->buf = (Uint8 *) msg;
|
||||
dest->flags = flags & BGPF_EXMSG; // only acceptable flag
|
||||
BGP_CLRATTRTAB(dest->table);
|
||||
return Bgp_SetErrStat(BGPENOERR);
|
||||
}
|
||||
|
||||
void Bgp_ClearMrt(Mrtrecord *rec)
|
||||
{
|
||||
const MemOps *memOps = MRT_MEMOPS(rec);
|
||||
memOps->Free(rec->allocp, rec->buf);
|
||||
memOps->Free(rec->allocp, rec->peerOffTab);
|
||||
|
||||
rec->buf = NULL;
|
||||
rec->peerOffTab = NULL;
|
||||
}
|
100
lonetix/bgp/parameters.c
Normal file
100
lonetix/bgp/parameters.c
Normal file
@@ -0,0 +1,100 @@
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
|
||||
/**
|
||||
* \file bgp/parameters.c
|
||||
*
|
||||
* Deals with BGP OPEN parameters.
|
||||
*
|
||||
* \copyright The DoubleFourteen Code Forge (C) All Rights Reserved
|
||||
* \author Lorenzo Cogotti
|
||||
*/
|
||||
|
||||
#include "bgp/bgp_local.h"
|
||||
|
||||
Judgement Bgp_StartMsgParms(Bgpparmiter *it, Bgpmsg *msg)
|
||||
{
|
||||
const Bgpopen *open = Bgp_GetMsgOpen(msg);
|
||||
if (!open)
|
||||
return NG; // error already set
|
||||
|
||||
Bgp_StartParms(it, BGP_OPENPARMS(open));
|
||||
return OK; // error already cleared
|
||||
}
|
||||
|
||||
void Bgp_StartParms(Bgpparmiter *it, const Bgpparmseg *p)
|
||||
{
|
||||
it->ptr = (Uint8 *) p->parms;
|
||||
it->base = it->ptr;
|
||||
it->lim = it->base + p->len;
|
||||
}
|
||||
|
||||
Bgpparm *Bgp_NextParm(Bgpparmiter *it)
|
||||
{
|
||||
if (it->ptr >= it->lim) {
|
||||
Bgp_SetErrStat(BGPENOERR);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Bgpparm *p = (Bgpparm *) it->ptr;
|
||||
size_t left = it->lim - it->ptr;
|
||||
if (left < 2uLL || left < 2uLL + p->len) {
|
||||
Bgp_SetErrStat(BGPETRUNCMSG);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
it->ptr += 2 + p->len;
|
||||
Bgp_SetErrStat(BGPENOERR);
|
||||
return p;
|
||||
}
|
||||
|
||||
Judgement Bgp_StartMsgCaps(Bgpcapiter *it, Bgpmsg *msg)
|
||||
{
|
||||
if (Bgp_StartMsgParms(&it->pi, msg) != OK)
|
||||
return NG; // error already set
|
||||
|
||||
// Set starting point so Bgp_NextCap() scans for next parameter
|
||||
it->base = it->lim = it->ptr = it->pi.ptr;
|
||||
return OK;
|
||||
}
|
||||
|
||||
void Bgp_StartCaps(Bgpcapiter *it, const Bgpparmseg *parms)
|
||||
{
|
||||
Bgp_StartParms(&it->pi, parms);
|
||||
// Set starting point so Bgp_NextCap() scans for next parameter
|
||||
it->base = it->lim = it->ptr = it->pi.ptr;
|
||||
}
|
||||
|
||||
Bgpcap *Bgp_NextCap(Bgpcapiter *it)
|
||||
{
|
||||
if (it->ptr >= it->lim) {
|
||||
// Try to find another CAPABILITY parameter
|
||||
Bgpparm *p;
|
||||
|
||||
while ((p = Bgp_NextParm(&it->pi)) != NULL) {
|
||||
if (p->code == BGP_PARM_CAPABILITY)
|
||||
break;
|
||||
}
|
||||
if (!p) {
|
||||
// Scanned all parameters, nothing found
|
||||
Bgp_SetErrStat(BGPENOERR);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Setup for reading new capabilities from `p`
|
||||
it->base = (Uint8 *) p + 2;
|
||||
it->lim = it->base + p->len;
|
||||
it->ptr = it->base;
|
||||
}
|
||||
|
||||
// Return current capability and move over
|
||||
size_t left = it->lim - it->ptr;
|
||||
Bgpcap *cap = (Bgpcap *) it->ptr;
|
||||
if (left < 2uLL || left < 2uLL + cap->len) {
|
||||
Bgp_SetErrStat(BGPETRUNCMSG);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
it->ptr += 2 + cap->len;
|
||||
Bgp_SetErrStat(BGPENOERR);
|
||||
return cap;
|
||||
}
|
518
lonetix/bgp/patricia.c
Normal file
518
lonetix/bgp/patricia.c
Normal file
@@ -0,0 +1,518 @@
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
|
||||
/**
|
||||
* \file bgp/patricia.c
|
||||
*
|
||||
* Implements PATRICIA trie utilities.
|
||||
*
|
||||
* \copyright The DoubleFourteen Code Forge (C) All Rights Reserved
|
||||
* \author Lorenzo Cogotti
|
||||
*/
|
||||
|
||||
#include "bgp/patricia.h"
|
||||
#include "sys/sys.h" // for Sys_OutOfMemory()
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#define PAT_BLOCK_ALIGN_SHIFT 2
|
||||
#define PAT_BLOCK_ALIGN (1uLL << PAT_BLOCK_ALIGN_SHIFT)
|
||||
#define PAT_BLOCK_SIZE 2048
|
||||
|
||||
struct Patblock {
|
||||
Patblock *nextBlock;
|
||||
Uint32 freeOfs;
|
||||
ALIGNED(PAT_BLOCK_ALIGN, Uint8 buf[PAT_BLOCK_SIZE]);
|
||||
};
|
||||
|
||||
/**
|
||||
* \brief Trie node, contains node and prefix info.
|
||||
*
|
||||
* A node may be either:
|
||||
* - Used - node is in use and part of the trie.
|
||||
* - Unused - node is inside free node list and may be reclaimed on further
|
||||
* node insertion.
|
||||
*/
|
||||
union Patnode {
|
||||
// Following struct is significant when node is used.
|
||||
struct {
|
||||
Patnode *parent; // NOTE: LSB used to mark glue nodes
|
||||
Patnode *children[2];
|
||||
|
||||
// Prefix part follows...
|
||||
Uint8 width;
|
||||
Uint8 bytes[FLEX_ARRAY];
|
||||
};
|
||||
// Following field is significant when node is unused.
|
||||
Patnode *nextFree;
|
||||
};
|
||||
|
||||
static Patnode *Pat_NodeForPrefix(const RawPrefix *pfx)
|
||||
{
|
||||
return (Patnode *) ((Uint8 *) pfx - offsetof(Patnode, width));
|
||||
}
|
||||
|
||||
static Boolean Pat_IsNodeGlue(const Patnode *n)
|
||||
{
|
||||
return (((Uintptr) n->parent) & 1uLL) != 0;
|
||||
}
|
||||
|
||||
static Patnode *Pat_GetNodeParent(const Patnode *n)
|
||||
{
|
||||
return (Patnode *) ((Uintptr) n->parent & ~1uLL);
|
||||
}
|
||||
|
||||
static void Pat_SetNodeParent(Patnode *n, Patnode *parent)
|
||||
{
|
||||
n->parent = (Patnode *) ((Uintptr) parent | (((Uintptr) n->parent) & 1uLL));
|
||||
}
|
||||
|
||||
static void Pat_SetNodeGlue(Patnode *n)
|
||||
{
|
||||
n->parent = (Patnode *) ((Uintptr) n->parent | 1uLL);
|
||||
}
|
||||
|
||||
static void Pat_ResetNodeGlue(Patnode *n)
|
||||
{
|
||||
n->parent = (Patnode *) ((Uintptr) n->parent & ~1uLL);
|
||||
}
|
||||
|
||||
static Patnode *Pat_AllocNode(Patricia *trie, Uint8 width)
|
||||
{
|
||||
Patnode *n;
|
||||
Patblock *block;
|
||||
size_t siz, len;
|
||||
unsigned idx;
|
||||
|
||||
// Calculate block size and lookup inside free cache
|
||||
len = PFXLEN(width);
|
||||
assert(len <= IPV6_SIZE);
|
||||
|
||||
idx = len >> PAT_BLOCK_ALIGN_SHIFT;
|
||||
siz = ALIGN(offsetof(Patnode, bytes[len]), PAT_BLOCK_ALIGN);
|
||||
n = trie->freeBins[idx];
|
||||
if (n) {
|
||||
// ...free list cache hit
|
||||
trie->freeBins[idx] = n->nextFree;
|
||||
|
||||
goto return_node;
|
||||
}
|
||||
|
||||
// Need to allocate a new node
|
||||
block = trie->blocks;
|
||||
if (!block || block->freeOfs + siz > PAT_BLOCK_SIZE) {
|
||||
// Must allocate a new block altoghether
|
||||
block = (Patblock *) malloc(sizeof(*block));
|
||||
if (!block) {
|
||||
Sys_OutOfMemory();
|
||||
return NULL; // too bad...
|
||||
}
|
||||
|
||||
block->freeOfs = 0;
|
||||
block->nextBlock = trie->blocks;
|
||||
trie->blocks = block;
|
||||
}
|
||||
|
||||
n = (Patnode *) (block->buf + block->freeOfs);
|
||||
block->freeOfs += siz;
|
||||
|
||||
return_node:
|
||||
n->parent = NULL;
|
||||
n->children[0] = n->children[1] = NULL;
|
||||
|
||||
n->width = width;
|
||||
return n;
|
||||
}
|
||||
|
||||
static void Pat_FreeNode(Patricia *trie, Patnode *n)
|
||||
{
|
||||
// Place inside free cache bins
|
||||
unsigned idx = PFXLEN(n->width) >> PAT_BLOCK_ALIGN_SHIFT;
|
||||
|
||||
n->nextFree = trie->freeBins[idx];
|
||||
trie->freeBins[idx] = n;
|
||||
}
|
||||
|
||||
RawPrefix *Pat_Insert(Patricia *trie, const RawPrefix *pfx)
|
||||
{
|
||||
Patnode *n;
|
||||
unsigned maxWidth;
|
||||
|
||||
assert(trie->afi == AFI_IP || trie->afi == AFI_IP6);
|
||||
maxWidth = (trie->afi == AFI_IP6) ? IPV6_WIDTH : IPV4_WIDTH;
|
||||
|
||||
assert(pfx->width <= maxWidth);
|
||||
|
||||
n = trie->head;
|
||||
if (!n) {
|
||||
// First node ever, create trie head node
|
||||
n = Pat_AllocNode(trie, pfx->width);
|
||||
if (!n)
|
||||
return NULL;
|
||||
|
||||
memcpy(n->bytes, pfx->bytes, PFXLEN(pfx->width));
|
||||
|
||||
// Place it in `trie`
|
||||
trie->head = n;
|
||||
trie->nprefixes++;
|
||||
return PLAINPFX(n);
|
||||
}
|
||||
|
||||
while (n->width < pfx->width || Pat_IsNodeGlue(n)) {
|
||||
int bit = (n->width < maxWidth) &&
|
||||
(pfx->bytes[n->width >> 3] & (0x80 >> (n->width & 0x07)));
|
||||
|
||||
if (!n->children[bit])
|
||||
break;
|
||||
|
||||
n = n->children[bit];
|
||||
}
|
||||
|
||||
unsigned checkBit = MIN(n->width, pfx->width);
|
||||
unsigned differBit = 0;
|
||||
|
||||
#if 1
|
||||
// unoptimized version
|
||||
unsigned r;
|
||||
for (unsigned i = 0, z = 0; z < checkBit; i++, z += 8) {
|
||||
r = (pfx->bytes[i] ^ n->bytes[i]);
|
||||
if (r == 0) {
|
||||
differBit = z + 8;
|
||||
continue;
|
||||
}
|
||||
|
||||
unsigned j;
|
||||
for (j = 0; j < 8; j++)
|
||||
if (r & (0x80 >> j))
|
||||
break;
|
||||
|
||||
differBit = z + j;
|
||||
break;
|
||||
}
|
||||
#else
|
||||
/* TODO possible optimization:
|
||||
* example 32 bit portion with different endianness:
|
||||
|
||||
LSB (visit using LSB->MSB) MSB (LE)
|
||||
01000000 00000000 00000100 00000000
|
||||
MSB (visit using MSB->LSB) LSB (BE)
|
||||
|
||||
leftmost bit is:
|
||||
-> 32 - bsr32() (BE)
|
||||
-> bsf32() - 1 (LE)
|
||||
*/
|
||||
|
||||
for (unsigned i = 0, z = 0; z < checkBit; i++, z += 32) {
|
||||
Uint32 r = (pfx->u32[i] ^ n->u32[i]);
|
||||
|
||||
if (r == 0) {
|
||||
differBit = z + 32;
|
||||
continue;
|
||||
}
|
||||
|
||||
unsigned j = (EDN_NATIVE == EDN_BIG) ? 32 - bsr32(r) : bsf32(r) - 1; // clz(beswap32(r));
|
||||
|
||||
differBit = z + j;
|
||||
break;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
if (differBit > checkBit)
|
||||
differBit = checkBit;
|
||||
|
||||
Patnode *parent = Pat_GetNodeParent(n);
|
||||
while (parent && parent->width >= differBit) {
|
||||
n = parent;
|
||||
parent = Pat_GetNodeParent(n);
|
||||
}
|
||||
|
||||
if (differBit == pfx->width && n->width == pfx->width) {
|
||||
if (Pat_IsNodeGlue(n)) {
|
||||
// Replace glue node
|
||||
Pat_ResetNodeGlue(n);
|
||||
memcpy(n->bytes, pfx->bytes, PFXLEN(pfx->width));
|
||||
}
|
||||
|
||||
trie->nprefixes++;
|
||||
return PLAINPFX(n);
|
||||
}
|
||||
|
||||
// Must allocate new node
|
||||
Patnode *newNode = Pat_AllocNode(trie, pfx->width);
|
||||
if (!newNode)
|
||||
return NULL; // out of memory
|
||||
|
||||
memcpy(newNode->bytes, pfx->bytes, PFXLEN(pfx->width));
|
||||
trie->nprefixes++;
|
||||
|
||||
if (n->width == differBit) {
|
||||
Pat_SetNodeParent(newNode, n);
|
||||
|
||||
int bit = (n->width < maxWidth) &&
|
||||
(pfx->bytes[n->width >> 3] & (0x80 >> (n->width & 0x07)));
|
||||
|
||||
n->children[bit] = newNode;
|
||||
return PLAINPFX(newNode);
|
||||
}
|
||||
|
||||
if (pfx->width == differBit) {
|
||||
int bit = (pfx->width < maxWidth) &&
|
||||
(n->bytes[pfx->width >> 3] & (0x80 >> (pfx->width & 0x07)));
|
||||
|
||||
newNode->children[bit] = n;
|
||||
Pat_SetNodeParent(newNode, n);
|
||||
|
||||
parent = Pat_GetNodeParent(n);
|
||||
if (!parent)
|
||||
trie->head = newNode;
|
||||
|
||||
else if (parent->children[1] == n) {
|
||||
int bit = (parent->children[1] == n);
|
||||
parent->children[bit] = n;
|
||||
}
|
||||
|
||||
Pat_SetNodeParent(n, newNode);
|
||||
} else {
|
||||
Patnode *glue = Pat_AllocNode(trie, differBit);
|
||||
if (!glue)
|
||||
return NULL;
|
||||
|
||||
parent = Pat_GetNodeParent(n);
|
||||
|
||||
glue->parent = parent;
|
||||
Pat_SetNodeGlue(glue);
|
||||
|
||||
int bit = (differBit < maxWidth) &&
|
||||
(pfx->bytes[differBit >> 3] & (0x80 >> (differBit & 0x07)));
|
||||
|
||||
glue->children[bit] = newNode;
|
||||
glue->children[!bit] = n;
|
||||
|
||||
newNode->parent = glue;
|
||||
if (!parent)
|
||||
trie->head = glue;
|
||||
|
||||
else {
|
||||
int bit = (parent->children[1] == n);
|
||||
parent->children[bit] = glue;
|
||||
}
|
||||
|
||||
Pat_SetNodeParent(n, glue);
|
||||
}
|
||||
|
||||
return PLAINPFX(newNode);
|
||||
}
|
||||
|
||||
static Boolean Ip_CompWithMask(const Uint8 *a, const Uint8 *b, Uint8 mask)
|
||||
{
|
||||
unsigned n = mask / 8;
|
||||
|
||||
if (memcmp(a, b, n) == 0) {
|
||||
unsigned m = ~0u << (8 - (mask % 8));
|
||||
|
||||
if ((mask & 0x7) == 0 || (a[n] & m) == (b[n] & m))
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
RawPrefix *Pat_SearchExact(const Patricia *trie, const RawPrefix *pfx)
|
||||
{
|
||||
const Patnode *n = trie->head;
|
||||
if (!n)
|
||||
return NULL;
|
||||
|
||||
while (n->width < pfx->width) {
|
||||
int bit = (pfx->bytes[n->width >> 3] & (0x80 >> (n->width & 0x07))) != 0;
|
||||
|
||||
n = n->children[bit];
|
||||
if (!n)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (n->width > pfx->width || Pat_IsNodeGlue(n))
|
||||
return NULL;
|
||||
|
||||
if (Ip_CompWithMask(n->bytes, pfx->bytes, pfx->width))
|
||||
return PLAINPFX(n);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Boolean Pat_IsSubnetOf(const Patricia *trie, const RawPrefix *pfx)
|
||||
{
|
||||
const Patnode *n = trie->head;
|
||||
|
||||
while (n && n->width < pfx->width) {
|
||||
if (!Pat_IsNodeGlue(n))
|
||||
return Ip_CompWithMask(n->bytes, pfx->bytes, n->width);
|
||||
|
||||
int bit = (pfx->bytes[n->width >> 3] & (0x80 >> (n->width & 0x07))) != 0;
|
||||
|
||||
n = n->children[bit];
|
||||
}
|
||||
|
||||
return n && !Pat_IsNodeGlue(n) &&
|
||||
n->width <= pfx->width &&
|
||||
Ip_CompWithMask(n->bytes, pfx->bytes, pfx->width);
|
||||
}
|
||||
|
||||
Boolean Pat_IsSupernetOf(const Patricia *trie, const RawPrefix *pfx)
|
||||
{
|
||||
Patnode *start = trie->head;
|
||||
while (start && start->width < pfx->width) {
|
||||
int bit = (pfx->bytes[start->width >> 3] & (0x80 >> (start->width & 0x07))) != 0;
|
||||
|
||||
start = start->children[bit];
|
||||
}
|
||||
|
||||
Patnode *node;
|
||||
|
||||
Patnode *stack[128+1];
|
||||
Patnode **sp = stack;
|
||||
Patnode *next = start;
|
||||
while ((node = next) != NULL) {
|
||||
if (!Pat_IsNodeGlue(node)) {
|
||||
if (Ip_CompWithMask(node->bytes, pfx->bytes, pfx->width))
|
||||
return TRUE;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (next->children[0]) {
|
||||
if (next->children[1])
|
||||
*sp++ = next->children[1];
|
||||
|
||||
next = next->children[0];
|
||||
} else if (next->children[1]) {
|
||||
next = next->children[1];
|
||||
} else if (sp != stack) {
|
||||
next = *(sp--);
|
||||
} else {
|
||||
next = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
Boolean Pat_IsRelatedOf(const Patricia *trie, const RawPrefix *pfx)
|
||||
{
|
||||
Patnode *start = trie->head;
|
||||
while (start && start->width < pfx->width) {
|
||||
if (!Pat_IsNodeGlue(start) && Ip_CompWithMask(start->bytes, pfx->bytes, start->width))
|
||||
return TRUE;
|
||||
|
||||
int bit = (pfx->bytes[start->width >> 3] & (0x80 >> (start->width & 0x07))) != 0;
|
||||
|
||||
start = start->children[bit];
|
||||
}
|
||||
|
||||
Patnode *node;
|
||||
|
||||
Patnode *stack[128+1];
|
||||
Patnode **sp = stack;
|
||||
Patnode *next = start;
|
||||
while ((node = next) != NULL) {
|
||||
if (!Pat_IsNodeGlue(node) && Ip_CompWithMask(node->bytes, pfx->bytes, pfx->width))
|
||||
return TRUE;
|
||||
|
||||
if (next->children[0]) {
|
||||
if (next->children[1])
|
||||
*sp++ = next->children[1];
|
||||
|
||||
next = next->children[0];
|
||||
} else if (next->children[1]) {
|
||||
next = next->children[1];
|
||||
} else if (sp != stack) {
|
||||
next = *(sp--);
|
||||
} else {
|
||||
next = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
Boolean Pat_Remove(Patricia *trie, const RawPrefix *pfx)
|
||||
{
|
||||
RawPrefix *res = Pat_SearchExact(trie, pfx);
|
||||
if (!res)
|
||||
return FALSE;
|
||||
|
||||
Patnode *n = Pat_NodeForPrefix(res);
|
||||
if (!n)
|
||||
return FALSE;
|
||||
|
||||
trie->nprefixes--;
|
||||
if (n->children[0] && n->children[1]) {
|
||||
Pat_SetNodeGlue(n);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
Patnode *parent, *pparent;
|
||||
Patnode *child;
|
||||
int bit;
|
||||
|
||||
parent = Pat_GetNodeParent(n);
|
||||
if (!n->children[0] && !n->children[1]) {
|
||||
Pat_FreeNode(trie, n);
|
||||
if (!parent) {
|
||||
trie->head = NULL;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
bit = (parent->children[1] == n);
|
||||
parent->children[bit] = NULL;
|
||||
child = parent->children[!bit];
|
||||
|
||||
if (!Pat_IsNodeGlue(parent))
|
||||
return TRUE;
|
||||
|
||||
// If here, then parent is glue, we need to remove them both
|
||||
pparent = Pat_GetNodeParent(parent);
|
||||
if (!pparent) {
|
||||
trie->head = child;
|
||||
|
||||
} else {
|
||||
bit = (pparent->children[1] == parent);
|
||||
pparent->children[bit] = child;
|
||||
}
|
||||
|
||||
Pat_SetNodeParent(child, pparent);
|
||||
Pat_FreeNode(trie, parent);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
bit = (n->children[1] != NULL);
|
||||
child = n->children[bit];
|
||||
|
||||
Pat_SetNodeParent(child, parent);
|
||||
Pat_FreeNode(trie, n);
|
||||
if (!parent) {
|
||||
trie->head = child;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
bit = (parent->children[1] == n);
|
||||
parent->children[bit] = child;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
void Pat_Clear(Patricia *trie)
|
||||
{
|
||||
while (trie->blocks) {
|
||||
Patblock *t = trie->blocks;
|
||||
|
||||
trie->blocks = t->nextBlock;
|
||||
free(t);
|
||||
}
|
||||
|
||||
trie->afi = 0;
|
||||
trie->nprefixes = 0;
|
||||
trie->head = NULL;
|
||||
memset(trie->freeBins, 0, sizeof(trie->freeBins));
|
||||
}
|
191
lonetix/bgp/prefix.c
Normal file
191
lonetix/bgp/prefix.c
Normal file
@@ -0,0 +1,191 @@
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
|
||||
/**
|
||||
* \file bgp/prefix.c
|
||||
*
|
||||
* Deal with network prefixes.
|
||||
*
|
||||
* \copyright The DoubleFourteen Code Forge (C) All Rights Reserved
|
||||
* \author Lorenzo Cogotti
|
||||
*/
|
||||
|
||||
#include "bgp/bgp_local.h"
|
||||
#include "sys/endian.h"
|
||||
#include "sys/ip.h"
|
||||
#include "numlib.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
|
||||
// ===========================================================================
|
||||
// Some performance oriented macros to avoid branching during prefix iteration
|
||||
|
||||
/// Calculate the minimum size of a possibly ADD_PATH enabled prefix.
|
||||
#define MINPFXSIZ(isAddPath) \
|
||||
((((isAddPath) != 0) << 2) + 1)
|
||||
|
||||
/// Extract the prefix portion out of a possibly ADD_PATH enabled prefix pointer.
|
||||
#define RAWPFXPTR(base, isAddPath) \
|
||||
((RawPrefix *) ((Uint8 *) (base) + (((isAddPath) != 0) << 2)))
|
||||
|
||||
/// Calculate maximum prefix width in bits given an address family
|
||||
#define MAXPFXWIDTH(family) (((family) == AFI_IP6) ? IPV6_WIDTH : IPV4_WIDTH) // simple CMOV
|
||||
|
||||
// ===========================================================================
|
||||
|
||||
char *Bgp_PrefixToString(Afi afi, const RawPrefix *prefix, char *dest)
|
||||
{
|
||||
Ipv4adr adr;
|
||||
Ipv6adr adr6;
|
||||
|
||||
switch (afi) {
|
||||
case AFI_IP:
|
||||
memset(&adr, 0, sizeof(adr));
|
||||
memcpy(&adr, prefix->bytes, PFXLEN(prefix->width));
|
||||
dest = Ipv4_AdrToString(&adr, dest);
|
||||
break;
|
||||
|
||||
case AFI_IP6:
|
||||
memset(&adr6, 0, sizeof(adr6));
|
||||
memcpy(&adr6, prefix->bytes, PFXLEN(prefix->width));
|
||||
dest = Ipv6_AdrToString(&adr6, dest);
|
||||
break;
|
||||
|
||||
default:
|
||||
return NULL; // invalid argument
|
||||
}
|
||||
|
||||
*dest++ = '/';
|
||||
dest = Utoa(prefix->width, dest);
|
||||
return dest;
|
||||
}
|
||||
|
||||
char *Bgp_ApPrefixToString(Afi afi, const ApRawPrefix *prefix, char *dest)
|
||||
{
|
||||
// NOTE: Test early to avoid polluting `dest` in case of invalid argument;
|
||||
// hopefully compilers will flatten this function to
|
||||
// eliminate duplicate test inside switch
|
||||
if (afi != AFI_IP && afi != AFI_IP6)
|
||||
return NULL; // invalid argument
|
||||
|
||||
dest = Utoa(beswap32(prefix->pathId), dest);
|
||||
*dest++ = ' ';
|
||||
return Bgp_PrefixToString(afi, PLAINPFX(prefix), dest);
|
||||
}
|
||||
|
||||
Judgement Bgp_StringToPrefix(const char *s, Prefix *dest)
|
||||
{
|
||||
Ipadr adr;
|
||||
unsigned width;
|
||||
NumConvRet res;
|
||||
|
||||
const char *ptr = s;
|
||||
while (*ptr != '/' && *ptr != '\0') ptr++;
|
||||
|
||||
size_t len = ptr - s;
|
||||
char *buf = (char *) alloca(len + 1);
|
||||
|
||||
memcpy(buf, s, len);
|
||||
buf[len] = '\0';
|
||||
|
||||
if (Ip_StringToAdr(buf, &adr) != OK)
|
||||
return NG; // Bad IP string
|
||||
|
||||
if (*ptr == '/') {
|
||||
ptr++; // skip '/' separator
|
||||
|
||||
char *eptr;
|
||||
width = Atou(ptr, &eptr, 10, &res);
|
||||
|
||||
if (res != NCVENOERR || *eptr != '\0')
|
||||
return NG;
|
||||
} else
|
||||
width = (adr.family == IP6) ? IPV6_WIDTH : IPV4_WIDTH; // implicit full prefix
|
||||
|
||||
switch (adr.family) {
|
||||
case IP4:
|
||||
if (width > IPV4_WIDTH) return NG; // illegal prefix length
|
||||
|
||||
dest->afi = AFI_IP;
|
||||
break;
|
||||
|
||||
case IP6:
|
||||
if (width > IPV6_WIDTH) return NG; // illegal prefix length
|
||||
|
||||
dest->afi = AFI_IP6;
|
||||
break;
|
||||
|
||||
default:
|
||||
UNREACHABLE;
|
||||
return NG;
|
||||
}
|
||||
|
||||
dest->isAddPath = FALSE;
|
||||
dest->width = width;
|
||||
memcpy(dest->bytes, adr.bytes, PFXLEN(width));
|
||||
return OK;
|
||||
}
|
||||
|
||||
Judgement Bgp_StartPrefixes(Prefixiter *it,
|
||||
Afi afi,
|
||||
Safi safi,
|
||||
const void *data,
|
||||
size_t nbytes,
|
||||
Boolean isAddPath)
|
||||
{
|
||||
if (afi != AFI_IP && afi != AFI_IP6) {
|
||||
Bgp_SetErrStat(BGPEAFIUNSUP);
|
||||
return NG;
|
||||
}
|
||||
if (safi != SAFI_UNICAST && safi != SAFI_MULTICAST) {
|
||||
Bgp_SetErrStat(BGPESAFIUNSUP);
|
||||
return NG;
|
||||
}
|
||||
|
||||
it->afi = afi;
|
||||
it->safi = safi;
|
||||
it->isAddPath = isAddPath;
|
||||
it->base = (Uint8 *) data;
|
||||
it->lim = it->base + nbytes;
|
||||
it->ptr = it->base;
|
||||
|
||||
Bgp_SetErrStat(BGPENOERR);
|
||||
return OK;
|
||||
}
|
||||
|
||||
void *Bgp_NextPrefix(Prefixiter *it)
|
||||
{
|
||||
if (it->ptr >= it->lim) {
|
||||
Bgp_SetErrStat(BGPENOERR);
|
||||
return NULL; // end of iteration
|
||||
}
|
||||
|
||||
// Basic check for prefix initial bytes
|
||||
size_t left = it->lim - it->ptr;
|
||||
size_t siz = MINPFXSIZ(it->isAddPath);
|
||||
if (left < siz) {
|
||||
Bgp_SetErrStat(BGPETRUNCPFX);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Adjust a pointer to skip Path identifier info if necessary
|
||||
const RawPrefix *rawPfx = RAWPFXPTR(it->ptr, it->isAddPath);
|
||||
if (rawPfx->width > MAXPFXWIDTH(it->afi)) {
|
||||
Bgp_SetErrStat(BGPEBADPFXWIDTH);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Ensure all the necessary prefix bytes are present
|
||||
siz += PFXLEN(rawPfx->width);
|
||||
if (left < siz) {
|
||||
Bgp_SetErrStat(BGPETRUNCPFX);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// All good, advance and return
|
||||
void *pfx = it->ptr;
|
||||
it->ptr += siz;
|
||||
Bgp_SetErrStat(BGPENOERR);
|
||||
|
||||
return pfx;
|
||||
}
|
838
lonetix/bgp/vm.c
Normal file
838
lonetix/bgp/vm.c
Normal file
@@ -0,0 +1,838 @@
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
|
||||
/**
|
||||
* \file bgp/vm.c
|
||||
*
|
||||
* BGP VM initialization and execution loop.
|
||||
*
|
||||
* \copyright The DoubleFourteen Code Forge (C) All Rights Reserved
|
||||
* \author Lorenzo Cogotti
|
||||
*/
|
||||
|
||||
#include "bgp/bgp_local.h"
|
||||
#include "bgp/patricia.h"
|
||||
#include "bgp/vmintrin.h"
|
||||
#include "sys/endian.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#if defined(__GNUC__) && !defined(DF_BGP_VM_NO_COMPUTED_GOTO)
|
||||
#define DF_BGP_VM_USES_COMPUTED_GOTO
|
||||
#include "bgp/vm_gccdef.h"
|
||||
#else
|
||||
#include "bgp/vm_cdef.h"
|
||||
#endif
|
||||
|
||||
#define BGP_VM_MINHEAPSIZ (4 * 1024)
|
||||
#define BGP_VM_STKSIZ (4 * 1024)
|
||||
|
||||
#define BGP_VM_GROWPROGN 128
|
||||
|
||||
/* During VM execution instructions update the current BGP message
|
||||
* match. But sometimes the match is irrelevant (think about something
|
||||
* like:
|
||||
*
|
||||
* ```
|
||||
* LOADU 1
|
||||
* NOT
|
||||
* CPASS
|
||||
* ```
|
||||
*
|
||||
* This bytecode doesn't examine any actual BGP message segment,
|
||||
* to simplify instructions, whenever an irrelevant match is being produced,
|
||||
* the following static variable is referenced by `vm->curMatch`,
|
||||
* to provide a dummy match that can be updated at will
|
||||
* by any VM and is always discarded by `Bgp_VmStoreMatch()`.
|
||||
*/
|
||||
static Bgpvmmatch discardMatch;
|
||||
|
||||
Judgement Bgp_InitVm(Bgpvm *vm, size_t heapSiz)
|
||||
{
|
||||
size_t siz = BGP_VM_STKSIZ + MAX(heapSiz, BGP_VM_MINHEAPSIZ);
|
||||
siz = ALIGN(siz, ALIGNMENT);
|
||||
|
||||
assert(siz <= 0xffffffffuLL);
|
||||
|
||||
void *heap = malloc(siz);
|
||||
if (!heap)
|
||||
return Bgp_SetErrStat(BGPENOMEM);
|
||||
|
||||
memset(vm, 0, sizeof(*vm));
|
||||
|
||||
vm->heap = heap;
|
||||
vm->hMemSiz = siz;
|
||||
vm->hHighMark = siz;
|
||||
return Bgp_SetErrStat(BGPENOERR);
|
||||
}
|
||||
|
||||
Judgement Bgp_VmEmit(Bgpvm *vm, Bgpvmbytec bytec)
|
||||
{
|
||||
assert(!vm->isRunning);
|
||||
|
||||
BGP_VMCLRERR(vm);
|
||||
|
||||
if (BGP_VMOPC(bytec) == BGP_VMOP_END)
|
||||
return Bgp_SetErrStat(BGPENOERR); // ignore useless emit
|
||||
|
||||
if (vm->progLen + 1 >= vm->progCap) {
|
||||
// Grow the VM program segment
|
||||
size_t newSiz = vm->progCap + BGP_VM_GROWPROGN;
|
||||
Bgpvmbytec *newProg = (Bgpvmbytec *) realloc(vm->prog, newSiz * sizeof(*newProg));
|
||||
if (!newProg) {
|
||||
// Flag the VM as bad
|
||||
vm->setupFailed = TRUE;
|
||||
vm->errCode = BGPENOMEM;
|
||||
|
||||
return Bgp_SetErrStat(BGPENOMEM);
|
||||
}
|
||||
|
||||
vm->prog = newProg;
|
||||
vm->progCap = newSiz;
|
||||
}
|
||||
|
||||
// Append instruction and follow it with BGP_VMOP_END
|
||||
vm->prog[vm->progLen++] = bytec;
|
||||
vm->prog[vm->progLen] = BGP_VMOP_END;
|
||||
return Bgp_SetErrStat(BGPENOERR);
|
||||
}
|
||||
|
||||
void *Bgp_VmPermAlloc(Bgpvm *vm, size_t size)
|
||||
{
|
||||
assert(!vm->isRunning);
|
||||
|
||||
BGP_VMCLRERR(vm);
|
||||
|
||||
size = ALIGN(size, ALIGNMENT);
|
||||
|
||||
if (vm->hLowMark + size > vm->hMemSiz) {
|
||||
// Flag the VM as bad
|
||||
vm->setupFailed = TRUE;
|
||||
vm->errCode = BGPEVMOOM;
|
||||
|
||||
Bgp_SetErrStat(BGPEVMOOM);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void *ptr = (Uint8 *) vm->heap + vm->hLowMark;
|
||||
vm->hLowMark += size;
|
||||
|
||||
Bgp_SetErrStat(BGPENOERR);
|
||||
return ptr;
|
||||
}
|
||||
|
||||
void *Bgp_VmTempAlloc(Bgpvm *vm, size_t size)
|
||||
{
|
||||
assert(vm->isRunning);
|
||||
|
||||
size = ALIGN(size, ALIGNMENT);
|
||||
|
||||
size_t stksiz = vm->si * sizeof(Bgpvmval);
|
||||
if (vm->hLowMark + stksiz + size > vm->hHighMark) UNLIKELY {
|
||||
// NOTE: VM is being executed, don't set BGP error state
|
||||
// it will be updated by Bgp_VmExec() as needed
|
||||
vm->errCode = BGPEVMOOM;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
assert(vm->hHighMark >= size);
|
||||
|
||||
vm->hHighMark -= size;
|
||||
return (Uint8 *) vm->heap + vm->hHighMark;
|
||||
}
|
||||
|
||||
void Bgp_VmTempFree(Bgpvm *vm, size_t size)
|
||||
{
|
||||
assert(vm->isRunning);
|
||||
|
||||
size = ALIGN(size, ALIGNMENT);
|
||||
|
||||
assert(size + vm->hHighMark <= vm->hMemSiz);
|
||||
vm->hHighMark += size;
|
||||
}
|
||||
|
||||
Boolean Bgp_VmExec(Bgpvm *vm, Bgpmsg *msg)
|
||||
{
|
||||
// Fundamental sanity checks
|
||||
assert(!vm->isRunning);
|
||||
|
||||
if (vm->setupFailed) UNLIKELY {
|
||||
vm->errCode = BGPEBADVM;
|
||||
goto cant_run;
|
||||
}
|
||||
if (!vm->prog) UNLIKELY {
|
||||
vm->errCode = BGPEVMNOPROG;
|
||||
goto cant_run;
|
||||
}
|
||||
|
||||
// Setup initial VM state
|
||||
Boolean result = TRUE; // assume PASS unless CFAIL says otherwise
|
||||
|
||||
vm->pc = 0;
|
||||
vm->si = 0;
|
||||
vm->nblk = 0;
|
||||
vm->nmatches = 0;
|
||||
vm->hHighMark = vm->hMemSiz;
|
||||
vm->msg = msg;
|
||||
vm->curMatch = &discardMatch;
|
||||
vm->matches = NULL;
|
||||
vm->errCode = BGPENOERR;
|
||||
|
||||
// Populate computed goto table if necessary
|
||||
#ifdef DF_BGP_VM_USES_COMPUTED_GOTO
|
||||
#include "bgp/vm_optab.h"
|
||||
#endif
|
||||
|
||||
// Execute bytecode according to the #included vm_<impl>def.h
|
||||
Bgpvmbytec ir; // Instruction Register
|
||||
|
||||
vm->isRunning = TRUE;
|
||||
while (TRUE) {
|
||||
// FETCH stage
|
||||
FETCH(ir, vm);
|
||||
|
||||
// DECODE-DISPATCH stage
|
||||
DISPATCH(BGP_VMOPC(ir)) {
|
||||
// EXECUTE stage
|
||||
EXECUTE(NOP): UNLIKELY;
|
||||
break;
|
||||
EXECUTE(LOAD):
|
||||
Bgp_VmDoLoad(vm, (Sint8) BGP_VMOPARG(ir));
|
||||
break;
|
||||
EXECUTE(LOADU):
|
||||
Bgp_VmDoLoadu(vm, BGP_VMOPARG(ir));
|
||||
break;
|
||||
EXECUTE(LOADN):
|
||||
Bgp_VmDoLoadn(vm);
|
||||
break;
|
||||
EXECUTE(LOADK):
|
||||
Bgp_VmDoLoadk(vm, BGP_VMOPARG(ir));
|
||||
break;
|
||||
EXECUTE(CALL):
|
||||
Bgp_VmDoCall(vm, BGP_VMOPARG(ir));
|
||||
break;
|
||||
EXECUTE(BLK):
|
||||
vm->nblk++;
|
||||
break;
|
||||
EXECUTE(ENDBLK):
|
||||
if (vm->nblk == 0) UNLIKELY {
|
||||
vm->errCode = BGPEVMBADENDBLK;
|
||||
goto terminate;
|
||||
}
|
||||
|
||||
vm->nblk--;
|
||||
break;
|
||||
EXECUTE(TAG):
|
||||
Bgp_VmDoTag(vm, BGP_VMOPARG(ir));
|
||||
break;
|
||||
EXECUTE(NOT):
|
||||
Bgp_VmDoNot(vm);
|
||||
|
||||
EXPECT(CFAIL, ir, vm);
|
||||
EXPECT(CPASS, ir, vm);
|
||||
break;
|
||||
EXECUTE(CFAIL):
|
||||
if (Bgp_VmDoCfail(vm)) {
|
||||
result = FALSE; // immediate terminate on FAIL
|
||||
goto terminate;
|
||||
}
|
||||
|
||||
break;
|
||||
EXECUTE(CPASS):
|
||||
if (Bgp_VmDoCpass(vm))
|
||||
goto terminate; // immediate PASS
|
||||
|
||||
break;
|
||||
EXECUTE(JZ):
|
||||
if (!BGP_VMCHKSTKSIZ(vm, 1)) UNLIKELY
|
||||
break;
|
||||
|
||||
if (!BGP_VMPEEK(vm, -1)) {
|
||||
// Zero, do jump
|
||||
vm->pc += BGP_VMOPARG(ir);
|
||||
if (vm->pc > vm->progLen) UNLIKELY
|
||||
vm->errCode = BGPEVMBADJMP; // jump target out of bounds
|
||||
|
||||
} else
|
||||
BGP_VMPOP(vm); // no jump, pop the stack and move on
|
||||
|
||||
break;
|
||||
EXECUTE(JNZ):
|
||||
if (!BGP_VMCHKSTKSIZ(vm, 1)) UNLIKELY
|
||||
break;
|
||||
|
||||
if (BGP_VMPEEK(vm, -1)) {
|
||||
// Non-Zero, do jump
|
||||
vm->pc += BGP_VMOPARG(ir);
|
||||
if (vm->pc > vm->progLen) UNLIKELY
|
||||
vm->errCode = BGPEVMBADJMP; // jump target out of bounds
|
||||
|
||||
} else
|
||||
BGP_VMPOP(vm); // no jump, pop the stack and move on
|
||||
|
||||
break;
|
||||
EXECUTE(CHKT):
|
||||
Bgp_VmDoChkt(vm, (BgpType) BGP_VMOPARG(ir));
|
||||
break;
|
||||
EXECUTE(CHKA):
|
||||
Bgp_VmDoChka(vm, (BgpAttrCode) BGP_VMOPARG(ir));
|
||||
break;
|
||||
EXECUTE(EXCT):
|
||||
Bgp_VmDoExct(vm, BGP_VMOPARG(ir));
|
||||
break;
|
||||
EXECUTE(SUPN):
|
||||
Bgp_VmDoSupn(vm, BGP_VMOPARG(ir));
|
||||
break;
|
||||
EXECUTE(SUBN):
|
||||
Bgp_VmDoSubn(vm, BGP_VMOPARG(ir));
|
||||
break;
|
||||
EXECUTE(RELT):
|
||||
Bgp_VmDoRelt(vm, BGP_VMOPARG(ir));
|
||||
break;
|
||||
EXECUTE(ASMTCH):
|
||||
Bgp_VmDoAsmtch(vm);
|
||||
break;
|
||||
EXECUTE(FASMTC):
|
||||
Bgp_VmDoFasmtc(vm);
|
||||
break;
|
||||
EXECUTE(COMTCH):
|
||||
Bgp_VmDoComtch(vm);
|
||||
break;
|
||||
EXECUTE(ACOMTC):
|
||||
Bgp_VmDoAcomtc(vm);
|
||||
break;
|
||||
EXECUTE(END): UNLIKELY;
|
||||
// Implicitly PASS current match
|
||||
vm->curMatch->isPassing = TRUE;
|
||||
goto terminate;
|
||||
EXECUTE_SIGILL: UNLIKELY;
|
||||
vm->errCode = BGPEVMILL;
|
||||
break;
|
||||
}
|
||||
|
||||
if (vm->errCode) UNLIKELY
|
||||
goto terminate; // error encountered, abort execution
|
||||
}
|
||||
|
||||
terminate:
|
||||
vm->curMatch = NULL; // prevent accidental access outside Bgp_VmExec()
|
||||
vm->isRunning = FALSE;
|
||||
|
||||
if (Bgp_SetErrStat(vm->errCode) != OK) UNLIKELY
|
||||
result = FALSE;
|
||||
|
||||
return result;
|
||||
|
||||
cant_run:
|
||||
Bgp_SetErrStat(vm->errCode);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
Judgement Bgp_VmStoreMsgTypeMatch(Bgpvm *vm, Boolean isMatching)
|
||||
{
|
||||
vm->curMatch = (Bgpvmmatch *) Bgp_VmTempAlloc(vm, sizeof(*vm->curMatch));
|
||||
if (!vm->curMatch) UNLIKELY
|
||||
return NG;
|
||||
|
||||
Bgphdr *hdr = BGP_HDR(vm->msg);
|
||||
|
||||
vm->curMatch->pc = BGP_VMCURPC(vm);
|
||||
vm->curMatch->tag = 0;
|
||||
vm->curMatch->base = (Uint8 *) hdr;
|
||||
vm->curMatch->lim = (Uint8 *) (hdr + 1);
|
||||
vm->curMatch->pos = &hdr->type;
|
||||
vm->curMatch->isMatching = isMatching;
|
||||
|
||||
Bgp_VmStoreMatch(vm);
|
||||
return OK;
|
||||
}
|
||||
|
||||
void Bgp_VmStoreMatch(Bgpvm *vm)
|
||||
{
|
||||
assert(vm->isRunning);
|
||||
|
||||
if (vm->curMatch == &discardMatch)
|
||||
return; // discard store request
|
||||
|
||||
// Prepend match to matches list, still keep the `curMatch` pointer
|
||||
// around in case result is updated by following instructions (e.g. NOT)
|
||||
vm->curMatch->nextMatch = vm->matches;
|
||||
vm->matches = vm->curMatch;
|
||||
vm->nmatches++;
|
||||
}
|
||||
|
||||
Boolean Bgp_VmDoCpass(Bgpvm *vm)
|
||||
{
|
||||
/* POPS:
|
||||
* -1: Last operation Boolean result (as a Sint64, 0 for FALSE)
|
||||
*
|
||||
* PUSHES:
|
||||
* * On PASS result:
|
||||
* - TRUE
|
||||
* * Otherwise:
|
||||
* - Nothing.
|
||||
*
|
||||
* SIDE-EFFECTS:
|
||||
* - Breaks current BLK on PASS
|
||||
* - Updates `curMatch->isPassing` flag accordingly
|
||||
*/
|
||||
|
||||
if (!BGP_VMCHKSTKSIZ(vm, 1)) UNLIKELY
|
||||
return FALSE; // error, let vm->errCode handle this
|
||||
|
||||
Boolean shouldTerm = FALSE; // unless proven otherwise
|
||||
|
||||
// If stack top is non-zero we FAIL
|
||||
if (BGP_VMPEEK(vm, -1)) {
|
||||
// Leave TRUE on stack and break current BLK, this is a PASS
|
||||
vm->curMatch->isPassing = TRUE;
|
||||
|
||||
if (vm->nblk > 0)
|
||||
Bgp_VmDoBreak(vm);
|
||||
else
|
||||
shouldTerm = TRUE; // no more BLK
|
||||
|
||||
} else {
|
||||
// Pop the stack and move on, no PASS
|
||||
vm->curMatch->isPassing = FALSE;
|
||||
BGP_VMPOP(vm);
|
||||
}
|
||||
|
||||
// Current match information has been collected,
|
||||
// discard anything up to the next relevant operation
|
||||
vm->curMatch = &discardMatch;
|
||||
|
||||
return shouldTerm;
|
||||
}
|
||||
|
||||
Boolean Bgp_VmDoCfail(Bgpvm *vm)
|
||||
{
|
||||
/* POPS:
|
||||
* -1: Last operation Boolean result (as a Sint64, 0 for FALSE)
|
||||
*
|
||||
* PUSHES:
|
||||
* * On FAIL result:
|
||||
* - FALSE
|
||||
* * Otherwise:
|
||||
* - Nothing.
|
||||
*
|
||||
* SIDE-EFFECTS:
|
||||
* - Breaks current BLK on FAIL
|
||||
* - Updates `curMatch->isPassing` flag accordingly
|
||||
*/
|
||||
|
||||
if (!BGP_VMCHKSTKSIZ(vm, 1)) UNLIKELY
|
||||
return FALSE; // error, let vm->errCode handle this
|
||||
|
||||
Boolean shouldTerm = FALSE; // unless proven otherwise
|
||||
|
||||
// If stack top is non-zero we FAIL
|
||||
Bgpvmval *v = BGP_VMSTKGET(vm, -1);
|
||||
if (v->val) {
|
||||
// Push FALSE and break current BLK, this is a FAIL
|
||||
vm->curMatch->isPassing = FALSE;
|
||||
|
||||
v->val = FALSE;
|
||||
if (vm->nblk > 0)
|
||||
Bgp_VmDoBreak(vm);
|
||||
else
|
||||
shouldTerm = TRUE; // no more BLK
|
||||
|
||||
} else {
|
||||
// Pop the stack and move on, no FAIL
|
||||
vm->curMatch->isPassing = TRUE;
|
||||
BGP_VMPOP(vm);
|
||||
}
|
||||
|
||||
// Current match information has been collected,
|
||||
// discard anything up to the next relevant operation
|
||||
vm->curMatch = &discardMatch;
|
||||
|
||||
return shouldTerm;
|
||||
}
|
||||
|
||||
void Bgp_VmDoChkt(Bgpvm *vm, BgpType type)
|
||||
{
|
||||
/* PUSHES:
|
||||
* TRUE if type matches, FALSE otherwise
|
||||
*/
|
||||
|
||||
if (!BGP_VMCHKSTK(vm, 1)) UNLIKELY
|
||||
return;
|
||||
|
||||
Boolean isMatching = (BGP_VMCHKMSGTYPE(vm, type) != NULL);
|
||||
|
||||
BGP_VMPUSH(vm, isMatching);
|
||||
Bgp_VmStoreMsgTypeMatch(vm, isMatching);
|
||||
}
|
||||
|
||||
void Bgp_VmDoChka(Bgpvm *vm, BgpAttrCode code)
|
||||
{
|
||||
/* PUSHES:
|
||||
* TRUE if attribute exists inside UPDATE message, FALSE otherwise
|
||||
*/
|
||||
|
||||
if (!BGP_VMCHKSTK(vm, 1)) UNLIKELY
|
||||
return;
|
||||
|
||||
Bgpupdate *update = (Bgpupdate *) BGP_VMCHKMSGTYPE(vm, BGP_UPDATE);
|
||||
if (!update) {
|
||||
Bgp_VmStoreMsgTypeMatch(vm, /*isMatching=*/FALSE);
|
||||
return;
|
||||
}
|
||||
|
||||
// Attribute lookup
|
||||
Bgpattrseg *tpa = Bgp_GetUpdateAttributes(update);
|
||||
if (!tpa) {
|
||||
vm->errCode = BGPEVMMSGERR;
|
||||
return;
|
||||
}
|
||||
|
||||
Bgpattr *attr = Bgp_GetUpdateAttribute(tpa, code, vm->msg->table);
|
||||
if (Bgp_GetErrStat(NULL)) {
|
||||
vm->errCode = BGPEVMMSGERR;
|
||||
return;
|
||||
}
|
||||
|
||||
Boolean isMatching = (attr != NULL);
|
||||
BGP_VMPUSH(vm, isMatching);
|
||||
|
||||
// Create a new match
|
||||
vm->curMatch = (Bgpvmmatch *) Bgp_VmTempAlloc(vm, sizeof(*vm->curMatch));
|
||||
if (!vm->curMatch) UNLIKELY
|
||||
return;
|
||||
|
||||
vm->curMatch->pc = BGP_VMCURPC(vm);
|
||||
vm->curMatch->tag = 0;
|
||||
vm->curMatch->base = tpa->attrs;
|
||||
vm->curMatch->lim = &tpa->attrs[beswap16(tpa->len)];
|
||||
vm->curMatch->pos = attr;
|
||||
vm->curMatch->isMatching = isMatching;
|
||||
|
||||
Bgp_VmStoreMatch(vm);
|
||||
}
|
||||
|
||||
static Judgement Bgp_VmStartNets(Bgpvm *vm, Bgpmpiter *it, Uint8 mode)
|
||||
{
|
||||
if (!BGP_VMCHKMSGTYPE(vm, BGP_UPDATE)) {
|
||||
Bgp_VmStoreMsgTypeMatch(vm, /*isMatching=*/FALSE);
|
||||
return NG;
|
||||
}
|
||||
|
||||
switch (mode) {
|
||||
case BGP_VMOPA_NLRI:
|
||||
Bgp_StartMsgNlri(&it->rng, vm->msg);
|
||||
it->nextAttr = NULL;
|
||||
break;
|
||||
case BGP_VMOPA_WITHDRAWN:
|
||||
Bgp_StartMsgWithdrawn(&it->rng, vm->msg);
|
||||
it->nextAttr = NULL;
|
||||
break;
|
||||
case BGP_VMOPA_ALL_NLRI:
|
||||
Bgp_StartAllMsgNlri(it, vm->msg);
|
||||
break;
|
||||
case BGP_VMOPA_ALL_WITHDRAWN:
|
||||
Bgp_StartAllMsgWithdrawn(it, vm->msg);
|
||||
break;
|
||||
|
||||
default: UNLIKELY;
|
||||
vm->errCode = BGPEVMBADOP;
|
||||
return NG;
|
||||
}
|
||||
if (Bgp_GetErrStat(NULL)) UNLIKELY {
|
||||
vm->errCode = BGPEVMMSGERR;
|
||||
return NG;
|
||||
}
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
static void Bgp_VmCollectNetMatch(Bgpvm *vm, Bgpmpiter *it)
|
||||
{
|
||||
// Push on stack first --
|
||||
// we know we have at least one available spot on the stack for
|
||||
// any network operation (we POP at least one Patricia address from it).
|
||||
// Perform the temporary allocation afterwards.
|
||||
// This saves one check on stack space
|
||||
BGP_VMPUSH(vm, TRUE);
|
||||
|
||||
vm->curMatch = (Bgpvmmatch *) Bgp_VmTempAlloc(vm, sizeof(*vm->curMatch));
|
||||
if (!vm->curMatch) UNLIKELY
|
||||
return;
|
||||
|
||||
vm->curMatch->pc = BGP_VMCURPC(vm);
|
||||
vm->curMatch->tag = 0;
|
||||
vm->curMatch->base = BGP_CURMPBASE(it);
|
||||
vm->curMatch->lim = BGP_CURMPLIM(it);
|
||||
vm->curMatch->pos = BGP_CURMPPFX(it);
|
||||
vm->curMatch->isMatching = TRUE;
|
||||
|
||||
Bgp_VmStoreMatch(vm);
|
||||
}
|
||||
|
||||
static void Bgp_VmNetMatchFailed(Bgpvm *vm)
|
||||
{
|
||||
BGP_VMPUSH(vm, FALSE); // NOTE: See `Bgp_VmCollectNetMatch()`
|
||||
|
||||
vm->curMatch = (Bgpvmmatch *) Bgp_VmTempAlloc(vm, sizeof(*vm->curMatch));
|
||||
if (!vm->curMatch) UNLIKELY
|
||||
return;
|
||||
|
||||
vm->curMatch->pc = BGP_VMCURPC(vm);
|
||||
vm->curMatch->tag = 0;
|
||||
vm->curMatch->base = NULL;
|
||||
vm->curMatch->lim = NULL;
|
||||
vm->curMatch->pos = NULL;
|
||||
vm->curMatch->isMatching = FALSE;
|
||||
|
||||
Bgp_VmStoreMatch(vm);
|
||||
}
|
||||
|
||||
static const Patricia emptyTrie4 = { AFI_IP };
|
||||
static const Patricia emptyTrie6 = { AFI_IP6 };
|
||||
|
||||
void Bgp_VmDoExct(Bgpvm *vm, Uint8 arg)
|
||||
{
|
||||
// POPS:
|
||||
// -1: AFI_IP6 Patricia (may be NULL)
|
||||
// -2: AFI_IP Patricia (may be NULL)
|
||||
//
|
||||
// PUSHES:
|
||||
// TRUE on EXACT match of any network prefix in message, FALSE otherwise
|
||||
|
||||
if (!BGP_VMCHKSTKSIZ(vm, 2)) UNLIKELY
|
||||
return;
|
||||
|
||||
Bgpmpiter it;
|
||||
const Patricia *trie6 = (Patricia *) BGP_VMPOPA(vm);
|
||||
const Patricia *trie = (Patricia *) BGP_VMPOPA(vm);
|
||||
if (!trie6) trie6 = &emptyTrie6;
|
||||
if (!trie) trie = &emptyTrie4;
|
||||
|
||||
if (trie->afi != AFI_IP || trie6->afi != AFI_IP6) UNLIKELY {
|
||||
vm->errCode = BGPEVMBADOP;
|
||||
return;
|
||||
}
|
||||
if (Bgp_VmStartNets(vm, &it, arg) != OK) UNLIKELY
|
||||
return; // error already set
|
||||
|
||||
Prefix *pfx;
|
||||
while ((pfx = Bgp_NextMpPrefix(&it)) != NULL) {
|
||||
RawPrefix *match = NULL;
|
||||
|
||||
// Select appropriate PATRICIA
|
||||
switch (pfx->afi) {
|
||||
case AFI_IP:
|
||||
match = Pat_SearchExact(trie, PLAINPFX(pfx));
|
||||
break;
|
||||
case AFI_IP6:
|
||||
match = Pat_SearchExact(trie6, PLAINPFX(pfx));
|
||||
break;
|
||||
|
||||
default: UNREACHABLE; return;
|
||||
}
|
||||
|
||||
if (match) {
|
||||
Bgp_VmCollectNetMatch(vm, &it);
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (Bgp_GetErrStat(NULL)) {
|
||||
vm->errCode = BGPEVMMSGERR;
|
||||
return;
|
||||
}
|
||||
|
||||
Bgp_VmNetMatchFailed(vm);
|
||||
}
|
||||
|
||||
void Bgp_VmDoSubn(Bgpvm *vm, Uint8 arg)
|
||||
{
|
||||
// POPS:
|
||||
// -1: AFI_IP6 Patricia (may be NULL)
|
||||
// -2: AFI_IP Patricia (may be NULL)
|
||||
//
|
||||
// PUSHES:
|
||||
// TRUE on SUBN match of any network prefix in message, FALSE otherwise
|
||||
|
||||
if (!BGP_VMCHKSTKSIZ(vm, 2)) UNLIKELY
|
||||
return;
|
||||
|
||||
Bgpmpiter it;
|
||||
const Patricia *trie6 = (Patricia *) BGP_VMPOPA(vm);
|
||||
const Patricia *trie = (Patricia *) BGP_VMPOPA(vm);
|
||||
if (!trie6) trie6 = &emptyTrie6;
|
||||
if (!trie) trie = &emptyTrie4;
|
||||
|
||||
if (trie->afi != AFI_IP || trie6->afi != AFI_IP6 ) UNLIKELY {
|
||||
vm->errCode = BGPEVMBADOP;
|
||||
return;
|
||||
}
|
||||
if (Bgp_VmStartNets(vm, &it, arg) != OK) UNLIKELY
|
||||
return; // error already set
|
||||
|
||||
Prefix *pfx;
|
||||
while ((pfx = Bgp_NextMpPrefix(&it)) != NULL) {
|
||||
Boolean isMatching = FALSE;
|
||||
|
||||
// Select appropriate PATRICIA
|
||||
switch (pfx->afi) {
|
||||
case AFI_IP:
|
||||
isMatching = Pat_IsSubnetOf(trie, PLAINPFX(pfx));
|
||||
break;
|
||||
case AFI_IP6:
|
||||
isMatching = Pat_IsSubnetOf(trie6, PLAINPFX(pfx));
|
||||
break;
|
||||
|
||||
default: UNREACHABLE; return;
|
||||
}
|
||||
|
||||
if (isMatching) {
|
||||
Bgp_VmCollectNetMatch(vm, &it);
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (Bgp_GetErrStat(NULL)) {
|
||||
vm->errCode = BGPEVMMSGERR;
|
||||
return;
|
||||
}
|
||||
|
||||
Bgp_VmNetMatchFailed(vm);
|
||||
}
|
||||
|
||||
void Bgp_VmDoSupn(Bgpvm *vm, Uint8 arg)
|
||||
{
|
||||
// POPS:
|
||||
// -1: AFI_IP6 Patricia (may be NULL)
|
||||
// -2: AFI_IP Patricia (may be NULL)
|
||||
//
|
||||
// PUSHES:
|
||||
// TRUE on SUPN match of any network prefix in message, FALSE otherwise
|
||||
|
||||
if (!BGP_VMCHKSTKSIZ(vm, 2)) UNLIKELY
|
||||
return;
|
||||
|
||||
Bgpmpiter it;
|
||||
const Patricia *trie6 = (Patricia *) BGP_VMPOPA(vm);
|
||||
const Patricia *trie = (Patricia *) BGP_VMPOPA(vm);
|
||||
if (!trie6) trie6 = &emptyTrie6;
|
||||
if (!trie) trie = &emptyTrie4;
|
||||
|
||||
if (trie->afi != AFI_IP || trie6->afi != AFI_IP6) UNLIKELY {
|
||||
vm->errCode = BGPEVMBADOP;
|
||||
return;
|
||||
}
|
||||
if (Bgp_VmStartNets(vm, &it, arg) != OK) UNLIKELY
|
||||
return; // error already set
|
||||
|
||||
Prefix *pfx;
|
||||
while ((pfx = Bgp_NextMpPrefix(&it)) != NULL) {
|
||||
Boolean isMatching = FALSE;
|
||||
|
||||
// Select appropriate PATRICIA
|
||||
switch (pfx->afi) {
|
||||
case AFI_IP:
|
||||
isMatching = Pat_IsSupernetOf(trie, PLAINPFX(pfx));
|
||||
break;
|
||||
|
||||
case AFI_IP6:
|
||||
isMatching = Pat_IsSupernetOf(trie6, PLAINPFX(pfx));
|
||||
break;
|
||||
|
||||
default: UNREACHABLE; return;
|
||||
}
|
||||
|
||||
if (isMatching) {
|
||||
Bgp_VmCollectNetMatch(vm, &it);
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (Bgp_GetErrStat(NULL)) {
|
||||
vm->errCode = BGPEVMMSGERR;
|
||||
return;
|
||||
}
|
||||
|
||||
Bgp_VmNetMatchFailed(vm);
|
||||
}
|
||||
|
||||
void Bgp_VmDoRelt(Bgpvm *vm, Uint8 arg)
|
||||
{
|
||||
// POPS:
|
||||
// -1: AFI_IP6 Patricia (may be NULL)
|
||||
// -2: AFI_IP Patricia (may be NULL)
|
||||
//
|
||||
// PUSHES:
|
||||
// TRUE on SUPN match of any network prefix in message, FALSE otherwise
|
||||
|
||||
if (!BGP_VMCHKSTKSIZ(vm, 2)) UNLIKELY
|
||||
return;
|
||||
|
||||
Bgpmpiter it;
|
||||
const Patricia *trie6 = (Patricia *) BGP_VMPOPA(vm);
|
||||
const Patricia *trie = (Patricia *) BGP_VMPOPA(vm);
|
||||
if (!trie6) trie6 = &emptyTrie6;
|
||||
if (!trie) trie = &emptyTrie4;
|
||||
|
||||
if (trie->afi != AFI_IP || trie6->afi != AFI_IP6) UNLIKELY {
|
||||
vm->errCode = BGPEVMBADOP;
|
||||
return;
|
||||
}
|
||||
if (Bgp_VmStartNets(vm, &it, arg) != OK) UNLIKELY
|
||||
return; // error already set
|
||||
|
||||
Prefix *pfx;
|
||||
while ((pfx = Bgp_NextMpPrefix(&it)) != NULL) {
|
||||
Boolean isMatching = FALSE;
|
||||
|
||||
// Select appropriate PATRICIA
|
||||
switch (pfx->afi) {
|
||||
case AFI_IP:
|
||||
isMatching = Pat_IsRelatedOf(trie, PLAINPFX(pfx));
|
||||
break;
|
||||
case AFI_IP6:
|
||||
isMatching = Pat_IsRelatedOf(trie6, PLAINPFX(pfx));
|
||||
break;
|
||||
|
||||
default: UNREACHABLE; return;
|
||||
}
|
||||
|
||||
if (isMatching) {
|
||||
Bgp_VmCollectNetMatch(vm, &it);
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (Bgp_GetErrStat(NULL)) {
|
||||
vm->errCode = BGPEVMMSGERR;
|
||||
return;
|
||||
}
|
||||
|
||||
Bgp_VmNetMatchFailed(vm);
|
||||
}
|
||||
|
||||
void Bgp_ResetVm(Bgpvm *vm)
|
||||
{
|
||||
assert(!vm->isRunning);
|
||||
|
||||
vm->nk = 0;
|
||||
vm->nfuncs = 0;
|
||||
vm->nmatches = 0;
|
||||
vm->progLen = 0;
|
||||
vm->hLowMark = 0;
|
||||
vm->hHighMark = vm->hMemSiz;
|
||||
|
||||
BGP_VMCLRSETUP(vm);
|
||||
BGP_VMCLRERR(vm);
|
||||
|
||||
memset(vm->k, 0, sizeof(vm->k));
|
||||
memset(vm->funcs, 0, sizeof(vm->funcs));
|
||||
|
||||
vm->matches = NULL;
|
||||
}
|
||||
|
||||
void Bgp_ClearVm(Bgpvm *vm)
|
||||
{
|
||||
assert(!vm->isRunning);
|
||||
|
||||
free(vm->heap);
|
||||
free(vm->prog);
|
||||
}
|
834
lonetix/bgp/vm_asmtch.c
Normal file
834
lonetix/bgp/vm_asmtch.c
Normal file
@@ -0,0 +1,834 @@
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
|
||||
/**
|
||||
* \file bgp/vm_asmtch.c
|
||||
*
|
||||
* Implements ASMTCH and FASMTC, and ASN match IR compilation.
|
||||
*
|
||||
* \copyright The DoubleFourteen Code Forge (C) All Rights Reserved
|
||||
* \author Lorenzo Cogotti
|
||||
*
|
||||
* This code is a modified version of the Plan9 regexp library matching algorithm.
|
||||
* The Plan9 regexp library is available under the Lucent Public License
|
||||
* at: https://9fans.github.io/plan9port/unix/libregexp9.tgz
|
||||
*
|
||||
* \see [Regular Expression Matching Can Be Simple And Fast](https://swtch.com/~rsc/regexp/regexp1.html)
|
||||
*/
|
||||
|
||||
#include "bgp/vmintrin.h"
|
||||
#include "sys/endian.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <setjmp.h>
|
||||
#include <string.h>
|
||||
|
||||
// Define this to dump the compiled AS MATCH expressions to stderr
|
||||
//#define DF_DEBUG_ASMTCH
|
||||
#ifdef NDEBUG
|
||||
// Force DF_DEBUG_ASMTCH off on release build
|
||||
#undef DF_DEBUG_ASMTCH
|
||||
#endif
|
||||
|
||||
#ifdef DF_DEBUG_ASMTCH
|
||||
#include "sys/con.h"
|
||||
#endif
|
||||
|
||||
#define AS 126
|
||||
#define NAS 127
|
||||
#define START 128 // start, used for marker on stack
|
||||
#define RPAR 129 // right parens, )
|
||||
#define LPAR 130 // left parens, (
|
||||
#define ALT 131 // alternation, |
|
||||
#define CAT 132 // concatentation, implicit operator
|
||||
#define STAR 133 // closure, *
|
||||
#define PLUS 134 // a+ == aa*
|
||||
#define QUEST 135 // a? == a|nothing, i.e. 0 or 1 a's
|
||||
#define ANY 192 // any character except newline, .
|
||||
#define NOP 193 // no operation
|
||||
#define BOL 194 // beginning of line, ^
|
||||
#define EOL 195 // end of line, $
|
||||
#define STOP 255 // terminate: match found
|
||||
|
||||
#define BOTTOM (START - 1)
|
||||
|
||||
#define NSTACK 32
|
||||
#define LISTSIZ 10
|
||||
#define BIGLISTSIZ (32 * LISTSIZ)
|
||||
|
||||
#define ASNBOLFLAG BIT(60) // used to mark the initial ASN, so that BOL operator works
|
||||
|
||||
#define ASN_EOL -1 // equals to -1 returned by Bgp_NextAsPath()
|
||||
|
||||
// Automaton instruction.
|
||||
typedef struct Nfainst Nfainst;
|
||||
struct Nfainst{
|
||||
int type; // instruction type, any of the macros above
|
||||
|
||||
union { // NOTE: keep `next` and `left` in the same union!
|
||||
Nfainst *next; // next instruction in chain
|
||||
Nfainst *left; // left output state, for ALT instructions
|
||||
};
|
||||
union {
|
||||
Uint32 asn; // ASN match, for AS/NAS instructions
|
||||
Uint16 grpid; // match group id, for LPAR/RPAR instructions
|
||||
Nfainst *right; // right output state, for ALT instructions
|
||||
};
|
||||
};
|
||||
|
||||
// A block of instructions (used during NFA construction)
|
||||
typedef struct Nfanode Nfanode;
|
||||
struct Nfanode {
|
||||
Nfainst *first, *last;
|
||||
};
|
||||
|
||||
// Start and end position of a subexpression match
|
||||
typedef struct {
|
||||
Aspathiter spos;
|
||||
Aspathiter epos;
|
||||
} Nfamatch;
|
||||
|
||||
typedef struct Nfastate Nfastate;
|
||||
struct Nfastate {
|
||||
Nfainst *ip;
|
||||
Nfamatch se[MAXBGPVMASNGRP];
|
||||
};
|
||||
|
||||
// State list used during simulation (clist, nlist)
|
||||
typedef struct Nfalist Nfalist;
|
||||
struct Nfalist {
|
||||
unsigned ns, lim;
|
||||
Nfastate list[FLEX_ARRAY];
|
||||
};
|
||||
|
||||
typedef struct Nfacomp Nfacomp;
|
||||
struct Nfacomp {
|
||||
Nfainst *basep; // base instruction pointer
|
||||
Nfainst *freep; // next free instruction pointer
|
||||
Nfanode andstack[NSTACK]; // operands stack
|
||||
Uint16 grpidstack[NSTACK]; // subexpression id stack
|
||||
Uint8 opstack[NSTACK]; // operators stack
|
||||
Boolean8 lastWasAnd; // whether last encountered term was an operand
|
||||
Uint16 nands, nops, ngrpids; // counters inside stacks
|
||||
Uint16 nparens; // currently encountered open parens counter
|
||||
Uint16 curgrpid; // next group id
|
||||
jmp_buf oops; // compilation error jump
|
||||
};
|
||||
|
||||
typedef struct Nfa Nfa;
|
||||
struct Nfa {
|
||||
Aspathiter spos; // Current ASN iterator start position
|
||||
Aspathiter cur; // Current ASN iterator position
|
||||
unsigned nmatches;
|
||||
Nfamatch se[MAXBGPVMASNGRP];
|
||||
};
|
||||
|
||||
static NORETURN void comperr(Nfacomp *nc, BgpvmRet err)
|
||||
{
|
||||
assert(err != BGPENOERR);
|
||||
|
||||
longjmp(nc->oops, err);
|
||||
}
|
||||
|
||||
static Nfainst *newinst(Nfacomp *nc, int t)
|
||||
{
|
||||
Nfainst *i = nc->freep++;
|
||||
|
||||
i->type = t;
|
||||
i->left = i->right = NULL;
|
||||
return i;
|
||||
}
|
||||
|
||||
static void pushator(Nfacomp *nc, Asn t)
|
||||
{
|
||||
if (nc->nops == NSTACK)
|
||||
comperr(nc, BGPEVMBADASMTCH);
|
||||
|
||||
nc->opstack[nc->nops++] = t;
|
||||
nc->grpidstack[nc->ngrpids++] = nc->curgrpid;
|
||||
}
|
||||
|
||||
static Nfanode *pushand(Nfacomp *nc, Nfainst *f, Nfainst *l)
|
||||
{
|
||||
if (nc->nands == NSTACK)
|
||||
comperr(nc, BGPEVMBADASMTCH);
|
||||
|
||||
Nfanode *n = &nc->andstack[nc->nands++];
|
||||
|
||||
n->first = f;
|
||||
n->last = l;
|
||||
return n;
|
||||
}
|
||||
|
||||
static Nfanode *popand(Nfacomp *nc)
|
||||
{
|
||||
if(nc->nands == 0)
|
||||
comperr(nc, BGPEVMBADASMTCH);
|
||||
|
||||
return &nc->andstack[--nc->nands];
|
||||
}
|
||||
|
||||
static Uint8 popator(Nfacomp *nc)
|
||||
{
|
||||
if (nc->nops == 0)
|
||||
comperr(nc, BGPEVMBADASMTCH);
|
||||
|
||||
--nc->ngrpids;
|
||||
return nc->opstack[--nc->nops];
|
||||
}
|
||||
|
||||
static void evaluntil(Nfacomp *nc, int prio)
|
||||
{
|
||||
Nfanode *op1, *op2;
|
||||
Nfainst *inst1, *inst2;
|
||||
|
||||
while (prio == RPAR || nc->opstack[nc->nops-1] >= prio) {
|
||||
switch (popator(nc)) {
|
||||
default: UNREACHABLE;
|
||||
|
||||
case LPAR:
|
||||
op1 = popand(nc);
|
||||
|
||||
inst2 = newinst(nc, RPAR);
|
||||
inst2->grpid = nc->grpidstack[nc->ngrpids-1];
|
||||
op1->last->next = inst2;
|
||||
|
||||
inst1 = newinst(nc, LPAR);
|
||||
inst1->grpid = nc->grpidstack[nc->ngrpids-1];
|
||||
inst1->next = op1->first;
|
||||
|
||||
pushand(nc, inst1, inst2);
|
||||
return;
|
||||
|
||||
case ALT:
|
||||
op2 = popand(nc), op1 = popand(nc);
|
||||
|
||||
inst2 = newinst(nc, NOP);
|
||||
op2->last->next = inst2;
|
||||
op1->last->next = inst2;
|
||||
|
||||
inst1 = newinst(nc, ALT);
|
||||
inst1->right = op1->first;
|
||||
inst1->left = op2->first;
|
||||
|
||||
pushand(nc, inst1, inst2);
|
||||
break;
|
||||
|
||||
case CAT:
|
||||
op2 = popand(nc), op1 = popand(nc);
|
||||
|
||||
op1->last->next = op2->first;
|
||||
|
||||
pushand(nc, op1->first, op2->last);
|
||||
break;
|
||||
|
||||
case STAR:
|
||||
op2 = popand(nc);
|
||||
|
||||
inst1 = newinst(nc, ALT);
|
||||
op2->last->next = inst1;
|
||||
|
||||
inst1->right = op2->first;
|
||||
|
||||
pushand(nc, inst1, inst1);
|
||||
break;
|
||||
|
||||
case PLUS:
|
||||
op2 = popand(nc);
|
||||
|
||||
inst1 = newinst(nc, ALT);
|
||||
op2->last->next = inst1;
|
||||
|
||||
inst1->right = op2->first;
|
||||
|
||||
pushand(nc, op2->first, inst1);
|
||||
break;
|
||||
|
||||
case QUEST:
|
||||
op2 = popand(nc);
|
||||
|
||||
inst1 = newinst(nc, ALT);
|
||||
inst2 = newinst(nc, NOP);
|
||||
inst1->left = inst2;
|
||||
inst1->right = op2->first;
|
||||
|
||||
op2->last->next = inst2;
|
||||
|
||||
pushand(nc, inst1, inst2);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void operator(Nfacomp *nc, int op)
|
||||
{
|
||||
if (op == RPAR) {
|
||||
if (nc->nparens == 0)
|
||||
comperr(nc, BGPEVMBADASMTCH);
|
||||
|
||||
nc->nparens--;
|
||||
}
|
||||
if (op == LPAR) {
|
||||
nc->curgrpid++;
|
||||
|
||||
if (nc->curgrpid == MAXBGPVMASNGRP)
|
||||
comperr(nc, BGPEVMBADASMTCH);
|
||||
|
||||
nc->nparens++;
|
||||
if (nc->lastWasAnd)
|
||||
operator(nc, CAT); // add implicit CAT before group
|
||||
} else
|
||||
evaluntil(nc, op);
|
||||
|
||||
if (op != RPAR)
|
||||
pushator(nc, op);
|
||||
|
||||
// Some operators behave like operands
|
||||
nc->lastWasAnd = (op == STAR || op == QUEST || op == PLUS || op == RPAR);
|
||||
}
|
||||
|
||||
static void operand(Nfacomp *nc, int t, Asn32 asn)
|
||||
{
|
||||
if (nc->lastWasAnd)
|
||||
operator(nc, CAT); // add implicit CAT
|
||||
|
||||
Nfainst *i = newinst(nc, t);
|
||||
if (t == AS || t == NAS)
|
||||
i->asn = asn;
|
||||
|
||||
pushand(nc, i, i);
|
||||
nc->lastWasAnd = TRUE;
|
||||
}
|
||||
|
||||
static void compinit(Nfacomp *nc, Nfainst *dest)
|
||||
{
|
||||
nc->nands = nc->nops = nc->ngrpids = 0;
|
||||
|
||||
nc->nparens = 0;
|
||||
nc->curgrpid = 0;
|
||||
nc->lastWasAnd = FALSE;
|
||||
nc->basep = nc->freep = dest;
|
||||
}
|
||||
|
||||
static Nfainst *compile(Nfacomp *nc, const Asn *expression, size_t n)
|
||||
{
|
||||
pushator(nc, BOTTOM);
|
||||
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
Asn asn = expression[i];
|
||||
|
||||
switch (asn) {
|
||||
case ASN_START: operand(nc, BOL, 0); break;
|
||||
case ASN_END: operand(nc, EOL, 0); break;
|
||||
case ASN_ANY: operand(nc, ANY, 0); break;
|
||||
case ASN_STAR: operator(nc, STAR); break;
|
||||
case ASN_QUEST: operator(nc, QUEST); break;
|
||||
case ASN_PLUS: operator(nc, PLUS); break;
|
||||
case ASN_NEWGRP: operator(nc, LPAR); break;
|
||||
case ASN_ALT: operator(nc, ALT); break;
|
||||
case ASN_ENDGRP: operator(nc, RPAR); break;
|
||||
default:
|
||||
if (ISASNNOT(asn)) operand(nc, NAS, ASN(asn));
|
||||
else operand(nc, AS, ASN(asn));
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
evaluntil(nc, START);
|
||||
operand(nc, STOP, 0);
|
||||
evaluntil(nc, START);
|
||||
if (nc->nparens != 0)
|
||||
comperr(nc, BGPEVMBADASMTCH);
|
||||
|
||||
return nc->andstack[nc->nands - 1].first;
|
||||
}
|
||||
|
||||
static void optimize(Bgpvm *vm, Nfacomp *nc, size_t bufsiz)
|
||||
{
|
||||
assert(IS_ALIGNED(bufsiz, ALIGNMENT));
|
||||
assert(vm->hLowMark >= bufsiz);
|
||||
|
||||
// Get rid of NOP chains
|
||||
for (Nfainst *i = nc->basep; i->type != STOP; i++) {
|
||||
Nfainst *j = i->next;
|
||||
|
||||
while (j->type == NOP)
|
||||
j = j->next;
|
||||
|
||||
i->next = j;
|
||||
}
|
||||
|
||||
// Initial program allocation is an upperbound, release excess memory
|
||||
size_t siz = (nc->freep - nc->basep) * sizeof(*nc->basep);
|
||||
size_t alsiz = ALIGN(siz, ALIGNMENT);
|
||||
assert(alsiz <= bufsiz);
|
||||
|
||||
vm->hLowMark -= (bufsiz - alsiz);
|
||||
assert(IS_ALIGNED(vm->hLowMark, ALIGNMENT));
|
||||
}
|
||||
|
||||
#ifdef DF_DEBUG_ASMTCH
|
||||
|
||||
static void dumpprog(const Nfainst *prog)
|
||||
{
|
||||
const Nfainst *i = prog;
|
||||
while (TRUE) {
|
||||
Sys_Printf(STDERR, "%d:\t%#2x", (int) (i - prog), (unsigned) i->type);
|
||||
switch (i->type) {
|
||||
case ALT:
|
||||
Sys_Printf(STDERR, "\t%d\t%d", (int) (i->left - prog), (int) (i->right - prog));
|
||||
break;
|
||||
case AS:
|
||||
Sys_Printf(STDERR, "\tASN(%lu)\t%d", (unsigned long) beswap32(i->asn), (int) (i->next - prog));
|
||||
break;
|
||||
case NAS:
|
||||
Sys_Printf(STDERR, "\t!ASN(%lu)\t%d", (unsigned long) beswap32(i->asn), (int) (i->next - prog));
|
||||
break;
|
||||
case LPAR: case RPAR:
|
||||
Sys_Printf(STDERR, "\tGRP(%d)", (int) i->grpid);
|
||||
// FALLTHROUGH
|
||||
default:
|
||||
Sys_Printf(STDERR, "\t%d", (int) (i->next - prog));
|
||||
break;
|
||||
|
||||
case NOP: case STOP:
|
||||
break;
|
||||
}
|
||||
|
||||
Sys_Print(STDERR, "\n");
|
||||
if (i->type == STOP)
|
||||
break;
|
||||
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
// `TRUE` if `pos` comes before `m` starting position
|
||||
static Boolean isbefore(const Aspathiter *pos, const Nfamatch *m)
|
||||
{
|
||||
return BGP_CURASINDEX(pos) < BGP_CURASINDEX(&m->spos);
|
||||
}
|
||||
|
||||
// `TRUE` if `a` starts at the same position as `b`, but terminates after it
|
||||
static Boolean islongermatch(const Nfamatch *a, const Nfamatch *b)
|
||||
{
|
||||
return BGP_CURASINDEX(&a->spos) == BGP_CURASINDEX(&b->spos) &&
|
||||
BGP_CURASINDEX(&a->epos) > BGP_CURASINDEX(&b->epos);
|
||||
}
|
||||
|
||||
/* An invalid AS INDEX, there can't be a 65535 AS index,
|
||||
* Given that an AS is at least 2 bytes wide and a legal TPA segment
|
||||
* is at most of 64K (though in practice its even smaller)
|
||||
*/
|
||||
#define BADASIDX 0xffffu
|
||||
|
||||
static void clearmatch(Nfamatch *m)
|
||||
{
|
||||
m->spos.asIdx = BADASIDX;
|
||||
m->epos.asIdx = 0;
|
||||
}
|
||||
|
||||
static Boolean isnullmatch(const Nfamatch *m)
|
||||
{
|
||||
return BGP_CURASINDEX(&m->spos) == BADASIDX;
|
||||
}
|
||||
|
||||
static void copymatches(Nfamatch *dest, const Nfamatch *src)
|
||||
{
|
||||
do *dest++ = *src; while (!isnullmatch(src++));
|
||||
}
|
||||
|
||||
static Boolean addstartinst(Nfalist *clist, Nfainst *start, const Nfa *nfa)
|
||||
{
|
||||
Nfastate *s;
|
||||
|
||||
// Don't add the instruction twice
|
||||
for (unsigned i = 0; i < clist->ns; i++) {
|
||||
s = &clist->list[i];
|
||||
|
||||
if (s->ip == start) {
|
||||
if (isbefore(&nfa->spos, &s->se[0])) {
|
||||
// Move match position
|
||||
s->se[0].spos = nfa->spos;
|
||||
s->se[0].epos.asIdx = 0; // so any end pos is accepted
|
||||
clearmatch(&s->se[1]);
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
if (clist->ns == clist->lim)
|
||||
return FALSE;
|
||||
|
||||
// Append to list
|
||||
s = &clist->list[clist->ns++];
|
||||
s->ip = start;
|
||||
|
||||
s->se[0].spos = nfa->spos;
|
||||
s->se[0].epos.asIdx = 0; // so any end pos is accepted
|
||||
clearmatch(&s->se[1]);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static Boolean addinst(Nfalist *nlist, Nfainst *in, const Nfamatch *se)
|
||||
{
|
||||
Nfastate *s;
|
||||
|
||||
// Don't add the same instruction twice
|
||||
for (unsigned i = 0; i < nlist->ns; i++) {
|
||||
s = &nlist->list[i];
|
||||
|
||||
if (s->ip == in) {
|
||||
if (isbefore(&se[0].spos, &s->se[0]))
|
||||
copymatches(s->se, se);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
if (nlist->ns == nlist->lim)
|
||||
return FALSE; // instruction list overflow
|
||||
|
||||
// Append to list
|
||||
s = &nlist->list[nlist->ns++];
|
||||
s->ip = in;
|
||||
copymatches(s->se, se);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void newmatch(Nfastate *s, Nfa *nfa)
|
||||
{
|
||||
// Accept the new match if it is the first one, or it comes before
|
||||
// a previous match, or if it is a longer match than the previous one
|
||||
if (nfa->nmatches == 0 ||
|
||||
isbefore(&s->se[0].spos, &nfa->se[0]) ||
|
||||
islongermatch(&s->se[0], &nfa->se[0])) {
|
||||
|
||||
copymatches(nfa->se, s->se);
|
||||
}
|
||||
|
||||
nfa->nmatches++;
|
||||
}
|
||||
|
||||
static Judgement step(Nfalist *clist, Asn asn, Nfalist *nlist, Nfa *nfa)
|
||||
{
|
||||
nlist->ns = 0;
|
||||
|
||||
for (unsigned i = 0; i < clist->ns; i++) {
|
||||
Nfastate *s = &clist->list[i];
|
||||
Nfainst *ip = s->ip;
|
||||
|
||||
eval_next:
|
||||
switch (ip->type) {
|
||||
default: UNREACHABLE;
|
||||
|
||||
case NOP:
|
||||
ip = ip->next;
|
||||
goto eval_next;
|
||||
case AS:
|
||||
if (ip->asn == ASN(asn) && !addinst(nlist, ip->next, s->se))
|
||||
return NG;
|
||||
|
||||
break;
|
||||
case NAS:
|
||||
if (ip->asn != ASN(asn) && !addinst(nlist, ip->next, s->se))
|
||||
return NG;
|
||||
|
||||
break;
|
||||
case ANY:
|
||||
if (asn != ASN_EOL && !addinst(nlist, ip->next, s->se))
|
||||
return NG;
|
||||
|
||||
break;
|
||||
case BOL:
|
||||
if (asn & ASNBOLFLAG) {
|
||||
ip = ip->next;
|
||||
goto eval_next;
|
||||
}
|
||||
|
||||
break;
|
||||
case EOL:
|
||||
if (asn == ASN_EOL) {
|
||||
ip = ip->next;
|
||||
goto eval_next;
|
||||
}
|
||||
|
||||
break;
|
||||
case LPAR:
|
||||
for (unsigned i = 0; i < ip->grpid; i++)
|
||||
assert(!isnullmatch(&s->se[i]));
|
||||
|
||||
assert(isnullmatch(&s->se[ip->grpid]));
|
||||
|
||||
s->se[ip->grpid].spos = nfa->spos;
|
||||
clearmatch(&s->se[ip->grpid+1]);
|
||||
|
||||
ip = ip->next;
|
||||
goto eval_next;
|
||||
case ALT:
|
||||
// Evaluate right branch later IN THIS LIST
|
||||
if (!addinst(clist, ip->right, s->se))
|
||||
return NG;
|
||||
|
||||
ip = ip->left; // take left branch now
|
||||
goto eval_next;
|
||||
case RPAR:
|
||||
for (unsigned i = 0; i < ip->grpid; i++)
|
||||
assert(!isnullmatch(&s->se[i]));
|
||||
|
||||
assert(!isnullmatch(&s->se[ip->grpid]));
|
||||
assert( isnullmatch(&s->se[ip->grpid+1]));
|
||||
|
||||
s->se[ip->grpid].epos = nfa->cur;
|
||||
ip = ip->next;
|
||||
goto eval_next;
|
||||
case STOP:
|
||||
// *** MATCH ***
|
||||
s->se[0].epos = nfa->cur;
|
||||
newmatch(s, nfa);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return BGPENOERR;
|
||||
}
|
||||
|
||||
static void collect(Bgpvm *vm, const Nfa *nfa)
|
||||
{
|
||||
Boolean isMatching = (nfa->nmatches > 0);
|
||||
BGP_VMPUSH(vm, isMatching);
|
||||
|
||||
vm->curMatch = (Bgpvmmatch *) Bgp_VmTempAlloc(vm, sizeof(*vm->curMatch));
|
||||
if (!vm->curMatch)
|
||||
return; // out of memory
|
||||
|
||||
Bgpattrseg *tpa = Bgp_GetUpdateAttributes(BGP_MSGUPDATE(vm->msg));
|
||||
assert(tpa != NULL);
|
||||
|
||||
// Generate matches list
|
||||
Bgpvmasmatch *matches = NULL;
|
||||
Bgpvmasmatch **pmatches = &matches;
|
||||
for (unsigned i = 0; !isnullmatch(&nfa->se[i]); i++) {
|
||||
const Nfamatch *src = &nfa->se[i];
|
||||
Bgpvmasmatch *dest = (Bgpvmasmatch *) Bgp_VmTempAlloc(vm, sizeof(*dest));
|
||||
if (!dest)
|
||||
return; // out of memory
|
||||
|
||||
dest->next = *pmatches;
|
||||
dest->spos = src->spos;
|
||||
dest->epos = src->epos;
|
||||
*pmatches = dest;
|
||||
|
||||
pmatches = &dest->next;
|
||||
}
|
||||
|
||||
vm->curMatch->pc = BGP_VMCURPC(vm);
|
||||
vm->curMatch->tag = 0;
|
||||
vm->curMatch->base = tpa->attrs;
|
||||
vm->curMatch->lim = &tpa->attrs[beswap16(tpa->len)];
|
||||
vm->curMatch->pos = matches;
|
||||
vm->curMatch->isMatching = isMatching;
|
||||
}
|
||||
|
||||
static BgpvmRet execute(Bgpvm *vm, Nfainst *program, unsigned listlen, Nfa *nfa)
|
||||
{
|
||||
// Prepare AS PATH iterator
|
||||
BgpvmRet err = BGPENOERR; // unless found otherwise
|
||||
if (Bgp_StartMsgRealAsPath(&nfa->cur, vm->msg) != OK) {
|
||||
vm->errCode = BGPEVMMSGERR;
|
||||
return vm->errCode;
|
||||
}
|
||||
|
||||
// Setup state lists
|
||||
Nfalist *clist, *nlist, *t;
|
||||
|
||||
size_t listsiz = offsetof(Nfalist, list[listlen]);
|
||||
if (listlen > LISTSIZ) {
|
||||
// Allocate on temporary memory
|
||||
clist = (Nfalist *) Bgp_VmTempAlloc(vm, listsiz);
|
||||
nlist = (Nfalist *) Bgp_VmTempAlloc(vm, listsiz);
|
||||
} else {
|
||||
// Allocate on stack
|
||||
clist = (Nfalist *) alloca(listsiz);
|
||||
nlist = (Nfalist *) alloca(listsiz);
|
||||
}
|
||||
if (!clist || !nlist)
|
||||
return vm->errCode;
|
||||
|
||||
clist->lim = nlist->lim = listlen;
|
||||
|
||||
// Simulate NFA, execute once per ASN (including ASN_BOL and ASN_EOL)
|
||||
nfa->nmatches = 0; // clear result list
|
||||
clist->ns = 0; // clear current list for the first time
|
||||
clearmatch(&nfa->se[0]); // by default no match
|
||||
|
||||
Asn asn, flag = ASNBOLFLAG; // first ASN is marked as BOL
|
||||
do {
|
||||
// Copy initial position to start
|
||||
nfa->spos = nfa->cur;
|
||||
|
||||
// Always include first instruction if no match took place yet
|
||||
if (nfa->nmatches == 0 && !addstartinst(clist, program, nfa)) {
|
||||
// State list overflow
|
||||
err = BGPEVMASMTCHESIZE;
|
||||
break;
|
||||
}
|
||||
|
||||
// Fetch new ASN
|
||||
asn = Bgp_NextAsPath(&nfa->cur);
|
||||
if (asn == -1 && Bgp_GetErrStat(NULL)) {
|
||||
err = BGPEVMMSGERR;
|
||||
break;
|
||||
}
|
||||
|
||||
// Advance NFA evaluating the current ASN
|
||||
if (step(clist, asn | flag, nlist, nfa) != OK) {
|
||||
// List overflow
|
||||
err = BGPEVMASMTCHESIZE;
|
||||
break;
|
||||
}
|
||||
|
||||
t = clist, clist = nlist, nlist = t; // swap lists
|
||||
|
||||
flag = 0; // no more the first ASN
|
||||
} while (asn != -1);
|
||||
|
||||
if (listlen > LISTSIZ) {
|
||||
Bgp_VmTempFree(vm, listsiz);
|
||||
Bgp_VmTempFree(vm, listsiz);
|
||||
}
|
||||
|
||||
vm->errCode = err;
|
||||
return err;
|
||||
}
|
||||
|
||||
void *Bgp_VmCompileAsMatch(Bgpvm *vm, const Asn *expression, size_t n)
|
||||
{
|
||||
Nfainst *buf, *prog;
|
||||
|
||||
// NOTE: Bgp_VmPermAlloc() already clears VM error and asserts !vm->isRunning
|
||||
|
||||
const size_t maxsiz = ALIGN(6 * n * sizeof(*buf), ALIGNMENT);
|
||||
|
||||
// we request an already aligned chunk
|
||||
// so optimize() can make accurate memory adjustments
|
||||
buf = Bgp_VmPermAlloc(vm, maxsiz);
|
||||
if (!buf)
|
||||
return NULL;
|
||||
|
||||
Nfacomp nc;
|
||||
compinit(&nc, buf);
|
||||
|
||||
int err;
|
||||
if ((err = setjmp(nc.oops)) != 0) {
|
||||
vm->errCode = err;
|
||||
vm->hLowMark -= maxsiz; // release permanent allocation
|
||||
return NULL;
|
||||
}
|
||||
|
||||
prog = compile(&nc, expression, n);
|
||||
optimize(vm, &nc, maxsiz);
|
||||
#ifdef DF_DEBUG_ASMTCH
|
||||
dumpprog(prog);
|
||||
#endif
|
||||
|
||||
// vm->errCode = BGPENOERR; - already set by Bgp_VmPermAlloc()
|
||||
return prog;
|
||||
}
|
||||
|
||||
void Bgp_VmDoAsmtch(Bgpvm *vm)
|
||||
{
|
||||
/* POPS:
|
||||
* -1: Asn match array length
|
||||
* -2: Address to Asn match array
|
||||
*
|
||||
* PUSHES:
|
||||
* TRUE on successful match, FALSE otherwise
|
||||
*/
|
||||
|
||||
Nfacomp nc;
|
||||
Nfa nfa;
|
||||
Nfainst *buf, *prog;
|
||||
|
||||
if (!BGP_VMCHKSTK(vm, 2))
|
||||
return;
|
||||
|
||||
// Pop arguments from stack
|
||||
Sint64 n = BGP_VMPOP(vm);
|
||||
const Asn *match = (const Asn *) BGP_VMPOPA(vm);
|
||||
if (n <= 0 || match == NULL) {
|
||||
vm->errCode = BGPEVMBADASMTCH;
|
||||
return;
|
||||
}
|
||||
if (!BGP_VMCHKMSGTYPE(vm, BGP_UPDATE)) {
|
||||
Bgp_VmStoreMsgTypeMatch(vm, /*isMatching=*/FALSE);
|
||||
BGP_VMPUSH(vm, FALSE);
|
||||
return;
|
||||
}
|
||||
|
||||
// Compile on the fly on temporary memory
|
||||
const size_t maxsiz = 6 * n * sizeof(*buf);
|
||||
|
||||
buf = (Nfainst *) Bgp_VmTempAlloc(vm, maxsiz);
|
||||
if (!buf)
|
||||
return;
|
||||
|
||||
compinit(&nc, buf);
|
||||
|
||||
int err; // compilation status
|
||||
if ((err = setjmp(nc.oops)) != 0) {
|
||||
vm->errCode = err;
|
||||
return;
|
||||
}
|
||||
|
||||
prog = compile(&nc, match, n);
|
||||
#ifdef DF_DEBUG_ASMTCH
|
||||
dumpprog(prog);
|
||||
#endif
|
||||
|
||||
BgpvmRet status = execute(vm, prog, LISTSIZ, &nfa);
|
||||
if (status == BGPEVMASMTCHESIZE)
|
||||
status = execute(vm, prog, BIGLISTSIZ, &nfa);
|
||||
|
||||
Bgp_VmTempFree(vm, maxsiz);
|
||||
|
||||
if (status == BGPENOERR)
|
||||
collect(vm, &nfa);
|
||||
}
|
||||
|
||||
void Bgp_VmDoFasmtc(Bgpvm *vm)
|
||||
{
|
||||
/* POPS:
|
||||
* -1: Precompiled NFA instructions
|
||||
*
|
||||
* PUSHES:
|
||||
* TRUE on successful match, FALSE otherwise
|
||||
*/
|
||||
|
||||
Nfa nfa;
|
||||
|
||||
if (!BGP_VMCHKSTK(vm, 1))
|
||||
return;
|
||||
|
||||
Nfainst *prog = (Nfainst *) BGP_VMPOPA(vm);
|
||||
if (!prog) {
|
||||
vm->errCode = BGPEVMBADASMTCH;
|
||||
return;
|
||||
}
|
||||
if (!BGP_VMCHKMSGTYPE(vm, BGP_UPDATE)) {
|
||||
Bgp_VmStoreMsgTypeMatch(vm, /*isMatching=*/FALSE);
|
||||
BGP_VMPUSH(vm, FALSE);
|
||||
return;
|
||||
}
|
||||
|
||||
BgpvmRet status = execute(vm, prog, LISTSIZ, &nfa);
|
||||
if (status == BGPEVMASMTCHESIZE)
|
||||
status = execute(vm, prog, BIGLISTSIZ, &nfa);
|
||||
|
||||
if (status == BGPENOERR)
|
||||
collect(vm, &nfa);
|
||||
}
|
33
lonetix/bgp/vm_cdef.h
Normal file
33
lonetix/bgp/vm_cdef.h
Normal file
@@ -0,0 +1,33 @@
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
|
||||
/**
|
||||
* \file bgp/vm_cdef.c
|
||||
*
|
||||
* Portable implementation for BGP VM execution loop
|
||||
*
|
||||
* \copyright The DoubleFourteen Code Forge (C) All Rights Reserved
|
||||
* \author Lorenzo Cogotti
|
||||
*
|
||||
* Plain C switch based FETCH-DECODE-DISPATCH-EXECUTE BGP filtering
|
||||
* engine VM implementation
|
||||
*
|
||||
* \note File should be `#include`d by bgp/vm.c
|
||||
*/
|
||||
|
||||
#ifdef DF_BGP_VMDEF_H_
|
||||
#error "Only one vm_<impl>def.h file may be #include-d"
|
||||
#endif
|
||||
#define DF_BGP_VMDEF_H_
|
||||
|
||||
#define LIKELY
|
||||
#define UNLIKELY
|
||||
|
||||
#define FETCH(ir, vm) (ir = (vm)->prog[(vm)->pc++])
|
||||
|
||||
#define EXPECT(opcode, ir, vm) ((void) 0)
|
||||
|
||||
#define DISPATCH(opcode) switch (opcode)
|
||||
|
||||
#define EXECUTE(opcode) case BGP_VMOP_ ## opcode
|
||||
|
||||
#define EXECUTE_SIGILL default
|
103
lonetix/bgp/vm_commsort.h
Normal file
103
lonetix/bgp/vm_commsort.h
Normal file
@@ -0,0 +1,103 @@
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
|
||||
/**
|
||||
* \file bgp/vm_commsort.h
|
||||
*
|
||||
* Generic basic sorting and binary searching over unsigned integer arrays.
|
||||
*
|
||||
* \copyright The DoubleFourteen Code Forge (C) All Rights Reserved
|
||||
* \author Lorenzo Cogotti
|
||||
*
|
||||
* The following defines a bunch of static functions to sort
|
||||
* and search basic integer arrays.
|
||||
*
|
||||
* `#define` `UINT_TYPE` with an unsigned <= 4 bytes and `FNSUFFIX`
|
||||
* before inclusion.
|
||||
*
|
||||
* \note No guards, file `#include`d by bgp/vm_communities.c
|
||||
*/
|
||||
|
||||
#define _CAT(X, Y) X ## Y
|
||||
#define _XCAT(X, Y) _CAT(X, Y)
|
||||
|
||||
#define _MANGLE(FN) _XCAT(FN, FNSUFFIX)
|
||||
|
||||
|
||||
static Sint64 _MANGLE(BinarySearch) (const UINT_TYPE *arr,
|
||||
Uint32 n,
|
||||
UINT_TYPE v)
|
||||
{
|
||||
Uint32 len = n;
|
||||
Uint32 mid = n;
|
||||
Sint64 off = 0;
|
||||
while (mid > 0) {
|
||||
mid = len >> 1;
|
||||
if (arr[off+mid] <= v)
|
||||
off += mid;
|
||||
|
||||
len -= mid;
|
||||
}
|
||||
|
||||
return (off < n && arr[off] == v) ? off : -1;
|
||||
}
|
||||
|
||||
static void _MANGLE(Radix) (int off,
|
||||
const UINT_TYPE *src,
|
||||
Uint32 n,
|
||||
UINT_TYPE *dest)
|
||||
{
|
||||
const Uint8 *sortKey;
|
||||
|
||||
Uint32 index[256];
|
||||
Uint32 count[256] = { 0 };
|
||||
|
||||
for (Uint32 i = 0; i < n; i++) {
|
||||
sortKey = ((const Uint8 *) &src[i]) + off;
|
||||
count[*sortKey]++;
|
||||
}
|
||||
|
||||
index[0] = 0;
|
||||
for (Uint32 i = 1; i < 256; i++)
|
||||
index[i] = index[i-1] + count[i-1];
|
||||
|
||||
for (Uint32 i = 0; i < n; i++) {
|
||||
sortKey = ((const Uint8 *) &src[i]) + off;
|
||||
dest[index[*sortKey]++] = src[i];
|
||||
}
|
||||
}
|
||||
|
||||
static void _MANGLE(RadixSort) (UINT_TYPE *arr, Uint32 n)
|
||||
{
|
||||
UINT_TYPE *scratch = (UINT_TYPE *) alloca(n * sizeof(*scratch));
|
||||
|
||||
STATIC_ASSERT(sizeof(UINT_TYPE) % 2 == 0, "?!");
|
||||
|
||||
if (EDN_NATIVE == EDN_LE) {
|
||||
for (unsigned i = 0; i < sizeof(UINT_TYPE); i += 2) {
|
||||
_MANGLE(Radix) (i + 0, arr, n, scratch);
|
||||
_MANGLE(Radix) (i + 1, scratch, n, arr);
|
||||
}
|
||||
} else {
|
||||
for (unsigned i = sizeof(UINT_TYPE); i > 0; i -= 2) {
|
||||
_MANGLE(Radix) (i - 1, arr, n, scratch);
|
||||
_MANGLE(Radix) (i - 2, scratch, n, arr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static Uint32 _MANGLE(Uniq) (UINT_TYPE *arr, Uint32 n)
|
||||
{
|
||||
Uint32 i, j;
|
||||
|
||||
if (n == 0) return 0;
|
||||
|
||||
for (i = 0, j = 1; j < n; j++) {
|
||||
if (arr[i] != arr[j])
|
||||
arr[++i] = arr[j];
|
||||
}
|
||||
return ++i;
|
||||
}
|
||||
|
||||
#undef _MANGLE
|
||||
#undef _XCAT
|
||||
#undef _CAT
|
442
lonetix/bgp/vm_communities.c
Normal file
442
lonetix/bgp/vm_communities.c
Normal file
@@ -0,0 +1,442 @@
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
|
||||
/**
|
||||
* \file bgp/vm_communities.c
|
||||
*
|
||||
* BGP VM COMTCH, ACOMTC instructions and COMMUNITY index.
|
||||
*
|
||||
* \copyright The DoubleFourteen Code Forge (C) All Rights Reserved
|
||||
* \author Lorenzo Cogotti
|
||||
*/
|
||||
|
||||
#include "bgp/bgp_local.h"
|
||||
|
||||
#include "bgp/vmintrin.h"
|
||||
#include "sys/endian.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
typedef struct {
|
||||
BgpVmOpt opt;
|
||||
Uint32 hiOnlyCount;
|
||||
Uint32 loOnlyCount;
|
||||
Uint32 fullCount;
|
||||
Uint32 bitsetWords;
|
||||
Uint32 *bitset;
|
||||
Uint16 *hiOnly; // parital match on community hi
|
||||
Uint16 *loOnly; // partial match on community lo
|
||||
Uint32 full[]; // full matches on whole community codes
|
||||
// Uint32 bitset[]; <- order preserves alignment requirements
|
||||
// Uint16 hi[];
|
||||
// Uint16 lo[];
|
||||
} Bgpcommidx;
|
||||
|
||||
FORCE_INLINE size_t BITSETWIDTH(const Bgpcommidx *idx)
|
||||
{
|
||||
return idx->hiOnlyCount + idx->loOnlyCount + idx->fullCount;
|
||||
}
|
||||
FORCE_INLINE size_t BITSETLEN(size_t width)
|
||||
{
|
||||
return (width >> 5) + ((width & 0x1f) != 0);
|
||||
}
|
||||
FORCE_INLINE size_t FULLBITIDX(const Bgpcommidx *idx, Uint32 i)
|
||||
{
|
||||
USED(idx);
|
||||
return i;
|
||||
}
|
||||
FORCE_INLINE size_t HIBITIDX(const Bgpcommidx *idx, Uint32 i)
|
||||
{
|
||||
return (size_t) idx->fullCount + i;
|
||||
}
|
||||
FORCE_INLINE size_t LOBITIDX(const Bgpcommidx *idx, Uint32 i)
|
||||
{
|
||||
return (size_t) idx->fullCount + idx->hiOnlyCount + i;
|
||||
}
|
||||
|
||||
FORCE_INLINE Boolean ISBITSET(const Uint32 *bitset, size_t idx)
|
||||
{
|
||||
return (bitset[idx >> 5] & (1u << (idx & 0x1f))) != 0;
|
||||
}
|
||||
|
||||
FORCE_INLINE void SETBIT(Uint32 *bitset, size_t idx)
|
||||
{
|
||||
bitset[idx >> 5] |= (1u << (idx & 0x1f));
|
||||
}
|
||||
|
||||
FORCE_INLINE void CLRBITSET(Uint32 *bitset, size_t len)
|
||||
{
|
||||
memset(bitset, 0, len * sizeof(*bitset));
|
||||
}
|
||||
|
||||
|
||||
#ifdef __GNUC__
|
||||
FORCE_INLINE unsigned FindFirstSet(Uint32 x)
|
||||
{
|
||||
STATIC_ASSERT(sizeof(x) == sizeof(int), "__builtin_ffs() operates on int");
|
||||
return __builtin_ffs(x);
|
||||
}
|
||||
#else
|
||||
FORCE_INLINE unsigned FindFirstSet(Uint32 x)
|
||||
{
|
||||
if (x == 0) return 0;
|
||||
|
||||
unsigned n = 0;
|
||||
|
||||
if ((x & 0x0000ffffu) == 0) n += 16, x >>= 16;
|
||||
if ((x & 0x000000ffu) == 0) n += 8, x >>= 8;
|
||||
if ((x & 0x0000000fu) == 0) n += 4, x >>= 4;
|
||||
if ((x & 0x00000003u) == 0) n += 2, x >>= 2;
|
||||
if ((x & 0x00000001u) == 0) n += 1;
|
||||
|
||||
return ++n;
|
||||
}
|
||||
#endif
|
||||
|
||||
static size_t FFZ(const Uint32 *bitset, size_t len)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
assert(len > 0);
|
||||
|
||||
len--;
|
||||
for (i = 0; i < len && bitset[i] == 0xffffffffu; i++);
|
||||
|
||||
size_t n = i << 6;
|
||||
n += FindFirstSet(~bitset[i]) - 1;
|
||||
return n;
|
||||
}
|
||||
|
||||
#define UINT_TYPE Uint16
|
||||
#define FNSUFFIX 16
|
||||
#include "bgp/vm_commsort.h"
|
||||
#undef UINT_TYPE
|
||||
#undef FNSUFFIX
|
||||
|
||||
#define UINT_TYPE Uint32
|
||||
#define FNSUFFIX 32
|
||||
#include "bgp/vm_commsort.h"
|
||||
#undef UINT_TYPE
|
||||
#undef FNSUFFIX
|
||||
|
||||
static Boolean MatchCommunity(const Bgpcomm *c, const Bgpcommidx *idx)
|
||||
{
|
||||
return BinarySearch16(idx->hiOnly, idx->hiOnlyCount, c->hi) >= 0 ||
|
||||
BinarySearch16(idx->loOnly, idx->loOnlyCount, c->lo) >= 0 ||
|
||||
BinarySearch32(idx->full, idx->fullCount, c->code) >= 0;
|
||||
}
|
||||
|
||||
static void OptimizeComtch(Bgpcommidx *idx)
|
||||
{
|
||||
// Remove every full match more specific than an existing partial match.
|
||||
|
||||
// NOTE: Assumes arrays have been sorted and Uniq()d
|
||||
|
||||
Uint32 i, j;
|
||||
Bgpcomm c;
|
||||
|
||||
for (i = 0, j = 0; i < idx->fullCount; i++) {
|
||||
c.code = idx->full[i];
|
||||
if (BinarySearch16(idx->hiOnly, idx->hiOnlyCount, c.hi) >= 0 ||
|
||||
BinarySearch16(idx->loOnly, idx->loOnlyCount, c.lo) >= 0)
|
||||
continue;
|
||||
|
||||
idx->full[j++] = idx->full[i];
|
||||
}
|
||||
|
||||
idx->fullCount = j;
|
||||
}
|
||||
|
||||
static void OptimizeAcomtc(Bgpcommidx *idx)
|
||||
{
|
||||
// Remove every partial match less specific than an existing full match
|
||||
|
||||
// NOTE: Assumes arrays have been sorted and Uniq()d
|
||||
|
||||
Uint32 i, j;
|
||||
|
||||
// Mark redundant entries in bitset
|
||||
CLRBITSET(idx->bitset, idx->bitsetWords);
|
||||
for (i = 0; i < idx->fullCount; i++) {
|
||||
Sint64 pos;
|
||||
Bgpcomm c;
|
||||
|
||||
c.code = idx->full[i];
|
||||
|
||||
pos = BinarySearch16(idx->hiOnly, idx->hiOnlyCount, c.hi);
|
||||
if (pos >= 0)
|
||||
SETBIT(idx->bitset, HIBITIDX(idx, pos));
|
||||
|
||||
pos = BinarySearch16(idx->loOnly, idx->loOnlyCount, c.lo);
|
||||
if (pos >= 0)
|
||||
SETBIT(idx->bitset, LOBITIDX(idx, pos));
|
||||
}
|
||||
|
||||
// Remove redundant entries
|
||||
for (i = 0, j = 0; i < idx->hiOnlyCount; i++) {
|
||||
if (!ISBITSET(idx->bitset, HIBITIDX(idx, i)))
|
||||
idx->hiOnly[j++] = idx->hiOnly[i];
|
||||
}
|
||||
idx->hiOnlyCount = j;
|
||||
|
||||
for (i = 0, j = 0; i < idx->loOnlyCount; i++) {
|
||||
if (!ISBITSET(idx->bitset, LOBITIDX(idx, i)))
|
||||
idx->loOnly[j++] = idx->loOnly[i];
|
||||
}
|
||||
idx->loOnlyCount = j;
|
||||
}
|
||||
|
||||
static void CompactIndex(Bgpvm *vm, Bgpcommidx *idx, size_t idxSize)
|
||||
{
|
||||
size_t offset = offsetof(Bgpcommidx, full[idx->fullCount]);
|
||||
size_t bitsetSiz = idx->bitsetWords * sizeof(*idx->bitset);
|
||||
size_t hiSiz = idx->hiOnlyCount * sizeof(*idx->hiOnly);
|
||||
size_t loSiz = idx->loOnlyCount * sizeof(*idx->loOnly);
|
||||
|
||||
Uint8 *ptr = (Uint8 *) idx + offset;
|
||||
|
||||
idx->bitset = (Uint32 *) memmove(ptr, idx->bitset, bitsetSiz);
|
||||
ptr += bitsetSiz;
|
||||
idx->hiOnly = (Uint16 *) memmove(ptr, idx->hiOnly, hiSiz);
|
||||
ptr += hiSiz;
|
||||
idx->loOnly = (Uint16 *) memmove(ptr, idx->loOnly, loSiz);
|
||||
ptr += loSiz;
|
||||
|
||||
size_t siz = ptr - (Uint8 *) idx;
|
||||
|
||||
siz = ALIGN(siz, ALIGNMENT);
|
||||
offset = idxSize - siz;
|
||||
|
||||
vm->hLowMark -= offset;
|
||||
}
|
||||
|
||||
void *Bgp_VmCompileCommunityMatch(Bgpvm *vm,
|
||||
const Bgpmatchcomm *match,
|
||||
size_t n,
|
||||
BgpVmOpt opt)
|
||||
{
|
||||
// NOTE: Bgp_VmPermAlloc() already clears VM error and asserts !vm->isRunning
|
||||
|
||||
Sint32 nlow = 0, nhigh = 0, nfull = 0, nbitswords = 0;
|
||||
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
const Bgpmatchcomm *m = &match[i];
|
||||
if (m->maskLo && m->maskHi) {
|
||||
vm->errCode = BGPEVMBADCOMTCH;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (m->maskLo)
|
||||
nhigh++;
|
||||
else if (m->maskHi)
|
||||
nlow++;
|
||||
else
|
||||
nfull++;
|
||||
}
|
||||
|
||||
Bgpcommidx *idx;
|
||||
|
||||
size_t offBits = offsetof(Bgpcommidx, full[nfull]);
|
||||
if (opt != BGP_VMOPT_ASSUME_COMTCH)
|
||||
nbitswords = BITSETLEN(nlow + nhigh + nfull); // must allocate bitset
|
||||
|
||||
size_t offHigh = offBits + nbitswords * sizeof(*idx->bitset);
|
||||
size_t offLow = offHigh + nhigh * sizeof(*idx->hiOnly);
|
||||
size_t nbytes = offLow + nlow * sizeof(*idx->loOnly);
|
||||
|
||||
nbytes = ALIGN(nbytes, ALIGNMENT);
|
||||
|
||||
idx = Bgp_VmPermAlloc(vm, nbytes);
|
||||
if (!idx)
|
||||
return NULL;
|
||||
|
||||
idx->bitset = (Uint32 *) ((Uint8 *) idx + offBits);
|
||||
idx->hiOnly = (Uint16 *) ((Uint8 *) idx + offHigh);
|
||||
idx->loOnly = (Uint16 *) ((Uint8 *) idx + offLow);
|
||||
|
||||
idx->opt = opt;
|
||||
idx->bitsetWords = nbitswords;
|
||||
idx->hiOnlyCount = idx->loOnlyCount = idx->fullCount = 0;
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
const Bgpmatchcomm *m = &match[i];
|
||||
if (m->maskLo)
|
||||
idx->hiOnly[idx->hiOnlyCount++] = m->c.hi;
|
||||
else if (m->maskHi)
|
||||
idx->loOnly[idx->loOnlyCount++] = m->c.lo;
|
||||
else
|
||||
idx->full[idx->fullCount++] = m->c.code;
|
||||
}
|
||||
|
||||
// Sort lookup arrays
|
||||
RadixSort16(idx->hiOnly, idx->hiOnlyCount);
|
||||
RadixSort16(idx->loOnly, idx->loOnlyCount);
|
||||
RadixSort32(idx->full, idx->fullCount);
|
||||
|
||||
// Optimize tables
|
||||
idx->hiOnlyCount = Uniq16(idx->hiOnly, idx->hiOnlyCount);
|
||||
idx->loOnlyCount = Uniq16(idx->loOnly, idx->loOnlyCount);
|
||||
idx->fullCount = Uniq32(idx->full, idx->fullCount);
|
||||
|
||||
// Discard redundant entries
|
||||
switch (opt) {
|
||||
case BGP_VMOPT_ASSUME_COMTCH: OptimizeComtch(idx); break;
|
||||
case BGP_VMOPT_ASSUME_ACOMTC: OptimizeAcomtc(idx); break;
|
||||
|
||||
default:
|
||||
case BGP_VMOPT_NONE:
|
||||
break;
|
||||
}
|
||||
|
||||
// Free-up excess memory after optimization
|
||||
CompactIndex(vm, idx, nbytes);
|
||||
return idx;
|
||||
}
|
||||
|
||||
static Bgpattr *Bgp_VmDoComSetup(Bgpvm *vm, Bgpcommiter *it, BgpAttrCode code)
|
||||
{
|
||||
Bgpupdate *update = (Bgpupdate *) BGP_VMCHKMSGTYPE(vm, BGP_UPDATE);
|
||||
if (!update) {
|
||||
Bgp_VmStoreMsgTypeMatch(vm, /*isMatching=*/FALSE);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Bgpattrseg *tpa = Bgp_GetUpdateAttributes(update);
|
||||
if (!tpa) {
|
||||
vm->errCode = BGPEVMMSGERR;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Bgpattr *attr = Bgp_GetUpdateAttribute(tpa, code, vm->msg->table);
|
||||
if (Bgp_GetErrStat(NULL)) {
|
||||
vm->errCode = BGPEVMMSGERR;
|
||||
return NULL;
|
||||
}
|
||||
if (attr)
|
||||
Bgp_StartCommunity(it, attr);
|
||||
|
||||
return attr;
|
||||
}
|
||||
|
||||
void Bgp_VmDoComtch(Bgpvm *vm)
|
||||
{
|
||||
if (!BGP_VMCHKSTKSIZ(vm, 1))
|
||||
return;
|
||||
|
||||
Bgpcommidx *idx = (Bgpcommidx *) BGP_VMPOPA(vm);
|
||||
if (!idx || idx->opt == BGP_VMOPT_ASSUME_ACOMTC) {
|
||||
vm->errCode = BGPEVMBADCOMTCH; // TODO: BGPEVMBADCOMIDX;
|
||||
return;
|
||||
}
|
||||
|
||||
Boolean isMatching = FALSE; // unless found otherwise
|
||||
|
||||
Bgpcommiter it;
|
||||
Bgpattr *attr = Bgp_VmDoComSetup(vm, &it, BGP_ATTR_COMMUNITY);
|
||||
if (vm->errCode)
|
||||
return;
|
||||
if (!attr)
|
||||
goto done;
|
||||
|
||||
Bgpcomm *c;
|
||||
while ((c = Bgp_NextCommunity(&it)) != NULL) {
|
||||
if (MatchCommunity(c, idx)) {
|
||||
isMatching = TRUE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (Bgp_GetErrStat(NULL)) {
|
||||
vm->errCode = BGPEVMMSGERR;
|
||||
return;
|
||||
}
|
||||
|
||||
done:
|
||||
BGP_VMPUSH(vm, isMatching);
|
||||
}
|
||||
|
||||
static void ScMatchCommunityAndSetBit(const Bgpcomm *c, Bgpcommidx *idx)
|
||||
{
|
||||
Sint64 pos = BinarySearch16(idx->hiOnly, idx->hiOnlyCount, c->hi);
|
||||
if (pos >= 0) {
|
||||
SETBIT(idx->bitset, HIBITIDX(idx, pos));
|
||||
return;
|
||||
}
|
||||
|
||||
pos = BinarySearch16(idx->loOnly, idx->loOnlyCount, c->lo);
|
||||
if (pos >= 0) {
|
||||
SETBIT(idx->bitset, LOBITIDX(idx, pos));
|
||||
return;
|
||||
}
|
||||
|
||||
pos = BinarySearch32(idx->full, idx->fullCount, c->code);
|
||||
if (pos >= 0)
|
||||
SETBIT(idx->bitset, FULLBITIDX(idx, pos));
|
||||
}
|
||||
|
||||
static void MatchCommunityAndSetBit(const Bgpcomm *c, Bgpcommidx *idx)
|
||||
{
|
||||
Sint64 pos = BinarySearch16(idx->hiOnly, idx->hiOnlyCount, c->hi);
|
||||
if (pos >= 0)
|
||||
SETBIT(idx->bitset, HIBITIDX(idx, pos));
|
||||
|
||||
pos = BinarySearch16(idx->loOnly, idx->loOnlyCount, c->lo);
|
||||
if (pos >= 0)
|
||||
SETBIT(idx->bitset, LOBITIDX(idx, pos));
|
||||
|
||||
pos = BinarySearch32(idx->full, idx->fullCount, c->code);
|
||||
if (pos >= 0)
|
||||
SETBIT(idx->bitset, FULLBITIDX(idx, pos));
|
||||
}
|
||||
|
||||
static Boolean Bgp_VmDoAcomtcFast(Bgpcommidx *idx, Bgpcommiter *it)
|
||||
{
|
||||
Bgpcomm *c;
|
||||
while ((c = Bgp_NextCommunity(it)) != NULL)
|
||||
ScMatchCommunityAndSetBit(c, idx);
|
||||
|
||||
return FFZ(idx->bitset, idx->bitsetWords) == BITSETWIDTH(idx);
|
||||
}
|
||||
|
||||
static Boolean Bgp_VmDoAcomtcSlow(Bgpcommidx *idx, Bgpcommiter *it)
|
||||
{
|
||||
Bgpcomm *c;
|
||||
while ((c = Bgp_NextCommunity(it)) != NULL)
|
||||
MatchCommunityAndSetBit(c, idx);
|
||||
|
||||
return FFZ(idx->bitset, idx->bitsetWords) == BITSETWIDTH(idx);
|
||||
}
|
||||
|
||||
void Bgp_VmDoAcomtc(Bgpvm *vm)
|
||||
{
|
||||
if (!BGP_VMCHKSTKSIZ(vm, 1))
|
||||
return;
|
||||
|
||||
Bgpcommidx *idx = (Bgpcommidx *) BGP_VMPOPA(vm);
|
||||
if (!idx || idx->opt == BGP_VMOPT_ASSUME_COMTCH) {
|
||||
vm->errCode = BGPEVMBADCOMTCH; // TODO: BGPEVMBADCOMIDX;
|
||||
return;
|
||||
}
|
||||
|
||||
Boolean isMatching = FALSE;
|
||||
|
||||
Bgpcommiter it;
|
||||
Bgpattr *attr = Bgp_VmDoComSetup(vm, &it, BGP_ATTR_COMMUNITY);
|
||||
if (vm->errCode)
|
||||
return;
|
||||
if (!attr)
|
||||
goto done;
|
||||
|
||||
CLRBITSET(idx->bitset, idx->bitsetWords);
|
||||
if (idx->opt == BGP_VMOPT_ASSUME_ACOMTC)
|
||||
isMatching = Bgp_VmDoAcomtcFast(idx, &it);
|
||||
else
|
||||
isMatching = Bgp_VmDoAcomtcSlow(idx, &it);
|
||||
|
||||
if (Bgp_GetErrStat(NULL)) {
|
||||
vm->errCode = BGPEVMMSGERR;
|
||||
return;
|
||||
}
|
||||
|
||||
done:
|
||||
BGP_VMPUSH(vm, isMatching);
|
||||
}
|
303
lonetix/bgp/vm_dump.c
Normal file
303
lonetix/bgp/vm_dump.c
Normal file
@@ -0,0 +1,303 @@
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
|
||||
/**
|
||||
* \file bgp/vm_dump.c
|
||||
*
|
||||
* BGP VM bytecode dump.
|
||||
*
|
||||
* \copyright The DoubleFourteen Code Forge (C) All Rights Reserved
|
||||
* \author Lorenzo Cogotti
|
||||
*/
|
||||
|
||||
#include "bgp/vmintrin.h"
|
||||
|
||||
#include "sys/dbg.h"
|
||||
#include "numlib.h"
|
||||
#include "strlib.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
|
||||
#define LINEDIGS 5
|
||||
#define MAXOPCSTRLEN 6
|
||||
#define CODELINELEN 50
|
||||
#define MAXINDENT 8
|
||||
#define INDENTLEN 2
|
||||
#define COMMENTLEN 70
|
||||
|
||||
static const char *OpcString(Bgpvmopc opc)
|
||||
{
|
||||
// NOTE: Invariant: strlen(return) <= MAXOPCSTRLEN
|
||||
switch (opc) {
|
||||
case BGP_VMOP_NOP: return "NOP";
|
||||
case BGP_VMOP_LOAD: return "LOAD";
|
||||
case BGP_VMOP_LOADU: return "LOADU";
|
||||
case BGP_VMOP_LOADN: return "LOADN";
|
||||
case BGP_VMOP_LOADK: return "LOADK";
|
||||
case BGP_VMOP_CALL: return "CALL";
|
||||
case BGP_VMOP_BLK: return "BLK";
|
||||
case BGP_VMOP_ENDBLK: return "ENDBLK";
|
||||
case BGP_VMOP_TAG: return "TAG";
|
||||
case BGP_VMOP_NOT: return "NOT";
|
||||
case BGP_VMOP_CFAIL: return "CFAIL";
|
||||
case BGP_VMOP_CPASS: return "CPASS";
|
||||
case BGP_VMOP_JZ: return "JZ";
|
||||
case BGP_VMOP_JNZ: return "JNZ";
|
||||
case BGP_VMOP_CHKT: return "CHKT";
|
||||
case BGP_VMOP_CHKA: return "CHKA";
|
||||
case BGP_VMOP_EXCT: return "EXCT";
|
||||
case BGP_VMOP_SUPN: return "SUPN";
|
||||
case BGP_VMOP_SUBN: return "SUBN";
|
||||
case BGP_VMOP_RELT: return "RELT";
|
||||
case BGP_VMOP_ASMTCH: return "ASMTCH";
|
||||
case BGP_VMOP_FASMTC: return "FASMTC";
|
||||
case BGP_VMOP_COMTCH: return "COMTCH";
|
||||
case BGP_VMOP_ACOMTC: return "ACOMTC";
|
||||
case BGP_VMOP_END: return "END";
|
||||
default: return "???";
|
||||
}
|
||||
}
|
||||
|
||||
static const char *BgpTypeString(BgpType typ)
|
||||
{
|
||||
switch (typ) {
|
||||
case BGP_OPEN: return "OPEN";
|
||||
case BGP_UPDATE: return "UPDATE";
|
||||
case BGP_NOTIFICATION: return "NOTIFICATION";
|
||||
case BGP_KEEPALIVE: return "KEEPALIVE";
|
||||
case BGP_ROUTE_REFRESH: return "ROUTE_REFRESH";
|
||||
case BGP_CLOSE: return "CLOSE";
|
||||
default: return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static const char *BgpAttrString(BgpAttrCode code)
|
||||
{
|
||||
switch (code) {
|
||||
case BGP_ATTR_ORIGIN: return "ORIGIN";
|
||||
case BGP_ATTR_AS_PATH: return "AS_PATH";
|
||||
case BGP_ATTR_NEXT_HOP: return "NEXT_HOP";
|
||||
case BGP_ATTR_MULTI_EXIT_DISC: return "MULTI_EXIT_DISC";
|
||||
case BGP_ATTR_LOCAL_PREF: return "LOCAL_PREF";
|
||||
case BGP_ATTR_ATOMIC_AGGREGATE: return "ATOMIC_AGGREGATE";
|
||||
case BGP_ATTR_AGGREGATOR: return "AGGREGATOR";
|
||||
case BGP_ATTR_COMMUNITY: return "COMMUNITY";
|
||||
case BGP_ATTR_ORIGINATOR_ID: return "ORIGINATOR_ID";
|
||||
case BGP_ATTR_CLUSTER_LIST: return "CLUSTER_LIST";
|
||||
case BGP_ATTR_DPA: return "DPA";
|
||||
case BGP_ATTR_ADVERTISER: return "ADVERTISER";
|
||||
case BGP_ATTR_RCID_PATH_CLUSTER_ID: return "RCID_PATH_CLUSTER_ID";
|
||||
case BGP_ATTR_MP_REACH_NLRI: return "MP_REACH_NLRI";
|
||||
case BGP_ATTR_MP_UNREACH_NLRI: return "MP_UNREACH_NLRI";
|
||||
case BGP_ATTR_EXTENDED_COMMUNITY: return "EXTENDED_COMMUNITY";
|
||||
case BGP_ATTR_AS4_PATH: return "AS4_PATH";
|
||||
case BGP_ATTR_AS4_AGGREGATOR: return "AS4_AGGREGATOR";
|
||||
case BGP_ATTR_SAFI_SSA: return "SAFI_SSA";
|
||||
case BGP_ATTR_CONNECTOR: return "CONNECTOR";
|
||||
case BGP_ATTR_AS_PATHLIMIT: return "AS_PATHLIMIT";
|
||||
case BGP_ATTR_PMSI_TUNNEL: return "PMSI_TUNNEL";
|
||||
case BGP_ATTR_TUNNEL_ENCAPSULATION: return "TUNNEL_ENCAPSULATION";
|
||||
case BGP_ATTR_TRAFFIC_ENGINEERING: return "TRAFFIC_ENGINEERING";
|
||||
case BGP_ATTR_IPV6_ADDRESS_SPECIFIC_EXTENDED_COMMUNITY: return "IPV6_ADDRESS_SPECIFIC_EXTENDED_COMMUNITY";
|
||||
case BGP_ATTR_AIGP: return "AIGP";
|
||||
case BGP_ATTR_PE_DISTINGUISHER_LABELS: return "PE_DISTINGUISHER_LABELS";
|
||||
case BGP_ATTR_ENTROPY_LEVEL_CAPABILITY: return "ENTROPY_LEVEL_CAPABILITY";
|
||||
case BGP_ATTR_LS: return "LS";
|
||||
case BGP_ATTR_LARGE_COMMUNITY: return "LARGE_COMMUNITY";
|
||||
case BGP_ATTR_BGPSEC_PATH: return "BGPSEC_PATH";
|
||||
case BGP_ATTR_COMMUNITY_CONTAINER: return "COMMUNITY_CONTAINER";
|
||||
case BGP_ATTR_PREFIX_SID: return "PREFIX_SID";
|
||||
case BGP_ATTR_SET: return "SET";
|
||||
default: return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static const char *NetOpArgString(Uint8 opa)
|
||||
{
|
||||
switch (opa) {
|
||||
case BGP_VMOPA_NLRI: return "NLRI";
|
||||
case BGP_VMOPA_MPREACH: return "MP_REACH_NLRI";
|
||||
case BGP_VMOPA_ALL_NLRI: return "ALL_NLRI";
|
||||
case BGP_VMOPA_WITHDRAWN: return "WITHDRAWN";
|
||||
case BGP_VMOPA_MPUNREACH: return "MP_UNREACH_NLRI";
|
||||
case BGP_VMOPA_ALL_WITHDRAWN: return "ALL_WITHDRAWN";
|
||||
default: return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static char *ExplainJump(char *buf, Uint32 ip, Uint8 disp, Uint32 progLen)
|
||||
{
|
||||
char *p = buf;
|
||||
|
||||
strcpy(p, "to line: "); p += 9;
|
||||
|
||||
Uint32 target = ip + 1;
|
||||
target += 1 + disp;
|
||||
|
||||
p = Utoa(target, p);
|
||||
if (target > progLen)
|
||||
strcpy(p, " (JUMP TARGET OUT OF BOUNDS!)");
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
static char *Indent(char *p, Bgpvmopc opc, int level)
|
||||
{
|
||||
int n = CLAMP(level, 0, MAXINDENT);
|
||||
int last = n - 1;
|
||||
|
||||
for (int i = 0; i < n; i++) {
|
||||
*p++ = (opc == BGP_VMOP_ENDBLK && i == last) ? '+' : '|';
|
||||
for (int j = 1; j < INDENTLEN; j++)
|
||||
*p++ = (opc == BGP_VMOP_ENDBLK && i == last) ? '-' : ' ';
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
static char *CommentCodeLine(char *line, const char *comment)
|
||||
{
|
||||
char *p = line;
|
||||
|
||||
p += Df_strpadr(p, ' ', CODELINELEN);
|
||||
|
||||
*p++ = ' ';
|
||||
*p++ = ';'; *p++ = ' ';
|
||||
|
||||
size_t n = strlen(comment);
|
||||
if (3 + n >= COMMENTLEN) {
|
||||
n = COMMENTLEN - 3 - 3;
|
||||
|
||||
memcpy(p, comment, n);
|
||||
p += n;
|
||||
*p++ = '.'; *p++ = '.'; *p++ = '.';
|
||||
} else {
|
||||
memcpy(p, comment, n);
|
||||
p += n;
|
||||
}
|
||||
|
||||
*p = '\0';
|
||||
return p;
|
||||
}
|
||||
|
||||
void Bgp_VmDumpProgram(Bgpvm *vm, void *streamp, const StmOps *ops)
|
||||
{
|
||||
char explainbuf[64];
|
||||
char buf[256];
|
||||
|
||||
int indent = 0;
|
||||
|
||||
// NOTE: <= so it includes trailing END
|
||||
for (Uint32 ip = 0; ip <= vm->progLen; ip++) {
|
||||
Bgpvmbytec ir = vm->prog[ip];
|
||||
Bgpvmopc opc = BGP_VMOPC(ir);
|
||||
Uint8 opa = BGP_VMOPARG(ir);
|
||||
|
||||
const char *opcnam = OpcString(opc);
|
||||
assert(strlen(opcnam) <= MAXOPCSTRLEN);
|
||||
|
||||
char *p = buf;
|
||||
|
||||
// Line number
|
||||
Utoa(ip+1, p);
|
||||
p += Df_strpadl(p, '0', LINEDIGS);
|
||||
|
||||
*p++ = ':';
|
||||
*p++ = ' ';
|
||||
|
||||
// Instruction hex dump
|
||||
*p++ = '0';
|
||||
*p++ = 'x';
|
||||
Xtoa(ir, p);
|
||||
p += Df_strpadl(p, '0', XDIGS(ir));
|
||||
*p++ = ' ';
|
||||
|
||||
// Code indent
|
||||
p = Indent(p, opc, indent);
|
||||
|
||||
// Opcode
|
||||
strcpy(p, opcnam);
|
||||
p += Df_strpadr(p, ' ', MAXOPCSTRLEN);
|
||||
|
||||
// Instruction argument
|
||||
const char *opastr = NULL;
|
||||
switch (opc) {
|
||||
case BGP_VMOP_LOAD:
|
||||
*p++ = ' ';
|
||||
|
||||
p = Itoa((Sint8) opa, p);
|
||||
|
||||
break;
|
||||
case BGP_VMOP_LOADU:
|
||||
case BGP_VMOP_JZ:
|
||||
case BGP_VMOP_JNZ:
|
||||
*p++ = ' ';
|
||||
|
||||
p = Utoa(opa, p);
|
||||
if (opc == BGP_VMOP_JZ || opc == BGP_VMOP_JNZ)
|
||||
opastr = ExplainJump(explainbuf, ip, opa, vm->progLen);
|
||||
|
||||
break;
|
||||
case BGP_VMOP_TAG:
|
||||
case BGP_VMOP_CHKT:
|
||||
case BGP_VMOP_CHKA:
|
||||
case BGP_VMOP_EXCT:
|
||||
case BGP_VMOP_SUBN:
|
||||
case BGP_VMOP_SUPN:
|
||||
case BGP_VMOP_RELT:
|
||||
*p++ = ' ';
|
||||
*p++ = '0'; *p++ = 'x';
|
||||
|
||||
Xtoa(opa, p);
|
||||
p += Df_strpadl(p, '0', XDIGS(opa));
|
||||
|
||||
if (opc == BGP_VMOP_CHKT)
|
||||
opastr = BgpTypeString(opa);
|
||||
else if (opc == BGP_VMOP_CHKA)
|
||||
opastr = BgpAttrString(opa);
|
||||
else
|
||||
opastr = NetOpArgString(opa);
|
||||
|
||||
break;
|
||||
case BGP_VMOP_LOADK:
|
||||
*p++ = ' ';
|
||||
*p++ = 'K';
|
||||
*p++ = '[';
|
||||
p = Utoa(opa, p);
|
||||
*p++ = ']';
|
||||
*p = '\0';
|
||||
break;
|
||||
case BGP_VMOP_CALL:
|
||||
*p++ = ' ';
|
||||
*p++ = 'F';
|
||||
*p++ = 'N';
|
||||
*p++ = '[';
|
||||
p = Utoa(opa, p);
|
||||
*p++ = ']';
|
||||
*p = '\0';
|
||||
|
||||
if (opa < vm->nfuncs) {
|
||||
Funsym fsym;
|
||||
|
||||
fsym.func = (void (*)(void)) vm->funcs[opa];
|
||||
opastr = Sys_GetSymbolName(fsym.sym);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
// Optional comment after CODELINELEN columns
|
||||
if (opastr)
|
||||
p = CommentCodeLine(buf, opastr);
|
||||
|
||||
// Flush line (no need for '\0')
|
||||
*p++ = '\n';
|
||||
ops->Write(streamp, buf, p - buf);
|
||||
|
||||
// Update indent
|
||||
if (opc == BGP_VMOP_BLK)
|
||||
indent++;
|
||||
if (opc == BGP_VMOP_ENDBLK)
|
||||
indent--;
|
||||
}
|
||||
}
|
66
lonetix/bgp/vm_gccdef.h
Normal file
66
lonetix/bgp/vm_gccdef.h
Normal file
@@ -0,0 +1,66 @@
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
|
||||
/**
|
||||
* \file bgp/vm_gccdef.h
|
||||
*
|
||||
* `#define`s for GNUC optimized BGP VM execution loop.
|
||||
*
|
||||
* \copyright The DoubleFourteen Code Forge (C) All Rights Reserved
|
||||
* \author Lorenzo Cogotti
|
||||
*
|
||||
* \note This file should be `#include`d by `bgp/vm.c`
|
||||
*/
|
||||
|
||||
#ifdef DF_BGP_VMDEF_H_
|
||||
#error "Only one vm_<impl>def.h file may be #include-d"
|
||||
#endif
|
||||
#define DF_BGP_VMDEF_H_
|
||||
|
||||
#define _CONCAT(x, y) x ## y
|
||||
#define _XCONCAT(x, y) _CONCAT(x, y)
|
||||
|
||||
#ifdef __clang__
|
||||
// No __attribute__ on labels in CLANG
|
||||
#define LIKELY
|
||||
#else
|
||||
|
||||
#define LIKELY \
|
||||
_XCONCAT(_BRANCH_PREDICT_HINT, __COUNTER__): \
|
||||
__attribute__((__hot__, __unused__))
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef __clang__
|
||||
// No __attribute__ on labels in CLANG
|
||||
#define UNLIKELY
|
||||
#else
|
||||
|
||||
#define UNLIKELY \
|
||||
_XCONCAT(_BRANCH_PREDICT_HINT, __COUNTER__): \
|
||||
__attribute__((__cold__, __unused__))
|
||||
|
||||
#endif
|
||||
|
||||
#define FETCH(ir, vm) (ir = (vm)->prog[(vm)->pc++])
|
||||
|
||||
#define EXPECT(opcode, ir, vm) \
|
||||
do { \
|
||||
if (__builtin_expect( \
|
||||
BGP_VMOPC((vm)->prog[(vm)->pc]) == BGP_VMOP_ ## opcode, \
|
||||
1 \
|
||||
)) { \
|
||||
ir = (vm)->prog[(vm)->pc++]; \
|
||||
goto EX_ ## opcode; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define DISPATCH(opcode) \
|
||||
_Pragma("GCC diagnostic push"); \
|
||||
_Pragma("GCC diagnostic ignored \"-Wpedantic\"") \
|
||||
goto *bgp_vmOpTab[opcode]; \
|
||||
_Pragma("GCC diagnostic pop") \
|
||||
switch (opcode) // This keeps consistency with regular vm_cdef.h
|
||||
|
||||
#define EXECUTE(opcode) case BGP_VMOP_ ## opcode: EX_ ## opcode
|
||||
|
||||
#define EXECUTE_SIGILL default: EX_SIGILL
|
98
lonetix/bgp/vm_optab.h
Normal file
98
lonetix/bgp/vm_optab.h
Normal file
@@ -0,0 +1,98 @@
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
|
||||
/**
|
||||
* \file bgp/vm_optab.h
|
||||
*
|
||||
* Computed goto table for GNUC optimized BGP VM execution loop.
|
||||
*
|
||||
* \copyright The DoubleFourteen Code Forge (C) All Rights Reserved
|
||||
* \author Lorenzo Cogotti
|
||||
*
|
||||
* File defines a GNUC-specific computed goto table.
|
||||
*
|
||||
* It can be used as an alternative to a regular switch to
|
||||
* accelerate the BGP filtering engine execution loop (Bgp_VmExec()).
|
||||
*
|
||||
* Constraints:
|
||||
* - Array length: 256 (8-bit OPCODE width)
|
||||
* - Label address MUST follow the convention EX_<OPCODE NAME> (e.g. EX_NOP, EX_EXCT)
|
||||
* - Any unused OPCODE MUST be set to EX_SIGILL
|
||||
*
|
||||
* Operations on the computed goto table are defined in: bgp/vm_gccdef.h
|
||||
*
|
||||
* \see [GCC Documentation](https://gcc.gnu.org/onlinedocs/gcc/Labels-as-Values.html)
|
||||
*
|
||||
* \warning KEEP TABLE IN SYNC WITH OPCODES IN: bgp/vm.h
|
||||
*/
|
||||
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wpedantic"
|
||||
#pragma GCC diagnostic ignored "-Woverride-init"
|
||||
|
||||
static void *const bgp_vmOpTab[256] = {
|
||||
// Following clears everything else in the array to SIGILL,
|
||||
// 8 instructions per line (256 &&EX_SIGILL)
|
||||
|
||||
&&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL,
|
||||
&&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL,
|
||||
&&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL,
|
||||
&&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL,
|
||||
&&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL,
|
||||
&&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL,
|
||||
&&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL,
|
||||
&&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL,
|
||||
&&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL,
|
||||
&&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL,
|
||||
&&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL,
|
||||
&&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL,
|
||||
&&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL,
|
||||
&&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL,
|
||||
&&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL,
|
||||
&&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL,
|
||||
&&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL,
|
||||
&&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL,
|
||||
&&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL,
|
||||
&&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL,
|
||||
&&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL,
|
||||
&&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL,
|
||||
&&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL,
|
||||
&&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL,
|
||||
&&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL,
|
||||
&&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL,
|
||||
&&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL,
|
||||
&&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL,
|
||||
&&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL,
|
||||
&&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL,
|
||||
&&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL,
|
||||
&&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL, &&EX_SIGILL,
|
||||
|
||||
// Following initializes valid OPCODEs
|
||||
|
||||
[BGP_VMOP_NOP] = &&EX_NOP,
|
||||
[BGP_VMOP_LOAD] = &&EX_LOAD,
|
||||
[BGP_VMOP_LOADU] = &&EX_LOADU,
|
||||
[BGP_VMOP_LOADN] = &&EX_LOADN,
|
||||
[BGP_VMOP_LOADK] = &&EX_LOADK,
|
||||
[BGP_VMOP_CALL] = &&EX_CALL,
|
||||
[BGP_VMOP_BLK] = &&EX_BLK,
|
||||
[BGP_VMOP_ENDBLK] = &&EX_ENDBLK,
|
||||
[BGP_VMOP_TAG] = &&EX_TAG,
|
||||
[BGP_VMOP_NOT] = &&EX_NOT,
|
||||
[BGP_VMOP_CFAIL] = &&EX_CFAIL,
|
||||
[BGP_VMOP_CPASS] = &&EX_CPASS,
|
||||
[BGP_VMOP_JZ] = &&EX_JZ,
|
||||
[BGP_VMOP_JNZ] = &&EX_JNZ,
|
||||
[BGP_VMOP_CHKT] = &&EX_CHKT,
|
||||
[BGP_VMOP_CHKA] = &&EX_CHKA,
|
||||
[BGP_VMOP_EXCT] = &&EX_EXCT,
|
||||
[BGP_VMOP_SUPN] = &&EX_SUPN,
|
||||
[BGP_VMOP_SUBN] = &&EX_SUBN,
|
||||
[BGP_VMOP_RELT] = &&EX_RELT,
|
||||
[BGP_VMOP_ASMTCH] = &&EX_ASMTCH,
|
||||
[BGP_VMOP_FASMTC] = &&EX_FASMTC,
|
||||
[BGP_VMOP_COMTCH] = &&EX_COMTCH,
|
||||
[BGP_VMOP_ACOMTC] = &&EX_ACOMTC,
|
||||
[BGP_VMOP_END] = &&EX_END
|
||||
};
|
||||
|
||||
#pragma GCC diagnostic pop
|
Reference in New Issue
Block a user