You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

226 lines
5.0 KiB
C

// SPDX-License-Identifier: GPL-3.0-or-later
/**
* \file bgpgrep_peer.c
*
* Peer matching expressions compilation.
*
* \copyright The DoubleFourteen Code Forge (C) All Rights Reserved
* \author Lorenzo Cogotti
*/
#include "bgpgrep_local.h"
#include "sys/fs.h"
#include "sys/endian.h"
#include "sys/sys.h"
#include "lexer.h"
#include "numlib.h"
#include <assert.h>
#include <stdlib.h>
#include <string.h>
typedef struct {
Peerlistop *head;
Peerlistop **lastp;
} Peermatch;
// Diagnostics used inside ParsePeerMatch()
#define WARN(fmt, ...) ((void) ((lexp) ? \
LexerWarning(lexp, fmt, __VA_ARGS__) : \
Bgpgrep_Warning("%s: " fmt, BgpgrepC_CurTerm(), __VA_ARGS__)))
#define FATAL(fmt, ...) ((void) ((lexp) ? \
LexerError(lexp, fmt, __VA_ARGS__) : \
Bgpgrep_Fatal("%s: " fmt, BgpgrepC_CurTerm(), __VA_ARGS__)))
static void ParsePeerMatch(Peermatch *dest, const char *tok, Lex *lexp)
{
Peeraddropc opc;
Ipadr addr;
Asn asn;
char *ep, *addrp, *asnp;
unsigned long long n;
NumConvRet res;
Boolean notFlag;
asn = ASN_ANY;
// Peer matches are in the form: "[[!]ip address] [[!]ASN]"
//
// Quotes may be omitted when matching only [ip address] or [ASN]
//
// At least one between [ip address] and [ASN] must be specified for
// each expression
ep = strchr(tok, ' ');
if (ep) {
// Both peer address and ASN specified
n = ep - tok;
addrp = (char *) alloca(n + 1);
memcpy(addrp, tok, n);
addrp[n] = '\0';
opc = PEER_ADDR_EQ;
if (*addrp == '!') {
opc = PEER_ADDR_NE;
addrp++;
}
if (Ip_StringToAdr(addrp, &addr) != OK)
FATAL("Got '%s' while expecting IP address", addrp);
asnp = ep + 1;
while (*asnp == ' ') asnp++;
notFlag = FALSE;
if (*asnp == '!') {
notFlag = TRUE;
asnp++;
}
n = Atoull(asnp, &ep, 10, &res);
if (res != NCVENOERR || *ep != '\0' || n > 0xffffffffuLL)
FATAL("Bad ASN '%s'", asnp);
asn = ASN32BIT(beswap32(n));
if (notFlag)
asn = ASNNOT(asn);
} else {
notFlag = FALSE;
if (*tok == '!') {
notFlag = TRUE;
tok++;
}
if (Ip_StringToAdr(tok, &addr) == OK) {
opc = notFlag ? PEER_ADDR_NE : PEER_ADDR_EQ;
} else {
// attempt ASN
n = Atoull(tok, &ep, 10, &res);
if (res != NCVENOERR || *ep != '\0' || n > 0xffffffffuLL)
FATAL("Got '%s' while expecting IP or ASN", tok);
opc = PEER_ADDR_NOP;
memset(&addr, 0, sizeof(addr));
asn = ASN32BIT(beswap32(n));
if (notFlag)
asn = ASNNOT(asn);
}
}
if (ISASTRANS(asn))
WARN("'%s': AS_TRANS in peer match expression", tok);
Peerlistop *p = Bgp_VmPermAlloc(&S.vm, sizeof(*p));
if (!p)
Bgpgrep_Fatal("Too many peer matches");
// Store match
p->opc = opc;
p->addr = addr;
p->asn = asn;
// Append to match list
p->next = *dest->lastp;
*dest->lastp = p;
dest->lastp = &p->next;
}
static Judgement ParsePeerMatchFile(Peermatch *dest, const char *filename)
{
Lex p;
Tok tok;
// Map file in memory
Fildes fd = Sys_Fopen(filename, FM_READ, FH_SEQ);
if (fd == FILDES_BAD)
return NG;
Sint64 fsiz = Sys_FileSize(fd); // NOTE: aborts on failure
assert(fsiz >= 0);
char *text = (char *) malloc(fsiz);
if (!text)
Sys_OutOfMemory();
Sint64 n = Sys_Fread(fd, text, fsiz);
if (n != fsiz)
Bgpgrep_Fatal("Short read from file '%s'", filename);
Sys_Fclose(fd);
// Setup Lex and start reading strings
unsigned flags = L_NOSTRCAT | L_ALLOWIPADDR | L_PLAINIPADDRONLY | L_COLORED;
if (S.noColor)
flags &= ~L_COLORED;
memset(&p, 0, sizeof(p));
BeginLexerSession(&p, filename, /*line=*/1);
SetLexerTextn(&p, text, fsiz);
SetLexerFlags(&p, flags);
SetLexerErrorFunc(&p, LEX_QUIT, LEX_WARN, NULL);
char buf[1 + MAXTOKLEN + 1];
while (Lex_ReadToken(&p, &tok)) {
strcpy(buf, tok.text);
if (tok.type == TT_PUNCT && tok.subtype == P_LOGIC_NOT) {
// Special case for straight "[[!]ip address]/[[!]ASN" with no quotes
if (tok.spacesBeforeToken == 0)
LexerError(&p, "%s: '!' following previous peer match expression requires space", BgpgrepC_CurTerm());
Lex_MatchAnyToken(&p, &tok);
strcat(buf, tok.text);
}
ParsePeerMatch(dest, buf, &p);
}
free(text);
return OK;
}
Sint32 BgpgrepC_ParsePeerExpression(void)
{
const char *tok = BgpgrepC_ExpectAnyToken();
if (strcmp(tok, "()") == 0)
return -1; // so the user doesn't have to split cmd line like \( \)
Peermatch matches;
memset(&matches, 0, sizeof(matches));
matches.lastp = &matches.head;
if (strcmp(tok, "(") == 0) {
// Explicit inline peer match list
while (TRUE) {
tok = BgpgrepC_ExpectAnyToken();
if (strcmp(tok, ")") == 0)
break;
ParsePeerMatch(&matches, tok, NULL);
}
} else {
// Anything else is interpreted as either:
// - A file argument, containing a space-separated prefix list
// - A single match, if the file is not found
// (shorthand for "( <peer match> )" )
if (ParsePeerMatchFile(&matches, tok) != OK)
ParsePeerMatch(&matches, tok, NULL);
}
if (!matches.head)
return -1; // empty list
Sint32 kidx = BGP_VMSETKA(&S.vm, Bgp_VmNewk(&S.vm), matches.head);
if (kidx < 0)
Bgpgrep_Fatal("Too many peer match expressions");
return kidx;
}