File: //opt/go/pkg/mod/github.com/hashicorp/go-sockaddr@v1.0.2/ifaddr_test.go
package sockaddr_test
import (
"fmt"
"net"
"os"
"strings"
"testing"
sockaddr "github.com/hashicorp/go-sockaddr"
)
func boolEnvVar(envvar string, emptyDefault bool) bool {
v := os.Getenv(envvar)
switch strings.ToLower(v) {
case "":
return emptyDefault
case "0", "f", "n":
return false
case "1", "t", "y":
return true
default:
fmt.Fprintf(os.Stderr, "Unsupported %s flag %q", envvar, v)
return true
}
}
// havePrivateIP is a helper function that returns true when we believe we
// should have a private IP address. This changes the failure mode of various
// tests that expect a private IP address.
//
// When you have a private IP assigned to the host, set the environment variable
// SOCKADDR_HAVE_PRIVATE_IP=1
func havePrivateIP() bool {
return boolEnvVar("SOCKADDR_HAVE_PRIVATE_IP", true)
}
// havePublicIP is a helper function that returns true when we believe we should
// have a public IP address. This changes the failure mode of various tests
// that expect a public IP address.
//
// When you have a public IP assigned to the host, set the environment variable
// SOCKADDR_HAVE_PUBLIC_IP=1
func havePublicIP() bool {
return boolEnvVar("SOCKADDR_HAVE_PUBLIC_IP", false)
}
func TestGetPrivateIP(t *testing.T) {
reportOnPrivate := func(args ...interface{}) {
if havePrivateIP() {
t.Fatalf(args[0].(string), args[1:]...)
} else {
t.Skipf(args[0].(string), args[1:]...)
}
}
ip, err := sockaddr.GetPrivateIP()
if err != nil {
reportOnPrivate("unable to get a private IP: %v", err)
}
if ip == "" {
reportOnPrivate("it's hard to test this reliably")
}
}
func TestGetPrivateIPs(t *testing.T) {
reportOnPrivate := func(args ...interface{}) {
if havePrivateIP() {
t.Fatalf(args[0].(string), args[1:]...)
} else {
t.Skipf(args[0].(string), args[1:]...)
}
}
ips, err := sockaddr.GetPrivateIPs()
if err != nil {
reportOnPrivate("unable to get a private IPs: %v", err)
}
if ips == "" {
reportOnPrivate("it's hard to test this reliably")
}
}
func TestGetPublicIP(t *testing.T) {
reportOnPublic := func(args ...interface{}) {
if havePublicIP() {
t.Fatalf(args[0].(string), args[1:]...)
} else {
t.Skipf(args[0].(string), args[1:]...)
}
}
ip, err := sockaddr.GetPublicIP()
if err != nil {
reportOnPublic("unable to get a public IP: %v", err)
}
if ip == "" {
reportOnPublic("it's hard to test this reliably")
}
}
func TestGetPublicIPs(t *testing.T) {
reportOnPublic := func(args ...interface{}) {
if havePublicIP() {
t.Fatalf(args[0].(string), args[1:]...)
} else {
t.Skipf(args[0].(string), args[1:]...)
}
}
ips, err := sockaddr.GetPublicIPs()
if err != nil {
reportOnPublic("unable to get a public IPs: %v", err)
}
if ips == "" {
reportOnPublic("it's hard to test this reliably")
}
}
func TestGetInterfaceIP(t *testing.T) {
ip, err := sockaddr.GetInterfaceIP(`^.*[\d]$`)
if err != nil {
t.Fatalf("regexp failed: %v", err)
}
if ip == "" {
t.Skip("it's hard to test this reliably")
}
}
func TestIfAddrAttr(t *testing.T) {
tests := []struct {
name string
ifAddr sockaddr.IfAddr
attr string
expected string
}{
{
name: "name",
ifAddr: sockaddr.IfAddr{
Interface: net.Interface{
Name: "abc0",
},
},
attr: "name",
expected: "abc0",
},
}
for i, test := range tests {
if test.name == "" {
t.Fatalf("test %d must have a name", i)
}
result, err := sockaddr.IfAttr(test.attr, test.ifAddr)
if err != nil {
t.Errorf("failed to get attr %q from %v", test.name, test.ifAddr)
}
if result != test.expected {
t.Errorf("unexpected result")
}
}
}
func TestIfAddrMath(t *testing.T) {
tests := []struct {
name string
ifAddr sockaddr.IfAddr
operation string
value string
expected string
wantFail bool
}{
{
name: "ipv4 address +2",
ifAddr: sockaddr.IfAddr{
SockAddr: sockaddr.MustIPv4Addr("127.0.0.1/8"),
},
operation: "address",
value: "+2",
expected: "127.0.0.3/8",
},
{
name: "ipv4 address -2",
ifAddr: sockaddr.IfAddr{
SockAddr: sockaddr.MustIPv4Addr("127.0.0.1/8"),
},
operation: "address",
value: "-2",
expected: "126.255.255.255/8",
},
{
name: "ipv4 address + overflow 0xff00ff03",
ifAddr: sockaddr.IfAddr{
SockAddr: sockaddr.MustIPv4Addr("127.0.0.1/8"),
},
operation: "address",
value: fmt.Sprintf("+%d", 0xff00ff03),
expected: "126.0.255.4/8",
},
{
name: "ipv4 address - underflow 0xff00ff04",
ifAddr: sockaddr.IfAddr{
SockAddr: sockaddr.MustIPv4Addr("127.0.0.1/8"),
},
operation: "address",
value: fmt.Sprintf("-%d", 0xff00ff04),
expected: "127.255.0.253/8",
},
{
name: "ipv6 address +2",
ifAddr: sockaddr.IfAddr{
SockAddr: sockaddr.MustIPv6Addr("::1/128"),
},
operation: "address",
value: "+2",
expected: "::3",
},
{
name: "ipv6 address -3",
ifAddr: sockaddr.IfAddr{
SockAddr: sockaddr.MustIPv6Addr("::4/128"),
},
operation: "address",
value: "-3",
expected: "::1",
},
{
name: "ipv6 address + overflow",
ifAddr: sockaddr.IfAddr{
SockAddr: sockaddr.MustIPv6Addr("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/128"),
},
operation: "address",
value: fmt.Sprintf("+%d", 0x03),
expected: "::2",
},
{
name: "ipv6 address + underflow",
ifAddr: sockaddr.IfAddr{
SockAddr: sockaddr.MustIPv6Addr("::1/128"),
},
operation: "address",
value: fmt.Sprintf("-%d", 0x03),
expected: "ffff:ffff:ffff:ffff:ffff:ffff:ffff:fffe",
},
{
name: "ipv4 network +2",
ifAddr: sockaddr.IfAddr{
SockAddr: sockaddr.MustIPv4Addr("127.0.0.1/8"),
},
operation: "network",
value: "+2",
expected: "127.0.0.2/8",
},
{
name: "ipv4 network -2",
ifAddr: sockaddr.IfAddr{
SockAddr: sockaddr.MustIPv4Addr("127.0.0.1/8"),
},
operation: "network",
value: "-2",
expected: "127.255.255.254/8",
},
{
// Value exceeds /8
name: "ipv4 network + overflow 0xff00ff03",
ifAddr: sockaddr.IfAddr{
SockAddr: sockaddr.MustIPv4Addr("127.0.0.1/8"),
},
operation: "network",
value: fmt.Sprintf("+%d", 0xff00ff03),
expected: "127.0.255.3/8",
},
{
// Value exceeds /8
name: "ipv4 network - underflow+wrap 0xff00ff04",
ifAddr: sockaddr.IfAddr{
SockAddr: sockaddr.MustIPv4Addr("127.0.0.1/8"),
},
operation: "network",
value: fmt.Sprintf("-%d", 0xff00ff04),
expected: "127.255.0.252/8",
},
{
name: "ipv6 network +6",
ifAddr: sockaddr.IfAddr{
SockAddr: sockaddr.MustIPv6Addr("fe80::1/64"),
},
operation: "network",
value: "+6",
expected: "fe80::6/64",
},
{
name: "ipv6 network -6",
ifAddr: sockaddr.IfAddr{
SockAddr: sockaddr.MustIPv6Addr("fe80::ff/64"),
},
operation: "network",
value: "-6",
expected: "fe80::ffff:ffff:ffff:fffa/64",
},
{
// Value exceeds /104 mask
name: "ipv6 network + overflow 0xff00ff03",
ifAddr: sockaddr.IfAddr{
SockAddr: sockaddr.MustIPv6Addr("fe80::1/104"),
},
operation: "network",
value: fmt.Sprintf("+%d", 0xff00ff03),
expected: "fe80::ff03/104",
},
{
// Value exceeds /104
name: "ipv6 network - underflow+wrap 0xff00ff04",
ifAddr: sockaddr.IfAddr{
SockAddr: sockaddr.MustIPv6Addr("fe80::1/104"),
},
operation: "network",
value: fmt.Sprintf("-%d", 0xff00ff04),
expected: "fe80::ff:fc/104",
},
{
name: "ipv4 address missing sign",
ifAddr: sockaddr.IfAddr{
SockAddr: sockaddr.MustIPv4Addr("127.0.0.1/8"),
},
operation: "address",
value: "123",
wantFail: true,
},
{
name: "ipv4 network missing sign",
ifAddr: sockaddr.IfAddr{
SockAddr: sockaddr.MustIPv4Addr("127.0.0.1/8"),
},
operation: "network",
value: "123",
wantFail: true,
},
{
name: "ipv6 address missing sign",
ifAddr: sockaddr.IfAddr{
SockAddr: sockaddr.MustIPv6Addr("::1/128"),
},
operation: "address",
value: "123",
wantFail: true,
},
{
name: "ipv6 network missing sign",
ifAddr: sockaddr.IfAddr{
SockAddr: sockaddr.MustIPv6Addr("::1/128"),
},
operation: "network",
value: "123",
wantFail: true,
},
{
name: "ipv4 address bad value",
ifAddr: sockaddr.IfAddr{
SockAddr: sockaddr.MustIPv4Addr("127.0.0.1/8"),
},
operation: "address",
value: "+xyz",
wantFail: true,
},
{
name: "ipv4 network bad value",
ifAddr: sockaddr.IfAddr{
SockAddr: sockaddr.MustIPv4Addr("127.0.0.1/8"),
},
operation: "network",
value: "-xyz",
wantFail: true,
},
{
name: "ipv6 address bad value",
ifAddr: sockaddr.IfAddr{
SockAddr: sockaddr.MustIPv6Addr("::1/128"),
},
operation: "address",
value: "+xyz",
wantFail: true,
},
{
name: "ipv6 network bad value",
ifAddr: sockaddr.IfAddr{
SockAddr: sockaddr.MustIPv6Addr("::1/128"),
},
operation: "network",
value: "-xyz",
wantFail: true,
},
{
name: "ipv4 bad operation",
ifAddr: sockaddr.IfAddr{
SockAddr: sockaddr.MustIPv4Addr("127.0.0.1/8"),
},
operation: "gooz",
value: "+xyz",
wantFail: true,
},
{
name: "ipv6 bad operation",
ifAddr: sockaddr.IfAddr{
SockAddr: sockaddr.MustIPv6Addr("::1/128"),
},
operation: "frabba",
value: "+xyz",
wantFail: true,
},
{
name: "ipv4 mask operand equals input ipv4 subnet mask",
ifAddr: sockaddr.IfAddr{
SockAddr: sockaddr.MustIPv4Addr("10.20.30.40/8"),
},
operation: "mask",
value: "8",
expected: "10.0.0.0/8",
},
{
name: "ipv4 mask operand larger than input ipv4 subnet mask",
ifAddr: sockaddr.IfAddr{
SockAddr: sockaddr.MustIPv4Addr("192.168.10.20/24"),
},
operation: "mask",
value: "16",
expected: "192.168.0.0/16",
},
{
name: "ipv4 host upper bound mask operand larger than input ipv4 subnet mask",
ifAddr: sockaddr.IfAddr{
SockAddr: sockaddr.MustIPv4Addr("192.168.255.255/24"),
},
operation: "mask",
value: "16",
expected: "192.168.0.0/16",
},
{
name: "ipv4 mask operand smaller than ipv4 subnet mask",
ifAddr: sockaddr.IfAddr{
SockAddr: sockaddr.MustIPv4Addr("10.20.30.40/8"),
},
operation: "mask",
value: "16",
expected: "10.20.0.0/8",
},
{
name: "ipv4 host upper bound mask operand smaller than input ipv4 subnet mask",
ifAddr: sockaddr.IfAddr{
SockAddr: sockaddr.MustIPv4Addr("10.20.255.255/8"),
},
operation: "mask",
value: "16",
expected: "10.20.0.0/8",
},
{
name: "ipv4 mask bad value upper bound",
ifAddr: sockaddr.IfAddr{
SockAddr: sockaddr.MustIPv4Addr("127.0.0.1/8"),
},
operation: "mask",
value: "33",
wantFail: true,
},
{
name: "ipv4 mask bad value lower bound",
ifAddr: sockaddr.IfAddr{
SockAddr: sockaddr.MustIPv4Addr("127.0.0.1/8"),
},
operation: "mask",
value: "-1",
wantFail: true,
},
{
name: "ipv6 mask operand equals input ipv6 subnet mask",
ifAddr: sockaddr.IfAddr{
SockAddr: sockaddr.MustIPv6Addr("2001:0db8:85a3::8a2e:0370:7334/64"),
},
operation: "mask",
value: "64",
expected: "2001:db8:85a3::/64",
},
{
name: "ipv6 mask operand larger than input ipv6 subnet mask",
ifAddr: sockaddr.IfAddr{
SockAddr: sockaddr.MustIPv6Addr("2001:0db8:85a3::8a2e:0370:7334/64"),
},
operation: "mask",
value: "32",
expected: "2001:db8::/32",
},
{
name: "ipv6 mask operand smaller than input ipv6 subnet mask",
ifAddr: sockaddr.IfAddr{
SockAddr: sockaddr.MustIPv6Addr("2001:0db8:85a3::8a2e:0370:7334/64"),
},
operation: "mask",
value: "96",
expected: "2001:db8:85a3::8a2e:0:0/64",
},
{
name: "ipv6 mask bad value upper bound",
ifAddr: sockaddr.IfAddr{
SockAddr: sockaddr.MustIPv6Addr("::1/128"),
},
operation: "mask",
value: "129",
wantFail: true,
},
{
name: "ipv6 mask bad value lower bound",
ifAddr: sockaddr.IfAddr{
SockAddr: sockaddr.MustIPv6Addr("::1/128"),
},
operation: "mask",
value: "-1",
wantFail: true,
},
{
name: "unix unsupported operation",
ifAddr: sockaddr.IfAddr{
SockAddr: sockaddr.MustUnixSock("/tmp/bar"),
},
operation: "address",
value: "+123",
wantFail: true,
},
{
name: "unix unsupported operation",
ifAddr: sockaddr.IfAddr{
SockAddr: sockaddr.MustUnixSock("/tmp/foo"),
},
operation: "network",
value: "+123",
wantFail: true,
},
{
name: "unix unsupported operation",
ifAddr: sockaddr.IfAddr{
SockAddr: sockaddr.MustUnixSock("/tmp/foo"),
},
operation: "mask",
value: "8",
wantFail: true,
},
}
for i, test := range tests {
if test.name == "" {
t.Fatalf("test %d must have a name", i)
}
results, err := sockaddr.IfAddrsMath(test.operation, test.value, sockaddr.IfAddrs{test.ifAddr})
if test.wantFail {
if err != nil {
continue
} else {
t.Fatalf("%s: failed to fail math operation %q with value %q on %v", test.name, test.operation, test.value, test.ifAddr)
}
} else if err != nil {
t.Fatalf("%s: failed to compute math operation %q with value %q on %v", test.name, test.operation, test.value, test.ifAddr)
}
if len(results) != 1 {
t.Fatalf("%s: bad", test.name)
}
result := results[0]
switch saType := result.Type(); saType {
case sockaddr.TypeIPv4:
ipv4 := sockaddr.ToIPv4Addr(result.SockAddr)
if ipv4 == nil {
t.Fatalf("bad: %T %+#v", result, result)
}
if got := ipv4.String(); got != test.expected {
t.Errorf("unexpected result %q: want %q got %q", test.name, test.expected, got)
}
case sockaddr.TypeIPv6:
ipv6 := sockaddr.ToIPv6Addr(result.SockAddr)
if ipv6 == nil {
t.Fatalf("bad: %T %+#v", result, result)
}
if got := ipv6.String(); got != test.expected {
t.Errorf("unexpected result %q: want %q got %q", test.name, test.expected, got)
}
default:
t.Fatalf("bad")
}
}
}