Commit 11b32d91 authored by Ondřej Zajíček's avatar Ondřej Zajíček

Major changes to BGP

Fixes two race conditions causing crash of Bird, several unhandled
cases during BGP initialization, and some other bugs. Also changes
handling of startup delay to be more useful and implement
reporting of last error in 'show protocols' command.
parent b933281e
......@@ -365,6 +365,7 @@ bgp_encode_attrs(struct bgp_proto *p, byte *w, ea_list *attrs, int remains)
int new_used;
int nl = as_path_convert_to_old(a->u.ptr, buf, &new_used);
DBG("BGP: Encoding old AS_PATH\n");
rv = bgp_encode_attr_hdr(w, BAF_TRANSITIVE, BA_AS_PATH, nl);
ADVANCE(w, remains, rv);
memcpy(w, buf, nl);
......@@ -381,6 +382,7 @@ bgp_encode_attrs(struct bgp_proto *p, byte *w, ea_list *attrs, int remains)
* discarded in bgp_check_as_path().
*/
DBG("BGP: Encoding AS4_PATH\n");
rv = bgp_encode_attr_hdr(w, BAF_OPTIONAL | BAF_TRANSITIVE, BA_AS4_PATH, len);
ADVANCE(w, remains, rv);
memcpy(w, a->u.ptr->data, len);
......
This diff is collapsed.
......@@ -47,12 +47,13 @@ struct bgp_conn {
struct timer *connect_retry_timer;
struct timer *hold_timer;
struct timer *keepalive_timer;
struct event *tx_ev;
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 */
int primary; /* This connection is primary */
u32 advertised_as; /* Temporary value for AS number received */
int as4_support; /* Peer supports 4B AS numbers [RFC4893] */
unsigned hold_time, keepalive_time; /* Times calculated from my and neighbor's requirements */
};
......@@ -60,8 +61,8 @@ struct bgp_proto {
struct proto p;
struct bgp_config *cf; /* Shortcut to BGP configuration */
u32 local_as, remote_as;
int start_state; /* Substates that partitions BS_START */
int is_internal; /* Internal BGP connection (local_as == remote_as) */
int as4_support; /* Peer supports 4B AS numbers [RFC4893] */
int as4_session; /* Session uses 4B AS numbers in AS_PATH (both sides support it) */
u32 local_id; /* BGP identifier of this router */
u32 remote_id; /* BGP identifier of the neighbor */
......@@ -75,13 +76,17 @@ struct bgp_proto {
ip_addr next_hop; /* Either the peer or multihop_via */
struct neighbor *neigh; /* Neighbor entry corresponding to next_hop */
ip_addr local_addr; /* Address of the local end of the link to next_hop */
struct event *event; /* Event for respawning and shutting process */
struct bgp_bucket **bucket_hash; /* Hash table of attribute buckets */
unsigned int hash_size, hash_count, hash_limit;
struct fib prefix_fib; /* Prefixes to be sent */
list bucket_queue; /* Queue of buckets to send */
struct bgp_bucket *withdraw_bucket; /* Withdrawn routes */
unsigned startup_delay; /* Time to delay protocol startup by due to errors */
bird_clock_t last_connect; /* Time of last connect attempt */
bird_clock_t last_proto_error; /* Time of last error that leads to protocol stop */
u8 last_error_class; /* Error class of last error */
u32 last_error_code; /* Error code of last error. BGP protocol errors
are encoded as (bgp_err_code << 16 | bgp_err_subcode) */
#ifdef IPV6
byte *mp_reach_start, *mp_unreach_start; /* Multiprotocol BGP attribute notes */
unsigned mp_reach_len, mp_unreach_len;
......@@ -118,6 +123,12 @@ void bgp_start_timer(struct timer *t, int value);
void bgp_check(struct bgp_config *c);
void bgp_error(struct bgp_conn *c, unsigned code, unsigned subcode, byte *data, int len);
void bgp_close_conn(struct bgp_conn *c);
void bgp_update_startup_delay(struct bgp_proto *p, struct bgp_conn *conn, unsigned code, unsigned subcode);
void bgp_conn_enter_established_state(struct bgp_conn *conn);
void bgp_conn_enter_close_state(struct bgp_conn *conn);
void bgp_conn_enter_idle_state(struct bgp_conn *conn);
void bgp_store_error(struct bgp_proto *p, struct bgp_conn *c, u8 class, u32 code);
#ifdef LOCAL_DEBUG
#define BGP_FORCE_DEBUG 1
......@@ -147,8 +158,10 @@ inline static void bgp_attach_attr_ip(struct ea_list **to, struct linpool *pool,
/* packets.c */
void bgp_schedule_packet(struct bgp_conn *conn, int type);
void bgp_kick_tx(void *vconn);
void bgp_tx(struct birdsock *sk);
int bgp_rx(struct birdsock *sk, int size);
const byte * bgp_error_dsc(byte *buff, unsigned code, unsigned subcode);
void bgp_log_error(struct bgp_proto *p, char *msg, unsigned code, unsigned subcode, byte *data, unsigned len);
/* Packet types */
......@@ -186,7 +199,7 @@ void bgp_log_error(struct bgp_proto *p, char *msg, unsigned code, unsigned subco
#define BA_AS4_PATH 0x11 /* [RFC4893] */
#define BA_AS4_AGGREGATOR 0x12
/* BGP states */
/* BGP connection states */
#define BS_IDLE 0
#define BS_CONNECT 1 /* Attempting to connect */
......@@ -194,6 +207,38 @@ void bgp_log_error(struct bgp_proto *p, char *msg, unsigned code, unsigned subco
#define BS_OPENSENT 3
#define BS_OPENCONFIRM 4
#define BS_ESTABLISHED 5
#define BS_CLOSE 6 /* Used during transition to BS_IDLE */
/* BGP start states
*
* Used in PS_START for fine-grained specification of starting state.
*
* When BGP protocol is started by core, it goes to BSS_PREPARE. When BGP protocol
* done what is neccessary to start itself (like acquiring the lock), it goes to BSS_CONNECT.
* When some connection attempt failed because of option or capability error, it goes to
* BSS_CONNECT_NOCAP.
*/
#define BSS_PREPARE 0 /* Used before ordinary BGP started, i. e. waiting for lock */
#define BSS_CONNECT 1 /* Ordinary BGP connecting */
#define BSS_CONNECT_NOCAP 2 /* Legacy BGP connecting (without capabilities) */
/* Error classes */
#define BE_NONE 0
#define BE_MISC 1 /* Miscellaneous error */
#define BE_SOCKET 2 /* Socket error */
#define BE_BGP_RX 3 /* BGP protocol error notification received */
#define BE_BGP_TX 4 /* BGP protocol error notification sent */
#define BE_AUTO_DOWN 5 /* Automatic shutdown */
#define BE_MAN_DOWN 6 /* Manual shutdown */
/* Misc error codes */
#define BEM_NEIGHBOR_LOST 1
#define BEM_INVALID_NEXT_HOP 2
#define BEM_INVALID_MD5 3 /* MD5 authentication kernel request failed (possibly not supported */
/* Well-known communities */
......
......@@ -17,6 +17,8 @@
#include "lib/unaligned.h"
#include "lib/socket.h"
#include "nest/cli.h"
#include "bgp.h"
static byte *
......@@ -318,7 +320,8 @@ bgp_fire_tx(struct bgp_conn *conn)
if (s & (1 << PKT_SCHEDULE_CLOSE))
{
bgp_close_conn(conn);
/* We can finally close connection and enter idle state */
bgp_conn_enter_idle_state(conn);
return 0;
}
if (s & (1 << PKT_NOTIFICATION))
......@@ -371,8 +374,17 @@ bgp_schedule_packet(struct bgp_conn *conn, int type)
DBG("BGP: Scheduling packet type %d\n", type);
conn->packets_to_send |= 1 << type;
if (conn->sk && conn->sk->tpos == conn->sk->tbuf)
while (bgp_fire_tx(conn))
;
ev_schedule(conn->tx_ev);
}
void
bgp_kick_tx(void *vconn)
{
struct bgp_conn *conn = vconn;
DBG("BGP: kicking TX\n");
while (bgp_fire_tx(conn))
;
}
void
......@@ -406,9 +418,9 @@ bgp_parse_capabilities(struct bgp_conn *conn, byte *opt, int len)
case 65:
if (cl != 4)
goto err;
p->as4_support = 1;
p->as4_session = p->cf->enable_as4;
if (p->as4_session)
conn->as4_support = 1;
if (p->cf->enable_as4)
conn->advertised_as = get_u32(opt + 2);
break;
......@@ -477,7 +489,7 @@ bgp_rx_open(struct bgp_conn *conn, byte *pkt, int len)
/* Check state */
if (conn->state != BS_OPENSENT)
{ bgp_error(conn, 5, 0, NULL, 0); }
{ bgp_error(conn, 5, 0, NULL, 0); return; }
/* Check message contents */
if (len < 29 || len != 29 + pkt[28])
......@@ -489,7 +501,7 @@ 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);
p->remote_id = id; // ???
conn->as4_support = 0; // Default value, possibly changed by capability.
if (bgp_parse_options(conn, pkt+29, pkt[28]))
return;
......@@ -499,7 +511,6 @@ bgp_rx_open(struct bgp_conn *conn, byte *pkt, int len)
if (!id || id == 0xffffffff || id == p->local_id)
{ bgp_error(conn, 2, 3, pkt+24, -4); return; }
if (conn->advertised_as != p->remote_as)
{
bgp_error(conn, 2, 2, (byte *) &(conn->advertised_as), -4); return;
......@@ -513,6 +524,7 @@ bgp_rx_open(struct bgp_conn *conn, byte *pkt, int len)
case BS_CONNECT:
case BS_ACTIVE:
case BS_OPENSENT:
case BS_CLOSE:
break;
case BS_OPENCONFIRM:
if ((p->local_id < id) == (conn == &p->incoming_conn))
......@@ -532,19 +544,13 @@ bgp_rx_open(struct bgp_conn *conn, byte *pkt, int len)
bug("bgp_rx_open: Unknown state");
}
/* Make this connection primary */
conn->primary = 1;
p->conn = conn;
/* Update our local variables */
if (hold < p->cf->hold_time)
conn->hold_time = hold;
else
conn->hold_time = p->cf->hold_time;
conn->hold_time = MIN(hold, p->cf->hold_time);
conn->keepalive_time = p->cf->keepalive_time ? : conn->hold_time / 3;
// p->remote_as = conn->advertised_as;
p->remote_id = id;
DBG("BGP: Hold timer set to %d, keepalive to %d, AS to %d, ID to %x\n", conn->hold_time, conn->keepalive_time, p->remote_as, p->remote_id);
p->as4_session = p->cf->enable_as4 && conn->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);
bgp_start_timer(conn->hold_timer, conn->hold_time);
......@@ -817,24 +823,41 @@ static struct {
{ 6, 0, "Cease" }
};
/**
* bgp_error_dsc - return BGP error description
* @buff: temporary buffer
* @code: BGP error code
* @subcode: BGP error subcode
*
* bgp_error_dsc() returns error description for BGP errors
* which might be static string or given temporary buffer.
*/
const byte *
bgp_error_dsc(byte *buff, unsigned code, unsigned subcode)
{
unsigned i;
for (i=0; i < ARRAY_SIZE(bgp_msg_table); i++)
if (bgp_msg_table[i].major == code && bgp_msg_table[i].minor == subcode)
{
return bgp_msg_table[i].msg;
}
bsprintf(buff, "Unknown error %d.%d", code, subcode);
return buff;
}
void
bgp_log_error(struct bgp_proto *p, char *msg, unsigned code, unsigned subcode, byte *data, unsigned len)
{
byte *name, namebuf[16];
const byte *name;
byte namebuf[32];
byte *t, argbuf[36];
unsigned i;
if (code == 6 && !subcode) /* Don't report Cease messages */
return;
bsprintf(namebuf, "%d.%d", code, subcode);
name = namebuf;
for (i=0; i < ARRAY_SIZE(bgp_msg_table); i++)
if (bgp_msg_table[i].major == code && bgp_msg_table[i].minor == subcode)
{
name = bgp_msg_table[i].msg;
break;
}
name = bgp_error_dsc(namebuf, code, subcode);
t = argbuf;
if (len)
{
......@@ -857,10 +880,13 @@ bgp_rx_notification(struct bgp_conn *conn, byte *pkt, int len)
bgp_error(conn, 1, 2, pkt+16, 2);
return;
}
bgp_log_error(conn->bgp, "Received error notification", pkt[19], pkt[20], pkt+21, len-21);
conn->error_flag = 1;
if (conn->primary)
proto_notify_state(&conn->bgp->p, PS_STOP);
unsigned code = pkt[19];
unsigned subcode = pkt[20];
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);
bgp_conn_enter_close_state(conn);
bgp_schedule_packet(conn, PKT_SCHEDULE_CLOSE);
}
......@@ -874,10 +900,7 @@ bgp_rx_keepalive(struct bgp_conn *conn)
switch (conn->state)
{
case BS_OPENCONFIRM:
DBG("BGP: UP!!!\n");
conn->state = BS_ESTABLISHED;
bgp_attr_init(conn->bgp);
proto_notify_state(&conn->bgp->p, PS_UP);
bgp_conn_enter_established_state(conn);
break;
case BS_ESTABLISHED:
break;
......@@ -930,18 +953,8 @@ bgp_rx(sock *sk, int size)
DBG("BGP: RX hook: Got %d bytes\n", size);
while (end >= pkt_start + BGP_HEADER_LENGTH)
{
if (conn->error_flag)
{
/*
* We still need to remember the erroneous packet, so that
* we can generate error notifications properly. To avoid
* subsequent reads rewriting the buffer, we just reset the
* rx_hook.
*/
DBG("BGP: Error, dropping input\n");
sk->rx_hook = NULL;
return 0;
}
if ((conn->state == BS_CLOSE) || (conn->sk != sk))
return 0;
for(i=0; i<16; i++)
if (pkt_start[i] != 0xff)
{
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment