summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--proto/bgp/bgp.c4
-rw-r--r--proto/bgp/bgp.h5
-rw-r--r--proto/bgp/packets.c49
3 files changed, 47 insertions, 11 deletions
diff --git a/proto/bgp/bgp.c b/proto/bgp/bgp.c
index d1b7dee..558b1e8 100644
--- a/proto/bgp/bgp.c
+++ b/proto/bgp/bgp.c
@@ -368,6 +368,10 @@ bgp_conn_enter_idle_state(struct bgp_conn *conn)
static void
bgp_send_open(struct bgp_conn *conn)
{
+ conn->start_state = conn->bgp->start_state;
+ conn->want_as4_support = conn->bgp->cf->enable_as4 && (conn->start_state != BSS_CONNECT_NOCAP);
+ conn->peer_as4_support = 0; // Default value, possibly changed by receiving capability.
+
DBG("BGP: Sending open\n");
conn->sk->rx_hook = bgp_rx;
conn->sk->tx_hook = bgp_tx;
diff --git a/proto/bgp/bgp.h b/proto/bgp/bgp.h
index 5c180cc..b706caf 100644
--- a/proto/bgp/bgp.h
+++ b/proto/bgp/bgp.h
@@ -51,9 +51,10 @@ struct bgp_conn {
int packets_to_send; /* Bitmap of packet types to be sent */
int notify_code, notify_subcode, notify_size;
byte *notify_data;
- int error_flag; /* Error state, ignore all input */
u32 advertised_as; /* Temporary value for AS number received */
- int as4_support; /* Peer supports 4B AS numbers [RFC4893] */
+ int start_state; /* protocol start_state snapshot when connection established */
+ int want_as4_support; /* Connection tries to establish AS4 session */
+ int peer_as4_support; /* Peer supports 4B AS numbers [RFC4893] */
unsigned hold_time, keepalive_time; /* Times calculated from my and neighbor's requirements */
};
diff --git a/proto/bgp/packets.c b/proto/bgp/packets.c
index 4e42d90..27c0755 100644
--- a/proto/bgp/packets.c
+++ b/proto/bgp/packets.c
@@ -69,13 +69,21 @@ bgp_create_open(struct bgp_conn *conn, byte *buf)
put_u16(buf+1, (p->local_as < 0xFFFF) ? p->local_as : AS_TRANS);
put_u16(buf+3, p->cf->hold_time);
put_u32(buf+5, p->local_id);
+
+ if (conn->start_state == BSS_CONNECT_NOCAP)
+ {
+ BGP_TRACE(D_PACKETS, "Skipping capabilities");
+ buf[9] = 0;
+ return buf + 10;
+ }
+
/* Skipped 3 B for length field and Capabilities parameter header */
cap = buf + 12;
#ifdef IPV6
cap = bgp_put_cap_ipv6(conn, cap);
#endif
- if (p->cf->enable_as4)
+ if (conn->want_as4_support)
cap = bgp_put_cap_as4(conn, cap);
cap_len = cap - buf - 12;
@@ -418,9 +426,8 @@ bgp_parse_capabilities(struct bgp_conn *conn, byte *opt, int len)
case 65:
if (cl != 4)
goto err;
- conn->as4_support = 1;
-
- if (p->cf->enable_as4)
+ conn->peer_as4_support = 1;
+ if (conn->want_as4_support)
conn->advertised_as = get_u32(opt + 2);
break;
@@ -501,7 +508,6 @@ bgp_rx_open(struct bgp_conn *conn, byte *pkt, int len)
id = get_u32(pkt+24);
BGP_TRACE(D_PACKETS, "Got OPEN(as=%d,hold=%d,id=%08x)", conn->advertised_as, hold, id);
- conn->as4_support = 0; // Default value, possibly changed by capability.
if (bgp_parse_options(conn, pkt+29, pkt[28]))
return;
@@ -548,8 +554,8 @@ bgp_rx_open(struct bgp_conn *conn, byte *pkt, int len)
conn->hold_time = MIN(hold, p->cf->hold_time);
conn->keepalive_time = p->cf->keepalive_time ? : conn->hold_time / 3;
p->remote_id = id;
- p->as4_session = p->cf->enable_as4 && conn->as4_support;
-
+ p->as4_session = conn->want_as4_support && conn->peer_as4_support;
+
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, p->as4_session);
bgp_schedule_packet(conn, PKT_KEEPALIVE);
@@ -820,7 +826,15 @@ static struct {
{ 3, 11, "Malformed AS_PATH" },
{ 4, 0, "Hold timer expired" },
{ 5, 0, "Finite state machine error" },
- { 6, 0, "Cease" }
+ { 6, 0, "Cease" }, /* Subcodes are according to [RFC4486] */
+ { 6, 1, "Maximum number of prefixes reached" },
+ { 6, 2, "Administrative shutdown" },
+ { 6, 3, "Peer de-configured" },
+ { 6, 4, "Administrative reset" },
+ { 6, 5, "Connection rejected" },
+ { 6, 6, "Other configuration change" },
+ { 6, 7, "Connection collision resolution" },
+ { 6, 8, "Out of Resources" }
};
/**
@@ -875,6 +889,7 @@ bgp_log_error(struct bgp_proto *p, char *msg, unsigned code, unsigned subcode, b
static void
bgp_rx_notification(struct bgp_conn *conn, byte *pkt, int len)
{
+ struct bgp_proto *p = conn->bgp;
if (len < 21)
{
bgp_error(conn, 1, 2, pkt+16, 2);
@@ -883,9 +898,25 @@ bgp_rx_notification(struct bgp_conn *conn, byte *pkt, int len)
unsigned code = pkt[19];
unsigned subcode = pkt[20];
+ int delay = 1;
+
+#ifndef IPV6
+ if ((code == 2) && ((subcode == 4) || (subcode == 7)))
+ {
+ /* Error related to capability:
+ * 4 - Peer does not support capabilities at all.
+ * 7 - Peer request some capability. Strange unless it is IPv6 only peer.
+ * We try connect without capabilities
+ */
+ BGP_TRACE(D_EVENTS, "Capability related error received, capabilities disabled");
+ conn->bgp->start_state = BSS_CONNECT_NOCAP;
+ delay = 0;
+ }
+#endif
+
bgp_log_error(conn->bgp, "Received error notification", code, subcode, pkt+21, len-21);
bgp_store_error(conn->bgp, conn, BE_BGP_RX, (code << 16) | subcode);
- bgp_update_startup_delay(conn->bgp, conn, code, subcode);
+ if (delay) bgp_update_startup_delay(conn->bgp, conn, code, subcode);
bgp_conn_enter_close_state(conn);
bgp_schedule_packet(conn, PKT_SCHEDULE_CLOSE);
}