summaryrefslogtreecommitdiff
path: root/proto/bgp
diff options
context:
space:
mode:
Diffstat (limited to 'proto/bgp')
-rw-r--r--proto/bgp/attrs.c15
-rw-r--r--proto/bgp/bgp.c70
-rw-r--r--proto/bgp/bgp.h9
-rw-r--r--proto/bgp/config.Y8
-rw-r--r--proto/bgp/packets.c24
5 files changed, 107 insertions, 19 deletions
diff --git a/proto/bgp/attrs.c b/proto/bgp/attrs.c
index 837a6861..d34e2ae3 100644
--- a/proto/bgp/attrs.c
+++ b/proto/bgp/attrs.c
@@ -238,7 +238,7 @@ bgp_format_aggregator(eattr *a, byte *buf, int buflen UNUSED)
as = get_u32(data);
data += 4;
- bsprintf(buf, "%d.%d.%d.%d AS%d", data[0], data[1], data[2], data[3], as);
+ bsprintf(buf, "%d.%d.%d.%d AS%u", data[0], data[1], data[2], data[3], as);
}
static int
@@ -1032,7 +1032,8 @@ bgp_create_attrs(struct bgp_proto *p, rte *e, ea_list **attrs, struct linpool *p
rta->dest != RTD_ROUTER ||
ipa_equal(rta->gw, IPA_NONE) ||
ipa_has_link_scope(rta->gw) ||
- (!p->is_internal && (!p->neigh || (rta->iface != p->neigh->iface))))
+ (!p->is_internal && !p->cf->next_hop_keep &&
+ (!p->neigh || (rta->iface != p->neigh->iface))))
set_next_hop(z, p->source_addr);
else
set_next_hop(z, rta->gw);
@@ -1046,8 +1047,9 @@ bgp_create_attrs(struct bgp_proto *p, rte *e, ea_list **attrs, struct linpool *p
static inline int
bgp_as_path_loopy(struct bgp_proto *p, rta *a)
{
+ int num = p->cf->allow_local_as + 1;
eattr *e = ea_find(a->eattrs, EA_CODE(EAP_BGP, BA_AS_PATH));
- return (e && as_path_is_member(e->u.ptr, p->local_as));
+ return (e && (num > 0) && as_path_contains(e->u.ptr, p->local_as, num));
}
static inline int
@@ -1100,10 +1102,13 @@ bgp_update_attrs(struct bgp_proto *p, rte *e, ea_list **attrs, struct linpool *p
/* iBGP -> keep next_hop, eBGP multi-hop -> use source_addr,
* eBGP single-hop -> keep next_hop if on the same iface.
* If the next_hop is zero (i.e. link-local), keep only if on the same iface.
+ *
+ * Note that same-iface-check uses iface from route, which is based on gw.
*/
a = ea_find(e->attrs->eattrs, EA_CODE(EAP_BGP, BA_NEXT_HOP));
if (a && !p->cf->next_hop_self &&
- ((p->is_internal && ipa_nonzero(*((ip_addr *) a->u.ptr->data))) ||
+ (p->cf->next_hop_keep ||
+ (p->is_internal && ipa_nonzero(*((ip_addr *) a->u.ptr->data))) ||
(p->neigh && (e->attrs->iface == p->neigh->iface))))
{
/* Leave the original next hop attribute, will check later where does it point */
@@ -1444,7 +1449,7 @@ bgp_rte_recalculate(rtable *table, net *net, rte *new, rte *old, rte *old_best)
/* The default case - find a new best-in-group route */
r = new; /* new may not be in the list */
- for (s=net->routes; s; s=s->next)
+ for (s=net->routes; rte_is_valid(s); s=s->next)
if (use_deterministic_med(s) && same_group(s, lpref, lasn))
{
s->u.bgp.suppressed = 1;
diff --git a/proto/bgp/bgp.c b/proto/bgp/bgp.c
index f290f227..81a263bb 100644
--- a/proto/bgp/bgp.c
+++ b/proto/bgp/bgp.c
@@ -59,8 +59,8 @@
#include "nest/iface.h"
#include "nest/protocol.h"
#include "nest/route.h"
-#include "nest/locks.h"
#include "nest/cli.h"
+#include "nest/locks.h"
#include "conf/conf.h"
#include "lib/socket.h"
#include "lib/resource.h"
@@ -76,6 +76,7 @@ static void bgp_close(struct bgp_proto *p, int apply_md5);
static void bgp_connect(struct bgp_proto *p);
static void bgp_active(struct bgp_proto *p);
static sock *bgp_setup_listen_sk(ip_addr addr, unsigned port, u32 flags);
+static void bgp_update_bfd(struct bgp_proto *p, int use_bfd);
/**
@@ -153,8 +154,12 @@ bgp_initiate(struct bgp_proto *p)
if (rv < 0)
return;
+ if (p->cf->bfd)
+ bgp_update_bfd(p, p->cf->bfd);
+
if (p->startup_delay)
{
+ p->start_state = BSS_DELAY;
BGP_TRACE(D_EVENTS, "Startup delayed by %d seconds", p->startup_delay);
bgp_start_timer(p->startup_timer, p->startup_delay);
}
@@ -386,10 +391,12 @@ bgp_conn_enter_close_state(struct bgp_conn *conn)
int os = conn->state;
bgp_conn_set_state(conn, BS_CLOSE);
- tm_stop(conn->hold_timer);
tm_stop(conn->keepalive_timer);
conn->sk->rx_hook = NULL;
+ /* Timeout for CLOSE state, if we cannot send notification soon then we just hangup */
+ bgp_start_timer(conn->hold_timer, 10);
+
if (os == BS_ESTABLISHED)
bgp_conn_leave_established_state(p);
}
@@ -483,9 +490,18 @@ static void
bgp_hold_timeout(timer *t)
{
struct bgp_conn *conn = t->data;
+ struct bgp_proto *p = conn->bgp;
DBG("BGP: Hold timeout\n");
+ /* We are already closing the connection - just do hangup */
+ if (conn->state == BS_CLOSE)
+ {
+ BGP_TRACE(D_EVENTS, "Connection stalled");
+ bgp_conn_enter_idle_state(conn);
+ return;
+ }
+
/* If there is something in input queue, we are probably congested
and perhaps just not processed BGP packets in time. */
@@ -737,6 +753,9 @@ bgp_neigh_notify(neighbor *n)
{
struct bgp_proto *p = (struct bgp_proto *) n->proto;
+ if (! (n->flags & NEF_STICKY))
+ return;
+
if (n->scope > 0)
{
if ((p->p.proto_state == PS_START) && (p->start_state == BSS_PREPARE))
@@ -756,6 +775,37 @@ bgp_neigh_notify(neighbor *n)
}
}
+static void
+bgp_bfd_notify(struct bfd_request *req)
+{
+ struct bgp_proto *p = req->data;
+ int ps = p->p.proto_state;
+
+ if (req->down && ((ps == PS_START) || (ps == PS_UP)))
+ {
+ BGP_TRACE(D_EVENTS, "BFD session down");
+ bgp_store_error(p, NULL, BE_MISC, BEM_BFD_DOWN);
+ if (ps == PS_UP)
+ bgp_update_startup_delay(p);
+ bgp_stop(p, 0);
+ }
+}
+
+static void
+bgp_update_bfd(struct bgp_proto *p, int use_bfd)
+{
+ if (use_bfd && !p->bfd_req)
+ p->bfd_req = bfd_request_session(p->p.pool, p->cf->remote_ip, p->source_addr,
+ p->cf->multihop ? NULL : p->neigh->iface,
+ bgp_bfd_notify, p);
+
+ if (!use_bfd && p->bfd_req)
+ {
+ rfree(p->bfd_req);
+ p->bfd_req = NULL;
+ }
+}
+
static int
bgp_reload_routes(struct proto *P)
{
@@ -816,6 +866,7 @@ bgp_start(struct proto *P)
p->outgoing_conn.state = BS_IDLE;
p->incoming_conn.state = BS_IDLE;
p->neigh = NULL;
+ p->bfd_req = NULL;
rt_lock_table(p->igp_table);
@@ -845,7 +896,6 @@ bgp_start(struct proto *P)
lock->iface = p->cf->iface;
lock->type = OBJLOCK_TCP;
lock->port = BGP_PORT;
- lock->iface = NULL;
lock->hook = bgp_start_locked;
lock->data = p;
olock_acquire(lock);
@@ -883,6 +933,7 @@ bgp_shutdown(struct proto *P)
subcode = 4; // Errcode 6, 4 - administrative reset
break;
+ case PDC_RX_LIMIT_HIT:
case PDC_IN_LIMIT_HIT:
subcode = 1; // Errcode 6, 1 - max number of prefixes reached
/* log message for compatibility */
@@ -981,6 +1032,9 @@ bgp_check_config(struct bgp_config *c)
ipa_has_link_scope(c->source_addr)))
cf_error("Multihop BGP cannot be used with link-local addresses");
+ if (c->multihop && c->bfd && ipa_zero(c->source_addr))
+ cf_error("Multihop BGP with BFD requires specified source address");
+
/* Different default based on rs_client */
if (!c->missing_lladdr)
@@ -1012,6 +1066,9 @@ bgp_reconfigure(struct proto *P, struct proto_config *C)
struct bgp_proto *p = (struct bgp_proto *) P;
struct bgp_config *old = p->cf;
+ if (proto_get_router_id(C) != p->local_id)
+ return 0;
+
int same = !memcmp(((byte *) old) + sizeof(struct proto_config),
((byte *) new) + sizeof(struct proto_config),
// password item is last and must be checked separately
@@ -1020,6 +1077,9 @@ bgp_reconfigure(struct proto *P, struct proto_config *C)
|| (old->password && new->password && !strcmp(old->password, new->password)))
&& (get_igp_table(old) == get_igp_table(new));
+ if (same && (p->start_state > BSS_PREPARE))
+ bgp_update_bfd(p, new->bfd);
+
/* We should update our copy of configuration ptr as old configuration will be freed */
if (same)
p->cf = new;
@@ -1101,7 +1161,7 @@ bgp_store_error(struct bgp_proto *p, struct bgp_conn *c, u8 class, u32 code)
static char *bgp_state_names[] = { "Idle", "Connect", "Active", "OpenSent", "OpenConfirm", "Established", "Close" };
static char *bgp_err_classes[] = { "", "Error: ", "Socket: ", "Received: ", "BGP Error: ", "Automatic shutdown: ", ""};
-static char *bgp_misc_errors[] = { "", "Neighbor lost", "Invalid next hop", "Kernel MD5 auth failed", "No listening socket" };
+static char *bgp_misc_errors[] = { "", "Neighbor lost", "Invalid next hop", "Kernel MD5 auth failed", "No listening socket", "BFD session down" };
static char *bgp_auto_errors[] = { "", "Route limit exceeded"};
static const char *
@@ -1195,7 +1255,7 @@ bgp_show_proto_info(struct proto *P)
cli_msg(-1006, " Source address: %I", p->source_addr);
if (P->cf->in_limit)
cli_msg(-1006, " Route limit: %d/%d",
- p->p.stats.imp_routes, P->cf->in_limit->limit);
+ p->p.stats.imp_routes + p->p.stats.filt_routes, P->cf->in_limit->limit);
cli_msg(-1006, " Hold timer: %d/%d",
tm_remains(c->hold_timer), c->hold_time);
cli_msg(-1006, " Keepalive timer: %d/%d",
diff --git a/proto/bgp/bgp.h b/proto/bgp/bgp.h
index b87de46e..a35c362c 100644
--- a/proto/bgp/bgp.h
+++ b/proto/bgp/bgp.h
@@ -11,6 +11,7 @@
#include <stdint.h>
#include "nest/route.h"
+#include "nest/bfd.h"
struct linpool;
struct eattr;
@@ -24,6 +25,7 @@ struct bgp_config {
int multihop; /* Number of hops if multihop */
int ttl_security; /* Enable TTL security [RFC5082] */
int next_hop_self; /* Always set next hop to local IP address */
+ int next_hop_keep; /* Do not touch next hop attribute */
int missing_lladdr; /* What we will do when we don' know link-local addr, see MLL_* */
int gw_mode; /* How we compute route gateway from next_hop attr, see GW_* */
int compare_path_lengths; /* Use path lengths when selecting best route */
@@ -44,6 +46,7 @@ struct bgp_config {
int interpret_communities; /* Hardwired handling of well-known communities */
int secondary; /* Accept also non-best routes (i.e. RA_ACCEPTED) */
int add_path; /* Use ADD-PATH extension [draft] */
+ int allow_local_as; /* Allow that number of local ASNs in incoming AS_PATHs */
unsigned connect_retry_time;
unsigned hold_time, initial_hold_time;
unsigned keepalive_time;
@@ -52,8 +55,10 @@ struct bgp_config {
unsigned error_delay_time_min; /* Time to wait after an error is detected */
unsigned error_delay_time_max;
unsigned disable_after_error; /* Disable the protocol when error is detected */
+
char *password; /* Password used for MD5 authentication */
struct rtable_config *igp_table; /* Table used for recursive next hop lookups */
+ int bfd; /* Use BFD for liveness detection */
};
#define MLL_SELF 1
@@ -106,6 +111,7 @@ struct bgp_proto {
struct bgp_conn incoming_conn; /* Incoming connection we have neither accepted nor rejected yet */
struct object_lock *lock; /* Lock for neighbor connection */
struct neighbor *neigh; /* Neighbor entry corresponding to remote ip, NULL if multihop */
+ struct bfd_request *bfd_req; /* BFD request, if BFD is used */
ip_addr source_addr; /* Local address used as an advertised next hop */
rtable *igp_table; /* Table used for recursive next hop lookups */
struct event *event; /* Event for respawning and shutting process */
@@ -274,6 +280,8 @@ void bgp_log_error(struct bgp_proto *p, u8 class, char *msg, unsigned code, unsi
#define BS_ESTABLISHED 5
#define BS_CLOSE 6 /* Used during transition to BS_IDLE */
+#define BS_MAX 7
+
/* BGP start states
*
* Used in PS_START for fine-grained specification of starting state.
@@ -305,6 +313,7 @@ void bgp_log_error(struct bgp_proto *p, u8 class, char *msg, unsigned code, unsi
#define BEM_INVALID_NEXT_HOP 2
#define BEM_INVALID_MD5 3 /* MD5 authentication kernel request failed (possibly not supported) */
#define BEM_NO_SOCKET 4
+#define BEM_BFD_DOWN 5
/* Automatic shutdown error codes */
diff --git a/proto/bgp/config.Y b/proto/bgp/config.Y
index 0b096339..ab12fed5 100644
--- a/proto/bgp/config.Y
+++ b/proto/bgp/config.Y
@@ -26,7 +26,7 @@ CF_KEYWORDS(BGP, LOCAL, NEIGHBOR, AS, HOLD, TIME, CONNECT, RETRY,
PREFER, OLDER, MISSING, LLADDR, DROP, IGNORE, ROUTE, REFRESH,
INTERPRET, COMMUNITIES, BGP_ORIGINATOR_ID, BGP_CLUSTER_LIST, IGP,
TABLE, GATEWAY, DIRECT, RECURSIVE, MED, TTL, SECURITY, DETERMINISTIC,
- SECONDARY, ADD, PATHS, RX, TX)
+ SECONDARY, ALLOW, BFD, ADD, PATHS, RX, TX)
CF_GRAMMAR
@@ -76,7 +76,8 @@ bgp_proto:
| bgp_proto KEEPALIVE TIME expr ';' { BGP_CFG->keepalive_time = $4; }
| bgp_proto MULTIHOP ';' { BGP_CFG->multihop = 64; }
| bgp_proto MULTIHOP expr ';' { BGP_CFG->multihop = $3; if (($3<1) || ($3>255)) cf_error("Multihop must be in range 1-255"); }
- | bgp_proto NEXT HOP SELF ';' { BGP_CFG->next_hop_self = 1; }
+ | bgp_proto NEXT HOP SELF ';' { BGP_CFG->next_hop_self = 1; BGP_CFG->next_hop_keep = 0; }
+ | bgp_proto NEXT HOP KEEP ';' { BGP_CFG->next_hop_keep = 1; BGP_CFG->next_hop_self = 0; }
| 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; }
@@ -110,8 +111,11 @@ bgp_proto:
| bgp_proto ADD PATHS RX ';' { BGP_CFG->add_path = ADD_PATH_RX; }
| bgp_proto ADD PATHS TX ';' { BGP_CFG->add_path = ADD_PATH_TX; }
| bgp_proto ADD PATHS bool ';' { BGP_CFG->add_path = $4 ? ADD_PATH_FULL : 0; }
+ | bgp_proto ALLOW LOCAL AS ';' { BGP_CFG->allow_local_as = -1; }
+ | bgp_proto ALLOW LOCAL AS expr ';' { BGP_CFG->allow_local_as = $5; }
| bgp_proto IGP TABLE rtable ';' { BGP_CFG->igp_table = $4; }
| bgp_proto TTL SECURITY bool ';' { BGP_CFG->ttl_security = $4; }
+ | bgp_proto BFD bool ';' { BGP_CFG->bfd = $3; cf_check_bfd($3); }
;
CF_ADDTO(dynamic_attr, BGP_ORIGIN
diff --git a/proto/bgp/packets.c b/proto/bgp/packets.c
index 3fae2c24..42064332 100644
--- a/proto/bgp/packets.c
+++ b/proto/bgp/packets.c
@@ -24,6 +24,13 @@
static struct rate_limit rl_rcv_update, rl_snd_update;
+/* Table for state -> RFC 6608 FSM error subcodes */
+static byte fsm_err_subcode[BS_MAX] = {
+ [BS_OPENSENT] = 1,
+ [BS_OPENCONFIRM] = 2,
+ [BS_ESTABLISHED] = 3
+};
+
/*
* MRT Dump format is not semantically specified.
* We will use these values in appropriate fields:
@@ -58,7 +65,7 @@ mrt_put_bgp4_hdr(byte *buf, struct bgp_conn *conn, int as4)
buf+=4;
}
- put_u16(buf+0, p->neigh ? p->neigh->iface->index : 0);
+ put_u16(buf+0, (p->neigh && p->neigh->iface) ? p->neigh->iface->index : 0);
put_u16(buf+2, BGP_AF);
buf+=4;
buf = ipa_put_addr(buf, conn->sk ? conn->sk->daddr : IPA_NONE);
@@ -758,7 +765,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); return; }
+ { bgp_error(conn, 5, fsm_err_subcode[conn->state], NULL, 0); return; }
/* Check message contents */
if (len < 29 || len != 29 + pkt[28])
@@ -917,7 +924,7 @@ bgp_set_next_hop(struct bgp_proto *p, rta *a)
ip_addr *nexthop = (ip_addr *) nh->u.ptr->data;
#ifdef IPV6
- int second = (nh->u.ptr->length == NEXT_HOP_LENGTH);
+ int second = (nh->u.ptr->length == NEXT_HOP_LENGTH) && ipa_nonzero(nexthop[1]);
/* First address should not be link-local, but may be zero in direct mode */
if (ipa_has_link_scope(*nexthop))
@@ -1148,7 +1155,7 @@ bgp_rx_update(struct bgp_conn *conn, byte *pkt, int len)
bgp_conn_enter_established_state(conn);
if (conn->state != BS_ESTABLISHED)
- { bgp_error(conn, 5, 0, NULL, 0); return; }
+ { bgp_error(conn, 5, fsm_err_subcode[conn->state], NULL, 0); return; }
bgp_start_timer(conn->hold_timer, conn->hold_time);
/* Find parts of the packet and check sizes */
@@ -1210,7 +1217,10 @@ static struct {
{ 3, 10, "Invalid network field" },
{ 3, 11, "Malformed AS_PATH" },
{ 4, 0, "Hold timer expired" },
- { 5, 0, "Finite state machine error" },
+ { 5, 0, "Finite state machine error" }, /* Subcodes are according to [RFC6608] */
+ { 5, 1, "Unexpected message in OpenSent state" },
+ { 5, 2, "Unexpected message in OpenConfirm state" },
+ { 5, 3, "Unexpected message in Established state" },
{ 6, 0, "Cease" }, /* Subcodes are according to [RFC4486] */
{ 6, 1, "Maximum number of prefixes reached" },
{ 6, 2, "Administrative shutdown" },
@@ -1341,7 +1351,7 @@ bgp_rx_keepalive(struct bgp_conn *conn)
case BS_ESTABLISHED:
break;
default:
- bgp_error(conn, 5, 0, NULL, 0);
+ bgp_error(conn, 5, fsm_err_subcode[conn->state], NULL, 0);
}
}
@@ -1353,7 +1363,7 @@ bgp_rx_route_refresh(struct bgp_conn *conn, byte *pkt, int len)
BGP_TRACE(D_PACKETS, "Got ROUTE-REFRESH");
if (conn->state != BS_ESTABLISHED)
- { bgp_error(conn, 5, 0, NULL, 0); return; }
+ { bgp_error(conn, 5, fsm_err_subcode[conn->state], NULL, 0); return; }
if (!p->cf->enable_refresh)
{ bgp_error(conn, 1, 3, pkt+18, 1); return; }