Patch (partial): IPv6 support (in progress)

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
12 messages Options
Reply | Threaded
Open this post in threaded view
|

Patch (partial): IPv6 support (in progress)

Phil Pennock-17
Can those with more experience in OCaml please take a look over this and
tell me if I'm heading down the right path?

This patch theoretically adds support for IPv6 in recon; it adds the
settings hooks for hkp too, but doesn't add anything that uses that yet.
It should both listen on IPv6 and connect outbound too.

It compiles.  It runs.  I see both listening sockets with lsof.  It
doesn't do anything soon enough for me to see tonight how well I've
done, as I need to go get some sleep.  I'm not sufficiently comfortable
to leave sks-peer.spodhuis.org running this code overnight, so I've
reverted to 1.1.0.

It's my first OCaml hacking and my style sucks and I spend more time
debugging to get it to compile than I care to think about.

A critical eye and constructive comments appreciated.

Oh, and I've no idea how recent support for IPv6 in OCaml is and have
made no effort to gracefully degrade to either old OCaml environments or
systems without IPv6 support.  First, I want to get it working for
anyone, *then* I can worry about making it conditional.

Thanks,
-Phil

diff -r e30dc5376cbb common.ml
--- a/common.ml Fri May 23 21:16:40 2008 -0400
+++ b/common.ml Mon Mar 02 02:43:24 2009 -0800
@@ -199,6 +199,7 @@
 
 let recon_port = !Settings.recon_port
 let recon_address = !Settings.recon_address
+let recon6_address = !Settings.recon6_address
 let http_port = !Settings.hkp_port
 let http_address = !Settings.hkp_address
 let db_command_name = Filename.concat !Settings.basedir "db_com_sock"
@@ -212,7 +213,15 @@
   | Unix.ADDR_INET (inet_addr,port) -> Unix.ADDR_INET (inet_addr,port + 1)
 
 
-let get_client_recon_addr () =
-  Unix.ADDR_INET (Unix.inet_addr_of_string recon_address,0)
+let get_client_recon_addr remote_addr = match Unix.domain_of_sockaddr remote_addr with
+    Unix.PF_UNIX -> failwith "Can't connect to remote local-domain sockets"
+  | Unix.PF_INET -> Unix.ADDR_INET (Unix.inet_addr_of_string recon_address,0)
+  | Unix.PF_INET6 -> Unix.ADDR_INET (Unix.inet_addr_of_string recon6_address,0)
 let get_client_recon_addr =
-  Utils.unit_memoize get_client_recon_addr
+  Utils.memoize get_client_recon_addr
+
+let sockaddr_to_string sockaddr = match sockaddr with
+    Unix.ADDR_UNIX s -> sprintf "<ADDR_UNIX %s>" s
+  | Unix.ADDR_INET (addr,p) -> sprintf "<ADDR_INET %s:%d>"
+      (Unix.string_of_inet_addr addr) p
+
diff -r e30dc5376cbb eventloop.ml
--- a/eventloop.ml Fri May 23 21:16:40 2008 -0400
+++ b/eventloop.ml Mon Mar 02 02:43:24 2009 -0800
@@ -121,10 +121,7 @@
 
 let create_sock addr =
   try
-    let domain =
-      match addr with
-  ADDR_UNIX _ -> PF_UNIX
- | ADDR_INET (_,_) -> PF_INET in
+    let domain = Unix.domain_of_sockaddr addr in
     let sock =
       socket ~domain ~kind:SOCK_STREAM ~protocol:0 in
     setsockopt sock SO_REUSEADDR true;
@@ -132,8 +129,10 @@
     listen sock ~max:20;
     sock
   with
-    | Unix_error (_,"bind",_) ->
- failwith "Failure while binding socket.  Probably another socket bound to this address"
+    | Unix_error (error, "bind",_) ->
+        let msg = error_message error in
+        let addrstr = sockaddr_to_string addr in
+ failwith (sprintf "Failure binding %s: %s" addrstr msg)
     | e -> raise e
 let add_events heap evlist =
   List.iter ~f:(fun (Event (time, callback)) ->
diff -r e30dc5376cbb membership.ml
--- a/membership.ml Fri May 23 21:16:40 2008 -0400
+++ b/membership.ml Mon Mar 02 02:43:24 2009 -0800
@@ -29,28 +29,80 @@
 exception Bug of string
 exception Lookup_failure of string
 exception Malformed_entry of string
+exception Empty_line
 
 let membership = ref ([| |],-1.)
 
 let whitespace = Str.regexp "[ \t]+"
 
-let lookup_hostname string =
-  try (Unix.gethostbyname string).Unix.h_addr_list.(0)
-  with
+(* Although the OCaml standard Unix library does not include AF_UNSPEC, the
+ * underlying C implementation uses AF_UNSPEC unless overriden (ie, there's
+ * no way to specify a value which matches the default behaviour).
+ * So for IPv4/IPv6 functionality, use getaddrinfo without an AI_FAMILY
+ * parameter.
+ * This does mean that we need a separate function to filter though.
+ *)
+
+let inet_addr_of_sockaddr sock = match sock with
+    Unix.ADDR_UNIX _ -> raise (Bug "asking for inet addr of local domain socket")
+  | Unix.ADDR_INET (addr, port) -> addr
+
+let inet_addr_of_addr_info ai = match ai.Unix.ai_addr with
+    Unix.ADDR_UNIX _ -> raise (Bug "converting getaddrinfo result which is local domain?")
+  | Unix.ADDR_INET (addr, port) -> addr
+
+let lookup_hostname_list ?(port = recon_port) string =
+  let portstr = string_of_int port in
+  try begin
+    let reslist = Unix.getaddrinfo string portstr [Unix.AI_SOCKTYPE Unix.SOCK_STREAM] in
+    if List.length reslist == 0 then raise (Lookup_failure string)
+    else List.map inet_addr_of_addr_info reslist
+  end
+  with
     | Invalid_argument _ | Not_found -> raise (Lookup_failure string)
 
-let local_recon_addr () =
-  Unix.ADDR_INET (lookup_hostname !Settings.hostname, recon_port)
+let lookup_hostname_list_filtered ~family ?(port = recon_port) string =
+  let portstr = string_of_int port in
+  let filter = (fun ai -> if ai.Unix.ai_family == family then true else false) in
+  try begin
+    let reslist = Unix.getaddrinfo string portstr [Unix.AI_SOCKTYPE Unix.SOCK_STREAM] in
+    if List.length reslist == 0 then raise (Lookup_failure string)
+    else List.map inet_addr_of_addr_info (List.filter filter reslist)
+  end
+  with
+    | Invalid_argument _ | Not_found -> raise (Lookup_failure string)
 
-let local_recon_addr = Utils.unit_memoize local_recon_addr
+let my_ipv4_recon_addresses () =
+  let r = Unix.inet_addr_of_string !Settings.recon_address in
+  if r = Unix.inet_addr_any
+  then lookup_hostname_list_filtered ~family:Unix.PF_INET !Settings.hostname
+  else [r]
+let my_ipv4_recon_addresses = Utils.unit_memoize my_ipv4_recon_addresses
+
+let my_ipv6_recon_addresses () =
+  let r = Unix.inet_addr_of_string !Settings.recon6_address in
+  if r = Unix.inet6_addr_any
+  then lookup_hostname_list_filtered ~family:Unix.PF_INET6 !Settings.hostname
+  else [r]
+let my_ipv6_recon_addresses = Utils.unit_memoize my_ipv6_recon_addresses
+
+let is_local_recon_addr peer = match Unix.domain_of_sockaddr peer with
+    Unix.PF_UNIX -> raise (Bug "trying to find local address of remote local domain socket")
+  | Unix.PF_INET -> List.exists (fun a -> (inet_addr_of_sockaddr peer) = a) (my_ipv4_recon_addresses ())
+  | Unix.PF_INET6 -> List.exists (fun a -> (inet_addr_of_sockaddr peer) = a) (my_ipv6_recon_addresses ())
+
+let is_local_recon_addr = Utils.memoize is_local_recon_addr
 
 let remove_self addresses =
-  List.filter ~f:(fun (addr,str) -> addr <> local_recon_addr ()) addresses
+  List.filter ~f:(fun (addr,str) -> not (is_local_recon_addr addr)) addresses
 
+(* For now, leave this single-address *)
 let convert_address l =
   try
-    sscanf l "%s %d"
-    (fun addr port -> Unix.ADDR_INET (lookup_hostname addr,port))
+    if String.length l == 0 then raise Empty_line
+    else
+      sscanf l "%s %d"
+      (fun addr port -> Unix.ADDR_INET (List.hd (lookup_hostname_list addr),port))
   with
     Scanf.Scan_failure _ | End_of_file | Failure _ -> raise (Malformed_entry l)
 
@@ -61,6 +113,7 @@
       let addr = convert_address line in
       (addr,line) :: loop list
     with
+      | Empty_line -> loop list
       | End_of_file -> list
       | Lookup_failure addr ->
   perror "Lookup failure on address %s" addr;
@@ -93,11 +146,6 @@
      )
     ~finally:(fun () -> close_in file)
 
