diff --git a/README.md b/README.md index 909100d4..83c4ba03 100644 --- a/README.md +++ b/README.md @@ -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* diff --git a/cmd/config/config.go b/cmd/config/config.go index c044ff6c..72c2d1f3 100644 --- a/cmd/config/config.go +++ b/cmd/config/config.go @@ -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"` @@ -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") @@ -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")) @@ -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) @@ -146,6 +150,10 @@ func (c *UpfConfig) Validate() error { c.UEIPPool = "" } + if c.N9Address == "n3addr" { + c.N9Address = c.N3Address + } + return nil } diff --git a/cmd/core/pfcp_connection.go b/cmd/core/pfcp_connection.go index 61d2e799..33301a0c 100644 --- a/cmd/core/pfcp_connection.go +++ b/cmd/core/pfcp_connection.go @@ -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 @@ -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()) @@ -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) @@ -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, diff --git a/cmd/core/pfcp_session_handlers.go b/cmd/core/pfcp_session_handlers.go index b201c45b..631e1145 100644 --- a/cmd/core/pfcp_session_handlers.go +++ b/cmd/core/pfcp_session_handlers.go @@ -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 @@ -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 @@ -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 @@ -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() @@ -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) diff --git a/cmd/main.go b/cmd/main.go index 6d042428..39932bd2 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -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()) } diff --git a/docs/Configuration.md b/docs/Configuration.md index 853cbb96..a73ec43a 100644 --- a/docs/Configuration.md +++ b/docs/Configuration.md @@ -8,6 +8,7 @@ Currently UPF have several config parameters shown below.
Parameters can be c |--------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-------------------|-----------------------|-------------|-----------------| | Interface name
`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
`Mandatory` | IPv4 address for N3 interface | `n3_address` | `UPF_N3_ADDRESS` | `--n3addr` | `127.0.0.1` | +| N9 address
`Optional` | IPv4 address for N9 interface | `n9_address` | `UPF_N9_ADDRESS` | `--n9addr` | `n3_address` | | XDP mode
`Optional` | XDP attach mode:
∘ **generic** – Kernel-level implementation. For evaluation purpose.
∘ **native** – Driver-level implenemntaion
∘ **offload** – NIC-level implementation. XDP can be loaded and executed directly on the NIC.
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
`Optional` | Local address for serving [REST API](api.md) server | `api_address` | `UPF_API_ADDRESS` | `--aaddr` | `:8080` | | PFCP address
`Optional` | Local address that PFCP server will listen to | `pfcp_address` | `UPF_PFCP_ADDRESS` | `--paddr` | `:8805` | @@ -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 @@ -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 diff --git a/docs/api.md b/docs/api.md index 350c92aa..6a7addfd 100644 --- a/docs/api.md +++ b/docs/api.md @@ -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, diff --git a/docs/deployments/free5gc-ulcl/eupf-b.yaml b/docs/deployments/free5gc-ulcl/eupf-b.yaml index eb834da6..aadd8b4b 100644 --- a/docs/deployments/free5gc-ulcl/eupf-b.yaml +++ b/docs/deployments/free5gc-ulcl/eupf-b.yaml @@ -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 diff --git a/docs/docs-ru_ru/Configuration.md b/docs/docs-ru_ru/Configuration.md index b820442a..8a7543c5 100644 --- a/docs/docs-ru_ru/Configuration.md +++ b/docs/docs-ru_ru/Configuration.md @@ -7,7 +7,8 @@ | Параметр | Описание | yaml | env | cli arg | Значение по умолчанию | |--------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-------------------|-----------------------|-------------|-------------| | Interface name
`Обязательный` | Список сетевых интерфейсов, обрабатывающих трафик N3 (GTP) и N6 (SGi). eUPF присоединяет перехватчик XDP к каждому интерфейсу в этом списке. Формат: `[ifnameA, ifnameB, ...]`. | `interface_name` | `UPF_INTERFACE_NAME` | `--iface` | `lo` | -| N3 address
`Обязательный` | IPv4 адрея для N3 интерфейса | `n3_address` | `UPF_N3_ADDRESS` | `--n3addr` | `127.0.0.1` | +| N3 address
`Обязательный` | IPv4 адреc для N3 интерфейса | `n3_address` | `UPF_N3_ADDRESS` | `--n3addr` | `127.0.0.1` | +| N9 address
`Дополнительный` | IPv4 адреc для N9 интерфейса. | `n9_address` | `UPF_N9_ADDRESS` | `--n9addr` | `n3_address` | | XDP mode
`Дополнительный` | XDP attach mode:
∘ **generic** – Реализация на уровне ядра. В целях оценки.
∘ **native** – реализация на уровне драйвера
∘ **offload** – реализация на уровне NIC. XDP можно загрузить и выполнить непосредственно на сетевой карте.
См. [Как работает XDP](https://www.tigera.io/learn/guides/ebpf/ebpf-xdp/#How-XDP-Works) | `xdp_attach_mode` | `UPF_XDP_ATTACH_MODE` | `--attach` | `generic` | | API address
`Дополнительный` | Локальный адрес для обслуживания сервера [REST API](../../docs/api.md) | `api_address` | `UPF_API_ADDRESS` | `--aaddr` | `:8080` | | PFCP address
`Дополнительный` | Локальный адрес, по которому буедт доступен PFCP server | `pfcp_address` | `UPF_PFCP_ADDRESS` | `--paddr` | `:8805` | @@ -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 @@ -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 diff --git a/docs/docs-ru_ru/readme.md b/docs/docs-ru_ru/readme.md index 34a0d080..1b7143a6 100644 --- a/docs/docs-ru_ru/readme.md +++ b/docs/docs-ru_ru/readme.md @@ -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*