0
0
mirror of https://gitlab.nic.cz/labs/bird.git synced 2024-12-22 09:41:54 +00:00

Protocol and BGP state information cleanup and fixes

There were some nasty problems with deferred protocol state updates and
race conditions on BGP startup, shutdown, and also with referencing the
cached states.

Now it looks fixed.
This commit is contained in:
Maria Matejka 2024-11-22 14:49:13 +01:00
parent e654efd354
commit 2956faf5a6
6 changed files with 251 additions and 148 deletions

View File

@ -11,7 +11,7 @@
#define _BIRD_LIB_ROUTE_H_ #define _BIRD_LIB_ROUTE_H_
#undef RT_SOURCE_DEBUG #undef RT_SOURCE_DEBUG
#define EA_FREE_DEBUG #undef EA_FREE_DEBUG
#include "lib/type.h" #include "lib/type.h"
#include "lib/rcu.h" #include "lib/rcu.h"

View File

@ -242,7 +242,17 @@ proto_add_channel(struct proto *p, struct channel_config *cf)
init_list(&c->roa_subscriptions); init_list(&c->roa_subscriptions);
/* Announcing existence of the channel */ /* Create the channel information ea_list */
struct ea_list *ca = NULL;
ea_set_attr(&ca, EA_LITERAL_STORE_STRING(&ea_name, 0, c->name));
ea_set_attr(&ca, EA_LITERAL_EMBEDDED(&ea_proto_id, 0, c->proto->id));
ea_set_attr(&ca, EA_LITERAL_EMBEDDED(&ea_channel_id, 0, c->id));
ea_set_attr(&ca, EA_LITERAL_EMBEDDED(&ea_in_keep, 0, c->in_keep));
ea_set_attr(&ca, EA_LITERAL_STORE_PTR(&ea_rtable, 0, c->table));
/* Announcing existence of the channel
* TODO: make this an actual notification */
PST_LOCKED(ts) PST_LOCKED(ts)
{ {
/* Allocating channel ID */ /* Allocating channel ID */
@ -260,30 +270,20 @@ proto_add_channel(struct proto *p, struct channel_config *cf)
ts->length_channels = ts->length_channels * 2; ts->length_channels = ts->length_channels * 2;
} }
/* Create the actual channel information */
struct ea_list *ca = NULL;
ea_set_attr(&ca, EA_LITERAL_STORE_STRING(&ea_name, 0, c->name));
ea_set_attr(&ca, EA_LITERAL_EMBEDDED(&ea_proto_id, 0, c->proto->id));
ea_set_attr(&ca, EA_LITERAL_EMBEDDED(&ea_channel_id, 0, c->id));
ea_set_attr(&ca, EA_LITERAL_EMBEDDED(&ea_in_keep, 0, c->in_keep));
ea_set_attr(&ca, EA_LITERAL_STORE_PTR(&ea_rtable, 0, c->table));
ASSERT_DIE(c->id < ts->length_channels); ASSERT_DIE(c->id < ts->length_channels);
ASSERT_DIE(ts->channels[c->id] == NULL); ASSERT_DIE(ts->channels[c->id] == NULL);
/* Set the channel info */
ts->channels[c->id] = ea_lookup_slow(ca, 0, EALS_IN_TABLE); ts->channels[c->id] = ea_lookup_slow(ca, 0, EALS_IN_TABLE);
/* Update channel list in protocol state */
ASSERT_DIE(c->proto->id < ts->length_states);
ea_set_attr(&p->ea_state,
EA_LITERAL_DIRECT_ADATA(&ea_proto_channel_list, 0, int_set_add(
tmp_linpool, ea_get_adata(p->ea_state, &ea_proto_channel_list), c->id)));
ea_lookup(p->ea_state, 0, EALS_CUSTOM);
proto_announce_state_locked(ts, c->proto, p->ea_state);
} }
/* Update channel list in protocol state */
struct ea_list *pes = p->ea_state;
ea_set_attr(&pes,
EA_LITERAL_DIRECT_ADATA(&ea_proto_channel_list, 0, int_set_add(
tmp_linpool, ea_get_adata(pes, &ea_proto_channel_list), c->id)));
proto_announce_state_later(c->proto, pes);
CALL(c->class->init, c, cf); CALL(c->class->init, c, cf);
add_tail(&p->channels, &c->n); add_tail(&p->channels, &c->n);
@ -300,13 +300,21 @@ proto_remove_channel(struct proto *p UNUSED, struct channel *c)
CD(c, "Removed", c->name); CD(c, "Removed", c->name);
ea_set_attr(&p->ea_state, struct ea_list *pes = p->ea_state;
ea_set_attr(&pes,
EA_LITERAL_DIRECT_ADATA(&ea_proto_channel_list, 0, int_set_del( EA_LITERAL_DIRECT_ADATA(&ea_proto_channel_list, 0, int_set_del(
tmp_linpool, ea_get_adata(p->ea_state, &ea_proto_channel_list), c->id))); tmp_linpool, ea_get_adata(p->ea_state, &ea_proto_channel_list), c->id)));
ea_lookup(p->ea_state, 0, EALS_CUSTOM); proto_announce_state_later(c->proto, pes);
proto_announce_state(c->proto, p->ea_state);
/*
* This may cause trouble if some protocol flaps really really fast,
* like trying to read channel info of a nonexistent channel,
* but until something like this actually happens in production,
* we are going to fix this anyway.
*
* (Famous last words by Maria.)
*/
PST_LOCKED(ts) PST_LOCKED(ts)
{ {
ASSERT_DIE(c->id < ts->length_channels); ASSERT_DIE(c->id < ts->length_channels);
@ -1319,6 +1327,9 @@ proto_new(struct proto_config *cf)
p->mrtdump = cf->mrtdump; p->mrtdump = cf->mrtdump;
p->name = cf->name; p->name = cf->name;
p->proto = cf->protocol; p->proto = cf->protocol;
p->proto_state = PS_DOWN_XX;
p->last_state_change = current_time();
p->net_type = cf->net_type; p->net_type = cf->net_type;
p->disabled = cf->disabled; p->disabled = cf->disabled;
p->hash_key = random_u32(); p->hash_key = random_u32();
@ -1344,7 +1355,7 @@ proto_new(struct proto_config *cf)
init_list(&p->channels); init_list(&p->channels);
/* /*
Making first version of proto eatters. Initial version of protocol state.
*/ */
struct ea_list *state = NULL; struct ea_list *state = NULL;
@ -1355,7 +1366,7 @@ proto_new(struct proto_config *cf)
ea_set_attr(&state, EA_LITERAL_EMBEDDED(&ea_proto_id, 0, p->id)); ea_set_attr(&state, EA_LITERAL_EMBEDDED(&ea_proto_id, 0, p->id));
ea_set_attr(&state, EA_LITERAL_STORE_ADATA(&ea_proto_channel_list, 0, NULL, 0)); ea_set_attr(&state, EA_LITERAL_STORE_ADATA(&ea_proto_channel_list, 0, NULL, 0));
proto_announce_state(p, state); proto_announce_state_later(p, state);
return p; return p;
} }
@ -1367,11 +1378,11 @@ proto_init(struct proto_config *c, struct proto *after)
struct proto *p = pr->init(c); struct proto *p = pr->init(c);
p->loop = &main_birdloop; p->loop = &main_birdloop;
p->proto_state = PS_DOWN_XX;
p->last_state_change = current_time();
p->vrf = c->vrf; p->vrf = c->vrf;
proto_add_after(&global_proto_list, p, after); proto_add_after(&global_proto_list, p, after);
proto_announce_state(p, p->ea_state);
p->event = ev_new_init(proto_pool, proto_event, p); p->event = ev_new_init(proto_pool, proto_event, p);
PD(p, "Initializing%s", p->disabled ? " [disabled]" : ""); PD(p, "Initializing%s", p->disabled ? " [disabled]" : "");
@ -1682,6 +1693,7 @@ protos_do_commit(struct config *new, struct config *old, int type)
{ {
OBSREF_CLEAR(p->global_config); OBSREF_CLEAR(p->global_config);
OBSREF_SET(p->global_config, new); OBSREF_SET(p->global_config, new);
proto_announce_state(p, p->ea_state);
PROTO_LEAVE_FROM_MAIN(proto_loop); PROTO_LEAVE_FROM_MAIN(proto_loop);
continue; continue;
} }
@ -1722,6 +1734,7 @@ protos_do_commit(struct config *new, struct config *old, int type)
} }
p->reconfiguring = 1; p->reconfiguring = 1;
proto_announce_state(p, p->ea_state);
PROTO_LEAVE_FROM_MAIN(proto_loop); PROTO_LEAVE_FROM_MAIN(proto_loop);
proto_rethink_goal(p); proto_rethink_goal(p);
@ -1798,12 +1811,13 @@ proto_rethink_goal(struct proto *p)
struct proto_config *nc = p->cf_new; struct proto_config *nc = p->cf_new;
struct proto *after = p->n.prev; struct proto *after = p->n.prev;
proto_announce_state(p, NULL);
DBG("%s has shut down for reconfiguration\n", p->name); DBG("%s has shut down for reconfiguration\n", p->name);
p->cf->proto = NULL; p->cf->proto = NULL;
OBSREF_CLEAR(p->global_config); OBSREF_CLEAR(p->global_config);
proto_remove_channels(p); proto_remove_channels(p);
proto_announce_state(p, NULL);
proto_rem_node(&global_proto_list, p); proto_rem_node(&global_proto_list, p);
rfree(p->event); rfree(p->event);
mb_free(p->message); mb_free(p->message);
@ -2421,19 +2435,18 @@ proto_do_down(struct proto *p)
void void
proto_notify_state(struct proto *p, uint state) proto_notify_state(struct proto *p, uint state)
{ {
ASSERT_DIE(birdloop_inside(p->loop));
uint ps = p->proto_state; uint ps = p->proto_state;
DBG("%s reporting state transition %s -> %s\n", p->name, p_states[ps], p_states[state]); DBG("%s reporting state transition %s -> %s\n", p->name, p_states[ps], p_states[state]);
/* If nothing happened, announce possible pending extended states immediately */
if (state == ps) if (state == ps)
return; return proto_announce_state(p, p->ea_state);
p->proto_state = state; p->proto_state = state;
p->last_state_change = current_time(); p->last_state_change = current_time();
ea_set_attr(&p->ea_state, EA_LITERAL_EMBEDDED(&ea_state, 0, p->proto_state));
ea_lookup(p->ea_state, 0, EALS_CUSTOM);
proto_announce_state(p, p->ea_state);
switch (state) switch (state)
{ {
case PS_START: case PS_START:
@ -2484,6 +2497,11 @@ proto_notify_state(struct proto *p, uint state)
bug("%s: Invalid state %d", p->name, ps); bug("%s: Invalid state %d", p->name, ps);
} }
ea_list *pes = p->ea_state;
ea_set_attr(&pes, EA_LITERAL_EMBEDDED(&ea_state, 0, p->proto_state));
ea_set_attr(&pes, EA_LITERAL_STORE_ADATA(&ea_last_modified, 0, &p->last_state_change, sizeof(btime)));
proto_announce_state(p, pes);
proto_log_state_change(p); proto_log_state_change(p);
} }
@ -2921,18 +2939,23 @@ proto_iterate_named(struct symbol *sym, struct protocol *proto, struct proto *ol
} }
static void static void
proto_journal_item_cleanup_(ea_list *proto_attr, ea_list *old_attr) proto_journal_item_cleanup_(bool withdrawal, ea_list *old_attr)
{ {
/* The old attrs can be finally unref'd */
ea_free_later(old_attr); ea_free_later(old_attr);
if (!proto_attr) if (!withdrawal)
return;
/* The protocol itself is finished, cleanup its ID
* The protocol's structure may be already gone,
* the only thing left is the ID and the old attributes. */
PST_LOCKED(tp)
{ {
PST_LOCKED(tp) u32 id = ea_get_int(old_attr, &ea_proto_id, 0);
{ ASSERT_DIE(id);
int p_id = ea_get_int(old_attr, &ea_proto_id, 0); ASSERT_DIE(tp->states[id] == NULL);
hmap_clear(&tp->proto_id_map, p_id); hmap_clear(&tp->proto_id_map, id);
tp->states[p_id] = NULL;
}
} }
} }
@ -2941,49 +2964,103 @@ proto_journal_item_cleanup(struct lfjour * journal UNUSED, struct lfjour_item *i
{ {
/* Called after a journal update was has been read. */ /* Called after a journal update was has been read. */
struct proto_pending_update *pupdate = SKIP_BACK(struct proto_pending_update, li, i); struct proto_pending_update *pupdate = SKIP_BACK(struct proto_pending_update, li, i);
proto_journal_item_cleanup_(pupdate->proto_attr, pupdate->old_proto_attr); proto_journal_item_cleanup_(!pupdate->new, pupdate->old);
} }
/*
* Protocol state announcement.
*
* The authoritative protocol state is always stored in ts->states[p->id]
* and it holds a reference. But sometimes it's too clumsy to announce all
* protocol changes happening in a fast succession, so there is a
* state-to-be-announced stored in the protocol itself, in p->ea_state.
* That one is also a reference.
*
* The usage pattern is simple. First, you need to have a local ea_list copy.
* ea_list *pes = p->ea_state;
*
* Then you update the state.
* ea_set_attr(&pes, EA_LITERAL_...);
* (possibly more of these)
*
* And finally, you queue the change for announcement.
* proto_announce_state_later(p, pes);
*
* Alternatively, if you need immediate announcement and no table is locked.
* proto_announce_state(p, pes);
*
* In all cases, the p->ea_state is updated immediately, and the public state
* is either updated directly (involving a table lock!) or the update is
* deferred after the current task ends.
*
* Never update p->ea_state from outside these functions.
* Also never access p->ea_state from outside the protocol context.
* It's a local copy for the protocol itself. From outside, always read the
* public state table.
*
* Also no explicit referencing or EAs is needed outside these functions,
* the reference lifecycle is complete here.
*/
struct proto_announce_state_deferred {
struct deferred_call dc;
struct proto *p;
};
void void
proto_announce_state_locked(struct proto_state_table_private* ts, struct proto *p, ea_list *attr) proto_announce_state_locked(struct proto_state_table_private* ts, struct proto *p, ea_list *new_state)
{ {
/* ASSERT_DIE(birdloop_inside(p->loop));
Should be called each time one (or more) variables tracked in proto eattrs changes.
Changes proto eattrs and activates journal.
*/
ea_set_attr(&attr, EA_LITERAL_STORE_ADATA(&ea_last_modified, 0, &p->last_state_change, sizeof(btime)));
attr = ea_lookup(attr, 0, EALS_CUSTOM); /* Cancel the previous deferred announcement */
if (p->deferred_state_announcement)
{
p->deferred_state_announcement->p = NULL;
p->deferred_state_announcement = NULL;
}
/* First we update the state-to-be-stored */
if (new_state != p->ea_state)
{
if (p->ea_state)
ea_free_later(p->ea_state);
p->ea_state = new_state ? ea_lookup(new_state, 0, EALS_IN_TABLE) : NULL;
}
/* Then we check the public state */
ASSERT_DIE(p->id < ts->length_states); ASSERT_DIE(p->id < ts->length_states);
ea_list *old_attr = ts->states[p->id]; ea_list *old_state = ts->states[p->id];
if (attr == old_attr) /* Nothing has changed? */
if (p->ea_state == old_state)
return;
/* Set the new state */
ts->states[p->id] = p->ea_state ? ea_ref(p->ea_state) : NULL;
/* Announce the new state */
struct lfjour_item *li = lfjour_push_prepare(&proto_state_table_pub.journal);
if (!li)
{ {
/* Nothing has changed */ /* No reader, cleanup immediately
ea_free_later(attr); * There is another version of this code in
* proto_journal_item_cleanup_() but it must
* be kept separated because of locking. */
ea_free_later(old_state);
/* Protocol ID cleanup */
if (!p->ea_state)
hmap_clear(&ts->proto_id_map, p->id);
return; return;
} }
ts->states[p->id] = attr; SKIP_BACK_DECLARE(struct proto_pending_update, ppu, li, li);
*ppu = (struct proto_pending_update) {
if (p->ea_state && p->ea_state->stored) .li = ppu->li, /* Keep the item's internal state */
ea_free_later(p->ea_state); .new = p->ea_state,
p->ea_state = attr ? ea_ref(attr) : NULL; .old = old_state,
struct proto_pending_update *pupdate = SKIP_BACK(struct proto_pending_update, li, lfjour_push_prepare(&proto_state_table_pub.journal));
if (!pupdate)
{
proto_journal_item_cleanup_(attr, old_attr);
return;
}
*pupdate = (struct proto_pending_update) {
.li = pupdate->li, /* Keep the item's internal state */
.proto_attr = attr,
.old_proto_attr = old_attr,
.protocol = p
}; };
lfjour_push_commit(&proto_state_table_pub.journal); lfjour_push_commit(&proto_state_table_pub.journal);
@ -2996,29 +3073,35 @@ proto_announce_state(struct proto *p, ea_list *attr)
proto_announce_state_locked(ts, p, attr); proto_announce_state_locked(ts, p, attr);
} }
struct proto_announce_state_deferred {
struct deferred_call dc;
struct proto *p;
};
static void proto_announce_state_deferred(struct deferred_call *dc) static void proto_announce_state_deferred(struct deferred_call *dc)
{ {
SKIP_BACK_DECLARE(struct proto_announce_state_deferred, pasd, dc, dc); SKIP_BACK_DECLARE(struct proto_announce_state_deferred, pasd, dc, dc);
proto_announce_state(pasd->p, pasd->p->ea_state); if (pasd->p)
proto_announce_state(pasd->p, pasd->p->ea_state);
} }
void void
proto_announce_state_later(struct proto *p, ea_list *attr) proto_announce_state_later_internal(struct proto *p, ea_list *new_state)
{ {
ea_free_later(p->ea_state); /* Cancel the previous deferred announcement */
p->ea_state = ea_lookup(attr, 0, EALS_CUSTOM); if (p->deferred_state_announcement)
p->deferred_state_announcement->p = NULL;
/* The old stored state isn't needed any more */
ea_free_later(p->ea_state);
/* Store the new one */
p->ea_state = ea_lookup(new_state, 0, EALS_IN_TABLE);
/* Defer the rest */
struct proto_announce_state_deferred pasd = { struct proto_announce_state_deferred pasd = {
.dc.hook = proto_announce_state_deferred, .dc.hook = proto_announce_state_deferred,
.p = p, .p = p,
}; };
defer_call(&pasd.dc, sizeof pasd); p->deferred_state_announcement =
SKIP_BACK(struct proto_announce_state_deferred, dc,
defer_call(&pasd.dc, sizeof pasd));
} }
ea_list * ea_list *

View File

@ -174,6 +174,7 @@ struct proto {
char *message; /* State-change message, allocated from proto_pool */ char *message; /* State-change message, allocated from proto_pool */
u32 id; /* Sequential ID used as index in proto_state_table */ u32 id; /* Sequential ID used as index in proto_state_table */
ea_list *ea_state; /* Last stored protocol state in proto_state_table (cached) */ ea_list *ea_state; /* Last stored protocol state in proto_state_table (cached) */
struct proto_announce_state_deferred *deferred_state_announcement;
/* /*
* General protocol hooks: * General protocol hooks:
@ -437,14 +438,27 @@ LOBJ_UNLOCK_CLEANUP(proto_state_table, rtable);
struct proto_pending_update { struct proto_pending_update {
LFJOUR_ITEM_INHERIT(li); LFJOUR_ITEM_INHERIT(li);
ea_list *proto_attr; ea_list *new, *old;
ea_list *old_proto_attr;
struct proto *protocol;
}; };
void proto_announce_state_locked(struct proto_state_table_private *ts, struct proto *p, ea_list *attr); void proto_announce_state_locked(struct proto_state_table_private *ts, struct proto *p, ea_list *attr);
void proto_announce_state(struct proto *p, ea_list *attr); void proto_announce_state(struct proto *p, ea_list *attr);
void proto_announce_state_later(struct proto *p, ea_list *attr);
void proto_announce_state_later_internal(struct proto *p, ea_list *attr);
#if 0
#define proto_announce_state_later(p, a) ( log(L_INFO "proto_announce_state_later(%s (%p), %p) at %s:%d", (p)->name, (p), (a), __FILE__, __LINE__), proto_announce_state_later_internal((p), (a)) )
#else
#define proto_announce_state_later proto_announce_state_later_internal
#endif
static inline void
proto_update_extended_state(struct proto *p, eattr ea)
{
ea_list *pes = p->ea_state;
ea_set_attr(&pes, ea);
proto_announce_state_later(p, pes);
}
ea_list *channel_get_state(int id); ea_list *channel_get_state(int id);
ea_list *proto_get_state(int id); ea_list *proto_get_state(int id);
void proto_states_subscribe(struct lfjour_recipient *r); void proto_states_subscribe(struct lfjour_recipient *r);

View File

@ -486,22 +486,22 @@ bgp_close_conn(struct bgp_conn *conn)
conn->local_open_length = 0; conn->local_open_length = 0;
conn->remote_open_length = 0; conn->remote_open_length = 0;
ea_list *attr = conn->bgp->p.ea_state; ea_list *pes = conn->bgp->p.ea_state;
if (conn == &conn->bgp->incoming_conn) if (conn == &conn->bgp->incoming_conn)
{ {
ea_set_attr(&attr, EA_LITERAL_STORE_ADATA(&ea_bgp_in_conn_local_open_msg, 0, NULL, 0)); ea_unset_attr(&pes, 0, &ea_bgp_in_conn_local_open_msg);
ea_set_attr(&attr, EA_LITERAL_STORE_ADATA(&ea_bgp_in_conn_remote_open_msg, 0, NULL, 0)); ea_unset_attr(&pes, 0, &ea_bgp_in_conn_remote_open_msg);
ea_set_attr(&attr, EA_LITERAL_STORE_ADATA(&ea_bgp_in_conn_sk, 0, NULL, 0)); ea_unset_attr(&pes, 0, &ea_bgp_in_conn_sk);
} }
else else
{ {
ASSERT_DIE(conn == &conn->bgp->outgoing_conn); ASSERT_DIE(conn == &conn->bgp->outgoing_conn);
ea_set_attr(&attr, EA_LITERAL_STORE_ADATA(&ea_bgp_out_conn_local_open_msg, 0, NULL, 0)); ea_unset_attr(&pes, 0, &ea_bgp_out_conn_local_open_msg);
ea_set_attr(&attr, EA_LITERAL_STORE_ADATA(&ea_bgp_out_conn_remote_open_msg, 0, NULL, 0)); ea_unset_attr(&pes, 0, &ea_bgp_out_conn_remote_open_msg);
ea_set_attr(&attr, EA_LITERAL_STORE_ADATA(&ea_bgp_out_conn_sk, 0, NULL, 0)); ea_unset_attr(&pes, 0, &ea_bgp_out_conn_sk);
} }
conn->bgp->p.ea_state = ea_lookup(conn->bgp->p.ea_state, 0, EALS_CUSTOM);
proto_announce_state_later(&conn->bgp->p, attr); proto_announce_state_later(&conn->bgp->p, pes);
mb_free(conn->local_caps); mb_free(conn->local_caps);
conn->local_caps = NULL; conn->local_caps = NULL;
@ -661,16 +661,17 @@ bgp_conn_set_state(struct bgp_conn *conn, uint new_state)
bgp_dump_state_change(conn, conn->state, new_state); bgp_dump_state_change(conn, conn->state, new_state);
conn->state = new_state; conn->state = new_state;
ea_list *pes = conn->bgp->p.ea_state;
if (conn == &conn->bgp->incoming_conn) if (conn == &conn->bgp->incoming_conn)
ea_set_attr(&conn->bgp->p.ea_state, EA_LITERAL_EMBEDDED(&ea_bgp_in_conn_state, 0, new_state)); ea_set_attr(&pes, EA_LITERAL_EMBEDDED(&ea_bgp_in_conn_state, 0, new_state));
else else
{ {
ASSERT_DIE(conn == &conn->bgp->outgoing_conn); ASSERT_DIE(conn == &conn->bgp->outgoing_conn);
ea_set_attr(&conn->bgp->p.ea_state, EA_LITERAL_EMBEDDED(&ea_bgp_out_conn_state, 0, new_state)); ea_set_attr(&pes, EA_LITERAL_EMBEDDED(&ea_bgp_out_conn_state, 0, new_state));
} }
conn->bgp->p.ea_state = ea_lookup(conn->bgp->p.ea_state, 0, EALS_CUSTOM);
proto_announce_state_later(&conn->bgp->p, conn->bgp->p.ea_state); proto_announce_state_later(&conn->bgp->p, pes);
} }
void void
@ -711,9 +712,10 @@ bgp_conn_enter_established_state(struct bgp_conn *conn)
p->last_error_code = 0; p->last_error_code = 0;
p->as4_session = conn->as4_session; p->as4_session = conn->as4_session;
ea_set_attr(&p->p.ea_state, EA_LITERAL_EMBEDDED(&ea_bgp_as4_session, 0, conn->as4_session));
p->p.ea_state = ea_lookup(p->p.ea_state, 0, EALS_CUSTOM); ea_list *pes = p->p.ea_state;
proto_announce_state_later(&conn->bgp->p, conn->bgp->p.ea_state); ea_set_attr(&pes, EA_LITERAL_EMBEDDED(&ea_bgp_as4_session, 0, conn->as4_session));
proto_announce_state_later(&conn->bgp->p, pes);
p->route_refresh = peer->route_refresh; p->route_refresh = peer->route_refresh;
p->enhanced_refresh = local->enhanced_refresh && peer->enhanced_refresh; p->enhanced_refresh = local->enhanced_refresh && peer->enhanced_refresh;
@ -847,10 +849,9 @@ bgp_conn_leave_established_state(struct bgp_conn *conn, struct bgp_proto *p)
}; };
memcpy(bscad->data, conn->notify_data, conn->notify_size); memcpy(bscad->data, conn->notify_data, conn->notify_size);
ea_set_attr(&p->p.ea_state, EA_LITERAL_DIRECT_ADATA(&ea_bgp_close_bmp, 0, &bscad->ad)); ea_list *pes = p->p.ea_state;
p->p.ea_state = ea_lookup(p->p.ea_state, 0, EALS_CUSTOM); ea_set_attr(&pes, EA_LITERAL_DIRECT_ADATA(&ea_bgp_close_bmp, 0, &bscad->ad));
proto_announce_state_later(&p->p, pes);
proto_announce_state_later(&p->p, p->p.ea_state);
} }
void void
@ -1243,17 +1244,6 @@ bgp_setup_conn(struct bgp_proto *p, struct bgp_conn *conn)
conn->hold_timer = tm_new_init(p->p.pool, bgp_hold_timeout, conn, 0, 0); conn->hold_timer = tm_new_init(p->p.pool, bgp_hold_timeout, conn, 0, 0);
conn->keepalive_timer = tm_new_init(p->p.pool, bgp_keepalive_timeout, conn, 0, 0); conn->keepalive_timer = tm_new_init(p->p.pool, bgp_keepalive_timeout, conn, 0, 0);
conn->send_hold_timer = tm_new_init(p->p.pool, bgp_send_hold_timeout, conn, 0, 0); conn->send_hold_timer = tm_new_init(p->p.pool, bgp_send_hold_timeout, conn, 0, 0);
ea_list *attr = conn->bgp->p.ea_state;
if (conn == &conn->bgp->incoming_conn)
ea_set_attr(&attr, EA_LITERAL_STORE_ADATA(&ea_bgp_in_conn_sk, 0, NULL, 0));
else
{
ASSERT_DIE(conn == &conn->bgp->outgoing_conn);
ea_set_attr(&attr, EA_LITERAL_STORE_ADATA(&ea_bgp_out_conn_sk, 0, NULL, 0));
}
conn->bgp->p.ea_state = ea_lookup(conn->bgp->p.ea_state, 0, EALS_CUSTOM);
proto_announce_state_later(&p->p, attr);
} }
static void static void
@ -1272,18 +1262,17 @@ bgp_setup_sk(struct bgp_conn *conn, sock *s)
.dport = s->dport, .dport = s->dport,
}; };
ea_list *attr = conn->bgp->p.ea_state; ea_list *pes = conn->bgp->p.ea_state;
if (conn == &conn->bgp->incoming_conn) if (conn == &conn->bgp->incoming_conn)
ea_set_attr(&attr, EA_LITERAL_DIRECT_ADATA(&ea_bgp_in_conn_sk, 0, &sk_ad.ad)); ea_set_attr(&pes, EA_LITERAL_DIRECT_ADATA(&ea_bgp_in_conn_sk, 0, &sk_ad.ad));
else else
{ {
ASSERT_DIE(conn == &conn->bgp->outgoing_conn); ASSERT_DIE(conn == &conn->bgp->outgoing_conn);
ea_set_attr(&attr, EA_LITERAL_DIRECT_ADATA(&ea_bgp_out_conn_sk, 0, &sk_ad.ad)); ea_set_attr(&pes, EA_LITERAL_DIRECT_ADATA(&ea_bgp_out_conn_sk, 0, &sk_ad.ad));
} }
conn->bgp->p.ea_state = ea_lookup(conn->bgp->p.ea_state, 0, EALS_CUSTOM); proto_announce_state_later(&conn->bgp->p, pes);
proto_announce_state_later(&conn->bgp->p, attr);
} }
static void static void
@ -1502,6 +1491,11 @@ err:
leave: leave:
UNLOCK_DOMAIN(rtable, bgp_listen_domain); UNLOCK_DOMAIN(rtable, bgp_listen_domain);
/* We need to announce possible state changes immediately before
* leaving the protocol's loop, otherwise we're gonna access the protocol
* without having it locked from proto_announce_state_later(). */
proto_announce_state(&p->p, p->p.ea_state);
birdloop_leave(p->p.loop); birdloop_leave(p->p.loop);
return 0; return 0;
} }
@ -1537,6 +1531,9 @@ bgp_neigh_notify(neighbor *n)
struct bgp_proto *p = (struct bgp_proto *) n->proto; struct bgp_proto *p = (struct bgp_proto *) n->proto;
int ps = p->p.proto_state; int ps = p->p.proto_state;
ASSERT_DIE(birdloop_inside(p->p.loop));
ASSERT_DIE(!birdloop_inside(&main_birdloop));
if (n != p->neigh) if (n != p->neigh)
return; return;
@ -1607,6 +1604,9 @@ bgp_bfd_notify(struct bfd_request *req)
bgp_stop(p, 0, NULL, 0); bgp_stop(p, 0, NULL, 0);
} }
} }
/* BFD notify may run from another thread */
proto_announce_state(&p->p, p->p.ea_state);
} }
static void static void
@ -1719,6 +1719,8 @@ bgp_start_locked(void *_p)
struct bgp_proto *p = _p; struct bgp_proto *p = _p;
const struct bgp_config *cf = p->cf; const struct bgp_config *cf = p->cf;
ASSERT_DIE(birdloop_inside(p->p.loop));
if (p->p.proto_state != PS_START) if (p->p.proto_state != PS_START)
{ {
DBG("BGP: Got lock in different state %d\n", p->p.proto_state); DBG("BGP: Got lock in different state %d\n", p->p.proto_state);
@ -1731,6 +1733,7 @@ bgp_start_locked(void *_p)
{ {
/* Multi-hop sessions do not use neighbor entries */ /* Multi-hop sessions do not use neighbor entries */
bgp_initiate(p); bgp_initiate(p);
proto_announce_state(&p->p, p->p.ea_state);
return; return;
} }
@ -1754,6 +1757,8 @@ bgp_start_locked(void *_p)
BGP_TRACE(D_EVENTS, "Waiting for link on %s", n->iface->name); BGP_TRACE(D_EVENTS, "Waiting for link on %s", n->iface->name);
else else
bgp_start_neighbor(p); bgp_start_neighbor(p);
proto_announce_state(&p->p, p->p.ea_state);
} }
static int static int
@ -1987,12 +1992,14 @@ bgp_init(struct proto_config *CF)
/* Add MPLS channel */ /* Add MPLS channel */
proto_configure_mpls_channel(P, CF, RTS_BGP); proto_configure_mpls_channel(P, CF, RTS_BGP);
ea_set_attr(&p->p.ea_state, EA_LITERAL_STORE_ADATA(&ea_bgp_rem_ip, 0, &cf->remote_ip, sizeof(ip_addr))); /* Export public info */
ea_set_attr(&p->p.ea_state, EA_LITERAL_EMBEDDED(&ea_bgp_peer_type, 0, cf->peer_type)); ea_list *pes = p->p.ea_state;
ea_set_attr(&p->p.ea_state, EA_LITERAL_EMBEDDED(&ea_bgp_loc_as, 0, cf->local_as)); ea_set_attr(&pes, EA_LITERAL_STORE_ADATA(&ea_bgp_rem_ip, 0, &cf->remote_ip, sizeof(ip_addr)));
ea_set_attr(&p->p.ea_state, EA_LITERAL_EMBEDDED(&ea_bgp_rem_as, 0, cf->remote_as)); ea_set_attr(&pes, EA_LITERAL_EMBEDDED(&ea_bgp_peer_type, 0, cf->peer_type));
ea_set_attr(&pes, EA_LITERAL_EMBEDDED(&ea_bgp_loc_as, 0, cf->local_as));
ea_set_attr(&pes, EA_LITERAL_EMBEDDED(&ea_bgp_rem_as, 0, cf->remote_as));
proto_announce_state_later(&p->p, p->p.ea_state); proto_announce_state_later(&p->p, pes);
return P; return P;
} }

