From 6b2c9b7c66d9b9648213d3a7f071d0f720048e46 Mon Sep 17 00:00:00 2001 From: Ondrej Zajicek Date: Thu, 27 Apr 2023 17:09:00 +0200 Subject: [PATCH 1/6] Conf: Fix symbol lookup The symbol table used just symbol name as a key, and used a trick with active flag to find symbols in active scopes with one hash table lookup. The disadvantage is that it can degenerate to O(n) for negative queries in situations where are many symbols with the same name in different scopes. Thanks to Yanko Kaneti for the bugreport. --- conf/cf-lex.l | 22 ++++++++++++---------- conf/conf.h | 4 +++- lib/hash.h | 2 +- 3 files changed, 16 insertions(+), 12 deletions(-) diff --git a/conf/cf-lex.l b/conf/cf-lex.l index ceedee8a..0a02dd73 100644 --- a/conf/cf-lex.l +++ b/conf/cf-lex.l @@ -73,10 +73,10 @@ static uint cf_hash(const byte *c); #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, n->scope #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_FN(k,s) cf_hash(k) ^ ptr_hash(s) #define SYM_ORDER 6 /* Initial */ #define SYM_REHASH sym_rehash @@ -602,29 +602,31 @@ cf_new_symbol(const byte *c) } /** - * cf_find_symbol - find a symbol by name + * cf_find_local_symbol - find a symbol by name * @cfg: specificed config + * @scope: specified symbol scope * @c: symbol name * * This functions searches the symbol table in the config @cfg for a symbol of - * given name. First it examines the current scope, then the second recent one + * given name. First it examines the scope @scope, then the parent scope * 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 * -cf_find_symbol(const struct config *cfg, const byte *c) +cf_find_local_symbol(const struct config *cfg, const struct sym_scope *scope, const byte *c) { struct symbol *s; - if (cfg->sym_hash.data && - (s = HASH_FIND(cfg->sym_hash, SYM, c, 1))) - return s; + if (cfg->sym_hash.data) + for (; scope; scope = scope->next) + if (s = HASH_FIND(cfg->sym_hash, SYM, c, scope)) + 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))) + (s = HASH_FIND(cfg->fallback->sym_hash, SYM, c, cfg->fallback->root_scope))) return s; return NULL; @@ -642,7 +644,7 @@ cf_find_symbol(const struct config *cfg, const byte *c) struct symbol * cf_get_symbol(const byte *c) { - return cf_find_symbol(new_config, c) ?: cf_new_symbol(c); + return cf_find_local_symbol(new_config, conf_this_scope, c) ?: cf_new_symbol(c); } /** diff --git a/conf/conf.h b/conf/conf.h index b409750e..cb47e7d0 100644 --- a/conf/conf.h +++ b/conf/conf.h @@ -187,7 +187,9 @@ int cf_lex(void); void cf_lex_init(int is_cli, struct config *c); void cf_lex_unwind(void); -struct symbol *cf_find_symbol(const struct config *cfg, const byte *c); +struct symbol *cf_find_local_symbol(const struct config *cfg, const struct sym_scope *scope, const byte *c); +static inline struct symbol *cf_find_symbol(const struct config *cfg, const byte *c) +{ return cf_find_local_symbol(cfg, cfg->root_scope, c); } struct symbol *cf_get_symbol(const byte *c); struct symbol *cf_default_name(char *template, int *counter); diff --git a/lib/hash.h b/lib/hash.h index 8febb33f..b30f7830 100644 --- a/lib/hash.h +++ b/lib/hash.h @@ -237,7 +237,7 @@ mem_hash(const void *p, uint s) } static inline uint -ptr_hash(void *ptr) +ptr_hash(const void *ptr) { uintptr_t p = (uintptr_t) ptr; return p ^ (p << 8) ^ (p >> 16); From 6e908775cba9efdae09f06a630cc7777282e2715 Mon Sep 17 00:00:00 2001 From: Ondrej Zajicek Date: Thu, 27 Apr 2023 18:20:49 +0200 Subject: [PATCH 2/6] Conf: Improve handling of keywords For whatever reason, parser allocated a symbol for every parsed keyword in each scope. That wasted time and memory. The effect is worsened with recent changes allowing local scopes, so keywords often promote soft scopes (with no symbols) to real scopes. Do not allocate a symbol for a keyword. Take care of keywords that could be promoted to symbols (kw_sym) and do it explicitly. --- conf/cf-lex.l | 17 +++++++++++++---- conf/conf.h | 3 +++ conf/confbase.Y | 4 +++- conf/gen_parser.m4 | 4 ++-- nest/config.Y | 2 ++ proto/bgp/config.Y | 2 +- 6 files changed, 24 insertions(+), 8 deletions(-) diff --git a/conf/cf-lex.l b/conf/cf-lex.l index 0a02dd73..9555949d 100644 --- a/conf/cf-lex.l +++ b/conf/cf-lex.l @@ -601,6 +601,10 @@ cf_new_symbol(const byte *c) return s; } +struct symbol * +cf_symbol_from_keyword(const struct keyword *kw) +{ return cf_new_symbol(kw->name); } + /** * cf_find_local_symbol - find a symbol by name * @cfg: specificed config @@ -692,18 +696,23 @@ static enum yytokentype cf_lex_symbol(const char *data) { /* Have we defined such a symbol? */ - struct symbol *sym = cf_get_symbol(data); - cf_lval.s = sym; + struct symbol *sym = cf_find_local_symbol(new_config, conf_this_scope, data); - if (sym->class != SYM_VOID) + if (sym && (sym->class != SYM_VOID)) + { + cf_lval.s = sym; return CF_SYM_KNOWN; + } /* Is it a keyword? */ struct keyword *k = HASH_FIND(kw_hash, KW, data); if (k) { if (k->value > 0) + { + cf_lval.kw = k; return k->value; + } else { cf_lval.i = -k->value; @@ -712,7 +721,7 @@ cf_lex_symbol(const char *data) } /* OK, undefined symbol */ - cf_lval.s = sym; + cf_lval.s = cf_new_symbol(data); return CF_SYM_UNDEFINED; } diff --git a/conf/conf.h b/conf/conf.h index cb47e7d0..d40f955e 100644 --- a/conf/conf.h +++ b/conf/conf.h @@ -191,6 +191,9 @@ struct symbol *cf_find_local_symbol(const struct config *cfg, const struct sym_s static inline struct symbol *cf_find_symbol(const struct config *cfg, const byte *c) { return cf_find_local_symbol(cfg, cfg->root_scope, c); } +struct keyword; +struct symbol *cf_symbol_from_keyword(const struct keyword *kw); + 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); diff --git a/conf/confbase.Y b/conf/confbase.Y index 1d5738ff..3e8f5807 100644 --- a/conf/confbase.Y +++ b/conf/confbase.Y @@ -61,6 +61,7 @@ CF_DECLS net_addr net; net_addr *net_ptr; struct symbol *s; + struct keyword *kw; const char *t; struct rtable_config *r; struct channel_config *cc; @@ -117,6 +118,7 @@ CF_DECLS %type text opttext %type symbol +%type kw_sym %nonassoc PREFIX_DUMMY %left AND OR @@ -172,7 +174,7 @@ expr_us: | expr US { $$ = $1 US_; } ; -symbol: CF_SYM_UNDEFINED | CF_SYM_KNOWN ; +symbol: CF_SYM_UNDEFINED | CF_SYM_KNOWN | kw_sym { $$ = cf_symbol_from_keyword($1); } ; /* Switches */ diff --git a/conf/gen_parser.m4 b/conf/gen_parser.m4 index af4b1455..7a2a9de4 100644 --- a/conf/gen_parser.m4 +++ b/conf/gen_parser.m4 @@ -29,9 +29,9 @@ m4_define(CF_END, `m4_divert(-1)') m4_define(CF_itera, `m4_ifelse($#, 1, [[CF_iter($1)]], [[CF_iter($1)[[]]CF_itera(m4_shift($@))]])') m4_define(CF_iterate, `m4_define([[CF_iter]], m4_defn([[$1]]))CF_itera($2)') -# Keywords act as untyped %token +# Keywords act as %token 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 +m4_define(CF_KEYWORDS, `m4_define([[CF_toks]],[[]])CF_iterate([[CF_keywd]], [[$@]])m4_ifelse(CF_toks,,,%token[[]]CF_toks )DNL') # CLI commands diff --git a/nest/config.Y b/nest/config.Y index e78350ca..c83c715b 100644 --- a/nest/config.Y +++ b/nest/config.Y @@ -154,6 +154,8 @@ CF_ENUM_PX(T_ENUM_AF, AF_, AFI_, IPV4, IPV6) CF_GRAMMAR +kw_sym: MIN MAX ; + /* Setting of router ID */ conf: rtrid ; diff --git a/proto/bgp/config.Y b/proto/bgp/config.Y index 013d14af..218e0d04 100644 --- a/proto/bgp/config.Y +++ b/proto/bgp/config.Y @@ -46,7 +46,7 @@ CF_KEYWORDS(CEASE, PREFIX, LIMIT, HIT, ADMINISTRATIVE, SHUTDOWN, RESET, PEER, CF_GRAMMAR /* Workaround for collisions between keywords and symbols */ -symbol: ROLE | PEER | PROVIDER | CUSTOMER | RS_SERVER | RS_CLIENT ; +kw_sym: ROLE | PEER | PROVIDER | CUSTOMER | RS_SERVER | RS_CLIENT ; proto: bgp_proto '}' ; From 2d988f71a5c3caf3b56426a45d4d234c4fd4fce7 Mon Sep 17 00:00:00 2001 From: Ondrej Zajicek Date: Mon, 21 Aug 2023 04:17:21 +0200 Subject: [PATCH 3/6] Nest: Use generic rte_announce() also for import tables Remove special rte_announce_in(), so we can use generic rte_announce() for bot feed and notifications. --- nest/rt-table.c | 65 ++++++++++++++++++++++++++++++------------------- 1 file changed, 40 insertions(+), 25 deletions(-) diff --git a/nest/rt-table.c b/nest/rt-table.c index e4b27814..03f03fc9 100644 --- a/nest/rt-table.c +++ b/nest/rt-table.c @@ -2148,11 +2148,11 @@ rt_setup(pool *pp, struct rtable_config *cf) init_list(&t->flowspec_links); init_list(&t->subscribers); + hmap_init(&t->id_map, p, 1024); + hmap_set(&t->id_map, 0); + if (!(t->internal = cf->internal)) { - hmap_init(&t->id_map, p, 1024); - hmap_set(&t->id_map, 0); - t->rt_event = ev_new_init(p, rt_event, t); t->prune_timer = tm_new_init(p, rt_prune_timer, t, 0, 0); t->last_rt_change = t->gc_time = current_time(); @@ -3107,25 +3107,15 @@ rte_update_in(struct channel *c, const net_addr *n, rte *new, struct rte_src *sr /* Remove the old rte */ *pos = old->next; - rte_free_quick(old); tab->rt_count--; - break; } - if (!new) - { - if (!old) - goto drop_withdraw; - - if (!net->routes) - fib_delete(&tab->fib, net); - - return 1; - } + if (!old && !new) + goto drop_withdraw; struct channel_limit *l = &c->rx_limit; - if (l->action && !old) + if (l->action && !old && new) { if (tab->rt_count >= l->limit) channel_notify_limit(c, l, PLD_RX, tab->rt_count); @@ -3140,15 +3130,40 @@ rte_update_in(struct channel *c, const net_addr *n, rte *new, struct rte_src *sr } } - /* Insert the new rte */ - rte *e = rte_do_cow(new); - e->flags |= REF_COW; - e->net = net; - e->sender = c; - e->lastmod = current_time(); - e->next = *pos; - *pos = e; - tab->rt_count++; + if (new) + { + /* Insert the new rte */ + rte *e = rte_do_cow(new); + e->flags |= REF_COW; + e->net = net; + e->sender = c; + e->lastmod = current_time(); + e->next = *pos; + *pos = new = e; + tab->rt_count++; + + if (!old) + { + new->id = hmap_first_zero(&tab->id_map); + hmap_set(&tab->id_map, new->id); + } + else + new->id = old->id; + } + + rte_announce(tab, RA_ANY, net, new, old, NULL, NULL); + + if (old) + { + if (!new) + hmap_clear(&tab->id_map, old->id); + + rte_free_quick(old); + } + + if (!net->routes) + fib_delete(&tab->fib, net); + return 1; drop_update: From b20b6a9ad204f2648ed3d62720435bb21dfb947c Mon Sep 17 00:00:00 2001 From: Maria Matejka Date: Wed, 11 Oct 2023 22:23:34 +0200 Subject: [PATCH 4/6] BGP/BMP: Moved temporary allocation checks to the freeing functions --- proto/bgp/attrs.c | 11 +++++++++ proto/bgp/bgp.h | 2 +- proto/bgp/packets.c | 58 ++++++++++++++------------------------------- 3 files changed, 30 insertions(+), 41 deletions(-) diff --git a/proto/bgp/attrs.c b/proto/bgp/attrs.c index de45cae0..adc201a7 100644 --- a/proto/bgp/attrs.c +++ b/proto/bgp/attrs.c @@ -1631,6 +1631,9 @@ bgp_defer_bucket(struct bgp_channel *c, struct bgp_bucket *b) void bgp_withdraw_bucket(struct bgp_channel *c, struct bgp_bucket *b) { + if (b->bmp) + return; + struct bgp_proto *p = (void *) c->c.proto; struct bgp_bucket *wb = bgp_get_withdraw_bucket(c); @@ -1710,6 +1713,14 @@ bgp_get_prefix(struct bgp_channel *c, net_addr *net, u32 path_id) void bgp_free_prefix(struct bgp_channel *c, struct bgp_prefix *px) { + /* BMP hack */ + if ( + !NODE_VALID(px->buck_node.prev) && + !NODE_VALID(px->buck_node.next) && + !SKIP_BACK(struct bgp_bucket, prefixes.head, px->buck_node.prev)->bmp + ) + return; + rem_node(&px->buck_node); HASH_REMOVE2(c->prefix_hash, PXH, c->pool, px); diff --git a/proto/bgp/bgp.h b/proto/bgp/bgp.h index 324df43c..73686545 100644 --- a/proto/bgp/bgp.h +++ b/proto/bgp/bgp.h @@ -413,6 +413,7 @@ struct bgp_bucket { struct bgp_bucket *next; /* Node in bucket hash table */ list prefixes; /* Prefixes in this bucket (struct bgp_prefix) */ u32 hash; /* Hash over extended attributes */ + u32 bmp:1; /* Temporary bucket for BMP encoding */ ea_list eattrs[0]; /* Per-bucket extended attributes */ }; @@ -439,7 +440,6 @@ struct bgp_write_state { int as4_session; int add_path; int mpls; - int sham; eattr *mp_next_hop; const adata *mpls_labels; diff --git a/proto/bgp/packets.c b/proto/bgp/packets.c index 6b728b4e..6b11eaf7 100644 --- a/proto/bgp/packets.c +++ b/proto/bgp/packets.c @@ -1575,10 +1575,7 @@ bgp_encode_nlri_ip4(struct bgp_write_state *s, struct bgp_bucket *buck, byte *bu memcpy(pos, &a, b); ADVANCE(pos, size, b); - if (!s->sham) - bgp_free_prefix(s->channel, px); - else - rem_node(&px->buck_node); + bgp_free_prefix(s->channel, px); } return pos - buf; @@ -1663,10 +1660,7 @@ bgp_encode_nlri_ip6(struct bgp_write_state *s, struct bgp_bucket *buck, byte *bu memcpy(pos, &a, b); ADVANCE(pos, size, b); - if (!s->sham) - bgp_free_prefix(s->channel, px); - else - rem_node(&px->buck_node); + bgp_free_prefix(s->channel, px); } return pos - buf; @@ -1754,10 +1748,7 @@ bgp_encode_nlri_vpn4(struct bgp_write_state *s, struct bgp_bucket *buck, byte *b memcpy(pos, &a, b); ADVANCE(pos, size, b); - if (!s->sham) - bgp_free_prefix(s->channel, px); - else - rem_node(&px->buck_node); + bgp_free_prefix(s->channel, px); } return pos - buf; @@ -1854,10 +1845,7 @@ bgp_encode_nlri_vpn6(struct bgp_write_state *s, struct bgp_bucket *buck, byte *b memcpy(pos, &a, b); ADVANCE(pos, size, b); - if (!s->sham) - bgp_free_prefix(s->channel, px); - else - rem_node(&px->buck_node); + bgp_free_prefix(s->channel, px); } return pos - buf; @@ -1944,10 +1932,7 @@ bgp_encode_nlri_flow4(struct bgp_write_state *s, struct bgp_bucket *buck, byte * memcpy(pos, net->data, flen); ADVANCE(pos, size, flen); - if (!s->sham) - bgp_free_prefix(s->channel, px); - else - rem_node(&px->buck_node); + bgp_free_prefix(s->channel, px); } return pos - buf; @@ -2039,10 +2024,7 @@ bgp_encode_nlri_flow6(struct bgp_write_state *s, struct bgp_bucket *buck, byte * memcpy(pos, net->data, flen); ADVANCE(pos, size, flen); - if (!s->sham) - bgp_free_prefix(s->channel, px); - else - rem_node(&px->buck_node); + bgp_free_prefix(s->channel, px); } return pos - buf; @@ -2286,8 +2268,7 @@ bgp_create_ip_reach(struct bgp_write_state *s, struct bgp_bucket *buck, byte *bu if (la < 0) { /* Attribute list too long */ - if (!s->sham) - bgp_withdraw_bucket(s->channel, buck); + bgp_withdraw_bucket(s->channel, buck); return NULL; } @@ -2334,8 +2315,7 @@ bgp_create_mp_reach(struct bgp_write_state *s, struct bgp_bucket *buck, byte *bu if (la < 0) { /* Attribute list too long */ - if (!s->sham) - bgp_withdraw_bucket(s->channel, buck); + bgp_withdraw_bucket(s->channel, buck); return NULL; } @@ -2427,9 +2407,6 @@ bgp_create_update_bmp(struct bgp_channel *c, byte *buf, struct bgp_bucket *buck, byte *res = NULL; /* FIXME: must be a bit shorter */ - struct lp_state tmpp; - lp_save(tmp_linpool, &tmpp); - struct bgp_caps *peer = p->conn->remote_caps; const struct bgp_af_caps *rem = bgp_find_af_caps(peer, c->afi); @@ -2441,7 +2418,6 @@ bgp_create_update_bmp(struct bgp_channel *c, byte *buf, struct bgp_bucket *buck, .as4_session = 1, .add_path = c->add_path_rx, .mpls = c->desc->mpls, - .sham = 1, }; if (!update) @@ -2457,8 +2433,6 @@ bgp_create_update_bmp(struct bgp_channel *c, byte *buf, struct bgp_bucket *buck, bgp_create_mp_reach(&s, buck, buf, end); } - lp_restore(tmp_linpool, &tmpp); - return res; } @@ -2484,17 +2458,19 @@ bgp_bmp_encode_rte(struct bgp_channel *c, byte *buf, const net_addr *n, uint bucket_size = sizeof(struct bgp_bucket) + ea_size; uint prefix_size = sizeof(struct bgp_prefix) + n->length; - /* Sham bucket */ - struct bgp_bucket *b = alloca(bucket_size); - *b = (struct bgp_bucket) { }; + struct lp_state tmpp; + lp_save(tmp_linpool, &tmpp); + + /* Temporary bucket */ + struct bgp_bucket *b = tmp_allocz(bucket_size); + b->bmp = 1; init_list(&b->prefixes); if (attrs) memcpy(b->eattrs, attrs, ea_size); - /* Sham prefix */ - struct bgp_prefix *px = alloca(prefix_size); - *px = (struct bgp_prefix) { }; + /* Temporary prefix */ + struct bgp_prefix *px = tmp_allocz(prefix_size); px->path_id = src->private_id; net_copy(px->net, n); add_tail(&b->prefixes, &px->buck_node); @@ -2504,6 +2480,8 @@ bgp_bmp_encode_rte(struct bgp_channel *c, byte *buf, const net_addr *n, if (end) bgp_bmp_prepare_bgp_hdr(buf, end - buf, PKT_UPDATE); + lp_restore(tmp_linpool, &tmpp); + return end; } From 4972590ea5cc9fb5ff20b28227badd6338830649 Mon Sep 17 00:00:00 2001 From: Maria Matejka Date: Thu, 12 Oct 2023 09:29:09 +0200 Subject: [PATCH 5/6] BGP: dropped bgp_fix_attr_flags() (useless remnant from previous versions) --- proto/bgp/attrs.c | 8 -------- proto/bgp/bgp.h | 2 -- 2 files changed, 10 deletions(-) diff --git a/proto/bgp/attrs.c b/proto/bgp/attrs.c index adc201a7..457a8b15 100644 --- a/proto/bgp/attrs.c +++ b/proto/bgp/attrs.c @@ -1151,14 +1151,6 @@ bgp_attr_known(uint code) return (code < ARRAY_SIZE(bgp_attr_table)) && bgp_attr_table[code].name; } -void bgp_fix_attr_flags(ea_list *attrs) -{ - for (u8 i = 0; i < attrs->count; i++) - { - attrs->attrs[i].flags = bgp_attr_table[EA_ID(attrs->attrs[i].id)].flags; - } -} - /* * Attribute export */ diff --git a/proto/bgp/bgp.h b/proto/bgp/bgp.h index 73686545..ceba958c 100644 --- a/proto/bgp/bgp.h +++ b/proto/bgp/bgp.h @@ -556,8 +556,6 @@ void bgp_store_error(struct bgp_proto *p, struct bgp_conn *c, u8 class, u32 code void bgp_stop(struct bgp_proto *p, int subcode, byte *data, uint len); const char *bgp_format_role_name(u8 role); -void bgp_fix_attr_flags(ea_list *attrs); - static inline int rte_resolvable(rte *rt) { From e2f08c382a2adbbbd94c5cd7d996ce9175e0fb9c Mon Sep 17 00:00:00 2001 From: Maria Matejka Date: Thu, 12 Oct 2023 10:57:55 +0200 Subject: [PATCH 6/6] BGP/BMP: fixed build with no bmp at all --- proto/bgp/bgp.c | 7 +++++++ proto/bgp/packets.c | 2 ++ 2 files changed, 9 insertions(+) diff --git a/proto/bgp/bgp.c b/proto/bgp/bgp.c index a6e9cf83..7ae688f5 100644 --- a/proto/bgp/bgp.c +++ b/proto/bgp/bgp.c @@ -125,7 +125,9 @@ #include "lib/string.h" #include "bgp.h" +#ifdef CONFIG_BMP #include "proto/bmp/bmp.h" +#endif static list STATIC_LIST_INIT(bgp_sockets); /* Global list of listening sockets */ @@ -692,8 +694,11 @@ bgp_conn_enter_established_state(struct bgp_conn *conn) bgp_conn_set_state(conn, BS_ESTABLISHED); proto_notify_state(&p->p, PS_UP); + +#ifdef CONFIG_BMP bmp_peer_up(p, conn->local_open_msg, conn->local_open_length, conn->remote_open_msg, conn->remote_open_length); +#endif } static void @@ -706,9 +711,11 @@ bgp_conn_leave_established_state(struct bgp_conn *conn, struct bgp_proto *p) if (p->p.proto_state == PS_UP) bgp_stop(p, 0, NULL, 0); +#ifdef CONFIG_BMP bmp_peer_down(p, p->last_error_class, conn->notify_code, conn->notify_subcode, conn->notify_data, conn->notify_size); +#endif } void diff --git a/proto/bgp/packets.c b/proto/bgp/packets.c index 6b11eaf7..ff51e309 100644 --- a/proto/bgp/packets.c +++ b/proto/bgp/packets.c @@ -26,7 +26,9 @@ #include "nest/cli.h" #include "bgp.h" +#ifdef CONFIG_BMP #include "proto/bmp/bmp.h" +#endif #define BGP_RR_REQUEST 0