return -1;
}
+/*
+ * @failed_caps: instance of bgp_caps for marking missing capabilities
+ */
int
-bgp_check_capabilities(struct bgp_conn *conn)
-{
+bgp_check_capabilities(struct bgp_conn *conn, struct bgp_caps *failed_caps)
+{
+#define CAPS_CHECK_FAIL(capability,value) \
+ do { \
+ if (failed_caps) \
+ failed_caps->capability = value; \
+ return 0; \
+ } while (0)
+
+#define AF_CAPS_CHECK_FAIL(_afi,capability,value) \
+ do { \
+ if (failed_caps) { \
+ failed_caps->af_data[0].afi = _afi; \
+ failed_caps->af_data[0].capability = value; \
+ failed_caps->af_count = 1; \
+ } \
+ return 0; \
+ } while (0)
+
struct bgp_proto *p = conn->bgp;
struct bgp_caps *local = conn->local_caps;
struct bgp_caps *remote = conn->remote_caps;
but we need to run this just after we receive OPEN message */
if (p->cf->require_refresh && !remote->route_refresh)
- return 0;
+ CAPS_CHECK_FAIL(route_refresh, 1);
if (p->cf->require_enhanced_refresh && !remote->enhanced_refresh)
- return 0;
+ CAPS_CHECK_FAIL(enhanced_refresh, 1);
if (p->cf->require_as4 && !remote->as4_support)
- return 0;
+ CAPS_CHECK_FAIL(as4_support, 1);
if (p->cf->require_extended_messages && !remote->ext_messages)
- return 0;
+ CAPS_CHECK_FAIL(ext_messages, 1);
if (p->cf->require_hostname && !remote->hostname)
- return 0;
+ CAPS_CHECK_FAIL(hostname, NULL);
if (p->cf->require_gr && !remote->gr_aware)
- return 0;
+ CAPS_CHECK_FAIL(gr_aware, 1);
if (p->cf->require_llgr && !remote->llgr_aware)
- return 0;
+ CAPS_CHECK_FAIL(llgr_aware, 1);
/* No check for require_roles, as it uses error code 2.11 instead of 2.7 */
+ u32 first_active_channel_afi = 0;
+
+ /*
+ * @failed_caps has one bgp_af_caps entry, but af_count is 0.
+ * AF_CAPS_CHECK_FAIL marks missing capability and increases af_count to 1.
+ */
+
BGP_WALK_CHANNELS(p, c)
{
const struct bgp_af_caps *loc = bgp_find_af_caps(local, c->afi);
/* Find out whether this channel will be active */
int active = loc->ready && rem->ready;
+ if (loc->ready && !first_active_channel_afi)
+ first_active_channel_afi = c->afi;
+
/* Mandatory must be active */
if (c->cf->mandatory && !active)
- return 0;
+ AF_CAPS_CHECK_FAIL(c->afi, ready, 1);
if (active)
{
if (c->cf->require_ext_next_hop && !rem->ext_next_hop)
- return 0;
+ AF_CAPS_CHECK_FAIL(c->afi, ext_next_hop, 1);
- if (c->cf->require_add_path && (loc->add_path & BGP_ADD_PATH_RX) && !(rem->add_path & BGP_ADD_PATH_TX))
- return 0;
+ u32 missing = 0;
- if (c->cf->require_add_path && (loc->add_path & BGP_ADD_PATH_TX) && !(rem->add_path & BGP_ADD_PATH_RX))
- return 0;
+ if (c->cf->require_add_path)
+ {
+ if ((loc->add_path & BGP_ADD_PATH_RX) && !(rem->add_path & BGP_ADD_PATH_TX))
+ missing |= BGP_ADD_PATH_TX;
+
+ if ((loc->add_path & BGP_ADD_PATH_TX) && !(rem->add_path & BGP_ADD_PATH_RX))
+ missing |= BGP_ADD_PATH_RX;
+ }
+
+ if (missing != 0)
+ AF_CAPS_CHECK_FAIL(c->afi, add_path, missing);
count++;
}
}
/* We need at least one channel active */
- if (!count)
- return 0;
+ if (count == 0)
+ {
+ if (!first_active_channel_afi)
+ return 0;
+
+ if (failed_caps)
+ AF_CAPS_CHECK_FAIL(first_active_channel_afi, ready, 1);
+ }
return 1;
+
+#undef CAPS_CHECK_FAIL
+#undef AF_CAPS_CHECK_FAIL
}
static int
if (!id || (p->is_internal && id == p->local_id))
{ bgp_error(conn, 2, 3, pkt+24, -4); return; }
+ /* Instance of bgp_caps with single bgp_af_caps entry used to store missing capabilities */
+ struct bgp_caps *failed_caps = allocz(sizeof(*failed_caps) + sizeof(failed_caps->af_data[0]));
+ failed_caps->role = BGP_ROLE_UNDEFINED;
+
/* RFC 5492 5 - check for required capabilities */
- if (p->cf->capabilities && !bgp_check_capabilities(conn))
+ if (p->cf->capabilities && !bgp_check_capabilities(conn, failed_caps))
{ bgp_error(conn, 2, 7, NULL, 0); return; }
struct bgp_caps *caps = conn->remote_caps;