Skip to content

Commit

Permalink
[SDFAB-1110] Add support for application filter (#54)
Browse files Browse the repository at this point in the history
* First commit

* Fix increment of internal loop

* Add QER gating parameters

* Refactor

* Fix gate status

* Fix gate status

* Refactor

* Allow to set gate status

* Fix short command

* Fix incorrect gate setting. Use app QER

* Partial implementation of app filter flag

* Address review comments

* Address review comments

* Add support for app-filter string

* Address review comments

* Address review comments

* Add copyright header

* Address review comments
  • Loading branch information
EmanueleGallone authored Mar 11, 2022
1 parent b9f0961 commit c1c9a74
Show file tree
Hide file tree
Showing 8 changed files with 172 additions and 44 deletions.
24 changes: 7 additions & 17 deletions api/pfcpsim.pb.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 1 addition & 2 deletions api/pfcpsim.proto
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,8 @@ message CreateSessionRequest {
int32 baseID = 2;
string nodeBAddress = 3;
string ueAddressPool = 4;
string sdfFilter = 5;
string appFilter = 5;
int32 qfi = 6; // Should be uint8
bool gateClosed = 7;
}

message ModifySessionRequest {
Expand Down
6 changes: 3 additions & 3 deletions internal/pfcpctl/commands/services.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,10 @@ import (
log "github.com/sirupsen/logrus"
)

type associate struct {}
type disassociate struct {}
type associate struct{}
type disassociate struct{}
type configureRemoteAddresses struct {
RemotePeerAddress string `short:"r" long:"remote-peer-addr" default:"" description:"The remote PFCP agent address."`
RemotePeerAddress string `short:"r" long:"remote-peer-addr" default:"" description:"The remote PFCP agent address."`
N3InterfaceAddress string `short:"n" long:"n3-addr" default:"" description:"The IPv4 address of the UPF's N3 interface"`
}

Expand Down
26 changes: 12 additions & 14 deletions internal/pfcpctl/commands/sessions.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,32 +12,31 @@ import (
)

type commonArgs struct {
Count int `short:"c" long:"count" default:"1" description:"The number of sessions to create"`
BaseID int `short:"i" long:"baseID" default:"1" description:"The base ID to use"`
UePool string `short:"u" long:"ue-pool" default:"17.0.0.0/24" description:"The UE pool address"`
GnBAddress string `short:"g" long:"gnb-addr" description:"The (g/e)nodeB address"`
SDFfilter string `short:"s" long:"sdf-filter" description:"The SDF Filter to use"`
QFI uint8 `short:"q" long:"qfi" description:"The QFI value for QERs. Max value 64."`
GateClosed bool `short:"t" long:"gate-closed" description:"If set, the application QER gate status will be CLOSED"`
Count int `short:"c" long:"count" default:"1" description:"The number of sessions to create"`
BaseID int `short:"i" long:"baseID" default:"1" description:"The base ID to use"`
UePool string `short:"u" long:"ue-pool" default:"17.0.0.0/24" description:"The UE pool address"`
GnBAddress string `short:"g" long:"gnb-addr" description:"The UE pool address"`
AppFilterString string `short:"a" long:"app-filter" description:"Specify an application filter. Format: '<Protocol>:<IP>/<SubnetMask>:<Port>-<Port>:<action>' . e.g. 'udp:10.0.0.0/8:80-88:allow'"`
QFI uint8 `short:"q" long:"qfi" description:"The QFI value for QERs. Max value 64."`
}

type sessionCreate struct {
Args struct{
Args struct {
commonArgs
}
}

type sessionModify struct {
Args struct {
commonArgs
BufferFlag bool `short:"b" long:"buffer" description:"If set, downlink FARs will have the buffer flag set to true"`
BufferFlag bool `short:"b" long:"buffer" description:"If set, downlink FARs will have the buffer flag set to true"`
NotifyCPFlag bool `short:"n" long:"notifycp" description:"If set, downlink FARs will have the notify CP flag set to true"`
}
}

type sessionDelete struct {
Args struct{
Count int `short:"c" long:"count" default:"1" description:"The number of sessions to create"`
Args struct {
Count int `short:"c" long:"count" default:"1" description:"The number of sessions to create"`
BaseID int `short:"i" long:"baseID" default:"1" description:"The base ID to use"`
}
}
Expand Down Expand Up @@ -65,9 +64,8 @@ func (s *sessionCreate) Execute(args []string) error {
BaseID: int32(s.Args.BaseID),
NodeBAddress: s.Args.GnBAddress,
UeAddressPool: s.Args.UePool,
SdfFilter: s.Args.SDFfilter,
Qfi: int32(s.Args.QFI),
GateClosed: s.Args.GateClosed,
AppFilter: s.Args.AppFilterString,
Qfi: int32(s.Args.QFI),
})

if err != nil {
Expand Down
56 changes: 56 additions & 0 deletions internal/pfcpsim/helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,17 @@
package pfcpsim

import (
"fmt"
"net"
"strconv"
"strings"

"github.com/omec-project/pfcpsim/pkg/pfcpsim"
"github.com/wmnsk/go-pfcp/ie"
)

const sdfFilterFormat = "permit out %v from %v to assigned %v-%v"

func connectPFCPSim() error {
if sim == nil {
localAddr, err := getLocalAddress(interfaceName)
Expand Down Expand Up @@ -71,3 +77,53 @@ func getLocalAddress(interfaceName string) (net.IP, error) {

return nil, pfcpsim.NewNoValidInterfaceError()
}

// parseAppFilter parses an application filter. Returns a tuple formed by a formatted SDF filter
// and a uint8 representing the Application QER gate status. Returns error if fail occurs while validating the filter string.
func parseAppFilter(filter string) (string, uint8, error) {
result := strings.Split(filter, ":")
if len(result) != 4 {
return "", 0, pfcpsim.NewInvalidFormatError("Parser was not able to generate the correct number of arguments." +
" Please make sure to use the right format")
}

proto, ipNetAddr, portRange, action := result[0], result[1], result[2], result[3]

if !(proto == "ip" || proto == "udp" || proto == "tcp") {
return "", 0, pfcpsim.NewInvalidFormatError("Unsupported or unknown protocol.")
}

_, _, err := net.ParseCIDR(ipNetAddr)
if err != nil {
return "", 0, pfcpsim.NewInvalidFormatError("IP and subnet mask.", err)
}

portList := strings.Split(portRange, "-")
if !(len(portList) == 2) {
return "", 0, pfcpsim.NewInvalidFormatError("Port range. Please make sure to use dash '-' to separate the two ports")
}

lowerPort, err := strconv.Atoi(portList[0])
if err != nil {
return "", 0, pfcpsim.NewInvalidFormatError("Port range.", err)
}

upperPort, err := strconv.Atoi(portList[1])
if err != nil {
return "", 0, pfcpsim.NewInvalidFormatError("Port range.", err)
}

if lowerPort > upperPort {
return "", 0, pfcpsim.NewInvalidFormatError("Port range. Lower port is greater than upper port")
}

if !(action == "allow" || action == "deny") {
return "", 0, pfcpsim.NewInvalidFormatError("Action. Please make sure to use 'allow' or 'deny'")
}

if action == "allow" {
return fmt.Sprintf(sdfFilterFormat, proto, ipNetAddr, lowerPort, upperPort), ie.GateStatusOpen, nil
}

return fmt.Sprintf(sdfFilterFormat, proto, ipNetAddr, lowerPort, upperPort), ie.GateStatusClosed, nil
}
77 changes: 77 additions & 0 deletions internal/pfcpsim/helpers_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2022-present Open Networking Foundation

package pfcpsim

import (
"testing"

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

func Test_parseAppFilter(t *testing.T) {
type args struct {
filterString string
}

type want struct {
SDFFilter string
gateStatus uint8
}

tests := []struct {
name string
args *args
want *want
wantErr bool
}{
{name: "Correct app filter",
args: &args{
filterString: "udp:10.0.0.0/8:80-80:allow",
},
want: &want{
SDFFilter: "permit out udp from 10.0.0.0/8 to assigned 80-80",
gateStatus: ie.GateStatusOpen,
},
},
{name: "Correct app filter with deny",
args: &args{
filterString: "udp:10.0.0.0/8:80-80:deny",
},
want: &want{
SDFFilter: "permit out udp from 10.0.0.0/8 to assigned 80-80",
gateStatus: ie.GateStatusClosed,
},
},
{name: "incorrect app filter bad protocol",
args: &args{
filterString: "test:10.0.0.0/8:80-80:allow",
},
want: &want{},
wantErr: true,
},
{name: "incorrect app filter bad IP format",
args: &args{
filterString: "ip:10/8:80-80:allow",
},
want: &want{},
wantErr: true,
},
}

for _, tt := range tests {
t.Run(
tt.name, func(t *testing.T) {
filter, gateStatus, err := parseAppFilter(tt.args.filterString)
if tt.wantErr {
require.Error(t, err)
return
}

require.Equal(t, tt.want.SDFFilter, filter)
require.Equal(t, tt.want.gateStatus, gateStatus)
},
)
}
}
17 changes: 9 additions & 8 deletions internal/pfcpsim/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -127,18 +127,19 @@ func (P pfcpSimService) CreateSession(ctx context.Context, request *pb.CreateSes
var SDFFilter = ""
var qfi, gateStatus uint8 = 0, ieLib.GateStatusOpen

if request.GateClosed {
gateStatus = ieLib.GateStatusClosed
if request.AppFilter != "" {
SDFFilter, gateStatus, err = parseAppFilter(request.AppFilter)
if err != nil {
return &pb.Response{}, status.Error(codes.Aborted, err.Error())
}

log.Infof("Successfully parsed application filter. SDF Filter: %v", SDFFilter)
}

if request.Qfi != 0 {
qfi = uint8(request.Qfi)
}

if request.SdfFilter != "" {
SDFFilter = request.SdfFilter
}

for i := baseID; i < (count*2 + baseID); i = i + 2 {
// using variables to ease comprehension on how rules are linked together
uplinkTEID := uint32(i)
Expand All @@ -150,7 +151,7 @@ func (P pfcpSimService) CreateSession(ctx context.Context, request *pb.CreateSes
downlinkFarID := uint32(i + 1)

uplinkPdrID := uint16(i)
dowlinkPdrID := uint16(i + 1)
downlinkPdrID := uint16(i + 1)

sessQerID := uint32(i + 3)

Expand All @@ -174,7 +175,7 @@ func (P pfcpSimService) CreateSession(ctx context.Context, request *pb.CreateSes

// DownlinkPDR
session.NewPDRBuilder().
WithID(dowlinkPdrID).
WithID(downlinkPdrID).
WithMethod(session.Create).
WithPrecedence(100).
WithUEAddress(ueAddress.String()).
Expand Down
7 changes: 7 additions & 0 deletions pkg/pfcpsim/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,13 @@ func NewNotEnoughSessionsError(err ...error) *pfcpSimError {
}
}

func NewInvalidFormatError(what string, err ...error) *pfcpSimError {
return &pfcpSimError{
message: fmt.Sprintf("Invalid format: %v", what),
error: err,
}
}

func NewNoValidInterfaceError(err ...error) *pfcpSimError {
return &pfcpSimError{
message: "No valid interface found",
Expand Down

0 comments on commit c1c9a74

Please sign in to comment.