Skip to content

Commit 46a80e5

Browse files
committed
add more tcp/ip socket options for linux only
1 parent 7093825 commit 46a80e5

File tree

3 files changed

+326
-41
lines changed

3 files changed

+326
-41
lines changed

lib_eio_linux/low_level.ml

Lines changed: 131 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -634,11 +634,19 @@ module Sockopt = struct
634634
let tcp_keepidle = 4 (* TCP_KEEPIDLE *)
635635
let tcp_keepintvl = 5 (* TCP_KEEPINTVL *)
636636
let tcp_keepcnt = 6 (* TCP_KEEPCNT *)
637+
let tcp_syncnt = 7 (* TCP_SYNCNT *)
637638
let tcp_linger2 = 8 (* TCP_LINGER2 *)
638639
let tcp_defer_accept = 9 (* TCP_DEFER_ACCEPT *)
640+
let tcp_window_clamp = 10 (* TCP_WINDOW_CLAMP *)
641+
let tcp_quickack = 12 (* TCP_QUICKACK *)
639642
let tcp_congestion = 13 (* TCP_CONGESTION *)
640643
let tcp_user_timeout = 18 (* TCP_USER_TIMEOUT *)
644+
let tcp_fastopen = 23 (* TCP_FASTOPEN *)
641645
let ipproto_tcp = 6 (* IPPROTO_TCP *)
646+
let ipproto_ip = 0 (* IPPROTO_IP *)
647+
let ip_freebind = 15 (* IP_FREEBIND *)
648+
let ip_bind_address_no_port = 24 (* IP_BIND_ADDRESS_NO_PORT *)
649+
let ip_local_port_range = 51 (* IP_LOCAL_PORT_RANGE *)
642650

643651
external setsockopt_int : Unix.file_descr -> int -> int -> int -> unit = "caml_eio_sockopt_int_set"
644652
external getsockopt_int : Unix.file_descr -> int -> int -> int = "caml_eio_sockopt_int_get"
@@ -653,69 +661,155 @@ module Sockopt = struct
653661
| TCP_KEEPCNT : int Eio.Net.Sockopt.t
654662
| TCP_USER_TIMEOUT : int Eio.Net.Sockopt.t
655663
| TCP_MAXSEG : int Eio.Net.Sockopt.t
656-
| TCP_LINGER2 : int Eio.Net.Sockopt.t
664+
| TCP_LINGER2 : int option Eio.Net.Sockopt.t
657665
| TCP_DEFER_ACCEPT : int Eio.Net.Sockopt.t
658666
| TCP_CONGESTION : string Eio.Net.Sockopt.t
667+
| TCP_SYNCNT : int Eio.Net.Sockopt.t
668+
| TCP_WINDOW_CLAMP : int Eio.Net.Sockopt.t
669+
| TCP_QUICKACK : bool Eio.Net.Sockopt.t
670+
| TCP_FASTOPEN : int Eio.Net.Sockopt.t
671+
| IP_FREEBIND : bool Eio.Net.Sockopt.t
672+
| IP_BIND_ADDRESS_NO_PORT : bool Eio.Net.Sockopt.t
673+
| IP_LOCAL_PORT_RANGE : (int * int) Eio.Net.Sockopt.t
659674

