enabled a route may end with a link-local immediate next hop when the
IGP route has one. Default: disabled.
+ <tag><label id="bgp-link-local-next-hop-format">link local next hop format native|single|double</tag>
+ For IPv6 routes, BGP assumes that the Next Hop attribute contains a
+ global IPv6 address (in the first position) and an optional link-local
+ IPv6 address (in the second position): [<m/global/, <m/link-local/].
+ When a BGP session is established using just link-local addresses, there
+ may be no global IPv6 address for the next hop. BGP implementations
+ differ on how to encode such next hops. BIRD <cf/native/ format is to
+ send [zero, <m/link-local/], <cf/single/ format is [<m/link-local/],
+ <cf/double/ format is [<m/link-local/, <m/link-local/]. BIRD accepts all
+ these variants when decoding received routes, but this option controls
+ which one it uses to encode such next hops. Default: native.
+
<tag><label id="bgp-gateway">gateway direct|recursive</tag>
For received routes, their <cf/gw/ (immediate next hop) attribute is
computed from received <cf/bgp_next_hop/ attribute. This option
<item><cf/next hop address/
<item><cf/next hop self/
<item><cf/next hop keep/
+ <item><cf/link local next hop format/
<item><cf/aigp/
<item><cf/aigp originate/
</itemize>
#define allocz(len) ({ void *_x = alloca(len); memset(_x, 0, len); _x; })
+#define alloca_copy(src,len) ({ void *_x = alloca(len); memcpy(_x, src, len); })
+
#endif
if (!ipa_equal(new->next_hop_addr, old->next_hop_addr) ||
(new->next_hop_self != old->next_hop_self) ||
(new->next_hop_keep != old->next_hop_keep) ||
+ (new->llnh_format != old->llnh_format) ||
(new->aigp != old->aigp) ||
(new->aigp_originate != old->aigp_originate))
*export_changed = 1;
u8 next_hop_self; /* Always set next hop to local IP address (NH_*) */
u8 next_hop_keep; /* Do not modify next hop attribute (NH_*) */
u8 next_hop_prefer; /* Prefer global or link-local next hop (NHP_*) */
+ u8 llnh_format; /* Requested link-local next hop format (LLNH_*) */
u8 mandatory; /* Channel is mandatory in capability negotiation */
u8 gw_mode; /* How we compute route gateway from next_hop attr, see GW_* */
u8 secondary; /* Accept also non-best routes (i.e. RA_ACCEPTED) */
#define MLL_DROP 2
#define MLL_IGNORE 3
+#define LLNH_NATIVE 0
+#define LLNH_SINGLE 1
+#define LLNH_DOUBLE 2
+
#define GW_DIRECT 1
#define GW_RECURSIVE 2
int mp_reach;
int as4_session;
int add_path;
+ int llnh_format;
int mpls;
int sham;
int ignore_non_bgp_attrs;
LIVED, STALE, IMPORT, IBGP, EBGP, MANDATORY, INTERNAL, EXTERNAL, SETS,
DYNAMIC, RANGE, NAME, DIGITS, BGP_AIGP, AIGP, ORIGINATE, COST, ENFORCE,
FIRST, FREE, VALIDATE, BASE, ROLE, ROLES, PEER, PROVIDER, CUSTOMER,
- RS_SERVER, RS_CLIENT, REQUIRE, BGP_OTC, GLOBAL, SEND, MIN, MAX)
+ RS_SERVER, RS_CLIENT, REQUIRE, BGP_OTC, GLOBAL, SEND, MIN, MAX,
+ FORMAT, NATIVE, SINGLE, DOUBLE)
-%type <i> bgp_nh
+%type <i> bgp_nh bgp_llnh
%type <i32> bgp_afi
CF_KEYWORDS(CEASE, PREFIX, LIMIT, HIT, ADMINISTRATIVE, SHUTDOWN, RESET, PEER,
bool { $$ = $1; }
| IBGP { $$ = NH_IBGP; }
| EBGP { $$ = NH_EBGP; }
+ ;
+
+bgp_llnh:
+ NATIVE { $$ = LLNH_NATIVE; }
+ | SINGLE { $$ = LLNH_SINGLE; }
+ | DOUBLE { $$ = LLNH_DOUBLE; }
+ ;
bgp_lladdr: SELF | DROP | IGNORE;
| NEXT HOP SELF bgp_nh { BGP_CC->next_hop_self = $4; }
| NEXT HOP KEEP bgp_nh { BGP_CC->next_hop_keep = $4; }
| NEXT HOP PREFER GLOBAL { BGP_CC->next_hop_prefer = NHP_GLOBAL; }
+ | LINK LOCAL NEXT HOP FORMAT bgp_llnh { BGP_CC->llnh_format = $6; }
| MANDATORY bool { BGP_CC->mandatory = $2; }
| MISSING LLADDR bgp_lladdr { cf_warn("%s.%s: Missing lladdr option is deprecated and ignored, remove it", this_proto->name, this_channel->name); }
| GATEWAY DIRECT { BGP_CC->gw_mode = GW_DIRECT; }
REJECT(NO_LABEL_STACK);
}
+static uint
+bgp_prepare_link_local_next_hop(struct bgp_write_state *s, ip_addr *nh)
+{
+ /*
+ * We have link-local next-hop in nh[1]
+ *
+ * Possible variants:
+ * [ ::, fe80::XX ] - BIRD's default/internal (LLNH_NATIVE)
+ * [ fe80::XX ] - draft-white-linklocal-capability (LLNH_SINGLE)
+ * [ fe80::XX, fe80::XX ] - Used in JunoOS (LLNH_DOUBLE)
+ */
+
+ switch (s->llnh_format)
+ {
+ case LLNH_NATIVE:
+ return 32;
+
+ case LLNH_SINGLE:
+ nh[0] = nh[1];
+ return 16;
+
+ case LLNH_DOUBLE:
+ nh[0] = nh[1];
+ return 32;
+
+ default:
+ ASSERT(0);
+ return 32;
+ }
+}
+
static uint
bgp_encode_next_hop_ip(struct bgp_write_state *s, eattr *a, byte *buf, uint size UNUSED)
{
return 4;
}
+ /* Handle variants of link-local-only next hop */
+ if (ipa_zero(nh[0]) && (len == 32))
+ {
+ nh = alloca_copy(nh, 32);
+ len = bgp_prepare_link_local_next_hop(s, nh);
+ }
+
put_ip6(buf, ipa_to_ip6(nh[0]));
if (len == 32)
return 12;
}
+ /* Handle variants of link-local-only next hop */
+ if (ipa_zero(nh[0]) && (len == 32))
+ {
+ nh = alloca_copy(nh, 32);
+ len = bgp_prepare_link_local_next_hop(s, nh);
+ }
+
put_u64(buf, 0); /* VPN RD is 0 */
put_ip6(buf+8, ipa_to_ip6(nh[0]));
.mp_reach = (c->afi != BGP_AF_IPV4) || c->ext_next_hop,
.as4_session = p->as4_session,
.add_path = c->add_path_tx,
+ .llnh_format = c->cf->llnh_format,
.mpls = c->desc->mpls,
};