-let sockaddr_to_string sockaddr = match sockaddr with
-    Unix.ADDR_UNIX s -> sprintf "<ADDR_UNIX %s>" s
-  | Unix.ADDR_INET (addr,p) -> sprintf "<ADDR_INET %s:%d>"
-      (Unix.string_of_inet_addr addr) p
-
 let membership_string () =
   let (mshp,_) = !membership in
   let to_string (addr,str) =
diff -r e30dc5376cbb reconCS.ml
--- a/reconCS.ml Fri May 23 21:16:40 2008 -0400
+++ b/reconCS.ml Mon Mar 02 02:43:24 2009 -0800
@@ -129,7 +129,7 @@
 
 
 (** function to connect to remote host to initate reconciliation *)
-let connect tree ~filters ~partner ~self =
+let connect tree ~filters ~partner =
   (* TODO: change the following to depend on the address type *)
   let s = Unix.socket
     ~domain:Unix.PF_INET
@@ -137,7 +137,7 @@
     ~protocol:0
   in
   let run () =
-    Unix.bind s ~addr:(get_client_recon_addr ());
+    Unix.bind s ~addr:(get_client_recon_addr partner);
     Unix.connect s ~addr:partner;
     let cin = Channel.sys_in_from_fd s
     and cout = Channel.sys_out_from_fd s in
diff -r e30dc5376cbb reconComm.ml
--- a/reconComm.ml Fri May 23 21:16:40 2008 -0400
+++ b/reconComm.ml Mon Mar 02 02:43:24 2009 -0800
@@ -67,11 +67,11 @@
 
 let get_keystrings_via_http addr hashes =
   let s = Unix.socket
-    ~domain:Unix.PF_INET
+    ~domain:(Unix.domain_of_sockaddr addr)
     ~kind:Unix.SOCK_STREAM
     ~protocol:0  in
   protect ~f:(fun () ->
- Unix.bind s ~addr:(get_client_recon_addr ());
+ Unix.bind s ~addr:(get_client_recon_addr addr);
  Unix.connect s ~addr;
  let cin = Channel.sys_in_from_fd s
  and cout = Channel.sys_out_from_fd s in
diff -r e30dc5376cbb reconserver.ml
--- a/reconserver.ml Fri May 23 21:16:40 2008 -0400
+++ b/reconserver.ml Mon Mar 02 02:43:24 2009 -0800
@@ -50,7 +50,9 @@
   (******************************************************************)
 
   let recon_addr = Unix.ADDR_INET (Unix.inet_addr_of_string recon_address,recon_port)
+  let recon6_addr = Unix.ADDR_INET (Unix.inet_addr_of_string recon6_address,recon_port)
   let reconsock = Eventloop.create_sock recon_addr
+  let recon6sock = Eventloop.create_sock recon6_addr
 
   let () =
     if Sys.file_exists recon_command_name
@@ -215,7 +217,6 @@
  let filters = get_filters () in
  let (results,http_addr) =
   ReconCS.connect (get_ptree ()) ~filters ~partner
-    ~self:Membership.local_recon_addr
  in
  let results = ZSet.elements results in
  plerror 4 "Reconciliation complete";
@@ -360,7 +361,12 @@
    ~timeout:!Settings.command_timeout
  );
  (reconsock, Eventloop.make_th
-   ~name:"reconciliation handler"
+   ~name:"reconciliation handler (IPv4)"
+   ~cb:recon_handler
+   ~timeout:!Settings.reconciliation_config_timeout
+ );
+ (recon6sock, Eventloop.make_th
+   ~name:"reconciliation handler (IPv6)"
    ~cb:recon_handler
    ~timeout:!Settings.reconciliation_config_timeout
  );
diff -r e30dc5376cbb settings.ml
--- a/settings.ml Fri May 23 21:16:40 2008 -0400
+++ b/settings.ml Mon Mar 02 02:43:24 2009 -0800
@@ -56,12 +56,16 @@
   seed := value
 
 let recon_port = ref 11370
-let recon_address = ref "0.0.0.0"
+let recon_address = ref (Unix.string_of_inet_addr Unix.inet_addr_any)
 let set_recon_address value = recon_address := value
+let recon6_address = ref (Unix.string_of_inet_addr Unix.inet6_addr_any)
+let set_recon6_address value = recon6_address := value
 
 let hkp_port = ref 11371
-let hkp_address = ref "0.0.0.0"
+let hkp_address = ref (Unix.string_of_inet_addr Unix.inet_addr_any)
 let set_hkp_address value = hkp_address := value
+let hkp6_address = ref (Unix.string_of_inet_addr Unix.inet6_addr_any)
+let set_hkp6_address value = hkp6_address := value
 
 let use_port_80 = ref false
 
@@ -247,9 +251,11 @@
     ("-baseport",Arg.Int set_base_port, " Set base port number");
     ("-logfile",Arg.String (fun _ -> ()), " DEPRECATED.  Now ignored.");
     ("-recon_port",Arg.Int set_recon_port, " Set recon port number");
-    ("-recon_address",Arg.String set_recon_address, " Set recon binding address");
+    ("-recon_address",Arg.String set_recon_address, " Set recon binding address (IPv4)");
+    ("-recon6_address",Arg.String set_recon6_address, " Set recon binding address (IPv6)");
     ("-hkp_port",Arg.Int set_hkp_port, " Set hkp port number");
-    ("-hkp_address",Arg.String set_hkp_address, " Set hkp binding address");
+    ("-hkp_address",Arg.String set_hkp_address, " Set hkp binding address (IPv4)");
+    ("-hkp6_address",Arg.String set_hkp6_address, " Set hkp binding address (IPv6)");
     ("-use_port_80",Arg.Set use_port_80,
      " Have the HKP interface listen on port 80, as well as the hkp_port");
     ("-basedir", Arg.Set_string basedir, " Base directory");
diff -r e30dc5376cbb sks.pod
--- a/sks.pod Fri May 23 21:16:40 2008 -0400
+++ b/sks.pod Mon Mar 02 02:43:24 2009 -0800
@@ -152,7 +152,11 @@
 
 =item -recon_address
 
-Set recon binding address.
+Set recon binding IPv4 address.  Also used for outbound connections.
+
+=item -recon6_address
+
+Set recon binding IPv6 address.  Also used for outbound connections.
 
 =item -hkp_port
 
@@ -160,7 +164,11 @@
 
 =item -hkp_address
 
-Set hkp binding address.
+Set hkp binding IPv4 address.
+
+=item -hkp6_address
+
+Set hkp binding IPv6 address.
 
 =item -use_port_80
 

_______________________________________________
Sks-devel mailing list
[hidden email]
http://lists.nongnu.org/mailman/listinfo/sks-devel

attachment0 (169 bytes) Download Attachment
Reply | Threaded
Open this post in threaded view
|

Re: Patch (partial): IPv6 support (in progress)

kmkaplan-3
Phil Pennock:

> Can those with more experience in OCaml please take a look over this and
> tell me if I'm heading down the right path?
>
> This patch theoretically adds support for IPv6 in recon; it adds the
> settings hooks for hkp too, but doesn't add anything that uses that yet.
> It should both listen on IPv6 and connect outbound too.
>
> It compiles.  It runs.  I see both listening sockets with lsof.  It
> doesn't do anything soon enough for me to see tonight how well I've
> done, as I need to go get some sleep.  I'm not sufficiently comfortable
> to leave sks-peer.spodhuis.org running this code overnight, so I've
> reverted to 1.1.0.
>
> It's my first OCaml hacking and my style sucks and I spend more time
> debugging to get it to compile than I care to think about.
>
> A critical eye and constructive comments appreciated.
>
> Oh, and I've no idea how recent support for IPv6 in OCaml is and have
> made no effort to gracefully degrade to either old OCaml environments or
> systems without IPv6 support.  First, I want to get it working for
> anyone, *then* I can worry about making it conditional.
Hello,

it is funny that you send this as I was just doing something very
similar this weekend.  Here is what I did.  I am still experimenting
with what actually works, what does not and trying to find what I broke.
I'll have a look at the way you did it when I have some time, probably
wednesday.

Kim Minh.


--- a/eventloop.ml Fri May 23 21:16:40 2008 -0400
+++ b/eventloop.ml Mon Mar 02 12:48:35 2009 +0000
@@ -121,10 +121,7 @@
 
 let create_sock addr =
   try
-    let domain =
-      match addr with
-  ADDR_UNIX _ -> PF_UNIX
- | ADDR_INET (_,_) -> PF_INET in
+    let domain = domain_of_sockaddr addr in
     let sock =
       socket ~domain ~kind:SOCK_STREAM ~protocol:0 in
     setsockopt sock SO_REUSEADDR true;
