summaryrefslogtreecommitdiff
path: root/libpod/network/util
diff options
context:
space:
mode:
Diffstat (limited to 'libpod/network/util')
-rw-r--r--libpod/network/util/filters.go55
-rw-r--r--libpod/network/util/interfaces.go34
-rw-r--r--libpod/network/util/ip.go113
-rw-r--r--libpod/network/util/ip_test.go125
4 files changed, 327 insertions, 0 deletions
diff --git a/libpod/network/util/filters.go b/libpod/network/util/filters.go
new file mode 100644
index 000000000..48e769196
--- /dev/null
+++ b/libpod/network/util/filters.go
@@ -0,0 +1,55 @@
+package util
+
+import (
+ "strings"
+
+ "github.com/containers/podman/v3/libpod/network/types"
+ "github.com/containers/podman/v3/pkg/util"
+ "github.com/pkg/errors"
+)
+
+func GenerateNetworkFilters(filters map[string][]string) ([]types.FilterFunc, error) {
+ filterFuncs := make([]types.FilterFunc, 0, len(filters))
+ for key, filterValues := range filters {
+ filterFunc, err := createFilterFuncs(key, filterValues)
+ if err != nil {
+ return nil, err
+ }
+ filterFuncs = append(filterFuncs, filterFunc)
+ }
+ return filterFuncs, nil
+}
+
+func createFilterFuncs(key string, filterValues []string) (types.FilterFunc, error) {
+ switch strings.ToLower(key) {
+ case "name":
+ // matches one name, regex allowed
+ return func(net types.Network) bool {
+ return util.StringMatchRegexSlice(net.Name, filterValues)
+ }, nil
+
+ case "label":
+ // matches all labels
+ return func(net types.Network) bool {
+ return util.MatchLabelFilters(filterValues, net.Labels)
+ }, nil
+
+ case "driver":
+ // matches network driver
+ return func(net types.Network) bool {
+ return util.StringInSlice(net.Driver, filterValues)
+ }, nil
+
+ case "id":
+ // matches part of one id
+ return func(net types.Network) bool {
+ return util.StringMatchRegexSlice(net.ID, filterValues)
+ }, nil
+
+ // FIXME: What should we do with the old plugin filter
+ // TODO: add dangling, dns enabled, internal filter
+
+ default:
+ return nil, errors.Errorf("invalid filter %q", key)
+ }
+}
diff --git a/libpod/network/util/interfaces.go b/libpod/network/util/interfaces.go
new file mode 100644
index 000000000..dc2bd601d
--- /dev/null
+++ b/libpod/network/util/interfaces.go
@@ -0,0 +1,34 @@
+package util
+
+import "net"
+
+// GetLiveNetworkSubnets returns a slice of subnets representing what the system
+// has defined as network interfaces
+func GetLiveNetworkSubnets() ([]*net.IPNet, error) {
+ addrs, err := net.InterfaceAddrs()
+ if err != nil {
+ return nil, err
+ }
+ nets := make([]*net.IPNet, 0, len(addrs))
+ for _, address := range addrs {
+ _, n, err := net.ParseCIDR(address.String())
+ if err != nil {
+ return nil, err
+ }
+ nets = append(nets, n)
+ }
+ return nets, nil
+}
+
+// GetLiveNetworkNames returns a list of network interface names on the system
+func GetLiveNetworkNames() ([]string, error) {
+ liveInterfaces, err := net.Interfaces()
+ if err != nil {
+ return nil, err
+ }
+ interfaceNames := make([]string, 0, len(liveInterfaces))
+ for _, i := range liveInterfaces {
+ interfaceNames = append(interfaceNames, i.Name)
+ }
+ return interfaceNames, nil
+}
diff --git a/libpod/network/util/ip.go b/libpod/network/util/ip.go
new file mode 100644
index 000000000..b2ba92735
--- /dev/null
+++ b/libpod/network/util/ip.go
@@ -0,0 +1,113 @@
+package util
+
+import (
+ "crypto/rand"
+ "net"
+
+ "github.com/pkg/errors"
+)
+
+// IsIPv6 returns true if netIP is IPv6.
+func IsIPv6(netIP net.IP) bool {
+ return netIP != nil && netIP.To4() == nil
+}
+
+// IsIPv4 returns true if netIP is IPv4.
+func IsIPv4(netIP net.IP) bool {
+ return netIP != nil && netIP.To4() != nil
+}
+
+func incByte(subnet *net.IPNet, idx int, shift uint) error {
+ if idx < 0 {
+ return errors.New("no more subnets left")
+ }
+ if subnet.IP[idx] == 255 {
+ subnet.IP[idx] = 0
+ return incByte(subnet, idx-1, 0)
+ }
+ subnet.IP[idx] += 1 << shift
+ return nil
+}
+
+// NextSubnet returns subnet incremented by 1
+func NextSubnet(subnet *net.IPNet) (*net.IPNet, error) {
+ newSubnet := &net.IPNet{
+ IP: subnet.IP,
+ Mask: subnet.Mask,
+ }
+ ones, bits := newSubnet.Mask.Size()
+ if ones == 0 {
+ return nil, errors.Errorf("%s has only one subnet", subnet.String())
+ }
+ zeroes := uint(bits - ones)
+ shift := zeroes % 8
+ idx := ones/8 - 1
+ if idx < 0 {
+ idx = 0
+ }
+ if err := incByte(newSubnet, idx, shift); err != nil {
+ return nil, err
+ }
+ return newSubnet, nil
+}
+
+// LastIPInSubnet gets the last IP in a subnet
+func LastIPInSubnet(addr *net.IPNet) (net.IP, error) { //nolint:interfacer
+ // re-parse to ensure clean network address
+ _, cidr, err := net.ParseCIDR(addr.String())
+ if err != nil {
+ return nil, err
+ }
+
+ ones, bits := cidr.Mask.Size()
+ if ones == bits {
+ return cidr.IP, nil
+ }
+ for i := range cidr.IP {
+ cidr.IP[i] = cidr.IP[i] | ^cidr.Mask[i]
+ }
+ return cidr.IP, nil
+}
+
+// FirstIPInSubnet gets the first IP in a subnet
+func FirstIPInSubnet(addr *net.IPNet) (net.IP, error) { //nolint:interfacer
+ // re-parse to ensure clean network address
+ _, cidr, err := net.ParseCIDR(addr.String())
+ if err != nil {
+ return nil, err
+ }
+ ones, bits := cidr.Mask.Size()
+ if ones == bits {
+ return cidr.IP, nil
+ }
+ cidr.IP[len(cidr.IP)-1]++
+ return cidr.IP, nil
+}
+
+func NetworkIntersectsWithNetworks(n *net.IPNet, networklist []*net.IPNet) bool {
+ for _, nw := range networklist {
+ if networkIntersect(n, nw) {
+ return true
+ }
+ }
+ return false
+}
+
+func networkIntersect(n1, n2 *net.IPNet) bool {
+ return n2.Contains(n1.IP) || n1.Contains(n2.IP)
+}
+
+// GetRandomIPv6Subnet returns a random internal ipv6 subnet as described in RFC3879.
+func GetRandomIPv6Subnet() (net.IPNet, error) {
+ ip := make(net.IP, 8, net.IPv6len)
+ // read 8 random bytes
+ _, err := rand.Read(ip)
+ if err != nil {
+ return net.IPNet{}, nil
+ }
+ // first byte must be FD as per RFC3879
+ ip[0] = 0xfd
+ // add 8 zero bytes
+ ip = append(ip, make([]byte, 8)...)
+ return net.IPNet{IP: ip, Mask: net.CIDRMask(64, 128)}, nil
+}
diff --git a/libpod/network/util/ip_test.go b/libpod/network/util/ip_test.go
new file mode 100644
index 000000000..c26ad140a
--- /dev/null
+++ b/libpod/network/util/ip_test.go
@@ -0,0 +1,125 @@
+package util
+
+import (
+ "fmt"
+ "net"
+ "reflect"
+ "testing"
+)
+
+func parseCIDR(n string) *net.IPNet {
+ _, parsedNet, _ := net.ParseCIDR(n)
+ return parsedNet
+}
+
+func TestNextSubnet(t *testing.T) {
+ type args struct {
+ subnet *net.IPNet
+ }
+ tests := []struct {
+ name string
+ args args
+ want *net.IPNet
+ wantErr bool
+ }{
+ {"class b", args{subnet: parseCIDR("192.168.0.0/16")}, parseCIDR("192.169.0.0/16"), false},
+ {"class c", args{subnet: parseCIDR("192.168.1.0/24")}, parseCIDR("192.168.2.0/24"), false},
+ }
+ for _, tt := range tests {
+ test := tt
+ t.Run(test.name, func(t *testing.T) {
+ got, err := NextSubnet(test.args.subnet)
+ if (err != nil) != test.wantErr {
+ t.Errorf("NextSubnet() error = %v, wantErr %v", err, test.wantErr)
+ return
+ }
+ if !reflect.DeepEqual(got, test.want) {
+ t.Errorf("NextSubnet() got = %v, want %v", got, test.want)
+ }
+ })
+ }
+}
+
+func TestFirstIPInSubnet(t *testing.T) {
+ tests := []struct {
+ name string
+ args *net.IPNet
+ want net.IP
+ wantErr bool
+ }{
+ {"class b", parseCIDR("192.168.0.0/16"), net.ParseIP("192.168.0.1"), false},
+ {"class c", parseCIDR("192.168.1.0/24"), net.ParseIP("192.168.1.1"), false},
+ {"cidr /23", parseCIDR("192.168.0.0/23"), net.ParseIP("192.168.0.1"), false},
+ {"cidr /25", parseCIDR("192.168.1.0/25"), net.ParseIP("192.168.1.1"), false},
+ {"cidr /26", parseCIDR("172.16.1.128/26"), net.ParseIP("172.16.1.129"), false},
+ {"class a", parseCIDR("10.0.0.0/8"), net.ParseIP("10.0.0.1"), false},
+ {"cidr /32", parseCIDR("192.168.255.4/32"), net.ParseIP("192.168.255.4"), false},
+ {"cidr /31", parseCIDR("192.168.255.4/31"), net.ParseIP("192.168.255.5"), false},
+ }
+ for _, tt := range tests {
+ test := tt
+ t.Run(test.name, func(t *testing.T) {
+ got, err := FirstIPInSubnet(test.args)
+ if (err != nil) != test.wantErr {
+ t.Errorf("FirstIPInSubnet() error = %v, wantErr %v", err, test.wantErr)
+ return
+ }
+ if !got.Equal(test.want) {
+ t.Errorf("FirstIPInSubnet() got = %v, want %v", got, test.want)
+ }
+ })
+ }
+}
+
+func TestLastIPInSubnet(t *testing.T) {
+ tests := []struct {
+ name string
+ args *net.IPNet
+ want net.IP
+ wantErr bool
+ }{
+ {"class b", parseCIDR("192.168.0.0/16"), net.ParseIP("192.168.255.255"), false},
+ {"class c", parseCIDR("192.168.1.0/24"), net.ParseIP("192.168.1.255"), false},
+ {"cidr /23", parseCIDR("192.168.0.0/23"), net.ParseIP("192.168.1.255"), false},
+ {"cidr /25", parseCIDR("192.168.1.0/25"), net.ParseIP("192.168.1.127"), false},
+ {"cidr /26", parseCIDR("172.16.1.128/26"), net.ParseIP("172.16.1.191"), false},
+ {"class a", parseCIDR("10.0.0.0/8"), net.ParseIP("10.255.255.255"), false},
+ {"cidr /32", parseCIDR("192.168.255.4/32"), net.ParseIP("192.168.255.4"), false},
+ {"cidr /31", parseCIDR("192.168.255.4/31"), net.ParseIP("192.168.255.5"), false},
+ }
+ for _, tt := range tests {
+ test := tt
+ t.Run(test.name, func(t *testing.T) {
+ got, err := LastIPInSubnet(test.args)
+ if (err != nil) != test.wantErr {
+ t.Errorf("LastIPInSubnet() error = %v, wantErr %v", err, test.wantErr)
+ return
+ }
+ if !got.Equal(test.want) {
+ t.Errorf("LastIPInSubnet() got = %v, want %v", got, test.want)
+ }
+ })
+ }
+}
+
+func TestGetRandomIPv6Subnet(t *testing.T) {
+ for i := 0; i < 1000; i++ {
+ t.Run(fmt.Sprintf("GetRandomIPv6Subnet %d", i), func(t *testing.T) {
+ sub, err := GetRandomIPv6Subnet()
+ if err != nil {
+ t.Errorf("GetRandomIPv6Subnet() error should be nil: %v", err)
+ return
+ }
+ if sub.IP.To4() != nil {
+ t.Errorf("ip %s is not an ipv6 address", sub.IP)
+ }
+ if sub.IP[0] != 0xfd {
+ t.Errorf("ipv6 %s does not start with fd", sub.IP)
+ }
+ ones, bytes := sub.Mask.Size()
+ if ones != 64 || bytes != 128 {
+ t.Errorf("wrong network mask %v, it should be /64", sub.Mask)
+ }
+ })
+ }
+}