diff --git a/configure.ac b/configure.ac index 5bef7d8..0bd527e 100644 --- a/configure.ac +++ b/configure.ac @@ -41,6 +41,16 @@ AC_ARG_ENABLE([unitTestDockerSupport], [echo "Docker support is disabled"]) AM_CONDITIONAL([UNIT_TEST_DOCKER_SUPPORT], [test x$UNIT_TEST_DOCKER_SUPPORT = xtrue]) +# Checks for Voice_support +AC_ARG_ENABLE([voice_mta_support], +[ --enable-voice_mta_support=val Turn on voice support, val=yes or no], +[case "${enableval}" in + yes) voice_support=yes;; + no) voice_support=no;; + *) AC_MSG_ERROR([bad value ${enableval} for --enable-voice_mta_support]) ;; +esac],[voice_mta_support=no]) +AM_CONDITIONAL(VOICE_MTA_SUPPORT, test x"$voice_support" = x"yes") + AC_PREFIX_DEFAULT(`pwd`) AC_ENABLE_SHARED AC_DISABLE_STATIC diff --git a/source/TR-181/integration_src.shared/cosa_x_cisco_com_mta_apis.c b/source/TR-181/integration_src.shared/cosa_x_cisco_com_mta_apis.c index f4ed8fc..c56c407 100644 --- a/source/TR-181/integration_src.shared/cosa_x_cisco_com_mta_apis.c +++ b/source/TR-181/integration_src.shared/cosa_x_cisco_com_mta_apis.c @@ -77,6 +77,10 @@ #include "safec_lib_common.h" #include "sysevent/sysevent.h" #include "ctype.h" +#if defined (VOICE_MTA_SUPPORT) +#include "voice_dhcp_hal.h" +#include "bcm_generic_hal.h" +#endif // #include "cosa_x_cisco_com_mta_internal.h" @@ -164,6 +168,145 @@ int mtaReapplytr104Conf(void) #endif // MTA_TR104SUPPORT +#if defined (VOICE_MTA_SUPPORT) + +/* + * @brief Set the voice interface name in brcm based on syscfg value. + * If the syscfg value is not set, default to "mta0". + * If the default interface name from bcm is different from the syscfg value, update it in bcm using setParameterValues API. +*/ +void setVoiceIfname(void) +{ + char cVoiceSupportIfaceName[32] = { 0 }; + syscfg_get(NULL, "VoiceSupport_IfaceName",cVoiceSupportIfaceName, sizeof(cVoiceSupportIfaceName)); + AnscTraceInfo(("%s:%d, VoiceSupport_IfaceName from syscfg is %s\n", __FUNCTION__, __LINE__, cVoiceSupportIfaceName)); + + if (0 == strlen(cVoiceSupportIfaceName)) + { + AnscTraceError(("VoiceSupport_IfaceName is not set in syscfg\n")); + snprintf(cVoiceSupportIfaceName, sizeof(cVoiceSupportIfaceName), "%s", "mta0"); + AnscTraceInfo(("Defaulting VoiceSupport_IfaceName to %s\n", cVoiceSupportIfaceName)); + } + char cFullpath[256] = "Device.Services.VoiceService.1.X_BROADCOM_COM_BoundIfName"; + char cValue[128] = { 0 }; + + BcmRet rc; + char *nameArray[1] = { cFullpath }; + BcmGenericParamInfo *getParamInfoArray = NULL; + UINT32 numParamInfo = 0; + + rc = bcm_generic_getParameterValues((const char **)nameArray, 1, FALSE, 0, + &getParamInfoArray, &numParamInfo); + if (BCMRET_SUCCESS == rc) + { + if (1 == numParamInfo) + { + AnscTraceInfo(("%s:%d, Value:%s\n", __FUNCTION__, __LINE__, getParamInfoArray[0].value)); + snprintf(cValue, sizeof(cValue), "%s", getParamInfoArray[0].value); + } + bcm_generic_freeParamInfoArray(&getParamInfoArray, numParamInfo); + } + + if (BCMRET_SUCCESS != rc || strlen(cValue) == 0 || strcmp(cVoiceSupportIfaceName, cValue) != 0) + { + BcmGenericParamInfo setParamInfoArray[1] = { 0 }; + + /* Fill config structure */ + setParamInfoArray[0].fullpath = cFullpath; + setParamInfoArray[0].type = "string"; + setParamInfoArray[0].value = cVoiceSupportIfaceName; + + AnscTraceInfo(("%s:%d, Setting %s to %s\n", __FUNCTION__, __LINE__, cFullpath, cVoiceSupportIfaceName)); + rc = bcm_generic_setParameterValues(setParamInfoArray, 1, 0); + if (BCMRET_SUCCESS != rc) { + AnscTraceError(("setParamString: bcm_generic_setParameterValues failed for %s\n", + cFullpath)); + } + } +} + +/* + * @brief Set firewall rule for voice interface using sysevent. If pCommand is NULL or empty, the firewall rule will be removed. +*/ +static void setFirewallRule(char * pCommand, uint8_t ui8Enable) +{ + int syseventFd = -1; + token_t syseventToken; + + syseventFd = sysevent_open("127.0.0.1", SE_SERVER_WELL_KNOWN_PORT, SE_VERSION, "Firewall", &syseventToken); + if (syseventFd < 0) + { + AnscTraceError(("%s:%d - sysevent_open failed\n", __FUNCTION__, __LINE__)); + return; + } + + AnscTraceInfo(("%s: ui8Enable=%d\n", __FUNCTION__, ui8Enable)); + AnscTraceInfo(("%s: Calling sysevent command: %s\n", __FUNCTION__, pCommand)); + if (ui8Enable) + { + if (NULL == pCommand || '\0' == pCommand[0]) + { + AnscTraceError(("%s: Invalid command string\n", __FUNCTION__)); + sysevent_close(syseventFd, syseventToken); + return; + } + sysevent_set(syseventFd, syseventToken, "VoiceIpRule", pCommand, 0); + } + else + sysevent_unset(syseventFd, syseventToken, "VoiceIpRule"); + + AnscTraceInfo(("%s: Calling sysevent firewall-restart\n", __FUNCTION__)); + sysevent_set(syseventFd, syseventToken, "firewall-restart", "", 0); + sysevent_close(syseventFd, syseventToken); +} + +static char voiceInterface[32] = { 0 }; + +static uint8_t cbSubsIfInfo(char *pIntfName, uint8_t enable) +{ + if (enable) + { + /* Save voice interface selection */ + strncpy(voiceInterface, pIntfName, sizeof(voiceInterface)); + } + + return 1; +} + +static uint8_t cbGetCertInfo(VoiceCertificateInfoType *pCertInfo) +{ + UNREFERENCED_PARAMETER(pCertInfo); + + return 0; +} + +static uint8_t cbSetFirewallRule(VoiceFirewallRuleType *pFirewallRule) +{ + char command[1000] = { 0 }; + char protocol[10] = "UDP"; + + /* Default all protocol to UDP except TCP. Do not support "TCP or UDP" option. */ + if (!strcmp("TCP", pFirewallRule->protocol)) + { + snprintf(protocol, sizeof(protocol), "%s", "TCP"); + } + + AnscTraceInfo(("%s: enable=%d, ifName=%s, protocol=%s, destPort=%u\n", + __FUNCTION__, pFirewallRule->enable, + pFirewallRule->ifName, + protocol, + pFirewallRule->destinationPort)); + if (pFirewallRule->enable) + { + snprintf(command, sizeof(command), "-A INPUT -p %s -i %s --dport %u -j ACCEPT", + protocol, pFirewallRule->ifName, pFirewallRule->destinationPort); + } + setFirewallRule(command, pFirewallRule->enable); + return 1; +} + + +#endif /* VOICE_MTA_SUPPORT */ ANSC_STATUS CosaDmlMTAInit @@ -177,8 +320,27 @@ CosaDmlMTAInit // PCOSA_DATAMODEL_MTA pMyObject = (PCOSA_DATAMODEL_MTA)phContext; + AnscTraceInfo(("CosaDmlMTAInit: mta_hal_InitDB() \n")); + if ( mta_hal_InitDB() == RETURN_OK ) + { +#if defined (VOICE_MTA_SUPPORT) + char cPartnerId[64] = { 0 }; + syscfg_get(NULL, "PartnerID", cPartnerId, sizeof(cPartnerId)); + if ('\0' != cPartnerId[0] && strcmp(cPartnerId, "comcast") == 0) + { + AnscTraceInfo(("CosaDmlMTAInit: voice_hal_register_cb() \n")); + setVoiceIfname(); + /* Register callback functions */ + voice_hal_register_cb(cbSubsIfInfo, cbSetFirewallRule, cbGetCertInfo); + } + else + { + AnscTraceInfo(("CosaDmlMTAInit: Not registering callbacks since partnerId is %s\n", cPartnerId)); + } +#endif return ANSC_STATUS_SUCCESS; + } else return ANSC_STATUS_FAILURE; } diff --git a/source/TR-181/middle_layer_src/Makefile.am b/source/TR-181/middle_layer_src/Makefile.am index 76dacfe..e481321 100644 --- a/source/TR-181/middle_layer_src/Makefile.am +++ b/source/TR-181/middle_layer_src/Makefile.am @@ -26,3 +26,7 @@ libCcspMtaAgent_middle_layer_src_la_CFLAGS = $(AM_CFLAGS) $(MTA_CFLAGS) libCcspMtaAgent_middle_layer_src_la_CPPFLAGS = -I$(top_srcdir)/source/TR-181/board_sbapi -I$(top_srcdir)/source/TR-181/middle_layer_src -I$(top_srcdir)/source/TR-181/include -I$(top_srcdir)/source/Custom libCcspMtaAgent_middle_layer_src_la_SOURCES = cosa_x_cisco_com_mta_internal.c plugin_main.c plugin_main_apis.c cosa_x_cisco_com_mta_dml.c ../../MtaAgentSsp/TR104_webconfig.c libCcspMtaAgent_middle_layer_src_la_LDFLAGS = -lccsp_common -lhal_mta -lmsgpackc + +if VOICE_MTA_SUPPORT +libCcspMtaAgent_middle_layer_src_la_SOURCES += cosa_rbus_apis.c cosa_voice_apis.c +endif diff --git a/source/TR-181/middle_layer_src/cosa_rbus_apis.c b/source/TR-181/middle_layer_src/cosa_rbus_apis.c new file mode 100644 index 0000000..060866b --- /dev/null +++ b/source/TR-181/middle_layer_src/cosa_rbus_apis.c @@ -0,0 +1,456 @@ +/* + * If not stated otherwise in this file or this component's LICENSE file the + * following copyright and licenses apply: + * + * Copyright 2026 RDK Management + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +*/ + +#include "ccsp_trace.h" +#include "ccsp_psm_helper.h" +#include "cosa_rbus_apis.h" +#include "cosa_voice_apis.h" +#include "syscfg/syscfg.h" + + +#define DHCPv4_VOICE_SUPPORT_PARAM "dmsb.voicesupport.Interface.IP.DHCPV4Interface" +#define DHCP_MGR_DHCPv4_TABLE "Device.DHCPv4.Client" +#define DHCP_MGR_DHCPv6_TABLE "Device.DHCPv6.Client" + +rbusHandle_t voiceRbusHandle = NULL; +extern ANSC_HANDLE bus_handle; +const char cSubsystem[ ]= "eRT."; +static char cBaseParam[32] = {0}; + +/** +* @brief Convert parameter value type to rbus value type. +* +* @param[in] paramType Parameter value type to be converted. +* @return Corresponding rbus value type. +*/ + +static rbusValueType_t convertRbusDataType(paramValueType_t paramType) +{ + rbusValueType_t rbusType = RBUS_NONE; + switch (paramType) + { + case BOOLEAN_PARAM: + rbusType = RBUS_BOOLEAN; + break; + case STRING_PARAM: + rbusType = RBUS_STRING; + break; + default: + CcspTraceWarning(("%s: Unsupported param type %d\n", __FUNCTION__, paramType)); + break; + } + return rbusType; +} + +/** + * @brief Set parameter in DHCP manager via RBUS. + * + * This function sets the specified parameter in the DHCP manager + * using RBUS. It constructs an rbus value from the provided parameter + * value and type, and performs the set operation. Errors during the + * process are logged appropriately. + * + * @param[in] pParamName Name of the parameter to be set. + * @param[in] pParamValue Value of the parameter to be set. + * @param[in] paramType Type of the parameter value. +*/ +static void setParamInDhcpMgr(const char * pParamName, const char * pParamValue, paramValueType_t paramType) +{ + if (voiceRbusHandle == NULL || pParamName == NULL || pParamValue == NULL) + { + CcspTraceError(("%s: Invalid rbus handle or NULL parameter\n", __FUNCTION__)); + return; + } + int iRet = -1; + rbusValue_t rbusValue; + rbusValueType_t rbusType = convertRbusDataType(paramType); + if (rbusType == RBUS_NONE) + { + CcspTraceError(("%s: Unsupported param type %d for param %s\n", __FUNCTION__, paramType, pParamName)); + return; + } + + rbusValue_Init(&rbusValue); + if (false == rbusValue_SetFromString(rbusValue, rbusType, pParamValue)) + { + CcspTraceError(("%s: rbusValue_SetFromString failed for param %s with value %s\n", __FUNCTION__, pParamName, pParamValue)); + rbusValue_Release(rbusValue); + return; + } + + iRet = rbus_set(voiceRbusHandle, pParamName, rbusValue, NULL); + if (iRet != RBUS_ERROR_SUCCESS) + { + CcspTraceError(("%s: rbus_set failed for param %s with error code %d\n", __FUNCTION__, pParamName, iRet)); + } + else + { + CcspTraceInfo(("%s: rbus_set successful for param %s with value %s\n", __FUNCTION__, pParamName, pParamValue)); + } + rbusValue_Release(rbusValue); +} + +/** + * @brief Retrieve interface index information for the MTA interface. + * + * This function retrieves Interface index information for the MTA interface + * from the PSM (Persistent Storage Manager) and logs the retrieved value. + * It reads the parameter "dmsb.voicesupport.Interface.IP.DHCPV4Interface" + * from the PSM and stores it in a static variable for later use. If the + * retrieval fails, it logs an error message. + */ + +void getIfaceIndexInfo(void) +{ + int iRetPsmGet = CCSP_SUCCESS; + char *pParamValue= NULL; + + iRetPsmGet = PSM_Get_Record_Value2(bus_handle, cSubsystem, DHCPv4_VOICE_SUPPORT_PARAM, NULL, &pParamValue); + if (iRetPsmGet != CCSP_SUCCESS) + { + CcspTraceError(("%s: PSM_Get_Record_Value2 failed for param %s with error code %d\n", __FUNCTION__, DHCPv4_VOICE_SUPPORT_PARAM, + iRetPsmGet)); + /* Set a safe default base parameter to avoid constructing invalid RBUS paths later. */ + snprintf(cBaseParam, sizeof(cBaseParam), "%s", DHCP_MGR_DHCPv4_TABLE); + return; + } + else + { + CcspTraceInfo(("%s: PSM_Get_Record_Value2 successful for param %s with value %s\n", __FUNCTION__, DHCPv4_VOICE_SUPPORT_PARAM, + pParamValue)); + if (strlen(pParamValue) == 0) + { + CcspTraceWarning(("%s: Retrieved empty value for param %s, using default base parameter\n", __FUNCTION__, DHCPv4_VOICE_SUPPORT_PARAM)); + snprintf(cBaseParam, sizeof(cBaseParam), "%s", DHCP_MGR_DHCPv4_TABLE); + } + else + { + snprintf(cBaseParam, sizeof(cBaseParam), "%s",pParamValue); + } + ((CCSP_MESSAGE_BUS_INFO *)bus_handle)->freefunc((char *)pParamValue); + } +} + +/** + * @brief Initialize the RBUS handle used by this module. + * + * This function initializes and/or acquires the RBUS handle required + * for subsequent RBUS operations performed by this component. It + * should be called before any other APIs in this module that depend + * on an active RBUS connection. + */ +void initRbusHandle(void) +{ + rbusError_t rbusReturn = RBUS_ERROR_SUCCESS; + if (voiceRbusHandle != NULL) + { + CcspTraceInfo(("%s: rbus handle already initialized\n", __FUNCTION__)); + return; + } + rbusReturn = rbus_open(&voiceRbusHandle, "VoiceSupportMtaInterface"); + if (rbusReturn != RBUS_ERROR_SUCCESS) + { + CcspTraceError(("%s: rbus_open failed with error code %d\n", __FUNCTION__, rbusReturn)); + return; + } + CcspTraceInfo(("%s: rbus_open successful\n", __FUNCTION__)); +} + +/** + * @brief Enable or configure IPv4 DHCP for the specified MTA interface. + * + * This function enables DHCPv4 for the MTA (e.g., telephony) interface + * identified by the given interface name. The interface name must be a + * valid, null-terminated string referring to an existing network + * interface on the device. + * Also it updates the interface name parameter in DHCP manager via RBUS. + * + * @param[in] pIfaceName Name of the MTA network interface for which + * DHCPv4 should be enabled. + */ +void enableDhcpv4ForMta(const char * pIfaceName) +{ + char cParamName[64] = {0}; + char cPartnerId[64] = { 0 }; + syscfg_get(NULL, "PartnerID", cPartnerId, sizeof(cPartnerId)); + if ('\0' != cPartnerId[0] && strcmp(cPartnerId, "comcast") == 0) + { + snprintf(cParamName, sizeof(cParamName), "%s.Interface", cBaseParam); + setParamInDhcpMgr(cParamName, pIfaceName, STRING_PARAM); + + snprintf(cParamName, sizeof(cParamName), "%s.Enable", cBaseParam); + setParamInDhcpMgr(cParamName, "true", BOOLEAN_PARAM); + } + else + { + CcspTraceInfo(("%s: Partner ID:%s is not Comcast, skipping DHCPv4 configuration\n", __FUNCTION__, cPartnerId)); + } +} +/** + * @brief DHCP client events handler for MTA interface. + * + * This function handles DHCP client events received via RBUS for the + * MTA interface. It processes events related to DHCPv4 and DHCPv6 + * clients, extracts relevant information, and logs the details. + * + * @param[in] voiceRbusHandle RBUS handle used for event handling. + * @param[in] pRbusEvent Pointer to the received RBUS event. + * @param[in] pRbusSubscription Pointer to the RBUS event subscription. + */ +static void dhcpClientEventsHandler(rbusHandle_t voiceRbusHandle, rbusEvent_t const* pRbusEvent, rbusEventSubscription_t* pRbusSubscription) +{ + (void)voiceRbusHandle; + (void)pRbusSubscription; + if (pRbusEvent == NULL) + { + CcspTraceError(("%s: Received NULL event\n", __FUNCTION__)); + return; + } + CcspTraceInfo(("%s: Received event %s\n", __FUNCTION__, pRbusEvent->name)); + + pthread_t dhcpEventThreadId; + + if(strstr(pRbusEvent->name, DHCP_MGR_DHCPv4_TABLE) || strstr(pRbusEvent->name, DHCP_MGR_DHCPv6_TABLE)) + { + if (0 == access("/tmp/dumpDHCPevent.txt", F_OK)) + { + FILE *fp = fopen("/tmp/dhcp_event_dump.txt", "a"); + if(fp) + { + CcspTraceError(("Writing DHCP event data to /tmp/dhcp_event_dump.txt\n")); + fprintf(fp, "Event Name: %s\n", pRbusEvent->name); + fprintf(fp, "Event Data:\n"); + rbusObject_fwrite(pRbusEvent->data, 0, fp); + fprintf(fp, "\n-----------------------\n"); + fclose(fp); + } + } + // Unwrap the data - check if it's wrapped in "initialValue" (from publishOnSubscribe) + rbusObject_t dataObj = pRbusEvent->data; + rbusValue_t initialValue = rbusObject_GetValue(dataObj, "initialValue"); + if (NULL != initialValue) + { + // Check the type before unwrapping + rbusValueType_t valueType = rbusValue_GetType(initialValue); + + if (valueType == RBUS_OBJECT) + { + // Valid lease data wrapped in object + dataObj = rbusValue_GetObject(initialValue); + CcspTraceInfo(("%s: Unwrapped initialValue object\n", __FUNCTION__)); + } + else if (valueType == RBUS_STRING) + { + // Empty string (no lease available yet) + const char *pStrVal = rbusValue_GetString(initialValue, NULL); + if (NULL == pStrVal || strlen(pStrVal) == 0) + { + CcspTraceInfo(("%s: Empty initialValue - no lease available yet\n", __FUNCTION__)); + return; + } + } + else + { + CcspTraceWarning(("%s: Unexpected initialValue type %d\n", __FUNCTION__, valueType)); + return; + } + } + + DhcpEventData_t *pDhcpEvtData = (DhcpEventData_t *)malloc(sizeof(DhcpEventData_t)); + if (pDhcpEvtData == NULL) + { + CcspTraceError(("%s: Memory allocation failed for DhcpEventData_t\n", __FUNCTION__)); + return; + } + memset(pDhcpEvtData, 0, sizeof(DhcpEventData_t)); + pDhcpEvtData->dhcpVersion = strstr(pRbusEvent->name, DHCP_MGR_DHCPv4_TABLE) ? DHCP_IPv4 : DHCP_IPv6; + + rbusValue_t rbusValue = rbusObject_GetValue(dataObj, "IfName"); + if (NULL == rbusValue) + { + CcspTraceInfo(("%s: No IfName in event (empty data)\n", __FUNCTION__)); + free(pDhcpEvtData); + return; + } + const char *pIfaceName = rbusValue_GetString(rbusValue, NULL); + if (NULL == pIfaceName || strlen(pIfaceName) == 0) + { + CcspTraceError(("%s: Invalid interface name in DHCP event\n", __FUNCTION__)); + free(pDhcpEvtData); + return; + } + snprintf(pDhcpEvtData->cIfaceName, sizeof(pDhcpEvtData->cIfaceName), "%s", pIfaceName); + CcspTraceInfo(("%s: DHCP %s event for interface %s\n", __FUNCTION__, + (pDhcpEvtData->dhcpVersion == DHCP_IPv4) ? "IPv4" : "IPv6", pDhcpEvtData->cIfaceName)); + + rbusValue = rbusObject_GetValue(dataObj, "MsgType"); + if (NULL == rbusValue) + { + CcspTraceInfo(("%s: No MsgType in event (empty data)\n", __FUNCTION__)); + free(pDhcpEvtData); + return; + } + pDhcpEvtData->dhcpMsgType = (DHCP_MESSAGE_TYPE)rbusValue_GetUInt32(rbusValue); + + CcspTraceInfo(("%s: DHCP Message Type %d\n", __FUNCTION__, pDhcpEvtData->dhcpMsgType)); + + if (DHCP_LEASE_UPDATE == pDhcpEvtData->dhcpMsgType || DHCP_LEASE_RENEW == pDhcpEvtData->dhcpMsgType || DHCP_LEASE_DEL == pDhcpEvtData->dhcpMsgType || \ + DHCP_CLIENT_STARTED == pDhcpEvtData->dhcpMsgType || DHCP_CLIENT_STOPPED == pDhcpEvtData->dhcpMsgType || DHCP_CLIENT_FAILED == pDhcpEvtData->dhcpMsgType) + { + int iByteLen = 0; + rbusValue = rbusObject_GetValue(dataObj, "LeaseInfo"); + if (NULL == rbusValue) + { + CcspTraceInfo(("%s: No LeaseInfo in event for DHCP Message Type %d\n", __FUNCTION__, pDhcpEvtData->dhcpMsgType)); + free(pDhcpEvtData); + return; + } + const uint8_t* pLeaseInfo = rbusValue_GetBytes(rbusValue, &iByteLen); + if (pLeaseInfo != NULL && iByteLen > 0) + { + if (DHCP_IPv4 == pDhcpEvtData->dhcpVersion) + { + CcspTraceInfo(("%s: Processing DHCPv4 LeaseInfo of size %d\n", __FUNCTION__, iByteLen)); + CcspTraceInfo(("%s:%d, DHCP_MGR_IPV4_MSG size: %ld\n", __FUNCTION__, __LINE__, sizeof(DHCP_MGR_IPV4_MSG))); + if (sizeof(DHCP_MGR_IPV4_MSG) == iByteLen) + { + memcpy(&pDhcpEvtData->leaseInfo.dhcpV4Msg, pLeaseInfo, sizeof(DHCP_MGR_IPV4_MSG)); + CcspTraceInfo(("%s:%d, DHCPv4 Lease Info - Ifname: %s, Address: %s, Netmask: %s, Gateway: %s\n", + __FUNCTION__, __LINE__, + pDhcpEvtData->leaseInfo.dhcpV4Msg.ifname, + pDhcpEvtData->leaseInfo.dhcpV4Msg.address, + pDhcpEvtData->leaseInfo.dhcpV4Msg.netmask, + pDhcpEvtData->leaseInfo.dhcpV4Msg.gateway)); + CcspTraceInfo(("%s:%d, DHCPv4 Lease Info - DNS Server: %s, DNS Server1: %s, TimeZone: %s\n", + __FUNCTION__, __LINE__, + pDhcpEvtData->leaseInfo.dhcpV4Msg.dnsServer, + pDhcpEvtData->leaseInfo.dhcpV4Msg.dnsServer1, + pDhcpEvtData->leaseInfo.dhcpV4Msg.timeZone)); + CcspTraceInfo(("%s:%d, DHCPv4 Lease Info - MTU Size: %u, Time Offset: %d, IsTimeOffsetAssigned: %d\n", + __FUNCTION__, __LINE__, + pDhcpEvtData->leaseInfo.dhcpV4Msg.mtuSize, + pDhcpEvtData->leaseInfo.dhcpV4Msg.timeOffset, + pDhcpEvtData->leaseInfo.dhcpV4Msg.isTimeOffsetAssigned)); + CcspTraceInfo(("%s:%d, DHCPv4 Lease Info - Upstream Rate: %u, Downstream Rate: %u\n", + __FUNCTION__, __LINE__, + pDhcpEvtData->leaseInfo.dhcpV4Msg.upstreamCurrRate, + pDhcpEvtData->leaseInfo.dhcpV4Msg.downstreamCurrRate)); + CcspTraceInfo(("%s:%d, DHCPv4 Lease Info - MTA Option 122: %s, MTA Option 67: %s, TFTP Server: %s\n", + __FUNCTION__, __LINE__, + pDhcpEvtData->leaseInfo.dhcpV4Msg.cOption122, + pDhcpEvtData->leaseInfo.dhcpV4Msg.cOption67, + pDhcpEvtData->leaseInfo.dhcpV4Msg.cTftpServer)); + CcspTraceInfo(("%s:%d, DHCPv4 Lease Info - HostName: %s, DomainName: %s\n", + __FUNCTION__, __LINE__, + pDhcpEvtData->leaseInfo.dhcpV4Msg.cHostName, + pDhcpEvtData->leaseInfo.dhcpV4Msg.cDomainName)); + } + else + { + CcspTraceError(("%s: Invalid DHCPv4 LeaseInfo size %d\n", __FUNCTION__, iByteLen)); + } + } + } + else + { + CcspTraceError(("%s: NULL or empty LeaseInfo in DHCP event\n", __FUNCTION__)); + free(pDhcpEvtData); + return; + } + } + else + { + CcspTraceInfo(("%s: No LeaseInfo for DHCP Message Type %d\n", __FUNCTION__, pDhcpEvtData->dhcpMsgType)); + free(pDhcpEvtData); + return; + } + if (pthread_create(&dhcpEventThreadId, NULL, dhcpClientEventsHandlerThread, (void *)pDhcpEvtData) != 0) + { + CcspTraceError(("%s: Failed to create DHCP event processing thread\n", __FUNCTION__)); + free(pDhcpEvtData); + return; + } + pthread_detach(dhcpEventThreadId); + usleep(10000); // Sleep for 10ms to allow thread to start + } + else + { + CcspTraceWarning(("%s: Unrecognized event %s\n", __FUNCTION__, pRbusEvent->name)); + } +} + +void * eventSubscriptionThread(void * pArg) +{ + (void)pArg; // Unused parameter + char cEventName[64] = {0}; + + snprintf(cEventName, sizeof(cEventName), "%s.Events", cBaseParam); + CcspTraceInfo(("%s:%d, Subscribing to DHCP client events for MTA interface with event name %s\n", __FUNCTION__, __LINE__, cEventName)); + + rbusEventSubscription_t rbusEventSubscription = { + .eventName = cEventName, + .filter = NULL, + .interval = 0, + .duration = 0, + .handler = dhcpClientEventsHandler, + .userData = NULL, + .handle = NULL, + .asyncHandler = NULL, + .publishOnSubscribe = true + }; + + if ('\0' == cEventName[0]) + { + CcspTraceError(("%s: Event name is empty, cannot subscribe\n", __FUNCTION__)); + return NULL; + } + CcspTraceInfo(("%s:%d, Subscribing to event %s in event subscription thread\n", __FUNCTION__, __LINE__, rbusEventSubscription.eventName)); + rbusError_t rbusRet = rbusEvent_SubscribeEx (voiceRbusHandle, &rbusEventSubscription, 1/* Number of subscriptions */, 5 /* retry interval in seconds */); + if (rbusRet != RBUS_ERROR_SUCCESS) + { + CcspTraceError(("%s: rbus_event_subscribe failed for event %s with error code %d\n", __FUNCTION__, rbusEventSubscription.eventName, rbusRet)); + } + else + { + CcspTraceInfo(("%s: rbus_event_subscribe successful for event %s\n", __FUNCTION__, rbusEventSubscription.eventName)); + } + pthread_exit(NULL); +} +/** + * @brief Subscribe to DHCP client events for MTA interface. + * This function sets up subscriptions to listen for DHCP client + * events related to the MTA interface. + */ + +void subscribeDhcpClientEvents(void) +{ + if (NULL == voiceRbusHandle) + { + CcspTraceError(("%s: rbus handle is NULL, cannot subscribe to events\n", __FUNCTION__)); + return; + } + + pthread_t eventSubscriptionThreadId = -1; + if (pthread_create(&eventSubscriptionThreadId, NULL, (void *)eventSubscriptionThread, NULL) != 0) + { + CcspTraceError(("%s: Failed to create event subscription thread\n", __FUNCTION__)); + return; + } + pthread_detach(eventSubscriptionThreadId); +} diff --git a/source/TR-181/middle_layer_src/cosa_rbus_apis.h b/source/TR-181/middle_layer_src/cosa_rbus_apis.h new file mode 100644 index 0000000..a3ebd20 --- /dev/null +++ b/source/TR-181/middle_layer_src/cosa_rbus_apis.h @@ -0,0 +1,93 @@ +/* + * If not stated otherwise in this file or this component's LICENSE file the + * following copyright and licenses apply: + * + * Copyright 2026 RDK Management + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +*/ + +#ifndef COSA_RBUS_APIS_H +#define COSA_RBUS_APIS_H + +#include "ipc_msg.h" + +typedef enum +{ + BOOLEAN_PARAM = 0, /**< bool true or false */ + STRING_PARAM, /**< null-terminated string */ + PARAM_NONE +} paramValueType_t; + +typedef enum +{ + DHCP_IPv4=0, + DHCP_IPv6, + DHCP_BOTH, + DHCP_NONE +}DhcpVersion; + +typedef struct _DhcpEventData +{ + char cIfaceName[32]; + DHCP_MESSAGE_TYPE dhcpMsgType; + DhcpVersion dhcpVersion; + union + { + DHCP_MGR_IPV4_MSG dhcpV4Msg; + DHCP_MGR_IPV6_MSG dhcpV6Msg; + }leaseInfo; +} DhcpEventData_t; +/* +* @brief Retrieve interface index information for the MTA interface. + * + * This function retrieves Interface index information for the MTA interface + * from the PSM (Persistent Storage Manager) and logs the retrieved value. + * It reads the parameter "dmsb.voicesupport.Interface.IP.DHCPV4Interface" + * from the PSM and stores it in a static variable for later use. If the + * retrieval fails, it logs an error message. + */ + +void getIfaceIndexInfo(void); +/** + * @brief Initialize the RBUS handle used by this module. + * + * This function initializes and/or acquires the RBUS handle required + * for subsequent RBUS operations performed by this component. It + * should be called before any other APIs in this module that depend + * on an active RBUS connection. + */ +void initRbusHandle(void); + +/** + * @brief Enable or configure IPv4 DHCP for the specified MTA interface. + * + * This function enables DHCPv4 for the MTA (e.g., telephony) interface + * identified by the given interface name. The interface name must be a + * valid, null-terminated string referring to an existing network + * interface on the device. + * Also it updates the interface name parameter in DHCP manager via RBUS. + * + * @param[in] pIfaceName Name of the MTA network interface for which + * DHCPv4 should be enabled. + */ +void enableDhcpv4ForMta(const char * pIfaceName); + +/** + * @brief Subscribe to DHCP client events for MTA interface. + * This function sets up subscriptions to listen for DHCP client + * events related to the MTA interface. + */ +void subscribeDhcpClientEvents(void); +#endif /* COSA_RBUS_APIS_H */ + diff --git a/source/TR-181/middle_layer_src/cosa_voice_apis.c b/source/TR-181/middle_layer_src/cosa_voice_apis.c new file mode 100644 index 0000000..a4b8a6e --- /dev/null +++ b/source/TR-181/middle_layer_src/cosa_voice_apis.c @@ -0,0 +1,564 @@ +/* + * If not stated otherwise in this file or this component's LICENSE file the + * following copyright and licenses apply: + * + * Copyright 2026 RDK Management + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +*/ +#include "ccsp_trace.h" +#include "ccsp_psm_helper.h" +#include "cosa_x_cisco_com_mta_internal.h" +#include "cosa_rbus_apis.h" +#include "voice_dhcp_hal.h" +#include "syscfg/syscfg.h" +#include +#include + +#define VOICE_SUPPORT_MODE_IPV4_ONLY "IPv4_Only" +#define VOICE_SUPPORT_MODE_DUAL_STACK "Dual_Stack" + +pthread_mutex_t voiceDataProcessingMutex = PTHREAD_MUTEX_INITIALIZER; +/** + * @brief Read the EMTA MAC address from the factory NVRAM file. + * + * This helper function scans the file "/tmp/factory_nvram.data" for a line + * beginning with the literal prefix "EMTA " and, if found, copies the + * remainder of that line (after the prefix and with the trailing newline + * removed) into the caller-supplied buffer as a null-terminated string. + * + * The MAC address format is whatever textual representation is stored in + * the file (for example, a hex string with or without separators); the + * string is copied verbatim without validation or normalization. + * + * @param[in,out] pMacAddress + * Pointer to a character buffer that receives the MAC address string. + * The pointer must be non-NULL and reference a buffer of at least + * 32 bytes in size to ensure sufficient space for the MAC address + * + * @note + * This function does not return a status; on failure to open or parse + * the file, it logs an error via CcspTraceError and leaves the contents + * of pMacAddress unchanged or partially unchanged. + */ +static void readMacAddress (char * pMacAddress) +{ + FILE *pFILE = fopen("/tmp/factory_nvram.data", "r"); + if (pFILE != NULL) + { + char cLine[128] = {0}; + while (fgets(cLine, sizeof(cLine), pFILE) != NULL) + { + if (strncmp(cLine, "EMTA ",5) == 0) + { + char *pMac = cLine + 5; + pMac[strcspn(pMac, "\n")] = 0; // Remove newline character + strncpy(pMacAddress, pMac, 32); + break; + } + } + fclose(pFILE); + } + else + { + CcspTraceError(("%s: Failed to open /tmp/factory_nvram.data\n", __FUNCTION__)); + } +} +/* + * @brief Check if the specified network interface is already up. + * + * This helper function checks whether the given network interface is + * currently in the "up" state by querying its flags via ioctl. + * + * @param[in] pIfaceName + * Pointer to a null-terminated string containing the name of the + * network interface to check (e.g., "mta0"). + * @return + * Returns true if the interface is up, false otherwise. +*/ +static bool IsIfaceAlreadyUp(char *pIfaceName) +{ + bool isUp = false; + if (NULL == pIfaceName) + { + CcspTraceError(("%s: NULL parameters are passed \n", __FUNCTION__)); + return false; + } + + int iSocketFd = socket (AF_INET, SOCK_DGRAM, 0); + if (iSocketFd < 0) + { + CcspTraceError(("%s: socket creation failed\n", __FUNCTION__)); + return false; + } + + //Set the interface name + struct ifreq ifr = {0}; + strncpy(ifr.ifr_name, pIfaceName, IFNAMSIZ-1); + ifr.ifr_name[IFNAMSIZ-1] = '\0'; + + // Check if interface exists + if (ioctl(iSocketFd, SIOCGIFFLAGS, &ifr) < 0) + { + CcspTraceError(("%s: ioctl SIOCGIFFLAGS failed for interface %s\n", __FUNCTION__, pIfaceName)); + close(iSocketFd); + return false; + } + // Check if interface is up + if (ifr.ifr_flags & IFF_UP) + { + CcspTraceInfo(("%s: Interface %s is already up\n", __FUNCTION__, pIfaceName)); + isUp = true; + } + close(iSocketFd); + return isUp; +} +/* + * @brief Check if the specified network interface has an IP address assigned. + * + * This helper function checks whether the given network interface has an IP address assigned by querying its address via ioctl. + * + * @param[in] pIfaceName + * Pointer to a null-terminated string containing the name of the + * network interface to check (e.g., "mta0"). + * @return + * Returns true if the interface has an IP address assigned, false otherwise. +*/ +static bool isIfaceHasIp(char *pIfaceName) +{ + bool hasIp = false; + if (NULL == pIfaceName) + { + CcspTraceError(("%s: NULL parameters are passed \n", __FUNCTION__)); + return false; + } + + int iSocketFd = socket (AF_INET, SOCK_DGRAM, 0); + if (iSocketFd < 0) + { + CcspTraceError(("%s: socket creation failed\n", __FUNCTION__)); + return false; + } + + //Set the interface name + struct ifreq ifr = {0}; + strncpy(ifr.ifr_name, pIfaceName, IFNAMSIZ-1); + ifr.ifr_name[IFNAMSIZ-1] = '\0'; + + // Check if interface has IP address + if (ioctl(iSocketFd, SIOCGIFADDR, &ifr) == 0) + { + CcspTraceInfo(("%s: Interface %s has IP address assigned\n", __FUNCTION__, pIfaceName)); + hasIp = true; + } + close(iSocketFd); + return hasIp; +} + +/* + * @brief Create the MTA network interface as a macvlan linked to the WAN interface. + * This function creates a macvlan interface with the specified name, + * assigns it the MAC address read from factory NVRAM, and brings the interface up. + * @param[in] pVoiceSupportIfaceName + * Pointer to a null-terminated string containing the name of the + * MTA network interface to create (e.g., "mta0"). + * @return + * Returns 0 on success, -1 on failure. +*/ +static int createMtaInterface(char * pVoiceSupportIfaceName) +{ + + char cMtaInterfaceMac[32] = {0}; + char cWanIfname[32] = {0}; + + if (NULL == pVoiceSupportIfaceName) + { + CcspTraceError(("%s: NULL parameters are passed \n", __FUNCTION__)); + return -1; + } + + //Read the mac address from platform_hal_GetMTAMacAddress API once it is implemented + readMacAddress(cMtaInterfaceMac); + if (cMtaInterfaceMac[0] == '\0') { + CcspTraceError(("%s: readMacAddress failed to get MAC address\n", __FUNCTION__)); + return -1; + } + CcspTraceInfo(("%s:%d, MTA MacVlan Mac is %s\n", __FUNCTION__, __LINE__, cMtaInterfaceMac)); + getWanIfaceName(cWanIfname, sizeof(cWanIfname)); + if (cWanIfname[0] == '\0') + snprintf(cWanIfname, sizeof(cWanIfname), "erouter0"); + + if (false == IsIfaceAlreadyUp(pVoiceSupportIfaceName)) + { + //Create the macVlan + CcspTraceInfo(("%s:%d, Creating macVlan interface %s with mac %s\n", __FUNCTION__, __LINE__, pVoiceSupportIfaceName, cMtaInterfaceMac)); + char cCmd[128] = {0}; + snprintf(cCmd, sizeof(cCmd), "ip link add link %s name %s type macvlan mode bridge", cWanIfname, pVoiceSupportIfaceName); + system(cCmd); + snprintf(cCmd, sizeof(cCmd), "ip link set dev %s address %s", pVoiceSupportIfaceName, cMtaInterfaceMac); + system(cCmd); + snprintf(cCmd, sizeof(cCmd), "ip link set dev %s up", pVoiceSupportIfaceName); + system(cCmd); + CcspTraceInfo(("%s:%d, Created macVlan interface %s\n", __FUNCTION__, __LINE__, pVoiceSupportIfaceName)); + } + else + { + CcspTraceInfo(("%s:%d, MTA interface %s is already up, skipping creation\n", __FUNCTION__, __LINE__, pVoiceSupportIfaceName)); + } + return 0; +} + +/* + * @brief Start the voice support feature by creating the MTA interface + * and enabling DHCPv4 if necessary. + * + * This function checks syscfg settings to determine if voice support is + * enabled and, if so, creates the MTA network interface and subscribes + * to DHCP client events. If the voice support mode includes IPv4, it + * enables DHCPv4 on the MTA interface if it does not already have an IP + * address assigned. + */ +void startVoiceFeature(void) +{ + + char cVoiceSupportEnabled[8] = {0}; + char cVoiceSupportMode[32] = {0}; + char cVoiceSupportIfaceName[32] = {0}; + + + syscfg_get(NULL, "VoiceSupport_IfaceName",cVoiceSupportIfaceName, sizeof(cVoiceSupportIfaceName)); + syscfg_get(NULL, "VoiceSupport_Mode",cVoiceSupportMode, sizeof(cVoiceSupportMode)); + syscfg_get(NULL, "VoiceSupport_Enabled", cVoiceSupportEnabled, sizeof(cVoiceSupportEnabled)); + + if (cVoiceSupportEnabled[0] == '\0' || strcmp(cVoiceSupportEnabled, "true") != 0) + { + CcspTraceError(("%s:%d, VoiceSupport_Enabled is false or not set, skipping MTA interface creation\n", __FUNCTION__, __LINE__)); + return; + } + + if (cVoiceSupportIfaceName[0] == '\0') + { + CcspTraceWarning(("%s:%d, VoiceSupport_IfaceName not set in syscfg, using default mta0\n", __FUNCTION__, __LINE__)); + snprintf(cVoiceSupportIfaceName, sizeof(cVoiceSupportIfaceName), "mta0"); + } + + if (cVoiceSupportMode[0] == '\0') + { + CcspTraceWarning(("%s:%d, VoiceSupport_Mode not set in syscfg, using default Dual_Stack\n", __FUNCTION__, __LINE__)); + snprintf(cVoiceSupportMode, sizeof(cVoiceSupportMode), VOICE_SUPPORT_MODE_DUAL_STACK); + } + + if (createMtaInterface(cVoiceSupportIfaceName) == 0) + { + CcspTraceInfo(("%s:%d, MTA interface created successfully\n", __FUNCTION__, __LINE__)); + } else { + CcspTraceError(("%s:%d, Failed to create MTA interface\n", __FUNCTION__, __LINE__)); + return; + } + subscribeDhcpClientEvents(); + + if (0 == strcmp(cVoiceSupportMode, VOICE_SUPPORT_MODE_IPV4_ONLY) || 0 == strcmp(cVoiceSupportMode, VOICE_SUPPORT_MODE_DUAL_STACK)) + { + CcspTraceInfo(("%s:%d, Starting udhcpc on MTA interface\n", __FUNCTION__, __LINE__)); + if (false == isIfaceHasIp(cVoiceSupportIfaceName)) + enableDhcpv4ForMta(cVoiceSupportIfaceName); + } else { + CcspTraceInfo(("%s:%d, VoiceSupport_Mode: %s is not set to %s or %s, skipping udhcpc start\n",__FUNCTION__, __LINE__, cVoiceSupportMode, VOICE_SUPPORT_MODE_IPV4_ONLY, VOICE_SUPPORT_MODE_DUAL_STACK)); + } +} +/* + *@brief Add IP route details for the MTA interface based on DHCP event data + *@param pDhcpEvtData - Pointer to the DHCP event data structure +*/ +static void addIpRouteDetails(DhcpEventData_t *pDhcpEvtData) +{ + if (NULL == pDhcpEvtData) + { + CcspTraceError(("%s: NULL DHCP event data provided\n", __FUNCTION__)); + return; + } + CcspTraceInfo(("%s:%d, Adding IP route details for interface %s\n", __FUNCTION__, __LINE__, pDhcpEvtData->cIfaceName)); + CcspTraceInfo(("%s:%d, Gateway address:%s\n",__FUNCTION__,__LINE__,pDhcpEvtData->leaseInfo.dhcpV4Msg.gateway)); + CcspTraceInfo(("%s:%d, TFTP server address:%s\n",__FUNCTION__,__LINE__,pDhcpEvtData->leaseInfo.dhcpV4Msg.cTftpServer)); + CcspTraceInfo(("%s:%d, MTA IP address:%s\n",__FUNCTION__,__LINE__,pDhcpEvtData->leaseInfo.dhcpV4Msg.address)); + + + char cParamName[256] = {0}; + struct in_addr ipAddr = {0}, netMask = {0}, networkAddr = {0}; + int iNetRet = 0; + + iNetRet = inet_pton(AF_INET, pDhcpEvtData->leaseInfo.dhcpV4Msg.address, &ipAddr); + if (iNetRet != 1) + { + CcspTraceError(("%s:%d, inet_pton failed for IP address %s with error code %d\n", __FUNCTION__, __LINE__, pDhcpEvtData->leaseInfo.dhcpV4Msg.address, iNetRet)); + return; + } + iNetRet = inet_pton(AF_INET, pDhcpEvtData->leaseInfo.dhcpV4Msg.netmask, &netMask); + if (iNetRet != 1) + { + CcspTraceError(("%s:%d, inet_pton failed for netmask %s with error code %d\n", __FUNCTION__, __LINE__, pDhcpEvtData->leaseInfo.dhcpV4Msg.netmask, iNetRet)); + return; + } + networkAddr.s_addr = ipAddr.s_addr & netMask.s_addr; + + char cNetworkAddr[32] = {0}; + char cNetworkAddrWithCidr[64] = {0}; + inet_ntop(AF_INET, &networkAddr, cNetworkAddr, sizeof(cNetworkAddr)); + //calculate CIDR notation + CcspTraceInfo(("%s:%d, Network Address:%s\n",__FUNCTION__,__LINE__,cNetworkAddr)); + snprintf(cNetworkAddrWithCidr, sizeof(cNetworkAddrWithCidr), "%s/%d", cNetworkAddr, __builtin_popcount(ntohl(netMask.s_addr))); + CcspTraceInfo(("%s:%d, Network Address in CIDR notation:%s\n",__FUNCTION__,__LINE__,cNetworkAddrWithCidr)); + + snprintf(cParamName, sizeof(cParamName), "ip route add %s dev %s", pDhcpEvtData->leaseInfo.dhcpV4Msg.address, pDhcpEvtData->cIfaceName); + system(cParamName); + snprintf(cParamName, sizeof(cParamName), "ip route add %s via %s dev %s", pDhcpEvtData->leaseInfo.dhcpV4Msg.cTftpServer, pDhcpEvtData->leaseInfo.dhcpV4Msg.gateway, pDhcpEvtData->cIfaceName); + system(cParamName); + snprintf(cParamName, sizeof(cParamName), "ip rule add from %s table 21", pDhcpEvtData->leaseInfo.dhcpV4Msg.address); + system(cParamName); + snprintf(cParamName, sizeof(cParamName), "ip route add %s dev %s table 21", cNetworkAddrWithCidr, pDhcpEvtData->cIfaceName); + system(cParamName); + snprintf(cParamName, sizeof(cParamName), "ip route add default via %s dev %s table 21", pDhcpEvtData->leaseInfo.dhcpV4Msg.gateway, pDhcpEvtData->cIfaceName); + system(cParamName); +} + +/* + *@brief Convert a hexadecimal string to a byte array. + * This helper function takes a hexadecimal string representation and converts it + * into its corresponding byte array. + * @param[in] pHexString + * Pointer to a null-terminated string containing the hexadecimal representation. + * @param[out] pByteArray + * Pointer to a byte array that will receive the converted bytes. + * @param[in] iByteArrayLen + * Length of the byte array to be filled. +*/ +void hexStringToByteArray(const char* pHexString, uint8_t* pByteArray, int iByteArrayLen) +{ + if (NULL == pHexString || NULL == pByteArray || iByteArrayLen <= 0) + { + CcspTraceError(("%s: NULL parameters are passed \n", __FUNCTION__)); + return; + } + + int iHexStringLen = strlen(pHexString); + for (int iIndex = 0; iIndex < iByteArrayLen && (iIndex * 2 + 1) < iHexStringLen; iIndex++) + { + sscanf(&pHexString[iIndex * 2], "%2hhx", &pByteArray[iIndex]); + } +} +/* + * @brief Extract a specific sub-option from DHCP Option 122 data. + * + * This helper function parses the provided DHCP Option 122 data to find + * and extract the value of a specified sub-option. + * + * @param[in] pOption122Data + * Pointer to the raw DHCP Option 122 data buffer. + * @param[in] iOption122Len + * Length of the DHCP Option 122 data buffer. + * @param[in] ui8WantedSubOption + * The sub-option number to extract. + * @param[out] pOutputVal + * Pointer to a variable that will receive a pointer to the extracted + * sub-option value within the original buffer. + * @param[out] pOutputValLen + * Pointer to a variable that will receive the length of the extracted + * sub-option value. + * + * @return + * Returns 0 on success, -1 if the sub-option is not found or on error. +*/ +static int getOption122_SubOptions(uint8_t *pOption122Data, uint16_t iOption122Len, uint8_t ui8WantedSubOption, uint8_t **pOutputVal, uint8_t *pOutputValLen) +{ + if (NULL == pOption122Data || iOption122Len == 0 || NULL == pOutputVal || NULL == pOutputValLen) + { + CcspTraceError(("%s: NULL parameters are passed \n", __FUNCTION__)); + return -1; + } + + uint16_t ui16Position = 0; + while(ui16Position + 2 <= iOption122Len) + { + uint8_t ui8SubOption = pOption122Data[ui16Position]; + uint8_t ui8SubOptionLen = pOption122Data[ui16Position + 1]; + + CcspTraceInfo(("%s:%d, SubOption:%u, SubOption Length:%u\n", __FUNCTION__, __LINE__, ui8SubOption, ui8SubOptionLen)); + CcspTraceInfo(("%s:%d, ui16Position:%d\n",__FUNCTION__, __LINE__, ui16Position)); + + if (ui16Position + 2 + ui8SubOptionLen > iOption122Len) + { + CcspTraceError(("%s: Malformed Option 122 data\n", __FUNCTION__)); + return -1; + } + + if (ui8SubOption == ui8WantedSubOption) + { + *pOutputVal = &pOption122Data[ui16Position + 2]; + *pOutputValLen = ui8SubOptionLen; + return 0; + } + + ui16Position += 2 + ui8SubOptionLen; + } + return -1; // Sub-option not found +} + +/* + *@brief This function initializes the voice support related parameters based on DHCP event data + *@param pDhcpEvtData - Pointer to the DHCP event data structure +*/ + +static void initializeVoiceSupport(DhcpEventData_t *pDhcpEvtData) +{ + if (NULL == pDhcpEvtData) + { + CcspTraceError(("%s: NULL DHCP event data provided\n", __FUNCTION__)); + return; + } + VoiceInterfaceInfoType sVoiceInterfaceInfoType = {0}; + snprintf(sVoiceInterfaceInfoType.intfName, sizeof(sVoiceInterfaceInfoType.intfName), "%s", pDhcpEvtData->cIfaceName); + sVoiceInterfaceInfoType.isPhyUp = 1; + sVoiceInterfaceInfoType.isIpv4Up = 1; + strncpy(sVoiceInterfaceInfoType.ipv4Addr, pDhcpEvtData->leaseInfo.dhcpV4Msg.address, sizeof(sVoiceInterfaceInfoType.ipv4Addr)-1); + strncpy(sVoiceInterfaceInfoType.v4NextServerIp, pDhcpEvtData->leaseInfo.dhcpV4Msg.cTftpServer, sizeof(sVoiceInterfaceInfoType.v4NextServerIp)-1); + strncpy(sVoiceInterfaceInfoType.v4BootFileName, pDhcpEvtData->leaseInfo.dhcpV4Msg.cOption67, sizeof(sVoiceInterfaceInfoType.v4BootFileName)-1); + + char cTmpDnsServers[VOICE_IPV4_ADDR_LEN*4] = {0}; + int iBufSize = sizeof(cTmpDnsServers); + int iLen = 0; + strncpy(cTmpDnsServers, pDhcpEvtData->leaseInfo.dhcpV4Msg.dnsServer, iBufSize-1); + cTmpDnsServers[iBufSize -1] = '\0'; + iLen = strlen(cTmpDnsServers); + if (iLen < (iBufSize -1) && strlen(pDhcpEvtData->leaseInfo.dhcpV4Msg.dnsServer1) > 0) + { + if (iLen > 0) + { + strncat(cTmpDnsServers, ",", iBufSize - strlen(cTmpDnsServers) -1); + } + strncat(cTmpDnsServers, pDhcpEvtData->leaseInfo.dhcpV4Msg.dnsServer1, iBufSize - strlen(cTmpDnsServers) -1); + } + + strncpy(sVoiceInterfaceInfoType.v4DnsServers, cTmpDnsServers, sizeof(sVoiceInterfaceInfoType.v4DnsServers)-1); + strncpy(sVoiceInterfaceInfoType.v4HostName, pDhcpEvtData->leaseInfo.dhcpV4Msg.cHostName, sizeof(sVoiceInterfaceInfoType.v4HostName)-1); + strncpy(sVoiceInterfaceInfoType.v4DomainName, pDhcpEvtData->leaseInfo.dhcpV4Msg.cDomainName, sizeof(sVoiceInterfaceInfoType.v4DomainName)-1); + strncpy(sVoiceInterfaceInfoType.v4LogServerIp, "(null)", sizeof(sVoiceInterfaceInfoType.v4LogServerIp)-1); + strncpy(sVoiceInterfaceInfoType.v4ServerHostName, "(null)", sizeof(sVoiceInterfaceInfoType.v4ServerHostName)-1); + + //Retrieve Option122 SubOption 3 for Provisioning Server + uint8_t *pSubOptData = NULL; + uint8_t ui8SubOptionLen = 0; + const char *pHexOption122Data = pDhcpEvtData->leaseInfo.dhcpV4Msg.cOption122; + uint16_t iOption122Len = strlen(pHexOption122Data) / 2; + uint8_t ui8Option122Data[iOption122Len]; + memset(ui8Option122Data, 0, sizeof(ui8Option122Data)); + hexStringToByteArray(pHexOption122Data, ui8Option122Data, iOption122Len); + + CcspTraceInfo(("%s:%d, Option122 Data: %s\n", __FUNCTION__, __LINE__, pDhcpEvtData->leaseInfo.dhcpV4Msg.cOption122)); + if (0 == getOption122_SubOptions(ui8Option122Data, + iOption122Len, + 3, + &pSubOptData, + &ui8SubOptionLen)) + { + int iCopyLen = (ui8SubOptionLen < sizeof(sVoiceInterfaceInfoType.v4ProvServer)-1) ? ui8SubOptionLen : (sizeof(sVoiceInterfaceInfoType.v4ProvServer)-1); + memcpy(sVoiceInterfaceInfoType.v4ProvServer, pSubOptData, iCopyLen); + sVoiceInterfaceInfoType.v4ProvServer[iCopyLen] = '\0'; + CcspTraceInfo(("%s:%d, Retrieved Option122 SubOption 3 for Provisioning Server: %s and len:%u\n", __FUNCTION__, __LINE__, sVoiceInterfaceInfoType.v4ProvServer, ui8SubOptionLen)); + char cHexBuf[256] = {0}; + long unsigned int uiPos = 0; + for (int i = 0; i < ui8SubOptionLen && uiPos < sizeof(cHexBuf) - 3; i++) { + uiPos += snprintf(cHexBuf + uiPos, sizeof(cHexBuf) - uiPos, "%02x ", pSubOptData[i]); + } + CcspTraceInfo(("%s:%d, Option122 SubOption 3 hex: %s\n", __FUNCTION__, __LINE__, cHexBuf)); + } + else { + CcspTraceError(("%s: Failed to get Option122 SubOption 3 for Provisioning Server\n", __FUNCTION__)); + } + + CcspTraceInfo(("%s:%d, Initializing Voice Support with following details:\n", __FUNCTION__, __LINE__)); + CcspTraceInfo(("%s:%d, Interface Name: %s\n", __FUNCTION__, __LINE__, sVoiceInterfaceInfoType.intfName)); + CcspTraceInfo(("%s:%d, IPv4 Address: %s\n", __FUNCTION__, __LINE__, sVoiceInterfaceInfoType.ipv4Addr)); + CcspTraceInfo(("%s:%d, Next Server IP: %s\n", __FUNCTION__, __LINE__, sVoiceInterfaceInfoType.v4NextServerIp)); + CcspTraceInfo(("%s:%d, Boot File Name: %s\n", __FUNCTION__, __LINE__, sVoiceInterfaceInfoType.v4BootFileName)); + CcspTraceInfo(("%s:%d, DNS Servers: %s\n", __FUNCTION__, __LINE__, sVoiceInterfaceInfoType.v4DnsServers)); + CcspTraceInfo(("%s:%d, Host Name: %s\n", __FUNCTION__, __LINE__, sVoiceInterfaceInfoType.v4HostName)); + CcspTraceInfo(("%s:%d, Domain Name: %s\n", __FUNCTION__, __LINE__, sVoiceInterfaceInfoType.v4DomainName)); + CcspTraceInfo(("%s:%d, Provisioning Server: %s\n", __FUNCTION__, __LINE__, sVoiceInterfaceInfoType.v4ProvServer)); + CcspTraceInfo(("%s:%d, Log Server IP: %s\n", __FUNCTION__, __LINE__, sVoiceInterfaceInfoType.v4LogServerIp)); + CcspTraceInfo(("%s:%d, Server Host Name: %s\n", __FUNCTION__, __LINE__, sVoiceInterfaceInfoType.v4ServerHostName)); + + voice_hal_interface_info_notify(&sVoiceInterfaceInfoType); +} + + +static void processVoiceDhcpEvent(DhcpEventData_t *pDhcpEvtData) +{ + if (NULL == pDhcpEvtData) + { + CcspTraceError(("%s: NULL DHCP event data provided\n", __FUNCTION__)); + return; + } + addIpRouteDetails(pDhcpEvtData); + initializeVoiceSupport(pDhcpEvtData); + +} +/* + * @brief Thread function to handle DHCP client events for voice support. + * This function processes DHCP events in a separate thread to avoid blocking + * the main event handling loop. It takes a pointer to DhcpEventData_t as an + * argument, which contains the DHCP event details. + * @param[in] pArg + * Pointer to a DhcpEventData_t structure containing the DHCP event data. + * @return + * Returns NULL upon completion. +*/ +void * dhcpClientEventsHandlerThread(void * pArg) +{ + if (NULL == pArg) + { + CcspTraceError(("%s: NULL argument provided\n", __FUNCTION__)); + return NULL; + } + + CcspTraceInfo(("%s:%d, DHCP Client Events Handler Thread started\n", __FUNCTION__, __LINE__)); + DhcpEventData_t *pDhcpEvtData = (DhcpEventData_t *)pArg; + + if (DHCP_IPv4 == pDhcpEvtData->dhcpVersion) + { + switch (pDhcpEvtData->dhcpMsgType) + { + case DHCP_CLIENT_STARTED: + case DHCP_CLIENT_STOPPED: + case DHCP_LEASE_RENEW: + case DHCP_LEASE_DEL: + case DHCP_LEASE_UPDATE: + case DHCP_CLIENT_FAILED: + { + pthread_mutex_lock(&voiceDataProcessingMutex); + processVoiceDhcpEvent((DhcpEventData_t *)pDhcpEvtData); + pthread_mutex_unlock(&voiceDataProcessingMutex); + break; + } + + default: + CcspTraceWarning(("%s: Unrecognized DHCPv4 message type %d\n", __FUNCTION__, pDhcpEvtData->dhcpMsgType)); + break; + } + } + else + { + CcspTraceError(("%s: Unsupported DHCP version %d\n", __FUNCTION__, pDhcpEvtData->dhcpVersion)); + } + free(pDhcpEvtData); + pthread_exit(NULL); +} diff --git a/source/TR-181/middle_layer_src/cosa_voice_apis.h b/source/TR-181/middle_layer_src/cosa_voice_apis.h new file mode 100644 index 0000000..a46c16a --- /dev/null +++ b/source/TR-181/middle_layer_src/cosa_voice_apis.h @@ -0,0 +1,48 @@ +/* + * If not stated otherwise in this file or this component's LICENSE file the + * following copyright and licenses apply: + * + * Copyright 2026 RDK Management + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +*/ + +#ifndef _COSA_VOICE_APIS_H_ +#define _COSA_VOICE_APIS_H_ + +/* + * @brief Start the voice support feature by creating the MTA interface + * and enabling DHCPv4 if necessary. + * + * This function checks syscfg settings to determine if voice support is + * enabled and, if so, creates the MTA network interface and subscribes + * to DHCP client events. If the voice support mode includes IPv4, it + * enables DHCPv4 on the MTA interface if it does not already have an IP + * address assigned. + */ +void startVoiceFeature(void); + +/* + * @brief Thread function to handle DHCP client events for voice support. + * This function processes DHCP events in a separate thread to avoid blocking + * the main event handling loop. It takes a pointer to DhcpEventData_t as an + * argument, which contains the DHCP event details. + * @param[in] pArg + * Pointer to a DhcpEventData_t structure containing the DHCP event data. + * @return + * Returns NULL upon completion. +*/ +void * dhcpClientEventsHandlerThread(void * pArg); + + +#endif diff --git a/source/TR-181/middle_layer_src/cosa_x_cisco_com_mta_dml.h b/source/TR-181/middle_layer_src/cosa_x_cisco_com_mta_dml.h index 3693a56..ac19a98 100644 --- a/source/TR-181/middle_layer_src/cosa_x_cisco_com_mta_dml.h +++ b/source/TR-181/middle_layer_src/cosa_x_cisco_com_mta_dml.h @@ -805,4 +805,4 @@ ULONG Handsets_Commit ( ANSC_HANDLE hInsContext - ); \ No newline at end of file + ); diff --git a/source/TR-181/middle_layer_src/cosa_x_cisco_com_mta_internal.c b/source/TR-181/middle_layer_src/cosa_x_cisco_com_mta_internal.c index 013af64..872504c 100644 --- a/source/TR-181/middle_layer_src/cosa_x_cisco_com_mta_internal.c +++ b/source/TR-181/middle_layer_src/cosa_x_cisco_com_mta_internal.c @@ -77,6 +77,10 @@ #include "mta_hal.h" #include #include "syscfg/syscfg.h" +#if defined (VOICE_MTA_SUPPORT) +#include "cosa_rbus_apis.h" +#include "cosa_voice_apis.h" +#endif #define MAX_BUFF_SIZE 128 #define MAX_IP_PREF_VAL 6 @@ -85,6 +89,7 @@ static int sysevent_fd; static token_t sysevent_token; + /********************************************************************** caller: owner of the object @@ -621,6 +626,50 @@ void WaitForDhcpOption() } CcspTraceInfo(("%s Didn't receive dhcp options in %d sec, initializing mta with default values \n",__FUNCTION__,maxCount)); } +#if defined (VOICE_MTA_SUPPORT) +/* + @brief This thread listens to the sysevent notifications for wan state and initializes the voice when wan is up. +*/ +void * voiceSyseventThread(void * hThisObject) +{ + (void)hThisObject; + + int iError = -1; + char cCurrWanState[8] = {0}; + char cEventName[32]={0}, cEventVal[32]={0}; + async_id_t wanStateAsyncId; + + sysevent_set_options(sysevent_fd, sysevent_token, "current_wan_state", TUPLE_FLAG_EVENT); + sysevent_setnotification(sysevent_fd, sysevent_token, "current_wan_state", &wanStateAsyncId); + CcspTraceInfo(("%s Registered for sysevent notifications for current_wan_state \n",__FUNCTION__)); + + sysevent_get(sysevent_fd, sysevent_token, "current_wan_state", cCurrWanState, sizeof(cCurrWanState)); + + if (0 == strcasecmp(cCurrWanState, "up")) + { + CcspTraceWarning(("%s:%d, current_wan_state up, Initializing MTA Interface \n",__FUNCTION__,__LINE__)); + startVoiceFeature(); + } + do + { + int iEventNameLen = sizeof(cEventName); + int iEventValLen = sizeof(cEventVal); + memset(cEventName, 0, iEventNameLen); + memset(cEventVal, 0, iEventValLen); + iError = sysevent_getnotification(sysevent_fd, sysevent_token, cEventName, &iEventNameLen, cEventVal, &iEventValLen, &wanStateAsyncId); + if (0 == iError) + { + CcspTraceWarning(("%s Recieved notification event %s, state %s\n",__FUNCTION__,cEventName,cEventVal)); + if (0 == strcmp(cEventName, "current_wan_state") && 0 == strcasecmp(cEventVal, "up")) + { + CcspTraceWarning(("%s:%d, current_wan_state up, Initializing voice MTA Interface \n",__FUNCTION__,__LINE__)); + startVoiceFeature(); + } + } + } while (TRUE); + return NULL; +} +#endif /*Coverity Fix CID 121026 Arg Type MisMatch */ void * Mta_Sysevent_thread_Dhcp_Option( void * hThisObject) @@ -1367,7 +1416,6 @@ void * Mta_Sysevent_thread(void * hThisObject) } - /********************************************************************** caller: self @@ -1407,12 +1455,41 @@ CosaMTAInitialize //Starting thread to monitor Wan mode and wan status CcspTraceInfo(("%s %d Starting sysevent thread \n", __FUNCTION__, __LINE__)); -#ifdef ENABLE_ETH_WAN +#if defined(VOICE_MTA_SUPPORT) +/* + * Note: + * - getIfaceIndexInfo() uses PSM (not RBUS) to retrieve interface index + * information and populate parameters such as cBaseParam. + * - These parameters are later used when making RBUS calls after + * initRbusHandle() completes. + * + * Therefore, getIfaceIndexInfo() is intentionally called before + * initRbusHandle() so that all required configuration is available + * by the time RBUS is initialized and RBUS calls are performed. +*/ + + getIfaceIndexInfo(); + initRbusHandle(); + sysevent_fd = sysevent_open("127.0.0.1", SE_SERVER_WELL_KNOWN_PORT, SE_VERSION, "WAN State", &sysevent_token); + pthread_t voiceMtaInit; + if (sysevent_fd < 0) + { + CcspTraceError(("%s: Failed to open sysevent connection\n", __FUNCTION__)); + return ANSC_STATUS_FAILURE; + } + CcspTraceInfo(("%s:%d, Starting sysevent thread for Voice Mta\n", __FUNCTION__, __LINE__)); + if (0 != pthread_create(&voiceMtaInit, NULL, &voiceSyseventThread, (ANSC_HANDLE) hThisObject)) + { + CcspTraceError(("%s: Failed to create Voice Mta sysevent thread\n", __FUNCTION__)); + } +#elif defined(ENABLE_ETH_WAN) sysevent_fd = sysevent_open("127.0.0.1", SE_SERVER_WELL_KNOWN_PORT, SE_VERSION, "WAN State", &sysevent_token); pthread_t MtaInit; #if defined (EROUTER_DHCP_OPTION_MTA) + CcspTraceInfo(("%s %d Starting sysevent thread for DHCP option 122/2171 \n", __FUNCTION__, __LINE__)); pthread_create(&MtaInit, NULL, &Mta_Sysevent_thread_Dhcp_Option, (ANSC_HANDLE) hThisObject); #else + CcspTraceInfo(("%s %d Starting sysevent thread for WAN state \n", __FUNCTION__, __LINE__)); pthread_create(&MtaInit, NULL, &Mta_Sysevent_thread, (ANSC_HANDLE) hThisObject); #endif // CosaMTAInitializeEthWanProv(hThisObject); @@ -1462,6 +1539,34 @@ CosaMTARemove return returnStatus; } - +/* + *@brief This function get the wan interface name from sysevent + *@param pIfname - pointer to store the wan interface name + *@param iIfnameLen - length of the pointer + *@return ANSC_STATUS_SUCCESS on success else ANSC_STATUS_FAILURE +*/ +ANSC_STATUS getWanIfaceName(char *pIfname, int iIfnameLen) +{ + ANSC_STATUS returnStatus = ANSC_STATUS_FAILURE; + if (!pIfname || iIfnameLen <= 0) + { + CcspTraceError(("%s: Invalid parameters\n", __FUNCTION__)); + return returnStatus; + } + if (sysevent_fd > 0) + { + if (0 != sysevent_get(sysevent_fd, sysevent_token, "wan_ifname", pIfname, iIfnameLen)) + { + CcspTraceError(("%s: sysevent_get wan_ifname failed\n", __FUNCTION__)); + returnStatus = ANSC_STATUS_FAILURE; + } + else + { + CcspTraceInfo(("%s: sysevent_get wan_ifname=%s\n", __FUNCTION__, pIfname)); + returnStatus = ANSC_STATUS_SUCCESS; + } + } + return returnStatus; +} //#endif diff --git a/source/TR-181/middle_layer_src/cosa_x_cisco_com_mta_internal.h b/source/TR-181/middle_layer_src/cosa_x_cisco_com_mta_internal.h index 670706c..7534de6 100644 --- a/source/TR-181/middle_layer_src/cosa_x_cisco_com_mta_internal.h +++ b/source/TR-181/middle_layer_src/cosa_x_cisco_com_mta_internal.h @@ -160,5 +160,12 @@ int checkIfDefMtaDhcpOptionEnabled(); void WaitForDhcpOption(); void * Mta_Sysevent_thread(void * hThisObject); void * Mta_Sysevent_thread_Dhcp_Option( void * hThisObject); +/* + *@brief This function get the wan interface name from sysevent + *@param pIfname - pointer to store the wan interface name + *@param iIfnameLen - length of the pointer + *@return ANSC_STATUS_SUCCESS on success else ANSC_STATUS_FAILURE +*/ +ANSC_STATUS getWanIfaceName(char *pIfname, int iIfnameLen); #endif // #endif