From 53ffbff39f054e1302fb296327b9bb1b4f88226c Mon Sep 17 00:00:00 2001 From: Ondrej Zajicek Date: Sun, 8 Jan 2012 15:28:27 +0100 Subject: [PATCH] Implements support for link-local addresses in BGP. Thanks Matthias Schiffer for the original patch. --- lib/printf.c | 18 ++++++++++++++++++ proto/bgp/attrs.c | 7 +++++-- proto/bgp/bgp.c | 35 +++++++++++++++++++++++------------ proto/bgp/bgp.h | 3 ++- proto/bgp/config.Y | 10 +++++++--- proto/bgp/packets.c | 41 ++++++++++++++++++++++++++++++++++++----- 6 files changed, 91 insertions(+), 23 deletions(-) diff --git a/lib/printf.c b/lib/printf.c index c3f7074d..14af1062 100644 --- a/lib/printf.c +++ b/lib/printf.c @@ -12,6 +12,8 @@ #include +#include "nest/iface.h" + /* we use this so that we can do without the ctype library */ #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; const char *s; char ipbuf[STD_ADDRESS_P_LENGTH+1]; + struct iface *iface; int flags; /* flags to number() */ @@ -279,6 +282,21 @@ int bvsnprintf(char *buf, int size, const char *fmt, va_list args) s = ipbuf; 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 */ case 'R': x = va_arg(args, u32); diff --git a/proto/bgp/attrs.c b/proto/bgp/attrs.c index 93c1f6d6..5a368783 100644 --- a/proto/bgp/attrs.c +++ b/proto/bgp/attrs.c @@ -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, - 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)); 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 */ } diff --git a/proto/bgp/bgp.c b/proto/bgp/bgp.c index 6bd18f09..66fdc603 100644 --- a/proto/bgp/bgp.c +++ b/proto/bgp/bgp.c @@ -111,7 +111,7 @@ bgp_open(struct bgp_proto *p) 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) { bgp_close(p, 0); @@ -178,7 +178,7 @@ bgp_close(struct bgp_proto *p, int apply_md5) bgp_counter--; 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) { @@ -578,6 +578,7 @@ bgp_connect(struct bgp_proto *p) /* Enter Connect state and start establishing c s->type = SK_TCP_ACTIVE; s->saddr = p->source_addr; s->daddr = p->cf->remote_ip; + s->iface = p->neigh ? p->neigh->iface : NULL; s->dport = BGP_PORT; s->ttl = p->cf->ttl_security ? 255 : hops; 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->password = p->cf->password; 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_sk(conn, s); 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) { 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 */ int acc = (p->p.proto_state == PS_START || p->p.proto_state == PS_UP) && (p->start_state >= BSS_CONNECT) && (!p->incoming_conn.sk); - BGP_TRACE(D_EVENTS, "Incoming connection from %I (port %d) %s", - sk->daddr, sk->dport, acc ? "accepted" : "rejected"); + BGP_TRACE(D_EVENTS, "Incoming connection from %I%J (port %d) %s", + sk->daddr, ipa_has_link_scope(sk->daddr) ? sk->iface : NULL, + sk->dport, acc ? "accepted" : "rejected"); if (!acc) 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: rfree(sk); return 0; @@ -713,6 +718,7 @@ bgp_start_neighbor(struct bgp_proto *p) { /* Called only for single-hop BGP sessions */ + /* Remove this ? */ if (ipa_zero(p->source_addr)) 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; - if (n->iface) + if (n->scope > 0) { if ((p->p.proto_state == PS_START) && (p->start_state == BSS_PREPARE)) { @@ -793,10 +799,10 @@ bgp_start_locked(struct object_lock *lock) 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)) { - 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 */ p->p.disabled = 1; 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) bgp_start_neighbor(p); 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 @@ -847,6 +853,7 @@ bgp_start(struct proto *P) lock = p->lock = olock_new(P->pool); lock->addr = p->cf->remote_ip; + lock->iface = p->cf->iface; lock->type = OBJLOCK_TCP; lock->port = BGP_PORT; lock->iface = NULL; @@ -951,6 +958,10 @@ bgp_check_config(struct bgp_config *c) if (c->multihop && (c->gw_mode == GW_DIRECT)) 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 */ if (!c->missing_lladdr) 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; 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); if (P->proto_state == PS_START) diff --git a/proto/bgp/bgp.h b/proto/bgp/bgp.h index 0c50583b..a8c5818e 100644 --- a/proto/bgp/bgp.h +++ b/proto/bgp/bgp.h @@ -19,9 +19,10 @@ struct bgp_config { struct proto_config c; u32 local_as, remote_as; 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 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 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_* */ diff --git a/proto/bgp/config.Y b/proto/bgp/config.Y index 3ef9b290..5fb60940 100644 --- a/proto/bgp/config.Y +++ b/proto/bgp/config.Y @@ -57,11 +57,15 @@ bgp_proto: | bgp_proto proto_item ';' | 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 NEIGHBOR ipa AS expr ';' { - if (ipa_nonzero(BGP_CFG->remote_ip)) cf_error("Only one neighbor per BGP instance is allowed"); + | 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_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_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 CLIENT ';' { BGP_CFG->rr_client = 1; } diff --git a/proto/bgp/packets.c b/proto/bgp/packets.c index c3a86731..bbb0865f 100644 --- a/proto/bgp/packets.c +++ b/proto/bgp/packets.c @@ -318,6 +318,13 @@ bgp_create_update(struct bgp_conn *conn, byte *buf) #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 * 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; ea_list *ea; eattr *nh; - neighbor *n; put_u16(buf, 0); 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 * problem, but it is contrary to RFC 2545 and Quagga does not * 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 (n && p->neigh && n->iface == p->neigh->iface) + if (ipa_zero(ip) || same_iface(p, &ip)) { if (second && ipa_nonzero(ipp[1])) ip_ll = ipp[1]; @@ -434,6 +443,9 @@ bgp_create_update(struct bgp_conn *conn, byte *buf) *tmp++ = BGP_AF_IPV6; *tmp++ = 1; + if (ipa_has_link_scope(ip)) + ip = IPA_NONE; + if (ipa_nonzero(ip_ll)) { *tmp++ = 32; @@ -809,13 +821,27 @@ bgp_set_next_hop(struct bgp_proto *p, rta *a) #ifdef IPV6 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 int second = 0; #endif 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) return 0; @@ -826,7 +852,12 @@ bgp_set_next_hop(struct bgp_proto *p, rta *a) a->igp_metric = 0; } 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; }