2015-12-30 06:59:32 -05:00
(* Copyright ( C ) 2015, Thomas Leonard <thomas.leonard@unikernel.com>
See the README file for details . * )
open Lwt . Infix
open Qubes
2016-10-01 09:42:27 -04:00
let src = Logs . Src . create " dao " ~ doc : " QubesDB data access "
module Log = ( val Logs . src_log src : Logs . LOG )
2015-12-30 06:59:32 -05:00
2016-10-01 09:42:27 -04:00
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 =
2022-03-30 03:12:01 -04:00
Xen_os . Xs . directory handle dir > | = function
2016-10-01 09:42:27 -04:00
| [ " " ] -> [] (* XenStore client bug *)
| items -> items
2020-04-29 09:58:01 -04:00
let db_root client_ip =
" /qubes-firewall/ " ^ ( Ipaddr . V4 . to_string client_ip )
let read_rules rules client_ip =
let root = db_root client_ip in
let rec get_rule n l : ( Pf_qubes . Parse_qubes . rule list , string ) result =
let pattern = root ^ " / " ^ Printf . sprintf " %04d " n in
Log . debug ( fun f -> f " reading %s " pattern ) ;
match Qubes . DB . KeyMap . find_opt pattern rules with
| None ->
Log . debug ( fun f -> f " rule %d does not exist; won't look for more " n ) ;
Ok ( List . rev l )
| Some rule ->
Log . debug ( fun f -> f " rule %d: %s " n rule ) ;
match Pf_qubes . Parse_qubes . parse_qubes ~ number : n rule with
| Error e -> Log . warn ( fun f -> f " Error parsing rule %d: %s " n e ) ; Error e
| Ok rule ->
Log . debug ( fun f -> f " parsed rule: %a " Pf_qubes . Parse_qubes . pp_rule rule ) ;
get_rule ( n + 1 ) ( rule :: l )
in
match get_rule 0 [] with
| Ok l -> l
| Error e ->
Log . warn ( fun f -> f " Defaulting to deny-all because of rule parse failure (%s) " e ) ;
[ Pf_qubes . Parse_qubes . ( { action = Drop ;
proto = None ;
specialtarget = None ;
dst = ` any ;
dstports = None ;
icmp_type = None ;
number = 0 ; } ) ]
2022-11-10 17:08:21 -05:00
let vifs client domid =
2024-04-24 12:31:12 -04:00
match int_of_string_opt domid with
2016-10-01 09:42:27 -04:00
| None -> Log . err ( fun f -> f " Invalid domid %S " domid ) ; Lwt . return []
| Some domid ->
let path = Printf . sprintf " backend/vif/%d " domid in
2022-11-10 17:08:21 -05:00
Xen_os . Xs . immediate client ( fun handle ->
directory ~ handle path > > =
Lwt_list . filter_map_p ( fun device_id ->
2024-04-24 12:31:12 -04:00
match int_of_string_opt device_id with
2022-11-10 17:08:21 -05:00
| 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 () -> Xen_os . Xs . read handle ( Printf . sprintf " %s/%d/ip " path device_id ) )
( fun client_ip ->
2024-04-24 12:31:12 -04:00
let client_ip' = match String . split_on_char ' ' client_ip with
2022-11-10 17:08:21 -05:00
| [] -> Log . err ( fun m -> m " unexpected empty list " ) ; " "
| [ ip ] -> ip
| ip :: rest ->
Log . warn ( fun m -> m " ignoring IPs %s from %a, we support one IP per client "
2024-04-24 12:31:12 -04:00
( String . concat " " rest ) ClientVif . pp vif ) ;
2022-11-10 17:08:21 -05:00
ip
in
match Ipaddr . V4 . of_string client_ip' with
| Ok ip -> Lwt . return ( Some ( vif , ip ) )
| Error ` Msg msg ->
Log . err ( fun f -> f " Error parsing IP address of %a from %s: %s "
ClientVif . pp vif client_ip msg ) ;
Lwt . return None
)
( 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
)
) )
2015-12-30 06:59:32 -05:00
let watch_clients fn =
2022-03-30 03:12:01 -04:00
Xen_os . Xs . make () > > = fun xs ->
2015-12-30 06:59:32 -05:00
let backend_vifs = " backend/vif " in
2016-10-01 09:42:27 -04:00
Log . info ( fun f -> f " Watching %s " backend_vifs ) ;
2022-03-30 03:12:01 -04:00
Xen_os . Xs . wait xs ( fun handle ->
2015-12-30 06:59:32 -05:00
begin Lwt . catch
2016-10-01 09:42:27 -04:00
( fun () -> directory ~ handle backend_vifs )
2015-12-30 06:59:32 -05:00
( function
2020-01-11 09:39:20 -05:00
| Xs_protocol . Enoent _ -> Lwt . return []
| ex -> Lwt . fail ex )
2015-12-30 06:59:32 -05:00
end > > = fun items ->
2022-11-10 17:08:21 -05:00
Xen_os . Xs . make () > > = fun xs ->
Lwt_list . map_p ( vifs xs ) items > > = fun items ->
2016-10-01 09:42:27 -04:00
fn ( List . concat items | > VifMap . of_list ) ;
2015-12-30 06:59:32 -05:00
(* Wait for further updates *)
2020-01-11 09:39:20 -05:00
Lwt . fail Xs_protocol . Eagain
2015-12-30 06:59:32 -05:00
)
type network_config = {
2023-07-05 05:56:19 -04:00
from_cmdline : bool ; (* Specify if we have network configuration from command line or from qubesDB *)
2023-06-30 09:31:30 -04:00
netvm_ip : Ipaddr . V4 . t ; (* The IP address of NetVM ( our gateway ) *)
our_ip : Ipaddr . V4 . t ; (* The IP address of our interface to NetVM *)
2021-11-10 09:26:17 -05:00
dns : Ipaddr . V4 . t ;
2022-09-07 10:53:45 -04:00
dns2 : Ipaddr . V4 . t ;
2015-12-30 06:59:32 -05:00
}
2019-04-28 11:06:03 -04:00
exception Missing_key of string
let try_read_network_config db =
2015-12-30 06:59:32 -05:00
let get name =
2019-04-28 11:06:03 -04:00
match DB . KeyMap . find_opt name db with
2023-07-05 05:56:19 -04:00
| None -> raise ( Missing_key name )
2023-06-30 07:59:03 -04:00
| Some value -> Ipaddr . V4 . of_string_exn value in
2023-06-30 09:31:30 -04:00
let our_ip = get " /qubes-ip " in (* - IP address for this VM ( only when VM has netvm set ) *)
let netvm_ip = get " /qubes-gateway " in (* - default gateway IP ( only when VM has netvm set ) ; VM should add host route to this address directly via eth0 ( or whatever default interface name is ) *)
2023-06-30 07:59:03 -04:00
let dns = get " /qubes-primary-dns " in
let dns2 = get " /qubes-secondary-dns " in
2023-07-05 05:56:19 -04:00
{ from_cmdline = false ; netvm_ip ; our_ip ; dns ; dns2 }
2015-12-30 06:59:32 -05:00
2019-04-28 11:06:03 -04:00
let read_network_config qubesDB =
let rec aux bindings =
try Lwt . return ( try_read_network_config bindings )
with Missing_key key ->
Log . warn ( fun f -> f " QubesDB key %S not (yet) present; waiting for QubesDB to change... " key ) ;
DB . after qubesDB bindings > > = aux
in
aux ( DB . bindings qubesDB )
2023-06-30 10:58:08 -04:00
let print_network_config config =
2023-07-11 08:26:12 -04:00
Log . info ( fun f -> f " @[<v2>Current network configuration (QubesDB or command line):@, \
2023-06-30 10:58:08 -04:00
NetVM IP on uplink network : % a @ , \
Our IP on client networks : % a @ , \
DNS primary resolver : % a @ , \
DNS secondary resolver : % a @ ] "
Ipaddr . V4 . pp config . netvm_ip
Ipaddr . V4 . pp config . our_ip
Ipaddr . V4 . pp config . dns
Ipaddr . V4 . pp config . dns2 )
2015-12-30 06:59:32 -05:00
let set_iptables_error db = Qubes . DB . write db " /qubes-iptables-error "