From 40dad3f56fcbcb60ba9483c0daf9aa65d21ae065 Mon Sep 17 00:00:00 2001 From: Allen00991 Date: Mon, 21 Oct 2024 08:33:57 +0000 Subject: [PATCH] Refactor NAT-D related code --- pkg/ike/handler/handler.go | 145 +++++++++++++++++++++++++------------ pkg/ike/handler/send.go | 32 +++----- pkg/ike/service/service.go | 10 +-- 3 files changed, 113 insertions(+), 74 deletions(-) diff --git a/pkg/ike/handler/handler.go b/pkg/ike/handler/handler.go index 2338400..61b9ab5 100644 --- a/pkg/ike/handler/handler.go +++ b/pkg/ike/handler/handler.go @@ -8,6 +8,7 @@ import ( "math/big" "net" + "github.com/pkg/errors" "github.com/sirupsen/logrus" "golang.org/x/sys/unix" @@ -52,7 +53,7 @@ const ( func HandleIKESAINIT( udpConn *net.UDPConn, - n3iwfAddr, ueAddr *net.UDPAddr, + ueAddr, n3iwfAddr *net.UDPAddr, message *ike_message.IKEMessage, ) { ikeLog.Infoln("Handle IKESA INIT") @@ -62,6 +63,7 @@ func HandleIKESAINIT( var notifications []*ike_message.Notification // For NAT-T var ueIsBehindNAT, n3iwfIsBehindNAT bool + var err error for _, ikePayload := range message.Payloads { switch ikePayload.Type() { @@ -89,50 +91,12 @@ func HandleIKESAINIT( } if len(notifications) != 0 { - for _, notification := range notifications { - switch notification.NotifyMessageType { - case ike_message.NAT_DETECTION_SOURCE_IP: - ikeLog.Trace("Received IKE Notify: NAT_DETECTION_SOURCE_IP") - // Calculate local NAT_DETECTION_SOURCE_IP hash - // : sha1(ispi | rspi | ueip | ueport) - localDetectionData := make([]byte, 22) - binary.BigEndian.PutUint64(localDetectionData[0:8], message.InitiatorSPI) - binary.BigEndian.PutUint64(localDetectionData[8:16], message.ResponderSPI) - copy(localDetectionData[16:20], ueAddr.IP.To4()) - binary.BigEndian.PutUint16(localDetectionData[20:22], uint16(ueAddr.Port)) - - sha1HashFunction := sha1.New() // #nosec G401 - if _, err := sha1HashFunction.Write(localDetectionData); err != nil { - ikeLog.Errorf("Hash function write error: %+v", err) - return - } - - if !bytes.Equal(notification.NotificationData, sha1HashFunction.Sum(nil)) { - ikeLog.Printf("ue is behind nat") - ueIsBehindNAT = true - } - case ike_message.NAT_DETECTION_DESTINATION_IP: - ikeLog.Trace("Received IKE Notify: NAT_DETECTION_DESTINATION_IP") - // Calculate local NAT_DETECTION_SOURCE_IP hash - // : sha1(ispi | rspi | n3iwfip | n3iwfport) - localDetectionData := make([]byte, 22) - binary.BigEndian.PutUint64(localDetectionData[0:8], message.InitiatorSPI) - binary.BigEndian.PutUint64(localDetectionData[8:16], message.ResponderSPI) - copy(localDetectionData[16:20], n3iwfAddr.IP.To4()) - binary.BigEndian.PutUint16(localDetectionData[20:22], uint16(n3iwfAddr.Port)) - - sha1HashFunction := sha1.New() // #nosec G401 - if _, err := sha1HashFunction.Write(localDetectionData); err != nil { - ikeLog.Errorf("Hash function write error: %+v", err) - return - } - - if !bytes.Equal(notification.NotificationData, sha1HashFunction.Sum(nil)) { - ikeLog.Printf("n3iwf is behind nat") - n3iwfIsBehindNAT = true - } - default: - } + ueIsBehindNAT, n3iwfIsBehindNAT, err = HandleNATDetect( + message.InitiatorSPI, message.ResponderSPI, + notifications, ueAddr, n3iwfAddr) + if err != nil { + ikeLog.Errorf("Handle IKE_SA_INIT: %v", err) + return } } @@ -154,7 +118,7 @@ func HandleIKESAINIT( N3IWFIsBehindNAT: n3iwfIsBehindNAT, } - err := ikeSecurityAssociation.IKESAKey.GenerateKeyForIKESA(ikeSecurityAssociation.ConcatenatedNonce, + err = ikeSecurityAssociation.IKESAKey.GenerateKeyForIKESA(ikeSecurityAssociation.ConcatenatedNonce, sharedKeyExchangeData, ikeSecurityAssociation.LocalSPI, ikeSecurityAssociation.RemoteSPI) if err != nil { ikeLog.Errorf("Generate key for IKE SA failed: %+v", err) @@ -797,6 +761,13 @@ func HandleCREATECHILDSA( return } + // NAT-T concern + if ikeSecurityAssociation.UEIsBehindNAT || ikeSecurityAssociation.N3IWFIsBehindNAT { + childSecurityAssociationContextUserPlane.EnableEncapsulate = true + childSecurityAssociationContextUserPlane.N3IWFPort = n3iwfAddr.Port + childSecurityAssociationContextUserPlane.NATPort = ueAddr.Port + } + n3ueSelf.N3ueInfo.XfrmiId++ // Aplly XFRM rules if err = xfrm.ApplyXFRMRule(false, n3ueSelf.N3ueInfo.XfrmiId, childSecurityAssociationContextUserPlane); err != nil { @@ -887,3 +858,85 @@ func HandleInformational( ikeLog.Warnf("Unimplemented informational message") } } + +func HandleNATDetect( + initiatorSPI, responderSPI uint64, + notifications []*ike_message.Notification, + ueAddr, n3iwfAddr *net.UDPAddr, +) (bool, bool, error) { + ueBehindNAT := false + n3iwfBehindNAT := false + + srcNatDData, err := GenerateNATDetectHash(initiatorSPI, responderSPI, n3iwfAddr) + if err != nil { + return false, false, errors.Wrapf(err, "handle NATD") + } + + dstNatDData, err := GenerateNATDetectHash(initiatorSPI, responderSPI, ueAddr) + if err != nil { + return false, false, errors.Wrapf(err, "handle NATD") + } + + for _, notification := range notifications { + switch notification.NotifyMessageType { + case ike_message.NAT_DETECTION_SOURCE_IP: + ikeLog.Tracef("Received IKE Notify: NAT_DETECTION_SOURCE_IP") + if !bytes.Equal(notification.NotificationData, srcNatDData) { + ikeLog.Tracef("N3IWF is behind NAT") + n3iwfBehindNAT = true + } + case ike_message.NAT_DETECTION_DESTINATION_IP: + ikeLog.Tracef("Received IKE Notify: NAT_DETECTION_DESTINATION_IP") + if !bytes.Equal(notification.NotificationData, dstNatDData) { + ikeLog.Tracef("UE(SPI: %016x) is behind NAT", responderSPI) + ueBehindNAT = true + } + default: + } + } + return ueBehindNAT, n3iwfBehindNAT, nil +} + +func BuildNATDetectNotifPayload( + localSPI uint64, remoteSPI uint64, + payload *ike_message.IKEPayloadContainer, + ueAddr, n3iwfAddr *net.UDPAddr, +) error { + srcNatDHash, err := GenerateNATDetectHash(localSPI, remoteSPI, ueAddr) + if err != nil { + return errors.Wrapf(err, "build NATD") + } + // Build and append notify payload for NAT_DETECTION_SOURCE_IP + payload.BuildNotification( + ike_message.TypeNone, ike_message.NAT_DETECTION_SOURCE_IP, nil, srcNatDHash) + + dstNatDHash, err := GenerateNATDetectHash(localSPI, remoteSPI, n3iwfAddr) + if err != nil { + return errors.Wrapf(err, "build NATD") + } + // Build and append notify payload for NAT_DETECTION_DESTINATION_IP + payload.BuildNotification( + ike_message.TypeNone, ike_message.NAT_DETECTION_DESTINATION_IP, nil, dstNatDHash) + + return nil +} + +func GenerateNATDetectHash( + initiatorSPI, responderSPI uint64, + addr *net.UDPAddr, +) ([]byte, error) { + // Calculate NAT_DETECTION hash for NAT-T + // : sha1(ispi | rspi | ip | port) + natdData := make([]byte, 22) + binary.BigEndian.PutUint64(natdData[0:8], initiatorSPI) + binary.BigEndian.PutUint64(natdData[8:16], responderSPI) + copy(natdData[16:20], addr.IP.To4()) + binary.BigEndian.PutUint16(natdData[20:22], uint16(addr.Port)) // #nosec G115 + + sha1HashFunction := sha1.New() // #nosec G401 + _, err := sha1HashFunction.Write(natdData) + if err != nil { + return nil, errors.Wrapf(err, "generate NATD Hash") + } + return sha1HashFunction.Sum(nil), nil +} diff --git a/pkg/ike/handler/send.go b/pkg/ike/handler/send.go index 7b592ca..3f24cc5 100644 --- a/pkg/ike/handler/send.go +++ b/pkg/ike/handler/send.go @@ -91,29 +91,15 @@ func SendIKESAINIT() { ikeMessage := ike_message.NewMessage(n3ueContext.IkeInitiatorSPI, 0, ike_message.IKE_SA_INIT, false, true, 0, *payload) - conn := n3ueContext.IKEConnection[4500] - - // Calculate NAT_DETECTION_SOURCE_IP for NAT-T - natDetectionSourceIP := make([]byte, 22) - binary.BigEndian.PutUint64(natDetectionSourceIP[0:8], n3ueContext.IkeInitiatorSPI) - binary.BigEndian.PutUint64(natDetectionSourceIP[8:16], 0) - copy(natDetectionSourceIP[16:20], conn.UEAddr.IP.To4()) - binary.BigEndian.PutUint16(natDetectionSourceIP[20:22], uint16(conn.UEAddr.Port)) - - // Build and append notify payload for NAT_DETECTION_SOURCE_IP - ikeMessage.Payloads.BuildNotification( - ike_message.TypeNone, ike_message.NAT_DETECTION_SOURCE_IP, nil, natDetectionSourceIP) - - // Calculate NAT_DETECTION_DESTINATION_IP for NAT-T - natDetectionDestinationIP := make([]byte, 22) - binary.BigEndian.PutUint64(natDetectionDestinationIP[0:8], n3ueContext.IkeInitiatorSPI) - binary.BigEndian.PutUint64(natDetectionDestinationIP[8:16], 0) - copy(natDetectionDestinationIP[16:20], conn.N3IWFAddr.IP.To4()) - binary.BigEndian.PutUint16(natDetectionDestinationIP[20:22], uint16(conn.N3IWFAddr.Port)) - - // Build and append notify payload for NAT_DETECTION_DESTINATION_IP - ikeMessage.Payloads.BuildNotification( - ike_message.TypeNone, ike_message.NAT_DETECTION_DESTINATION_IP, nil, natDetectionDestinationIP) + n3ueContext.N3IWFUe.IKEConnection = n3ueContext.IKEConnection[500] + + err = BuildNATDetectNotifPayload(n3ueContext.IkeInitiatorSPI, 0, &ikeMessage.Payloads, + n3ueContext.N3IWFUe.IKEConnection.UEAddr, + n3ueContext.N3IWFUe.IKEConnection.N3IWFAddr) + if err != nil { + ikeLog.Errorf("SendIKESAINIT(): %v", err) + return + } // Send to n3iwf err = SendIKEMessageToN3IWF(n3ueContext.N3IWFUe.IKEConnection.Conn, diff --git a/pkg/ike/service/service.go b/pkg/ike/service/service.go index 7d5f423..e24c03c 100644 --- a/pkg/ike/service/service.go +++ b/pkg/ike/service/service.go @@ -12,7 +12,6 @@ import ( "github.com/google/gopacket" "github.com/google/gopacket/layers" "github.com/pkg/errors" - "github.com/sirupsen/logrus" "github.com/free5gc/ike" @@ -54,7 +53,7 @@ func Run() error { // Listen and serve errChan := make(chan error) - go listenAndServe(ikeAddrPort, 500, errChan) + go listenAndServe(ikeAddrPort, errChan) if err, ok := <-errChan; ok { ikeLog.Errorln(err) return errors.New("IKE service 500 port run failed") @@ -62,7 +61,7 @@ func Run() error { errChan = make(chan error) - go listenAndServe(nattAddrPort, 4500, errChan) + go listenAndServe(nattAddrPort, errChan) if err, ok := <-errChan; ok { ikeLog.Errorln(err) return errors.New("IKE service 4500 port run failed") @@ -71,7 +70,7 @@ func Run() error { return nil } -func listenAndServe(localAddr *net.UDPAddr, port int, errChan chan<- error) { +func listenAndServe(localAddr *net.UDPAddr, errChan chan<- error) { defer func() { if p := recover(); p != nil { // Print stack for panic to log. Fatalf() will let program exit. @@ -87,6 +86,7 @@ func listenAndServe(localAddr *net.UDPAddr, port int, errChan chan<- error) { } n3ueContext := context.N3UESelf() + port := localAddr.Port n3iwfUDPAddr, err := net.ResolveUDPAddr("udp", factory.N3iwfInfo.IPSecIfaceAddr+":"+strconv.Itoa(port)) if err != nil { @@ -124,7 +124,7 @@ func listenAndServe(localAddr *net.UDPAddr, port int, errChan chan<- error) { forwardData := make([]byte, n) copy(forwardData, data[:n]) - if localAddr.Port == DEFAULT_NATT_PORT { + if port == DEFAULT_NATT_PORT { forwardData, err = handleNattMsg(forwardData, remoteAddr, localAddr, handleESPPacket) if err != nil { ikeLog.Errorf("Handle NATT msg: %v", err)