Skip to content

Commit

Permalink
Allow disabling VRF in UE (#64)
Browse files Browse the repository at this point in the history
refs #51

Signed-off-by: Junxiao Shi <git@mail1.yoursunny.com>
  • Loading branch information
yoursunny committed Jan 11, 2024
1 parent 10b428c commit 10da3f7
Show file tree
Hide file tree
Showing 7 changed files with 117 additions and 55 deletions.
13 changes: 11 additions & 2 deletions cmd/packetrusher.go
Original file line number Diff line number Diff line change
Expand Up @@ -93,8 +93,9 @@ func main() {
&cli.IntFlag{Name: "timeBeforeXnHandover", Value: 0, Aliases: []string{"xnh"}, Usage: "The time in ms, before triggering a UE handover using Xn Handover. 0 to disable handover. This requires at least two gNodeB, eg: two N2/N3 IPs."},
&cli.IntFlag{Name: "timeBeforeIdle", Value: 0, Aliases: []string{"idl"}, Usage: "The time in ms, before switching UE to Idle. 0 to disable Idling."},
&cli.IntFlag{Name: "numPduSessions", Value: 1, Aliases: []string{"nPdu"}, Usage: "The number of PDU Sessions to create"},
&cli.BoolFlag{Name: "loop", Aliases: []string{"l"}, Usage: "Enable the creation of the GTP-U tunnel interface."},
&cli.BoolFlag{Name: "loop", Aliases: []string{"l"}, Usage: "Register UEs in a loop."},
&cli.BoolFlag{Name: "tunnel", Aliases: []string{"t"}, Usage: "Enable the creation of the GTP-U tunnel interface."},
&cli.BoolFlag{Name: "tunnel-vrf", Value: true, Usage: "Enable/disable VRP usage of the GTP-U tunnel interface."},
&cli.BoolFlag{Name: "dedicatedGnb", Aliases: []string{"d"}, Usage: "Enable the creation of a dedicated gNB per UE. Require one IP on N2/N3 per gNB."},
&cli.PathFlag{Name: "pcap", Usage: "Capture traffic to given PCAP file when a path is given", Value: "./dump.pcap"},
},
Expand Down Expand Up @@ -122,7 +123,15 @@ func main() {
pcap.CaptureTraffic(c.Path("pcap"))
}

templates.TestMultiUesInQueue(numUes, c.Bool("tunnel"), c.Bool("dedicatedGnb"), c.Bool("loop"), c.Int("timeBetweenRegistration"), c.Int("timeBeforeDeregistration"), c.Int("timeBeforeNgapHandover"), c.Int("timeBeforeXnHandover"), c.Int("timeBeforeIdle"), c.Int("numPduSessions"))
tunnelMode := config.TunnelDisabled
if c.Bool("tunnel") {
if c.Bool("tunnel-vrf") {
tunnelMode = config.TunnelVrf
} else {
tunnelMode = config.TunnelTun
}
}
templates.TestMultiUesInQueue(numUes, tunnelMode, c.Bool("dedicatedGnb"), c.Bool("loop"), c.Int("timeBetweenRegistration"), c.Int("timeBeforeDeregistration"), c.Int("timeBeforeNgapHandover"), c.Int("timeBeforeXnHandover"), c.Int("timeBeforeIdle"), c.Int("numPduSessions"))

return nil
},
Expand Down
36 changes: 24 additions & 12 deletions config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,18 @@ import (
"gopkg.in/yaml.v2"
)

// TunnelMode indicates how to create a GTP-U tunnel interface in an UE.
type TunnelMode int

const (
// TunnelDisabled disables the GTP-U tunnel.
TunnelDisabled TunnelMode = iota
// TunnelPlain creates a TUN device only.
TunnelTun
// TunnelPlain creates a TUN device and a VRF device.
TunnelVrf
)

var config *Config

