github.com/craftyguy/u-root@v1.0.0/pkg/pxe/pxe_test.go (about) 1 // Copyright 2017-2018 the u-root Authors. All rights reserved 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package pxe 6 7 import ( 8 "fmt" 9 "net" 10 "net/url" 11 "reflect" 12 "testing" 13 14 "github.com/u-root/u-root/pkg/uio" 15 ) 16 17 func TestProbeFiles(t *testing.T) { 18 // Anyone got some ideas for other test cases? 19 for _, tt := range []struct { 20 mac net.HardwareAddr 21 ip net.IP 22 files []string 23 }{ 24 { 25 mac: []byte{0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff}, 26 ip: []byte{192, 168, 0, 1}, 27 files: []string{ 28 "01-aa-bb-cc-dd-ee-ff", 29 "C0A80001", 30 "C0A8000", 31 "C0A800", 32 "C0A80", 33 "C0A8", 34 "C0A", 35 "C0", 36 "C", 37 "default", 38 }, 39 }, 40 { 41 mac: []byte{0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd}, 42 ip: []byte{192, 168, 2, 91}, 43 files: []string{ 44 "01-88-99-aa-bb-cc-dd", 45 "C0A8025B", 46 "C0A8025", 47 "C0A802", 48 "C0A80", 49 "C0A8", 50 "C0A", 51 "C0", 52 "C", 53 "default", 54 }, 55 }, 56 } { 57 got := probeFiles(tt.mac, tt.ip) 58 if !reflect.DeepEqual(got, tt.files) { 59 t.Errorf("probeFiles(%s, %s) = %v, want %v", tt.mac, tt.ip, got, tt.files) 60 } 61 } 62 } 63 64 func TestAppendFile(t *testing.T) { 65 content1 := "1111" 66 content2 := "2222" 67 content3 := "3333" 68 content4 := "4444" 69 70 type label struct { 71 kernel string 72 kernelErr error 73 initrd string 74 initrdErr error 75 cmdline string 76 } 77 type config struct { 78 defaultEntry string 79 labels map[string]label 80 } 81 82 for i, tt := range []struct { 83 desc string 84 configFileURI string 85 schemeFunc func() Schemes 86 wd *url.URL 87 config *Config 88 want config 89 err error 90 }{ 91 { 92 desc: "all files exist, simple config with cmdline initrd", 93 configFileURI: "pxelinux.cfg/default", 94 schemeFunc: func() Schemes { 95 s := make(Schemes) 96 fs := NewMockScheme("tftp") 97 fs.Add("1.2.3.4", "/foobar/pxelinux.0", "") 98 conf := `default foo 99 label foo 100 kernel ./pxefiles/kernel 101 append initrd=./pxefiles/initrd` 102 fs.Add("1.2.3.4", "/foobar/pxelinux.cfg/default", conf) 103 fs.Add("1.2.3.4", "/foobar/pxefiles/kernel", content1) 104 fs.Add("1.2.3.4", "/foobar/pxefiles/initrd", content2) 105 s.Register(fs.scheme, fs) 106 return s 107 }, 108 wd: &url.URL{ 109 Scheme: "tftp", 110 Host: "1.2.3.4", 111 Path: "/foobar", 112 }, 113 want: config{ 114 defaultEntry: "foo", 115 labels: map[string]label{ 116 "foo": { 117 kernel: content1, 118 initrd: content2, 119 cmdline: "initrd=./pxefiles/initrd", 120 }, 121 }, 122 }, 123 }, 124 { 125 desc: "all files exist, simple config with directive initrd", 126 configFileURI: "pxelinux.cfg/default", 127 schemeFunc: func() Schemes { 128 s := make(Schemes) 129 fs := NewMockScheme("tftp") 130 fs.Add("1.2.3.4", "/foobar/pxelinux.0", "") 131 conf := `default foo 132 label foo 133 kernel ./pxefiles/kernel 134 initrd ./pxefiles/initrd 135 append foo=bar` 136 fs.Add("1.2.3.4", "/foobar/pxelinux.cfg/default", conf) 137 fs.Add("1.2.3.4", "/foobar/pxefiles/kernel", content1) 138 fs.Add("1.2.3.4", "/foobar/pxefiles/initrd", content2) 139 s.Register(fs.scheme, fs) 140 return s 141 }, 142 wd: &url.URL{ 143 Scheme: "tftp", 144 Host: "1.2.3.4", 145 Path: "/foobar", 146 }, 147 want: config{ 148 defaultEntry: "foo", 149 labels: map[string]label{ 150 "foo": { 151 kernel: content1, 152 initrd: content2, 153 cmdline: "foo=bar", 154 }, 155 }, 156 }, 157 }, 158 { 159 desc: "all files exist, simple config, no initrd", 160 configFileURI: "pxelinux.cfg/default", 161 schemeFunc: func() Schemes { 162 s := make(Schemes) 163 fs := NewMockScheme("tftp") 164 fs.Add("1.2.3.4", "/foobar/pxelinux.0", "") 165 conf := `default foo 166 label foo 167 kernel ./pxefiles/kernel` 168 fs.Add("1.2.3.4", "/foobar/pxelinux.cfg/default", conf) 169 fs.Add("1.2.3.4", "/foobar/pxefiles/kernel", content1) 170 s.Register(fs.scheme, fs) 171 return s 172 }, 173 wd: &url.URL{ 174 Scheme: "tftp", 175 Host: "1.2.3.4", 176 Path: "/foobar", 177 }, 178 want: config{ 179 defaultEntry: "foo", 180 labels: map[string]label{ 181 "foo": { 182 kernel: content1, 183 initrd: "", 184 cmdline: "", 185 }, 186 }, 187 }, 188 }, 189 { 190 desc: "kernel does not exist, simple config", 191 configFileURI: "pxelinux.cfg/default", 192 schemeFunc: func() Schemes { 193 s := make(Schemes) 194 fs := NewMockScheme("tftp") 195 fs.Add("1.2.3.4", "/foobar/pxelinux.0", "") 196 conf := `default foo 197 label foo 198 kernel ./pxefiles/kernel` 199 fs.Add("1.2.3.4", "/foobar/pxelinux.cfg/default", conf) 200 s.Register(fs.scheme, fs) 201 return s 202 }, 203 wd: &url.URL{ 204 Scheme: "tftp", 205 Host: "1.2.3.4", 206 Path: "/foobar", 207 }, 208 want: config{ 209 defaultEntry: "foo", 210 labels: map[string]label{ 211 "foo": { 212 kernelErr: &URLError{ 213 URL: &url.URL{ 214 Scheme: "tftp", 215 Host: "1.2.3.4", 216 Path: "/foobar/pxefiles/kernel", 217 }, 218 Err: errNoSuchFile, 219 }, 220 initrd: "", 221 cmdline: "", 222 }, 223 }, 224 }, 225 }, 226 { 227 desc: "config file does not exist", 228 configFileURI: "pxelinux.cfg/default", 229 schemeFunc: func() Schemes { 230 s := make(Schemes) 231 fs := NewMockScheme("tftp") 232 s.Register(fs.scheme, fs) 233 return s 234 }, 235 wd: &url.URL{ 236 Scheme: "tftp", 237 Host: "1.2.3.4", 238 Path: "/foobar", 239 }, 240 err: &URLError{ 241 URL: &url.URL{ 242 Scheme: "tftp", 243 Host: "1.2.3.4", 244 Path: "/foobar/pxelinux.cfg/default", 245 }, 246 Err: errNoSuchHost, 247 }, 248 }, 249 { 250 desc: "empty config", 251 configFileURI: "pxelinux.cfg/default", 252 schemeFunc: func() Schemes { 253 s := make(Schemes) 254 fs := NewMockScheme("tftp") 255 fs.Add("1.2.3.4", "/foobar/pxelinux.cfg/default", "") 256 s.Register(fs.scheme, fs) 257 return s 258 }, 259 wd: &url.URL{ 260 Scheme: "tftp", 261 Host: "1.2.3.4", 262 Path: "/foobar", 263 }, 264 want: config{ 265 defaultEntry: "", 266 }, 267 }, 268 { 269 desc: "valid config with two labels", 270 configFileURI: "pxelinux.cfg/default", 271 schemeFunc: func() Schemes { 272 s := make(Schemes) 273 fs := NewMockScheme("tftp") 274 fs.Add("1.2.3.4", "/foobar/pxelinux.0", "") 275 conf := `default foo 276 277 label foo 278 kernel ./pxefiles/fookernel 279 append earlyprintk=ttyS0 printk=ttyS0 280 281 label bar 282 kernel ./pxefiles/barkernel 283 append console=ttyS0` 284 fs.Add("1.2.3.4", "/foobar/pxelinux.cfg/default", conf) 285 fs.Add("1.2.3.4", "/foobar/pxefiles/fookernel", content1) 286 fs.Add("1.2.3.4", "/foobar/pxefiles/barkernel", content2) 287 s.Register(fs.scheme, fs) 288 return s 289 }, 290 wd: &url.URL{ 291 Scheme: "tftp", 292 Host: "1.2.3.4", 293 Path: "/foobar", 294 }, 295 want: config{ 296 defaultEntry: "foo", 297 labels: map[string]label{ 298 "foo": { 299 kernel: content1, 300 cmdline: "earlyprintk=ttyS0 printk=ttyS0", 301 }, 302 "bar": { 303 kernel: content2, 304 cmdline: "console=ttyS0", 305 }, 306 }, 307 }, 308 }, 309 { 310 desc: "valid config with global APPEND directive", 311 configFileURI: "pxelinux.cfg/default", 312 schemeFunc: func() Schemes { 313 s := make(Schemes) 314 fs := NewMockScheme("tftp") 315 fs.Add("1.2.3.4", "/foobar/pxelinux.0", "") 316 conf := `default foo 317 append foo=bar 318 319 label foo 320 kernel ./pxefiles/fookernel 321 append earlyprintk=ttyS0 printk=ttyS0 322 323 label bar 324 kernel ./pxefiles/barkernel 325 326 label baz 327 kernel ./pxefiles/barkernel 328 append -` 329 fs.Add("1.2.3.4", "/foobar/pxelinux.cfg/default", conf) 330 fs.Add("1.2.3.4", "/foobar/pxefiles/fookernel", content1) 331 fs.Add("1.2.3.4", "/foobar/pxefiles/barkernel", content2) 332 s.Register(fs.scheme, fs) 333 return s 334 }, 335 wd: &url.URL{ 336 Scheme: "tftp", 337 Host: "1.2.3.4", 338 Path: "/foobar", 339 }, 340 want: config{ 341 defaultEntry: "foo", 342 labels: map[string]label{ 343 "foo": { 344 kernel: content1, 345 // Does not contain global APPEND. 346 cmdline: "earlyprintk=ttyS0 printk=ttyS0", 347 }, 348 "bar": { 349 kernel: content2, 350 // Contains only global APPEND. 351 cmdline: "foo=bar", 352 }, 353 "baz": { 354 kernel: content2, 355 // "APPEND -" means ignore global APPEND. 356 cmdline: "", 357 }, 358 }, 359 }, 360 }, 361 { 362 desc: "valid config with global APPEND with initrd", 363 configFileURI: "pxelinux.cfg/default", 364 schemeFunc: func() Schemes { 365 s := make(Schemes) 366 fs := NewMockScheme("tftp") 367 fs.Add("1.2.3.4", "/foobar/pxelinux.0", "") 368 conf := `default mcnulty 369 append initrd=./pxefiles/normal_person 370 371 label mcnulty 372 kernel ./pxefiles/copkernel 373 append earlyprintk=ttyS0 printk=ttyS0 374 375 label lester 376 kernel ./pxefiles/copkernel 377 378 label omar 379 kernel ./pxefiles/drugkernel 380 append - 381 382 label stringer 383 kernel ./pxefiles/drugkernel 384 initrd ./pxefiles/criminal 385 ` 386 fs.Add("1.2.3.4", "/foobar/pxelinux.cfg/default", conf) 387 fs.Add("1.2.3.4", "/foobar/pxefiles/copkernel", content1) 388 fs.Add("1.2.3.4", "/foobar/pxefiles/drugkernel", content2) 389 fs.Add("1.2.3.4", "/foobar/pxefiles/normal_person", content3) 390 fs.Add("1.2.3.4", "/foobar/pxefiles/criminal", content4) 391 s.Register(fs.scheme, fs) 392 return s 393 }, 394 wd: &url.URL{ 395 Scheme: "tftp", 396 Host: "1.2.3.4", 397 Path: "/foobar", 398 }, 399 want: config{ 400 defaultEntry: "mcnulty", 401 labels: map[string]label{ 402 "mcnulty": { 403 kernel: content1, 404 // Does not contain global APPEND. 405 cmdline: "earlyprintk=ttyS0 printk=ttyS0", 406 }, 407 "lester": { 408 kernel: content1, 409 initrd: content3, 410 // Contains only global APPEND. 411 cmdline: "initrd=./pxefiles/normal_person", 412 }, 413 "omar": { 414 kernel: content2, 415 // "APPEND -" means ignore global APPEND. 416 cmdline: "", 417 }, 418 "stringer": { 419 kernel: content2, 420 // See TODO in pxe.go initrd handling. 421 initrd: content4, 422 cmdline: "initrd=./pxefiles/normal_person", 423 }, 424 }, 425 }, 426 }, 427 { 428 desc: "default label does not exist", 429 configFileURI: "pxelinux.cfg/default", 430 schemeFunc: func() Schemes { 431 s := make(Schemes) 432 fs := NewMockScheme("tftp") 433 conf := `default avon` 434 435 fs.Add("1.2.3.4", "/foobar/pxelinux.cfg/default", conf) 436 s.Register(fs.scheme, fs) 437 return s 438 }, 439 wd: &url.URL{ 440 Scheme: "tftp", 441 Host: "1.2.3.4", 442 Path: "/foobar", 443 }, 444 err: ErrDefaultEntryNotFound, 445 want: config{ 446 defaultEntry: "avon", 447 }, 448 }, 449 { 450 desc: "multi-scheme valid config", 451 configFileURI: "pxelinux.cfg/default", 452 schemeFunc: func() Schemes { 453 conf := `default sheeeit 454 455 label sheeeit 456 kernel ./pxefiles/kernel 457 initrd http://someplace.com/someinitrd.gz` 458 459 tftp := NewMockScheme("tftp") 460 tftp.Add("1.2.3.4", "/foobar/pxelinux.0", "") 461 tftp.Add("1.2.3.4", "/foobar/pxelinux.cfg/default", conf) 462 tftp.Add("1.2.3.4", "/foobar/pxefiles/kernel", content2) 463 464 http := NewMockScheme("http") 465 http.Add("someplace.com", "/someinitrd.gz", content3) 466 467 s := make(Schemes) 468 s.Register(tftp.scheme, tftp) 469 s.Register(http.scheme, http) 470 return s 471 }, 472 wd: &url.URL{ 473 Scheme: "tftp", 474 Host: "1.2.3.4", 475 Path: "/foobar", 476 }, 477 want: config{ 478 defaultEntry: "sheeeit", 479 labels: map[string]label{ 480 "sheeeit": { 481 kernel: content2, 482 initrd: content3, 483 }, 484 }, 485 }, 486 }, 487 { 488 desc: "valid config with three includes", 489 configFileURI: "pxelinux.cfg/default", 490 schemeFunc: func() Schemes { 491 s := make(Schemes) 492 fs := NewMockScheme("tftp") 493 fs.Add("1.2.3.4", "/foobar/pxelinux.0", "") 494 conf := `default mcnulty 495 496 include installer/txt.cfg 497 include installer/stdmenu.cfg 498 499 menu begin advanced 500 menu title Advanced Options 501 include installer/stdmenu.cfg 502 menu end 503 ` 504 505 txt := ` 506 label mcnulty 507 kernel ./pxefiles/copkernel 508 append earlyprintk=ttyS0 printk=ttyS0 509 ` 510 511 stdmenu := ` 512 label omar 513 kernel ./pxefiles/drugkernel 514 ` 515 fs.Add("1.2.3.4", "/foobar/pxelinux.cfg/default", conf) 516 fs.Add("1.2.3.4", "/foobar/installer/stdmenu.cfg", stdmenu) 517 fs.Add("1.2.3.4", "/foobar/installer/txt.cfg", txt) 518 fs.Add("1.2.3.4", "/foobar/pxefiles/copkernel", content1) 519 fs.Add("1.2.3.4", "/foobar/pxefiles/drugkernel", content2) 520 s.Register(fs.scheme, fs) 521 return s 522 }, 523 wd: &url.URL{ 524 Scheme: "tftp", 525 Host: "1.2.3.4", 526 Path: "/foobar", 527 }, 528 want: config{ 529 defaultEntry: "mcnulty", 530 labels: map[string]label{ 531 "mcnulty": { 532 kernel: content1, 533 cmdline: "earlyprintk=ttyS0 printk=ttyS0", 534 }, 535 "omar": { 536 kernel: content2, 537 }, 538 }, 539 }, 540 }, 541 } { 542 t.Run(fmt.Sprintf("Test [%02d] %s", i, tt.desc), func(t *testing.T) { 543 s := tt.schemeFunc() 544 c := NewConfig(tt.wd) 545 c.schemes = s 546 547 if err := c.AppendFile(tt.configFileURI); !reflect.DeepEqual(err, tt.err) { 548 t.Errorf("AppendFile() got %v, want %v", err, tt.err) 549 } else if err != nil { 550 return 551 } 552 553 if got, want := c.DefaultEntry, tt.want.defaultEntry; got != want { 554 t.Errorf("DefaultEntry got %v, want %v", got, want) 555 } 556 557 for labelName, want := range tt.want.labels { 558 t.Run(fmt.Sprintf("label %s", labelName), func(t *testing.T) { 559 label, ok := c.Entries[labelName] 560 if !ok { 561 t.Errorf("Config label %v does not exist", labelName) 562 return 563 } 564 565 // Same kernel? 566 if label.Kernel == nil && (len(want.kernel) > 0 || want.kernelErr != nil) { 567 t.Errorf("want kernel, got none") 568 } 569 if label.Kernel != nil { 570 k, err := uio.ReadAll(label.Kernel) 571 if !reflect.DeepEqual(err, want.kernelErr) { 572 t.Errorf("could not read kernel of label %q: %v, want %v", labelName, err, want.kernelErr) 573 } 574 if got, want := string(k), want.kernel; got != want { 575 t.Errorf("got kernel %s, want %s", got, want) 576 } 577 } 578 579 // Same initrd? 580 if label.Initrd == nil && (len(want.initrd) > 0 || want.initrdErr != nil) { 581 t.Errorf("want initrd, got none") 582 } 583 if label.Initrd != nil { 584 i, err := uio.ReadAll(label.Initrd) 585 if err != want.initrdErr { 586 t.Errorf("could not read initrd of label %q: %v, want %v", labelName, err, want.initrdErr) 587 } 588 if got, want := string(i), want.initrd; got != want { 589 t.Errorf("got initrd %s, want %s", got, want) 590 } 591 } 592 593 // Same cmdline? 594 if got, want := label.Cmdline, want.cmdline; got != want { 595 t.Errorf("got cmdline %s, want %s", got, want) 596 } 597 }) 598 } 599 600 // Check that the parser didn't make up labels. 601 for labelName := range c.Entries { 602 if _, ok := tt.want.labels[labelName]; !ok { 603 t.Errorf("config has extra label %s, but not wanted", labelName) 604 } 605 } 606 }) 607 } 608 }