qubes-mirage-firewall/router.ml
Thomas Leonard 62aec06be9 Try to avoid running out of memory on NAT reset
Before, when resetting the NAT table to handle an out-of-memory
condition we tried to allocate the new table while still holding
the reference to the old one. It should be more reliable to drop
the old reference first.

Log showed:

    2016-01-31 19:33.47: INF [firewall] added NAT redirect 10.137.3.12:32860 -> 53:firewall:52517 -> 53:net-vm
    2016-01-31 19:33.52: WRN [firewall] Out_of_memory adding NAT rule. Dropping NAT table...
    --- End dump ---
    Fatal error: exception Out of memory
    Raised by primitive operation at file "hashtbl.ml", line 63, characters 52-70
    Called from file "router.ml", line 47, characters 11-30
    Called from file "src/core/lwt.ml", line 907, characters 20-24
    Mirage exiting with status 2
    Do_exit called!
2016-01-31 21:03:35 +00:00

54 lines
1.7 KiB
OCaml

(* Copyright (C) 2015, Thomas Leonard <thomas.leonard@unikernel.com>
See the README file for details. *)
open Utils
let src = Logs.Src.create "router" ~doc:"Router"
module Log = (val Logs.src_log src : Logs.LOG)
(* The routing table *)
type t = {
client_eth : Client_eth.t;
mutable nat : Nat_lookup.t;
uplink : interface;
}
let create ~client_eth ~uplink =
let nat = Nat_lookup.empty () in
{ client_eth; nat; uplink }
let target t buf =
let open Wire_structs.Ipv4_wire in
let dst_ip = get_ipv4_dst buf |> Ipaddr.V4.of_int32 in
if Ipaddr.V4.Prefix.mem dst_ip (Client_eth.prefix t.client_eth) then (
match Client_eth.lookup t.client_eth dst_ip with
| Some client_link -> Some (client_link :> interface)
| None ->
Log.warn (fun f -> f "Packet to unknown internal client %a - dropping"
Ipaddr.V4.pp_hum dst_ip);
None
) else Some t.uplink
let add_client t = Client_eth.add_client t.client_eth
let remove_client t = Client_eth.remove_client t.client_eth
let classify t ip =
if ip = Ipaddr.V4 t.uplink#my_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)
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
(* To avoid needing to allocate a new NAT table when we've run out of
memory, pre-allocate the new one ahead of time. *)
let next_nat = ref (Nat_lookup.empty ())
let reset t =
t.nat <- !next_nat;
(* (at this point, the big old NAT table can be GC'd, so allocating
a new one should be OK) *)
next_nat := Nat_lookup.empty ()