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
|
2015-12-30 04:52:24 -05:00
|
|
|
open Utils
|
|
|
|
|
2015-12-30 08:48:13 -05:00
|
|
|
module Netback = Netchannel.Backend.Make(Netchannel.Xenstore.Make(OS.Xs))
|
|
|
|
module ClientEth = Ethif.Make(Netback)
|
2015-12-30 04:52:24 -05:00
|
|
|
|
2015-12-30 08:48:13 -05:00
|
|
|
let src = Logs.Src.create "net" ~doc:"Client networking"
|
|
|
|
module Log = (val Logs.src_log src : Logs.LOG)
|
2015-12-30 04:52:24 -05:00
|
|
|
|
2016-01-01 06:32:57 -05:00
|
|
|
class client_iface eth ~gateway_ip ~client_ip client_mac : client_link = object
|
2015-12-30 08:48:13 -05:00
|
|
|
method my_mac = ClientEth.mac eth
|
2015-12-30 11:07:16 -05:00
|
|
|
method other_mac = client_mac
|
2016-01-01 06:32:57 -05:00
|
|
|
method my_ip = gateway_ip
|
2015-12-30 11:07:16 -05:00
|
|
|
method other_ip = client_ip
|
2015-12-30 08:48:13 -05:00
|
|
|
method writev ip =
|
|
|
|
let eth_hdr = eth_header_ipv4 ~src:(ClientEth.mac eth) ~dst:client_mac in
|
|
|
|
ClientEth.writev eth (fixup_checksums (Cstruct.concat (eth_hdr :: ip)))
|
2015-12-30 04:52:24 -05:00
|
|
|
end
|
2015-12-30 08:48:13 -05:00
|
|
|
|
|
|
|
let clients : Cleanup.t IntMap.t ref = ref IntMap.empty
|
|
|
|
|
2015-12-31 09:56:24 -05:00
|
|
|
(** Handle an ARP message from the client. *)
|
2015-12-31 10:56:56 -05:00
|
|
|
let input_arp ~fixed_arp ~eth request =
|
|
|
|
match Client_eth.ARP.input fixed_arp request with
|
2015-12-31 09:56:24 -05:00
|
|
|
| None -> return ()
|
2015-12-31 10:56:56 -05:00
|
|
|
| Some response -> ClientEth.write eth response
|
2015-12-31 09:56:24 -05:00
|
|
|
|
|
|
|
(** Handle an IPv4 packet from the client. *)
|
|
|
|
let input_ipv4 ~client_ip ~router frame packet =
|
|
|
|
let src = Wire_structs.Ipv4_wire.get_ipv4_src packet |> Ipaddr.V4.of_int32 in
|
2016-01-01 05:55:34 -05:00
|
|
|
if src = client_ip then Firewall.ipv4_from_client router frame
|
2015-12-31 09:56:24 -05:00
|
|
|
else (
|
|
|
|
Log.warn "Incorrect source IP %a in IP packet from %a (dropping)"
|
|
|
|
(fun f -> f Ipaddr.V4.pp_hum src Ipaddr.V4.pp_hum client_ip);
|
|
|
|
return ()
|
|
|
|
)
|
|
|
|
|
|
|
|
(** Connect to a new client's interface and listen for incoming frames. *)
|
|
|
|
let add_vif { Dao.domid; device_id; client_ip } ~router ~cleanup_tasks =
|
|
|
|
Netback.make ~domid ~device_id >>= fun backend ->
|
|
|
|
Log.info "Client %d (IP: %s) ready" (fun f ->
|
|
|
|
f domid (Ipaddr.V4.to_string client_ip));
|
|
|
|
ClientEth.connect backend >>= or_fail "Can't make Ethernet device" >>= fun eth ->
|
|
|
|
let client_mac = Netback.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
|
|
|
|
let iface = new client_iface eth ~gateway_ip ~client_ip client_mac in
|
2015-12-31 09:56:24 -05:00
|
|
|
Router.add_client router iface;
|
|
|
|
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
|
2015-12-31 09:56:24 -05:00
|
|
|
Netback.listen backend (fun frame ->
|
2015-12-31 10:56:56 -05:00
|
|
|
match Wire_structs.parse_ethernet_frame frame with
|
|
|
|
| None -> Log.warn "Invalid Ethernet frame" Logs.unit; return ()
|
|
|
|
| Some (typ, _destination, payload) ->
|
|
|
|
match typ with
|
|
|
|
| Some Wire_structs.ARP -> input_arp ~fixed_arp ~eth payload
|
|
|
|
| Some Wire_structs.IPv4 -> input_ipv4 ~client_ip ~router frame payload
|
|
|
|
| Some Wire_structs.IPv6 -> return ()
|
|
|
|
| None -> Logs.warn "Unknown Ethernet type" Logs.unit; Lwt.return_unit
|
2015-12-31 09:56:24 -05:00
|
|
|
)
|
|
|
|
|
|
|
|
(** A new client VM has been found in XenStore. Find its interface and connect to it. *)
|
|
|
|
let add_client ~router domid =
|
2015-12-30 08:48:13 -05:00
|
|
|
let cleanup_tasks = Cleanup.create () in
|
2015-12-31 09:56:24 -05:00
|
|
|
Log.info "add client domain %d" (fun f -> f domid);
|
2015-12-30 08:48:13 -05:00
|
|
|
Lwt.async (fun () ->
|
|
|
|
Lwt.catch (fun () ->
|
2015-12-31 09:56:24 -05:00
|
|
|
Dao.client_vifs domid >>= function
|
|
|
|
| [] ->
|
|
|
|
Log.warn "Client has no interfaces" Logs.unit;
|
|
|
|
return ()
|
2015-12-30 08:48:13 -05:00
|
|
|
| vif :: others ->
|
|
|
|
if others <> [] then Log.warn "Client has multiple interfaces; using first" Logs.unit;
|
2015-12-31 09:56:24 -05:00
|
|
|
add_vif vif ~router ~cleanup_tasks
|
2015-12-30 08:48:13 -05:00
|
|
|
)
|
|
|
|
(fun ex ->
|
|
|
|
Log.warn "Error connecting client domain %d: %s"
|
|
|
|
(fun f -> f domid (Printexc.to_string ex));
|
|
|
|
return ()
|
|
|
|
)
|
|
|
|
);
|
|
|
|
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 =
|
|
|
|
let backend_vifs = "backend/vif" in
|
|
|
|
Log.info "Watching %s" (fun f -> f backend_vifs);
|
|
|
|
Dao.watch_clients (fun new_set ->
|
|
|
|
(* Check for removed clients *)
|
|
|
|
!clients |> IntMap.iter (fun key cleanup ->
|
|
|
|
if not (IntSet.mem key new_set) then (
|
|
|
|
clients := !clients |> IntMap.remove key;
|
2015-12-31 09:56:24 -05:00
|
|
|
Log.info "client %d has gone" (fun f -> f key);
|
2015-12-30 08:48:13 -05:00
|
|
|
Cleanup.cleanup cleanup
|
|
|
|
)
|
|
|
|
);
|
|
|
|
(* Check for added clients *)
|
|
|
|
new_set |> IntSet.iter (fun key ->
|
|
|
|
if not (IntMap.mem key !clients) then (
|
2015-12-31 09:56:24 -05:00
|
|
|
let cleanup = add_client ~router key in
|
2015-12-30 08:48:13 -05:00
|
|
|
clients := !clients |> IntMap.add key cleanup
|
|
|
|
)
|
|
|
|
)
|
|
|
|
)
|