]> git.ipfire.org Git - thirdparty/bird.git/commitdiff
Protocol and BGP state information cleanup and fixes
authorMaria Matejka <mq@ucw.cz>
Fri, 22 Nov 2024 13:49:13 +0000 (14:49 +0100)
committerMaria Matejka <mq@ucw.cz>
Sun, 24 Nov 2024 21:51:08 +0000 (22:51 +0100)
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.

lib/route.h
nest/proto.c
nest/protocol.h
proto/bgp/bgp.c
proto/bgp/packets.c
proto/bmp/bmp.c

index 11b06e2aadeb29c3ab79c55a52208cdd834c449e..8d744e4399910003f93fe004ede8d303db90fb2f 100644 (file)
@@ -11,7 +11,7 @@
 #define _BIRD_LIB_ROUTE_H_
 
 #undef RT_SOURCE_DEBUG
-#define EA_FREE_DEBUG
+#undef EA_FREE_DEBUG
 
 #include "lib/type.h"
 #include "lib/rcu.h"
index 4492772027a00d00996797bb00e2caacbf712a59..c45289aea921a45047422f558ff87a6279fa6c0b 100644 (file)
@@ -242,7 +242,17 @@ proto_add_channel(struct proto *p, struct channel_config *cf)
 
   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)
   {
     /* Allocating channel ID */
@@ -260,29 +270,19 @@ proto_add_channel(struct proto *p, struct channel_config *cf)
       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(ts->channels[c->id] == NULL);
-    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);
+    /* Set the channel info */
+    ts->channels[c->id] = ea_lookup_slow(ca, 0, EALS_IN_TABLE);
+  }
 
-    ea_set_attr(&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(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);
-  }
+         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);
 
@@ -300,13 +300,21 @@ proto_remove_channel(struct proto *p UNUSED, struct channel *c)
 
   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(
       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(c->proto, p->ea_state);
+  proto_announce_state_later(c->proto, pes);
 
+  /*
+   * 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)
   {
     ASSERT_DIE(c->id < ts->length_channels);
@@ -1319,6 +1327,9 @@ proto_new(struct proto_config *cf)
   p->mrtdump = cf->mrtdump;
   p->name = cf->name;
   p->proto = cf->protocol;
+  p->proto_state = PS_DOWN_XX;
+  p->last_state_change = current_time();
+
   p->net_type = cf->net_type;
   p->disabled = cf->disabled;
   p->hash_key = random_u32();
@@ -1344,7 +1355,7 @@ proto_new(struct proto_config *cf)
   init_list(&p->channels);
 
   /*
-    Making first version of proto eatters.
+    Initial version of protocol state.
   */
   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_STORE_ADATA(&ea_proto_channel_list, 0, NULL, 0));
 
-  proto_announce_state(p, state);
+  proto_announce_state_later(p, state);
 
   return p;
 }
@@ -1367,11 +1378,11 @@ proto_init(struct proto_config *c, struct proto *after)
   struct proto *p = pr->init(c);
 
   p->loop = &main_birdloop;
-  p->proto_state = PS_DOWN_XX;
-  p->last_state_change = current_time();
   p->vrf = c->vrf;
   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);
 
   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_SET(p->global_config, new);
+         proto_announce_state(p, p->ea_state);
          PROTO_LEAVE_FROM_MAIN(proto_loop);
          continue;
        }
@@ -1722,6 +1734,7 @@ protos_do_commit(struct config *new, struct config *old, int type)
       }
 
       p->reconfiguring = 1;
+      proto_announce_state(p, p->ea_state);
       PROTO_LEAVE_FROM_MAIN(proto_loop);
 
       proto_rethink_goal(p);
@@ -1798,12 +1811,13 @@ proto_rethink_goal(struct proto *p)
     struct proto_config *nc = p->cf_new;
     struct proto *after = p->n.prev;
 
-    proto_announce_state(p, NULL);
-
     DBG("%s has shut down for reconfiguration\n", p->name);
     p->cf->proto = NULL;
     OBSREF_CLEAR(p->global_config);
     proto_remove_channels(p);