diff -r e30dc5376cbb membership.ml
--- a/membership.ml Fri May 23 21:16:40 2008 -0400
+++ b/membership.ml Mon Mar 02 12:48:35 2009 +0000
@@ -34,32 +34,34 @@
 
 let whitespace = Str.regexp "[ \t]+"
 
-let lookup_hostname string =
-  try (Unix.gethostbyname string).Unix.h_addr_list.(0)
-  with
-    | Invalid_argument _ | Not_found -> raise (Lookup_failure string)
+let lookup_hostname string port =
+  let addrs = Unix.getaddrinfo string port [Unix.AI_SOCKTYPE(Unix.SOCK_STREAM)]
+  in
+  if List.length addrs = 0 then raise (Lookup_failure string);
+  addrs
 
 let local_recon_addr () =
-  Unix.ADDR_INET (lookup_hostname !Settings.hostname, recon_port)
+  lookup_hostname !Settings.hostname (string_of_int recon_port)
 
 let local_recon_addr = Utils.unit_memoize local_recon_addr
 
 let remove_self addresses =
-  List.filter ~f:(fun (addr,str) -> addr <> local_recon_addr ()) addresses
+  List.filter ~f:(fun (addr, str) -> List.mem addr (local_recon_addr()))
+    addresses
 
 let convert_address l =
-  try
-    sscanf l "%s %d"
-    (fun addr port -> Unix.ADDR_INET (lookup_hostname addr,port))
-  with
+  try
+    sscanf l "%s %s" lookup_hostname
+  with
     Scanf.Scan_failure _ | End_of_file | Failure _ -> raise (Malformed_entry l)
 
 let load_membership_file file =
   let rec loop list =
     try
       let line = decomment (input_line file) in
-      let addr = convert_address line in
-      (addr,line) :: loop list
+      let addrs = convert_address line in
+      let pairs = List.map (fun addr -> (addr,line)) addrs in
+      pairs @ loop list
     with
       | End_of_file -> list
       | Lookup_failure addr ->
@@ -101,7 +103,7 @@
 let membership_string () =
   let (mshp,_) = !membership in
   let to_string (addr,str) =
-    sprintf "%s(%s)" (sockaddr_to_string addr) str
+    sprintf "%s(%s)" (sockaddr_to_string addr.Unix.ai_addr) str
   in
   let strings = List.map ~f:to_string (Array.to_list mshp) in
   "Membership: " ^ String.concat ~sep:", " strings
@@ -149,7 +151,14 @@
 
 let same_inet_addr addr1 addr2 =
   match (addr1,addr2) with
-      (Unix.ADDR_INET (ip1,_), Unix.ADDR_INET (ip2,_)) -> ip1 = ip2
+      (Unix.ADDR_INET (ip1,_), Unix.ADDR_INET (ip2,_)) ->
+ let ipv6_addr addr =
+  let addr_str = Unix.string_of_inet_addr addr in
+  if String.contains addr_str ':'
+  then addr_str
+  else "::ffff:" ^ addr_str
+ in
+ ipv6_addr(ip1) = ipv6_addr(ip2)
     | _ -> false
 
 let test addr =
@@ -159,7 +168,7 @@
   let found = ref false in
   let i = ref 0 in
   while !i < Array.length m && not !found do
-    if same_inet_addr addr (fst m.(!i)) then
+    if same_inet_addr addr (fst m.(!i)).Unix.ai_addr then
       found := true;
     incr i
   done;
diff -r e30dc5376cbb reconCS.ml
--- a/reconCS.ml Fri May 23 21:16:40 2008 -0400
+++ b/reconCS.ml Mon Mar 02 12:48:35 2009 +0000
@@ -132,21 +132,21 @@
 let connect tree ~filters ~partner ~self =
   (* TODO: change the following to depend on the address type *)
   let s = Unix.socket
-    ~domain:Unix.PF_INET
-    ~kind:Unix.SOCK_STREAM
-    ~protocol:0
+    ~domain:partner.Unix.ai_family
+    ~kind:partner.Unix.ai_socktype
+    ~protocol:partner.Unix.ai_protocol
   in
   let run () =
     Unix.bind s ~addr:(get_client_recon_addr ());
-    Unix.connect s ~addr:partner;
+    Unix.connect s ~addr:partner.Unix.ai_addr;
     let cin = Channel.sys_in_from_fd s
     and cout = Channel.sys_out_from_fd s in
     plerror 4 "Initiating reconciliation";
-    let remote_config = handle_config cin cout filters partner in
+    let remote_config = handle_config cin cout filters partner.Unix.ai_addr in
     ignore (Unix.alarm !Settings.reconciliation_timeout);
 
     let http_port = config_get_http_port remote_config in
-    let remote_http_address = change_port partner http_port in
+    let remote_http_address = change_port partner.Unix.ai_addr http_port in
 
     let data = Server.handle tree cin cout in
     (data,remote_http_address)
diff -r e30dc5376cbb reconComm.ml
--- a/reconComm.ml Fri May 23 21:16:40 2008 -0400
+++ b/reconComm.ml Mon Mar 02 12:48:35 2009 +0000
@@ -65,13 +65,42 @@
   with
       Not_found -> false
 
+(** Get the protocol family needed for the use of an address *)
+let get_addr_pf addr =
+  match addr with
+    Unix.ADDR_INET(ip, _) ->
+      if String.contains (Unix.string_of_inet_addr ip) ':'
+      then Unix.PF_INET6
+      else Unix.PF_INET
+  | Unix.ADDR_UNIX(_) -> Unix.PF_UNIX
+
+(** Given two protocol families, guess a minimal compatible family *)
+let get_compat_pf addr1 addr2 =
+  match (get_addr_pf addr1, get_addr_pf addr2) with
+    (Unix.PF_INET6, _) | (_, Unix.PF_INET6) -> Unix.PF_INET6
+  | (pf, _) -> pf
+
+(** Map an IP address to IPv6 if protocol family is PF_INET6 *)
+let map_addr pf addr =
+  match (pf, addr) with
+    (Unix.PF_INET6, Unix.ADDR_INET(ip, port)) ->
+      let addr_str = Unix.string_of_inet_addr ip in
+      if String.contains addr_str ':'
+      then addr
+      else Unix.ADDR_INET(Unix.inet_addr_of_string ("::ffff:" ^ addr_str), port)
+  | _ -> addr
+
 let get_keystrings_via_http addr hashes =
+  let recon_addr = get_client_recon_addr() in
+  let pf = get_compat_pf addr recon_addr in
+  let addr = map_addr pf addr in
+  let recon_addr = map_addr pf recon_addr in
   let s = Unix.socket
