Skip to content

Commit

Permalink
Add cidr groups support (#1341)
Browse files Browse the repository at this point in the history
Signed-off-by: Artem Glazychev <artem.glazychev@xored.com>

Signed-off-by: Artem Glazychev <artem.glazychev@xored.com>
  • Loading branch information
glazychev-art committed Sep 12, 2022
1 parent e72a32c commit daf1d3a
Show file tree
Hide file tree
Showing 2 changed files with 204 additions and 0 deletions.
120 changes: 120 additions & 0 deletions pkg/tools/cidr/group.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
// Copyright (c) 2022 Cisco and/or its affiliates.
//
// SPDX-License-Identifier: Apache-2.0
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at:
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package cidr

import (
"net"
"strings"

"github.com/pkg/errors"
)

// Groups allows parsing cidr groups.
// Example: [v1, v2, v3], [v4], v5 =>
// [][]*net.IPNet{
// {v1, v2, v3},
// {v4},
// {v5},
// }
// Can be used for env configuration.
// See at https://github.com/kelseyhightower/envconfig#custom-decoders
type Groups [][]*net.IPNet

const (
// inside - means that we are inside a group
inside int = iota

// outside - means that we are outside a group
outside
)

// Decode parses values from passed string.
func (g *Groups) Decode(value string) error {
value = strings.ReplaceAll(value, " ", "")
if value == "" {
return nil
}
if err := validateParentheses(value); err != nil {
return err
}

list := strings.Split(value, ",")
var groups [][]*net.IPNet

state := outside

for _, item := range list {
if state == outside {
groups = append(groups, []*net.IPNet{})
}
toAdd := strings.Trim(item, "[]")
if toAdd == "" {
return errors.New("empty value")
}
_, ipNet, parseErr := net.ParseCIDR(strings.TrimSpace(toAdd))
if parseErr != nil {
return errors.Errorf("сould not parse CIDR %s; %+v", item, parseErr)
}

groups[len(groups)-1] = append(groups[len(groups)-1], ipNet)

if strings.HasSuffix(item, "]") {
state = outside
} else if strings.HasPrefix(item, "[") {
state = inside
}
}
*g = groups
return nil
}

func validateParentheses(value string) error {
i := 0
length := len(value)
if length < 2 {
return errors.New("value is too short")
}

parenthesesCounter := 0
for ; i < length; i++ {
if value[i] == '[' {
if parenthesesCounter == 1 {
return errors.Errorf("unexpected character: %c", value[i])
}
parenthesesCounter++
} else if value[i] == ']' {
if parenthesesCounter == 0 {
return errors.Errorf("unexpected character: %c", value[i])
}
if i+1 == length {
return nil
}
if i+2 == length {
return errors.New("unexpected end of value")
}
if value[i+1] != ',' {
return errors.Errorf("unexpected character: %c", value[i+1])
}
parenthesesCounter--
}
}

if parenthesesCounter != 0 {
return errors.New("parenteses are not balanced")
}
return nil
}
84 changes: 84 additions & 0 deletions pkg/tools/cidr/group_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
// Copyright (c) 2022 Cisco and/or its affiliates.
//
// SPDX-License-Identifier: Apache-2.0
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at:
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package cidr_test

import (
"fmt"
"net"
"testing"

"github.com/stretchr/testify/require"

"github.com/networkservicemesh/sdk/pkg/tools/cidr"
)

func Test_CidrGroupsDecoder_EmptyInput(t *testing.T) {
var expected [][]*net.IPNet
var groups cidr.Groups
err := groups.Decode(``)
require.NoError(t, err)
require.Equal(t, expected, [][]*net.IPNet(groups))
}

func Test_CidrGroupsDecoder_CorrectInput(t *testing.T) {
_, cidr1, err := net.ParseCIDR("172.168.1.0/24")
require.NoError(t, err)
_, cidr2, err := net.ParseCIDR("172.168.2.0/24")
require.NoError(t, err)
_, cidr3, err := net.ParseCIDR("172.168.3.0/24")
require.NoError(t, err)

var expected = [][]*net.IPNet{
{cidr1},
{cidr1, cidr2},
{cidr1},
{cidr1, cidr2, cidr3},
}
var groups cidr.Groups
err = groups.Decode(fmt.Sprintf("[%v], [%v, %v], %v, [%v, %v , %v]",
cidr1.String(),
cidr1.String(), cidr2.String(),
cidr1.String(),
cidr1.String(), cidr2.String(), cidr3.String()))
require.NoError(t, err)
require.Equal(t, expected, [][]*net.IPNet(groups))
}

func Test_CidrGroupsDecoder_WrongInput(t *testing.T) {
cidr1 := "172.168.1.0/24"
var groups cidr.Groups
err := groups.Decode(fmt.Sprintf("[%[1]v, %[1]v], [[%[1]v],,[%[1]v]", cidr1))
require.Error(t, err)

err = groups.Decode(fmt.Sprintf("[%[1]v, %[1]v", cidr1))
require.Error(t, err)

err = groups.Decode(fmt.Sprintf("[%[1]v, %[1]v],", cidr1))
require.Error(t, err)

err = groups.Decode(fmt.Sprintf("[%[1]v, %[1]v][%[1]v]", cidr1))
require.Error(t, err)

err = groups.Decode(fmt.Sprintf("[%[1]v, %[1]v],[]", cidr1))
require.Error(t, err)

err = groups.Decode(fmt.Sprintf("[%[1]v,, %[1]v]", cidr1))
require.Error(t, err)

err = groups.Decode(fmt.Sprintf("[%[1]v, %[1]v]", "172.168.1./24"))
require.Error(t, err)
}

0 comments on commit daf1d3a

Please sign in to comment.