gvisor.dev/gvisor@v0.0.0-20240520182842-f9d4d51c7e0f/pkg/tcpip/header/ipv4_test.go (about)

     1  // Copyright 2020 The gVisor Authors.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package header_test
    16  
    17  import (
    18  	"testing"
    19  
    20  	"github.com/google/go-cmp/cmp"
    21  	"gvisor.dev/gvisor/pkg/tcpip"
    22  	"gvisor.dev/gvisor/pkg/tcpip/header"
    23  	"gvisor.dev/gvisor/pkg/tcpip/prependable"
    24  )
    25  
    26  func TestIPv4OptionsSerializer(t *testing.T) {
    27  	optCases := []struct {
    28  		name   string
    29  		option []header.IPv4SerializableOption
    30  		expect []byte
    31  	}{
    32  		{
    33  			name: "NOP",
    34  			option: []header.IPv4SerializableOption{
    35  				&header.IPv4SerializableNOPOption{},
    36  			},
    37  			expect: []byte{1, 0, 0, 0},
    38  		},
    39  		{
    40  			name: "ListEnd",
    41  			option: []header.IPv4SerializableOption{
    42  				&header.IPv4SerializableListEndOption{},
    43  			},
    44  			expect: []byte{0, 0, 0, 0},
    45  		},
    46  		{
    47  			name: "RouterAlert",
    48  			option: []header.IPv4SerializableOption{
    49  				&header.IPv4SerializableRouterAlertOption{},
    50  			},
    51  			expect: []byte{148, 4, 0, 0},
    52  		}, {
    53  			name: "NOP and RouterAlert",
    54  			option: []header.IPv4SerializableOption{
    55  				&header.IPv4SerializableNOPOption{},
    56  				&header.IPv4SerializableRouterAlertOption{},
    57  			},
    58  			expect: []byte{1, 148, 4, 0, 0, 0, 0, 0},
    59  		},
    60  	}
    61  
    62  	for _, opt := range optCases {
    63  		t.Run(opt.name, func(t *testing.T) {
    64  			s := header.IPv4OptionsSerializer(opt.option)
    65  			l := s.Length()
    66  			if got := len(opt.expect); got != int(l) {
    67  				t.Fatalf("s.Length() = %d, want = %d", got, l)
    68  			}
    69  			b := make([]byte, l)
    70  			for i := range b {
    71  				// Fill the buffer with full bytes to ensure padding is being set
    72  				// correctly.
    73  				b[i] = 0xFF
    74  			}
    75  			if serializedLength := s.Serialize(b); serializedLength != l {
    76  				t.Fatalf("s.Serialize(_) = %d, want %d", serializedLength, l)
    77  			}
    78  			if diff := cmp.Diff(opt.expect, b); diff != "" {
    79  				t.Errorf("mismatched serialized option (-want +got):\n%s", diff)
    80  			}
    81  		})
    82  	}
    83  }
    84  
    85  // TestIPv4Encode checks that ipv4.Encode correctly fills out the requested
    86  // fields when options are supplied.
    87  func TestIPv4EncodeOptions(t *testing.T) {
    88  	tests := []struct {
    89  		name           string
    90  		numberOfNops   int
    91  		encodedOptions header.IPv4Options // reply should look like this
    92  		wantIHL        int
    93  	}{
    94  		{
    95  			name:    "valid no options",
    96  			wantIHL: header.IPv4MinimumSize,
    97  		},
    98  		{
    99  			name:           "one byte options",
   100  			numberOfNops:   1,
   101  			encodedOptions: header.IPv4Options{1, 0, 0, 0},
   102  			wantIHL:        header.IPv4MinimumSize + 4,
   103  		},
   104  		{
   105  			name:           "two byte options",
   106  			numberOfNops:   2,
   107  			encodedOptions: header.IPv4Options{1, 1, 0, 0},
   108  			wantIHL:        header.IPv4MinimumSize + 4,
   109  		},
   110  		{
   111  			name:           "three byte options",
   112  			numberOfNops:   3,
   113  			encodedOptions: header.IPv4Options{1, 1, 1, 0},
   114  			wantIHL:        header.IPv4MinimumSize + 4,
   115  		},
   116  		{
   117  			name:           "four byte options",
   118  			numberOfNops:   4,
   119  			encodedOptions: header.IPv4Options{1, 1, 1, 1},
   120  			wantIHL:        header.IPv4MinimumSize + 4,
   121  		},
   122  		{
   123  			name:           "five byte options",
   124  			numberOfNops:   5,
   125  			encodedOptions: header.IPv4Options{1, 1, 1, 1, 1, 0, 0, 0},
   126  			wantIHL:        header.IPv4MinimumSize + 8,
   127  		},
   128  		{
   129  			name:         "thirty nine byte options",
   130  			numberOfNops: 39,
   131  			encodedOptions: header.IPv4Options{
   132  				1, 1, 1, 1, 1, 1, 1, 1,
   133  				1, 1, 1, 1, 1, 1, 1, 1,
   134  				1, 1, 1, 1, 1, 1, 1, 1,
   135  				1, 1, 1, 1, 1, 1, 1, 1,
   136  				1, 1, 1, 1, 1, 1, 1, 0,
   137  			},
   138  			wantIHL: header.IPv4MinimumSize + 40,
   139  		},
   140  	}
   141  	for _, test := range tests {
   142  		t.Run(test.name, func(t *testing.T) {
   143  			serializeOpts := header.IPv4OptionsSerializer(make([]header.IPv4SerializableOption, test.numberOfNops))
   144  			for i := range serializeOpts {
   145  				serializeOpts[i] = &header.IPv4SerializableNOPOption{}
   146  			}
   147  			paddedOptionLength := serializeOpts.Length()
   148  			ipHeaderLength := int(header.IPv4MinimumSize + paddedOptionLength)
   149  			if ipHeaderLength > header.IPv4MaximumHeaderSize {
   150  				t.Fatalf("IP header length too large: got = %d, want <= %d ", ipHeaderLength, header.IPv4MaximumHeaderSize)
   151  			}
   152  			totalLen := uint16(ipHeaderLength)
   153  			hdr := prependable.New(int(totalLen))
   154  			ip := header.IPv4(hdr.Prepend(ipHeaderLength))
   155  			// To check the padding works, poison the last byte of the options space.
   156  			if paddedOptionLength != serializeOpts.Length() {
   157  				ip.SetHeaderLength(uint8(ipHeaderLength))
   158  				ip.Options()[paddedOptionLength-1] = 0xff
   159  				ip.SetHeaderLength(0)
   160  			}
   161  			ip.Encode(&header.IPv4Fields{
   162  				Options: serializeOpts,
   163  			})
   164  			options := ip.Options()
   165  			wantOptions := test.encodedOptions
   166  			if got, want := int(ip.HeaderLength()), test.wantIHL; got != want {
   167  				t.Errorf("got IHL of %d, want %d", got, want)
   168  			}
   169  
   170  			// cmp.Diff does not consider nil slices equal to empty slices, but we do.
   171  			if len(wantOptions) == 0 && len(options) == 0 {
   172  				return
   173  			}
   174  
   175  			if diff := cmp.Diff(wantOptions, options); diff != "" {
   176  				t.Errorf("options mismatch (-want +got):\n%s", diff)
   177  			}
   178  		})
   179  	}
   180  }
   181  
   182  func TestIsV4LinkLocalUnicastAddress(t *testing.T) {
   183  	tests := []struct {
   184  		name     string
   185  		addr     string
   186  		expected bool
   187  	}{
   188  		{
   189  			name:     "Valid (lowest)",
   190  			addr:     "\xa9\xfe\x00\x00",
   191  			expected: true,
   192  		},
   193  		{
   194  			name:     "Valid (highest)",
   195  			addr:     "\xa9\xfe\xff\xff",
   196  			expected: true,
   197  		},
   198  		{
   199  			name:     "Invalid (before subnet)",
   200  			addr:     "\xa9\xfd\xff\xff",
   201  			expected: false,
   202  		},
   203  		{
   204  			name:     "Invalid (after subnet)",
   205  			addr:     "\xa9\xff\x00\x00",
   206  			expected: false,
   207  		},
   208  	}
   209  
   210  	for _, test := range tests {
   211  		t.Run(test.name, func(t *testing.T) {
   212  			if got := header.IsV4LinkLocalUnicastAddress(tcpip.AddrFromSlice([]byte(test.addr))); got != test.expected {
   213  				t.Errorf("got header.IsV4LinkLocalUnicastAddress(%s) = %t, want = %t", test.addr, got, test.expected)
   214  			}
   215  		})
   216  	}
   217  }
   218  
   219  func TestIsV4LinkLocalMulticastAddress(t *testing.T) {
   220  	tests := []struct {
   221  		name     string
   222  		addr     string
   223  		expected bool
   224  	}{
   225  		{
   226  			name:     "Valid (lowest)",
   227  			addr:     "\xe0\x00\x00\x00",
   228  			expected: true,
   229  		},
   230  		{
   231  			name:     "Valid (highest)",
   232  			addr:     "\xe0\x00\x00\xff",
   233  			expected: true,
   234  		},
   235  		{
   236  			name:     "Invalid (before subnet)",
   237  			addr:     "\xdf\xff\xff\xff",
   238  			expected: false,
   239  		},
   240  		{
   241  			name:     "Invalid (after subnet)",
   242  			addr:     "\xe0\x00\x01\x00",
   243  			expected: false,
   244  		},
   245  	}
   246  
   247  	for _, test := range tests {
   248  		t.Run(test.name, func(t *testing.T) {
   249  			if got := header.IsV4LinkLocalMulticastAddress(tcpip.AddrFrom4Slice([]byte(test.addr))); got != test.expected {
   250  				t.Errorf("got header.IsV4LinkLocalMulticastAddress(%s) = %t, want = %t", test.addr, got, test.expected)
   251  			}
   252  		})
   253  	}
   254  }