summaryrefslogtreecommitdiff
path: root/proto/bgp/packets.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/packets.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/packets.c')
-rw-r--r--proto/bgp/packets.c362
1 files changed, 185 insertions, 177 deletions
diff --git a/proto/bgp/packets.c b/proto/bgp/packets.c
index 5819965d..e11be197 100644
--- a/proto/bgp/packets.c
+++ b/proto/bgp/packets.c
@@ -760,7 +760,8 @@ bgp_apply_next_hop(struct bgp_parse_state *s, rta *a, ip_addr gw, ip_addr ll)
if (ipa_zero(gw))
WITHDRAW(BAD_NEXT_HOP);
- s->hostentry = rt_get_hostentry(c->igp_table, gw, ll, c->c.table);
+ rtable *tab = ipa_is_ip4(gw) ? c->igp_table_ip4 : c->igp_table_ip6;
+ s->hostentry = rt_get_hostentry(tab, gw, ll, c->c.table);
if (!s->mpls)
rta_apply_hostentry(a, s->hostentry, NULL);
@@ -887,18 +888,181 @@ bgp_update_next_hop_ip(struct bgp_export_state *s, eattr *a, ea_list **to)
ip_addr peer = s->proto->cf->remote_ip;
uint len = a->u.ptr->length;
+ /* Forbid zero next hop */
if (ipa_zero(nh[0]) && ((len != 32) || ipa_zero(nh[1])))
WITHDRAW(BAD_NEXT_HOP);
+ /* Forbid next hop equal to neighbor IP */
if (ipa_equal(peer, nh[0]) || ((len == 32) && ipa_equal(peer, nh[1])))
WITHDRAW(BAD_NEXT_HOP);
+ /* Forbid next hop with non-matching AF */
+ if ((ipa_is_ip4(nh[0]) != bgp_channel_is_ipv4(s->channel)) &&
+ !s->channel->ext_next_hop)
+ WITHDRAW(BAD_NEXT_HOP);
+
/* Just check if MPLS stack */
if (s->mpls && !bgp_find_attr(*to, BA_MPLS_LABEL_STACK))
WITHDRAW(NO_LABEL_STACK);
}
static uint
+bgp_encode_next_hop_ip(struct bgp_write_state *s, eattr *a, byte *buf, uint size UNUSED)
+{
+ /* This function is used only for MP-BGP, see bgp_encode_next_hop() for IPv4 BGP */
+ ip_addr *nh = (void *) a->u.ptr->data;
+ uint len = a->u.ptr->length;
+
+ ASSERT((len == 16) || (len == 32));
+
+ /*
+ * Both IPv4 and IPv6 next hops can be used (with ext_next_hop enabled). This
+ * is specified in RFC 5549 for IPv4 and in RFC 4798 for IPv6. The difference
+ * is that IPv4 address is directly encoded with IPv4 NLRI, but as IPv4-mapped
+ * IPv6 address with IPv6 NLRI.
+ */
+
+ if (bgp_channel_is_ipv4(s->channel) && ipa_is_ip4(nh[0]))
+ {
+ put_ip4(buf, ipa_to_ip4(nh[0]));
+ return 4;
+ }
+
+ put_ip6(buf, ipa_to_ip6(nh[0]));
+
+ if (len == 32)
+ put_ip6(buf+16, ipa_to_ip6(nh[1]));
+
+ return len;
+}
+
+static void
+bgp_decode_next_hop_ip(struct bgp_parse_state *s, byte *data, uint len, rta *a)
+{
+ struct bgp_channel *c = s->channel;
+ struct adata *ad = lp_alloc_adata(s->pool, 32);
+ ip_addr *nh = (void *) ad->data;
+
+ if (len == 4)
+ {
+ nh[0] = ipa_from_ip4(get_ip4(data));
+ nh[1] = IPA_NONE;
+ }
+ else if (len == 16)
+ {
+ nh[0] = ipa_from_ip6(get_ip6(data));
+ nh[1] = IPA_NONE;
+
+ if (ipa_is_link_local(nh[0]))
+ { nh[1] = nh[0]; nh[0] = IPA_NONE; }
+ }
+ else if (len == 32)
+ {
+ nh[0] = ipa_from_ip6(get_ip6(data));
+ nh[1] = ipa_from_ip6(get_ip6(data+16));
+
+ if (ipa_is_ip4(nh[0]) || !ip6_is_link_local(nh[1]))
+ nh[1] = IPA_NONE;
+ }
+ else
+ bgp_parse_error(s, 9);
+
+ if (ipa_zero(nh[1]))
+ ad->length = 16;
+
+ if ((bgp_channel_is_ipv4(c) != ipa_is_ip4(nh[0])) && !c->ext_next_hop)
+ WITHDRAW(BAD_NEXT_HOP);
+
+ // XXXX validate next hop
+
+ bgp_set_attr_ptr(&(a->eattrs), s->pool, BA_NEXT_HOP, 0, ad);
+ bgp_apply_next_hop(s, a, nh[0], nh[1]);
+}
+
+static uint
+bgp_encode_next_hop_vpn(struct bgp_write_state *s, eattr *a, byte *buf, uint size UNUSED)
+{
+ ip_addr *nh = (void *) a->u.ptr->data;
+ uint len = a->u.ptr->length;
+
+ ASSERT((len == 16) || (len == 32));
+
+ /*
+ * Both IPv4 and IPv6 next hops can be used (with ext_next_hop enabled). This
+ * is specified in RFC 5549 for VPNv4 and in RFC 4659 for VPNv6. The difference
+ * is that IPv4 address is directly encoded with VPNv4 NLRI, but as IPv4-mapped
+ * IPv6 address with VPNv6 NLRI.
+ */
+
+ if (bgp_channel_is_ipv4(s->channel) && ipa_is_ip4(nh[0]))
+ {
+ put_u64(buf, 0); /* VPN RD is 0 */
+ put_ip4(buf+8, ipa_to_ip4(nh[0]));
+ return 12;
+ }
+
+ put_u64(buf, 0); /* VPN RD is 0 */
+ put_ip6(buf+8, ipa_to_ip6(nh[0]));
+
+ if (len == 16)
+ return 24;
+
+ put_u64(buf+24, 0); /* VPN RD is 0 */
+ put_ip6(buf+32, ipa_to_ip6(nh[1]));
+
+ return 48;
+}
+
+static void
+bgp_decode_next_hop_vpn(struct bgp_parse_state *s, byte *data, uint len, rta *a)
+{
+ struct bgp_channel *c = s->channel;
+ struct adata *ad = lp_alloc_adata(s->pool, 32);
+ ip_addr *nh = (void *) ad->data;
+
+ if (len == 12)
+ {
+ nh[0] = ipa_from_ip4(get_ip4(data+8));
+ nh[1] = IPA_NONE;
+ }
+ else if (len == 24)
+ {
+ nh[0] = ipa_from_ip6(get_ip6(data+8));
+ nh[1] = IPA_NONE;
+
+ if (ipa_is_link_local(nh[0]))
+ { nh[1] = nh[0]; nh[0] = IPA_NONE; }
+ }
+ else if (len == 48)
+ {
+ nh[0] = ipa_from_ip6(get_ip6(data+8));
+ nh[1] = ipa_from_ip6(get_ip6(data+32));
+
+ if (ipa_is_ip4(nh[0]) || !ip6_is_link_local(nh[1]))
+ nh[1] = IPA_NONE;
+ }
+ else
+ bgp_parse_error(s, 9);
+
+ if (ipa_zero(nh[1]))
+ ad->length = 16;
+
+ /* XXXX which error */
+ if ((get_u64(data) != 0) || ((len == 48) && (get_u64(data+24) != 0)))
+ bgp_parse_error(s, 9);
+
+ if ((bgp_channel_is_ipv4(c) != ipa_is_ip4(nh[0])) && !c->ext_next_hop)
+ WITHDRAW(BAD_NEXT_HOP);
+
+ // XXXX validate next hop
+
+ bgp_set_attr_ptr(&(a->eattrs), s->pool, BA_NEXT_HOP, 0, ad);
+ bgp_apply_next_hop(s, a, nh[0], nh[1]);
+}
+
+
+
+static uint
bgp_encode_next_hop_none(struct bgp_write_state *s UNUSED, eattr *a UNUSED, byte *buf UNUSED, uint size UNUSED)
{
return 0;
@@ -1115,32 +1279,6 @@ bgp_decode_nlri_ip4(struct bgp_parse_state *s, byte *pos, uint len, rta *a)
}
}
-static uint
-bgp_encode_next_hop_ip4(struct bgp_write_state *s UNUSED, eattr *a, byte *buf, uint size UNUSED)
-{
- /* This function is used only for MP-BGP, see bgp_encode_next_hop() for IPv4 BGP */
-
- ASSERT(a->u.ptr->length == sizeof(ip_addr));
-
- put_ip4(buf, ipa_to_ip4( *(ip_addr *) a->u.ptr->data ));
-
- return 4;
-}
-
-static void
-bgp_decode_next_hop_ip4(struct bgp_parse_state *s, byte *data, uint len, rta *a)
-{
- if (len != 4)
- bgp_parse_error(s, 9);
-
- ip_addr nh = ipa_from_ip4(get_ip4(data));
-
- // XXXX validate next hop
-
- bgp_set_attr_data(&(a->eattrs), s->pool, BA_NEXT_HOP, 0, &nh, sizeof(nh));
- bgp_apply_next_hop(s, a, nh, IPA_NONE);
-}
-
static uint
bgp_encode_nlri_ip6(struct bgp_write_state *s, struct bgp_bucket *buck, byte *buf, uint size)
@@ -1227,53 +1365,6 @@ bgp_decode_nlri_ip6(struct bgp_parse_state *s, byte *pos, uint len, rta *a)
}
static uint
-bgp_encode_next_hop_ip6(struct bgp_write_state *s UNUSED, eattr *a, byte *buf, uint size UNUSED)
-{
- ip_addr *nh = (void *) a->u.ptr->data;
- uint len = a->u.ptr->length;
-
- ASSERT((len == 16) || (len == 32));
-
- put_ip6(buf, ipa_to_ip6(nh[0]));
-
- if (len == 32)
- put_ip6(buf+16, ipa_to_ip6(nh[1]));
-
- return len;
-}
-
-static void
-bgp_decode_next_hop_ip6(struct bgp_parse_state *s, byte *data, uint len, rta *a)
-{
- struct adata *ad = lp_alloc_adata(s->pool, 32);
- ip_addr *nh = (void *) ad->data;
-
- if ((len != 16) && (len != 32))
- bgp_parse_error(s, 9);
-
- nh[0] = ipa_from_ip6(get_ip6(data));
- nh[1] = (len == 32) ? ipa_from_ip6(get_ip6(data+16)) : IPA_NONE;
-
- if (ip6_is_link_local(nh[0]))
- {
- nh[1] = nh[0];
- nh[0] = IPA_NONE;
- }
-
- if (!ip6_is_link_local(nh[1]))
- nh[1] = IPA_NONE;
-
- if (ipa_zero(nh[1]))
- ad->length = 16;
-
- // XXXX validate next hop
-
- bgp_set_attr_ptr(&(a->eattrs), s->pool, BA_NEXT_HOP, 0, ad);
- bgp_apply_next_hop(s, a, nh[0], nh[1]);
-}
-
-
-static uint
bgp_encode_nlri_vpn4(struct bgp_write_state *s, struct bgp_bucket *buck, byte *buf, uint size)
{
byte *pos = buf;
@@ -1367,37 +1458,6 @@ bgp_decode_nlri_vpn4(struct bgp_parse_state *s, byte *pos, uint len, rta *a)
}
}
-static uint
-bgp_encode_next_hop_vpn4(struct bgp_write_state *s UNUSED, eattr *a, byte *buf, uint size UNUSED)
-{
- /* This function is used only for MP-BGP, see bgp_encode_next_hop() for IPv4 BGP */
-
- ASSERT(a->u.ptr->length == sizeof(ip_addr));
-
- put_u64(buf, 0); /* VPN RD is 0 */
- put_ip4(buf+8, ipa_to_ip4( *(ip_addr *) a->u.ptr->data ));
-
- return 12;
-}
-
-static void
-bgp_decode_next_hop_vpn4(struct bgp_parse_state *s, byte *data, uint len, rta *a)
-{
- if (len != 12)
- bgp_parse_error(s, 9);
-
- /* XXXX which error */
- if (get_u64(data) != 0)
- bgp_parse_error(s, 9);
-
- ip_addr nh = ipa_from_ip4(get_ip4(data+8));
-
- // XXXX validate next hop
-
- bgp_set_attr_data(&(a->eattrs), s->pool, BA_NEXT_HOP, 0, &nh, sizeof(nh));
- bgp_apply_next_hop(s, a, nh, IPA_NONE);
-}
-
static uint
bgp_encode_nlri_vpn6(struct bgp_write_state *s, struct bgp_bucket *buck, byte *buf, uint size)
@@ -1494,60 +1554,6 @@ bgp_decode_nlri_vpn6(struct bgp_parse_state *s, byte *pos, uint len, rta *a)
}
}
-static uint
-bgp_encode_next_hop_vpn6(struct bgp_write_state *s UNUSED, eattr *a, byte *buf, uint size UNUSED)
-{
- ip_addr *nh = (void *) a->u.ptr->data;
- uint len = a->u.ptr->length;
-
- ASSERT((len == 16) || (len == 32));
-
- put_u64(buf, 0); /* VPN RD is 0 */
- put_ip6(buf+8, ipa_to_ip6(nh[0]));
-
- if (len == 16)
- return 24;
-
- put_u64(buf+24, 0); /* VPN RD is 0 */
- put_ip6(buf+32, ipa_to_ip6(nh[1]));
-
- return 48;
-}
-
-static void
-bgp_decode_next_hop_vpn6(struct bgp_parse_state *s, byte *data, uint len, rta *a)
-{
- struct adata *ad = lp_alloc_adata(s->pool, 32);
- ip_addr *nh = (void *) ad->data;
-
- if ((len != 24) && (len != 48))
- bgp_parse_error(s, 9);
-
- /* XXXX which error */
- if ((get_u64(data) != 0) || ((len == 48) && (get_u64(data+24) != 0)))
- bgp_parse_error(s, 9);
-
- nh[0] = ipa_from_ip6(get_ip6(data+8));
- nh[1] = (len == 48) ? ipa_from_ip6(get_ip6(data+32)) : IPA_NONE;
-
- if (ip6_is_link_local(nh[0]))
- {
- nh[1] = nh[0];
- nh[0] = IPA_NONE;
- }
-
- if (!ip6_is_link_local(nh[1]))
- nh[1] = IPA_NONE;
-
- if (ipa_zero(nh[1]))
- ad->length = 16;
-
- // XXXX validate next hop
-
- bgp_set_attr_ptr(&(a->eattrs), s->pool, BA_NEXT_HOP, 0, ad);
- bgp_apply_next_hop(s, a, nh[0], nh[1]);
-}
-
static uint
bgp_encode_nlri_flow4(struct bgp_write_state *s, struct bgp_bucket *buck, byte *buf, uint size)
@@ -1740,8 +1746,8 @@ static const struct bgp_af_desc bgp_af_table[] = {
.name = "ipv4",
.encode_nlri = bgp_encode_nlri_ip4,
.decode_nlri = bgp_decode_nlri_ip4,
- .encode_next_hop = bgp_encode_next_hop_ip4,
- .decode_next_hop = bgp_decode_next_hop_ip4,
+ .encode_next_hop = bgp_encode_next_hop_ip,
+ .decode_next_hop = bgp_decode_next_hop_ip,
.update_next_hop = bgp_update_next_hop_ip,
},
{
@@ -1750,8 +1756,8 @@ static const struct bgp_af_desc bgp_af_table[] = {
.name = "ipv4-mc",
.encode_nlri = bgp_encode_nlri_ip4,
.decode_nlri = bgp_decode_nlri_ip4,
- .encode_next_hop = bgp_encode_next_hop_ip4,
- .decode_next_hop = bgp_decode_next_hop_ip4,
+ .encode_next_hop = bgp_encode_next_hop_ip,
+ .decode_next_hop = bgp_decode_next_hop_ip,
.update_next_hop = bgp_update_next_hop_ip,
},
{
@@ -1761,8 +1767,8 @@ static const struct bgp_af_desc bgp_af_table[] = {
.name = "ipv4-mpls",
.encode_nlri = bgp_encode_nlri_ip4,
.decode_nlri = bgp_decode_nlri_ip4,
- .encode_next_hop = bgp_encode_next_hop_ip4,
- .decode_next_hop = bgp_decode_next_hop_ip4,
+ .encode_next_hop = bgp_encode_next_hop_ip,
+ .decode_next_hop = bgp_decode_next_hop_ip,
.update_next_hop = bgp_update_next_hop_ip,
},
{
@@ -1771,8 +1777,8 @@ static const struct bgp_af_desc bgp_af_table[] = {
.name = "ipv6",
.encode_nlri = bgp_encode_nlri_ip6,
.decode_nlri = bgp_decode_nlri_ip6,
- .encode_next_hop = bgp_encode_next_hop_ip6,
- .decode_next_hop = bgp_decode_next_hop_ip6,
+ .encode_next_hop = bgp_encode_next_hop_ip,
+ .decode_next_hop = bgp_decode_next_hop_ip,
.update_next_hop = bgp_update_next_hop_ip,
},
{
@@ -1781,8 +1787,8 @@ static const struct bgp_af_desc bgp_af_table[] = {
.name = "ipv6-mc",
.encode_nlri = bgp_encode_nlri_ip6,
.decode_nlri = bgp_decode_nlri_ip6,
- .encode_next_hop = bgp_encode_next_hop_ip6,
- .decode_next_hop = bgp_decode_next_hop_ip6,
+ .encode_next_hop = bgp_encode_next_hop_ip,
+ .decode_next_hop = bgp_decode_next_hop_ip,
.update_next_hop = bgp_update_next_hop_ip,
},
{
@@ -1792,8 +1798,8 @@ static const struct bgp_af_desc bgp_af_table[] = {
.name = "ipv6-mpls",
.encode_nlri = bgp_encode_nlri_ip6,
.decode_nlri = bgp_decode_nlri_ip6,
- .encode_next_hop = bgp_encode_next_hop_ip6,
- .decode_next_hop = bgp_decode_next_hop_ip6,
+ .encode_next_hop = bgp_encode_next_hop_ip,
+ .decode_next_hop = bgp_decode_next_hop_ip,
.update_next_hop = bgp_update_next_hop_ip,
},
{
@@ -1803,8 +1809,8 @@ static const struct bgp_af_desc bgp_af_table[] = {
.name = "vpn4-mpls",
.encode_nlri = bgp_encode_nlri_vpn4,
.decode_nlri = bgp_decode_nlri_vpn4,
- .encode_next_hop = bgp_encode_next_hop_vpn4,
- .decode_next_hop = bgp_decode_next_hop_vpn4,
+ .encode_next_hop = bgp_encode_next_hop_vpn,
+ .decode_next_hop = bgp_decode_next_hop_vpn,
.update_next_hop = bgp_update_next_hop_ip,
},
{
@@ -1814,13 +1820,14 @@ static const struct bgp_af_desc bgp_af_table[] = {
.name = "vpn6-mpls",
.encode_nlri = bgp_encode_nlri_vpn6,
.decode_nlri = bgp_decode_nlri_vpn6,
- .encode_next_hop = bgp_encode_next_hop_vpn6,
- .decode_next_hop = bgp_decode_next_hop_vpn6,
+ .encode_next_hop = bgp_encode_next_hop_vpn,
+ .decode_next_hop = bgp_decode_next_hop_vpn,
.update_next_hop = bgp_update_next_hop_ip,
},
{
.afi = BGP_AF_FLOW4,
.net = NET_FLOW4,
+ .no_igp = 1,
.name = "flow4",
.encode_nlri = bgp_encode_nlri_flow4,
.decode_nlri = bgp_decode_nlri_flow4,
@@ -1831,6 +1838,7 @@ static const struct bgp_af_desc bgp_af_table[] = {
{
.afi = BGP_AF_FLOW6,
.net = NET_FLOW6,
+ .no_igp = 1,
.name = "flow6",
.encode_nlri = bgp_encode_nlri_flow6,
.decode_nlri = bgp_decode_nlri_flow6,
@@ -2039,7 +2047,7 @@ again: ;
/* Try unreachable bucket */
if ((buck = c->withdraw_bucket) && !EMPTY_LIST(buck->prefixes))
{
- res = (c->afi == BGP_AF_IPV4) ?
+ res = (c->afi == BGP_AF_IPV4) && !c->ext_next_hop ?
bgp_create_ip_unreach(&s, buck, buf, end):
bgp_create_mp_unreach(&s, buck, buf, end);
@@ -2058,7 +2066,7 @@ again: ;
goto again;
}
- res = (c->afi == BGP_AF_IPV4) ?
+ res = (c->afi == BGP_AF_IPV4) && !c->ext_next_hop ?
bgp_create_ip_reach(&s, buck, buf, end):
bgp_create_mp_reach(&s, buck, buf, end);