Skip to content

Commit

Permalink
feat: support configuring local IP address for N9 interface with N3 a…
Browse files Browse the repository at this point in the history
…ddress by default (#585)

Co-authored-by: Pau Espin Pedrol <[email protected]>
  • Loading branch information
pirog-spb and pespin authored Dec 23, 2024
1 parent 72cd736 commit 307b80a
Show file tree
Hide file tree
Showing 10 changed files with 52 additions and 11 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ Use following command to mount it: `sudo mount bpffs /sys/fs/bpf -t bpf`

- UPF_INTERFACE_NAME=lo *Network interfaces handling N3 (GTP) & N6 (SGi) traffic.*
- UPF_N3_ADDRESS=127.0.0.1 *IPv4 address for N3 interface*
- UPF_N9_ADDRESS=127.0.0.1 *IPv4 address for N9 interface*
- UPF_XDP_ATTACH_MODE=generic *XDP attach mode. Generic-only at the moment*
- UPF_API_ADDRESS=:8080 *Local host:port for serving [REST API](api.md) server*
- UPF_PFCP_ADDRESS=:8805 *Local host:port that PFCP server will listen to*
Expand Down
8 changes: 8 additions & 0 deletions cmd/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ type UpfConfig struct {
AssociationSetupTimeout uint32 `mapstructure:"association_setup_timeout" json:"association_setup_timeout"`
MetricsAddress string `mapstructure:"metrics_address" validate:"hostname_port" json:"metrics_address"`
N3Address string `mapstructure:"n3_address" validate:"ipv4" json:"n3_address"`
N9Address string `mapstructure:"n9_address" validate:"ipv4" json:"n9_address"`
GtpPeer []string `mapstructure:"gtp_peer" validate:"omitempty,dive,hostname_port" json:"gtp_peer"`
GtpEchoInterval uint32 `mapstructure:"gtp_echo_interval" validate:"min=1" json:"gtp_echo_interval"`
QerMapSize uint32 `mapstructure:"qer_map_size" validate:"min=1" json:"qer_map_size"`
Expand Down Expand Up @@ -47,6 +48,7 @@ func init() {
pflag.String("nodeid", "127.0.0.1", "PFCP Server Node ID")
pflag.String("maddr", ":9090", "Address to bind metrics server to")
pflag.String("n3addr", "127.0.0.1", "Address for communication over N3 interface")
pflag.String("n9addr", "n3addr", "Address for communication over N9 interface")
pflag.StringArray("peer", []string{}, "Address of GTP peer")
pflag.Uint32("echo", 10, "Interval of sending echo requests in seconds")
pflag.Uint32("qersize", 1024, "Size of the QER ebpf map")
Expand Down Expand Up @@ -76,6 +78,7 @@ func init() {
_ = v.BindPFlag("association_setup_timeout", pflag.Lookup("astimeout"))
_ = v.BindPFlag("metrics_address", pflag.Lookup("maddr"))
_ = v.BindPFlag("n3_address", pflag.Lookup("n3addr"))
_ = v.BindPFlag("n9_address", pflag.Lookup("n9addr"))
_ = v.BindPFlag("gtp_peer", pflag.Lookup("peer"))
_ = v.BindPFlag("gtp_echo_interval", pflag.Lookup("echo"))
_ = v.BindPFlag("qer_map_size", pflag.Lookup("qersize"))
Expand All @@ -100,6 +103,7 @@ func init() {
v.SetDefault("association_setup_timeout", 5)
v.SetDefault("metrics_address", ":9090")
v.SetDefault("n3_address", "127.0.0.1")
v.SetDefault("n9_address", "n3addr")
v.SetDefault("gtp_echo_interval", 10)
v.SetDefault("qer_map_size", 1024)
v.SetDefault("far_map_size", 1024)
Expand Down Expand Up @@ -146,6 +150,10 @@ func (c *UpfConfig) Validate() error {
c.UEIPPool = ""
}

if c.N9Address == "n3addr" {
c.N9Address = c.N3Address
}

return nil
}

Expand Down
11 changes: 9 additions & 2 deletions cmd/core/pfcp_connection.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ type PfcpConnection struct {
nodeId string
nodeAddrV4 net.IP
n3Address net.IP
n9Address net.IP
mapOperations ebpf.ForwardingPlaneController
RecoveryTimestamp time.Time
featuresOctets []uint8
Expand All @@ -55,7 +56,7 @@ func (connection *PfcpConnection) GetAssociation(assocAddr string) *NodeAssociat
return nil
}

func NewPfcpConnection(addr string, nodeId string, n3Ip string, mapOperations ebpf.ForwardingPlaneController, resourceManager *service.ResourceManager) (*PfcpConnection, error) {
func NewPfcpConnection(addr string, nodeId string, n3Ip string, n9Ip string, mapOperations ebpf.ForwardingPlaneController, resourceManager *service.ResourceManager) (*PfcpConnection, error) {
udpAddr, err := net.ResolveUDPAddr("udp", addr)
if err != nil {
log.Warn().Msgf("Can't resolve UDP address: %s", err.Error())
Expand All @@ -71,7 +72,12 @@ func NewPfcpConnection(addr string, nodeId string, n3Ip string, mapOperations eb
if n3Addr == nil {
return nil, fmt.Errorf("failed to parse N3 IP address ID: %s", n3Ip)
}
log.Info().Msgf("Starting PFCP connection: %v with Node ID: %v and N3 address: %v", udpAddr, nodeId, n3Addr)
n9Addr := net.ParseIP(n9Ip)
if n9Addr == nil {
return nil, fmt.Errorf("failed to parse N9 IP address ID: %s", n9Ip)
}

log.Info().Msgf("Starting PFCP connection: %v with Node ID: %v, N3 address: %v, N9 address: %v", udpAddr, nodeId, n3Addr, n9Addr)

featuresOctets := []uint8{0, 0, 0}
featuresOctets[1] = setBit(featuresOctets[1], 0)
Expand All @@ -90,6 +96,7 @@ func NewPfcpConnection(addr string, nodeId string, n3Ip string, mapOperations eb
nodeId: nodeId,
nodeAddrV4: udpAddr.IP,
n3Address: n3Addr,
n9Address: n9Addr,
mapOperations: mapOperations,
RecoveryTimestamp: time.Now(),
featuresOctets: featuresOctets,
Expand Down
28 changes: 21 additions & 7 deletions cmd/core/pfcp_session_handlers.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ func HandlePfcpSessionEstablishmentRequest(conn *PfcpConnection, msg message.Mes
err = func() error {
mapOperations := conn.mapOperations
for _, far := range req.CreateFAR {
farInfo, err := composeFarInfo(far, conn.n3Address.To4(), ebpf.FarInfo{})
farInfo, err := composeFarInfo(far, conn.n3Address.To4(), conn.n9Address.To4(), ebpf.FarInfo{})
if err != nil {
log.Info().Msgf("Error extracting FAR info: %s", err.Error())
continue
Expand Down Expand Up @@ -256,7 +256,7 @@ func HandlePfcpSessionModificationRequest(conn *PfcpConnection, msg message.Mess
mapOperations := conn.mapOperations

for _, far := range req.CreateFAR {
farInfo, err := composeFarInfo(far, conn.n3Address.To4(), ebpf.FarInfo{})
farInfo, err := composeFarInfo(far, conn.n3Address.To4(), conn.n9Address.To4(), ebpf.FarInfo{})
if err != nil {
log.Info().Msgf("Error extracting FAR info: %s", err.Error())
continue
Expand All @@ -278,7 +278,7 @@ func HandlePfcpSessionModificationRequest(conn *PfcpConnection, msg message.Mess
return err
}
sFarInfo := session.GetFar(farid)
sFarInfo.FarInfo, err = composeFarInfo(far, conn.n3Address.To4(), sFarInfo.FarInfo)
sFarInfo.FarInfo, err = composeFarInfo(far, conn.n3Address.To4(), conn.n9Address.To4(), sFarInfo.FarInfo)
if err != nil {
log.Info().Msgf("Error extracting FAR info: %s", err.Error())
continue
Expand Down Expand Up @@ -553,14 +553,14 @@ func cloneIP(ip net.IP) net.IP {
return dup
}

func composeFarInfo(far *ie.IE, localIp net.IP, farInfo ebpf.FarInfo) (ebpf.FarInfo, error) {
farInfo.LocalIP = binary.LittleEndian.Uint32(localIp)
func composeFarInfo(far *ie.IE, localN3Ip net.IP, localN9Ip net.IP, farInfo ebpf.FarInfo) (ebpf.FarInfo, error) {
if applyAction, err := far.ApplyAction(); err == nil {
farInfo.Action = applyAction[0]
}
var forward []*ie.IE
var err error
if far.Type == ie.CreateFAR {
farInfo.LocalIP = binary.LittleEndian.Uint32(localN3Ip)
forward, err = far.ForwardingParameters()
} else if far.Type == ie.UpdateFAR {
forward, err = far.UpdateForwardingParameters()
Expand All @@ -570,18 +570,32 @@ func composeFarInfo(far *ie.IE, localIp net.IP, farInfo ebpf.FarInfo) (ebpf.FarI
if err == nil {
outerHeaderCreationIndex := findIEindex(forward, 84) // IE Type Outer Header Creation
if outerHeaderCreationIndex == -1 {
log.Info().Msg("WARN: No OuterHeaderCreation")
log.Warn().Msg("No OuterHeaderCreation")
} else {
outerHeaderCreation, _ := forward[outerHeaderCreationIndex].OuterHeaderCreation()
farInfo.OuterHeaderCreation = uint8(outerHeaderCreation.OuterHeaderCreationDescription >> 8)

farInfo.Teid = outerHeaderCreation.TEID
if outerHeaderCreation.HasIPv4() {
farInfo.RemoteIP = binary.LittleEndian.Uint32(outerHeaderCreation.IPv4Address)
}
if outerHeaderCreation.HasIPv6() {
log.Info().Msg("WARN: IPv6 not supported yet, ignoring")
log.Warn().Msg("IPv6 not supported yet, ignoring")
return ebpf.FarInfo{}, fmt.Errorf("IPv6 not supported yet")
}

destInterfaceIndex := findIEindex(forward, 42) // IE Destination Interface
if destInterfaceIndex == -1 {
log.Warn().Msg("No Destination Interface IE")
} else {
destInterface, _ := forward[destInterfaceIndex].DestinationInterface()
// OuterHeaderCreation == GTP-U/UDP/IPv4 && DestinationInterface == Core:
if (farInfo.OuterHeaderCreation&0x01) == 0x01 && (destInterface == 0x01) {
farInfo.LocalIP = binary.LittleEndian.Uint32(localN9Ip)
} else {
farInfo.LocalIP = binary.LittleEndian.Uint32(localN3Ip)
}
}
}
}
transportLevelMarking, err := GetTransportLevelMarking(far)
Expand Down
4 changes: 3 additions & 1 deletion cmd/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,9 @@ func main() {
}

// Create PFCP connection
pfcpConn, err := core.NewPfcpConnection(config.Conf.PfcpAddress, config.Conf.PfcpNodeId, config.Conf.N3Address, bpfObjects, resourceManager)
pfcpConn, err := core.NewPfcpConnection(config.Conf.PfcpAddress, config.Conf.PfcpNodeId,
config.Conf.N3Address, config.Conf.N9Address,
bpfObjects, resourceManager)
if err != nil {
log.Fatal().Msgf("Could not create PFCP connection: %s", err.Error())
}
Expand Down
3 changes: 3 additions & 0 deletions docs/Configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ Currently UPF have several config parameters shown below.<br>Parameters can be c
|--------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-------------------|-----------------------|-------------|-----------------|
| Interface name<br>`Mandatory` | List of network interfaces handling N3 (GTP) & N6 (SGi) traffic. eUPF attaches XDP hook to every interface in this list. Format: `[ifnameA, ifnameB, ...]`. | `interface_name` | `UPF_INTERFACE_NAME` | `--iface` | `lo` |
| N3 address <br>`Mandatory` | IPv4 address for N3 interface | `n3_address` | `UPF_N3_ADDRESS` | `--n3addr` | `127.0.0.1` |
| N9 address <br>`Optional` | IPv4 address for N9 interface | `n9_address` | `UPF_N9_ADDRESS` | `--n9addr` | `n3_address` |
| XDP mode <br>`Optional` | XDP attach mode: <br> ∘ **generic** – Kernel-level implementation. For evaluation purpose. <br> ∘ **native** – Driver-level implenemntaion <br> ∘ **offload** – NIC-level implementation. XDP can be loaded and executed directly on the NIC. <br> Refer to [How XDP Works](https://www.tigera.io/learn/guides/ebpf/ebpf-xdp/#How-XDP-Works) | `xdp_attach_mode` | `UPF_XDP_ATTACH_MODE` | `--attach` | `generic` |
| API address <br>`Optional` | Local address for serving [REST API](api.md) server | `api_address` | `UPF_API_ADDRESS` | `--aaddr` | `:8080` |
| PFCP address <br>`Optional` | Local address that PFCP server will listen to | `pfcp_address` | `UPF_PFCP_ADDRESS` | `--paddr` | `:8805` |
Expand Down Expand Up @@ -46,6 +47,7 @@ pfcp_address: :8805
pfcp_node_id: 127.0.0.1
metrics_address: :9090
n3_address: 127.0.0.1
n9_address: 127.0.0.1
qer_map_size: 1024
far_map_size: 1024
pdr_map_size: 1024
Expand All @@ -65,6 +67,7 @@ UPF_PFCP_ADDRESS=:8806
UPF_METRICS_ADDRESS=:9091
UPF_PFCP_NODE_ID: 10.100.50.241 # address on n4 interface
UPF_N3_ADDRESS: 10.100.50.233
UPF_N9_ADDRESS: 10.100.50.233
```
### CLI
Expand Down
1 change: 1 addition & 0 deletions docs/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ In addition to prometheus metrics the eUPF API provides a set of endpoints for m
"pfcp_node_id": "10.100.200.14",
"metrics_address": ":9090",
"n3_address": "10.100.200.14",
"n9_address": "10.100.200.14",
"qer_map_size": 1024,
"far_map_size": 1024,
"pdr_map_size": 1024,
Expand Down
1 change: 1 addition & 0 deletions docs/deployments/free5gc-ulcl/eupf-b.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ configMaps:
env:
UPF_PFCP_NODE_ID: 10.100.50.241 # address on n4 interface
# UPF_N3_ADDRESS: 10.100.50.233
# UPF_N9_ADDRESS: 10.100.50.233

volumes:
- name: sys
Expand Down
5 changes: 4 additions & 1 deletion docs/docs-ru_ru/Configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@
| Параметр | Описание | yaml | env | cli arg | Значение по умолчанию |
|--------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-------------------|-----------------------|-------------|-------------|
| Interface name<br>`Обязательный` | Список сетевых интерфейсов, обрабатывающих трафик N3 (GTP) и N6 (SGi). eUPF присоединяет перехватчик XDP к каждому интерфейсу в этом списке. Формат: `[ifnameA, ifnameB, ...]`. | `interface_name` | `UPF_INTERFACE_NAME` | `--iface` | `lo` |
| N3 address <br>`Обязательный` | IPv4 адрея для N3 интерфейса | `n3_address` | `UPF_N3_ADDRESS` | `--n3addr` | `127.0.0.1` |
| N3 address <br>`Обязательный` | IPv4 адреc для N3 интерфейса | `n3_address` | `UPF_N3_ADDRESS` | `--n3addr` | `127.0.0.1` |
| N9 address <br>`Дополнительный` | IPv4 адреc для N9 интерфейса. | `n9_address` | `UPF_N9_ADDRESS` | `--n9addr` | `n3_address` |
| XDP mode <br>`Дополнительный` | XDP attach mode: <br> ∘ **generic** – Реализация на уровне ядра. В целях оценки. <br> ∘ **native** – реализация на уровне драйвера <br> ∘ **offload** – реализация на уровне NIC. XDP можно загрузить и выполнить непосредственно на сетевой карте. <br> См. [Как работает XDP](https://www.tigera.io/learn/guides/ebpf/ebpf-xdp/#How-XDP-Works) | `xdp_attach_mode` | `UPF_XDP_ATTACH_MODE` | `--attach` | `generic` |
| API address <br>`Дополнительный` | Локальный адрес для обслуживания сервера [REST API](../../docs/api.md) | `api_address` | `UPF_API_ADDRESS` | `--aaddr` | `:8080` |
| PFCP address <br>`Дополнительный` | Локальный адрес, по которому буедт доступен PFCP server | `pfcp_address` | `UPF_PFCP_ADDRESS` | `--paddr` | `:8805` |
Expand Down Expand Up @@ -43,6 +44,7 @@ pfcp_address: :8805
pfcp_node_id: 127.0.0.1
metrics_address: :9090
n3_address: 127.0.0.1
n9_address: 127.0.0.1
qer_map_size: 1024
far_map_size: 1024
pdr_map_size: 1024
Expand All @@ -60,6 +62,7 @@ UPF_PFCP_ADDRESS=:8806
UPF_METRICS_ADDRESS=:9091
UPF_PFCP_NODE_ID: 10.100.50.241 # address on n4 interface
UPF_N3_ADDRESS: 10.100.50.233
UPF_N9_ADDRESS: 10.100.50.233
```
### CLI
Expand Down
1 change: 1 addition & 0 deletions docs/docs-ru_ru/readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ sudo docker run -d --rm --privileged \

- UPF_INTERFACE_NAME=lo *Network interfaces handling N3 (GTP) & N6 (SGi) traffic.*
- UPF_N3_ADDRESS=127.0.0.1 *IPv4 address for N3 interface*
- UPF_N9_ADDRESS=127.0.0.1 *IPv4 address for N9 interface*
- UPF_XDP_ATTACH_MODE=generic *XDP attach mode. Generic-only at the moment*
- UPF_API_ADDRESS=:8080 *Local host:port for serving [REST API](api.md) server*
- UPF_PFCP_ADDRESS=:8805 *Local host:port that PFCP server will listen to*
Expand Down

0 comments on commit 307b80a

Please sign in to comment.