1998-11-27 19:35:50 +00:00
|
|
|
/*
|
|
|
|
* BIRD -- Configuration Lexer
|
|
|
|
*
|
2000-01-17 11:52:50 +00:00
|
|
|
* (c) 1998--2000 Martin Mares <mj@ucw.cz>
|
1998-11-27 19:35:50 +00:00
|
|
|
*
|
|
|
|
* Can be freely distributed and used under the terms of the GNU GPL.
|
|
|
|
*/
|
|
|
|
|
2000-06-03 18:23:00 +00:00
|
|
|
/**
|
2000-06-07 12:29:08 +00:00
|
|
|
* DOC: Lexical analyzer
|
2000-06-03 18:23:00 +00:00
|
|
|
*
|
2000-06-07 12:29:08 +00:00
|
|
|
* The lexical analyzer used for configuration files and CLI commands
|
2000-06-07 13:25:53 +00:00
|
|
|
* is generated using the |flex| tool accompanied by a couple of
|
2000-06-03 18:23:00 +00:00
|
|
|
* functions maintaining the hash tables containing information about
|
|
|
|
* symbols and keywords.
|
|
|
|
*
|
|
|
|
* Each symbol is represented by a &symbol structure containing name
|
2013-07-25 20:33:57 +00:00
|
|
|
* of the symbol, its lexical scope, symbol class (%SYM_PROTO for a
|
|
|
|
* name of a protocol, %SYM_CONSTANT for a constant etc.) and class
|
|
|
|
* dependent data. When an unknown symbol is encountered, it's
|
|
|
|
* automatically added to the symbol table with class %SYM_VOID.
|
2000-06-03 18:23:00 +00:00
|
|
|
*
|
|
|
|
* The keyword tables are generated from the grammar templates
|
|
|
|
* using the |gen_keywords.m4| script.
|
|
|
|
*/
|
|
|
|
|
1998-11-27 19:35:50 +00:00
|
|
|
%{
|
1998-12-06 23:10:28 +00:00
|
|
|
#undef REJECT /* Avoid name clashes */
|
1998-11-27 19:35:50 +00:00
|
|
|
|
|
|
|
#include <errno.h>
|
|
|
|
#include <stdlib.h>
|
1998-12-06 23:10:28 +00:00
|
|
|
#include <stdarg.h>
|
2016-08-16 07:23:55 +00:00
|
|
|
#include <stdint.h>
|
2011-11-10 08:22:20 +00:00
|
|
|
#include <unistd.h>
|
2012-07-18 17:29:33 +00:00
|
|
|
#include <libgen.h>
|
|
|
|
#include <glob.h>
|
|
|
|
#include <fcntl.h>
|
|
|
|
#include <sys/stat.h>
|
|
|
|
#include <sys/types.h>
|
|
|
|
#include <sys/stat.h>
|
1998-11-27 19:35:50 +00:00
|
|
|
|
2011-03-13 10:33:50 +00:00
|
|
|
#define PARSER 1
|
|
|
|
|
1998-11-27 19:35:50 +00:00
|
|
|
#include "nest/bird.h"
|
1999-11-15 11:35:41 +00:00
|
|
|
#include "nest/route.h"
|
2010-02-19 23:03:31 +00:00
|
|
|
#include "nest/protocol.h"
|
1999-11-15 11:35:41 +00:00
|
|
|
#include "filter/filter.h"
|
2019-02-07 20:25:38 +00:00
|
|
|
#include "filter/f-inst.h"
|
1998-11-27 19:35:50 +00:00
|
|
|
#include "conf/conf.h"
|
|
|
|
#include "conf/cf-parse.tab.h"
|
2000-01-17 11:52:50 +00:00
|
|
|
#include "lib/string.h"
|
2017-05-25 21:30:39 +00:00
|
|
|
#include "lib/hash.h"
|
1998-11-27 19:35:50 +00:00
|
|
|
|
2000-04-28 15:12:03 +00:00
|
|
|
struct keyword {
|
1998-11-27 19:35:50 +00:00
|
|
|
byte *name;
|
|
|
|
int value;
|
2000-04-28 15:12:03 +00:00
|
|
|
};
|
|
|
|
|
1999-01-10 00:18:32 +00:00
|
|
|
#include "conf/keywords.h"
|
1998-11-27 19:35:50 +00:00
|
|
|
|
2017-05-30 12:43:49 +00:00
|
|
|
/* Could be defined by Bison in cf-parse.tab.h, inteferes with SYM hash */
|
|
|
|
#ifdef SYM
|
|
|
|
#undef SYM
|
|
|
|
#endif
|
|
|
|
|
1999-11-30 14:03:36 +00:00
|
|
|
|
2019-02-25 16:19:47 +00:00
|
|
|
static uint cf_hash(const byte *c);
|
1998-11-27 19:35:50 +00:00
|
|
|
|
2022-05-02 18:29:03 +00:00
|
|
|
#define SYM_KEY(n) n->name
|
2017-05-25 21:30:39 +00:00
|
|
|
#define SYM_NEXT(n) n->next
|
2022-05-02 18:29:03 +00:00
|
|
|
#define SYM_EQ(a,b) !strcmp(a,b)
|
|
|
|
#define SYM_FN(k) cf_hash(k)
|
|
|
|
#define SYM_ORDER 4 /* Initial */
|
2017-05-25 21:30:39 +00:00
|
|
|
|
|
|
|
#define SYM_REHASH sym_rehash
|
2022-05-02 18:29:03 +00:00
|
|
|
#define SYM_PARAMS /8, *1, 2, 2, 4, 20
|
2017-05-25 21:30:39 +00:00
|
|
|
|
|
|
|
|
|
|
|
HASH_DEFINE_REHASH_FN(SYM, struct symbol)
|
|
|
|
|
2022-05-02 18:29:03 +00:00
|
|
|
struct sym_scope *global_root_scope;
|
Filter: Methods rework
Methods can now be called as x.m(y), as long as x can have its type
inferred in config time. If used as a command, it modifies the object,
if used as a value, it keeps the original object intact.
Also functions add(x,y), delete(x,y), filter(x,y) and prepend(x,y) now
spit a warning and are considered deprecated.
It's also possible to call a method on a constant, see filter/test.conf
for examples like bgp_path = +empty+.prepend(1).
Inside instruction definitions (filter/f-inst.c), a METHOD_CONSTRUCTOR()
call is added, which registers the instruction as a method for the type
of its first argument. Each type has its own method symbol table and
filter parser switches between them based on the inferred type of the
object calling the method.
Also FI_CLIST_(ADD|DELETE|FILTER) instructions have been split to allow
for this method dispatch. With type inference, it's now possible.
2023-06-16 15:35:37 +00:00
|
|
|
pool *global_root_scope_pool;
|
|
|
|
linpool *global_root_scope_linpool;
|
1998-11-27 19:35:50 +00:00
|
|
|
|
1998-12-06 11:59:18 +00:00
|
|
|
linpool *cfg_mem;
|
1998-11-27 19:35:50 +00:00
|
|
|
|
2011-09-11 19:21:47 +00:00
|
|
|
int (*cf_read_hook)(byte *buf, unsigned int max, int fd);
|
2011-10-09 23:01:58 +00:00
|
|
|
struct include_file_stack *ifs;
|
2012-07-18 17:29:33 +00:00
|
|
|
static struct include_file_stack *ifs_head;
|
|
|
|
|
2019-02-25 16:19:47 +00:00
|
|
|
#define QUOTED_BUFFER_SIZE 4096
|
|
|
|
static BUFFER_(char) quoted_buffer;
|
|
|
|
static char quoted_buffer_data[QUOTED_BUFFER_SIZE];
|
|
|
|
static inline void quoted_buffer_init(void) {
|
|
|
|
quoted_buffer.used = 0;
|
|
|
|
quoted_buffer.size = QUOTED_BUFFER_SIZE;
|
|
|
|
quoted_buffer.data = quoted_buffer_data;
|
|
|
|
}
|
|
|
|
|
2012-07-18 17:29:33 +00:00
|
|
|
#define MAX_INCLUDE_DEPTH 8
|
1998-11-27 19:35:50 +00:00
|
|
|
|
2012-07-18 17:29:33 +00:00
|
|
|
#define YY_INPUT(buf,result,max) result = cf_read_hook(buf, max, ifs->fd);
|
1998-11-27 19:35:50 +00:00
|
|
|
#define YY_NO_UNPUT
|
|
|
|
#define YY_FATAL_ERROR(msg) cf_error(msg)
|
2018-09-11 14:55:41 +00:00
|
|
|
#define YY_USER_ACTION ifs->chno += yyleng; ifs->toklen = yyleng;
|
1998-11-27 19:35:50 +00:00
|
|
|
|
2012-07-18 17:29:33 +00:00
|
|
|
static void cf_include(char *arg, int alen);
|
2011-09-11 19:21:47 +00:00
|
|
|
static int check_eof(void);
|
|
|
|
|
2019-02-25 16:19:47 +00:00
|
|
|
static enum yytokentype cf_lex_symbol(const char *data);
|
|
|
|
|
1998-11-27 19:35:50 +00:00
|
|
|
%}
|
|
|
|
|
|
|
|
%option noyywrap
|
2010-05-02 20:41:40 +00:00
|
|
|
%option noinput
|
|
|
|
%option nounput
|
|
|
|
%option noreject
|
1998-11-27 19:35:50 +00:00
|
|
|
|
2019-02-25 16:19:47 +00:00
|
|
|
%x COMMENT CCOMM CLI QUOTED APOSTROPHED INCLUDE
|
1998-11-27 19:35:50 +00:00
|
|
|
|
|
|
|
ALPHA [a-zA-Z_]
|
|
|
|
DIGIT [0-9]
|
|
|
|
XIGIT [0-9a-fA-F]
|
|
|
|
ALNUM [a-zA-Z_0-9]
|
|
|
|
WHITE [ \t]
|
|
|
|
|
|
|
|
%%
|
2019-02-25 16:19:47 +00:00
|
|
|
^{WHITE}*include{WHITE}*\" {
|
2012-07-18 17:29:33 +00:00
|
|
|
if (!ifs->depth)
|
|
|
|
cf_error("Include not allowed in CLI");
|
|
|
|
|
2019-02-25 16:19:47 +00:00
|
|
|
BEGIN(INCLUDE);
|
|
|
|
}
|
|
|
|
|
|
|
|
<INCLUDE>[^"\n]+["]{WHITE}*; {
|
|
|
|
char *start, *end;
|
|
|
|
|
|
|
|
start = yytext;
|
2012-07-18 17:29:33 +00:00
|
|
|
|
|
|
|
end = strchr(start, '"');
|
|
|
|
*end = 0;
|
|
|
|
|
|
|
|
if (start == end)
|
|
|
|
cf_error("Include with empty argument");
|
|
|
|
|
|
|
|
cf_include(start, end-start);
|
2019-02-25 16:19:47 +00:00
|
|
|
|
|
|
|
BEGIN(INITIAL);
|
2012-07-18 17:29:33 +00:00
|
|
|
}
|
1998-11-27 19:35:50 +00:00
|
|
|
|
2019-02-25 16:19:47 +00:00
|
|
|
<INCLUDE>["] cf_error("Include with empty argument");
|
|
|
|
<INCLUDE>. cf_error("Unterminated include");
|
|
|
|
<INCLUDE>\n cf_error("Unterminated include");
|
|
|
|
<INCLUDE><<EOF>> cf_error("Unterminated include");
|
|
|
|
|
|
|
|
|
2017-03-28 15:35:32 +00:00
|
|
|
{DIGIT}+:{DIGIT}+ {
|
2017-05-18 12:51:36 +00:00
|
|
|
uint len1 UNUSED, len2;
|
|
|
|
u64 l;
|
2017-03-28 15:35:32 +00:00
|
|
|
char *e;
|
|
|
|
|
|
|
|
errno = 0;
|
2019-06-13 12:27:58 +00:00
|
|
|
l = bstrtoul10(yytext, &e);
|
2019-08-17 13:03:09 +00:00
|
|
|
if (!e || (*e != ':') || (errno == ERANGE) || (l >> 32))
|
2017-03-28 15:35:32 +00:00
|
|
|
cf_error("ASN out of range");
|
|
|
|
|
|
|
|
if (l >> 16)
|
|
|
|
{
|
|
|
|
len1 = 32;
|
|
|
|
len2 = 16;
|
|
|
|
cf_lval.i64 = (2ULL << 48) | (((u64) l) << len2);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
len1 = 16;
|
|
|
|
len2 = 32;
|
|
|
|
cf_lval.i64 = 0 | (((u64) l) << len2);
|
|
|
|
}
|
|
|
|
|
|
|
|
errno = 0;
|
2019-06-13 12:27:58 +00:00
|
|
|
l = bstrtoul10(e+1, &e);
|
2019-08-17 13:03:09 +00:00
|
|
|
if (!e || *e || (errno == ERANGE) || (l >> len2))
|
2017-03-28 15:35:32 +00:00
|
|
|
cf_error("Number out of range");
|
|
|
|
cf_lval.i64 |= l;
|
|
|
|
|
|
|
|
return VPN_RD;
|
|
|
|
}
|
|
|
|
|
2016-02-09 13:53:29 +00:00
|
|
|
[02]:{DIGIT}+:{DIGIT}+ {
|
2017-05-18 12:51:36 +00:00
|
|
|
uint len1, len2;
|
|
|
|
u64 l;
|
2016-02-09 13:53:29 +00:00
|
|
|
char *e;
|
|
|
|
|
|
|
|
if (yytext[0] == '0')
|
2017-02-20 01:26:45 +00:00
|
|
|
{
|
2016-02-09 13:53:29 +00:00
|
|
|
cf_lval.i64 = 0;
|
2017-02-20 01:26:45 +00:00
|
|
|
len1 = 16;
|
|
|
|
len2 = 32;
|
|
|
|
}
|
2016-02-09 13:53:29 +00:00
|
|
|
else
|
2017-02-20 01:26:45 +00:00
|
|
|
{
|
|
|
|
cf_lval.i64 = 2ULL << 48;
|
|
|
|
len1 = 32;
|
|
|
|
len2 = 16;
|
|
|
|
}
|
2016-02-09 13:53:29 +00:00
|
|
|
|
|
|
|
errno = 0;
|
2019-06-13 12:27:58 +00:00
|
|
|
l = bstrtoul10(yytext+2, &e);
|
2019-08-17 13:03:09 +00:00
|
|
|
if (!e || (*e != ':') || (errno == ERANGE) || (l >> len1))
|
2016-02-09 13:53:29 +00:00
|
|
|
cf_error("ASN out of range");
|
2017-02-20 01:26:45 +00:00
|
|
|
cf_lval.i64 |= ((u64) l) << len2;
|
|
|
|
|
2016-02-09 13:53:29 +00:00
|
|
|
errno = 0;
|
2019-06-13 12:27:58 +00:00
|
|
|
l = bstrtoul10(e+1, &e);
|
2019-08-17 13:03:09 +00:00
|
|
|
if (!e || *e || (errno == ERANGE) || (l >> len2))
|
2017-02-20 01:26:45 +00:00
|
|
|
cf_error("Number out of range");
|
2016-02-09 13:53:29 +00:00
|
|
|
cf_lval.i64 |= l;
|
2017-02-20 01:26:45 +00:00
|
|
|
|
2016-02-09 13:53:29 +00:00
|
|
|
return VPN_RD;
|
|
|
|
}
|
|
|
|
|
2017-03-28 15:35:32 +00:00
|
|
|
{DIGIT}+\.{DIGIT}+\.{DIGIT}+\.{DIGIT}+:{DIGIT}+ {
|
2016-02-09 13:53:29 +00:00
|
|
|
unsigned long int l;
|
|
|
|
ip4_addr ip4;
|
2017-02-20 01:26:45 +00:00
|
|
|
char *e;
|
|
|
|
|
|
|
|
cf_lval.i64 = 1ULL << 48;
|
|
|
|
|
2017-03-28 15:35:32 +00:00
|
|
|
e = strchr(yytext, ':');
|
2017-02-20 01:26:45 +00:00
|
|
|
*e++ = '\0';
|
2017-03-28 15:35:32 +00:00
|
|
|
if (!ip4_pton(yytext, &ip4))
|
|
|
|
cf_error("Invalid IPv4 address %s in Route Distinguisher", yytext);
|
2017-02-20 01:26:45 +00:00
|
|
|
cf_lval.i64 |= ((u64) ip4_to_u32(ip4)) << 16;
|
|
|
|
|
2016-02-09 13:53:29 +00:00
|
|
|
errno = 0;
|
2019-06-13 12:27:58 +00:00
|
|
|
l = bstrtoul10(e, &e);
|
2019-08-17 13:03:09 +00:00
|
|
|
if (!e || *e || (errno == ERANGE) || (l >> 16))
|
2017-02-20 01:26:45 +00:00
|
|
|
cf_error("Number out of range");
|
|
|
|
cf_lval.i64 |= l;
|
|
|
|
|
2016-02-09 13:53:29 +00:00
|
|
|
return VPN_RD;
|
|
|
|
}
|
|
|
|
|
1998-11-27 19:35:50 +00:00
|
|
|
{DIGIT}+\.{DIGIT}+\.{DIGIT}+\.{DIGIT}+ {
|
2015-11-05 11:48:52 +00:00
|
|
|
if (!ip4_pton(yytext, &cf_lval.ip4))
|
2014-10-24 09:11:43 +00:00
|
|
|
cf_error("Invalid IPv4 address %s", yytext);
|
2015-11-05 11:48:52 +00:00
|
|
|
return IP4;
|
1999-08-03 19:36:06 +00:00
|
|
|
}
|
|
|
|
|
2023-06-23 14:47:37 +00:00
|
|
|
({XIGIT}{2}){16,}|{XIGIT}{2}(:{XIGIT}{2}){15,}|hex:({XIGIT}{2}(:?{XIGIT}{2})*)? {
|
2023-08-24 01:04:58 +00:00
|
|
|
char *s = yytext;
|
|
|
|
struct bytestring *bs;
|
2021-04-14 19:39:43 +00:00
|
|
|
|
2023-08-24 01:04:58 +00:00
|
|
|
/* Skip 'hex:' prefix */
|
|
|
|
if (s[0] == 'h' && s[1] == 'e' && s[2] == 'x' && s[3] == ':')
|
|
|
|
s += 4;
|
|
|
|
|
|
|
|
int len = bstrhextobin(s, NULL);
|
|
|
|
if (len < 0)
|
|
|
|
cf_error("Invalid hex string");
|
|
|
|
|
|
|
|
bs = cfg_allocz(sizeof(struct bytestring) + len);
|
|
|
|
bs->length = bstrhextobin(s, bs->data);
|
|
|
|
ASSERT(bs->length == len);
|
|
|
|
|
|
|
|
cf_lval.bs = bs;
|
2023-08-24 02:30:42 +00:00
|
|
|
return BYTETEXT;
|
2021-04-14 19:39:43 +00:00
|
|
|
}
|
|
|
|
|
1999-08-03 19:36:06 +00:00
|
|
|
({XIGIT}*::|({XIGIT}*:){3,})({XIGIT}*|{DIGIT}+\.{DIGIT}+\.{DIGIT}+\.{DIGIT}+) {
|
2015-11-05 11:48:52 +00:00
|
|
|
if (!ip6_pton(yytext, &cf_lval.ip6))
|
|
|
|
cf_error("Invalid IPv6 address %s", yytext);
|
|
|
|
return IP6;
|
1998-11-27 19:35:50 +00:00
|
|
|
}
|
|
|
|
|
2010-05-02 20:41:40 +00:00
|
|
|
0x{XIGIT}+ {
|
1998-11-27 19:35:50 +00:00
|
|
|
char *e;
|
2011-12-22 12:44:43 +00:00
|
|
|
unsigned long int l;
|
1998-11-27 19:35:50 +00:00
|
|
|
errno = 0;
|
2019-06-13 12:27:58 +00:00
|
|
|
l = bstrtoul16(yytext+2, &e);
|
2019-08-17 13:03:09 +00:00
|
|
|
if (!e || *e || errno == ERANGE || (unsigned long int)(unsigned int) l != l)
|
1998-11-27 19:35:50 +00:00
|
|
|
cf_error("Number out of range");
|
|
|
|
cf_lval.i = l;
|
|
|
|
return NUM;
|
|
|
|
}
|
|
|
|
|
|
|
|
{DIGIT}+ {
|
|
|
|
char *e;
|
2011-12-22 12:44:43 +00:00
|
|
|
unsigned long int l;
|
1998-11-27 19:35:50 +00:00
|
|
|
errno = 0;
|
2019-06-13 12:27:58 +00:00
|
|
|
l = bstrtoul10(yytext, &e);
|
2019-08-17 13:03:09 +00:00
|
|
|
if (!e || *e || errno == ERANGE || (unsigned long int)(unsigned int) l != l)
|
1998-11-27 19:35:50 +00:00
|
|
|
cf_error("Number out of range");
|
|
|
|
cf_lval.i = l;
|
|
|
|
return NUM;
|
|
|
|
}
|
|
|
|
|
2011-03-23 11:49:53 +00:00
|
|
|
else: {
|
|
|
|
/* Hack to distinguish if..else from else: in case */
|
|
|
|
return ELSECOL;
|
|
|
|
}
|
|
|
|
|
2019-02-25 16:19:47 +00:00
|
|
|
['] {
|
|
|
|
BEGIN(APOSTROPHED);
|
|
|
|
quoted_buffer_init();
|
|
|
|
}
|
2017-05-25 21:30:39 +00:00
|
|
|
|
2019-02-25 16:19:47 +00:00
|
|
|
<APOSTROPHED>{ALNUM}|[-]|[.:] BUFFER_PUSH(quoted_buffer) = yytext[0];
|
|
|
|
<APOSTROPHED>\n cf_error("Unterminated symbol");
|
|
|
|
<APOSTROPHED><<EOF>> cf_error("Unterminated symbol");
|
|
|
|
<APOSTROPHED>['] {
|
|
|
|
BEGIN(INITIAL);
|
|
|
|
BUFFER_PUSH(quoted_buffer) = 0;
|
|
|
|
return cf_lex_symbol(quoted_buffer_data);
|
|
|
|
}
|
|
|
|
<APOSTROPHED>. cf_error("Invalid character in apostrophed symbol");
|
2017-05-25 21:30:39 +00:00
|
|
|
|
2019-02-25 16:19:47 +00:00
|
|
|
({ALPHA}{ALNUM}*) {
|
|
|
|
return cf_lex_symbol(yytext);
|
1998-11-27 19:35:50 +00:00
|
|
|
}
|
|
|
|
|
1999-12-02 12:04:39 +00:00
|
|
|
<CLI>(.|\n) {
|
Parse CLI commands. We use the same parser as for configuration files (because
we want to allow filter and similar complex constructs to be used in commands
and we should avoid code duplication), only with CLI_MARKER token prepended
before the whole input.
Defined macro CF_CLI(cmd, args, help) for defining CLI commands in .Y files.
The first argument specifies the command itself, the remaining two arguments
are copied to the help file (er, will be copied after the help file starts
to exist). This macro automatically creates a skeleton rule for the command,
you only need to append arguments as in:
CF_CLI(STEAL MONEY, <$>, [[Steal <$> US dollars or equivalent in any other currency]]): NUM {
cli_msg(0, "%d$ stolen", $3);
} ;
Also don't forget to reset lexer state between inputs.
1999-10-31 17:47:47 +00:00
|
|
|
BEGIN(INITIAL);
|
|
|
|
return CLI_MARKER;
|
|
|
|
}
|
|
|
|
|
2011-05-06 20:00:54 +00:00
|
|
|
\.\. {
|
|
|
|
return DDOT;
|
|
|
|
}
|
|
|
|
|
2009-01-27 16:35:00 +00:00
|
|
|
[={}:;,.()+*/%<>~\[\]?!\|-] {
|
1998-11-27 19:35:50 +00:00
|
|
|
return yytext[0];
|
|
|
|
}
|
|
|
|
|
2019-02-25 16:19:47 +00:00
|
|
|
["] {
|
|
|
|
BEGIN(QUOTED);
|
|
|
|
quoted_buffer_init();
|
|
|
|
}
|
|
|
|
|
|
|
|
<QUOTED>\n cf_error("Unterminated string");
|
|
|
|
<QUOTED><<EOF>> cf_error("Unterminated string");
|
|
|
|
<QUOTED>["] {
|
|
|
|
BEGIN(INITIAL);
|
|
|
|
BUFFER_PUSH(quoted_buffer) = 0;
|
|
|
|
cf_lval.t = cfg_strdup(quoted_buffer_data);
|
1998-11-27 19:35:50 +00:00
|
|
|
return TEXT;
|
|
|
|
}
|
|
|
|
|
2019-02-25 16:19:47 +00:00
|
|
|
<QUOTED>. BUFFER_PUSH(quoted_buffer) = yytext[0];
|
1998-11-27 19:35:50 +00:00
|
|
|
|
2012-07-18 17:29:33 +00:00
|
|
|
<INITIAL,COMMENT><<EOF>> { if (check_eof()) return END; }
|
1998-11-27 19:35:50 +00:00
|
|
|
|
|
|
|
{WHITE}+
|
|
|
|
|
2018-09-11 14:55:41 +00:00
|
|
|
\n ifs->lino++; ifs->chno = 0;
|
1998-11-27 19:35:50 +00:00
|
|
|
|
1999-02-13 21:34:33 +00:00
|
|
|
# BEGIN(COMMENT);
|
1998-11-27 19:35:50 +00:00
|
|
|
|
1999-02-13 21:34:33 +00:00
|
|
|
\/\* BEGIN(CCOMM);
|
1998-11-27 19:35:50 +00:00
|
|
|
|
|
|
|
. cf_error("Unknown character");
|
|
|
|
|
|
|
|
<COMMENT>\n {
|
2012-07-18 17:29:33 +00:00
|
|
|
ifs->lino++;
|
2018-09-11 14:55:41 +00:00
|
|
|
ifs->chno = 0;
|
1998-11-27 19:35:50 +00:00
|
|
|
BEGIN(INITIAL);
|
|
|
|
}
|
|
|
|
|
|
|
|
<COMMENT>.
|
|
|
|
|
|
|
|
<CCOMM>\*\/ BEGIN(INITIAL);
|
2018-09-11 14:55:41 +00:00
|
|
|
<CCOMM>\n ifs->lino++; ifs->chno = 0;
|
1998-11-27 19:35:50 +00:00
|
|
|
<CCOMM>\/\* cf_error("Comment nesting not supported");
|
|
|
|
<CCOMM><<EOF>> cf_error("Unterminated comment");
|
|
|
|
<CCOMM>.
|
|
|
|
|
2000-03-10 20:21:12 +00:00
|
|
|
\!\= return NEQ;
|
2016-09-20 13:13:01 +00:00
|
|
|
\!\~ return NMA;
|
2000-03-10 20:21:12 +00:00
|
|
|
\<\= return LEQ;
|
|
|
|
\>\= return GEQ;
|
2000-06-01 08:43:29 +00:00
|
|
|
\&\& return AND;
|
|
|
|
\|\| return OR;
|
2000-03-10 20:21:12 +00:00
|
|
|
|
2009-03-14 11:43:10 +00:00
|
|
|
\[\= return PO;
|
|
|
|
\=\] return PC;
|
|
|
|
|
1998-11-27 19:35:50 +00:00
|
|
|
%%
|
|
|
|
|
2017-05-25 21:30:39 +00:00
|
|
|
static uint
|
2019-02-25 16:19:47 +00:00
|
|
|
cf_hash(const byte *c)
|
1998-11-27 19:35:50 +00:00
|
|
|
{
|
2017-05-25 21:30:39 +00:00
|
|
|
uint h = 13 << 24;
|
1998-11-27 19:35:50 +00:00
|
|
|
|
|
|
|
while (*c)
|
2017-05-25 21:30:39 +00:00
|
|
|
h = h + (h >> 2) + (h >> 5) + ((uint) *c++ << 24);
|
1998-11-27 19:35:50 +00:00
|
|
|
return h;
|
|
|
|
}
|
|
|
|
|
2012-07-18 17:29:33 +00:00
|
|
|
/*
|
|
|
|
* IFS stack - it contains structures needed for recursive processing
|
|
|
|
* of include in config files. On the top of the stack is a structure
|
|
|
|
* for currently processed file. Other structures are either for
|
|
|
|
* active files interrupted because of include directive (these have
|
|
|
|
* fd and flex buffer) or for inactive files scheduled to be processed
|
|
|
|
* later (when parent requested including of several files by wildcard
|
|
|
|
* match - these do not have fd and flex buffer yet).
|
|
|
|
*
|
|
|
|
* FIXME: Most of these ifs and include functions are really sysdep/unix.
|
|
|
|
*/
|
|
|
|
|
|
|
|
static struct include_file_stack *
|
|
|
|
push_ifs(struct include_file_stack *old)
|
|
|
|
{
|
|
|
|
struct include_file_stack *ret;
|
|
|
|
ret = cfg_allocz(sizeof(struct include_file_stack));
|
|
|
|
ret->lino = 1;
|
|
|
|
ret->prev = old;
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct include_file_stack *
|
|
|
|
pop_ifs(struct include_file_stack *old)
|
|
|
|
{
|
|
|
|
yy_delete_buffer(old->buffer);
|
|
|
|
close(old->fd);
|
|
|
|
return old->prev;
|
|
|
|
}
|
|
|
|
|
2011-09-11 19:21:47 +00:00
|
|
|
static void
|
2012-07-18 17:29:33 +00:00
|
|
|
enter_ifs(struct include_file_stack *new)
|
2011-09-11 19:21:47 +00:00
|
|
|
{
|
2012-07-18 17:29:33 +00:00
|
|
|
if (!new->buffer)
|
|
|
|
{
|
|
|
|
new->fd = open(new->file_name, O_RDONLY);
|
|
|
|
if (new->fd < 0)
|
|
|
|
{
|
|
|
|
ifs = ifs->up;
|
|
|
|
cf_error("Unable to open included file %s: %m", new->file_name);
|
|
|
|
}
|
|
|
|
|
|
|
|
new->buffer = yy_create_buffer(NULL, YY_BUF_SIZE);
|
|
|
|
}
|
2011-09-11 19:21:47 +00:00
|
|
|
|
2012-07-18 17:29:33 +00:00
|
|
|
yy_switch_to_buffer(new->buffer);
|
|
|
|
}
|
2011-09-11 19:21:47 +00:00
|
|
|
|
2014-03-25 13:58:00 +00:00
|
|
|
/**
|
|
|
|
* cf_lex_unwind - unwind lexer state during error
|
|
|
|
*
|
|
|
|
* cf_lex_unwind() frees the internal state on IFS stack when the lexical
|
|
|
|
* analyzer is terminated by cf_error().
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
cf_lex_unwind(void)
|
|
|
|
{
|
|
|
|
struct include_file_stack *n;
|
|
|
|
|
|
|
|
for (n = ifs; n != ifs_head; n = n->prev)
|
|
|
|
{
|
|
|
|
/* Memory is freed automatically */
|
|
|
|
if (n->buffer)
|
|
|
|
yy_delete_buffer(n->buffer);
|
|
|
|
if (n->fd)
|
|
|
|
close(n->fd);
|
|
|
|
}
|
|
|
|
|
|
|
|
ifs = ifs_head;
|
|
|
|
}
|
|
|
|
|
2012-07-18 17:29:33 +00:00
|
|
|
static void
|
|
|
|
cf_include(char *arg, int alen)
|
|
|
|
{
|
|
|
|
struct include_file_stack *base_ifs = ifs;
|
|
|
|
int new_depth, rv, i;
|
|
|
|
char *patt;
|
2014-03-25 13:58:00 +00:00
|
|
|
glob_t g = {};
|
2012-07-18 17:29:33 +00:00
|
|
|
|
|
|
|
new_depth = ifs->depth + 1;
|
|
|
|
if (new_depth > MAX_INCLUDE_DEPTH)
|
|
|
|
cf_error("Max include depth reached");
|
2011-09-11 19:21:47 +00:00
|
|
|
|
2012-07-18 17:29:33 +00:00
|
|
|
/* expand arg to properly handle relative filenames */
|
|
|
|
if (*arg != '/')
|
|
|
|
{
|
|
|
|
int dlen = strlen(ifs->file_name);
|
|
|
|
char *dir = alloca(dlen + 1);
|
|
|
|
patt = alloca(dlen + alen + 2);
|
|
|
|
memcpy(dir, ifs->file_name, dlen + 1);
|
|
|
|
sprintf(patt, "%s/%s", dirname(dir), arg);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
patt = arg;
|
2011-09-11 19:21:47 +00:00
|
|
|
|
2012-07-18 17:29:33 +00:00
|
|
|
/* Skip globbing if there are no wildcards, mainly to get proper
|
|
|
|
response when the included config file is missing */
|
|
|
|
if (!strpbrk(arg, "?*["))
|
|
|
|
{
|
|
|
|
ifs = push_ifs(ifs);
|
|
|
|
ifs->file_name = cfg_strdup(patt);
|
|
|
|
ifs->depth = new_depth;
|
|
|
|
ifs->up = base_ifs;
|
|
|
|
enter_ifs(ifs);
|
|
|
|
return;
|
|
|
|
}
|
2011-09-11 19:21:47 +00:00
|
|
|
|
2012-07-18 17:29:33 +00:00
|
|
|
/* Expand the pattern */
|
|
|
|
rv = glob(patt, GLOB_ERR | GLOB_NOESCAPE, NULL, &g);
|
|
|
|
if (rv == GLOB_ABORTED)
|
|
|
|
cf_error("Unable to match pattern %s: %m", patt);
|
|
|
|
if ((rv != 0) || (g.gl_pathc <= 0))
|
|
|
|
return;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Now we put all found files to ifs stack in reverse order, they
|
|
|
|
* will be activated and processed in order as ifs stack is popped
|
|
|
|
* by pop_ifs() and enter_ifs() in check_eof().
|
|
|
|
*/
|
|
|
|
for(i = g.gl_pathc - 1; i >= 0; i--)
|
|
|
|
{
|
|
|
|
char *fname = g.gl_pathv[i];
|
|
|
|
struct stat fs;
|
|
|
|
|
|
|
|
if (stat(fname, &fs) < 0)
|
2014-03-25 13:58:00 +00:00
|
|
|
{
|
|
|
|
globfree(&g);
|
|
|
|
cf_error("Unable to stat included file %s: %m", fname);
|
|
|
|
}
|
2012-07-18 17:29:33 +00:00
|
|
|
|
|
|
|
if (fs.st_mode & S_IFDIR)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
/* Prepare new stack item */
|
|
|
|
ifs = push_ifs(ifs);
|
|
|
|
ifs->file_name = cfg_strdup(fname);
|
|
|
|
ifs->depth = new_depth;
|
|
|
|
ifs->up = base_ifs;
|
|
|
|
}
|
|
|
|
|
|
|
|
globfree(&g);
|
|
|
|
enter_ifs(ifs);
|
2011-09-11 19:21:47 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
check_eof(void)
|
|
|
|
{
|
2012-07-18 17:29:33 +00:00
|
|
|
if (ifs == ifs_head)
|
|
|
|
{
|
|
|
|
/* EOF in main config file */
|
|
|
|
ifs->lino = 1; /* Why this? */
|
|
|
|
return 1;
|
|
|
|
}
|
2011-09-11 19:21:47 +00:00
|
|
|
|
2012-07-18 17:29:33 +00:00
|
|
|
ifs = pop_ifs(ifs);
|
|
|
|
enter_ifs(ifs);
|
|
|
|
return 0;
|
2011-09-11 19:21:47 +00:00
|
|
|
}
|
|
|
|
|
2023-06-13 08:51:03 +00:00
|
|
|
static inline void cf_swap_soft_scope(struct config *conf);
|
2022-03-06 15:37:30 +00:00
|
|
|
|
Filter: Methods rework
Methods can now be called as x.m(y), as long as x can have its type
inferred in config time. If used as a command, it modifies the object,
if used as a value, it keeps the original object intact.
Also functions add(x,y), delete(x,y), filter(x,y) and prepend(x,y) now
spit a warning and are considered deprecated.
It's also possible to call a method on a constant, see filter/test.conf
for examples like bgp_path = +empty+.prepend(1).
Inside instruction definitions (filter/f-inst.c), a METHOD_CONSTRUCTOR()
call is added, which registers the instruction as a method for the type
of its first argument. Each type has its own method symbol table and
filter parser switches between them based on the inferred type of the
object calling the method.
Also FI_CLIST_(ADD|DELETE|FILTER) instructions have been split to allow
for this method dispatch. With type inference, it's now possible.
2023-06-16 15:35:37 +00:00
|
|
|
struct symbol *
|
2023-06-13 08:51:03 +00:00
|
|
|
cf_new_symbol(struct sym_scope *scope, pool *p, struct linpool *lp, const byte *c)
|
2000-06-04 19:30:13 +00:00
|
|
|
{
|
2023-06-13 09:09:41 +00:00
|
|
|
if (scope->readonly)
|
|
|
|
cf_error("Unknown symbol %s", c);
|
|
|
|
|
2017-05-25 21:30:39 +00:00
|
|
|
struct symbol *s;
|
|
|
|
|
|
|
|
uint l = strlen(c);
|
2000-06-04 19:30:13 +00:00
|
|
|
if (l > SYM_MAX_LEN)
|
|
|
|
cf_error("Symbol too long");
|
2017-05-25 21:30:39 +00:00
|
|
|
|
2023-06-13 08:51:03 +00:00
|
|
|
s = lp_alloc(lp, sizeof(struct symbol) + l + 1);
|
|
|
|
*s = (struct symbol) { .scope = scope, .class = SYM_VOID, };
|
2000-06-04 19:30:13 +00:00
|
|
|
strcpy(s->name, c);
|
1998-11-27 19:35:50 +00:00
|
|
|
|
2023-06-13 08:51:03 +00:00
|
|
|
if (!scope->hash.data)
|
|
|
|
HASH_INIT(scope->hash, p, SYM_ORDER);
|
2015-11-08 23:42:02 +00:00
|
|
|
|
2023-06-13 08:51:03 +00:00
|
|
|
HASH_INSERT2(scope->hash, SYM, p, s);
|
2015-11-08 23:42:02 +00:00
|
|
|
|
2023-06-13 08:51:03 +00:00
|
|
|
if (new_config && (scope == new_config->root_scope))
|
2022-05-02 18:29:03 +00:00
|
|
|
add_tail(&(new_config->symbols), &(s->n));
|
2019-02-26 15:44:24 +00:00
|
|
|
|
2017-05-25 21:30:39 +00:00
|
|
|
return s;
|
1998-11-27 19:35:50 +00:00
|
|
|
}
|
|
|
|
|
2000-06-03 18:23:00 +00:00
|
|
|
/**
|
2022-05-02 18:29:03 +00:00
|
|
|
* cf_find_symbol_scope - find a symbol by name
|
|
|
|
* @scope: config scope
|
2015-11-08 23:42:02 +00:00
|
|
|
* @c: symbol name
|
|
|
|
*
|
2022-05-02 18:29:03 +00:00
|
|
|
* This functions searches the symbol table in the scope @scope for a symbol of
|
|
|
|
* given name. First it examines the current scope, then the underlying one
|
2015-11-08 23:42:02 +00:00
|
|
|
* and so on until it either finds the symbol and returns a pointer to its
|
|
|
|
* &symbol structure or reaches the end of the scope chain and returns %NULL to
|
|
|
|
* signify no match.
|
|
|
|
*/
|
|
|
|
struct symbol *
|
2022-05-02 18:29:03 +00:00
|
|
|
cf_find_symbol_scope(const struct sym_scope *scope, const byte *c)
|
2015-11-08 23:42:02 +00:00
|
|
|
{
|
2017-05-25 21:30:39 +00:00
|
|
|
struct symbol *s;
|
|
|
|
|
2022-05-02 18:29:03 +00:00
|
|
|
/* Find the symbol here or anywhere below */
|
|
|
|
while (scope)
|
|
|
|
if (scope->hash.data && (s = HASH_FIND(scope->hash, SYM, c)))
|
|
|
|
return s;
|
|
|
|
else
|
|
|
|
scope = scope->next;
|
2017-05-25 21:30:39 +00:00
|
|
|
|
|
|
|
return NULL;
|
2015-11-08 23:42:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* cf_get_symbol - get a symbol by name
|
2000-06-03 18:23:00 +00:00
|
|
|
* @c: symbol name
|
|
|
|
*
|
2015-11-08 23:42:02 +00:00
|
|
|
* This functions searches the symbol table of the currently parsed config
|
|
|
|
* (@new_config) for a symbol of given name. It returns either the already
|
|
|
|
* existing symbol or a newly allocated undefined (%SYM_VOID) symbol if no
|
|
|
|
* existing symbol is found.
|
2000-06-03 18:23:00 +00:00
|
|
|
*/
|
1999-05-17 20:06:19 +00:00
|
|
|
struct symbol *
|
2023-06-13 08:51:03 +00:00
|
|
|
cf_get_symbol(struct config *conf, const byte *c)
|
1999-05-17 20:06:19 +00:00
|
|
|
{
|
2023-06-13 08:51:03 +00:00
|
|
|
return cf_find_symbol_scope(conf->current_scope, c) ?: (
|
|
|
|
cf_swap_soft_scope(conf),
|
|
|
|
cf_new_symbol(conf->current_scope, conf->pool, conf->mem, c)
|
|
|
|
);
|
1999-05-17 20:06:19 +00:00
|
|
|
}
|
|
|
|
|
2019-02-15 12:53:17 +00:00
|
|
|
/**
|
|
|
|
* cf_localize_symbol - get the local instance of given symbol
|
|
|
|
* @sym: the symbol to localize
|
|
|
|
*
|
|
|
|
* This functions finds the symbol that is local to current scope
|
|
|
|
* for purposes of cf_define_symbol().
|
|
|
|
*/
|
|
|
|
struct symbol *
|
2023-06-13 08:51:03 +00:00
|
|
|
cf_localize_symbol(struct config *conf, struct symbol *sym)
|
2019-02-15 12:53:17 +00:00
|
|
|
{
|
|
|
|
/* If the symbol type is void, it has been recently allocated just in this scope. */
|
|
|
|
if (!sym->class)
|
|
|
|
return sym;
|
2022-03-06 15:37:30 +00:00
|
|
|
|
2019-02-15 12:53:17 +00:00
|
|
|
/* If the scope is the current, it is already defined in this scope. */
|
2023-06-13 08:51:03 +00:00
|
|
|
if (cf_symbol_is_local(conf, sym))
|
2022-03-06 15:37:30 +00:00
|
|
|
cf_error("Symbol '%s' already defined", sym->name);
|
2019-02-15 12:53:17 +00:00
|
|
|
|
|
|
|
/* Not allocated here yet, doing it now. */
|
2023-06-13 08:51:03 +00:00
|
|
|
cf_swap_soft_scope(conf);
|
|
|
|
return cf_new_symbol(conf->current_scope, conf->pool, conf->mem, sym->name);
|
2019-02-15 12:53:17 +00:00
|
|
|
}
|
|
|
|
|
1998-11-27 21:07:02 +00:00
|
|
|
struct symbol *
|
2023-06-13 08:51:03 +00:00
|
|
|
cf_default_name(struct config *conf, char *template, int *counter)
|
1998-11-27 21:07:02 +00:00
|
|
|
{
|
2015-11-08 23:42:02 +00:00
|
|
|
char buf[SYM_MAX_LEN];
|
1998-11-27 21:07:02 +00:00
|
|
|
struct symbol *s;
|
2000-01-17 11:52:50 +00:00
|
|
|
char *perc = strchr(template, '%');
|
1998-11-27 21:07:02 +00:00
|
|
|
|
2000-01-17 11:52:50 +00:00
|
|
|
for(;;)
|
1998-11-27 21:07:02 +00:00
|
|
|
{
|
2000-01-17 11:52:50 +00:00
|
|
|
bsprintf(buf, template, ++(*counter));
|
2023-06-13 08:51:03 +00:00
|
|
|
s = cf_get_symbol(conf, buf);
|
2000-01-17 11:52:50 +00:00
|
|
|
if (s->class == SYM_VOID)
|
|
|
|
return s;
|
|
|
|
if (!perc)
|
|
|
|
break;
|
1998-11-27 21:07:02 +00:00
|
|
|
}
|
2000-01-17 11:52:50 +00:00
|
|
|
cf_error("Unable to generate default name");
|
1998-11-27 21:07:02 +00:00
|
|
|
}
|
|
|
|
|
2019-02-25 16:19:47 +00:00
|
|
|
static enum yytokentype
|
|
|
|
cf_lex_symbol(const char *data)
|
|
|
|
{
|
2019-05-17 20:18:49 +00:00
|
|
|
/* Have we defined such a symbol? */
|
2023-06-13 08:51:03 +00:00
|
|
|
struct symbol *sym = cf_get_symbol(new_config, data);
|
2022-05-02 18:29:03 +00:00
|
|
|
cf_lval.s = sym;
|
2019-05-17 20:18:49 +00:00
|
|
|
|
2023-06-09 10:49:19 +00:00
|
|
|
switch (sym->class)
|
2019-02-25 16:19:47 +00:00
|
|
|
{
|
2023-06-09 10:49:19 +00:00
|
|
|
case SYM_KEYWORD:
|
2019-02-25 16:19:47 +00:00
|
|
|
{
|
2023-06-09 10:49:19 +00:00
|
|
|
int val = sym->keyword->value;
|
|
|
|
if (val > 0) return val;
|
|
|
|
cf_lval.i = -val;
|
2019-02-25 16:19:47 +00:00
|
|
|
return ENUM;
|
|
|
|
}
|
Filter: Methods rework
Methods can now be called as x.m(y), as long as x can have its type
inferred in config time. If used as a command, it modifies the object,
if used as a value, it keeps the original object intact.
Also functions add(x,y), delete(x,y), filter(x,y) and prepend(x,y) now
spit a warning and are considered deprecated.
It's also possible to call a method on a constant, see filter/test.conf
for examples like bgp_path = +empty+.prepend(1).
Inside instruction definitions (filter/f-inst.c), a METHOD_CONSTRUCTOR()
call is added, which registers the instruction as a method for the type
of its first argument. Each type has its own method symbol table and
filter parser switches between them based on the inferred type of the
object calling the method.
Also FI_CLIST_(ADD|DELETE|FILTER) instructions have been split to allow
for this method dispatch. With type inference, it's now possible.
2023-06-16 15:35:37 +00:00
|
|
|
case SYM_METHOD:
|
|
|
|
return sym->method->arg_num ? CF_SYM_METHOD_ARGS : CF_SYM_METHOD_BARE;
|
2023-06-09 10:49:19 +00:00
|
|
|
case SYM_VOID:
|
|
|
|
return CF_SYM_UNDEFINED;
|
|
|
|
default:
|
|
|
|
return CF_SYM_KNOWN;
|
2019-02-25 16:19:47 +00:00
|
|
|
}
|
1999-11-30 14:03:36 +00:00
|
|
|
}
|
|
|
|
|
Filter: Methods rework
Methods can now be called as x.m(y), as long as x can have its type
inferred in config time. If used as a command, it modifies the object,
if used as a value, it keeps the original object intact.
Also functions add(x,y), delete(x,y), filter(x,y) and prepend(x,y) now
spit a warning and are considered deprecated.
It's also possible to call a method on a constant, see filter/test.conf
for examples like bgp_path = +empty+.prepend(1).
Inside instruction definitions (filter/f-inst.c), a METHOD_CONSTRUCTOR()
call is added, which registers the instruction as a method for the type
of its first argument. Each type has its own method symbol table and
filter parser switches between them based on the inferred type of the
object calling the method.
Also FI_CLIST_(ADD|DELETE|FILTER) instructions have been split to allow
for this method dispatch. With type inference, it's now possible.
2023-06-16 15:35:37 +00:00
|
|
|
void f_type_methods_register(void);
|
|
|
|
|
2000-06-03 18:23:00 +00:00
|
|
|
/**
|
|
|
|
* cf_lex_init - initialize the lexer
|
|
|
|
* @is_cli: true if we're going to parse CLI command, false for configuration
|
2016-05-12 13:49:44 +00:00
|
|
|
* @c: configuration structure
|
2000-06-03 18:23:00 +00:00
|
|
|
*
|
2000-06-07 12:29:08 +00:00
|
|
|
* cf_lex_init() initializes the lexical analyzer and prepares it for
|
2000-06-03 18:23:00 +00:00
|
|
|
* parsing of a new input.
|
|
|
|
*/
|
1998-11-27 19:35:50 +00:00
|
|
|
void
|
2011-09-11 19:21:47 +00:00
|
|
|
cf_lex_init(int is_cli, struct config *c)
|
1998-11-27 19:35:50 +00:00
|
|
|
{
|
2023-06-09 10:49:19 +00:00
|
|
|
if (!global_root_scope_pool)
|
|
|
|
{
|
|
|
|
global_root_scope_pool = rp_new(&root_pool, "Keywords pool");
|
Filter: Methods rework
Methods can now be called as x.m(y), as long as x can have its type
inferred in config time. If used as a command, it modifies the object,
if used as a value, it keeps the original object intact.
Also functions add(x,y), delete(x,y), filter(x,y) and prepend(x,y) now
spit a warning and are considered deprecated.
It's also possible to call a method on a constant, see filter/test.conf
for examples like bgp_path = +empty+.prepend(1).
Inside instruction definitions (filter/f-inst.c), a METHOD_CONSTRUCTOR()
call is added, which registers the instruction as a method for the type
of its first argument. Each type has its own method symbol table and
filter parser switches between them based on the inferred type of the
object calling the method.
Also FI_CLIST_(ADD|DELETE|FILTER) instructions have been split to allow
for this method dispatch. With type inference, it's now possible.
2023-06-16 15:35:37 +00:00
|
|
|
global_root_scope_linpool = lp_new(global_root_scope_pool);
|
|
|
|
global_root_scope = lp_allocz(global_root_scope_linpool, sizeof(*global_root_scope));
|
2023-06-09 10:49:19 +00:00
|
|
|
|
|
|
|
for (const struct keyword *k = keyword_list; k->name; k++)
|
2023-06-13 08:51:03 +00:00
|
|
|
{
|
Filter: Methods rework
Methods can now be called as x.m(y), as long as x can have its type
inferred in config time. If used as a command, it modifies the object,
if used as a value, it keeps the original object intact.
Also functions add(x,y), delete(x,y), filter(x,y) and prepend(x,y) now
spit a warning and are considered deprecated.
It's also possible to call a method on a constant, see filter/test.conf
for examples like bgp_path = +empty+.prepend(1).
Inside instruction definitions (filter/f-inst.c), a METHOD_CONSTRUCTOR()
call is added, which registers the instruction as a method for the type
of its first argument. Each type has its own method symbol table and
filter parser switches between them based on the inferred type of the
object calling the method.
Also FI_CLIST_(ADD|DELETE|FILTER) instructions have been split to allow
for this method dispatch. With type inference, it's now possible.
2023-06-16 15:35:37 +00:00
|
|
|
struct symbol *sym = cf_new_symbol(global_root_scope, global_root_scope_pool, global_root_scope_linpool, k->name);
|
2023-06-13 08:51:03 +00:00
|
|
|
sym->class = SYM_KEYWORD;
|
|
|
|
sym->keyword = k;
|
|
|
|
}
|
2023-06-13 09:09:41 +00:00
|
|
|
|
Filter: Methods rework
Methods can now be called as x.m(y), as long as x can have its type
inferred in config time. If used as a command, it modifies the object,
if used as a value, it keeps the original object intact.
Also functions add(x,y), delete(x,y), filter(x,y) and prepend(x,y) now
spit a warning and are considered deprecated.
It's also possible to call a method on a constant, see filter/test.conf
for examples like bgp_path = +empty+.prepend(1).
Inside instruction definitions (filter/f-inst.c), a METHOD_CONSTRUCTOR()
call is added, which registers the instruction as a method for the type
of its first argument. Each type has its own method symbol table and
filter parser switches between them based on the inferred type of the
object calling the method.
Also FI_CLIST_(ADD|DELETE|FILTER) instructions have been split to allow
for this method dispatch. With type inference, it's now possible.
2023-06-16 15:35:37 +00:00
|
|
|
global_root_scope->readonly = 1;
|
|
|
|
|
|
|
|
f_type_methods_register();
|
2023-06-09 10:49:19 +00:00
|
|
|
}
|
2012-07-18 17:29:33 +00:00
|
|
|
|
|
|
|
ifs_head = ifs = push_ifs(NULL);
|
2016-11-08 16:46:29 +00:00
|
|
|
if (!is_cli)
|
2012-07-18 17:29:33 +00:00
|
|
|
{
|
|
|
|
ifs->file_name = c->file_name;
|
|
|
|
ifs->fd = c->file_fd;
|
|
|
|
ifs->depth = 1;
|
|
|
|
}
|
|
|
|
|
Parse CLI commands. We use the same parser as for configuration files (because
we want to allow filter and similar complex constructs to be used in commands
and we should avoid code duplication), only with CLI_MARKER token prepended
before the whole input.
Defined macro CF_CLI(cmd, args, help) for defining CLI commands in .Y files.
The first argument specifies the command itself, the remaining two arguments
are copied to the help file (er, will be copied after the help file starts
to exist). This macro automatically creates a skeleton rule for the command,
you only need to append arguments as in:
CF_CLI(STEAL MONEY, <$>, [[Steal <$> US dollars or equivalent in any other currency]]): NUM {
cli_msg(0, "%d$ stolen", $3);
} ;
Also don't forget to reset lexer state between inputs.
1999-10-31 17:47:47 +00:00
|
|
|
yyrestart(NULL);
|
2012-07-18 17:29:33 +00:00
|
|
|
ifs->buffer = YY_CURRENT_BUFFER;
|
|
|
|
|
Parse CLI commands. We use the same parser as for configuration files (because
we want to allow filter and similar complex constructs to be used in commands
and we should avoid code duplication), only with CLI_MARKER token prepended
before the whole input.
Defined macro CF_CLI(cmd, args, help) for defining CLI commands in .Y files.
The first argument specifies the command itself, the remaining two arguments
are copied to the help file (er, will be copied after the help file starts
to exist). This macro automatically creates a skeleton rule for the command,
you only need to append arguments as in:
CF_CLI(STEAL MONEY, <$>, [[Steal <$> US dollars or equivalent in any other currency]]): NUM {
cli_msg(0, "%d$ stolen", $3);
} ;
Also don't forget to reset lexer state between inputs.
1999-10-31 17:47:47 +00:00
|
|
|
if (is_cli)
|
|
|
|
BEGIN(CLI);
|
|
|
|
else
|
|
|
|
BEGIN(INITIAL);
|
2012-07-18 17:29:33 +00:00
|
|
|
|
2023-06-13 08:51:03 +00:00
|
|
|
c->root_scope = c->current_scope = cfg_allocz(sizeof(struct sym_scope));
|
2022-05-02 18:29:03 +00:00
|
|
|
|
|
|
|
if (is_cli)
|
2023-06-13 08:51:03 +00:00
|
|
|
c->current_scope->next = config->root_scope;
|
2022-05-02 18:29:03 +00:00
|
|
|
else
|
Filter: Methods rework
Methods can now be called as x.m(y), as long as x can have its type
inferred in config time. If used as a command, it modifies the object,
if used as a value, it keeps the original object intact.
Also functions add(x,y), delete(x,y), filter(x,y) and prepend(x,y) now
spit a warning and are considered deprecated.
It's also possible to call a method on a constant, see filter/test.conf
for examples like bgp_path = +empty+.prepend(1).
Inside instruction definitions (filter/f-inst.c), a METHOD_CONSTRUCTOR()
call is added, which registers the instruction as a method for the type
of its first argument. Each type has its own method symbol table and
filter parser switches between them based on the inferred type of the
object calling the method.
Also FI_CLIST_(ADD|DELETE|FILTER) instructions have been split to allow
for this method dispatch. With type inference, it's now possible.
2023-06-16 15:35:37 +00:00
|
|
|
c->current_scope->next = global_root_scope;
|
1998-11-27 19:35:50 +00:00
|
|
|
}
|
|
|
|
|
2000-06-03 18:23:00 +00:00
|
|
|
/**
|
|
|
|
* cf_push_scope - enter new scope
|
|
|
|
* @sym: symbol representing scope name
|
|
|
|
*
|
|
|
|
* If we want to enter a new scope to process declarations inside
|
|
|
|
* a nested block, we can just call cf_push_scope() to push a new
|
|
|
|
* scope onto the scope stack which will cause all new symbols to be
|
|
|
|
* defined in this scope and all existing symbols to be sought for
|
|
|
|
* in all scopes stored on the stack.
|
|
|
|
*/
|
1999-11-04 13:51:52 +00:00
|
|
|
void
|
2023-06-13 08:51:03 +00:00
|
|
|
cf_push_scope(struct config *conf, struct symbol *sym)
|
1999-11-04 13:51:52 +00:00
|
|
|
{
|
2020-11-24 03:09:11 +00:00
|
|
|
struct sym_scope *s = cfg_allocz(sizeof(struct sym_scope));
|
1999-11-04 13:51:52 +00:00
|
|
|
|
2023-06-13 08:51:03 +00:00
|
|
|
s->next = conf->current_scope;
|
|
|
|
conf->current_scope = s;
|
1999-11-04 13:51:52 +00:00
|
|
|
s->active = 1;
|
|
|
|
s->name = sym;
|
2019-06-25 14:18:06 +00:00
|
|
|
s->slots = 0;
|
1999-11-04 13:51:52 +00:00
|
|
|
}
|
|
|
|
|
2000-06-03 18:23:00 +00:00
|
|
|
/**
|
|
|
|
* cf_pop_scope - leave a scope
|
|
|
|
*
|
|
|
|
* cf_pop_scope() pops the topmost scope from the scope stack,
|
|
|
|
* leaving all its symbols in the symbol table, but making them
|
|
|
|
* invisible to the rest of the config.
|
|
|
|
*/
|
1999-11-04 13:51:52 +00:00
|
|
|
void
|
2023-06-13 08:51:03 +00:00
|
|
|
cf_pop_scope(struct config *conf)
|
1999-11-04 13:51:52 +00:00
|
|
|
{
|
2023-06-13 08:51:03 +00:00
|
|
|
ASSERT(!conf->current_scope->soft_scopes);
|
2022-03-06 15:37:30 +00:00
|
|
|
|
2023-06-13 08:51:03 +00:00
|
|
|
conf->current_scope->active = 0;
|
|
|
|
conf->current_scope = conf->current_scope->next;
|
2019-07-02 08:45:53 +00:00
|
|
|
|
2023-06-13 08:51:03 +00:00
|
|
|
ASSERT(conf->current_scope);
|
1999-11-04 13:51:52 +00:00
|
|
|
}
|
2000-01-19 12:30:19 +00:00
|
|
|
|
2022-03-06 15:37:30 +00:00
|
|
|
/**
|
|
|
|
* cf_push_soft_scope - enter new soft scope
|
|
|
|
*
|
|
|
|
* If we want to enter a new anonymous scope that most likely will not contain
|
|
|
|
* any symbols, we can use cf_push_soft_scope() insteas of cf_push_scope().
|
|
|
|
* Such scope will be converted to a regular scope on first use.
|
|
|
|
*/
|
|
|
|
void
|
2023-06-13 08:51:03 +00:00
|
|
|
cf_push_soft_scope(struct config *conf)
|
2022-03-06 15:37:30 +00:00
|
|
|
{
|
2023-06-13 08:51:03 +00:00
|
|
|
if (conf->current_scope->soft_scopes < 0xfe)
|
|
|
|
conf->current_scope->soft_scopes++;
|
2022-03-06 15:37:30 +00:00
|
|
|
else
|
2023-06-13 08:51:03 +00:00
|
|
|
cf_push_block_scope(conf);
|
2022-03-06 15:37:30 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* cf_pop_soft_scope - leave a soft scope
|
|
|
|
*
|
|
|
|
* Leave a soft scope entered by cf_push_soft_scope().
|
|
|
|
*/
|
|
|
|
void
|
2023-06-13 08:51:03 +00:00
|
|
|
cf_pop_soft_scope(struct config *conf)
|
2022-03-06 15:37:30 +00:00
|
|
|
{
|
2023-06-13 08:51:03 +00:00
|
|
|
if (conf->current_scope->soft_scopes)
|
|
|
|
conf->current_scope->soft_scopes--;
|
2022-03-06 15:37:30 +00:00
|
|
|
else
|
2023-06-13 08:51:03 +00:00
|
|
|
cf_pop_block_scope(conf);
|
2022-03-06 15:37:30 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* cf_swap_soft_scope - convert soft scope to regular scope
|
|
|
|
*
|
|
|
|
* Soft scopes cannot hold symbols, so they must be converted to regular scopes
|
|
|
|
* on first use. It is done automatically by cf_new_symbol().
|
|
|
|
*/
|
|
|
|
static inline void
|
2023-06-13 08:51:03 +00:00
|
|
|
cf_swap_soft_scope(struct config *conf)
|
2022-03-06 15:37:30 +00:00
|
|
|
{
|
2023-06-13 08:51:03 +00:00
|
|
|
if (conf->current_scope->soft_scopes)
|
2022-03-06 15:37:30 +00:00
|
|
|
{
|
2023-06-13 08:51:03 +00:00
|
|
|
conf->current_scope->soft_scopes--;
|
|
|
|
cf_push_block_scope(conf);
|
2022-03-06 15:37:30 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2000-06-03 18:23:00 +00:00
|
|
|
/**
|
|
|
|
* cf_symbol_class_name - get name of a symbol class
|
|
|
|
* @sym: symbol
|
|
|
|
*
|
|
|
|
* This function returns a string representing the class
|
|
|
|
* of the given symbol.
|
|
|
|
*/
|
2000-01-19 12:30:19 +00:00
|
|
|
char *
|
|
|
|
cf_symbol_class_name(struct symbol *sym)
|
|
|
|
{
|
|
|
|
switch (sym->class)
|
|
|
|
{
|
|
|
|
case SYM_VOID:
|
|
|
|
return "undefined";
|
|
|
|
case SYM_PROTO:
|
|
|
|
return "protocol";
|
2013-07-25 20:33:57 +00:00
|
|
|
case SYM_TEMPLATE:
|
|
|
|
return "protocol template";
|
2000-01-19 12:30:19 +00:00
|
|
|
case SYM_FUNCTION:
|
|
|
|
return "function";
|
|
|
|
case SYM_FILTER:
|
|
|
|
return "filter";
|
|
|
|
case SYM_TABLE:
|
|
|
|
return "routing table";
|
2019-01-30 13:03:47 +00:00
|
|
|
case SYM_ATTRIBUTE:
|
|
|
|
return "custom attribute";
|
|
|
|
case SYM_CONSTANT_RANGE:
|
|
|
|
return "constant";
|
|
|
|
case SYM_VARIABLE_RANGE:
|
|
|
|
return "variable";
|
2000-01-19 12:30:19 +00:00
|
|
|
default:
|
|
|
|
return "unknown type";
|
|
|
|
}
|
|
|
|
}
|
2000-06-04 15:22:20 +00:00
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* DOC: Parser
|
|
|
|
*
|
2000-06-07 12:29:08 +00:00
|
|
|
* Both the configuration and CLI commands are analyzed using a syntax
|
2000-06-04 15:22:20 +00:00
|
|
|
* driven parser generated by the |bison| tool from a grammar which
|
|
|
|
* is constructed from information gathered from grammar snippets by
|
|
|
|
* the |gen_parser.m4| script.
|
|
|
|
*
|
|
|
|
* Grammar snippets are files (usually with extension |.Y|) contributed
|
2000-06-07 13:25:53 +00:00
|
|
|
* by various BIRD modules in order to provide information about syntax of their
|
2000-06-04 15:22:20 +00:00
|
|
|
* configuration and their CLI commands. Each snipped consists of several
|
2000-06-08 12:37:21 +00:00
|
|
|
* sections, each of them starting with a special keyword: |CF_HDR| for
|
2000-06-04 15:22:20 +00:00
|
|
|
* a list of |#include| directives needed by the C code, |CF_DEFINES|
|
|
|
|
* for a list of C declarations, |CF_DECLS| for |bison| declarations
|
|
|
|
* including keyword definitions specified as |CF_KEYWORDS|, |CF_GRAMMAR|
|
2000-06-07 12:29:08 +00:00
|
|
|
* for the grammar rules, |CF_CODE| for auxiliary C code and finally
|
2000-06-04 15:22:20 +00:00
|
|
|
* |CF_END| at the end of the snippet.
|
|
|
|
*
|
|
|
|
* To create references between the snippets, it's possible to define
|
|
|
|
* multi-part rules by utilizing the |CF_ADDTO| macro which adds a new
|
|
|
|
* alternative to a multi-part rule.
|
|
|
|
*
|
|
|
|
* CLI commands are defined using a |CF_CLI| macro. Its parameters are:
|
2000-06-07 12:29:08 +00:00
|
|
|
* the list of keywords determining the command, the list of parameters,
|
2000-06-04 15:22:20 +00:00
|
|
|
* help text for the parameters and help text for the command.
|
|
|
|
*
|
|
|
|
* Values of |enum| filter types can be defined using |CF_ENUM| with
|
|
|
|
* the following parameters: name of filter type, prefix common for all
|
2000-06-08 12:37:21 +00:00
|
|
|
* literals of this type and names of all the possible values.
|
2000-06-04 15:22:20 +00:00
|
|
|
*/
|