use a fresh client for requesting vif and ip

in the callback to "Xs_client.wait", all operations are tracked and new watches
are installed (that are never removed, due to xenstore's xs_handle
"accessed_path" never removes any elements of the "accessed_paths" (a mutable
StringSet). So, whatever is done in the callback of wait needs to take care
(if returning EAGAIN and thus forcing xenstore to continue waiting/watching)
that accesses are tracked.

Our way out is to create a fresh client and read the IP address with that new
client -> the watcher isn't extended -> no dangling (leaking) watches, and no
leaking only-expanding StringSet.
This commit is contained in:
Hannes Mehnert 2022-11-10 23:08:21 +01:00
parent 0e0917f4fe
commit d094b20950

70
dao.ml
View File

@ -65,43 +65,44 @@ let read_rules rules client_ip =
icmp_type = None; icmp_type = None;
number = 0;})] number = 0;})]
let vifs ~handle domid = let vifs client domid =
match String.to_int domid with match String.to_int domid with
| None -> Log.err (fun f -> f "Invalid domid %S" domid); Lwt.return [] | None -> Log.err (fun f -> f "Invalid domid %S" domid); Lwt.return []
| Some domid -> | Some domid ->
let path = Printf.sprintf "backend/vif/%d" domid in let path = Printf.sprintf "backend/vif/%d" domid in
directory ~handle path >>= Xen_os.Xs.immediate client (fun handle ->
Lwt_list.filter_map_p (fun device_id -> directory ~handle path >>=
match String.to_int device_id with Lwt_list.filter_map_p (fun device_id ->
| None -> Log.err (fun f -> f "Invalid device ID %S for domid %d" device_id domid); Lwt.return_none match String.to_int device_id with
| Some device_id -> | None -> Log.err (fun f -> f "Invalid device ID %S for domid %d" device_id domid); Lwt.return_none
let vif = { ClientVif.domid; device_id } in | Some device_id ->
Lwt.try_bind let vif = { ClientVif.domid; device_id } in
(fun () -> Xen_os.Xs.read handle (Printf.sprintf "%s/%d/ip" path device_id)) Lwt.try_bind
(fun client_ip -> (fun () -> Xen_os.Xs.read handle (Printf.sprintf "%s/%d/ip" path device_id))
let client_ip' = match String.cuts ~sep:" " client_ip with (fun client_ip ->
| [] -> Log.err (fun m -> m "unexpected empty list"); "" let client_ip' = match String.cuts ~sep:" " client_ip with
| [ ip ] -> ip | [] -> Log.err (fun m -> m "unexpected empty list"); ""
| ip::rest -> | [ ip ] -> ip
Log.warn (fun m -> m "ignoring IPs %s from %a, we support one IP per client" | ip::rest ->
(String.concat ~sep:" " rest) ClientVif.pp vif); Log.warn (fun m -> m "ignoring IPs %s from %a, we support one IP per client"
ip (String.concat ~sep:" " rest) ClientVif.pp vif);
in ip
match Ipaddr.V4.of_string client_ip' with in
| Ok ip -> Lwt.return (Some (vif, ip)) match Ipaddr.V4.of_string client_ip' with
| Error `Msg msg -> | Ok ip -> Lwt.return (Some (vif, ip))
Log.err (fun f -> f "Error parsing IP address of %a from %s: %s" | Error `Msg msg ->
ClientVif.pp vif client_ip msg); Log.err (fun f -> f "Error parsing IP address of %a from %s: %s"
Lwt.return None ClientVif.pp vif client_ip msg);
) Lwt.return None
(function )
| Xs_protocol.Enoent _ -> Lwt.return None (function
| ex -> | Xs_protocol.Enoent _ -> Lwt.return None
Log.err (fun f -> f "Error getting IP address of %a: %s" | ex ->
ClientVif.pp vif (Printexc.to_string ex)); Log.err (fun f -> f "Error getting IP address of %a: %s"
Lwt.return None ClientVif.pp vif (Printexc.to_string ex));
) Lwt.return None
) )
))
let watch_clients fn = let watch_clients fn =
Xen_os.Xs.make () >>= fun xs -> Xen_os.Xs.make () >>= fun xs ->
@ -114,7 +115,8 @@ let watch_clients fn =
| Xs_protocol.Enoent _ -> Lwt.return [] | Xs_protocol.Enoent _ -> Lwt.return []
| ex -> Lwt.fail ex) | ex -> Lwt.fail ex)
end >>= fun items -> end >>= fun items ->
Lwt_list.map_p (vifs ~handle) items >>= fun items -> Xen_os.Xs.make () >>= fun xs ->
Lwt_list.map_p (vifs xs) items >>= fun items ->
fn (List.concat items |> VifMap.of_list); fn (List.concat items |> VifMap.of_list);
(* Wait for further updates *) (* Wait for further updates *)
Lwt.fail Xs_protocol.Eagain Lwt.fail Xs_protocol.Eagain