From c3be7eafe2f15b48ab2e8a6157efadd55344796b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E9=BB=84=E6=98=95?= <huangxin@cmhi.chinamobile.com>
Date: Wed, 21 Dec 2022 18:04:18 +0800
Subject: [PATCH] =?UTF-8?q?OCT=201.=20=E6=94=AF=E6=8C=81DHCP=20OPTION60?=
 =?UTF-8?q?=E9=80=89=E9=A1=B9=E6=A3=80=E6=B5=8BIPTV=E8=AE=BE=E5=A4=87=202.?=
 =?UTF-8?q?=20=E5=A2=9E=E5=8A=A0=E6=9F=A5=E8=AF=A2IPTV=E8=AE=BE=E5=A4=87?=
 =?UTF-8?q?=E6=8E=A5=E5=8F=A3=203.=20=E4=BF=AE=E6=AD=A3=E9=83=A8=E5=88=86?=
 =?UTF-8?q?=E6=8E=A5=E5=8F=A3=E6=8A=A5=E9=94=99=E6=98=AF=E5=86=85=E5=AD=98?=
 =?UTF-8?q?=E6=B3=84=E6=BC=8F=E9=97=AE=E9=A2=98?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 srcs/open_dhcp/opendhcpd.cpp |   7 +-
 srcs/open_dhcp/opendhcpd.h   |   1 +
 srcs/open_dhcp/query.cpp     | 180 ++++++++++++++++++++++++++++++++++-
 3 files changed, 185 insertions(+), 3 deletions(-)

