Skip to content

Commit

Permalink
Refactor NAT-D related code
Browse files Browse the repository at this point in the history
  • Loading branch information
Allen00991 committed Oct 21, 2024
1 parent c01f7e0 commit 40dad3f
Show file tree
Hide file tree
Showing 3 changed files with 113 additions and 74 deletions.
145 changes: 99 additions & 46 deletions pkg/ike/handler/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"math/big"
"net"

"github.com/pkg/errors"
"github.com/sirupsen/logrus"
"golang.org/x/sys/unix"

Expand Down Expand Up @@ -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")
Expand All @@ -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() {
Expand Down Expand Up @@ -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
}
}

Expand All @@ -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)
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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
}
32 changes: 9 additions & 23 deletions pkg/ike/handler/send.go
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
10 changes: 5 additions & 5 deletions pkg/ike/service/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -54,15 +53,15 @@ 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")
}

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")
Expand All @@ -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.
Expand All @@ -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 {
Expand Down Expand Up @@ -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)
Expand Down

0 comments on commit 40dad3f

Please sign in to comment.