diff options
author | Ondrej Zajicek <santiago@crfreenet.org> | 2014-03-20 14:07:12 +0100 |
---|---|---|
committer | Ondrej Zajicek <santiago@crfreenet.org> | 2014-03-20 14:07:12 +0100 |
commit | 0c791f873aeb7c1052c97db7da4fe23873d69603 (patch) | |
tree | 48496c5965cb6e9f54d7863827c35054c3697c19 /proto/bgp/packets.c | |
parent | 4e398e34bf140baf73fe8dceaf81078fb343f65a (diff) |
BGP graceful restart support.
Also significant core protocol state changes needed for that,
global graceful restart recovery state and kernel proto support
for recovery.
Diffstat (limited to 'proto/bgp/packets.c')
-rw-r--r-- | proto/bgp/packets.c | 150 |
1 files changed, 135 insertions, 15 deletions
diff --git a/proto/bgp/packets.c b/proto/bgp/packets.c index 649d8078..2d4da8c9 100644 --- a/proto/bgp/packets.c +++ b/proto/bgp/packets.c @@ -122,7 +122,7 @@ bgp_create_notification(struct bgp_conn *conn, byte *buf) #ifdef IPV6 static byte * -bgp_put_cap_ipv6(struct bgp_conn *conn UNUSED, byte *buf) +bgp_put_cap_ipv6(struct bgp_proto *p UNUSED, byte *buf) { *buf++ = 1; /* Capability 1: Multiprotocol extensions */ *buf++ = 4; /* Capability data length */ @@ -136,7 +136,7 @@ bgp_put_cap_ipv6(struct bgp_conn *conn UNUSED, byte *buf) #else static byte * -bgp_put_cap_ipv4(struct bgp_conn *conn UNUSED, byte *buf) +bgp_put_cap_ipv4(struct bgp_proto *p UNUSED, byte *buf) { *buf++ = 1; /* Capability 1: Multiprotocol extensions */ *buf++ = 4; /* Capability data length */ @@ -149,7 +149,7 @@ bgp_put_cap_ipv4(struct bgp_conn *conn UNUSED, byte *buf) #endif static byte * -bgp_put_cap_rr(struct bgp_conn *conn UNUSED, byte *buf) +bgp_put_cap_rr(struct bgp_proto *p UNUSED, byte *buf) { *buf++ = 2; /* Capability 2: Support for route refresh */ *buf++ = 0; /* Capability data length */ @@ -157,16 +157,44 @@ bgp_put_cap_rr(struct bgp_conn *conn UNUSED, byte *buf) } static byte * -bgp_put_cap_as4(struct bgp_conn *conn, byte *buf) +bgp_put_cap_gr1(struct bgp_proto *p, byte *buf) +{ + *buf++ = 64; /* Capability 64: Support for graceful restart */ + *buf++ = 6; /* Capability data length */ + + put_u16(buf, p->cf->gr_time); + if (p->p.gr_recovery) + buf[0] |= BGP_GRF_RESTART; + buf += 2; + + *buf++ = 0; /* Appropriate AF */ + *buf++ = BGP_AF; + *buf++ = 1; /* and SAFI 1 */ + *buf++ = p->p.gr_recovery ? BGP_GRF_FORWARDING : 0; + + return buf; +} + +static byte * +bgp_put_cap_gr2(struct bgp_proto *p, byte *buf) +{ + *buf++ = 64; /* Capability 64: Support for graceful restart */ + *buf++ = 2; /* Capability data length */ + put_u16(buf, 0); + return buf + 2; +} + +static byte * +bgp_put_cap_as4(struct bgp_proto *p, byte *buf) { *buf++ = 65; /* Capability 65: Support for 4-octet AS number */ *buf++ = 4; /* Capability data length */ - put_u32(buf, conn->bgp->local_as); + put_u32(buf, p->local_as); return buf + 4; } static byte * -bgp_put_cap_add_path(struct bgp_conn *conn, byte *buf) +bgp_put_cap_add_path(struct bgp_proto *p, byte *buf) { *buf++ = 69; /* Capability 69: Support for ADD-PATH */ *buf++ = 4; /* Capability data length */ @@ -175,7 +203,7 @@ bgp_put_cap_add_path(struct bgp_conn *conn, byte *buf) *buf++ = BGP_AF; *buf++ = 1; /* SAFI 1 */ - *buf++ = conn->bgp->cf->add_path; + *buf++ = p->cf->add_path; return buf; } @@ -206,21 +234,26 @@ bgp_create_open(struct bgp_conn *conn, byte *buf) #ifndef IPV6 if (p->cf->advertise_ipv4) - cap = bgp_put_cap_ipv4(conn, cap); + cap = bgp_put_cap_ipv4(p, cap); #endif #ifdef IPV6 - cap = bgp_put_cap_ipv6(conn, cap); + cap = bgp_put_cap_ipv6(p, cap); #endif if (p->cf->enable_refresh) - cap = bgp_put_cap_rr(conn, cap); + cap = bgp_put_cap_rr(p, cap); + + if (p->cf->gr_mode == BGP_GR_ABLE) + cap = bgp_put_cap_gr1(p, cap); + else if (p->cf->gr_mode == BGP_GR_AWARE) + cap = bgp_put_cap_gr2(p, cap); if (p->cf->enable_as4) - cap = bgp_put_cap_as4(conn, cap); + cap = bgp_put_cap_as4(p, cap); if (p->cf->add_path) - cap = bgp_put_cap_add_path(conn, cap); + cap = bgp_put_cap_add_path(p, cap); cap_len = cap - buf - 12; if (cap_len > 0) @@ -351,6 +384,16 @@ bgp_create_update(struct bgp_conn *conn, byte *buf) return NULL; } +static byte * +bgp_create_end_mark(struct bgp_conn *conn, byte *buf) +{ + struct bgp_proto *p = conn->bgp; + BGP_TRACE(D_PACKETS, "Sending End-of-RIB"); + + put_u32(buf, 0); + return buf+4; +} + #else /* IPv6 version */ static inline int @@ -520,6 +563,26 @@ bgp_create_update(struct bgp_conn *conn, byte *buf) return NULL; } +static byte * +bgp_create_end_mark(struct bgp_conn *conn, byte *buf) +{ + struct bgp_proto *p = conn->bgp; + BGP_TRACE(D_PACKETS, "Sending End-of-RIB"); + + put_u16(buf+0, 0); + put_u16(buf+2, 6); /* length 4-9 */ + buf += 4; + + /* Empty MP_UNREACH_NLRI atribute */ + *buf++ = BAF_OPTIONAL; + *buf++ = BA_MP_UNREACH_NLRI; + *buf++ = 3; /* Length 7-9 */ + *buf++ = 0; /* AFI */ + *buf++ = BGP_AF_IPV6; + *buf++ = 1; /* SAFI */ + return buf; +} + #endif static byte * @@ -606,10 +669,16 @@ bgp_fire_tx(struct bgp_conn *conn) { end = bgp_create_update(conn, pkt); type = PKT_UPDATE; + if (!end) { conn->packets_to_send = 0; - return 0; + + if (!p->send_end_mark) + return 0; + + p->send_end_mark = 0; + end = bgp_create_end_mark(conn, pkt); } } else @@ -678,6 +747,22 @@ bgp_parse_capabilities(struct bgp_conn *conn, byte *opt, int len) conn->peer_refresh_support = 1; break; + case 64: /* Graceful restart capability, RFC 4724 */ + if (cl % 4 != 2) + goto err; + conn->peer_gr_aware = 1; + conn->peer_gr_able = 0; + conn->peer_gr_time = get_u16(opt + 2) & 0x0fff; + conn->peer_gr_flags = opt[2] & 0xf0; + conn->peer_gr_aflags = 0; + for (i = 2; i < cl; i += 4) + if (opt[2+i+0] == 0 && opt[2+i+1] == BGP_AF && opt[2+i+2] == 1) /* Match AFI/SAFI */ + { + conn->peer_gr_able = 1; + conn->peer_gr_aflags = opt[2+i+3]; + } + break; + case 65: /* AS4 capability, RFC 4893 */ if (cl != 4) goto err; @@ -704,7 +789,7 @@ bgp_parse_capabilities(struct bgp_conn *conn, byte *opt, int len) } return; - err: + err: bgp_error(conn, 2, 0, NULL, 0); return; } @@ -807,12 +892,17 @@ bgp_rx_open(struct bgp_conn *conn, byte *pkt, int len) other = (conn == &p->outgoing_conn) ? &p->incoming_conn : &p->outgoing_conn; switch (other->state) { - case BS_IDLE: case BS_CONNECT: case BS_ACTIVE: + /* Stop outgoing connection attempts */ + bgp_conn_enter_idle_state(other); + break; + + case BS_IDLE: case BS_OPENSENT: case BS_CLOSE: break; + case BS_OPENCONFIRM: if ((p->local_id < id) == (conn == &p->incoming_conn)) { @@ -838,6 +928,7 @@ bgp_rx_open(struct bgp_conn *conn, byte *pkt, int len) p->as4_session = p->cf->enable_as4 && conn->peer_as4_support; p->add_path_rx = (p->cf->add_path & ADD_PATH_RX) && (conn->peer_add_path & ADD_PATH_TX); p->add_path_tx = (p->cf->add_path & ADD_PATH_TX) && (conn->peer_add_path & ADD_PATH_RX); + p->gr_ready = p->cf->gr_mode && conn->peer_gr_able; if (p->add_path_tx) p->p.accept_ra_types = RA_ANY; @@ -849,6 +940,20 @@ bgp_rx_open(struct bgp_conn *conn, byte *pkt, int len) bgp_conn_enter_openconfirm_state(conn); } + +static inline void +bgp_rx_end_mark(struct bgp_proto *p) +{ + BGP_TRACE(D_PACKETS, "Got End-of-RIB"); + + if (p->p.gr_recovery) + proto_graceful_restart_unlock(&p->p); + + if (p->gr_active) + bgp_graceful_restart_done(p); +} + + #define DECODE_PREFIX(pp, ll) do { \ if (p->add_path_rx) \ { \ @@ -983,6 +1088,13 @@ bgp_do_rx_update(struct bgp_conn *conn, u32 path_id = 0; u32 last_id = 0; + /* Check for End-of-RIB marker */ + if (!withdrawn_len && !attr_len && !nlri_len) + { + bgp_rx_end_mark(p); + return; + } + /* Withdraw routes */ while (withdrawn_len) { @@ -1088,6 +1200,14 @@ bgp_do_rx_update(struct bgp_conn *conn, if (conn->state != BS_ESTABLISHED) /* fatal error during decoding */ return; + /* Check for End-of-RIB marker */ + if ((attr_len < 8) && !withdrawn_len && !attr_len && + (p->mp_unreach_len == 3) && (get_u16(p->mp_unreach_start) == BGP_AF_IPV6)) + { + bgp_rx_end_mark(p); + return; + } + DO_NLRI(mp_unreach) { while (len) |