From 0a86d91f0cb828867a55cb3b1344628a6a4f7459 Mon Sep 17 00:00:00 2001 From: free5gc-org Date: Thu, 13 Aug 2020 19:24:22 +0000 Subject: [PATCH] Hotfix https://github.com/free5gc/free5gc/issues/84 free5gc issue#84 --- context/ue.go | 4 ++ ike/handler/handler.go | 83 +++++-------------------- ngap/handler/handler.go | 130 ++++++++++++++++++++++++++++++++-------- ngap/service/service.go | 5 +- relay/relay.go | 69 +++++++++++++++------ 5 files changed, 179 insertions(+), 112 deletions(-) diff --git a/context/ue.go b/context/ue.go index 377c662..7dfabf2 100644 --- a/context/ue.go +++ b/context/ue.go @@ -35,6 +35,10 @@ type N3IWFUe struct { /* PDU Session Setup Temporary Data */ TemporaryPDUSessionSetupData *PDUSessionSetupTemporaryData + /* Temporary cached NAS message */ + // Used when NAS registration accept arrived before + // UE setup NAS TCP connection with N3IWF + TemporaryCachedNASMessage []byte /* Security */ Kn3iwf []uint8 // 32 bytes (256 bits), value is from NGAP IE "Security Key" diff --git a/ike/handler/handler.go b/ike/handler/handler.go index ffa5e6b..064039e 100644 --- a/ike/handler/handler.go +++ b/ike/handler/handler.go @@ -15,7 +15,6 @@ import ( ike_message "free5gc/src/n3iwf/ike/message" "free5gc/src/n3iwf/logger" ngap_message "free5gc/src/n3iwf/ngap/message" - "free5gc/src/n3iwf/relay" "github.com/sirupsen/logrus" "golang.org/x/sys/unix" @@ -211,6 +210,7 @@ func HandleIKESAINIT(udpConn *net.UDPConn, n3iwfAddr, ueAddr *net.UDPAddr, messa // Create new IKE security association ikeSecurityAssociation := n3iwfSelf.NewIKESecurityAssociation() ikeSecurityAssociation.RemoteSPI = message.InitiatorSPI + ikeSecurityAssociation.MessageID = message.MessageID // Record algorithm in context ikeSecurityAssociation.EncryptionAlgorithm = responseSecurityAssociation.Proposals[0].EncryptionAlgorithm[0] @@ -391,6 +391,7 @@ func HandleIKEAUTH(udpConn *net.UDPConn, n3iwfAddr, ueAddr *net.UDPAddr, message // NOTE: tune it transformPseudorandomFunction := ikeSecurityAssociation.PseudorandomFunction + ikeSecurityAssociation.MessageID = message.MessageID switch ikeSecurityAssociation.State { case PreSignalling: @@ -634,12 +635,12 @@ func HandleIKEAUTH(udpConn *net.UDPConn, n3iwfAddr, ueAddr *net.UDPAddr, message return } - // Send IKE message to UE - SendIKEMessageToUE(udpConn, n3iwfAddr, ueAddr, responseIKEMessage) - // Shift state ikeSecurityAssociation.State++ + // Send IKE message to UE + SendIKEMessageToUE(udpConn, n3iwfAddr, ueAddr, responseIKEMessage) + case EAPSignalling: // If success, N3IWF will send an UPLinkNASTransport to AMF if eap != nil { @@ -1252,6 +1253,9 @@ func HandleCREATECHILDSA(udpConn *net.UDPConn, n3iwfAddr, ueAddr *net.UDPAddr, m } } + // Record message ID + ikeSecurityAssociation.MessageID = message.MessageID + // UE context thisUE := ikeSecurityAssociation.ThisUE if thisUE == nil { @@ -1326,72 +1330,15 @@ func HandleCREATECHILDSA(udpConn *net.UDPConn, n3iwfAddr, ueAddr *net.UDPAddr, m return } - // Setup GTP tunnel for UE - ueAssociatedGTPConnection := pduSession.GTPConnection - if userPlaneConnection, ok := n3iwfSelf.GTPConnectionWithUPFLoad(ueAssociatedGTPConnection.UPFIPAddr); ok { - // UPF UDP address - upfUDPAddr, err := net.ResolveUDPAddr("udp", ueAssociatedGTPConnection.UPFIPAddr+":2152") - if err != nil { - ikeLog.Errorf("Resolve UDP address failed: %+v", err) - return - } - - // UE TEID - ueTEID := n3iwfSelf.NewTEID(thisUE) - if ueTEID == 0 { - ikeLog.Error("Invalid TEID (0).") - return - } - - // Set UE associated GTP connection - ueAssociatedGTPConnection.UPFUDPAddr = upfUDPAddr - ueAssociatedGTPConnection.IncomingTEID = ueTEID - ueAssociatedGTPConnection.UserPlaneConnection = userPlaneConnection - - // Append NGAP PDU session resource setup response transfer - transfer, err := ngap_message.BuildPDUSessionResourceSetupResponseTransfer(pduSession) - if err != nil { - ikeLog.Errorf("Build PDU session resource setup response transfer failed: %+v", err) - return - } - ngap_message.AppendPDUSessionResourceSetupListSURes(temporaryPDUSessionSetupData.SetupListSURes, pduSessionID, transfer) - } else { - // Setup GTP connection with UPF - userPlaneConnection, upfUDPAddr, err := relay.SetupGTPTunnelWithUPF(ueAssociatedGTPConnection.UPFIPAddr) - if err != nil { - ikeLog.Errorf("Setup GTP connection with UPF failed: %+v", err) - return - } - // Listen GTP tunnel - if err := relay.ListenGTP(userPlaneConnection); err != nil { - ikeLog.Errorf("Listening GTP tunnel failed: %+v", err) - return - } - - // UE TEID - ueTEID := n3iwfSelf.NewTEID(thisUE) - if ueTEID == 0 { - ikeLog.Error("Invalid TEID (0).") - return - } - - // Setup GTP connection with UPF - ueAssociatedGTPConnection.UPFUDPAddr = upfUDPAddr - ueAssociatedGTPConnection.IncomingTEID = ueTEID - ueAssociatedGTPConnection.UserPlaneConnection = userPlaneConnection - - // Store GTP connection with UPF into N3IWF context - n3iwfSelf.GTPConnectionWithUPFStore(ueAssociatedGTPConnection.UPFIPAddr, userPlaneConnection) - - // Append NGAP PDU session resource setup response transfer - transfer, err := ngap_message.BuildPDUSessionResourceSetupResponseTransfer(pduSession) - if err != nil { - ikeLog.Errorf("Build PDU session resource setup response transfer failed: %+v", err) - return - } - ngap_message.AppendPDUSessionResourceSetupListSURes(temporaryPDUSessionSetupData.SetupListSURes, pduSessionID, transfer) + // Append NGAP PDU session resource setup response transfer + transfer, err := ngap_message.BuildPDUSessionResourceSetupResponseTransfer(pduSession) + if err != nil { + ikeLog.Errorf("Build PDU session resource setup response transfer failed: %+v", err) + return } + ngap_message.AppendPDUSessionResourceSetupListSURes(temporaryPDUSessionSetupData.SetupListSURes, pduSessionID, transfer) + // Remove handled PDU session setup request from queue temporaryPDUSessionSetupData.UnactivatedPDUSession = temporaryPDUSessionSetupData.UnactivatedPDUSession[1:] for { diff --git a/ngap/handler/handler.go b/ngap/handler/handler.go index 00ba271..1cc43b8 100644 --- a/ngap/handler/handler.go +++ b/ngap/handler/handler.go @@ -10,6 +10,7 @@ import ( ike_message "free5gc/src/n3iwf/ike/message" "free5gc/src/n3iwf/logger" ngap_message "free5gc/src/n3iwf/ngap/message" + "free5gc/src/n3iwf/relay" "math/rand" "net" "time" @@ -709,17 +710,21 @@ func HandleInitialContextSetupRequest(amf *context.N3IWFAMF, message *ngapType.N return } + n3iwfUe.N3IWFIKESecurityAssociation.State++ + // Send IKE message to UE handler.SendIKEMessageToUE(n3iwfUe.IKEConnection.Conn, n3iwfUe.IKEConnection.N3IWFAddr, n3iwfUe.IKEConnection.UEAddr, responseIKEMessage) - - n3iwfUe.N3IWFIKESecurityAssociation.State++ } -// handlePDUSessionResourceSetupRequestTransfer parse and store needed information -// for setup user plane connection for UE -// When success, it will return a status "success" set to "true" for indication and set -// unsuccessfulTransfer to nil, otherwise, return false and corresponding transfer -func handlePDUSessionResourceSetupRequestTransfer(ue *context.N3IWFUe, pduSession *context.PDUSession, transfer ngapType.PDUSessionResourceSetupRequestTransfer) (success bool, unsuccessfulTransfer []byte) { +// handlePDUSessionResourceSetupRequestTransfer parse and store needed information from NGAP +// and setup user plane connection for UE +// Parameters: +// UE context :: a pointer to the UE's pdusession data structure :: +// SMF PDU session resource setup request transfer +// Return value: +// a status value indicate whether the handlling is "success" :: +// if failed, an unsuccessfulTransfer is set, otherwise, set to nil +func handlePDUSessionResourceSetupRequestTransfer(ue *context.N3IWFUe, pduSession *context.PDUSession, transfer ngapType.PDUSessionResourceSetupRequestTransfer) (bool, []byte) { var pduSessionAMBR *ngapType.PDUSessionAggregateMaximumBitRate var ulNGUUPTNLInformation *ngapType.UPTransportLayerInformation @@ -759,15 +764,13 @@ func handlePDUSessionResourceSetupRequestTransfer(ue *context.N3IWFUe, pduSessio } if len(iesCriticalityDiagnostics.List) > 0 { - success = false cause := buildCause(ngapType.CausePresentProtocol, ngapType.CauseProtocolPresentAbstractSyntaxErrorFalselyConstructedMessage) criticalityDiagnostics := buildCriticalityDiagnostics(nil, nil, nil, &iesCriticalityDiagnostics) responseTransfer, err := ngap_message.BuildPDUSessionResourceSetupUnsuccessfulTransfer(*cause, &criticalityDiagnostics) if err != nil { ngapLog.Errorf("Build PDUSessionResourceSetupUnsuccessfulTransfer Error: %+v\n", err) } - unsuccessfulTransfer = responseTransfer - return + return false, responseTransfer } pduSession.Ambr = pduSessionAMBR @@ -785,14 +788,12 @@ func handlePDUSessionResourceSetupRequestTransfer(ue *context.N3IWFUe, pduSessio pduSession.SecurityIntegrity = true default: ngapLog.Error("Unknown security integrity indication") - success = false cause := buildCause(ngapType.CausePresentProtocol, ngapType.CauseProtocolPresentSemanticError) responseTransfer, err := ngap_message.BuildPDUSessionResourceSetupUnsuccessfulTransfer(*cause, nil) if err != nil { ngapLog.Errorf("Build PDUSessionResourceSetupUnsuccessfulTransfer Error: %+v\n", err) } - unsuccessfulTransfer = responseTransfer - return + return false, responseTransfer } switch securityIndication.ConfidentialityProtectionIndication.Value { @@ -804,14 +805,12 @@ func handlePDUSessionResourceSetupRequestTransfer(ue *context.N3IWFUe, pduSessio pduSession.SecurityCipher = true default: ngapLog.Error("Unknown security confidentiality indication") - success = false cause := buildCause(ngapType.CausePresentProtocol, ngapType.CauseProtocolPresentSemanticError) responseTransfer, err := ngap_message.BuildPDUSessionResourceSetupUnsuccessfulTransfer(*cause, nil) if err != nil { ngapLog.Errorf("Build PDUSessionResourceSetupUnsuccessfulTransfer Error: %+v\n", err) } - unsuccessfulTransfer = responseTransfer - return + return false, responseTransfer } } else { pduSession.SecurityIntegrity = false @@ -833,16 +832,98 @@ func handlePDUSessionResourceSetupRequestTransfer(ue *context.N3IWFUe, pduSessio // TODO: Support IPv6 upfIPv4, _ := ngapConvert.IPAddressToString(ulNGUUPTNLInformation.GTPTunnel.TransportLayerAddress) if upfIPv4 != "" { + n3iwfSelf := context.N3IWFSelf() + gtpConnection := &context.GTPConnectionInfo{ UPFIPAddr: upfIPv4, OutgoingTEID: binary.BigEndian.Uint32(ulNGUUPTNLInformation.GTPTunnel.GTPTEID.Value), } + + if userPlaneConnection, ok := n3iwfSelf.GTPConnectionWithUPFLoad(upfIPv4); ok { + // UPF UDP address + upfUDPAddr, err := net.ResolveUDPAddr("udp", upfIPv4+":2152") + if err != nil { + ngapLog.Errorf("Resolve UDP address failed: %+v", err) + cause := buildCause(ngapType.CausePresentTransport, ngapType.CauseTransportPresentTransportResourceUnavailable) + responseTransfer, err := ngap_message.BuildPDUSessionResourceSetupUnsuccessfulTransfer(*cause, nil) + if err != nil { + ngapLog.Errorf("Build PDUSessionResourceSetupUnsuccessfulTransfer Error: %+v\n", err) + } + return false, responseTransfer + } + + // UE TEID + ueTEID := n3iwfSelf.NewTEID(ue) + if ueTEID == 0 { + ngapLog.Error("Invalid TEID (0).") + cause := buildCause(ngapType.CausePresentProtocol, ngapType.CauseProtocolPresentUnspecified) + responseTransfer, err := ngap_message.BuildPDUSessionResourceSetupUnsuccessfulTransfer(*cause, nil) + if err != nil { + ngapLog.Errorf("Build PDUSessionResourceSetupUnsuccessfulTransfer Error: %+v\n", err) + } + return false, responseTransfer + } + + // Set UE associated GTP connection + gtpConnection.UPFUDPAddr = upfUDPAddr + gtpConnection.IncomingTEID = ueTEID + gtpConnection.UserPlaneConnection = userPlaneConnection + } else { + // Setup GTP connection with UPF + userPlaneConnection, upfUDPAddr, err := relay.SetupGTPTunnelWithUPF(upfIPv4) + if err != nil { + ngapLog.Errorf("Setup GTP connection with UPF failed: %+v", err) + cause := buildCause(ngapType.CausePresentTransport, ngapType.CauseTransportPresentTransportResourceUnavailable) + responseTransfer, err := ngap_message.BuildPDUSessionResourceSetupUnsuccessfulTransfer(*cause, nil) + if err != nil { + ngapLog.Errorf("Build PDUSessionResourceSetupUnsuccessfulTransfer Error: %+v\n", err) + } + return false, responseTransfer + } + // Listen GTP tunnel + if err := relay.ListenGTP(userPlaneConnection); err != nil { + ngapLog.Errorf("Listening GTP tunnel failed: %+v", err) + cause := buildCause(ngapType.CausePresentTransport, ngapType.CauseTransportPresentTransportResourceUnavailable) + responseTransfer, err := ngap_message.BuildPDUSessionResourceSetupUnsuccessfulTransfer(*cause, nil) + if err != nil { + ngapLog.Errorf("Build PDUSessionResourceSetupUnsuccessfulTransfer Error: %+v\n", err) + } + return false, responseTransfer + } + + // UE TEID + ueTEID := n3iwfSelf.NewTEID(ue) + if ueTEID == 0 { + ngapLog.Error("Invalid TEID (0).") + cause := buildCause(ngapType.CausePresentProtocol, ngapType.CauseProtocolPresentUnspecified) + responseTransfer, err := ngap_message.BuildPDUSessionResourceSetupUnsuccessfulTransfer(*cause, nil) + if err != nil { + ngapLog.Errorf("Build PDUSessionResourceSetupUnsuccessfulTransfer Error: %+v\n", err) + } + return false, responseTransfer + } + + // Setup GTP connection with UPF + gtpConnection.UPFUDPAddr = upfUDPAddr + gtpConnection.IncomingTEID = ueTEID + gtpConnection.UserPlaneConnection = userPlaneConnection + + // Store GTP connection with UPF into N3IWF context + n3iwfSelf.GTPConnectionWithUPFStore(upfIPv4, userPlaneConnection) + } + pduSession.GTPConnection = gtpConnection + } else { + ngapLog.Error("Cannot parse \"PDU session resource setup request transfer\" message \"UL NG-U UP TNL Information\"") + cause := buildCause(ngapType.CausePresentProtocol, ngapType.CauseProtocolPresentAbstractSyntaxErrorReject) + responseTransfer, err := ngap_message.BuildPDUSessionResourceSetupUnsuccessfulTransfer(*cause, nil) + if err != nil { + ngapLog.Errorf("Build PDUSessionResourceSetupUnsuccessfulTransfer Error: %+v\n", err) + } + return false, responseTransfer } - success = true - unsuccessfulTransfer = nil - return + return true, nil } func HandleUEContextModificationRequest(amf *context.N3IWFAMF, message *ngapType.NGAPPDU) { @@ -1075,7 +1156,7 @@ func HandleDownlinkNASTransport(amf *context.N3IWFAMF, message *ngapType.NGAPPDU var iesCriticalityDiagnostics ngapType.CriticalityDiagnosticsIEList var n3iwfUe *context.N3IWFUe - var n3iwfSelf = context.N3IWFSelf() + var n3iwfSelf *context.N3IWFContext = context.N3IWFSelf() if message == nil { ngapLog.Error("NGAP Message is nil") @@ -1152,7 +1233,6 @@ func HandleDownlinkNASTransport(amf *context.N3IWFAMF, message *ngapType.NGAPPDU if n3iwfUe.AmfUeNgapId == context.AmfUeNgapIdUnspecified { ngapLog.Tracef("Create new logical UE-associated NG-connection") n3iwfUe.AmfUeNgapId = amfUeNgapID.Value - // n3iwfUe.SCTPAddr = amf.SCTPAddr } else { if n3iwfUe.AmfUeNgapId != amfUeNgapID.Value { ngapLog.Warn("AMFUENGAPID unmatched") @@ -1213,10 +1293,12 @@ func HandleDownlinkNASTransport(amf *context.N3IWFAMF, message *ngapType.NGAPPDU handler.SendIKEMessageToUE(n3iwfUe.IKEConnection.Conn, n3iwfUe.IKEConnection.N3IWFAddr, n3iwfUe.IKEConnection.UEAddr, responseIKEMessage) } else { // Check ue.TCPConnection. If failed, retry 2 times. - for i := 0; i < 3; i++ { + maxRetryTimes := 3 + for i := 0; i < maxRetryTimes; i++ { if n3iwfUe.TCPConnection == nil { - if i == 2 { - ngapLog.Warn("No connection found for UE to send NAS message.") + if i == (maxRetryTimes - 1) { + ngapLog.Warn("No connection found for UE to send NAS message. This message will be cached in N3IWF") + n3iwfUe.TemporaryCachedNASMessage = nasPDU.Value return } else { ngapLog.Warn("No NAS signalling session found, retry...") diff --git a/ngap/service/service.go b/ngap/service/service.go index 7dbcc9a..186f8da 100644 --- a/ngap/service/service.go +++ b/ngap/service/service.go @@ -118,7 +118,10 @@ func listenAndServe(localAddr, remoteAddr *sctp.SCTPAddr, errChan chan<- error) continue } - go ngap.Dispatch(conn, data[:n]) + forwardData := make([]byte, n) + copy(forwardData, data[:n]) + + go ngap.Dispatch(conn, forwardData) } } } diff --git a/relay/relay.go b/relay/relay.go index 005da32..e72e164 100644 --- a/relay/relay.go +++ b/relay/relay.go @@ -53,7 +53,7 @@ func ListenN1UPTraffic() error { func listenRawSocket(rawSocket *ipv4.RawConn) { defer rawSocket.Close() - buffer := make([]byte, 1500) + buffer := make([]byte, 65535) for { ipHeader, ipPayload, _, err := rawSocket.ReadFrom(buffer) @@ -63,7 +63,10 @@ func listenRawSocket(rawSocket *ipv4.RawConn) { return } - go ForwardUPTrafficFromN1(ipHeader.Src.String(), ipPayload[4:]) + forwardData := make([]byte, len(ipPayload[4:])) + copy(forwardData, ipPayload[4:]) + + go ForwardUPTrafficFromN1(ipHeader.Src.String(), forwardData) } } @@ -154,7 +157,7 @@ func ListenGTP(userPlaneConnection *gtpv1.UPlaneConn) error { func listenGTP(userPlaneConnection *gtpv1.UPlaneConn) { defer userPlaneConnection.Close() - payload := make([]byte, 1500) + payload := make([]byte, 65535) for { n, _, teid, err := userPlaneConnection.ReadFromGTP(payload) @@ -164,7 +167,10 @@ func listenGTP(userPlaneConnection *gtpv1.UPlaneConn) { return } - go ForwardUPTrafficFromN3(teid, payload[:n]) + forwardData := make([]byte, n) + copy(forwardData, payload[:n]) + + go ForwardUPTrafficFromN3(teid, forwardData) } } @@ -219,6 +225,8 @@ func ForwardUPTrafficFromN3(ueTEID uint32, packet []byte) { } } +// SetupNASTCPServer setup N3IWF NAS for UE to forward NAS message +// to AMF func SetupNASTCPServer() error { // N3IWF context n3iwfSelf := n3iwf_context.N3IWFSelf() @@ -230,6 +238,8 @@ func SetupNASTCPServer() error { return errors.New("Listen failed") } + relayLog.Tracef("Successfully listen %+v", tcpAddr) + go tcpServerListen(tcpListener) return nil @@ -245,23 +255,40 @@ func tcpServerListen(tcpListener net.Listener) { return } - go tcpConnectionHandler(connection) - } -} + relayLog.Tracef("Accepted one UE from %+v", connection.RemoteAddr()) -func tcpConnectionHandler(connection net.Conn) { - defer connection.Close() + // Find UE context and store this connection in to it, then check if + // there is any cached NAS message for this UE. If yes, send to it. + n3iwfSelf := n3iwf_context.N3IWFSelf() - // N3IWF context - n3iwfSelf := n3iwf_context.N3IWFSelf() + ueIP := strings.Split(connection.RemoteAddr().String(), ":")[0] + ue, ok := n3iwfSelf.AllocatedUEIPAddressLoad(ueIP) + if !ok { + relayLog.Errorf("UE context not found for peer %+v", ueIP) + continue + } - ueIP := strings.Split(connection.RemoteAddr().String(), ":")[0] - ue, ok := n3iwfSelf.AllocatedUEIPAddressLoad(ueIP) - if !ok { - relayLog.Error("UE context not found") - return + // Store connection + ue.TCPConnection = connection + + if ue.TemporaryCachedNASMessage != nil { + // Send to UE + if n, err := connection.Write(ue.TemporaryCachedNASMessage); err != nil { + relayLog.Errorf("Writing via IPSec signalling SA failed: %+v", err) + } else { + relayLog.Trace("Forward N1 <- N2") + relayLog.Tracef("Wrote %d bytes", n) + } + // Clean the cached message + ue.TemporaryCachedNASMessage = nil + } + + go tcpConnectionHandler(ue, connection) } - ue.TCPConnection = connection +} + +func tcpConnectionHandler(ue *n3iwf_context.N3IWFUe, connection net.Conn) { + defer connection.Close() data := make([]byte, 65535) for { @@ -275,13 +302,17 @@ func tcpConnectionHandler(connection net.Conn) { relayLog.Errorf("Read TCP connection failed: %+v", err) } } - relayLog.Tracef("Get NAS PDU from UE:\nNAS length: %d\nNAS content:\n%s", n, hex.Dump(data[:n])) - go ForwardCPTrafficFromN1(ue, data[:n]) + forwardData := make([]byte, n) + copy(forwardData, data[:n]) + + go ForwardCPTrafficFromN1(ue, forwardData) } } +// ForwardCPTrafficFromN1 forward NAS message sent from UE to the +// associated AMF func ForwardCPTrafficFromN1(ue *n3iwf_context.N3IWFUe, packet []byte) { relayLog.Trace("Forward N1 -> N2") ngap_message.SendUplinkNASTransport(ue.AMF, ue, packet)