helpers/subnetmap: allow to use IP addresses

They will be considered as /32 or /128.
This commit is contained in:
Vincent Bernat
2022-07-31 22:39:14 +02:00
parent 992b5a3f4c
commit c0d9d301b3
2 changed files with 51 additions and 15 deletions

View File

@@ -7,6 +7,7 @@ import (
"fmt"
"net"
"reflect"
"strings"
"github.com/kentik/patricia"
tree "github.com/kentik/patricia/generics_tree"
@@ -55,22 +56,35 @@ func SubnetMapUnmarshallerHook[V any]() mapstructure.DecodeHookFunc {
return nil, fmt.Errorf("key %d is not a string (%s)", i, k.Kind())
}
// Parse key
_, ipNet, err := net.ParseCIDR(k.String())
if err != nil {
return nil, err
}
// Convert key to IPv6
ones, bits := ipNet.Mask.Size()
if bits != 32 && bits != 128 {
return nil, fmt.Errorf("key %d has an invalid netmask", i)
}
var key string
if bits == 32 {
key = fmt.Sprintf("::ffff:%s/%d", ipNet.IP.String(), ones+96)
if strings.Contains(k.String(), "/") {
// Subnet
_, ipNet, err := net.ParseCIDR(k.String())
if err != nil {
return nil, err
}
// Convert key to IPv6
ones, bits := ipNet.Mask.Size()
if bits != 32 && bits != 128 {
return nil, fmt.Errorf("key %d has an invalid netmask", i)
}
if bits == 32 {
key = fmt.Sprintf("::ffff:%s/%d", ipNet.IP.String(), ones+96)
} else {
key = ipNet.String()
}
} else {
key = ipNet.String()
// IP
ip := net.ParseIP(k.String())
if ip == nil {
return nil, fmt.Errorf("key %d is not a valid subnet", i)
}
if ipv4 := ip.To4(); ipv4 != nil {
key = fmt.Sprintf("::ffff:%s/128", ipv4.String())
} else {
key = fmt.Sprintf("%s/128", ip.String())
}
}
output[key] = v.Interface()
}
} else if from.Type() == reflect.TypeOf(zero) || from.Type().ConvertibleTo(reflect.TypeOf(zero)) {

View File

@@ -36,7 +36,7 @@ func TestSubnetMapUnmarshalHook(t *testing.T) {
"203.0.113.1": "",
},
}, {
Description: "IPv4",
Description: "IPv4 subnet",
Input: gin.H{"203.0.113.0/24": "customer1"},
Tests: map[string]string{
"::ffff:203.0.113.18": "customer1",
@@ -46,13 +46,31 @@ func TestSubnetMapUnmarshalHook(t *testing.T) {
"2001:db8:1::12": "",
},
}, {
Description: "IPv6",
Description: "IPv4 IP",
Input: gin.H{"203.0.113.1": "customer1"},
Tests: map[string]string{
"::ffff:203.0.113.1": "customer1",
"203.0.113.1": "customer1",
"2001:db8:1::12": "",
},
YAML: gin.H{"203.0.113.1/32": "customer1"},
}, {
Description: "IPv6 subnet",
Input: gin.H{"2001:db8:1::/64": "customer2"},
Tests: map[string]string{
"2001:db8:1::1": "customer2",
"2001:db8:1::2": "customer2",
"2001:db8:2::2": "",
},
}, {
Description: "IPv6 IP",
Input: gin.H{"2001:db8:1::1": "customer2"},
Tests: map[string]string{
"2001:db8:1::1": "customer2",
"2001:db8:1::2": "",
"2001:db8:2::2": "",
},
YAML: gin.H{"2001:db8:1::1/128": "customer2"},
}, {
Description: "Invalid subnet (1)",
Input: gin.H{"192.0.2.1/38": "customer"},
@@ -65,6 +83,10 @@ func TestSubnetMapUnmarshalHook(t *testing.T) {
Description: "Invalid subnet (3)",
Input: gin.H{"2001:db8::/1000": "customer"},
Error: true,
}, {
Description: "Invalid IP",
Input: gin.H{"200.33.300.1": "customer"},
Error: true,
}, {
Description: "Single value",
Input: "customer",