diff --git a/lonetix/bgp/vm.c b/lonetix/bgp/vm.c index 9af09e9..aba7c9e 100644 --- a/lonetix/bgp/vm.c +++ b/lonetix/bgp/vm.c @@ -47,6 +47,8 @@ * by any VM and is always discarded by `Bgp_VmStoreMatch()`. */ static Bgpvmmatch discardMatch; +// Used for empty programs +static const Bgpvmbytec emptyProg = BGP_VMOP_END; Judgement Bgp_InitVm(Bgpvm *vm, size_t heapSiz) { @@ -64,6 +66,7 @@ Judgement Bgp_InitVm(Bgpvm *vm, size_t heapSiz) vm->heap = heap; vm->hMemSiz = siz; vm->hHighMark = siz; + vm->prog = (Bgpvmbytec *) &emptyProg; return Bgp_SetErrStat(BGPENOERR); } @@ -76,8 +79,12 @@ Judgement Bgp_VmEmit(Bgpvm *vm, Bgpvmbytec bytec) if (vm->progLen + 1 >= vm->progCap) { // Grow the VM program segment + Bgpvmbytec *oldProg = vm->prog; + if (vm->prog == &emptyProg) + oldProg = NULL; + size_t newSiz = vm->progCap + BGP_VM_GROWPROGN; - Bgpvmbytec *newProg = (Bgpvmbytec *) realloc(vm->prog, newSiz * sizeof(*newProg)); + Bgpvmbytec *newProg = (Bgpvmbytec *) realloc(oldProg, newSiz * sizeof(*newProg)); if (!newProg) { // Flag the VM as bad vm->setupFailed = TRUE; @@ -157,7 +164,7 @@ Boolean Bgp_VmExec(Bgpvm *vm, Bgpmsg *msg) } // Setup initial VM state - Boolean result = TRUE; // assume PASS unless CFAIL says otherwise + Boolean result = TRUE; // assume pass unless found otherwise vm->pc = 0; vm->si = 0; @@ -217,20 +224,29 @@ Boolean Bgp_VmExec(Bgpvm *vm, Bgpmsg *msg) 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 + if (!Bgp_VmDoBreakPoint(vm, /*breakIf=*/TRUE, /*onBreak=*/FALSE)) { + result = FALSE; // immediate failure goto terminate; } break; EXECUTE(CPASS): - if (Bgp_VmDoCpass(vm)) - goto terminate; // immediate PASS + if (!Bgp_VmDoBreakPoint(vm, /*breakIf=*/TRUE, /*onBreak=*/TRUE)) + goto terminate; // immediate pass + + break; + EXECUTE(ORFAIL): + if (!Bgp_VmDoBreakPoint(vm, /*breakIf=*/FALSE, /*onBreak=*/FALSE)) { + result = FALSE; // immediate failure + goto terminate; + } + + break; + EXECUTE(ORPASS): + if (!Bgp_VmDoBreakPoint(vm, /*breakIf=*/FALSE, /*onBreak=*/TRUE)) + goto terminate; // immediate pass break; EXECUTE(JZ): @@ -243,9 +259,9 @@ Boolean Bgp_VmExec(Bgpvm *vm, Bgpmsg *msg) if (vm->pc > vm->progLen) UNLIKELY vm->errCode = BGPEVMBADJMP; // jump target out of bounds - } else + } else { BGP_VMPOP(vm); // no jump, pop the stack and move on - + } break; EXECUTE(JNZ): if (!BGP_VMCHKSTKSIZ(vm, 1)) UNLIKELY @@ -257,9 +273,9 @@ Boolean Bgp_VmExec(Bgpvm *vm, Bgpmsg *msg) if (vm->pc > vm->progLen) UNLIKELY vm->errCode = BGPEVMBADJMP; // jump target out of bounds - } else + } else { BGP_VMPOP(vm); // no jump, pop the stack and move on - + } break; EXECUTE(CHKT): Bgp_VmDoChkt(vm, (BgpType) BGP_VMOPARG(ir)); @@ -282,9 +298,6 @@ Boolean Bgp_VmExec(Bgpvm *vm, Bgpmsg *msg) EXECUTE(ASMTCH): Bgp_VmDoAsmtch(vm); break; - EXECUTE(FASMTC): - Bgp_VmDoFasmtc(vm); - break; EXECUTE(COMTCH): Bgp_VmDoComtch(vm); break; @@ -348,86 +361,43 @@ void Bgp_VmStoreMatch(Bgpvm *vm) 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) +Boolean Bgp_VmDoBreakPoint(Bgpvm *vm, + Boolean breakIf, + Boolean onBreak) { /* POPS: * -1: Last operation Boolean result (as a Sint64, 0 for FALSE) * * PUSHES: - * * On FAIL result: - * - FALSE + * * On break result: + * - `onBreak` * * Otherwise: * - Nothing. * * SIDE-EFFECTS: - * - Breaks current BLK on FAIL + * - Breaks current BLK if condition is met * - Updates `curMatch->isPassing` flag accordingly */ if (!BGP_VMCHKSTKSIZ(vm, 1)) UNLIKELY - return FALSE; // error, let vm->errCode handle this + return TRUE; // error, let vm->errCode handle this - Boolean shouldTerm = FALSE; // unless proven otherwise + Boolean res = TRUE; // unless we're out of blocks - // 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; + if (!!(v->val) == breakIf) { + // Push `onBreak` and break current BLK + vm->curMatch->isPassing = onBreak; - v->val = FALSE; + v->val = onBreak; if (vm->nblk > 0) Bgp_VmDoBreak(vm); else - shouldTerm = TRUE; // no more BLK + res = FALSE; // no more BLK } else { - // Pop the stack and move on, no FAIL - vm->curMatch->isPassing = TRUE; + // Pop the stack and move on, no break + vm->curMatch->isPassing = !onBreak; BGP_VMPOP(vm); } @@ -435,7 +405,7 @@ Boolean Bgp_VmDoCfail(Bgpvm *vm) // discard anything up to the next relevant operation vm->curMatch = &discardMatch; - return shouldTerm; + return res; } void Bgp_VmDoChkt(Bgpvm *vm, BgpType type) @@ -813,5 +783,6 @@ void Bgp_ResetVm(Bgpvm *vm) void Bgp_ClearVm(Bgpvm *vm) { free(vm->heap); - free(vm->prog); + if (vm->prog != &emptyProg) + free(vm->prog); } diff --git a/lonetix/bgp/vm_asmtch.c b/lonetix/bgp/vm_asmtch.c index b2171cd..bf456ac 100644 --- a/lonetix/bgp/vm_asmtch.c +++ b/lonetix/bgp/vm_asmtch.c @@ -741,65 +741,6 @@ void *Bgp_VmCompileAsMatch(Bgpvm *vm, const Asn *expression, size_t n) } 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); - 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 diff --git a/lonetix/bgp/vm_dump.c b/lonetix/bgp/vm_dump.c index fec1919..34a582a 100644 --- a/lonetix/bgp/vm_dump.c +++ b/lonetix/bgp/vm_dump.c @@ -41,6 +41,8 @@ static const char *OpcString(Bgpvmopc opc) case BGP_VMOP_NOT: return "NOT"; case BGP_VMOP_CFAIL: return "CFAIL"; case BGP_VMOP_CPASS: return "CPASS"; + case BGP_VMOP_ORFAIL: return "ORFAIL"; + case BGP_VMOP_ORPASS: return "ORPASS"; case BGP_VMOP_JZ: return "JZ"; case BGP_VMOP_JNZ: return "JNZ"; case BGP_VMOP_CHKT: return "CHKT"; @@ -50,7 +52,6 @@ static const char *OpcString(Bgpvmopc opc) 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"; @@ -181,7 +182,7 @@ static char *CommentCodeLine(char *line, const char *comment) void Bgp_VmDumpProgram(Bgpvm *vm, void *streamp, const StmOps *ops) { - char explainbuf[64]; + char explainbuf[64]; char buf[256]; int indent = 0; @@ -234,7 +235,7 @@ void Bgp_VmDumpProgram(Bgpvm *vm, void *streamp, const StmOps *ops) p = Utoa(opa, p); if (opc == BGP_VMOP_JZ || opc == BGP_VMOP_JNZ) - opastr = ExplainJump(explainbuf, ip, opa, vm->progLen); + opastr = ExplainJump(explainbuf, ip, opa, vm->progLen); break; case BGP_VMOP_TAG: diff --git a/lonetix/bgp/vm_optab.h b/lonetix/bgp/vm_optab.h index 015a0a3..f032891 100644 --- a/lonetix/bgp/vm_optab.h +++ b/lonetix/bgp/vm_optab.h @@ -80,6 +80,8 @@ static void *const bgp_vmOpTab[256] = { [BGP_VMOP_NOT] = &&EX_NOT, [BGP_VMOP_CFAIL] = &&EX_CFAIL, [BGP_VMOP_CPASS] = &&EX_CPASS, + [BGP_VMOP_ORFAIL] = &&EX_ORFAIL, + [BGP_VMOP_ORPASS] = &&EX_ORPASS, [BGP_VMOP_JZ] = &&EX_JZ, [BGP_VMOP_JNZ] = &&EX_JNZ, [BGP_VMOP_CHKT] = &&EX_CHKT, @@ -89,7 +91,6 @@ static void *const bgp_vmOpTab[256] = { [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 diff --git a/lonetix/include/df/bgp/vm.h b/lonetix/include/df/bgp/vm.h index d094483..189b4ee 100755 --- a/lonetix/include/df/bgp/vm.h +++ b/lonetix/include/df/bgp/vm.h @@ -75,26 +75,40 @@ FORCE_INLINE Uint8 BGP_VMOPARG(Bgpvmbytec bytec) #define BGP_VMOP_TAG U8_C(8) /// NOT - Boolean negate the topmost stack element #define BGP_VMOP_NOT U8_C(9) -/// CONDITIONAL FAIL If TRUE - Fail the current matching `BLK` if topmost stack element is non-zero +/// CONDITIONAL FAIL If TRUE - Fail current match `BLK` if topmost stack element is non-zero #define BGP_VMOP_CFAIL U8_C(10) -/// CONDITIONAL PASS If TRUE - Pass the current matching `BLK` if topmost stack element is non-zero +/// CONDITIONAL PASS If TRUE - Pass current match `BLK` if topmost stack element is non-zero #define BGP_VMOP_CPASS U8_C(11) +/// FAIL IF FALSE - Fail current match `BLK` if topmost stack element is zero +#define BGP_VMOP_ORFAIL U8_C(12) +/// PASS IF FALSE - Pass current match `BLK` if topmost stack element is zero +#define BGP_VMOP_ORPASS U8_C(13) + +FORCE_INLINE Boolean BGP_ISVMOPBREAKING(Bgpvmopc opc) +{ + return opc >= BGP_VMOP_CFAIL && opc <= BGP_VMOP_ORPASS; +} /// Jump if zero - Skip over a positive number of instructions if topmost stack element is 0. -#define BGP_VMOP_JZ U8_C(12) +#define BGP_VMOP_JZ U8_C(14) /// Jump if non-zero - Skip over a positive number of instructions if topmost stack element is not 0. -#define BGP_VMOP_JNZ U8_C(13) +#define BGP_VMOP_JNZ U8_C(15) + +FORCE_INLINE Boolean Bgp_ISVMOPJMP(Bgpvmopc opc) +{ + return opc == BGP_VMOP_JZ || opc == BGP_VMOP_JNZ; +} /// CHECK TYPE - ARG is the `BgpType` to test against -#define BGP_VMOP_CHKT U8_C(14) +#define BGP_VMOP_CHKT U8_C(16) /// CHECK ATTRIBUTE - ARG is the `BgpAttrCode` to test for existence -#define BGP_VMOP_CHKA U8_C(15) +#define BGP_VMOP_CHKA U8_C(17) -#define BGP_VMOP_EXCT U8_C(16) -#define BGP_VMOP_SUPN U8_C(17) -#define BGP_VMOP_SUBN U8_C(18) +#define BGP_VMOP_EXCT U8_C(18) +#define BGP_VMOP_SUPN U8_C(19) +#define BGP_VMOP_SUBN U8_C(20) /// RELATED - Tests whether the BGP message contains prefixes related with the provided ones -#define BGP_VMOP_RELT U8_C(19) +#define BGP_VMOP_RELT U8_C(21) /// Returns `TRUE` if `opc` belongs to an instruction operating on NETwork prefixes. FORCE_INLINE Boolean BGP_ISVMOPNET(Bgpvmopc opc) @@ -102,17 +116,20 @@ FORCE_INLINE Boolean BGP_ISVMOPNET(Bgpvmopc opc) return opc >= BGP_VMOP_EXCT && opc <= BGP_VMOP_RELT; } -/// AS PATH MATCH - Tests BGP message AS PATH against a match expression -#define BGP_VMOP_ASMTCH U8_C(20) -/// FAST AS PATH MATCH - AS PATH test using precompiled AS PATH match expression -#define BGP_VMOP_FASMTC U8_C(21) +/// AS PATH MATCH - Tests BGP message AS PATH against a precompiled match expression +#define BGP_VMOP_ASMTCH U8_C(22) /// COMMUNITY MATCH - COMMUNITY test using a precompiled COMMUNITY match expression -#define BGP_VMOP_COMTCH U8_C(22) +#define BGP_VMOP_COMTCH U8_C(23) /// ALL COMMUNITY MATCH - Like COMTCH, but requires all communities to be present inside message -#define BGP_VMOP_ACOMTC U8_C(23) +#define BGP_VMOP_ACOMTC U8_C(24) /// END - Terminate VM execution with the latest result -#define BGP_VMOP_END U8_C(24) +#define BGP_VMOP_END U8_C(25) + +FORCE_INLINE Boolean BGP_ISVMOPENDING(Bgpvmopc opc) +{ + return opc == BGP_VMOP_ENDBLK || opc == BGP_VMOP_END; +} // #define BGP_VMOP_MOVK MOVE K - Move topmost K index to ARG K index // #define BGP_VMOP_DISCRD DISCARD - discard vm->curMatch if any diff --git a/lonetix/include/df/bgp/vmintrin.h b/lonetix/include/df/bgp/vmintrin.h index 3627f71..8416d6e 100755 --- a/lonetix/include/df/bgp/vmintrin.h +++ b/lonetix/include/df/bgp/vmintrin.h @@ -201,10 +201,11 @@ FORCE_INLINE void Bgp_VmDoCall(Bgpvm *vm, Uint8 idx) if (fn) fn(vm); } -/// Implement `CPASS` (Conditional `PASS` if `TRUE`). -Boolean Bgp_VmDoCpass(Bgpvm *vm); -/// Implement `CFAIL` (Conditional `FAIL` if `TRUE`). -Boolean Bgp_VmDoCfail(Bgpvm *vm); +/** + * \brief Implement `CPASS`, `CFAIL`, `ORPASS`, `ORFAIL`, depending + * on break condition and value. + */ +Boolean Bgp_VmDoBreakPoint(Bgpvm *vm, Boolean breakIf, Boolean onBreak); /// Implement `TAG` instruction with argument `tag`. FORCE_INLINE void Bgp_VmDoTag(Bgpvm *vm, Uint8 tag) @@ -242,7 +243,6 @@ void Bgp_VmDoSubn(Bgpvm *vm, Uint8 arg); void Bgp_VmDoRelt(Bgpvm *vm, Uint8 arg); void Bgp_VmDoAsmtch(Bgpvm *vm); -void Bgp_VmDoFasmtc(Bgpvm *vm); void Bgp_VmDoComtch(Bgpvm *vm); void Bgp_VmDoAcomtc(Bgpvm *vm);