From 85ebda434d44f93e656ee5d3e52dc258134495d0 Mon Sep 17 00:00:00 2001
From: Nardi Ivan <nardi.ivan@gmail.com>
Date: Wed, 31 Jul 2024 18:26:13 +0200
Subject: OpenVPN, Wireguard: improve sub-classification

Allow sub-classification of OpenVPN/Wireguard flows using their server IP.
That is useful to detect the specific VPN application/app used.
At the moment, the supported protocols are: Mullvad, NordVPN, ProtonVPN.

This feature is configurable.
---
 src/lib/ndpi_main.c           |  4 ++++
 src/lib/protocols/openvpn.c   | 16 +++++++++++++---
 src/lib/protocols/wireguard.c | 25 ++++++++++++++++++++-----
 3 files changed, 37 insertions(+), 8 deletions(-)

(limited to 'src/lib')

diff --git a/src/lib/ndpi_main.c b/src/lib/ndpi_main.c
index 5f8711ca7..a234f7afe 100644
--- a/src/lib/ndpi_main.c
+++ b/src/lib/ndpi_main.c
@@ -11430,6 +11430,10 @@ static const struct cfg_param {
 
   { "rtp",           "search_for_stun",                         "disable", NULL, NULL, CFG_PARAM_ENABLE_DISABLE, __OFF(rtp_search_for_stun), NULL },
 
+  { "openvpn",       "subclassification_by_ip",                 "enable", NULL, NULL, CFG_PARAM_ENABLE_DISABLE, __OFF(openvpn_subclassification_by_ip), NULL },
+
+  { "wireguard",     "subclassification_by_ip",                 "enable", NULL, NULL, CFG_PARAM_ENABLE_DISABLE, __OFF(wireguard_subclassification_by_ip), NULL },
+
   { "$PROTO_NAME_OR_ID", "log",                                 "disable", NULL, NULL, CFG_PARAM_PROTOCOL_ENABLE_DISABLE, __OFF(debug_bitmask), NULL },
   { "$PROTO_NAME_OR_ID", "ip_list.load",                        "1", NULL, NULL, CFG_PARAM_PROTOCOL_ENABLE_DISABLE, __OFF(ip_list_bitmask), NULL },
 
diff --git a/src/lib/protocols/openvpn.c b/src/lib/protocols/openvpn.c
index 3eb30cd8a..a56af25be 100644
--- a/src/lib/protocols/openvpn.c
+++ b/src/lib/protocols/openvpn.c
@@ -60,6 +60,16 @@
 #define P_PACKET_ID_ARRAY_LEN_OFFSET(hmac_size)  (P_HARD_RESET_PACKET_ID_OFFSET(hmac_size) + 8 * (!!(hmac_size)))
 
 
+static void ndpi_int_openvpn_add_connection(struct ndpi_detection_module_struct * const ndpi_struct,
+                                            struct ndpi_flow_struct * const flow)
+{
+  if(ndpi_struct->cfg.openvpn_subclassification_by_ip &&
+     ndpi_struct->proto_defaults[flow->guessed_protocol_id_by_ip].protoCategory == NDPI_PROTOCOL_CATEGORY_VPN) {
+    ndpi_set_detected_protocol(ndpi_struct, flow, flow->guessed_protocol_id_by_ip, NDPI_PROTOCOL_OPENVPN, NDPI_CONFIDENCE_DPI);
+  } else {
+    ndpi_set_detected_protocol(ndpi_struct, flow, NDPI_PROTOCOL_OPENVPN, NDPI_PROTOCOL_UNKNOWN, NDPI_CONFIDENCE_DPI);
+  }
+}
 
 static int is_opcode_valid(u_int8_t opcode)
 {
@@ -193,14 +203,14 @@ static void ndpi_search_openvpn(struct ndpi_detection_module_struct* ndpi_struct
        flow->packet_direction_counter[!dir] >= 2) {
       /* (2) */
       NDPI_LOG_INFO(ndpi_struct,"found openvpn (session ids match on both direction)\n");
-	ndpi_set_detected_protocol(ndpi_struct, flow, NDPI_PROTOCOL_OPENVPN, NDPI_PROTOCOL_UNKNOWN, NDPI_CONFIDENCE_DPI);
+	ndpi_int_openvpn_add_connection(ndpi_struct, flow);
 	return;
       }
     if(flow->packet_direction_counter[dir] >= 4 &&
        flow->packet_direction_counter[!dir] == 0) {
       /* (3) */
       NDPI_LOG_INFO(ndpi_struct,"found openvpn (asymmetric)\n");
-      ndpi_set_detected_protocol(ndpi_struct, flow, NDPI_PROTOCOL_OPENVPN, NDPI_PROTOCOL_UNKNOWN, NDPI_CONFIDENCE_DPI);
+      ndpi_int_openvpn_add_connection(ndpi_struct, flow);
       return;
     }
   } else {
@@ -231,7 +241,7 @@ static void ndpi_search_openvpn(struct ndpi_detection_module_struct* ndpi_struct
 
           if(memcmp(flow->ovpn_session_id[!dir], session_remote, 8) == 0) {
             NDPI_LOG_INFO(ndpi_struct,"found openvpn\n");
-            ndpi_set_detected_protocol(ndpi_struct, flow, NDPI_PROTOCOL_OPENVPN, NDPI_PROTOCOL_UNKNOWN, NDPI_CONFIDENCE_DPI);
+            ndpi_int_openvpn_add_connection(ndpi_struct, flow);
             return;
 	  } else {
             NDPI_LOG_DBG2(ndpi_struct, "key mismatch 0x%lx\n", ndpi_ntohll(*(u_int64_t *)session_remote));
diff --git a/src/lib/protocols/wireguard.c b/src/lib/protocols/wireguard.c
index 86e457872..f8abf31cb 100644
--- a/src/lib/protocols/wireguard.c
+++ b/src/lib/protocols/wireguard.c
@@ -40,6 +40,21 @@ enum wg_message_type {
   WG_TYPE_TRANSPORT_DATA = 4
 };
 
+static void ndpi_int_wireguard_add_connection(struct ndpi_detection_module_struct * const ndpi_struct,
+                                              struct ndpi_flow_struct * const flow,
+                                              u_int16_t app_protocol)
+{
+  if(ndpi_struct->cfg.wireguard_subclassification_by_ip &&
+     ndpi_struct->proto_defaults[flow->guessed_protocol_id_by_ip].protoCategory == NDPI_PROTOCOL_CATEGORY_VPN) {
+    ndpi_set_detected_protocol(ndpi_struct, flow, flow->guessed_protocol_id_by_ip, NDPI_PROTOCOL_WIREGUARD, NDPI_CONFIDENCE_DPI);
+  } else if(app_protocol != NDPI_PROTOCOL_UNKNOWN) {
+    ndpi_set_detected_protocol(ndpi_struct, flow, app_protocol, NDPI_PROTOCOL_WIREGUARD, NDPI_CONFIDENCE_DPI);
+  } else {
+    ndpi_set_detected_protocol(ndpi_struct, flow, NDPI_PROTOCOL_WIREGUARD, NDPI_PROTOCOL_UNKNOWN, NDPI_CONFIDENCE_DPI);
+  }
+}
+
+
 static void ndpi_search_wireguard(struct ndpi_detection_module_struct *ndpi_struct,
 				  struct ndpi_flow_struct *flow)
 {
@@ -109,7 +124,7 @@ static void ndpi_search_wireguard(struct ndpi_detection_module_struct *ndpi_stru
 
     if(flow->num_processed_pkts > 1) {
       /* This looks like a retransmission and probably this communication is blocked hence let's stop here */
-      ndpi_set_detected_protocol(ndpi_struct, flow, NDPI_PROTOCOL_WIREGUARD, NDPI_PROTOCOL_UNKNOWN, NDPI_CONFIDENCE_DPI);
+      ndpi_int_wireguard_add_connection(ndpi_struct, flow, NDPI_PROTOCOL_UNKNOWN);
       return;
     } 
     /* need more packets before deciding */
@@ -125,9 +140,9 @@ static void ndpi_search_wireguard(struct ndpi_detection_module_struct *ndpi_stru
 
       if (receiver_index == flow->l4.udp.wireguard_peer_index[1 - packet->packet_direction]) {
         if(packet->payload_packet_len == 100)
-          ndpi_set_detected_protocol(ndpi_struct, flow, NDPI_PROTOCOL_TUNNELBEAR, NDPI_PROTOCOL_WIREGUARD, NDPI_CONFIDENCE_DPI);
+          ndpi_int_wireguard_add_connection(ndpi_struct, flow, NDPI_PROTOCOL_TUNNELBEAR);
         else
-          ndpi_set_detected_protocol(ndpi_struct, flow, NDPI_PROTOCOL_WIREGUARD, NDPI_PROTOCOL_UNKNOWN, NDPI_CONFIDENCE_DPI);
+          ndpi_int_wireguard_add_connection(ndpi_struct, flow, NDPI_PROTOCOL_UNKNOWN);
       } else {
         NDPI_EXCLUDE_PROTO(ndpi_struct, flow);
       }
@@ -143,7 +158,7 @@ static void ndpi_search_wireguard(struct ndpi_detection_module_struct *ndpi_stru
     if (flow->l4.udp.wireguard_stage == 2 - packet->packet_direction) {
       u_int32_t receiver_index = get_u_int32_t(payload, 4);
       if (receiver_index == flow->l4.udp.wireguard_peer_index[1 - packet->packet_direction]) {
-        ndpi_set_detected_protocol(ndpi_struct, flow, NDPI_PROTOCOL_WIREGUARD, NDPI_PROTOCOL_UNKNOWN, NDPI_CONFIDENCE_DPI);
+        ndpi_int_wireguard_add_connection(ndpi_struct, flow, NDPI_PROTOCOL_UNKNOWN);
       } else {
         NDPI_EXCLUDE_PROTO(ndpi_struct, flow);
       }
@@ -171,7 +186,7 @@ static void ndpi_search_wireguard(struct ndpi_detection_module_struct *ndpi_stru
       /* need more packets before deciding */
     } else if (flow->l4.udp.wireguard_stage == 5) {
       if (receiver_index == flow->l4.udp.wireguard_peer_index[packet->packet_direction]) {
-        ndpi_set_detected_protocol(ndpi_struct, flow, NDPI_PROTOCOL_WIREGUARD, NDPI_PROTOCOL_UNKNOWN, NDPI_CONFIDENCE_DPI);
+        ndpi_int_wireguard_add_connection(ndpi_struct, flow, NDPI_PROTOCOL_UNKNOWN);
       } else {
         NDPI_EXCLUDE_PROTO(ndpi_struct, flow);
       }
-- 
cgit v1.2.3