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

[SDFAB-1110] Add support for application filter #54

Merged
merged 22 commits into from
Mar 11, 2022
Merged
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
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) {
EmanueleGallone marked this conversation as resolved.
Show resolved Hide resolved
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