Implements support for link-local addresses in BGP.
Thanks Matthias Schiffer for the original patch.
This commit is contained in:
parent
eb1451a3a0
commit
53ffbff39f
6 changed files with 91 additions and 23 deletions
18
lib/printf.c
18
lib/printf.c
|
@ -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);
|
||||||
|
|
|
@ -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 */
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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_* */
|
||||||
|
|
|
@ -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; }
|
||||||
|
|
|
@ -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 */
|
||||||
rta_set_recursive_next_hop(p->p.table, a, p->igp_table, nexthop, nexthop + second);
|
{
|
||||||
|
if (ipa_zero(*nexthop))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
rta_set_recursive_next_hop(p->p.table, a, p->igp_table, nexthop, nexthop + second);
|
||||||
|
}
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue