Skip to content

Commit

Permalink
refactor(node): add reject_sybil_outbounds_range_limit
Browse files Browse the repository at this point in the history
  • Loading branch information
lrubiorod authored and clbartoli committed Feb 25, 2021
1 parent 2d73670 commit 50820dd
Show file tree
Hide file tree
Showing 8 changed files with 75 additions and 25 deletions.
19 changes: 15 additions & 4 deletions config/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -236,10 +236,14 @@ pub struct Connections {
))]
pub bucketing_ice_period: Duration,

/// Reject (tarpit) inbound connections coming from addresses in the same /14 IP range, so as
/// to prevent sybil peers from monopolizing our inbound capacity (128 by default).
/// Reject (tarpit) inbound connections coming from addresses that are alike, so as
/// to prevent sybil peers from monopolizing our inbound capacity.
pub reject_sybil_inbounds: bool,

/// Limit to reject (tarpit) inbound connections. If the limit is set to 18, the addresses having
/// the same first 18 bits in the IP will collide, so as to prevent sybil peers from monopolizing our inbound capacity.
pub reject_sybil_inbounds_range_limit: u8,

/// Limit the number of requested blocks that will be processed as one batch
pub requested_blocks_batch_limit: u32,
}
Expand Down Expand Up @@ -643,6 +647,10 @@ impl Connections {
.reject_sybil_inbounds
.to_owned()
.unwrap_or_else(|| defaults.connections_reject_sybil_inbounds()),
reject_sybil_inbounds_range_limit: config
.reject_sybil_inbounds_range_limit
.to_owned()
.unwrap_or_else(|| defaults.connections_reject_sybil_inbounds_range_limit()),
requested_blocks_batch_limit: config
.requested_blocks_batch_limit
.to_owned()
Expand All @@ -669,6 +677,7 @@ impl Connections {
bucketing_ice_period: Some(self.bucketing_ice_period),
bucketing_update_period: Some(self.bucketing_update_period),
reject_sybil_inbounds: Some(self.reject_sybil_inbounds),
reject_sybil_inbounds_range_limit: Some(self.reject_sybil_inbounds_range_limit),
requested_blocks_batch_limit: Some(self.requested_blocks_batch_limit),
}
}
Expand Down Expand Up @@ -1223,7 +1232,8 @@ mod tests {
consensus_c: Some(51),
bucketing_ice_period: Some(Duration::from_secs(13200)),
bucketing_update_period: Some(200),
reject_sybil_inbounds: Some(false),
reject_sybil_inbounds: Some(true),
reject_sybil_inbounds_range_limit: Some(14),
requested_blocks_batch_limit: Some(99),
};
let config = Connections::from_partial(&partial_config, &Testnet);
Expand All @@ -1244,7 +1254,8 @@ mod tests {
assert_eq!(config.consensus_c, 51);
assert_eq!(config.bucketing_ice_period, Duration::from_secs(13200));
assert_eq!(config.bucketing_update_period, 200);
assert_eq!(config.reject_sybil_inbounds, false);
assert_eq!(config.reject_sybil_inbounds, true);
assert_eq!(config.reject_sybil_inbounds_range_limit, 14);
assert_eq!(config.requested_blocks_batch_limit, 99);
}

Expand Down
14 changes: 12 additions & 2 deletions config/src/defaults.rs
Original file line number Diff line number Diff line change
Expand Up @@ -92,12 +92,18 @@ pub trait Defaults {
300
}

/// Reject (tarpit) inbound connections coming from addresses in the same /14 IP range, so as
/// to prevent sybil peers from monopolizing our inbound capacity (128 by default).
/// Reject (tarpit) inbound connections coming from addresses that are alike, so as
/// to prevent sybil peers from monopolizing our inbound capacity.
fn connections_reject_sybil_inbounds(&self) -> bool {
true
}

