Skip to content

Commit

Permalink
[feg] S8_proxy additon of delete session
Browse files Browse the repository at this point in the history
Signed-off-by: Oriol Batalla <obatalla@fb.com>
  • Loading branch information
uri200 committed Feb 25, 2021
1 parent 3c25393 commit 1a08d0f
Show file tree
Hide file tree
Showing 16 changed files with 655 additions and 372 deletions.
505 changes: 292 additions & 213 deletions feg/cloud/go/protos/s8_proxy.pb.go

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,7 @@ type SubscriptionData struct {
APNConfigurationProfile APNConfigurationProfile `avp:"APN-Configuration-Profile"`
SubscribedPeriodicRauTauTimer uint32 `avp:"Subscribed-Periodic-RAU-TAU-Timer"`
TgppChargingCharacteristics string `avp:"TGPP-Charging-Characteristics"`
RegionalSubscriptionZoneCode []datatype.OctetString `avp:"Regional-Subscription-Zone-Code"`
RegionalSubscriptionZoneCode []datatype.OctetString `avp:"Regional-Subscription-Zone-Code"`
}

type ULA struct {
Expand Down
2 changes: 1 addition & 1 deletion feg/gateway/services/s6a_proxy/servicers/s6a_proxy_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ func TestS6aProxyService(t *testing.T) {
t.Errorf("Unexpected ULA Error Code: %d", ulResp.ErrorCode)
}
if len(ulResp.RegionalSubscriptionZoneCode) != 2 ||
(ulResp.RegionalSubscriptionZoneCode[0] != "112233" || ulResp.RegionalSubscriptionZoneCode[1]!= "445566") {
(ulResp.RegionalSubscriptionZoneCode[0] != "112233" || ulResp.RegionalSubscriptionZoneCode[1] != "445566") {
t.Errorf("There should be 2 Regional Subscription Zone Codes : %+v", ulResp.RegionalSubscriptionZoneCode)
}

Expand Down
14 changes: 6 additions & 8 deletions feg/gateway/services/s8_proxy/client_api_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,8 @@ import (
)

const (
IMSI1 = "001010000000055"
)

const (
IMSI1 = "001010000000055"
BEARER = 5
PGW_ADDRS = "127.0.0.1:0"
S8_PROXY_ADDRS = ":0"
)
Expand Down Expand Up @@ -44,9 +42,9 @@ func TestS8ProxyClient(t *testing.T) {
Mcc: "222",
Mnc: "333",
},
RatType: 0,
RatType: protos.RATType_EUTRAN,
BearerContext: &protos.BearerContext{
Id: 5,
Id: BEARER,
UserPlaneFteid: &protos.Fteid{
Ipv4Address: "127.0.0.10",
Ipv6Address: "",
Expand Down Expand Up @@ -117,8 +115,8 @@ func TestS8ProxyClient(t *testing.T) {

//------------------------
//---- Delete session ----
cdReq := &protos.DeleteSessionRequestPgw{Imsi: IMSI1}
_, err = s8_proxy.DeleteSession(cdReq)
dsReq := &protos.DeleteSessionRequestPgw{Imsi: IMSI1}
_, err = s8_proxy.DeleteSession(dsReq)
assert.NoError(t, err)

//------------------------
Expand Down
39 changes: 34 additions & 5 deletions feg/gateway/services/s8_proxy/servicers/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,10 @@ limitations under the License.
package servicers

import (
"net"
"os"
"strconv"
"strings"

mcfgprotos "magma/feg/cloud/go/protos/mconfig"
"magma/gateway/mconfig"
Expand All @@ -33,17 +36,43 @@ func GetS8ProxyConfig() *S8ProxyConfig {
configPtr := &mcfgprotos.S8Config{}
err := mconfig.GetServiceConfigs(S8ProxyServiceName, configPtr)
if err != nil {
glog.V(2).Infof("%s Managed Configs Load Error: %v\nUsing EnvVars", S8ProxyServiceName, err)
glog.V(2).Infof("%s Managed Configs Load Error: %v Using EnvVars", S8ProxyServiceName, err)
return &S8ProxyConfig{
ClientAddr: os.Getenv(ClientAddrEnv),
ServerAddr: os.Getenv(ServerAddrEnv),
ServerAddr: ParseAddress(os.Getenv(ServerAddrEnv)),
}
}

glog.V(2).Infof("Loaded %+v configs: %+v", S8ProxyConfig{}, *configPtr)

return &S8ProxyConfig{
ClientAddr: configPtr.LocalAddress,
ServerAddr: configPtr.PgwAddress,
ServerAddr: ParseAddress(configPtr.PgwAddress),
}
}

//parseAddress will parse an ip:port address. If parse fails it will just return nil
func ParseAddress(ipAndPort string) *net.UDPAddr {
if ipAndPort == "" {
return nil
}
splitted := strings.Split(ipAndPort, ":")
if len(splitted) != 2 {
glog.Warningf("Malformed address. It must be formatted as IP:Port, but %s was received", ipAndPort)
return nil
}
ip := splitted[0]
if ip == "" {
glog.Warningf("Empty IP during parsing address on config file: %s", ipAndPort)
return nil
}
port, err := strconv.Atoi(splitted[1])
if err != nil {
glog.Warningf("Malformed PORT during parsing address on config file: %s", ipAndPort)
return nil
}
ipAddr := net.ParseIP(ip)
if ipAddr == nil {
glog.Warningf("Malformed IP during parsing address on config file: %s", ipAndPort)
return nil
}
return &net.UDPAddr{IP: ipAddr, Port: port, Zone: ""}
}
19 changes: 18 additions & 1 deletion feg/gateway/services/s8_proxy/servicers/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,13 @@ import (
func TestGetS8ProxyConfig(t *testing.T) {
conf := generateS8Mconfig(t, config)
assert.Equal(t, ":1", conf.ClientAddr)
assert.Equal(t, "10.0.0.1:0", conf.ServerAddr)
assert.Equal(t, "10.0.0.1:0", conf.ServerAddr.String())
}

func TestGetS8ProxyConfig_noPgw(t *testing.T) {
conf := generateS8Mconfig(t, config_noPGW)
assert.Equal(t, ":1", conf.ClientAddr)
assert.Nil(t, conf.ServerAddr)
}

func generateS8Mconfig(t *testing.T, configString string) *servicers.S8ProxyConfig {
Expand All @@ -31,4 +37,15 @@ var (
}
}
}`

config_noPGW = `{
"configsByKey": {
"s8_proxy": {
"@type": "type.googleapis.com/magma.mconfig.S8Config",
"logLevel": "INFO",
"local_address": ":1",
"pgw_address": ""
}
}
}`
)
17 changes: 12 additions & 5 deletions feg/gateway/services/s8_proxy/servicers/gtp_handlers.go
Original file line number Diff line number Diff line change
Expand Up @@ -84,18 +84,25 @@ func getHandle_CreateSessionResponse() gtpv2.HandlerFunc {
return &gtpv2.RequiredIEMissingError{Type: ie.PDNAddressAllocation}
}

// control plane fteid
if fteidcIE := csResGtp.PGWS5S8FTEIDC; fteidcIE != nil {
fteidc, interfaceType, err := handleFTEID(fteidcIE)
// Pgw control plane fteid
if pgwCFteidIE := csResGtp.PGWS5S8FTEIDC; pgwCFteidIE != nil {
pgwCFteid, interfaceType, err := handleFTEID(pgwCFteidIE)
if err != nil {
return err
return fmt.Errorf("Couldn't get PGW control plane FTEID: %s ", err)
}
session.AddTEID(interfaceType, fteidc.GetTeid())
session.AddTEID(interfaceType, pgwCFteid.GetTeid())
csRes.CPgwFteid = pgwCFteid
} else {
c.RemoveSession(session)
return &gtpv2.RequiredIEMissingError{Type: ie.FullyQualifiedTEID}
}

// AGW (sgw) control plane fteid
csRes.CAgwTeid, err = session.GetTEID(gtpv2.IFTypeS5S8SGWGTPC)
if err != nil {
return fmt.Errorf("Couldn't get local (sgw) control plane TEID: %s ", err)
}

// TODO: handle more than one bearer
if brCtxIE := csResGtp.BearerContextsCreated; brCtxIE != nil {
bearerCtx := &protos.BearerContext{}
Expand Down
39 changes: 30 additions & 9 deletions feg/gateway/services/s8_proxy/servicers/ie_conversions.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,19 +33,20 @@ type MagmaSessionFTeids struct {
}

// buildCreateSessionRequestIE creates a slice with all the IE needed for a Create Session Request
func buildCreateSessionRequestIE(cPgwUDPAddr *net.UDPAddr, req *protos.CreateSessionRequestPgw, gtpCli *gtp.Client) ([]*ie.IE, MagmaSessionFTeids, error) {
// Create session needs 3 FTEIDs:
func buildCreateSessionRequestIE(cPgwUDPAddr *net.UDPAddr, req *protos.CreateSessionRequestPgw, gtpCli *gtp.Client) (MagmaSessionFTeids, []*ie.IE, error) {
sessionTeids := MagmaSessionFTeids{}
// Create session needs e FTEIDs:
// - FEG (S8) control plane FTEID will be built using local address and TEID handled by s8_proxy
// - PGW control plane FTEID will bu built using the pgwAddrs on the Request and 0 as TEID
// - AGW user plane FTEID, provided by the bearer in the request

// FEG control plane TEID
// TODO: look for a better way to find the local ip (avoid pinging on each request)
// (obtain the IP that is going to send the packet first)
ip, err := gtp.GetOutboundIP(cPgwUDPAddr)
if err != nil {
return nil, MagmaSessionFTeids{}, err
return sessionTeids, nil, err
}

// FEG control plane TEID
cFegFTeid := gtpCli.NewSenderFTEID(ip.String(), "")

// AGW user plane TEID (comming from request)
Expand All @@ -67,17 +68,17 @@ func buildCreateSessionRequestIE(cPgwUDPAddr *net.UDPAddr, req *protos.CreateSes
daylightSavingTime := uint8(req.TimeZone.DaylightSavingTime)

// TODO: set charging characteristics
return []*ie.IE{
ies := []*ie.IE{
ie.NewIMSI(req.GetImsi()),
bearer,
cFegFTeid,
getUserLocationIndication(req.ServingNetwork.Mcc, req.ServingNetwork.Mcc, req.Uli),
getPdnType(req.PdnType),
getPDNAddressAllocation(req),
getRatType(req.RatType),
ie.NewMSISDN(string(req.Msisdn[:])),
ie.NewMobileEquipmentIdentity(req.Mei),
ie.NewServingNetwork(req.ServingNetwork.Mcc, req.ServingNetwork.Mnc),
ie.NewRATType(uint8(req.RatType)),
ie.NewAccessPointName(req.Apn),
// TODO: selection mode (hadcoded for now)
ie.NewSelectionMode(gtpv2.SelectionModeMSorNetworkProvidedAPNSubscribedVerified),
Expand All @@ -87,7 +88,10 @@ func buildCreateSessionRequestIE(cPgwUDPAddr *net.UDPAddr, req *protos.CreateSes
ie.NewAPNRestriction(gtpv2.APNRestrictionNoExistingContextsorRestriction),
ie.NewAggregateMaximumBitRate(uint32(req.Ambr.BrUl), uint32(req.Ambr.BrDl)),
ie.NewUETimeZone(offset, daylightSavingTime),
}, MagmaSessionFTeids{cFegFTeid, uAgwFTeid}, nil
}
sessionTeids.cFegFTeid = cFegFTeid
sessionTeids.uAgwFTeid = uAgwFTeid
return sessionTeids, ies, nil
}

// buildModifyBearerRequest creates a slice with all the IE needed for a Modify Bearer Request
Expand All @@ -105,6 +109,12 @@ func buildModifyBearerRequest(req *protos.ModifyBearerRequestPgw, bearerId uint8
}
}

func buildDeleteSessionRequest(session *gtpv2.Session) []*ie.IE {
return []*ie.IE{
ie.NewEPSBearerID(session.GetDefaultBearer().EBI),
}
}

func getPDNAddressAllocation(req *protos.CreateSessionRequestPgw) *ie.IE {
var res *ie.IE
if req.PdnType == protos.PDNType_IPV4 {
Expand Down Expand Up @@ -132,7 +142,7 @@ func getPdnType(pdnType protos.PDNType) *ie.IE {
case protos.PDNType_NonIP:
res = gtpv2.PDNTypeNonIP // nonIP
default:
panic(fmt.Sprintf("getPdnType %d does not exist", pdnType))
panic(fmt.Sprintf("PdnType %d does not exist", pdnType))
}
return ie.NewPDNType(res)
}
Expand Down Expand Up @@ -175,3 +185,14 @@ func getUserLocationIndication(mcc, mnc string, uli *protos.UserLocationInformat
}
return ie.NewUserLocationInformationStruct(cgi, sai, rai, tai, ecgi, lai, menbi, emenbi)
}

func getRatType(ratType protos.RATType) *ie.IE {
var rType uint8
switch ratType {
case protos.RATType_EUTRAN:
rType = 6
default:
panic(fmt.Sprintf("RatType %d does not exist", ratType))
}
return ie.NewRATType(rType)
}
5 changes: 3 additions & 2 deletions feg/gateway/services/s8_proxy/servicers/mock_pgw/cs.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ import (

func (mPgw *MockPgw) getHandleCreateSessionRequest() gtpv2.HandlerFunc {
return func(c *gtpv2.Conn, sgwAddr net.Addr, msg message.Message) error {
fmt.Println("mock PGW Received a CreateSessionRequest")
fmt.Println("mock PGW received a CreateSessionRequest")

session := gtpv2.NewSession(sgwAddr, &gtpv2.Subscriber{Location: &gtpv2.Location{}})
bearer := session.GetDefaultBearer()
Expand Down Expand Up @@ -150,7 +150,8 @@ func (mPgw *MockPgw) getHandleCreateSessionRequest() gtpv2.HandlerFunc {
cIP := strings.Split(c.LocalAddr().String(), ":")[0]
pgwFTEIDc := c.NewSenderFTEID(cIP, "").WithInstance(1)
uIP := strings.Split(dummyUserPlanePgwIP, ":")[0]
pgwFTEIDu := ie.NewFullyQualifiedTEID(gtpv2.IFTypeS5S8PGWGTPU, rand.Uint32(), uIP, "")
randUTeid := (rand.Uint32() / 1000) * 1000 // for easy identification, this teid will always end in 000
pgwFTEIDu := ie.NewFullyQualifiedTEID(gtpv2.IFTypeS5S8PGWGTPU, randUTeid, uIP, "")

sgwTEIDc, err := session.GetTEID(gtpv2.IFTypeS5S8SGWGTPC)

Expand Down
2 changes: 1 addition & 1 deletion feg/gateway/services/s8_proxy/servicers/mock_pgw/ds.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ func (mPgw *MockPgw) getHandleDeleteSessionRequest() gtpv2.HandlerFunc {
return err
}

fmt.Printf("Session deleted for Subscriber: %s", session.IMSI)
fmt.Printf("mock PWG deleted a session for Subscriber: %s\n", session.IMSI)
c.RemoveSession(session)
return nil
}
Expand Down
37 changes: 24 additions & 13 deletions feg/gateway/services/s8_proxy/servicers/s8_proxy.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ type S8Proxy struct {

type S8ProxyConfig struct {
ClientAddr string
ServerAddr string //TODO: delete since server will be provided by mme
ServerAddr *net.UDPAddr
}

// NewS8Proxy creates an s8 proxy, but does not checks the PGW is alive
Expand All @@ -52,7 +52,7 @@ func NewS8Proxy(config *S8ProxyConfig) (*S8Proxy, error) {

//NewS8ProxyWithEcho creates an s8 proxy already connected to a server (checks with echo if PGW is alive)
func NewS8ProxyWithEcho(config *S8ProxyConfig) (*S8Proxy, error) {
gtpCli, err := gtp.NewConnectedAutoClient(context.Background(), config.ServerAddr, gtpv2.IFTypeS5S8SGWGTPC)
gtpCli, err := gtp.NewConnectedAutoClient(context.Background(), config.ServerAddr.String(), gtpv2.IFTypeS5S8SGWGTPC)
if err != nil {
return nil, fmt.Errorf("Error creating S8_Proxy: %s", err)
}
Expand All @@ -71,22 +71,21 @@ func newS8ProxyImp(cli *gtp.Client, config *S8ProxyConfig) (*S8Proxy, error) {
}

func (s *S8Proxy) CreateSession(ctx context.Context, req *protos.CreateSessionRequestPgw) (*protos.CreateSessionResponsePgw, error) {
// check pgw address
cPgwUDPAddr, err := net.ResolveUDPAddr("udp", req.PgwAddrs)
cPgwUDPAddr, err := s.getPgwAddress(req.PgwAddrs)
if err != nil {
err = fmt.Errorf("Create Session Request couldn't get PgwAddrs %s: %s", cPgwUDPAddr, err)
err = fmt.Errorf("Create Session Request failed due to missing server address: %s", err)
glog.Error(err)
return nil, err
}

// build csReq IE message
csReqIEs, sessionTeids, err := buildCreateSessionRequestIE(cPgwUDPAddr, req, s.gtpClient)
sessionTeids, csReqIEs, err := buildCreateSessionRequestIE(cPgwUDPAddr, req, s.gtpClient)
if err != nil {
return nil, err
}

// send, register and receive create session (session is created on the gtp client during this process too)
csRes, err := s.sendAndReceiveCreateSession(cPgwUDPAddr, csReqIEs, sessionTeids)
csRes, err := s.sendAndReceiveCreateSession(cPgwUDPAddr, sessionTeids, csReqIEs)
if err != nil {
err = fmt.Errorf("Create Session Request failed: %s", err)
glog.Error(err)
Expand Down Expand Up @@ -124,23 +123,21 @@ func (s *S8Proxy) DeleteSession(ctx context.Context, req *protos.DeleteSessionRe
return nil, err
}

cdRes, err := s.sendAndReceiveDeleteSession(teid, session)
dsReqIE := buildDeleteSessionRequest(session)
cdRes, err := s.sendAndReceiveDeleteSession(teid, session, dsReqIE)
if err != nil {
glog.Errorf("Couldnt delete session for IMSI %s:, %s", req.Imsi, err)
return nil, err
}

// remove session from the s8_proxy client
s.gtpClient.RemoveSession(session)

return cdRes, nil
}

func (s *S8Proxy) SendEcho(ctx context.Context, req *protos.EchoRequest) (*protos.EchoResponse, error) {
// check pgw address
cPgwUDPAddr, err := net.ResolveUDPAddr("udp", req.PgwAddrs)
cPgwUDPAddr, err := s.getPgwAddress(req.PgwAddrs)
if err != nil {
err = fmt.Errorf("SendEcho couldn't find %s: %s", cPgwUDPAddr, err)
err = fmt.Errorf("SendEcho to %s failed: %s", cPgwUDPAddr, err)
glog.Error(err)
return nil, err
}
Expand All @@ -151,3 +148,17 @@ func (s *S8Proxy) SendEcho(ctx context.Context, req *protos.EchoRequest) (*proto
}
return &protos.EchoResponse{}, nil
}

// getPgwAddress returns an UDPAddrs if the passed string corresponds to a valid ip,
// otherwise it uses the server address configured on s8_proxy
func (s *S8Proxy) getPgwAddress(pgwAddrsFromRequest string) (*net.UDPAddr, error) {
addrs := ParseAddress(pgwAddrsFromRequest)
if addrs != nil {
// address comming from string has precednece
return addrs, nil
}
if s.config.ServerAddr != nil {
return s.config.ServerAddr, nil
}
return nil, fmt.Errorf("Neither the request nor s8_proxy has a valid server (pgw) address")
}

0 comments on commit 1a08d0f

Please sign in to comment.