qubes-mirage-firewall/my_nat.ml

79 lines
2.2 KiB
OCaml
Raw Normal View History

2017-03-02 14:52:55 +00: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 16:09:36 +00:00
| `NAT
| `Redirect of Mirage_nat.endpoint
2017-03-02 14:52:55 +00:00
]
2017-03-10 16:09:36 +00:00
module Nat = Mirage_nat_hashtable
2017-03-02 14:52:55 +00:00
2017-03-10 16:09:36 +00:00
type t = {
table : Nat.t;
get_time : unit -> Mirage_nat.time;
}
2017-03-02 14:52:55 +00:00
2017-03-10 16:09:36 +00:00
let create ~get_time =
Nat.empty () >|= fun table ->
{ 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 10:02:54 +00:00
| Ok packet -> Some packet
2017-03-02 14:52:55 +00:00
let random_user_port () =
1024 + Random.int (0xffff - 1024)
2017-03-10 16:09:36 +00:00
let reset t =
Nat.reset t.table
2017-03-02 14:52:55 +00:00
2017-03-10 16:09:36 +00:00
let add_nat_rule_and_translate t ~xl_host action packet =
let now = t.get_time () in
2017-03-02 14:52:55 +00:00
let apply_action xl_port =
2017-03-07 10:02:54 +00:00
Lwt.catch (fun () ->
2017-03-10 16:09:36 +00:00
Nat.add t.table ~now packet (xl_host, xl_port) action
2017-03-02 14:52:55 +00: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...");
2017-03-10 16:09:36 +00:00
Nat.reset t.table >>= fun () ->
2017-03-02 14:52:55 +00:00
aux ~retries:(retries - 1)
| 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");
2017-03-10 16:09:36 +00:00
Nat.reset t.table >>= fun () ->
2017-03-02 14:52:55 +00:00
aux ~retries:(retries - 1)
) else (
aux ~retries:(retries - 1)
)
2017-03-07 10:02:54 +00:00
| Error `Cannot_NAT ->
Lwt.return (Error "Cannot NAT this packet")
2017-03-02 14:52:55 +00:00
| Ok () ->
translate t packet >|= function
| None -> Error "No NAT entry, even after adding one!"
| Some packet ->
Ok packet
in
aux ~retries:100