github.com/gopacket/gopacket@v1.1.0/layers/radius_test.go (about) 1 // Copyright 2020 The GoPacket Authors. All rights reserved. 2 // 3 // Use of this source code is governed by a BSD-style license that can be found 4 // in the LICENSE file in the root of the source tree. 5 6 package layers 7 8 import ( 9 "reflect" 10 "testing" 11 12 "github.com/gopacket/gopacket" 13 ) 14 15 func checkRADIUS(desc string, t *testing.T, packetBytes []byte, pExpectedRADIUS *RADIUS) { 16 // Analyse the packet bytes, yielding a new packet object p. 17 p := gopacket.NewPacket(packetBytes, LinkTypeEthernet, gopacket.Default) 18 if p.ErrorLayer() != nil { 19 t.Errorf("Failed to decode packet %s: %v", desc, p.ErrorLayer().Error()) 20 } 21 22 // Ensure that the packet analysis yielded the correct set of layers: 23 // Link Layer = Ethernet. 24 // Network Layer = IPv4. 25 // Transport Layer = UDP. 26 // Application Layer = RADIUS. 27 checkLayers(p, []gopacket.LayerType{ 28 LayerTypeEthernet, 29 LayerTypeIPv4, 30 LayerTypeUDP, 31 LayerTypeRADIUS, 32 }, t) 33 34 // Select the Application (RADIUS) layer. 35 pResultRADIUS, ok := p.ApplicationLayer().(*RADIUS) 36 if !ok { 37 t.Error("No RADIUS layer type found in packet in " + desc + ".") 38 } 39 40 // Compare the generated RADIUS object with the expected RADIUS object. 41 if !reflect.DeepEqual(pResultRADIUS, pExpectedRADIUS) { 42 t.Errorf("RADIUS packet processing failed for packet "+desc+ 43 ":\ngot :\n%#v\n\nwant :\n%#v\n\n", pResultRADIUS, pExpectedRADIUS) 44 } 45 buf := gopacket.NewSerializeBuffer() 46 opts := gopacket.SerializeOptions{} 47 err := pResultRADIUS.SerializeTo(buf, opts) 48 if err != nil { 49 t.Error(err) 50 } 51 if !reflect.DeepEqual(pResultRADIUS.BaseLayer.Contents, buf.Bytes()) { 52 t.Errorf("RADIUS packet serialization failed for packet "+desc+ 53 ":\ngot :\n%x\n\nwant :\n%x\n\n", buf.Bytes(), packetBytes) 54 } 55 } 56 57 func faliedRADIUS(t *testing.T, desc string, data *RADIUS) { 58 t.Run(desc, func(t *testing.T) { 59 buf := gopacket.NewSerializeBuffer() 60 opts := gopacket.SerializeOptions{} 61 if err := data.SerializeTo(buf, opts); err != nil { 62 t.Error(err) 63 } 64 65 p := gopacket.NewPacket(buf.Bytes(), LayerTypeRADIUS, gopacket.Default) 66 if p.ErrorLayer() == nil { 67 t.Errorf("No Error layer type found in packet in %s.\n", desc) 68 } 69 }) 70 } 71 72 func TestRADIUSCode(t *testing.T) { 73 tests := []struct { 74 name string 75 code RADIUSCode 76 }{ 77 {name: "Unknown(0)", code: RADIUSCode(0)}, 78 {name: "Access-Request", code: RADIUSCodeAccessRequest}, 79 {name: "Access-Accept", code: RADIUSCodeAccessAccept}, 80 {name: "Access-Reject", code: RADIUSCodeAccessReject}, 81 {name: "Accounting-Request", code: RADIUSCodeAccountingRequest}, 82 {name: "Accounting-Response", code: RADIUSCodeAccountingResponse}, 83 {name: "Access-Challenge", code: RADIUSCodeAccessChallenge}, 84 {name: "Status-Server", code: RADIUSCodeStatusServer}, 85 {name: "Status-Client", code: RADIUSCodeStatusClient}, 86 {name: "Reserved", code: RADIUSCodeReserved}, 87 } 88 for _, tt := range tests { 89 t.Run(tt.name, func(t *testing.T) { 90 if tt.name != tt.code.String() { 91 t.Errorf("Failed to convert constrant value to string: %d\n", tt.code) 92 } 93 }) 94 } 95 } 96 97 func TestRADIUSAttributeType(t *testing.T) { 98 tests := []struct { 99 name string 100 code RADIUSAttributeType 101 }{ 102 {name: "Unknown(0)", code: RADIUSAttributeType(0)}, 103 {name: "User-Name", code: RADIUSAttributeTypeUserName}, 104 {name: "User-Password", code: RADIUSAttributeTypeUserPassword}, 105 {name: "CHAP-Password", code: RADIUSAttributeTypeCHAPPassword}, 106 {name: "NAS-IP-Address", code: RADIUSAttributeTypeNASIPAddress}, 107 {name: "NAS-Port", code: RADIUSAttributeTypeNASPort}, 108 {name: "Service-Type", code: RADIUSAttributeTypeServiceType}, 109 {name: "Framed-Protocol", code: RADIUSAttributeTypeFramedProtocol}, 110 {name: "Framed-IP-Address", code: RADIUSAttributeTypeFramedIPAddress}, 111 {name: "Framed-IP-Netmask", code: RADIUSAttributeTypeFramedIPNetmask}, 112 {name: "Framed-Routing", code: RADIUSAttributeTypeFramedRouting}, 113 {name: "Filter-Id", code: RADIUSAttributeTypeFilterId}, 114 {name: "Framed-MTU", code: RADIUSAttributeTypeFramedMTU}, 115 {name: "Framed-Compression", code: RADIUSAttributeTypeFramedCompression}, 116 {name: "Login-IP-Host", code: RADIUSAttributeTypeLoginIPHost}, 117 {name: "Login-Service", code: RADIUSAttributeTypeLoginService}, 118 {name: "Login-TCP-Port", code: RADIUSAttributeTypeLoginTCPPort}, 119 {name: "Reply-Message", code: RADIUSAttributeTypeReplyMessage}, 120 {name: "Callback-Number", code: RADIUSAttributeTypeCallbackNumber}, 121 {name: "Callback-Id", code: RADIUSAttributeTypeCallbackId}, 122 {name: "Framed-Route", code: RADIUSAttributeTypeFramedRoute}, 123 {name: "Framed-IPX-Network", code: RADIUSAttributeTypeFramedIPXNetwork}, 124 {name: "State", code: RADIUSAttributeTypeState}, 125 {name: "Class", code: RADIUSAttributeTypeClass}, 126 {name: "Vendor-Specific", code: RADIUSAttributeTypeVendorSpecific}, 127 {name: "Session-Timeout", code: RADIUSAttributeTypeSessionTimeout}, 128 {name: "Idle-Timeout", code: RADIUSAttributeTypeIdleTimeout}, 129 {name: "Termination-Action", code: RADIUSAttributeTypeTerminationAction}, 130 {name: "Called-Station-Id", code: RADIUSAttributeTypeCalledStationId}, 131 {name: "Calling-Station-Id", code: RADIUSAttributeTypeCallingStationId}, 132 {name: "NAS-Identifier", code: RADIUSAttributeTypeNASIdentifier}, 133 {name: "Proxy-State", code: RADIUSAttributeTypeProxyState}, 134 {name: "Login-LAT-Service", code: RADIUSAttributeTypeLoginLATService}, 135 {name: "Login-LAT-Node", code: RADIUSAttributeTypeLoginLATNode}, 136 {name: "Login-LAT-Group", code: RADIUSAttributeTypeLoginLATGroup}, 137 {name: "Framed-AppleTalk-Link", code: RADIUSAttributeTypeFramedAppleTalkLink}, 138 {name: "Framed-AppleTalk-Network", code: RADIUSAttributeTypeFramedAppleTalkNetwork}, 139 {name: "Framed-AppleTalk-Zone", code: RADIUSAttributeTypeFramedAppleTalkZone}, 140 {name: "Acct-Status-Type", code: RADIUSAttributeTypeAcctStatusType}, 141 {name: "Acct-Delay-Time", code: RADIUSAttributeTypeAcctDelayTime}, 142 {name: "Acct-Input-Octets", code: RADIUSAttributeTypeAcctInputOctets}, 143 {name: "Acct-Output-Octets", code: RADIUSAttributeTypeAcctOutputOctets}, 144 {name: "Acct-Session-Id", code: RADIUSAttributeTypeAcctSessionId}, 145 {name: "Acct-Authentic", code: RADIUSAttributeTypeAcctAuthentic}, 146 {name: "Acct-Session-Time", code: RADIUSAttributeTypeAcctSessionTime}, 147 {name: "Acct-Input-Packets", code: RADIUSAttributeTypeAcctInputPackets}, 148 {name: "Acct-Output-Packets", code: RADIUSAttributeTypeAcctOutputPackets}, 149 {name: "Acct-Terminate-Cause", code: RADIUSAttributeTypeAcctTerminateCause}, 150 {name: "Acct-Multi-Session-Id", code: RADIUSAttributeTypeAcctMultiSessionId}, 151 {name: "Acct-Link-Count", code: RADIUSAttributeTypeAcctLinkCount}, 152 {name: "Acct-Input-Gigawords", code: RADIUSAttributeTypeAcctInputGigawords}, 153 {name: "Acct-Output-Gigawords", code: RADIUSAttributeTypeAcctOutputGigawords}, 154 {name: "Event-Timestamp", code: RADIUSAttributeTypeEventTimestamp}, 155 {name: "CHAP-Challenge", code: RADIUSAttributeTypeCHAPChallenge}, 156 {name: "NAS-Port-Type", code: RADIUSAttributeTypeNASPortType}, 157 {name: "Port-Limit", code: RADIUSAttributeTypePortLimit}, 158 {name: "Login-LAT-Port", code: RADIUSAttributeTypeLoginLATPort}, 159 {name: "Tunnel-Type", code: RADIUSAttributeTypeTunnelType}, 160 {name: "Tunnel-Medium-Type", code: RADIUSAttributeTypeTunnelMediumType}, 161 {name: "Tunnel-Client-Endpoint", code: RADIUSAttributeTypeTunnelClientEndpoint}, 162 {name: "Tunnel-Server-Endpoint", code: RADIUSAttributeTypeTunnelServerEndpoint}, 163 {name: "Acct-Tunnel-Connection", code: RADIUSAttributeTypeAcctTunnelConnection}, 164 {name: "Tunnel-Password", code: RADIUSAttributeTypeTunnelPassword}, 165 {name: "ARAP-Password", code: RADIUSAttributeTypeARAPPassword}, 166 {name: "ARAP-Features", code: RADIUSAttributeTypeARAPFeatures}, 167 {name: "ARAP-Zone-Access", code: RADIUSAttributeTypeARAPZoneAccess}, 168 {name: "ARAP-Security", code: RADIUSAttributeTypeARAPSecurity}, 169 {name: "ARAP-Security-Data", code: RADIUSAttributeTypeARAPSecurityData}, 170 {name: "Password-Retry", code: RADIUSAttributeTypePasswordRetry}, 171 {name: "Prompt", code: RADIUSAttributeTypePrompt}, 172 {name: "Connect-Info", code: RADIUSAttributeTypeConnectInfo}, 173 {name: "Configuration-Token", code: RADIUSAttributeTypeConfigurationToken}, 174 {name: "EAP-Message", code: RADIUSAttributeTypeEAPMessage}, 175 {name: "Message-Authenticator", code: RADIUSAttributeTypeMessageAuthenticator}, 176 {name: "Tunnel-Private-Group-ID", code: RADIUSAttributeTypeTunnelPrivateGroupID}, 177 {name: "Tunnel-Assignment-ID", code: RADIUSAttributeTypeTunnelAssignmentID}, 178 {name: "Tunnel-Preference", code: RADIUSAttributeTypeTunnelPreference}, 179 {name: "ARAP-Challenge-Response", code: RADIUSAttributeTypeARAPChallengeResponse}, 180 {name: "Acct-Interim-Interval", code: RADIUSAttributeTypeAcctInterimInterval}, 181 {name: "Acct-Tunnel-Packets-Lost", code: RADIUSAttributeTypeAcctTunnelPacketsLost}, 182 {name: "NAS-Port-Id", code: RADIUSAttributeTypeNASPortId}, 183 {name: "Framed-Pool", code: RADIUSAttributeTypeFramedPool}, 184 {name: "Tunnel-Client-Auth-ID", code: RADIUSAttributeTypeTunnelClientAuthID}, 185 {name: "Tunnel-Server-Auth-ID", code: RADIUSAttributeTypeTunnelServerAuthID}, 186 } 187 for _, tt := range tests { 188 t.Run(tt.name, func(t *testing.T) { 189 if tt.name != tt.code.String() { 190 t.Errorf("Failed to convert constrant value to string: %d\n", tt.code) 191 } 192 }) 193 } 194 } 195 196 func TestRADIUSRecordSize(t *testing.T) { 197 tests := []struct { 198 name string 199 size int 200 }{ 201 {name: "Minimum-1", size: radiusMinimumRecordSizeInBytes - 1}, 202 {name: "Maximum+1", size: radiusMaximumRecordSizeInBytes + 1}, 203 } 204 for _, tt := range tests { 205 t.Run(tt.name, func(t *testing.T) { 206 var testPacketRADIUS = make([]byte, tt.size) 207 p := gopacket.NewPacket(testPacketRADIUS, LayerTypeRADIUS, gopacket.Default) 208 if p.ErrorLayer() == nil { 209 t.Errorf("No Error layer type found in packet in %s.\n", tt.name) 210 } 211 }) 212 } 213 } 214 215 func TestRADIUSLengthField(t *testing.T) { 216 tests := []struct { 217 name string 218 data *RADIUS 219 }{ 220 { 221 name: "Minimum-1", 222 data: &RADIUS{ 223 Length: RADIUSLength(radiusMinimumRecordSizeInBytes - 1), 224 }, 225 }, 226 { 227 name: "Minimum+1", 228 data: &RADIUS{ 229 Length: RADIUSLength(radiusMinimumRecordSizeInBytes + 1), 230 }, 231 }, 232 { 233 name: "Maximum-1", 234 data: &RADIUS{ 235 Length: RADIUSLength(radiusMaximumRecordSizeInBytes - 1), 236 }, 237 }, 238 { 239 name: "Maximum+1", 240 data: &RADIUS{ 241 Length: RADIUSLength(radiusMaximumRecordSizeInBytes + 1), 242 }, 243 }, 244 } 245 for _, tt := range tests { 246 faliedRADIUS(t, tt.name, tt.data) 247 } 248 } 249 250 func TestRADIUSAttributesLengthField(t *testing.T) { 251 tests := []struct { 252 name string 253 data *RADIUS 254 }{ 255 { 256 name: "Minimum-1", 257 data: &RADIUS{ 258 Length: RADIUSLength(radiusMinimumRecordSizeInBytes + radiusAttributesMinimumRecordSizeInBytes), 259 Attributes: []RADIUSAttribute{ 260 { 261 Length: RADIUSAttributeLength(radiusAttributesMinimumRecordSizeInBytes - 1), 262 Value: make([]byte, 1), 263 }, 264 }, 265 }, 266 }, 267 { 268 name: "Minimum-1", 269 data: &RADIUS{ 270 Length: RADIUSLength(radiusMinimumRecordSizeInBytes + radiusAttributesMinimumRecordSizeInBytes - 1), 271 Attributes: []RADIUSAttribute{ 272 { 273 Length: RADIUSAttributeLength(radiusAttributesMinimumRecordSizeInBytes), 274 Value: make([]byte, 1), 275 }, 276 }, 277 }, 278 }, 279 { 280 name: "Minimum+1", 281 data: &RADIUS{ 282 Length: RADIUSLength(radiusMinimumRecordSizeInBytes + radiusAttributesMinimumRecordSizeInBytes), 283 Attributes: []RADIUSAttribute{ 284 { 285 Length: RADIUSAttributeLength(radiusAttributesMinimumRecordSizeInBytes + 1), 286 Value: make([]byte, 1), 287 }, 288 }, 289 }, 290 }, 291 { 292 name: "Minimum+1", 293 data: &RADIUS{ 294 Length: RADIUSLength(radiusMinimumRecordSizeInBytes + radiusAttributesMinimumRecordSizeInBytes + 1), 295 Attributes: []RADIUSAttribute{ 296 { 297 Length: RADIUSAttributeLength(radiusAttributesMinimumRecordSizeInBytes), 298 Value: make([]byte, 1), 299 }, 300 }, 301 }, 302 }, 303 } 304 for _, tt := range tests { 305 faliedRADIUS(t, tt.name, tt.data) 306 } 307 } 308 309 func TestRADIUSAccessRequest(t *testing.T) { 310 // This test packet is the first RADIUS packet in the RADIUS sample capture 311 // pcap file radtest.pcap on the Wireshark sample captures page: 312 // 313 // https://github.com/egxp/docker-compose-test-radius 314 var testPacketRADIUS = []byte{ 315 0x02, 0x42, 0xac, 0x14, 0x00, 0x02, 0x02, 0x42, 0x06, 0x4d, 0xad, 0xbf, 0x08, 0x00, 0x45, 0x00, 316 0x00, 0x67, 0xee, 0xea, 0x40, 0x00, 0x40, 0x11, 0xf3, 0x6f, 0xac, 0x14, 0x00, 0x01, 0xac, 0x14, 317 0x00, 0x02, 0xd8, 0x29, 0x07, 0x14, 0x00, 0x53, 0x58, 0x90, 0x01, 0x8d, 0x00, 0x4b, 0x3b, 0xbd, 318 0x22, 0x52, 0xb4, 0xc8, 0xd8, 0x44, 0x1b, 0x46, 0x79, 0xbf, 0x4a, 0x2b, 0x86, 0x01, 0x01, 0x07, 319 0x41, 0x64, 0x6d, 0x69, 0x6e, 0x02, 0x12, 0x4d, 0x2f, 0x62, 0x0b, 0x33, 0x9d, 0x6d, 0x1f, 0xe0, 320 0xe4, 0x6d, 0x1f, 0x9b, 0xda, 0xff, 0xf0, 0x04, 0x06, 0x7f, 0x00, 0x01, 0x01, 0x05, 0x06, 0x00, 321 0x00, 0x00, 0x00, 0x50, 0x12, 0x41, 0x73, 0xed, 0x26, 0xd3, 0xb3, 0xa9, 0x64, 0xff, 0x4d, 0xc3, 322 0x0d, 0x94, 0x33, 0xe8, 0x2a, 323 } 324 325 // Assemble the RADIUS object that we expect to emerge from this test. 326 pExpectedRADIUS := &RADIUS{ 327 BaseLayer: BaseLayer{ 328 Contents: []byte{ 329 0x01, 0x8d, 0x00, 0x4b, 0x3b, 0xbd, 0x22, 0x52, 0xb4, 0xc8, 0xd8, 0x44, 0x1b, 0x46, 0x79, 0xbf, 330 0x4a, 0x2b, 0x86, 0x01, 0x01, 0x07, 0x41, 0x64, 0x6d, 0x69, 0x6e, 0x02, 0x12, 0x4d, 0x2f, 0x62, 331 0x0b, 0x33, 0x9d, 0x6d, 0x1f, 0xe0, 0xe4, 0x6d, 0x1f, 0x9b, 0xda, 0xff, 0xf0, 0x04, 0x06, 0x7f, 332 0x00, 0x01, 0x01, 0x05, 0x06, 0x00, 0x00, 0x00, 0x00, 0x50, 0x12, 0x41, 0x73, 0xed, 0x26, 0xd3, 333 0xb3, 0xa9, 0x64, 0xff, 0x4d, 0xc3, 0x0d, 0x94, 0x33, 0xe8, 0x2a, 334 }, 335 Payload: nil, 336 }, 337 Code: RADIUSCodeAccessRequest, 338 Identifier: RADIUSIdentifier(0x8d), 339 Length: RADIUSLength(0x004b), 340 Authenticator: RADIUSAuthenticator([16]byte{ 341 0x3b, 0xbd, 0x22, 0x52, 0xb4, 0xc8, 0xd8, 0x44, 0x1b, 0x46, 0x79, 0xbf, 0x4a, 0x2b, 0x86, 0x01, 342 }), 343 Attributes: []RADIUSAttribute{ 344 { 345 Type: RADIUSAttributeTypeUserName, 346 Length: RADIUSAttributeLength(0x07), 347 Value: RADIUSAttributeValue("Admin"), 348 }, 349 { 350 Type: RADIUSAttributeTypeUserPassword, 351 Length: RADIUSAttributeLength(0x12), 352 Value: RADIUSAttributeValue("\x4d\x2f\x62\x0b\x33\x9d\x6d\x1f\xe0\xe4\x6d\x1f\x9b\xda\xff\xf0"), 353 }, 354 { 355 Type: RADIUSAttributeTypeNASIPAddress, 356 Length: RADIUSAttributeLength(0x06), 357 Value: RADIUSAttributeValue("\x7f\x00\x01\x01"), 358 }, 359 { 360 Type: RADIUSAttributeTypeNASPort, 361 Length: RADIUSAttributeLength(0x06), 362 Value: RADIUSAttributeValue("\x00\x00\x00\x00"), 363 }, 364 { 365 Type: RADIUSAttributeTypeMessageAuthenticator, 366 Length: RADIUSAttributeLength(0x12), 367 Value: RADIUSAttributeValue("\x41\x73\xed\x26\xd3\xb3\xa9\x64\xff\x4d\xc3\x0d\x94\x33\xe8\x2a"), 368 }, 369 }, 370 } 371 372 checkRADIUS("AccessRequest", t, testPacketRADIUS, pExpectedRADIUS) 373 } 374 375 func TestRADIUSAccessAccept(t *testing.T) { 376 // This test packet is the first RADIUS packet in the RADIUS sample capture 377 // pcap file radtest.pcap on the Wireshark sample captures page: 378 // 379 // https://github.com/egxp/docker-compose-test-radius 380 var testPacketRADIUS = []byte{ 381 0x02, 0x42, 0x06, 0x4d, 0xad, 0xbf, 0x02, 0x42, 0xac, 0x14, 0x00, 0x02, 0x08, 0x00, 0x45, 0x00, 382 0x00, 0x30, 0xee, 0xfd, 0x00, 0x00, 0x40, 0x11, 0x33, 0x94, 0xac, 0x14, 0x00, 0x02, 0xac, 0x14, 383 0x00, 0x01, 0x07, 0x14, 0xd8, 0x29, 0x00, 0x1c, 0x58, 0x59, 0x02, 0x8d, 0x00, 0x14, 0x86, 0xa8, 384 0xd5, 0xcd, 0x69, 0x3c, 0x07, 0x5e, 0x9e, 0x18, 0xa2, 0x2d, 0xdd, 0x5f, 0x2b, 0xff, 385 } 386 387 // Assemble the RADIUS object that we expect to emerge from this test. 388 pExpectedRADIUS := &RADIUS{ 389 BaseLayer: BaseLayer{ 390 Contents: []byte{ 391 0x02, 0x8d, 0x00, 0x14, 0x86, 0xa8, 0xd5, 0xcd, 0x69, 0x3c, 0x07, 0x5e, 0x9e, 0x18, 0xa2, 0x2d, 392 0xdd, 0x5f, 0x2b, 0xff, 393 }, 394 Payload: nil, 395 }, 396 Code: RADIUSCodeAccessAccept, 397 Identifier: RADIUSIdentifier(0x8d), 398 Length: RADIUSLength(0x0014), 399 Authenticator: RADIUSAuthenticator([16]byte{ 400 0x86, 0xa8, 0xd5, 0xcd, 0x69, 0x3c, 0x07, 0x5e, 0x9e, 0x18, 0xa2, 0x2d, 0xdd, 0x5f, 0x2b, 0xff, 401 }), 402 } 403 404 checkRADIUS("AccessAccept", t, testPacketRADIUS, pExpectedRADIUS) 405 }