github.com/SagerNet/gvisor@v0.0.0-20210707092255-7731c139d75c/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 "github.com/SagerNet/gvisor/pkg/tcpip" 22 "github.com/SagerNet/gvisor/pkg/tcpip/buffer" 23 "github.com/SagerNet/gvisor/pkg/tcpip/header" 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 := buffer.NewPrependable(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 tcpip.Address 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(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 tcpip.Address 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(test.addr); got != test.expected { 250 t.Errorf("got header.IsV4LinkLocalMulticastAddress(%s) = %t, want = %t", test.addr, got, test.expected) 251 } 252 }) 253 } 254 }