+
+    proto_announce_state(p, NULL);
+
     proto_rem_node(&global_proto_list, p);
     rfree(p->event);
     mb_free(p->message);
@@ -2421,19 +2435,18 @@ proto_do_down(struct proto *p)
 void
 proto_notify_state(struct proto *p, uint state)
 {
+  ASSERT_DIE(birdloop_inside(p->loop));
+
   uint ps = p->proto_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)
-    return;
+    return proto_announce_state(p, p->ea_state);
 
   p->proto_state = state;
   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)
   {
   case PS_START:
@@ -2484,6 +2497,11 @@ proto_notify_state(struct proto *p, uint state)
     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);
 }
 
@@ -2921,18 +2939,23 @@ proto_iterate_named(struct symbol *sym, struct protocol *proto, struct proto *ol
 }
 
 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);
 
-  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)
-    {
-      int p_id = ea_get_int(old_attr, &ea_proto_id, 0);
-      hmap_clear(&tp->proto_id_map, p_id);
-      tp->states[p_id] = NULL;
-    }
+    u32 id = ea_get_int(old_attr, &ea_proto_id, 0);
+    ASSERT_DIE(id);
+    ASSERT_DIE(tp->states[id] == NULL);
+    hmap_clear(&tp->proto_id_map, id);
   }
 }
 
@@ -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. */
   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
-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)
 {
-  /*
-    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)));
+  ASSERT_DIE(birdloop_inside(p->loop));
 
-  attr = ea_lookup(attr, 0, EALS_CUSTOM);
-
-  ASSERT_DIE(p->id < ts->length_states);
-  ea_list *old_attr = ts->states[p->id];
+  /* Cancel the previous deferred announcement */
+  if (p->deferred_state_announcement)
+  {
+    p->deferred_state_announcement->p = NULL;
+    p->deferred_state_announcement = NULL;
+  }
 
-  if (attr == old_attr)
+  /* First we update the state-to-be-stored */
+  if (new_state != p->ea_state)
   {
-    /* Nothing has changed */
-    ea_free_later(attr);
-    return;
+    if (p->ea_state)
+      ea_free_later(p->ea_state);
+
+    p->ea_state = new_state ? ea_lookup(new_state, 0, EALS_IN_TABLE) : NULL;
   }
 
-  ts->states[p->id] = attr;
+  /* Then we check the public state */
+  ASSERT_DIE(p->id < ts->length_states);
+  ea_list *old_state = ts->states[p->id];
 
-  if (p->ea_state && p->ea_state->stored)
-    ea_free_later(p->ea_state);
-  p->ea_state = attr ? ea_ref(attr) : NULL;
+  /* Nothing has changed? */
+  if (p->ea_state == old_state)
+    return;
 
-  struct proto_pending_update *pupdate = SKIP_BACK(struct proto_pending_update, li, lfjour_push_prepare(&proto_state_table_pub.journal));
+  /* Set the new state */
+  ts->states[p->id] = p->ea_state ? ea_ref(p->ea_state) : NULL;
 
-  if (!pupdate)
+  /* Announce the new state */
+  struct lfjour_item *li = lfjour_push_prepare(&proto_state_table_pub.journal);
+  if (!li)
   {
-    proto_journal_item_cleanup_(attr, old_attr);
+    /* No reader, cleanup immediately
+     * 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;
   }
 
-  *pupdate = (struct proto_pending_update) {
-    .li = pupdate->li, /* Keep the item's internal state */
-    .proto_attr = attr,
-    .old_proto_attr = old_attr,
-    .protocol = p
+  SKIP_BACK_DECLARE(struct proto_pending_update, ppu, li, li);
+  *ppu = (struct proto_pending_update) {
+    .li = ppu->li,     /* Keep the item's internal state */
+    .new = p->ea_state,
+    .old = old_state,
   };
 
   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);
 }
 
