Skip to content
Draft
Show file tree
Hide file tree
Changes from 11 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
43 changes: 28 additions & 15 deletions cmd/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package config
import (
"log"

"github.com/fsnotify/fsnotify"
"github.com/go-playground/validator/v10"
"github.com/spf13/pflag"
"github.com/spf13/viper"
Expand All @@ -11,21 +12,21 @@ import (
var v = viper.GetViper()

type UpfConfig struct {
InterfaceName []string `mapstructure:"interface_name"`
XDPAttachMode string `mapstructure:"xdp_attach_mode" validate:"oneof=generic native offload"`
ApiAddress string `mapstructure:"api_address" validate:"hostname_port"`
PfcpAddress string `mapstructure:"pfcp_address" validate:"hostname_port"`
PfcpNodeId string `mapstructure:"pfcp_node_id" validate:"hostname|ip"`
MetricsAddress string `mapstructure:"metrics_address" validate:"hostname_port"`
N3Address string `mapstructure:"n3_address" validate:"ipv4"`
QerMapSize uint32 `mapstructure:"qer_map_size" validate:"min=1"`
FarMapSize uint32 `mapstructure:"far_map_size" validate:"min=1"`
PdrMapSize uint32 `mapstructure:"pdr_map_size" validate:"min=1"`
EbpfMapResize bool `mapstructure:"resize_ebpf_maps"`
HeartbeatRetries uint32 `mapstructure:"heartbeat_retries"`
HeartbeatInterval uint32 `mapstructure:"heartbeat_interval"`
HeartbeatTimeout uint32 `mapstructure:"heartbeat_timeout"`
LoggingLevel string `mapstructure:"logging_level"`
InterfaceName []string `mapstructure:"interface_name" json:"interface_name"`
XDPAttachMode string `mapstructure:"xdp_attach_mode" validate:"oneof=generic native offload" json:"xdp_attach_mode"`
ApiAddress string `mapstructure:"api_address" validate:"hostname_port" json:"api_address"`
PfcpAddress string `mapstructure:"pfcp_address" validate:"hostname_port" json:"pfcp_address"`
PfcpNodeId string `mapstructure:"pfcp_node_id" validate:"hostname|ip" json:"pfcp_node_id"`
MetricsAddress string `mapstructure:"metrics_address" validate:"hostname_port" json:"metrics_address"`
N3Address string `mapstructure:"n3_address" validate:"ipv4" json:"n3_address"`
QerMapSize uint32 `mapstructure:"qer_map_size" validate:"min=1" json:"qer_map_size"`
FarMapSize uint32 `mapstructure:"far_map_size" validate:"min=1" json:"far_map_size"`
PdrMapSize uint32 `mapstructure:"pdr_map_size" validate:"min=1" json:"pdr_map_size"`
EbpfMapResize bool `mapstructure:"resize_ebpf_maps" json:"resize_ebpf_maps"`
HeartbeatRetries uint32 `mapstructure:"heartbeat_retries" json:"heartbeat_retries"`
HeartbeatInterval uint32 `mapstructure:"heartbeat_interval" json:"heartbeat_interval"`
HeartbeatTimeout uint32 `mapstructure:"heartbeat_timeout" json:"heartbeat_timeout"`
LoggingLevel string `mapstructure:"logging_level" validate:"required" json:"logging_level"`
}

func init() {
Expand Down Expand Up @@ -86,8 +87,11 @@ func init() {
v.SetEnvPrefix("upf")
v.AutomaticEnv()

configFileAvailable = true
Comment thread
kade-ddnkv marked this conversation as resolved.
Outdated

if err := v.ReadInConfig(); err != nil {
log.Printf("Unable to read config file, %v", err)
configFileAvailable = false
}

log.Printf("Get raw config: %+v", v.AllSettings())
Expand All @@ -101,3 +105,12 @@ func (c *UpfConfig) Validate() error {
func (c *UpfConfig) Unmarshal() error {
return v.UnmarshalExact(c)
}

var configFileAvailable bool

func SetWatchConfig(onConfigChange func(e fsnotify.Event)) {
Comment thread
kade-ddnkv marked this conversation as resolved.
Outdated
if configFileAvailable {
viper.OnConfigChange(onConfigChange)
viper.WatchConfig()
}
}
16 changes: 16 additions & 0 deletions cmd/config/startup.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,19 @@ func Init() {

log.Printf("Apply eUPF config: %+v", Conf)
}

func GetUpdatedConf() (UpfConfig, error) {
Comment thread
kade-ddnkv marked this conversation as resolved.
Outdated
var updatedConf UpfConfig
if err := updatedConf.Unmarshal(); err != nil {
log.Fatalf("Unable to decode into struct, %v", err)
return UpfConfig{}, err
}

if err := updatedConf.Validate(); err != nil {
log.Fatalf("eUPF config is invalid: %v", err)
return UpfConfig{}, err
}

log.Printf("Apply updated eUPF config: %+v", updatedConf)
Comment thread
kade-ddnkv marked this conversation as resolved.
Outdated
return updatedConf, nil
}
28 changes: 26 additions & 2 deletions cmd/core/api.go
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
package core

import (
"fmt"
"net"
"net/http"
"strconv"
"unsafe"

"github.com/edgecomllc/eupf/cmd/config"
"github.com/edgecomllc/eupf/cmd/ebpf"

"github.com/edgecomllc/eupf/cmd/config"
eupfDocs "github.com/edgecomllc/eupf/cmd/docs"
"github.com/gin-contrib/cors"
"github.com/gin-gonic/gin"
Expand All @@ -33,10 +34,15 @@ func CreateApiServer(bpfObjects *ebpf.BpfObjects, pfcpSrv *PfcpConnection, forwa
v1 := router.Group("/api/v1")
{
v1.GET("upf_pipeline", ListUpfPipeline(bpfObjects))
v1.GET("config", DisplayConfig())
v1.GET("xdp_stats", DisplayXdpStatistics(forwardPlaneStats))
v1.GET("packet_stats", DisplayPacketStats(forwardPlaneStats))

config := v1.Group("config")
{
config.GET("", DisplayConfig())
config.POST("", EditConfig)
}

qerMap := v1.Group("qer_map")
{
qerMap.GET("", ListQerMapContent(bpfObjects))
Expand All @@ -60,6 +66,24 @@ func CreateApiServer(bpfObjects *ebpf.BpfObjects, pfcpSrv *PfcpConnection, forwa
return &ApiServer{router: router}
}

func EditConfig(c *gin.Context) {
Comment thread
kade-ddnkv marked this conversation as resolved.
Outdated
var conf config.UpfConfig
if err := c.BindJSON(&conf); err != nil {
c.IndentedJSON(http.StatusBadRequest, gin.H{
"message": "Request body json has incorrect format. Use one or more fields from the following structure",
"correctFormat": config.UpfConfig{},
})
return
}
if err := SetConfig(conf); err != nil {
c.IndentedJSON(http.StatusBadRequest, gin.H{
"message": fmt.Sprintf("Error during editing config: %s", err.Error()),
})
} else {
c.Status(http.StatusOK)
}
}

type PacketStats struct {
RxArp uint64 `json:"rx_arp"`
RxIcmp uint64 `json:"rx_icmp"`
Expand Down
18 changes: 18 additions & 0 deletions cmd/core/config_edit.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package core

import (
"fmt"

"github.com/edgecomllc/eupf/cmd/config"

"github.com/rs/zerolog"
)

func SetConfig(conf config.UpfConfig) error {
// For now only logging_level parameter update in config.UpfConfig is supported.
if err := SetLoggerLevel(conf.LoggingLevel); err != nil {
return fmt.Errorf("Logger configuring error: %s. Using '%s' level",
err.Error(), zerolog.GlobalLevel().String())
}
return nil
}
28 changes: 28 additions & 0 deletions cmd/core/logger.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package core

import (
"fmt"
"os"

"github.com/edgecomllc/eupf/cmd/config"
"github.com/rs/zerolog"
"github.com/rs/zerolog/log"
)

func InitLogger() {
output := zerolog.ConsoleWriter{Out: os.Stdout, TimeFormat: "2006/01/02 15:04:05"}
log.Logger = zerolog.New(output).With().Timestamp().Logger()
}

func SetLoggerLevel(loggingLevel string) error {
if loggingLevel == "" {
return fmt.Errorf("Logging level can't be empty")
}
if loglvl, err := zerolog.ParseLevel(loggingLevel); err == nil {
zerolog.SetGlobalLevel(loglvl)
config.Conf.LoggingLevel = zerolog.GlobalLevel().String()
} else {
return fmt.Errorf("Can't parse logging level: '%s'", loggingLevel)
}
return nil
}
24 changes: 15 additions & 9 deletions cmd/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (

"github.com/edgecomllc/eupf/cmd/core"
"github.com/edgecomllc/eupf/cmd/ebpf"
"github.com/fsnotify/fsnotify"

"github.com/cilium/ebpf/link"
"github.com/edgecomllc/eupf/cmd/config"
Expand All @@ -27,7 +28,10 @@ func main() {

// Warning: inefficient log writing.
// As zerolog docs says: "Pretty logging on the console is made possible using the provided (but inefficient) zerolog.ConsoleWriter."
ConfigureLogger()
core.InitLogger()
if err := core.SetLoggerLevel(config.Conf.LoggingLevel); err != nil {
log.Error().Msgf("Logger configuring error: %s. Using '%s' level", err.Error(), zerolog.GlobalLevel().String())
}

if err := ebpf.IncreaseResourceLimits(); err != nil {
log.Fatal().Msgf("Can't increase resource limits: %s", err.Error())
Expand Down Expand Up @@ -104,6 +108,9 @@ func main() {
}
}()

// Handle config file change.
config.SetWatchConfig(OnConfigFileChange)

// Print the contents of the BPF hash map (source IP address -> packet count).
ticker := time.NewTicker(5 * time.Second)
defer ticker.Stop()
Expand Down Expand Up @@ -137,13 +144,12 @@ func StringToXDPAttachMode(Mode string) link.XDPAttachFlags {
}
}

func ConfigureLogger() {
output := zerolog.ConsoleWriter{Out: os.Stdout, TimeFormat: "2006/01/02 15:04:05"}
log.Logger = zerolog.New(output).With().Timestamp().Logger()

if loglvl, err := zerolog.ParseLevel(config.Conf.LoggingLevel); err == nil {
zerolog.SetGlobalLevel(loglvl)
} else {
log.Warn().Msgf("Can't parse logging level: %s. Using \"info\" level.", err.Error())
func OnConfigFileChange(e fsnotify.Event) {
if e.Has(fsnotify.Create) || e.Has(fsnotify.Write) {
if conf, err := config.GetUpdatedConf(); err == nil {
if err := core.SetConfig(conf); err != nil {
log.Error().Msgf("Error during config file change handling: %s", err.Error())
}
}
}
}