(* Copyright (C) 2015, Thomas Leonard See the README file for details. *) open Lwt.Infix open Utils open Qubes open Astring let src = Logs.Src.create "dao" ~doc:"QubesDB data access" module Log = (val Logs.src_log src : Logs.LOG) module ClientVif = struct type t = { domid : int; device_id : int; } let pp f { domid; device_id } = Fmt.pf f "{domid=%d;device_id=%d}" domid device_id let compare = compare end module VifMap = struct include Map.Make(ClientVif) let rec of_list = function | [] -> empty | (k, v) :: rest -> add k v (of_list rest) let find key t = try Some (find key t) with Not_found -> None end let directory ~handle dir = OS.Xs.directory handle dir >|= function | [""] -> [] (* XenStore client bug *) | items -> items let vifs ~handle domid = match String.to_int domid with | None -> Log.err (fun f -> f "Invalid domid %S" domid); Lwt.return [] | Some domid -> let path = Printf.sprintf "backend/vif/%d" domid in directory ~handle path >>= Lwt_list.filter_map_p (fun device_id -> match String.to_int device_id with | None -> Log.err (fun f -> f "Invalid device ID %S for domid %d" device_id domid); Lwt.return_none | Some device_id -> let vif = { ClientVif.domid; device_id } in Lwt.try_bind (fun () -> OS.Xs.read handle (Printf.sprintf "%s/%d/ip" path device_id)) (fun client_ip -> let client_ip = Ipaddr.V4.of_string_exn client_ip in Lwt.return (Some (vif, client_ip)) ) (function | Xs_protocol.Enoent _ -> Lwt.return None | ex -> Log.err (fun f -> f "Error getting IP address of %a: %s" ClientVif.pp vif (Printexc.to_string ex)); Lwt.return None ) ) let watch_clients fn = OS.Xs.make () >>= fun xs -> let backend_vifs = "backend/vif" in Log.info (fun f -> f "Watching %s" backend_vifs); OS.Xs.wait xs (fun handle -> begin Lwt.catch (fun () -> directory ~handle backend_vifs) (function | Xs_protocol.Enoent _ -> return [] | ex -> fail ex) end >>= fun items -> Lwt_list.map_p (vifs ~handle) items >>= fun items -> fn (List.concat items |> VifMap.of_list); (* Wait for further updates *) fail Xs_protocol.Eagain ) type network_config = { uplink_netvm_ip : Ipaddr.V4.t; (* The IP address of NetVM (our gateway) *) uplink_our_ip : Ipaddr.V4.t; (* The IP address of our interface to NetVM *) clients_our_ip : Ipaddr.V4.t; (* The IP address of our interface to our client VMs (their gateway) *) } (* TODO: /qubes-secondary-dns *) let read_network_config qubesDB = let get name = match DB.read qubesDB name with | None -> raise (error "QubesDB key %S not present" name) | Some value -> value in let uplink_our_ip = get "/qubes-ip" |> Ipaddr.V4.of_string_exn in let uplink_netvm_ip = get "/qubes-gateway" |> Ipaddr.V4.of_string_exn in let clients_our_ip = get "/qubes-netvm-gateway" |> Ipaddr.V4.of_string_exn in { uplink_netvm_ip; uplink_our_ip; clients_our_ip } let set_iptables_error db = Qubes.DB.write db "/qubes-iptables-error"