-struct proto_announce_state_deferred {
-  struct deferred_call dc;
-  struct proto *p;
-};
-
 static void proto_announce_state_deferred(struct deferred_call *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
-proto_announce_state_later(struct proto *p, ea_list *attr)
+proto_announce_state_later_internal(struct proto *p, ea_list *new_state)
 {
+  /* Cancel the previous deferred announcement */
+  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);
-  p->ea_state = ea_lookup(attr, 0, EALS_CUSTOM);
 
+  /* Store the new one */
+  p->ea_state = ea_lookup(new_state, 0, EALS_IN_TABLE);
+
+  /* Defer the rest */
   struct proto_announce_state_deferred pasd = {
     .dc.hook = proto_announce_state_deferred,
     .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 *
index d643d8bab118716d9793e625387c010a42cb1b8b..cc8b098b407d78bfd1d596852040b2f326acecd3 100644 (file)
@@ -174,6 +174,7 @@ struct proto {
   char *message;                       /* State-change message, allocated from proto_pool */
   u32 id;                              /* Sequential ID used as index in proto_state_table */
   ea_list *ea_state;                   /* Last stored protocol state in proto_state_table (cached) */
+  struct proto_announce_state_deferred *deferred_state_announcement;
 
   /*
    *   General protocol hooks:
@@ -437,14 +438,27 @@ LOBJ_UNLOCK_CLEANUP(proto_state_table, rtable);
 
 struct proto_pending_update {
   LFJOUR_ITEM_INHERIT(li);
-  ea_list *proto_attr;
-  ea_list *old_proto_attr;
-  struct proto *protocol;
+  ea_list *new, *old;
 };
 
 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_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 *proto_get_state(int id);
 void proto_states_subscribe(struct lfjour_recipient *r);
index e381a73cb0118db2d66c0d9160e13d7ac38b14b3..408053d5cfe07b36cfe403cbc9fb372b24b6256e 100644 (file)
@@ -486,22 +486,22 @@ bgp_close_conn(struct bgp_conn *conn)
   conn->local_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)
   {
-    ea_set_attr(&attr, EA_LITERAL_STORE_ADATA(&ea_bgp_in_conn_local_open_msg, 0, NULL, 0));
-    ea_set_attr(&attr, EA_LITERAL_STORE_ADATA(&ea_bgp_in_conn_remote_open_msg, 0, NULL, 0));
-    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_local_open_msg);
+    ea_unset_attr(&pes, 0, &ea_bgp_in_conn_remote_open_msg);
+    ea_unset_attr(&pes, 0, &ea_bgp_in_conn_sk);
   }
   else
   {
     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_set_attr(&attr, EA_LITERAL_STORE_ADATA(&ea_bgp_out_conn_remote_open_msg, 0, NULL, 0));
-    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_local_open_msg);
+    ea_unset_attr(&pes, 0, &ea_bgp_out_conn_remote_open_msg);
+    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);
   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);
 
   conn->state = new_state;
+  ea_list *pes = conn->bgp->p.ea_state;
 
   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
   {
     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
@@ -711,9 +712,10 @@ bgp_conn_enter_established_state(struct bgp_conn *conn)
   p->last_error_code = 0;
 
   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);
-  proto_announce_state_later(&conn->bgp->p, conn->bgp->p.ea_state);
+
+  ea_list *pes = p->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->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);
 
-  ea_set_attr(&p->p.ea_state, EA_LITERAL_DIRECT_ADATA(&ea_bgp_close_bmp, 0, &bscad->ad));
-  p->p.ea_state = ea_lookup(p->p.ea_state, 0, EALS_CUSTOM);
-
-  proto_announce_state_later(&p->p, p->p.ea_state);
+  ea_list *pes = p->p.ea_state;
+  ea_set_attr(&pes, EA_LITERAL_DIRECT_ADATA(&ea_bgp_close_bmp, 0, &bscad->ad));
+  proto_announce_state_later(&p->p, pes);
 }
 
 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->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);
-
-  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
@@ -1272,18 +1262,17 @@ bgp_setup_sk(struct bgp_conn *conn, sock *s)
     .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)
-    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
   {
     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, attr);
+  proto_announce_state_later(&conn->bgp->p, pes);
 }
 
 static void
@@ -1502,6 +1491,11 @@ err:
 
 leave:
   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);
   return 0;
 }
@@ -1537,6 +1531,9 @@ bgp_neigh_notify(neighbor *n)
   struct bgp_proto *p = (struct bgp_proto *) n->proto;
   int ps = p->p.proto_state;
 
+  ASSERT_DIE(birdloop_inside(p->p.loop));
+  ASSERT_DIE(!birdloop_inside(&main_birdloop));
+
   if (n != p->neigh)
     return;
 
@@ -1607,6 +1604,9 @@ bgp_bfd_notify(struct bfd_request *req)
       bgp_stop(p, 0, NULL, 0);
     }
   }
+
+  /* BFD notify may run from another thread */
+  proto_announce_state(&p->p, p->p.ea_state);
 }
 
 static void
@@ -1719,6 +1719,8 @@ bgp_start_locked(void *_p)
   struct bgp_proto *p = _p;
   const struct bgp_config *cf = p->cf;
 
+  ASSERT_DIE(birdloop_inside(p->p.loop));
+
   if (p->p.proto_state != PS_START)
   {
     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 */
     bgp_initiate(p);
+    proto_announce_state(&p->p, p->p.ea_state);
     return;
   }
 
@@ -1754,6 +1757,8 @@ bgp_start_locked(void *_p)
     BGP_TRACE(D_EVENTS, "Waiting for link on %s", n->iface->name);
   else
     bgp_start_neighbor(p);
+
+  proto_announce_state(&p->p, p->p.ea_state);
 }
 
 static int
@@ -1987,12 +1992,14 @@ bgp_init(struct proto_config *CF)
   /* Add MPLS channel */
   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)));
