mirror of
https://github.com/mirage/qubes-mirage-firewall.git
synced 2025-07-16 11:58:45 -04:00
Monitor set of client interfaces, not client domains
Qubes does not remove the client directory itself when the domain exits.
Combined with 63cbb4bed0
, this prevented clients from reconnecting.
This may also make it possible to connect clients to the firewall via
multiple interfaces, although this doesn't seem useful.
This commit is contained in:
parent
79092e1463
commit
312627e078
3 changed files with 92 additions and 62 deletions
|
@ -33,7 +33,7 @@ class client_iface eth ~gateway_ip ~client_ip client_mac : client_link = object
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
let clients : Cleanup.t IntMap.t ref = ref IntMap.empty
|
let clients : Cleanup.t Dao.VifMap.t ref = ref Dao.VifMap.empty
|
||||||
|
|
||||||
(** Handle an ARP message from the client. *)
|
(** Handle an ARP message from the client. *)
|
||||||
let input_arp ~fixed_arp ~eth request =
|
let input_arp ~fixed_arp ~eth request =
|
||||||
|
@ -52,7 +52,7 @@ let input_ipv4 ~client_ip ~router frame packet =
|
||||||
)
|
)
|
||||||
|
|
||||||
(** Connect to a new client's interface and listen for incoming frames. *)
|
(** Connect to a new client's interface and listen for incoming frames. *)
|
||||||
let add_vif { Dao.domid; device_id; client_ip } ~router ~cleanup_tasks =
|
let add_vif { Dao.ClientVif.domid; device_id } ~client_ip ~router ~cleanup_tasks =
|
||||||
Netback.make ~domid ~device_id >>= fun backend ->
|
Netback.make ~domid ~device_id >>= fun backend ->
|
||||||
Log.info (fun f -> f "Client %d (IP: %s) ready" domid (Ipaddr.V4.to_string client_ip));
|
Log.info (fun f -> f "Client %d (IP: %s) ready" domid (Ipaddr.V4.to_string client_ip));
|
||||||
ClientEth.connect backend >>= or_fail "Can't make Ethernet device" >>= fun eth ->
|
ClientEth.connect backend >>= or_fail "Can't make Ethernet device" >>= fun eth ->
|
||||||
|
@ -75,22 +75,16 @@ let add_vif { Dao.domid; device_id; client_ip } ~router ~cleanup_tasks =
|
||||||
)
|
)
|
||||||
|
|
||||||
(** A new client VM has been found in XenStore. Find its interface and connect to it. *)
|
(** A new client VM has been found in XenStore. Find its interface and connect to it. *)
|
||||||
let add_client ~router domid =
|
let add_client ~router vif client_ip =
|
||||||
let cleanup_tasks = Cleanup.create () in
|
let cleanup_tasks = Cleanup.create () in
|
||||||
Log.info (fun f -> f "add client domain %d" domid);
|
Log.info (fun f -> f "add client vif %a" Dao.ClientVif.pp vif);
|
||||||
Lwt.async (fun () ->
|
Lwt.async (fun () ->
|
||||||
Lwt.catch (fun () ->
|
Lwt.catch (fun () ->
|
||||||
Dao.client_vifs domid >>= function
|
add_vif vif ~client_ip ~router ~cleanup_tasks
|
||||||
| [] ->
|
|
||||||
Log.warn (fun f -> f "Client has no interfaces");
|
|
||||||
return ()
|
|
||||||
| vif :: others ->
|
|
||||||
if others <> [] then Log.warn (fun f -> f "Client has multiple interfaces; using first");
|
|
||||||
add_vif vif ~router ~cleanup_tasks
|
|
||||||
)
|
)
|
||||||
(fun ex ->
|
(fun ex ->
|
||||||
Log.warn (fun f -> f "Error connecting client domain %d: %s"
|
Log.warn (fun f -> f "Error connecting client %a: %s"
|
||||||
domid (Printexc.to_string ex));
|
Dao.ClientVif.pp vif (Printexc.to_string ex));
|
||||||
return ()
|
return ()
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
@ -98,22 +92,20 @@ let add_client ~router domid =
|
||||||
|
|
||||||
(** Watch XenStore for notifications of new clients. *)
|
(** Watch XenStore for notifications of new clients. *)
|
||||||
let listen router =
|
let listen router =
|
||||||
let backend_vifs = "backend/vif" in
|
|
||||||
Log.info (fun f -> f "Watching %s" backend_vifs);
|
|
||||||
Dao.watch_clients (fun new_set ->
|
Dao.watch_clients (fun new_set ->
|
||||||
(* Check for removed clients *)
|
(* Check for removed clients *)
|
||||||
!clients |> IntMap.iter (fun key cleanup ->
|
!clients |> Dao.VifMap.iter (fun key cleanup ->
|
||||||
if not (IntSet.mem key new_set) then (
|
if not (Dao.VifMap.mem key new_set) then (
|
||||||
clients := !clients |> IntMap.remove key;
|
clients := !clients |> Dao.VifMap.remove key;
|
||||||
Log.info (fun f -> f "client %d has gone" key);
|
Log.info (fun f -> f "client %a has gone" Dao.ClientVif.pp key);
|
||||||
Cleanup.cleanup cleanup
|
Cleanup.cleanup cleanup
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
(* Check for added clients *)
|
(* Check for added clients *)
|
||||||
new_set |> IntSet.iter (fun key ->
|
new_set |> Dao.VifMap.iter (fun key ip_addr ->
|
||||||
if not (IntMap.mem key !clients) then (
|
if not (Dao.VifMap.mem key !clients) then (
|
||||||
let cleanup = add_client ~router key in
|
let cleanup = add_client ~router key ip_addr in
|
||||||
clients := !clients |> IntMap.add key cleanup
|
clients := !clients |> Dao.VifMap.add key cleanup
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
65
dao.ml
65
dao.ml
|
@ -4,38 +4,75 @@
|
||||||
open Lwt.Infix
|
open Lwt.Infix
|
||||||
open Utils
|
open Utils
|
||||||
open Qubes
|
open Qubes
|
||||||
|
open Astring
|
||||||
|
|
||||||
type client_vif = {
|
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;
|
domid : int;
|
||||||
device_id : int;
|
device_id : int;
|
||||||
client_ip : Ipaddr.V4.t;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
let client_vifs domid =
|
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
|
let path = Printf.sprintf "backend/vif/%d" domid in
|
||||||
OS.Xs.make () >>= fun xs ->
|
directory ~handle path >>=
|
||||||
OS.Xs.immediate xs (fun h ->
|
Lwt_list.filter_map_p (fun device_id ->
|
||||||
OS.Xs.directory h path >>=
|
match String.to_int device_id with
|
||||||
Lwt_list.map_p (fun device_id ->
|
| None -> Log.err (fun f -> f "Invalid device ID %S for domid %d" device_id domid); Lwt.return_none
|
||||||
let device_id = int_of_string device_id in
|
| Some device_id ->
|
||||||
OS.Xs.read h (Printf.sprintf "%s/%d/ip" path device_id) >|= fun client_ip ->
|
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
|
let client_ip = Ipaddr.V4.of_string_exn client_ip in
|
||||||
{ domid; device_id; client_ip }
|
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 =
|
let watch_clients fn =
|
||||||
OS.Xs.make () >>= fun xs ->
|
OS.Xs.make () >>= fun xs ->
|
||||||
let backend_vifs = "backend/vif" in
|
let backend_vifs = "backend/vif" in
|
||||||
|
Log.info (fun f -> f "Watching %s" backend_vifs);
|
||||||
OS.Xs.wait xs (fun handle ->
|
OS.Xs.wait xs (fun handle ->
|
||||||
begin Lwt.catch
|
begin Lwt.catch
|
||||||
(fun () -> OS.Xs.directory handle backend_vifs)
|
(fun () -> directory ~handle backend_vifs)
|
||||||
(function
|
(function
|
||||||
| Xs_protocol.Enoent _ -> return []
|
| Xs_protocol.Enoent _ -> return []
|
||||||
| ex -> fail ex)
|
| ex -> fail ex)
|
||||||
end >>= fun items ->
|
end >>= fun items ->
|
||||||
let items = items |> List.fold_left (fun acc key -> IntSet.add (int_of_string key) acc) IntSet.empty in
|
Lwt_list.map_p (vifs ~handle) items >>= fun items ->
|
||||||
fn items;
|
fn (List.concat items |> VifMap.of_list);
|
||||||
(* Wait for further updates *)
|
(* Wait for further updates *)
|
||||||
fail Xs_protocol.Eagain
|
fail Xs_protocol.Eagain
|
||||||
)
|
)
|
||||||
|
|
23
dao.mli
23
dao.mli
|
@ -3,20 +3,21 @@
|
||||||
|
|
||||||
(** Wrapper for XenStore and QubesDB databases. *)
|
(** Wrapper for XenStore and QubesDB databases. *)
|
||||||
|
|
||||||
open Utils
|
module ClientVif : sig
|
||||||
|
type t = {
|
||||||
type client_vif = {
|
|
||||||
domid : int;
|
domid : int;
|
||||||
device_id : int;
|
device_id : int;
|
||||||
client_ip : Ipaddr.V4.t;
|
}
|
||||||
}
|
val pp : t Fmt.t
|
||||||
|
end
|
||||||
|
module VifMap : sig
|
||||||
|
include Map.S with type key = ClientVif.t
|
||||||
|
val find : key -> 'a t -> 'a option
|
||||||
|
end
|
||||||
|
|
||||||
val watch_clients : (IntSet.t -> unit) -> 'a Lwt.t
|
val watch_clients : (Ipaddr.V4.t VifMap.t -> unit) -> 'a Lwt.t
|
||||||
(** [watch_clients fn] calls [fn clients] with the current set of backend client domain IDs
|
(** [watch_clients fn] calls [fn clients] with the list of backend clients
|
||||||
in XenStore, and again each time the set changes. *)
|
in XenStore, and again each time XenStore updates. *)
|
||||||
|
|
||||||
val client_vifs : int -> client_vif list Lwt.t
|
|
||||||
(** [client_vif domid] is the list of network interfaces to the client VM [domid]. *)
|
|
||||||
|
|
||||||
type network_config = {
|
type network_config = {
|
||||||
uplink_netvm_ip : Ipaddr.V4.t; (* The IP address of NetVM (our gateway) *)
|
uplink_netvm_ip : Ipaddr.V4.t; (* The IP address of NetVM (our gateway) *)
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue