APID-based virtual switch for SpaceOS inter-guest routing
at main 169 lines 5.7 kB view raw
1open Space_net 2 3let camera : Config.tenant = 4 { 5 name = "camera"; 6 apids = Config.apid_range 0x010 0x01F; 7 can_send_to = [ Config.apid_range 0x020 0x02F ]; 8 } 9 10let processor : Config.tenant = 11 { 12 name = "processor"; 13 apids = Config.apid_range 0x020 0x02F; 14 can_send_to = [ Config.apid_range 0x010 0x01F ]; 15 } 16 17let test_config socket_dir : Config.t = 18 { tenants = [ camera; processor ]; socket_dir } 19 20let routable_frame ~src_apid ~dest_apid typ payload = 21 let base = Space_wire.Msg.v typ ~apid:src_apid payload in 22 { base with reserved = dest_apid } 23 24let read_frame reader = 25 let s = Eio.Buf_read.take Space_wire.Msg.frame_size reader in 26 Wire.Codec.decode Space_wire.Msg.codec (Bytes.of_string s) 0 27 28let write_frame dst frame = 29 let buf = Bytes.make Space_wire.Msg.frame_size '\x00' in 30 Wire.Codec.encode Space_wire.Msg.codec frame buf 0; 31 Eio.Flow.copy_string (Bytes.unsafe_to_string buf) dst 32 33let with_timeout clock f = 34 match Eio.Time.with_timeout clock 2.0 (fun () -> Ok (f ())) with 35 | Ok v -> v 36 | Error `Timeout -> Alcotest.fail "timeout" 37 38let socket_dir prefix = 39 let dir = 40 Filename.concat 41 (Filename.get_temp_dir_name ()) 42 (Fmt.str "%s-%d" prefix (Unix.getpid ())) 43 in 44 (try Unix.mkdir dir 0o755 with Unix.Unix_error (Unix.EEXIST, _, _) -> ()); 45 dir 46 47let setup_switch ~sw ~net ~clock config = 48 let switch = Switch.v ~config () in 49 Switch.run switch ~sw ~net; 50 let cam_path = Config.socket_path config camera in 51 let proc_path = Config.socket_path config processor in 52 let cam_sock = Eio.Net.connect ~sw net (`Unix cam_path) in 53 let proc_sock = Eio.Net.connect ~sw net (`Unix proc_path) in 54 Eio.Time.sleep clock 0.1; 55 (switch, cam_sock, proc_sock) 56 57let test_inter_guest () = 58 Eio_main.run @@ fun env -> 59 Eio.Switch.run @@ fun sw -> 60 let clock = Eio.Stdenv.clock env in 61 let net = Eio.Stdenv.net env in 62 let sd = socket_dir "space-net-inter" in 63 let config = test_config sd in 64 let _switch, cam_sock, proc_sock = setup_switch ~sw ~net ~clock config in 65 let proc_reader = 66 Eio.Buf_read.of_flow 67 ~max_size:(Space_wire.Msg.frame_size * 10) 68 (proc_sock :> _ Eio.Flow.source) 69 in 70 let frame = 71 routable_frame ~src_apid:0x010 ~dest_apid:0x020 TM "hello processor" 72 in 73 write_frame (cam_sock :> _ Eio.Flow.sink) frame; 74 with_timeout clock (fun () -> 75 let received = read_frame proc_reader in 76 Alcotest.(check string) 77 "payload" "hello processor" 78 (Space_wire.Msg.payload_bytes received)) 79 80let test_source_enforcement () = 81 Eio_main.run @@ fun env -> 82 Eio.Switch.run @@ fun sw -> 83 let clock = Eio.Stdenv.clock env in 84 let net = Eio.Stdenv.net env in 85 let sd = socket_dir "space-net-src" in 86 let config = test_config sd in 87 let _switch, cam_sock, _proc_sock = setup_switch ~sw ~net ~clock config in 88 let cam_reader = 89 Eio.Buf_read.of_flow 90 ~max_size:(Space_wire.Msg.frame_size * 10) 91 (cam_sock :> _ Eio.Flow.source) 92 in 93 let bad_frame = 94 routable_frame ~src_apid:0x020 ~dest_apid:0x020 TM "spoofed" 95 in 96 write_frame (cam_sock :> _ Eio.Flow.sink) bad_frame; 97 with_timeout clock (fun () -> 98 let response = read_frame cam_reader in 99 Alcotest.(check int) 100 "error type" 101 (Space_wire.Msg.kind_to_int ERROR) 102 response.Space_wire.Msg.msg_type) 103 104let test_inject () = 105 Eio_main.run @@ fun env -> 106 Eio.Switch.run @@ fun sw -> 107 let clock = Eio.Stdenv.clock env in 108 let net = Eio.Stdenv.net env in 109 let sd = socket_dir "space-net-inj" in 110 let config = test_config sd in 111 let switch, cam_sock, _proc_sock = setup_switch ~sw ~net ~clock config in 112 let cam_reader = 113 Eio.Buf_read.of_flow 114 ~max_size:(Space_wire.Msg.frame_size * 10) 115 (cam_sock :> _ Eio.Flow.source) 116 in 117 let injected = 118 { (Space_wire.Msg.v TC ~apid:0 "from DTN") with reserved = 0x010 } 119 in 120 Switch.inject switch injected; 121 with_timeout clock (fun () -> 122 let received = read_frame cam_reader in 123 Alcotest.(check string) 124 "injected payload" "from DTN" 125 (Space_wire.Msg.payload_bytes received)) 126 127let test_system_handler () = 128 Eio_main.run @@ fun env -> 129 Eio.Switch.run @@ fun sw -> 130 let clock = Eio.Stdenv.clock env in 131 let net = Eio.Stdenv.net env in 132 let sd = socket_dir "space-net-sys" in 133 let config = test_config sd in 134 let system_called = ref false in 135 let on_system _frame = 136 system_called := true; 137 Some (Space_wire.Msg.v Space_wire.Msg.EVR ~apid:0x001 "system ack") 138 in 139 let switch = Switch.v ~config ~on_system () in 140 Switch.run switch ~sw ~net; 141 let cam_path = Config.socket_path config camera in 142 let proc_path = Config.socket_path config processor in 143 let cam_sock = Eio.Net.connect ~sw net (`Unix cam_path) in 144 let _proc_sock = Eio.Net.connect ~sw net (`Unix proc_path) in 145 Eio.Time.sleep clock 0.1; 146 let cam_reader = 147 Eio.Buf_read.of_flow 148 ~max_size:(Space_wire.Msg.frame_size * 10) 149 (cam_sock :> _ Eio.Flow.source) 150 in 151 let frame = 152 routable_frame ~src_apid:0x010 ~dest_apid:0x001 TM "ping system" 153 in 154 write_frame (cam_sock :> _ Eio.Flow.sink) frame; 155 with_timeout clock (fun () -> 156 let response = read_frame cam_reader in 157 Alcotest.(check string) 158 "system response" "system ack" 159 (Space_wire.Msg.payload_bytes response)); 160 Alcotest.(check bool) "system handler called" true !system_called 161 162let suite = 163 ( "switch", 164 [ 165 Alcotest.test_case "inter-guest" `Quick test_inter_guest; 166 Alcotest.test_case "source enforcement" `Quick test_source_enforcement; 167 Alcotest.test_case "inject" `Quick test_inject; 168 Alcotest.test_case "system handler" `Quick test_system_handler; 169 ] )