-  ea_set_attr(&p->p.ea_state, EA_LITERAL_EMBEDDED(&ea_bgp_peer_type, 0, cf->peer_type));
-  ea_set_attr(&p->p.ea_state, EA_LITERAL_EMBEDDED(&ea_bgp_loc_as, 0, cf->local_as));
-  ea_set_attr(&p->p.ea_state, EA_LITERAL_EMBEDDED(&ea_bgp_rem_as, 0, cf->remote_as));
+  /* Export public info */
+  ea_list *pes = p->p.ea_state;
+  ea_set_attr(&pes, EA_LITERAL_STORE_ADATA(&ea_bgp_rem_ip, 0, &cf->remote_ip, sizeof(ip_addr)));
+  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;
 }
 
index 7a01dc0034d9a0b22e89fe90e32f87415dbb966a..8a61a1c5424f92d6e77bb72b2e77554748ec24ec 100644 (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_length = len - BGP_HEADER_LENGTH;
 
-  ea_list *attr = p->p.ea_state;
   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
   {
     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)
     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;
   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)
-    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
   {
     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));
   }
-
-  p->p.ea_state = ea_lookup(p->p.ea_state, 0, EALS_CUSTOM);
-  proto_announce_state_later(&p->p, p->p.ea_state);
+  proto_announce_state_later(&p->p, pes);
 
   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);
@@ -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_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)
-      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
     {
       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, attr);
+    proto_announce_state_later(&p->p, pes);
 
     return bgp_send(conn, PKT_OPEN, end - buf);
   }
index c72891c257cc5abf2323217f0d91db08d38e3a68..d880545ad0b32f8f623dc2177ddfbf441c0754b9 100644 (file)
@@ -1333,13 +1333,13 @@ bmp_process_proto_state_change(struct bmp_proto *p, struct lfjour_item *last_up)
   if (!ppu)
     return;
 
-  if (bmp_peer_up_inout(p, ppu->proto_attr, true))
+  if (bmp_peer_up_inout(p, ppu->new, true))
     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)
   {
-    bmp_peer_down_(p, ppu->proto_attr, bscad);
+    bmp_peer_down_(p, ppu->new, bscad);
     goto done;
   }