diff options
Diffstat (limited to 'proto/bgp')
-rw-r--r-- | proto/bgp/attrs.c | 28 | ||||
-rw-r--r-- | proto/bgp/bgp.c | 16 | ||||
-rw-r--r-- | proto/bgp/bgp.h | 9 | ||||
-rw-r--r-- | proto/bgp/config.Y | 9 | ||||
-rw-r--r-- | proto/bgp/packets.c | 112 |
5 files changed, 155 insertions, 19 deletions
diff --git a/proto/bgp/attrs.c b/proto/bgp/attrs.c index d839ed0..7b49bdf 100644 --- a/proto/bgp/attrs.c +++ b/proto/bgp/attrs.c @@ -165,6 +165,22 @@ bgp_check_next_hop(struct bgp_proto *p UNUSED, byte *a, int len) #endif } +static void +bgp_format_next_hop(eattr *a, byte *buf, int buflen UNUSED) +{ + ip_addr *ipp = (ip_addr *) a->u.ptr->data; +#ifdef IPV6 + /* in IPv6, we might have two addresses in NEXT HOP */ + if ((a->u.ptr->length == NEXT_HOP_LENGTH) && ipa_nonzero(ipp[1])) + { + bsprintf(buf, "%I %I", ipp[0], ipp[1]); + return; + } +#endif + + bsprintf(buf, "%I", ipp[0]); +} + static int bgp_check_aggregator(struct bgp_proto *p, byte *a UNUSED, int len) { @@ -234,7 +250,7 @@ static struct attr_desc bgp_attr_table[] = { { "as_path", -1, BAF_TRANSITIVE, EAF_TYPE_AS_PATH, 1, /* BA_AS_PATH */ NULL, NULL }, /* is checked by validate_as_path() as a special case */ { "next_hop", 4, BAF_TRANSITIVE, EAF_TYPE_IP_ADDRESS, 1, /* BA_NEXT_HOP */ - bgp_check_next_hop, NULL }, + bgp_check_next_hop, bgp_format_next_hop }, { "med", 4, BAF_OPTIONAL, EAF_TYPE_INT, 1, /* BA_MULTI_EXIT_DISC */ NULL, NULL }, { "local_pref", 4, BAF_TRANSITIVE, EAF_TYPE_INT, 0, /* BA_LOCAL_PREF */ @@ -1044,7 +1060,6 @@ bgp_rte_better(rte *new, rte *old) return 0; /* RFC 4271 9.1.2.2. c) Compare MED's */ - if (bgp_get_neighbor(new) == bgp_get_neighbor(old)) { x = ea_find(new->attrs->eattrs, EA_CODE(EAP_BGP, BA_MULTI_EXIT_DISC)); @@ -1082,12 +1097,19 @@ bgp_rte_better(rte *new, rte *old) y = ea_find(old->attrs->eattrs, EA_CODE(EAP_BGP, BA_ORIGINATOR_ID)); n = x ? x->u.data : new_bgp->remote_id; o = y ? y->u.data : old_bgp->remote_id; + + /* RFC 5004 - prefer older routes */ + /* (if both are external and from different peer) */ + if ((new_bgp->cf->prefer_older || old_bgp->cf->prefer_older) && + !new_bgp->is_internal && n != o) + return 0; + + /* rest of RFC 4271 9.1.2.2. f) */ if (n < o) return 1; if (n > o) return 0; - /* RFC 4271 9.1.2.2. g) Compare peer IP adresses */ return (ipa_compare(new_bgp->cf->remote_ip, old_bgp->cf->remote_ip) < 0); } diff --git a/proto/bgp/bgp.c b/proto/bgp/bgp.c index b38c6b1..24cd202 100644 --- a/proto/bgp/bgp.c +++ b/proto/bgp/bgp.c @@ -676,6 +676,17 @@ bgp_neigh_notify(neighbor *n) } } +static int +bgp_reload_routes(struct proto *P) +{ + struct bgp_proto *p = (struct bgp_proto *) P; + if (!p->conn || !p->conn->peer_refresh_support) + return 0; + + bgp_schedule_packet(p->conn, PKT_ROUTE_REFRESH); + return 1; +} + static void bgp_start_locked(struct object_lock *lock) { @@ -792,6 +803,7 @@ bgp_init(struct proto_config *C) P->rte_better = bgp_rte_better; P->import_control = bgp_import_control; P->neigh_notify = bgp_neigh_notify; + P->reload_routes = bgp_reload_routes; p->cf = c; p->local_as = c->local_as; p->remote_as = c->remote_as; @@ -886,6 +898,10 @@ bgp_check(struct bgp_config *c) if ((c->local_as == c->remote_as) && (c->rs_client)) cf_error("Only external neighbor can be RS client"); + + /* Different default based on rs_client */ + if (c->missing_lladdr == 0) + c->missing_lladdr = c->rs_client ? MLL_DROP : MLL_SELF; } static char *bgp_state_names[] = { "Idle", "Connect", "Active", "OpenSent", "OpenConfirm", "Established", "Close" }; diff --git a/proto/bgp/bgp.h b/proto/bgp/bgp.h index 0a82be2..59ec9c1 100644 --- a/proto/bgp/bgp.h +++ b/proto/bgp/bgp.h @@ -23,10 +23,13 @@ struct bgp_config { ip_addr multihop_via; /* Multihop: address to route to */ ip_addr source_addr; /* Source address to use */ int next_hop_self; /* Always set next hop to local IP address */ + int missing_lladdr; /* What we will do when we don' know link-local addr, see MLL_* */ int compare_path_lengths; /* Use path lengths when selecting best route */ + int prefer_older; /* Prefer older routes according to RFC 5004 */ u32 default_local_pref; /* Default value for LOCAL_PREF attribute */ u32 default_med; /* Default value for MULTI_EXIT_DISC attribute */ int capabilities; /* Enable capability handshake [RFC3392] */ + int enable_refresh; /* Enable local support for route refresh [RFC2918] */ int enable_as4; /* Enable local support for 4B AS numbers [RFC4893] */ u32 rr_cluster_id; /* Route reflector cluster ID, if different from local ID */ int rr_client; /* Whether neighbor is RR client of me */ @@ -45,6 +48,10 @@ struct bgp_config { char *password; /* Password used for MD5 authentication */ }; +#define MLL_SELF 1 +#define MLL_DROP 2 +#define MLL_IGNORE 3 + struct bgp_conn { struct bgp_proto *bgp; struct birdsock *sk; @@ -60,6 +67,7 @@ struct bgp_conn { 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] */ + int peer_refresh_support; /* Peer supports route refresh [RFC2918] */ unsigned hold_time, keepalive_time; /* Times calculated from my and neighbor's requirements */ }; @@ -196,6 +204,7 @@ void bgp_log_error(struct bgp_proto *p, u8 class, char *msg, unsigned code, unsi #define PKT_UPDATE 0x02 #define PKT_NOTIFICATION 0x03 #define PKT_KEEPALIVE 0x04 +#define PKT_ROUTE_REFRESH 0x05 #define PKT_SCHEDULE_CLOSE 0x1f /* Used internally to schedule socket close */ /* Attributes */ diff --git a/proto/bgp/config.Y b/proto/bgp/config.Y index 7360820..3c73d60 100644 --- a/proto/bgp/config.Y +++ b/proto/bgp/config.Y @@ -22,7 +22,8 @@ CF_KEYWORDS(BGP, LOCAL, NEIGHBOR, AS, HOLD, TIME, CONNECT, RETRY, KEEPALIVE, BGP_PATH, BGP_LOCAL_PREF, BGP_MED, BGP_ORIGIN, BGP_NEXT_HOP, BGP_ATOMIC_AGGR, BGP_AGGREGATOR, BGP_COMMUNITY, SOURCE, ADDRESS, PASSWORD, RR, RS, CLIENT, CLUSTER, ID, AS4, ADVERTISE, IPV4, - CAPABILITIES, LIMIT, PASSIVE) + CAPABILITIES, LIMIT, PASSIVE, PREFER, OLDER, MISSING, LLADDR, + DROP, IGNORE, ROUTE, REFRESH) CF_GRAMMAR @@ -39,6 +40,7 @@ bgp_proto_start: proto_start BGP { BGP_CFG->error_amnesia_time = 300; BGP_CFG->error_delay_time_min = 60; BGP_CFG->error_delay_time_max = 300; + BGP_CFG->enable_refresh = 1; BGP_CFG->enable_as4 = bgp_as4_support; BGP_CFG->capabilities = 2; BGP_CFG->advertise_ipv4 = 1; @@ -64,7 +66,11 @@ bgp_proto: | bgp_proto KEEPALIVE TIME expr ';' { BGP_CFG->keepalive_time = $4; } | bgp_proto MULTIHOP expr VIA ipa ';' { BGP_CFG->multihop = $3; BGP_CFG->multihop_via = $5; } | bgp_proto NEXT HOP SELF ';' { BGP_CFG->next_hop_self = 1; } + | bgp_proto MISSING LLADDR SELF ';' { BGP_CFG->missing_lladdr = MLL_SELF; } + | bgp_proto MISSING LLADDR DROP ';' { BGP_CFG->missing_lladdr = MLL_DROP; } + | bgp_proto MISSING LLADDR IGNORE ';' { BGP_CFG->missing_lladdr = MLL_IGNORE; } | bgp_proto PATH METRIC bool ';' { BGP_CFG->compare_path_lengths = $4; } + | bgp_proto PREFER OLDER bool ';' { BGP_CFG->prefer_older = $4; } | bgp_proto DEFAULT BGP_MED expr ';' { BGP_CFG->default_med = $4; } | bgp_proto DEFAULT BGP_LOCAL_PREF expr ';' { BGP_CFG->default_local_pref = $4; } | bgp_proto SOURCE ADDRESS ipa ';' { BGP_CFG->source_addr = $4; } @@ -72,6 +78,7 @@ bgp_proto: | bgp_proto ERROR FORGET TIME expr ';' { BGP_CFG->error_amnesia_time = $5; } | bgp_proto ERROR WAIT TIME expr ',' expr ';' { BGP_CFG->error_delay_time_min = $5; BGP_CFG->error_delay_time_max = $7; } | bgp_proto DISABLE AFTER ERROR bool ';' { BGP_CFG->disable_after_error = $5; } + | bgp_proto ENABLE ROUTE REFRESH bool ';' { BGP_CFG->enable_refresh = $5; } | bgp_proto ENABLE AS4 bool ';' { BGP_CFG->enable_as4 = $4; } | bgp_proto CAPABILITIES bool ';' { BGP_CFG->capabilities = $3; } | bgp_proto ADVERTISE IPV4 bool ';' { BGP_CFG->advertise_ipv4 = $4; } diff --git a/proto/bgp/packets.c b/proto/bgp/packets.c index d126fe5..91b4792 100644 --- a/proto/bgp/packets.c +++ b/proto/bgp/packets.c @@ -64,6 +64,14 @@ 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) +{ + *buf++ = 2; /* Capability 2: Support for route refresh */ + *buf++ = 0; /* Capability data length */ + return buf; +} + +static byte * bgp_put_cap_as4(struct bgp_conn *conn, byte *buf) { *buf++ = 65; /* Capability 65: Support for 4-octet AS number */ @@ -105,6 +113,9 @@ bgp_create_open(struct bgp_conn *conn, byte *buf) cap = bgp_put_cap_ipv6(conn, cap); #endif + if (p->cf->enable_refresh) + cap = bgp_put_cap_rr(conn, cap); + if (conn->want_as4_support) cap = bgp_put_cap_as4(conn, cap); @@ -199,7 +210,7 @@ bgp_create_update(struct bgp_conn *conn, byte *buf) if (a_size < 0) { - log(L_ERR "%s: Attribute list too long, skipping corresponding route group", p->p.name); + log(L_ERR "%s: Attribute list too long, skipping corresponding routes", p->p.name); bgp_flush_prefixes(p, buck); rem_node(&buck->send_node); bgp_free_bucket(p, buck); @@ -234,9 +245,9 @@ bgp_create_update(struct bgp_conn *conn, byte *buf) { struct bgp_proto *p = conn->bgp; struct bgp_bucket *buck; - int size, second; + int size, second, rem_stored; int remains = BGP_MAX_PACKET_LENGTH - BGP_HEADER_LENGTH - 4; - byte *w, *tmp, *tstart; + byte *w, *w_stored, *tmp, *tstart; ip_addr *ipp, ip, ip_ll; ea_list *ea; eattr *nh; @@ -272,28 +283,25 @@ bgp_create_update(struct bgp_conn *conn, byte *buf) } DBG("Processing bucket %p\n", buck); - size = bgp_encode_attrs(p, w, buck->eattrs, 2048); + rem_stored = remains; + w_stored = w; + size = bgp_encode_attrs(p, w, buck->eattrs, 2048); if (size < 0) { - log(L_ERR "%s: Attribute list too long, ignoring corresponding route group", p->p.name); + log(L_ERR "%s: Attribute list too long, skipping corresponding routes", p->p.name); bgp_flush_prefixes(p, buck); rem_node(&buck->send_node); bgp_free_bucket(p, buck); continue; } - w += size; remains -= size; - tstart = tmp = bgp_attach_attr_wa(&ea, bgp_linpool, BA_MP_REACH_NLRI, remains-8); - *tmp++ = 0; - *tmp++ = BGP_AF_IPV6; - *tmp++ = 1; - nh = ea_find(buck->eattrs, EA_CODE(EAP_BGP, BA_NEXT_HOP)); - ASSERT(nh); - /* We have two addresses here in 'nh'. Really. + /* We have two addresses here in NEXT_HOP eattr. Really. Unless NEXT_HOP was modified by filter */ + nh = ea_find(buck->eattrs, EA_CODE(EAP_BGP, BA_NEXT_HOP)); + ASSERT(nh); second = (nh->u.ptr->length == NEXT_HOP_LENGTH); ipp = (ip_addr *) nh->u.ptr->data; ip = ipp[0]; @@ -322,12 +330,32 @@ bgp_create_update(struct bgp_conn *conn, byte *buf) ip_ll = ipp[1]; else { - ip = p->source_addr; - ip_ll = p->local_link; + switch (p->cf->missing_lladdr) + { + case MLL_SELF: + ip = p->source_addr; + ip_ll = p->local_link; + break; + case MLL_DROP: + log(L_ERR "%s: Missing link-local next hop address, skipping corresponding routes", p->p.name); + w = w_stored; + remains = rem_stored; + bgp_flush_prefixes(p, buck); + rem_node(&buck->send_node); + bgp_free_bucket(p, buck); + continue; + case MLL_IGNORE: + break; + } } } } + tstart = tmp = bgp_attach_attr_wa(&ea, bgp_linpool, BA_MP_REACH_NLRI, remains-8); + *tmp++ = 0; + *tmp++ = BGP_AF_IPV6; + *tmp++ = 1; + if (ipa_nonzero(ip_ll)) { *tmp++ = 32; @@ -369,6 +397,24 @@ bgp_create_update(struct bgp_conn *conn, byte *buf) #endif +static byte * +bgp_create_route_refresh(struct bgp_conn *conn, byte *buf) +{ + struct bgp_proto *p = conn->bgp; + BGP_TRACE(D_PACKETS, "Sending ROUTE-REFRESH"); + +#ifdef IPV6 + *buf++ = 0; /* AFI IPv6 */ + *buf++ = BGP_AF_IPV6; +#else + *buf++ = 0; /* AFI IPv4 */ + *buf++ = BGP_AF_IPV4; +#endif + *buf++ = 0; /* RFU */ + *buf++ = 1; /* and SAFI 1 */ + return buf; +} + static void bgp_create_header(byte *buf, unsigned int len, unsigned int type) { @@ -430,6 +476,12 @@ bgp_fire_tx(struct bgp_conn *conn) type = PKT_OPEN; end = bgp_create_open(conn, pkt); } + else if (s & (1 << PKT_ROUTE_REFRESH)) + { + s &= ~(1 << PKT_ROUTE_REFRESH); + type = PKT_ROUTE_REFRESH; + end = bgp_create_route_refresh(conn, pkt); + } else if (s & (1 << PKT_UPDATE)) { end = bgp_create_update(conn, pkt); @@ -500,6 +552,11 @@ bgp_parse_capabilities(struct bgp_conn *conn, byte *opt, int len) switch (opt[0]) { + case 2: + if (cl != 0) + goto err; + conn->peer_refresh_support = 1; + break; case 65: if (cl != 4) goto err; @@ -1067,6 +1124,30 @@ bgp_rx_keepalive(struct bgp_conn *conn) } } +static void +bgp_rx_route_refresh(struct bgp_conn *conn, byte *pkt, int len) +{ + struct bgp_proto *p = conn->bgp; + + BGP_TRACE(D_PACKETS, "Got ROUTE-REFRESH"); + + if (conn->state != BS_ESTABLISHED) + { bgp_error(conn, 5, 0, NULL, 0); return; } + + if (!p->cf->enable_refresh) + { bgp_error(conn, 1, 3, pkt+18, 1); return; } + + if (len != (BGP_HEADER_LENGTH + 4)) + { bgp_error(conn, 1, 2, pkt+16, 2); return; } + + /* FIXME - we ignore AFI/SAFI values, as we support + just one value and even an error code for an invalid + request is not defined */ + + proto_request_feeding(&p->p); +} + + /** * bgp_rx_packet - handle a received packet * @conn: BGP connection @@ -1086,6 +1167,7 @@ bgp_rx_packet(struct bgp_conn *conn, byte *pkt, unsigned len) case PKT_UPDATE: return bgp_rx_update(conn, pkt, len); case PKT_NOTIFICATION: return bgp_rx_notification(conn, pkt, len); case PKT_KEEPALIVE: return bgp_rx_keepalive(conn); + case PKT_ROUTE_REFRESH: return bgp_rx_route_refresh(conn, pkt, len); default: bgp_error(conn, 1, 3, pkt+18, 1); } } |