summaryrefslogtreecommitdiff
path: root/proto/bgp/bgp.c
diff options
context:
space:
mode:
authorOndrej Zajicek (work) <santiago@crfreenet.org>2017-03-26 19:20:15 +0200
committerOndrej Zajicek (work) <santiago@crfreenet.org>2017-03-26 19:20:15 +0200
commitef57b70fa51687865e5823c0af2df2c6de338215 (patch)
treeab79f94a41470375b7826498aabb4b8e25bdfbff /proto/bgp/bgp.c
parent01111fc42c461202d427260fb56807eac256e8d5 (diff)
BGP: Support for routes with mixed-AF next hops
Covers IPv4/VPNv4 routes with IPv6 next hop (RFC 5549), IPv6 routes with IPv4 next hop (RFC 4798) and VPNv6 routes with IPv4 next hop (RFC 4659). Unfortunately it also makes next hop hooks more messy. Each BGP channel now could have two IGP tables, one for IPv4 next hops, the other for IPv6 next hops.
Diffstat (limited to 'proto/bgp/bgp.c')
-rw-r--r--proto/bgp/bgp.c123
1 files changed, 101 insertions, 22 deletions
diff --git a/proto/bgp/bgp.c b/proto/bgp/bgp.c
index 976fbd90..7d974d17 100644
--- a/proto/bgp/bgp.c
+++ b/proto/bgp/bgp.c
@@ -1066,7 +1066,6 @@ bgp_start_neighbor(struct bgp_proto *p)
{
/* Find some link-local address for given iface */
struct ifa *a;
- p->link_addr = IPA_NONE;
WALK_LIST(a, p->neigh->iface->addrs)
if (a->scope == SCOPE_LINK)
{
@@ -1294,6 +1293,7 @@ bgp_start(struct proto *P)
p->remote_id = 0;
p->source_addr = p->cf->local_ip;
+ p->link_addr = IPA_NONE;
/* XXXX */
if (p->p.gr_recovery && p->cf->gr_mode)
@@ -1415,12 +1415,6 @@ bgp_init(struct proto_config *CF)
return P;
}
-static inline rtable *
-get_igp_table(struct bgp_channel_config *cf)
-{
- return cf->igp_table ? cf->igp_table->table : cf->c.table->table;
-}
-
static void
bgp_channel_init(struct channel *C, struct channel_config *CF)
{
@@ -1429,8 +1423,13 @@ bgp_channel_init(struct channel *C, struct channel_config *CF)
c->cf = cf;
c->afi = cf->afi;
- c->desc = bgp_get_af_desc(c->afi);
- c->igp_table = get_igp_table(cf);
+ c->desc = cf->desc;
+
+ if (cf->igp_table_ip4)
+ c->igp_table_ip4 = cf->igp_table_ip4->table;
+
+ if (cf->igp_table_ip6)
+ c->igp_table_ip6 = cf->igp_table_ip6->table;
}
static int
@@ -1440,7 +1439,11 @@ bgp_channel_start(struct channel *C)
struct bgp_channel *c = (void *) C;
ip_addr src = p->source_addr;
- rt_lock_table(c->igp_table);
+ if (c->igp_table_ip4)
+ rt_lock_table(c->igp_table_ip4);
+
+ if (c->igp_table_ip6)
+ rt_lock_table(c->igp_table_ip6);
c->pool = p->p.pool; // XXXX
bgp_init_bucket_table(c);
@@ -1453,15 +1456,22 @@ bgp_channel_start(struct channel *C)
/* Try to use source address as next hop address */
if (ipa_zero(c->next_hop_addr))
{
- if (bgp_channel_is_ipv4(c) && ipa_is_ip4(src))
+ if (bgp_channel_is_ipv4(c) && (ipa_is_ip4(src) || c->ext_next_hop))
c->next_hop_addr = src;
- if (bgp_channel_is_ipv6(c) && ipa_is_ip6(src) && !ipa_is_link_local(src))
+ if (bgp_channel_is_ipv6(c) && (ipa_is_ip6(src) || c->ext_next_hop))
c->next_hop_addr = src;
}
+ /* Exit if no feasible next hop address is found */
+ if (ipa_zero(c->next_hop_addr))
+ {
+ log(L_WARN "%s: Missing next hop address", p->p.name);
+ return 0;
+ }
+
/* Set link-local address for IPv6 single-hop BGP */
- if (bgp_channel_is_ipv6(c) && p->neigh)
+ if (ipa_is_ip6(c->next_hop_addr) && p->neigh)
{
c->link_addr = p->link_addr;
@@ -1469,9 +1479,9 @@ bgp_channel_start(struct channel *C)
log(L_WARN "%s: Missing link-local address", p->p.name);
}
- /* No next hop address is valid on IPv6 link-local BGP */
- if (ipa_zero(c->next_hop_addr) && !ipa_is_link_local(src))
- log(L_WARN "%s: Missing next hop address", p->p.name);
+ /* Link local address is already in c->link_addr */
+ if (ipa_is_link_local(c->next_hop_addr))
+ c->next_hop_addr = IPA_NONE;
return 0; /* XXXX: Currently undefined */
}
@@ -1492,9 +1502,55 @@ bgp_channel_cleanup(struct channel *C)
{
struct bgp_channel *c = (void *) C;
- rt_unlock_table(c->igp_table);
+ if (c->igp_table_ip4)
+ rt_unlock_table(c->igp_table_ip4);
+
+ if (c->igp_table_ip6)
+ rt_unlock_table(c->igp_table_ip6);
+}
+
+static inline struct bgp_channel_config *
+bgp_find_channel_config(struct bgp_config *cf, u32 afi)
+{
+ struct bgp_channel_config *cc;
+
+ WALK_LIST(cc, cf->c.channels)
+ if (cc->afi == afi)
+ return cc;
+
+ return NULL;
}
+struct rtable_config *
+bgp_default_igp_table(struct bgp_config *cf, struct bgp_channel_config *cc, u32 type)
+{
+ struct bgp_channel_config *cc2;
+ struct rtable_config *tab;
+
+ /* First, try table connected by the channel */
+ if (cc->c.table->addr_type == type)
+ return cc->c.table;
+
+ /* Find paired channel with the same SAFI but the other AFI */
+ u32 afi2 = cc->afi ^ 0x30000;
+ cc2 = bgp_find_channel_config(cf, afi2);
+
+ /* Second, try IGP table configured in the paired channel */
+ if (cc2 && (tab = (type == NET_IP4) ? cc2->igp_table_ip4 : cc2->igp_table_ip6))
+ return tab;
+
+ /* Third, try table connected by the paired channel */
+ if (cc2 && (cc2->c.table->addr_type == type))
+ return cc2->c.table;
+
+ /* Last, try default table of given type */
+ if (tab = cf->c.global->def_tables[type])
+ return tab;
+
+ cf_error("Undefined IGP table");
+}
+
+
void
bgp_postconfig(struct proto_config *CF)
{
@@ -1568,6 +1624,15 @@ bgp_postconfig(struct proto_config *CF)
if (cc->gr_able == 0xff)
cc->gr_able = (cf->gr_mode == BGP_GR_ABLE);
+ if ((cc->gw_mode == GW_RECURSIVE) && !cc->desc->no_igp)
+ {
+ if (!cc->igp_table_ip4 && (bgp_cc_is_ipv4(cc) || cc->ext_next_hop))
+ cc->igp_table_ip4 = bgp_default_igp_table(cf, cc, NET_IP4);
+
+ if (!cc->igp_table_ip6 && (bgp_cc_is_ipv6(cc) || cc->ext_next_hop))
+ cc->igp_table_ip6 = bgp_default_igp_table(cf, cc, NET_IP6);
+ }
+
if (cf->multihop && (cc->gw_mode == GW_DIRECT))
cf_error("Multihop BGP cannot use direct gateway mode");
@@ -1637,11 +1702,17 @@ bgp_channel_reconfigure(struct channel *C, struct channel_config *CC)
if (memcmp(((byte *) old) + sizeof(struct channel_config),
((byte *) new) + sizeof(struct channel_config),
- /* igp_table item is last and must be checked separately */
- OFFSETOF(struct bgp_channel_config, igp_table) - sizeof(struct channel_config)))
+ /* Remaining items must be checked separately */
+ OFFSETOF(struct bgp_channel_config, rest) - sizeof(struct channel_config)))
return 0;
- if (get_igp_table(old) != get_igp_table(new))
+ /* Check change in IGP tables */
+ rtable *old4 = old->igp_table_ip4 ? old->igp_table_ip4->table : NULL;
+ rtable *old6 = old->igp_table_ip6 ? old->igp_table_ip6->table : NULL;
+ rtable *new4 = new->igp_table_ip4 ? new->igp_table_ip4->table : NULL;
+ rtable *new6 = new->igp_table_ip6 ? new->igp_table_ip6->table : NULL;
+
+ if ((old4 != new4) || (old6 != new6))
return 0;
c->cf = new;
@@ -1956,9 +2027,17 @@ bgp_show_proto_info(struct proto *P)
{
/* XXXX ?? */
- struct channel *c;
+ struct bgp_channel *c;
WALK_LIST(c, p->p.channels)
- channel_show_info(c);
+ {
+ channel_show_info(&c->c);
+
+ if (c->igp_table_ip4)
+ cli_msg(-1006, " IGP IPv4 table: %s", c->igp_table_ip4->name);
+
+ if (c->igp_table_ip6)
+ cli_msg(-1006, " IGP IPv6 table: %s", c->igp_table_ip6->name);
+ }
}
}