Implements support for link-local addresses in BGP.

Thanks Matthias Schiffer for the original patch.
This commit is contained in:
Ondrej Zajicek 2012-01-08 15:28:27 +01:00
parent eb1451a3a0
commit 53ffbff39f
6 changed files with 91 additions and 23 deletions

View file

@ -12,6 +12,8 @@
#include <errno.h> #include <errno.h>
#include "nest/iface.h"
/* we use this so that we can do without the ctype library */ /* we use this so that we can do without the ctype library */
#define is_digit(c) ((c) >= '0' && (c) <= '9') #define is_digit(c) ((c) >= '0' && (c) <= '9')
@ -138,6 +140,7 @@ int bvsnprintf(char *buf, int size, const char *fmt, va_list args)
char *str, *start; char *str, *start;
const char *s; const char *s;
char ipbuf[STD_ADDRESS_P_LENGTH+1]; char ipbuf[STD_ADDRESS_P_LENGTH+1];
struct iface *iface;
int flags; /* flags to number() */ int flags; /* flags to number() */
@ -279,6 +282,21 @@ int bvsnprintf(char *buf, int size, const char *fmt, va_list args)
s = ipbuf; s = ipbuf;
goto str; goto str;
/* Interface scope after link-local IP address */
case 'J':
iface = va_arg(args, struct iface *);
if (!iface)
continue;
if (!size)
return -1;
*str++ = '%';
start++;
size--;
s = iface->name;
goto str;
/* Router/Network ID - essentially IPv4 address in u32 value */ /* Router/Network ID - essentially IPv4 address in u32 value */
case 'R': case 'R':
x = va_arg(args, u32); x = va_arg(args, u32);

View file

