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;
27const CHECK_TIMEOUT_INTERVAL: u64 = 1;
29const DEFAULT_TIMEOUT: u64 = 8;
30const MAX_ADDRS: usize = 10;
31
32#[allow(dead_code)]
34#[derive(Clone, Debug)]
35pub enum Misbehavior {
36 DuplicateReceived,
38 Timeout,
40 InvalidData,
42 TooManyAddresses(usize),
44}
45
46pub enum MisbehaveResult {
48 Continue,
50 Disconnect,
52}
53
54impl MisbehaveResult {
55 pub fn is_disconnect(&self) -> bool {
56 matches!(self, MisbehaveResult::Disconnect)
57 }
58}
59
60#[async_trait]
62pub trait Callback: Clone + Send {
63 fn register(&self, context: &ProtocolContextMutRef, version: &str);
65 fn unregister(&self, context: &ProtocolContextMutRef);
67 async fn received_identify(
69 &mut self,
70 context: &mut ProtocolContextMutRef<'_>,
71 identify: &[u8],
72 ) -> MisbehaveResult;
73 fn identify(&mut self) -> &[u8];
75 fn local_listen_addrs(&mut self) -> Vec<Multiaddr>;
77 fn add_remote_listen_addrs(&mut self, session: &SessionContext, addrs: Vec<Multiaddr>);
79 fn add_observed_addr(&mut self, addr: Multiaddr, session_id: SessionId) -> MisbehaveResult;
81 fn misbehave(&mut self, session: &SessionContext, kind: Misbehavior) -> MisbehaveResult;
83}
84
85pub 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 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 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 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 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 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 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 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 #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
603 pub struct Flags: u64 {
604 const COMPATIBILITY = 0b1;
606 const DISCOVERY = 0b10;
608 const SYNC = 0b100;
610 const RELAY = 0b1000;
612 const LIGHT_CLIENT = 0b10000;
614 const BLOCK_FILTER = 0b100000;
616 }
617}