diff --git a/backend/WebUI/api_verify.go b/backend/WebUI/api_verify.go index a1ced142..e5823542 100644 --- a/backend/WebUI/api_verify.go +++ b/backend/WebUI/api_verify.go @@ -18,7 +18,7 @@ import ( type VerifyScope struct { Supi string `json:"supi"` - Sd string `json:"sd"` + Sd string `json:"sd,omitempty"` Sst int `json:"sst"` Dnn string `json:"dnn"` Ipaddr string `json:"ipaddr"` @@ -75,13 +75,14 @@ func GetSmfUserPlaneInfo() (interface{}, error) { return jsonData, nil } -func getDnnStaticIpPool(snssai models.Snssai, dnn string) (netip.Prefix, error) { +func getDnnStaticIpPool(snssai models.Snssai, dnn string) ([]netip.Prefix, error) { var userplaneinfo smf_factory.UserPlaneInformation raw_info, get_err := GetSmfUserPlaneInfo() if get_err != nil { logger.ProcLog.Errorf("GetSmfUserPlaneInfo(): %+v", get_err) - return netip.ParsePrefix("0.0.0.0/32") + // net, parseErr := netip.ParsePrefix("0.0.0.0/32") + return []netip.Prefix{}, get_err } tmp, err := json.Marshal(raw_info) @@ -103,18 +104,28 @@ func getDnnStaticIpPool(snssai models.Snssai, dnn string) (netip.Prefix, error) // Find the DNN name if dnnInfo.Dnn == dnn { if len(dnnInfo.StaticPools) > 0 { - staticPoolstr := dnnInfo.StaticPools[0].Cidr - return netip.ParsePrefix(staticPoolstr) + result := []netip.Prefix{} + for _, pool := range dnnInfo.StaticPools { + staticPoolstr := pool.Cidr + net, parseErr := netip.ParsePrefix(staticPoolstr) + if parseErr != nil { + return result, parseErr + } + result = append(result, net) + } + return result, nil } // If there is no static pool, return smallest - return netip.ParsePrefix("0.0.0.0/32") + net, parseErr := netip.ParsePrefix("0.0.0.0/32") + return []netip.Prefix{net}, parseErr } } } } } } - return netip.ParsePrefix("0.0.0.0/32") + net, parseErr := netip.ParsePrefix("0.0.0.0/32") + return []netip.Prefix{net}, parseErr } func VerifyStaticIP(c *gin.Context) { @@ -136,46 +147,84 @@ func VerifyStaticIP(c *gin.Context) { return } - staticIp, parse_err := netip.ParseAddr(checkData.Ipaddr) - if parse_err != nil { - logger.ProcLog.Errorln(parse_err.Error()) - c.JSON(http.StatusOK, gin.H{ - "valid": false, - "cause": parse_err.Error(), - }) - return - } - logger.ProcLog.Debugln("check IP address:", staticIp) - snssai := models.Snssai{ - Sd: checkData.Sd, Sst: int32(checkData.Sst), } + if checkData.Sd != "" { + snssai.Sd = checkData.Sd + } - staticPool, get_pool_err := getDnnStaticIpPool(snssai, checkData.Dnn) + staticPools, get_pool_err := getDnnStaticIpPool(snssai, checkData.Dnn) if get_pool_err != nil { c.JSON(http.StatusInternalServerError, gin.H{ "error": get_pool_err, - "ipaddr": staticIp, + "ipaddr": checkData.Ipaddr, "valid": false, "cause": get_pool_err.Error(), }) return } + VerifyStaticIpProcedure(c, checkData, staticPools) +} + +func VerifyStaticIpProcedure( + c *gin.Context, + checkData VerifyScope, + staticPools []netip.Prefix, +) { + staticIp, parse_err := netip.ParseAddr(checkData.Ipaddr) + if parse_err != nil { + logger.ProcLog.Errorln(parse_err.Error()) + c.JSON(http.StatusOK, gin.H{ + "valid": false, + "cause": parse_err.Error(), + }) + return + } + logger.ProcLog.Debugln("check IP address:", staticIp) // Check in Static Pool - result := staticPool.Contains(staticIp) + result := false + for _, staticPool := range staticPools { + result = staticPool.Contains(staticIp) + if result { + break + } + } if !result { c.JSON(http.StatusOK, gin.H{ "ipaddr": staticIp, "valid": result, - "cause": "Not in static pool: " + staticPool.String(), + "cause": "Not in static pools!", }) - logger.ProcLog.Debugln("StaticIP", staticIp, ": not in static pool: "+staticPool.String()) + logger.ProcLog.Debugln("StaticIP", staticIp, ": not in static pool!") return } - // Check not used by other UE + if gin.Mode() != "test" && checkIpCollisionFromDb(c, checkData) { + return + } + + // Return the result + c.JSON(http.StatusOK, gin.H{ + "ipaddr": staticIp, + "valid": result, + "cause": "", + }) +} + +// Check IP not used by other UE +func checkIpCollisionFromDb( + c *gin.Context, + checkData VerifyScope, +) bool { + snssai := models.Snssai{ + Sst: int32(checkData.Sst), + } + if checkData.Sd != "" { + snssai.Sd = checkData.Sd + } + smDataColl := "subscriptionData.provisionedData.smData" filter := bson.M{ "singleNssai": snssai, @@ -185,17 +234,17 @@ func VerifyStaticIP(c *gin.Context) { if mongo_err != nil { logger.ProcLog.Warningln(smDataColl, "mongo error: ", mongo_err) c.JSON(http.StatusInternalServerError, gin.H{ - "ipaddr": staticIp, + "ipaddr": checkData.Ipaddr, "valid": false, "cause": mongo_err.Error(), }) - return + return true } var smDatas []models.SessionManagementSubscriptionData if err := json.Unmarshal(sliceToByte(smDataDataInterface), &smDatas); err != nil { logger.ProcLog.Errorf("Unmarshal smDatas err: %+v", err) c.JSON(http.StatusInternalServerError, gin.H{}) - return + return true } for _, smData := range smDatas { if dnnConfig, ok := smData.DnnConfigurations[checkData.Dnn]; ok { @@ -204,20 +253,14 @@ func VerifyStaticIP(c *gin.Context) { msg := "StaticIP: " + checkData.Ipaddr + " has already exist!" logger.ProcLog.Warningln(msg) c.JSON(http.StatusOK, gin.H{ - "ipaddr": staticIp, + "ipaddr": checkData.Ipaddr, "valid": false, "cause": msg, }) - return + return true } } } } - - // Return the result - c.JSON(http.StatusOK, gin.H{ - "ipaddr": staticIp, - "valid": result, - "cause": "", - }) + return false } diff --git a/backend/WebUI/api_verify_test.go b/backend/WebUI/api_verify_test.go new file mode 100644 index 00000000..c60fa39d --- /dev/null +++ b/backend/WebUI/api_verify_test.go @@ -0,0 +1,63 @@ +package WebUI_test + +import ( + "encoding/json" + "net/http" + "net/http/httptest" + "net/netip" + "testing" + + "github.com/free5gc/webconsole/backend/WebUI" + "github.com/gin-gonic/gin" + "github.com/stretchr/testify/require" +) + +func TestVerifyStaticIpProcedure(t *testing.T) { + gin.SetMode(gin.TestMode) + + testcases := []struct { + Name string + Scope WebUI.VerifyScope + IpPools []string + Result bool + }{ + { + Name: "One Static Pool - PASS", + Scope: WebUI.VerifyScope{ + Supi: "imsi-", + Sst: 1, + Sd: "010203", + Dnn: "internet", + Ipaddr: "10.163.100.100", + }, + IpPools: []string{"10.163.100.0/24"}, + Result: true, + }, + } + + for _, tc := range testcases { + w := httptest.NewRecorder() + ctx, _ := gin.CreateTestContext(w) + + pools := []netip.Prefix{} + for _, pool := range tc.IpPools { + net, err := netip.ParsePrefix(pool) + require.NoError(t, err) + + pools = append(pools, net) + } + + WebUI.VerifyStaticIpProcedure(ctx, tc.Scope, pools) + require.Equal(t, http.StatusOK, w.Code) + + var result gin.H + rawByte := w.Body + errUnmarshal := json.Unmarshal(rawByte.Bytes(), &result) + require.NoError(t, errUnmarshal) + + valid, exist := result["valid"] + require.True(t, exist) + + require.Equal(t, tc.Result, valid) + } +} diff --git a/go.mod b/go.mod index 73611c3f..2440cfef 100644 --- a/go.mod +++ b/go.mod @@ -15,6 +15,7 @@ require ( github.com/jlaffaye/ftp v0.1.0 github.com/pkg/errors v0.9.1 github.com/sirupsen/logrus v1.9.0 + github.com/stretchr/testify v1.8.3 github.com/urfave/cli v1.22.5 go.mongodb.org/mongo-driver v1.11.3 golang.org/x/crypto v0.21.0 @@ -63,6 +64,7 @@ require ( github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe // indirect github.com/pelletier/go-toml/v2 v2.0.8 // indirect github.com/pkg/sftp v1.13.5 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/tidwall/gjson v1.14.4 // indirect github.com/tidwall/match v1.1.1 // indirect