Commit 583163a9 authored by Julien's avatar Julien

P2p: Nack with motive

Nack send the motive of connection rejection.
parent b116d790
......@@ -64,14 +64,15 @@ let announced
p2p_version = greatest p2p_versions ;
}
let may_select_version accepted_versions remote_version =
let may_select_version accepted_versions remote_version motive =
let open Error_monad in
let best_local_version = greatest accepted_versions in
if best_local_version <= remote_version then
Some best_local_version
ok best_local_version
else if List.mem remote_version accepted_versions then
Some remote_version
ok remote_version
else
None
P2p_rejection.rejecting motive
let select
~chain_name
......@@ -81,15 +82,17 @@ let select
assert (distributed_db_versions <> []) ;
assert (p2p_versions <> []) ;
if chain_name <> remote.chain_name then
None
P2p_rejection.rejecting Unknown_chain_name
else
let open Option in
let open Error_monad in
may_select_version
distributed_db_versions
remote.distributed_db_version >>= fun distributed_db_version ->
remote.distributed_db_version
Deprecated_distributed_db_version >>? fun distributed_db_version ->
may_select_version
p2p_versions
remote.p2p_version >>= fun p2p_version ->
some { chain_name ;
distributed_db_version ;
p2p_version }
remote.p2p_version
Deprecated_p2p_version >>? fun p2p_version ->
ok { chain_name ;
distributed_db_version ;
p2p_version }
......@@ -50,4 +50,4 @@ val select:
chain_name: Distributed_db_version.name ->
distributed_db_versions: Distributed_db_version.t list ->
p2p_versions: P2p_version.t list ->
t -> t option
t -> t Error_monad.tzresult
......@@ -868,37 +868,41 @@ and raw_authenticate pool ?point_info canceler fd point =
| Some _ as point_info, _ | _, (Some _ as point_info) -> point_info in
let peer_info = register_peer pool info.peer_id in
let acceptable_version =
Network_version.select
~chain_name:pool.message_config.chain_name
~distributed_db_versions:pool.message_config.distributed_db_versions
~p2p_versions:pool.custom_p2p_versions
info.announced_version in
let acceptable_capacity =
let active = active_connections pool in
pool.config.max_connections > active in
let acceptable_point, rejection_argument =
Option.unopt_map connection_point_info
~default:(not pool.config.private_mode, "unknown point in private mode")
~f:begin fun connection_point_info ->
match P2p_point_state.get connection_point_info with
| Requested _ -> not incoming, "Requested status and incoming"
| Disconnected ->
let unexpected =
pool.config.private_mode
&& not (P2p_point_state.Info.trusted connection_point_info) in
if unexpected then
warn "[private node] incoming connection from untrusted \
peer rejected!";
(not unexpected, "unexpected connection in private mode")
| Accepted _ | Running _ -> (false, "already running or accepted point")
end in
let acceptable_peer_id =
match P2p_peer_state.get peer_info with
| Accepted _ ->
(* TODO: in some circumstances cancel and accept... *)
false
| Running _ -> false
| Disconnected -> true in
begin
Network_version.select
~chain_name:pool.message_config.chain_name
~distributed_db_versions:pool.message_config.distributed_db_versions
~p2p_versions:pool.custom_p2p_versions
info.announced_version >>? fun version ->
begin if pool.config.max_connections > active_connections pool
then ok version
else P2p_rejection.(rejecting Too_many_connections) end >>? fun version ->
Option.unopt_map connection_point_info
~default:(if pool.config.private_mode then P2p_rejection.(rejecting No_argument)
else ok version)
~f:begin fun connection_point_info ->
match P2p_point_state.get connection_point_info with
| Requested _ -> if incoming then P2p_rejection.(rejecting Already_connected)
else ok version
| Disconnected ->
let unexpected =
pool.config.private_mode
&& not (P2p_point_state.Info.trusted connection_point_info) in
if unexpected then
begin warn "[private node] incoming connection from untrusted \
peer rejected!";
P2p_rejection.(rejecting No_argument)
end
else ok version
| Accepted _ | Running _ -> P2p_rejection.(rejecting Already_connected)
end >>? fun version ->
match P2p_peer_state.get peer_info with
| Accepted _
(* TODO: in some circumstances cancel and accept... *)
| Running _ -> P2p_rejection.(rejecting Already_connected)
| Disconnected -> ok version
end
in
(* To Verify : the thread must ? not be interrupted between
point removal from incoming and point registration into
active connection to prevent flooding attack.
......@@ -913,8 +917,28 @@ and raw_authenticate pool ?point_info canceler fd point =
~f:(fun point_info ->
P2p_point_state.set_private point_info info.private_node) ;
match acceptable_version with
| Some version
when acceptable_capacity && acceptable_peer_id && acceptable_point -> begin
| Error [ P2p_rejection.Rejecting { motive } ] -> begin
log pool (Rejecting_request (point, info.id_point, info.peer_id)) ;
lwt_debug "authenticate: %a -> kick %a, motive %a)"
P2p_point.Id.pp point
P2p_peer.Id.pp info.peer_id
P2p_rejection.pp motive >>= fun () ->
list_known_points pool >>= fun point_list ->
P2p_socket.kick auth_fd motive point_list >>= fun () ->
if not incoming then begin
Option.iter ~f:P2p_point_state.set_disconnected point_info ;
(* FIXME P2p_peer_state.set_disconnected ~requested:true peer_info ; *)
end ;
match motive with
| Unknown_chain_name
| Deprecated_distributed_db_version
| Deprecated_p2p_version ->
fail (P2p_errors.Rejected_no_common_protocol
{ announced = info.announced_version })
| _ -> fail (P2p_errors.Rejected info.peer_id)
end
| Error _ as err -> Lwt.return err
| Ok version -> begin
log pool (Accepting_request (point, info.id_point, info.peer_id)) ;
Option.iter connection_point_info
~f:(fun point_info ->
......@@ -964,41 +988,6 @@ and raw_authenticate pool ?point_info canceler fd point =
pool conn
id_point connection_point_info peer_info version)
end
| _ -> begin
log pool (Rejecting_request (point, info.id_point, info.peer_id)) ;
begin
match acceptable_version with
| None ->
lwt_debug "authenticate: %a -> kick %a,\
no compatible network (announced %a)"
P2p_point.Id.pp point
P2p_peer.Id.pp info.peer_id
Network_version.pp info.announced_version
| _ ->
lwt_debug "authenticate: %a -> kick %a enough slots:%B,\
point acceptable: %B%a, peer_id acceptable: %B"
P2p_point.Id.pp point
P2p_peer.Id.pp info.peer_id
acceptable_capacity
acceptable_point
(fun fmt (acceptable_point, rejection_argument) ->
if not acceptable_point then
Format.fprintf fmt "(%s)" rejection_argument)
(acceptable_point, rejection_argument)
acceptable_peer_id
end >>= fun () ->
list_known_points pool >>= fun point_list ->
P2p_socket.kick auth_fd point_list >>= fun () ->
if not incoming then begin
Option.iter ~f:P2p_point_state.set_disconnected point_info ;
(* FIXME P2p_peer_state.set_disconnected ~requested:true peer_info ; *)
end ;
match acceptable_version with
| None ->
fail (P2p_errors.Rejected_no_common_protocol
{ announced = info.announced_version })
| Some _ -> fail (P2p_errors.Rejected info.peer_id)
end
and create_connection pool p2p_conn id_point point_info peer_info negotiated_version =
let peer_id = P2p_peer_state.Info.peer_id peer_info in
......
......@@ -225,14 +225,16 @@ module Ack = struct
type t = Ack
| Nack_v_0
| Nack of { potential_peers_to_connect : P2p_point.Id.t list }
| Nack of { motive : P2p_rejection.argument;
potential_peers_to_connect : P2p_point.Id.t list }
let encoding =
let open Data_encoding in
let ack_encoding = obj1 (req "ack" empty) in
let nack_v_0_encoding = obj1 (req "nack_v_0" empty) in
let nack_encoding =
obj1 (req "nack" (Data_encoding.list P2p_point.Id.encoding))
obj2 (req "nack_motive" P2p_rejection.encoding)
(req "nack_list" (Data_encoding.list P2p_point.Id.encoding))
in
let ack_case tag =
case tag ack_encoding
......@@ -243,11 +245,11 @@ module Ack = struct
case tag nack_encoding
~title:"Nack"
(function
| Nack { potential_peers_to_connect } ->
Some potential_peers_to_connect
| Nack { motive; potential_peers_to_connect } ->
Some ( motive , potential_peers_to_connect )
| _ -> None
)
(fun lst -> Nack { potential_peers_to_connect = lst }) in
(fun (motive, lst ) -> Nack { motive ; potential_peers_to_connect = lst }) in
let nack_v_0_case tag =
case tag nack_v_0_encoding
~title:"Nack_v_0"
......@@ -291,7 +293,7 @@ type 'meta authenticated_connection = {
cryptobox_data: Crypto.data ;
}
let kick { fd ; cryptobox_data ; info } potential_peers_to_connect =
let kick { fd ; cryptobox_data ; info } motive potential_peers_to_connect =
let nack =
if P2p_version.feature_available
P2p_version.Nack_with_list
......@@ -300,7 +302,7 @@ let kick { fd ; cryptobox_data ; info } potential_peers_to_connect =
debug "Nack point %a with point list %a"
P2p_connection.Id.pp info.id_point
P2p_point.Id.pp_list potential_peers_to_connect ;
Ack.Nack { potential_peers_to_connect }
Ack.Nack { motive ; potential_peers_to_connect }
end else begin
debug "Nack point %a (no point list due to p2p version)"
P2p_connection.Id.pp info.id_point ;
......@@ -614,9 +616,14 @@ let accept
end ;
return conn
| Nack_v_0 ->
fail (P2p_errors.Rejected_by_nack { alternative_points = None })
| Nack { potential_peers_to_connect } ->
fail (P2p_errors.Rejected_by_nack { alternative_points = Some potential_peers_to_connect })
fail (P2p_errors.Rejected_by_nack
{motive = (P2p_rejection.Unknown_argument
{error_code=0}) ;
alternative_points = None })
| Nack { motive ; potential_peers_to_connect } ->
fail (P2p_errors.Rejected_by_nack
{ motive;
alternative_points = Some potential_peers_to_connect })
let catch_closed_pipe f =
Lwt.catch f begin function
......
......@@ -76,11 +76,17 @@ val authenticate:
[P2P_io_scheduler.connection] into an [authenticated_connection] (auth
correct, acceptation undecided). *)
val kick: 'meta authenticated_connection -> P2p_point.Id.t list -> unit Lwt.t
(** (Low-level) (Cancelable) [kick afd] notifies the remote peer that
we refuse this connection and then closes [afd]. Used in
[P2p_connection_pool] to reject an [authenticated_connection] which we do
not want to connect to for some reason. *)
val kick: 'meta authenticated_connection ->
P2p_rejection.argument ->
P2p_point.Id.t list ->
unit Lwt.t
(** (Low-level) (Cancelable) [kick afd motive alternative_points]
notifies the remote peer that we refuse this connection, sending the
[motive] and a list of [alternative_points] it can try to connect to
and then closes [afd].
Used in [P2p_connection_pool] to reject a [authenticated_connection]
which we do not want to connect to for some [motive]. *)
val accept:
?incoming_message_queue_size:int ->
......
......@@ -203,7 +203,7 @@ module Kick = struct
_assert (info.incoming) __LOC__ "" >>=? fun () ->
_assert (P2p_peer.Id.compare info.peer_id id2.peer_id = 0)
__LOC__ "" >>=? fun () ->
P2p_socket.kick auth_fd [] >>= fun () ->
P2p_socket.kick auth_fd P2p_rejection.No_argument [] >>= fun () ->
return_unit
let client _ch sched addr port =
......@@ -228,7 +228,7 @@ module Kicked = struct
let client _ch sched addr port =
connect sched addr port id2 >>=? fun auth_fd ->
P2p_socket.kick auth_fd [] >>= fun () ->
P2p_socket.kick auth_fd P2p_rejection.No_argument [] >>= fun () ->
return_unit
let run _dir = run_nodes client server
......
......@@ -45,7 +45,8 @@ type error += Decipher_error
type error += Invalid_message_size
type error += Encoding_error
type error += Rejected_socket_connection
type error += Rejected_by_nack of { alternative_points : P2p_point.Id.t list option }
type error += Rejected_by_nack of { motive: P2p_rejection.argument ;
alternative_points : P2p_point.Id.t list option }
type error += Rejected_no_common_protocol of { announced : Network_version.t }
type error += Decoding_error
type error += Myself of P2p_connection.Id.t
......@@ -104,9 +105,14 @@ let () =
~pp:(fun ppf _lst ->
Format.fprintf ppf
"Rejected peer connection: rejected by Nack with a list of alternative peers.")
Data_encoding.(obj1 (opt "alternative_points" (list P2p_point.Id.encoding)))
(function Rejected_by_nack { alternative_points } -> Some alternative_points | _ -> None)
(fun alternative_points -> Rejected_by_nack { alternative_points }) ;
Data_encoding.(obj2 (req "motive" (P2p_rejection.encoding))
(opt "alternative_points" (list P2p_point.Id.encoding)))
(function
Rejected_by_nack {motive ; alternative_points } ->
Some ( motive , alternative_points)
| _ -> None)
(fun ( motive , alternative_points ) ->
Rejected_by_nack { motive ; alternative_points }) ;
(* Rejected socket connection, no common network protocol *)
register_error_kind
`Permanent
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment