/** * @brief computes dependencies and triggers port learning tree updates * * @param triggerPorts port map consisting in the ports which triggered the update * * This function browses through all the ports and determines how to waterfall the update * event from the trigger ports to all other ports depending on them. * * Once the list of ports to be updated is determined this function * calls @ref ixEthDBCreateTrees. * * @internal */ IX_ETH_DB_PUBLIC void ixEthDBUpdatePortLearningTrees(IxEthDBPortMap triggerPorts) { IxEthDBPortMap updatePorts; UINT32 portIndex; ixEthDBUpdateLock(); SET_EMPTY_DEPENDENCY_MAP(updatePorts); for (portIndex = 0 ; portIndex < IX_ETH_DB_NUMBER_OF_PORTS ; portIndex++) { PortInfo *port = &ixEthDBPortInfo[portIndex]; BOOL mapsCollide; MAPS_COLLIDE(mapsCollide, triggerPorts, port->dependencyPortMap); if (mapsCollide /* do triggers influence this port? */ && !IS_PORT_INCLUDED(portIndex, updatePorts) /* and it's not already in the update list */ && port->updateMethod.updateEnabled) /* and we're allowed to update it */ { IX_ETH_DB_UPDATE_TRACE("DB: (Update) Adding port %d to update set\n", portIndex); JOIN_PORT_TO_MAP(updatePorts, portIndex); } else { IX_ETH_DB_UPDATE_TRACE("DB: (Update) Didn't add port %d to update set, reasons follow:\n", portIndex); if (!mapsCollide) { IX_ETH_DB_UPDATE_TRACE("\tMaps don't collide on port %d\n", portIndex); } if (IS_PORT_INCLUDED(portIndex, updatePorts)) { IX_ETH_DB_UPDATE_TRACE("\tPort %d is already in the update set\n", portIndex); } if (!port->updateMethod.updateEnabled) { IX_ETH_DB_UPDATE_TRACE("\tPort %d doesn't have updateEnabled set\n", portIndex); } } } IX_ETH_DB_UPDATE_TRACE("DB: (Update) Updating port set\n"); ixEthDBCreateTrees(updatePorts); ixEthDBUpdateUnlock(); }
IX_ETH_DB_PUBLIC IxEthDBStatus ixEthDBDatabaseClear(IxEthDBPortId portID, IxEthDBRecordType recordType) { IxEthDBPortMap triggerPorts; HashIterator iterator; if (portID >= IX_ETH_DB_NUMBER_OF_PORTS && portID != IX_ETH_DB_ALL_PORTS) { return IX_ETH_DB_INVALID_PORT; } /* check if the user passes some extra bits */ if ((recordType | IX_ETH_DB_ALL_RECORD_TYPES) != IX_ETH_DB_ALL_RECORD_TYPES) { return IX_ETH_DB_INVALID_ARG; } SET_EMPTY_DEPENDENCY_MAP(triggerPorts); /* browse database and age entries */ BUSY_RETRY(ixEthDBInitHashIterator(&dbHashtable, &iterator)); while (IS_ITERATOR_VALID(&iterator)) { MacDescriptor *descriptor = (MacDescriptor *) iterator.node->data; if (((descriptor->portID == portID) || (portID == IX_ETH_DB_ALL_PORTS)) && ((descriptor->type & recordType) != 0)) { /* add to trigger if automatic updates are required */ if (ixEthDBPortUpdateRequired[descriptor->type]) { /* add port to the set of update trigger ports */ JOIN_PORT_TO_MAP(triggerPorts, descriptor->portID); } /* delete entry */ BUSY_RETRY(ixEthDBRemoveEntryAtHashIterator(&dbHashtable, &iterator)); } else { /* move to the next record */ BUSY_RETRY(ixEthDBIncrementHashIterator(&dbHashtable, &iterator)); } } /* update ports which lost records */ ixEthDBUpdatePortLearningTrees(triggerPorts); return IX_ETH_DB_SUCCESS; }
/** * @brief remove a record from the Ethernet database * * @param templateRecord template record used to determine * what record is to be removed * @param updateTrigger port map containing the update triggers * resulting from this update operation * * This function will examine the template record it receives * and attempts to delete a record of the same type and containing * the same keys as the template record. If deletion is successful * and the record type is registered for automatic port updates the * port will also be set in the updateTrigger port map, so that the * client can perform an update of the port. * * @retval IX_ETH_DB_SUCCESS removal was successful * @retval IX_ETH_DB_NO_SUCH_ADDR the record with the given MAC address was not found * @retval IX_ETH_DB_BUSY database busy, cannot remove due to locking * * @internal */ IX_ETH_DB_PUBLIC IxEthDBStatus ixEthDBRemove(MacDescriptor *templateRecord, IxEthDBPortMap updateTrigger) { IxEthDBStatus result; PortInfo *portInfo; TEST_FIXTURE_INCREMENT_DB_CORE_ACCESS_COUNTER; BUSY_RETRY_WITH_RESULT(ixEthDBRemoveHashEntry(&dbHashtable, ixEthDBKeyType[templateRecord->type], templateRecord), result); if (result != IX_ETH_DB_SUCCESS) { return IX_ETH_DB_NO_SUCH_ADDR; /* not found */ } portInfo = &ixEthDBPortInfo[templateRecord->portID]; if (templateRecord->type == IX_ETH_DB_WIFI_RECORD) { /* decrement the wifi records counter when entry is deleted from the database for the port */ portInfo->wifiRecordsCount = portInfo->wifiRecordsCount - 1; } if (templateRecord->type == IX_ETH_DB_FIREWALL_RECORD || templateRecord->type == IX_ETH_DB_MASKED_FIREWALL_RECORD) { /* decrement the firewall records counter when entry is deleted from the database for the port */ portInfo->fwRecordsCount = portInfo->fwRecordsCount - 1; } /* trigger add/remove update if required by type */ if (updateTrigger != NULL &&ixEthDBPortUpdateRequired[templateRecord->type]) { /* add new port to update list */ JOIN_PORT_TO_MAP(updateTrigger, templateRecord->portID); } return IX_ETH_DB_SUCCESS; }
/** * @brief remove a record from the Ethernet database * * @param templateRecord template record used to determine * what record is to be removed * @param updateTrigger port map containing the update triggers * resulting from this update operation * * This function will examine the template record it receives * and attempts to delete a record of the same type and containing * the same keys as the template record. If deletion is successful * and the record type is registered for automatic port updates the * port will also be set in the updateTrigger port map, so that the * client can perform an update of the port. * * @retval IX_ETH_DB_SUCCESS removal was successful * @retval IX_ETH_DB_NO_SUCH_ADDR the record with the given MAC address was not found * @retval IX_ETH_DB_BUSY database busy, cannot remove due to locking * * @internal */ IX_ETH_DB_PUBLIC IxEthDBStatus ixEthDBRemove(MacDescriptor *templateRecord, IxEthDBPortMap updateTrigger) { IxEthDBStatus result; TEST_FIXTURE_INCREMENT_DB_CORE_ACCESS_COUNTER; BUSY_RETRY_WITH_RESULT(ixEthDBRemoveHashEntry(&dbHashtable, ixEthDBKeyType[templateRecord->type], templateRecord), result); if (result != IX_ETH_DB_SUCCESS) { return IX_ETH_DB_NO_SUCH_ADDR; /* not found */ } /* trigger add/remove update if required by type */ if (updateTrigger != NULL &&ixEthDBPortUpdateRequired[templateRecord->type]) { /* add new port to update list */ JOIN_PORT_TO_MAP(updateTrigger, templateRecord->portID); } return IX_ETH_DB_SUCCESS; }
/** * @brief adds a new entry to the Ethernet database * * @param newRecordTemplate address of the record template to use * @param updateTrigger port map containing the update triggers * resulting from this update operation * * Creates a new database entry, populates it with the data * copied from the given template and adds the record to the * database hash table. * It also checks whether the new record type is registered to trigger * automatic updates; if it is, the update trigger will contain the * port on which the record insertion was performed, as well as the * old port in case the addition was a record migration (from one port * to the other). The caller can use the updateTrigger to trigger * automatic updates on the ports changed as a result of this addition. * * @retval IX_ETH_DB_SUCCESS addition successful * @retval IX_ETH_DB_NOMEM insertion failed, no memory left in the mac descriptor memory pool * @retval IX_ETH_DB_BUSY database busy, cannot insert due to locking * * @internal */ IX_ETH_DB_PUBLIC IxEthDBStatus ixEthDBAdd(MacDescriptor *newRecordTemplate, IxEthDBPortMap updateTrigger) { IxEthDBStatus result; MacDescriptor *newDescriptor; IxEthDBPortId originalPortID; HashNode *node = NULL; BUSY_RETRY(ixEthDBSearchHashEntry(&dbHashtable, ixEthDBKeyType[newRecordTemplate->type], newRecordTemplate, &node)); TEST_FIXTURE_INCREMENT_DB_CORE_ACCESS_COUNTER; if (node == NULL) { /* not found, create a new one */ newDescriptor = ixEthDBAllocMacDescriptor(); if (newDescriptor == NULL) { return IX_ETH_DB_NOMEM; /* no memory */ } /* old port does not exist, avoid unnecessary updates */ originalPortID = newRecordTemplate->portID; } else { /* a node with the same key exists, will update node */ newDescriptor = (MacDescriptor *) node->data; /* save original port id */ originalPortID = newDescriptor->portID; } /* copy/update fields into new record */ memcpy(newDescriptor->macAddress, newRecordTemplate->macAddress, sizeof (IxEthDBMacAddr)); memcpy(&newDescriptor->recordData, &newRecordTemplate->recordData, sizeof (IxEthDBRecordData)); newDescriptor->type = newRecordTemplate->type; newDescriptor->portID = newRecordTemplate->portID; newDescriptor->user = newRecordTemplate->user; if (node == NULL) { /* new record, insert into hashtable */ BUSY_RETRY_WITH_RESULT(ixEthDBAddHashEntry(&dbHashtable, newDescriptor), result); if (result != IX_ETH_DB_SUCCESS) { ixEthDBFreeMacDescriptor(newDescriptor); return result; /* insertion failed */ } } if (node != NULL) { /* release access */ ixEthDBReleaseHashNode(node); } /* trigger add/remove update if required by type */ if (updateTrigger != NULL && ixEthDBPortUpdateRequired[newRecordTemplate->type]) { /* add new port to update list */ JOIN_PORT_TO_MAP(updateTrigger, newRecordTemplate->portID); /* check if record has moved, we'll need to update the old port as well */ if (originalPortID != newDescriptor->portID) { JOIN_PORT_TO_MAP(updateTrigger, originalPortID); } } return IX_ETH_DB_SUCCESS; }
/** * @brief disables a port * * @param portID ID of the port to disable * * This function is fully documented in the * main header file, IxEthDB.h * * @return IX_ETH_DB_SUCCESS if disabling was * successful or an appropriate error message * otherwise */ IX_ETH_DB_PUBLIC IxEthDBStatus ixEthDBPortDisable(IxEthDBPortId portID) { HashIterator iterator; IxEthDBPortMap triggerPorts; /* ports who will have deleted records and therefore will need updating */ BOOL result; PortInfo *portInfo; IxEthDBFeature learningEnabled; IxEthDBPriorityTable classZeroTable; IX_ETH_DB_CHECK_PORT_EXISTS(portID); IX_ETH_DB_CHECK_SINGLE_NPE(portID); portInfo = &ixEthDBPortInfo[portID]; if (!portInfo->enabled) { /* redundant */ return IX_ETH_DB_SUCCESS; } if (ixEthDBPortDefinitions[portID].type == IX_ETH_NPE) { /* save filtering state */ ixEthDBPortState[portID].firewallMode = portInfo->firewallMode; ixEthDBPortState[portID].frameFilter = portInfo->frameFilter; ixEthDBPortState[portID].taggingAction = portInfo->taggingAction; ixEthDBPortState[portID].stpBlocked = portInfo->stpBlocked; ixEthDBPortState[portID].srcAddressFilterEnabled = portInfo->srcAddressFilterEnabled; ixEthDBPortState[portID].maxRxFrameSize = portInfo->maxRxFrameSize; ixEthDBPortState[portID].maxTxFrameSize = portInfo->maxTxFrameSize; memcpy(ixEthDBPortState[portID].vlanMembership, portInfo->vlanMembership, sizeof (IxEthDBVlanSet)); memcpy(ixEthDBPortState[portID].transmitTaggingInfo, portInfo->transmitTaggingInfo, sizeof (IxEthDBVlanSet)); memcpy(ixEthDBPortState[portID].priorityTable, portInfo->priorityTable, sizeof (IxEthDBPriorityTable)); ixEthDBPortState[portID].saved = TRUE; /* now turn off all EthDB filtering features on the port */ /* VLAN & QoS */ if ((portInfo->featureCapability & IX_ETH_DB_VLAN_QOS) != 0) { ixEthDBPortVlanMembershipRangeAdd((IxEthDBPortId) portID, 0, IX_ETH_DB_802_1Q_MAX_VLAN_ID); ixEthDBEgressVlanRangeTaggingEnabledSet((IxEthDBPortId) portID, 0, IX_ETH_DB_802_1Q_MAX_VLAN_ID, FALSE); ixEthDBAcceptableFrameTypeSet((IxEthDBPortId) portID, IX_ETH_DB_ACCEPT_ALL_FRAMES); ixEthDBIngressVlanTaggingEnabledSet((IxEthDBPortId) portID, IX_ETH_DB_PASS_THROUGH); memset(classZeroTable, 0, sizeof (classZeroTable)); ixEthDBPriorityMappingTableSet((IxEthDBPortId) portID, classZeroTable); } /* STP */ if ((portInfo->featureCapability & IX_ETH_DB_SPANNING_TREE_PROTOCOL) != 0) { ixEthDBSpanningTreeBlockingStateSet((IxEthDBPortId) portID, FALSE); } /* Firewall */ if ((portInfo->featureCapability & IX_ETH_DB_FIREWALL) != 0) { ixEthDBFirewallModeSet((IxEthDBPortId) portID, IX_ETH_DB_FIREWALL_BLACK_LIST); ixEthDBFirewallTableDownload((IxEthDBPortId) portID); ixEthDBFirewallInvalidAddressFilterEnable((IxEthDBPortId) portID, FALSE); } /* Frame size filter */ ixEthDBFilteringPortMaximumFrameSizeSet((IxEthDBPortId) portID, IX_ETH_DB_DEFAULT_FRAME_SIZE); /* WiFi */ if ((portInfo->featureCapability & IX_ETH_DB_WIFI_HEADER_CONVERSION) != 0) { ixEthDBWiFiConversionTableDownload((IxEthDBPortId) portID); } /* save and disable the learning feature bit */ learningEnabled = portInfo->featureStatus & IX_ETH_DB_LEARNING; portInfo->featureStatus &= ~IX_ETH_DB_LEARNING; } else { /* save the learning feature bit */ learningEnabled = portInfo->featureStatus & IX_ETH_DB_LEARNING; } SET_EMPTY_DEPENDENCY_MAP(triggerPorts); ixEthDBUpdateLock(); /* wipe out current entries for this port */ BUSY_RETRY(ixEthDBInitHashIterator(&dbHashtable, &iterator)); while (IS_ITERATOR_VALID(&iterator)) { MacDescriptor *descriptor = (MacDescriptor *) iterator.node->data; /* check if the port match. If so, remove the entry */ if (descriptor->portID == portID && (descriptor->type == IX_ETH_DB_FILTERING_RECORD || descriptor->type == IX_ETH_DB_FILTERING_VLAN_RECORD) && !descriptor->recordData.filteringData.staticEntry) { /* delete entry */ BUSY_RETRY(ixEthDBRemoveEntryAtHashIterator(&dbHashtable, &iterator)); /* add port to the set of update trigger ports */ JOIN_PORT_TO_MAP(triggerPorts, portID); } else { /* move to the next record */ BUSY_RETRY(ixEthDBIncrementHashIterator(&dbHashtable, &iterator)); } } if (ixEthDBPortDefinitions[portID].type == IX_ETH_NPE) { if (portInfo->updateMethod.searchTree != NULL) { ixEthDBFreeMacTreeNode(portInfo->updateMethod.searchTree); portInfo->updateMethod.searchTree = NULL; } ixEthDBNPEUpdateHandler(portID, IX_ETH_DB_FILTERING_RECORD); } /* mark as disabled */ portInfo->enabled = FALSE; /* disable updates unless the user has specifically altered the default behavior */ if (ixEthDBPortDefinitions[portID].type == IX_ETH_NPE) { if (!portInfo->updateMethod.userControlled) { portInfo->updateMethod.updateEnabled = FALSE; } /* make sure we re-initialize the NPE learning tree when the port is re-enabled */ portInfo->updateMethod.treeInitialized = FALSE; } ixEthDBUpdateUnlock(); /* restore learning feature bit */ portInfo->featureStatus |= learningEnabled; /* if we've removed any records or lost any events make sure to force an update */ IS_EMPTY_DEPENDENCY_MAP(result, triggerPorts); if (!result) { ixEthDBUpdatePortLearningTrees(triggerPorts); } return IX_ETH_DB_SUCCESS; }
IX_ETH_DB_PUBLIC void ixEthDBDatabaseMaintenance() { HashIterator iterator; UINT32 portIndex; BOOL agingRequired = FALSE; /* ports who will have deleted records and therefore will need updating */ IxEthDBPortMap triggerPorts; if (IX_FEATURE_CTRL_SWCONFIG_ENABLED != ixFeatureCtrlSwConfigurationCheck (IX_FEATURECTRL_ETH_LEARNING)) { return; } SET_EMPTY_DEPENDENCY_MAP(triggerPorts); /* check if there's at least a port that needs aging */ for (portIndex = 0 ; portIndex < IX_ETH_DB_NUMBER_OF_PORTS ; portIndex++) { if (ixEthDBPortInfo[portIndex].agingEnabled && ixEthDBPortInfo[portIndex].enabled) { agingRequired = TRUE; } } if (agingRequired) { /* ask each NPE port to write back the database for aging inspection */ for (portIndex = 0 ; portIndex < IX_ETH_DB_NUMBER_OF_PORTS ; portIndex++) { if (ixEthDBPortDefinitions[portIndex].type == IX_ETH_NPE && ixEthDBPortInfo[portIndex].agingEnabled && ixEthDBPortInfo[portIndex].enabled) { IxNpeMhMessage message; IX_STATUS result; /* send EDB_GetMACAddressDatabase message */ FILL_GETMACADDRESSDATABASE(message, 0 /* unused */, IX_OSAL_MMU_VIRT_TO_PHYS(ixEthDBPortInfo[portIndex].updateMethod.npeUpdateZone)); IX_ETHDB_SEND_NPE_MSG(IX_ETHNPE_PHYSICAL_ID_TO_NODE(portIndex), message, result); if (result == IX_SUCCESS) { /* analyze NPE copy */ ixEthDBNPESyncScan(portIndex, ixEthDBPortInfo[portIndex].updateMethod.npeUpdateZone, FULL_ELT_BYTE_SIZE); IX_ETH_DB_SUPPORT_TRACE("DB: (API) Finished scanning NPE tree on port %d\n", portIndex); } else { ixOsalLog(IX_OSAL_LOG_LVL_WARNING, IX_OSAL_LOG_DEV_STDOUT, "EthDB: (Maintenance) warning, Clearing Database records for all types for port %d\n", portIndex, 0, 0, 0, 0, 0); ixEthDBDatabaseClear(portIndex, IX_ETH_DB_ALL_RECORD_TYPES); } } } /* browse database and age entries */ BUSY_RETRY(ixEthDBInitHashIterator(&dbHashtable, &iterator)); while (IS_ITERATOR_VALID(&iterator)) { MacDescriptor *descriptor = (MacDescriptor *) iterator.node->data; UINT32 *age = NULL; BOOL staticEntry = TRUE; if (descriptor->type == IX_ETH_DB_FILTERING_RECORD) { age = &descriptor->recordData.filteringData.age; staticEntry = descriptor->recordData.filteringData.staticEntry; } else if (descriptor->type == IX_ETH_DB_FILTERING_VLAN_RECORD) { age = &descriptor->recordData.filteringVlanData.age; staticEntry = descriptor->recordData.filteringVlanData.staticEntry; } else { staticEntry = TRUE; } if (ixEthDBPortInfo[descriptor->portID].agingEnabled && (staticEntry == FALSE)) { /* manually increment the age if the port has no such capability */ if ((ixEthDBPortDefinitions[descriptor->portID].capabilities & IX_ETH_ENTRY_AGING) == 0) { *age += (IX_ETH_DB_MAINTENANCE_TIME / 60); } /* age entry if it exceeded the maximum time to live */ if (*age >= (IX_ETH_DB_LEARNING_ENTRY_AGE_TIME / 60)) { /* add port to the set of update trigger ports */ JOIN_PORT_TO_MAP(triggerPorts, descriptor->portID); /* delete entry */ BUSY_RETRY(ixEthDBRemoveEntryAtHashIterator(&dbHashtable, &iterator)); } else { /* move to the next record */ BUSY_RETRY(ixEthDBIncrementHashIterator(&dbHashtable, &iterator)); } } else { /* move to the next record */ BUSY_RETRY(ixEthDBIncrementHashIterator(&dbHashtable, &iterator)); } } /* update ports which lost records */ ixEthDBUpdatePortLearningTrees(triggerPorts); } }