2016-09-25 09:38:17 -04:00
|
|
|
(* Copyright (C) 2016, Thomas Leonard <thomas.leonard@unikernel.com>
|
2015-12-30 08:48:13 -05:00
|
|
|
See the README file for details. *)
|
|
|
|
|
|
|
|
open Utils
|
2016-09-25 10:14:16 -04:00
|
|
|
open Lwt.Infix
|
2015-12-30 08:48:13 -05:00
|
|
|
|
2016-09-25 09:38:17 -04:00
|
|
|
let src = Logs.Src.create "client_eth" ~doc:"Ethernet networks for NetVM clients"
|
2015-12-30 08:48:13 -05:00
|
|
|
module Log = (val Logs.src_log src : Logs.LOG)
|
|
|
|
|
|
|
|
type t = {
|
|
|
|
mutable iface_of_ip : client_link IpMap.t;
|
2016-09-25 10:14:16 -04:00
|
|
|
changed : unit Lwt_condition.t; (* Fires when [iface_of_ip] changes. *)
|
2015-12-30 08:48:13 -05:00
|
|
|
client_gw : Ipaddr.V4.t; (* The IP that clients are given as their default gateway. *)
|
|
|
|
}
|
|
|
|
|
2016-01-01 06:32:57 -05:00
|
|
|
type host =
|
|
|
|
[ `Client of client_link
|
|
|
|
| `Client_gateway
|
|
|
|
| `External of Ipaddr.t ]
|
|
|
|
|
2016-09-25 09:38:17 -04:00
|
|
|
let create ~client_gw =
|
2016-09-25 10:14:16 -04:00
|
|
|
let changed = Lwt_condition.create () in
|
|
|
|
{ iface_of_ip = IpMap.empty; client_gw; changed }
|
2015-12-30 08:48:13 -05:00
|
|
|
|
2016-01-01 06:32:57 -05:00
|
|
|
let client_gw t = t.client_gw
|
2015-12-30 08:48:13 -05:00
|
|
|
|
|
|
|
let add_client t iface =
|
2015-12-30 11:07:16 -05:00
|
|
|
let ip = iface#other_ip in
|
2016-09-25 10:14:16 -04:00
|
|
|
let rec aux () =
|
|
|
|
if IpMap.mem ip t.iface_of_ip then (
|
|
|
|
(* Wait for old client to disappear before adding one with the same IP address.
|
|
|
|
Otherwise, its [remove_client] call will remove the new client instead. *)
|
|
|
|
Log.info (fun f -> f "Waiting for old client %a to go away before accepting new one" Ipaddr.V4.pp_hum ip);
|
|
|
|
Lwt_condition.wait t.changed >>= aux
|
|
|
|
) else (
|
|
|
|
t.iface_of_ip <- t.iface_of_ip |> IpMap.add ip iface;
|
|
|
|
Lwt_condition.broadcast t.changed ();
|
|
|
|
Lwt.return_unit
|
|
|
|
)
|
|
|
|
in
|
|
|
|
aux ()
|
2015-12-30 08:48:13 -05:00
|
|
|
|
|
|
|
let remove_client t iface =
|
2015-12-30 11:07:16 -05:00
|
|
|
let ip = iface#other_ip in
|
2015-12-30 08:48:13 -05:00
|
|
|
assert (IpMap.mem ip t.iface_of_ip);
|
2016-09-25 10:14:16 -04:00
|
|
|
t.iface_of_ip <- t.iface_of_ip |> IpMap.remove ip;
|
|
|
|
Lwt_condition.broadcast t.changed ()
|
2015-12-30 08:48:13 -05:00
|
|
|
|
|
|
|
let lookup t ip = IpMap.find ip t.iface_of_ip
|
|
|
|
|
2015-12-31 04:56:58 -05:00
|
|
|
let classify t ip =
|
|
|
|
match ip with
|
|
|
|
| Ipaddr.V6 _ -> `External ip
|
|
|
|
| Ipaddr.V4 ip4 ->
|
2016-01-01 05:55:34 -05:00
|
|
|
if ip4 = t.client_gw then `Client_gateway
|
2015-12-31 04:56:58 -05:00
|
|
|
else match lookup t ip4 with
|
2015-12-30 11:07:16 -05:00
|
|
|
| Some client_link -> `Client client_link
|
2015-12-31 04:56:58 -05:00
|
|
|
| None -> `External ip
|
2015-12-30 11:07:16 -05:00
|
|
|
|
2016-01-01 06:32:57 -05:00
|
|
|
let resolve t : host -> Ipaddr.t = function
|
|
|
|
| `Client client_link -> Ipaddr.V4 client_link#other_ip
|
|
|
|
| `Client_gateway -> Ipaddr.V4 t.client_gw
|
|
|
|
| `External addr -> addr
|
|
|
|
|
2015-12-30 08:48:13 -05:00
|
|
|
module ARP = struct
|
|
|
|
type arp = {
|
|
|
|
net : t;
|
|
|
|
client_link : client_link;
|
|
|
|
}
|
|
|
|
|
|
|
|
let lookup t ip =
|
2016-01-01 05:55:34 -05:00
|
|
|
if ip = t.net.client_gw then Some t.client_link#my_mac
|
2016-09-25 09:38:17 -04:00
|
|
|
else None
|
|
|
|
(* We're now treating client networks as point-to-point links,
|
|
|
|
so we no longer respond on behalf of other clients. *)
|
|
|
|
(*
|
2015-12-30 08:48:13 -05:00
|
|
|
else match IpMap.find ip t.net.iface_of_ip with
|
2015-12-30 11:07:16 -05:00
|
|
|
| Some client_iface -> Some client_iface#other_mac
|
2015-12-30 08:48:13 -05:00
|
|
|
| None -> None
|
2016-09-25 09:38:17 -04:00
|
|
|
*)
|
2015-12-30 08:48:13 -05:00
|
|
|
|
|
|
|
let create ~net client_link = {net; client_link}
|
|
|
|
|
|
|
|
type arp_msg = {
|
|
|
|
op: [ `Request |`Reply |`Unknown of int ];
|
|
|
|
sha: Macaddr.t;
|
|
|
|
spa: Ipaddr.V4.t;
|
|
|
|
tha: Macaddr.t;
|
|
|
|
tpa: Ipaddr.V4.t;
|
|
|
|
}
|
|
|
|
|
|
|
|
let to_wire arp =
|
|
|
|
let open Arpv4_wire in
|
|
|
|
(* Obtain a buffer to write into *)
|
|
|
|
let buf = Cstruct.create (Wire_structs.sizeof_ethernet + sizeof_arp) in
|
|
|
|
(* Write the ARP packet *)
|
|
|
|
let dmac = Macaddr.to_bytes arp.tha in
|
|
|
|
let smac = Macaddr.to_bytes arp.sha in
|
|
|
|
let spa = Ipaddr.V4.to_int32 arp.spa in
|
|
|
|
let tpa = Ipaddr.V4.to_int32 arp.tpa in
|
|
|
|
let op =
|
|
|
|
match arp.op with
|
|
|
|
|`Request -> 1
|
|
|
|
|`Reply -> 2
|
|
|
|
|`Unknown n -> n
|
|
|
|
in
|
|
|
|
Wire_structs.set_ethernet_dst dmac 0 buf;
|
|
|
|
Wire_structs.set_ethernet_src smac 0 buf;
|
|
|
|
Wire_structs.set_ethernet_ethertype buf 0x0806; (* ARP *)
|
|
|
|
let arpbuf = Cstruct.shift buf 14 in
|
|
|
|
set_arp_htype arpbuf 1;
|
|
|
|
set_arp_ptype arpbuf 0x0800; (* IPv4 *)
|
|
|
|
set_arp_hlen arpbuf 6; (* ethernet mac size *)
|
|
|
|
set_arp_plen arpbuf 4; (* ipv4 size *)
|
|
|
|
set_arp_op arpbuf op;
|
|
|
|
set_arp_sha smac 0 arpbuf;
|
|
|
|
set_arp_spa arpbuf spa;
|
|
|
|
set_arp_tha dmac 0 arpbuf;
|
|
|
|
set_arp_tpa arpbuf tpa;
|
|
|
|
buf
|
|
|
|
|
|
|
|
let input_query t frame =
|
|
|
|
let open Arpv4_wire in
|
|
|
|
let req_ipv4 = Ipaddr.V4.of_int32 (get_arp_tpa frame) in
|
2016-01-08 06:31:27 -05:00
|
|
|
Log.info (fun f -> f "who-has %s?" (Ipaddr.V4.to_string req_ipv4));
|
2016-01-01 05:55:34 -05:00
|
|
|
if req_ipv4 = t.client_link#other_ip then (
|
2016-01-08 06:31:27 -05:00
|
|
|
Log.info (fun f -> f "ignoring request for client's own IP");
|
2015-12-30 08:48:13 -05:00
|
|
|
None
|
|
|
|
) else match lookup t req_ipv4 with
|
|
|
|
| None ->
|
2016-01-08 06:31:27 -05:00
|
|
|
Log.info (fun f -> f "unknown address; not responding");
|
2015-12-30 08:48:13 -05:00
|
|
|
None
|
|
|
|
| Some req_mac ->
|
2016-01-08 06:31:27 -05:00
|
|
|
Log.info (fun f -> f "responding to: who-has %s?" (Ipaddr.V4.to_string req_ipv4));
|
2015-12-30 08:48:13 -05:00
|
|
|
Some (to_wire {
|
|
|
|
op = `Reply;
|
|
|
|
(* The Target Hardware Address and IP are copied from the request *)
|
|
|
|
tha = Macaddr.of_bytes_exn (copy_arp_sha frame);
|
|
|
|
tpa = Ipaddr.V4.of_int32 (get_arp_spa frame);
|
|
|
|
sha = req_mac;
|
|
|
|
spa = req_ipv4;
|
|
|
|
})
|
|
|
|
|
|
|
|
let input_gratuitous t frame =
|
|
|
|
let open Arpv4_wire in
|
|
|
|
let spa = Ipaddr.V4.of_int32 (get_arp_spa frame) in
|
|
|
|
let sha = Macaddr.of_bytes_exn (copy_arp_sha frame) in
|
|
|
|
match lookup t spa with
|
|
|
|
| Some real_mac when Macaddr.compare sha real_mac = 0 ->
|
2016-01-08 06:31:27 -05:00
|
|
|
Log.info (fun f -> f "client suggests updating %s -> %s (as expected)"
|
|
|
|
(Ipaddr.V4.to_string spa) (Macaddr.to_string sha));
|
2015-12-30 08:48:13 -05:00
|
|
|
| Some other_mac ->
|
2016-01-08 06:31:27 -05:00
|
|
|
Log.warn (fun f -> f "client suggests incorrect update %s -> %s (should be %s)"
|
|
|
|
(Ipaddr.V4.to_string spa) (Macaddr.to_string sha) (Macaddr.to_string other_mac));
|
2015-12-30 08:48:13 -05:00
|
|
|
| None ->
|
2016-01-08 06:31:27 -05:00
|
|
|
Log.warn (fun f -> f "client suggests incorrect update %s -> %s (unexpected IP)"
|
|
|
|
(Ipaddr.V4.to_string spa) (Macaddr.to_string sha))
|
2015-12-30 08:48:13 -05:00
|
|
|
|
|
|
|
let input t frame =
|
|
|
|
match Arpv4_wire.get_arp_op frame with
|
|
|
|
|1 -> input_query t frame
|
|
|
|
|2 -> input_gratuitous t frame; None
|
2016-01-08 06:31:27 -05:00
|
|
|
|n -> Log.warn (fun f -> f "unknown message %d - ignored" n); None
|
2015-12-30 08:48:13 -05:00
|
|
|
end
|