660675
let set : type a. Fd.t -> a Eio.Net.Sockopt.t -> a -> unit = fun fd opt v ->
661676
match opt with
662677
| TCP_CORK ->
663-
Fd.use_exn "setsockopt" fd (fun unix_fd ->
664-
setsockopt_int unix_fd ipproto_tcp tcp_cork (if v then 1 else 0))
678+
Fd.use_exn "setsockopt" fd (fun fd ->
679+
setsockopt_int fd ipproto_tcp tcp_cork (if v then 1 else 0))
665680
| TCP_KEEPIDLE ->
666-
Fd.use_exn "setsockopt" fd (fun unix_fd ->
667-
setsockopt_int unix_fd ipproto_tcp tcp_keepidle v)
681+
if v < 0 then
682+
invalid_arg (Printf.sprintf "TCP_KEEPIDLE must be non-negative, got %d" v);
683+
Fd.use_exn "setsockopt" fd (fun fd ->
684+
setsockopt_int fd ipproto_tcp tcp_keepidle v)
668685
| TCP_KEEPINTVL ->
669-
Fd.use_exn "setsockopt" fd (fun unix_fd ->
670-
setsockopt_int unix_fd ipproto_tcp tcp_keepintvl v)
686+
if v < 0 then
687+
invalid_arg (Printf.sprintf "TCP_KEEPINTVL must be non-negative, got %d" v);
688+
Fd.use_exn "setsockopt" fd (fun fd ->
689+
setsockopt_int fd ipproto_tcp tcp_keepintvl v)
671690
| TCP_KEEPCNT ->
672-
Fd.use_exn "setsockopt" fd (fun unix_fd ->
673-
setsockopt_int unix_fd ipproto_tcp tcp_keepcnt v)
691+
if v < 0 then
692+
invalid_arg (Printf.sprintf "TCP_KEEPCNT must be non-negative, got %d" v);
693+
Fd.use_exn "setsockopt" fd (fun fd ->
694+
setsockopt_int fd ipproto_tcp tcp_keepcnt v)
674695
| TCP_USER_TIMEOUT ->
675-
Fd.use_exn "setsockopt" fd (fun unix_fd ->
676-
setsockopt_int unix_fd ipproto_tcp tcp_user_timeout v)
696+
if v < 0 then
697+
invalid_arg (Printf.sprintf "TCP_USER_TIMEOUT must be non-negative, got %d" v);
698+
Fd.use_exn "setsockopt" fd (fun fd ->
699+
setsockopt_int fd ipproto_tcp tcp_user_timeout v)
677700
| TCP_MAXSEG ->
678-
Fd.use_exn "setsockopt" fd (fun unix_fd ->
679-
setsockopt_int unix_fd ipproto_tcp tcp_maxseg v)
701+
if v < 0 then
702+
invalid_arg (Printf.sprintf "TCP_MAXSEG must be non-negative, got %d" v);
703+
Fd.use_exn "setsockopt" fd (fun fd ->
704+
setsockopt_int fd ipproto_tcp tcp_maxseg v)
680705
| TCP_LINGER2 ->
681-
Fd.use_exn "setsockopt" fd (fun unix_fd ->
682-
setsockopt_int unix_fd ipproto_tcp tcp_linger2 v)
706+
let v = match v with
707+
| None -> -1
708+
| Some n when n < 0 ->
709+
invalid_arg (Printf.sprintf "TCP_LINGER2 must be non-negative, got %d" n);
710+
| Some n -> n
711+
in
712+
Fd.use_exn "setsockopt" fd (fun fd ->
713+
setsockopt_int fd ipproto_tcp tcp_linger2 v)
683714
| TCP_DEFER_ACCEPT ->
684-
Fd.use_exn "setsockopt" fd (fun unix_fd ->
685-
setsockopt_int unix_fd ipproto_tcp tcp_defer_accept v)
715+
if v < 0 then
716+
invalid_arg (Printf.sprintf "TCP_DEFER_ACCEPT must be non-negative, got %d" v);
717+
Fd.use_exn "setsockopt" fd (fun fd ->
718+
setsockopt_int fd ipproto_tcp tcp_defer_accept v)
686719
| TCP_CONGESTION ->
687-
Fd.use_exn "setsockopt" fd (fun unix_fd ->
688-
setsockopt_string unix_fd ipproto_tcp tcp_congestion v)
720+
Fd.use_exn "setsockopt" fd (fun fd ->
721+
setsockopt_string fd ipproto_tcp tcp_congestion v)
722+
| TCP_SYNCNT ->
723+
if v < 1 || v > 255 then
724+
invalid_arg (Printf.sprintf "TCP_SYNCNT must be between 1 and 255, got %d" v);
725+
Fd.use_exn "setsockopt" fd (fun fd ->
726+
setsockopt_int fd ipproto_tcp tcp_syncnt v)
727+
| TCP_WINDOW_CLAMP ->
728+
if v < 0 then
729+
invalid_arg (Printf.sprintf "TCP_WINDOW_CLAMP must be non-negative, got %d" v);
730+
Fd.use_exn "setsockopt" fd (fun fd ->
731+
setsockopt_int fd ipproto_tcp tcp_window_clamp v)
732+
| TCP_QUICKACK ->
733+
Fd.use_exn "setsockopt" fd (fun fd ->
734+
setsockopt_int fd ipproto_tcp tcp_quickack (if v then 1 else 0))
735+
| TCP_FASTOPEN ->
736+
if v < 0 then
737+
invalid_arg (Printf.sprintf "TCP_FASTOPEN queue length must be non-negative, got %d" v);
738+
Fd.use_exn "setsockopt" fd (fun fd ->
739+
setsockopt_int fd ipproto_tcp tcp_fastopen v)
740+
| IP_FREEBIND ->
741+
Fd.use_exn "setsockopt" fd (fun fd ->
742+
setsockopt_int fd ipproto_ip ip_freebind (if v then 1 else 0))
743+
| IP_BIND_ADDRESS_NO_PORT ->
744+
Fd.use_exn "setsockopt" fd (fun fd ->
745+
setsockopt_int fd ipproto_ip ip_bind_address_no_port (if v then 1 else 0))
746+
| IP_LOCAL_PORT_RANGE ->
747+
let (lower, upper) = v in
748+
if lower < 0 || lower > 65535 then
749+
invalid_arg (Printf.sprintf "IP_LOCAL_PORT_RANGE lower bound must be 0-65535, got %d" lower);
750+
if upper < 0 || upper > 65535 then
751+
invalid_arg (Printf.sprintf "IP_LOCAL_PORT_RANGE upper bound must be 0-65535, got %d" upper);
752+
if lower <> 0 && upper <> 0 && lower > upper then
753+
invalid_arg (Printf.sprintf "IP_LOCAL_PORT_RANGE lower bound (%d) must be <= upper bound (%d)" lower upper);
754+
let combined = (upper lsl 16) lor lower in
755+
Fd.use_exn "setsockopt" fd (fun fd ->
756+
setsockopt_int fd ipproto_ip ip_local_port_range combined)
689757
| _ -> Eio_unix.Net.Sockopt.set fd opt v
690758