@ -1001,10 +1001,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, /* iBGP -> keep next_hop, eBGP multi-hop -> use source_addr,
eBGP single-hop -> keep next_hop if on the same iface */ * 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.
*/
a = ea_find(e->attrs->eattrs, EA_CODE(EAP_BGP, BA_NEXT_HOP)); a = ea_find(e->attrs->eattrs, EA_CODE(EAP_BGP, BA_NEXT_HOP));
if (a && !p->cf->next_hop_self && if (a && !p->cf->next_hop_self &&
(p->is_internal || (p->neigh && (e->attrs->iface == p->neigh->iface)))) ((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 */ /* Leave the original next hop attribute, will check later where does it point */
} }

View file

@ -111,7 +111,7 @@ bgp_open(struct bgp_proto *p)
if (p->cf->password) if (p->cf->password)
{ {
int rv = sk_set_md5_auth(bgp_listen_sk, p->cf->remote_ip, p->cf->password); int rv = sk_set_md5_auth(bgp_listen_sk, p->cf->remote_ip, p->cf->iface, p->cf->password);
if (rv < 0) if (rv < 0)
{ {
bgp_close(p, 0); bgp_close(p, 0);
@ -178,7 +178,7 @@ bgp_close(struct bgp_proto *p, int apply_md5)
bgp_counter--; bgp_counter--;
if (p->cf->password && apply_md5) if (p->cf->password && apply_md5)
sk_set_md5_auth(bgp_listen_sk, p->cf->remote_ip, NULL); sk_set_md5_auth(bgp_listen_sk, p->cf->remote_ip, p->cf->iface, NULL);
if (!bgp_counter) if (!bgp_counter)
{ {
@ -578,6 +578,7 @@ bgp_connect(struct bgp_proto *p) /* Enter Connect state and start establishing c
s->type = SK_TCP_ACTIVE; s->type = SK_TCP_ACTIVE;
s->saddr = p->source_addr; s->saddr = p->source_addr;
s->daddr = p->cf->remote_ip; s->daddr = p->cf->remote_ip;
s->iface = p->neigh ? p->neigh->iface : NULL;
s->dport = BGP_PORT; s->dport = BGP_PORT;
s->ttl = p->cf->ttl_security ? 255 : hops; s->ttl = p->cf->ttl_security ? 255 : hops;
s->rbsize = BGP_RX_BUFFER_SIZE; s->rbsize = BGP_RX_BUFFER_SIZE;
@ -585,7 +586,8 @@ bgp_connect(struct bgp_proto *p) /* Enter Connect state and start establishing c
s->tos = IP_PREC_INTERNET_CONTROL; s->tos = IP_PREC_INTERNET_CONTROL;
s->password = p->cf->password; s->password = p->cf->password;
s->tx_hook = bgp_connected; s->tx_hook = bgp_connected;
BGP_TRACE(D_EVENTS, "Connecting to %I from local address %I", s->daddr, s->saddr); BGP_TRACE(D_EVENTS, "Connecting to %I%J from local address %I%J", s->daddr, p->cf->iface,
s->saddr, ipa_has_link_scope(s->saddr) ? s->iface : NULL);
bgp_setup_conn(p, conn); bgp_setup_conn(p, conn);
bgp_setup_sk(conn, s); bgp_setup_sk(conn, s);
bgp_conn_set_state(conn, BS_CONNECT); bgp_conn_set_state(conn, BS_CONNECT);
@ -634,14 +636,16 @@ bgp_incoming_connection(sock *sk, int dummy UNUSED)
if (pc->protocol == &proto_bgp && pc->proto) if (pc->protocol == &proto_bgp && pc->proto)
{ {
struct bgp_proto *p = (struct bgp_proto *) pc->proto; struct bgp_proto *p = (struct bgp_proto *) pc->proto;
if (ipa_equal(p->cf->remote_ip, sk->daddr)) if (ipa_equal(p->cf->remote_ip, sk->daddr) &&
(!ipa_has_link_scope(sk->daddr) || (p->cf->iface == sk->iface)))
{ {
/* We are in proper state and there is no other incoming connection */ /* We are in proper state and there is no other incoming connection */
int acc = (p->p.proto_state == PS_START || p->p.proto_state == PS_UP) && int acc = (p->p.proto_state == PS_START || p->p.proto_state == PS_UP) &&
(p->start_state >= BSS_CONNECT) && (!p->incoming_conn.sk); (p->start_state >= BSS_CONNECT) && (!p->incoming_conn.sk);
BGP_TRACE(D_EVENTS, "Incoming connection from %I (port %d) %s", BGP_TRACE(D_EVENTS, "Incoming connection from %I%J (port %d) %s",
sk->daddr, sk->dport, acc ? "accepted" : "rejected"); sk->daddr, ipa_has_link_scope(sk->daddr) ? sk->iface : NULL,
sk->dport, acc ? "accepted" : "rejected");
if (!acc) if (!acc)
goto err; goto err;
@ -667,7 +671,8 @@ bgp_incoming_connection(sock *sk, int dummy UNUSED)
} }
} }
log(L_WARN "BGP: Unexpected connect from unknown address %I (port %d)", sk->daddr, sk->dport); log(L_WARN "BGP: Unexpected connect from unknown address %I%J (port %d)",
sk->daddr, ipa_has_link_scope(sk->daddr) ? sk->iface : NULL, sk->dport);
err: err:
rfree(sk); rfree(sk);
return 0; return 0;
@ -713,6 +718,7 @@ bgp_start_neighbor(struct bgp_proto *p)
{ {
/* Called only for single-hop BGP sessions */ /* Called only for single-hop BGP sessions */
/* Remove this ? */
if (ipa_zero(p->source_addr)) if (ipa_zero(p->source_addr))
p->source_addr = p->neigh->iface->addr->ip; p->source_addr = p->neigh->iface->addr->ip;
@ -742,7 +748,7 @@ bgp_neigh_notify(neighbor *n)
{ {
struct bgp_proto *p = (struct bgp_proto *) n->proto; struct bgp_proto *p = (struct bgp_proto *) n->proto;
if (n->iface) if (n->scope > 0)
{ {
if ((p->p.proto_state == PS_START) && (p->start_state == BSS_PREPARE)) if ((p->p.proto_state == PS_START) && (p->start_state == BSS_PREPARE))
{ {
@ -793,10 +799,10 @@ bgp_start_locked(struct object_lock *lock)
return; return;
} }
p->neigh = neigh_find(&p->p, &cf->remote_ip, NEF_STICKY); p->neigh = neigh_find2(&p->p, &cf->remote_ip, cf->iface, NEF_STICKY);
if (!p->neigh || (p->neigh->scope == SCOPE_HOST)) if (!p->neigh || (p->neigh->scope == SCOPE_HOST))
{ {
log(L_ERR "%s: Invalid remote address %I", p->p.name, cf->remote_ip); log(L_ERR "%s: Invalid remote address %I%J", p->p.name, cf->remote_ip, cf->iface);
/* As we do not start yet, we can just disable protocol */ /* As we do not start yet, we can just disable protocol */
p->p.disabled = 1; p->p.disabled = 1;
bgp_store_error(p, NULL, BE_MISC, BEM_INVALID_NEXT_HOP); bgp_store_error(p, NULL, BE_MISC, BEM_INVALID_NEXT_HOP);
@ -807,7 +813,7 @@ bgp_start_locked(struct object_lock *lock)
if (p->neigh->scope > 0) if (p->neigh->scope > 0)
bgp_start_neighbor(p); bgp_start_neighbor(p);
else else
BGP_TRACE(D_EVENTS, "Waiting for %I to become my neighbor", cf->remote_ip); BGP_TRACE(D_EVENTS, "Waiting for %I%J to become my neighbor", cf->remote_ip, cf->iface);
} }
static int static int
@ -847,6 +853,7 @@ bgp_start(struct proto *P)
lock = p->lock = olock_new(P->pool); lock = p->lock = olock_new(P->pool);
lock->addr = p->cf->remote_ip; lock->addr = p->cf->remote_ip;
lock->iface = p->cf->iface;
lock->type = OBJLOCK_TCP; lock->type = OBJLOCK_TCP;
lock->port = BGP_PORT; lock->port = BGP_PORT;
lock->iface = NULL; lock->iface = NULL;
@ -951,6 +958,10 @@ bgp_check_config(struct bgp_config *c)
if (c->multihop && (c->gw_mode == GW_DIRECT)) if (c->multihop && (c->gw_mode == GW_DIRECT))
cf_error("Multihop BGP cannot use direct gateway mode"); cf_error("Multihop BGP cannot use direct gateway mode");
if (c->multihop && (ipa_has_link_scope(c->remote_ip) ||
ipa_has_link_scope(c->source_addr)))
cf_error("Multihop BGP cannot be used with link-local addresses");
/* Different default based on rs_client */ /* Different default based on rs_client */
if (!c->missing_lladdr) if (!c->missing_lladdr)
c->missing_lladdr = c->rs_client ? MLL_IGNORE : MLL_SELF; c->missing_lladdr = c->rs_client ? MLL_IGNORE : MLL_SELF;
@ -1115,7 +1126,7 @@ bgp_show_proto_info(struct proto *P)
struct bgp_conn *c = p->conn; struct bgp_conn *c = p->conn;
cli_msg(-1006, " BGP state: %s", bgp_state_dsc(p)); cli_msg(-1006, " BGP state: %s", bgp_state_dsc(p));
cli_msg(-1006, " Neighbor address: %I", p->cf->remote_ip); cli_msg(-1006, " Neighbor address: %I%J", p->cf->remote_ip, p->cf->iface);
cli_msg(-1006, " Neighbor AS: %u", p->remote_as); cli_msg(-1006, " Neighbor AS: %u", p->remote_as);
if (P->proto_state == PS_START) if (P->proto_state == PS_START)

View file

@ -19,9 +19,10 @@ struct bgp_config {
struct proto_config c; struct proto_config c;
u32 local_as, remote_as; u32 local_as, remote_as;
ip_addr remote_ip; ip_addr remote_ip;
ip_addr source_addr; /* Source address to use */
struct iface *iface; /* Interface for link-local addresses */
int multihop; /* Number of hops if multihop */ int multihop; /* Number of hops if multihop */
int ttl_security; /* Enable TTL security [RFC5082] */ int ttl_security; /* Enable TTL security [RFC5082] */
ip_addr source_addr; /* Source address to use */
int next_hop_self; /* Always set next hop to local IP address */ 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 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 gw_mode; /* How we compute route gateway from next_hop attr, see GW_* */

View file

@ -57,11 +57,15 @@ bgp_proto:
| bgp_proto proto_item ';' | bgp_proto proto_item ';'
| bgp_proto LOCAL AS expr ';' { BGP_CFG->local_as = $4; } | bgp_proto LOCAL AS expr ';' { BGP_CFG->local_as = $4; }
| bgp_proto LOCAL ipa AS expr ';' { BGP_CFG->source_addr = $3; BGP_CFG->local_as = $5; } | bgp_proto LOCAL ipa AS expr ';' { BGP_CFG->source_addr = $3; BGP_CFG->local_as = $5; }
| bgp_proto NEIGHBOR ipa AS expr ';' { | bgp_proto NEIGHBOR ipa ipa_scope AS expr ';' {
if (ipa_nonzero(BGP_CFG->remote_ip)) cf_error("Only one neighbor per BGP instance is allowed"); if (ipa_nonzero(BGP_CFG->remote_ip))
cf_error("Only one neighbor per BGP instance is allowed");
if (!ipa_has_link_scope($3) != !$4)
cf_error("Link-local address and interface scope must be used together");
BGP_CFG->remote_ip = $3; BGP_CFG->remote_ip = $3;
BGP_CFG->remote_as = $5; BGP_CFG->iface = $4;
BGP_CFG->remote_as = $6;
} }
| bgp_proto RR CLUSTER ID idval ';' { BGP_CFG->rr_cluster_id = $5; } | bgp_proto RR CLUSTER ID idval ';' { BGP_CFG->rr_cluster_id = $5; }
| bgp_proto RR CLIENT ';' { BGP_CFG->rr_client = 1; } | bgp_proto RR CLIENT ';' { BGP_CFG->rr_client = 1; }

View file

@ -318,6 +318,13 @@ bgp_create_update(struct bgp_conn *conn, byte *buf)
#else /* IPv6 version */ #else /* IPv6 version */
static inline int
same_iface(struct bgp_proto *p, ip_addr *ip)
{
neighbor *n = neigh_find(&p->p, ip, 0);
return n && p->neigh && n->iface == p->neigh->iface;
}
static byte * static byte *
bgp_create_update(struct bgp_conn *conn, byte *buf) bgp_create_update(struct bgp_conn *conn, byte *buf)
{ {
@ -329,7 +336,6 @@ bgp_create_update(struct bgp_conn *conn, byte *buf)
ip_addr *ipp, ip, ip_ll; ip_addr *ipp, ip, ip_ll;
ea_list *ea; ea_list *ea;
eattr *nh; eattr *nh;
neighbor *n;
put_u16(buf, 0); put_u16(buf, 0);
w = buf+4; w = buf+4;
@ -399,10 +405,13 @@ bgp_create_update(struct bgp_conn *conn, byte *buf)
* link local address seems to be a natural way to solve that * link local address seems to be a natural way to solve that
* problem, but it is contrary to RFC 2545 and Quagga does not * problem, but it is contrary to RFC 2545 and Quagga does not
* accept such routes. * accept such routes.
*
* There are two cases, either we have global IP, or
* IPA_NONE if the neighbor is link-local. For IPA_NONE,
* we suppose it is on the same iface, see bgp_update_attrs().
*/ */
n = neigh_find(&p->p, &ip, 0); if (ipa_zero(ip) || same_iface(p, &ip))
if (n && p->neigh && n->iface == p->neigh->iface)
{ {
if (second && ipa_nonzero(ipp[1])) if (second && ipa_nonzero(ipp[1]))
ip_ll = ipp[1]; ip_ll = ipp[1];
@ -434,6 +443,9 @@ bgp_create_update(struct bgp_conn *conn, byte *buf)
*tmp++ = BGP_AF_IPV6; *tmp++ = BGP_AF_IPV6;
*tmp++ = 1; *tmp++ = 1;
if (ipa_has_link_scope(ip))
ip = IPA_NONE;
if (ipa_nonzero(ip_ll)) if (ipa_nonzero(ip_ll))
{ {
*tmp++ = 32; *tmp++ = 32;
@ -809,13 +821,27 @@ bgp_set_next_hop(struct bgp_proto *p, rta *a)
#ifdef IPV6 #ifdef IPV6
int second = (nh->u.ptr->length == NEXT_HOP_LENGTH); int second = (nh->u.ptr->length == NEXT_HOP_LENGTH);
/* First address should not be link-local, but may be zero in direct mode */
if (ipa_has_link_scope(*nexthop))
*nexthop = IPA_NONE;
#else #else
int second = 0; int second = 0;
#endif #endif
if (p->cf->gw_mode == GW_DIRECT) if (p->cf->gw_mode == GW_DIRECT)
{ {
neighbor *ng = neigh_find(&p->p, nexthop, 0) ? : p->neigh; neighbor *ng;
if (ipa_nonzero(*nexthop))
ng = neigh_find(&p->p, nexthop, 0);
else if (second) /* GW_DIRECT -> single_hop -> p->neigh != NULL */
ng = neigh_find2(&p->p, nexthop + 1, p->neigh->iface, 0);
/* Fallback */
if (!ng)
ng = p->neigh;
if (ng->scope == SCOPE_HOST) if (ng->scope == SCOPE_HOST)
return 0; return 0;
@ -826,7 +852,12 @@ bgp_set_next_hop(struct bgp_proto *p, rta *a)
a->igp_metric = 0; a->igp_metric = 0;
} }
else /* GW_RECURSIVE */ else /* GW_RECURSIVE */
{
if (ipa_zero(*nexthop))
return 0;
rta_set_recursive_next_hop(p->p.table, a, p->igp_table, nexthop, nexthop + second); rta_set_recursive_next_hop(p->p.table, a, p->igp_table, nexthop, nexthop + second);
}
return 1; return 1;
} }