/// Limit to reject (tarpit) inbound connections. If the limit is set to 18, the addresses having
/// the same first 18 bits in the IP will collide, so as to prevent sybil peers from monopolizing our inbound capacity.
fn connections_reject_sybil_inbounds_range_limit(&self) -> u8 {
18
}

/// Limit the number of requested blocks that will be processed as one batch
fn connections_requested_blocks_batch_limit(&self) -> u32 {
// Default: 500 blocks
Expand Down Expand Up @@ -443,6 +449,10 @@ impl Defaults for Development {
fn connections_reject_sybil_inbounds(&self) -> bool {
false
}

fn connections_reject_sybil_inbounds_range_limit(&self) -> u8 {
0
}
}

impl Defaults for Mainnet {
Expand Down
5 changes: 5 additions & 0 deletions node/src/actors/sessions_manager/actor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,11 @@ impl Actor for SessionsManager {
config.connections.outbound_limit,
);

// Set reject_sybil_outbounds range limit
act.sessions
.inbound_network_ranges
.set_range_limit(config.connections.reject_sybil_inbounds_range_limit);

// Initialized epoch from config
let mut checkpoints_period = config.consensus_constants.checkpoints_period;
let checkpoint_zero_timestamp =
Expand Down
2 changes: 1 addition & 1 deletion node/src/actors/sessions_manager/handlers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ impl Handler<Create> for SessionsManager {
// condition
if config.connections.reject_sybil_inbounds && msg.session_type == SessionType::Inbound {
if let Some(range) = self.sessions.is_similar_to_inbound_session(&remote_addr) {
log::trace!("Refusing to accept {} as inbound peer because there is already an inbound session with another peer in IP range {}", remote_addr, ip_range_string(range));
log::trace!("Refusing to accept {} as inbound peer because there is already an inbound session with another peer in IP range {}", remote_addr, ip_range_string(range, config.connections.reject_sybil_inbounds_range_limit));
return;
}
};
Expand Down
34 changes: 25 additions & 9 deletions p2p/src/peers/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -495,19 +495,20 @@ pub fn get_range_address(socket_addr: &SocketAddr, range: u8) -> [u8; 4] {
match socket_addr {
SocketAddr::V4(addr) => {
let ip = addr.ip().octets();
if range == 0 {
[0, 0, 0, 0]
} else {
let range = 32u8.saturating_sub(range);

let ip_bytes = u32::from(ip[0]) << 24
| (u32::from(ip[1])) << 16
| (u32::from(ip[2])) << 8
| (u32::from(ip[3]));
let ip_bytes = u32::from_be_bytes(ip);
let ip_bytes = ip_bytes >> range;
let ip_bytes = ip_bytes << range;

let ip_bytes = ip_bytes >> range;
let ip_bytes = ip_bytes << range;

ip_bytes.to_be_bytes()
ip_bytes.to_be_bytes()
}
}
SocketAddr::V6(_addr) => {
// TODO: This address type is not currently in use
// FIXME(#740)
[0, 0, 0, 0]
}
}
Expand Down Expand Up @@ -570,3 +571,18 @@ pub fn calculate_index_for_new(sk: u64, src_group: &[u8], group: &[u8], host_id:

(bucket * 64) + slot
}

#[test]
fn test_get_range_address() {
let address = "255.255.255.255:8002".parse().unwrap();

assert_eq!(get_range_address(&address, 32), [255, 255, 255, 255]);
assert_eq!(get_range_address(&address, 28), [255, 255, 255, 240]);
assert_eq!(get_range_address(&address, 24), [255, 255, 255, 0]);
assert_eq!(get_range_address(&address, 20), [255, 255, 240, 0]);
assert_eq!(get_range_address(&address, 16), [255, 255, 0, 0]);
assert_eq!(get_range_address(&address, 12), [255, 240, 0, 0]);
assert_eq!(get_range_address(&address, 8), [255, 0, 0, 0]);
assert_eq!(get_range_address(&address, 4), [240, 0, 0, 0]);
assert_eq!(get_range_address(&address, 0), [0, 0, 0, 0]);
}
19 changes: 13 additions & 6 deletions p2p/src/sessions/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -397,12 +397,13 @@ pub struct GetConsolidatedPeersResult {
#[derive(Default)]
pub struct NetworkRangesCollection {
inner: HashSet<[u8; 4]>,
range_limit: u8,
}

impl NetworkRangesCollection {
/// Checks whether a range is present in the collection as derived from a socket address.
pub fn contains_address(&self, address: &SocketAddr) -> Option<&[u8; 4]> {
let range_vec = get_range_address(address, 14);
let range_vec = get_range_address(address, self.range_limit);
let mut range = [0, 0, 0, 0];
range[..4].copy_from_slice(&range_vec);

Expand All @@ -416,7 +417,7 @@ impl NetworkRangesCollection {

/// Insert a range into the collection as derived from a socket address.
pub fn insert_address(&mut self, address: &SocketAddr) -> bool {
let range_vec = get_range_address(address, 14);
let range_vec = get_range_address(address, self.range_limit);
let mut range = [0, 0, 0, 0];
range[..4].copy_from_slice(&range_vec);

Expand All @@ -430,7 +431,7 @@ impl NetworkRangesCollection {

/// Remove a range from the collection as derived from a socket address.
pub fn remove_address(&mut self, address: &SocketAddr) -> bool {
let range_vec = get_range_address(address, 14);
let range_vec = get_range_address(address, self.range_limit);
let mut range = [0, 0, 0, 0];
range[..4].copy_from_slice(&range_vec);

Expand All @@ -441,14 +442,20 @@ impl NetworkRangesCollection {
pub fn remove_range(&mut self, range: [u8; 4]) -> bool {
self.inner.remove(&range)
}

/// Set range limit
pub fn set_range_limit(&mut self, range_limit: u8) {
self.range_limit = range_limit;
}
}

/// Compose a string for representing an IPV4 range
pub fn ip_range_string(range: &[u8]) -> String {
pub fn ip_range_string(range: &[u8], range_limit: u8) -> String {
format!(
"{}0.0/16",
"{}/{}",
range.iter().fold(String::new(), |acc, &octet| acc
+ octet.to_string().as_str()
+ ".")
+ "."),
range_limit,
)
}
3 changes: 2 additions & 1 deletion p2p/tests/sessions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -554,11 +554,12 @@ fn p2p_sessions_register_more_than_limit() {
}

#[test]
/// Check that peer addresses are considered "similar" if the first three octets of the IP are
/// Check that peer addresses are considered "similar" if the first 14 bits of the IP are
/// matching.
fn p2p_peer_address_is_similar_to_inbound_session() {
// Create sessions struct
let mut sessions = Sessions::<String>::default();
sessions.inbound_network_ranges.set_range_limit(18);

let inbound_address_1 = "127.0.0.1:8002".parse().unwrap();
let inbound_address_2 = "127.0.0.1:8003".parse().unwrap();
Expand Down
4 changes: 2 additions & 2 deletions witnet.toml
Original file line number Diff line number Diff line change
Expand Up @@ -114,8 +114,8 @@ known_peers = [
outbound_limit = 8
# Period for opening new peer connections while the current number of peers is lower than `outbound_limit`.
bootstrap_peers_period_seconds = 1
# Reject (tarpit) inbound connections coming from addresses in the same /14 IP range, so as to prevent sybil peers from
# monopolizing our inbound capacity (128 by default).
# Reject (tarpit) inbound connections coming from addresses that are alike (i.e. by default having the first 18 bits equal),
# so as to prevent sybil peers from monopolizing our inbound capacity.
reject_sybil_inbounds = true

[storage]
Expand Down

0 comments on commit 50820dd

Please sign in to comment.