ckb_network/protocols/identify/
mod.rs

1use std::borrow::Cow;
2use std::collections::HashMap;
3use std::sync::{Arc, atomic::Ordering};
4
5use ckb_logger::{debug, error, trace, warn};
6use ckb_systemtime::{Duration, Instant};
7use p2p::{
8    SessionId, async_trait,
9    bytes::Bytes,
10    context::{ProtocolContext, ProtocolContextMutRef, SessionContext},
11    multiaddr::{Multiaddr, Protocol},
12    service::TargetProtocol,
13    traits::ServiceProtocol,
14    utils::{extract_peer_id, is_reachable, multiaddr_to_socketaddr},
15};
16
17mod protocol;
18
19use crate::{NetworkState, PeerIdentifyInfo, SupportProtocols, peer_store::required_flags_filter};
20use ckb_types::{packed, prelude::*};
21
22use protocol::IdentifyMessage;
23
24const MAX_RETURN_LISTEN_ADDRS: usize = 10;
25const BAN_ON_NOT_SAME_NET: Duration = Duration::from_secs(5 * 60);
26const CHECK_TIMEOUT_TOKEN: u64 = 100;
27// Check timeout interval (seconds)
28const CHECK_TIMEOUT_INTERVAL: u64 = 1;
29const DEFAULT_TIMEOUT: u64 = 8;
30const MAX_ADDRS: usize = 10;
31
32/// The misbehavior to report to underlying peer storage
33#[allow(dead_code)]
34#[derive(Clone, Debug)]
35pub enum Misbehavior {
36    /// Repeat received message
37    DuplicateReceived,
38    /// Timeout reached
39    Timeout,
40    /// Remote peer send invalid data
41    InvalidData,
42    /// Send too many addresses in listen addresses
43    TooManyAddresses(usize),
44}
45
46/// Misbehavior report result
47pub enum MisbehaveResult {
48    /// Continue to run
49    Continue,
50    /// Disconnect this peer
51    Disconnect,
52}
53
54impl MisbehaveResult {
55    pub fn is_disconnect(&self) -> bool {
56        matches!(self, MisbehaveResult::Disconnect)
57    }
58}
59
60/// The trait to communicate with underlying peer storage
61#[async_trait]
62pub trait Callback: Clone + Send {
63    // Register open protocol
64    fn register(&self, context: &ProtocolContextMutRef, version: &str);
65    // remove registered identify protocol
66    fn unregister(&self, context: &ProtocolContextMutRef);
67    /// Received custom message
68    async fn received_identify(
69        &mut self,
70        context: &mut ProtocolContextMutRef<'_>,
71        identify: &[u8],
72    ) -> MisbehaveResult;
73    /// Get custom identify message
74    fn identify(&mut self) -> &[u8];
75    /// Get local listen addresses
76    fn local_listen_addrs(&mut self) -> Vec<Multiaddr>;
77    /// Add remote peer's listen addresses
78    fn add_remote_listen_addrs(&mut self, session: &SessionContext, addrs: Vec<Multiaddr>);
79    /// Add our address observed by remote peer
80    fn add_observed_addr(&mut self, addr: Multiaddr, session_id: SessionId) -> MisbehaveResult;
81    /// Report misbehavior
82    fn misbehave(&mut self, session: &SessionContext, kind: Misbehavior) -> MisbehaveResult;
83}
84
85/// Identify protocol
86pub struct IdentifyProtocol<T> {
87    callback: T,
88    remote_infos: HashMap<SessionId, RemoteInfo>,
89    global_ip_only: bool,
90}
91
92impl<T: Callback> IdentifyProtocol<T> {
93    pub fn new(callback: T) -> IdentifyProtocol<T> {
94        IdentifyProtocol {
95            callback,
96            remote_infos: HashMap::default(),
97            global_ip_only: true,
98        }
99    }
100
101    #[cfg(test)]
102    pub fn global_ip_only(mut self, only: bool) -> Self {
103        self.global_ip_only = only;
104        self
105    }
106
107    fn check_duplicate(&mut self, context: &mut ProtocolContextMutRef) -> MisbehaveResult {
108        let session = context.session;
109        let info = self
110            .remote_infos
111            .get_mut(&session.id)
112            .expect("RemoteInfo must exists");
113
114        if info.has_received {
115            self.callback
116                .misbehave(&info.session, Misbehavior::DuplicateReceived)
117        } else {
118            info.has_received = true;
119            MisbehaveResult::Continue
120        }
121    }
122
123    fn process_listens(
124        &mut self,
125        context: &mut ProtocolContextMutRef,
126        listens: Vec<Multiaddr>,
127    ) -> MisbehaveResult {
128        let session = context.session;
129        let info = self
130            .remote_infos
131            .get_mut(&session.id)
132            .expect("RemoteInfo must exists");
133
134        if listens.len() > MAX_ADDRS {
135            self.callback
136                .misbehave(&info.session, Misbehavior::TooManyAddresses(listens.len()))
137        } else {
138            let global_ip_only = self.global_ip_only;
139            let reachable_addrs = listens
140                .into_iter()
141                .filter(|addr| match multiaddr_to_socketaddr(addr) {
142                    Some(socket_addr) => !global_ip_only || is_reachable(socket_addr.ip()),
143                    None => true,
144                })
145                .collect::<Vec<_>>();
146            self.callback
147                .add_remote_listen_addrs(session, reachable_addrs);
148            MisbehaveResult::Continue
149        }
150    }
151
152    fn process_observed(
153        &mut self,
154        context: &mut ProtocolContextMutRef,
155        observed: Multiaddr,
156    ) -> MisbehaveResult {
157        debug!(
158            "IdentifyProtocol process observed address, session: {:?}, observed: {}",
159            context.session, observed,
160        );
161
162        let session = context.session;
163        let info = self
164            .remote_infos
165            .get_mut(&session.id)
166            .expect("RemoteInfo must exists");
167        self.callback.add_observed_addr(observed, info.session.id);
168        MisbehaveResult::Continue
169    }
170}
171
172pub(crate) struct RemoteInfo {
173    session: SessionContext,
174    connected_at: Instant,
175    timeout: Duration,
176    has_received: bool,
177}
178
179impl RemoteInfo {
180    fn new(session: SessionContext, timeout: Duration) -> RemoteInfo {
181        RemoteInfo {
182            session,
183            connected_at: Instant::now(),
184            timeout,
185            has_received: false,
186        }
187    }
188}
189
190#[async_trait]
191impl<T: Callback> ServiceProtocol for IdentifyProtocol<T> {
192    async fn init(&mut self, context: &mut ProtocolContext) {
193        let proto_id = context.proto_id;
194        if let Err(err) = context
195            .set_service_notify(
196                proto_id,
197                Duration::from_secs(CHECK_TIMEOUT_INTERVAL),
198                CHECK_TIMEOUT_TOKEN,
199            )
200            .await
201        {
202            error!("IdentifyProtocol init error: {:?}", err)
203        }
204    }
205
206    async fn connected(&mut self, context: ProtocolContextMutRef<'_>, version: &str) {
207        let session = context.session;
208        debug!("IdentifyProtocol connected, session: {:?}", session);
209
210        self.callback.register(&context, version);
211
212        let remote_info = RemoteInfo::new(session.clone(), Duration::from_secs(DEFAULT_TIMEOUT));
213        self.remote_infos.insert(session.id, remote_info);
214
215        let listen_addrs: Vec<Multiaddr> = self
216            .callback
217            .local_listen_addrs()
218            .iter()
219            .filter(|addr| {
220                multiaddr_to_socketaddr(addr)
221                    .map(|socket_addr| !self.global_ip_only || is_reachable(socket_addr.ip()))
222                    .unwrap_or(false)
223            })
224            .take(MAX_ADDRS)
225            .cloned()
226            .collect();
227
228        let identify = self.callback.identify();
229        let data = IdentifyMessage::new(listen_addrs, session.address.clone(), identify).encode();
230        let _ = context
231            .quick_send_message(data)
232            .await
233            .map_err(|err| error!("IdentifyProtocol quick_send_message, error: {:?}", err));
234    }
235
236    async fn disconnected(&mut self, context: ProtocolContextMutRef<'_>) {
237        self.remote_infos
238            .remove(&context.session.id)
239            .expect("RemoteInfo must exists");
240        debug!(
241            "IdentifyProtocol disconnected, session: {:?}",
242            context.session
243        );
244        self.callback.unregister(&context);
245    }
246
247    async fn received(&mut self, mut context: ProtocolContextMutRef<'_>, data: Bytes) {
248        let session = context.session;
249        match IdentifyMessage::decode(&data) {
250            Some(message) => {
251                trace!(
252                    "IdentifyProtocol received, session: {:?}, listen_addrs: {:?}, observed_addr: {}",
253                    context.session, message.listen_addrs, message.observed_addr
254                );
255
256                // Interrupt processing if error, avoid pollution
257                if let MisbehaveResult::Disconnect = self.check_duplicate(&mut context) {
258                    error!(
259                        "Disconnect IdentifyProtocol session {:?} due to duplication.",
260                        session
261                    );
262                    let _ = context.disconnect(session.id).await;
263                    return;
264                }
265                if let MisbehaveResult::Disconnect = self
266                    .callback
267                    .received_identify(&mut context, message.identify)
268                    .await
269                {
270                    error!(
271                        "Disconnect IdentifyProtocol session {:?} due to invalid identify message.",
272                        session,
273                    );
274                    let _ = context.disconnect(session.id).await;
275                    return;
276                }
277                if let MisbehaveResult::Disconnect =
278                    self.process_listens(&mut context, message.listen_addrs.clone())
279                {
280                    error!(
281                        "Disconnect IdentifyProtocol session {:?} due to invalid listen addrs: {:?}.",
282                        session, message.listen_addrs,
283                    );
284                    let _ = context.disconnect(session.id).await;
285                    return;
286                }
287                if let MisbehaveResult::Disconnect =
288                    self.process_observed(&mut context, message.observed_addr.clone())
289                {
290                    error!(
291                        "Disconnect IdentifyProtocol session {:?} due to invalid observed addr: {}.",
292                        session, message.observed_addr,
293                    );
294                    let _ = context.disconnect(session.id).await;
295                }
296            }
297            None => {
298                let info = self
299                    .remote_infos
300                    .get(&session.id)
301                    .expect("RemoteInfo must exists");
302                if self
303                    .callback
304                    .misbehave(&info.session, Misbehavior::InvalidData)
305                    .is_disconnect()
306                {
307                    let _ = context.disconnect(session.id).await;
308                }
309            }
310        }
311    }
312
313    async fn notify(&mut self, context: &mut ProtocolContext, _token: u64) {
314        for (session_id, info) in &self.remote_infos {
315            if !info.has_received && (info.connected_at + info.timeout) <= Instant::now() {
316                let misbehave_result = self.callback.misbehave(&info.session, Misbehavior::Timeout);
317                if misbehave_result.is_disconnect() {
318                    let _ = context.disconnect(*session_id).await;
319                }
320            }
321        }
322    }
323}
324
325#[derive(Clone)]
326pub struct IdentifyCallback {
327    network_state: Arc<NetworkState>,
328    identify: Identify,
329}
330
331impl IdentifyCallback {
332    pub(crate) fn new(
333        network_state: Arc<NetworkState>,
334        name: String,
335        client_version: String,
336        flags: Flags,
337    ) -> IdentifyCallback {
338        IdentifyCallback {
339            network_state,
340            identify: Identify::new(name, flags, client_version),
341        }
342    }
343
344    fn listen_addrs(&self) -> Vec<Multiaddr> {
345        let addrs = self.network_state.public_addrs(MAX_RETURN_LISTEN_ADDRS * 2);
346        addrs
347            .into_iter()
348            .take(MAX_RETURN_LISTEN_ADDRS)
349            .collect::<Vec<_>>()
350    }
351}
352
353#[async_trait]
354impl Callback for IdentifyCallback {
355    fn register(&self, context: &ProtocolContextMutRef, version: &str) {
356        self.network_state.with_peer_registry_mut(|reg| {
357            reg.get_peer_mut(context.session.id).map(|peer| {
358                peer.protocols.insert(context.proto_id, version.to_owned());
359            })
360        });
361    }
362
363    fn unregister(&self, context: &ProtocolContextMutRef) {
364        let protocol_version_match = self
365            .network_state
366            .with_peer_registry(|reg| {
367                reg.get_peer(context.session.id)
368                    .map(|p| p.protocol_version(context.proto_id))
369            })
370            .flatten()
371            .map(|version| version != "3")
372            .unwrap_or_default();
373
374        if self.network_state.ckb2023.load(Ordering::SeqCst) && protocol_version_match {
375        } else if context.session.ty.is_outbound() {
376            // Due to the filtering strategy of the peer store, if the node is
377            // disconnected after a long connection is maintained for more than seven days,
378            // it is possible that the node will be accidentally evicted, so it is necessary
379            // to reset the last_connected_time of the node when disconnected.
380            self.network_state.with_peer_store_mut(|peer_store| {
381                peer_store.update_outbound_addr_last_connected_ms(context.session.address.clone());
382            });
383        }
384    }
385
386    fn identify(&mut self) -> &[u8] {
387        self.identify.encode()
388    }
389
390    async fn received_identify(
391        &mut self,
392        context: &mut ProtocolContextMutRef<'_>,
393        identify: &[u8],
394    ) -> MisbehaveResult {
395        match self.identify.verify(identify) {
396            None => {
397                self.network_state.ban_session(
398                    &context.control().clone().into(),
399                    context.session.id,
400                    BAN_ON_NOT_SAME_NET,
401                    "The nodes are not on the same network".to_string(),
402                );
403                MisbehaveResult::Disconnect
404            }
405            Some((flags, client_version)) => {
406                let registry_client_version = |version: String| {
407                    self.network_state.with_peer_registry_mut(|registry| {
408                        if let Some(peer) = registry.get_peer_mut(context.session.id) {
409                            peer.identify_info = Some(PeerIdentifyInfo {
410                                client_version: version,
411                                flags,
412                            })
413                        }
414                    });
415                };
416
417                registry_client_version(client_version);
418
419                let required_flags = self.network_state.required_flags;
420
421                let protocol_version_match = self
422                    .network_state
423                    .with_peer_registry(|reg| {
424                        reg.get_peer(context.session.id)
425                            .map(|p| p.protocol_version(context.proto_id))
426                    })
427                    .flatten()
428                    .map(|version| version != "3")
429                    .unwrap_or_default();
430                let ckb2023 = self.network_state.ckb2023.load(Ordering::SeqCst);
431
432                let renew = if ckb2023 && protocol_version_match {
433                    if context.session.ty.is_outbound() {
434                        self.network_state
435                            .peer_store
436                            .lock()
437                            .mut_addr_manager()
438                            .remove(&context.session.address);
439                    }
440                    false
441                } else {
442                    true
443                };
444
445                if context.session.ty.is_outbound() {
446                    // why don't set inbound here?
447                    // because inbound address can't feeler during staying connected
448                    // and if set it to peer store, it will be broadcast to the entire network,
449                    // but this is an unverified address
450                    if renew {
451                        self.network_state.with_peer_store_mut(|peer_store| {
452                            peer_store.add_outbound_addr(context.session.address.clone(), flags);
453                        });
454                    }
455
456                    if self.network_state.with_peer_registry_mut(|reg| {
457                        reg.change_feeler_flags(&context.session.address, flags)
458                    }) {
459                        let _ = context
460                            .open_protocols(
461                                context.session.id,
462                                TargetProtocol::Single(SupportProtocols::Feeler.protocol_id()),
463                            )
464                            .await;
465                    } else if required_flags_filter(required_flags, flags) {
466                        // The remote end can support all local protocols.
467                        let _ = context
468                            .open_protocols(
469                                context.session.id,
470                                TargetProtocol::Filter(Box::new(move |id| {
471                                    if ckb2023 {
472                                        id != &SupportProtocols::Feeler.protocol_id()
473                                            && id != &SupportProtocols::RelayV2.protocol_id()
474                                    } else {
475                                        id != &SupportProtocols::Feeler.protocol_id()
476                                    }
477                                })),
478                            )
479                            .await;
480                    } else {
481                        // The remote end cannot support all local protocols.
482                        warn!(
483                            "Session closed from IdentifyProtocol due to peer's flag not meeting the requirements"
484                        );
485                        return MisbehaveResult::Disconnect;
486                    }
487                }
488                MisbehaveResult::Continue
489            }
490        }
491    }
492
493    /// Get local listen addresses
494    fn local_listen_addrs(&mut self) -> Vec<Multiaddr> {
495        let mut listens = self.listen_addrs();
496
497        if listens.len() < MAX_RETURN_LISTEN_ADDRS {
498            let observe_addrs = self
499                .network_state
500                .observed_addrs(MAX_RETURN_LISTEN_ADDRS - listens.len());
501            listens.extend(observe_addrs);
502            listens
503        } else {
504            listens
505        }
506    }
507
508    fn add_remote_listen_addrs(&mut self, session: &SessionContext, addrs: Vec<Multiaddr>) {
509        trace!(
510            "IdentifyProtocol add remote listening addresses, session: {:?}, addresses : {:?}",
511            session, addrs,
512        );
513        let flags = self.network_state.with_peer_registry_mut(|reg| {
514            if let Some(peer) = reg.get_peer_mut(session.id) {
515                peer.listened_addrs = addrs.clone();
516                peer.identify_info
517                    .as_ref()
518                    .map(|a| a.flags)
519                    .unwrap_or(Flags::COMPATIBILITY)
520            } else {
521                Flags::COMPATIBILITY
522            }
523        });
524        self.network_state.with_peer_store_mut(|peer_store| {
525            for addr in addrs {
526                if let Err(err) = peer_store.add_addr(addr.clone(), flags) {
527                    error!("IdentifyProtocol failed to add address to peer store, address: {}, error: {:?}", addr, err);
528                }
529            }
530        })
531    }
532
533    fn add_observed_addr(&mut self, mut addr: Multiaddr, session_id: SessionId) -> MisbehaveResult {
534        if extract_peer_id(&addr).is_none() {
535            addr.push(Protocol::P2P(Cow::Borrowed(
536                self.network_state.local_peer_id().as_bytes(),
537            )))
538        }
539
540        self.network_state.add_observed_addr(session_id, addr);
541        // NOTE: for future usage
542        MisbehaveResult::Continue
543    }
544
545    fn misbehave(&mut self, session: &SessionContext, reason: Misbehavior) -> MisbehaveResult {
546        error!(
547            "IdentifyProtocol detects abnormal behavior, session: {:?}, reason: {:?}",
548            session, reason
549        );
550        MisbehaveResult::Disconnect
551    }
552}
553
554#[derive(Clone)]
555struct Identify {
556    name: String,
557    encode_data: ckb_types::bytes::Bytes,
558}
559
560impl Identify {
561    fn new(name: String, flags: Flags, client_version: String) -> Self {
562        Identify {
563            encode_data: packed::Identify::new_builder()
564                .name(name.as_str().pack())
565                .flag(flags.bits().pack())
566                .client_version(client_version.as_str().pack())
567                .build()
568                .as_bytes(),
569            name,
570        }
571    }
572
573    fn encode(&mut self) -> &[u8] {
574        &self.encode_data
575    }
576
577    fn verify(&self, data: &[u8]) -> Option<(Flags, String)> {
578        let reader = packed::IdentifyReader::from_slice(data).ok()?;
579
580        let name = reader.name().as_utf8().ok()?.to_owned();
581        if self.name != name {
582            warn!(
583                "IdentifyProtocol detects peer has different network identifiers, local network id: {}, remote network id: {}",
584                self.name, name,
585            );
586            return None;
587        }
588
589        let flag: u64 = reader.flag().unpack();
590        if flag == 0 {
591            return None;
592        }
593
594        let raw_client_version = reader.client_version().as_utf8().ok()?.to_owned();
595
596        Some((Flags::from_bits_truncate(flag), raw_client_version))
597    }
598}
599
600bitflags::bitflags! {
601    /// Node Function Identification
602    #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
603    pub struct Flags: u64 {
604        /// Compatibility reserved
605        const COMPATIBILITY = 0b1;
606        /// Discovery protocol, which can provide peers data service
607        const DISCOVERY = 0b10;
608        /// Sync protocol can provide Block and Header download service
609        const SYNC = 0b100;
610        /// Relay protocol, which can provide CompactBlock and Transaction broadcast/forwarding services
611        const RELAY = 0b1000;
612        /// Light client protocol, which can provide Block / Transaction data and existence-proof services
613        const LIGHT_CLIENT = 0b10000;
614        /// Client-side block filter protocol can provide BlockFilter download service
615        const BLOCK_FILTER = 0b100000;
616    }
617}