691759
let get : type a. Fd.t -> a Eio.Net.Sockopt.t -> a = fun fd opt ->
692760
match opt with
693761
| TCP_CORK ->
694-
Fd.use_exn "getsockopt" fd (fun unix_fd ->
695-
getsockopt_int unix_fd ipproto_tcp tcp_cork <> 0)
762+
Fd.use_exn "getsockopt" fd (fun fd ->
763+
getsockopt_int fd ipproto_tcp tcp_cork <> 0)
696764
| TCP_KEEPIDLE ->
697-
Fd.use_exn "getsockopt" fd (fun unix_fd ->
698-
getsockopt_int unix_fd ipproto_tcp tcp_keepidle)
765+
Fd.use_exn "getsockopt" fd (fun fd ->
766+
getsockopt_int fd ipproto_tcp tcp_keepidle)
699767
| TCP_KEEPINTVL ->
700-
Fd.use_exn "getsockopt" fd (fun unix_fd ->
701-
getsockopt_int unix_fd ipproto_tcp tcp_keepintvl)
768+
Fd.use_exn "getsockopt" fd (fun fd ->
769+
getsockopt_int fd ipproto_tcp tcp_keepintvl)
702770
| TCP_KEEPCNT ->
703-
Fd.use_exn "getsockopt" fd (fun unix_fd ->
704-
getsockopt_int unix_fd ipproto_tcp tcp_keepcnt)
771+
Fd.use_exn "getsockopt" fd (fun fd ->
772+
getsockopt_int fd ipproto_tcp tcp_keepcnt)
705773
| TCP_USER_TIMEOUT ->
706-
Fd.use_exn "getsockopt" fd (fun unix_fd ->
707-
getsockopt_int unix_fd ipproto_tcp tcp_user_timeout)
774+
Fd.use_exn "getsockopt" fd (fun fd ->
775+
getsockopt_int fd ipproto_tcp tcp_user_timeout)
708776
| TCP_MAXSEG ->
709-
Fd.use_exn "getsockopt" fd (fun unix_fd ->
710-
getsockopt_int unix_fd ipproto_tcp tcp_maxseg)
777+
Fd.use_exn "getsockopt" fd (fun fd ->
778+
getsockopt_int fd ipproto_tcp tcp_maxseg)
711779
| TCP_LINGER2 ->
712-
Fd.use_exn "getsockopt" fd (fun unix_fd ->
713-
getsockopt_int unix_fd ipproto_tcp tcp_linger2)
780+
let v = Fd.use_exn "getsockopt" fd (fun fd ->
781+
getsockopt_int fd ipproto_tcp tcp_linger2)
782+
in
783+
if v = -1 then None else Some v
714784
| TCP_DEFER_ACCEPT ->
715-
Fd.use_exn "getsockopt" fd (fun unix_fd ->
716-
getsockopt_int unix_fd ipproto_tcp tcp_defer_accept)
785+
Fd.use_exn "getsockopt" fd (fun fd ->
786+
getsockopt_int fd ipproto_tcp tcp_defer_accept)
717787
| TCP_CONGESTION ->
718-
Fd.use_exn "getsockopt" fd (fun unix_fd ->
719-
getsockopt_string unix_fd ipproto_tcp tcp_congestion)
788+
Fd.use_exn "getsockopt" fd (fun fd ->
789+
getsockopt_string fd ipproto_tcp tcp_congestion)
790+
| TCP_SYNCNT ->
791+
Fd.use_exn "getsockopt" fd (fun fd ->
792+
getsockopt_int fd ipproto_tcp tcp_syncnt)
793+
| TCP_WINDOW_CLAMP ->
794+
Fd.use_exn "getsockopt" fd (fun fd ->
795+
getsockopt_int fd ipproto_tcp tcp_window_clamp)
796+
| TCP_QUICKACK ->
797+
Fd.use_exn "getsockopt" fd (fun fd ->
798+
getsockopt_int fd ipproto_tcp tcp_quickack <> 0)
799+
| TCP_FASTOPEN ->
800+
Fd.use_exn "getsockopt" fd (fun fd ->
801+
getsockopt_int fd ipproto_tcp tcp_fastopen)
802+
| IP_FREEBIND ->
803+
Fd.use_exn "getsockopt" fd (fun fd ->
804+
getsockopt_int fd ipproto_ip ip_freebind <> 0)
805+
| IP_BIND_ADDRESS_NO_PORT ->
806+
Fd.use_exn "getsockopt" fd (fun fd ->
807+
getsockopt_int fd ipproto_ip ip_bind_address_no_port <> 0)
808+
| IP_LOCAL_PORT_RANGE ->
809+
Fd.use_exn "getsockopt" fd (fun fd ->
810+
let combined = getsockopt_int fd ipproto_ip ip_local_port_range in
811+
let lower = combined land 0xFFFF in
812+
let upper = (combined lsr 16) land 0xFFFF in
813+
(lower, upper))
720814
| _ -> Eio_unix.Net.Sockopt.get fd opt
721815
end

