Skip to content

Commit d3e78b8

Browse files
arxxmartempirog-spb
authored
feat: setting the FTUP flag and allocation of TEID (#476)
Co-authored-by: artem <artem@pingocean.com> Co-authored-by: pirog-spb <arozhkevich@eedgecom.ru>
1 parent 36fdd3a commit d3e78b8

18 files changed

Lines changed: 599 additions & 141 deletions

cmd/config/config.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ type UpfConfig struct {
2828
HeartbeatInterval uint32 `mapstructure:"heartbeat_interval" json:"heartbeat_interval"`
2929
HeartbeatTimeout uint32 `mapstructure:"heartbeat_timeout" json:"heartbeat_timeout"`
3030
LoggingLevel string `mapstructure:"logging_level" validate:"required" json:"logging_level"`
31+
FTEIDPool uint32 `mapstructure:"teid_pool" json:"teid_pool"`
32+
FeatureFTUP bool `mapstructure:"feature_ftup" json:"feature_ftup"`
3133
}
3234

3335
func init() {
@@ -50,6 +52,8 @@ func init() {
5052
pflag.Uint32("hbinterval", 5, "Heartbeat interval in seconds")
5153
pflag.Uint32("hbtimeout", 5, "Heartbeat timeout in seconds")
5254
pflag.String("loglvl", "", "Logging level")
55+
pflag.Bool("feature_ftup", true, "Enable or disable feature_ftup")
56+
pflag.Uint32("teid_pool", 65536, "TEID Pool")
5357
pflag.Parse()
5458

5559
// Bind flag errors only when flag is nil, and we ignore empty cli args
@@ -70,6 +74,8 @@ func init() {
7074
_ = v.BindPFlag("heartbeat_interval", pflag.Lookup("hbinterval"))
7175
_ = v.BindPFlag("heartbeat_timeout", pflag.Lookup("hbtimeout"))
7276
_ = v.BindPFlag("logging_level", pflag.Lookup("loglvl"))
77+
_ = v.BindPFlag("feature_ftup", pflag.Lookup("feature_ftup"))
78+
_ = v.BindPFlag("teid_pool", pflag.Lookup("teid_pool"))
7379

7480
v.SetDefault("interface_name", "lo")
7581
v.SetDefault("xdp_attach_mode", "generic")
@@ -87,6 +93,8 @@ func init() {
8793
v.SetDefault("heartbeat_interval", 5)
8894
v.SetDefault("heartbeat_timeout", 5)
8995
v.SetDefault("logging_level", "info")
96+
v.SetDefault("feature_ftup", false)
97+
v.SetDefault("teid_pool", 0)
9098

9199
v.SetConfigFile(*configPath)
92100

cmd/core/api.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package core
22

33
import (
44
"fmt"
5+
eupfDocs "github.com/edgecomllc/eupf/cmd/docs"
56
"net"
67
"net/http"
78
"strconv"
@@ -10,7 +11,6 @@ import (
1011
"github.com/edgecomllc/eupf/cmd/config"
1112
"github.com/edgecomllc/eupf/cmd/ebpf"
1213

13-
eupfDocs "github.com/edgecomllc/eupf/cmd/docs"
1414
"github.com/gin-contrib/cors"
1515
"github.com/gin-gonic/gin"
1616
"github.com/rs/zerolog/log"

cmd/core/ie_overwrite_test.go

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,6 @@ func (mapOps *MapOperationsMock) DeleteQer(internalId uint32) error {
6161
func TestSessionOverwrite(t *testing.T) {
6262

6363
mapOps := MapOperationsMock{}
64-
6564
// Create pfcp connection struct
6665
pfcpConn := PfcpConnection{
6766
NodeAssociations: make(map[string]*NodeAssociation),

cmd/core/pdr.go

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
package core
2+
3+
import (
4+
"net"
5+
6+
"github.com/edgecomllc/eupf/cmd/ebpf"
7+
"github.com/rs/zerolog/log"
8+
"github.com/wmnsk/go-pfcp/ie"
9+
)
10+
11+
func applyPDR(spdrInfo SPDRInfo, mapOperations ebpf.ForwardingPlaneController) {
12+
if spdrInfo.Ipv4 != nil {
13+
if err := mapOperations.PutPdrDownlink(spdrInfo.Ipv4, spdrInfo.PdrInfo); err != nil {
14+
log.Info().Msgf("Can't apply IPv4 PDR: %s", err.Error())
15+
}
16+
} else if spdrInfo.Ipv6 != nil {
17+
if err := mapOperations.PutDownlinkPdrIp6(spdrInfo.Ipv6, spdrInfo.PdrInfo); err != nil {
18+
log.Info().Msgf("Can't apply IPv6 PDR: %s", err.Error())
19+
}
20+
} else {
21+
if err := mapOperations.PutPdrUplink(spdrInfo.Teid, spdrInfo.PdrInfo); err != nil {
22+
log.Info().Msgf("Can't apply GTP PDR: %s", err.Error())
23+
}
24+
}
25+
}
26+
27+
func processCreatedPDRs(createdPDRs []SPDRInfo, n3Address net.IP) []*ie.IE {
28+
var additionalIEs []*ie.IE
29+
for _, pdr := range createdPDRs {
30+
if pdr.Allocated {
31+
if pdr.Ipv4 != nil {
32+
additionalIEs = append(additionalIEs, ie.NewCreatedPDR(ie.NewPDRID(uint16(pdr.PdrID)), ie.NewUEIPAddress(0, pdr.Ipv4.String(), "", 0, 0)))
33+
} else if pdr.Ipv6 != nil {
34+
35+
} else {
36+
additionalIEs = append(additionalIEs, ie.NewCreatedPDR(ie.NewPDRID(uint16(pdr.PdrID)), ie.NewFTEID(0x01, pdr.Teid, cloneIP(n3Address), nil, 0)))
37+
}
38+
}
39+
}
40+
return additionalIEs
41+
}

cmd/core/pdr_creation_context.go

Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
package core
2+
3+
import (
4+
"errors"
5+
"fmt"
6+
"github.com/edgecomllc/eupf/cmd/core/service"
7+
"github.com/edgecomllc/eupf/cmd/ebpf"
8+
"github.com/rs/zerolog/log"
9+
"github.com/wmnsk/go-pfcp/ie"
10+
)
11+
12+
type PDRCreationContext struct {
13+
Session *Session
14+
ResourceManager *service.ResourceManager
15+
TEIDCache map[uint8]uint32
16+
}
17+
18+
func NewPDRCreationContext(session *Session, resourceManager *service.ResourceManager) *PDRCreationContext {
19+
return &PDRCreationContext{
20+
Session: session,
21+
ResourceManager: resourceManager,
22+
TEIDCache: make(map[uint8]uint32),
23+
}
24+
}
25+
26+
func (pcc *PDRCreationContext) extractPDR(pdr *ie.IE, spdrInfo *SPDRInfo) error {
27+
if outerHeaderRemoval, err := pdr.OuterHeaderRemovalDescription(); err == nil {
28+
spdrInfo.PdrInfo.OuterHeaderRemoval = outerHeaderRemoval
29+
}
30+
if farid, err := pdr.FARID(); err == nil {
31+
spdrInfo.PdrInfo.FarId = pcc.getFARID(farid)
32+
}
33+
if qerid, err := pdr.QERID(); err == nil {
34+
spdrInfo.PdrInfo.QerId = pcc.getQERID(qerid)
35+
}
36+
37+
pdi, err := pdr.PDI()
38+
if err != nil {
39+
return fmt.Errorf("PDI IE is missing")
40+
}
41+
42+
if sdfFilter, err := pdr.SDFFilter(); err == nil {
43+
if sdfFilterParsed, err := ParseSdfFilter(sdfFilter.FlowDescription); err == nil {
44+
spdrInfo.PdrInfo.SdfFilter = &sdfFilterParsed
45+
} else {
46+
log.Error().Msgf("SDFFilter err: %v", err)
47+
return err
48+
}
49+
}
50+
51+
if teidPdiId := findIEindex(pdi, 21); teidPdiId != -1 { // IE Type F-TEID
52+
if fteid, err := pdi[teidPdiId].FTEID(); err == nil {
53+
var teid = fteid.TEID
54+
if fteid.HasCh() {
55+
var allocate = true
56+
if fteid.HasChID() {
57+
if teidFromCache, ok := pcc.hasTEIDCache(fteid.ChooseID); ok {
58+
allocate = false
59+
teid = teidFromCache
60+
spdrInfo.Allocated = true
61+
}
62+
}
63+
if allocate {
64+
allocatedTeid, err := pcc.getFTEID(pcc.Session.RemoteSEID, spdrInfo.PdrID)
65+
if err != nil {
66+
log.Error().Msgf("AllocateTEID err: %v", err)
67+
return fmt.Errorf("can't allocate TEID: %s", causeToString(ie.CauseNoResourcesAvailable))
68+
}
69+
teid = allocatedTeid
70+
spdrInfo.Allocated = true
71+
if fteid.HasChID() {
72+
pcc.setTEIDCache(fteid.ChooseID, teid)
73+
}
74+
}
75+
}
76+
spdrInfo.Teid = teid
77+
return nil
78+
}
79+
return fmt.Errorf("F-TEID IE is missing")
80+
} else if ueIP, err := pdr.UEIPAddress(); err == nil {
81+
if ueIP.IPv4Address != nil {
82+
spdrInfo.Ipv4 = cloneIP(ueIP.IPv4Address)
83+
} else if ueIP.IPv6Address != nil {
84+
spdrInfo.Ipv6 = cloneIP(ueIP.IPv6Address)
85+
} else {
86+
return fmt.Errorf("UE IP Address IE is missing")
87+
}
88+
89+
return nil
90+
} else {
91+
log.Info().Msg("Both F-TEID IE and UE IP Address IE are missing")
92+
return err
93+
}
94+
}
95+
96+
func (pcc *PDRCreationContext) deletePDR(spdrInfo SPDRInfo, mapOperations ebpf.ForwardingPlaneController) error {
97+
if spdrInfo.Ipv4 != nil {
98+
if err := mapOperations.DeletePdrDownlink(spdrInfo.Ipv4); err != nil {
99+
return fmt.Errorf("Can't delete IPv4 PDR: %s", err.Error())
100+
}
101+
} else if spdrInfo.Ipv6 != nil {
102+
if err := mapOperations.DeleteDownlinkPdrIp6(spdrInfo.Ipv6); err != nil {
103+
return fmt.Errorf("Can't delete IPv6 PDR: %s", err.Error())
104+
}
105+
} else {
106+
if err := mapOperations.DeletePdrUplink(spdrInfo.Teid); err != nil {
107+
return fmt.Errorf("Can't delete GTP PDR: %s", err.Error())
108+
}
109+
}
110+
if spdrInfo.Teid != 0 {
111+
pcc.ResourceManager.FTEIDM.ReleaseTEID(pcc.Session.RemoteSEID)
112+
}
113+
return nil
114+
}
115+
116+
func (pcc *PDRCreationContext) getFARID(farid uint32) uint32 {
117+
return pcc.Session.GetFar(farid).GlobalId
118+
}
119+
120+
func (pcc *PDRCreationContext) getQERID(qerid uint32) uint32 {
121+
return pcc.Session.GetQer(qerid).GlobalId
122+
}
123+
124+
func (pcc *PDRCreationContext) getFTEID(seID uint64, pdrID uint32) (uint32, error) {
125+
if pcc.ResourceManager == nil {
126+
return 0, errors.New("resource manager is nil")
127+
} else {
128+
if pcc.ResourceManager.FTEIDM == nil {
129+
return 0, errors.New("FTEID manager is nil")
130+
}
131+
}
132+
allocatedTeid, err := pcc.ResourceManager.FTEIDM.AllocateTEID(seID, pdrID)
133+
if err != nil {
134+
log.Error().Msgf("AllocateTEID err: %v", err)
135+
return 0, fmt.Errorf("Can't allocate TEID: %s", causeToString(ie.CauseNoResourcesAvailable))
136+
}
137+
return allocatedTeid, nil
138+
}
139+
140+
func (pcc *PDRCreationContext) hasTEIDCache(chooseID uint8) (uint32, bool) {
141+
teid, ok := pcc.TEIDCache[chooseID]
142+
return teid, ok
143+
}
144+
145+
func (pcc *PDRCreationContext) setTEIDCache(chooseID uint8, teid uint32) {
146+
pcc.TEIDCache[chooseID] = teid
147+
}

cmd/core/pfcp_connection.go

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package core
22

33
import (
44
"fmt"
5+
"github.com/edgecomllc/eupf/cmd/core/service"
56
"net"
67
"time"
78

@@ -21,6 +22,7 @@ type PfcpConnection struct {
2122
n3Address net.IP
2223
mapOperations ebpf.ForwardingPlaneController
2324
RecoveryTimestamp time.Time
25+
ResourceManager *service.ResourceManager
2426
}
2527

2628
func (connection *PfcpConnection) GetAssociation(assocAddr string) *NodeAssociation {
@@ -30,7 +32,7 @@ func (connection *PfcpConnection) GetAssociation(assocAddr string) *NodeAssociat
3032
return nil
3133
}
3234

33-
func CreatePfcpConnection(addr string, pfcpHandlerMap PfcpHandlerMap, nodeId string, n3Ip string, mapOperations ebpf.ForwardingPlaneController) (*PfcpConnection, error) {
35+
func CreatePfcpConnection(addr string, pfcpHandlerMap PfcpHandlerMap, nodeId string, n3Ip string, mapOperations ebpf.ForwardingPlaneController, resourceManager *service.ResourceManager) (*PfcpConnection, error) {
3436
udpAddr, err := net.ResolveUDPAddr("udp", addr)
3537
if err != nil {
3638
log.Warn().Msgf("Can't resolve UDP address: %s", err.Error())
@@ -57,6 +59,7 @@ func CreatePfcpConnection(addr string, pfcpHandlerMap PfcpHandlerMap, nodeId str
5759
n3Address: n3Addr,
5860
mapOperations: mapOperations,
5961
RecoveryTimestamp: time.Now(),
62+
ResourceManager: resourceManager,
6063
}, nil
6164
}
6265

@@ -146,8 +149,9 @@ func (connection *PfcpConnection) DeleteSession(session *Session) {
146149
for _, qer := range session.QERs {
147150
_ = connection.mapOperations.DeleteQer(qer.GlobalId)
148151
}
152+
pdrContext := NewPDRCreationContext(session, connection.ResourceManager)
149153
for _, PDR := range session.PDRs {
150-
_ = deletePDR(PDR, connection.mapOperations)
154+
_ = pdrContext.deletePDR(PDR, connection.mapOperations)
151155
}
152156
}
153157

@@ -162,3 +166,17 @@ func (connection *PfcpConnection) GetSessionCount() int {
162166
func (connection *PfcpConnection) GetAssiciationCount() int {
163167
return len(connection.NodeAssociations)
164168
}
169+
170+
func (connection *PfcpConnection) ReleaseResources(seID uint64) {
171+
if connection.ResourceManager == nil {
172+
return
173+
}
174+
175+
if connection.ResourceManager.IPAM != nil {
176+
connection.ResourceManager.IPAM.ReleaseIP(seID)
177+
}
178+
179+
if connection.ResourceManager.FTEIDM != nil {
180+
connection.ResourceManager.FTEIDM.ReleaseTEID(seID)
181+
}
182+
}

cmd/core/pfcp_handlers.go

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package core
22

33
import (
4+
"github.com/edgecomllc/eupf/cmd/config"
45
"net"
56
"time"
67

@@ -44,6 +45,11 @@ func (handlerMap PfcpHandlerMap) Handle(conn *PfcpConnection, buf []byte, addr *
4445
return nil
4546
}
4647

48+
func setBit(n uint8, pos uint) uint8 {
49+
n |= (1 << pos)
50+
return n
51+
}
52+
4753
// https://www.etsi.org/deliver/etsi_ts/129200_129299/129244/16.04.00_60/ts_129244v160400p.pdf page 95
4854
func HandlePfcpAssociationSetupRequest(conn *PfcpConnection, msg message.Message, addr string) (message.Message, error) {
4955
asreq := msg.(*message.AssociationSetupRequest)
@@ -86,14 +92,18 @@ func HandlePfcpAssociationSetupRequest(conn *PfcpConnection, msg message.Message
8692
conn.NodeAssociations[addr] = remoteNode
8793
log.Info().Msgf("Saving new association: %+v", remoteNode)
8894

95+
featuresOctets := []uint8{0, 0, 0}
96+
if config.Conf.FeatureFTUP {
97+
featuresOctets[0] = setBit(featuresOctets[0], 4)
98+
}
99+
100+
upFunctionFeaturesIE := ie.NewUPFunctionFeatures(featuresOctets...)
101+
89102
// shall send a PFCP Association Setup Response including:
90103
asres := message.NewAssociationSetupResponse(asreq.SequenceNumber,
91104
ie.NewCause(ie.CauseRequestAccepted), // a successful cause
92105
newIeNodeID(conn.nodeId), // its Node ID;
93-
ie.NewUPFunctionFeatures(), // information of all supported optional features in the UP function; We don't support any optional features at the moment
94-
// ... other IEs
95-
// optionally one or more UE IP address Pool Information IE which contains a list of UE IP Address Pool Identities per Network Instance, S-NSSAI and IP version;
96-
// optionally the NF Instance ID of the UPF if available
106+
upFunctionFeaturesIE,
97107
)
98108

99109
// Send AssociationSetupResponse

0 commit comments

Comments
 (0)