Skip to content

Commit

Permalink
[feg] s8_proxy fix TEIDs on Create Session and add DeleteSession to s…
Browse files Browse the repository at this point in the history
…8_cli and made s8 stateless (#5011)
  • Loading branch information
uri200 committed Mar 5, 2021
1 parent 08974c7 commit 5243a71
Show file tree
Hide file tree
Showing 24 changed files with 1,796 additions and 968 deletions.
493 changes: 252 additions & 241 deletions feg/cloud/go/protos/s8_proxy.pb.go

Large diffs are not rendered by default.

Expand Up @@ -46,15 +46,6 @@ func (s S8RelayRouter) CreateSession(
return client.CreateSession(ctx, req)
}

func (s S8RelayRouter) ModifyBearer(c context.Context, req *protos.ModifyBearerRequestPgw) (*protos.ModifyBearerResponsePgw, error) {
client, ctx, cancel, err := s.getS8Client(c, req.GetImsi())
if err != nil {
return nil, err
}
defer cancel()
return client.ModifyBearer(ctx, req)
}

func (s S8RelayRouter) DeleteSession(c context.Context, req *protos.DeleteSessionRequestPgw) (*protos.DeleteSessionResponsePgw, error) {
client, ctx, cancel, err := s.getS8Client(c, req.GetImsi())
if err != nil {
Expand Down
2 changes: 2 additions & 0 deletions feg/gateway/go.sum
Expand Up @@ -272,6 +272,7 @@ github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMyw
github.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4=
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.4 h1:L8R9j+yAqZuZjsqh/z+F1NCffTKKLShY6zXTItVIZ8M=
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/gofuzz v0.0.0-20161122191042-44d81051d367/go.mod h1:HP5RmnzzSNb993RKQDq4+1A4ia9nllfqcQFTQJedwGI=
github.com/google/gofuzz v0.0.0-20170612174753-24818f796faf/go.mod h1:HP5RmnzzSNb993RKQDq4+1A4ia9nllfqcQFTQJedwGI=
Expand Down Expand Up @@ -465,6 +466,7 @@ github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJ
github.com/ory/go-acc v0.2.6/go.mod h1:4Kb/UnPcT8qRAk3IAxta+hvVapdxTLWtrr7bFLlEgpw=
github.com/ory/viper v1.7.5/go.mod h1:ypOuyJmEUb3oENywQZRgeAMwqgOyDqwboO1tj3DjTaM=
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
github.com/pascaldekloe/goe v0.1.0 h1:cBOtyMzM9HTpWjXfbbunk26uA6nG3a8n06Wieeh0MwY=
github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k=
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
Expand Down
15 changes: 11 additions & 4 deletions feg/gateway/gtp/enriched_message/message.go
Expand Up @@ -20,21 +20,25 @@ import (
"github.com/wmnsk/go-gtp/gtpv2/message"
)

// MessageWithGrpc wraps Message interface so we can use is as a Message. It adds a field to store
// a GRPC message that should be the translation of the GPT message
// MessageWithGrpc wraps Message interface so we can use it as a Message.
// grpcMessage field to store the GRPC version of the Message
// err stores any possible error that were associated with parsing message into grpcMessage
type MessageWithGrpc struct {
message.Message // GTP
grpcMessage proto.Message // GRPC
err error
}

func NewMessageWithGrpc(gtpMessage message.Message, grpcMessage proto.Message) *MessageWithGrpc {
// NewMessageWithGrpc returns a full valid MessageWithGrpc which include all parameters
func NewMessageWithGrpc(gtpMessage message.Message, grpcMessage proto.Message, err error) *MessageWithGrpc {
return &MessageWithGrpc{
Message: gtpMessage,
grpcMessage: grpcMessage,
err: err,
}
}

func (m *MessageWithGrpc) GetGrpcMessage() proto.Message {
func (m MessageWithGrpc) GetGrpcMessage() proto.Message {
return m.grpcMessage
}

Expand All @@ -47,6 +51,9 @@ func ExtractGrpcMessageFromGtpMessage(incomingMsg message.Message) (proto.Messag
default:
return nil, fmt.Errorf("incomming message it is not MessageWithGrpc type %+v", incomingMsg)
}
if withGrpc.err != nil {
return nil, withGrpc.err
}
grpcMessage := withGrpc.GetGrpcMessage()
return grpcMessage, nil
}
131 changes: 78 additions & 53 deletions feg/gateway/gtp/gtp_client.go
Expand Up @@ -20,99 +20,115 @@ import (
"context"
"fmt"
"net"
"strconv"
"strings"
"time"

"github.com/golang/glog"
"github.com/wmnsk/go-gtp/gtpv2"
)

const (
GTPC_PORT = 0 // if set to 0, port is set automatically
DefaultGtpTimeout = 3 * time.Second
SGWControlPlaneIfType = gtpv2.IFTypeS5S8SGWGTPC

ANY_IP = "0.0.0.0"
GTPC_AUTO_PORT = 0 // if set to 0, port is set automatically
)

type Client struct {
*gtpv2.Conn
connType uint8
localAddr *net.UDPAddr
remoteAddr *net.UDPAddr
GtpTimeout time.Duration
}

// NewConnectedAutoClient creates a GTP client finding out automatically the local IP Address to
// be used to reach the remote IP.
// It checks if remote end is alive using echo.
// It runs the GTP-C server to serve incoming calls and responses.
func NewConnectedAutoClient(ctx context.Context, remoteIPAndPortStr string, connType uint8) (*Client, error) {
remoteAddr, err := net.ResolveUDPAddr("udp", remoteIPAndPortStr)
if err != nil {
return nil, fmt.Errorf("could not resolve remote address %s: %s", remoteIPAndPortStr, err)
// NewRunningClient creates a GTP-C client. It also runs the GTP-C server waiting for incomming calls
// localIpAndPort is in form ip:port (127.0.0.1:1)
// - In case localIpAndPort is empty it uses any IP and a random port
// - In case ip is not provided ( :port, or 0.0.0.0:port) it uses any interface
// - In case port is set to 0 it uses a random port ( 0.0.0.0:0, or 10.0.0.1:0)
// If you need to check server availability before any connection, use NewConnectedClient
func NewRunningClient(ctx context.Context, localIpAndPort string, connType uint8, gtpTimeout time.Duration) (*Client, error) {
if localIpAndPort == "" {
localIpAndPort = fmt.Sprintf("%s:%d", ANY_IP, GTPC_AUTO_PORT)
}
splitted := strings.Split(localIpAndPort, ":")
if len(splitted) != 2 {
return nil, fmt.Errorf("LocalIpAndPort must be formatted as IP:Port, but %s was received", localIpAndPort)
}
ip := splitted[0]
if ip == "" {
ip = ANY_IP
}
localAddrIp, err := GetOutboundIP(remoteAddr)
port, err := strconv.Atoi(splitted[1])
if err != nil {
return nil, fmt.Errorf("could not find local address automatically: %s", err)
return nil, fmt.Errorf("Failed to parse GTP port: %s", err)
}
ipAddr := net.ParseIP(ip)
if ipAddr == nil {
return nil, fmt.Errorf("Failed to parse IP address: %s from %s", ip, localIpAndPort)
}
localAddr := &net.UDPAddr{IP: localAddrIp, Port: GTPC_PORT, Zone: ""}

return NewConnectedClient(ctx, localAddr, remoteAddr, connType)
}

// NewConnectedClient creates a GTP-C client and checks with an echo if remote Addrs is
// available. It also runs the GTP-C server waiting for incoming calls
func NewConnectedClient(ctx context.Context, localAddr, remoteAddr *net.UDPAddr, connType uint8) (*Client, error) {
var err error
c := NewClient(localAddr, remoteAddr, connType)
c.Conn, err = gtpv2.Dial(ctx, localAddr, remoteAddr, connType, 0)
localAddr := &net.UDPAddr{IP: ipAddr, Port: port, Zone: ""}
c := newClient(localAddr, connType, gtpTimeout)
c.enable(localAddr)
err = c.run(ctx)
if err != nil {
return nil, fmt.Errorf("could not connect to GTP-C %s server: %s", remoteAddr.String(), err)
return nil, err
}
c.WaitUntilClientIsReady(0)
c.DisableValidation()
return c, nil
}

// NewRunningAutoClient creates a GTP-C client inding out automatically the local IP Address to
// NewConnectedAutoClient creates a GTP client finding out automatically the local IP Address to
// be used to reach the remote IP.
// It DOES NOT send initial echo to check if the server is alive
// It checks if remote end is alive using echo.
// It runs the GTP-C server to serve incoming calls and responses.
func NewRunningAutoClient(ctx context.Context, remoteIPAndPortStr string, connType uint8) (*Client, error) {
func NewConnectedAutoClient(ctx context.Context, remoteIPAndPortStr string, connType uint8, gtpTimeout time.Duration) (*Client, error) {
remoteAddr, err := net.ResolveUDPAddr("udp", remoteIPAndPortStr)
if err != nil {
return nil, fmt.Errorf("could not resolve remote address %s: %s", remoteIPAndPortStr, err)
}
localAddrIp, err := GetOutboundIP(remoteAddr)
localAddrIp, err := GetLocalOutboundIP(remoteAddr)
if err != nil {
return nil, fmt.Errorf("could not find local address automatically: %s", err)
}
localAddr := &net.UDPAddr{IP: localAddrIp, Port: GTPC_PORT, Zone: ""}
return NewRunningClient(ctx, localAddr, remoteAddr, connType)
localAddr := &net.UDPAddr{IP: localAddrIp, Port: GTPC_AUTO_PORT, Zone: ""}

return NewConnectedClient(ctx, localAddr, remoteAddr, connType, gtpTimeout)
}

// NewRunningClient creates a GTP-C client. It also runs the GTP-C server waiting for incomming calls
// If you need to check raddrs availability, use NewConnectedClient
func NewRunningClient(ctx context.Context, localAddr, remoteAddr *net.UDPAddr, connType uint8) (*Client, error) {
c := NewClient(localAddr, remoteAddr, connType)
c.Enable()
err := c.Run(ctx)
// NewConnectedClient creates a GTP-C client and checks with an echo if remote Addrs is
// available. It also runs the GTP-C server waiting for incoming calls
func NewConnectedClient(ctx context.Context, localAddr, remoteAddr *net.UDPAddr, connType uint8, gtpTimeout time.Duration) (*Client, error) {
var err error
c := newClient(localAddr, connType, gtpTimeout)
c.Conn, err = gtpv2.Dial(ctx, localAddr, remoteAddr, connType, 0)
if err != nil {
return nil, err
return nil, fmt.Errorf("could not connect to GTP-C %s server: %s", remoteAddr.String(), err)
}
c.DisableValidation()
return c, nil
}

// NewClient creates basic configuration structure for a GTP-C client. It does
// not starts any connection or server.
func NewClient(localAddr, remoteAddr *net.UDPAddr, connType uint8) *Client {
return &Client{
func newClient(localAddr *net.UDPAddr, connType uint8, gtpTimeout time.Duration) *Client {
cli := &Client{
connType: connType,
localAddr: localAddr,
remoteAddr: remoteAddr,
GtpTimeout: configOrDefaultTimeout(gtpTimeout),
}
return cli
}

// Enable just creates the object connection enabling messages to be sent
func (c *Client) Enable() {
c.Conn = gtpv2.NewConn(c.localAddr, c.connType, 0)
func (c *Client) enable(localAddr *net.UDPAddr) {
c.Conn = gtpv2.NewConn(localAddr, c.connType, 0)
}

// Run launches the actual GTP-C cluent which will be able to send and receive GTP-C messages
func (c *Client) Run(ctx context.Context) error {
func (c *Client) run(ctx context.Context) error {
if c.Conn == nil {
return fmt.Errorf("nil conn object. You may need to Enable the client first")
}
Expand All @@ -125,34 +141,43 @@ func (c *Client) Run(ctx context.Context) error {
return
}
}()
//TODO: remove this wait once there is a way to check when the listener is ready
return nil
}

func (c *Client) GetServerAddress() *net.UDPAddr {
return c.remoteAddr
}

func (c *Client) GetLocalAddress() *net.UDPAddr {
return c.localAddr
}

// Get preferred outbound ip of this machine
func GetOutboundIP(testIp *net.UDPAddr) (net.IP, error) {
func GetLocalOutboundIP(testIp *net.UDPAddr) (net.IP, error) {
connection, err := net.Dial("udp", testIp.String())
if err != nil {
return nil, err
}

defer connection.Close()
localAddr := connection.LocalAddr().(*net.UDPAddr)
return localAddr.IP, nil
}

// configOrDefaultTimeout sets a default timeout if config timeout is 0
func configOrDefaultTimeout(configTimeout time.Duration) time.Duration {
if configTimeout == 0 {
return DefaultGtpTimeout
}
return configTimeout
}

//TODO: remove this once we find a way to safely wait for initialization of the service
// WaitUntilClientIsReady is a hack to know when the client is ready and avoid null pointer issues using
// the GTP-C client too early. Since go-gtp doesn't offer any visibuility on the readines of the connection
// we use LocalAddrs as indicator
func (c *Client) WaitUntilClientIsReady(count int) {

// TODO: only use those 3 waits for debugging
time.Sleep(time.Millisecond * 20)
time.Sleep(time.Millisecond * 20)
time.Sleep(time.Millisecond * 20)

defer func() {

if count > 50 {
time.Sleep(time.Millisecond * 20)
}
Expand Down

0 comments on commit 5243a71

Please sign in to comment.