summaryrefslogtreecommitdiffstats
path: root/proto/bgp
diff options
context:
space:
mode:
Diffstat (limited to 'proto/bgp')
-rw-r--r--proto/bgp/attrs.c28
-rw-r--r--proto/bgp/bgp.c16
-rw-r--r--proto/bgp/bgp.h9
-rw-r--r--proto/bgp/config.Y9
-rw-r--r--proto/bgp/packets.c112
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);
}
}