type Config struct {
Expand Down Expand Up @@ -51,18 +63,18 @@ type SliceSupportList struct {
}

type Ue struct {
Msin string `yaml:"msin"`
Key string `yaml:"key"`
Opc string `yaml:"opc"`
Amf string `yaml:"amf"`
Sqn string `yaml:"sqn"`
Dnn string `yaml:"dnn"`
RoutingIndicator string `yaml:"routingindicator"`
Hplmn Hplmn `yaml:"hplmn"`
Snssai Snssai `yaml:"snssai"`
Integrity Integrity `yaml:"integrity"`
Ciphering Ciphering `yaml:"ciphering"`
TunnelEnabled bool `yaml:"tunnelenabled"`
Msin string `yaml:"msin"`
Key string `yaml:"key"`
Opc string `yaml:"opc"`
Amf string `yaml:"amf"`
Sqn string `yaml:"sqn"`
Dnn string `yaml:"dnn"`
RoutingIndicator string `yaml:"routingindicator"`
Hplmn Hplmn `yaml:"hplmn"`
Snssai Snssai `yaml:"snssai"`
Integrity Integrity `yaml:"integrity"`
Ciphering Ciphering `yaml:"ciphering"`
TunnelMode TunnelMode `yaml:"-"`
}

type Hplmn struct {
Expand Down
29 changes: 20 additions & 9 deletions internal/control_test_engine/ue/context/context.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"encoding/hex"
"errors"
"fmt"
"my5G-RANTester/config"
"my5G-RANTester/internal/control_test_engine/gnb/context"
"my5G-RANTester/internal/control_test_engine/ue/scenario"
"net"
Expand Down Expand Up @@ -56,9 +57,9 @@ type UEContext struct {
amfInfo Amf

// TODO: Modify config so you can configure these parameters per PDUSession
Dnn string
Snssai models.Snssai
TunnelEnabled bool
Dnn string
Snssai models.Snssai
TunnelMode config.TunnelMode

// Sync primitive
scenarioChan chan scenario.ScenarioMessage
Expand All @@ -77,6 +78,7 @@ type UEPDUSession struct {
ueIP string
ueGnbIP net.IP
tun netlink.Link
rule *netlink.Rule
routeTun *netlink.Route
vrf *netlink.Vrf
stopSignal chan bool
Expand Down Expand Up @@ -110,7 +112,7 @@ type SECURITY struct {
func (ue *UEContext) NewRanUeContext(msin string,
ueSecurityCapability *nasType.UESecurityCapability,
k, opc, op, amf, sqn, mcc, mnc, routingIndicator, dnn string,
sst int32, sd string, tunnelEnabled bool, scenarioChan chan scenario.ScenarioMessage,
sst int32, sd string, tunnelMode config.TunnelMode, scenarioChan chan scenario.ScenarioMessage,
gnbInboundChannel chan context.UEMessage, id int) {

// added SUPI.
Expand Down Expand Up @@ -152,7 +154,7 @@ func (ue *UEContext) NewRanUeContext(msin string,

// added Domain Network Name.
ue.Dnn = dnn
ue.TunnelEnabled = tunnelEnabled
ue.TunnelMode = tunnelMode

// encode mcc and mnc for mobileIdentity5Gs.
resu := ue.GetMccAndMncInOctets()
Expand Down Expand Up @@ -290,10 +292,6 @@ func (ue *UEContext) Unlock() {
ue.lock.Unlock()
}

func (ue *UEContext) IsTunnelEnabled() bool {
return ue.TunnelEnabled
}

func (ue *UEContext) GetPduSession(pduSessionid uint8) (*UEPDUSession, error) {
if pduSessionid > 15 || ue.PduSession[pduSessionid-1] == nil {
return nil, errors.New("Unable to find GnbPDUSession ID " + string(pduSessionid))
Expand Down Expand Up @@ -363,6 +361,14 @@ func (pduSession *UEPDUSession) GetTunInterface() netlink.Link {
return pduSession.tun
}

func (pduSession *UEPDUSession) SetTunRule(rule *netlink.Rule) {
pduSession.rule = rule
}

func (pduSession *UEPDUSession) GetTunRule() *netlink.Rule {
return pduSession.rule
}

func (pduSession *UEPDUSession) SetTunRoute(route *netlink.Route) {
pduSession.routeTun = route
}
Expand Down Expand Up @@ -696,6 +702,7 @@ func (ue *UEContext) Terminate() {
for _, pduSession := range ue.PduSession {
if pduSession != nil {
ueTun := pduSession.GetTunInterface()
ueRule := pduSession.GetTunRule()
ueRoute := pduSession.GetTunRoute()
ueVrf := pduSession.GetVrfDevice()

Expand All @@ -704,6 +711,10 @@ func (ue *UEContext) Terminate() {
_ = netlink.LinkDel(ueTun)
}

if ueRule != nil {
_ = netlink.RuleDel(ueRule)
}

if ueRoute != nil {
_ = netlink.RouteDel(ueRoute)
}
Expand Down
68 changes: 45 additions & 23 deletions internal/control_test_engine/ue/gtp/service/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ package service

import (
"fmt"
"my5G-RANTester/config"
gtpLink "my5G-RANTester/internal/cmd/gogtp5g-link"
gtpTunnel "my5G-RANTester/internal/cmd/gogtp5g-tunnel"
gnbContext "my5G-RANTester/internal/control_test_engine/gnb/context"
Expand All @@ -29,7 +30,7 @@ func SetupGtpInterface(ue *context.UEContext, msg gnbContext.UEMessage) {
}
pduSession.GnbPduSession = gnbPduSession

if !ue.IsTunnelEnabled() {
if ue.TunnelMode == config.TunnelDisabled {
log.Info(fmt.Sprintf("[UE][GTP] Interface for UE %s has not been created. Tunnel has been disabled.", ue.GetMsin()))
return
}
Expand Down Expand Up @@ -117,33 +118,48 @@ func SetupGtpInterface(ue *context.UEContext, msg gnbContext.UEMessage) {

tableId, _ := strconv.Atoi(fmt.Sprint(gnbPduSession.GetTeidUplink()))

vrfDevice := &netlink.Vrf{
LinkAttrs: netlink.LinkAttrs{
Name: vrfInf,
},
Table: uint32(tableId),
}
_ = netlink.LinkDel(vrfDevice)
switch ue.TunnelMode {
case config.TunnelTun:
rule := netlink.NewRule()
rule.Priority = 100
rule.Table = tableId
rule.Src = addrTun.IPNet
_ = netlink.RuleDel(rule)

if err := netlink.LinkAdd(vrfDevice); err != nil {
log.Fatal("[UE][DATA] Unable to create VRF for UE", err)
return
}
if err := netlink.RuleAdd(rule); err != nil {
log.Fatal("[UE][DATA] Unable to create routing policy rule for UE", err)
return
}
pduSession.SetTunRule(rule)
case config.TunnelVrf:
vrfDevice := &netlink.Vrf{
LinkAttrs: netlink.LinkAttrs{
Name: vrfInf,
},
Table: uint32(tableId),
}
_ = netlink.LinkDel(vrfDevice)

if err := netlink.LinkSetMaster(link, vrfDevice); err != nil {
log.Fatal("[UE][DATA] Unable to set GTP tunnel as slave of VRF interface", err)
return
}
if err := netlink.LinkAdd(vrfDevice); err != nil {
log.Fatal("[UE][DATA] Unable to create VRF for UE", err)
return
}

if err := netlink.LinkSetUp(vrfDevice); err != nil {
log.Fatal("[UE][DATA] Unable to set interface VRF UP", err)
return
if err := netlink.LinkSetMaster(link, vrfDevice); err != nil {
log.Fatal("[UE][DATA] Unable to set GTP tunnel as slave of VRF interface", err)
return
}

if err := netlink.LinkSetUp(vrfDevice); err != nil {
log.Fatal("[UE][DATA] Unable to set interface VRF UP", err)
return
}
pduSession.SetVrfDevice(vrfDevice)
}
pduSession.SetVrfDevice(vrfDevice)

route := &netlink.Route{
Dst: &net.IPNet{IP: net.IPv4zero, Mask: net.CIDRMask(0, 32)}, // default
LinkIndex: link.Attrs().Index, // dev gtp-<ECI>
LinkIndex: link.Attrs().Index, // dev val<MSIN>
Scope: netlink.SCOPE_LINK, // scope link
Protocol: 4, // proto static
Priority: 1, // metric 1
Expand All @@ -156,6 +172,12 @@ func SetupGtpInterface(ue *context.UEContext, msg gnbContext.UEMessage) {
pduSession.SetTunRoute(route)

log.Info(fmt.Sprintf("[UE][GTP] Interface %s has successfully been configured for UE %s", nameInf, ueIp))
log.Info(fmt.Sprintf("[UE][GTP] You can do traffic for this UE using VRF %s, eg:", vrfInf))
log.Info(fmt.Sprintf("[UE][GTP] sudo ip vrf exec %s iperf3 -c IPERF_SERVER -p PORT -t 9000", vrfInf))
switch ue.TunnelMode {
case config.TunnelTun:
log.Info(fmt.Sprintf("[UE][GTP] You can do traffic for this UE by binding to IP %s, eg:", ueIp))
log.Info(fmt.Sprintf("[UE][GTP] iperf3 -B %s -c IPERF_SERVER -p PORT -t 9000", ueIp))
case config.TunnelVrf:
log.Info(fmt.Sprintf("[UE][GTP] You can do traffic for this UE using VRF %s, eg:", vrfInf))
log.Info(fmt.Sprintf("[UE][GTP] sudo ip vrf exec %s iperf3 -c IPERF_SERVER -p PORT -t 9000", vrfInf))
}
}
2 changes: 1 addition & 1 deletion internal/control_test_engine/ue/ue.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ func NewUE(conf config.Config, id int, ueMgrChannel chan procedures.UeTesterMess
conf.Ue.Dnn,
int32(conf.Ue.Snssai.Sst),
conf.Ue.Snssai.Sd,
conf.Ue.TunnelEnabled,
conf.Ue.TunnelMode,
scenarioChan,
gnbInboundChannel,
id)
Expand Down
8 changes: 7 additions & 1 deletion internal/templates/test-attach-ue-with-configuration.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,12 @@
*/
package templates

import "my5G-RANTester/config"

func TestAttachUeWithConfiguration(tunnelEnabled bool) {
TestMultiUesInQueue(1, tunnelEnabled, true, false, 500, 0, 0, 0, 0, 1)
tunnelMode := config.TunnelDisabled
if tunnelEnabled {
tunnelMode = config.TunnelVrf
}
TestMultiUesInQueue(1, tunnelMode, true, false, 500, 0, 0, 0, 0, 1)
}
16 changes: 9 additions & 7 deletions internal/templates/test-multi-ues-in-queue.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,14 @@ import (
log "github.com/sirupsen/logrus"
)

func TestMultiUesInQueue(numUes int, tunnelEnabled bool, dedicatedGnb bool, loop bool, timeBetweenRegistration int, timeBeforeDeregistration int, timeBeforeNgapHandover int, timeBeforeXnHandover int, timeBeforeIdle int, numPduSessions int) {
if tunnelEnabled && !dedicatedGnb {
log.Fatal("You cannot use the --tunnel option, without using the --dedicatedGnb option")
}
if tunnelEnabled && timeBetweenRegistration < 500 {
log.Fatal("When using the --tunnel option, --timeBetweenRegistration must be equal to at least 500 ms, or else gtp5g kernel module may crash if you create tunnels too rapidly.")
func TestMultiUesInQueue(numUes int, tunnelMode config.TunnelMode, dedicatedGnb bool, loop bool, timeBetweenRegistration int, timeBeforeDeregistration int, timeBeforeNgapHandover int, timeBeforeXnHandover int, timeBeforeIdle int, numPduSessions int) {
if tunnelMode != config.TunnelDisabled {
if !dedicatedGnb {
log.Fatal("You cannot use the --tunnel option, without using the --dedicatedGnb option")
}
if timeBetweenRegistration < 500 {
log.Fatal("When using the --tunnel option, --timeBetweenRegistration must be equal to at least 500 ms, or else gtp5g kernel module may crash if you create tunnels too rapidly.")
}
}

if numPduSessions > 16 {
Expand All @@ -48,7 +50,7 @@ func TestMultiUesInQueue(numUes int, tunnelEnabled bool, dedicatedGnb bool, loop
// TODO: We should wait for NGSetupResponse instead
time.Sleep(1 * time.Second)

cfg.Ue.TunnelEnabled = tunnelEnabled
cfg.Ue.TunnelMode = tunnelMode

scenarioChans := make([]chan procedures.UeTesterMessage, numUes+1)

Expand Down

0 comments on commit 10da3f7

Please sign in to comment.