RDKB-62810,RDKB-62812:Create Virtual Interface for voice and initialize DHCP#25
RDKB-62810,RDKB-62812:Create Virtual Interface for voice and initialize DHCP#25Amaresh-Kotekal wants to merge 19 commits intordkcentral:developfrom
Conversation
Reason for change:Added the new entry for virual mta interface Test Procedure: verify the logic and functionality Risks: Low Priority: P1
Reason for change:Added the new entry for mac Vlan interface Test Procedure: verify the logic and functionality Risks: High Priority: P1
Reason for change:Added the new entry for mac vlan interface Test Procedure: verify the logic and functionality Risks: High Priority: P1
Reason for change:Added the new entry for mac vlan interface Test Procedure: verify the logic and functionality Risks: High Priority: P1
Reason for change:Added the new entry for mac vlan interface Test Procedure: verify the logic and functionality Risks: High Priority: P1
Reason for change:Added the new entry for virual mta interface Test Procedure: verify the logic and functionality Risks: High Priority: P1
Reason for change:Added the new entry for virual mta interface Test Procedure: verify the logic and functionality Risks: High Priority: P1
There was a problem hiding this comment.
Pull request overview
This PR adds functionality to create a virtual macvlan interface for voice services and initialize DHCP on that interface for the SCXF10 platform. The implementation reads voice configuration from syscfg, creates a macvlan interface with a MAC address read from factory data, and starts DHCP client using rbus communication.
Changes:
- Added new rbus API wrapper functions for DHCP configuration
- Implemented macvlan interface creation with MAC address assignment from factory data
- Integrated interface creation into the MTA sysevent thread on WAN state transitions
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 22 comments.
| File | Description |
|---|---|
| source/TR-181/middle_layer_src/cosa_x_cisco_com_mta_internal.c | Added readMacAddress() and createMtaInterface() functions; integrated calls into Mta_Sysevent_thread for WAN up events |
| source/TR-181/middle_layer_src/cosa_rbus_apis.h | New header defining rbus API wrapper function prototypes and parameter value type enum |
| source/TR-181/middle_layer_src/cosa_rbus_apis.c | New implementation of rbus API wrappers for initializing rbus handle and enabling DHCPv4 |
| source/TR-181/middle_layer_src/Makefile.am | Added cosa_rbus_apis.c to build sources |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
|
|
||
| #define DHCPv4_VOICE_SUPPORT_PARAM "dmsb.voicesupport.Interface.IP.DHCPV4Interface" | ||
|
|
||
| rbusHandle_t voiceRbusHandle = NULL; |
There was a problem hiding this comment.
The voiceRbusHandle is a global variable that is initialized in initRbusHandle() but never cleaned up. If the component is unloaded or terminated, rbus_close() should be called to properly release the rbus handle and associated resources, preventing resource leaks.
| 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; | ||
| } | ||
|
|
||
| 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); | ||
| } | ||
|
|
||
| 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__)); | ||
| } | ||
|
|
||
| void enableDhcpv4ForMta(const char * pIfaceName) | ||
| { | ||
| int iRetPsmGet = CCSP_SUCCESS; | ||
| char cBaseParam[32] = {0}; | ||
| char cParamName[64] = {0}; | ||
| 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)); | ||
| return; | ||
| } | ||
| else | ||
| { | ||
| CcspTraceInfo(("%s: PSM_Get_Record_Value2 successful for param %s with value %s\n", __FUNCTION__, DHCPv4_VOICE_SUPPORT_PARAM, | ||
| pParamValue)); | ||
| snprintf(cBaseParam, sizeof(cBaseParam), "%s",pParamValue); | ||
| ((CCSP_MESSAGE_BUS_INFO *)bus_handle)->freefunc((char *)pParamValue); | ||
| } | ||
|
|
||
| snprintf(cParamName, sizeof(cParamName), "%s.Enable", cBaseParam); | ||
| setParamInDhcpMgr(cParamName, "true", BOOLEAN_PARAM); | ||
|
|
||
| snprintf(cParamName, sizeof(cParamName), "%s.Interface", cBaseParam); | ||
| setParamInDhcpMgr(cParamName, pIfaceName, STRING_PARAM); | ||
| } |
There was a problem hiding this comment.
The new rbus API functions in this file lack test coverage. Given that the repository has comprehensive automated testing for similar functionality, these new functions should have corresponding unit tests to verify their behavior with various input conditions and error scenarios.
|
|
||
| #define DHCPv4_VOICE_SUPPORT_PARAM "dmsb.voicesupport.Interface.IP.DHCPV4Interface" | ||
|
|
||
| rbusHandle_t voiceRbusHandle = NULL; |
There was a problem hiding this comment.
The voiceRbusHandle is a global variable that can be accessed from multiple threads. The Mta_Sysevent_thread function calls initRbusHandle() and createMtaInterface() which both use this global variable, but there is no mutex or other synchronization mechanism to protect concurrent access. This could lead to race conditions if multiple threads attempt to initialize or use the rbus handle simultaneously.
| char cMtaIfaceDhcpV4Enabled[32] = {0}; | ||
| char cVoiceSupportIfaceName[32] = {0}; | ||
| char cMtaInterfaceMac[32] = {0}; | ||
| char cWanIfname[32] = {0}; | ||
| syscfg_get(NULL, "VoiceSupport_Enabled", cVoiceSupportEnabled, sizeof(cVoiceSupportEnabled)); | ||
| syscfg_get(NULL, "VoiceSupport_IfaceName", cVoiceSupportIfaceName, sizeof(cVoiceSupportIfaceName)); | ||
|
|
||
| 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') { | ||
| CcspTraceError(("%s:%d, mtaIface not set in syscfg, using default mta0\n", __FUNCTION__, __LINE__)); | ||
| strcpy_s(cVoiceSupportIfaceName, sizeof(cVoiceSupportIfaceName), "mta0"); | ||
| } | ||
| //Read the mac address from platform_hal_GetMTAMacAddress API once it is implemented | ||
| readMacAddress(cMtaInterfaceMac); | ||
| if (strlen(cMtaInterfaceMac) == 0 || cMtaInterfaceMac[0] == '\0') { | ||
| CcspTraceError(("%s: readMacAddress failed to get MAC address\n", __FUNCTION__)); | ||
| return; | ||
| } | ||
| CcspTraceInfo(("%s:%d, MTA MacVlan Mac is %s\n", __FUNCTION__, __LINE__, cMtaInterfaceMac)); | ||
| syscfg_get(NULL, "wan_physical_ifname", cWanIfname, sizeof(cWanIfname)); | ||
| CcspTraceInfo(("%s:%d, WAN Physical Ifname is %s\n", __FUNCTION__, __LINE__, cWanIfname)); | ||
| if (cWanIfname[0] == '\0') | ||
| strcpy_s(cWanIfname, sizeof(cWanIfname), "erouter0"); | ||
|
|
||
| //Create the macVlan | ||
| CcspTraceInfo(("%s:%d, Creating macVlan interface %s with mac %s\n", __FUNCTION__, __LINE__, cVoiceSupportIfaceName, cMtaInterfaceMac)); | ||
| char cCmd[2048] = {0}; | ||
| snprintf(cCmd, sizeof(cCmd), "ip link add link %s name %s type macvlan mode bridge", cWanIfname, cVoiceSupportIfaceName); | ||
| system(cCmd); | ||
| snprintf(cCmd, sizeof(cCmd), "ip link set dev %s address %s", cVoiceSupportIfaceName, cMtaInterfaceMac); | ||
| system(cCmd); | ||
| snprintf(cCmd, sizeof(cCmd), "ip link set dev %s up", cVoiceSupportIfaceName); | ||
| system(cCmd); | ||
| CcspTraceInfo(("%s:%d, Created macVlan interface %s\n", __FUNCTION__, __LINE__, cVoiceSupportIfaceName)); | ||
|
|
||
|
|
||
| syscfg_get(NULL, "VoiceSupport_Mode",cMtaIfaceDhcpV4Enabled, sizeof(cMtaIfaceDhcpV4Enabled)); |
There was a problem hiding this comment.
The variable name "cMtaIfaceDhcpV4Enabled" is misleading. The variable is used to store the voice support mode ("IPv4_Only" or "Dual_Stack"), not a boolean flag indicating whether DHCPv4 is enabled. A more accurate name would be "cVoiceSupportMode" or "cMtaIfaceMode" to reflect what it actually contains.
| setParamInDhcpMgr(cParamName, pIfaceName, STRING_PARAM); | ||
| } | ||
|
|
||
| //void subscribeDhcpClientEvents() |
There was a problem hiding this comment.
This commented-out function declaration should either be implemented if it's intended to be part of this feature, or removed entirely to keep the code clean. Leaving commented-out code can cause confusion about whether the functionality is planned, incomplete, or deprecated.
| //void subscribeDhcpClientEvents() |
|
Is this a Bug or a User Story (US)? If it is a User Story: Please list all dependent PRs from other components, if any. The commit message must include both the User Story ticket and the Subtask ticket --> addressed All changes related to the User Story must be squashed and merged in a single commit. Do not raise pull requests for partial User Story changes. Has the code development for the User Story been completed? --> Not yet If yes, please share the Gerrit topic or list all dependent PRs across components, including any meta-layer changes. If yes, please share the links to the validation comments. |
Reason for change:Creating the virtual interface and initializing the dhcp Test Procedure: verify the interface creation and IP assignment. Risks: High Priority: P1
Reason for change:Subscribing for DHCP events via R-BUS Test Procedure: verify the DHCP message is received or not Risks: High Priority: P1
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 4 out of 4 changed files in this pull request and generated 20 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| static void createMtaInterface(void) | ||
| { | ||
| char cVoiceSupportEnabled[8] = {0}; | ||
| char cMtaIfaceDhcpV4Enabled[32] = {0}; |
There was a problem hiding this comment.
The variable name cMtaIfaceDhcpV4Enabled is misleading. Based on its usage (lines 316-322), it actually stores the VoiceSupport_Mode configuration value (e.g., "IPv4_Only" or "Dual_Stack"), not a simple enabled/disabled boolean. Consider renaming to something more descriptive like cVoiceSupportMode or cMtaIfaceMode to better reflect its actual content and purpose.
| syscfg_get(NULL, "VoiceSupport_Mode",cMtaIfaceDhcpV4Enabled, sizeof(cMtaIfaceDhcpV4Enabled)); | ||
| if (0 == strcmp(cMtaIfaceDhcpV4Enabled, VOICE_SUPPORT_MODE_IPV4_ONLY) || 0 == strcmp(cMtaIfaceDhcpV4Enabled, VOICE_SUPPORT_MODE_DUAL_STACK)) { | ||
| CcspTraceInfo(("%s:%d, Starting udhcpc on MTA interface %s\n", __FUNCTION__, __LINE__, cVoiceSupportIfaceName)); | ||
| subscribeDhcpClientEvents(); |
There was a problem hiding this comment.
This line uses a tab character for indentation while the surrounding code uses spaces. This creates inconsistent indentation. Please use consistent whitespace formatting (spaces) to match the rest of the file.
| subscribeDhcpClientEvents(); | |
| subscribeDhcpClientEvents(); |
| /* | ||
| * @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) |
There was a problem hiding this comment.
The function documentation comment starting with /* should use /** to be consistent with Doxygen-style documentation used elsewhere.
| snprintf(cCmd, sizeof(cCmd), "ip link add link %s name %s type macvlan mode bridge", cWanIfname, cVoiceSupportIfaceName); | ||
| system(cCmd); | ||
| snprintf(cCmd, sizeof(cCmd), "ip link set dev %s address %s", cVoiceSupportIfaceName, cMtaInterfaceMac); | ||
| system(cCmd); | ||
| snprintf(cCmd, sizeof(cCmd), "ip link set dev %s up", cVoiceSupportIfaceName); | ||
| system(cCmd); |
There was a problem hiding this comment.
Using system() to execute shell commands with variables derived from syscfg is a security risk. The variables cWanIfname, cVoiceSupportIfaceName, and cMtaInterfaceMac could potentially contain shell metacharacters that lead to command injection. Although these values come from configuration, it's best practice to validate/sanitize the input or use safer alternatives like direct system calls or properly escaped commands.
| rbusValue_t rbusValue = rbusObject_GetValue(pRbusEvent->data, "Ifname"); | ||
| snprintf(sDhcpEvtData.cIfaceName, sizeof(sDhcpEvtData.cIfaceName), "%s", rbusValue_GetString(rbusValue, NULL)); | ||
| CcspTraceInfo(("%s: DHCP %s event for interface %s\n", __FUNCTION__, | ||
| (sDhcpEvtData.dhcpVersion == DHCP_IPv4) ? "IPv4" : "IPv6", sDhcpEvtData.cIfaceName)); | ||
|
|
||
| rbusValue = rbusObject_GetValue(pRbusEvent->data, "MsgType"); | ||
| sDhcpEvtData.dhcpMsgType = (DHCP_MESSAGE_TYPE)rbusValue_GetUInt32(rbusValue); | ||
| CcspTraceInfo(("%s: DHCP Message Type %d\n", __FUNCTION__, sDhcpEvtData.dhcpMsgType)); |
There was a problem hiding this comment.
The dhcpClientEventsHandler function does not check if rbusObject_GetValue returns NULL before using the rbusValue. If the expected fields are not present in the event data, this could lead to a NULL pointer dereference. Add NULL checks after each rbusObject_GetValue call.
| /* | ||
| * @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. | ||
| */ |
There was a problem hiding this comment.
The comment block starts with /* on line 51 instead of /** which is inconsistent with the Doxygen-style documentation used for other functions in this file (lines 62, 72, 86). Use /** to maintain consistency.
| 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 | ||
| strcpy_s(pMacAddress, 32, pMac); | ||
| break; | ||
| } | ||
| } | ||
| fclose(pFILE); | ||
| } | ||
| else | ||
| { | ||
| CcspTraceError(("%s: Failed to open /tmp/factory_nvram.data\n", __FUNCTION__)); | ||
| } | ||
| } |
There was a problem hiding this comment.
The readMacAddress function does not handle the case where the file is successfully opened but the "EMTA " line is not found. In this scenario, pMacAddress would remain uninitialized or contain its previous value, potentially leading to the use of an invalid MAC address. Consider initializing pMacAddress to an empty string at the start of the function or logging an error if the expected line is not found after reading the entire file.
| DHCP_IPv4=0, | ||
| DHCP_IPv6, | ||
| DHCP_BOTH, | ||
| DHCP_NONE | ||
| }DhcpVersion; | ||
|
|
There was a problem hiding this comment.
The DhcpVersion type is used but the enum values use inconsistent casing. The enum name is "DhcpVersion" (camelCase with capital D and V) but the values like DHCP_IPv4 use all uppercase with underscores, and IPv4/IPv6 have inconsistent internal casing. Consider using consistent naming conventions throughout, such as DHCP_IPV4, DHCP_IPV6, etc.
| DHCP_IPv4=0, | |
| DHCP_IPv6, | |
| DHCP_BOTH, | |
| DHCP_NONE | |
| }DhcpVersion; | |
| DHCP_IPV4 = 0, | |
| DHCP_IPV6, | |
| DHCP_BOTH, | |
| DHCP_NONE | |
| } DhcpVersion; | |
| /* Backward compatibility for older enum value names */ | |
| #define DHCP_IPv4 DHCP_IPV4 | |
| #define DHCP_IPv6 DHCP_IPV6 |
| CosaMTAInitializeEthWanProv(pMyObject); | ||
| } | ||
| #if defined (SCXF10) | ||
| CcspTraceWarning(("%s:%d, current_wan_state up, Initializing MTA Inteface \n",__FUNCTION__,__LINE__)); |
There was a problem hiding this comment.
Spelling error: "Inteface" should be "Interface".
| CcspTraceWarning(("%s:%d, current_wan_state up, Initializing MTA Inteface \n",__FUNCTION__,__LINE__)); | |
| CcspTraceWarning(("%s:%d, current_wan_state up, Initializing MTA Interface \n",__FUNCTION__,__LINE__)); |
| snprintf(cCmd, sizeof(cCmd), "ip link add link %s name %s type macvlan mode bridge", cWanIfname, cVoiceSupportIfaceName); | ||
| system(cCmd); | ||
| snprintf(cCmd, sizeof(cCmd), "ip link set dev %s address %s", cVoiceSupportIfaceName, cMtaInterfaceMac); | ||
| system(cCmd); | ||
| snprintf(cCmd, sizeof(cCmd), "ip link set dev %s up", cVoiceSupportIfaceName); | ||
| system(cCmd); |
There was a problem hiding this comment.
The system() calls to create and configure the macvlan interface do not check return values. If any of these commands fail (e.g., interface already exists, insufficient permissions, invalid parameters), the failure will be silent and subsequent operations may fail in unexpected ways. Consider checking the return value of system() calls and logging errors or taking appropriate action when commands fail.
f68286a to
8d305c7
Compare
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 8 out of 8 changed files in this pull request and generated 19 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| syscfg_get(NULL, "VoiceSupport_IfaceName",cVoiceSupportIfaceName, sizeof(cVoiceSupportIfaceName)); | ||
| syscfg_get(NULL, "VoiceSupport_Mode",cVoiceSupportMode, sizeof(cVoiceSupportMode)); | ||
| syscfg_get(NULL, "VoiceSupport_Enabled", cVoiceSupportEnabled, sizeof(cVoiceSupportEnabled)); | ||
|
|
There was a problem hiding this comment.
Missing validation for syscfg_get return values. The syscfg_get calls on lines 239-241 don't check their return values to determine if the retrieval was successful. If syscfg_get fails, the buffers may contain garbage or empty values, leading to incorrect behavior. While there are checks for empty strings, explicit return value checking would make the error handling more robust.
| syscfg_get(NULL, "VoiceSupport_IfaceName",cVoiceSupportIfaceName, sizeof(cVoiceSupportIfaceName)); | |
| syscfg_get(NULL, "VoiceSupport_Mode",cVoiceSupportMode, sizeof(cVoiceSupportMode)); | |
| syscfg_get(NULL, "VoiceSupport_Enabled", cVoiceSupportEnabled, sizeof(cVoiceSupportEnabled)); | |
| int rc; | |
| rc = syscfg_get(NULL, "VoiceSupport_IfaceName", cVoiceSupportIfaceName, sizeof(cVoiceSupportIfaceName)); | |
| if (rc != 0) | |
| { | |
| CcspTraceError(("%s:%d, syscfg_get failed for key VoiceSupport_IfaceName (rc=%d)\n", __FUNCTION__, __LINE__, rc)); | |
| } | |
| rc = syscfg_get(NULL, "VoiceSupport_Mode", cVoiceSupportMode, sizeof(cVoiceSupportMode)); | |
| if (rc != 0) | |
| { | |
| CcspTraceError(("%s:%d, syscfg_get failed for key VoiceSupport_Mode (rc=%d)\n", __FUNCTION__, __LINE__, rc)); | |
| } | |
| rc = syscfg_get(NULL, "VoiceSupport_Enabled", cVoiceSupportEnabled, sizeof(cVoiceSupportEnabled)); | |
| if (rc != 0) | |
| { | |
| CcspTraceError(("%s:%d, syscfg_get failed for key VoiceSupport_Enabled (rc=%d)\n", __FUNCTION__, __LINE__, rc)); | |
| } |
| { | ||
| //Create the macVlan | ||
| CcspTraceInfo(("%s:%d, Creating macVlan interface %s with mac %s\n", __FUNCTION__, __LINE__, pVoiceSupportIfaceName, cMtaInterfaceMac)); | ||
| char cCmd[2048] = {0}; |
There was a problem hiding this comment.
The buffer size for cCmd is excessively large (2048 bytes) when constructing simple commands that use interface names and MAC addresses which have known maximum lengths. This wastes stack space. Consider using a more appropriate size like 256 or 512 bytes, which is sufficient for these commands.
| char cCmd[2048] = {0}; | |
| char cCmd[256] = {0}; |
| #if 0 | ||
| typedef struct | ||
| { | ||
| char intfName[VOICE_IFNAME_LEN]; | ||
| uint8_t isPhyUp; /* link state (1=up) */ | ||
|
|
||
| /* DHCPv4 */ | ||
| uint8_t isIpv4Up; /* IPv4 address valid */ | ||
| char ipv4Addr[VOICE_IPV4_ADDR_LEN]; /* IP or CIDR, e.g., "192.0.2.10/24" */ | ||
| char v4NextServerIp[VOICE_IPV4_ADDR_LEN]; /* BOOTP siaddr */ | ||
| char v4ServerHostName[VOICE_STRMAX_128]; /* BOOTP sname or opt 66 */ | ||
| char v4BootFileName[VOICE_STRMAX_128]; /* BOOTP file or opt 67 */ | ||
| char v4DnsServers[VOICE_IPV4_ADDR_LEN*4]; /* opt 6, comma-separated */ | ||
| char v4LogServerIp[VOICE_IPV4_ADDR_LEN]; /* opt 7 (syslog), if present */ | ||
| char v4HostName[VOICE_STRMAX_128]; /* opt 12 */ | ||
| char v4DomainName[VOICE_STRMAX_128]; /* opt 15 */ | ||
| char v4ProvServer[VOICE_STRMAX_128]; /* PacketCable v4: Opt122 subopt 3 (IPv4/FQDN) */ | ||
|
|
||
| /* DHCPv6 */ | ||
| uint8_t isIpv6Up; /* at least one global IPv6 present */ | ||
| char ipv6GlobalAddr[VOICE_IPV6_ADDR_LEN]; /* preferred global address */ | ||
| char v6TftpServerIp[VOICE_IPV6_ADDR_LEN]; /* Opt17 subopt 32 (IPv6) */ | ||
| char v6TftpFileName[VOICE_STRMAX_128]; /* Opt17 subopt 33 */ | ||
| char v6SyslogServerIp[VOICE_IPV6_ADDR_LEN]; /* Opt17 subopt 34 (IPv6) */ | ||
| char v6ProvServerIp[VOICE_STRMAX_128]; /* Opt17 subopt 2171:3 (IPv6/FQDN/IPv4 as text) */ | ||
| char v6DnsServers[VOICE_IPV6_ADDR_LEN*4]; /* opt 23, comma-separated */ | ||
| char v6DomainName[VOICE_STRMAX_128]; /* opt 24 (first decoded name) */ | ||
| char v6ClientFqdn[VOICE_STRMAX_128]; /* opt 39 (decoded FQDN) */ | ||
| } VoiceInterfaceInfoType; | ||
| #endif |
There was a problem hiding this comment.
Dead code that should be removed. Lines 299-328 contain a large block of commented-out structure definition that appears to be documentation or a reference. This should either be moved to proper documentation or removed entirely to avoid clutter and confusion.
| pthread_mutex_lock(&voiceDataProcessingMutex); | ||
| CcspTraceInfo(("%s:%d, DHCP Client Events Handler Thread started\n", __FUNCTION__, __LINE__)); | ||
| DhcpEventData_t *pDhcpEvtData = (DhcpEventData_t *)pArg; | ||
| pthread_detach(pthread_self()); | ||
|
|
||
| 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: | ||
| { | ||
| processVoiceDhcpEvent((DhcpEventData_t *)pDhcpEvtData); | ||
| 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_mutex_unlock(&voiceDataProcessingMutex); |
There was a problem hiding this comment.
Incorrect mutex usage pattern. The mutex is locked at the beginning of the thread (line 361), but if the thread is cancelled or exits unexpectedly before line 390, the mutex will remain locked indefinitely. The mutex lock should be placed after pthread_detach (line 364) and should use a more localized scope. Additionally, unlocking the mutex (line 390) immediately before pthread_exit (line 391) serves little purpose since the thread is terminating. Consider restructuring the mutex usage to protect only the critical sections where shared resources are accessed.
|
|
||
| if (cVoiceSupportIfaceName[0] == '\0') | ||
| { | ||
| CcspTraceError(("%s:%d, VoiceSupport_IfaceName not set in syscfg, using default mta0\n", __FUNCTION__, __LINE__)); |
There was a problem hiding this comment.
The error log message on line 251 uses CcspTraceError when it should use CcspTraceWarning or CcspTraceInfo. The message indicates a fallback to default behavior (using "mta0") rather than an actual error condition. This could confuse log analysis and alerting systems.
| CcspTraceError(("%s:%d, VoiceSupport_IfaceName not set in syscfg, using default mta0\n", __FUNCTION__, __LINE__)); | |
| CcspTraceWarning(("%s:%d, VoiceSupport_IfaceName not set in syscfg, using default mta0\n", __FUNCTION__, __LINE__)); |
| *@breif 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 | ||
| */ |
There was a problem hiding this comment.
Spelling error in the documentation comment. The word "breif" should be "brief".
| *@breif 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 | |
| */ | |
| *@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 | |
| */ |
| free(pDhcpEvtData); | ||
| return; | ||
| } | ||
| usleep(10000); // Sleep for 10ms to allow thread to start |
There was a problem hiding this comment.
Potential thread creation issue with insufficient error recovery. If pthread_create fails on line 273, the allocated memory is freed correctly, but the calling code in dhcpClientEventsHandler continues execution without knowing that processing failed. Additionally, the usleep(10000) on line 279 is a fragile synchronization mechanism. Consider using proper thread synchronization primitives or redesigning to ensure the thread has properly started before continuing.
| usleep(10000); // Sleep for 10ms to allow thread to start |
| rbusValue_t rbusValue = rbusObject_GetValue(pRbusEvent->data, "Ifname"); | ||
| snprintf(pDhcpEvtData->cIfaceName, sizeof(pDhcpEvtData->cIfaceName), "%s", rbusValue_GetString(rbusValue, NULL)); | ||
| CcspTraceInfo(("%s: DHCP %s event for interface %s\n", __FUNCTION__, | ||
| (pDhcpEvtData->dhcpVersion == DHCP_IPv4) ? "IPv4" : "IPv6", pDhcpEvtData->cIfaceName)); | ||
|
|
||
| rbusValue = rbusObject_GetValue(pRbusEvent->data, "MsgType"); | ||
| pDhcpEvtData->dhcpMsgType = (DHCP_MESSAGE_TYPE)rbusValue_GetUInt32(rbusValue); | ||
| CcspTraceInfo(("%s: DHCP Message Type %d\n", __FUNCTION__, pDhcpEvtData->dhcpMsgType)); | ||
|
|
||
| if (DHCP_LEASE_UPDATE == pDhcpEvtData->dhcpMsgType) | ||
| { | ||
| int iByteLen = 0; | ||
| rbusValue = rbusObject_GetValue(pRbusEvent->data, "LeaseInfo"); | ||
| const uint8_t* pLeaseInfo = rbusValue_GetBytes(rbusValue, &iByteLen); |
There was a problem hiding this comment.
Missing null check for rbusObject_GetValue return value. The rbusValue returned by rbusObject_GetValue on lines 220, 225, and 232 may be NULL if the specified property doesn't exist in the event data. This would cause crashes when attempting to extract values. Add null checks before using these values.
| } | ||
| CcspTraceInfo(("%s: Received event %s\n", __FUNCTION__, pRbusEvent->name)); | ||
|
|
||
| pthread_t dhcpEventThreadId = -1; |
There was a problem hiding this comment.
Ambiguous variable naming. The variable "dhcpEventThreadId" is initialized to -1 (line 208), but pthread_t is typically an opaque type that shouldn't be initialized with integer values. The variable should either not be initialized or should be initialized with appropriate pthread types. Additionally, the thread ID is not used after thread creation, making the variable name misleading.
| pthread_t dhcpEventThreadId = -1; | |
| pthread_t dhcpEventThreadId; |
|
|
||
| typedef struct _DhcpEventData | ||
| { | ||
| char cIfaceName[32]; |
There was a problem hiding this comment.
Inconsistent field naming in struct. The struct _DhcpEventData uses "cIfaceName" but it's referenced as "ifName" in the code comment on line 283 of cosa_voice_apis.c. While "cIfaceName" follows Hungarian notation (c prefix for char array), this should be consistently named across the codebase. Consider using just "ifaceName" or "interfaceName" for clarity.
Reason for change:Subscribing for DHCP events via R-BUS Test Procedure: verify the DHCP message is received or not Risks: High Priority: P1
8d305c7 to
e92cc75
Compare
Reason for change:Initializing the voice Test Procedure: verify the voice is initialized or not Risks: High Priority: P1
Reason for change:Extractign the received data from the provider Test Procedure: verify the voice is initialized or not Risks: High Priority: P1
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 13 out of 13 changed files in this pull request and generated 23 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| /* Default all protocol to UDP except TCP. Do not support "TCP or UDP" option. */ | ||
| if (!strcmp("TCP", pFirewallRule->protocol)) | ||
| { | ||
| strcpy(protocol, "TCP"); |
There was a problem hiding this comment.
The function cbSetFirewallRule uses strcpy on line 849 to copy into a stack buffer without bounds checking. While the source "TCP" is a string literal and safe in this case, using strcpy is generally discouraged. Consider using strncpy or safer string handling functions to maintain consistency with security best practices throughout the codebase.
| strcpy(protocol, "TCP"); | |
| strcpy_s(protocol, sizeof(protocol), "TCP"); |
| 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)) | ||
| { | ||
| strcpy(protocol, "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); | ||
| } |
There was a problem hiding this comment.
The snprintf call constructs an iptables command using user-controlled data (protocol, ifName, destinationPort from pFirewallRule) without input validation. While these values come from the voice HAL, they could potentially contain shell metacharacters if compromised. The command is then passed to setSysevent which eventually executes it via sysevent. Validate that ifName contains only alphanumeric characters and valid interface name characters, protocol is either "TCP" or "UDP", and destinationPort is a valid port number before constructing the command.
| 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)) | |
| { | |
| strcpy(protocol, "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); | |
| } | |
| static int isValidInterfaceName(const char *ifName) | |
| { | |
| size_t i; | |
| if (ifName == NULL || ifName[0] == '\0') | |
| { | |
| return 0; | |
| } | |
| for (i = 0; ifName[i] != '\0'; ++i) | |
| { | |
| unsigned char c = (unsigned char)ifName[i]; | |
| if (!isalnum(c) && c != '_' && c != '.' && c != '-' && c != ':') | |
| { | |
| return 0; | |
| } | |
| } | |
| return 1; | |
| } | |
| static int isValidProtocol(const char *protocolIn, char *protocolOut, size_t protocolOutLen) | |
| { | |
| if (protocolOut == NULL || protocolOutLen < 4) | |
| { | |
| return 0; | |
| } | |
| /* Only allow TCP or UDP explicitly. Default to UDP only if input is exactly "UDP". */ | |
| if (protocolIn != NULL) | |
| { | |
| if (strcmp(protocolIn, "TCP") == 0) | |
| { | |
| strcpy(protocolOut, "TCP"); | |
| return 1; | |
| } | |
| else if (strcmp(protocolIn, "UDP") == 0) | |
| { | |
| strcpy(protocolOut, "UDP"); | |
| return 1; | |
| } | |
| } | |
| return 0; | |
| } | |
| static int isValidPort(uint32_t port) | |
| { | |
| /* Valid TCP/UDP ports are in the range 1-65535. */ | |
| if (port == 0 || port > 65535) | |
| { | |
| return 0; | |
| } | |
| return 1; | |
| } | |
| static uint8_t cbSetFirewallRule(VoiceFirewallRuleType *pFirewallRule) | |
| { | |
| char command[1000] = { 0 }; | |
| char protocol[10] = "UDP"; | |
| if (pFirewallRule == NULL) | |
| { | |
| AnscTraceError(("%s: pFirewallRule is NULL\n", __FUNCTION__)); | |
| return 0; | |
| } | |
| /* Validate inputs before constructing firewall rule when enabling. */ | |
| if (pFirewallRule->enable) | |
| { | |
| if (!isValidInterfaceName(pFirewallRule->ifName)) | |
| { | |
| AnscTraceError(("%s: Invalid interface name: %s\n", | |
| __FUNCTION__, | |
| (pFirewallRule->ifName != NULL) ? pFirewallRule->ifName : "(null)")); | |
| return 0; | |
| } | |
| if (!isValidProtocol(pFirewallRule->protocol, protocol, sizeof(protocol))) | |
| { | |
| AnscTraceError(("%s: Invalid protocol: %s\n", | |
| __FUNCTION__, | |
| (pFirewallRule->protocol != NULL) ? pFirewallRule->protocol : "(null)")); | |
| return 0; | |
| } | |
| if (!isValidPort(pFirewallRule->destinationPort)) | |
| { | |
| AnscTraceError(("%s: Invalid destination port: %u\n", | |
| __FUNCTION__, pFirewallRule->destinationPort)); | |
| return 0; | |
| } | |
| } | |
| AnscTraceInfo(("%s: enable=%d, ifName=%s, protocol=%s, destPort=%u\n", | |
| __FUNCTION__, pFirewallRule->enable, | |
| (pFirewallRule->ifName != NULL) ? pFirewallRule->ifName : "(null)", | |
| 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); | |
| } |
| { | ||
| 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) |
There was a problem hiding this comment.
The size check on line 268 verifies that the received lease info size matches sizeof(DHCP_MGR_IPV4_MSG) using equality. While this ensures the structures match exactly, it's fragile to structure padding and version changes. If the structure size changes slightly (due to compiler differences, padding, or version updates), valid data might be rejected. Consider using a minimum size check instead of exact equality, or add version/magic number fields to the structure for more robust validation.
config/CcspMtaAgent.xml
Outdated
| <?ifdef FEATURE_SUPPORT_VOICE_MTA?> | ||
| <object><name>Services</name><objectType>object</objectType> | ||
| <objects><object><name>BrcmVoiceService</name><objectType>object</objectType> | ||
|
|
||
| <functions> | ||
| <func_SetParamStringValue>BrcmVoiceService_NotifyIfIp_SetParamStringValue</func_SetParamStringValue> | ||
| </functions> | ||
|
|
||
| <parameters> | ||
| <parameter> | ||
| <name>X_BROADCOM_COM_NotifyIfIp</name> | ||
| <type>string(128)</type><syntax>string</syntax><writable>true</writable> | ||
| </parameter> | ||
| </parameters> | ||
|
|
||
| <objects><object><name>1</name><objectType>object</objectType> | ||
|
|
||
| <functions> | ||
| <func_GetParamStringValue>BrcmVoiceService_GetParamStringValue</func_GetParamStringValue> | ||
| <func_SetParamStringValue>BrcmVoiceService_SetParamStringValue</func_SetParamStringValue> | ||
| </functions> | ||
|
|
||
| <parameters> | ||
| <parameter> | ||
| <name>X_BROADCOM_COM_BoundIfName</name> | ||
| <type>string(32)</type><syntax>string</syntax><writable>true</writable> | ||
| </parameter> | ||
| <parameter> | ||
| <name>X_BROADCOM_COM_ModuleLogLevels</name> | ||
| <type>string(128)</type><syntax>string</syntax><writable>true</writable> | ||
| </parameter> | ||
| </parameters> | ||
|
|
||
| <objects><object><name>SIP</name><objectType>object</objectType> | ||
|
|
||
| <objects> | ||
| <object><name>Network</name><objectType>object</objectType> | ||
| <objects><object><name>1</name><objectType>object</objectType> | ||
|
|
||
| <functions> | ||
| <func_GetParamStringValue>BrcmVoiceNetwork_GetParamStringValue</func_GetParamStringValue> | ||
| <func_SetParamStringValue>BrcmVoiceNetwork_SetParamStringValue</func_SetParamStringValue> | ||
| </functions> | ||
|
|
||
| <parameters> | ||
| <parameter> | ||
| <name>ProxyServer</name> | ||
| <type>string(128)</type><syntax>string</syntax><writable>true</writable> | ||
| </parameter> | ||
| <parameter> | ||
| <name>RegistrarServer</name> | ||
| <type>string(128)</type><syntax>string</syntax><writable>true</writable> | ||
| </parameter> | ||
| </parameters> | ||
|
|
||
| </object></objects> | ||
| </object> | ||
|
|
||
| <object><name>Client</name><objectType>object</objectType> | ||
| <objects><object><name>1</name><objectType>object</objectType> | ||
|
|
||
| <functions> | ||
| <func_GetParamStringValue>BrcmVoiceClient_GetParamStringValue</func_GetParamStringValue> | ||
| <func_SetParamStringValue>BrcmVoiceClient_SetParamStringValue</func_SetParamStringValue> | ||
| </functions> | ||
|
|
||
| <parameters> | ||
| <parameter> | ||
| <name>RegisterURI</name> | ||
| <type>string(128)</type><syntax>string</syntax><writable>true</writable> | ||
| </parameter> | ||
| <parameter> | ||
| <name>AuthUserName</name> | ||
| <type>string(128)</type><syntax>string</syntax><writable>true</writable> | ||
| </parameter> | ||
| <parameter> | ||
| <name>AuthPassword</name> | ||
| <type>string(128)</type><syntax>string</syntax><writable>true</writable> | ||
| </parameter> | ||
| </parameters> | ||
|
|
||
| </object></objects> | ||
| </object> | ||
| </objects> | ||
|
|
||
| </object></objects> | ||
| </object></objects> | ||
| </object></objects> | ||
| </object> | ||
| <?endif?> |
There was a problem hiding this comment.
The XML configuration introduces new data model objects under Services.BrcmVoiceService with various string parameters. However, there's no input validation specified in the XML or visible in the corresponding DML functions (BrcmVoiceService_SetParamStringValue, BrcmVoiceNetwork_SetParamStringValue, BrcmVoiceClient_SetParamStringValue) that validates the input before passing it to bcm_generic_setParameterValues. Consider adding length checks and format validation for parameters like RegisterURI, ProxyServer, RegistrarServer, etc., to prevent injection attacks or malformed data from being stored.
| free(pDhcpEvtData); | ||
| return; | ||
| } | ||
| usleep(10000); // Sleep for 10ms to allow thread to start |
There was a problem hiding this comment.
The usleep(10000) call after pthread_create is used to "allow thread to start". This is a race condition fix attempt that is fragile and not guaranteed to work. Instead of sleeping, consider using proper synchronization mechanisms (e.g., condition variables, or checking return values) to ensure the thread has properly started if that's actually necessary. In most cases, pthread_create returning 0 is sufficient indication that the thread was created successfully, and no additional synchronization is needed.
| usleep(10000); // Sleep for 10ms to allow thread to start |
| rbusValue_t rbusValue = rbusObject_GetValue(dataObj, "IfName"); | ||
| const char *pIfaceName = rbusValue_GetString(rbusValue, NULL); |
There was a problem hiding this comment.
In the dhcpClientEventsHandler, when retrieving the IfName from the RBUS event (lines 240-242), rbusValue_GetString is called without checking if rbusValue is NULL. If rbusObject_GetValue returns NULL (meaning the "IfName" property doesn't exist), the subsequent rbusValue_GetString call will dereference a NULL pointer. Add a NULL check for rbusValue before calling rbusValue_GetString.
| rbusValue = rbusObject_GetValue(dataObj, "MsgType"); | ||
| pDhcpEvtData->dhcpMsgType = (DHCP_MESSAGE_TYPE)rbusValue_GetUInt32(rbusValue); |
There was a problem hiding this comment.
Similar to the IfName retrieval, when getting "MsgType" on lines 252-253, rbusValue is not checked for NULL before calling rbusValue_GetUInt32. Add NULL checks for all rbusObject_GetValue calls before using the returned rbusValue.
| .userData = NULL, | ||
| .handle = NULL, | ||
| .asyncHandler = NULL, | ||
| .publishOnSubscribe = true |
There was a problem hiding this comment.
The publishOnSubscribe flag is set to true in the RBUS event subscription on line 360. This means when subscribing, the DHCP manager will immediately publish the current state. The handler checks for and unwraps "initialValue" (lines 233-239), which is good. However, if the initial event contains no valid LeaseInfo (e.g., DHCP client not yet started), the handler will free pDhcpEvtData and return early (lines 309-311, 316-318). This is correct behavior, but consider logging at Info level rather than Error level for these cases, since receiving an initial event without lease info is expected when publishOnSubscribe is true and DHCP hasn't completed yet.
| CcspTraceWarning(("%s:%d, current_wan_state up, Initializing MTA Inteface \n",__FUNCTION__,__LINE__)); | ||
| startVoiceFeature(); |
There was a problem hiding this comment.
The function startVoiceFeature is called in two different locations (lines 1314 and 1362) when current_wan_state is up. This could lead to duplicate MTA interface creation attempts if both code paths are executed. The static boolean isVoiceFeatureStarted provides protection, but calling the same function from multiple locations indicates unclear control flow. Consider consolidating the initialization logic to avoid redundant calls.
| CcspTraceWarning(("%s:%d, current_wan_state up, Initializing MTA Inteface \n",__FUNCTION__,__LINE__)); | |
| startVoiceFeature(); | |
| CcspTraceWarning(("%s:%d, current_wan_state up notification received\n", __FUNCTION__, __LINE__)); |
| AnscTraceInfo(("%s: Calling sysevent command: %s\n", __FUNCTION__, pCommand)); | ||
| if (ui8Enable) | ||
| { | ||
| if (NULL == pCommand || '\0' == pCommand[0] || strlen(pCommand) <= 0) |
There was a problem hiding this comment.
The condition on line 825 checks three redundant conditions: NULL == pCommand, '\0' == pCommand[0], and strlen(pCommand) <= 0. The first check prevents NULL pointer dereference. The second check (pCommand[0] == '\0') is sufficient to check for empty string. The third check strlen(pCommand) <= 0 is redundant because strlen cannot return negative values, and checking for 0 is the same as pCommand[0] == '\0'. Simplify to: if (NULL == pCommand || pCommand[0] == '\0')
| if (NULL == pCommand || '\0' == pCommand[0] || strlen(pCommand) <= 0) | |
| if (NULL == pCommand || '\0' == pCommand[0]) |
Reason for change:filling the hal structure with provisioning server details Test Procedure: verify the voice is initialized or not Risks: High Priority: P1
Reason for change:Extracting the received data from the provider Test Procedure: verify the voice is initialized or not Risks: High Priority: P1
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 13 out of 13 changed files in this pull request and generated 15 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| return; | ||
| } | ||
| else | ||
| { | ||
| CcspTraceInfo(("%s: PSM_Get_Record_Value2 successful for param %s with value %s\n", __FUNCTION__, DHCPv4_VOICE_SUPPORT_PARAM, | ||
| pParamValue)); | ||
| snprintf(cBaseParam, sizeof(cBaseParam), "%s",pParamValue); |
There was a problem hiding this comment.
If PSM_Get_Record_Value2 fails, cBaseParam remains empty, but subsequent code still builds RBUS parameter names from it (e.g., ".Interface", ".Events"), which will cause RBUS set/subscribe to target invalid paths. Consider setting a safe default or returning an error that prevents later RBUS operations when cBaseParam is unset.
| return; | |
| } | |
| else | |
| { | |
| CcspTraceInfo(("%s: PSM_Get_Record_Value2 successful for param %s with value %s\n", __FUNCTION__, DHCPv4_VOICE_SUPPORT_PARAM, | |
| pParamValue)); | |
| snprintf(cBaseParam, sizeof(cBaseParam), "%s",pParamValue); | |
| /* 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)); | |
| snprintf(cBaseParam, sizeof(cBaseParam), "%s", pParamValue); |
| case DHCP_CLIENT_STARTED: | ||
| case DHCP_CLIENT_STOPPED: | ||
| case DHCP_LEASE_RENEW: | ||
| case DHCP_LEASE_DEL: | ||
| case DHCP_LEASE_UPDATE: |
There was a problem hiding this comment.
DHCP_CLIENT_STOPPED / DHCP_LEASE_DEL represent loss of lease/client down, but they are currently grouped with renew/update and will be processed as if the interface is up. Handle these message types separately (cleanup routes/rules and notify down) instead of treating them like lease updates.
| 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); |
There was a problem hiding this comment.
initializeVoiceSupport derives iOption122Len from strlen(cOption122)/2 and then declares a VLA uint8_t ui8Option122Data[iOption122Len];. If cOption122 is empty, odd-length, or unexpectedly large, this can result in a zero-sized/huge VLA and undefined behavior or stack exhaustion. Add validation (non-empty, even hex length, reasonable max) and avoid VLAs by using a fixed-size buffer or heap allocation with bounds checks.
| if (len >= (int)(sizeof(in6Addr.s6_addr) + 4)) // Account for 2 byte tag and length | ||
| { | ||
| char tmpBuff[MAX_STR] = {0}; | ||
|
|
||
| memcpy(&in6Addr.s6_addr, tmpBuff, sizeof(in6Addr.s6_addr)); | ||
| inet_ntop(AF_INET6, &in6Addr, pIfInfo->v6DnsServers, sizeof(pIfInfo->v6DnsServers)); | ||
| AnscTraceVerbose(("option 23 v6DnsServers = [%s]\n", pIfInfo->v6DnsServers)); |
There was a problem hiding this comment.
This memcpy populates in6Addr from tmpBuff, but tmpBuff is never filled. As a result, v6DnsServers will be based on zero/garbage instead of option-23 data. Copy from the decrypted option-23 buffer (or parse it properly) before calling inet_ntop.
| if (len >= (int)(sizeof(in6Addr.s6_addr) + 4)) // Account for 2 byte tag and length | |
| { | |
| char tmpBuff[MAX_STR] = {0}; | |
| memcpy(&in6Addr.s6_addr, tmpBuff, sizeof(in6Addr.s6_addr)); | |
| inet_ntop(AF_INET6, &in6Addr, pIfInfo->v6DnsServers, sizeof(pIfInfo->v6DnsServers)); | |
| AnscTraceVerbose(("option 23 v6DnsServers = [%s]\n", pIfInfo->v6DnsServers)); | |
| if (len >= (int)(sizeof(uint16_t) * 2 + sizeof(in6Addr.s6_addr))) /* option code + length + at least one IPv6 address */ | |
| { | |
| /* The option format is: uint16_t code, uint16_t length, followed by data (one or more IPv6 addresses). */ | |
| subLen = ntohs(*(uint16_t *)(buffer + sizeof(uint16_t))); /* length of the option data */ | |
| if (subLen >= (int)sizeof(in6Addr.s6_addr)) | |
| { | |
| /* Copy the first IPv6 address from the option payload */ | |
| memcpy(in6Addr.s6_addr, buffer + (sizeof(uint16_t) * 2), sizeof(in6Addr.s6_addr)); | |
| inet_ntop(AF_INET6, &in6Addr, pIfInfo->v6DnsServers, sizeof(pIfInfo->v6DnsServers)); | |
| AnscTraceVerbose(("option 23 v6DnsServers = [%s]\n", pIfInfo->v6DnsServers)); | |
| } |
| subLen = buf[i]; | ||
| if (0 == subLen) | ||
| { | ||
| fqdn[i - 1] = '\0'; | ||
| } |
There was a problem hiding this comment.
When subLen is 0, this writes fqdn[i-1]. If i==0 (e.g., empty/zero-length input), this becomes an out-of-bounds write. Add guards for empty input and only write fqdn[i-1] when i>0, while still ensuring the output is NUL-terminated.
| }; | ||
|
|
||
| pthread_t eventSubscriptionThreadId = -1; | ||
| if (pthread_create(&eventSubscriptionThreadId, NULL, (void *)eventSubscriptionThread, (void *)&rbusEventSubscription) != 0) |
There was a problem hiding this comment.
pthread_create is called with (void *)eventSubscriptionThread as the start routine. This is an incompatible cast (function pointer -> object pointer) and can fail compilation or invoke UB. Pass eventSubscriptionThread directly (type void* (*)(void*)) without casting to void*.
| if (pthread_create(&eventSubscriptionThreadId, NULL, (void *)eventSubscriptionThread, (void *)&rbusEventSubscription) != 0) | |
| if (pthread_create(&eventSubscriptionThreadId, NULL, eventSubscriptionThread, (void *)&rbusEventSubscription) != 0) |
| case DHCP_CLIENT_STOPPED: | ||
| case DHCP_LEASE_RENEW: | ||
| case DHCP_LEASE_DEL: | ||
| case DHCP_LEASE_UPDATE: | ||
| case DHCP_CLIENT_FAILED: | ||
| { | ||
| processVoiceDhcpEvent((DhcpEventData_t *)pDhcpEvtData); | ||
| break; | ||
| } | ||
|
|
There was a problem hiding this comment.
processVoiceDhcpEvent() adds routes/rules and populates VoiceInterfaceInfoType with isIpv4Up = 1 for every message type in this block. For failure/stop/delete events this will push incorrect state and leave stale routing policy. Consider branching by dhcpMsgType so only lease-acquired/renewed paths add routes, and teardown paths remove them.
| case DHCP_CLIENT_STOPPED: | |
| case DHCP_LEASE_RENEW: | |
| case DHCP_LEASE_DEL: | |
| case DHCP_LEASE_UPDATE: | |
| case DHCP_CLIENT_FAILED: | |
| { | |
| processVoiceDhcpEvent((DhcpEventData_t *)pDhcpEvtData); | |
| break; | |
| } | |
| case DHCP_LEASE_RENEW: | |
| case DHCP_LEASE_UPDATE: | |
| { | |
| /* Lease-acquired/renew/update events: process to add/update routes and state */ | |
| processVoiceDhcpEvent((DhcpEventData_t *)pDhcpEvtData); | |
| break; | |
| } | |
| case DHCP_CLIENT_STOPPED: | |
| case DHCP_LEASE_DEL: | |
| case DHCP_CLIENT_FAILED: | |
| { | |
| /* Teardown/failure events: avoid calling processVoiceDhcpEvent() to prevent | |
| * incorrectly marking IPv4 as up or adding routes. Any teardown-specific | |
| * handling should be implemented in a dedicated path. | |
| */ | |
| CcspTraceInfo(("%s: DHCPv4 teardown/failure event %d received for interface %s\n", | |
| __FUNCTION__, pDhcpEvtData->dhcpMsgType, pDhcpEvtData->ifName)); | |
| break; | |
| } |
| /* Read tag */ | ||
| subCode = ntohs(*((uint16_t *)(buffPtr))); | ||
| buffPtr += sizeof(uint16_t); | ||
|
|
||
| /* Read length */ | ||
| subLen = ntohs(*((uint16_t *)(buffPtr))); |
There was a problem hiding this comment.
Casting buffPtr to uint16_t* can cause unaligned memory access on some platforms. Read the 16-bit fields with memcpy (or byte-wise) before applying ntohs to avoid UB/crashes.
| /* Read tag */ | |
| subCode = ntohs(*((uint16_t *)(buffPtr))); | |
| buffPtr += sizeof(uint16_t); | |
| /* Read length */ | |
| subLen = ntohs(*((uint16_t *)(buffPtr))); | |
| uint16_t tmp16; | |
| /* Read tag */ | |
| memcpy(&tmp16, buffPtr, sizeof(tmp16)); | |
| subCode = ntohs(tmp16); | |
| buffPtr += sizeof(uint16_t); | |
| /* Read length */ | |
| memcpy(&tmp16, buffPtr, sizeof(tmp16)); | |
| subLen = ntohs(tmp16); |
| #if defined(SCXF10) | ||
| getIfaceIndexInfo(); | ||
| initRbusHandle(); | ||
| #endif |
There was a problem hiding this comment.
getIfaceIndexInfo() / initRbusHandle() are called when SCXF10 is defined, but their implementations are only compiled when the Automake conditional VOICE_SUPPORT_ENABLED adds cosa_rbus_apis.c to the build. If SCXF10 can be enabled without --enable-voice_support, this will cause unresolved symbols at link time. Consider guarding these calls with the same feature macro used for compilation, or always compile the RBUS/voice sources when SCXF10 is enabled.
| pthread_t eventSubscriptionThreadId = -1; | ||
| if (pthread_create(&eventSubscriptionThreadId, NULL, (void *)eventSubscriptionThread, (void *)&rbusEventSubscription) != 0) | ||
| { | ||
| CcspTraceError(("%s: Failed to create event subscription thread\n", __FUNCTION__)); | ||
| return; | ||
| } | ||
| pthread_detach(eventSubscriptionThreadId); |
There was a problem hiding this comment.
subscribeDhcpClientEvents() passes the address of a stack-allocated rbusEventSubscription_t into a detached thread. After this function returns, the thread will dereference invalid memory (use-after-scope). Allocate the subscription struct with a lifetime that outlives the thread (heap/static), or subscribe synchronously without spawning a thread.
| pthread_t eventSubscriptionThreadId = -1; | |
| if (pthread_create(&eventSubscriptionThreadId, NULL, (void *)eventSubscriptionThread, (void *)&rbusEventSubscription) != 0) | |
| { | |
| CcspTraceError(("%s: Failed to create event subscription thread\n", __FUNCTION__)); | |
| return; | |
| } | |
| pthread_detach(eventSubscriptionThreadId); | |
| CcspTraceInfo(("%s:%d, Subscribing to event %s synchronously\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)); | |
| } |
Reason for change:Setting the ifname in bcm if not set Test Procedure: verify the voice is initialized or not Risks: High Priority: P1
4a6b1e5 to
322656a
Compare
| */ | ||
| void startVoiceFeature(void) | ||
| { | ||
| static bool isVoiceFeatureStarted = false; |
| { | ||
| CcspTraceInfo(("%s: rbus_event_subscribe successful for event %s\n", __FUNCTION__, rbusEventSubscription.eventName)); | ||
| } | ||
| return NULL; |
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 13 out of 13 changed files in this pull request and generated 31 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| 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; | ||
| } |
There was a problem hiding this comment.
The dhcpClientEventsHandler processes all message types (DHCP_CLIENT_STARTED, STOPPED, LEASE_RENEW, LEASE_DEL, LEASE_UPDATE, CLIENT_FAILED) identically by calling processVoiceDhcpEvent. However, processVoiceDhcpEvent always adds IP routes and initializes voice support. For events like LEASE_DEL or CLIENT_STOPPED, this logic doesn't make sense - these should likely tear down routes and deinitialize, not set them up. Consider implementing different handling logic for different event types.
| 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; | ||
| } |
There was a problem hiding this comment.
Static global variable voiceInterface is accessed by callback functions without synchronization. If callbacks are invoked from different threads, this could lead to race conditions. Consider adding mutex protection or documenting thread-safety requirements.
| 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); |
There was a problem hiding this comment.
The strncpy calls do not guarantee null-termination if the source string equals or exceeds the destination buffer size. While the '-1' in the size parameter helps, the subsequent array access patterns could still be problematic. Use snprintf or ensure explicit null-termination after each strncpy call.
| return FALSE; | ||
| } | ||
|
|
||
| //Commend out the broadcom patch changes for now, will be requried to test basic voice functionality supported by broadcom. |
There was a problem hiding this comment.
Typo in comment: "Commend" should be "Comment".
| ); | ||
| int CosaDmlTR104DataSet(char* pString,int bootup); | ||
|
|
||
| //Commend out the broadcom patch changes for now, will be requried to test basic voice functionality supported by broadcom. |
There was a problem hiding this comment.
Typo in comment: "Commend" should be "Comment".
| //Commend out the broadcom patch changes for now, will be requried to test basic voice functionality supported by broadcom. | |
| //Comment out the broadcom patch changes for now, will be requried to test basic voice functionality supported by broadcom. |
| 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); |
There was a problem hiding this comment.
Using system() to execute shell commands creates a command injection vulnerability. The variables from pDhcpEvtData (including addresses and gateway from DHCP responses) are used directly in command strings. An attacker controlling DHCP responses could inject malicious commands. Use safer alternatives like direct netlink API calls or properly escape all inputs.
| if (cWanIfname[0] == '\0') | ||
| snprintf(cWanIfname, sizeof(cWanIfname), "erouter0"); |
There was a problem hiding this comment.
Missing error handling: If getWanIfaceName fails (returns ANSC_STATUS_FAILURE), cWanIfname will still contain an empty string and the fallback to "erouter0" will be used. However, there's no log message indicating this fallback is happening. Consider adding a trace message when the fallback occurs to aid debugging.
| if (cWanIfname[0] == '\0') | |
| snprintf(cWanIfname, sizeof(cWanIfname), "erouter0"); | |
| if (cWanIfname[0] == '\0') | |
| { | |
| CcspTraceInfo(("%s:%d, getWanIfaceName returned empty, falling back to default WAN interface 'erouter0'\n", __FUNCTION__, __LINE__)); | |
| snprintf(cWanIfname, sizeof(cWanIfname), "erouter0"); | |
| } |
| for (int iIndex = 0; iIndex < iByteArrayLen && (iIndex * 2 + 1) < iHexStringLen; iIndex++) | ||
| { |
There was a problem hiding this comment.
Missing bounds check: The integer arithmetic (iIndex * 2 + 1) at line 354 could potentially overflow if iByteArrayLen is very large. While unlikely in practice for DHCP options, consider adding a bounds check to prevent potential issues.
| for (int iIndex = 0; iIndex < iByteArrayLen && (iIndex * 2 + 1) < iHexStringLen; iIndex++) | |
| { | |
| int maxBytesFromHex = iHexStringLen / 2; | |
| if (maxBytesFromHex > iByteArrayLen) | |
| { | |
| maxBytesFromHex = iByteArrayLen; | |
| } | |
| for (int iIndex = 0; iIndex < maxBytesFromHex; iIndex++) | |
| { |
| 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)); | ||
| } | ||
| } |
There was a problem hiding this comment.
Race condition: The voiceDataProcessingMutex is used to protect processVoiceDhcpEvent, but startVoiceFeature() can be called multiple times concurrently (from different WAN state change events) without synchronization. This could lead to multiple concurrent attempts to create the MTA interface, subscribe to events, or start DHCP. Consider adding a guard to ensure startVoiceFeature() is idempotent or can only execute once.
| rbusHandle_t voiceRbusHandle = NULL; | ||
| extern ANSC_HANDLE bus_handle; | ||
| const char cSubsystem[ ]= "eRT."; | ||
| static char cBaseParam[32] = {0}; |
There was a problem hiding this comment.
Using a static global variable cBaseParam that's populated from PSM creates potential thread-safety issues. If multiple threads call functions that depend on cBaseParam before getIfaceIndexInfo completes, they could read uninitialized or partially initialized data. Consider adding mutex protection or documenting the initialization order requirements.
b3679f6 to
114219d
Compare
114219d to
28be417
Compare
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 13 out of 13 changed files in this pull request and generated 12 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| 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; | ||
| } | ||
| } |
There was a problem hiding this comment.
Missing input validation: The getWanIfaceName function should validate that sysevent_fd is actually greater than 0 before using it, not just check if it's non-negative. A file descriptor of 0 is stdin and would be an invalid sysevent file descriptor. The check should be sysevent_fd >= 0 or maintain consistency with the error check pattern elsewhere in the code that checks for sysevent_fd < 0.
| 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; | |
| } | |
| } | |
| /* Validate sysevent_fd before using it. A value <= 0 is invalid. */ | |
| if (sysevent_fd <= 0) | |
| { | |
| CcspTraceError(("%s: Invalid sysevent_fd=%d\n", __FUNCTION__, sysevent_fd)); | |
| return returnStatus; | |
| } | |
| 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; | |
| } |
| 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__)); |
There was a problem hiding this comment.
Missing pthread error check: The pthread_create call at line 1486 checks for error (returns non-zero), but doesn't return ANSC_STATUS_FAILURE or propagate the error properly - it only logs an error message and continues. This could lead to the system operating without critical voice functionality. Consider returning ANSC_STATUS_FAILURE on thread creation failure to properly signal the error.
| CcspTraceError(("%s: Failed to create Voice Mta sysevent thread\n", __FUNCTION__)); | |
| CcspTraceError(("%s: Failed to create Voice Mta sysevent thread\n", __FUNCTION__)); | |
| return ANSC_STATUS_FAILURE; |
| getWanIfaceName(cWanIfname, sizeof(cWanIfname)); | ||
| if (cWanIfname[0] == '\0') | ||
| snprintf(cWanIfname, sizeof(cWanIfname), "erouter0"); | ||
|
|
There was a problem hiding this comment.
Missing error handling: If getWanIfaceName fails to retrieve the WAN interface name (line 197), the function defaults to "erouter0" but never checks if cWanIfname is actually valid. This could lead to attempting to create a macvlan on a non-existent interface. Consider checking if the WAN interface exists before proceeding with macvlan creation.
| /* Verify that the WAN interface exists before attempting macvlan creation */ | |
| if (if_nametoindex(cWanIfname) == 0) | |
| { | |
| CcspTraceError(("%s: WAN interface %s does not exist, aborting macvlan creation\n", __FUNCTION__, cWanIfname)); | |
| return -1; | |
| } |
| { | ||
| if (0 == access("/tmp/dumpDHCPevent.txt", F_OK)) | ||
| { | ||
| FILE *fp = fopen("/tmp/dhcp_event_dump.txt", "a"); |
There was a problem hiding this comment.
Hardcoded file path: The debug file path "/tmp/dhcp_event_dump.txt" is hardcoded. This should be defined as a constant at the file level for better maintainability.
| if (ioctl(iSocketFd, SIOCGIFFLAGS, &ifr) < 0) | ||
| { | ||
| CcspTraceError(("%s: ioctl SIOCGIFFLAGS failed for interface %s\n", __FUNCTION__, pIfaceName)); | ||
| close(iSocketFd); | ||
| return false; | ||
| } |
There was a problem hiding this comment.
Resource leak: The socket file descriptor is not closed on all error paths. If strncpy at line 107 or ioctl at line 111 succeeds but line 118 check fails, the socket remains open. Consider adding close(iSocketFd) before the return false at line 115.
| 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); |
There was a problem hiding this comment.
Magic number: The routing table number 21 is hardcoded without explanation. This should be defined as a named constant (e.g., VOICE_ROUTING_TABLE) with a comment explaining why this specific table number is used for voice routing.
| */ | ||
| static void readMacAddress (char * pMacAddress) | ||
| { | ||
| FILE *pFILE = fopen("/tmp/factory_nvram.data", "r"); |
There was a problem hiding this comment.
Hardcoded credentials location: The file path "/tmp/factory_nvram.data" is hardcoded for reading sensitive MAC address information. This file path should be defined as a constant at the file level or passed as a configuration parameter to improve maintainability and testability.
| 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__)); | ||
| } |
There was a problem hiding this comment.
Race condition: The global variable voiceRbusHandle is accessed and modified without synchronization. Multiple threads could potentially call initRbusHandle() simultaneously, leading to a race condition at line 161-166. Consider using a mutex to protect the check-and-initialize pattern or use atomic operations.
| 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); |
There was a problem hiding this comment.
Race condition with global cBaseParam: The static variable cBaseParam is set by getIfaceIndexInfo() and read by multiple functions (enableDhcpv4ForMta, eventSubscriptionThread). If multiple threads call these functions simultaneously before getIfaceIndexInfo() completes, they could read an empty or partially initialized cBaseParam. Consider protecting access to cBaseParam with a mutex or ensuring initialization is complete before any threads are created.
| return FALSE; | ||
| } | ||
|
|
||
| //Commend out the broadcom patch changes for now, will be requried to test basic voice functionality supported by broadcom. |
There was a problem hiding this comment.
Duplicate spelling errors across multiple files: The same spelling errors "Commend" (should be "Comment") and "requried" (should be "required") appear in at least 5 different files. These should all be corrected for consistency and professionalism.
Reason for change:Addressing the code review comments Test Procedure: verify the voice is initialized or not Risks: High Priority: P1
28be417 to
97c65e6
Compare
config/CcspMtaAgent.xml
Outdated
| </parameters> | ||
| </object> | ||
|
|
||
| <?ifdef VOICE_MTA_SUPPORT?> |
There was a problem hiding this comment.
we don't need these TR181, comment out these dml's
Reason for change:Addressing the code review comments Test Procedure: verify the voice is initialized or not Risks: High Priority: P1
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 9 out of 10 changed files in this pull request and generated 17 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| return; | ||
| } | ||
| pthread_detach(dhcpEventThreadId); | ||
| usleep(10000); // Sleep for 10ms to allow thread to start |
There was a problem hiding this comment.
Questionable sleep in event handler: The usleep(10000) call after pthread_detach appears to be a workaround to allow the thread to start. This is a race condition mitigation that doesn't guarantee the thread will actually start. Since the thread is detached and allocated data is passed to it, this could still result in use-after-free if the event handler returns and is freed before the thread accesses pDhcpEvtData. Consider using proper synchronization or removing the sleep if it's not necessary.
| usleep(10000); // Sleep for 10ms to allow thread to start |
| char cParamName[64] = {0}; | ||
| char cPartnerId[64] = { 0 }; | ||
| syscfg_get(NULL, "PartnerID", cPartnerId, sizeof(cPartnerId)); | ||
| if ('\0' != cPartnerId[0] && strcmp(cPartnerId, "comcast") == 0) |
There was a problem hiding this comment.
Case-sensitive partner ID comparison: The partner ID comparison uses strcmp which is case-sensitive. If the PartnerID syscfg value is stored as "Comcast" or "COMCAST", the comparison will fail and DHCPv4 won't be configured. Consider using strcasecmp for case-insensitive comparison to be more robust.
| if ('\0' != cPartnerId[0] && strcmp(cPartnerId, "comcast") == 0) | |
| if ('\0' != cPartnerId[0] && strcasecmp(cPartnerId, "comcast") == 0) |
| static uint8_t cbSubsIfInfo(char *pIntfName, uint8_t enable) | ||
| { | ||
| if (enable) | ||
| { | ||
| /* Save voice interface selection */ | ||
| strncpy(voiceInterface, pIntfName, sizeof(voiceInterface)); | ||
| } | ||
|
|
||
| return 1; | ||
| } |
There was a problem hiding this comment.
Missing return value documentation: The function cbSubsIfInfo always returns 1 without documenting what the return value represents or whether returning 0 would indicate an error. The function documentation or comment should explain the meaning of the return value.
| char cParamName[64] = {0}; | ||
| char cPartnerId[64] = { 0 }; | ||
| syscfg_get(NULL, "PartnerID", cPartnerId, sizeof(cPartnerId)); | ||
| if ('\0' != cPartnerId[0] && strcmp(cPartnerId, "comcast") == 0) |
There was a problem hiding this comment.
Redundant check: The condition '\0' != cPartnerId[0] is redundant because if strlen(cPartnerId) was 0 (checked by the following strcmp), cPartnerId[0] would already be '\0'. Consider simplifying to just: if (strcmp(cPartnerId, "comcast") == 0)
| if ('\0' != cPartnerId[0] && strcmp(cPartnerId, "comcast") == 0) | |
| if (strcmp(cPartnerId, "comcast") == 0) |
| if (pFirewallRule->enable) | ||
| { | ||
| snprintf(command, sizeof(command), "-A INPUT -p %s -i %s --dport %u -j ACCEPT", | ||
| protocol, pFirewallRule->ifName, pFirewallRule->destinationPort); |
There was a problem hiding this comment.
Missing bounds check on port number: The destinationPort field is used directly in the firewall command without validation. If it's 0 or exceeds 65535, it could create an invalid firewall rule. Consider validating: if (pFirewallRule->destinationPort == 0 || pFirewallRule->destinationPort > 65535) return error.
| } | ||
| addIpRouteDetails(pDhcpEvtData); | ||
| initializeVoiceSupport(pDhcpEvtData); | ||
|
|
There was a problem hiding this comment.
Trailing whitespace: There is trailing whitespace at the end of this line which should be removed to maintain clean code formatting.
| } | ||
| #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); |
There was a problem hiding this comment.
Missing error handling: When pthread_create fails, the function only logs an error but continues execution and returns ANSC_STATUS_SUCCESS. This is inconsistent with the sysevent_open error handling above which returns ANSC_STATUS_FAILURE. Consider returning ANSC_STATUS_FAILURE when thread creation fails to properly indicate initialization failure.
| } | |
| #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); | |
| return ANSC_STATUS_FAILURE; | |
| } | |
| #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__)); | |
| if (0 != pthread_create(&MtaInit, NULL, &Mta_Sysevent_thread_Dhcp_Option, (ANSC_HANDLE) hThisObject)) | |
| { | |
| CcspTraceError(("%s: Failed to create MTA sysevent thread for DHCP option 122/2171\n", __FUNCTION__)); | |
| return ANSC_STATUS_FAILURE; | |
| } | |
| #else | |
| CcspTraceInfo(("%s %d Starting sysevent thread for WAN state \n", __FUNCTION__, __LINE__)); | |
| if (0 != pthread_create(&MtaInit, NULL, &Mta_Sysevent_thread, (ANSC_HANDLE) hThisObject)) | |
| { | |
| CcspTraceError(("%s: Failed to create MTA sysevent thread for WAN state\n", __FUNCTION__)); | |
| return ANSC_STATUS_FAILURE; | |
| } |
| * @param[in] pIfaceName Name of the MTA network interface for which | ||
| * DHCPv4 should be enabled. | ||
| */ | ||
| void enableDhcpv4ForMta(const char * pIfaceName) |
There was a problem hiding this comment.
Inconsistent function naming convention: The function name uses camelCase (enableDhcpv4ForMta) which is inconsistent with other function names in the file that use camelCase but with different capitalization for abbreviations. Looking at the codebase, HAL-related functions use mixed conventions, but for consistency within this module, consider using enableDhcpV4ForMta (capital V4) to match the naming of other functions like subscribeDhcpClientEvents.
| } | ||
|
|
||
| CcspTraceInfo(("%s:%d, DHCP Client Events Handler Thread started\n", __FUNCTION__, __LINE__)); | ||
| DhcpEventData_t *pDhcpEvtData = (DhcpEventData_t *)pArg; |
There was a problem hiding this comment.
Potential null pointer dereference: The function casts pArg to DhcpEventData_t * without checking if it's NULL until after the cast. While there is a NULL check immediately after, the parameter documentation and early NULL check are good practices. The current implementation is safe but could be clearer by checking before the cast.
| DhcpEventData_t *pDhcpEvtData = (DhcpEventData_t *)pArg; | |
| DhcpEventData_t *pDhcpEvtData = (DhcpEventData_t *)pArg; | |
| if (NULL == pDhcpEvtData) | |
| { | |
| CcspTraceError(("%s: NULL DHCP event data after cast\n", __FUNCTION__)); | |
| return NULL; | |
| } |
| if (enable) | ||
| { | ||
| /* Save voice interface selection */ | ||
| strncpy(voiceInterface, pIntfName, sizeof(voiceInterface)); |
There was a problem hiding this comment.
Buffer overflow potential: The voiceInterface buffer is 32 bytes, and strncpy copies from pIntfName without checking its length first. If pIntfName is NULL or points to an invalid address, this will crash. Although strncpy limits the copy, the size check should happen before the copy. Also, the strncpy call doesn't ensure null-termination. Consider: if (pIntfName && strlen(pIntfName) < sizeof(voiceInterface)) { strncpy(...); voiceInterface[sizeof(voiceInterface)-1] = '\0'; }
Reason for change:Creating the virtual interface and initializing the dhcp
Test Procedure: verify the interface creation and IP assignment.
Risks: High
Priority: P1