Jimmy
    • Create new note
    • Create a note from template
      • Sharing URL Link copied
      • /edit
      • View mode
        • Edit mode
        • View mode
        • Book mode
        • Slide mode
        Edit mode View mode Book mode Slide mode
      • Customize slides
      • Note Permission
      • Read
        • Only me
        • Signed-in users
        • Everyone
        Only me Signed-in users Everyone
      • Write
        • Only me
        • Signed-in users
        • Everyone
        Only me Signed-in users Everyone
      • Engagement control Commenting, Suggest edit, Emoji Reply
    • Invite by email
      Invitee

      This note has no invitees

    • Publish Note

      Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note

      Your note will be visible on your profile and discoverable by anyone.
      Your note is now live.
      This note is visible on your profile and discoverable online.
      Everyone on the web can find and read all notes of this public team.
      See published notes
      Unpublish note
      Please check the box to agree to the Community Guidelines.
      View profile
    • Commenting
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
      • Everyone
    • Suggest edit
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
    • Emoji Reply
    • Enable
    • Versions and GitHub Sync
    • Note settings
    • Note Insights New
    • Engagement control
    • Make a copy
    • Transfer ownership
    • Delete this note
    • Save as template
    • Insert from template
    • Import from
      • Dropbox
      • Google Drive
      • Gist
      • Clipboard
    • Export to
      • Dropbox
      • Google Drive
      • Gist
    • Download
      • Markdown
      • HTML
      • Raw HTML
Menu Note settings Note Insights Versions and GitHub Sync Sharing URL Create Help
Create Create new note Create a note from template
Menu
Options
Engagement control Make a copy Transfer ownership Delete this note
Import from
Dropbox Google Drive Gist Clipboard
Export to
Dropbox Google Drive Gist
Download
Markdown HTML Raw HTML
Back
Sharing URL Link copied
/edit
View mode
  • Edit mode
  • View mode
  • Book mode
  • Slide mode
Edit mode View mode Book mode Slide mode
Customize slides
Note Permission
Read
Only me
  • Only me
  • Signed-in users
  • Everyone
Only me Signed-in users Everyone
Write
Only me
  • Only me
  • Signed-in users
  • Everyone
