Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add F-TEID allocation support #840

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .wordlist.txt
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,7 @@ sudo
tc
TCP
tcpdump
TEID
TRex
UDP
UE
Expand Down
2 changes: 1 addition & 1 deletion VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
1.5.1-dev
2.0.0
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
module github.com/omec-project/upf-epc

go 1.21
go 1.23

require (
github.com/Showmax/go-fqdn v1.0.0
Expand Down
75 changes: 75 additions & 0 deletions pfcpiface/fteid.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2024 Canonical Ltd.

package pfcpiface

import (
"errors"
"math"
"sync"
)

const (
minValue = 1
maxValue = math.MaxUint32
)

type FTEIDGenerator struct {
lock sync.Mutex
offset uint32
usedMap map[uint32]bool
}

func NewFTEIDGenerator() *FTEIDGenerator {
return &FTEIDGenerator{
offset: 0,
usedMap: make(map[uint32]bool),
}
}

// Allocate and return an id in range [minValue, maxValue]
func (idGenerator *FTEIDGenerator) Allocate() (uint32, error) {
idGenerator.lock.Lock()
defer idGenerator.lock.Unlock()

offsetBegin := idGenerator.offset
for {
if _, ok := idGenerator.usedMap[idGenerator.offset]; ok {
idGenerator.updateOffset()

if idGenerator.offset == offsetBegin {
return 0, errors.New("no available value range to allocate id")
}
} else {
break
}
}
idGenerator.usedMap[idGenerator.offset] = true
id := idGenerator.offset + minValue
idGenerator.updateOffset()
return id, nil
}

func (idGenerator *FTEIDGenerator) FreeID(id uint32) {
if id < minValue {
return
}
idGenerator.lock.Lock()
defer idGenerator.lock.Unlock()
delete(idGenerator.usedMap, id-minValue)
}

func (idGenerator *FTEIDGenerator) IsAllocated(id uint32) bool {
if id < minValue {
return false
}
idGenerator.lock.Lock()
defer idGenerator.lock.Unlock()
_, ok := idGenerator.usedMap[id-minValue]
return ok
}

func (idGenerator *FTEIDGenerator) updateOffset() {
idGenerator.offset++
idGenerator.offset = idGenerator.offset % maxValue
}
39 changes: 39 additions & 0 deletions pfcpiface/fteid_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2024 Canonical Ltd.

package pfcpiface_test

import (
"testing"

"github.com/omec-project/upf-epc/pfcpiface"
)

func TestFTEIDAllocate(t *testing.T) {
fteidGenerator := pfcpiface.NewFTEIDGenerator()

fteid, err := fteidGenerator.Allocate()
if err != nil {
t.Errorf("FTEID allocation failed: %v", err)
}
if fteid < 1 {
t.Errorf("FTEID allocation failed, value is too small: %v", fteid)
}
if !fteidGenerator.IsAllocated(fteid) {
t.Errorf("FTEID was not allocated")
}
}

func TestFTEIDFree(t *testing.T) {
fteidGenerator := pfcpiface.NewFTEIDGenerator()
fteid, err := fteidGenerator.Allocate()
if err != nil {
t.Errorf("FTEID allocation failed: %v", err)
}

fteidGenerator.FreeID(fteid)

if fteidGenerator.IsAllocated(fteid) {
t.Errorf("FTEID was not freed")
}
}
2 changes: 2 additions & 0 deletions pfcpiface/messages_conn.go
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,8 @@ func (pConn *PFCPConn) associationIEs() []*ie.IE {
setUeipFeature(features...)
}

setFTUPFeature(features...)

if upf.enableEndMarker {
setEndMarkerFeature(features...)
}
Expand Down
16 changes: 14 additions & 2 deletions pfcpiface/messages_session.go
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,18 @@ func (pConn *PFCPConn) handleSessionEstablishmentRequest(msg message.Message) (m
return errProcessReply(err, ie.CauseRequestRejected)
}

if p.UPAllocateFteid {
var fteid uint32
fteid, err = pConn.upf.fteidGenerator.Allocate()
if err != nil {
return errProcessReply(err, ie.CauseNoResourcesAvailable)
}
p.tunnelTEID = fteid
p.tunnelTEIDMask = 0xFFFFFFFF
p.tunnelIP4Dst = ip2int(upf.accessIP)
p.tunnelIP4DstMask = 0xFFFFFFFF
}

p.fseidIP = fseidIP
session.CreatePDR(p)
addPDRs = append(addPDRs, p)
Expand Down Expand Up @@ -164,8 +176,7 @@ func (pConn *PFCPConn) handleSessionEstablishmentRequest(msg message.Message) (m
ie.NewCause(ie.CauseRequestAccepted), /* accept it blindly for the time being */
localFSEID,
)

addPdrInfo(seres, &session)
addPdrInfo(seres, addPDRs)

return seres, nil
}
Expand Down Expand Up @@ -231,6 +242,7 @@ func (pConn *PFCPConn) handleSessionModificationRequest(msg message.Message) (me
session.CreatePDR(p)
addPDRs = append(addPDRs, p)
}
logger.PfcpLog.Debugln("PDRs added:", addPDRs)

