diff options
-rw-r--r-- | doc/bird.sgml | 6 | ||||
-rw-r--r-- | proto/babel/babel.c | 29 | ||||
-rw-r--r-- | proto/babel/babel.h | 4 | ||||
-rw-r--r-- | proto/babel/config.Y | 5 | ||||
-rw-r--r-- | proto/babel/packets.c | 115 |
5 files changed, 118 insertions, 41 deletions
diff --git a/doc/bird.sgml b/doc/bird.sgml index 001fcbd4..6f3fcfc6 100644 --- a/doc/bird.sgml +++ b/doc/bird.sgml @@ -1929,6 +1929,7 @@ protocol babel [<name>] { check link <switch>; next hop ipv4 <address>; next hop ipv6 <address>; + extended next hop <switch>; authentication none|mac [permissive]; password "<text>"; password "<text>" { @@ -2033,6 +2034,11 @@ protocol babel [<name>] { source for Babel packets will be used. In normal operation, it should not be necessary to set this option. + <tag><label id="babel-extended-next-hop">extended next hop <m/switch/</tag> + If enabled, BIRD will accept and emit IPv4 routes with an IPv6 next + hop when IPv4 addresses are absent from the interface as described in + <rfc id="9229">. Default: yes. + <tag><label id="babel-authentication">authentication none|mac [permissive]</tag> Selects authentication method to be used. <cf/none/ means that packets are not authenticated at all, <cf/mac/ means MAC authentication is diff --git a/proto/babel/babel.c b/proto/babel/babel.c index becff6d0..a9a08e31 100644 --- a/proto/babel/babel.c +++ b/proto/babel/babel.c @@ -1003,8 +1003,18 @@ babel_send_update_(struct babel_iface *ifa, btime changed, struct fib *rtable) msg.update.router_id = e->router_id; net_copy(&msg.update.net, e->n.addr); - msg.update.next_hop = ((e->n.addr->type == NET_IP4) ? - ifa->next_hop_ip4 : ifa->next_hop_ip6); + if (e->n.addr->type == NET_IP4) + { + /* Always prefer IPv4 nexthop if set */ + if (ipa_nonzero(ifa->next_hop_ip4)) + msg.update.next_hop = ifa->next_hop_ip4; + + /* Only send IPv6 nexthop if enabled */ + else if (ifa->cf->ext_next_hop) + msg.update.next_hop = ifa->next_hop_ip6; + } + else + msg.update.next_hop = ifa->next_hop_ip6; /* Do not send route if next hop is unknown, e.g. no configured IPv4 address */ if (ipa_zero(msg.update.next_hop)) @@ -1263,6 +1273,13 @@ babel_handle_update(union babel_msg *m, struct babel_iface *ifa) return; } + /* Reject IPv4 via IPv6 routes if disabled */ + if ((msg->net.type == NET_IP4) && ipa_is_ip6(msg->next_hop) && !ifa->cf->ext_next_hop) + { + DBG("Babel: Ignoring disabled IPv4 via IPv6 route.\n"); + return; + } + /* Retraction */ if (msg->metric == BABEL_INFINITY) { @@ -1729,7 +1746,7 @@ babel_iface_update_addr4(struct babel_iface *ifa) ip_addr addr4 = ifa->iface->addr4 ? ifa->iface->addr4->ip : IPA_NONE; ifa->next_hop_ip4 = ipa_nonzero(ifa->cf->next_hop_ip4) ? ifa->cf->next_hop_ip4 : addr4; - if (ipa_zero(ifa->next_hop_ip4) && p->ip4_channel) + if (ipa_zero(ifa->next_hop_ip4) && p->ip4_channel && !ifa->cf->ext_next_hop) log(L_WARN "%s: Missing IPv4 next hop address for %s", p->p.name, ifa->ifname); if (ifa->up) @@ -1806,8 +1823,8 @@ babel_add_iface(struct babel_proto *p, struct iface *new, struct babel_iface_con ifa->next_hop_ip4 = ipa_nonzero(ic->next_hop_ip4) ? ic->next_hop_ip4 : addr4; ifa->next_hop_ip6 = ipa_nonzero(ic->next_hop_ip6) ? ic->next_hop_ip6 : ifa->addr; - if (ipa_zero(ifa->next_hop_ip4) && p->ip4_channel) - log(L_WARN "%s: Missing IPv4 next hop address for %s", p->p.name, new->name); + if (ipa_zero(ifa->next_hop_ip4) && p->ip4_channel && !ic->ext_next_hop) + log(L_WARN "%s: Missing IPv4 next hop address for %s", p->p.name, ifa->ifname); init_list(&ifa->neigh_list); ifa->hello_seqno = 1; @@ -1927,7 +1944,7 @@ babel_reconfigure_iface(struct babel_proto *p, struct babel_iface *ifa, struct b if ((new->auth_type != BABEL_AUTH_NONE) && (new->auth_type != old->auth_type)) babel_auth_reset_index(ifa); - if (ipa_zero(ifa->next_hop_ip4) && p->ip4_channel) + if (ipa_zero(ifa->next_hop_ip4) && p->ip4_channel && !new->ext_next_hop) log(L_WARN "%s: Missing IPv4 next hop address for %s", p->p.name, ifa->ifname); if (ifa->next_hello > (current_time() + new->hello_interval)) diff --git a/proto/babel/babel.h b/proto/babel/babel.h index da8386b3..6699127e 100644 --- a/proto/babel/babel.h +++ b/proto/babel/babel.h @@ -114,6 +114,7 @@ enum babel_ae_type { BABEL_AE_IP4 = 1, BABEL_AE_IP6 = 2, BABEL_AE_IP6_LL = 3, + BABEL_AE_IP4_VIA_IP6 = 4, BABEL_AE_MAX }; @@ -147,8 +148,9 @@ struct babel_iface_config { ip_addr next_hop_ip4; ip_addr next_hop_ip6; + u8 ext_next_hop; /* Enable IPv4 via IPv6 */ - u8 auth_type; /* Authentication type (BABEL_AUTH_*) */ + u8 auth_type; /* Authentication type (BABEL_AUTH_*) */ u8 auth_permissive; /* Don't drop packets failing auth check */ uint mac_num_keys; /* Number of configured HMAC keys */ uint mac_total_len; /* Total digest length for all configured keys */ diff --git a/proto/babel/config.Y b/proto/babel/config.Y index 05210fa4..1b4dc6f5 100644 --- a/proto/babel/config.Y +++ b/proto/babel/config.Y @@ -25,7 +25,8 @@ CF_DECLS CF_KEYWORDS(BABEL, INTERFACE, METRIC, RXCOST, HELLO, UPDATE, INTERVAL, PORT, TYPE, WIRED, WIRELESS, RX, TX, BUFFER, PRIORITY, LENGTH, CHECK, LINK, NEXT, HOP, IPV4, IPV6, BABEL_METRIC, SHOW, INTERFACES, NEIGHBORS, - ENTRIES, RANDOMIZE, ROUTER, ID, AUTHENTICATION, NONE, MAC, PERMISSIVE) + ENTRIES, RANDOMIZE, ROUTER, ID, AUTHENTICATION, NONE, MAC, PERMISSIVE, + EXTENDED) CF_GRAMMAR @@ -67,6 +68,7 @@ babel_iface_start: BABEL_IFACE->tx_tos = IP_PREC_INTERNET_CONTROL; BABEL_IFACE->tx_priority = sk_priority_control; BABEL_IFACE->check_link = 1; + BABEL_IFACE->ext_next_hop = 1; }; @@ -143,6 +145,7 @@ babel_iface_item: | CHECK LINK bool { BABEL_IFACE->check_link = $3; } | NEXT HOP IPV4 ipa { BABEL_IFACE->next_hop_ip4 = $4; if (!ipa_is_ip4($4)) cf_error("Must be an IPv4 address"); } | NEXT HOP IPV6 ipa { BABEL_IFACE->next_hop_ip6 = $4; if (!ipa_is_ip6($4)) cf_error("Must be an IPv6 address"); } + | EXTENDED NEXT HOP bool { BABEL_IFACE->ext_next_hop = $4; } | AUTHENTICATION NONE { BABEL_IFACE->auth_type = BABEL_AUTH_NONE; } | AUTHENTICATION MAC { BABEL_IFACE->auth_type = BABEL_AUTH_MAC; BABEL_IFACE->auth_permissive = 0; } | AUTHENTICATION MAC PERMISSIVE { BABEL_IFACE->auth_type = BABEL_AUTH_MAC; BABEL_IFACE->auth_permissive = 1; } diff --git a/proto/babel/packets.c b/proto/babel/packets.c index d4acc170..28bf9f63 100644 --- a/proto/babel/packets.c +++ b/proto/babel/packets.c @@ -166,10 +166,12 @@ struct babel_parse_state { ip_addr next_hop_ip6; u64 router_id; /* Router ID used in subsequent updates */ u8 def_ip6_prefix[16]; /* Implicit IPv6 prefix in network order */ - u8 def_ip4_prefix[4]; /* Implicit IPv4 prefix in network order */ + u8 def_ip4_prefix[4]; /* Implicit IPv4 prefix (AE 1) in network order */ + u8 def_ip4_via_ip6_prefix[4]; /* Implicit IPv4 prefix (AE 4) in network order */ u8 router_id_seen; /* router_id field is valid */ u8 def_ip6_prefix_seen; /* def_ip6_prefix is valid */ u8 def_ip4_prefix_seen; /* def_ip4_prefix is valid */ + u8 def_ip4_via_ip6_prefix_seen; /* def_ip4_via_ip6_prefix is valid */ u8 current_tlv_endpos; /* End of self-terminating TLVs (offset from start) */ u8 sadr_enabled; u8 is_unicast; @@ -515,9 +517,6 @@ babel_read_ihu(struct babel_tlv *hdr, union babel_msg *m, msg->addr = IPA_NONE; msg->sender = state->saddr; - if (msg->ae >= BABEL_AE_MAX) - return PARSE_IGNORE; - /* * We only actually read link-local IPs. In every other case, the addr field * will be 0 but validation will succeed. The handler takes care of these @@ -526,17 +525,20 @@ babel_read_ihu(struct babel_tlv *hdr, union babel_msg *m, */ switch (msg->ae) { + case BABEL_AE_WILDCARD: + return PARSE_SUCCESS; + case BABEL_AE_IP4: if (TLV_OPT_LENGTH(tlv) < 4) return PARSE_ERROR; state->current_tlv_endpos += 4; - break; + return PARSE_SUCCESS; case BABEL_AE_IP6: if (TLV_OPT_LENGTH(tlv) < 16) return PARSE_ERROR; state->current_tlv_endpos += 16; - break; + return PARSE_SUCCESS; case BABEL_AE_IP6_LL: if (TLV_OPT_LENGTH(tlv) < 8) @@ -544,10 +546,17 @@ babel_read_ihu(struct babel_tlv *hdr, union babel_msg *m, msg->addr = ipa_from_ip6(get_ip6_ll(&tlv->addr)); state->current_tlv_endpos += 8; - break; + return PARSE_SUCCESS; + + /* RFC 9229 2.4 - IHU TLV MUST NOT carry the AE 4 (IPv4-via-IPv6) */ + case BABEL_AE_IP4_VIA_IP6: + return PARSE_ERROR; + + default: + return PARSE_IGNORE; } - return PARSE_SUCCESS; + return PARSE_IGNORE; } static uint @@ -640,6 +649,10 @@ babel_read_next_hop(struct babel_tlv *hdr, union babel_msg *m UNUSED, state->current_tlv_endpos += 8; return PARSE_IGNORE; + /* RFC 9229 2.4 - Next Hop TLV MUST NOT carry the AE 4 (IPv4-via-IPv6) */ + case BABEL_AE_IP4_VIA_IP6: + return PARSE_ERROR; + default: return PARSE_IGNORE; } @@ -692,6 +705,42 @@ babel_write_next_hop(struct babel_tlv *hdr, ip_addr addr, return 0; } +/* This is called directly from babel_read_update() to handle + both BABEL_AE_IP4 and BABEL_AE_IP4_VIA_IP6 encodings */ +static int +babel_read_ip4_prefix(struct babel_tlv_update *tlv, struct babel_msg_update *msg, + u8 *def_prefix, u8 *def_prefix_seen, ip_addr next_hop, int len) +{ + if (tlv->plen > IP4_MAX_PREFIX_LENGTH) + return PARSE_ERROR; + + /* Cannot omit data if there is no saved prefix */ + if (tlv->omitted && !*def_prefix_seen) + return PARSE_ERROR; + + /* Update must have next hop, unless it is retraction */ + if (ipa_zero(next_hop) && msg->metric != BABEL_INFINITY) + return PARSE_ERROR; + + /* Merge saved prefix and received prefix parts */ + u8 buf[4] = {}; + memcpy(buf, def_prefix, tlv->omitted); + memcpy(buf + tlv->omitted, tlv->addr, len); + + ip4_addr prefix4 = get_ip4(buf); + net_fill_ip4(&msg->net, prefix4, tlv->plen); + + if (tlv->flags & BABEL_UF_DEF_PREFIX) + { + put_ip4(def_prefix, prefix4); + *def_prefix_seen = 1; + } + + msg->next_hop = next_hop; + + return PARSE_SUCCESS; +} + static int babel_read_update(struct babel_tlv *hdr, union babel_msg *m, struct babel_parse_state *state) @@ -706,11 +755,11 @@ babel_read_update(struct babel_tlv *hdr, union babel_msg *m, /* Length of received prefix data without omitted part */ int len = BYTES(tlv->plen) - (int) tlv->omitted; - u8 buf[16] = {}; if ((len < 0) || ((uint) len > TLV_OPT_LENGTH(tlv))) return PARSE_ERROR; + int rc; switch (tlv->ae) { case BABEL_AE_WILDCARD: @@ -724,31 +773,20 @@ babel_read_update(struct babel_tlv *hdr, union babel_msg *m, break; case BABEL_AE_IP4: - if (tlv->plen > IP4_MAX_PREFIX_LENGTH) - return PARSE_ERROR; - - /* Cannot omit data if there is no saved prefix */ - if (tlv->omitted && !state->def_ip4_prefix_seen) - return PARSE_ERROR; - - /* Update must have next hop, unless it is retraction */ - if (ipa_zero(state->next_hop_ip4) && (msg->metric != BABEL_INFINITY)) - return PARSE_IGNORE; - - /* Merge saved prefix and received prefix parts */ - memcpy(buf, state->def_ip4_prefix, tlv->omitted); - memcpy(buf + tlv->omitted, tlv->addr, len); + rc = babel_read_ip4_prefix(tlv, msg, state->def_ip4_prefix, + &state->def_ip4_prefix_seen, + state->next_hop_ip4, len); + if (rc != PARSE_SUCCESS) + return rc; - ip4_addr prefix4 = get_ip4(buf); - net_fill_ip4(&msg->net, prefix4, tlv->plen); - - if (tlv->flags & BABEL_UF_DEF_PREFIX) - { - put_ip4(state->def_ip4_prefix, prefix4); - state->def_ip4_prefix_seen = 1; - } + break; - msg->next_hop = state->next_hop_ip4; + case BABEL_AE_IP4_VIA_IP6: + rc = babel_read_ip4_prefix(tlv, msg, state->def_ip4_via_ip6_prefix, + &state->def_ip4_via_ip6_prefix_seen, + state->next_hop_ip6, len); + if (rc != PARSE_SUCCESS) + return rc; break; @@ -761,6 +799,7 @@ babel_read_update(struct babel_tlv *hdr, union babel_msg *m, return PARSE_ERROR; /* Merge saved prefix and received prefix parts */ + u8 buf[16] = {}; memcpy(buf, state->def_ip6_prefix, tlv->omitted); memcpy(buf + tlv->omitted, tlv->addr, len); @@ -863,7 +902,7 @@ babel_write_update(struct babel_tlv *hdr, union babel_msg *m, } else if (msg->net.type == NET_IP4) { - tlv->ae = BABEL_AE_IP4; + tlv->ae = ipa_is_ip4(msg->next_hop) ? BABEL_AE_IP4 : BABEL_AE_IP4_VIA_IP6; tlv->plen = net4_pxlen(&msg->net); put_ip4_px(tlv->addr, &msg->net); } @@ -931,7 +970,12 @@ babel_read_route_request(struct babel_tlv *hdr, union babel_msg *m, msg->full = 1; return PARSE_SUCCESS; + /* + * RFC 9229 2.3 - When receiving requests, AE 1 (IPv4) and AE 4 + * (IPv4-via-IPv6) MUST be treated in the same manner. + */ case BABEL_AE_IP4: + case BABEL_AE_IP4_VIA_IP6: if (tlv->plen > IP4_MAX_PREFIX_LENGTH) return PARSE_ERROR; @@ -1032,7 +1076,12 @@ babel_read_seqno_request(struct babel_tlv *hdr, union babel_msg *m, case BABEL_AE_WILDCARD: return PARSE_ERROR; + /* + * RFC 9229 2.3 - When receiving requests, AE 1 (IPv4) and AE 4 + * (IPv4-via-IPv6) MUST be treated in the same manner. + */ case BABEL_AE_IP4: + case BABEL_AE_IP4_VIA_IP6: if (tlv->plen > IP4_MAX_PREFIX_LENGTH) return PARSE_ERROR; |