Skip to content

Commit

Permalink
Fix IPSec allocate error
Browse files Browse the repository at this point in the history
  • Loading branch information
Allen00991 committed Oct 17, 2024
1 parent ef3c2d5 commit d490467
Show file tree
Hide file tree
Showing 9 changed files with 161 additions and 46 deletions.
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ require (
github.com/free5gc/ike v1.1.1-0.20241014015325-083f89768f43
github.com/free5gc/ngap v1.0.9-0.20240708062829-734d184eed74
github.com/free5gc/sctp v1.0.1
github.com/free5gc/util v1.0.7-0.20240713162917-350ee8f4af4c
github.com/free5gc/util v1.0.7-0.20241016064137-5698cc626593
github.com/gin-contrib/pprof v1.5.0
github.com/gin-gonic/gin v1.10.0
github.com/google/gopacket v1.1.19
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@ github.com/free5gc/openapi v1.0.9-0.20240503143645-eac9f06c2f6b h1:+VcgZq+3apB6X
github.com/free5gc/openapi v1.0.9-0.20240503143645-eac9f06c2f6b/go.mod h1:0qRW+H1/Nyzw5tjjvyp+90m+2SOZZefGQC9QV8iPwu8=
github.com/free5gc/sctp v1.0.1 h1:g8WDO97r8B9ubkT5Hyk9b4I1fZUOii9Z39gQ2eRaASo=
github.com/free5gc/sctp v1.0.1/go.mod h1:7QXfRWCmlkBGD0EIu3qL5o71bslfIakydz4h2QDZdjQ=
github.com/free5gc/util v1.0.7-0.20240713162917-350ee8f4af4c h1:baToZn4hxGKoCm3BWwYlRuZoCQ74cMZUJzg9BVLEdE0=
github.com/free5gc/util v1.0.7-0.20240713162917-350ee8f4af4c/go.mod h1:IHKIBd4OM9rwSJ0fG/hv6pXbVC+Eu4Lcaq++BWkfSsY=
github.com/free5gc/util v1.0.7-0.20241016064137-5698cc626593 h1:Wp32mLckyP25feOmwHBTS32pSkIAjuZgq8iP7njbe/k=
github.com/free5gc/util v1.0.7-0.20241016064137-5698cc626593/go.mod h1:IHKIBd4OM9rwSJ0fG/hv6pXbVC+Eu4Lcaq++BWkfSsY=
github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0=
github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk=
github.com/gin-contrib/pprof v1.5.0 h1:E/Oy7g+kNw94KfdCy3bZxQFtyDnAX2V7axRS7sNYVrU=
Expand Down
59 changes: 23 additions & 36 deletions internal/context/context.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import (
"github.com/free5gc/openapi/models"
"github.com/free5gc/sctp"
"github.com/free5gc/util/idgenerator"
"github.com/free5gc/util/ippool"
)

type n3iwf interface {
Expand Down Expand Up @@ -59,7 +60,8 @@ type N3IWFContext struct {
N3IWFCertificate []byte
N3IWFPrivateKey *rsa.PrivateKey

UeIPRange *net.IPNet
IPSecInnerIPPool *ippool.IPPool
// TODO: [TWIF] TwifUe may has its own IP address pool

/* XFRM interface */
XfrmIfaces sync.Map // map[uint32]*netlink.Link, XfrmIfaceId as key
Expand Down Expand Up @@ -130,11 +132,11 @@ func NewContext(n3iwf n3iwf) (*N3IWFContext, error) {
n.N3IWFCertificate = block.Bytes

// UE IP address range
_, ueIPRange, err := net.ParseCIDR(cfg.GetUEIPAddrRange())
ueIPPool, err := ippool.NewIPPool(cfg.GetUEIPAddrRange())
if err != nil {
return nil, errors.Errorf("Parse CIDR failed: %+v", err)
return nil, errors.Errorf("NewContext(): %+v", err)
}
n.UeIPRange = ueIPRange
n.IPSecInnerIPPool = ueIPPool

// XFRM related
ikeBindIfaceName, err := getInterfaceName(cfg.GetIKEBindAddr())
Expand Down Expand Up @@ -461,26 +463,30 @@ func (c *N3IWFContext) GTPConnectionWithUPFStore(upfAddr string, conn *gtpv1.UPl
c.GTPConnectionWithUPF.Store(upfAddr, conn)
}

func (c *N3IWFContext) NewInternalUEIPAddr(ikeUe *N3IWFIkeUe) net.IP {
func (c *N3IWFContext) NewIPsecInnerUEIP(ikeUe *N3IWFIkeUe) (net.IP, error) {
var ueIPAddr net.IP

var err error
cfg := c.Config()
ipsecGwAddr := cfg.GetIPSecGatewayAddr()
// TODO: Check number of allocated IP to detect running out of IPs

for {
ueIPAddr = generateRandomIPinRange(c.UeIPRange)
if ueIPAddr != nil {
if ueIPAddr.String() == ipsecGwAddr {
continue
}
_, ok := c.AllocatedUEIPAddress.LoadOrStore(ueIPAddr.String(), ikeUe)
if !ok {
break
}
ueIPAddr, err = c.IPSecInnerIPPool.Allocate(nil)
if err != nil {
return nil, errors.Wrapf(err, "NewIPsecInnerUEIP()")
}
if ueIPAddr.String() == ipsecGwAddr {
continue
}
_, ok := c.AllocatedUEIPAddress.LoadOrStore(ueIPAddr.String(), ikeUe)
if ok {
logger.CtxLog.Warnf("NewIPsecInnerUEIP(): IP(%v) is used by other IkeUE",
ueIPAddr.String())
} else {
break
}
}

return ueIPAddr
return ueIPAddr, nil
}

func (c *N3IWFContext) DeleteInternalUEIPAddr(ipAddr string) {
Expand Down Expand Up @@ -554,22 +560,3 @@ func (c *N3IWFContext) AMFSelection(
}
return availableAMF
}

func generateRandomIPinRange(subnet *net.IPNet) net.IP {
ipAddr := make([]byte, 4)
randomNumber := make([]byte, 4)

_, err := rand.Read(randomNumber)
if err != nil {
logger.CtxLog.Errorf("Generate random number for IP address failed: %+v", err)
return nil
}

// TODO: elimenate network name, gateway, and broadcast
for i := 0; i < 4; i++ {
alter := randomNumber[i] & (subnet.Mask[i] ^ 255)
ipAddr[i] = subnet.IP[i] + alter
}

return net.IPv4(ipAddr[0], ipAddr[1], ipAddr[2], ipAddr[3])
}
106 changes: 106 additions & 0 deletions internal/context/context_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
package context_test

import (
"context"
"net"
"sync"
"testing"

"github.com/stretchr/testify/require"

n3iwf_context "github.com/free5gc/n3iwf/internal/context"
"github.com/free5gc/n3iwf/pkg/factory"
"github.com/free5gc/util/ippool"
)

type n3iwfTestApp struct {
cfg *factory.Config
n3iwfCtx *n3iwf_context.N3IWFContext
ctx context.Context
cancel context.CancelFunc
wg *sync.WaitGroup
}

func (a *n3iwfTestApp) Config() *factory.Config {
return a.cfg
}

func (a *n3iwfTestApp) Context() *n3iwf_context.N3IWFContext {
return a.n3iwfCtx
}

func (a *n3iwfTestApp) CancelContext() context.Context {
return a.ctx
}

func NewN3iwfTestApp(cfg *factory.Config) (*n3iwfTestApp, error) {
var err error
ctx, cancel := context.WithCancel(context.Background())

n3iwfApp := &n3iwfTestApp{
cfg: cfg,
ctx: ctx,
cancel: cancel,
wg: &sync.WaitGroup{},
}

n3iwfApp.n3iwfCtx, err = n3iwf_context.NewTestContext(n3iwfApp)
if err != nil {
return nil, err
}
return n3iwfApp, err
}

func NewTestCfg() *factory.Config {
return &factory.Config{
Configuration: &factory.Configuration{
IPSecGatewayAddr: "10.0.0.1",
UEIPAddressRange: "10.0.0.0/24",
},
}
}

func TestNewInternalUEIPAddr(t *testing.T) {
cfg := NewTestCfg()
var app *n3iwfTestApp
var err error
var ip, invalidIP, invalidIP2 net.IP

app, err = NewN3iwfTestApp(cfg)
require.NoError(t, err)

n3iwfCtx := app.n3iwfCtx

invalidIP = net.ParseIP("10.0.0.0")
invalidIP2 = net.ParseIP("10.0.0.255")
n3iwfCtx.IPSecInnerIPPool, err = ippool.NewIPPool("10.0.0.0/24")
require.NoError(t, err)

for i := 1; i <= 253; i++ {
ip, err = n3iwfCtx.NewIPsecInnerUEIP(&n3iwf_context.N3IWFIkeUe{})
require.NoError(t, err)
require.NotEqual(t, cfg.GetIPSecGatewayAddr(), ip.String())
require.NotEqual(t, ip, invalidIP)
require.NotEqual(t, ip, invalidIP2)
}

_, err = n3iwfCtx.NewIPsecInnerUEIP(&n3iwf_context.N3IWFIkeUe{})
require.Error(t, err)

n3iwfCtx.AllocatedUEIPAddress = sync.Map{}

n3iwfCtx.IPSecInnerIPPool, err = ippool.NewIPPool("10.0.0.0/16")
require.NoError(t, err)

invalidIP2 = net.ParseIP("10.0.255.255")
for i := 1; i <= 65533; i++ {
ip, err = n3iwfCtx.NewIPsecInnerUEIP(&n3iwf_context.N3IWFIkeUe{})
require.NoError(t, err)
require.NotEqual(t, cfg.GetIPSecGatewayAddr(), ip.String())
require.NotEqual(t, ip, invalidIP)
require.NotEqual(t, ip, invalidIP2)
}

_, err = n3iwfCtx.NewIPsecInnerUEIP(&n3iwf_context.N3IWFIkeUe{})
require.Error(t, err)
}
5 changes: 5 additions & 0 deletions internal/context/ikeue.go
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,11 @@ func (ikeUe *N3IWFIkeUe) Remove() error {
n3iwfCtx.DeleteIKESecurityAssociation(ikeUe.N3IWFIKESecurityAssociation.LocalSPI)
n3iwfCtx.DeleteInternalUEIPAddr(ikeUe.IPSecInnerIP.String())

err := n3iwfCtx.IPSecInnerIPPool.Release(net.ParseIP(ikeUe.IPSecInnerIP.String()).To4())
if err != nil {
return errors.Wrapf(err, "N3IWFIkeUe Remove()")
}

for _, childSA := range ikeUe.N3IWFChildSecurityAssociation {
if err := ikeUe.DeleteChildSA(childSA); err != nil {
return err
Expand Down
13 changes: 9 additions & 4 deletions internal/ike/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -737,15 +737,21 @@ func (s *Server) HandleIKEAUTH(
var ueIPAddr, n3iwfIPAddr net.IP
if addrRequest {
// IP addresses (IPSec)
ueIPAddr = n3iwfCtx.NewInternalUEIPAddr(ikeUE).To4()
var ueIp net.IP
ueIp, err = n3iwfCtx.NewIPsecInnerUEIP(ikeUE)
if err != nil {
ikeLog.Errorf("HandleIKEAUTH(): %v", err)
return
}
ueIPAddr = ueIp.To4()
n3iwfIPAddr = net.ParseIP(ipsecGwAddr).To4()

responseConfiguration := responseIKEPayload.BuildConfiguration(
ike_message.CFG_REPLY)
responseConfiguration.ConfigurationAttribute.BuildConfigurationAttribute(
ike_message.INTERNAL_IP4_ADDRESS, ueIPAddr)
responseConfiguration.ConfigurationAttribute.BuildConfigurationAttribute(
ike_message.INTERNAL_IP4_NETMASK, n3iwfCtx.UeIPRange.Mask)
ike_message.INTERNAL_IP4_NETMASK, n3iwfCtx.IPSecInnerIPPool.IPSubnet.Mask)

var ipsecInnerIPAddr *net.IPAddr
ikeUE.IPSecInnerIP = ueIPAddr
Expand Down Expand Up @@ -1072,7 +1078,7 @@ func (s *Server) continueCreateChildSA(
// Setup XFRM interface for ipsec
var linkIPSec netlink.Link
n3iwfIPAddr := net.ParseIP(ipsecGwAddr).To4()
n3iwfIPAddrAndSubnet := net.IPNet{IP: n3iwfIPAddr, Mask: n3iwfCtx.UeIPRange.Mask}
n3iwfIPAddrAndSubnet := net.IPNet{IP: n3iwfIPAddr, Mask: n3iwfCtx.IPSecInnerIPPool.IPSubnet.Mask}
newXfrmiId += cfg.GetXfrmIfaceId() + n3iwfCtx.XfrmIfaceIdOffsetForUP
newXfrmiName := fmt.Sprintf("%s-%d", cfg.GetXfrmIfaceName(), newXfrmiId)

Expand Down Expand Up @@ -1152,7 +1158,6 @@ func (s *Server) HandleInformational(
switch ikePayload.Type() {
case ike_message.TypeD:
deletePayload = ikePayload.(*ike_message.Delete)

default:
ikeLog.Warnf(
"Get IKE payload (type %d) in Inoformational message, this payload will not be handled by IKE handler",
Expand Down
7 changes: 7 additions & 0 deletions internal/ike/handler_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
ike_message "github.com/free5gc/ike/message"
n3iwf_context "github.com/free5gc/n3iwf/internal/context"
"github.com/free5gc/n3iwf/pkg/factory"
"github.com/free5gc/util/ippool"
)

func TestRemoveIkeUe(t *testing.T) {
Expand All @@ -22,8 +23,14 @@ func TestRemoveIkeUe(t *testing.T) {
ikeSA := n3iwfCtx.NewIKESecurityAssociation()
ikeUe := n3iwfCtx.NewN3iwfIkeUe(ikeSA.LocalSPI)
ikeUe.N3IWFIKESecurityAssociation = ikeSA
ikeUe.IPSecInnerIP = net.ParseIP("10.0.0.1")
ikeSA.IsUseDPD = false

n3iwfCtx.IPSecInnerIPPool, err = ippool.NewIPPool("10.0.0.0/24")
require.NoError(t, err)
_, err = n3iwfCtx.IPSecInnerIPPool.Allocate(nil)
require.NoError(t, err)

ikeUe.CreateHalfChildSA(1, 123, 1)

ikeAuth := &ike_message.SecurityAssociation{}
Expand Down
7 changes: 6 additions & 1 deletion internal/ike/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package ike
import (
"bytes"
"context"
"encoding/hex"
"fmt"
"net"
"runtime/debug"
Expand Down Expand Up @@ -173,6 +174,7 @@ func (s *Server) receiver(

msgBuf := make([]byte, n)
copy(msgBuf, buf)
ikeLog.Tracef("recv from port(%d):\n%s", localAddr.Port, hex.Dump(msgBuf))

// As specified in RFC 7296 section 3.1, the IKE message send from/to UDP port 4500
// should prepend a 4 bytes zero
Expand Down Expand Up @@ -349,14 +351,17 @@ func constructPacketWithESP(srcIP, dstIP *net.UDPAddr, espPacket []byte) ([]byte
}

func handleESPPacket(srcIP, dstIP *net.UDPAddr, espPacket []byte) error {
ikeLog := logger.IKELog
ikeLog.Tracef("Handle ESPPacket")

fd, err := syscall.Socket(syscall.AF_INET, syscall.SOCK_RAW, syscall.IPPROTO_RAW)
if err != nil {
return errors.Errorf("socket error: %v", err)
}

defer func() {
if err = syscall.Close(fd); err != nil {
logger.IKELog.Errorf("Close fd error : %v", err)
ikeLog.Errorf("Close fd error : %v", err)
}
}()

Expand Down
4 changes: 2 additions & 2 deletions pkg/service/init.go
Original file line number Diff line number Diff line change
Expand Up @@ -205,7 +205,7 @@ func (a *N3iwfApp) initDefaultXfrmInterface() error {
cfg := a.Config()
mainLog := logger.MainLog
n3iwfIPAddr := net.ParseIP(cfg.GetIPSecGatewayAddr()).To4()
n3iwfIPAddrAndSubnet := net.IPNet{IP: n3iwfIPAddr, Mask: n3iwfCtx.UeIPRange.Mask}
n3iwfIPAddrAndSubnet := net.IPNet{IP: n3iwfIPAddr, Mask: n3iwfCtx.IPSecInnerIPPool.IPSubnet.Mask}
newXfrmiName := fmt.Sprintf("%s-default", cfg.GetXfrmIfaceName())

if linkIPSec, err = xfrm.SetupIPsecXfrmi(newXfrmiName, n3iwfCtx.XfrmParentIfaceName,
Expand All @@ -216,7 +216,7 @@ func (a *N3iwfApp) initDefaultXfrmInterface() error {

route := &netlink.Route{
LinkIndex: linkIPSec.Attrs().Index,
Dst: n3iwfCtx.UeIPRange,
Dst: n3iwfCtx.IPSecInnerIPPool.IPSubnet,
}

if err := netlink.RouteAdd(route); err != nil {
Expand Down

0 comments on commit d490467

Please sign in to comment.