diff options
-rw-r--r-- | pkg/tcpip/tcpip.go | 37 | ||||
-rw-r--r-- | pkg/tcpip/tcpip_test.go | 55 |
2 files changed, 92 insertions, 0 deletions
diff --git a/pkg/tcpip/tcpip.go b/pkg/tcpip/tcpip.go index a5259341b..61272cb05 100644 --- a/pkg/tcpip/tcpip.go +++ b/pkg/tcpip/tcpip.go @@ -666,6 +666,43 @@ func (a Address) String() string { switch len(a) { case 4: return fmt.Sprintf("%d.%d.%d.%d", int(a[0]), int(a[1]), int(a[2]), int(a[3])) + case 16: + // Find the longest subsequence of hexadecimal zeros. + start, end := -1, -1 + for i := 0; i < len(a); i += 2 { + j := i + for j < len(a) && a[j] == 0 && a[j+1] == 0 { + j += 2 + } + if j > i+2 && j-i > end-start { + start, end = i, j + } + } + + var b strings.Builder + for i := 0; i < len(a); i += 2 { + if i == start { + b.WriteString("::") + i = end + if end >= len(a) { + break + } + } else if i > 0 { + b.WriteByte(':') + } + v := uint16(a[i+0])<<8 | uint16(a[i+1]) + if v == 0 { + b.WriteByte('0') + } else { + const digits = "0123456789abcdef" + for i := uint(3); i < 4; i-- { + if v := v >> (i * 4); v != 0 { + b.WriteByte(digits[v&0xf]) + } + } + } + } + return b.String() default: return fmt.Sprintf("%x", []byte(a)) } diff --git a/pkg/tcpip/tcpip_test.go b/pkg/tcpip/tcpip_test.go index 6bf82d632..9b20c74c6 100644 --- a/pkg/tcpip/tcpip_test.go +++ b/pkg/tcpip/tcpip_test.go @@ -15,6 +15,7 @@ package tcpip import ( + "net" "testing" ) @@ -138,3 +139,57 @@ func TestRouteMatch(t *testing.T) { } } } + +func TestAddressString(t *testing.T) { + for _, want := range []string{ + // Taken from stdlib. + "2001:db8::123:12:1", + "2001:db8::1", + "2001:db8:0:1:0:1:0:1", + "2001:db8:1:0:1:0:1:0", + "2001::1:0:0:1", + "2001:db8:0:0:1::", + "2001:db8::1:0:0:1", + "2001:db8::a:b:c:d", + + // Leading zeros. + "::1", + // Trailing zeros. + "8::", + // No zeros. + "1:1:1:1:1:1:1:1", + // Longer sequence is after other zeros, but not at the end. + "1:0:0:1::1", + // Longer sequence is at the beginning, shorter sequence is at + // the end. + "::1:1:1:0:0", + // Longer sequence is not at the beginning, shorter sequence is + // at the end. + "1::1:1:0:0", + // Longer sequence is at the beginning, shorter sequence is not + // at the end. + "::1:1:0:0:1", + // Neither sequence is at an end, longer is after shorter. + "1:0:0:1::1", + // Shorter sequence is at the beginning, longer sequence is not + // at the end. + "0:0:1:1::1", + // Shorter sequence is at the beginning, longer sequence is at + // the end. + "0:0:1:1:1::", + // Short sequences at both ends, longer one in the middle. + "0:1:1::1:1:0", + // Short sequences at both ends, longer one in the middle. + "0:1::1:0:0", + // Short sequences at both ends, longer one in the middle. + "0:0:1::1:0", + // Longer sequence surrounded by shorter sequences, but none at + // the end. + "1:0:1::1:0:1", + } { + addr := Address(net.ParseIP(want)) + if got := addr.String(); got != want { + t.Errorf("Address(%x).String() = '%s', want = '%s'", addr, got, want) + } + } +} |