Skip to content

Commit abda2be

Browse files
allen0091Allen00991
andauthored
Feature/support nat t (#12)
* Add IKE NAT-T support * Refactor NAT-D related code --------- Co-authored-by: 123 <123> Co-authored-by: Allen00991 <[email protected]>
1 parent d6f17dd commit abda2be

File tree

9 files changed

+299
-20
lines changed

9 files changed

+299
-20
lines changed

config/n3ue.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ configuration:
88
N3UEInformation:
99
VisitedPLMN: # Optional
1010
MCC: 208 # Mobile Country Code (3 digits string, digit: 0~9)
11-
MNC: 93 # Mobile Network Code (2 or 3 digits string, digit: 0~9)
11+
MNC: 93 # Mobile Network Code (2 or 3 digits string, digit: 0~9)
1212
IMSI:
1313
PLMNID: # Public Land Mobile Network ID
1414
MCC: 208 # Mobile Country Code (3 digits string, digit: 0~9)

go.mod

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ require (
1414
github.com/free5gc/openapi v1.0.8
1515
github.com/free5gc/util v1.0.7-0.20241017071924-da29aef99a1c
1616
github.com/go-ping/ping v0.0.0-20211014180314-6e2b003bffdd
17+
github.com/google/gopacket v1.1.19
1718
github.com/pkg/errors v0.9.1
1819
github.com/sirupsen/logrus v1.8.1
1920
github.com/vishvananda/netlink v1.1.0

go.sum

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,8 @@ github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
135135
github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
136136
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
137137
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
138+
github.com/google/gopacket v1.1.19 h1:ves8RnFZPGiFnTS0uPQStjwru6uO6h+nlr9j6fL7kF8=
139+
github.com/google/gopacket v1.1.19/go.mod h1:iJ8V8n6KS+z2U1A8pUwu8bW5SyEMkXJB8Yo/Vo+TKTo=
138140
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
139141
github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
140142
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=

internal/util/initContext.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ func InitN3UEContext() {
5050
Len: uint16(len(suci)),
5151
Buffer: suci,
5252
}
53+
n3ueContext.IKEConnection = make(map[int]*context.UDPSocketInfo)
5354
}
5455

5556
func getAuthSubscription() (authSubs models.AuthenticationSubscription) {

pkg/context/context.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ type N3UE struct {
5151
CurrentState chan uint8
5252
Kn3iwf []uint8
5353
GUTI *nasType.GUTI5G
54+
IKEConnection map[int]*UDPSocketInfo
5455

5556
// Temporary data , used to create GreTunnel
5657
TemporaryXfrmiName string
@@ -140,6 +141,10 @@ type IKESecurityAssociation struct {
140141

141142
// Temporary data stored for the use in later exchange
142143
IKEAuthResponseSA *message.SecurityAssociation
144+
145+
// NAT detection
146+
UEIsBehindNAT bool
147+
N3IWFIsBehindNAT bool
143148
}
144149

145150
func (ikeSA *IKESecurityAssociation) String() string {

pkg/ike/handler/handler.go

Lines changed: 122 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,14 @@
11
package handler
22

33
import (
4+
"bytes"
5+
"crypto/sha1"
46
"encoding/binary"
57
"fmt"
68
"math/big"
79
"net"
810

11+
"github.com/pkg/errors"
912
"github.com/sirupsen/logrus"
1013
"golang.org/x/sys/unix"
1114

@@ -50,13 +53,17 @@ const (
5053

5154
func HandleIKESAINIT(
5255
udpConn *net.UDPConn,
53-
n3iwfAddr, ueAddr *net.UDPAddr,
56+
ueAddr, n3iwfAddr *net.UDPAddr,
5457
message *ike_message.IKEMessage,
5558
) {
5659
ikeLog.Infoln("Handle IKESA INIT")
5760

5861
var sharedKeyExchangeData []byte
5962
var remoteNonce []byte
63+
var notifications []*ike_message.Notification
64+
// For NAT-T
65+
var ueIsBehindNAT, n3iwfIsBehindNAT bool
66+
var err error
6067

6168
for _, ikePayload := range message.Payloads {
6269
switch ikePayload.Type() {
@@ -78,6 +85,18 @@ func HandleIKESAINIT(
7885
Bytes()
7986
case ike_message.TypeNiNr:
8087
remoteNonce = ikePayload.(*ike_message.Nonce).NonceData
88+
case ike_message.TypeN:
89+
notifications = append(notifications, ikePayload.(*ike_message.Notification))
90+
}
91+
}
92+
93+
if len(notifications) != 0 {
94+
ueIsBehindNAT, n3iwfIsBehindNAT, err = HandleNATDetect(
95+
message.InitiatorSPI, message.ResponderSPI,
96+
notifications, ueAddr, n3iwfAddr)
97+
if err != nil {
98+
ikeLog.Errorf("Handle IKE_SA_INIT: %v", err)
99+
return
81100
}
82101
}
83102

@@ -95,9 +114,11 @@ func HandleIKESAINIT(
95114
ConcatenatedNonce: append(n3ueSelf.LocalNonce, remoteNonce...),
96115
ResponderSignedOctets: append(n3ueSelf.N3IWFUe.N3IWFIKESecurityAssociation.
97116
ResponderSignedOctets, remoteNonce...),
117+
UEIsBehindNAT: ueIsBehindNAT,
118+
N3IWFIsBehindNAT: n3iwfIsBehindNAT,
98119
}
99120

100-
err := ikeSecurityAssociation.IKESAKey.GenerateKeyForIKESA(ikeSecurityAssociation.ConcatenatedNonce,
121+
err = ikeSecurityAssociation.IKESAKey.GenerateKeyForIKESA(ikeSecurityAssociation.ConcatenatedNonce,
101122
sharedKeyExchangeData, ikeSecurityAssociation.LocalSPI, ikeSecurityAssociation.RemoteSPI)
102123
if err != nil {
103124
ikeLog.Errorf("Generate key for IKE SA failed: %+v", err)
@@ -111,7 +132,7 @@ func HandleIKESAINIT(
111132

112133
func HandleIKEAUTH(
113134
udpConn *net.UDPConn,
114-
n3iwfAddr, ueAddr *net.UDPAddr,
135+
ueAddr, n3iwfAddr *net.UDPAddr,
115136
message *ike_message.IKEMessage,
116137
) {
117138
ikeLog.Infoln("Handle IKE AUTH")
@@ -581,6 +602,13 @@ func HandleIKEAUTH(
581602
childSecurityAssociationContext.InitiatorToResponderIntegrityKey,
582603
)
583604

605+
// NAT-T concern
606+
if ikeSecurityAssociation.UEIsBehindNAT || ikeSecurityAssociation.N3IWFIsBehindNAT {
607+
childSecurityAssociationContext.EnableEncapsulate = true
608+
childSecurityAssociationContext.N3IWFPort = n3iwfAddr.Port
609+
childSecurityAssociationContext.NATPort = ueAddr.Port
610+
}
611+
584612
// Setup interface for ipsec
585613
newXfrmiName := fmt.Sprintf("%s-%d", n3ueSelf.N3ueInfo.XfrmiName, n3ueSelf.N3ueInfo.XfrmiId)
586614
if _, err = xfrm.SetupIPsecXfrmi(newXfrmiName,
@@ -603,7 +631,7 @@ func HandleIKEAUTH(
603631

604632
func HandleCREATECHILDSA(
605633
udpConn *net.UDPConn,
606-
n3iwfAddr, ueAddr *net.UDPAddr,
634+
ueAddr, n3iwfAddr *net.UDPAddr,
607635
message *ike_message.IKEMessage,
608636
) {
609637
ikeLog.Tracef("Handle CreateChildSA")
@@ -733,6 +761,13 @@ func HandleCREATECHILDSA(
733761
return
734762
}
735763

764+
// NAT-T concern
765+
if ikeSecurityAssociation.UEIsBehindNAT || ikeSecurityAssociation.N3IWFIsBehindNAT {
766+
childSecurityAssociationContextUserPlane.EnableEncapsulate = true
767+
childSecurityAssociationContextUserPlane.N3IWFPort = n3iwfAddr.Port
768+
childSecurityAssociationContextUserPlane.NATPort = ueAddr.Port
769+
}
770+
736771
n3ueSelf.N3ueInfo.XfrmiId++
737772
// Aplly XFRM rules
738773
if err = xfrm.ApplyXFRMRule(false, n3ueSelf.N3ueInfo.XfrmiId, childSecurityAssociationContextUserPlane); err != nil {
@@ -809,7 +844,7 @@ func HandleCREATECHILDSA(
809844

810845
func HandleInformational(
811846
udpConn *net.UDPConn,
812-
n3iwfAddr, ueAddr *net.UDPAddr,
847+
ueAddr, n3iwfAddr *net.UDPAddr,
813848
message *ike_message.IKEMessage,
814849
) {
815850
ikeLog.Infoln("Handle Informational")
@@ -823,3 +858,85 @@ func HandleInformational(
823858
ikeLog.Warnf("Unimplemented informational message")
824859
}
825860
}
861+
862+
func HandleNATDetect(
863+
initiatorSPI, responderSPI uint64,
864+
notifications []*ike_message.Notification,
865+
ueAddr, n3iwfAddr *net.UDPAddr,
866+
) (bool, bool, error) {
867+
ueBehindNAT := false
868+
n3iwfBehindNAT := false
869+
870+
srcNatDData, err := GenerateNATDetectHash(initiatorSPI, responderSPI, n3iwfAddr)
871+
if err != nil {
872+
return false, false, errors.Wrapf(err, "handle NATD")
873+
}
874+
875+
dstNatDData, err := GenerateNATDetectHash(initiatorSPI, responderSPI, ueAddr)
876+
if err != nil {
877+
return false, false, errors.Wrapf(err, "handle NATD")
878+
}
879+
880+
for _, notification := range notifications {
881+
switch notification.NotifyMessageType {
882+
case ike_message.NAT_DETECTION_SOURCE_IP:
883+
ikeLog.Tracef("Received IKE Notify: NAT_DETECTION_SOURCE_IP")
884+
if !bytes.Equal(notification.NotificationData, srcNatDData) {
885+
ikeLog.Tracef("N3IWF is behind NAT")
886+
n3iwfBehindNAT = true
887+
}
888+
case ike_message.NAT_DETECTION_DESTINATION_IP:
889+
ikeLog.Tracef("Received IKE Notify: NAT_DETECTION_DESTINATION_IP")
890+
if !bytes.Equal(notification.NotificationData, dstNatDData) {
891+
ikeLog.Tracef("UE(SPI: %016x) is behind NAT", responderSPI)
892+
ueBehindNAT = true
893+
}
894+
default:
895+
}
896+
}
897+
return ueBehindNAT, n3iwfBehindNAT, nil
898+
}
899+
900+
func BuildNATDetectNotifPayload(
901+
localSPI uint64, remoteSPI uint64,
902+
payload *ike_message.IKEPayloadContainer,
903+
ueAddr, n3iwfAddr *net.UDPAddr,
904+
) error {
905+
srcNatDHash, err := GenerateNATDetectHash(localSPI, remoteSPI, ueAddr)
906+
if err != nil {
907+
return errors.Wrapf(err, "build NATD")
908+
}
909+
// Build and append notify payload for NAT_DETECTION_SOURCE_IP
910+
payload.BuildNotification(
911+
ike_message.TypeNone, ike_message.NAT_DETECTION_SOURCE_IP, nil, srcNatDHash)
912+
913+
dstNatDHash, err := GenerateNATDetectHash(localSPI, remoteSPI, n3iwfAddr)
914+
if err != nil {
915+
return errors.Wrapf(err, "build NATD")
916+
}
917+
// Build and append notify payload for NAT_DETECTION_DESTINATION_IP
918+
payload.BuildNotification(
919+
ike_message.TypeNone, ike_message.NAT_DETECTION_DESTINATION_IP, nil, dstNatDHash)
920+
921+
return nil
922+
}
923+
924+
func GenerateNATDetectHash(
925+
initiatorSPI, responderSPI uint64,
926+
addr *net.UDPAddr,
927+
) ([]byte, error) {
928+
// Calculate NAT_DETECTION hash for NAT-T
929+
// : sha1(ispi | rspi | ip | port)
930+
natdData := make([]byte, 22)
931+
binary.BigEndian.PutUint64(natdData[0:8], initiatorSPI)
932+
binary.BigEndian.PutUint64(natdData[8:16], responderSPI)
933+
copy(natdData[16:20], addr.IP.To4())
934+
binary.BigEndian.PutUint16(natdData[20:22], uint16(addr.Port)) // #nosec G115
935+
936+
sha1HashFunction := sha1.New() // #nosec G401
937+
_, err := sha1HashFunction.Write(natdData)
938+
if err != nil {
939+
return nil, errors.Wrapf(err, "generate NATD Hash")
940+
}
941+
return sha1HashFunction.Sum(nil), nil
942+
}

pkg/ike/handler/send.go

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,16 @@ func SendIKESAINIT() {
9191
ikeMessage := ike_message.NewMessage(n3ueContext.IkeInitiatorSPI, 0,
9292
ike_message.IKE_SA_INIT, false, true, 0, *payload)
9393

94+
n3ueContext.N3IWFUe.IKEConnection = n3ueContext.IKEConnection[500]
95+
96+
err = BuildNATDetectNotifPayload(n3ueContext.IkeInitiatorSPI, 0, &ikeMessage.Payloads,
97+
n3ueContext.N3IWFUe.IKEConnection.UEAddr,
98+
n3ueContext.N3IWFUe.IKEConnection.N3IWFAddr)
99+
if err != nil {
100+
ikeLog.Errorf("SendIKESAINIT(): %v", err)
101+
return
102+
}
103+
94104
// Send to n3iwf
95105
err = SendIKEMessageToN3IWF(n3ueContext.N3IWFUe.IKEConnection.Conn,
96106
n3ueContext.N3IWFUe.IKEConnection.UEAddr,
@@ -116,8 +126,9 @@ func SendIKEAUTH() {
116126
ikeLog.Tracef("IKE_AUTH message")
117127

118128
n3ueContext := context.N3UESelf()
129+
ikeSA := n3ueContext.N3IWFUe.N3IWFIKESecurityAssociation
119130

120-
n3ueContext.N3IWFUe.N3IWFIKESecurityAssociation.InitiatorMessageID++
131+
ikeSA.InitiatorMessageID++
121132

122133
var ikePayload ike_message.IKEPayloadContainer
123134

@@ -171,13 +182,16 @@ func SendIKEAUTH() {
171182
)
172183

173184
ikeMessage := ike_message.NewMessage(
174-
n3ueContext.N3IWFUe.N3IWFIKESecurityAssociation.LocalSPI,
175-
n3ueContext.N3IWFUe.N3IWFIKESecurityAssociation.RemoteSPI,
185+
ikeSA.LocalSPI, ikeSA.RemoteSPI,
176186
ike_message.IKE_AUTH, false, true,
177-
n3ueContext.N3IWFUe.N3IWFIKESecurityAssociation.InitiatorMessageID,
187+
ikeSA.InitiatorMessageID,
178188
ikePayload,
179189
)
180190

191+
if ikeSA.UEIsBehindNAT || ikeSA.N3IWFIsBehindNAT {
192+
n3ueContext.N3IWFUe.IKEConnection = n3ueContext.IKEConnection[4500]
193+
}
194+
181195
err := SendIKEMessageToN3IWF(n3ueContext.N3IWFUe.IKEConnection.Conn, n3ueContext.N3IWFUe.IKEConnection.UEAddr,
182196
n3ueContext.N3IWFUe.IKEConnection.N3IWFAddr, ikeMessage,
183197
n3ueContext.N3IWFUe.N3IWFIKESecurityAssociation.IKESAKey)

0 commit comments

Comments
 (0)