View File

@ -918,16 +918,17 @@ bgp_rx_open(struct bgp_conn *conn, byte *pkt, uint len)
conn->remote_open_msg = bgp_copy_open(p, pkt, len); conn->remote_open_msg = bgp_copy_open(p, pkt, len);
conn->remote_open_length = len - BGP_HEADER_LENGTH; conn->remote_open_length = len - BGP_HEADER_LENGTH;
ea_list *attr = p->p.ea_state;
if (conn == &conn->bgp->incoming_conn) if (conn == &conn->bgp->incoming_conn)
ea_set_attr(&attr, EA_LITERAL_STORE_ADATA(&ea_bgp_in_conn_remote_open_msg, 0, conn->remote_open_msg, conn->remote_open_length)); proto_update_extended_state(&conn->bgp->p,
EA_LITERAL_STORE_ADATA(&ea_bgp_in_conn_remote_open_msg, 0,
conn->remote_open_msg, conn->remote_open_length));
else else
{ {
ASSERT_DIE(conn == &conn->bgp->outgoing_conn); ASSERT_DIE(conn == &conn->bgp->outgoing_conn);
ea_set_attr(&attr, EA_LITERAL_STORE_ADATA(&ea_bgp_out_conn_remote_open_msg, 0, conn->remote_open_msg, conn->remote_open_length)); proto_update_extended_state(&conn->bgp->p,
EA_LITERAL_STORE_ADATA(&ea_bgp_out_conn_remote_open_msg, 0,
conn->remote_open_msg, conn->remote_open_length));
} }
p->p.ea_state = ea_lookup(p->p.ea_state, 0, EALS_CUSTOM);
proto_announce_state_later(&p->p, attr);
if (bgp_read_options(conn, pkt+29, pkt[28], len-29) < 0) if (bgp_read_options(conn, pkt+29, pkt[28], len-29) < 0)
return; return;
@ -1059,18 +1060,17 @@ bgp_rx_open(struct bgp_conn *conn, byte *pkt, uint len)
conn->ext_messages = conn->local_caps->ext_messages && caps->ext_messages; conn->ext_messages = conn->local_caps->ext_messages && caps->ext_messages;
p->remote_id = id; p->remote_id = id;
ea_set_attr(&p->p.ea_state, EA_LITERAL_EMBEDDED(&ea_bgp_rem_id, 0, p->remote_id)); ea_list *pes = p->p.ea_state;
ea_set_attr(&pes, EA_LITERAL_EMBEDDED(&ea_bgp_rem_id, 0, p->remote_id));
if (conn == &p->incoming_conn) if (conn == &p->incoming_conn)
ea_set_attr(&p->p.ea_state, EA_LITERAL_EMBEDDED(&ea_bgp_as4_in_conn, 0, conn->as4_session)); ea_set_attr(&pes, EA_LITERAL_EMBEDDED(&ea_bgp_as4_in_conn, 0, conn->as4_session));
else else
{ {
ASSERT_DIE(conn == &p->outgoing_conn); ASSERT_DIE(conn == &p->outgoing_conn);
ea_set_attr(&p->p.ea_state, EA_LITERAL_EMBEDDED(&ea_bgp_as4_out_conn, 0, conn->as4_session)); ea_set_attr(&pes, EA_LITERAL_EMBEDDED(&ea_bgp_as4_out_conn, 0, conn->as4_session));
} }
proto_announce_state_later(&p->p, pes);
p->p.ea_state = ea_lookup(p->p.ea_state, 0, EALS_CUSTOM);
proto_announce_state_later(&p->p, p->p.ea_state);
DBG("BGP: Hold timer set to %d, keepalive to %d, AS to %d, ID to %x, AS4 session to %d\n", DBG("BGP: Hold timer set to %d, keepalive to %d, AS to %d, ID to %x, AS4 session to %d\n",
conn->hold_time, conn->keepalive_time, p->remote_as, p->remote_id, conn->as4_session); conn->hold_time, conn->keepalive_time, p->remote_as, p->remote_id, conn->as4_session);
@ -3143,16 +3143,15 @@ bgp_fire_tx(struct bgp_conn *conn)
conn->local_open_msg = bgp_copy_open(p, buf, end - buf); conn->local_open_msg = bgp_copy_open(p, buf, end - buf);
conn->local_open_length = end - buf - BGP_HEADER_LENGTH; conn->local_open_length = end - buf - BGP_HEADER_LENGTH;
ea_list *attr = p->p.ea_state; ea_list *pes = p->p.ea_state;
if (conn == &conn->bgp->incoming_conn) if (conn == &conn->bgp->incoming_conn)
ea_set_attr(&attr, EA_LITERAL_STORE_ADATA(&ea_bgp_in_conn_local_open_msg, 0, conn->local_open_msg, conn->local_open_length)); ea_set_attr(&pes, EA_LITERAL_STORE_ADATA(&ea_bgp_in_conn_local_open_msg, 0, conn->local_open_msg, conn->local_open_length));
else else
{ {
ASSERT_DIE(conn == &conn->bgp->outgoing_conn); ASSERT_DIE(conn == &conn->bgp->outgoing_conn);
ea_set_attr(&attr, EA_LITERAL_STORE_ADATA(&ea_bgp_out_conn_local_open_msg, 0, conn->local_open_msg, conn->local_open_length)); ea_set_attr(&pes, EA_LITERAL_STORE_ADATA(&ea_bgp_out_conn_local_open_msg, 0, conn->local_open_msg, conn->local_open_length));
} }
p->p.ea_state = ea_lookup(p->p.ea_state, 0, EALS_CUSTOM); proto_announce_state_later(&p->p, pes);
proto_announce_state_later(&p->p, attr);
return bgp_send(conn, PKT_OPEN, end - buf); return bgp_send(conn, PKT_OPEN, end - buf);
} }

View File

@ -1333,13 +1333,13 @@ bmp_process_proto_state_change(struct bmp_proto *p, struct lfjour_item *last_up)
if (!ppu) if (!ppu)
return; return;
if (bmp_peer_up_inout(p, ppu->proto_attr, true)) if (bmp_peer_up_inout(p, ppu->new, true))
goto done; goto done;
SKIP_BACK_DECLARE(struct bgp_session_close_ad, bscad, ad, ea_get_adata(ppu->proto_attr, &ea_bgp_close_bmp)); SKIP_BACK_DECLARE(struct bgp_session_close_ad, bscad, ad, ea_get_adata(ppu->new, &ea_bgp_close_bmp));
if (bscad) if (bscad)
{ {
bmp_peer_down_(p, ppu->proto_attr, bscad); bmp_peer_down_(p, ppu->new, bscad);
goto done; goto done;
} }