-    ~domain:Unix.PF_INET
+    ~domain:pf
     ~kind:Unix.SOCK_STREAM
     ~protocol:0  in
   protect ~f:(fun () ->
- Unix.bind s ~addr:(get_client_recon_addr ());
+ Unix.bind s ~addr:recon_addr;
  Unix.connect s ~addr;
  let cin = Channel.sys_in_from_fd s
  and cout = Channel.sys_out_from_fd s in
diff -r e30dc5376cbb reconserver.ml
--- a/reconserver.ml Fri May 23 21:16:40 2008 -0400
+++ b/reconserver.ml Mon Mar 02 12:48:35 2009 +0000
@@ -211,7 +211,7 @@
     else
       begin
  let partner = choose_partner () in
- plerror 4 "Recon partner: %s" (sockaddr_to_string partner);
+ plerror 4 "Recon partner: %s" (sockaddr_to_string partner.Unix.ai_addr);
  let filters = get_filters () in
  let (results,http_addr) =
   ReconCS.connect (get_ptree ()) ~filters ~partner

_______________________________________________
Sks-devel mailing list
[hidden email]
http://lists.nongnu.org/mailman/listinfo/sks-devel
Reply | Threaded
Open this post in threaded view
|

Re: Patch (partial): IPv6 support (in progress)

Phil Pennock-17
On 2009-03-02 at 12:55 +0000, Kim Minh Kaplan wrote:
> it is funny that you send this as I was just doing something very
> similar this weekend.  Here is what I did.  I am still experimenting
> with what actually works, what does not and trying to find what I broke.
> I'll have a look at the way you did it when I have some time, probably
> wednesday.

You're using IPv6-mapped IPv4 addresses for IPv4 handling.  This will
break on systems where this is disabled by default; eg, FreeBSD with
net.inet6.ip6.v6only set to 1, as it is by default.

To undo this, the program neets to setsockopt IPV6_V6ONLY at level
IPPROTO_IPV6.  Looking through unix.mli on my system, it looks as though
O'Caml only supports setting some common options at level SOL_SOCKET.

It seems that using !Unix.setsockopt_int you do have access to set the
option (looking at O'Caml otherlibs/unix/sockopt.c) and the value of
IPPROTO_IPV6 is fixed, but the value of IPV6_V6ONLY is, AIUI,
OS-dependent.

Then too there's the fact that I need to bind to specific IP addresses
on my system, instead of 0.0.0.0 so just switching to :: would be bad; I
use recon_address in sksconf and needed some way to set a specific IPv4
address and a specific IPv6 address, so multiple sockets it became.

-Phil

_______________________________________________
Sks-devel mailing list
[hidden email]
http://lists.nongnu.org/mailman/listinfo/sks-devel

attachment0 (169 bytes) Download Attachment
Reply | Threaded
Open this post in threaded view
|

Re: Patch (partial): IPv6 support (in progress)

Kim Minh Kaplan
In reply to this post by kmkaplan-3
I have set up a mercurial repository with patches for support of IPv6 in
SKS.  It is at http://www.kim-minh.com/hg/sks

The server keyserver.kim-minh.com is running the changeset
20: af240280a035.

Kim Minh.


_______________________________________________
Sks-devel mailing list
[hidden email]
http://lists.nongnu.org/mailman/listinfo/sks-devel
Reply | Threaded
Open this post in threaded view
|

[PATCH] IPv6 support

Phil Pennock-17
In reply to this post by Phil Pennock-17
On 2009-03-02 at 02:52 -0800, Phil Pennock wrote:

> Can those with more experience in OCaml please take a look over this and
> tell me if I'm heading down the right path?
>
> This patch theoretically adds support for IPv6 in recon; it adds the
> settings hooks for hkp too, but doesn't add anything that uses that yet.
> It should both listen on IPv6 and connect outbound too.
>
> It compiles.  It runs.  I see both listening sockets with lsof.  It
> doesn't do anything soon enough for me to see tonight how well I've
> done, as I need to go get some sleep.  I'm not sufficiently comfortable
> to leave sks-peer.spodhuis.org running this code overnight, so I've
> reverted to 1.1.0.
>
> It's my first OCaml hacking and my style sucks and I spend more time
> debugging to get it to compile than I care to think about.
>
> A critical eye and constructive comments appreciated.
>
> Oh, and I've no idea how recent support for IPv6 in OCaml is and have
> made no effort to gracefully degrade to either old OCaml environments or
> systems without IPv6 support.  First, I want to get it working for
> anyone, *then* I can worry about making it conditional.
I've now extended the code to implement IPv6 for hkp, not just recon,
and to make outbound connections fine.

People have a choice:

 * Kim's simpler patch, which relies upon the OS supporting IPv6-mapped
   IPv4 addresses; the *BSD systems disable this by default, but you can
   change this system-wide by setting sysctl net.inet6.ip6.v6only to 0.
   I believe that OCaml lacks the necessary support in its setsockopt
   shim to set this option, so it can't be worked around

 * My larger patch, which opens separate sockets for each stack and
   which adds recon6_address and hkp6_address options to sksconf if you
   want to use a binding address other than :: (the IPv6 equivalent to
   0.0.0.0).

In particular, if anyone has an IPv4-only system, I would like to know
what problems are caused by my patch, but I'll fully understand if
people don't want to try that out.  :)

I'm now running my patch live, eating my own dogfood.  I've used gpg to
get a key from the HKP port over IPv6, but not yet seen recon, in part
because my DNS didn't include an AAAA record.  I've updated my DNS and
we're now past the TTL expiry.
  $ gpg --keyserver 'hkp://[2001:980:fff:31::10]' --recv-key $keyid

I am not providing a Mercurial repository, as I'm not sufficiently
familiar with the system to assess the security risks of doing so and
don't want to leave up a daemon which I'd access so rarely.  If I used
Mercurial on a regular basis, that would be a different matter.

So, attached, please find what I currently believe to be a complete IPv6
support patch.  Unless you define "complete" to mean "can disable it".

I would have sent this mail 2 hours ago but I realised that the
membership code just taking the first address found was a bad plan, as
then there would be a 50% chance of a connection from a mutually IPv6
host failing -- if they connected over IPv4 and you only record the IPv6
address, that would be rejected.  So I've just broken my brain on
O'Caml's type system while making sure that the membership handling will
track _all_ the IP addresses of each peer.

Note that one side-effect is that in theory, with this, you could peer
with a cluster of hosts where one name has multiple IPs.  But you
wouldn't want to do that normally, especially since the "that's myself"
test will drop any item where any of the IP addresses are any of the
local recon addresses (either the one configured address or any of the
addresses for hostname).  Oh, and that would possibly not be a good plan
from the point of view of the synchronisation protocol itself.

With this latest fix, I'm not rejecting so many recon connects now.  ;)

If you try my patch out, I'd appreciate feedback.  If you're peering
with sks-peer.spodhuis.org then you might want to keep an eye on
recon.log and see if you get an IPv6 peering.  :)  But please, keep a
safe copy of your normal binary to fall back to.

Thanks,
-Phil

diff -r e30dc5376cbb common.ml
--- a/common.ml Fri May 23 21:16:40 2008 -0400
+++ b/common.ml Wed Mar 04 23:06:51 2009 -0800
@@ -199,8 +199,10 @@
 
 let recon_port = !Settings.recon_port
 let recon_address = !Settings.recon_address
+let recon6_address = !Settings.recon6_address
 let http_port = !Settings.hkp_port
 let http_address = !Settings.hkp_address
+let http6_address = !Settings.hkp6_address
 let db_command_name = Filename.concat !Settings.basedir "db_com_sock"
 let recon_command_name = Filename.concat !Settings.basedir "recon_com_sock"
 
@@ -212,7 +214,15 @@
   | Unix.ADDR_INET (inet_addr,port) -> Unix.ADDR_INET (inet_addr,port + 1)
 
 
-let get_client_recon_addr () =
-  Unix.ADDR_INET (Unix.inet_addr_of_string recon_address,0)
+let get_client_recon_addr remote_addr = match Unix.domain_of_sockaddr remote_addr with
+    Unix.PF_UNIX -> failwith "Can't connect to remote local-domain sockets"
+  | Unix.PF_INET -> Unix.ADDR_INET (Unix.inet_addr_of_string recon_address,0)
+  | Unix.PF_INET6 -> Unix.ADDR_INET (Unix.inet_addr_of_string recon6_address,0)
 let get_client_recon_addr =
-  Utils.unit_memoize get_client_recon_addr
+  Utils.memoize get_client_recon_addr
+
+let sockaddr_to_string sockaddr = match sockaddr with
+    Unix.ADDR_UNIX s -> sprintf "<ADDR_UNIX %s>" s
+  | Unix.ADDR_INET (addr,p) -> sprintf "<ADDR_INET [%s]:%d>"
+      (Unix.string_of_inet_addr addr) p
+
diff -r e30dc5376cbb dbserver.ml
--- a/dbserver.ml Fri May 23 21:16:40 2008 -0400
+++ b/dbserver.ml Wed Mar 04 23:06:51 2009 -0800
@@ -57,8 +57,10 @@
       failwith "Running sks_db without transactions is no longer supported."
 
 
-  let addr = inet_addr_of_string http_address
-  let websock = Eventloop.create_sock (ADDR_INET (addr,http_port))
+  let addr4 = inet_addr_of_string http_address
+  let addr6 = inet_addr_of_string http6_address
+  let websock4 = Eventloop.create_sock (ADDR_INET (addr4,http_port))
+  let websock6 = Eventloop.create_sock (ADDR_INET (addr6,http_port))
   let () =
     if Sys.file_exists db_command_name
     then Unix.unlink db_command_name
@@ -652,7 +654,11 @@
       )
 
       (
- (websock, Eventloop.make_th ~name:"webserver"
+ (websock4, Eventloop.make_th ~name:"webserver (IPv4)"
+  ~timeout:!Settings.wserver_timeout
+  ~cb:(Wserver.accept_connection webhandler ~recover_timeout:1))
+        ::
+        (websock6, Eventloop.make_th ~name:"webserver (IPv6)"
   ~timeout:!Settings.wserver_timeout
   ~cb:(Wserver.accept_connection webhandler ~recover_timeout:1))
  ::
@@ -661,11 +667,16 @@
     ~cb:(eventify_handler command_handler))
  ::
  (if !Settings.use_port_80 then
-    let sock = Eventloop.create_sock (ADDR_INET (addr,80)) in
-    (sock,Eventloop.make_th ~name:"webserver80"
+    let sock4 = Eventloop.create_sock (ADDR_INET (addr4,80)) in
+    let sock6 = Eventloop.create_sock (ADDR_INET (addr6,80)) in
+    (sock4,Eventloop.make_th ~name:"webserver80 (IPv4)"
        ~timeout:!Settings.wserver_timeout
-       ~cb:(Wserver.accept_connection webhandler ~recover_timeout:1)
-    )::[]
+       ~cb:(Wserver.accept_connection webhandler ~recover_timeout:1))
+            ::
+    (sock6,Eventloop.make_th ~name:"webserver80 (IPv6)"
+       ~timeout:!Settings.wserver_timeout
+       ~cb:(Wserver.accept_connection webhandler ~recover_timeout:1))
+            ::[]
   else
     []
  )
diff -r e30dc5376cbb eventloop.ml
--- a/eventloop.ml Fri May 23 21:16:40 2008 -0400
+++ b/eventloop.ml Wed Mar 04 23:06:51 2009 -0800
@@ -121,10 +121,7 @@
 
 let create_sock addr =
   try
-    let domain =
-      match addr with
-  ADDR_UNIX _ -> PF_UNIX
- | ADDR_INET (_,_) -> PF_INET in
+    let domain = Unix.domain_of_sockaddr addr in
     let sock =
       socket ~domain ~kind:SOCK_STREAM ~protocol:0 in
     setsockopt sock SO_REUSEADDR true;
@@ -132,8 +129,10 @@
     listen sock ~max:20;
     sock
   with
-    | Unix_error (_,"bind",_) ->
- failwith "Failure while binding socket.  Probably another socket bound to this address"
+    | Unix_error (error, "bind",_) ->
+        let msg = error_message error in
+        let addrstr = sockaddr_to_string addr in
+ failwith (sprintf "Failure binding %s: %s" addrstr msg)
     | e -> raise e
 let add_events heap evlist =
   List.iter ~f:(fun (Event (time, callback)) ->
diff -r e30dc5376cbb membership.ml
--- a/membership.ml Fri May 23 21:16:40 2008 -0400
+++ b/membership.ml Wed Mar 04 23:06:51 2009 -0800
@@ -29,38 +29,112 @@
 exception Bug of string
 exception Lookup_failure of string
 exception Malformed_entry of string
+exception Empty_line
 
 let membership = ref ([| |],-1.)
 
 let whitespace = Str.regexp "[ \t]+"
 
-let lookup_hostname string =
-  try (Unix.gethostbyname string).Unix.h_addr_list.(0)
-  with
+(* Although the OCaml standard Unix library does not include AF_UNSPEC, the
+ * underlying C implementation uses AF_UNSPEC unless overriden (ie, there's
+ * no way to specify a value which matches the default behaviour).
+ * So for IPv4/IPv6 functionality, use getaddrinfo without an AI_FAMILY
+ * parameter.
+ * This does mean that we need a separate function to filter though.
+ *)
+
+let inet_addr_of_sockaddr sock = match sock with
+    Unix.ADDR_UNIX _ -> raise (Bug "asking for inet addr of local domain socket")
+  | Unix.ADDR_INET (addr, port) -> addr
+
+let inet_addr_of_addr_info ai = match ai.Unix.ai_addr with
+    Unix.ADDR_UNIX _ -> raise (Bug "converting getaddrinfo result which is local domain?")
+  | Unix.ADDR_INET (addr, port) -> addr
+
+let lookup_hostname_as_ai_list ?(port = recon_port) string =
+  let portstr = string_of_int port in
+  try begin
+    let reslist = Unix.getaddrinfo string portstr [Unix.AI_SOCKTYPE Unix.SOCK_STREAM] in
+    if List.length reslist == 0 then raise (Lookup_failure string)
+    else List.map (fun (a) -> a.Unix.ai_addr) reslist
+  end
+  with
     | Invalid_argument _ | Not_found -> raise (Lookup_failure string)
 
-let local_recon_addr () =
-  Unix.ADDR_INET (lookup_hostname !Settings.hostname, recon_port)
+let lookup_hostname_list_filtered ~family ?(port = recon_port) string =
+  let portstr = string_of_int port in
+  let filter = (fun ai -> if ai.Unix.ai_family == family then true else false) in
+  try begin
+    let reslist = Unix.getaddrinfo string portstr [Unix.AI_SOCKTYPE Unix.SOCK_STREAM] in
+    if List.length reslist == 0 then raise (Lookup_failure string)
+    else List.map inet_addr_of_addr_info (List.filter filter reslist)
+  end
+  with
+    | Invalid_argument _ | Not_found -> raise (Lookup_failure string)
 
-let local_recon_addr = Utils.unit_memoize local_recon_addr
+let my_ipv4_recon_addresses () =
+  let r = Unix.inet_addr_of_string !Settings.recon_address in
+  if r = Unix.inet_addr_any
+  then lookup_hostname_list_filtered ~family:Unix.PF_INET !Settings.hostname
+  else [r]
+let my_ipv4_recon_addresses = Utils.unit_memoize my_ipv4_recon_addresses
 
-let remove_self addresses =
-  List.filter ~f:(fun (addr,str) -> addr <> local_recon_addr ()) addresses
+let my_ipv6_recon_addresses () =
+  let r = Unix.inet_addr_of_string !Settings.recon6_address in
+  if r = Unix.inet6_addr_any
+  then lookup_hostname_list_filtered ~family:Unix.PF_INET6 !Settings.hostname
+  else [r]
+let my_ipv6_recon_addresses = Utils.unit_memoize my_ipv6_recon_addresses
 
-let convert_address l =
+let is_local_recon_addr peer = match Unix.domain_of_sockaddr peer with
+    Unix.PF_UNIX -> raise (Bug "trying to find local address of remote local domain socket")
+  | Unix.PF_INET -> List.exists (fun a -> (inet_addr_of_sockaddr peer) = a) (my_ipv4_recon_addresses ())
+  | Unix.PF_INET6 -> List.exists (fun a -> (inet_addr_of_sockaddr peer) = a) (my_ipv6_recon_addresses ())
+
+let is_local_recon_addr = Utils.memoize is_local_recon_addr
+
+(* We have to support storing all INET_ADDR items for a given peer, so that
+ * membership tests will accept connections from both of a peer's addresses,
+ * instead of accepting only one, vaguely randomly.
+ *
+ * So while previously we stored only one IP address for a peer, we now record
+ * all IP addresses, both v4 and v6, which also lets a peer be multi-addressed
+ * for resilient service.  Bonus!
+ *
+ * The internal membership storage is only accessed within membership.ml, so
+ * we can change the storage without changing the API.  Thus we just replace
+ * storing "an address" with "a list of addresses" and update the test
+ * interfaces too.
+ *)
+
+let convert_hostname_to_sockaddrs l =
   try
-    sscanf l "%s %d"
-    (fun addr port -> Unix.ADDR_INET (lookup_hostname addr,port))
+    if String.length l == 0 then raise Empty_line
+    else
+      sscanf l "%s %d"
+      (fun addr port -> lookup_hostname_as_ai_list ~port:port addr)
   with
     Scanf.Scan_failure _ | End_of_file | Failure _ -> raise (Malformed_entry l)
 
+(* Note that we filter out any peers where any of our own IPs is one of the
+ * IPs in DNS for the peer, so if you're running a cluster of servers you'll
+ * internally need to use split hostnames for each instance in your membership.
+ *)
+
+let remove_self membership_items =
+  let check_list addr_list =
+    not (List.exists ~f:is_local_recon_addr addr_list)
+  in
+  List.filter ~f:(fun (addrlist, str) -> check_list addrlist) membership_items
+
 let load_membership_file file =
   let rec loop list =
     try
       let line = decomment (input_line file) in
-      let addr = convert_address line in
-      (addr,line) :: loop list
+      let sockaddr_list = convert_hostname_to_sockaddrs line in
+      (sockaddr_list,line) :: loop list
     with
+      | Empty_line -> loop list
       | End_of_file -> list
       | Lookup_failure addr ->
   perror "Lookup failure on address %s" addr;
@@ -93,15 +167,13 @@
      )
     ~finally:(fun () -> close_in file)
 
-let sockaddr_to_string sockaddr = match sockaddr with
-    Unix.ADDR_UNIX s -> sprintf "<ADDR_UNIX %s>" s
-  | Unix.ADDR_INET (addr,p) -> sprintf "<ADDR_INET %s:%d>"
-      (Unix.string_of_inet_addr addr) p
-
 let membership_string () =
   let (mshp,_) = !membership in
-  let to_string (addr,str) =
-    sprintf "%s(%s)" (sockaddr_to_string addr) str
+  let to_string (addr_list,str) =
+    String.concat " " (
+      "(" :: (
+      List.map (fun (a) -> (sockaddr_to_string a)) addr_list
+      ) @ str :: ")" :: [] )
   in
   let strings = List.map ~f:to_string (Array.to_list mshp) in
   "Membership: " ^ String.concat ~sep:", " strings
@@ -147,19 +219,25 @@
   in
   Array.map ~f:fst mshp
 
-let same_inet_addr addr1 addr2 =
-  match (addr1,addr2) with
-      (Unix.ADDR_INET (ip1,_), Unix.ADDR_INET (ip2,_)) -> ip1 = ip2
-    | _ -> false
+(* Used to test if a peer is in our membership file.
+ * When this was IPv4-only, it sufficed to test a single address against
+ * a single address.  Now, we want to check for a non-empty intersection
+ * of two sets of addresses.  We don't use real Set stuff because the
+ * total number of addresses is expected to be small, typically 1 or 2.
+ *)
+
+let ipmatch_ip_in_socklist ip1 sockaddr_list =
+  List.exists ~f:(fun (a2) -> ip1 = (inet_addr_of_sockaddr a2)) sockaddr_list
 
 let test addr =
   reload_if_changed ();
   let (m,mtime) = !membership in
+  let ip = inet_addr_of_sockaddr addr in
   
   let found = ref false in
   let i = ref 0 in
   while !i < Array.length m && not !found do
-    if same_inet_addr addr (fst m.(!i)) then
+    if ipmatch_ip_in_socklist ip (fst m.(!i)) then
       found := true;
     incr i
   done;
diff -r e30dc5376cbb reconCS.ml
--- a/reconCS.ml Fri May 23 21:16:40 2008 -0400
+++ b/reconCS.ml Wed Mar 04 23:06:51 2009 -0800
@@ -129,15 +129,17 @@
 
 
 (** function to connect to remote host to initate reconciliation *)
-let connect tree ~filters ~partner ~self =
+let connect tree ~filters ~partner =
   (* TODO: change the following to depend on the address type *)
+  let peer_addr = get_client_recon_addr partner in
+  let sock_domain = Unix.domain_of_sockaddr partner in
   let s = Unix.socket
-    ~domain:Unix.PF_INET
-    ~kind:Unix.SOCK_STREAM
-    ~protocol:0
+    ~domain:sock_domain
+    ~kind:Unix.SOCK_STREAM
+    ~protocol:0
   in
   let run () =
-    Unix.bind s ~addr:(get_client_recon_addr ());
+    Unix.bind s ~addr:peer_addr;
     Unix.connect s ~addr:partner;
     let cin = Channel.sys_in_from_fd s
     and cout = Channel.sys_out_from_fd s in
diff -r e30dc5376cbb reconComm.ml
--- a/reconComm.ml Fri May 23 21:16:40 2008 -0400
+++ b/reconComm.ml Wed Mar 04 23:06:51 2009 -0800
@@ -67,11 +67,11 @@
 
 let get_keystrings_via_http addr hashes =
   let s = Unix.socket
-    ~domain:Unix.PF_INET
+    ~domain:(Unix.domain_of_sockaddr addr)
     ~kind:Unix.SOCK_STREAM
     ~protocol:0  in
   protect ~f:(fun () ->
- Unix.bind s ~addr:(get_client_recon_addr ());
+ Unix.bind s ~addr:(get_client_recon_addr addr);
  Unix.connect s ~addr;
  let cin = Channel.sys_in_from_fd s
  and cout = Channel.sys_out_from_fd s in
diff -r e30dc5376cbb reconserver.ml
--- a/reconserver.ml Fri May 23 21:16:40 2008 -0400
+++ b/reconserver.ml Wed Mar 04 23:06:51 2009 -0800
@@ -50,7 +50,9 @@
   (******************************************************************)
 
   let recon_addr = Unix.ADDR_INET (Unix.inet_addr_of_string recon_address,recon_port)
+  let recon6_addr = Unix.ADDR_INET (Unix.inet_addr_of_string recon6_address,recon_port)
   let reconsock = Eventloop.create_sock recon_addr
+  let recon6sock = Eventloop.create_sock recon6_addr
 
   let () =
     if Sys.file_exists recon_command_name
@@ -78,9 +80,13 @@
     if Array.length array = 0 then raise Not_found
     else array.(Random.int (Array.length array))
 
+  let choose_addr list =
+    if List.length list = 0 then raise Not_found
+    else List.nth list (Random.int (List.length list))
+
   let choose_partner () =
     try
-      choose (Membership.get ())
+      choose_addr (choose (Membership.get ()))
     with
  Not_found | Invalid_argument _ ->
   failwith "No gossip partners available"
@@ -215,7 +221,6 @@
  let filters = get_filters () in
  let (results,http_addr) =
   ReconCS.connect (get_ptree ()) ~filters ~partner
-    ~self:Membership.local_recon_addr
  in
  let results = ZSet.elements results in
  plerror 4 "Reconciliation complete";
@@ -360,7 +365,12 @@
    ~timeout:!Settings.command_timeout
  );
  (reconsock, Eventloop.make_th
-   ~name:"reconciliation handler"
+   ~name:"reconciliation handler (IPv4)"
+   ~cb:recon_handler
+   ~timeout:!Settings.reconciliation_config_timeout
+ );
+ (recon6sock, Eventloop.make_th
+   ~name:"reconciliation handler (IPv6)"
    ~cb:recon_handler
    ~timeout:!Settings.reconciliation_config_timeout
  );
diff -r e30dc5376cbb settings.ml
--- a/settings.ml Fri May 23 21:16:40 2008 -0400
+++ b/settings.ml Wed Mar 04 23:06:51 2009 -0800
@@ -56,12 +56,16 @@
   seed := value
 
 let recon_port = ref 11370
-let recon_address = ref "0.0.0.0"
+let recon_address = ref (Unix.string_of_inet_addr Unix.inet_addr_any)
 let set_recon_address value = recon_address := value
+let recon6_address = ref (Unix.string_of_inet_addr Unix.inet6_addr_any)
+let set_recon6_address value = recon6_address := value
 
 let hkp_port = ref 11371
-let hkp_address = ref "0.0.0.0"
+let hkp_address = ref (Unix.string_of_inet_addr Unix.inet_addr_any)
 let set_hkp_address value = hkp_address := value
+let hkp6_address = ref (Unix.string_of_inet_addr Unix.inet6_addr_any)
+let set_hkp6_address value = hkp6_address := value
 
 let use_port_80 = ref false
 
@@ -247,9 +251,11 @@
     ("-baseport",Arg.Int set_base_port, " Set base port number");
     ("-logfile",Arg.String (fun _ -> ()), " DEPRECATED.  Now ignored.");
     ("-recon_port",Arg.Int set_recon_port, " Set recon port number");
-    ("-recon_address",Arg.String set_recon_address, " Set recon binding address");
+    ("-recon_address",Arg.String set_recon_address, " Set recon binding address (IPv4)");
+    ("-recon6_address",Arg.String set_recon6_address, " Set recon binding address (IPv6)");
     ("-hkp_port",Arg.Int set_hkp_port, " Set hkp port number");
-    ("-hkp_address",Arg.String set_hkp_address, " Set hkp binding address");
+    ("-hkp_address",Arg.String set_hkp_address, " Set hkp binding address (IPv4)");
+    ("-hkp6_address",Arg.String set_hkp6_address, " Set hkp binding address (IPv6)");
     ("-use_port_80",Arg.Set use_port_80,
      " Have the HKP interface listen on port 80, as well as the hkp_port");
     ("-basedir", Arg.Set_string basedir, " Base directory");
diff -r e30dc5376cbb sks.pod
--- a/sks.pod Fri May 23 21:16:40 2008 -0400
+++ b/sks.pod Wed Mar 04 23:06:51 2009 -0800
@@ -152,7 +152,11 @@
 
 =item -recon_address
 
-Set recon binding address.
+Set recon binding IPv4 address.  Also used for outbound connections.
+
+=item -recon6_address
+
+Set recon binding IPv6 address.  Also used for outbound connections.
 
 =item -hkp_port
 
@@ -160,7 +164,11 @@
 
 =item -hkp_address
 
-Set hkp binding address.
+Set hkp binding IPv4 address.
+
+=item -hkp6_address
+
+Set hkp binding IPv6 address.
 
 =item -use_port_80
 

_______________________________________________
Sks-devel mailing list
[hidden email]
http://lists.nongnu.org/mailman/listinfo/sks-devel

attachment0 (169 bytes) Download Attachment
Reply | Threaded
Open this post in threaded view
|

Re: [PATCH] IPv6 support

Phil Pennock-17
On 2009-03-04 at 23:24 -0800, Phil Pennock wrote:
> If you try my patch out, I'd appreciate feedback.  If you're peering
> with sks-peer.spodhuis.org then you might want to keep an eye on
> recon.log and see if you get an IPv6 peering.  :)  But please, keep a
> safe copy of your normal binary to fall back to.

