Rationalised firewall rules syntax

Added explicit NAT target, allowing NAT even within client net and
making it clear that NAT is used externally.

Changed Redirect_to_netvm to NAT_to, and allow specifying any target
host.
This commit is contained in:
Thomas Leonard 2016-01-01 11:32:57 +00:00
parent 4032a5d776
commit 2002126b8b
11 changed files with 85 additions and 38 deletions

View File

@ -12,10 +12,17 @@ type t = {
client_gw : Ipaddr.V4.t; (* The IP that clients are given as their default gateway. *) client_gw : Ipaddr.V4.t; (* The IP that clients are given as their default gateway. *)
} }
type host =
[ `Client of client_link
| `Unknown_client of Ipaddr.t
| `Client_gateway
| `External of Ipaddr.t ]
let create ~prefix ~client_gw = let create ~prefix ~client_gw =
{ iface_of_ip = IpMap.empty; client_gw; prefix } { iface_of_ip = IpMap.empty; client_gw; prefix }
let prefix t = t.prefix let prefix t = t.prefix
let client_gw t = t.client_gw
let add_client t iface = let add_client t iface =
let ip = iface#other_ip in let ip = iface#other_ip in
@ -41,6 +48,12 @@ let classify t ip =
| None when Ipaddr.V4.Prefix.mem ip4 t.prefix -> `Unknown_client ip | None when Ipaddr.V4.Prefix.mem ip4 t.prefix -> `Unknown_client ip
| None -> `External ip | None -> `External ip
let resolve t : host -> Ipaddr.t = function
| `Client client_link -> Ipaddr.V4 client_link#other_ip
| `Client_gateway -> Ipaddr.V4 t.client_gw
| `Unknown_client addr
| `External addr -> addr
module ARP = struct module ARP = struct
type arp = { type arp = {
net : t; net : t;

View File

@ -8,6 +8,12 @@ open Utils
type t type t
(** A network for client AppVMs to join. *) (** A network for client AppVMs to join. *)
type host =
[ `Client of client_link
| `Unknown_client of Ipaddr.t
| `Client_gateway
| `External of Ipaddr.t ]
val create : prefix:Ipaddr.V4.Prefix.t -> client_gw:Ipaddr.V4.t -> t val create : prefix:Ipaddr.V4.Prefix.t -> client_gw:Ipaddr.V4.t -> t
(** [create ~prefix ~client_gw] is a network of client machines. (** [create ~prefix ~client_gw] is a network of client machines.
Their IP addresses all start with [prefix] and they are configured to Their IP addresses all start with [prefix] and they are configured to
@ -17,9 +23,10 @@ val add_client : t -> client_link -> unit
val remove_client : t -> client_link -> unit val remove_client : t -> client_link -> unit
val prefix : t -> Ipaddr.V4.Prefix.t val prefix : t -> Ipaddr.V4.Prefix.t
val client_gw : t -> Ipaddr.V4.t
val classify : t -> Ipaddr.t -> val classify : t -> Ipaddr.t -> host
[ `Client of client_link | `Unknown_client of Ipaddr.t | `Client_gateway | `External of Ipaddr.t ] val resolve : t -> host -> Ipaddr.t
val lookup : t -> Ipaddr.V4.t -> client_link option val lookup : t -> Ipaddr.V4.t -> client_link option

View File

@ -10,9 +10,10 @@ module ClientEth = Ethif.Make(Netback)
let src = Logs.Src.create "net" ~doc:"Client networking" let src = Logs.Src.create "net" ~doc:"Client networking"
module Log = (val Logs.src_log src : Logs.LOG) module Log = (val Logs.src_log src : Logs.LOG)
class client_iface eth client_ip client_mac : client_link = object class client_iface eth ~gateway_ip ~client_ip client_mac : client_link = object
method my_mac = ClientEth.mac eth method my_mac = ClientEth.mac eth
method other_mac = client_mac method other_mac = client_mac
method my_ip = gateway_ip
method other_ip = client_ip method other_ip = client_ip
method writev ip = method writev ip =
let eth_hdr = eth_header_ipv4 ~src:(ClientEth.mac eth) ~dst:client_mac in let eth_hdr = eth_header_ipv4 ~src:(ClientEth.mac eth) ~dst:client_mac in
@ -44,10 +45,12 @@ let add_vif { Dao.domid; device_id; client_ip } ~router ~cleanup_tasks =
f domid (Ipaddr.V4.to_string client_ip)); f 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 ->
let client_mac = Netback.mac backend in let client_mac = Netback.mac backend in
let iface = new client_iface eth client_ip client_mac in 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
Router.add_client router iface; Router.add_client router iface;
Cleanup.on_cleanup cleanup_tasks (fun () -> Router.remove_client router iface); Cleanup.on_cleanup cleanup_tasks (fun () -> Router.remove_client router iface);
let fixed_arp = Client_eth.ARP.create ~net:router.Router.client_eth iface in let fixed_arp = Client_eth.ARP.create ~net:client_eth iface in
Netback.listen backend (fun frame -> Netback.listen backend (fun frame ->
match Wire_structs.parse_ethernet_frame frame with match Wire_structs.parse_ethernet_frame frame with
| None -> Log.warn "Invalid Ethernet frame" Logs.unit; return () | None -> Log.warn "Invalid Ethernet frame" Logs.unit; return ()

View File

@ -51,6 +51,7 @@ let pp_ports fmt {sport; dport} =
let pp_host fmt = function let pp_host fmt = function
| `Client c -> Ipaddr.V4.pp_hum fmt (c#other_ip) | `Client c -> Ipaddr.V4.pp_hum fmt (c#other_ip)
| `Unknown_client ip -> Format.fprintf fmt "unknown-client(%a)" Ipaddr.pp_hum ip | `Unknown_client ip -> Format.fprintf fmt "unknown-client(%a)" Ipaddr.pp_hum ip
| `NetVM -> Format.pp_print_string fmt "net-vm"
| `External ip -> Format.fprintf fmt "external(%a)" Ipaddr.pp_hum ip | `External ip -> Format.fprintf fmt "external(%a)" Ipaddr.pp_hum ip
| `Firewall_uplink -> Format.pp_print_string fmt "firewall(uplink)" | `Firewall_uplink -> Format.pp_print_string fmt "firewall(uplink)"
| `Client_gateway -> Format.pp_print_string fmt "firewall(client-gw)" | `Client_gateway -> Format.pp_print_string fmt "firewall(client-gw)"
@ -94,8 +95,9 @@ let rec add_nat_rule_and_transmit t frame fn fmt logf =
(* Add a NAT rule for the endpoints in this frame, via a random port on the firewall. *) (* Add a NAT rule for the endpoints in this frame, via a random port on the firewall. *)
let add_nat_and_forward_ipv4 t frame = let add_nat_and_forward_ipv4 t frame =
let xl_host = Ipaddr.V4 t.Router.uplink#my_ip in
add_nat_rule_and_transmit t frame add_nat_rule_and_transmit t frame
(Nat_rewrite.make_nat_entry t.Router.nat frame t.Router.my_uplink_ip) (Nat_rewrite.make_nat_entry t.Router.nat frame xl_host)
"added NAT entry: %s:%d -> firewall:%d -> %d:%s" "added NAT entry: %s:%d -> firewall:%d -> %d:%s"
(fun xl_port f -> (fun xl_port f ->
match Nat_rewrite.layers frame with match Nat_rewrite.layers frame with
@ -106,12 +108,13 @@ let add_nat_and_forward_ipv4 t frame =
f (Ipaddr.to_string src) sport xl_port dport (Ipaddr.to_string dst) f (Ipaddr.to_string src) sport xl_port dport (Ipaddr.to_string dst)
) )
(* Add a NAT rule to redirect this conversation to NetVM instead of us. *) (* Add a NAT rule to redirect this conversation to [host:port] instead of us. *)
let redirect_to_netvm t ~frame ~port = let nat_to t ~frame ~host ~port =
let gw = Ipaddr.V4 t.Router.default_gateway#other_ip in let gw = Router.resolve t host in
let xl_host = Ipaddr.V4 t.Router.uplink#my_ip in
add_nat_rule_and_transmit t frame add_nat_rule_and_transmit t frame
(fun xl_port -> (fun xl_port ->
Nat_rewrite.make_redirect_entry t.Router.nat frame (t.Router.my_uplink_ip, xl_port) (gw, port) Nat_rewrite.make_redirect_entry t.Router.nat frame (xl_host, xl_port) (gw, port)
) )
"added NAT redirect %s:%d -> %d:firewall:%d -> %d:NetVM" "added NAT redirect %s:%d -> %d:firewall:%d -> %d:NetVM"
(fun xl_port f -> (fun xl_port f ->
@ -141,14 +144,15 @@ let ipv4_from_client t frame =
| Some info -> | Some info ->
match Rules.from_client info, info.dst with match Rules.from_client info, info.dst with
| `Accept, `Client client_link -> transmit ~frame client_link | `Accept, `Client client_link -> transmit ~frame client_link
| `Accept, `External _ -> add_nat_and_forward_ipv4 t frame | `Accept, (`External _ | `NetVM) -> transmit ~frame t.Router.uplink
| `Accept, `Unknown_client _ -> | `Accept, `Unknown_client _ ->
Log.warn "Dropping packet to unknown client %a" (fun f -> f pp_packet info); Log.warn "Dropping packet to unknown client %a" (fun f -> f pp_packet info);
return () return ()
| `Accept, (`Firewall_uplink | `Client_gateway) -> | `Accept, (`Firewall_uplink | `Client_gateway) ->
Log.warn "Bad rule: firewall can't accept packets %a" (fun f -> f pp_packet info); Log.warn "Bad rule: firewall can't accept packets %a" (fun f -> f pp_packet info);
return () return ()
| `Redirect_to_netvm port, _ -> redirect_to_netvm t ~frame ~port | `NAT, _ -> add_nat_and_forward_ipv4 t frame
| `NAT_to (host, port), _ -> nat_to t ~frame ~host ~port
| `Drop reason, _ -> | `Drop reason, _ ->
Log.info "Dropped packet (%s) %a" (fun f -> f reason pp_packet info); Log.info "Dropped packet (%s) %a" (fun f -> f reason pp_packet info);
return () return ()
@ -166,7 +170,7 @@ let ipv4_from_netvm t frame =
| `Client _ | `Unknown_client _ | `Firewall_uplink | `Client_gateway -> | `Client _ | `Unknown_client _ | `Firewall_uplink | `Client_gateway ->
Log.warn "Frame from NetVM has internal source IP address! %a" (fun f -> f pp_packet info); Log.warn "Frame from NetVM has internal source IP address! %a" (fun f -> f pp_packet info);
return () return ()
| `External _ -> | `External _ | `NetVM ->
match translate t frame with match translate t frame with
| Some frame -> forward_ipv4 t frame | Some frame -> forward_ipv4 t frame
| None -> | None ->

View File

@ -11,7 +11,7 @@ type ports = {
} }
type host = type host =
[ `Client of client_link | `Unknown_client of Ipaddr.t | `Client_gateway | `Firewall_uplink | `External of Ipaddr.t ] [ `Client of client_link | `Unknown_client of Ipaddr.t | `Client_gateway | `Firewall_uplink | `NetVM | `External of Ipaddr.t ]
type info = { type info = {
frame : Cstruct.t; frame : Cstruct.t;

View File

@ -11,13 +11,12 @@ module Log = (val Logs.src_log src : Logs.LOG)
type t = { type t = {
client_eth : Client_eth.t; client_eth : Client_eth.t;
nat : Nat_lookup.t; nat : Nat_lookup.t;
default_gateway : interface; uplink : interface;
my_uplink_ip : Ipaddr.t;
} }
let create ~client_eth ~default_gateway ~my_uplink_ip = let create ~client_eth ~uplink =
let nat = Nat_lookup.empty () in let nat = Nat_lookup.empty () in
{ client_eth; nat; default_gateway; my_uplink_ip } { client_eth; nat; uplink }
let target t buf = let target t buf =
let open Wire_structs.Ipv4_wire in let open Wire_structs.Ipv4_wire in
@ -29,12 +28,17 @@ let target t buf =
Log.warn "Packet to unknown internal client %a - dropping" Log.warn "Packet to unknown internal client %a - dropping"
(fun f -> f Ipaddr.V4.pp_hum dst_ip); (fun f -> f Ipaddr.V4.pp_hum dst_ip);
None None
) else Some t.default_gateway ) else Some t.uplink
let add_client t = Client_eth.add_client t.client_eth let add_client t = Client_eth.add_client t.client_eth
let remove_client t = Client_eth.remove_client t.client_eth let remove_client t = Client_eth.remove_client t.client_eth
let classify t ip = let classify t ip =
let (===) a b = (Ipaddr.compare a b = 0) in if ip = Ipaddr.V4 t.uplink#my_ip then `Firewall_uplink
if ip === t.my_uplink_ip then `Firewall_uplink else if ip = Ipaddr.V4 t.uplink#other_ip then `NetVM
else (Client_eth.classify t.client_eth ip :> Packet.host) else (Client_eth.classify t.client_eth ip :> Packet.host)
let resolve t = function
| `Firewall_uplink -> Ipaddr.V4 t.uplink#my_ip
| `NetVM -> Ipaddr.V4 t.uplink#other_ip
| #Client_eth.host as host -> Client_eth.resolve t.client_eth host

View File

@ -8,19 +8,16 @@ open Utils
type t = private { type t = private {
client_eth : Client_eth.t; client_eth : Client_eth.t;
nat : Nat_lookup.t; nat : Nat_lookup.t;
default_gateway : interface; uplink : interface;
my_uplink_ip : Ipaddr.t;
} }
(** A routing table. *) (** A routing table. *)
val create : val create :
client_eth:Client_eth.t -> client_eth:Client_eth.t ->
default_gateway:interface -> uplink:interface ->
my_uplink_ip:Ipaddr.t ->
t t
(** [create ~client_eth ~default_gateway ~my_uplink_ip] is a new routing table (** [create ~client_eth ~uplink] is a new routing table
that routes packets outside of [client_eth] to [default_gateway], changing their that routes packets outside of [client_eth] via [uplink]. *)
source address to [my_uplink_ip] for NAT. *)
val target : t -> Cstruct.t -> interface option val target : t -> Cstruct.t -> interface option
(** [target t packet] is the interface to which [packet] (an IP packet) should be routed. *) (** [target t packet] is the interface to which [packet] (an IP packet) should be routed. *)
@ -32,3 +29,4 @@ val add_client : t -> client_link -> unit
val remove_client : t -> client_link -> unit val remove_client : t -> client_link -> unit
val classify : t -> Ipaddr.t -> Packet.host val classify : t -> Ipaddr.t -> Packet.host
val resolve : t -> Packet.host -> Ipaddr.t

View File

@ -8,11 +8,28 @@ open Packet
(* OCaml normally warns if you don't match all fields, but that's OK here. *) (* OCaml normally warns if you don't match all fields, but that's OK here. *)
[@@@ocaml.warning "-9"] [@@@ocaml.warning "-9"]
(** {2 Actions}
The possible actions are:
- [`Accept] : Send the packet to its destination.
- [`NAT] : Rewrite the packet's source field so packet appears to
have come from the firewall, via an unused port.
Also, add NAT rules so related packets will be translated accordingly.
- [`NAT_to (host, port)] :
As for [`NAT], but also rewrite the packet's destination fields so it
will be sent to [host:port].
- [`Drop reason] drop the packet and log the reason.
*)
(** Decide what to do with a packet from a client VM. (** Decide what to do with a packet from a client VM.
Note: If the packet matched an existing NAT rule then this isn't called. *) Note: If the packet matched an existing NAT rule then this isn't called. *)
let from_client = function let from_client = function
| { dst = `External _ } -> `Accept | { dst = (`External _ | `NetVM) } -> `NAT
| { dst = `Client_gateway; proto = `UDP { dport = 53 } } -> `Redirect_to_netvm 53 | { dst = `Client_gateway; proto = `UDP { dport = 53 } } -> `NAT_to (`NetVM, 53)
| { dst = (`Client_gateway | `Firewall_uplink) } -> `Drop "packet addressed to firewall itself" | { dst = (`Client_gateway | `Firewall_uplink) } -> `Drop "packet addressed to firewall itself"
| { dst = `Client _ } -> `Drop "prevent communication between client VMs" | { dst = `Client _ } -> `Drop "prevent communication between client VMs"
| { dst = `Unknown_client _ } -> `Drop "target client not running" | { dst = `Unknown_client _ } -> `Drop "target client not running"

View File

@ -37,8 +37,7 @@ module Main (Clock : V1.CLOCK) = struct
(* Set up routing between networks and hosts *) (* Set up routing between networks and hosts *)
let router = Router.create let router = Router.create
~client_eth ~client_eth
~default_gateway:(Uplink.interface uplink) ~uplink:(Uplink.interface uplink) in
~my_uplink_ip:(Ipaddr.V4 config.Dao.uplink_our_ip) in
(* Handle packets from both networks *) (* Handle packets from both networks *)
Lwt.join [ Lwt.join [
Client_net.listen router; Client_net.listen router;

View File

@ -18,12 +18,12 @@ module Make(Clock : V1.CLOCK) = struct
eth : Eth.t; eth : Eth.t;
arp : Arp.t; arp : Arp.t;
interface : interface; interface : interface;
my_ip : Ipaddr.t;
} }
class netvm_iface eth mac netvm_ip : interface = object class netvm_iface eth mac ~my_ip ~other_ip : interface = object
method my_mac = Eth.mac eth method my_mac = Eth.mac eth
method other_ip = netvm_ip method my_ip = my_ip
method other_ip = other_ip
method writev ip = method writev ip =
mac >>= fun dst -> mac >>= fun dst ->
let eth_hdr = eth_header_ipv4 ~src:(Eth.mac eth) ~dst in let eth_hdr = eth_header_ipv4 ~src:(Eth.mac eth) ~dst in
@ -51,7 +51,8 @@ module Make(Clock : V1.CLOCK) = struct
let netvm_mac = Arp.query arp config.Dao.uplink_netvm_ip >|= function let netvm_mac = Arp.query arp config.Dao.uplink_netvm_ip >|= function
| `Timeout -> failwith "ARP timeout getting MAC of our NetVM" | `Timeout -> failwith "ARP timeout getting MAC of our NetVM"
| `Ok netvm_mac -> netvm_mac in | `Ok netvm_mac -> netvm_mac in
let my_ip = Ipaddr.V4 ip in let interface = new netvm_iface eth netvm_mac
let interface = new netvm_iface eth netvm_mac config.Dao.uplink_netvm_ip in ~my_ip:ip
return { net; eth; arp; interface; my_ip } ~other_ip:config.Dao.uplink_netvm_ip in
return { net; eth; arp; interface }
end end

View File

@ -22,6 +22,7 @@ module IntMap = Map.Make(Int)
class type interface = object class type interface = object
method my_mac : Macaddr.t method my_mac : Macaddr.t
method writev : Cstruct.t list -> unit Lwt.t method writev : Cstruct.t list -> unit Lwt.t
method my_ip : Ipaddr.V4.t
method other_ip : Ipaddr.V4.t method other_ip : Ipaddr.V4.t
end end