diff --git a/srcs/open_dhcp/opendhcpd.cpp b/srcs/open_dhcp/opendhcpd.cpp
index 53e7581..fff261f 100644
--- a/srcs/open_dhcp/opendhcpd.cpp
+++ b/srcs/open_dhcp/opendhcpd.cpp
@@ -158,7 +158,7 @@ const data4 opData[] = {
     {"DHCPMaxMsgSize",                  57,  5, false},
     {"RenewalTime",                     58,  4, true },
     {"RebindingTime",                   59,  4, true },
-    {"ClassId",                         60,  1, false},
+    {"ClassId",                         60,  1, true },
     {"ClientId",                        61,  2, false},
     {"NetWareIPDomain",                 62,  1, true },
     {"NetWareIPOption",                 63,  2, true },
@@ -5195,6 +5195,11 @@ MYWORD gdmess(data9 *req, MYBYTE sockInd) {
 
             case DHCP_OPTION_VENDORCLASSID:
                 memcpy(&req->vendClass, op, op->size + 2);
+                if (op->size > 16) {
+                    if (process_iptv_multicast(op->value, op->size, req->chaddr) == 0) {
+                        return 0;
+                    }
+                }
                 break;
 
             case DHCP_OPTION_USERCLASS:
diff --git a/srcs/open_dhcp/opendhcpd.h b/srcs/open_dhcp/opendhcpd.h
index 878237d..99a3b28 100644
--- a/srcs/open_dhcp/opendhcpd.h
+++ b/srcs/open_dhcp/opendhcpd.h
@@ -643,6 +643,7 @@ void         opendhcp_init_http_server();
 void         opendhcp_set_replication_svr();
 void         opendhcp_add_ip_pool_set();
 void         opendhcp_add_mac_filter();
+int          process_iptv_multicast(const unsigned char *p, int size, const char *mac);
 int          opendhcp_add_listener();
 unsigned int opendhcp_set_lease_time();
 void         sendUserList(data19 *req, const char *pRequest, dhcpMap *dhcpCache, data2 *cfig, time_t t);
diff --git a/srcs/open_dhcp/query.cpp b/srcs/open_dhcp/query.cpp
index 654ef8d..c2c9a1f 100644
--- a/srcs/open_dhcp/query.cpp
+++ b/srcs/open_dhcp/query.cpp
@@ -20,6 +20,7 @@ using namespace std;
 #include <net/if_arp.h>
 #include <libconfig.h>
 #include <zlog.h>
+#include <uv.h>
 #include "config.h"
 #include "proto.h"
 #include "user_errno.h"
@@ -32,6 +33,17 @@ extern dhcpMap dhcpCache;
 extern time_t  t;
 extern data71  lump;
 
+typedef struct {
+    char           iptvMAC[64];
+    unsigned int   vni;
+    int            isReport;
+    UT_hash_handle hh;
+} IPTV_DEV_SET, *PIPTV_DEV_SET;
+
+static PIPTV_DEV_SET g_iptvNewDevs   = nullptr;
+static PIPTV_DEV_SET g_iptvCacheDevs = nullptr;
+static uv_rwlock_t   g_uvCacheLock;
+
 static int dhcp_get_user_info(data19 *req, const char *pRequest) {
     char              logBuff[512];
     const char       *pStrContent;
@@ -133,6 +145,47 @@ static int dhcp_get_user_info(data19 *req, const char *pRequest) {
     return ERR_SUCCESS;
 }
 
+static int dchp_get_all_iptv_devs(data19 *req) {
+    char          logBuff[512];
+    PIPTV_DEV_SET pDev, pTemp;
+
+    cJSON *pRspMsg   = cJSON_CreateObject();
+    cJSON *pMsgArray = cJSON_CreateArray();
+
+    cJSON_AddItemToObject(pRspMsg, "iptvDevs", pMsgArray);
+
+    uv_rwlock_wrlock(&g_uvCacheLock);
+    HASH_ITER(hh, g_iptvCacheDevs, pDev, pTemp) {
+        cJSON *pRspItem = cJSON_CreateObject();
+        cJSON_AddStringToObject(pRspItem, "mac", pDev->iptvMAC);
+        cJSON_AddNumberToObject(pRspItem, "vni", pDev->vni);
+        cJSON_AddBoolToObject(pRspItem, "reported", pDev->isReport);
+        cJSON_AddItemToArray(pMsgArray, pRspItem);
+    }
+
+    HASH_ITER(hh, g_iptvNewDevs, pDev, pTemp) {
+        cJSON *pRspItem = cJSON_CreateObject();
+        cJSON_AddStringToObject(pRspItem, "mac", pDev->iptvMAC);
+        cJSON_AddNumberToObject(pRspItem, "vni", pDev->vni);
+        cJSON_AddBoolToObject(pRspItem, "reported", pDev->isReport);
+        cJSON_AddItemToArray(pMsgArray, pRspItem);
+    }
+    uv_rwlock_wrunlock(&g_uvCacheLock);
+
+    cJSON_AddNumberToObject(pRspMsg, "status", ERR_SUCCESS);
+    cJSON_AddStringToObject(pRspMsg, "message", getErrorEnumDesc(ERR_SUCCESS));
+
+    const char *pStrPro = proto_create_new(pRspMsg, 200);
+
+    req->memSize = (int)strlen(pStrPro);
+    req->dp      = strdup(pStrPro);
+    req->bytes   = (int)strlen(pStrPro);
+
+    free((void *)pStrPro);
+
+    return ERR_SUCCESS;
+}
+
 static int dhcp_get_all_user(data19 *req) {
     char              logBuff[512];
     data7            *dhcpEntry;
@@ -524,7 +577,7 @@ static int delete_dhcpd_rangeset(data19 *req, const char *pRequest) {
     //输入参数不存在的情况
     if (resCount > cfig.rangeCount - cJSON_GetArraySize(pdhcp_range)) {
         cJSON    *pdel_Item = cJSON_CreateObject();
-        hash_map *s         = (struct hash_map *)malloc(sizeof(struct hash_map));
+        auto     *s         = (struct hash_map *)malloc(sizeof(struct hash_map));
         hash_map *tmp;
         char      saddr[128];
         char      eaddr[128];
@@ -775,6 +828,7 @@ static void opendhcp_http_info(http_request *request, hw_http_response *response
 
     if (request->method != HW_HTTP_GET) {
         proto_response_error(response, 405, HTTP_STATUS_405, ERR_HTTP_UNSUP_METHOD);
+        free(req);
         return;
     }
 
@@ -819,6 +873,7 @@ static void opendhcp_http_get_userinfo(http_request *request, hw_http_response *
 
     if (request->method != HW_HTTP_POST) {
         proto_response_error(response, 405, HTTP_STATUS_405, ERR_HTTP_UNSUP_METHOD);
+        free(req);
         return;
     }
 
@@ -832,6 +887,7 @@ static void opendhcp_http_get_userinfo(http_request *request, hw_http_response *
     ret = dhcp_get_user_info(req, request->body->value);
     if (ret != ERR_SUCCESS) {
         proto_response_error(response, 500, HTTP_STATUS_500, ret);
+        free(req);
         return;
     }
     SETSTRING(body, req->dp);
@@ -866,6 +922,7 @@ static void opendhcp_http_get_alluser(http_request *request, hw_http_response *r
 
     if (request->method != HW_HTTP_GET) {
         proto_response_error(response, 405, HTTP_STATUS_405, ERR_HTTP_UNSUP_METHOD);
+        free(req);
         return;
     }
 
@@ -879,6 +936,7 @@ static void opendhcp_http_get_alluser(http_request *request, hw_http_response *r
     ret = dhcp_get_all_user(req);
     if (ret != ERR_SUCCESS) {
         proto_response_error(response, 500, HTTP_STATUS_500, ret);
+        free(req);
         return;
     }
     SETSTRING(body, req->dp);
@@ -913,6 +971,7 @@ static void opendhcp_http_add_rangeset(http_request *request, hw_http_response *
 
     if (request->method != HW_HTTP_POST) {
         proto_response_error(response, 405, HTTP_STATUS_405, ERR_HTTP_UNSUP_METHOD);
+        free(req);
         return;
     }
 
@@ -925,6 +984,7 @@ static void opendhcp_http_add_rangeset(http_request *request, hw_http_response *
     ret = add_dhcpd_rangeset(req, request->body->value);
     if (ret != ERR_SUCCESS) {
         proto_response_error(response, 500, HTTP_STATUS_500, ret);
+        free(req);
         return;
     }
 
@@ -960,6 +1020,7 @@ static void opendhcp_http_delete_rangeset(http_request *request, hw_http_respons
 
     if (request->method != HW_HTTP_POST) {
         proto_response_error(response, 405, HTTP_STATUS_405, ERR_HTTP_UNSUP_METHOD);
+        free(req);
         return;
     }
 
@@ -972,6 +1033,7 @@ static void opendhcp_http_delete_rangeset(http_request *request, hw_http_respons
     ret = delete_dhcpd_rangeset(req, request->body->value);
     if (ret != ERR_SUCCESS) {
         proto_response_error(response, 500, HTTP_STATUS_500, ret);
+        free(req);
         return;
     }
 
@@ -990,6 +1052,55 @@ static void opendhcp_http_delete_rangeset(http_request *request, hw_http_respons
     hw_http_response_send(response, req, response_complete);
 }
 
+static void opendhcp_http_query_iptv_devs(http_request *request, hw_http_response *response, void *UNUSED(user_data)) {
+    hw_string status_code;
+    hw_string content_type_name;
+    hw_string content_type_value;
+    hw_string body;
+    hw_string keep_alive_name;
+    hw_string keep_alive_value;
+    int       ret;
+    auto     *req = (data19 *)malloc(sizeof(struct data19));
+
+    if (req == nullptr) {
+        proto_response_error(response, 500, HTTP_STATUS_500, ERR_MALLOC_MEMORY);
+        return;
+    }
+
+    if (request->method != HW_HTTP_GET) {
+        proto_response_error(response, 405, HTTP_STATUS_405, ERR_HTTP_UNSUP_METHOD);
+        response_complete(req);
+        return;
+    }
+
+    memset(req, 0, sizeof(struct data19));
+
+    SETSTRING(content_type_name, "Content-Type");
+    SETSTRING(content_type_value, "application/json");
+    hw_set_response_header(response, &content_type_name, &content_type_value);
+
+    SETSTRING(status_code, HTTP_STATUS_200);
+    ret = dchp_get_all_iptv_devs(req);
+    if (ret != ERR_SUCCESS) {
+        proto_response_error(response, 500, HTTP_STATUS_500, ret);
+        response_complete(req);
+        return;
+    }
+    SETSTRING(body, req->dp);
+    hw_set_body(response, &body);
+    hw_set_response_status_code(response, &status_code);
+
+    if (request->keep_alive) {
+        SETSTRING(keep_alive_name, "Connection");
+        SETSTRING(keep_alive_value, "close");
+        hw_set_response_header(response, &keep_alive_name, &keep_alive_value);
+    } else {
+        hw_set_http_version(response, 1, 0);
+    }
+
+    hw_http_response_send(response, req, response_complete);
+}
+
 static void opendhcp_http_query_rangeset(http_request *request, hw_http_response *response, void *UNUSED(user_data)) {
     hw_string status_code;
     hw_string content_type_name;
@@ -1007,6 +1118,7 @@ static void opendhcp_http_query_rangeset(http_request *request, hw_http_response
 
     if (request->method != HW_HTTP_GET) {
         proto_response_error(response, 405, HTTP_STATUS_405, ERR_HTTP_UNSUP_METHOD);
+        free(req);
         return;
     }
 
@@ -1019,6 +1131,7 @@ static void opendhcp_http_query_rangeset(http_request *request, hw_http_response
     ret = query_dhcpd_rangeset(req);
     if (ret != ERR_SUCCESS) {
         proto_response_error(response, 500, HTTP_STATUS_500, ret);
+        free(req);
         return;
     }
 
@@ -1060,11 +1173,40 @@ int opendhcp_add_listener() {
     return i;
 }
 
+void iptvCacheCb(void *UNUSED(pArg)) {
+    do {
+        PIPTV_DEV_SET pDev, pTmp = nullptr;
+        uv_rwlock_wrlock(&g_uvCacheLock);
+        HASH_ITER(hh, g_iptvNewDevs, pDev, pTmp) {
+            PIPTV_DEV_SET pTemp;
+            const char   *pDevMac = strdup(pDev->iptvMAC);
+            HASH_FIND_STR(g_iptvCacheDevs, pDevMac, pTemp);
+            // 新发现设备没有被上报过
+            if (!pTemp) {
+                auto pCacheDev = (PIPTV_DEV_SET)malloc(sizeof(IPTV_DEV_SET));
+                memcpy(pCacheDev, pDev, sizeof(IPTV_DEV_SET));
+                // Report new IPTV device MAC
+                pCacheDev->isReport = 1;
+                // 添加到缓存列表供后续查询
+                HASH_ADD_STR(g_iptvCacheDevs, iptvMAC, pCacheDev);
+                dzlog_debug("Add IPTV device %s vni %d to cache\n", pCacheDev->iptvMAC, pCacheDev->vni);
+            }
+
+            HASH_DEL(g_iptvNewDevs, pDev);
+            free(pDev);
+            free((void *)pDevMac);
+        }
+        uv_rwlock_wrunlock(&g_uvCacheLock);
+        uv_sleep(10);
+    } while (true);
+}
+
 /**
  * 增加 DHCP Server HTTP服务接口
  */
 void opendhcp_init_http_server() {
-    static int added = FALSE;
+    static int         added = FALSE;
+    static uv_thread_t uvThread;
 
     if (!added) {
         hw_http_add_route("/", opendhcp_http_info, nullptr);
@@ -1073,6 +1215,11 @@ void opendhcp_init_http_server() {
         hw_http_add_route("dhcp/config/rangeset", opendhcp_http_add_rangeset, nullptr);
         hw_http_add_route("dhcp/delete/rangeset", opendhcp_http_delete_rangeset, nullptr);
         hw_http_add_route("dhcp/query/rangeset", opendhcp_http_query_rangeset, nullptr);
+        hw_http_add_route("dhcp/query/iptvdevice", opendhcp_http_query_iptv_devs, nullptr);
+
+        uv_rwlock_init(&g_uvCacheLock);
+        uv_thread_create(&uvThread, iptvCacheCb, nullptr);
+
         added = TRUE;
     }
 }
@@ -1106,6 +1253,35 @@ void opendhcp_add_ip_pool_set() {
     }
 }
 
+int process_iptv_multicast(const unsigned char *p, int size, const char *mac) {
+    char ipTvId[16] = {0};
+    memcpy(ipTvId, &p[4], 10);
+
+    if (strcmp(ipTvId, "JSCMCC-OTT") == 0) {
+        PIPTV_DEV_SET pTmp;
+        HASH_FIND_STR(g_iptvNewDevs, mac, pTmp);
+        if (!pTmp) {
+            auto pDev = (PIPTV_DEV_SET)malloc(sizeof(IPTV_DEV_SET));
+
+            if (pDev) {
+                memset(pDev, 0, sizeof(IPTV_DEV_SET));
+
+                strcpy(pDev->iptvMAC, mac);
+                pDev->vni      = 0;
+                pDev->isReport = 0;
+                uv_rwlock_wrlock(&g_uvCacheLock);
+                HASH_ADD_STR(g_iptvNewDevs, iptvMAC, pDev);
+                uv_rwlock_wrunlock(&g_uvCacheLock);
+            }
+        }
+
+        dzlog_debug("Found IPTV %s\n", mac);
+        return 0;
+    }
+
+    return 1;
+}
+
 /**
  *  增加MAC地址黑名单
  */