Harald Welte | c4ac9e0 | 2021-04-22 23:07:44 +0200 | [diff] [blame] | 1 | module OPCAP_CLIENT_Tests { |
| 2 | |
| 3 | import from OPCAP_Adapter all; |
| 4 | import from OPCAP_Types all; |
| 5 | import from OPCAP_Templates all; |
| 6 | |
| 7 | import from IPL4asp_Types all; |
| 8 | import from IPL4asp_PortType all; |
| 9 | import from Osmocom_Types all; |
| 10 | import from Osmocom_VTY_Functions all; |
| 11 | import from TELNETasp_PortType all; |
| 12 | import from Socket_API_Definitions all; |
| 13 | |
| 14 | type record IpPort { |
| 15 | charstring ip, |
| 16 | integer udp_port |
| 17 | }; |
| 18 | |
| 19 | modulepar { |
| 20 | /* local IP address listening for OPCAP connections */ |
| 21 | charstring mp_local_opcap_ip := "127.0.0.1"; |
| 22 | /* local TCP base port for inbound OPCAP connections */ |
| 23 | integer mp_local_opcap_port := 5000; |
| 24 | |
| 25 | /* IP + port for simulating user traffic */ |
| 26 | IpPort mp_traffic_a := { "127.0.0.23", 44423 }; |
| 27 | IpPort mp_traffic_b := { "127.0.0.42", 44442 }; |
| 28 | }; |
| 29 | |
| 30 | type component test_CT extends OPCAP_Adapter_CT { |
| 31 | timer g_Tguard := 30.0; |
| 32 | |
| 33 | port TELNETasp_PT VTY; |
| 34 | |
| 35 | /* port to generate IP traffic that may or may not be captured */ |
| 36 | port IPL4asp_PT IP; |
Harald Welte | 1c8d16c | 2021-04-25 13:03:41 +0200 | [diff] [blame] | 37 | var integer g_traffic_conn_id; |
Harald Welte | c4ac9e0 | 2021-04-22 23:07:44 +0200 | [diff] [blame] | 38 | }; |
| 39 | |
| 40 | private altstep as_Tguard() runs on test_CT { |
| 41 | [] g_Tguard.timeout { |
| 42 | setverdict(fail, "global guard timeout"); |
| 43 | mtc.stop; |
| 44 | } |
| 45 | } |
| 46 | |
| 47 | /* initialize one of the OPCAP servers, wait for client to connect */ |
| 48 | private function f_init_one_srv(integer idx, template (present) uint32_t linktype) runs on test_CT { |
| 49 | /* start guard timer */ |
| 50 | activate(as_Tguard()); |
| 51 | g_Tguard.start; |
| 52 | log("Waiting for client-", idx, " connection..."); |
| 53 | /* wait for connection */ |
| 54 | f_bind(mp_local_opcap_ip, mp_local_opcap_port+idx, idx); |
| 55 | f_wait_client_connect(idx); |
| 56 | /* wait for file header */ |
| 57 | f_opcap_exp(tr_OPCAP_FILE_HDR(linktype), idx); |
| 58 | }; |
| 59 | |
| 60 | |
| 61 | /* global initialization */ |
| 62 | private function f_init() runs on test_CT { |
| 63 | map(self:VTY, system:VTY); |
| 64 | f_vty_set_prompts(VTY); |
| 65 | f_vty_transceive(VTY, "enable"); |
| 66 | |
| 67 | map(self:IP, system:IP); |
| 68 | var IPL4asp_Types.Result res |
| 69 | |
| 70 | /* 0 -> 1 */ |
| 71 | res := f_IPL4_connect(IP, mp_traffic_b.ip, mp_traffic_b.udp_port, |
| 72 | mp_traffic_a.ip, mp_traffic_a.udp_port, -1, { udp:={} }); |
Harald Welte | 1c8d16c | 2021-04-25 13:03:41 +0200 | [diff] [blame] | 73 | g_traffic_conn_id := res.connId; |
Harald Welte | c4ac9e0 | 2021-04-22 23:07:44 +0200 | [diff] [blame] | 74 | } |
| 75 | |
| 76 | /* generate user traffic from A -> B */ |
| 77 | function f_trafic_pkt_ab(octetstring payload) runs on test_CT { |
Harald Welte | 1c8d16c | 2021-04-25 13:03:41 +0200 | [diff] [blame] | 78 | IP.send(ASP_Send:{g_traffic_conn_id, omit, payload}) |
Harald Welte | c4ac9e0 | 2021-04-22 23:07:44 +0200 | [diff] [blame] | 79 | } |
| 80 | |
| 81 | /* expect a specified UDP payload on the OPCAP connection 'idx' */ |
| 82 | function f_opcap_exp_udp(octetstring udp_payload, integer idx) runs on test_CT { |
| 83 | var octetstring rx_tail; |
| 84 | var integer udp_payload_len, rx_pdu_len; |
| 85 | var OPCAP_PDU rx_pdu; |
| 86 | |
| 87 | udp_payload_len := lengthof(udp_payload); |
| 88 | |
| 89 | /* sadly I couldn't figure out how to create an octetstring template |
| 90 | * for 'match an octetstring ending in 'udp_payload' */ |
| 91 | rx_pdu := f_opcap_exp(tr_OPCAP_PKT(?), idx); |
| 92 | rx_pdu_len := lengthof(rx_pdu.u.packet.payload); |
| 93 | rx_tail := substr(rx_pdu.u.packet.payload, rx_pdu_len - udp_payload_len, udp_payload_len); |
| 94 | if (rx_tail != udp_payload) { |
| 95 | log("captured UDP payload: ", rx_tail, " but expected: ", udp_payload); |
| 96 | setverdict(fail); |
| 97 | } else { |
| 98 | setverdict(pass); |
| 99 | } |
| 100 | } |
| 101 | |
| 102 | /* create an additional pcap-store-connection via the VTY */ |
| 103 | function f_vty_create_addl_connection(integer idx) runs on test_CT |
| 104 | { |
| 105 | f_vty_config3(VTY, { "client", "pcap-store-connectio second-" & int2str(idx) }, |
| 106 | { "server ip " & mp_local_opcap_ip, |
| 107 | "server port " & int2str(mp_local_opcap_port + idx), |
| 108 | "connect" } |
| 109 | ); |
| 110 | } |
| 111 | |
| 112 | |
| 113 | |
| 114 | /* wait for inbound client connection and reception of link header */ |
| 115 | testcase TC_connect_rx_hdr() runs on test_CT |
| 116 | { |
| 117 | f_init(); |
| 118 | f_init_one_srv(0, ?); |
| 119 | setverdict(pass); |
| 120 | } |
| 121 | |
| 122 | /* check if client connection is re-started after a close */ |
| 123 | testcase TC_reconnect(integer idx := 0) runs on test_CT |
| 124 | { |
| 125 | f_init(); |
| 126 | f_init_one_srv(idx, ?); |
| 127 | f_sleep(2.0); |
| 128 | |
| 129 | log("Disconnecting client-", idx); |
| 130 | f_disconnect(idx); |
| 131 | |
| 132 | f_wait_client_connect(idx); |
| 133 | f_opcap_exp(tr_OPCAP_FILE_HDR(?), idx); |
| 134 | setverdict(pass); |
| 135 | } |
| 136 | |
| 137 | /* capture a packet that's within the filter */ |
| 138 | testcase TC_capture() runs on test_CT |
| 139 | { |
| 140 | f_init(); |
| 141 | f_init_one_srv(0, ?); |
| 142 | |
| 143 | for (var integer i := 0; i < 10; i := i + 1) { |
| 144 | var octetstring udp_payload; |
| 145 | |
| 146 | /* we assume 1400 is low enough to avoid IP fragmentation */ |
| 147 | udp_payload := f_rnd_octstring(f_rnd_int(1400)); |
| 148 | f_trafic_pkt_ab(udp_payload); |
| 149 | |
| 150 | f_opcap_exp_udp(udp_payload, 0); |
| 151 | } |
| 152 | } |
| 153 | |
| 154 | /* wait for inbound client connections and reception of link header */ |
| 155 | testcase TC_multi_connect_rx_hdr() runs on test_CT |
| 156 | { |
| 157 | f_init(); |
| 158 | f_init_one_srv(0, ?); |
| 159 | f_vty_create_addl_connection(1); |
| 160 | f_init_one_srv(1, ?); |
| 161 | setverdict(pass); |
| 162 | } |
| 163 | |
| 164 | /* ensure a packet that's within the filter is sent to secondary clients */ |
| 165 | testcase TC_multi_capture() runs on test_CT |
| 166 | { |
| 167 | f_init(); |
| 168 | f_init_one_srv(0, ?); |
| 169 | f_vty_create_addl_connection(1); |
| 170 | f_init_one_srv(1, ?); |
| 171 | |
| 172 | for (var integer i := 0; i < 10; i := i + 1) { |
| 173 | var octetstring udp_payload; |
| 174 | |
| 175 | /* we assume 1400 is low enough to avoid IP fragmentation */ |
| 176 | udp_payload := f_rnd_octstring(f_rnd_int(1400)); |
| 177 | f_trafic_pkt_ab(udp_payload); |
| 178 | |
| 179 | /* expect packet to arrive on both simulated servers */ |
| 180 | f_opcap_exp_udp(udp_payload, 0); |
| 181 | f_opcap_exp_udp(udp_payload, 1); |
| 182 | } |
| 183 | } |
| 184 | |
| 185 | /* TODO: ensure a packet outside the filter is dropped */ |
| 186 | /* TODO: capture of truncated packet */ |
| 187 | /* TODO: stall the receive window */ |
| 188 | /* TODO: different link type (ethernet, not SLL) */ |
| 189 | |
| 190 | |
| 191 | control { |
| 192 | execute( TC_connect_rx_hdr() ); |
| 193 | execute( TC_reconnect() ); |
| 194 | execute( TC_capture() ); |
| 195 | execute( TC_multi_connect_rx_hdr() ); |
| 196 | execute( TC_multi_capture() ); |
| 197 | }; |
| 198 | |
| 199 | |
| 200 | }; |