Skip to main content

zebra_network/protocol/external/addr/
canonical.rs

1//! Zebra's canonical node address format.
2//!
3//! Zebra canonicalises all received addresses into Rust [`PeerSocketAddr`]s.
4//! If the address is an [IPv4-mapped IPv6 address], it becomes a [`SocketAddr::V4`]
5//!
6//! [IPv4-mapped IPv6 address]: https://en.wikipedia.org/wiki/IPv6#IPv4-mapped_IPv6_addresses
7
8use std::net::{IpAddr, Ipv6Addr, SocketAddr};
9
10use crate::PeerSocketAddr;
11
12/// Transform a Zcash-deserialized [`Ipv6Addr`] into a canonical Zebra [`IpAddr`].
13///
14/// Zcash uses IPv6-mapped IPv4 addresses in its `addr` (v1) network messages.
15/// Zebra converts those addresses to `Ipv4Addr`s, for maximum compatibility
16/// with systems that don't understand IPv6.
17///
18/// Zebra also uses this canonical format for addresses from other sources.
19pub(in super::super) fn canonical_ip_addr(v6_addr: &Ipv6Addr) -> IpAddr {
20    use IpAddr::*;
21
22    // if it is an IPv4-mapped address, convert to V4, otherwise leave it as V6
23    v6_addr
24        .to_ipv4_mapped()
25        .map(V4)
26        .unwrap_or_else(|| V6(*v6_addr))
27}
28
29/// Transform a [`SocketAddr`] into a canonical Zebra [`SocketAddr`], converting
30/// IPv6-mapped IPv4 addresses, and removing IPv6 scope IDs and flow information.
31///
32/// Use [`canonical_peer_addr()`] and [`PeerSocketAddr`] for remote peer addresses,
33/// so that Zebra doesn't log sensitive information about peers.
34///
35/// See [`canonical_ip_addr`] for detailed info on IPv6-mapped IPv4 addresses.
36pub fn canonical_socket_addr(socket_addr: impl Into<SocketAddr>) -> SocketAddr {
37    use SocketAddr::*;
38
39    let mut socket_addr = socket_addr.into();
40    if let V6(v6_socket_addr) = socket_addr {
41        let canonical_ip = canonical_ip_addr(v6_socket_addr.ip());
42        // creating a new SocketAddr removes scope IDs and flow information
43        socket_addr = SocketAddr::new(canonical_ip, socket_addr.port());
44    }
45
46    socket_addr
47}
48
49/// Transform a [`PeerSocketAddr`] into a canonical Zebra [`PeerSocketAddr`], converting
50/// IPv6-mapped IPv4 addresses, and removing IPv6 scope IDs and flow information.
51///
52/// See [`canonical_ip_addr`] for detailed info on IPv6-mapped IPv4 addresses.
53pub fn canonical_peer_addr(peer_socket_addr: impl Into<PeerSocketAddr>) -> PeerSocketAddr {
54    let peer_socket_addr = peer_socket_addr.into();
55
56    canonical_socket_addr(*peer_socket_addr).into()
57}
58
59/// Returns the peer group key for an IP address: the set of addresses Zebra
60/// treats as one entity.
61///
62/// Used to enforce [`Config::max_connections_per_ip`](crate::config::Config),
63/// and to apply misbehavior bans.
64///
65/// - IPv4: the address itself. Each address costs an attacker a separate IPv4
66///   allocation, so it already bounds a single host's share.
67/// - IPv6: the `/64` prefix — the lower 64 bits are zeroed. A standard IPv6
68///   `/64` allocation holds 2⁶⁴ addresses that can all belong to one host,
69///   so exact-address comparison would not bound a single host's share.
70/// - IPv4-mapped IPv6 addresses map to their plain IPv4 address, so both
71///   spellings share one key.
72///
73/// The port is always dropped, so a peer cannot get a fresh key by
74/// reconnecting from a different source port.
75///
76/// # Security
77///
78/// Connection limits and bans are only as strong as this grouping. Every
79/// limit and ban check must key peers through this function.
80pub(crate) fn connection_limit_key(ip: IpAddr) -> IpAddr {
81    // Unmap IPv4-mapped IPv6 first, so that both spellings of an IPv4 host
82    // share one key.
83    let ip = match ip {
84        IpAddr::V6(v6) => canonical_ip_addr(&v6),
85        ipv4 @ IpAddr::V4(_) => ipv4,
86    };
87
88    match ip {
89        // IPv4 addresses are scarce enough to be their own key.
90        IpAddr::V4(_) => ip,
91        IpAddr::V6(v6) => {
92            let o = v6.octets();
93            let mut masked = [0u8; 16];
94            masked[..8].copy_from_slice(&o[..8]);
95            IpAddr::V6(Ipv6Addr::from(masked))
96        }
97    }
98}