qubes-mirage-firewall/my_nat.ml

82 lines
2.4 KiB
OCaml
Raw Normal View History

2017-03-02 09:52:55 -05:00
(* Copyright (C) 2015, Thomas Leonard <thomas.leonard@unikernel.com>
See the README file for details. *)
open Lwt.Infix
let src = Logs.Src.create "my-nat" ~doc:"NAT shim"
module Log = (val Logs.src_log src : Logs.LOG)
type action = [
2017-03-10 11:09:36 -05:00
| `NAT
| `Redirect of Mirage_nat.endpoint
2017-03-02 09:52:55 -05:00
]
2017-10-15 09:35:03 -04:00
module Nat = Mirage_nat_lru
2017-03-02 09:52:55 -05:00
2017-03-10 11:09:36 -05:00
type t = {
table : Nat.t;
get_time : unit -> Mirage_nat.time;
}
2017-03-02 09:52:55 -05:00
let create ~get_time ~max_entries =
let tcp_size = 7 * max_entries / 8 in
let udp_size = max_entries - tcp_size in
Nat.empty ~tcp_size ~udp_size ~icmp_size:100 >|= fun table ->
2017-03-10 11:09:36 -05:00
{ get_time; table }
let translate t packet =
Nat.translate t.table packet >|= function
| Error (`Untranslated | `TTL_exceeded as e) ->
Log.debug (fun f -> f "Failed to NAT %a: %a"
Nat_packet.pp packet
Mirage_nat.pp_error e
);
None
2017-03-07 05:02:54 -05:00
| Ok packet -> Some packet
2017-03-02 09:52:55 -05:00
let random_user_port () =
1024 + Random.int (0xffff - 1024)
2017-03-10 11:09:36 -05:00
let reset t =
Nat.reset t.table
2017-03-02 09:52:55 -05:00
2017-03-10 11:09:36 -05:00
let add_nat_rule_and_translate t ~xl_host action packet =
let now = t.get_time () in
2017-03-02 09:52:55 -05:00
let apply_action xl_port =
2017-03-07 05:02:54 -05:00
Lwt.catch (fun () ->
2017-03-10 11:09:36 -05:00
Nat.add t.table ~now packet (xl_host, xl_port) action
2017-03-02 09:52:55 -05:00
)
(function
| Out_of_memory -> Lwt.return (Error `Out_of_memory)
| x -> Lwt.fail x
)
in
let rec aux ~retries =
let xl_port = random_user_port () in
apply_action xl_port >>= function
| Error `Out_of_memory ->
(* Because hash tables resize in big steps, this can happen even if we have a fair
chunk of free memory. *)
Log.warn (fun f -> f "Out_of_memory adding NAT rule. Dropping NAT table...");
Nat.reset t.table >>= fun () ->
aux ~retries:(retries - 1)
2017-03-02 09:52:55 -05:00
| Error `Overlap when retries < 0 -> Lwt.return (Error "Too many retries")
| Error `Overlap ->
if retries = 0 then (
Log.warn (fun f -> f "Failed to find a free port; resetting NAT table");
Nat.reset t.table >>= fun () ->
aux ~retries:(retries - 1)
) else (
aux ~retries:(retries - 1)
)
2017-03-07 05:02:54 -05:00
| Error `Cannot_NAT ->
Lwt.return (Error "Cannot NAT this packet")
2017-03-02 09:52:55 -05:00
| Ok () ->
Log.debug (fun f -> f "Updated NAT table: %a" Nat.pp_summary t.table);
translate t packet >|= function
| None -> Error "No NAT entry, even after adding one!"
| Some packet ->
Ok packet
2017-03-02 09:52:55 -05:00
in
aux ~retries:100