Sorry to follow up to myself, but I'll note one design decision I forgot
to mention:

sks does random peering already.  When choosing an IP address from those
published by a hostname, I could choose to refactor the code to fallback
across IPs, or I could just make things a bit more random and randomly
choose one of the available IPs.

I chose to add the extra randomness.

Rationale:
 * the random connects should still let the peering mesh work
 * most people with IPv6 service will probably choose to move to IPv6
   recon within a short enough period of time that the gain would be
   short-lived
 * if someone really doesn't want to peer over IPv6, then either:
   (a) they're IPv4-only, so this doesn't affect them at all
   or
   (b) they should ask their peers to use a hostname which doesn't have
       AAAA records in membership files

Just be aware that you'll see more of your outbound connections failing,
if the remote side uses a hostname with AAAA records but no AAAA recon.

Note that a web-server proxying AAAA 11370 connections onto sks does not
work and will not work and never would have worked -- the code for
checking the hostname of a peer would have seen a connection from
localhost, and since you're not peering with localhost (I assume), this
would have failed the membership test.  A proxy will only ever be a
work-around for the HKP port.

Regards,
-Phil

_______________________________________________
Sks-devel mailing list
[hidden email]
http://lists.nongnu.org/mailman/listinfo/sks-devel

attachment0 (169 bytes) Download Attachment
Reply | Threaded
Open this post in threaded view
|

