(* * Copyright (c) 2015-16 Magnus Skjegstad * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. *) let (>>=) = Lwt.(>>=) module type Backend = sig include Vnetif.BACKEND val create : unit -> t end (** This backend enforces an Ethernet frame size. *) module Frame_size_enforced = struct module X = Basic_backend.Make type t = { xt : X.t; mutable frame_size : int; } let register t = X.register t.xt let unregister t id = X.unregister t.xt id let mac t id = X.mac t.xt id let set_listen_fn t id buf = X.set_listen_fn t.xt id buf let unregister_and_flush t id = X.unregister_and_flush t.xt id let write t id ~size fill = if size > t.frame_size then Lwt.return (Error `Invalid_length) else X.write t.xt id ~size fill let set_frame_size t m = t.frame_size <- m let set_max_ip_mtu t m = t.frame_size <- m + Ethernet.Packet.sizeof_ethernet let create ~frame_size () = let xt = X.create ~use_async_readers:true ~yield:(fun() -> Lwt.pause () ) () in { xt ; frame_size } let create () = create ~frame_size:(1500 + Ethernet.Packet.sizeof_ethernet) () end (** This backend adds a random number of trailing bytes to each frame *) module Trailing_bytes : Backend = struct module X = Basic_backend.Make include X let max_bytes_to_add = 10 (* Just adds trailing bytes, doesn't store anything in them *) let add_random_bytes src = let bytes_to_add = Random.int max_bytes_to_add in let len = Cstruct.length src in let dst = Cstruct.create (len + bytes_to_add) in Cstruct.blit src 0 dst 0 len; dst let set_listen_fn t id fn = (* Add random bytes before returning result to real listener *) X.set_listen_fn t id (fun buf -> fn (add_random_bytes buf)) let create () = X.create ~use_async_readers:true ~yield:(fun() -> Lwt.pause () ) () end (** This backend drops packets *) module Uniform_packet_loss : Backend = struct module X = Basic_backend.Make include X let drop_p = 0.01 let write t id ~size fill = if Random.float 1.0 < drop_p then Lwt.return (Ok ()) (* drop packet *) else X.write t id ~size fill (* pass to real write *) let create () = X.create ~use_async_readers:true ~yield:(fun() -> Lwt.pause () ) () end (** This backend uniformly drops packets with no payload *) module Uniform_no_payload_packet_loss : Backend = struct module X = Basic_backend.Make include X (* We assume that packets with payload are usually filled. We could make the * payload check more accurate by parsing the packet properly. *) let no_payload_len = 100 (* Drop probability, if no payload *) let drop_p = 0.10 let write t id ~size fill = if size <= no_payload_len && Random.float 1.0 < drop_p then Lwt.return (Ok ()) (* drop packet *) else X.write t id ~size fill (* pass to real write *) let create () = X.create ~use_async_readers:true ~yield:(fun() -> Lwt.pause () ) () end (** This backend drops packets for 1 second after 1 megabyte has been * transferred *) module Drop_1_second_after_1_megabyte : Backend = struct module X = Basic_backend.Make type t = { xt : X.t; mutable sent_bytes : int; mutable is_dropping : bool; mutable done_dropping : bool; } let byte_limit : int = 1_000_000 let time_to_sleep : float = 1.0 let register t = X.register t.xt let unregister t id = X.unregister t.xt id let mac t id = X.mac t.xt id let set_listen_fn t id buf = X.set_listen_fn t.xt id buf let unregister_and_flush t id = X.unregister_and_flush t.xt id let should_drop t = if (t.sent_bytes > byte_limit) && (t.is_dropping = false) && (t.done_dropping = false) then begin Logs.info (fun f -> f "Backend dropping packets for %f sec" time_to_sleep); t.is_dropping <- true; Lwt.async(fun () -> Lwt_unix.sleep time_to_sleep >>= fun () -> t.done_dropping <- true; t.is_dropping <- false; Logs.info (fun f -> f "Stopped dropping"); Lwt.return_unit ); true end else begin if t.is_dropping = true then true else false end let write t id ~size fill = t.sent_bytes <- t.sent_bytes + size; if should_drop t then Lwt.return (Ok ()) else X.write t.xt id ~size fill (* pass to real write *) let create () = let xt = X.create ~use_async_readers:true ~yield:(fun() -> Lwt.pause ()) () in { xt ; done_dropping = false; is_dropping = false; sent_bytes = 0 } end (** This backend has a global on/off switch which drops all the packets *) module On_off_switch = struct module X = Basic_backend.Make include X let send_packets = ref true let write t id ~size fill = if not !send_packets then begin Logs.info (fun f -> f "write dropping 1 packet"); Lwt.return (Ok ()) (* drop packet *) end else X.write t id ~size fill (* pass to real write *) let create () = X.create ~use_async_readers:true ~yield:(fun() -> Lwt.pause () ) () end (** This backend delivers all packets unmodified *) module Basic : Backend = struct module X = Basic_backend.Make include X let create () = X.create ~use_async_readers:true ~yield:(fun() -> Lwt.pause () ) () end