2015-12-30 04:52:24 -05:00
|
|
|
(* Copyright (C) 2015, Thomas Leonard <thomas.leonard@unikernel.com>
|
|
|
|
See the README file for details. *)
|
|
|
|
|
2015-12-30 08:48:13 -05:00
|
|
|
open Lwt.Infix
|
2017-03-02 09:52:55 -05:00
|
|
|
open Fw_utils
|
2015-12-30 04:52:24 -05:00
|
|
|
|
2015-12-30 08:48:13 -05:00
|
|
|
module Netback = Netchannel.Backend.Make(Netchannel.Xenstore.Make(OS.Xs))
|
2019-03-17 17:32:17 -04:00
|
|
|
module ClientEth = Ethernet.Make(Netback)
|
2015-12-30 04:52:24 -05:00
|
|
|
|
2016-09-25 10:25:51 -04:00
|
|
|
let src = Logs.Src.create "client_net" ~doc:"Client networking"
|
2015-12-30 08:48:13 -05:00
|
|
|
module Log = (val Logs.src_log src : Logs.LOG)
|
2015-12-30 04:52:24 -05:00
|
|
|
|
2019-03-17 17:32:17 -04:00
|
|
|
let writev eth dst proto fillfn =
|
2016-09-25 10:25:51 -04:00
|
|
|
Lwt.catch
|
2017-03-02 09:52:55 -05:00
|
|
|
(fun () ->
|
2019-03-17 17:32:17 -04:00
|
|
|
ClientEth.write eth dst proto fillfn >|= function
|
2017-03-02 09:52:55 -05:00
|
|
|
| Ok () -> ()
|
|
|
|
| Error e ->
|
2019-03-17 17:32:17 -04:00
|
|
|
Log.err (fun f -> f "error trying to send to client: @[%a@]"
|
|
|
|
ClientEth.pp_error e);
|
2017-03-02 09:52:55 -05:00
|
|
|
)
|
2016-09-25 10:25:51 -04:00
|
|
|
(fun ex ->
|
|
|
|
(* Usually Netback_shutdown, because the client disconnected *)
|
2019-03-17 17:32:17 -04:00
|
|
|
Log.err (fun f -> f "uncaught exception trying to send to client: @[%s@]"
|
|
|
|
(Printexc.to_string ex));
|
2016-09-25 10:25:51 -04:00
|
|
|
Lwt.return ()
|
|
|
|
)
|
|
|
|
|
2019-05-06 04:54:35 -04:00
|
|
|
class client_iface eth ~domid ~gateway_ip ~client_ip client_mac : client_link =
|
|
|
|
let log_header = Fmt.strf "dom%d:%a" domid Ipaddr.V4.pp client_ip in
|
|
|
|
object
|
|
|
|
val queue = FrameQ.create (Ipaddr.V4.to_string client_ip)
|
|
|
|
method my_mac = ClientEth.mac eth
|
|
|
|
method other_mac = client_mac
|
|
|
|
method my_ip = gateway_ip
|
|
|
|
method other_ip = client_ip
|
|
|
|
method writev proto fillfn =
|
|
|
|
FrameQ.send queue (fun () ->
|
|
|
|
writev eth client_mac proto fillfn
|
|
|
|
)
|
|
|
|
method log_header = log_header
|
|
|
|
end
|
2015-12-30 08:48:13 -05:00
|
|
|
|
2016-10-01 09:42:27 -04:00
|
|
|
let clients : Cleanup.t Dao.VifMap.t ref = ref Dao.VifMap.empty
|
2015-12-30 08:48:13 -05:00
|
|
|
|
2015-12-31 09:56:24 -05:00
|
|
|
(** Handle an ARP message from the client. *)
|
2017-03-02 09:52:55 -05:00
|
|
|
let input_arp ~fixed_arp ~iface request =
|
2019-03-17 17:32:17 -04:00
|
|
|
match Arp_packet.decode request with
|
2017-03-02 09:52:55 -05:00
|
|
|
| Error e ->
|
2019-03-17 17:32:17 -04:00
|
|
|
Log.warn (fun f -> f "Ignored unknown ARP message: %a" Arp_packet.pp_error e);
|
2017-03-02 09:52:55 -05:00
|
|
|
Lwt.return ()
|
|
|
|
| Ok arp ->
|
|
|
|
match Client_eth.ARP.input fixed_arp arp with
|
|
|
|
| None -> return ()
|
|
|
|
| Some response ->
|
2019-03-17 17:32:17 -04:00
|
|
|
iface#writev `ARP (fun b -> Arp_packet.encode_into response b; Arp_packet.size)
|
2015-12-31 09:56:24 -05:00
|
|
|
|
|
|
|
(** Handle an IPv4 packet from the client. *)
|
2019-04-17 06:03:17 -04:00
|
|
|
let input_ipv4 ~iface ~router packet =
|
2017-03-05 11:31:04 -05:00
|
|
|
match Nat_packet.of_ipv4_packet packet with
|
2017-03-02 09:52:55 -05:00
|
|
|
| Error e ->
|
2017-03-05 11:31:04 -05:00
|
|
|
Log.warn (fun f -> f "Ignored unknown IPv4 message: %a" Nat_packet.pp_error e);
|
2017-03-02 09:52:55 -05:00
|
|
|
Lwt.return ()
|
2017-03-05 11:31:04 -05:00
|
|
|
| Ok packet ->
|
|
|
|
let `IPv4 (ip, _) = packet in
|
2017-03-02 09:52:55 -05:00
|
|
|
let src = ip.Ipv4_packet.src in
|
2019-04-17 06:03:17 -04:00
|
|
|
if src = iface#other_ip then Firewall.ipv4_from_client router ~src:iface packet
|
2017-03-02 09:52:55 -05:00
|
|
|
else (
|
|
|
|
Log.warn (fun f -> f "Incorrect source IP %a in IP packet from %a (dropping)"
|
2019-04-17 06:03:17 -04:00
|
|
|
Ipaddr.V4.pp src Ipaddr.V4.pp iface#other_ip);
|
2017-03-02 09:52:55 -05:00
|
|
|
return ()
|
|
|
|
)
|
2015-12-31 09:56:24 -05:00
|
|
|
|
|
|
|
(** Connect to a new client's interface and listen for incoming frames. *)
|
2016-10-01 09:42:27 -04:00
|
|
|
let add_vif { Dao.ClientVif.domid; device_id } ~client_ip ~router ~cleanup_tasks =
|
2015-12-31 09:56:24 -05:00
|
|
|
Netback.make ~domid ~device_id >>= fun backend ->
|
2016-01-08 06:31:27 -05:00
|
|
|
Log.info (fun f -> f "Client %d (IP: %s) ready" domid (Ipaddr.V4.to_string client_ip));
|
2017-03-02 09:52:55 -05:00
|
|
|
ClientEth.connect backend >>= fun eth ->
|
2019-05-01 05:05:14 -04:00
|
|
|
let client_mac = Netback.frontend_mac backend in
|
2016-01-01 06:32:57 -05:00
|
|
|
let client_eth = router.Router.client_eth in
|
|
|
|
let gateway_ip = Client_eth.client_gw client_eth in
|
2019-05-06 04:54:35 -04:00
|
|
|
let iface = new client_iface eth ~domid ~gateway_ip ~client_ip client_mac in
|
2016-09-25 10:14:16 -04:00
|
|
|
Router.add_client router iface >>= fun () ->
|
2015-12-31 09:56:24 -05:00
|
|
|
Cleanup.on_cleanup cleanup_tasks (fun () -> Router.remove_client router iface);
|
2016-01-01 06:32:57 -05:00
|
|
|
let fixed_arp = Client_eth.ARP.create ~net:client_eth iface in
|
2019-03-24 09:29:21 -04:00
|
|
|
Netback.listen backend ~header_size:Ethernet_wire.sizeof_ethernet (fun frame ->
|
2019-03-17 17:32:17 -04:00
|
|
|
match Ethernet_packet.Unmarshal.of_cstruct frame with
|
2017-03-06 09:30:41 -05:00
|
|
|
| exception ex ->
|
|
|
|
Log.err (fun f -> f "Error unmarshalling ethernet frame from client: %s@.%a" (Printexc.to_string ex)
|
|
|
|
Cstruct.hexdump_pp frame
|
|
|
|
);
|
|
|
|
Lwt.return_unit
|
2017-03-02 09:52:55 -05:00
|
|
|
| Error err -> Log.warn (fun f -> f "Invalid Ethernet frame: %s" err); return ()
|
|
|
|
| Ok (eth, payload) ->
|
2019-03-17 17:32:17 -04:00
|
|
|
match eth.Ethernet_packet.ethertype with
|
|
|
|
| `ARP -> input_arp ~fixed_arp ~iface payload
|
2019-04-17 06:03:17 -04:00
|
|
|
| `IPv4 -> input_ipv4 ~iface ~router payload
|
2019-03-17 17:32:17 -04:00
|
|
|
| `IPv6 -> return () (* TODO: oh no! *)
|
2015-12-31 09:56:24 -05:00
|
|
|
)
|
2017-03-02 09:52:55 -05:00
|
|
|
>|= or_raise "Listen on client interface" Netback.pp_error
|
2015-12-31 09:56:24 -05:00
|
|
|
|
|
|
|
(** A new client VM has been found in XenStore. Find its interface and connect to it. *)
|
2016-10-01 09:42:27 -04:00
|
|
|
let add_client ~router vif client_ip =
|
2015-12-30 08:48:13 -05:00
|
|
|
let cleanup_tasks = Cleanup.create () in
|
2019-05-06 04:54:35 -04:00
|
|
|
Log.info (fun f -> f "add client vif %a with IP %a" Dao.ClientVif.pp vif Ipaddr.V4.pp client_ip);
|
2015-12-30 08:48:13 -05:00
|
|
|
Lwt.async (fun () ->
|
2016-10-01 09:42:27 -04:00
|
|
|
Lwt.catch (fun () ->
|
|
|
|
add_vif vif ~client_ip ~router ~cleanup_tasks
|
|
|
|
)
|
|
|
|
(fun ex ->
|
2017-11-09 10:20:55 -05:00
|
|
|
Log.warn (fun f -> f "Error with client %a: %s"
|
2016-10-01 09:42:27 -04:00
|
|
|
Dao.ClientVif.pp vif (Printexc.to_string ex));
|
|
|
|
return ()
|
|
|
|
)
|
|
|
|
);
|
2015-12-30 08:48:13 -05:00
|
|
|
cleanup_tasks
|
|
|
|
|
2015-12-31 09:56:24 -05:00
|
|
|
(** Watch XenStore for notifications of new clients. *)
|
2015-12-30 08:48:13 -05:00
|
|
|
let listen router =
|
|
|
|
Dao.watch_clients (fun new_set ->
|
|
|
|
(* Check for removed clients *)
|
2016-10-01 09:42:27 -04:00
|
|
|
!clients |> Dao.VifMap.iter (fun key cleanup ->
|
|
|
|
if not (Dao.VifMap.mem key new_set) then (
|
|
|
|
clients := !clients |> Dao.VifMap.remove key;
|
|
|
|
Log.info (fun f -> f "client %a has gone" Dao.ClientVif.pp key);
|
2015-12-30 08:48:13 -05:00
|
|
|
Cleanup.cleanup cleanup
|
|
|
|
)
|
|
|
|
);
|
|
|
|
(* Check for added clients *)
|
2016-10-01 09:42:27 -04:00
|
|
|
new_set |> Dao.VifMap.iter (fun key ip_addr ->
|
|
|
|
if not (Dao.VifMap.mem key !clients) then (
|
|
|
|
let cleanup = add_client ~router key ip_addr in
|
|
|
|
clients := !clients |> Dao.VifMap.add key cleanup
|
2015-12-30 08:48:13 -05:00
|
|
|
)
|
|
|
|
)
|
|
|
|
)
|