Re: [PATCH] IPv6 support

Yaron Minsky
In reply to this post by Phil Pennock-17
I'm really happy to see these changes being tried out.  I'll read over the diff today and try to get you some feedback.

y

On Thu, Mar 5, 2009 at 2:24 AM, Phil Pennock <[hidden email]> wrote:
On 2009-03-02 at 02:52 -0800, Phil Pennock wrote:
> Can those with more experience in OCaml please take a look over this and
> tell me if I'm heading down the right path?
>
> This patch theoretically adds support for IPv6 in recon; it adds the
> settings hooks for hkp too, but doesn't add anything that uses that yet.
> It should both listen on IPv6 and connect outbound too.
>
> It compiles.  It runs.  I see both listening sockets with lsof.  It
> doesn't do anything soon enough for me to see tonight how well I've
> done, as I need to go get some sleep.  I'm not sufficiently comfortable
> to leave sks-peer.spodhuis.org running this code overnight, so I've
> reverted to 1.1.0.
>
> It's my first OCaml hacking and my style sucks and I spend more time
> debugging to get it to compile than I care to think about.
>
> A critical eye and constructive comments appreciated.
>
> Oh, and I've no idea how recent support for IPv6 in OCaml is and have
> made no effort to gracefully degrade to either old OCaml environments or
> systems without IPv6 support.  First, I want to get it working for
> anyone, *then* I can worry about making it conditional.

