|
25 | 25 | NetworkEvent, |
26 | 26 | TCPConnect, |
27 | 27 | TLSHandshake, |
| 28 | + OpenVPNHandshake, |
| 29 | + OpenVPNNetworkEvent, |
28 | 30 | maybe_binary_data_to_bytes, |
29 | 31 | ) |
30 | 32 | from oonidata.models.nettests.base_measurement import BaseMeasurement |
|
36 | 38 | TCPObservation, |
37 | 39 | TLSObservation, |
38 | 40 | WebObservation, |
| 41 | + OpenVPNObservation, |
39 | 42 | ) |
40 | 43 | from oonidata.datautils import ( |
41 | 44 | InvalidCertificateChain, |
@@ -760,6 +763,63 @@ def make_measurement_meta(msmt: BaseMeasurement, bucket_date: str) -> Measuremen |
760 | 763 | measurement_start_time=measurement_start_time, |
761 | 764 | ) |
762 | 765 |
|
| 766 | +def count_key_exchange_packets(network_events: List[OpenVPNNetworkEvent]) -> int: |
| 767 | + """ |
| 768 | + return number of packets exchanged in the SENT_KEY state |
| 769 | + """ |
| 770 | + n = 0 |
| 771 | + for evt in network_events: |
| 772 | + if evt.stage == "SENT_KEY" and evt.operation.startswith("packet_"): |
| 773 | + n+=1 |
| 774 | + return n |
| 775 | + |
| 776 | +def measurement_to_openvpn_observation( |
| 777 | + msmt_meta: MeasurementMeta, |
| 778 | + probe_meta: ProbeMeta, |
| 779 | + netinfodb: NetinfoDB, |
| 780 | + openvpn_h: OpenVPNHandshake, |
| 781 | + tcp_connect: Optional[List[TCPConnect]], |
| 782 | + network_events: Optional[List[OpenVPNNetworkEvent]], |
| 783 | +) -> OpenVPNObservation: |
| 784 | + |
| 785 | + oo = OpenVPNObservation( |
| 786 | + measurement_meta=msmt_meta, |
| 787 | + probe_meta=probe_meta, |
| 788 | + failure=normalize_failure(openvpn_h.failure), |
| 789 | + timestamp=make_timestamp(msmt_meta.measurement_start_time, openvpn_h.t), |
| 790 | + success=openvpn_h.failure == None, |
| 791 | + protocol="openvpn", |
| 792 | + ) |
| 793 | + |
| 794 | + if len(tcp_connect) != 0: |
| 795 | + tcp = tcp_connect[0] |
| 796 | + oo.tcp_success = tcp.status.success |
| 797 | + oo.tcp_failure = tcp.status.failure |
| 798 | + oo.tcp_t = tcp.t |
| 799 | + |
| 800 | + oo.handshake_failure = openvpn_h.failure |
| 801 | + oo.handshake_t = openvpn_h.t |
| 802 | + oo.handshake_t0 = openvpn_h.t0 |
| 803 | + |
| 804 | + for evt in network_events: |
| 805 | + if evt.packet is not None: |
| 806 | + if evt.packet.opcode == "P_CONTROL_HARD_RESET_CLIENT_V2": |
| 807 | + oo.openvpn_handshake_hr_client_t = evt.t |
| 808 | + elif evt.packet.opcode == "P_CONTROL_HARD_RESET_SERVER_V2": |
| 809 | + oo.openvpn_handshake_hr_server_t = evt.t |
| 810 | + elif "client_hello" in evt.tags: |
| 811 | + oo.openvpn_handshake_clt_hello_t = evt.t |
| 812 | + elif "server_hello" in evt.tags: |
| 813 | + oo.openvpn_handshake_clt_hello_t = evt.t |
| 814 | + elif evt.operation == "state" and evt.stage == "GOT_KEY": |
| 815 | + oo.openvpn_handshake_got_keys__t = evt.t |
| 816 | + elif evt.operation == "state" and evt.stage == "GENERATED_KEYS": |
| 817 | + oo.openvpn_handshake_gen_keys__t = evt.t |
| 818 | + |
| 819 | + oo.openvpn_handshake_key_exchg_n = count_key_exchange_packets(network_events) |
| 820 | + |
| 821 | + return oo |
| 822 | + |
763 | 823 |
|
764 | 824 | class MeasurementTransformer: |
765 | 825 | """ |
@@ -911,7 +971,7 @@ def consume_web_observations( |
911 | 971 |
|
912 | 972 | It will attempt to map them via the transaction_id or ip:port tuple. |
913 | 973 |
|
914 | | - Any observation that cannot be mapped will be returned inside of it's |
| 974 | + Any observation that cannot be mapped will be returned inside of its |
915 | 975 | own WebObservation with all other columns set to None. |
916 | 976 | """ |
917 | 977 | web_obs_list: List[WebObservation] = [] |
@@ -1008,5 +1068,38 @@ def consume_web_observations( |
1008 | 1068 |
|
1009 | 1069 | return web_obs_list |
1010 | 1070 |
|
| 1071 | + def make_openvpn_observations(self, |
| 1072 | + tcp_connect: Optional[List[TCPConnect]], |
| 1073 | + openvpn_handshakes: Optional[List[OpenVPNHandshake]], |
| 1074 | + network_events: Optional[List[OpenVPNNetworkEvent]], |
| 1075 | + bootstrap_time: float, |
| 1076 | + ) -> List[OpenVPNObservation]: |
| 1077 | + """ |
| 1078 | + Returns a list of OpenVPNObservations by mapping all related |
| 1079 | + TCPObservations, OpenVPNNetworkevents and OpenVPNHandshakes. |
| 1080 | + """ |
| 1081 | + openvpn_obs_list: List[OpenVPNObservation] = [] |
| 1082 | + |
| 1083 | + for openvpn_handshake in openvpn_handshakes: |
| 1084 | + web_obs_list.append( |
| 1085 | + measurement_to_openvpn_observation( |
| 1086 | + msmt_meta=self.measurement_meta, |
| 1087 | + probe_meta=self.probe_meta, |
| 1088 | + netinfodb=self.netinfodb, |
| 1089 | + tcp_connect=tcp_connect, |
| 1090 | + openvpn_handshake=openvpn_handshake, |
| 1091 | + network_events=network_events, |
| 1092 | + ) |
| 1093 | + ) |
| 1094 | + |
| 1095 | + # TODO: can factor out function with web_observation |
| 1096 | + for idx, obs in enumerate(openvpn_obs_list): |
| 1097 | + obs.observation_id = f"{obs.measurement_meta.measurement_uid}_{idx}" |
| 1098 | + obs.created_at = datetime.now(timezone.utc).replace( |
| 1099 | + microsecond=0, tzinfo=None |
| 1100 | + ) |
| 1101 | + |
| 1102 | + return openvpn_obs_list |
| 1103 | + |
1011 | 1104 | def make_observations(self, measurement): |
1012 | 1105 | assert RuntimeError("make_observations is not implemented") |
0 commit comments