Browse Source

bgp: fix node removal when alternative paths exist

- route_bgp_process_nodeinfo: record alternative paths even when NODEINFO
  version hasn't changed (fixes nodes disappearing on link down despite
  having other active links)
- route_bgp_process_withdraw: only remove node when all paths gone (ret==1)
- route_bgp_remove_conn: broadcast withdraw for each removed node with
  correct wd_source
chatgui
Evgeny 3 months ago
parent
commit
1c09ad2e5a
  1. 25
      src/route_bgp.c

25
src/route_bgp.c

@ -441,14 +441,12 @@ void route_bgp_remove_conn(struct ETCP_CONN* conn) {
struct ROUTE_TABLE* rt = conn->instance->rt; struct ROUTE_TABLE* rt = conn->instance->rt;
// Remove this connection from all nodes' path lists // Remove this connection from all nodes' path lists
bool need_withdraw = false;
int nodes_removed = 0; int nodes_removed = 0;
struct ll_entry* node_entry = bgp->nodes ? bgp->nodes->head : NULL; struct ll_entry* node_entry = bgp->nodes ? bgp->nodes->head : NULL;
while (node_entry) { while (node_entry) {
struct ll_entry* next = node_entry->next; struct ll_entry* next = node_entry->next;
struct NODEINFO_Q* nq = (struct NODEINFO_Q*)node_entry; struct NODEINFO_Q* nq = (struct NODEINFO_Q*)node_entry;
if (route_bgp_remove_path(nq, conn) == 1) { if (route_bgp_remove_path(nq, conn) == 1) {
need_withdraw = true;
if (rt) route_delete(rt, nq); if (rt) route_delete(rt, nq);
if (conn->instance && conn->instance->control_srv) if (conn->instance && conn->instance->control_srv)
control_server_notify_node_removed(conn->instance->control_srv, nq->node.node_id); control_server_notify_node_removed(conn->instance->control_srv, nq->node.node_id);
@ -460,6 +458,8 @@ void route_bgp_remove_conn(struct ETCP_CONN* conn) {
if (entry) { queue_remove_data(bgp->nodes, entry); queue_entry_free(entry); } if (entry) { queue_remove_data(bgp->nodes, entry); queue_entry_free(entry); }
nodes_removed++; nodes_removed++;
etcp_router_conn_close_all_for_node(bgp->instance, key); etcp_router_conn_close_all_for_node(bgp->instance, key);
uint64_t wd_src = (key == conn->peer_node_id) ? conn->instance->node_id : conn->peer_node_id;
route_bgp_broadcast_withdraw(bgp, key, wd_src, NULL);
DEBUG_DEBUG(DEBUG_CATEGORY_BGP, "Removed node %016llx after link down", (unsigned long long)key); DEBUG_DEBUG(DEBUG_CATEGORY_BGP, "Removed node %016llx after link down", (unsigned long long)key);
} }
node_entry = next; node_entry = next;
@ -477,12 +477,8 @@ void route_bgp_remove_conn(struct ETCP_CONN* conn) {
e = e->next; e = e->next;
} }
if (need_withdraw) { DEBUG_INFO(DEBUG_CATEGORY_BGP, "BGP peer removed: %s nodes=%d",
route_bgp_broadcast_withdraw(bgp, conn->peer_node_id, conn->instance->node_id, NULL); conn->log_name, nodes_removed);
}
DEBUG_INFO(DEBUG_CATEGORY_BGP, "BGP peer removed: %s nodes=%d withdraw=%d",
conn->log_name, nodes_removed, need_withdraw);
} }
@ -755,7 +751,16 @@ int route_bgp_process_nodeinfo(struct ROUTE_BGP* bgp, struct ETCP_CONN* from, co
uint8_t new_ver = ni->ver; uint8_t new_ver = ni->ver;
if (nodeinfo1 && (int8_t)(nodeinfo1->last_ver-new_ver)>=0) { if (nodeinfo1 && (int8_t)(nodeinfo1->last_ver-new_ver)>=0) {
DEBUG_TRACE(DEBUG_CATEGORY_BGP, "NODEINFO ignored old ver: node=%016llx ver=%d<=%d from=%s", int new_hops = ni->hop_count + 1;
if (new_hops <= MAX_HOPS) {
uint64_t hop_list[MAX_HOPS];
uint64_t* pkt_hops = (uint64_t*)((uint8_t*)ni + sizeof(struct NODEINFO) + dyn_size);
memcpy(hop_list, pkt_hops, ni->hop_count * 8);
hop_list[ni->hop_count] = from->peer_node_id;
route_bgp_remove_path_by_hop(nodeinfo1, from->peer_node_id);
route_bgp_add_path(nodeinfo1, from, hop_list, new_hops);
}
DEBUG_TRACE(DEBUG_CATEGORY_BGP, "NODEINFO old ver: node=%016llx ver=%d<=%d from=%s (paths updated)",
(unsigned long long)node_id, new_ver, nodeinfo1->last_ver, from->log_name); (unsigned long long)node_id, new_ver, nodeinfo1->last_ver, from->log_name);
return 0; return 0;
} }
@ -887,7 +892,7 @@ int route_bgp_process_withdraw(struct ROUTE_BGP* bgp, struct ETCP_CONN* sender,
return 0; return 0;
} }
int ret=route_bgp_remove_path_by_hop(nq, wd_source); int ret=route_bgp_remove_path_by_hop(nq, wd_source);
if (ret>0 || !nq->paths || (nq->paths && queue_entry_count(nq->paths) == 0)) { if (ret == 1) {
if (bgp->instance && bgp->instance->rt) route_delete(bgp->instance->rt, nq); if (bgp->instance && bgp->instance->rt) route_delete(bgp->instance->rt, nq);
if (bgp->instance->control_srv) control_server_notify_node_removed(bgp->instance->control_srv, node_id); if (bgp->instance->control_srv) control_server_notify_node_removed(bgp->instance->control_srv, node_id);
nq->dirty = 1; nq->dirty = 1;

Loading…
Cancel
Save