I've now extended the code to implement IPv6 for hkp, not just recon,
and to make outbound connections fine.

People have a choice:

 * Kim's simpler patch, which relies upon the OS supporting IPv6-mapped
  IPv4 addresses; the *BSD systems disable this by default, but you can
  change this system-wide by setting sysctl net.inet6.ip6.v6only to 0.
  I believe that OCaml lacks the necessary support in its setsockopt
  shim to set this option, so it can't be worked around

 * My larger patch, which opens separate sockets for each stack and
  which adds recon6_address and hkp6_address options to sksconf if you
  want to use a binding address other than :: (the IPv6 equivalent to
  0.0.0.0).

In particular, if anyone has an IPv4-only system, I would like to know
what problems are caused by my patch, but I'll fully understand if
people don't want to try that out.  :)

I'm now running my patch live, eating my own dogfood.  I've used gpg to
get a key from the HKP port over IPv6, but not yet seen recon, in part
because my DNS didn't include an AAAA record.  I've updated my DNS and
we're now past the TTL expiry.
 $ gpg --keyserver 'hkp://[2001:980:fff:31::10]' --recv-key $keyid

I am not providing a Mercurial repository, as I'm not sufficiently
familiar with the system to assess the security risks of doing so and
don't want to leave up a daemon which I'd access so rarely.  If I used
Mercurial on a regular basis, that would be a different matter.

So, attached, please find what I currently believe to be a complete IPv6
support patch.  Unless you define "complete" to mean "can disable it".

I would have sent this mail 2 hours ago but I realised that the
membership code just taking the first address found was a bad plan, as
then there would be a 50% chance of a connection from a mutually IPv6
host failing -- if they connected over IPv4 and you only record the IPv6
address, that would be rejected.  So I've just broken my brain on
O'Caml's type system while making sure that the membership handling will
track _all_ the IP addresses of each peer.

Note that one side-effect is that in theory, with this, you could peer
with a cluster of hosts where one name has multiple IPs.  But you
wouldn't want to do that normally, especially since the "that's myself"
test will drop any item where any of the IP addresses are any of the
local recon addresses (either the one configured address or any of the
addresses for hostname).  Oh, and that would possibly not be a good plan
from the point of view of the synchronisation protocol itself.

With this latest fix, I'm not rejecting so many recon connects now.  ;)

If you try my patch out, I'd appreciate feedback.  If you're peering
with sks-peer.spodhuis.org then you might want to keep an eye on
recon.log and see if you get an IPv6 peering.  :)  But please, keep a
safe copy of your normal binary to fall back to.

Thanks,
-Phil

_______________________________________________
Sks-devel mailing list
[hidden email]
http://lists.nongnu.org/mailman/listinfo/sks-devel



_______________________________________________
Sks-devel mailing list
[hidden email]
http://lists.nongnu.org/mailman/listinfo/sks-devel
Reply | Threaded
Open this post in threaded view
|

Re: [PATCH] IPv6 support

Yaron Minsky
I've managed to do a quick look-over the patch.  It's pretty good for a first foray into OCaml.  I did some surface cleanups of the code, but I haven't yet thought carefully about the behavior of the new code.

I've attached a cleaned up version of your patch.  It compiles but is otherwise untested.  The main changes I made were:
  • Cleaned up the way that IPv4 and IPv6 logic was chosen in the code so as to reduce boilerplate
  • Got rid of incorrect use of ==.  In OCaml, == is physical equality, whereas = is ordinary structural equality.
  • streamlined some repetitive code in membership.ml
  • other minor tweaks
  • Defined the |! operator (used for creating function pipelines) and used that to make the code more readable in a few spots.
I'd be happy for feedback on the modified patch.

y

On Thu, Mar 5, 2009 at 7:33 AM, Yaron Minsky <[hidden email]> wrote:
I'm really happy to see these changes being tried out.  I'll read over the diff today and try to get you some feedback.

y

On Thu, Mar 5, 2009 at 2:24 AM, Phil Pennock <[hidden email]> wrote:
On 2009-03-02 at 02:52 -0800, Phil Pennock wrote:
> Can those with more experience in OCaml please take a look over this and
> tell me if I'm heading down the right path?
>
> This patch theoretically adds support for IPv6 in recon; it adds the
> settings hooks for hkp too, but doesn't add anything that uses that yet.
> It should both listen on IPv6 and connect outbound too.
>
> It compiles.  It runs.  I see both listening sockets with lsof.  It
> doesn't do anything soon enough for me to see tonight how well I've
> done, as I need to go get some sleep.  I'm not sufficiently comfortable
> to leave sks-peer.spodhuis.org running this code overnight, so I've
> reverted to 1.1.0.
>
> It's my first OCaml hacking and my style sucks and I spend more time
> debugging to get it to compile than I care to think about.
>
> A critical eye and constructive comments appreciated.
>
> Oh, and I've no idea how recent support for IPv6 in OCaml is and have
> made no effort to gracefully degrade to either old OCaml environments or
> systems without IPv6 support.  First, I want to get it working for
> anyone, *then* I can worry about making it conditional.

I've now extended the code to implement IPv6 for hkp, not just recon,
and to make outbound connections fine.

People have a choice:

 * Kim's simpler patch, which relies upon the OS supporting IPv6-mapped
  IPv4 addresses; the *BSD systems disable this by default, but you can
  change this system-wide by setting sysctl net.inet6.ip6.v6only to 0.
  I believe that OCaml lacks the necessary support in its setsockopt
  shim to set this option, so it can't be worked around

 * My larger patch, which opens separate sockets for each stack and
  which adds recon6_address and hkp6_address options to sksconf if you
  want to use a binding address other than :: (the IPv6 equivalent to
  0.0.0.0).

In particular, if anyone has an IPv4-only system, I would like to know
what problems are caused by my patch, but I'll fully understand if
people don't want to try that out.  :)

I'm now running my patch live, eating my own dogfood.  I've used gpg to
get a key from the HKP port over IPv6, but not yet seen recon, in part
because my DNS didn't include an AAAA record.  I've updated my DNS and
we're now past the TTL expiry.
 $ gpg --keyserver 'hkp://[2001:980:fff:31::10]' --recv-key $keyid

I am not providing a Mercurial repository, as I'm not sufficiently
familiar with the system to assess the security risks of doing so and
don't want to leave up a daemon which I'd access so rarely.  If I used
Mercurial on a regular basis, that would be a different matter.

So, attached, please find what I currently believe to be a complete IPv6
support patch.  Unless you define "complete" to mean "can disable it".

I would have sent this mail 2 hours ago but I realised that the
membership code just taking the first address found was a bad plan, as
then there would be a 50% chance of a connection from a mutually IPv6
host failing -- if they connected over IPv4 and you only record the IPv6
address, that would be rejected.  So I've just broken my brain on
O'Caml's type system while making sure that the membership handling will
track _all_ the IP addresses of each peer.

