summaryrefslogtreecommitdiffhomepage
path: root/pkg/tcpip/checker/checker.go
diff options
context:
space:
mode:
Diffstat (limited to 'pkg/tcpip/checker/checker.go')
-rw-r--r--pkg/tcpip/checker/checker.go1415
1 files changed, 0 insertions, 1415 deletions
diff --git a/pkg/tcpip/checker/checker.go b/pkg/tcpip/checker/checker.go
deleted file mode 100644
index 8830c45d7..000000000
--- a/pkg/tcpip/checker/checker.go
+++ /dev/null
@@ -1,1415 +0,0 @@
-// Copyright 2018 The gVisor Authors.
-//
-// 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 checker provides helper functions to check networking packets for
-// validity.
-package checker
-
-import (
- "encoding/binary"
- "reflect"
- "testing"
- "time"
-
- "github.com/google/go-cmp/cmp"
- "gvisor.dev/gvisor/pkg/tcpip"
- "gvisor.dev/gvisor/pkg/tcpip/buffer"
- "gvisor.dev/gvisor/pkg/tcpip/header"
- "gvisor.dev/gvisor/pkg/tcpip/seqnum"
-)
-
-// NetworkChecker is a function to check a property of a network packet.
-type NetworkChecker func(*testing.T, []header.Network)
-
-// TransportChecker is a function to check a property of a transport packet.
-type TransportChecker func(*testing.T, header.Transport)
-
-// ControlMessagesChecker is a function to check a property of ancillary data.
-type ControlMessagesChecker func(*testing.T, tcpip.ControlMessages)
-
-// IPv4 checks the validity and properties of the given IPv4 packet. It is
-// expected to be used in conjunction with other network checkers for specific
-// properties. For example, to check the source and destination address, one
-// would call:
-//
-// checker.IPv4(t, b, checker.SrcAddr(x), checker.DstAddr(y))
-func IPv4(t *testing.T, b []byte, checkers ...NetworkChecker) {
- t.Helper()
-
- ipv4 := header.IPv4(b)
-
- if !ipv4.IsValid(len(b)) {
- t.Error("Not a valid IPv4 packet")
- }
-
- xsum := ipv4.CalculateChecksum()
- if xsum != 0 && xsum != 0xffff {
- t.Errorf("Bad checksum: 0x%x, checksum in packet: 0x%x", xsum, ipv4.Checksum())
- }
-
- for _, f := range checkers {
- f(t, []header.Network{ipv4})
- }
- if t.Failed() {
- t.FailNow()
- }
-}
-
-// IPv6 checks the validity and properties of the given IPv6 packet. The usage
-// is similar to IPv4.
-func IPv6(t *testing.T, b []byte, checkers ...NetworkChecker) {
- t.Helper()
-
- ipv6 := header.IPv6(b)
- if !ipv6.IsValid(len(b)) {
- t.Error("Not a valid IPv6 packet")
- }
-
- for _, f := range checkers {
- f(t, []header.Network{ipv6})
- }
- if t.Failed() {
- t.FailNow()
- }
-}
-
-// SrcAddr creates a checker that checks the source address.
-func SrcAddr(addr tcpip.Address) NetworkChecker {
- return func(t *testing.T, h []header.Network) {
- t.Helper()
-
- if a := h[0].SourceAddress(); a != addr {
- t.Errorf("Bad source address, got %v, want %v", a, addr)
- }
- }
-}
-
-// DstAddr creates a checker that checks the destination address.
-func DstAddr(addr tcpip.Address) NetworkChecker {
- return func(t *testing.T, h []header.Network) {
- t.Helper()
-
- if a := h[0].DestinationAddress(); a != addr {
- t.Errorf("Bad destination address, got %v, want %v", a, addr)
- }
- }
-}
-
-// TTL creates a checker that checks the TTL (ipv4) or HopLimit (ipv6).
-func TTL(ttl uint8) NetworkChecker {
- return func(t *testing.T, h []header.Network) {
- t.Helper()
-
- var v uint8
- switch ip := h[0].(type) {
- case header.IPv4:
- v = ip.TTL()
- case header.IPv6:
- v = ip.HopLimit()
- }
- if v != ttl {
- t.Fatalf("Bad TTL, got = %d, want = %d", v, ttl)
- }
- }
-}
-
-// IPFullLength creates a checker for the full IP packet length. The
-// expected size is checked against both the Total Length in the
-// header and the number of bytes received.
-func IPFullLength(packetLength uint16) NetworkChecker {
- return func(t *testing.T, h []header.Network) {
- t.Helper()
-
- var v uint16
- var l uint16
- switch ip := h[0].(type) {
- case header.IPv4:
- v = ip.TotalLength()
- l = uint16(len(ip))
- case header.IPv6:
- v = ip.PayloadLength() + header.IPv6FixedHeaderSize
- l = uint16(len(ip))
- default:
- t.Fatalf("unexpected network header passed to checker, got = %T, want = header.IPv4 or header.IPv6", ip)
- }
- if l != packetLength {
- t.Errorf("bad packet length, got = %d, want = %d", l, packetLength)
- }
- if v != packetLength {
- t.Errorf("unexpected packet length in header, got = %d, want = %d", v, packetLength)
- }
- }
-}
-
-// IPv4HeaderLength creates a checker that checks the IPv4 Header length.
-func IPv4HeaderLength(headerLength int) NetworkChecker {
- return func(t *testing.T, h []header.Network) {
- t.Helper()
-
- switch ip := h[0].(type) {
- case header.IPv4:
- if hl := ip.HeaderLength(); hl != uint8(headerLength) {
- t.Errorf("Bad header length, got = %d, want = %d", hl, headerLength)
- }
- default:
- t.Fatalf("unexpected network header passed to checker, got = %T, want = header.IPv4", ip)
- }
- }
-}
-
-// PayloadLen creates a checker that checks the payload length.
-func PayloadLen(payloadLength int) NetworkChecker {
- return func(t *testing.T, h []header.Network) {
- t.Helper()
-
- if l := len(h[0].Payload()); l != payloadLength {
- t.Errorf("Bad payload length, got = %d, want = %d", l, payloadLength)
- }
- }
-}
-
-// IPPayload creates a checker that checks the payload.
-func IPPayload(payload []byte) NetworkChecker {
- return func(t *testing.T, h []header.Network) {
- t.Helper()
-
- got := h[0].Payload()
-
- // cmp.Diff does not consider nil slices equal to empty slices, but we do.
- if len(got) == 0 && len(payload) == 0 {
- return
- }
-
- if diff := cmp.Diff(payload, got); diff != "" {
- t.Errorf("payload mismatch (-want +got):\n%s", diff)
- }
- }
-}
-
-// IPv4Options returns a checker that checks the options in an IPv4 packet.
-func IPv4Options(want header.IPv4Options) NetworkChecker {
- return func(t *testing.T, h []header.Network) {
- t.Helper()
-
- ip, ok := h[0].(header.IPv4)
- if !ok {
- t.Fatalf("unexpected network header passed to checker, got = %T, want = header.IPv4", h[0])
- }
- options := ip.Options()
- // cmp.Diff does not consider nil slices equal to empty slices, but we do.
- if len(want) == 0 && len(options) == 0 {
- return
- }
- if diff := cmp.Diff(want, options); diff != "" {
- t.Errorf("options mismatch (-want +got):\n%s", diff)
- }
- }
-}
-
-// IPv4RouterAlert returns a checker that checks that the RouterAlert option is
-// set in an IPv4 packet.
-func IPv4RouterAlert() NetworkChecker {
- return func(t *testing.T, h []header.Network) {
- t.Helper()
- ip, ok := h[0].(header.IPv4)
- if !ok {
- t.Fatalf("unexpected network header passed to checker, got = %T, want = header.IPv4", h[0])
- }
- iterator := ip.Options().MakeIterator()
- for {
- opt, done, err := iterator.Next()
- if err != nil {
- t.Fatalf("error acquiring next IPv4 option %s", err)
- }
- if done {
- break
- }
- if opt.Type() != header.IPv4OptionRouterAlertType {
- continue
- }
- want := [header.IPv4OptionRouterAlertLength]byte{
- byte(header.IPv4OptionRouterAlertType),
- header.IPv4OptionRouterAlertLength,
- header.IPv4OptionRouterAlertValue,
- header.IPv4OptionRouterAlertValue,
- }
- if diff := cmp.Diff(want[:], opt.Contents()); diff != "" {
- t.Errorf("router alert option mismatch (-want +got):\n%s", diff)
- }
- return
- }
- t.Errorf("failed to find router alert option in %v", ip.Options())
- }
-}
-
-// FragmentOffset creates a checker that checks the FragmentOffset field.
-func FragmentOffset(offset uint16) NetworkChecker {
- return func(t *testing.T, h []header.Network) {
- t.Helper()
-
- // We only do this for IPv4 for now.
- switch ip := h[0].(type) {
- case header.IPv4:
- if v := ip.FragmentOffset(); v != offset {
- t.Errorf("Bad fragment offset, got = %d, want = %d", v, offset)
- }
- }
- }
-}
-
-// FragmentFlags creates a checker that checks the fragment flags field.
-func FragmentFlags(flags uint8) NetworkChecker {
- return func(t *testing.T, h []header.Network) {
- t.Helper()
-
- // We only do this for IPv4 for now.
- switch ip := h[0].(type) {
- case header.IPv4:
- if v := ip.Flags(); v != flags {
- t.Errorf("Bad fragment offset, got = %d, want = %d", v, flags)
- }
- }
- }
-}
-
-// ReceiveTClass creates a checker that checks the TCLASS field in
-// ControlMessages.
-func ReceiveTClass(want uint32) ControlMessagesChecker {
- return func(t *testing.T, cm tcpip.ControlMessages) {
- t.Helper()
- if !cm.HasTClass {
- t.Errorf("got cm.HasTClass = %t, want = true", cm.HasTClass)
- } else if got := cm.TClass; got != want {
- t.Errorf("got cm.TClass = %d, want %d", got, want)
- }
- }
-}
-
-// ReceiveTOS creates a checker that checks the TOS field in ControlMessages.
-func ReceiveTOS(want uint8) ControlMessagesChecker {
- return func(t *testing.T, cm tcpip.ControlMessages) {
- t.Helper()
- if !cm.HasTOS {
- t.Errorf("got cm.HasTOS = %t, want = true", cm.HasTOS)
- } else if got := cm.TOS; got != want {
- t.Errorf("got cm.TOS = %d, want %d", got, want)
- }
- }
-}
-
-// ReceiveIPPacketInfo creates a checker that checks the PacketInfo field in
-// ControlMessages.
-func ReceiveIPPacketInfo(want tcpip.IPPacketInfo) ControlMessagesChecker {
- return func(t *testing.T, cm tcpip.ControlMessages) {
- t.Helper()
- if !cm.HasIPPacketInfo {
- t.Errorf("got cm.HasIPPacketInfo = %t, want = true", cm.HasIPPacketInfo)
- } else if diff := cmp.Diff(want, cm.PacketInfo); diff != "" {
- t.Errorf("IPPacketInfo mismatch (-want +got):\n%s", diff)
- }
- }
-}
-
-// ReceiveOriginalDstAddr creates a checker that checks the OriginalDstAddress
-// field in ControlMessages.
-func ReceiveOriginalDstAddr(want tcpip.FullAddress) ControlMessagesChecker {
- return func(t *testing.T, cm tcpip.ControlMessages) {
- t.Helper()
- if !cm.HasOriginalDstAddress {
- t.Errorf("got cm.HasOriginalDstAddress = %t, want = true", cm.HasOriginalDstAddress)
- } else if diff := cmp.Diff(want, cm.OriginalDstAddress); diff != "" {
- t.Errorf("OriginalDstAddress mismatch (-want +got):\n%s", diff)
- }
- }
-}
-
-// TOS creates a checker that checks the TOS field.
-func TOS(tos uint8, label uint32) NetworkChecker {
- return func(t *testing.T, h []header.Network) {
- t.Helper()
-
- if v, l := h[0].TOS(); v != tos || l != label {
- t.Errorf("Bad TOS, got = (%d, %d), want = (%d,%d)", v, l, tos, label)
- }
- }
-}
-
-// Raw creates a checker that checks the bytes of payload.
-// The checker always checks the payload of the last network header.
-// For instance, in case of IPv6 fragments, the payload that will be checked
-// is the one containing the actual data that the packet is carrying, without
-// the bytes added by the IPv6 fragmentation.
-func Raw(want []byte) NetworkChecker {
- return func(t *testing.T, h []header.Network) {
- t.Helper()
-
- if got := h[len(h)-1].Payload(); !reflect.DeepEqual(got, want) {
- t.Errorf("Wrong payload, got %v, want %v", got, want)
- }
- }
-}
-
-// IPv6Fragment creates a checker that validates an IPv6 fragment.
-func IPv6Fragment(checkers ...NetworkChecker) NetworkChecker {
- return func(t *testing.T, h []header.Network) {
- t.Helper()
-
- if p := h[0].TransportProtocol(); p != header.IPv6FragmentHeader {
- t.Errorf("Bad protocol, got = %d, want = %d", p, header.UDPProtocolNumber)
- }
-
- ipv6Frag := header.IPv6Fragment(h[0].Payload())
- if !ipv6Frag.IsValid() {
- t.Error("Not a valid IPv6 fragment")
- }
-
- for _, f := range checkers {
- f(t, []header.Network{h[0], ipv6Frag})
- }
- if t.Failed() {
- t.FailNow()
- }
- }
-}
-
-// TCP creates a checker that checks that the transport protocol is TCP and
-// potentially additional transport header fields.
-func TCP(checkers ...TransportChecker) NetworkChecker {
- return func(t *testing.T, h []header.Network) {
- t.Helper()
-
- first := h[0]
- last := h[len(h)-1]
-
- if p := last.TransportProtocol(); p != header.TCPProtocolNumber {
- t.Errorf("Bad protocol, got = %d, want = %d", p, header.TCPProtocolNumber)
- }
-
- // Verify the checksum.
- tcp := header.TCP(last.Payload())
- l := uint16(len(tcp))
-
- xsum := header.Checksum([]byte(first.SourceAddress()), 0)
- xsum = header.Checksum([]byte(first.DestinationAddress()), xsum)
- xsum = header.Checksum([]byte{0, byte(last.TransportProtocol())}, xsum)
- xsum = header.Checksum([]byte{byte(l >> 8), byte(l)}, xsum)
- xsum = header.Checksum(tcp, xsum)
-
- if xsum != 0 && xsum != 0xffff {
- t.Errorf("Bad checksum: 0x%x, checksum in segment: 0x%x", xsum, tcp.Checksum())
- }
-
- // Run the transport checkers.
- for _, f := range checkers {
- f(t, tcp)
- }
- if t.Failed() {
- t.FailNow()
- }
- }
-}
-
-// UDP creates a checker that checks that the transport protocol is UDP and
-// potentially additional transport header fields.
-func UDP(checkers ...TransportChecker) NetworkChecker {
- return func(t *testing.T, h []header.Network) {
- t.Helper()
-
- last := h[len(h)-1]
-
- if p := last.TransportProtocol(); p != header.UDPProtocolNumber {
- t.Errorf("Bad protocol, got = %d, want = %d", p, header.UDPProtocolNumber)
- }
-
- udp := header.UDP(last.Payload())
- for _, f := range checkers {
- f(t, udp)
- }
- if t.Failed() {
- t.FailNow()
- }
- }
-}
-
-// SrcPort creates a checker that checks the source port.
-func SrcPort(port uint16) TransportChecker {
- return func(t *testing.T, h header.Transport) {
- t.Helper()
-
- if p := h.SourcePort(); p != port {
- t.Errorf("Bad source port, got = %d, want = %d", p, port)
- }
- }
-}
-
-// DstPort creates a checker that checks the destination port.
-func DstPort(port uint16) TransportChecker {
- return func(t *testing.T, h header.Transport) {
- t.Helper()
-
- if p := h.DestinationPort(); p != port {
- t.Errorf("Bad destination port, got = %d, want = %d", p, port)
- }
- }
-}
-
-// NoChecksum creates a checker that checks if the checksum is zero.
-func NoChecksum(noChecksum bool) TransportChecker {
- return func(t *testing.T, h header.Transport) {
- t.Helper()
-
- udp, ok := h.(header.UDP)
- if !ok {
- t.Fatalf("UDP header not found in h: %T", h)
- }
-
- if b := udp.Checksum() == 0; b != noChecksum {
- t.Errorf("bad checksum state, got %t, want %t", b, noChecksum)
- }
- }
-}
-
-// TCPSeqNum creates a checker that checks the sequence number.
-func TCPSeqNum(seq uint32) TransportChecker {
- return func(t *testing.T, h header.Transport) {
- t.Helper()
-
- tcp, ok := h.(header.TCP)
- if !ok {
- t.Fatalf("TCP header not found in h: %T", h)
- }
-
- if s := tcp.SequenceNumber(); s != seq {
- t.Errorf("Bad sequence number, got = %d, want = %d", s, seq)
- }
- }
-}
-
-// TCPAckNum creates a checker that checks the ack number.
-func TCPAckNum(seq uint32) TransportChecker {
- return func(t *testing.T, h header.Transport) {
- t.Helper()
-
- tcp, ok := h.(header.TCP)
- if !ok {
- t.Fatalf("TCP header not found in h: %T", h)
- }
-
- if s := tcp.AckNumber(); s != seq {
- t.Errorf("Bad ack number, got = %d, want = %d", s, seq)
- }
- }
-}
-
-// TCPWindow creates a checker that checks the tcp window.
-func TCPWindow(window uint16) TransportChecker {
- return func(t *testing.T, h header.Transport) {
- t.Helper()
-
- tcp, ok := h.(header.TCP)
- if !ok {
- t.Fatalf("TCP header not found in hdr : %T", h)
- }
-
- if w := tcp.WindowSize(); w != window {
- t.Errorf("Bad window, got %d, want %d", w, window)
- }
- }
-}
-
-// TCPWindowGreaterThanEq creates a checker that checks that the TCP window
-// is greater than or equal to the provided value.
-func TCPWindowGreaterThanEq(window uint16) TransportChecker {
- return func(t *testing.T, h header.Transport) {
- t.Helper()
-
- tcp, ok := h.(header.TCP)
- if !ok {
- t.Fatalf("TCP header not found in h: %T", h)
- }
-
- if w := tcp.WindowSize(); w < window {
- t.Errorf("Bad window, got %d, want > %d", w, window)
- }
- }
-}
-
-// TCPWindowLessThanEq creates a checker that checks that the tcp window
-// is less than or equal to the provided value.
-func TCPWindowLessThanEq(window uint16) TransportChecker {
- return func(t *testing.T, h header.Transport) {
- t.Helper()
-
- tcp, ok := h.(header.TCP)
- if !ok {
- t.Fatalf("TCP header not found in h: %T", h)
- }
-
- if w := tcp.WindowSize(); w > window {
- t.Errorf("Bad window, got %d, want < %d", w, window)
- }
- }
-}
-
-// TCPFlags creates a checker that checks the tcp flags.
-func TCPFlags(flags uint8) TransportChecker {
- return func(t *testing.T, h header.Transport) {
- t.Helper()
-
- tcp, ok := h.(header.TCP)
- if !ok {
- t.Fatalf("TCP header not found in h: %T", h)
- }
-
- if f := tcp.Flags(); f != flags {
- t.Errorf("Bad flags, got 0x%x, want 0x%x", f, flags)
- }
- }
-}
-
-// TCPFlagsMatch creates a checker that checks that the tcp flags, masked by the
-// given mask, match the supplied flags.
-func TCPFlagsMatch(flags, mask uint8) TransportChecker {
- return func(t *testing.T, h header.Transport) {
- t.Helper()
-
- tcp, ok := h.(header.TCP)
- if !ok {
- t.Fatalf("TCP header not found in h: %T", h)
- }
-
- if f := tcp.Flags(); (f & mask) != (flags & mask) {
- t.Errorf("Bad masked flags, got 0x%x, want 0x%x, mask 0x%x", f, flags, mask)
- }
- }
-}
-
-// TCPSynOptions creates a checker that checks the presence of TCP options in
-// SYN segments.
-//
-// If wndscale is negative, the window scale option must not be present.
-func TCPSynOptions(wantOpts header.TCPSynOptions) TransportChecker {
- return func(t *testing.T, h header.Transport) {
- t.Helper()
-
- tcp, ok := h.(header.TCP)
- if !ok {
- return
- }
- opts := tcp.Options()
- limit := len(opts)
- foundMSS := false
- foundWS := false
- foundTS := false
- foundSACKPermitted := false
- tsVal := uint32(0)
- tsEcr := uint32(0)
- for i := 0; i < limit; {
- switch opts[i] {
- case header.TCPOptionEOL:
- i = limit
- case header.TCPOptionNOP:
- i++
- case header.TCPOptionMSS:
- v := uint16(opts[i+2])<<8 | uint16(opts[i+3])
- if wantOpts.MSS != v {
- t.Errorf("Bad MSS, got = %d, want = %d", v, wantOpts.MSS)
- }
- foundMSS = true
- i += 4
- case header.TCPOptionWS:
- if wantOpts.WS < 0 {
- t.Error("WS present when it shouldn't be")
- }
- v := int(opts[i+2])
- if v != wantOpts.WS {
- t.Errorf("Bad WS, got = %d, want = %d", v, wantOpts.WS)
- }
- foundWS = true
- i += 3
- case header.TCPOptionTS:
- if i+9 >= limit {
- t.Errorf("TS Option truncated , option is only: %d bytes, want 10", limit-i)
- }
- if opts[i+1] != 10 {
- t.Errorf("Bad length %d for TS option, limit: %d", opts[i+1], limit)
- }
- tsVal = binary.BigEndian.Uint32(opts[i+2:])
- tsEcr = uint32(0)
- if tcp.Flags()&header.TCPFlagAck != 0 {
- // If the syn is an SYN-ACK then read
- // the tsEcr value as well.
- tsEcr = binary.BigEndian.Uint32(opts[i+6:])
- }
- foundTS = true
- i += 10
- case header.TCPOptionSACKPermitted:
- if i+1 >= limit {
- t.Errorf("SACKPermitted option truncated, option is only : %d bytes, want 2", limit-i)
- }
- if opts[i+1] != 2 {
- t.Errorf("Bad length %d for SACKPermitted option, limit: %d", opts[i+1], limit)
- }
- foundSACKPermitted = true
- i += 2
-
- default:
- i += int(opts[i+1])
- }
- }
-
- if !foundMSS {
- t.Errorf("MSS option not found. Options: %x", opts)
- }
-
- if !foundWS && wantOpts.WS >= 0 {
- t.Errorf("WS option not found. Options: %x", opts)
- }
- if wantOpts.TS && !foundTS {
- t.Errorf("TS option not found. Options: %x", opts)
- }
- if foundTS && tsVal == 0 {
- t.Error("TS option specified but the timestamp value is zero")
- }
- if foundTS && tsEcr == 0 && wantOpts.TSEcr != 0 {
- t.Errorf("TS option specified but TSEcr is incorrect, got = %d, want = %d", tsEcr, wantOpts.TSEcr)
- }
- if wantOpts.SACKPermitted && !foundSACKPermitted {
- t.Errorf("SACKPermitted option not found. Options: %x", opts)
- }
- }
-}
-
-// TCPTimestampChecker creates a checker that validates that a TCP segment has a
-// TCP Timestamp option if wantTS is true, it also compares the wantTSVal and
-// wantTSEcr values with those in the TCP segment (if present).
-//
-// If wantTSVal or wantTSEcr is zero then the corresponding comparison is
-// skipped.
-func TCPTimestampChecker(wantTS bool, wantTSVal uint32, wantTSEcr uint32) TransportChecker {
- return func(t *testing.T, h header.Transport) {
- t.Helper()
-
- tcp, ok := h.(header.TCP)
- if !ok {
- return
- }
- opts := []byte(tcp.Options())
- limit := len(opts)
- foundTS := false
- tsVal := uint32(0)
- tsEcr := uint32(0)
- for i := 0; i < limit; {
- switch opts[i] {
- case header.TCPOptionEOL:
- i = limit
- case header.TCPOptionNOP:
- i++
- case header.TCPOptionTS:
- if i+9 >= limit {
- t.Errorf("TS option found, but option is truncated, option length: %d, want 10 bytes", limit-i)
- }
- if opts[i+1] != 10 {
- t.Errorf("TS option found, but bad length specified: got = %d, want = 10", opts[i+1])
- }
- tsVal = binary.BigEndian.Uint32(opts[i+2:])
- tsEcr = binary.BigEndian.Uint32(opts[i+6:])
- foundTS = true
- i += 10
- default:
- // We don't recognize this option, just skip over it.
- if i+2 > limit {
- return
- }
- l := int(opts[i+1])
- if i < 2 || i+l > limit {
- return
- }
- i += l
- }
- }
-
- if wantTS != foundTS {
- t.Errorf("TS Option mismatch, got TS= %t, want TS= %t", foundTS, wantTS)
- }
- if wantTS && wantTSVal != 0 && wantTSVal != tsVal {
- t.Errorf("Timestamp value is incorrect, got = %d, want = %d", tsVal, wantTSVal)
- }
- if wantTS && wantTSEcr != 0 && tsEcr != wantTSEcr {
- t.Errorf("Timestamp Echo Reply is incorrect, got = %d, want = %d", tsEcr, wantTSEcr)
- }
- }
-}
-
-// TCPNoSACKBlockChecker creates a checker that verifies that the segment does
-// not contain any SACK blocks in the TCP options.
-func TCPNoSACKBlockChecker() TransportChecker {
- return TCPSACKBlockChecker(nil)
-}
-
-// TCPSACKBlockChecker creates a checker that verifies that the segment does
-// contain the specified SACK blocks in the TCP options.
-func TCPSACKBlockChecker(sackBlocks []header.SACKBlock) TransportChecker {
- return func(t *testing.T, h header.Transport) {
- t.Helper()
- tcp, ok := h.(header.TCP)
- if !ok {
- return
- }
- var gotSACKBlocks []header.SACKBlock
-
- opts := []byte(tcp.Options())
- limit := len(opts)
- for i := 0; i < limit; {
- switch opts[i] {
- case header.TCPOptionEOL:
- i = limit
- case header.TCPOptionNOP:
- i++
- case header.TCPOptionSACK:
- if i+2 > limit {
- // Malformed SACK block.
- t.Errorf("malformed SACK option in options: %v", opts)
- }
- sackOptionLen := int(opts[i+1])
- if i+sackOptionLen > limit || (sackOptionLen-2)%8 != 0 {
- // Malformed SACK block.
- t.Errorf("malformed SACK option length in options: %v", opts)
- }
- numBlocks := sackOptionLen / 8
- for j := 0; j < numBlocks; j++ {
- start := binary.BigEndian.Uint32(opts[i+2+j*8:])
- end := binary.BigEndian.Uint32(opts[i+2+j*8+4:])
- gotSACKBlocks = append(gotSACKBlocks, header.SACKBlock{
- Start: seqnum.Value(start),
- End: seqnum.Value(end),
- })
- }
- i += sackOptionLen
- default:
- // We don't recognize this option, just skip over it.
- if i+2 > limit {
- break
- }
- l := int(opts[i+1])
- if l < 2 || i+l > limit {
- break
- }
- i += l
- }
- }
-
- if !reflect.DeepEqual(gotSACKBlocks, sackBlocks) {
- t.Errorf("SACKBlocks are not equal, got = %v, want = %v", gotSACKBlocks, sackBlocks)
- }
- }
-}
-
-// Payload creates a checker that checks the payload.
-func Payload(want []byte) TransportChecker {
- return func(t *testing.T, h header.Transport) {
- t.Helper()
-
- if got := h.Payload(); !reflect.DeepEqual(got, want) {
- t.Errorf("Wrong payload, got %v, want %v", got, want)
- }
- }
-}
-
-// ICMPv4 creates a checker that checks that the transport protocol is ICMPv4
-// and potentially additional ICMPv4 header fields.
-func ICMPv4(checkers ...TransportChecker) NetworkChecker {
- return func(t *testing.T, h []header.Network) {
- t.Helper()
-
- last := h[len(h)-1]
-
- if p := last.TransportProtocol(); p != header.ICMPv4ProtocolNumber {
- t.Fatalf("Bad protocol, got %d, want %d", p, header.ICMPv4ProtocolNumber)
- }
-
- icmp := header.ICMPv4(last.Payload())
- for _, f := range checkers {
- f(t, icmp)
- }
- if t.Failed() {
- t.FailNow()
- }
- }
-}
-
-// ICMPv4Type creates a checker that checks the ICMPv4 Type field.
-func ICMPv4Type(want header.ICMPv4Type) TransportChecker {
- return func(t *testing.T, h header.Transport) {
- t.Helper()
-
- icmpv4, ok := h.(header.ICMPv4)
- if !ok {
- t.Fatalf("unexpected transport header passed to checker, got = %T, want = header.ICMPv4", h)
- }
- if got := icmpv4.Type(); got != want {
- t.Fatalf("unexpected icmp type, got = %d, want = %d", got, want)
- }
- }
-}
-
-// ICMPv4Code creates a checker that checks the ICMPv4 Code field.
-func ICMPv4Code(want header.ICMPv4Code) TransportChecker {
- return func(t *testing.T, h header.Transport) {
- t.Helper()
-
- icmpv4, ok := h.(header.ICMPv4)
- if !ok {
- t.Fatalf("unexpected transport header passed to checker, got = %T, want = header.ICMPv4", h)
- }
- if got := icmpv4.Code(); got != want {
- t.Fatalf("unexpected ICMP code, got = %d, want = %d", got, want)
- }
- }
-}
-
-// ICMPv4Ident creates a checker that checks the ICMPv4 echo Ident.
-func ICMPv4Ident(want uint16) TransportChecker {
- return func(t *testing.T, h header.Transport) {
- t.Helper()
-
- icmpv4, ok := h.(header.ICMPv4)
- if !ok {
- t.Fatalf("unexpected transport header passed to checker, got = %T, want = header.ICMPv4", h)
- }
- if got := icmpv4.Ident(); got != want {
- t.Fatalf("unexpected ICMP ident, got = %d, want = %d", got, want)
- }
- }
-}
-
-// ICMPv4Seq creates a checker that checks the ICMPv4 echo Sequence.
-func ICMPv4Seq(want uint16) TransportChecker {
- return func(t *testing.T, h header.Transport) {
- t.Helper()
-
- icmpv4, ok := h.(header.ICMPv4)
- if !ok {
- t.Fatalf("unexpected transport header passed to checker, got = %T, want = header.ICMPv4", h)
- }
- if got := icmpv4.Sequence(); got != want {
- t.Fatalf("unexpected ICMP sequence, got = %d, want = %d", got, want)
- }
- }
-}
-
-// ICMPv4Pointer creates a checker that checks the ICMPv4 Param Problem pointer.
-func ICMPv4Pointer(want uint8) TransportChecker {
- return func(t *testing.T, h header.Transport) {
- t.Helper()
-
- icmpv4, ok := h.(header.ICMPv4)
- if !ok {
- t.Fatalf("unexpected transport header passed to checker, got = %T, want = header.ICMPv4", h)
- }
- if got := icmpv4.Pointer(); got != want {
- t.Fatalf("unexpected ICMP Param Problem pointer, got = %d, want = %d", got, want)
- }
- }
-}
-
-// ICMPv4Checksum creates a checker that checks the ICMPv4 Checksum.
-// This assumes that the payload exactly makes up the rest of the slice.
-func ICMPv4Checksum() TransportChecker {
- return func(t *testing.T, h header.Transport) {
- t.Helper()
-
- icmpv4, ok := h.(header.ICMPv4)
- if !ok {
- t.Fatalf("unexpected transport header passed to checker, got = %T, want = header.ICMPv4", h)
- }
- heldChecksum := icmpv4.Checksum()
- icmpv4.SetChecksum(0)
- newChecksum := ^header.Checksum(icmpv4, 0)
- icmpv4.SetChecksum(heldChecksum)
- if heldChecksum != newChecksum {
- t.Errorf("unexpected ICMP checksum, got = %d, want = %d", heldChecksum, newChecksum)
- }
- }
-}
-
-// ICMPv4Payload creates a checker that checks the payload in an ICMPv4 packet.
-func ICMPv4Payload(want []byte) TransportChecker {
- return func(t *testing.T, h header.Transport) {
- t.Helper()
-
- icmpv4, ok := h.(header.ICMPv4)
- if !ok {
- t.Fatalf("unexpected transport header passed to checker, got = %T, want = header.ICMPv4", h)
- }
- payload := icmpv4.Payload()
-
- // cmp.Diff does not consider nil slices equal to empty slices, but we do.
- if len(want) == 0 && len(payload) == 0 {
- return
- }
-
- if diff := cmp.Diff(want, payload); diff != "" {
- t.Errorf("ICMP payload mismatch (-want +got):\n%s", diff)
- }
- }
-}
-
-// ICMPv6 creates a checker that checks that the transport protocol is ICMPv6 and
-// potentially additional ICMPv6 header fields.
-//
-// ICMPv6 will validate the checksum field before calling checkers.
-func ICMPv6(checkers ...TransportChecker) NetworkChecker {
- return func(t *testing.T, h []header.Network) {
- t.Helper()
-
- last := h[len(h)-1]
-
- if p := last.TransportProtocol(); p != header.ICMPv6ProtocolNumber {
- t.Fatalf("Bad protocol, got %d, want %d", p, header.ICMPv6ProtocolNumber)
- }
-
- icmp := header.ICMPv6(last.Payload())
- if got, want := icmp.Checksum(), header.ICMPv6Checksum(icmp, last.SourceAddress(), last.DestinationAddress(), buffer.VectorisedView{}); got != want {
- t.Fatalf("Bad ICMPv6 checksum; got %d, want %d", got, want)
- }
-
- for _, f := range checkers {
- f(t, icmp)
- }
- if t.Failed() {
- t.FailNow()
- }
- }
-}
-
-// ICMPv6Type creates a checker that checks the ICMPv6 Type field.
-func ICMPv6Type(want header.ICMPv6Type) TransportChecker {
- return func(t *testing.T, h header.Transport) {
- t.Helper()
-
- icmpv6, ok := h.(header.ICMPv6)
- if !ok {
- t.Fatalf("unexpected transport header passed to checker, got = %T, want = header.ICMPv6", h)
- }
- if got := icmpv6.Type(); got != want {
- t.Fatalf("unexpected icmp type, got = %d, want = %d", got, want)
- }
- }
-}
-
-// ICMPv6Code creates a checker that checks the ICMPv6 Code field.
-func ICMPv6Code(want header.ICMPv6Code) TransportChecker {
- return func(t *testing.T, h header.Transport) {
- t.Helper()
-
- icmpv6, ok := h.(header.ICMPv6)
- if !ok {
- t.Fatalf("unexpected transport header passed to checker, got = %T, want = header.ICMPv6", h)
- }
- if got := icmpv6.Code(); got != want {
- t.Fatalf("unexpected ICMP code, got = %d, want = %d", got, want)
- }
- }
-}
-
-// ICMPv6TypeSpecific creates a checker that checks the ICMPv6 TypeSpecific
-// field.
-func ICMPv6TypeSpecific(want uint32) TransportChecker {
- return func(t *testing.T, h header.Transport) {
- t.Helper()
-
- icmpv6, ok := h.(header.ICMPv6)
- if !ok {
- t.Fatalf("unexpected transport header passed to checker, got = %T, want = header.ICMPv6", h)
- }
- if got := icmpv6.TypeSpecific(); got != want {
- t.Fatalf("unexpected ICMP TypeSpecific, got = %d, want = %d", got, want)
- }
- }
-}
-
-// ICMPv6Payload creates a checker that checks the payload in an ICMPv6 packet.
-func ICMPv6Payload(want []byte) TransportChecker {
- return func(t *testing.T, h header.Transport) {
- t.Helper()
-
- icmpv6, ok := h.(header.ICMPv6)
- if !ok {
- t.Fatalf("unexpected transport header passed to checker, got = %T, want = header.ICMPv6", h)
- }
- payload := icmpv6.Payload()
-
- // cmp.Diff does not consider nil slices equal to empty slices, but we do.
- if len(want) == 0 && len(payload) == 0 {
- return
- }
-
- if diff := cmp.Diff(want, payload); diff != "" {
- t.Errorf("ICMP payload mismatch (-want +got):\n%s", diff)
- }
- }
-}
-
-// MLD creates a checker that checks that the packet contains a valid MLD
-// message for type of mldType, with potentially additional checks specified by
-// checkers.
-//
-// Checkers may assume that a valid ICMPv6 is passed to it containing a valid
-// MLD message as far as the size of the message (minSize) is concerned. The
-// values within the message are up to checkers to validate.
-func MLD(msgType header.ICMPv6Type, minSize int, checkers ...TransportChecker) NetworkChecker {
- return func(t *testing.T, h []header.Network) {
- t.Helper()
-
- // Check normal ICMPv6 first.
- ICMPv6(
- ICMPv6Type(msgType),
- ICMPv6Code(0))(t, h)
-
- last := h[len(h)-1]
-
- icmp := header.ICMPv6(last.Payload())
- if got := len(icmp.MessageBody()); got < minSize {
- t.Fatalf("ICMPv6 MLD (type = %d) payload size of %d is less than the minimum size of %d", msgType, got, minSize)
- }
-
- for _, f := range checkers {
- f(t, icmp)
- }
- if t.Failed() {
- t.FailNow()
- }
- }
-}
-
-// MLDMaxRespDelay creates a checker that checks the Maximum Response Delay
-// field of a MLD message.
-//
-// The returned TransportChecker assumes that a valid ICMPv6 is passed to it
-// containing a valid MLD message as far as the size is concerned.
-func MLDMaxRespDelay(want time.Duration) TransportChecker {
- return func(t *testing.T, h header.Transport) {
- t.Helper()
-
- icmp := h.(header.ICMPv6)
- ns := header.MLD(icmp.MessageBody())
-
- if got := ns.MaximumResponseDelay(); got != want {
- t.Errorf("got %T.MaximumResponseDelay() = %s, want = %s", ns, got, want)
- }
- }
-}
-
-// MLDMulticastAddress creates a checker that checks the Multicast Address
-// field of a MLD message.
-//
-// The returned TransportChecker assumes that a valid ICMPv6 is passed to it
-// containing a valid MLD message as far as the size is concerned.
-func MLDMulticastAddress(want tcpip.Address) TransportChecker {
- return func(t *testing.T, h header.Transport) {
- t.Helper()
-
- icmp := h.(header.ICMPv6)
- ns := header.MLD(icmp.MessageBody())
-
- if got := ns.MulticastAddress(); got != want {
- t.Errorf("got %T.MulticastAddress() = %s, want = %s", ns, got, want)
- }
- }
-}
-
-// NDP creates a checker that checks that the packet contains a valid NDP
-// message for type of ty, with potentially additional checks specified by
-// checkers.
-//
-// Checkers may assume that a valid ICMPv6 is passed to it containing a valid
-// NDP message as far as the size of the message (minSize) is concerned. The
-// values within the message are up to checkers to validate.
-func NDP(msgType header.ICMPv6Type, minSize int, checkers ...TransportChecker) NetworkChecker {
- return func(t *testing.T, h []header.Network) {
- t.Helper()
-
- // Check normal ICMPv6 first.
- ICMPv6(
- ICMPv6Type(msgType),
- ICMPv6Code(0))(t, h)
-
- last := h[len(h)-1]
-
- icmp := header.ICMPv6(last.Payload())
- if got := len(icmp.MessageBody()); got < minSize {
- t.Fatalf("ICMPv6 NDP (type = %d) payload size of %d is less than the minimum size of %d", msgType, got, minSize)
- }
-
- for _, f := range checkers {
- f(t, icmp)
- }
- if t.Failed() {
- t.FailNow()
- }
- }
-}
-
-// NDPNS creates a checker that checks that the packet contains a valid NDP
-// Neighbor Solicitation message (as per the raw wire format), with potentially
-// additional checks specified by checkers.
-//
-// Checkers may assume that a valid ICMPv6 is passed to it containing a valid
-// NDPNS message as far as the size of the message is concerned. The values
-// within the message are up to checkers to validate.
-func NDPNS(checkers ...TransportChecker) NetworkChecker {
- return NDP(header.ICMPv6NeighborSolicit, header.NDPNSMinimumSize, checkers...)
-}
-
-// NDPNSTargetAddress creates a checker that checks the Target Address field of
-// a header.NDPNeighborSolicit.
-//
-// The returned TransportChecker assumes that a valid ICMPv6 is passed to it
-// containing a valid NDPNS message as far as the size is concerned.
-func NDPNSTargetAddress(want tcpip.Address) TransportChecker {
- return func(t *testing.T, h header.Transport) {
- t.Helper()
-
- icmp := h.(header.ICMPv6)
- ns := header.NDPNeighborSolicit(icmp.MessageBody())
-
- if got := ns.TargetAddress(); got != want {
- t.Errorf("got %T.TargetAddress() = %s, want = %s", ns, got, want)
- }
- }
-}
-
-// NDPNA creates a checker that checks that the packet contains a valid NDP
-// Neighbor Advertisement message (as per the raw wire format), with potentially
-// additional checks specified by checkers.
-//
-// Checkers may assume that a valid ICMPv6 is passed to it containing a valid
-// NDPNA message as far as the size of the message is concerned. The values
-// within the message are up to checkers to validate.
-func NDPNA(checkers ...TransportChecker) NetworkChecker {
- return NDP(header.ICMPv6NeighborAdvert, header.NDPNAMinimumSize, checkers...)
-}
-
-// NDPNATargetAddress creates a checker that checks the Target Address field of
-// a header.NDPNeighborAdvert.
-//
-// The returned TransportChecker assumes that a valid ICMPv6 is passed to it
-// containing a valid NDPNA message as far as the size is concerned.
-func NDPNATargetAddress(want tcpip.Address) TransportChecker {
- return func(t *testing.T, h header.Transport) {
- t.Helper()
-
- icmp := h.(header.ICMPv6)
- na := header.NDPNeighborAdvert(icmp.MessageBody())
-
- if got := na.TargetAddress(); got != want {
- t.Errorf("got %T.TargetAddress() = %s, want = %s", na, got, want)
- }
- }
-}
-
-// NDPNASolicitedFlag creates a checker that checks the Solicited field of
-// a header.NDPNeighborAdvert.
-//
-// The returned TransportChecker assumes that a valid ICMPv6 is passed to it
-// containing a valid NDPNA message as far as the size is concerned.
-func NDPNASolicitedFlag(want bool) TransportChecker {
- return func(t *testing.T, h header.Transport) {
- t.Helper()
-
- icmp := h.(header.ICMPv6)
- na := header.NDPNeighborAdvert(icmp.MessageBody())
-
- if got := na.SolicitedFlag(); got != want {
- t.Errorf("got %T.SolicitedFlag = %t, want = %t", na, got, want)
- }
- }
-}
-
-// ndpOptions checks that optsBuf only contains opts.
-func ndpOptions(t *testing.T, optsBuf header.NDPOptions, opts []header.NDPOption) {
- t.Helper()
-
- it, err := optsBuf.Iter(true)
- if err != nil {
- t.Errorf("optsBuf.Iter(true): %s", err)
- return
- }
-
- i := 0
- for {
- opt, done, err := it.Next()
- if err != nil {
- // This should never happen as Iter(true) above did not return an error.
- t.Fatalf("unexpected error when iterating over NDP options: %s", err)
- }
- if done {
- break
- }
-
- if i >= len(opts) {
- t.Errorf("got unexpected option: %s", opt)
- continue
- }
-
- switch wantOpt := opts[i].(type) {
- case header.NDPSourceLinkLayerAddressOption:
- gotOpt, ok := opt.(header.NDPSourceLinkLayerAddressOption)
- if !ok {
- t.Errorf("got type = %T at index = %d; want = %T", opt, i, wantOpt)
- } else if got, want := gotOpt.EthernetAddress(), wantOpt.EthernetAddress(); got != want {
- t.Errorf("got EthernetAddress() = %s at index %d, want = %s", got, i, want)
- }
- case header.NDPTargetLinkLayerAddressOption:
- gotOpt, ok := opt.(header.NDPTargetLinkLayerAddressOption)
- if !ok {
- t.Errorf("got type = %T at index = %d; want = %T", opt, i, wantOpt)
- } else if got, want := gotOpt.EthernetAddress(), wantOpt.EthernetAddress(); got != want {
- t.Errorf("got EthernetAddress() = %s at index %d, want = %s", got, i, want)
- }
- default:
- t.Fatalf("checker not implemented for expected NDP option: %T", wantOpt)
- }
-
- i++
- }
-
- if missing := opts[i:]; len(missing) > 0 {
- t.Errorf("missing options: %s", missing)
- }
-}
-
-// NDPNAOptions creates a checker that checks that the packet contains the
-// provided NDP options within an NDP Neighbor Solicitation message.
-//
-// The returned TransportChecker assumes that a valid ICMPv6 is passed to it
-// containing a valid NDPNA message as far as the size is concerned.
-func NDPNAOptions(opts []header.NDPOption) TransportChecker {
- return func(t *testing.T, h header.Transport) {
- t.Helper()
-
- icmp := h.(header.ICMPv6)
- na := header.NDPNeighborAdvert(icmp.MessageBody())
- ndpOptions(t, na.Options(), opts)
- }
-}
-
-// NDPNSOptions creates a checker that checks that the packet contains the
-// provided NDP options within an NDP Neighbor Solicitation message.
-//
-// The returned TransportChecker assumes that a valid ICMPv6 is passed to it
-// containing a valid NDPNS message as far as the size is concerned.
-func NDPNSOptions(opts []header.NDPOption) TransportChecker {
- return func(t *testing.T, h header.Transport) {
- t.Helper()
-
- icmp := h.(header.ICMPv6)
- ns := header.NDPNeighborSolicit(icmp.MessageBody())
- ndpOptions(t, ns.Options(), opts)
- }
-}
-
-// NDPRS creates a checker that checks that the packet contains a valid NDP
-// Router Solicitation message (as per the raw wire format).
-//
-// Checkers may assume that a valid ICMPv6 is passed to it containing a valid
-// NDPRS as far as the size of the message is concerned. The values within the
-// message are up to checkers to validate.
-func NDPRS(checkers ...TransportChecker) NetworkChecker {
- return NDP(header.ICMPv6RouterSolicit, header.NDPRSMinimumSize, checkers...)
-}
-
-// NDPRSOptions creates a checker that checks that the packet contains the
-// provided NDP options within an NDP Router Solicitation message.
-//
-// The returned TransportChecker assumes that a valid ICMPv6 is passed to it
-// containing a valid NDPRS message as far as the size is concerned.
-func NDPRSOptions(opts []header.NDPOption) TransportChecker {
- return func(t *testing.T, h header.Transport) {
- t.Helper()
-
- icmp := h.(header.ICMPv6)
- rs := header.NDPRouterSolicit(icmp.MessageBody())
- ndpOptions(t, rs.Options(), opts)
- }
-}
-
-// IGMP checks the validity and properties of the given IGMP packet. It is
-// expected to be used in conjunction with other IGMP transport checkers for
-// specific properties.
-func IGMP(checkers ...TransportChecker) NetworkChecker {
- return func(t *testing.T, h []header.Network) {
- t.Helper()
-
- last := h[len(h)-1]
-
- if p := last.TransportProtocol(); p != header.IGMPProtocolNumber {
- t.Fatalf("Bad protocol, got %d, want %d", p, header.IGMPProtocolNumber)
- }
-
- igmp := header.IGMP(last.Payload())
- for _, f := range checkers {
- f(t, igmp)
- }
- if t.Failed() {
- t.FailNow()
- }
- }
-}
-
-// IGMPType creates a checker that checks the IGMP Type field.
-func IGMPType(want header.IGMPType) TransportChecker {
- return func(t *testing.T, h header.Transport) {
- t.Helper()
-
- igmp, ok := h.(header.IGMP)
- if !ok {
- t.Fatalf("got transport header = %T, want = header.IGMP", h)
- }
- if got := igmp.Type(); got != want {
- t.Errorf("got igmp.Type() = %d, want = %d", got, want)
- }
- }
-}
-
-// IGMPMaxRespTime creates a checker that checks the IGMP Max Resp Time field.
-func IGMPMaxRespTime(want time.Duration) TransportChecker {
- return func(t *testing.T, h header.Transport) {
- t.Helper()
-
- igmp, ok := h.(header.IGMP)
- if !ok {
- t.Fatalf("got transport header = %T, want = header.IGMP", h)
- }
- if got := igmp.MaxRespTime(); got != want {
- t.Errorf("got igmp.MaxRespTime() = %s, want = %s", got, want)
- }
- }
-}
-
-// IGMPGroupAddress creates a checker that checks the IGMP Group Address field.
-func IGMPGroupAddress(want tcpip.Address) TransportChecker {
- return func(t *testing.T, h header.Transport) {
- t.Helper()
-
- igmp, ok := h.(header.IGMP)
- if !ok {
- t.Fatalf("got transport header = %T, want = header.IGMP", h)
- }
- if got := igmp.GroupAddress(); got != want {
- t.Errorf("got igmp.GroupAddress() = %s, want = %s", got, want)
- }
- }
-}