diff --git a/factory/config.go b/factory/config.go index 5efa413..6971ab3 100644 --- a/factory/config.go +++ b/factory/config.go @@ -120,81 +120,196 @@ func init() { func (c *Config) UpdateConfig(commChannel chan *protos.NetworkSliceResponse) bool { var minConfig bool + for rsp := range commChannel { logger.GrpcLog.Infoln("Received updateConfig in the nssf app : ", rsp) + + // for thread safety + ConfigLock.Lock() + for _, ns := range rsp.NetworkSlice { logger.GrpcLog.Infoln("Network Slice Name ", ns.Name) - if ns.Site != nil { - logger.GrpcLog.Infoln("Network Slice has site name present ") - site := ns.Site - logger.GrpcLog.Infoln("Site name ", site.SiteName) - if site.Plmn != nil { - logger.GrpcLog.Infoln("Plmn mcc ", site.Plmn.Mcc) - logger.GrpcLog.Infoln("Plmn mnc ", site.Plmn.Mnc) - plmn := new(models.PlmnId) - plmn.Mnc = site.Plmn.Mnc - plmn.Mcc = site.Plmn.Mcc - sNssaiInPlmns := SupportedNssaiInPlmn{} - sNssaiInPlmns.PlmnId = plmn - nssai := new(models.Snssai) - val, err := strconv.ParseInt(ns.Nssai.Sst, 10, 64) - if err != nil { - logger.GrpcLog.Infoln("Error in parsing sst ", err) - } - nssai.Sst = int32(val) - nssai.Sd = ns.Nssai.Sd - logger.GrpcLog.Infoln("Slice Sst ", ns.Nssai.Sst) - logger.GrpcLog.Infoln("Slice Sd ", ns.Nssai.Sd) - sNssaiInPlmns.SupportedSnssaiList = append(sNssaiInPlmns.SupportedSnssaiList, *nssai) - found := false - for i, cplmn := range NssfConfig.Configuration.SupportedPlmnList { - if cplmn.Mnc == plmn.Mnc && cplmn.Mcc == plmn.Mcc { - // Check if the S-NSSAI exists already - exists := false - for _, existingSnssai := range NssfConfig.Configuration.SupportedNssaiInPlmnList[i].SupportedSnssaiList { - if existingSnssai.Sst == nssai.Sst && existingSnssai.Sd == nssai.Sd { - exists = true - break - } - } - // Only append if a new slice - if !exists { - NssfConfig.Configuration.SupportedNssaiInPlmnList[i].SupportedSnssaiList = append(NssfConfig.Configuration.SupportedNssaiInPlmnList[i].SupportedSnssaiList, *nssai) - } - found = true - break - } - } - if !found { - NssfConfig.Configuration.SupportedPlmnList = append(NssfConfig.Configuration.SupportedPlmnList, *plmn) - NssfConfig.Configuration.SupportedNssaiInPlmnList = append(NssfConfig.Configuration.SupportedNssaiInPlmnList, sNssaiInPlmns) + + if ns.Site == nil { + continue + } + + site := ns.Site + logger.GrpcLog.Infoln("Site name ", site.SiteName) + + if site.Plmn == nil { + logger.GrpcLog.Infoln("Plmn not present in the message ") + continue + } + + plmn := models.PlmnId{ + Mcc: site.Plmn.Mcc, + Mnc: site.Plmn.Mnc, + } + + logger.GrpcLog.Infof("PLMN: MCC=%s MNC=%s", plmn.Mcc, plmn.Mnc) + + // Parse NSSAI + val, err := strconv.ParseInt(ns.Nssai.Sst, 10, 64) + if err != nil { + logger.GrpcLog.Errorf("Error parsing SST: %v", err) + continue + } + + nssai := models.Snssai{ + Sst: int32(val), + Sd: ns.Nssai.Sd, + } + + logger.GrpcLog.Infof("Slice Sst=%d Sd=%s", nssai.Sst, nssai.Sd) + + // ========================= + // STEP 1: Update SupportedPlmn + NSSAI + // ========================= + plmnIndex := -1 + + for i, existingPlmn := range NssfConfig.Configuration.SupportedPlmnList { + if existingPlmn.Mcc == plmn.Mcc && existingPlmn.Mnc == plmn.Mnc { + plmnIndex = i + break + } + } + + if plmnIndex >= 0 { + // PLMN exists → update NSSAI list + exists := false + + for _, existingSnssai := range NssfConfig.Configuration.SupportedNssaiInPlmnList[plmnIndex].SupportedSnssaiList { + if existingSnssai.Sst == nssai.Sst && existingSnssai.Sd == nssai.Sd { + exists = true + break } - } else { - logger.GrpcLog.Infoln("Plmn not present in the message ") } + + if !exists { + NssfConfig.Configuration.SupportedNssaiInPlmnList[plmnIndex].SupportedSnssaiList = append(NssfConfig.Configuration.SupportedNssaiInPlmnList[plmnIndex].SupportedSnssaiList, nssai) + } + } else { + // New PLMN + NssfConfig.Configuration.SupportedPlmnList = append(NssfConfig.Configuration.SupportedPlmnList, plmn) + newEntry := SupportedNssaiInPlmn{ + PlmnId: &plmn, + SupportedSnssaiList: []models.Snssai{nssai}, + } + + NssfConfig.Configuration.SupportedNssaiInPlmnList = append(NssfConfig.Configuration.SupportedNssaiInPlmnList, newEntry) } - } - if !minConfig { - // first slice Created - if (len(NssfConfig.Configuration.SupportedPlmnList) > 0) && - (len(NssfConfig.Configuration.SupportedNssaiInPlmnList) > 0) { - minConfig = true - ConfigPodTrigger <- true - logger.GrpcLog.Infoln("Send config trigger to main routine") + + // ========================= + // STEP 2: Update MappingListFromPlmn + // ========================= + mappingIndex := -1 + + for i, mapping := range NssfConfig.Configuration.MappingListFromPlmn { + if mapping.HomePlmnId != nil && + mapping.HomePlmnId.Mcc == plmn.Mcc && + mapping.HomePlmnId.Mnc == plmn.Mnc { + mappingIndex = i + break + } + } + + newMapping := models.MappingOfSnssai{ + HomeSnssai: &nssai, + ServingSnssai: &nssai, // 1:1 mapping (can customize later) } - } else { - // all slices deleted - if (len(NssfConfig.Configuration.SupportedPlmnList) > 0) && - (len(NssfConfig.Configuration.SupportedNssaiInPlmnList) > 0) { - minConfig = false - ConfigPodTrigger <- false - logger.GrpcLog.Infoln("Send config trigger to main routine") + + if mappingIndex >= 0 { + // Update existing mapping + exists := false + + for _, m := range NssfConfig.Configuration.MappingListFromPlmn[mappingIndex].MappingOfSnssai { + if m.HomeSnssai.Sst == nssai.Sst && m.HomeSnssai.Sd == nssai.Sd { + exists = true + break + } + } + + if !exists { + NssfConfig.Configuration.MappingListFromPlmn[mappingIndex].MappingOfSnssai = append(NssfConfig.Configuration.MappingListFromPlmn[mappingIndex].MappingOfSnssai, newMapping) + } } else { - ConfigPodTrigger <- true - logger.GrpcLog.Infoln("Send config trigger to main routine") + // Create new mapping entry + newEntry := MappingFromPlmnConfig{ + HomePlmnId: &plmn, + MappingOfSnssai: []models.MappingOfSnssai{newMapping}, + } + + NssfConfig.Configuration.MappingListFromPlmn = append(NssfConfig.Configuration.MappingListFromPlmn, newEntry) + } + // ========================= + // STEP 3: Update TA List + // ========================= + for _, gnb := range site.Gnb { + if gnb == nil { + continue + } + if gnb.Tac == 0 { + logger.GrpcLog.Warnln("TAC is 0 or not set in GNB") + continue + } + + // Convert TAC int32 → string + tacStr := strconv.Itoa(int(gnb.Tac)) + + tai := &models.Tai{ + PlmnId: &models.PlmnId{ + Mcc: site.Plmn.Mcc, + Mnc: site.Plmn.Mnc, + }, + Tac: tacStr, + } + + // Set Access Type (most cases 3GPP) + accessType := models.AccessType__3_GPP_ACCESS + + taConfig := TaConfig{ + Tai: tai, + AccessType: &accessType, + SupportedSnssaiList: []models.Snssai{nssai}, + // Optional: keep empty unless needed + RestrictedSnssaiList: nil, + } + + exists := false + + for _, existingTai := range NssfConfig.Configuration.TaList { + if existingTai.Tai.PlmnId.Mcc == tai.PlmnId.Mcc && + existingTai.Tai.PlmnId.Mnc == tai.PlmnId.Mnc && + existingTai.Tai.Tac == tai.Tac { + exists = true + break + } + } + + if !exists { + NssfConfig.Configuration.TaList = append(NssfConfig.Configuration.TaList, taConfig) + + logger.GrpcLog.Infof("Added TA from GNB: MCC=%s MNC=%s TAC=%s", + tai.PlmnId.Mcc, tai.PlmnId.Mnc, tai.Tac) + } } } + + // ========================= + // Unlock after update + // ========================= + ConfigLock.Unlock() + + hasConfig := len(NssfConfig.Configuration.SupportedPlmnList) > 0 && len(NssfConfig.Configuration.SupportedNssaiInPlmnList) > 0 + + if hasConfig != minConfig { + minConfig = hasConfig + ConfigPodTrigger <- hasConfig + logger.GrpcLog.Infof("Config trigger sent: %v", hasConfig) + } } + return true } diff --git a/producer/nsselection_for_registration.go b/producer/nsselection_for_registration.go index ecfabf5..b27ec22 100644 --- a/producer/nsselection_for_registration.go +++ b/producer/nsselection_for_registration.go @@ -249,6 +249,18 @@ func nsselectionForRegistration(param plugin.NsselectionQueryParameter, // Find mappings for S-NSSAIs in `subscribedSnssai` for _, subscribedSnssai := range param.SliceInfoRequestForRegistration.SubscribedNssai { if util.CheckStandardSnssai(*subscribedSnssai.SubscribedSnssai) { + logger.Nsselection.Debugf("Standard NSSAI → allowing directly") + + var allowedSnssaiElement models.AllowedSnssai + allowedSnssaiElement.AllowedSnssai = new(models.Snssai) + *allowedSnssaiElement.AllowedSnssai = *subscribedSnssai.SubscribedSnssai + + accessType := models.AccessType__3_GPP_ACCESS + if param.Tai != nil { + accessType = util.GetAccessTypeFromConfig(*param.Tai) + } + + util.AddAllowedSnssai(allowedSnssaiElement, accessType, authorizedNetworkSliceInfo) continue } diff --git a/util/util.go b/util/util.go index c9e6131..8162bde 100644 --- a/util/util.go +++ b/util/util.go @@ -193,75 +193,193 @@ func CheckSupportedSnssaiInTa(snssai models.Snssai, tai models.Tai) bool { factory.ConfigLock.RLock() defer factory.ConfigLock.RUnlock() + logger.Util.Debugf("Input NSSAI: SST=%d SD=%s", snssai.Sst, snssai.Sd) + + if tai.PlmnId != nil { + logger.Util.Debugf("Input TAI: MCC=%s MNC=%s TAC=%s", + tai.PlmnId.Mcc, tai.PlmnId.Mnc, tai.Tac) + } else { + logger.Util.Warnf("TAI PLMN is nil") + } + + // ========================= // 1. Check Global TaList - for _, taConfig := range factory.NssfConfig.Configuration.TaList { - if taConfig.Tai != nil && compareTai(*taConfig.Tai, tai) { - for _, supportedSnssai := range taConfig.SupportedSnssaiList { - if supportedSnssai == snssai { + // ========================= + logger.Util.Debugf("Checking Global TaList...") + + for i, taConfig := range factory.NssfConfig.Configuration.TaList { + if taConfig.Tai == nil { + logger.Util.Warnf("TaList[%d]: TAI is nil", i) + continue + } + + if compareTai(*taConfig.Tai, tai) { + logger.Util.Debugf("TaList[%d]: TAI MATCH FOUND", i) + + for j, supportedSnssai := range taConfig.SupportedSnssaiList { + logger.Util.Debugf("TaList[%d]: Checking Supported NSSAI[%d]: SST=%d SD=%s", + i, j, supportedSnssai.Sst, supportedSnssai.Sd) + + if supportedSnssai == snssai || supportedSnssai.Sst == snssai.Sst { + logger.Util.Debugf("TaList[%d]: NSSAI MATCH FOUND → RETURN TRUE", i) return true } } + + logger.Util.Warnf("TaList[%d]: TAI matched but NSSAI NOT FOUND → RETURN FALSE", i) return false - // If TA matches but Slice is not found, we don't return false yet; - // we check other sources just in case. } } - // 2. Check AMF List (Fallback for Dynamic/GRPC Config) - for _, amfConfig := range factory.NssfConfig.Configuration.AmfList { - for _, supportedData := range amfConfig.SupportedNssaiAvailabilityData { - if supportedData.Tai != nil && compareTai(*supportedData.Tai, tai) { - for _, supportedSnssai := range supportedData.SupportedSnssaiList { + + // ========================= + // 2. Check AMF List + // ========================= + logger.Util.Debugf("Checking AMF SupportedNssaiAvailabilityData...") + + for i, amfConfig := range factory.NssfConfig.Configuration.AmfList { + logger.Util.Debugf("AMF[%d]: NfId=%s", i, amfConfig.NfId) + for j, supportedData := range amfConfig.SupportedNssaiAvailabilityData { + if supportedData.Tai == nil { + logger.Util.Warnf("AMF[%d] Data[%d]: TAI is nil", i, j) + continue + } + if compareTai(*supportedData.Tai, tai) { + logger.Util.Debugf("AMF[%d] Data[%d]: TAI MATCH FOUND", i, j) + + for k, supportedSnssai := range supportedData.SupportedSnssaiList { + logger.Util.Debugf("AMF[%d] Data[%d]: Checking NSSAI[%d]: SST=%d SD=%s", + i, j, k, supportedSnssai.Sst, supportedSnssai.Sd) + if supportedSnssai == snssai { + logger.Util.Debugf("AMF[%d] Data[%d]: NSSAI MATCH FOUND → RETURN TRUE", i, j) return true } } + logger.Util.Warnf("AMF[%d] Data[%d]: TAI matched but NSSAI NOT FOUND", i, j) } } } - // 3. Standard S-NSSAI Fallback - // If it is a Standard S-NSSAI (SST 1-3, no SD) and the PLMN check passed, - // we often assume it is supported unless explicitly restricted. - // (Enable this if your test environment implies standard slices are always on) + // ========================= + // 3. Standard NSSAI Fallback + // ========================= + if CheckStandardSnssai(snssai) { - // Verify if the PLMN supports it - if CheckSupportedSnssaiInPlmn(snssai, *tai.PlmnId) { - return true + logger.Util.Debugf("NSSAI is STANDARD (SST=%d, SD empty)", snssai.Sst) + + if tai.PlmnId != nil { + plmnSupported := CheckSupportedSnssaiInPlmn(snssai, *tai.PlmnId) + logger.Util.Debugf("PLMN support check result: %v", plmnSupported) + + if plmnSupported { + logger.Util.Debugf("Standard NSSAI allowed via PLMN → RETURN TRUE") + return true + } + } else { + logger.Util.Warnf("Cannot check PLMN support → PLMN is nil") } + } else { + logger.Util.Debugf("NSSAI is NON-STANDARD → skipping fallback") } + logger.Util.Warnf("No condition matched → RETURN FALSE") return false } // Check whether S-NSSAI is in SupportedNssaiAvailabilityData under the given TAI -func CheckSupportedNssaiAvailabilityData( - snssai models.Snssai, tai models.Tai, s []models.SupportedNssaiAvailabilityData, -) bool { - for _, supportedNssaiAvailabilityData := range s { - if reflect.DeepEqual(*supportedNssaiAvailabilityData.Tai, tai) && - CheckSnssaiInNssai(snssai, supportedNssaiAvailabilityData.SupportedSnssaiList) { +func CheckSupportedNssaiAvailabilityData(snssai models.Snssai, tai models.Tai, s []models.SupportedNssaiAvailabilityData) bool { + for i, data := range s { + if data.Tai == nil { + logger.Util.Warnf("Entry[%d]: Configured TAI is nil", i) + continue + } + + // Log CONFIGURED TAI (from NSSF config) + if data.Tai.PlmnId != nil { + logger.Util.Debugf("Entry[%d]: Configured TAI -> MCC=%s MNC=%s TAC=%s", + i, + data.Tai.PlmnId.Mcc, + data.Tai.PlmnId.Mnc, + data.Tai.Tac, + ) + } else { + logger.Util.Warnf("Entry[%d]: Configured TAI PLMN is nil", i) + } + + // Log REQUESTED TAI (incoming from AMF) + if tai.PlmnId != nil { + logger.Util.Debugf("Entry[%d]: Requested TAI -> MCC=%s MNC=%s TAC=%s", + i, + tai.PlmnId.Mcc, + tai.PlmnId.Mnc, + tai.Tac, + ) + } else { + logger.Util.Warnf("Entry[%d]: Requested TAI PLMN is nil", i) + } + + // Replace DeepEqual with manual comparison + taiMatch := false + + if data.Tai.PlmnId != nil && tai.PlmnId != nil { + if data.Tai.PlmnId.Mcc == tai.PlmnId.Mcc && + data.Tai.PlmnId.Mnc == tai.PlmnId.Mnc && + data.Tai.Tac == tai.Tac { + taiMatch = true + } + } + + logger.Util.Debugf("Entry[%d]: TAI Match Result = %v", i, taiMatch) + + if !taiMatch { + logger.Util.Warnf("Entry[%d]: TAI mismatch → Skipping NSSAI check", i) + continue + } + + // Check NSSAI + nssaiMatch := CheckSnssaiInNssai(snssai, data.SupportedSnssaiList) + + logger.Util.Debugf("Entry[%d]: NSSAI Match = %v", i, nssaiMatch) + + if nssaiMatch { + logger.Util.Debugf("Entry[%d]: MATCH FOUND (TAI + NSSAI)", i) return true } } + logger.Util.Warnf("No matching TAI + NSSAI found") return false } // Check whether S-NSSAI is supported or not by the AMF at UE's current TA func CheckSupportedSnssaiInAmfTa(snssai models.Snssai, nfId string, tai models.Tai) bool { - // Uncomment following lines if supported S-NSSAI lists of AMF Sets are independent of those of AMFs - // for _, amfSetConfig := range factory.NssfConfig.Configuration.AmfSetList { - // if amfSetConfig.AmfList != nil && len(amfSetConfig.AmfList) != 0 && Contain(nfId, amfSetConfig.AmfList) { - // return checkSupportedNssaiAvailabilityData(snssai, tai, amfSetConfig.SupportedNssaiAvailabilityData) - // } - // } - - for _, amfConfig := range factory.NssfConfig.Configuration.AmfList { + logger.Util.Debugf("Input NF ID: %s", nfId) + logger.Util.Debugf("Input SNSSAI: SST=%d SD=%s", snssai.Sst, snssai.Sd) + if tai.PlmnId != nil { + logger.Util.Debugf("Input TAI: MCC=%s MNC=%s TAC=%d", tai.PlmnId.Mcc, tai.PlmnId.Mnc, tai.Tac) + } else { + logger.Util.Warnf("TAI PLMN is nil") + } + logger.Util.Debugf("Configured AMF count: %d", len(factory.NssfConfig.Configuration.AmfList)) + for i, amfConfig := range factory.NssfConfig.Configuration.AmfList { + logger.Util.Debugf("Checking AMF[%d]: NfId=%s", i, amfConfig.NfId) if amfConfig.NfId == nfId { - return CheckSupportedNssaiAvailabilityData(snssai, tai, amfConfig.SupportedNssaiAvailabilityData) + logger.Util.Debugf("Match found for NF ID: %s", nfId) + if amfConfig.SupportedNssaiAvailabilityData == nil { + logger.Util.Warnf("SupportedNssaiAvailabilityData is nil for AMF %s", nfId) + } else { + logger.Util.Debugf("SupportedNssaiAvailabilityData entries: %d", + len(amfConfig.SupportedNssaiAvailabilityData)) + } + result := CheckSupportedNssaiAvailabilityData( + snssai, + tai, + amfConfig.SupportedNssaiAvailabilityData, + ) + logger.Util.Debugf("Result from CheckSupportedNssaiAvailabilityData: %v", result) + return result } } - - logger.Util.Warnf("no AMF %s in NSSF configuration", nfId) + logger.Util.Warnf("No AMF found for NF ID: %s in NSSF configuration", nfId) return false } @@ -290,23 +408,81 @@ func CheckStandardSnssai(snssai models.Snssai) bool { // Check whether the NSSAI contains the specific S-NSSAI func CheckSnssaiInNssai(targetSnssai models.Snssai, nssai []models.Snssai) bool { - for _, snssai := range nssai { - if snssai == targetSnssai { + logger.Util.Debugf("Requested NSSAI -> SST=%d SD=%s", targetSnssai.Sst, targetSnssai.Sd) + + for i, snssai := range nssai { + logger.Util.Debugf("Configured NSSAI[%d] -> SST=%d SD=%s", i, snssai.Sst, snssai.Sd) + + sstMatch := snssai.Sst == targetSnssai.Sst + sdMatch := snssai.Sd == targetSnssai.Sd + + logger.Util.Infof("Comparison Result[%d] -> SST Match=%v, SD Match=%v", i, sstMatch, sdMatch) + + if sstMatch && sdMatch { + logger.Util.Infof("NSSAI MATCH FOUND at index %d", i) + logger.Util.Infof("==== CheckSnssaiInNssai END ====") return true } + + if !sstMatch || !sdMatch { + logger.Util.Warnf("NSSAI mismatch at index %d -> Requested(SST=%d SD=%s) vs Configured(SST=%d SD=%s)", + i, + targetSnssai.Sst, targetSnssai.Sd, + snssai.Sst, snssai.Sd, + ) + } } + + logger.Util.Warnf("No matching NSSAI found in configured list") return false } -// Get S-NSSAI mappings of the given Home PLMN ID from configuration func GetMappingOfPlmnFromConfig(homePlmnId models.PlmnId) []models.MappingOfSnssai { factory.ConfigLock.RLock() defer factory.ConfigLock.RUnlock() - for _, mappingFromPlmn := range factory.NssfConfig.Configuration.MappingListFromPlmn { + + logger.CfgLog.Infof("GetMappingOfPlmnFromConfig called with HomePlmnId: MCC=%s, MNC=%s", + homePlmnId.Mcc, homePlmnId.Mnc) + + if factory.NssfConfig.Configuration == nil { + logger.CfgLog.Errorf("NSSF Config or Configuration is nil") + return nil + } + logger.CfgLog.Infof("MappingListFromPlmn length: %d", + len(factory.NssfConfig.Configuration.MappingListFromPlmn)) + + if factory.NssfConfig.Configuration.MappingListFromPlmn == nil { + logger.CfgLog.Warn("MappingListFromPlmn is nil") + } + + for idx, mappingFromPlmn := range factory.NssfConfig.Configuration.MappingListFromPlmn { + if mappingFromPlmn.HomePlmnId == nil { + logger.CfgLog.Warnf("MappingListFromPlmn[%d] has nil HomePlmnId", idx) + continue + } + + logger.CfgLog.Infof("Checking MappingListFromPlmn[%d]: MCC=%s, MNC=%s", + idx, + mappingFromPlmn.HomePlmnId.Mcc, + mappingFromPlmn.HomePlmnId.Mnc, + ) + if *mappingFromPlmn.HomePlmnId == homePlmnId { + logger.CfgLog.Infof("Match found for HomePlmnId at index %d", idx) + + if mappingFromPlmn.MappingOfSnssai == nil { + logger.CfgLog.Warnf("MappingOfSnssai is nil for matched PLMN at index %d", idx) + } else { + logger.CfgLog.Infof("MappingOfSnssai count: %d", len(mappingFromPlmn.MappingOfSnssai)) + } + return mappingFromPlmn.MappingOfSnssai } } + + logger.CfgLog.Infof("No mapping found for HomePlmnId: MCC=%s, MNC=%s", + homePlmnId.Mcc, homePlmnId.Mnc) + return nil } @@ -326,7 +502,9 @@ func GetNsiInformationListFromConfig(snssai models.Snssai) []models.NsiInformati func GetAccessTypeFromConfig(tai models.Tai) models.AccessType { factory.ConfigLock.RLock() defer factory.ConfigLock.RUnlock() - for _, taConfig := range factory.NssfConfig.Configuration.TaList { + logger.Util.Infof("Checking length[%d]", len(factory.NssfConfig.Configuration.TaList)) + for i, taConfig := range factory.NssfConfig.Configuration.TaList { + logger.Util.Infof("Checking TA[%d]: %+v", i, taConfig.Tai) if reflect.DeepEqual(*taConfig.Tai, tai) { return *taConfig.AccessType }