Note that one side-effect is that in theory, with this, you could peer
with a cluster of hosts where one name has multiple IPs.  But you
wouldn't want to do that normally, especially since the "that's myself"
test will drop any item where any of the IP addresses are any of the
local recon addresses (either the one configured address or any of the
addresses for hostname).  Oh, and that would possibly not be a good plan
from the point of view of the synchronisation protocol itself.

With this latest fix, I'm not rejecting so many recon connects now.  ;)

If you try my patch out, I'd appreciate feedback.  If you're peering
with sks-peer.spodhuis.org then you might want to keep an eye on
recon.log and see if you get an IPv6 peering.  :)  But please, keep a
safe copy of your normal binary to fall back to.

Thanks,
-Phil

_______________________________________________
Sks-devel mailing list
[hidden email]
http://lists.nongnu.org/mailman/listinfo/sks-devel




_______________________________________________
Sks-devel mailing list
[hidden email]
http://lists.nongnu.org/mailman/listinfo/sks-devel

sks-ipv6-v3.diff (24K) Download Attachment
Reply | Threaded
Open this post in threaded view
|

Re: [PATCH] IPv6 support

Phil Pennock-17
On 2009-03-06 at 23:06 -0500, Yaron Minsky wrote:
>    - Cleaned up the way that IPv4 and IPv6 logic was chosen in the code so
>    as to reduce boilerplate

"let foo = bar in" in the middle of a list.  I have a lot to learn.  ;)

>    - Got rid of incorrect use of ==.  In OCaml, == is physical equality,
>    whereas = is ordinary structural equality.

I discovered this at some point while writing the patch and thought I'd
cleaned things up; I think I left == only on things like character
tests, for which pervasives.mli documented == as identitcal to =.  I
guess it's better to just get in the habit of always using =, since they
are identical.

>    - streamlined some repetitive code in membership.ml

I had somehow missed 'a option and Some; I just had an O'Caml-speaker
explain it to me.  *sigh*  I so wish I'd known about Some at the time.

>    - other minor tweaks
>    - Defined the |! operator (used for creating function pipelines) and used
>    that to make the code more readable in a few spots.

Nice.

So, is there an easy way to detect from OCaml if there's a routable IPv6
service available, or should we provide knobs to disable IPv4 or IPv6?

In C, I'd use
  addrinfo_obj.ai_flags |= AI_ADDRCONFIG
for the getaddrinfo() call, so that addresses are only returned for
those address families for which such an address is configured on the
local system.  I couldn't see a way to do that in O'Caml.  I'm running
3.10.2 (FreeBSD port, ocaml-notk-3.10.2_1) and the socket handling in
unix.mli leaves somewhat to be desired, both in setsockopt for anything
other than socket-level options and flags for some of the newer
interfaces such as getaddrinfo().  :-/

-Phil

_______________________________________________
Sks-devel mailing list
[hidden email]
http://lists.nongnu.org/mailman/listinfo/sks-devel

attachment0 (169 bytes) Download Attachment
Reply | Threaded
Open this post in threaded view
|

Re: [PATCH] IPv6 support

Yaron Minsky
On Sat, Mar 7, 2009 at 2:53 AM, Phil Pennock <[hidden email]> wrote:
On 2009-03-06 at 23:06 -0500, Yaron Minsky wrote:
>    - Cleaned up the way that IPv4 and IPv6 logic was chosen in the code so
>    as to reduce boilerplate

"let foo = bar in" in the middle of a list.  I have a lot to learn.  ;)

Yup.  Expressions are lurking everywhere....
 

>    - Got rid of incorrect use of ==.  In OCaml, == is physical equality,
>    whereas = is ordinary structural equality.

I discovered this at some point while writing the patch and thought I'd
cleaned things up; I think I left == only on things like character
tests, for which pervasives.mli documented == as identitcal to =.  I
guess it's better to just get in the habit of always using =, since they
are identical.

Exactly.  You should pretty much only use == when there's a reason, that is, specifically when the semantics are different.
 

>    - streamlined some repetitive code in membership.ml

I had somehow missed 'a option and Some; I just had an O'Caml-speaker
explain it to me.  *sigh*  I so wish I'd known about Some at the time.

Yeah, options are fantastic.  Much much cleaner and more explicit than sprinkling nulls around everywhere, which is what you need to do in most languages.
 

>    - other minor tweaks
>    - Defined the |! operator (used for creating function pipelines) and used
>    that to make the code more readable in a few spots.

Nice.

So, is there an easy way to detect from OCaml if there's a routable IPv6
service available, or should we provide knobs to disable IPv4 or IPv6?

In C, I'd use
 addrinfo_obj.ai_flags |= AI_ADDRCONFIG
for the getaddrinfo() call, so that addresses are only returned for
those address families for which such an address is configured on the
local system.  I couldn't see a way to do that in O'Caml.  I'm running
3.10.2 (FreeBSD port, ocaml-notk-3.10.2_1) and the socket handling in
unix.mli leaves somewhat to be desired, both in setsockopt for anything
other than socket-level options and flags for some of the newer
interfaces such as getaddrinfo().  :-/

This is the part I need to think about.  I actually know next to nothing about IPv6.  I really only looked at the code for very broad readability issues.

You should take a look at 3.11 and see if it fixes any of your issues.  That's what I"m currently compiling against.

If we really need to, we can write some C code to set the socket options and then wrap that up so we can call it from OCaml.

y
 

-Phil

_______________________________________________
Sks-devel mailing list
[hidden email]
http://lists.nongnu.org/mailman/listinfo/sks-devel



_______________________________________________
Sks-devel mailing list
[hidden email]
http://lists.nongnu.org/mailman/listinfo/sks-devel
Reply | Threaded
Open this post in threaded view
|

Re: [PATCH] IPv6 support

Kim Minh Kaplan
In reply to this post by Phil Pennock-17
Phil Pennock:

>  * My larger patch, which opens separate sockets for each stack and
>    which adds recon6_address and hkp6_address options to sksconf if you
>    want to use a binding address other than :: (the IPv6 equivalent to
>    0.0.0.0).

I think this is the right approach except that instead of having
separate settings for IPv4 and IPv6 recon_address and hkp_address should
be extended to accept an unlimited number of names.  The db and recon
server would then bind all associated addresses.

Regarding your code it looks fine to me except that the membership part
seems overcomplicated for what it does, but I read Yaron Minsky did some
rewriting on it.

Oh, just a little nitpick: when you want to check if a list is empty
compare it to [].  The List.length function walks the whole list.

Kim Minh.


_______________________________________________
Sks-devel mailing list
[hidden email]
http://lists.nongnu.org/mailman/listinfo/sks-devel
Reply | Threaded
Open this post in threaded view
|

Re: [PATCH] IPv6 support

Phil Pennock-17
In reply to this post by Yaron Minsky
On 2009-03-07 at 12:53 -0500, Yaron Minsky wrote:

> On Sat, Mar 7, 2009 at 2:53 AM, Phil Pennock <[hidden email]>wrote:
> > So, is there an easy way to detect from OCaml if there's a routable IPv6
> > service available, or should we provide knobs to disable IPv4 or IPv6?
> >
> > In C, I'd use
> >  addrinfo_obj.ai_flags |= AI_ADDRCONFIG
> > for the getaddrinfo() call, so that addresses are only returned for
> > those address families for which such an address is configured on the
> > local system.  I couldn't see a way to do that in O'Caml.  I'm running
> > 3.10.2 (FreeBSD port, ocaml-notk-3.10.2_1) and the socket handling in
> > unix.mli leaves somewhat to be desired, both in setsockopt for anything
> > other than socket-level options and flags for some of the newer
> > interfaces such as getaddrinfo().  :-/
>
>
> This is the part I need to think about.  I actually know next to nothing
> about IPv6.  I really only looked at the code for very broad readability
> issues.
>
> You should take a look at 3.11 and see if it fixes any of your issues.
> That's what I"m currently compiling against.

I go with whatever FreeBSD Ports ships as the stable version.  But I
just downloaded 3.11.0 and looked.

 (1) otherlibs/unix/sockopt.c has been overhauled and now supports
     options at different levels, but it still has to be from a
     hardcoded table of options; IPPROTO_IPV6/IPV6_V6ONLY is present,
     but no other relevant options.

     So with OCaml 3.11, Kim's patch could be adjusted to let it work on
     any platform which supports non-V6ONLY IPv6 sockets.

 (2) otherlibs/unix/getaddrinfo.c doesn't support AI_ADDRCONFIG; it
     looks like it needs an addition to unix.{ml,mli} and then an extra
     case in a switch block in getaddrinfo_option().

In general, the IPv6 support looks like it has "enough to get working
sockets up" but not enough to demonstrate that it's been used in any
serious way.

It looks like the most stable way forward, without creating maintenance
problems, is going to be a Settings.disable_ipv6 so that those who use a
system without IPv6 configured up can at least manually disable it.

-Phil


_______________________________________________
Sks-devel mailing list
[hidden email]
http://lists.nongnu.org/mailman/listinfo/sks-devel