Only me Signed-in users Everyone
Engagement control Commenting, Suggest edit, Emoji Reply
  • Invite by email
    Invitee

    This note has no invitees

  • Publish Note

    Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note

    Your note will be visible on your profile and discoverable by anyone.
    Your note is now live.
    This note is visible on your profile and discoverable online.
    Everyone on the web can find and read all notes of this public team.
    See published notes
    Unpublish note
    Please check the box to agree to the Community Guidelines.
    View profile
    Engagement control
    Commenting
    Permission
    Disabled Forbidden Owners Signed-in users Everyone
    Enable
    Permission
    • Forbidden
    • Owners
    • Signed-in users
    • Everyone
    Suggest edit
    Permission
    Disabled Forbidden Owners Signed-in users Everyone
    Enable
    Permission
    • Forbidden
    • Owners
    • Signed-in users
    Emoji Reply
    Enable
    Import from Dropbox Google Drive Gist Clipboard
       Owned this note    Owned this note      
    Published Linked with GitHub
    • Any changes
      Be notified of any changes
    • Mention me
      Be notified of mention me
    • Unsubscribe
    :::spoiler Jump through topic > # Modify the packet parameters with “Security Mode Command” > > # Modify the packet parameters with “Initial Context Setup Request” ::: :::spoiler Original log from core ![image](https://hackmd.io/_uploads/H1W_Es436.png) ![image](https://hackmd.io/_uploads/rk9Y4sE3a.png) ![image](https://hackmd.io/_uploads/SyFoEo42T.png) ![image](https://hackmd.io/_uploads/HyXnNoV2a.png) ::: # 1. Modify the packet parameters with “Authentication request” ## After modification in ![image](https://hackmd.io/_uploads/rktlGBZdp.png) ### Make problem : imported not use ![image](https://hackmd.io/_uploads/Sypi7Hb_a.png) :::success * Detached this two module. ![image](https://hackmd.io/_uploads/HyT1NBbdT.png) * Then compile again and it's successful. * Then run core, gnb and UE again. ::: ### Core error 1. ![image](https://hackmd.io/_uploads/Bkq8vSWdp.png) ![image](https://hackmd.io/_uploads/ryzKOHZda.png) 2. Make(compiler) problem(Need to add one more parameter to the function) (Screenshot gone) ![](https://hackmd.io/_uploads/Sk_2Svt-p.png) ![](https://hackmd.io/_uploads/BkyaHvtba.png) :::success **Solution** * Modification in ![image](https://hackmd.io/_uploads/Bk6i0H-uT.png) ::: ## After modification in ![image](https://hackmd.io/_uploads/Bk6i0H-uT.png) ![image](https://hackmd.io/_uploads/B1Xl9IW_T.png) :::success **Solution** ![image](https://hackmd.io/_uploads/B1DH9Ub_T.png) ::: ### UE error ![image](https://hackmd.io/_uploads/HkF2uHbd6.png) :::success * It is related to CN, after CN errors are fixed, it can access the network. ::: # 2. Remove “AUSF” ## File editting to ignore this NF ``` nano /home/ubuntu/free5gc/run.sh nano /home/ubuntu/free5gc/Makefile ``` * run.sh ![image](https://hackmd.io/_uploads/Sk_pbKzua.png) * Makefile ![image](https://hackmd.io/_uploads/H1XIZKzOp.png) * **Then complile it again, we can see the error messages on 5gc log, it is a important clue to find where to modify in source code.** ![](https://hackmd.io/_uploads/S1U0EPFb6.png) ![image](https://hackmd.io/_uploads/H1zGmtf_T.png) ## After modification in ![image](https://hackmd.io/_uploads/ByeEvwtO6.png) * Need to detached the including function. ![image](https://hackmd.io/_uploads/ryelTJKOp.png) :::success **Solution** ![image](https://hackmd.io/_uploads/rkFm6yF_T.png) ::: :::danger **Problem** ![image](https://hackmd.io/_uploads/SkhwRJt_T.png) * It looks the same as I met in lll testing process. * And it it not related to MongoDB because in lll testing I thought that the error message included the mongodb part, but it only related to UPF problem. :::success **Solution** * forcekill and make agaiin and again, then it runs successfully. ::: ## After compiling ### Core (From NG setup) ![image](https://hackmd.io/_uploads/SJdyZlFuT.png) * We can search the error message in VScode, and find it is in send.go ### UE (Past screenshot) ![B1cxXfnGa](https://hackmd.io/_uploads/rJpX-xF_a.png) ## After modification in ![image](https://hackmd.io/_uploads/SJVzwwKuT.png) * **Test successfully, see the testing result.** # 3. Remove "NSSF" ## File editting * Makefile ![image](https://hackmd.io/_uploads/SJxMPF-KOa.png) * run.sh ![image](https://hackmd.io/_uploads/rJLit-tda.png) ### Record the log before accessing UE * We can see if there is defference between this and the orignal clean 5GC. ![image](https://hackmd.io/_uploads/SJb_9WY_a.png) ![image](https://hackmd.io/_uploads/ryzYcbKuT.png) ![image](https://hackmd.io/_uploads/S1zc5ZKOp.png) ![image](https://hackmd.io/_uploads/ByEacZtda.png) * The NSSF part in original log disappear, see below is the original one. ![image](https://hackmd.io/_uploads/BytX3WYd6.png) ## The error messages after ignore NSSF ### AMF can't select NSSF :::spoiler Past note ![unnamed](https://hackmd.io/_uploads/HkHd2ji_6.jpg) ![error1.png](https://hackmd.io/_uploads/rkAQwXrXT.png) ![error_gnb 1.png](https://hackmd.io/_uploads/S1AyvXBX6.png) ![AMF can not select an NSSF by NRF.png](https://hackmd.io/_uploads/ByJlvmrQT.png) :::success ::: #### Core side ![image](https://hackmd.io/_uploads/ByGUMvYOT.png) ![image](https://hackmd.io/_uploads/H1a5fPtOT.png) ![image](https://hackmd.io/_uploads/ByHazPtOp.png) * Repeat that **AMF can not select an NSSF Instance by NRF[Error: AMF can not select an NSSF by NRF]** #### UE side ![image](https://hackmd.io/_uploads/r1PlVwF_p.png) * After a while, it keep trying retransmitting PDU session establishment request, and when it attempts 5 time it will abort the SM procedure. ### Error fixing process :::warning 1. Use **ctrl+shift+F** to find the keyword in the whole folder, here we can see there are two places which may print out this error messages. ![image](https://hackmd.io/_uploads/S179CPYOa.png) 2. In this line ![image](https://hackmd.io/_uploads/r1UMMBc_T.png), we can see it is printed from GMM(GPRS Mobility Management, [LINK1](https://www.mpirical.com/glossary/gmm-gprs-mobility-management), [LINK2](https://blog.csdn.net/hitguolu/article/details/109215507)), so first we modify the handler.go which is in the GMM folder. ```go= if needSliceSelection { if ue.NssfUri == "" { for { err := consumer.SearchNssfNSSelectionInstance(ue, amfSelf.NrfUri, models.NfType_NSSF, models.NfType_AMF, nil) if err != nil { //ue.GmmLog.Errorf("AMF can not select an NSSF Instance by NRF[Error: %+v]", err) //time.Sleep(2 * time.Second) } else { break } } } ``` * After modifying and compiling, the error message is still the same, so we should modify the **sm_context.go**. 3. If I directly ignore the error printing and time sleep lines, the error message is gone, but it will enter a infinite loop to look for NSSF. ![image](https://hackmd.io/_uploads/SkmpqI5Oa.png) 4. Then I add some messages printed to see the log sequence running in the code, and try to break the for loop after TEST04. * ![image](https://hackmd.io/_uploads/Syuc4D9_6.png) ```go= func SelectSmf( ue *amf_context.AmfUe, anType models.AccessType, pduSessionID int32, snssai models.Snssai, dnn string, ) (*amf_context.SmContext, uint8, error) { var ( smfID string smfUri string ) ue.GmmLog.Infof("Select SMF [snssai: %+v, dnn: %+v]", snssai, dnn) nrfUri := ue.ServingAMF().NrfUri // default NRF URI is pre-configured by AMF ue.GmmLog.Errorf("/////////////////TEST01////////////////") nsiInformation := ue.GetNsiInformationFromSnssai(anType, snssai) ue.GmmLog.Errorf("/////////////////TEST02////////////////") if nsiInformation == nil { if ue.NssfUri == "" { // TODO: Set a timeout of NSSF Selection or will starvation here for { ue.GmmLog.Errorf("/////////////////TEST03////////////////") if err := SearchNssfNSSelectionInstance(ue, nrfUri, models.NfType_NSSF, models.NfType_AMF, nil); err != nil { ue.GmmLog.Errorf("/////////////////TEST04////////////////") //ue.GmmLog.Errorf("AMF can not select an NSSF Instance by NRF[Error: %+v]", err) //time.Sleep(2 * time.Second) break } else { break } } } response, problemDetails, err := NSSelectionGetForPduSession(ue, snssai) if err != nil { err = fmt.Errorf("NSSelection Get Error[%+v]", err) return nil, nasMessage.Cause5GMMPayloadWasNotForwarded, err } else if problemDetails != nil { err = fmt.Errorf("NSSelection Get Failed Problem[%+v]", problemDetails) return nil, nasMessage.Cause5GMMPayloadWasNotForwarded, err } nsiInformation = response.NsiInformation } . . . ``` * ![image](https://hackmd.io/_uploads/Byd_Vv9u6.png) ```go= func SendSearchNFInstances(nrfUri string, targetNfType, requestNfType models.NfType, param *Nnrf_NFDiscovery.SearchNFInstancesParamOpts, ) (models.SearchResult, error) { // Set client and set url fmt.Println("////TEST A////") configuration := Nnrf_NFDiscovery.NewConfiguration() fmt.Println("////TEST B////") configuration.SetBasePath(nrfUri) client := Nnrf_NFDiscovery.NewAPIClient(configuration) fmt.Println("////TEST C////") result, res, err := client.NFInstancesStoreApi.SearchNFInstances(context.TODO(), targetNfType, requestNfType, param) // 從這行印出 Handle NFDiscoveryRequest 和 200 GET,然後就到下面return回去了。 fmt.Println("////TEST D////") if res != nil && res.StatusCode == http.StatusTemporaryRedirect { err = fmt.Errorf("Temporary Redirect For Non NRF Consumer") } if res == nil || res.Body == nil { return result, err } defer func() { if res != nil { if bodyCloseErr := res.Body.Close(); bodyCloseErr != nil { err = fmt.Errorf("SearchNFInstances' response body cannot close: %+w", bodyCloseErr) } } }() return result, err } ``` 5. The result is like below. ![image](https://hackmd.io/_uploads/rkQIIwqOa.png) * Although it breaks successfully, it gets in another error which go back to resend DL NAS Transport. * Then enters another infinite loop which still want to find SMF based on snssai. ::: ### Select SMD failed: NSSelection Get Error[NSSF No Response] * From 5. ### Error fixing process :::warning * Originally I thought that the error message is from here, where is below the ignored two lines. ![image](https://hackmd.io/_uploads/HygrjPcup.png) * But when searching the prefix of this line![image](https://hackmd.io/_uploads/SJPS7d9OT.png), it actually starts from **func CreatePDUSession** in ![image](https://hackmd.io/_uploads/BJzsQ_9ua.png) ```go= func CreatePDUSession(ulNasTransport *nasMessage.ULNASTransport, ue *context.AmfUe, anType models.AccessType, pduSessionID int32, smMessage []uint8, ) (setNewSmContext bool, err error) { var ( snssai models.Snssai dnn string ) // A) AMF shall select an SMF // If the S-NSSAI IE is not included and the user's subscription context obtained from UDM. AMF shall // select a default snssai if ulNasTransport.SNSSAI != nil { snssai = nasConvert.SnssaiToModels(ulNasTransport.SNSSAI) } else { if allowedNssai, ok := ue.AllowedNssai[anType]; ok { snssai = *allowedNssai[0].AllowedSnssai } else { return false, errors.New("Ue doesn't have allowedNssai") } } if ulNasTransport.DNN != nil { dnn = ulNasTransport.DNN.GetDNN() } else { // if user's subscription context obtained from UDM does not contain the default DNN for the, // S-NSSAI, the AMF shall use a locally configured DNN as the DNN dnn = ue.ServingAMF().SupportDnnLists[0] if ue.SmfSelectionData != nil { snssaiStr := util.SnssaiModelsToHex(snssai) if snssaiInfo, ok := ue.SmfSelectionData.SubscribedSnssaiInfos[snssaiStr]; ok { for _, dnnInfo := range snssaiInfo.DnnInfos { if dnnInfo.DefaultDnnIndicator { dnn = dnnInfo.Dnn } } } } } if newSmContext, cause, err := consumer.SelectSmf(ue, anType, pduSessionID, snssai, dnn); err != nil { ue.GmmLog.Errorf("Select SMF failed: %+v", err) gmm_message.SendDLNASTransport(ue.RanUe[anType], nasMessage.PayloadContainerTypeN1SMInfo, smMessage, pduSessionID, cause, nil, 0) } else { _, smContextRef, errResponse, problemDetail, err := consumer.SendCreateSmContextRequest( ue, newSmContext, nil, smMessage) if err != nil { ue.GmmLog.Errorf("CreateSmContextRequest Error: %+v", err) return false, nil } else if problemDetail != nil { // TODO: error handling return false, fmt.Errorf("Failed to Create smContext[pduSessionID: %d], Error[%v]", pduSessionID, problemDetail) } else if errResponse != nil { ue.GmmLog.Warnf("PDU Session Establishment Request is rejected by SMF[pduSessionId:%d]", pduSessionID) gmm_message.SendDLNASTransport(ue.RanUe[anType], nasMessage.PayloadContainerTypeN1SMInfo, errResponse.BinaryDataN1SmMessage, pduSessionID, 0, nil, 0) } else { newSmContext.SetSmContextRef(smContextRef) newSmContext.SetUserLocation(deepcopy.Copy(ue.Location).(models.UserLocation)) ue.StoreSmContext(pduSessionID, newSmContext) ue.GmmLog.Infof("create smContext[pduSessionID: %d] Success", pduSessionID) // TODO: handle response(response N2SmInfo to RAN if exists) return true, nil } } return false, nil } ``` * It is kinda complicated, so I draw the clall flow : ![unnamed](https://hackmd.io/_uploads/Hkma3oodp.jpg) 6. Until here, I may have two ways to solve it : - Keep ingoring the lines produce the error messages. 1. First I try to skip the SendDLNASTransport process, let it continue to next else function. ![image](https://hackmd.io/_uploads/SJb-JhsOT.png) but it turns out ![image](https://hackmd.io/_uploads/BJeNghodp.png) or modify like this ![image](https://hackmd.io/_uploads/SJvex3j_T.png) but it need the parameters passed from SelectSMF ![image](https://hackmd.io/_uploads/BkHvxhsOa.png) 2. Then I turn to **sm_context.go** to see if I can ignore the error parameter(err) which will be returned to **handler.go**. 3. I add some text before each return line in **func SelectSmf**, and result is that it returns at here : ![image](https://hackmd.io/_uploads/rk2if2j_6.png) in the first if function, ![image](https://hackmd.io/_uploads/Hyskm3sOT.png) and it actually means that the prefix of the ![image](https://hackmd.io/_uploads/SJPS7d9OT.png) is from **CreatePDUSession** but afterpart(suffix) is printed here before return back(return01), so if I ignore this ![image](https://hackmd.io/_uploads/SyCDNhj_T.png), the log looks like this ![image](https://hackmd.io/_uploads/HJcsVnsup.png), which is different from this ![image](https://hackmd.io/_uploads/BJNANhjda.png). 4. Then I scroll down to see the rest code of the func SelectSmf after return01, and find that maybe I can try to ignore the whole if function, because it seems that it doesnt really need it, if I remove it, the error will also not be put in err parameter. * Try to modify the code, let SMF stop to find NSSF or give AMF a fixed value snssai to select SMF. 1. So in the last method, we cannot just ignore the error meassage lines to jump through this process. 2. Like the point 4. up there, I try to ingore the whole nsiInformation checking function, the result put in below part. ::: ### Solution :::success 1. Continued to say, because the if function I modified seems to be useless for afterpart in SelectSmf : ```go= . . . smContext := amf_context.NewSmContext(pduSessionID) smContext.SetSnssai(snssai) smContext.SetDnn(dnn) smContext.SetAccessType(anType) if nsiInformation == nil { ue.GmmLog.Warnf("nsiInformation is still nil, use default NRF[%s]", nrfUri) } else { smContext.SetNsInstance(nsiInformation.NsiId) nrfApiUri, err := url.Parse(nsiInformation.NrfId) if err != nil { ue.GmmLog.Errorf("Parse NRF URI error, use default NRF[%s]", nrfUri) } else { nrfUri = fmt.Sprintf("%s://%s", nrfApiUri.Scheme, nrfApiUri.Host) } } param := Nnrf_NFDiscovery.SearchNFInstancesParamOpts{ ServiceNames: optional.NewInterface([]models.ServiceName{models.ServiceName_NSMF_PDUSESSION}), Dnn: optional.NewString(dnn), Snssais: optional.NewInterface(openapi.MarshToJsonString([]models.Snssai{snssai})), } if ue.PlmnId.Mcc != "" { param.TargetPlmnList = optional.NewInterface(openapi.MarshToJsonString(ue.PlmnId)) } if amf_context.GetSelf().Locality != "" { param.PreferredLocality = optional.NewString(amf_context.GetSelf().Locality) } ue.GmmLog.Debugf("Search SMF from NRF[%s]", nrfUri) result, err := SendSearchNFInstances(nrfUri, models.NfType_SMF, models.NfType_AMF, &param) if err != nil { ue.GmmLog.Errorf("////return03////") return nil, nasMessage.Cause5GMMPayloadWasNotForwarded, err } if len(result.NfInstances) == 0 { err = fmt.Errorf("DNN[%s] is not supported or not subscribed in the slice[Snssai: %+v]", dnn, snssai) ue.GmmLog.Errorf("////return04////") return nil, nasMessage.Cause5GMMDNNNotSupportedOrNotSubscribedInTheSlice, err } // select the first SMF, TODO: select base on other info for _, nfProfile := range result.NfInstances { smfUri = util.SearchNFServiceUri(nfProfile, models.ServiceName_NSMF_PDUSESSION, models.NfServiceStatus_REGISTERED) if smfUri != "" { break } } smContext.SetSmfID(smfID) smContext.SetSmfUri(smfUri) ue.GmmLog.Errorf("////return05////") return smContext, 0, nil } ``` 2. I try to do this, not just ingore the lines produce the error messages, but stop the NSSelection error production : ![image](https://hackmd.io/_uploads/rJn3JIfKp.png) 3. And it works, the PDUSession is builded successfully, though there is a warning message, see the [testing result](https://hackmd.io/inBBnqB5Qx-ZMNsb_jaq0Q#Remove-NSSF). ::: # 4. Remove "PCF" ## File editting * Makefile ![image](https://hackmd.io/_uploads/B1T03qGY6.png) * run.sh ![image](https://hackmd.io/_uploads/rkTNaqMtT.png) ### Record the log before accessing UE * You can see PCF log dispear, and check the difference with the last part "Remove NSSF" ![image](https://hackmd.io/_uploads/By_wTcfYp.png) ![image](https://hackmd.io/_uploads/By8OpczFT.png) ![image](https://hackmd.io/_uploads/Syk56qMFp.png) ![image](https://hackmd.io/_uploads/rkMn65Mt6.png) ## The error logs after ignoring PCF and acessing the UE ### 1. AMF can not select an PCF by NRF **Core side** ![image](https://hackmd.io/_uploads/HywYgsMKT.png) ![image](https://hackmd.io/_uploads/BJU9xsMta.png) ![image](https://hackmd.io/_uploads/ByU2eifKa.png) **gnb side** * Repeated messages ![image](https://hackmd.io/_uploads/r14EejftT.png) **UE side** * Repeated messages ![image](https://hackmd.io/_uploads/HyP-xiMYp.png) ### Error fixing process :::warning 1. Search in the folder ![image](https://hackmd.io/_uploads/rkdQEimtT.png) * In handler.go, the logs is from this function, and you can check it start from printing out **Handle InitialRegistration** by see the log up there(Core side). ```go= func HandleInitialRegistration(ue *context.AmfUe, anType models.AccessType) error { ue.GmmLog.Infoln("Handle InitialRegistration") amfSelf := context.GetSelf() // update Kgnb/Kn3iwf ue.UpdateSecurityContext(anType) // Registration with AMF re-allocation (TS 23.502 4.2.2.2.3) if len(ue.SubscribedNssai) == 0 { getSubscribedNssai(ue) } if err := handleRequestedNssai(ue, anType); err != nil { return err } if ue.RegistrationRequest.Capability5GMM != nil { ue.Capability5GMM = *ue.RegistrationRequest.Capability5GMM } else { gmm_message.SendRegistrationReject(ue.RanUe[anType], nasMessage.Cause5GMMProtocolErrorUnspecified, "") return fmt.Errorf("Capability5GMM is nil") } storeLastVisitedRegisteredTAI(ue, ue.RegistrationRequest.LastVisitedRegisteredTAI) if ue.RegistrationRequest.MICOIndication != nil { ue.GmmLog.Warnf("Receive MICO Indication[RAAI: %d], Not Supported", ue.RegistrationRequest.MICOIndication.GetRAAI()) } // TODO: Negotiate DRX value if need (TS 23.501 5.4.5) negotiateDRXParameters(ue, ue.RegistrationRequest.RequestedDRXParameters) // TODO (step 10 optional): send Namf_Communication_RegistrationCompleteNotify to old AMF if need if ue.ServingAmfChanged { // If the AMF has changed the new AMF notifies the old AMF that the registration of the UE in the new AMF is completed req := models.UeRegStatusUpdateReqData{ TransferStatus: models.UeContextTransferStatus_TRANSFERRED, } // TODO: based on locol policy, decide if need to change serving PCF for UE regStatusTransferComplete, problemDetails, err := consumer.RegistrationStatusUpdate(ue, req) if problemDetails != nil { ue.GmmLog.Errorf("Registration Status Update Failed Problem[%+v]", problemDetails) } else if err != nil { ue.GmmLog.Errorf("Registration Status Update Error[%+v]", err) } else { if regStatusTransferComplete { ue.GmmLog.Infof("Registration Status Transfer complete") } } } if len(ue.Pei) == 0 { gmm_message.SendIdentityRequest(ue.RanUe[anType], anType, nasMessage.MobileIdentity5GSTypeImei) return nil } // TODO (step 12 optional): the new AMF initiates ME identity check by invoking the // N5g-eir_EquipmentIdentityCheck_Get service operation if ue.ServingAmfChanged || ue.State[models.AccessType_NON_3_GPP_ACCESS].Is(context.Registered) || !ue.ContextValid { if err := communicateWithUDM(ue, anType); err != nil { ue.GmmLog.Errorf("communicateWithUDM error: %v", err) gmm_message.SendRegistrationReject(ue.RanUe[anType], nasMessage.Cause5GMMPLMNNotAllowed, "") return errors.Wrap(err, "communicateWithUDM failed") } } param := Nnrf_NFDiscovery.SearchNFInstancesParamOpts{ Supi: optional.NewString(ue.Supi), } if amfSelf.Locality != "" { param.PreferredLocality = optional.NewString(amfSelf.Locality) } for { resp, err := consumer.SendSearchNFInstances(amfSelf.NrfUri, models.NfType_PCF, models.NfType_AMF, &param) if err != nil { ue.GmmLog.Error("AMF can not select an PCF by NRF") } else { // select the first PCF, TODO: select base on other info var pcfUri string for _, nfProfile := range resp.NfInstances { pcfUri = util.SearchNFServiceUri(nfProfile, models.ServiceName_NPCF_AM_POLICY_CONTROL, models.NfServiceStatus_REGISTERED) if pcfUri != "" { ue.PcfId = nfProfile.NfInstanceId break } } if ue.PcfUri = pcfUri; ue.PcfUri == "" { ue.GmmLog.Error("AMF can not select an PCF by NRF") } else { break } } time.Sleep(500 * time.Millisecond) // sleep a while when search NF Instance fail } problemDetails, err := consumer.AMPolicyControlCreate(ue, anType) if problemDetails != nil { ue.GmmLog.Errorf("AM Policy Control Create Failed Problem[%+v]", problemDetails) } else if err != nil { ue.GmmLog.Errorf("AM Policy Control Create Error[%+v]", err) } // Service Area Restriction are applicable only to 3GPP access if anType == models.AccessType__3_GPP_ACCESS { if ue.AmPolicyAssociation != nil && ue.AmPolicyAssociation.ServAreaRes != nil { servAreaRes := ue.AmPolicyAssociation.ServAreaRes if servAreaRes.RestrictionType == models.RestrictionType_ALLOWED_AREAS { numOfallowedTAs := 0 for _, area := range servAreaRes.Areas { numOfallowedTAs += len(area.Tacs) } // if numOfallowedTAs < int(servAreaRes.MaxNumOfTAs) { // TODO: based on AMF Policy, assign additional allowed area for UE, // and the upper limit is servAreaRes.MaxNumOfTAs (TS 29.507 4.2.2.3) // } } } } // TODO (step 18 optional): // If the AMF has changed and the old AMF has indicated an existing NGAP UE association towards a N3IWF, the new AMF // creates an NGAP UE association towards the N3IWF to which the UE is connectedsend N2 AMF mobility request to N3IWF // if anType == models.AccessType_NON_3_GPP_ACCESS && ue.ServingAmfChanged { // TODO: send N2 AMF Mobility Request // } amfSelf.AllocateRegistrationArea(ue, anType) ue.GmmLog.Debugf("Use original GUTI[%s]", ue.Guti) assignLadnInfo(ue, anType) amfSelf.AddAmfUeToUePool(ue, ue.Supi) ue.T3502Value = amfSelf.T3502Value if anType == models.AccessType__3_GPP_ACCESS { ue.T3512Value = amfSelf.T3512Value } else { ue.Non3gppDeregTimerValue = amfSelf.Non3gppDeregTimerValue } gmm_message.SendRegistrationAccept(ue, anType, nil, nil, nil, nil, nil) return nil } ``` 2. Try to break after error * After first error printing message, it still enter infinite loop, so the error is not printed here. ![image](https://hackmd.io/_uploads/BJ598o7Ka.png) * So it is actually printed from here. ![image](https://hackmd.io/_uploads/SJXgto7Yp.png) 3. And it turns out to be panic error. * Core side ![image](https://hackmd.io/_uploads/SyEqKoXFp.png) * Repeated UE side ![image](https://hackmd.io/_uploads/HJpWijXYT.png) 4. But sometimes it will print out another kind of panic error. * Core side ![image](https://hackmd.io/_uploads/H1tlqoXYp.png) ![image](https://hackmd.io/_uploads/r1iWqo7K6.png) * Repeated UE side ![image](https://hackmd.io/_uploads/S1DSqimtp.png) * Here the 4. should be the right way to print out the message, because when we close core, gnb, ue completely, and restart all of them, they always look like 4. * **But if we dont stop gnb and ue**, just restart the core, then also restart the gnb, and ue can connect again which doesnt need to stop and restart, you will see in the message ![image](https://hackmd.io/_uploads/Hk-O3sXYT.png) will be printed out in the core side, then when ue connect to core again, the error in 3. will happen. * The **panic error** means some of pointer parameters have an error address or value, which is also met in NSSF removing(in SMF selection), so I guess that if we dont stop the ue, the reconnected ue will cause the authentication parameter error in restarted 5GC. * So when we meet panic error, we may accidentally comment out some parameter which is using in the process of ignoring error printing line. * In the past experence, the panic error is hard to solve, because we may need to redefine the parameter or give some value to it * If we derectly comment out the patameter, other function need it. * If we dont comment out it, the function returning process need the parameter which we want to comment out, like the err parameter. * For example, response is needed for other function, err is needed for access the returned value, but we want to comment out err. ![image](https://hackmd.io/_uploads/ryEDM2QY6.png) 5. **But** in here, we can see the panic error is after another error message ![image](https://hackmd.io/_uploads/rJOMQ27tp.png) which is happened after ![image](https://hackmd.io/_uploads/SkJE73mKp.png), so we can deal with it first and see if the panic error disapears. ::: ### 2. AM Policy Control Create Error[server no response] * From 5. ### Error fixing process :::warning 6. You can see this error is from here. * And when you check other parts of **func HandleInitialRegistration**, you can see the only part has relation with PCF is this part, but I need to study more about PolicyControl to know what kind of relation it is. ```go= . . . for { resp, err := consumer.SendSearchNFInstances(amfSelf.NrfUri, models.NfType_PCF, models.NfType_AMF, &param) if err != nil { ue.GmmLog.Error("AMF can not select an PCF by NRF") //break } else { // select the first PCF, TODO: select base on other info var pcfUri string for _, nfProfile := range resp.NfInstances { pcfUri = util.SearchNFServiceUri(nfProfile, models.ServiceName_NPCF_AM_POLICY_CONTROL, models.NfServiceStatus_REGISTERED) if pcfUri != "" { ue.PcfId = nfProfile.NfInstanceId break } } if ue.PcfUri = pcfUri; ue.PcfUri == "" { ue.GmmLog.Error("AMF can not select an PCF by NRF") break } else { break } } time.Sleep(500 * time.Millisecond) // sleep a while when search NF Instance fail } problemDetails, err := consumer.AMPolicyControlCreate(ue, anType) if problemDetails != nil { ue.GmmLog.Errorf("AM Policy Control Create Failed Problem[%+v]", problemDetails) } else if err != nil { ue.GmmLog.Errorf("AM Policy Control Create Error[%+v]", err) } . . . ``` 7. I think there are some ways to **comment out** the PCF related part in initial registration. 1. **Remove the function SendSearchNFInstances** to stop the production of err. * The function SendSearchNFInstances is like a **global variable** in free5gc, other part of code will call it to find NF. * There will has an error about resp because it is used at below code. 2. **Break the for loop** after error printing line(this line can be commented out or it will be printed out as usual) * It seems to be OK, it successfully jump to next part and will not cause some implicit errors that will not be seen in logs, because the original error is trigered by PCF's uri is blank, and the blank PCFuri will not cause other problem, it is just because PCF is removed. * If this is OK, maybe we dont need to comment out the whole for loop, but if the err parameter is used in afterpart and the value stored in it caused another problem, we should do this or other modification(In the code, after this part, err will not be used.) 3. **Remove the function AMPolicyControlCreate** * It seems to be ok because the parameters problemDetails and err is only used in here. 4. But I just **comment out this part** ![image](https://hackmd.io/_uploads/ByJoO8SFT.png) 8. As 7-2 says, I also comment it out, ![image](https://hackmd.io/_uploads/Skufq8HtT.png) and the result is like below, the error message are gone, but the panic error still exists. ![image](https://hackmd.io/_uploads/SkhB3IrFT.png) 9. As stated above, now I just need to try figuring out how to solve this out. * The panic error happended **after I break the for loop**, and it is **printed after Send Initial Context Setup Request**, which is produced here : 1. In the end of func HandleInitialRegistration, it calls![image](https://hackmd.io/_uploads/B1-rxvSYT.png) 2. The func SendRegistrationAccept is in ![image](https://hackmd.io/_uploads/BJvAlvBKp.png) , which will print out this before Send Initial Context Setup Request ![image](https://hackmd.io/_uploads/SyUSGvBKT.png) ![image](https://hackmd.io/_uploads/r1g-fvHKT.png) 3. And func SendRegistrationAccept calls this function ![image](https://hackmd.io/_uploads/B1plbwBKT.png) , which is in here and will print out this ![image](https://hackmd.io/_uploads/BkqhMPBFa.png) ![image](https://hackmd.io/_uploads/r1HtZvrY6.png) * So there might have some parameters needed in the process of Send Registration Accept and Send Initial Context Setup Request, like below is one of the ue info but not be written in because the printed error and break. ![image](https://hackmd.io/_uploads/BJezLDBF6.png) 10. Originally I think the panic may have relation with commenting out some kind of parameter needed in **consumer.AMPolicyControlCreate** * (Uncertain) But compare to the panic error screenshot attached above, we can say there is no difference between this(after commenting out) and that(before commenting out). * So we might still turn to 9. for the answar. ::: ### 3. [FATA][AMF][Ngap] panic: runtime error: invalid memory address or nil pointer dereference * From 10. ### Error fixing process :::warning 11. Continue from 9. * After doing this, it still has panic. ![image](https://hackmd.io/_uploads/S1qQV5wKa.png) 12. And I think the reason is in the parameters passed from HandleInitialRegistration to SendRegistrationAccept and to SendInitialContextSetupRequest. ![image](https://hackmd.io/_uploads/H1xWB9PYT.png) ![image](https://hackmd.io/_uploads/Bk0Wr9PKT.png) 13. So I add some lines of fmt.println. to see the value passing process. If you has an error which says no import "fmt", you need to add this. ![image](https://hackmd.io/_uploads/SkXuucPFp.png) In this process, we need to repeat compiling core and change the line of print to see where the panic happens. 1. First in ![image](https://hackmd.io/_uploads/SyLQAERYp.png), wa can see the printed info before passing to SendRegistrationAccept : ![image](https://hackmd.io/_uploads/r1VEKovt6.png) 2. Then in ![image](https://hackmd.io/_uploads/Bk9YAECYa.png), the info can be printed out after BuildRegistrationAccept and before the if lines : ![image](https://hackmd.io/_uploads/HJgLKoDFp.png) 3. Then in the **SendInitialContextSetupRequest** of ![image](https://hackmd.io/_uploads/BytrMSRYp.png), I add these lines so I can check where the panic is from, here the HERE3 is printed and HERE4 is not printed, which means the panic is from **BuildInitialContextSetupRequest**. ![image](https://hackmd.io/_uploads/HybdtsPKa.png) 14. In this function, there are so many if functions, about 300 lines, so I just repeat printing to see which one is the last one run before panic error. ![image](https://hackmd.io/_uploads/rJVRFiwY6.png) ![image](https://hackmd.io/_uploads/Sym19iPFp.png) And result is here, wa can see +++++ is printed, and ===== is not printed. ![image](https://hackmd.io/_uploads/ryRe9iwYp.png) 15. The log is like below, because the value of ue is printed in original type, I dont know how to modify it. ![image](https://hackmd.io/_uploads/Skc4ciDKT.png) 16. Here just try to fix the PcfId, but it change nothing. ![image](https://hackmd.io/_uploads/H1gDI1OKT.png) 17. Keep tracing, we can dig more deep in **BuildIEMobilityRestrictionList**. ![image](https://hackmd.io/_uploads/BkZsPSCFa.png) And it is in ![image](https://hackmd.io/_uploads/HJUAPH0Yp.png), in the function I just try this if line first because it relates to PolicyAssociation, then the line of 55555 is not printed, so I want to print the ServAreaRes parameter, but it is not printed too, so the panic reason is in the parameter. ![image](https://hackmd.io/_uploads/HJd4OB0YT.png) 18. Here we can see the log stops at 33333. ![image](https://hackmd.io/_uploads/Hy-adHAKp.png) At the same time, when checking the log of panic error, we can see it has already printed out the start of error ![image](https://hackmd.io/_uploads/HyqUtBCKa.png) and the end of the error, ![image](https://hackmd.io/_uploads/H1buKB0t6.png) so next time we can follow this. 19. Next step, I find where is AmPolicyAssociation and ServAreaRes. One is in **type AmfUe struct** of this file ![image](https://hackmd.io/_uploads/ry2LiBRKa.png), the other one is in ![image](https://hackmd.io/_uploads/SJz1hrRYT.png). 20. If I derectly comment out here. ![image](https://hackmd.io/_uploads/r1lN2SCYa.png) I will get another panic error. ::: ### 4. [ERRO][SMF][GIN] panic: runtime error: index out of range [0] with length 0 * from 20. **Core side** * It skip the policy part of AMF and it seems entering the part of SMF but get panic error and repeat it. ![image](https://hackmd.io/_uploads/H1rS6SCFa.png) ![image](https://hackmd.io/_uploads/SyiYTS0Fp.png) ![image](https://hackmd.io/_uploads/H18sTHAYp.png) **gnb side** ![image](https://hackmd.io/_uploads/rJWVABAFp.png) **UE side** ![image](https://hackmd.io/_uploads/BysPABAtT.png) ### Error fixing process :::warning 21. ![image](https://hackmd.io/_uploads/HJWPztk9T.png) This line just disappears if I directly comment out in the code, so I didnt touch it. 22. ![image](https://hackmd.io/_uploads/BJSWqYJq6.png) Then I search this information to find where the panic error happend, and it is in here. ![image](https://hackmd.io/_uploads/B1v9qYyqT.png) When I want to print the value of rep.NfInstances[0], it turns out this, the error happenning place move to line 444. ![image](https://hackmd.io/_uploads/H1o4iFk9T.png) 23. Test in original full CN, the value of this **structure** is ![image](https://hackmd.io/_uploads/HJnzhY1ca.png) * Because I use logger.CtxLog.Warningf() to print it, it can be more conspicuous in many logs. * Because I use %v, the printed values is not only the first element, it print out all values in the struc. * Using %+v can print in detailed including the parameter name. ![image](https://hackmd.io/_uploads/B1RwatJqa.png)... 24. And I use the original value of NfInstance Id to write into it. ![image](https://hackmd.io/_uploads/BJg3N51cp.png) But the result stay the same. ![image](https://hackmd.io/_uploads/HJWREqyq6.png) I guess it may need all value of struc, so I do this to write the parameter if it has value in original CN. ![image](https://hackmd.io/_uploads/SJqxt9156.png) But the result stay the same. ![image](https://hackmd.io/_uploads/H1_bK9kcT.png) 25. ![image](https://hackmd.io/_uploads/HJ6t3915T.png) ![image](https://hackmd.io/_uploads/r139hqyca.png) ![image](https://hackmd.io/_uploads/ByS8nckqp.png) ![image](https://hackmd.io/_uploads/BJz_n9kcT.png) ![image](https://hackmd.io/_uploads/Sy7rhqk56.png) ![image](https://hackmd.io/_uploads/HkJN29J96.png) ::: ### 5. PDU Session Establishment Request is rejected by SMF * From 25. **Core side** ![image](https://hackmd.io/_uploads/H1aUC5JcT.png) ![image](https://hackmd.io/_uploads/rkgFA51ca.png) **gnb side** ![image](https://hackmd.io/_uploads/BJ4kkjkqT.png) **UE side** ![image](https://hackmd.io/_uploads/BkuZ1jyc6.png) ### Error fixing process :::warning 26. Only reserve these two lines. ![image](https://hackmd.io/_uploads/S1ViLT1qp.png) ![image](https://hackmd.io/_uploads/SJB5IaJ56.png) And dont comment out here. ![image](https://hackmd.io/_uploads/BJPE00e9T.png) * Result stay the same 27. Original values which printed in free5gc_v3.3_01(full core). ![image](https://hackmd.io/_uploads/S1Jewak56.png) ![image](https://hackmd.io/_uploads/SJgkPak56.png) ![image](https://hackmd.io/_uploads/S1sjTRgqT.png) ![image](https://hackmd.io/_uploads/BkLJR0lcp.png) 28. Dont comment out these lines and replace the apiprefix, I want to recover the policy in SMF. ![image](https://hackmd.io/_uploads/BJO8AkW5a.png) ![image](https://hackmd.io/_uploads/rJQ_8gb5T.png) * It is hard to specify the NfServices, so i didnt use original parameter service.ApiPrefix * Result stay the same * It use this kind of url to use the reference from github of openapi, in [detail](https://hackmd.io/DETj57LjQFimKBIOaD6csQ?view#Use-code-from-github). ![image](https://hackmd.io/_uploads/r1ac5uL5p.png) ![image](https://hackmd.io/_uploads/HJ6pqOUca.png) ::: ### 6. Combine PCF to SMF to recover the policy creating process * From 28. Because the log result has already on the stage of SMcontext and PDUsession creaing, so if I keep commenting out something, it may influence the whole process. #### 6-1. Investiigation in original core :::success 29. Compare to the modified core side(get the 504 error and release the IP of UE), the highlighted part in the picture(original core), maybe we can recover it through moving the removed function of PCF to SMF. ![image](https://hackmd.io/_uploads/BJxOCyb9p.png) * If I searh the first message(**[PCF][SMpolicy] Handle CreateSmPolicy**) in code directly, I can find this kind of sequence : **smpolicy.go <== api_default.go <== routers.go** (HandleCreateSmPolicyRequest <== HTTPSmPoliciesPost <== var routes = Routes) ![image](https://hackmd.io/_uploads/Bkj0N_8cT.png) ![image](https://hackmd.io/_uploads/r1RlHu8q6.png) ![image](https://hackmd.io/_uploads/rkeEBdL5p.png) 30. Print some numbers to seperate them so that I can know the breakpoints, then can know how and where to call router.go. ![image](https://hackmd.io/_uploads/H1qvuEUcp.png) ![image](https://hackmd.io/_uploads/rkmYuE8cp.png) ![image](https://hackmd.io/_uploads/SJWoON8qp.png) ![image](https://hackmd.io/_uploads/H1IeYVIcp.png) ![image](https://hackmd.io/_uploads/HJDZFE8qp.png) * Call between 1 and 2, which is by **consumer.SendSMPolicyAssociationCreate(smContext)**. 31. Keep tracing to **sm_policy.go** in sbi/consumer of SMF, we can find it calls the API producer in PCF through router.go between 2-6 and 2-7, which produce the messages in the log. ![image](https://hackmd.io/_uploads/ByykwuU5p.png) ![image](https://hackmd.io/_uploads/rJo6vuI9a.png) ![image](https://hackmd.io/_uploads/BJmK_uU5p.png) 32. About the detailed situation of openAPI call flow, [see here](https://hackmd.io/DETj57LjQFimKBIOaD6csQ?both#OpenAPI). 33. Keep finding where the logs between 2-6 and 2-7, we can find that they are all in the func createSMPolicyProcedure which is in the same SMF calling file (smpolicy.go). Here are the sources of each line : 1. ![image](https://hackmd.io/_uploads/rJijLIJs6.png) ![image](https://hackmd.io/_uploads/HJqWKUJip.png) 2. ![image](https://hackmd.io/_uploads/Sk5BYUJop.png) ![image](https://hackmd.io/_uploads/S1xRYIJj6.png) 3. ![image](https://hackmd.io/_uploads/SJ6UAI1ip.png) ![image](https://hackmd.io/_uploads/rJXrRU1iT.png) 4. ![image](https://hackmd.io/_uploads/rJmOCI1ip.png) ![image](https://hackmd.io/_uploads/Sk-FRLJsp.png) 5. ![image](https://hackmd.io/_uploads/ryMqRLJjp.png) ![image](https://hackmd.io/_uploads/rJ9iRL1sp.png) 6. ![image](https://hackmd.io/_uploads/HyYT0LJjp.png) ![image](https://hackmd.io/_uploads/SJEWkvyop.png) ::: #### 6-2. :::success 34. Before moving the function between 2-6~2-7, I find that I miss some clues : * In modifed core, it send 504 error after PDUAdress allocated. ![image](https://hackmd.io/_uploads/HkzjBUooa.png) * But in original core, it will send 200 for finding PCF successfully after PDUAdress allocated. ![image](https://hackmd.io/_uploads/Syj_LUoop.png) * And if I just move the part I investigated in 6-1, although it is truely the lacked policy part in SMF, I may still have error because of skipping this difference before 1 and some value printed by me. * The difference here is also caused by the lack of PCF I guess. 35. The releasing part after api 504. ![image](https://hackmd.io/_uploads/rkD0ozz2T.png) ![image](https://hackmd.io/_uploads/Skng3zGh6.png) ![image](https://hackmd.io/_uploads/r1LhlmGhT.png) ::: ### *v3.3_PCF_v0.3 & v3.3_Full_PCF_v0.1*

    Import from clipboard

    Paste your markdown or webpage here...

    Advanced permission required

    Your current role can only read. Ask the system administrator to acquire write and comment permission.

    This team is disabled

    Sorry, this team is disabled. You can't edit this note.

    This note is locked

    Sorry, only owner can edit this note.

    Reach the limit

    Sorry, you've reached the max length this note can be.
    Please reduce the content or divide it to more notes, thank you!

    Import from Gist

    Import from Snippet

    or

    Export to Snippet

    Are you sure?

    Do you really want to delete this note?
    All users will lose their connection.

    Create a note from template

    Create a note from template

    Oops...
    This template has been removed or transferred.
    Upgrade
    All
    • All
    • Team
    No template.

    Create a template

    Upgrade

    Delete template

    Do you really want to delete this template?
    Turn this template into a regular note and keep its content, versions, and comments.

    This page need refresh

    You have an incompatible client version.
    Refresh to update.
    New version available!
    See releases notes here
    Refresh to enjoy new features.
    Your user state has changed.
    Refresh to load new user state.

    Sign in

    Forgot password

    or

    By clicking below, you agree to our terms of service.

    Sign in via Facebook Sign in via Twitter Sign in via GitHub Sign in via Dropbox Sign in with Wallet
    Wallet ( )
    Connect another wallet

    New to HackMD? Sign up

    Help

    • English
    • 中文
    • Français
    • Deutsch
    • 日本語
    • Español
    • Català
    • Ελληνικά
    • Português
    • italiano
    • Türkçe
    • Русский
    • Nederlands
    • hrvatski jezik
    • język polski
    • Українська
    • हिन्दी
    • svenska
    • Esperanto
    • dansk

    Documents

    Help & Tutorial

    How to use Book mode

    Slide Example

    API Docs

    Edit in VSCode

    Install browser extension

    Contacts

    Feedback

    Discord

    Send us email

    Resources

    Releases

    Pricing

    Blog

    Policy

    Terms

    Privacy

    Cheatsheet

    Syntax Example Reference
    # Header Header 基本排版
    - Unordered List
    • Unordered List
    1. Ordered List
    1. Ordered List
    - [ ] Todo List
    • Todo List
    > Blockquote
    Blockquote
    **Bold font** Bold font
    *Italics font* Italics font
    ~~Strikethrough~~ Strikethrough
    19^th^ 19th
    H~2~O H2O
    ++Inserted text++ Inserted text
    ==Marked text== Marked text
    [link text](https:// "title") Link
    ![image alt](https:// "title") Image
    `Code` Code 在筆記中貼入程式碼
    ```javascript
    var i = 0;
    ```
    var i = 0;
    :smile: :smile: Emoji list
    {%youtube youtube_id %} Externals
    $L^aT_eX$ LaTeX
    :::info
    This is a alert area.
    :::

    This is a alert area.

    Versions and GitHub Sync
    Get Full History Access

    • Edit version name
    • Delete

    revision author avatar     named on  

    More Less

    Note content is identical to the latest version.
    Compare
      Choose a version
      No search result
      Version not found
    Sign in to link this note to GitHub
    Learn more
    This note is not linked with GitHub
     

    Feedback

    Submission failed, please try again

    Thanks for your support.

    On a scale of 0-10, how likely is it that you would recommend HackMD to your friends, family or business associates?

    Please give us some advice and help us improve HackMD.

     

    Thanks for your feedback

    Remove version name

    Do you want to remove this version name and description?

    Transfer ownership

    Transfer to
      Warning: is a public team. If you transfer note to this team, everyone on the web can find and read this note.

        Link with GitHub

        Please authorize HackMD on GitHub
        • Please sign in to GitHub and install the HackMD app on your GitHub repo.
        • HackMD links with GitHub through a GitHub App. You can choose which repo to install our App.
        Learn more  Sign in to GitHub

        Push the note to GitHub Push to GitHub Pull a file from GitHub

          Authorize again
         

        Choose which file to push to

        Select repo
        Refresh Authorize more repos
        Select branch
        Select file
        Select branch
        Choose version(s) to push
        • Save a new version and push
        • Choose from existing versions
        Include title and tags
        Available push count

        Pull from GitHub

         
        File from GitHub
        File from HackMD

        GitHub Link Settings

        File linked

        Linked by
        File path
        Last synced branch
        Available push count

        Danger Zone

        Unlink
        You will no longer receive notification when GitHub file changes after unlink.

        Syncing

        Push failed

        Push successfully