lib_eio_linux/low_level.mli

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -219,15 +219,40 @@ module Sockopt : sig
219219
| TCP_MAXSEG : int Eio.Net.Sockopt.t
220220
(** The maximum segment size for outgoing TCP packets. If set before connection
221221
establishment, it also changes the MSS value announced to the other end. *)
222-
| TCP_LINGER2 : int Eio.Net.Sockopt.t
223-
(** The lifetime of orphaned FIN_WAIT2 state sockets (in seconds).
222+
| TCP_LINGER2 : int option Eio.Net.Sockopt.t
223+
(** The lifetime of orphaned FIN_WAIT2 state sockets.
224+
[Some n] sets the timeout to [n] seconds.
225+
[None] uses the system default from /proc/sys/net/ipv4/tcp_fin_timeout.
224226
Not to be confused with SO_LINGER. *)
225227
| TCP_DEFER_ACCEPT : int Eio.Net.Sockopt.t
226228
(** Allow a listener to be awakened only when data arrives on the socket.
227229
Value is the maximum time in seconds to wait for data. *)
228230
| TCP_CONGESTION : string Eio.Net.Sockopt.t
229231
(** Set the TCP congestion control algorithm to be used (e.g., "cubic", "reno").
230232
Unprivileged processes are restricted to algorithms in tcp_allowed_congestion_control. *)
233+
| TCP_SYNCNT : int Eio.Net.Sockopt.t
234+
(** Set the number of SYN retransmits that TCP should send before aborting
235+
the attempt to connect. Cannot exceed 255. *)
236+
| TCP_WINDOW_CLAMP : int Eio.Net.Sockopt.t
237+
(** Bound the size of the advertised window to this value.
238+
The kernel imposes a minimum size. *)
239+
| TCP_QUICKACK : bool Eio.Net.Sockopt.t
240+
(** Enable quickack mode if set or disable if cleared. In quickack mode,
241+
acks are sent immediately rather than delayed. This flag is not permanent. *)
242+
| TCP_FASTOPEN : int Eio.Net.Sockopt.t
243+
(** Enable Fast Open (RFC 7413) on the listener socket. The value specifies
244+
the maximum length of pending SYNs (similar to backlog in listen). *)
245+
| IP_FREEBIND : bool Eio.Net.Sockopt.t
246+
(** Allow binding to an IP address that is nonlocal or does not (yet) exist.
247+
This permits listening on a socket without requiring the underlying
248+
network interface to be up. *)
249+
| IP_BIND_ADDRESS_NO_PORT : bool Eio.Net.Sockopt.t
250+
(** Inform the kernel to not reserve an ephemeral port when using bind()
251+
with a port number of 0. The port will be chosen at connect() time. *)
252+
| IP_LOCAL_PORT_RANGE : (int * int) Eio.Net.Sockopt.t
253+
(** Set the per-socket local port range as (lower_bound, upper_bound).
254+
Both bounds are inclusive and must be in range 0-65535.
255+
Use (0, 0) to reset to system defaults. *)
231256

232257
val set : fd -> 'a Eio.Net.Sockopt.t -> 'a -> unit
233258
(** [set fd opt v] sets socket option [opt] to value [v] on file descriptor [fd]. *)

0 commit comments

Comments
 (0)