diff --git a/.gitignore b/.gitignore index 3fa15bf..1a88130 100644 --- a/.gitignore +++ b/.gitignore @@ -14,3 +14,4 @@ setup.log _build *.native .*.swp +_tests/* diff --git a/.merlin b/.merlin new file mode 100644 index 0000000..bf3fb89 --- /dev/null +++ b/.merlin @@ -0,0 +1,5 @@ +PKG lwt alcotest cstruct tuntap io-page +S lib/ +S lib_test/ +B _build/lib/ +B _build/ diff --git a/.travis.yml b/.travis.yml index d0ae7e4..3f0807a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,5 +2,8 @@ language: c install: wget https://raw.githubusercontent.com/ocaml/ocaml-travisci-skeleton/master/.travis-opam.sh script: bash -ex .travis-opam.sh env: - - OCAML_VERSION=4.01 + global: + - PACKAGE=mirage-net-unix + matrix: - OCAML_VERSION=4.02 + - OCAML_VERSION=4.03 diff --git a/Makefile b/Makefile index 8c9c0a7..6775f2d 100644 --- a/Makefile +++ b/Makefile @@ -12,7 +12,7 @@ setup.bin: setup.ml @rm -f setup.cmx setup.cmi setup.o setup.cmo setup.data: setup.bin - @./setup.bin -configure --enable-tests + @./setup.bin -configure $(CONFIGUREFLAGS) build: setup.data setup.bin @./setup.bin -build -j $(J) @@ -26,7 +26,9 @@ install: setup.bin uninstall: @ocamlfind remove $(NAME) || true -test: setup.bin build +test: setup.bin + @./setup.bin -configure --enable-tests + @./setup.bin -build -j $(J) @./setup.bin -test reinstall: setup.bin diff --git a/_oasis b/_oasis index 027625e..78d96be 100644 --- a/_oasis +++ b/_oasis @@ -21,7 +21,7 @@ Executable test MainIs: test.ml Custom: true Install: false - BuildDepends: lwt, lwt.unix, mirage-net-unix, cstruct, oUnit, io-page.unix, io-page + BuildDepends: lwt, lwt.unix, mirage-net-unix, cstruct, alcotest, io-page.unix, io-page Test test Run$: flag(tests) diff --git a/_tags b/_tags index 8b6b420..9c84f53 100644 --- a/_tags +++ b/_tags @@ -1,5 +1,5 @@ # OASIS_START -# DO NOT EDIT (digest: 7adba8fcc5769d2c5df14f6fc4a2c06a) +# DO NOT EDIT (digest: e2a972a46ecf908b3818d7dca4061cda) # Ignore VCS directories, you can use the same kind of rule outside # OASIS_START/STOP if you want to exclude directories that contains # useless stuff for the build process @@ -23,6 +23,7 @@ true: annot, bin_annot : pkg_mirage-types : pkg_tuntap # Executable test +: pkg_alcotest : pkg_cstruct : pkg_cstruct.lwt : pkg_io-page @@ -30,9 +31,9 @@ true: annot, bin_annot : pkg_lwt : pkg_lwt.unix : pkg_mirage-types -: pkg_oUnit : pkg_tuntap : use_mirage-net-unix +: pkg_alcotest : pkg_cstruct : pkg_cstruct.lwt : pkg_io-page @@ -40,7 +41,6 @@ true: annot, bin_annot : pkg_lwt : pkg_lwt.unix : pkg_mirage-types -: pkg_oUnit : pkg_tuntap : use_mirage-net-unix : custom diff --git a/lib/netif.ml b/lib/netif.ml index d128e8a..ef5134c 100644 --- a/lib/netif.ml +++ b/lib/netif.ml @@ -64,7 +64,6 @@ let err_permission_denied devname = let err_partial_write len' page = fail "tap: partial write (%d, expected %d)" len' page.Cstruct.len - let connect devname = try Random.self_init (); @@ -87,7 +86,9 @@ let connect devname = let disconnect t = log "disconnect %s" t.id; - Tuntap.closetun t.id; + t.active <- false; + Lwt_unix.close t.dev >>= fun () -> + Tuntap.closetap t.id; Lwt.return_unit type macaddr = Macaddr.t @@ -152,30 +153,21 @@ let rec listen t fn = listen t fn | false -> Lwt.return_unit -(* Transmit a packet from an Io_page *) -let write t page = +(* Transmit a packet from a Cstruct.t *) +let write t buffer = let open Cstruct in (* Unfortunately we peek inside the cstruct type here: *) - Lwt_bytes.write t.dev page.buffer page.off page.len >>= fun len' -> + Lwt_bytes.write t.dev buffer.buffer buffer.off buffer.len >>= fun len' -> t.stats.tx_pkts <- Int32.succ t.stats.tx_pkts; - t.stats.tx_bytes <- Int64.add t.stats.tx_bytes (Int64.of_int page.len); - if len' <> page.len then err_partial_write len' page + t.stats.tx_bytes <- Int64.add t.stats.tx_bytes (Int64.of_int buffer.len); + if len' <> buffer.len then err_partial_write len' buffer else Lwt.return_unit -(* TODO use writev: but do a copy for now *) let writev t = function | [] -> Lwt.return_unit | [page] -> write t page | pages -> - let page = Io_page.(to_cstruct (get 1)) in - let off = ref 0 in - List.iter (fun p -> - let len = Cstruct.len p in - Cstruct.blit p 0 page !off len; - off := !off + len; - ) pages; - let v = Cstruct.sub page 0 !off in - write t v + write t @@ Cstruct.concat pages let mac t = t.mac diff --git a/lib_test/test.ml b/lib_test/test.ml index 8051e0d..b1b591b 100644 --- a/lib_test/test.ml +++ b/lib_test/test.ml @@ -15,9 +15,11 @@ *) open Lwt -open OUnit open Printf +let run test = + Lwt_main.run (test ()) + let err_connect e = let buf = Buffer.create 10 in let fmt = Format.formatter_of_buffer buf in @@ -25,26 +27,35 @@ let err_connect e = failwith (Buffer.contents buf) let test_open () = - let thread = - Netif.connect "tap0" >>= function - | `Error e -> err_connect e - | `Ok t -> - printf "connected\n%!"; - Netif.listen t (fun buf -> - printf "got packet of len %d\n%!" (Cstruct.len buf); - Lwt.return_unit - ) - in - Lwt_main.run thread + Netif.connect "tap0" >>= function + | `Error e -> err_connect e + | `Ok _t -> + printf "connected\n%!"; + Lwt.return_unit + +let test_close () = + Netif.connect "tap1" >>= function + | `Error e -> err_connect e + | `Ok t -> + printf "connected\n%!"; + Netif.disconnect t >>= function () -> + printf "disconnected\n%!"; + Lwt.return_unit + +let test_write () = + Netif.connect "tap2" >>= function + | `Error e -> err_connect e + | `Ok t -> + let data = Cstruct.create 4096 in + Netif.writev t [ data ] >>= fun () -> + Netif.writev t [ data ; (Cstruct.create 14) ] >>= fun () -> + Lwt.return_unit + +let suite : Alcotest.test_case list = [ + "connect", `Quick, (fun () -> run test_open) ; + "disconnect", `Quick, (fun () -> run test_close); + "write", `Quick, (fun () -> run test_write); +] let _ = - let verbose = ref false in - Arg.parse [ - "-verbose", Arg.Unit (fun _ -> verbose := true), "Run in verbose mode"; - ] (fun x -> Printf.fprintf stderr "Ignoring argument: %s" x) - "Test unix net driver"; - - let suite = "net" >::: [ - "test open" >:: test_open; - ] in - run_test_tt ~verbose:!verbose suite + Alcotest.run "mirage-net-unix" [ "tests", suite ] diff --git a/myocamlbuild.ml b/myocamlbuild.ml index 7be4842..b454267 100644 --- a/myocamlbuild.ml +++ b/myocamlbuild.ml @@ -1,5 +1,5 @@ (* OASIS_START *) -(* DO NOT EDIT (digest: a0716e785352706021093042bece5760) *) +(* DO NOT EDIT (digest: fcd72908a1049f5e9fbc426b31d2040e) *) module OASISGettext = struct (* # 22 "src/oasis/OASISGettext.ml" *) @@ -29,6 +29,166 @@ module OASISGettext = struct end +module OASISString = struct +(* # 22 "src/oasis/OASISString.ml" *) + + + (** Various string utilities. + + Mostly inspired by extlib and batteries ExtString and BatString libraries. + + @author Sylvain Le Gall + *) + + + let nsplitf str f = + if str = "" then + [] + else + let buf = Buffer.create 13 in + let lst = ref [] in + let push () = + lst := Buffer.contents buf :: !lst; + Buffer.clear buf + in + let str_len = String.length str in + for i = 0 to str_len - 1 do + if f str.[i] then + push () + else + Buffer.add_char buf str.[i] + done; + push (); + List.rev !lst + + + (** [nsplit c s] Split the string [s] at char [c]. It doesn't include the + separator. + *) + let nsplit str c = + nsplitf str ((=) c) + + + let find ~what ?(offset=0) str = + let what_idx = ref 0 in + let str_idx = ref offset in + while !str_idx < String.length str && + !what_idx < String.length what do + if str.[!str_idx] = what.[!what_idx] then + incr what_idx + else + what_idx := 0; + incr str_idx + done; + if !what_idx <> String.length what then + raise Not_found + else + !str_idx - !what_idx + + + let sub_start str len = + let str_len = String.length str in + if len >= str_len then + "" + else + String.sub str len (str_len - len) + + + let sub_end ?(offset=0) str len = + let str_len = String.length str in + if len >= str_len then + "" + else + String.sub str 0 (str_len - len) + + + let starts_with ~what ?(offset=0) str = + let what_idx = ref 0 in + let str_idx = ref offset in + let ok = ref true in + while !ok && + !str_idx < String.length str && + !what_idx < String.length what do + if str.[!str_idx] = what.[!what_idx] then + incr what_idx + else + ok := false; + incr str_idx + done; + if !what_idx = String.length what then + true + else + false + + + let strip_starts_with ~what str = + if starts_with ~what str then + sub_start str (String.length what) + else + raise Not_found + + + let ends_with ~what ?(offset=0) str = + let what_idx = ref ((String.length what) - 1) in + let str_idx = ref ((String.length str) - 1) in + let ok = ref true in + while !ok && + offset <= !str_idx && + 0 <= !what_idx do + if str.[!str_idx] = what.[!what_idx] then + decr what_idx + else + ok := false; + decr str_idx + done; + if !what_idx = -1 then + true + else + false + + + let strip_ends_with ~what str = + if ends_with ~what str then + sub_end str (String.length what) + else + raise Not_found + + + let replace_chars f s = + let buf = Buffer.create (String.length s) in + String.iter (fun c -> Buffer.add_char buf (f c)) s; + Buffer.contents buf + + let lowercase_ascii = + replace_chars + (fun c -> + if (c >= 'A' && c <= 'Z') then + Char.chr (Char.code c + 32) + else + c) + + let uncapitalize_ascii s = + if s <> "" then + (lowercase_ascii (String.sub s 0 1)) ^ (String.sub s 1 ((String.length s) - 1)) + else + s + + let uppercase_ascii = + replace_chars + (fun c -> + if (c >= 'a' && c <= 'z') then + Char.chr (Char.code c - 32) + else + c) + + let capitalize_ascii s = + if s <> "" then + (uppercase_ascii (String.sub s 0 1)) ^ (String.sub s 1 ((String.length s) - 1)) + else + s + +end + module OASISExpr = struct (* # 22 "src/oasis/OASISExpr.ml" *) @@ -129,7 +289,7 @@ module OASISExpr = struct end -# 132 "myocamlbuild.ml" +# 292 "myocamlbuild.ml" module BaseEnvLight = struct (* # 22 "src/base/BaseEnvLight.ml" *) @@ -234,7 +394,7 @@ module BaseEnvLight = struct end -# 237 "myocamlbuild.ml" +# 397 "myocamlbuild.ml" module MyOCamlbuildFindlib = struct (* # 22 "src/plugins/ocamlbuild/MyOCamlbuildFindlib.ml" *) @@ -516,7 +676,7 @@ module MyOCamlbuildBase = struct | nm, [], intf_modules -> ocaml_lib nm; let cmis = - List.map (fun m -> (String.uncapitalize m) ^ ".cmi") + List.map (fun m -> (OASISString.uncapitalize_ascii m) ^ ".cmi") intf_modules in dep ["ocaml"; "link"; "library"; "file:"^nm^".cma"] cmis | nm, dir :: tl, intf_modules -> @@ -529,7 +689,7 @@ module MyOCamlbuildBase = struct ["compile"; "infer_interface"; "doc"]) tl; let cmis = - List.map (fun m -> dir^"/"^(String.uncapitalize m)^".cmi") + List.map (fun m -> dir^"/"^(OASISString.uncapitalize_ascii m)^".cmi") intf_modules in dep ["ocaml"; "link"; "library"; "file:"^dir^"/"^nm^".cma"] cmis) @@ -603,7 +763,7 @@ module MyOCamlbuildBase = struct end -# 606 "myocamlbuild.ml" +# 766 "myocamlbuild.ml" open Ocamlbuild_plugin;; let package_default = { @@ -618,7 +778,7 @@ let conf = {MyOCamlbuildFindlib.no_automatic_syntax = false} let dispatch_default = MyOCamlbuildBase.dispatch_default conf package_default;; -# 622 "myocamlbuild.ml" +# 782 "myocamlbuild.ml" (* OASIS_STOP *) Ocamlbuild_plugin.dispatch dispatch_default;; (* Ocamlbuild_pack.Flags.mark_tag_used "tests";; *) diff --git a/opam b/opam index 776f903..6e4dc45 100644 --- a/opam +++ b/opam @@ -15,12 +15,12 @@ build: [make] install: [make "install"] remove: ["ocamlfind" "remove" "mirage-net-unix"] depends: [ - "cstruct" {>= "1.0.1"} + "cstruct" {>= "1.7.1"} "ocamlfind" "lwt" {>= "2.4.3"} "mirage-types" {>= "2.3.0"} "io-page" {>= "1.0.1"} "tuntap" {>= "1.3.0"} - "ounit" {build} + "alcotest" {test} ] -available: [ ocaml-version >= "4.00.0"] +available: [ ocaml-version >= "4.02.3"] diff --git a/setup.ml b/setup.ml index b9e7e52..daa531e 100644 --- a/setup.ml +++ b/setup.ml @@ -1,9 +1,9 @@ (* setup.ml generated for the first time by OASIS v0.3.0 *) (* OASIS_START *) -(* DO NOT EDIT (digest: c372dff9745db242f53b3ae816014804) *) +(* DO NOT EDIT (digest: 67c2cb933d479b10d979d2ce355fda10) *) (* - Regenerated by OASIS v0.4.5 + Regenerated by OASIS v0.4.6 Visit http://oasis.forge.ocamlcore.org for more information and documentation about functions used in this file. *) @@ -246,6 +246,33 @@ module OASISString = struct String.iter (fun c -> Buffer.add_char buf (f c)) s; Buffer.contents buf + let lowercase_ascii = + replace_chars + (fun c -> + if (c >= 'A' && c <= 'Z') then + Char.chr (Char.code c + 32) + else + c) + + let uncapitalize_ascii s = + if s <> "" then + (lowercase_ascii (String.sub s 0 1)) ^ (String.sub s 1 ((String.length s) - 1)) + else + s + + let uppercase_ascii = + replace_chars + (fun c -> + if (c >= 'a' && c <= 'z') then + Char.chr (Char.code c - 32) + else + c) + + let capitalize_ascii s = + if s <> "" then + (uppercase_ascii (String.sub s 0 1)) ^ (String.sub s 1 ((String.length s) - 1)) + else + s end @@ -315,19 +342,15 @@ module OASISUtils = struct let compare_csl s1 s2 = - String.compare (String.lowercase s1) (String.lowercase s2) + String.compare (OASISString.lowercase_ascii s1) (OASISString.lowercase_ascii s2) module HashStringCsl = Hashtbl.Make (struct type t = string - - let equal s1 s2 = - (String.lowercase s1) = (String.lowercase s2) - - let hash s = - Hashtbl.hash (String.lowercase s) + let equal s1 s2 = (compare_csl s1 s2) = 0 + let hash s = Hashtbl.hash (OASISString.lowercase_ascii s) end) module SetStringCsl = @@ -365,7 +388,7 @@ module OASISUtils = struct else buf in - String.lowercase buf + OASISString.lowercase_ascii buf end @@ -471,7 +494,7 @@ module PropList = struct order = Queue.create (); name_norm = (if case_insensitive then - String.lowercase + OASISString.lowercase_ascii else fun s -> s); } @@ -1822,13 +1845,13 @@ module OASISUnixPath = struct let capitalize_file f = let dir = dirname f in let base = basename f in - concat dir (String.capitalize base) + concat dir (OASISString.capitalize_ascii base) let uncapitalize_file f = let dir = dirname f in let base = basename f in - concat dir (String.uncapitalize base) + concat dir (OASISString.uncapitalize_ascii base) end @@ -2890,7 +2913,7 @@ module OASISFileUtil = struct end -# 2893 "setup.ml" +# 2916 "setup.ml" module BaseEnvLight = struct (* # 22 "src/base/BaseEnvLight.ml" *) @@ -2995,7 +3018,7 @@ module BaseEnvLight = struct end -# 2998 "setup.ml" +# 3021 "setup.ml" module BaseContext = struct (* # 22 "src/base/BaseContext.ml" *) @@ -5406,7 +5429,7 @@ module BaseSetup = struct end -# 5409 "setup.ml" +# 5432 "setup.ml" module InternalConfigurePlugin = struct (* # 22 "src/plugins/internal/InternalConfigurePlugin.ml" *) @@ -5845,8 +5868,8 @@ module InternalInstallPlugin = struct let make_fnames modul sufx = List.fold_right begin fun sufx accu -> - (String.capitalize modul ^ sufx) :: - (String.uncapitalize modul ^ sufx) :: + (OASISString.capitalize_ascii modul ^ sufx) :: + (OASISString.uncapitalize_ascii modul ^ sufx) :: accu end sufx @@ -6270,7 +6293,7 @@ module InternalInstallPlugin = struct end -# 6273 "setup.ml" +# 6296 "setup.ml" module OCamlbuildCommon = struct (* # 22 "src/plugins/ocamlbuild/OCamlbuildCommon.ml" *) @@ -6648,7 +6671,7 @@ module OCamlbuildDocPlugin = struct end -# 6651 "setup.ml" +# 6674 "setup.ml" module CustomPlugin = struct (* # 22 "src/plugins/custom/CustomPlugin.ml" *) @@ -6796,7 +6819,7 @@ module CustomPlugin = struct end -# 6799 "setup.ml" +# 6822 "setup.ml" open OASISTypes;; let setup_t = @@ -6969,7 +6992,7 @@ let setup_t = FindlibPackage ("lwt.unix", None); InternalLibrary "mirage-net-unix"; FindlibPackage ("cstruct", None); - FindlibPackage ("oUnit", None); + FindlibPackage ("alcotest", None); FindlibPackage ("io-page.unix", None); FindlibPackage ("io-page", None) ]; @@ -7018,8 +7041,8 @@ let setup_t = plugin_data = [] }; oasis_fn = Some "_oasis"; - oasis_version = "0.4.5"; - oasis_digest = Some "\247\254\236\191S\n\152mpd9H\021\217]\195"; + oasis_version = "0.4.6"; + oasis_digest = Some "\237\027\014*\248\241\216Gj\0314M~\164 8"; oasis_exec = None; oasis_setup_args = []; setup_update = false @@ -7027,6 +7050,6 @@ let setup_t = let setup () = BaseSetup.setup setup_t;; -# 7031 "setup.ml" +# 7054 "setup.ml" (* OASIS_STOP *) let () = setup ();;