qubes-mirage-firewall/my_nat.ml

72 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 = [
| `Rewrite
| `Redirect of Ipaddr.t * int
]
type t = Nat : (module Mirage_nat.S with type t = 't) * 't -> t
2017-03-02 14:52:55 +00:00
let create (type t) (nat:(module Mirage_nat.S with type t = t)) (table:t) =
let (module Nat : Mirage_nat.S with type t = t) = nat in
Nat (nat, table)
2017-03-02 14:52:55 +00:00
let translate (Nat ((module Nat), table)) packet =
Nat.translate table packet >|= function
2017-03-07 10:02:54 +00:00
| Error `Untranslated -> None
| Ok packet -> Some packet
2017-03-02 14:52:55 +00:00
let random_user_port () =
1024 + Random.int (0xffff - 1024)
let reset (Nat ((module Nat), table)) =
Nat.reset table
2017-03-02 14:52:55 +00:00
let add_nat_rule_and_translate ((Nat ((module Nat), table)) as t) ~xl_host action packet =
2017-03-02 14:52:55 +00:00
let apply_action xl_port =
2017-03-07 10:02:54 +00:00
Lwt.catch (fun () ->
match action with
| `Rewrite ->
Nat.add_nat table packet (xl_host, xl_port)
2017-03-07 10:02:54 +00:00
| `Redirect target ->
Nat.add_redirect table packet (xl_host, xl_port) target
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...");
Nat.reset 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");
Nat.reset 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