#include <errno.h>
#include <stdlib.h>
#include <stdarg.h>
+#include <stdint.h>
#include <unistd.h>
#include <libgen.h>
#include <glob.h>
#include "nest/route.h"
#include "nest/protocol.h"
#include "filter/filter.h"
+#include "filter/f-inst.h"
#include "conf/conf.h"
#include "conf/cf-parse.tab.h"
#include "lib/string.h"
+#include "lib/hash.h"
struct keyword {
byte *name;
#include "conf/keywords.h"
-#define KW_HASH_SIZE 64
-static struct keyword *kw_hash[KW_HASH_SIZE];
-static int kw_hash_inited;
+/* Could be defined by Bison in cf-parse.tab.h, inteferes with SYM hash */
+#ifdef SYM
+#undef SYM
+#endif
-#define SYM_HASH_SIZE 128
-struct sym_scope {
- struct sym_scope *next; /* Next on scope stack */
- struct symbol *name; /* Name of this scope */
- int active; /* Currently entered */
-};
-static struct sym_scope *conf_this_scope;
+static uint cf_hash(const byte *c);
+
+#define KW_KEY(n) n->name
+#define KW_NEXT(n) n->next
+#define KW_EQ(a,b) !strcmp(a,b)
+#define KW_FN(k) cf_hash(k)
+#define KW_ORDER 8 /* Fixed */
+
+#define SYM_KEY(n) n->name, n->scope->active
+#define SYM_NEXT(n) n->next
+#define SYM_EQ(a,s1,b,s2) !strcmp(a,b) && s1 == s2
+#define SYM_FN(k,s) cf_hash(k)
+#define SYM_ORDER 6 /* Initial */
+
+#define SYM_REHASH sym_rehash
+#define SYM_PARAMS /8, *1, 2, 2, 6, 20
+
+
+HASH_DEFINE_REHASH_FN(SYM, struct symbol)
+
+HASH(struct keyword) kw_hash;
+
-static int cf_hash(byte *c);
-static inline struct symbol * cf_get_sym(byte *c, uint h0);
+struct sym_scope *conf_this_scope;
linpool *cfg_mem;
struct include_file_stack *ifs;
static struct include_file_stack *ifs_head;
+#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;
+}
+
#define MAX_INCLUDE_DEPTH 8
#define YY_INPUT(buf,result,max) result = cf_read_hook(buf, max, ifs->fd);
#define YY_NO_UNPUT
#define YY_FATAL_ERROR(msg) cf_error(msg)
+#define YY_USER_ACTION ifs->chno += yyleng; ifs->toklen = yyleng;
static void cf_include(char *arg, int alen);
static int check_eof(void);
+static enum yytokentype cf_lex_symbol(const char *data);
+
%}
%option noyywrap
%option nounput
%option noreject
-%x COMMENT CCOMM CLI
+%x COMMENT CCOMM CLI QUOTED APOSTROPHED INCLUDE
ALPHA [a-zA-Z_]
DIGIT [0-9]
XIGIT [0-9a-fA-F]
ALNUM [a-zA-Z_0-9]
WHITE [ \t]
-include ^{WHITE}*include{WHITE}*\".*\"{WHITE}*;
%%
-{include} {
- char *start, *end;
-
+^{WHITE}*include{WHITE}*\" {
if (!ifs->depth)
cf_error("Include not allowed in CLI");
- start = strchr(yytext, '"');
- start++;
+ BEGIN(INCLUDE);
+}
+
+<INCLUDE>[^"\n]+["]{WHITE}*; {
+ char *start, *end;
+
+ start = yytext;
end = strchr(start, '"');
*end = 0;
cf_error("Include with empty argument");
cf_include(start, end-start);
+
+ BEGIN(INITIAL);
+}
+
+<INCLUDE>["] cf_error("Include with empty argument");
+<INCLUDE>. cf_error("Unterminated include");
+<INCLUDE>\n cf_error("Unterminated include");
+<INCLUDE><<EOF>> cf_error("Unterminated include");
+
+
+{DIGIT}+:{DIGIT}+ {
+ uint len1 UNUSED, len2;
+ u64 l;
+ char *e;
+
+ errno = 0;
+ l = bstrtoul10(yytext, &e);
+ if (e && (*e != ':') || (errno == ERANGE) || (l >> 32))
+ 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;
+ l = bstrtoul10(e+1, &e);
+ if (e && *e || (errno == ERANGE) || (l >> len2))
+ cf_error("Number out of range");
+ cf_lval.i64 |= l;
+
+ return VPN_RD;
+}
+
+[02]:{DIGIT}+:{DIGIT}+ {
+ uint len1, len2;
+ u64 l;
+ char *e;
+
+ if (yytext[0] == '0')
+ {
+ cf_lval.i64 = 0;
+ len1 = 16;
+ len2 = 32;
+ }
+ else
+ {
+ cf_lval.i64 = 2ULL << 48;
+ len1 = 32;
+ len2 = 16;
+ }
+
+ errno = 0;
+ l = bstrtoul10(yytext+2, &e);
+ if (e && (*e != ':') || (errno == ERANGE) || (l >> len1))
+ cf_error("ASN out of range");
+ cf_lval.i64 |= ((u64) l) << len2;
+
+ errno = 0;
+ l = bstrtoul10(e+1, &e);
+ if (e && *e || (errno == ERANGE) || (l >> len2))
+ cf_error("Number out of range");
+ cf_lval.i64 |= l;
+
+ return VPN_RD;
+}
+
+{DIGIT}+\.{DIGIT}+\.{DIGIT}+\.{DIGIT}+:{DIGIT}+ {
+ unsigned long int l;
+ ip4_addr ip4;
+ char *e;
+
+ cf_lval.i64 = 1ULL << 48;
+
+ e = strchr(yytext, ':');
+ *e++ = '\0';
+ if (!ip4_pton(yytext, &ip4))
+ cf_error("Invalid IPv4 address %s in Route Distinguisher", yytext);
+ cf_lval.i64 |= ((u64) ip4_to_u32(ip4)) << 16;
+
+ errno = 0;
+ l = bstrtoul10(e, &e);
+ if (e && *e || (errno == ERANGE) || (l >> 16))
+ cf_error("Number out of range");
+ cf_lval.i64 |= l;
+
+ return VPN_RD;
}
{DIGIT}+\.{DIGIT}+\.{DIGIT}+\.{DIGIT}+ {
char *e;
unsigned long int l;
errno = 0;
- l = strtoul(yytext+2, &e, 16);
+ l = bstrtoul16(yytext+2, &e);
if (e && *e || errno == ERANGE || (unsigned long int)(unsigned int) l != l)
cf_error("Number out of range");
cf_lval.i = l;
char *e;
unsigned long int l;
errno = 0;
- l = strtoul(yytext, &e, 10);
+ l = bstrtoul10(yytext, &e);
if (e && *e || errno == ERANGE || (unsigned long int)(unsigned int) l != l)
cf_error("Number out of range");
cf_lval.i = l;
return ELSECOL;
}
-({ALPHA}{ALNUM}*|[']({ALNUM}|[-]|[\.]|[:])*[']) {
- if(*yytext == '\'') {
- yytext[yyleng-1] = 0;
- yytext++;
- }
- unsigned int h = cf_hash(yytext);
- struct keyword *k = kw_hash[h & (KW_HASH_SIZE-1)];
- while (k)
- {
- if (!strcmp(k->name, yytext))
- {
- if (k->value > 0)
- return k->value;
- else
- {
- cf_lval.i = -k->value;
- return ENUM;
- }
- }
- k=k->next;
- }
- cf_lval.s = cf_get_sym(yytext, h);
- return SYM;
+['] {
+ BEGIN(APOSTROPHED);
+ quoted_buffer_init();
+}
+
+<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");
+
+({ALPHA}{ALNUM}*) {
+ return cf_lex_symbol(yytext);
}
<CLI>(.|\n) {
return yytext[0];
}
-["][^"\n]*["] {
- yytext[yyleng-1] = 0;
- cf_lval.t = cfg_strdup(yytext+1);
+["] {
+ 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);
return TEXT;
}
-["][^"\n]*\n cf_error("Unterminated string");
+<QUOTED>. BUFFER_PUSH(quoted_buffer) = yytext[0];
<INITIAL,COMMENT><<EOF>> { if (check_eof()) return END; }
{WHITE}+
-\n ifs->lino++;
+\n ifs->lino++; ifs->chno = 0;
# BEGIN(COMMENT);
<COMMENT>\n {
ifs->lino++;
+ ifs->chno = 0;
BEGIN(INITIAL);
}
<COMMENT>.
<CCOMM>\*\/ BEGIN(INITIAL);
-<CCOMM>\n ifs->lino++;
+<CCOMM>\n ifs->lino++; ifs->chno = 0;
<CCOMM>\/\* cf_error("Comment nesting not supported");
<CCOMM><<EOF>> cf_error("Unterminated comment");
<CCOMM>.
\!\= return NEQ;
+\!\~ return NMA;
\<\= return LEQ;
\>\= return GEQ;
\&\& return AND;
%%
-static int
-cf_hash(byte *c)
+static uint
+cf_hash(const byte *c)
{
- unsigned int h = 13;
+ uint h = 13 << 24;
while (*c)
- h = (h * 37) + *c++;
+ h = h + (h >> 2) + (h >> 5) + ((uint) *c++ << 24);
return h;
}
-
/*
* IFS stack - it contains structures needed for recursive processing
* of include in config files. On the top of the stack is a structure
}
static struct symbol *
-cf_new_sym(byte *c, uint h0)
+cf_new_symbol(const byte *c)
{
- uint h = h0 & (SYM_HASH_SIZE-1);
- struct symbol *s, **ht;
- int l;
-
- if (!new_config->sym_hash)
- new_config->sym_hash = cfg_allocz(SYM_HASH_SIZE * sizeof(struct keyword *));
- ht = new_config->sym_hash;
- l = strlen(c);
+ struct symbol *s;
+
+ uint l = strlen(c);
if (l > SYM_MAX_LEN)
cf_error("Symbol too long");
- s = cfg_alloc(sizeof(struct symbol) + l);
- s->next = ht[h];
- ht[h] = s;
- s->scope = conf_this_scope;
- s->class = SYM_VOID;
- s->def = NULL;
- s->aux = 0;
+
+ s = cfg_allocz(sizeof(struct symbol) + l + 1);
+ *s = (struct symbol) { .scope = conf_this_scope, .class = SYM_VOID, };
strcpy(s->name, c);
- return s;
-}
-static struct symbol *
-cf_find_sym(struct config *cfg, byte *c, uint h0)
-{
- uint h = h0 & (SYM_HASH_SIZE-1);
- struct symbol *s, **ht;
+ if (!new_config->sym_hash.data)
+ HASH_INIT(new_config->sym_hash, new_config->pool, SYM_ORDER);
- if (ht = cfg->sym_hash)
- {
- for(s = ht[h]; s; s=s->next)
- if (!strcmp(s->name, c) && s->scope->active)
- return s;
- }
- if (ht = cfg->sym_fallback)
- {
- /* We know only top-level scope is active */
- for(s = ht[h]; s; s=s->next)
- if (!strcmp(s->name, c) && s->scope->active)
- return s;
- }
+ HASH_INSERT2(new_config->sym_hash, SYM, new_config->pool, s);
- return NULL;
-}
+ add_tail(&(new_config->symbols), &(s->n));
-static inline struct symbol *
-cf_get_sym(byte *c, uint h0)
-{
- return cf_find_sym(new_config, c, h0) ?: cf_new_sym(c, h0);
+ return s;
}
/**
* signify no match.
*/
struct symbol *
-cf_find_symbol(struct config *cfg, byte *c)
+cf_find_symbol(const struct config *cfg, const byte *c)
{
- return cf_find_sym(cfg, c, cf_hash(c));
+ struct symbol *s;
+
+ if (cfg->sym_hash.data &&
+ (s = HASH_FIND(cfg->sym_hash, SYM, c, 1)))
+ return s;
+
+ /* In CLI command parsing, fallback points to the current config, otherwise it is NULL. */
+ if (cfg->fallback &&
+ cfg->fallback->sym_hash.data &&
+ (s = HASH_FIND(cfg->fallback->sym_hash, SYM, c, 1)))
+ return s;
+
+ return NULL;
}
/**
* existing symbol is found.
*/
struct symbol *
-cf_get_symbol(byte *c)
+cf_get_symbol(const byte *c)
{
- return cf_get_sym(c, cf_hash(c));
+ return cf_find_symbol(new_config, c) ?: cf_new_symbol(c);
+}
+
+/**
+ * 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 *
+cf_localize_symbol(struct symbol *sym)
+{
+ /* If the symbol type is void, it has been recently allocated just in this scope. */
+ if (!sym->class)
+ return sym;
+
+ /* If the scope is the current, it is already defined in this scope. */
+ if (sym->scope == conf_this_scope)
+ cf_error("Symbol already defined");
+
+ /* Not allocated here yet, doing it now. */
+ return cf_new_symbol(sym->name);
}
struct symbol *
for(;;)
{
bsprintf(buf, template, ++(*counter));
- s = cf_get_sym(buf, cf_hash(buf));
+ s = cf_get_symbol(buf);
if (s->class == SYM_VOID)
return s;
if (!perc)
cf_error("Unable to generate default name");
}
-/**
- * cf_define_symbol - define meaning of a symbol
- * @sym: symbol to be defined
- * @type: symbol class to assign
- * @def: class dependent data
- *
- * Defines new meaning of a symbol. If the symbol is an undefined
- * one (%SYM_VOID), it's just re-defined to the new type. If it's defined
- * in different scope, a new symbol in current scope is created and the
- * meaning is assigned to it. If it's already defined in the current scope,
- * an error is reported via cf_error().
- *
- * Result: Pointer to the newly defined symbol. If we are in the top-level
- * scope, it's the same @sym as passed to the function.
- */
-struct symbol *
-cf_define_symbol(struct symbol *sym, int type, void *def)
+static enum yytokentype
+cf_lex_symbol(const char *data)
{
- if (sym->class)
+ /* Have we defined such a symbol? */
+ struct symbol *sym = cf_get_symbol(data);
+ cf_lval.s = sym;
+
+ if (sym->class != SYM_VOID)
+ return CF_SYM_KNOWN;
+
+ /* Is it a keyword? */
+ struct keyword *k = HASH_FIND(kw_hash, KW, data);
+ if (k)
+ {
+ if (k->value > 0)
+ return k->value;
+ else
{
- if (sym->scope == conf_this_scope)
- cf_error("Symbol already defined");
- sym = cf_new_sym(sym->name, cf_hash(sym->name));
+ cf_lval.i = -k->value;
+ return ENUM;
}
- sym->class = type;
- sym->def = def;
- return sym;
+ }
+
+ /* OK, undefined symbol */
+ cf_lval.s = sym;
+ return CF_SYM_UNDEFINED;
}
static void
cf_lex_init_kh(void)
{
- struct keyword *k;
+ HASH_INIT(kw_hash, &root_pool, KW_ORDER);
- for(k=keyword_list; k->name; k++)
- {
- unsigned h = cf_hash(k->name) & (KW_HASH_SIZE-1);
- k->next = kw_hash[h];
- kw_hash[h] = k;
- }
- kw_hash_inited = 1;
+ struct keyword *k;
+ for (k=keyword_list; k->name; k++)
+ HASH_INSERT(kw_hash, KW, k);
}
/**
* cf_lex_init - initialize the lexer
* @is_cli: true if we're going to parse CLI command, false for configuration
+ * @c: configuration structure
*
* cf_lex_init() initializes the lexical analyzer and prepares it for
* parsing of a new input.
void
cf_lex_init(int is_cli, struct config *c)
{
- if (!kw_hash_inited)
+ if (!kw_hash.data)
cf_lex_init_kh();
ifs_head = ifs = push_ifs(NULL);
- if (!is_cli)
+ if (!is_cli)
{
ifs->file_name = c->file_name;
ifs->fd = c->file_fd;
else
BEGIN(INITIAL);
- conf_this_scope = cfg_allocz(sizeof(struct sym_scope));
+ c->root_scope = cfg_allocz(sizeof(struct sym_scope));
+ conf_this_scope = c->root_scope;
conf_this_scope->active = 1;
}
conf_this_scope = s;
s->active = 1;
s->name = sym;
+ s->slots = 0;
}
/**
{
conf_this_scope->active = 0;
conf_this_scope = conf_this_scope->next;
- ASSERT(conf_this_scope);
-}
-struct symbol *
-cf_walk_symbols(struct config *cf, struct symbol *sym, int *pos)
-{
- for(;;)
- {
- if (!sym)
- {
- if (*pos >= SYM_HASH_SIZE)
- return NULL;
- sym = cf->sym_hash[(*pos)++];
- }
- else
- sym = sym->next;
- if (sym && sym->scope->active)
- return sym;
- }
+ ASSERT(conf_this_scope);
}
/**
char *
cf_symbol_class_name(struct symbol *sym)
{
- if (cf_symbol_is_constant(sym))
- return "constant";
-
switch (sym->class)
{
case SYM_VOID:
return "filter";
case SYM_TABLE:
return "routing table";
- case SYM_ROA:
- return "ROA table";
+ case SYM_ATTRIBUTE:
+ return "custom attribute";
+ case SYM_CONSTANT_RANGE:
+ return "constant";
+ case SYM_VARIABLE_RANGE:
+ return "variable";
default:
return "unknown type";
}