Skip to content

Commit

Permalink
Feature/support nat t (#12)
Browse files Browse the repository at this point in the history
* Add IKE NAT-T support

* Refactor NAT-D related code

---------

Co-authored-by: 123 <123>
Co-authored-by: Allen00991 <[email protected]>
  • Loading branch information
allen0091 and Allen00991 authored Oct 22, 2024
1 parent d6f17dd commit abda2be
Show file tree
Hide file tree
Showing 9 changed files with 299 additions and 20 deletions.
2 changes: 1 addition & 1 deletion config/n3ue.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ configuration:
N3UEInformation:
VisitedPLMN: # Optional
MCC: 208 # Mobile Country Code (3 digits string, digit: 0~9)
MNC: 93 # Mobile Network Code (2 or 3 digits string, digit: 0~9)
MNC: 93 # Mobile Network Code (2 or 3 digits string, digit: 0~9)
IMSI:
PLMNID: # Public Land Mobile Network ID
MCC: 208 # Mobile Country Code (3 digits string, digit: 0~9)
Expand Down
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ require (
github.com/free5gc/openapi v1.0.8
github.com/free5gc/util v1.0.7-0.20241017071924-da29aef99a1c
github.com/go-ping/ping v0.0.0-20211014180314-6e2b003bffdd
github.com/google/gopacket v1.1.19
github.com/pkg/errors v0.9.1
github.com/sirupsen/logrus v1.8.1
github.com/vishvananda/netlink v1.1.0
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,8 @@ github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/gopacket v1.1.19 h1:ves8RnFZPGiFnTS0uPQStjwru6uO6h+nlr9j6fL7kF8=
github.com/google/gopacket v1.1.19/go.mod h1:iJ8V8n6KS+z2U1A8pUwu8bW5SyEMkXJB8Yo/Vo+TKTo=
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
Expand Down
1 change: 1 addition & 0 deletions internal/util/initContext.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ func InitN3UEContext() {
Len: uint16(len(suci)),
Buffer: suci,
}
n3ueContext.IKEConnection = make(map[int]*context.UDPSocketInfo)
}

func getAuthSubscription() (authSubs models.AuthenticationSubscription) {
Expand Down
5 changes: 5 additions & 0 deletions pkg/context/context.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ type N3UE struct {
CurrentState chan uint8
Kn3iwf []uint8
GUTI *nasType.GUTI5G
IKEConnection map[int]*UDPSocketInfo

// Temporary data , used to create GreTunnel
TemporaryXfrmiName string
Expand Down Expand Up @@ -140,6 +141,10 @@ type IKESecurityAssociation struct {

// Temporary data stored for the use in later exchange
IKEAuthResponseSA *message.SecurityAssociation

// NAT detection
UEIsBehindNAT bool
N3IWFIsBehindNAT bool
}

func (ikeSA *IKESecurityAssociation) String() string {
Expand Down
127 changes: 122 additions & 5 deletions pkg/ike/handler/handler.go
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
package handler

import (
"bytes"
"crypto/sha1"
"encoding/binary"
"fmt"
"math/big"
"net"

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

Expand Down Expand Up @@ -50,13 +53,17 @@ const (

func HandleIKESAINIT(
udpConn *net.UDPConn,
n3iwfAddr, ueAddr *net.UDPAddr,
ueAddr, n3iwfAddr *net.UDPAddr,
message *ike_message.IKEMessage,
) {
ikeLog.Infoln("Handle IKESA INIT")

var sharedKeyExchangeData []byte
var remoteNonce []byte
var notifications []*ike_message.Notification
// For NAT-T
var ueIsBehindNAT, n3iwfIsBehindNAT bool
var err error

for _, ikePayload := range message.Payloads {
switch ikePayload.Type() {
Expand All @@ -78,6 +85,18 @@ func HandleIKESAINIT(
Bytes()
case ike_message.TypeNiNr:
remoteNonce = ikePayload.(*ike_message.Nonce).NonceData
case ike_message.TypeN:
notifications = append(notifications, ikePayload.(*ike_message.Notification))
}
}

if len(notifications) != 0 {
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 @@ -95,9 +114,11 @@ func HandleIKESAINIT(
ConcatenatedNonce: append(n3ueSelf.LocalNonce, remoteNonce...),
ResponderSignedOctets: append(n3ueSelf.N3IWFUe.N3IWFIKESecurityAssociation.
ResponderSignedOctets, remoteNonce...),
UEIsBehindNAT: ueIsBehindNAT,
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 All @@ -111,7 +132,7 @@ func HandleIKESAINIT(

func HandleIKEAUTH(
udpConn *net.UDPConn,
n3iwfAddr, ueAddr *net.UDPAddr,
ueAddr, n3iwfAddr *net.UDPAddr,
message *ike_message.IKEMessage,
) {
ikeLog.Infoln("Handle IKE AUTH")
Expand Down Expand Up @@ -581,6 +602,13 @@ func HandleIKEAUTH(
childSecurityAssociationContext.InitiatorToResponderIntegrityKey,
)

// NAT-T concern
if ikeSecurityAssociation.UEIsBehindNAT || ikeSecurityAssociation.N3IWFIsBehindNAT {
childSecurityAssociationContext.EnableEncapsulate = true
childSecurityAssociationContext.N3IWFPort = n3iwfAddr.Port
childSecurityAssociationContext.NATPort = ueAddr.Port
}

// Setup interface for ipsec
newXfrmiName := fmt.Sprintf("%s-%d", n3ueSelf.N3ueInfo.XfrmiName, n3ueSelf.N3ueInfo.XfrmiId)
if _, err = xfrm.SetupIPsecXfrmi(newXfrmiName,
Expand All @@ -603,7 +631,7 @@ func HandleIKEAUTH(

func HandleCREATECHILDSA(
udpConn *net.UDPConn,
n3iwfAddr, ueAddr *net.UDPAddr,
ueAddr, n3iwfAddr *net.UDPAddr,
message *ike_message.IKEMessage,
) {
ikeLog.Tracef("Handle CreateChildSA")
Expand Down Expand Up @@ -733,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 @@ -809,7 +844,7 @@ func HandleCREATECHILDSA(

func HandleInformational(
udpConn *net.UDPConn,
n3iwfAddr, ueAddr *net.UDPAddr,
ueAddr, n3iwfAddr *net.UDPAddr,
message *ike_message.IKEMessage,
) {
ikeLog.Infoln("Handle Informational")
Expand All @@ -823,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
}
22 changes: 18 additions & 4 deletions pkg/ike/handler/send.go
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,16 @@ func SendIKESAINIT() {
ikeMessage := ike_message.NewMessage(n3ueContext.IkeInitiatorSPI, 0,
ike_message.IKE_SA_INIT, false, true, 0, *payload)

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,
n3ueContext.N3IWFUe.IKEConnection.UEAddr,
Expand All @@ -116,8 +126,9 @@ func SendIKEAUTH() {
ikeLog.Tracef("IKE_AUTH message")

n3ueContext := context.N3UESelf()
ikeSA := n3ueContext.N3IWFUe.N3IWFIKESecurityAssociation

n3ueContext.N3IWFUe.N3IWFIKESecurityAssociation.InitiatorMessageID++
ikeSA.InitiatorMessageID++

var ikePayload ike_message.IKEPayloadContainer

Expand Down Expand Up @@ -171,13 +182,16 @@ func SendIKEAUTH() {
)

ikeMessage := ike_message.NewMessage(
n3ueContext.N3IWFUe.N3IWFIKESecurityAssociation.LocalSPI,
n3ueContext.N3IWFUe.N3IWFIKESecurityAssociation.RemoteSPI,
ikeSA.LocalSPI, ikeSA.RemoteSPI,
ike_message.IKE_AUTH, false, true,
n3ueContext.N3IWFUe.N3IWFIKESecurityAssociation.InitiatorMessageID,
ikeSA.InitiatorMessageID,
ikePayload,
)

if ikeSA.UEIsBehindNAT || ikeSA.N3IWFIsBehindNAT {
n3ueContext.N3IWFUe.IKEConnection = n3ueContext.IKEConnection[4500]
}

err := SendIKEMessageToN3IWF(n3ueContext.N3IWFUe.IKEConnection.Conn, n3ueContext.N3IWFUe.IKEConnection.UEAddr,
n3ueContext.N3IWFUe.IKEConnection.N3IWFAddr, ikeMessage,
n3ueContext.N3IWFUe.N3IWFIKESecurityAssociation.IKESAKey)
Expand Down
Loading

0 comments on commit abda2be

Please sign in to comment.