for _, cFAR := range smreq.CreateFAR {
var f far
Expand Down
25 changes: 14 additions & 11 deletions pfcpiface/parse_pdr.go
Original file line number Diff line number Diff line change
Expand Up @@ -269,10 +269,11 @@ type applicationFilter struct {
}

type pdr struct {
srcIface uint8
tunnelIP4Dst uint32
tunnelTEID uint32
ueAddress uint32
UPAllocateFteid bool
srcIface uint8
tunnelIP4Dst uint32
tunnelTEID uint32
ueAddress uint32

srcIfaceMask uint8
tunnelIP4DstMask uint32
Expand Down Expand Up @@ -390,13 +391,15 @@ func (p *pdr) parseFTEID(teidIE *ie.IE) error {
}

teid := fteid.TEID
tunnelIPv4Address := fteid.IPv4Address

if teid != 0 {
p.tunnelTEID = teid
p.tunnelTEIDMask = 0xFFFFFFFF
p.tunnelIP4Dst = ip2int(tunnelIPv4Address)
p.tunnelIP4DstMask = 0xFFFFFFFF
if fteid.HasCh() {
p.UPAllocateFteid = true
} else {
if teid != 0 {
p.tunnelTEID = teid
p.tunnelTEIDMask = 0xFFFFFFFF
p.tunnelIP4Dst = ip2int(fteid.IPv4Address)
p.tunnelIP4DstMask = 0xFFFFFFFF
}
}

return nil
Expand Down
19 changes: 12 additions & 7 deletions pfcpiface/session_pdr.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,23 +23,28 @@ func releaseAllocatedIPs(ippool *IPPool, session *PFCPSession) error {
return ippool.DeallocIP(session.localSEID)
}
}

return nil
}

func addPdrInfo(msg *message.SessionEstablishmentResponse,
session *PFCPSession) {
func addPdrInfo(msg *message.SessionEstablishmentResponse, pdrs []pdr) {
logger.PfcpLog.Infoln("add PDRs with UPF alloc IPs to Establishment response")

for _, pdr := range session.pdrs {
logger.PfcpLog.Infoln("PDRs:", pdrs)
for _, pdr := range pdrs {
logger.PfcpLog.Infoln("pdrID:", pdr.pdrID)
if pdr.UPAllocateFteid {
logger.PfcpLog.Infoln("adding PDR with tunnel TEID:", pdr.tunnelTEID)
msg.CreatedPDR = append(msg.CreatedPDR,
ie.NewCreatedPDR(
ie.NewPDRID(uint16(pdr.pdrID)),
ie.NewFTEID(0x01, pdr.tunnelTEID, int2ip(pdr.tunnelIP4Dst), nil, 0),
))
}
if (pdr.allocIPFlag) && (pdr.srcIface == core) {
logger.PfcpLog.Debugln("pdrID:", pdr.pdrID)

var (
flags uint8 = 0x02
ueIP net.IP = int2ip(pdr.ueAddress)
)

logger.PfcpLog.Debugln("ueIP:", ueIP.String())
msg.CreatedPDR = append(msg.CreatedPDR,
ie.NewCreatedPDR(
Expand Down
3 changes: 3 additions & 0 deletions pfcpiface/upf.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ type upf struct {
reportNotifyChan chan uint64
sliceInfo *SliceInfo
readTimeout time.Duration
fteidGenerator *FTEIDGenerator

datapath
maxReqRetries uint8
Expand Down Expand Up @@ -129,6 +130,8 @@ func NewUPF(conf *Conf, fp datapath) *upf {
maxReqRetries: conf.MaxReqRetries,
enableHBTimer: conf.EnableHBTimer,
readTimeout: time.Second * time.Duration(conf.ReadTimeout),
fteidGenerator: NewFTEIDGenerator(),

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Ling GHA is failing because if this empty line. The following change will fix the issue

Suggested change

n4addr: conf.N4Addr,
}

Expand Down
7 changes: 7 additions & 0 deletions pfcpiface/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,13 @@ func setUeipFeature(features ...uint8) {
}
}

// Set the 5th bit of the first octet to 1.
func setFTUPFeature(features ...uint8) {
if len(features) >= 1 {
features[0] = features[0] | 0x10
}
}

func setEndMarkerFeature(features ...uint8) {
if len(features) >= 2 {
features[1] = features[1] | 0x01
Expand Down
25 changes: 25 additions & 0 deletions pfcpiface/utils_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ package pfcpiface

import (
"github.com/stretchr/testify/require"
"github.com/wmnsk/go-pfcp/ie"

"net"
"reflect"
Expand Down Expand Up @@ -91,3 +92,27 @@ func TestGetSliceTcMeterIndex(t *testing.T) {
)
}
}

func TestSetUeipFeature(t *testing.T) {
features := make([]uint8, 4)

setUeipFeature(features...)

ie := ie.NewUPFunctionFeatures(features...)
hasUeIPAlloc := ie.HasUEIP()
if !hasUeIPAlloc {
t.Errorf("Expected UEIPAlloc to be set")
}
}

func TestSetFTUPFeature(t *testing.T) {
features := make([]uint8, 4)

setFTUPFeature(features...)

ie := ie.NewUPFunctionFeatures(features...)
hasFTUP := ie.HasFTUP()
if !hasFTUP {
t.Errorf("Expected FTUP to be set")
}
}
Loading