Skip to content

Commit

Permalink
Remove internal application and release ID if not used by any UE sess…
Browse files Browse the repository at this point in the history
…ion (#583)
  • Loading branch information
Tomasz Osiński committed Mar 25, 2022
1 parent fff9c9b commit ec5aa0e
Show file tree
Hide file tree
Showing 4 changed files with 147 additions and 110 deletions.
164 changes: 113 additions & 51 deletions pfcpiface/up4.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,20 @@ var (
p4RtcServerPort = flag.String("p4RtcServerPort", "", "P4 Server port")
)

type application struct {
// internalAppReference <F-SEID (UE session); PDR-ID> pair that
// uniquely identifies application filter among different PDR IEs of the same UE session.
type internalAppReference struct {
fseid uint64
pdrID uint32
}

type internalApp struct {
id uint8
// usedBy keeps track of <F-SEID (UE session); PDR-ID> pairs using this application filter.
usedBy set.Set
}

type up4ApplicationFilter struct {
appIP uint32
appL4Port portRange
appProto uint8
Expand Down Expand Up @@ -120,7 +133,8 @@ type UP4 struct {
tunnelPeerMu sync.Mutex
tunnelPeerIDs map[tunnelParams]tunnelPeer
tunnelPeerIDsPool []uint8
applicationIDs map[application]uint8
applicationMu sync.Mutex
applicationIDs map[up4ApplicationFilter]internalApp
applicationIDsPool []uint8

// meters stores the mapping from <F-SEID; QER ID> -> P4 Meter Cell ID.
Expand All @@ -142,6 +156,25 @@ type UP4 struct {
endMarkerChan chan []byte
}

func toUP4ApplicationFilter(p pdr) up4ApplicationFilter {
var appFilter up4ApplicationFilter
if p.IsUplink() {
appFilter = up4ApplicationFilter{
appIP: p.appFilter.dstIP,
appL4Port: p.appFilter.dstPortRange,
}
} else if p.IsDownlink() {
appFilter = up4ApplicationFilter{
appIP: p.appFilter.srcIP,
appL4Port: p.appFilter.srcPortRange,
}
}

appFilter.appProto = p.appFilter.proto

return appFilter
}

func (m meter) String() string {
return fmt.Sprintf("Meter(type=%d, uplinkCellID=%d, downlinkCellID=%d)",
m.meterType, m.uplinkCellID, m.downlinkCellID)
Expand Down Expand Up @@ -346,7 +379,7 @@ func (up4 *UP4) initTunnelPeerIDs() {
}

func (up4 *UP4) initApplicationIDs() {
up4.applicationIDs = make(map[application]uint8)
up4.applicationIDs = make(map[up4ApplicationFilter]internalApp)
// a simple queue storing available application IDs
// 0 is reserved;
up4.applicationIDsPool = make([]uint8, 0, maxApplicationIDs)
Expand Down Expand Up @@ -757,7 +790,7 @@ func (up4 *UP4) removeGTPTunnelPeer(far far) {
}

// Returns error if we reach maximum supported Application IDs.
func (up4 *UP4) allocateInternalApplicationID(app application) (uint8, error) {
func (up4 *UP4) unsafeAllocateInternalApplicationID() (uint8, error) {
if len(up4.applicationIDsPool) == 0 {
return 0, ErrOperationFailedWithReason("allocate Application ID",
"no free application IDs available")
Expand All @@ -767,53 +800,83 @@ func (up4 *UP4) allocateInternalApplicationID(app application) (uint8, error) {
allocated := up4.applicationIDsPool[0]
up4.applicationIDsPool = up4.applicationIDsPool[1:]

up4.applicationIDs[app] = allocated

return allocated, nil
}

// FIXME: SDFAB-960
//nolint:unused
func (up4 *UP4) releaseInternalApplicationID(appFilter applicationFilter) {
app := application{
appIP: appFilter.srcIP,
appL4Port: appFilter.srcPortRange,
appProto: appFilter.proto,
func (up4 *UP4) unsafeReleaseInternalApplicationID(appFilter up4ApplicationFilter) {
allocated, exists := up4.applicationIDs[appFilter]
if exists {
up4.applicationIDsPool = append(up4.applicationIDsPool, allocated.id)
delete(up4.applicationIDs, appFilter)
}
}

allocated, exists := up4.applicationIDs[app]
if exists {
delete(up4.applicationIDs, app)
up4.applicationIDsPool = append(up4.applicationIDsPool, allocated)
func (up4 *UP4) addInternalApplicationIDAndGetP4rtEntry(pdr pdr) (*p4.TableEntry, uint8, error) {
up4.applicationMu.Lock()
defer up4.applicationMu.Unlock()

appFilter := toUP4ApplicationFilter(pdr)
if up4Application, exists := up4.applicationIDs[appFilter]; exists {
// application already exists, increment 'usedBy'.
// since we use Set usedBy will not be incremented if
// application was already created for this UE session + PDR ID.
up4Application.usedBy.Add(internalAppReference{
pdr.fseID, pdr.pdrID,
})

return nil, up4Application.id, nil
}

newAppID, err := up4.unsafeAllocateInternalApplicationID()
if err != nil {
return nil, 0, err
}

up4Application := internalApp{
id: newAppID,
usedBy: set.NewSet(internalAppReference{
pdr.fseID, pdr.pdrID,
}),
}

applicationsEntry, err := up4.p4RtTranslator.BuildApplicationsTableEntry(pdr, up4.conf.SliceID, newAppID)
if err != nil {
up4.unsafeReleaseInternalApplicationID(appFilter)
return nil, 0, ErrOperationFailedWithReason("build P4rt table entry for Applications table", err.Error())
}

up4.applicationIDs[appFilter] = up4Application

return applicationsEntry, up4Application.id, nil
}

func (up4 *UP4) getOrAllocateInternalApplicationID(pdr pdr) (uint8, error) {
var app application
if pdr.IsUplink() {
app = application{
appIP: pdr.appFilter.dstIP,
appL4Port: pdr.appFilter.dstPortRange,
}
} else if pdr.IsDownlink() {
app = application{
appIP: pdr.appFilter.srcIP,
appL4Port: pdr.appFilter.srcPortRange,
}
func (up4 *UP4) removeInternalApplicationIDAndGetP4rtEntry(pdr pdr) (*p4.TableEntry, uint8) {
up4.applicationMu.Lock()
defer up4.applicationMu.Unlock()

appFilter := toUP4ApplicationFilter(pdr)

internalApp, exists := up4.applicationIDs[appFilter]
if !exists {
return nil, 0
}

app.appProto = pdr.appFilter.proto
internalApp.usedBy.Remove(internalAppReference{
pdr.fseID, pdr.pdrID,
})

if allocated, exists := up4.applicationIDs[app]; exists {
return allocated, nil
if internalApp.usedBy.Cardinality() != 0 {
return nil, internalApp.id
}

newAppID, err := up4.allocateInternalApplicationID(app)
applicationsEntry, err := up4.p4RtTranslator.BuildApplicationsTableEntry(pdr, up4.conf.SliceID, internalApp.id)
if err != nil {
return 0, err
return nil, internalApp.id
}

return newAppID, nil
up4.unsafeReleaseInternalApplicationID(appFilter)

return applicationsEntry, internalApp.id
}

func (up4 *UP4) allocateAppMeterCellID() (uint32, error) {
Expand Down Expand Up @@ -1257,27 +1320,26 @@ func (up4 *UP4) modifyUP4ForwardingConfiguration(pdrs []pdr, allFARs []far, qers
pdr.ueAddress = ueAddr
}

var applicationsEntry *p4.TableEntry

// as a default value is installed if no application filtering rule exists
var applicationID uint8 = DefaultApplicationID

if !pdr.IsAppFilterEmpty() {
applicationID, err = up4.getOrAllocateInternalApplicationID(pdr)
if err != nil {
pdrLog.Error("failed to get or allocate internal application ID")
return err
}
}
if methodType != p4.Update_DELETE {
if entry, appID, err := up4.addInternalApplicationIDAndGetP4rtEntry(pdr); err == nil {
if entry != nil {
entriesToApply = append(entriesToApply, entry)
}

// TODO: the same app filter can be simultaneously used by another UE session. We cannot remove it.
// We should come up with a way to check if an app filter is still in use.
if applicationID != 0 && methodType != p4.Update_DELETE {
applicationsEntry, err = up4.p4RtTranslator.BuildApplicationsTableEntry(pdr, up4.conf.SliceID, applicationID)
if err != nil {
return ErrOperationFailedWithReason("build P4rt table entry for Applications table", err.Error())
}
applicationID = appID
}
} else {
entry, appID := up4.removeInternalApplicationIDAndGetP4rtEntry(pdr)
if entry != nil {
entriesToApply = append(entriesToApply, entry)
}

entriesToApply = append(entriesToApply, applicationsEntry)
applicationID = appID
}
}

var appMeter = meter{meterTypeApplication, 0, 0}
Expand Down
49 changes: 10 additions & 39 deletions test/integration/basic_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import (
"time"

"github.com/omec-project/pfcpsim/pkg/pfcpsim/session"
"github.com/omec-project/upf-epc/test/integration/providers"
"github.com/stretchr/testify/require"
"github.com/wmnsk/go-pfcp/ie"
)
Expand Down Expand Up @@ -41,9 +40,7 @@ func TestUPFBasedUeIPAllocation(t *testing.T) {
expected: p4RtValues{
// first IP address from pool configured in ue_ip_alloc.json
ueAddress: "10.250.0.1",
// no application filtering rule expected
appID: 0,
tc: 3,
tc: 3,
},
desc: "UPF-based UE IP allocation",
}
Expand Down Expand Up @@ -90,8 +87,7 @@ func TestSingleUEAttachAndDetach(t *testing.T) {
80, 80,
},
},
appID: 1,
tc: 3,
tc: 3,
},
desc: "APPLICATION FILTERING permit out udp from any 80-80 to assigned",
},
Expand All @@ -115,10 +111,7 @@ func TestSingleUEAttachAndDetach(t *testing.T) {
80, 100,
},
},
// FIXME: there is a dependency on previous test because pfcpiface doesn't clear application IDs properly
// See SDFAB-960
appID: 2,
tc: 3,
tc: 3,
},
desc: "APPLICATION FILTERING permit out udp from 192.168.1.1/32 to assigned 80-100",
},
Expand All @@ -135,8 +128,7 @@ func TestSingleUEAttachAndDetach(t *testing.T) {
},
expected: p4RtValues{
// no application filtering rule expected
appID: 0,
tc: 3,
tc: 3,
},
desc: "APPLICATION FILTERING ALLOW_ALL",
},
Expand Down Expand Up @@ -168,8 +160,7 @@ func TestSingleUEAttachAndDetach(t *testing.T) {
80, 80,
},
},
appID: 1,
tc: 3,
tc: 3,
},
desc: "QER_METERING - 1 session QER, 2 app QERs",
},
Expand Down Expand Up @@ -201,8 +192,7 @@ func TestSingleUEAttachAndDetach(t *testing.T) {
80, 80,
},
},
appID: 1,
tc: 3,
tc: 3,
},
desc: "QER_METERING - session QER only",
},
Expand Down Expand Up @@ -234,8 +224,7 @@ func TestSingleUEAttachAndDetach(t *testing.T) {
80, 80,
},
},
appID: 1,
tc: 2,
tc: 2,
},
desc: "QER_METERING - TC for QFI",
},
Expand Down Expand Up @@ -268,8 +257,7 @@ func TestSingleUEAttachAndDetach(t *testing.T) {
80, 80,
},
},
appID: 1,
tc: 2,
tc: 2,
},
desc: "QER UL gating",
},
Expand Down Expand Up @@ -302,8 +290,7 @@ func TestSingleUEAttachAndDetach(t *testing.T) {
80, 80,
},
},
appID: 1,
tc: 2,
tc: 2,
},
desc: "QER DL gating",
},
Expand Down Expand Up @@ -340,8 +327,7 @@ func TestUEBuffering(t *testing.T) {
80, 80,
},
},
appID: 1,
tc: 3,
tc: 3,
},
}

Expand Down Expand Up @@ -514,19 +500,4 @@ func testUEDetach(t *testing.T, testcase *testCase) {
func testUEAttachDetach(t *testing.T, testcase *testCase) {
testUEAttach(t, testcase)
testUEDetach(t, testcase)

if isDatapathUP4() {
// clear Applications table
// FIXME: Temporary solution. They should be cleared by pfcpiface, see SDFAB-960
p4rtClient, _ := providers.ConnectP4rt("127.0.0.1:50001", TimeBasedElectionId())
defer func() {
providers.DisconnectP4rt()
// give pfcpiface time to become master controller again
time.Sleep(3 * time.Second)
}()
entries, _ := p4rtClient.ReadTableEntryWildcard("PreQosPipe.applications")
for _, entry := range entries {
p4rtClient.DeleteTableEntry(entry)
}
}
}
1 change: 0 additions & 1 deletion test/integration/framework.go
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,6 @@ type appFilter struct {
type p4RtValues struct {
tc uint8
ueAddress string
appID uint8
appFilter appFilter

pdrs []*ie.IE
Expand Down
Loading

0 comments on commit ec5aa0e

Please sign in to comment.