diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 1c8aa869..3336073b 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -60,14 +60,6 @@ stages: - linux - amd64 -build-debian-8-amd64: - <<: *build-linux - image: registry.nic.cz/labs/bird:debian-8-amd64 - -build-debian-8-i386: - <<: *build-linux - image: registry.nic.cz/labs/bird:debian-8-i386 - build-debian-9-amd64: <<: *build-linux image: registry.nic.cz/labs/bird:debian-9-amd64 @@ -224,28 +216,6 @@ build-opensuse-15.3-amd64: paths: - pkg/pkgs/* -# Dpkg error: PATH is not set -#pkg-debian-8-amd64: -# <<: *pkg-deb -# needs: [build-debian-8-amd64] -# image: registry.nic.cz/labs/bird:debian-8-amd64 - -# Dpkg error: PATH is not set -#pkg-debian-8-i386: -# <<: *pkg-deb -# needs: [build-debian-8-i386] -# image: registry.nic.cz/labs/bird:debian-8-i386 - -pkg-debian-9-amd64: - <<: *pkg-deb - needs: [build-debian-9-amd64] - image: registry.nic.cz/labs/bird:debian-9-amd64 - -pkg-debian-9-i386: - <<: *pkg-deb - needs: [build-debian-9-i386] - image: registry.nic.cz/labs/bird:debian-9-i386 - pkg-debian-10-amd64: <<: *pkg-deb needs: [build-debian-10-amd64] @@ -284,7 +254,7 @@ pkg-fedora-33-amd64: pkg-fedora-34-amd64: <<: *pkg-rpm needs: [build-fedora-34-amd64] - image: registry.labs.nic.cz/labs/bird:fedora-34-amd64 + image: registry.nic.cz/labs/bird:fedora-34-amd64 pkg-centos-8-amd64: <<: *pkg-rpm-wa @@ -465,11 +435,11 @@ test-ibgp-loop: variables: TEST_NAME: cf-ibgp-loop -test-ibgp-loop-big: - <<: *test-base - variables: - TEST_NAME: cf-ibgp-loop-big - +#test-ibgp-loop-big: +# <<: *test-base +# variables: +# TEST_NAME: cf-ibgp-loop-big +# test-ibgp-flat: <<: *test-base variables: diff --git a/NEWS b/NEWS index 5a344f42..5e51a2bd 100644 --- a/NEWS +++ b/NEWS @@ -29,6 +29,32 @@ Version 3.0-alpha0 (2022-02-07) o Lots of refactoring o Bugfixes and improvements as they came along +Version 2.13.1 (2023-06-23) + o BGP: Fix role check when no capability option is present + o Filter: Fixed segfault when a case option had an empty block + + This is a bugfix version. + +Version 2.13 (2023-04-21) + o Babel: IPv4 via IPv6 extension (RFC 9229) + o Babel: Improve authentication on lossy networks + o BGP: New 'allow bgp_med' option + o BSD: Support for IPv4 routes with IPv6 nexthop on FreeBSD + o Experimental BMP protocol implementation + o Important bugfixes + + Notes: + + We changed versioning scheme from .. to more common + .. . From now on, you may expect that BIRD 2.13.x will be + strictly only fixing bugs found in 2.13, whereas BIRD 2.14 will also contain + new features. + + This BIRD version contains an alpha release of BMP protocol implementation. + It is not ready for production usage and therefore it is not compiled by + default and have to be enabled during installation. + + Version 2.0.12 (2023-01-23) o Filter: New 'onlink' route attribute o Compile-time option to use 4-way tries instead of 16-way ones diff --git a/bird-gdb.py b/bird-gdb.py index c53a3b55..b277cb0a 100644 --- a/bird-gdb.py +++ b/bird-gdb.py @@ -34,6 +34,7 @@ class BIRDFValPrinter(BIRDPrinter): "T_IP": "ip", "T_NET": "net", "T_STRING": "s", + "T_BYTESTRING": "bs", "T_PATH_MASK": "path_mask", "T_PATH": "ad", "T_CLIST": "ad", diff --git a/conf/cf-lex.l b/conf/cf-lex.l index 7ce457fe..263311c2 100644 --- a/conf/cf-lex.l +++ b/conf/cf-lex.l @@ -42,7 +42,7 @@ #define PARSER 1 #include "nest/bird.h" -#include "nest/rt.h" +#include "nest/route.h" #include "nest/protocol.h" #include "filter/filter.h" #include "filter/f-inst.h" @@ -51,12 +51,6 @@ #include "lib/string.h" #include "lib/hash.h" -struct keyword { - byte *name; - int value; - struct keyword *next; -}; - #include "conf/keywords.h" /* Could be defined by Bison in cf-parse.tab.h, inteferes with SYM hash */ @@ -67,16 +61,10 @@ struct keyword { 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_KEY(n) n->name #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_EQ(a,b) !strcmp(a,b) +#define SYM_FN(k) cf_hash(k) #define SYM_ORDER 4 /* Initial */ #define SYM_REHASH sym_rehash @@ -85,14 +73,19 @@ static uint cf_hash(const byte *c); HASH_DEFINE_REHASH_FN(SYM, struct symbol) -HASH(struct keyword) kw_hash; -HASH(struct ea_class) ea_name_hash; +/* Global symbol scopes */ +pool *global_root_scope_pool; +linpool *global_root_scope_linpool; +struct sym_scope + global_root_scope = { + }, + global_filter_scope = { + .next = &global_root_scope, + }; +/* Local symbol scope: TODO this isn't thread-safe */ struct sym_scope *conf_this_scope; -static struct sym_scope global_root_scope__init = { .active = 1, }; -struct sym_scope *global_root_scope = &global_root_scope__init; - linpool *cfg_mem; int (*cf_read_hook)(byte *buf, unsigned int max, int fd); @@ -258,35 +251,24 @@ WHITE [ \t] return IP4; } -{XIGIT}{2}((:{XIGIT}{2}){15,}|({XIGIT}{2}){15,}) { +({XIGIT}{2}){16,}|{XIGIT}{2}(:{XIGIT}{2}){15,}|hex:({XIGIT}{2}(:?{XIGIT}{2})*)? { char *s = yytext; - size_t len = 0, i; - struct bytestring *bytes; - byte *b; + struct adata *bs; - while (*s) { - len++; - s += 2; - if (*s == ':') - s++; - } - bytes = cfg_allocz(sizeof(*bytes) + len); + /* Skip 'hex:' prefix */ + if (s[0] == 'h' && s[1] == 'e' && s[2] == 'x' && s[3] == ':') + s += 4; - bytes->length = len; - b = &bytes->data[0]; - s = yytext; - errno = 0; - for (i = 0; i < len; i++) { - *b = bstrtobyte16(s); - if (errno == ERANGE) - cf_error("Invalid hex string"); - b++; - s += 2; - if (*s == ':') - s++; - } - cf_lval.bs = bytes; - return BYTESTRING; + int len = bstrhextobin(s, NULL); + if (len < 0) + cf_error("Invalid hex string"); + + bs = cfg_allocz(sizeof(struct adata) + len); + bs->length = bstrhextobin(s, bs->data); + ASSERT(bs->length == len); + + cf_lval.bs = bs; + return BYTETEXT; } ({XIGIT}*::|({XIGIT}*:){3,})({XIGIT}*|{DIGIT}+\.{DIGIT}+\.{DIGIT}+\.{DIGIT}+) { @@ -402,6 +384,7 @@ else: { \>\= return GEQ; \&\& return AND; \|\| return OR; +\-\> return IMP; \[\= return PO; \=\] return PC; @@ -577,49 +560,56 @@ check_eof(void) return 0; } -static inline void cf_swap_soft_scope(void); +static inline void cf_swap_soft_scope(struct config *conf); -static struct symbol * -cf_new_symbol(const byte *c) +struct symbol * +cf_new_symbol(struct sym_scope *scope, pool *p, struct linpool *lp, const byte *c) { + if (scope->readonly) + cf_error("Unknown symbol %s", c); + struct symbol *s; uint l = strlen(c); if (l > SYM_MAX_LEN) cf_error("Symbol too long"); - cf_swap_soft_scope(); + s = lp_alloc(lp, sizeof(struct symbol) + l + 1); + *s = (struct symbol) { .scope = scope, .class = SYM_VOID, }; + memcpy(s->name, c, l+1); - s = cfg_allocz(sizeof(struct symbol) + l + 1); - *s = (struct symbol) { .scope = conf_this_scope, .class = SYM_VOID, }; - strcpy(s->name, c); + if (!scope->hash.data) + HASH_INIT(scope->hash, p, SYM_ORDER); - if (!conf_this_scope->hash.data) - HASH_INIT(conf_this_scope->hash, new_config->pool, SYM_ORDER); + HASH_INSERT2(scope->hash, SYM, p, s); - HASH_INSERT2(conf_this_scope->hash, SYM, new_config->pool, s); - - if (conf_this_scope == new_config->root_scope) + if (new_config && (scope == new_config->root_scope)) add_tail(&(new_config->symbols), &(s->n)); return s; } -static struct symbol * -cf_root_symbol(const byte *c) +struct symbol * +cf_root_symbol(const byte *c, struct sym_scope *ss) { uint l = strlen(c); if (l > SYM_MAX_LEN) bug("Root symbol %s too long", c); - struct symbol *s = mb_alloc(&root_pool, sizeof(struct symbol) + l + 1); - *s = (struct symbol) { .scope = global_root_scope, .class = SYM_VOID, }; + if (!global_root_scope_pool) + { + global_root_scope_pool = rp_new(&root_pool, the_bird_domain.the_bird, "Keywords pool"); + global_root_scope_linpool = lp_new(global_root_scope_pool); + } + + struct symbol *s = lp_alloc(global_root_scope_linpool, sizeof(struct symbol) + l + 1); + *s = (struct symbol) { .scope = ss, .class = SYM_VOID, }; memcpy(s->name, c, l+1); - if (!global_root_scope->hash.data) - HASH_INIT(global_root_scope->hash, &root_pool, SYM_ORDER); + if (!ss->hash.data) + HASH_INIT(ss->hash, &root_pool, SYM_ORDER); - HASH_INSERT2(global_root_scope->hash, SYM, &root_pool, s); + HASH_INSERT2(ss->hash, SYM, &root_pool, s); return s; } @@ -642,7 +632,8 @@ cf_find_symbol_scope(const struct sym_scope *scope, const byte *c) /* Find the symbol here or anywhere below */ while (scope) - if (scope->hash.data && (s = HASH_FIND(scope->hash, SYM, c, 1))) + if (((scope != &global_filter_scope) || !new_config || new_config->allow_attributes) && + scope->hash.data && (s = HASH_FIND(scope->hash, SYM, c))) return s; else scope = scope->next; @@ -660,9 +651,12 @@ cf_find_symbol_scope(const struct sym_scope *scope, const byte *c) * existing symbol is found. */ struct symbol * -cf_get_symbol(const byte *c) +cf_get_symbol(struct config *conf, const byte *c) { - return cf_find_symbol_scope(conf_this_scope, c) ?: cf_new_symbol(c); + 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) + ); } /** @@ -673,22 +667,23 @@ cf_get_symbol(const byte *c) * for purposes of cf_define_symbol(). */ struct symbol * -cf_localize_symbol(struct symbol *sym) +cf_localize_symbol(struct config *conf, 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 (cf_symbol_is_local(sym)) + if (cf_symbol_is_local(conf, sym)) cf_error("Symbol '%s' already defined", sym->name); /* Not allocated here yet, doing it now. */ - return cf_new_symbol(sym->name); + cf_swap_soft_scope(conf); + return cf_new_symbol(conf->current_scope, conf->pool, conf->mem, sym->name); } struct symbol * -cf_default_name(char *template, int *counter) +cf_default_name(struct config *conf, char *template, int *counter) { char buf[SYM_MAX_LEN]; struct symbol *s; @@ -697,7 +692,7 @@ cf_default_name(char *template, int *counter) for(;;) { bsprintf(buf, template, ++(*counter)); - s = cf_get_symbol(buf); + s = cf_get_symbol(conf, buf); if (s->class == SYM_VOID) return s; if (!perc) @@ -710,67 +705,56 @@ static enum yytokentype cf_lex_symbol(const char *data) { /* Have we defined such a symbol? */ - struct symbol *sym = cf_get_symbol(data); + struct symbol *sym = cf_get_symbol(new_config, data); cf_lval.s = sym; - /* Is it a keyword? Prefer the keyword. */ - struct keyword *k = HASH_FIND(kw_hash, KW, data); - if (k) + switch (sym->class) { - if (k->value > 0) - return k->value; - else + case SYM_KEYWORD: { - cf_lval.i = -k->value; + int val = sym->keyword->value; + if (val > 0) return val; + cf_lval.i = -val; return ENUM; } + case SYM_METHOD: + return (sym->method->arg_num > 1) ? CF_SYM_METHOD_ARGS : CF_SYM_METHOD_BARE; + case SYM_VOID: + return CF_SYM_UNDEFINED; + default: + return CF_SYM_KNOWN; } - - /* OK, only a symbol. */ - if (sym->class == SYM_VOID) - return CF_SYM_UNDEFINED; - else - return CF_SYM_KNOWN; -} - -static void -cf_lex_init_kh(void) -{ - HASH_INIT(kw_hash, config_pool, KW_ORDER); - - struct keyword *k; - for (k=keyword_list; k->name; k++) - HASH_INSERT(kw_hash, KW, k); } void ea_lex_register(struct ea_class *def) { - struct symbol *sym = cf_root_symbol(def->name); - sym->class = SYM_ATTRIBUTE; - sym->attribute = def; - def->sym = sym; + def->sym = cf_root_symbol(def->name, &global_filter_scope); + def->sym->class = SYM_ATTRIBUTE; + def->sym->attribute = def; } +#if 0 +/* When we start to support complete protocol removal, we may need this function */ void ea_lex_unregister(struct ea_class *def) { struct symbol *sym = def->sym; - HASH_REMOVE2(global_root_scope->hash, SYM, &root_pool, sym); + HASH_REMOVE2(global_filter_scope.hash, SYM, &root_pool, sym); mb_free(sym); def->sym = NULL; } +#endif struct ea_class * ea_class_find_by_name(const char *name) { - struct symbol *sym = cf_find_symbol(global_root_scope, name); - if (!sym || (sym->class != SYM_ATTRIBUTE)) - return NULL; - else - return sym->attribute; + struct symbol *sym = cf_find_symbol_scope(config ? config->root_scope : &global_filter_scope, name); + return sym && (sym->class == SYM_ATTRIBUTE) ? sym->attribute : NULL; } +void f_type_methods_register(void); + /** * cf_lex_init - initialize the lexer * @is_cli: true if we're going to parse CLI command, false for configuration @@ -782,8 +766,20 @@ ea_class_find_by_name(const char *name) void cf_lex_init(int is_cli, struct config *c) { - if (!kw_hash.data) - cf_lex_init_kh(); + if (!global_root_scope.readonly) + { + for (const struct keyword *k = keyword_list; k->name; k++) + { + struct symbol *s = cf_root_symbol(k->name, &global_root_scope); + s->class = SYM_KEYWORD; + s->keyword = k; + } + + global_root_scope.readonly = 1; + global_filter_scope.readonly = 1; + + f_type_methods_register(); + } ifs_head = ifs = push_ifs(NULL); if (!is_cli) @@ -801,14 +797,12 @@ cf_lex_init(int is_cli, struct config *c) else BEGIN(INITIAL); - c->root_scope = cfg_allocz(sizeof(struct sym_scope)); - conf_this_scope = c->root_scope; - conf_this_scope->active = 1; + c->root_scope = c->current_scope = cfg_allocz(sizeof(struct sym_scope)); if (is_cli) - conf_this_scope->next = config->root_scope; + c->current_scope->next = config->root_scope; else - conf_this_scope->next = global_root_scope; + c->current_scope->next = &global_filter_scope; } /** @@ -822,13 +816,12 @@ cf_lex_init(int is_cli, struct config *c) * in all scopes stored on the stack. */ void -cf_push_scope(struct symbol *sym) +cf_push_scope(struct config *conf, struct symbol *sym) { struct sym_scope *s = cfg_allocz(sizeof(struct sym_scope)); - s->next = conf_this_scope; - conf_this_scope = s; - s->active = 1; + s->next = conf->current_scope; + conf->current_scope = s; s->name = sym; s->slots = 0; } @@ -841,14 +834,11 @@ cf_push_scope(struct symbol *sym) * invisible to the rest of the config. */ void -cf_pop_scope(void) +cf_pop_scope(struct config *conf) { - ASSERT(!conf_this_scope->soft_scopes); - - conf_this_scope->active = 0; - conf_this_scope = conf_this_scope->next; - - ASSERT(conf_this_scope); + ASSERT(!conf->current_scope->soft_scopes); + conf->current_scope = conf->current_scope->next; + ASSERT(conf->current_scope); } /** @@ -859,12 +849,12 @@ cf_pop_scope(void) * Such scope will be converted to a regular scope on first use. */ void -cf_push_soft_scope(void) +cf_push_soft_scope(struct config *conf) { - if (conf_this_scope->soft_scopes < 0xfe) - conf_this_scope->soft_scopes++; + if (conf->current_scope->soft_scopes < 0xfe) + conf->current_scope->soft_scopes++; else - cf_push_block_scope(); + cf_push_block_scope(conf); } /** @@ -873,12 +863,12 @@ cf_push_soft_scope(void) * Leave a soft scope entered by cf_push_soft_scope(). */ void -cf_pop_soft_scope(void) +cf_pop_soft_scope(struct config *conf) { - if (conf_this_scope->soft_scopes) - conf_this_scope->soft_scopes--; + if (conf->current_scope->soft_scopes) + conf->current_scope->soft_scopes--; else - cf_pop_block_scope(); + cf_pop_block_scope(conf); } /** @@ -888,15 +878,36 @@ cf_pop_soft_scope(void) * on first use. It is done automatically by cf_new_symbol(). */ static inline void -cf_swap_soft_scope(void) +cf_swap_soft_scope(struct config *conf) { - if (conf_this_scope->soft_scopes) + if (conf->current_scope->soft_scopes) { - conf_this_scope->soft_scopes--; - cf_push_block_scope(); + conf->current_scope->soft_scopes--; + cf_push_block_scope(conf); } } +/** + * cf_enter_filters - enable filter / route attributes namespace + */ +void +cf_enter_filters(void) +{ + ASSERT_DIE(!new_config->allow_attributes); + new_config->allow_attributes = 1; +} + +/** + * cf_exit_filters - disable filter / route attributes namespace + */ +void +cf_exit_filters(void) +{ + ASSERT_DIE(new_config->allow_attributes); + new_config->allow_attributes = 0; +} + + /** * cf_symbol_class_name - get name of a symbol class * @sym: symbol @@ -923,6 +934,8 @@ cf_symbol_class_name(struct symbol *sym) return "routing table"; case SYM_ATTRIBUTE: return "custom attribute"; + case SYM_KEYWORD: + return "symbol"; case SYM_CONSTANT_RANGE: return "constant"; case SYM_VARIABLE_RANGE: diff --git a/conf/conf.c b/conf/conf.c index d828b6df..84a4c048 100644 --- a/conf/conf.c +++ b/conf/conf.c @@ -46,7 +46,7 @@ #undef LOCAL_DEBUG #include "nest/bird.h" -#include "nest/rt.h" +#include "nest/route.h" #include "nest/protocol.h" #include "nest/iface.h" #include "lib/resource.h" @@ -60,7 +60,8 @@ static jmp_buf conf_jmpbuf; -struct config *config, *new_config; +struct config *config; +_Thread_local struct config *new_config; pool *config_pool; static struct config *old_config; /* Old configuration */ diff --git a/conf/conf.h b/conf/conf.h index 58a2733b..841d5c1f 100644 --- a/conf/conf.h +++ b/conf/conf.h @@ -57,6 +57,8 @@ struct config { int thread_count; /* How many worker threads to prefork */ struct sym_scope *root_scope; /* Scope for root symbols */ + struct sym_scope *current_scope; /* Current scope where we are actually in while parsing */ + int allow_attributes; /* Allow attributes in the current state of configuration parsing */ _Atomic int obstacle_count; /* Number of items blocking freeing of this config */ event done_event; /* Called when obstacle_count reaches zero */ int shutdown; /* This is a pseudo-config for daemon shutdown */ @@ -66,7 +68,7 @@ struct config { /* Please don't use these variables in protocols. Use proto_config->global instead. */ extern struct config *config; /* Currently active configuration */ -extern struct config *new_config; /* Configuration being parsed */ +extern _Thread_local struct config *new_config; /* Configuration being parsed */ struct config *config_alloc(const char *name); int config_parse(struct config *); @@ -80,6 +82,7 @@ int config_status(void); btime config_timer_status(void); void config_init(void); void cf_error(const char *msg, ...) NORET; +#define cf_warn(msg, args...) log(L_WARN "%s:%d:%d: " msg, ifs->file_name, ifs->lino, ifs->chno - ifs->toklen + 1, ##args) void config_add_obstacle(struct config *); void config_del_obstacle(struct config *); void order_shutdown(int gr); @@ -112,6 +115,11 @@ void cfg_copy_list(list *dest, list *src, unsigned node_size); extern int (*cf_read_hook)(byte *buf, uint max, int fd); +struct keyword { + byte *name; + int value; +}; + struct symbol { node n; /* In list of symbols in config */ struct symbol *next; @@ -127,6 +135,8 @@ struct symbol { struct ea_class *attribute; /* For SYM_ATTRIBUTE */ struct f_val *val; /* For SYM_CONSTANT */ uint offset; /* For SYM_VARIABLE */ + const struct keyword *keyword; /* For SYM_KEYWORD */ + const struct f_method *method; /* For SYM_METHOD */ }; char name[0]; @@ -139,17 +149,17 @@ struct sym_scope { HASH(struct symbol) hash; /* Local symbol hash */ uint slots; /* Variable slots */ - byte active; /* Currently entered */ - byte block; /* No independent stack frame */ byte soft_scopes; /* Number of soft scopes above */ + byte block:1; /* No independent stack frame */ + byte readonly:1; /* Do not add new symbols */ }; -extern struct sym_scope *global_root_scope; +void cf_enter_filters(void); +void cf_exit_filters(void); + +extern pool *global_root_scope_pool; +extern linpool *global_root_scope_linpool; -struct bytestring { - size_t length; - byte data[]; -}; #define SYM_MAX_LEN 64 @@ -161,6 +171,8 @@ struct bytestring { #define SYM_FILTER 4 #define SYM_TABLE 5 #define SYM_ATTRIBUTE 6 +#define SYM_KEYWORD 7 +#define SYM_METHOD 8 #define SYM_VARIABLE 0x100 /* 0x100-0x1ff are variable types */ #define SYM_VARIABLE_RANGE SYM_VARIABLE ... (SYM_VARIABLE | 0xff) @@ -188,8 +200,6 @@ struct include_file_stack { extern struct include_file_stack *ifs; -extern struct sym_scope *conf_this_scope; - int cf_lex(void); void cf_lex_init(int is_cli, struct config *c); void cf_lex_unwind(void); @@ -203,12 +213,16 @@ static inline struct symbol *cf_find_symbol_cfg(const struct config *cfg, const struct sym_scope: cf_find_symbol_scope \ )((where), (what)) -struct symbol *cf_get_symbol(const byte *c); -struct symbol *cf_default_name(char *template, int *counter); -struct symbol *cf_localize_symbol(struct symbol *sym); +struct symbol *cf_get_symbol(struct config *conf, const byte *c); +struct symbol *cf_default_name(struct config *conf, char *template, int *counter); +struct symbol *cf_localize_symbol(struct config *conf, struct symbol *sym); -static inline int cf_symbol_is_local(struct symbol *sym) -{ return (sym->scope == conf_this_scope) && !conf_this_scope->soft_scopes; } +static inline int cf_symbol_is_local(struct config *conf, struct symbol *sym) +{ return (sym->scope == conf->current_scope) && !conf->current_scope->soft_scopes; } + +/* internal */ +struct symbol *cf_new_symbol(struct sym_scope *scope, pool *p, struct linpool *lp, const byte *c); +struct symbol *cf_root_symbol(const byte *, struct sym_scope *); /** * cf_define_symbol - define meaning of a symbol @@ -225,22 +239,22 @@ static inline int cf_symbol_is_local(struct symbol *sym) * 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. */ -#define cf_define_symbol(osym_, type_, var_, def_) ({ \ - struct symbol *sym_ = cf_localize_symbol(osym_); \ +#define cf_define_symbol(conf_, osym_, type_, var_, def_) ({ \ + struct symbol *sym_ = cf_localize_symbol(conf_, osym_); \ sym_->class = type_; \ sym_->var_ = def_; \ sym_; }) -void cf_push_scope(struct symbol *); -void cf_pop_scope(void); -void cf_push_soft_scope(void); -void cf_pop_soft_scope(void); +void cf_push_scope(struct config *, struct symbol *); +void cf_pop_scope(struct config *); +void cf_push_soft_scope(struct config *); +void cf_pop_soft_scope(struct config *); -static inline void cf_push_block_scope(void) -{ cf_push_scope(NULL); conf_this_scope->block = 1; } +static inline void cf_push_block_scope(struct config *conf) +{ cf_push_scope(conf, NULL); conf->current_scope->block = 1; } -static inline void cf_pop_block_scope(void) -{ ASSERT(conf_this_scope->block); cf_pop_scope(); } +static inline void cf_pop_block_scope(struct config *conf) +{ ASSERT(conf->current_scope->block); cf_pop_scope(conf); } char *cf_symbol_class_name(struct symbol *sym); diff --git a/conf/confbase.Y b/conf/confbase.Y index c764f9e4..37198751 100644 --- a/conf/confbase.Y +++ b/conf/confbase.Y @@ -19,7 +19,7 @@ CF_HDR #include "lib/string.h" #include "nest/protocol.h" #include "nest/iface.h" -#include "nest/rt.h" +#include "nest/route.h" #include "nest/bfd.h" #include "nest/cli.h" #include "filter/filter.h" @@ -97,19 +97,20 @@ CF_DECLS struct timeformat *tf; struct settle_config settle; struct adata *ad; - struct bytestring *bs; + const struct adata *bs; + struct aggr_item_node *ai; } %token END CLI_MARKER INVALID_TOKEN ELSECOL DDOT -%token GEQ LEQ NEQ AND OR +%token GEQ LEQ NEQ AND OR IMP %token PO PC %token NUM ENUM %token IP4 %token IP6 %token VPN_RD -%token CF_SYM_KNOWN CF_SYM_UNDEFINED +%token CF_SYM_KNOWN CF_SYM_UNDEFINED CF_SYM_METHOD_BARE CF_SYM_METHOD_ARGS %token TEXT -%token BYTESTRING +%token BYTETEXT %type ipa_scope %type expr bool pxlen4 @@ -121,11 +122,15 @@ CF_DECLS %type label_stack_start label_stack %type text opttext -%type symbol symbol_known toksym +%type bytestring +%type symbol + +%type bytestring_text text_or_ipa +%type bytestring_expr %nonassoc PREFIX_DUMMY %left AND OR -%nonassoc '=' '<' '>' '~' GEQ LEQ NEQ NMA PO PC +%nonassoc '=' '<' '>' '~' GEQ LEQ NEQ NMA IMP PO PC %left '|' '&' %left '+' '-' %left '*' '/' '%' @@ -134,7 +139,7 @@ CF_DECLS %start config -CF_KEYWORDS(DEFINE, ON, OFF, YES, NO, S, MS, US, PORT, VPN, MPLS, FROM) +CF_KEYWORDS(DEFINE, ON, OFF, YES, NO, S, MS, US, PORT, VPN, MPLS, FROM, MAX, AS) CF_GRAMMAR @@ -158,16 +163,15 @@ conf: definition ; definition: DEFINE symbol '=' term ';' { - struct f_val val; - if (f_eval(f_linearize($4, 1), &val) > F_RETURN) cf_error("Runtime error"); - cf_define_symbol($2, SYM_CONSTANT | val.type, val, lp_val_copy(cfg_mem, &val)); + struct f_val *val = cf_eval($4, T_VOID); + cf_define_symbol(new_config, $2, SYM_CONSTANT | val->type, val, val); } ; expr: NUM - | '(' term ')' { $$ = f_eval_int(f_linearize($2, 1)); } - | symbol_known { + | '(' term ')' { $$ = cf_eval_int($2); } + | CF_SYM_KNOWN { if ($1->class != (SYM_CONSTANT | T_INT)) cf_error("Number constant expected"); $$ = SYM_VAL($1).i; } ; @@ -178,9 +182,7 @@ expr_us: | expr US { $$ = $1 US_; } ; -toksym: FROM | PREFERENCE ; -symbol: CF_SYM_UNDEFINED | CF_SYM_KNOWN | toksym ; -symbol_known: CF_SYM_KNOWN | toksym ; +symbol: CF_SYM_UNDEFINED | CF_SYM_KNOWN ; /* Switches */ @@ -410,6 +412,45 @@ opttext: | /* empty */ { $$ = NULL; } ; +text_or_ipa: + TEXT { $$.type = T_STRING; $$.val.s = $1; } + | IP4 { $$.type = T_IP; $$.val.ip = ipa_from_ip4($1); } + | IP6 { $$.type = T_IP; $$.val.ip = ipa_from_ip6($1); } + | CF_SYM_KNOWN { + if (($1->class == (SYM_CONSTANT | T_STRING)) || + ($1->class == (SYM_CONSTANT | T_IP))) + $$ = *($1->val); + else + cf_error("String or IP constant expected"); + } + | '(' term ')' { + $$ = *cf_eval($2, T_VOID); + if (($$.type != T_BYTESTRING) && ($$.type != T_STRING)) + cf_error("Bytestring or string value expected"); + } + ; + +bytestring: + BYTETEXT + | bytestring_expr { $$ = cf_eval($1, T_BYTESTRING)->val.bs; } + ; + +bytestring_text: + BYTETEXT { $$.type = T_BYTESTRING; $$.val.bs = $1; } + | TEXT { $$.type = T_STRING; $$.val.s = $1; } + | bytestring_expr { + $$ = *cf_eval($1, T_VOID); + if (($$.type != T_BYTESTRING) && ($$.type != T_STRING)) + cf_error("Bytestring or string value expected"); + } + ; + +bytestring_expr: + lvalue { $$ = f_lval_getter(&$1); } + | term_bs + | '(' term ')' { $$ = $2; } + ; + CF_CODE diff --git a/conf/gen_keywords.m4 b/conf/gen_keywords.m4 index 53226e4d..5667ceaa 100644 --- a/conf/gen_keywords.m4 +++ b/conf/gen_keywords.m4 @@ -23,7 +23,7 @@ m4_define(CF_DECLS, `m4_divert(-1)') m4_define(CF_DEFINES, `m4_divert(-1)') # Keywords are translated to C initializers -m4_define(CF_handle_kw, `m4_divert(1){ "m4_translit($1,[[A-Z]],[[a-z]])", $1, NULL }, +m4_define(CF_handle_kw, `m4_divert(1){ "m4_translit($1,[[A-Z]],[[a-z]])", $1 }, m4_divert(-1)') m4_define(CF_keywd, `m4_ifdef([[CF_tok_$1]],,[[m4_define([[CF_tok_$1]],1)CF_handle_kw($1)]])') m4_define(CF_KEYWORDS, `CF_iterate([[CF_keywd]], [[$@]])DNL') @@ -34,16 +34,16 @@ m4_define(CF_CLI, `CF_KEYWORDS(m4_translit($1, [[ ]], [[,]])) # Enums are translated to C initializers: use CF_ENUM(typename, prefix, values) # For different prefix: CF_ENUM_PX(typename, external prefix, C prefix, values) -m4_define(CF_enum, `m4_divert(1){ "CF_enum_prefix_ext[[]]$1", -((CF_enum_type<<16) | CF_enum_prefix_int[[]]$1), NULL }, +m4_define(CF_enum, `m4_divert(1){ "CF_enum_prefix_ext[[]]$1", -((CF_enum_type<<16) | CF_enum_prefix_int[[]]$1) }, m4_divert(-1)') m4_define(CF_ENUM, `m4_define([[CF_enum_type]],$1)m4_define([[CF_enum_prefix_ext]],$2)m4_define([[CF_enum_prefix_int]],$2)CF_iterate([[CF_enum]], [[m4_shift(m4_shift($@))]])DNL') m4_define(CF_ENUM_PX, `m4_define([[CF_enum_type]],$1)m4_define([[CF_enum_prefix_ext]],$2)m4_define([[CF_enum_prefix_int]],$3)CF_iterate([[CF_enum]], [[m4_shift(m4_shift(m4_shift($@)))]])DNL') -# After all configuration templates end, we generate the +# After all configuration templates end, we generate the keyword list m4_m4wrap(` m4_divert(0) -static struct keyword keyword_list[] = { -m4_undivert(1){ NULL, -1, NULL } }; +static const struct keyword keyword_list[] = { +m4_undivert(1){ NULL, -1 } }; ') # As we are processing C source, we must access all M4 primitives via diff --git a/conf/gen_parser.m4 b/conf/gen_parser.m4 index af4b1455..b7167d7a 100644 --- a/conf/gen_parser.m4 +++ b/conf/gen_parser.m4 @@ -33,6 +33,8 @@ m4_define(CF_iterate, `m4_define([[CF_iter]], m4_defn([[$1]]))CF_itera($2)') m4_define(CF_keywd, `m4_ifdef([[CF_tok_$1]],,[[m4_define([[CF_tok_$1]],1)m4_define([[CF_toks]],CF_toks $1)]])') m4_define(CF_KEYWORDS, `m4_define([[CF_toks]],[[]])CF_iterate([[CF_keywd]], [[$@]])m4_ifelse(CF_toks,,,%token[[]]CF_toks )DNL') +m4_define(CF_METHODS, `m4_define([[CF_toks]],[[]])CF_iterate([[CF_keywd]], [[$@]])m4_ifelse(CF_toks,,,%token[[]]CF_toks +)DNL') # CLI commands m4_define(CF_CLI, `m4_define([[CF_cmd]], cmd_[[]]m4_translit($1, [[ ]], _))DNL diff --git a/configure.ac b/configure.ac index bf5c6df4..b9970443 100644 --- a/configure.ac +++ b/configure.ac @@ -307,16 +307,18 @@ if test "$enable_mpls_kernel" != no ; then fi # temporarily removed "mrt" from all_protocols to speed up 3.0-alpha1 release -all_protocols="bfd babel bgp ospf perf pipe radv rip rpki static" +all_protocols="aggregator bfd babel bgp ospf perf pipe radv rip rpki static" all_protocols=`echo $all_protocols | sed 's/ /,/g'` if test "$with_protocols" = all ; then with_protocols="$all_protocols" fi +AH_TEMPLATE([CONFIG_AGGREGATOR],[Aggregator protocol]) AH_TEMPLATE([CONFIG_BABEL], [Babel protocol]) AH_TEMPLATE([CONFIG_BFD], [BFD protocol]) AH_TEMPLATE([CONFIG_BGP], [BGP protocol]) +AH_TEMPLATE([CONFIG_BMP], [BMP protocol]) AH_TEMPLATE([CONFIG_MRT], [MRT protocol]) AH_TEMPLATE([CONFIG_OSPF], [OSPF protocol]) AH_TEMPLATE([CONFIG_PIPE], [Pipe protocol]) diff --git a/distro/config/apkg.toml b/distro/config/apkg.toml index c70306d6..73a79a3b 100644 --- a/distro/config/apkg.toml +++ b/distro/config/apkg.toml @@ -6,3 +6,7 @@ make_archive_script = "tools/make-dev-archive" [upstream] # needed for get-archive archive_url = "https://bird.network.cz/download/bird-{{ version }}.tar.gz" + +[apkg] +# apkg compat level +compat = 3 diff --git a/distro/pkg/deb/bird.xml b/distro/pkg/deb/bird.xml index 4ba3868f..2cc69575 100644 --- a/distro/pkg/deb/bird.xml +++ b/distro/pkg/deb/bird.xml @@ -65,11 +65,23 @@ man(1), man(7), http://www.tldp.org/HOWTO/Man-Page/ &dhemail; + + Jakub + Ružička + Updated this manpage for birdcl. +
+ jakub.ruzicka@nic.cz +
+
2010 &dhusername; + + 2022 + Jakub Ružička + This manual page was written for the Debian system (and may be used by others). @@ -95,6 +107,10 @@ man(1), man(7), http://www.tldp.org/HOWTO/Man-Page/ birdc BIRD Internet Routing Daemon remote control + + birdcl + BIRD Internet Routing Daemon remote control light + @@ -118,6 +134,13 @@ man(1), man(7), http://www.tldp.org/HOWTO/Man-Page/ + + birdcl + + + + + @@ -136,7 +159,10 @@ man(1), man(7), http://www.tldp.org/HOWTO/Man-Page/ communicate. Once started, bird will give access to an interactive shell: commands can be completed with TAB and help can be requested by pressing the key `?'. More documentation on - the available commands can be foung on the website, see below. + the available commands can be found on the website, see below. + birdcl is a light version of birdc + remote control for bird without readline/ncurses support. + TAB completion isn't available. @@ -230,7 +256,7 @@ man(1), man(7), http://www.tldp.org/HOWTO/Man-Page/ - The birdc accepts these options: + birdc and birdcl accept these options: @@ -279,8 +305,7 @@ man(1), man(7), http://www.tldp.org/HOWTO/Man-Page/ SEE ALSO - More documentation con be found on the website: + More documentation can be found on the website: https://bird.network.cz/. - diff --git a/distro/pkg/deb/bird2.lintian-overrides b/distro/pkg/deb/bird2.lintian-overrides deleted file mode 100644 index e64c0ef6..00000000 --- a/distro/pkg/deb/bird2.lintian-overrides +++ /dev/null @@ -1 +0,0 @@ -bird2: binary-without-manpage usr/sbin/birdcl diff --git a/distro/pkg/deb/bird2.manpages b/distro/pkg/deb/bird2.manpages index 94898ea1..cd80062c 100644 --- a/distro/pkg/deb/bird2.manpages +++ b/distro/pkg/deb/bird2.manpages @@ -1,2 +1,3 @@ bird.8 birdc.8 +birdcl.8 diff --git a/distro/pkg/deb/changelog b/distro/pkg/deb/changelog index d371bc06..cdfe3d95 100644 --- a/distro/pkg/deb/changelog +++ b/distro/pkg/deb/changelog @@ -2,4 +2,4 @@ bird2 ({{ version }}-cznic.{{ release }}) unstable; urgency=medium * upstream package - -- Jakub Ružička Mon, 29 Mar 2021 14:15:50 +0000 + -- Jakub Ružička {{ now }} diff --git a/distro/pkg/deb/compat b/distro/pkg/deb/compat index ec635144..b4de3947 100644 --- a/distro/pkg/deb/compat +++ b/distro/pkg/deb/compat @@ -1 +1 @@ -9 +11 diff --git a/distro/pkg/deb/control b/distro/pkg/deb/control index d431022b..638795e2 100644 --- a/distro/pkg/deb/control +++ b/distro/pkg/deb/control @@ -5,18 +5,18 @@ Build-Depends: bison, debhelper, docbook-xsl, flex, - libncurses5-dev, + libncurses-dev, libreadline-dev | libreadline6-dev | libreadline5-dev, libssh-gcrypt-dev, - linuxdoc-tools-latex, m4, - opensp, quilt, - texlive-latex-extra, xsltproc +Build-Depends-Indep: linuxdoc-tools-latex, + opensp, + texlive-latex-extra Maintainer: Jakub Ružička Uploaders: Ondřej Surý -Standards-Version: 4.3.0 +Standards-Version: 4.6.2 Vcs-Browser: https://salsa.debian.org/debian/bird2 Vcs-Git: https://salsa.debian.org/debian/bird2.git Homepage: https://bird.network.cz/ @@ -26,7 +26,6 @@ Architecture: kfreebsd-any linux-any Pre-Depends: init-system-helpers, ${misc:Pre-Depends} Depends: adduser, - lsb-base, ucf, ${misc:Depends}, ${shlibs:Depends} diff --git a/distro/pkg/deb/rules b/distro/pkg/deb/rules index 5630ed1c..1740fb95 100755 --- a/distro/pkg/deb/rules +++ b/distro/pkg/deb/rules @@ -23,8 +23,7 @@ LDFLAGS += -g -O2 -fno-strict-aliasing -fno-strict-overflow -fPIC -Wl,-z,defs -W override_dh_auto_configure: CFLAGS="$(CFLAGS)" LDFLAGS="$(LDFLAGS)" dh_auto_configure -- $(COMMON_FLAGS) --with-protocols=all -override_dh_auto_build: - dh_auto_build +override_dh_auto_build-indep: dh_auto_build -- docs override_dh_auto_install: @@ -47,10 +46,10 @@ override_dh_installman: bird.8 override_dh_clean: dh_clean - -rm -f bird.8 birdc.8 + -rm -f bird.8 birdc.8 birdcl.8 override_dh_missing: - dh_missing --fail-missing + dh_missing override_dh_compress: dh_compress -X.conf diff --git a/distro/pkg/deb/watch b/distro/pkg/deb/watch index 1874bfbb..cec2d664 100644 --- a/distro/pkg/deb/watch +++ b/distro/pkg/deb/watch @@ -1,2 +1,2 @@ -version=3 +version=4 https://bird.network.cz/download/bird-(2\.[\d.]+).tar.gz diff --git a/distro/pkg/rpm/bird.spec b/distro/pkg/rpm/bird.spec index cd51ef51..eb6b8aa9 100644 --- a/distro/pkg/rpm/bird.spec +++ b/distro/pkg/rpm/bird.spec @@ -1,17 +1,21 @@ %global _hardened_build 1 %global _without_doc 1 +%{!?_rundir:%global _rundir %%{_localstatedir}/run} Name: bird Version: {{ version }} Release: cznic.{{ release }}%{?dist} Summary: BIRD Internet Routing Daemon +Group: System Environment/Daemons License: GPL-2.0-or-later URL: https://bird.network.cz/ Source0: https://bird.network.cz/download/bird-%{version}.tar.gz Source1: bird.service Source2: bird.tmpfilesd +Source3: bird.sysusersd +BuildRequires: autoconf BuildRequires: flex BuildRequires: bison BuildRequires: ncurses-devel @@ -20,14 +24,13 @@ BuildRequires: sed BuildRequires: gcc BuildRequires: make BuildRequires: libssh-devel -%if 0%{?fedora} || (0%{?rhel} && 0%{?rhel} > 7) -BuildRequires: systemd-rpm-macros -%else -BuildRequires: systemd +%if 0%{?rhel} && 0%{?rhel} < 8 +# http://trubka.network.cz/pipermail/bird-users/2019-August/013631.html +BuildRequires: devtoolset-8-toolchain %endif - -Obsoletes: bird6 < 2.0.2-1 -Provides: bird6 = %{version}-%{release} +BuildRequires: systemd-rpm-macros +%{?systemd_requires} +%{?sysusers_requires_compat} %description BIRD is a dynamic IP routing daemon supporting both, IPv4 and IPv6, Border @@ -41,6 +44,7 @@ powerful language for route filtering. %if 0%{!?_without_doc:1} %package doc Summary: Documentation for BIRD Internet Routing Daemon +Group: Documentation BuildRequires: linuxdoc-tools sgml-common perl(FindBin) BuildArch: noarch @@ -57,9 +61,13 @@ powerful language for route filtering. %endif %prep -%setup -q +%setup -q -n bird-%{version} %build +%if 0%{?rhel} && 0%{?rhel} < 8 +. /opt/rh/devtoolset-8/enable +%endif + %configure --runstatedir=%{_rundir}/bird %make_build all %{!?_without_doc:docs} @@ -70,17 +78,18 @@ powerful language for route filtering. install -d %{buildroot}{%{_localstatedir}/lib/bird,%{_rundir}/bird} install -D -p -m 0644 %{SOURCE1} %{buildroot}%{_unitdir}/bird.service install -D -p -m 0644 %{SOURCE2} %{buildroot}%{_tmpfilesdir}/bird.conf +install -D -p -m 0644 %{SOURCE3} %{buildroot}%{_sysusersdir}/bird.conf {% endraw %} %check +%if 0%{?rhel} && 0%{?rhel} < 8 +. /opt/rh/devtoolset-8/enable +%endif + make test %pre -getent group bird >/dev/null || groupadd -r bird -getent passwd bird >/dev/null || \ - useradd -r -g bird -d %{_localstatedir}/lib/bird -s /sbin/nologin \ - -c "BIRD daemon user" bird -exit 0 +%sysusers_create_compat %{SOURCE3} %post %systemd_post bird.service @@ -95,12 +104,13 @@ exit 0 %doc NEWS README %attr(0640,root,bird) %config(noreplace) %{_sysconfdir}/bird.conf %{_unitdir}/bird.service +%{_sysusersdir}/bird.conf %{_tmpfilesdir}/bird.conf %{_sbindir}/bird %{_sbindir}/birdc %{_sbindir}/birdcl %dir %attr(0750,bird,bird) %{_localstatedir}/lib/bird -%dir %attr(0750,bird,bird) %ghost %{_rundir}/bird +%dir %attr(0750,bird,bird) %{_rundir}/bird %if 0%{!?_without_doc:1} %files doc diff --git a/distro/pkg/rpm/bird.sysusersd b/distro/pkg/rpm/bird.sysusersd new file mode 100644 index 00000000..1c808666 --- /dev/null +++ b/distro/pkg/rpm/bird.sysusersd @@ -0,0 +1,2 @@ +#Type Name ID GECOS Home directory Shell +u bird - "BIRD daemon user" /var/lib/bird - diff --git a/distro/tests/control b/distro/tests/control new file mode 100644 index 00000000..f588af8f --- /dev/null +++ b/distro/tests/control @@ -0,0 +1,3 @@ +Tests: test-bird.sh +Restrictions: needs-root +Depends: bird2, iproute2 diff --git a/distro/tests/test-bird.sh b/distro/tests/test-bird.sh new file mode 100755 index 00000000..464be32f --- /dev/null +++ b/distro/tests/test-bird.sh @@ -0,0 +1,105 @@ +#!/bin/bash + +set -e + +LOCAL=2001:db8:dead:: + +EXTERNAL=2001:db8:beef:: +EXTERNAL_NET=${EXTERNAL}/48 +EXTERNAL_NH=${LOCAL}beef + +LEARN=2001:db8:feed:: +LEARN_NET=${LEARN}/48 +LEARN_NH=${LOCAL}feed + +IFACE=bird-test-dummy +IFACE_EXISTS=false + +BIRD_RUNNING=false + +D=$(mktemp -d) +pushd ${D} >/dev/null + +stop_bird() { + birdc -l down >/dev/null + sleep 1 + grep -q " Shutdown completed" bird.log + [ ! -e bird.pid ] + [ ! -e bird.ctl ] +} + +cleanup() { + if ${BIRD_RUNNING}; then + stop_bird + if [ -e bird.pid ]; then + kill -9 $( /dev/null + rm -rf ${D} +} + +failed() { + cleanup + exit 1 +} + +trap failed ERR +trap failed INT +trap failed HUP + +ip link add ${IFACE} type dummy +IFACE_EXISTS=true + +ip link set ${IFACE} up +ip -6 addr add ${LOCAL}/64 dev bird-test-dummy + +ip -6 route add ${LEARN_NET} via ${LEARN_NH} + +cat >bird.conf <